Package org.eclipse.ui.texteditor

Source Code of org.eclipse.ui.texteditor.AbstractTextEditor

/*******************************************************************************
* Copyright (c) 2000, 2012 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
*     Chris.Dennis@invidi.com - http://bugs.eclipse.org/bugs/show_bug.cgi?id=29027
*     Michel Ishizuka (cqw10305@nifty.com) - http://bugs.eclipse.org/bugs/show_bug.cgi?id=68963
*     Genady Beryozkin, me@genady.org - https://bugs.eclipse.org/bugs/show_bug.cgi?id=11668
*     Benjamin Muskalla <b.muskalla@gmx.net> - https://bugs.eclipse.org/bugs/show_bug.cgi?id=41573
*     Stephan Wahlbrink <stephan.wahlbrink@walware.de> - Wrong operations mode/feedback for text drag over/drop in text editors - https://bugs.eclipse.org/bugs/show_bug.cgi?id=206043
*     Tom Eicher (Avaloq Evolution AG) - block selection mode
*     Nick Sandonato <nsandona@us.ibm.com> - [implementation] AbstractTextEditor does not prompt when out of sync in MultiPageEditorPart - http://bugs.eclipse.org/337719
*******************************************************************************/
package org.eclipse.ui.texteditor;

import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.ResourceBundle;

import org.osgi.framework.Bundle;

import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.BusyIndicator;
import org.eclipse.swt.custom.ST;
import org.eclipse.swt.custom.StyledText;
import org.eclipse.swt.custom.VerifyKeyListener;
import org.eclipse.swt.dnd.DND;
import org.eclipse.swt.dnd.DragSource;
import org.eclipse.swt.dnd.DragSourceAdapter;
import org.eclipse.swt.dnd.DragSourceEvent;
import org.eclipse.swt.dnd.DropTargetAdapter;
import org.eclipse.swt.dnd.DropTargetEvent;
import org.eclipse.swt.dnd.DropTargetListener;
import org.eclipse.swt.dnd.TextTransfer;
import org.eclipse.swt.dnd.Transfer;
import org.eclipse.swt.events.KeyEvent;
import org.eclipse.swt.events.KeyListener;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.events.MouseListener;
import org.eclipse.swt.events.VerifyEvent;
import org.eclipse.swt.events.VerifyListener;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.Font;
import org.eclipse.swt.graphics.FontData;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.ImageData;
import org.eclipse.swt.graphics.PaletteData;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.RGB;
import org.eclipse.swt.widgets.Caret;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.swt.widgets.Shell;

import org.eclipse.core.commands.operations.IOperationApprover;
import org.eclipse.core.commands.operations.IOperationHistory;
import org.eclipse.core.commands.operations.IUndoContext;
import org.eclipse.core.commands.operations.OperationHistoryFactory;

import org.eclipse.core.runtime.Assert;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IConfigurationElement;
import org.eclipse.core.runtime.ILog;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.SafeRunner;
import org.eclipse.core.runtime.Status;

import org.eclipse.text.undo.DocumentUndoManagerRegistry;
import org.eclipse.text.undo.IDocumentUndoManager;

import org.eclipse.jface.action.Action;
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.IStatusLineManager;
import org.eclipse.jface.action.MenuManager;
import org.eclipse.jface.action.Separator;
import org.eclipse.jface.dialogs.ErrorDialog;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.operation.IRunnableWithProgress;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.jface.preference.PreferenceConverter;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.jface.resource.JFaceResources;
import org.eclipse.jface.util.IPropertyChangeListener;
import org.eclipse.jface.util.PropertyChangeEvent;
import org.eclipse.jface.util.SafeRunnable;
import org.eclipse.jface.viewers.IPostSelectionProvider;
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.viewers.StructuredSelection;
import org.eclipse.jface.window.IShellProvider;

import org.eclipse.jface.text.AbstractInformationControlManager;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.DefaultLineTracker;
import org.eclipse.jface.text.DocumentEvent;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.IDocumentListener;
import org.eclipse.jface.text.IFindReplaceTarget;
import org.eclipse.jface.text.IFindReplaceTargetExtension;
import org.eclipse.jface.text.IInformationControlCreator;
import org.eclipse.jface.text.IMarkRegionTarget;
import org.eclipse.jface.text.IRegion;
import org.eclipse.jface.text.IRewriteTarget;
import org.eclipse.jface.text.ISelectionValidator;
import org.eclipse.jface.text.ITextHover;
import org.eclipse.jface.text.ITextInputListener;
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.ITextViewerExtension;
import org.eclipse.jface.text.ITextViewerExtension2;
import org.eclipse.jface.text.ITextViewerExtension4;
import org.eclipse.jface.text.ITextViewerExtension5;
import org.eclipse.jface.text.ITextViewerExtension6;
import org.eclipse.jface.text.ITextViewerExtension7;
import org.eclipse.jface.text.ITextViewerExtension8;
import org.eclipse.jface.text.ITextViewerExtension8.EnrichMode;
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.TabsToSpacesConverter;
import org.eclipse.jface.text.TextEvent;
import org.eclipse.jface.text.TextSelection;
import org.eclipse.jface.text.TextUtilities;
import org.eclipse.jface.text.hyperlink.HyperlinkManager;
import org.eclipse.jface.text.hyperlink.IHyperlinkDetector;
import org.eclipse.jface.text.information.IInformationProvider;
import org.eclipse.jface.text.information.IInformationProviderExtension2;
import org.eclipse.jface.text.link.LinkedModeModel;
import org.eclipse.jface.text.link.LinkedPosition;
import org.eclipse.jface.text.quickassist.IQuickAssistAssistant;
import org.eclipse.jface.text.revisions.RevisionInformation;
import org.eclipse.jface.text.source.Annotation;
import org.eclipse.jface.text.source.CompositeRuler;
import org.eclipse.jface.text.source.IAnnotationHover;
import org.eclipse.jface.text.source.IAnnotationModel;
import org.eclipse.jface.text.source.ISourceViewer;
import org.eclipse.jface.text.source.ISourceViewerExtension3;
import org.eclipse.jface.text.source.ISourceViewerExtension4;
import org.eclipse.jface.text.source.IVerticalRuler;
import org.eclipse.jface.text.source.IVerticalRulerColumn;
import org.eclipse.jface.text.source.IVerticalRulerExtension;
import org.eclipse.jface.text.source.IVerticalRulerInfo;
import org.eclipse.jface.text.source.SourceViewer;
import org.eclipse.jface.text.source.SourceViewerConfiguration;
import org.eclipse.jface.text.source.VerticalRuler;

import org.eclipse.ui.IActionBars;
import org.eclipse.ui.IEditorDescriptor;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.IEditorRegistry;
import org.eclipse.ui.IEditorSite;
import org.eclipse.ui.IKeyBindingService;
import org.eclipse.ui.IMemento;
import org.eclipse.ui.INavigationLocation;
import org.eclipse.ui.INavigationLocationProvider;
import org.eclipse.ui.IPartListener;
import org.eclipse.ui.IPartService;
import org.eclipse.ui.IPersistableEditor;
import org.eclipse.ui.IReusableEditor;
import org.eclipse.ui.ISaveablesLifecycleListener;
import org.eclipse.ui.ISaveablesSource;
import org.eclipse.ui.IWindowListener;
import org.eclipse.ui.IWorkbenchActionConstants;
import org.eclipse.ui.IWorkbenchCommandConstants;
import org.eclipse.ui.IWorkbenchPart;
import org.eclipse.ui.IWorkbenchWindow;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.Saveable;
import org.eclipse.ui.SaveablesLifecycleEvent;
import org.eclipse.ui.actions.ActionFactory;
import org.eclipse.ui.actions.ActionFactory.IWorkbenchAction;
import org.eclipse.ui.actions.CommandNotMappedException;
import org.eclipse.ui.actions.ContributedAction;
import org.eclipse.ui.dialogs.PropertyDialogAction;
import org.eclipse.ui.dnd.IDragAndDropService;
import org.eclipse.ui.internal.texteditor.EditPosition;
import org.eclipse.ui.internal.texteditor.FocusedInformationPresenter;
import org.eclipse.ui.internal.texteditor.NLSUtility;
import org.eclipse.ui.internal.texteditor.TextEditorPlugin;
import org.eclipse.ui.internal.texteditor.rulers.StringSetSerializer;
import org.eclipse.ui.operations.LinearUndoViolationUserApprover;
import org.eclipse.ui.operations.NonLocalUndoUserApprover;
import org.eclipse.ui.operations.OperationHistoryActionHandler;
import org.eclipse.ui.operations.RedoActionHandler;
import org.eclipse.ui.operations.UndoActionHandler;
import org.eclipse.ui.part.EditorPart;

import org.eclipse.ui.texteditor.rulers.IColumnSupport;
import org.eclipse.ui.texteditor.rulers.IContributedRulerColumn;
import org.eclipse.ui.texteditor.rulers.RulerColumnDescriptor;
import org.eclipse.ui.texteditor.rulers.RulerColumnPreferenceAdapter;
import org.eclipse.ui.texteditor.rulers.RulerColumnRegistry;


/**
* Abstract base implementation of a text editor.
* <p>
* Subclasses are responsible for configuring the editor appropriately. The standard text editor,
* <code>TextEditor</code>, is one such example.
* </p>
* <p>
* If a subclass calls {@linkplain #setEditorContextMenuId(String) setEditorContextMenuId} the
* argument is used as the id under which the editor's context menu is registered for extensions. If
* no id is set, the context menu is registered under <b>[editor_id].EditorContext</b> whereby
* [editor_id] is replaced with the editor's part id. If the editor is instructed to run in version
* 1.0 context menu registration compatibility mode, the latter form of the registration even
* happens if a context menu id has been set via {@linkplain #setEditorContextMenuId(String)
* setEditorContextMenuId}. If no id is set while in compatibility mode, the menu is registered
* under {@link #DEFAULT_EDITOR_CONTEXT_MENU_ID}.
* </p>
* <p>
* If a subclass calls {@linkplain #setRulerContextMenuId(String) setRulerContextMenuId} the
* argument is used as the id under which the ruler's context menu is registered for extensions. If
* no id is set, the context menu is registered under <b>[editor_id].RulerContext</b> whereby
* [editor_id] is replaced with the editor's part id. If the editor is instructed to run in version
* 1.0 context menu registration compatibility mode, the latter form of the registration even
* happens if a context menu id has been set via {@linkplain #setRulerContextMenuId(String)
* setRulerContextMenuId}. If no id is set while in compatibility mode, the menu is registered under
* {@link #DEFAULT_RULER_CONTEXT_MENU_ID}.
* </p>
* <p>
* As of 3.5, contributers can contribute editor and ruler context menu actions to all subclasses of
* this class by using {@link #COMMON_EDITOR_CONTEXT_MENU_ID} and
* {@link #COMMON_RULER_CONTEXT_MENU_ID}.
* </p>
*/
public abstract class AbstractTextEditor extends EditorPart implements ITextEditor, IReusableEditor, ITextEditorExtension, ITextEditorExtension2, ITextEditorExtension3, ITextEditorExtension4, ITextEditorExtension5, INavigationLocationProvider, ISaveablesSource, IPersistableEditor {

  /**
   * Tag used in xml configuration files to specify editor action contributions.
   * Current value: <code>editorContribution</code>
   * @since 2.0
   */
  private static final String TAG_CONTRIBUTION_TYPE= "editorContribution"; //$NON-NLS-1$

  /**
   * Tag used in the {@link IMemento} when saving and restoring the editor's selection offset.
   *
   * @see #saveState(IMemento)
   * @see #restoreState(IMemento)
   * @see #doRestoreState(IMemento)
   * @since 3.3
   */
  protected static final String TAG_SELECTION_OFFSET= "selectionOffset"; //$NON-NLS-1$

  /**
   * Tag used in the {@link IMemento} when saving and restoring the editor's selection length.
   *
   * @see #saveState(IMemento)
   * @see #restoreState(IMemento)
   * @see #doRestoreState(IMemento)
   * @since 3.3
   */
  protected static final String TAG_SELECTION_LENGTH= "selectionLength"; //$NON-NLS-1$

  /**
   * Tag used in the {@link IMemento} when saving and restoring the editor's top pixel value.
   *
   * @see #saveState(IMemento)
   * @see #restoreState(IMemento)
   * @see #doRestoreState(IMemento)
   * @since 3.6
   */
  protected static final String TAG_SELECTION_TOP_PIXEL= "selectionTopPixel"; //$NON-NLS-1$

  /**
   * Tag used in the {@link IMemento} when saving and restoring the editor's horizontal pixel
   * value.
   *
   * @see #saveState(IMemento)
   * @see #restoreState(IMemento)
   * @see #doRestoreState(IMemento)
   * @since 3.6
   */
  protected static final String TAG_SELECTION_HORIZONTAL_PIXEL= "selectionHorizontalPixel"; //$NON-NLS-1$


  /**
   * The caret width for the wide (double) caret.
   * See https://bugs.eclipse.org/bugs/show_bug.cgi?id=21715.
   * Value: {@value}
   * @since 3.0
   */
  private static final int WIDE_CARET_WIDTH= 2;

  /**
   * The caret width for the narrow (single) caret.
   * See https://bugs.eclipse.org/bugs/show_bug.cgi?id=21715.
   * Value: {@value}
   * @since 3.0
   */
  private static final int SINGLE_CARET_WIDTH= 1;

  /**
   * The symbolic name of the block seletion mode font.
   *
   * @since 3.5
   */
  private static final String BLOCK_SELECTION_MODE_FONT= "org.eclipse.ui.workbench.texteditor.blockSelectionModeFont"; //$NON-NLS-1$

  /**
   * The text input listener.
   *
   * @see ITextInputListener
   * @since 2.1
   */
  private static class TextInputListener implements ITextInputListener {
    /** Indicates whether the editor input changed during the process of state validation. */
    public boolean inputChanged;

    /* Detectors for editor input changes during the process of state validation. */
    public void inputDocumentAboutToBeChanged(IDocument oldInput, IDocument newInput) {}
    public void inputDocumentChanged(IDocument oldInput, IDocument newInput) { inputChanged= true; }
  }

  /**
   * Internal element state listener.
   */
  class ElementStateListener implements IElementStateListener, IElementStateListenerExtension {

      /**
       * Internal <code>VerifyListener</code> for performing the state validation of the
       * editor input in case of the first attempted manipulation via typing on the keyboard.
       * @since 2.0
       */
      class Validator implements VerifyListener {
        /*
         * @see VerifyListener#verifyText(org.eclipse.swt.events.VerifyEvent)
         */
        public void verifyText(VerifyEvent e) {
          IDocument document= getDocumentProvider().getDocument(getEditorInput());
          final boolean[] documentChanged= new boolean[1];
          IDocumentListener listener= new IDocumentListener() {
            public void documentAboutToBeChanged(DocumentEvent event) {
            }
            public void documentChanged(DocumentEvent event) {
              documentChanged[0]= true;
            }
          };
          try {
            if (document != null)
              document.addDocumentListener(listener);
            if (! validateEditorInputState() || documentChanged[0])
              e.doit= false;
          } finally {
            if (document != null)
              document.removeDocumentListener(listener);
          }
        }
      }

    /**
     * The listener's validator.
     * @since 2.0
     */
    private Validator fValidator;
    /**
     * The display used for posting runnable into the UI thread.
     * @since 3.0
     */
    private Display fDisplay;

    /*
     * @see IElementStateListenerExtension#elementStateValidationChanged(Object, boolean)
     * @since 2.0
     */
    public void elementStateValidationChanged(final Object element, final boolean isStateValidated) {
      if (element != null && element.equals(getEditorInput())) {
        Runnable r= new Runnable() {
          public void run() {
            enableSanityChecking(true);
            if (isStateValidated) {
              if (fValidator != null) {
                ISourceViewer viewer= fSourceViewer;
                if (viewer != null) {
                  StyledText textWidget= viewer.getTextWidget();
                  if (textWidget != null && !textWidget.isDisposed())
                    textWidget.removeVerifyListener(fValidator);
                  fValidator= null;
                }
              }
              enableStateValidation(false);
            } else if (!isStateValidated && fValidator == null) {
              ISourceViewer viewer= fSourceViewer;
              if (viewer != null) {
                StyledText textWidget= viewer.getTextWidget();
                if (textWidget != null && !textWidget.isDisposed()) {
                  fValidator= new Validator();
                  enableStateValidation(true);
                  textWidget.addVerifyListener(fValidator);
                }
              }
            }
          }
        };
        execute(r, false);
      }
    }


    /*
     * @see IElementStateListener#elementDirtyStateChanged(Object, boolean)
     */
    public void elementDirtyStateChanged(Object element, boolean isDirty) {
      if (element != null && element.equals(getEditorInput())) {
        Runnable r= new Runnable() {
          public void run() {
            enableSanityChecking(true);
            firePropertyChange(PROP_DIRTY);
          }
        };
        execute(r, false);
      }
    }

    /*
     * @see IElementStateListener#elementContentAboutToBeReplaced(Object)
     */
    public void elementContentAboutToBeReplaced(Object element) {
      if (element != null && element.equals(getEditorInput())) {
        Runnable r= new Runnable() {
          public void run() {
            enableSanityChecking(true);
            rememberSelection();
            resetHighlightRange();
          }
        };
        execute(r, false);
      }
    }

    /*
     * @see IElementStateListener#elementContentReplaced(Object)
     */
    public void elementContentReplaced(Object element) {
      if (element != null && element.equals(getEditorInput())) {
        Runnable r= new Runnable() {
          public void run() {
            enableSanityChecking(true);
            firePropertyChange(PROP_DIRTY);
            restoreSelection();
            handleElementContentReplaced();
          }
        };
        execute(r, false);
      }
    }

    /*
     * @see IElementStateListener#elementDeleted(Object)
     */
    public void elementDeleted(Object deletedElement) {
      if (deletedElement != null && deletedElement.equals(getEditorInput())) {
        Runnable r= new Runnable() {
          public void run() {
            enableSanityChecking(true);
            close(false);
          }
        };
        execute(r, false);
      }
    }

    /*
     * @see IElementStateListener#elementMoved(Object, Object)
     */
    public void elementMoved(final Object originalElement, final Object movedElement) {
      if (originalElement != null && originalElement.equals(getEditorInput())) {
        final boolean doValidationAsync= Display.getCurrent() != null;
        Runnable r= new Runnable() {
          public void run() {
            enableSanityChecking(true);

            if (fSourceViewer == null)
              return;

            if (!canHandleMove((IEditorInput) originalElement, (IEditorInput) movedElement)) {
              close(true);
              return;
            }

            if (movedElement == null || movedElement instanceof IEditorInput) {
              rememberSelection();

              final IDocumentProvider d= getDocumentProvider();
              final String previousContent;
              IDocumentUndoManager previousUndoManager=null;
              IDocument changed= null;
              boolean wasDirty= isDirty();
              changed= d.getDocument(getEditorInput());
              if (changed != null) {
                if (wasDirty)
                  previousContent= changed.get();
                else
                  previousContent= null;

                previousUndoManager= DocumentUndoManagerRegistry.getDocumentUndoManager(changed);
                if (previousUndoManager != null)
                  previousUndoManager.connect(this);
              }
              else
                previousContent= null;

              setInput((IEditorInput) movedElement);

              // The undo manager needs to be replaced with one for the new document.
              // Transfer the undo history and then disconnect from the old undo manager.
              if (previousUndoManager != null) {
                IDocument newDocument= getDocumentProvider().getDocument(movedElement);
                if (newDocument != null) {
                  IDocumentUndoManager newUndoManager= DocumentUndoManagerRegistry.getDocumentUndoManager(newDocument);
                  if (newUndoManager != null)
                    newUndoManager.transferUndoHistory(previousUndoManager);
                }
                previousUndoManager.disconnect(this);
              }

              if (wasDirty && changed != null) {
                Runnable r2= new Runnable() {
                  public void run() {
                    validateState(getEditorInput());
                    d.getDocument(getEditorInput()).set(previousContent);
                    updateStatusField(ITextEditorActionConstants.STATUS_CATEGORY_ELEMENT_STATE);
                    restoreSelection();
                  }
                };
                execute(r2, doValidationAsync);
              } else
                restoreSelection();

            }
          }
        };
        execute(r, false);
      }
    }

    /*
     * @see IElementStateListenerExtension#elementStateChanging(Object)
     * @since 2.0
     */
    public void elementStateChanging(Object element) {
      if (element != null && element.equals(getEditorInput()))
        enableSanityChecking(false);
    }

    /*
     * @see IElementStateListenerExtension#elementStateChangeFailed(Object)
     * @since 2.0
     */
    public void elementStateChangeFailed(Object element) {
      if (element != null && element.equals(getEditorInput()))
        enableSanityChecking(true);
    }

    /**
     * Executes the given runnable in the UI thread.
     * <p>
     * See https://bugs.eclipse.org/bugs/show_bug.cgi?id=76765 for details
     * about why the parameter <code>postAsync</code> has been
     * introduced in the course of 3.1.
     *
     * @param runnable runnable to be executed
     * @param postAsync <code>true</code> if the runnable must be posted asynchronous, <code>false</code> otherwise
     * @since 3.0
     */
    private void execute(Runnable runnable, boolean postAsync) {
      if (postAsync || Display.getCurrent() == null) {
        if (fDisplay == null)
          fDisplay= getSite().getShell().getDisplay();
        fDisplay.asyncExec(runnable);
      } else
        runnable.run();
    }
  }

  /**
   * Internal text listener for updating all content dependent
   * actions. The updating is done asynchronously.
   */
  class TextListener implements ITextListener, ITextInputListener {

    /** The posted updater code. */
    private Runnable fRunnable= new Runnable() {
      public void run() {
        fIsRunnablePosted= false;

        if (fSourceViewer != null) {
          updateContentDependentActions();

          // remember the last edit position
          if (isDirty() && fUpdateLastEditPosition) {
            fUpdateLastEditPosition= false;
            ISelection sel= getSelectionProvider().getSelection();
            IEditorInput input= getEditorInput();
            IDocument document= getDocumentProvider().getDocument(input);

            if (fLocalLastEditPosition != null) {
              document.removePosition(fLocalLastEditPosition);
              fLocalLastEditPosition= null;
            }

            if (sel instanceof ITextSelection && !sel.isEmpty()) {
              ITextSelection s= (ITextSelection) sel;
              fLocalLastEditPosition= new Position(s.getOffset(), s.getLength());
              try {
                document.addPosition(fLocalLastEditPosition);
              } catch (BadLocationException ex) {
                fLocalLastEditPosition= null;
              }
            }
            TextEditorPlugin.getDefault().setLastEditPosition(new EditPosition(input, getEditorSite().getId(), fLocalLastEditPosition));
          }
        }
      }
    };

    /** Display used for posting the updater code. */
    private Display fDisplay;
    /**
     * The editor's last edit position
     * @since 3.0
     */
    private Position fLocalLastEditPosition;
    /**
     * Has the runnable been posted?
     * @since 3.0
     */
    private boolean fIsRunnablePosted= false;
    /**
     * Should the last edit position be updated?
     * @since 3.0
     */
    private boolean fUpdateLastEditPosition= false;

    /*
     * @see ITextListener#textChanged(TextEvent)
     */
    public void textChanged(TextEvent event) {

      /*
       * Also works for text events which do not base on a DocumentEvent.
       * This way, if the visible document of the viewer changes, all content
       * dependent actions are updated as well.
       */

      if (fDisplay == null)
        fDisplay= getSite().getShell().getDisplay();

      if (event.getDocumentEvent() != null)
        fUpdateLastEditPosition= true;

      if (!fIsRunnablePosted) {
        fIsRunnablePosted= true;
        fDisplay.asyncExec(fRunnable);
      }
    }

    /*
     * @see org.eclipse.jface.text.ITextInputListener#inputDocumentAboutToBeChanged(org.eclipse.jface.text.IDocument, org.eclipse.jface.text.IDocument)
     */
    public void inputDocumentAboutToBeChanged(IDocument oldInput, IDocument newInput) {
      if (oldInput != null && fLocalLastEditPosition != null) {
        oldInput.removePosition(fLocalLastEditPosition);
        fLocalLastEditPosition= null;
      }
    }

    /*
     * @see org.eclipse.jface.text.ITextInputListener#inputDocumentChanged(org.eclipse.jface.text.IDocument, org.eclipse.jface.text.IDocument)
     */
    public void inputDocumentChanged(IDocument oldInput, IDocument newInput) {
    }
  }

  /**
   * Internal property change listener for handling changes in the editor's preferences.
   */
  class PropertyChangeListener implements IPropertyChangeListener {
    /*
     * @see IPropertyChangeListener#propertyChange(org.eclipse.jface.util.PropertyChangeEvent)
     */
    public void propertyChange(PropertyChangeEvent event) {
      handlePreferenceStoreChanged(event);
    }
  }

  /**
   * Internal property change listener for handling workbench font changes.
   * @since 2.1
   */
  class FontPropertyChangeListener implements IPropertyChangeListener {
    /*
     * @see IPropertyChangeListener#propertyChange(org.eclipse.jface.util.PropertyChangeEvent)
     */
    public void propertyChange(PropertyChangeEvent event) {
      if (fSourceViewer == null)
        return;

      String property= event.getProperty();

      // IMPORTANT: Do not call isBlockSelectionModeEnabled() before checking the property!

      if (BLOCK_SELECTION_MODE_FONT.equals(property) && isBlockSelectionModeEnabled()) {
        Font blockFont= JFaceResources.getFont(BLOCK_SELECTION_MODE_FONT);
        setFont(fSourceViewer, blockFont);
        disposeFont();
        updateCaret();
        return;
      }
      if (getFontPropertyPreferenceKey().equals(property) && !isBlockSelectionModeEnabled()) {
        initializeViewerFont(fSourceViewer);
        updateCaret();
        return;
      }
    }
  }

  /**
   * Internal key verify listener for triggering action activation codes.
   */
  class ActivationCodeTrigger implements VerifyKeyListener {

    /** Indicates whether this trigger has been installed. */
    private boolean fIsInstalled= false;
    /**
     * The key binding service to use.
     * @since 2.0
     */
    private IKeyBindingService fKeyBindingService;

    /*
     * @see VerifyKeyListener#verifyKey(org.eclipse.swt.events.VerifyEvent)
     */
    public void verifyKey(VerifyEvent event) {

      ActionActivationCode code= null;
      int size= fActivationCodes.size();
      for (int i= 0; i < size; i++) {
        code= (ActionActivationCode) fActivationCodes.get(i);
        if (code.matches(event)) {
          IAction action= getAction(code.fActionId);
          if (action != null) {

            if (action instanceof IUpdate)
              ((IUpdate) action).update();

            if (!action.isEnabled() && action instanceof IReadOnlyDependent) {
              IReadOnlyDependent dependent= (IReadOnlyDependent) action;
              boolean writable= dependent.isEnabled(true);
              if (writable) {
                event.doit= false;
                return;
              }
            } else if (action.isEnabled()) {
              event.doit= false;
              action.run();
              return;
            }
          }
        }
      }
    }

    /**
     * Installs this trigger on the editor's text widget.
     * @since 2.0
     */
    public void install() {
      if (!fIsInstalled) {

        if (fSourceViewer instanceof ITextViewerExtension) {
          ITextViewerExtension e= (ITextViewerExtension) fSourceViewer;
          e.prependVerifyKeyListener(this);
        } else {
          StyledText text= fSourceViewer.getTextWidget();
          text.addVerifyKeyListener(this);
        }

        fKeyBindingService= getEditorSite().getKeyBindingService();
        fIsInstalled= true;
      }
    }

    /**
     * Uninstalls this trigger from the editor's text widget.
     * @since 2.0
     */
    public void uninstall() {
      if (fIsInstalled) {

        if (fSourceViewer instanceof ITextViewerExtension) {
          ITextViewerExtension e= (ITextViewerExtension) fSourceViewer;
          e.removeVerifyKeyListener(this);
        } else if (fSourceViewer != null) {
          StyledText text= fSourceViewer.getTextWidget();
          if (text != null && !text.isDisposed())
            text.removeVerifyKeyListener(fActivationCodeTrigger);
        }

        fIsInstalled= false;
        fKeyBindingService= null;
      }
    }

    /**
     * Registers the given action for key activation.
     * @param action the action to be registered
     * @since 2.0
     */
    public void registerActionForKeyActivation(IAction action) {
      if (fIsInstalled && action.getActionDefinitionId() != null)
        fKeyBindingService.registerAction(action);
    }

    /**
     * The given action is no longer available for key activation
     * @param action the action to be unregistered
     * @since 2.0
     */
    public void unregisterActionFromKeyActivation(IAction action) {
      if (fIsInstalled && action.getActionDefinitionId() != null)
        fKeyBindingService.unregisterAction(action);
    }

    /**
     * Sets the key binding scopes for this editor.
     * @param keyBindingScopes the key binding scopes
     * @since 2.1
     */
    public void setScopes(String[] keyBindingScopes) {
      if (keyBindingScopes != null && keyBindingScopes.length > 0)
        fKeyBindingService.setScopes(keyBindingScopes);
    }
  }

  /**
   * Representation of action activation codes.
   */
  static class ActionActivationCode {

    /** The action id. */
    public String fActionId;
    /** The character. */
    public char fCharacter;
    /** The key code. */
    public int fKeyCode= -1;
    /** The state mask. */
    public int fStateMask= SWT.DEFAULT;

    /**
     * Creates a new action activation code for the given action id.
     * @param actionId the action id
     */
    public ActionActivationCode(String actionId) {
      fActionId= actionId;
    }

    /**
     * Returns <code>true</code> if this activation code matches the given verify event.
     * @param event the event to test for matching
     * @return whether this activation code matches <code>event</code>
     */
    public boolean matches(VerifyEvent event) {
      return (event.character == fCharacter &&
            (fKeyCode == -1 || event.keyCode == fKeyCode) &&
            (fStateMask == SWT.DEFAULT || event.stateMask == fStateMask));
    }
  }

  /**
   * Internal part and shell activation listener for triggering state validation.
   * @since 2.0
   */
  class ActivationListener implements IPartListener, IWindowListener {

    /** Cache of the active workbench part. */
    private IWorkbenchPart fActivePart;
    /** Indicates whether activation handling is currently be done. */
    private boolean fIsHandlingActivation= false;
    /**
     * The part service.
     * @since 3.1
     */
    private IPartService fPartService;

    /**
     * Creates this activation listener.
     *
     * @param partService the part service on which to add the part listener
     * @since 3.1
     */
    public ActivationListener(IPartService partService) {
      fPartService= partService;
      fPartService.addPartListener(this);
      PlatformUI.getWorkbench().addWindowListener(this);
    }

    /**
     * Disposes this activation listener.
     *
     * @since 3.1
     */
    public void dispose() {
      fPartService.removePartListener(this);
      PlatformUI.getWorkbench().removeWindowListener(this);
      fPartService= null;
    }

    /*
     * @see IPartListener#partActivated(org.eclipse.ui.IWorkbenchPart)
     */
    public void partActivated(IWorkbenchPart part) {
      fActivePart= part;
      handleActivation();
    }

    /*
     * @see IPartListener#partBroughtToTop(org.eclipse.ui.IWorkbenchPart)
     */
    public void partBroughtToTop(IWorkbenchPart part) {
    }

    /*
     * @see IPartListener#partClosed(org.eclipse.ui.IWorkbenchPart)
     */
    public void partClosed(IWorkbenchPart part) {
    }

    /*
     * @see IPartListener#partDeactivated(org.eclipse.ui.IWorkbenchPart)
     */
    public void partDeactivated(IWorkbenchPart part) {
      fActivePart= null;
    }

    /*
     * @see IPartListener#partOpened(org.eclipse.ui.IWorkbenchPart)
     */
    public void partOpened(IWorkbenchPart part) {
      // Restore the saved state if any
      if ((part == AbstractTextEditor.this || part.getAdapter(AbstractTextEditor.class) == AbstractTextEditor.this) && fMementoToRestore != null && containsSavedState(fMementoToRestore)) {
        doRestoreState(fMementoToRestore);
        fMementoToRestore= null;
      }
    }

    /**
     * Handles the activation triggering a element state check in the editor.
     */
    private void handleActivation() {
      if (fIsHandlingActivation)
        return;

      if (fActivePart == AbstractTextEditor.this || fActivePart != null && fActivePart.getAdapter(AbstractTextEditor.class) == AbstractTextEditor.this) {
        fIsHandlingActivation= true;
        try {
          safelySanityCheckState(getEditorInput());
        } finally {
          fIsHandlingActivation= false;
          fHasBeenActivated= true;
        }
      }
    }

