Package org.eclipse.php.internal.ui.compare

Source Code of org.eclipse.php.internal.ui.compare.ContentMergeViewer$Resizer

/*******************************************************************************
* Copyright (c) 2009 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
*     Zend Technologies
*******************************************************************************/

package org.eclipse.php.internal.ui.compare;

import java.util.ResourceBundle;

import org.eclipse.compare.*;
import org.eclipse.compare.contentmergeviewer.IFlushable;
import org.eclipse.compare.contentmergeviewer.IMergeViewerContentProvider;
import org.eclipse.compare.contentmergeviewer.TextMergeViewer;
import org.eclipse.compare.internal.*;
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.Separator;
import org.eclipse.jface.action.ToolBarManager;
import org.eclipse.jface.dialogs.IDialogConstants;
import org.eclipse.jface.dialogs.MessageDialog;
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.php.internal.ui.PHPUIMessages;
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.*;

/**
* 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 {
  // https://bugs.eclipse.org/bugs/show_bug.cgi?id=330672
  org.eclipse.compare.contentmergeviewer.ContentMergeViewer cmv;

  class SaveAction extends MergeViewerAction {

    SaveAction(boolean left) {
      super(true, false, false);

      setText(PHPUIMessages.ContentMergeViewer_0);
      setToolTipText(PHPUIMessages.ContentMergeViewer_1);
      setDescription(PHPUIMessages.ContentMergeViewer_2);
    }

    public void run() {
      flush(null);
    }
  }

  /* 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 {

    public Point computeSize(Composite c, int w, int h, boolean force) {
      return new Point(100, 100);
    }

    public void layout(Composite composite, boolean force) {

      // 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(new DisposeListener() {
        public void widgetDisposed(DisposeEvent e) {
          fControl = null;
        }
      });
    }

    public void mouseDoubleClick(MouseEvent e) {
      if ((fDirection & HORIZONTAL) != 0)
        fHSplit = -1;
      if ((fDirection & VERTICAL) != 0)
        fVSplit = VSPLIT;
      fComposite.layout(true);
    }

    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;
    }

    public void mouseUp(MouseEvent e) {
      fIsDown = false;
      if (!fLiveResize)
        resize(e);
    }

    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 ICompareInputChangeListener fCompareInputChangeListener;
  private ListenerList 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 Action fCopyLeftToRightAction; // copy from left to right
  private Action fCopyRightToLeftAction; // copy from right to left

  MergeViewerAction fLeftSaveAction;
  MergeViewerAction fRightSaveAction;

  private CompareHandlerService fHandlerService;

  // SWT widgets
  /* package */Composite fComposite;
  private CLabel fAncestorLabel;
  private CLabel fLeftLabel;
  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 = new ILabelProviderListener() {
    public void labelProviderChanged(LabelProviderChangedEvent 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) {

    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);

    setContentProvider(new MergeViewerContentProvider(cc));

    fCompareInputChangeListener = new ICompareInputChangeListener() {
      public void compareInputChanged(ICompareInput input) {
        if (input == getInput()) {
          handleCompareInputChange();
        }
      }
    };

    // Make sure the compare configuration is not null
    if (cc == null)
      fCompareConfiguration = new CompareConfiguration();
    else
      fCompareConfiguration = cc;
    fPropertyChangeListener = new IPropertyChangeListener() {
      public void propertyChange(PropertyChangeEvent event) {
        ContentMergeViewer.this.handlePropertyChangeEvent(event);
      }
    };
    fCompareConfiguration
        .addPropertyChangeListener(fPropertyChangeListener);

    fLeftSaveAction = new SaveAction(true);
    fLeftSaveAction.setEnabled(false);
    fRightSaveAction = new SaveAction(false);
    fRightSaveAction.setEnabled(false);

    // this is used to update the dirty status,if we use
    // org.eclipse.php.internal.ui.compare.ContentMergeViewer,we will get a
    // ClassCastException
    cmv = new org.eclipse.compare.contentmergeviewer.ContentMergeViewer(
        fStyles, fBundle, fCompareConfiguration) {

      @Override
      protected void createControls(Composite composite) {

      }

      @Override
      protected void handleResizeAncestor(int x, int y, int width,
          int height) {

      }

      @Override
      protected void handleResizeLeftRight(int x, int y, int leftWidth,
          int centerWidth, int rightWidth, int height) {

      }

      @Override
      protected void updateContent(Object ancestor, Object left,
          Object right) {

      }

      @Override
      protected void copy(boolean leftToRight) {

      }

      @Override
      protected byte[] getContents(boolean left) {
        return null;
      }

      @Override
      public boolean internalIsLeftDirty() {
        return ContentMergeViewer.this.isLeftDirty();
      }

      @Override
      public boolean internalIsRightDirty() {
        return ContentMergeViewer.this.isRightDirty();
      }
    };
  }

  // ---- 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, or <code>null</code> if
   * this viewer does not yet have a configuration.
   *
   * @return the compare configuration, or <code>null</code> if none
   */
  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.
   */
  public void setContentProvider(IContentProvider contentProvider) {
    Assert.isTrue(contentProvider instanceof IMergeViewerContentProvider);
    super.setContentProvider(contentProvider);
  }

  /* 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.
   */
  public ISelection getSelection() {
    return new ISelection() {
      public boolean isEmpty() {
        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)
   */
  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,
            new Boolean(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
   */
  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;
  }

  /*
   * (non-Javadoc)
   *
   * @see org.eclipse.jface.viewers.Viewer#refresh()
   */
  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();
      ToolBarManager tbm = CompareViewerPane.getToolBarManager(fComposite
          .getParent());
      if (tbm != null) {
        updateToolItems();
        tbm.update(true);
        tbm.getControl().getParent().layout(true);
      }

    }
  }

  // ---- 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
      public boolean setFocus() {
        return ContentMergeViewer.this.handleSetFocus();
      }
    };
    fComposite.setData(CompareUI.COMPARE_VIEWER_TITLE, getTitle());

    hookControl(fComposite); // hook help & dispose listener

    fComposite.setLayout(new ContentMergeViewerLayout());

    int style = SWT.SHADOW_OUT;
    fAncestorLabel = new CLabel(fComposite, style
        | Window.getDefaultOrientation());

    fLeftLabel = new CLabel(fComposite, style
        | Window.getDefaultOrientation());
    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;
  }

  private void initializeToolbars(Composite parent) {
    ToolBarManager tbm = CompareViewerPane.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$

      CompareConfiguration cc = getCompareConfiguration();

      if (cc.isRightEditable()) {
        fCopyLeftToRightAction = new Action() {
          public void run() {
            copy(true);
          }
        };
        Utilities.initAction(fCopyLeftToRightAction,
            getResourceBundle(), "action.CopyLeftToRight."); //$NON-NLS-1$
        tbm.appendToGroup("merge", fCopyLeftToRightAction); //$NON-NLS-1$
        fHandlerService.registerAction(fCopyLeftToRightAction,
            "org.eclipse.compare.copyAllLeftToRight"); //$NON-NLS-1$
      }

      if (cc.isLeftEditable()) {
        fCopyRightToLeftAction = new Action() {
          public void run() {
            copy(false);
          }
        };
        Utilities.initAction(fCopyRightToLeftAction,
            getResourceBundle(), "action.CopyRightToLeft."); //$NON-NLS-1$
        tbm.appendToGroup("merge", fCopyRightToLeftAction); //$NON-NLS-1$
        fHandlerService.registerAction(fCopyRightToLeftAction,
            "org.eclipse.compare.copyAllRightToLeft"); //$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);
    }
  }

  /**
   * 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;
  }

  /*
   * (non-Javadoc)
   *
   * @see org.eclipse.jface.viewers.Viewer#getControl()
   */
  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)
   */
  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;
    }

    fAncestorLabel = null;
    fLeftLabel = null;
    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 (fCopyLeftToRightAction != null) {
      boolean enable = content.isRightEditable(input);
      // if (enable && input instanceof ICompareInput) {
      // ITypedElement e= ((ICompareInput) input).getLeft();
      // if (e == null)
      // enable= false;
      // }
      fCopyLeftToRightAction.setEnabled(enable);
    }

    if (fCopyRightToLeftAction != null) {
      boolean enable = content.isLeftEditable(input);
      // if (enable && input instanceof ICompareInput) {
      // ITypedElement e= ((ICompareInput) input).getRight();
      // if (e == null)
      // enable= false;
      // }
      fCopyRightToLeftAction.setEnabled(enable);
    }
  }

  /**
   * 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(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(TextProcessor.process(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(TextProcessor.process(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

  /*
   * (non-Javadoc)
   *
   * @see
   * org.eclipse.compare.IPropertyChangeNotifier#addPropertyChangeListener
   * (org.eclipse.jface.util.IPropertyChangeListener)
   */
  public void addPropertyChangeListener(IPropertyChangeListener listener) {
    if (fListenerList == null)
      fListenerList = new ListenerList();
    fListenerList.add(listener);
  }

  /*
   * (non-Javadoc)
   *
   * @see
   * org.eclipse.compare.IPropertyChangeNotifier#removePropertyChangeListener
   * (org.eclipse.jface.util.IPropertyChangeListener)
   */
  public void removePropertyChangeListener(IPropertyChangeListener listener) {
    if (fListenerList != null) {
      fListenerList.remove(listener);
      if (fListenerList.isEmpty())
        fListenerList = null;
    }
  }

  private void fireDirtyState(boolean state) {
    Utilities.firePropertyChange(fListenerList, cmv,
        CompareEditorInput.DIRTY_STATE, null, new Boolean(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) {
      fLeftSaveAction.setEnabled(dirty);
      // Only fire the event if the combined dirty state has changed
      if ((!isRightDirty() && !isLeftDirty())
          || (!isRightDirty() && isLeftDirty()))
        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) {
      fRightSaveAction.setEnabled(dirty);
      // Only fire the event if the combined dirty state has changed
      if ((!isRightDirty() && !isLeftDirty())
          || (isRightDirty() && !isLeftDirty()))
        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)}.
   */
  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
   */
  public final void flush(IProgressMonitor monitor) {
    flushContent(getInput(), monitor);
  }

  /**
   * Flush 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 flushContentOld(Object input, IProgressMonitor monitor) {

    // write back modified contents
    IMergeViewerContentProvider content = (IMergeViewerContentProvider) getContentProvider();

    boolean leftEmpty = content.getLeftContent(input) == null;
    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);
    }

    if (getCompareConfiguration().isRightEditable() && isRightDirty()) {
      byte[] bytes = getContents(false);
      if (leftEmpty && bytes != null && bytes.length == 0)
        bytes = null;
      setRightDirty(false);
      content.saveRightContent(input, bytes);
    }
  }

  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.
   */
  public void flushLeft(IProgressMonitor monitor) {
    flushLeftSide(getInput(), monitor);
  }

  /**
   * @param monitor
   * @noreference This method is not intended to be referenced by clients.
   */
  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 fRightSaveAction.isEnabled();
  }

  /**
   * 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 fLeftSaveAction.isEnabled();
  }

  /**
   * 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. Subclasses may override.
   *
   * @since 3.3
   */
  protected void handleCompareInputChange() {
    // before setting the new input we have to save the old
    Object input = getInput();
    if (isLeftDirty() || isRightDirty()) {
      flushContent(input, null);
    }
    refresh();
  }
}
TOP

Related Classes of org.eclipse.php.internal.ui.compare.ContentMergeViewer$Resizer

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.