| /******************************************************************************* |
| * Copyright (c) 2000, 2013 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.compare; |
| |
| import java.lang.reflect.InvocationTargetException; |
| import java.util.ResourceBundle; |
| |
| import org.eclipse.compare.contentmergeviewer.ContentMergeViewer; |
| import org.eclipse.compare.contentmergeviewer.IFlushable; |
| import org.eclipse.compare.internal.BinaryCompareViewer; |
| import org.eclipse.compare.internal.ChangePropertyAction; |
| import org.eclipse.compare.internal.CompareContentViewerSwitchingPane; |
| import org.eclipse.compare.internal.CompareEditorInputNavigator; |
| import org.eclipse.compare.internal.CompareMessages; |
| import org.eclipse.compare.internal.ComparePreferencePage; |
| import org.eclipse.compare.internal.CompareStructureViewerSwitchingPane; |
| import org.eclipse.compare.internal.CompareUIPlugin; |
| import org.eclipse.compare.internal.ICompareUIConstants; |
| import org.eclipse.compare.internal.IFlushable2; |
| import org.eclipse.compare.internal.OutlineViewerCreator; |
| import org.eclipse.compare.internal.Utilities; |
| import org.eclipse.compare.internal.ViewerDescriptor; |
| import org.eclipse.compare.structuremergeviewer.DiffTreeViewer; |
| import org.eclipse.compare.structuremergeviewer.ICompareInput; |
| import org.eclipse.compare.structuremergeviewer.ICompareInputChangeListener; |
| import org.eclipse.compare.structuremergeviewer.IDiffContainer; |
| import org.eclipse.core.resources.IFile; |
| 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.IStatus; |
| import org.eclipse.core.runtime.ListenerList; |
| import org.eclipse.core.runtime.OperationCanceledException; |
| import org.eclipse.core.runtime.PlatformObject; |
| import org.eclipse.core.runtime.Status; |
| import org.eclipse.core.runtime.jobs.Job; |
| import org.eclipse.jface.action.IStatusLineManager; |
| import org.eclipse.jface.action.MenuManager; |
| import org.eclipse.jface.action.Separator; |
| import org.eclipse.jface.action.ToolBarManager; |
| import org.eclipse.jface.dialogs.ErrorDialog; |
| import org.eclipse.jface.dialogs.IDialogConstants; |
| import org.eclipse.jface.operation.IRunnableWithProgress; |
| import org.eclipse.jface.preference.IPreferenceStore; |
| import org.eclipse.jface.resource.ImageDescriptor; |
| import org.eclipse.jface.text.IFindReplaceTarget; |
| import org.eclipse.jface.util.IPropertyChangeListener; |
| import org.eclipse.jface.util.PropertyChangeEvent; |
| import org.eclipse.jface.viewers.DoubleClickEvent; |
| import org.eclipse.jface.viewers.IDoubleClickListener; |
| import org.eclipse.jface.viewers.IOpenListener; |
| import org.eclipse.jface.viewers.ISelection; |
| import org.eclipse.jface.viewers.ISelectionChangedListener; |
| import org.eclipse.jface.viewers.ISelectionProvider; |
| import org.eclipse.jface.viewers.IStructuredSelection; |
| import org.eclipse.jface.viewers.OpenEvent; |
| import org.eclipse.jface.viewers.SelectionChangedEvent; |
| import org.eclipse.jface.viewers.StructuredSelection; |
| import org.eclipse.jface.viewers.Viewer; |
| import org.eclipse.osgi.util.NLS; |
| import org.eclipse.swt.SWT; |
| import org.eclipse.swt.custom.BusyIndicator; |
| 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.ui.IActionBars; |
| import org.eclipse.ui.IEditorInput; |
| import org.eclipse.ui.IPersistableElement; |
| import org.eclipse.ui.ISaveablesSource; |
| import org.eclipse.ui.IWorkbenchPart; |
| import org.eclipse.ui.PlatformUI; |
| import org.eclipse.ui.part.FileEditorInput; |
| import org.eclipse.ui.part.IShowInSource; |
| import org.eclipse.ui.part.ShowInContext; |
| import org.eclipse.ui.services.IServiceLocator; |
| import org.eclipse.ui.texteditor.ITextEditorExtension3; |
| |
| |
| /** |
| * A compare operation which can present its results in a special editor. |
| * Running the compare operation and presenting the results in a compare editor |
| * are combined in one class because it allows a client to keep the implementation |
| * all in one place while separating it from the innards of a specific UI implementation of compare/merge. |
| * <p> |
| * A <code>CompareEditorInput</code> defines methods for the following sequence steps: |
| * <UL> |
| * <LI>running a lengthy compare operation under progress monitor control, |
| * <LI>creating a UI for displaying the model and initializing the some widgets with the compare result, |
| * <LI>tracking the dirty state of the model in case of merge, |
| * <LI>saving the model. |
| * </UL> |
| * The Compare plug-in's <code>openCompareEditor</code> method takes an <code>CompareEditorInput</code> |
| * and starts sequencing through the above steps. If the compare result is not empty a new compare editor |
| * is opened and takes over the sequence until eventually closed. |
| * <p> |
| * The <code>prepareInput</code> method should contain the |
| * code of the compare operation. It is executed under control of a progress monitor |
| * and can be canceled. If the result of the compare is not empty, that is if there are differences |
| * that needs to be presented, the <code>ICompareInput</code> should hold onto them and return them with |
| * the <code>getCompareResult</code> method. |
| * If the value returned from <code>getCompareResult</code> is not <code>null</code> |
| * a compare editor is opened on the <code>ICompareInput</code> with title and title image initialized by the |
| * corresponding methods of the <code>ICompareInput</code>. |
| * <p> |
| * Creation of the editor's SWT controls is delegated to the <code>createContents</code> method. |
| * Here the SWT controls must be created and initialized with the result of the compare operation. |
| * <p> |
| * If merging is allowed, the modification state of the compared constituents must be tracked and the dirty |
| * state returned from method <code>isSaveNeeded</code>. The value <code>true</code> triggers a subsequent call |
| * to <code>save</code> where the modified resources can be saved. |
| * <p> |
| * The most important part of this implementation is the setup of the compare/merge UI. |
| * The UI uses a simple browser metaphor to present compare results. |
| * The top half of the layout shows the structural compare results (e.g. added, deleted, and changed files), |
| * the bottom half the content compare results (e.g. textual differences between two files). |
| * A selection in the top pane is fed to the bottom pane. If a content viewer is registered |
| * for the type of the selected object, this viewer is installed in the pane. |
| * In addition if a structure viewer is registered for the selection type the top pane |
| * is split vertically to make room for another pane and the structure viewer is installed |
| * in it. When comparing Java files this second structure viewer would show the structural |
| * differences within a Java file, e.g. added, deleted or changed methods and fields. |
| * <p> |
| * Subclasses provide custom setups, e.g. for a Catch-up/Release operation |
| * by passing a subclass of <code>CompareConfiguration</code> and by implementing the <code>prepareInput</code> method. |
| * If a subclass cannot use the <code>DiffTreeViewer</code> which is installed by default in the |
| * top left pane, method <code>createDiffViewer</code> can be overridden. |
| * <p> |
| * If subclasses of this class implement {@link ISaveablesSource}, the compare editor will |
| * pass these models through to the workbench. The editor will still show the dirty indicator |
| * if one of these underlying models is dirty. It is the responsibility of subclasses that |
| * implement this interface to call {@link #setDirty(boolean)} when the dirty state of |
| * any of the models managed by the subclass change dirty state. |
| * |
| * @see CompareUI |
| * @see CompareEditorInput |
| */ |
| public abstract class CompareEditorInput extends PlatformObject implements IEditorInput, IPropertyChangeNotifier, IRunnableWithProgress, ICompareContainer { |
| |
| private static final boolean DEBUG= false; |
| |
| /** |
| * The name of the "dirty" property (value <code>"DIRTY_STATE"</code>). |
| */ |
| public static final String DIRTY_STATE= "DIRTY_STATE"; //$NON-NLS-1$ |
| |
| /** |
| * The name of the "title" property. This property is fired when the title |
| * of the compare input changes. Clients should also re-obtain the tool tip |
| * when this property changes. |
| * @see #getTitle() |
| * @since 3.3 |
| */ |
| public static final String PROP_TITLE= ICompareUIConstants.PROP_TITLE; |
| |
| /** |
| * The name of the "title image" property. This property is fired when the title |
| * image of the compare input changes. |
| * @see #getTitleImage() |
| * @since 3.3 |
| */ |
| public static final String PROP_TITLE_IMAGE= ICompareUIConstants.PROP_TITLE_IMAGE; |
| |
| /** |
| * The name of the "selected edition" property. This property is fired when the selected |
| * edition of the compare input changes. |
| * @see #isEditionSelectionDialog() |
| * @see #getSelectedEdition() |
| * @since 3.3 |
| */ |
| public static final String PROP_SELECTED_EDITION= ICompareUIConstants.PROP_SELECTED_EDITION; |
| |
| private static final String COMPARE_EDITOR_IMAGE_NAME= "eview16/compare_view.png"; //$NON-NLS-1$ |
| private static Image fgTitleImage; |
| |
| private Splitter fComposite; |
| private CompareConfiguration fCompareConfiguration; |
| private CompareViewerPane fStructureInputPane; |
| private CompareViewerSwitchingPane fStructurePane1; |
| private CompareViewerSwitchingPane fStructurePane2; |
| private CompareViewerSwitchingPane fContentInputPane; |
| private CompareViewerPane fFocusPane; |
| private String fMessage; |
| private Object fInput; |
| private String fTitle; |
| private ListenerList<IPropertyChangeListener> fListenerList= new ListenerList<>(); |
| private CompareNavigator fNavigator; |
| private boolean fLeftDirty = false; |
| private boolean fRightDirty = false; |
| private IPropertyChangeListener fDirtyStateListener; |
| |
| boolean fStructureCompareOnSingleClick= true; |
| |
| private ICompareContainer fContainer; |
| private boolean fContainerProvided; |
| private String fHelpContextId; |
| private InternalOutlineViewerCreator fOutlineView; |
| private ViewerDescriptor fContentViewerDescriptor; |
| private ViewerDescriptor fStructureViewerDescriptor; |
| |
| private class InternalOutlineViewerCreator extends OutlineViewerCreator { |
| private OutlineViewerCreator getWrappedCreator() { |
| if (fContentInputPane != null) { |
| Viewer v = fContentInputPane.getViewer(); |
| if (v != null) { |
| return Adapters.adapt(v, OutlineViewerCreator.class); |
| } |
| } |
| return null; |
| } |
| |
| @Override |
| public Viewer findStructureViewer(Viewer oldViewer, |
| ICompareInput input, Composite parent, |
| CompareConfiguration configuration) { |
| OutlineViewerCreator creator = getWrappedCreator(); |
| if (creator != null) |
| return creator.findStructureViewer(oldViewer, input, parent, configuration); |
| return null; |
| } |
| |
| @Override |
| public boolean hasViewerFor(Object input) { |
| OutlineViewerCreator creator = getWrappedCreator(); |
| return creator != null; |
| } |
| |
| @Override |
| public Object getInput() { |
| OutlineViewerCreator creator = getWrappedCreator(); |
| if (creator != null) |
| return creator.getInput(); |
| return null; |
| } |
| } |
| |
| /** |
| * Creates a <code>CompareEditorInput</code> which is initialized with the given |
| * compare configuration. |
| * The compare configuration is passed to subsequently created viewers. |
| * |
| * @param configuration the compare configuration |
| */ |
| public CompareEditorInput(CompareConfiguration configuration) { |
| fCompareConfiguration= configuration; |
| Assert.isNotNull(configuration); |
| |
| fDirtyStateListener= new IPropertyChangeListener() { |
| @Override |
| public void propertyChange(PropertyChangeEvent e) { |
| String propertyName= e.getProperty(); |
| if (CompareEditorInput.DIRTY_STATE.equals(propertyName)) { |
| boolean changed= false; |
| Object newValue= e.getNewValue(); |
| if (newValue instanceof Boolean) |
| changed= ((Boolean)newValue).booleanValue(); |
| setDirty(e.getSource(), changed); |
| } |
| } |
| }; |
| |
| IPreferenceStore ps= configuration.getPreferenceStore(); |
| if (ps != null) |
| fStructureCompareOnSingleClick= ps.getBoolean(ComparePreferencePage.OPEN_STRUCTURE_COMPARE); |
| |
| fContainer = configuration.getContainer(); |
| configuration.setContainer(this); |
| } |
| |
| private boolean structureCompareOnSingleClick() { |
| return fStructureCompareOnSingleClick; |
| } |
| |
| private boolean isShowStructureInOutlineView() { |
| Object object= getCompareConfiguration().getProperty(CompareConfiguration.USE_OUTLINE_VIEW); |
| return object instanceof Boolean && ((Boolean)object).booleanValue(); |
| } |
| |
| @Override |
| @SuppressWarnings("unchecked") |
| public <T> T getAdapter(Class<T> adapter) { |
| if (ICompareNavigator.class.equals(adapter) || CompareNavigator.class.equals(adapter)) { |
| return (T) getNavigator(); |
| } |
| if (adapter == IShowInSource.class) { |
| final IFile file = Adapters.adapt(this, IFile.class); |
| if (file != null) |
| return (T) new IShowInSource() { |
| @Override |
| public ShowInContext getShowInContext() { |
| return new ShowInContext(new FileEditorInput(file), StructuredSelection.EMPTY); |
| } |
| }; |
| } |
| if (adapter == OutlineViewerCreator.class) { |
| synchronized (this) { |
| if (fOutlineView == null) |
| fOutlineView = new InternalOutlineViewerCreator(); |
| return (T) fOutlineView; |
| } |
| } |
| if (adapter == IFindReplaceTarget.class) { |
| if (fContentInputPane != null) { |
| Viewer v = fContentInputPane.getViewer(); |
| if (v != null) { |
| return (T) Adapters.adapt(v, IFindReplaceTarget.class); |
| } |
| } |
| } |
| if (adapter == IEditorInput.class) { |
| if (fContentInputPane != null) { |
| Viewer v = fContentInputPane.getViewer(); |
| if (v != null) { |
| return (T) Adapters.adapt(v, IEditorInput.class); |
| } |
| } |
| } |
| |
| if (adapter == ITextEditorExtension3.class) { |
| if (fContentInputPane != null) { |
| Viewer v = fContentInputPane.getViewer(); |
| if (v != null) { |
| return (T) Adapters.adapt(v, ITextEditorExtension3.class); |
| } |
| } |
| } |
| |
| return super.getAdapter(adapter); |
| } |
| |
| @Override |
| public synchronized ICompareNavigator getNavigator() { |
| if (fNavigator == null) |
| fNavigator= new CompareEditorInputNavigator( |
| new Object[] { |
| fStructureInputPane, |
| fStructurePane1, |
| fStructurePane2, |
| fContentInputPane |
| } |
| ); |
| return fNavigator; |
| } |
| |
| @Override |
| public ImageDescriptor getImageDescriptor() { |
| return null; |
| } |
| |
| @Override |
| public String getToolTipText() { |
| return getTitle(); |
| } |
| |
| @Override |
| public String getName() { |
| return getTitle(); |
| } |
| |
| /** |
| * Returns <code>null</code> since this editor cannot be persisted. |
| * |
| * @return <code>null</code> because this editor cannot be persisted |
| */ |
| @Override |
| public IPersistableElement getPersistable() { |
| return null; |
| } |
| |
| /** |
| * Returns <code>false</code> to indicate that this input |
| * should not appear in the "File Most Recently Used" menu. |
| * |
| * @return <code>false</code> |
| */ |
| @Override |
| public boolean exists() { |
| return false; |
| } |
| |
| /* |
| * FIXME! |
| */ |
| protected void setMessage(String message) { |
| fMessage= message; |
| } |
| |
| /* |
| * FIXME! |
| */ |
| public String getMessage() { |
| return fMessage; |
| } |
| |
| /** |
| * Returns the title which will be used in the compare editor's title bar. |
| * It can be set with <code>setTitle</code>. |
| * |
| * @return the title |
| */ |
| public String getTitle() { |
| if (fTitle == null) |
| return Utilities.getString("CompareEditorInput.defaultTitle"); //$NON-NLS-1$ |
| return fTitle; |
| } |
| |
| /** |
| * Sets the title which will be used when presenting the compare result. |
| * This method must be called before the editor is opened. |
| * |
| * @param title the title to use for the CompareEditor |
| */ |
| public void setTitle(String title) { |
| String oldTitle = fTitle; |
| fTitle= title; |
| Utilities.firePropertyChange(fListenerList, this, PROP_TITLE, oldTitle, title); |
| } |
| |
| /** |
| * Returns the title image which will be used in the compare editor's title bar. |
| * Returns the title image which will be used when presenting the compare result. |
| * This implementation returns a generic compare icon. |
| * Subclasses can override. |
| * |
| * @return the title image, or <code>null</code> if none |
| */ |
| public Image getTitleImage() { |
| if (fgTitleImage == null) { |
| fgTitleImage= CompareUIPlugin.getImageDescriptor(COMPARE_EDITOR_IMAGE_NAME).createImage(); |
| CompareUI.disposeOnShutdown(fgTitleImage); |
| } |
| return fgTitleImage; |
| } |
| |
| /** |
| * Returns the configuration object for the viewers within the compare editor. |
| * Returns the configuration which was passed to the constructor. |
| * |
| * @return the compare configuration |
| */ |
| public CompareConfiguration getCompareConfiguration() { |
| return fCompareConfiguration; |
| } |
| |
| /** |
| * Adds standard actions to the given <code>ToolBarManager</code>. |
| * <p> |
| * Subclasses may override to add their own actions. |
| * </p> |
| * |
| * @param toolBarManager the <code>ToolBarManager</code> to which to contribute |
| */ |
| public void contributeToToolBar(ToolBarManager toolBarManager) { |
| ResourceBundle bundle= CompareUI.getResourceBundle(); |
| ChangePropertyAction ignoreWhitespace= ChangePropertyAction.createIgnoreWhiteSpaceAction(bundle, getCompareConfiguration()); |
| toolBarManager.getControl().addDisposeListener(ignoreWhitespace); |
| ChangePropertyAction showPseudoConflicts= ChangePropertyAction.createShowPseudoConflictsAction(bundle, getCompareConfiguration()); |
| toolBarManager.getControl().addDisposeListener(showPseudoConflicts); |
| toolBarManager.add(new Separator()); |
| toolBarManager.add(ignoreWhitespace); |
| toolBarManager.add(showPseudoConflicts); |
| } |
| |
| /** |
| * Runs the compare operation and stores the compare result. |
| * |
| * @param monitor the progress monitor to use to display progress and receive |
| * requests for cancellation |
| * @throws InvocationTargetException if the <code>prepareInput</code> method must propagate a checked exception, |
| * it should wrap it inside an <code>InvocationTargetException</code>; runtime exceptions are automatically |
| * wrapped in an <code>InvocationTargetException</code> by the calling context |
| * @throws InterruptedException if the operation detects a request to cancel, |
| * using <code>IProgressMonitor.isCanceled()</code>, it should exit by throwing |
| * <code>InterruptedException</code> |
| */ |
| @Override |
| public void run(IProgressMonitor monitor) throws InterruptedException, InvocationTargetException { |
| fInput= prepareInput(monitor); |
| } |
| |
| /** |
| * Runs the compare operation and returns the compare result. |
| * If <code>null</code> is returned no differences were found and no compare editor needs to be opened. |
| * Progress should be reported to the given progress monitor. |
| * A request to cancel the operation should be honored and acknowledged |
| * by throwing <code>InterruptedException</code>. |
| * <p> |
| * Note: this method is typically called in a modal context thread which doesn't have a Display assigned. |
| * Implementors of this method shouldn't therefore allocated any SWT resources in this method. |
| * </p> |
| * |
| * @param monitor the progress monitor to use to display progress and receive |
| * requests for cancellation |
| * @return the result of the compare operation, or <code>null</code> if there are no differences |
| * @exception InvocationTargetException if the <code>prepareInput</code> method must propagate a checked exception, |
| * it should wrap it inside an <code>InvocationTargetException</code>; runtime exceptions are automatically |
| * wrapped in an <code>InvocationTargetException</code> by the calling context |
| * @exception InterruptedException if the operation detects a request to cancel, |
| * using <code>IProgressMonitor.isCanceled()</code>, it should exit by throwing |
| * <code>InterruptedException</code> |
| */ |
| protected abstract Object prepareInput(IProgressMonitor monitor) |
| throws InvocationTargetException, InterruptedException; |
| |
| /** |
| * Returns the compare result computed by the most recent call to the |
| * <code>run</code> method. Returns <code>null</code> if no |
| * differences were found. |
| * |
| * @return the compare result prepared in method <code>prepareInput</code> |
| * or <code>null</code> if there were no differences |
| */ |
| public Object getCompareResult() { |
| return fInput; |
| } |
| |
| /** |
| * Create the SWT controls that are used to display the result of the compare operation. |
| * Creates the SWT Controls and sets up the wiring between the individual panes. |
| * This implementation creates all four panes but makes only the necessary ones visible. |
| * Finally it feeds the compare result into the top left structure viewer |
| * and the content viewer. |
| * <p> |
| * Subclasses may override if they need to change the layout or wiring between panes. |
| * |
| * @param parent the parent control under which the control must be created |
| * @return the SWT control hierarchy for the compare editor |
| */ |
| public Control createContents(Composite parent) { |
| fComposite= new Splitter(parent, SWT.VERTICAL); |
| fComposite.setData(this); |
| |
| Control outline= createOutlineContents(fComposite, SWT.HORIZONTAL); |
| |
| fContentInputPane= createContentViewerSwitchingPane(fComposite, SWT.BORDER | SWT.FLAT, this); |
| |
| if (fFocusPane == null) |
| fFocusPane= fContentInputPane; |
| if (outline != null) |
| fComposite.setVisible(outline, false); |
| fComposite.setVisible(fContentInputPane, true); |
| |
| if (fStructureInputPane != null && fComposite.getChildren().length == 2) |
| fComposite.setWeights(new int[] { 30, 70 }); |
| |
| fComposite.layout(); |
| |
| feedInput(); |
| |
| fComposite.addDisposeListener(new DisposeListener() { |
| @Override |
| public void widgetDisposed(DisposeEvent e) { |
| /* |
| * When the UI associated with this compare editor input is |
| * disposed each composite being part of the UI releases its |
| * children first. A dispose listener is added to the last |
| * widget found in that structure. Therefore, compare editor |
| * input is disposed at the end making it possible to refer |
| * during widgets disposal. |
| */ |
| Composite composite = (Composite)e.widget; |
| Control control = composite; |
| while (composite.getChildren().length > 0) { |
| control = composite.getChildren()[composite.getChildren().length - 1]; |
| if (control instanceof Composite) { |
| composite = (Composite) control; |
| } else { |
| break; |
| } |
| } |
| control.addDisposeListener(new DisposeListener() { |
| @Override |
| public void widgetDisposed(DisposeEvent ev) { |
| handleDispose(); |
| } |
| }); |
| } |
| }); |
| if (fHelpContextId != null) |
| PlatformUI.getWorkbench().getHelpSystem().setHelp(fComposite, fHelpContextId); |
| contentsCreated(); |
| return fComposite; |
| } |
| |
| /** |
| * @param parent the parent control under which the control must be created |
| * @param style the style of widget to construct |
| * @param cei the compare editor input for the viewer |
| * @return the pane displaying content changes |
| * @nooverride This method is not intended to be re-implemented or extended by clients. |
| * @noreference This method is not intended to be referenced by clients. |
| */ |
| protected CompareViewerSwitchingPane createContentViewerSwitchingPane(Splitter parent, int style, CompareEditorInput cei) { |
| return new CompareContentViewerSwitchingPane(parent, style, cei); |
| } |
| |
| /** |
| * Callback that occurs when the UI associated with this compare editor |
| * input is disposed. This method will only be invoked if the UI has been |
| * created (i.e. after the call to {@link #createContents(Composite)}. |
| * Subclasses can extend this method but ensure that the overridden method |
| * is invoked. |
| * |
| * @since 3.3 |
| */ |
| protected void handleDispose() { |
| fContainerProvided = false; |
| fContainer = null; |
| fComposite = null; |
| fStructureInputPane = null; |
| fStructurePane1 = null; |
| fStructurePane2 = null; |
| fContentInputPane = null; |
| fFocusPane = null; |
| fNavigator = null; |
| fCompareConfiguration.dispose(); |
| } |
| |
| /** |
| * Callback that occurs after the control for the input has |
| * been created. If this method gets invoked then {@link #handleDispose()} |
| * will be invoked when the control is disposed. Subclasses may extend this |
| * method to register any listeners that need to be de-registered when the |
| * input is disposed. |
| * @since 3.3 |
| */ |
| protected void contentsCreated() { |
| // Default is to do nothing |
| } |
| |
| /** |
| * @param parent the parent control under which the control must be created |
| * @param direction the layout direction of the contents, either </code>SWT.HORIZONTAL<code> or </code>SWT.VERTICAL<code> |
| * @return the SWT control hierarchy for the outline part of the compare editor |
| * @since 3.0 |
| */ |
| public Control createOutlineContents(Composite parent, int direction) { |
| final Splitter h= new Splitter(parent, direction); |
| |
| fStructureInputPane= createStructureInputPane(h); |
| if (hasChildren(getCompareResult())) |
| fFocusPane= fStructureInputPane; |
| |
| fStructurePane1= new CompareStructureViewerSwitchingPane(h, SWT.BORDER | SWT.FLAT, true, this); |
| h.setVisible(fStructurePane1, false); |
| |
| fStructurePane2= new CompareStructureViewerSwitchingPane(h, SWT.BORDER | SWT.FLAT, true, this); |
| h.setVisible(fStructurePane2, false); |
| |
| // setup the wiring for top left pane |
| fStructureInputPane.addOpenListener( |
| new IOpenListener() { |
| @Override |
| public void open(OpenEvent oe) { |
| feed1(oe.getSelection()); |
| } |
| } |
| ); |
| fStructureInputPane.addSelectionChangedListener( |
| new ISelectionChangedListener() { |
| @Override |
| public void selectionChanged(SelectionChangedEvent e) { |
| ISelection s= e.getSelection(); |
| if (s == null || s.isEmpty()) |
| feed1(s); |
| if (isEditionSelectionDialog()) |
| firePropertyChange(new PropertyChangeEvent(this, PROP_SELECTED_EDITION, null, getSelectedEdition())); |
| } |
| } |
| ); |
| fStructureInputPane.addDoubleClickListener( |
| new IDoubleClickListener() { |
| @Override |
| public void doubleClick(DoubleClickEvent event) { |
| feedDefault1(event.getSelection()); |
| } |
| } |
| ); |
| |
| fStructurePane1.addSelectionChangedListener( |
| new ISelectionChangedListener() { |
| @Override |
| public void selectionChanged(SelectionChangedEvent e) { |
| feed2(e.getSelection()); |
| } |
| } |
| ); |
| |
| fStructurePane2.addSelectionChangedListener( |
| new ISelectionChangedListener() { |
| @Override |
| public void selectionChanged(SelectionChangedEvent e) { |
| feed3(e.getSelection()); |
| } |
| } |
| ); |
| |
| return h; |
| } |
| |
| /** |
| * Create the pane that will contain the structure input pane (upper left). |
| * By default, a {@link CompareViewerSwitchingPane} is returned. Subclasses |
| * may override to provide an alternate pane. |
| * @param parent the parent composite |
| * @return the structure input pane |
| * @since 3.3 |
| */ |
| protected CompareViewerPane createStructureInputPane(final Composite parent) { |
| return new CompareStructureViewerSwitchingPane(parent, SWT.BORDER | SWT.FLAT, true, this) { |
| @Override |
| protected Viewer getViewer(Viewer oldViewer, Object input) { |
| if (CompareEditorInput.this.hasChildren(input)) { |
| return createDiffViewer(this); |
| } |
| return super.getViewer(oldViewer, input); |
| } |
| }; |
| } |
| |
| boolean hasChildren(Object input) { |
| if (input instanceof IDiffContainer) { |
| IDiffContainer dn= (IDiffContainer) input; |
| return dn.hasChildren(); |
| } |
| return false; |
| } |
| |
| private void feedInput() { |
| if (fStructureInputPane != null |
| && (fInput instanceof ICompareInput |
| || isCustomStructureInputPane())) { |
| if (hasChildren(fInput) || isCustomStructureInputPane()) { |
| // The input has multiple entries so set the input of the structure input pane |
| fStructureInputPane.setInput(fInput); |
| } else if (!structureCompareOnSingleClick() || isShowStructureInOutlineView()) { |
| // We want to avoid showing the structure in the editor if we can so first |
| // we'll set the content pane to see if we need to provide a structure |
| internalSetContentPaneInput(fInput); |
| // If the content viewer is unusable |
| if (hasUnusableContentViewer() |
| || (structureCompareOnSingleClick() |
| && isShowStructureInOutlineView() |
| && !hasOutlineViewer(fInput))) { |
| fStructureInputPane.setInput(fInput); |
| } |
| } else { |
| fStructureInputPane.setInput(fInput); |
| } |
| ISelection sel= fStructureInputPane.getSelection(); |
| if (sel == null || sel.isEmpty()) |
| feed1(sel); // we only feed downstream viewers if the top left pane is empty |
| } |
| } |
| |
| private boolean hasOutlineViewer(Object input) { |
| if (!isShowStructureInOutlineView()) |
| return false; |
| OutlineViewerCreator creator = getAdapter(OutlineViewerCreator.class); |
| if (creator != null) |
| return creator.hasViewerFor(input); |
| return false; |
| } |
| |
| private boolean hasUnusableContentViewer() { |
| return fContentInputPane.isEmpty() || fContentInputPane.getViewer() instanceof BinaryCompareViewer; |
| } |
| |
| private boolean isCustomStructureInputPane() { |
| return !(fStructureInputPane instanceof CompareViewerSwitchingPane); |
| } |
| |
| private void feed1(final ISelection selection) { |
| BusyIndicator.showWhile(fComposite.getDisplay(), |
| new Runnable() { |
| @Override |
| public void run() { |
| if (selection == null || selection.isEmpty()) { |
| Object input= fStructureInputPane.getInput(); |
| if (input != null) |
| internalSetContentPaneInput(input); |
| if (!Utilities.okToUse(fStructurePane1) || !Utilities.okToUse(fStructurePane2)) |
| return; |
| fStructurePane2.setInput(null); // clear downstream pane |
| fStructurePane1.setInput(null); |
| } else { |
| Object input= getElement(selection); |
| internalSetContentPaneInput(input); |
| if (!Utilities.okToUse(fStructurePane1) || !Utilities.okToUse(fStructurePane2)) |
| return; |
| if (structureCompareOnSingleClick() || hasUnusableContentViewer()) |
| fStructurePane1.setInput(input); |
| fStructurePane2.setInput(null); // clear downstream pane |
| if (fStructurePane1.getInput() != input) |
| fStructurePane1.setInput(null); |
| } |
| } |
| } |
| ); |
| } |
| |
| private void feedDefault1(final ISelection selection) { |
| BusyIndicator.showWhile(fComposite.getDisplay(), |
| new Runnable() { |
| @Override |
| public void run() { |
| if (!selection.isEmpty()) |
| fStructurePane1.setInput(getElement(selection)); |
| } |
| } |
| ); |
| } |
| |
| private void feed2(final ISelection selection) { |
| BusyIndicator.showWhile(fComposite.getDisplay(), |
| new Runnable() { |
| @Override |
| public void run() { |
| if (selection.isEmpty()) { |
| Object input= fStructurePane1.getInput(); |
| internalSetContentPaneInput(input); |
| fStructurePane2.setInput(null); |
| } else { |
| Object input= getElement(selection); |
| internalSetContentPaneInput(input); |
| fStructurePane2.setInput(input); |
| } |
| } |
| } |
| ); |
| } |
| |
| private void feed3(final ISelection selection) { |
| BusyIndicator.showWhile(fComposite.getDisplay(), |
| new Runnable() { |
| @Override |
| public void run() { |
| if (selection.isEmpty()) |
| internalSetContentPaneInput(fStructurePane2.getInput()); |
| else |
| internalSetContentPaneInput(getElement(selection)); |
| } |
| } |
| ); |
| |
| } |
| |
| private void internalSetContentPaneInput(Object input) { |
| Object oldInput = fContentInputPane.getInput(); |
| fContentInputPane.setInput(input); |
| if (fOutlineView != null) |
| fOutlineView.fireInputChange(oldInput, input); |
| } |
| |
| /** |
| * Returns the first element of the given selection if the selection |
| * is a <code>IStructuredSelection</code> with exactly one element. Returns |
| * <code>null</code> otherwise. |
| * |
| * @param selection the selection |
| * @return the first element of the selection, or <code>null</code> |
| */ |
| private static Object getElement(ISelection selection) { |
| if (selection instanceof IStructuredSelection) { |
| IStructuredSelection ss= (IStructuredSelection) selection; |
| if (ss.size() == 1) |
| return ss.getFirstElement(); |
| } |
| return null; |
| } |
| |
| /** |
| * Asks this input to take focus within its container (editor). |
| * |
| * @noreference Clients should not call this method but they may override if |
| * they implement a different layout with different visual |
| * components. Clients are free to call the inherited method. |
| * |
| * @deprecated Please use {@link #setFocus2()} instead. |
| */ |
| @Deprecated |
| public void setFocus() { |
| setFocus2(); |
| } |
| |
| /** |
| * Asks this input to take focus within its container (editor). |
| * |
| * @noreference Clients should not call this method but they may override if |
| * they implement a different layout with different visual |
| * components. Clients are free to call the inherited method. |
| * |
| * @return <code>true</code> if the input got focus, and <code>false</code> |
| * if it was unable to. |
| * @since 3.5 |
| */ |
| public boolean setFocus2() { |
| if (fFocusPane != null) { |
| return fFocusPane.setFocus(); |
| } else if (fComposite != null) |
| return fComposite.setFocus(); |
| return false; |
| } |
| |
| /** |
| * Factory method for creating a differences viewer for the top left pane. |
| * It is called from <code>createContents</code> and returns a <code>DiffTreeViewer</code>. |
| * <p> |
| * Subclasses may override if they need a different viewer. |
| * </p> |
| * |
| * @param parent the SWT parent control under which to create the viewer's SWT controls |
| * @return a compare viewer for the top left pane |
| */ |
| public Viewer createDiffViewer(Composite parent) { |
| return new DiffTreeViewer(parent, fCompareConfiguration); |
| } |
| |
| /** |
| * Implements the dynamic viewer switching for structure viewers. |
| * The method must return a compare viewer based on the old (or current) viewer |
| * and a new input object. If the old viewer is suitable for showing the new input the old viewer |
| * can be returned. Otherwise a new viewer must be created under the given parent composite or |
| * <code>null</code> can be returned to indicate that no viewer could be found. |
| * <p> |
| * This implementation forwards the request to <code>CompareUI.findStructureViewer</code>. |
| * <p> |
| * Subclasses may override to implement a different strategy. |
| * </p> |
| * @param oldViewer a new viewer is only created if this old viewer cannot show the given input |
| * @param input the input object for which to find a structure viewer |
| * @param parent the SWT parent composite under which the new viewer is created |
| * @return a compare viewer which is suitable for the given input object or <code>null</code> |
| */ |
| public Viewer findStructureViewer(Viewer oldViewer, ICompareInput input, Composite parent) { |
| return fStructureViewerDescriptor != null ? fStructureViewerDescriptor.createViewer(oldViewer, parent, |
| fCompareConfiguration) : CompareUI.findStructureViewer(oldViewer, |
| input, parent, fCompareConfiguration); |
| } |
| |
| /** |
| * Implements the dynamic viewer switching for content viewers. |
| * The method must return a compare viewer based on the old (or current) viewer |
| * and a new input object. If the old viewer is suitable for showing the new input the old viewer |
| * can be returned. Otherwise a new viewer must be created under the given parent composite or |
| * <code>null</code> can be returned to indicate that no viewer could be found. |
| * <p> |
| * This implementation forwards the request to <code>CompareUI.findContentViewer</code>. |
| * <p> |
| * Subclasses may override to implement a different strategy. |
| * </p> |
| * @param oldViewer a new viewer is only created if this old viewer cannot show the given input |
| * @param input the input object for which to find a structure viewer |
| * @param parent the SWT parent composite under which the new viewer is created |
| * @return a compare viewer which is suitable for the given input object or <code>null</code> |
| */ |
| public Viewer findContentViewer(Viewer oldViewer, ICompareInput input, Composite parent) { |
| |
| Viewer newViewer = fContentViewerDescriptor != null ? fContentViewerDescriptor.createViewer(oldViewer, parent, |
| fCompareConfiguration) : CompareUI.findContentViewer(oldViewer, |
| input, parent, fCompareConfiguration); |
| |
| boolean isNewViewer= newViewer != oldViewer; |
| if (DEBUG) System.out.println("CompareEditorInput.findContentViewer: " + isNewViewer); //$NON-NLS-1$ |
| |
| if (isNewViewer && newViewer instanceof IPropertyChangeNotifier) { |
| final IPropertyChangeNotifier dsp= (IPropertyChangeNotifier) newViewer; |
| dsp.addPropertyChangeListener(fDirtyStateListener); |
| |
| Control c= newViewer.getControl(); |
| c.addDisposeListener( |
| new DisposeListener() { |
| @Override |
| public void widgetDisposed(DisposeEvent e) { |
| dsp.removePropertyChangeListener(fDirtyStateListener); |
| } |
| } |
| ); |
| } |
| |
| return newViewer; |
| } |
| |
| /** |
| * @param vd |
| * the content viewer descriptor |
| * @noreference This method is not intended to be referenced by clients. |
| * @nooverride This method is not intended to be re-implemented or extended |
| * by clients. |
| */ |
| public void setContentViewerDescriptor(ViewerDescriptor vd) { |
| this.fContentViewerDescriptor = vd; |
| } |
| |
| /** |
| * @return the content viewer descriptor set for the input |
| * @noreference This method is not intended to be referenced by clients. |
| * @nooverride This method is not intended to be re-implemented or extended |
| * by clients. |
| */ |
| public ViewerDescriptor getContentViewerDescriptor() { |
| return this.fContentViewerDescriptor; |
| } |
| |
| /** |
| * @param vd |
| * the structure viewer descriptor |
| * @noreference This method is not intended to be referenced by clients. |
| * @nooverride This method is not intended to be re-implemented or extended |
| * by clients. |
| */ |
| public void setStructureViewerDescriptor(ViewerDescriptor vd) { |
| this.fStructureViewerDescriptor = vd; |
| } |
| |
| /** |
| * @return the structure viewer descriptor set for the input |
| * @noreference This method is not intended to be referenced by clients. |
| * @nooverride This method is not intended to be re-implemented or extended |
| * by clients. |
| */ |
| public ViewerDescriptor getStructureViewerDescriptor() { |
| return this.fStructureViewerDescriptor; |
| } |
| |
| /** |
| * Returns <code>true</code> if there are unsaved changes in either left or |
| * right side. The value returned is the value of the |
| * <code>DIRTY_STATE</code> property of this input object. |
| * |
| * Returns <code>true</code> if left or right side has unsaved changes |
| * Subclasses don't have to override if the functionality provided by |
| * <code>setDirty</code> is sufficient. |
| * |
| * @return <code>true</code> if there are changes that need to be saved |
| */ |
| public boolean isSaveNeeded() { |
| return isLeftSaveNeeded() || isRightSaveNeeded(); |
| } |
| |
| /** |
| * Returns <code>true</code> if there are unsaved changes for left side. |
| * |
| * @return <code>true</code> if there are changes that need to be saved |
| * @noreference This method is not intended to be referenced by clients. |
| */ |
| protected boolean isLeftSaveNeeded() { |
| return fLeftDirty; |
| } |
| |
| /** |
| * Returns <code>true</code> if there are unsaved changes for right side. |
| * |
| * @return <code>true</code> if there are changes that need to be saved |
| * @noreference This method is not intended to be referenced by clients. |
| */ |
| protected boolean isRightSaveNeeded() { |
| return fRightDirty; |
| } |
| |
| /** |
| * Returns <code>true</code> if there are unsaved changes. |
| * The method should be called by any parts or dialogs |
| * that contain the input. |
| * By default, this method calls {@link #isSaveNeeded()} |
| * but subclasses may extend. |
| * @return <code>true</code> if there are unsaved changes |
| * @since 3.3 |
| */ |
| public boolean isDirty() { |
| return isSaveNeeded(); |
| } |
| |
| /** |
| * Sets the dirty state of this input to the given value and sends out a |
| * <code>PropertyChangeEvent</code> if the new value differs from the old |
| * value. Direct calling this method with parameter dirty equal to |
| * <code>false</code> when there are unsaved changes in viewers, results in |
| * inconsistent state. The dirty state of compare input should be based only |
| * on the information if there are changes in viewers for left or right |
| * side. |
| * |
| * @param dirty |
| * the dirty state for this compare input |
| */ |
| public void setDirty(boolean dirty) { |
| boolean oldDirty = isSaveNeeded(); |
| fLeftDirty = dirty; |
| fRightDirty = dirty; |
| |
| if (oldDirty != isSaveNeeded()) { |
| Utilities.firePropertyChange(fListenerList, this, DIRTY_STATE, Boolean.valueOf(oldDirty), Boolean.valueOf(isSaveNeeded())); |
| } |
| } |
| |
| /** |
| * Sets the dirty state of left site of this input to the given value and |
| * sends out a <code>PropertyChangeEvent</code> if the new value for whole |
| * input differs from the old value. Direct calling this method with |
| * parameter dirty equal to <code>false</code> when there are unsaved |
| * changes in left viewer, results in inconsistent state. The dirty state of |
| * compare input should be based only on the information if there are |
| * changes in viewers for left side. |
| * |
| * @param dirty |
| * the dirty state for this compare input |
| * @since 3.7 |
| * @noreference This method is not intended to be referenced by clients. |
| * @nooverride This method is not intended to be re-implemented or extended |
| * by clients. |
| */ |
| protected void setLeftDirty(boolean dirty) { |
| boolean oldDirty = isSaveNeeded(); |
| fLeftDirty = dirty; |
| |
| if (oldDirty != isSaveNeeded()) { |
| Utilities.firePropertyChange(fListenerList, this, DIRTY_STATE, |
| Boolean.valueOf(oldDirty), Boolean.valueOf(isSaveNeeded())); |
| } |
| } |
| |
| /** |
| * Sets the dirty state of right site of this input to the given value and |
| * sends out a <code>PropertyChangeEvent</code> if the new value for whole |
| * input differs from the old value. Direct calling this method with |
| * parameter dirty equal to <code>false</code> when there are unsaved |
| * changes in right viewer, results in inconsistent state. The dirty state |
| * of compare input should be based only on the information if there are |
| * changes in viewers for right side. |
| * |
| * @param dirty |
| * the dirty state for this compare input |
| * @since 3.7 |
| * @noreference This method is not intended to be referenced by clients. |
| * @nooverride This method is not intended to be re-implemented or extended |
| * by clients. |
| */ |
| protected void setRightDirty(boolean dirty) { |
| boolean oldDirty = isSaveNeeded(); |
| fRightDirty = dirty; |
| |
| if (oldDirty != isSaveNeeded()) { |
| Utilities.firePropertyChange(fListenerList, this, DIRTY_STATE, |
| Boolean.valueOf(oldDirty), Boolean.valueOf(isSaveNeeded())); |
| } |
| } |
| |
| /** |
| * Method adds or removes viewers that changed left or right side of this |
| * compare input. Any modification of any of the list of viewers may result |
| * in dirty state change. |
| * |
| * @param source |
| * the object that fired <code>PropertyChangeEvent</code> |
| * modifying the dirty state |
| * @param dirty |
| * value that describes if the changes were added or removed |
| */ |
| private void setDirty(Object source, boolean dirty) { |
| Assert.isNotNull(source); |
| boolean oldDirty = isSaveNeeded(); |
| |
| if (source instanceof ContentMergeViewer) { |
| ContentMergeViewer cmv = (ContentMergeViewer) source; |
| |
| if (dirty == cmv.internalIsLeftDirty()) { |
| fLeftDirty = cmv.internalIsLeftDirty(); |
| } |
| |
| if (dirty == cmv.internalIsRightDirty()) { |
| fRightDirty = cmv.internalIsRightDirty(); |
| } |
| } else { |
| fLeftDirty = dirty; |
| } |
| |
| boolean newDirty = isSaveNeeded(); |
| if (DEBUG) { |
| System.out.println("setDirty(" + source + ", " + dirty + "): " + newDirty); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ |
| } |
| if (oldDirty != newDirty) { |
| Utilities.firePropertyChange(fListenerList, this, DIRTY_STATE, Boolean.valueOf(oldDirty), Boolean.valueOf(newDirty)); |
| } |
| } |
| |
| @Override |
| public void addPropertyChangeListener(IPropertyChangeListener listener) { |
| if (listener != null) |
| fListenerList.add(listener); |
| } |
| |
| @Override |
| public void removePropertyChangeListener(IPropertyChangeListener listener) { |
| if (fListenerList != null) { |
| fListenerList.remove(listener); |
| } |
| } |
| |
| /** |
| * Save any unsaved changes. |
| * Empty implementation. |
| * Subclasses must override to save any changes. |
| * |
| * @param pm an <code>IProgressMonitor</code> that the implementation of save may use to show progress |
| * @deprecated Override method saveChanges instead. |
| */ |
| @Deprecated |
| public void save(IProgressMonitor pm) { |
| // empty default implementation |
| } |
| |
| /** |
| * Save any unsaved changes. |
| * Subclasses must override to save any changes. |
| * This implementation tries to flush changes in all viewers by |
| * calling <code>ISavable.save</code> on them. |
| * |
| * @param monitor an <code>IProgressMonitor</code> that the implementation of save may use to show progress |
| * @throws CoreException |
| * @since 2.0 |
| */ |
| public void saveChanges(IProgressMonitor monitor) throws CoreException { |
| flushViewers(monitor); |
| |
| save(monitor); |
| } |
| |
| /** |
| * Flush the viewer contents into the input. |
| * @param monitor a progress monitor |
| * @since 3.3 |
| */ |
| protected void flushViewers(IProgressMonitor monitor) { |
| // flush changes in any dirty viewer |
| flushViewer(fStructureInputPane, monitor); |
| flushViewer(fStructurePane1, monitor); |
| flushViewer(fStructurePane2, monitor); |
| flushViewer(fContentInputPane, monitor); |
| } |
| |
| /** |
| * @param monitor |
| * @noreference This method is not intended to be referenced by clients. |
| */ |
| protected void flushLeftViewers(IProgressMonitor monitor) { |
| // flush changes in left dirty viewer |
| flushViewer(fStructureInputPane, monitor); |
| flushViewer(fStructurePane1, monitor); |
| flushViewer(fStructurePane2, monitor); |
| flushLeftViewer(fContentInputPane, monitor); |
| } |
| |
| /** |
| * @param monitor |
| * @noreference This method is not intended to be referenced by clients. |
| */ |
| protected void flushRightViewers(IProgressMonitor monitor) { |
| // flush changes in right dirty viewer |
| flushViewer(fStructureInputPane, monitor); |
| flushViewer(fStructurePane1, monitor); |
| flushViewer(fStructurePane2, monitor); |
| flushRightViewer(fContentInputPane, monitor); |
| } |
| |
| private static void flushViewer(CompareViewerPane pane, IProgressMonitor pm) { |
| if (pane != null) { |
| IFlushable flushable = Adapters.adapt(pane, IFlushable.class); |
| if (flushable != null) |
| flushable.flush(pm); |
| } |
| } |
| |
| private static void flushLeftViewer(CompareViewerPane pane, IProgressMonitor pm) { |
| if (pane != null) { |
| IFlushable2 flushable = Adapters.adapt(pane, IFlushable2.class); |
| if (flushable != null) |
| flushable.flushLeft(pm); |
| } |
| } |
| |
| private static void flushRightViewer(CompareViewerPane pane, IProgressMonitor pm) { |
| if (pane != null) { |
| IFlushable2 flushable = Adapters.adapt(pane, IFlushable2.class); |
| if (flushable != null) |
| flushable.flushRight(pm); |
| } |
| } |
| |
| @Override |
| public void addCompareInputChangeListener(ICompareInput input, |
| ICompareInputChangeListener listener) { |
| if (fContainer == null) { |
| input.addCompareInputChangeListener(listener); |
| } else { |
| fContainer.addCompareInputChangeListener(input, listener); |
| } |
| } |
| |
| /* (non-Javadoc) |
| * @see org.eclipse.compare.ICompareContainer#removeCompareInputChangeListener(org.eclipse.compare.structuremergeviewer.ICompareInput, org.eclipse.compare.structuremergeviewer.ICompareInputChangeListener) |
| */ |
| @Override |
| public void removeCompareInputChangeListener(ICompareInput input, |
| ICompareInputChangeListener listener) { |
| if (fContainer == null) { |
| input.removeCompareInputChangeListener(listener); |
| } else { |
| fContainer.removeCompareInputChangeListener(input, listener); |
| } |
| } |
| |
| /* (non-Javadoc) |
| * @see org.eclipse.compare.ICompareContainer#registerContextMenu(org.eclipse.jface.action.MenuManager, org.eclipse.jface.viewers.ISelectionProvider) |
| */ |
| @Override |
| public void registerContextMenu(MenuManager menu, ISelectionProvider selectionProvider) { |
| if (fContainer != null) |
| fContainer.registerContextMenu(menu, selectionProvider); |
| } |
| |
| @Override |
| public void setStatusMessage(String message) { |
| if (!fContainerProvided) { |
| // Try the action bars directly |
| IActionBars actionBars= getActionBars(); |
| if (actionBars != null) { |
| IStatusLineManager slm= actionBars.getStatusLineManager(); |
| if (slm != null) { |
| slm.setMessage(message); |
| } |
| } |
| } else if (fContainer != null) { |
| fContainer.setStatusMessage(message); |
| } |
| } |
| |
| @Override |
| public IActionBars getActionBars() { |
| if (fContainer != null) { |
| IActionBars actionBars = fContainer.getActionBars(); |
| if (actionBars == null && !fContainerProvided) { |
| // The old way to find the action bars |
| return Utilities.findActionBars(fComposite); |
| } |
| return actionBars; |
| } |
| return null; |
| } |
| |
| /* (non-Javadoc) |
| * @see org.eclipse.compare.ICompareContainer#getServiceLocator() |
| */ |
| @Override |
| public IServiceLocator getServiceLocator() { |
| IServiceLocator serviceLocator = fContainer.getServiceLocator(); |
| if (serviceLocator == null && !fContainerProvided) { |
| // The old way to find the service locator |
| return Utilities.findSite(fComposite); |
| } |
| return serviceLocator; |
| } |
| |
| /* (non-Javadoc) |
| * @see org.eclipse.compare.ICompareContainer#getWorkbenchPart() |
| */ |
| @Override |
| public IWorkbenchPart getWorkbenchPart() { |
| if (fContainer != null) |
| return fContainer.getWorkbenchPart(); |
| return null; |
| } |
| |
| /* (non-Javadoc) |
| * @see org.eclipse.jface.operation.IRunnableContext#run(boolean, boolean, org.eclipse.jface.operation.IRunnableWithProgress) |
| */ |
| @Override |
| public void run(boolean fork, boolean cancelable, |
| IRunnableWithProgress runnable) throws InvocationTargetException, |
| InterruptedException { |
| if (fContainer != null) |
| fContainer.run(fork, cancelable, runnable); |
| } |
| |
| @Override |
| public void runAsynchronously(IRunnableWithProgress runnable) { |
| if (fContainer != null) |
| fContainer.runAsynchronously(runnable); |
| } |
| |
| /** |
| * Set the container of this input to the given container |
| * @param container the container |
| * @since 3.3 |
| */ |
| public void setContainer(ICompareContainer container) { |
| Assert.isNotNull(container); |
| this.fContainer = container; |
| fContainerProvided = true; |
| } |
| |
| /** |
| * Return the container of this input or <code>null</code> if there is no container |
| * set. |
| * @return the container of this input or <code>null</code> |
| * @since 3.3 |
| */ |
| public final ICompareContainer getContainer() { |
| return fContainer; |
| } |
| |
| /** |
| * Fire the given property change event to all listeners |
| * registered with this compare editor input. |
| * @param event the property change event |
| * @since 3.3 |
| */ |
| protected void firePropertyChange(PropertyChangeEvent event) { |
| Utilities.firePropertyChange(fListenerList, event); |
| } |
| |
| /** |
| * Return whether this compare editor input can be run as a job. |
| * By default, <code>false</code> is returned since traditionally inputs |
| * were prepared in the foreground (i.e the UI was blocked when the |
| * {@link #run(IProgressMonitor)} method (and indirectly the |
| * {@link #prepareInput(IProgressMonitor)} method) was invoked. Subclasses |
| * may override. |
| * @return whether this compare editor input can be run in the background |
| * @since 3.3 |
| */ |
| public boolean canRunAsJob() { |
| return false; |
| } |
| |
| /** |
| * Return whether this input belongs to the given family |
| * when it is run as a job. |
| * @see #canRunAsJob() |
| * @see Job#belongsTo(Object) |
| * @param family the job family |
| * @return whether this input belongs to the given family |
| * @since 3.3 |
| */ |
| public boolean belongsTo(Object family) { |
| return family == this; |
| } |
| |
| /** |
| * Return whether this input is intended to be used to select |
| * a particular edition of an element in a dialog. The result |
| * of this method is only consider if neither sides of the |
| * input are editable. By default, <code>false</code> is returned. |
| * @return whether this input is intended to be used to select |
| * a particular edition of an element in a dialog |
| * @see #getOKButtonLabel() |
| * @see #okPressed() |
| * @see #getSelectedEdition() |
| * @since 3.3 |
| */ |
| public boolean isEditionSelectionDialog() { |
| return false; |
| } |
| |
| /** |
| * Return the label to be used for the <code>OK</code> |
| * button when this input is displayed in a dialog. |
| * By default, different labels are used depending on |
| * whether the input is editable or is for edition selection |
| * (see {@link #isEditionSelectionDialog()}. |
| * @return the label to be used for the <code>OK</code> |
| * button when this input is displayed in a dialog |
| * @since 3.3 |
| */ |
| public String getOKButtonLabel() { |
| if (isEditable()) |
| return CompareMessages.CompareDialog_commit_button; |
| if (isEditionSelectionDialog()) |
| return CompareMessages.CompareEditorInput_0; |
| return IDialogConstants.OK_LABEL; |
| } |
| |
| /** |
| * Return the label used for the <code>CANCEL</code> |
| * button when this input is shown in a compare dialog |
| * using {@link CompareUI#openCompareDialog(CompareEditorInput)}. |
| * @return the label used for the <code>CANCEL</code> button |
| * @since 3.3 |
| */ |
| public String getCancelButtonLabel() { |
| return IDialogConstants.CANCEL_LABEL; |
| } |
| |
| private boolean isEditable() { |
| return getCompareConfiguration().isLeftEditable() |
| || getCompareConfiguration().isRightEditable(); |
| } |
| |
| /** |
| * The <code>OK</code> button was pressed in a dialog. If one or both of |
| * the sides of the input is editable then any changes will be saved. If the |
| * input is for edition selection (see {@link #isEditionSelectionDialog()}), |
| * it is up to subclasses to override this method in order to perform the |
| * appropriate operation on the selected edition. |
| * |
| * @return whether the dialog should be closed or not. |
| * @since 3.3 |
| */ |
| public boolean okPressed() { |
| if (isEditable()) { |
| if (!saveChanges()) |
| return false; |
| } |
| return true; |
| } |
| |
| /** |
| * The <code>CANCEL</code> button was pressed in a dialog. |
| * By default, nothing is done. Subclasses may override. |
| * @since 3.3 |
| */ |
| public void cancelPressed() { |
| // Do nothing |
| } |
| |
| private boolean saveChanges() { |
| try { |
| PlatformUI.getWorkbench().getProgressService().run(true, true, new IRunnableWithProgress() { |
| @Override |
| public void run(IProgressMonitor monitor) throws InvocationTargetException, InterruptedException { |
| try { |
| saveChanges(monitor); |
| } catch (CoreException e) { |
| throw new InvocationTargetException(e); |
| } |
| } |
| |
| }); |
| return true; |
| } catch (InterruptedException x) { |
| // Ignore |
| } catch (OperationCanceledException x) { |
| // Ignore |
| } catch (InvocationTargetException x) { |
| ErrorDialog.openError(fComposite.getShell(), CompareMessages.CompareDialog_error_title, null, |
| new Status(IStatus.ERROR, CompareUIPlugin.PLUGIN_ID, 0, |
| NLS.bind(CompareMessages.CompareDialog_error_message, x.getTargetException().getMessage()), x.getTargetException())); |
| } |
| return false; |
| } |
| |
| /** |
| * Return the selected edition or <code>null</code> if no edition is selected. |
| * The result of this method should only be considered if {@link #isEditionSelectionDialog()} |
| * returns <code>true</code>. |
| * @return the selected edition or <code>null</code> |
| * @since 3.3 |
| */ |
| public Object getSelectedEdition() { |
| if (fStructureInputPane != null) { |
| ISelection selection = fStructureInputPane.getSelection(); |
| if (selection instanceof IStructuredSelection) { |
| IStructuredSelection ss = (IStructuredSelection) selection; |
| if (!ss.isEmpty()) |
| return ss.getFirstElement(); |
| |
| } |
| } |
| return null; |
| } |
| |
| /** |
| * Set the help context id for this input. |
| * @param helpContextId the help context id. |
| * @since 3.3 |
| */ |
| public void setHelpContextId(String helpContextId) { |
| this.fHelpContextId = helpContextId; |
| } |
| |
| } |