    /*
     * @see org.eclipse.ui.IWindowListener#windowActivated(org.eclipse.ui.IWorkbenchWindow)
     * @since 3.1
     */
    public void windowActivated(IWorkbenchWindow window) {
      if (window == getEditorSite().getWorkbenchWindow()) {
        /*
         * Workaround for problem described in
         * http://dev.eclipse.org/bugs/show_bug.cgi?id=11731
         * Will be removed when SWT has solved the problem.
         */
        window.getShell().getDisplay().asyncExec(new Runnable() {
          public void run() {
            handleActivation();
          }
        });
      }
    }

    /*
     * @see org.eclipse.ui.IWindowListener#windowDeactivated(org.eclipse.ui.IWorkbenchWindow)
     * @since 3.1
     */
    public void windowDeactivated(IWorkbenchWindow window) {
    }

    /*
     * @see org.eclipse.ui.IWindowListener#windowClosed(org.eclipse.ui.IWorkbenchWindow)
     * @since 3.1
     */
    public void windowClosed(IWorkbenchWindow window) {
    }

    /*
     * @see org.eclipse.ui.IWindowListener#windowOpened(org.eclipse.ui.IWorkbenchWindow)
     * @since 3.1
     */
    public void windowOpened(IWorkbenchWindow window) {
    }
  }

  /**
   * Internal interface for a cursor listener. I.e. aggregation
   * of mouse and key listener.
   * @since 2.0
   */
  interface ICursorListener extends MouseListener, KeyListener {
  }

  /**
   * Maps an action definition id to an StyledText action.
   * @since 2.0
   */
  protected static final class IdMapEntry {

    /** The action id. */
    private String fActionId;
    /** The StyledText action. */
    private int fAction;

    /**
     * Creates a new mapping.
     * @param actionId the action id
     * @param action the StyledText action
     */
    public IdMapEntry(String actionId, int action) {
      fActionId= actionId;
      fAction= action;
    }

    /**
     * Returns the action id.
     * @return the action id
     */
    public String getActionId() {
      return fActionId;
    }

    /**
     * Returns the action.
     * @return the action
     */
    public int getAction() {
      return fAction;
    }
  }

  /**
   * Internal action to scroll the editor's viewer by a specified number of lines.
   * @since 2.0
   */
  class ScrollLinesAction extends Action {

    /** Number of lines to scroll. */
    private int fScrollIncrement;

    /**
     * Creates a new scroll action that scroll the given number of lines. If the
     * increment is &lt; 0, it's scrolling up, if &gt; 0 it's scrolling down.
     * @param scrollIncrement the number of lines to scroll
     */
    public ScrollLinesAction(int scrollIncrement) {
      fScrollIncrement= scrollIncrement;
    }

    /*
     * @see IAction#run()
     */
    public void run() {
      if (fSourceViewer instanceof ITextViewerExtension5) {
        ITextViewerExtension5 extension= (ITextViewerExtension5) fSourceViewer;
        StyledText textWidget= fSourceViewer.getTextWidget();
        int topIndex= textWidget.getTopIndex();
        int newTopIndex= Math.max(0, topIndex + fScrollIncrement);
        fSourceViewer.setTopIndex(extension.widgetLine2ModelLine(newTopIndex));
      } else {
        int topIndex= fSourceViewer.getTopIndex();
        int newTopIndex= Math.max(0, topIndex + fScrollIncrement);
        fSourceViewer.setTopIndex(newTopIndex);
      }
    }
  }

  /**
   * Action to toggle the insert mode. The action is checked if smart mode is
   * turned on.
   *
   * @since 2.1
   */
  class ToggleInsertModeAction extends ResourceAction {

    public ToggleInsertModeAction(ResourceBundle bundle, String prefix) {
      super(bundle, prefix, IAction.AS_CHECK_BOX);
    }

    /*
     * @see org.eclipse.jface.action.IAction#run()
     */
    public void run() {
      switchToNextInsertMode();
    }

    /*
     * @see org.eclipse.jface.action.IAction#isChecked()
     * @since 3.0
     */
    public boolean isChecked() {
      return fInsertMode == SMART_INSERT;
    }
  }

  /**
   * Action to toggle the overwrite mode.
   *
   * @since 3.0
   */
  class ToggleOverwriteModeAction extends ResourceAction {

    public ToggleOverwriteModeAction(ResourceBundle bundle, String prefix) {
      super(bundle, prefix);
    }

    /*
     * @see org.eclipse.jface.action.IAction#run()
     */
    public void run() {
      toggleOverwriteMode();
    }
  }

  /**
   * This action implements smart end.
   * Instead of going to the end of a line it does the following:
   * - if smart home/end is enabled and the caret is before the line's last non-whitespace and then the caret is moved directly after it
   * - if the caret is after last non-whitespace the caret is moved at the end of the line
   * - if the caret is at the end of the line the caret is moved directly after the line's last non-whitespace character
   * @since 2.1 (in 3.3 the access modifier changed from package visibility to protected)
   */
  protected class LineEndAction extends TextNavigationAction {

    /** boolean flag which tells if the text up to the line end should be selected. */
    private boolean fDoSelect;

    /**
     * Create a new line end action.
     *
     * @param textWidget the styled text widget
     * @param doSelect a boolean flag which tells if the text up to the line end should be selected
     */
    public LineEndAction(StyledText textWidget, boolean doSelect) {
      super(textWidget, ST.LINE_END);
      fDoSelect= doSelect;
    }

    /**
     * Computes the offset of the line end position.
     *
     * @param document the document where to compute the line end position
     * @param line the line to determine the end position of
     * @param length the length of the line
     * @param offset the caret position in the document
     * @return the offset of the line end
     * @since 3.4 protected, was added in 3.3 as private method
     */
    protected int getLineEndPosition(final IDocument document, final String line, final int length, final int offset) {
      int index= length - 1;
      while (index > -1 && Character.isWhitespace(line.charAt(index)))
        index--;
      index++;

      LinkedModeModel model= LinkedModeModel.getModel(document, offset);
      if (model != null) {
        LinkedPosition linkedPosition= model.findPosition(new LinkedPosition(document, offset, 0));
        if (linkedPosition != null) {
          int linkedPositionEnd= linkedPosition.getOffset() + linkedPosition.getLength();
          int lineOffset;
          try {
            lineOffset= document.getLineInformationOfOffset(offset).getOffset();
            if (offset != linkedPositionEnd && linkedPositionEnd - lineOffset < index)
              index= linkedPositionEnd - lineOffset;
          } catch (BadLocationException e) {
            //should not happen
          }
        }
      }
      return index;
    }

    /*
     * @see org.eclipse.jface.action.IAction#run()
     */
    public void run() {
      boolean isSmartHomeEndEnabled= false;
      IPreferenceStore store= getPreferenceStore();
      if (store != null)
        isSmartHomeEndEnabled= store.getBoolean(AbstractTextEditor.PREFERENCE_NAVIGATION_SMART_HOME_END);

      StyledText st= fSourceViewer.getTextWidget();
      if (st == null || st.isDisposed())
        return;
      int caretOffset= st.getCaretOffset();
      int lineNumber= st.getLineAtOffset(caretOffset);
      int lineOffset= st.getOffsetAtLine(lineNumber);

      int lineLength;
      int caretOffsetInDocument;
      final IDocument document= fSourceViewer.getDocument();

      try {
        caretOffsetInDocument= widgetOffset2ModelOffset(fSourceViewer, caretOffset);
        lineLength= document.getLineInformationOfOffset(caretOffsetInDocument).getLength();
      } catch (BadLocationException ex) {
        return;
      }
      int lineEndOffset= lineOffset + lineLength;

      int delta= lineEndOffset - st.getCharCount();
      if (delta > 0) {
        lineEndOffset -= delta;
        lineLength -= delta;
      }

      String line= ""; //$NON-NLS-1$
      if (lineLength > 0)
        line= st.getText(lineOffset, lineEndOffset - 1);

      // Remember current selection
      Point oldSelection= st.getSelection();

      // The new caret position
      int newCaretOffset= -1;

      if (isSmartHomeEndEnabled) {
        // Compute the line end offset
        int i= getLineEndPosition(document, line, lineLength, caretOffsetInDocument);

        if (caretOffset - lineOffset == i)
          // to end of line
          newCaretOffset= lineEndOffset;
        else
          // to end of text
          newCaretOffset= lineOffset + i;

      } else {

        if (caretOffset < lineEndOffset)
          // to end of line
          newCaretOffset= lineEndOffset;

      }

      if (newCaretOffset == -1)
        newCaretOffset= caretOffset;
      else
        st.setCaretOffset(newCaretOffset);

      st.setCaretOffset(newCaretOffset);
      if (fDoSelect) {
        if (caretOffset < oldSelection.y)
          st.setSelection(oldSelection.y, newCaretOffset);
        else
          st.setSelection(oldSelection.x, newCaretOffset);
      } else
        st.setSelection(newCaretOffset);

      fireSelectionChanged(oldSelection);
    }
  }

  /**
   * This action implements smart home.
   * Instead of going to the start of a line it does the following:
   * - if smart home/end is enabled and the caret is after the line's first non-whitespace then the caret is moved directly before it
   * - if the caret is before the line's first non-whitespace the caret is moved to the beginning of the line
   * - if the caret is at the beginning of the line the caret is moved directly before the line's first non-whitespace character
   * @since 2.1
   */
  protected class LineStartAction extends TextNavigationAction {

    /** boolean flag which tells if the text up to the beginning of the line should be selected. */
    private final boolean fDoSelect;

    /**
     * Creates a new line start action.
     *
     * @param textWidget the styled text widget
     * @param doSelect a boolean flag which tells if the text up to the beginning of the line should be selected
     */
    public LineStartAction(final StyledText textWidget, final boolean doSelect) {
      super(textWidget, ST.LINE_START);
      fDoSelect= doSelect;
    }

    /**
     * Computes the offset of the line start position.
     *
     * @param document the document where to compute the line start position
     * @param line the line to determine the start position of
     * @param length the length of the line
     * @param offset the caret position in the document
     * @return the offset of the line start
     * @since 3.0
     */
    protected int getLineStartPosition(final IDocument document, final String line, final int length, final int offset) {
      int index= 0;
      while (index < length && Character.isWhitespace(line.charAt(index)))
        index++;

      LinkedModeModel model= LinkedModeModel.getModel(document, offset);
      if (model != null) {
        LinkedPosition linkedPosition= model.findPosition(new LinkedPosition(document, offset, 0));
        if (linkedPosition != null) {
          int linkedPositionOffset= linkedPosition.getOffset();
          int lineOffset;
          try {
            lineOffset= document.getLineInformationOfOffset(offset).getOffset();
            if (offset != linkedPositionOffset && index < linkedPositionOffset - lineOffset)
              index= linkedPositionOffset - lineOffset;
          } catch (BadLocationException e) {
            //should not happen
          }
        }
      }
      return index;
    }

    /*
     * @see org.eclipse.jface.action.IAction#run()
     */
    public void run() {
      boolean isSmartHomeEndEnabled= false;
      IPreferenceStore store= getPreferenceStore();
      if (store != null)
        isSmartHomeEndEnabled= store.getBoolean(AbstractTextEditor.PREFERENCE_NAVIGATION_SMART_HOME_END);

      StyledText st= fSourceViewer.getTextWidget();
      if (st == null || st.isDisposed())
        return;

      int caretOffset= st.getCaretOffset();
      int lineNumber= st.getLineAtOffset(caretOffset);
      int lineOffset= st.getOffsetAtLine(lineNumber);

      int lineLength;
      int caretOffsetInDocument;
      final IDocument document= fSourceViewer.getDocument();

      try {
        caretOffsetInDocument= widgetOffset2ModelOffset(fSourceViewer, caretOffset);
        lineLength= document.getLineInformationOfOffset(caretOffsetInDocument).getLength();
      } catch (BadLocationException ex) {
        return;
      }

      String line= ""; //$NON-NLS-1$
      if (lineLength > 0) {
        int end= lineOffset + lineLength - 1;
        end= Math.min(end, st.getCharCount() -1);
        line= st.getText(lineOffset, end);
      }

      // Remember current selection
      Point oldSelection= st.getSelection();

      // The new caret position
      int newCaretOffset= -1;

      if (isSmartHomeEndEnabled) {

        // Compute the line start offset
        int index= getLineStartPosition(document, line, lineLength, caretOffsetInDocument);

        if (caretOffset - lineOffset == index)
          // to beginning of line
          newCaretOffset= lineOffset;
        else
          // to beginning of text
          newCaretOffset= lineOffset + index;

      } else {

        if (caretOffset > lineOffset)
          // to beginning of line
          newCaretOffset= lineOffset;
      }

      if (newCaretOffset == -1)
        newCaretOffset= caretOffset;
      else
        st.setCaretOffset(newCaretOffset);

      if (fDoSelect) {
        if (caretOffset < oldSelection.y)
          st.setSelection(oldSelection.y, newCaretOffset);
        else
          st.setSelection(oldSelection.x, newCaretOffset);
      } else
        st.setSelection(newCaretOffset);

      fireSelectionChanged(oldSelection);
    }

  }

  /**
   * Internal action to show the editor's ruler context menu (accessibility).
   * @since 2.0
   */
  class ShowRulerContextMenuAction extends Action {
    /*
     * @see IAction#run()
     */
    public void run() {
      if (fSourceViewer == null)
        return;

      StyledText text= fSourceViewer.getTextWidget();
      if (text == null || text.isDisposed())
        return;

      Point location= text.getLocationAtOffset(text.getCaretOffset());
      location.x= 0;

      if (fVerticalRuler instanceof IVerticalRulerExtension)
        ((IVerticalRulerExtension) fVerticalRuler).setLocationOfLastMouseButtonActivity(location.x, location.y);

      location= text.toDisplay(location);
      fRulerContextMenu.setLocation(location.x, location.y);
      fRulerContextMenu.setVisible(true);
    }
  }

  /**
   * Editor specific selection provider which wraps the source viewer's selection provider.
   *
   * @since 3.4 protected, was added in 2.1 as private class
   */
  protected class SelectionProvider implements IPostSelectionProvider, ISelectionValidator {

    /*
     * @see org.eclipse.jface.viewers.ISelectionProvider#addSelectionChangedListener(ISelectionChangedListener)
     */
    public void addSelectionChangedListener(ISelectionChangedListener listener) {
      if (fSourceViewer != null)
        fSourceViewer.getSelectionProvider().addSelectionChangedListener(listener);
    }

    /*
     * @see org.eclipse.jface.viewers.ISelectionProvider#getSelection()
     */
    public ISelection getSelection() {
      return doGetSelection();
    }

    /*
     * @see org.eclipse.jface.viewers.ISelectionProvider#removeSelectionChangedListener(ISelectionChangedListener)
     */
    public void removeSelectionChangedListener(ISelectionChangedListener listener) {
      if (fSourceViewer != null)
        fSourceViewer.getSelectionProvider().removeSelectionChangedListener(listener);
    }

    /*
     * @see org.eclipse.jface.viewers.ISelectionProvider#setSelection(ISelection)
     */
    public void setSelection(ISelection selection) {
      doSetSelection(selection);
    }

    /*
     * @see org.eclipse.jface.text.IPostSelectionProvider#addPostSelectionChangedListener(org.eclipse.jface.viewers.ISelectionChangedListener)
     * @since 3.0
     */
    public void addPostSelectionChangedListener(ISelectionChangedListener listener) {
      if (fSourceViewer != null) {
        if (fSourceViewer.getSelectionProvider() instanceof IPostSelectionProvider)  {
          IPostSelectionProvider provider= (IPostSelectionProvider) fSourceViewer.getSelectionProvider();
          provider.addPostSelectionChangedListener(listener);
        }
      }
    }

    /*
     * @see org.eclipse.jface.text.IPostSelectionProvider#removePostSelectionChangedListener(org.eclipse.jface.viewers.ISelectionChangedListener)
     * @since 3.0
     */
    public void removePostSelectionChangedListener(ISelectionChangedListener listener) {
      if (fSourceViewer != null)  {
        if (fSourceViewer.getSelectionProvider() instanceof IPostSelectionProvider)  {
          IPostSelectionProvider provider= (IPostSelectionProvider) fSourceViewer.getSelectionProvider();
          provider.removePostSelectionChangedListener(listener);
        }
      }
    }

    /*
     * @see org.eclipse.jface.text.IPostSelectionValidator#isValid()
     * @since 3.0
     */
    public boolean isValid(ISelection postSelection) {
      return fSelectionListener != null && fSelectionListener.isValid(postSelection);
    }
  }

  /**
   * Internal implementation class for a change listener.
   * @since 3.0
   */
  protected abstract class AbstractSelectionChangedListener implements ISelectionChangedListener  {

    /**
     * Installs this selection changed listener with the given selection provider. If the
     * selection provider is a post selection provider, post selection changed events are the
     * preferred choice, otherwise normal selection changed events are requested.
     *
     * @param selectionProvider the selection provider
     */
    public void install(ISelectionProvider selectionProvider) {
      if (selectionProvider == null)
        return;

      if (selectionProvider instanceof IPostSelectionProvider)  {
        IPostSelectionProvider provider= (IPostSelectionProvider) selectionProvider;
        provider.addPostSelectionChangedListener(this);
      } else  {
        selectionProvider.addSelectionChangedListener(this);
      }
    }

    /**
     * Removes this selection changed listener from the given selection provider.
     *
     * @param selectionProvider the selection provider
     */
    public void uninstall(ISelectionProvider selectionProvider) {
      if (selectionProvider == null)
        return;

      if (selectionProvider instanceof IPostSelectionProvider)  {
        IPostSelectionProvider provider= (IPostSelectionProvider) selectionProvider;
        provider.removePostSelectionChangedListener(this);
      } else  {
        selectionProvider.removeSelectionChangedListener(this);
      }
    }
  }

  /**
   * This selection listener allows the SelectionProvider to implement {@link ISelectionValidator}.
   *
   * @since 3.0
   */
  private class SelectionListener extends AbstractSelectionChangedListener implements IDocumentListener {

    private IDocument fDocument;
    private final Object INVALID_SELECTION= new Object();
    private Object fPostSelection= INVALID_SELECTION;

    /*
     * @see org.eclipse.jface.viewers.ISelectionChangedListener#selectionChanged(org.eclipse.jface.viewers.SelectionChangedEvent)
     */
    public synchronized void selectionChanged(SelectionChangedEvent event) {
      fPostSelection= event.getSelection();
    }

    /*
     * @see org.eclipse.jface.text.IDocumentListener#documentAboutToBeChanged(org.eclipse.jface.text.DocumentEvent)
     * @since 3.0
     */
    public synchronized void documentAboutToBeChanged(DocumentEvent event) {
      fPostSelection= INVALID_SELECTION;
    }

    /*
     * @see org.eclipse.jface.text.IDocumentListener#documentChanged(org.eclipse.jface.text.DocumentEvent)
     * @since 3.0
     */
    public void documentChanged(DocumentEvent event) {
    }

    public synchronized boolean isValid(ISelection selection) {
      return fPostSelection != INVALID_SELECTION && fPostSelection == selection;
    }

    public void setDocument(IDocument document) {
      if (fDocument != null)
        fDocument.removeDocumentListener(this);

      fDocument= document;
      if (fDocument != null)
        fDocument.addDocumentListener(this);
    }

    /*
     * @see org.eclipse.ui.texteditor.AbstractTextEditor.AbstractSelectionChangedListener#install(org.eclipse.jface.viewers.ISelectionProvider)
     * @since 3.0
     */
    public void install(ISelectionProvider selectionProvider) {
      super.install(selectionProvider);

      if (selectionProvider != null)
        selectionProvider.addSelectionChangedListener(this);
    }

    /*
     * @see org.eclipse.ui.texteditor.AbstractTextEditor.AbstractSelectionChangedListener#uninstall(org.eclipse.jface.viewers.ISelectionProvider)
     * @since 3.0
     */
    public void uninstall(ISelectionProvider selectionProvider) {
      if (selectionProvider != null)
        selectionProvider.removeSelectionChangedListener(this);

      if (fDocument != null) {
        fDocument.removeDocumentListener(this);
        fDocument= null;
      }
      super.uninstall(selectionProvider);
    }
  }


  /**
   * Implements the ruler column support of for the given editor.
   * <p>
   * This is currently only used to support vertical ruler columns.
   * </p>
   *
   * @since 3.3
   */
  protected static class ColumnSupport implements IColumnSupport {
    private final AbstractTextEditor fEditor;
    private final RulerColumnRegistry fRegistry;
    private final List fColumns;

    /**
     * Creates a new column support for the given editor. Only the editor itself should normally
     * create such an instance.
     *
     * @param editor the editor
     * @param registry the contribution registry to refer to
     */
    public ColumnSupport(AbstractTextEditor editor, RulerColumnRegistry registry) {
      Assert.isLegal(editor != null);
      Assert.isLegal(registry != null);
      fEditor= editor;
      fRegistry= registry;
      fColumns= new ArrayList();
    }

    /*
     * @see org.eclipse.ui.texteditor.IColumnSupport#setColumnVisible(java.lang.String, boolean)
     */
    public final void setColumnVisible(RulerColumnDescriptor descriptor, boolean visible) {
      Assert.isLegal(descriptor != null);

      final CompositeRuler ruler= getRuler();
      if (ruler == null)
        return;

      if (!isColumnSupported(descriptor))
        visible= false;

      if (isColumnVisible(descriptor)) {
        if (!visible)
          removeColumn(ruler, descriptor);
      } else {
        if (visible)
          addColumn(ruler, descriptor);
      }
    }

    private void addColumn(final CompositeRuler ruler, final RulerColumnDescriptor descriptor) {

      final int idx= computeIndex(ruler, descriptor);

      SafeRunnable runnable= new SafeRunnable() {
        public void run() throws Exception {
          IContributedRulerColumn column= descriptor.createColumn(fEditor);
          fColumns.add(column);
          initializeColumn(column);
          ruler.addDecorator(idx, column);
        }
      };
      SafeRunner.run(runnable);
    }

    /**
     * Hook to let subclasses initialize a newly created column.
     * <p>
     * Subclasses may extend this method.</p>
     *
     * @param column the created column
     */
    protected void initializeColumn(IContributedRulerColumn column) {
    }

    private void removeColumn(final CompositeRuler ruler, final RulerColumnDescriptor descriptor) {
      removeColumn(ruler, getVisibleColumn(ruler, descriptor));
    }

    private void removeColumn(final CompositeRuler ruler, final IContributedRulerColumn rulerColumn) {
      if (rulerColumn != null) {
        SafeRunnable runnable= new SafeRunnable() {
          public void run() throws Exception {
            if (ruler != null)
              ruler.removeDecorator(rulerColumn);
            rulerColumn.columnRemoved();
          }
        };
        SafeRunner.run(runnable);
      }
    }

    /**
     * Returns the currently visible column matching <code>id</code>, <code>null</code> if
     * none.
     *
     * @param ruler the composite ruler to scan
     * @param descriptor the descriptor of the column of interest
     * @return the matching column or <code>null</code>
     */
    private IContributedRulerColumn getVisibleColumn(CompositeRuler ruler, RulerColumnDescriptor descriptor) {
      for (Iterator it= ruler.getDecoratorIterator(); it.hasNext();) {
        IVerticalRulerColumn column= (IVerticalRulerColumn)it.next();
        if (column instanceof IContributedRulerColumn) {
          IContributedRulerColumn rulerColumn= (IContributedRulerColumn)column;
          RulerColumnDescriptor rcd= rulerColumn.getDescriptor();
          if (descriptor.equals(rcd))
            return rulerColumn;
        }
      }
      return null;
    }

    /**
     * Computes the insertion index for a column contribution into the currently visible columns.
     *
     * @param ruler the composite ruler into which to insert the column
     * @param descriptor the descriptor to compute the index for
     * @return the insertion index for a new column
     */
    private int computeIndex(CompositeRuler ruler, RulerColumnDescriptor descriptor) {
      int index= 0;
      List all= fRegistry.getColumnDescriptors();
      int newPos= all.indexOf(descriptor);
      for (Iterator it= ruler.getDecoratorIterator(); it.hasNext();) {
        IVerticalRulerColumn column= (IVerticalRulerColumn) it.next();
        if (column instanceof IContributedRulerColumn) {
          RulerColumnDescriptor rcd= ((IContributedRulerColumn)column).getDescriptor();
          if (rcd != null && all.indexOf(rcd) > newPos)
            break;
        } else if ("org.eclipse.jface.text.source.projection.ProjectionRulerColumn".equals(column.getClass().getName())) { //$NON-NLS-1$
          // projection column is always the rightmost column
          break;
        }
        index++;
      }
      return index;
    }

    /*
     * @see org.eclipse.ui.texteditor.IColumnSupport#isColumnVisible(java.lang.String)
     */
    public final boolean isColumnVisible(RulerColumnDescriptor descriptor) {
      Assert.isLegal(descriptor != null);
      CompositeRuler ruler= getRuler();
      return ruler != null && getVisibleColumn(ruler, descriptor) != null;
    }

    /*
     * @see org.eclipse.ui.texteditor.IColumnSupport#isColumnSupported(java.lang.String)
     */
    public final boolean isColumnSupported(RulerColumnDescriptor descriptor) {
      Assert.isLegal(descriptor != null);
      if (getRuler() == null)
        return false;

      return descriptor.matchesEditor(fEditor);
    }

    /**
     * Returns the editor's vertical ruler, if it is a {@link CompositeRuler}, <code>null</code>
     * otherwise.
     *
     * @return the editor's {@link CompositeRuler} or <code>null</code>
     */
    private CompositeRuler getRuler() {
      Object ruler= fEditor.getAdapter(IVerticalRulerInfo.class);
      if (ruler instanceof CompositeRuler)
        return (CompositeRuler) ruler;
      return null;
    }

    /**
     * {@inheritDoc}
     * <p>
     * Subclasses may extend this method.</p>
     *
     */
    public void dispose() {
      for (Iterator iter= new ArrayList(fColumns).iterator(); iter.hasNext();)
        removeColumn(getRuler(), (IContributedRulerColumn)iter.next());
      fColumns.clear();
    }
  }



  /**
   * This action behaves in two different ways: If there is no current text
   * hover, the javadoc is displayed using information presenter. If there is
   * a current text hover, it is converted into a information presenter in
   * order to make it sticky.
   *
   * @since 3.3
   */
  private final class InformationDispatchAction extends TextEditorAction {

    /** The wrapped text operation action. */
    private final TextOperationAction fTextOperationAction;

    /**
     * Creates a dispatch action.
     *
     * @param resourceBundle the resource bundle
     * @param prefix the prefix
     * @param textOperationAction the text operation action
     */
    public InformationDispatchAction(ResourceBundle resourceBundle, String prefix, final TextOperationAction textOperationAction) {
      super(resourceBundle, prefix, AbstractTextEditor.this);
      if (textOperationAction == null)
        throw new IllegalArgumentException();
      fTextOperationAction= textOperationAction;
    }

    /*
     * @see org.eclipse.jface.action.IAction#run()
     */
    public void run() {

      ISourceViewer sourceViewer= getSourceViewer();
      if (sourceViewer == null) {
        if (fTextOperationAction.isEnabled())
          fTextOperationAction.run();
        return;
      }

      if (sourceViewer instanceof ITextViewerExtension4)  {
        ITextViewerExtension4 extension4= (ITextViewerExtension4) sourceViewer;
        if (extension4.moveFocusToWidgetToken())
          return;
      }

      if (sourceViewer instanceof ITextViewerExtension2) {
        // does a text hover exist?
        ITextHover textHover= ((ITextViewerExtension2) sourceViewer).getCurrentTextHover();
        if (textHover != null && makeTextHoverFocusable(sourceViewer, textHover))
          return;
      }

      if (sourceViewer instanceof ISourceViewerExtension3) {
        // does an annotation hover exist?
        IAnnotationHover annotationHover= ((ISourceViewerExtension3) sourceViewer).getCurrentAnnotationHover();
        if (annotationHover != null && makeAnnotationHoverFocusable(annotationHover))
          return;
      }

      // otherwise, just run the action
      if (fTextOperationAction.isEnabled())
        fTextOperationAction.run();
    }

    /**
     * Tries to make a text hover focusable (or "sticky").
     *
     * @param sourceViewer the source viewer to display the hover over
     * @param textHover the hover to make focusable
     * @return <code>true</code> if successful, <code>false</code> otherwise
     */
    private boolean makeTextHoverFocusable(ISourceViewer sourceViewer, ITextHover textHover) {
      Point hoverEventLocation= ((ITextViewerExtension2) sourceViewer).getHoverEventLocation();
      int offset= computeOffsetAtLocation(sourceViewer, hoverEventLocation.x, hoverEventLocation.y);
      if (offset == -1)
        return false;

      try {
        IRegion hoverRegion= textHover.getHoverRegion(sourceViewer, offset);
        if (hoverRegion == null)
          return false;

        String hoverInfo= textHover.getHoverInfo(sourceViewer, hoverRegion);

        IInformationControlCreator controlCreator= null;
        if (textHover instanceof IInformationProviderExtension2) // this is conceptually wrong, but left here for backwards compatibility
          controlCreator= ((IInformationProviderExtension2)textHover).getInformationPresenterControlCreator();

        IInformationProvider informationProvider= new FocusedInformationPresenter.InformationProvider(hoverRegion, hoverInfo, controlCreator);

        FocusedInformationPresenter informationPresenter= getInformationPresenter();
        informationPresenter.setOffset(offset);
        informationPresenter.setAnchor(AbstractInformationControlManager.ANCHOR_BOTTOM);
        informationPresenter.setMargins(6, 6); // default values from AbstractInformationControlManager
        String contentType= TextUtilities.getContentType(sourceViewer.getDocument(), getSourceViewerConfiguration().getConfiguredDocumentPartitioning(getSourceViewer()), offset, true);
        informationPresenter.setInformationProvider(informationProvider, contentType);
        informationPresenter.showInformation();

        return true;

      } catch (BadLocationException e) {
        return false;
      }
    }

    /**
     * Tries to make an annotation hover focusable (or "sticky").
     *
     * @param annotationHover the hover to make focusable
     * @return <code>true</code> if successful, <code>false</code> otherwise
     */
    private boolean makeAnnotationHoverFocusable(IAnnotationHover annotationHover) {
      IVerticalRulerInfo info= getVerticalRuler();
      int line= info.getLineOfLastMouseButtonActivity();
      if (line == -1)
        return false;

      return getInformationPresenter().openFocusedAnnotationHover(annotationHover, line);
        }
   
    /**
     * Returns the information presenter (creates it if necessary).
     *
     * @return the information presenter
     * @since 3.6
     */
    private FocusedInformationPresenter getInformationPresenter() {
      if (fInformationPresenter == null) {
        fInformationPresenter= new FocusedInformationPresenter(getSourceViewer(), getSourceViewerConfiguration());
      }
      return fInformationPresenter;
    }

    // modified version from TextViewer
    private int computeOffsetAtLocation(ITextViewer textViewer, int x, int y) {

      StyledText styledText= textViewer.getTextWidget();
      IDocument document= textViewer.getDocument();

      if (document == null)
        return -1;

      try {
        int widgetOffset= styledText.getOffsetAtLocation(new Point(x, y));
        Point p= styledText.getLocationAtOffset(widgetOffset);
        if (p.x > x)
          widgetOffset--;

        if (textViewer instanceof ITextViewerExtension5) {
          ITextViewerExtension5 extension= (ITextViewerExtension5) textViewer;
          return extension.widgetOffset2ModelOffset(widgetOffset);
        }
        IRegion visibleRegion= textViewer.getVisibleRegion();
        return widgetOffset + visibleRegion.getOffset();
      } catch (IllegalArgumentException e) {
        return -1;
      }
    }
  }

