org.eclipse.php.internal.ui.compare.MergeSourceViewer.java Source code

Java tutorial

Introduction

Here is the source code for org.eclipse.php.internal.ui.compare.MergeSourceViewer.java

Source

/*******************************************************************************
 * Copyright (c) 2009 IBM Corporation and others.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 * 
 * Contributors:
 *     IBM Corporation - initial API and implementation
 *     Zend Technologies
 *******************************************************************************/
package org.eclipse.php.internal.ui.compare;

import java.util.*;

import org.eclipse.compare.ICompareContainer;
import org.eclipse.compare.internal.MergeViewerAction;
import org.eclipse.compare.internal.Utilities;
import org.eclipse.core.commands.operations.IOperationHistory;
import org.eclipse.core.commands.operations.IOperationHistoryListener;
import org.eclipse.core.commands.operations.IUndoContext;
import org.eclipse.core.commands.operations.OperationHistoryEvent;
import org.eclipse.jface.action.*;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.jface.preference.PreferenceConverter;
import org.eclipse.jface.text.*;
import org.eclipse.jface.text.source.*;
import org.eclipse.jface.util.IPropertyChangeListener;
import org.eclipse.jface.util.PropertyChangeEvent;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.StyledText;
import org.eclipse.swt.graphics.*;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.ui.IWorkbenchActionConstants;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.editors.text.EditorsUI;
import org.eclipse.ui.texteditor.AbstractDecoratedTextEditorPreferenceConstants;
import org.eclipse.ui.texteditor.FindReplaceAction;
import org.eclipse.wst.sse.ui.internal.StructuredTextViewer;

/**
 * Extends the JFace SourceViewer with some convenience methods.
 */
