blob: b3105d2de166574a5ea41e6d2796894fe8a240c3 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2019 IBM Corporation and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* IBM Corporation - initial API and implementation
* Chris.Dennis@invidi.com - http://bugs.eclipse.org/bugs/show_bug.cgi?id=29027
* Michel Ishizuka (cqw10305@nifty.com) - http://bugs.eclipse.org/bugs/show_bug.cgi?id=68963
* Genady Beryozkin, me@genady.org - https://bugs.eclipse.org/bugs/show_bug.cgi?id=11668
* Benjamin Muskalla <b.muskalla@gmx.net> - https://bugs.eclipse.org/bugs/show_bug.cgi?id=41573
* Stephan Wahlbrink <stephan.wahlbrink@walware.de> - Wrong operations mode/feedback for text drag over/drop in text editors - https://bugs.eclipse.org/bugs/show_bug.cgi?id=206043
* Tom Eicher (Avaloq Evolution AG) - block selection mode
* Nick Sandonato <nsandona@us.ibm.com> - [implementation] AbstractTextEditor does not prompt when out of sync in MultiPageEditorPart - http://bugs.eclipse.org/337719
* Holger Voormann - Word Wrap - https://bugs.eclipse.org/bugs/show_bug.cgi?id=35779
* Florian Weßling <flo@cdhq.de> - Word Wrap - https://bugs.eclipse.org/bugs/show_bug.cgi?id=35779
* Andrey Loskutov <loskutov@gmx.de> - Word Wrap - https://bugs.eclipse.org/bugs/show_bug.cgi?id=35779
* Angelo Zerr <angelo.zerr@gmail.com> - [CodeMining] Provide extension point for CodeMining - Bug 528419
*******************************************************************************/
package org.eclipse.ui.texteditor;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.ResourceBundle;
import java.util.concurrent.atomic.AtomicInteger;
import org.osgi.framework.Bundle;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.BusyIndicator;
import org.eclipse.swt.custom.ST;
import org.eclipse.swt.custom.StyledText;
import org.eclipse.swt.custom.VerifyKeyListener;
import org.eclipse.swt.dnd.DND;
import org.eclipse.swt.dnd.DragSource;
import org.eclipse.swt.dnd.DragSourceAdapter;
import org.eclipse.swt.dnd.DragSourceEvent;
import org.eclipse.swt.dnd.DropTargetAdapter;
import org.eclipse.swt.dnd.DropTargetEvent;
import org.eclipse.swt.dnd.DropTargetListener;
import org.eclipse.swt.dnd.TextTransfer;
import org.eclipse.swt.dnd.Transfer;
import org.eclipse.swt.events.GestureEvent;
import org.eclipse.swt.events.GestureListener;
import org.eclipse.swt.events.KeyEvent;
import org.eclipse.swt.events.KeyListener;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.events.MouseListener;
import org.eclipse.swt.events.VerifyEvent;
import org.eclipse.swt.events.VerifyListener;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.Font;
import org.eclipse.swt.graphics.FontData;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.ImageData;
import org.eclipse.swt.graphics.PaletteData;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.RGB;
import org.eclipse.swt.widgets.Caret;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.core.commands.operations.IOperationApprover;
import org.eclipse.core.commands.operations.IOperationHistory;
import org.eclipse.core.commands.operations.IUndoContext;
import org.eclipse.core.commands.operations.OperationHistoryFactory;
import org.eclipse.core.runtime.Assert;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IConfigurationElement;
import org.eclipse.core.runtime.ILog;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.SafeRunner;
import org.eclipse.core.runtime.Status;
import org.eclipse.text.undo.DocumentUndoManagerRegistry;
import org.eclipse.text.undo.IDocumentUndoManager;
import org.eclipse.jface.action.Action;
import org.eclipse.jface.action.GroupMarker;
import org.eclipse.jface.action.IAction;
import org.eclipse.jface.action.IMenuListener;
import org.eclipse.jface.action.IMenuManager;
import org.eclipse.jface.action.IStatusLineManager;
import org.eclipse.jface.action.MenuManager;
import org.eclipse.jface.action.Separator;
import org.eclipse.jface.dialogs.ErrorDialog;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.operation.IRunnableWithProgress;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.jface.preference.PreferenceConverter;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.jface.resource.JFaceResources;
import org.eclipse.jface.util.IPropertyChangeListener;
import org.eclipse.jface.util.PropertyChangeEvent;
import org.eclipse.jface.util.SafeRunnable;
import org.eclipse.jface.viewers.IPostSelectionProvider;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.ISelectionProvider;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.jface.text.AbstractInformationControlManager;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.BlockTextSelection;
import org.eclipse.jface.text.DefaultLineTracker;
import org.eclipse.jface.text.DocumentEvent;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.IDocumentListener;
import org.eclipse.jface.text.IFindReplaceTarget;
import org.eclipse.jface.text.IFindReplaceTargetExtension;
import org.eclipse.jface.text.IInformationControlCreator;
import org.eclipse.jface.text.IMarkRegionTarget;
import org.eclipse.jface.text.IRegion;
import org.eclipse.jface.text.IRewriteTarget;
import org.eclipse.jface.text.ISelectionValidator;
import org.eclipse.jface.text.ITextHover;
import org.eclipse.jface.text.ITextInputListener;
import org.eclipse.jface.text.ITextListener;
import org.eclipse.jface.text.ITextOperationTarget;
import org.eclipse.jface.text.ITextSelection;
import org.eclipse.jface.text.ITextViewer;
import org.eclipse.jface.text.ITextViewerExtension;
import org.eclipse.jface.text.ITextViewerExtension2;
import org.eclipse.jface.text.ITextViewerExtension4;
import org.eclipse.jface.text.ITextViewerExtension5;
import org.eclipse.jface.text.ITextViewerExtension6;
import org.eclipse.jface.text.ITextViewerExtension7;
import org.eclipse.jface.text.ITextViewerExtension8;
import org.eclipse.jface.text.ITextViewerExtension8.EnrichMode;
import org.eclipse.jface.text.IUndoManager;
import org.eclipse.jface.text.IUndoManagerExtension;
import org.eclipse.jface.text.Position;
import org.eclipse.jface.text.Region;
import org.eclipse.jface.text.TabsToSpacesConverter;
import org.eclipse.jface.text.TextEvent;
import org.eclipse.jface.text.TextSelection;
import org.eclipse.jface.text.TextUtilities;
import org.eclipse.jface.text.codemining.ICodeMiningProvider;
import org.eclipse.jface.text.hyperlink.HyperlinkManager;
import org.eclipse.jface.text.hyperlink.IHyperlinkDetector;
import org.eclipse.jface.text.information.IInformationProvider;
import org.eclipse.jface.text.information.IInformationProviderExtension2;
import org.eclipse.jface.text.link.LinkedModeModel;
import org.eclipse.jface.text.link.LinkedPosition;
import org.eclipse.jface.text.quickassist.IQuickAssistAssistant;
import org.eclipse.jface.text.revisions.RevisionInformation;
import org.eclipse.jface.text.source.Annotation;
import org.eclipse.jface.text.source.CompositeRuler;
import org.eclipse.jface.text.source.IAnnotationHover;
import org.eclipse.jface.text.source.IAnnotationModel;
import org.eclipse.jface.text.source.ISourceViewer;
import org.eclipse.jface.text.source.ISourceViewerExtension3;
import org.eclipse.jface.text.source.ISourceViewerExtension4;
import org.eclipse.jface.text.source.ISourceViewerExtension5;
import org.eclipse.jface.text.source.IVerticalRuler;
import org.eclipse.jface.text.source.IVerticalRulerColumn;
import org.eclipse.jface.text.source.IVerticalRulerExtension;
import org.eclipse.jface.text.source.IVerticalRulerInfo;
import org.eclipse.jface.text.source.SourceViewer;
import org.eclipse.jface.text.source.SourceViewerConfiguration;
import org.eclipse.jface.text.source.VerticalRuler;
import org.eclipse.ui.IActionBars;
import org.eclipse.ui.IEditorDescriptor;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.IEditorRegistry;
import org.eclipse.ui.IEditorSite;
import org.eclipse.ui.IKeyBindingService;
import org.eclipse.ui.IMemento;
import org.eclipse.ui.INavigationLocation;
import org.eclipse.ui.INavigationLocationProvider;
import org.eclipse.ui.IPartListener;
import org.eclipse.ui.IPartService;
import org.eclipse.ui.IPersistableEditor;
import org.eclipse.ui.IReusableEditor;
import org.eclipse.ui.ISaveablesLifecycleListener;
import org.eclipse.ui.ISaveablesSource;
import org.eclipse.ui.IWindowListener;
import org.eclipse.ui.IWorkbenchActionConstants;
import org.eclipse.ui.IWorkbenchCommandConstants;
import org.eclipse.ui.IWorkbenchPart;
import org.eclipse.ui.IWorkbenchPartSite;
import org.eclipse.ui.IWorkbenchWindow;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.Saveable;
import org.eclipse.ui.SaveablesLifecycleEvent;
import org.eclipse.ui.actions.ActionFactory;
import org.eclipse.ui.actions.ActionFactory.IWorkbenchAction;
import org.eclipse.ui.actions.CommandNotMappedException;
import org.eclipse.ui.actions.ContributedAction;
import org.eclipse.ui.commands.ICommandImageService;
import org.eclipse.ui.dialogs.PropertyDialogAction;
import org.eclipse.ui.dnd.IDragAndDropService;
import org.eclipse.ui.internal.texteditor.EditPosition;
import org.eclipse.ui.internal.texteditor.FocusedInformationPresenter;
import org.eclipse.ui.internal.texteditor.NLSUtility;
import org.eclipse.ui.internal.texteditor.TextEditorPlugin;
import org.eclipse.ui.internal.texteditor.rulers.StringSetSerializer;
import org.eclipse.ui.operations.LinearUndoViolationUserApprover;
import org.eclipse.ui.operations.NonLocalUndoUserApprover;
import org.eclipse.ui.operations.OperationHistoryActionHandler;
import org.eclipse.ui.operations.RedoActionHandler;
import org.eclipse.ui.operations.UndoActionHandler;
import org.eclipse.ui.part.EditorPart;
import org.eclipse.ui.part.MultiPageEditorSite;
import org.eclipse.ui.texteditor.rulers.IColumnSupport;
import org.eclipse.ui.texteditor.rulers.IContributedRulerColumn;
import org.eclipse.ui.texteditor.rulers.RulerColumnDescriptor;
import org.eclipse.ui.texteditor.rulers.RulerColumnPreferenceAdapter;
import org.eclipse.ui.texteditor.rulers.RulerColumnRegistry;
/**
* Abstract base implementation of a text editor.
* <p>
* Subclasses are responsible for configuring the editor appropriately. The standard text editor,
* <code>TextEditor</code>, is one such example.
* </p>
* <p>
* If a subclass calls {@linkplain #setEditorContextMenuId(String) setEditorContextMenuId} the
* argument is used as the id under which the editor's context menu is registered for extensions. If
* no id is set, the context menu is registered under <b>[editor_id].EditorContext</b> whereby
* [editor_id] is replaced with the editor's part id. If the editor is instructed to run in version
* 1.0 context menu registration compatibility mode, the latter form of the registration even
* happens if a context menu id has been set via {@linkplain #setEditorContextMenuId(String)
* setEditorContextMenuId}. If no id is set while in compatibility mode, the menu is registered
* under {@link #DEFAULT_EDITOR_CONTEXT_MENU_ID}.
* </p>
* <p>
* If a subclass calls {@linkplain #setRulerContextMenuId(String) setRulerContextMenuId} the
* argument is used as the id under which the ruler's context menu is registered for extensions. If
* no id is set, the context menu is registered under <b>[editor_id].RulerContext</b> whereby
* [editor_id] is replaced with the editor's part id. If the editor is instructed to run in version
* 1.0 context menu registration compatibility mode, the latter form of the registration even
* happens if a context menu id has been set via {@linkplain #setRulerContextMenuId(String)
* setRulerContextMenuId}. If no id is set while in compatibility mode, the menu is registered under
* {@link #DEFAULT_RULER_CONTEXT_MENU_ID}.
* </p>
* <p>
* As of 3.5, contributers can contribute editor and ruler context menu actions to all subclasses of
* this class by using {@link #COMMON_EDITOR_CONTEXT_MENU_ID} and
* {@link #COMMON_RULER_CONTEXT_MENU_ID}.
* </p>
*/
public abstract class AbstractTextEditor extends EditorPart implements ITextEditor, IReusableEditor, ITextEditorExtension, ITextEditorExtension2, ITextEditorExtension3, ITextEditorExtension4, ITextEditorExtension5, ITextEditorExtension6, INavigationLocationProvider, ISaveablesSource, IPersistableEditor {
/**
* Tag used in xml configuration files to specify editor action contributions.
* Current value: <code>editorContribution</code>
* @since 2.0
*/
private static final String TAG_CONTRIBUTION_TYPE= "editorContribution"; //$NON-NLS-1$
/**
* Tag used in the {@link IMemento} when saving and restoring the editor's selection offset.
*
* @see #saveState(IMemento)
* @see #restoreState(IMemento)
* @see #doRestoreState(IMemento)
* @since 3.3
*/
protected static final String TAG_SELECTION_OFFSET= "selectionOffset"; //$NON-NLS-1$
/**
* Tag used in the {@link IMemento} when saving and restoring the editor's selection length.
*
* @see #saveState(IMemento)
* @see #restoreState(IMemento)
* @see #doRestoreState(IMemento)
* @since 3.3
*/
protected static final String TAG_SELECTION_LENGTH= "selectionLength"; //$NON-NLS-1$
/**
* Tag used in the {@link IMemento} when saving and restoring the editor's top pixel value.
*
* @see #saveState(IMemento)
* @see #restoreState(IMemento)
* @see #doRestoreState(IMemento)
* @since 3.6
*/
protected static final String TAG_SELECTION_TOP_PIXEL= "selectionTopPixel"; //$NON-NLS-1$
/**
* Tag used in the {@link IMemento} when saving and restoring the editor's horizontal pixel
* value.
*
* @see #saveState(IMemento)
* @see #restoreState(IMemento)
* @see #doRestoreState(IMemento)
* @since 3.6
*/
protected static final String TAG_SELECTION_HORIZONTAL_PIXEL= "selectionHorizontalPixel"; //$NON-NLS-1$
/**
* The caret width for the wide (double) caret.
* See https://bugs.eclipse.org/bugs/show_bug.cgi?id=21715.
* Value: {@value}
* @since 3.0
*/
private static final int WIDE_CARET_WIDTH= 2;
/**
* The caret width for the narrow (single) caret.
* See https://bugs.eclipse.org/bugs/show_bug.cgi?id=21715.
* Value: {@value}
* @since 3.0
*/
private static final int SINGLE_CARET_WIDTH= 1;
/**
* The symbolic name of the block selection mode font.
*
* @since 3.5
*/
private static final String BLOCK_SELECTION_MODE_FONT= "org.eclipse.ui.workbench.texteditor.blockSelectionModeFont"; //$NON-NLS-1$
/**
* The text input listener.
*
* @see ITextInputListener
* @since 2.1
*/
private static class TextInputListener implements ITextInputListener {
/** Indicates whether the editor input changed during the process of state validation. */
public boolean inputChanged;
/* Detectors for editor input changes during the process of state validation. */
@Override
public void inputDocumentAboutToBeChanged(IDocument oldInput, IDocument newInput) {}
@Override
public void inputDocumentChanged(IDocument oldInput, IDocument newInput) { inputChanged= true; }
}
/**
* Internal element state listener.
*/
class ElementStateListener implements IElementStateListener, IElementStateListenerExtension {
/**
* Internal <code>VerifyListener</code> for performing the state validation of the
* editor input in case of the first attempted manipulation via typing on the keyboard.
* @since 2.0
*/
class Validator implements VerifyListener {
@Override
public void verifyText(VerifyEvent e) {
IDocument document= getDocumentProvider().getDocument(getEditorInput());
final boolean[] documentChanged= new boolean[1];
IDocumentListener listener= new IDocumentListener() {
@Override
public void documentAboutToBeChanged(DocumentEvent event) {
}
@Override
public void documentChanged(DocumentEvent event) {
documentChanged[0]= true;
}
};
try {
if (document != null)
document.addDocumentListener(listener);
if (! validateEditorInputState() || documentChanged[0])
e.doit= false;
} finally {
if (document != null)
document.removeDocumentListener(listener);
}
}
}
/**
* The listener's validator.
* @since 2.0
*/
private Validator fValidator;
/**
* The display used for posting runnable into the UI thread.
* @since 3.0
*/
private Display fDisplay;
@Override
public void elementStateValidationChanged(final Object element, final boolean isStateValidated) {
if (element != null && element.equals(getEditorInput())) {
Runnable r = () -> {
enableSanityChecking(true);
if (isStateValidated) {
if (fValidator != null) {
ISourceViewer viewer1 = fSourceViewer;
if (viewer1 != null) {
StyledText textWidget1 = viewer1.getTextWidget();
if (textWidget1 != null && !textWidget1.isDisposed())
textWidget1.removeVerifyListener(fValidator);
fValidator = null;
}
}
enableStateValidation(false);
} else if (!isStateValidated && fValidator == null) {
ISourceViewer viewer2 = fSourceViewer;
if (viewer2 != null) {
StyledText textWidget2 = viewer2.getTextWidget();
if (textWidget2 != null && !textWidget2.isDisposed()) {
fValidator = new Validator();
enableStateValidation(true);
textWidget2.addVerifyListener(fValidator);
}
}
}
};
execute(r, false);
}
}
@Override
public void elementDirtyStateChanged(Object element, boolean isDirty) {
if (element != null && element.equals(getEditorInput())) {
Runnable r = () -> {
enableSanityChecking(true);
firePropertyChange(PROP_DIRTY);
};
execute(r, false);
}
}
@Override
public void elementContentAboutToBeReplaced(Object element) {
if (element != null && element.equals(getEditorInput())) {
Runnable r = () -> {
enableSanityChecking(true);
rememberSelection();
resetHighlightRange();
};
execute(r, false);
}
}
@Override
public void elementContentReplaced(Object element) {
if (element != null && element.equals(getEditorInput())) {
Runnable r = () -> {
enableSanityChecking(true);
firePropertyChange(PROP_DIRTY);
restoreSelection();
handleElementContentReplaced();
};
execute(r, false);
}
}
@Override
public void elementDeleted(Object deletedElement) {
if (deletedElement != null && deletedElement.equals(getEditorInput())) {
Runnable r = () -> {
enableSanityChecking(true);
close(false);
};
execute(r, false);
}
}
@Override
public void elementMoved(final Object originalElement, final Object movedElement) {
if (originalElement != null && originalElement.equals(getEditorInput())) {
final boolean doValidationAsync= Display.getCurrent() != null;
Runnable r= new Runnable() {
@Override
public void run() {
enableSanityChecking(true);
if (fSourceViewer == null)
return;
if (!canHandleMove((IEditorInput) originalElement, (IEditorInput) movedElement)) {
close(true);
return;
}
if (movedElement == null || movedElement instanceof IEditorInput) {
rememberSelection();
final IDocumentProvider d= getDocumentProvider();
final String previousContent;
IDocumentUndoManager previousUndoManager=null;
IDocument changed= null;
boolean wasDirty= isDirty();
changed= d.getDocument(getEditorInput());
if (changed != null) {
if (wasDirty)
previousContent= changed.get();
else
previousContent= null;
previousUndoManager= DocumentUndoManagerRegistry.getDocumentUndoManager(changed);
if (previousUndoManager != null)
previousUndoManager.connect(this);
}
else
previousContent= null;
setInput((IEditorInput) movedElement);
// The undo manager needs to be replaced with one for the new document.
// Transfer the undo history and then disconnect from the old undo manager.
if (previousUndoManager != null) {
IDocument newDocument= getDocumentProvider().getDocument(movedElement);
if (newDocument != null) {
IDocumentUndoManager newUndoManager= DocumentUndoManagerRegistry.getDocumentUndoManager(newDocument);
if (newUndoManager != null)
newUndoManager.transferUndoHistory(previousUndoManager);
}
previousUndoManager.disconnect(this);
}
if (wasDirty && changed != null) {
Runnable r2 = () -> {
validateState(getEditorInput());
d.getDocument(getEditorInput()).set(previousContent);
updateStatusField(ITextEditorActionConstants.STATUS_CATEGORY_ELEMENT_STATE);
restoreSelection();
};
execute(r2, doValidationAsync);
} else
restoreSelection();
}
}
};
execute(r, false);
}
}
@Override
public void elementStateChanging(Object element) {
if (element != null && element.equals(getEditorInput()))
enableSanityChecking(false);
}
@Override
public void elementStateChangeFailed(Object element) {
if (element != null && element.equals(getEditorInput()))
enableSanityChecking(true);
}
/**
* Executes the given runnable in the UI thread.
* <p>
* See https://bugs.eclipse.org/bugs/show_bug.cgi?id=76765 for details
* about why the parameter <code>postAsync</code> has been
* introduced in the course of 3.1.
*
* @param runnable runnable to be executed
* @param postAsync <code>true</code> if the runnable must be posted asynchronous, <code>false</code> otherwise
* @since 3.0
*/
private void execute(Runnable runnable, boolean postAsync) {
if (postAsync || Display.getCurrent() == null) {
if (fDisplay == null)
fDisplay= getSite().getShell().getDisplay();
fDisplay.asyncExec(runnable);
} else
runnable.run();
}
}
/**
* Internal text listener for updating all content dependent
* actions. The updating is done asynchronously.
*/
class TextListener implements ITextListener, ITextInputListener {
/**
* Should the last edit position be updated?
*
* @since 3.0
*/
private boolean fUpdateLastEditPosition = false;
/**
* The editor's last edit position
*
* @since 3.0
*/
private Position fLocalLastEditPosition;
/** The posted updater code. */
private Runnable fRunnable = () -> {
fIsRunnablePosted = false;
if (fSourceViewer != null) {
updateContentDependentActions();
// remember the last edit position
if (isDirty() && fUpdateLastEditPosition) {
fUpdateLastEditPosition = false;
ISelection sel = getSelectionProvider().getSelection();
IEditorInput input = getEditorInput();
IDocument document = getDocumentProvider().getDocument(input);
if (fLocalLastEditPosition != null) {
if (document != null) {
document.removePosition(fLocalLastEditPosition);
}
fLocalLastEditPosition = null;
}
if (sel instanceof ITextSelection && !sel.isEmpty()) {
ITextSelection s = (ITextSelection) sel;
fLocalLastEditPosition = new Position(s.getOffset(), s.getLength());
if (document != null) {
try {
document.addPosition(fLocalLastEditPosition);
} catch (BadLocationException ex) {
fLocalLastEditPosition = null;
}
}
}
IEditorSite editorSite = getEditorSite();
if (editorSite instanceof MultiPageEditorSite)
editorSite = ((MultiPageEditorSite) editorSite).getMultiPageEditor().getEditorSite();
TextEditorPlugin.getDefault()
.setLastEditPosition(new EditPosition(input, editorSite.getId(), fLocalLastEditPosition));
}
}
};
/** Display used for posting the updater code. */
private Display fDisplay;
/**
* Has the runnable been posted?
* @since 3.0
*/
private boolean fIsRunnablePosted= false;
@Override
public void textChanged(TextEvent event) {
/*
* Also works for text events which do not base on a DocumentEvent.
* This way, if the visible document of the viewer changes, all content
* dependent actions are updated as well.
*/
if (fDisplay == null)
fDisplay= getSite().getShell().getDisplay();
if (event.getDocumentEvent() != null)
fUpdateLastEditPosition= true;
if (!fIsRunnablePosted) {
fIsRunnablePosted= true;
fDisplay.asyncExec(fRunnable);
}
}
@Override
public void inputDocumentAboutToBeChanged(IDocument oldInput, IDocument newInput) {
if (oldInput != null && fLocalLastEditPosition != null) {
oldInput.removePosition(fLocalLastEditPosition);
fLocalLastEditPosition= null;
}
}
@Override
public void inputDocumentChanged(IDocument oldInput, IDocument newInput) {
}
}
/**
* Internal property change listener for handling changes in the editor's preferences.
*/
class PropertyChangeListener implements IPropertyChangeListener {
@Override
public void propertyChange(PropertyChangeEvent event) {
handlePreferenceStoreChanged(event);
}
}
/**
* Internal property change listener for handling workbench font changes.
* @since 2.1
*/
class FontPropertyChangeListener implements IPropertyChangeListener {
@Override
public void propertyChange(PropertyChangeEvent event) {
if (fSourceViewer == null)
return;
String property= event.getProperty();
// IMPORTANT: Do not call isBlockSelectionModeEnabled() before checking the property!
if (BLOCK_SELECTION_MODE_FONT.equals(property) && isBlockSelectionModeEnabled()) {
Font blockFont= JFaceResources.getFont(BLOCK_SELECTION_MODE_FONT);
setFont(fSourceViewer, blockFont);
disposeFont();
updateCaret();
return;
}
if (getFontPropertyPreferenceKey().equals(property) && !isBlockSelectionModeEnabled()) {
initializeViewerFont(fSourceViewer);
updateCaret();
return;
}
}
}
/**
* Internal key verify listener for triggering action activation codes.
*/
class ActivationCodeTrigger implements VerifyKeyListener {
/** Indicates whether this trigger has been installed. */
private boolean fIsInstalled= false;
/**
* The key binding service to use.
* @since 2.0
*/
private IKeyBindingService fKeyBindingService;
@Override
public void verifyKey(VerifyEvent event) {
ActionActivationCode code= null;
int size= fActivationCodes.size();
for (int i= 0; i < size; i++) {
code= fActivationCodes.get(i);
if (code.matches(event)) {
IAction action= getAction(code.fActionId);
if (action != null) {
if (action instanceof IUpdate)
((IUpdate) action).update();
if (!action.isEnabled() && action instanceof IReadOnlyDependent) {
IReadOnlyDependent dependent= (IReadOnlyDependent) action;
boolean writable= dependent.isEnabled(true);
if (writable) {
event.doit= false;
return;
}
} else if (action.isEnabled()) {
event.doit= false;
action.run();
return;
}
}
}
}
}
/**
* Installs this trigger on the editor's text widget.
* @since 2.0
*/
public void install() {
if (!fIsInstalled) {
if (fSourceViewer instanceof ITextViewerExtension) {
ITextViewerExtension e= (ITextViewerExtension) fSourceViewer;
e.prependVerifyKeyListener(this);
} else {
StyledText text= fSourceViewer.getTextWidget();
text.addVerifyKeyListener(this);
}
fKeyBindingService= getEditorSite().getKeyBindingService();
fIsInstalled= true;
}
}
/**
* Uninstalls this trigger from the editor's text widget.
* @since 2.0
*/
public void uninstall() {
if (fIsInstalled) {
if (fSourceViewer instanceof ITextViewerExtension) {
ITextViewerExtension e= (ITextViewerExtension) fSourceViewer;
e.removeVerifyKeyListener(this);
} else if (fSourceViewer != null) {
StyledText text= fSourceViewer.getTextWidget();
if (text != null && !text.isDisposed())
text.removeVerifyKeyListener(fActivationCodeTrigger);
}
fIsInstalled= false;
fKeyBindingService= null;
}
}
/**
* Registers the given action for key activation.
* @param action the action to be registered
* @since 2.0
*/
public void registerActionForKeyActivation(IAction action) {
if (fIsInstalled && action.getActionDefinitionId() != null)
fKeyBindingService.registerAction(action);
}
/**
* The given action is no longer available for key activation
* @param action the action to be unregistered
* @since 2.0
*/
public void unregisterActionFromKeyActivation(IAction action) {
if (fIsInstalled && action.getActionDefinitionId() != null)
fKeyBindingService.unregisterAction(action);
}
/**
* Sets the key binding scopes for this editor.
* @param keyBindingScopes the key binding scopes
* @since 2.1
*/
public void setScopes(String[] keyBindingScopes) {
if (keyBindingScopes != null && keyBindingScopes.length > 0)
fKeyBindingService.setScopes(keyBindingScopes);
}
}
/**
* Representation of action activation codes.
*/
static class ActionActivationCode {
/** The action id. */
public String fActionId;
/** The character. */
public char fCharacter;
/** The key code. */
public int fKeyCode= -1;
/** The state mask. */
public int fStateMask= SWT.DEFAULT;
/**
* Creates a new action activation code for the given action id.
* @param actionId the action id
*/
public ActionActivationCode(String actionId) {
fActionId= actionId;
}
/**
* Returns <code>true</code> if this activation code matches the given verify event.
* @param event the event to test for matching
* @return whether this activation code matches <code>event</code>
*/
public boolean matches(VerifyEvent event) {
return (event.character == fCharacter &&
(fKeyCode == -1 || event.keyCode == fKeyCode) &&
(fStateMask == SWT.DEFAULT || event.stateMask == fStateMask));
}
}
/**
* Internal part and shell activation listener for triggering state validation.
* @since 2.0
*/
class ActivationListener implements IPartListener, IWindowListener {
/** Cache of the active workbench part. */
private IWorkbenchPart fActivePart;
/**
* The part service.
* @since 3.1
*/
private IPartService fPartService;
/**
* Creates this activation listener.
*
* @param partService the part service on which to add the part listener
* @since 3.1
*/
public ActivationListener(IPartService partService) {
fPartService= partService;
fPartService.addPartListener(this);
PlatformUI.getWorkbench().addWindowListener(this);
}
/**
* Disposes this activation listener.
*
* @since 3.1
*/
public void dispose() {
fPartService.removePartListener(this);
PlatformUI.getWorkbench().removeWindowListener(this);
fPartService= null;
}
@Override
public void partActivated(IWorkbenchPart part) {
fActivePart= part;
handleActivation();
}
@Override
public void partBroughtToTop(IWorkbenchPart part) {
}
@Override
public void partClosed(IWorkbenchPart part) {
}
@Override
public void partDeactivated(IWorkbenchPart part) {
fActivePart= null;
}
@Override
public void partOpened(IWorkbenchPart part) {
// Restore the saved state if any
if ((part == AbstractTextEditor.this || part.getAdapter(AbstractTextEditor.class) == AbstractTextEditor.this) && fMementoToRestore != null && containsSavedState(fMementoToRestore)) {
doRestoreState(fMementoToRestore);
fMementoToRestore= null;
}
}
/**
* Handles the activation triggering a element state check in the editor.
*/
private void handleActivation() {
if (!fHandleActivation)
return;
if (fActivePart == AbstractTextEditor.this || fActivePart != null && fActivePart.getAdapter(AbstractTextEditor.class) == AbstractTextEditor.this) {
fHandleActivation= false;
try {
safelySanityCheckState(getEditorInput());
} finally {
fHandleActivation= true;
fHasBeenActivated= true;
}
}
}
@Override
public void windowActivated(IWorkbenchWindow window) {
if (fHandleActivation && window == getEditorSite().getWorkbenchWindow()) {
/*
* Workaround for problem described in
* http://dev.eclipse.org/bugs/show_bug.cgi?id=11731
* Will be removed when SWT has solved the problem.
*/
window.getShell().getDisplay().asyncExec(() -> handleActivation());
}
}
@Override
public void windowDeactivated(IWorkbenchWindow window) {
}
@Override
public void windowClosed(IWorkbenchWindow window) {
}
@Override
public void windowOpened(IWorkbenchWindow window) {
}
}
/**
* Internal interface for a cursor listener. I.e. aggregation
* of mouse and key listener.
* @since 2.0
*/
interface ICursorListener extends MouseListener, KeyListener {
}
/**
* Maps an action definition id to an StyledText action.
* @since 2.0
*/
protected static final class IdMapEntry {
/** The action id. */
private String fActionId;
/** The StyledText action. */
private int fAction;
/**
* Creates a new mapping.
* @param actionId the action id
* @param action the StyledText action
*/
public IdMapEntry(String actionId, int action) {
fActionId= actionId;
fAction= action;
}
/**
* Returns the action id.
* @return the action id
*/
public String getActionId() {
return fActionId;
}
/**
* Returns the action.
* @return the action
*/
public int getAction() {
return fAction;
}
}
/**
* Internal action to scroll the editor's viewer by a specified number of lines.
* @since 2.0
*/
class ScrollLinesAction extends Action {
/** Number of lines to scroll. */
private int fScrollIncrement;
/**
* Creates a new scroll action that scroll the given number of lines. If the
* increment is &lt; 0, it's scrolling up, if &gt; 0 it's scrolling down.
* @param scrollIncrement the number of lines to scroll
*/
public ScrollLinesAction(int scrollIncrement) {
fScrollIncrement= scrollIncrement;
}
@Override
public void run() {
if (fSourceViewer instanceof ITextViewerExtension5) {
ITextViewerExtension5 extension= (ITextViewerExtension5) fSourceViewer;
StyledText textWidget= fSourceViewer.getTextWidget();
int topIndex= textWidget.getTopIndex();
int newTopIndex= Math.max(0, topIndex + fScrollIncrement);
fSourceViewer.setTopIndex(extension.widgetLine2ModelLine(newTopIndex));
} else {
int topIndex= fSourceViewer.getTopIndex();
int newTopIndex= Math.max(0, topIndex + fScrollIncrement);
fSourceViewer.setTopIndex(newTopIndex);
}
}
}
/**
* Action to toggle the insert mode. The action is checked if smart mode is
* turned on.
*
* @since 2.1
*/
class ToggleInsertModeAction extends ResourceAction {
public ToggleInsertModeAction(ResourceBundle bundle, String prefix) {
super(bundle, prefix, IAction.AS_CHECK_BOX);
}
@Override
public void run() {
switchToNextInsertMode();
}
@Override
public boolean isChecked() {
return fInsertMode == SMART_INSERT;
}
}
/**
* Action to toggle the overwrite mode.
*
* @since 3.0
*/
class ToggleOverwriteModeAction extends ResourceAction {
public ToggleOverwriteModeAction(ResourceBundle bundle, String prefix) {
super(bundle, prefix);
}
@Override
public void run() {
toggleOverwriteMode();
}
}
/**
* This action implements smart end.
* Instead of going to the end of a line it does the following:
* - if smart home/end is enabled and the caret is before the line's last non-whitespace and then the caret is moved directly after it
* - if the caret is after last non-whitespace the caret is moved at the end of the line
* - if the caret is at the end of the line the caret is moved directly after the line's last non-whitespace character
* @since 2.1 (in 3.3 the access modifier changed from package visibility to protected)
*/
protected class LineEndAction extends TextNavigationAction {
/** boolean flag which tells if the text up to the line end should be selected. */
private boolean fDoSelect;
/**
* Create a new line end action.
*
* @param textWidget the styled text widget
* @param doSelect a boolean flag which tells if the text up to the line end should be selected
*/
public LineEndAction(StyledText textWidget, boolean doSelect) {
super(textWidget, ST.LINE_END);
fDoSelect= doSelect;
}
/**
* Computes the offset of the line end position.
*
* @param document the document where to compute the line end position
* @param line the line to determine the end position of
* @param length the length of the line
* @param offset the caret position in the document
* @return the offset of the line end
* @since 3.4 protected, was added in 3.3 as private method
*/
protected int getLineEndPosition(final IDocument document, final String line, final int length, final int offset) {
int index= length - 1;
while (index > -1 && Character.isWhitespace(line.charAt(index)))
index--;
index++;
LinkedModeModel model= LinkedModeModel.getModel(document, offset);
if (model != null) {
LinkedPosition linkedPosition= model.findPosition(new LinkedPosition(document, offset, 0));
if (linkedPosition != null) {
int linkedPositionEnd= linkedPosition.getOffset() + linkedPosition.getLength();
int lineOffset;
try {
lineOffset= document.getLineInformationOfOffset(offset).getOffset();
if (offset != linkedPositionEnd && linkedPositionEnd - lineOffset < index)
index= linkedPositionEnd - lineOffset;
} catch (BadLocationException e) {
//should not happen
}
}
}
return index;
}
@Override
public void run() {
boolean isSmartHomeEndEnabled= false;
IPreferenceStore store= getPreferenceStore();
if (store != null)
isSmartHomeEndEnabled= store.getBoolean(AbstractTextEditor.PREFERENCE_NAVIGATION_SMART_HOME_END);
StyledText st= fSourceViewer.getTextWidget();
if (st == null || st.isDisposed())
return;
int caretOffset= st.getCaretOffset();
int lineNumber= st.getLineAtOffset(caretOffset);
int lineOffset= st.getOffsetAtLine(lineNumber);
int lineLength;
int caretOffsetInDocument;
final IDocument document= fSourceViewer.getDocument();
try {
caretOffsetInDocument= widgetOffset2ModelOffset(fSourceViewer, caretOffset);
lineLength= document.getLineInformationOfOffset(caretOffsetInDocument).getLength();
} catch (BadLocationException ex) {
return;
}
int lineEndOffset= lineOffset + lineLength;
int delta= lineEndOffset - st.getCharCount();
if (delta > 0) {
lineEndOffset -= delta;
lineLength -= delta;
}
String line= ""; //$NON-NLS-1$
if (lineLength > 0)
line= st.getText(lineOffset, lineEndOffset - 1);
// Remember current selection
Point oldSelection= st.getSelection();
// The new caret position
int newCaretOffset= -1;
if (isSmartHomeEndEnabled) {
// Compute the line end offset
int i= getLineEndPosition(document, line, lineLength, caretOffsetInDocument);
if (caretOffset - lineOffset == i)
// to end of line
newCaretOffset= lineEndOffset;
else
// to end of text
newCaretOffset= lineOffset + i;
} else {
if (caretOffset < lineEndOffset)
// to end of line
newCaretOffset= lineEndOffset;
}
if (newCaretOffset == -1)
newCaretOffset= caretOffset;
else
st.setCaretOffset(newCaretOffset);
st.setCaretOffset(newCaretOffset);
if (fDoSelect) {
if (caretOffset < oldSelection.y)
st.setSelection(oldSelection.y, newCaretOffset);
else
st.setSelection(oldSelection.x, newCaretOffset);
} else
st.setSelection(newCaretOffset);
fireSelectionChanged(oldSelection);
}
}
/**
* This action implements smart home.
* Instead of going to the start of a line it does the following:
* - if smart home/end is enabled and the caret is after the line's first non-whitespace then the caret is moved directly before it
* - if the caret is before the line's first non-whitespace the caret is moved to the beginning of the line
* - if the caret is at the beginning of the line the caret is moved directly before the line's first non-whitespace character
* @since 2.1
*/
protected class LineStartAction extends TextNavigationAction {
/** boolean flag which tells if the text up to the beginning of the line should be selected. */
private final boolean fDoSelect;
/**
* Creates a new line start action.
*
* @param textWidget the styled text widget
* @param doSelect a boolean flag which tells if the text up to the beginning of the line should be selected
*/
public LineStartAction(final StyledText textWidget, final boolean doSelect) {
super(textWidget, ST.LINE_START);
fDoSelect= doSelect;
}
/**
* Computes the offset of the line start position.
*
* @param document the document where to compute the line start position
* @param line the line to determine the start position of
* @param length the length of the line
* @param offset the caret position in the document
* @return the offset of the line start
* @since 3.0
*/
protected int getLineStartPosition(final IDocument document, final String line, final int length, final int offset) {
int index= 0;
while (index < length && Character.isWhitespace(line.charAt(index)))
index++;
LinkedModeModel model= LinkedModeModel.getModel(document, offset);
if (model != null) {
LinkedPosition linkedPosition= model.findPosition(new LinkedPosition(document, offset, 0));
if (linkedPosition != null) {
int linkedPositionOffset= linkedPosition.getOffset();
int lineOffset;
try {
lineOffset= document.getLineInformationOfOffset(offset).getOffset();
if (offset != linkedPositionOffset && index < linkedPositionOffset - lineOffset)
index= linkedPositionOffset - lineOffset;
} catch (BadLocationException e) {
//should not happen
}
}
}
return index;
}
@Override
public void run() {
boolean isSmartHomeEndEnabled= false;
IPreferenceStore store= getPreferenceStore();
if (store != null)
isSmartHomeEndEnabled= store.getBoolean(AbstractTextEditor.PREFERENCE_NAVIGATION_SMART_HOME_END);
StyledText st= fSourceViewer.getTextWidget();
if (st == null || st.isDisposed())
return;
int caretOffset= st.getCaretOffset();
int lineNumber= st.getLineAtOffset(caretOffset);
int lineOffset= st.getOffsetAtLine(lineNumber);
int lineLength;
int caretOffsetInDocument;
final IDocument document= fSourceViewer.getDocument();
try {
caretOffsetInDocument= widgetOffset2ModelOffset(fSourceViewer, caretOffset);
lineLength= document.getLineInformationOfOffset(caretOffsetInDocument).getLength();
} catch (BadLocationException ex) {
return;
}
String line= ""; //$NON-NLS-1$
if (lineLength > 0) {
int end= lineOffset + lineLength - 1;
end= Math.min(end, st.getCharCount() -1);
line= st.getText(lineOffset, end);
}
// Remember current selection
Point oldSelection= st.getSelection();
// The new caret position
int newCaretOffset= -1;
if (isSmartHomeEndEnabled) {
// Compute the line start offset
int index= getLineStartPosition(document, line, lineLength, caretOffsetInDocument);
if (caretOffset - lineOffset == index)
// to beginning of line
newCaretOffset= lineOffset;
else
// to beginning of text
newCaretOffset= lineOffset + index;
} else {
if (caretOffset > lineOffset)
// to beginning of line
newCaretOffset= lineOffset;
}
if (newCaretOffset == -1)
newCaretOffset= caretOffset;
else
st.setCaretOffset(newCaretOffset);
if (fDoSelect) {
if (caretOffset < oldSelection.y)
st.setSelection(oldSelection.y, newCaretOffset);
else
st.setSelection(oldSelection.x, newCaretOffset);
} else
st.setSelection(newCaretOffset);
fireSelectionChanged(oldSelection);
}
}
/**
* Internal action to show the editor's ruler context menu (accessibility).
* @since 2.0
*/
class ShowRulerContextMenuAction extends Action {
@Override
public void run() {
if (fSourceViewer == null)
return;
StyledText text= fSourceViewer.getTextWidget();
if (text == null || text.isDisposed())
return;
Point location= text.getLocationAtOffset(text.getCaretOffset());
location.x= 0;
if (fVerticalRuler instanceof IVerticalRulerExtension)
((IVerticalRulerExtension) fVerticalRuler).setLocationOfLastMouseButtonActivity(location.x, location.y);
location= text.toDisplay(location);
fRulerContextMenu.setLocation(location.x, location.y);
fRulerContextMenu.setVisible(true);
}
}
/**
* Editor specific selection provider which wraps the source viewer's selection provider.
*
* @since 3.4 protected, was added in 2.1 as private class
*/
protected class SelectionProvider implements IPostSelectionProvider, ISelectionValidator {
@Override
public void addSelectionChangedListener(ISelectionChangedListener listener) {
if (fSourceViewer != null)
fSourceViewer.getSelectionProvider().addSelectionChangedListener(listener);
}
@Override
public ISelection getSelection() {
return doGetSelection();
}
@Override
public void removeSelectionChangedListener(ISelectionChangedListener listener) {
if (fSourceViewer != null)
fSourceViewer.getSelectionProvider().removeSelectionChangedListener(listener);
}
@Override
public void setSelection(ISelection selection) {
doSetSelection(selection);
}
@Override
public void addPostSelectionChangedListener(ISelectionChangedListener listener) {
if (fSourceViewer != null) {
if (fSourceViewer.getSelectionProvider() instanceof IPostSelectionProvider) {
IPostSelectionProvider provider= (IPostSelectionProvider) fSourceViewer.getSelectionProvider();
provider.addPostSelectionChangedListener(listener);
}
}
}
@Override
public void removePostSelectionChangedListener(ISelectionChangedListener listener) {
if (fSourceViewer != null) {
if (fSourceViewer.getSelectionProvider() instanceof IPostSelectionProvider) {
IPostSelectionProvider provider= (IPostSelectionProvider) fSourceViewer.getSelectionProvider();
provider.removePostSelectionChangedListener(listener);
}
}
}
@Override
public boolean isValid(ISelection postSelection) {
return fSelectionListener != null && fSelectionListener.isValid(postSelection);
}
}
/**
* Internal implementation class for a change listener.
* @since 3.0
*/
protected abstract class AbstractSelectionChangedListener implements ISelectionChangedListener {
/**
* Installs this selection changed listener with the given selection provider. If the
* selection provider is a post selection provider, post selection changed events are the
* preferred choice, otherwise normal selection changed events are requested.
*
* @param selectionProvider the selection provider
*/
public void install(ISelectionProvider selectionProvider) {
if (selectionProvider == null)
return;
if (selectionProvider instanceof IPostSelectionProvider) {
IPostSelectionProvider provider= (IPostSelectionProvider) selectionProvider;
provider.addPostSelectionChangedListener(this);
} else {
selectionProvider.addSelectionChangedListener(this);
}
}
/**
* Removes this selection changed listener from the given selection provider.
*
* @param selectionProvider the selection provider
*/
public void uninstall(ISelectionProvider selectionProvider) {
if (selectionProvider == null)
return;
if (selectionProvider instanceof IPostSelectionProvider) {
IPostSelectionProvider provider= (IPostSelectionProvider) selectionProvider;
provider.removePostSelectionChangedListener(this);
} else {
selectionProvider.removeSelectionChangedListener(this);
}
}
}
/**
* This selection listener allows the SelectionProvider to implement {@link ISelectionValidator}.
*
* @since 3.0
*/
private class SelectionListener extends AbstractSelectionChangedListener implements IDocumentListener {
private IDocument fDocument;
private final Object INVALID_SELECTION= new Object();
private Object fPostSelection= INVALID_SELECTION;
@Override
public synchronized void selectionChanged(SelectionChangedEvent event) {
fPostSelection= event.getSelection();
}
@Override
public synchronized void documentAboutToBeChanged(DocumentEvent event) {
fPostSelection= INVALID_SELECTION;
}
@Override
public void documentChanged(DocumentEvent event) {
}
public synchronized boolean isValid(ISelection selection) {
return fPostSelection != INVALID_SELECTION && fPostSelection == selection;
}
public void setDocument(IDocument document) {
if (fDocument != null)
fDocument.removeDocumentListener(this);
fDocument= document;
if (fDocument != null)
fDocument.addDocumentListener(this);
}
@Override
public void install(ISelectionProvider selectionProvider) {
super.install(selectionProvider);
if (selectionProvider != null)
selectionProvider.addSelectionChangedListener(this);
}
@Override
public void uninstall(ISelectionProvider selectionProvider) {
if (selectionProvider != null)
selectionProvider.removeSelectionChangedListener(this);
if (fDocument != null) {
fDocument.removeDocumentListener(this);
fDocument= null;
}
super.uninstall(selectionProvider);
}
}
/**
* Implements the ruler column support of for the given editor.
* <p>
* This is currently only used to support vertical ruler columns.
* </p>
*
* @since 3.3
*/
protected static class ColumnSupport implements IColumnSupport {
private final AbstractTextEditor fEditor;
private final RulerColumnRegistry fRegistry;
private final List<IContributedRulerColumn> fColumns;
/**
* Creates a new column support for the given editor. Only the editor itself should normally
* create such an instance.
*
* @param editor the editor
* @param registry the contribution registry to refer to
*/
public ColumnSupport(AbstractTextEditor editor, RulerColumnRegistry registry) {
Assert.isLegal(editor != null);
Assert.isLegal(registry != null);
fEditor= editor;
fRegistry= registry;
fColumns= new ArrayList<>();
}
@Override
public final void setColumnVisible(RulerColumnDescriptor descriptor, boolean visible) {
Assert.isLegal(descriptor != null);
final CompositeRuler ruler= getRuler();
if (ruler == null)
return;
if (!isColumnSupported(descriptor))
visible= false;
if (isColumnVisible(descriptor)) {
if (!visible)
removeColumn(ruler, descriptor);
} else {
if (visible)
addColumn(ruler, descriptor);
}
}
private void addColumn(final CompositeRuler ruler, final RulerColumnDescriptor descriptor) {
final int idx= computeIndex(ruler, descriptor);
SafeRunnable runnable= new SafeRunnable() {
@Override
public void run() throws Exception {
IContributedRulerColumn column= descriptor.createColumn(fEditor);
fColumns.add(column);
initializeColumn(column);
ruler.addDecorator(idx, column);
}
};
SafeRunner.run(runnable);
}
/**
* Hook to let subclasses initialize a newly created column.
* <p>
* Subclasses may extend this method.</p>
*
* @param column the created column
*/
protected void initializeColumn(IContributedRulerColumn column) {
}
private void removeColumn(final CompositeRuler ruler, final RulerColumnDescriptor descriptor) {
removeColumn(ruler, getVisibleColumn(ruler, descriptor));
}
private void removeColumn(final CompositeRuler ruler, final IContributedRulerColumn rulerColumn) {
if (rulerColumn != null) {
SafeRunnable runnable= new SafeRunnable() {
@Override
public void run() throws Exception {
if (ruler != null)
ruler.removeDecorator(rulerColumn);
rulerColumn.columnRemoved();
}
};
SafeRunner.run(runnable);
}
}
/**
* Returns the currently visible column matching <code>id</code>, <code>null</code> if
* none.
*
* @param ruler the composite ruler to scan
* @param descriptor the descriptor of the column of interest
* @return the matching column or <code>null</code>
*/
private IContributedRulerColumn getVisibleColumn(CompositeRuler ruler, RulerColumnDescriptor descriptor) {
for (Iterator<IVerticalRulerColumn> it= ruler.getDecoratorIterator(); it.hasNext();) {
IVerticalRulerColumn column= it.next();
if (column instanceof IContributedRulerColumn) {
IContributedRulerColumn rulerColumn= (IContributedRulerColumn)column;
RulerColumnDescriptor rcd= rulerColumn.getDescriptor();
if (descriptor.equals(rcd))
return rulerColumn;
}
}
return null;
}
/**
* Computes the insertion index for a column contribution into the currently visible columns.
*
* @param ruler the composite ruler into which to insert the column
* @param descriptor the descriptor to compute the index for
* @return the insertion index for a new column
*/
private int computeIndex(CompositeRuler ruler, RulerColumnDescriptor descriptor) {
int index= 0;
List<RulerColumnDescriptor> all= fRegistry.getColumnDescriptors();
int newPos= all.indexOf(descriptor);
for (Iterator<IVerticalRulerColumn> it= ruler.getDecoratorIterator(); it.hasNext();) {
IVerticalRulerColumn column= it.next();
if (column instanceof IContributedRulerColumn) {
RulerColumnDescriptor rcd= ((IContributedRulerColumn)column).getDescriptor();
if (rcd != null && all.indexOf(rcd) > newPos)
break;
} else if ("org.eclipse.jface.text.source.projection.ProjectionRulerColumn".equals(column.getClass().getName())) { //$NON-NLS-1$
// projection column is always the rightmost column
break;
}
index++;
}
return index;
}
@Override
public final boolean isColumnVisible(RulerColumnDescriptor descriptor) {
Assert.isLegal(descriptor != null);
CompositeRuler ruler= getRuler();
return ruler != null && getVisibleColumn(ruler, descriptor) != null;
}
@Override
public final boolean isColumnSupported(RulerColumnDescriptor descriptor) {
Assert.isLegal(descriptor != null);
if (getRuler() == null)
return false;
return descriptor.matchesEditor(fEditor);
}
/**
* Returns the editor's vertical ruler, if it is a {@link CompositeRuler}, <code>null</code>
* otherwise.
*
* @return the editor's {@link CompositeRuler} or <code>null</code>
*/
private CompositeRuler getRuler() {
Object ruler= fEditor.getAdapter(IVerticalRulerInfo.class);
if (ruler instanceof CompositeRuler)
return (CompositeRuler) ruler;
return null;
}
/**
* {@inheritDoc}
* <p>
* Subclasses may extend this method.</p>
*
*/
@Override
public void dispose() {
for (Iterator<IContributedRulerColumn> iter= new ArrayList<>(fColumns).iterator(); iter.hasNext();)
removeColumn(getRuler(), iter.next());
fColumns.clear();
}
}
/**
* This action behaves in two different ways: If there is no current text
* hover, the javadoc is displayed using information presenter. If there is
* a current text hover, it is converted into a information presenter in
* order to make it sticky.
*
* @since 3.3
*/
private final class InformationDispatchAction extends TextEditorAction {
/** The wrapped text operation action. */
private final TextOperationAction fTextOperationAction;
/**
* Creates a dispatch action.
*
* @param resourceBundle the resource bundle
* @param prefix the prefix
* @param textOperationAction the text operation action
*/
public InformationDispatchAction(ResourceBundle resourceBundle, String prefix, final TextOperationAction textOperationAction) {
super(resourceBundle, prefix, AbstractTextEditor.this);
if (textOperationAction == null)
throw new IllegalArgumentException();
fTextOperationAction= textOperationAction;
}
@Override
public void run() {
ISourceViewer sourceViewer= getSourceViewer();
if (sourceViewer == null) {
if (fTextOperationAction.isEnabled())
fTextOperationAction.run();
return;
}
if (sourceViewer instanceof ITextViewerExtension4) {
ITextViewerExtension4 extension4= (ITextViewerExtension4) sourceViewer;
if (extension4.moveFocusToWidgetToken())
return;
}
if (sourceViewer instanceof ITextViewerExtension2) {
// does a text hover exist?
ITextHover textHover= ((ITextViewerExtension2) sourceViewer).getCurrentTextHover();
if (textHover != null && makeTextHoverFocusable(sourceViewer, textHover))
return;
}
if (sourceViewer instanceof ISourceViewerExtension3) {
// does an annotation hover exist?
IAnnotationHover annotationHover= ((ISourceViewerExtension3) sourceViewer).getCurrentAnnotationHover();
if (annotationHover != null && makeAnnotationHoverFocusable(annotationHover))
return;
}
// otherwise, just run the action
if (fTextOperationAction.isEnabled())
fTextOperationAction.run();
}
/**
* Tries to make a text hover focusable (or "sticky").
*
* @param sourceViewer the source viewer to display the hover over
* @param textHover the hover to make focusable
* @return <code>true</code> if successful, <code>false</code> otherwise
*/
private boolean makeTextHoverFocusable(ISourceViewer sourceViewer, ITextHover textHover) {
Point hoverEventLocation= ((ITextViewerExtension2) sourceViewer).getHoverEventLocation();
int offset= computeOffsetAtLocation(sourceViewer, hoverEventLocation.x, hoverEventLocation.y);
if (offset == -1)
return false;
try {
IRegion hoverRegion= textHover.getHoverRegion(sourceViewer, offset);
if (hoverRegion == null)
return false;
String hoverInfo= textHover.getHoverInfo(sourceViewer, hoverRegion);
IInformationControlCreator controlCreator= null;
if (textHover instanceof IInformationProviderExtension2) // this is conceptually wrong, but left here for backwards compatibility
controlCreator= ((IInformationProviderExtension2)textHover).getInformationPresenterControlCreator();
IInformationProvider informationProvider= new FocusedInformationPresenter.InformationProvider(hoverRegion, hoverInfo, controlCreator);
FocusedInformationPresenter informationPresenter= getInformationPresenter();
informationPresenter.setOffset(offset);
informationPresenter.setAnchor(AbstractInformationControlManager.ANCHOR_BOTTOM);
informationPresenter.setMargins(6, 6); // default values from AbstractInformationControlManager
String contentType= TextUtilities.getContentType(sourceViewer.getDocument(), getSourceViewerConfiguration().getConfiguredDocumentPartitioning(getSourceViewer()), offset, true);
informationPresenter.setInformationProvider(informationProvider, contentType);
informationPresenter.showInformation();
return true;
} catch (BadLocationException e) {
return false;
}
}
/**
* Tries to make an annotation hover focusable (or "sticky").
*
* @param annotationHover the hover to make focusable
* @return <code>true</code> if successful, <code>false</code> otherwise
*/
private boolean makeAnnotationHoverFocusable(IAnnotationHover annotationHover) {
IVerticalRulerInfo info= getVerticalRuler();
int line= info.getLineOfLastMouseButtonActivity();
if (line == -1)
return false;
return getInformationPresenter().openFocusedAnnotationHover(annotationHover, line);
}
/**
* Returns the information presenter (creates it if necessary).
*
* @return the information presenter
* @since 3.6
*/
private FocusedInformationPresenter getInformationPresenter() {
if (fInformationPresenter == null) {
fInformationPresenter= new FocusedInformationPresenter(getSourceViewer(), getSourceViewerConfiguration());
}
return fInformationPresenter;
}
// modified version from TextViewer
private int computeOffsetAtLocation(ITextViewer textViewer, int x, int y) {
StyledText styledText= textViewer.getTextWidget();
IDocument document= textViewer.getDocument();
int widgetOffset = styledText.getOffsetAtPoint(new Point(x, y));
if (document == null || widgetOffset == -1) {
return -1;
}
try {
Point p= styledText.getLocationAtOffset(widgetOffset);
if (p.x > x) {
widgetOffset--;
}
if (textViewer instanceof ITextViewerExtension5) {
ITextViewerExtension5 extension= (ITextViewerExtension5) textViewer;
return extension.widgetOffset2ModelOffset(widgetOffset);
}
IRegion visibleRegion= textViewer.getVisibleRegion();
return widgetOffset + visibleRegion.getOffset();
} catch (IllegalArgumentException e) {
return -1;
}
}
}
/**
* Key used to look up font preference.
* Value: <code>"org.eclipse.jface.textfont"</code>
*
* @deprecated As of 2.1, replaced by {@link JFaceResources#TEXT_FONT}
*/
@Deprecated
public static final String PREFERENCE_FONT= JFaceResources.TEXT_FONT;
/**
* Key used to look up foreground color preference.
* Value: <code>AbstractTextEditor.Color.Foreground</code>
* @since 2.0
*/
public static final String PREFERENCE_COLOR_FOREGROUND= "AbstractTextEditor.Color.Foreground"; //$NON-NLS-1$
/**
* Key used to look up background color preference.
* Value: <code>AbstractTextEditor.Color.Background</code>
* @since 2.0
*/
public static final String PREFERENCE_COLOR_BACKGROUND= "AbstractTextEditor.Color.Background"; //$NON-NLS-1$
/**
* Key used to look up foreground color system default preference.
* Value: <code>AbstractTextEditor.Color.Foreground.SystemDefault</code>
* @since 2.0
*/
public static final String PREFERENCE_COLOR_FOREGROUND_SYSTEM_DEFAULT= "AbstractTextEditor.Color.Foreground.SystemDefault"; //$NON-NLS-1$
/**
* Key used to look up background color system default preference.
* Value: <code>AbstractTextEditor.Color.Background.SystemDefault</code>
* @since 2.0
*/
public static final String PREFERENCE_COLOR_BACKGROUND_SYSTEM_DEFAULT= "AbstractTextEditor.Color.Background.SystemDefault"; //$NON-NLS-1$
/**
* Key used to look up selection foreground color preference.
* Value: <code>AbstractTextEditor.Color.SelectionForeground</code>
* @since 3.0
*/
public static final String PREFERENCE_COLOR_SELECTION_FOREGROUND= "AbstractTextEditor.Color.SelectionForeground"; //$NON-NLS-1$
/**
* Key used to look up selection background color preference.
* Value: <code>AbstractTextEditor.Color.SelectionBackground</code>
* @since 3.0
*/
public static final String PREFERENCE_COLOR_SELECTION_BACKGROUND= "AbstractTextEditor.Color.SelectionBackground"; //$NON-NLS-1$
/**
* Key used to look up selection foreground color system default preference.
* Value: <code>AbstractTextEditor.Color.SelectionForeground.SystemDefault</code>
* @since 3.0
*/
public static final String PREFERENCE_COLOR_SELECTION_FOREGROUND_SYSTEM_DEFAULT= "AbstractTextEditor.Color.SelectionForeground.SystemDefault"; //$NON-NLS-1$
/**
* Key used to look up selection background color system default preference.
* Value: <code>AbstractTextEditor.Color.SelectionBackground.SystemDefault</code>
* @since 3.0
*/
public static final String PREFERENCE_COLOR_SELECTION_BACKGROUND_SYSTEM_DEFAULT= "AbstractTextEditor.Color.SelectionBackground.SystemDefault"; //$NON-NLS-1$
/**
* Key used to look up find scope background color preference.
* Value: <code>AbstractTextEditor.Color.FindScope</code>
* @since 2.0
*/
public static final String PREFERENCE_COLOR_FIND_SCOPE= "AbstractTextEditor.Color.FindScope"; //$NON-NLS-1$
/**
* Key used to look up smart home/end preference.
* Value: <code>AbstractTextEditor.Navigation.SmartHomeEnd</code>
* @since 2.1
*/
public static final String PREFERENCE_NAVIGATION_SMART_HOME_END= "AbstractTextEditor.Navigation.SmartHomeEnd"; //$NON-NLS-1$
/**
* Key used to look up the custom caret preference.
* Value: {@value}
* @since 3.0
*/
public static final String PREFERENCE_USE_CUSTOM_CARETS= "AbstractTextEditor.Accessibility.UseCustomCarets"; //$NON-NLS-1$
/**
* Key used to look up the caret width preference.
* Value: {@value}
* @since 3.0
*/
public static final String PREFERENCE_WIDE_CARET= "AbstractTextEditor.Accessibility.WideCaret"; //$NON-NLS-1$
/**
* A named preference that controls if hyperlinks are turned on or off.
* <p>
* Value is of type <code>Boolean</code>.
* </p>
*
* @since 3.1
*/
public static final String PREFERENCE_HYPERLINKS_ENABLED= "hyperlinksEnabled"; //$NON-NLS-1$
/**
* A named preference that controls the key modifier for hyperlinks.
* <p>
* Value is of type <code>String</code>.
* </p>
*
* @since 3.1
*/
public static final String PREFERENCE_HYPERLINK_KEY_MODIFIER= "hyperlinkKeyModifier"; //$NON-NLS-1$
/**
* A named preference that controls the key modifier mask for hyperlinks.
* The value is only used if the value of <code>PREFERENCE_HYPERLINK_KEY_MODIFIER</code>
* cannot be resolved to valid SWT modifier bits.
* <p>
* Value is of type <code>String</code>.
* </p>
*
* @see #PREFERENCE_HYPERLINK_KEY_MODIFIER
* @since 3.1
*/
public static final String PREFERENCE_HYPERLINK_KEY_MODIFIER_MASK= "hyperlinkKeyModifierMask"; //$NON-NLS-1$
/**
* A named preference that controls the visible ruler column contributions.
* <p>
* Value is of type <code>String</code> and should be read using a {@link RulerColumnPreferenceAdapter}.
* </p>
*
* @since 3.3
*/
public static final String PREFERENCE_RULER_CONTRIBUTIONS= "rulerContributions"; //$NON-NLS-1$
/**
* A named preference that controls the display of whitespace characters.
* <p>
* Value is of type <code>Boolean</code>.
* </p>
*
* <p>
* The following preferences can be used for fine-grained configuration when
* enabled.
* </p>
* <ul>
* <li>{@link #PREFERENCE_SHOW_LEADING_SPACES}</li>
* <li>{@link #PREFERENCE_SHOW_ENCLOSED_SPACES}</li>
* <li>{@link #PREFERENCE_SHOW_TRAILING_SPACES}</li>
* <li>{@link #PREFERENCE_SHOW_LEADING_IDEOGRAPHIC_SPACES}</li>
* <li>{@link #PREFERENCE_SHOW_ENCLOSED_IDEOGRAPHIC_SPACES}</li>
* <li>{@link #PREFERENCE_SHOW_TRAILING_IDEOGRAPHIC_SPACES}</li>
* <li>{@link #PREFERENCE_SHOW_LEADING_TABS}</li>
* <li>{@link #PREFERENCE_SHOW_ENCLOSED_TABS}</li>
* <li>{@link #PREFERENCE_SHOW_TRAILING_TABS}</li>
* <li>{@link #PREFERENCE_SHOW_CARRIAGE_RETURN}</li>
* <li>{@link #PREFERENCE_SHOW_LINE_FEED}</li>
* <li>{@link #PREFERENCE_WHITESPACE_CHARACTER_ALPHA_VALUE}</li>
* </ul>
*
* @since 3.3
*/
public static final String PREFERENCE_SHOW_WHITESPACE_CHARACTERS= "showWhitespaceCharacters"; //$NON-NLS-1$
/**
* A named preference that controls the display of leading Space characters. The value is used
* only if the value of {@link #PREFERENCE_SHOW_WHITESPACE_CHARACTERS} is <code>true</code>.
* <p>
* Value is of type <code>Boolean</code>.
* </p>
*
* @since 3.7
*/
public static final String PREFERENCE_SHOW_LEADING_SPACES= "showLeadingSpaces"; //$NON-NLS-1$
/**
* A named preference that controls the display of enclosed Space characters. The value is used
* only if the value of {@link #PREFERENCE_SHOW_WHITESPACE_CHARACTERS} is <code>true</code>.
* <p>
* Value is of type <code>Boolean</code>.
* </p>
*
* @since 3.7
*/
public static final String PREFERENCE_SHOW_ENCLOSED_SPACES= "showEnclosedSpaces"; //$NON-NLS-1$
/**
* A named preference that controls the display of trailing Space characters. The value is used
* only if the value of {@link #PREFERENCE_SHOW_WHITESPACE_CHARACTERS} is <code>true</code>.
* <p>
* Value is of type <code>Boolean</code>.
* </p>
*
* @since 3.7
*/
public static final String PREFERENCE_SHOW_TRAILING_SPACES= "showTrailingSpaces"; //$NON-NLS-1$
/**
* A named preference that controls the display of leading Ideographic Space characters. The
* value is used only if the value of {@link #PREFERENCE_SHOW_WHITESPACE_CHARACTERS} is
* <code>true</code>.
* <p>
* Value is of type <code>Boolean</code>.
* </p>
*
* @since 3.7
*/
public static final String PREFERENCE_SHOW_LEADING_IDEOGRAPHIC_SPACES= "showLeadingIdeographicSpaces"; //$NON-NLS-1$
/**
* A named preference that controls the display of enclosed Ideographic Space characters. The
* value is used only if the value of {@link #PREFERENCE_SHOW_WHITESPACE_CHARACTERS} is
* <code>true</code>.
* <p>
* Value is of type <code>Boolean</code>.
* </p>
*
* @since 3.7
*/
public static final String PREFERENCE_SHOW_ENCLOSED_IDEOGRAPHIC_SPACES= "showEnclosedIdeographicSpaces"; //$NON-NLS-1$
/**
* A named preference that controls the display of trailing Ideographic Space characters. The
* value is used only if the value of {@link #PREFERENCE_SHOW_WHITESPACE_CHARACTERS} is
* <code>true</code>.
* <p>
* Value is of type <code>Boolean</code>.
* </p>
*
* @since 3.7
*/
public static final String PREFERENCE_SHOW_TRAILING_IDEOGRAPHIC_SPACES= "showTrailingIdeographicSpaces"; //$NON-NLS-1$
/**
* A named preference that controls the display of leading Tab characters. The value is used
* only if the value of {@link #PREFERENCE_SHOW_WHITESPACE_CHARACTERS} is <code>true</code>.
* <p>
* Value is of type <code>Boolean</code>.
* </p>
*
* @since 3.7
*/
public static final String PREFERENCE_SHOW_LEADING_TABS= "showLeadingTabs"; //$NON-NLS-1$
/**
* A named preference that controls the display of enclosed Tab characters. The value is used
* only if the value of {@link #PREFERENCE_SHOW_WHITESPACE_CHARACTERS} is <code>true</code>.
* <p>
* Value is of type <code>Boolean</code>.
* </p>
*
* @since 3.7
*/
public static final String PREFERENCE_SHOW_ENCLOSED_TABS= "showEnclosedTabs"; //$NON-NLS-1$
/**
* A named preference that controls the display of trailing Tab characters. The value is used
* only if the value of {@link #PREFERENCE_SHOW_WHITESPACE_CHARACTERS} is <code>true</code>.
* <p>
* Value is of type <code>Boolean</code>.
* </p>
*
* @since 3.7
*/
public static final String PREFERENCE_SHOW_TRAILING_TABS= "showTrailingTabs"; //$NON-NLS-1$
/**
* A named preference that controls the display of Carriage Return characters. The value is used
* only if the value of {@link #PREFERENCE_SHOW_WHITESPACE_CHARACTERS} is <code>true</code>.
* <p>
* Value is of type <code>Boolean</code>.
* </p>
*
* @since 3.7
*/
public static final String PREFERENCE_SHOW_CARRIAGE_RETURN= "showCarriageReturn"; //$NON-NLS-1$
/**
* A named preference that controls the display of Line Feed characters. The value is used only
* if the value of {@link #PREFERENCE_SHOW_WHITESPACE_CHARACTERS} is <code>true</code>.
* <p>
* Value is of type <code>Boolean</code>.
* </p>
*
* @since 3.7
*/
public static final String PREFERENCE_SHOW_LINE_FEED= "showLineFeed"; //$NON-NLS-1$
/**
* A named preference that controls the alpha value of whitespace characters. The value is used
* only if the value of {@link #PREFERENCE_SHOW_WHITESPACE_CHARACTERS} is <code>true</code>.
* <p>
* Value is of type <code>Integer</code>.
* </p>
*
* @since 3.7
*/
public static final String PREFERENCE_WHITESPACE_CHARACTER_ALPHA_VALUE= "whitespaceCharacterAlphaValue"; //$NON-NLS-1$
/**
* A named preference that controls whether text drag and drop is enabled.
* <p>
* Value is of type <code>Boolean</code>.
* </p>
*
* @since 3.3
*/
public static final String PREFERENCE_TEXT_DRAG_AND_DROP_ENABLED= "textDragAndDropEnabled"; //$NON-NLS-1$
/**
* A named preference that controls if hovers should automatically be closed
* when the mouse is moved into them, or when they should be enriched.
* <p>
* Value is of type <code>Integer</code> and maps to the following
* {@link org.eclipse.jface.text.ITextViewerExtension8.EnrichMode}:
* </p>
* <ul>
* <li>-1: <code>null</code> (don't allow moving the mouse into a hover),</li>
* <li>0: {@link org.eclipse.jface.text.ITextViewerExtension8.EnrichMode#AFTER_DELAY},</li>
* <li>1: {@link org.eclipse.jface.text.ITextViewerExtension8.EnrichMode#IMMEDIATELY},</li>
* <li>2: {@link org.eclipse.jface.text.ITextViewerExtension8.EnrichMode#ON_CLICK}.</li>
* </ul>
*
* @since 3.4
*/
public static final String PREFERENCE_HOVER_ENRICH_MODE= "hoverReplaceMode"; //$NON-NLS-1$
/**
* A named preference to control the initial word wrap status.
* <p>
* Value is of type <code>Boolean</code>.
* </p>
*
* @since 3.10
*/
public static final String PREFERENCE_WORD_WRAP_ENABLED= "wordwrap.enabled"; //$NON-NLS-1$
/**
* A named preference to control the initial caret offset visibility on the status line.
* <p>
* Value is of type <code>Boolean</code>.
* </p>
*
* @since 3.13
*/
public static final String PREFERENCE_SHOW_CARET_OFFSET = "showCaretOffset"; //$NON-NLS-1$
/**
* A named preference to control the selection visibility on the status line.
* <p>
* Value is of type <code>Boolean</code>.
* </p>
*
* @since 3.13
*/
public static final String PREFERENCE_SHOW_SELECTION_SIZE = "showSelectionSize"; //$NON-NLS-1$
/** Menu id for the editor context menu. */
public static final String DEFAULT_EDITOR_CONTEXT_MENU_ID= "#EditorContext"; //$NON-NLS-1$
/** Menu id for the ruler context menu. */
public static final String DEFAULT_RULER_CONTEXT_MENU_ID= "#RulerContext"; //$NON-NLS-1$
/**
* Menu id used to contribute to the editor context menu of all textual editors.
*
* @since 3.5
*/
public static final String COMMON_EDITOR_CONTEXT_MENU_ID= "#AbstractTextEditorContext"; //$NON-NLS-1$
/**
* Menu id used to contribute to the ruler context menu of all textual editors.
*
* @since 3.5
*/
public static final String COMMON_RULER_CONTEXT_MENU_ID= "#AbstractTextEditorRulerContext"; //$NON-NLS-1$
/** The width of the vertical ruler. */
protected static final int VERTICAL_RULER_WIDTH= 12;
/**
* The complete mapping between action definition IDs used by eclipse and StyledText actions.
*
* @since 2.0
*/
protected static final IdMapEntry[] ACTION_MAP= new IdMapEntry[] {
// navigation
new IdMapEntry(ITextEditorActionDefinitionIds.LINE_UP, ST.LINE_UP),
new IdMapEntry(ITextEditorActionDefinitionIds.LINE_DOWN, ST.LINE_DOWN),
new IdMapEntry(ITextEditorActionDefinitionIds.LINE_START, ST.LINE_START),
new IdMapEntry(ITextEditorActionDefinitionIds.LINE_END, ST.LINE_END),
new IdMapEntry(ITextEditorActionDefinitionIds.COLUMN_PREVIOUS, ST.COLUMN_PREVIOUS),
new IdMapEntry(ITextEditorActionDefinitionIds.COLUMN_NEXT, ST.COLUMN_NEXT),
new IdMapEntry(ITextEditorActionDefinitionIds.PAGE_UP, ST.PAGE_UP),
new IdMapEntry(ITextEditorActionDefinitionIds.PAGE_DOWN, ST.PAGE_DOWN),
new IdMapEntry(ITextEditorActionDefinitionIds.WORD_PREVIOUS, ST.WORD_PREVIOUS),
new IdMapEntry(ITextEditorActionDefinitionIds.WORD_NEXT, ST.WORD_NEXT),
new IdMapEntry(ITextEditorActionDefinitionIds.TEXT_START, ST.TEXT_START),
new IdMapEntry(ITextEditorActionDefinitionIds.TEXT_END, ST.TEXT_END),
new IdMapEntry(ITextEditorActionDefinitionIds.WINDOW_START, ST.WINDOW_START),
new IdMapEntry(ITextEditorActionDefinitionIds.WINDOW_END, ST.WINDOW_END),
// selection
new IdMapEntry(ITextEditorActionDefinitionIds.SELECT_LINE_UP, ST.SELECT_LINE_UP),
new IdMapEntry(ITextEditorActionDefinitionIds.SELECT_LINE_DOWN, ST.SELECT_LINE_DOWN),
new IdMapEntry(ITextEditorActionDefinitionIds.SELECT_LINE_START, ST.SELECT_LINE_START),
new IdMapEntry(ITextEditorActionDefinitionIds.SELECT_LINE_END, ST.SELECT_LINE_END),
new IdMapEntry(ITextEditorActionDefinitionIds.SELECT_COLUMN_PREVIOUS, ST.SELECT_COLUMN_PREVIOUS),
new IdMapEntry(ITextEditorActionDefinitionIds.SELECT_COLUMN_NEXT, ST.SELECT_COLUMN_NEXT),
new IdMapEntry(ITextEditorActionDefinitionIds.SELECT_PAGE_UP, ST.SELECT_PAGE_UP),
new IdMapEntry(ITextEditorActionDefinitionIds.SELECT_PAGE_DOWN, ST.SELECT_PAGE_DOWN),
new IdMapEntry(ITextEditorActionDefinitionIds.SELECT_WORD_PREVIOUS, ST.SELECT_WORD_PREVIOUS),
new IdMapEntry(ITextEditorActionDefinitionIds.SELECT_WORD_NEXT, ST.SELECT_WORD_NEXT),
new IdMapEntry(ITextEditorActionDefinitionIds.SELECT_TEXT_START, ST.SELECT_TEXT_START),
new IdMapEntry(ITextEditorActionDefinitionIds.SELECT_TEXT_END, ST.SELECT_TEXT_END),
new IdMapEntry(ITextEditorActionDefinitionIds.SELECT_WINDOW_START, ST.SELECT_WINDOW_START),
new IdMapEntry(ITextEditorActionDefinitionIds.SELECT_WINDOW_END, ST.SELECT_WINDOW_END),
// modification
new IdMapEntry(IWorkbenchCommandConstants.EDIT_CUT, ST.CUT),
new IdMapEntry(IWorkbenchCommandConstants.EDIT_COPY, ST.COPY),
new IdMapEntry(IWorkbenchCommandConstants.EDIT_PASTE, ST.PASTE),
new IdMapEntry(ITextEditorActionDefinitionIds.DELETE_PREVIOUS, ST.DELETE_PREVIOUS),
new IdMapEntry(ITextEditorActionDefinitionIds.DELETE_NEXT, ST.DELETE_NEXT),
new IdMapEntry(ITextEditorActionDefinitionIds.DELETE_PREVIOUS_WORD, ST.DELETE_WORD_PREVIOUS),
new IdMapEntry(ITextEditorActionDefinitionIds.DELETE_NEXT_WORD, ST.DELETE_WORD_NEXT),
// miscellaneous
new IdMapEntry(ITextEditorActionDefinitionIds.TOGGLE_OVERWRITE, ST.TOGGLE_OVERWRITE)
};
private final String fReadOnlyLabel= EditorMessages.Editor_statusline_state_readonly_label;
private final String fWritableLabel= EditorMessages.Editor_statusline_state_writable_label;
private final String fInsertModeLabel= EditorMessages.Editor_statusline_mode_insert_label;
private final String fOverwriteModeLabel= EditorMessages.Editor_statusline_mode_overwrite_label;
private final String fSmartInsertModeLabel= EditorMessages.Editor_statusline_mode_smartinsert_label;
/** The error message shown in the status line in case of failed information look up. */
protected final String fErrorLabel= EditorMessages.Editor_statusline_error_label;
/**
* Data structure for the position label value.
*/
private static class PositionLabelValue {
public int fValue;
@Override
public String toString() {
return String.valueOf(fValue);
}
}
/** The pattern used to show the position label in the status line. */
private final String fPositionLabelPattern= EditorMessages.Editor_statusline_position_pattern;
/** The position label value of the current line. */
private final PositionLabelValue fLineLabel= new PositionLabelValue();
/** The position label value of the current column. */
private final PositionLabelValue fColumnLabel= new PositionLabelValue();
/** The position label value of the current offset. */
private final PositionLabelValue fOffsetLabel = new PositionLabelValue();
/** The arguments for the position label pattern. */
private final Object[] fPositionLabelPatternArguments = new Object[] { fLineLabel, fColumnLabel, fOffsetLabel };
/**
* The column support of this editor.
* @since 3.3
*/
private IColumnSupport fColumnSupport;
/** The editor's explicit document provider. */
private IDocumentProvider fExplicitDocumentProvider;
/** The editor's preference store. */
private IPreferenceStore fPreferenceStore;
/** The editor's range indicator. */
private Annotation fRangeIndicator;
/** The editor's source viewer configuration. */
private SourceViewerConfiguration fConfiguration;
/** The editor's source viewer. */
private ISourceViewer fSourceViewer;
/**
* The editor's selection provider.
* @since 2.1
*/
private SelectionProvider fSelectionProvider= new SelectionProvider();
/**
* The editor's selection listener.
* @since 3.0
*/
private SelectionListener fSelectionListener;
/** The editor's font. */
private Font fFont; /**
* The editor's foreground color.
* @since 2.0
*/
private Color fForegroundColor;
/**
* The editor's background color.
* @since 2.0
*/
private Color fBackgroundColor;
/**
* The editor's selection foreground color.
* @since 3.0
*/
private Color fSelectionForegroundColor;
/**
* The editor's selection background color.
* @since 3.0
*/
private Color fSelectionBackgroundColor;
/**
* The find scope's highlight color.
* @since 2.0
*/
private Color fFindScopeHighlightColor;
/**
* The editor's status line.
* @since 2.1
*/
private IEditorStatusLine fEditorStatusLine;
/** The editor's vertical ruler. */
private IVerticalRuler fVerticalRuler;
/** The editor's context menu id. */
private String fEditorContextMenuId;
/** The ruler's context menu id. */
private String fRulerContextMenuId;
/** The editor's help context id. */
private String fHelpContextId;
/** The editor's presentation mode. */
private boolean fShowHighlightRangeOnly;
/** The actions registered with the editor. */
private Map<String, IAction> fActions= new HashMap<>(10);
/** The actions marked as selection dependent. */
private List<String> fSelectionActions= new ArrayList<>(5);
/** The actions marked as content dependent. */
private List<String> fContentActions= new ArrayList<>(5);
/**
* The actions marked as property dependent.
* @since 2.0
*/
private List<String> fPropertyActions= new ArrayList<>(5);
/**
* The actions marked as state dependent.
* @since 2.0
*/
private List<String> fStateActions= new ArrayList<>(5);
/** The editor's action activation codes. */
private List<ActionActivationCode> fActivationCodes= new ArrayList<>(2);
/** The verify key listener for activation code triggering. */
private ActivationCodeTrigger fActivationCodeTrigger= new ActivationCodeTrigger();
/** Context menu listener. */
private IMenuListener fMenuListener;
/** Vertical ruler mouse listener. */
private MouseListener fMouseListener;
/** Selection changed listener. */
private ISelectionChangedListener fSelectionChangedListener;
/** Title image to be disposed. */
private Image fTitleImage;
/** The text context menu to be disposed. */
private Menu fTextContextMenu;
/** The ruler context menu to be disposed. */
private Menu fRulerContextMenu;
/** The editor's element state listener. */
private IElementStateListener fElementStateListener= new ElementStateListener();
/**
* The editor's text input listener.
* @since 2.1
*/
private TextInputListener fTextInputListener= new TextInputListener();
/** The editor's text listener. */
private TextListener fTextListener= new TextListener();
/** The editor's property change listener. */
private IPropertyChangeListener fPropertyChangeListener= new PropertyChangeListener();
/**
* The editor's font properties change listener.
* @since 2.1
*/
private IPropertyChangeListener fFontPropertyChangeListener= new FontPropertyChangeListener();
/**
* The editor's activation listener.
* @since 2.0
*/
private ActivationListener fActivationListener;
/**
* Indicates activation should be handled.
* @since 3.9
*/
private boolean fHandleActivation= true;
/**
* The map of the editor's status fields.
* @since 2.0
*/
private Map<String, IStatusField> fStatusFields;
/**
* The editor's cursor listener.
* @since 2.0
*/
private ICursorListener fCursorListener;
/**
* The editor's remembered text selection.
* @since 2.0
*/
private ISelection fRememberedSelection;
/**
* Indicates whether the editor runs in 1.0 context menu registration compatibility mode.
* @since 2.0
*/
private boolean fCompatibilityMode= true;
/**
* The number of re-entrances into error correction code while saving.
* @since 2.0
*/
private int fErrorCorrectionOnSave;
/**
* The delete line target.
* @since 2.1
*/
private IDeleteLineTarget fDeleteLineTarget;
/**
* The incremental find target.
* @since 2.0
*/
private IncrementalFindTarget fIncrementalFindTarget;
/**
* The mark region target.
* @since 2.0
*/
private IMarkRegionTarget fMarkRegionTarget;
/**
* Cached modification stamp of the editor's input.
* @since 2.0
*/
private long fModificationStamp= -1;
/**
* Ruler context menu listeners.
* @since 2.0
*/
private List<IMenuListener> fRulerContextMenuListeners= new ArrayList<>();
/**
* Indicates whether sanity checking in enabled.
* @since 2.0
*/
private boolean fIsSanityCheckEnabled= true;
/**
* The find replace target.
* @since 2.1
*/
private FindReplaceTarget fFindReplaceTarget;
/**
* Indicates whether state validation is enabled.
* @since 2.1
*/
private boolean fIsStateValidationEnabled= true;
/**
* The key binding scopes of this editor.
* @since 2.1
*/
private String[] fKeyBindingScopes;
/**
* Whether the overwrite mode can be turned on.
* @since 3.0
*/
private boolean fIsOverwriteModeEnabled= true;
/**
* Whether the overwrite mode is currently on.
* @since 3.0
*/
private boolean fIsOverwriting= false;
/**
* The editor's insert mode.
* @since 3.0
*/
private InsertMode fInsertMode= SMART_INSERT;
/**
* The sequence of legal editor insert modes.
* @since 3.0
*/
private List<InsertMode> fLegalInsertModes= null;
/**
* The non-default caret.
* @since 3.0
*/
private Caret fNonDefaultCaret;
/**
* The image used in non-default caret.
* @since 3.0
*/
private Image fNonDefaultCaretImage;
/**
* The styled text's initial caret.
* @since 3.0
*/
private Caret fInitialCaret;
/**
* The operation approver used to warn on undoing of non-local operations.
* @since 3.1
*/
private IOperationApprover fNonLocalOperationApprover;
/**
* The operation approver used to warn of linear undo violations.
* @since 3.1
*/
private IOperationApprover fLinearUndoViolationApprover;
/**
* This editor's memento holding data for restoring it after restart.
* @since 3.3
*/
private IMemento fMementoToRestore;
/**
* This editor's savable.
* @since 3.3
*/
private TextEditorSavable fSavable;
/**
* Tells whether text drag and drop has been installed on the control.
* @since 3.3
*/
private boolean fIsTextDragAndDropInstalled= false;
/**
* Helper token to decide whether drag and
* drop happens inside the same editor.
* @since 3.3
*/
private Object fTextDragAndDropToken;
/**
* The information presenter, may be <code>null</code>.
* @since 3.3
*/
private FocusedInformationPresenter fInformationPresenter;
/**
* Tells whether this editor has been activated at least once.
* @since 3.3.2
*/
private boolean fHasBeenActivated= false;
/**
* Key binding support for the quick assist assistant.
* @since 3.4
*/
private KeyBindingSupportForAssistant fKeyBindingSupportForQuickAssistant;
/**
* Key binding support for the quick assist assistant.
* @since 3.5
*/
private KeyBindingSupportForAssistant fKeyBindingSupportForContentAssistant;
/**
* The save action.
* @since 3.6.1
*/
private IWorkbenchAction fSaveAction;
/**
* Creates a new text editor. If not explicitly set, this editor uses
* a <code>SourceViewerConfiguration</code> to configure its
* source viewer. This viewer does not have a range indicator installed,
* nor any menu id set. By default, the created editor runs in 1.0 context
* menu registration compatibility mode.
*/
protected AbstractTextEditor() {
super();
fEditorContextMenuId= null;
fRulerContextMenuId= null;
fHelpContextId= null;
}
@Override
public IDocumentProvider getDocumentProvider() {
return fExplicitDocumentProvider;
}
/**
* Returns the editor's range indicator. May return <code>null</code> if no
* range indicator is installed.
*
* @return the editor's range indicator which may be <code>null</code>
*/
protected final Annotation getRangeIndicator() {
return fRangeIndicator;
}
/**
* Returns the editor's source viewer configuration. May return <code>null</code>
* before the editor's part has been created and after disposal.
*
* @return the editor's source viewer configuration which may be <code>null</code>
*/
protected final SourceViewerConfiguration getSourceViewerConfiguration() {
return fConfiguration;
}
/**
* Returns the editor's source viewer. May return <code>null</code> before
* the editor's part has been created and after disposal.
*
* @return the editor's source viewer which may be <code>null</code>
*/
protected final ISourceViewer getSourceViewer() {
return fSourceViewer;
}
/**
* Returns the editor's vertical ruler. May return <code>null</code> before
* the editor's part has been created and after disposal.
*
* @return the editor's vertical ruler which may be <code>null</code>
*/
protected final IVerticalRuler getVerticalRuler() {
return fVerticalRuler;
}
/**
* Returns the editor's context menu id. May return <code>null</code> before
* the editor's part has been created.
*
* @return the editor's context menu id which may be <code>null</code>
*/
protected final String getEditorContextMenuId() {
return fEditorContextMenuId;
}
/**
* Returns the ruler's context menu id. May return <code>null</code> before
* the editor's part has been created.
*
* @return the ruler's context menu id which may be <code>null</code>
*/
protected final String getRulerContextMenuId() {
return fRulerContextMenuId;
}
/**
* Returns the editor's help context id or <code>null</code> if none has
* been set.
*
* @return the editor's help context id which may be <code>null</code>
*/
protected final String getHelpContextId() {
return fHelpContextId;
}
/**
* Returns this editor's preference store or <code>null</code> if none has
* been set.
*
* @return this editor's preference store which may be <code>null</code>
*/
protected final IPreferenceStore getPreferenceStore() {
return fPreferenceStore;
}
/**
* Sets this editor's document provider. This method must be
* called before the editor's control is created.
*
* @param provider the document provider
*/
protected void setDocumentProvider(IDocumentProvider provider) {
fExplicitDocumentProvider= provider;
}
/**
* Sets this editor's source viewer configuration used to configure its
* internal source viewer. This method must be called before the editor's
* control is created. If not, this editor uses a <code>SourceViewerConfiguration</code>.
*
* @param configuration the source viewer configuration object
*/
protected void setSourceViewerConfiguration(SourceViewerConfiguration configuration) {
Assert.isNotNull(configuration);
fConfiguration= configuration;
}
/**
* Sets the annotation which this editor uses to represent the highlight
* range if the editor is configured to show the entire document. If the
* range indicator is not set, this editor will not show a range indication.
*
* @param rangeIndicator the annotation
*/
protected void setRangeIndicator(Annotation rangeIndicator) {
Assert.isNotNull(rangeIndicator);
fRangeIndicator= rangeIndicator;
}
/**
* Sets this editor's context menu id.
*
* @param contextMenuId the context menu id
*/
protected void setEditorContextMenuId(String contextMenuId) {
Assert.isNotNull(contextMenuId);
fEditorContextMenuId= contextMenuId;
}
/**
* Sets the ruler's context menu id.
*
* @param contextMenuId the context menu id
*/
protected void setRulerContextMenuId(String contextMenuId) {
Assert.isNotNull(contextMenuId);
fRulerContextMenuId= contextMenuId;
}
/**
* Sets the context menu registration 1.0 compatibility mode. (See class
* description for more details.)
*
* @param compatible <code>true</code> if compatibility mode is enabled
* @since 2.0
*/
protected final void setCompatibilityMode(boolean compatible) {
fCompatibilityMode= compatible;
}
/**
* Sets the editor's help context id.
*
* @param helpContextId the help context id
*/
protected void setHelpContextId(String helpContextId) {
Assert.isNotNull(helpContextId);
fHelpContextId= helpContextId;
}
/**
* Sets the key binding scopes for this editor.
*
* @param scopes a non-empty array of key binding scope identifiers
* @since 2.1
*/
protected void setKeyBindingScopes(String[] scopes) {
Assert.isTrue(scopes != null && scopes.length > 0);
fKeyBindingScopes= scopes;
}
/**
* Sets this editor's preference store. This method must be
* called before the editor's control is created.
*
* @param store the preference store or <code>null</code> to remove the
* preference store
*/
protected void setPreferenceStore(IPreferenceStore store) {
if (fPreferenceStore != null) {
fPreferenceStore.removePropertyChangeListener(fPropertyChangeListener);
fPreferenceStore.removePropertyChangeListener(fFontPropertyChangeListener);
}
fPreferenceStore= store;
if (fPreferenceStore != null) {
fPreferenceStore.addPropertyChangeListener(fPropertyChangeListener);
fPreferenceStore.addPropertyChangeListener(fFontPropertyChangeListener);
}
}
@Override
public boolean isEditable() {
IDocumentProvider provider= getDocumentProvider();
if (provider instanceof IDocumentProviderExtension) {
IDocumentProviderExtension extension= (IDocumentProviderExtension) provider;
return extension.isModifiable(getEditorInput());
}
return false;
}
/**
* {@inheritDoc}
* <p>
* Returns <code>null</code> after disposal.
* </p>
*
* @return the selection provider or <code>null</code> if the editor has
* been disposed
*/
@Override
public ISelectionProvider getSelectionProvider() {
return fSelectionProvider;
}
/**
* Remembers the current selection of this editor. This method is called when, e.g.,
* the content of the editor is about to be reverted to the saved state. This method
* remembers the selection in a semantic format, i.e., in a format which allows to
* restore the selection even if the originally selected text is no longer part of the
* editor's content.
* <p>
* Subclasses should implement this method including all necessary state. This
* default implementation remembers the textual range only and is thus purely
* syntactic.</p>
*
* @see #restoreSelection()
* @since 2.0
*/
protected void rememberSelection() {
fRememberedSelection= doGetSelection();
}
/**
* Returns the current selection.
* @return ISelection
* @since 2.1
*/
protected ISelection doGetSelection() {
ISelectionProvider sp= null;
if (fSourceViewer != null)
sp= fSourceViewer.getSelectionProvider();
return (sp == null ? null : sp.getSelection());
}
/**
* Restores a selection previously remembered by <code>rememberSelection</code>.
* Subclasses may reimplement this method and thereby semantically adapt the
* remembered selection. This default implementation just selects the
* remembered textual range.
*
* @see #rememberSelection()
* @since 2.0
*/
protected void restoreSelection() {
if (fRememberedSelection instanceof ITextSelection) {
ITextSelection textSelection= (ITextSelection)fRememberedSelection;
if (isValidSelection(textSelection.getOffset(), textSelection.getLength()))
doSetSelection(fRememberedSelection);
}
fRememberedSelection= null;
}
/**
* Tells whether the given selection is valid.
*
* @param offset the offset of the selection
* @param length the length of the selection
* @return <code>true</code> if the selection is valid
* @since 2.1
*/
private boolean isValidSelection(int offset, int length) {
IDocumentProvider provider= getDocumentProvider();
if (provider != null) {
IDocument document= provider.getDocument(getEditorInput());
if (document != null) {
int end= offset + length;
int documentLength= document.getLength();
return 0 <= offset && offset <= documentLength && 0 <= end && end <= documentLength && length >= 0;
}
}
return false;
}
/**
* Sets the given selection.
*
* @param selection the selection
* @since 2.1
*/
protected void doSetSelection(ISelection selection) {
if (selection instanceof ITextSelection) {
ITextSelection textSelection= (ITextSelection) selection;
selectAndReveal(textSelection.getOffset(), textSelection.getLength());
}
}
/**
* Creates the listener on this editor's context menus.
*
* @return the created menu listener
* @since 3.4
*/
protected IMenuListener createContextMenuListener() {
return menu -> {
String id = menu.getId();
if (getRulerContextMenuId().equals(id)) {
setFocus();
rulerContextMenuAboutToShow(menu);
} else if (getEditorContextMenuId().equals(id)) {
setFocus();
editorContextMenuAboutToShow(menu);
}
};
}
/**
* Creates and returns the listener on this editor's context menus.
*
* @return the menu listener
*/
protected final IMenuListener getContextMenuListener() {
if (fMenuListener == null)
fMenuListener= createContextMenuListener();
return fMenuListener;
}
/**
* Creates and returns the listener on this editor's vertical ruler.
*
* @return the mouse listener
*/
protected final MouseListener getRulerMouseListener() {
if (fMouseListener == null) {
fMouseListener= new MouseListener() {
private boolean fDoubleClicked= false;
private final int fDoubleClickTime= getSite().getShell().getDisplay().getDoubleClickTime();
private long fMouseUpDelta= 0;
private void triggerAction(String actionID, MouseEvent e) {
// ActionId can be prefixed with modifiers
StringBuilder newActionId = new StringBuilder(""); //$NON-NLS-1$
if ((e.stateMask & SWT.MOD1) > 0) {
newActionId.append("M1+"); //$NON-NLS-1$
}
if ((e.stateMask & SWT.MOD2) > 0) {
newActionId.append("M2+"); //$NON-NLS-1$
}
if ((e.stateMask & SWT.MOD3) > 0) {
newActionId.append("M3+"); //$NON-NLS-1$
}
newActionId.append(actionID);
IAction action = getAction(newActionId.toString());
// If action does not exist with specified
// modifiers+actionId, try to retrieve action with only
// actionId
if (action == null) {
action = getAction(actionID);
}
if (action != null) {
if (action instanceof IUpdate)
((IUpdate) action).update();
if (action.isEnabled()) {
Event event= new Event();
event.type= fDoubleClicked ? SWT.MouseDoubleClick : SWT.MouseUp;
event.display= e.display;
event.widget= e.widget;
event.time= e.time;
event.data= e.data;
event.x= e.x;
event.y= e.y;
event.button= e.button;
event.stateMask= e.stateMask;
event.count= e.count;
action.runWithEvent(event);
}
}
}
@Override
public void mouseUp(final MouseEvent e) {
setFocus();
final int delay= fMouseUpDelta == 0 ? 0 : fDoubleClickTime - (int)(System.currentTimeMillis() - fMouseUpDelta);
if (1 != e.button)
return;
Runnable runnable = () -> {
if (!fDoubleClicked)
triggerAction(ITextEditorActionConstants.RULER_CLICK, e);
};
if (delay <= 0)
runnable.run();
else
e.widget.getDisplay().timerExec(delay, runnable);
}
@Override
public void mouseDoubleClick(MouseEvent e) {
if (1 == e.button) {
fDoubleClicked= true;
triggerAction(ITextEditorActionConstants.RULER_DOUBLE_CLICK, e);
}
}
@Override
public void mouseDown(MouseEvent e) {
fMouseUpDelta= System.currentTimeMillis();
fDoubleClicked= false;
if (fRulerContextMenu != null && !fRulerContextMenu.isDisposed()) {
Display display= fRulerContextMenu.getDisplay();
Point location= display.getCursorLocation();
fRulerContextMenu.setLocation(location.x, location.y);
}
}
};
}
return fMouseListener;
}
/**
* Returns this editor's selection changed listener to be installed
* on the editor's source viewer.
*
* @return the listener
*/
protected final ISelectionChangedListener getSelectionChangedListener() {
if (fSelectionChangedListener == null) {
fSelectionChangedListener= new ISelectionChangedListener() {
private Runnable fRunnable = () -> {
// check whether editor has not been disposed yet
if (fSourceViewer != null && fSourceViewer.getDocument() != null) {
handleCursorPositionChanged();
updateSelectionDependentActions();
}
};
private Display fDisplay;
@Override
public void selectionChanged(SelectionChangedEvent event) {
if (fDisplay == null)
fDisplay= getSite().getShell().getDisplay();
if (Display.getCurrent() == fDisplay)
fRunnable.run();
else
fDisplay.asyncExec(fRunnable);
}
};
}
return fSelectionChangedListener;
}
/**
* Returns this editor's "cursor" listener to be installed on the editor's
* source viewer. This listener is listening to key and mouse button events.
* It triggers the updating of the status line by calling
* <code>handleCursorPositionChanged()</code>.
*
* @return the listener
* @since 2.0
*/
protected final ICursorListener getCursorListener() {
if (fCursorListener == null) {
fCursorListener= new ICursorListener() {
@Override
public void keyPressed(KeyEvent e) {
handleCursorPositionChanged();
}
@Override
public void keyReleased(KeyEvent e) {
}
@Override
public void mouseDoubleClick(MouseEvent e) {
}
@Override
public void mouseDown(MouseEvent e) {
}
@Override
public void mouseUp(MouseEvent e) {
handleCursorPositionChanged();
}
};
}
return fCursorListener;
}
/**
* Implements the <code>init</code> method of <code>IEditorPart</code>.
* Subclasses replacing <code>init</code> may choose to call this method in
* their implementation.
*
* @param window the workbench window
* @param site the editor's site
* @param input the editor input for the editor being created
* @throws PartInitException if {@link #doSetInput(IEditorInput)} fails or gets canceled
*
* @see org.eclipse.ui.IEditorPart#init(org.eclipse.ui.IEditorSite, org.eclipse.ui.IEditorInput)
* @since 2.1
*/
protected final void internalInit(IWorkbenchWindow window, final IEditorSite site, final IEditorInput input) throws PartInitException {
IRunnableWithProgress runnable = monitor -> {
try {
if (getDocumentProvider() instanceof IDocumentProviderExtension2) {
IDocumentProviderExtension2 extension1 = (IDocumentProviderExtension2) getDocumentProvider();
extension1.setProgressMonitor(monitor);
}
doSetInput(input);
} catch (CoreException x) {
throw new InvocationTargetException(x);
} finally {
if (getDocumentProvider() instanceof IDocumentProviderExtension2) {
IDocumentProviderExtension2 extension2 = (IDocumentProviderExtension2) getDocumentProvider();
extension2.setProgressMonitor(null);
}
}
};
try {
// When using the progress service always a modal dialog pops up. The site should be asked for a runnable context
// which could be the workbench window or the progress service, depending on what the site represents.
// getSite().getWorkbenchWindow().getWorkbench().getProgressService().run(false, true, runnable);
getSite().getWorkbenchWindow().run(false, true, runnable);
} catch (InterruptedException x) {
} catch (InvocationTargetException x) {
Throwable t= x.getTargetException();
if (t instanceof CoreException) {
/*
/* XXX: Remove unpacking of CoreException once the following bug is
* fixed: https://bugs.eclipse.org/bugs/show_bug.cgi?id=81640
*/
CoreException e= (CoreException)t;
IStatus status= e.getStatus();
if (status.getException() != null)
throw new PartInitException(status);
throw new PartInitException(new Status(status.getSeverity(), status.getPlugin(), status.getCode(), status.getMessage(), t));
}
throw new PartInitException(new Status(IStatus.ERROR, TextEditorPlugin.PLUGIN_ID, IStatus.OK, EditorMessages.Editor_error_init, t));
}
}
@Override
public void init(final IEditorSite site, final IEditorInput input) throws PartInitException {
setSite(site);
internalInit(site.getWorkbenchWindow(), site, input);
fActivationListener= new ActivationListener(site.getWorkbenchWindow().getPartService());
}
/**
* Creates the vertical ruler to be used by this editor.
* Subclasses may re-implement this method.
*
* @return the vertical ruler
*/
protected IVerticalRuler createVerticalRuler() {
return new VerticalRuler(VERTICAL_RULER_WIDTH);
}
/**
* Adds enabled ruler contributions to the vertical ruler.
* <p>
* Clients may extend or replace.</p>
*
* @param ruler the composite ruler to add contributions to
* @since 3.3
*/
protected void updateContributedRulerColumns(CompositeRuler ruler) {
IColumnSupport support= getAdapter(IColumnSupport.class);
if (support == null)
return;
RulerColumnPreferenceAdapter adapter= null;
if (fPreferenceStore != null)
adapter= new RulerColumnPreferenceAdapter(getPreferenceStore(), PREFERENCE_RULER_CONTRIBUTIONS);
RulerColumnRegistry registry= RulerColumnRegistry.getDefault();
List<RulerColumnDescriptor> descriptors= registry.getColumnDescriptors();
for (Iterator<RulerColumnDescriptor> it= descriptors.iterator(); it.hasNext();) {
final RulerColumnDescriptor descriptor= it.next();
support.setColumnVisible(descriptor, adapter == null || adapter.isEnabled(descriptor));
}
}
/**
* Creates the column support to be used by this editor to manage the
* contributed ruler columns.
* Subclasses may re-implement this method using the {@link ColumnSupport},
* e.g. by returning <code>new ColumnSupport(this, RulerColumnRegistry.getDefault());</code>.
* <p>
* <strong>Note:</strong> If you override this method to provide column support you will
* also need to override {@link #createVerticalRuler()} to return a {@link CompositeRuler}.</p>
* <p>
* Out of the box this class does not install this support and hence this
* implementation always returns <code>null</code>.</p>
*
* @return the column support or <code>null</code> if none
* @since 3.3
*/
protected IColumnSupport createColumnSupport() {
return null;
}
/**
* Creates the source viewer to be used by this editor.
* Subclasses may re-implement this method.
*
* @param parent the parent control
* @param ruler the vertical ruler
* @param styles style bits, <code>SWT.WRAP</code> is currently not supported
* @return the source viewer
*/
protected ISourceViewer createSourceViewer(Composite parent, IVerticalRuler ruler, int styles) {
return new SourceViewer(parent, ruler, styles);
}
/**
* Initializes the drag and drop support for the given viewer based on
* provided editor adapter for drop target listeners.
*
* @param viewer the viewer
* @since 3.0
*/
protected void initializeDragAndDrop(ISourceViewer viewer) {
IDragAndDropService dndService= getSite().getService(IDragAndDropService.class);
if (dndService == null)
return;
ITextEditorDropTargetListener listener= getAdapter(ITextEditorDropTargetListener.class);
if (listener == null) {
Object object= Platform.getAdapterManager().loadAdapter(this, "org.eclipse.ui.texteditor.ITextEditorDropTargetListener"); //$NON-NLS-1$
if (object instanceof ITextEditorDropTargetListener)
listener= (ITextEditorDropTargetListener)object;
}
if (listener != null)
dndService.addMergedDropTarget(viewer.getTextWidget(), DND.DROP_MOVE | DND.DROP_COPY, listener.getTransfers(), listener);
IPreferenceStore store= getPreferenceStore();
if (store != null && store.getBoolean(PREFERENCE_TEXT_DRAG_AND_DROP_ENABLED))
installTextDragAndDrop(viewer);
}
/**
* The <code>AbstractTextEditor</code> implementation of this
* <code>IWorkbenchPart</code> method creates the vertical ruler and source
* viewer.
* <p>
* Subclasses may extend this method. Besides extending this method, the
* behavior of <code>createPartControl</code> may be customized by calling,
* extending or replacing the following methods: <br>
* Subclasses may supply customized implementations for some members using
* the following methods before <code>createPartControl</code> is invoked:
* </p>
* <ul>
* <li>{@linkplain #setSourceViewerConfiguration(SourceViewerConfiguration)
* setSourceViewerConfiguration} to supply a custom source viewer
* configuration,</li>
* <li>{@linkplain #setRangeIndicator(Annotation) setRangeIndicator} to
* provide a range indicator,</li>
* <li>{@linkplain #setHelpContextId(String) setHelpContextId} to provide a
* help context id,</li>
* <li>{@linkplain #setEditorContextMenuId(String) setEditorContextMenuId}
* to set a custom context menu id,</li>
* <li>{@linkplain #setRulerContextMenuId(String) setRulerContextMenuId} to
* set a custom ruler context menu id.</li>
* </ul>
* <br>
* Subclasses may replace the following methods called from within
* <code>createPartControl</code>:
* <ul>
* <li>{@linkplain #createVerticalRuler() createVerticalRuler} to supply a
* custom vertical ruler,</li>
* <li>{@linkplain #createSourceViewer(Composite, IVerticalRuler, int)
* createSourceViewer} to supply a custom source viewer,</li>
* <li>{@linkplain #getSelectionProvider() getSelectionProvider} to supply a
* custom selection provider.</li>
* </ul>
* <br>
* Subclasses may extend the following methods called from within
* <code>createPartControl</code>:
* <ul>
* <li>{@linkplain #initializeViewerColors(ISourceViewer)
* initializeViewerColors} to customize the viewer color scheme (may also be
* replaced),</li>
* <li>{@linkplain #initializeDragAndDrop(ISourceViewer)
* initializeDragAndDrop} to customize drag and drop (may also be
* replaced),</li>
* <li>{@linkplain #createNavigationActions() createNavigationActions} to
* add navigation actions,</li>
* <li>{@linkplain #createActions() createActions} to add text editor
* actions.</li>
* </ul>
*
* @param parent
* the parent composite
*/
@Override
public void createPartControl(Composite parent) {
fVerticalRuler= createVerticalRuler();
int styles= SWT.V_SCROLL | SWT.H_SCROLL | SWT.MULTI | SWT.BORDER | SWT.FULL_SELECTION;
fSourceViewer= createSourceViewer(parent, fVerticalRuler, styles);
if (fConfiguration == null)
fConfiguration= new SourceViewerConfiguration();
fSourceViewer.configure(fConfiguration);
if (fSourceViewer instanceof ISourceViewerExtension4)
fKeyBindingSupportForContentAssistant= new KeyBindingSupportForAssistant(((ISourceViewerExtension4)fSourceViewer));
if (fSourceViewer instanceof ISourceViewerExtension3) {
IQuickAssistAssistant assistant= ((ISourceViewerExtension3)fSourceViewer).getQuickAssistAssistant();
if (assistant != null)
fKeyBindingSupportForQuickAssistant= new KeyBindingSupportForAssistant(assistant);
}
if (fRangeIndicator != null)
fSourceViewer.setRangeIndicator(fRangeIndicator);
fSourceViewer.addTextListener(fTextListener);
fSourceViewer.addTextInputListener(fTextListener);
getSelectionProvider().addSelectionChangedListener(getSelectionChangedListener());
initializeViewerFont(fSourceViewer);
initializeViewerColors(fSourceViewer);
initializeFindScopeColor(fSourceViewer);
initializeDragAndDrop(fSourceViewer);
StyledText styledText= fSourceViewer.getTextWidget();
styledText.addMouseListener(getCursorListener());
styledText.addKeyListener(getCursorListener());
// Disable orientation switching until we fully support it.
styledText.addListener(SWT.OrientationChange, event -> event.doit = false);
if (getHelpContextId() != null)
PlatformUI.getWorkbench().getHelpSystem().setHelp(styledText, getHelpContextId());
String id= fEditorContextMenuId != null ? fEditorContextMenuId : DEFAULT_EDITOR_CONTEXT_MENU_ID;
MenuManager manager= new MenuManager(id, id);
manager.setRemoveAllWhenShown(true);
manager.addMenuListener(getContextMenuListener());
fTextContextMenu= manager.createContextMenu(styledText);
styledText.setMenu(fTextContextMenu);
if (fEditorContextMenuId != null)
getEditorSite().registerContextMenu(fEditorContextMenuId, manager, getSelectionProvider(), isEditorInputIncludedInContextMenu());
else if (fCompatibilityMode)
getEditorSite().registerContextMenu(DEFAULT_EDITOR_CONTEXT_MENU_ID, manager, getSelectionProvider(), isEditorInputIncludedInContextMenu());
if ((fEditorContextMenuId != null && fCompatibilityMode) || fEditorContextMenuId == null) {
String partId= getEditorSite().getId();
if (partId != null)
getEditorSite().registerContextMenu(partId + ".EditorContext", manager, getSelectionProvider(), isEditorInputIncludedInContextMenu()); //$NON-NLS-1$
}
getEditorSite().registerContextMenu(COMMON_EDITOR_CONTEXT_MENU_ID, manager, getSelectionProvider(), false);
if (fEditorContextMenuId == null)
fEditorContextMenuId= DEFAULT_EDITOR_CONTEXT_MENU_ID;
id= fRulerContextMenuId != null ? fRulerContextMenuId : DEFAULT_RULER_CONTEXT_MENU_ID;
manager= new MenuManager(id, id);
manager.setRemoveAllWhenShown(true);
manager.addMenuListener(getContextMenuListener());
Control rulerControl= fVerticalRuler.getControl();
fRulerContextMenu= manager.createContextMenu(rulerControl);
rulerControl.setMenu(fRulerContextMenu);
rulerControl.addMouseListener(getRulerMouseListener());
if (fRulerContextMenuId != null)
getEditorSite().registerContextMenu(fRulerContextMenuId, manager, getSelectionProvider(), false);
else if (fCompatibilityMode)
getEditorSite().registerContextMenu(DEFAULT_RULER_CONTEXT_MENU_ID, manager, getSelectionProvider(), false);
if ((fRulerContextMenuId != null && fCompatibilityMode) || fRulerContextMenuId == null) {
String partId= getSite().getId();
if (partId != null)
getEditorSite().registerContextMenu(partId + ".RulerContext", manager, getSelectionProvider(), false); //$NON-NLS-1$
}
getEditorSite().registerContextMenu(COMMON_RULER_CONTEXT_MENU_ID, manager, getSelectionProvider(), false);
if (fRulerContextMenuId == null)
fRulerContextMenuId= DEFAULT_RULER_CONTEXT_MENU_ID;
initializeZoomGestures(rulerControl, fSourceViewer);
getSite().setSelectionProvider(getSelectionProvider());
fSelectionListener= new SelectionListener();
fSelectionListener.install(getSelectionProvider());
fSelectionListener.setDocument(getDocumentProvider().getDocument(getEditorInput()));
initializeActivationCodeTrigger();
createNavigationActions();
createAccessibilityActions();
createActions();
initializeSourceViewer(getEditorInput());
/* since 3.2 - undo redo actions should be created after
* the source viewer is initialized, so that the undo manager
* can obtain its undo context from its document.
*/
createUndoRedoActions();
JFaceResources.getFontRegistry().addListener(fFontPropertyChangeListener);
IVerticalRuler ruler= getVerticalRuler();
if (ruler instanceof CompositeRuler)
updateContributedRulerColumns((CompositeRuler) ruler);
if (isWordWrapSupported()) {
setWordWrap(getInitialWordWrapStatus());
}
}
/**
* Installs text drag and drop on the given source viewer.
*
* @param viewer the viewer
* @since 3.3
*/
protected void installTextDragAndDrop(final ISourceViewer viewer) {
if (viewer == null || fIsTextDragAndDropInstalled)
return;
final IDragAndDropService dndService= getSite().getService(IDragAndDropService.class);
if (dndService == null)
return;
final StyledText st= viewer.getTextWidget();
// Install drag source
final ISelectionProvider selectionProvider= viewer.getSelectionProvider();
final DragSource source= new DragSource(st, DND.DROP_COPY | DND.DROP_MOVE);
source.setTransfer(new Transfer[] {TextTransfer.getInstance()});
source.addDragListener(new DragSourceAdapter() {
String fSelectedText;
Point fSelection;
@Override
public void dragStart(DragSourceEvent event) {
fTextDragAndDropToken= null;
try {
fSelection= st.getSelection();
event.doit= isLocationSelected(new Point(event.x, event.y));
ISelection selection= selectionProvider.getSelection();
if (selection instanceof ITextSelection)
fSelectedText= ((ITextSelection)selection).getText();
else // fallback to widget
fSelectedText= st.getSelectionText();
} catch (IllegalArgumentException ex) {
event.doit= false;
}
}
private boolean isLocationSelected(Point point) {
// FIXME: https://bugs.eclipse.org/bugs/show_bug.cgi?id=260922
if (isBlockSelectionModeEnabled())
return false;
int offset = st.getOffsetAtPoint(point);
Point p= st.getLocationAtOffset(offset);
if (p.x > point.x)
offset--;
return offset >= fSelection.x && offset < fSelection.y;
}
@Override
public void dragSetData(DragSourceEvent event) {
event.data= fSelectedText;
fTextDragAndDropToken= this; // Can be any non-null object
}
@Override
public void dragFinished(DragSourceEvent event) {
try {
if (event.detail == DND.DROP_MOVE && validateEditorInputState()) {
Point newSelection= st.getSelection();
int length= fSelection.y - fSelection.x;
int delta= 0;
if (newSelection.x < fSelection.x)
delta= length;
st.replaceTextRange(fSelection.x + delta, length, ""); //$NON-NLS-1$
if (fTextDragAndDropToken == null) {
// Move in same editor - end compound change
IRewriteTarget target= getAdapter(IRewriteTarget.class);
if (target != null)
target.endCompoundChange();
}
}
} finally {
fTextDragAndDropToken= null;
}
}
});
// Install drag target
DropTargetListener dropTargetListener= new DropTargetAdapter() {
private Point fSelection;
@Override
public void dragEnter(DropTargetEvent event) {
fTextDragAndDropToken= null;
fSelection= st.getSelection();
if (event.detail == DND.DROP_DEFAULT) {
if ((event.operations & DND.DROP_MOVE) != 0) {
event.detail= DND.DROP_MOVE;
} else if ((event.operations & DND.DROP_COPY) != 0) {
event.detail= DND.DROP_COPY;
} else {
event.detail= DND.DROP_NONE;
}
}
}
@Override
public void dragOperationChanged(DropTargetEvent event) {
if (event.detail == DND.DROP_DEFAULT) {
if ((event.operations & DND.DROP_MOVE) != 0) {
event.detail= DND.DROP_MOVE;
} else if ((event.operations & DND.DROP_COPY) != 0) {
event.detail= DND.DROP_COPY;
} else {
event.detail= DND.DROP_NONE;
}
}
}
@Override
public void dragOver(DropTargetEvent event) {
event.feedback |= DND.FEEDBACK_SCROLL;
}
@Override
public void drop(DropTargetEvent event) {
try {
if (fTextDragAndDropToken != null && event.detail == DND.DROP_MOVE) {
// Move in same editor
int caretOffset= st.getCaretOffset();
if (fSelection.x <= caretOffset && caretOffset <= fSelection.y) {
event.detail= DND.DROP_NONE;
return;
}
// Start compound change
IRewriteTarget target= getAdapter(IRewriteTarget.class);
if (target != null)
target.beginCompoundChange();
}
if (!validateEditorInputState()) {
event.detail= DND.DROP_NONE;
return;
}
String text= (String)event.data;
if (isBlockSelectionModeEnabled()) {
// FIXME fix block selection and DND
// if (fTextDNDColumnSelection != null && fTextDragAndDropToken != null && event.detail == DND.DROP_MOVE) {
// // DND_MOVE within same editor - remove origin before inserting
// Rectangle newSelection= st.getColumnSelection();
// st.replaceColumnSelection(fTextDNDColumnSelection, ""); //$NON-NLS-1$
// st.replaceColumnSelection(newSelection, text);
// st.setColumnSelection(newSelection.x, newSelection.y, newSelection.x + fTextDNDColumnSelection.width - fTextDNDColumnSelection.x, newSelection.y + fTextDNDColumnSelection.height - fTextDNDColumnSelection.y);
// } else {
// Point newSelection= st.getSelection();
// st.insert(text);
// IDocument document= getDocumentProvider().getDocument(getEditorInput());
// int startLine= st.getLineAtOffset(newSelection.x);
// int startColumn= newSelection.x - st.getOffsetAtLine(startLine);
// int endLine= startLine + document.computeNumberOfLines(text);
// int endColumn= startColumn + TextUtilities.indexOf(document.getLegalLineDelimiters(), text, 0)[0];
// st.setColumnSelection(startColumn, startLine, endColumn, endLine);
// }
} else {
Point newSelection= st.getSelection();
try {
int modelOffset= widgetOffset2ModelOffset(viewer, newSelection.x);
viewer.getDocument().replace(modelOffset, 0, text);
} catch (BadLocationException e) {
return;
}
st.setSelectionRange(newSelection.x, text.length());
}
} finally {
fTextDragAndDropToken= null;
}
}
};
dndService.addMergedDropTarget(st, DND.DROP_MOVE | DND.DROP_COPY, new Transfer[] {TextTransfer.getInstance()}, dropTargetListener);
fIsTextDragAndDropInstalled= true;
}
/**
* Uninstalls text drag and drop from the given source viewer.
*
* @param viewer the viewer
* @since 3.3
*/
protected void uninstallTextDragAndDrop(ISourceViewer viewer) {
if (viewer == null || !fIsTextDragAndDropInstalled)
return;
final IDragAndDropService dndService= getSite().getService(IDragAndDropService.class);
if (dndService == null)
return;
StyledText st= viewer.getTextWidget();
dndService.removeMergedDropTarget(st);
DragSource dragSource= (DragSource)st.getData(DND.DRAG_SOURCE_KEY);
if (dragSource != null) {
dragSource.dispose();
st.setData(DND.DRAG_SOURCE_KEY, null);
}
fIsTextDragAndDropInstalled= false;
}
/**
* Tells whether the editor input should be included when adding object
* contributions to this editor's context menu.
* <p>
* This implementation always returns <code>true</code>.
* </p>
*
* @return <code>true</code> if the editor input should be considered
* @since 3.2
*/
protected boolean isEditorInputIncludedInContextMenu() {
return true;
}
/**
* Initializes the activation code trigger.
*
* @since 2.1
*/
private void initializeActivationCodeTrigger() {
fActivationCodeTrigger.install();
fActivationCodeTrigger.setScopes(fKeyBindingScopes);
}
/**
* Initializes the given viewer's font.
*
* @param viewer the viewer
* @since 2.0
*/
private void initializeViewerFont(ISourceViewer viewer) {
boolean isSharedFont= true;
Font font= null;
String symbolicFontName= getSymbolicFontName();
if (symbolicFontName != null)
font= JFaceResources.getFont(symbolicFontName);
else if (fPreferenceStore != null) {
// Backward compatibility
if (fPreferenceStore.contains(JFaceResources.TEXT_FONT) && !fPreferenceStore.isDefault(JFaceResources.TEXT_FONT)) {
FontData data= PreferenceConverter.getFontData(fPreferenceStore, JFaceResources.TEXT_FONT);
if (data != null) {
isSharedFont= false;
font= new Font(viewer.getTextWidget().getDisplay(), data);
}
}
}
if (font == null)
font= JFaceResources.getTextFont();
if (!font.equals(fSourceViewer.getTextWidget().getFont())) {
setFont(viewer, font);
disposeFont();
if (!isSharedFont)
fFont= font;
} else if (!isSharedFont) {
font.dispose();
}
}
/**
* Disposes of the non-shared font.
*
* @since 3.5
*/
private void disposeFont() {
if (fFont != null) {
fFont.dispose();
fFont= null;
}
}
/**
* Sets the font for the given viewer sustaining selection and scroll position.
*
* @param sourceViewer the source viewer
* @param font the font
* @since 2.0
*/
private void setFont(ISourceViewer sourceViewer, Font font) {
if (sourceViewer.getDocument() != null) {
ISelectionProvider provider= sourceViewer.getSelectionProvider();
ISelection selection= provider.getSelection();
int topIndex= sourceViewer.getTopIndex();
StyledText styledText= sourceViewer.getTextWidget();
Control parent= styledText;
if (sourceViewer instanceof ITextViewerExtension) {
ITextViewerExtension extension= (ITextViewerExtension) sourceViewer;
parent= extension.getControl();
}
parent.setRedraw(false);
styledText.setFont(font);
if (fVerticalRuler instanceof IVerticalRulerExtension) {
IVerticalRulerExtension e= (IVerticalRulerExtension) fVerticalRuler;
e.setFont(font);
}
provider.setSelection(selection);
sourceViewer.setTopIndex(topIndex);
if (parent instanceof Composite) {
Composite composite= (Composite) parent;
composite.layout(true);
}
parent.setRedraw(true);
} else {
StyledText styledText= sourceViewer.getTextWidget();
styledText.setFont(font);
if (fVerticalRuler instanceof IVerticalRulerExtension) {
IVerticalRulerExtension e= (IVerticalRulerExtension) fVerticalRuler;
e.setFont(font);
}
}
}
private void initializeZoomGestures(Control rulerControl, final ISourceViewer sourceViewer) {
final StyledText styledText= sourceViewer.getTextWidget();
GestureListener gestureListener= new GestureListener() {
private Font fMagnificationStartFont;
private int fLastHeight= -1;
@Override
public void gesture(GestureEvent e) {
if (e.detail == SWT.GESTURE_BEGIN) {
fMagnificationStartFont= styledText.getFont();
} else if (e.detail == SWT.GESTURE_END) {
fMagnificationStartFont= null;
updateStatusField(ITextEditorActionConstants.STATUS_CATEGORY_INPUT_POSITION);
} else if (e.detail == SWT.GESTURE_ROTATE) {
if (Math.abs(e.rotation) > 45) {
fMagnificationStartFont= null; // don't observe magnify events after reset
initializeViewerFont(fSourceViewer);
updateCaret();
IStatusField statusField= getStatusField(ITextEditorActionConstants.STATUS_CATEGORY_INPUT_POSITION);
if (statusField != null) {
int newHeight= styledText.getFont().getFontData()[0].getHeight();
statusField.setText(NLSUtility.format(EditorMessages.Editor_font_reset_message, Integer.valueOf(newHeight)));
}
}
} else if (e.detail == SWT.GESTURE_MAGNIFY && fMagnificationStartFont != null) {
FontData fontData= fMagnificationStartFont.getFontData()[0];
int startHeight= fontData.getHeight();
int newHeight= Math.max(1, (int) (startHeight * e.magnification));
if (newHeight != fLastHeight) {
fLastHeight= newHeight;
fontData.setHeight(newHeight);
Font newFont= new Font(fMagnificationStartFont.getDevice(), fontData);
setFont(sourceViewer, newFont);
disposeFont();
updateCaret();
IStatusField statusField= getStatusField(ITextEditorActionConstants.STATUS_CATEGORY_INPUT_POSITION);
if (statusField != null) {
statusField.setText(NLSUtility.format(EditorMessages.Editor_font_zoom_message, new Object[] { Integer.valueOf(startHeight), Integer.valueOf(newHeight) }));
}
}
}
}
};
styledText.addGestureListener(gestureListener);
rulerControl.addGestureListener(gestureListener);
}
/**
* Creates a color from the information stored in the given preference store.
* Returns <code>null</code> if there is no such information available.
*
* @param store the store to read from
* @param key the key used for the lookup in the preference store
* @param display the display used create the color
* @return the created color according to the specification in the preference store
* @since 2.0
*/
private Color createColor(IPreferenceStore store, String key, Display display) {
RGB rgb= null;
if (store.contains(key)) {
if (store.isDefault(key))
rgb= PreferenceConverter.getDefaultColor(store, key);
else
rgb= PreferenceConverter.getColor(store, key);
if (rgb != null)
return new Color(display, rgb);
}
return null;
}
/**
* Initializes the fore- and background colors of the given viewer for both
* normal and selected text.
*
* @param viewer the viewer to be initialized
* @since 2.0
*/
protected void initializeViewerColors(ISourceViewer viewer) {
IPreferenceStore store= getPreferenceStore();
if (store != null) {
StyledText styledText= viewer.getTextWidget();
// ----------- foreground color --------------------
Color color= store.getBoolean(PREFERENCE_COLOR_FOREGROUND_SYSTEM_DEFAULT)
? null
: createColor(store, PREFERENCE_COLOR_FOREGROUND, styledText.getDisplay());
styledText.setForeground(color);
if (fForegroundColor != null)
fForegroundColor.dispose();
fForegroundColor= color;
// ---------- background color ----------------------
color= store.getBoolean(PREFERENCE_COLOR_BACKGROUND_SYSTEM_DEFAULT)
? null
: createColor(store, PREFERENCE_COLOR_BACKGROUND, styledText.getDisplay());
styledText.setBackground(color);
if (fBackgroundColor != null)
fBackgroundColor.dispose();
fBackgroundColor= color;
// ----------- selection foreground color --------------------
color= store.getBoolean(PREFERENCE_COLOR_SELECTION_FOREGROUND_SYSTEM_DEFAULT)
? null
: createColor(store, PREFERENCE_COLOR_SELECTION_FOREGROUND, styledText.getDisplay());
styledText.setSelectionForeground(color);
if (fSelectionForegroundColor != null)
fSelectionForegroundColor.dispose();
fSelectionForegroundColor= color;
// ---------- selection background color ----------------------
color= store.getBoolean(PREFERENCE_COLOR_SELECTION_BACKGROUND_SYSTEM_DEFAULT)
? null
: createColor(store, PREFERENCE_COLOR_SELECTION_BACKGROUND, styledText.getDisplay());
styledText.setSelectionBackground(color);
if (fSelectionBackgroundColor != null)
fSelectionBackgroundColor.dispose();
fSelectionBackgroundColor= color;
}
}
/**
* Initializes the background color used for highlighting the document ranges
* defining search scopes.
*
* @param viewer the viewer to initialize
* @since 2.0
*/
private void initializeFindScopeColor(ISourceViewer viewer) {
IPreferenceStore store= getPreferenceStore();
if (store != null) {
StyledText styledText= viewer.getTextWidget();
Color color= createColor(store, PREFERENCE_COLOR_FIND_SCOPE, styledText.getDisplay());
IFindReplaceTarget target= viewer.getFindReplaceTarget();
if (target != null && target instanceof IFindReplaceTargetExtension)
((IFindReplaceTargetExtension) target).setScopeHighlightColor(color);
if (fFindScopeHighlightColor != null)
fFindScopeHighlightColor.dispose();
fFindScopeHighlightColor= color;
}
}
/**
* Initializes the editor's source viewer based on the given editor input.
*
* @param input the editor input to be used to initialize the source viewer
*/
private void initializeSourceViewer(IEditorInput input) {
IDocumentProvider documentProvider= getDocumentProvider();
IAnnotationModel model= documentProvider.getAnnotationModel(input);
IDocument document= documentProvider.getDocument(input);
if (document != null) {
fSourceViewer.setDocument(document, model);
fSourceViewer.setEditable(isEditable());
fSourceViewer.showAnnotations(model != null);
}
if (fElementStateListener instanceof IElementStateListenerExtension) {
boolean isStateValidated= false;
if (documentProvider instanceof IDocumentProviderExtension)
isStateValidated= ((IDocumentProviderExtension)documentProvider).isStateValidated(input);
IElementStateListenerExtension extension= (IElementStateListenerExtension) fElementStateListener;
extension.elementStateValidationChanged(input, isStateValidated);
}
if (fInitialCaret == null)
fInitialCaret= fSourceViewer.getTextWidget().getCaret();
if (fIsOverwriting)
fSourceViewer.getTextWidget().invokeAction(ST.TOGGLE_OVERWRITE);
handleInsertModeChanged();
if (isTabsToSpacesConversionEnabled())
installTabsToSpacesConverter();
if (fSourceViewer instanceof ITextViewerExtension8) {
IPreferenceStore store= getPreferenceStore();
EnrichMode mode= store != null ? convertEnrichModePreference(store.getInt(PREFERENCE_HOVER_ENRICH_MODE)) : EnrichMode.AFTER_DELAY;
((ITextViewerExtension8)fSourceViewer).setHoverEnrichMode(mode);
}
if (fSourceViewer instanceof ISourceViewerExtension5)
installCodeMiningProviders();
}
/**
* Install codemining providers.
*
* @since 3.11
*/
protected void installCodeMiningProviders() {
ICodeMiningProvider[] providers = TextEditorPlugin.getDefault().getCodeMiningProviderRegistry()
.getProviders(this.getSourceViewer(), this);
((ISourceViewerExtension5) fSourceViewer).setCodeMiningProviders(providers);
}
/**
* Converts the {link #PREFERENCE_HOVER_ENRICH_MODE} preference value to
* {@link org.eclipse.jface.text.ITextViewerExtension8.EnrichMode}.
*
* @param mode the preference value
* @return the enrich mode, can be <code>null</code>
* @since 3.4
*/
private EnrichMode convertEnrichModePreference(int mode) {
switch (mode) {
case -1:
return null;
case 0:
return EnrichMode.AFTER_DELAY;
case 1:
return EnrichMode.IMMEDIATELY;
case 2:
return EnrichMode.ON_CLICK;
default:
Assert.isLegal(false);
return null;
}
}
/**
* Initializes the editor's title based on the given editor input.
* <p>
* <strong>Note:</strong> We use the editor's image instead of the image from the
* editor input to distinguish situations where the same editor input is
* opened in different kinds of editors.
* </p>
*
* @param input the editor input to be used
*/
private void initializeTitle(IEditorInput input) {
Image oldImage= fTitleImage;
fTitleImage= null;
String title= ""; //$NON-NLS-1$
if (input != null) {
IEditorRegistry editorRegistry= PlatformUI.getWorkbench().getEditorRegistry();
IEditorDescriptor editorDesc= editorRegistry.findEditor(getSite().getId());
ImageDescriptor imageDesc= editorDesc != null ? editorDesc.getImageDescriptor() : null;
fTitleImage= imageDesc != null ? imageDesc.createImage() : null;
title= input.getName();
}
setTitleImage(fTitleImage);
setPartName(title);
firePropertyChange(PROP_DIRTY);
if (oldImage != null && !oldImage.isDisposed())
oldImage.dispose();
}
/**
* Hook method for setting the document provider for the given input.
* This default implementation does nothing. Clients may
* reimplement.
*
* @param input the input of this editor.
* @since 3.0
*/
protected void setDocumentProvider(IEditorInput input) {
}
/**
* If there is no explicit document provider set, the implicit one is
* re-initialized based on the given editor input.
*
* @param input the editor input.
*/
private void updateDocumentProvider(IEditorInput input) {
IProgressMonitor rememberedProgressMonitor= null;
IDocumentProvider provider= getDocumentProvider();
if (provider != null) {
provider.removeElementStateListener(fElementStateListener);
if (provider instanceof IDocumentProviderExtension2) {
IDocumentProviderExtension2 extension= (IDocumentProviderExtension2) provider;
rememberedProgressMonitor= extension.getProgressMonitor();
extension.setProgressMonitor(null);
}
}
setDocumentProvider(input);
provider= getDocumentProvider();
if (provider != null) {
provider.addElementStateListener(fElementStateListener);
if (provider instanceof IDocumentProviderExtension2) {
IDocumentProviderExtension2 extension= (IDocumentProviderExtension2) provider;
extension.setProgressMonitor(rememberedProgressMonitor);
}
}
}
/**
* Called directly from <code>setInput</code> and from within a workspace
* runnable from <code>init</code>, this method does the actual setting
* of the editor input. Closes the editor if <code>input</code> is
* <code>null</code>. Disconnects from any previous editor input and its
* document provider and connects to the new one.
* <p>
* Subclasses may extend.
* </p>
*
* @param input the input to be set
* @exception CoreException if input cannot be connected to the document
* provider
*/
protected void doSetInput(IEditorInput input) throws CoreException {
ISaveablesLifecycleListener listener= getSite().getService(ISaveablesLifecycleListener.class);
if (listener == null)
fSavable= null;
if (input == null) {
close(isSaveOnCloseNeeded());
if (fSavable != null) {
listener.handleLifecycleEvent(new SaveablesLifecycleEvent(this, SaveablesLifecycleEvent.POST_CLOSE, getSaveables(), false));
fSavable.disconnectEditor();
fSavable= null;
}
} else {
boolean mustSendLifeCycleEvent= false;
if (fSavable != null) {
listener.handleLifecycleEvent(new SaveablesLifecycleEvent(this, SaveablesLifecycleEvent.POST_CLOSE, getSaveables(), false));
fSavable.disconnectEditor();
fSavable= null;
mustSendLifeCycleEvent= true;
}
IEditorInput oldInput= getEditorInput();
if (oldInput != null)
getDocumentProvider().disconnect(oldInput);
super.setInput(input);
updateDocumentProvider(input);
IDocumentProvider provider= getDocumentProvider();
if (provider == null) {
IStatus s= new Status(IStatus.ERROR, PlatformUI.PLUGIN_ID, IStatus.OK, EditorMessages.Editor_error_no_provider, null);
throw new CoreException(s);
}
provider.connect(input);
initializeTitle(input);
if (fSourceViewer != null) {
initializeSourceViewer(input);
// Reset the undo context for the undo and redo action handlers
IAction undoAction= getAction(ITextEditorActionConstants.UNDO);
IAction redoAction= getAction(ITextEditorActionConstants.REDO);
boolean areOperationActionHandlersInstalled= undoAction instanceof OperationHistoryActionHandler && redoAction instanceof OperationHistoryActionHandler;
IUndoContext undoContext= getUndoContext();
if (undoContext != null && areOperationActionHandlersInstalled) {
((OperationHistoryActionHandler)undoAction).setContext(undoContext);
((OperationHistoryActionHandler)redoAction).setContext(undoContext);
} else {
createUndoRedoActions();
}
}
if (fIsOverwriting)
toggleOverwriteMode();
setInsertMode(getLegalInsertModes().get(0));
updateCaret();
updateStatusField(ITextEditorActionConstants.STATUS_CATEGORY_ELEMENT_STATE);
if (fSelectionListener != null)
fSelectionListener.setDocument(getDocumentProvider().getDocument(input));
IVerticalRuler ruler= getVerticalRuler();
if (ruler instanceof CompositeRuler)
updateContributedRulerColumns((CompositeRuler) ruler);
// Send savable life-cycle if needed.
if (mustSendLifeCycleEvent && listener != null)
listener.handleLifecycleEvent(new SaveablesLifecycleEvent(this, SaveablesLifecycleEvent.POST_OPEN, getSaveables(), false));
}
}
/**
* Returns this editor's viewer's undo manager undo context.
*
* @return the undo context or <code>null</code> if not available
* @since 3.1
*/
private IUndoContext getUndoContext() {
if (fSourceViewer instanceof ITextViewerExtension6) {
IUndoManager undoManager= ((ITextViewerExtension6)fSourceViewer).getUndoManager();
if (undoManager instanceof IUndoManagerExtension)
return ((IUndoManagerExtension)undoManager).getUndoContext();
}
return null;
}
@Override
protected final void setInputWithNotify(IEditorInput input) {
try {
doSetInput(input);
/*
* The following bugs explain why we fire this property change:
* https://bugs.eclipse.org/bugs/show_bug.cgi?id=90283
* https://bugs.eclipse.org/bugs/show_bug.cgi?id=92049
* https://bugs.eclipse.org/bugs/show_bug.cgi?id=92286
*/
firePropertyChange(IEditorPart.PROP_INPUT);
} catch (CoreException x) {
String title= EditorMessages.Editor_error_setinput_title;
String msg= EditorMessages.Editor_error_setinput_message;
Shell shell= getSite().getShell();
ErrorDialog.openError(shell, title, msg, x.getStatus());
}
}
@Override
public final void setInput(IEditorInput input) {
setInputWithNotify(input);
}
/*
* @see ITextEditor#close
*/
@Override
public void close(final boolean save) {
enableSanityChecking(false);
Display display= getSite().getShell().getDisplay();
display.asyncExec(() -> {
if (fSourceViewer != null)
getSite().getPage().closeEditor(AbstractTextEditor.this, save);
});
}
/**
* The <code>AbstractTextEditor</code> implementation of this
* <code>IWorkbenchPart</code> method may be extended by subclasses.
* Subclasses must call <code>super.dispose()</code>.
* <p>
* Note that many methods may return <code>null</code> after the editor is
* disposed.
* </p>
*/
@Override
public void dispose() {
if (fActivationListener != null) {
fActivationListener.dispose();
fActivationListener= null;
}
if (fTitleImage != null) {
fTitleImage.dispose();
fTitleImage= null;
}
disposeFont();
disposeNonDefaultCaret();
fInitialCaret= null;
if (fForegroundColor != null) {
fForegroundColor.dispose();
fForegroundColor= null;
}
if (fBackgroundColor != null) {
fBackgroundColor.dispose();
fBackgroundColor= null;
}
if (fSelectionForegroundColor != null) {
fSelectionForegroundColor.dispose();
fSelectionForegroundColor= null;
}
if (fSelectionBackgroundColor != null) {
fSelectionBackgroundColor.dispose();
fSelectionBackgroundColor= null;
}
if (fFindScopeHighlightColor != null) {
fFindScopeHighlightColor.dispose();
fFindScopeHighlightColor= null;
}
if (fFontPropertyChangeListener != null) {
JFaceResources.getFontRegistry().removeListener(fFontPropertyChangeListener);
if (fPreferenceStore != null)
fPreferenceStore.removePropertyChangeListener(fFontPropertyChangeListener);
fFontPropertyChangeListener= null;
}
if (fPropertyChangeListener != null) {
if (fPreferenceStore != null) {
fPreferenceStore.removePropertyChangeListener(fPropertyChangeListener);
fPreferenceStore= null;
}
fPropertyChangeListener= null;
}
if (fActivationCodeTrigger != null) {
fActivationCodeTrigger.uninstall();
fActivationCodeTrigger= null;
}
if (fSelectionListener != null) {
fSelectionListener.uninstall(getSelectionProvider());
fSelectionListener= null;
}
if (fSavable != null) {
fSavable.disconnectEditor();
fSavable= null;
}
disposeDocumentProvider();
if (fSourceViewer != null) {
if (fTextListener != null) {
fSourceViewer.removeTextListener(fTextListener);
fSourceViewer.removeTextInputListener(fTextListener);
fTextListener= null;
}
uninstallTabsToSpacesConverter();
fTextInputListener= null;
fSelectionProvider= null;
fSourceViewer= null;
}
if (fTextContextMenu != null) {
fTextContextMenu.dispose();
fTextContextMenu= null;
}
if (fRulerContextMenu != null) {
fRulerContextMenu.dispose();
fRulerContextMenu= null;
}
if (fActions != null) {
registerUndoRedoAction(ITextEditorActionConstants.UNDO, null);
registerUndoRedoAction(ITextEditorActionConstants.REDO, null);
fActions.clear();
fActions= null;
}
if (fSelectionActions != null) {
fSelectionActions.clear();
fSelectionActions= null;
}
if (fContentActions != null) {
fContentActions.clear();
fContentActions= null;
}
if (fPropertyActions != null) {
fPropertyActions.clear();
fPropertyActions= null;
}
if (fStateActions != null) {
fStateActions.clear();
fStateActions= null;
}
if (fActivationCodes != null) {
fActivationCodes.clear();
fActivationCodes= null;
}
if (fEditorStatusLine != null)
fEditorStatusLine= null;
if (fConfiguration != null)
fConfiguration= null;
if (fColumnSupport != null) {
fColumnSupport.dispose();
fColumnSupport= null;
}
if (fVerticalRuler != null)
fVerticalRuler= null;
IOperationHistory history= OperationHistoryFactory.getOperationHistory();
if (history != null) {
if (fNonLocalOperationApprover != null)
history.removeOperationApprover(fNonLocalOperationApprover);
if (fLinearUndoViolationApprover != null)
history.removeOperationApprover(fLinearUndoViolationApprover);
}
fNonLocalOperationApprover= null;
fLinearUndoViolationApprover= null;
if (fKeyBindingSupportForContentAssistant != null) {
fKeyBindingSupportForContentAssistant.dispose();
fKeyBindingSupportForContentAssistant= null;
}
if (fKeyBindingSupportForQuickAssistant != null) {
fKeyBindingSupportForQuickAssistant.dispose();
fKeyBindingSupportForQuickAssistant= null;
}
if (fInformationPresenter != null) {
fInformationPresenter.uninstall();
fInformationPresenter= null;
}
if (fSaveAction != null) {
fSaveAction.dispose();
fSaveAction= null;
}
super.dispose();
}
/**
* Disposes of the connection with the document provider. Subclasses
* may extend.
*
* @since 3.0
*/
protected void disposeDocumentProvider() {
IDocumentProvider provider= getDocumentProvider();
if (provider != null) {
IEditorInput input= getEditorInput();
if (input != null)
provider.disconnect(input);
if (fElementStateListener != null) {
provider.removeElementStateListener(fElementStateListener);
fElementStateListener= null;
}
}
fExplicitDocumentProvider= null;
}
/**
* Determines whether the given preference change affects the editor's
* presentation. This implementation always returns <code>false</code>.
* May be reimplemented by subclasses.
*
* @param event the event which should be investigated
* @return <code>true</code> if the event describes a preference change affecting the editor's presentation
* @since 2.0
*/
protected boolean affectsTextPresentation(PropertyChangeEvent event) {
return false;
}
/**
* Returns the symbolic font name for this editor as defined in XML.
*
* @return a String with the symbolic font name or <code>null</code> if
* none is defined
* @since 2.1
*/
/*package*/ String getSymbolicFontName() {
if (getConfigurationElement() != null)
return getConfigurationElement().getAttribute("symbolicFontName"); //$NON-NLS-1$
return null;
}
/**
* Returns the property preference key for the editor font.
* <p>
* If the editor is defined with a <code>symbolicFontName </code> then this name is returned and
* the font is looked up in the JFace resource registry. Otherwise,
* {@link JFaceResources#TEXT_FONT} is returned and the font is looked up in this editor's
* preference store.
* </p>
*
* @return a String with the key
* @since 2.1
*/
protected final String getFontPropertyPreferenceKey() {
String symbolicFontName= getSymbolicFontName();
if (symbolicFontName != null)
return symbolicFontName;
return JFaceResources.TEXT_FONT;
}
/**
* Handles a property change event describing a change of the editor's
* preference store and updates the preference related editor properties.
* <p>
* Subclasses may extend.
* </p>
*
* @param event the property change event
*/
protected void handlePreferenceStoreChanged(PropertyChangeEvent event) {
if (fSourceViewer == null)
return;
String property= event.getProperty();
if (getFontPropertyPreferenceKey().equals(property))
// There is a separate handler for font preference changes
return;
if (PREFERENCE_COLOR_FOREGROUND.equals(property) || PREFERENCE_COLOR_FOREGROUND_SYSTEM_DEFAULT.equals(property) ||
PREFERENCE_COLOR_BACKGROUND.equals(property) || PREFERENCE_COLOR_BACKGROUND_SYSTEM_DEFAULT.equals(property) ||
PREFERENCE_COLOR_SELECTION_FOREGROUND.equals(property) || PREFERENCE_COLOR_SELECTION_FOREGROUND_SYSTEM_DEFAULT.equals(property) ||
PREFERENCE_COLOR_SELECTION_BACKGROUND.equals(property) || PREFERENCE_COLOR_SELECTION_BACKGROUND_SYSTEM_DEFAULT.equals(property))
{
initializeViewerColors(fSourceViewer);
} else if (PREFERENCE_COLOR_FIND_SCOPE.equals(property)) {
initializeFindScopeColor(fSourceViewer);
} else if (PREFERENCE_USE_CUSTOM_CARETS.equals(property)) {
updateCaret();
} else if (PREFERENCE_WIDE_CARET.equals(property)) {
updateCaret();
}
if (affectsTextPresentation(event))
fSourceViewer.invalidateTextPresentation();
if (PREFERENCE_HYPERLINKS_ENABLED.equals(property)) {
if (fSourceViewer instanceof ITextViewerExtension6) {
IHyperlinkDetector[] detectors= getSourceViewerConfiguration().getHyperlinkDetectors(fSourceViewer);
int stateMask= getSourceViewerConfiguration().getHyperlinkStateMask(fSourceViewer);
ITextViewerExtension6 textViewer6= (ITextViewerExtension6)fSourceViewer;
textViewer6.setHyperlinkDetectors(detectors, stateMask);
}
return;
}
if (PREFERENCE_HYPERLINK_KEY_MODIFIER.equals(property)) {
if (fSourceViewer instanceof ITextViewerExtension6) {
ITextViewerExtension6 textViewer6= (ITextViewerExtension6)fSourceViewer;
IHyperlinkDetector[] detectors= getSourceViewerConfiguration().getHyperlinkDetectors(fSourceViewer);
int stateMask= getSourceViewerConfiguration().getHyperlinkStateMask(fSourceViewer);
textViewer6.setHyperlinkDetectors(detectors, stateMask);
}
return;
}
if (PREFERENCE_RULER_CONTRIBUTIONS.equals(property)) {
String[] difference= StringSetSerializer.getDifference((String) event.getOldValue(), (String) event.getNewValue());
IColumnSupport support= getAdapter(IColumnSupport.class);
for (int i= 0; i < difference.length; i++) {
RulerColumnDescriptor desc= RulerColumnRegistry.getDefault().getColumnDescriptor(difference[i]);
if (desc != null && support.isColumnSupported(desc)) {
boolean newState= !support.isColumnVisible(desc);
support.setColumnVisible(desc, newState);
}
}
return;
}
if (PREFERENCE_SHOW_WHITESPACE_CHARACTERS.equals(property) ||
PREFERENCE_SHOW_LEADING_SPACES.equals(property) ||
PREFERENCE_SHOW_ENCLOSED_SPACES.equals(property) ||
PREFERENCE_SHOW_TRAILING_SPACES.equals(property) ||
PREFERENCE_SHOW_LEADING_IDEOGRAPHIC_SPACES.equals(property) ||
PREFERENCE_SHOW_ENCLOSED_IDEOGRAPHIC_SPACES.equals(property) ||
PREFERENCE_SHOW_TRAILING_IDEOGRAPHIC_SPACES.equals(property) ||
PREFERENCE_SHOW_LEADING_TABS.equals(property) ||
PREFERENCE_SHOW_ENCLOSED_TABS.equals(property) ||
PREFERENCE_SHOW_TRAILING_TABS.equals(property) ||
PREFERENCE_SHOW_CARRIAGE_RETURN.equals(property) ||
PREFERENCE_SHOW_LINE_FEED.equals(property) ||
PREFERENCE_WHITESPACE_CHARACTER_ALPHA_VALUE.equals(property)) {
IAction action= getAction(ITextEditorActionConstants.SHOW_WHITESPACE_CHARACTERS);
if (action instanceof IUpdate)
((IUpdate)action).update();
return;
}
if (PREFERENCE_TEXT_DRAG_AND_DROP_ENABLED.equals(property)) {
IPreferenceStore store= getPreferenceStore();
if (store != null && store.getBoolean(PREFERENCE_TEXT_DRAG_AND_DROP_ENABLED))
installTextDragAndDrop(getSourceViewer());
else
uninstallTextDragAndDrop(getSourceViewer());
return;
}
if (PREFERENCE_HOVER_ENRICH_MODE.equals(property)) {
if (fSourceViewer instanceof ITextViewerExtension8) {
IPreferenceStore store= getPreferenceStore();
if (store != null) {
((ITextViewerExtension8)fSourceViewer).setHoverEnrichMode(convertEnrichModePreference(store.getInt(PREFERENCE_HOVER_ENRICH_MODE)));
}
}
return;
}
}
/**
* Returns the progress monitor related to this editor. It should not be
* necessary to extend this method.
*
* @return the progress monitor related to this editor
* @since 2.1
*/
protected IProgressMonitor getProgressMonitor() {
IProgressMonitor pm= null;
IStatusLineManager manager= getStatusLineManager();
if (manager != null)
pm= manager.getProgressMonitor();
return pm != null ? pm : new NullProgressMonitor();
}
/**
* Handles an external change of the editor's input element. Subclasses may
* extend.
*/
protected void handleEditorInputChanged() {
String title;
String msg;
Shell shell= getSite().getShell();
final IDocumentProvider provider= getDocumentProvider();
if (provider == null) {
// fix for http://dev.eclipse.org/bugs/show_bug.cgi?id=15066
close(false);
return;
}
final IEditorInput input= getEditorInput();
final String inputName= input.getToolTipText();
if (provider.isDeleted(input)) {
if (isSaveAsAllowed()) {
title= EditorMessages.Editor_error_activated_deleted_save_title;
msg= NLSUtility.format(EditorMessages.Editor_error_activated_deleted_save_message, inputName);
String[] buttons= {
EditorMessages.Editor_error_activated_deleted_save_button_save,
EditorMessages.Editor_error_activated_deleted_save_button_close,
};
MessageDialog dialog= new MessageDialog(shell, title, null, msg, MessageDialog.QUESTION, buttons, 0);
if (dialog.open() == 0) {
IProgressMonitor pm= getProgressMonitor();
try {
performSaveAs(pm);
if (pm.isCanceled())
handleEditorInputChanged();
} finally {
pm.done();
}
} else {
close(false);
}
} else {
title= EditorMessages.Editor_error_activated_deleted_close_title;
msg= NLSUtility.format(EditorMessages.Editor_error_activated_deleted_close_message, inputName);
if (MessageDialog.openConfirm(shell, title, msg))
close(false);
}
} else if (fHasBeenActivated) {
title= EditorMessages.Editor_error_activated_outofsync_title;
msg= NLSUtility.format(EditorMessages.Editor_error_activated_outofsync_message, inputName);
if (MessageDialog.open(MessageDialog.QUESTION, shell, title, msg, SWT.NONE,
new String[] { EditorMessages.Editor_error_replace_button_label,
EditorMessages.Editor_error_dontreplace_button_label }) == 0) {
try {
if (provider instanceof IDocumentProviderExtension) {
IDocumentProviderExtension extension= (IDocumentProviderExtension) provider;
extension.synchronize(input);
} else {
doSetInput(input);
}
} catch (CoreException x) {
IStatus status= x.getStatus();
if (status == null || status.getSeverity() != IStatus.CANCEL) {
title= EditorMessages.Editor_error_refresh_outofsync_title;
msg= NLSUtility.format(EditorMessages.Editor_error_refresh_outofsync_message, inputName);
ErrorDialog.openError(shell, title, msg, x.getStatus());
}
}
} else if (!isDirty()) {
// Trigger dummy change to dirty the editor, for details see https://bugs.eclipse.org/344101 .
try {
IDocument document= provider.getDocument(input);
if (document != null)
document.replace(0, 0, ""); //$NON-NLS-1$
} catch (BadLocationException e) {
// Ignore as this can't happen
}
}
}
}
/**
* The <code>AbstractTextEditor</code> implementation of this
* <code>IEditorPart</code> method calls <code>performSaveAs</code>.
* Subclasses may reimplement.
*/
@Override
public void doSaveAs() {
IProgressMonitor monitor= getProgressMonitor();
try {
/*
* 1GEUSSR: ITPUI:ALL - User should never loose changes made in the editors.
* Changed Behavior to make sure that if called inside a regular save (because
* of deletion of input element) there is a way to report back to the caller.
*/
performSaveAs(monitor);
} finally {
monitor.done();
}
}
/**
* Performs a save as and reports the result state back to the
* given progress monitor. This default implementation does nothing.
* Subclasses may reimplement.
*
* @param progressMonitor the progress monitor for communicating result state or <code>null</code>
*/
protected void performSaveAs(IProgressMonitor progressMonitor) {
}
/**
* The <code>AbstractTextEditor</code> implementation of this
* <code>IEditorPart</code> method may be extended by subclasses.
*
* @param progressMonitor the progress monitor for communicating result state or <code>null</code>
*/
@Override
public void doSave(IProgressMonitor progressMonitor) {
IDocumentProvider p= getDocumentProvider();
if (p == null)
return;
if (p.isDeleted(getEditorInput())) {
if (isSaveAsAllowed()) {
/*
* 1GEUSSR: ITPUI:ALL - User should never loose changes made in the editors.
* Changed Behavior to make sure that if called inside a regular save (because
* of deletion of input element) there is a way to report back to the caller.
*/
performSaveAs(progressMonitor);
} else {
Shell shell= getSite().getShell();
String title= EditorMessages.Editor_error_save_deleted_title;
String msg= EditorMessages.Editor_error_save_deleted_message;
MessageDialog.openError(shell, title, msg);
}
} else {
updateState(getEditorInput());
validateState(getEditorInput());
performSave(false, progressMonitor);
}
}
/**
* Enables/disables sanity checking.
* @param enable <code>true</code> if sanity checking should be enabled, <code>false</code> otherwise
* @since 2.0
*/
protected void enableSanityChecking(boolean enable) {
synchronized (this) {
fIsSanityCheckEnabled= enable;
}
}
/**
* Checks the state of the given editor input if sanity checking is enabled.
* @param input the editor input whose state is to be checked
* @since 2.0
*/
protected void safelySanityCheckState(IEditorInput input) {
boolean enabled= false;
synchronized (this) {
enabled= fIsSanityCheckEnabled;
}
if (enabled)
sanityCheckState(input);
}
/**
* Checks the state of the given editor input.
* @param input the editor input whose state is to be checked
* @since 2.0
*/
protected void sanityCheckState(IEditorInput input) {
IDocumentProvider p= getDocumentProvider();
if (p == null)
return;
if (p instanceof IDocumentProviderExtension3) {
IDocumentProviderExtension3 p3= (IDocumentProviderExtension3) p;
long stamp= p.getModificationStamp(input);
if (stamp != fModificationStamp) {
fModificationStamp= stamp;
if (!p3.isSynchronized(input))
handleEditorInputChanged();
}
} else {
if (fModificationStamp == -1)
fModificationStamp= p.getSynchronizationStamp(input);
long stamp= p.getModificationStamp(input);
if (stamp != fModificationStamp) {
fModificationStamp= stamp;
if (stamp != p.getSynchronizationStamp(input))
handleEditorInputChanged();
}
}
updateState(getEditorInput());
updateStatusField(ITextEditorActionConstants.STATUS_CATEGORY_ELEMENT_STATE);
}
/**
* Enables/disables state validation.
* @param enable <code>true</code> if state validation should be enabled, <code>false</code> otherwise
* @since 2.1
*/
protected void enableStateValidation(boolean enable) {
synchronized (this) {
fIsStateValidationEnabled= enable;
}
}
/**
* Validates the state of the given editor input. The predominate intent
* of this method is to take any action probably necessary to ensure that
* the input can persistently be changed.
*
* @param input the input to be validated
* @since 2.0
*/
protected void validateState(IEditorInput input) {
IDocumentProvider provider= getDocumentProvider();
if (! (provider instanceof IDocumentProviderExtension))
return;
IDocumentProviderExtension extension= (IDocumentProviderExtension) provider;
try {
extension.validateState(input, getSite().getShell());
} catch (CoreException x) {
IStatus status= x.getStatus();
if (status == null || status.getSeverity() != IStatus.CANCEL) {
Bundle bundle= Platform.getBundle(PlatformUI.PLUGIN_ID);
ILog log= Platform.getLog(bundle);
log.log(x.getStatus());
Shell shell= getSite().getShell();
String title= EditorMessages.Editor_error_validateEdit_title;
String msg= EditorMessages.Editor_error_validateEdit_message;
ErrorDialog.openError(shell, title, msg, x.getStatus());
}
return;
}
if (fSourceViewer != null)
fSourceViewer.setEditable(isEditable());
updateStateDependentActions();
}
@Override
public boolean validateEditorInputState() {
boolean enabled= false;
synchronized (this) {
enabled= fIsStateValidationEnabled;
}
if (enabled) {
ISourceViewer viewer= fSourceViewer;
if (viewer == null)
return false;
fTextInputListener.inputChanged= false;
viewer.addTextInputListener(fTextInputListener);
try {
final IEditorInput input= getEditorInput();
BusyIndicator.showWhile(getSite().getShell().getDisplay(), () -> validateState(input));
sanityCheckState(input);
return !isEditorInputReadOnly() && !fTextInputListener.inputChanged;
} finally {
viewer.removeTextInputListener(fTextInputListener);
}
}
return !isEditorInputReadOnly();
}
/**
* Updates the state of the given editor input such as read-only flag.
*
* @param input the input to be validated
* @since 2.0
*/
protected void updateState(IEditorInput input) {
IDocumentProvider provider= getDocumentProvider();
if (provider instanceof IDocumentProviderExtension) {
IDocumentProviderExtension extension= (IDocumentProviderExtension) provider;
try {
boolean wasReadOnly= isEditorInputReadOnly();
extension.updateStateCache(input);
if (fSourceViewer != null)
fSourceViewer.setEditable(isEditable());
if (wasReadOnly != isEditorInputReadOnly())
updateStateDependentActions();
} catch (CoreException x) {
Bundle bundle= Platform.getBundle(PlatformUI.PLUGIN_ID);
ILog log= Platform.getLog(bundle);
log.log(x.getStatus());
}
}
}
/**
* Performs the save and handles errors appropriately.
*
* @param overwrite indicates whether or not overwriting is allowed
* @param progressMonitor the monitor in which to run the operation
* @since 3.0
*/
protected void performSave(boolean overwrite, IProgressMonitor progressMonitor) {
IDocumentProvider provider= getDocumentProvider();
if (provider == null)
return;
fHandleActivation= false;
try {
provider.aboutToChange(getEditorInput());
IEditorInput input= getEditorInput();
provider.saveDocument(progressMonitor, input, getDocumentProvider().getDocument(input), overwrite);
editorSaved();
} catch (CoreException x) {
IStatus status= x.getStatus();
if (status == null || status.getSeverity() != IStatus.CANCEL)
handleExceptionOnSave(x, progressMonitor);
} finally {
provider.changed(getEditorInput());
fHandleActivation= true;
}
}
/**
* Handles the given exception. If the exception reports an out-of-sync
* situation, this is reported to the user. Otherwise, the exception
* is generically reported.
*
* @param exception the exception to handle
* @param progressMonitor the progress monitor
*/
protected void handleExceptionOnSave(CoreException exception, IProgressMonitor progressMonitor) {
try {
++fErrorCorrectionOnSave;
boolean isSynchronized= false;
IDocumentProvider p= getDocumentProvider();
if (p instanceof IDocumentProviderExtension3) {
IDocumentProviderExtension3 p3= (IDocumentProviderExtension3) p;
isSynchronized= p3.isSynchronized(getEditorInput());
} else if (p != null) {
long modifiedStamp= p.getModificationStamp(getEditorInput());
long synchStamp= p.getSynchronizationStamp(getEditorInput());
isSynchronized= (modifiedStamp == synchStamp);
}
if (isNotSynchronizedException(exception) && fErrorCorrectionOnSave == 1 && !isSynchronized) {
String title= EditorMessages.Editor_error_save_outofsync_title;
String msg= NLSUtility.format(EditorMessages.Editor_error_save_outofsync_message, getEditorInput().getToolTipText());
if (MessageDialog.openQuestion(getSite().getShell(), title, msg))
performSave(true, progressMonitor);
else {
/*
* 1GEUPKR: ITPJUI:ALL - Loosing work with simultaneous edits
* Set progress monitor to canceled in order to report back
* to enclosing operations.
*/
if (progressMonitor != null)
progressMonitor.setCanceled(true);
}
} else {
String title= EditorMessages.Editor_error_save_title;
String msg= EditorMessages.Editor_error_save_message;
openSaveErrorDialog(title, msg, exception);
/*
* 1GEUPKR: ITPJUI:ALL - Loosing work with simultaneous edits
* Set progress monitor to canceled in order to report back
* to enclosing operations.
*/
if (progressMonitor != null)
progressMonitor.setCanceled(true);
}
} finally {
--fErrorCorrectionOnSave;
}
}
/**
* Presents an error dialog to the user when a problem
* happens during save.
* <p>
* Subclasses can decide to override the given title and message.
* </p>
*
* @param title the dialog title
* @param message the message to display
* @param exception the exception to handle
* @since 3.3
*/
protected void openSaveErrorDialog(String title, String message, CoreException exception) {
ErrorDialog.openError(getSite().getShell(), title, message, exception.getStatus());
}
/**
* Tells whether the given core exception is exactly the
* exception which is thrown for a non-synchronized element.
*
* @param ex the core exception
* @return <code>true</code> iff the given core exception is exactly the
* exception which is thrown for a non-synchronized element
* @since 3.1
*/
private boolean isNotSynchronizedException(CoreException ex) {
IDocumentProvider provider= getDocumentProvider();
if (provider instanceof IDocumentProviderExtension5)
return ((IDocumentProviderExtension5)provider).isNotSynchronizedException(getEditorInput(), ex);
return false;
}
/**
* The <code>AbstractTextEditor</code> implementation of this
* <code>IEditorPart</code> method returns <code>false</code>.
* Subclasses may override.
*
* @return <code>false</code>
*/
@Override
public boolean isSaveAsAllowed() {
return false;
}
@Override
public boolean isDirty() {
IDocumentProvider p= getDocumentProvider();
return p == null ? false : p.canSaveDocument(getEditorInput());
}
/**
* The <code>AbstractTextEditor</code> implementation of this
* <code>ITextEditor</code> method may be extended by subclasses.
*/
@Override
public void doRevertToSaved() {
IDocumentProvider p= getDocumentProvider();
if (p == null)
return;
performRevert();
}
/**
* Performs revert and handles errors appropriately.
* <p>
* Subclasses may extend.
* </p>
*
* @since 3.0
*/
protected void performRevert() {
IDocumentProvider provider= getDocumentProvider();
if (provider == null)
return;
try {
provider.aboutToChange(getEditorInput());
provider.resetDocument(getEditorInput());
editorSaved();
} catch (CoreException x) {
IStatus status= x.getStatus();
if (status == null || status.getSeverity() != IStatus.CANCEL ) {
Shell shell= getSite().getShell();
String title= EditorMessages.Editor_error_revert_title;
String msg= EditorMessages.Editor_error_revert_message;
ErrorDialog.openError(shell, title, msg, x.getStatus());
}
} finally {
provider.changed(getEditorInput());
}
}
/**
* Performs any additional action necessary to perform after the input
* document's content has been replaced.
* <p>
* Clients may extended this method.
*
* @since 3.0
*/
protected void handleElementContentReplaced() {
}
@Override
public void setAction(String actionID, IAction action) {
Assert.isNotNull(actionID);
if (action == null) {
action= fActions.remove(actionID);
if (action != null)
fActivationCodeTrigger.unregisterActionFromKeyActivation(action);
} else {
if (action.getId() == null)
action.setId(actionID); // make sure the action ID has been set
fActions.put(actionID, action);
fActivationCodeTrigger.registerActionForKeyActivation(action);
}
}
/**
* Sets this editor's actions into activated (default) or deactived state.
* <p>
* XXX: This is called by the Java editor for its breadcrumb feature. We
* don't want to make this risky method API because the Java editor
* breadcrumb might become a Platform UI feature during 3.5 and hence we can
* then delete this workaround.
* </p>
*
* @param state <code>true</code> if activated
* @since 3.4
*/
private void setActionActivation(boolean state) {
if (state) {
fActivationCodeTrigger.install();
Iterator<IAction> iter= fActions.values().iterator();
while (iter.hasNext()) {
IAction action= iter.next();
if (action != null)
fActivationCodeTrigger.registerActionForKeyActivation(action);
}
getEditorSite().getActionBarContributor().setActiveEditor(this);
} else {
getEditorSite().getActionBarContributor().setActiveEditor(null);
Iterator<IAction> iter= fActions.values().iterator();
while (iter.hasNext()) {
IAction action= iter.next();
if (action != null)
fActivationCodeTrigger.unregisterActionFromKeyActivation(action);
}
fActivationCodeTrigger.uninstall();
}
}
private static final boolean HACK_TO_SUPPRESS_UNUSUED_WARNING= false;
{
if (HACK_TO_SUPPRESS_UNUSUED_WARNING)
setActionActivation(true);
}
@Override
public void setActionActivationCode(String actionID, char activationCharacter, int activationKeyCode, int activationStateMask) {
Assert.isNotNull(actionID);
ActionActivationCode found= findActionActivationCode(actionID);
if (found == null) {
found= new ActionActivationCode(actionID);
fActivationCodes.add(found);
}
found.fCharacter= activationCharacter;
found.fKeyCode= activationKeyCode;
found.fStateMask= activationStateMask;
}
/**
* Returns the activation code registered for the specified action.
*
* @param actionID the action id
* @return the registered activation code or <code>null</code> if no code has been installed
*/
private ActionActivationCode findActionActivationCode(String actionID) {
int size= fActivationCodes.size();
for (int i= 0; i < size; i++) {
ActionActivationCode code= fActivationCodes.get(i);
if (actionID.equals(code.fActionId))
return code;
}
return null;
}
@Override
public void removeActionActivationCode(String actionID) {
Assert.isNotNull(actionID);
ActionActivationCode code= findActionActivationCode(actionID);
if (code != null)
fActivationCodes.remove(code);
}
@Override
public IAction getAction(String actionID) {
Assert.isNotNull(actionID);
IAction action= fActions.get(actionID);
if (action == null) {
action= findContributedAction(actionID);
if (action != null)
setAction(actionID, action);
}
return action;
}
/**
* Returns the action with the given action id that has been contributed via XML to this editor.
* The lookup honors the dependencies of plug-ins.
*
* @param actionID the action id to look up
* @return the action that has been contributed
* @since 2.0
*/
private IAction findContributedAction(String actionID) {
List<IConfigurationElement> actions= new ArrayList<>();
IConfigurationElement[] elements= Platform.getExtensionRegistry().getConfigurationElementsFor(PlatformUI.PLUGIN_ID, "editorActions"); //$NON-NLS-1$
for (int i= 0; i < elements.length; i++) {
IConfigurationElement element= elements[i];
if (TAG_CONTRIBUTION_TYPE.equals(element.getName())) {
IWorkbenchPartSite site = getSite();
if (site == null) {
return null;
}
if (!site.getId().equals(element.getAttribute("targetID"))) //$NON-NLS-1$
continue;
IConfigurationElement[] children= element.getChildren("action"); //$NON-NLS-1$
for (int j= 0; j < children.length; j++) {
IConfigurationElement child= children[j];
if (actionID.equals(child.getAttribute("actionID"))) //$NON-NLS-1$
actions.add(child);
}
}
}
int actionSize= actions.size();
if (actionSize > 0) {
IConfigurationElement element;
if (actionSize > 1) {
IConfigurationElement[] actionArray= actions.toArray(new IConfigurationElement[actionSize]);
ConfigurationElementSorter sorter= new ConfigurationElementSorter() {
@Override
public IConfigurationElement getConfigurationElement(Object object) {
return (IConfigurationElement)object;
}
};
sorter.sort(actionArray);
element= actionArray[0];
} else
element= actions.get(0);
try {
return new ContributedAction(getSite(), element);
} catch (CommandNotMappedException e) {
// out of luck, no command action mapping
}
}
return null;
}
/**
* Updates the specified action by calling <code>IUpdate.update</code>
* if applicable.
*
* @param actionId the action id
*/
private void updateAction(String actionId) {
Assert.isNotNull(actionId);
if (fActions != null) {
IAction action= fActions.get(actionId);
if (action instanceof IUpdate)
((IUpdate) action).update();
}
}
/**
* Marks or unmarks the given action to be updated on text selection changes.
*
* @param actionId the action id
* @param mark <code>true</code> if the action is selection dependent
*/
public void markAsSelectionDependentAction(String actionId, boolean mark) {
Assert.isNotNull(actionId);
if (mark) {
if (!fSelectionActions.contains(actionId))
fSelectionActions.add(actionId);
} else
fSelectionActions.remove(actionId);
}
/**
* Marks or unmarks the given action to be updated on content changes.
*
* @param actionId the action id
* @param mark <code>true</code> if the action is content dependent
*/
public void markAsContentDependentAction(String actionId, boolean mark) {
Assert.isNotNull(actionId);
if (mark) {
if (!fContentActions.contains(actionId))
fContentActions.add(actionId);
} else
fContentActions.remove(actionId);
}
/**
* Marks or unmarks the given action to be updated on property changes.
*
* @param actionId the action id
* @param mark <code>true</code> if the action is property dependent
* @since 2.0
*/
public void markAsPropertyDependentAction(String actionId, boolean mark) {
Assert.isNotNull(actionId);
if (mark) {
if (!fPropertyActions.contains(actionId))
fPropertyActions.add(actionId);
} else
fPropertyActions.remove(actionId);
}
/**
* Marks or unmarks the given action to be updated on state changes.
*
* @param actionId the action id
* @param mark <code>true</code> if the action is state dependent
* @since 2.0
*/
public void markAsStateDependentAction(String actionId, boolean mark) {
Assert.isNotNull(actionId);
if (mark) {
if (!fStateActions.contains(actionId))
fStateActions.add(actionId);
} else
fStateActions.remove(actionId);
}
/**
* Updates all selection dependent actions.
*/
protected void updateSelectionDependentActions() {
if (fSelectionActions != null) {
Iterator<String> e= fSelectionActions.iterator();
while (e.hasNext())
updateAction(e.next());
}
}
/**
* Updates all content dependent actions.
*/
protected void updateContentDependentActions() {
if (fContentActions != null) {
Iterator<String> e= fContentActions.iterator();
while (e.hasNext())
updateAction(e.next());
}
}
/**
* Updates all property dependent actions.
* @since 2.0
*/
protected void updatePropertyDependentActions() {
if (fPropertyActions != null) {
Iterator<String> e= fPropertyActions.iterator();
while (e.hasNext())
updateAction(e.next());
}
}
/**
* Updates all state dependent actions.
* @since 2.0
*/
protected void updateStateDependentActions() {
if (fStateActions != null) {
Iterator<String> e= fStateActions.iterator();
while (e.hasNext())
updateAction(e.next());
}
}
/**
* Creates action entries for all SWT StyledText actions as defined in
* <code>org.eclipse.swt.custom.ST</code>. Overwrites and
* extends the list of these actions afterwards.
* <p>
* Subclasses may extend.
* </p>
* @since 2.0
*/
protected void createNavigationActions() {
IAction action;
StyledText textWidget= fSourceViewer.getTextWidget();
for (int i= 0; i < ACTION_MAP.length; i++) {
IdMapEntry entry= ACTION_MAP[i];
action= new TextNavigationAction(textWidget, entry.getAction());
action.setActionDefinitionId(entry.getActionId());
setAction(entry.getActionId(), action);
}
action= new ToggleOverwriteModeAction(EditorMessages.getBundleForConstructedKeys(), "Editor.ToggleOverwriteMode."); //$NON-NLS-1$
action.setActionDefinitionId(ITextEditorActionDefinitionIds.TOGGLE_OVERWRITE);
setAction(ITextEditorActionDefinitionIds.TOGGLE_OVERWRITE, action);
textWidget.setKeyBinding(SWT.INSERT, SWT.NULL);
action= new ScrollLinesAction(-1);
action.setActionDefinitionId(ITextEditorActionDefinitionIds.SCROLL_LINE_UP);
setAction(ITextEditorActionDefinitionIds.SCROLL_LINE_UP, action);
action= new ScrollLinesAction(1);
action.setActionDefinitionId(ITextEditorActionDefinitionIds.SCROLL_LINE_DOWN);
setAction(ITextEditorActionDefinitionIds.SCROLL_LINE_DOWN, action);
action= new LineEndAction(textWidget, false);
action.setActionDefinitionId(ITextEditorActionDefinitionIds.LINE_END);
setAction(ITextEditorActionDefinitionIds.LINE_END, action);
action= new LineStartAction(textWidget, false);
action.setActionDefinitionId(ITextEditorActionDefinitionIds.LINE_START);
setAction(ITextEditorActionDefinitionIds.LINE_START, action);
action= new LineEndAction(textWidget, true);
action.setActionDefinitionId(ITextEditorActionDefinitionIds.SELECT_LINE_END);
setAction(ITextEditorActionDefinitionIds.SELECT_LINE_END, action);
action= new LineStartAction(textWidget, true);
action.setActionDefinitionId(ITextEditorActionDefinitionIds.SELECT_LINE_START);
setAction(ITextEditorActionDefinitionIds.SELECT_LINE_START, action);
// to accommodate https://bugs.eclipse.org/bugs/show_bug.cgi?id=51516
// nullify handling of DELETE key by StyledText
textWidget.setKeyBinding(SWT.DEL, SWT.NULL);
}
/**
* Creates this editor's accessibility actions.
* @since 2.0
*/
private void createAccessibilityActions() {
IAction action= new ShowRulerContextMenuAction();
action.setActionDefinitionId(ITextEditorActionDefinitionIds.SHOW_RULER_CONTEXT_MENU);
setAction(ITextEditorActionDefinitionIds.SHOW_RULER_CONTEXT_MENU, action);
}
/**
* Creates this editor's undo/redo actions.
* <p>
* Subclasses may override or extend.</p>
*
* @since 3.1
*/
protected void createUndoRedoActions() {
IUndoContext undoContext= getUndoContext();
if (undoContext != null) {
// Use actions provided by global undo/redo
// Create the undo action
OperationHistoryActionHandler undoAction= new UndoActionHandler(getEditorSite(), undoContext);
PlatformUI.getWorkbench().getHelpSystem().setHelp(undoAction, IAbstractTextEditorHelpContextIds.UNDO_ACTION);
undoAction.setActionDefinitionId(IWorkbenchCommandConstants.EDIT_UNDO);
registerUndoRedoAction(ITextEditorActionConstants.UNDO, undoAction);
// Create the redo action.
OperationHistoryActionHandler redoAction= new RedoActionHandler(getEditorSite(), undoContext);
PlatformUI.getWorkbench().getHelpSystem().setHelp(redoAction, IAbstractTextEditorHelpContextIds.REDO_ACTION);
redoAction.setActionDefinitionId(IWorkbenchCommandConstants.EDIT_REDO);
registerUndoRedoAction(ITextEditorActionConstants.REDO, redoAction);
// Install operation approvers
IOperationHistory history= OperationHistoryFactory.getOperationHistory();
// The first approver will prompt when operations affecting outside elements are to be undone or redone.
if (fNonLocalOperationApprover != null)
history.removeOperationApprover(fNonLocalOperationApprover);
fNonLocalOperationApprover= getUndoRedoOperationApprover(undoContext);
history.addOperationApprover(fNonLocalOperationApprover);
// The second approver will prompt from this editor when an undo is attempted on an operation
// and it is not the most recent operation in the editor.
if (fLinearUndoViolationApprover != null)
history.removeOperationApprover(fLinearUndoViolationApprover);
fLinearUndoViolationApprover= new LinearUndoViolationUserApprover(undoContext, this);
history.addOperationApprover(fLinearUndoViolationApprover);
} else {
// Use text operation actions (pre 3.1 style)
ResourceAction action;
if (getAction(ITextEditorActionConstants.UNDO) == null) {
action= new TextOperationAction(EditorMessages.getBundleForConstructedKeys(), "Editor.Undo.", this, ITextOperationTarget.UNDO); //$NON-NLS-1$
action.setHelpContextId(IAbstractTextEditorHelpContextIds.UNDO_ACTION);
action.setActionDefinitionId(IWorkbenchCommandConstants.EDIT_UNDO);
setAction(ITextEditorActionConstants.UNDO, action);
}
if (getAction(ITextEditorActionConstants.REDO) == null) {
action= new TextOperationAction(EditorMessages.getBundleForConstructedKeys(), "Editor.Redo.", this, ITextOperationTarget.REDO); //$NON-NLS-1$
action.setHelpContextId(IAbstractTextEditorHelpContextIds.REDO_ACTION);
action.setActionDefinitionId(IWorkbenchCommandConstants.EDIT_REDO);
setAction(ITextEditorActionConstants.REDO, action);
}
}
}
/**
* Registers the given undo/redo action under the given ID and ensures that previously installed
* actions get disposed. It also takes care of re-registering the new action with the global
* action handler.
*
* @param actionId the action id under which to register the action
* @param action the action to register or <code>null</code> to dispose them
* @since 3.1
*/
private void registerUndoRedoAction(String actionId, OperationHistoryActionHandler action) {
IAction oldAction= getAction(actionId);
if (oldAction instanceof OperationHistoryActionHandler)
((OperationHistoryActionHandler)oldAction).dispose();
if (action == null)
return;
setAction(actionId, action);
IActionBars actionBars= getEditorSite().getActionBars();
if (actionBars != null)
actionBars.setGlobalActionHandler(actionId, action);
}
/**
* Return an {@link IOperationApprover} appropriate for approving the undo and
* redo of operations that have the specified undo context.
* <p>
* Subclasses may override.
* </p>
* @param undoContext the IUndoContext of operations that should be examined
* by the operation approver
* @return the <code>IOperationApprover</code> appropriate for approving undo
* and redo operations inside this editor, or <code>null</code> if no
* approval is needed
* @since 3.1
*/
protected IOperationApprover getUndoRedoOperationApprover(IUndoContext undoContext) {
return new NonLocalUndoUserApprover(undoContext, this, new Object [] { getEditorInput() }, Object.class);
}
/**
* Creates this editor's standard actions and connects them with the global
* workbench actions.
* <p>
* Subclasses may extend.</p>
*/
protected void createActions() {
ResourceAction action;
setAction(IWorkbenchCommandConstants.EDIT_CUT, null);
action= new TextOperationAction(EditorMessages.getBundleForConstructedKeys(), "Editor.Cut.", this, ITextOperationTarget.CUT); //$NON-NLS-1$
action.setHelpContextId(IAbstractTextEditorHelpContextIds.CUT_ACTION);
action.setActionDefinitionId(IWorkbenchCommandConstants.EDIT_CUT);
updateImages(action, IWorkbenchCommandConstants.EDIT_CUT);
setAction(ITextEditorActionConstants.CUT, action);
setAction(IWorkbenchCommandConstants.EDIT_COPY, null);
action= new TextOperationAction(EditorMessages.getBundleForConstructedKeys(), "Editor.Copy.", this, ITextOperationTarget.COPY, true); //$NON-NLS-1$
action.setHelpContextId(IAbstractTextEditorHelpContextIds.COPY_ACTION);
action.setActionDefinitionId(IWorkbenchCommandConstants.EDIT_COPY);
updateImages(action, IWorkbenchCommandConstants.EDIT_COPY);
setAction(ITextEditorActionConstants.COPY, action);
setAction(IWorkbenchCommandConstants.EDIT_PASTE, null);
action= new TextOperationAction(EditorMessages.getBundleForConstructedKeys(), "Editor.Paste.", this, ITextOperationTarget.PASTE); //$NON-NLS-1$
action.setHelpContextId(IAbstractTextEditorHelpContextIds.PASTE_ACTION);
action.setActionDefinitionId(IWorkbenchCommandConstants.EDIT_PASTE);
updateImages(action, IWorkbenchCommandConstants.EDIT_PASTE);
setAction(ITextEditorActionConstants.PASTE, action);
action= new TextOperationAction(EditorMessages.getBundleForConstructedKeys(), "Editor.Delete.", this, ITextOperationTarget.DELETE); //$NON-NLS-1$
action.setHelpContextId(IAbstractTextEditorHelpContextIds.DELETE_ACTION);
action.setActionDefinitionId(IWorkbenchCommandConstants.EDIT_DELETE);
updateImages(action, IWorkbenchCommandConstants.EDIT_DELETE);
setAction(ITextEditorActionConstants.DELETE, action);
action= new DeleteLineAction(EditorMessages.getBundleForConstructedKeys(), "Editor.DeleteLine.", this, DeleteLineAction.WHOLE, false); //$NON-NLS-1$
action.setHelpContextId(IAbstractTextEditorHelpContextIds.DELETE_LINE_ACTION);
action.setActionDefinitionId(ITextEditorActionDefinitionIds.DELETE_LINE);
setAction(ITextEditorActionConstants.DELETE_LINE, action);
action= new DeleteLineAction(EditorMessages.getBundleForConstructedKeys(), "Editor.CutLine.", this, DeleteLineAction.WHOLE, true); //$NON-NLS-1$
action.setHelpContextId(IAbstractTextEditorHelpContextIds.CUT_LINE_ACTION);
action.setActionDefinitionId(ITextEditorActionDefinitionIds.CUT_LINE);
setAction(ITextEditorActionConstants.CUT_LINE, action);
action= new DeleteLineAction(EditorMessages.getBundleForConstructedKeys(), "Editor.DeleteLineToBeginning.", this, DeleteLineAction.TO_BEGINNING, false); //$NON-NLS-1$
action.setHelpContextId(IAbstractTextEditorHelpContextIds.DELETE_LINE_TO_BEGINNING_ACTION);
action.setActionDefinitionId(ITextEditorActionDefinitionIds.DELETE_LINE_TO_BEGINNING);
setAction(ITextEditorActionConstants.DELETE_LINE_TO_BEGINNING, action);
action= new DeleteLineAction(EditorMessages.getBundleForConstructedKeys(), "Editor.CutLineToBeginning.", this, DeleteLineAction.TO_BEGINNING, true); //$NON-NLS-1$
action.setHelpContextId(IAbstractTextEditorHelpContextIds.CUT_LINE_TO_BEGINNING_ACTION);
action.setActionDefinitionId(ITextEditorActionDefinitionIds.CUT_LINE_TO_BEGINNING);
setAction(ITextEditorActionConstants.CUT_LINE_TO_BEGINNING, action);
action= new DeleteLineAction(EditorMessages.getBundleForConstructedKeys(), "Editor.DeleteLineToEnd.", this, DeleteLineAction.TO_END, false); //$NON-NLS-1$
action.setHelpContextId(IAbstractTextEditorHelpContextIds.DELETE_LINE_TO_END_ACTION);
action.setActionDefinitionId(ITextEditorActionDefinitionIds.DELETE_LINE_TO_END);
setAction(ITextEditorActionConstants.DELETE_LINE_TO_END, action);
action= new DeleteLineAction(EditorMessages.getBundleForConstructedKeys(), "Editor.CutLineToEnd.", this, DeleteLineAction.TO_END, true); //$NON-NLS-1$
action.setHelpContextId(IAbstractTextEditorHelpContextIds.CUT_LINE_TO_END_ACTION);
action.setActionDefinitionId(ITextEditorActionDefinitionIds.CUT_LINE_TO_END);
setAction(ITextEditorActionConstants.CUT_LINE_TO_END, action);
action= new JoinLinesAction(EditorMessages.getBundleForConstructedKeys(), "Editor.JoinLines.", this, " "); //$NON-NLS-1$ //$NON-NLS-2$
action.setHelpContextId(IAbstractTextEditorHelpContextIds.JOIN_LINES_ACTION);
action.setActionDefinitionId(ITextEditorActionDefinitionIds.JOIN_LINES);
setAction(ITextEditorActionConstants.JOIN_LINES, action);
action= new MarkAction(EditorMessages.getBundleForConstructedKeys(), "Editor.SetMark.", this, MarkAction.SET_MARK); //$NON-NLS-1$
action.setHelpContextId(IAbstractTextEditorHelpContextIds.SET_MARK_ACTION);
action.setActionDefinitionId(ITextEditorActionDefinitionIds.SET_MARK);
setAction(ITextEditorActionConstants.SET_MARK, action);
action= new MarkAction(EditorMessages.getBundleForConstructedKeys(), "Editor.ClearMark.", this, MarkAction.CLEAR_MARK); //$NON-NLS-1$
action.setHelpContextId(IAbstractTextEditorHelpContextIds.CLEAR_MARK_ACTION);
action.setActionDefinitionId(ITextEditorActionDefinitionIds.CLEAR_MARK);
setAction(ITextEditorActionConstants.CLEAR_MARK, action);
action= new MarkAction(EditorMessages.getBundleForConstructedKeys(), "Editor.SwapMark.", this, MarkAction.SWAP_MARK); //$NON-NLS-1$
action.setHelpContextId(IAbstractTextEditorHelpContextIds.SWAP_MARK_ACTION);
action.setActionDefinitionId(ITextEditorActionDefinitionIds.SWAP_MARK);
setAction(ITextEditorActionConstants.SWAP_MARK, action);
action= new TextOperationAction(EditorMessages.getBundleForConstructedKeys(), "Editor.SelectAll.", this, ITextOperationTarget.SELECT_ALL, true); //$NON-NLS-1$
action.setHelpContextId(IAbstractTextEditorHelpContextIds.SELECT_ALL_ACTION);
action.setActionDefinitionId(IWorkbenchCommandConstants.EDIT_SELECT_ALL);
setAction(ITextEditorActionConstants.SELECT_ALL, action);
action= new ShiftAction(EditorMessages.getBundleForConstructedKeys(), "Editor.ShiftRight.", this, ITextOperationTarget.SHIFT_RIGHT); //$NON-NLS-1$
action.setHelpContextId(IAbstractTextEditorHelpContextIds.SHIFT_RIGHT_ACTION);
action.setActionDefinitionId(ITextEditorActionDefinitionIds.SHIFT_RIGHT);
setAction(ITextEditorActionConstants.SHIFT_RIGHT, action);
action= new ShiftAction(EditorMessages.getBundleForConstructedKeys(), "Editor.ShiftRight.", this, ITextOperationTarget.SHIFT_RIGHT) { //$NON-NLS-1$
@Override
public void update() {
updateForTab();
}
};
setAction(ITextEditorActionConstants.SHIFT_RIGHT_TAB, action);
action= new ShiftAction(EditorMessages.getBundleForConstructedKeys(), "Editor.ShiftLeft.", this, ITextOperationTarget.SHIFT_LEFT); //$NON-NLS-1$
action.setHelpContextId(IAbstractTextEditorHelpContextIds.SHIFT_LEFT_ACTION);
action.setActionDefinitionId(ITextEditorActionDefinitionIds.SHIFT_LEFT);
setAction(ITextEditorActionConstants.SHIFT_LEFT, action);
action= new TextOperationAction(EditorMessages.getBundleForConstructedKeys(), "Editor.Print.", this, ITextOperationTarget.PRINT, true); //$NON-NLS-1$
action.setHelpContextId(IAbstractTextEditorHelpContextIds.PRINT_ACTION);
action.setActionDefinitionId(IWorkbenchCommandConstants.FILE_PRINT);
updateImages(action, IWorkbenchCommandConstants.FILE_PRINT);
setAction(ITextEditorActionConstants.PRINT, action);
action= new FindReplaceAction(EditorMessages.getBundleForConstructedKeys(), "Editor.FindReplace.", this); //$NON-NLS-1$
action.setHelpContextId(IAbstractTextEditorHelpContextIds.FIND_ACTION);
action.setActionDefinitionId(IWorkbenchCommandConstants.EDIT_FIND_AND_REPLACE);
updateImages(action, IWorkbenchCommandConstants.EDIT_FIND_AND_REPLACE);
setAction(ITextEditorActionConstants.FIND, action);
action= new FindNextAction(EditorMessages.getBundleForConstructedKeys(), "Editor.FindNext.", this, true); //$NON-NLS-1$
action.setHelpContextId(IAbstractTextEditorHelpContextIds.FIND_NEXT_ACTION);
action.setActionDefinitionId(IWorkbenchActionDefinitionIds.FIND_NEXT);
setAction(ITextEditorActionConstants.FIND_NEXT, action);
action= new FindNextAction(EditorMessages.getBundleForConstructedKeys(), "Editor.FindPrevious.", this, false); //$NON-NLS-1$
action.setHelpContextId(IAbstractTextEditorHelpContextIds.FIND_PREVIOUS_ACTION);
action.setActionDefinitionId(IWorkbenchActionDefinitionIds.FIND_PREVIOUS);
setAction(ITextEditorActionConstants.FIND_PREVIOUS, action);
action= new IncrementalFindAction(EditorMessages.getBundleForConstructedKeys(), "Editor.FindIncremental.", this, true); //$NON-NLS-1$
action.setHelpContextId(IAbstractTextEditorHelpContextIds.FIND_INCREMENTAL_ACTION);
action.setActionDefinitionId(IWorkbenchActionDefinitionIds.FIND_INCREMENTAL);
setAction(ITextEditorActionConstants.FIND_INCREMENTAL, action);
action= new IncrementalFindAction(EditorMessages.getBundleForConstructedKeys(), "Editor.FindIncrementalReverse.", this, false); //$NON-NLS-1$
action.setHelpContextId(IAbstractTextEditorHelpContextIds.FIND_INCREMENTAL_REVERSE_ACTION);
action.setActionDefinitionId(IWorkbenchActionDefinitionIds.FIND_INCREMENTAL_REVERSE);
setAction(ITextEditorActionConstants.FIND_INCREMENTAL_REVERSE, action);
fSaveAction= ActionFactory.SAVE.create(getSite().getWorkbenchWindow());
setAction(ITextEditorActionConstants.SAVE, fSaveAction);
action= new RevertToSavedAction(EditorMessages.getBundleForConstructedKeys(), "Editor.Revert.", this); //$NON-NLS-1$
action.setHelpContextId(IAbstractTextEditorHelpContextIds.REVERT_TO_SAVED_ACTION);
action.setActionDefinitionId(IWorkbenchCommandConstants.FILE_REVERT);
updateImages(action, IWorkbenchCommandConstants.FILE_REVERT);
setAction(ITextEditorActionConstants.REVERT_TO_SAVED, action);
action= new GotoLineAction(EditorMessages.getBundleForConstructedKeys(), "Editor.GotoLine.", this); //$NON-NLS-1$
action.setHelpContextId(IAbstractTextEditorHelpContextIds.GOTO_LINE_ACTION);
action.setActionDefinitionId(ITextEditorActionDefinitionIds.LINE_GOTO);
setAction(ITextEditorActionConstants.GOTO_LINE, action);
action= new MoveLinesAction(EditorMessages.getBundleForConstructedKeys(), "Editor.MoveLinesUp.", this, getSourceViewer(), true, false); //$NON-NLS-1$
action.setHelpContextId(IAbstractTextEditorHelpContextIds.MOVE_LINES_ACTION);
action.setActionDefinitionId(ITextEditorActionDefinitionIds.MOVE_LINES_UP);
setAction(ITextEditorActionConstants.MOVE_LINE_UP, action);
action= new MoveLinesAction(EditorMessages.getBundleForConstructedKeys(), "Editor.MoveLinesDown.", this, getSourceViewer(), false, false); //$NON-NLS-1$
action.setHelpContextId(IAbstractTextEditorHelpContextIds.MOVE_LINES_ACTION);
action.setActionDefinitionId(ITextEditorActionDefinitionIds.MOVE_LINES_DOWN);
setAction(ITextEditorActionConstants.MOVE_LINE_DOWN, action);
action= new MoveLinesAction(EditorMessages.getBundleForConstructedKeys(), "Editor.CopyLineUp.", this, getSourceViewer(), true, true); //$NON-NLS-1$
action.setHelpContextId(IAbstractTextEditorHelpContextIds.COPY_LINES_ACTION);
action.setActionDefinitionId(ITextEditorActionDefinitionIds.COPY_LINES_UP);
setAction(ITextEditorActionConstants.COPY_LINE_UP, action);
action= new MoveLinesAction(EditorMessages.getBundleForConstructedKeys(), "Editor.CopyLineDown.", this, getSourceViewer(), false, true); //$NON-NLS-1$
action.setHelpContextId(IAbstractTextEditorHelpContextIds.COPY_LINES_ACTION);
action.setActionDefinitionId(ITextEditorActionDefinitionIds.COPY_LINES_DOWN);
setAction(ITextEditorActionConstants.COPY_LINE_DOWN, action);
action= new CaseAction(EditorMessages.getBundleForConstructedKeys(), "Editor.UpperCase.", this, true); //$NON-NLS-1$
action.setHelpContextId(IAbstractTextEditorHelpContextIds.UPPER_CASE_ACTION);
action.setActionDefinitionId(ITextEditorActionDefinitionIds.UPPER_CASE);
setAction(ITextEditorActionConstants.UPPER_CASE, action);
action= new CaseAction(EditorMessages.getBundleForConstructedKeys(), "Editor.LowerCase.", this, false); //$NON-NLS-1$
action.setHelpContextId(IAbstractTextEditorHelpContextIds.LOWER_CASE_ACTION);
action.setActionDefinitionId(ITextEditorActionDefinitionIds.LOWER_CASE);
setAction(ITextEditorActionConstants.LOWER_CASE, action);
action= new InsertLineAction(EditorMessages.getBundleForConstructedKeys(), "Editor.SmartEnter.", this, false); //$NON-NLS-1$
action.setHelpContextId(IAbstractTextEditorHelpContextIds.SMART_ENTER_ACTION);
action.setActionDefinitionId(ITextEditorActionDefinitionIds.SMART_ENTER);
setAction(ITextEditorActionConstants.SMART_ENTER, action);
action= new InsertLineAction(EditorMessages.getBundleForConstructedKeys(), "Editor.SmartEnterInverse.", this, true); //$NON-NLS-1$
action.setHelpContextId(IAbstractTextEditorHelpContextIds.SMART_ENTER_ACTION);
action.setActionDefinitionId(ITextEditorActionDefinitionIds.SMART_ENTER_INVERSE);
setAction(ITextEditorActionConstants.SMART_ENTER_INVERSE, action);
action= new ToggleInsertModeAction(EditorMessages.getBundleForConstructedKeys(), "Editor.ToggleInsertMode."); //$NON-NLS-1$
action.setHelpContextId(IAbstractTextEditorHelpContextIds.TOGGLE_INSERT_MODE_ACTION);
action.setActionDefinitionId(ITextEditorActionDefinitionIds.TOGGLE_INSERT_MODE);
setAction(ITextEditorActionConstants.TOGGLE_INSERT_MODE, action);
action= new HippieCompleteAction(EditorMessages.getBundleForConstructedKeys(), "Editor.HippieCompletion.", this); //$NON-NLS-1$
action.setHelpContextId(IAbstractTextEditorHelpContextIds.HIPPIE_COMPLETION_ACTION);
action.setActionDefinitionId(ITextEditorActionDefinitionIds.HIPPIE_COMPLETION);
setAction(ITextEditorActionConstants.HIPPIE_COMPLETION, action);
action= new ContentAssistAction(EditorMessages.getBundleForConstructedKeys(), "Editor.ContentAssistProposal.", this); //$NON-NLS-1$
action.setHelpContextId(IAbstractTextEditorHelpContextIds.CONTENT_ASSIST_ACTION);
action.setActionDefinitionId(ITextEditorActionDefinitionIds.CONTENT_ASSIST_PROPOSALS);
setAction(ITextEditorActionConstants.CONTENT_ASSIST, action);
markAsStateDependentAction(ITextEditorActionConstants.CONTENT_ASSIST, true);
action= new TextOperationAction(EditorMessages.getBundleForConstructedKeys(), "Editor.ContentAssistContextInformation.", this, ISourceViewer.CONTENTASSIST_CONTEXT_INFORMATION); //$NON-NLS-1$
action.setHelpContextId(IAbstractTextEditorHelpContextIds.CONTENT_ASSIST_CONTEXT_INFORMATION_ACTION);
action.setActionDefinitionId(ITextEditorActionDefinitionIds.CONTENT_ASSIST_CONTEXT_INFORMATION);
setAction(ITextEditorActionConstants.CONTENT_ASSIST_CONTEXT_INFORMATION, action);
markAsStateDependentAction(ITextEditorActionConstants.CONTENT_ASSIST_CONTEXT_INFORMATION, true);
action= new TextOperationAction(EditorMessages.getBundleForConstructedKeys(), "Editor.QuickAssist.", this, ISourceViewer.QUICK_ASSIST); //$NON-NLS-1$
action.setHelpContextId(IAbstractTextEditorHelpContextIds.QUICK_ASSIST_ACTION);
action.setActionDefinitionId(ITextEditorActionDefinitionIds.QUICK_ASSIST);
setAction(ITextEditorActionConstants.QUICK_ASSIST, action);
markAsStateDependentAction(ITextEditorActionConstants.QUICK_ASSIST, true);
action= new GotoAnnotationAction(this, true);
setAction(ITextEditorActionConstants.NEXT, action);
action= new GotoAnnotationAction(this, false);
setAction(ITextEditorActionConstants.PREVIOUS, action);
action= new RecenterAction(EditorMessages.getBundleForConstructedKeys(), "Editor.Recenter.", this); //$NON-NLS-1$
action.setHelpContextId(IAbstractTextEditorHelpContextIds.RECENTER_ACTION);
action.setActionDefinitionId(ITextEditorActionDefinitionIds.RECENTER);
setAction(ITextEditorActionConstants.RECENTER, action);
action= new ShowWhitespaceCharactersAction(EditorMessages.getBundleForConstructedKeys(), "Editor.ShowWhitespaceCharacters.", this, getPreferenceStore()); //$NON-NLS-1$
action.setHelpContextId(IAbstractTextEditorHelpContextIds.SHOW_WHITESPACE_CHARACTERS_ACTION);
action.setActionDefinitionId(ITextEditorActionDefinitionIds.SHOW_WHITESPACE_CHARACTERS);
setAction(ITextEditorActionConstants.SHOW_WHITESPACE_CHARACTERS, action);
action= new TextOperationAction(EditorMessages.getBundleForConstructedKeys(), "Editor.ShowInformation.", this, ISourceViewer.INFORMATION, true); //$NON-NLS-1$
action= new InformationDispatchAction(EditorMessages.getBundleForConstructedKeys(), "Editor.ShowInformation.", (TextOperationAction) action); //$NON-NLS-1$
action.setHelpContextId(IAbstractTextEditorHelpContextIds.SHOW_INFORMATION_ACTION);
action.setActionDefinitionId(ITextEditorActionDefinitionIds.SHOW_INFORMATION);
setAction(ITextEditorActionConstants.SHOW_INFORMATION, action);
final BlockSelectionModeToggleAction blockAction= new BlockSelectionModeToggleAction(EditorMessages.getBundleForConstructedKeys(), "Editor.ToggleBlockSelectionMode.", this); //$NON-NLS-1$
action= blockAction;
action.setHelpContextId(IAbstractTextEditorHelpContextIds.BLOCK_SELECTION_MODE_ACTION);
action.setActionDefinitionId(ITextEditorActionDefinitionIds.BLOCK_SELECTION_MODE);
setAction(ITextEditorActionConstants.BLOCK_SELECTION_MODE, action);
if (isWordWrapSupported()) {
final WordWrapToggleAction wrapAction= new WordWrapToggleAction(EditorMessages.getBundleForConstructedKeys(), "Editor.ToggleWordWrap.", this, getInitialWordWrapStatus()); //$NON-NLS-1$
action= wrapAction;
action.setHelpContextId(IAbstractTextEditorHelpContextIds.WORD_WRAP_TOGGLE_ACTION);
action.setActionDefinitionId(ITextEditorActionDefinitionIds.WORD_WRAP);
setAction(ITextEditorActionConstants.WORD_WRAP, action);
blockAction.addPropertyChangeListener(event -> {
if (IAction.CHECKED == event.getProperty() && Boolean.TRUE.equals(event.getNewValue())) {
wrapAction.setChecked(false);
}
});
wrapAction.addPropertyChangeListener(event -> {
if (IAction.CHECKED == event.getProperty() && Boolean.TRUE.equals(event.getNewValue())) {
blockAction.setChecked(false);
}
});
}
action= new TextOperationAction(EditorMessages.getBundleForConstructedKeys(), "Editor.OpenHyperlink.", this, HyperlinkManager.OPEN_HYPERLINK, true); //$NON-NLS-1$;
action.setHelpContextId(IAbstractTextEditorHelpContextIds.OPEN_HYPERLINK_ACTION);
action.setActionDefinitionId(ITextEditorActionDefinitionIds.OPEN_HYPERLINK);
setAction(ITextEditorActionConstants.OPEN_HYPERLINK, action);
PropertyDialogAction openProperties= new PropertyDialogAction(
() -> getSite().getShell(),
new ISelectionProvider() {
@Override
public void addSelectionChangedListener(ISelectionChangedListener listener) {
}
@Override
public ISelection getSelection() {
return new StructuredSelection(getEditorInput());
}
@Override
public void removeSelectionChangedListener(ISelectionChangedListener listener) {
}
@Override
public void setSelection(ISelection selection) {
}
});
openProperties.setActionDefinitionId(IWorkbenchCommandConstants.FILE_PROPERTIES);
updateImages(openProperties, IWorkbenchCommandConstants.FILE_PROPERTIES);
setAction(ITextEditorActionConstants.PROPERTIES, openProperties);
markAsContentDependentAction(ITextEditorActionConstants.UNDO, true);
markAsContentDependentAction(ITextEditorActionConstants.REDO, true);
markAsContentDependentAction(ITextEditorActionConstants.FIND, true);
markAsContentDependentAction(ITextEditorActionConstants.FIND_NEXT, true);
markAsContentDependentAction(ITextEditorActionConstants.FIND_PREVIOUS, true);
markAsContentDependentAction(ITextEditorActionConstants.FIND_INCREMENTAL, true);
markAsContentDependentAction(ITextEditorActionConstants.FIND_INCREMENTAL_REVERSE, true);
markAsSelectionDependentAction(ITextEditorActionConstants.CUT, true);
markAsSelectionDependentAction(ITextEditorActionConstants.COPY, true);
markAsSelectionDependentAction(ITextEditorActionConstants.PASTE, true);
markAsSelectionDependentAction(ITextEditorActionConstants.DELETE, true);
markAsSelectionDependentAction(ITextEditorActionConstants.SHIFT_RIGHT, true);
markAsSelectionDependentAction(ITextEditorActionConstants.SHIFT_RIGHT_TAB, true);
markAsSelectionDependentAction(ITextEditorActionConstants.UPPER_CASE, true);
markAsSelectionDependentAction(ITextEditorActionConstants.LOWER_CASE, true);
markAsPropertyDependentAction(ITextEditorActionConstants.UNDO, true);
markAsPropertyDependentAction(ITextEditorActionConstants.REDO, true);
markAsPropertyDependentAction(ITextEditorActionConstants.REVERT_TO_SAVED, true);
markAsPropertyDependentAction(ITextEditorActionConstants.SAVE, true);
markAsStateDependentAction(ITextEditorActionConstants.UNDO, true);
markAsStateDependentAction(ITextEditorActionConstants.REDO, true);
markAsStateDependentAction(ITextEditorActionConstants.CUT, true);
markAsStateDependentAction(ITextEditorActionConstants.PASTE, true);
markAsStateDependentAction(ITextEditorActionConstants.DELETE, true);
markAsStateDependentAction(ITextEditorActionConstants.SHIFT_RIGHT, true);
markAsStateDependentAction(ITextEditorActionConstants.SHIFT_RIGHT_TAB, true);
markAsStateDependentAction(ITextEditorActionConstants.SHIFT_LEFT, true);
markAsStateDependentAction(ITextEditorActionConstants.FIND, true);
markAsStateDependentAction(ITextEditorActionConstants.DELETE_LINE, true);
markAsStateDependentAction(ITextEditorActionConstants.DELETE_LINE_TO_BEGINNING, true);
markAsStateDependentAction(ITextEditorActionConstants.DELETE_LINE_TO_END, true);
markAsStateDependentAction(ITextEditorActionConstants.MOVE_LINE_UP, true);
markAsStateDependentAction(ITextEditorActionConstants.MOVE_LINE_DOWN, true);
markAsStateDependentAction(ITextEditorActionConstants.CUT_LINE, true);
markAsStateDependentAction(ITextEditorActionConstants.CUT_LINE_TO_BEGINNING, true);
markAsStateDependentAction(ITextEditorActionConstants.CUT_LINE_TO_END, true);
setActionActivationCode(ITextEditorActionConstants.SHIFT_RIGHT_TAB,'\t', -1, SWT.NONE);
setActionActivationCode(ITextEditorActionConstants.SHIFT_LEFT, '\t', -1, SWT.SHIFT);
}
private void updateImages(IAction action, String commandId) {
if (action.getImageDescriptor() != null) {
return;
}
ICommandImageService imgService = getSite().getService(ICommandImageService.class);
if (imgService == null) {
return;
}
action.setImageDescriptor(imgService.getImageDescriptor(commandId));
action.setDisabledImageDescriptor(imgService.getImageDescriptor(commandId, ICommandImageService.TYPE_DISABLED));
action.setHoverImageDescriptor(imgService.getImageDescriptor(commandId, ICommandImageService.TYPE_HOVER));
}
/**
* Convenience method to add the action installed under the given action id to the given menu.
* @param menu the menu to add the action to
* @param actionId the id of the action to be added
*/
protected final void addAction(IMenuManager menu, String actionId) {
IAction action= getAction(actionId);
if (action != null) {
if (action instanceof IUpdate)
((IUpdate) action).update();
menu.add(action);
}
}
/**
* Convenience method to add the action installed under the given action id to the specified group of the menu.
* @param menu the menu to add the action to
* @param group the group in the menu
* @param actionId the id of the action to add
*/
protected final void addAction(IMenuManager menu, String group, String actionId) {
IAction action= getAction(actionId);
if (action != null) {
if (action instanceof IUpdate)
((IUpdate) action).update();
IMenuManager subMenu= menu.findMenuUsingPath(group);
if (subMenu != null)
subMenu.add(action);
else
menu.appendToGroup(group, action);
}
}
/**
* Convenience method to add a new group after the specified group.
* @param menu the menu to add the new group to
* @param existingGroup the group after which to insert the new group
* @param newGroup the new group
*/
protected final void addGroup(IMenuManager menu, String existingGroup, String newGroup) {
IMenuManager subMenu= menu.findMenuUsingPath(existingGroup);
if (subMenu != null)
subMenu.add(new Separator(newGroup));
else
menu.appendToGroup(existingGroup, new Separator(newGroup));
}
/**
* Sets up the ruler context menu before it is made visible.
* <p>
* Subclasses may extend to add other actions.</p>
*
* @param menu the menu
*/
protected void rulerContextMenuAboutToShow(IMenuManager menu) {
menu.add(new Separator(ITextEditorActionConstants.GROUP_REST));
menu.add(new Separator(IWorkbenchActionConstants.MB_ADDITIONS));
for (Iterator<IMenuListener> i= fRulerContextMenuListeners.iterator(); i.hasNext();)
i.next().menuAboutToShow(menu);
addAction(menu, ITextEditorActionConstants.RULER_MANAGE_BOOKMARKS);
addAction(menu, ITextEditorActionConstants.RULER_MANAGE_TASKS);
}
/**
* Sets up this editor's context menu before it is made visible.
* <p>
* Subclasses may extend to add other actions.</p>
*
* @param menu the menu
*/
protected void editorContextMenuAboutToShow(IMenuManager menu) {
menu.add(new Separator(ITextEditorActionConstants.GROUP_UNDO));
menu.add(new GroupMarker(ITextEditorActionConstants.GROUP_SAVE));
menu.add(new Separator(ITextEditorActionConstants.GROUP_COPY));
menu.add(new Separator(ITextEditorActionConstants.GROUP_PRINT));
menu.add(new Separator(ITextEditorActionConstants.GROUP_EDIT));
menu.add(new Separator(ITextEditorActionConstants.GROUP_FIND));
menu.add(new Separator(IWorkbenchActionConstants.GROUP_ADD));
menu.add(new Separator(ITextEditorActionConstants.GROUP_REST));
menu.add(new Separator(IWorkbenchActionConstants.MB_ADDITIONS));
if (isEditable()) {
addAction(menu, ITextEditorActionConstants.GROUP_UNDO, ITextEditorActionConstants.UNDO);
addAction(menu, ITextEditorActionConstants.GROUP_UNDO, ITextEditorActionConstants.REVERT_TO_SAVED);
addAction(menu, ITextEditorActionConstants.GROUP_SAVE, ITextEditorActionConstants.SAVE);
addAction(menu, ITextEditorActionConstants.GROUP_COPY, ITextEditorActionConstants.CUT);
addAction(menu, ITextEditorActionConstants.GROUP_COPY, ITextEditorActionConstants.COPY);
addAction(menu, ITextEditorActionConstants.GROUP_COPY, ITextEditorActionConstants.PASTE);
IAction action= getAction(ITextEditorActionConstants.QUICK_ASSIST);
if (action != null && action.isEnabled())
addAction(menu, ITextEditorActionConstants.GROUP_EDIT, ITextEditorActionConstants.QUICK_ASSIST);
} else {
addAction(menu, ITextEditorActionConstants.GROUP_COPY, ITextEditorActionConstants.COPY);
}
}
/**
* Returns the status line manager of this editor.
*
* @return the status line manager of this editor
* @since 2.0, protected since 3.3
*/
protected IStatusLineManager getStatusLineManager() {
return getEditorSite().getActionBars().getStatusLineManager();
}
@SuppressWarnings("unchecked")
@Override
public <T> T getAdapter(Class<T> required) {
if (IEditorStatusLine.class.equals(required)) {
if (fEditorStatusLine == null) {
IStatusLineManager statusLineManager= getStatusLineManager();
ISelectionProvider selectionProvider= getSelectionProvider();
if (statusLineManager != null && selectionProvider != null)
fEditorStatusLine= new EditorStatusLine(statusLineManager, selectionProvider);
}
return (T) fEditorStatusLine;
}
if (IVerticalRulerInfo.class.equals(required)) {
if (fVerticalRuler != null)
return (T) fVerticalRuler;
}
if (IMarkRegionTarget.class.equals(required)) {
if (fMarkRegionTarget == null) {
IStatusLineManager manager= getStatusLineManager();
if (manager != null)
fMarkRegionTarget= (fSourceViewer == null ? null : new MarkRegionTarget(fSourceViewer, manager));
}
return (T) fMarkRegionTarget;
}
if (IDeleteLineTarget.class.equals(required)) {
if (fDeleteLineTarget == null) {
fDeleteLineTarget= new TextViewerDeleteLineTarget(fSourceViewer);
}
return (T) fDeleteLineTarget;
}
if (IncrementalFindTarget.class.equals(required)) {
if (fIncrementalFindTarget == null) {
IStatusLineManager manager= getStatusLineManager();
if (manager != null)
fIncrementalFindTarget= (fSourceViewer == null ? null : new IncrementalFindTarget(fSourceViewer, manager));
}
return (T) fIncrementalFindTarget;
}
if (IFindReplaceTarget.class.equals(required)) {
if (fFindReplaceTarget == null) {
IFindReplaceTarget target= (fSourceViewer == null ? null : fSourceViewer.getFindReplaceTarget());
if (target != null) {
fFindReplaceTarget= new FindReplaceTarget(this, target);
if (fFindScopeHighlightColor != null)
fFindReplaceTarget.setScopeHighlightColor(fFindScopeHighlightColor);
}
}
return (T) fFindReplaceTarget;
}
if (ITextOperationTarget.class.equals(required))
return (fSourceViewer == null ? null : (T) fSourceViewer.getTextOperationTarget());
if (IRewriteTarget.class.equals(required)) {
if (fSourceViewer instanceof ITextViewerExtension) {
ITextViewerExtension extension= (ITextViewerExtension) fSourceViewer;
return (T) extension.getRewriteTarget();
}
return null;
}
if (Control.class.equals(required))
return fSourceViewer != null ? (T) fSourceViewer.getTextWidget() : null;
if (IColumnSupport.class.equals(required)) {
if (fColumnSupport == null)
fColumnSupport= createColumnSupport();
return (T) fColumnSupport;
}
if (ITextViewer.class.equals(required))
return (fSourceViewer == null ? null : (T) fSourceViewer);
return super.getAdapter(required);
}
@Override
public void setFocus() {
if (fSourceViewer != null) {
StyledText widget= fSourceViewer.getTextWidget();
if (widget != null && !widget.isDisposed()) {
widget.setFocus();
}
}
}
@Override
public boolean showsHighlightRangeOnly() {
return fShowHighlightRangeOnly;
}
@Override
public void showHighlightRangeOnly(boolean showHighlightRangeOnly) {
fShowHighlightRangeOnly= showHighlightRangeOnly;
}
@Override
public void setHighlightRange(int offset, int length, boolean moveCursor) {
if (fSourceViewer == null)
return;
if (fShowHighlightRangeOnly) {
if (moveCursor)
fSourceViewer.setVisibleRegion(offset, length);
} else {
IRegion rangeIndication= fSourceViewer.getRangeIndication();
if (rangeIndication == null || offset != rangeIndication.getOffset() || length != rangeIndication.getLength())
fSourceViewer.setRangeIndication(offset, length, moveCursor);
}
}
@Override
public IRegion getHighlightRange() {
if (fSourceViewer == null)
return null;
if (fShowHighlightRangeOnly)
return getCoverage(fSourceViewer);
return fSourceViewer.getRangeIndication();
}
/*
* @see ITextEditor#resetHighlightRange
*/
@Override
public void resetHighlightRange() {
if (fSourceViewer == null)
return;
if (fShowHighlightRangeOnly)
fSourceViewer.resetVisibleRegion();
else
fSourceViewer.removeRangeIndication();
}
/**
* Adjusts the highlight range so that at least the specified range
* is highlighted.
* <p>
* Subclasses may re-implement this method.</p>
*
* @param offset the offset of the range which at least should be highlighted
* @param length the length of the range which at least should be highlighted
*/
protected void adjustHighlightRange(int offset, int length) {
if (fSourceViewer == null)
return;
if (fSourceViewer instanceof ITextViewerExtension5) {
ITextViewerExtension5 extension= (ITextViewerExtension5) fSourceViewer;
extension.exposeModelRange(new Region(offset, length));
} else if (!isVisible(fSourceViewer, offset, length)) {
fSourceViewer.resetVisibleRegion();
}
}
@Override
public void selectAndReveal(int start, int length) {
selectAndReveal(start, length, start, length);
}
/**
* Selects and reveals the specified ranges in this text editor.
*
* @param selectionStart the offset of the selection
* @param selectionLength the length of the selection
* @param revealStart the offset of the revealed range
* @param revealLength the length of the revealed range
* @since 3.0
*/
protected void selectAndReveal(int selectionStart, int selectionLength, int revealStart, int revealLength) {
if (fSourceViewer == null)
return;
ISelection selection= getSelectionProvider().getSelection();
if (selection instanceof ITextSelection) {
ITextSelection textSelection= (ITextSelection) selection;
if (textSelection.getOffset() != 0 || textSelection.getLength() != 0)
markInNavigationHistory();
}
StyledText widget= fSourceViewer.getTextWidget();
widget.setRedraw(false);
adjustHighlightRange(revealStart, revealLength);
fSourceViewer.revealRange(revealStart, revealLength);
fSourceViewer.setSelectedRange(selectionStart, selectionLength);
markInNavigationHistory();
widget.setRedraw(true);
}
/*
* @see org.eclipse.ui.INavigationLocationProvider#createNavigationLocation()
* @since 2.1
*/
@Override
public INavigationLocation createEmptyNavigationLocation() {
return new TextSelectionNavigationLocation(this, false);
}
@Override
public INavigationLocation createNavigationLocation() {
return new TextSelectionNavigationLocation(this, true);
}
/**
* Writes a check mark of the given situation into the navigation history.
* @since 2.1
*/
protected void markInNavigationHistory() {
getSite().getPage().getNavigationHistory().markLocation(this);
}
/**
* Hook which gets called when the editor has been saved.
* Subclasses may extend.
* @since 2.1
*/
protected void editorSaved() {
INavigationLocation[] locations= getSite().getPage().getNavigationHistory().getLocations();
IEditorInput input= getEditorInput();
for (int i= 0; i < locations.length; i++) {
if (locations[i] instanceof TextSelectionNavigationLocation) {
if(input.equals(locations[i].getInput())) {
TextSelectionNavigationLocation location= (TextSelectionNavigationLocation) locations[i];
location.partSaved(this);
}
}
}
}
@Override
protected void firePropertyChange(int property) {
super.firePropertyChange(property);
updatePropertyDependentActions();
}
@Override
public void setStatusField(IStatusField field, String category) {
Assert.isNotNull(category);
if (field != null) {
if (fStatusFields == null)
fStatusFields= new HashMap<>(3);
fStatusFields.put(category, field);
updateStatusField(category);
} else if (fStatusFields != null)
fStatusFields.remove(category);
if (fIncrementalFindTarget != null && ITextEditorActionConstants.STATUS_CATEGORY_FIND_FIELD.equals(category))
fIncrementalFindTarget.setStatusField(field);
}
/**
* Returns the current status field for the given status category.
*
* @param category the status category
* @return the current status field for the given status category
* @since 2.0
*/
protected IStatusField getStatusField(String category) {
if (category != null && fStatusFields != null)
return fStatusFields.get(category);
return null;
}
/**
* Returns whether this editor is in overwrite or insert mode.
*
* @return <code>true</code> if in insert mode, <code>false</code> for overwrite mode
* @since 2.0
*/
protected boolean isInInsertMode() {
return !fIsOverwriting;
}
@Override
public InsertMode getInsertMode() {
return fInsertMode;
}
@Override
public void setInsertMode(InsertMode newMode) {
List<InsertMode> legalModes= getLegalInsertModes();
if (!legalModes.contains(newMode))
throw new IllegalArgumentException();
fInsertMode= newMode;
handleInsertModeChanged();
}
/**
* Returns the set of legal insert modes. If insert modes are configured all defined insert modes
* are legal.
*
* @return the set of legal insert modes
* @since 3.0
*/
protected List<InsertMode> getLegalInsertModes() {
if (fLegalInsertModes == null) {
fLegalInsertModes= new ArrayList<>();
fLegalInsertModes.add(SMART_INSERT);
fLegalInsertModes.add(INSERT);
}
return fLegalInsertModes;
}
private void switchToNextInsertMode() {
InsertMode mode= getInsertMode();
List<InsertMode> legalModes= getLegalInsertModes();
int i= 0;
while (i < legalModes.size()) {
if (legalModes.get(i) == mode) break;
++ i;
}
i= (i + 1) % legalModes.size();
InsertMode newMode= legalModes.get(i);
setInsertMode(newMode);
}
private void toggleOverwriteMode() {
if (fIsOverwriteModeEnabled) {
fIsOverwriting= !fIsOverwriting;
fSourceViewer.getTextWidget().invokeAction(ST.TOGGLE_OVERWRITE);
handleInsertModeChanged();
}
}
/**
* Configures the given insert mode as legal or illegal. This call is ignored if the set of legal
* input modes would be empty after the call.
*
* @param mode the insert mode to be configured
* @param legal <code>true</code> if the given mode is legal, <code>false</code> otherwise
* @since 3.0
*/
protected void configureInsertMode(InsertMode mode, boolean legal) {
List<InsertMode> legalModes= getLegalInsertModes();
if (legal) {
if (!legalModes.contains(mode))
legalModes.add(mode);
} else if (legalModes.size() > 1) {
if (getInsertMode() == mode)
switchToNextInsertMode();
legalModes.remove(mode);
}
}
/**
* Sets the overwrite mode enablement.
*
* @param enable <code>true</code> to enable new overwrite mode,
* <code>false</code> to disable
* @since 3.0
*/
protected void enableOverwriteMode(boolean enable) {
if (fIsOverwriting && !enable)
toggleOverwriteMode();
fIsOverwriteModeEnabled= enable;
}
private Caret createOverwriteCaret(StyledText styledText) {
Caret caret= new Caret(styledText, SWT.NULL);
GC gc= new GC(styledText);
// XXX: this overwrite box is not proportional-font aware
// take 'a' as a medium sized character
Point charSize= gc.stringExtent("a"); //$NON-NLS-1$
// XXX: Filed request to get a caret with auto-height: https://bugs.eclipse.org/bugs/show_bug.cgi?id=118612
caret.setSize(charSize.x, styledText.getLineHeight());
caret.setFont(styledText.getFont());
gc.dispose();
return caret;
}
private Caret createInsertCaret(StyledText styledText) {
Caret caret= new Caret(styledText, SWT.NULL);
// XXX: Filed request to get a caret with auto-height: https://bugs.eclipse.org/bugs/show_bug.cgi?id=118612
caret.setSize(getCaretWidthPreference(), styledText.getLineHeight());
caret.setFont(styledText.getFont());
return caret;
}
private Image createRawInsertModeCaretImage(StyledText styledText) {
PaletteData caretPalette= new PaletteData(new RGB[] {new RGB (0,0,0), new RGB (255,255,255)});
int width= getCaretWidthPreference();
int widthOffset= width - 1;
// XXX: Filed request to get a caret with auto-height: https://bugs.eclipse.org/bugs/show_bug.cgi?id=118612
ImageData imageData= new ImageData(4 + widthOffset, styledText.getLineHeight(), 1, caretPalette);
Display display= styledText.getDisplay();
Image bracketImage= new Image(display, imageData);
GC gc= new GC (bracketImage);
gc.setForeground(display.getSystemColor(SWT.COLOR_WHITE));
gc.setLineWidth(0); // NOTE: 0 means width is 1 but with optimized performance
int height= imageData.height / 3;
// gap between two bars of one third of the height
// draw boxes using lines as drawing a line of a certain width produces
// rounded corners.
for (int i= 0; i < width ; i++) {
gc.drawLine(i, 0, i, height - 1);
gc.drawLine(i, imageData.height - height, i, imageData.height - 1);
}
gc.dispose();
return bracketImage;
}
private Caret createRawInsertModeCaret(StyledText styledText) {
// don't draw special raw caret if no smart mode is enabled
if (!getLegalInsertModes().contains(SMART_INSERT))
return createInsertCaret(styledText);
Caret caret= new Caret(styledText, SWT.NULL);
Image image= createRawInsertModeCaretImage(styledText);
caret.setImage(image);
caret.setFont(styledText.getFont());
return caret;
}
private int getCaretWidthPreference() {
if (getPreferenceStore() != null && getPreferenceStore().getBoolean(PREFERENCE_WIDE_CARET))
return WIDE_CARET_WIDTH;
return SINGLE_CARET_WIDTH;
}
private void updateCaret() {
if (fSourceViewer == null || fSourceViewer.getTextWidget() == null) {
return;
}
StyledText styledText= fSourceViewer.getTextWidget();
InsertMode mode= getInsertMode();
styledText.setCaret(null);
disposeNonDefaultCaret();
if (getPreferenceStore() == null || !getPreferenceStore().getBoolean(PREFERENCE_USE_CUSTOM_CARETS))
Assert.isTrue(fNonDefaultCaret == null);
else if (fIsOverwriting)
fNonDefaultCaret= createOverwriteCaret(styledText);
else if (SMART_INSERT == mode)
fNonDefaultCaret= createInsertCaret(styledText);
else if (INSERT == mode)
fNonDefaultCaret= createRawInsertModeCaret(styledText);
if (fNonDefaultCaret != null) {
styledText.setCaret(fNonDefaultCaret);
fNonDefaultCaretImage= fNonDefaultCaret.getImage();
} else if (fInitialCaret != styledText.getCaret())
styledText.setCaret(fInitialCaret);
}
private void disposeNonDefaultCaret() {
if (fNonDefaultCaretImage != null) {
fNonDefaultCaretImage.dispose();
fNonDefaultCaretImage= null;
}
if (fNonDefaultCaret != null) {
fNonDefaultCaret.dispose();
fNonDefaultCaret= null;
}
}
/**
* Handles a change of the editor's insert mode.
* Subclasses may extend.
*
* @since 2.0
*/
protected void handleInsertModeChanged() {
updateInsertModeAction();
updateCaret();
updateStatusField(ITextEditorActionConstants.STATUS_CATEGORY_INPUT_MODE);
}
private void updateInsertModeAction() {
// this may be called before the part is fully initialized (see configureInsertMode)
// drop out in this case.
if (getSite() == null)
return;
IAction action= getAction(ITextEditorActionConstants.TOGGLE_INSERT_MODE);
if (action != null) {
action.setEnabled(!fIsOverwriting);
action.setChecked(fInsertMode == SMART_INSERT);
}
}
/**
* Handles a potential change of the cursor position.
* Subclasses may extend.
*
* @since 2.0
*/
protected void handleCursorPositionChanged() {
updateStatusField(ITextEditorActionConstants.STATUS_CATEGORY_INPUT_POSITION);
}
/**
* Updates the status fields for the given category.
*
* @param category the category
* @since 2.0
*/
protected void updateStatusField(String category) {
if (category == null)
return;
IStatusField field= getStatusField(category);
if (field != null) {
String text= null;
if (ITextEditorActionConstants.STATUS_CATEGORY_INPUT_POSITION.equals(category))
text= getCursorPosition();
else if (ITextEditorActionConstants.STATUS_CATEGORY_ELEMENT_STATE.equals(category))
text= isEditorInputReadOnly() ? fReadOnlyLabel : fWritableLabel;
else if (ITextEditorActionConstants.STATUS_CATEGORY_INPUT_MODE.equals(category)) {
InsertMode mode= getInsertMode();
if (fIsOverwriting)
text= fOverwriteModeLabel;
else if (INSERT == mode)
text= fInsertModeLabel;
else if (SMART_INSERT == mode)
text= fSmartInsertModeLabel;
}
field.setText(text == null ? fErrorLabel : text);
}
}
/**
* Updates all status fields.
*
* @since 2.0
*/
protected void updateStatusFields() {
if (fStatusFields != null) {
Iterator<String> e= fStatusFields.keySet().iterator();
while (e.hasNext())
updateStatusField(e.next());
}
}
/**
* Returns a description of the cursor position.
*
* @return a description of the cursor position
* @since 2.0
*/
protected String getCursorPosition() {
if (fSourceViewer == null)
return fErrorLabel;
StyledText styledText= fSourceViewer.getTextWidget();
int caret= widgetOffset2ModelOffset(fSourceViewer, styledText.getCaretOffset());
IDocument document= fSourceViewer.getDocument();
if (document == null)
return fErrorLabel;
try {
int line= document.getLineOfOffset(caret);
int lineOffset= document.getLineOffset(line);
int tabWidth= styledText.getTabs();
int column= 0;
for (int i= lineOffset; i < caret; i++)
if ('\t' == document.getChar(i))
column += tabWidth - (tabWidth == 0 ? 0 : column % tabWidth);
else
column++;
fLineLabel.fValue= line + 1;
fColumnLabel.fValue= column + 1;
boolean showSelection = getPreferenceStore().getBoolean(PREFERENCE_SHOW_SELECTION_SIZE);
boolean showOffset = getPreferenceStore().getBoolean(PREFERENCE_SHOW_CARET_OFFSET);
Point selectedRange = fSourceViewer.getSelectedRange();
int selectionLength = selectedRange != null ? selectedRange.y : 0;
fOffsetLabel.fValue = selectionLength == 0 ? caret : selectionLength;
showSelection = showSelection && selectionLength > 0;
if (!showSelection) {
if (!showOffset) {
// shows line : column
return NLSUtility.format(fPositionLabelPattern, fPositionLabelPatternArguments);
}
// shows line : column : offset
return NLSUtility.format(EditorMessages.Editor_statusline_position_pattern_offset,
fPositionLabelPatternArguments);
}
// To show *right* selection, we first need to know if we are in the
// block selection mode or not
if (isBlockSelectionModeSupported() && isBlockSelectionModeEnabled()) {
BlockTextSelection block = (BlockTextSelection) fSourceViewer.getSelectionProvider().getSelection();
AtomicInteger sum = new AtomicInteger();
Arrays.asList(block.getRegions()).forEach(r -> sum.addAndGet(r.getLength()));
fOffsetLabel.fValue = sum.intValue();
}
// shows line : column [selection size]
return NLSUtility.format(EditorMessages.Editor_statusline_position_pattern_selection,
fPositionLabelPatternArguments);
} catch (BadLocationException x) {
return fErrorLabel;
}
}
@Override
public boolean isEditorInputReadOnly() {
IDocumentProvider provider= getDocumentProvider();
if (provider instanceof IDocumentProviderExtension) {
IDocumentProviderExtension extension= (IDocumentProviderExtension) provider;
return extension.isReadOnly(getEditorInput());
}
return true;
}
@Override
public boolean isEditorInputModifiable() {
IDocumentProvider provider= getDocumentProvider();
if (provider instanceof IDocumentProviderExtension) {
IDocumentProviderExtension extension= (IDocumentProviderExtension) provider;
return extension.isModifiable(getEditorInput());
}
return true;
}
@Override
public void addRulerContextMenuListener(IMenuListener listener) {
fRulerContextMenuListeners.add(listener);
}
@Override
public void removeRulerContextMenuListener(IMenuListener listener) {
fRulerContextMenuListeners.remove(listener);
}
/**
* Returns whether this editor can handle the move of the original element
* so that it ends up being the moved element. By default this method
* returns <code>true</code>. Subclasses may reimplement.
*
* @param originalElement the original element
* @param movedElement the moved element
* @return whether this editor can handle the move of the original element
* so that it ends up being the moved element
* @since 2.0
*/
protected boolean canHandleMove(IEditorInput originalElement, IEditorInput movedElement) {
return true;
}
/**
* Returns the offset of the given source viewer's document that corresponds
* to the given widget offset or <code>-1</code> if there is no such offset.
*
* @param viewer the source viewer
* @param widgetOffset the widget offset
* @return the corresponding offset in the source viewer's document or <code>-1</code>
* @since 2.1
*/
protected static final int widgetOffset2ModelOffset(ISourceViewer viewer, int widgetOffset) {
if (viewer instanceof ITextViewerExtension5) {
ITextViewerExtension5 extension= (ITextViewerExtension5) viewer;
return extension.widgetOffset2ModelOffset(widgetOffset);
}
return widgetOffset + viewer.getVisibleRegion().getOffset();
}
/**
* Returns the offset of the given source viewer's text widget that corresponds
* to the given model offset or <code>-1</code> if there is no such offset.
*
* @param viewer the source viewer
* @param modelOffset the model offset
* @return the corresponding offset in the source viewer's text widget or <code>-1</code>
* @since 3.0
*/
protected static final int modelOffset2WidgetOffset(ISourceViewer viewer, int modelOffset) {
if (viewer instanceof ITextViewerExtension5) {
ITextViewerExtension5 extension= (ITextViewerExtension5) viewer;
return extension.modelOffset2WidgetOffset(modelOffset);
}
return modelOffset - viewer.getVisibleRegion().getOffset();
}
/**
* Returns the minimal region of the given source viewer's document that completely
* comprises everything that is visible in the viewer's widget.
*
* @param viewer the viewer go return the coverage for
* @return the minimal region of the source viewer's document comprising the contents of the viewer's widget
* @since 2.1
*/
protected static final IRegion getCoverage(ISourceViewer viewer) {
if (viewer instanceof ITextViewerExtension5) {
ITextViewerExtension5 extension= (ITextViewerExtension5) viewer;
return extension.getModelCoverage();
}
return viewer.getVisibleRegion();
}
/**
* Tells whether the given region is visible in the given source viewer.
*
* @param viewer the source viewer
* @param offset the offset of the region
* @param length the length of the region
* @return <code>true</code> if visible
* @since 2.1
*/
protected static final boolean isVisible(ISourceViewer viewer, int offset, int length) {
if (viewer instanceof ITextViewerExtension5) {
ITextViewerExtension5 extension= (ITextViewerExtension5) viewer;
IRegion overlap= extension.modelRange2WidgetRange(new Region(offset, length));
return overlap != null;
}
return viewer.overlapsWithVisibleRegion(offset, length);
}
@Override
public void showChangeInformation(boolean show) {
// do nothing
}
@Override
public boolean isChangeInformationShowing() {
return false;
}
/**
* Sets the given message as error message to this editor's status line.
*
* @param message message to be set
* @since 3.2
*/
protected void setStatusLineErrorMessage(String message) {
IEditorStatusLine statusLine= getAdapter(IEditorStatusLine.class);
if (statusLine != null)
statusLine.setMessage(true, message, null);
}
/**
* Sets the given message as message to this editor's status line.
*
* @param message message to be set
* @since 3.2
*/
protected void setStatusLineMessage(String message) {
IEditorStatusLine statusLine= getAdapter(IEditorStatusLine.class);
if (statusLine != null)
statusLine.setMessage(false, message, null);
}
/**
* Jumps to the next annotation according to the given direction.
*
* @param forward <code>true</code> if search direction is forward, <code>false</code> if backward
* @return the selected annotation or <code>null</code> if none
* @see #isNavigationTarget(Annotation)
* @see #findAnnotation(int, int, boolean, Position)
* @since 3.2
*/
@Override
public Annotation gotoAnnotation(boolean forward) {
ITextSelection selection= (ITextSelection) getSelectionProvider().getSelection();
Position position= new Position(0, 0);
Annotation annotation= findAnnotation(selection.getOffset(), selection.getLength(), forward, position);
setStatusLineErrorMessage(null);
setStatusLineMessage(null);
if (annotation != null) {
selectAndReveal(position.getOffset(), position.getLength());
setStatusLineMessage(annotation.getText());
}
return annotation;
}
/**
* Returns the annotation closest to the given range respecting the given
* direction. If an annotation is found, the annotations current position
* is copied into the provided annotation position.
*
* @param offset the region offset
* @param length the region length
* @param forward <code>true</code> for forwards, <code>false</code> for backward
* @param annotationPosition the position of the found annotation
* @return the found annotation
* @since 3.2
*/
protected Annotation findAnnotation(final int offset, final int length, boolean forward, Position annotationPosition) {
Annotation nextAnnotation= null;
Position nextAnnotationPosition= null;
Annotation containingAnnotation= null;
Position containingAnnotationPosition= null;
boolean currentAnnotation= false;
IDocument document= getDocumentProvider().getDocument(getEditorInput());
int endOfDocument = 0;
if (document != null) {
endOfDocument = document.getLength();
}
int distance= Integer.MAX_VALUE;
IAnnotationModel model= getDocumentProvider().getAnnotationModel(getEditorInput());
Iterator<Annotation> e= model.getAnnotationIterator();
while (e.hasNext()) {
Annotation a= e.next();
if (!isNavigationTarget(a))
continue;
Position p= model.getPosition(a);
if (p == null)
continue;
if (forward && p.offset == offset || !forward && p.offset + p.getLength() == offset + length) {// || p.includes(offset)) {
if (containingAnnotation == null || (forward && p.length >= containingAnnotationPosition.length || !forward && p.length >= containingAnnotationPosition.length)) {
containingAnnotation= a;
containingAnnotationPosition= p;
currentAnnotation= p.length == length;
}
} else {
int currentDistance= 0;
if (forward) {
currentDistance= p.getOffset() - offset;
if (currentDistance < 0)
currentDistance= endOfDocument + currentDistance;
if (currentDistance < distance || currentDistance == distance && p.length < nextAnnotationPosition.length) {
distance= currentDistance;
nextAnnotation= a;
nextAnnotationPosition= p;
}
} else {
currentDistance= offset + length - (p.getOffset() + p.length);
if (currentDistance < 0)
currentDistance= endOfDocument + currentDistance;
if (currentDistance < distance || currentDistance == distance && p.length < nextAnnotationPosition.length) {
distance= currentDistance;
nextAnnotation= a;
nextAnnotationPosition= p;
}
}
}
}
if (containingAnnotationPosition != null && (!currentAnnotation || nextAnnotation == null)) {
annotationPosition.setOffset(containingAnnotationPosition.getOffset());
annotationPosition.setLength(containingAnnotationPosition.getLength());
return containingAnnotation;
}
if (nextAnnotationPosition != null) {
annotationPosition.setOffset(nextAnnotationPosition.getOffset());
annotationPosition.setLength(nextAnnotationPosition.getLength());
}
return nextAnnotation;
}
/**
* Returns whether the given annotation is configured as a target for the
* "Go to Next/Previous Annotation" actions.
* <p>
* Per default every annotation is a target.
* </p>
*
* @param annotation the annotation
* @return <code>true</code> if this is a target, <code>false</code> otherwise
* @since 3.2
*/
protected boolean isNavigationTarget(Annotation annotation) {
return true;
}
@Override
public void showRevisionInformation(RevisionInformation info, String quickDiffProviderId) {
// no implementation
}
@Override
public void restoreState(IMemento memento) {
fMementoToRestore= memento;
}
@Override
public void saveState(IMemento memento) {
ISelection selection= doGetSelection();
if (selection instanceof ITextSelection) {
memento.putInteger(TAG_SELECTION_OFFSET, ((ITextSelection)selection).getOffset());
memento.putInteger(TAG_SELECTION_LENGTH, ((ITextSelection)selection).getLength());
}
final StyledText textWidget= fSourceViewer.getTextWidget();
memento.putInteger(TAG_SELECTION_TOP_PIXEL, textWidget.getTopPixel());
memento.putInteger(TAG_SELECTION_HORIZONTAL_PIXEL, textWidget.getHorizontalPixel());
}
/**
* Returns whether the given memento contains saved state
* <p>
* Subclasses may extend or override this method.</p>
*
* @param memento the saved state of this editor
* @return <code>true</code> if the given memento contains saved state
* @since 3.3
*/
protected boolean containsSavedState(IMemento memento) {
return memento.getInteger(TAG_SELECTION_OFFSET) != null && memento.getInteger(TAG_SELECTION_LENGTH) != null;
}
/**
* Restores this editor's state using the given memento.
* <p>
* Subclasses may extend or override this method.</p>
*
* @param memento the saved state of this editor
* @since 3.3
*/
protected void doRestoreState(IMemento memento) {
Integer offset= memento.getInteger(TAG_SELECTION_OFFSET);
if (offset == null)
return;
Integer length= memento.getInteger(TAG_SELECTION_LENGTH);
if (length == null)
return;
doSetSelection(new TextSelection(offset.intValue(), length.intValue()));
final StyledText textWidget= fSourceViewer.getTextWidget();
Integer topPixel= memento.getInteger(TAG_SELECTION_TOP_PIXEL);
if (topPixel != null)
textWidget.setTopPixel(topPixel.intValue());
Integer horizontalPixel= memento.getInteger(TAG_SELECTION_HORIZONTAL_PIXEL);
if (horizontalPixel != null)
textWidget.setHorizontalPixel(horizontalPixel.intValue());
}
@Override
public Saveable[] getSaveables() {
if (fSavable == null)
fSavable= new TextEditorSavable(this);
return new Saveable[] { fSavable };
}
@Override
public Saveable[] getActiveSaveables() {
return getSaveables();
}
/**
* This text editor's savable.
*
* @since 3.3
*/
protected static class TextEditorSavable extends Saveable {
/** The cached editor. */
private ITextEditor fTextEditor;
/** The cached editor input. */
private IEditorInput fEditorInput;
/** The cached document. */
private IDocument fDocument;
/**
* Creates a new savable for this text editor.
*
* @param textEditor the text editor
*/
public TextEditorSavable(ITextEditor textEditor) {
Assert.isLegal(textEditor != null);
fTextEditor= textEditor;
fEditorInput= fTextEditor.getEditorInput();
Assert.isLegal(fEditorInput != null);
}
/**
* Disconnects the editor from this savable.
*/
public void disconnectEditor() {
getAdapter(IDocument.class); // make sure the document is cached
fTextEditor= null;
}
@Override
public String getName() {
return fEditorInput.getName();
}
@Override
public String getToolTipText() {
return fEditorInput.getToolTipText();
}
@Override
public ImageDescriptor getImageDescriptor() {
return fEditorInput.getImageDescriptor();
}
@Override
public void doSave(IProgressMonitor monitor) throws CoreException {
try {
fTextEditor.doSave(monitor);
} catch (NullPointerException e) {
// This should not happen. Code added to handle the below bug.
// https://bugs.eclipse.org/bugs/show_bug.cgi?id=550336
Bundle bundle = Platform.getBundle(PlatformUI.PLUGIN_ID);
ILog log = Platform.getLog(bundle);
Status status = new Status(IStatus.ERROR, TextEditorPlugin.PLUGIN_ID, null, e);
log.log(status);
}
}
@Override
public boolean isDirty() {
try {
return fTextEditor.isDirty();
} catch (NullPointerException e) {
// This should not happen. Code added to handle the below bug.
// https://bugs.eclipse.org/bugs/show_bug.cgi?id=550336
Bundle bundle = Platform.getBundle(PlatformUI.PLUGIN_ID);
ILog log = Platform.getLog(bundle);
Status status = new Status(IStatus.ERROR, TextEditorPlugin.PLUGIN_ID, null, e);
log.log(status);
return false;
}
}
/*
* @see org.eclipse.ui.Saveable#supportsBackgroundSave()
*/
public boolean supportsBackgroundSave() {
return false;
}
@Override
public int hashCode() {
Object document= getAdapter(IDocument.class);
if (document == null)
return 0;
return document.hashCode();
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (!(obj instanceof Saveable))
return false;
Object thisDocument= getAdapter(IDocument.class);
Object otherDocument= ((Saveable)obj).getAdapter(IDocument.class);
if (thisDocument == null && otherDocument == null)
return true;
return thisDocument != null && thisDocument.equals(otherDocument);
}
/**
* Explicit comment needed to suppress wrong warning caused by
* http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4848177
*
* @see org.eclipse.ui.Saveable#getAdapter(java.lang.Class)
*/
@SuppressWarnings("unchecked")
@Override
public <T> T getAdapter(Class<T> adapter) {
if (adapter == IDocument.class) {
if (fDocument == null) {
IDocumentProvider documentProvider= fTextEditor.getDocumentProvider();
if (documentProvider != null)
fDocument= documentProvider.getDocument(fEditorInput);
}
return (T) fDocument;
}
return super.getAdapter(adapter);
}
}
//---- Tabs to spaces conversion support ------------------
/**
* Installs a tabs to spaces converter.
*
* <p>Subclasses may extend or override this method.</p>
*
* @since 3.3
*/
protected void installTabsToSpacesConverter() {
SourceViewerConfiguration config= getSourceViewerConfiguration();
if (config != null && fSourceViewer instanceof ITextViewerExtension7) {
int tabWidth= config.getTabWidth(fSourceViewer);
TabsToSpacesConverter tabToSpacesConverter= new TabsToSpacesConverter();
tabToSpacesConverter.setLineTracker(new DefaultLineTracker());
tabToSpacesConverter.setNumberOfSpacesPerTab(tabWidth);
((ITextViewerExtension7)fSourceViewer).setTabsToSpacesConverter(tabToSpacesConverter);
updateIndentPrefixes();
}
}
/**
* Installs a tabs to spaces converter.
*
* <p>Subclasses may extend or override this method.</p>
*
* @since 3.3
*/
protected void uninstallTabsToSpacesConverter() {
if (fSourceViewer instanceof ITextViewerExtension7) {
((ITextViewerExtension7)fSourceViewer).setTabsToSpacesConverter(null);
if (fSourceViewer.getTextWidget() != null)
updateIndentPrefixes();
}
}
/**
* Tells whether tabs should be converted to
* spaces while editing inside this editor.
*
* <p>Subclasses may override this method.</p>
*
* @return <code>true</code> if tabs should be converted to spaces
* @since 3.3
*/
protected boolean isTabsToSpacesConversionEnabled() {
return false;
}
/**
* Updates the source viewer's indent prefixes with
* the values provided by the source viewer configuration.
*
* @since 3.3
*/
protected final void updateIndentPrefixes() {
SourceViewerConfiguration configuration= getSourceViewerConfiguration();
String[] types= configuration.getConfiguredContentTypes(fSourceViewer);
for (int i= 0; i < types.length; i++) {
String[] prefixes= configuration.getIndentPrefixes(fSourceViewer, types[i]);
if (prefixes != null && prefixes.length > 0)
fSourceViewer.setIndentPrefixes(prefixes, types[i]);
}
}
/**
* Tells whether selection mode is supported.
* <p>
* By default block selection mode is supported. Subclasses may override this method to disable
* it.
* </p>
*
* @return <code>true</code> if block selection mode is supported, <code>false</code> otherwise
* @since 3.5
*/
protected boolean isBlockSelectionModeSupported() {
return true;
}
/**
* @see org.eclipse.ui.texteditor.ITextEditorExtension5#isBlockSelectionModeEnabled()
* @since 3.5
*/
@Override
public final boolean isBlockSelectionModeEnabled() {
ISourceViewer viewer= getSourceViewer();
if (viewer != null) {
StyledText styledText= viewer.getTextWidget();
if (styledText != null)
return styledText.getBlockSelection();
}
return false;
}
/**
* @see org.eclipse.ui.texteditor.ITextEditorExtension5#setBlockSelectionMode(boolean)
* @since 3.5
*/
@Override
public void setBlockSelectionMode(boolean enable) {
if (!isBlockSelectionModeSupported())
return;
ISourceViewer viewer= getSourceViewer();
if (viewer != null) {
StyledText styledText= viewer.getTextWidget();
if (styledText != null) {
/*
* Font switching. block selection mode needs a monospace font.
* - set the font _before enabling_ block selection mode in order to maintain the
* selection
* - revert the font _after disabling_ block selection mode in order to maintain the
* selection
*/
if (enable) {
Font blockFont= JFaceResources.getFont(BLOCK_SELECTION_MODE_FONT);
Font normalFont= styledText.getFont();
if (!blockFont.equals(normalFont) && !normalFont.getFontData()[0].equals(blockFont.getFontData()[0])) {
setFont(viewer, blockFont);
disposeFont();
updateCaret();
}
// we must unset word wrap before we can set block selection
if (isWordWrapEnabled()) {
setWordWrap(false);
}
}
styledText.setBlockSelection(enable);
if (!enable) {
initializeViewerFont(viewer);
updateCaret();
}
}
}
}
/**
* Tells whether word wrap is supported.
* <p>
* By default word wrap is supported. Subclasses may override this method to disable
* it.
* </p>
*
* @return <code>true</code> if word wrap is supported, <code>false</code> otherwise
* @since 3.10
*/
protected boolean isWordWrapSupported() {
return true;
}
/**
* <code>true</code> if word wrap is supported and enabled, <code>false</code> otherwise
* @return the receiver's word wrap state if word wrap is supported
* @since 3.10
* @see AbstractTextEditor#isWordWrapSupported()
*/
@Override
public final boolean isWordWrapEnabled() {
if (!isWordWrapSupported()) {
return false;
}
ISourceViewer viewer= getSourceViewer();
if (viewer != null) {
StyledText styledText= viewer.getTextWidget();
if (styledText != null) {
return styledText.getWordWrap();
}
}
return false;
}
/**
* @see org.eclipse.ui.texteditor.ITextEditorExtension6#setWordWrap(boolean)
* @since 3.10
*/
@Override
public void setWordWrap(boolean enable) {
if (!isWordWrapSupported() || isWordWrapEnabled() == enable) {
return;
}
ISourceViewer viewer= getSourceViewer();
if (viewer != null) {
StyledText styledText= viewer.getTextWidget();
if (styledText != null) {
if (isBlockSelectionModeEnabled()) {
setBlockSelectionMode(false);
}
styledText.setWordWrap(enable);
if (fVerticalRuler != null) {
// update ruler layout so that it can consider
// changed horizontal scrollbar visibility
boolean updated= false;
if (viewer instanceof ITextViewerExtension) {
Control control= ((ITextViewerExtension)viewer).getControl();
if (control instanceof Composite) {
((Composite)control).layout();
updated= true;
}
}
if (!updated) {
fVerticalRuler.update();
}
}
}
}
}
/**
* Returns the initial word wrap status.
*
* @return initial word wrap status
* @since 3.10
*/
protected boolean getInitialWordWrapStatus() {
IPreferenceStore store= getPreferenceStore();
if (store == null) {
return false;
}
return store.getBoolean(PREFERENCE_WORD_WRAP_ENABLED);
}
}