  /**
   * Key used to look up font preference.
   * Value: <code>"org.eclipse.jface.textfont"</code>
   *
   * @deprecated As of 2.1, replaced by {@link JFaceResources#TEXT_FONT}
   */
  public static final String PREFERENCE_FONT= JFaceResources.TEXT_FONT;
  /**
   * Key used to look up foreground color preference.
   * Value: <code>AbstractTextEditor.Color.Foreground</code>
   * @since 2.0
   */
  public static final String PREFERENCE_COLOR_FOREGROUND= "AbstractTextEditor.Color.Foreground"; //$NON-NLS-1$
  /**
   * Key used to look up background color preference.
   * Value: <code>AbstractTextEditor.Color.Background</code>
   * @since 2.0
   */
  public static final String PREFERENCE_COLOR_BACKGROUND= "AbstractTextEditor.Color.Background"; //$NON-NLS-1$
  /**
   * Key used to look up foreground color system default preference.
   * Value: <code>AbstractTextEditor.Color.Foreground.SystemDefault</code>
   * @since 2.0
   */
  public static final String PREFERENCE_COLOR_FOREGROUND_SYSTEM_DEFAULT= "AbstractTextEditor.Color.Foreground.SystemDefault"; //$NON-NLS-1$
  /**
   * Key used to look up background color system default preference.
   * Value: <code>AbstractTextEditor.Color.Background.SystemDefault</code>
   * @since 2.0
   */
  public static final String PREFERENCE_COLOR_BACKGROUND_SYSTEM_DEFAULT= "AbstractTextEditor.Color.Background.SystemDefault"; //$NON-NLS-1$
  /**
   * Key used to look up selection foreground color preference.
   * Value: <code>AbstractTextEditor.Color.SelectionForeground</code>
   * @since 3.0
   */
  public static final String PREFERENCE_COLOR_SELECTION_FOREGROUND= "AbstractTextEditor.Color.SelectionForeground"; //$NON-NLS-1$
  /**
   * Key used to look up selection background color preference.
   * Value: <code>AbstractTextEditor.Color.SelectionBackground</code>
   * @since 3.0
   */
  public static final String PREFERENCE_COLOR_SELECTION_BACKGROUND= "AbstractTextEditor.Color.SelectionBackground"; //$NON-NLS-1$
  /**
   * Key used to look up selection foreground color system default preference.
   * Value: <code>AbstractTextEditor.Color.SelectionForeground.SystemDefault</code>
   * @since 3.0
   */
  public static final String PREFERENCE_COLOR_SELECTION_FOREGROUND_SYSTEM_DEFAULT= "AbstractTextEditor.Color.SelectionForeground.SystemDefault"; //$NON-NLS-1$
  /**
   * Key used to look up selection background color system default preference.
   * Value: <code>AbstractTextEditor.Color.SelectionBackground.SystemDefault</code>
   * @since 3.0
   */
  public static final String PREFERENCE_COLOR_SELECTION_BACKGROUND_SYSTEM_DEFAULT= "AbstractTextEditor.Color.SelectionBackground.SystemDefault"; //$NON-NLS-1$
  /**
   * Key used to look up find scope background color preference.
   * Value: <code>AbstractTextEditor.Color.FindScope</code>
   * @since 2.0
   */
  public static final String PREFERENCE_COLOR_FIND_SCOPE= "AbstractTextEditor.Color.FindScope"; //$NON-NLS-1$
  /**
   * Key used to look up smart home/end preference.
   * Value: <code>AbstractTextEditor.Navigation.SmartHomeEnd</code>
   * @since 2.1
   */
  public static final String PREFERENCE_NAVIGATION_SMART_HOME_END= "AbstractTextEditor.Navigation.SmartHomeEnd"; //$NON-NLS-1$
  /**
   * Key used to look up the custom caret preference.
   * Value: {@value}
   * @since 3.0
   */
  public static final String PREFERENCE_USE_CUSTOM_CARETS= "AbstractTextEditor.Accessibility.UseCustomCarets"; //$NON-NLS-1$
  /**
   * Key used to look up the caret width preference.
   * Value: {@value}
   * @since 3.0
   */
  public static final String PREFERENCE_WIDE_CARET= "AbstractTextEditor.Accessibility.WideCaret"; //$NON-NLS-1$
  /**
   * A named preference that controls if hyperlinks are turned on or off.
   * <p>
   * Value is of type <code>Boolean</code>.
   * </p>
   *
   * @since 3.1
   */
  public static final String PREFERENCE_HYPERLINKS_ENABLED= "hyperlinksEnabled"; //$NON-NLS-1$

  /**
   * A named preference that controls the key modifier for hyperlinks.
   * <p>
   * Value is of type <code>String</code>.
   * </p>
   *
   * @since 3.1
   */
  public static final String PREFERENCE_HYPERLINK_KEY_MODIFIER= "hyperlinkKeyModifier"; //$NON-NLS-1$
  /**
   * A named preference that controls the key modifier mask for hyperlinks.
   * The value is only used if the value of <code>PREFERENCE_HYPERLINK_KEY_MODIFIER</code>
   * cannot be resolved to valid SWT modifier bits.
   * <p>
   * Value is of type <code>String</code>.
   * </p>
   *
   * @see #PREFERENCE_HYPERLINK_KEY_MODIFIER
   * @since 3.1
   */
  public static final String PREFERENCE_HYPERLINK_KEY_MODIFIER_MASK= "hyperlinkKeyModifierMask"; //$NON-NLS-1$
  /**
   * A named preference that controls the visible ruler column contributions.
   * <p>
   * Value is of type <code>String</code> and should be read using a {@link RulerColumnPreferenceAdapter}.
   * </p>
   *
   * @since 3.3
   */
  public static final String PREFERENCE_RULER_CONTRIBUTIONS= "rulerContributions"; //$NON-NLS-1$
 
  /**
   * A named preference that controls the display of whitespace characters.
   * <p>
   * Value is of type <code>Boolean</code>.
   * </p>
   *
   * <p>
   * The following preferences can be used for fine-grained configuration when enabled.
   * <ul>
   * <li>{@link #PREFERENCE_SHOW_LEADING_SPACES}</li>
   * <li>{@link #PREFERENCE_SHOW_ENCLOSED_SPACES}</li>
   * <li>{@link #PREFERENCE_SHOW_TRAILING_SPACES}</li>
   * <li>{@link #PREFERENCE_SHOW_LEADING_IDEOGRAPHIC_SPACES}</li>
   * <li>{@link #PREFERENCE_SHOW_ENCLOSED_IDEOGRAPHIC_SPACES}</li>
   * <li>{@link #PREFERENCE_SHOW_TRAILING_IDEOGRAPHIC_SPACES}</li>
   * <li>{@link #PREFERENCE_SHOW_LEADING_TABS}</li>
   * <li>{@link #PREFERENCE_SHOW_ENCLOSED_TABS}</li>
   * <li>{@link #PREFERENCE_SHOW_TRAILING_TABS}</li>
   * <li>{@link #PREFERENCE_SHOW_CARRIAGE_RETURN}</li>
   * <li>{@link #PREFERENCE_SHOW_LINE_FEED}</li>
   * <li>{@link #PREFERENCE_WHITESPACE_CHARACTER_ALPHA_VALUE}</li>
   * </ul>
   * </p>
   *
   * @since 3.3
   */
  public static final String PREFERENCE_SHOW_WHITESPACE_CHARACTERS= "showWhitespaceCharacters"; //$NON-NLS-1$

  /**
   * A named preference that controls the display of leading Space characters. The value is used
   * only if the value of {@link #PREFERENCE_SHOW_WHITESPACE_CHARACTERS} is <code>true</code>.
   * <p>
   * Value is of type <code>Boolean</code>.
   * </p>
   *
   * @since 3.7
   */
  public static final String PREFERENCE_SHOW_LEADING_SPACES= "showLeadingSpaces"; //$NON-NLS-1$

  /**
   * A named preference that controls the display of enclosed Space characters. The value is used
   * only if the value of {@link #PREFERENCE_SHOW_WHITESPACE_CHARACTERS} is <code>true</code>.
   * <p>
   * Value is of type <code>Boolean</code>.
   * </p>
   *
   * @since 3.7
   */
  public static final String PREFERENCE_SHOW_ENCLOSED_SPACES= "showEnclosedSpaces"; //$NON-NLS-1$

  /**
   * A named preference that controls the display of trailing Space characters. The value is used
   * only if the value of {@link #PREFERENCE_SHOW_WHITESPACE_CHARACTERS} is <code>true</code>.
   * <p>
   * Value is of type <code>Boolean</code>.
   * </p>
   *
   * @since 3.7
   */
  public static final String PREFERENCE_SHOW_TRAILING_SPACES= "showTrailingSpaces"; //$NON-NLS-1$

  /**
   * A named preference that controls the display of leading Ideographic Space characters. The
   * value is used only if the value of {@link #PREFERENCE_SHOW_WHITESPACE_CHARACTERS} is
   * <code>true</code>.
   * <p>
   * Value is of type <code>Boolean</code>.
   * </p>
   *
   * @since 3.7
   */
  public static final String PREFERENCE_SHOW_LEADING_IDEOGRAPHIC_SPACES= "showLeadingIdeographicSpaces"; //$NON-NLS-1$

  /**
   * A named preference that controls the display of enclosed Ideographic Space characters. The
   * value is used only if the value of {@link #PREFERENCE_SHOW_WHITESPACE_CHARACTERS} is
   * <code>true</code>.
   * <p>
   * Value is of type <code>Boolean</code>.
   * </p>
   *
   * @since 3.7
   */
  public static final String PREFERENCE_SHOW_ENCLOSED_IDEOGRAPHIC_SPACES= "showEnclosedIdeographicSpaces"; //$NON-NLS-1$

  /**
   * A named preference that controls the display of trailing Ideographic Space characters. The
   * value is used only if the value of {@link #PREFERENCE_SHOW_WHITESPACE_CHARACTERS} is
   * <code>true</code>.
   * <p>
   * Value is of type <code>Boolean</code>.
   * </p>
   *
   * @since 3.7
   */
  public static final String PREFERENCE_SHOW_TRAILING_IDEOGRAPHIC_SPACES= "showTrailingIdeographicSpaces"; //$NON-NLS-1$

  /**
   * A named preference that controls the display of leading Tab characters. The value is used
   * only if the value of {@link #PREFERENCE_SHOW_WHITESPACE_CHARACTERS} is <code>true</code>.
   * <p>
   * Value is of type <code>Boolean</code>.
   * </p>
   *
   * @since 3.7
   */
  public static final String PREFERENCE_SHOW_LEADING_TABS= "showLeadingTabs"; //$NON-NLS-1$

  /**
   * A named preference that controls the display of enclosed Tab characters. The value is used
   * only if the value of {@link #PREFERENCE_SHOW_WHITESPACE_CHARACTERS} is <code>true</code>.
   * <p>
   * Value is of type <code>Boolean</code>.
   * </p>
   *
   * @since 3.7
   */
  public static final String PREFERENCE_SHOW_ENCLOSED_TABS= "showEnclosedTabs"; //$NON-NLS-1$

  /**
   * A named preference that controls the display of trailing Tab characters. The value is used
   * only if the value of {@link #PREFERENCE_SHOW_WHITESPACE_CHARACTERS} is <code>true</code>.
   * <p>
   * Value is of type <code>Boolean</code>.
   * </p>
   *
   * @since 3.7
   */
  public static final String PREFERENCE_SHOW_TRAILING_TABS= "showTrailingTabs"; //$NON-NLS-1$

  /**
   * A named preference that controls the display of Carriage Return characters. The value is used
   * only if the value of {@link #PREFERENCE_SHOW_WHITESPACE_CHARACTERS} is <code>true</code>.
   * <p>
   * Value is of type <code>Boolean</code>.
   * </p>
   *
   * @since 3.7
   */
  public static final String PREFERENCE_SHOW_CARRIAGE_RETURN= "showCarriageReturn"; //$NON-NLS-1$

  /**
   * A named preference that controls the display of Line Feed characters. The value is used only
   * if the value of {@link #PREFERENCE_SHOW_WHITESPACE_CHARACTERS} is <code>true</code>.
   * <p>
   * Value is of type <code>Boolean</code>.
   * </p>
   *
   * @since 3.7
   */
  public static final String PREFERENCE_SHOW_LINE_FEED= "showLineFeed"; //$NON-NLS-1$

  /**
   * A named preference that controls the alpha value of whitespace characters. The value is used
   * only if the value of {@link #PREFERENCE_SHOW_WHITESPACE_CHARACTERS} is <code>true</code>.
   * <p>
   * Value is of type <code>Integer</code>.
   * </p>
   *
   * @since 3.7
   */
  public static final String PREFERENCE_WHITESPACE_CHARACTER_ALPHA_VALUE= "whitespaceCharacterAlphaValue"; //$NON-NLS-1$

  /**
   * A named preference that controls whether text drag and drop is enabled.
   * <p>
   * Value is of type <code>Boolean</code>.
   * </p>
   *
   * @since 3.3
   */
  public static final String PREFERENCE_TEXT_DRAG_AND_DROP_ENABLED= "textDragAndDropEnabled"; //$NON-NLS-1$

  /**
   * A named preference that controls if hovers should automatically be closed
   * when the mouse is moved into them, or when they should be enriched.
   * <p>
   * Value is of type <code>Integer</code> and maps to the following
   * {@link org.eclipse.jface.text.ITextViewerExtension8.EnrichMode}:
   * </p>
   * <ul>
   * <li>-1: <code>null</code> (don't allow moving the mouse into a hover),</li>
   * <li>0: {@link org.eclipse.jface.text.ITextViewerExtension8.EnrichMode#AFTER_DELAY},</li>
   * <li>1: {@link org.eclipse.jface.text.ITextViewerExtension8.EnrichMode#IMMEDIATELY},</li>
   * <li>2: {@link org.eclipse.jface.text.ITextViewerExtension8.EnrichMode#ON_CLICK}.</li>
   * </ul>
   *
   * @since 3.4
   */
  public static final String PREFERENCE_HOVER_ENRICH_MODE= "hoverReplaceMode"; //$NON-NLS-1$

  /** Menu id for the editor context menu. */
  public static final String DEFAULT_EDITOR_CONTEXT_MENU_ID= "#EditorContext"; //$NON-NLS-1$
  /** Menu id for the ruler context menu. */
  public static final String DEFAULT_RULER_CONTEXT_MENU_ID= "#RulerContext"; //$NON-NLS-1$

  /**
   * Menu id used to contribute to the editor context menu of all textual editors.
   *
   * @since 3.5
   */
  public static final String COMMON_EDITOR_CONTEXT_MENU_ID= "#AbstractTextEditorContext"; //$NON-NLS-1$

  /**
   * Menu id used to contribute to the ruler context menu of all textual editors.
   *
   * @since 3.5
   */
  public static final String COMMON_RULER_CONTEXT_MENU_ID= "#AbstractTextEditorRulerContext"; //$NON-NLS-1$

  /** The width of the vertical ruler. */
  protected static final int VERTICAL_RULER_WIDTH= 12;

  /**
   * The complete mapping between action definition IDs used by eclipse and StyledText actions.
   *
   * @since 2.0
   */
  protected static final IdMapEntry[] ACTION_MAP= new IdMapEntry[] {
    // navigation
    new IdMapEntry(ITextEditorActionDefinitionIds.LINE_UP, ST.LINE_UP),
    new IdMapEntry(ITextEditorActionDefinitionIds.LINE_DOWN, ST.LINE_DOWN),
    new IdMapEntry(ITextEditorActionDefinitionIds.LINE_START, ST.LINE_START),
    new IdMapEntry(ITextEditorActionDefinitionIds.LINE_END, ST.LINE_END),
    new IdMapEntry(ITextEditorActionDefinitionIds.COLUMN_PREVIOUS, ST.COLUMN_PREVIOUS),
    new IdMapEntry(ITextEditorActionDefinitionIds.COLUMN_NEXT, ST.COLUMN_NEXT),
    new IdMapEntry(ITextEditorActionDefinitionIds.PAGE_UP, ST.PAGE_UP),
    new IdMapEntry(ITextEditorActionDefinitionIds.PAGE_DOWN, ST.PAGE_DOWN),
    new IdMapEntry(ITextEditorActionDefinitionIds.WORD_PREVIOUS, ST.WORD_PREVIOUS),
    new IdMapEntry(ITextEditorActionDefinitionIds.WORD_NEXT, ST.WORD_NEXT),
    new IdMapEntry(ITextEditorActionDefinitionIds.TEXT_START, ST.TEXT_START),
    new IdMapEntry(ITextEditorActionDefinitionIds.TEXT_END, ST.TEXT_END),
    new IdMapEntry(ITextEditorActionDefinitionIds.WINDOW_START, ST.WINDOW_START),
    new IdMapEntry(ITextEditorActionDefinitionIds.WINDOW_END, ST.WINDOW_END),
    // selection
    new IdMapEntry(ITextEditorActionDefinitionIds.SELECT_LINE_UP, ST.SELECT_LINE_UP),
    new IdMapEntry(ITextEditorActionDefinitionIds.SELECT_LINE_DOWN, ST.SELECT_LINE_DOWN),
    new IdMapEntry(ITextEditorActionDefinitionIds.SELECT_LINE_START, ST.SELECT_LINE_START),
    new IdMapEntry(ITextEditorActionDefinitionIds.SELECT_LINE_END, ST.SELECT_LINE_END),
    new IdMapEntry(ITextEditorActionDefinitionIds.SELECT_COLUMN_PREVIOUS, ST.SELECT_COLUMN_PREVIOUS),
    new IdMapEntry(ITextEditorActionDefinitionIds.SELECT_COLUMN_NEXT, ST.SELECT_COLUMN_NEXT),
    new IdMapEntry(ITextEditorActionDefinitionIds.SELECT_PAGE_UP, ST.SELECT_PAGE_UP),
    new IdMapEntry(ITextEditorActionDefinitionIds.SELECT_PAGE_DOWN, ST.SELECT_PAGE_DOWN),
    new IdMapEntry(ITextEditorActionDefinitionIds.SELECT_WORD_PREVIOUS, ST.SELECT_WORD_PREVIOUS),
    new IdMapEntry(ITextEditorActionDefinitionIds.SELECT_WORD_NEXT,  ST.SELECT_WORD_NEXT),
    new IdMapEntry(ITextEditorActionDefinitionIds.SELECT_TEXT_START, ST.SELECT_TEXT_START),
    new IdMapEntry(ITextEditorActionDefinitionIds.SELECT_TEXT_END, ST.SELECT_TEXT_END),
    new IdMapEntry(ITextEditorActionDefinitionIds.SELECT_WINDOW_START, ST.SELECT_WINDOW_START),
    new IdMapEntry(ITextEditorActionDefinitionIds.SELECT_WINDOW_END, ST.SELECT_WINDOW_END),
    // modification
    new IdMapEntry(IWorkbenchCommandConstants.EDIT_CUT, ST.CUT),
    new IdMapEntry(IWorkbenchCommandConstants.EDIT_COPY, ST.COPY),
    new IdMapEntry(IWorkbenchCommandConstants.EDIT_PASTE, ST.PASTE),
    new IdMapEntry(ITextEditorActionDefinitionIds.DELETE_PREVIOUS, ST.DELETE_PREVIOUS),
    new IdMapEntry(ITextEditorActionDefinitionIds.DELETE_NEXT, ST.DELETE_NEXT),
    new IdMapEntry(ITextEditorActionDefinitionIds.DELETE_PREVIOUS_WORD, ST.DELETE_WORD_PREVIOUS),
    new IdMapEntry(ITextEditorActionDefinitionIds.DELETE_NEXT_WORD, ST.DELETE_WORD_NEXT),
    // miscellaneous
    new IdMapEntry(ITextEditorActionDefinitionIds.TOGGLE_OVERWRITE, ST.TOGGLE_OVERWRITE)
  };

  private final String fReadOnlyLabel= EditorMessages.Editor_statusline_state_readonly_label;
  private final String fWritableLabel= EditorMessages.Editor_statusline_state_writable_label;
  private final String fInsertModeLabel= EditorMessages.Editor_statusline_mode_insert_label;
  private final String fOverwriteModeLabel= EditorMessages.Editor_statusline_mode_overwrite_label;
  private final String fSmartInsertModeLabel= EditorMessages.Editor_statusline_mode_smartinsert_label;

  /** The error message shown in the status line in case of failed information look up. */
  protected final String fErrorLabel= EditorMessages.Editor_statusline_error_label;

  /**
   * Data structure for the position label value.
   */
  private static class PositionLabelValue {

    public int fValue;

    public String toString() {
      return String.valueOf(fValue);
    }
  }
  /** The pattern used to show the position label in the status line. */
  private final String fPositionLabelPattern= EditorMessages.Editor_statusline_position_pattern;
  /** The position label value of the current line. */
  private final PositionLabelValue fLineLabel= new PositionLabelValue();
  /** The position label value of the current column. */
  private final PositionLabelValue fColumnLabel= new PositionLabelValue();
  /** The arguments for the position label pattern. */
  private final Object[] fPositionLabelPatternArguments= new Object[] { fLineLabel, fColumnLabel };
  /**
   * The column support of this editor.
   * @since 3.3
   */
  private IColumnSupport fColumnSupport;

  /** The editor's explicit document provider. */
  private IDocumentProvider fExplicitDocumentProvider;
  /** The editor's preference store. */
  private IPreferenceStore fPreferenceStore;
  /** The editor's range indicator. */
  private Annotation fRangeIndicator;
  /** The editor's source viewer configuration. */
  private SourceViewerConfiguration fConfiguration;
  /** The editor's source viewer. */
  private ISourceViewer fSourceViewer;
  /**
   * The editor's selection provider.
   * @since 2.1
   */
  private SelectionProvider fSelectionProvider= new SelectionProvider();
  /**
   * The editor's selection listener.
   * @since 3.0
   */
  private SelectionListener fSelectionListener;
  /** The editor's font. */
  private Font fFont;  /**
   * The editor's foreground color.
   * @since 2.0
   */
  private Color fForegroundColor;
  /**
   * The editor's background color.
   * @since 2.0
   */
  private Color fBackgroundColor;
  /**
   * The editor's selection foreground color.
   * @since 3.0
   */
  private Color fSelectionForegroundColor;
  /**
   * The editor's selection background color.
   * @since 3.0
   */
  private Color fSelectionBackgroundColor;
  /**
   * The find scope's highlight color.
   * @since 2.0
   */
  private Color fFindScopeHighlightColor;

  /**
   * The editor's status line.
   * @since 2.1
   */
  private IEditorStatusLine fEditorStatusLine;
  /** The editor's vertical ruler. */
  private IVerticalRuler fVerticalRuler;
  /** The editor's context menu id. */
  private String fEditorContextMenuId;
  /** The ruler's context menu id. */
  private String fRulerContextMenuId;
  /** The editor's help context id. */
  private String fHelpContextId;
  /** The editor's presentation mode. */
  private boolean fShowHighlightRangeOnly;
  /** The actions registered with the editor. */
  private Map fActions= new HashMap(10);
  /** The actions marked as selection dependent. */
  private List fSelectionActions= new ArrayList(5);
  /** The actions marked as content dependent. */
  private List fContentActions= new ArrayList(5);
  /**
   * The actions marked as property dependent.
   * @since 2.0
   */
  private List fPropertyActions= new ArrayList(5);
  /**
   * The actions marked as state dependent.
   * @since 2.0
   */
  private List fStateActions= new ArrayList(5);
  /** The editor's action activation codes. */
  private List fActivationCodes= new ArrayList(2);
  /** The verify key listener for activation code triggering. */
  private ActivationCodeTrigger fActivationCodeTrigger= new ActivationCodeTrigger();
  /** Context menu listener. */
  private IMenuListener fMenuListener;
  /** Vertical ruler mouse listener. */
  private MouseListener fMouseListener;
  /** Selection changed listener. */
  private ISelectionChangedListener fSelectionChangedListener;
  /** Title image to be disposed. */
  private Image fTitleImage;
  /** The text context menu to be disposed. */
  private Menu fTextContextMenu;
  /** The ruler context menu to be disposed. */
  private Menu fRulerContextMenu;
  /** The editor's element state listener. */
  private IElementStateListener fElementStateListener= new ElementStateListener();
  /**
   * The editor's text input listener.
   * @since 2.1
   */
  private TextInputListener fTextInputListener= new TextInputListener();
  /** The editor's text listener. */
  private TextListener fTextListener= new TextListener();
  /** The editor's property change listener. */
  private IPropertyChangeListener fPropertyChangeListener= new PropertyChangeListener();
  /**
   * The editor's font properties change listener.
   * @since 2.1
   */
  private IPropertyChangeListener fFontPropertyChangeListener= new FontPropertyChangeListener();

  /**
   * The editor's activation listener.
   * @since 2.0
   */
  private ActivationListener fActivationListener;
  /**
   * The map of the editor's status fields.
   * @since 2.0
   */
  private Map fStatusFields;
  /**
   * The editor's cursor listener.
   * @since 2.0
   */
  private ICursorListener fCursorListener;
  /**
   * The editor's remembered text selection.
   * @since 2.0
   */
  private ISelection fRememberedSelection;
  /**
   * Indicates whether the editor runs in 1.0 context menu registration compatibility mode.
   * @since 2.0
   */
  private boolean fCompatibilityMode= true;
  /**
   * The number of re-entrances into error correction code while saving.
   * @since 2.0
   */
  private int fErrorCorrectionOnSave;
  /**
   * The delete line target.
   * @since 2.1
   */
  private IDeleteLineTarget fDeleteLineTarget;
  /**
   * The incremental find target.
   * @since 2.0
   */
  private IncrementalFindTarget fIncrementalFindTarget;
  /**
   * The mark region target.
   * @since 2.0
   */
  private IMarkRegionTarget fMarkRegionTarget;
  /**
   * Cached modification stamp of the editor's input.
   * @since 2.0
   */
  private long fModificationStamp= -1;
  /**
   * Ruler context menu listeners.
   * @since 2.0
   */
  private List fRulerContextMenuListeners= new ArrayList();
  /**
   * Indicates whether sanity checking in enabled.
   * @since 2.0
   */
  private boolean fIsSanityCheckEnabled= true;
  /**
   * The find replace target.
   * @since 2.1
   */
  private FindReplaceTarget fFindReplaceTarget;
  /**
   * Indicates whether state validation is enabled.
   * @since 2.1
   */
  private boolean fIsStateValidationEnabled= true;
  /**
   * The key binding scopes of this editor.
   * @since 2.1
   */
  private String[] fKeyBindingScopes;
  /**
   * Whether the overwrite mode can be turned on.
   * @since 3.0
   */
  private boolean fIsOverwriteModeEnabled= true;
  /**
   * Whether the overwrite mode is currently on.
   * @since 3.0
   */
  private boolean fIsOverwriting= false;
  /**
   * The editor's insert mode.
   * @since 3.0
   */
  private InsertMode fInsertMode= SMART_INSERT;
  /**
   * The sequence of legal editor insert modes.
   * @since 3.0
   */
  private List fLegalInsertModes= null;
  /**
   * The non-default caret.
   * @since 3.0
   */
  private Caret fNonDefaultCaret;
  /**
   * The image used in non-default caret.
   * @since 3.0
   */
  private Image fNonDefaultCaretImage;
  /**
   * The styled text's initial caret.
   * @since 3.0
   */
  private Caret fInitialCaret;
  /**
   * The operation approver used to warn on undoing of non-local operations.
   * @since 3.1
   */
  private IOperationApprover fNonLocalOperationApprover;
  /**
   * The operation approver used to warn of linear undo violations.
   * @since 3.1
   */
  private IOperationApprover fLinearUndoViolationApprover;
  /**
   * This editor's memento holding data for restoring it after restart.
   * @since 3.3
   */
  private IMemento fMementoToRestore;
  /**
   * This editor's savable.
   * @since 3.3
   */
  private TextEditorSavable fSavable;
  /**
   * Tells whether text drag and drop has been installed on the control.
   * @since 3.3
   */
  private boolean fIsTextDragAndDropInstalled= false;
  /**
   * Helper token to decide whether drag and
   * drop happens inside the same editor.
   * @since 3.3
   */
  private Object fTextDragAndDropToken;
  /**
   * The information presenter, may be <code>null</code>.
   * @since 3.3
   */
  private FocusedInformationPresenter fInformationPresenter;

  /**
   * Tells whether this editor has been activated at least once.
   * @since 3.3.2
   */
  private boolean fHasBeenActivated= false;
  /**
   * Key binding support for the quick assist assistant.
   * @since 3.4
   */
  private KeyBindingSupportForAssistant fKeyBindingSupportForQuickAssistant;

  /**
   * Key binding support for the quick assist assistant.
   * @since 3.5
   */
  private KeyBindingSupportForAssistant fKeyBindingSupportForContentAssistant;

  /**
   * The save action.
   * @since 3.6.1
   */
  private IWorkbenchAction fSaveAction;


  /**
   * Creates a new text editor. If not explicitly set, this editor uses
   * a <code>SourceViewerConfiguration</code> to configure its
   * source viewer. This viewer does not have a range indicator installed,
   * nor any menu id set. By default, the created editor runs in 1.0 context
   * menu registration compatibility mode.
   */
  protected AbstractTextEditor() {
    super();
    fEditorContextMenuId= null;
    fRulerContextMenuId= null;
    fHelpContextId= null;
  }

  /*
   * @see ITextEditor#getDocumentProvider()
   */
  public IDocumentProvider getDocumentProvider() {
    return fExplicitDocumentProvider;
  }

  /**
   * Returns the editor's range indicator. May return <code>null</code> if no
   * range indicator is installed.
   *
   * @return the editor's range indicator which may be <code>null</code>
   */
  protected final Annotation getRangeIndicator() {
    return fRangeIndicator;
  }

  /**
   * Returns the editor's source viewer configuration. May return <code>null</code>
   * before the editor's part has been created and after disposal.
   *
   * @return the editor's source viewer configuration which may be <code>null</code>
   */
  protected final SourceViewerConfiguration getSourceViewerConfiguration() {
    return fConfiguration;
  }

  /**
   * Returns the editor's source viewer. May return <code>null</code> before
   * the editor's part has been created and after disposal.
   *
   * @return the editor's source viewer which may be <code>null</code>
   */
  protected final ISourceViewer getSourceViewer() {
    return fSourceViewer;
  }

  /**
   * Returns the editor's vertical ruler. May return <code>null</code> before
   * the editor's part has been created and after disposal.
   *
   * @return the editor's vertical ruler which may be <code>null</code>
   */
  protected final IVerticalRuler getVerticalRuler() {
    return fVerticalRuler;
  }

  /**
   * Returns the editor's context menu id. May return <code>null</code> before
   * the editor's part has been created.
   *
   * @return the editor's context menu id which may be <code>null</code>
   */
  protected final String getEditorContextMenuId() {
    return fEditorContextMenuId;
  }

  /**
   * Returns the ruler's context menu id. May return <code>null</code> before
   * the editor's part has been created.
   *
   * @return the ruler's context menu id which may be <code>null</code>
   */
  protected final String getRulerContextMenuId() {
    return fRulerContextMenuId;
  }

  /**
   * Returns the editor's help context id or <code>null</code> if none has
   * been set.
   *
   * @return the editor's help context id which may be <code>null</code>
   */
  protected final String getHelpContextId() {
    return fHelpContextId;
  }

  /**
   * Returns this editor's preference store or <code>null</code> if none has
   * been set.
   *
   * @return this editor's preference store which may be <code>null</code>
   */
  protected final IPreferenceStore getPreferenceStore() {
    return fPreferenceStore;
  }

  /**
   * Sets this editor's document provider. This method must be
   * called before the editor's control is created.
   *
   * @param provider the document provider
   */
  protected void setDocumentProvider(IDocumentProvider provider) {
    fExplicitDocumentProvider= provider;
  }

  /**
   * Sets this editor's source viewer configuration used to configure its
   * internal source viewer. This method must be called before the editor's
   * control is created. If not, this editor uses a <code>SourceViewerConfiguration</code>.
   *
   * @param configuration the source viewer configuration object
   */
  protected void setSourceViewerConfiguration(SourceViewerConfiguration configuration) {
    Assert.isNotNull(configuration);
    fConfiguration= configuration;
  }

  /**
   * Sets the annotation which this editor uses to represent the highlight
   * range if the editor is configured to show the entire document. If the
   * range indicator is not set, this editor will not show a range indication.
   *
   * @param rangeIndicator the annotation
   */
  protected void setRangeIndicator(Annotation rangeIndicator) {
    Assert.isNotNull(rangeIndicator);
    fRangeIndicator= rangeIndicator;
  }

  /**
   * Sets this editor's context menu id.
   *
   * @param contextMenuId the context menu id
   */
  protected void setEditorContextMenuId(String contextMenuId) {
    Assert.isNotNull(contextMenuId);
    fEditorContextMenuId= contextMenuId;
  }

  /**
   * Sets the ruler's context menu id.
   *
   * @param contextMenuId the context menu id
   */
  protected void setRulerContextMenuId(String contextMenuId) {
    Assert.isNotNull(contextMenuId);
    fRulerContextMenuId= contextMenuId;
  }

