Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
Diffstat (limited to 'bundles/org.eclipse.compare/compare/org/eclipse/compare/contentmergeviewer/ContentMergeViewer.java')
-rw-r--r--bundles/org.eclipse.compare/compare/org/eclipse/compare/contentmergeviewer/ContentMergeViewer.java1435
1 files changed, 1435 insertions, 0 deletions
diff --git a/bundles/org.eclipse.compare/compare/org/eclipse/compare/contentmergeviewer/ContentMergeViewer.java b/bundles/org.eclipse.compare/compare/org/eclipse/compare/contentmergeviewer/ContentMergeViewer.java
new file mode 100644
index 000000000..065620b11
--- /dev/null
+++ b/bundles/org.eclipse.compare/compare/org/eclipse/compare/contentmergeviewer/ContentMergeViewer.java
@@ -0,0 +1,1435 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2017 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ * Alex Blewitt <alex.blewitt@gmail.com> - replace new Boolean with Boolean.valueOf - https://bugs.eclipse.org/470344
+ * Stefan Xenos <sxenos@gmail.com> (Google) - bug 448968 - Add diagnostic logging
+ * Conrad Groth - Bug 213780 - Compare With direction should be configurable
+ *******************************************************************************/
+
+package org.eclipse.compare.contentmergeviewer;
+
+import java.io.IOException;
+import java.util.ResourceBundle;
+
+import org.eclipse.compare.CompareConfiguration;
+import org.eclipse.compare.CompareEditorInput;
+import org.eclipse.compare.CompareUI;
+import org.eclipse.compare.CompareViewerPane;
+import org.eclipse.compare.ICompareContainer;
+import org.eclipse.compare.ICompareInputLabelProvider;
+import org.eclipse.compare.IPropertyChangeNotifier;
+import org.eclipse.compare.internal.ChangePropertyAction;
+import org.eclipse.compare.internal.CompareEditor;
+import org.eclipse.compare.internal.CompareHandlerService;
+import org.eclipse.compare.internal.CompareMessages;
+import org.eclipse.compare.internal.ComparePreferencePage;
+import org.eclipse.compare.internal.CompareUIPlugin;
+import org.eclipse.compare.internal.ICompareUIConstants;
+import org.eclipse.compare.internal.IFlushable2;
+import org.eclipse.compare.internal.ISavingSaveable;
+import org.eclipse.compare.internal.MergeViewerContentProvider;
+import org.eclipse.compare.internal.MirroredMergeViewerContentProvider;
+import org.eclipse.compare.internal.Policy;
+import org.eclipse.compare.internal.Utilities;
+import org.eclipse.compare.internal.ViewerSwitchingCancelled;
+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.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.ListenerList;
+import org.eclipse.jface.action.Action;
+import org.eclipse.jface.action.ActionContributionItem;
+import org.eclipse.jface.action.IToolBarManager;
+import org.eclipse.jface.action.LegacyActionTools;
+import org.eclipse.jface.action.Separator;
+import org.eclipse.jface.action.ToolBarManager;
+import org.eclipse.jface.dialogs.IDialogConstants;
+import org.eclipse.jface.dialogs.MessageDialog;
+import org.eclipse.jface.preference.IPersistentPreferenceStore;
+import org.eclipse.jface.preference.IPreferenceStore;
+import org.eclipse.jface.util.IPropertyChangeListener;
+import org.eclipse.jface.util.PropertyChangeEvent;
+import org.eclipse.jface.viewers.*;
+import org.eclipse.jface.window.Window;
+import org.eclipse.osgi.util.TextProcessor;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.custom.CLabel;
+import org.eclipse.swt.events.*;
+import org.eclipse.swt.graphics.Cursor;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Layout;
+import org.eclipse.swt.widgets.Sash;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.ui.ISaveablesSource;
+import org.eclipse.ui.IWorkbenchPart;
+import org.eclipse.ui.Saveable;
+
+
+/**
+ * An abstract compare and merge viewer with two side-by-side content areas
+ * and an optional content area for the ancestor. The implementation makes no
+ * assumptions about the content type.
+ * <p>
+ * <code>ContentMergeViewer</code>
+ * <ul>
+ * <li>implements the overall layout and defines hooks so that subclasses
+ * can easily provide an implementation for a specific content type,
+ * <li>implements the UI for making the areas resizable,
+ * <li>has an action for controlling whether the ancestor area is visible or not,
+ * <li>has actions for copying one side of the input to the other side,
+ * <li>tracks the dirty state of the left and right sides and send out notification
+ * on state changes.
+ * </ul>
+ * A <code>ContentMergeViewer</code> accesses its
+ * model by means of a content provider which must implement the
+ * <code>IMergeViewerContentProvider</code> interface.
+ * </p>
+ * <p>
+ * Clients may wish to use the standard concrete subclass <code>TextMergeViewer</code>,
+ * or define their own subclass.
+ *
+ * @see IMergeViewerContentProvider
+ * @see TextMergeViewer
+ */
+public abstract class ContentMergeViewer extends ContentViewer
+ implements IPropertyChangeNotifier, IFlushable, IFlushable2 {
+ /* package */ static final int HORIZONTAL= 1;
+ /* package */ static final int VERTICAL= 2;
+
+ static final double HSPLIT= 0.5;
+ static final double VSPLIT= 0.3;
+
+ private class ContentMergeViewerLayout extends Layout {
+ @Override
+ public Point computeSize(Composite c, int w, int h, boolean force) {
+ return new Point(100, 100);
+ }
+
+ @Override
+ public void layout(Composite composite, boolean force) {
+ if (fLeftLabel == null) {
+ if (composite.isDisposed()) {
+ CompareUIPlugin
+ .log(new IllegalArgumentException("Attempted to perform a layout on a disposed composite")); //$NON-NLS-1$
+ }
+ if (Policy.debugContentMergeViewer) {
+ logTrace("found bad label. Layout = " + System.identityHashCode(this) + ". composite = " //$NON-NLS-1$//$NON-NLS-2$
+ + System.identityHashCode(composite) + ". fComposite = " //$NON-NLS-1$
+ + System.identityHashCode(fComposite) + ". fComposite.isDisposed() = " //$NON-NLS-1$
+ + fComposite.isDisposed());
+ logStackTrace();
+ }
+ // Help to find out the cause for bug 449558
+ NullPointerException npe= new NullPointerException("fLeftLabel is 'null';fLeftLabelSet is " + fLeftLabelSet + ";fComposite.isDisposed() is " + fComposite.isDisposed()); //$NON-NLS-1$ //$NON-NLS-2$
+
+ // Allow to test whether doing nothing helps
+ if (Boolean.getBoolean("ContentMergeViewer.DEBUG")) { //$NON-NLS-1$
+ CompareUIPlugin.log(npe);
+ return;
+ }
+
+ throw npe;
+ }
+
+ // determine some derived sizes
+ int headerHeight= fLeftLabel.computeSize(SWT.DEFAULT, SWT.DEFAULT, true).y;
+ Rectangle r= composite.getClientArea();
+
+ int centerWidth= getCenterWidth();
+ int width1= (int) ((r.width - centerWidth) * getHorizontalSplitRatio());
+ int width2= r.width - width1 - centerWidth;
+
+ int height1= 0;
+ int height2= 0;
+ if (fIsThreeWay && fAncestorVisible) {
+ height1= (int) ((r.height - (2 * headerHeight)) * fVSplit);
+ height2= r.height - (2 * headerHeight) - height1;
+ } else {
+ height1= 0;
+ height2= r.height - headerHeight;
+ }
+
+ int y= 0;
+
+ if (fIsThreeWay && fAncestorVisible) {
+ fAncestorLabel.setBounds(0, y, r.width, headerHeight);
+ fAncestorLabel.setVisible(true);
+ y+= headerHeight;
+ handleResizeAncestor(0, y, r.width, height1);
+ y+= height1;
+ } else {
+ fAncestorLabel.setVisible(false);
+ handleResizeAncestor(0, 0, 0, 0);
+ }
+
+ fLeftLabel.getSize(); // without this resizing would not always work
+
+ if (centerWidth > 3) {
+ fLeftLabel.setBounds(0, y, width1 + 1, headerHeight);
+ fDirectionLabel.setVisible(true);
+ fDirectionLabel.setBounds(width1 + 1, y, centerWidth - 1, headerHeight);
+ fRightLabel.setBounds(width1+centerWidth, y, width2, headerHeight);
+ } else {
+ fLeftLabel.setBounds(0, y, width1, headerHeight);
+ fDirectionLabel.setVisible(false);
+ fRightLabel.setBounds(width1, y, r.width - width1, headerHeight);
+ }
+
+ y+= headerHeight;
+
+ if (fCenter != null && !fCenter.isDisposed())
+ fCenter.setBounds(width1, y, centerWidth, height2);
+
+ handleResizeLeftRight(0, y, width1, centerWidth, width2, height2);
+ }
+
+ private double getHorizontalSplitRatio() {
+ if (fHSplit < 0) {
+ Object input = getInput();
+ if (input instanceof ICompareInput) {
+ ICompareInput ci = (ICompareInput) input;
+ if (ci.getLeft() == null)
+ return 0.1;
+ if (ci.getRight() == null)
+ return 0.9;
+ }
+ return HSPLIT;
+ }
+ return fHSplit;
+ }
+ }
+
+ class Resizer extends MouseAdapter implements MouseMoveListener {
+ Control fControl;
+ int fX, fY;
+ int fWidth1, fWidth2;
+ int fHeight1, fHeight2;
+ int fDirection;
+ boolean fLiveResize;
+ boolean fIsDown;
+
+ public Resizer(Control c, int dir) {
+ fDirection= dir;
+ fControl= c;
+ fLiveResize= !(fControl instanceof Sash);
+ updateCursor(c, dir);
+ fControl.addMouseListener(this);
+ fControl.addMouseMoveListener(this);
+ fControl.addDisposeListener(
+ e -> fControl= null
+ );
+ }
+
+ @Override
+ public void mouseDoubleClick(MouseEvent e) {
+ if ((fDirection & HORIZONTAL) != 0)
+ fHSplit= -1;
+ if ((fDirection & VERTICAL) != 0)
+ fVSplit= VSPLIT;
+ fComposite.layout(true);
+ }
+
+ @Override
+ public void mouseDown(MouseEvent e) {
+ Composite parent= fControl.getParent();
+
+ Point s= parent.getSize();
+ Point as= fAncestorLabel.getSize();
+ Point ys= fLeftLabel.getSize();
+ Point ms= fRightLabel.getSize();
+
+ fWidth1= ys.x;
+ fWidth2= ms.x;
+ fHeight1= fLeftLabel.getLocation().y - as.y;
+ fHeight2= s.y - (fLeftLabel.getLocation().y + ys.y);
+
+ fX= e.x;
+ fY= e.y;
+ fIsDown= true;
+ }
+
+ @Override
+ public void mouseUp(MouseEvent e) {
+ fIsDown= false;
+ if (!fLiveResize)
+ resize(e);
+ }
+
+ @Override
+ public void mouseMove(MouseEvent e) {
+ if (fIsDown && fLiveResize)
+ resize(e);
+ }
+
+ private void resize(MouseEvent e) {
+ int dx= e.x-fX;
+ int dy= e.y-fY;
+
+ int centerWidth= fCenter.getSize().x;
+
+ if (fWidth1 + dx > centerWidth && fWidth2 - dx > centerWidth) {
+ fWidth1 += dx;
+ fWidth2 -= dx;
+ if ((fDirection & HORIZONTAL) != 0)
+ fHSplit= (double) fWidth1 / (double) (fWidth1 + fWidth2);
+ }
+ if (fHeight1 + dy > centerWidth && fHeight2 - dy > centerWidth) {
+ fHeight1 += dy;
+ fHeight2 -= dy;
+ if ((fDirection & VERTICAL) != 0)
+ fVSplit= (double) fHeight1 / (double) (fHeight1 + fHeight2);
+ }
+
+ fComposite.layout(true);
+ fControl.getDisplay().update();
+ }
+ }
+
+ /** Style bits for top level composite */
+ private int fStyles;
+ private ResourceBundle fBundle;
+ private final CompareConfiguration fCompareConfiguration;
+ private IPropertyChangeListener fPropertyChangeListener;
+ private IPropertyChangeListener fPreferenceChangeListener;
+ private ICompareInputChangeListener fCompareInputChangeListener;
+ private ListenerList<IPropertyChangeListener> fListenerList;
+ boolean fConfirmSave= true;
+
+ private double fHSplit= -1; // width ratio of left and right panes
+ private double fVSplit= VSPLIT; // height ratio of ancestor and bottom panes
+
+ private boolean fIsThreeWay; // whether their is an ancestor
+ private boolean fAncestorVisible; // whether the ancestor pane is visible
+ private ActionContributionItem fAncestorItem;
+
+ private ActionContributionItem copyLeftToRightItem; // copy from left to right
+ private ActionContributionItem copyRightToLeftItem; // copy from right to left
+
+ private boolean fIsLeftDirty;
+ private boolean fIsRightDirty;
+
+ private CompareHandlerService fHandlerService;
+
+ private final MergeViewerContentProvider fDefaultContentProvider;
+ private Action fSwitchLeftAndRight;
+
+ // SWT widgets
+ /* package */ Composite fComposite;
+ private CLabel fAncestorLabel;
+ private CLabel fLeftLabel;
+
+ private boolean fLeftLabelSet= false; // needed for debug output for bug 449558
+ private CLabel fRightLabel;
+ /* package */ CLabel fDirectionLabel;
+ /* package */ Control fCenter;
+
+ //---- SWT resources to be disposed
+ private Image fRightArrow;
+ private Image fLeftArrow;
+ private Image fBothArrow;
+ Cursor fNormalCursor;
+ private Cursor fHSashCursor;
+ private Cursor fVSashCursor;
+ private Cursor fHVSashCursor;
+
+ private ILabelProviderListener labelChangeListener = event -> {
+ Object[] elements = event.getElements();
+ for (int i = 0; i < elements.length; i++) {
+ Object object = elements[i];
+ if (object == getInput())
+ updateHeader();
+ }
+ };
+
+ //---- end
+
+ /**
+ * Creates a new content merge viewer and initializes with a resource bundle and a
+ * configuration.
+ *
+ * @param style SWT style bits
+ * @param bundle the resource bundle
+ * @param cc the configuration object
+ */
+ protected ContentMergeViewer(int style, ResourceBundle bundle, CompareConfiguration cc) {
+ if (Policy.debugContentMergeViewer) {
+ logTrace("constructed (fLeftLabel == null)"); //$NON-NLS-1$
+ logStackTrace();
+ }
+
+ fStyles= style & ~(SWT.LEFT_TO_RIGHT | SWT.RIGHT_TO_LEFT); // remove BIDI direction bits
+ fBundle= bundle;
+
+ fAncestorVisible= Utilities.getBoolean(cc, ICompareUIConstants.PROP_ANCESTOR_VISIBLE, fAncestorVisible);
+ fConfirmSave= Utilities.getBoolean(cc, CompareEditor.CONFIRM_SAVE_PROPERTY, fConfirmSave);
+
+ fCompareInputChangeListener = (input) -> { if (input == getInput()) handleCompareInputChange(); };
+
+ // Make sure the compare configuration is not null
+ fCompareConfiguration = cc != null ? cc : new CompareConfiguration();
+ fPropertyChangeListener = event -> handlePropertyChangeEvent(event);
+ fCompareConfiguration.addPropertyChangeListener(fPropertyChangeListener);
+ fPreferenceChangeListener = event -> {
+ if (event.getProperty().equals(ComparePreferencePage.SWAPPED)) {
+ getCompareConfiguration().setProperty(CompareConfiguration.MIRRORED, event.getNewValue());
+ updateContentProvider();
+ updateToolItems();
+ }
+ };
+ cc.getPreferenceStore().addPropertyChangeListener(fPreferenceChangeListener);
+
+ fDefaultContentProvider = new MergeViewerContentProvider(fCompareConfiguration);
+ updateContentProvider();
+
+ fIsLeftDirty = false;
+ fIsRightDirty = false;
+ }
+
+ private void logStackTrace() {
+ new Exception("<Fake exception> in " + getClass().getName()).printStackTrace(System.out); //$NON-NLS-1$
+ }
+
+ private void logTrace(String string) {
+ System.out.println("ContentMergeViewer " + System.identityHashCode(this) + ": " + string); //$NON-NLS-1$//$NON-NLS-2$
+ }
+
+ //---- hooks ---------------------
+
+
+ /**
+ * Returns the viewer's name.
+ *
+ * @return the viewer's name
+ */
+ public String getTitle() {
+ return Utilities.getString(getResourceBundle(), "title"); //$NON-NLS-1$
+ }
+
+ /**
+ * Creates the SWT controls for the ancestor, left, and right
+ * content areas of this compare viewer.
+ * Implementations typically hold onto the controls
+ * so that they can be initialized with the input objects in method
+ * <code>updateContent</code>.
+ *
+ * @param composite the container for the three areas
+ */
+ abstract protected void createControls(Composite composite);
+
+ /**
+ * Lays out the ancestor area of the compare viewer.
+ * It is called whenever the viewer is resized or when the sashes between
+ * the areas are moved to adjust the size of the areas.
+ *
+ * @param x the horizontal position of the ancestor area within its container
+ * @param y the vertical position of the ancestor area within its container
+ * @param width the width of the ancestor area
+ * @param height the height of the ancestor area
+ */
+ abstract protected void handleResizeAncestor(int x, int y, int width, int height);
+
+ /**
+ * Lays out the left and right areas of the compare viewer.
+ * It is called whenever the viewer is resized or when the sashes between
+ * the areas are moved to adjust the size of the areas.
+ *
+ * @param x the horizontal position of the left area within its container
+ * @param y the vertical position of the left and right area within its container
+ * @param leftWidth the width of the left area
+ * @param centerWidth the width of the gap between the left and right areas
+ * @param rightWidth the width of the right area
+ * @param height the height of the left and right areas
+ */
+ abstract protected void handleResizeLeftRight(int x, int y, int leftWidth, int centerWidth,
+ int rightWidth, int height);
+
+ /**
+ * Contributes items to the given <code>ToolBarManager</code>.
+ * It is called when this viewer is installed in its container and if the container
+ * has a <code>ToolBarManager</code>.
+ * The <code>ContentMergeViewer</code> implementation of this method does nothing.
+ * Subclasses may reimplement.
+ *
+ * @param toolBarManager the toolbar manager to contribute to
+ */
+ protected void createToolItems(ToolBarManager toolBarManager) {
+ // empty implementation
+ }
+
+ /**
+ * Initializes the controls of the three content areas with the given input objects.
+ *
+ * @param ancestor the input for the ancestor area
+ * @param left the input for the left area
+ * @param right the input for the right area
+ */
+ abstract protected void updateContent(Object ancestor, Object left, Object right);
+
+ /**
+ * Copies the content of one side to the other side.
+ * Called from the (internal) actions for copying the sides of the viewer's input object.
+ *
+ * @param leftToRight if <code>true</code>, the left side is copied to the right side;
+ * if <code>false</code>, the right side is copied to the left side
+ */
+ abstract protected void copy(boolean leftToRight);
+
+ /**
+ * Returns the byte contents of the left or right side. If the viewer
+ * has no editable content <code>null</code> can be returned.
+ *
+ * @param left if <code>true</code>, the byte contents of the left area is returned;
+ * if <code>false</code>, the byte contents of the right area
+ * @return the content as an array of bytes, or <code>null</code>
+ */
+ abstract protected byte[] getContents(boolean left);
+
+ //----------------------------
+
+ /**
+ * Returns the resource bundle of this viewer.
+ *
+ * @return the resource bundle
+ */
+ protected ResourceBundle getResourceBundle() {
+ return fBundle;
+ }
+
+ /**
+ * Returns the compare configuration of this viewer.
+ *
+ * @return the compare configuration, never <code>null</code>
+ */
+ protected CompareConfiguration getCompareConfiguration() {
+ return fCompareConfiguration;
+ }
+
+ /**
+ * The <code>ContentMergeViewer</code> implementation of this
+ * <code>ContentViewer</code> method
+ * checks to ensure that the content provider is an <code>IMergeViewerContentProvider</code>.
+ * @param contentProvider the content provider to set. Must implement IMergeViewerContentProvider.
+ */
+ @Override
+ public void setContentProvider(IContentProvider contentProvider) {
+ Assert.isTrue(contentProvider instanceof IMergeViewerContentProvider);
+ super.setContentProvider(contentProvider);
+ }
+
+ private void updateContentProvider() {
+ setContentProvider(getCompareConfiguration().isMirrored()
+ ? new MirroredMergeViewerContentProvider(getCompareConfiguration(), fDefaultContentProvider)
+ : fDefaultContentProvider);
+ }
+
+ /* package */ IMergeViewerContentProvider getMergeContentProvider() {
+ return (IMergeViewerContentProvider) getContentProvider();
+ }
+
+ /**
+ * The <code>ContentMergeViewer</code> implementation of this
+ * <code>Viewer</code> method returns the empty selection. Subclasses may override.
+ * @return empty selection.
+ */
+ @Override
+ public ISelection getSelection() {
+ return () -> true;
+ }
+
+ /**
+ * The <code>ContentMergeViewer</code> implementation of this
+ * <code>Viewer</code> method does nothing. Subclasses may reimplement.
+ * @see org.eclipse.jface.viewers.Viewer#setSelection(org.eclipse.jface.viewers.ISelection, boolean)
+ */
+ @Override
+ public void setSelection(ISelection selection, boolean reveal) {
+ // Empty implementation.
+ }
+
+ /**
+ * Callback that is invoked when a property in the compare configuration
+ * ({@link #getCompareConfiguration()} changes.
+ * @param event the property change event
+ * @since 3.3
+ */
+ protected void handlePropertyChangeEvent(PropertyChangeEvent event) {
+ String key= event.getProperty();
+
+ if (key.equals(ICompareUIConstants.PROP_ANCESTOR_VISIBLE)) {
+ fAncestorVisible= Utilities.getBoolean(getCompareConfiguration(), ICompareUIConstants.PROP_ANCESTOR_VISIBLE, fAncestorVisible);
+ fComposite.layout(true);
+
+ updateCursor(fLeftLabel, VERTICAL);
+ updateCursor(fDirectionLabel, HORIZONTAL | VERTICAL);
+ updateCursor(fRightLabel, VERTICAL);
+
+ return;
+ }
+
+ if (key.equals(ICompareUIConstants.PROP_IGNORE_ANCESTOR)) {
+ setAncestorVisibility(false, !Utilities.getBoolean(getCompareConfiguration(), ICompareUIConstants.PROP_IGNORE_ANCESTOR, false));
+ return;
+ }
+ }
+
+ void updateCursor(Control c, int dir) {
+ if (!(c instanceof Sash)) {
+ Cursor cursor= null;
+ switch (dir) {
+ case VERTICAL:
+ if (fAncestorVisible) {
+ if (fVSashCursor == null)
+ fVSashCursor= new Cursor(c.getDisplay(), SWT.CURSOR_SIZENS);
+ cursor= fVSashCursor;
+ } else {
+ if (fNormalCursor == null)
+ fNormalCursor= new Cursor(c.getDisplay(), SWT.CURSOR_ARROW);
+ cursor= fNormalCursor;
+ }
+ break;
+ case HORIZONTAL:
+ if (fHSashCursor == null)
+ fHSashCursor= new Cursor(c.getDisplay(), SWT.CURSOR_SIZEWE);
+ cursor= fHSashCursor;
+ break;
+ case VERTICAL + HORIZONTAL:
+ if (fAncestorVisible) {
+ if (fHVSashCursor == null)
+ fHVSashCursor= new Cursor(c.getDisplay(), SWT.CURSOR_SIZEALL);
+ cursor= fHVSashCursor;
+ } else {
+ if (fHSashCursor == null)
+ fHSashCursor= new Cursor(c.getDisplay(), SWT.CURSOR_SIZEWE);
+ cursor= fHSashCursor;
+ }
+ break;
+ }
+ if (cursor != null)
+ c.setCursor(cursor);
+ }
+ }
+
+ private void setAncestorVisibility(boolean visible, boolean enabled) {
+ if (fAncestorItem != null) {
+ Action action= (Action) fAncestorItem.getAction();
+ if (action != null) {
+ action.setChecked(visible);
+ action.setEnabled(enabled);
+ }
+ }
+ getCompareConfiguration().setProperty(ICompareUIConstants.PROP_ANCESTOR_VISIBLE, Boolean.valueOf(visible));
+ }
+
+ //---- input
+
+ /**
+ * Return whether the input is a three-way comparison.
+ * @return whether the input is a three-way comparison
+ * @since 3.3
+ */
+ protected boolean isThreeWay() {
+ return fIsThreeWay;
+ }
+
+ /**
+ * Internal hook method called when the input to this viewer is
+ * initially set or subsequently changed.
+ * <p>
+ * The <code>ContentMergeViewer</code> implementation of this <code>Viewer</code>
+ * method tries to save the old input by calling <code>doSave(...)</code> and
+ * then calls <code>internalRefresh(...)</code>.
+ *
+ * @param input the new input of this viewer, or <code>null</code> if there is no new input
+ * @param oldInput the old input element, or <code>null</code> if there was previously no input
+ */
+ @Override
+ protected final void inputChanged(Object input, Object oldInput) {
+ if (input != oldInput && oldInput != null) {
+ ICompareInputLabelProvider lp = getCompareConfiguration().getLabelProvider();
+ if (lp != null)
+ lp.removeListener(labelChangeListener);
+ }
+
+ if (input != oldInput && oldInput instanceof ICompareInput) {
+ ICompareContainer container = getCompareConfiguration().getContainer();
+ container.removeCompareInputChangeListener((ICompareInput)oldInput, fCompareInputChangeListener);
+ }
+
+ boolean success= doSave(input, oldInput);
+
+ if (input != oldInput && input instanceof ICompareInput) {
+ ICompareContainer container = getCompareConfiguration().getContainer();
+ container.addCompareInputChangeListener((ICompareInput)input, fCompareInputChangeListener);
+ }
+
+ if (input != oldInput && input != null) {
+ ICompareInputLabelProvider lp = getCompareConfiguration().getLabelProvider();
+ if (lp != null)
+ lp.addListener(labelChangeListener);
+ }
+
+ if (success) {
+ setLeftDirty(false);
+ setRightDirty(false);
+ }
+
+ if (input != oldInput)
+ internalRefresh(input);
+ }
+
+ /**
+ * This method is called from the <code>Viewer</code> method <code>inputChanged</code>
+ * to save any unsaved changes of the old input.
+ * <p>
+ * The <code>ContentMergeViewer</code> implementation of this
+ * method calls <code>saveContent(...)</code>. If confirmation has been turned on
+ * with <code>setConfirmSave(true)</code>, a confirmation alert is posted before saving.
+ * </p>
+ * Clients can override this method and are free to decide whether
+ * they want to call the inherited method.
+ * @param newInput the new input of this viewer, or <code>null</code> if there is no new input
+ * @param oldInput the old input element, or <code>null</code> if there was previously no input
+ * @return <code>true</code> if saving was successful, or if the user didn't want to save (by pressing 'NO' in the confirmation dialog).
+ * @since 2.0
+ */
+ protected boolean doSave(Object newInput, Object oldInput) {
+ // before setting the new input we have to save the old
+ if (isLeftDirty() || isRightDirty()) {
+ if (Utilities.RUNNING_TESTS) {
+ if (Utilities.TESTING_FLUSH_ON_COMPARE_INPUT_CHANGE) {
+ flushContent(oldInput, null);
+ }
+ } else if (fConfirmSave) {
+ // post alert
+ Shell shell= fComposite.getShell();
+
+ MessageDialog dialog= new MessageDialog(shell,
+ Utilities.getString(getResourceBundle(), "saveDialog.title"), //$NON-NLS-1$
+ null, // accept the default window icon
+ Utilities.getString(getResourceBundle(), "saveDialog.message"), //$NON-NLS-1$
+ MessageDialog.QUESTION,
+ new String[] { IDialogConstants.YES_LABEL, IDialogConstants.NO_LABEL, },
+ 0); // default button index
+
+ switch (dialog.open()) { // open returns index of pressed button
+ case 0:
+ flushContent(oldInput, null);
+ break;
+ case 1:
+ setLeftDirty(false);
+ setRightDirty(false);
+ break;
+ case 2:
+ throw new ViewerSwitchingCancelled();
+ }
+ } else {
+ flushContent(oldInput, null);
+ }
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Controls whether <code>doSave(Object, Object)</code> asks for confirmation before saving
+ * the old input with <code>saveContent(Object)</code>.
+ * @param enable a value of <code>true</code> enables confirmation
+ * @since 2.0
+ */
+ public void setConfirmSave(boolean enable) {
+ fConfirmSave= enable;
+ }
+
+ @Override
+ public void refresh() {
+ internalRefresh(getInput());
+ }
+
+ private void internalRefresh(Object input) {
+ IMergeViewerContentProvider content= getMergeContentProvider();
+ if (content != null) {
+ Object ancestor= content.getAncestorContent(input);
+ boolean oldFlag = fIsThreeWay;
+ if (Utilities.isHunk(input)) {
+ fIsThreeWay = true;
+ } else if (input instanceof ICompareInput) {
+ fIsThreeWay= (((ICompareInput) input).getKind() & Differencer.DIRECTION_MASK) != 0;
+ } else {
+ fIsThreeWay= ancestor != null;
+ }
+
+ if (fAncestorItem != null)
+ fAncestorItem.setVisible(fIsThreeWay);
+
+ if (fAncestorVisible && oldFlag != fIsThreeWay)
+ fComposite.layout(true);
+
+ Object left= content.getLeftContent(input);
+ Object right= content.getRightContent(input);
+ updateContent(ancestor, left, right);
+
+ updateHeader();
+ if (Utilities.okToUse(fComposite) && Utilities.okToUse(fComposite.getParent())) {
+ ToolBarManager tbm = (ToolBarManager) getToolBarManager(fComposite.getParent());
+ if (tbm != null ) {
+ updateToolItems();
+ tbm.update(true);
+ tbm.getControl().getParent().layout(true);
+ }
+ }
+ }
+ }
+
+ @Override
+ protected void hookControl(Control control) {
+ if (Policy.debugContentMergeViewer) {
+ logTrace("Attached dispose listener to control " + System.identityHashCode(control)); //$NON-NLS-1$
+ }
+ super.hookControl(control);
+ }
+
+ //---- layout & SWT control creation
+
+ /**
+ * Builds the SWT controls for the three areas of a compare/merge viewer.
+ * <p>
+ * Calls the hooks <code>createControls</code> and <code>createToolItems</code>
+ * to let subclasses build the specific content areas and to add items to
+ * an enclosing toolbar.
+ * <p>
+ * This method must only be called in the constructor of subclasses.
+ *
+ * @param parent the parent control
+ * @return the new control
+ */
+ protected final Control buildControl(Composite parent) {
+ fComposite= new Composite(parent, fStyles | SWT.LEFT_TO_RIGHT) { // We force a specific direction
+ @Override
+ public boolean setFocus() {
+ return ContentMergeViewer.this.handleSetFocus();
+ }
+ };
+ fComposite.setData(CompareUI.COMPARE_VIEWER_TITLE, getTitle());
+
+ hookControl(fComposite); // Hook help & dispose listener.
+
+ fComposite.setLayout(new ContentMergeViewerLayout());
+ if (Policy.debugContentMergeViewer) {
+ logTrace("Created composite " + System.identityHashCode(fComposite) + " with layout " //$NON-NLS-1$//$NON-NLS-2$
+ + System.identityHashCode(fComposite.getLayout()));
+ logStackTrace();
+ }
+
+ int style= SWT.SHADOW_OUT;
+ fAncestorLabel= new CLabel(fComposite, style | Window.getDefaultOrientation());
+
+ fLeftLabel= new CLabel(fComposite, style | Window.getDefaultOrientation());
+ if (Policy.debugContentMergeViewer) {
+ logTrace("fLeftLabel initialized"); //$NON-NLS-1$
+ logStackTrace();
+ }
+
+ fLeftLabelSet= true;
+ new Resizer(fLeftLabel, VERTICAL);
+
+ fDirectionLabel= new CLabel(fComposite, style);
+ fDirectionLabel.setAlignment(SWT.CENTER);
+ new Resizer(fDirectionLabel, HORIZONTAL | VERTICAL);
+
+ fRightLabel= new CLabel(fComposite, style | Window.getDefaultOrientation());
+ new Resizer(fRightLabel, VERTICAL);
+
+ if (fCenter == null || fCenter.isDisposed())
+ fCenter= createCenterControl(fComposite);
+
+ createControls(fComposite);
+
+ fHandlerService= CompareHandlerService.createFor(getCompareConfiguration().getContainer(), fComposite.getShell());
+
+ initializeToolbars(parent);
+
+ return fComposite;
+ }
+
+ /**
+ * Returns the toolbar manager for this viewer.
+ *
+ * Subclasses may extend this method and use either the toolbar manager
+ * provided by the inherited method by calling
+ * super.getToolBarManager(parent) or provide an alternate toolbar manager.
+ *
+ * @param parent
+ * a <code>Composite</code> or <code>null</code>
+ * @return a <code>IToolBarManager</code>
+ * @since 3.4
+ */
+ protected IToolBarManager getToolBarManager(Composite parent) {
+ return CompareViewerPane.getToolBarManager(parent);
+ }
+
+ private void initializeToolbars(Composite parent) {
+ ToolBarManager tbm = (ToolBarManager) getToolBarManager(parent);
+ if (tbm != null) {
+ tbm.removeAll();
+
+ // Define groups.
+ tbm.add(new Separator("modes")); //$NON-NLS-1$
+ tbm.add(new Separator("merge")); //$NON-NLS-1$
+ tbm.add(new Separator("navigation")); //$NON-NLS-1$
+
+ copyLeftToRightItem= createCopyAction(true);
+ Utilities.initAction(copyLeftToRightItem.getAction(), getResourceBundle(), "action.CopyLeftToRight."); //$NON-NLS-1$
+ tbm.appendToGroup("merge", copyLeftToRightItem); //$NON-NLS-1$
+ fHandlerService.registerAction(copyLeftToRightItem.getAction(), "org.eclipse.compare.copyAllLeftToRight"); //$NON-NLS-1$
+
+ copyRightToLeftItem= createCopyAction(false);
+ Utilities.initAction(copyRightToLeftItem.getAction(), getResourceBundle(), "action.CopyRightToLeft."); //$NON-NLS-1$
+ tbm.appendToGroup("merge", copyRightToLeftItem); //$NON-NLS-1$
+ fHandlerService.registerAction(copyRightToLeftItem.getAction(), "org.eclipse.compare.copyAllRightToLeft"); //$NON-NLS-1$
+
+ fSwitchLeftAndRight = new Action() {
+ @Override
+ public void run() {
+ IPreferenceStore preferences = getCompareConfiguration().getPreferenceStore();
+ preferences.setValue(ComparePreferencePage.SWAPPED, !getCompareConfiguration().isMirrored());
+ if (preferences instanceof IPersistentPreferenceStore) {
+ try {
+ ((IPersistentPreferenceStore) preferences).save();
+ } catch (IOException e) {
+ CompareUIPlugin.log(e);
+ }
+ }
+ }
+ };
+ Utilities.initAction(fSwitchLeftAndRight, getResourceBundle(), "action.SwitchLeftAndRight."); //$NON-NLS-1$
+ tbm.appendToGroup("modes", fSwitchLeftAndRight); //$NON-NLS-1$
+
+ final ChangePropertyAction a= new ChangePropertyAction(fBundle, getCompareConfiguration(), "action.EnableAncestor.", ICompareUIConstants.PROP_ANCESTOR_VISIBLE); //$NON-NLS-1$
+ a.setChecked(fAncestorVisible);
+ fAncestorItem= new ActionContributionItem(a);
+ fAncestorItem.setVisible(false);
+ tbm.appendToGroup("modes", fAncestorItem); //$NON-NLS-1$
+ tbm.getControl().addDisposeListener(a);
+
+ createToolItems(tbm);
+ updateToolItems();
+
+ tbm.update(true);
+ }
+ }
+
+ private ActionContributionItem createCopyAction(boolean leftToRight) {
+ return new ActionContributionItem(new Action() {
+ @Override
+ public void run() {
+ copy(leftToRight);
+ }
+ });
+ }
+
+ /**
+ * Callback that is invoked when the control of this merge viewer is given focus.
+ * This method should return <code>true</code> if a particular widget was given focus
+ * and false otherwise. By default, <code>false</code> is returned. Subclasses may override.
+ * @return whether particular widget was given focus
+ * @since 3.3
+ */
+ protected boolean handleSetFocus() {
+ return false;
+ }
+
+ /**
+ * Return the desired width of the center control. This width is used
+ * to calculate the values used to layout the ancestor, left and right sides.
+ * @return the desired width of the center control
+ * @see #handleResizeLeftRight(int, int, int, int, int, int)
+ * @see #handleResizeAncestor(int, int, int, int)
+ * @since 3.3
+ */
+ protected int getCenterWidth() {
+ return 3;
+ }
+
+ /**
+ * Return whether the ancestor pane is visible or not.
+ * @return whether the ancestor pane is visible or not
+ * @since 3.3
+ */
+ protected boolean isAncestorVisible() {
+ return fAncestorVisible;
+ }
+
+ /**
+ * Create the control that divides the left and right sides of the merge viewer.
+ * @param parent the parent composite
+ * @return the center control
+ * @since 3.3
+ */
+ protected Control createCenterControl(Composite parent) {
+ Sash sash= new Sash(parent, SWT.VERTICAL);
+ new Resizer(sash, HORIZONTAL);
+ return sash;
+ }
+
+ /**
+ * Return the center control that divides the left and right sides of the merge viewer.
+ * This method returns the control that was created by calling {@link #createCenterControl(Composite)}.
+ * @see #createCenterControl(Composite)
+ * @return the center control
+ * @since 3.3
+ */
+ protected Control getCenterControl() {
+ return fCenter;
+ }
+
+ @Override
+ public Control getControl() {
+ return fComposite;
+ }
+
+ /**
+ * Called on the viewer disposal.
+ * Unregisters from the compare configuration.
+ * Clients may extend if they have to do additional cleanup.
+ * @see org.eclipse.jface.viewers.ContentViewer#handleDispose(org.eclipse.swt.events.DisposeEvent)
+ */
+ @Override
+ protected void handleDispose(DisposeEvent event) {
+ if (fHandlerService != null)
+ fHandlerService.dispose();
+
+ Object input= getInput();
+ if (input instanceof ICompareInput) {
+ ICompareContainer container = getCompareConfiguration().getContainer();
+ container.removeCompareInputChangeListener((ICompareInput)input, fCompareInputChangeListener);
+ }
+ if (input != null) {
+ ICompareInputLabelProvider lp = getCompareConfiguration().getLabelProvider();
+ if (lp != null)
+ lp.removeListener(labelChangeListener);
+ }
+
+ if (fPropertyChangeListener != null) {
+ fCompareConfiguration.removePropertyChangeListener(fPropertyChangeListener);
+ fPropertyChangeListener= null;
+ }
+
+ if (fPreferenceChangeListener != null) {
+ fCompareConfiguration.getPreferenceStore().removePropertyChangeListener(fPreferenceChangeListener);
+ fPreferenceChangeListener= null;
+ }
+
+ fAncestorLabel= null;
+ fLeftLabel= null;
+ if (Policy.debugContentMergeViewer) {
+ logTrace("handleDispose(...) - fLeftLabel = null. event.widget = " + System.identityHashCode(event.widget)); //$NON-NLS-1$
+ logStackTrace();
+ }
+ fDirectionLabel= null;
+ fRightLabel= null;
+ fCenter= null;
+
+ if (fRightArrow != null) {
+ fRightArrow.dispose();
+ fRightArrow= null;
+ }
+ if (fLeftArrow != null) {
+ fLeftArrow.dispose();
+ fLeftArrow= null;
+ }
+ if (fBothArrow != null) {
+ fBothArrow.dispose();
+ fBothArrow= null;
+ }
+
+ if (fNormalCursor != null) {
+ fNormalCursor.dispose();
+ fNormalCursor= null;
+ }
+ if (fHSashCursor != null) {
+ fHSashCursor.dispose();
+ fHSashCursor= null;
+ }
+ if (fVSashCursor != null) {
+ fVSashCursor.dispose();
+ fVSashCursor= null;
+ }
+ if (fHVSashCursor != null) {
+ fHVSashCursor.dispose();
+ fHVSashCursor= null;
+ }
+
+ super.handleDispose(event);
+ }
+
+ /**
+ * Updates the enabled state of the toolbar items.
+ * <p>
+ * This method is called whenever the state of the items needs updating.
+ * <p>
+ * Subclasses may extend this method, although this is generally not required.
+ */
+ protected void updateToolItems() {
+ IMergeViewerContentProvider content= getMergeContentProvider();
+
+ Object input= getInput();
+
+ if (copyLeftToRightItem != null) {
+ boolean rightEditable = content.isRightEditable(input);
+ copyLeftToRightItem.setVisible(rightEditable);
+ copyLeftToRightItem.getAction().setEnabled(rightEditable);
+ }
+
+ if (copyRightToLeftItem != null) {
+ boolean leftEditable = content.isLeftEditable(input);
+ copyRightToLeftItem.setVisible(leftEditable);
+ copyRightToLeftItem.getAction().setEnabled(leftEditable);
+ }
+
+ if (fSwitchLeftAndRight != null) {
+ fSwitchLeftAndRight.setChecked(getCompareConfiguration().isMirrored());
+ }
+ }
+
+ /**
+ * Updates the headers of the three areas
+ * by querying the content provider for a name and image for
+ * the three sides of the input object.
+ * <p>
+ * This method is called whenever the header must be updated.
+ * <p>
+ * Subclasses may extend this method, although this is generally not required.
+ */
+ protected void updateHeader() {
+ IMergeViewerContentProvider content= getMergeContentProvider();
+ Object input= getInput();
+
+ // Only change a label if there is a new label available
+ if (fAncestorLabel != null) {
+ Image ancestorImage = content.getAncestorImage(input);
+ if (ancestorImage != null)
+ fAncestorLabel.setImage(ancestorImage);
+ String ancestorLabel = content.getAncestorLabel(input);
+ if (ancestorLabel != null)
+ fAncestorLabel.setText(LegacyActionTools.escapeMnemonics(TextProcessor.process(ancestorLabel)));
+ }
+ if (fLeftLabel != null) {
+ Image leftImage = content.getLeftImage(input);
+ if (leftImage != null)
+ fLeftLabel.setImage(leftImage);
+ String leftLabel = content.getLeftLabel(input);
+ if (leftLabel != null)
+ fLeftLabel.setText(LegacyActionTools.escapeMnemonics(leftLabel));
+ }
+ if (fRightLabel != null) {
+ Image rightImage = content.getRightImage(input);
+ if (rightImage != null)
+ fRightLabel.setImage(rightImage);
+ String rightLabel = content.getRightLabel(input);
+ if (rightLabel != null)
+ fRightLabel.setText(LegacyActionTools.escapeMnemonics(rightLabel));
+ }
+ }
+
+ /**
+ * Calculates the height of the header.
+ */
+ /* package */ int getHeaderHeight() {
+ int headerHeight= fLeftLabel.computeSize(SWT.DEFAULT, SWT.DEFAULT, true).y;
+ headerHeight= Math.max(headerHeight, fDirectionLabel.computeSize(SWT.DEFAULT, SWT.DEFAULT, true).y);
+ return headerHeight;
+ }
+
+ //---- dirty state & saving state
+
+ @Override
+ public void addPropertyChangeListener(IPropertyChangeListener listener) {
+ if (fListenerList == null)
+ fListenerList= new ListenerList<>();
+ fListenerList.add(listener);
+ }
+
+ @Override
+ public void removePropertyChangeListener(IPropertyChangeListener listener) {
+ if (fListenerList != null) {
+ fListenerList.remove(listener);
+ if (fListenerList.isEmpty())
+ fListenerList= null;
+ }
+ }
+
+ private void fireDirtyState(boolean state) {
+ Utilities.firePropertyChange(fListenerList, this, CompareEditorInput.DIRTY_STATE, null, Boolean.valueOf(state));
+ }
+
+ /**
+ * Sets the dirty state of the left side of this viewer.
+ * If the new value differs from the old
+ * all registered listener are notified with
+ * a <code>PropertyChangeEvent</code> with the
+ * property name <code>CompareEditorInput.DIRTY_STATE</code>.
+ *
+ * @param dirty the state of the left side dirty flag
+ */
+ protected void setLeftDirty(boolean dirty) {
+ if (isLeftDirty() != dirty) {
+ fIsLeftDirty = dirty;
+ // Always fire the event if the dirty state has changed
+ fireDirtyState(dirty);
+ }
+ }
+
+ /**
+ * Sets the dirty state of the right side of this viewer.
+ * If the new value differs from the old
+ * all registered listener are notified with
+ * a <code>PropertyChangeEvent</code> with the
+ * property name <code>CompareEditorInput.DIRTY_STATE</code>.
+ *
+ * @param dirty the state of the right side dirty flag
+ */
+ protected void setRightDirty(boolean dirty) {
+ if (isRightDirty() != dirty) {
+ fIsRightDirty = dirty;
+ // Always fire the event if the dirty state has changed
+ fireDirtyState(dirty);
+ }
+ }
+
+ /**
+ * Method from the old internal <code>ISavable</code> interface
+ * Save the viewers's content.
+ * Note: this method is for internal use only. Clients should not call this method.
+ *
+ * @param monitor a progress monitor
+ * @throws CoreException
+ * @deprecated use {@link IFlushable#flush(IProgressMonitor)}.
+ */
+ @Deprecated
+ public void save(IProgressMonitor monitor) throws CoreException {
+ flush(monitor);
+ }
+
+ /**
+ * Flush any modifications made in the viewer into the compare input. This method
+ * calls {@link #flushContent(Object, IProgressMonitor)} with the compare input
+ * of the viewer as the first parameter.
+ *
+ * @param monitor a progress monitor
+ * @see org.eclipse.compare.contentmergeviewer.IFlushable#flush(org.eclipse.core.runtime.IProgressMonitor)
+ * @since 3.3
+ */
+ @Override
+ public final void flush(IProgressMonitor monitor) {
+ flushContent(getInput(), monitor);
+ }
+
+ /**
+ * Flushes the modified content back to input elements via the content provider.
+ * The provided input may be the current input of the viewer or it may be
+ * the previous input (i.e. this method may be called to flush modified content
+ * during an input change).
+ *
+ * @param input the compare input
+ * @param monitor a progress monitor or <code>null</code> if the method
+ * was call from a place where a progress monitor was not available.
+ * @since 3.3
+ */
+ protected void flushContent(Object input, IProgressMonitor monitor) {
+ flushLeftSide(input, monitor);
+ flushRightSide(input, monitor);
+ }
+
+ void flushLeftSide(Object input, IProgressMonitor monitor) {
+ IMergeViewerContentProvider content = (IMergeViewerContentProvider) getContentProvider();
+
+ boolean rightEmpty = content.getRightContent(input) == null;
+
+ if (getCompareConfiguration().isLeftEditable() && isLeftDirty()) {
+ byte[] bytes = getContents(true);
+ if (rightEmpty && bytes != null && bytes.length == 0)
+ bytes = null;
+ setLeftDirty(false);
+ content.saveLeftContent(input, bytes);
+ }
+ }
+
+ void flushRightSide(Object input, IProgressMonitor monitor) {
+ IMergeViewerContentProvider content = (IMergeViewerContentProvider) getContentProvider();
+
+ boolean leftEmpty = content.getLeftContent(input) == null;
+
+ if (getCompareConfiguration().isRightEditable() && isRightDirty()) {
+ byte[] bytes = getContents(false);
+ if (leftEmpty && bytes != null && bytes.length == 0)
+ bytes = null;
+ setRightDirty(false);
+ content.saveRightContent(input, bytes);
+ }
+ }
+
+ /**
+ * @param monitor
+ * @noreference This method is not intended to be referenced by clients.
+ */
+ @Override
+ public void flushLeft(IProgressMonitor monitor) {
+ flushLeftSide(getInput(), monitor);
+ }
+
+ /**
+ * @param monitor
+ * @noreference This method is not intended to be referenced by clients.
+ */
+ @Override
+ public void flushRight(IProgressMonitor monitor) {
+ flushRightSide(getInput(), monitor);
+ }
+
+ /**
+ * Return the dirty state of the right side of this viewer.
+ * @return the dirty state of the right side of this viewer
+ * @since 3.3
+ */
+ protected boolean isRightDirty() {
+ return fIsRightDirty;
+ }
+
+ /**
+ * @return the dirty state of the right side of this viewer
+ * @since 3.7
+ * @noreference This method is not intended to be referenced by clients.
+ */
+ public boolean internalIsRightDirty() {
+ return isRightDirty();
+ }
+
+ /**
+ * Return the dirty state of the left side of this viewer.
+ * @return the dirty state of the left side of this viewer
+ * @since 3.3
+ */
+ protected boolean isLeftDirty() {
+ return fIsLeftDirty;
+ }
+
+ /**
+ * @return the dirty state of the left side of this viewer
+ * @since 3.7
+ * @noreference This method is not intended to be referenced by clients.
+ */
+ public boolean internalIsLeftDirty() {
+ return isLeftDirty();
+ }
+
+ /**
+ * Handle a change to the given input reported from an {@link org.eclipse.compare.structuremergeviewer.ICompareInputChangeListener}.
+ * This class registers a listener with its input and reports any change events through
+ * this method. By default, this method prompts for any unsaved changes and then refreshes
+ * the viewer. Subclasses may override.
+ * @since 3.3
+ */
+ protected void handleCompareInputChange() {
+ // Before setting the new input we have to save the old.
+ Object input = getInput();
+ if (!isSaving() && (isLeftDirty() || isRightDirty())) {
+
+ if (Utilities.RUNNING_TESTS) {
+ if (Utilities.TESTING_FLUSH_ON_COMPARE_INPUT_CHANGE) {
+ flushContent(input, null);
+ }
+ } else {
+ // post alert
+ Shell shell= fComposite.getShell();
+
+ MessageDialog dialog= new MessageDialog(shell,
+ CompareMessages.ContentMergeViewer_resource_changed_title,
+ null, // accept the default window icon
+ CompareMessages.ContentMergeViewer_resource_changed_description,
+ MessageDialog.QUESTION,
+ new String[] {
+ IDialogConstants.YES_LABEL, // 0
+ IDialogConstants.NO_LABEL, // 1
+ },
+ 0); // default button index
+
+ switch (dialog.open()) { // open returns index of pressed button
+ case 0:
+ flushContent(input, null);
+ break;
+ case 1:
+ setLeftDirty(false);
+ setRightDirty(false);
+ break;
+ }
+ }
+ }
+ if (isSaving() && (isLeftDirty() || isRightDirty())) {
+ return; // Do not refresh until saving both sides is complete.
+ }
+ refresh();
+ }
+
+ CompareHandlerService getCompareHandlerService() {
+ return fHandlerService;
+ }
+
+ /**
+ * @return true if any of the Saveables is being saved
+ */
+ private boolean isSaving() {
+ ICompareContainer container = fCompareConfiguration.getContainer();
+ ISaveablesSource source = null;
+ if (container instanceof ISaveablesSource) {
+ source = (ISaveablesSource) container;
+ } else {
+ IWorkbenchPart part = container.getWorkbenchPart();
+ if (part instanceof ISaveablesSource) {
+ source = (ISaveablesSource) part;
+ }
+ }
+ if (source != null) {
+ Saveable[] saveables = source.getSaveables();
+ for (int i = 0; i < saveables.length; i++) {
+ if (saveables[i] instanceof ISavingSaveable) {
+ ISavingSaveable saveable = (ISavingSaveable) saveables[i];
+ if (saveable.isSaving())
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ /**
+ * If the inputs are mirrored, this asks the right model value.
+ *
+ * @return true if the left viewer is editable
+ * @since 3.7
+ */
+ protected boolean isLeftEditable() {
+ return fCompareConfiguration.isMirrored() ? fCompareConfiguration.isRightEditable() : fCompareConfiguration.isLeftEditable();
+ }
+
+ /**
+ * If the inputs are mirrored, this asks the left model value.
+ *
+ * @return true if the right viewer is editable
+ * @since 3.7
+ */
+ protected boolean isRightEditable() {
+ return fCompareConfiguration.isMirrored() ? fCompareConfiguration.isLeftEditable() : fCompareConfiguration.isRightEditable();
+ }
+}

Back to the top