blob: 84ee242fe956d7ca889b5ac6742d40705eef5fae [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2017 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.ui.texteditor;
import java.io.File;
import java.lang.reflect.InvocationTargetException;
import java.nio.charset.Charset;
import java.nio.charset.CharsetEncoder;
import java.nio.charset.IllegalCharsetNameException;
import java.nio.charset.UnsupportedCharsetException;
import java.util.Iterator;
import java.util.List;
import com.ibm.icu.text.BreakIterator;
import com.ibm.icu.text.MessageFormat;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.StyledText;
import org.eclipse.swt.custom.StyledTextPrintOptions;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.FileDialog;
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.IUndoContext;
import org.eclipse.core.filesystem.EFS;
import org.eclipse.core.filesystem.IFileStore;
import org.eclipse.core.filesystem.URIUtil;
import org.eclipse.core.runtime.Assert;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Platform;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IMarker;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IResourceStatus;
import org.eclipse.core.resources.IWorkspace;
import org.eclipse.core.resources.IWorkspaceRoot;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.filebuffers.FileBuffers;
import org.eclipse.core.filebuffers.IFileBufferStatusCodes;
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.MenuManager;
import org.eclipse.jface.action.Separator;
import org.eclipse.jface.dialogs.ErrorDialog;
import org.eclipse.jface.dialogs.IDialogConstants;
import org.eclipse.jface.dialogs.IMessageProvider;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.dialogs.MessageDialogWithToggle;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.jface.util.PropertyChangeEvent;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.ISelectionProvider;
import org.eclipse.jface.window.Window;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.ITextSelection;
import org.eclipse.jface.text.ITextViewerExtension6;
import org.eclipse.jface.text.ITextViewerExtension8;
import org.eclipse.jface.text.JFaceTextUtil;
import org.eclipse.jface.text.Position;
import org.eclipse.jface.text.hyperlink.IHyperlinkDetector;
import org.eclipse.jface.text.revisions.IRevisionRulerColumn;
import org.eclipse.jface.text.revisions.IRevisionRulerColumnExtension;
import org.eclipse.jface.text.revisions.IRevisionRulerColumnExtension.RenderingMode;
import org.eclipse.jface.text.revisions.RevisionInformation;
import org.eclipse.jface.text.source.Annotation;
import org.eclipse.jface.text.source.AnnotationRulerColumn;
import org.eclipse.jface.text.source.ChangeRulerColumn;
import org.eclipse.jface.text.source.CompositeRuler;
import org.eclipse.jface.text.source.IAnnotationAccess;
import org.eclipse.jface.text.source.IAnnotationAccessExtension2;
import org.eclipse.jface.text.source.IAnnotationHover;
import org.eclipse.jface.text.source.IAnnotationModel;
import org.eclipse.jface.text.source.IChangeRulerColumn;
import org.eclipse.jface.text.source.IOverviewRuler;
import org.eclipse.jface.text.source.IOverviewRulerExtension;
import org.eclipse.jface.text.source.ISharedTextColors;
import org.eclipse.jface.text.source.ISourceViewer;
import org.eclipse.jface.text.source.ISourceViewerExtension;
import org.eclipse.jface.text.source.ISourceViewerExtension3;
import org.eclipse.jface.text.source.IVerticalRuler;
import org.eclipse.jface.text.source.IVerticalRulerColumn;
import org.eclipse.jface.text.source.LineChangeHover;
import org.eclipse.jface.text.source.LineNumberChangeRulerColumn;
import org.eclipse.jface.text.source.LineNumberRulerColumn;
import org.eclipse.jface.text.source.OverviewRuler;
import org.eclipse.jface.text.source.SourceViewer;
import org.eclipse.ui.IEditorDescriptor;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.IFileEditorInput;
import org.eclipse.ui.IURIEditorInput;
import org.eclipse.ui.IWorkbenchActionConstants;
import org.eclipse.ui.IWorkbenchCommandConstants;
import org.eclipse.ui.IWorkbenchPage;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.actions.ContributionItemFactory;
import org.eclipse.ui.actions.OpenWithMenu;
import org.eclipse.ui.actions.WorkspaceModifyOperation;
import org.eclipse.ui.dialogs.PreferencesUtil;
import org.eclipse.ui.dialogs.SaveAsDialog;
import org.eclipse.ui.ide.FileStoreEditorInput;
import org.eclipse.ui.ide.IDEActionFactory;
import org.eclipse.ui.ide.IGotoMarker;
import org.eclipse.ui.internal.editors.quickdiff.CompositeRevertAction;
import org.eclipse.ui.internal.editors.quickdiff.RestoreAction;
import org.eclipse.ui.internal.editors.quickdiff.RevertBlockAction;
import org.eclipse.ui.internal.editors.quickdiff.RevertLineAction;
import org.eclipse.ui.internal.editors.quickdiff.RevertSelectionAction;
import org.eclipse.ui.internal.editors.text.EditorsPlugin;
import org.eclipse.ui.internal.editors.text.NLSUtility;
import org.eclipse.ui.internal.editors.text.RefreshEditorAction;
import org.eclipse.ui.internal.texteditor.AnnotationColumn;
import org.eclipse.ui.internal.texteditor.BooleanPreferenceToggleAction;
import org.eclipse.ui.internal.texteditor.FocusedInformationPresenter;
import org.eclipse.ui.internal.texteditor.LineNumberColumn;
import org.eclipse.ui.internal.texteditor.TextChangeHover;
import org.eclipse.ui.keys.IBindingService;
import org.eclipse.ui.operations.NonLocalUndoUserApprover;
import org.eclipse.ui.part.FileEditorInput;
import org.eclipse.ui.part.IShowInSource;
import org.eclipse.ui.part.ShowInContext;
import org.eclipse.ui.views.markers.MarkerViewUtil;
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;
import org.eclipse.ui.editors.text.DefaultEncodingSupport;
import org.eclipse.ui.editors.text.EditorsUI;
import org.eclipse.ui.editors.text.ForwardingDocumentProvider;
import org.eclipse.ui.editors.text.IEncodingSupport;
import org.eclipse.ui.editors.text.IStorageDocumentProvider;
import org.eclipse.ui.editors.text.ITextEditorHelpContextIds;
/**
* An intermediate editor comprising functionality not present in the leaner <code>AbstractTextEditor</code>,
* but used in many heavy weight (and especially source editing) editors, such as line numbers,
* change ruler, overview ruler, print margins, current line highlighting, etc.
*
* @since 3.0
*/
public abstract class AbstractDecoratedTextEditor extends StatusTextEditor {
/**
* Preference key for showing the line number ruler.
*/
private final static String LINE_NUMBER_RULER= AbstractDecoratedTextEditorPreferenceConstants.EDITOR_LINE_NUMBER_RULER;
/**
* Preference key for showing the overview ruler.
*/
private final static String OVERVIEW_RULER= AbstractDecoratedTextEditorPreferenceConstants.EDITOR_OVERVIEW_RULER;
/**
* Preference key for highlighting current line.
*/
private final static String CURRENT_LINE= AbstractDecoratedTextEditorPreferenceConstants.EDITOR_CURRENT_LINE;
/**
* Preference key for highlight color of current line.
*/
private final static String CURRENT_LINE_COLOR= AbstractDecoratedTextEditorPreferenceConstants.EDITOR_CURRENT_LINE_COLOR;
/**
* Preference key for showing print margin ruler.
*/
private final static String PRINT_MARGIN= AbstractDecoratedTextEditorPreferenceConstants.EDITOR_PRINT_MARGIN;
/**
* Preference key for print margin ruler color.
*/
private final static String PRINT_MARGIN_COLOR= AbstractDecoratedTextEditorPreferenceConstants.EDITOR_PRINT_MARGIN_COLOR;
/**
* Preference key for print margin ruler column.
*/
private final static String PRINT_MARGIN_COLUMN= AbstractDecoratedTextEditorPreferenceConstants.EDITOR_PRINT_MARGIN_COLUMN;
/**
* Preference key to get whether the overwrite mode is disabled.
* @since 3.1
*/
private final static String DISABLE_OVERWRITE_MODE= AbstractDecoratedTextEditorPreferenceConstants.EDITOR_DISABLE_OVERWRITE_MODE;
/**
* Menu id for the overview ruler context menu.
*
* @since 3.4
*/
public final static String DEFAULT_OVERVIEW_RULER_CONTEXT_MENU_ID= "#OverviewRulerContext"; //$NON-NLS-1$
/**
* Preference key that controls whether to use saturated colors in the overview ruler.
*
* @since 3.8
*/
private static final String USE_SATURATED_COLORS_IN_OVERVIEW_RULER= AbstractDecoratedTextEditorPreferenceConstants.USE_SATURATED_COLORS_IN_OVERVIEW_RULER;
/**
* Adapter class for <code>IGotoMarker</code>.
*/
private class GotoMarkerAdapter implements IGotoMarker {
@Override
public void gotoMarker(IMarker marker) {
AbstractDecoratedTextEditor.this.gotoMarker(marker);
}
}
/**
* The annotation preferences.
*/
private MarkerAnnotationPreferences fAnnotationPreferences;
/**
* The overview ruler of this editor.
*
* <p>This field should not be referenced by subclasses. It is <code>protected</code> for API
* compatibility reasons and will be made <code>private</code> soon. Use
* {@link #getOverviewRuler()} instead.</p>
*/
protected IOverviewRuler fOverviewRuler;
/**
* The overview ruler's context menu id.
*
* @since 3.4
*/
private String fOverviewRulerContextMenuId;
/**
* Helper for accessing annotation from the perspective of this editor.
*
* <p>This field should not be referenced by subclasses. It is <code>protected</code> for API
* compatibility reasons and will be made <code>private</code> soon. Use
* {@link #getAnnotationAccess()} instead.</p>
*/
protected IAnnotationAccess fAnnotationAccess;
/**
* Helper for managing the decoration support of this editor's viewer.
*
* <p>This field should not be referenced by subclasses. It is <code>protected</code> for API
* compatibility reasons and will be made <code>private</code> soon. Use
* {@link #getSourceViewerDecorationSupport(ISourceViewer)} instead.</p>
*/
protected SourceViewerDecorationSupport fSourceViewerDecorationSupport;
/**
* The line number column.
*
* <p>This field should not be referenced by subclasses. It is <code>protected</code> for API
* compatibility reasons and will be made <code>private</code> soon. Use
* {@link AbstractTextEditor#getVerticalRuler()} to access the vertical bar instead.</p>
*/
protected LineNumberRulerColumn fLineNumberRulerColumn;
/**
* The delegating line number ruler contribution.
* @since 3.3
*/
private LineNumberColumn fLineColumn;
/**
* The editor's implicit document provider.
*/
private IDocumentProvider fImplicitDocumentProvider;
/**
* The editor's goto marker adapter.
*/
private Object fGotoMarkerAdapter= new GotoMarkerAdapter();
/**
* Indicates whether this editor is updating views that show markers.
* @see #updateMarkerViews(Annotation)
* @since 3.2
*/
protected boolean fIsUpdatingMarkerViews= false;
/**
* Indicates whether it wants to update the marker views after a gotoMarker call.
* @see #updateMarkerViews(Annotation)
* @see #gotoMarker(IMarker)
* @since 3.6
*/
private boolean fIsComingFromGotoMarker= false;
/**
* Tells whether editing the current derived editor input is allowed.
* @since 3.3
*/
private boolean fIsEditingDerivedFileAllowed= true;
/**
* Tells whether the derived state has been validated.
* @since 3.3
*/
private boolean fIsDerivedStateValidated= false;
/**
* The focused information presenter, or <code>null</code> if not created yet.
* @since 3.5
*/
private FocusedInformationPresenter fInformationPresenter;
/*
* Workaround for IllegalAccessError thrown because we are accessing
* a protected method in a different bundle from an inner class.
* @since 3.3
*/
private IVerticalRuler internalGetVerticalRuler() {
return getVerticalRuler();
}
/**
* Creates a new text editor.
*
* @see #initializeEditor()
* @see #initializeKeyBindingScopes()
*/
public AbstractDecoratedTextEditor() {
super();
fAnnotationPreferences= EditorsPlugin.getDefault().getMarkerAnnotationPreferences();
setRangeIndicator(new DefaultRangeIndicator());
initializeKeyBindingScopes();
initializeEditor();
}
/**
* Initializes this editor. Subclasses may re-implement. If sub-classes do
* not change the contract, this method should not be extended, i.e. do not
* call <code>super.initializeEditor()</code> in order to avoid the
* temporary creation of objects that are immediately overwritten by
* subclasses.
*/
protected void initializeEditor() {
setPreferenceStore(EditorsPlugin.getDefault().getPreferenceStore());
}
/**
* Initializes the key binding scopes of this editor.
*/
protected void initializeKeyBindingScopes() {
setKeyBindingScopes(new String[] { "org.eclipse.ui.textEditorScope" }); //$NON-NLS-1$
}
@Override
public void dispose() {
if (fSourceViewerDecorationSupport != null) {
fSourceViewerDecorationSupport.dispose();
fSourceViewerDecorationSupport= null;
}
fAnnotationAccess= null;
fAnnotationPreferences= null;
fLineNumberRulerColumn= null;
fLineColumn= null;
if (fInformationPresenter != null) {
fInformationPresenter.uninstall();
fInformationPresenter= null;
}
super.dispose();
}
@Override
protected ISourceViewer createSourceViewer(Composite parent, IVerticalRuler ruler, int styles) {
fAnnotationAccess= getAnnotationAccess();
fOverviewRuler= createOverviewRuler(getSharedColors());
ISourceViewer viewer= new SourceViewer(parent, ruler, getOverviewRuler(), isOverviewRulerVisible(), styles);
// ensure decoration support has been created and configured.
getSourceViewerDecorationSupport(viewer);
return viewer;
}
protected ISharedTextColors getSharedColors() {
return EditorsPlugin.getDefault().getSharedTextColors();
}
protected IOverviewRuler createOverviewRuler(ISharedTextColors sharedColors) {
IOverviewRuler ruler= new OverviewRuler(getAnnotationAccess(), VERTICAL_RULER_WIDTH, sharedColors);
Iterator<AnnotationPreference> e= fAnnotationPreferences.getAnnotationPreferences().iterator();
while (e.hasNext()) {
AnnotationPreference preference= e.next();
if (preference.contributesToHeader())
ruler.addHeaderAnnotationType(preference.getAnnotationType());
}
return ruler;
}
/**
* Creates the annotation access for this editor.
*
* @return the created annotation access
*/
protected IAnnotationAccess createAnnotationAccess() {
return new DefaultMarkerAnnotationAccess();
}
/**
* Configures the decoration support for this editor's source viewer. Subclasses may override this
* method, but should call their superclass' implementation at some point.
*
* @param support the decoration support to configure
*/
protected void configureSourceViewerDecorationSupport(SourceViewerDecorationSupport support) {
Iterator<AnnotationPreference> e= fAnnotationPreferences.getAnnotationPreferences().iterator();
while (e.hasNext())
support.setAnnotationPreference(e.next());
support.setCursorLinePainterPreferenceKeys(CURRENT_LINE, CURRENT_LINE_COLOR);
support.setMarginPainterPreferenceKeys(PRINT_MARGIN, PRINT_MARGIN_COLOR, PRINT_MARGIN_COLUMN);
}
/*
* @see org.eclipse.ui.texteditor.AbstractTextEditor.createPartControl(Composite)
*/
@Override
public void createPartControl(Composite parent) {
super.createPartControl(parent);
if (fSourceViewerDecorationSupport != null)
fSourceViewerDecorationSupport.install(getPreferenceStore());
IColumnSupport columnSupport= getAdapter(IColumnSupport.class);
if (isLineNumberRulerVisible()) {
RulerColumnDescriptor lineNumberColumnDescriptor= RulerColumnRegistry.getDefault().getColumnDescriptor(LineNumberColumn.ID);
if (lineNumberColumnDescriptor != null)
columnSupport.setColumnVisible(lineNumberColumnDescriptor, true);
}
if (isPrefQuickDiffAlwaysOn())
showChangeInformation(true);
if (fOverviewRuler instanceof IOverviewRulerExtension)
((IOverviewRulerExtension)fOverviewRuler).setUseSaturatedColors(isPrefUseSaturatedColorsOn());
if (!isOverwriteModeEnabled())
enableOverwriteMode(false);
if (!isRangeIndicatorEnabled()) {
getSourceViewer().removeRangeIndication();
getSourceViewer().setRangeIndicator(null);
}
// Assign the quick assist assistant to the annotation access.
ISourceViewer viewer= getSourceViewer();
if (fAnnotationAccess instanceof IAnnotationAccessExtension2 && viewer instanceof ISourceViewerExtension3)
((IAnnotationAccessExtension2)fAnnotationAccess).setQuickAssistAssistant(((ISourceViewerExtension3)viewer).getQuickAssistAssistant());
createOverviewRulerContextMenu();
}
/**
* Creates the context menu for the overview ruler.
* <p>
* Subclasses may extend or replace this method.
* </p>
*
* @since 3.4
*/
protected void createOverviewRulerContextMenu() {
if (fOverviewRulerContextMenuId == null)
fOverviewRulerContextMenuId= DEFAULT_OVERVIEW_RULER_CONTEXT_MENU_ID;
if (fOverviewRuler == null || fOverviewRuler.getControl() == null)
return;
MenuManager menuManager= new MenuManager(fOverviewRulerContextMenuId, fOverviewRulerContextMenuId);
menuManager.setRemoveAllWhenShown(true);
menuManager.addMenuListener(getContextMenuListener());
Menu menu= menuManager.createContextMenu(getOverviewRuler().getControl());
getOverviewRuler().getControl().setMenu(menu);
getEditorSite().registerContextMenu(fOverviewRulerContextMenuId, menuManager, getSelectionProvider(), false);
}
@Override
protected IMenuListener createContextMenuListener() {
final IMenuListener superListener= super.createContextMenuListener();
return new IMenuListener() {
@Override
public void menuAboutToShow(IMenuManager menu) {
if (!getOverviewRulerContextMenuId().equals(menu.getId())) {
superListener.menuAboutToShow(menu);
return;
}
setFocus();
overviewRulerContextMenuAboutToShow(menu);
}
};
}
@Override
protected Control createStatusControl(Composite parent, final IStatus status) {
Object adapter= getAdapter(IEncodingSupport.class);
DefaultEncodingSupport encodingSupport= null;
if (adapter instanceof DefaultEncodingSupport)
encodingSupport= (DefaultEncodingSupport)adapter;
if (encodingSupport == null || !encodingSupport.isEncodingError(status))
return super.createStatusControl(parent, status);
Shell shell= getSite().getShell();
Display display= shell.getDisplay();
Color bgColor= display.getSystemColor(SWT.COLOR_LIST_BACKGROUND);
Color fgColor= display.getSystemColor(SWT.COLOR_LIST_FOREGROUND);
Composite composite= new Composite(parent, SWT.NONE);
composite.setLayout(new GridLayout());
composite.setBackground(bgColor);
composite.setForeground(fgColor);
Control control= super.createStatusControl(composite, status);
control.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
Composite buttonComposite= new Composite(composite, SWT.NONE);
buttonComposite.setLayout(new GridLayout());
buttonComposite.setLayoutData(new GridData(GridData.FILL_BOTH));
buttonComposite.setBackground(bgColor);
buttonComposite.setForeground(fgColor);
encodingSupport.createStatusEncodingChangeControl(buttonComposite, status);
return composite;
}
/**
* Tells whether the overview ruler is visible.
*
* @return whether the overview ruler is visible
*/
protected boolean isOverviewRulerVisible() {
IPreferenceStore store= getPreferenceStore();
return store != null ? store.getBoolean(OVERVIEW_RULER) : false;
}
@Override
public void showChangeInformation(boolean show) {
if (show == isChangeInformationShowing())
return;
IColumnSupport columnSupport= getAdapter(IColumnSupport.class);
// only handle visibility of the combined column, but not the number/change only state
if (show && fLineColumn == null) {
RulerColumnDescriptor lineNumberColumnDescriptor= RulerColumnRegistry.getDefault().getColumnDescriptor(LineNumberColumn.ID);
if (lineNumberColumnDescriptor != null)
columnSupport.setColumnVisible(lineNumberColumnDescriptor, true);
} else if (!show && fLineColumn != null && !isLineNumberRulerVisible()) {
columnSupport.setColumnVisible(fLineColumn.getDescriptor(), false);
fLineColumn= null;
}
}
@Override
public boolean isChangeInformationShowing() {
return fLineColumn != null && fLineColumn.isShowingChangeInformation();
}
@Override
public void showRevisionInformation(RevisionInformation info, String quickDiffProviderId) {
if (info.getHoverControlCreator() == null)
info.setHoverControlCreator(new RevisionHoverInformationControlCreator(false));
if (info.getInformationPresenterControlCreator() == null)
info.setInformationPresenterControlCreator(new RevisionHoverInformationControlCreator(true));
showChangeInformation(true);
if (fLineColumn != null)
fLineColumn.showRevisionInformation(info, quickDiffProviderId);
}
/**
* Returns whether the line number ruler column should be
* visible according to the preference store settings. Subclasses may override this
* method to provide a custom preference setting.
*
* @return <code>true</code> if the line numbers should be visible
*/
protected boolean isLineNumberRulerVisible() {
IPreferenceStore store= getPreferenceStore();
return store != null ? store.getBoolean(LINE_NUMBER_RULER) : false;
}
/**
* Returns whether the overwrite mode is enabled according to the preference
* store settings. Subclasses may override this method to provide a custom
* preference setting.
*
* @return <code>true</code> if overwrite mode is enabled
* @since 3.1
*/
protected boolean isOverwriteModeEnabled() {
IPreferenceStore store= getPreferenceStore();
return store != null ? !store.getBoolean(DISABLE_OVERWRITE_MODE) : true;
}
/**
* Returns whether the range indicator is enabled according to the preference
* store settings. Subclasses may override this method to provide a custom
* preference setting.
*
* @return <code>true</code> if overwrite mode is enabled
* @since 3.1
*/
private boolean isRangeIndicatorEnabled() {
IPreferenceStore store= getPreferenceStore();
return store != null ? store.getBoolean(AbstractDecoratedTextEditorPreferenceConstants.SHOW_RANGE_INDICATOR) : true;
}
/**
* Returns whether quick diff info should be visible upon opening an editor
* according to the preference store settings.
*
* @return <code>true</code> if the line numbers should be visible
*/
protected boolean isPrefQuickDiffAlwaysOn() {
IPreferenceStore store= getPreferenceStore();
boolean setting= store != null ? store.getBoolean(AbstractDecoratedTextEditorPreferenceConstants.QUICK_DIFF_ALWAYS_ON) : false;
return setting && isEditorInputModifiable();
}
/**
* Checks if the preference to use saturated colors is enabled for the overview ruler.
*
* @return <code>true</code> if the saturated colors preference is enabled, <code>false</code>
* otherwise
* @since 3.8
*/
private boolean isPrefUseSaturatedColorsOn() {
IPreferenceStore store= getPreferenceStore();
return store != null ? store.getBoolean(USE_SATURATED_COLORS_IN_OVERVIEW_RULER) : false;
}
/**
* Initializes the given line number ruler column from the preference store.
*
* @param rulerColumn the ruler column to be initialized
*/
protected void initializeLineNumberRulerColumn(LineNumberRulerColumn rulerColumn) {
/*
* Left for compatibility. See LineNumberColumn.
*/
if (fLineColumn != null)
fLineColumn.initializeLineNumberRulerColumn(rulerColumn);
}
/**
* Creates a new line number ruler column that is appropriately initialized.
*
* @return the created line number column
*/
protected IVerticalRulerColumn createLineNumberRulerColumn() {
/*
* Left for compatibility. See LineNumberColumn.
*/
fLineNumberRulerColumn= new LineNumberChangeRulerColumn(getSharedColors());
((IChangeRulerColumn) fLineNumberRulerColumn).setHover(createChangeHover());
initializeLineNumberRulerColumn(fLineNumberRulerColumn);
return fLineNumberRulerColumn;
}
/**
* Creates and returns a <code>LineChangeHover</code> to be used on this editor's change
* ruler column. This default implementation returns a plain <code>LineChangeHover</code>.
* Subclasses may override.
*
* @return the change hover to be used by this editors quick diff display
*/
protected LineChangeHover createChangeHover() {
return new TextChangeHover();
}
/**
* Creates a new change ruler column for quick diff display independent of the
* line number ruler column
*
* @return a new change ruler column
* @deprecated as of 3.3. Not called any longer, replaced by {@link #createLineNumberRulerColumn()}
*/
@Deprecated
protected IChangeRulerColumn createChangeRulerColumn() {
/*
* Left for compatibility. See LineNumberColumn.
*/
return new ChangeRulerColumn(getSharedColors());
}
/**
* Returns {@link #createCompositeRuler()}. Subclasses should not override this method, but
* rather <code>createCompositeRuler</code> if they want to contribute their own vertical ruler
* implementation. If not an instance of {@link CompositeRuler} is returned, the built-in ruler
* columns (line numbers, annotations) will not work.
*
* <p>May become <code>final</code> in the future.</p>
*
* @see AbstractTextEditor#createVerticalRuler()
*/
@Override
protected IVerticalRuler createVerticalRuler() {
return createCompositeRuler();
}
/**
* Creates a composite ruler to be used as the vertical ruler by this editor.
* Subclasses may re-implement this method.
*
* @return the vertical ruler
*/
protected CompositeRuler createCompositeRuler() {
return new CompositeRuler();
}
/**
* Creates the annotation ruler column. Subclasses may re-implement or extend.
*
* @param ruler the composite ruler that the column will be added
* @return an annotation ruler column
* @since 3.2
*/
protected IVerticalRulerColumn createAnnotationRulerColumn(CompositeRuler ruler) {
return new AnnotationRulerColumn(VERTICAL_RULER_WIDTH, getAnnotationAccess());
}
@Override
protected final IColumnSupport createColumnSupport() {
return new ColumnSupport(this, RulerColumnRegistry.getDefault()) {
@Override
protected void initializeColumn(IContributedRulerColumn column) {
super.initializeColumn(column);
RulerColumnDescriptor descriptor= column.getDescriptor();
IVerticalRuler ruler= internalGetVerticalRuler();
if (ruler instanceof CompositeRuler) {
if (AnnotationColumn.ID.equals(descriptor.getId())) {
((AnnotationColumn)column).setDelegate(createAnnotationRulerColumn((CompositeRuler) ruler));
} else if (LineNumberColumn.ID.equals(descriptor.getId())) {
fLineColumn= ((LineNumberColumn) column);
fLineColumn.setForwarder(new LineNumberColumn.ICompatibilityForwarder() {
@Override
public IVerticalRulerColumn createLineNumberRulerColumn() {
return AbstractDecoratedTextEditor.this.createLineNumberRulerColumn();
}
@Override
public boolean isQuickDiffEnabled() {
return AbstractDecoratedTextEditor.this.isPrefQuickDiffAlwaysOn();
}
@Override
public boolean isLineNumberRulerVisible() {
return AbstractDecoratedTextEditor.this.isLineNumberRulerVisible();
}
});
}
}
}
@Override
public void dispose() {
fLineColumn= null;
super.dispose();
}
};
}
@Override
protected void handlePreferenceStoreChanged(PropertyChangeEvent event) {
try {
ISourceViewer sourceViewer= getSourceViewer();
if (sourceViewer == null)
return;
String property= event.getProperty();
if (fSourceViewerDecorationSupport != null && fOverviewRuler != null && OVERVIEW_RULER.equals(property)) {
if (isOverviewRulerVisible())
showOverviewRuler();
else
hideOverviewRuler();
return;
}
if (USE_SATURATED_COLORS_IN_OVERVIEW_RULER.equals(property)) {
if (fOverviewRuler instanceof IOverviewRulerExtension) {
((IOverviewRulerExtension)fOverviewRuler).setUseSaturatedColors(isPrefUseSaturatedColorsOn());
fOverviewRuler.update();
}
}
if (DISABLE_OVERWRITE_MODE.equals(property)) {
enableOverwriteMode(isOverwriteModeEnabled());
return;
}
if (LINE_NUMBER_RULER.equals(property)) {
// only handle visibility of the combined column, but not the number/change only state
IColumnSupport columnSupport= getAdapter(IColumnSupport.class);
if (isLineNumberRulerVisible() && fLineColumn == null) {
RulerColumnDescriptor lineNumberColumnDescriptor= RulerColumnRegistry.getDefault().getColumnDescriptor(LineNumberColumn.ID);
if (lineNumberColumnDescriptor != null)
columnSupport.setColumnVisible(lineNumberColumnDescriptor, true);
} else if (!isLineNumberRulerVisible() && fLineColumn != null && !fLineColumn.isShowingChangeInformation()) {
columnSupport.setColumnVisible(fLineColumn.getDescriptor(), false);
fLineColumn= null;
}
return;
}
if (AbstractDecoratedTextEditorPreferenceConstants.QUICK_DIFF_ALWAYS_ON.equals(property)) {
showChangeInformation(isPrefQuickDiffAlwaysOn());
}
if (AbstractDecoratedTextEditorPreferenceConstants.EDITOR_TAB_WIDTH.equals(property)) {
IPreferenceStore store= getPreferenceStore();
if (store != null)
sourceViewer.getTextWidget().setTabs(store.getInt(AbstractDecoratedTextEditorPreferenceConstants.EDITOR_TAB_WIDTH));
if (isTabsToSpacesConversionEnabled()) {
uninstallTabsToSpacesConverter();
installTabsToSpacesConverter();
}
return;
}
if (AbstractDecoratedTextEditorPreferenceConstants.EDITOR_SPACES_FOR_TABS.equals(property)) {
if (isTabsToSpacesConversionEnabled())
installTabsToSpacesConverter();
else
uninstallTabsToSpacesConverter();
return;
}
if (AbstractDecoratedTextEditorPreferenceConstants.EDITOR_UNDO_HISTORY_SIZE.equals(property) && sourceViewer instanceof ITextViewerExtension6) {
IPreferenceStore store= getPreferenceStore();
if (store != null)
((ITextViewerExtension6)sourceViewer).getUndoManager().setMaximalUndoLevel(store.getInt(AbstractDecoratedTextEditorPreferenceConstants.EDITOR_UNDO_HISTORY_SIZE));
return;
}
if (AbstractDecoratedTextEditorPreferenceConstants.SHOW_RANGE_INDICATOR.equals(property)) {
if (isRangeIndicatorEnabled()) {
getSourceViewer().setRangeIndicator(getRangeIndicator());
} else {
getSourceViewer().removeRangeIndication();
getSourceViewer().setRangeIndicator(null);
}
}
if (sourceViewer instanceof ITextViewerExtension6) {
HyperlinkDetectorDescriptor[] descriptor= EditorsUI.getHyperlinkDetectorRegistry().getHyperlinkDetectorDescriptors();
for (int i= 0; i < descriptor.length; i++) {
if (descriptor[i].getId().equals(property) || (descriptor[i].getId() + HyperlinkDetectorDescriptor.STATE_MASK_POSTFIX).equals(property)) {
IHyperlinkDetector[] detectors= getSourceViewerConfiguration().getHyperlinkDetectors(sourceViewer);
int stateMask= getSourceViewerConfiguration().getHyperlinkStateMask(sourceViewer);
ITextViewerExtension6 textViewer6= (ITextViewerExtension6)sourceViewer;
textViewer6.setHyperlinkDetectors(detectors, stateMask);
return;
}
}
}
} finally {
super.handlePreferenceStoreChanged(event);
}
}
/**
* Shows the overview ruler.
*/
protected void showOverviewRuler() {
if (fOverviewRuler != null) {
if (getSourceViewer() instanceof ISourceViewerExtension) {
((ISourceViewerExtension) getSourceViewer()).showAnnotationsOverview(true);
fSourceViewerDecorationSupport.updateOverviewDecorations();
}
}
}
/**
* Hides the overview ruler.
*/
protected void hideOverviewRuler() {
if (getSourceViewer() instanceof ISourceViewerExtension) {
fSourceViewerDecorationSupport.hideAnnotationOverview();
((ISourceViewerExtension) getSourceViewer()).showAnnotationsOverview(false);
}
}
/**
* Returns the annotation access.
*
* @return the annotation access
*/
protected IAnnotationAccess getAnnotationAccess() {
if (fAnnotationAccess == null)
fAnnotationAccess= createAnnotationAccess();
return fAnnotationAccess;
}
/**
* Returns the annotation preference lookup.
*
* @return the annotation preference lookup
*/
protected AnnotationPreferenceLookup getAnnotationPreferenceLookup() {
return EditorsPlugin.getDefault().getAnnotationPreferenceLookup();
}
/**
* Returns the overview ruler.
*
* @return the overview ruler
*/
protected IOverviewRuler getOverviewRuler() {
if (fOverviewRuler == null)
fOverviewRuler= createOverviewRuler(getSharedColors());
return fOverviewRuler;
}
/**
* Returns the source viewer decoration support.
*
* @param viewer the viewer for which to return a decoration support
* @return the source viewer decoration support
*/
protected SourceViewerDecorationSupport getSourceViewerDecorationSupport(ISourceViewer viewer) {
if (fSourceViewerDecorationSupport == null) {
fSourceViewerDecorationSupport= new SourceViewerDecorationSupport(viewer, getOverviewRuler(), getAnnotationAccess(), getSharedColors());
configureSourceViewerDecorationSupport(fSourceViewerDecorationSupport);
}
return fSourceViewerDecorationSupport;
}
/**
* Returns the annotation preferences.
*
* @return the annotation preferences
*/
protected MarkerAnnotationPreferences getAnnotationPreferences() {
return fAnnotationPreferences;
}
/**
* If the editor can be saved all marker ranges have been changed according to
* the text manipulations. However, those changes are not yet propagated to the
* marker manager. Thus, when opening a marker, the marker's position in the editor
* must be determined as it might differ from the position stated in the marker.
*
* @param marker the marker to go to
* @deprecated visibility will be reduced, use <code>getAdapter(IGotoMarker.class) for accessing this method</code>
*/
@Deprecated
public void gotoMarker(IMarker marker) {
if (fIsUpdatingMarkerViews)
return;
if (getSourceViewer() == null)
return;
int start= MarkerUtilities.getCharStart(marker);
int end= MarkerUtilities.getCharEnd(marker);
boolean selectLine= start < 0 || end < 0;
// look up the current range of the marker when the document has been edited
IAnnotationModel model= getDocumentProvider().getAnnotationModel(getEditorInput());
if (model instanceof AbstractMarkerAnnotationModel) {
AbstractMarkerAnnotationModel markerModel= (AbstractMarkerAnnotationModel) model;
Position pos= markerModel.getMarkerPosition(marker);
if (pos != null && !pos.isDeleted()) {
// use position instead of marker values
start= pos.getOffset();
end= pos.getOffset() + pos.getLength();
}
if (pos != null && pos.isDeleted()) {
// do nothing if position has been deleted
return;
}
}
IDocument document= getDocumentProvider().getDocument(getEditorInput());
if (selectLine) {
int line;
try {
if (start >= 0)
line= document.getLineOfOffset(start);
else {
line= MarkerUtilities.getLineNumber(marker);
// Marker line numbers are 1-based
-- line;
start= document.getLineOffset(line);
}
end= start + document.getLineLength(line) - 1;
} catch (BadLocationException e) {
return;
}
}
int length= document.getLength();
if (end <= length && start <= length) {
fIsComingFromGotoMarker= true;
selectAndReveal(start, end - start);
}
}
@Override
public boolean isEditable() {
if (!super.isEditable())
return false;
return fIsEditingDerivedFileAllowed;
}
@Override
public boolean validateEditorInputState() {
if (!super.validateEditorInputState())
return false;
return validateEditorInputDerived();
}
/**
* Validates the editor input for derived state.
* If the given input is derived then this method
* can show a dialog asking whether to edit the
* derived file.
*
* @return <code>true</code> if the input is OK for editing, <code>false</code> otherwise
* @since 3.3
*/
private boolean validateEditorInputDerived() {
if (fIsDerivedStateValidated)
return fIsEditingDerivedFileAllowed;
if (getDocumentProvider() instanceof IDocumentProviderExtension) {
IDocumentProviderExtension extension= (IDocumentProviderExtension)getDocumentProvider();
IStatus status= extension.getStatus(getEditorInput());
String pluginId= status.getPlugin();
boolean isDerivedStatus= status.getCode() == IFileBufferStatusCodes.DERIVED_FILE && (FileBuffers.PLUGIN_ID.equals(pluginId) || EditorsUI.PLUGIN_ID.equals(pluginId));
if (!isDerivedStatus)
return true;
}
final String warnKey= AbstractDecoratedTextEditorPreferenceConstants.EDITOR_WARN_IF_INPUT_DERIVED;
IPreferenceStore store= getPreferenceStore();
if (!store.getBoolean(warnKey))
return true;
MessageDialogWithToggle toggleDialog= MessageDialogWithToggle.openYesNoQuestion(
getSite().getShell(),
TextEditorMessages.AbstractDecoratedTextEditor_warning_derived_title,
TextEditorMessages.AbstractDecoratedTextEditor_warning_derived_message,
TextEditorMessages.AbstractDecoratedTextEditor_warning_derived_dontShowAgain,
false,
null,
null);
EditorsUI.getPreferenceStore().setValue(warnKey, !toggleDialog.getToggleState());
fIsDerivedStateValidated= true;
return fIsEditingDerivedFileAllowed= toggleDialog.getReturnCode() == IDialogConstants.YES_ID;
}
/*
* For an explanation why we override this method see http://bugs.eclipse.org/42230
*
* @see org.eclipse.ui.texteditor.StatusTextEditor#isErrorStatus(org.eclipse.core.runtime.IStatus)
*/
@Override
protected boolean isErrorStatus(IStatus status) {
if (!super.isErrorStatus(status))
return false;
if (!status.isMultiStatus())
return !isReadOnlyLocalStatus(status);
IStatus[] childrenStatus= status.getChildren();
for (int i= 0; i < childrenStatus.length; i++) {
if (childrenStatus[i].getSeverity() == IStatus.ERROR && !isReadOnlyLocalStatus(childrenStatus[i]))
return true;
}
return false;
}
/**
* Check whether the given status is a <code>IResourceStatus.READ_ONLY_LOCAL</code>
* error.
*
* @param status the status to be checked
* @return <code>true</code> if the given status is a <code>IResourceStatus.READ_ONLY_LOCAL</code> error
* @since 3.3
*/
private boolean isReadOnlyLocalStatus(IStatus status) {
return status.getCode() == IResourceStatus.READ_ONLY_LOCAL;
}
@Override
protected void createActions() {
super.createActions();
ResourceAction action= new AddMarkerAction(TextEditorMessages.getBundleForConstructedKeys(), "Editor.AddBookmark.", this, IMarker.BOOKMARK, true); //$NON-NLS-1$
action.setHelpContextId(ITextEditorHelpContextIds.BOOKMARK_ACTION);
action.setActionDefinitionId(IWorkbenchCommandConstants.EDIT_ADD_BOOKMARK);
setAction(IDEActionFactory.BOOKMARK.getId(), action);
action= new AddTaskAction(TextEditorMessages.getBundleForConstructedKeys(), "Editor.AddTask.", this); //$NON-NLS-1$
action.setHelpContextId(ITextEditorHelpContextIds.ADD_TASK_ACTION);
action.setActionDefinitionId(IWorkbenchCommandConstants.EDIT_ADD_TASK);
setAction(IDEActionFactory.ADD_TASK.getId(), action);
action= new ChangeEncodingAction(TextEditorMessages.getBundleForConstructedKeys(), "Editor.ChangeEncodingAction.", this); //$NON-NLS-1$
action.setHelpContextId(ITextEditorHelpContextIds.CHANGE_ENCODING);
action.setActionDefinitionId(ITextEditorActionDefinitionIds.CHANGE_ENCODING);
setAction(ITextEditorActionConstants.CHANGE_ENCODING, action);
markAsPropertyDependentAction(ITextEditorActionConstants.CHANGE_ENCODING, true);
action= new ResourceAction(TextEditorMessages.getBundleForConstructedKeys(), "Editor.ToggleLineNumbersAction.", IAction.AS_CHECK_BOX) { //$NON-NLS-1$
@Override
public void run() {
toggleLineNumberRuler();
}
};
action.setActionDefinitionId(ITextEditorActionDefinitionIds.LINENUMBER_TOGGLE);
setAction(ITextEditorActionConstants.LINENUMBERS_TOGGLE, action);
action= new ResourceAction(TextEditorMessages.getBundleForConstructedKeys(), "Editor.ToggleQuickDiffAction.", IAction.AS_CHECK_BOX) { //$NON-NLS-1$
@Override
public void run() {
toggleQuickDiffRuler();
}
};
action.setActionDefinitionId(ITextEditorActionDefinitionIds.QUICKDIFF_TOGGLE);
setAction(ITextEditorActionConstants.QUICKDIFF_TOGGLE, action);
action= new RevertLineAction(this, false);
action.setActionDefinitionId(ITextEditorActionDefinitionIds.QUICKDIFF_REVERTLINE);
setAction(ITextEditorActionConstants.QUICKDIFF_REVERTLINE, action);
action= new RevertSelectionAction(this, false);
setAction(ITextEditorActionConstants.QUICKDIFF_REVERTSELECTION, action);
action= new RevertBlockAction(this, false);
setAction(ITextEditorActionConstants.QUICKDIFF_REVERTBLOCK, action);
action= new RestoreAction(this, false);
setAction(ITextEditorActionConstants.QUICKDIFF_REVERTDELETION, action);
IAction action2= new CompositeRevertAction(this, new IAction[] {
getAction(ITextEditorActionConstants.QUICKDIFF_REVERTSELECTION),
getAction(ITextEditorActionConstants.QUICKDIFF_REVERTBLOCK),
getAction(ITextEditorActionConstants.QUICKDIFF_REVERTDELETION),
getAction(ITextEditorActionConstants.QUICKDIFF_REVERTLINE)});
action2.setActionDefinitionId(ITextEditorActionDefinitionIds.QUICKDIFF_REVERT);
setAction(ITextEditorActionConstants.QUICKDIFF_REVERT, action2);
action= new ResourceAction(TextEditorMessages.getBundleForConstructedKeys(), "Editor.HideRevisionInformationAction.") { //$NON-NLS-1$
@Override
public void run() {
if (fLineColumn != null)
fLineColumn.hideRevisionInformation();
}
};
setAction(ITextEditorActionConstants.REVISION_HIDE_INFO, action);
action= new ResourceAction(TextEditorMessages.getBundleForConstructedKeys(), "Editor.CycleRevisionRenderingAction.") { //$NON-NLS-1$
@Override
public void run() {
final RenderingMode[] modes= { IRevisionRulerColumnExtension.AGE, IRevisionRulerColumnExtension.AUTHOR, IRevisionRulerColumnExtension.AUTHOR_SHADED_BY_AGE};
IPreferenceStore store= EditorsUI.getPreferenceStore();
String current= store.getString(AbstractDecoratedTextEditorPreferenceConstants.REVISION_RULER_RENDERING_MODE);
for (int i= 0; i < modes.length; i++) {
String mode= modes[i].name();
if (mode.equals(current)) {
int nextIndex= (i + 1) % modes.length;
RenderingMode nextMode= modes[nextIndex];
store.setValue(AbstractDecoratedTextEditorPreferenceConstants.REVISION_RULER_RENDERING_MODE, nextMode.name());
}
}
}
};
action.setActionDefinitionId(ITextEditorActionDefinitionIds.REVISION_RENDERING_CYCLE);
setAction(ITextEditorActionConstants.REVISION_RENDERING_CYCLE, action);
action= new BooleanPreferenceToggleAction(TextEditorMessages.getBundleForConstructedKeys(), "Editor.ToggleRevisionAuthorAction.", IAction.AS_CHECK_BOX, EditorsUI.getPreferenceStore(), AbstractDecoratedTextEditorPreferenceConstants.REVISION_RULER_SHOW_AUTHOR); //$NON-NLS-1$
action.setActionDefinitionId(ITextEditorActionDefinitionIds.REVISION_AUTHOR_TOGGLE);
setAction(ITextEditorActionConstants.REVISION_SHOW_AUTHOR_TOGGLE, action);
action= new BooleanPreferenceToggleAction(TextEditorMessages.getBundleForConstructedKeys(), "Editor.ToggleRevisionIdAction.", IAction.AS_CHECK_BOX, EditorsUI.getPreferenceStore(), AbstractDecoratedTextEditorPreferenceConstants.REVISION_RULER_SHOW_REVISION); //$NON-NLS-1$
action.setActionDefinitionId(ITextEditorActionDefinitionIds.REVISION_ID_TOGGLE);
setAction(ITextEditorActionConstants.REVISION_SHOW_ID_TOGGLE, action);
final Shell shell;
if (getSourceViewer() != null)
shell= getSourceViewer().getTextWidget().getShell();
else
shell= null;
action= new ResourceAction(TextEditorMessages.getBundleForConstructedKeys(), "Editor.RulerPreferencesAction.") { //$NON-NLS-1$
@Override
public void run() {
String[] preferencePages= collectRulerMenuPreferencePages();
if (preferencePages.length > 0 && (shell == null || !shell.isDisposed()))
PreferencesUtil.createPreferenceDialogOn(shell, preferencePages[0], preferencePages, null).open();
}
};
setAction(ITextEditorActionConstants.RULER_PREFERENCES, action);
action= new ResourceAction(TextEditorMessages.getBundleForConstructedKeys(), "Editor.ContextPreferencesAction.") { //$NON-NLS-1$
@Override
public void run() {
String[] preferencePages= collectContextMenuPreferencePages();
if (preferencePages.length > 0 && (shell == null || !shell.isDisposed()))
PreferencesUtil.createPreferenceDialogOn(shell, preferencePages[0], preferencePages, null).open();
}
};
setAction(ITextEditorActionConstants.CONTEXT_PREFERENCES, action);
IAction showWhitespaceCharactersAction= getAction(ITextEditorActionConstants.SHOW_WHITESPACE_CHARACTERS);
if (showWhitespaceCharactersAction instanceof ShowWhitespaceCharactersAction)
((ShowWhitespaceCharactersAction)showWhitespaceCharactersAction).setPreferenceStore(EditorsUI.getPreferenceStore());
setAction(ITextEditorActionConstants.REFRESH, new RefreshEditorAction(this));
markAsPropertyDependentAction(ITextEditorActionConstants.REFRESH, true);
// Override print action to provide additional options
if (getAction(ITextEditorActionConstants.PRINT).isEnabled() && getSourceViewer() instanceof ITextViewerExtension8)
createPrintAction();
action= new ResourceAction(TextEditorMessages.getBundleForConstructedKeys(), "Editor.ShowChangeRulerInformation.", IAction.AS_PUSH_BUTTON) { //$NON-NLS-1$
@Override
public void run() {
showChangeRulerInformation();
}
};
action.setActionDefinitionId(ITextEditorActionDefinitionIds.SHOW_CHANGE_RULER_INFORMATION_ID);
setAction(ITextEditorActionConstants.SHOW_CHANGE_RULER_INFORMATION, action);
action= new ResourceAction(TextEditorMessages.getBundleForConstructedKeys(), "Editor.ShowRulerAnnotationInformation.", IAction.AS_PUSH_BUTTON) { //$NON-NLS-1$
@Override
public void run() {
showRulerAnnotationInformation();
}
};
action.setActionDefinitionId(ITextEditorActionDefinitionIds.SHOW_RULER_ANNOTATION_INFORMATION_ID);
setAction(ITextEditorActionConstants.SHOW_RULER_ANNOTATION_INFORMATION, action);
}
/**
* Opens a sticky change ruler hover for the caret line. Does nothing if no change hover is
* available.
*
* @since 3.5
*/
private void showChangeRulerInformation() {
IVerticalRuler ruler= getVerticalRuler();
if (!(ruler instanceof CompositeRuler) || fLineColumn == null)
return;
CompositeRuler compositeRuler= (CompositeRuler)ruler;
// fake a mouse move (some hovers rely on this to determine the hovered line):
int x= fLineColumn.getControl().getLocation().x;
ISourceViewer sourceViewer= getSourceViewer();
StyledText textWidget= sourceViewer.getTextWidget();
int caretOffset= textWidget.getCaretOffset();
int caretLine= textWidget.getLineAtOffset(caretOffset);
int y= textWidget.getLinePixel(caretLine);
compositeRuler.setLocationOfLastMouseButtonActivity(x, y);
IAnnotationHover hover= fLineColumn.getHover();
showFocusedRulerHover(hover, sourceViewer, caretOffset);
}
/**
* Opens a sticky annotation ruler hover for the caret line. Does nothing if no annotation hover
* is available.
*
* @since 3.6
*/
private void showRulerAnnotationInformation() {
ISourceViewer sourceViewer= getSourceViewer();
IAnnotationHover hover= getSourceViewerConfiguration().getAnnotationHover(sourceViewer);
int caretOffset= sourceViewer.getTextWidget().getCaretOffset();
showFocusedRulerHover(hover, sourceViewer, caretOffset);
}
/**
* Shows a focused hover at the specified offset.
* Does nothing if <code>hover</code> is <code>null</code> or cannot be shown.
*
* @param hover the hover to be shown, can be <code>null</code>
* @param sourceViewer the source viewer
* @param caretOffset the caret offset
*
* @since 3.6
*/
private void showFocusedRulerHover(IAnnotationHover hover, ISourceViewer sourceViewer, int caretOffset) {
if (hover == null)
return;
int modelCaretOffset= widgetOffset2ModelOffset(sourceViewer, caretOffset);
if (modelCaretOffset == -1)
return;
IDocument document= sourceViewer.getDocument();
if (document == null)
return;
try {
int line= document.getLineOfOffset(modelCaretOffset);
if (fInformationPresenter == null) {
fInformationPresenter= new FocusedInformationPresenter(sourceViewer, getSourceViewerConfiguration());
}
fInformationPresenter.openFocusedAnnotationHover(hover, line);
} catch (BadLocationException e) {
return;
}
}
/**
* Creates and registers the print action.
*
* @since 3.4
*/
private void createPrintAction() {
final ISourceViewer viewer= getSourceViewer();
ResourceAction action= new ResourceAction(TextEditorMessages.getBundleForConstructedKeys(), "Editor.Print.") { //$NON-NLS-1$
@Override
public void run() {
StyledTextPrintOptions options= new StyledTextPrintOptions();
options.printTextFontStyle= true;
options.printTextForeground= true;
options.printTextBackground= true;
options.jobName= getTitle();
options.header= StyledTextPrintOptions.SEPARATOR + getTitle();
options.footer= StyledTextPrintOptions.SEPARATOR + NLSUtility.format(TextEditorMessages.AbstractDecoratedTextEditor_printPageNumber, StyledTextPrintOptions.PAGE_TAG);
if (isLineNumberRulerVisible()) {
options.printLineNumbers= true;
// Compute line number labels
options.lineLabels= new String[viewer.getTextWidget().getLineCount()];
for (int i= 0; i < options.lineLabels.length; i++)
options.lineLabels[i]= String.valueOf(JFaceTextUtil.widgetLine2ModelLine(viewer, i) + 1);
}
((ITextViewerExtension8)viewer).print(options);
}
};
action.setHelpContextId(IAbstractTextEditorHelpContextIds.PRINT_ACTION);
action.setActionDefinitionId(IWorkbenchCommandConstants.FILE_PRINT);
setAction(ITextEditorActionConstants.PRINT, action);
}
@SuppressWarnings("unchecked")
@Override
public <T> T getAdapter(Class<T> adapter) {
if (IGotoMarker.class.equals(adapter))
return (T) fGotoMarkerAdapter;
if (IAnnotationAccess.class.equals(adapter))
return (T) getAnnotationAccess();
if (adapter == IShowInSource.class) {
return (T) new IShowInSource() {
@Override
public ShowInContext getShowInContext() {
ISelection selection= null;
ISelectionProvider selectionProvider= getSelectionProvider();
if (selectionProvider != null)
selection= selectionProvider.getSelection();
return new ShowInContext(getEditorInput(), selection);
}
};
}
if (IRevisionRulerColumn.class.equals(adapter)) {
if (fLineNumberRulerColumn instanceof IRevisionRulerColumn)
return (T) fLineNumberRulerColumn;
}
if (MarkerAnnotationPreferences.class.equals(adapter))
return (T) EditorsPlugin.getDefault().getMarkerAnnotationPreferences();
return super.getAdapter(adapter);
}
/*
* If there is no explicit document provider set, the implicit one is
* re-initialized based on the given editor input.
*
* @see org.eclipse.ui.texteditor.AbstractTextEditor#setDocumentProvider(org.eclipse.ui.IEditorInput)
*/
@Override
protected void setDocumentProvider(IEditorInput input) {
fImplicitDocumentProvider= DocumentProviderRegistry.getDefault().getDocumentProvider(input);
IDocumentProvider provider= super.getDocumentProvider();
if (provider instanceof ForwardingDocumentProvider) {
ForwardingDocumentProvider forwarder= (ForwardingDocumentProvider) provider;
forwarder.setParentProvider(fImplicitDocumentProvider);
}
}
@Override
public IDocumentProvider getDocumentProvider() {
IDocumentProvider provider= super.getDocumentProvider();
if (provider == null)
return fImplicitDocumentProvider;
return provider;
}
@Override
protected void disposeDocumentProvider() {
super.disposeDocumentProvider();
fImplicitDocumentProvider= null;
}
/*
* @see AbstractTextEditor#doSetInput(IEditorInput)
*
* This implementation also updates change information in the quick diff
* ruler.
*/
@Override
protected void doSetInput(IEditorInput input) throws CoreException {
fIsDerivedStateValidated= false;
fIsEditingDerivedFileAllowed= true;
if (fLineColumn != null)
fLineColumn.hideRevisionInformation();
super.doSetInput(input);
RulerColumnDescriptor lineNumberColumnDescriptor= RulerColumnRegistry.getDefault().getColumnDescriptor(LineNumberColumn.ID);
if (lineNumberColumnDescriptor != null) {
IColumnSupport columnSupport= getAdapter(IColumnSupport.class);
columnSupport.setColumnVisible(lineNumberColumnDescriptor, isLineNumberRulerVisible() || isPrefQuickDiffAlwaysOn());
}
}
@Override
protected void handleEditorInputChanged() {
final IDocumentProvider provider= getDocumentProvider();
IEditorInput input= getEditorInput();
if (provider != null && input != null) {
if (!isDirty() && input.getAdapter(IFile.class) != null) {
if (Platform.getPreferencesService().getBoolean(ResourcesPlugin.PI_RESOURCES, ResourcesPlugin.PREF_LIGHTWEIGHT_AUTO_REFRESH, false, null))
return;
}
}
super.handleEditorInputChanged();
}
/**
* This implementation asks the user for the workspace path of a file resource and saves the document there.
*
* @param progressMonitor the progress monitor to be used
* @since 3.2
*/
@Override
protected void performSaveAs(IProgressMonitor progressMonitor) {
Shell shell= PlatformUI.getWorkbench().getModalDialogShellProvider().getShell();
final IEditorInput input= getEditorInput();
IDocumentProvider provider= getDocumentProvider();
final IEditorInput newInput;
if (input instanceof IURIEditorInput && !(input instanceof IFileEditorInput)) {
FileDialog dialog= new FileDialog(shell, SWT.SAVE);
IPath oldPath= URIUtil.toPath(((IURIEditorInput)input).getURI());
if (oldPath != null && !oldPath.isEmpty()) {
dialog.setFileName(oldPath.lastSegment());
dialog.setFilterPath(oldPath.removeLastSegments(1).toOSString());
}
String path= dialog.open();
if (path == null) {
if (progressMonitor != null)
progressMonitor.setCanceled(true);
return;
}
// Check whether file exists and if so, confirm overwrite
final File localFile= new File(path);
if (localFile.exists()) {
MessageDialog overwriteDialog= new MessageDialog(
shell,
TextEditorMessages.AbstractDecoratedTextEditor_saveAs_overwrite_title,
null,
NLSUtility.format(TextEditorMessages.AbstractDecoratedTextEditor_saveAs_overwrite_message, path),
MessageDialog.WARNING,
new String[] { IDialogConstants.YES_LABEL, IDialogConstants.NO_LABEL },
1); // 'No' is the default
if (overwriteDialog.open() != Window.OK) {
if (progressMonitor != null) {
progressMonitor.setCanceled(true);
return;
}
}
}
IFileStore fileStore;
try {
fileStore= EFS.getStore(localFile.toURI());
} catch (CoreException ex) {
EditorsPlugin.log(ex.getStatus());
String title= TextEditorMessages.AbstractDecoratedTextEditor_error_saveAs_title;
String msg= NLSUtility.format(TextEditorMessages.AbstractDecoratedTextEditor_error_saveAs_message, ex.getMessage());
MessageDialog.openError(shell, title, msg);
return;
}
IFile file= getWorkspaceFile(fileStore);
if (file != null)
newInput= new FileEditorInput(file);
else
newInput= new FileStoreEditorInput(fileStore);
} else {
SaveAsDialog dialog= new SaveAsDialog(shell);
IFile original= (input instanceof IFileEditorInput) ? ((IFileEditorInput) input).getFile() : null;
if (original != null)
dialog.setOriginalFile(original);
else
dialog.setOriginalName(input.getName());
dialog.create();
if (provider.isDeleted(input) && original != null) {
String message= NLSUtility.format(TextEditorMessages.AbstractDecoratedTextEditor_warning_saveAs_deleted, original.getName());
dialog.setErrorMessage(null);
dialog.setMessage(message, IMessageProvider.WARNING);
}
if (dialog.open() == Window.CANCEL) {
if (progressMonitor != null)
progressMonitor.setCanceled(true);
return;
}
IPath filePath= dialog.getResult();
if (filePath == null) {
if (progressMonitor != null)
progressMonitor.setCanceled(true);
return;
}
IWorkspace workspace= ResourcesPlugin.getWorkspace();
IFile file= workspace.getRoot().getFile(filePath);
newInput= new FileEditorInput(file);
}
if (provider == null) {
// editor has programmatically been closed while the dialog was open
return;
}
boolean success= false;
try {
provider.aboutToChange(newInput);
provider.saveDocument(progressMonitor, newInput, provider.getDocument(input), true);
success= true;
} catch (CoreException x) {
final IStatus status= x.getStatus();
if (status == null || status.getSeverity() != IStatus.CANCEL) {
String title= TextEditorMessages.AbstractDecoratedTextEditor_error_saveAs_title;
String msg= NLSUtility.format(TextEditorMessages.AbstractDecoratedTextEditor_error_saveAs_message, x.getMessage());
MessageDialog.openError(shell, title, msg);
}
} finally {
provider.changed(newInput);
if (success)
setInput(newInput);
}
if (progressMonitor != null)
progressMonitor.setCanceled(!success);
}
/**
* Presents an error dialog to the user when a problem happens during save.
* <p>
* Overrides the default behavior by showing a more advanced error dialog in case of encoding
* problems.
* </p>
*
* @param title the dialog title
* @param message the message to display
* @param exception the exception to handle
* @since 3.6
*/
@Override
protected void openSaveErrorDialog(String title, String message, CoreException exception) {
IStatus status= exception.getStatus();
final IDocumentProvider documentProvider= getDocumentProvider();
if (!(status.getCode() == IFileBufferStatusCodes.CHARSET_MAPPING_FAILED && documentProvider instanceof IStorageDocumentProvider)) {
super.openSaveErrorDialog(title, message, exception);
return;
}
final int saveAsUTF8ButtonId= IDialogConstants.OK_ID + IDialogConstants.CANCEL_ID + 1;
final int selectUnmappableCharButtonId= saveAsUTF8ButtonId + 1;
final Charset charset= getCharset();
ErrorDialog errorDialog= new ErrorDialog(getSite().getShell(), title, message, status, IStatus.ERROR) {
@Override
protected void createButtonsForButtonBar(Composite parent) {
super.createButtonsForButtonBar(parent);
createButton(parent, saveAsUTF8ButtonId, TextEditorMessages.AbstractDecoratedTextEditor_save_error_Dialog_button_saveAsUTF8, false);
if (charset != null)
createButton(parent, selectUnmappableCharButtonId, TextEditorMessages.AbstractDecoratedTextEditor_save_error_Dialog_button_selectUnmappable, false);
}
@Override
protected void buttonPressed(int id) {
if (id == saveAsUTF8ButtonId || id == selectUnmappableCharButtonId) {
setReturnCode(id);
close();
} else
super.buttonPressed(id);
}
@Override
protected boolean shouldShowDetailsButton() {
return false;
}
};
int returnCode= errorDialog.open();
if (returnCode == saveAsUTF8ButtonId) {
((IStorageDocumentProvider)documentProvider).setEncoding(getEditorInput(), "UTF-8"); //$NON-NLS-1$
IProgressMonitor monitor= getProgressMonitor();
try {
doSave(monitor);
} finally {
monitor.done();
}
} else if (returnCode == selectUnmappableCharButtonId) {
CharsetEncoder encoder= charset.newEncoder();
IDocument document= getDocumentProvider().getDocument(getEditorInput());
int documentLength= document.getLength();
int offset= 0;
BreakIterator charBreakIterator= BreakIterator.getCharacterInstance();
charBreakIterator.setText(document.get());
while (offset < documentLength) {
try {
int next= charBreakIterator.next();
String ch= document.get(offset, next - offset);
if (!encoder.canEncode(ch)) {
selectAndReveal(offset, next - offset);
return;
}
offset= next;
} catch (BadLocationException ex) {
EditorsPlugin.log(ex);
// Skip this character. Showing yet another dialog here is overkill
}
}
}
}
/**
* Returns the charset of the current editor input.
*
* @return the charset of the current editor input or <code>null</code> if it fails
* @since 3.6
*/
private Charset getCharset() {
IEncodingSupport encodingSupport= getAdapter(IEncodingSupport.class);
if (encodingSupport == null)
return null;
try {
return Charset.forName(encodingSupport.getEncoding());
} catch (UnsupportedCharsetException ex) {
return null;
} catch (IllegalCharsetNameException ex) {
return null;
}
}
/**
* Checks whether there given file store points
* to a file in the workspace. Only returns a
* workspace file if there's a single match.
*
* @param fileStore the file store
* @return the <code>IFile</code> that matches the given file store
* @since 3.2
*/
private IFile getWorkspaceFile(IFileStore fileStore) {
IWorkspaceRoot workspaceRoot= ResourcesPlugin.getWorkspace().getRoot();
IFile[] files= workspaceRoot.findFilesForLocationURI(fileStore.toURI());
if (files != null && files.length == 1)
return files[0];
return null;
}
/**
* Sets the ruler's overview context menu id.
*
* @param contextMenuId the overview ruler context menu id
* @since 3.4
*/
protected void setOverviewRulerContextMenuId(String contextMenuId) {
Assert.isNotNull(contextMenuId);
fOverviewRulerContextMenuId= contextMenuId;
}
/**
* Returns the ruler's overview 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>
* @since 3.4
*/
protected final String getOverviewRulerContextMenuId() {
return fOverviewRulerContextMenuId;
}
/**
* Sets up the overview ruler context menu before it is made visible.
* <p>
* Subclasses may extend to add other actions.
* </p>
*
* @param menu the menu
* @since 3.4
*/
protected void overviewRulerContextMenuAboutToShow(IMenuManager menu) {
final String preferenceLabel= findSelectedOverviewRulerAnnotationLabel();
final Shell shell= getSite().getShell();
IAction action= new ResourceAction(TextEditorMessages.getBundleForConstructedKeys(), "Editor.RulerPreferencesAction.") { //$NON-NLS-1$
@Override
public void run() {
String[] preferencePages= collectOverviewRulerMenuPreferencePages();
if (preferencePages.length > 0 && (shell == null || !shell.isDisposed())) {
PreferencesUtil.createPreferenceDialogOn(shell, preferencePages[0], preferencePages, preferenceLabel).open();
}
}
};
menu.add(new Separator(ITextEditorActionConstants.GROUP_REST));
menu.add(new Separator(IWorkbenchActionConstants.MB_ADDITIONS));
menu.add(action);
}
private String findSelectedOverviewRulerAnnotationLabel() {
IAnnotationModel model= getSourceViewer().getAnnotationModel();
if (model == null) {
return null;
}
Point selection= getSourceViewer().getSelectedRange();
Annotation annotation= null;
Iterator<Annotation> iter= model.getAnnotationIterator();
while (iter.hasNext()) {
annotation= iter.next();
Position p= model.getPosition(annotation);
if (p.getOffset() == selection.x && p.getLength() == selection.y)
break;
}
if (annotation != null) {
AnnotationPreference ap= getAnnotationPreferenceLookup().getAnnotationPreference(annotation);
if (ap != null)
return ap.getPreferenceLabel();
}
return null;
}
@Override
protected void rulerContextMenuAboutToShow(IMenuManager menu) {
/*
* XXX: workaround for reliable menu item ordering.
* This can be changed once the action contribution story converges,
* see http://dev.eclipse.org/viewcvs/index.cgi/~checkout~/platform-ui-home/R3_1/dynamic_teams/dynamic_teams.html#actionContributions
*/
// pre-install menus for contributions and call super
menu.add(new Separator("debug")); //$NON-NLS-1$
menu.add(new Separator(IWorkbenchActionConstants.MB_ADDITIONS));
menu.add(new GroupMarker(ITextEditorActionConstants.GROUP_RESTORE));
menu.add(new Separator("add")); //$NON-NLS-1$
menu.add(new Separator(ITextEditorActionConstants.GROUP_RULERS));
menu.add(new Separator(ITextEditorActionConstants.GROUP_REST));
super.rulerContextMenuAboutToShow(menu);
addRulerContributionActions(menu);
/* quick diff */
if (isEditorInputModifiable()) {
IAction quickdiffAction= getAction(ITextEditorActionConstants.QUICKDIFF_TOGGLE);
quickdiffAction.setChecked(isChangeInformationShowing());
menu.appendToGroup(ITextEditorActionConstants.GROUP_RULERS, quickdiffAction);
if (isChangeInformationShowing()) {
TextEditorAction revertLine= new RevertLineAction(this, true);
TextEditorAction revertSelection= new RevertSelectionAction(this, true);
TextEditorAction revertBlock= new RevertBlockAction(this, true);
TextEditorAction revertDeletion= new RestoreAction(this, true);
revertSelection.update();
revertBlock.update();
revertLine.update();
revertDeletion.update();
// only add block action if selection action is not enabled
if (revertSelection.isEnabled())
menu.appendToGroup(ITextEditorActionConstants.GROUP_RESTORE, revertSelection);
else if (revertBlock.isEnabled())
menu.appendToGroup(ITextEditorActionConstants.GROUP_RESTORE, revertBlock);
if (revertLine.isEnabled())
menu.appendToGroup(ITextEditorActionConstants.GROUP_RESTORE, revertLine);
if (revertDeletion.isEnabled())
menu.appendToGroup(ITextEditorActionConstants.GROUP_RESTORE, revertDeletion);
}
}
// revision info
if (fLineColumn != null && fLineColumn.isShowingRevisionInformation()) {
IMenuManager revisionMenu= new MenuManager(TextEditorMessages.AbstractDecoratedTextEditor_revisions_menu);
menu.appendToGroup(ITextEditorActionConstants.GROUP_RULERS, revisionMenu);
IAction hideRevisionInfoAction= getAction(ITextEditorActionConstants.REVISION_HIDE_INFO);
revisionMenu.add(hideRevisionInfoAction);
revisionMenu.add(new Separator());
String[] labels= { TextEditorMessages.AbstractDecoratedTextEditor_revision_colors_option_by_date, TextEditorMessages.AbstractDecoratedTextEditor_revision_colors_option_by_author, TextEditorMessages.AbstractDecoratedTextEditor_revision_colors_option_by_author_and_date };
final RenderingMode[] modes= { IRevisionRulerColumnExtension.AGE, IRevisionRulerColumnExtension.AUTHOR, IRevisionRulerColumnExtension.AUTHOR_SHADED_BY_AGE};
final IPreferenceStore uiStore= EditorsUI.getPreferenceStore();
String current= uiStore.getString(AbstractDecoratedTextEditorPreferenceConstants.REVISION_RULER_RENDERING_MODE);
for (int i= 0; i < modes.length; i++) {
final String mode= modes[i].name();
IAction action= new Action(labels[i], IAction.AS_RADIO_BUTTON) {
@Override
public void run() {
// set preference globally, LineNumberColumn reacts on preference change
uiStore.setValue(AbstractDecoratedTextEditorPreferenceConstants.REVISION_RULER_RENDERING_MODE, mode);
}
};
action.setChecked(mode.equals(current));
revisionMenu.add(action);
}
revisionMenu.add(new Separator());
IAction action= getAction(ITextEditorActionConstants.REVISION_SHOW_AUTHOR_TOGGLE);
if (action instanceof IUpdate)
((IUpdate)action).update();
revisionMenu.add(action);
action= getAction(ITextEditorActionConstants.REVISION_SHOW_ID_TOGGLE);
if (action instanceof IUpdate)
((IUpdate)action).update();
revisionMenu.add(action);
}
IAction lineNumberAction= getAction(ITextEditorActionConstants.LINENUMBERS_TOGGLE);
lineNumberAction.setChecked(fLineColumn != null && fLineColumn.isShowingLineNumbers());
menu.appendToGroup(ITextEditorActionConstants.GROUP_RULERS, lineNumberAction);
IAction preferencesAction= getAction(ITextEditorActionConstants.RULER_PREFERENCES);
menu.appendToGroup(ITextEditorActionConstants.GROUP_RULERS, new Separator(ITextEditorActionConstants.GROUP_SETTINGS));
menu.appendToGroup(ITextEditorActionConstants.GROUP_SETTINGS, preferencesAction);
}
/**
* Adds "show" actions for all contributed rulers that support it.
*
* @param menu the ruler context menu
* @since 3.3
*/
private void addRulerContributionActions(IMenuManager menu) {
// store directly in generic editor preferences
final IColumnSupport support= getAdapter(IColumnSupport.class);
IPreferenceStore store= EditorsUI.getPreferenceStore();
final RulerColumnPreferenceAdapter adapter= new RulerColumnPreferenceAdapter(store, AbstractTextEditor.PREFERENCE_RULER_CONTRIBUTIONS);
List<RulerColumnDescriptor> descriptors= RulerColumnRegistry.getDefault().getColumnDescriptors();
for (Iterator<RulerColumnDescriptor> t= descriptors.iterator(); t.hasNext();) {
final RulerColumnDescriptor descriptor= t.next();
if (!descriptor.isIncludedInMenu() || !support.isColumnSupported(descriptor))
continue;
final boolean isVisible= support.isColumnVisible(descriptor);
IAction action= new Action(MessageFormat.format(TextEditorMessages.AbstractDecoratedTextEditor_show_ruler_label, new Object[] {descriptor.getName()}), IAction.AS_CHECK_BOX) {
@Override
public void run() {
if (descriptor.isGlobal())
// column state is modified via preference listener of AbstractTextEditor
adapter.setEnabled(descriptor, !isVisible);
else
// directly modify column for this editor instance
support.setColumnVisible(descriptor, !isVisible);
}
};
action.setChecked(isVisible);
action.setImageDescriptor(descriptor.getIcon());
menu.appendToGroup(ITextEditorActionConstants.GROUP_RULERS, action);
}
}
/**
* Toggles the line number global preference and shows the line number ruler
* accordingly.
*
* @since 3.1
*/
private void toggleLineNumberRuler() {
// globally
IPreferenceStore store= EditorsUI.getPreferenceStore();
store.setValue(LINE_NUMBER_RULER, !isLineNumberRulerVisible());
}
/**
* Toggles the quick diff global preference and shows the quick diff ruler
* accordingly.
*
* @since 3.1
*/
private void toggleQuickDiffRuler() {
// change the visibility locally if this editor is not in sync with the global preference
// toggle the preference if we are in sync.
IPreferenceStore store= EditorsUI.getPreferenceStore();
boolean current= store.getBoolean(AbstractDecoratedTextEditorPreferenceConstants.QUICK_DIFF_ALWAYS_ON);
if (current == isChangeInformationShowing())
store.setValue(AbstractDecoratedTextEditorPreferenceConstants.QUICK_DIFF_ALWAYS_ON, !current);
else
showChangeInformation(current);
}
@Override
protected void editorContextMenuAboutToShow(IMenuManager menu) {
super.editorContextMenuAboutToShow(menu);
IAction preferencesAction= getAction(ITextEditorActionConstants.CONTEXT_PREFERENCES);
menu.appendToGroup(IWorkbenchActionConstants.MB_ADDITIONS, new Separator(ITextEditorActionConstants.GROUP_SETTINGS));
menu.appendToGroup(ITextEditorActionConstants.GROUP_SETTINGS, preferencesAction);
menu.appendToGroup(ITextEditorActionConstants.GROUP_SAVE, new Separator(ITextEditorActionConstants.GROUP_OPEN));
IEditorInput editorInput= getEditorInput();
if ((editorInput.getAdapter(IResource.class)) instanceof IFile) {
MenuManager openWithSubMenu= new MenuManager(TextEditorMessages.AbstractDecoratedTextEditor_openWith_menu);
final IWorkbenchPage page= getEditorSite().getPage();
// XXX: Internal reference will get fixed during 3.7, see https://bugs.eclipse.org/bugs/show_bug.cgi?id=307026
openWithSubMenu.add(new OpenWithMenu(page, editorInput) {
@Override
protected void openEditor(IEditorDescriptor editorDescriptor, boolean openUsingDescriptor) {
super.openEditor(editorDescriptor, openUsingDescriptor);
ISelection selection= getSelectionProvider().getSelection();
if (selection instanceof ITextSelection) {
revealInEditor(page.getActiveEditor(), ((ITextSelection)selection).getOffset(), ((ITextSelection)selection).getLength());
}
}
});
menu.appendToGroup(ITextEditorActionConstants.GROUP_OPEN, openWithSubMenu);
}
MenuManager showInSubMenu= new MenuManager(getShowInMenuLabel());
showInSubMenu.add(ContributionItemFactory.VIEWS_SHOW_IN.create(getEditorSite().getWorkbenchWindow()));
menu.appendToGroup(ITextEditorActionConstants.GROUP_OPEN, showInSubMenu);
}
/**
* Selects and reveals the given offset and length in the given editor part.
*
* @param editor the editor part
* @param offset the offset
* @param length the length
* @since 3.7
*/
private static void revealInEditor(IEditorPart editor, final int offset, final int length) {
if (editor instanceof ITextEditor) {
((ITextEditor)editor).selectAndReveal(offset, length);
return;
}
// Support for non-text editor - try IGotoMarker interface
final IGotoMarker gotoMarkerTarget;
if (editor instanceof IGotoMarker)
gotoMarkerTarget= (IGotoMarker)editor;
else
gotoMarkerTarget= editor != null ? editor.getAdapter(IGotoMarker.class) : null;
if (gotoMarkerTarget != null) {
final IEditorInput input= editor.getEditorInput();
if (input instanceof IFileEditorInput) {
WorkspaceModifyOperation op= new WorkspaceModifyOperation() {
@Override
protected void execute(IProgressMonitor monitor) throws CoreException {
IMarker marker= null;
try {
marker= ((IFileEditorInput)input).getFile().createMarker(IMarker.TEXT);
marker.setAttribute(IMarker.CHAR_START, offset);
marker.setAttribute(IMarker.CHAR_END, offset + length);
gotoMarkerTarget.gotoMarker(marker);
} finally {
if (marker != null)
marker.delete();
}
}
};
try {
op.run(null);
} catch (InvocationTargetException ex) {
// reveal failed
} catch (InterruptedException e) {
Assert.isTrue(false, "this operation can not be canceled"); //$NON-NLS-1$
}
}
return;
}
}
/**
* Returns the menu label for 'Show In' together with its key binding string.
*
* @return the 'Show In' menu label
* @since 3.2
*/
private String getShowInMenuLabel() {
String keyBinding= null;
IBindingService bindingService= PlatformUI.getWorkbench().getAdapter(IBindingService.class);
if (bindingService != null)
keyBinding= bindingService.getBestActiveBindingFormattedFor(IWorkbenchCommandConstants.NAVIGATE_SHOW_IN_QUICK_MENU);
if (keyBinding == null)
keyBinding= ""; //$NON-NLS-1$
return NLSUtility.format(TextEditorMessages.AbstractDecoratedTextEditor_showIn_menu, keyBinding);
}
/**
* Returns the preference page ids of the preference pages to be shown when executing the
* preferences action from the editor context menu. The first page will be selected.
* <p>
* Subclasses may extend or replace.
* </p>
*
* @return the preference page ids to show, may be empty
* @since 3.1
*/
protected String[] collectContextMenuPreferencePages() {
return new String[] { "org.eclipse.ui.preferencePages.GeneralTextEditor", //$NON-NLS-1$
"org.eclipse.ui.editors.preferencePages.Annotations", //$NON-NLS-1$
"org.eclipse.ui.editors.preferencePages.QuickDiff", //$NON-NLS-1$
"org.eclipse.ui.editors.preferencePages.Accessibility", //$NON-NLS-1$
"org.eclipse.ui.editors.preferencePages.Spelling", //$NON-NLS-1$
"org.eclipse.ui.editors.preferencePages.LinkedModePreferencePage", //$NON-NLS-1$
"org.eclipse.ui.preferencePages.ColorsAndFonts", //$NON-NLS-1$
};
}
/**
* Returns the preference page ids of the preference pages to be shown when executing the
* preferences action from the editor ruler context menu. The first page will be selected.
* <p>
* The default is to return the same list as <code>collectContextMenuPreferencePages</code>.
* </p>
* <p>
* Subclasses may extend or replace.
* </p>
*
* @return the preference page ids to show, may be empty
* @since 3.1
*/
protected String[] collectRulerMenuPreferencePages() {
return collectContextMenuPreferencePages();
}
/**
* Returns the preference page ids of the preference pages to be shown when executing the
* preferences action from the editor overview ruler context menu. The first page will be
* selected.
* <p>
* The default is to select the 'Annotations' preference page.
* </p>
* <p>
* Subclasses may extend or replace.
* </p>
*
* @return the preference page ids to show, may be empty
* @since 3.4
*/
protected String[] collectOverviewRulerMenuPreferencePages() {
return new String[] { "org.eclipse.ui.editors.preferencePages.Annotations", //$NON-NLS-1$
"org.eclipse.ui.preferencePages.GeneralTextEditor", //$NON-NLS-1$
"org.eclipse.ui.editors.preferencePages.Accessibility", //$NON-NLS-1$
"org.eclipse.ui.editors.preferencePages.QuickDiff", //$NON-NLS-1$
"org.eclipse.ui.preferencePages.ColorsAndFonts" //$NON-NLS-1$
};
}
@Override
protected IOperationApprover getUndoRedoOperationApprover(IUndoContext undoContext) {
IEditorInput input= getEditorInput();
if (input != null && input.getAdapter(IResource.class) != null)
return new NonLocalUndoUserApprover(undoContext, this, new Object [] { input }, IResource.class);
return super.getUndoRedoOperationApprover(undoContext);
}
/**
* Returns whether the given annotation is configured as a target for the
* "Go to Next/Previous Annotation" actions.
* <p>
* The annotation is a target if their annotation type is configured to be
* in the Next/Previous tool bar drop down menu and if it is checked.
* </p>
*
* @param annotation the annotation
* @return <code>true</code> if this is a target, <code>false</code> otherwise
* @since 3.2
*/
@Override
protected boolean isNavigationTarget(Annotation annotation) {
AnnotationPreference preference= getAnnotationPreferenceLookup().getAnnotationPreference(annotation);
// See bug 41689
// String key= forward ? preference.getIsGoToNextNavigationTargetKey() : preference.getIsGoToPreviousNavigationTargetKey();
String key= preference == null ? null : preference.getIsGoToNextNavigationTargetKey();
return (key != null && getPreferenceStore().getBoolean(key));
}
/**
* {@inheritDoc}
* <p>
* This extended implementation updates views that also show the
* select marker annotation.
* </p>
* @since 3.2
*/
@Override
public Annotation gotoAnnotation(boolean forward) {
Annotation annotation= super.gotoAnnotation(forward);
if (annotation != null)
updateMarkerViews(annotation);
return annotation;
}
/**
* Updates visible views that show markers.
* <p>
* If the given annotation can be associated with a marker then this method tries select the
* this marker in views that show markers.
* </p>
*
* @param annotation the annotation
* @since 3.2
*/
protected void updateMarkerViews(Annotation annotation) {
if (fIsComingFromGotoMarker) {
fIsComingFromGotoMarker= false;
return;
}
IMarker marker= null;
if (annotation instanceof MarkerAnnotation)
marker= ((MarkerAnnotation)annotation).getMarker();
if (marker != null) {
try {
fIsUpdatingMarkerViews= true;
IWorkbenchPage page= getSite().getPage();
MarkerViewUtil.showMarker(page, marker, false);
} finally {
fIsUpdatingMarkerViews= false;
}
}
}
/*
* @see org.eclipse.ui.texteditor.AbstractTextEditor#isTabConversionEnabled()
* @since 3.3
*/
@Override
protected boolean isTabsToSpacesConversionEnabled() {
return getPreferenceStore() != null && getPreferenceStore().getBoolean(AbstractDecoratedTextEditorPreferenceConstants.EDITOR_SPACES_FOR_TABS);
}
}