  /**
   * Sets the context menu registration 1.0 compatibility mode. (See class
   * description for more details.)
   *
   * @param compatible <code>true</code> if compatibility mode is enabled
   * @since 2.0
   */
  protected final void setCompatibilityMode(boolean compatible) {
    fCompatibilityMode= compatible;
  }

  /**
   * Sets the editor's help context id.
   *
   * @param helpContextId the help context id
   */
  protected void setHelpContextId(String helpContextId) {
    Assert.isNotNull(helpContextId);
    fHelpContextId= helpContextId;
  }

  /**
   * Sets the key binding scopes for this editor.
   *
   * @param scopes a non-empty array of key binding scope identifiers
   * @since 2.1
   */
  protected void setKeyBindingScopes(String[] scopes) {
    Assert.isTrue(scopes != null && scopes.length > 0);
    fKeyBindingScopes= scopes;
  }

  /**
   * Sets this editor's preference store. This method must be
   * called before the editor's control is created.
   *
   * @param store the preference store or <code>null</code> to remove the
   *       preference store
   */
  protected void setPreferenceStore(IPreferenceStore store) {
    if (fPreferenceStore != null) {
      fPreferenceStore.removePropertyChangeListener(fPropertyChangeListener);
      fPreferenceStore.removePropertyChangeListener(fFontPropertyChangeListener);
    }

    fPreferenceStore= store;

    if (fPreferenceStore != null) {
      fPreferenceStore.addPropertyChangeListener(fPropertyChangeListener);
      fPreferenceStore.addPropertyChangeListener(fFontPropertyChangeListener);
    }
  }

  /*
   * @see ITextEditor#isEditable()
   */
  public boolean isEditable() {
    IDocumentProvider provider= getDocumentProvider();
    if (provider instanceof IDocumentProviderExtension) {
      IDocumentProviderExtension extension= (IDocumentProviderExtension) provider;
      return extension.isModifiable(getEditorInput());
    }
    return false;
  }

  /**
   * {@inheritDoc}
   * <p>
   * Returns <code>null</code> after disposal.
   * </p>
   *
   * @return the selection provider or <code>null</code> if the editor has
   *         been disposed
   */
  public ISelectionProvider getSelectionProvider() {
    return fSelectionProvider;
  }

  /**
   * Remembers the current selection of this editor. This method is called when, e.g.,
   * the content of the editor is about to be reverted to the saved state. This method
   * remembers the selection in a semantic format, i.e., in a format which allows to
   * restore the selection even if the originally selected text is no longer part of the
   * editor's content.
   * <p>
   * Subclasses should implement this method including all necessary state. This
   * default implementation remembers the textual range only and is thus purely
   * syntactic.</p>
   *
   * @see #restoreSelection()
   * @since 2.0
   */
  protected void rememberSelection() {
    fRememberedSelection= doGetSelection();
  }

  /**
   * Returns the current selection.
   * @return ISelection
   * @since 2.1
   */
  protected ISelection doGetSelection() {
    ISelectionProvider sp= null;
    if (fSourceViewer != null)
      sp= fSourceViewer.getSelectionProvider();
    return (sp == null ? null : sp.getSelection());
  }

  /**
   * Restores a selection previously remembered by <code>rememberSelection</code>.
   * Subclasses may reimplement this method and thereby semantically adapt the
   * remembered selection. This default implementation just selects the
   * remembered textual range.
   *
   * @see #rememberSelection()
   * @since 2.0
   */
  protected void restoreSelection() {
    if (fRememberedSelection instanceof ITextSelection) {
      ITextSelection textSelection= (ITextSelection)fRememberedSelection;
      if (isValidSelection(textSelection.getOffset(), textSelection.getLength()))
        doSetSelection(fRememberedSelection);
    }
    fRememberedSelection= null;
  }

  /**
   * Tells whether the given selection is valid.
   *
   * @param offset the offset of the selection
   * @param length the length of the selection
   * @return <code>true</code> if the selection is valid
   * @since 2.1
   */
  private boolean isValidSelection(int offset, int length) {
    IDocumentProvider provider= getDocumentProvider();
    if (provider != null) {
      IDocument document= provider.getDocument(getEditorInput());
      if (document != null) {
        int end= offset + length;
        int documentLength= document.getLength();
        return 0 <= offset  && offset <= documentLength && 0 <= end && end <= documentLength && length >= 0;
      }
    }
    return false;
  }

  /**
   * Sets the given selection.
   *
   * @param selection the selection
   * @since 2.1
   */
  protected void doSetSelection(ISelection selection) {
    if (selection instanceof ITextSelection) {
      ITextSelection textSelection= (ITextSelection) selection;
      selectAndReveal(textSelection.getOffset(), textSelection.getLength());
    }
  }

  /**
   * Creates the listener on this editor's context menus.
   *
   * @return the created menu listener
   * @since 3.4
   */
  protected IMenuListener createContextMenuListener() {
    return new IMenuListener() {
      public void menuAboutToShow(IMenuManager menu) {
        String id= menu.getId();
        if (getRulerContextMenuId().equals(id)) {
          setFocus();
          rulerContextMenuAboutToShow(menu);
        } else if (getEditorContextMenuId().equals(id)) {
          setFocus();
          editorContextMenuAboutToShow(menu);
        }
      }
    };
  }

  /**
   * Creates and returns the listener on this editor's context menus.
   *
   * @return the menu listener
   */
  protected final IMenuListener getContextMenuListener() {
    if (fMenuListener == null)
      fMenuListener= createContextMenuListener();
    return fMenuListener;
  }

  /**
   * Creates and returns the listener on this editor's vertical ruler.
   *
   * @return the mouse listener
   */
  protected final MouseListener getRulerMouseListener() {
    if (fMouseListener == null) {
      fMouseListener= new MouseListener() {

        private boolean fDoubleClicked= false;
        private final int fDoubleClickTime= getSite().getShell().getDisplay().getDoubleClickTime();
        private long fMouseUpDelta= 0;

        private void triggerAction(String actionID, MouseEvent e) {
          IAction action= getAction(actionID);
          if (action != null) {
            if (action instanceof IUpdate)
              ((IUpdate) action).update();
            if (action.isEnabled()) {
              Event event= new Event();
              event.type= fDoubleClicked ? SWT.MouseDoubleClick : SWT.MouseUp;
              event.display= e.display;
              event.widget= e.widget;
              event.time= e.time;
              event.data= e.data;
              event.x= e.x;
              event.y= e.y;
              event.button= e.button;
              event.stateMask= e.stateMask;
              event.count= e.count;
              action.runWithEvent(event);
            }
          }
        }

        public void mouseUp(final MouseEvent e) {
          setFocus();
          final int delay= fMouseUpDelta == 0 ? 0 : fDoubleClickTime - (int)(System.currentTimeMillis() - fMouseUpDelta);
          if (1 != e.button)
            return;

          Runnable runnable= new Runnable() {
            public void run() {
              if (!fDoubleClicked)
                triggerAction(ITextEditorActionConstants.RULER_CLICK, e);
            }
          };
          if (delay <= 0)
            runnable.run();
          else
            e.widget.getDisplay().timerExec(delay, runnable);
        }

        public void mouseDoubleClick(MouseEvent e) {
          if (1 == e.button) {
            fDoubleClicked= true;
            triggerAction(ITextEditorActionConstants.RULER_DOUBLE_CLICK, e);
          }
        }

        public void mouseDown(MouseEvent e) {
          fMouseUpDelta= System.currentTimeMillis();
          fDoubleClicked= false;
          if (fRulerContextMenu != null && !fRulerContextMenu.isDisposed()) {
            Display display= fRulerContextMenu.getDisplay();
            Point location= display.getCursorLocation();
            fRulerContextMenu.setLocation(location.x, location.y);
          }
        }
      };
    }
    return fMouseListener;
  }

  /**
   * Returns this editor's selection changed listener to be installed
   * on the editor's source viewer.
   *
   * @return the listener
   */
  protected final ISelectionChangedListener getSelectionChangedListener() {
    if (fSelectionChangedListener == null) {
      fSelectionChangedListener= new ISelectionChangedListener() {

        private Runnable fRunnable= new Runnable() {
          public void run() {
            // check whether editor has not been disposed yet
            if (fSourceViewer != null && fSourceViewer.getDocument() != null) {
              updateSelectionDependentActions();
            }
          }
        };

        private Display fDisplay;

        public void selectionChanged(SelectionChangedEvent event) {
          if (fDisplay == null)
            fDisplay= getSite().getShell().getDisplay();
          fDisplay.asyncExec(fRunnable);
          handleCursorPositionChanged();
        }
      };
    }

    return fSelectionChangedListener;
  }

  /**
   * Returns this editor's "cursor" listener to be installed on the editor's
   * source viewer. This listener is listening to key and mouse button events.
   * It triggers the updating of the status line by calling
   * <code>handleCursorPositionChanged()</code>.
   *
   * @return the listener
   * @since 2.0
   */
  protected final ICursorListener getCursorListener() {
    if (fCursorListener == null) {
      fCursorListener= new ICursorListener() {

        public void keyPressed(KeyEvent e) {
          handleCursorPositionChanged();
        }

        public void keyReleased(KeyEvent e) {
        }

        public void mouseDoubleClick(MouseEvent e) {
        }

        public void mouseDown(MouseEvent e) {
        }

        public void mouseUp(MouseEvent e) {
          handleCursorPositionChanged();
        }
      };
    }
    return fCursorListener;
  }

  /**
   * Implements the <code>init</code> method of <code>IEditorPart</code>.
   * Subclasses replacing <code>init</code> may choose to call this method in
   * their implementation.
   *
   * @param window the workbench window
   * @param site the editor's site
   * @param input the editor input for the editor being created
   * @throws PartInitException if {@link #doSetInput(IEditorInput)} fails or gets canceled
   *
   * @see org.eclipse.ui.IEditorPart#init(org.eclipse.ui.IEditorSite, org.eclipse.ui.IEditorInput)
   * @since 2.1
   */
  protected final void internalInit(IWorkbenchWindow window, final IEditorSite site, final IEditorInput input) throws PartInitException {

    IRunnableWithProgress runnable= new IRunnableWithProgress() {
      public void run(IProgressMonitor monitor) throws InvocationTargetException, InterruptedException {
        try {

          if (getDocumentProvider() instanceof IDocumentProviderExtension2) {
            IDocumentProviderExtension2 extension= (IDocumentProviderExtension2) getDocumentProvider();
            extension.setProgressMonitor(monitor);
          }

          doSetInput(input);

        } catch (CoreException x) {
          throw new InvocationTargetException(x);
        } finally {
          if (getDocumentProvider() instanceof IDocumentProviderExtension2) {
            IDocumentProviderExtension2 extension= (IDocumentProviderExtension2) getDocumentProvider();
            extension.setProgressMonitor(null);
          }
        }
      }
    };

    try {
//      When using the progress service always a modal dialog pops up. The site should be asked for a runnable context
//      which could be the workbench window or the progress service, depending on what the site represents.
//      getSite().getWorkbenchWindow().getWorkbench().getProgressService().run(false, true, runnable);

      getSite().getWorkbenchWindow().run(false, true, runnable);

    } catch (InterruptedException x) {
    } catch (InvocationTargetException x) {
      Throwable t= x.getTargetException();
      if (t instanceof CoreException) {
                /*
                /* XXX: Remove unpacking of CoreException once the following bug is
                 *    fixed: https://bugs.eclipse.org/bugs/show_bug.cgi?id=81640
                 */
                CoreException e= (CoreException)t;
                IStatus status= e.getStatus();
                if (status.getException() != null)
                    throw new PartInitException(status);
                 throw new PartInitException(new Status(status.getSeverity(), status.getPlugin(), status.getCode(), status.getMessage(), t));
            }
      throw new PartInitException(new Status(IStatus.ERROR, TextEditorPlugin.PLUGIN_ID, IStatus.OK, EditorMessages.Editor_error_init, t));
    }
  }

  /*
   * @see IEditorPart#init(org.eclipse.ui.IEditorSite, org.eclipse.ui.IEditorInput)
   */
  public void init(final IEditorSite site, final IEditorInput input) throws PartInitException {

    setSite(site);

    internalInit(site.getWorkbenchWindow(), site, input);
    fActivationListener= new ActivationListener(site.getWorkbenchWindow().getPartService());
  }

  /**
   * Creates the vertical ruler to be used by this editor.
   * Subclasses may re-implement this method.
   *
   * @return the vertical ruler
   */
  protected IVerticalRuler createVerticalRuler() {
    return new VerticalRuler(VERTICAL_RULER_WIDTH);
  }

  /**
   * Adds enabled ruler contributions to the vertical ruler.
   * <p>
   * Clients may extend or replace.</p>
   *
   * @param ruler the composite ruler to add contributions to
   * @since 3.3
   */
  protected void updateContributedRulerColumns(CompositeRuler ruler) {
    IColumnSupport support= (IColumnSupport)getAdapter(IColumnSupport.class);
    if (support == null)
      return;

    RulerColumnPreferenceAdapter adapter= null;
    if (fPreferenceStore != null)
      adapter= new RulerColumnPreferenceAdapter(getPreferenceStore(), PREFERENCE_RULER_CONTRIBUTIONS);

    RulerColumnRegistry registry= RulerColumnRegistry.getDefault();
    List descriptors= registry.getColumnDescriptors();
    for (Iterator it= descriptors.iterator(); it.hasNext();) {
      final RulerColumnDescriptor descriptor= (RulerColumnDescriptor) it.next();
      support.setColumnVisible(descriptor, adapter == null || adapter.isEnabled(descriptor));
    }
  }

  /**
   * Creates the column support to be used by this editor to manage the
   * contributed ruler columns.
   * Subclasses may re-implement this method using the {@link ColumnSupport},
   * e.g. by returning <code>new ColumnSupport(this, RulerColumnRegistry.getDefault());</code>.
   * <p>
   * <strong>Note:</strong> If you override this method to provide column support you will
   * also need to override {@link #createVerticalRuler()} to return a {@link CompositeRuler}.</p>
   * <p>
   * Out of the box this class does not install this support and hence this
   * implementation always returns <code>null</code>.</p>
   *
   * @return the column support or <code>null</code> if none
   * @since 3.3
   */
  protected IColumnSupport createColumnSupport() {
    return null;
  }

  /**
   * Creates the source viewer to be used by this editor.
   * Subclasses may re-implement this method.
   *
   * @param parent the parent control
   * @param ruler the vertical ruler
   * @param styles style bits, <code>SWT.WRAP</code> is currently not supported
   * @return the source viewer
   */
  protected ISourceViewer createSourceViewer(Composite parent, IVerticalRuler ruler, int styles) {
    return new SourceViewer(parent, ruler, styles);
  }

  /**
   * Initializes the drag and drop support for the given viewer based on
   * provided editor adapter for drop target listeners.
   *
   * @param viewer the viewer
   * @since 3.0
   */
  protected void initializeDragAndDrop(ISourceViewer viewer) {
    IDragAndDropService dndService= (IDragAndDropService)getSite().getService(IDragAndDropService.class);
    if (dndService == null)
      return;

    ITextEditorDropTargetListener listener= (ITextEditorDropTargetListener) getAdapter(ITextEditorDropTargetListener.class);

    if (listener == null) {
      Object object= Platform.getAdapterManager().loadAdapter(this, "org.eclipse.ui.texteditor.ITextEditorDropTargetListener"); //$NON-NLS-1$
      if (object instanceof ITextEditorDropTargetListener)
        listener= (ITextEditorDropTargetListener)object;
    }

    if (listener != null)
      dndService.addMergedDropTarget(viewer.getTextWidget(), DND.DROP_MOVE | DND.DROP_COPY, listener.getTransfers(), listener);

    IPreferenceStore store= getPreferenceStore();
    if (store != null && store.getBoolean(PREFERENCE_TEXT_DRAG_AND_DROP_ENABLED))
      installTextDragAndDrop(viewer);

  }

  /**
   * The <code>AbstractTextEditor</code> implementation of this
   * <code>IWorkbenchPart</code> method creates the vertical ruler and
   * source viewer.
   * <p>
   * Subclasses may extend this method. Besides extending this method, the
   * behavior of <code>createPartControl</code> may be customized by
   * calling, extending or replacing the following methods: <br>
   * Subclasses may supply customized implementations for some members using
   * the following methods before <code>createPartControl</code> is invoked:
   * <ul>
   * <li>
   * {@linkplain #setSourceViewerConfiguration(SourceViewerConfiguration) setSourceViewerConfiguration}
   * to supply a custom source viewer configuration,</li>
   * <li>{@linkplain #setRangeIndicator(Annotation) setRangeIndicator} to
   * provide a range indicator,</li>
   * <li>{@linkplain #setHelpContextId(String) setHelpContextId} to provide a
   * help context id,</li>
   * <li>{@linkplain #setEditorContextMenuId(String) setEditorContextMenuId}
   * to set a custom context menu id,</li>
   * <li>{@linkplain #setRulerContextMenuId(String) setRulerContextMenuId} to
   * set a custom ruler context menu id.</li>
   * </ul>
   * <br>
   * Subclasses may replace the following methods called from within
   * <code>createPartControl</code>:
   * <ul>
   * <li>{@linkplain #createVerticalRuler() createVerticalRuler} to supply a
   * custom vertical ruler,</li>
   * <li>{@linkplain #createSourceViewer(Composite, IVerticalRuler, int) createSourceViewer}
   * to supply a custom source viewer,</li>
   * <li>{@linkplain #getSelectionProvider() getSelectionProvider} to supply
   * a custom selection provider.</li>
   * </ul>
   * <br>
   * Subclasses may extend the following methods called from within
   * <code>createPartControl</code>:
   * <ul>
   * <li>
   * {@linkplain #initializeViewerColors(ISourceViewer) initializeViewerColors}
   * to customize the viewer color scheme (may also be replaced),</li>
   * <li>
   * {@linkplain #initializeDragAndDrop(ISourceViewer) initializeDragAndDrop}
   * to customize drag and drop (may also be replaced),</li>
   * <li>{@linkplain #createNavigationActions() createNavigationActions} to
   * add navigation actions,</li>
   * <li>{@linkplain #createActions() createActions} to add text editor
   * actions.</li>
   * </ul>
   * </p>
   *
   * @param parent the parent composite
   */
  public void createPartControl(Composite parent) {

    fVerticalRuler= createVerticalRuler();

    int styles= SWT.V_SCROLL | SWT.H_SCROLL | SWT.MULTI | SWT.BORDER | SWT.FULL_SELECTION;
    fSourceViewer= createSourceViewer(parent, fVerticalRuler, styles);

    if (fConfiguration == null)
      fConfiguration= new SourceViewerConfiguration();
    fSourceViewer.configure(fConfiguration);

    if (fSourceViewer instanceof ISourceViewerExtension4)
      fKeyBindingSupportForContentAssistant= new KeyBindingSupportForAssistant(((ISourceViewerExtension4)fSourceViewer));

    if (fSourceViewer instanceof ISourceViewerExtension3) {
      IQuickAssistAssistant assistant= ((ISourceViewerExtension3)fSourceViewer).getQuickAssistAssistant();
      if (assistant != null)
        fKeyBindingSupportForQuickAssistant= new KeyBindingSupportForAssistant(assistant);
    }

    if (fRangeIndicator != null)
      fSourceViewer.setRangeIndicator(fRangeIndicator);

    fSourceViewer.addTextListener(fTextListener);
    fSourceViewer.addTextInputListener(fTextListener);
    getSelectionProvider().addSelectionChangedListener(getSelectionChangedListener());

    initializeViewerFont(fSourceViewer);
    initializeViewerColors(fSourceViewer);
    initializeFindScopeColor(fSourceViewer);
    initializeDragAndDrop(fSourceViewer);

    StyledText styledText= fSourceViewer.getTextWidget();

    /* gestures commented out until proper solution (i.e. preference page) can be found
     * for bug # 28417:
     *
    final Map gestureMap= new HashMap();

    gestureMap.put("E", IWorkbenchCommandConstants.NAVIGATE_FORWARDHISTORY); //$NON-NLS-1$
    gestureMap.put("N", IWorkbenchCommandConstants.FILE_SAVE); //$NON-NLS-1$
    gestureMap.put("NW", IWorkbenchCommandConstants.FILE_SAVEALL); //$NON-NLS-1$
    gestureMap.put("S", IWorkbenchCommandConstants.FILE_CLOSE); //$NON-NLS-1$
    gestureMap.put("SW", IWorkbenchCommandConstants.FILE_CLOSEALL); //$NON-NLS-1$
    gestureMap.put("W", IWorkbenchCommandConstants.NAVIGATE_BACKWARDHISTORY); //$NON-NLS-1$
    gestureMap.put("EN", IWorkbenchCommandConstants.EDIT_COPY); //$NON-NLS-1$
    gestureMap.put("ES", IWorkbenchCommandConstants.EDIT_PASTE); //$NON-NLS-1$
    gestureMap.put("EW", IWorkbenchCommandConstants.EDIT_CUT); //$NON-NLS-1$

    Capture capture= Capture.create();
    capture.setControl(styledText);

    capture.addCaptureListener(new CaptureListener() {
      public void gesture(Gesture gesture) {
        if (gesture.getPen() == 3) {
          String actionId= (String) gestureMap.get(Util.recognize(gesture.getPoints(), 20));

          if (actionId != null) {
            IKeyBindingService keyBindingService= getEditorSite().getKeyBindingService();

            if (keyBindingService instanceof KeyBindingService) {
              IAction action= ((KeyBindingService) keyBindingService).getAction(actionId);

              if (action != null) {
                if (action instanceof IUpdate)
                  ((IUpdate) action).update();

                if (action.isEnabled())
                  action.run();
              }
            }

            return;
          }

          fTextContextMenu.setVisible(true);
        }
      };
    });
    */

    styledText.addMouseListener(getCursorListener());
    styledText.addKeyListener(getCursorListener());

    // Disable orientation switching until we fully support it.
    styledText.addListener(SWT.OrientationChange, new Listener() {
      public void handleEvent(Event event) {
        event.doit= false;
      }
    });

    if (getHelpContextId() != null)
      PlatformUI.getWorkbench().getHelpSystem().setHelp(styledText, getHelpContextId());


    String id= fEditorContextMenuId != null ?  fEditorContextMenuId : DEFAULT_EDITOR_CONTEXT_MENU_ID;

    MenuManager manager= new MenuManager(id, id);
    manager.setRemoveAllWhenShown(true);
    manager.addMenuListener(getContextMenuListener());
    fTextContextMenu= manager.createContextMenu(styledText);

    // comment this line if using gestures, above.
    styledText.setMenu(fTextContextMenu);

    if (fEditorContextMenuId != null)
      getEditorSite().registerContextMenu(fEditorContextMenuId, manager, getSelectionProvider(), isEditorInputIncludedInContextMenu());
    else if (fCompatibilityMode)
      getEditorSite().registerContextMenu(DEFAULT_EDITOR_CONTEXT_MENU_ID, manager, getSelectionProvider(), isEditorInputIncludedInContextMenu());

    if ((fEditorContextMenuId != null && fCompatibilityMode) || fEditorContextMenuId  == null) {
      String partId= getEditorSite().getId();
      if (partId != null)
        getEditorSite().registerContextMenu(partId + ".EditorContext", manager, getSelectionProvider(), isEditorInputIncludedInContextMenu()); //$NON-NLS-1$
    }

    getEditorSite().registerContextMenu(COMMON_EDITOR_CONTEXT_MENU_ID, manager, getSelectionProvider(), false);

    if (fEditorContextMenuId == null)
      fEditorContextMenuId= DEFAULT_EDITOR_CONTEXT_MENU_ID;


    id= fRulerContextMenuId != null ? fRulerContextMenuId : DEFAULT_RULER_CONTEXT_MENU_ID;
    manager= new MenuManager(id, id);
    manager.setRemoveAllWhenShown(true);
    manager.addMenuListener(getContextMenuListener());

    Control rulerControl= fVerticalRuler.getControl();
    fRulerContextMenu= manager.createContextMenu(rulerControl);
    rulerControl.setMenu(fRulerContextMenu);
    rulerControl.addMouseListener(getRulerMouseListener());

    if (fRulerContextMenuId != null)
      getEditorSite().registerContextMenu(fRulerContextMenuId, manager, getSelectionProvider(), false);
    else if (fCompatibilityMode)
      getEditorSite().registerContextMenu(DEFAULT_RULER_CONTEXT_MENU_ID, manager, getSelectionProvider(), false);

    if ((fRulerContextMenuId != null && fCompatibilityMode) || fRulerContextMenuId  == null) {
      String partId= getSite().getId();
      if (partId != null)
        getEditorSite().registerContextMenu(partId + ".RulerContext", manager, getSelectionProvider(), false); //$NON-NLS-1$
    }

    getEditorSite().registerContextMenu(COMMON_RULER_CONTEXT_MENU_ID, manager, getSelectionProvider(), false);

    if (fRulerContextMenuId == null)
      fRulerContextMenuId= DEFAULT_RULER_CONTEXT_MENU_ID;

    getSite().setSelectionProvider(getSelectionProvider());

    fSelectionListener= new SelectionListener();
    fSelectionListener.install(getSelectionProvider());
    fSelectionListener.setDocument(getDocumentProvider().getDocument(getEditorInput()));

    initializeActivationCodeTrigger();

    createNavigationActions();
    createAccessibilityActions();
    createActions();

    initializeSourceViewer(getEditorInput());

    /* since 3.2 - undo redo actions should be created after
     * the source viewer is initialized, so that the undo manager
     * can obtain its undo context from its document.
     */
    createUndoRedoActions();

    JFaceResources.getFontRegistry().addListener(fFontPropertyChangeListener);

    IVerticalRuler ruler= getVerticalRuler();
    if (ruler instanceof CompositeRuler)
      updateContributedRulerColumns((CompositeRuler) ruler);
  }

  /**
   * Installs text drag and drop on the given source viewer.
   *
   * @param viewer the viewer
   * @since 3.3
   */
  protected void installTextDragAndDrop(final ISourceViewer viewer) {
    if (viewer == null || fIsTextDragAndDropInstalled)
      return;

    final IDragAndDropService dndService= (IDragAndDropService)getSite().getService(IDragAndDropService.class);
    if (dndService == null)
      return;

    final StyledText st= viewer.getTextWidget();

    // Install drag source
    final ISelectionProvider selectionProvider= viewer.getSelectionProvider();
    final DragSource source= new DragSource(st, DND.DROP_COPY | DND.DROP_MOVE);
    source.setTransfer(new Transfer[] {TextTransfer.getInstance()});
    source.addDragListener(new DragSourceAdapter() {
      String fSelectedText;
      Point fSelection;
      public void dragStart(DragSourceEvent event) {
        fTextDragAndDropToken= null;
        try {
          fSelection= st.getSelection();
          event.doit= isLocationSelected(new Point(event.x, event.y));

          ISelection selection= selectionProvider.getSelection();
          if (selection instanceof ITextSelection)
            fSelectedText= ((ITextSelection)selection).getText();
          else // fallback to widget
            fSelectedText= st.getSelectionText();
        } catch (IllegalArgumentException ex) {
          event.doit= false;
        }
      }

      private boolean isLocationSelected(Point point) {
        // FIXME: https://bugs.eclipse.org/bugs/show_bug.cgi?id=260922
        if (isBlockSelectionModeEnabled())
          return false;

        int offset= st.getOffsetAtLocation(point);
        Point p= st.getLocationAtOffset(offset);
        if (p.x > point.x)
          offset--;
        return offset >= fSelection.x && offset < fSelection.y;
      }

      public void dragSetData(DragSourceEvent event) {
        event.data= fSelectedText;
        fTextDragAndDropToken= this; // Can be any non-null object
      }

      public void dragFinished(DragSourceEvent event) {
        try {
          if (event.detail == DND.DROP_MOVE && validateEditorInputState()) {
            Point newSelection= st.getSelection();
            int length= fSelection.y - fSelection.x;
            int delta= 0;
            if (newSelection.x < fSelection.x)
              delta= length;
            st.replaceTextRange(fSelection.x + delta, length, ""); //$NON-NLS-1$

            if (fTextDragAndDropToken == null) {
              // Move in same editor - end compound change
              IRewriteTarget target= (IRewriteTarget)getAdapter(IRewriteTarget.class);
              if (target != null)
                target.endCompoundChange();
            }

          }
        } finally {
          fTextDragAndDropToken= null;
        }
      }
    });

    // Install drag target
    DropTargetListener dropTargetListener= new DropTargetAdapter() {

      private Point fSelection;

      public void dragEnter(DropTargetEvent event) {
        fTextDragAndDropToken= null;
        fSelection= st.getSelection();
        if (event.detail == DND.DROP_DEFAULT) {
          if ((event.operations & DND.DROP_MOVE) != 0) {
            event.detail= DND.DROP_MOVE;
          } else if ((event.operations & DND.DROP_COPY) != 0) {
            event.detail= DND.DROP_COPY;
          } else {
            event.detail= DND.DROP_NONE;
          }
        }
      }

      public void dragOperationChanged(DropTargetEvent event) {
        if (event.detail == DND.DROP_DEFAULT) {
          if ((event.operations & DND.DROP_MOVE) != 0) {
            event.detail= DND.DROP_MOVE;
          } else if ((event.operations & DND.DROP_COPY) != 0) {
            event.detail= DND.DROP_COPY;
          } else {
            event.detail= DND.DROP_NONE;
          }
        }
      }

      public void dragOver(DropTargetEvent event) {
        event.feedback |= DND.FEEDBACK_SCROLL;
      }

      public void drop(DropTargetEvent event) {
        try {
          if (fTextDragAndDropToken != null && event.detail == DND.DROP_MOVE) {
            // Move in same editor
            int caretOffset= st.getCaretOffset();
            if (fSelection.x <= caretOffset && caretOffset <= fSelection.y) {
              event.detail= DND.DROP_NONE;
              return;
            }

            // Start compound change
            IRewriteTarget target= (IRewriteTarget)getAdapter(IRewriteTarget.class);
            if (target != null)
              target.beginCompoundChange();
          }

          if (!validateEditorInputState()) {
            event.detail= DND.DROP_NONE;
            return;
          }

          String text= (String)event.data;
          if (isBlockSelectionModeEnabled()) {
            // FIXME fix block selection and DND
//            if (fTextDNDColumnSelection != null && fTextDragAndDropToken != null && event.detail == DND.DROP_MOVE) {
//              // DND_MOVE within same editor - remove origin before inserting
//              Rectangle newSelection= st.getColumnSelection();
//              st.replaceColumnSelection(fTextDNDColumnSelection, ""); //$NON-NLS-1$
//              st.replaceColumnSelection(newSelection, text);
//              st.setColumnSelection(newSelection.x, newSelection.y, newSelection.x + fTextDNDColumnSelection.width - fTextDNDColumnSelection.x, newSelection.y + fTextDNDColumnSelection.height - fTextDNDColumnSelection.y);
//            } else {
//              Point newSelection= st.getSelection();
//              st.insert(text);
//              IDocument document= getDocumentProvider().getDocument(getEditorInput());
//              int startLine= st.getLineAtOffset(newSelection.x);
//              int startColumn= newSelection.x - st.getOffsetAtLine(startLine);
//              int endLine= startLine + document.computeNumberOfLines(text);
//              int endColumn= startColumn + TextUtilities.indexOf(document.getLegalLineDelimiters(), text, 0)[0];
//              st.setColumnSelection(startColumn, startLine, endColumn, endLine);
//            }
          } else {
            Point newSelection= st.getSelection();
            try {
              int modelOffset= widgetOffset2ModelOffset(viewer, newSelection.x);
              viewer.getDocument().replace(modelOffset, 0, text);
            } catch (BadLocationException e) {
              return;
            }
            st.setSelectionRange(newSelection.x, text.length());
          }
        } finally {
          fTextDragAndDropToken= null;
        }
      }
    };
    dndService.addMergedDropTarget(st, DND.DROP_MOVE | DND.DROP_COPY, new Transfer[] {TextTransfer.getInstance()}, dropTargetListener);

    fIsTextDragAndDropInstalled= true;
  }

  /**
   * Uninstalls text drag and drop from the given source viewer.
   *
   * @param viewer the viewer
   * @since 3.3
   */
  protected void uninstallTextDragAndDrop(ISourceViewer viewer) {
    if (viewer == null || !fIsTextDragAndDropInstalled)
      return;

    final IDragAndDropService dndService= (IDragAndDropService)getSite().getService(IDragAndDropService.class);
    if (dndService == null)
      return;

    StyledText st= viewer.getTextWidget();
    dndService.removeMergedDropTarget(st);

    DragSource dragSource= (DragSource)st.getData(DND.DRAG_SOURCE_KEY);
    if (dragSource != null) {
      dragSource.dispose();
      st.setData(DND.DRAG_SOURCE_KEY, null);
    }

    fIsTextDragAndDropInstalled= false;
  }