public class MergeSourceViewer extends StructuredTextViewer
        implements ISelectionChangedListener, ITextListener, IMenuListener, IOperationHistoryListener {

    public static final String UNDO_ID = "undo"; //$NON-NLS-1$
    public static final String REDO_ID = "redo"; //$NON-NLS-1$
    public static final String CUT_ID = "cut"; //$NON-NLS-1$
    public static final String COPY_ID = "copy"; //$NON-NLS-1$
    public static final String PASTE_ID = "paste"; //$NON-NLS-1$
    public static final String DELETE_ID = "delete"; //$NON-NLS-1$
    public static final String SELECT_ALL_ID = "selectAll"; //$NON-NLS-1$
    public static final String SAVE_ID = "save"; //$NON-NLS-1$
    public static final String FIND_ID = "find"; //$NON-NLS-1$

    class TextOperationAction extends MergeViewerAction {

        private int fOperationCode;

        TextOperationAction(int operationCode, boolean mutable, boolean selection, boolean content) {
            this(operationCode, null, mutable, selection, content);

        }

        public TextOperationAction(int operationCode, String actionDefinitionId, boolean mutable, boolean selection,
                boolean content) {
            super(mutable, selection, content);
            if (actionDefinitionId != null)
                setActionDefinitionId(actionDefinitionId);
            fOperationCode = operationCode;
            update();
        }

        public void run() {
            if (isEnabled())
                doOperation(fOperationCode);
        }

        public boolean isEnabled() {
            return fOperationCode != -1 && canDoOperation(fOperationCode);
        }

        public void update() {
            this.setEnabled(isEnabled());
        }
    }

    private ResourceBundle fResourceBundle;
    private Position fRegion;
    private boolean fEnabled = true;
    private HashMap fActions = new HashMap();
    private IDocument fRememberedDocument;

    private boolean fAddSaveAction = true;
    private boolean isConfigured = false;

    // line number ruler support
    private IPropertyChangeListener fPreferenceChangeListener;
    private boolean fShowLineNumber = false;
    private LineNumberRulerColumn fLineNumberColumn;
    private List textActions = new ArrayList();

    public MergeSourceViewer(Composite parent, int style, ResourceBundle bundle, ICompareContainer container) {
        super(parent, new CompositeRuler(), null, false, style | SWT.H_SCROLL | SWT.V_SCROLL);

        fResourceBundle = bundle;

        MenuManager menu = new MenuManager();
        menu.setRemoveAllWhenShown(true);
        menu.addMenuListener(this);
        StyledText te = getTextWidget();
        te.setMenu(menu.createContextMenu(te));
        container.registerContextMenu(menu, this);

        // for listening to editor show/hide line number preference value
        fPreferenceChangeListener = new IPropertyChangeListener() {
            public void propertyChange(PropertyChangeEvent event) {
                MergeSourceViewer.this.handlePropertyChangeEvent(event);
            }
        };
        EditorsUI.getPreferenceStore().addPropertyChangeListener(fPreferenceChangeListener);
        fShowLineNumber = EditorsUI.getPreferenceStore()
                .getBoolean(AbstractDecoratedTextEditorPreferenceConstants.EDITOR_LINE_NUMBER_RULER);
        if (fShowLineNumber) {
            updateLineNumberRuler();
        }

        IOperationHistory history = getHistory();
        if (history != null)
            history.addOperationHistoryListener(this);
    }

    public void rememberDocument(IDocument doc) {
        // if (doc != null && fRememberedDocument != null) {
        //         System.err.println("MergeSourceViewer.rememberDocument: fRememberedDocument != null: shouldn't happen"); //$NON-NLS-1$
        // }
        fRememberedDocument = doc;
    }

    public IDocument getRememberedDocument() {
        return fRememberedDocument;
    }

    public void hideSaveAction() {
        fAddSaveAction = false;
    }

    public void setFont(Font font) {
        StyledText te = getTextWidget();
        if (te != null)
            te.setFont(font);
        if (fLineNumberColumn != null) {
            fLineNumberColumn.setFont(font);
            layoutViewer();
        }
    }

    public void setBackgroundColor(Color color) {
        StyledText te = getTextWidget();
        if (te != null)
            te.setBackground(color);
        if (fLineNumberColumn != null)
            fLineNumberColumn.setBackground(color);
    }

    public void setEnabled(boolean enabled) {
        if (enabled != fEnabled) {
            fEnabled = enabled;
            StyledText c = getTextWidget();
            if (c != null) {
                c.setEnabled(enabled);
                Display d = c.getDisplay();
                c.setBackground(enabled ? d.getSystemColor(SWT.COLOR_LIST_BACKGROUND) : null);
            }
        }
    }

    public boolean getEnabled() {
        return fEnabled;
    }

    public void setRegion(Position region) {
        fRegion = region;
    }

    public Position getRegion() {
        return fRegion;
    }

    public boolean isControlOkToUse() {
        StyledText t = getTextWidget();
        return t != null && !t.isDisposed();
    }

    public void setSelection(Position position) {
        if (position != null)
            setSelectedRange(position.getOffset(), position.getLength());
    }

    public void setLineBackground(Position position, Color c) {
        StyledText t = getTextWidget();
        if (t != null && !t.isDisposed()) {
            Point region = new Point(0, 0);
            getLineRange(position, region);

            region.x -= getDocumentRegionOffset();

            try {
                t.setLineBackground(region.x, region.y, c);
            } catch (IllegalArgumentException ex) {
                // silently ignored
            }
        }
    }

    public void resetLineBackground() {
        StyledText t = getTextWidget();
        if (t != null && !t.isDisposed()) {
            int lines = getLineCount();
            t.setLineBackground(0, lines, null);
        }
    }

    /*
     * Returns number of lines in document region.
     */
    public int getLineCount() {
        IRegion region = getVisibleRegion();

        int length = region.getLength();
        if (length == 0)
            return 0;

        IDocument doc = getDocument();
        int startLine = 0;
        int endLine = 0;

        int start = region.getOffset();
        try {
            startLine = doc.getLineOfOffset(start);
        } catch (BadLocationException ex) {
            // silently ignored
        }
        try {
            endLine = doc.getLineOfOffset(start + length);
        } catch (BadLocationException ex) {
            // silently ignored
        }

        return endLine - startLine + 1;
    }

    public int getViewportLines() {
        StyledText te = getTextWidget();
        Rectangle clArea = te.getClientArea();
        if (!clArea.isEmpty())
            return clArea.height / te.getLineHeight();
        return 0;
    }

    public int getViewportHeight() {
        StyledText te = getTextWidget();
        Rectangle clArea = te.getClientArea();
        if (!clArea.isEmpty())
            return clArea.height;
        return 0;
    }

    /*
     * Returns lines
     */
    public int getDocumentRegionOffset() {
        int start = getVisibleRegion().getOffset();
        IDocument doc = getDocument();
        if (doc != null) {
            try {
                return doc.getLineOfOffset(start);
            } catch (BadLocationException ex) {
                // silently ignored
            }
        }
        return 0;
    }

    public int getVerticalScrollOffset() {
        StyledText st = getTextWidget();
        int lineHeight = st.getLineHeight();
        return getTopInset() - ((getDocumentRegionOffset() * lineHeight) + st.getTopPixel());
    }

    /*
     * Returns the start line and the number of lines which correspond to the
     * given position. Starting line number is 0 based.
     */
    public Point getLineRange(Position p, Point region) {

        IDocument doc = getDocument();

        if (p == null || doc == null) {
            region.x = 0;
            region.y = 0;
            return region;
        }

        int start = p.getOffset();
        int length = p.getLength();

        int startLine = 0;
        try {
            startLine = doc.getLineOfOffset(start);
        } catch (BadLocationException e) {
            // silently ignored
        }

        int lineCount = 0;

        if (length == 0) {
            // // if range length is 0 and if range starts a new line
            // try {
            // if (start == doc.getLineStartOffset(startLine)) {
            // lines--;
            // }
            // } catch (BadLocationException e) {
            // lines--;
            // }

        } else {
            int endLine = 0;
            try {
                endLine = doc.getLineOfOffset(start + length - 1); // why -1?
            } catch (BadLocationException e) {
                // silently ignored
            }
            lineCount = endLine - startLine + 1;
        }

        region.x = startLine;
        region.y = lineCount;
        return region;
    }

    /*
     * Scroll TextPart to the given line.
     */
    public void vscroll(int line) {

        int srcViewSize = getLineCount();
        int srcExtentSize = getViewportLines();

        if (srcViewSize > srcExtentSize) {

            if (line < 0)
                line = 0;

            int cp = getTopIndex();
            if (cp != line)
                setTopIndex(line + getDocumentRegionOffset());
        }
    }

    public void addAction(String actionId, MergeViewerAction action) {
        fActions.put(actionId, action);
    }

    public IAction getAction(String actionId) {
        IAction action = (IAction) fActions.get(actionId);
        if (action == null) {
            action = createAction(actionId);
            if (action == null)
                return null;
            if (action instanceof MergeViewerAction) {
                MergeViewerAction mva = (MergeViewerAction) action;
                if (mva.isContentDependent())
                    addTextListener(this);
                if (mva.isSelectionDependent())
                    addSelectionChangedListener(this);

                Utilities.initAction(action, fResourceBundle, "action." + actionId + "."); //$NON-NLS-1$ //$NON-NLS-2$
            }
            addAction(actionId, action);

        }
        if (action instanceof MergeViewerAction) {
            MergeViewerAction mva = (MergeViewerAction) action;
            if (mva.isEditableDependent() && !isEditable())
                return null;
        }
        return action;
    }

    protected IAction createAction(String actionId) {
        if (UNDO_ID.equals(actionId))
            return new TextOperationAction(UNDO, "org.eclipse.ui.edit.undo", true, false, true); //$NON-NLS-1$
        if (REDO_ID.equals(actionId))
            return new TextOperationAction(REDO, "org.eclipse.ui.edit.redo", true, false, true); //$NON-NLS-1$
        if (CUT_ID.equals(actionId))
            return new TextOperationAction(CUT, "org.eclipse.ui.edit.cut", true, true, false); //$NON-NLS-1$
        if (COPY_ID.equals(actionId))
            return new TextOperationAction(COPY, "org.eclipse.ui.edit.copy", false, true, false); //$NON-NLS-1$
        if (PASTE_ID.equals(actionId))
            return new TextOperationAction(PASTE, "org.eclipse.ui.edit.paste", true, false, false); //$NON-NLS-1$
        if (DELETE_ID.equals(actionId))
            return new TextOperationAction(DELETE, "org.eclipse.ui.edit.delete", true, false, false); //$NON-NLS-1$
        if (SELECT_ALL_ID.equals(actionId))
            return new TextOperationAction(SELECT_ALL, "org.eclipse.ui.edit.selectAll", false, false, false); //$NON-NLS-1$
        return null;
    }

    public void selectionChanged(SelectionChangedEvent event) {
        Iterator e = fActions.values().iterator();
        while (e.hasNext()) {
            Object next = e.next();
            if (next instanceof MergeViewerAction) {
                MergeViewerAction action = (MergeViewerAction) next;
                if (action.isSelectionDependent())
                    action.update();
            }
        }
    }

    public void textChanged(TextEvent event) {
        updateContentDependantActions();
    }

    void updateContentDependantActions() {
        Iterator e = fActions.values().iterator();
        while (e.hasNext()) {
            Object next = e.next();
            if (next instanceof MergeViewerAction) {
                MergeViewerAction action = (MergeViewerAction) next;
                if (action.isContentDependent())
                    action.update();
            }
        }
    }

    /*
     * Allows the viewer to add menus and/or tools to the context menu.
     */
    public void menuAboutToShow(IMenuManager menu) {

        menu.add(new Separator("undo")); //$NON-NLS-1$
        addMenu(menu, UNDO_ID);
        addMenu(menu, REDO_ID);
        menu.add(new GroupMarker("save")); //$NON-NLS-1$
        if (fAddSaveAction)
            addMenu(menu, SAVE_ID);
        menu.add(new Separator("file")); //$NON-NLS-1$

        menu.add(new Separator("ccp")); //$NON-NLS-1$
        addMenu(menu, CUT_ID);
        addMenu(menu, COPY_ID);
        addMenu(menu, PASTE_ID);
        addMenu(menu, DELETE_ID);
        addMenu(menu, SELECT_ALL_ID);

        menu.add(new Separator("edit")); //$NON-NLS-1$
        menu.add(new Separator("find")); //$NON-NLS-1$
        addMenu(menu, FIND_ID);

        menu.add(new Separator(IWorkbenchActionConstants.MB_ADDITIONS));

        menu.add(new Separator("text")); //$NON-NLS-1$
        for (Iterator iterator = textActions.iterator(); iterator.hasNext();) {
            IAction action = (IAction) iterator.next();
            menu.add(action);
        }

        menu.add(new Separator("rest")); //$NON-NLS-1$

        // update all actions
        // to get undo redo right
        updateActions();
    }

    private void addMenu(IMenuManager menu, String actionId) {
        IAction action = getAction(actionId);
        if (action != null)
            menu.add(action);
    }

    protected void handleDispose() {

        removeTextListener(this);
        removeSelectionChangedListener(this);
        EditorsUI.getPreferenceStore().removePropertyChangeListener(fPreferenceChangeListener);

        IOperationHistory history = getHistory();
        if (history != null)
            history.removeOperationHistoryListener(this);

        super.handleDispose();
    }

    /**
     * update all actions independent of their type
     * 
     */
    public void updateActions() {
        Iterator e = fActions.values().iterator();
        while (e.hasNext()) {
            Object next = e.next();
            if (next instanceof MergeViewerAction) {
                MergeViewerAction action = (MergeViewerAction) next;
                action.update();
            }
            if (next instanceof FindReplaceAction) {
                FindReplaceAction action = (FindReplaceAction) next;
                action.update();
            }
        }
    }

    public void configure(SourceViewerConfiguration configuration) {
        if (isConfigured)
            unconfigure();
        isConfigured = true;
        super.configure(configuration);
    }

    /**
     * specific implementation to support a vertical ruler
     * 
     * @param x
     * @param y
     * @param width
     * @param height
     */
    public void setBounds(int x, int y, int width, int height) {
        if (getControl() instanceof Composite) {
            ((Composite) getControl()).setBounds(x, y, width, height);
        } else {
            getTextWidget().setBounds(x, y, width, height);
        }
    }

    /**
     * handle show/hide line numbers from editor preferences
     * 
     * @param event
     */
    protected void handlePropertyChangeEvent(PropertyChangeEvent event) {

        String key = event.getProperty();

        if (key.equals(AbstractDecoratedTextEditorPreferenceConstants.EDITOR_LINE_NUMBER_RULER)) {
            boolean b = EditorsUI.getPreferenceStore()
                    .getBoolean(AbstractDecoratedTextEditorPreferenceConstants.EDITOR_LINE_NUMBER_RULER);
            if (b != fShowLineNumber) {
                toggleLineNumberRuler();
            }
        } else if (key.equals(AbstractDecoratedTextEditorPreferenceConstants.EDITOR_LINE_NUMBER_RULER_COLOR)) {
            updateLineNumberColumnPresentation(true);
        }
    }

    /**
     * Hides or shows line number ruler column based of preference setting
     */
    private void updateLineNumberRuler() {
        IVerticalRuler v = getVerticalRuler();
        if (v != null && v instanceof CompositeRuler) {
            CompositeRuler c = (CompositeRuler) v;

            if (!fShowLineNumber) {
                if (fLineNumberColumn != null) {
                    c.removeDecorator(fLineNumberColumn);
                }
            } else {
                if (fLineNumberColumn == null) {
                    fLineNumberColumn = new LineNumberRulerColumn();
                    updateLineNumberColumnPresentation(false);
                }
                c.addDecorator(0, fLineNumberColumn);
            }
        }
    }

    private void updateLineNumberColumnPresentation(boolean refresh) {
        if (fLineNumberColumn == null)
            return;
        RGB rgb = getColorFromStore(EditorsUI.getPreferenceStore(),
                AbstractDecoratedTextEditorPreferenceConstants.EDITOR_LINE_NUMBER_RULER_COLOR);
        if (rgb == null)
            rgb = new RGB(0, 0, 0);
        ISharedTextColors sharedColors = getSharedColors();
        fLineNumberColumn.setForeground(sharedColors.getColor(rgb));
        if (refresh) {
            fLineNumberColumn.redraw();
        }
    }

    private void layoutViewer() {
        Control parent = getControl();
        if (parent instanceof Composite && !parent.isDisposed())
            ((Composite) parent).layout(true);
    }

    private ISharedTextColors getSharedColors() {
        return EditorsUI.getSharedTextColors();
    }

    private RGB getColorFromStore(IPreferenceStore store, String key) {
        RGB rgb = null;
        if (store.contains(key)) {
            if (store.isDefault(key))
                rgb = PreferenceConverter.getDefaultColor(store, key);
            else
                rgb = PreferenceConverter.getColor(store, key);
        }
        return rgb;
    }

    /**
     * Toggles line number ruler column.
     */
    private void toggleLineNumberRuler() {
        fShowLineNumber = !fShowLineNumber;

        updateLineNumberRuler();
    }

    public void addTextAction(IAction textEditorPropertyAction) {
        textActions.add(textEditorPropertyAction);
    }

    public void addAction(String id, IAction action) {
        fActions.put(id, action);
    }

    private IOperationHistory getHistory() {
        if (PlatformUI.getWorkbench() == null) {
            return null;
        }
        return PlatformUI.getWorkbench().getOperationSupport().getOperationHistory();
    }

    public void historyNotification(OperationHistoryEvent event) {
        // This method updates the enablement of all content operations
        // when the undo history changes. It could be localized to UNDO and
        // REDO.
        IUndoContext context = getUndoContext();
        if (context != null && event.getOperation().hasContext(context)) {
            Display.getDefault().asyncExec(new Runnable() {
                public void run() {
                    updateContentDependantActions();
                }
            });
        }
    }

    private IUndoContext getUndoContext() {
        IUndoManager undoManager = getUndoManager();
        if (undoManager instanceof IUndoManagerExtension)
            return ((IUndoManagerExtension) undoManager).getUndoContext();
        return null;
    }
}