0001: /*******************************************************************************
0002: * Copyright (c) 2000, 2007 IBM Corporation and others.
0003: * All rights reserved. This program and the accompanying materials
0004: * are made available under the terms of the Eclipse Public License v1.0
0005: * which accompanies this distribution, and is available at
0006: * http://www.eclipse.org/legal/epl-v10.html
0007: *
0008: * Contributors:
0009: * IBM Corporation - initial API and implementation
0010: * Chris.Dennis@invidi.com - http://bugs.eclipse.org/bugs/show_bug.cgi?id=29027
0011: * Michel Ishizuka (cqw10305@nifty.com) - http://bugs.eclipse.org/bugs/show_bug.cgi?id=68963
0012: * Genady Beryozkin, me@genady.org - https://bugs.eclipse.org/bugs/show_bug.cgi?id=11668
0013: * Benjamin Muskalla <b.muskalla@gmx.net> - https://bugs.eclipse.org/bugs/show_bug.cgi?id=41573
0014: *******************************************************************************/package org.eclipse.ui.texteditor;
0015:
0016: import java.lang.reflect.InvocationTargetException;
0017: import java.util.ArrayList;
0018: import java.util.HashMap;
0019: import java.util.Iterator;
0020: import java.util.List;
0021: import java.util.Map;
0022: import java.util.ResourceBundle;
0023:
0024: import org.osgi.framework.Bundle;
0025:
0026: import org.eclipse.swt.SWT;
0027: import org.eclipse.swt.custom.BusyIndicator;
0028: import org.eclipse.swt.custom.ST;
0029: import org.eclipse.swt.custom.StyledText;
0030: import org.eclipse.swt.custom.VerifyKeyListener;
0031: import org.eclipse.swt.dnd.DND;
0032: import org.eclipse.swt.dnd.DragSource;
0033: import org.eclipse.swt.dnd.DragSourceAdapter;
0034: import org.eclipse.swt.dnd.DragSourceEvent;
0035: import org.eclipse.swt.dnd.DropTargetAdapter;
0036: import org.eclipse.swt.dnd.DropTargetEvent;
0037: import org.eclipse.swt.dnd.DropTargetListener;
0038: import org.eclipse.swt.dnd.TextTransfer;
0039: import org.eclipse.swt.dnd.Transfer;
0040: import org.eclipse.swt.events.KeyEvent;
0041: import org.eclipse.swt.events.KeyListener;
0042: import org.eclipse.swt.events.MouseEvent;
0043: import org.eclipse.swt.events.MouseListener;
0044: import org.eclipse.swt.events.VerifyEvent;
0045: import org.eclipse.swt.events.VerifyListener;
0046: import org.eclipse.swt.graphics.Color;
0047: import org.eclipse.swt.graphics.Font;
0048: import org.eclipse.swt.graphics.FontData;
0049: import org.eclipse.swt.graphics.GC;
0050: import org.eclipse.swt.graphics.Image;
0051: import org.eclipse.swt.graphics.ImageData;
0052: import org.eclipse.swt.graphics.PaletteData;
0053: import org.eclipse.swt.graphics.Point;
0054: import org.eclipse.swt.graphics.RGB;
0055: import org.eclipse.swt.widgets.Caret;
0056: import org.eclipse.swt.widgets.Composite;
0057: import org.eclipse.swt.widgets.Control;
0058: import org.eclipse.swt.widgets.Display;
0059: import org.eclipse.swt.widgets.Menu;
0060: import org.eclipse.swt.widgets.Shell;
0061:
0062: import org.eclipse.core.commands.operations.IOperationApprover;
0063: import org.eclipse.core.commands.operations.IOperationHistory;
0064: import org.eclipse.core.commands.operations.IUndoContext;
0065: import org.eclipse.core.commands.operations.OperationHistoryFactory;
0066:
0067: import org.eclipse.core.runtime.Assert;
0068: import org.eclipse.core.runtime.CoreException;
0069: import org.eclipse.core.runtime.IConfigurationElement;
0070: import org.eclipse.core.runtime.ILog;
0071: import org.eclipse.core.runtime.IProgressMonitor;
0072: import org.eclipse.core.runtime.IStatus;
0073: import org.eclipse.core.runtime.NullProgressMonitor;
0074: import org.eclipse.core.runtime.Platform;
0075: import org.eclipse.core.runtime.SafeRunner;
0076: import org.eclipse.core.runtime.Status;
0077:
0078: import org.eclipse.text.undo.DocumentUndoManagerRegistry;
0079: import org.eclipse.text.undo.IDocumentUndoManager;
0080:
0081: import org.eclipse.jface.action.Action;
0082: import org.eclipse.jface.action.GroupMarker;
0083: import org.eclipse.jface.action.IAction;
0084: import org.eclipse.jface.action.IMenuListener;
0085: import org.eclipse.jface.action.IMenuManager;
0086: import org.eclipse.jface.action.IStatusLineManager;
0087: import org.eclipse.jface.action.MenuManager;
0088: import org.eclipse.jface.action.Separator;
0089: import org.eclipse.jface.dialogs.ErrorDialog;
0090: import org.eclipse.jface.dialogs.MessageDialog;
0091: import org.eclipse.jface.internal.text.html.HTMLTextPresenter;
0092: import org.eclipse.jface.operation.IRunnableWithProgress;
0093: import org.eclipse.jface.preference.IPreferenceStore;
0094: import org.eclipse.jface.preference.PreferenceConverter;
0095: import org.eclipse.jface.resource.ImageDescriptor;
0096: import org.eclipse.jface.resource.JFaceResources;
0097: import org.eclipse.jface.util.IPropertyChangeListener;
0098: import org.eclipse.jface.util.PropertyChangeEvent;
0099: import org.eclipse.jface.util.SafeRunnable;
0100: import org.eclipse.jface.viewers.IPostSelectionProvider;
0101: import org.eclipse.jface.viewers.ISelection;
0102: import org.eclipse.jface.viewers.ISelectionChangedListener;
0103: import org.eclipse.jface.viewers.ISelectionProvider;
0104: import org.eclipse.jface.viewers.SelectionChangedEvent;
0105: import org.eclipse.jface.viewers.StructuredSelection;
0106: import org.eclipse.jface.window.IShellProvider;
0107:
0108: import org.eclipse.jface.text.AbstractInformationControlManager;
0109: import org.eclipse.jface.text.BadLocationException;
0110: import org.eclipse.jface.text.DefaultInformationControl;
0111: import org.eclipse.jface.text.DefaultLineTracker;
0112: import org.eclipse.jface.text.DocumentEvent;
0113: import org.eclipse.jface.text.IDocument;
0114: import org.eclipse.jface.text.IDocumentListener;
0115: import org.eclipse.jface.text.IFindReplaceTarget;
0116: import org.eclipse.jface.text.IFindReplaceTargetExtension;
0117: import org.eclipse.jface.text.IInformationControl;
0118: import org.eclipse.jface.text.IInformationControlCreator;
0119: import org.eclipse.jface.text.IMarkRegionTarget;
0120: import org.eclipse.jface.text.IRegion;
0121: import org.eclipse.jface.text.IRewriteTarget;
0122: import org.eclipse.jface.text.ISelectionValidator;
0123: import org.eclipse.jface.text.ITextHover;
0124: import org.eclipse.jface.text.ITextInputListener;
0125: import org.eclipse.jface.text.ITextListener;
0126: import org.eclipse.jface.text.ITextOperationTarget;
0127: import org.eclipse.jface.text.ITextSelection;
0128: import org.eclipse.jface.text.ITextViewer;
0129: import org.eclipse.jface.text.ITextViewerExtension;
0130: import org.eclipse.jface.text.ITextViewerExtension2;
0131: import org.eclipse.jface.text.ITextViewerExtension4;
0132: import org.eclipse.jface.text.ITextViewerExtension5;
0133: import org.eclipse.jface.text.ITextViewerExtension6;
0134: import org.eclipse.jface.text.ITextViewerExtension7;
0135: import org.eclipse.jface.text.IUndoManager;
0136: import org.eclipse.jface.text.IUndoManagerExtension;
0137: import org.eclipse.jface.text.Position;
0138: import org.eclipse.jface.text.Region;
0139: import org.eclipse.jface.text.TabsToSpacesConverter;
0140: import org.eclipse.jface.text.TextEvent;
0141: import org.eclipse.jface.text.TextSelection;
0142: import org.eclipse.jface.text.TextUtilities;
0143: import org.eclipse.jface.text.hyperlink.IHyperlinkDetector;
0144: import org.eclipse.jface.text.information.IInformationProvider;
0145: import org.eclipse.jface.text.information.IInformationProviderExtension;
0146: import org.eclipse.jface.text.information.IInformationProviderExtension2;
0147: import org.eclipse.jface.text.information.InformationPresenter;
0148: import org.eclipse.jface.text.link.LinkedModeModel;
0149: import org.eclipse.jface.text.link.LinkedPosition;
0150: import org.eclipse.jface.text.revisions.RevisionInformation;
0151: import org.eclipse.jface.text.source.Annotation;
0152: import org.eclipse.jface.text.source.CompositeRuler;
0153: import org.eclipse.jface.text.source.IAnnotationHover;
0154: import org.eclipse.jface.text.source.IAnnotationHoverExtension;
0155: import org.eclipse.jface.text.source.IAnnotationModel;
0156: import org.eclipse.jface.text.source.ILineRange;
0157: import org.eclipse.jface.text.source.ISourceViewer;
0158: import org.eclipse.jface.text.source.ISourceViewerExtension3;
0159: import org.eclipse.jface.text.source.IVerticalRuler;
0160: import org.eclipse.jface.text.source.IVerticalRulerColumn;
0161: import org.eclipse.jface.text.source.IVerticalRulerExtension;
0162: import org.eclipse.jface.text.source.IVerticalRulerInfo;
0163: import org.eclipse.jface.text.source.SourceViewer;
0164: import org.eclipse.jface.text.source.SourceViewerConfiguration;
0165: import org.eclipse.jface.text.source.VerticalRuler;
0166:
0167: import org.eclipse.ui.IActionBars;
0168: import org.eclipse.ui.IEditorDescriptor;
0169: import org.eclipse.ui.IEditorInput;
0170: import org.eclipse.ui.IEditorPart;
0171: import org.eclipse.ui.IEditorRegistry;
0172: import org.eclipse.ui.IEditorSite;
0173: import org.eclipse.ui.IKeyBindingService;
0174: import org.eclipse.ui.IMemento;
0175: import org.eclipse.ui.INavigationLocation;
0176: import org.eclipse.ui.INavigationLocationProvider;
0177: import org.eclipse.ui.IPartListener;
0178: import org.eclipse.ui.IPartService;
0179: import org.eclipse.ui.IPersistableEditor;
0180: import org.eclipse.ui.IReusableEditor;
0181: import org.eclipse.ui.ISaveablesLifecycleListener;
0182: import org.eclipse.ui.ISaveablesSource;
0183: import org.eclipse.ui.IWindowListener;
0184: import org.eclipse.ui.IWorkbenchActionConstants;
0185: import org.eclipse.ui.IWorkbenchPart;
0186: import org.eclipse.ui.IWorkbenchWindow;
0187: import org.eclipse.ui.PartInitException;
0188: import org.eclipse.ui.PlatformUI;
0189: import org.eclipse.ui.Saveable;
0190: import org.eclipse.ui.SaveablesLifecycleEvent;
0191: import org.eclipse.ui.actions.CommandNotMappedException;
0192: import org.eclipse.ui.actions.ContributedAction;
0193: import org.eclipse.ui.dialogs.PropertyDialogAction;
0194: import org.eclipse.ui.dnd.IDragAndDropService;
0195: import org.eclipse.ui.internal.texteditor.EditPosition;
0196: import org.eclipse.ui.internal.texteditor.NLSUtility;
0197: import org.eclipse.ui.internal.texteditor.TextEditorPlugin;
0198: import org.eclipse.ui.internal.texteditor.rulers.StringSetSerializer;
0199: import org.eclipse.ui.operations.LinearUndoViolationUserApprover;
0200: import org.eclipse.ui.operations.NonLocalUndoUserApprover;
0201: import org.eclipse.ui.operations.OperationHistoryActionHandler;
0202: import org.eclipse.ui.operations.RedoActionHandler;
0203: import org.eclipse.ui.operations.UndoActionHandler;
0204: import org.eclipse.ui.part.EditorPart;
0205: import org.eclipse.ui.texteditor.rulers.IColumnSupport;
0206: import org.eclipse.ui.texteditor.rulers.IContributedRulerColumn;
0207: import org.eclipse.ui.texteditor.rulers.RulerColumnDescriptor;
0208: import org.eclipse.ui.texteditor.rulers.RulerColumnPreferenceAdapter;
0209: import org.eclipse.ui.texteditor.rulers.RulerColumnRegistry;
0210:
0211: /**
0212: * Abstract base implementation of a text editor.
0213: * <p>
0214: * Subclasses are responsible for configuring the editor appropriately.
0215: * The standard text editor, <code>TextEditor</code>, is one such example.</p>
0216: * <p>
0217: * If a subclass calls {@linkplain #setEditorContextMenuId(String) setEditorContextMenuId} the argument is
0218: * used as the id under which the editor's context menu is registered for extensions.
0219: * If no id is set, the context menu is registered under <b>[editor_id].EditorContext</b>
0220: * whereby [editor_id] is replaced with the editor's part id. If the editor is instructed to
0221: * run in version 1.0 context menu registration compatibility mode, the latter form of the
0222: * registration even happens if a context menu id has been set via {@linkplain #setEditorContextMenuId(String) setEditorContextMenuId}.
0223: * If no id is set while in compatibility mode, the menu is registered under
0224: * {@link #DEFAULT_EDITOR_CONTEXT_MENU_ID}.</p>
0225: * <p>
0226: * If a subclass calls {@linkplain #setRulerContextMenuId(String) setRulerContextMenuId} the argument is
0227: * used as the id under which the ruler's context menu is registered for extensions.
0228: * If no id is set, the context menu is registered under <b>[editor_id].RulerContext</b>
0229: * whereby [editor_id] is replaced with the editor's part id. If the editor is instructed to
0230: * run in version 1.0 context menu registration compatibility mode, the latter form of the
0231: * registration even happens if a context menu id has been set via {@linkplain #setRulerContextMenuId(String) setRulerContextMenuId}.
0232: * If no id is set while in compatibility mode, the menu is registered under
0233: * {@link #DEFAULT_RULER_CONTEXT_MENU_ID}.</p>
0234: */
0235: public abstract class AbstractTextEditor extends EditorPart implements
0236: ITextEditor, IReusableEditor, ITextEditorExtension,
0237: ITextEditorExtension2, ITextEditorExtension3,
0238: ITextEditorExtension4, INavigationLocationProvider,
0239: ISaveablesSource, IPersistableEditor {
0240:
0241: /**
0242: * Tag used in xml configuration files to specify editor action contributions.
0243: * Current value: <code>editorContribution</code>
0244: * @since 2.0
0245: */
0246: private static final String TAG_CONTRIBUTION_TYPE = "editorContribution"; //$NON-NLS-1$
0247:
0248: /**
0249: * Tags used in the {@link IMemento} when saving and
0250: * restoring editor state.
0251: *
0252: * @see #saveState(IMemento)
0253: * @see #restoreState(IMemento)
0254: * @since 3.3
0255: */
0256: protected static final String TAG_SELECTION_OFFSET = "selectionOffset"; //$NON-NLS-1$
0257: protected static final String TAG_SELECTION_LENGTH = "selectionLength"; //$NON-NLS-1$
0258: // XXX: workaround for https://bugs.eclipse.org/bugs/show_bug.cgi?id=168524
0259: private static final String TAG_SELECTION_HPIXEL = "selectionHPixel"; //$NON-NLS-1$
0260:
0261: /**
0262: * The caret width for the wide (double) caret.
0263: * See https://bugs.eclipse.org/bugs/show_bug.cgi?id=21715.
0264: * Value: {@value}
0265: * @since 3.0
0266: */
0267: private static final int WIDE_CARET_WIDTH = 2;
0268:
0269: /**
0270: * The caret width for the narrow (single) caret.
0271: * See https://bugs.eclipse.org/bugs/show_bug.cgi?id=21715.
0272: * Value: {@value}
0273: * @since 3.0
0274: */
0275: private static final int SINGLE_CARET_WIDTH = 1;
0276:
0277: /**
0278: * The text input listener.
0279: *
0280: * @see ITextInputListener
0281: * @since 2.1
0282: */
0283: private static class TextInputListener implements
0284: ITextInputListener {
0285: /** Indicates whether the editor input changed during the process of state validation. */
0286: public boolean inputChanged;
0287:
0288: /* Detectors for editor input changes during the process of state validation. */
0289: public void inputDocumentAboutToBeChanged(IDocument oldInput,
0290: IDocument newInput) {
0291: }
0292:
0293: public void inputDocumentChanged(IDocument oldInput,
0294: IDocument newInput) {
0295: inputChanged = true;
0296: }
0297: }
0298:
0299: /**
0300: * Internal element state listener.
0301: */
0302: class ElementStateListener implements IElementStateListener,
0303: IElementStateListenerExtension {
0304:
0305: /**
0306: * Internal <code>VerifyListener</code> for performing the state validation of the
0307: * editor input in case of the first attempted manipulation via typing on the keyboard.
0308: * @since 2.0
0309: */
0310: class Validator implements VerifyListener {
0311: /*
0312: * @see VerifyListener#verifyText(org.eclipse.swt.events.VerifyEvent)
0313: */
0314: public void verifyText(VerifyEvent e) {
0315: IDocument document = getDocumentProvider().getDocument(
0316: getEditorInput());
0317: final boolean[] documentChanged = new boolean[1];
0318: IDocumentListener listener = new IDocumentListener() {
0319: public void documentAboutToBeChanged(
0320: DocumentEvent event) {
0321: }
0322:
0323: public void documentChanged(DocumentEvent event) {
0324: documentChanged[0] = true;
0325: }
0326: };
0327: try {
0328: if (document != null)
0329: document.addDocumentListener(listener);
0330: if (!validateEditorInputState()
0331: || documentChanged[0])
0332: e.doit = false;
0333: } finally {
0334: if (document != null)
0335: document.removeDocumentListener(listener);
0336: }
0337: }
0338: }
0339:
0340: /**
0341: * The listener's validator.
0342: * @since 2.0
0343: */
0344: private Validator fValidator;
0345: /**
0346: * The display used for posting runnable into the UI thread.
0347: * @since 3.0
0348: */
0349: private Display fDisplay;
0350:
0351: /*
0352: * @see IElementStateListenerExtension#elementStateValidationChanged(Object, boolean)
0353: * @since 2.0
0354: */
0355: public void elementStateValidationChanged(final Object element,
0356: final boolean isStateValidated) {
0357: if (element != null && element.equals(getEditorInput())) {
0358: Runnable r = new Runnable() {
0359: public void run() {
0360: enableSanityChecking(true);
0361: if (isStateValidated && fValidator != null) {
0362: ISourceViewer viewer = fSourceViewer;
0363: if (viewer != null) {
0364: StyledText textWidget = viewer
0365: .getTextWidget();
0366: if (textWidget != null
0367: && !textWidget.isDisposed())
0368: textWidget
0369: .removeVerifyListener(fValidator);
0370: fValidator = null;
0371: enableStateValidation(false);
0372: }
0373: } else if (!isStateValidated
0374: && fValidator == null) {
0375: ISourceViewer viewer = fSourceViewer;
0376: if (viewer != null) {
0377: StyledText textWidget = viewer
0378: .getTextWidget();
0379: if (textWidget != null
0380: && !textWidget.isDisposed()) {
0381: fValidator = new Validator();
0382: enableStateValidation(true);
0383: textWidget
0384: .addVerifyListener(fValidator);
0385: }
0386: }
0387: }
0388: }
0389: };
0390: execute(r, false);
0391: }
0392: }
0393:
0394: /*
0395: * @see IElementStateListener#elementDirtyStateChanged(Object, boolean)
0396: */
0397: public void elementDirtyStateChanged(Object element,
0398: boolean isDirty) {
0399: if (element != null && element.equals(getEditorInput())) {
0400: Runnable r = new Runnable() {
0401: public void run() {
0402: enableSanityChecking(true);
0403: firePropertyChange(PROP_DIRTY);
0404: }
0405: };
0406: execute(r, false);
0407: }
0408: }
0409:
0410: /*
0411: * @see IElementStateListener#elementContentAboutToBeReplaced(Object)
0412: */
0413: public void elementContentAboutToBeReplaced(Object element) {
0414: if (element != null && element.equals(getEditorInput())) {
0415: Runnable r = new Runnable() {
0416: public void run() {
0417: enableSanityChecking(true);
0418: rememberSelection();
0419: resetHighlightRange();
0420: }
0421: };
0422: execute(r, false);
0423: }
0424: }
0425:
0426: /*
0427: * @see IElementStateListener#elementContentReplaced(Object)
0428: */
0429: public void elementContentReplaced(Object element) {
0430: if (element != null && element.equals(getEditorInput())) {
0431: Runnable r = new Runnable() {
0432: public void run() {
0433: enableSanityChecking(true);
0434: firePropertyChange(PROP_DIRTY);
0435: restoreSelection();
0436: handleElementContentReplaced();
0437: }
0438: };
0439: execute(r, false);
0440: }
0441: }
0442:
0443: /*
0444: * @see IElementStateListener#elementDeleted(Object)
0445: */
0446: public void elementDeleted(Object deletedElement) {
0447: if (deletedElement != null
0448: && deletedElement.equals(getEditorInput())) {
0449: Runnable r = new Runnable() {
0450: public void run() {
0451: enableSanityChecking(true);
0452: close(false);
0453: }
0454: };
0455: execute(r, false);
0456: }
0457: }
0458:
0459: /*
0460: * @see IElementStateListener#elementMoved(Object, Object)
0461: */
0462: public void elementMoved(final Object originalElement,
0463: final Object movedElement) {
0464: if (originalElement != null
0465: && originalElement.equals(getEditorInput())) {
0466: final boolean doValidationAsync = Display.getCurrent() != null;
0467: Runnable r = new Runnable() {
0468: public void run() {
0469: enableSanityChecking(true);
0470:
0471: if (fSourceViewer == null)
0472: return;
0473:
0474: if (!canHandleMove(
0475: (IEditorInput) originalElement,
0476: (IEditorInput) movedElement)) {
0477: close(true);
0478: return;
0479: }
0480:
0481: if (movedElement == null
0482: || movedElement instanceof IEditorInput) {
0483: rememberSelection();
0484:
0485: final IDocumentProvider d = getDocumentProvider();
0486: final String previousContent;
0487: IDocumentUndoManager previousUndoManager = null;
0488: IDocument changed = null;
0489: boolean wasDirty = isDirty();
0490: changed = d.getDocument(getEditorInput());
0491: if (changed != null) {
0492: if (wasDirty)
0493: previousContent = changed.get();
0494: else
0495: previousContent = null;
0496:
0497: previousUndoManager = DocumentUndoManagerRegistry
0498: .getDocumentUndoManager(changed);
0499: if (previousUndoManager != null)
0500: previousUndoManager.connect(this );
0501: } else
0502: previousContent = null;
0503:
0504: setInput((IEditorInput) movedElement);
0505:
0506: // The undo manager needs to be replaced with one for the new document.
0507: // Transfer the undo history and then disconnect from the old undo manager.
0508: if (previousUndoManager != null) {
0509: IDocument newDocument = getDocumentProvider()
0510: .getDocument(movedElement);
0511: if (newDocument != null) {
0512: IDocumentUndoManager newUndoManager = DocumentUndoManagerRegistry
0513: .getDocumentUndoManager(newDocument);
0514: if (newUndoManager != null)
0515: newUndoManager
0516: .transferUndoHistory(previousUndoManager);
0517: }
0518: previousUndoManager.disconnect(this );
0519: }
0520:
0521: if (wasDirty && changed != null) {
0522: Runnable r2 = new Runnable() {
0523: public void run() {
0524: validateState(getEditorInput());
0525: d.getDocument(getEditorInput())
0526: .set(previousContent);
0527: updateStatusField(ITextEditorActionConstants.STATUS_CATEGORY_ELEMENT_STATE);
0528: restoreSelection();
0529: }
0530: };
0531: execute(r2, doValidationAsync);
0532: } else
0533: restoreSelection();
0534:
0535: }
0536: }
0537: };
0538: execute(r, false);
0539: }
0540: }
0541:
0542: /*
0543: * @see IElementStateListenerExtension#elementStateChanging(Object)
0544: * @since 2.0
0545: */
0546: public void elementStateChanging(Object element) {
0547: if (element != null && element.equals(getEditorInput()))
0548: enableSanityChecking(false);
0549: }
0550:
0551: /*
0552: * @see IElementStateListenerExtension#elementStateChangeFailed(Object)
0553: * @since 2.0
0554: */
0555: public void elementStateChangeFailed(Object element) {
0556: if (element != null && element.equals(getEditorInput()))
0557: enableSanityChecking(true);
0558: }
0559:
0560: /**
0561: * Executes the given runnable in the UI thread.
0562: * <p>
0563: * See https://bugs.eclipse.org/bugs/show_bug.cgi?id=76765 for details
0564: * about why the parameter <code>postAsync</code> has been
0565: * introduced in the course of 3.1.
0566: *
0567: * @param runnable runnable to be executed
0568: * @param postAsync <code>true</code> if the runnable must be posted asynchronous, <code>false</code> otherwise
0569: * @since 3.0
0570: */
0571: private void execute(Runnable runnable, boolean postAsync) {
0572: if (postAsync || Display.getCurrent() == null) {
0573: if (fDisplay == null)
0574: fDisplay = getSite().getShell().getDisplay();
0575: fDisplay.asyncExec(runnable);
0576: } else
0577: runnable.run();
0578: }
0579: }
0580:
0581: /**
0582: * Internal text listener for updating all content dependent
0583: * actions. The updating is done asynchronously.
0584: */
0585: class TextListener implements ITextListener, ITextInputListener {
0586:
0587: /** The posted updater code. */
0588: private Runnable fRunnable = new Runnable() {
0589: public void run() {
0590: fIsRunnablePosted = false;
0591:
0592: if (fSourceViewer != null) {
0593: updateContentDependentActions();
0594:
0595: // remember the last edit position
0596: if (isDirty() && fUpdateLastEditPosition) {
0597: fUpdateLastEditPosition = false;
0598: ISelection sel = getSelectionProvider()
0599: .getSelection();
0600: IEditorInput input = getEditorInput();
0601: IDocument document = getDocumentProvider()
0602: .getDocument(input);
0603:
0604: if (fLocalLastEditPosition != null) {
0605: document
0606: .removePosition(fLocalLastEditPosition);
0607: fLocalLastEditPosition = null;
0608: }
0609:
0610: if (sel instanceof ITextSelection
0611: && !sel.isEmpty()) {
0612: ITextSelection s = (ITextSelection) sel;
0613: fLocalLastEditPosition = new Position(s
0614: .getOffset(), s.getLength());
0615: try {
0616: document
0617: .addPosition(fLocalLastEditPosition);
0618: } catch (BadLocationException ex) {
0619: fLocalLastEditPosition = null;
0620: }
0621: }
0622: TextEditorPlugin
0623: .getDefault()
0624: .setLastEditPosition(
0625: new EditPosition(
0626: input,
0627: getEditorSite().getId(),
0628: fLocalLastEditPosition));
0629: }
0630: }
0631: }
0632: };
0633:
0634: /** Display used for posting the updater code. */
0635: private Display fDisplay;
0636: /**
0637: * The editor's last edit position
0638: * @since 3.0
0639: */
0640: private Position fLocalLastEditPosition;
0641: /**
0642: * Has the runnable been posted?
0643: * @since 3.0
0644: */
0645: private boolean fIsRunnablePosted = false;
0646: /**
0647: * Should the last edit position be updated?
0648: * @since 3.0
0649: */
0650: private boolean fUpdateLastEditPosition = false;
0651:
0652: /*
0653: * @see ITextListener#textChanged(TextEvent)
0654: */
0655: public void textChanged(TextEvent event) {
0656:
0657: /*
0658: * Also works for text events which do not base on a DocumentEvent.
0659: * This way, if the visible document of the viewer changes, all content
0660: * dependent actions are updated as well.
0661: */
0662:
0663: if (fDisplay == null)
0664: fDisplay = getSite().getShell().getDisplay();
0665:
0666: if (event.getDocumentEvent() != null)
0667: fUpdateLastEditPosition = true;
0668:
0669: if (!fIsRunnablePosted) {
0670: fIsRunnablePosted = true;
0671: fDisplay.asyncExec(fRunnable);
0672: }
0673: }
0674:
0675: /*
0676: * @see org.eclipse.jface.text.ITextInputListener#inputDocumentAboutToBeChanged(org.eclipse.jface.text.IDocument, org.eclipse.jface.text.IDocument)
0677: */
0678: public void inputDocumentAboutToBeChanged(IDocument oldInput,
0679: IDocument newInput) {
0680: if (oldInput != null && fLocalLastEditPosition != null) {
0681: oldInput.removePosition(fLocalLastEditPosition);
0682: fLocalLastEditPosition = null;
0683: }
0684: }
0685:
0686: /*
0687: * @see org.eclipse.jface.text.ITextInputListener#inputDocumentChanged(org.eclipse.jface.text.IDocument, org.eclipse.jface.text.IDocument)
0688: */
0689: public void inputDocumentChanged(IDocument oldInput,
0690: IDocument newInput) {
0691: }
0692: }
0693:
0694: /**
0695: * Internal property change listener for handling changes in the editor's preferences.
0696: */
0697: class PropertyChangeListener implements IPropertyChangeListener {
0698: /*
0699: * @see IPropertyChangeListener#propertyChange(org.eclipse.jface.util.PropertyChangeEvent)
0700: */
0701: public void propertyChange(PropertyChangeEvent event) {
0702: handlePreferenceStoreChanged(event);
0703: }
0704: }
0705:
0706: /**
0707: * Internal property change listener for handling workbench font changes.
0708: * @since 2.1
0709: */
0710: class FontPropertyChangeListener implements IPropertyChangeListener {
0711: /*
0712: * @see IPropertyChangeListener#propertyChange(org.eclipse.jface.util.PropertyChangeEvent)
0713: */
0714: public void propertyChange(PropertyChangeEvent event) {
0715: if (fSourceViewer == null)
0716: return;
0717:
0718: String property = event.getProperty();
0719:
0720: if (getFontPropertyPreferenceKey().equals(property)) {
0721: initializeViewerFont(fSourceViewer);
0722: updateCaret();
0723: }
0724: }
0725: }
0726:
0727: /**
0728: * Internal key verify listener for triggering action activation codes.
0729: */
0730: class ActivationCodeTrigger implements VerifyKeyListener {
0731:
0732: /** Indicates whether this trigger has been installed. */
0733: private boolean fIsInstalled = false;
0734: /**
0735: * The key binding service to use.
0736: * @since 2.0
0737: */
0738: private IKeyBindingService fKeyBindingService;
0739:
0740: /*
0741: * @see VerifyKeyListener#verifyKey(org.eclipse.swt.events.VerifyEvent)
0742: */
0743: public void verifyKey(VerifyEvent event) {
0744:
0745: ActionActivationCode code = null;
0746: int size = fActivationCodes.size();
0747: for (int i = 0; i < size; i++) {
0748: code = (ActionActivationCode) fActivationCodes.get(i);
0749: if (code.matches(event)) {
0750: IAction action = getAction(code.fActionId);
0751: if (action != null) {
0752:
0753: if (action instanceof IUpdate)
0754: ((IUpdate) action).update();
0755:
0756: if (!action.isEnabled()
0757: && action instanceof IReadOnlyDependent) {
0758: IReadOnlyDependent dependent = (IReadOnlyDependent) action;
0759: boolean writable = dependent
0760: .isEnabled(true);
0761: if (writable) {
0762: event.doit = false;
0763: return;
0764: }
0765: } else if (action.isEnabled()) {
0766: event.doit = false;
0767: action.run();
0768: return;
0769: }
0770: }
0771: }
0772: }
0773: }
0774:
0775: /**
0776: * Installs this trigger on the editor's text widget.
0777: * @since 2.0
0778: */
0779: public void install() {
0780: if (!fIsInstalled) {
0781:
0782: if (fSourceViewer instanceof ITextViewerExtension) {
0783: ITextViewerExtension e = (ITextViewerExtension) fSourceViewer;
0784: e.prependVerifyKeyListener(this );
0785: } else {
0786: StyledText text = fSourceViewer.getTextWidget();
0787: text.addVerifyKeyListener(this );
0788: }
0789:
0790: fKeyBindingService = getEditorSite()
0791: .getKeyBindingService();
0792: fIsInstalled = true;
0793: }
0794: }
0795:
0796: /**
0797: * Uninstalls this trigger from the editor's text widget.
0798: * @since 2.0
0799: */
0800: public void uninstall() {
0801: if (fIsInstalled) {
0802:
0803: if (fSourceViewer instanceof ITextViewerExtension) {
0804: ITextViewerExtension e = (ITextViewerExtension) fSourceViewer;
0805: e.removeVerifyKeyListener(this );
0806: } else if (fSourceViewer != null) {
0807: StyledText text = fSourceViewer.getTextWidget();
0808: if (text != null && !text.isDisposed())
0809: text
0810: .removeVerifyKeyListener(fActivationCodeTrigger);
0811: }
0812:
0813: fIsInstalled = false;
0814: fKeyBindingService = null;
0815: }
0816: }
0817:
0818: /**
0819: * Registers the given action for key activation.
0820: * @param action the action to be registered
0821: * @since 2.0
0822: */
0823: public void registerActionForKeyActivation(IAction action) {
0824: if (action.getActionDefinitionId() != null)
0825: fKeyBindingService.registerAction(action);
0826: }
0827:
0828: /**
0829: * The given action is no longer available for key activation
0830: * @param action the action to be unregistered
0831: * @since 2.0
0832: */
0833: public void unregisterActionFromKeyActivation(IAction action) {
0834: if (action.getActionDefinitionId() != null)
0835: fKeyBindingService.unregisterAction(action);
0836: }
0837:
0838: /**
0839: * Sets the key binding scopes for this editor.
0840: * @param keyBindingScopes the key binding scopes
0841: * @since 2.1
0842: */
0843: public void setScopes(String[] keyBindingScopes) {
0844: if (keyBindingScopes != null && keyBindingScopes.length > 0)
0845: fKeyBindingService.setScopes(keyBindingScopes);
0846: }
0847: }
0848:
0849: /**
0850: * Representation of action activation codes.
0851: */
0852: static class ActionActivationCode {
0853:
0854: /** The action id. */
0855: public String fActionId;
0856: /** The character. */
0857: public char fCharacter;
0858: /** The key code. */
0859: public int fKeyCode = -1;
0860: /** The state mask. */
0861: public int fStateMask = SWT.DEFAULT;
0862:
0863: /**
0864: * Creates a new action activation code for the given action id.
0865: * @param actionId the action id
0866: */
0867: public ActionActivationCode(String actionId) {
0868: fActionId = actionId;
0869: }
0870:
0871: /**
0872: * Returns <code>true</code> if this activation code matches the given verify event.
0873: * @param event the event to test for matching
0874: * @return whether this activation code matches <code>event</code>
0875: */
0876: public boolean matches(VerifyEvent event) {
0877: return (event.character == fCharacter
0878: && (fKeyCode == -1 || event.keyCode == fKeyCode) && (fStateMask == SWT.DEFAULT || event.stateMask == fStateMask));
0879: }
0880: }
0881:
0882: /**
0883: * Internal part and shell activation listener for triggering state validation.
0884: * @since 2.0
0885: */
0886: class ActivationListener implements IPartListener, IWindowListener {
0887:
0888: /** Cache of the active workbench part. */
0889: private IWorkbenchPart fActivePart;
0890: /** Indicates whether activation handling is currently be done. */
0891: private boolean fIsHandlingActivation = false;
0892: /**
0893: * The part service.
0894: * @since 3.1
0895: */
0896: private IPartService fPartService;
0897:
0898: /**
0899: * Creates this activation listener.
0900: *
0901: * @param partService the part service on which to add the part listener
0902: * @since 3.1
0903: */
0904: public ActivationListener(IPartService partService) {
0905: fPartService = partService;
0906: fPartService.addPartListener(this );
0907: PlatformUI.getWorkbench().addWindowListener(this );
0908: }
0909:
0910: /**
0911: * Disposes this activation listener.
0912: *
0913: * @since 3.1
0914: */
0915: public void dispose() {
0916: fPartService.removePartListener(this );
0917: PlatformUI.getWorkbench().removeWindowListener(this );
0918: fPartService = null;
0919: }
0920:
0921: /*
0922: * @see IPartListener#partActivated(org.eclipse.ui.IWorkbenchPart)
0923: */
0924: public void partActivated(IWorkbenchPart part) {
0925: fActivePart = part;
0926: handleActivation();
0927: }
0928:
0929: /*
0930: * @see IPartListener#partBroughtToTop(org.eclipse.ui.IWorkbenchPart)
0931: */
0932: public void partBroughtToTop(IWorkbenchPart part) {
0933: }
0934:
0935: /*
0936: * @see IPartListener#partClosed(org.eclipse.ui.IWorkbenchPart)
0937: */
0938: public void partClosed(IWorkbenchPart part) {
0939: }
0940:
0941: /*
0942: * @see IPartListener#partDeactivated(org.eclipse.ui.IWorkbenchPart)
0943: */
0944: public void partDeactivated(IWorkbenchPart part) {
0945: fActivePart = null;
0946: }
0947:
0948: /*
0949: * @see IPartListener#partOpened(org.eclipse.ui.IWorkbenchPart)
0950: */
0951: public void partOpened(IWorkbenchPart part) {
0952: // Restore the saved state if any
0953: if (part == AbstractTextEditor.this
0954: && fMementoToRestore != null
0955: && containsSavedState(fMementoToRestore))
0956: doRestoreState(fMementoToRestore);
0957: fMementoToRestore = null;
0958: }
0959:
0960: /**
0961: * Handles the activation triggering a element state check in the editor.
0962: */
0963: private void handleActivation() {
0964: if (fIsHandlingActivation)
0965: return;
0966:
0967: if (fActivePart == AbstractTextEditor.this ) {
0968: fIsHandlingActivation = true;
0969: try {
0970: safelySanityCheckState(getEditorInput());
0971: } finally {
0972: fIsHandlingActivation = false;
0973: }
0974: }
0975: }
0976:
0977: /*
0978: * @see org.eclipse.ui.IWindowListener#windowActivated(org.eclipse.ui.IWorkbenchWindow)
0979: * @since 3.1
0980: */
0981: public void windowActivated(IWorkbenchWindow window) {
0982: if (window == getEditorSite().getWorkbenchWindow()) {
0983: /*
0984: * Workaround for problem described in
0985: * http://dev.eclipse.org/bugs/show_bug.cgi?id=11731
0986: * Will be removed when SWT has solved the problem.
0987: */
0988: window.getShell().getDisplay().asyncExec(
0989: new Runnable() {
0990: public void run() {
0991: handleActivation();
0992: }
0993: });
0994: }
0995: }
0996:
0997: /*
0998: * @see org.eclipse.ui.IWindowListener#windowDeactivated(org.eclipse.ui.IWorkbenchWindow)
0999: * @since 3.1
1000: */
1001: public void windowDeactivated(IWorkbenchWindow window) {
1002: }
1003:
1004: /*
1005: * @see org.eclipse.ui.IWindowListener#windowClosed(org.eclipse.ui.IWorkbenchWindow)
1006: * @since 3.1
1007: */
1008: public void windowClosed(IWorkbenchWindow window) {
1009: }
1010:
1011: /*
1012: * @see org.eclipse.ui.IWindowListener#windowOpened(org.eclipse.ui.IWorkbenchWindow)
1013: * @since 3.1
1014: */
1015: public void windowOpened(IWorkbenchWindow window) {
1016: }
1017: }
1018:
1019: /**
1020: * Internal interface for a cursor listener. I.e. aggregation
1021: * of mouse and key listener.
1022: * @since 2.0
1023: */
1024: interface ICursorListener extends MouseListener, KeyListener {
1025: }
1026:
1027: /**
1028: * Maps an action definition id to an StyledText action.
1029: * @since 2.0
1030: */
1031: protected static final class IdMapEntry {
1032:
1033: /** The action id. */
1034: private String fActionId;
1035: /** The StyledText action. */
1036: private int fAction;
1037:
1038: /**
1039: * Creates a new mapping.
1040: * @param actionId the action id
1041: * @param action the StyledText action
1042: */
1043: public IdMapEntry(String actionId, int action) {
1044: fActionId = actionId;
1045: fAction = action;
1046: }
1047:
1048: /**
1049: * Returns the action id.
1050: * @return the action id
1051: */
1052: public String getActionId() {
1053: return fActionId;
1054: }
1055:
1056: /**
1057: * Returns the action.
1058: * @return the action
1059: */
1060: public int getAction() {
1061: return fAction;
1062: }
1063: }
1064:
1065: /**
1066: * Internal action to scroll the editor's viewer by a specified number of lines.
1067: * @since 2.0
1068: */
1069: class ScrollLinesAction extends Action {
1070:
1071: /** Number of lines to scroll. */
1072: private int fScrollIncrement;
1073:
1074: /**
1075: * Creates a new scroll action that scroll the given number of lines. If the
1076: * increment is < 0, it's scrolling up, if > 0 it's scrolling down.
1077: * @param scrollIncrement the number of lines to scroll
1078: */
1079: public ScrollLinesAction(int scrollIncrement) {
1080: fScrollIncrement = scrollIncrement;
1081: }
1082:
1083: /*
1084: * @see IAction#run()
1085: */
1086: public void run() {
1087: if (fSourceViewer instanceof ITextViewerExtension5) {
1088: ITextViewerExtension5 extension = (ITextViewerExtension5) fSourceViewer;
1089: StyledText textWidget = fSourceViewer.getTextWidget();
1090: int topIndex = textWidget.getTopIndex();
1091: int newTopIndex = Math.max(0, topIndex
1092: + fScrollIncrement);
1093: fSourceViewer.setTopIndex(extension
1094: .widgetLine2ModelLine(newTopIndex));
1095: } else {
1096: int topIndex = fSourceViewer.getTopIndex();
1097: int newTopIndex = Math.max(0, topIndex
1098: + fScrollIncrement);
1099: fSourceViewer.setTopIndex(newTopIndex);
1100: }
1101: }
1102: }
1103:
1104: /**
1105: * Action to toggle the insert mode. The action is checked if smart mode is
1106: * turned on.
1107: *
1108: * @since 2.1
1109: */
1110: class ToggleInsertModeAction extends ResourceAction {
1111:
1112: public ToggleInsertModeAction(ResourceBundle bundle,
1113: String prefix) {
1114: super (bundle, prefix, IAction.AS_CHECK_BOX);
1115: }
1116:
1117: /*
1118: * @see org.eclipse.jface.action.IAction#run()
1119: */
1120: public void run() {
1121: switchToNextInsertMode();
1122: }
1123:
1124: /*
1125: * @see org.eclipse.jface.action.IAction#isChecked()
1126: * @since 3.0
1127: */
1128: public boolean isChecked() {
1129: return fInsertMode == SMART_INSERT;
1130: }
1131: }
1132:
1133: /**
1134: * Action to toggle the overwrite mode.
1135: *
1136: * @since 3.0
1137: */
1138: class ToggleOverwriteModeAction extends ResourceAction {
1139:
1140: public ToggleOverwriteModeAction(ResourceBundle bundle,
1141: String prefix) {
1142: super (bundle, prefix);
1143: }
1144:
1145: /*
1146: * @see org.eclipse.jface.action.IAction#run()
1147: */
1148: public void run() {
1149: toggleOverwriteMode();
1150: }
1151: }
1152:
1153: /**
1154: * This action implements smart end.
1155: * Instead of going to the end of a line it does the following:
1156: * - 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
1157: * - if the caret is after last non-whitespace the caret is moved at the end of the line
1158: * - if the caret is at the end of the line the caret is moved directly after the line's last non-whitespace character
1159: * @since 2.1 (in 3.3 the access modifier changed from package visibility to protected)
1160: */
1161: protected class LineEndAction extends TextNavigationAction {
1162:
1163: /** boolean flag which tells if the text up to the line end should be selected. */
1164: private boolean fDoSelect;
1165:
1166: /**
1167: * Create a new line end action.
1168: *
1169: * @param textWidget the styled text widget
1170: * @param doSelect a boolean flag which tells if the text up to the line end should be selected
1171: */
1172: public LineEndAction(StyledText textWidget, boolean doSelect) {
1173: super (textWidget, ST.LINE_END);
1174: fDoSelect = doSelect;
1175: }
1176:
1177: /**
1178: * Computes the offset of the line end position.
1179: *
1180: * @param document the document where to compute the line end position
1181: * @param line the line to determine the end position of
1182: * @param length the length of the line
1183: * @param offset the caret position in the document
1184: * @return the offset of the line end
1185: * @since 3.3, protected since 3.4
1186: */
1187: protected int getLineEndPosition(final IDocument document,
1188: final String line, final int length, final int offset) {
1189: int index = length - 1;
1190: while (index > -1
1191: && Character.isWhitespace(line.charAt(index)))
1192: index--;
1193: index++;
1194:
1195: LinkedModeModel model = LinkedModeModel.getModel(document,
1196: offset);
1197: if (model != null) {
1198: LinkedPosition linkedPosition = model
1199: .findPosition(new LinkedPosition(document,
1200: offset, 0));
1201: if (linkedPosition != null) {
1202: int linkedPositionEnd = linkedPosition.getOffset()
1203: + linkedPosition.getLength();
1204: int lineOffset;
1205: try {
1206: lineOffset = document
1207: .getLineInformationOfOffset(offset)
1208: .getOffset();
1209: if (offset != linkedPositionEnd
1210: && linkedPositionEnd - lineOffset < index)
1211: index = linkedPositionEnd - lineOffset;
1212: } catch (BadLocationException e) {
1213: //should not happen
1214: }
1215: }
1216: }
1217: return index;
1218: }
1219:
1220: /*
1221: * @see org.eclipse.jface.action.IAction#run()
1222: */
1223: public void run() {
1224: boolean isSmartHomeEndEnabled = false;
1225: IPreferenceStore store = getPreferenceStore();
1226: if (store != null)
1227: isSmartHomeEndEnabled = store
1228: .getBoolean(AbstractTextEditor.PREFERENCE_NAVIGATION_SMART_HOME_END);
1229:
1230: StyledText st = fSourceViewer.getTextWidget();
1231: if (st == null || st.isDisposed())
1232: return;
1233: int caretOffset = st.getCaretOffset();
1234: int lineNumber = st.getLineAtOffset(caretOffset);
1235: int lineOffset = st.getOffsetAtLine(lineNumber);
1236:
1237: int lineLength;
1238: int caretOffsetInDocument;
1239: final IDocument document = fSourceViewer.getDocument();
1240:
1241: try {
1242: caretOffsetInDocument = widgetOffset2ModelOffset(
1243: fSourceViewer, caretOffset);
1244: lineLength = document.getLineInformationOfOffset(
1245: caretOffsetInDocument).getLength();
1246: } catch (BadLocationException ex) {
1247: return;
1248: }
1249: int lineEndOffset = lineOffset + lineLength;
1250:
1251: int delta = lineEndOffset - st.getCharCount();
1252: if (delta > 0) {
1253: lineEndOffset -= delta;
1254: lineLength -= delta;
1255: }
1256:
1257: String line = ""; //$NON-NLS-1$
1258: if (lineLength > 0)
1259: line = st.getText(lineOffset, lineEndOffset - 1);
1260:
1261: // Remember current selection
1262: Point oldSelection = st.getSelection();
1263:
1264: // The new caret position
1265: int newCaretOffset = -1;
1266:
1267: if (isSmartHomeEndEnabled) {
1268: // Compute the line end offset
1269: int i = getLineEndPosition(document, line, lineLength,
1270: caretOffsetInDocument);
1271:
1272: if (caretOffset - lineOffset == i)
1273: // to end of line
1274: newCaretOffset = lineEndOffset;
1275: else
1276: // to end of text
1277: newCaretOffset = lineOffset + i;
1278:
1279: } else {
1280:
1281: if (caretOffset < lineEndOffset)
1282: // to end of line
1283: newCaretOffset = lineEndOffset;
1284:
1285: }
1286:
1287: if (newCaretOffset == -1)
1288: newCaretOffset = caretOffset;
1289: else
1290: st.setCaretOffset(newCaretOffset);
1291:
1292: st.setCaretOffset(newCaretOffset);
1293: if (fDoSelect) {
1294: if (caretOffset < oldSelection.y)
1295: st.setSelection(oldSelection.y, newCaretOffset);
1296: else
1297: st.setSelection(oldSelection.x, newCaretOffset);
1298: } else
1299: st.setSelection(newCaretOffset);
1300:
1301: fireSelectionChanged(oldSelection);
1302: }
1303: }
1304:
1305: /**
1306: * This action implements smart home.
1307: * Instead of going to the start of a line it does the following:
1308: * - 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
1309: * - if the caret is before the line's first non-whitespace the caret is moved to the beginning of the line
1310: * - if the caret is at the beginning of the line the caret is moved directly before the line's first non-whitespace character
1311: * @since 2.1
1312: */
1313: protected class LineStartAction extends TextNavigationAction {
1314:
1315: /** boolean flag which tells if the text up to the beginning of the line should be selected. */
1316: private final boolean fDoSelect;
1317:
1318: /**
1319: * Creates a new line start action.
1320: *
1321: * @param textWidget the styled text widget
1322: * @param doSelect a boolean flag which tells if the text up to the beginning of the line should be selected
1323: */
1324: public LineStartAction(final StyledText textWidget,
1325: final boolean doSelect) {
1326: super (textWidget, ST.LINE_START);
1327: fDoSelect = doSelect;
1328: }
1329:
1330: /**
1331: * Computes the offset of the line start position.
1332: *
1333: * @param document the document where to compute the line start position
1334: * @param line the line to determine the start position of
1335: * @param length the length of the line
1336: * @param offset the caret position in the document
1337: * @return the offset of the line start
1338: * @since 3.0
1339: */
1340: protected int getLineStartPosition(final IDocument document,
1341: final String line, final int length, final int offset) {
1342: int index = 0;
1343: while (index < length
1344: && Character.isWhitespace(line.charAt(index)))
1345: index++;
1346:
1347: LinkedModeModel model = LinkedModeModel.getModel(document,
1348: offset);
1349: if (model != null) {
1350: LinkedPosition linkedPosition = model
1351: .findPosition(new LinkedPosition(document,
1352: offset, 0));
1353: if (linkedPosition != null) {
1354: int linkedPositionOffset = linkedPosition
1355: .getOffset();
1356: int lineOffset;
1357: try {
1358: lineOffset = document
1359: .getLineInformationOfOffset(offset)
1360: .getOffset();
1361: if (offset != linkedPositionOffset
1362: && index < linkedPositionOffset
1363: - lineOffset)
1364: index = linkedPositionOffset - lineOffset;
1365: } catch (BadLocationException e) {
1366: //should not happen
1367: }
1368: }
1369: }
1370: return index;
1371: }
1372:
1373: /*
1374: * @see org.eclipse.jface.action.IAction#run()
1375: */
1376: public void run() {
1377: boolean isSmartHomeEndEnabled = false;
1378: IPreferenceStore store = getPreferenceStore();
1379: if (store != null)
1380: isSmartHomeEndEnabled = store
1381: .getBoolean(AbstractTextEditor.PREFERENCE_NAVIGATION_SMART_HOME_END);
1382:
1383: StyledText st = fSourceViewer.getTextWidget();
1384: if (st == null || st.isDisposed())
1385: return;
1386:
1387: int caretOffset = st.getCaretOffset();
1388: int lineNumber = st.getLineAtOffset(caretOffset);
1389: int lineOffset = st.getOffsetAtLine(lineNumber);
1390:
1391: int lineLength;
1392: int caretOffsetInDocument;
1393: final IDocument document = fSourceViewer.getDocument();
1394:
1395: try {
1396: caretOffsetInDocument = widgetOffset2ModelOffset(
1397: fSourceViewer, caretOffset);
1398: lineLength = document.getLineInformationOfOffset(
1399: caretOffsetInDocument).getLength();
1400: } catch (BadLocationException ex) {
1401: return;
1402: }
1403:
1404: String line = ""; //$NON-NLS-1$
1405: if (lineLength > 0) {
1406: int end = lineOffset + lineLength - 1;
1407: end = Math.min(end, st.getCharCount() - 1);
1408: line = st.getText(lineOffset, end);
1409: }
1410:
1411: // Remember current selection
1412: Point oldSelection = st.getSelection();
1413:
1414: // The new caret position
1415: int newCaretOffset = -1;
1416:
1417: if (isSmartHomeEndEnabled) {
1418:
1419: // Compute the line start offset
1420: int index = getLineStartPosition(document, line,
1421: lineLength, caretOffsetInDocument);
1422:
1423: if (caretOffset - lineOffset == index)
1424: // to beginning of line
1425: newCaretOffset = lineOffset;
1426: else
1427: // to beginning of text
1428: newCaretOffset = lineOffset + index;
1429:
1430: } else {
1431:
1432: if (caretOffset > lineOffset)
1433: // to beginning of line
1434: newCaretOffset = lineOffset;
1435: }
1436:
1437: if (newCaretOffset == -1)
1438: newCaretOffset = caretOffset;
1439: else
1440: st.setCaretOffset(newCaretOffset);
1441:
1442: if (fDoSelect) {
1443: if (caretOffset < oldSelection.y)
1444: st.setSelection(oldSelection.y, newCaretOffset);
1445: else
1446: st.setSelection(oldSelection.x, newCaretOffset);
1447: } else
1448: st.setSelection(newCaretOffset);
1449:
1450: fireSelectionChanged(oldSelection);
1451: }
1452:
1453: }
1454:
1455: /**
1456: * Internal action to show the editor's ruler context menu (accessibility).
1457: * @since 2.0
1458: */
1459: class ShowRulerContextMenuAction extends Action {
1460: /*
1461: * @see IAction#run()
1462: */
1463: public void run() {
1464: if (fSourceViewer == null)
1465: return;
1466:
1467: StyledText text = fSourceViewer.getTextWidget();
1468: if (text == null || text.isDisposed())
1469: return;
1470:
1471: Point location = text.getLocationAtOffset(text
1472: .getCaretOffset());
1473: location.x = 0;
1474:
1475: if (fVerticalRuler instanceof IVerticalRulerExtension)
1476: ((IVerticalRulerExtension) fVerticalRuler)
1477: .setLocationOfLastMouseButtonActivity(
1478: location.x, location.y);
1479:
1480: location = text.toDisplay(location);
1481: fRulerContextMenu.setLocation(location.x, location.y);
1482: fRulerContextMenu.setVisible(true);
1483: }
1484: }
1485:
1486: /**
1487: * Editor specific selection provider which wraps the source viewer's selection provider.
1488: * @since 2.1
1489: */
1490: class SelectionProvider implements IPostSelectionProvider,
1491: ISelectionValidator {
1492:
1493: /*
1494: * @see org.eclipse.jface.viewers.ISelectionProvider#addSelectionChangedListener(ISelectionChangedListener)
1495: */
1496: public void addSelectionChangedListener(
1497: ISelectionChangedListener listener) {
1498: if (fSourceViewer != null)
1499: fSourceViewer.getSelectionProvider()
1500: .addSelectionChangedListener(listener);
1501: }
1502:
1503: /*
1504: * @see org.eclipse.jface.viewers.ISelectionProvider#getSelection()
1505: */
1506: public ISelection getSelection() {
1507: return doGetSelection();
1508: }
1509:
1510: /*
1511: * @see org.eclipse.jface.viewers.ISelectionProvider#removeSelectionChangedListener(ISelectionChangedListener)
1512: */
1513: public void removeSelectionChangedListener(
1514: ISelectionChangedListener listener) {
1515: if (fSourceViewer != null)
1516: fSourceViewer.getSelectionProvider()
1517: .removeSelectionChangedListener(listener);
1518: }
1519:
1520: /*
1521: * @see org.eclipse.jface.viewers.ISelectionProvider#setSelection(ISelection)
1522: */
1523: public void setSelection(ISelection selection) {
1524: doSetSelection(selection);
1525: }
1526:
1527: /*
1528: * @see org.eclipse.jface.text.IPostSelectionProvider#addPostSelectionChangedListener(org.eclipse.jface.viewers.ISelectionChangedListener)
1529: * @since 3.0
1530: */
1531: public void addPostSelectionChangedListener(
1532: ISelectionChangedListener listener) {
1533: if (fSourceViewer != null) {
1534: if (fSourceViewer.getSelectionProvider() instanceof IPostSelectionProvider) {
1535: IPostSelectionProvider provider = (IPostSelectionProvider) fSourceViewer
1536: .getSelectionProvider();
1537: provider.addPostSelectionChangedListener(listener);
1538: }
1539: }
1540: }
1541:
1542: /*
1543: * @see org.eclipse.jface.text.IPostSelectionProvider#removePostSelectionChangedListener(org.eclipse.jface.viewers.ISelectionChangedListener)
1544: * @since 3.0
1545: */
1546: public void removePostSelectionChangedListener(
1547: ISelectionChangedListener listener) {
1548: if (fSourceViewer != null) {
1549: if (fSourceViewer.getSelectionProvider() instanceof IPostSelectionProvider) {
1550: IPostSelectionProvider provider = (IPostSelectionProvider) fSourceViewer
1551: .getSelectionProvider();
1552: provider
1553: .removePostSelectionChangedListener(listener);
1554: }
1555: }
1556: }
1557:
1558: /*
1559: * @see org.eclipse.jface.text.IPostSelectionValidator#isValid()
1560: * @since 3.0
1561: */
1562: public boolean isValid(ISelection postSelection) {
1563: return fSelectionListener != null
1564: && fSelectionListener.isValid(postSelection);
1565: }
1566: }
1567:
1568: /**
1569: * Internal implementation class for a change listener.
1570: * @since 3.0
1571: */
1572: protected abstract class AbstractSelectionChangedListener implements
1573: ISelectionChangedListener {
1574:
1575: /**
1576: * Installs this selection changed listener with the given selection provider. If
1577: * the selection provider is a post selection provider, post selection changed
1578: * events are the preferred choice, otherwise normal selection changed events
1579: * are requested.
1580: *
1581: * @param selectionProvider
1582: */
1583: public void install(ISelectionProvider selectionProvider) {
1584: if (selectionProvider == null)
1585: return;
1586:
1587: if (selectionProvider instanceof IPostSelectionProvider) {
1588: IPostSelectionProvider provider = (IPostSelectionProvider) selectionProvider;
1589: provider.addPostSelectionChangedListener(this );
1590: } else {
1591: selectionProvider.addSelectionChangedListener(this );
1592: }
1593: }
1594:
1595: /**
1596: * Removes this selection changed listener from the given selection provider.
1597: *
1598: * @param selectionProvider the selection provider
1599: */
1600: public void uninstall(ISelectionProvider selectionProvider) {
1601: if (selectionProvider == null)
1602: return;
1603:
1604: if (selectionProvider instanceof IPostSelectionProvider) {
1605: IPostSelectionProvider provider = (IPostSelectionProvider) selectionProvider;
1606: provider.removePostSelectionChangedListener(this );
1607: } else {
1608: selectionProvider.removeSelectionChangedListener(this );
1609: }
1610: }
1611: }
1612:
1613: /**
1614: * This selection listener allows the SelectionProvider to implement {@link ISelectionValidator}.
1615: *
1616: * @since 3.0
1617: */
1618: private class SelectionListener extends
1619: AbstractSelectionChangedListener implements
1620: IDocumentListener {
1621:
1622: private IDocument fDocument;
1623: private final Object INVALID_SELECTION = new Object();
1624: private Object fPostSelection = INVALID_SELECTION;
1625:
1626: /*
1627: * @see org.eclipse.jface.viewers.ISelectionChangedListener#selectionChanged(org.eclipse.jface.viewers.SelectionChangedEvent)
1628: */
1629: public synchronized void selectionChanged(
1630: SelectionChangedEvent event) {
1631: fPostSelection = event.getSelection();
1632: }
1633:
1634: /*
1635: * @see org.eclipse.jface.text.IDocumentListener#documentAboutToBeChanged(org.eclipse.jface.text.DocumentEvent)
1636: * @since 3.0
1637: */
1638: public synchronized void documentAboutToBeChanged(
1639: DocumentEvent event) {
1640: fPostSelection = INVALID_SELECTION;
1641: }
1642:
1643: /*
1644: * @see org.eclipse.jface.text.IDocumentListener#documentChanged(org.eclipse.jface.text.DocumentEvent)
1645: * @since 3.0
1646: */
1647: public void documentChanged(DocumentEvent event) {
1648: }
1649:
1650: public synchronized boolean isValid(ISelection selection) {
1651: return fPostSelection != INVALID_SELECTION
1652: && fPostSelection == selection;
1653: }
1654:
1655: public void setDocument(IDocument document) {
1656: if (fDocument != null)
1657: fDocument.removeDocumentListener(this );
1658:
1659: fDocument = document;
1660: if (fDocument != null)
1661: fDocument.addDocumentListener(this );
1662: }
1663:
1664: /*
1665: * @see org.eclipse.ui.texteditor.AbstractTextEditor.AbstractSelectionChangedListener#install(org.eclipse.jface.viewers.ISelectionProvider)
1666: * @since 3.0
1667: */
1668: public void install(ISelectionProvider selectionProvider) {
1669: super .install(selectionProvider);
1670:
1671: if (selectionProvider != null)
1672: selectionProvider.addSelectionChangedListener(this );
1673: }
1674:
1675: /*
1676: * @see org.eclipse.ui.texteditor.AbstractTextEditor.AbstractSelectionChangedListener#uninstall(org.eclipse.jface.viewers.ISelectionProvider)
1677: * @since 3.0
1678: */
1679: public void uninstall(ISelectionProvider selectionProvider) {
1680: if (selectionProvider != null)
1681: selectionProvider.removeSelectionChangedListener(this );
1682:
1683: if (fDocument != null) {
1684: fDocument.removeDocumentListener(this );
1685: fDocument = null;
1686: }
1687: super .uninstall(selectionProvider);
1688: }
1689: }
1690:
1691: /**
1692: * Implements the ruler column support of for the given editor.
1693: * <p>
1694: * This is currently only used to support vertical ruler columns.
1695: * </p>
1696: *
1697: * @since 3.3
1698: */
1699: protected static class ColumnSupport implements IColumnSupport {
1700: private final AbstractTextEditor fEditor;
1701: private final RulerColumnRegistry fRegistry;
1702: private final List fColumns;
1703:
1704: /**
1705: * Creates a new column support for the given editor. Only the editor itself should normally
1706: * create such an instance.
1707: *
1708: * @param editor the editor
1709: * @param registry the contribution registry to refer to
1710: */
1711: public ColumnSupport(AbstractTextEditor editor,
1712: RulerColumnRegistry registry) {
1713: Assert.isLegal(editor != null);
1714: Assert.isLegal(registry != null);
1715: fEditor = editor;
1716: fRegistry = registry;
1717: fColumns = new ArrayList();
1718: }
1719:
1720: /*
1721: * @see org.eclipse.ui.texteditor.IColumnSupport#setColumnVisible(java.lang.String, boolean)
1722: */
1723: public final void setColumnVisible(
1724: RulerColumnDescriptor descriptor, boolean visible) {
1725: Assert.isLegal(descriptor != null);
1726:
1727: final CompositeRuler ruler = getRuler();
1728: if (ruler == null)
1729: return;
1730:
1731: if (!isColumnSupported(descriptor))
1732: visible = false;
1733:
1734: if (isColumnVisible(descriptor)) {
1735: if (!visible)
1736: removeColumn(ruler, descriptor);
1737: } else {
1738: if (visible)
1739: addColumn(ruler, descriptor);
1740: }
1741: }
1742:
1743: private void addColumn(final CompositeRuler ruler,
1744: final RulerColumnDescriptor descriptor) {
1745:
1746: final int idx = computeIndex(ruler, descriptor);
1747:
1748: SafeRunnable runnable = new SafeRunnable() {
1749: public void run() throws Exception {
1750: IContributedRulerColumn column = descriptor
1751: .createColumn(fEditor);
1752: fColumns.add(column);
1753: initializeColumn(column);
1754: ruler.addDecorator(idx, column);
1755: }
1756: };
1757: SafeRunner.run(runnable);
1758: }
1759:
1760: /**
1761: * Hook to let subclasses initialize a newly created column.
1762: * <p>
1763: * Subclasses may extend this method.</p>
1764: *
1765: * @param column the created column
1766: */
1767: protected void initializeColumn(IContributedRulerColumn column) {
1768: }
1769:
1770: private void removeColumn(final CompositeRuler ruler,
1771: final RulerColumnDescriptor descriptor) {
1772: removeColumn(ruler, getVisibleColumn(ruler, descriptor));
1773: }
1774:
1775: private void removeColumn(final CompositeRuler ruler,
1776: final IContributedRulerColumn rulerColumn) {
1777: if (rulerColumn != null) {
1778: SafeRunnable runnable = new SafeRunnable() {
1779: public void run() throws Exception {
1780: if (ruler != null)
1781: ruler.removeDecorator(rulerColumn);
1782: rulerColumn.columnRemoved();
1783: }
1784: };
1785: SafeRunner.run(runnable);
1786: }
1787: }
1788:
1789: /**
1790: * Returns the currently visible column matching <code>id</code>, <code>null</code> if
1791: * none.
1792: *
1793: * @param ruler the composite ruler to scan
1794: * @param descriptor the descriptor of the column of interest
1795: * @return the matching column or <code>null</code>
1796: */
1797: private IContributedRulerColumn getVisibleColumn(
1798: CompositeRuler ruler, RulerColumnDescriptor descriptor) {
1799: for (Iterator it = ruler.getDecoratorIterator(); it
1800: .hasNext();) {
1801: IVerticalRulerColumn column = (IVerticalRulerColumn) it
1802: .next();
1803: if (column instanceof IContributedRulerColumn) {
1804: IContributedRulerColumn rulerColumn = (IContributedRulerColumn) column;
1805: RulerColumnDescriptor rcd = rulerColumn
1806: .getDescriptor();
1807: if (descriptor.equals(rcd))
1808: return rulerColumn;
1809: }
1810: }
1811: return null;
1812: }
1813:
1814: /**
1815: * Computes the insertion index for a column contribution into the currently visible columns.
1816: *
1817: * @param ruler the composite ruler into which to insert the column
1818: * @param descriptor the descriptor to compute the index for
1819: * @return the insertion index for a new column
1820: */
1821: private int computeIndex(CompositeRuler ruler,
1822: RulerColumnDescriptor descriptor) {
1823: int index = 0;
1824: List all = fRegistry.getColumnDescriptors();
1825: int newPos = all.indexOf(descriptor);
1826: for (Iterator it = ruler.getDecoratorIterator(); it
1827: .hasNext();) {
1828: IVerticalRulerColumn column = (IVerticalRulerColumn) it
1829: .next();
1830: if (column instanceof IContributedRulerColumn) {
1831: RulerColumnDescriptor rcd = ((IContributedRulerColumn) column)
1832: .getDescriptor();
1833: if (rcd != null && all.indexOf(rcd) > newPos)
1834: break;
1835: } else if ("org.eclipse.jface.text.source.projection.ProjectionRulerColumn".equals(column.getClass().getName())) { //$NON-NLS-1$
1836: // projection column is always the rightmost column
1837: break;
1838: }
1839: index++;
1840: }
1841: return index;
1842: }
1843:
1844: /*
1845: * @see org.eclipse.ui.texteditor.IColumnSupport#isColumnVisible(java.lang.String)
1846: */
1847: public final boolean isColumnVisible(
1848: RulerColumnDescriptor descriptor) {
1849: Assert.isLegal(descriptor != null);
1850: CompositeRuler ruler = getRuler();
1851: return ruler != null
1852: && getVisibleColumn(ruler, descriptor) != null;
1853: }
1854:
1855: /*
1856: * @see org.eclipse.ui.texteditor.IColumnSupport#isColumnSupported(java.lang.String)
1857: */
1858: public final boolean isColumnSupported(
1859: RulerColumnDescriptor descriptor) {
1860: Assert.isLegal(descriptor != null);
1861: if (getRuler() == null)
1862: return false;
1863:
1864: if (descriptor == null)
1865: return false;
1866:
1867: return descriptor.matchesEditor(fEditor);
1868: }
1869:
1870: /**
1871: * Returns the editor's vertical ruler, if it is a {@link CompositeRuler}, <code>null</code>
1872: * otherwise.
1873: *
1874: * @return the editor's {@link CompositeRuler} or <code>null</code>
1875: */
1876: private CompositeRuler getRuler() {
1877: Object ruler = fEditor.getAdapter(IVerticalRulerInfo.class);
1878: if (ruler instanceof CompositeRuler)
1879: return (CompositeRuler) ruler;
1880: return null;
1881: }
1882:
1883: /**
1884: * {@inheritDoc}
1885: * <p>
1886: * Subclasses may extend this method.</p>
1887: *
1888: */
1889: public void dispose() {
1890: for (Iterator iter = new ArrayList(fColumns).iterator(); iter
1891: .hasNext();)
1892: removeColumn(getRuler(), (IContributedRulerColumn) iter
1893: .next());
1894: fColumns.clear();
1895: }
1896: }
1897:
1898: /**
1899: * Information provider used to present focusable information shells.
1900: *
1901: * @since 3.3
1902: */
1903: private static final class InformationProvider implements
1904: IInformationProvider, IInformationProviderExtension,
1905: IInformationProviderExtension2 {
1906:
1907: private IRegion fHoverRegion;
1908: private Object fHoverInfo;
1909: private IInformationControlCreator fControlCreator;
1910:
1911: InformationProvider(IRegion hoverRegion, Object hoverInfo,
1912: IInformationControlCreator controlCreator) {
1913: fHoverRegion = hoverRegion;
1914: fHoverInfo = hoverInfo;
1915: fControlCreator = controlCreator;
1916: }
1917:
1918: /*
1919: * @see org.eclipse.jface.text.information.IInformationProvider#getSubject(org.eclipse.jface.text.ITextViewer, int)
1920: */
1921: public IRegion getSubject(ITextViewer textViewer,
1922: int invocationOffset) {
1923: return fHoverRegion;
1924: }
1925:
1926: /*
1927: * @see org.eclipse.jface.text.information.IInformationProvider#getInformation(org.eclipse.jface.text.ITextViewer, org.eclipse.jface.text.IRegion)
1928: */
1929: public String getInformation(ITextViewer textViewer,
1930: IRegion subject) {
1931: return fHoverInfo.toString();
1932: }
1933:
1934: /*
1935: * @see org.eclipse.jface.text.information.IInformationProviderExtension#getInformation2(org.eclipse.jface.text.ITextViewer, org.eclipse.jface.text.IRegion)
1936: * @since 3.2
1937: */
1938: public Object getInformation2(ITextViewer textViewer,
1939: IRegion subject) {
1940: return fHoverInfo;
1941: }
1942:
1943: /*
1944: * @see org.eclipse.jface.text.information.IInformationProviderExtension2#getInformationPresenterControlCreator()
1945: */
1946: public IInformationControlCreator getInformationPresenterControlCreator() {
1947: return fControlCreator;
1948: }
1949: }
1950:
1951: /**
1952: * This action behaves in two different ways: If there is no current text
1953: * hover, the javadoc is displayed using information presenter. If there is
1954: * a current text hover, it is converted into a information presenter in
1955: * order to make it sticky.
1956: *
1957: * @since 3.3
1958: */
1959: private final class InformationDispatchAction extends
1960: TextEditorAction {
1961:
1962: /** The wrapped text operation action. */
1963: private final TextOperationAction fTextOperationAction;
1964:
1965: /**
1966: * Creates a dispatch action.
1967: *
1968: * @param resourceBundle the resource bundle
1969: * @param prefix the prefix
1970: * @param textOperationAction the text operation action
1971: */
1972: public InformationDispatchAction(ResourceBundle resourceBundle,
1973: String prefix,
1974: final TextOperationAction textOperationAction) {
1975: super (resourceBundle, prefix, AbstractTextEditor.this );
1976: if (textOperationAction == null)
1977: throw new IllegalArgumentException();
1978: fTextOperationAction = textOperationAction;
1979: }
1980:
1981: /*
1982: * @see org.eclipse.jface.action.IAction#run()
1983: */
1984: public void run() {
1985:
1986: ISourceViewer sourceViewer = getSourceViewer();
1987: if (sourceViewer == null) {
1988: if (fTextOperationAction.isEnabled())
1989: fTextOperationAction.run();
1990: return;
1991: }
1992:
1993: if (sourceViewer instanceof ITextViewerExtension4) {
1994: ITextViewerExtension4 extension4 = (ITextViewerExtension4) sourceViewer;
1995: if (extension4.moveFocusToWidgetToken())
1996: return;
1997: }
1998:
1999: if (sourceViewer instanceof ITextViewerExtension2) {
2000: // does a text hover exist?
2001: ITextHover textHover = ((ITextViewerExtension2) sourceViewer)
2002: .getCurrentTextHover();
2003: if (textHover != null
2004: && makeTextHoverFocusable(sourceViewer,
2005: textHover))
2006: return;
2007: }
2008:
2009: if (sourceViewer instanceof ISourceViewerExtension3) {
2010: // does an annotation hover exist?
2011: IAnnotationHover annotationHover = ((ISourceViewerExtension3) sourceViewer)
2012: .getCurrentAnnotationHover();
2013: if (annotationHover != null
2014: && makeAnnotationHoverFocusable(sourceViewer,
2015: annotationHover))
2016: return;
2017: }
2018:
2019: // otherwise, just run the action
2020: if (fTextOperationAction.isEnabled())
2021: fTextOperationAction.run();
2022: }
2023:
2024: /**
2025: * Tries to make a text hover focusable (or "sticky").
2026: *
2027: * @param sourceViewer the source viewer to display the hover over
2028: * @param textHover the hover to make focusable
2029: * @return <code>true</code> if successful, <code>false</code> otherwise
2030: */
2031: private boolean makeTextHoverFocusable(
2032: ISourceViewer sourceViewer, ITextHover textHover) {
2033: Point hoverEventLocation = ((ITextViewerExtension2) sourceViewer)
2034: .getHoverEventLocation();
2035: int offset = computeOffsetAtLocation(sourceViewer,
2036: hoverEventLocation.x, hoverEventLocation.y);
2037: if (offset == -1)
2038: return false;
2039:
2040: try {
2041: IRegion hoverRegion = textHover.getHoverRegion(
2042: sourceViewer, offset);
2043: if (hoverRegion == null)
2044: return false;
2045:
2046: String hoverInfo = textHover.getHoverInfo(sourceViewer,
2047: hoverRegion);
2048:
2049: IInformationControlCreator controlCreator = null;
2050: if (textHover instanceof IInformationProviderExtension2)
2051: controlCreator = ((IInformationProviderExtension2) textHover)
2052: .getInformationPresenterControlCreator();
2053:
2054: IInformationProvider informationProvider = new InformationProvider(
2055: hoverRegion, hoverInfo, controlCreator);
2056:
2057: fInformationPresenter.setOffset(offset);
2058: fInformationPresenter
2059: .setAnchor(AbstractInformationControlManager.ANCHOR_BOTTOM);
2060: fInformationPresenter.setMargins(6, 6); // default values from AbstractInformationControlManager
2061: String contentType = TextUtilities.getContentType(
2062: sourceViewer.getDocument(),
2063: getSourceViewerConfiguration()
2064: .getConfiguredDocumentPartitioning(
2065: getSourceViewer()), offset,
2066: true);
2067: fInformationPresenter.setInformationProvider(
2068: informationProvider, contentType);
2069: fInformationPresenter.showInformation();
2070:
2071: return true;
2072:
2073: } catch (BadLocationException e) {
2074: return false;
2075: }
2076: }
2077:
2078: /**
2079: * Tries to make an annotation hover focusable (or "sticky").
2080: *
2081: * @param sourceViewer the source viewer to display the hover over
2082: * @param annotationHover the hover to make focusable
2083: * @return <code>true</code> if successful, <code>false</code> otherwise
2084: */
2085: private boolean makeAnnotationHoverFocusable(
2086: ISourceViewer sourceViewer,
2087: IAnnotationHover annotationHover) {
2088: IVerticalRulerInfo info = getVerticalRuler();
2089: int line = info.getLineOfLastMouseButtonActivity();
2090: if (line == -1)
2091: return false;
2092:
2093: try {
2094:
2095: // compute the hover information
2096: Object hoverInfo;
2097: if (annotationHover instanceof IAnnotationHoverExtension) {
2098: IAnnotationHoverExtension extension = (IAnnotationHoverExtension) annotationHover;
2099: ILineRange hoverLineRange = extension
2100: .getHoverLineRange(sourceViewer, line);
2101: if (hoverLineRange == null)
2102: return false;
2103: final int maxVisibleLines = Integer.MAX_VALUE; // allow any number of lines being displayed, as we support scrolling
2104: hoverInfo = extension.getHoverInfo(sourceViewer,
2105: hoverLineRange, maxVisibleLines);
2106: } else {
2107: hoverInfo = annotationHover.getHoverInfo(
2108: sourceViewer, line);
2109: }
2110:
2111: // hover region: the beginning of the concerned line to place the control right over the line
2112: IDocument document = sourceViewer.getDocument();
2113: int offset = document.getLineOffset(line);
2114: String contentType = TextUtilities.getContentType(
2115: document, getSourceViewerConfiguration()
2116: .getConfiguredDocumentPartitioning(
2117: getSourceViewer()), offset,
2118: true);
2119:
2120: IInformationControlCreator controlCreator = null;
2121: if (annotationHover instanceof IInformationProviderExtension2)
2122: controlCreator = ((IInformationProviderExtension2) annotationHover)
2123: .getInformationPresenterControlCreator();
2124: else if (annotationHover instanceof IAnnotationHoverExtension)
2125: controlCreator = ((IAnnotationHoverExtension) annotationHover)
2126: .getHoverControlCreator();
2127:
2128: IInformationProvider informationProvider = new InformationProvider(
2129: new Region(offset, 0), hoverInfo,
2130: controlCreator);
2131:
2132: fInformationPresenter.setOffset(offset);
2133: fInformationPresenter
2134: .setAnchor(AbstractInformationControlManager.ANCHOR_RIGHT);
2135: fInformationPresenter.setMargins(4, 0); // AnnotationBarHoverManager sets (5,0), minus SourceViewer.GAP_SIZE_1
2136: fInformationPresenter.setInformationProvider(
2137: informationProvider, contentType);
2138: fInformationPresenter.showInformation();
2139:
2140: return true;
2141:
2142: } catch (BadLocationException e) {
2143: return false;
2144: }
2145: }
2146:
2147: // modified version from TextViewer
2148: private int computeOffsetAtLocation(ITextViewer textViewer,
2149: int x, int y) {
2150:
2151: StyledText styledText = textViewer.getTextWidget();
2152: IDocument document = textViewer.getDocument();
2153:
2154: if (document == null)
2155: return -1;
2156:
2157: try {
2158: int widgetOffset = styledText
2159: .getOffsetAtLocation(new Point(x, y));
2160: Point p = styledText.getLocationAtOffset(widgetOffset);
2161: if (p.x > x)
2162: widgetOffset--;
2163:
2164: if (textViewer instanceof ITextViewerExtension5) {
2165: ITextViewerExtension5 extension = (ITextViewerExtension5) textViewer;
2166: return extension
2167: .widgetOffset2ModelOffset(widgetOffset);
2168: }
2169: IRegion visibleRegion = textViewer.getVisibleRegion();
2170: return widgetOffset + visibleRegion.getOffset();
2171: } catch (IllegalArgumentException e) {
2172: return -1;
2173: }
2174:
2175: }
2176: }
2177:
2178: /**
2179: * Key used to look up font preference.
2180: * Value: <code>"org.eclipse.jface.textfont"</code>
2181: *
2182: * @deprecated As of 2.1, replaced by {@link JFaceResources#TEXT_FONT}
2183: */
2184: public final static String PREFERENCE_FONT = JFaceResources.TEXT_FONT;
2185: /**
2186: * Key used to look up foreground color preference.
2187: * Value: <code>AbstractTextEditor.Color.Foreground</code>
2188: * @since 2.0
2189: */
2190: public final static String PREFERENCE_COLOR_FOREGROUND = "AbstractTextEditor.Color.Foreground"; //$NON-NLS-1$
2191: /**
2192: * Key used to look up background color preference.
2193: * Value: <code>AbstractTextEditor.Color.Background</code>
2194: * @since 2.0
2195: */
2196: public final static String PREFERENCE_COLOR_BACKGROUND = "AbstractTextEditor.Color.Background"; //$NON-NLS-1$
2197: /**
2198: * Key used to look up foreground color system default preference.
2199: * Value: <code>AbstractTextEditor.Color.Foreground.SystemDefault</code>
2200: * @since 2.0
2201: */
2202: public final static String PREFERENCE_COLOR_FOREGROUND_SYSTEM_DEFAULT = "AbstractTextEditor.Color.Foreground.SystemDefault"; //$NON-NLS-1$
2203: /**
2204: * Key used to look up background color system default preference.
2205: * Value: <code>AbstractTextEditor.Color.Background.SystemDefault</code>
2206: * @since 2.0
2207: */
2208: public final static String PREFERENCE_COLOR_BACKGROUND_SYSTEM_DEFAULT = "AbstractTextEditor.Color.Background.SystemDefault"; //$NON-NLS-1$
2209: /**
2210: * Key used to look up selection foreground color preference.
2211: * Value: <code>AbstractTextEditor.Color.SelectionForeground</code>
2212: * @since 3.0
2213: */
2214: public final static String PREFERENCE_COLOR_SELECTION_FOREGROUND = "AbstractTextEditor.Color.SelectionForeground"; //$NON-NLS-1$
2215: /**
2216: * Key used to look up selection background color preference.
2217: * Value: <code>AbstractTextEditor.Color.SelectionBackground</code>
2218: * @since 3.0
2219: */
2220: public final static String PREFERENCE_COLOR_SELECTION_BACKGROUND = "AbstractTextEditor.Color.SelectionBackground"; //$NON-NLS-1$
2221: /**
2222: * Key used to look up selection foreground color system default preference.
2223: * Value: <code>AbstractTextEditor.Color.SelectionForeground.SystemDefault</code>
2224: * @since 3.0
2225: */
2226: public final static String PREFERENCE_COLOR_SELECTION_FOREGROUND_SYSTEM_DEFAULT = "AbstractTextEditor.Color.SelectionForeground.SystemDefault"; //$NON-NLS-1$
2227: /**
2228: * Key used to look up selection background color system default preference.
2229: * Value: <code>AbstractTextEditor.Color.SelectionBackground.SystemDefault</code>
2230: * @since 3.0
2231: */
2232: public final static String PREFERENCE_COLOR_SELECTION_BACKGROUND_SYSTEM_DEFAULT = "AbstractTextEditor.Color.SelectionBackground.SystemDefault"; //$NON-NLS-1$
2233: /**
2234: * Key used to look up find scope background color preference.
2235: * Value: <code>AbstractTextEditor.Color.FindScope</code>
2236: * @since 2.0
2237: */
2238: public final static String PREFERENCE_COLOR_FIND_SCOPE = "AbstractTextEditor.Color.FindScope"; //$NON-NLS-1$
2239: /**
2240: * Key used to look up smart home/end preference.
2241: * Value: <code>AbstractTextEditor.Navigation.SmartHomeEnd</code>
2242: * @since 2.1
2243: */
2244: public final static String PREFERENCE_NAVIGATION_SMART_HOME_END = "AbstractTextEditor.Navigation.SmartHomeEnd"; //$NON-NLS-1$
2245: /**
2246: * Key used to look up the custom caret preference.
2247: * Value: {@value}
2248: * @since 3.0
2249: */
2250: public final static String PREFERENCE_USE_CUSTOM_CARETS = "AbstractTextEditor.Accessibility.UseCustomCarets"; //$NON-NLS-1$
2251: /**
2252: * Key used to look up the caret width preference.
2253: * Value: {@value}
2254: * @since 3.0
2255: */
2256: public final static String PREFERENCE_WIDE_CARET = "AbstractTextEditor.Accessibility.WideCaret"; //$NON-NLS-1$
2257: /**
2258: * A named preference that controls if hyperlinks are turned on or off.
2259: * <p>
2260: * Value is of type <code>Boolean</code>.
2261: * </p>
2262: *
2263: * @since 3.1
2264: */
2265: public static final String PREFERENCE_HYPERLINKS_ENABLED = "hyperlinksEnabled"; //$NON-NLS-1$
2266:
2267: /**
2268: * A named preference that controls the key modifier for hyperlinks.
2269: * <p>
2270: * Value is of type <code>String</code>.
2271: * </p>
2272: *
2273: * @since 3.1
2274: */
2275: public static final String PREFERENCE_HYPERLINK_KEY_MODIFIER = "hyperlinkKeyModifier"; //$NON-NLS-1$
2276: /**
2277: * A named preference that controls the key modifier mask for hyperlinks.
2278: * The value is only used if the value of <code>PREFERENCE_HYPERLINK_KEY_MODIFIER</code>
2279: * cannot be resolved to valid SWT modifier bits.
2280: * <p>
2281: * Value is of type <code>String</code>.
2282: * </p>
2283: *
2284: * @see #PREFERENCE_HYPERLINK_KEY_MODIFIER
2285: * @since 3.1
2286: */
2287: public static final String PREFERENCE_HYPERLINK_KEY_MODIFIER_MASK = "hyperlinkKeyModifierMask"; //$NON-NLS-1$
2288: /**
2289: * A named preference that controls the visible ruler column contributions.
2290: * <p>
2291: * Value is of type <code>String</code> and should be read using a {@link RulerColumnPreferenceAdapter}.
2292: * </p>
2293: *
2294: * @since 3.3
2295: */
2296: public static final String PREFERENCE_RULER_CONTRIBUTIONS = "rulerContributions"; //$NON-NLS-1$
2297: /**
2298: * A named preference that controls the display of whitespace characters.
2299: * <p>
2300: * Value is of type <code>Boolean</code>.
2301: * </p>
2302: *
2303: * @since 3.3
2304: */
2305: public static final String PREFERENCE_SHOW_WHITESPACE_CHARACTERS = "showWhitespaceCharacters"; //$NON-NLS-1$
2306: /**
2307: * A named preference that controls whether text drag and drop is enabled.
2308: * <p>
2309: * Value is of type <code>Boolean</code>.
2310: * </p>
2311: *
2312: * @since 3.3
2313: */
2314: public static final String PREFERENCE_TEXT_DRAG_AND_DROP_ENABLED = "textDragAndDropEnabled"; //$NON-NLS-1$
2315:
2316: /** Menu id for the editor context menu. */
2317: public final static String DEFAULT_EDITOR_CONTEXT_MENU_ID = "#EditorContext"; //$NON-NLS-1$
2318: /** Menu id for the ruler context menu. */
2319: public final static String DEFAULT_RULER_CONTEXT_MENU_ID = "#RulerContext"; //$NON-NLS-1$
2320:
2321: /** The width of the vertical ruler. */
2322: protected final static int VERTICAL_RULER_WIDTH = 12;
2323:
2324: /**
2325: * The complete mapping between action definition IDs used by eclipse and StyledText actions.
2326: *
2327: * @since 2.0
2328: */
2329: protected final static IdMapEntry[] ACTION_MAP = new IdMapEntry[] {
2330: // navigation
2331: new IdMapEntry(ITextEditorActionDefinitionIds.LINE_UP,
2332: ST.LINE_UP),
2333: new IdMapEntry(ITextEditorActionDefinitionIds.LINE_DOWN,
2334: ST.LINE_DOWN),
2335: new IdMapEntry(ITextEditorActionDefinitionIds.LINE_START,
2336: ST.LINE_START),
2337: new IdMapEntry(ITextEditorActionDefinitionIds.LINE_END,
2338: ST.LINE_END),
2339: new IdMapEntry(
2340: ITextEditorActionDefinitionIds.COLUMN_PREVIOUS,
2341: ST.COLUMN_PREVIOUS),
2342: new IdMapEntry(ITextEditorActionDefinitionIds.COLUMN_NEXT,
2343: ST.COLUMN_NEXT),
2344: new IdMapEntry(ITextEditorActionDefinitionIds.PAGE_UP,
2345: ST.PAGE_UP),
2346: new IdMapEntry(ITextEditorActionDefinitionIds.PAGE_DOWN,
2347: ST.PAGE_DOWN),
2348: new IdMapEntry(
2349: ITextEditorActionDefinitionIds.WORD_PREVIOUS,
2350: ST.WORD_PREVIOUS),
2351: new IdMapEntry(ITextEditorActionDefinitionIds.WORD_NEXT,
2352: ST.WORD_NEXT),
2353: new IdMapEntry(ITextEditorActionDefinitionIds.TEXT_START,
2354: ST.TEXT_START),
2355: new IdMapEntry(ITextEditorActionDefinitionIds.TEXT_END,
2356: ST.TEXT_END),
2357: new IdMapEntry(ITextEditorActionDefinitionIds.WINDOW_START,
2358: ST.WINDOW_START),
2359: new IdMapEntry(ITextEditorActionDefinitionIds.WINDOW_END,
2360: ST.WINDOW_END),
2361: // selection
2362: new IdMapEntry(
2363: ITextEditorActionDefinitionIds.SELECT_LINE_UP,
2364: ST.SELECT_LINE_UP),
2365: new IdMapEntry(
2366: ITextEditorActionDefinitionIds.SELECT_LINE_DOWN,
2367: ST.SELECT_LINE_DOWN),
2368: new IdMapEntry(
2369: ITextEditorActionDefinitionIds.SELECT_LINE_START,
2370: ST.SELECT_LINE_START),
2371: new IdMapEntry(
2372: ITextEditorActionDefinitionIds.SELECT_LINE_END,
2373: ST.SELECT_LINE_END),
2374: new IdMapEntry(
2375: ITextEditorActionDefinitionIds.SELECT_COLUMN_PREVIOUS,
2376: ST.SELECT_COLUMN_PREVIOUS),
2377: new IdMapEntry(
2378: ITextEditorActionDefinitionIds.SELECT_COLUMN_NEXT,
2379: ST.SELECT_COLUMN_NEXT),
2380: new IdMapEntry(
2381: ITextEditorActionDefinitionIds.SELECT_PAGE_UP,
2382: ST.SELECT_PAGE_UP),
2383: new IdMapEntry(
2384: ITextEditorActionDefinitionIds.SELECT_PAGE_DOWN,
2385: ST.SELECT_PAGE_DOWN),
2386: new IdMapEntry(
2387: ITextEditorActionDefinitionIds.SELECT_WORD_PREVIOUS,
2388: ST.SELECT_WORD_PREVIOUS),
2389: new IdMapEntry(
2390: ITextEditorActionDefinitionIds.SELECT_WORD_NEXT,
2391: ST.SELECT_WORD_NEXT),
2392: new IdMapEntry(
2393: ITextEditorActionDefinitionIds.SELECT_TEXT_START,
2394: ST.SELECT_TEXT_START),
2395: new IdMapEntry(
2396: ITextEditorActionDefinitionIds.SELECT_TEXT_END,
2397: ST.SELECT_TEXT_END),
2398: new IdMapEntry(
2399: ITextEditorActionDefinitionIds.SELECT_WINDOW_START,
2400: ST.SELECT_WINDOW_START),
2401: new IdMapEntry(
2402: ITextEditorActionDefinitionIds.SELECT_WINDOW_END,
2403: ST.SELECT_WINDOW_END),
2404: // modification
2405: new IdMapEntry(IWorkbenchActionDefinitionIds.CUT, ST.CUT),
2406: new IdMapEntry(IWorkbenchActionDefinitionIds.COPY, ST.COPY),
2407: new IdMapEntry(IWorkbenchActionDefinitionIds.PASTE,
2408: ST.PASTE),
2409: new IdMapEntry(
2410: ITextEditorActionDefinitionIds.DELETE_PREVIOUS,
2411: ST.DELETE_PREVIOUS),
2412: new IdMapEntry(ITextEditorActionDefinitionIds.DELETE_NEXT,
2413: ST.DELETE_NEXT),
2414: new IdMapEntry(
2415: ITextEditorActionDefinitionIds.DELETE_PREVIOUS_WORD,
2416: ST.DELETE_WORD_PREVIOUS),
2417: new IdMapEntry(
2418: ITextEditorActionDefinitionIds.DELETE_NEXT_WORD,
2419: ST.DELETE_WORD_NEXT),
2420: // miscellaneous
2421: new IdMapEntry(
2422: ITextEditorActionDefinitionIds.TOGGLE_OVERWRITE,
2423: ST.TOGGLE_OVERWRITE) };
2424:
2425: private final String fReadOnlyLabel = EditorMessages.Editor_statusline_state_readonly_label;
2426: private final String fWritableLabel = EditorMessages.Editor_statusline_state_writable_label;
2427: private final String fInsertModeLabel = EditorMessages.Editor_statusline_mode_insert_label;
2428: private final String fOverwriteModeLabel = EditorMessages.Editor_statusline_mode_overwrite_label;
2429: private final String fSmartInsertModeLabel = EditorMessages.Editor_statusline_mode_smartinsert_label;
2430:
2431: /** The error message shown in the status line in case of failed information look up. */
2432: protected final String fErrorLabel = EditorMessages.Editor_statusline_error_label;
2433:
2434: /**
2435: * Data structure for the position label value.
2436: */
2437: private static class PositionLabelValue {
2438:
2439: public int fValue;
2440:
2441: public String toString() {
2442: return String.valueOf(fValue);
2443: }
2444: }
2445:
2446: /** The pattern used to show the position label in the status line. */
2447: private final String fPositionLabelPattern = EditorMessages.Editor_statusline_position_pattern;
2448: /** The position label value of the current line. */
2449: private final PositionLabelValue fLineLabel = new PositionLabelValue();
2450: /** The position label value of the current column. */
2451: private final PositionLabelValue fColumnLabel = new PositionLabelValue();
2452: /** The arguments for the position label pattern. */
2453: private final Object[] fPositionLabelPatternArguments = new Object[] {
2454: fLineLabel, fColumnLabel };
2455: /**
2456: * The column support of this editor.
2457: * @since 3.3
2458: */
2459: private IColumnSupport fColumnSupport;
2460:
2461: /** The editor's explicit document provider. */
2462: private IDocumentProvider fExplicitDocumentProvider;
2463: /** The editor's preference store. */
2464: private IPreferenceStore fPreferenceStore;
2465: /** The editor's range indicator. */
2466: private Annotation fRangeIndicator;
2467: /** The editor's source viewer configuration. */
2468: private SourceViewerConfiguration fConfiguration;
2469: /** The editor's source viewer. */
2470: private ISourceViewer fSourceViewer;
2471: /**
2472: * The editor's selection provider.
2473: * @since 2.1
2474: */
2475: private SelectionProvider fSelectionProvider = new SelectionProvider();
2476: /**
2477: * The editor's selection listener.
2478: * @since 3.0
2479: */
2480: private SelectionListener fSelectionListener;
2481: /** The editor's font. */
2482: private Font fFont;
2483: /**
2484: * The editor's foreground color.
2485: * @since 2.0
2486: */
2487: private Color fForegroundColor;
2488: /**
2489: * The editor's background color.
2490: * @since 2.0
2491: */
2492: private Color fBackgroundColor;
2493: /**
2494: * The editor's selection foreground color.
2495: * @since 3.0
2496: */
2497: private Color fSelectionForegroundColor;
2498: /**
2499: * The editor's selection background color.
2500: * @since 3.0
2501: */
2502: private Color fSelectionBackgroundColor;
2503: /**
2504: * The find scope's highlight color.
2505: * @since 2.0
2506: */
2507: private Color fFindScopeHighlightColor;
2508:
2509: /**
2510: * The editor's status line.
2511: * @since 2.1
2512: */
2513: private IEditorStatusLine fEditorStatusLine;
2514: /** The editor's vertical ruler. */
2515: private IVerticalRuler fVerticalRuler;
2516: /** The editor's context menu id. */
2517: private String fEditorContextMenuId;
2518: /** The ruler's context menu id. */
2519: private String fRulerContextMenuId;
2520: /** The editor's help context id. */
2521: private String fHelpContextId;
2522: /** The editor's presentation mode. */
2523: private boolean fShowHighlightRangeOnly;
2524: /** The actions registered with the editor. */
2525: private Map fActions = new HashMap(10);
2526: /** The actions marked as selection dependent. */
2527: private List fSelectionActions = new ArrayList(5);
2528: /** The actions marked as content dependent. */
2529: private List fContentActions = new ArrayList(5);
2530: /**
2531: * The actions marked as property dependent.
2532: * @since 2.0
2533: */
2534: private List fPropertyActions = new ArrayList(5);
2535: /**
2536: * The actions marked as state dependent.
2537: * @since 2.0
2538: */
2539: private List fStateActions = new ArrayList(5);
2540: /** The editor's action activation codes. */
2541: private List fActivationCodes = new ArrayList(2);
2542: /** The verify key listener for activation code triggering. */
2543: private ActivationCodeTrigger fActivationCodeTrigger = new ActivationCodeTrigger();
2544: /** Context menu listener. */
2545: private IMenuListener fMenuListener;
2546: /** Vertical ruler mouse listener. */
2547: private MouseListener fMouseListener;
2548: /** Selection changed listener. */
2549: private ISelectionChangedListener fSelectionChangedListener;
2550: /** Title image to be disposed. */
2551: private Image fTitleImage;
2552: /** The text context menu to be disposed. */
2553: private Menu fTextContextMenu;
2554: /** The ruler context menu to be disposed. */
2555: private Menu fRulerContextMenu;
2556: /** The editor's element state listener. */
2557: private IElementStateListener fElementStateListener = new ElementStateListener();
2558: /**
2559: * The editor's text input listener.
2560: * @since 2.1
2561: */
2562: private TextInputListener fTextInputListener = new TextInputListener();
2563: /** The editor's text listener. */
2564: private TextListener fTextListener = new TextListener();
2565: /** The editor's property change listener. */
2566: private IPropertyChangeListener fPropertyChangeListener = new PropertyChangeListener();
2567: /**
2568: * The editor's font properties change listener.
2569: * @since 2.1
2570: */
2571: private IPropertyChangeListener fFontPropertyChangeListener = new FontPropertyChangeListener();
2572:
2573: /**
2574: * The editor's activation listener.
2575: * @since 2.0
2576: */
2577: private ActivationListener fActivationListener;
2578: /**
2579: * The map of the editor's status fields.
2580: * @since 2.0
2581: */
2582: private Map fStatusFields;
2583: /**
2584: * The editor's cursor listener.
2585: * @since 2.0
2586: */
2587: private ICursorListener fCursorListener;
2588: /**
2589: * The editor's remembered text selection.
2590: * @since 2.0
2591: */
2592: private ISelection fRememberedSelection;
2593: /**
2594: * Indicates whether the editor runs in 1.0 context menu registration compatibility mode.
2595: * @since 2.0
2596: */
2597: private boolean fCompatibilityMode = true;
2598: /**
2599: * The number of re-entrances into error correction code while saving.
2600: * @since 2.0
2601: */
2602: private int fErrorCorrectionOnSave;
2603: /**
2604: * The delete line target.
2605: * @since 2.1
2606: */
2607: private DeleteLineTarget fDeleteLineTarget;
2608: /**
2609: * The incremental find target.
2610: * @since 2.0
2611: */
2612: private IncrementalFindTarget fIncrementalFindTarget;
2613: /**
2614: * The mark region target.
2615: * @since 2.0
2616: */
2617: private IMarkRegionTarget fMarkRegionTarget;
2618: /**
2619: * Cached modification stamp of the editor's input.
2620: * @since 2.0
2621: */
2622: private long fModificationStamp = -1;
2623: /**
2624: * Ruler context menu listeners.
2625: * @since 2.0
2626: */
2627: private List fRulerContextMenuListeners = new ArrayList();
2628: /**
2629: * Indicates whether sanity checking in enabled.
2630: * @since 2.0
2631: */
2632: private boolean fIsSanityCheckEnabled = true;
2633: /**
2634: * The find replace target.
2635: * @since 2.1
2636: */
2637: private FindReplaceTarget fFindReplaceTarget;
2638: /**
2639: * Indicates whether state validation is enabled.
2640: * @since 2.1
2641: */
2642: private boolean fIsStateValidationEnabled = true;
2643: /**
2644: * The key binding scopes of this editor.
2645: * @since 2.1
2646: */
2647: private String[] fKeyBindingScopes;
2648: /**
2649: * Whether the overwrite mode can be turned on.
2650: * @since 3.0
2651: */
2652: private boolean fIsOverwriteModeEnabled = true;
2653: /**
2654: * Whether the overwrite mode is currently on.
2655: * @since 3.0
2656: */
2657: private boolean fIsOverwriting = false;
2658: /**
2659: * The editor's insert mode.
2660: * @since 3.0
2661: */
2662: private InsertMode fInsertMode = SMART_INSERT;
2663: /**
2664: * The sequence of legal editor insert modes.
2665: * @since 3.0
2666: */
2667: private List fLegalInsertModes = null;
2668: /**
2669: * The non-default caret.
2670: * @since 3.0
2671: */
2672: private Caret fNonDefaultCaret;
2673: /**
2674: * The image used in non-default caret.
2675: * @since 3.0
2676: */
2677: private Image fNonDefaultCaretImage;
2678: /**
2679: * The styled text's initial caret.
2680: * @since 3.0
2681: */
2682: private Caret fInitialCaret;
2683: /**
2684: * The operation approver used to warn on undoing of non-local operations.
2685: * @since 3.1
2686: */
2687: private IOperationApprover fNonLocalOperationApprover;
2688: /**
2689: * The operation approver used to warn of linear undo violations.
2690: * @since 3.1
2691: */
2692: private IOperationApprover fLinearUndoViolationApprover;
2693: /**
2694: * This editor's memento holding data for restoring it after restart.
2695: * @since 3.3
2696: */
2697: private IMemento fMementoToRestore;
2698: /**
2699: * This editor's savable.
2700: * @since 3.3
2701: */
2702: private TextEditorSavable fSavable;
2703: /**
2704: * Tells whether text drag and drop is enabled.
2705: * @since 3.3
2706: */
2707: private boolean fIsTextDragAndDropEnabled = false;
2708: /**
2709: * Tells whether text drag and drop has been installed on the control.
2710: * @since 3.3
2711: */
2712: private boolean fIsTextDragAndDropInstalled = false;
2713: /**
2714: * Helper token to decide whether drag and
2715: * drop happens inside the same editor.
2716: * @since 3.3
2717: */
2718: private Object fTextDragAndDropToken;
2719: /**
2720: * The information presenter.
2721: * @since 3.3
2722: */
2723: private InformationPresenter fInformationPresenter;
2724:
2725: /**
2726: * Creates a new text editor. If not explicitly set, this editor uses
2727: * a <code>SourceViewerConfiguration</code> to configure its
2728: * source viewer. This viewer does not have a range indicator installed,
2729: * nor any menu id set. By default, the created editor runs in 1.0 context
2730: * menu registration compatibility mode.
2731: */
2732: protected AbstractTextEditor() {
2733: super ();
2734: fEditorContextMenuId = null;
2735: fRulerContextMenuId = null;
2736: fHelpContextId = null;
2737: }
2738:
2739: /*
2740: * @see ITextEditor#getDocumentProvider()
2741: */
2742: public IDocumentProvider getDocumentProvider() {
2743: return fExplicitDocumentProvider;
2744: }
2745:
2746: /**
2747: * Returns the editor's range indicator. May return <code>null</code> if no
2748: * range indicator is installed.
2749: *
2750: * @return the editor's range indicator which may be <code>null</code>
2751: */
2752: protected final Annotation getRangeIndicator() {
2753: return fRangeIndicator;
2754: }
2755:
2756: /**
2757: * Returns the editor's source viewer configuration. May return <code>null</code>
2758: * before the editor's part has been created and after disposal.
2759: *
2760: * @return the editor's source viewer configuration which may be <code>null</code>
2761: */
2762: protected final SourceViewerConfiguration getSourceViewerConfiguration() {
2763: return fConfiguration;
2764: }
2765:
2766: /**
2767: * Returns the editor's source viewer. May return <code>null</code> before
2768: * the editor's part has been created and after disposal.
2769: *
2770: * @return the editor's source viewer which may be <code>null</code>
2771: */
2772: protected final ISourceViewer getSourceViewer() {
2773: return fSourceViewer;
2774: }
2775:
2776: /**
2777: * Returns the editor's vertical ruler. May return <code>null</code> before
2778: * the editor's part has been created and after disposal.
2779: *
2780: * @return the editor's vertical ruler which may be <code>null</code>
2781: */
2782: protected final IVerticalRuler getVerticalRuler() {
2783: return fVerticalRuler;
2784: }
2785:
2786: /**
2787: * Returns the editor's context menu id. May return <code>null</code> before
2788: * the editor's part has been created.
2789: *
2790: * @return the editor's context menu id which may be <code>null</code>
2791: */
2792: protected final String getEditorContextMenuId() {
2793: return fEditorContextMenuId;
2794: }
2795:
2796: /**
2797: * Returns the ruler's context menu id. May return <code>null</code> before
2798: * the editor's part has been created.
2799: *
2800: * @return the ruler's context menu id which may be <code>null</code>
2801: */
2802: protected final String getRulerContextMenuId() {
2803: return fRulerContextMenuId;
2804: }
2805:
2806: /**
2807: * Returns the editor's help context id or <code>null</code> if none has
2808: * been set.
2809: *
2810: * @return the editor's help context id which may be <code>null</code>
2811: */
2812: protected final String getHelpContextId() {
2813: return fHelpContextId;
2814: }
2815:
2816: /**
2817: * Returns this editor's preference store or <code>null</code> if none has
2818: * been set.
2819: *
2820: * @return this editor's preference store which may be <code>null</code>
2821: */
2822: protected final IPreferenceStore getPreferenceStore() {
2823: return fPreferenceStore;
2824: }
2825:
2826: /**
2827: * Sets this editor's document provider. This method must be
2828: * called before the editor's control is created.
2829: *
2830: * @param provider the document provider
2831: */
2832: protected void setDocumentProvider(IDocumentProvider provider) {
2833: fExplicitDocumentProvider = provider;
2834: }
2835:
2836: /**
2837: * Sets this editor's source viewer configuration used to configure its
2838: * internal source viewer. This method must be called before the editor's
2839: * control is created. If not, this editor uses a <code>SourceViewerConfiguration</code>.
2840: *
2841: * @param configuration the source viewer configuration object
2842: */
2843: protected void setSourceViewerConfiguration(
2844: SourceViewerConfiguration configuration) {
2845: Assert.isNotNull(configuration);
2846: fConfiguration = configuration;
2847: }
2848:
2849: /**
2850: * Sets the annotation which this editor uses to represent the highlight
2851: * range if the editor is configured to show the entire document. If the
2852: * range indicator is not set, this editor will not show a range indication.
2853: *
2854: * @param rangeIndicator the annotation
2855: */
2856: protected void setRangeIndicator(Annotation rangeIndicator) {
2857: Assert.isNotNull(rangeIndicator);
2858: fRangeIndicator = rangeIndicator;
2859: }
2860:
2861: /**
2862: * Sets this editor's context menu id.
2863: *
2864: * @param contextMenuId the context menu id
2865: */
2866: protected void setEditorContextMenuId(String contextMenuId) {
2867: Assert.isNotNull(contextMenuId);
2868: fEditorContextMenuId = contextMenuId;
2869: }
2870:
2871: /**
2872: * Sets the ruler's context menu id.
2873: *
2874: * @param contextMenuId the context menu id
2875: */
2876: protected void setRulerContextMenuId(String contextMenuId) {
2877: Assert.isNotNull(contextMenuId);
2878: fRulerContextMenuId = contextMenuId;
2879: }
2880:
2881: /**
2882: * Sets the context menu registration 1.0 compatibility mode. (See class
2883: * description for more details.)
2884: *
2885: * @param compatible <code>true</code> if compatibility mode is enabled
2886: * @since 2.0
2887: */
2888: protected final void setCompatibilityMode(boolean compatible) {
2889: fCompatibilityMode = compatible;
2890: }
2891:
2892: /**
2893: * Sets the editor's help context id.
2894: *
2895: * @param helpContextId the help context id
2896: */
2897: protected void setHelpContextId(String helpContextId) {
2898: Assert.isNotNull(helpContextId);
2899: fHelpContextId = helpContextId;
2900: }
2901:
2902: /**
2903: * Sets the key binding scopes for this editor.
2904: *
2905: * @param scopes a non-empty array of key binding scope identifiers
2906: * @since 2.1
2907: */
2908: protected void setKeyBindingScopes(String[] scopes) {
2909: Assert.isTrue(scopes != null && scopes.length > 0);
2910: fKeyBindingScopes = scopes;
2911: }
2912:
2913: /**
2914: * Sets this editor's preference store. This method must be
2915: * called before the editor's control is created.
2916: *
2917: * @param store the preference store or <code>null</code> to remove the
2918: * preference store
2919: */
2920: protected void setPreferenceStore(IPreferenceStore store) {
2921: if (fPreferenceStore != null)
2922: fPreferenceStore
2923: .removePropertyChangeListener(fPropertyChangeListener);
2924:
2925: fPreferenceStore = store;
2926:
2927: if (fPreferenceStore != null)
2928: fPreferenceStore
2929: .addPropertyChangeListener(fPropertyChangeListener);
2930: }
2931:
2932: /*
2933: * @see ITextEditor#isEditable()
2934: */
2935: public boolean isEditable() {
2936: IDocumentProvider provider = getDocumentProvider();
2937: if (provider instanceof IDocumentProviderExtension) {
2938: IDocumentProviderExtension extension = (IDocumentProviderExtension) provider;
2939: return extension.isModifiable(getEditorInput());
2940: }
2941: return false;
2942: }
2943:
2944: /**
2945: * {@inheritDoc}
2946: * <p>
2947: * Returns <code>null</code> after disposal.
2948: * </p>
2949: *
2950: * @return the selection provider or <code>null</code> if the editor has
2951: * been disposed
2952: */
2953: public ISelectionProvider getSelectionProvider() {
2954: return fSelectionProvider;
2955: }
2956:
2957: /**
2958: * Remembers the current selection of this editor. This method is called when, e.g.,
2959: * the content of the editor is about to be reverted to the saved state. This method
2960: * remembers the selection in a semantic format, i.e., in a format which allows to
2961: * restore the selection even if the originally selected text is no longer part of the
2962: * editor's content.
2963: * <p>
2964: * Subclasses should implement this method including all necessary state. This
2965: * default implementation remembers the textual range only and is thus purely
2966: * syntactic.</p>
2967: *
2968: * @see #restoreSelection()
2969: * @since 2.0
2970: */
2971: protected void rememberSelection() {
2972: fRememberedSelection = doGetSelection();
2973: }
2974:
2975: /**
2976: * Returns the current selection.
2977: * @return ISelection
2978: * @since 2.1
2979: */
2980: protected ISelection doGetSelection() {
2981: ISelectionProvider sp = null;
2982: if (fSourceViewer != null)
2983: sp = fSourceViewer.getSelectionProvider();
2984: return (sp == null ? null : sp.getSelection());
2985: }
2986:
2987: /**
2988: * Restores a selection previously remembered by <code>rememberSelection</code>.
2989: * Subclasses may reimplement this method and thereby semantically adapt the
2990: * remembered selection. This default implementation just selects the
2991: * remembered textual range.
2992: *
2993: * @see #rememberSelection()
2994: * @since 2.0
2995: */
2996: protected void restoreSelection() {
2997: if (fRememberedSelection instanceof ITextSelection) {
2998: ITextSelection textSelection = (ITextSelection) fRememberedSelection;
2999: if (isValidSelection(textSelection.getOffset(),
3000: textSelection.getLength()))
3001: doSetSelection(fRememberedSelection);
3002: }
3003: fRememberedSelection = null;
3004: }
3005:
3006: /**
3007: * Tells whether the given selection is valid.
3008: *
3009: * @param offset the offset of the selection
3010: * @param length the length of the selection
3011: * @return <code>true</code> if the selection is valid
3012: * @since 2.1
3013: */
3014: private boolean isValidSelection(int offset, int length) {
3015: IDocumentProvider provider = getDocumentProvider();
3016: if (provider != null) {
3017: IDocument document = provider.getDocument(getEditorInput());
3018: if (document != null) {
3019: int end = offset + length;
3020: int documentLength = document.getLength();
3021: return 0 <= offset && offset <= documentLength
3022: && 0 <= end && end <= documentLength
3023: && length >= 0;
3024: }
3025: }
3026: return false;
3027: }
3028:
3029: /**
3030: * Sets the given selection.
3031: * @param selection
3032: * @since 2.1
3033: */
3034: protected void doSetSelection(ISelection selection) {
3035: if (selection instanceof ITextSelection) {
3036: ITextSelection textSelection = (ITextSelection) selection;
3037: selectAndReveal(textSelection.getOffset(), textSelection
3038: .getLength());
3039: }
3040: }
3041:
3042: /**
3043: * Creates and returns the listener on this editor's context menus.
3044: *
3045: * @return the menu listener
3046: */
3047: protected final IMenuListener getContextMenuListener() {
3048: if (fMenuListener == null) {
3049: fMenuListener = new IMenuListener() {
3050:
3051: public void menuAboutToShow(IMenuManager menu) {
3052: String id = menu.getId();
3053: if (getRulerContextMenuId().equals(id)) {
3054: setFocus();
3055: rulerContextMenuAboutToShow(menu);
3056: } else if (getEditorContextMenuId().equals(id)) {
3057: setFocus();
3058: editorContextMenuAboutToShow(menu);
3059: }
3060: }
3061: };
3062: }
3063: return fMenuListener;
3064: }
3065:
3066: /**
3067: * Creates and returns the listener on this editor's vertical ruler.
3068: *
3069: * @return the mouse listener
3070: */
3071: protected final MouseListener getRulerMouseListener() {
3072: if (fMouseListener == null) {
3073: fMouseListener = new MouseListener() {
3074:
3075: private boolean fDoubleClicked = false;
3076: private final int fDoubleClickTime = Display
3077: .getDefault().getDoubleClickTime();
3078: private long fMouseUpDelta = 0;
3079:
3080: private void triggerAction(String actionID) {
3081: IAction action = getAction(actionID);
3082: if (action != null) {
3083: if (action instanceof IUpdate)
3084: ((IUpdate) action).update();
3085: if (action.isEnabled())
3086: action.run();
3087: }
3088: }
3089:
3090: public void mouseUp(final MouseEvent e) {
3091: setFocus();
3092: final int delay = fDoubleClickTime
3093: - (int) (System.currentTimeMillis() - fMouseUpDelta);
3094: if (1 != e.button)
3095: return;
3096:
3097: Runnable runnable = new Runnable() {
3098: public void run() {
3099: if (!fDoubleClicked)
3100: triggerAction(ITextEditorActionConstants.RULER_CLICK);
3101: }
3102: };
3103: if (delay <= 0)
3104: runnable.run();
3105: else
3106: Display.getDefault().timerExec(delay, runnable);
3107: }
3108:
3109: public void mouseDoubleClick(MouseEvent e) {
3110: if (1 == e.button) {
3111: fDoubleClicked = true;
3112: triggerAction(ITextEditorActionConstants.RULER_DOUBLE_CLICK);
3113: }
3114: }
3115:
3116: public void mouseDown(MouseEvent e) {
3117: fMouseUpDelta = System.currentTimeMillis();
3118: fDoubleClicked = false;
3119: StyledText text = fSourceViewer.getTextWidget();
3120: if (text != null && !text.isDisposed()) {
3121: Display display = text.getDisplay();
3122: Point location = display.getCursorLocation();
3123: fRulerContextMenu.setLocation(location.x,
3124: location.y);
3125: }
3126: }
3127: };
3128: }
3129: return fMouseListener;
3130: }
3131:
3132: /**
3133: * Returns this editor's selection changed listener to be installed
3134: * on the editor's source viewer.
3135: *
3136: * @return the listener
3137: */
3138: protected final ISelectionChangedListener getSelectionChangedListener() {
3139: if (fSelectionChangedListener == null) {
3140: fSelectionChangedListener = new ISelectionChangedListener() {
3141:
3142: private Runnable fRunnable = new Runnable() {
3143: public void run() {
3144: // check whether editor has not been disposed yet
3145: if (fSourceViewer != null
3146: && fSourceViewer.getDocument() != null) {
3147: updateSelectionDependentActions();
3148: }
3149: }
3150: };
3151:
3152: private Display fDisplay;
3153:
3154: public void selectionChanged(SelectionChangedEvent event) {
3155: if (fDisplay == null)
3156: fDisplay = getSite().getShell().getDisplay();
3157: fDisplay.asyncExec(fRunnable);
3158: handleCursorPositionChanged();
3159: }
3160: };
3161: }
3162:
3163: return fSelectionChangedListener;
3164: }
3165:
3166: /**
3167: * Returns this editor's "cursor" listener to be installed on the editor's
3168: * source viewer. This listener is listening to key and mouse button events.
3169: * It triggers the updating of the status line by calling
3170: * <code>handleCursorPositionChanged()</code>.
3171: *
3172: * @return the listener
3173: * @since 2.0
3174: */
3175: protected final ICursorListener getCursorListener() {
3176: if (fCursorListener == null) {
3177: fCursorListener = new ICursorListener() {
3178:
3179: public void keyPressed(KeyEvent e) {
3180: handleCursorPositionChanged();
3181: }
3182:
3183: public void keyReleased(KeyEvent e) {
3184: }
3185:
3186: public void mouseDoubleClick(MouseEvent e) {
3187: }
3188:
3189: public void mouseDown(MouseEvent e) {
3190: }
3191:
3192: public void mouseUp(MouseEvent e) {
3193: handleCursorPositionChanged();
3194: }
3195: };
3196: }
3197: return fCursorListener;
3198: }
3199:
3200: /**
3201: * Implements the <code>init</code> method of <code>IEditorPart</code>.
3202: * Subclasses replacing <code>init</code> may choose to call this method in
3203: * their implementation.
3204: *
3205: * @param window the workbench window
3206: * @param site the editor's site
3207: * @param input the editor input for the editor being created
3208: * @throws PartInitException if {@link #doSetInput(IEditorInput)} fails or gets canceled
3209: *
3210: * @see org.eclipse.ui.IEditorPart#init(org.eclipse.ui.IEditorSite, org.eclipse.ui.IEditorInput)
3211: * @since 2.1
3212: */
3213: protected final void internalInit(IWorkbenchWindow window,
3214: final IEditorSite site, final IEditorInput input)
3215: throws PartInitException {
3216:
3217: IRunnableWithProgress runnable = new IRunnableWithProgress() {
3218: public void run(IProgressMonitor monitor)
3219: throws InvocationTargetException,
3220: InterruptedException {
3221: try {
3222:
3223: if (getDocumentProvider() instanceof IDocumentProviderExtension2) {
3224: IDocumentProviderExtension2 extension = (IDocumentProviderExtension2) getDocumentProvider();
3225: extension.setProgressMonitor(monitor);
3226: }
3227:
3228: doSetInput(input);
3229:
3230: } catch (CoreException x) {
3231: throw new InvocationTargetException(x);
3232: } finally {
3233: if (getDocumentProvider() instanceof IDocumentProviderExtension2) {
3234: IDocumentProviderExtension2 extension = (IDocumentProviderExtension2) getDocumentProvider();
3235: extension.setProgressMonitor(null);
3236: }
3237: }
3238: }
3239: };
3240:
3241: try {
3242: // When using the progress service always a modal dialog pops up. The site should be asked for a runnable context
3243: // which could be the workbench window or the progress service, depending on what the site represents.
3244: // getSite().getWorkbenchWindow().getWorkbench().getProgressService().run(false, true, runnable);
3245:
3246: getSite().getWorkbenchWindow().run(false, true, runnable);
3247:
3248: } catch (InterruptedException x) {
3249: } catch (InvocationTargetException x) {
3250: Throwable t = x.getTargetException();
3251: if (t instanceof CoreException) {
3252: /*
3253: /* XXX: Remove unpacking of CoreException once the following bug is
3254: * fixed: https://bugs.eclipse.org/bugs/show_bug.cgi?id=81640
3255: */
3256: CoreException e = (CoreException) t;
3257: IStatus status = e.getStatus();
3258: if (status.getException() != null)
3259: throw new PartInitException(status);
3260: throw new PartInitException(new Status(status
3261: .getSeverity(), status.getPlugin(), status
3262: .getCode(), status.getMessage(), t));
3263: }
3264: throw new PartInitException(new Status(IStatus.ERROR,
3265: TextEditorPlugin.PLUGIN_ID, IStatus.OK,
3266: EditorMessages.Editor_error_init, t));
3267: }
3268: }
3269:
3270: /*
3271: * @see IEditorPart#init(org.eclipse.ui.IEditorSite, org.eclipse.ui.IEditorInput)
3272: */
3273: public void init(final IEditorSite site, final IEditorInput input)
3274: throws PartInitException {
3275:
3276: setSite(site);
3277:
3278: internalInit(site.getWorkbenchWindow(), site, input);
3279: fActivationListener = new ActivationListener(site
3280: .getWorkbenchWindow().getPartService());
3281: }
3282:
3283: /**
3284: * Creates the vertical ruler to be used by this editor.
3285: * Subclasses may re-implement this method.
3286: *
3287: * @return the vertical ruler
3288: */
3289: protected IVerticalRuler createVerticalRuler() {
3290: return new VerticalRuler(VERTICAL_RULER_WIDTH);
3291: }
3292:
3293: /**
3294: * Adds enabled ruler contributions to the vertical ruler.
3295: * <p>
3296: * Clients may extend or replace.</p>
3297: *
3298: * @param ruler the composite ruler to add contributions to
3299: * @since 3.3
3300: */
3301: protected void updateContributedRulerColumns(CompositeRuler ruler) {
3302: IColumnSupport support = (IColumnSupport) getAdapter(IColumnSupport.class);
3303: if (support == null)
3304: return;
3305:
3306: RulerColumnPreferenceAdapter adapter = null;
3307: if (fPreferenceStore != null)
3308: adapter = new RulerColumnPreferenceAdapter(
3309: getPreferenceStore(),
3310: PREFERENCE_RULER_CONTRIBUTIONS);
3311:
3312: RulerColumnRegistry registry = RulerColumnRegistry.getDefault();
3313: List descriptors = registry.getColumnDescriptors();
3314: for (Iterator it = descriptors.iterator(); it.hasNext();) {
3315: final RulerColumnDescriptor descriptor = (RulerColumnDescriptor) it
3316: .next();
3317: support.setColumnVisible(descriptor, adapter == null
3318: || adapter.isEnabled(descriptor));
3319: }
3320: }
3321:
3322: /**
3323: * Creates the column support to be used by this editor to manage the
3324: * contributed ruler columns.
3325: * Subclasses may re-implement this method using the {@link ColumnSupport},
3326: * e.g. by returning <code>new ColumnSupport(this, RulerColumnRegistry.getDefault());</code>.
3327: * <p>
3328: * <strong>Note:</strong> If you override this method to provide column support you will
3329: * also need to override {@link #createVerticalRuler()} to return a {@link CompositeRuler}.</p>
3330: * <p>
3331: * Out of the box this class does not install this support and hence this
3332: * implementation always returns <code>null</code>.</p>
3333: *
3334: * @return the column support or <code>null</code> if none
3335: * @since 3.3
3336: */
3337: protected IColumnSupport createColumnSupport() {
3338: return null;
3339: }
3340:
3341: /**
3342: * Creates the source viewer to be used by this editor.
3343: * Subclasses may re-implement this method.
3344: *
3345: * @param parent the parent control
3346: * @param ruler the vertical ruler
3347: * @param styles style bits, <code>SWT.WRAP</code> is currently not supported
3348: * @return the source viewer
3349: */
3350: protected ISourceViewer createSourceViewer(Composite parent,
3351: IVerticalRuler ruler, int styles) {
3352: return new SourceViewer(parent, ruler, styles);
3353: }
3354:
3355: /**
3356: * Initializes the drag and drop support for the given viewer based on
3357: * provided editor adapter for drop target listeners.
3358: *
3359: * @param viewer the viewer
3360: * @since 3.0
3361: */
3362: protected void initializeDragAndDrop(ISourceViewer viewer) {
3363: IDragAndDropService dndService = (IDragAndDropService) getSite()
3364: .getService(IDragAndDropService.class);
3365: if (dndService == null)
3366: return;
3367:
3368: ITextEditorDropTargetListener listener = (ITextEditorDropTargetListener) getAdapter(ITextEditorDropTargetListener.class);
3369:
3370: if (listener == null) {
3371: Object object = Platform
3372: .getAdapterManager()
3373: .loadAdapter(this ,
3374: "org.eclipse.ui.texteditor.ITextEditorDropTargetListener"); //$NON-NLS-1$
3375: if (object instanceof ITextEditorDropTargetListener)
3376: listener = (ITextEditorDropTargetListener) object;
3377: }
3378:
3379: if (listener != null)
3380: dndService.addMergedDropTarget(viewer.getTextWidget(),
3381: DND.DROP_MOVE | DND.DROP_COPY, listener
3382: .getTransfers(), listener);
3383:
3384: IPreferenceStore store = getPreferenceStore();
3385: if (store != null
3386: && store
3387: .getBoolean(PREFERENCE_TEXT_DRAG_AND_DROP_ENABLED))
3388: installTextDragAndDrop(viewer);
3389:
3390: }
3391:
3392: /**
3393: * The <code>AbstractTextEditor</code> implementation of this
3394: * <code>IWorkbenchPart</code> method creates the vertical ruler and
3395: * source viewer.
3396: * <p>
3397: * Subclasses may extend this method. Besides extending this method, the
3398: * behavior of <code>createPartControl</code> may be customized by
3399: * calling, extending or replacing the following methods: <br>
3400: * Subclasses may supply customized implementations for some members using
3401: * the following methods before <code>createPartControl</code> is invoked:
3402: * <ul>
3403: * <li>
3404: * {@linkplain #setSourceViewerConfiguration(SourceViewerConfiguration) setSourceViewerConfiguration}
3405: * to supply a custom source viewer configuration,</li>
3406: * <li>{@linkplain #setRangeIndicator(Annotation) setRangeIndicator} to
3407: * provide a range indicator,</li>
3408: * <li>{@linkplain #setHelpContextId(String) setHelpContextId} to provide a
3409: * help context id,</li>
3410: * <li>{@linkplain #setEditorContextMenuId(String) setEditorContextMenuId}
3411: * to set a custom context menu id,</li>
3412: * <li>{@linkplain #setRulerContextMenuId(String) setRulerContextMenuId} to
3413: * set a custom ruler context menu id.</li>
3414: * </ul>
3415: * <br>
3416: * Subclasses may replace the following methods called from within
3417: * <code>createPartControl</code>:
3418: * <ul>
3419: * <li>{@linkplain #createVerticalRuler() createVerticalRuler} to supply a
3420: * custom vertical ruler,</li>
3421: * <li>{@linkplain #createSourceViewer(Composite, IVerticalRuler, int) createSourceViewer}
3422: * to supply a custom source viewer,</li>
3423: * <li>{@linkplain #getSelectionProvider() getSelectionProvider} to supply
3424: * a custom selection provider.</li>
3425: * </ul>
3426: * <br>
3427: * Subclasses may extend the following methods called from within
3428: * <code>createPartControl</code>:
3429: * <ul>
3430: * <li>
3431: * {@linkplain #initializeViewerColors(ISourceViewer) initializeViewerColors}
3432: * to customize the viewer color scheme (may also be replaced),</li>
3433: * <li>
3434: * {@linkplain #initializeDragAndDrop(ISourceViewer) initializeDragAndDrop}
3435: * to customize drag and drop (may also be replaced),</li>
3436: * <li>{@linkplain #createNavigationActions() createNavigationActions} to
3437: * add navigation actions,</li>
3438: * <li>{@linkplain #createActions() createActions} to add text editor
3439: * actions.</li>
3440: * </ul>
3441: * </p>
3442: *
3443: * @param parent the parent composite
3444: */
3445: public void createPartControl(Composite parent) {
3446:
3447: fVerticalRuler = createVerticalRuler();
3448:
3449: int styles = SWT.V_SCROLL | SWT.H_SCROLL | SWT.MULTI
3450: | SWT.BORDER | SWT.FULL_SELECTION;
3451: fSourceViewer = createSourceViewer(parent, fVerticalRuler,
3452: styles);
3453:
3454: if (fConfiguration == null)
3455: fConfiguration = new SourceViewerConfiguration();
3456: fSourceViewer.configure(fConfiguration);
3457:
3458: if (fRangeIndicator != null)
3459: fSourceViewer.setRangeIndicator(fRangeIndicator);
3460:
3461: fSourceViewer.addTextListener(fTextListener);
3462: fSourceViewer.addTextInputListener(fTextListener);
3463: getSelectionProvider().addSelectionChangedListener(
3464: getSelectionChangedListener());
3465:
3466: initializeViewerFont(fSourceViewer);
3467: initializeViewerColors(fSourceViewer);
3468: initializeFindScopeColor(fSourceViewer);
3469: initializeDragAndDrop(fSourceViewer);
3470:
3471: StyledText styledText = fSourceViewer.getTextWidget();
3472:
3473: /* gestures commented out until proper solution (i.e. preference page) can be found
3474: * for bug # 28417:
3475: *
3476: final Map gestureMap= new HashMap();
3477:
3478: gestureMap.put("E", "org.eclipse.ui.navigate.forwardHistory");
3479: gestureMap.put("N", "org.eclipse.ui.file.save");
3480: gestureMap.put("NW", "org.eclipse.ui.file.saveAll");
3481: gestureMap.put("S", "org.eclipse.ui.file.close");
3482: gestureMap.put("SW", "org.eclipse.ui.file.closeAll");
3483: gestureMap.put("W", "org.eclipse.ui.navigate.backwardHistory");
3484: gestureMap.put("EN", "org.eclipse.ui.edit.copy");
3485: gestureMap.put("ES", "org.eclipse.ui.edit.paste");
3486: gestureMap.put("EW", "org.eclipse.ui.edit.cut");
3487:
3488: Capture capture= Capture.create();
3489: capture.setControl(styledText);
3490:
3491: capture.addCaptureListener(new CaptureListener() {
3492: public void gesture(Gesture gesture) {
3493: if (gesture.getPen() == 3) {
3494: String actionId= (String) gestureMap.get(Util.recognize(gesture.getPoints(), 20));
3495:
3496: if (actionId != null) {
3497: IKeyBindingService keyBindingService= getEditorSite().getKeyBindingService();
3498:
3499: if (keyBindingService instanceof KeyBindingService) {
3500: IAction action= ((KeyBindingService) keyBindingService).getAction(actionId);
3501:
3502: if (action != null) {
3503: if (action instanceof IUpdate)
3504: ((IUpdate) action).update();
3505:
3506: if (action.isEnabled())
3507: action.run();
3508: }
3509: }
3510:
3511: return;
3512: }
3513:
3514: fTextContextMenu.setVisible(true);
3515: }
3516: };
3517: });
3518: */
3519:
3520: styledText.addMouseListener(getCursorListener());
3521: styledText.addKeyListener(getCursorListener());
3522:
3523: if (getHelpContextId() != null)
3524: PlatformUI.getWorkbench().getHelpSystem().setHelp(
3525: styledText, getHelpContextId());
3526:
3527: String id = fEditorContextMenuId != null ? fEditorContextMenuId
3528: : DEFAULT_EDITOR_CONTEXT_MENU_ID;
3529:
3530: MenuManager manager = new MenuManager(id, id);
3531: manager.setRemoveAllWhenShown(true);
3532: manager.addMenuListener(getContextMenuListener());
3533: fTextContextMenu = manager.createContextMenu(styledText);
3534:
3535: // comment this line if using gestures, above.
3536: styledText.setMenu(fTextContextMenu);
3537:
3538: if (fEditorContextMenuId != null)
3539: getEditorSite().registerContextMenu(fEditorContextMenuId,
3540: manager, getSelectionProvider(),
3541: isEditorInputIncludedInContextMenu());
3542: else if (fCompatibilityMode)
3543: getEditorSite().registerContextMenu(
3544: DEFAULT_EDITOR_CONTEXT_MENU_ID, manager,
3545: getSelectionProvider(),
3546: isEditorInputIncludedInContextMenu());
3547:
3548: if ((fEditorContextMenuId != null && fCompatibilityMode)
3549: || fEditorContextMenuId == null) {
3550: String partId = getEditorSite().getId();
3551: if (partId != null)
3552: getEditorSite()
3553: .registerContextMenu(
3554: partId + ".EditorContext", manager, getSelectionProvider(), isEditorInputIncludedInContextMenu()); //$NON-NLS-1$
3555: }
3556:
3557: if (fEditorContextMenuId == null)
3558: fEditorContextMenuId = DEFAULT_EDITOR_CONTEXT_MENU_ID;
3559:
3560: id = fRulerContextMenuId != null ? fRulerContextMenuId
3561: : DEFAULT_RULER_CONTEXT_MENU_ID;
3562: manager = new MenuManager(id, id);
3563: manager.setRemoveAllWhenShown(true);
3564: manager.addMenuListener(getContextMenuListener());
3565:
3566: Control rulerControl = fVerticalRuler.getControl();
3567: fRulerContextMenu = manager.createContextMenu(rulerControl);
3568: rulerControl.setMenu(fRulerContextMenu);
3569: rulerControl.addMouseListener(getRulerMouseListener());
3570:
3571: if (fRulerContextMenuId != null)
3572: getEditorSite().registerContextMenu(fRulerContextMenuId,
3573: manager, getSelectionProvider(), false);
3574: else if (fCompatibilityMode)
3575: getEditorSite().registerContextMenu(
3576: DEFAULT_RULER_CONTEXT_MENU_ID, manager,
3577: getSelectionProvider(), false);
3578:
3579: if ((fRulerContextMenuId != null && fCompatibilityMode)
3580: || fRulerContextMenuId == null) {
3581: String partId = getSite().getId();
3582: if (partId != null)
3583: getEditorSite()
3584: .registerContextMenu(
3585: partId + ".RulerContext", manager, getSelectionProvider(), false); //$NON-NLS-1$
3586: }
3587:
3588: if (fRulerContextMenuId == null)
3589: fRulerContextMenuId = DEFAULT_RULER_CONTEXT_MENU_ID;
3590:
3591: getSite().setSelectionProvider(getSelectionProvider());
3592:
3593: fSelectionListener = new SelectionListener();
3594: fSelectionListener.install(getSelectionProvider());
3595: fSelectionListener.setDocument(getDocumentProvider()
3596: .getDocument(getEditorInput()));
3597:
3598: initializeActivationCodeTrigger();
3599:
3600: createNavigationActions();
3601: createAccessibilityActions();
3602: createActions();
3603:
3604: initializeSourceViewer(getEditorInput());
3605:
3606: /* since 3.2 - undo redo actions should be created after
3607: * the source viewer is initialized, so that the undo manager
3608: * can obtain its undo context from its document.
3609: */
3610: createUndoRedoActions();
3611:
3612: JFaceResources.getFontRegistry().addListener(
3613: fFontPropertyChangeListener);
3614:
3615: IVerticalRuler ruler = getVerticalRuler();
3616: if (ruler instanceof CompositeRuler)
3617: updateContributedRulerColumns((CompositeRuler) ruler);
3618:
3619: IInformationControlCreator informationControlCreator = new IInformationControlCreator() {
3620: public IInformationControl createInformationControl(
3621: Shell shell) {
3622: boolean cutDown = false;
3623: int style = cutDown ? SWT.NONE
3624: : (SWT.V_SCROLL | SWT.H_SCROLL);
3625: return new DefaultInformationControl(shell, SWT.RESIZE
3626: | SWT.TOOL, style, new HTMLTextPresenter(
3627: cutDown));
3628: }
3629: };
3630:
3631: fInformationPresenter = new InformationPresenter(
3632: informationControlCreator);
3633: fInformationPresenter.setSizeConstraints(60, 10, true, true);
3634: fInformationPresenter.install(getSourceViewer());
3635: fInformationPresenter
3636: .setDocumentPartitioning(getSourceViewerConfiguration()
3637: .getConfiguredDocumentPartitioning(
3638: getSourceViewer()));
3639:
3640: }
3641:
3642: /**
3643: * Installs text drag and drop on the given source viewer.
3644: *
3645: * @param viewer the viewer
3646: * @since 3.3
3647: */
3648: protected void installTextDragAndDrop(final ISourceViewer viewer) {
3649: if (fIsTextDragAndDropEnabled || viewer == null)
3650: return;
3651:
3652: if (fIsTextDragAndDropInstalled) {
3653: fIsTextDragAndDropEnabled = true;
3654: return;
3655: }
3656:
3657: final IDragAndDropService dndService = (IDragAndDropService) getSite()
3658: .getService(IDragAndDropService.class);
3659: if (dndService == null)
3660: return;
3661:
3662: fIsTextDragAndDropEnabled = true;
3663:
3664: final StyledText st = viewer.getTextWidget();
3665:
3666: // Install drag source
3667: final ISelectionProvider selectionProvider = viewer
3668: .getSelectionProvider();
3669: final DragSource source = new DragSource(st, DND.DROP_COPY
3670: | DND.DROP_MOVE);
3671: source
3672: .setTransfer(new Transfer[] { TextTransfer
3673: .getInstance() });
3674: source.addDragListener(new DragSourceAdapter() {
3675: String fSelectedText;
3676: Point fSelection;
3677:
3678: public void dragStart(DragSourceEvent event) {
3679: fTextDragAndDropToken = null;
3680:
3681: // XXX: This is only a workaround for https://bugs.eclipse.org/bugs/show_bug.cgi?id=162192
3682: if (!fIsTextDragAndDropEnabled) {
3683: event.doit = false;
3684: event.image = null;
3685: return;
3686: }
3687:
3688: try {
3689: fSelection = st.getSelection();
3690: int offset = st.getOffsetAtLocation(new Point(
3691: event.x, event.y));
3692: Point p = st.getLocationAtOffset(offset);
3693: if (p.x > event.x)
3694: offset--;
3695: event.doit = offset > fSelection.x
3696: && offset < fSelection.y;
3697:
3698: ISelection selection = selectionProvider
3699: .getSelection();
3700: if (selection instanceof ITextSelection)
3701: fSelectedText = ((ITextSelection) selection)
3702: .getText();
3703: else
3704: // fallback to widget
3705: fSelectedText = st.getSelectionText();
3706: } catch (IllegalArgumentException ex) {
3707: event.doit = false;
3708: }
3709: }
3710:
3711: public void dragSetData(DragSourceEvent event) {
3712: event.data = fSelectedText;
3713: fTextDragAndDropToken = this ; // Can be any non-null object
3714: }
3715:
3716: public void dragFinished(DragSourceEvent event) {
3717: try {
3718: if (event.detail == DND.DROP_MOVE
3719: && validateEditorInputState()) {
3720: Point newSelection = st.getSelection();
3721: int length = fSelection.y - fSelection.x;
3722: int delta = 0;
3723: if (newSelection.x < fSelection.x)
3724: delta = length;
3725: st.replaceTextRange(fSelection.x + delta,
3726: length, ""); //$NON-NLS-1$
3727:
3728: if (fTextDragAndDropToken == null) {
3729: // Move in same editor - end compound change
3730: IRewriteTarget target = (IRewriteTarget) getAdapter(IRewriteTarget.class);
3731: if (target != null)
3732: target.endCompoundChange();
3733: }
3734:
3735: }
3736: } finally {
3737: fTextDragAndDropToken = null;
3738: }
3739: }
3740: });
3741:
3742: // Install drag target
3743: DropTargetListener dropTargetListener = new DropTargetAdapter() {
3744:
3745: private Point fSelection;
3746:
3747: public void dragEnter(DropTargetEvent event) {
3748: fTextDragAndDropToken = null;
3749: fSelection = st.getSelection();
3750:
3751: // XXX: This is only a workaround for https://bugs.eclipse.org/bugs/show_bug.cgi?id=162192
3752: if (!fIsTextDragAndDropEnabled) {
3753: event.detail = DND.DROP_NONE;
3754: event.feedback = DND.FEEDBACK_NONE;
3755: return;
3756: }
3757:
3758: if (event.detail == DND.DROP_DEFAULT)
3759: event.detail = DND.DROP_MOVE;
3760: }
3761:
3762: public void dragOperationChanged(DropTargetEvent event) {
3763: if (!fIsTextDragAndDropEnabled) {
3764: event.detail = DND.DROP_NONE;
3765: event.feedback = DND.FEEDBACK_NONE;
3766: return;
3767: }
3768:
3769: if (event.detail == DND.DROP_DEFAULT)
3770: event.detail = DND.DROP_MOVE;
3771: }
3772:
3773: public void dragOver(DropTargetEvent event) {
3774:
3775: // XXX: This is only a workaround for https://bugs.eclipse.org/bugs/show_bug.cgi?id=162192
3776: if (!fIsTextDragAndDropEnabled) {
3777: event.feedback = DND.FEEDBACK_NONE;
3778: return;
3779: }
3780:
3781: event.feedback |= DND.FEEDBACK_SCROLL;
3782: }
3783:
3784: public void drop(DropTargetEvent event) {
3785: try {
3786: if (!fIsTextDragAndDropEnabled)
3787: return;
3788:
3789: if (fTextDragAndDropToken != null
3790: && event.detail == DND.DROP_MOVE) {
3791: // Move in same editor
3792: int caretOffset = st.getCaretOffset();
3793: if (fSelection.x <= caretOffset
3794: && caretOffset <= fSelection.y) {
3795: event.detail = DND.DROP_NONE;
3796: return;
3797: }
3798:
3799: // Start compound change
3800: IRewriteTarget target = (IRewriteTarget) getAdapter(IRewriteTarget.class);
3801: if (target != null)
3802: target.beginCompoundChange();
3803: }
3804:
3805: if (!validateEditorInputState()) {
3806: event.detail = DND.DROP_NONE;
3807: return;
3808: }
3809:
3810: String text = (String) event.data;
3811: Point newSelection = st.getSelection();
3812: try {
3813: int modelOffset = widgetOffset2ModelOffset(
3814: viewer, newSelection.x);
3815: viewer.getDocument().replace(modelOffset, 0,
3816: text);
3817: } catch (BadLocationException e) {
3818: return;
3819: }
3820: st.setSelectionRange(newSelection.x, text.length());
3821: } finally {
3822: fTextDragAndDropToken = null;
3823: }
3824: }
3825: };
3826: dndService.addMergedDropTarget(st, DND.DROP_MOVE
3827: | DND.DROP_COPY, new Transfer[] { TextTransfer
3828: .getInstance() }, dropTargetListener);
3829:
3830: fIsTextDragAndDropInstalled = true;
3831: fIsTextDragAndDropEnabled = true;
3832: }
3833:
3834: /**
3835: * Uninstalls text drag and drop from the given source viewer.
3836: *
3837: * @param viewer the viewer
3838: * @since 3.3
3839: */
3840: protected void uninstallTextDragAndDrop(ISourceViewer viewer) {
3841: fIsTextDragAndDropEnabled = false;
3842: }
3843:
3844: /**
3845: * Tells whether the editor input should be included when adding object
3846: * contributions to this editor's context menu.
3847: * <p>
3848: * This implementation always returns <code>true</code>.
3849: * </p>
3850: *
3851: * @return <code>true</code> if the editor input should be considered
3852: * @since 3.2
3853: */
3854: protected boolean isEditorInputIncludedInContextMenu() {
3855: return true;
3856: }
3857:
3858: /**
3859: * Initializes the activation code trigger.
3860: *
3861: * @since 2.1
3862: */
3863: private void initializeActivationCodeTrigger() {
3864: fActivationCodeTrigger.install();
3865: fActivationCodeTrigger.setScopes(fKeyBindingScopes);
3866: }
3867:
3868: /**
3869: * Initializes the given viewer's font.
3870: *
3871: * @param viewer the viewer
3872: * @since 2.0
3873: */
3874: private void initializeViewerFont(ISourceViewer viewer) {
3875:
3876: boolean isSharedFont = true;
3877: Font font = null;
3878: String symbolicFontName = getSymbolicFontName();
3879:
3880: if (symbolicFontName != null)
3881: font = JFaceResources.getFont(symbolicFontName);
3882: else if (fPreferenceStore != null) {
3883: // Backward compatibility
3884: if (fPreferenceStore.contains(JFaceResources.TEXT_FONT)
3885: && !fPreferenceStore
3886: .isDefault(JFaceResources.TEXT_FONT)) {
3887: FontData data = PreferenceConverter.getFontData(
3888: fPreferenceStore, JFaceResources.TEXT_FONT);
3889:
3890: if (data != null) {
3891: isSharedFont = false;
3892: font = new Font(
3893: viewer.getTextWidget().getDisplay(), data);
3894: }
3895: }
3896: }
3897: if (font == null)
3898: font = JFaceResources.getTextFont();
3899:
3900: setFont(viewer, font);
3901:
3902: if (fFont != null) {
3903: fFont.dispose();
3904: fFont = null;
3905: }
3906:
3907: if (!isSharedFont)
3908: fFont = font;
3909: }
3910:
3911: /**
3912: * Sets the font for the given viewer sustaining selection and scroll position.
3913: *
3914: * @param sourceViewer the source viewer
3915: * @param font the font
3916: * @since 2.0
3917: */
3918: private void setFont(ISourceViewer sourceViewer, Font font) {
3919: if (sourceViewer.getDocument() != null) {
3920:
3921: Point selection = sourceViewer.getSelectedRange();
3922: int topIndex = sourceViewer.getTopIndex();
3923:
3924: StyledText styledText = sourceViewer.getTextWidget();
3925: Control parent = styledText;
3926: if (sourceViewer instanceof ITextViewerExtension) {
3927: ITextViewerExtension extension = (ITextViewerExtension) sourceViewer;
3928: parent = extension.getControl();
3929: }
3930:
3931: parent.setRedraw(false);
3932:
3933: styledText.setFont(font);
3934:
3935: if (fVerticalRuler instanceof IVerticalRulerExtension) {
3936: IVerticalRulerExtension e = (IVerticalRulerExtension) fVerticalRuler;
3937: e.setFont(font);
3938: }
3939:
3940: sourceViewer.setSelectedRange(selection.x, selection.y);
3941: sourceViewer.setTopIndex(topIndex);
3942:
3943: if (parent instanceof Composite) {
3944: Composite composite = (Composite) parent;
3945: composite.layout(true);
3946: }
3947:
3948: parent.setRedraw(true);
3949:
3950: } else {
3951:
3952: StyledText styledText = sourceViewer.getTextWidget();
3953: styledText.setFont(font);
3954:
3955: if (fVerticalRuler instanceof IVerticalRulerExtension) {
3956: IVerticalRulerExtension e = (IVerticalRulerExtension) fVerticalRuler;
3957: e.setFont(font);
3958: }
3959: }
3960: }
3961:
3962: /**
3963: * Creates a color from the information stored in the given preference store.
3964: * Returns <code>null</code> if there is no such information available.
3965: *
3966: * @param store the store to read from
3967: * @param key the key used for the lookup in the preference store
3968: * @param display the display used create the color
3969: * @return the created color according to the specification in the preference store
3970: * @since 2.0
3971: */
3972: private Color createColor(IPreferenceStore store, String key,
3973: Display display) {
3974:
3975: RGB rgb = null;
3976:
3977: if (store.contains(key)) {
3978:
3979: if (store.isDefault(key))
3980: rgb = PreferenceConverter.getDefaultColor(store, key);
3981: else
3982: rgb = PreferenceConverter.getColor(store, key);
3983:
3984: if (rgb != null)
3985: return new Color(display, rgb);
3986: }
3987:
3988: return null;
3989: }
3990:
3991: /**
3992: * Initializes the fore- and background colors of the given viewer for both
3993: * normal and selected text.
3994: *
3995: * @param viewer the viewer to be initialized
3996: * @since 2.0
3997: */
3998: protected void initializeViewerColors(ISourceViewer viewer) {
3999:
4000: IPreferenceStore store = getPreferenceStore();
4001: if (store != null) {
4002:
4003: StyledText styledText = viewer.getTextWidget();
4004:
4005: // ----------- foreground color --------------------
4006: Color color = store
4007: .getBoolean(PREFERENCE_COLOR_FOREGROUND_SYSTEM_DEFAULT) ? null
4008: : createColor(store, PREFERENCE_COLOR_FOREGROUND,
4009: styledText.getDisplay());
4010: styledText.setForeground(color);
4011:
4012: if (fForegroundColor != null)
4013: fForegroundColor.dispose();
4014:
4015: fForegroundColor = color;
4016:
4017: // ---------- background color ----------------------
4018: color = store
4019: .getBoolean(PREFERENCE_COLOR_BACKGROUND_SYSTEM_DEFAULT) ? null
4020: : createColor(store, PREFERENCE_COLOR_BACKGROUND,
4021: styledText.getDisplay());
4022: styledText.setBackground(color);
4023:
4024: if (fBackgroundColor != null)
4025: fBackgroundColor.dispose();
4026:
4027: fBackgroundColor = color;
4028:
4029: // ----------- selection foreground color --------------------
4030: color = store
4031: .getBoolean(PREFERENCE_COLOR_SELECTION_FOREGROUND_SYSTEM_DEFAULT) ? null
4032: : createColor(store,
4033: PREFERENCE_COLOR_SELECTION_FOREGROUND,
4034: styledText.getDisplay());
4035: styledText.setSelectionForeground(color);
4036:
4037: if (fSelectionForegroundColor != null)
4038: fSelectionForegroundColor.dispose();
4039:
4040: fSelectionForegroundColor = color;
4041:
4042: // ---------- selection background color ----------------------
4043: color = store
4044: .getBoolean(PREFERENCE_COLOR_SELECTION_BACKGROUND_SYSTEM_DEFAULT) ? null
4045: : createColor(store,
4046: PREFERENCE_COLOR_SELECTION_BACKGROUND,
4047: styledText.getDisplay());
4048: styledText.setSelectionBackground(color);
4049:
4050: if (fSelectionBackgroundColor != null)
4051: fSelectionBackgroundColor.dispose();
4052:
4053: fSelectionBackgroundColor = color;
4054: }
4055: }
4056:
4057: /**
4058: * Initializes the background color used for highlighting the document ranges
4059: * defining search scopes.
4060: *
4061: * @param viewer the viewer to initialize
4062: * @since 2.0
4063: */
4064: private void initializeFindScopeColor(ISourceViewer viewer) {
4065:
4066: IPreferenceStore store = getPreferenceStore();
4067: if (store != null) {
4068:
4069: StyledText styledText = viewer.getTextWidget();
4070:
4071: Color color = createColor(store,
4072: PREFERENCE_COLOR_FIND_SCOPE, styledText
4073: .getDisplay());
4074:
4075: IFindReplaceTarget target = viewer.getFindReplaceTarget();
4076: if (target != null
4077: && target instanceof IFindReplaceTargetExtension)
4078: ((IFindReplaceTargetExtension) target)
4079: .setScopeHighlightColor(color);
4080:
4081: if (fFindScopeHighlightColor != null)
4082: fFindScopeHighlightColor.dispose();
4083:
4084: fFindScopeHighlightColor = color;
4085: }
4086: }
4087:
4088: /**
4089: * Initializes the editor's source viewer based on the given editor input.
4090: *
4091: * @param input the editor input to be used to initialize the source viewer
4092: */
4093: private void initializeSourceViewer(IEditorInput input) {
4094:
4095: IAnnotationModel model = getDocumentProvider()
4096: .getAnnotationModel(input);
4097: IDocument document = getDocumentProvider().getDocument(input);
4098:
4099: if (document != null) {
4100: fSourceViewer.setDocument(document, model);
4101: fSourceViewer.setEditable(isEditable());
4102: fSourceViewer.showAnnotations(model != null);
4103: }
4104:
4105: if (fElementStateListener instanceof IElementStateListenerExtension) {
4106: IElementStateListenerExtension extension = (IElementStateListenerExtension) fElementStateListener;
4107: extension.elementStateValidationChanged(input, false);
4108: }
4109:
4110: if (fInitialCaret == null)
4111: fInitialCaret = fSourceViewer.getTextWidget().getCaret();
4112:
4113: if (fIsOverwriting)
4114: fSourceViewer.getTextWidget().invokeAction(
4115: ST.TOGGLE_OVERWRITE);
4116: handleInsertModeChanged();
4117:
4118: if (isTabsToSpacesConversionEnabled())
4119: installTabsToSpacesConverter();
4120:
4121: }
4122:
4123: /**
4124: * Initializes the editor's title based on the given editor input.
4125: * <p>
4126: * <strong>Note:</strong> We use the editor's image instead of the image from the
4127: * editor input to distinguish situations where the same editor input is
4128: * opened in different kinds of editors.
4129: * </p>
4130: *
4131: * @param input the editor input to be used
4132: */
4133: private void initializeTitle(IEditorInput input) {
4134:
4135: Image oldImage = fTitleImage;
4136: fTitleImage = null;
4137: String title = ""; //$NON-NLS-1$
4138:
4139: if (input != null) {
4140: IEditorRegistry editorRegistry = PlatformUI.getWorkbench()
4141: .getEditorRegistry();
4142: IEditorDescriptor editorDesc = editorRegistry
4143: .findEditor(getSite().getId());
4144: ImageDescriptor imageDesc = editorDesc != null ? editorDesc
4145: .getImageDescriptor() : null;
4146:
4147: fTitleImage = imageDesc != null ? imageDesc.createImage()
4148: : null;
4149: title = input.getName();
4150: }
4151:
4152: setTitleImage(fTitleImage);
4153: setPartName(title);
4154:
4155: firePropertyChange(PROP_DIRTY);
4156:
4157: if (oldImage != null && !oldImage.isDisposed())
4158: oldImage.dispose();
4159: }
4160:
4161: /**
4162: * Hook method for setting the document provider for the given input.
4163: * This default implementation does nothing. Clients may
4164: * reimplement.
4165: *
4166: * @param input the input of this editor.
4167: * @since 3.0
4168: */
4169: protected void setDocumentProvider(IEditorInput input) {
4170: }
4171:
4172: /**
4173: * If there is no explicit document provider set, the implicit one is
4174: * re-initialized based on the given editor input.
4175: *
4176: * @param input the editor input.
4177: */
4178: private void updateDocumentProvider(IEditorInput input) {
4179:
4180: IProgressMonitor rememberedProgressMonitor = null;
4181:
4182: IDocumentProvider provider = getDocumentProvider();
4183: if (provider != null) {
4184: provider.removeElementStateListener(fElementStateListener);
4185: if (provider instanceof IDocumentProviderExtension2) {
4186: IDocumentProviderExtension2 extension = (IDocumentProviderExtension2) provider;
4187: rememberedProgressMonitor = extension
4188: .getProgressMonitor();
4189: extension.setProgressMonitor(null);
4190: }
4191: }
4192:
4193: setDocumentProvider(input);
4194:
4195: provider = getDocumentProvider();
4196: if (provider != null) {
4197: provider.addElementStateListener(fElementStateListener);
4198: if (provider instanceof IDocumentProviderExtension2) {
4199: IDocumentProviderExtension2 extension = (IDocumentProviderExtension2) provider;
4200: extension.setProgressMonitor(rememberedProgressMonitor);
4201: }
4202: }
4203: }
4204:
4205: /**
4206: * Called directly from <code>setInput</code> and from within a workspace
4207: * runnable from <code>init</code>, this method does the actual setting
4208: * of the editor input. Closes the editor if <code>input</code> is
4209: * <code>null</code>. Disconnects from any previous editor input and its
4210: * document provider and connects to the new one.
4211: * <p>
4212: * Subclasses may extend.
4213: * </p>
4214: *
4215: * @param input the input to be set
4216: * @exception CoreException if input cannot be connected to the document
4217: * provider
4218: */
4219: protected void doSetInput(IEditorInput input) throws CoreException {
4220: ISaveablesLifecycleListener listener = (ISaveablesLifecycleListener) getSite()
4221: .getService(ISaveablesLifecycleListener.class);
4222: if (listener == null)
4223: fSavable = null;
4224:
4225: if (input == null) {
4226: close(isSaveOnCloseNeeded());
4227:
4228: if (fSavable != null) {
4229: listener
4230: .handleLifecycleEvent(new SaveablesLifecycleEvent(
4231: this ,
4232: SaveablesLifecycleEvent.POST_CLOSE,
4233: getSaveables(), false));
4234: fSavable.disconnectEditor();
4235: fSavable = null;
4236: }
4237:
4238: } else {
4239: boolean mustSendLifeCycleEvent = false;
4240: if (fSavable != null) {
4241: listener
4242: .handleLifecycleEvent(new SaveablesLifecycleEvent(
4243: this ,
4244: SaveablesLifecycleEvent.POST_CLOSE,
4245: getSaveables(), false));
4246: fSavable.disconnectEditor();
4247: fSavable = null;
4248: mustSendLifeCycleEvent = true;
4249: }
4250:
4251: IEditorInput oldInput = getEditorInput();
4252: if (oldInput != null)
4253: getDocumentProvider().disconnect(oldInput);
4254:
4255: super .setInput(input);
4256:
4257: updateDocumentProvider(input);
4258:
4259: IDocumentProvider provider = getDocumentProvider();
4260: if (provider == null) {
4261: IStatus s = new Status(IStatus.ERROR,
4262: PlatformUI.PLUGIN_ID, IStatus.OK,
4263: EditorMessages.Editor_error_no_provider, null);
4264: throw new CoreException(s);
4265: }
4266:
4267: provider.connect(input);
4268:
4269: initializeTitle(input);
4270:
4271: if (fSourceViewer != null) {
4272: initializeSourceViewer(input);
4273:
4274: // Reset the undo context for the undo and redo action handlers
4275: IAction undoAction = getAction(ITextEditorActionConstants.UNDO);
4276: IAction redoAction = getAction(ITextEditorActionConstants.REDO);
4277: boolean areOperationActionHandlersInstalled = undoAction instanceof OperationHistoryActionHandler
4278: && redoAction instanceof OperationHistoryActionHandler;
4279: IUndoContext undoContext = getUndoContext();
4280: if (undoContext != null
4281: && areOperationActionHandlersInstalled) {
4282: ((OperationHistoryActionHandler) undoAction)
4283: .setContext(undoContext);
4284: ((OperationHistoryActionHandler) redoAction)
4285: .setContext(undoContext);
4286: } else {
4287: createUndoRedoActions();
4288: }
4289: }
4290:
4291: if (fIsOverwriting)
4292: toggleOverwriteMode();
4293: setInsertMode((InsertMode) getLegalInsertModes().get(0));
4294: updateCaret();
4295:
4296: updateStatusField(ITextEditorActionConstants.STATUS_CATEGORY_ELEMENT_STATE);
4297:
4298: if (fSelectionListener != null)
4299: fSelectionListener.setDocument(getDocumentProvider()
4300: .getDocument(input));
4301:
4302: IVerticalRuler ruler = getVerticalRuler();
4303: if (ruler instanceof CompositeRuler)
4304: updateContributedRulerColumns((CompositeRuler) ruler);
4305:
4306: // Send savable life-cycle if needed.
4307: if (mustSendLifeCycleEvent && listener != null)
4308: listener
4309: .handleLifecycleEvent(new SaveablesLifecycleEvent(
4310: this ,
4311: SaveablesLifecycleEvent.POST_OPEN,
4312: getSaveables(), false));
4313:
4314: }
4315:
4316: }
4317:
4318: /**
4319: * Returns this editor's viewer's undo manager undo context.
4320: *
4321: * @return the undo context or <code>null</code> if not available
4322: * @since 3.1
4323: */
4324: private IUndoContext getUndoContext() {
4325: if (fSourceViewer instanceof ITextViewerExtension6) {
4326: IUndoManager undoManager = ((ITextViewerExtension6) fSourceViewer)
4327: .getUndoManager();
4328: if (undoManager instanceof IUndoManagerExtension)
4329: return ((IUndoManagerExtension) undoManager)
4330: .getUndoContext();
4331: }
4332: return null;
4333: }
4334:
4335: /*
4336: * @see org.eclipse.ui.part.EditorPart#setInputWithNotify(org.eclipse.ui.IEditorInput)
4337: * @since 3.2
4338: */
4339: protected final void setInputWithNotify(IEditorInput input) {
4340: try {
4341:
4342: doSetInput(input);
4343:
4344: /*
4345: * The following bugs explain why we fire this property change:
4346: * https://bugs.eclipse.org/bugs/show_bug.cgi?id=90283
4347: * https://bugs.eclipse.org/bugs/show_bug.cgi?id=92049
4348: * https://bugs.eclipse.org/bugs/show_bug.cgi?id=92286
4349: */
4350: firePropertyChange(IEditorPart.PROP_INPUT);
4351:
4352: } catch (CoreException x) {
4353: String title = EditorMessages.Editor_error_setinput_title;
4354: String msg = EditorMessages.Editor_error_setinput_message;
4355: Shell shell = getSite().getShell();
4356: ErrorDialog.openError(shell, title, msg, x.getStatus());
4357: }
4358: }
4359:
4360: /*
4361: * @see EditorPart#setInput(org.eclipse.ui.IEditorInput)
4362: */
4363: public final void setInput(IEditorInput input) {
4364: setInputWithNotify(input);
4365: }
4366:
4367: /*
4368: * @see ITextEditor#close
4369: */
4370: public void close(final boolean save) {
4371:
4372: enableSanityChecking(false);
4373:
4374: Display display = getSite().getShell().getDisplay();
4375: display.asyncExec(new Runnable() {
4376: public void run() {
4377: if (fSourceViewer != null)
4378: getSite().getPage().closeEditor(
4379: AbstractTextEditor.this , save);
4380: }
4381: });
4382: }
4383:
4384: /**
4385: * The <code>AbstractTextEditor</code> implementation of this
4386: * <code>IWorkbenchPart</code> method may be extended by subclasses.
4387: * Subclasses must call <code>super.dispose()</code>.
4388: * <p>
4389: * Note that many methods may return <code>null</code> after the editor is
4390: * disposed.
4391: * </p>
4392: */
4393: public void dispose() {
4394:
4395: if (fActivationListener != null) {
4396: fActivationListener.dispose();
4397: fActivationListener = null;
4398: }
4399:
4400: if (fTitleImage != null) {
4401: fTitleImage.dispose();
4402: fTitleImage = null;
4403: }
4404:
4405: if (fFont != null) {
4406: fFont.dispose();
4407: fFont = null;
4408: }
4409:
4410: disposeNonDefaultCaret();
4411: fInitialCaret = null;
4412:
4413: if (fForegroundColor != null) {
4414: fForegroundColor.dispose();
4415: fForegroundColor = null;
4416: }
4417:
4418: if (fBackgroundColor != null) {
4419: fBackgroundColor.dispose();
4420: fBackgroundColor = null;
4421: }
4422:
4423: if (fSelectionForegroundColor != null) {
4424: fSelectionForegroundColor.dispose();
4425: fSelectionForegroundColor = null;
4426: }
4427:
4428: if (fSelectionBackgroundColor != null) {
4429: fSelectionBackgroundColor.dispose();
4430: fSelectionBackgroundColor = null;
4431: }
4432:
4433: if (fFindScopeHighlightColor != null) {
4434: fFindScopeHighlightColor.dispose();
4435: fFindScopeHighlightColor = null;
4436: }
4437:
4438: if (fFontPropertyChangeListener != null) {
4439: JFaceResources.getFontRegistry().removeListener(
4440: fFontPropertyChangeListener);
4441: fFontPropertyChangeListener = null;
4442: }
4443:
4444: if (fPropertyChangeListener != null) {
4445: if (fPreferenceStore != null) {
4446: fPreferenceStore
4447: .removePropertyChangeListener(fPropertyChangeListener);
4448: fPreferenceStore = null;
4449: }
4450: fPropertyChangeListener = null;
4451: }
4452:
4453: if (fActivationCodeTrigger != null) {
4454: fActivationCodeTrigger.uninstall();
4455: fActivationCodeTrigger = null;
4456: }
4457:
4458: if (fSelectionListener != null) {
4459: fSelectionListener.uninstall(getSelectionProvider());
4460: fSelectionListener = null;
4461: }
4462:
4463: if (fSavable != null) {
4464: fSavable.disconnectEditor();
4465: fSavable = null;
4466: }
4467:
4468: disposeDocumentProvider();
4469:
4470: if (fSourceViewer != null) {
4471:
4472: if (fTextListener != null) {
4473: fSourceViewer.removeTextListener(fTextListener);
4474: fSourceViewer.removeTextInputListener(fTextListener);
4475: fTextListener = null;
4476: }
4477:
4478: uninstallTabsToSpacesConverter();
4479:
4480: fTextInputListener = null;
4481: fSelectionProvider = null;
4482: fSourceViewer = null;
4483: }
4484:
4485: if (fTextContextMenu != null) {
4486: fTextContextMenu.dispose();
4487: fTextContextMenu = null;
4488: }
4489:
4490: if (fRulerContextMenu != null) {
4491: fRulerContextMenu.dispose();
4492: fRulerContextMenu = null;
4493: }
4494:
4495: if (fActions != null) {
4496: fActions.clear();
4497: fActions = null;
4498: }
4499:
4500: if (fSelectionActions != null) {
4501: fSelectionActions.clear();
4502: fSelectionActions = null;
4503: }
4504:
4505: if (fContentActions != null) {
4506: fContentActions.clear();
4507: fContentActions = null;
4508: }
4509:
4510: if (fPropertyActions != null) {
4511: fPropertyActions.clear();
4512: fPropertyActions = null;
4513: }
4514:
4515: if (fStateActions != null) {
4516: fStateActions.clear();
4517: fStateActions = null;
4518: }
4519:
4520: if (fActivationCodes != null) {
4521: fActivationCodes.clear();
4522: fActivationCodes = null;
4523: }
4524:
4525: if (fEditorStatusLine != null)
4526: fEditorStatusLine = null;
4527:
4528: if (fConfiguration != null)
4529: fConfiguration = null;
4530:
4531: if (fColumnSupport != null) {
4532: fColumnSupport.dispose();
4533: fColumnSupport = null;
4534: }
4535:
4536: if (fVerticalRuler != null)
4537: fVerticalRuler = null;
4538:
4539: IOperationHistory history = OperationHistoryFactory
4540: .getOperationHistory();
4541: if (history != null) {
4542: if (fNonLocalOperationApprover != null)
4543: history
4544: .removeOperationApprover(fNonLocalOperationApprover);
4545: if (fLinearUndoViolationApprover != null)
4546: history
4547: .removeOperationApprover(fLinearUndoViolationApprover);
4548: }
4549: fNonLocalOperationApprover = null;
4550: fLinearUndoViolationApprover = null;
4551:
4552: super .dispose();
4553: }
4554:
4555: /**
4556: * Disposes of the connection with the document provider. Subclasses
4557: * may extend.
4558: *
4559: * @since 3.0
4560: */
4561: protected void disposeDocumentProvider() {
4562: IDocumentProvider provider = getDocumentProvider();
4563: if (provider != null) {
4564:
4565: IEditorInput input = getEditorInput();
4566: if (input != null)
4567: provider.disconnect(input);
4568:
4569: if (fElementStateListener != null) {
4570: provider
4571: .removeElementStateListener(fElementStateListener);
4572: fElementStateListener = null;
4573: }
4574:
4575: }
4576: fExplicitDocumentProvider = null;
4577: }
4578:
4579: /**
4580: * Determines whether the given preference change affects the editor's
4581: * presentation. This implementation always returns <code>false</code>.
4582: * May be reimplemented by subclasses.
4583: *
4584: * @param event the event which should be investigated
4585: * @return <code>true</code> if the event describes a preference change affecting the editor's presentation
4586: * @since 2.0
4587: */
4588: protected boolean affectsTextPresentation(PropertyChangeEvent event) {
4589: return false;
4590: }
4591:
4592: /**
4593: * Returns the symbolic font name for this editor as defined in XML.
4594: *
4595: * @return a String with the symbolic font name or <code>null</code> if
4596: * none is defined
4597: * @since 2.1
4598: */
4599: private String getSymbolicFontName() {
4600: if (getConfigurationElement() != null)
4601: return getConfigurationElement().getAttribute(
4602: "symbolicFontName"); //$NON-NLS-1$
4603: return null;
4604: }
4605:
4606: /**
4607: * Returns the property preference key for the editor font. Subclasses may
4608: * replace this method.
4609: *
4610: * @return a String with the key
4611: * @since 2.1
4612: */
4613: protected final String getFontPropertyPreferenceKey() {
4614: String symbolicFontName = getSymbolicFontName();
4615: if (symbolicFontName != null)
4616: return symbolicFontName;
4617: return JFaceResources.TEXT_FONT;
4618: }
4619:
4620: /**
4621: * Handles a property change event describing a change of the editor's
4622: * preference store and updates the preference related editor properties.
4623: * <p>
4624: * Subclasses may extend.
4625: * </p>
4626: *
4627: * @param event the property change event
4628: */
4629: protected void handlePreferenceStoreChanged(
4630: PropertyChangeEvent event) {
4631:
4632: if (fSourceViewer == null)
4633: return;
4634:
4635: String property = event.getProperty();
4636:
4637: if (getFontPropertyPreferenceKey().equals(property))
4638: // There is a separate handler for font preference changes
4639: return;
4640:
4641: if (PREFERENCE_COLOR_FOREGROUND.equals(property)
4642: || PREFERENCE_COLOR_FOREGROUND_SYSTEM_DEFAULT
4643: .equals(property)
4644: || PREFERENCE_COLOR_BACKGROUND.equals(property)
4645: || PREFERENCE_COLOR_BACKGROUND_SYSTEM_DEFAULT
4646: .equals(property)
4647: || PREFERENCE_COLOR_SELECTION_FOREGROUND
4648: .equals(property)
4649: || PREFERENCE_COLOR_SELECTION_FOREGROUND_SYSTEM_DEFAULT
4650: .equals(property)
4651: || PREFERENCE_COLOR_SELECTION_BACKGROUND
4652: .equals(property)
4653: || PREFERENCE_COLOR_SELECTION_BACKGROUND_SYSTEM_DEFAULT
4654: .equals(property)) {
4655: initializeViewerColors(fSourceViewer);
4656: } else if (PREFERENCE_COLOR_FIND_SCOPE.equals(property)) {
4657: initializeFindScopeColor(fSourceViewer);
4658: } else if (PREFERENCE_USE_CUSTOM_CARETS.equals(property)) {
4659: updateCaret();
4660: } else if (PREFERENCE_WIDE_CARET.equals(property)) {
4661: updateCaret();
4662: }
4663:
4664: if (affectsTextPresentation(event))
4665: fSourceViewer.invalidateTextPresentation();
4666:
4667: if (PREFERENCE_HYPERLINKS_ENABLED.equals(property)) {
4668: if (fSourceViewer instanceof ITextViewerExtension6) {
4669: IHyperlinkDetector[] detectors = getSourceViewerConfiguration()
4670: .getHyperlinkDetectors(fSourceViewer);
4671: int stateMask = getSourceViewerConfiguration()
4672: .getHyperlinkStateMask(fSourceViewer);
4673: ITextViewerExtension6 textViewer6 = (ITextViewerExtension6) fSourceViewer;
4674: textViewer6.setHyperlinkDetectors(detectors, stateMask);
4675: }
4676: return;
4677: }
4678:
4679: if (PREFERENCE_HYPERLINK_KEY_MODIFIER.equals(property)) {
4680: if (fSourceViewer instanceof ITextViewerExtension6) {
4681: ITextViewerExtension6 textViewer6 = (ITextViewerExtension6) fSourceViewer;
4682: IHyperlinkDetector[] detectors = getSourceViewerConfiguration()
4683: .getHyperlinkDetectors(fSourceViewer);
4684: int stateMask = getSourceViewerConfiguration()
4685: .getHyperlinkStateMask(fSourceViewer);
4686: textViewer6.setHyperlinkDetectors(detectors, stateMask);
4687: }
4688: return;
4689: }
4690:
4691: if (PREFERENCE_RULER_CONTRIBUTIONS.equals(property)) {
4692: String[] difference = StringSetSerializer.getDifference(
4693: (String) event.getOldValue(), (String) event
4694: .getNewValue());
4695: IColumnSupport support = (IColumnSupport) getAdapter(IColumnSupport.class);
4696: for (int i = 0; i < difference.length; i++) {
4697: RulerColumnDescriptor desc = RulerColumnRegistry
4698: .getDefault()
4699: .getColumnDescriptor(difference[i]);
4700: if (desc != null && support.isColumnSupported(desc)) {
4701: boolean newState = !support.isColumnVisible(desc);
4702: support.setColumnVisible(desc, newState);
4703: }
4704: }
4705: return;
4706: }
4707:
4708: if (PREFERENCE_SHOW_WHITESPACE_CHARACTERS.equals(property)) {
4709: IAction action = getAction(ITextEditorActionConstants.SHOW_WHITESPACE_CHARACTERS);
4710: if (action instanceof IUpdate)
4711: ((IUpdate) action).update();
4712: return;
4713: }
4714:
4715: if (PREFERENCE_TEXT_DRAG_AND_DROP_ENABLED.equals(property)) {
4716: IPreferenceStore store = getPreferenceStore();
4717: if (store != null
4718: && store
4719: .getBoolean(PREFERENCE_TEXT_DRAG_AND_DROP_ENABLED))
4720: installTextDragAndDrop(getSourceViewer());
4721: else
4722: uninstallTextDragAndDrop(getSourceViewer());
4723: return;
4724: }
4725:
4726: }
4727:
4728: /**
4729: * Returns the progress monitor related to this editor. It should not be
4730: * necessary to extend this method.
4731: *
4732: * @return the progress monitor related to this editor
4733: * @since 2.1
4734: */
4735: protected IProgressMonitor getProgressMonitor() {
4736:
4737: IProgressMonitor pm = null;
4738:
4739: IStatusLineManager manager = getStatusLineManager();
4740: if (manager != null)
4741: pm = manager.getProgressMonitor();
4742:
4743: return pm != null ? pm : new NullProgressMonitor();
4744: }
4745:
4746: /**
4747: * Handles an external change of the editor's input element. Subclasses may
4748: * extend.
4749: */
4750: protected void handleEditorInputChanged() {
4751:
4752: String title;
4753: String msg;
4754: Shell shell = getSite().getShell();
4755:
4756: final IDocumentProvider provider = getDocumentProvider();
4757: if (provider == null) {
4758: // fix for http://dev.eclipse.org/bugs/show_bug.cgi?id=15066
4759: close(false);
4760: return;
4761: }
4762:
4763: final IEditorInput input = getEditorInput();
4764: if (provider.isDeleted(input)) {
4765:
4766: if (isSaveAsAllowed()) {
4767:
4768: title = EditorMessages.Editor_error_activated_deleted_save_title;
4769: msg = EditorMessages.Editor_error_activated_deleted_save_message;
4770:
4771: String[] buttons = {
4772: EditorMessages.Editor_error_activated_deleted_save_button_save,
4773: EditorMessages.Editor_error_activated_deleted_save_button_close, };
4774:
4775: MessageDialog dialog = new MessageDialog(shell, title,
4776: null, msg, MessageDialog.QUESTION, buttons, 0);
4777:
4778: if (dialog.open() == 0) {
4779: IProgressMonitor pm = getProgressMonitor();
4780: performSaveAs(pm);
4781: if (pm.isCanceled())
4782: handleEditorInputChanged();
4783: } else {
4784: close(false);
4785: }
4786:
4787: } else {
4788:
4789: title = EditorMessages.Editor_error_activated_deleted_close_title;
4790: msg = EditorMessages.Editor_error_activated_deleted_close_message;
4791: if (MessageDialog.openConfirm(shell, title, msg))
4792: close(false);
4793: }
4794:
4795: } else {
4796:
4797: title = EditorMessages.Editor_error_activated_outofsync_title;
4798: msg = EditorMessages.Editor_error_activated_outofsync_message;
4799:
4800: if (MessageDialog.openQuestion(shell, title, msg)) {
4801:
4802: try {
4803: if (provider instanceof IDocumentProviderExtension) {
4804: IDocumentProviderExtension extension = (IDocumentProviderExtension) provider;
4805: extension.synchronize(input);
4806: } else {
4807: doSetInput(input);
4808: }
4809: } catch (CoreException x) {
4810: IStatus status = x.getStatus();
4811: if (status == null
4812: || status.getSeverity() != IStatus.CANCEL) {
4813: title = EditorMessages.Editor_error_refresh_outofsync_title;
4814: msg = EditorMessages.Editor_error_refresh_outofsync_message;
4815: ErrorDialog.openError(shell, title, msg, x
4816: .getStatus());
4817: }
4818: }
4819: }
4820: }
4821: }
4822:
4823: /**
4824: * The <code>AbstractTextEditor</code> implementation of this
4825: * <code>IEditorPart</code> method calls <code>performSaveAs</code>.
4826: * Subclasses may reimplement.
4827: */
4828: public void doSaveAs() {
4829: /*
4830: * 1GEUSSR: ITPUI:ALL - User should never loose changes made in the editors.
4831: * Changed Behavior to make sure that if called inside a regular save (because
4832: * of deletion of input element) there is a way to report back to the caller.
4833: */
4834: performSaveAs(getProgressMonitor());
4835: }
4836:
4837: /**
4838: * Performs a save as and reports the result state back to the
4839: * given progress monitor. This default implementation does nothing.
4840: * Subclasses may reimplement.
4841: *
4842: * @param progressMonitor the progress monitor for communicating result state or <code>null</code>
4843: */
4844: protected void performSaveAs(IProgressMonitor progressMonitor) {
4845: }
4846:
4847: /**
4848: * The <code>AbstractTextEditor</code> implementation of this
4849: * <code>IEditorPart</code> method may be extended by subclasses.
4850: *
4851: * @param progressMonitor the progress monitor for communicating result state or <code>null</code>
4852: */
4853: public void doSave(IProgressMonitor progressMonitor) {
4854:
4855: IDocumentProvider p = getDocumentProvider();
4856: if (p == null)
4857: return;
4858:
4859: if (p.isDeleted(getEditorInput())) {
4860:
4861: if (isSaveAsAllowed()) {
4862:
4863: /*
4864: * 1GEUSSR: ITPUI:ALL - User should never loose changes made in the editors.
4865: * Changed Behavior to make sure that if called inside a regular save (because
4866: * of deletion of input element) there is a way to report back to the caller.
4867: */
4868: performSaveAs(progressMonitor);
4869:
4870: } else {
4871:
4872: Shell shell = getSite().getShell();
4873: String title = EditorMessages.Editor_error_save_deleted_title;
4874: String msg = EditorMessages.Editor_error_save_deleted_message;
4875: MessageDialog.openError(shell, title, msg);
4876: }
4877:
4878: } else {
4879: updateState(getEditorInput());
4880: validateState(getEditorInput());
4881: performSave(false, progressMonitor);
4882: }
4883: }
4884:
4885: /**
4886: * Enables/disables sanity checking.
4887: * @param enable <code>true</code> if sanity checking should be enabled, <code>false</code> otherwise
4888: * @since 2.0
4889: */
4890: protected void enableSanityChecking(boolean enable) {
4891: synchronized (this ) {
4892: fIsSanityCheckEnabled = enable;
4893: }
4894: }
4895:
4896: /**
4897: * Checks the state of the given editor input if sanity checking is enabled.
4898: * @param input the editor input whose state is to be checked
4899: * @since 2.0
4900: */
4901: protected void safelySanityCheckState(IEditorInput input) {
4902: boolean enabled = false;
4903:
4904: synchronized (this ) {
4905: enabled = fIsSanityCheckEnabled;
4906: }
4907:
4908: if (enabled)
4909: sanityCheckState(input);
4910: }
4911:
4912: /**
4913: * Checks the state of the given editor input.
4914: * @param input the editor input whose state is to be checked
4915: * @since 2.0
4916: */
4917: protected void sanityCheckState(IEditorInput input) {
4918:
4919: IDocumentProvider p = getDocumentProvider();
4920: if (p == null)
4921: return;
4922:
4923: if (p instanceof IDocumentProviderExtension3) {
4924:
4925: IDocumentProviderExtension3 p3 = (IDocumentProviderExtension3) p;
4926:
4927: long stamp = p.getModificationStamp(input);
4928: if (stamp != fModificationStamp) {
4929: fModificationStamp = stamp;
4930: if (!p3.isSynchronized(input))
4931: handleEditorInputChanged();
4932: }
4933:
4934: } else {
4935:
4936: if (fModificationStamp == -1)
4937: fModificationStamp = p.getSynchronizationStamp(input);
4938:
4939: long stamp = p.getModificationStamp(input);
4940: if (stamp != fModificationStamp) {
4941: fModificationStamp = stamp;
4942: if (stamp != p.getSynchronizationStamp(input))
4943: handleEditorInputChanged();
4944: }
4945: }
4946:
4947: updateState(getEditorInput());
4948: updateStatusField(ITextEditorActionConstants.STATUS_CATEGORY_ELEMENT_STATE);
4949: }
4950:
4951: /**
4952: * Enables/disables state validation.
4953: * @param enable <code>true</code> if state validation should be enabled, <code>false</code> otherwise
4954: * @since 2.1
4955: */
4956: protected void enableStateValidation(boolean enable) {
4957: synchronized (this ) {
4958: fIsStateValidationEnabled = enable;
4959: }
4960: }
4961:
4962: /**
4963: * Validates the state of the given editor input. The predominate intent
4964: * of this method is to take any action probably necessary to ensure that
4965: * the input can persistently be changed.
4966: *
4967: * @param input the input to be validated
4968: * @since 2.0
4969: */
4970: protected void validateState(IEditorInput input) {
4971:
4972: IDocumentProvider provider = getDocumentProvider();
4973: if (!(provider instanceof IDocumentProviderExtension))
4974: return;
4975:
4976: IDocumentProviderExtension extension = (IDocumentProviderExtension) provider;
4977:
4978: try {
4979:
4980: extension.validateState(input, getSite().getShell());
4981:
4982: } catch (CoreException x) {
4983: IStatus status = x.getStatus();
4984: if (status == null
4985: || status.getSeverity() != IStatus.CANCEL) {
4986: Bundle bundle = Platform
4987: .getBundle(PlatformUI.PLUGIN_ID);
4988: ILog log = Platform.getLog(bundle);
4989: log.log(x.getStatus());
4990:
4991: Shell shell = getSite().getShell();
4992: String title = EditorMessages.Editor_error_validateEdit_title;
4993: String msg = EditorMessages.Editor_error_validateEdit_message;
4994: ErrorDialog.openError(shell, title, msg, x.getStatus());
4995: }
4996: return;
4997: }
4998:
4999: if (fSourceViewer != null)
5000: fSourceViewer.setEditable(isEditable());
5001:
5002: updateStateDependentActions();
5003: }
5004:
5005: /*
5006: * @see org.eclipse.ui.texteditor.ITextEditorExtension2#validateEditorInputState()
5007: * @since 2.1
5008: */
5009: public boolean validateEditorInputState() {
5010:
5011: boolean enabled = false;
5012:
5013: synchronized (this ) {
5014: enabled = fIsStateValidationEnabled;
5015: }
5016:
5017: if (enabled) {
5018:
5019: ISourceViewer viewer = fSourceViewer;
5020: if (viewer == null)
5021: return false;
5022:
5023: fTextInputListener.inputChanged = false;
5024: viewer.addTextInputListener(fTextInputListener);
5025:
5026: try {
5027: final IEditorInput input = getEditorInput();
5028: BusyIndicator.showWhile(getSite().getShell()
5029: .getDisplay(), new Runnable() {
5030: /*
5031: * @see java.lang.Runnable#run()
5032: */
5033: public void run() {
5034: validateState(input);
5035: }
5036: });
5037: sanityCheckState(input);
5038: return !isEditorInputReadOnly()
5039: && !fTextInputListener.inputChanged;
5040:
5041: } finally {
5042: viewer.removeTextInputListener(fTextInputListener);
5043: }
5044:
5045: }
5046:
5047: return !isEditorInputReadOnly();
5048: }
5049:
5050: /**
5051: * Updates the state of the given editor input such as read-only flag.
5052: *
5053: * @param input the input to be validated
5054: * @since 2.0
5055: */
5056: protected void updateState(IEditorInput input) {
5057: IDocumentProvider provider = getDocumentProvider();
5058: if (provider instanceof IDocumentProviderExtension) {
5059: IDocumentProviderExtension extension = (IDocumentProviderExtension) provider;
5060: try {
5061:
5062: boolean wasReadOnly = isEditorInputReadOnly();
5063: extension.updateStateCache(input);
5064:
5065: if (fSourceViewer != null)
5066: fSourceViewer.setEditable(isEditable());
5067:
5068: if (wasReadOnly != isEditorInputReadOnly())
5069: updateStateDependentActions();
5070:
5071: } catch (CoreException x) {
5072: Bundle bundle = Platform
5073: .getBundle(PlatformUI.PLUGIN_ID);
5074: ILog log = Platform.getLog(bundle);
5075: log.log(x.getStatus());
5076: }
5077: }
5078: }
5079:
5080: /**
5081: * Performs the save and handles errors appropriately.
5082: *
5083: * @param overwrite indicates whether or not overwriting is allowed
5084: * @param progressMonitor the monitor in which to run the operation
5085: * @since 3.0
5086: */
5087: protected void performSave(boolean overwrite,
5088: IProgressMonitor progressMonitor) {
5089:
5090: IDocumentProvider provider = getDocumentProvider();
5091: if (provider == null)
5092: return;
5093:
5094: try {
5095:
5096: provider.aboutToChange(getEditorInput());
5097: IEditorInput input = getEditorInput();
5098: provider
5099: .saveDocument(progressMonitor, input,
5100: getDocumentProvider().getDocument(input),
5101: overwrite);
5102: editorSaved();
5103:
5104: } catch (CoreException x) {
5105: IStatus status = x.getStatus();
5106: if (status == null
5107: || status.getSeverity() != IStatus.CANCEL)
5108: handleExceptionOnSave(x, progressMonitor);
5109: } finally {
5110: provider.changed(getEditorInput());
5111: }
5112: }
5113:
5114: /**
5115: * Handles the given exception. If the exception reports an out-of-sync
5116: * situation, this is reported to the user. Otherwise, the exception
5117: * is generically reported.
5118: *
5119: * @param exception the exception to handle
5120: * @param progressMonitor the progress monitor
5121: */
5122: protected void handleExceptionOnSave(CoreException exception,
5123: IProgressMonitor progressMonitor) {
5124:
5125: try {
5126: ++fErrorCorrectionOnSave;
5127:
5128: boolean isSynchronized = false;
5129: IDocumentProvider p = getDocumentProvider();
5130:
5131: if (p instanceof IDocumentProviderExtension3) {
5132: IDocumentProviderExtension3 p3 = (IDocumentProviderExtension3) p;
5133: isSynchronized = p3.isSynchronized(getEditorInput());
5134: } else {
5135: long modifiedStamp = p
5136: .getModificationStamp(getEditorInput());
5137: long synchStamp = p
5138: .getSynchronizationStamp(getEditorInput());
5139: isSynchronized = (modifiedStamp == synchStamp);
5140: }
5141:
5142: if (isNotSynchronizedException(exception)
5143: && fErrorCorrectionOnSave == 1 && !isSynchronized) {
5144: String title = EditorMessages.Editor_error_save_outofsync_title;
5145: String msg = NLSUtility
5146: .format(
5147: EditorMessages.Editor_error_save_outofsync_message,
5148: getEditorInput().getToolTipText());
5149:
5150: if (MessageDialog.openQuestion(getSite().getShell(),
5151: title, msg))
5152: performSave(true, progressMonitor);
5153: else {
5154: /*
5155: * 1GEUPKR: ITPJUI:ALL - Loosing work with simultaneous edits
5156: * Set progress monitor to canceled in order to report back
5157: * to enclosing operations.
5158: */
5159: if (progressMonitor != null)
5160: progressMonitor.setCanceled(true);
5161: }
5162: } else {
5163: String title = EditorMessages.Editor_error_save_title;
5164: String msg = EditorMessages.Editor_error_save_message;
5165: openSaveErrorDialog(title, msg, exception);
5166:
5167: /*
5168: * 1GEUPKR: ITPJUI:ALL - Loosing work with simultaneous edits
5169: * Set progress monitor to canceled in order to report back
5170: * to enclosing operations.
5171: */
5172: if (progressMonitor != null)
5173: progressMonitor.setCanceled(true);
5174: }
5175: } finally {
5176: --fErrorCorrectionOnSave;
5177: }
5178: }
5179:
5180: /**
5181: * Presents an error dialog to the user when a problem
5182: * happens during save.
5183: * <p>
5184: * Subclasses can decide to override the given title and message.
5185: * </p>
5186: *
5187: * @param title the dialog title
5188: * @param message the message to display
5189: * @param exception the exception to handle
5190: * @since 3.3
5191: */
5192: protected void openSaveErrorDialog(String title, String message,
5193: CoreException exception) {
5194: ErrorDialog.openError(getSite().getShell(), title, message,
5195: exception.getStatus());
5196: }
5197:
5198: /**
5199: * Tells whether the given core exception is exactly the
5200: * exception which is thrown for a non-synchronized element.
5201: *
5202: * @param ex the core exception
5203: * @return <code>true</code> iff the given core exception is exactly the
5204: * exception which is thrown for a non-synchronized element
5205: * @since 3.1
5206: */
5207: private boolean isNotSynchronizedException(CoreException ex) {
5208: IDocumentProvider provider = getDocumentProvider();
5209: if (provider instanceof IDocumentProviderExtension5)
5210: return ((IDocumentProviderExtension5) provider)
5211: .isNotSynchronizedException(getEditorInput(), ex);
5212: return false;
5213: }
5214:
5215: /**
5216: * The <code>AbstractTextEditor</code> implementation of this
5217: * <code>IEditorPart</code> method returns <code>false</code>.
5218: * Subclasses may override.
5219: *
5220: * @return <code>false</code>
5221: */
5222: public boolean isSaveAsAllowed() {
5223: return false;
5224: }
5225:
5226: /*
5227: * @see EditorPart#isDirty()
5228: */
5229: public boolean isDirty() {
5230: IDocumentProvider p = getDocumentProvider();
5231: return p == null ? false : p.canSaveDocument(getEditorInput());
5232: }
5233:
5234: /**
5235: * The <code>AbstractTextEditor</code> implementation of this
5236: * <code>ITextEditor</code> method may be extended by subclasses.
5237: */
5238: public void doRevertToSaved() {
5239: IDocumentProvider p = getDocumentProvider();
5240: if (p == null)
5241: return;
5242:
5243: performRevert();
5244: }
5245:
5246: /**
5247: * Performs revert and handles errors appropriately.
5248: * <p>
5249: * Subclasses may extend.
5250: * </p>
5251: *
5252: * @since 3.0
5253: */
5254: protected void performRevert() {
5255:
5256: IDocumentProvider provider = getDocumentProvider();
5257: if (provider == null)
5258: return;
5259:
5260: try {
5261:
5262: provider.aboutToChange(getEditorInput());
5263: provider.resetDocument(getEditorInput());
5264: editorSaved();
5265:
5266: } catch (CoreException x) {
5267: IStatus status = x.getStatus();
5268: if (status == null
5269: || status.getSeverity() != IStatus.CANCEL) {
5270: Shell shell = getSite().getShell();
5271: String title = EditorMessages.Editor_error_revert_title;
5272: String msg = EditorMessages.Editor_error_revert_message;
5273: ErrorDialog.openError(shell, title, msg, x.getStatus());
5274: }
5275: } finally {
5276: provider.changed(getEditorInput());
5277: }
5278: }
5279:
5280: /**
5281: * Performs any additional action necessary to perform after the input
5282: * document's content has been replaced.
5283: * <p>
5284: * Clients may extended this method.
5285: *
5286: * @since 3.0
5287: */
5288: protected void handleElementContentReplaced() {
5289: }
5290:
5291: /*
5292: * @see ITextEditor#setAction(String, IAction)
5293: */
5294: public void setAction(String actionID, IAction action) {
5295: Assert.isNotNull(actionID);
5296: if (action == null) {
5297: action = (IAction) fActions.remove(actionID);
5298: if (action != null)
5299: fActivationCodeTrigger
5300: .unregisterActionFromKeyActivation(action);
5301: } else {
5302: fActions.put(actionID, action);
5303: fActivationCodeTrigger
5304: .registerActionForKeyActivation(action);
5305: }
5306: }
5307:
5308: /*
5309: * @see ITextEditor#setActionActivationCode(String, char, int, int)
5310: */
5311: public void setActionActivationCode(String actionID,
5312: char activationCharacter, int activationKeyCode,
5313: int activationStateMask) {
5314:
5315: Assert.isNotNull(actionID);
5316:
5317: ActionActivationCode found = findActionActivationCode(actionID);
5318: if (found == null) {
5319: found = new ActionActivationCode(actionID);
5320: fActivationCodes.add(found);
5321: }
5322:
5323: found.fCharacter = activationCharacter;
5324: found.fKeyCode = activationKeyCode;
5325: found.fStateMask = activationStateMask;
5326: }
5327:
5328: /**
5329: * Returns the activation code registered for the specified action.
5330: *
5331: * @param actionID the action id
5332: * @return the registered activation code or <code>null</code> if no code has been installed
5333: */
5334: private ActionActivationCode findActionActivationCode(
5335: String actionID) {
5336: int size = fActivationCodes.size();
5337: for (int i = 0; i < size; i++) {
5338: ActionActivationCode code = (ActionActivationCode) fActivationCodes
5339: .get(i);
5340: if (actionID.equals(code.fActionId))
5341: return code;
5342: }
5343: return null;
5344: }
5345:
5346: /*
5347: * @see ITextEditor#removeActionActivationCode(String)
5348: */
5349: public void removeActionActivationCode(String actionID) {
5350: Assert.isNotNull(actionID);
5351: ActionActivationCode code = findActionActivationCode(actionID);
5352: if (code != null)
5353: fActivationCodes.remove(code);
5354: }
5355:
5356: /*
5357: * @see ITextEditor#getAction(String)
5358: */
5359: public IAction getAction(String actionID) {
5360: Assert.isNotNull(actionID);
5361: IAction action = (IAction) fActions.get(actionID);
5362:
5363: if (action == null) {
5364: action = findContributedAction(actionID);
5365: if (action != null)
5366: setAction(actionID, action);
5367: }
5368:
5369: return action;
5370: }
5371:
5372: /**
5373: * Returns the action with the given action id that has been contributed via XML to this editor.
5374: * The lookup honors the dependencies of plug-ins.
5375: *
5376: * @param actionID the action id to look up
5377: * @return the action that has been contributed
5378: * @since 2.0
5379: */
5380: private IAction findContributedAction(String actionID) {
5381: List actions = new ArrayList();
5382: IConfigurationElement[] elements = Platform
5383: .getExtensionRegistry().getConfigurationElementsFor(
5384: PlatformUI.PLUGIN_ID, "editorActions"); //$NON-NLS-1$
5385: for (int i = 0; i < elements.length; i++) {
5386: IConfigurationElement element = elements[i];
5387: if (TAG_CONTRIBUTION_TYPE.equals(element.getName())) {
5388: if (!getSite().getId().equals(
5389: element.getAttribute("targetID"))) //$NON-NLS-1$
5390: continue;
5391:
5392: IConfigurationElement[] children = element
5393: .getChildren("action"); //$NON-NLS-1$
5394: for (int j = 0; j < children.length; j++) {
5395: IConfigurationElement child = children[j];
5396: if (actionID.equals(child.getAttribute("actionID"))) //$NON-NLS-1$
5397: actions.add(child);
5398: }
5399: }
5400: }
5401: int actionSize = actions.size();
5402: if (actionSize > 0) {
5403: IConfigurationElement element;
5404: if (actionSize > 1) {
5405: IConfigurationElement[] actionArray = (IConfigurationElement[]) actions
5406: .toArray(new IConfigurationElement[actionSize]);
5407: ConfigurationElementSorter sorter = new ConfigurationElementSorter() {
5408: /*
5409: * @see org.eclipse.ui.texteditor.ConfigurationElementSorter#getConfigurationElement(java.lang.Object)
5410: */
5411: public IConfigurationElement getConfigurationElement(
5412: Object object) {
5413: return (IConfigurationElement) object;
5414: }
5415: };
5416: sorter.sort(actionArray);
5417: element = actionArray[0];
5418: } else
5419: element = (IConfigurationElement) actions.get(0);
5420:
5421: try {
5422: return new ContributedAction(getSite(), element);
5423: } catch (CommandNotMappedException e) {
5424: // out of luck, no command action mapping
5425: }
5426: }
5427:
5428: return null;
5429: }
5430:
5431: /**
5432: * Updates the specified action by calling <code>IUpdate.update</code>
5433: * if applicable.
5434: *
5435: * @param actionId the action id
5436: */
5437: private void updateAction(String actionId) {
5438: Assert.isNotNull(actionId);
5439: if (fActions != null) {
5440: IAction action = (IAction) fActions.get(actionId);
5441: if (action instanceof IUpdate)
5442: ((IUpdate) action).update();
5443: }
5444: }
5445:
5446: /**
5447: * Marks or unmarks the given action to be updated on text selection changes.
5448: *
5449: * @param actionId the action id
5450: * @param mark <code>true</code> if the action is selection dependent
5451: */
5452: public void markAsSelectionDependentAction(String actionId,
5453: boolean mark) {
5454: Assert.isNotNull(actionId);
5455: if (mark) {
5456: if (!fSelectionActions.contains(actionId))
5457: fSelectionActions.add(actionId);
5458: } else
5459: fSelectionActions.remove(actionId);
5460: }
5461:
5462: /**
5463: * Marks or unmarks the given action to be updated on content changes.
5464: *
5465: * @param actionId the action id
5466: * @param mark <code>true</code> if the action is content dependent
5467: */
5468: public void markAsContentDependentAction(String actionId,
5469: boolean mark) {
5470: Assert.isNotNull(actionId);
5471: if (mark) {
5472: if (!fContentActions.contains(actionId))
5473: fContentActions.add(actionId);
5474: } else
5475: fContentActions.remove(actionId);
5476: }
5477:
5478: /**
5479: * Marks or unmarks the given action to be updated on property changes.
5480: *
5481: * @param actionId the action id
5482: * @param mark <code>true</code> if the action is property dependent
5483: * @since 2.0
5484: */
5485: public void markAsPropertyDependentAction(String actionId,
5486: boolean mark) {
5487: Assert.isNotNull(actionId);
5488: if (mark) {
5489: if (!fPropertyActions.contains(actionId))
5490: fPropertyActions.add(actionId);
5491: } else
5492: fPropertyActions.remove(actionId);
5493: }
5494:
5495: /**
5496: * Marks or unmarks the given action to be updated on state changes.
5497: *
5498: * @param actionId the action id
5499: * @param mark <code>true</code> if the action is state dependent
5500: * @since 2.0
5501: */
5502: public void markAsStateDependentAction(String actionId, boolean mark) {
5503: Assert.isNotNull(actionId);
5504: if (mark) {
5505: if (!fStateActions.contains(actionId))
5506: fStateActions.add(actionId);
5507: } else
5508: fStateActions.remove(actionId);
5509: }
5510:
5511: /**
5512: * Updates all selection dependent actions.
5513: */
5514: protected void updateSelectionDependentActions() {
5515: if (fSelectionActions != null) {
5516: Iterator e = fSelectionActions.iterator();
5517: while (e.hasNext())
5518: updateAction((String) e.next());
5519: }
5520: }
5521:
5522: /**
5523: * Updates all content dependent actions.
5524: */
5525: protected void updateContentDependentActions() {
5526: if (fContentActions != null) {
5527: Iterator e = fContentActions.iterator();
5528: while (e.hasNext())
5529: updateAction((String) e.next());
5530: }
5531: }
5532:
5533: /**
5534: * Updates all property dependent actions.
5535: * @since 2.0
5536: */
5537: protected void updatePropertyDependentActions() {
5538: if (fPropertyActions != null) {
5539: Iterator e = fPropertyActions.iterator();
5540: while (e.hasNext())
5541: updateAction((String) e.next());
5542: }
5543: }
5544:
5545: /**
5546: * Updates all state dependent actions.
5547: * @since 2.0
5548: */
5549: protected void updateStateDependentActions() {
5550: if (fStateActions != null) {
5551: Iterator e = fStateActions.iterator();
5552: while (e.hasNext())
5553: updateAction((String) e.next());
5554: }
5555: }
5556:
5557: /**
5558: * Creates action entries for all SWT StyledText actions as defined in
5559: * <code>org.eclipse.swt.custom.ST</code>. Overwrites and
5560: * extends the list of these actions afterwards.
5561: * <p>
5562: * Subclasses may extend.
5563: * </p>
5564: * @since 2.0
5565: */
5566: protected void createNavigationActions() {
5567:
5568: IAction action;
5569:
5570: StyledText textWidget = fSourceViewer.getTextWidget();
5571: for (int i = 0; i < ACTION_MAP.length; i++) {
5572: IdMapEntry entry = ACTION_MAP[i];
5573: action = new TextNavigationAction(textWidget, entry
5574: .getAction());
5575: action.setActionDefinitionId(entry.getActionId());
5576: setAction(entry.getActionId(), action);
5577: }
5578:
5579: action = new ToggleOverwriteModeAction(EditorMessages
5580: .getBundleForConstructedKeys(),
5581: "Editor.ToggleOverwriteMode."); //$NON-NLS-1$
5582: action
5583: .setActionDefinitionId(ITextEditorActionDefinitionIds.TOGGLE_OVERWRITE);
5584: setAction(ITextEditorActionDefinitionIds.TOGGLE_OVERWRITE,
5585: action);
5586: textWidget.setKeyBinding(SWT.INSERT, SWT.NULL);
5587:
5588: action = new ScrollLinesAction(-1);
5589: action
5590: .setActionDefinitionId(ITextEditorActionDefinitionIds.SCROLL_LINE_UP);
5591: setAction(ITextEditorActionDefinitionIds.SCROLL_LINE_UP, action);
5592:
5593: action = new ScrollLinesAction(1);
5594: action
5595: .setActionDefinitionId(ITextEditorActionDefinitionIds.SCROLL_LINE_DOWN);
5596: setAction(ITextEditorActionDefinitionIds.SCROLL_LINE_DOWN,
5597: action);
5598:
5599: action = new LineEndAction(textWidget, false);
5600: action
5601: .setActionDefinitionId(ITextEditorActionDefinitionIds.LINE_END);
5602: setAction(ITextEditorActionDefinitionIds.LINE_END, action);
5603:
5604: action = new LineStartAction(textWidget, false);
5605: action
5606: .setActionDefinitionId(ITextEditorActionDefinitionIds.LINE_START);
5607: setAction(ITextEditorActionDefinitionIds.LINE_START, action);
5608:
5609: action = new LineEndAction(textWidget, true);
5610: action
5611: .setActionDefinitionId(ITextEditorActionDefinitionIds.SELECT_LINE_END);
5612: setAction(ITextEditorActionDefinitionIds.SELECT_LINE_END,
5613: action);
5614:
5615: action = new LineStartAction(textWidget, true);
5616: action
5617: .setActionDefinitionId(ITextEditorActionDefinitionIds.SELECT_LINE_START);
5618: setAction(ITextEditorActionDefinitionIds.SELECT_LINE_START,
5619: action);
5620:
5621: // to accommodate https://bugs.eclipse.org/bugs/show_bug.cgi?id=51516
5622: // nullify handling of DELETE key by StyledText
5623: textWidget.setKeyBinding(SWT.DEL, SWT.NULL);
5624: }
5625:
5626: /**
5627: * Creates this editor's accessibility actions.
5628: * @since 2.0
5629: */
5630: private void createAccessibilityActions() {
5631: IAction action = new ShowRulerContextMenuAction();
5632: action
5633: .setActionDefinitionId(ITextEditorActionDefinitionIds.SHOW_RULER_CONTEXT_MENU);
5634: setAction(
5635: ITextEditorActionDefinitionIds.SHOW_RULER_CONTEXT_MENU,
5636: action);
5637: }
5638:
5639: /**
5640: * Creates this editor's undo/redo actions.
5641: * <p>
5642: * Subclasses may override or extend.</p>
5643: *
5644: * @since 3.1
5645: */
5646: protected void createUndoRedoActions() {
5647: IUndoContext undoContext = getUndoContext();
5648: if (undoContext != null) {
5649: // Use actions provided by global undo/redo
5650:
5651: // Create the undo action
5652: OperationHistoryActionHandler undoAction = new UndoActionHandler(
5653: getEditorSite(), undoContext);
5654: PlatformUI.getWorkbench().getHelpSystem().setHelp(
5655: undoAction,
5656: IAbstractTextEditorHelpContextIds.UNDO_ACTION);
5657: undoAction
5658: .setActionDefinitionId(IWorkbenchActionDefinitionIds.UNDO);
5659: registerUndoRedoAction(ITextEditorActionConstants.UNDO,
5660: undoAction);
5661:
5662: // Create the redo action.
5663: OperationHistoryActionHandler redoAction = new RedoActionHandler(
5664: getEditorSite(), undoContext);
5665: PlatformUI.getWorkbench().getHelpSystem().setHelp(
5666: redoAction,
5667: IAbstractTextEditorHelpContextIds.REDO_ACTION);
5668: redoAction
5669: .setActionDefinitionId(IWorkbenchActionDefinitionIds.REDO);
5670: registerUndoRedoAction(ITextEditorActionConstants.REDO,
5671: redoAction);
5672:
5673: // Install operation approvers
5674: IOperationHistory history = OperationHistoryFactory
5675: .getOperationHistory();
5676:
5677: // The first approver will prompt when operations affecting outside elements are to be undone or redone.
5678: if (fNonLocalOperationApprover != null)
5679: history
5680: .removeOperationApprover(fNonLocalOperationApprover);
5681: fNonLocalOperationApprover = getUndoRedoOperationApprover(undoContext);
5682: history.addOperationApprover(fNonLocalOperationApprover);
5683:
5684: // The second approver will prompt from this editor when an undo is attempted on an operation
5685: // and it is not the most recent operation in the editor.
5686: if (fLinearUndoViolationApprover != null)
5687: history
5688: .removeOperationApprover(fLinearUndoViolationApprover);
5689: fLinearUndoViolationApprover = new LinearUndoViolationUserApprover(
5690: undoContext, this );
5691: history.addOperationApprover(fLinearUndoViolationApprover);
5692:
5693: } else {
5694: // Use text operation actions (pre 3.1 style)
5695:
5696: ResourceAction action;
5697:
5698: if (getAction(ITextEditorActionConstants.UNDO) == null) {
5699: action = new TextOperationAction(EditorMessages
5700: .getBundleForConstructedKeys(),
5701: "Editor.Undo.", this , ITextOperationTarget.UNDO); //$NON-NLS-1$
5702: action
5703: .setHelpContextId(IAbstractTextEditorHelpContextIds.UNDO_ACTION);
5704: action
5705: .setActionDefinitionId(IWorkbenchActionDefinitionIds.UNDO);
5706: setAction(ITextEditorActionConstants.UNDO, action);
5707: }
5708:
5709: if (getAction(ITextEditorActionConstants.REDO) == null) {
5710: action = new TextOperationAction(EditorMessages
5711: .getBundleForConstructedKeys(),
5712: "Editor.Redo.", this , ITextOperationTarget.REDO); //$NON-NLS-1$
5713: action
5714: .setHelpContextId(IAbstractTextEditorHelpContextIds.REDO_ACTION);
5715: action
5716: .setActionDefinitionId(IWorkbenchActionDefinitionIds.REDO);
5717: setAction(ITextEditorActionConstants.REDO, action);
5718: }
5719: }
5720: }
5721:
5722: /**
5723: * Registers the given undo/redo action under the given ID and
5724: * ensures that previously installed actions get disposed. It
5725: * also takes care of re-registering the new action with the
5726: * global action handler.
5727: *
5728: * @param actionId the action id under which to register the action
5729: * @param action the action to register
5730: * @since 3.1
5731: */
5732: private void registerUndoRedoAction(String actionId,
5733: OperationHistoryActionHandler action) {
5734: IAction oldAction = getAction(actionId);
5735: if (oldAction instanceof OperationHistoryActionHandler)
5736: ((OperationHistoryActionHandler) oldAction).dispose();
5737:
5738: setAction(actionId, action);
5739:
5740: IActionBars actionBars = getEditorSite().getActionBars();
5741: if (actionBars != null)
5742: actionBars.setGlobalActionHandler(actionId, action);
5743: }
5744:
5745: /**
5746: * Return an {@link IOperationApprover} appropriate for approving the undo and
5747: * redo of operations that have the specified undo context.
5748: * <p>
5749: * Subclasses may override.
5750: * </p>
5751: * @param undoContext the IUndoContext of operations that should be examined
5752: * by the operation approver
5753: * @return the <code>IOperationApprover</code> appropriate for approving undo
5754: * and redo operations inside this editor, or <code>null</code> if no
5755: * approval is needed
5756: * @since 3.1
5757: */
5758: protected IOperationApprover getUndoRedoOperationApprover(
5759: IUndoContext undoContext) {
5760: return new NonLocalUndoUserApprover(undoContext, this ,
5761: new Object[] { getEditorInput() }, Object.class);
5762: }
5763:
5764: /**
5765: * Creates this editor's standard actions and connects them with the global
5766: * workbench actions.
5767: * <p>
5768: * Subclasses may extend.</p>
5769: */
5770: protected void createActions() {
5771:
5772: ResourceAction action;
5773:
5774: action = new TextOperationAction(EditorMessages
5775: .getBundleForConstructedKeys(),
5776: "Editor.Cut.", this , ITextOperationTarget.CUT); //$NON-NLS-1$
5777: action
5778: .setHelpContextId(IAbstractTextEditorHelpContextIds.CUT_ACTION);
5779: action.setActionDefinitionId(IWorkbenchActionDefinitionIds.CUT);
5780: setAction(ITextEditorActionConstants.CUT, action);
5781:
5782: action = new TextOperationAction(EditorMessages
5783: .getBundleForConstructedKeys(),
5784: "Editor.Copy.", this , ITextOperationTarget.COPY, true); //$NON-NLS-1$
5785: action
5786: .setHelpContextId(IAbstractTextEditorHelpContextIds.COPY_ACTION);
5787: action
5788: .setActionDefinitionId(IWorkbenchActionDefinitionIds.COPY);
5789: setAction(ITextEditorActionConstants.COPY, action);
5790:
5791: action = new TextOperationAction(EditorMessages
5792: .getBundleForConstructedKeys(),
5793: "Editor.Paste.", this , ITextOperationTarget.PASTE); //$NON-NLS-1$
5794: action
5795: .setHelpContextId(IAbstractTextEditorHelpContextIds.PASTE_ACTION);
5796: action
5797: .setActionDefinitionId(IWorkbenchActionDefinitionIds.PASTE);
5798: setAction(ITextEditorActionConstants.PASTE, action);
5799:
5800: action = new TextOperationAction(EditorMessages
5801: .getBundleForConstructedKeys(),
5802: "Editor.Delete.", this , ITextOperationTarget.DELETE); //$NON-NLS-1$
5803: action
5804: .setHelpContextId(IAbstractTextEditorHelpContextIds.DELETE_ACTION);
5805: action
5806: .setActionDefinitionId(IWorkbenchActionDefinitionIds.DELETE);
5807: setAction(ITextEditorActionConstants.DELETE, action);
5808:
5809: action = new DeleteLineAction(
5810: EditorMessages.getBundleForConstructedKeys(),
5811: "Editor.DeleteLine.", this , DeleteLineAction.WHOLE, false); //$NON-NLS-1$
5812: action
5813: .setHelpContextId(IAbstractTextEditorHelpContextIds.DELETE_LINE_ACTION);
5814: action
5815: .setActionDefinitionId(ITextEditorActionDefinitionIds.DELETE_LINE);
5816: setAction(ITextEditorActionConstants.DELETE_LINE, action);
5817:
5818: action = new DeleteLineAction(EditorMessages
5819: .getBundleForConstructedKeys(),
5820: "Editor.CutLine.", this , DeleteLineAction.WHOLE, true); //$NON-NLS-1$
5821: action
5822: .setHelpContextId(IAbstractTextEditorHelpContextIds.CUT_LINE_ACTION);
5823: action
5824: .setActionDefinitionId(ITextEditorActionDefinitionIds.CUT_LINE);
5825: setAction(ITextEditorActionConstants.CUT_LINE, action);
5826:
5827: action = new DeleteLineAction(
5828: EditorMessages.getBundleForConstructedKeys(),
5829: "Editor.DeleteLineToBeginning.", this , DeleteLineAction.TO_BEGINNING, false); //$NON-NLS-1$
5830: action
5831: .setHelpContextId(IAbstractTextEditorHelpContextIds.DELETE_LINE_TO_BEGINNING_ACTION);
5832: action
5833: .setActionDefinitionId(ITextEditorActionDefinitionIds.DELETE_LINE_TO_BEGINNING);
5834: setAction(ITextEditorActionConstants.DELETE_LINE_TO_BEGINNING,
5835: action);
5836:
5837: action = new DeleteLineAction(
5838: EditorMessages.getBundleForConstructedKeys(),
5839: "Editor.CutLineToBeginning.", this , DeleteLineAction.TO_BEGINNING, true); //$NON-NLS-1$
5840: action
5841: .setHelpContextId(IAbstractTextEditorHelpContextIds.CUT_LINE_TO_BEGINNING_ACTION);
5842: action
5843: .setActionDefinitionId(ITextEditorActionDefinitionIds.CUT_LINE_TO_BEGINNING);
5844: setAction(ITextEditorActionConstants.CUT_LINE_TO_BEGINNING,
5845: action);
5846:
5847: action = new DeleteLineAction(
5848: EditorMessages.getBundleForConstructedKeys(),
5849: "Editor.DeleteLineToEnd.", this , DeleteLineAction.TO_END, false); //$NON-NLS-1$
5850: action
5851: .setHelpContextId(IAbstractTextEditorHelpContextIds.DELETE_LINE_TO_END_ACTION);
5852: action
5853: .setActionDefinitionId(ITextEditorActionDefinitionIds.DELETE_LINE_TO_END);
5854: setAction(ITextEditorActionConstants.DELETE_LINE_TO_END, action);
5855:
5856: action = new DeleteLineAction(
5857: EditorMessages.getBundleForConstructedKeys(),
5858: "Editor.CutLineToEnd.", this , DeleteLineAction.TO_END, true); //$NON-NLS-1$
5859: action
5860: .setHelpContextId(IAbstractTextEditorHelpContextIds.CUT_LINE_TO_END_ACTION);
5861: action
5862: .setActionDefinitionId(ITextEditorActionDefinitionIds.CUT_LINE_TO_END);
5863: setAction(ITextEditorActionConstants.CUT_LINE_TO_END, action);
5864:
5865: action = new JoinLinesAction(EditorMessages
5866: .getBundleForConstructedKeys(),
5867: "Editor.JoinLines.", this , " "); //$NON-NLS-1$ //$NON-NLS-2$
5868: action
5869: .setHelpContextId(IAbstractTextEditorHelpContextIds.JOIN_LINES_ACTION);
5870: action
5871: .setActionDefinitionId(ITextEditorActionDefinitionIds.JOIN_LINES);
5872: setAction(ITextEditorActionConstants.JOIN_LINES, action);
5873:
5874: action = new MarkAction(EditorMessages
5875: .getBundleForConstructedKeys(),
5876: "Editor.SetMark.", this , MarkAction.SET_MARK); //$NON-NLS-1$
5877: action
5878: .setHelpContextId(IAbstractTextEditorHelpContextIds.SET_MARK_ACTION);
5879: action
5880: .setActionDefinitionId(ITextEditorActionDefinitionIds.SET_MARK);
5881: setAction(ITextEditorActionConstants.SET_MARK, action);
5882:
5883: action = new MarkAction(EditorMessages
5884: .getBundleForConstructedKeys(),
5885: "Editor.ClearMark.", this , MarkAction.CLEAR_MARK); //$NON-NLS-1$
5886: action
5887: .setHelpContextId(IAbstractTextEditorHelpContextIds.CLEAR_MARK_ACTION);
5888: action
5889: .setActionDefinitionId(ITextEditorActionDefinitionIds.CLEAR_MARK);
5890: setAction(ITextEditorActionConstants.CLEAR_MARK, action);
5891:
5892: action = new MarkAction(EditorMessages
5893: .getBundleForConstructedKeys(),
5894: "Editor.SwapMark.", this , MarkAction.SWAP_MARK); //$NON-NLS-1$
5895: action
5896: .setHelpContextId(IAbstractTextEditorHelpContextIds.SWAP_MARK_ACTION);
5897: action
5898: .setActionDefinitionId(ITextEditorActionDefinitionIds.SWAP_MARK);
5899: setAction(ITextEditorActionConstants.SWAP_MARK, action);
5900:
5901: action = new TextOperationAction(
5902: EditorMessages.getBundleForConstructedKeys(),
5903: "Editor.SelectAll.", this , ITextOperationTarget.SELECT_ALL, true); //$NON-NLS-1$
5904: action
5905: .setHelpContextId(IAbstractTextEditorHelpContextIds.SELECT_ALL_ACTION);
5906: action
5907: .setActionDefinitionId(IWorkbenchActionDefinitionIds.SELECT_ALL);
5908: setAction(ITextEditorActionConstants.SELECT_ALL, action);
5909:
5910: action = new ShiftAction(
5911: EditorMessages.getBundleForConstructedKeys(),
5912: "Editor.ShiftRight.", this , ITextOperationTarget.SHIFT_RIGHT); //$NON-NLS-1$
5913: action
5914: .setHelpContextId(IAbstractTextEditorHelpContextIds.SHIFT_RIGHT_ACTION);
5915: action
5916: .setActionDefinitionId(ITextEditorActionDefinitionIds.SHIFT_RIGHT);
5917: setAction(ITextEditorActionConstants.SHIFT_RIGHT, action);
5918:
5919: action = new ShiftAction(
5920: EditorMessages.getBundleForConstructedKeys(),
5921: "Editor.ShiftRight.", this , ITextOperationTarget.SHIFT_RIGHT) { //$NON-NLS-1$
5922: public void update() {
5923: updateForTab();
5924: }
5925: };
5926: setAction(ITextEditorActionConstants.SHIFT_RIGHT_TAB, action);
5927:
5928: action = new ShiftAction(
5929: EditorMessages.getBundleForConstructedKeys(),
5930: "Editor.ShiftLeft.", this , ITextOperationTarget.SHIFT_LEFT); //$NON-NLS-1$
5931: action
5932: .setHelpContextId(IAbstractTextEditorHelpContextIds.SHIFT_LEFT_ACTION);
5933: action
5934: .setActionDefinitionId(ITextEditorActionDefinitionIds.SHIFT_LEFT);
5935: setAction(ITextEditorActionConstants.SHIFT_LEFT, action);
5936:
5937: action = new TextOperationAction(EditorMessages
5938: .getBundleForConstructedKeys(),
5939: "Editor.Print.", this , ITextOperationTarget.PRINT, true); //$NON-NLS-1$
5940: action
5941: .setHelpContextId(IAbstractTextEditorHelpContextIds.PRINT_ACTION);
5942: action
5943: .setActionDefinitionId(IWorkbenchActionDefinitionIds.PRINT);
5944: setAction(ITextEditorActionConstants.PRINT, action);
5945:
5946: action = new FindReplaceAction(EditorMessages
5947: .getBundleForConstructedKeys(),
5948: "Editor.FindReplace.", this ); //$NON-NLS-1$
5949: action
5950: .setHelpContextId(IAbstractTextEditorHelpContextIds.FIND_ACTION);
5951: action
5952: .setActionDefinitionId(IWorkbenchActionDefinitionIds.FIND_REPLACE);
5953: setAction(ITextEditorActionConstants.FIND, action);
5954:
5955: action = new FindNextAction(EditorMessages
5956: .getBundleForConstructedKeys(),
5957: "Editor.FindNext.", this , true); //$NON-NLS-1$
5958: action
5959: .setHelpContextId(IAbstractTextEditorHelpContextIds.FIND_NEXT_ACTION);
5960: action
5961: .setActionDefinitionId(IWorkbenchActionDefinitionIds.FIND_NEXT);
5962: setAction(ITextEditorActionConstants.FIND_NEXT, action);
5963:
5964: action = new FindNextAction(EditorMessages
5965: .getBundleForConstructedKeys(),
5966: "Editor.FindPrevious.", this , false); //$NON-NLS-1$
5967: action
5968: .setHelpContextId(IAbstractTextEditorHelpContextIds.FIND_PREVIOUS_ACTION);
5969: action
5970: .setActionDefinitionId(IWorkbenchActionDefinitionIds.FIND_PREVIOUS);
5971: setAction(ITextEditorActionConstants.FIND_PREVIOUS, action);
5972:
5973: action = new IncrementalFindAction(EditorMessages
5974: .getBundleForConstructedKeys(),
5975: "Editor.FindIncremental.", this , true); //$NON-NLS-1$
5976: action
5977: .setHelpContextId(IAbstractTextEditorHelpContextIds.FIND_INCREMENTAL_ACTION);
5978: action
5979: .setActionDefinitionId(IWorkbenchActionDefinitionIds.FIND_INCREMENTAL);
5980: setAction(ITextEditorActionConstants.FIND_INCREMENTAL, action);
5981:
5982: action = new IncrementalFindAction(EditorMessages
5983: .getBundleForConstructedKeys(),
5984: "Editor.FindIncrementalReverse.", this , false); //$NON-NLS-1$
5985: action
5986: .setHelpContextId(IAbstractTextEditorHelpContextIds.FIND_INCREMENTAL_REVERSE_ACTION);
5987: action
5988: .setActionDefinitionId(IWorkbenchActionDefinitionIds.FIND_INCREMENTAL_REVERSE);
5989: setAction(ITextEditorActionConstants.FIND_INCREMENTAL_REVERSE,
5990: action);
5991:
5992: action = new SaveAction(EditorMessages
5993: .getBundleForConstructedKeys(), "Editor.Save.", this ); //$NON-NLS-1$
5994: action
5995: .setHelpContextId(IAbstractTextEditorHelpContextIds.SAVE_ACTION);
5996: /*
5997: * if the line below is uncommented then the key binding does not work any more
5998: * see https://bugs.eclipse.org/bugs/show_bug.cgi?id=53417
5999: */
6000: // action.setActionDefinitionId(ITextEditorActionDefinitionIds.SAVE);
6001: setAction(ITextEditorActionConstants.SAVE, action);
6002:
6003: action = new RevertToSavedAction(EditorMessages
6004: .getBundleForConstructedKeys(), "Editor.Revert.", this ); //$NON-NLS-1$
6005: action
6006: .setHelpContextId(IAbstractTextEditorHelpContextIds.REVERT_TO_SAVED_ACTION);
6007: action
6008: .setActionDefinitionId(IWorkbenchActionDefinitionIds.REVERT_TO_SAVED);
6009: setAction(ITextEditorActionConstants.REVERT_TO_SAVED, action);
6010:
6011: action = new GotoLineAction(EditorMessages
6012: .getBundleForConstructedKeys(),
6013: "Editor.GotoLine.", this ); //$NON-NLS-1$
6014: action
6015: .setHelpContextId(IAbstractTextEditorHelpContextIds.GOTO_LINE_ACTION);
6016: action
6017: .setActionDefinitionId(ITextEditorActionDefinitionIds.LINE_GOTO);
6018: setAction(ITextEditorActionConstants.GOTO_LINE, action);
6019:
6020: action = new MoveLinesAction(EditorMessages
6021: .getBundleForConstructedKeys(),
6022: "Editor.MoveLinesUp.", this , true, false); //$NON-NLS-1$
6023: action
6024: .setHelpContextId(IAbstractTextEditorHelpContextIds.MOVE_LINES_ACTION);
6025: action
6026: .setActionDefinitionId(ITextEditorActionDefinitionIds.MOVE_LINES_UP);
6027: setAction(ITextEditorActionConstants.MOVE_LINE_UP, action);
6028:
6029: action = new MoveLinesAction(EditorMessages
6030: .getBundleForConstructedKeys(),
6031: "Editor.MoveLinesDown.", this , false, false); //$NON-NLS-1$
6032: action
6033: .setHelpContextId(IAbstractTextEditorHelpContextIds.MOVE_LINES_ACTION);
6034: action
6035: .setActionDefinitionId(ITextEditorActionDefinitionIds.MOVE_LINES_DOWN);
6036: setAction(ITextEditorActionConstants.MOVE_LINE_DOWN, action);
6037:
6038: action = new MoveLinesAction(EditorMessages
6039: .getBundleForConstructedKeys(),
6040: "Editor.CopyLineUp.", this , true, true); //$NON-NLS-1$
6041: action
6042: .setHelpContextId(IAbstractTextEditorHelpContextIds.COPY_LINES_ACTION);
6043: action
6044: .setActionDefinitionId(ITextEditorActionDefinitionIds.COPY_LINES_UP);
6045: setAction(ITextEditorActionConstants.COPY_LINE_UP, action);
6046:
6047: action = new MoveLinesAction(EditorMessages
6048: .getBundleForConstructedKeys(),
6049: "Editor.CopyLineDown.", this , false, true); //$NON-NLS-1$
6050: action
6051: .setHelpContextId(IAbstractTextEditorHelpContextIds.COPY_LINES_ACTION);
6052: action
6053: .setActionDefinitionId(ITextEditorActionDefinitionIds.COPY_LINES_DOWN);
6054: setAction(ITextEditorActionConstants.COPY_LINE_DOWN, action);
6055:
6056: action = new CaseAction(EditorMessages
6057: .getBundleForConstructedKeys(),
6058: "Editor.UpperCase.", this , true); //$NON-NLS-1$
6059: action
6060: .setHelpContextId(IAbstractTextEditorHelpContextIds.UPPER_CASE_ACTION);
6061: action
6062: .setActionDefinitionId(ITextEditorActionDefinitionIds.UPPER_CASE);
6063: setAction(ITextEditorActionConstants.UPPER_CASE, action);
6064:
6065: action = new CaseAction(EditorMessages
6066: .getBundleForConstructedKeys(),
6067: "Editor.LowerCase.", this , false); //$NON-NLS-1$
6068: action
6069: .setHelpContextId(IAbstractTextEditorHelpContextIds.LOWER_CASE_ACTION);
6070: action
6071: .setActionDefinitionId(ITextEditorActionDefinitionIds.LOWER_CASE);
6072: setAction(ITextEditorActionConstants.LOWER_CASE, action);
6073:
6074: action = new InsertLineAction(EditorMessages
6075: .getBundleForConstructedKeys(),
6076: "Editor.SmartEnter.", this , false); //$NON-NLS-1$
6077: action
6078: .setHelpContextId(IAbstractTextEditorHelpContextIds.SMART_ENTER_ACTION);
6079: action
6080: .setActionDefinitionId(ITextEditorActionDefinitionIds.SMART_ENTER);
6081: setAction(ITextEditorActionConstants.SMART_ENTER, action);
6082:
6083: action = new InsertLineAction(EditorMessages
6084: .getBundleForConstructedKeys(),
6085: "Editor.SmartEnterInverse.", this , true); //$NON-NLS-1$
6086: action
6087: .setHelpContextId(IAbstractTextEditorHelpContextIds.SMART_ENTER_ACTION);
6088: action
6089: .setActionDefinitionId(ITextEditorActionDefinitionIds.SMART_ENTER_INVERSE);
6090: setAction(ITextEditorActionConstants.SMART_ENTER_INVERSE,
6091: action);
6092:
6093: action = new ToggleInsertModeAction(EditorMessages
6094: .getBundleForConstructedKeys(),
6095: "Editor.ToggleInsertMode."); //$NON-NLS-1$
6096: action
6097: .setHelpContextId(IAbstractTextEditorHelpContextIds.TOGGLE_INSERT_MODE_ACTION);
6098: action
6099: .setActionDefinitionId(ITextEditorActionDefinitionIds.TOGGLE_INSERT_MODE);
6100: setAction(ITextEditorActionConstants.TOGGLE_INSERT_MODE, action);
6101:
6102: action = new HippieCompleteAction(EditorMessages
6103: .getBundleForConstructedKeys(),
6104: "Editor.HippieCompletion.", this ); //$NON-NLS-1$
6105: action
6106: .setHelpContextId(IAbstractTextEditorHelpContextIds.HIPPIE_COMPLETION_ACTION);
6107: action
6108: .setActionDefinitionId(ITextEditorActionDefinitionIds.HIPPIE_COMPLETION);
6109: setAction(ITextEditorActionConstants.HIPPIE_COMPLETION, action);
6110:
6111: action = new TextOperationAction(EditorMessages
6112: .getBundleForConstructedKeys(),
6113: "Editor.QuickAssist.", this , ISourceViewer.QUICK_ASSIST); //$NON-NLS-1$
6114: action
6115: .setHelpContextId(IAbstractTextEditorHelpContextIds.QUICK_ASSIST_ACTION);
6116: action
6117: .setActionDefinitionId(ITextEditorActionDefinitionIds.QUICK_ASSIST);
6118: setAction(ITextEditorActionConstants.QUICK_ASSIST, action);
6119: markAsStateDependentAction(
6120: ITextEditorActionConstants.QUICK_ASSIST, true);
6121:
6122: action = new GotoAnnotationAction(this , true);
6123: setAction(ITextEditorActionConstants.NEXT, action);
6124: action = new GotoAnnotationAction(this , false);
6125: setAction(ITextEditorActionConstants.PREVIOUS, action);
6126:
6127: action = new RecenterAction(EditorMessages
6128: .getBundleForConstructedKeys(),
6129: "Editor.Recenter.", this ); //$NON-NLS-1$
6130: action
6131: .setHelpContextId(IAbstractTextEditorHelpContextIds.RECENTER_ACTION);
6132: action
6133: .setActionDefinitionId(ITextEditorActionDefinitionIds.RECENTER);
6134: setAction(ITextEditorActionConstants.RECENTER, action);
6135:
6136: action = new ShowWhitespaceCharactersAction(
6137: EditorMessages.getBundleForConstructedKeys(),
6138: "Editor.ShowWhitespaceCharacters.", this , getPreferenceStore()); //$NON-NLS-1$
6139: action
6140: .setHelpContextId(IAbstractTextEditorHelpContextIds.SHOW_WHITESPACE_CHARACTERS_ACTION);
6141: action
6142: .setActionDefinitionId(ITextEditorActionDefinitionIds.SHOW_WHITESPACE_CHARACTERS);
6143: setAction(
6144: ITextEditorActionConstants.SHOW_WHITESPACE_CHARACTERS,
6145: action);
6146:
6147: ResourceAction resAction = new TextOperationAction(
6148: EditorMessages.getBundleForConstructedKeys(),
6149: "Editor.ShowInformation.", this , ISourceViewer.INFORMATION, true); //$NON-NLS-1$
6150: resAction = new InformationDispatchAction(
6151: EditorMessages.getBundleForConstructedKeys(),
6152: "Editor.ShowInformation.", (TextOperationAction) resAction); //$NON-NLS-1$
6153: action
6154: .setHelpContextId(IAbstractTextEditorHelpContextIds.SHOW_INFORMATION_ACTION);
6155: resAction
6156: .setActionDefinitionId(ITextEditorActionDefinitionIds.SHOW_INFORMATION);
6157: setAction(ITextEditorActionConstants.SHOW_INFORMATION,
6158: resAction);
6159:
6160: PropertyDialogAction openProperties = new PropertyDialogAction(
6161: new IShellProvider() {
6162: public Shell getShell() {
6163: return getSite().getShell();
6164: }
6165: }, new ISelectionProvider() {
6166: public void addSelectionChangedListener(
6167: ISelectionChangedListener listener) {
6168: }
6169:
6170: public ISelection getSelection() {
6171: return new StructuredSelection(getEditorInput());
6172: }
6173:
6174: public void removeSelectionChangedListener(
6175: ISelectionChangedListener listener) {
6176: }
6177:
6178: public void setSelection(ISelection selection) {
6179: }
6180: });
6181: openProperties
6182: .setActionDefinitionId(IWorkbenchActionDefinitionIds.PROPERTIES);
6183: setAction(ITextEditorActionConstants.PROPERTIES, openProperties);
6184:
6185: markAsContentDependentAction(ITextEditorActionConstants.UNDO,
6186: true);
6187: markAsContentDependentAction(ITextEditorActionConstants.REDO,
6188: true);
6189: markAsContentDependentAction(ITextEditorActionConstants.FIND,
6190: true);
6191: markAsContentDependentAction(
6192: ITextEditorActionConstants.FIND_NEXT, true);
6193: markAsContentDependentAction(
6194: ITextEditorActionConstants.FIND_PREVIOUS, true);
6195: markAsContentDependentAction(
6196: ITextEditorActionConstants.FIND_INCREMENTAL, true);
6197: markAsContentDependentAction(
6198: ITextEditorActionConstants.FIND_INCREMENTAL_REVERSE,
6199: true);
6200:
6201: markAsSelectionDependentAction(ITextEditorActionConstants.CUT,
6202: true);
6203: markAsSelectionDependentAction(ITextEditorActionConstants.COPY,
6204: true);
6205: markAsSelectionDependentAction(
6206: ITextEditorActionConstants.PASTE, true);
6207: markAsSelectionDependentAction(
6208: ITextEditorActionConstants.DELETE, true);
6209: markAsSelectionDependentAction(
6210: ITextEditorActionConstants.SHIFT_RIGHT, true);
6211: markAsSelectionDependentAction(
6212: ITextEditorActionConstants.SHIFT_RIGHT_TAB, true);
6213: markAsSelectionDependentAction(
6214: ITextEditorActionConstants.UPPER_CASE, true);
6215: markAsSelectionDependentAction(
6216: ITextEditorActionConstants.LOWER_CASE, true);
6217:
6218: markAsPropertyDependentAction(ITextEditorActionConstants.UNDO,
6219: true);
6220: markAsPropertyDependentAction(ITextEditorActionConstants.REDO,
6221: true);
6222: markAsPropertyDependentAction(
6223: ITextEditorActionConstants.REVERT_TO_SAVED, true);
6224:
6225: markAsStateDependentAction(ITextEditorActionConstants.UNDO,
6226: true);
6227: markAsStateDependentAction(ITextEditorActionConstants.REDO,
6228: true);
6229: markAsStateDependentAction(ITextEditorActionConstants.CUT, true);
6230: markAsStateDependentAction(ITextEditorActionConstants.PASTE,
6231: true);
6232: markAsStateDependentAction(ITextEditorActionConstants.DELETE,
6233: true);
6234: markAsStateDependentAction(
6235: ITextEditorActionConstants.SHIFT_RIGHT, true);
6236: markAsStateDependentAction(
6237: ITextEditorActionConstants.SHIFT_RIGHT_TAB, true);
6238: markAsStateDependentAction(
6239: ITextEditorActionConstants.SHIFT_LEFT, true);
6240: markAsStateDependentAction(ITextEditorActionConstants.FIND,
6241: true);
6242: markAsStateDependentAction(
6243: ITextEditorActionConstants.DELETE_LINE, true);
6244: markAsStateDependentAction(
6245: ITextEditorActionConstants.DELETE_LINE_TO_BEGINNING,
6246: true);
6247: markAsStateDependentAction(
6248: ITextEditorActionConstants.DELETE_LINE_TO_END, true);
6249: markAsStateDependentAction(
6250: ITextEditorActionConstants.MOVE_LINE_UP, true);
6251: markAsStateDependentAction(
6252: ITextEditorActionConstants.MOVE_LINE_DOWN, true);
6253: markAsStateDependentAction(ITextEditorActionConstants.CUT_LINE,
6254: true);
6255: markAsStateDependentAction(
6256: ITextEditorActionConstants.CUT_LINE_TO_BEGINNING, true);
6257: markAsStateDependentAction(
6258: ITextEditorActionConstants.CUT_LINE_TO_END, true);
6259:
6260: setActionActivationCode(
6261: ITextEditorActionConstants.SHIFT_RIGHT_TAB, '\t', -1,
6262: SWT.NONE);
6263: setActionActivationCode(ITextEditorActionConstants.SHIFT_LEFT,
6264: '\t', -1, SWT.SHIFT);
6265: }
6266:
6267: /**
6268: * Convenience method to add the action installed under the given action id to the given menu.
6269: * @param menu the menu to add the action to
6270: * @param actionId the id of the action to be added
6271: */
6272: protected final void addAction(IMenuManager menu, String actionId) {
6273: IAction action = getAction(actionId);
6274: if (action != null) {
6275: if (action instanceof IUpdate)
6276: ((IUpdate) action).update();
6277: menu.add(action);
6278: }
6279: }
6280:
6281: /**
6282: * Convenience method to add the action installed under the given action id to the specified group of the menu.
6283: * @param menu the menu to add the action to
6284: * @param group the group in the menu
6285: * @param actionId the id of the action to add
6286: */
6287: protected final void addAction(IMenuManager menu, String group,
6288: String actionId) {
6289: IAction action = getAction(actionId);
6290: if (action != null) {
6291: if (action instanceof IUpdate)
6292: ((IUpdate) action).update();
6293:
6294: IMenuManager subMenu = menu.findMenuUsingPath(group);
6295: if (subMenu != null)
6296: subMenu.add(action);
6297: else
6298: menu.appendToGroup(group, action);
6299: }
6300: }
6301:
6302: /**
6303: * Convenience method to add a new group after the specified group.
6304: * @param menu the menu to add the new group to
6305: * @param existingGroup the group after which to insert the new group
6306: * @param newGroup the new group
6307: */
6308: protected final void addGroup(IMenuManager menu,
6309: String existingGroup, String newGroup) {
6310: IMenuManager subMenu = menu.findMenuUsingPath(existingGroup);
6311: if (subMenu != null)
6312: subMenu.add(new Separator(newGroup));
6313: else
6314: menu.appendToGroup(existingGroup, new Separator(newGroup));
6315: }
6316:
6317: /**
6318: * Sets up the ruler context menu before it is made visible.
6319: * <p>
6320: * Subclasses may extend to add other actions.</p>
6321: *
6322: * @param menu the menu
6323: */
6324: protected void rulerContextMenuAboutToShow(IMenuManager menu) {
6325:
6326: menu.add(new Separator(ITextEditorActionConstants.GROUP_REST));
6327: menu.add(new Separator(IWorkbenchActionConstants.MB_ADDITIONS));
6328:
6329: for (Iterator i = fRulerContextMenuListeners.iterator(); i
6330: .hasNext();)
6331: ((IMenuListener) i.next()).menuAboutToShow(menu);
6332:
6333: addAction(menu,
6334: ITextEditorActionConstants.RULER_MANAGE_BOOKMARKS);
6335: addAction(menu, ITextEditorActionConstants.RULER_MANAGE_TASKS);
6336: }
6337:
6338: /**
6339: * Sets up this editor's context menu before it is made visible.
6340: * <p>
6341: * Subclasses may extend to add other actions.</p>
6342: *
6343: * @param menu the menu
6344: */
6345: protected void editorContextMenuAboutToShow(IMenuManager menu) {
6346:
6347: menu.add(new Separator(ITextEditorActionConstants.GROUP_UNDO));
6348: menu
6349: .add(new GroupMarker(
6350: ITextEditorActionConstants.GROUP_SAVE));
6351: menu.add(new Separator(ITextEditorActionConstants.GROUP_COPY));
6352: menu.add(new Separator(ITextEditorActionConstants.GROUP_PRINT));
6353: menu.add(new Separator(ITextEditorActionConstants.GROUP_EDIT));
6354: menu.add(new Separator(ITextEditorActionConstants.GROUP_FIND));
6355: menu.add(new Separator(IWorkbenchActionConstants.GROUP_ADD));
6356: menu.add(new Separator(ITextEditorActionConstants.GROUP_REST));
6357: menu.add(new Separator(IWorkbenchActionConstants.MB_ADDITIONS));
6358:
6359: if (isEditable()) {
6360: addAction(menu, ITextEditorActionConstants.GROUP_UNDO,
6361: ITextEditorActionConstants.UNDO);
6362: addAction(menu, ITextEditorActionConstants.GROUP_UNDO,
6363: ITextEditorActionConstants.REVERT_TO_SAVED);
6364: addAction(menu, ITextEditorActionConstants.GROUP_SAVE,
6365: ITextEditorActionConstants.SAVE);
6366: addAction(menu, ITextEditorActionConstants.GROUP_COPY,
6367: ITextEditorActionConstants.CUT);
6368: addAction(menu, ITextEditorActionConstants.GROUP_COPY,
6369: ITextEditorActionConstants.COPY);
6370: addAction(menu, ITextEditorActionConstants.GROUP_COPY,
6371: ITextEditorActionConstants.PASTE);
6372: } else {
6373: addAction(menu, ITextEditorActionConstants.GROUP_COPY,
6374: ITextEditorActionConstants.COPY);
6375: }
6376: }
6377:
6378: /**
6379: * Returns the status line manager of this editor.
6380: *
6381: * @return the status line manager of this editor
6382: * @since 2.0, protected since 3.3
6383: */
6384: protected IStatusLineManager getStatusLineManager() {
6385: return getEditorSite().getActionBars().getStatusLineManager();
6386: }
6387:
6388: /*
6389: * @see IAdaptable#getAdapter(java.lang.Class)
6390: */
6391: public Object getAdapter(Class required) {
6392:
6393: if (IEditorStatusLine.class.equals(required)) {
6394: if (fEditorStatusLine == null) {
6395: IStatusLineManager statusLineManager = getStatusLineManager();
6396: ISelectionProvider selectionProvider = getSelectionProvider();
6397: if (statusLineManager != null
6398: && selectionProvider != null)
6399: fEditorStatusLine = new EditorStatusLine(
6400: statusLineManager, selectionProvider);
6401: }
6402: return fEditorStatusLine;
6403: }
6404:
6405: if (IVerticalRulerInfo.class.equals(required)) {
6406: if (fVerticalRuler != null)
6407: return fVerticalRuler;
6408: }
6409:
6410: if (IMarkRegionTarget.class.equals(required)) {
6411: if (fMarkRegionTarget == null) {
6412: IStatusLineManager manager = getStatusLineManager();
6413: if (manager != null)
6414: fMarkRegionTarget = (fSourceViewer == null ? null
6415: : new MarkRegionTarget(fSourceViewer,
6416: manager));
6417: }
6418: return fMarkRegionTarget;
6419: }
6420:
6421: if (DeleteLineTarget.class.equals(required)) {
6422: if (fDeleteLineTarget == null) {
6423: fDeleteLineTarget = new DeleteLineTarget(fSourceViewer);
6424: }
6425: return fDeleteLineTarget;
6426: }
6427:
6428: if (IncrementalFindTarget.class.equals(required)) {
6429: if (fIncrementalFindTarget == null) {
6430: IStatusLineManager manager = getStatusLineManager();
6431: if (manager != null)
6432: fIncrementalFindTarget = (fSourceViewer == null ? null
6433: : new IncrementalFindTarget(fSourceViewer,
6434: manager));
6435: }
6436: return fIncrementalFindTarget;
6437: }
6438:
6439: if (IFindReplaceTarget.class.equals(required)) {
6440: if (fFindReplaceTarget == null) {
6441: IFindReplaceTarget target = (fSourceViewer == null ? null
6442: : fSourceViewer.getFindReplaceTarget());
6443: if (target != null) {
6444: fFindReplaceTarget = new FindReplaceTarget(this ,
6445: target);
6446: if (fFindScopeHighlightColor != null)
6447: fFindReplaceTarget
6448: .setScopeHighlightColor(fFindScopeHighlightColor);
6449: }
6450: }
6451: return fFindReplaceTarget;
6452: }
6453:
6454: if (ITextOperationTarget.class.equals(required))
6455: return (fSourceViewer == null ? null : fSourceViewer
6456: .getTextOperationTarget());
6457:
6458: if (IRewriteTarget.class.equals(required)) {
6459: if (fSourceViewer instanceof ITextViewerExtension) {
6460: ITextViewerExtension extension = (ITextViewerExtension) fSourceViewer;
6461: return extension.getRewriteTarget();
6462: }
6463: return null;
6464: }
6465:
6466: if (Control.class.equals(required))
6467: return fSourceViewer != null ? fSourceViewer
6468: .getTextWidget() : null;
6469:
6470: if (IColumnSupport.class.equals(required)) {
6471: if (fColumnSupport == null)
6472: fColumnSupport = createColumnSupport();
6473: return fColumnSupport;
6474: }
6475:
6476: return super .getAdapter(required);
6477: }
6478:
6479: /*
6480: * @see IWorkbenchPart#setFocus()
6481: */
6482: public void setFocus() {
6483: if (fSourceViewer != null
6484: && fSourceViewer.getTextWidget() != null)
6485: fSourceViewer.getTextWidget().setFocus();
6486: }
6487:
6488: /*
6489: * @see ITextEditor#showsHighlightRangeOnly()
6490: */
6491: public boolean showsHighlightRangeOnly() {
6492: return fShowHighlightRangeOnly;
6493: }
6494:
6495: /*
6496: * @see ITextEditor#showHighlightRangeOnly(boolean)
6497: */
6498: public void showHighlightRangeOnly(boolean showHighlightRangeOnly) {
6499: fShowHighlightRangeOnly = showHighlightRangeOnly;
6500: }
6501:
6502: /*
6503: * @see ITextEditor#setHighlightRange(int, int, boolean)
6504: */
6505: public void setHighlightRange(int offset, int length,
6506: boolean moveCursor) {
6507: if (fSourceViewer == null)
6508: return;
6509:
6510: if (fShowHighlightRangeOnly) {
6511: if (moveCursor)
6512: fSourceViewer.setVisibleRegion(offset, length);
6513: } else {
6514: IRegion rangeIndication = fSourceViewer
6515: .getRangeIndication();
6516: if (rangeIndication == null
6517: || offset != rangeIndication.getOffset()
6518: || length != rangeIndication.getLength())
6519: fSourceViewer.setRangeIndication(offset, length,
6520: moveCursor);
6521: }
6522: }
6523:
6524: /*
6525: * @see ITextEditor#getHighlightRange()
6526: */
6527: public IRegion getHighlightRange() {
6528: if (fSourceViewer == null)
6529: return null;
6530:
6531: if (fShowHighlightRangeOnly)
6532: return getCoverage(fSourceViewer);
6533:
6534: return fSourceViewer.getRangeIndication();
6535: }
6536:
6537: /*
6538: * @see ITextEditor#resetHighlightRange
6539: */
6540: public void resetHighlightRange() {
6541: if (fSourceViewer == null)
6542: return;
6543:
6544: if (fShowHighlightRangeOnly)
6545: fSourceViewer.resetVisibleRegion();
6546: else
6547: fSourceViewer.removeRangeIndication();
6548: }
6549:
6550: /**
6551: * Adjusts the highlight range so that at least the specified range
6552: * is highlighted.
6553: * <p>
6554: * Subclasses may re-implement this method.</p>
6555: *
6556: * @param offset the offset of the range which at least should be highlighted
6557: * @param length the length of the range which at least should be highlighted
6558: */
6559: protected void adjustHighlightRange(int offset, int length) {
6560: if (fSourceViewer == null)
6561: return;
6562:
6563: if (fSourceViewer instanceof ITextViewerExtension5) {
6564: ITextViewerExtension5 extension = (ITextViewerExtension5) fSourceViewer;
6565: extension.exposeModelRange(new Region(offset, length));
6566: } else if (!isVisible(fSourceViewer, offset, length)) {
6567: fSourceViewer.resetVisibleRegion();
6568: }
6569: }
6570:
6571: /*
6572: * @see ITextEditor#selectAndReveal(int, int)
6573: */
6574: public void selectAndReveal(int start, int length) {
6575: selectAndReveal(start, length, start, length);
6576: }
6577:
6578: /**
6579: * Selects and reveals the specified ranges in this text editor.
6580: *
6581: * @param selectionStart the offset of the selection
6582: * @param selectionLength the length of the selection
6583: * @param revealStart the offset of the revealed range
6584: * @param revealLength the length of the revealed range
6585: * @since 3.0
6586: */
6587: protected void selectAndReveal(int selectionStart,
6588: int selectionLength, int revealStart, int revealLength) {
6589: if (fSourceViewer == null)
6590: return;
6591:
6592: ISelection selection = getSelectionProvider().getSelection();
6593: if (selection instanceof ITextSelection) {
6594: ITextSelection textSelection = (ITextSelection) selection;
6595: if (textSelection.getOffset() != 0
6596: || textSelection.getLength() != 0)
6597: markInNavigationHistory();
6598: }
6599:
6600: StyledText widget = fSourceViewer.getTextWidget();
6601: widget.setRedraw(false);
6602: {
6603: adjustHighlightRange(revealStart, revealLength);
6604: fSourceViewer.revealRange(revealStart, revealLength);
6605:
6606: fSourceViewer.setSelectedRange(selectionStart,
6607: selectionLength);
6608:
6609: markInNavigationHistory();
6610: }
6611: widget.setRedraw(true);
6612: }
6613:
6614: /*
6615: * @see org.eclipse.ui.INavigationLocationProvider#createNavigationLocation()
6616: * @since 2.1
6617: */
6618: public INavigationLocation createEmptyNavigationLocation() {
6619: return new TextSelectionNavigationLocation(this , false);
6620: }
6621:
6622: /*
6623: * @see org.eclipse.ui.INavigationLocationProvider#createNavigationLocation()
6624: */
6625: public INavigationLocation createNavigationLocation() {
6626: return new TextSelectionNavigationLocation(this , true);
6627: }
6628:
6629: /**
6630: * Writes a check mark of the given situation into the navigation history.
6631: * @since 2.1
6632: */
6633: protected void markInNavigationHistory() {
6634: getSite().getPage().getNavigationHistory().markLocation(this );
6635: }
6636:
6637: /**
6638: * Hook which gets called when the editor has been saved.
6639: * Subclasses may extend.
6640: * @since 2.1
6641: */
6642: protected void editorSaved() {
6643: INavigationLocation[] locations = getSite().getPage()
6644: .getNavigationHistory().getLocations();
6645: IEditorInput input = getEditorInput();
6646: for (int i = 0; i < locations.length; i++) {
6647: if (locations[i] instanceof TextSelectionNavigationLocation) {
6648: if (input.equals(locations[i].getInput())) {
6649: TextSelectionNavigationLocation location = (TextSelectionNavigationLocation) locations[i];
6650: location.partSaved(this );
6651: }
6652: }
6653: }
6654: }
6655:
6656: /*
6657: * @see WorkbenchPart#firePropertyChange(int)
6658: */
6659: protected void firePropertyChange(int property) {
6660: super .firePropertyChange(property);
6661: updatePropertyDependentActions();
6662: }
6663:
6664: /*
6665: * @see ITextEditorExtension#setStatusField(IStatusField, String)
6666: * @since 2.0
6667: */
6668: public void setStatusField(IStatusField field, String category) {
6669: Assert.isNotNull(category);
6670: if (field != null) {
6671:
6672: if (fStatusFields == null)
6673: fStatusFields = new HashMap(3);
6674:
6675: fStatusFields.put(category, field);
6676: updateStatusField(category);
6677:
6678: } else if (fStatusFields != null)
6679: fStatusFields.remove(category);
6680:
6681: if (fIncrementalFindTarget != null
6682: && ITextEditorActionConstants.STATUS_CATEGORY_FIND_FIELD
6683: .equals(category))
6684: fIncrementalFindTarget.setStatusField(field);
6685: }
6686:
6687: /**
6688: * Returns the current status field for the given status category.
6689: *
6690: * @param category the status category
6691: * @return the current status field for the given status category
6692: * @since 2.0
6693: */
6694: protected IStatusField getStatusField(String category) {
6695: if (category != null && fStatusFields != null)
6696: return (IStatusField) fStatusFields.get(category);
6697: return null;
6698: }
6699:
6700: /**
6701: * Returns whether this editor is in overwrite or insert mode.
6702: *
6703: * @return <code>true</code> if in insert mode, <code>false</code> for overwrite mode
6704: * @since 2.0
6705: */
6706: protected boolean isInInsertMode() {
6707: return !fIsOverwriting;
6708: }
6709:
6710: /*
6711: * @see org.eclipse.ui.texteditor.ITextEditorExtension3#getInsertMode()
6712: * @since 3.0
6713: */
6714: public InsertMode getInsertMode() {
6715: return fInsertMode;
6716: }
6717:
6718: /*
6719: * @see org.eclipse.ui.texteditor.ITextEditorExtension3#setInsertMode(org.eclipse.ui.texteditor.ITextEditorExtension3.InsertMode)
6720: * @since 3.0
6721: */
6722: public void setInsertMode(InsertMode newMode) {
6723: List legalModes = getLegalInsertModes();
6724: if (!legalModes.contains(newMode))
6725: throw new IllegalArgumentException();
6726:
6727: fInsertMode = newMode;
6728:
6729: handleInsertModeChanged();
6730: }
6731:
6732: /**
6733: * Returns the set of legal insert modes. If insert modes are configured all defined insert modes
6734: * are legal.
6735: *
6736: * @return the set of legal insert modes
6737: * @since 3.0
6738: */
6739: protected List getLegalInsertModes() {
6740: if (fLegalInsertModes == null) {
6741: fLegalInsertModes = new ArrayList();
6742: fLegalInsertModes.add(SMART_INSERT);
6743: fLegalInsertModes.add(INSERT);
6744: }
6745: return fLegalInsertModes;
6746: }
6747:
6748: private void switchToNextInsertMode() {
6749:
6750: InsertMode mode = getInsertMode();
6751: List legalModes = getLegalInsertModes();
6752:
6753: int i = 0;
6754: while (i < legalModes.size()) {
6755: if (legalModes.get(i) == mode)
6756: break;
6757: ++i;
6758: }
6759:
6760: i = (i + 1) % legalModes.size();
6761: InsertMode newMode = (InsertMode) legalModes.get(i);
6762: setInsertMode(newMode);
6763: }
6764:
6765: private void toggleOverwriteMode() {
6766: if (fIsOverwriteModeEnabled) {
6767: fIsOverwriting = !fIsOverwriting;
6768: fSourceViewer.getTextWidget().invokeAction(
6769: ST.TOGGLE_OVERWRITE);
6770: handleInsertModeChanged();
6771: }
6772: }
6773:
6774: /**
6775: * Configures the given insert mode as legal or illegal. This call is ignored if the set of legal
6776: * input modes would be empty after the call.
6777: *
6778: * @param mode the insert mode to be configured
6779: * @param legal <code>true</code> if the given mode is legal, <code>false</code> otherwise
6780: * @since 3.0
6781: */
6782: protected void configureInsertMode(InsertMode mode, boolean legal) {
6783: List legalModes = getLegalInsertModes();
6784: if (legal) {
6785: if (!legalModes.contains(mode))
6786: legalModes.add(mode);
6787: } else if (legalModes.size() > 1) {
6788: if (getInsertMode() == mode)
6789: switchToNextInsertMode();
6790: legalModes.remove(mode);
6791: }
6792: }
6793:
6794: /**
6795: * Sets the overwrite mode enablement.
6796: *
6797: * @param enable <code>true</code> to enable new overwrite mode,
6798: * <code>false</code> to disable
6799: * @since 3.0
6800: */
6801: protected void enableOverwriteMode(boolean enable) {
6802: if (fIsOverwriting && !enable)
6803: toggleOverwriteMode();
6804: fIsOverwriteModeEnabled = enable;
6805: }
6806:
6807: private Caret createOverwriteCaret(StyledText styledText) {
6808: Caret caret = new Caret(styledText, SWT.NULL);
6809: GC gc = new GC(styledText);
6810: // XXX this overwrite box is not proportional-font aware
6811: // take 'a' as a medium sized character
6812: Point charSize = gc.stringExtent("a"); //$NON-NLS-1$
6813:
6814: // XXX: Filed request to get a caret with auto-height: https://bugs.eclipse.org/bugs/show_bug.cgi?id=118612
6815: caret.setSize(charSize.x, styledText.getLineHeight());
6816: caret.setFont(styledText.getFont());
6817:
6818: gc.dispose();
6819:
6820: return caret;
6821: }
6822:
6823: private Caret createInsertCaret(StyledText styledText) {
6824: Caret caret = new Caret(styledText, SWT.NULL);
6825:
6826: // XXX: Filed request to get a caret with auto-height: https://bugs.eclipse.org/bugs/show_bug.cgi?id=118612
6827: caret.setSize(getCaretWidthPreference(), styledText
6828: .getLineHeight());
6829: caret.setFont(styledText.getFont());
6830:
6831: return caret;
6832: }
6833:
6834: private Image createRawInsertModeCaretImage(StyledText styledText) {
6835:
6836: PaletteData caretPalette = new PaletteData(new RGB[] {
6837: new RGB(0, 0, 0), new RGB(255, 255, 255) });
6838: int width = getCaretWidthPreference();
6839: int widthOffset = width - 1;
6840:
6841: // XXX: Filed request to get a caret with auto-height: https://bugs.eclipse.org/bugs/show_bug.cgi?id=118612
6842: ImageData imageData = new ImageData(4 + widthOffset, styledText
6843: .getLineHeight(), 1, caretPalette);
6844:
6845: Display display = styledText.getDisplay();
6846: Image bracketImage = new Image(display, imageData);
6847: GC gc = new GC(bracketImage);
6848: gc.setForeground(display.getSystemColor(SWT.COLOR_WHITE));
6849: gc.setLineWidth(0); // NOTE: 0 means width is 1 but with optimized performance
6850: int height = imageData.height / 3;
6851: // gap between two bars of one third of the height
6852: // draw boxes using lines as drawing a line of a certain width produces
6853: // rounded corners.
6854: for (int i = 0; i < width; i++) {
6855: gc.drawLine(i, 0, i, height - 1);
6856: gc.drawLine(i, imageData.height - height, i,
6857: imageData.height - 1);
6858: }
6859:
6860: gc.dispose();
6861:
6862: return bracketImage;
6863: }
6864:
6865: private Caret createRawInsertModeCaret(StyledText styledText) {
6866: // don't draw special raw caret if no smart mode is enabled
6867: if (!getLegalInsertModes().contains(SMART_INSERT))
6868: return createInsertCaret(styledText);
6869:
6870: Caret caret = new Caret(styledText, SWT.NULL);
6871: Image image = createRawInsertModeCaretImage(styledText);
6872: if (image != null)
6873: caret.setImage(image);
6874: else {
6875: // XXX: Filed request to get a caret with auto-height: https://bugs.eclipse.org/bugs/show_bug.cgi?id=118612
6876: caret.setSize(getCaretWidthPreference(), styledText
6877: .getLineHeight());
6878: }
6879:
6880: caret.setFont(styledText.getFont());
6881:
6882: return caret;
6883: }
6884:
6885: private int getCaretWidthPreference() {
6886: if (getPreferenceStore() != null
6887: && getPreferenceStore().getBoolean(
6888: PREFERENCE_WIDE_CARET))
6889: return WIDE_CARET_WIDTH;
6890:
6891: return SINGLE_CARET_WIDTH;
6892: }
6893:
6894: private void updateCaret() {
6895:
6896: if (fSourceViewer == null)
6897: return;
6898:
6899: StyledText styledText = fSourceViewer.getTextWidget();
6900:
6901: InsertMode mode = getInsertMode();
6902:
6903: styledText.setCaret(null);
6904: disposeNonDefaultCaret();
6905:
6906: if (getPreferenceStore() == null
6907: || !getPreferenceStore().getBoolean(
6908: PREFERENCE_USE_CUSTOM_CARETS))
6909: Assert.isTrue(fNonDefaultCaret == null);
6910: else if (fIsOverwriting)
6911: fNonDefaultCaret = createOverwriteCaret(styledText);
6912: else if (SMART_INSERT == mode)
6913: fNonDefaultCaret = createInsertCaret(styledText);
6914: else if (INSERT == mode)
6915: fNonDefaultCaret = createRawInsertModeCaret(styledText);
6916:
6917: if (fNonDefaultCaret != null) {
6918: styledText.setCaret(fNonDefaultCaret);
6919: fNonDefaultCaretImage = fNonDefaultCaret.getImage();
6920: } else if (fInitialCaret != styledText.getCaret())
6921: styledText.setCaret(fInitialCaret);
6922: }
6923:
6924: private void disposeNonDefaultCaret() {
6925: if (fNonDefaultCaretImage != null) {
6926: fNonDefaultCaretImage.dispose();
6927: fNonDefaultCaretImage = null;
6928: }
6929:
6930: if (fNonDefaultCaret != null) {
6931: fNonDefaultCaret.dispose();
6932: fNonDefaultCaret = null;
6933: }
6934: }
6935:
6936: /**
6937: * Handles a change of the editor's insert mode.
6938: * Subclasses may extend.
6939: *
6940: * @since 2.0
6941: */
6942: protected void handleInsertModeChanged() {
6943: updateInsertModeAction();
6944: updateCaret();
6945: updateStatusField(ITextEditorActionConstants.STATUS_CATEGORY_INPUT_MODE);
6946: }
6947:
6948: private void updateInsertModeAction() {
6949:
6950: // this may be called before the part is fully initialized (see configureInsertMode)
6951: // drop out in this case.
6952: if (getSite() == null)
6953: return;
6954:
6955: IAction action = getAction(ITextEditorActionConstants.TOGGLE_INSERT_MODE);
6956: if (action != null) {
6957: action.setEnabled(!fIsOverwriting);
6958: action.setChecked(fInsertMode == SMART_INSERT);
6959: }
6960: }
6961:
6962: /**
6963: * Handles a potential change of the cursor position.
6964: * Subclasses may extend.
6965: *
6966: * @since 2.0
6967: */
6968: protected void handleCursorPositionChanged() {
6969: updateStatusField(ITextEditorActionConstants.STATUS_CATEGORY_INPUT_POSITION);
6970: }
6971:
6972: /**
6973: * Updates the status fields for the given category.
6974: *
6975: * @param category
6976: * @since 2.0
6977: */
6978: protected void updateStatusField(String category) {
6979:
6980: if (category == null)
6981: return;
6982:
6983: IStatusField field = getStatusField(category);
6984: if (field != null) {
6985:
6986: String text = null;
6987:
6988: if (ITextEditorActionConstants.STATUS_CATEGORY_INPUT_POSITION
6989: .equals(category))
6990: text = getCursorPosition();
6991: else if (ITextEditorActionConstants.STATUS_CATEGORY_ELEMENT_STATE
6992: .equals(category))
6993: text = isEditorInputReadOnly() ? fReadOnlyLabel
6994: : fWritableLabel;
6995: else if (ITextEditorActionConstants.STATUS_CATEGORY_INPUT_MODE
6996: .equals(category)) {
6997: InsertMode mode = getInsertMode();
6998: if (fIsOverwriting)
6999: text = fOverwriteModeLabel;
7000: else if (INSERT == mode)
7001: text = fInsertModeLabel;
7002: else if (SMART_INSERT == mode)
7003: text = fSmartInsertModeLabel;
7004: }
7005:
7006: field.setText(text == null ? fErrorLabel : text);
7007: }
7008: }
7009:
7010: /**
7011: * Updates all status fields.
7012: *
7013: * @since 2.0
7014: */
7015: protected void updateStatusFields() {
7016: if (fStatusFields != null) {
7017: Iterator e = fStatusFields.keySet().iterator();
7018: while (e.hasNext())
7019: updateStatusField((String) e.next());
7020: }
7021: }
7022:
7023: /**
7024: * Returns a description of the cursor position.
7025: *
7026: * @return a description of the cursor position
7027: * @since 2.0
7028: */
7029: protected String getCursorPosition() {
7030:
7031: if (fSourceViewer == null)
7032: return fErrorLabel;
7033:
7034: StyledText styledText = fSourceViewer.getTextWidget();
7035: int caret = widgetOffset2ModelOffset(fSourceViewer, styledText
7036: .getCaretOffset());
7037: IDocument document = fSourceViewer.getDocument();
7038:
7039: if (document == null)
7040: return fErrorLabel;
7041:
7042: try {
7043:
7044: int line = document.getLineOfOffset(caret);
7045:
7046: int lineOffset = document.getLineOffset(line);
7047: int tabWidth = styledText.getTabs();
7048: int column = 0;
7049: for (int i = lineOffset; i < caret; i++)
7050: if ('\t' == document.getChar(i))
7051: column += tabWidth
7052: - (tabWidth == 0 ? 0 : column % tabWidth);
7053: else
7054: column++;
7055:
7056: fLineLabel.fValue = line + 1;
7057: fColumnLabel.fValue = column + 1;
7058: return NLSUtility.format(fPositionLabelPattern,
7059: fPositionLabelPatternArguments);
7060:
7061: } catch (BadLocationException x) {
7062: return fErrorLabel;
7063: }
7064: }
7065:
7066: /*
7067: * @see ITextEditorExtension#isEditorInputReadOnly()
7068: * @since 2.0
7069: */
7070: public boolean isEditorInputReadOnly() {
7071: IDocumentProvider provider = getDocumentProvider();
7072: if (provider instanceof IDocumentProviderExtension) {
7073: IDocumentProviderExtension extension = (IDocumentProviderExtension) provider;
7074: return extension.isReadOnly(getEditorInput());
7075: }
7076: return true;
7077: }
7078:
7079: /*
7080: * @see ITextEditorExtension2#isEditorInputModifiable()
7081: * @since 2.1
7082: */
7083: public boolean isEditorInputModifiable() {
7084: IDocumentProvider provider = getDocumentProvider();
7085: if (provider instanceof IDocumentProviderExtension) {
7086: IDocumentProviderExtension extension = (IDocumentProviderExtension) provider;
7087: return extension.isModifiable(getEditorInput());
7088: }
7089: return true;
7090: }
7091:
7092: /*
7093: * @see ITextEditorExtension#addRulerContextMenuListener(IMenuListener)
7094: * @since 2.0
7095: */
7096: public void addRulerContextMenuListener(IMenuListener listener) {
7097: fRulerContextMenuListeners.add(listener);
7098: }
7099:
7100: /*
7101: * @see ITextEditorExtension#removeRulerContextMenuListener(IMenuListener)
7102: * @since 2.0
7103: */
7104: public void removeRulerContextMenuListener(IMenuListener listener) {
7105: fRulerContextMenuListeners.remove(listener);
7106: }
7107:
7108: /**
7109: * Returns whether this editor can handle the move of the original element
7110: * so that it ends up being the moved element. By default this method
7111: * returns <code>true</code>. Subclasses may reimplement.
7112: *
7113: * @param originalElement the original element
7114: * @param movedElement the moved element
7115: * @return whether this editor can handle the move of the original element
7116: * so that it ends up being the moved element
7117: * @since 2.0
7118: */
7119: protected boolean canHandleMove(IEditorInput originalElement,
7120: IEditorInput movedElement) {
7121: return true;
7122: }
7123:
7124: /**
7125: * Returns the offset of the given source viewer's document that corresponds
7126: * to the given widget offset or <code>-1</code> if there is no such offset.
7127: *
7128: * @param viewer the source viewer
7129: * @param widgetOffset the widget offset
7130: * @return the corresponding offset in the source viewer's document or <code>-1</code>
7131: * @since 2.1
7132: */
7133: protected final static int widgetOffset2ModelOffset(
7134: ISourceViewer viewer, int widgetOffset) {
7135: if (viewer instanceof ITextViewerExtension5) {
7136: ITextViewerExtension5 extension = (ITextViewerExtension5) viewer;
7137: return extension.widgetOffset2ModelOffset(widgetOffset);
7138: }
7139: return widgetOffset + viewer.getVisibleRegion().getOffset();
7140: }
7141:
7142: /**
7143: * Returns the offset of the given source viewer's text widget that corresponds
7144: * to the given model offset or <code>-1</code> if there is no such offset.
7145: *
7146: * @param viewer the source viewer
7147: * @param modelOffset the model offset
7148: * @return the corresponding offset in the source viewer's text widget or <code>-1</code>
7149: * @since 3.0
7150: */
7151: protected final static int modelOffset2WidgetOffset(
7152: ISourceViewer viewer, int modelOffset) {
7153: if (viewer instanceof ITextViewerExtension5) {
7154: ITextViewerExtension5 extension = (ITextViewerExtension5) viewer;
7155: return extension.modelOffset2WidgetOffset(modelOffset);
7156: }
7157: return modelOffset - viewer.getVisibleRegion().getOffset();
7158: }
7159:
7160: /**
7161: * Returns the minimal region of the given source viewer's document that completely
7162: * comprises everything that is visible in the viewer's widget.
7163: *
7164: * @param viewer the viewer go return the coverage for
7165: * @return the minimal region of the source viewer's document comprising the contents of the viewer's widget
7166: * @since 2.1
7167: */
7168: protected final static IRegion getCoverage(ISourceViewer viewer) {
7169: if (viewer instanceof ITextViewerExtension5) {
7170: ITextViewerExtension5 extension = (ITextViewerExtension5) viewer;
7171: return extension.getModelCoverage();
7172: }
7173: return viewer.getVisibleRegion();
7174: }
7175:
7176: /**
7177: * Tells whether the given region is visible in the given source viewer.
7178: *
7179: * @param viewer the source viewer
7180: * @param offset the offset of the region
7181: * @param length the length of the region
7182: * @return <code>true</code> if visible
7183: * @since 2.1
7184: */
7185: protected static final boolean isVisible(ISourceViewer viewer,
7186: int offset, int length) {
7187: if (viewer instanceof ITextViewerExtension5) {
7188: ITextViewerExtension5 extension = (ITextViewerExtension5) viewer;
7189: IRegion overlap = extension
7190: .modelRange2WidgetRange(new Region(offset, length));
7191: return overlap != null;
7192: }
7193: return viewer.overlapsWithVisibleRegion(offset, length);
7194: }
7195:
7196: /*
7197: * @see org.eclipse.ui.texteditor.ITextEditorExtension3#showChangeInformation(boolean)
7198: * @since 3.0
7199: */
7200: public void showChangeInformation(boolean show) {
7201: // do nothing
7202: }
7203:
7204: /*
7205: * @see org.eclipse.ui.texteditor.ITextEditorExtension3#isChangeInformationShowing()
7206: * @since 3.0
7207: */
7208: public boolean isChangeInformationShowing() {
7209: return false;
7210: }
7211:
7212: /**
7213: * Sets the given message as error message to this editor's status line.
7214: *
7215: * @param message message to be set
7216: * @since 3.2
7217: */
7218: protected void setStatusLineErrorMessage(String message) {
7219: IEditorStatusLine statusLine = (IEditorStatusLine) getAdapter(IEditorStatusLine.class);
7220: if (statusLine != null)
7221: statusLine.setMessage(true, message, null);
7222: }
7223:
7224: /**
7225: * Sets the given message as message to this editor's status line.
7226: *
7227: * @param message message to be set
7228: * @since 3.2
7229: */
7230: protected void setStatusLineMessage(String message) {
7231: IEditorStatusLine statusLine = (IEditorStatusLine) getAdapter(IEditorStatusLine.class);
7232: if (statusLine != null)
7233: statusLine.setMessage(false, message, null);
7234: }
7235:
7236: /**
7237: * Jumps to the next annotation according to the given direction.
7238: *
7239: * @param forward <code>true</code> if search direction is forward, <code>false</code> if backward
7240: * @return the selected annotation or <code>null</code> if none
7241: * @see #isNavigationTarget(Annotation)
7242: * @see #findAnnotation(int, int, boolean, Position)
7243: * @since 3.2
7244: */
7245: public Annotation gotoAnnotation(boolean forward) {
7246: ITextSelection selection = (ITextSelection) getSelectionProvider()
7247: .getSelection();
7248: Position position = new Position(0, 0);
7249: Annotation annotation = findAnnotation(selection.getOffset(),
7250: selection.getLength(), forward, position);
7251: setStatusLineErrorMessage(null);
7252: setStatusLineMessage(null);
7253:
7254: if (annotation != null) {
7255: selectAndReveal(position.getOffset(), position.getLength());
7256: setStatusLineMessage(annotation.getText());
7257: }
7258: return annotation;
7259: }
7260:
7261: /**
7262: * Returns the annotation closest to the given range respecting the given
7263: * direction. If an annotation is found, the annotations current position
7264: * is copied into the provided annotation position.
7265: *
7266: * @param offset the region offset
7267: * @param length the region length
7268: * @param forward <code>true</code> for forwards, <code>false</code> for backward
7269: * @param annotationPosition the position of the found annotation
7270: * @return the found annotation
7271: * @since 3.2
7272: */
7273: protected Annotation findAnnotation(final int offset,
7274: final int length, boolean forward,
7275: Position annotationPosition) {
7276:
7277: Annotation nextAnnotation = null;
7278: Position nextAnnotationPosition = null;
7279: Annotation containingAnnotation = null;
7280: Position containingAnnotationPosition = null;
7281: boolean currentAnnotation = false;
7282:
7283: IDocument document = getDocumentProvider().getDocument(
7284: getEditorInput());
7285: int endOfDocument = document.getLength();
7286: int distance = Integer.MAX_VALUE;
7287:
7288: IAnnotationModel model = getDocumentProvider()
7289: .getAnnotationModel(getEditorInput());
7290: Iterator e = model.getAnnotationIterator();
7291: while (e.hasNext()) {
7292: Annotation a = (Annotation) e.next();
7293: if (!isNavigationTarget(a))
7294: continue;
7295:
7296: Position p = model.getPosition(a);
7297: if (p == null)
7298: continue;
7299:
7300: if (forward && p.offset == offset || !forward
7301: && p.offset + p.getLength() == offset + length) {// || p.includes(offset)) {
7302: if (containingAnnotation == null
7303: || (forward
7304: && p.length >= containingAnnotationPosition.length || !forward
7305: && p.length >= containingAnnotationPosition.length)) {
7306: containingAnnotation = a;
7307: containingAnnotationPosition = p;
7308: currentAnnotation = p.length == length;
7309: }
7310: } else {
7311: int currentDistance = 0;
7312:
7313: if (forward) {
7314: currentDistance = p.getOffset() - offset;
7315: if (currentDistance < 0)
7316: currentDistance = endOfDocument
7317: + currentDistance;
7318:
7319: if (currentDistance < distance
7320: || currentDistance == distance
7321: && p.length < nextAnnotationPosition.length) {
7322: distance = currentDistance;
7323: nextAnnotation = a;
7324: nextAnnotationPosition = p;
7325: }
7326: } else {
7327: currentDistance = offset + length
7328: - (p.getOffset() + p.length);
7329: if (currentDistance < 0)
7330: currentDistance = endOfDocument
7331: + currentDistance;
7332:
7333: if (currentDistance < distance
7334: || currentDistance == distance
7335: && p.length < nextAnnotationPosition.length) {
7336: distance = currentDistance;
7337: nextAnnotation = a;
7338: nextAnnotationPosition = p;
7339: }
7340: }
7341: }
7342: }
7343: if (containingAnnotationPosition != null
7344: && (!currentAnnotation || nextAnnotation == null)) {
7345: annotationPosition.setOffset(containingAnnotationPosition
7346: .getOffset());
7347: annotationPosition.setLength(containingAnnotationPosition
7348: .getLength());
7349: return containingAnnotation;
7350: }
7351: if (nextAnnotationPosition != null) {
7352: annotationPosition.setOffset(nextAnnotationPosition
7353: .getOffset());
7354: annotationPosition.setLength(nextAnnotationPosition
7355: .getLength());
7356: }
7357:
7358: return nextAnnotation;
7359: }
7360:
7361: /**
7362: * Returns whether the given annotation is configured as a target for the
7363: * "Go to Next/Previous Annotation" actions.
7364: * <p>
7365: * Per default every annotation is a target.
7366: * </p>
7367: *
7368: * @param annotation the annotation
7369: * @return <code>true</code> if this is a target, <code>false</code> otherwise
7370: * @since 3.2
7371: */
7372: protected boolean isNavigationTarget(Annotation annotation) {
7373: return true;
7374: }
7375:
7376: /*
7377: * @see org.eclipse.ui.texteditor.ITextEditorExtension4#showRevisionInformation(org.eclipse.jface.text.revisions.RevisionInformation, java.lang.String)
7378: * @since 3.2
7379: */
7380: public void showRevisionInformation(RevisionInformation info,
7381: String quickDiffProviderId) {
7382: // no implementation
7383: }
7384:
7385: /*
7386: * @see org.eclipse.ui.IEditorPersistable#restoreState(org.eclipse.ui.IMemento)
7387: * @since 3.3
7388: */
7389: public void restoreState(IMemento memento) {
7390: fMementoToRestore = memento;
7391: }
7392:
7393: /*
7394: * @see org.eclipse.ui.IPersistable#saveState(org.eclipse.ui.IMemento)
7395: * @since 3.3
7396: */
7397: public void saveState(IMemento memento) {
7398: ISelection selection = doGetSelection();
7399: if (selection instanceof ITextSelection) {
7400: memento.putInteger(TAG_SELECTION_OFFSET,
7401: ((ITextSelection) selection).getOffset());
7402: memento.putInteger(TAG_SELECTION_LENGTH,
7403: ((ITextSelection) selection).getLength());
7404: // https://bugs.eclipse.org/bugs/show_bug.cgi?id=168524
7405: memento.putInteger(TAG_SELECTION_HPIXEL, getSourceViewer()
7406: .getTextWidget().getHorizontalPixel());
7407: }
7408: }
7409:
7410: /**
7411: * Returns whether the given memento contains saved state
7412: * <p>
7413: * Subclasses may extend or override this method.</p>
7414: *
7415: * @param memento the saved state of this editor
7416: * @return <code>true</code> if the given memento contains saved state
7417: * @since 3.3
7418: */
7419: protected boolean containsSavedState(IMemento memento) {
7420: return memento.getInteger(TAG_SELECTION_OFFSET) != null
7421: && memento.getInteger(TAG_SELECTION_LENGTH) != null;
7422: }
7423:
7424: /**
7425: * Restores this editor's state using the given memento.
7426: * <p>
7427: * Subclasses may extend or override this method.</p>
7428: *
7429: * @param memento the saved state of this editor
7430: * @since 3.3
7431: */
7432: protected void doRestoreState(IMemento memento) {
7433: Integer offset = memento.getInteger(TAG_SELECTION_OFFSET);
7434: if (offset == null)
7435: return;
7436:
7437: Integer length = memento.getInteger(TAG_SELECTION_LENGTH);
7438: if (length == null)
7439: return;
7440:
7441: doSetSelection(new TextSelection(offset.intValue(), length
7442: .intValue()));
7443:
7444: // XXX: https://bugs.eclipse.org/bugs/show_bug.cgi?id=168524
7445: Integer horizontalPixel = memento
7446: .getInteger(TAG_SELECTION_HPIXEL);
7447: if (horizontalPixel == null)
7448: return;
7449: StyledText textWidget = getSourceViewer().getTextWidget();
7450: if (!textWidget.isVisible())
7451: textWidget.setHorizontalPixel(horizontalPixel.intValue());
7452: }
7453:
7454: /*
7455: * @see org.eclipse.ui.ISaveablesSource#getSaveables()
7456: * @since 3.3
7457: */
7458: public Saveable[] getSaveables() {
7459: if (fSavable == null)
7460: fSavable = new TextEditorSavable(this );
7461:
7462: return new Saveable[] { fSavable };
7463: }
7464:
7465: /*
7466: * @see org.eclipse.ui.ISaveablesSource#getActiveSaveables()
7467: * @since 3.3
7468: */
7469: public Saveable[] getActiveSaveables() {
7470: return getSaveables();
7471: }
7472:
7473: /**
7474: * This text editor's savable.
7475: *
7476: * @since 3.3
7477: */
7478: protected static class TextEditorSavable extends Saveable {
7479:
7480: /** The cached editor. */
7481: private ITextEditor fTextEditor;
7482: /** The cached editor input. */
7483: private IEditorInput fEditorInput;
7484: /** The cached document. */
7485: private IDocument fDocument;
7486:
7487: /**
7488: * Creates a new savable for this text editor.
7489: *
7490: * @param textEditor the text editor
7491: */
7492: public TextEditorSavable(ITextEditor textEditor) {
7493: Assert.isLegal(textEditor != null);
7494: fTextEditor = textEditor;
7495: fEditorInput = fTextEditor.getEditorInput();
7496: Assert.isLegal(fEditorInput != null);
7497: }
7498:
7499: /**
7500: * Disconnects the editor from this savable.
7501: */
7502: public void disconnectEditor() {
7503: getAdapter(IDocument.class); // make sure the document is cached
7504: fTextEditor = null;
7505: }
7506:
7507: /*
7508: * @see org.eclipse.ui.Saveable#getName()
7509: */
7510: public String getName() {
7511: return fEditorInput.getName();
7512: }
7513:
7514: /*
7515: * @see org.eclipse.ui.Saveable#getToolTipText()
7516: */
7517: public String getToolTipText() {
7518: return fEditorInput.getToolTipText();
7519: }
7520:
7521: /*
7522: * @see org.eclipse.ui.Saveable#getImageDescriptor()
7523: */
7524: public ImageDescriptor getImageDescriptor() {
7525: return fEditorInput.getImageDescriptor();
7526: }
7527:
7528: /*
7529: * @see org.eclipse.ui.Saveable#doSave(org.eclipse.core.runtime.IProgressMonitor)
7530: * @since 3.3
7531: */
7532: public void doSave(IProgressMonitor monitor)
7533: throws CoreException {
7534: fTextEditor.doSave(monitor);
7535: }
7536:
7537: public boolean isDirty() {
7538: return fTextEditor.isDirty();
7539: }
7540:
7541: /*
7542: * @see org.eclipse.ui.Saveable#supportsBackgroundSave()
7543: */
7544: public boolean supportsBackgroundSave() {
7545: return false;
7546: }
7547:
7548: /*
7549: * @see org.eclipse.ui.Saveable#hashCode()
7550: */
7551: public int hashCode() {
7552: Object document = getAdapter(IDocument.class);
7553: if (document == null)
7554: return 0;
7555: return document.hashCode();
7556: }
7557:
7558: /*
7559: * @see org.eclipse.ui.Saveable#equals(java.lang.Object)
7560: */
7561: public boolean equals(Object obj) {
7562: if (this == obj)
7563: return true;
7564:
7565: if (!(obj instanceof Saveable))
7566: return false;
7567:
7568: Object this Document = getAdapter(IDocument.class);
7569: Object otherDocument = ((Saveable) obj)
7570: .getAdapter(IDocument.class);
7571:
7572: if (this Document == null && otherDocument == null)
7573: return true;
7574:
7575: return this Document != null
7576: && this Document.equals(otherDocument);
7577: }
7578:
7579: /**
7580: * Explicit comment needed to suppress wrong waning caused
7581: * by http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4848177
7582: *
7583: * @see org.eclipse.ui.Saveable#getAdapter(java.lang.Class)
7584: */
7585: public Object getAdapter(Class adapter) {
7586: if (adapter == IDocument.class) {
7587: if (fDocument == null) {
7588: IDocumentProvider documentProvider = fTextEditor
7589: .getDocumentProvider();
7590: if (documentProvider != null)
7591: fDocument = documentProvider
7592: .getDocument(fEditorInput);
7593: }
7594: return fDocument;
7595: }
7596: return super .getAdapter(adapter);
7597: }
7598: }
7599:
7600: //---- Tabs to spaces conversion support ------------------
7601:
7602: /**
7603: * Installs a tabs to spaces converter.
7604: *
7605: * <p>Subclasses may extend or override this method.</p>
7606: *
7607: * @since 3.3
7608: */
7609: protected void installTabsToSpacesConverter() {
7610: SourceViewerConfiguration config = getSourceViewerConfiguration();
7611: if (config != null
7612: && fSourceViewer instanceof ITextViewerExtension7) {
7613: int tabWidth = config.getTabWidth(fSourceViewer);
7614: TabsToSpacesConverter tabToSpacesConverter = new TabsToSpacesConverter();
7615: tabToSpacesConverter
7616: .setLineTracker(new DefaultLineTracker());
7617: tabToSpacesConverter.setNumberOfSpacesPerTab(tabWidth);
7618: ((ITextViewerExtension7) fSourceViewer)
7619: .setTabsToSpacesConverter(tabToSpacesConverter);
7620: updateIndentPrefixes();
7621: }
7622: }
7623:
7624: /**
7625: * Installs a tabs to spaces converter.
7626: *
7627: * <p>Subclasses may extend or override this method.</p>
7628: *
7629: * @since 3.3
7630: */
7631: protected void uninstallTabsToSpacesConverter() {
7632: if (fSourceViewer instanceof ITextViewerExtension7) {
7633: ((ITextViewerExtension7) fSourceViewer)
7634: .setTabsToSpacesConverter(null);
7635: if (fSourceViewer.getTextWidget() != null)
7636: updateIndentPrefixes();
7637: }
7638: }
7639:
7640: /**
7641: * Tells whether tabs should be converted to
7642: * spaces while editing inside this editor.
7643: *
7644: * <p>Subclasses may override this method.</p>
7645: *
7646: * @return <code>true</code> if tabs should be converted to spaces
7647: * @since 3.3
7648: */
7649: protected boolean isTabsToSpacesConversionEnabled() {
7650: return false;
7651: }
7652:
7653: /**
7654: * Updates the source viewer's indent prefixes with
7655: * the values provided by the source viewer configuration.
7656: *
7657: * @since 3.3
7658: */
7659: protected final void updateIndentPrefixes() {
7660: SourceViewerConfiguration configuration = getSourceViewerConfiguration();
7661: String[] types = configuration
7662: .getConfiguredContentTypes(fSourceViewer);
7663: for (int i = 0; i < types.length; i++) {
7664: String[] prefixes = configuration.getIndentPrefixes(
7665: fSourceViewer, types[i]);
7666: if (prefixes != null && prefixes.length > 0)
7667: fSourceViewer.setIndentPrefixes(prefixes, types[i]);
7668: }
7669: }
7670:
7671: }
|