  /**
     * Tells whether the editor input should be included when adding object
     * contributions to this editor's context menu.
     * <p>
     * This implementation always returns <code>true</code>.
     * </p>
     *
   * @return <code>true</code> if the editor input should be considered
     * @since 3.2
   */
  protected boolean isEditorInputIncludedInContextMenu() {
    return true;
  }

  /**
   * Initializes the activation code trigger.
   *
   * @since 2.1
   */
  private void initializeActivationCodeTrigger() {
    fActivationCodeTrigger.install();
    fActivationCodeTrigger.setScopes(fKeyBindingScopes);
  }

  /**
   * Initializes the given viewer's font.
   *
   * @param viewer the viewer
   * @since 2.0
   */
  private void initializeViewerFont(ISourceViewer viewer) {

    boolean isSharedFont= true;
    Font font= null;
    String symbolicFontName= getSymbolicFontName();

    if (symbolicFontName != null)
      font= JFaceResources.getFont(symbolicFontName);
    else if (fPreferenceStore != null) {
      // Backward compatibility
      if (fPreferenceStore.contains(JFaceResources.TEXT_FONT) && !fPreferenceStore.isDefault(JFaceResources.TEXT_FONT)) {
        FontData data= PreferenceConverter.getFontData(fPreferenceStore, JFaceResources.TEXT_FONT);

        if (data != null) {
          isSharedFont= false;
          font= new Font(viewer.getTextWidget().getDisplay(), data);
        }
      }
    }
    if (font == null)
      font= JFaceResources.getTextFont();

    if (!font.equals(fSourceViewer.getTextWidget().getFont())) {
      setFont(viewer, font);

      disposeFont();
      if (!isSharedFont)
        fFont= font;
    } else if (!isSharedFont) {
      font.dispose();
    }
  }

  /**
   * Disposes of the non-shared font.
   *
   * @since 3.5
   */
  private void disposeFont() {
    if (fFont != null) {
      fFont.dispose();
      fFont= null;
    }
  }

  /**
   * Sets the font for the given viewer sustaining selection and scroll position.
   *
   * @param sourceViewer the source viewer
   * @param font the font
   * @since 2.0
   */
  private void setFont(ISourceViewer sourceViewer, Font font) {
    if (sourceViewer.getDocument() != null) {

      ISelectionProvider provider= sourceViewer.getSelectionProvider();
      ISelection selection= provider.getSelection();
      int topIndex= sourceViewer.getTopIndex();

      StyledText styledText= sourceViewer.getTextWidget();
      Control parent= styledText;
      if (sourceViewer instanceof ITextViewerExtension) {
        ITextViewerExtension extension= (ITextViewerExtension) sourceViewer;
        parent= extension.getControl();
      }

      parent.setRedraw(false);

      styledText.setFont(font);

      if (fVerticalRuler instanceof IVerticalRulerExtension) {
        IVerticalRulerExtension e= (IVerticalRulerExtension) fVerticalRuler;
        e.setFont(font);
      }

      provider.setSelection(selection);
      sourceViewer.setTopIndex(topIndex);

      if (parent instanceof Composite) {
        Composite composite= (Composite) parent;
        composite.layout(true);
      }

      parent.setRedraw(true);


    } else {

      StyledText styledText= sourceViewer.getTextWidget();
      styledText.setFont(font);

      if (fVerticalRuler instanceof IVerticalRulerExtension) {
        IVerticalRulerExtension e= (IVerticalRulerExtension) fVerticalRuler;
        e.setFont(font);
      }
    }
  }

  /**
   * Creates a color from the information stored in the given preference store.
   * Returns <code>null</code> if there is no such information available.
   *
   * @param store the store to read from
   * @param key the key used for the lookup in the preference store
   * @param display the display used create the color
   * @return the created color according to the specification in the preference store
   * @since 2.0
   */
  private Color createColor(IPreferenceStore store, String key, Display display) {

    RGB rgb= null;

    if (store.contains(key)) {

      if (store.isDefault(key))
        rgb= PreferenceConverter.getDefaultColor(store, key);
      else
        rgb= PreferenceConverter.getColor(store, key);

      if (rgb != null)
        return new Color(display, rgb);
    }

    return null;
  }

  /**
   * Initializes the fore- and background colors of the given viewer for both
   * normal and selected text.
   *
   * @param viewer the viewer to be initialized
   * @since 2.0
   */
  protected void initializeViewerColors(ISourceViewer viewer) {

    IPreferenceStore store= getPreferenceStore();
    if (store != null) {

      StyledText styledText= viewer.getTextWidget();

      // ----------- foreground color --------------------
      Color color= store.getBoolean(PREFERENCE_COLOR_FOREGROUND_SYSTEM_DEFAULT)
        ? null
        : createColor(store, PREFERENCE_COLOR_FOREGROUND, styledText.getDisplay());
      styledText.setForeground(color);

      if (fForegroundColor != null)
        fForegroundColor.dispose();

      fForegroundColor= color;

      // ---------- background color ----------------------
      color= store.getBoolean(PREFERENCE_COLOR_BACKGROUND_SYSTEM_DEFAULT)
        ? null
        : createColor(store, PREFERENCE_COLOR_BACKGROUND, styledText.getDisplay());
      styledText.setBackground(color);

      if (fBackgroundColor != null)
        fBackgroundColor.dispose();

      fBackgroundColor= color;

      // ----------- selection foreground color --------------------
      color= store.getBoolean(PREFERENCE_COLOR_SELECTION_FOREGROUND_SYSTEM_DEFAULT)
        ? null
        : createColor(store, PREFERENCE_COLOR_SELECTION_FOREGROUND, styledText.getDisplay());
      styledText.setSelectionForeground(color);

      if (fSelectionForegroundColor != null)
        fSelectionForegroundColor.dispose();

      fSelectionForegroundColor= color;

      // ---------- selection background color ----------------------
      color= store.getBoolean(PREFERENCE_COLOR_SELECTION_BACKGROUND_SYSTEM_DEFAULT)
        ? null
        : createColor(store, PREFERENCE_COLOR_SELECTION_BACKGROUND, styledText.getDisplay());
      styledText.setSelectionBackground(color);

      if (fSelectionBackgroundColor != null)
        fSelectionBackgroundColor.dispose();

      fSelectionBackgroundColor= color;
    }
  }

  /**
   * Initializes the background color used for highlighting the document ranges
   * defining search scopes.
   *
   * @param viewer the viewer to initialize
   * @since 2.0
   */
  private void initializeFindScopeColor(ISourceViewer viewer) {

    IPreferenceStore store= getPreferenceStore();
    if (store != null) {

      StyledText styledText= viewer.getTextWidget();

      Color color= createColor(store, PREFERENCE_COLOR_FIND_SCOPE, styledText.getDisplay());

      IFindReplaceTarget target= viewer.getFindReplaceTarget();
      if (target != null && target instanceof IFindReplaceTargetExtension)
        ((IFindReplaceTargetExtension) target).setScopeHighlightColor(color);

      if (fFindScopeHighlightColor != null)
        fFindScopeHighlightColor.dispose();

      fFindScopeHighlightColor= color;
    }
  }


  /**
   * Initializes the editor's source viewer based on the given editor input.
   *
   * @param input the editor input to be used to initialize the source viewer
   */
  private void initializeSourceViewer(IEditorInput input) {

    IDocumentProvider documentProvider= getDocumentProvider();
    IAnnotationModel model= documentProvider.getAnnotationModel(input);
    IDocument document= documentProvider.getDocument(input);

    if (document != null) {
      fSourceViewer.setDocument(document, model);
      fSourceViewer.setEditable(isEditable());
      fSourceViewer.showAnnotations(model != null);
    }

    if (fElementStateListener instanceof IElementStateListenerExtension) {
      boolean isStateValidated= false;
      if (documentProvider instanceof IDocumentProviderExtension)
        isStateValidated= ((IDocumentProviderExtension)documentProvider).isStateValidated(input);

      IElementStateListenerExtension extension= (IElementStateListenerExtension) fElementStateListener;
      extension.elementStateValidationChanged(input, isStateValidated);
    }

    if (fInitialCaret == null)
      fInitialCaret= fSourceViewer.getTextWidget().getCaret();

    if (fIsOverwriting)
      fSourceViewer.getTextWidget().invokeAction(ST.TOGGLE_OVERWRITE);
    handleInsertModeChanged();

    if (isTabsToSpacesConversionEnabled())
      installTabsToSpacesConverter();

    if (fSourceViewer instanceof ITextViewerExtension8) {
      IPreferenceStore store= getPreferenceStore();
      EnrichMode mode= store != null ? convertEnrichModePreference(store.getInt(PREFERENCE_HOVER_ENRICH_MODE)) : EnrichMode.AFTER_DELAY;
      ((ITextViewerExtension8)fSourceViewer).setHoverEnrichMode(mode);
    }
  }

  /**
   * Converts the {link #PREFERENCE_HOVER_ENRICH_MODE} preference value to
   * {@link org.eclipse.jface.text.ITextViewerExtension8.EnrichMode}.
   *
   * @param mode the preference value
   * @return the enrich mode, can be <code>null</code>
   * @since 3.4
   */
  private EnrichMode convertEnrichModePreference(int mode) {
    switch (mode) {
      case -1:
        return null;
      case 0:
        return EnrichMode.AFTER_DELAY;
      case 1:
        return EnrichMode.IMMEDIATELY;
      case 2:
        return EnrichMode.ON_CLICK;
      default:
        Assert.isLegal(false);
      return null;
    }
  }

  /**
   * Initializes the editor's title based on the given editor input.
   * <p>
   * <strong>Note:</strong> We use the editor's image instead of the image from the
   * editor input to distinguish situations where the same editor input is
   * opened in different kinds of editors.
   * </p>
   *
   * @param input the editor input to be used
   */
  private void initializeTitle(IEditorInput input) {

    Image oldImage= fTitleImage;
    fTitleImage= null;
    String title= ""; //$NON-NLS-1$

    if (input != null) {
      IEditorRegistry editorRegistry= PlatformUI.getWorkbench().getEditorRegistry();
      IEditorDescriptor editorDesc= editorRegistry.findEditor(getSite().getId());
      ImageDescriptor imageDesc= editorDesc != null ? editorDesc.getImageDescriptor() : null;

      fTitleImage= imageDesc != null ? imageDesc.createImage() : null;
      title= input.getName();
    }

    setTitleImage(fTitleImage);
    setPartName(title);

    firePropertyChange(PROP_DIRTY);

    if (oldImage != null && !oldImage.isDisposed())
      oldImage.dispose();
  }

  /**
   * Hook method for setting the document provider for the given input.
   * This default implementation does nothing. Clients may
   * reimplement.
   *
   * @param input the input of this editor.
   * @since 3.0
   */
  protected void setDocumentProvider(IEditorInput input) {
  }

  /**
   * If there is no explicit document provider set, the implicit one is
   * re-initialized based on the given editor input.
   *
   * @param input the editor input.
   */
  private void updateDocumentProvider(IEditorInput input) {

    IProgressMonitor rememberedProgressMonitor= null;

    IDocumentProvider provider= getDocumentProvider();
    if (provider != null) {
      provider.removeElementStateListener(fElementStateListener);
      if (provider instanceof IDocumentProviderExtension2) {
        IDocumentProviderExtension2 extension= (IDocumentProviderExtension2) provider;
        rememberedProgressMonitor= extension.getProgressMonitor();
        extension.setProgressMonitor(null);
      }
    }

    setDocumentProvider(input);

    provider= getDocumentProvider();
    if (provider != null) {
      provider.addElementStateListener(fElementStateListener);
      if (provider instanceof IDocumentProviderExtension2) {
        IDocumentProviderExtension2 extension= (IDocumentProviderExtension2) provider;
        extension.setProgressMonitor(rememberedProgressMonitor);
      }
    }
  }

  /**
   * Called directly from <code>setInput</code> and from within a workspace
   * runnable from <code>init</code>, this method does the actual setting
   * of the editor input. Closes the editor if <code>input</code> is
   * <code>null</code>. Disconnects from any previous editor input and its
   * document provider and connects to the new one.
   * <p>
   * Subclasses may extend.
   * </p>
   *
   * @param input the input to be set
   * @exception CoreException if input cannot be connected to the document
   *            provider
   */
  protected void doSetInput(IEditorInput input) throws CoreException {
    ISaveablesLifecycleListener listener= (ISaveablesLifecycleListener)getSite().getService(ISaveablesLifecycleListener.class);
    if (listener == null)
      fSavable= null;

    if (input == null) {
      close(isSaveOnCloseNeeded());

      if (fSavable != null) {
        listener.handleLifecycleEvent(new SaveablesLifecycleEvent(this,  SaveablesLifecycleEvent.POST_CLOSE,  getSaveables(), false));
        fSavable.disconnectEditor();
        fSavable= null;
      }

    } else {
      boolean mustSendLifeCycleEvent= false;
      if (fSavable != null) {
        listener.handleLifecycleEvent(new SaveablesLifecycleEvent(this,  SaveablesLifecycleEvent.POST_CLOSE,  getSaveables(), false));
        fSavable.disconnectEditor();
        fSavable= null;
        mustSendLifeCycleEvent= true;
      }

      IEditorInput oldInput= getEditorInput();
      if (oldInput != null)
        getDocumentProvider().disconnect(oldInput);

      super.setInput(input);

      updateDocumentProvider(input);

      IDocumentProvider provider= getDocumentProvider();
      if (provider == null) {
        IStatus s= new Status(IStatus.ERROR, PlatformUI.PLUGIN_ID, IStatus.OK, EditorMessages.Editor_error_no_provider, null);
        throw new CoreException(s);
      }

      provider.connect(input);

      initializeTitle(input);

      if (fSourceViewer != null) {
        initializeSourceViewer(input);

        // Reset the undo context for the undo and redo action handlers
        IAction undoAction= getAction(ITextEditorActionConstants.UNDO);
        IAction redoAction= getAction(ITextEditorActionConstants.REDO);
        boolean areOperationActionHandlersInstalled= undoAction instanceof OperationHistoryActionHandler && redoAction instanceof OperationHistoryActionHandler;
        IUndoContext undoContext= getUndoContext();
        if (undoContext != null && areOperationActionHandlersInstalled) {
          ((OperationHistoryActionHandler)undoAction).setContext(undoContext);
          ((OperationHistoryActionHandler)redoAction).setContext(undoContext);
        } else {
          createUndoRedoActions();
        }
      }

      if (fIsOverwriting)
        toggleOverwriteMode();
      setInsertMode((InsertMode) getLegalInsertModes().get(0));
      updateCaret();

      updateStatusField(ITextEditorActionConstants.STATUS_CATEGORY_ELEMENT_STATE);

      if (fSelectionListener != null)
        fSelectionListener.setDocument(getDocumentProvider().getDocument(input));

      IVerticalRuler ruler= getVerticalRuler();
      if (ruler instanceof CompositeRuler)
        updateContributedRulerColumns((CompositeRuler) ruler);

      // Send savable life-cycle if needed.
      if (mustSendLifeCycleEvent && listener != null)
        listener.handleLifecycleEvent(new SaveablesLifecycleEvent(this,  SaveablesLifecycleEvent.POST_OPEN, getSaveables(), false));

    }

  }

  /**
   * Returns this editor's viewer's undo manager undo context.
   *
   * @return the undo context or <code>null</code> if not available
   * @since 3.1
   */
  private IUndoContext getUndoContext() {
    if (fSourceViewer instanceof ITextViewerExtension6) {
      IUndoManager undoManager= ((ITextViewerExtension6)fSourceViewer).getUndoManager();
      if (undoManager instanceof IUndoManagerExtension)
        return ((IUndoManagerExtension)undoManager).getUndoContext();
    }
    return null;
  }

  /*
   * @see org.eclipse.ui.part.EditorPart#setInputWithNotify(org.eclipse.ui.IEditorInput)
   * @since 3.2
   */
  protected final void setInputWithNotify(IEditorInput input) {
    try {

      doSetInput(input);

      /*
       * The following bugs explain why we fire this property change:
       *   https://bugs.eclipse.org/bugs/show_bug.cgi?id=90283
       *   https://bugs.eclipse.org/bugs/show_bug.cgi?id=92049
       *   https://bugs.eclipse.org/bugs/show_bug.cgi?id=92286
       */
      firePropertyChange(IEditorPart.PROP_INPUT);

    } catch (CoreException x) {
      String title= EditorMessages.Editor_error_setinput_title;
      String msg= EditorMessages.Editor_error_setinput_message;
      Shell shell= getSite().getShell();
      ErrorDialog.openError(shell, title, msg, x.getStatus());
    }
  }

  /*
   * @see EditorPart#setInput(org.eclipse.ui.IEditorInput)
   */
  public final void setInput(IEditorInput input) {
    setInputWithNotify(input);
  }

  /*
   * @see ITextEditor#close
   */
  public void close(final boolean save) {

    enableSanityChecking(false);

    Display display= getSite().getShell().getDisplay();
    display.asyncExec(new Runnable() {
      public void run() {
        if (fSourceViewer != null)
          getSite().getPage().closeEditor(AbstractTextEditor.this, save);
      }
    });
  }

  /**
   * The <code>AbstractTextEditor</code> implementation of this
   * <code>IWorkbenchPart</code> method may be extended by subclasses.
   * Subclasses must call <code>super.dispose()</code>.
   * <p>
   * Note that many methods may return <code>null</code> after the editor is
   * disposed.
   * </p>
   */
  public void dispose() {

    if (fActivationListener != null) {
      fActivationListener.dispose();
      fActivationListener= null;
    }

    if (fTitleImage != null) {
      fTitleImage.dispose();
      fTitleImage= null;
    }

    disposeFont();

    disposeNonDefaultCaret();
    fInitialCaret= null;

    if (fForegroundColor != null) {
      fForegroundColor.dispose();
      fForegroundColor= null;
    }

    if (fBackgroundColor != null) {
      fBackgroundColor.dispose();
      fBackgroundColor= null;
    }

    if (fSelectionForegroundColor != null) {
      fSelectionForegroundColor.dispose();
      fSelectionForegroundColor= null;
    }

    if (fSelectionBackgroundColor != null) {
      fSelectionBackgroundColor.dispose();
      fSelectionBackgroundColor= null;
    }

    if (fFindScopeHighlightColor != null) {
      fFindScopeHighlightColor.dispose();
      fFindScopeHighlightColor= null;
    }

    if (fFontPropertyChangeListener != null) {
      JFaceResources.getFontRegistry().removeListener(fFontPropertyChangeListener);
      if (fPreferenceStore != null)
        fPreferenceStore.removePropertyChangeListener(fFontPropertyChangeListener);
      fFontPropertyChangeListener= null;
    }

    if (fPropertyChangeListener != null) {
      if (fPreferenceStore != null) {
        fPreferenceStore.removePropertyChangeListener(fPropertyChangeListener);
        fPreferenceStore= null;
      }
      fPropertyChangeListener= null;
    }

    if (fActivationCodeTrigger != null) {
      fActivationCodeTrigger.uninstall();
      fActivationCodeTrigger= null;
    }

    if (fSelectionListener != null)  {
      fSelectionListener.uninstall(getSelectionProvider());
      fSelectionListener= null;
    }

    if (fSavable != null) {
      fSavable.disconnectEditor();
      fSavable= null;
    }

    disposeDocumentProvider();

    if (fSourceViewer != null) {

      if (fTextListener != null) {
        fSourceViewer.removeTextListener(fTextListener);
        fSourceViewer.removeTextInputListener(fTextListener);
        fTextListener= null;
      }

      uninstallTabsToSpacesConverter();

      fTextInputListener= null;
      fSelectionProvider= null;
      fSourceViewer= null;
    }

    if (fTextContextMenu != null) {
      fTextContextMenu.dispose();
      fTextContextMenu= null;
    }

    if (fRulerContextMenu != null) {
      fRulerContextMenu.dispose();
      fRulerContextMenu= null;
    }

    if (fActions != null) {
      registerUndoRedoAction(ITextEditorActionConstants.UNDO, null);
      registerUndoRedoAction(ITextEditorActionConstants.REDO, null);
      fActions.clear();
      fActions= null;
    }

    if (fSelectionActions != null) {
      fSelectionActions.clear();
      fSelectionActions= null;
    }

    if (fContentActions != null) {
      fContentActions.clear();
      fContentActions= null;
    }

    if (fPropertyActions != null) {
      fPropertyActions.clear();
      fPropertyActions= null;
    }

    if (fStateActions != null) {
      fStateActions.clear();
      fStateActions= null;
    }

    if (fActivationCodes != null) {
      fActivationCodes.clear();
      fActivationCodes= null;
    }

    if (fEditorStatusLine != null)
      fEditorStatusLine= null;

    if (fConfiguration != null)
      fConfiguration= null;

    if (fColumnSupport != null) {
      fColumnSupport.dispose();
      fColumnSupport= null;
    }

    if (fVerticalRuler != null)
      fVerticalRuler= null;

    IOperationHistory history= OperationHistoryFactory.getOperationHistory();
    if (history != null) {
      if (fNonLocalOperationApprover != null)
        history.removeOperationApprover(fNonLocalOperationApprover);
      if (fLinearUndoViolationApprover != null)
        history.removeOperationApprover(fLinearUndoViolationApprover);
    }
    fNonLocalOperationApprover= null;
    fLinearUndoViolationApprover= null;

    if (fKeyBindingSupportForContentAssistant != null) {
      fKeyBindingSupportForContentAssistant.dispose();
      fKeyBindingSupportForContentAssistant= null;
    }

    if (fKeyBindingSupportForQuickAssistant != null) {
      fKeyBindingSupportForQuickAssistant.dispose();
      fKeyBindingSupportForQuickAssistant= null;
    }
   
    if (fInformationPresenter != null) {
      fInformationPresenter.uninstall();
      fInformationPresenter= null;
    }

    if (fSaveAction != null) {
      fSaveAction.dispose();
      fSaveAction= null;
    }

    super.dispose();
  }

  /**
   * Disposes of the connection with the document provider. Subclasses
   * may extend.
   *
   * @since 3.0
   */
  protected void disposeDocumentProvider() {
    IDocumentProvider provider= getDocumentProvider();
    if (provider != null) {

      IEditorInput input= getEditorInput();
      if (input != null)
        provider.disconnect(input);

      if (fElementStateListener != null) {
        provider.removeElementStateListener(fElementStateListener);
        fElementStateListener= null;
      }

    }
    fExplicitDocumentProvider= null;
  }

  /**
   * Determines whether the given preference change affects the editor's
   * presentation. This implementation always returns <code>false</code>.
   * May be reimplemented by subclasses.
   *
   * @param event the event which should be investigated
   * @return <code>true</code> if the event describes a preference change affecting the editor's presentation
   * @since 2.0
   */
  protected boolean affectsTextPresentation(PropertyChangeEvent event) {
    return false;
  }

  /**
   * Returns the symbolic font name for this editor as defined in XML.
   *
   * @return a String with the symbolic font name or <code>null</code> if
   *         none is defined
   * @since 2.1
   */
  private String getSymbolicFontName() {
    if (getConfigurationElement() != null)
      return getConfigurationElement().getAttribute("symbolicFontName"); //$NON-NLS-1$
    return null;
  }

  /**
   * Returns the property preference key for the editor font.
   * <p>
   * If the editor is defined with a <code>symbolicFontName </code> then this name is returned and
   * the font is looked up in the JFace resource registry. Otherwise,
   * {@link JFaceResources#TEXT_FONT} is returned and the font is looked up in this editor's
   * preference store.
   * </p>
   *
   * @return a String with the key
   * @since 2.1
   */
  protected final String getFontPropertyPreferenceKey() {
    String symbolicFontName= getSymbolicFontName();
    if (symbolicFontName != null)
      return symbolicFontName;
    return JFaceResources.TEXT_FONT;
  }

  /**
   * Handles a property change event describing a change of the editor's
   * preference store and updates the preference related editor properties.
   * <p>
   * Subclasses may extend.
   * </p>
   *
   * @param event the property change event
   */
  protected void handlePreferenceStoreChanged(PropertyChangeEvent event) {

    if (fSourceViewer == null)
      return;

    String property= event.getProperty();

    if (getFontPropertyPreferenceKey().equals(property))
      // There is a separate handler for font preference changes
      return;

    if (PREFERENCE_COLOR_FOREGROUND.equals(property) || PREFERENCE_COLOR_FOREGROUND_SYSTEM_DEFAULT.equals(property) ||
        PREFERENCE_COLOR_BACKGROUND.equals(property) ||  PREFERENCE_COLOR_BACKGROUND_SYSTEM_DEFAULT.equals(property) ||
        PREFERENCE_COLOR_SELECTION_FOREGROUND.equals(property) || PREFERENCE_COLOR_SELECTION_FOREGROUND_SYSTEM_DEFAULT.equals(property) ||
        PREFERENCE_COLOR_SELECTION_BACKGROUND.equals(property) ||  PREFERENCE_COLOR_SELECTION_BACKGROUND_SYSTEM_DEFAULT.equals(property))
    {
      initializeViewerColors(fSourceViewer);
    } else if (PREFERENCE_COLOR_FIND_SCOPE.equals(property)) {
      initializeFindScopeColor(fSourceViewer);
    } else if (PREFERENCE_USE_CUSTOM_CARETS.equals(property)) {
      updateCaret();
    } else if (PREFERENCE_WIDE_CARET.equals(property)) {
      updateCaret();
    }

    if (affectsTextPresentation(event))
      fSourceViewer.invalidateTextPresentation();

    if (PREFERENCE_HYPERLINKS_ENABLED.equals(property)) {
      if (fSourceViewer instanceof ITextViewerExtension6) {
        IHyperlinkDetector[] detectors= getSourceViewerConfiguration().getHyperlinkDetectors(fSourceViewer);
        int stateMask= getSourceViewerConfiguration().getHyperlinkStateMask(fSourceViewer);
        ITextViewerExtension6 textViewer6= (ITextViewerExtension6)fSourceViewer;
        textViewer6.setHyperlinkDetectors(detectors, stateMask);
      }
      return;
    }

    if (PREFERENCE_HYPERLINK_KEY_MODIFIER.equals(property)) {
      if (fSourceViewer instanceof ITextViewerExtension6) {
        ITextViewerExtension6 textViewer6= (ITextViewerExtension6)fSourceViewer;
        IHyperlinkDetector[] detectors= getSourceViewerConfiguration().getHyperlinkDetectors(fSourceViewer);
        int stateMask= getSourceViewerConfiguration().getHyperlinkStateMask(fSourceViewer);
        textViewer6.setHyperlinkDetectors(detectors, stateMask);
      }
      return;
    }

    if (PREFERENCE_RULER_CONTRIBUTIONS.equals(property)) {
      String[] difference= StringSetSerializer.getDifference((String) event.getOldValue(), (String) event.getNewValue());
      IColumnSupport support= (IColumnSupport) getAdapter(IColumnSupport.class);
      for (int i= 0; i < difference.length; i++) {
        RulerColumnDescriptor desc= RulerColumnRegistry.getDefault().getColumnDescriptor(difference[i]);
        if (desc != null &&  support.isColumnSupported(desc)) {
          boolean newState= !support.isColumnVisible(desc);
          support.setColumnVisible(desc, newState);
        }
      }
      return;
    }

    if (PREFERENCE_SHOW_WHITESPACE_CHARACTERS.equals(property) ||
        PREFERENCE_SHOW_LEADING_SPACES.equals(property) ||
        PREFERENCE_SHOW_ENCLOSED_SPACES.equals(property) ||
        PREFERENCE_SHOW_TRAILING_SPACES.equals(property) ||
        PREFERENCE_SHOW_LEADING_IDEOGRAPHIC_SPACES.equals(property) ||
        PREFERENCE_SHOW_ENCLOSED_IDEOGRAPHIC_SPACES.equals(property) ||
        PREFERENCE_SHOW_TRAILING_IDEOGRAPHIC_SPACES.equals(property) ||
        PREFERENCE_SHOW_LEADING_TABS.equals(property) ||
        PREFERENCE_SHOW_ENCLOSED_TABS.equals(property) ||
        PREFERENCE_SHOW_TRAILING_TABS.equals(property) ||
        PREFERENCE_SHOW_CARRIAGE_RETURN.equals(property) ||
        PREFERENCE_SHOW_LINE_FEED.equals(property) ||
        PREFERENCE_WHITESPACE_CHARACTER_ALPHA_VALUE.equals(property)) {
      IAction action= getAction(ITextEditorActionConstants.SHOW_WHITESPACE_CHARACTERS);
      if (action instanceof IUpdate)
        ((IUpdate)action).update();
      return;
    }

    if (PREFERENCE_TEXT_DRAG_AND_DROP_ENABLED.equals(property)) {
      IPreferenceStore store= getPreferenceStore();
      if (store != null && store.getBoolean(PREFERENCE_TEXT_DRAG_AND_DROP_ENABLED))
        installTextDragAndDrop(getSourceViewer());
      else
        uninstallTextDragAndDrop(getSourceViewer());
      return;
    }

    if (PREFERENCE_HOVER_ENRICH_MODE.equals(property)) {
      if (fSourceViewer instanceof ITextViewerExtension8) {
        IPreferenceStore store= getPreferenceStore();
        if (store != null) {
          ((ITextViewerExtension8)fSourceViewer).setHoverEnrichMode(convertEnrichModePreference(store.getInt(PREFERENCE_HOVER_ENRICH_MODE)));
        }
      }
      return;
    }

  }

  /**
   * Returns the progress monitor related to this editor. It should not be
   * necessary to extend this method.
   *
   * @return the progress monitor related to this editor
   * @since 2.1
   */
  protected IProgressMonitor getProgressMonitor() {

    IProgressMonitor pm= null;

    IStatusLineManager manager= getStatusLineManager();
    if (manager != null)
      pm= manager.getProgressMonitor();

    return pm != null ? pm : new NullProgressMonitor();
  }

  /**
   * Handles an external change of the editor's input element. Subclasses may
   * extend.
   */
  protected void handleEditorInputChanged() {

    String title;
    String msg;
    Shell shell= getSite().getShell();

    final IDocumentProvider provider= getDocumentProvider();
    if (provider == null) {
      // fix for http://dev.eclipse.org/bugs/show_bug.cgi?id=15066
      close(false);
      return;
    }

    final IEditorInput input= getEditorInput();
    final String inputName= input.getToolTipText();

    if (provider.isDeleted(input)) {

      if (isSaveAsAllowed()) {

        title= EditorMessages.Editor_error_activated_deleted_save_title;
        msg= NLSUtility.format(EditorMessages.Editor_error_activated_deleted_save_message, inputName);

        String[] buttons= {
            EditorMessages.Editor_error_activated_deleted_save_button_save,
            EditorMessages.Editor_error_activated_deleted_save_button_close,
        };

        MessageDialog dialog= new MessageDialog(shell, title, null, msg, MessageDialog.QUESTION, buttons, 0);

        if (dialog.open() == 0) {
          IProgressMonitor pm= getProgressMonitor();
          performSaveAs(pm);
          if (pm.isCanceled())
            handleEditorInputChanged();
        } else {
          close(false);
        }

      } else {

        title= EditorMessages.Editor_error_activated_deleted_close_title;
        msg= NLSUtility.format(EditorMessages.Editor_error_activated_deleted_close_message, inputName);
        if (MessageDialog.openConfirm(shell, title, msg))
          close(false);
      }

    } else if (fHasBeenActivated) {

      title= EditorMessages.Editor_error_activated_outofsync_title;
      msg= NLSUtility.format(EditorMessages.Editor_error_activated_outofsync_message, inputName);

      if (MessageDialog.openQuestion(shell, title, msg)) {


        try {
          if (provider instanceof IDocumentProviderExtension) {
            IDocumentProviderExtension extension= (IDocumentProviderExtension) provider;
            extension.synchronize(input);
          } else {
            doSetInput(input);
          }
        } catch (CoreException x) {
          IStatus status= x.getStatus();
          if (status == null || status.getSeverity() != IStatus.CANCEL) {
            title= EditorMessages.Editor_error_refresh_outofsync_title;
            msg= NLSUtility.format(EditorMessages.Editor_error_refresh_outofsync_message, inputName);
            ErrorDialog.openError(shell, title, msg, x.getStatus());
          }
        }
      } else if (!isDirty()) {
        // Trigger dummy change to dirty the editor, for details see https://bugs.eclipse.org/344101 .
        try {
          IDocument document= provider.getDocument(input);
          if (document != null)
            document.replace(0, 0, ""); //$NON-NLS-1$
        } catch (BadLocationException e) {
          // Ignore as this can't happen
        }
      }
    }
  }

