Skip to main content
summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
Diffstat (limited to 'bundles/org.eclipse.compare/compare/org/eclipse/compare/contentmergeviewer/TextMergeViewer.java')
-rw-r--r--bundles/org.eclipse.compare/compare/org/eclipse/compare/contentmergeviewer/TextMergeViewer.java2372
1 files changed, 2372 insertions, 0 deletions
diff --git a/bundles/org.eclipse.compare/compare/org/eclipse/compare/contentmergeviewer/TextMergeViewer.java b/bundles/org.eclipse.compare/compare/org/eclipse/compare/contentmergeviewer/TextMergeViewer.java
new file mode 100644
index 000000000..86111de15
--- /dev/null
+++ b/bundles/org.eclipse.compare/compare/org/eclipse/compare/contentmergeviewer/TextMergeViewer.java
@@ -0,0 +1,2372 @@
+/*
+ * Licensed Materials - Property of IBM,
+ * WebSphere Studio Workbench
+ * (c) Copyright IBM Corp 2000, 2001
+ */
+package org.eclipse.compare.contentmergeviewer;
+
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.HashMap;
+import java.util.ResourceBundle;
+import java.io.InputStream;
+import java.io.IOException;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.*;
+import org.eclipse.swt.graphics.GC;
+import org.eclipse.swt.graphics.RGB;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.widgets.*;
+import org.eclipse.swt.custom.*;
+
+import org.eclipse.jface.action.*;
+import org.eclipse.jface.text.*;
+import org.eclipse.jface.text.source.SourceViewer;
+import org.eclipse.jface.util.PropertyChangeEvent;
+import org.eclipse.jface.viewers.ISelection;
+
+import org.eclipse.core.runtime.CoreException;
+
+import org.eclipse.ui.texteditor.IUpdate;
+
+import org.eclipse.compare.*;
+import org.eclipse.compare.internal.MergeSourceViewer;
+import org.eclipse.compare.internal.BufferedCanvas;
+import org.eclipse.compare.internal.Utilities;
+import org.eclipse.compare.internal.TokenComparator;
+import org.eclipse.compare.internal.ChangePropertyAction;
+import org.eclipse.compare.rangedifferencer.RangeDifference;
+import org.eclipse.compare.rangedifferencer.RangeDifferencer;
+import org.eclipse.compare.internal.DocLineComparator;
+
+
+/**
+ * A text merge viewer uses the <code>RangeDifferencer</code> to perform a
+ * textual, line-by-line comparison of two (or three) input documents.
+ * It is based on the <code>ContentMergeViewer</code> and uses <code>TextViewer</code>s
+ * to implement the ancestor, left, and right content areas.
+ * <p>
+ * In the three-way compare case ranges of differing lines are highlighted and framed
+ * with different colors to show whether the difference is an incoming, outgoing, or conflicting change.
+ * The <code>TextMergeViewer</code> supports the notion of a current "differing range"
+ * and provides toolbar buttons to navigate from one range to the next (or previous).
+ * <p>
+ * If there is a current "differing range" and the underlying document is editable
+ * the <code>TextMergeViewer</code> enables actions in context menu and toolbar to
+ * copy a range from one side to the other side, thereby performing a merge operation.
+ * <p>
+ * In addition to a line-by-line comparison the <code>TextMergeViewer</code>
+ * uses a token based compare on differing lines.
+ * The token compare is activated when navigating into
+ * a range of differing lines. At first the lines are selected as a block.
+ * When navigating into this block the token compare shows for every line
+ * the differing token by selecting them.
+ * <p>
+ * The <code>TextMergeViewer</code>'s default token compare works on characters separated
+ * by whitespace. If a different strategy is needed (for example, Java tokens in
+ * a Java-aware merge viewer), clients can create their own token
+ * comparators by implementing the <code>ITokenComparator</code> interface and overriding the
+ * <code>TextMergeViewer.createTokenComparator</code> factory method).
+ * <p>
+ * Access to the <code>TextMergeViewer</code>'s model is by means of an
+ * <code>IMergeViewerContentProvider</code>. Its <code>get<it>X</it></code>Content</code> methods must return
+ * either an <code>IDocument</code>, an <code>IDocumentRange</code>, or an <code>IStreamContentAccessor</code>.
+ * In the <code>IDocumentRange</code> case the <code>TextMergeViewer</code>
+ * works on a subrange of a document. In the <code>IStreamContentAccessor</code> case
+ * a document is created internally and initialized from the stream.
+ * <p>
+ * A <code>TextMergeViewer</code> can be used as is. However clients may subclass
+ * to customize the behavior. For example a <code>MergeTextViewer</code> for Java would override
+ * the <code>configureTextViewer</code> method to configure the <code>TextViewer</code> for Java source code,
+ * the <code>createTokenComparator</code> method to create a Java specific tokenizer.
+ *
+ * @see org.eclipse.compare.rangedifferencer.RangeDifferencer
+ * @see org.eclipse.jface.text.TextViewer
+ * @see ITokenComparator
+ * @see IDocumentRange
+ * @see org.eclipse.compare.IStreamContentAccessor
+ */
+public class TextMergeViewer extends ContentMergeViewer {
+
+ static class TextMergeAction extends Action implements IUpdate {
+
+ int fOperationCode;
+ ITextOperationTarget fTarget;
+
+ TextMergeAction(ResourceBundle bundle, String key, ITextOperationTarget target, int operationCode) {
+ Utilities.initAction(this, bundle, key);
+ fTarget= target;
+ fOperationCode= operationCode;
+ update();
+ }
+
+ public void run() {
+ if (fOperationCode != -1 && fTarget.canDoOperation(fOperationCode))
+ fTarget.doOperation(fOperationCode);
+ }
+
+ public boolean isEnabled() {
+ return fTarget.canDoOperation(fOperationCode);
+ }
+
+ public void update() {
+ setEnabled(isEnabled());
+ }
+ }
+
+ private static final String MY_UPDATER= "my_updater";
+
+ private static final String SYNC_SCROLLING= "SYNC_SCROLLING";
+
+ private static final String BUNDLE_NAME= "org.eclipse.compare.contentmergeviewer.TextMergeViewerResources";
+
+ // constants
+ /** Width of left and right vertical bar */
+ private static final int MARGIN_WIDTH= 10;
+ /** Width of center bar */
+ private static final int CENTER_WIDTH= 40;
+ /** */
+ private static final int LW= 1;
+ /** Provide more merge controls in Pane toolbar */
+ private static final boolean USE_MORE_CONTROLS= true;
+ /** Selects between smartTokenDiff and mergingTokenDiff */
+ private static final boolean USE_MERGING_TOKEN_DIFF= false;
+ /** if DEAD_STEP is true navigation with the next/previous buttons needs an extra step
+ when wrapping around the beginning or end */
+ private static final boolean DEAD_STEP= false;
+
+ private static final boolean IS_MOTIF= false;
+
+ // Colors to use
+ private static final RGB INCOMING= new RGB(100, 100, 200);
+ private static final RGB INCOMING_FILL= new RGB(230, 230, 240);
+ private static final RGB SELECTED_INCOMING= new RGB(0, 0, 255);
+ private static final RGB SELECTED_INCOMING_FILL= new RGB(255, 255, 255);
+
+ private static final RGB CONFLICT= new RGB(200, 100, 100);
+ private static final RGB CONFLICT_FILL= new RGB(240, 230, 230);
+ private static final RGB SELECTED_CONFLICT= new RGB(255, 0, 0);
+ private static final RGB SELECTED_CONFLICT_FILL= new RGB(255, 255, 255);
+
+ private static final RGB OUTGOING= new RGB(100, 100, 100);
+ private static final RGB OUTGOING_FILL= new RGB(230, 230, 230);
+ private static final RGB SELECTED_OUTGOING= new RGB(0, 0, 0);
+ private static final RGB SELECTED_OUTGOING_FILL= new RGB(255, 255, 255);
+
+ private IDocumentListener fDocumentListener;
+
+ /** All diffs for calculating scrolling position (includes line ranges without changes) */
+ private ArrayList fAllDiffs;
+ /** Subset of above: just real differences. */
+ private ArrayList fChangeDiffs;
+ /** The current diff */
+ private Diff fCurrentDiff;
+
+ private MergeSourceViewer fAncestor;
+ private MergeSourceViewer fLeft;
+ private MergeSourceViewer fRight;
+
+ private int fLeftLineCount;
+ private int fRightLineCount;
+
+ private boolean fLeftContentsChanged;
+ private boolean fRightContentsChanged;
+
+ private boolean fInScrolling;
+
+ private int fPts[]= new int[8]; // scratch area for polygon drawing
+
+ private ActionContributionItem fNextItem; // goto next difference
+ private ActionContributionItem fPreviousItem; // goto previous difference
+ private ActionContributionItem fCopyDiffLeftToRightItem;
+ private ActionContributionItem fCopyDiffRightToLeftItem;
+
+ private boolean fSynchronizedScrolling= true;
+
+ private MergeSourceViewer fFocusPart;
+
+ private boolean fSubDoc= true;
+ private IPositionUpdater fPositionUpdater;
+
+
+ // SWT widgets
+ private BufferedCanvas fAncestorCanvas;
+ private BufferedCanvas fLeftCanvas;
+ private BufferedCanvas fRightCanvas;
+ private ScrollBar fVScrollBar;
+
+ // SWT resources to be disposed
+ private Map fColors;
+
+
+ /**
+ * A Diff represents synchronized character ranges in two or three Documents.
+ * The MergeTextViewer uses Diffs to find differences in line and token ranges.
+ */
+ /* package */ class Diff {
+ /** character range in ancestor document */
+ Position fAncestorPos;
+ /** character range in left document */
+ Position fLeftPos;
+ /** character range in right document */
+ Position fRightPos;
+ /** if this is a TokenDiff fParent points to the enclosing LineDiff */
+ Diff fParent;
+ /** if Diff has been resolved */
+ boolean fResolved;
+ int fDirection;
+ boolean fIsToken= false;
+ ArrayList fDiffs;
+
+ /**
+ * Create Diff from two ranges and an optional parent diff.
+ */
+ Diff(Diff parent, int dir, IDocument ancestorDoc, int ancestorStart, int ancestorEnd,
+ IDocument leftDoc, int leftStart, int leftEnd,
+ IDocument rightDoc, int rightStart, int rightEnd) {
+ fParent= parent != null ? parent : this;
+ fDirection= dir;
+
+ fLeftPos= createPosition(leftDoc, leftStart, leftEnd);
+ fRightPos= createPosition(rightDoc, rightStart, rightEnd);
+ if (ancestorDoc != null)
+ fAncestorPos= createPosition(ancestorDoc, ancestorStart, ancestorEnd);
+ }
+
+ Position createPosition(IDocument doc, int start, int end) {
+ try {
+ int dl= doc.getLength();
+ int l= end-start;
+ if (start+l > dl)
+ l= dl-start;
+
+ Position p= null;
+ try {
+ p= new Position(start, l);
+ } catch (RuntimeException ex) {
+ System.out.println("Diff.createPosition: " + start + " " + l);
+ }
+
+ try {
+ doc.addPosition(MY_UPDATER, p);
+ } catch (BadPositionCategoryException ex) {
+ }
+ return p;
+ } catch (BadLocationException ee) {
+ //System.out.println("Diff.createPosition: " + start + " " + end);
+ }
+ return null;
+ }
+
+ void add(Diff d) {
+ if (fDiffs == null)
+ fDiffs= new ArrayList();
+ fDiffs.add(d);
+ }
+
+ boolean isDeleted() {
+ if (fAncestorPos != null && fAncestorPos.isDeleted())
+ return true;
+ return fLeftPos.isDeleted() || fRightPos.isDeleted();
+ }
+
+ void setResolved(boolean r) {
+ fResolved= r;
+ if (r)
+ fDiffs= null;
+ }
+
+ boolean isResolved() {
+ if (!fResolved && fDiffs != null) {
+ Iterator e= fDiffs.iterator();
+ while (e.hasNext()) {
+ Diff d= (Diff) e.next();
+ if (!d.isResolved())
+ return false;
+ }
+ return true;
+ }
+ return fResolved;
+ }
+
+ Position getPosition(MergeSourceViewer w) {
+ if (w == fLeft)
+ return fLeftPos;
+ if (w == fRight)
+ return fRightPos;
+ if (w == fAncestor)
+ return fAncestorPos;
+ return null;
+ }
+
+ /**
+ * Returns true if given character range overlaps with this Diff.
+ */
+ boolean contains(MergeSourceViewer w, int start, int end) {
+ Position h= getPosition(w);
+ if (h != null) {
+ int offset= h.getOffset();
+ if (start >= offset) {
+ int endPos= offset+h.getLength();
+ if (end < endPos)
+ return true;
+ if (endPos == w.getDocument().getLength())
+ return true;
+ }
+ }
+ return false;
+ }
+
+ int getMaxDiffHeight(boolean withAncestor) {
+ Point region= new Point(0, 0);
+ int h= fLeft.getLineRange(fLeftPos, region).y;
+ if (withAncestor)
+ h= Math.max(h, fAncestor.getLineRange(fAncestorPos, region).y);
+ return Math.max(h, fRight.getLineRange(fRightPos, region).y);
+ }
+ }
+
+ //---- MergeTextViewer
+
+ /**
+ * Creates a text merge viewer under the given parent control.
+ *
+ * @param parent the parent control
+ * @param configuration the configuration object
+ */
+ public TextMergeViewer(Composite parent, CompareConfiguration configuration) {
+ this(parent, SWT.NULL, configuration);
+ }
+
+ /**
+ * Creates a text merge viewer under the given parent control.
+ *
+ * @param parent the parent control
+ * @param style SWT style bits for top level composite of this viewer
+ * @param configuration the configuration object
+ */
+ public TextMergeViewer(Composite parent, int style, CompareConfiguration configuration) {
+ super(style, ResourceBundle.getBundle(BUNDLE_NAME), configuration);
+
+ buildControl(parent);
+
+ fSynchronizedScrolling= Utilities.getBoolean(configuration, SYNC_SCROLLING, fSynchronizedScrolling);
+
+ fDocumentListener= new IDocumentListener() {
+
+ public void documentAboutToBeChanged(DocumentEvent e) {
+ }
+
+ public void documentChanged(DocumentEvent e) {
+ TextMergeViewer.this.documentChanged(e);
+ }
+ };
+ }
+
+ public String getTitle() {
+ return "Text Compare";
+ }
+
+ /**
+ * Configures the passed text viewer.
+ * This method is called after the three text viewers have been created for the
+ * content areas.
+ * The <code>TextMergeViewer</code> implementation of this method does nothing.
+ * Subclasses may reimplement to provide a specific configuration for the text viewer.
+ *
+ * @param textViewer the text viewer to configure
+ */
+ protected void configureTextViewer(TextViewer textViewer) {
+ }
+
+ /**
+ * Creates an <code>ITokenComparator</code> which is used to show the
+ * intra line differences.
+ * The <code>TextMergeViewer</code> implementation of this method returns a
+ * tokenizer that breaks a line into words separated by whitespace.
+ * Subclasses may reimplement to provide a specific tokenizer.
+ *
+ * @return a ITokenComparator which is used for a second level token compare.
+ */
+ protected ITokenComparator createTokenComparator(String s) {
+ return new TokenComparator(s);
+ }
+
+ /**
+ * Returns a document partitioner which is suitable for the underlying content type.
+ * This method is only called if the input provided by the content provider is a
+ * <code>IStreamContentAccessor</code> and an internal document must be created. This
+ * document is initialized with the partitioner returned from this method.
+ * <p>
+ * The <code>TextMergeViewer</code> implementation of this method returns
+ * <code>null</code>. Subclasses may reimplement to create a partitioner for a
+ * specific content type.
+ *
+ * @return a document partitioner, or <code>null</code>
+ */
+ protected IDocumentPartitioner getDocumentPartitioner() {
+ return null;
+ }
+
+ /**
+ * Called on the viewer disposal.
+ * Unregisters from the compare configuration.
+ * Clients may extend if they have to do additional cleanup.
+ */
+ protected void handleDispose(DisposeEvent event) {
+
+ fLeftCanvas= null;
+ fRightCanvas= null;
+ fVScrollBar= null;
+
+ unsetDocument(fAncestor);
+ unsetDocument(fLeft);
+ unsetDocument(fRight);
+
+ if (fColors != null) {
+ Iterator i= fColors.values().iterator();
+ while (i.hasNext()) {
+ Color color= (Color) i.next();
+ if (!color.isDisposed())
+ color.dispose();
+ }
+ }
+
+ super.handleDispose(event);
+ }
+
+ //-------------------------------------------------------------------------------------------------------------
+ //--- internal ------------------------------------------------------------------------------------------------
+ //-------------------------------------------------------------------------------------------------------------
+
+ /**
+ * Creates the specific SWT controls for the content areas.
+ * Clients must not call or override this method.
+ */
+ protected void createControls(Composite composite) {
+
+ // 1st row
+ fAncestorCanvas= new BufferedCanvas(composite, SWT.NONE) {
+ public void doPaint(GC gc) {
+ paintSides(gc, fAncestor, fAncestorCanvas, false);
+ }
+ };
+
+ fAncestor= createPart(composite);
+ fAncestor.setEditable(false);
+
+ // 2nd row
+ fLeftCanvas= new BufferedCanvas(composite, SWT.NONE) {
+ public void doPaint(GC gc) {
+ paintSides(gc, fLeft, fLeftCanvas, false);
+ }
+ };
+
+ fLeft= createPart(composite);
+ fLeft.getTextWidget().getVerticalBar().setVisible(false);
+
+ fRight= createPart(composite);
+ fRight.getTextWidget().getVerticalBar().setVisible(false);
+
+ fRightCanvas= new BufferedCanvas(composite, SWT.V_SCROLL) {
+ public void doPaint(GC gc) {
+ paintSides(gc, fRight, fRightCanvas, fSynchronizedScrolling);
+ }
+ };
+
+ fVScrollBar= fRightCanvas.getVerticalBar();
+ fVScrollBar.setIncrement(1);
+ fVScrollBar.setVisible(true);
+ fVScrollBar.addListener(SWT.Selection,
+ new Listener() {
+ public void handleEvent(Event e) {
+ scrollVertical(((ScrollBar)e.widget).getSelection(), null);
+ }
+ }
+ );
+ }
+
+ /* package */ boolean internalSetFocus() {
+ if (fFocusPart != null) {
+ StyledText st= fFocusPart.getTextWidget();
+ if (st != null)
+ return st.setFocus();
+ }
+ return false; // could not set focus
+ }
+
+ /**
+ * Creates the central Canvas.
+ * Called from ContentMergeViewer.
+ */
+ /* package */ Control createCenter(Composite parent) {
+ if (fSynchronizedScrolling) {
+ final Canvas canvas= new BufferedCanvas(parent, SWT.NONE) {
+ public void doPaint(GC gc) {
+ paintCenter(this, gc);
+ }
+ };
+ new Resizer(canvas, HORIZONTAL);
+ return canvas;
+ }
+ return super.createCenter(parent);
+ }
+
+ /**
+ * Returns width of central canvas.
+ * Overridden from ContentMergeViewer.
+ */
+ /* package */ int getCenterWidth() {
+ if (fSynchronizedScrolling)
+ return CENTER_WIDTH;
+ return super.getCenterWidth();
+ }
+
+ /**
+ * Creates and initializes a text part.
+ */
+ private MergeSourceViewer createPart(Composite parent) {
+
+ final MergeSourceViewer part= new MergeSourceViewer(parent);
+ final StyledText te= part.getTextWidget();
+
+ te.addPaintListener(
+ new PaintListener() {
+ public void paintControl(PaintEvent e) {
+ paint(e, part);
+ }
+ }
+ );
+ te.addKeyListener(
+ new KeyAdapter() {
+ public void keyPressed(KeyEvent e) {
+ handleSelectionChanged(part);
+ }
+ }
+ );
+ te.addMouseListener(
+ new MouseAdapter() {
+ public void mouseDown(MouseEvent e) {
+ //syncViewport(part);
+ handleSelectionChanged(part);
+ }
+ }
+ );
+
+ te.addFocusListener(
+ new FocusAdapter() {
+ public void focusGained(FocusEvent fe) {
+ fFocusPart= part;
+ }
+ }
+ );
+
+ MenuManager mm= new MenuManager();
+ mm.setRemoveAllWhenShown(true);
+ mm.addMenuListener(
+ new IMenuListener() {
+ public void menuAboutToShow(IMenuManager mm) {
+ fillContextMenu(mm, part);
+ }
+ }
+ );
+ te.setMenu(mm.createContextMenu(te));
+
+ part.addViewportListener(
+ new IViewportListener() {
+ public void viewportChanged(int verticalPosition) {
+ syncViewport(part);
+ }
+ }
+ );
+
+ configureTextViewer(part);
+
+ return part;
+ }
+
+ /**
+ * Allows the viewer to add menus and/or tools to the context menu.
+ */
+ private void fillContextMenu(IMenuManager menu, MergeSourceViewer part) {
+
+ ResourceBundle rb= getResourceBundle();
+ boolean mutable= part.isEditable();
+
+ menu.add(new Separator("undo"));
+ if (mutable) {
+ menu.add(new TextMergeAction(rb, "action.UndoText.", part, MergeSourceViewer.UNDO));
+ menu.add(new TextMergeAction(rb, "action.RedoText.", part, MergeSourceViewer.REDO));
+ }
+
+ menu.add(new Separator("ccp"));
+ if (mutable)
+ menu.add(new TextMergeAction(rb, "action.CutText.", part, MergeSourceViewer.CUT));
+ menu.add(new TextMergeAction(rb, "action.CopyText.", part, MergeSourceViewer.COPY));
+ if (mutable) {
+ menu.add(new TextMergeAction(rb, "action.PasteText.", part, MergeSourceViewer.PASTE));
+ menu.add(new TextMergeAction(rb, "action.DeleteText.", part, MergeSourceViewer.DELETE));
+ }
+ menu.add(new TextMergeAction(rb, "action.SelectAllText.", part, MergeSourceViewer.SELECT_ALL));
+
+ menu.add(new Separator("edit"));
+ menu.add(new Separator("find"));
+ //contributeAction(menu, part, "find", "Find");
+ //menu.add(new TextMergeAction(rb, "action.ContentAssistProposal.", part, MergeSourceViewer.CONTENTASSIST_PROPOSAL));
+ //menu.add(new TextMergeAction(rb, "action.ContentAssistTip.", part, MergeSourceViewer.CONTENTASSIST_TIP));
+
+ menu.add(new Separator("save"));
+ //if (mutable)
+ // contributeAction(menu, part, "save", "Save");
+
+ menu.add(new Separator("rest"));
+ }
+
+ /**
+ * Initializes the text viewers of the three content areas with the given input objects.
+ * Subclasses may extend.
+ */
+ protected void updateContent(Object ancestor, Object left, Object right) {
+
+ // clear stuff
+ fCurrentDiff= null;
+ fChangeDiffs= null;
+ fAllDiffs= null;
+
+ fLeftContentsChanged= false;
+ fRightContentsChanged= false;
+
+ CompareConfiguration cc= getCompareConfiguration();
+ IMergeViewerContentProvider cp= getMergeContentProvider();
+
+ boolean rightEditable= cc.isRightEditable() && cp.isRightEditable(getInput());
+ boolean leftEditable= cc.isLeftEditable() && cp.isLeftEditable(getInput());
+
+ fRight.setEditable(rightEditable);
+ fLeft.setEditable(leftEditable);
+
+ // set new documents
+ setDocument(fAncestor, ancestor);
+
+ setDocument(fLeft, left);
+ fLeftLineCount= fLeft.getLineCount();
+
+ setDocument(fRight, right);
+ fRightLineCount= fRight.getLineCount();
+
+ doDiff();
+
+ invalidateLines();
+ updateVScrollBar();
+ selectFirstDiff();
+ }
+
+ private void updateDiffBackground(Diff diff) {
+
+ if (diff == null || diff.fIsToken)
+ return;
+ Point region= new Point(0, 0);
+
+ Color c= getColor(getFillColor(diff));
+ if (c == null)
+ return;
+
+ if (isThreeWay())
+ fAncestor.setLineBackground(diff.fAncestorPos, c);
+ fLeft.setLineBackground(diff.fLeftPos, c);
+ fRight.setLineBackground(diff.fRightPos, c);
+ }
+
+ private void unsetDocument(MergeSourceViewer tp) {
+ IDocument oldDoc= tp.getDocument();
+ if (oldDoc != null) { // deinstall old positions
+ if (fPositionUpdater != null)
+ oldDoc.removePositionUpdater(fPositionUpdater);
+ try {
+ oldDoc.removePositionCategory(MY_UPDATER);
+ } catch (BadPositionCategoryException ex) {
+ }
+ }
+ }
+
+ /**
+ * Called whenver one of the documents changes.
+ * Sets the dirty state of this viewer and updates the lines.
+ * Implements IDocumentListener.
+ */
+ private void documentChanged(DocumentEvent e) {
+
+ IDocument doc= e.getDocument();
+
+ if (doc == fLeft.getDocument()) {
+ fLeftContentsChanged= true;
+ setLeftDirty(true);
+ } else if (doc == fRight.getDocument()) {
+ setRightDirty(true);
+ fRightContentsChanged= true;
+ }
+
+ updateLines(doc);
+ }
+
+ /**
+ * Returns true if a new Document could be installed.
+ */
+ private boolean setDocument(MergeSourceViewer tp, Object o) {
+
+ if (tp == null)
+ return false;
+
+ IDocument newDoc= null;
+
+ if (o instanceof IDocumentRange) {
+ newDoc= ((IDocumentRange)o).getDocument();
+
+ } else if (o instanceof Document) {
+ newDoc= (Document) o;
+
+ } else if (o instanceof IStreamContentAccessor) {
+ IStreamContentAccessor sca= (IStreamContentAccessor) o;
+ if (sca != null) {
+ String s= null;
+
+ try {
+ s= Utilities.readString(sca.getContents());
+ } catch (CoreException ex) {
+ }
+
+ newDoc= new Document(s != null ? s : "");
+ IDocumentPartitioner partitioner= getDocumentPartitioner();
+ if (partitioner != null) {
+ newDoc.setDocumentPartitioner(partitioner);
+ partitioner.connect(newDoc);
+ }
+ }
+ }
+
+ boolean enabled= true;
+ if (newDoc == null) {
+ newDoc= new Document("");
+ enabled= false;
+ }
+
+ IDocument oldDoc= tp.getDocument();
+
+ unsetDocument(tp);
+
+ if (newDoc != null) {
+ newDoc.addPositionCategory(MY_UPDATER);
+ if (fPositionUpdater == null)
+ fPositionUpdater= new DefaultPositionUpdater(MY_UPDATER);
+ newDoc.addPositionUpdater(fPositionUpdater);
+ }
+
+ if (newDoc != oldDoc) { // new document
+
+ // deinstall old document
+ if (oldDoc != null)
+ oldDoc.removeDocumentListener(fDocumentListener);
+
+ // install new document
+ if (newDoc != null) {
+
+ IRegion region= null;
+ if (o instanceof IDocumentRange) {
+ Position range= ((IDocumentRange) o).getRange();
+ if (range != null)
+ region= new Region(range.getOffset(), range.getLength());
+ }
+
+ tp.setRegion(region);
+ if (fSubDoc) {
+ if (region != null) {
+ IRegion r= normalizeDocumentRegion(newDoc, region);
+ tp.setDocument(newDoc, r.getOffset(), r.getLength());
+ } else
+ tp.setDocument(newDoc);
+ } else
+ tp.setDocument(newDoc);
+
+ newDoc.addDocumentListener(fDocumentListener);
+ }
+
+ } else { // just different range
+
+ IRegion region= null;
+ if (o instanceof IDocumentRange) {
+ Position range= ((IDocumentRange) o).getRange();
+ if (range != null)
+ region= new Region(range.getOffset(), range.getLength());
+ }
+
+ tp.setRegion(region);
+ if (fSubDoc) {
+ if (region != null) {
+ IRegion r= normalizeDocumentRegion(tp.getDocument(), region);
+ tp.setVisibleRegion(r.getOffset(), r.getLength());
+ }
+ }
+ }
+
+ tp.setEnabled(enabled);
+
+ return enabled;
+ }
+
+ /**
+ * Returns the contents of the underlying document as an array of bytes.
+ *
+ * @param left if <code>true</code> the contents of the left side is returned; otherwise the right side
+ * @return the contents of the left or right document
+ */
+ protected byte[] getContents(boolean left) {
+
+ if (left) {
+ if (fLeftContentsChanged)
+ return fLeft.getDocument().get().getBytes();
+ } else {
+ if (fRightContentsChanged)
+ return fRight.getDocument().get().getBytes();
+ }
+ return null;
+ }
+
+ private IRegion normalizeDocumentRegion(IDocument doc, IRegion region) {
+
+ if (region == null || doc == null)
+ return region;
+
+ int maxLength= doc.getLength();
+
+ int start= region.getOffset();
+ if (start < 0)
+ start= 0;
+ else if (start > maxLength)
+ start= maxLength;
+
+ int length= region.getLength();
+ if (length < 0)
+ length= 0;
+ else if (start + length > maxLength)
+ length= maxLength - start;
+
+ return new Region(start, length);
+ }
+
+ protected final void handleResizeAncestor(int x, int y, int width, int height) {
+ if (width > 0) {
+ Rectangle trim= fLeft.getTextWidget().computeTrim(0, 0, 0, 0);
+ int scrollbarHeight= trim.height;
+ if (Utilities.okToUse(fAncestorCanvas))
+ fAncestorCanvas.setVisible(true);
+ if (fAncestor.isControlOkToUse())
+ fAncestor.getTextWidget().setVisible(true);
+ fAncestorCanvas.setBounds(x, y, MARGIN_WIDTH, height-scrollbarHeight);
+ fAncestor.getTextWidget().setBounds(x+MARGIN_WIDTH, y, width-MARGIN_WIDTH, height);
+ } else {
+ if (Utilities.okToUse(fAncestorCanvas))
+ fAncestorCanvas.setVisible(false);
+ if (fAncestor.isControlOkToUse()) {
+ StyledText t= fAncestor.getTextWidget();
+ t.setVisible(false);
+ t.setBounds(0, 0, 0, 0);
+ }
+ }
+ }
+
+ /**
+ * Lays out everything.
+ */
+ protected final void handleResizeLeftRight(int x, int y, int width1, int centerWidth, int width2, int height) {
+
+ // determine some minimal sizes
+ int scrollbarWidth= 0;
+ if (fSynchronizedScrolling && fRightCanvas != null)
+ scrollbarWidth= fRightCanvas.computeTrim(0, 0, 0, 0).width;
+
+ Rectangle trim= fLeft.getTextWidget().computeTrim(0, 0, 0, 0);
+ int scrollbarHeight= trim.height;
+
+ // determine some derived sizes
+ Composite composite= (Composite) getControl();
+
+ int leftTextWidth= width1-MARGIN_WIDTH;
+ int rightTextWidth= width2-MARGIN_WIDTH-scrollbarWidth;
+
+ fLeftCanvas.setBounds(x, y, MARGIN_WIDTH, height-scrollbarHeight);
+ x+= MARGIN_WIDTH;
+
+ fLeft.getTextWidget().setBounds(x, y, leftTextWidth, height);
+ x+= leftTextWidth;
+
+ if (fCenter == null || fCenter.isDisposed())
+ fCenter= createCenter(composite);
+ fCenter.setBounds(x, y, centerWidth, height-scrollbarHeight);
+ x+= centerWidth;
+
+ if (!fSynchronizedScrolling) {
+ if (fRightCanvas != null) {
+ fRightCanvas.setBounds(x, y, MARGIN_WIDTH, height-scrollbarHeight);
+ fRightCanvas.redraw();
+ }
+ // we draw the canvas to the left of the text widget
+ x+= MARGIN_WIDTH;
+ }
+
+ fRight.getTextWidget().setBounds(x, y, rightTextWidth, height);
+ x+= rightTextWidth;
+
+ if (fSynchronizedScrolling && fRightCanvas != null)
+ fRightCanvas.setBounds(x, y, scrollbarWidth+MARGIN_WIDTH, height-scrollbarHeight);
+
+ // doesn't work since TextEditors don't have their correct size yet.
+ updateVScrollBar();
+ }
+
+ /**
+ * Track selection changes to update the current Diff.
+ */
+ private void handleSelectionChanged(MergeSourceViewer tw) {
+ Point p= tw.getSelectedRange();
+ Diff d= findDiff(tw, p.x, p.x+p.y);
+ setCurrentDiff(d, false); // don't select or reveal
+ }
+
+ //---- the differencing
+
+ /**
+ * Perform a two level 2- or 3-way diff.
+ * The first level is based on line comparison, the second level on token comparison.
+ */
+ private void doDiff() {
+
+ fAllDiffs= new ArrayList();
+ fChangeDiffs= new ArrayList();
+
+ IDocument aDoc= null;
+ IDocument iDoc= fLeft.getDocument();
+ IDocument oDoc= fRight.getDocument();
+
+ if (iDoc == null || oDoc == null)
+ return;
+
+ IRegion aRegion= null;
+ IRegion iRegion= fLeft.getRegion();
+ IRegion oRegion= fRight.getRegion();
+
+ boolean threeWay= isThreeWay();
+
+ if (threeWay) {
+ aDoc= fAncestor.getDocument();
+ aRegion= fAncestor.getRegion();
+ }
+
+ fAncestor.resetLineBackground();
+ fLeft.resetLineBackground();
+ fRight.resetLineBackground();
+
+ boolean ignoreWhiteSpace= Utilities.getBoolean(getCompareConfiguration(), CompareConfiguration.IGNORE_WHITESPACE, false);
+
+ DocLineComparator sright= new DocLineComparator(oDoc, oRegion, ignoreWhiteSpace);
+ DocLineComparator sleft= new DocLineComparator(iDoc, iRegion, ignoreWhiteSpace);
+ DocLineComparator sancestor= null;
+ if (aDoc != null)
+ sancestor= new DocLineComparator(aDoc, aRegion, ignoreWhiteSpace);
+
+ if (!fSubDoc && oRegion != null && iRegion != null) {
+ // we have to add a diff for the ignored lines
+
+ int astart= 0;
+ int as= 0;
+ if (aRegion != null) {
+ astart= aRegion.getOffset();
+ as= Math.max(0, astart-1);
+ }
+ int ys= Math.max(0, iRegion.getOffset()-1);
+ int ms= Math.max(0, oRegion.getOffset()-1);
+
+ if (as > 0 || ys > 0 || ms > 0) {
+ Diff diff= new Diff(null, RangeDifference.NOCHANGE,
+ aDoc, 0, astart,
+ iDoc, 0, iRegion.getOffset(),
+ oDoc, 0, oRegion.getOffset());
+ fAllDiffs.add(diff);
+ }
+ }
+
+ RangeDifference[] e= RangeDifferencer.findRanges(sancestor, sleft, sright);
+
+ for (int i= 0; i < e.length; i++) {
+ String a= null, s= null, d= null;
+ RangeDifference es= e[i];
+
+ int kind= es.kind();
+
+ int ancestorStart= 0;
+ int ancestorEnd= 0;
+ if (sancestor != null) {
+ ancestorStart= sancestor.getTokenStart(es.ancestorStart());
+ ancestorEnd= sancestor.getTokenEnd(es.ancestorStart(), es.ancestorLength());
+ }
+
+ int leftStart= sleft.getTokenStart(es.leftStart());
+ int leftEnd= sleft.getTokenEnd(es.leftStart(), es.leftLength());
+
+ int rightStart= sright.getTokenStart(es.rightStart());
+ int rightEnd= sright.getTokenEnd(es.rightStart(), es.rightLength());
+
+ Diff diff= new Diff(null, kind,
+ aDoc, ancestorStart, ancestorEnd,
+ iDoc, leftStart, leftEnd,
+ oDoc, rightStart, rightEnd);
+
+ fAllDiffs.add(diff); // remember all range diffs for scrolling
+
+ if (ignoreWhiteSpace) {
+ if (sancestor != null)
+ a= sancestor.extract(es.ancestorStart(), es.ancestorLength());
+ s= sleft.extract(es.leftStart(), es.leftLength());
+ d= sright.extract(es.rightStart(), es.rightLength());
+
+ if ((a == null || a.trim().length() == 0) && s.trim().length() == 0 && d.trim().length() == 0)
+ continue;
+ }
+
+ if (kind != RangeDifference.NOCHANGE && kind != RangeDifference.ANCESTOR) {
+ fChangeDiffs.add(diff); // here we remember only the real diffs
+ updateDiffBackground(diff);
+
+ if (s == null)
+ s= sleft.extract(es.leftStart(), es.leftLength());
+ if (d == null)
+ d= sright.extract(es.rightStart(), es.rightLength());
+
+ if (s.length() > 0 && d.length() > 0) {
+ if (a == null && sancestor != null)
+ a= sancestor.extract(es.ancestorStart(), es.ancestorLength());
+ if (USE_MERGING_TOKEN_DIFF)
+ mergingTokenDiff(diff, aDoc, a, oDoc, d, iDoc, s);
+ else
+ simpleTokenDiff(diff, aDoc, a, oDoc, d, iDoc, s);
+ }
+ }
+ }
+
+ if (!fSubDoc && oRegion != null && iRegion != null) {
+ // we have to add a diff for the ignored lines
+
+ int aEnd= 0;
+ int aLen= 0;
+ if (aRegion != null && aDoc != null) {
+ aEnd= aRegion.getOffset()+aRegion.getLength();
+ aLen= aDoc.getLength();
+ }
+ Diff diff= new Diff(null, RangeDifference.NOCHANGE,
+ aDoc, aEnd, aLen,
+ iDoc, iRegion.getOffset()+iRegion.getLength(), iDoc.getLength(),
+ oDoc, oRegion.getOffset()+oRegion.getLength(), oDoc.getLength());
+ fAllDiffs.add(diff);
+ }
+ }
+
+ private int getTokenEnd(ITokenComparator tc, int start, int count) {
+ if (count <= 0)
+ return tc.getTokenStart(start);
+ int index= start + count - 1;
+ int l= tc.getTokenLength(index);
+ if (l < 0)
+ System.out.println("getTokenEnd: l < 0");
+ return tc.getTokenStart(index) + l;
+ }
+
+ /**
+ * Performs a token based 3-way diff on the character range specified by the given baseDiff.
+ */
+ private void simpleTokenDiff(final Diff baseDiff,
+ IDocument ancestorDoc, String a,
+ IDocument rightDoc, String d,
+ IDocument leftDoc, String s) {
+
+
+ int ancestorStart= 0;
+ int ancestorEnd= 0;
+ ITokenComparator sa= null;
+ if (ancestorDoc != null) {
+ ancestorStart= baseDiff.fAncestorPos.getOffset();
+ ancestorEnd= ancestorStart + baseDiff.fAncestorPos.getLength();
+ sa= createTokenComparator(a);
+ }
+
+ int rightStart= baseDiff.fRightPos.getOffset();
+ int rightEnd= rightStart + baseDiff.fRightPos.getLength();
+ ITokenComparator sm= createTokenComparator(d);
+
+ int leftStart= baseDiff.fLeftPos.getOffset();
+ int leftEnd= leftStart + baseDiff.fLeftPos.getLength();
+ ITokenComparator sy= createTokenComparator(s);
+
+ RangeDifference[] e= RangeDifferencer.findRanges(sa, sy, sm);
+ for (int i= 0; i < e.length; i++) {
+ RangeDifference es= e[i];
+ int kind= es.kind();
+ if (kind != RangeDifference.NOCHANGE && kind != RangeDifference.ANCESTOR) {
+
+ int ancestorStart2= ancestorStart;
+ int ancestorEnd2= ancestorStart;
+ if (ancestorDoc != null) {
+ ancestorStart2 += sa.getTokenStart(es.ancestorStart());
+ ancestorEnd2 += getTokenEnd(sa, es.ancestorStart(), es.ancestorLength());
+ }
+
+ int leftStart2= leftStart + sy.getTokenStart(es.leftStart());
+ int leftEnd2= leftStart + getTokenEnd(sy, es.leftStart(), es.leftLength());
+
+ int rightStart2= rightStart + sm.getTokenStart(es.rightStart());
+ int rightEnd2= rightStart + getTokenEnd(sm, es.rightStart(), es.rightLength());
+
+ Diff diff= new Diff(baseDiff, kind,
+ ancestorDoc, ancestorStart2, ancestorEnd2,
+ leftDoc, leftStart2, leftEnd2,
+ rightDoc, rightStart2, rightEnd2);
+ diff.fIsToken= true;
+ // add to base Diff
+ baseDiff.add(diff);
+ }
+ }
+ }
+
+ /**
+ * Performs a "smart" token based 3-way diff on the character range specified by the given baseDiff.
+ * It is smart because it tries to minimize the number of token diffs by merging them.
+ */
+ private void mergingTokenDiff(Diff baseDiff,
+ IDocument ancestorDoc, String a,
+ IDocument rightDoc, String d,
+ IDocument leftDoc, String s) {
+
+ ITokenComparator sa= null;
+ int ancestorStart= 0;
+ int ancestorEnd= 0;
+ if (ancestorDoc != null) {
+ sa= createTokenComparator(a);
+ ancestorStart= baseDiff.fAncestorPos.getOffset();
+ ancestorEnd= ancestorStart + baseDiff.fAncestorPos.getLength();
+ }
+
+ int rightStart= baseDiff.fRightPos.getOffset();
+ int rightEnd= rightStart + baseDiff.fRightPos.getLength();
+ ITokenComparator sm= createTokenComparator(d);
+
+ int leftStart= baseDiff.fLeftPos.getOffset();
+ int leftEnd= leftStart + baseDiff.fLeftPos.getLength();
+ ITokenComparator sy= createTokenComparator(s);
+
+ RangeDifference[] r= RangeDifferencer.findRanges(sa, sy, sm);
+
+ for (int i= 0; i < r.length; i++) {
+ RangeDifference es= r[i];
+
+ // determine range of diffs in one line
+ int start= i;
+ int leftLine= -1;
+ int rightLine= -1;
+ try {
+ leftLine= leftDoc.getLineOfOffset(leftStart+sy.getTokenStart(es.leftStart()));
+ rightLine= rightDoc.getLineOfOffset(rightStart+sm.getTokenStart(es.rightStart()));
+ } catch (BadLocationException e) {
+ }
+ i++;
+ for (; i < r.length; i++) {
+ es= r[i];
+ int ll, rl;
+ try {
+ if (leftLine != leftDoc.getLineOfOffset(leftStart+sy.getTokenStart(es.leftStart())))
+ break;
+ if (rightLine != rightDoc.getLineOfOffset(rightStart+sm.getTokenStart(es.rightStart())))
+ break;
+ } catch (BadLocationException e) {
+ }
+ }
+ int end= i;
+
+ // find first diff from left
+ RangeDifference first= null;
+ for (int ii= start; ii < end; ii++) {
+ es= r[ii];
+ int kind= es.kind();
+ if (kind != RangeDifference.NOCHANGE && kind != RangeDifference.ANCESTOR) {
+ first= es;
+ break;
+ }
+ }
+
+ // find first diff from mine
+ RangeDifference last= null;
+ for (int ii= end-1; ii >= start; ii--) {
+ es= r[ii];
+ int kind= es.kind();
+ if (kind != RangeDifference.NOCHANGE && kind != RangeDifference.ANCESTOR) {
+ last= es;
+ break;
+ }
+ }
+
+
+ if (first != null && last != null) {
+
+ int ancestorStart2= 0;
+ int ancestorEnd2= 0;
+ if (ancestorDoc != null) {
+ ancestorStart2= ancestorStart+sa.getTokenStart(first.ancestorStart());
+ ancestorEnd2= ancestorStart+getTokenEnd(sa, last.ancestorStart(), last.ancestorLength());
+ }
+
+ int leftStart2= leftStart+sy.getTokenStart(first.leftStart());
+ int leftEnd2= leftStart+getTokenEnd(sy, last.leftStart(), last.leftLength());
+
+ int rightStart2= rightStart+sm.getTokenStart(first.rightStart());
+ int rightEnd2= rightStart+getTokenEnd(sm, last.rightStart(), last.rightLength());
+
+ Diff diff= new Diff(baseDiff, first.kind(),
+ ancestorDoc, ancestorStart2, ancestorEnd2+1,
+ leftDoc, leftStart2, leftEnd2+1,
+ rightDoc, rightStart2, rightEnd2+1);
+ diff.fIsToken= true;
+ baseDiff.add(diff);
+ }
+ }
+ }
+
+ //---- update UI stuff
+
+ private void updateControls() {
+
+ boolean leftToRight= false;
+ boolean rightToLeft= false;
+
+ if (fCurrentDiff != null) {
+ IMergeViewerContentProvider cp= getMergeContentProvider();
+ if (cp != null) {
+ rightToLeft= cp.isLeftEditable(getInput());
+ leftToRight= cp.isRightEditable(getInput());
+ }
+ }
+
+ if (fCopyDiffLeftToRightItem != null)
+ ((Action)fCopyDiffLeftToRightItem.getAction()).setEnabled(leftToRight);
+ if (fCopyDiffRightToLeftItem != null)
+ ((Action)fCopyDiffRightToLeftItem.getAction()).setEnabled(rightToLeft);
+//
+// int fAutoResolve= 0;
+// int fUnresolvedDiffs= 0;
+// if (fChangeDiffs != null) {
+// fUnresolvedDiffs= fChangeDiffs.size();
+// if (fUnresolvedDiffs > 0) {
+// Iterator e= fChangeDiffs.iterator();
+// while (e.hasNext()) {
+// Diff diff= (Diff) e.next();
+// if (diff.isResolved()) {
+// fUnresolvedDiffs--;
+// } else {
+// if (diff.fDirection == RangeDifference.RIGHT || diff.fDirection == RangeDifference.LEFT) {
+// fAutoResolve++;
+// }
+// }
+// }
+// }
+// }
+//
+// boolean acceptReject= false;
+// boolean both= false;
+//
+// String s= "";
+//
+// if (fCurrentDiff != null) {
+// if (fCurrentDiff.isResolved()) {
+// s= "resolved";
+// } else {
+// s= "unresolved";
+//
+// IMergeViewerContentProvider twr= getContentProvider();
+// Object input= getInput();
+// boolean rightEditable= twr.isRightEditable(input);
+// boolean leftEditable= twr.isLeftEditable(input);
+//
+// switch (fCurrentDiff.fDirection) {
+// case RangeDifference.RIGHT: // outgoing
+// if (rightEditable)
+// acceptReject= true;
+// break;
+// case RangeDifference.LEFT: // incoming
+// if (leftEditable)
+// acceptReject= true;
+// break;
+// case RangeDifference.CONFLICT:
+// if (rightEditable) {
+// acceptReject= true;
+// both= true;
+// }
+// break;
+// }
+// }
+// } else {
+// if (fUnresolvedDiffs <= 0)
+// s= "allresolved";
+// else
+// s= "same";
+// }
+//
+// getAction(fTakeLeftActionItem).setEnabled(acceptReject);
+// getAction(fRejectItem).setEnabled(acceptReject);
+// if (fBothItem != null)
+// getAction(fBothItem).setEnabled(both);
+// if (fAutoItem != null)
+// getAction(fAutoItem).setEnabled(fAutoResolve > 0);
+//
+// if (s.length() > 0)
+// s= getBundle().getString("status." + s);
+//
+// ApplicationWindow b= getApplicationWindow();
+// if (b != null) {
+// String format= fBundle.getString(fUnresolvedDiffs > 0
+// ? "status.unresolvedformat"
+// : "status.resolvedformat");
+// b.setStatus(MessageFormat.format(format, new String[] { s, "" + fUnresolvedDiffs } ));
+// }
+ }
+
+
+ protected void updateHeader() {
+
+ super.updateHeader();
+
+ IMergeViewerContentProvider content= getMergeContentProvider();
+ Object input= getInput();
+ boolean m= content.isRightEditable(input);
+ boolean y= content.isLeftEditable(input);
+
+ CompareConfiguration mp= getCompareConfiguration();
+ //fLeft.setEditable(y && mp.isLeftEditable());
+ //fRight.setEditable(m && mp.isRightEditable());
+
+ updateControls();
+ }
+
+ /**
+ * Creates the two items for copying a difference range from one side to the other
+ * and adds them to the given toolbar manager.
+ */
+ protected void createToolItems(ToolBarManager tbm) {
+
+// if (USE_MORE_CONTROLS) {
+// fBothItem= new ActionContributionItem(
+// new Action(fBundle, "action.AcceptBoth.") {
+// public void actionPerformed(Window w) {
+// accept(fCurrentDiff, true, false);
+// }
+// }
+// );
+// tbm.appendToGroup("merge", fBothItem);
+//
+// fAutoItem= new ActionContributionItem(
+// new Action(fBundle, "action.AcceptAll.") {
+// public void actionPerformed(Window w) {
+// autoResolve();
+// }
+// }
+// );
+// tbm.appendToGroup("merge", fAutoItem);
+// }
+// fRejectItem= new ActionContributionItem(
+// new Action(fBundle, "action.AcceptIgnoreNow.") {
+// public void actionPerformed(Window w) {
+// reject(fCurrentDiff, true);
+// }
+// }
+// );
+// tbm.appendToGroup("merge", fRejectItem);
+//
+ Action a= new ChangePropertyAction(getResourceBundle(), getCompareConfiguration(), "action.SynchMode.", SYNC_SCROLLING);
+ a.setChecked(fSynchronizedScrolling);
+ tbm.appendToGroup("modes", a);
+
+ tbm.add(new Separator());
+
+ a= new Action() {
+ public void run() {
+ navigate(true);
+ }
+ };
+ Utilities.initAction(a, getResourceBundle(), "action.NextDiff.");
+ fNextItem= new ActionContributionItem(a);
+ tbm.appendToGroup("navigation", fNextItem);
+
+ a= new Action() {
+ public void run() {
+ navigate(false);
+ }
+ };
+ Utilities.initAction(a, getResourceBundle(), "action.PrevDiff.");
+ fPreviousItem= new ActionContributionItem(a);
+ tbm.appendToGroup("navigation", fPreviousItem);
+
+
+ CompareConfiguration cc= getCompareConfiguration();
+
+ if (cc.isRightEditable()) {
+ a= new Action() {
+ public void run() {
+ copyDiffLeftToRight();
+ }
+ };
+ Utilities.initAction(a, getResourceBundle(), "action.CopyDiffLeftToRight.");
+ fCopyDiffLeftToRightItem= new ActionContributionItem(a);
+ tbm.appendToGroup("merge", fCopyDiffLeftToRightItem);
+ }
+
+ if (cc.isLeftEditable()) {
+ a= new Action() {
+ public void run() {
+ copyDiffRightToLeft();
+ }
+ };
+ Utilities.initAction(a, getResourceBundle(), "action.CopyDiffRightToLeft.");
+ fCopyDiffRightToLeftItem= new ActionContributionItem(a);
+ tbm.appendToGroup("merge", fCopyDiffRightToLeftItem);
+ }
+ }
+
+ /* package */ void propertyChange(PropertyChangeEvent event) {
+
+ String key= event.getProperty();
+
+ if (key.equals(CompareConfiguration.IGNORE_WHITESPACE)) {
+ // clear stuff
+ fCurrentDiff= null;
+ fChangeDiffs= null;
+ fAllDiffs= null;
+
+ doDiff();
+
+ invalidateLines();
+ updateVScrollBar();
+
+ selectFirstDiff();
+ } else if (key.equals(SYNC_SCROLLING)) {
+
+ boolean b= Utilities.getBoolean(getCompareConfiguration(), SYNC_SCROLLING, true);
+ if (b != fSynchronizedScrolling)
+ toggleSynchMode();
+
+ } else
+ super.propertyChange(event);
+ }
+
+ private void selectFirstDiff() {
+ Diff firstDiff= findNext(fRight, fChangeDiffs, -1, -1);
+ setCurrentDiff(firstDiff, true);
+ }
+
+ private void toggleSynchMode() {
+ fSynchronizedScrolling= ! fSynchronizedScrolling;
+
+ scrollVertical(0, null);
+
+ // throw away central control (Sash or Canvas)
+ Control center= getCenter();
+ if (center != null && !center.isDisposed())
+ center.dispose();
+
+ fLeft.getTextWidget().getVerticalBar().setVisible(!fSynchronizedScrolling);
+ fRight.getTextWidget().getVerticalBar().setVisible(!fSynchronizedScrolling);
+
+ // recreates central control (Sash or Canvas)
+ //handleResize();
+ fComposite.layout(true);
+ }
+
+ protected void updateToolItems() {
+
+ boolean visible= false;
+ Object input= getInput();
+ if (input != null) {
+ visible= true;
+
+ IMergeViewerContentProvider content= getMergeContentProvider();
+
+ //boolean y= getMergePolicy().isLeftEditable();
+ //boolean m= getMergePolicy().isRightEditable();
+
+ //destinationEditable= content.isRightEditable(getInput());
+ //destinationEditable= content.isLeftEditable(getInput());
+ /*
+ if (USE_MORE_CONTROLS) {
+ fBothItem.setVisible(destinationEditable);
+ fAutoItem.setVisible(destinationEditable);
+ }
+ fRejectItem.setVisible(destinationEditable);
+ */
+ }
+
+ //fNextItem.setVisible(visible);
+ //fPreviousItem.setVisible(visible);
+
+ super.updateToolItems();
+ }
+
+ //---- painting lines
+
+ /**
+ *
+ */
+ private void updateLines(IDocument d) {
+
+ boolean left= false;
+ boolean right= false;
+
+ // FIXME: this optimization is incorrect because
+ // it doesn't take replace operations into account where
+ // the old and new line count does not differ
+ if (d == fLeft.getDocument()) {
+ int l= fLeft.getLineCount();
+ left= fLeftLineCount != l;
+ fLeftLineCount= l;
+ } else if (d == fRight.getDocument()) {
+ int l= fRight.getLineCount();
+ right= fRightLineCount != l;
+ fRightLineCount= l;
+ }
+
+ if (left || right) {
+
+ if (left) {
+ if (fLeftCanvas != null)
+ fLeftCanvas.redraw();
+ } else {
+ if (fRightCanvas != null)
+ fRightCanvas.redraw();
+ }
+ Control center= getCenter();
+ if (center != null)
+ center.redraw();
+
+ updateVScrollBar();
+ }
+ }
+
+ private void invalidateLines() {
+ if (isThreeWay()) {
+ if (Utilities.okToUse(fAncestorCanvas))
+ fAncestorCanvas.redraw();
+ if (fAncestor.isControlOkToUse())
+ fAncestor.getTextWidget().redraw();
+ }
+
+ if (Utilities.okToUse(fLeftCanvas))
+ fLeftCanvas.redraw();
+
+ if (fLeft.isControlOkToUse())
+ fLeft.getTextWidget().redraw();
+
+ if (Utilities.okToUse(getCenter()))
+ getCenter().redraw();
+
+ if (fRight.isControlOkToUse())
+ fRight.getTextWidget().redraw();
+
+ if (Utilities.okToUse(fRightCanvas))
+ fRightCanvas.redraw();
+ }
+
+ private void paintCenter(Canvas canvas, GC g) {
+
+ if (! fSynchronizedScrolling)
+ return;
+
+ int lineHeight= fLeft.getTextWidget().getLineHeight();
+ int visibleHeight= fRight.getViewportHeight();
+
+ Point size= canvas.getSize();
+ int x= 0;
+ int w= size.x;
+
+ g.setBackground(canvas.getBackground());
+ g.fillRectangle(x+1, 0, w-2, size.y);
+
+ if (!IS_MOTIF) {
+ // draw thin line between center ruler and both texts
+ g.setBackground(fLeftCanvas.getDisplay().getSystemColor(SWT.COLOR_WIDGET_NORMAL_SHADOW));
+ g.fillRectangle(0, 0, 1, size.y);
+ g.fillRectangle(w-1, 0, 1, size.y);
+ }
+
+ if (fChangeDiffs != null) {
+ int lshift= fLeft.getVerticalScrollOffset();
+ int rshift= fRight.getVerticalScrollOffset();
+
+ Point region= new Point(0, 0);
+
+ Iterator e= fChangeDiffs.iterator();
+ while (e.hasNext()) {
+ Diff diff= (Diff) e.next();
+ if (diff.isDeleted())
+ continue;
+
+ fLeft.getLineRange(diff.fLeftPos, region);
+ int ly= (region.x * lineHeight) + lshift;
+ int lh= region.y * lineHeight;
+
+ fRight.getLineRange(diff.fRightPos, region);
+ int ry= (region.x * lineHeight) + rshift;
+ int rh= region.y * lineHeight;
+
+ if (Math.max(ly+lh, ry+rh) < 0)
+ continue;
+ if (Math.min(ly, ry) >= visibleHeight)
+ break;
+
+ fPts[0]= x; fPts[1]= ly; fPts[2]= w; fPts[3]= ry;
+ fPts[6]= x; fPts[7]= ly+lh; fPts[4]= w; fPts[5]= ry+rh;
+
+ g.setBackground(getColor(getFillColor(diff)));
+ g.fillPolygon(fPts);
+
+ g.setLineWidth(LW);
+ g.setForeground(getColor(getStrokeColor(diff)));
+ g.drawLine(fPts[0], fPts[1], fPts[2], fPts[3]);
+ g.drawLine(fPts[6], fPts[7], fPts[4], fPts[5]);
+ }
+ }
+ }
+
+ private void paintSides(GC g, MergeSourceViewer tp, Canvas canvas, boolean right) {
+
+ int lineHeight= tp.getTextWidget().getLineHeight();
+ int visibleHeight= tp.getViewportHeight();
+
+ Point size= canvas.getSize();
+ int x= 0;
+ int w= MARGIN_WIDTH;
+
+ g.setBackground(canvas.getBackground());
+ g.fillRectangle(x, 0, w, size.y);
+
+ if (!IS_MOTIF) {
+ // draw thin line between ruler and text
+ g.setBackground(canvas.getDisplay().getSystemColor(SWT.COLOR_WIDGET_NORMAL_SHADOW));
+ if (right)
+ g.fillRectangle(0, 0, 1, size.y);
+ else
+ g.fillRectangle(size.x-1, 0, 1, size.y);
+ }
+
+ if (fChangeDiffs != null) {
+ int shift= tp.getVerticalScrollOffset() + (2-LW);
+
+ Point region= new Point(0, 0);
+ Iterator e= fChangeDiffs.iterator();
+ while (e.hasNext()) {
+ Diff diff= (Diff) e.next();
+ if (diff.isDeleted())
+ continue;
+
+ tp.getLineRange(diff.getPosition(tp), region);
+ int y= (region.x * lineHeight) + shift;
+ int h= region.y * lineHeight;
+
+ if (y+h < 0)
+ continue;
+ if (y >= visibleHeight)
+ break;
+
+ g.setBackground(getColor(getFillColor(diff)));
+ if (right)
+ g.fillRectangle(x, y, w-5, h);
+ else
+ g.fillRectangle(x+5, y, w-3, h);
+
+ g.setBackground(getColor(getStrokeColor(diff)));
+ if (right) {
+ g.fillRectangle(x, y-1, w-4, LW);
+ g.fillRectangle(x+5, y, LW, h);
+ g.fillRectangle(x, y+h-1, w-4, LW);
+ } else {
+ g.fillRectangle(x+3, y-1, w-3, LW);
+ g.fillRectangle(x+3, y, LW, h);
+ g.fillRectangle(x+3, y+h-1, w-3, LW);
+ }
+ }
+ }
+ }
+
+ private void paint(PaintEvent event, MergeSourceViewer tp) {
+
+ if (fChangeDiffs == null)
+ return;
+
+ Control canvas= (Control) event.widget;
+ GC g= event.gc;
+
+ int lineHeight= tp.getTextWidget().getLineHeight();
+ int w= canvas.getSize().x;
+ int shift= tp.getVerticalScrollOffset() + (2-LW);
+ int maxh= event.y+event.height; // visibleHeight
+
+ Point range= new Point(0, 0);
+
+ Iterator e= fChangeDiffs.iterator();
+ while (e.hasNext()) {
+ Diff diff= (Diff) e.next();
+ if (diff.isDeleted())
+ continue;
+
+ tp.getLineRange(diff.getPosition(tp), range);
+ int y= (range.x * lineHeight) + shift;
+ int h= range.y * lineHeight;
+
+ if (y+h < event.y)
+ continue;
+ if (y > maxh)
+ break;
+
+ g.setBackground(getColor(getStrokeColor(diff)));
+ g.fillRectangle(0, y-1, w, LW);
+ g.fillRectangle(0, y+h-1, w, LW);
+ }
+ }
+
+ private RGB getFillColor(Diff diff) {
+ boolean selected= fCurrentDiff != null && fCurrentDiff.fParent == diff;
+ switch (diff.fDirection) {
+ case RangeDifference.RIGHT:
+ return selected ? SELECTED_OUTGOING_FILL : OUTGOING_FILL;
+ case RangeDifference.ANCESTOR:
+ return selected ? SELECTED_CONFLICT_FILL : CONFLICT_FILL;
+ case RangeDifference.LEFT:
+ return selected ? SELECTED_INCOMING_FILL : INCOMING_FILL;
+ case RangeDifference.CONFLICT:
+ return selected ? SELECTED_CONFLICT_FILL : CONFLICT_FILL;
+ }
+ return null;
+ }
+
+ private RGB getStrokeColor(Diff diff) {
+ boolean selected= fCurrentDiff != null && fCurrentDiff.fParent == diff;
+ switch (diff.fDirection) {
+ case RangeDifference.RIGHT:
+ return selected ? SELECTED_OUTGOING : OUTGOING;
+ case RangeDifference.ANCESTOR:
+ return selected ? SELECTED_CONFLICT : CONFLICT;
+ case RangeDifference.LEFT:
+ return selected ? SELECTED_INCOMING : INCOMING;
+ case RangeDifference.CONFLICT:
+ return selected ? SELECTED_CONFLICT : CONFLICT;
+ }
+ return null;
+ }
+
+ private Color getColor(RGB rgb) {
+ if (rgb == null)
+ return null;
+ if (fColors == null)
+ fColors= new HashMap(20);
+ Color c= (Color) fColors.get(rgb);
+ if (c == null) {
+ c= new Color(fComposite.getDisplay(), rgb);
+ fColors.put(rgb, c);
+ }
+ return c;
+ }
+
+ //---- Navigating and resolving Diffs
+
+ /**
+ */
+ private void navigate(boolean down) {
+
+ Diff diff= null;
+ if (fChangeDiffs != null) {
+ MergeSourceViewer part= fFocusPart;
+ if (part == null)
+ part= fRight;
+
+ if (part != null) {
+ Point s= part.getSelectedRange();
+ if (down)
+ diff= findNext(part, fChangeDiffs, s.x, s.x+s.y);
+ else
+ diff= findPrev(part, fChangeDiffs, s.x, s.x+s.y);
+ }
+ }
+
+ if (diff == null) {
+ Control c= getControl();
+ if (Utilities.okToUse(c))
+ c.getDisplay().beep();
+ if (DEAD_STEP)
+ return;
+ if (fChangeDiffs.size() > 0) {
+ if (down)
+ diff= (Diff) fChangeDiffs.get(0);
+ else
+ diff= (Diff) fChangeDiffs.get(fChangeDiffs.size()-1);
+ }
+ }
+
+ setCurrentDiff(diff, true);
+ }
+
+ /**
+ * Find the Diff that overlaps with the given TextPart's text range.
+ * If the range doesn't overlap with any range <code>null</code>
+ * is returned.
+ */
+ private Diff findDiff(MergeSourceViewer tp, int rangeStart, int rangeEnd) {
+ if (fChangeDiffs != null) {
+ Iterator e= fChangeDiffs.iterator();
+ while (e.hasNext()) {
+ Diff diff= (Diff) e.next();
+ if (diff.contains(tp, rangeStart, rangeEnd))
+ return diff;
+ }
+ }
+ return null;
+ }
+
+ private static Diff findNext(MergeSourceViewer tp, List v, int start, int end) {
+ for (int i= 0; i < v.size(); i++) {
+ Diff diff= (Diff) v.get(i);
+ Position p= diff.getPosition(tp);
+ if (p != null) {
+ int startOffset= p.getOffset();
+ if (end < startOffset)
+ return diff;
+ if (diff.fDiffs != null) {
+ Diff d= null;
+ int endOffset= startOffset + p.getLength();
+ if (start == startOffset && end == endOffset) {
+ d= findNext(tp, diff.fDiffs, start, start);
+ } else if (end < endOffset) {
+ d= findNext(tp, diff.fDiffs, start, end);
+ }
+ if (d != null)
+ return d;
+ }
+ }
+ }
+ return null;
+ }
+
+ private static Diff findPrev(MergeSourceViewer tp, List v, int start, int end) {
+ for (int i= v.size()-1; i >= 0; i--) {
+ Diff diff= (Diff) v.get(i);
+ Position p= diff.getPosition(tp);
+ if (p != null) {
+ int startOffset= p.getOffset();
+ int endOffset= startOffset + p.getLength();
+ if (start > endOffset)
+ return diff;
+ if (diff.fDiffs != null) {
+ Diff d= null;
+ if (start == startOffset && end == endOffset) {
+ d= findPrev(tp, diff.fDiffs, end, end);
+ } else if (start >= startOffset) {
+ d= findPrev(tp, diff.fDiffs, start, end);
+ }
+ if (d != null)
+ return d;
+ }
+ }
+ }
+ return null;
+ }
+
+ /*
+ * Set the currently active Diff and update the toolbars controls and lines.
+ * If <code>revealAndSelect</code> is <code>true</code> the Diff is revealed and
+ * selected in both TextParts.
+ */
+ private void setCurrentDiff(Diff d, boolean revealAndSelect) {
+
+ if (d == fCurrentDiff)
+ return;
+
+ Diff oldDiff= fCurrentDiff;
+
+ if (d != null && revealAndSelect) {
+
+ // before we set fCurrentDiff we change the selection
+ // so that the paint code uses the old background colors
+ // otherwise we get screen cheese
+ if (isThreeWay())
+ fAncestor.setSelection(d.fAncestorPos);
+ fLeft.setSelection(d.fLeftPos);
+ fRight.setSelection(d.fRightPos);
+
+ // now switch diffs
+ fCurrentDiff= d;
+ revealDiff(d, d.fIsToken);
+ } else {
+ fCurrentDiff= d;
+ }
+
+ if (d != null && !d.fIsToken) {
+ updateDiffBackground(oldDiff);
+ updateDiffBackground(fCurrentDiff);
+ }
+
+ updateControls();
+ invalidateLines();
+ }
+
+ private void revealDiff(Diff d, boolean smart) {
+
+ boolean ancestorIsVisible= false;
+ boolean leftIsVisible= false;
+ boolean rightIsVisible= false;
+
+ if (smart) {
+ Point region= new Point(0, 0);
+ // find the starting line of the diff in all text widgets
+ int ls= fLeft.getLineRange(d.fLeftPos, region).x;
+ int rs= fRight.getLineRange(d.fRightPos, region).x;
+
+ if (isThreeWay()) {
+ int as= fAncestor.getLineRange(d.fAncestorPos, region).x;
+ if (as >= fAncestor.getTopIndex() && as <= fAncestor.getBottomIndex())
+ ancestorIsVisible= true;
+ }
+
+ if (ls >= fLeft.getTopIndex() && ls <= fLeft.getBottomIndex())
+ leftIsVisible= true;
+
+ if (rs >= fRight.getTopIndex() && rs <= fRight.getBottomIndex())
+ rightIsVisible= true;
+
+ if (leftIsVisible && rightIsVisible)
+ return;
+ }
+
+ int vpos= 0;
+
+ MergeSourceViewer allButThis= null;
+ if (leftIsVisible) {
+ vpos= realToVirtualPosition(fLeft, fLeft.getTopIndex());
+ allButThis= fLeft;
+ } else if (rightIsVisible) {
+ vpos= realToVirtualPosition(fRight, fRight.getTopIndex());
+ allButThis= fRight;
+ } else if (ancestorIsVisible) {
+ vpos= realToVirtualPosition(fAncestor, fAncestor.getTopIndex());
+ allButThis= fAncestor;
+ } else {
+ if (fAllDiffs != null) {
+ Iterator e= fAllDiffs.iterator();
+ for (int i= 0; e.hasNext(); i++) {
+ Diff diff= (Diff) e.next();
+ if (diff == d)
+ break;
+ vpos+= diff.getMaxDiffHeight(fShowAncestor);
+ }
+ }
+ //vpos-= fRight.getViewportLines()/4;
+ }
+
+ scrollVertical(vpos, allButThis);
+
+ if (fVScrollBar != null) {
+ //int value= Math.max(0, Math.min(vpos, getVirtualHeight() - maxExtentHeight));
+ fVScrollBar.setSelection(vpos);
+ }
+ }
+
+ //--------------------------------------------------------------------------------
+
+ protected void copy(boolean leftToRight) {
+ if (leftToRight) {
+ if (fLeft.getEnabled()) {
+ // copy text
+ String text= fLeft.getTextWidget().getText();
+ fRight.getTextWidget().setText(text);
+ fRight.setEnabled(true);
+ } else {
+ // delete
+ fRight.getTextWidget().setText("");
+ fRight.setEnabled(false);
+ }
+ fRightLineCount= fRight.getLineCount();
+ setRightDirty(true);
+ fRightContentsChanged= false;
+ } else {
+ if (fRight.getEnabled()) {
+ // copy text
+ String text= fRight.getTextWidget().getText();
+ fLeft.getTextWidget().setText(text);
+ fLeft.setEnabled(true);
+ } else {
+ // delete
+ fLeft.getTextWidget().setText("");
+ fLeft.setEnabled(false);
+ }
+ fLeftLineCount= fLeft.getLineCount();
+ setLeftDirty(true);
+ fLeftContentsChanged= false;
+ }
+ doDiff();
+ invalidateLines();
+ updateVScrollBar();
+ selectFirstDiff();
+ }
+
+ private void copyDiffLeftToRight() {
+ copy(fCurrentDiff, true, false, false);
+ }
+
+ private void copyDiffRightToLeft() {
+ copy(fCurrentDiff, false, false, false);
+ }
+
+// private void accept(Diff diff, boolean both, boolean gotoNext) {
+// if (getCompareConfiguration().isRightEditable())
+// copy(diff, true, both, gotoNext);
+// else if (getCompareConfiguration().isLeftEditable())
+// copy(diff, false, both, gotoNext);
+// }
+
+ private void copy(Diff diff, boolean leftToRight, boolean both, boolean gotoNext) {
+
+ if (diff != null && !diff.isResolved()) {
+
+ Position fromPos= null;
+ Position toPos= null;
+ IDocument fromDoc= null;
+ IDocument toDoc= null;
+
+ if (leftToRight) {
+ fRight.setEnabled(true);
+ fromPos= diff.fLeftPos;
+ toPos= diff.fRightPos;
+ fromDoc= fLeft.getDocument();
+ toDoc= fRight.getDocument();
+ } else {
+ fLeft.setEnabled(true);
+ fromPos= diff.fRightPos;
+ toPos= diff.fLeftPos;
+ fromDoc= fRight.getDocument();
+ toDoc= fLeft.getDocument();
+ }
+
+ if (fromDoc != null) {
+
+ int fromStart= fromPos.getOffset();
+ int fromLen= fromPos.getLength();
+
+ int toStart= toPos.getOffset();
+ int toLen= toPos.getLength();
+
+ try {
+ String s= null;
+
+ switch (diff.fDirection) {
+ case RangeDifference.RIGHT:
+ case RangeDifference.LEFT:
+ s= fromDoc.get(fromStart, fromLen);
+ break;
+ case RangeDifference.ANCESTOR:
+ break;
+ case RangeDifference.CONFLICT:
+ s= fromDoc.get(fromStart, fromLen);
+ if (both)
+ s+= toDoc.get(toStart, toLen);
+ break;
+ }
+ if (s != null) {
+ toDoc.replace(toStart, toLen, s);
+ toPos.setOffset(toStart);
+ toPos.setLength(s.length());
+ }
+
+ } catch (BadLocationException e) {
+ }
+ }
+
+ diff.setResolved(true);
+
+ if (gotoNext) {
+ navigate(true/*, true*/);
+ } else {
+ revealDiff(diff, true);
+ updateControls();
+ }
+ }
+ }
+
+ /**
+ */
+// private void reject(Diff diff, boolean gotoNext) {
+//
+// if (diff != null && !diff.isResolved()) {
+//
+// switch (diff.fDirection) {
+// case RangeDifference.RIGHT:
+// setRightDirty(true); // mark dirty to force save!
+// break;
+// case RangeDifference.LEFT:
+// setLeftDirty(true); // mark dirty to force save!
+// break;
+// case RangeDifference.ANCESTOR:
+// break;
+// case RangeDifference.CONFLICT:
+// setLeftDirty(true); // mark dirty to force save!
+// setRightDirty(true); // mark dirty to force save!
+// break;
+// }
+//
+// diff.setResolved(true);
+//
+// if (gotoNext) {
+// navigate(true/*, true*/);
+// } else {
+// revealDiff(diff, true);
+// updateControls();
+// }
+// }
+// }
+
+
+// private void autoResolve() {
+// fCurrentDiff= null;
+// Diff firstConflict= null;
+//
+// Iterator e= fChangeDiffs.iterator();
+// for (int i= 0; e.hasNext(); i++) {
+// Diff diff= (Diff) e.next();
+// if (!diff.isResolved()) {
+// switch (diff.fDirection) {
+// case RangeDifference.RIGHT: // outgoing
+// case RangeDifference.LEFT: // incoming
+// accept(diff, false, false);
+// break;
+// case RangeDifference.CONFLICT: // incoming
+// if (firstConflict == null)
+// firstConflict= diff;
+// break;
+// }
+// }
+// }
+//
+// if (firstConflict == null)
+// firstConflict= (Diff) fChangeDiffs.get(0);
+// setCurrentDiff(firstConflict, true);
+// }
+
+ //---- scrolling
+
+ /**
+ * Calculates virtual height (in lines) of views by adding the maximum of corresponding diffs.
+ */
+ private int getVirtualHeight() {
+ int h= 1;
+ if (fAllDiffs != null) {
+ Iterator e= fAllDiffs.iterator();
+ for (int i= 0; e.hasNext(); i++) {
+ Diff diff= (Diff) e.next();
+ h+= diff.getMaxDiffHeight(fShowAncestor);
+ }
+ }
+ return h;
+ }
+
+ /**
+ * The height of the TextEditors in lines.
+ */
+ private int getViewportHeight() {
+ StyledText te= fLeft.getTextWidget();
+
+ int vh= te.getClientArea().height;
+ if (vh == 0) {
+ // seems to be a bug in TextEditor.getClientArea(): returns bogus value on first
+ // call; as a workaround we calculate the clientArea from its container...
+ Rectangle trim= te.computeTrim(0, 0, 0, 0);
+ int scrollbarHeight= trim.height;
+
+ int headerHeight= getHeaderHeight();
+
+ Composite composite= (Composite) getControl();
+ Rectangle r= composite.getClientArea();
+
+ vh= r.height-headerHeight-scrollbarHeight;
+ }
+
+ return vh / te.getLineHeight();
+ }
+
+ /**
+ * Returns the virtual position for the given view position.
+ */
+ private int realToVirtualPosition(MergeSourceViewer w, int vpos) {
+
+ if (! fSynchronizedScrolling || fAllDiffs == null)
+ return vpos;
+
+ int viewPos= 0; // real view position
+ int virtualPos= 0; // virtual position
+ Point region= new Point(0, 0);
+
+ Iterator e= fAllDiffs.iterator();
+ while (e.hasNext()) {
+ Diff diff= (Diff) e.next();
+ Position pos= diff.getPosition(w);
+ w.getLineRange(pos, region);
+ int realHeight= region.y;
+ int virtualHeight= diff.getMaxDiffHeight(fShowAncestor);
+ if (vpos <= viewPos + realHeight) { // OK, found!
+ vpos-= viewPos; // make relative to this slot
+ // now scale position within this slot to virtual slot
+ if (realHeight <= 0)
+ vpos= 0;
+ else
+ vpos= (vpos*virtualHeight)/realHeight;
+ return virtualPos+vpos;
+ }
+ viewPos+= realHeight;
+ virtualPos+= virtualHeight;
+ }
+ return virtualPos;
+ }
+
+ private void scrollVertical(int virtualPos, MergeSourceViewer allBut) {
+
+ if (virtualPos < 0)
+ virtualPos= virtualPos;
+
+ GC gc;
+ if (fSynchronizedScrolling) {
+ int s= 0;
+
+ if (true) {
+ s= getVirtualHeight() - virtualPos;
+ int height= fRight.getViewportLines()/4;
+ if (s < 0)
+ s= 0;
+ if (s > height)
+ s= height;
+ }
+
+ fInScrolling= true;
+
+ if (isThreeWay() && allBut != fAncestor) {
+ int y= virtualToRealPosition(fAncestor, virtualPos+s)-s;
+ fAncestor.vscroll(y);
+ }
+
+ if (allBut != fLeft) {
+ int y= virtualToRealPosition(fLeft, virtualPos+s)-s;
+ fLeft.vscroll(y);
+ }
+
+ if (allBut != fRight) {
+ int y= virtualToRealPosition(fRight, virtualPos+s)-s;
+ fRight.vscroll(y);
+ }
+
+ fInScrolling= false;
+
+ if (isThreeWay())
+ fAncestorCanvas.repaint();
+
+ fLeftCanvas.repaint();
+
+ Control center= getCenter();
+ if (center instanceof BufferedCanvas)
+ ((BufferedCanvas)center).repaint();
+
+ fRightCanvas.repaint();
+ } else {
+ if (allBut == fAncestor && isThreeWay())
+ fAncestorCanvas.repaint();
+
+ if (allBut == fLeft)
+ fLeftCanvas.repaint();
+
+ if (allBut == fRight)
+ fRightCanvas.repaint();
+ }
+ }
+
+ /**
+ * Updates Scrollbars with viewports.
+ */
+ private void syncViewport(MergeSourceViewer w) {
+
+ if (fInScrolling)
+ return;
+
+ int ix= w.getTopIndex();
+ int ix2= w.getDocumentRegionOffset();
+
+ int viewPosition= realToVirtualPosition(w, ix-ix2);
+
+ scrollVertical(viewPosition, w); // scroll all but the given views
+
+ if (fVScrollBar != null) {
+ int value= Math.max(0, Math.min(viewPosition, getVirtualHeight() - getViewportHeight()));
+ fVScrollBar.setSelection(value);
+ }
+ }
+
+ /**
+ */
+ private void updateVScrollBar() {
+
+ if (Utilities.okToUse(fVScrollBar) && fVScrollBar.isVisible()) {
+ int virtualHeight= getVirtualHeight();
+ int viewPortHeight= getViewportHeight();
+ fVScrollBar.setPageIncrement(viewPortHeight-1);
+ fVScrollBar.setMaximum(virtualHeight);
+ if (viewPortHeight > virtualHeight)
+ fVScrollBar.setThumb(virtualHeight);
+ else
+ fVScrollBar.setThumb(viewPortHeight);
+ }
+ }
+
+ /**
+ * maps given virtual position into a real view position of this view.
+ */
+ private int virtualToRealPosition(MergeSourceViewer part, int v) {
+
+ if (! fSynchronizedScrolling || fAllDiffs == null)
+ return v;
+
+ int virtualPos= 0;
+ int viewPos= 0;
+ Point region= new Point(0, 0);
+
+ Iterator e= fAllDiffs.iterator();
+ while (e.hasNext()) {
+ Diff diff= (Diff) e.next();
+ Position pos= diff.getPosition(part);
+ int viewHeight= part.getLineRange(pos, region).y;
+ int virtualHeight= diff.getMaxDiffHeight(fShowAncestor);
+ if (v < (virtualPos + virtualHeight)) {
+ v-= virtualPos; // make relative to this slot
+ if (viewHeight <= 0) {
+ v= 0;
+ } else {
+ v= (v*viewHeight)/virtualHeight;
+ }
+ return viewPos+v;
+ }
+ virtualPos+= virtualHeight;
+ viewPos+= viewHeight;
+ }
+ return viewPos;
+ }
+
+}

Back to the top