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

Java tutorial

Introduction

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

Source

/*******************************************************************************
 * Copyright (c) 2000, 2013 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
 *     Max Weninger (max.weninger@windriver.com) - Bug 131895 [Edit] Undo in compare
 *     Max Weninger (max.weninger@windriver.com) - Bug 72936 [Viewers] Show line numbers in comparision
 *******************************************************************************/
package org.eclipse.compare.internal;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.ResourceBundle;

import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.StyledText;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.Font;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.RGB;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;

import org.eclipse.core.commands.Command;
import org.eclipse.core.commands.IHandler;
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.core.runtime.Assert;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IAdaptable;
import org.eclipse.core.runtime.IProgressMonitor;

import org.eclipse.jface.action.GroupMarker;
import org.eclipse.jface.action.IAction;
import org.eclipse.jface.action.IMenuListener;
import org.eclipse.jface.action.IMenuManager;
import org.eclipse.jface.action.MenuManager;
import org.eclipse.jface.action.Separator;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.jface.preference.PreferenceConverter;
import org.eclipse.jface.util.IPropertyChangeListener;
import org.eclipse.jface.util.PropertyChangeEvent;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.ISelectionProvider;
import org.eclipse.jface.viewers.SelectionChangedEvent;

import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.IRegion;
import org.eclipse.jface.text.ITextListener;
import org.eclipse.jface.text.ITextOperationTarget;
import org.eclipse.jface.text.ITextSelection;
import org.eclipse.jface.text.ITextViewer;
import org.eclipse.jface.text.ITextViewerExtension5;
import org.eclipse.jface.text.IUndoManager;
import org.eclipse.jface.text.IUndoManagerExtension;
import org.eclipse.jface.text.Position;
import org.eclipse.jface.text.Region;
import org.eclipse.jface.text.TextEvent;
import org.eclipse.jface.text.source.IAnnotationModel;
import org.eclipse.jface.text.source.ISharedTextColors;
import org.eclipse.jface.text.source.LineNumberRulerColumn;
import org.eclipse.jface.text.source.SourceViewer;
import org.eclipse.jface.text.source.SourceViewerConfiguration;

import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.IEditorSite;
import org.eclipse.ui.IPropertyListener;
import org.eclipse.ui.IWorkbenchActionConstants;
import org.eclipse.ui.IWorkbenchCommandConstants;
import org.eclipse.ui.IWorkbenchPartSite;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.commands.ICommandService;
import org.eclipse.ui.menus.CommandContributionItem;
import org.eclipse.ui.menus.CommandContributionItemParameter;

import org.eclipse.ui.texteditor.AbstractDecoratedTextEditorPreferenceConstants;
import org.eclipse.ui.texteditor.ChangeEncodingAction;
import org.eclipse.ui.texteditor.FindReplaceAction;
import org.eclipse.ui.texteditor.IDocumentProvider;
import org.eclipse.ui.texteditor.IElementStateListener;
import org.eclipse.ui.texteditor.ITextEditor;

import org.eclipse.ui.editors.text.EditorsUI;

import org.eclipse.compare.ICompareContainer;

/**
 * Wraps a JFace SourceViewer and add some convenience methods.
 */