  /**
   * The <code>AbstractTextEditor</code> implementation of this
   * <code>IEditorPart</code> method calls <code>performSaveAs</code>.
   * Subclasses may reimplement.
   */
  public void doSaveAs() {
    /*
     * 1GEUSSR: ITPUI:ALL - User should never loose changes made in the editors.
     * Changed Behavior to make sure that if called inside a regular save (because
     * of deletion of input element) there is a way to report back to the caller.
     */
    performSaveAs(getProgressMonitor());
  }

  /**
   * Performs a save as and reports the result state back to the
   * given progress monitor. This default implementation does nothing.
   * Subclasses may reimplement.
   *
   * @param progressMonitor the progress monitor for communicating result state or <code>null</code>
   */
  protected void performSaveAs(IProgressMonitor progressMonitor) {
  }

  /**
   * The <code>AbstractTextEditor</code> implementation of this
   * <code>IEditorPart</code> method may be extended by subclasses.
   *
   * @param progressMonitor the progress monitor for communicating result state or <code>null</code>
   */
  public void doSave(IProgressMonitor progressMonitor) {

    IDocumentProvider p= getDocumentProvider();
    if (p == null)
      return;

    if (p.isDeleted(getEditorInput())) {

      if (isSaveAsAllowed()) {

        /*
         * 1GEUSSR: ITPUI:ALL - User should never loose changes made in the editors.
         * Changed Behavior to make sure that if called inside a regular save (because
         * of deletion of input element) there is a way to report back to the caller.
         */
        performSaveAs(progressMonitor);

      } else {

        Shell shell= getSite().getShell();
        String title= EditorMessages.Editor_error_save_deleted_title;
        String msg= EditorMessages.Editor_error_save_deleted_message;
        MessageDialog.openError(shell, title, msg);
      }

    } else {
      updateState(getEditorInput());
      validateState(getEditorInput());
      performSave(false, progressMonitor);
    }
  }

  /**
   * Enables/disables sanity checking.
   * @param enable <code>true</code> if sanity checking should be enabled, <code>false</code> otherwise
   * @since 2.0
   */
  protected void enableSanityChecking(boolean enable) {
    synchronized (this) {
      fIsSanityCheckEnabled= enable;
    }
  }

  /**
   * Checks the state of the given editor input if sanity checking is enabled.
   * @param input the editor input whose state is to be checked
   * @since 2.0
   */
  protected void safelySanityCheckState(IEditorInput input) {
    boolean enabled= false;

    synchronized (this) {
      enabled= fIsSanityCheckEnabled;
    }

    if (enabled)
      sanityCheckState(input);
  }

  /**
   * Checks the state of the given editor input.
   * @param input the editor input whose state is to be checked
   * @since 2.0
   */
  protected void sanityCheckState(IEditorInput input) {

    IDocumentProvider p= getDocumentProvider();
    if (p == null)
      return;

    if (p instanceof IDocumentProviderExtension3)  {

      IDocumentProviderExtension3 p3= (IDocumentProviderExtension3) p;

      long stamp= p.getModificationStamp(input);
      if (stamp != fModificationStamp) {
        fModificationStamp= stamp;
        if (!p3.isSynchronized(input))
          handleEditorInputChanged();
      }

    } else  {

      if (fModificationStamp == -1)
        fModificationStamp= p.getSynchronizationStamp(input);

      long stamp= p.getModificationStamp(input);
      if (stamp != fModificationStamp) {
        fModificationStamp= stamp;
        if (stamp != p.getSynchronizationStamp(input))
          handleEditorInputChanged();
      }
    }

    updateState(getEditorInput());
    updateStatusField(ITextEditorActionConstants.STATUS_CATEGORY_ELEMENT_STATE);
  }

  /**
   * Enables/disables state validation.
   * @param enable <code>true</code> if state validation should be enabled, <code>false</code> otherwise
   * @since 2.1
   */
  protected void enableStateValidation(boolean enable) {
    synchronized (this) {
      fIsStateValidationEnabled= enable;
    }
  }

  /**
   * Validates the state of the given editor input. The predominate intent
   * of this method is to take any action probably necessary to ensure that
   * the input can persistently be changed.
   *
   * @param input the input to be validated
   * @since 2.0
   */
  protected void validateState(IEditorInput input) {

    IDocumentProvider provider= getDocumentProvider();
    if (! (provider instanceof IDocumentProviderExtension))
      return;

    IDocumentProviderExtension extension= (IDocumentProviderExtension) provider;

    try {

      extension.validateState(input, getSite().getShell());

    } catch (CoreException x) {
      IStatus status= x.getStatus();
      if (status == null || status.getSeverity() != IStatus.CANCEL) {
        Bundle bundle= Platform.getBundle(PlatformUI.PLUGIN_ID);
        ILog log= Platform.getLog(bundle);
        log.log(x.getStatus());

        Shell shell= getSite().getShell();
        String title= EditorMessages.Editor_error_validateEdit_title;
        String msg= EditorMessages.Editor_error_validateEdit_message;
        ErrorDialog.openError(shell, title, msg, x.getStatus());
      }
      return;
    }

    if (fSourceViewer != null)
      fSourceViewer.setEditable(isEditable());

    updateStateDependentActions();
  }

  /*
   * @see org.eclipse.ui.texteditor.ITextEditorExtension2#validateEditorInputState()
   * @since 2.1
   */
  public boolean validateEditorInputState() {

    boolean enabled= false;

    synchronized (this) {
      enabled= fIsStateValidationEnabled;
    }

    if (enabled) {

      ISourceViewer viewer= fSourceViewer;
      if (viewer == null)
        return false;

      fTextInputListener.inputChanged= false;
      viewer.addTextInputListener(fTextInputListener);

      try {
        final IEditorInput input= getEditorInput();
        BusyIndicator.showWhile(getSite().getShell().getDisplay(), new Runnable() {
          /*
           * @see java.lang.Runnable#run()
           */
          public void run() {
            validateState(input);
          }
        });
        sanityCheckState(input);
        return !isEditorInputReadOnly() && !fTextInputListener.inputChanged;

      } finally {
        viewer.removeTextInputListener(fTextInputListener);
      }

    }

    return !isEditorInputReadOnly();
  }

  /**
   * Updates the state of the given editor input such as read-only flag.
   *
   * @param input the input to be validated
   * @since 2.0
   */
  protected void updateState(IEditorInput input) {
    IDocumentProvider provider= getDocumentProvider();
    if (provider instanceof IDocumentProviderExtension) {
      IDocumentProviderExtension extension= (IDocumentProviderExtension) provider;
      try {

        boolean wasReadOnly= isEditorInputReadOnly();
        extension.updateStateCache(input);

        if (fSourceViewer != null)
          fSourceViewer.setEditable(isEditable());

        if (wasReadOnly != isEditorInputReadOnly())
          updateStateDependentActions();

      } catch (CoreException x) {
        Bundle bundle= Platform.getBundle(PlatformUI.PLUGIN_ID);
        ILog log= Platform.getLog(bundle);
        log.log(x.getStatus());
      }
    }
  }

  /**
   * Performs the save and handles errors appropriately.
   *
   * @param overwrite indicates whether or not overwriting is allowed
   * @param progressMonitor the monitor in which to run the operation
   * @since 3.0
   */
  protected void performSave(boolean overwrite, IProgressMonitor progressMonitor) {

    IDocumentProvider provider= getDocumentProvider();
    if (provider == null)
      return;

    try {

      provider.aboutToChange(getEditorInput());
      IEditorInput input= getEditorInput();
      provider.saveDocument(progressMonitor, input, getDocumentProvider().getDocument(input), overwrite);
      editorSaved();

    } catch (CoreException x) {
      IStatus status= x.getStatus();
      if (status == null || status.getSeverity() != IStatus.CANCEL)
        handleExceptionOnSave(x, progressMonitor);
    } finally {
      provider.changed(getEditorInput());
    }
  }

  /**
   * Handles the given exception. If the exception reports an out-of-sync
   * situation, this is reported to the user. Otherwise, the exception
   * is generically reported.
   *
   * @param exception the exception to handle
   * @param progressMonitor the progress monitor
   */
  protected void handleExceptionOnSave(CoreException exception, IProgressMonitor progressMonitor) {

    try {
      ++ fErrorCorrectionOnSave;

      boolean isSynchronized= false;
      IDocumentProvider p= getDocumentProvider();

      if (p instanceof IDocumentProviderExtension3)  {
        IDocumentProviderExtension3 p3= (IDocumentProviderExtension3) p;
        isSynchronized= p3.isSynchronized(getEditorInput());
      } else  {
        long modifiedStamp= p.getModificationStamp(getEditorInput());
        long synchStamp= p.getSynchronizationStamp(getEditorInput());
        isSynchronized= (modifiedStamp == synchStamp);
      }

      if (isNotSynchronizedException(exception) && fErrorCorrectionOnSave == 1 && !isSynchronized) {
        String title= EditorMessages.Editor_error_save_outofsync_title;
        String msg= NLSUtility.format(EditorMessages.Editor_error_save_outofsync_message, getEditorInput().getToolTipText());

        if (MessageDialog.openQuestion(getSite().getShell(), title, msg))
          performSave(true, progressMonitor);
        else {
          /*
           * 1GEUPKR: ITPJUI:ALL - Loosing work with simultaneous edits
           * Set progress monitor to canceled in order to report back
           * to enclosing operations.
           */
          if (progressMonitor != null)
            progressMonitor.setCanceled(true);
        }
      } else {
        String title= EditorMessages.Editor_error_save_title;
        String msg= EditorMessages.Editor_error_save_message;
        openSaveErrorDialog(title, msg, exception);

        /*
         * 1GEUPKR: ITPJUI:ALL - Loosing work with simultaneous edits
         * Set progress monitor to canceled in order to report back
         * to enclosing operations.
         */
        if (progressMonitor != null)
          progressMonitor.setCanceled(true);
      }
    } finally {
      -- fErrorCorrectionOnSave;
    }
  }

  /**
   * Presents an error dialog to the user when a problem
   * happens during save.
   * <p>
   * Subclasses can decide to override the given title and message.
   * </p>
   *
   * @param title  the dialog title
   * @param message the message to display
   * @param exception the exception to handle
   * @since 3.3
   */
  protected void openSaveErrorDialog(String title, String message, CoreException  exception) {
    ErrorDialog.openError(getSite().getShell(), title, message, exception.getStatus());
  }

  /**
   * Tells whether the given core exception is exactly the
   * exception which is thrown for a non-synchronized element.
   *
   * @param ex the core exception
   * @return <code>true</code> iff the given core exception is exactly the
   *      exception which is thrown for a non-synchronized element
   * @since 3.1
   */
  private boolean isNotSynchronizedException(CoreException ex) {
    IDocumentProvider provider= getDocumentProvider();
    if (provider instanceof IDocumentProviderExtension5)
      return ((IDocumentProviderExtension5)provider).isNotSynchronizedException(getEditorInput(), ex);
    return false;
  }

  /**
   * The <code>AbstractTextEditor</code> implementation of this
   * <code>IEditorPart</code> method returns <code>false</code>.
   * Subclasses may override.
   *
   * @return <code>false</code>
   */
  public boolean isSaveAsAllowed() {
    return false;
  }

  /*
   * @see EditorPart#isDirty()
   */
  public boolean isDirty() {
    IDocumentProvider p= getDocumentProvider();
    return p == null ? false : p.canSaveDocument(getEditorInput());
  }

  /**
   * The <code>AbstractTextEditor</code> implementation of this
   * <code>ITextEditor</code> method may be extended by subclasses.
   */
  public void doRevertToSaved() {
    IDocumentProvider p= getDocumentProvider();
    if (p == null)
      return;

    performRevert();
  }

  /**
   * Performs revert and handles errors appropriately.
   * <p>
   * Subclasses may extend.
   * </p>
   *
   * @since 3.0
   */
  protected void performRevert() {

    IDocumentProvider provider= getDocumentProvider();
    if (provider == null)
      return;

    try {

      provider.aboutToChange(getEditorInput());
      provider.resetDocument(getEditorInput());
      editorSaved();

    } catch (CoreException x) {
      IStatus status= x.getStatus();
      if (status == null || status.getSeverity() != IStatus.CANCEL ) {
        Shell shell= getSite().getShell();
        String title= EditorMessages.Editor_error_revert_title;
        String msg= EditorMessages.Editor_error_revert_message;
        ErrorDialog.openError(shell, title, msg, x.getStatus());
      }
    } finally {
      provider.changed(getEditorInput());
    }
  }

  /**
   * Performs any additional action necessary to perform after the input
   * document's content has been replaced.
   * <p>
   * Clients may extended this method.
   *
   * @since 3.0
   */
  protected void handleElementContentReplaced() {
  }

  /*
   * @see ITextEditor#setAction(String, IAction)
   */
  public void setAction(String actionID, IAction action) {
    Assert.isNotNull(actionID);
    if (action == null) {
      action= (IAction) fActions.remove(actionID);
      if (action != null)
        fActivationCodeTrigger.unregisterActionFromKeyActivation(action);
    } else {
      if (action.getId() == null)
        action.setId(actionID); // make sure the action ID has been set
      fActions.put(actionID, action);
      fActivationCodeTrigger.registerActionForKeyActivation(action);
    }
  }

  /**
   * Sets this editor's actions into activated (default) or deactived state.
   * <p>
   * XXX: This is called by the Java editor for its breadcrumb feature. We
   * don't want to make this risky method API because the Java editor
   * breadcrumb might become a Platform UI feature during 3.5 and hence we can
   * then delete this workaround.
   * </p>
   *
   * @param state <code>true</code> if activated
   * @since 3.4
   */
  private void setActionActivation(boolean state) {
    if (state) {
      fActivationCodeTrigger.install();
      Iterator iter= fActions.values().iterator();
      while (iter.hasNext()) {
        IAction action= (IAction)iter.next();
        if (action != null)
          fActivationCodeTrigger.registerActionForKeyActivation(action);
      }
      getEditorSite().getActionBarContributor().setActiveEditor(this);
    } else {
      getEditorSite().getActionBarContributor().setActiveEditor(null);
      Iterator iter= fActions.values().iterator();
      while (iter.hasNext()) {
        IAction action= (IAction)iter.next();
        if (action != null)
          fActivationCodeTrigger.unregisterActionFromKeyActivation(action);
      }
      fActivationCodeTrigger.uninstall();
    }
  }

  private static final boolean HACK_TO_SUPPRESS_UNUSUED_WARNING= false;
  {
    if (HACK_TO_SUPPRESS_UNUSUED_WARNING)
      setActionActivation(true);
  }

  /*
   * @see ITextEditor#setActionActivationCode(String, char, int, int)
   */
  public void setActionActivationCode(String actionID, char activationCharacter, int activationKeyCode, int activationStateMask) {

    Assert.isNotNull(actionID);

    ActionActivationCode found= findActionActivationCode(actionID);
    if (found == null) {
      found= new ActionActivationCode(actionID);
      fActivationCodes.add(found);
    }

    found.fCharacter= activationCharacter;
    found.fKeyCode= activationKeyCode;
    found.fStateMask= activationStateMask;
  }

  /**
   * Returns the activation code registered for the specified action.
   *
   * @param actionID the action id
   * @return the registered activation code or <code>null</code> if no code has been installed
   */
  private ActionActivationCode findActionActivationCode(String actionID) {
    int size= fActivationCodes.size();
    for (int i= 0; i < size; i++) {
      ActionActivationCode code= (ActionActivationCode) fActivationCodes.get(i);
      if (actionID.equals(code.fActionId))
        return code;
    }
    return null;
  }

  /*
   * @see ITextEditor#removeActionActivationCode(String)
   */
  public void removeActionActivationCode(String actionID) {
    Assert.isNotNull(actionID);
    ActionActivationCode code= findActionActivationCode(actionID);
    if (code != null)
      fActivationCodes.remove(code);
  }

  /*
   * @see ITextEditor#getAction(String)
   */
  public IAction getAction(String actionID) {
    Assert.isNotNull(actionID);
    IAction action= (IAction) fActions.get(actionID);

    if (action == null) {
      action= findContributedAction(actionID);
      if (action != null)
        setAction(actionID, action);
    }

    return action;
  }

  /**
   * Returns the action with the given action id that has been contributed via XML to this editor.
   * The lookup honors the dependencies of plug-ins.
   *
   * @param actionID the action id to look up
   * @return the action that has been contributed
   * @since 2.0
   */
  private IAction findContributedAction(String actionID) {
    List actions= new ArrayList();
    IConfigurationElement[] elements= Platform.getExtensionRegistry().getConfigurationElementsFor(PlatformUI.PLUGIN_ID, "editorActions"); //$NON-NLS-1$
    for (int i= 0; i < elements.length; i++) {
      IConfigurationElement element= elements[i];
      if (TAG_CONTRIBUTION_TYPE.equals(element.getName())) {
        if (!getSite().getId().equals(element.getAttribute("targetID"))) //$NON-NLS-1$
          continue;

        IConfigurationElement[] children= element.getChildren("action"); //$NON-NLS-1$
        for (int j= 0; j < children.length; j++) {
          IConfigurationElement child= children[j];
          if (actionID.equals(child.getAttribute("actionID"))) //$NON-NLS-1$
            actions.add(child);
        }
      }
    }
    int actionSize= actions.size();
    if (actionSize > 0) {
      IConfigurationElement element;
      if (actionSize > 1) {
        IConfigurationElement[] actionArray= (IConfigurationElement[])actions.toArray(new IConfigurationElement[actionSize]);
        ConfigurationElementSorter sorter= new ConfigurationElementSorter() {
          /*
           * @see org.eclipse.ui.texteditor.ConfigurationElementSorter#getConfigurationElement(java.lang.Object)
           */
          public IConfigurationElement getConfigurationElement(Object object) {
            return (IConfigurationElement)object;
          }
        };
        sorter.sort(actionArray);
        element= actionArray[0];
      } else
        element= (IConfigurationElement)actions.get(0);

      try {
        return new ContributedAction(getSite(), element);
      } catch (CommandNotMappedException e) {
        // out of luck, no command action mapping
      }
    }

    return null;
  }

  /**
   * Updates the specified action by calling <code>IUpdate.update</code>
   * if applicable.
   *
   * @param actionId the action id
   */
  private void updateAction(String actionId) {
    Assert.isNotNull(actionId);
    if (fActions != null) {
      IAction action= (IAction) fActions.get(actionId);
      if (action instanceof IUpdate)
        ((IUpdate) action).update();
    }
  }

  /**
   * Marks or unmarks the given action to be updated on text selection changes.
   *
   * @param actionId the action id
   * @param mark <code>true</code> if the action is selection dependent
   */
  public void markAsSelectionDependentAction(String actionId, boolean mark) {
    Assert.isNotNull(actionId);
    if (mark) {
      if (!fSelectionActions.contains(actionId))
        fSelectionActions.add(actionId);
    } else
      fSelectionActions.remove(actionId);
  }

  /**
   * Marks or unmarks the given action to be updated on content changes.
   *
   * @param actionId the action id
   * @param mark <code>true</code> if the action is content dependent
   */
  public void markAsContentDependentAction(String actionId, boolean mark) {
    Assert.isNotNull(actionId);
    if (mark) {
      if (!fContentActions.contains(actionId))
        fContentActions.add(actionId);
    } else
      fContentActions.remove(actionId);
  }

  /**
   * Marks or unmarks the given action to be updated on property changes.
   *
   * @param actionId the action id
   * @param mark <code>true</code> if the action is property dependent
   * @since 2.0
   */
  public void markAsPropertyDependentAction(String actionId, boolean mark) {
    Assert.isNotNull(actionId);
    if (mark) {
      if (!fPropertyActions.contains(actionId))
        fPropertyActions.add(actionId);
    } else
      fPropertyActions.remove(actionId);
  }

  /**
   * Marks or unmarks the given action to be updated on state changes.
   *
   * @param actionId the action id
   * @param mark <code>true</code> if the action is state dependent
   * @since 2.0
   */
  public void markAsStateDependentAction(String actionId, boolean mark) {
    Assert.isNotNull(actionId);
    if (mark) {
      if (!fStateActions.contains(actionId))
        fStateActions.add(actionId);
    } else
      fStateActions.remove(actionId);
  }

  /**
   * Updates all selection dependent actions.
   */
  protected void updateSelectionDependentActions() {
    if (fSelectionActions != null) {
      Iterator e= fSelectionActions.iterator();
      while (e.hasNext())
        updateAction((String) e.next());
    }
  }

  /**
   * Updates all content dependent actions.
   */
  protected void updateContentDependentActions() {
    if (fContentActions != null) {
      Iterator e= fContentActions.iterator();
      while (e.hasNext())
        updateAction((String) e.next());
    }
  }

  /**
   * Updates all property dependent actions.
   * @since 2.0
   */
  protected void updatePropertyDependentActions() {
    if (fPropertyActions != null) {
      Iterator e= fPropertyActions.iterator();
      while (e.hasNext())
        updateAction((String) e.next());
    }
  }

  /**
   * Updates all state dependent actions.
   * @since 2.0
   */
  protected void updateStateDependentActions() {
    if (fStateActions != null) {
      Iterator e= fStateActions.iterator();
      while (e.hasNext())
        updateAction((String) e.next());
    }
  }

  /**
   * Creates action entries for all SWT StyledText actions as defined in
   * <code>org.eclipse.swt.custom.ST</code>. Overwrites and
   * extends the list of these actions afterwards.
   * <p>
   * Subclasses may extend.
   * </p>
   * @since 2.0
   */
  protected void createNavigationActions() {

    IAction action;

    StyledText textWidget= fSourceViewer.getTextWidget();
    for (int i= 0; i < ACTION_MAP.length; i++) {
      IdMapEntry entry= ACTION_MAP[i];
      action= new TextNavigationAction(textWidget, entry.getAction());
      action.setActionDefinitionId(entry.getActionId());
      setAction(entry.getActionId(), action);
    }

    action= new ToggleOverwriteModeAction(EditorMessages.getBundleForConstructedKeys(), "Editor.ToggleOverwriteMode."); //$NON-NLS-1$
    action.setActionDefinitionId(ITextEditorActionDefinitionIds.TOGGLE_OVERWRITE);
    setAction(ITextEditorActionDefinitionIds.TOGGLE_OVERWRITE, action);
    textWidget.setKeyBinding(SWT.INSERT, SWT.NULL);

    action=  new ScrollLinesAction(-1);
    action.setActionDefinitionId(ITextEditorActionDefinitionIds.SCROLL_LINE_UP);
    setAction(ITextEditorActionDefinitionIds.SCROLL_LINE_UP, action);

    action= new ScrollLinesAction(1);
    action.setActionDefinitionId(ITextEditorActionDefinitionIds.SCROLL_LINE_DOWN);
    setAction(ITextEditorActionDefinitionIds.SCROLL_LINE_DOWN, action);

    action= new LineEndAction(textWidget, false);
    action.setActionDefinitionId(ITextEditorActionDefinitionIds.LINE_END);
    setAction(ITextEditorActionDefinitionIds.LINE_END, action);

    action= new LineStartAction(textWidget, false);
    action.setActionDefinitionId(ITextEditorActionDefinitionIds.LINE_START);
    setAction(ITextEditorActionDefinitionIds.LINE_START, action);

    action= new LineEndAction(textWidget, true);
    action.setActionDefinitionId(ITextEditorActionDefinitionIds.SELECT_LINE_END);
    setAction(ITextEditorActionDefinitionIds.SELECT_LINE_END, action);

    action= new LineStartAction(textWidget, true);
    action.setActionDefinitionId(ITextEditorActionDefinitionIds.SELECT_LINE_START);
    setAction(ITextEditorActionDefinitionIds.SELECT_LINE_START, action);

    // to accommodate https://bugs.eclipse.org/bugs/show_bug.cgi?id=51516
    // nullify handling of DELETE key by StyledText
    textWidget.setKeyBinding(SWT.DEL, SWT.NULL);
  }

  /**
   * Creates this editor's accessibility actions.
   * @since 2.0
   */
  private void createAccessibilityActions() {
    IAction action= new ShowRulerContextMenuAction();
    action.setActionDefinitionId(ITextEditorActionDefinitionIds.SHOW_RULER_CONTEXT_MENU);
    setAction(ITextEditorActionDefinitionIds.SHOW_RULER_CONTEXT_MENU, action);
  }

  /**
   * Creates this editor's undo/redo actions.
   * <p>
   * Subclasses may override or extend.</p>
   *
   * @since 3.1
   */
  protected void createUndoRedoActions() {
    IUndoContext undoContext= getUndoContext();
    if (undoContext != null) {
      // Use actions provided by global undo/redo

      // Create the undo action
      OperationHistoryActionHandler undoAction= new UndoActionHandler(getEditorSite(), undoContext);
      PlatformUI.getWorkbench().getHelpSystem().setHelp(undoAction, IAbstractTextEditorHelpContextIds.UNDO_ACTION);
      undoAction.setActionDefinitionId(IWorkbenchCommandConstants.EDIT_UNDO);
      registerUndoRedoAction(ITextEditorActionConstants.UNDO, undoAction);

      // Create the redo action.
      OperationHistoryActionHandler redoAction= new RedoActionHandler(getEditorSite(), undoContext);
      PlatformUI.getWorkbench().getHelpSystem().setHelp(redoAction, IAbstractTextEditorHelpContextIds.REDO_ACTION);
      redoAction.setActionDefinitionId(IWorkbenchCommandConstants.EDIT_REDO);
      registerUndoRedoAction(ITextEditorActionConstants.REDO, redoAction);

      // Install operation approvers
      IOperationHistory history= OperationHistoryFactory.getOperationHistory();

      // The first approver will prompt when operations affecting outside elements are to be undone or redone.
      if (fNonLocalOperationApprover != null)
        history.removeOperationApprover(fNonLocalOperationApprover);
      fNonLocalOperationApprover= getUndoRedoOperationApprover(undoContext);
      history.addOperationApprover(fNonLocalOperationApprover);

      // The second approver will prompt from this editor when an undo is attempted on an operation
      // and it is not the most recent operation in the editor.
      if (fLinearUndoViolationApprover != null)
        history.removeOperationApprover(fLinearUndoViolationApprover);
      fLinearUndoViolationApprover= new LinearUndoViolationUserApprover(undoContext, this);
      history.addOperationApprover(fLinearUndoViolationApprover);

    } else {
      // Use text operation actions (pre 3.1 style)

      ResourceAction action;

      if (getAction(ITextEditorActionConstants.UNDO) == null) {
        action= new TextOperationAction(EditorMessages.getBundleForConstructedKeys(), "Editor.Undo.", this, ITextOperationTarget.UNDO); //$NON-NLS-1$
        action.setHelpContextId(IAbstractTextEditorHelpContextIds.UNDO_ACTION);
        action.setActionDefinitionId(IWorkbenchCommandConstants.EDIT_UNDO);
        setAction(ITextEditorActionConstants.UNDO, action);
      }

      if (getAction(ITextEditorActionConstants.REDO) == null) {
        action= new TextOperationAction(EditorMessages.getBundleForConstructedKeys(), "Editor.Redo.", this, ITextOperationTarget.REDO); //$NON-NLS-1$
        action.setHelpContextId(IAbstractTextEditorHelpContextIds.REDO_ACTION);
        action.setActionDefinitionId(IWorkbenchCommandConstants.EDIT_REDO);
        setAction(ITextEditorActionConstants.REDO, action);
      }
    }
  }

  /**
   * Registers the given undo/redo action under the given ID and ensures that previously installed
   * actions get disposed. It also takes care of re-registering the new action with the global
   * action handler.
   *
   * @param actionId the action id under which to register the action
   * @param action the action to register or <code>null</code> to dispose them
   * @since 3.1
   */
  private void registerUndoRedoAction(String actionId, OperationHistoryActionHandler action) {
    IAction oldAction= getAction(actionId);
    if (oldAction instanceof OperationHistoryActionHandler)
      ((OperationHistoryActionHandler)oldAction).dispose();

    if (action == null)
      return;

    setAction(actionId, action);

    IActionBars actionBars= getEditorSite().getActionBars();
    if (actionBars != null)
      actionBars.setGlobalActionHandler(actionId, action);
  }

  /**
   * Return an {@link IOperationApprover} appropriate for approving the undo and
   * redo of operations that have the specified undo context.
   * <p>
   * Subclasses may override.
   * </p>
   * @param undoContext  the IUndoContext of operations that should be examined
   *             by the operation approver
   * @return  the <code>IOperationApprover</code> appropriate for approving undo
   *       and redo operations inside this editor, or <code>null</code> if no
   *       approval is needed
   * @since 3.1
   */
  protected IOperationApprover getUndoRedoOperationApprover(IUndoContext undoContext) {
    return new NonLocalUndoUserApprover(undoContext, this, new Object [] { getEditorInput() }, Object.class);
  }

