blob: 0d0ba9d04cabbbf35684099c13b73da67c18ff00 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2009, 2017 IBM Corporation and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.team.internal.ui.synchronize;
import java.lang.reflect.InvocationTargetException;
import org.eclipse.compare.CompareConfiguration;
import org.eclipse.compare.CompareEditorInput;
import org.eclipse.compare.ICompareContainer;
import org.eclipse.compare.IEditableContent;
import org.eclipse.compare.IPropertyChangeNotifier;
import org.eclipse.compare.IResourceProvider;
import org.eclipse.compare.ITypedElement;
import org.eclipse.compare.structuremergeviewer.Differencer;
import org.eclipse.compare.structuremergeviewer.ICompareInput;
import org.eclipse.compare.structuremergeviewer.ICompareInputChangeListener;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IMarker;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.Adapters;
import org.eclipse.core.runtime.Assert;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.ISafeRunnable;
import org.eclipse.core.runtime.ListenerList;
import org.eclipse.core.runtime.SafeRunner;
import org.eclipse.jface.action.IContributionItem;
import org.eclipse.jface.action.IMenuManager;
import org.eclipse.jface.action.MenuManager;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.jface.resource.ImageRegistry;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.ITextViewer;
import org.eclipse.jface.util.IPropertyChangeListener;
import org.eclipse.jface.viewers.ISelectionProvider;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.osgi.util.NLS;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.team.internal.ui.ITeamUIImages;
import org.eclipse.team.internal.ui.TeamUIMessages;
import org.eclipse.team.internal.ui.TeamUIPlugin;
import org.eclipse.team.internal.ui.history.CompareFileRevisionEditorInput;
import org.eclipse.team.internal.ui.mapping.AbstractCompareInput;
import org.eclipse.team.internal.ui.mapping.CompareInputChangeNotifier;
import org.eclipse.team.internal.ui.synchronize.EditableSharedDocumentAdapter.ISharedDocumentAdapterListener;
import org.eclipse.team.ui.mapping.SaveableComparison;
import org.eclipse.team.ui.synchronize.SaveableCompareEditorInput;
import org.eclipse.ui.IEditorDescriptor;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.IFileEditorInput;
import org.eclipse.ui.IPropertyListener;
import org.eclipse.ui.ISaveablesLifecycleListener;
import org.eclipse.ui.ISaveablesSource;
import org.eclipse.ui.IWorkbenchCommandConstants;
import org.eclipse.ui.IWorkbenchPage;
import org.eclipse.ui.IWorkbenchPart;
import org.eclipse.ui.IWorkbenchSite;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.Saveable;
import org.eclipse.ui.SaveablesLifecycleEvent;
import org.eclipse.ui.actions.ContributionItemFactory;
import org.eclipse.ui.actions.OpenFileAction;
import org.eclipse.ui.actions.OpenWithMenu;
import org.eclipse.ui.actions.WorkspaceModifyOperation;
import org.eclipse.ui.ide.IGotoMarker;
import org.eclipse.ui.keys.IBindingService;
import org.eclipse.ui.services.IDisposable;
import org.eclipse.ui.texteditor.ITextEditor;
/**
* A compare editor input that makes use of a {@link Saveable} to manage the
* save lifecycle of the left and right sides of the comparison. The ancestor
* part of the comparison is not editable.
*/
public class SaveablesCompareEditorInput extends CompareEditorInput implements
ISaveablesSource {
private IPropertyListener fLeftPropertyListener;
private IPropertyListener fRightPropertyListener;
private Saveable fLeftSaveable;
private Saveable fRightSaveable;
private ITypedElement fAncestorElement;
private ITypedElement fLeftElement;
private ITypedElement fRightElement;
private final IWorkbenchPage page;
private final ListenerList<ICompareInputChangeListener> inputChangeListeners = new ListenerList<>(
ListenerList.IDENTITY);
private ICompareInputChangeListener compareInputChangeListener;
public SaveablesCompareEditorInput(ITypedElement ancestor,
ITypedElement left, ITypedElement right, IWorkbenchPage page) {
super(new CompareConfiguration());
this.page = page;
this.fAncestorElement = ancestor;
this.fLeftElement = left;
this.fRightElement = right;
}
private static ITypedElement getFileElement(ITypedElement element,
CompareEditorInput editorInput) {
if (element instanceof LocalResourceTypedElement) {
return element;
}
if (editorInput instanceof CompareFileRevisionEditorInput) {
return ((CompareFileRevisionEditorInput) editorInput)
.getLocalElement();
}
return null;
}
/**
* Return a typed element that represents a local file. If the element
* returned from this method is used as the left contributor of the compare
* input for a {@link SaveableCompareEditorInput}, then the file will be
* properly saved when the compare editor input or viewers are saved.
*
* @param file
* the file
* @return a typed element that represents a local file.
*/
public static ITypedElement createFileElement(IFile file) {
return new LocalResourceTypedElement(file);
}
private ISaveablesLifecycleListener getSaveablesLifecycleListener(
IWorkbenchPart part) {
ISaveablesLifecycleListener listener = Adapters.adapt(part, ISaveablesLifecycleListener.class);
if (listener == null)
listener = part.getSite().getService(ISaveablesLifecycleListener.class);
return listener;
}
@Override
protected void contentsCreated() {
super.contentsCreated();
compareInputChangeListener = source -> {
if (source == getCompareResult()) {
boolean closed = false;
if (source.getKind() == Differencer.NO_CHANGE) {
closed = closeEditor(true);
}
if (!closed) {
// The editor was not closed either because the compare
// input still has changes or because the editor input
// is dirty. In either case, fire the changes
// to the registered listeners
propogateInputChange();
}
}
};
getCompareInput().addCompareInputChangeListener(
compareInputChangeListener);
if (getLeftSaveable() instanceof SaveableComparison) {
SaveableComparison lscm = (SaveableComparison) fLeftSaveable;
fLeftPropertyListener = (source, propId) -> {
if (propId == SaveableComparison.PROP_DIRTY) {
setLeftDirty(fLeftSaveable.isDirty());
}
};
lscm.addPropertyListener(fLeftPropertyListener);
}
if (getRightSaveable() instanceof SaveableComparison) {
SaveableComparison rscm = (SaveableComparison) fRightSaveable;
fRightPropertyListener = (source, propId) -> {
if (propId == SaveableComparison.PROP_DIRTY) {
setRightDirty(fRightSaveable.isDirty());
}
};
rscm.addPropertyListener(fRightPropertyListener);
}
setLeftDirty(fLeftSaveable.isDirty());
setRightDirty(fRightSaveable.isDirty());
}
@Override
protected void handleDispose() {
super.handleDispose();
ICompareInput compareInput = getCompareInput();
if (compareInput != null)
compareInput
.removeCompareInputChangeListener(compareInputChangeListener);
compareInputChangeListener = null;
if (fLeftSaveable instanceof SaveableComparison) {
SaveableComparison scm = (SaveableComparison) fLeftSaveable;
scm.removePropertyListener(fLeftPropertyListener);
}
if (fLeftSaveable instanceof LocalResourceSaveableComparison) {
LocalResourceSaveableComparison rsc = (LocalResourceSaveableComparison) fLeftSaveable;
rsc.dispose();
}
if (fRightSaveable instanceof SaveableComparison) {
SaveableComparison scm = (SaveableComparison) fRightSaveable;
scm.removePropertyListener(fRightPropertyListener);
}
if (fRightSaveable instanceof LocalResourceSaveableComparison) {
LocalResourceSaveableComparison rsc = (LocalResourceSaveableComparison) fRightSaveable;
rsc.dispose();
}
if (getCompareResult() instanceof IDisposable) {
((IDisposable) getCompareResult()).dispose();
}
}
private String[] getLabels() {
IResource leftResource = getResource(fLeftElement);
IResource rightResource = getResource(fRightElement);
if (leftResource != null && rightResource != null) {
String leftLabel = leftResource.getFullPath().makeRelative().toString();
String rightLabel = rightResource.getFullPath().makeRelative().toString();
if (fAncestorElement != null) {
IResource ancestorResource = getResource(fAncestorElement);
if (ancestorResource != null) {
String ancestorLabel = rightResource.getFullPath().makeRelative().toString();
return new String[] { ancestorLabel, leftLabel, rightLabel };
}
}
return new String[] { leftLabel, rightLabel };
}
if (fAncestorElement != null) {
return new String[] { fAncestorElement.getName(), fLeftElement.getName(), fRightElement.getName() };
}
return new String[] { fLeftElement.getName(), fRightElement.getName() };
}
@Override
public String getToolTipText() {
String[] labels = getLabels();
if (labels.length == 3)
return NLS.bind(TeamUIMessages.SaveablesCompareEditorInput_threeWayTooltip, labels);
return NLS.bind(TeamUIMessages.SaveablesCompareEditorInput_twoWayTooltip, labels);
}
@Override
public String getTitle() {
String[] labels = getLabels();
if (labels.length == 3)
return NLS.bind(TeamUIMessages.SaveablesCompareEditorInput_threeWayTitle, labels);
return NLS.bind(TeamUIMessages.SaveablesCompareEditorInput_twoWayTitle, labels);
}
private IWorkbenchPage getPage() {
if (page == null)
return PlatformUI.getWorkbench().getActiveWorkbenchWindow()
.getActivePage();
return page;
}
/**
* Return the compare input of this editor input.
*
* @return the compare input of this editor input
*/
protected final ICompareInput getCompareInput() {
return (ICompareInput) getCompareResult();
}
/**
* Callback from the resource saveable that is invoked when the resource is
* saved so that this input can fire a change event for its input.
* Subclasses only need this method if the left side of their compare input
* is an element returned from
* {@link SaveableCompareEditorInput#createFileElement(IFile)}.
*/
protected void fireInputChange() {
((MyDiffNode) getCompareResult()).fireChange();
}
protected Saveable getLeftSaveable() {
if (fLeftSaveable == null) {
fLeftSaveable = createLeftSaveable();
}
return fLeftSaveable;
}
protected Saveable getRightSaveable() {
if (fRightSaveable == null) {
fRightSaveable = createRightSaveable();
}
return fRightSaveable;
}
protected Saveable createLeftSaveable() {
Object compareResult = getCompareResult();
Assert
.isNotNull(compareResult,
"This method cannot be called until after prepareInput is called"); //$NON-NLS-1$
ITypedElement leftFileElement = getFileElement(getCompareInput()
.getLeft(), this);
return new InternalResourceSaveableComparison(
(ICompareInput) compareResult, this, leftFileElement);
}
protected Saveable createRightSaveable() {
Object compareResult = getCompareResult();
Assert
.isNotNull(compareResult,
"This method cannot be called until after prepareInput is called"); //$NON-NLS-1$
ITypedElement rightFileElement = getFileElement(getCompareInput()
.getRight(), this);
return new InternalResourceSaveableComparison(
(ICompareInput) compareResult, this, rightFileElement);
}
@Override
public Saveable[] getActiveSaveables() {
if (getCompareResult() == null)
return new Saveable[0];
return new Saveable[] { getLeftSaveable(), getRightSaveable() };
}
@Override
public Saveable[] getSaveables() {
return getActiveSaveables();
}
@Override
public Viewer findContentViewer(Viewer pOldViewer, ICompareInput pInput,
Composite pParent) {
Viewer newViewer = super.findContentViewer(pOldViewer, pInput, pParent);
boolean isNewViewer = newViewer != pOldViewer;
if (isNewViewer && newViewer instanceof IPropertyChangeNotifier
&& fLeftSaveable instanceof IPropertyChangeListener
&& fRightSaveable instanceof IPropertyChangeListener) {
// Register the model for change events if appropriate
final IPropertyChangeNotifier dsp = (IPropertyChangeNotifier) newViewer;
final IPropertyChangeListener lpcl = (IPropertyChangeListener) fLeftSaveable;
final IPropertyChangeListener rpcl = (IPropertyChangeListener) fRightSaveable;
dsp.addPropertyChangeListener(lpcl);
dsp.addPropertyChangeListener(rpcl);
Control c = newViewer.getControl();
c.addDisposeListener(e -> {
dsp.removePropertyChangeListener(lpcl);
dsp.removePropertyChangeListener(rpcl);
});
}
return newViewer;
}
@Override
public boolean isDirty() {
if (fLeftSaveable != null && fLeftSaveable.isDirty())
return true;
if (fRightSaveable != null && fRightSaveable.isDirty())
return true;
return super.isDirty();
}
/**
* Returns <code>true</code> if the given saveable contains any unsaved
* changes. If the saveable doesn't match either left nor right side of the
* current editor input {@link CompareEditorInput#isSaveNeeded()} is called.
* <p>
* This method is preferred to {@link CompareEditorInput#isSaveNeeded()}.
*
* @param the
* the saveable to check
* @return <code>true</code> if there are changes that need to be saved
* @since 3.7
*/
boolean isSaveNeeded(Saveable saveable) {
if (saveable == null) {
return isSaveNeeded();
}
if (saveable.equals(fLeftSaveable)) {
return isLeftSaveNeeded();
}
if (saveable.equals(fRightSaveable)) {
return isRightSaveNeeded();
}
// Fallback call returning true if there are unsaved changes in either
// left or right side
return isSaveNeeded();
}
void setDirty(boolean dirty, Saveable saveable) {
if (saveable.equals(fLeftSaveable)) {
setLeftDirty(dirty);
}
if (saveable.equals(fRightSaveable)) {
setRightDirty(dirty);
}
}
void saveChanges(IProgressMonitor monitor, Saveable saveable)
throws CoreException {
if (saveable.equals(fLeftSaveable)) {
flushLeftViewers(monitor);
return;
} else if (saveable.equals(fRightSaveable)) {
flushRightViewers(monitor);
return;
}
Assert.isTrue(false, "invalid saveable parameter"); //$NON-NLS-1$
}
/**
* Close the editor if it is not dirty. If it is still dirty, let the
* content merge viewer handle the compare input change.
*
* @param checkForUnsavedChanges
* whether to check for unsaved changes
* @return <code>true</code> if the editor was closed (note that the close
* may be asynchronous)
*/
protected boolean closeEditor(boolean checkForUnsavedChanges) {
if (isSaveNeeded() && checkForUnsavedChanges) {
return false;
} else {
Runnable runnable = () -> {
IEditorPart part = getPage().findEditor(
SaveablesCompareEditorInput.this);
getPage().closeEditor(part, false);
};
if (Display.getCurrent() != null) {
runnable.run();
} else {
Display display = getPage().getWorkbenchWindow().getShell()
.getDisplay();
display.asyncExec(runnable);
}
return true;
}
}
/**
* Prepare the compare input of this editor input. This method is not
* intended to be overridden of extended by subclasses (but is not final for
* backwards compatibility reasons). The implementation of this method in
* this class delegates the creation of the compare input to the
* {@link #prepareCompareInput(IProgressMonitor)} method which subclasses
* must implement.
*
* @see org.eclipse.compare.CompareEditorInput#prepareInput(org.eclipse.core.runtime.IProgressMonitor)
*/
@Override
protected Object prepareInput(IProgressMonitor monitor)
throws InvocationTargetException, InterruptedException {
final ICompareInput input = prepareCompareInput(monitor);
if (input != null)
setTitle(NLS.bind(TeamUIMessages.SyncInfoCompareInput_title,
new String[] { input.getName() }));
return input;
}
/**
* Method called from {@link #prepareInput(IProgressMonitor)} to obtain the input. Its purpose
* is to ensure that the input is an instance of {@link ICompareInput}.
*
* @param monitor a progress monitor
* @return the compare input
* @throws InvocationTargetException
* @throws InterruptedException
*/
protected ICompareInput prepareCompareInput(IProgressMonitor monitor)
throws InvocationTargetException, InterruptedException {
ICompareInput input = createCompareInput();
getCompareConfiguration().setLeftEditable(isEditable(input.getLeft()));
getCompareConfiguration()
.setRightEditable(isEditable(input.getRight()));
initLabels();
return input;
}
private boolean isEditable(Object obj) {
if (obj instanceof IEditableContent) {
return ((IEditableContent) obj).isEditable();
}
return false;
}
private void initLabels() {
CompareConfiguration cc = getCompareConfiguration();
IResource ancestorResource = getResource(fAncestorElement);
IResource leftResource = getResource(fLeftElement);
IResource rightResource = getResource(fRightElement);
if (ancestorResource != null) {
String ancestorLabel = ancestorResource.getFullPath()
.makeRelative().toString();
cc.setAncestorLabel(ancestorLabel);
}
if (leftResource != null && rightResource != null) {
String leftLabel = leftResource.getFullPath().makeRelative()
.toString();
String rightLabel = rightResource.getFullPath().makeRelative()
.toString();
cc.setLeftLabel(leftLabel);
cc.setRightLabel(rightLabel);
}
}
private ICompareInput createCompareInput() {
return fAncestorElement == null ? new MyDiffNode(fLeftElement,
fRightElement) : new MyDiffNode(fAncestorElement, fLeftElement,
fRightElement);
}
private CompareInputChangeNotifier notifier = new CompareInputChangeNotifier() {
@Override
protected IResource[] getResources(ICompareInput input) {
IResource leftResource = getResource(fLeftElement);
IResource rightResource = getResource(fRightElement);
if (leftResource == null && rightResource == null)
return new IResource[0];
if (leftResource == null && rightResource != null)
return new IResource[] { rightResource };
if (leftResource != null && rightResource == null)
return new IResource[] { leftResource };
return new IResource[] { leftResource, rightResource };
}
};
private class MyDiffNode extends AbstractCompareInput {
public MyDiffNode(ITypedElement left, ITypedElement right) {
super(Differencer.CHANGE, null, left, right);
}
public MyDiffNode(ITypedElement ancestor, ITypedElement left,
ITypedElement right) {
super(Differencer.CONFLICTING, ancestor, left, right);
}
@Override
public void fireChange() {
super.fireChange();
}
@Override
protected CompareInputChangeNotifier getChangeNotifier() {
return notifier;
}
@Override
public boolean needsUpdate() {
return true;
}
@Override
public void update() {
fireChange();
}
}
private IResource getResource(ITypedElement pElement) {
if (pElement instanceof LocalResourceTypedElement
&& pElement instanceof IResourceProvider) {
return ((IResourceProvider) pElement).getResource();
}
return null;
}
@Override
public void registerContextMenu(final MenuManager pMenuManager,
final ISelectionProvider pSelectionProvider) {
super.registerContextMenu(pMenuManager, pSelectionProvider);
final Saveable lLeftSaveable = getLeftSaveable();
final ITypedElement lLeftElement = getFileElement(getCompareInput()
.getLeft(), this);
if (lLeftSaveable instanceof LocalResourceSaveableComparison) {
pMenuManager.addMenuListener(manager -> handleMenuAboutToShow(manager, getContainer(), lLeftSaveable, lLeftElement, pSelectionProvider));
}
final Saveable lRightSaveable = getRightSaveable();
final ITypedElement lRightElement = getFileElement(getCompareInput()
.getRight(), this);
if (lRightSaveable instanceof LocalResourceSaveableComparison) {
pMenuManager.addMenuListener(manager -> handleMenuAboutToShow(manager, getContainer(), lRightSaveable, lRightElement, pSelectionProvider));
}
}
@Override
public void addCompareInputChangeListener(ICompareInput input,
ICompareInputChangeListener listener) {
if (input == getCompareResult()) {
inputChangeListeners.add(listener);
} else {
super.addCompareInputChangeListener(input, listener);
}
}
@Override
public void removeCompareInputChangeListener(ICompareInput input,
ICompareInputChangeListener listener) {
if (input == getCompareResult()) {
inputChangeListeners.remove(listener);
} else {
super.removeCompareInputChangeListener(input, listener);
}
}
private void propogateInputChange() {
if (!inputChangeListeners.isEmpty()) {
Object[] allListeners = inputChangeListeners.getListeners();
final ICompareInput compareResult = (ICompareInput) getCompareResult();
for (Object l : allListeners) {
final ICompareInputChangeListener listener = (ICompareInputChangeListener) l;
SafeRunner.run(new ISafeRunnable() {
@Override
public void run() throws Exception {
listener.compareInputChanged(compareResult);
}
@Override
public void handleException(Throwable exception) {
// Logged by the safe runner
}
});
}
}
}
@Override
public Image getTitleImage() {
ImageRegistry reg = TeamUIPlugin.getPlugin().getImageRegistry();
Image image = reg.get(ITeamUIImages.IMG_SYNC_VIEW);
if (image == null) {
image = getImageDescriptor().createImage();
reg.put(ITeamUIImages.IMG_SYNC_VIEW, image);
}
return image;
}
@Override
public ImageDescriptor getImageDescriptor() {
return TeamUIPlugin.getImageDescriptor(ITeamUIImages.IMG_SYNC_VIEW);
}
@Override
public boolean canRunAsJob() {
return true;
}
private static 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 NLS
.bind(TeamUIMessages.SaveableCompareEditorInput_0, keyBinding);
}
// TODO: add getAdapter for IFile[]
private class InternalResourceSaveableComparison extends
LocalResourceSaveableComparison implements
ISharedDocumentAdapterListener {
private LocalResourceTypedElement lrte;
private boolean connected = false;
public InternalResourceSaveableComparison(ICompareInput input,
CompareEditorInput editorInput, ITypedElement element) {
super(input, editorInput, element);
if (element instanceof LocalResourceTypedElement) {
lrte = (LocalResourceTypedElement) element;
if (lrte.isConnected()) {
registerSaveable(true);
} else {
lrte.setSharedDocumentListener(this);
}
}
}
@Override
protected void fireInputChange() {
SaveablesCompareEditorInput.this.fireInputChange();
}
@Override
public void dispose() {
super.dispose();
if (lrte != null)
lrte.setSharedDocumentListener(null);
}
@Override
public void handleDocumentConnected() {
if (connected)
return;
connected = true;
registerSaveable(false);
if (lrte != null)
lrte.setSharedDocumentListener(null);
}
private void registerSaveable(boolean init) {
ICompareContainer container = getContainer();
IWorkbenchPart part = container.getWorkbenchPart();
if (part != null) {
ISaveablesLifecycleListener lifecycleListener = getSaveablesLifecycleListener(part);
// Remove this saveable from the lifecycle listener
if (!init)
lifecycleListener
.handleLifecycleEvent(new SaveablesLifecycleEvent(
part, SaveablesLifecycleEvent.POST_CLOSE,
new Saveable[] { this }, false));
// Now fix the hashing so it uses the connected document
initializeHashing();
// Finally, add this saveable back to the listener
lifecycleListener
.handleLifecycleEvent(new SaveablesLifecycleEvent(part,
SaveablesLifecycleEvent.POST_OPEN,
new Saveable[] { this }, false));
}
}
@Override
public void handleDocumentDeleted() {
// Ignore
}
@Override
public void handleDocumentDisconnected() {
// Ignore
}
@Override
public void handleDocumentFlushed() {
// Ignore
}
@Override
public void handleDocumentSaved() {
// Ignore
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (!(obj instanceof Saveable))
return false;
Object document = getAdapter(IDocument.class);
if (document != null) {
Object otherDocument = ((Saveable) obj)
.getAdapter(IDocument.class);
return document.equals(otherDocument);
}
if (obj instanceof InternalResourceSaveableComparison) {
InternalResourceSaveableComparison rscm = (InternalResourceSaveableComparison) obj;
return rscm.getInput().equals(getInput()) && rscm.lrte.equals(lrte);
}
return false;
}
}
public static void handleMenuAboutToShow(IMenuManager manager, ICompareContainer container, Saveable saveable, ITypedElement element, ISelectionProvider provider) {
if (provider instanceof ITextViewer) {
final ITextViewer v= (ITextViewer)provider;
IDocument d= v.getDocument();
IDocument other= Adapters.adapt(saveable, IDocument.class);
if (d == other) {
if (element instanceof IResourceProvider) {
IResourceProvider rp= (IResourceProvider)element;
IResource resource= rp.getResource();
StructuredSelection selection= new StructuredSelection(resource);
IWorkbenchPart workbenchPart= container.getWorkbenchPart();
if (workbenchPart != null) {
final IWorkbenchSite ws= workbenchPart.getSite();
MenuManager submenu1= new MenuManager(getShowInMenuLabel());
IContributionItem showInMenu= ContributionItemFactory.VIEWS_SHOW_IN.create(ws.getWorkbenchWindow());
submenu1.add(showInMenu);
manager.insertAfter("file", submenu1); //$NON-NLS-1$
MenuManager submenu2= new MenuManager(TeamUIMessages.OpenWithActionGroup_0);
// XXX: Internal reference will get fixed during 3.7, see https://bugs.eclipse.org/bugs/show_bug.cgi?id=307026
submenu2.add(new OpenWithMenu(ws.getPage(), resource) {
@Override
protected void openEditor(IEditorDescriptor editorDescriptor, boolean openUsingDescriptor) {
super.openEditor(editorDescriptor, openUsingDescriptor);
IEditorPart editor= ws.getPage().getActiveEditor();
Point selectedRange= v.getSelectedRange();
revealInEditor(editor, selectedRange.x, selectedRange.y);
}
});
manager.insertAfter("file", submenu2); //$NON-NLS-1$
// XXX: Internal reference will get fixed during 3.7, see https://bugs.eclipse.org/bugs/show_bug.cgi?id=307026
OpenFileAction openFileAction= new OpenFileAction(ws.getPage()) {
@Override
public void run() {
super.run();
IEditorPart editor= ws.getPage().getActiveEditor();
Point selectedRange= v.getSelectedRange();
revealInEditor(editor, selectedRange.x, selectedRange.y);
}
};
openFileAction.selectionChanged(selection);
manager.insertAfter("file", openFileAction); //$NON-NLS-1$
}
}
}
}
}
/**
* 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.6
*/
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 ? (IGotoMarker)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;
}
}
}