/*******************************************************************************
* Copyright (c) 2011, 2015 Wind River Systems, Inc. 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:
* Wind River Systems - initial API and implementation
* William Chen (Wind River)- [345552] Edit the remote files with a proper editor
*******************************************************************************/
package org.eclipse.tcf.te.tcf.filesystem.ui.internal.compare;
import java.lang.reflect.InvocationTargetException;
import org.eclipse.compare.CompareConfiguration;
import org.eclipse.compare.CompareEditorInput;
import org.eclipse.compare.IPropertyChangeNotifier;
import org.eclipse.compare.structuremergeviewer.Differencer;
import org.eclipse.compare.structuremergeviewer.ICompareInput;
import org.eclipse.compare.structuremergeviewer.ICompareInputChangeListener;
import org.eclipse.core.runtime.Assert;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.ListenerList;
import org.eclipse.core.runtime.SafeRunner;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.jface.util.SafeRunnable;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.osgi.util.NLS;
import org.eclipse.osgi.util.TextProcessor;
import org.eclipse.swt.events.DisposeEvent;
import org.eclipse.swt.events.DisposeListener;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.tcf.te.tcf.filesystem.ui.activator.UIPlugin;
import org.eclipse.tcf.te.tcf.filesystem.ui.internal.ImageConsts;
import org.eclipse.tcf.te.tcf.filesystem.ui.nls.Messages;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.IPropertyListener;
import org.eclipse.ui.ISaveablesSource;
import org.eclipse.ui.IWorkbenchPage;
import org.eclipse.ui.IWorkbenchPartConstants;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.Saveable;
/**
* A <code>MergeEditorInput</code> is the input of a compare editor used to
* compare the local file and the remote file in a Target Explorer file system.
*/
public class MergeEditorInput extends CompareEditorInput implements
ISaveablesSource, IPropertyListener, ICompareInputChangeListener {
// The left element is a local file which can be editable.
private LocalTypedElement left;
// The right element is the remote file which is read only.
private RemoteTypedElement right;
// The active page of the current workbench.
private final IWorkbenchPage page;
// The current input change listener list.
private final ListenerList inputChangeListeners = new ListenerList(ListenerList.IDENTITY);
// The current saveable for the left element, i.e., the local file.
private LocalFileSaveable saveable;
/**
* Creates a new MergeEditorInput.
*
* @param left
* @param right
* @param page
*/
public MergeEditorInput(LocalTypedElement left, RemoteTypedElement right, IWorkbenchPage page) {
super(new CompareConfiguration());
this.page = page;
this.left = left;
this.right = right;
configureCompare();
}
/**
* Configure the compare configuration using the left
* and right elements. Set the left and right label.
* Set the editable status.
*/
protected void configureCompare() {
CompareConfiguration cc = getCompareConfiguration();
cc.setLeftEditable(true);
cc.setRightEditable(false);
String name = TextProcessor.process(left.getName());
String label = NLS.bind(Messages.MergeEditorInput_LocalFile, name);
cc.setLeftLabel(label);
name = TextProcessor.process(right.toString());
label = NLS.bind(Messages.MergeEditorInput_RemoteFile, name);
cc.setRightLabel(label);
}
/**
* Returns <code>true</code> if the other object is of simulator
* <code>MergeEditorInput</code> and both of their corresponding fLeft and
* fRight objects are identical. The content is not considered.
*/
@Override
public boolean equals(Object obj) {
if (obj == this)
return true;
if (obj instanceof MergeEditorInput) {
MergeEditorInput other = (MergeEditorInput) obj;
return other.left.equals(left) && other.right.equals(right);
}
return false;
}
/**
* Override hashCode to provide identical value when
* two MergeEditorInputs are equal to each other.
*/
@Override
public int hashCode() {
return left.hashCode() + right.hashCode();
}
/**
* 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 {
right.cacheContents(monitor);
MergeInput input = new MergeInput(left, right);
setTitle(input.getName());
return input;
}
/* (non-Javadoc)
* @see org.eclipse.compare.CompareEditorInput#getToolTipText()
*/
@Override
public String getToolTipText() {
return NLS.bind(Messages.MergeEditorInput_CompareLeftAndRight, left, right);
}
/* (non-Javadoc)
* @see org.eclipse.compare.CompareEditorInput#getTitle()
*/
@Override
public String getTitle() {
return NLS.bind(Messages.MergeEditorInput_CompareWithLocalCache, left.getName());
}
/**
* Override the super method to provide clear typed input.
*/
@Override
public MergeInput getCompareResult() {
return (MergeInput) super.getCompareResult();
}
/* (non-Javadoc)
* @see org.eclipse.compare.CompareEditorInput#handleDispose()
*/
@Override
protected void handleDispose() {
super.handleDispose();
ICompareInput compareInput = getCompareResult();
if (compareInput != null)
compareInput.removeCompareInputChangeListener(this);
if(getCompareResult()!=null){
getSaveable().removePropertyListener(this);
getSaveable().dispose();
}
left.discardBuffer();
}
/* (non-Javadoc)
* @see org.eclipse.compare.CompareEditorInput#contentsCreated()
*/
@Override
protected void contentsCreated() {
super.contentsCreated();
if (getCompareResult() != null) {
getCompareResult().addCompareInputChangeListener(this);
getSaveable().addPropertyListener(this);
setDirty(getSaveable().isDirty());
}
}
/* (non-Javadoc)
* @see org.eclipse.ui.IPropertyListener#propertyChanged(java.lang.Object, int)
*/
@Override
public void propertyChanged(Object source, int propId) {
if (propId == IWorkbenchPartConstants.PROP_DIRTY && getCompareResult()!=null) {
setDirty(getSaveable().isDirty());
}
}
/**
* 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;
}
final IWorkbenchPage page = getPage();
if (page == null)
return false;
Runnable runnable = new Runnable() {
@Override
public void run() {
Shell shell = page.getWorkbenchWindow().getShell();
if (shell == null)
return;
IEditorPart part = page.findEditor(MergeEditorInput.this);
getPage().closeEditor(part, false);
}
};
if (Display.getCurrent() != null) {
runnable.run();
} else {
Shell shell = page.getWorkbenchWindow().getShell();
if (shell == null)
return false;
Display display = shell.getDisplay();
display.asyncExec(runnable);
}
return true;
}
/**
* Get the current active page.
* @return the workbench page.
*/
IWorkbenchPage getPage() {
if (page == null)
return PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage();
return page;
}
/**
* Propagate the input change event to the compare input change listener list.
*/
/* default */void propogateInputChange() {
if (!inputChangeListeners.isEmpty()) {
Object[] allListeners = inputChangeListeners.getListeners();
for (int i = 0; i < allListeners.length; i++) {
final ICompareInputChangeListener listener = (ICompareInputChangeListener) allListeners[i];
SafeRunner.run(new SafeRunnable() {
@Override
public void handleException(Throwable e) {
// Ignore exception
}
@Override
public void run() throws Exception {
listener.compareInputChanged(getCompareResult());
}
});
}
}
}
/**
* Get the fSaveable that provides the save behavior for this compare editor
* input. The {@link #getSaveable()} is called to create the fSaveable if
* it does not yet exist. This method cannot be called until after the input
* is prepared (i.e. until after the {@link #run(IProgressMonitor)} method
* is called which will in turn will invoke
* {@link #prepareInput(IProgressMonitor)}.
*
* @return fSaveable that provides the save behavior for this compare editor
* input.
*/
public LocalFileSaveable getSaveable() {
if (saveable == null) {
Assert.isNotNull(getCompareResult());
saveable = new LocalFileSaveable(getCompareResult(), this, left);
}
return saveable;
}
/* (non-Javadoc)
* @see org.eclipse.ui.ISaveablesSource#getActiveSaveables()
*/
@Override
public Saveable[] getActiveSaveables() {
if (getCompareResult() == null)
return new Saveable[0];
return new Saveable[] { getSaveable() };
}
/* (non-Javadoc)
* @see org.eclipse.ui.ISaveablesSource#getSaveables()
*/
@Override
public Saveable[] getSaveables() {
return getActiveSaveables();
}
/* (non-Javadoc)
* @see org.eclipse.compare.CompareEditorInput#addCompareInputChangeListener(org.eclipse.compare.structuremergeviewer.ICompareInput, org.eclipse.compare.structuremergeviewer.ICompareInputChangeListener)
*/
@Override
public void addCompareInputChangeListener(ICompareInput input,
ICompareInputChangeListener listener) {
if (input == getCompareResult()) {
inputChangeListeners.add(listener);
} else {
super.addCompareInputChangeListener(input, listener);
}
}
/* (non-Javadoc)
* @see org.eclipse.compare.CompareEditorInput#removeCompareInputChangeListener(org.eclipse.compare.structuremergeviewer.ICompareInput, org.eclipse.compare.structuremergeviewer.ICompareInputChangeListener)
*/
@Override
public void removeCompareInputChangeListener(ICompareInput input,
ICompareInputChangeListener listener) {
if (input == getCompareResult()) {
inputChangeListeners.remove(listener);
} else {
super.removeCompareInputChangeListener(input, listener);
}
}
/* (non-Javadoc)
* @see org.eclipse.compare.CompareEditorInput#getTitleImage()
*/
@Override
public Image getTitleImage() {
return UIPlugin.getImage(ImageConsts.COMPARE_EDITOR);
}
/* (non-Javadoc)
* @see org.eclipse.compare.CompareEditorInput#getImageDescriptor()
*/
@Override
public ImageDescriptor getImageDescriptor() {
return UIPlugin.getImageDescriptor(ImageConsts.COMPARE_EDITOR);
}
/* (non-Javadoc)
* @see org.eclipse.compare.CompareEditorInput#findContentViewer(org.eclipse.jface.viewers.Viewer, org.eclipse.compare.structuremergeviewer.ICompareInput, org.eclipse.swt.widgets.Composite)
*/
@Override
public Viewer findContentViewer(Viewer oldViewer, ICompareInput input,
Composite parent) {
Viewer newViewer = super.findContentViewer(oldViewer, input, parent);
boolean isNewViewer = newViewer != oldViewer;
if (isNewViewer && newViewer instanceof IPropertyChangeNotifier && getCompareResult()!=null) {
// Register the model for change events if appropriate
final IPropertyChangeNotifier dsp = (IPropertyChangeNotifier) newViewer;
dsp.addPropertyChangeListener(getSaveable());
Control c = newViewer.getControl();
c.addDisposeListener(new DisposeListener() {
@Override
public void widgetDisposed(DisposeEvent e) {
dsp.removePropertyChangeListener(getSaveable());
}
});
}
return newViewer;
}
/* (non-Javadoc)
* @see org.eclipse.compare.CompareEditorInput#canRunAsJob()
*/
@Override
public boolean canRunAsJob() {
return true;
}
/* (non-Javadoc)
* @see org.eclipse.compare.CompareEditorInput#isDirty()
*/
@Override
public boolean isDirty() {
if (saveable != null)
return saveable.isDirty();
return super.isDirty();
}
/* (non-Javadoc)
* @see org.eclipse.compare.structuremergeviewer.ICompareInputChangeListener#compareInputChanged(org.eclipse.compare.structuremergeviewer.ICompareInput)
*/
@Override
public void compareInputChanged(ICompareInput source) {
if (source == getCompareResult()) {
boolean closed = false;
if (source.getKind() == Differencer.NO_CHANGE) {
closed = closeEditor(true);
}
if (!closed) {
// The editor was 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();
}
}
}
}