public class MergeSourceViewer
        implements ISelectionChangedListener, ITextListener, IMenuListener, IOperationHistoryListener, IAdaptable {

    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 FIND_ID = "find"; //$NON-NLS-1$
    public static final String GOTO_LINE_ID = "gotoLine"; //$NON-NLS-1$
    public static final String CHANGE_ENCODING_ID = "changeEncoding"; //$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())
                getSourceViewer().doOperation(fOperationCode);
        }

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

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

    /**
     * TODO: The only purpose of this class is to provide "Go to Line" action in
     * TextMergeViewer. The adapter should be removed as soon as we implement
     * embedded TextEditor in a similar way JDT has it done for Java compare.
     */
    class TextEditorAdapter implements ITextEditor {

        public void close(boolean save) {
            // defining interface method
        }

        public void doRevertToSaved() {
            // defining interface method
        }

        public IAction getAction(String actionId) {
            // defining interface method
            return null;
        }

        public IDocumentProvider getDocumentProvider() {
            return new IDocumentProvider() {

                public void aboutToChange(Object element) {
                    // defining interface method
                }

                public void addElementStateListener(IElementStateListener listener) {
                    // defining interface method
                }

                public boolean canSaveDocument(Object element) {
                    // defining interface method
                    return false;
                }

                public void changed(Object element) {
                    // defining interface method
                }

                public void connect(Object element) throws CoreException {
                    // defining interface method
                }

                public void disconnect(Object element) {
                    // defining interface method
                }

                public IAnnotationModel getAnnotationModel(Object element) {
                    // defining interface method
                    return null;
                }

                public IDocument getDocument(Object element) {
                    return MergeSourceViewer.this.getSourceViewer().getDocument();
                }

                public long getModificationStamp(Object element) {
                    // defining interface method
                    return 0;
                }

                public long getSynchronizationStamp(Object element) {
                    // defining interface method
                    return 0;
                }

                public boolean isDeleted(Object element) {
                    // defining interface method
                    return false;
                }

                public boolean mustSaveDocument(Object element) {
                    // defining interface method
                    return false;
                }

                public void removeElementStateListener(IElementStateListener listener) {
                    // defining interface method
                }

                public void resetDocument(Object element) throws CoreException {
                    // defining interface method
                }

                public void saveDocument(IProgressMonitor monitor, Object element, IDocument document,
                        boolean overwrite) throws CoreException {
                    // defining interface method
                }
            };
        }

        public IRegion getHighlightRange() {
            // defining interface method
            return null;
        }

        public ISelectionProvider getSelectionProvider() {
            return MergeSourceViewer.this.getSourceViewer().getSelectionProvider();
        }

        public boolean isEditable() {
            // defining interface method
            return false;
        }

        public void removeActionActivationCode(String actionId) {
            // defining interface method
        }

        public void resetHighlightRange() {
            // defining interface method
        }

        public void selectAndReveal(int start, int length) {
            selectAndReveal(start, length, start, length);
        }

        /*
         * @see org.eclipse.ui.texteditor.AbstractTextEditor#selectAndReveal(int, int, int, int)
         */
        private void selectAndReveal(int selectionStart, int selectionLength, int revealStart, int revealLength) {

            ISelection selection = getSelectionProvider().getSelection();
            if (selection instanceof ITextSelection) {
                ITextSelection textSelection = (ITextSelection) selection;
                if (textSelection.getOffset() != 0 || textSelection.getLength() != 0)
                    markInNavigationHistory();
            }

            StyledText widget = MergeSourceViewer.this.getSourceViewer().getTextWidget();
            widget.setRedraw(false);
            {
                adjustHighlightRange(revealStart, revealLength);
                MergeSourceViewer.this.getSourceViewer().revealRange(revealStart, revealLength);

                MergeSourceViewer.this.getSourceViewer().setSelectedRange(selectionStart, selectionLength);

                markInNavigationHistory();
            }
            widget.setRedraw(true);
        }

        /*
         * @see org.eclipse.ui.texteditor.AbstractTextEditor#markInNavigationHistory()
         */
        private void markInNavigationHistory() {
            getSite().getPage().getNavigationHistory().markLocation(this);
        }

        /*
         * @see org.eclipse.ui.texteditor.AbstractTextEditor#adjustHighlightRange(int, int)
         */
        private void adjustHighlightRange(int offset, int length) {

            if (MergeSourceViewer.this instanceof ITextViewerExtension5) {
                ITextViewerExtension5 extension = (ITextViewerExtension5) MergeSourceViewer.this;
                extension.exposeModelRange(new Region(offset, length));
            } else if (!isVisible(MergeSourceViewer.this.getSourceViewer(), offset, length)) {
                MergeSourceViewer.this.getSourceViewer().resetVisibleRegion();
            }
        }

        /*
         * @see org.eclipse.ui.texteditor.AbstractTextEditor#isVisible(ISourceViewer, int, int)
         */
        private /*static*/ final boolean isVisible(ITextViewer viewer, int offset, int length) {
            if (viewer instanceof ITextViewerExtension5) {
                ITextViewerExtension5 extension = (ITextViewerExtension5) viewer;
                IRegion overlap = extension.modelRange2WidgetRange(new Region(offset, length));
                return overlap != null;
            }
            return viewer.overlapsWithVisibleRegion(offset, length);
        }

        public void setAction(String actionID, IAction action) {
            // defining interface method
        }

        public void setActionActivationCode(String actionId, char activationCharacter, int activationKeyCode,
                int activationStateMask) {
            // defining interface method
        }

        public void setHighlightRange(int offset, int length, boolean moveCursor) {
            // defining interface method
        }

        public void showHighlightRangeOnly(boolean showHighlightRangeOnly) {
            // defining interface method
        }

        public boolean showsHighlightRangeOnly() {
            // defining interface method
            return false;
        }

        public IEditorInput getEditorInput() {
            if (MergeSourceViewer.this.fContainer.getWorkbenchPart() instanceof IEditorPart)
                return ((IEditorPart) MergeSourceViewer.this.fContainer.getWorkbenchPart()).getEditorInput();
            return null;
        }

        public IEditorSite getEditorSite() {
            // defining interface method
            return null;
        }

        public void init(IEditorSite site, IEditorInput input) throws PartInitException {
            // defining interface method
        }

        public void addPropertyListener(IPropertyListener listener) {
            // defining interface method
        }

        public void createPartControl(Composite parent) {
            // defining interface method
        }

        public void dispose() {
            // defining interface method
        }

        public IWorkbenchPartSite getSite() {
            return MergeSourceViewer.this.fContainer.getWorkbenchPart().getSite();
        }

        public String getTitle() {
            // defining interface method
            return null;
        }

        public Image getTitleImage() {
            // defining interface method
            return null;
        }

        public String getTitleToolTip() {
            // defining interface method
            return null;
        }

        public void removePropertyListener(IPropertyListener listener) {
            // defining interface method
        }

        public void setFocus() {
            // defining interface method
        }

        public Object getAdapter(Class adapter) {
            // defining interface method
            return null;
        }

        public void doSave(IProgressMonitor monitor) {
            // defining interface method
        }

        public void doSaveAs() {
            // defining interface method
        }

        public boolean isDirty() {
            // defining interface method
            return false;
        }

        public boolean isSaveAsAllowed() {
            // defining interface method
            return false;
        }

        public boolean isSaveOnCloseNeeded() {
            // defining interface method
            return false;
        }
    }

    private ResourceBundle fResourceBundle;
    private ICompareContainer fContainer;
    private SourceViewer fSourceViewer;
    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();
    private CommandContributionItem fSaveContributionItem;

    public MergeSourceViewer(SourceViewer sourceViewer, ResourceBundle bundle, ICompareContainer container) {
        Assert.isNotNull(sourceViewer);
        fSourceViewer = sourceViewer;
        fResourceBundle = bundle;
        fContainer = container;

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

        // 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);

        // don't add save when in a dialog, IWorkbenchPart is null in dialog containers
        fAddSaveAction = fContainer.getWorkbenchPart() != null;
    }

    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 = getSourceViewer().getTextWidget();
        if (te != null)
            te.setFont(font);
        if (fLineNumberColumn != null) {
            fLineNumberColumn.setFont(font);
            layoutViewer();
        }
    }

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

    public void setForegroundColor(Color color) {
        StyledText te = getSourceViewer().getTextWidget();
        if (te != null)
            te.setForeground(color);
    }

    public void setEnabled(boolean enabled) {
        if (enabled != fEnabled) {
            fEnabled = enabled;
            StyledText c = getSourceViewer().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 = getSourceViewer().getTextWidget();
        return t != null && !t.isDisposed();
    }

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

    public void setLineBackground(Position position, Color c) {
        StyledText t = getSourceViewer().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 = getSourceViewer().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 = getSourceViewer().getVisibleRegion();

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

        IDocument doc = getSourceViewer().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 = getSourceViewer().getTextWidget();
        Rectangle clArea = te.getClientArea();
        if (!clArea.isEmpty())
            return clArea.height / te.getLineHeight();
        return 0;
    }

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

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

    public int getVerticalScrollOffset() {
        StyledText st = getSourceViewer().getTextWidget();
        int lineHeight = st.getLineHeight();
        return getSourceViewer().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 = getSourceViewer().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 = getSourceViewer().getTopIndex();
            if (cp != line)
                getSourceViewer().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())
                    getSourceViewer().addTextListener(this);
                if (mva.isSelectionDependent())
                    getSourceViewer().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() && !getSourceViewer().isEditable())
                return null;
        }
        return action;
    }

    protected IAction createAction(String actionId) {
        if (UNDO_ID.equals(actionId))
            return new TextOperationAction(ITextOperationTarget.UNDO, IWorkbenchCommandConstants.EDIT_UNDO, true,
                    false, true);
        if (REDO_ID.equals(actionId))
            return new TextOperationAction(ITextOperationTarget.REDO, IWorkbenchCommandConstants.EDIT_REDO, true,
                    false, true);
        if (CUT_ID.equals(actionId))
            return new TextOperationAction(ITextOperationTarget.CUT, IWorkbenchCommandConstants.EDIT_CUT, true,
                    true, false);
        if (COPY_ID.equals(actionId))
            return new TextOperationAction(ITextOperationTarget.COPY, IWorkbenchCommandConstants.EDIT_COPY, false,
                    true, false);
        if (PASTE_ID.equals(actionId))
            return new TextOperationAction(ITextOperationTarget.PASTE, IWorkbenchCommandConstants.EDIT_PASTE, true,
                    false, false);
        if (DELETE_ID.equals(actionId))
            return new TextOperationAction(ITextOperationTarget.DELETE, IWorkbenchCommandConstants.EDIT_DELETE,
                    true, false, false);
        if (SELECT_ALL_ID.equals(actionId))
            return new TextOperationAction(ITextOperationTarget.SELECT_ALL,
                    IWorkbenchCommandConstants.EDIT_SELECT_ALL, false, false, false);
        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)
            addSave(menu);
        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$
        addMenu(menu, CHANGE_ENCODING_ID);
        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);
    }

    private void addSave(IMenuManager menu) {
        ICommandService commandService = (ICommandService) fContainer.getWorkbenchPart().getSite()
                .getService(ICommandService.class);
        final Command command = commandService.getCommand(IWorkbenchCommandConstants.FILE_SAVE);

        final IHandler handler = command.getHandler();
        if (handler != null) {
            if (fSaveContributionItem == null) {
                fSaveContributionItem = new CommandContributionItem(
                        new CommandContributionItemParameter(fContainer.getWorkbenchPart().getSite(), null,
                                command.getId(), CommandContributionItem.STYLE_PUSH));
            }
            // save is an editable dependent action, ie add only when edit
            // is possible
            if (handler.isHandled() && getSourceViewer().isEditable())
                menu.add(fSaveContributionItem);
        }
    }

    /**
     * The viewer is no longer part of the UI, it's a wrapper only. The disposal
     * doesn't take place while releasing the editor pane's children. The method
     * have to be called it manually. The wrapped viewer is disposed as a
     * regular viewer, while disposing the UI.
     */
    public void dispose() {
        getSourceViewer().removeTextListener(this);
        getSourceViewer().removeSelectionChangedListener(this);
        EditorsUI.getPreferenceStore().removePropertyChangeListener(fPreferenceChangeListener);

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

    /**
     * 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();
            } else if (next instanceof FindReplaceAction) {
                FindReplaceAction action = (FindReplaceAction) next;
                action.update();
            } else if (next instanceof ChangeEncodingAction) {
                ChangeEncodingAction action = (ChangeEncodingAction) next;
                action.update();
            }
        }
    }

    public void configure(SourceViewerConfiguration configuration) {
        if (isConfigured)
            getSourceViewer().unconfigure();
        isConfigured = true;
        getSourceViewer().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 (getSourceViewer().getControl() instanceof Composite) {
            ((Composite) getSourceViewer().getControl()).setBounds(x, y, width, height);
        } else {
            getSourceViewer().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() {
        if (!fShowLineNumber) {
            if (fLineNumberColumn != null) {
                getSourceViewer().removeVerticalRulerColumn(fLineNumberColumn);
            }
        } else {
            if (fLineNumberColumn == null) {
                fLineNumberColumn = new LineNumberRulerColumn();
                updateLineNumberColumnPresentation(false);
            }
            getSourceViewer().addVerticalRulerColumn(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 = getSourceViewer().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 boolean removeTextAction(IAction textEditorPropertyAction) {
        return textActions.remove(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 = getSourceViewer().getUndoManager();
        if (undoManager instanceof IUndoManagerExtension)
            return ((IUndoManagerExtension) undoManager).getUndoContext();
        return null;
    }

    /**
     * @return the wrapped viewer
     */
    public SourceViewer getSourceViewer() {
        return fSourceViewer;
    }

    public Object getAdapter(Class adapter) {
        if (adapter == ITextEditor.class) {
            return new TextEditorAdapter();
        }
        return null;
    }
}