blob: 387024e2c36ebd5adcfe376b825c08c5a53acebb [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2006 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.util.Iterator;
import org.eclipse.osgi.util.NLS;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.RGB;
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.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.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.Path;
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.jface.action.GroupMarker;
import org.eclipse.jface.action.IAction;
import org.eclipse.jface.action.IMenuManager;
import org.eclipse.jface.action.MenuManager;
import org.eclipse.jface.action.Separator;
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.preference.PreferenceConverter;
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.ITextViewerExtension6;
import org.eclipse.jface.text.Position;
import org.eclipse.jface.text.revisions.IRevisionRulerColumn;
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.IAnnotationModel;
import org.eclipse.jface.text.source.IAnnotationModelExtension;
import org.eclipse.jface.text.source.IChangeRulerColumn;
import org.eclipse.jface.text.source.ILineDifferExtension;
import org.eclipse.jface.text.source.IOverviewRuler;
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.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.ITextEditorHelpContextIds;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.IFileEditorInput;
import org.eclipse.ui.IWorkbenchActionConstants;
import org.eclipse.ui.IWorkbenchPage;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.actions.ContributionItemFactory;
import org.eclipse.ui.dialogs.PreferencesUtil;
import org.eclipse.ui.dialogs.SaveAsDialog;
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.JavaFileEditorInput;
import org.eclipse.ui.internal.editors.text.NLSUtility;
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.texteditor.quickdiff.QuickDiff;
import org.eclipse.ui.views.markers.MarkerViewUtil;
/**
* 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 the foreground color of the line numbers.
*/
private final static String LINE_NUMBER_COLOR= AbstractDecoratedTextEditorPreferenceConstants.EDITOR_LINE_NUMBER_RULER_COLOR;
/**
* 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;
/**
* Preference key to get whether the overwrite mode is disabled.
* @since 3.2
*/
private final static String REVISION_ASK_BEFORE_QUICKDIFF_SWITCH= AbstractDecoratedTextEditorPreferenceConstants.REVISION_ASK_BEFORE_QUICKDIFF_SWITCH;
/**
* Adapter class for <code>IGotoMarker</code>.
*/
private class GotoMarkerAdapter implements IGotoMarker {
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;
/**
* 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 change ruler column.
*/
private IChangeRulerColumn fChangeRulerColumn;
/**
* Whether quick diff information is displayed, either on a change ruler or the line number ruler.
*/
private boolean fIsChangeInformationShown;
/**
* The annotation ruler column used in the vertical ruler.
*/
private AnnotationRulerColumn fAnnotationRulerColumn;
/**
* 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;
/**
* Whether quick diff information is displayed, either on a change ruler or the line number
* ruler.
* @since 3.2
*/
private boolean fIsRevisionInformationShown;
/**
* The revision information displayed on the ruler, possibly <code>null</code>.
* @since 3.2
*/
private RevisionInformation fRevisionInfo;
/**
* Creates a new text editor.
*/
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$
}
/*
* @see IWorkbenchPart#dispose()
*/
public void dispose() {
if (fSourceViewerDecorationSupport != null) {
fSourceViewerDecorationSupport.dispose();
fSourceViewerDecorationSupport= null;
}
fAnnotationAccess= null;
fAnnotationPreferences= null;
fAnnotationRulerColumn= null;
super.dispose();
}
/*
* @see org.eclipse.ui.texteditor.AbstractTextEditor#createSourceViewer(Composite, IVerticalRuler, int)
*/
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() {
ISharedTextColors sharedColors= EditorsPlugin.getDefault().getSharedTextColors();
return sharedColors;
}
protected IOverviewRuler createOverviewRuler(ISharedTextColors sharedColors) {
IOverviewRuler ruler= new OverviewRuler(getAnnotationAccess(), VERTICAL_RULER_WIDTH, sharedColors);
Iterator e= fAnnotationPreferences.getAnnotationPreferences().iterator();
while (e.hasNext()) {
AnnotationPreference preference= (AnnotationPreference) 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 e= fAnnotationPreferences.getAnnotationPreferences().iterator();
while (e.hasNext())
support.setAnnotationPreference((AnnotationPreference) e.next());
support.setCursorLinePainterPreferenceKeys(CURRENT_LINE, CURRENT_LINE_COLOR);
support.setMarginPainterPreferenceKeys(PRINT_MARGIN, PRINT_MARGIN_COLOR, PRINT_MARGIN_COLUMN);
support.setSymbolicFontName(getFontPropertyPreferenceKey());
}
/*
* @see org.eclipse.ui.IWorkbenchPart#createPartControl(org.eclipse.swt.widgets.Composite)
*/
public void createPartControl(Composite parent) {
super.createPartControl(parent);
if (fSourceViewerDecorationSupport != null)
fSourceViewerDecorationSupport.install(getPreferenceStore());
if (isPrefQuickDiffAlwaysOn())
showChangeInformation(true);
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());
}
/*
* @see org.eclipse.ui.texteditor.StatusTextEditor#createStatusControl(org.eclipse.swt.widgets.Composite, org.eclipse.core.runtime.IStatus)
* @since 3.1
*/
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);
//
// Button button= new Button(buttonComposite, SWT.PUSH | SWT.FLAT);
// button.setText(action.getText());
// button.addSelectionListener(new SelectionAdapter() {
// /*
// * @see org.eclipse.swt.events.SelectionAdapter#widgetDefaultSelected(org.eclipse.swt.events.SelectionEvent)
// */
// public void widgetSelected(SelectionEvent e) {
// action.run();
// }
// });
//
// Label filler= new Label(buttonComposite, SWT.NONE);
// filler.setLayoutData(new GridData(GridData.FILL_BOTH));
// filler.setBackground(bgColor);
//
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;
}
/*
* @see org.eclipse.ui.texteditor.ITextEditorExtension3#showChangeInformation(boolean)
*/
public void showChangeInformation(boolean show) {
if (show == fIsChangeInformationShown)
return;
if (fIsChangeInformationShown) {
uninstallChangeRulerModel();
showChangeRuler(false); // hide change ruler if its displayed - if the line number ruler is showing, only the colors get removed by de-installing the model
} else {
ensureChangeInfoCanBeDisplayed();
installChangeRulerModel();
}
if (getChangeColumn() != null && getChangeColumn().getModel() != null)
fIsChangeInformationShown= true;
else
fIsChangeInformationShown= false;
}
/*
* @see org.eclipse.ui.texteditor.ITextEditorExtension4#showRevisionInformation(org.eclipse.jface.text.revisions.RevisionInformation, java.lang.String)
* @since 3.2
*/
public void showRevisionInformation(RevisionInformation info, String quickDiffProviderId) {
if (!ensureQuickDiffProvider(quickDiffProviderId))
return;
IRevisionRulerColumn revisionColumn= getRevisionColumn();
if (revisionColumn == null)
return;
revisionColumn.setRevisionInformation(info);
fRevisionInfo= info;
fIsRevisionInformationShown= true;
}
/**
* Hides revision information in this editor.
*
* @since 3.2
*/
private void hideRevisionInformation() {
if (fChangeRulerColumn instanceof IRevisionRulerColumn)
((IRevisionRulerColumn) fChangeRulerColumn).setRevisionInformation(null);
if (fLineNumberRulerColumn instanceof IRevisionRulerColumn)
((IRevisionRulerColumn) fLineNumberRulerColumn).setRevisionInformation(null);
fIsRevisionInformationShown= false;
fRevisionInfo= null;
}
/**
* Returns the revision ruler column of this editor, creating one if needed.
*
* @return the revision ruler column of this editor
* @since 3.2
*/
private IRevisionRulerColumn getRevisionColumn() {
if (fChangeRulerColumn instanceof IRevisionRulerColumn)
return (IRevisionRulerColumn) fChangeRulerColumn;
if (fLineNumberRulerColumn instanceof IRevisionRulerColumn)
return (IRevisionRulerColumn) fLineNumberRulerColumn;
return null;
}
/**
* Ensures that quick diff information is displayed and the quick diff provider is the one with
* the specified id. If a different quick diff provider is in use, the user may be asked whether
* he wants to switch.
*
* @param diffProviderId the quick diff provider id to use
* @return <code>true</code> if quick diff could be enabled for the given id,
* <code>false</code> otherwise
* @since 3.2
*/
private boolean ensureQuickDiffProvider(String diffProviderId) {
ISourceViewer viewer= getSourceViewer();
if (viewer == null)
return false;
if (!fIsChangeInformationShown) {
ensureChangeInfoCanBeDisplayed();
installChangeRulerModel();
}
IAnnotationModel oldDiffer= getDiffer();
if (oldDiffer == null)
return false; // quick diff is enabled, but no differ? not working for whatever reason
QuickDiff util= new QuickDiff();
if (util.getConfiguredQuickDiffProvider(oldDiffer).equals(diffProviderId)) {
if (oldDiffer instanceof ILineDifferExtension)
((ILineDifferExtension) oldDiffer).resume();
return true;
}
// quick diff is showing with the wrong provider - ask the user whether he wants to switch
IPreferenceStore store= EditorsUI.getPreferenceStore();
if (!store.getString(REVISION_ASK_BEFORE_QUICKDIFF_SWITCH).equals(MessageDialogWithToggle.ALWAYS)) {
MessageDialogWithToggle toggleDialog= MessageDialogWithToggle.openOkCancelConfirm(
viewer.getTextWidget().getShell(),
TextEditorMessages.AbstractDecoratedTextEditor_revision_quickdiff_switch_title,
TextEditorMessages.AbstractDecoratedTextEditor_revision_quickdiff_switch_message,
TextEditorMessages.AbstractDecoratedTextEditor_revision_quickdiff_switch_rememberquestion,
true,
store,
REVISION_ASK_BEFORE_QUICKDIFF_SWITCH);
if (toggleDialog.getReturnCode() != Window.OK)
return false;
}
IAnnotationModel m= viewer.getAnnotationModel();
if (!(m instanceof IAnnotationModelExtension))
return false;
IAnnotationModelExtension model= (IAnnotationModelExtension) m;
model.removeAnnotationModel(IChangeRulerColumn.QUICK_DIFF_MODEL_ID);
IAnnotationModel newDiffer= util.createQuickDiffAnnotationModel(this, diffProviderId);
model.addAnnotationModel(IChangeRulerColumn.QUICK_DIFF_MODEL_ID, newDiffer);
IChangeRulerColumn changeColumn= getChangeColumn();
if (changeColumn != null)
changeColumn.setModel(m);
return true;
}
/**
* Installs the differ annotation model with the current quick diff display.
*/
private void installChangeRulerModel() {
IChangeRulerColumn column= getChangeColumn();
if (column != null) {
getOrCreateDiffer();
column.setModel(getSourceViewer().getAnnotationModel());
}
IOverviewRuler ruler= getOverviewRuler();
if (ruler != null) {
ruler.addAnnotationType("org.eclipse.ui.workbench.texteditor.quickdiffChange"); //$NON-NLS-1$
ruler.addAnnotationType("org.eclipse.ui.workbench.texteditor.quickdiffAddition"); //$NON-NLS-1$
ruler.addAnnotationType("org.eclipse.ui.workbench.texteditor.quickdiffDeletion"); //$NON-NLS-1$
ruler.update();
}
}
/**
* Uninstalls the differ annotation model from the current quick diff display.
*/
private void uninstallChangeRulerModel() {
IChangeRulerColumn column= getChangeColumn();
if (column != null)
column.setModel(null);
IOverviewRuler ruler= getOverviewRuler();
if (ruler != null) {
ruler.removeAnnotationType("org.eclipse.ui.workbench.texteditor.quickdiffChange"); //$NON-NLS-1$
ruler.removeAnnotationType("org.eclipse.ui.workbench.texteditor.quickdiffAddition"); //$NON-NLS-1$
ruler.removeAnnotationType("org.eclipse.ui.workbench.texteditor.quickdiffDeletion"); //$NON-NLS-1$
ruler.update();
}
IAnnotationModel model= getDiffer();
if (model instanceof ILineDifferExtension)
((ILineDifferExtension) model).suspend();
}
/**
* Ensures that either the line number display is a <code>LineNumberChangeRuler</code> or
* a separate change ruler gets displayed.
*/
private void ensureChangeInfoCanBeDisplayed() {
if (fLineNumberRulerColumn != null) {
if (!(fLineNumberRulerColumn instanceof IChangeRulerColumn)) {
hideLineNumberRuler();
showLineNumberRuler();
}
} else
showChangeRuler(true);
}
/*
* @see org.eclipse.ui.texteditor.ITextEditorExtension3#isChangeInformationShowing()
*/
public boolean isChangeInformationShowing() {
return fIsChangeInformationShown;
}
/**
* Extracts the line differ from the displayed document's annotation model. If none can be found,
* a new differ is created and attached to the annotation model.
*
* @return the line differ, or <code>null</code> if none could be found or created
*/
private IAnnotationModel getOrCreateDiffer() {
IAnnotationModel differ= getDiffer();
// create diff model if it doesn't
if (differ == null) {
IPreferenceStore store= getPreferenceStore();
if (store != null) {
String defaultId= store.getString(AbstractDecoratedTextEditorPreferenceConstants.QUICK_DIFF_DEFAULT_PROVIDER);
differ= new QuickDiff().createQuickDiffAnnotationModel(this, defaultId);
if (differ != null) {
ISourceViewer viewer= getSourceViewer();
if (viewer == null)
return null;
IAnnotationModel m= viewer.getAnnotationModel();
IAnnotationModelExtension model;
if (m instanceof IAnnotationModelExtension)
model= (IAnnotationModelExtension) m;
else
return null;
model.addAnnotationModel(IChangeRulerColumn.QUICK_DIFF_MODEL_ID, differ);
}
}
} else if (differ instanceof ILineDifferExtension && !fIsChangeInformationShown)
((ILineDifferExtension) differ).resume();
return differ;
}
/**
* Extracts the line differ from the displayed document's annotation model. If none can be found,
* <code>null</code> is returned.
*
* @return the line differ, or <code>null</code> if none could be found
*/
private IAnnotationModel getDiffer() {
// get annotation model extension
ISourceViewer viewer= getSourceViewer();
if (viewer == null)
return null;
IAnnotationModel m= viewer.getAnnotationModel();
IAnnotationModelExtension model;
if (m instanceof IAnnotationModelExtension)
model= (IAnnotationModelExtension) m;
else
return null;
// get diff model if it exists already
return model.getAnnotationModel(IChangeRulerColumn.QUICK_DIFF_MODEL_ID);
}
/**
* Returns the <code>IChangeRulerColumn</code> of this editor, or <code>null</code> if there is none. Either
* the line number bar or a separate change ruler column can be returned.
*
* @return an instance of <code>IChangeRulerColumn</code> or <code>null</code>.
*/
private IChangeRulerColumn getChangeColumn() {
if (fChangeRulerColumn != null)
return fChangeRulerColumn;
else if (fLineNumberRulerColumn instanceof IChangeRulerColumn)
return (IChangeRulerColumn) fLineNumberRulerColumn;
else
return null;
}
/**
* Sets the display state of the separate change ruler column (not the quick diff display on
* the line number ruler column) to <code>show</code>.
*
* @param show <code>true</code> if the change ruler column should be shown, <code>false</code> if it should be hidden
*/
private void showChangeRuler(boolean show) {
IVerticalRuler v= getVerticalRuler();
if (v instanceof CompositeRuler) {
CompositeRuler c= (CompositeRuler) v;
if (show && fChangeRulerColumn == null) {
c.addDecorator(1, createChangeRulerColumn());
if (fIsRevisionInformationShown) {
IRevisionRulerColumn column= getRevisionColumn();
column.setRevisionInformation(fRevisionInfo);
}
} else if (!show && fChangeRulerColumn != null) {
c.removeDecorator(fChangeRulerColumn);
fChangeRulerColumn= null;
}
}
}
/**
* Shows the line number ruler column.
*/
private void showLineNumberRuler() {
showChangeRuler(false);
if (fLineNumberRulerColumn == null) {
IVerticalRuler v= getVerticalRuler();
if (v instanceof CompositeRuler) {
CompositeRuler c= (CompositeRuler) v;
c.addDecorator(1, createLineNumberRulerColumn());
}
}
if (fIsRevisionInformationShown) {
IRevisionRulerColumn column= getRevisionColumn();
column.setRevisionInformation(fRevisionInfo);
}
}
/**
* Hides the line number ruler column.
*/
private void hideLineNumberRuler() {
if (fLineNumberRulerColumn != null) {
IVerticalRuler v= getVerticalRuler();
if (v instanceof CompositeRuler) {
CompositeRuler c= (CompositeRuler) v;
c.removeDecorator(fLineNumberRulerColumn);
}
fLineNumberRulerColumn = null;
}
if (fIsChangeInformationShown)
showChangeRuler(true);
}
/**
* 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();
return store != null ? store.getBoolean(AbstractDecoratedTextEditorPreferenceConstants.QUICK_DIFF_ALWAYS_ON) : 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) {
ISharedTextColors sharedColors= getSharedColors();
IPreferenceStore store= getPreferenceStore();
if (store != null) {
RGB rgb= null;
// foreground color
if (store.contains(LINE_NUMBER_COLOR)) {
if (store.isDefault(LINE_NUMBER_COLOR))
rgb= PreferenceConverter.getDefaultColor(store, LINE_NUMBER_COLOR);
else
rgb= PreferenceConverter.getColor(store, LINE_NUMBER_COLOR);
}
if (rgb == null)
rgb= new RGB(0, 0, 0);
rulerColumn.setForeground(sharedColors.getColor(rgb));
rgb= null;
// background color: same as editor, or system default
if (!store.getBoolean(PREFERENCE_COLOR_BACKGROUND_SYSTEM_DEFAULT)) {
if (store.contains(PREFERENCE_COLOR_BACKGROUND)) {
if (store.isDefault(PREFERENCE_COLOR_BACKGROUND))
rgb= PreferenceConverter.getDefaultColor(store, PREFERENCE_COLOR_BACKGROUND);
else
rgb= PreferenceConverter.getColor(store, PREFERENCE_COLOR_BACKGROUND);
}
}
rulerColumn.setBackground(sharedColors.getColor(rgb));
rulerColumn.redraw();
}
}
/**
* Initializes the given change ruler column from the preference store.
*
* @param changeColumn the ruler column to be initialized
*/
private void initializeChangeRulerColumn(IChangeRulerColumn changeColumn) {
ISharedTextColors sharedColors= getSharedColors();
IPreferenceStore store= getPreferenceStore();
if (store != null) {
ISourceViewer v= getSourceViewer();
if (v != null && v.getAnnotationModel() != null) {
changeColumn.setModel(v.getAnnotationModel());
}
Iterator iter= fAnnotationPreferences.getAnnotationPreferences().iterator();
while (iter.hasNext()) {
AnnotationPreference pref= (AnnotationPreference) iter.next();
if ("org.eclipse.ui.workbench.texteditor.quickdiffChange".equals(pref.getAnnotationType())) { //$NON-NLS-1$
RGB rgb= getColorPreference(store, pref);
changeColumn.setChangedColor(sharedColors.getColor(rgb));
} else if ("org.eclipse.ui.workbench.texteditor.quickdiffAddition".equals(pref.getAnnotationType())) { //$NON-NLS-1$
RGB rgb= getColorPreference(store, pref);
changeColumn.setAddedColor(sharedColors.getColor(rgb));
} else if ("org.eclipse.ui.workbench.texteditor.quickdiffDeletion".equals(pref.getAnnotationType())) { //$NON-NLS-1$
RGB rgb= getColorPreference(store, pref);
changeColumn.setDeletedColor(sharedColors.getColor(rgb));
}
}
RGB rgb= null;
// background color: same as editor, or system default
if (!store.getBoolean(PREFERENCE_COLOR_BACKGROUND_SYSTEM_DEFAULT)) {
if (store.contains(PREFERENCE_COLOR_BACKGROUND)) {
if (store.isDefault(PREFERENCE_COLOR_BACKGROUND))
rgb= PreferenceConverter.getDefaultColor(store, PREFERENCE_COLOR_BACKGROUND);
else
rgb= PreferenceConverter.getColor(store, PREFERENCE_COLOR_BACKGROUND);
}
}
changeColumn.setBackground(sharedColors.getColor(rgb));
if (changeColumn instanceof LineNumberChangeRulerColumn) {
LineNumberChangeRulerColumn lncrc= (LineNumberChangeRulerColumn) changeColumn;
lncrc.setDisplayMode(store.getBoolean(AbstractDecoratedTextEditorPreferenceConstants.QUICK_DIFF_CHARACTER_MODE));
}
}
changeColumn.redraw();
}
/**
* Extracts the color preference for the given preference from the given store.
* If the given store indicates that the default value is to be used, or
* the value stored in the preferences store is <code>null</code>,
* the value is taken from the <code>AnnotationPreference</code>'s default
* color value.
* <p>
* The return value is
* </p>
*
* @param store the preference store
* @param pref the annotation preference
* @return the RGB color preference, not <code>null</code>
*/
private RGB getColorPreference(IPreferenceStore store, AnnotationPreference pref) {
RGB rgb= null;
if (store.contains(pref.getColorPreferenceKey())) {
if (store.isDefault(pref.getColorPreferenceKey()))
rgb= pref.getColorPreferenceValue();
else
rgb= PreferenceConverter.getColor(store, pref.getColorPreferenceKey());
}
if (rgb == null)
rgb= pref.getColorPreferenceValue();
return rgb;
}
/**
* Creates a new line number ruler column that is appropriately initialized.
*
* @return the created line number column
*/
protected IVerticalRulerColumn createLineNumberRulerColumn() {
if (isPrefQuickDiffAlwaysOn()) {
LineNumberChangeRulerColumn column= new LineNumberChangeRulerColumn(getSharedColors());
column.setHover(createChangeHover());
initializeChangeRulerColumn(column);
fLineNumberRulerColumn= column;
} else {
fLineNumberRulerColumn= new LineNumberRulerColumn();
}
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
*/
protected IChangeRulerColumn createChangeRulerColumn() {
IChangeRulerColumn column= new ChangeRulerColumn(getSharedColors());
column.setHover(createChangeHover());
fChangeRulerColumn= column;
initializeChangeRulerColumn(fChangeRulerColumn);
return fChangeRulerColumn;
}
/**
* 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()
*/
protected IVerticalRuler createVerticalRuler() {
CompositeRuler ruler= createCompositeRuler();
IPreferenceStore store= getPreferenceStore();
if (ruler != null && store != null) {
for (Iterator iter= ruler.getDecoratorIterator(); iter.hasNext();) {
IVerticalRulerColumn column= (IVerticalRulerColumn)iter.next();
if (column instanceof AnnotationRulerColumn) {
fAnnotationRulerColumn= (AnnotationRulerColumn)column;
for (Iterator iter2= fAnnotationPreferences.getAnnotationPreferences().iterator(); iter2.hasNext();) {
AnnotationPreference preference= (AnnotationPreference)iter2.next();
String key= preference.getVerticalRulerPreferenceKey();
boolean showAnnotation= true;
if (key != null && store.contains(key))
showAnnotation= store.getBoolean(key);
if (showAnnotation)
fAnnotationRulerColumn.addAnnotationType(preference.getAnnotationType());
}
fAnnotationRulerColumn.addAnnotationType(Annotation.TYPE_UNKNOWN);
break;
}
}
}
return ruler;
}
/**
* 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() {
CompositeRuler ruler= new CompositeRuler();
ruler.addDecorator(0, createAnnotationRulerColumn(ruler));
if (isLineNumberRulerVisible())
ruler.addDecorator(1, createLineNumberRulerColumn());
else if (isPrefQuickDiffAlwaysOn())
ruler.addDecorator(1, createChangeRulerColumn());
return ruler;
}
/**
* 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());
}
/*
* @see AbstractTextEditor#handlePreferenceStoreChanged(PropertyChangeEvent)
*/
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 (DISABLE_OVERWRITE_MODE.equals(property)) {
enableOverwriteMode(isOverwriteModeEnabled());
return;
}
if (LINE_NUMBER_RULER.equals(property)) {
if (isLineNumberRulerVisible())
showLineNumberRuler();
else
hideLineNumberRuler();
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));
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 (fLineNumberRulerColumn != null
&& (LINE_NUMBER_COLOR.equals(property)
|| PREFERENCE_COLOR_BACKGROUND_SYSTEM_DEFAULT.equals(property)
|| PREFERENCE_COLOR_BACKGROUND.equals(property))) {
initializeLineNumberRulerColumn(fLineNumberRulerColumn);
}
if (fChangeRulerColumn != null
&& (LINE_NUMBER_COLOR.equals(property)
|| PREFERENCE_COLOR_BACKGROUND_SYSTEM_DEFAULT.equals(property)
|| PREFERENCE_COLOR_BACKGROUND.equals(property))) {
initializeChangeRulerColumn(fChangeRulerColumn);
}
if (fLineNumberRulerColumn instanceof LineNumberChangeRulerColumn
&& AbstractDecoratedTextEditorPreferenceConstants.QUICK_DIFF_CHARACTER_MODE.equals(property)) {
initializeChangeRulerColumn(getChangeColumn());
}
if (AbstractDecoratedTextEditorPreferenceConstants.SHOW_RANGE_INDICATOR.equals(property)) {
if (isRangeIndicatorEnabled()) {
getSourceViewer().setRangeIndicator(getRangeIndicator());
} else {
getSourceViewer().removeRangeIndication();
getSourceViewer().setRangeIndicator(null);
}
}
AnnotationPreference pref= getAnnotationPreference(property);
if (pref != null) {
IChangeRulerColumn column= getChangeColumn();
if (column != null) {
Object type= pref.getAnnotationType();
if (type instanceof String) {
String annotationType= (String) type;
if (annotationType.startsWith("org.eclipse.ui.workbench.texteditor.quickdiff")) //$NON-NLS-1$
initializeChangeRulerColumn(column);
}
}
}
AnnotationPreference annotationPreference= getVerticalRulerAnnotationPreference(property);
if (annotationPreference != null && event.getNewValue() instanceof Boolean) {
Object type= annotationPreference.getAnnotationType();
if (((Boolean)event.getNewValue()).booleanValue())
fAnnotationRulerColumn.addAnnotationType(type);
else
fAnnotationRulerColumn.removeAnnotationType(type);
getVerticalRuler().update();
}
} finally {
super.handlePreferenceStoreChanged(event);
}
}
/**
* Returns the <code>AnnotationPreference</code> corresponding to <code>colorKey</code>.
*
* @param colorKey the color key.
* @return the corresponding <code>AnnotationPreference</code>
*/
private AnnotationPreference getAnnotationPreference(String colorKey) {
for (Iterator iter= fAnnotationPreferences.getAnnotationPreferences().iterator(); iter.hasNext();) {
AnnotationPreference pref= (AnnotationPreference) iter.next();
if (colorKey.equals(pref.getColorPreferenceKey()))
return pref;
}
return null;
}
/**
* Returns the annotation preference for which the given
* preference matches a vertical ruler preference key.
*
* @param preferenceKey the preference key string
* @return the annotation preference or <code>null</code> if none
*/
private AnnotationPreference getVerticalRulerAnnotationPreference(String preferenceKey) {
if (preferenceKey == null)
return null;
Iterator e= fAnnotationPreferences.getAnnotationPreferences().iterator();
while (e.hasNext()) {
AnnotationPreference info= (AnnotationPreference) e.next();
if (info != null && preferenceKey.equals(info.getVerticalRulerPreferenceKey()))
return info;
}
return null;
}
/**
* 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>
*/
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 - 1 < length && start < length)
selectAndReveal(start, end - start);
}
/*
* @see org.eclipse.ui.texteditor.StatusTextEditor#isErrorStatus(org.eclipse.core.runtime.IStatus)
*/
protected boolean isErrorStatus(IStatus status) {
// see bug 42230
return super.isErrorStatus(status) && status.getCode() != IResourceStatus.READ_ONLY_LOCAL && status.getSeverity() != IStatus.CANCEL;
}
/*
* @see org.eclipse.ui.texteditor.AbstractTextEditor#createActions()
*/
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(IWorkbenchActionDefinitionIds.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(IWorkbenchActionDefinitionIds.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$
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$
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(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$
public void run() {
hideRevisionInformation();
}
};
setAction(ITextEditorActionConstants.REVISION_HIDE_INFO, action);
final Shell shell;
if (getSourceViewer() != null)
shell= getSourceViewer().getTextWidget().getShell();
else
shell= null;
action= new ResourceAction(TextEditorMessages.getBundleForConstructedKeys(), "Editor.RulerPreferencesAction.") { //$NON-NLS-1$
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$
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);
}
public Object getAdapter(Class adapter) {
if (IGotoMarker.class.equals(adapter))
return fGotoMarkerAdapter;
if (IAnnotationAccess.class.equals(adapter))
return getAnnotationAccess();
if (adapter == IShowInSource.class) {
return new IShowInSource() {
public ShowInContext getShowInContext() {
ISelection selection= null;
ISelectionProvider selectionProvider= getSelectionProvider();
if (selectionProvider != null)
selection= selectionProvider.getSelection();
return new ShowInContext(getEditorInput(), selection);
}
};
}
// XXX: This will go away in 3.3, see bug https://bugs.eclipse.org/bugs/show_bug.cgi?id=135231
if (adapter != null && "org.eclipse.jface.internal.text.revisions.RevisionSelectionProvider".equals(adapter.getName())) { //$NON-NLS-1$
IRevisionRulerColumn revisionColumn= getRevisionColumn();
if (revisionColumn instanceof ChangeRulerColumn)
return ((ChangeRulerColumn) revisionColumn).getRevisionSelectionProvider();
if (revisionColumn instanceof LineNumberChangeRulerColumn)
return ((LineNumberChangeRulerColumn) revisionColumn).getRevisionSelectionProvider();
}
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)
*/
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);
}
}
/*
* @see org.eclipse.ui.texteditor.ITextEditor#getDocumentProvider()
*/
public IDocumentProvider getDocumentProvider() {
IDocumentProvider provider= super.getDocumentProvider();
if (provider == null)
return fImplicitDocumentProvider;
return provider;
}
/*
* @see org.eclipse.ui.texteditor.AbstractTextEditor#disposeDocumentProvider()
*/
protected void disposeDocumentProvider() {
super.disposeDocumentProvider();
fImplicitDocumentProvider= null;
}
/*
* @see AbstractTextEditor#doSetInput(IEditorInput)
*
* This implementation also updates change information in the quick diff
* ruler.
*/
protected void doSetInput(IEditorInput input) throws CoreException {
if (fIsChangeInformationShown) {
if (isPrefQuickDiffAlwaysOn()) {
// only uninstall the model since we will reuse the change ruler
uninstallChangeRulerModel();
fIsChangeInformationShown= false;
} else
showChangeInformation(false);
}
if (fIsRevisionInformationShown)
hideRevisionInformation();
super.doSetInput(input);
if (isPrefQuickDiffAlwaysOn())
showChangeInformation(true);
}
/**
* 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
*/
protected void performSaveAs(IProgressMonitor progressMonitor) {
// XXX: To be removed in 3.3: we will always execute this code
if (!handleSaveAs())
return;
Shell shell= getSite().getShell();
final IEditorInput input= getEditorInput();
IDocumentProvider provider= getDocumentProvider();
final IEditorInput newInput;
if (input instanceof JavaFileEditorInput) {
FileDialog dialog= new FileDialog(shell, SWT.SAVE);
dialog.setFileName(((JavaFileEditorInput)input).getName());
dialog.setFilterPath(((JavaFileEditorInput)input).getPath().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 externalFile= new File(path);
if (externalFile.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= EFS.getLocalFileSystem().getStore(externalFile.toURI());
IFile file= getWorkspaceFile(fileStore);
if (file != null)
newInput= new FileEditorInput(file);
else
newInput= new JavaFileEditorInput(fileStore);
} else {
SaveAsDialog dialog= new SaveAsDialog(shell);
IFile original= (input instanceof IFileEditorInput) ? ((IFileEditorInput) input).getFile() : null;
if (original != null)
dialog.setOriginalFile(original);
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);
}
/**
* 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.findFilesForLocation(new Path(fileStore.toURI().getPath()));
if (files != null && files.length == 1)
return files[0];
return null;
}
/**
* Tells whether this class should handle {@link #performSaveAs(IProgressMonitor)}.
*
* @return <code>true</code> if handled by this class
* @since 3.2
*/
private boolean handleSaveAs() {
IPreferenceStore store= EditorsUI.getPreferenceStore();
String key= getEditorSite().getId() + ".internal.delegateSaveAs"; //$NON-NLS-1$
return store.getBoolean(key);
}
/*
* @see org.eclipse.ui.texteditor.AbstractTextEditor#rulerContextMenuAboutToShow(org.eclipse.jface.action.IMenuManager)
* @since 3.1
*/
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);
/* 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 (fIsRevisionInformationShown) {
IAction hideRevisionInfoAction= getAction(ITextEditorActionConstants.REVISION_HIDE_INFO);
menu.appendToGroup(ITextEditorActionConstants.GROUP_RULERS, hideRevisionInfoAction);
}
IAction lineNumberAction= getAction(ITextEditorActionConstants.LINENUMBERS_TOGGLE);
lineNumberAction.setChecked(fLineNumberRulerColumn != null);
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);
}
/**
* Toggles the line number global preference and shows the line number ruler
* accordingly.
*
* @since 3.1
*/
private void toggleLineNumberRuler() {
boolean newSetting= fLineNumberRulerColumn == null;
// locally
if (newSetting)
showLineNumberRuler();
else
hideLineNumberRuler();
// globally
IPreferenceStore store= EditorsUI.getPreferenceStore();
if (store != null) {
store.setValue(LINE_NUMBER_RULER, newSetting);
}
}
/**
* Toggles the quick diff global preference and shows the quick diff ruler
* accordingly.
*
* @since 3.1
*/
private void toggleQuickDiffRuler() {
boolean newSetting= !isChangeInformationShowing();
// change locally to ensure we get it right even if our setting
// does not adhere to the global setting
showChangeInformation(newSetting);
// change global setting which will inform other interested parties
// the it is changed
IPreferenceStore store= EditorsUI.getPreferenceStore();
if (store != null) {
store.setValue(AbstractDecoratedTextEditorPreferenceConstants.QUICK_DIFF_ALWAYS_ON, newSetting);
}
}
/*
* @see org.eclipse.ui.texteditor.AbstractTextEditor#editorContextMenuAboutToShow(org.eclipse.jface.action.IMenuManager)
* @since 3.1
*/
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));
MenuManager showInSubMenu= new MenuManager(getShowInMenuLabel());
showInSubMenu.add(ContributionItemFactory.VIEWS_SHOW_IN.create(getEditorSite().getWorkbenchWindow()));
menu.appendToGroup(ITextEditorActionConstants.GROUP_OPEN, showInSubMenu);
}
/**
* 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= (IBindingService)PlatformUI.getWorkbench().getAdapter(IBindingService.class);
if (bindingService != null)
keyBinding= bindingService.getBestActiveBindingFormattedFor("org.eclipse.ui.navigate.showInQuickMenu"); //$NON-NLS-1$
if (keyBinding == null)
keyBinding= ""; //$NON-NLS-1$
return NLS.bind(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.
* <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$
};
}
/**
* Returns the preference page ids of the preference pages to be shown when
* executing the preferences action from the editor ruler context menu.
* <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();
}
/*
* @see AbstractTextEditor#getUndoRedoOperationApprover(IUndoContext)
* @since 3.1
*/
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
*/
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
*/
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
* @since 3.2
*/
protected void updateMarkerViews(Annotation annotation) {
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;
}
}
}
}