  /**
   * Creates this editor's standard actions and connects them with the global
   * workbench actions.
   * <p>
   * Subclasses may extend.</p>
   */
  protected void createActions() {

    ResourceAction action;

    setAction(IWorkbenchCommandConstants.EDIT_CUT, null);
    action= new TextOperationAction(EditorMessages.getBundleForConstructedKeys(), "Editor.Cut.", this, ITextOperationTarget.CUT); //$NON-NLS-1$
    action.setHelpContextId(IAbstractTextEditorHelpContextIds.CUT_ACTION);
    action.setActionDefinitionId(IWorkbenchCommandConstants.EDIT_CUT);
    setAction(ITextEditorActionConstants.CUT, action);

    setAction(IWorkbenchCommandConstants.EDIT_COPY, null);
    action= new TextOperationAction(EditorMessages.getBundleForConstructedKeys(), "Editor.Copy.", this, ITextOperationTarget.COPY, true); //$NON-NLS-1$
    action.setHelpContextId(IAbstractTextEditorHelpContextIds.COPY_ACTION);
    action.setActionDefinitionId(IWorkbenchCommandConstants.EDIT_COPY);
    setAction(ITextEditorActionConstants.COPY, action);

    setAction(IWorkbenchCommandConstants.EDIT_PASTE, null);
    action= new TextOperationAction(EditorMessages.getBundleForConstructedKeys(), "Editor.Paste.", this, ITextOperationTarget.PASTE); //$NON-NLS-1$
    action.setHelpContextId(IAbstractTextEditorHelpContextIds.PASTE_ACTION);
    action.setActionDefinitionId(IWorkbenchCommandConstants.EDIT_PASTE);
    setAction(ITextEditorActionConstants.PASTE, action);

    action= new TextOperationAction(EditorMessages.getBundleForConstructedKeys(), "Editor.Delete.", this, ITextOperationTarget.DELETE); //$NON-NLS-1$
    action.setHelpContextId(IAbstractTextEditorHelpContextIds.DELETE_ACTION);
    action.setActionDefinitionId(IWorkbenchCommandConstants.EDIT_DELETE);
    setAction(ITextEditorActionConstants.DELETE, action);

    action= new DeleteLineAction(EditorMessages.getBundleForConstructedKeys(), "Editor.DeleteLine.", this, DeleteLineAction.WHOLE, false); //$NON-NLS-1$
    action.setHelpContextId(IAbstractTextEditorHelpContextIds.DELETE_LINE_ACTION);
    action.setActionDefinitionId(ITextEditorActionDefinitionIds.DELETE_LINE);
    setAction(ITextEditorActionConstants.DELETE_LINE, action);

    action= new DeleteLineAction(EditorMessages.getBundleForConstructedKeys(), "Editor.CutLine.", this, DeleteLineAction.WHOLE, true); //$NON-NLS-1$
    action.setHelpContextId(IAbstractTextEditorHelpContextIds.CUT_LINE_ACTION);
    action.setActionDefinitionId(ITextEditorActionDefinitionIds.CUT_LINE);
    setAction(ITextEditorActionConstants.CUT_LINE, action);

    action= new DeleteLineAction(EditorMessages.getBundleForConstructedKeys(), "Editor.DeleteLineToBeginning.", this, DeleteLineAction.TO_BEGINNING, false); //$NON-NLS-1$
    action.setHelpContextId(IAbstractTextEditorHelpContextIds.DELETE_LINE_TO_BEGINNING_ACTION);
    action.setActionDefinitionId(ITextEditorActionDefinitionIds.DELETE_LINE_TO_BEGINNING);
    setAction(ITextEditorActionConstants.DELETE_LINE_TO_BEGINNING, action);

    action= new DeleteLineAction(EditorMessages.getBundleForConstructedKeys(), "Editor.CutLineToBeginning.", this, DeleteLineAction.TO_BEGINNING, true); //$NON-NLS-1$
    action.setHelpContextId(IAbstractTextEditorHelpContextIds.CUT_LINE_TO_BEGINNING_ACTION);
    action.setActionDefinitionId(ITextEditorActionDefinitionIds.CUT_LINE_TO_BEGINNING);
    setAction(ITextEditorActionConstants.CUT_LINE_TO_BEGINNING, action);

    action= new DeleteLineAction(EditorMessages.getBundleForConstructedKeys(), "Editor.DeleteLineToEnd.", this, DeleteLineAction.TO_END, false); //$NON-NLS-1$
    action.setHelpContextId(IAbstractTextEditorHelpContextIds.DELETE_LINE_TO_END_ACTION);
    action.setActionDefinitionId(ITextEditorActionDefinitionIds.DELETE_LINE_TO_END);
    setAction(ITextEditorActionConstants.DELETE_LINE_TO_END, action);

    action= new DeleteLineAction(EditorMessages.getBundleForConstructedKeys(), "Editor.CutLineToEnd.", this, DeleteLineAction.TO_END, true); //$NON-NLS-1$
    action.setHelpContextId(IAbstractTextEditorHelpContextIds.CUT_LINE_TO_END_ACTION);
    action.setActionDefinitionId(ITextEditorActionDefinitionIds.CUT_LINE_TO_END);
    setAction(ITextEditorActionConstants.CUT_LINE_TO_END, action);

    action= new JoinLinesAction(EditorMessages.getBundleForConstructedKeys(), "Editor.JoinLines.", this, " "); //$NON-NLS-1$ //$NON-NLS-2$
    action.setHelpContextId(IAbstractTextEditorHelpContextIds.JOIN_LINES_ACTION);
    action.setActionDefinitionId(ITextEditorActionDefinitionIds.JOIN_LINES);
    setAction(ITextEditorActionConstants.JOIN_LINES, action);

    action= new MarkAction(EditorMessages.getBundleForConstructedKeys(), "Editor.SetMark.", this, MarkAction.SET_MARK); //$NON-NLS-1$
    action.setHelpContextId(IAbstractTextEditorHelpContextIds.SET_MARK_ACTION);
    action.setActionDefinitionId(ITextEditorActionDefinitionIds.SET_MARK);
    setAction(ITextEditorActionConstants.SET_MARK, action);

    action= new MarkAction(EditorMessages.getBundleForConstructedKeys(), "Editor.ClearMark.", this, MarkAction.CLEAR_MARK); //$NON-NLS-1$
    action.setHelpContextId(IAbstractTextEditorHelpContextIds.CLEAR_MARK_ACTION);
    action.setActionDefinitionId(ITextEditorActionDefinitionIds.CLEAR_MARK);
    setAction(ITextEditorActionConstants.CLEAR_MARK, action);

    action= new MarkAction(EditorMessages.getBundleForConstructedKeys(), "Editor.SwapMark.", this, MarkAction.SWAP_MARK); //$NON-NLS-1$
    action.setHelpContextId(IAbstractTextEditorHelpContextIds.SWAP_MARK_ACTION);
    action.setActionDefinitionId(ITextEditorActionDefinitionIds.SWAP_MARK);
    setAction(ITextEditorActionConstants.SWAP_MARK, action);

    action= new TextOperationAction(EditorMessages.getBundleForConstructedKeys(), "Editor.SelectAll.", this, ITextOperationTarget.SELECT_ALL, true); //$NON-NLS-1$
    action.setHelpContextId(IAbstractTextEditorHelpContextIds.SELECT_ALL_ACTION);
    action.setActionDefinitionId(IWorkbenchCommandConstants.EDIT_SELECT_ALL);
    setAction(ITextEditorActionConstants.SELECT_ALL, action);

    action= new ShiftAction(EditorMessages.getBundleForConstructedKeys(), "Editor.ShiftRight.", this, ITextOperationTarget.SHIFT_RIGHT); //$NON-NLS-1$
    action.setHelpContextId(IAbstractTextEditorHelpContextIds.SHIFT_RIGHT_ACTION);
    action.setActionDefinitionId(ITextEditorActionDefinitionIds.SHIFT_RIGHT);
    setAction(ITextEditorActionConstants.SHIFT_RIGHT, action);

    action= new ShiftAction(EditorMessages.getBundleForConstructedKeys(), "Editor.ShiftRight.", this, ITextOperationTarget.SHIFT_RIGHT) { //$NON-NLS-1$
      public void update() {
        updateForTab();
      }
    };
    setAction(ITextEditorActionConstants.SHIFT_RIGHT_TAB, action);

    action= new ShiftAction(EditorMessages.getBundleForConstructedKeys(), "Editor.ShiftLeft.", this, ITextOperationTarget.SHIFT_LEFT); //$NON-NLS-1$
    action.setHelpContextId(IAbstractTextEditorHelpContextIds.SHIFT_LEFT_ACTION);
    action.setActionDefinitionId(ITextEditorActionDefinitionIds.SHIFT_LEFT);
    setAction(ITextEditorActionConstants.SHIFT_LEFT, action);

    action= new TextOperationAction(EditorMessages.getBundleForConstructedKeys(), "Editor.Print.", this, ITextOperationTarget.PRINT, true); //$NON-NLS-1$
    action.setHelpContextId(IAbstractTextEditorHelpContextIds.PRINT_ACTION);
    action.setActionDefinitionId(IWorkbenchCommandConstants.FILE_PRINT);
    setAction(ITextEditorActionConstants.PRINT, action);

    action= new FindReplaceAction(EditorMessages.getBundleForConstructedKeys(), "Editor.FindReplace.", this); //$NON-NLS-1$
    action.setHelpContextId(IAbstractTextEditorHelpContextIds.FIND_ACTION);
    action.setActionDefinitionId(IWorkbenchCommandConstants.EDIT_FIND_AND_REPLACE);
    setAction(ITextEditorActionConstants.FIND, action);

    action= new FindNextAction(EditorMessages.getBundleForConstructedKeys(), "Editor.FindNext.", this, true); //$NON-NLS-1$
    action.setHelpContextId(IAbstractTextEditorHelpContextIds.FIND_NEXT_ACTION);
    action.setActionDefinitionId(IWorkbenchActionDefinitionIds.FIND_NEXT);
    setAction(ITextEditorActionConstants.FIND_NEXT, action);

    action= new FindNextAction(EditorMessages.getBundleForConstructedKeys(), "Editor.FindPrevious.", this, false); //$NON-NLS-1$
    action.setHelpContextId(IAbstractTextEditorHelpContextIds.FIND_PREVIOUS_ACTION);
    action.setActionDefinitionId(IWorkbenchActionDefinitionIds.FIND_PREVIOUS);
    setAction(ITextEditorActionConstants.FIND_PREVIOUS, action);

    action= new IncrementalFindAction(EditorMessages.getBundleForConstructedKeys(), "Editor.FindIncremental.", this, true); //$NON-NLS-1$
    action.setHelpContextId(IAbstractTextEditorHelpContextIds.FIND_INCREMENTAL_ACTION);
    action.setActionDefinitionId(IWorkbenchActionDefinitionIds.FIND_INCREMENTAL);
    setAction(ITextEditorActionConstants.FIND_INCREMENTAL, action);

    action= new IncrementalFindAction(EditorMessages.getBundleForConstructedKeys(), "Editor.FindIncrementalReverse.", this, false); //$NON-NLS-1$
    action.setHelpContextId(IAbstractTextEditorHelpContextIds.FIND_INCREMENTAL_REVERSE_ACTION);
    action.setActionDefinitionId(IWorkbenchActionDefinitionIds.FIND_INCREMENTAL_REVERSE);
    setAction(ITextEditorActionConstants.FIND_INCREMENTAL_REVERSE, action);

    fSaveAction= ActionFactory.SAVE.create(getSite().getWorkbenchWindow());
    setAction(ITextEditorActionConstants.SAVE, fSaveAction);

    action= new RevertToSavedAction(EditorMessages.getBundleForConstructedKeys(), "Editor.Revert.", this); //$NON-NLS-1$
    action.setHelpContextId(IAbstractTextEditorHelpContextIds.REVERT_TO_SAVED_ACTION);
    action.setActionDefinitionId(IWorkbenchCommandConstants.FILE_REVERT);
    setAction(ITextEditorActionConstants.REVERT_TO_SAVED, action);

    action= new GotoLineAction(EditorMessages.getBundleForConstructedKeys(), "Editor.GotoLine.", this); //$NON-NLS-1$
    action.setHelpContextId(IAbstractTextEditorHelpContextIds.GOTO_LINE_ACTION);
    action.setActionDefinitionId(ITextEditorActionDefinitionIds.LINE_GOTO);
    setAction(ITextEditorActionConstants.GOTO_LINE, action);

    action= new MoveLinesAction(EditorMessages.getBundleForConstructedKeys(), "Editor.MoveLinesUp.", this, getSourceViewer(), true, false); //$NON-NLS-1$
    action.setHelpContextId(IAbstractTextEditorHelpContextIds.MOVE_LINES_ACTION);
    action.setActionDefinitionId(ITextEditorActionDefinitionIds.MOVE_LINES_UP);
    setAction(ITextEditorActionConstants.MOVE_LINE_UP, action);

    action= new MoveLinesAction(EditorMessages.getBundleForConstructedKeys(), "Editor.MoveLinesDown.", this, getSourceViewer(), false, false); //$NON-NLS-1$
    action.setHelpContextId(IAbstractTextEditorHelpContextIds.MOVE_LINES_ACTION);
    action.setActionDefinitionId(ITextEditorActionDefinitionIds.MOVE_LINES_DOWN);
    setAction(ITextEditorActionConstants.MOVE_LINE_DOWN, action);

    action= new MoveLinesAction(EditorMessages.getBundleForConstructedKeys(), "Editor.CopyLineUp.", this, getSourceViewer(), true, true); //$NON-NLS-1$
    action.setHelpContextId(IAbstractTextEditorHelpContextIds.COPY_LINES_ACTION);
    action.setActionDefinitionId(ITextEditorActionDefinitionIds.COPY_LINES_UP);
    setAction(ITextEditorActionConstants.COPY_LINE_UP, action);

    action= new MoveLinesAction(EditorMessages.getBundleForConstructedKeys(), "Editor.CopyLineDown.", this, getSourceViewer(), false, true); //$NON-NLS-1$
    action.setHelpContextId(IAbstractTextEditorHelpContextIds.COPY_LINES_ACTION);
    action.setActionDefinitionId(ITextEditorActionDefinitionIds.COPY_LINES_DOWN);
    setAction(ITextEditorActionConstants.COPY_LINE_DOWN, action);

    action= new CaseAction(EditorMessages.getBundleForConstructedKeys(), "Editor.UpperCase.", this, true); //$NON-NLS-1$
    action.setHelpContextId(IAbstractTextEditorHelpContextIds.UPPER_CASE_ACTION);
    action.setActionDefinitionId(ITextEditorActionDefinitionIds.UPPER_CASE);
    setAction(ITextEditorActionConstants.UPPER_CASE, action);

    action= new CaseAction(EditorMessages.getBundleForConstructedKeys(), "Editor.LowerCase.", this, false); //$NON-NLS-1$
    action.setHelpContextId(IAbstractTextEditorHelpContextIds.LOWER_CASE_ACTION);
    action.setActionDefinitionId(ITextEditorActionDefinitionIds.LOWER_CASE);
    setAction(ITextEditorActionConstants.LOWER_CASE, action);

    action= new InsertLineAction(EditorMessages.getBundleForConstructedKeys(), "Editor.SmartEnter.", this, false); //$NON-NLS-1$
    action.setHelpContextId(IAbstractTextEditorHelpContextIds.SMART_ENTER_ACTION);
    action.setActionDefinitionId(ITextEditorActionDefinitionIds.SMART_ENTER);
    setAction(ITextEditorActionConstants.SMART_ENTER, action);

    action= new InsertLineAction(EditorMessages.getBundleForConstructedKeys(), "Editor.SmartEnterInverse.", this, true); //$NON-NLS-1$
    action.setHelpContextId(IAbstractTextEditorHelpContextIds.SMART_ENTER_ACTION);
    action.setActionDefinitionId(ITextEditorActionDefinitionIds.SMART_ENTER_INVERSE);
    setAction(ITextEditorActionConstants.SMART_ENTER_INVERSE, action);

    action= new ToggleInsertModeAction(EditorMessages.getBundleForConstructedKeys(), "Editor.ToggleInsertMode."); //$NON-NLS-1$
    action.setHelpContextId(IAbstractTextEditorHelpContextIds.TOGGLE_INSERT_MODE_ACTION);
    action.setActionDefinitionId(ITextEditorActionDefinitionIds.TOGGLE_INSERT_MODE);
    setAction(ITextEditorActionConstants.TOGGLE_INSERT_MODE, action);

    action= new HippieCompleteAction(EditorMessages.getBundleForConstructedKeys(), "Editor.HippieCompletion.", this); //$NON-NLS-1$
    action.setHelpContextId(IAbstractTextEditorHelpContextIds.HIPPIE_COMPLETION_ACTION);
    action.setActionDefinitionId(ITextEditorActionDefinitionIds.HIPPIE_COMPLETION);
    setAction(ITextEditorActionConstants.HIPPIE_COMPLETION, action);

    action= new ContentAssistAction(EditorMessages.getBundleForConstructedKeys(), "Editor.ContentAssistProposal.", this); //$NON-NLS-1$
    action.setHelpContextId(IAbstractTextEditorHelpContextIds.CONTENT_ASSIST_ACTION);
    action.setActionDefinitionId(ITextEditorActionDefinitionIds.CONTENT_ASSIST_PROPOSALS);
    setAction(ITextEditorActionConstants.CONTENT_ASSIST, action);
    markAsStateDependentAction(ITextEditorActionConstants.CONTENT_ASSIST, true);
   
    action= new TextOperationAction(EditorMessages.getBundleForConstructedKeys(), "Editor.ContentAssistContextInformation.", this, ISourceViewer.CONTENTASSIST_CONTEXT_INFORMATION); //$NON-NLS-1$
    action.setHelpContextId(IAbstractTextEditorHelpContextIds.CONTENT_ASSIST_CONTEXT_INFORMATION_ACTION);
    action.setActionDefinitionId(ITextEditorActionDefinitionIds.CONTENT_ASSIST_CONTEXT_INFORMATION);
    setAction(ITextEditorActionConstants.CONTENT_ASSIST_CONTEXT_INFORMATION, action);
    markAsStateDependentAction(ITextEditorActionConstants.CONTENT_ASSIST_CONTEXT_INFORMATION, true);

    action= new TextOperationAction(EditorMessages.getBundleForConstructedKeys(), "Editor.QuickAssist.", this, ISourceViewer.QUICK_ASSIST); //$NON-NLS-1$
    action.setHelpContextId(IAbstractTextEditorHelpContextIds.QUICK_ASSIST_ACTION);
    action.setActionDefinitionId(ITextEditorActionDefinitionIds.QUICK_ASSIST);
    setAction(ITextEditorActionConstants.QUICK_ASSIST, action);
    markAsStateDependentAction(ITextEditorActionConstants.QUICK_ASSIST, true);

    action= new GotoAnnotationAction(this, true);
    setAction(ITextEditorActionConstants.NEXT, action);
    action= new GotoAnnotationAction(this, false);
    setAction(ITextEditorActionConstants.PREVIOUS, action);

    action= new RecenterAction(EditorMessages.getBundleForConstructedKeys(), "Editor.Recenter.", this); //$NON-NLS-1$
    action.setHelpContextId(IAbstractTextEditorHelpContextIds.RECENTER_ACTION);
    action.setActionDefinitionId(ITextEditorActionDefinitionIds.RECENTER);
    setAction(ITextEditorActionConstants.RECENTER, action);

    action= new ShowWhitespaceCharactersAction(EditorMessages.getBundleForConstructedKeys(), "Editor.ShowWhitespaceCharacters.", this, getPreferenceStore()); //$NON-NLS-1$
    action.setHelpContextId(IAbstractTextEditorHelpContextIds.SHOW_WHITESPACE_CHARACTERS_ACTION);
    action.setActionDefinitionId(ITextEditorActionDefinitionIds.SHOW_WHITESPACE_CHARACTERS);
    setAction(ITextEditorActionConstants.SHOW_WHITESPACE_CHARACTERS, action);

    action= new TextOperationAction(EditorMessages.getBundleForConstructedKeys(), "Editor.ShowInformation.", this, ISourceViewer.INFORMATION, true); //$NON-NLS-1$
    action= new InformationDispatchAction(EditorMessages.getBundleForConstructedKeys(), "Editor.ShowInformation.", (TextOperationAction) action); //$NON-NLS-1$
    action.setHelpContextId(IAbstractTextEditorHelpContextIds.SHOW_INFORMATION_ACTION);
    action.setActionDefinitionId(ITextEditorActionDefinitionIds.SHOW_INFORMATION);
    setAction(ITextEditorActionConstants.SHOW_INFORMATION, action);

    action= new BlockSelectionModeToggleAction(EditorMessages.getBundleForConstructedKeys(), "Editor.ToggleBlockSelectionMode.", this); //$NON-NLS-1$
    action.setHelpContextId(IAbstractTextEditorHelpContextIds.BLOCK_SELECTION_MODE_ACTION);
    action.setActionDefinitionId(ITextEditorActionDefinitionIds.BLOCK_SELECTION_MODE);
    setAction(ITextEditorActionConstants.BLOCK_SELECTION_MODE, action);

    action= new TextOperationAction(EditorMessages.getBundleForConstructedKeys(), "Editor.OpenHyperlink.", this, HyperlinkManager.OPEN_HYPERLINK, true); //$NON-NLS-1$;
    action.setHelpContextId(IAbstractTextEditorHelpContextIds.OPEN_HYPERLINK_ACTION);
    action.setActionDefinitionId(ITextEditorActionDefinitionIds.OPEN_HYPERLINK);
    setAction(ITextEditorActionConstants.OPEN_HYPERLINK, action);

    PropertyDialogAction openProperties= new PropertyDialogAction(
        new IShellProvider() {
          public Shell getShell() {
            return getSite().getShell();
          }
        },
        new ISelectionProvider() {
          public void addSelectionChangedListener(ISelectionChangedListener listener) {
          }
          public ISelection getSelection() {
            return new StructuredSelection(getEditorInput());
          }
          public void removeSelectionChangedListener(ISelectionChangedListener listener) {
          }
          public void setSelection(ISelection selection) {
          }
        });
    openProperties.setActionDefinitionId(IWorkbenchCommandConstants.FILE_PROPERTIES);
    setAction(ITextEditorActionConstants.PROPERTIES, openProperties);

    markAsContentDependentAction(ITextEditorActionConstants.UNDO, true);
    markAsContentDependentAction(ITextEditorActionConstants.REDO, true);
    markAsContentDependentAction(ITextEditorActionConstants.FIND, true);
    markAsContentDependentAction(ITextEditorActionConstants.FIND_NEXT, true);
    markAsContentDependentAction(ITextEditorActionConstants.FIND_PREVIOUS, true);
    markAsContentDependentAction(ITextEditorActionConstants.FIND_INCREMENTAL, true);
    markAsContentDependentAction(ITextEditorActionConstants.FIND_INCREMENTAL_REVERSE, true);

    markAsSelectionDependentAction(ITextEditorActionConstants.CUT, true);
    markAsSelectionDependentAction(ITextEditorActionConstants.COPY, true);
    markAsSelectionDependentAction(ITextEditorActionConstants.PASTE, true);
    markAsSelectionDependentAction(ITextEditorActionConstants.DELETE, true);
    markAsSelectionDependentAction(ITextEditorActionConstants.SHIFT_RIGHT, true);
    markAsSelectionDependentAction(ITextEditorActionConstants.SHIFT_RIGHT_TAB, true);
    markAsSelectionDependentAction(ITextEditorActionConstants.UPPER_CASE, true);
    markAsSelectionDependentAction(ITextEditorActionConstants.LOWER_CASE, true);

    markAsPropertyDependentAction(ITextEditorActionConstants.UNDO, true);
    markAsPropertyDependentAction(ITextEditorActionConstants.REDO, true);
    markAsPropertyDependentAction(ITextEditorActionConstants.REVERT_TO_SAVED, true);
    markAsPropertyDependentAction(ITextEditorActionConstants.SAVE, true);

    markAsStateDependentAction(ITextEditorActionConstants.UNDO, true);
    markAsStateDependentAction(ITextEditorActionConstants.REDO, true);
    markAsStateDependentAction(ITextEditorActionConstants.CUT, true);
    markAsStateDependentAction(ITextEditorActionConstants.PASTE, true);
    markAsStateDependentAction(ITextEditorActionConstants.DELETE, true);
    markAsStateDependentAction(ITextEditorActionConstants.SHIFT_RIGHT, true);
    markAsStateDependentAction(ITextEditorActionConstants.SHIFT_RIGHT_TAB, true);
    markAsStateDependentAction(ITextEditorActionConstants.SHIFT_LEFT, true);
    markAsStateDependentAction(ITextEditorActionConstants.FIND, true);
    markAsStateDependentAction(ITextEditorActionConstants.DELETE_LINE, true);
    markAsStateDependentAction(ITextEditorActionConstants.DELETE_LINE_TO_BEGINNING, true);
    markAsStateDependentAction(ITextEditorActionConstants.DELETE_LINE_TO_END, true);
    markAsStateDependentAction(ITextEditorActionConstants.MOVE_LINE_UP, true);
    markAsStateDependentAction(ITextEditorActionConstants.MOVE_LINE_DOWN, true);
    markAsStateDependentAction(ITextEditorActionConstants.CUT_LINE, true);
    markAsStateDependentAction(ITextEditorActionConstants.CUT_LINE_TO_BEGINNING, true);
    markAsStateDependentAction(ITextEditorActionConstants.CUT_LINE_TO_END, true);

    setActionActivationCode(ITextEditorActionConstants.SHIFT_RIGHT_TAB,'\t', -1, SWT.NONE);
    setActionActivationCode(ITextEditorActionConstants.SHIFT_LEFT, '\t', -1, SWT.SHIFT);
  }

  /**
   * Convenience method to add the action installed under the given action id to the given menu.
   * @param menu the menu to add the action to
   * @param actionId the id of the action to be added
   */
  protected final void addAction(IMenuManager menu, String actionId) {
    IAction action= getAction(actionId);
    if (action != null) {
      if (action instanceof IUpdate)
        ((IUpdate) action).update();
      menu.add(action);
    }
  }

  /**
   * Convenience method to add the action installed under the given action id to the specified group of the menu.
   * @param menu the menu to add the action to
   * @param group the group in the menu
   * @param actionId the id of the action to add
   */
  protected final void addAction(IMenuManager menu, String group, String actionId) {
     IAction action= getAction(actionId);
     if (action != null) {
       if (action instanceof IUpdate)
         ((IUpdate) action).update();

       IMenuManager subMenu= menu.findMenuUsingPath(group);
       if (subMenu != null)
         subMenu.add(action);
       else
         menu.appendToGroup(group, action);
     }
  }

  /**
   * Convenience method to add a new group after the specified group.
   * @param menu the menu to add the new group to
   * @param existingGroup the group after which to insert the new group
   * @param newGroup the new group
   */
  protected final void addGroup(IMenuManager menu, String existingGroup, String newGroup) {
     IMenuManager subMenu= menu.findMenuUsingPath(existingGroup);
     if (subMenu != null)
       subMenu.add(new Separator(newGroup));
     else
       menu.appendToGroup(existingGroup, new Separator(newGroup));
  }

  /**
   * Sets up the ruler context menu before it is made visible.
   * <p>
   * Subclasses may extend to add other actions.</p>
   *
   * @param menu the menu
   */
  protected void rulerContextMenuAboutToShow(IMenuManager menu) {

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

    for (Iterator i= fRulerContextMenuListeners.iterator(); i.hasNext();)
      ((IMenuListener) i.next()).menuAboutToShow(menu);

    addAction(menu, ITextEditorActionConstants.RULER_MANAGE_BOOKMARKS);
    addAction(menu, ITextEditorActionConstants.RULER_MANAGE_TASKS);
  }

  /**
   * Sets up this editor's context menu before it is made visible.
   * <p>
   * Subclasses may extend to add other actions.</p>
   *
   * @param menu the menu
   */
  protected void editorContextMenuAboutToShow(IMenuManager menu) {

    menu.add(new Separator(ITextEditorActionConstants.GROUP_UNDO));
    menu.add(new GroupMarker(ITextEditorActionConstants.GROUP_SAVE));
    menu.add(new Separator(ITextEditorActionConstants.GROUP_COPY));
    menu.add(new Separator(ITextEditorActionConstants.GROUP_PRINT));
    menu.add(new Separator(ITextEditorActionConstants.GROUP_EDIT));
    menu.add(new Separator(ITextEditorActionConstants.GROUP_FIND));
    menu.add(new Separator(IWorkbenchActionConstants.GROUP_ADD));
    menu.add(new Separator(ITextEditorActionConstants.GROUP_REST));
    menu.add(new Separator(IWorkbenchActionConstants.MB_ADDITIONS));

    if (isEditable()) {
      addAction(menu, ITextEditorActionConstants.GROUP_UNDO, ITextEditorActionConstants.UNDO);
      addAction(menu, ITextEditorActionConstants.GROUP_UNDO, ITextEditorActionConstants.REVERT_TO_SAVED);
      addAction(menu, ITextEditorActionConstants.GROUP_SAVE, ITextEditorActionConstants.SAVE);
      addAction(menu, ITextEditorActionConstants.GROUP_COPY, ITextEditorActionConstants.CUT);
      addAction(menu, ITextEditorActionConstants.GROUP_COPY, ITextEditorActionConstants.COPY);
      addAction(menu, ITextEditorActionConstants.GROUP_COPY, ITextEditorActionConstants.PASTE);
      IAction action= getAction(ITextEditorActionConstants.QUICK_ASSIST);
      if (action != null && action.isEnabled())
        addAction(menu, ITextEditorActionConstants.GROUP_EDIT, ITextEditorActionConstants.QUICK_ASSIST);
    } else {
      addAction(menu, ITextEditorActionConstants.GROUP_COPY, ITextEditorActionConstants.COPY);
    }
  }

  /**
   * Returns the status line manager of this editor.
   *
   * @return the status line manager of this editor
   * @since 2.0, protected since 3.3
   */
  protected IStatusLineManager getStatusLineManager() {
    return getEditorSite().getActionBars().getStatusLineManager();
  }

  /*
   * @see IAdaptable#getAdapter(java.lang.Class)
   */
  public Object getAdapter(Class required) {

    if (IEditorStatusLine.class.equals(required)) {
      if (fEditorStatusLine == null) {
        IStatusLineManager statusLineManager= getStatusLineManager();
        ISelectionProvider selectionProvider= getSelectionProvider();
        if (statusLineManager != null && selectionProvider != null)
          fEditorStatusLine= new EditorStatusLine(statusLineManager, selectionProvider);
      }
      return fEditorStatusLine;
    }

    if (IVerticalRulerInfo.class.equals(required)) {
      if (fVerticalRuler != null)
        return fVerticalRuler;
    }

    if (IMarkRegionTarget.class.equals(required)) {
      if (fMarkRegionTarget == null) {
        IStatusLineManager manager= getStatusLineManager();
        if (manager != null)
          fMarkRegionTarget= (fSourceViewer == null ? null : new MarkRegionTarget(fSourceViewer, manager));
      }
      return fMarkRegionTarget;
    }

    if (IDeleteLineTarget.class.equals(required)) {
      if (fDeleteLineTarget == null) {
        fDeleteLineTarget= new TextViewerDeleteLineTarget(fSourceViewer);
      }
      return fDeleteLineTarget;
    }

    if (IncrementalFindTarget.class.equals(required)) {
      if (fIncrementalFindTarget == null) {
        IStatusLineManager manager= getStatusLineManager();
        if (manager != null)
          fIncrementalFindTarget= (fSourceViewer == null ? null : new IncrementalFindTarget(fSourceViewer, manager));
      }
      return fIncrementalFindTarget;
    }

    if (IFindReplaceTarget.class.equals(required)) {
      if (fFindReplaceTarget == null) {
        IFindReplaceTarget target= (fSourceViewer == null ? null : fSourceViewer.getFindReplaceTarget());
        if (target != null) {
          fFindReplaceTarget= new FindReplaceTarget(this, target);
          if (fFindScopeHighlightColor != null)
            fFindReplaceTarget.setScopeHighlightColor(fFindScopeHighlightColor);
        }
      }
      return fFindReplaceTarget;
    }

    if (ITextOperationTarget.class.equals(required))
      return (fSourceViewer == null ? null : fSourceViewer.getTextOperationTarget());

    if (IRewriteTarget.class.equals(required)) {
      if (fSourceViewer instanceof ITextViewerExtension) {
        ITextViewerExtension extension= (ITextViewerExtension) fSourceViewer;
        return extension.getRewriteTarget();
      }
      return null;
    }

    if (Control.class.equals(required))
      return fSourceViewer != null ? fSourceViewer.getTextWidget() : null;

    if (IColumnSupport.class.equals(required)) {
      if (fColumnSupport == null)
        fColumnSupport= createColumnSupport();
      return fColumnSupport;
    }

    return super.getAdapter(required);
  }

  /*
   * @see IWorkbenchPart#setFocus()
   */
  public void setFocus() {
    if (fSourceViewer != null && fSourceViewer.getTextWidget() != null)
      fSourceViewer.getTextWidget().setFocus();
  }

  /*
   * @see ITextEditor#showsHighlightRangeOnly()
   */
  public boolean showsHighlightRangeOnly() {
    return fShowHighlightRangeOnly;
  }

  /*
   * @see ITextEditor#showHighlightRangeOnly(boolean)
   */
  public void showHighlightRangeOnly(boolean showHighlightRangeOnly) {
    fShowHighlightRangeOnly= showHighlightRangeOnly;
  }

  /*
   * @see ITextEditor#setHighlightRange(int, int, boolean)
   */
  public void setHighlightRange(int offset, int length, boolean moveCursor) {
    if (fSourceViewer == null)
      return;

    if (fShowHighlightRangeOnly) {
      if (moveCursor)
        fSourceViewer.setVisibleRegion(offset, length);
    } else {
      IRegion rangeIndication= fSourceViewer.getRangeIndication();
      if (rangeIndication == null || offset != rangeIndication.getOffset() || length != rangeIndication.getLength())
        fSourceViewer.setRangeIndication(offset, length, moveCursor);
    }
  }

  /*
   * @see ITextEditor#getHighlightRange()
   */
  public IRegion getHighlightRange() {
    if (fSourceViewer == null)
      return null;

    if (fShowHighlightRangeOnly)
      return getCoverage(fSourceViewer);

    return fSourceViewer.getRangeIndication();
  }

  /*
   * @see ITextEditor#resetHighlightRange
   */
  public void resetHighlightRange() {
    if (fSourceViewer == null)
      return;

    if (fShowHighlightRangeOnly)
      fSourceViewer.resetVisibleRegion();
    else
      fSourceViewer.removeRangeIndication();
  }

  /**
   * Adjusts the highlight range so that at least the specified range
   * is highlighted.
   * <p>
   * Subclasses may re-implement this method.</p>
   *
   * @param offset the offset of the range which at least should be highlighted
   * @param length the length of the range which at least should be highlighted
   */
  protected void adjustHighlightRange(int offset, int length) {
    if (fSourceViewer == null)
      return;

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

  /*
   * @see ITextEditor#selectAndReveal(int, int)
   */
  public void selectAndReveal(int start, int length) {
    selectAndReveal(start, length, start, length);
  }

  /**
   * Selects and reveals the specified ranges in this text editor.
   *
   * @param selectionStart the offset of the selection
   * @param selectionLength the length of the selection
   * @param revealStart the offset of the revealed range
   * @param revealLength the length of the revealed range
   * @since 3.0
   */
  protected void selectAndReveal(int selectionStart, int selectionLength, int revealStart, int revealLength) {
    if (fSourceViewer == null)
      return;

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

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

      fSourceViewer.setSelectedRange(selectionStart, selectionLength);

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

  /*
   * @see org.eclipse.ui.INavigationLocationProvider#createNavigationLocation()
   * @since 2.1
   */
  public INavigationLocation createEmptyNavigationLocation() {
    return new TextSelectionNavigationLocation(this, false);
  }

  /*
   * @see org.eclipse.ui.INavigationLocationProvider#createNavigationLocation()
   */
  public INavigationLocation createNavigationLocation() {
    return new TextSelectionNavigationLocation(this, true);
  }

  /**
   * Writes a check mark of the given situation into the navigation history.
   * @since 2.1
   */
  protected void markInNavigationHistory() {
    getSite().getPage().getNavigationHistory().markLocation(this);
  }

  /**
   * Hook which gets called when the editor has been saved.
   * Subclasses may extend.
   * @since 2.1
   */
  protected void editorSaved() {
    INavigationLocation[] locations= getSite().getPage().getNavigationHistory().getLocations();
    IEditorInput input= getEditorInput();
    for (int i= 0; i < locations.length; i++) {
      if (locations[i] instanceof TextSelectionNavigationLocation) {
        if(input.equals(locations[i].getInput())) {
          TextSelectionNavigationLocation location= (TextSelectionNavigationLocation) locations[i];
          location.partSaved(this);
        }
      }
    }
  }

  /*
   * @see WorkbenchPart#firePropertyChange(int)
   */
  protected void firePropertyChange(int property) {
    super.firePropertyChange(property);
    updatePropertyDependentActions();
  }

  /*
   * @see ITextEditorExtension#setStatusField(IStatusField, String)
   * @since 2.0
   */
  public void setStatusField(IStatusField field, String category) {
    Assert.isNotNull(category);
    if (field != null) {

      if (fStatusFields == null)
        fStatusFields= new HashMap(3);

      fStatusFields.put(category, field);
      updateStatusField(category);

    } else if (fStatusFields != null)
      fStatusFields.remove(category);

    if (fIncrementalFindTarget != null && ITextEditorActionConstants.STATUS_CATEGORY_FIND_FIELD.equals(category))
      fIncrementalFindTarget.setStatusField(field);
  }

  /**
   * Returns the current status field for the given status category.
   *
   * @param category the status category
   * @return the current status field for the given status category
   * @since 2.0
   */
  protected IStatusField getStatusField(String category) {
    if (category != null && fStatusFields != null)
      return (IStatusField) fStatusFields.get(category);
    return null;
  }

  /**
   * Returns whether this editor is in overwrite or insert mode.
   *
   * @return <code>true</code> if in insert mode, <code>false</code> for overwrite mode
   * @since 2.0
   */
  protected boolean isInInsertMode() {
    return !fIsOverwriting;
  }

  /*
   * @see org.eclipse.ui.texteditor.ITextEditorExtension3#getInsertMode()
   * @since 3.0
   */
  public InsertMode getInsertMode() {
    return fInsertMode;
  }

  /*
   * @see org.eclipse.ui.texteditor.ITextEditorExtension3#setInsertMode(org.eclipse.ui.texteditor.ITextEditorExtension3.InsertMode)
   * @since 3.0
   */
  public void setInsertMode(InsertMode newMode) {
    List legalModes= getLegalInsertModes();
    if (!legalModes.contains(newMode))
      throw new IllegalArgumentException();

    fInsertMode= newMode;

    handleInsertModeChanged();
  }

  /**
   * Returns the set of legal insert modes. If insert modes are configured all defined insert modes
   * are legal.
   *
   * @return the set of legal insert modes
   * @since 3.0
   */
  protected List getLegalInsertModes() {
    if (fLegalInsertModes == null) {
      fLegalInsertModes= new ArrayList();
      fLegalInsertModes.add(SMART_INSERT);
      fLegalInsertModes.add(INSERT);
    }
    return fLegalInsertModes;
  }

  private void switchToNextInsertMode() {

    InsertMode mode= getInsertMode();
    List legalModes= getLegalInsertModes();

    int i= 0;
    while (i < legalModes.size()) {
      if (legalModes.get(i) == mode) break;
      ++ i;
    }

    i= (i + 1) % legalModes.size();
    InsertMode newMode= (InsertMode) legalModes.get(i);
    setInsertMode(newMode);
  }

  private void toggleOverwriteMode() {
    if (fIsOverwriteModeEnabled) {
      fIsOverwriting= !fIsOverwriting;
      fSourceViewer.getTextWidget().invokeAction(ST.TOGGLE_OVERWRITE);
      handleInsertModeChanged();
    }
  }

  /**
   * Configures the given insert mode as legal or illegal. This call is ignored if the set of legal
   * input modes would be empty after the call.
   *
   * @param mode the insert mode to be configured
   * @param legal <code>true</code> if the given mode is legal, <code>false</code> otherwise
   * @since 3.0
   */
  protected void configureInsertMode(InsertMode mode, boolean legal) {
    List legalModes= getLegalInsertModes();
    if (legal) {
      if (!legalModes.contains(mode))
        legalModes.add(mode);
    } else if (legalModes.size() > 1) {
      if (getInsertMode() == mode)
        switchToNextInsertMode();
      legalModes.remove(mode);
    }
  }

  /**
   * Sets the overwrite mode enablement.
   *
   * @param enable <code>true</code> to enable new overwrite mode,
   *        <code>false</code> to disable
   * @since 3.0
   */
  protected void enableOverwriteMode(boolean enable) {
    if (fIsOverwriting && !enable)
      toggleOverwriteMode();
    fIsOverwriteModeEnabled= enable;
  }

  private Caret createOverwriteCaret(StyledText styledText) {
    Caret caret= new Caret(styledText, SWT.NULL);
    GC gc= new GC(styledText);
    // XXX: this overwrite box is not proportional-font aware
    // take 'a' as a medium sized character
    Point charSize= gc.stringExtent("a"); //$NON-NLS-1$

    // XXX: Filed request to get a caret with auto-height: https://bugs.eclipse.org/bugs/show_bug.cgi?id=118612
    caret.setSize(charSize.x, styledText.getLineHeight());
    caret.setFont(styledText.getFont());

    gc.dispose();

    return caret;
  }

  private Caret createInsertCaret(StyledText styledText) {
    Caret caret= new Caret(styledText, SWT.NULL);

    // XXX: Filed request to get a caret with auto-height: https://bugs.eclipse.org/bugs/show_bug.cgi?id=118612
    caret.setSize(getCaretWidthPreference(), styledText.getLineHeight());
    caret.setFont(styledText.getFont());

    return caret;
  }

  private Image createRawInsertModeCaretImage(StyledText styledText) {

    PaletteData caretPalette= new PaletteData(new RGB[] {new RGB (0,0,0), new RGB (255,255,255)});
    int width= getCaretWidthPreference();
    int widthOffset= width - 1;

    // XXX: Filed request to get a caret with auto-height: https://bugs.eclipse.org/bugs/show_bug.cgi?id=118612
    ImageData imageData= new ImageData(4 + widthOffset, styledText.getLineHeight(), 1, caretPalette);

    Display display= styledText.getDisplay();
    Image bracketImage= new Image(display, imageData);
    GC gc= new GC (bracketImage);
    gc.setForeground(display.getSystemColor(SWT.COLOR_WHITE));
    gc.setLineWidth(0); // NOTE: 0 means width is 1 but with optimized performance
    int height= imageData.height / 3;
    // gap between two bars of one third of the height
    // draw boxes using lines as drawing a line of a certain width produces
    // rounded corners.
    for (int i= 0; i < width ; i++) {
      gc.drawLine(i, 0, i, height - 1);
      gc.drawLine(i, imageData.height - height, i, imageData.height - 1);
    }

    gc.dispose();

    return bracketImage;
  }

  private Caret createRawInsertModeCaret(StyledText styledText) {
    // don't draw special raw caret if no smart mode is enabled
    if (!getLegalInsertModes().contains(SMART_INSERT))
      return createInsertCaret(styledText);

    Caret caret= new Caret(styledText, SWT.NULL);
    Image image= createRawInsertModeCaretImage(styledText);
    if (image != null)
      caret.setImage(image);
    else {
      // XXX: Filed request to get a caret with auto-height: https://bugs.eclipse.org/bugs/show_bug.cgi?id=118612
      caret.setSize(getCaretWidthPreference(), styledText.getLineHeight());
    }

    caret.setFont(styledText.getFont());

    return caret;
  }

  private int getCaretWidthPreference() {
    if (getPreferenceStore() != null && getPreferenceStore().getBoolean(PREFERENCE_WIDE_CARET))
      return WIDE_CARET_WIDTH;

    return SINGLE_CARET_WIDTH;
  }

  private void updateCaret() {

    if (fSourceViewer == null)
      return;

    StyledText styledText= fSourceViewer.getTextWidget();

    InsertMode mode= getInsertMode();

    styledText.setCaret(null);
    disposeNonDefaultCaret();

    if (getPreferenceStore() == null || !getPreferenceStore().getBoolean(PREFERENCE_USE_CUSTOM_CARETS))
      Assert.isTrue(fNonDefaultCaret == null);
    else if (fIsOverwriting)
      fNonDefaultCaret= createOverwriteCaret(styledText);
    else if (SMART_INSERT == mode)
      fNonDefaultCaret= createInsertCaret(styledText);
    else if (INSERT == mode)
      fNonDefaultCaret= createRawInsertModeCaret(styledText);

    if (fNonDefaultCaret != null) {
      styledText.setCaret(fNonDefaultCaret);
      fNonDefaultCaretImage= fNonDefaultCaret.getImage();
    } else if (fInitialCaret != styledText.getCaret())
        styledText.setCaret(fInitialCaret);
  }

  private void disposeNonDefaultCaret() {
    if (fNonDefaultCaretImage != null) {
      fNonDefaultCaretImage.dispose();
      fNonDefaultCaretImage= null;
    }

    if (fNonDefaultCaret != null) {
      fNonDefaultCaret.dispose();
      fNonDefaultCaret= null;
    }
  }

  /**
   * Handles a change of the editor's insert mode.
   * Subclasses may extend.
   *
   * @since 2.0
   */
  protected void handleInsertModeChanged() {
    updateInsertModeAction();
    updateCaret();
    updateStatusField(ITextEditorActionConstants.STATUS_CATEGORY_INPUT_MODE);
  }

  private void updateInsertModeAction() {

    // this may be called before the part is fully initialized (see configureInsertMode)
    // drop out in this case.
    if (getSite() == null)
      return;

    IAction action= getAction(ITextEditorActionConstants.TOGGLE_INSERT_MODE);
    if (action != null) {
      action.setEnabled(!fIsOverwriting);
      action.setChecked(fInsertMode == SMART_INSERT);
    }
  }

  /**
   * Handles a potential change of the cursor position.
   * Subclasses may extend.
   *
   * @since 2.0
   */
  protected void handleCursorPositionChanged() {
    updateStatusField(ITextEditorActionConstants.STATUS_CATEGORY_INPUT_POSITION);
  }

  /**
   * Updates the status fields for the given category.
   *
   * @param category the category
   * @since 2.0
   */
  protected void updateStatusField(String category) {

    if (category == null)
      return;

    IStatusField field= getStatusField(category);
    if (field != null) {

      String text= null;

      if (ITextEditorActionConstants.STATUS_CATEGORY_INPUT_POSITION.equals(category))
        text= getCursorPosition();
      else if (ITextEditorActionConstants.STATUS_CATEGORY_ELEMENT_STATE.equals(category))
        text= isEditorInputReadOnly() ? fReadOnlyLabel : fWritableLabel;
      else if (ITextEditorActionConstants.STATUS_CATEGORY_INPUT_MODE.equals(category)) {
        InsertMode mode= getInsertMode();
        if (fIsOverwriting)
          text= fOverwriteModeLabel;
        else if (INSERT == mode)
          text= fInsertModeLabel;
        else if (SMART_INSERT == mode)
          text= fSmartInsertModeLabel;
      }

      field.setText(text == null ? fErrorLabel : text);
    }
  }

  /**
   * Updates all status fields.
   *
   * @since 2.0
   */
  protected void updateStatusFields() {
    if (fStatusFields != null) {
      Iterator e= fStatusFields.keySet().iterator();
      while (e.hasNext())
        updateStatusField((String) e.next());
    }
  }

  /**
   * Returns a description of the cursor position.
   *
   * @return a description of the cursor position
   * @since 2.0
   */
  protected String getCursorPosition() {

    if (fSourceViewer == null)
      return fErrorLabel;

    StyledText styledText= fSourceViewer.getTextWidget();
    int caret= widgetOffset2ModelOffset(fSourceViewer, styledText.getCaretOffset());
    IDocument document= fSourceViewer.getDocument();

    if (document == null)
      return fErrorLabel;

    try {

      int line= document.getLineOfOffset(caret);

      int lineOffset= document.getLineOffset(line);
      int tabWidth= styledText.getTabs();
      int column= 0;
      for (int i= lineOffset; i < caret; i++)
        if ('\t' == document.getChar(i))
          column += tabWidth - (tabWidth == 0 ? 0 : column % tabWidth);
        else
          column++;

      fLineLabel.fValue= line + 1;
      fColumnLabel.fValue= column + 1;
      return NLSUtility.format(fPositionLabelPattern, fPositionLabelPatternArguments);

    } catch (BadLocationException x) {
      return fErrorLabel;
    }
  }

  /*
   * @see ITextEditorExtension#isEditorInputReadOnly()
   * @since 2.0
   */
  public boolean isEditorInputReadOnly() {
    IDocumentProvider provider= getDocumentProvider();
    if (provider instanceof IDocumentProviderExtension) {
      IDocumentProviderExtension extension= (IDocumentProviderExtension) provider;
      return extension.isReadOnly(getEditorInput());
    }
    return true;
  }

  /*
   * @see ITextEditorExtension2#isEditorInputModifiable()
   * @since 2.1
   */
  public boolean isEditorInputModifiable() {
    IDocumentProvider provider= getDocumentProvider();
    if (provider instanceof IDocumentProviderExtension) {
      IDocumentProviderExtension extension= (IDocumentProviderExtension) provider;
      return extension.isModifiable(getEditorInput());
    }
    return true;
  }

  /*
   * @see ITextEditorExtension#addRulerContextMenuListener(IMenuListener)
   * @since 2.0
   */
  public void addRulerContextMenuListener(IMenuListener listener) {
    fRulerContextMenuListeners.add(listener);
  }

  /*
   * @see ITextEditorExtension#removeRulerContextMenuListener(IMenuListener)
   * @since 2.0
   */
  public void removeRulerContextMenuListener(IMenuListener listener) {
    fRulerContextMenuListeners.remove(listener);
  }

  /**
   * Returns whether this editor can handle the move of the original element
   * so that it ends up being the moved element. By default this method
   * returns <code>true</code>. Subclasses may reimplement.
   *
   * @param originalElement the original element
   * @param movedElement the moved element
   * @return whether this editor can handle the move of the original element
   *         so that it ends up being the moved element
   * @since 2.0
   */
  protected boolean canHandleMove(IEditorInput originalElement, IEditorInput movedElement) {
    return true;
  }

  /**
   * Returns the offset of the given source viewer's document that corresponds
   * to the given widget offset or <code>-1</code> if there is no such offset.
   *
   * @param viewer the source viewer
   * @param widgetOffset the widget offset
   * @return the corresponding offset in the source viewer's document or <code>-1</code>
   * @since 2.1
   */
  protected static final int widgetOffset2ModelOffset(ISourceViewer viewer, int widgetOffset) {
    if (viewer instanceof ITextViewerExtension5) {
      ITextViewerExtension5 extension= (ITextViewerExtension5) viewer;
      return extension.widgetOffset2ModelOffset(widgetOffset);
    }
    return widgetOffset + viewer.getVisibleRegion().getOffset();
  }

  /**
   * Returns the offset of the given source viewer's text widget that corresponds
   * to the given model offset or <code>-1</code> if there is no such offset.
   *
   * @param viewer the source viewer
   * @param modelOffset the model offset
   * @return the corresponding offset in the source viewer's text widget or <code>-1</code>
   * @since 3.0
   */
  protected static final int modelOffset2WidgetOffset(ISourceViewer viewer, int modelOffset) {
    if (viewer instanceof ITextViewerExtension5) {
      ITextViewerExtension5 extension= (ITextViewerExtension5) viewer;
      return extension.modelOffset2WidgetOffset(modelOffset);
    }
    return modelOffset - viewer.getVisibleRegion().getOffset();
  }

  /**
   * Returns the minimal region of the given source viewer's document that completely
   * comprises everything that is visible in the viewer's widget.
   *
   * @param viewer the viewer go return the coverage for
   * @return the minimal region of the source viewer's document comprising the contents of the viewer's widget
   * @since 2.1
   */
  protected static final IRegion getCoverage(ISourceViewer viewer) {
    if (viewer instanceof ITextViewerExtension5) {
      ITextViewerExtension5 extension= (ITextViewerExtension5) viewer;
      return extension.getModelCoverage();
    }
    return viewer.getVisibleRegion();
  }

  /**
   * Tells whether the given region is visible in the given source viewer.
   *
   * @param viewer the source viewer
   * @param offset the offset of the region
   * @param length the length of the region
   * @return <code>true</code> if visible
   * @since 2.1
   */
  protected static final boolean isVisible(ISourceViewer 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);
  }

  /*
   * @see org.eclipse.ui.texteditor.ITextEditorExtension3#showChangeInformation(boolean)
   * @since 3.0
   */
  public void showChangeInformation(boolean show) {
    // do nothing
  }

  /*
   * @see org.eclipse.ui.texteditor.ITextEditorExtension3#isChangeInformationShowing()
   * @since 3.0
   */
  public boolean isChangeInformationShowing() {
    return false;
  }

  /**
   * Sets the given message as error message to this editor's status line.
   *
   * @param message message to be set
   * @since 3.2
   */
  protected void setStatusLineErrorMessage(String message) {
    IEditorStatusLine statusLine= (IEditorStatusLine)getAdapter(IEditorStatusLine.class);
    if (statusLine != null)
      statusLine.setMessage(true, message, null);
  }

  /**
   * Sets the given message as message to this editor's status line.
   *
   * @param message message to be set
   * @since 3.2
   */
  protected void setStatusLineMessage(String message) {
    IEditorStatusLine statusLine= (IEditorStatusLine)getAdapter(IEditorStatusLine.class);
    if (statusLine != null)
      statusLine.setMessage(false, message, null);
  }

  /**
   * Jumps to the next annotation according to the given direction.
   *
   * @param forward <code>true</code> if search direction is forward, <code>false</code> if backward
   * @return the selected annotation or <code>null</code> if none
   * @see #isNavigationTarget(Annotation)
   * @see #findAnnotation(int, int, boolean, Position)
   * @since 3.2
   */
  public Annotation gotoAnnotation(boolean forward) {
    ITextSelection selection= (ITextSelection) getSelectionProvider().getSelection();
    Position position= new Position(0, 0);
    Annotation annotation= findAnnotation(selection.getOffset(), selection.getLength(), forward, position);
    setStatusLineErrorMessage(null);
    setStatusLineMessage(null);

    if (annotation != null) {
      selectAndReveal(position.getOffset(), position.getLength());
      setStatusLineMessage(annotation.getText());
    }
    return annotation;
  }

  /**
   * Returns the annotation closest to the given range respecting the given
   * direction. If an annotation is found, the annotations current position
   * is copied into the provided annotation position.
   *
   * @param offset the region offset
   * @param length the region length
   * @param forward <code>true</code> for forwards, <code>false</code> for backward
   * @param annotationPosition the position of the found annotation
   * @return the found annotation
   * @since 3.2
   */
  protected Annotation findAnnotation(final int offset, final int length, boolean forward, Position annotationPosition) {

    Annotation nextAnnotation= null;
    Position nextAnnotationPosition= null;
    Annotation containingAnnotation= null;
    Position containingAnnotationPosition= null;
    boolean currentAnnotation= false;

    IDocument document= getDocumentProvider().getDocument(getEditorInput());
    int endOfDocument= document.getLength();
    int distance= Integer.MAX_VALUE;

    IAnnotationModel model= getDocumentProvider().getAnnotationModel(getEditorInput());
    Iterator e= model.getAnnotationIterator();
    while (e.hasNext()) {
      Annotation a= (Annotation) e.next();
      if (!isNavigationTarget(a))
        continue;

      Position p= model.getPosition(a);
      if (p == null)
        continue;

      if (forward && p.offset == offset || !forward && p.offset + p.getLength() == offset + length) {// || p.includes(offset)) {
        if (containingAnnotation == null || (forward && p.length >= containingAnnotationPosition.length || !forward && p.length >= containingAnnotationPosition.length)) {
          containingAnnotation= a;
          containingAnnotationPosition= p;
          currentAnnotation= p.length == length;
        }
      } else {
        int currentDistance= 0;

        if (forward) {
          currentDistance= p.getOffset() - offset;
          if (currentDistance < 0)
            currentDistance= endOfDocument + currentDistance;

          if (currentDistance < distance || currentDistance == distance && p.length < nextAnnotationPosition.length) {
            distance= currentDistance;
            nextAnnotation= a;
            nextAnnotationPosition= p;
          }
        } else {
          currentDistance= offset + length - (p.getOffset() + p.length);
          if (currentDistance < 0)
            currentDistance= endOfDocument + currentDistance;

          if (currentDistance < distance || currentDistance == distance && p.length < nextAnnotationPosition.length) {
            distance= currentDistance;
            nextAnnotation= a;
            nextAnnotationPosition= p;
          }
        }
      }
    }
    if (containingAnnotationPosition != null && (!currentAnnotation || nextAnnotation == null)) {
      annotationPosition.setOffset(containingAnnotationPosition.getOffset());
      annotationPosition.setLength(containingAnnotationPosition.getLength());
      return containingAnnotation;
    }
    if (nextAnnotationPosition != null) {
      annotationPosition.setOffset(nextAnnotationPosition.getOffset());
      annotationPosition.setLength(nextAnnotationPosition.getLength());
    }

    return nextAnnotation;
  }

  /**
   * Returns whether the given annotation is configured as a target for the
   * "Go to Next/Previous Annotation" actions.
   * <p>
   * Per default every annotation is a target.
   * </p>
   *
   * @param annotation the annotation
   * @return <code>true</code> if this is a target, <code>false</code> otherwise
   * @since 3.2
   */
  protected boolean isNavigationTarget(Annotation annotation) {
    return true;
  }

  /*
   * @see org.eclipse.ui.texteditor.ITextEditorExtension4#showRevisionInformation(org.eclipse.jface.text.revisions.RevisionInformation, java.lang.String)
   * @since 3.2
   */
  public void showRevisionInformation(RevisionInformation info, String quickDiffProviderId) {
    // no implementation
  }

  /*
   * @see org.eclipse.ui.IEditorPersistable#restoreState(org.eclipse.ui.IMemento)
   * @since 3.3
   */
  public void restoreState(IMemento memento) {
    fMementoToRestore= memento;
  }

  /*
   * @see org.eclipse.ui.IPersistable#saveState(org.eclipse.ui.IMemento)
   * @since 3.3
   */
  public void saveState(IMemento memento) {
    ISelection selection= doGetSelection();
    if (selection instanceof ITextSelection) {
      memento.putInteger(TAG_SELECTION_OFFSET, ((ITextSelection)selection).getOffset());
      memento.putInteger(TAG_SELECTION_LENGTH, ((ITextSelection)selection).getLength());
    }
    final StyledText textWidget= fSourceViewer.getTextWidget();
    memento.putInteger(TAG_SELECTION_TOP_PIXEL, textWidget.getTopPixel());
    memento.putInteger(TAG_SELECTION_HORIZONTAL_PIXEL, textWidget.getHorizontalPixel());
  }

  /**
   * Returns whether the given memento contains saved state
   * <p>
   * Subclasses may extend or override this method.</p>
   *
   * @param memento the saved state of this editor
   * @return <code>true</code> if the given memento contains saved state
   * @since 3.3
   */
  protected boolean containsSavedState(IMemento memento) {
    return memento.getInteger(TAG_SELECTION_OFFSET) != null && memento.getInteger(TAG_SELECTION_LENGTH) != null;
  }

  /**
   * Restores this editor's state using the given memento.
   * <p>
   * Subclasses may extend or override this method.</p>
   *
   * @param memento the saved state of this editor
   * @since 3.3
   */
  protected void doRestoreState(IMemento memento) {
    Integer offset= memento.getInteger(TAG_SELECTION_OFFSET);
    if (offset == null)
      return;

    Integer length= memento.getInteger(TAG_SELECTION_LENGTH);
    if (length == null)
      return;

    doSetSelection(new TextSelection(offset.intValue(), length.intValue()));

    final StyledText textWidget= fSourceViewer.getTextWidget();

    Integer topPixel= memento.getInteger(TAG_SELECTION_TOP_PIXEL);
    if (topPixel != null)
      textWidget.setTopPixel(topPixel.intValue());

    Integer horizontalPixel= memento.getInteger(TAG_SELECTION_HORIZONTAL_PIXEL);
    if (horizontalPixel != null)
      textWidget.setHorizontalPixel(horizontalPixel.intValue());
  }

  /*
   * @see org.eclipse.ui.ISaveablesSource#getSaveables()
   * @since 3.3
   */
  public Saveable[] getSaveables() {
    if (fSavable == null)
      fSavable= new TextEditorSavable(this);

    return new Saveable[] { fSavable };
  }

  /*
   * @see org.eclipse.ui.ISaveablesSource#getActiveSaveables()
   * @since 3.3
   */
  public Saveable[] getActiveSaveables() {
    return getSaveables();
  }

  /**
   * This text editor's savable.
   *
   * @since 3.3
   */
  protected static class TextEditorSavable extends Saveable {

    /** The cached editor. */
    private ITextEditor fTextEditor;
    /** The cached editor input. */
    private IEditorInput fEditorInput;
    /** The cached document. */
    private IDocument fDocument;

    /**
     * Creates a new savable for this text editor.
     *
     * @param textEditor the text editor
     */
    public TextEditorSavable(ITextEditor textEditor) {
      Assert.isLegal(textEditor != null);
      fTextEditor= textEditor;
      fEditorInput= fTextEditor.getEditorInput();
      Assert.isLegal(fEditorInput != null);
    }

    /**
     * Disconnects the editor from this savable.
     */
    public void disconnectEditor() {
      getAdapter(IDocument.class); // make sure the document is cached
      fTextEditor= null;
    }

    /*
     * @see org.eclipse.ui.Saveable#getName()
     */
    public String getName() {
      return fEditorInput.getName();
    }

    /*
     * @see org.eclipse.ui.Saveable#getToolTipText()
     */
    public String getToolTipText() {
      return fEditorInput.getToolTipText();
    }

    /*
     * @see org.eclipse.ui.Saveable#getImageDescriptor()
     */
    public ImageDescriptor getImageDescriptor() {
      return fEditorInput.getImageDescriptor();
    }

    /*
     * @see org.eclipse.ui.Saveable#doSave(org.eclipse.core.runtime.IProgressMonitor)
     * @since 3.3
     */
    public void doSave(IProgressMonitor monitor) throws CoreException {
      fTextEditor.doSave(monitor);
    }

    public boolean isDirty() {
      return fTextEditor.isDirty();
    }

    /*
     * @see org.eclipse.ui.Saveable#supportsBackgroundSave()
     */
    public boolean supportsBackgroundSave() {
      return false;
    }

    /*
     * @see org.eclipse.ui.Saveable#hashCode()
     */
    public int hashCode() {
      Object document= getAdapter(IDocument.class);
      if (document == null)
        return 0;
      return document.hashCode();
    }

    /*
     * @see org.eclipse.ui.Saveable#equals(java.lang.Object)
     */
    public boolean equals(Object obj) {
      if (this == obj)
        return true;

      if (!(obj instanceof Saveable))
        return false;

      Object thisDocument= getAdapter(IDocument.class);
      Object otherDocument= ((Saveable)obj).getAdapter(IDocument.class);

      if (thisDocument == null && otherDocument == null)
        return true;

      return thisDocument != null && thisDocument.equals(otherDocument);
    }

    /**
     * Explicit comment needed to suppress wrong warning caused by
     * http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4848177
     *
     * @see org.eclipse.ui.Saveable#getAdapter(java.lang.Class)
     */
    public Object getAdapter(Class adapter) {
      if (adapter == IDocument.class) {
        if (fDocument == null) {
          IDocumentProvider documentProvider= fTextEditor.getDocumentProvider();
          if (documentProvider != null)
            fDocument= documentProvider.getDocument(fEditorInput);
        }
        return fDocument;
      }
      return super.getAdapter(adapter);
    }
  }

  //---- Tabs to spaces conversion support ------------------

  /**
   * Installs a tabs to spaces converter.
   *
   * <p>Subclasses may extend or override this method.</p>
   *
   * @since 3.3
   */
  protected void installTabsToSpacesConverter() {
    SourceViewerConfiguration config= getSourceViewerConfiguration();
    if (config != null && fSourceViewer instanceof ITextViewerExtension7) {
      int tabWidth= config.getTabWidth(fSourceViewer);
      TabsToSpacesConverter tabToSpacesConverter= new TabsToSpacesConverter();
      tabToSpacesConverter.setLineTracker(new DefaultLineTracker());
      tabToSpacesConverter.setNumberOfSpacesPerTab(tabWidth);
      ((ITextViewerExtension7)fSourceViewer).setTabsToSpacesConverter(tabToSpacesConverter);
      updateIndentPrefixes();
    }
  }

  /**
   * Installs a tabs to spaces converter.
   *
   * <p>Subclasses may extend or override this method.</p>
   *
   * @since 3.3
   */
  protected void uninstallTabsToSpacesConverter() {
    if (fSourceViewer instanceof ITextViewerExtension7) {
      ((ITextViewerExtension7)fSourceViewer).setTabsToSpacesConverter(null);
      if (fSourceViewer.getTextWidget() != null)
        updateIndentPrefixes();
    }
  }

  /**
   * Tells whether tabs should be converted to
   * spaces while editing inside this editor.
   *
   * <p>Subclasses may override this method.</p>
   *
   * @return <code>true</code> if tabs should be converted to spaces
   * @since 3.3
   */
  protected boolean isTabsToSpacesConversionEnabled() {
    return false;
  }

  /**
   * Updates the source viewer's indent prefixes with
   * the values provided by the source viewer configuration.
   *
   * @since 3.3
   */
  protected final void updateIndentPrefixes() {
    SourceViewerConfiguration configuration= getSourceViewerConfiguration();
    String[] types= configuration.getConfiguredContentTypes(fSourceViewer);
    for (int i= 0; i < types.length; i++) {
      String[] prefixes= configuration.getIndentPrefixes(fSourceViewer, types[i]);
      if (prefixes != null && prefixes.length > 0)
        fSourceViewer.setIndentPrefixes(prefixes, types[i]);
    }
  }

  /**
   * Tells whether selection mode is supported.
   * <p>
   * By default block selection mode is supported. Subclasses may override this method to disable
   * it.
   * </p>
   *
   * @return <code>true</code> if block selection mode is supported, <code>false</code> otherwise
   * @since 3.5
   */
  protected boolean isBlockSelectionModeSupported() {
    return true;
  }

  /**
   * @see org.eclipse.ui.texteditor.ITextEditorExtension5#isBlockSelectionModeEnabled()
   * @since 3.5
   */
  public final boolean isBlockSelectionModeEnabled() {
    ISourceViewer viewer= getSourceViewer();
    if (viewer != null) {
      StyledText styledText= viewer.getTextWidget();
      if (styledText != null)
        return styledText.getBlockSelection();
    }
    return false;
  }

  /**
   * @see org.eclipse.ui.texteditor.ITextEditorExtension5#setBlockSelectionMode(boolean)
   * @since 3.5
   */
  public void setBlockSelectionMode(boolean enable) {
    if (!isBlockSelectionModeSupported())
      return;

    ISourceViewer viewer= getSourceViewer();
    if (viewer != null) {
      StyledText styledText= viewer.getTextWidget();
      if (styledText != null) {
        /*
         * Font switching. block selection mode needs a monospace font.
         *  - set the font _before enabling_ block selection mode in order to maintain the
         * selection
         * - revert the font _after disabling_ block selection mode in order to maintain the
         * selection
         */
        if (enable) {
          Font blockFont= JFaceResources.getFont(BLOCK_SELECTION_MODE_FONT);
          Font normalFont= styledText.getFont();
          if (!blockFont.equals(normalFont) && !normalFont.getFontData()[0].equals(blockFont.getFontData()[0])) {
            setFont(viewer, blockFont);
            disposeFont();
            updateCaret();
          }
        }

        styledText.setBlockSelection(enable);
       
        if (!enable) {
          initializeViewerFont(viewer);
          updateCaret();
        }
      }
    }
  }
}
TOP

Related Classes of org.eclipse.ui.texteditor.AbstractTextEditor

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.