KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > eclipse > compare > contentmergeviewer > TextMergeViewer


1 /*******************************************************************************
2  * Copyright (c) 2000, 2007 IBM Corporation and others.
3  * All rights reserved. This program and the accompanying materials
4  * are made available under the terms of the Eclipse Public License v1.0
5  * which accompanies this distribution, and is available at
6  * http://www.eclipse.org/legal/epl-v10.html
7  *
8  * Contributors:
9  * IBM Corporation - initial API and implementation
10  * channingwalton@mac.com - curved line code
11  * gilles.querret@free.fr - fix for https://bugs.eclipse.org/bugs/show_bug.cgi?id=72995
12  * Max Weninger (max.weninger@windriver.com) - Bug 131895 [Edit] Undo in compare
13  * Max Weninger (max.weninger@windriver.com) - Bug 72936 [Viewers] Show line numbers in comparision
14  *******************************************************************************/

15 package org.eclipse.compare.contentmergeviewer;
16
17 import java.io.UnsupportedEncodingException JavaDoc;
18 import java.lang.reflect.InvocationTargetException JavaDoc;
19 import java.util.*;
20 import java.util.List JavaDoc;
21
22 import org.eclipse.compare.*;
23 import org.eclipse.compare.internal.*;
24 import org.eclipse.compare.patch.IHunk;
25 import org.eclipse.compare.rangedifferencer.*;
26 import org.eclipse.compare.structuremergeviewer.*;
27 import org.eclipse.core.resources.ResourcesPlugin;
28 import org.eclipse.core.runtime.*;
29 import org.eclipse.core.runtime.Assert;
30 import org.eclipse.jface.action.*;
31 import org.eclipse.jface.dialogs.ErrorDialog;
32 import org.eclipse.jface.dialogs.MessageDialog;
33 import org.eclipse.jface.operation.IRunnableWithProgress;
34 import org.eclipse.jface.preference.IPreferenceStore;
35 import org.eclipse.jface.resource.ColorRegistry;
36 import org.eclipse.jface.resource.JFaceResources;
37 import org.eclipse.jface.text.*;
38 import org.eclipse.jface.text.Region;
39 import org.eclipse.jface.text.source.SourceViewerConfiguration;
40 import org.eclipse.jface.util.IPropertyChangeListener;
41 import org.eclipse.jface.util.PropertyChangeEvent;
42 import org.eclipse.jface.viewers.*;
43 import org.eclipse.jface.window.Window;
44 import org.eclipse.osgi.util.NLS;
45 import org.eclipse.swt.SWT;
46 import org.eclipse.swt.accessibility.AccessibleAdapter;
47 import org.eclipse.swt.accessibility.AccessibleEvent;
48 import org.eclipse.swt.custom.StyleRange;
49 import org.eclipse.swt.custom.StyledText;
50 import org.eclipse.swt.events.*;
51 import org.eclipse.swt.graphics.*;
52 import org.eclipse.swt.widgets.*;
53 import org.eclipse.ui.*;
54 import org.eclipse.ui.actions.ActionFactory;
55 import org.eclipse.ui.progress.IProgressService;
56 import org.eclipse.ui.texteditor.*;
57
58 import com.ibm.icu.text.MessageFormat;
59
60 /**
61  * A text merge viewer uses the <code>RangeDifferencer</code> to perform a
62  * textual, line-by-line comparison of two (or three) input documents.
63  * It is based on the <code>ContentMergeViewer</code> and uses <code>TextViewer</code>s
64  * to implement the ancestor, left, and right content areas.
65  * <p>
66  * In the three-way compare case ranges of differing lines are highlighted and framed
67  * with different colors to show whether the difference is an incoming, outgoing, or conflicting change.
68  * The <code>TextMergeViewer</code> supports the notion of a current "differing range"
69  * and provides toolbar buttons to navigate from one range to the next (or previous).
70  * <p>
71  * If there is a current "differing range" and the underlying document is editable
72  * the <code>TextMergeViewer</code> enables actions in context menu and toolbar to
73  * copy a range from one side to the other side, thereby performing a merge operation.
74  * <p>
75  * In addition to a line-by-line comparison the <code>TextMergeViewer</code>
76  * uses a token based compare on differing lines.
77  * The token compare is activated when navigating into
78  * a range of differing lines. At first the lines are selected as a block.
79  * When navigating into this block the token compare shows for every line
80  * the differing token by selecting them.
81  * <p>
82  * The <code>TextMergeViewer</code>'s default token compare works on characters separated
83  * by whitespace. If a different strategy is needed (for example, Java tokens in
84  * a Java-aware merge viewer), clients can create their own token
85  * comparators by implementing the <code>ITokenComparator</code> interface and overriding the
86  * <code>TextMergeViewer.createTokenComparator</code> factory method).
87  * <p>
88  * Access to the <code>TextMergeViewer</code>'s model is by means of an
89  * <code>IMergeViewerContentProvider</code>. Its <code>get<it>X</it></code>Content</code> methods must return
90  * either an <code>IDocument</code>, an <code>IDocumentRange</code>, or an <code>IStreamContentAccessor</code>.
91  * In the <code>IDocumentRange</code> case the <code>TextMergeViewer</code>
92  * works on a subrange of a document. In the <code>IStreamContentAccessor</code> case
93  * a document is created internally and initialized from the stream.
94  * <p>
95  * A <code>TextMergeViewer</code> can be used as is. However clients may subclass
96  * to customize the behavior. For example a <code>MergeTextViewer</code> for Java would override
97  * the <code>configureTextViewer</code> method to configure the <code>TextViewer</code> for Java source code,
98  * the <code>createTokenComparator</code> method to create a Java specific tokenizer.
99  *
100  * @see org.eclipse.compare.rangedifferencer.RangeDifferencer
101  * @see org.eclipse.jface.text.TextViewer
102  * @see ITokenComparator
103  * @see IDocumentRange
104  * @see org.eclipse.compare.IStreamContentAccessor
105  */

106 public class TextMergeViewer extends ContentMergeViewer implements IAdaptable {
107     
108     private static final String JavaDoc COPY_LEFT_TO_RIGHT_INDICATOR = ">"; //$NON-NLS-1$
109
private static final String JavaDoc COPY_RIGHT_TO_LEFT_INDICATOR = "<"; //$NON-NLS-1$
110
private static final char ANCESTOR_CONTRIBUTOR = MergeViewerContentProvider.ANCESTOR_CONTRIBUTOR;
111     private static final char RIGHT_CONTRIBUTOR = MergeViewerContentProvider.RIGHT_CONTRIBUTOR;
112     private static final char LEFT_CONTRIBUTOR = MergeViewerContentProvider.LEFT_CONTRIBUTOR;
113     
114     private static final String JavaDoc DIFF_RANGE_CATEGORY = CompareUIPlugin.PLUGIN_ID + ".DIFF_RANGE_CATEGORY"; //$NON-NLS-1$
115

116     static final boolean DEBUG= false;
117     
118     private static final boolean FIX_47640= true;
119     
120     private static final String JavaDoc[] GLOBAL_ACTIONS= {
121         ActionFactory.UNDO.getId(),
122         ActionFactory.REDO.getId(),
123         ActionFactory.CUT.getId(),
124         ActionFactory.COPY.getId(),
125         ActionFactory.PASTE.getId(),
126         ActionFactory.DELETE.getId(),
127         ActionFactory.SELECT_ALL.getId(),
128         ActionFactory.SAVE.getId(),
129         ActionFactory.FIND.getId()
130     };
131     private static final String JavaDoc[] TEXT_ACTIONS= {
132         MergeSourceViewer.UNDO_ID,
133         MergeSourceViewer.REDO_ID,
134         MergeSourceViewer.CUT_ID,
135         MergeSourceViewer.COPY_ID,
136         MergeSourceViewer.PASTE_ID,
137         MergeSourceViewer.DELETE_ID,
138         MergeSourceViewer.SELECT_ALL_ID,
139         MergeSourceViewer.SAVE_ID,
140         MergeSourceViewer.FIND_ID
141     };
142                     
143     private static final String JavaDoc BUNDLE_NAME= "org.eclipse.compare.contentmergeviewer.TextMergeViewerResources"; //$NON-NLS-1$
144

145     // the following symbolic constants must match the IDs in Compare's plugin.xml
146
private static final String JavaDoc INCOMING_COLOR= "INCOMING_COLOR"; //$NON-NLS-1$
147
private static final String JavaDoc OUTGOING_COLOR= "OUTGOING_COLOR"; //$NON-NLS-1$
148
private static final String JavaDoc CONFLICTING_COLOR= "CONFLICTING_COLOR"; //$NON-NLS-1$
149
private static final String JavaDoc RESOLVED_COLOR= "RESOLVED_COLOR"; //$NON-NLS-1$
150

151     // constants
152
/** Width of left and right vertical bar */
153     private static final int MARGIN_WIDTH= 6;
154     /** Width of center bar */
155     private static final int CENTER_WIDTH= 34;
156     /** Width of birds eye view */
157     private static final int BIRDS_EYE_VIEW_WIDTH= 12;
158     /** Width of birds eye view */
159     private static final int BIRDS_EYE_VIEW_INSET= 2;
160     /** */
161     private static final int RESOLVE_SIZE= 5;
162     /** if true copying conflicts from one side to other concatenates both sides */
163     private static final boolean APPEND_CONFLICT= true;
164
165     /** line width of change borders */
166     private static final int LW= 1;
167     /** Selects between smartTokenDiff and mergingTokenDiff */
168     private static final boolean USE_MERGING_TOKEN_DIFF= false;
169         
170     // determines whether a change between left and right is considered incoming or outgoing
171
private boolean fLeftIsLocal;
172     private boolean fShowCurrentOnly= false;
173     private boolean fShowCurrentOnly2= false;
174     private int fMarginWidth= MARGIN_WIDTH;
175     private int fTopInset;
176     
177     // Colors
178
private RGB fBackground;
179     private RGB fForeground;
180     private boolean fPollSystemForeground= true;
181     private boolean fPollSystemBackground= true;
182     
183     private RGB SELECTED_INCOMING;
184     private RGB INCOMING;
185     private RGB INCOMING_FILL;
186     private RGB INCOMING_TEXT_FILL;
187     
188     private RGB SELECTED_CONFLICT;
189     private RGB CONFLICT;
190     private RGB CONFLICT_FILL;
191     private RGB CONFLICT_TEXT_FILL;
192     
193     private RGB SELECTED_OUTGOING;
194     private RGB OUTGOING;
195     private RGB OUTGOING_FILL;
196     private RGB OUTGOING_TEXT_FILL;
197     
198     private RGB RESOLVED;
199     
200     private IPreferenceStore fPreferenceStore;
201     private IPropertyChangeListener fPreferenceChangeListener;
202     
203     /** All diffs for calculating scrolling position (includes line ranges without changes) */
204     private ArrayList fAllDiffs;
205     /** Subset of above: just real differences. */
206     private ArrayList fChangeDiffs;
207     /** The current diff */
208     private Diff fCurrentDiff;
209     
210     private HashMap fNewAncestorRanges= new HashMap();
211     private HashMap fNewLeftRanges= new HashMap();
212     private HashMap fNewRightRanges= new HashMap();
213     
214     private MergeSourceViewer fAncestor;
215     private MergeSourceViewer fLeft;
216     private MergeSourceViewer fRight;
217     
218     private int fLeftLineCount;
219     private int fRightLineCount;
220     
221     private boolean fInScrolling;
222     
223     private int fPts[]= new int[8]; // scratch area for polygon drawing
224

225     private int fInheritedDirection; // inherited direction
226
private int fTextDirection; // requested direction for embedded SourceViewer
227

228     private ActionContributionItem fIgnoreAncestorItem;
229     private boolean fHighlightRanges;
230     
231     private boolean fShowPseudoConflicts= false;
232     
233     private boolean fUseSplines= true;
234     private boolean fUseSingleLine= true;
235     private boolean fUseResolveUI= true;
236     private boolean fHighlightTokenChanges = false;
237
238     private String JavaDoc fSymbolicFontName;
239
240     private ActionContributionItem fNextDiff; // goto next difference
241
private ActionContributionItem fPreviousDiff; // goto previous difference
242
private ActionContributionItem fCopyDiffLeftToRightItem;
243     private ActionContributionItem fCopyDiffRightToLeftItem;
244     
245     private CompareHandlerService fHandlerService;
246     
247     private boolean fSynchronizedScrolling= true;
248     private boolean fShowMoreInfo= false;
249     
250     private MergeSourceViewer fFocusPart;
251     
252     private boolean fSubDoc= true;
253     private IPositionUpdater fPositionUpdater;
254     private boolean fIsMotif;
255     private boolean fIsCarbon;
256     
257     private boolean fHasErrors;
258         
259
260     // SWT widgets
261
private BufferedCanvas fAncestorCanvas;
262     private BufferedCanvas fLeftCanvas;
263     private BufferedCanvas fRightCanvas;
264     private Canvas fScrollCanvas;
265     private ScrollBar fVScrollBar;
266     private Canvas fBirdsEyeCanvas;
267     private Canvas fSummaryHeader;
268     private HeaderPainter fHeaderPainter;
269     
270     // SWT resources to be disposed
271
private Map fColors;
272     private Cursor fBirdsEyeCursor;
273                 
274     // points for center curves
275
private double[] fBasicCenterCurve;
276     
277     private Button fCenterButton;
278     private Diff fButtonDiff;
279
280     private ContributorInfo fLeftContributor;
281     private ContributorInfo fRightContributor;
282     private ContributorInfo fAncestorContributor;
283     private boolean isRefreshing;
284     private int fSynchronziedScrollPosition;
285     private ActionContributionItem fNextChange;
286     private ActionContributionItem fPreviousChange;
287     private ShowWhitespaceAction showWhitespaceAction;
288     private InternalOutlineViewerCreator fOutlineViewerCreator;
289     private TextEditorPropertyAction toggleLineNumbersAction;
290     private IFindReplaceTarget fFindReplaceTarget;
291     private ChangePropertyAction fIgnoreWhitespace;
292
293     private final class InternalOutlineViewerCreator extends OutlineViewerCreator implements ISelectionChangedListener {
294         public Viewer findStructureViewer(Viewer oldViewer,
295                 ICompareInput input, Composite parent,
296                 CompareConfiguration configuration) {
297             if (input != getInput())
298                 return null;
299             final Viewer v = CompareUI.findStructureViewer(oldViewer, input, parent, configuration);
300             if (v != null) {
301                 v.getControl().addDisposeListener(new DisposeListener() {
302                     public void widgetDisposed(DisposeEvent e) {
303                         v.removeSelectionChangedListener(InternalOutlineViewerCreator.this);
304                     }
305                 });
306                 v.addSelectionChangedListener(this);
307             }
308                 
309             return v;
310         }
311         
312         public boolean hasViewerFor(Object JavaDoc input) {
313             return true;
314         }
315
316         public void selectionChanged(SelectionChangedEvent event) {
317             ISelection s = event.getSelection();
318             if (s instanceof IStructuredSelection) {
319                 IStructuredSelection ss = (IStructuredSelection) s;
320                 Object JavaDoc element = ss.getFirstElement();
321                 Diff diff = findDiff(element);
322                 if (diff != null)
323                     setCurrentDiff(diff, true);
324             }
325         }
326
327         private Diff findDiff(Object JavaDoc element) {
328             if (element instanceof ICompareInput) {
329                 ICompareInput ci = (ICompareInput) element;
330                 Position p = getPosition(ci.getLeft());
331                 if (p != null)
332                     return findDiff(p, true);
333                 p = getPosition(ci.getRight());
334                 if (p != null)
335                     return findDiff(p, false);
336             }
337             return null;
338         }
339         
340         private Diff findDiff(Position p, boolean left) {
341             for (Iterator iterator = fAllDiffs.iterator(); iterator.hasNext();) {
342                 Diff diff = (Diff) iterator.next();
343                 Position diffPos;
344                 if (left) {
345                     diffPos = diff.fLeftPos;
346                 } else {
347                     diffPos = diff.fRightPos;
348                 }
349                 // If the element falls within a diff, highlight that diff
350
if (diffPos.offset + diffPos.length >= p.offset && diff.fDirection != RangeDifference.NOCHANGE)
351                     return diff;
352                 // Otherwise, highlight the first diff after the elements position
353
if (diffPos.offset >= p.offset)
354                     return diff;
355             }
356             return null;
357         }
358
359         private Position getPosition(ITypedElement left) {
360             if (left instanceof DocumentRangeNode) {
361                 DocumentRangeNode drn = (DocumentRangeNode) left;
362                 return drn.getRange();
363             }
364             return null;
365         }
366
367         public Object JavaDoc getInput() {
368             return TextMergeViewer.this.getInput();
369         }
370     }
371
372     class ContributorInfo implements IElementStateListener, VerifyListener, IDocumentListener {
373         private final TextMergeViewer fViewer;
374         private final Object JavaDoc fElement;
375         private char fLeg;
376         private String JavaDoc fEncoding;
377         private IDocumentProvider fDocumentProvider;
378         private IEditorInput fDocumentKey;
379         private ISelection fSelection;
380         private int fTopIndex = -1;
381         private boolean fNeedsValidation = false;
382         private MergeSourceViewer fSourceViewer;
383         
384         public ContributorInfo(TextMergeViewer viewer, Object JavaDoc element, char leg) {
385             fViewer = viewer;
386             fElement = element;
387             fLeg = leg;
388             if (fElement instanceof IEncodedStreamContentAccessor) {
389                 try {
390                     fEncoding = ((IEncodedStreamContentAccessor)fElement).getCharset();
391                 } catch (CoreException e) {
392                     // silently ignored
393
}
394             }
395         }
396         
397         public String JavaDoc getEncoding() {
398             if (fEncoding == null)
399                 return ResourcesPlugin.getEncoding();
400             return fEncoding;
401         }
402
403         public void setEncodingIfAbsent(ContributorInfo otherContributor) {
404             if (fEncoding == null)
405                 fEncoding = otherContributor.fEncoding;
406         }
407         
408         public IDocument getDocument() {
409             if (fDocumentProvider != null) {
410                 IDocument document = fDocumentProvider.getDocument(getDocumentKey());
411                 if (document != null)
412                     return document;
413             }
414             if (fElement instanceof IDocument)
415                 return (IDocument) fElement;
416             if (fElement instanceof IDocumentRange)
417                 return ((IDocumentRange) fElement).getDocument();
418             if (fElement instanceof IStreamContentAccessor)
419                 return DocumentManager.get(fElement);
420             return null;
421         }
422         
423         public void setDocument(MergeSourceViewer viewer, boolean isEditable) {
424             // Ensure that this method is only called once
425
Assert.isTrue(fSourceViewer == null);
426             fSourceViewer = viewer;
427             try {
428                 internalSetDocument(viewer);
429             } catch (RuntimeException JavaDoc e) {
430                 // The error may be due to a stale entry in the DocumentManager (see bug 184489)
431
clearCachedDocument();
432                 throw e;
433             }
434             viewer.setEditable(isEditable);
435             // Verify changes if the document is editable
436
if (isEditable) {
437                 fNeedsValidation = true;
438                 viewer.getTextWidget().addVerifyListener(this);
439             }
440         }
441         
442         /*
443          * Returns true if a new Document could be installed.
444          */

445         private boolean internalSetDocument(MergeSourceViewer tp) {
446             
447             if (tp == null)
448                 return false;
449             
450             IDocument newDocument = null;
451             Position range= null;
452
453             if (fElement instanceof IDocumentRange) {
454                 newDocument= ((IDocumentRange)fElement).getDocument();
455                 range= ((IDocumentRange)fElement).getRange();
456                 connectToSharedDocument();
457
458             } else if (fElement instanceof IDocument) {
459                 newDocument= (IDocument) fElement;
460                 
461             } else if (fElement instanceof IStreamContentAccessor) {
462                 newDocument= DocumentManager.get(fElement);
463                 if (newDocument == null) {
464                     newDocument = createDocument();
465                     DocumentManager.put(fElement, newDocument);
466                     setupDocument(newDocument);
467                 } else if (fDocumentProvider == null) {
468                     // Connect to a shared document so we can get the proper save synchronization
469
connectToSharedDocument();
470                 }
471             } else if (fElement == null) { // deletion on one side
472

473                 ITypedElement parent= this.fViewer.getParent(fLeg); // we try to find an insertion position within the deletion's parent
474

475                 if (parent instanceof IDocumentRange) {
476                     newDocument= ((IDocumentRange)parent).getDocument();
477                     newDocument.addPositionCategory(DIFF_RANGE_CATEGORY);
478                     Object JavaDoc input= this.fViewer.getInput();
479                     range= this.fViewer.getNewRange(fLeg, input);
480                     if (range == null) {
481                         int pos= 0;
482                         if (input instanceof ICompareInput)
483                             pos= this.fViewer.findInsertionPosition(fLeg, (ICompareInput)input);
484                         range= new Position(pos, 0);
485                         try {
486                             newDocument.addPosition(DIFF_RANGE_CATEGORY, range);
487                         } catch (BadPositionCategoryException ex) {
488                             // silently ignored
489
if (TextMergeViewer.DEBUG) System.out.println("BadPositionCategoryException: " + ex); //$NON-NLS-1$
490
} catch (BadLocationException ex) {
491                             // silently ignored
492
if (TextMergeViewer.DEBUG) System.out.println("BadLocationException: " + ex); //$NON-NLS-1$
493
}
494                         this.fViewer.addNewRange(fLeg, input, range);
495                     }
496                 } else if (parent instanceof IDocument) {
497                     newDocument= ((IDocumentRange)fElement).getDocument();
498                 }
499             }
500
501             boolean enabled= true;
502             if (newDocument == null) {
503                 newDocument= new Document(""); //$NON-NLS-1$
504
enabled= false;
505             }
506             
507             // Update the viewer document or range
508
IDocument oldDoc= tp.getDocument();
509             if (newDocument != oldDoc) {
510                 updateViewerDocument(tp, newDocument, range);
511             } else { // same document but different range
512
updateViewerDocumentRange(tp, range);
513             }
514             newDocument.addDocumentListener(this);
515             
516             tp.setEnabled(enabled);
517
518             return enabled;
519         }
520
521         /*
522          * The viewer document is the same but the range has changed
523          */

524         private void updateViewerDocumentRange(MergeSourceViewer tp, Position range) {
525             tp.setRegion(range);
526             if (this.fViewer.fSubDoc) {
527                 if (range != null) {
528                     IRegion r= this.fViewer.normalizeDocumentRegion(tp.getDocument(), TextMergeViewer.toRegion(range));
529                     tp.setVisibleRegion(r.getOffset(), r.getLength());
530                 } else
531                     tp.resetVisibleRegion();
532             } else
533                 tp.resetVisibleRegion();
534         }
535
536         /*
537          * The viewer has a new document
538          */

539         private void updateViewerDocument(MergeSourceViewer tp, IDocument document, Position range) {
540             unsetDocument(tp);
541             if (document == null)
542                 return;
543             
544             // Add a position updater to the document
545
document.addPositionCategory(DIFF_RANGE_CATEGORY);
546             if (this.fViewer.fPositionUpdater == null)
547                 this.fViewer.fPositionUpdater= this.fViewer.new ChildPositionUpdater(DIFF_RANGE_CATEGORY);
548             else
549                 document.removePositionUpdater(this.fViewer.fPositionUpdater);
550             document.addPositionUpdater(this.fViewer.fPositionUpdater);
551
552             // install new document
553
tp.setRegion(range);
554             if (this.fViewer.fSubDoc) {
555                 if (range != null) {
556                     IRegion r= this.fViewer.normalizeDocumentRegion(document, TextMergeViewer.toRegion(range));
557                     tp.setDocument(document, r.getOffset(), r.getLength());
558                 } else
559                     tp.setDocument(document);
560             } else
561                 tp.setDocument(document);
562                             
563             tp.rememberDocument(document);
564         }
565         
566         private void unsetDocument(MergeSourceViewer tp) {
567             IDocument oldDoc= internalGetDocument(tp);
568             if (oldDoc != null) {
569                 tp.rememberDocument(null);
570                 try {
571                     oldDoc.removePositionCategory(DIFF_RANGE_CATEGORY);
572                 } catch (BadPositionCategoryException ex) {
573                     // Ignore
574
}
575                 if (fPositionUpdater != null)
576                     oldDoc.removePositionUpdater(fPositionUpdater);
577                 oldDoc.removeDocumentListener(this);
578             }
579         }
580         
581         private IDocument createDocument() {
582             // If the content provider is a text content provider, attempt to obtain
583
// a shared document (i.e. file buffer)
584
IDocument newDoc = connectToSharedDocument();
585             
586             if (newDoc == null) {
587                 IStreamContentAccessor sca= (IStreamContentAccessor) fElement;
588                 String JavaDoc s= null;
589
590                 try {
591                     String JavaDoc encoding = getEncoding();
592                     s = Utilities.readString(sca, encoding);
593                 } catch (CoreException ex) {
594                     this.fViewer.setError(fLeg, ex.getMessage());
595                 }
596
597                 newDoc= new Document(s != null ? s : ""); //$NON-NLS-1$
598
}
599             return newDoc;
600         }
601
602         /**
603          * Connect to a shared document if possible. Return <code>null</code>
604          * if the connection was not possible.
605          * @return the shared document or <code>null</code> if connection to a
606          * shared document was not possible
607          */

608         private IDocument connectToSharedDocument() {
609             IEditorInput key = getDocumentKey();
610             if (key != null) {
611                 if (fDocumentProvider != null) {
612                     // We've already connected and setup the document
613
return fDocumentProvider.getDocument(key);
614                 }
615                 IDocumentProvider documentProvider = getDocumentProvider();
616                 if (documentProvider != null) {
617                     try {
618                         connect(documentProvider, key);
619                         setCachedDocumentProvider(key,
620                                 documentProvider);
621                         IDocument newDoc = documentProvider.getDocument(key);
622                         this.fViewer.updateDirtyState(key, documentProvider, fLeg);
623                         return newDoc;
624                     } catch (CoreException e) {
625                         // Connection failed. Log the error and continue without a shared document
626
CompareUIPlugin.log(e);
627                     }
628                 }
629             }
630             return null;
631         }
632         
633         private void connect(IDocumentProvider documentProvider, IEditorInput input) throws CoreException {
634             final ISharedDocumentAdapter sda = (ISharedDocumentAdapter) Utilities.getAdapter(fElement, ISharedDocumentAdapter.class);
635             if (sda != null) {
636                 sda.connect(documentProvider, input);
637             } else {
638                 documentProvider.connect(input);
639             }
640         }
641         
642         private void disconnect(IDocumentProvider provider, IEditorInput input) {
643             final ISharedDocumentAdapter sda = (ISharedDocumentAdapter) Utilities.getAdapter(fElement, ISharedDocumentAdapter.class);
644             if (sda != null) {
645                 sda.disconnect(provider, input);
646             } else {
647                 provider.disconnect(input);
648             }
649         }
650
651         private void setCachedDocumentProvider(IEditorInput key,
652                 IDocumentProvider documentProvider) {
653             fDocumentKey = key;
654             fDocumentProvider = documentProvider;
655             documentProvider.addElementStateListener(this);
656         }
657         
658         public void disconnect() {
659             IDocumentProvider provider = null;
660             IEditorInput input = getDocumentKey();
661             synchronized(this) {
662                 if (fDocumentProvider != null) {
663                     provider = fDocumentProvider;
664                     fDocumentProvider = null;
665                     fDocumentKey = null;
666                 }
667             }
668             if (provider != null) {
669                 disconnect(provider, input);
670                 provider.removeElementStateListener(this);
671             }
672             // If we have a listener registered with the widget, remove it
673
if (fSourceViewer != null && !fSourceViewer.getTextWidget().isDisposed()) {
674                 if (fNeedsValidation) {
675                     fSourceViewer.getTextWidget().removeVerifyListener(this);
676                     fNeedsValidation = false;
677                 }
678                 IDocument oldDoc= internalGetDocument(fSourceViewer);
679                 if (oldDoc != null) {
680                     oldDoc.removeDocumentListener(this);
681                 }
682             }
683             clearCachedDocument();
684         }
685
686         private void clearCachedDocument() {
687             // Finally, remove the document from the document manager
688
IDocument doc = DocumentManager.get(fElement);
689             if (doc != null)
690                 DocumentManager.remove(doc);
691         }
692         
693         private IDocument internalGetDocument(MergeSourceViewer tp) {
694             IDocument oldDoc= tp.getDocument();
695             if (oldDoc == null) {
696                 oldDoc= tp.getRememberedDocument();
697             }
698             return oldDoc;
699         }
700         
701         /**
702          * Return the document key used to obtain a shared document. A <code>null</code>
703          * is returned in the following cases:
704          * <ol>
705          * <li>This contributor does not have a shared document adapter.</li>
706          * <li>This text merge viewer has a document partitioner but uses the default partitioning.</li>
707          * <li>This text merge viewer does not use he default content provider.</li>
708          * </ol>
709          * @return the document key used to obtain a shared document or <code>null</code>
710          */

711         private IEditorInput getDocumentKey() {
712             if (fDocumentKey != null)
713                 return fDocumentKey;
714             if (isUsingDefaultContentProvider() && fElement != null && canHaveSharedDocument()) {
715                 ISharedDocumentAdapter sda = (ISharedDocumentAdapter)Utilities.getAdapter(fElement, ISharedDocumentAdapter.class, true);
716                 if (sda != null) {
717                     return sda.getDocumentKey(fElement);
718                 }
719             }
720             return null;
721         }
722         
723         private IDocumentProvider getDocumentProvider() {
724             if (fDocumentProvider != null)
725                 return fDocumentProvider;
726             // We will only use document providers if the content provider is the
727
// default content provider
728
if (isUsingDefaultContentProvider()) {
729                 IEditorInput input = getDocumentKey();
730                 if (input != null)
731                     return SharedDocumentAdapter.getDocumentProvider(input);
732             }
733             return null;
734         }
735
736         private boolean isUsingDefaultContentProvider() {
737             return fViewer.isUsingDefaultContentProvider();
738         }
739         
740         private boolean canHaveSharedDocument() {
741             return fViewer.canHaveSharedDocument();
742         }
743         
744         boolean hasSharedDocument(Object JavaDoc object) {
745             return (fElement == object &&
746                     fDocumentProvider != null
747                     && fDocumentProvider.getDocument(getDocumentKey()) != null);
748         }
749         
750         public boolean flush() throws CoreException {
751             if (fDocumentProvider != null) {
752                 IEditorInput input = getDocumentKey();
753                 IDocument document = fDocumentProvider.getDocument(input);
754                 if (document != null) {
755                     final ISharedDocumentAdapter sda = (ISharedDocumentAdapter) Utilities.getAdapter(fElement, ISharedDocumentAdapter.class);
756                     if (sda != null) {
757                         sda.flushDocument(fDocumentProvider, input, document, false);
758                         return true;
759                     }
760                     try {
761                         fDocumentProvider.aboutToChange(input);
762                         fDocumentProvider.saveDocument(new NullProgressMonitor(), input, document, false);
763                         return true;
764                     } finally {
765                         fDocumentProvider.changed(input);
766                     }
767                 }
768             }
769             return false;
770         }
771         
772         public void elementMoved(Object JavaDoc originalElement, Object JavaDoc movedElement) {
773             IEditorInput input = getDocumentKey();
774             if (input != null && input.equals(originalElement)) {
775                 // This method will only get called if the buffer is not dirty
776
resetDocument();
777             }
778         }
779         public void elementDirtyStateChanged(Object JavaDoc element, boolean isDirty) {
780             if (!checkState())
781                 return;
782             IEditorInput input = getDocumentKey();
783             if (input != null && input.equals(element)) {
784                 this.fViewer.updateDirtyState(input, getDocumentProvider(), fLeg);
785             }
786         }
787
788         public void elementDeleted(Object JavaDoc element) {
789             IEditorInput input = getDocumentKey();
790             if (input != null && input.equals(element)) {
791                 // This method will only get called if the buffer is not dirty
792
resetDocument();
793             }
794         }
795         private void resetDocument() {
796             // Need to remove the document from the manager before refreshing
797
// or the old document will still be found
798
clearCachedDocument();
799             // TODO: This is fine for now but may need to be revisited if a refresh is performed
800
// higher up as well (e.g. perhaps a refresh request that waits until after all parties
801
// have been notified).
802
if (checkState())
803                 fViewer.refresh();
804         }
805
806         private boolean checkState() {
807             if (fViewer == null)
808                 return false;
809             Control control = fViewer.getControl();
810             if (control == null)
811                 return false;
812             return !control.isDisposed();
813         }
814
815         public void elementContentReplaced(Object JavaDoc element) {
816             if (!checkState())
817                 return;
818             IEditorInput input = getDocumentKey();
819             if (input != null && input.equals(element)) {
820                 this.fViewer.updateDirtyState(input, getDocumentProvider(), fLeg);
821             }
822         }
823         public void elementContentAboutToBeReplaced(Object JavaDoc element) {
824             // Nothing to do
825
}
826
827         public Object JavaDoc getElement() {
828             return fElement;
829         }
830
831         public void cacheSelection(MergeSourceViewer viewer) {
832             if (viewer == null) {
833                 this.fSelection = null;
834                 this.fTopIndex = -1;
835             } else {
836                 this.fSelection = viewer.getSelection();
837                 this.fTopIndex = viewer.getTopIndex();
838             }
839         }
840
841         public void updateSelection(MergeSourceViewer viewer, boolean includeScroll) {
842             if (fSelection != null)
843                 viewer.setSelection(fSelection);
844             if (includeScroll && fTopIndex != -1) {
845                 viewer.setTopIndex(fTopIndex);
846             }
847         }
848
849         public void transferContributorStateFrom(
850                 ContributorInfo oldContributor) {
851             if (oldContributor != null) {
852                 fSelection = oldContributor.fSelection;
853                 fTopIndex = oldContributor.fTopIndex;
854             }
855                 
856         }
857
858         public boolean validateChange() {
859             if (fElement == null)
860                 return true;
861             if (fDocumentProvider instanceof IDocumentProviderExtension) {
862                 IDocumentProviderExtension ext = (IDocumentProviderExtension)fDocumentProvider;
863                 if (ext.isReadOnly(fDocumentKey)) {
864                     try {
865                         ext.validateState(fDocumentKey, getControl().getShell());
866                         ext.updateStateCache(fDocumentKey);
867                     } catch (CoreException e) {
868                         ErrorDialog.openError(getControl().getShell(), CompareMessages.TextMergeViewer_12, CompareMessages.TextMergeViewer_13, e.getStatus());
869                         return false;
870                     }
871                 }
872                 return !ext.isReadOnly(fDocumentKey);
873             }
874             IEditableContentExtension ext = (IEditableContentExtension)Utilities.getAdapter(fElement, IEditableContentExtension.class);
875             if (ext != null) {
876                 if (ext.isReadOnly()) {
877                     IStatus status = ext.validateEdit(getControl().getShell());
878                     if (!status.isOK()) {
879                         if (status.getSeverity() == IStatus.ERROR) {
880                             ErrorDialog.openError(getControl().getShell(), CompareMessages.TextMergeViewer_14, CompareMessages.TextMergeViewer_15, status);
881                             return false;
882                         }
883                         if (status.getSeverity() == IStatus.CANCEL)
884                             return false;
885                     }
886                 }
887             }
888             return true;
889         }
890
891         /* (non-Javadoc)
892          * @see org.eclipse.swt.events.VerifyListener#verifyText(org.eclipse.swt.events.VerifyEvent)
893          */

894         public void verifyText(VerifyEvent e) {
895             if (!validateChange()) {
896                 e.doit= false;
897             }
898         }
899         
900         /* (non-Javadoc)
901          * @see org.eclipse.jface.text.IDocumentListener#documentAboutToBeChanged(org.eclipse.jface.text.DocumentEvent)
902          */

903         public void documentAboutToBeChanged(DocumentEvent e) {
904             // nothing to do
905
}
906         
907         /* (non-Javadoc)
908          * @see org.eclipse.jface.text.IDocumentListener#documentChanged(org.eclipse.jface.text.DocumentEvent)
909          */

910         public void documentChanged(DocumentEvent e) {
911             boolean dirty = true;
912             if (fDocumentProvider != null && fDocumentKey != null) {
913                 dirty = fDocumentProvider.canSaveDocument(fDocumentKey);
914             }
915             TextMergeViewer.this.documentChanged(e, dirty);
916             // Remove our verify listener since the document is now dirty
917
if (fNeedsValidation && fSourceViewer != null && !fSourceViewer.getTextWidget().isDisposed()) {
918                 fSourceViewer.getTextWidget().removeVerifyListener(this);
919                 fNeedsValidation = false;
920             }
921         }
922     }
923     
924     class HeaderPainter implements PaintListener {
925         
926         private static final int INSET= BIRDS_EYE_VIEW_INSET;
927
928         private RGB fIndicatorColor;
929         private Color fSeparatorColor;
930         
931         public HeaderPainter() {
932             fSeparatorColor= fSummaryHeader.getDisplay().getSystemColor(SWT.COLOR_WIDGET_NORMAL_SHADOW);
933         }
934         
935         /*
936          * Returns true on color change
937          */

938         public boolean setColor(RGB color) {
939             RGB oldColor= fIndicatorColor;
940             fIndicatorColor= color;
941             if (color == null)
942                 return oldColor != null;
943             if (oldColor != null)
944                 return !color.equals(oldColor);
945             return true;
946         }
947         
948         private void drawBevelRect(GC gc, int x, int y, int w, int h, Color topLeft, Color bottomRight) {
949             gc.setForeground(topLeft);
950             gc.drawLine(x, y, x + w -1, y);
951             gc.drawLine(x, y, x, y + h -1);
952         
953             gc.setForeground(bottomRight);
954             gc.drawLine(x + w, y, x + w, y + h);
955             gc.drawLine(x, y + h, x + w, y + h);
956         }
957         
958         public void paintControl(PaintEvent e) {
959             
960             Point s= fSummaryHeader.getSize();
961             
962             if (fIndicatorColor != null) {
963                 Display d= fSummaryHeader.getDisplay();
964                 e.gc.setBackground(getColor(d, fIndicatorColor));
965                 int min= Math.min(s.x, s.y)-2*INSET;
966                 Rectangle r= new Rectangle((s.x-min)/2, (s.y-min)/2, min, min);
967                 e.gc.fillRectangle(r);
968                 if (d != null)
969                     drawBevelRect(e.gc, r.x, r.y, r.width -1, r.height -1, d.getSystemColor(SWT.COLOR_WIDGET_NORMAL_SHADOW), d.getSystemColor(SWT.COLOR_WIDGET_HIGHLIGHT_SHADOW));
970
971                 e.gc.setForeground(fSeparatorColor);
972                 e.gc.setLineWidth(0 /* 1 */);
973                 e.gc.drawLine(0+1, s.y-1, s.x-1-1, s.y-1);
974             }
975         }
976     }
977
978     /*
979      * The position updater used to adapt the positions representing
980      * the child document ranges to changes of the parent document.
981      */

982     class ChildPositionUpdater extends DefaultPositionUpdater {
983         
984         /*
985          * Creates the position updated.
986          */

987         protected ChildPositionUpdater(String JavaDoc category) {
988             super(category);
989         }
990         
991         /*
992          * Child document ranges cannot be deleted other then by calling
993          * freeChildDocument.
994          */

995         protected boolean notDeleted() {
996             return true;
997         }
998         
999         /*
1000         * If an insertion happens at a child document's start offset, the
1001         * position is extended rather than shifted. Also, if something is added
1002         * right behind the end of the position, the position is extended rather
1003         * than kept stable.
1004         */

1005        protected void adaptToInsert() {
1006            
1007            if (fPosition == fLeft.getRegion() || fPosition == fRight.getRegion()) {
1008                int myStart= fPosition.offset;
1009                int myEnd= fPosition.offset + fPosition.length;
1010                myEnd= Math.max(myStart, myEnd);
1011                
1012                int yoursStart= fOffset;
1013                int yoursEnd= fOffset + fReplaceLength -1;
1014                yoursEnd= Math.max(yoursStart, yoursEnd);
1015                
1016                if (myEnd < yoursStart)
1017                    return;
1018                
1019                if (myStart <= yoursStart)
1020                    fPosition.length += fReplaceLength;
1021                else
1022                    fPosition.offset += fReplaceLength;
1023            } else {
1024                super.adaptToInsert();
1025            }
1026        }
1027    }
1028    
1029    /*
1030     * A Diff represents synchronized character ranges in two or three Documents.
1031     * The MergeTextViewer uses Diffs to find differences in line and token ranges.
1032     */

1033    /* package */ class Diff {
1034        /** character range in ancestor document */
1035        Position fAncestorPos;
1036        /** character range in left document */
1037        Position fLeftPos;
1038        /** character range in right document */
1039        Position fRightPos;
1040        /** if this is a TokenDiff fParent points to the enclosing LineDiff */
1041        Diff fParent;
1042        /** if Diff has been resolved */
1043        boolean fResolved;
1044        int fDirection;
1045        boolean fIsToken= false;
1046        /** child token diffs */
1047        ArrayList fDiffs;
1048        boolean fIsWhitespace= false;
1049
1050        /*
1051         * Create Diff from two ranges and an optional parent diff.
1052         */

1053        Diff(Diff parent, int dir, IDocument ancestorDoc, Position aRange, int ancestorStart, int ancestorEnd,
1054                             IDocument leftDoc, Position lRange, int leftStart, int leftEnd,
1055                             IDocument rightDoc, Position rRange, int rightStart, int rightEnd) {
1056            fParent= parent != null ? parent : this;
1057            fDirection= dir;
1058            
1059            fLeftPos= createPosition(leftDoc, lRange, leftStart, leftEnd);
1060            fRightPos= createPosition(rightDoc, rRange, rightStart, rightEnd);
1061            if (ancestorDoc != null)
1062                fAncestorPos= createPosition(ancestorDoc, aRange, ancestorStart, ancestorEnd);
1063        }
1064        
1065        Position getPosition(char type) {
1066            switch (type) {
1067            case ANCESTOR_CONTRIBUTOR:
1068                return fAncestorPos;
1069            case LEFT_CONTRIBUTOR:
1070                return fLeftPos;
1071            case RIGHT_CONTRIBUTOR:
1072                return fRightPos;
1073            }
1074            return null;
1075        }
1076        
1077        boolean isInRange(char type, int pos) {
1078            Position p= getPosition(type);
1079            return (pos >= p.offset) && (pos < (p.offset+p.length));
1080        }
1081        
1082        String JavaDoc changeType() {
1083            boolean leftEmpty= fLeftPos.length == 0;
1084            boolean rightEmpty= fRightPos.length == 0;
1085            
1086            if (fDirection == RangeDifference.LEFT) {
1087                if (!leftEmpty && rightEmpty)
1088                    return CompareMessages.TextMergeViewer_changeType_addition;
1089                if (leftEmpty && !rightEmpty)
1090                    return CompareMessages.TextMergeViewer_changeType_deletion;
1091            } else {
1092                if (leftEmpty && !rightEmpty)
1093                    return CompareMessages.TextMergeViewer_changeType_addition;
1094                if (!leftEmpty && rightEmpty)
1095                    return CompareMessages.TextMergeViewer_changeType_deletion;
1096            }
1097            return CompareMessages.TextMergeViewer_changeType_change;
1098        }
1099        
1100        Image getImage() {
1101            int code= Differencer.CHANGE;
1102            switch (fDirection) {
1103            case RangeDifference.RIGHT:
1104                code+= Differencer.LEFT;
1105                break;
1106            case RangeDifference.LEFT:
1107                code+= Differencer.RIGHT;
1108                break;
1109            case RangeDifference.ANCESTOR:
1110            case RangeDifference.CONFLICT:
1111                code+= Differencer.CONFLICTING;
1112                break;
1113            }
1114            if (code != 0)
1115                return getCompareConfiguration().getImage(code);
1116            return null;
1117        }
1118        
1119        Position createPosition(IDocument doc, Position range, int start, int end) {
1120            try {
1121                int l= end-start;
1122                if (range != null) {
1123                    int dl= range.length;
1124                    if (l > dl)
1125                        l= dl;
1126                } else {
1127                    int dl= doc.getLength();
1128                    if (start+l > dl)
1129                        l= dl-start;
1130                }
1131                    
1132                Position p= null;
1133                try {
1134                    p= new Position(start, l);
1135                } catch (RuntimeException JavaDoc ex) {
1136                    p= new Position(0, 0);
1137                }
1138                
1139                try {
1140                    doc.addPosition(DIFF_RANGE_CATEGORY, p);
1141                } catch (BadPositionCategoryException ex) {
1142                    // silently ignored
1143
}
1144                return p;
1145            } catch (BadLocationException ee) {
1146                // silently ignored
1147
}
1148            return null;
1149        }
1150
1151        void add(Diff d) {
1152            if (fDiffs == null)
1153                fDiffs= new ArrayList();
1154            fDiffs.add(d);
1155        }
1156        
1157        boolean isDeleted() {
1158            if (fAncestorPos != null && fAncestorPos.isDeleted())
1159                return true;
1160            return fLeftPos.isDeleted() || fRightPos.isDeleted();
1161        }
1162        
1163        void setResolved(boolean r) {
1164            fResolved= r;
1165            if (r)
1166                fDiffs= null;
1167        }
1168
1169        boolean isResolved() {
1170            if (!fResolved && fDiffs != null) {
1171                Iterator e= fDiffs.iterator();
1172                while (e.hasNext()) {
1173                    Diff d= (Diff) e.next();
1174                    if (!d.isResolved())
1175                        return false;
1176                }
1177                return true;
1178            }
1179            return fResolved;
1180        }
1181        
1182// private boolean isIncoming() {
1183
// switch (fDirection) {
1184
// case RangeDifference.RIGHT:
1185
// if (fLeftIsLocal)
1186
// return true;
1187
// break;
1188
// case RangeDifference.LEFT:
1189
// if (!fLeftIsLocal)
1190
// return true;
1191
// break;
1192
// }
1193
// return false;
1194
// }
1195

1196        private boolean isIncomingOrConflicting() {
1197            switch (fDirection) {
1198            case RangeDifference.RIGHT:
1199                if (fLeftIsLocal)
1200                    return true;
1201                break;
1202            case RangeDifference.LEFT:
1203                if (!fLeftIsLocal)
1204                    return true;
1205                break;
1206            case RangeDifference.CONFLICT:
1207                return true;
1208            }
1209            return false;
1210        }
1211        
1212// private boolean isUnresolvedIncoming() {
1213
// if (fResolved)
1214
// return false;
1215
// return isIncoming();
1216
// }
1217

1218        private boolean isUnresolvedIncomingOrConflicting() {
1219            if (fResolved)
1220                return false;
1221            return isIncomingOrConflicting();
1222        }
1223                
1224        Position getPosition(MergeSourceViewer w) {
1225            if (w == fLeft)
1226                return fLeftPos;
1227            if (w == fRight)
1228                return fRightPos;
1229            if (w == fAncestor)
1230                return fAncestorPos;
1231            return null;
1232        }
1233        
1234        /*
1235         * Returns true if given character range overlaps with this Diff.
1236         */

1237        boolean overlaps(MergeSourceViewer w, int start, int end) {
1238            Position h= getPosition(w);
1239            if (h != null) {
1240                int ds= h.getOffset();
1241                int de= ds + h.getLength();
1242                if ((start < de) && (end >= ds))
1243                    return true;
1244            }
1245            return false;
1246        }
1247                
1248        int getMaxDiffHeight() {
1249            Point region= new Point(0, 0);
1250            int h= fLeft.getLineRange(fLeftPos, region).y;
1251            if (isThreeWay())
1252                h= Math.max(h, fAncestor.getLineRange(fAncestorPos, region).y);
1253            return Math.max(h, fRight.getLineRange(fRightPos, region).y);
1254        }
1255        
1256        int getAncestorHeight() {
1257            Point region= new Point(0, 0);
1258            return fAncestor.getLineRange(fAncestorPos, region).y;
1259        }
1260
1261        int getLeftHeight() {
1262            Point region= new Point(0, 0);
1263            return fLeft.getLineRange(fLeftPos, region).y;
1264        }
1265
1266        int getRightHeight() {
1267            Point region= new Point(0, 0);
1268            return fRight.getLineRange(fRightPos, region).y;
1269        }
1270
1271        public Diff[] getChangeDiffs(MergeSourceViewer viewer, IRegion region) {
1272            if (fDiffs != null && intersectsRegion(viewer, region)) {
1273                List JavaDoc result = new ArrayList();
1274                for (Iterator iterator = fDiffs.iterator(); iterator.hasNext();) {
1275                    Diff diff = (Diff) iterator.next();
1276                    if (diff.intersectsRegion(viewer, region)) {
1277                        result.add(diff);
1278                    }
1279                }
1280                return (Diff[]) result.toArray(new Diff[result.size()]);
1281            }
1282            return new Diff[0];
1283        }
1284
1285        private boolean intersectsRegion(MergeSourceViewer viewer,
1286                IRegion region) {
1287            Position p = getPosition(viewer);
1288            if (p != null)
1289                return p.overlapsWith(region.getOffset(), region.getLength());
1290            return false;
1291        }
1292
1293        public StyleRange getStyleRange(MergeSourceViewer viewer, IRegion region) {
1294            //Color cText = getColor(null, getTextColor());
1295
Color cTextFill = getColor(null, getTextFillColor());
1296            if (cTextFill == null)
1297                return null;
1298            Position p = getPosition(viewer);
1299            int start = p.getOffset();
1300            int length = p.getLength();
1301            // Don't start before the region
1302
if (start < region.getOffset()) {
1303                length = length - (region.getOffset() - start);
1304                start = region.getOffset();
1305            }
1306            // Don't go past the end of the region
1307
int regionEnd = region.getOffset() + region.getLength();
1308            if (start + length > regionEnd) {
1309                length = regionEnd - start;
1310            }
1311            if (length < 0)
1312                return null;
1313            
1314            return new StyleRange(start, length, null, cTextFill);
1315        }
1316        
1317        private RGB getTextFillColor() {
1318            if (isThreeWay() && !isIgnoreAncestor()) {
1319                switch (fDirection) {
1320                case RangeDifference.RIGHT:
1321                    if (fLeftIsLocal)
1322                        return INCOMING_TEXT_FILL;
1323                    return OUTGOING_TEXT_FILL;
1324                case RangeDifference.ANCESTOR:
1325                    return CONFLICT_TEXT_FILL;
1326                case RangeDifference.LEFT:
1327                    if (fLeftIsLocal)
1328                        return OUTGOING_TEXT_FILL;
1329                    return INCOMING_TEXT_FILL;
1330                case RangeDifference.CONFLICT:
1331                    return CONFLICT_TEXT_FILL;
1332                }
1333                return null;
1334            }
1335            return OUTGOING_TEXT_FILL;
1336        }
1337
1338        public boolean hasChildren() {
1339            return fDiffs != null && !fDiffs.isEmpty();
1340        }
1341    }
1342    
1343    private class ChangeHighlighter implements ITextPresentationListener {
1344
1345        private final MergeSourceViewer viewer;
1346
1347        public ChangeHighlighter(MergeSourceViewer viewer) {
1348            this.viewer = viewer;
1349        }
1350
1351        /* (non-Javadoc)
1352         * @see org.eclipse.jface.text.ITextPresentationListener#applyTextPresentation(org.eclipse.jface.text.TextPresentation)
1353         */

1354        public void applyTextPresentation(TextPresentation textPresentation) {
1355            if (!fHighlightTokenChanges)
1356                return;
1357            IRegion region= textPresentation.getExtent();
1358            Diff[] changeDiffs = getChangeDiffs(region);
1359            for (int i = 0; i < changeDiffs.length; i++) {
1360                Diff diff = changeDiffs[i];
1361                StyleRange range = getStyleRange(diff, region);
1362                if (range != null)
1363                    textPresentation.mergeStyleRange(range);
1364            }
1365        }
1366
1367        private StyleRange getStyleRange(Diff diff, IRegion region) {
1368            return diff.getStyleRange(viewer, region);
1369        }
1370
1371        private Diff[] getChangeDiffs(IRegion region) {
1372            if (fChangeDiffs == null)
1373                return new Diff[0];
1374            List JavaDoc intersectingDiffs = new ArrayList();
1375            for (Iterator iterator = fChangeDiffs.iterator(); iterator.hasNext();) {
1376                Diff diff = (Diff) iterator.next();
1377                Diff[] changeDiffs = diff.getChangeDiffs(viewer, region);
1378                for (int i = 0; i < changeDiffs.length; i++) {
1379                    Diff changeDiff = changeDiffs[i];
1380                    intersectingDiffs.add(changeDiff);
1381                }
1382            }
1383            return (Diff[]) intersectingDiffs.toArray(new Diff[intersectingDiffs.size()]);
1384        }
1385        
1386    }
1387    
1388    private class FindReplaceTarget implements IFindReplaceTarget {
1389
1390        public boolean canPerformFind() {
1391            return fFocusPart != null;
1392        }
1393
1394        public int findAndSelect(int widgetOffset, String JavaDoc findString,
1395                boolean searchForward, boolean caseSensitive, boolean wholeWord) {
1396            return fFocusPart.getFindReplaceTarget().findAndSelect(widgetOffset, findString, searchForward, caseSensitive, wholeWord);
1397        }
1398
1399        public Point getSelection() {
1400            return fFocusPart.getFindReplaceTarget().getSelection();
1401        }
1402
1403        public String JavaDoc getSelectionText() {
1404            return fFocusPart.getFindReplaceTarget().getSelectionText();
1405        }
1406
1407        public boolean isEditable() {
1408            return fFocusPart.getFindReplaceTarget().isEditable();
1409        }
1410
1411        public void replaceSelection(String JavaDoc text) {
1412            fFocusPart.getFindReplaceTarget().replaceSelection(text);
1413        }
1414        
1415    }
1416
1417    //---- MergeTextViewer
1418

1419    /**
1420     * Creates a text merge viewer under the given parent control.
1421     *
1422     * @param parent the parent control
1423     * @param configuration the configuration object
1424     */

1425    public TextMergeViewer(Composite parent, CompareConfiguration configuration) {
1426        this(parent, SWT.NULL, configuration);
1427    }
1428    
1429    /**
1430     * Creates a text merge viewer under the given parent control.
1431     *
1432     * @param parent the parent control
1433     * @param style SWT style bits for top level composite of this viewer
1434     * @param configuration the configuration object
1435     */

1436    public TextMergeViewer(Composite parent, int style, CompareConfiguration configuration) {
1437        super(style, ResourceBundle.getBundle(BUNDLE_NAME), configuration);
1438        
1439        int inheritedStyle= parent.getStyle();
1440        if ((inheritedStyle & SWT.LEFT_TO_RIGHT) != 0)
1441            fInheritedDirection= SWT.LEFT_TO_RIGHT;
1442        else if ((inheritedStyle & SWT.RIGHT_TO_LEFT) != 0)
1443            fInheritedDirection= SWT.RIGHT_TO_LEFT;
1444        else
1445            fInheritedDirection= SWT.NONE;
1446        
1447        if ((style & SWT.LEFT_TO_RIGHT) != 0)
1448            fTextDirection= SWT.LEFT_TO_RIGHT;
1449        else if ((style & SWT.RIGHT_TO_LEFT) != 0)
1450            fTextDirection= SWT.RIGHT_TO_LEFT;
1451        else
1452            fTextDirection= SWT.NONE;
1453        
1454        fSymbolicFontName= getClass().getName();
1455        
1456        String JavaDoc platform= SWT.getPlatform();
1457        fIsMotif= "motif".equals(platform); //$NON-NLS-1$
1458
fIsCarbon= "carbon".equals(platform); //$NON-NLS-1$
1459

1460        if (fIsMotif)
1461            fMarginWidth= 0;
1462            
1463        Display display= parent.getDisplay();
1464        
1465        fPreferenceChangeListener= new IPropertyChangeListener() {
1466            public void propertyChange(PropertyChangeEvent event) {
1467                TextMergeViewer.this.handlePropertyChangeEvent(event);
1468            }
1469        };
1470
1471        fPreferenceStore= getCompareConfiguration().getPreferenceStore();
1472        if (fPreferenceStore != null) {
1473            fPreferenceStore.addPropertyChangeListener(fPreferenceChangeListener);
1474            
1475            checkForColorUpdate(display);
1476
1477            fLeftIsLocal= Utilities.getBoolean(getCompareConfiguration(), "LEFT_IS_LOCAL", false); //$NON-NLS-1$
1478
fSynchronizedScrolling= fPreferenceStore.getBoolean(ComparePreferencePage.SYNCHRONIZE_SCROLLING);
1479            fShowMoreInfo= fPreferenceStore.getBoolean(ComparePreferencePage.SHOW_MORE_INFO);
1480            fShowPseudoConflicts= fPreferenceStore.getBoolean(ComparePreferencePage.SHOW_PSEUDO_CONFLICTS);
1481            //fUseSplines= fPreferenceStore.getBoolean(ComparePreferencePage.USE_SPLINES);
1482
fUseSingleLine= fPreferenceStore.getBoolean(ComparePreferencePage.USE_SINGLE_LINE);
1483            fHighlightTokenChanges= fPreferenceStore.getBoolean(ComparePreferencePage.HIGHLIGHT_TOKEN_CHANGES);
1484            //fUseResolveUI= fPreferenceStore.getBoolean(ComparePreferencePage.USE_RESOLVE_UI);
1485
}
1486        
1487        buildControl(parent);
1488        
1489        INavigatable nav= new INavigatable() {
1490            public boolean selectChange(int flag) {
1491                if (flag == INavigatable.FIRST_CHANGE || flag == INavigatable.LAST_CHANGE) {
1492                    selectFirstDiff(flag == INavigatable.FIRST_CHANGE);
1493                    return false;
1494                }
1495                return navigate(flag == INavigatable.NEXT_CHANGE, false, false);
1496            }
1497            public Object JavaDoc getInput() {
1498                return TextMergeViewer.this.getInput();
1499            }
1500            public boolean openSelectedChange() {
1501                return false;
1502            }
1503            public boolean hasChange(int flag) {
1504                return getNextVisibleDiff(flag == INavigatable.NEXT_CHANGE, false) != null;
1505            }
1506        };
1507        fComposite.setData(INavigatable.NAVIGATOR_PROPERTY, nav);
1508        
1509        fBirdsEyeCursor= new Cursor(parent.getDisplay(), SWT.CURSOR_HAND);
1510        
1511        JFaceResources.getFontRegistry().addListener(fPreferenceChangeListener);
1512        JFaceResources.getColorRegistry().addListener(fPreferenceChangeListener);
1513        updateFont();
1514    }
1515    
1516    private void updateFont() {
1517        Font f= JFaceResources.getFont(fSymbolicFontName);
1518        if (f != null) {
1519            if (fAncestor != null)
1520                fAncestor.setFont(f);
1521            if (fLeft != null)
1522                fLeft.setFont(f);
1523            if (fRight != null)
1524                fRight.setFont(f);
1525        }
1526    }
1527    
1528    private void checkForColorUpdate(Display display) {
1529        if (fPollSystemForeground) {
1530            RGB fg= display.getSystemColor(SWT.COLOR_LIST_FOREGROUND).getRGB();
1531            if (fForeground == null || !fg.equals(fForeground)) {
1532                fForeground= fg;
1533                updateColors(display);
1534            }
1535        }
1536        if (fPollSystemBackground) {
1537            RGB bg= display.getSystemColor(SWT.COLOR_LIST_BACKGROUND).getRGB();
1538            if (fBackground == null || !bg.equals(fBackground)) {
1539                fBackground= bg;
1540                updateColors(display);
1541            }
1542        }
1543    }
1544    
1545    /**
1546     * Sets the viewer's background color to the given RGB value.
1547     * If the value is <code>null</code> the system's default background color is used.
1548     * @param background the background color or <code>null</code> to use the system's default background color
1549     * @since 2.0
1550     */

1551    public void setBackgroundColor(RGB background) {
1552        fPollSystemBackground= (background == null);
1553        fBackground= background;
1554        updateColors(null);
1555    }
1556    
1557    private RGB getBackground(Display display) {
1558        if (fBackground != null)
1559            return fBackground;
1560        if (display == null)
1561            display= fComposite.getDisplay();
1562        return display.getSystemColor(SWT.COLOR_LIST_BACKGROUND).getRGB();
1563    }
1564    
1565    /**
1566     * Sets the viewer's foreground color to the given RGB value.
1567     * If the value is <code>null</code> the system's default foreground color is used.
1568     * @param foreground the foreground color or <code>null</code> to use the system's default foreground color
1569     * @since 2.0
1570     */

1571    public void setForegroundColor(RGB foreground) {
1572        fPollSystemForeground= (foreground == null);
1573        fForeground= foreground;
1574        updateColors(null);
1575    }
1576    
1577    private void updateColors(Display display) {
1578        
1579        if (display == null)
1580            display= fComposite.getDisplay();
1581        
1582        Color color= null;
1583        if (fBackground != null)
1584            color= getColor(display, fBackground);
1585        
1586        if (fAncestor != null)
1587            fAncestor.setBackgroundColor(color);
1588        if (fLeft != null)
1589            fLeft.setBackgroundColor(color);
1590        if (fRight != null)
1591            fRight.setBackgroundColor(color);
1592                        
1593        ColorRegistry registry= JFaceResources.getColorRegistry();
1594        
1595        RGB bg= getBackground(display);
1596        SELECTED_INCOMING= registry.getRGB(INCOMING_COLOR);
1597        if (SELECTED_INCOMING == null)
1598            SELECTED_INCOMING= new RGB(0, 0, 255); // BLUE
1599
INCOMING= interpolate(SELECTED_INCOMING, bg, 0.6);
1600        INCOMING_FILL= interpolate(SELECTED_INCOMING, bg, 0.97);
1601        INCOMING_TEXT_FILL= interpolate(SELECTED_INCOMING, bg, 0.85);
1602
1603        SELECTED_OUTGOING= registry.getRGB(OUTGOING_COLOR);
1604        if (SELECTED_OUTGOING == null)
1605            SELECTED_OUTGOING= new RGB(0, 0, 0); // BLACK
1606
OUTGOING= interpolate(SELECTED_OUTGOING, bg, 0.6);
1607        OUTGOING_FILL= interpolate(SELECTED_OUTGOING, bg, 0.97);
1608        OUTGOING_TEXT_FILL= interpolate(SELECTED_OUTGOING, bg, 0.85);
1609        
1610        SELECTED_CONFLICT= registry.getRGB(CONFLICTING_COLOR);
1611        if (SELECTED_CONFLICT == null)
1612            SELECTED_CONFLICT= new RGB(255, 0, 0); // RED
1613
CONFLICT= interpolate(SELECTED_CONFLICT, bg, 0.6);
1614        CONFLICT_FILL= interpolate(SELECTED_CONFLICT, bg, 0.97);
1615        CONFLICT_TEXT_FILL= interpolate(SELECTED_CONFLICT, bg, 0.85);
1616    
1617        RESOLVED= registry.getRGB(RESOLVED_COLOR);
1618        if (RESOLVED == null)
1619            RESOLVED= new RGB(0, 255, 0); // GREEN
1620

1621        updatePresentation(display);
1622    }
1623
1624    private void updatePresentation(Display display) {
1625        if (display == null)
1626            display= fComposite.getDisplay();
1627        refreshBirdsEyeView();
1628        invalidateLines();
1629        updateAllDiffBackgrounds(display);
1630        invalidateTextPresentation();
1631    }
1632    
1633    /**
1634     * Invalidates the current presentation by invalidating the three text viewers.
1635     * @since 2.0
1636     */

1637    public void invalidateTextPresentation() {
1638        if (fAncestor != null)
1639            fAncestor.invalidateTextPresentation();
1640        if (fLeft != null)
1641            fLeft.invalidateTextPresentation();
1642        if (fRight != null)
1643            fRight.invalidateTextPresentation();
1644    }
1645    
1646    /**
1647     * Configures the passed text viewer. This method is called after the three
1648     * text viewers have been created for the content areas. The
1649     * <code>TextMergeViewer</code> implementation of this method will
1650     * configure the viewer with a {@link SourceViewerConfiguration}.
1651     * Subclasses may reimplement to provide a specific configuration for the
1652     * text viewer.
1653     *
1654     * @param textViewer
1655     * the text viewer to configure
1656     */

1657    protected void configureTextViewer(TextViewer textViewer) {
1658        // to get undo for all text files
1659
// bugzilla 131895, 33665
1660
if(textViewer instanceof MergeSourceViewer){
1661            SourceViewerConfiguration configuration= new SourceViewerConfiguration();
1662            ((MergeSourceViewer)textViewer).configure(configuration);
1663        }
1664    }
1665                
1666    /**
1667     * Creates an <code>ITokenComparator</code> which is used to show the
1668     * intra line differences.
1669     * The <code>TextMergeViewer</code> implementation of this method returns a
1670     * tokenizer that breaks a line into words separated by whitespace.
1671     * Subclasses may reimplement to provide a specific tokenizer.
1672     * @param line the line for which to create the <code>ITokenComparator</code>
1673     * @return a ITokenComparator which is used for a second level token compare.
1674     */

1675    protected ITokenComparator createTokenComparator(String JavaDoc line) {
1676        return new TokenComparator(line);
1677    }
1678    
1679    /**
1680     * Setup the given document for use with this viewer. By default,
1681     * the partitioner returned from {@link #getDocumentPartitioner()}
1682     * is registered as the default partitioner for the document.
1683     * Subclasses that return a partitioner must also override
1684     * {@link #getDocumentPartitioning()} if they wish to be able to use shared
1685     * documents (i.e. file buffers).
1686     * @param document the document to be set up
1687     *
1688     * @since 3.3
1689     */

1690    protected void setupDocument(IDocument document) {
1691        String JavaDoc partitioning = getDocumentPartitioning();
1692        if (partitioning == null || !(document instanceof IDocumentExtension3)) {
1693            if (document.getDocumentPartitioner() == null) {
1694                IDocumentPartitioner partitioner= getDocumentPartitioner();
1695                if (partitioner != null) {
1696                    document.setDocumentPartitioner(partitioner);
1697                    partitioner.connect(document);
1698                }
1699            }
1700        } else {
1701            IDocumentExtension3 ex3 = (IDocumentExtension3) document;
1702            if (ex3.getDocumentPartitioner(partitioning) == null) {
1703                IDocumentPartitioner partitioner= getDocumentPartitioner();
1704                if (partitioner != null) {
1705                    ex3.setDocumentPartitioner(partitioning, partitioner);
1706                    partitioner.connect(document);
1707                }
1708            }
1709        }
1710    }
1711    
1712    /**
1713     * Returns a document partitioner which is suitable for the underlying content type.
1714     * This method is only called if the input provided by the content provider is a
1715     * <code>IStreamContentAccessor</code> and an internal document must be obtained. This
1716     * document is initialized with the partitioner returned from this method.
1717     * <p>
1718     * The <code>TextMergeViewer</code> implementation of this method returns
1719     * <code>null</code>. Subclasses may reimplement to create a partitioner for a
1720     * specific content type. Subclasses that do return a partitioner should also
1721     * return a partitioning from {@link #getDocumentPartitioning()} in order to make
1722     * use of shared documents (e.g. file buffers).
1723     *
1724     * @return a document partitioner, or <code>null</code>
1725     */

1726    protected IDocumentPartitioner getDocumentPartitioner() {
1727        return null;
1728    }
1729    
1730    /**
1731     * Return the partitioning to which the partitioner returned from
1732     * {@link #getDocumentPartitioner()} is to be associated. Return <code>null</code>
1733     * only if partitioning is not needed or if the subclass
1734     * overrode {@link #setupDocument(IDocument)} directly.
1735     * By default, <code>null</code> is returned which means that shared
1736     * documents that return a partitioner from {@link #getDocumentPartitioner()}
1737     * will not be able to use shared documents.
1738     * @see IDocumentExtension3
1739     * @return a partitioning
1740     *
1741     * @since 3.3
1742     */

1743    protected String JavaDoc getDocumentPartitioning() {
1744        return null;
1745    }
1746    
1747    /**
1748     * Called on the viewer disposal.
1749     * Unregisters from the compare configuration.
1750     * Clients may extend if they have to do additional cleanup.
1751     * @param event
1752     */

1753    protected void handleDispose(DisposeEvent event) {
1754        
1755        if (fHandlerService != null)
1756            fHandlerService.dispose();
1757        
1758        Object JavaDoc input= getInput();
1759        removeFromDocumentManager(ANCESTOR_CONTRIBUTOR, input);
1760        removeFromDocumentManager(LEFT_CONTRIBUTOR, input);
1761        removeFromDocumentManager(RIGHT_CONTRIBUTOR, input);
1762        
1763        if (DEBUG)
1764            DocumentManager.dump();
1765
1766        if (fPreferenceChangeListener != null) {
1767            JFaceResources.getFontRegistry().removeListener(fPreferenceChangeListener);
1768            JFaceResources.getColorRegistry().removeListener(fPreferenceChangeListener);
1769            if (fPreferenceStore != null)
1770                fPreferenceStore.removePropertyChangeListener(fPreferenceChangeListener);
1771            fPreferenceChangeListener= null;
1772        }
1773        
1774        fLeftCanvas= null;
1775        fRightCanvas= null;
1776        fVScrollBar= null;
1777        fBirdsEyeCanvas= null;
1778        fSummaryHeader= null;
1779
1780        fAncestorContributor.unsetDocument(fAncestor);
1781        fLeftContributor.unsetDocument(fLeft);
1782        fRightContributor.unsetDocument(fRight);
1783        
1784        disconnect(fLeftContributor);
1785        disconnect(fRightContributor);
1786        disconnect(fAncestorContributor);
1787        
1788        if (fColors != null) {
1789            Iterator i= fColors.values().iterator();
1790            while (i.hasNext()) {
1791                Color color= (Color) i.next();
1792                if (!color.isDisposed())
1793                    color.dispose();
1794            }
1795            fColors= null;
1796        }
1797        
1798        if (fBirdsEyeCursor != null) {
1799            fBirdsEyeCursor.dispose();
1800            fBirdsEyeCursor= null;
1801        }
1802        
1803        if (showWhitespaceAction != null)
1804            showWhitespaceAction.dispose();
1805        
1806        if (toggleLineNumbersAction != null)
1807            toggleLineNumbersAction.dispose();
1808        
1809        if (fIgnoreWhitespace != null)
1810            fIgnoreWhitespace.dispose();
1811        
1812        super.handleDispose(event);
1813    }
1814                                
1815    private void disconnect(ContributorInfo legInfo) {
1816        if (legInfo != null)
1817            legInfo.disconnect();
1818    }
1819
1820    //-------------------------------------------------------------------------------------------------------------
1821
//--- internal ------------------------------------------------------------------------------------------------
1822
//-------------------------------------------------------------------------------------------------------------
1823

1824    /*
1825     * Creates the specific SWT controls for the content areas.
1826     * Clients must not call or override this method.
1827     */

1828    protected void createControls(Composite composite) {
1829        
1830        PlatformUI.getWorkbench().getHelpSystem().setHelp(composite, ICompareContextIds.TEXT_MERGE_VIEW);
1831        
1832        // 1st row
1833
if (fMarginWidth > 0) {
1834            fAncestorCanvas= new BufferedCanvas(composite, SWT.NONE) {
1835                public void doPaint(GC gc) {
1836                    paintSides(gc, fAncestor, fAncestorCanvas, false);
1837                }
1838            };
1839            fAncestorCanvas.addMouseListener(
1840                new MouseAdapter() {
1841                    public void mouseDown(MouseEvent e) {
1842                        setCurrentDiff2(handleMouseInSides(fAncestorCanvas, fAncestor, e.y), false);
1843                    }
1844                }
1845            );
1846        }
1847                                    
1848        fAncestor= createPart(composite);
1849        fAncestor.setEditable(false);
1850        fAncestor.getTextWidget().getAccessible().addAccessibleListener(new AccessibleAdapter() {
1851            public void getName(AccessibleEvent e) {
1852                e.result = NLS.bind(CompareMessages.TextMergeViewer_accessible_ancestor, getCompareConfiguration().getAncestorLabel(getInput()));
1853            }
1854        });
1855        fAncestor.addTextPresentationListener(new ChangeHighlighter(fAncestor));
1856        
1857        fSummaryHeader= new Canvas(composite, SWT.NONE);
1858        fHeaderPainter= new HeaderPainter();
1859        fSummaryHeader.addPaintListener(fHeaderPainter);
1860        updateResolveStatus();
1861                
1862        // 2nd row
1863
if (fMarginWidth > 0) {
1864            fLeftCanvas= new BufferedCanvas(composite, SWT.NONE) {
1865                public void doPaint(GC gc) {
1866                    paintSides(gc, fLeft, fLeftCanvas, false);
1867                }
1868            };
1869            fLeftCanvas.addMouseListener(
1870                new MouseAdapter() {
1871                    public void mouseDown(MouseEvent e) {
1872                        setCurrentDiff2(handleMouseInSides(fLeftCanvas, fLeft, e.y), false);
1873                    }
1874                }
1875            );
1876        }
1877        
1878        fLeft= createPart(composite);
1879        fLeft.getTextWidget().getVerticalBar().setVisible(!fSynchronizedScrolling);
1880        fLeft.addAction(MergeSourceViewer.SAVE_ID, fLeftSaveAction);
1881        fLeft.getTextWidget().getAccessible().addAccessibleListener(new AccessibleAdapter() {
1882            public void getName(AccessibleEvent e) {
1883                e.result = NLS.bind(CompareMessages.TextMergeViewer_accessible_left, getCompareConfiguration().getLeftLabel(getInput()));
1884            }
1885        });
1886        fLeft.addTextPresentationListener(new ChangeHighlighter(fLeft));
1887        
1888        fRight= createPart(composite);
1889        fRight.getTextWidget().getVerticalBar().setVisible(!fSynchronizedScrolling);
1890        fRight.addAction(MergeSourceViewer.SAVE_ID, fRightSaveAction);
1891        fRight.getTextWidget().getAccessible().addAccessibleListener(new AccessibleAdapter() {
1892            public void getName(AccessibleEvent e) {
1893                e.result = NLS.bind(CompareMessages.TextMergeViewer_accessible_right, getCompareConfiguration().getRightLabel(getInput()));
1894            }
1895        });
1896        fRight.addTextPresentationListener(new ChangeHighlighter(fRight));
1897        
1898        hsynchViewport(fAncestor, fLeft, fRight);
1899        hsynchViewport(fLeft, fAncestor, fRight);
1900        hsynchViewport(fRight, fAncestor, fLeft);
1901
1902        if (fMarginWidth > 0) {
1903            fRightCanvas= new BufferedCanvas(composite, SWT.NONE) {
1904                public void doPaint(GC gc) {
1905                    paintSides(gc, fRight, fRightCanvas, fSynchronizedScrolling);
1906                }
1907            };
1908            fRightCanvas.addMouseListener(
1909                new MouseAdapter() {
1910                    public void mouseDown(MouseEvent e) {
1911                        setCurrentDiff2(handleMouseInSides(fRightCanvas, fRight, e.y), false);
1912                    }
1913                }
1914            );
1915        }
1916        
1917        fScrollCanvas= new Canvas(composite, SWT.V_SCROLL);
1918        Rectangle trim= fLeft.getTextWidget().computeTrim(0, 0, 0, 0);
1919        fTopInset= trim.y;
1920        
1921        fVScrollBar= fScrollCanvas.getVerticalBar();
1922        fVScrollBar.setIncrement(1);
1923        fVScrollBar.setVisible(true);
1924        fVScrollBar.addListener(SWT.Selection,
1925            new Listener() {
1926                public void handleEvent(Event e) {
1927                    int vpos= ((ScrollBar)e.widget).getSelection();
1928                    synchronizedScrollVertical(vpos);
1929                }
1930            }
1931        );
1932        
1933        fBirdsEyeCanvas= new BufferedCanvas(composite, SWT.NONE) {
1934            public void doPaint(GC gc) {
1935                paintBirdsEyeView(this, gc);
1936            }
1937        };
1938        fBirdsEyeCanvas.addMouseListener(
1939            new MouseAdapter() {
1940                public void mouseDown(MouseEvent e) {
1941                    setCurrentDiff2(handlemouseInBirdsEyeView(fBirdsEyeCanvas, e.y), true);
1942                }
1943            }
1944        );
1945        fBirdsEyeCanvas.addMouseMoveListener(
1946            new MouseMoveListener() {
1947                
1948                private Cursor fLastCursor;
1949                
1950                public void mouseMove(MouseEvent e) {
1951                    Cursor cursor= null;
1952                    Diff diff= handlemouseInBirdsEyeView(fBirdsEyeCanvas, e.y);
1953                    if (diff != null && diff.fDirection != RangeDifference.NOCHANGE)
1954                        cursor= fBirdsEyeCursor;
1955                    if (fLastCursor != cursor) {
1956                        fBirdsEyeCanvas.setCursor(cursor);
1957                        fLastCursor= cursor;
1958                    }
1959                }
1960            }
1961        );
1962    }
1963    
1964    private void hsynchViewport(final TextViewer tv1, final TextViewer tv2, final TextViewer tv3) {
1965        final StyledText st1= tv1.getTextWidget();
1966        final StyledText st2= tv2.getTextWidget();
1967        final StyledText st3= tv3.getTextWidget();
1968        final ScrollBar sb1= st1.getHorizontalBar();
1969        sb1.addSelectionListener(new SelectionAdapter() {
1970            public void widgetSelected(SelectionEvent e) {
1971                if (fSynchronizedScrolling) {
1972                    int max= sb1.getMaximum()-sb1.getThumb();
1973                    double v= 0.0;
1974                    if (max > 0)
1975                        v= (float)sb1.getSelection() / (float)max;
1976                    if (st2.isVisible()) {
1977                        ScrollBar sb2= st2.getHorizontalBar();
1978                        st2.setHorizontalPixel((int)((sb2.getMaximum()-sb2.getThumb()) * v));
1979                    }
1980                    if (st3.isVisible()) {
1981                        ScrollBar sb3= st3.getHorizontalBar();
1982                        st3.setHorizontalPixel((int)((sb3.getMaximum()-sb3.getThumb()) * v));
1983                    }
1984                    workaround65205();
1985                }
1986            }
1987        });
1988    }
1989
1990    /**
1991     * A workaround for bug #65205.
1992     * On MacOS X a Display.update() is required to flush pending paint requests after
1993     * programmatically scrolling.
1994     */

1995    private void workaround65205() {
1996        if (fIsCarbon && fComposite != null && !fComposite.isDisposed())
1997            fComposite.getDisplay().update();
1998    }
1999
2000    private void setCurrentDiff2(Diff diff, boolean reveal) {
2001        if (diff != null && diff.fDirection != RangeDifference.NOCHANGE) {
2002            //fCurrentDiff= null;
2003
setCurrentDiff(diff, reveal);
2004        }
2005    }
2006    
2007    private Diff handleMouseInSides(Canvas canvas, MergeSourceViewer tp, int my) {
2008
2009        int lineHeight= tp.getTextWidget().getLineHeight();
2010        int visibleHeight= tp.getViewportHeight();
2011
2012        if (! fHighlightRanges)
2013            return null;
2014
2015        if (fChangeDiffs != null) {
2016            int shift= tp.getVerticalScrollOffset() + (2-LW);
2017
2018            Point region= new Point(0, 0);
2019            Iterator e= fChangeDiffs.iterator();
2020            while (e.hasNext()) {
2021                Diff diff= (Diff) e.next();
2022                if (diff.isDeleted())
2023                    continue;
2024
2025                if (fShowCurrentOnly2 && !isCurrentDiff(diff))
2026                    continue;
2027
2028                tp.getLineRange(diff.getPosition(tp), region);
2029                int y= (region.x * lineHeight) + shift;
2030                int h= region.y * lineHeight;
2031
2032                if (y+h < 0)
2033                    continue;
2034                if (y >= visibleHeight)
2035                    break;
2036                    
2037                if (my >= y && my < y+h)
2038                    return diff;
2039            }
2040        }
2041        return null;
2042    }
2043    
2044    private Diff getDiffUnderMouse(Canvas canvas, int mx, int my, Rectangle r) {
2045
2046        if (! fSynchronizedScrolling)
2047            return null;
2048
2049        int lineHeight= fLeft.getTextWidget().getLineHeight();
2050        int visibleHeight= fRight.getViewportHeight();
2051
2052        Point size= canvas.getSize();
2053        int w= size.x;
2054
2055        if (! fHighlightRanges)
2056            return null;
2057
2058        if (fChangeDiffs != null) {
2059            int lshift= fLeft.getVerticalScrollOffset();
2060            int rshift= fRight.getVerticalScrollOffset();
2061
2062            Point region= new Point(0, 0);
2063
2064            Iterator e= fChangeDiffs.iterator();
2065            while (e.hasNext()) {
2066                Diff diff= (Diff) e.next();
2067                if (diff.isDeleted())
2068                    continue;
2069
2070                if (fShowCurrentOnly2 && !isCurrentDiff(diff))
2071                    continue;
2072
2073                fLeft.getLineRange(diff.fLeftPos, region);
2074                int ly= (region.x * lineHeight) + lshift;
2075                int lh= region.y * lineHeight;
2076
2077                fRight.getLineRange(diff.fRightPos, region);
2078                int ry= (region.x * lineHeight) + rshift;
2079                int rh= region.y * lineHeight;
2080
2081                if (Math.max(ly+lh, ry+rh) < 0)
2082                    continue;
2083                if (Math.min(ly, ry) >= visibleHeight)
2084                    break;
2085
2086                int cx= (w-RESOLVE_SIZE)/2;
2087                int cy= ((ly+lh/2) + (ry+rh/2) - RESOLVE_SIZE)/2;
2088                if (my >= cy && my < cy+RESOLVE_SIZE && mx >= cx && mx < cx+RESOLVE_SIZE) {
2089                    if (r != null) {
2090                        int SIZE= fIsCarbon ? 30 : 20;
2091                        r.x= cx+(RESOLVE_SIZE-SIZE)/2;
2092                        r.y= cy+(RESOLVE_SIZE-SIZE)/2;
2093                        r.width= SIZE;
2094                        r.height= SIZE;
2095                    }
2096                    return diff;
2097                }
2098            }
2099        }
2100        return null;
2101    }
2102
2103    private Diff handlemouseInBirdsEyeView(Canvas canvas, int my) {
2104        int yy, hh;
2105        
2106        Point size= canvas.getSize();
2107        
2108        int virtualHeight= fSynchronizedScrolling ? getVirtualHeight() : getRightHeight();
2109        if (virtualHeight < getViewportHeight())
2110            return null;
2111        
2112        int y= 0;
2113        if (fAllDiffs != null) {
2114            Iterator e= fAllDiffs.iterator();
2115            for (int i= 0; e.hasNext(); i++) {
2116                Diff diff= (Diff) e.next();
2117                int h= fSynchronizedScrolling ? diff.getMaxDiffHeight()
2118                                              : diff.getRightHeight();
2119                if (useChange(diff.fDirection) && !diff.fIsWhitespace) {
2120                                    
2121                    yy= (y*size.y)/virtualHeight;
2122                    hh= (h*size.y)/virtualHeight;
2123                    if (hh < 3)
2124                        hh= 3;
2125                        
2126                    if (my >= yy && my < yy+hh)
2127                        return diff;
2128                }
2129                y+= h;
2130            }
2131        }
2132        return null;
2133    }
2134    
2135    private void paintBirdsEyeView(Canvas canvas, GC gc) {
2136        
2137        Color c;
2138        Rectangle r= new Rectangle(0, 0, 0, 0);
2139        int yy, hh;
2140        
2141        Point size= canvas.getSize();
2142        
2143        int virtualHeight= fSynchronizedScrolling ? getVirtualHeight() : getRightHeight();
2144        if (virtualHeight < getViewportHeight())
2145            return;
2146                
2147        Display display= canvas.getDisplay();
2148        int y= 0;
2149        if (fAllDiffs != null) {
2150            Iterator e= fAllDiffs.iterator();
2151            for (int i= 0; e.hasNext(); i++) {
2152                Diff diff= (Diff) e.next();
2153                int h= fSynchronizedScrolling ? diff.getMaxDiffHeight()
2154                                              : diff.getRightHeight();
2155                                
2156                if (useChange(diff.fDirection) && !diff.fIsWhitespace) {
2157                    
2158                    yy= (y*size.y)/virtualHeight;
2159                    hh= (h*size.y)/virtualHeight;
2160                    if (hh < 3)
2161                        hh= 3;
2162                    
2163                    c= getColor(display, getFillColor(diff));
2164                    if (c != null) {
2165                        gc.setBackground(c);
2166                        gc.fillRectangle(BIRDS_EYE_VIEW_INSET, yy, size.x-(2*BIRDS_EYE_VIEW_INSET),hh);
2167                    }
2168                    c= getColor(display, getStrokeColor(diff));
2169                    if (c != null) {
2170                        gc.setForeground(c);
2171                        r.x= BIRDS_EYE_VIEW_INSET;
2172                        r.y= yy;
2173                        r.width= size.x-(2*BIRDS_EYE_VIEW_INSET)-1;
2174                        r.height= hh;
2175                        if (diff == fCurrentDiff ||
2176                                (fCurrentDiff != null && diff == fCurrentDiff.fParent)) {
2177                            gc.setLineWidth(2);
2178                            r.x++;
2179                            r.y++;
2180                            r.width--;
2181                            r.height--;
2182                        } else {
2183                            gc.setLineWidth(0 /* 1 */);
2184                        }
2185                        gc.drawRectangle(r);
2186                    }
2187                }
2188                
2189                y+= h;
2190            }
2191        }
2192    }
2193    
2194    private void refreshBirdsEyeView() {
2195        if (fBirdsEyeCanvas != null)
2196            fBirdsEyeCanvas.redraw();
2197    }
2198    
2199    /**
2200     * Override to give focus to the pane that previously had focus or to a suitable
2201     * default pane.
2202     * @see org.eclipse.compare.contentmergeviewer.ContentMergeViewer#handleSetFocus()
2203     * @since 3.3
2204     */

2205    protected boolean handleSetFocus() {
2206        if (fFocusPart == null) {
2207            if (fLeft != null && fLeft.getEnabled()) {
2208                fFocusPart= fLeft;
2209            } else if (fRight != null && fRight.getEnabled()) {
2210                fFocusPart= fRight;
2211            } else if (fAncestor != null && fAncestor.getEnabled()) {
2212                fFocusPart= fAncestor;
2213            }
2214        }
2215        if (fFocusPart != null) {
2216            StyledText st= fFocusPart.getTextWidget();
2217            if (st != null)
2218                return st.setFocus();
2219        }
2220        return false; // could not set focus
2221
}
2222    
2223    
2224    class HoverResizer extends Resizer {
2225        Canvas fCanvas;
2226        public HoverResizer(Canvas c, int dir) {
2227            super(c, dir);
2228            fCanvas= c;
2229        }
2230        public void mouseMove(MouseEvent e) {
2231            if (!fIsDown && fUseSingleLine && showResolveUI() && handleMouseMoveOverCenter(fCanvas, e.x, e.y))
2232                return;
2233            super.mouseMove(e);
2234        }
2235    }
2236    
2237    /* (non-Javadoc)
2238     * @see org.eclipse.compare.contentmergeviewer.ContentMergeViewer#createCenterControl(org.eclipse.swt.widgets.Composite)
2239     */

2240    protected final Control createCenterControl(Composite parent) {
2241        if (fSynchronizedScrolling) {
2242            final Canvas canvas= new BufferedCanvas(parent, SWT.NONE) {
2243                public void doPaint(GC gc) {
2244                    paintCenter(this, gc);
2245                }
2246            };
2247            if (fUseResolveUI) {
2248                
2249                new HoverResizer(canvas, HORIZONTAL);
2250                                
2251                fCenterButton= new Button(canvas, fIsCarbon ? SWT.FLAT : SWT.PUSH);
2252                if (fNormalCursor == null) fNormalCursor= new Cursor(canvas.getDisplay(), SWT.CURSOR_ARROW);
2253                fCenterButton.setCursor(fNormalCursor);
2254                fCenterButton.setText(COPY_RIGHT_TO_LEFT_INDICATOR);
2255                fCenterButton.pack();
2256                fCenterButton.setVisible(false);
2257                fCenterButton.addSelectionListener(
2258                    new SelectionAdapter() {
2259                        public void widgetSelected(SelectionEvent e) {
2260                            fCenterButton.setVisible(false);
2261                            if (fButtonDiff != null) {
2262                                setCurrentDiff(fButtonDiff, false);
2263                                copy(fCurrentDiff,
2264                                        fCenterButton.getText().equals(COPY_LEFT_TO_RIGHT_INDICATOR),
2265                                        fCurrentDiff.fDirection != RangeDifference.CONFLICT);
2266                            }
2267                        }
2268                    }
2269                );
2270            } else {
2271                new Resizer(canvas, HORIZONTAL);
2272            }
2273            
2274            return canvas;
2275        }
2276        return super.createCenterControl(parent);
2277    }
2278    
2279    private boolean handleMouseMoveOverCenter(Canvas canvas, int x, int y) {
2280        Rectangle r= new Rectangle(0, 0, 0, 0);
2281        Diff diff= getDiffUnderMouse(canvas, x, y, r);
2282        if (diff != null && !diff.isUnresolvedIncomingOrConflicting())
2283            diff= null;
2284        if (diff != fButtonDiff) {
2285            if (diff != null) {
2286                if (fLeft.isEditable()) {
2287                    fButtonDiff= diff;
2288                    fCenterButton.setText(COPY_RIGHT_TO_LEFT_INDICATOR);
2289                    String JavaDoc tt= fCopyDiffRightToLeftItem.getAction().getToolTipText();
2290                    fCenterButton.setToolTipText(tt);
2291                    fCenterButton.setBounds(r);
2292                    fCenterButton.setVisible(true);
2293                } else if (fRight.isEditable()) {
2294                    fButtonDiff= diff;
2295                    fCenterButton.setText(COPY_LEFT_TO_RIGHT_INDICATOR);
2296                    String JavaDoc tt= fCopyDiffLeftToRightItem.getAction().getToolTipText();
2297                    fCenterButton.setToolTipText(tt);
2298                    fCenterButton.setBounds(r);
2299                    fCenterButton.setVisible(true);
2300                } else
2301                    fButtonDiff= null;
2302            } else {
2303                fCenterButton.setVisible(false);
2304                fButtonDiff= null;
2305            }
2306        }
2307        return fButtonDiff != null;
2308    }
2309    
2310    /* (non-Javadoc)
2311     * @see org.eclipse.compare.contentmergeviewer.ContentMergeViewer#getCenterWidth()
2312     */

2313    protected final int getCenterWidth() {
2314        if (fSynchronizedScrolling)
2315            return CENTER_WIDTH;
2316        return super.getCenterWidth();
2317    }
2318
2319    private int getDirection() {
2320        switch (fTextDirection) {
2321        case SWT.LEFT_TO_RIGHT:
2322        case SWT.RIGHT_TO_LEFT:
2323            if (fInheritedDirection == fTextDirection)
2324                return SWT.NONE;
2325            return fTextDirection;
2326        }
2327        return fInheritedDirection;
2328    }
2329    
2330    /*
2331     * Creates and initializes a text part.
2332     */

2333    private MergeSourceViewer createPart(Composite parent) {
2334        
2335        final MergeSourceViewer part= new MergeSourceViewer(parent, getDirection(), getResourceBundle(), getCompareConfiguration().getContainer());
2336        final StyledText te= part.getTextWidget();
2337        
2338        if (!fConfirmSave)
2339            part.hideSaveAction();
2340        
2341        te.addPaintListener(
2342            new PaintListener() {
2343                public void paintControl(PaintEvent e) {
2344                    paint(e, part);
2345                }
2346            }
2347        );
2348        te.addKeyListener(
2349            new KeyAdapter() {
2350                public void keyPressed(KeyEvent e) {
2351                    handleSelectionChanged(part);
2352                }
2353            }
2354        );
2355        te.addMouseListener(
2356            new MouseAdapter() {
2357                public void mouseDown(MouseEvent e) {
2358                    //syncViewport(part);
2359
handleSelectionChanged(part);
2360                }
2361            }
2362        );
2363                    
2364        te.addFocusListener(
2365            new FocusAdapter() {
2366                public void focusGained(FocusEvent fe) {
2367                    fFocusPart= part;
2368                    connectGlobalActions(fFocusPart);
2369                }
2370                public void focusLost(FocusEvent fe) {
2371                    connectGlobalActions(null);
2372                }
2373            }
2374        );
2375        
2376        part.addViewportListener(
2377            new IViewportListener() {
2378                public void viewportChanged(int verticalPosition) {
2379                    syncViewport(part);
2380                }
2381            }
2382        );
2383        
2384        Font font= JFaceResources.getFont(fSymbolicFontName);
2385        if (font != null)
2386            te.setFont(font);
2387        
2388        if (fBackground != null) // not default
2389
te.setBackground(getColor(parent.getDisplay(), fBackground));
2390        
2391        // Add the find action to the popup menu of the viewer
2392
contributeFindAction(part);
2393        
2394        configureTextViewer(part);
2395        
2396        return part;
2397    }
2398
2399    private void contributeFindAction(MergeSourceViewer viewer) {
2400        IAction action;
2401        IWorkbenchPart wp = getCompareConfiguration().getContainer().getWorkbenchPart();
2402        if (wp != null)
2403            action = new FindReplaceAction(getResourceBundle(), "Editor.FindReplace.", wp); //$NON-NLS-1$
2404
else
2405            action = new FindReplaceAction(getResourceBundle(), "Editor.FindReplace.", viewer.getControl().getShell(), getFindReplaceTarget()); //$NON-NLS-1$
2406
action.setActionDefinitionId(IWorkbenchActionDefinitionIds.FIND_REPLACE);
2407        viewer.addAction(MergeSourceViewer.FIND_ID, action);
2408    }
2409    
2410    private void connectGlobalActions(final MergeSourceViewer part) {
2411        if (fHandlerService != null) {
2412            if (part != null)
2413                part.updateActions();
2414            fHandlerService.updatePaneActionHandlers(new Runnable JavaDoc() {
2415                public void run() {
2416                    for (int i= 0; i < GLOBAL_ACTIONS.length; i++) {
2417                        IAction action= null;
2418                        if (part != null) {
2419                            action= part.getAction(TEXT_ACTIONS[i]);
2420                            if (action == null && TEXT_ACTIONS[i].equals(MergeSourceViewer.SAVE_ID)) {
2421                                if (part == fLeft)
2422                                    action= fLeftSaveAction;
2423                                else
2424                                    action= fRightSaveAction;
2425                            }
2426                        }
2427                        fHandlerService.setGlobalActionHandler(GLOBAL_ACTIONS[i], action);
2428                    }
2429                }
2430            });
2431        }
2432    }
2433
2434    private IDocument getElementDocument(char type, Object JavaDoc element) {
2435        if (element instanceof IDocument) {
2436            return (IDocument) element;
2437        }
2438        ITypedElement te= Utilities.getLeg(type, element);
2439        // First check the contributors for the document
2440
IDocument document = null;
2441        switch (type) {
2442        case ANCESTOR_CONTRIBUTOR:
2443            document = getDocument(te, fAncestorContributor);
2444            break;
2445        case LEFT_CONTRIBUTOR:
2446            document = getDocument(te, fLeftContributor);
2447            break;
2448        case RIGHT_CONTRIBUTOR:
2449            document = getDocument(te, fRightContributor);
2450            break;
2451        }
2452        if (document != null)
2453            return document;
2454        // The document is not associated with the input of the viewer so try to find the document
2455
return Utilities.getDocument(type, element, isUsingDefaultContentProvider(), canHaveSharedDocument());
2456    }
2457    
2458    private boolean isUsingDefaultContentProvider() {
2459        return getContentProvider() instanceof MergeViewerContentProvider;
2460    }
2461    
2462    private boolean canHaveSharedDocument() {
2463        return getDocumentPartitioning() != null
2464            || getDocumentPartitioner() == null;
2465    }
2466    
2467    private IDocument getDocument(ITypedElement te, ContributorInfo info) {
2468        if (info != null && info.getElement() == te)
2469            return info.getDocument();
2470        return null;
2471    }
2472    
2473    IDocument getDocument(char type, Object JavaDoc input) {
2474        IDocument doc= getElementDocument(type, input);
2475        if (doc != null)
2476            return doc;
2477            
2478        if (input instanceof IDiffElement) {
2479            IDiffContainer parent= ((IDiffElement)input).getParent();
2480            return getElementDocument(type, parent);
2481        }
2482        return null;
2483    }
2484    
2485    /*
2486     * Returns true if the given inputs map to the same documents
2487     */

2488    boolean sameDoc(char type, Object JavaDoc newInput, Object JavaDoc oldInput) {
2489        IDocument newDoc= getDocument(type, newInput);
2490        IDocument oldDoc= getDocument(type, oldInput);
2491        return newDoc == oldDoc;
2492    }
2493    
2494    /**
2495     * Overridden to prevent save confirmation if new input is sub document of current input.
2496     * @param newInput the new input of this viewer, or <code>null</code> if there is no new input
2497     * @param oldInput the old input element, or <code>null</code> if there was previously no input
2498     * @return <code>true</code> if saving was successful, or if the user didn't want to save (by pressing 'NO' in the confirmation dialog).
2499     * @since 2.0
2500     */

2501    protected boolean doSave(Object JavaDoc newInput, Object JavaDoc oldInput) {
2502        // TODO: Would be good if this could be restated in terms of Saveables and moved up
2503
if (oldInput != null && newInput != null) {
2504            // check whether underlying documents have changed.
2505
if (sameDoc(ANCESTOR_CONTRIBUTOR, newInput, oldInput) &&
2506                    sameDoc(LEFT_CONTRIBUTOR, newInput, oldInput) &&
2507                        sameDoc(RIGHT_CONTRIBUTOR, newInput, oldInput)) {
2508                if (DEBUG) System.out.println("----- Same docs !!!!"); //$NON-NLS-1$
2509
return false;
2510            }
2511        }
2512        
2513        if (DEBUG) System.out.println("***** New docs !!!!"); //$NON-NLS-1$
2514

2515        removeFromDocumentManager(ANCESTOR_CONTRIBUTOR, oldInput);
2516        removeFromDocumentManager(LEFT_CONTRIBUTOR, oldInput);
2517        removeFromDocumentManager(RIGHT_CONTRIBUTOR, oldInput);
2518        
2519        if (DEBUG)
2520            DocumentManager.dump();
2521        
2522        return super.doSave(newInput, oldInput);
2523    }
2524    
2525    private void removeFromDocumentManager(char leg, Object JavaDoc oldInput) {
2526        IDocument document= getDocument(leg, oldInput);
2527        if (document != null)
2528            DocumentManager.remove(document);
2529    }
2530    
2531    private ITypedElement getParent(char type) {
2532        Object JavaDoc input= getInput();
2533        if (input instanceof IDiffElement) {
2534            IDiffContainer parent= ((IDiffElement)input).getParent();
2535            return Utilities.getLeg(type, parent);
2536        }
2537        return null;
2538    }
2539        
2540    /*
2541     * Initializes the text viewers of the three content areas with the given input objects.
2542     * Subclasses may extend.
2543     */

2544    protected void updateContent(Object JavaDoc ancestor, Object JavaDoc left, Object JavaDoc right) {
2545        
2546        boolean emptyInput= (ancestor == null && left == null && right == null);
2547
2548        Object JavaDoc input= getInput();
2549
2550        Position leftRange= null;
2551        Position rightRange= null;
2552        
2553        // if one side is empty use container
2554
if (FIX_47640 && !emptyInput && (left == null || right == null)) {
2555            if (input instanceof IDiffElement) {
2556                IDiffContainer parent= ((IDiffElement)input).getParent();
2557                if (parent instanceof ICompareInput) {
2558                    ICompareInput ci= (ICompareInput) parent;
2559                    
2560                    if (ci.getAncestor() instanceof IDocumentRange
2561                            || ci.getLeft() instanceof IDocumentRange
2562                                    || ci.getRight() instanceof IDocumentRange) {
2563                    
2564                            if (left instanceof IDocumentRange)
2565                                leftRange= ((IDocumentRange)left).getRange();
2566                            if (right instanceof IDocumentRange)
2567                                rightRange= ((IDocumentRange)right).getRange();
2568                        
2569                        ancestor= ci.getAncestor();
2570                        left= ci.getLeft();
2571                        right= ci.getRight();
2572                    }
2573                }
2574            }
2575        }
2576
2577        int n= 0;
2578        if (left != null)
2579            n++;
2580        if (right != null)
2581            n++;
2582        fHighlightRanges= n > 1;
2583        
2584        resetDiffs();
2585        fHasErrors= false; // start with no errors
2586

2587        CompareConfiguration cc= getCompareConfiguration();
2588        IMergeViewerContentProvider cp= getMergeContentProvider();
2589        
2590        if (cp instanceof MergeViewerContentProvider) {
2591            MergeViewerContentProvider mcp= (MergeViewerContentProvider) cp;
2592            mcp.setAncestorError(null);
2593            mcp.setLeftError(null);
2594            mcp.setRightError(null);
2595        }
2596
2597        // Record current contributors so we disconnect after creating the new ones.
2598
// This is done in case the old and new use the same document.
2599
ContributorInfo oldLeftContributor = fLeftContributor;
2600        ContributorInfo oldRightContributor = fRightContributor;
2601        ContributorInfo oldAncestorContributor = fAncestorContributor;
2602        
2603        // Create the new contributor
2604
fLeftContributor = createLegInfoFor(left, LEFT_CONTRIBUTOR);
2605        fRightContributor = createLegInfoFor(right, RIGHT_CONTRIBUTOR);
2606        fAncestorContributor = createLegInfoFor(ancestor, ANCESTOR_CONTRIBUTOR);
2607        
2608        fLeftContributor.transferContributorStateFrom(oldLeftContributor);
2609        fRightContributor.transferContributorStateFrom(oldRightContributor);
2610        fAncestorContributor.transferContributorStateFrom(oldAncestorContributor);
2611        
2612        // Now disconnect the old ones
2613
disconnect(oldLeftContributor);
2614        disconnect(oldRightContributor);
2615        disconnect(oldAncestorContributor);
2616        
2617        // Get encodings from streams. If an encoding is null, abide by the other one
2618
// Defaults to workbench encoding only if both encodings are null
2619
fLeftContributor.setEncodingIfAbsent(fRightContributor);
2620        fRightContributor.setEncodingIfAbsent(fLeftContributor);
2621        fAncestorContributor.setEncodingIfAbsent(fLeftContributor);
2622        
2623        // set new documents
2624
fLeftContributor.setDocument(fLeft, cc.isLeftEditable() && cp.isLeftEditable(input));
2625        fLeftLineCount= fLeft.getLineCount();
2626        
2627        fRightContributor.setDocument(fRight, cc.isRightEditable() && cp.isRightEditable(input));
2628        fRightLineCount= fRight.getLineCount();
2629        
2630        fAncestorContributor.setDocument(fAncestor, false);
2631        
2632        //if the input is part of a patch hunk, toggle synchronized scrolling
2633
if (isPatchHunk()){
2634            setSyncScrolling(false);
2635        } else {
2636            setSyncScrolling(fPreferenceStore.getBoolean(ComparePreferencePage.SYNCHRONIZE_SCROLLING));
2637        }
2638        
2639        update(false);
2640        
2641        if (!fHasErrors && !emptyInput && !fComposite.isDisposed()) {
2642            if (isRefreshing()) {
2643                fLeftContributor.updateSelection(fLeft, !fSynchronizedScrolling);
2644                fRightContributor.updateSelection(fRight, !fSynchronizedScrolling);
2645                fAncestorContributor.updateSelection(fAncestor, !fSynchronizedScrolling);
2646                if (fSynchronizedScrolling && fSynchronziedScrollPosition != -1) {
2647                    synchronizedScrollVertical(fSynchronziedScrollPosition);
2648                }
2649            } else {
2650                if (isPatchHunk()) {
2651                    if (right != null && Utilities.getAdapter(right, IHunk.class) != null)
2652                        fLeft.setTopIndex(getHunkStart());
2653                    else
2654                        fRight.setTopIndex(getHunkStart());
2655                } else {
2656                    Diff selectDiff= null;
2657                    if (FIX_47640) {
2658                        if (leftRange != null)
2659                            selectDiff= findDiff(LEFT_CONTRIBUTOR, leftRange);
2660                        else if (rightRange != null)
2661                            selectDiff= findDiff(RIGHT_CONTRIBUTOR, rightRange);
2662                    }
2663                    if (selectDiff != null)
2664                        setCurrentDiff(selectDiff, true);
2665                    else
2666                        selectFirstDiff(true);
2667                }
2668            }
2669        }
2670        
2671    }
2672
2673    private boolean isRefreshing() {
2674        return isRefreshing;
2675    }
2676
2677    private ContributorInfo createLegInfoFor(Object JavaDoc element, char leg) {
2678        return new ContributorInfo(this, element, leg);
2679    }
2680    
2681    private Diff findDiff(char c, Position range) {
2682        
2683        MergeSourceViewer v;
2684        int start= range.getOffset();
2685        int end= start + range.getLength();
2686        if (c == LEFT_CONTRIBUTOR)
2687            v= fLeft;
2688        else if (c == RIGHT_CONTRIBUTOR)
2689            v= fRight;
2690        else
2691            return null;
2692        
2693        if (fChangeDiffs != null) {
2694            Iterator iter= fChangeDiffs.iterator();
2695            while (iter.hasNext()) {
2696                Diff diff= (Diff) iter.next();
2697                if (diff.isDeleted() || diff.fDirection == RangeDifference.NOCHANGE)
2698                    continue;
2699                if (diff.overlaps(v, start, end))
2700                    return diff;
2701            }
2702        }
2703        return null;
2704    }
2705    
2706    private void updateDiffBackground(Diff diff) {
2707        
2708        if (! fHighlightRanges)
2709            return;
2710        
2711        if (diff == null || diff.fIsToken)
2712            return;
2713            
2714        if (fShowCurrentOnly && !isCurrentDiff(diff))
2715            return;
2716                        
2717        Color c= getColor(null, getFillColor(diff));
2718        if (c == null)
2719            return;
2720            
2721        if (isThreeWay())
2722            fAncestor.setLineBackground(diff.fAncestorPos, c);
2723        fLeft.setLineBackground(diff.fLeftPos, c);
2724        fRight.setLineBackground(diff.fRightPos, c);
2725    }
2726    
2727    private void updateAllDiffBackgrounds(Display display) {
2728        if (fChangeDiffs != null) {
2729            boolean threeWay= isThreeWay();
2730            Iterator iter= fChangeDiffs.iterator();
2731            while (iter.hasNext()) {
2732                Diff diff= (Diff) iter.next();
2733                Color c= getColor(display, getFillColor(diff));
2734                if (threeWay)
2735                    fAncestor.setLineBackground(diff.fAncestorPos, c);
2736                fLeft.setLineBackground(diff.fLeftPos, c);
2737                fRight.setLineBackground(diff.fRightPos, c);
2738            }
2739        }
2740    }
2741    
2742    boolean isCurrentDiff(Diff diff) {
2743        if (diff == null)
2744            return false;
2745        if (diff == fCurrentDiff)
2746            return true;
2747        if (fCurrentDiff != null && fCurrentDiff.fParent == diff)
2748            return true;
2749        return false;
2750    }
2751    
2752    /*
2753     * Called whenever one of the documents changes.
2754     * Sets the dirty state of this viewer and updates the lines.
2755     * Implements IDocumentListener.
2756     */

2757    private void documentChanged(DocumentEvent e, boolean dirty) {
2758        
2759        IDocument doc= e.getDocument();
2760        
2761        if (doc == fLeft.getDocument()) {
2762            setLeftDirty(dirty);
2763        } else if (doc == fRight.getDocument()) {
2764            setRightDirty(dirty);
2765        }
2766
2767        updateLines(doc);
2768    }
2769        
2770    /*
2771     * This method is called if a range of text on one side is copied into an empty sub-document
2772     * on the other side. The method returns the position where the sub-document is placed into the base document.
2773     * This default implementation determines the position by using the text range differencer.
2774     * However this position is not always optimal for specific types of text.
2775     * So subclasses (which are aware of the type of text they are dealing with)
2776     * may override this method to find a better position where to insert a newly added
2777     * piece of text.
2778     * @param type the side for which the insertion position should be determined: 'A' for ancestor, 'L' for left hand side, 'R' for right hand side.
2779     * @param input the current input object of this viewer
2780     * @since 2.0
2781     */

2782    protected int findInsertionPosition(char type, ICompareInput input) {
2783            
2784        ITypedElement other= null;
2785        char otherType= 0;
2786        
2787        switch (type) {
2788        case ANCESTOR_CONTRIBUTOR:
2789            other= input.getLeft();
2790            otherType= LEFT_CONTRIBUTOR;
2791            if (other == null) {
2792                other= input.getRight();
2793                otherType= RIGHT_CONTRIBUTOR;
2794            }
2795            break;
2796        case LEFT_CONTRIBUTOR:
2797            other= input.getRight();
2798            otherType= RIGHT_CONTRIBUTOR;
2799            if (other == null) {
2800                other= input.getAncestor();
2801                otherType= ANCESTOR_CONTRIBUTOR;
2802            }
2803            break;
2804        case RIGHT_CONTRIBUTOR:
2805            other= input.getLeft();
2806            otherType= LEFT_CONTRIBUTOR;
2807            if (other == null) {
2808                other= input.getAncestor();
2809                otherType= ANCESTOR_CONTRIBUTOR;
2810            }
2811            break;
2812        }
2813        
2814        if (other instanceof IDocumentRange) {
2815            IDocumentRange dr= (IDocumentRange) other;
2816            Position p= dr.getRange();
2817            Diff diff= findDiff(otherType, p.offset);
2818            if (diff != null) {
2819                switch (type) {
2820                case ANCESTOR_CONTRIBUTOR:
2821                    if (diff.fAncestorPos != null)
2822                        return diff.fAncestorPos.offset;
2823                    break;
2824                case LEFT_CONTRIBUTOR:
2825                    if (diff.fLeftPos != null)
2826                        return diff.fLeftPos.offset;
2827                    break;
2828                case RIGHT_CONTRIBUTOR:
2829                    if (diff.fRightPos != null)
2830                        return diff.fRightPos.offset;
2831                    break;
2832                }
2833            }
2834        }
2835        return 0;
2836    }
2837    
2838    private void setError(char type, String JavaDoc message) {
2839        IMergeViewerContentProvider cp= getMergeContentProvider();
2840        if (cp instanceof MergeViewerContentProvider) {
2841            MergeViewerContentProvider mcp= (MergeViewerContentProvider) cp;
2842            switch (type) {
2843            case ANCESTOR_CONTRIBUTOR:
2844                mcp.setAncestorError(message);
2845                break;
2846            case LEFT_CONTRIBUTOR:
2847                mcp.setLeftError(message);
2848                break;
2849            case RIGHT_CONTRIBUTOR:
2850                mcp.setRightError(message);
2851                break;
2852            }
2853        }
2854        fHasErrors= true;
2855    }
2856
2857    private void updateDirtyState(IEditorInput key,
2858            IDocumentProvider documentProvider, char type) {
2859        boolean dirty = documentProvider.canSaveDocument(key);
2860        if (type == LEFT_CONTRIBUTOR)
2861            setLeftDirty(dirty);
2862        else if (type == RIGHT_CONTRIBUTOR)
2863            setRightDirty(dirty);
2864    }
2865
2866    private Position getNewRange(char type, Object JavaDoc input) {
2867        switch (type) {
2868        case ANCESTOR_CONTRIBUTOR:
2869            return (Position) fNewAncestorRanges.get(input);
2870        case LEFT_CONTRIBUTOR:
2871            return (Position) fNewLeftRanges.get(input);
2872        case RIGHT_CONTRIBUTOR:
2873            return (Position) fNewRightRanges.get(input);
2874        }
2875        return null;
2876    }
2877    
2878    private void addNewRange(char type, Object JavaDoc input, Position range) {
2879        switch (type) {
2880        case ANCESTOR_CONTRIBUTOR:
2881            fNewAncestorRanges.put(input, range);
2882            break;
2883        case LEFT_CONTRIBUTOR:
2884            fNewLeftRanges.put(input, range);
2885            break;
2886        case RIGHT_CONTRIBUTOR:
2887            fNewRightRanges.put(input, range);
2888            break;
2889        }
2890    }
2891    
2892    /**
2893     * Returns the contents of the underlying document as an array of bytes using the current workbench encoding.
2894     *
2895     * @param left if <code>true</code> the contents of the left side is returned; otherwise the right side
2896     * @return the contents of the left or right document or null
2897     */

2898    protected byte[] getContents(boolean left) {
2899        MergeSourceViewer v= left ? fLeft : fRight;
2900        if (v != null) {
2901            IDocument d= v.getDocument();
2902            if (d != null) {
2903                String JavaDoc contents= d.get();
2904                if (contents != null) {
2905                    byte[] bytes;
2906                    try {
2907                        bytes= contents.getBytes(left ? fLeftContributor.getEncoding() : fRightContributor.getEncoding());
2908                    } catch(UnsupportedEncodingException JavaDoc ex) {
2909                        // use default encoding
2910
bytes= contents.getBytes();
2911                    }
2912                    return bytes;
2913                }
2914            }
2915        }
2916        return null;
2917    }
2918        
2919    private IRegion normalizeDocumentRegion(IDocument doc, IRegion region) {
2920        
2921        if (region == null || doc == null)
2922            return region;
2923            
2924        int maxLength= doc.getLength();
2925        
2926        int start= region.getOffset();
2927        if (start < 0)
2928            start= 0;
2929        else if (start > maxLength)
2930            start= maxLength;
2931            
2932        int length= region.getLength();
2933        if (length < 0)
2934            length= 0;
2935        else if (start + length > maxLength)
2936            length= maxLength - start;
2937            
2938        return new Region(start, length);
2939    }
2940        
2941    /* (non-Javadoc)
2942     * @see org.eclipse.compare.contentmergeviewer.ContentMergeViewer#handleResizeAncestor(int, int, int, int)
2943     */

2944    protected final void handleResizeAncestor(int x, int y, int width, int height) {
2945        if (width > 0) {
2946            Rectangle trim= fLeft.getTextWidget().computeTrim(0, 0, 0, 0);
2947            int scrollbarHeight= trim.height;
2948            if (Utilities.okToUse(fAncestorCanvas))
2949                fAncestorCanvas.setVisible(true);
2950            if (fAncestor.isControlOkToUse())
2951                fAncestor.getTextWidget().setVisible(true);
2952            
2953            if (fAncestorCanvas != null) {
2954                fAncestorCanvas.setBounds(x, y, fMarginWidth, height-scrollbarHeight);
2955                x+= fMarginWidth;
2956                width-= fMarginWidth;
2957            }
2958            fAncestor.setBounds(x, y, width, height);
2959        } else {
2960            if (Utilities.okToUse(fAncestorCanvas))
2961                fAncestorCanvas.setVisible(false);
2962            if (fAncestor.isControlOkToUse()) {
2963                StyledText t= fAncestor.getTextWidget();
2964                t.setVisible(false);
2965                fAncestor.setBounds(0, 0, 0, 0);
2966                if (fFocusPart == fAncestor) {
2967                    fFocusPart= fLeft;
2968                    fFocusPart.getTextWidget().setFocus();
2969                }
2970            }
2971        }
2972    }
2973
2974    /* (non-Javadoc)
2975     * @see org.eclipse.compare.contentmergeviewer.ContentMergeViewer#handleResizeLeftRight(int, int, int, int, int, int)
2976     */

2977    protected final void handleResizeLeftRight(int x, int y, int width1, int centerWidth, int width2, int height) {
2978                
2979        if (fBirdsEyeCanvas != null)
2980            width2-= BIRDS_EYE_VIEW_WIDTH;
2981            
2982        Rectangle trim= fLeft.getTextWidget().computeTrim(0, 0, 0, 0);
2983        int scrollbarHeight= trim.height + trim.x;
2984
2985        Composite composite= (Composite) getControl();
2986
2987        int leftTextWidth= width1;
2988        if (fLeftCanvas != null) {
2989            fLeftCanvas.setBounds(x, y, fMarginWidth, height-scrollbarHeight);
2990            x+= fMarginWidth;
2991            leftTextWidth-= fMarginWidth;
2992        }
2993        
2994        fLeft.setBounds(x, y, leftTextWidth, height);
2995        x+= leftTextWidth;
2996        
2997        if (fCenter == null || fCenter.isDisposed())
2998            fCenter= createCenterControl(composite);
2999        fCenter.setBounds(x, y, centerWidth, height-scrollbarHeight);
3000        x+= centerWidth;
3001        
3002        if (!fSynchronizedScrolling) { // canvas is to the left of text
3003
if (fRightCanvas != null) {
3004                fRightCanvas.setBounds(x, y, fMarginWidth, height-scrollbarHeight);
3005                fRightCanvas.redraw();
3006                x+= fMarginWidth;
3007            }
3008            // we draw the canvas to the left of the text widget
3009
}
3010        
3011        int scrollbarWidth= 0;
3012        if (fSynchronizedScrolling && fScrollCanvas != null) {
3013            trim= fLeft.getTextWidget().computeTrim(0, 0, 0, 0);
3014            // one pixel was cut off
3015
scrollbarWidth= trim.width + 2*trim.x+1;
3016        }
3017        int rightTextWidth= width2-scrollbarWidth;
3018        if (fRightCanvas != null)
3019            rightTextWidth-= fMarginWidth;
3020        fRight.setBounds(x, y, rightTextWidth, height);
3021        x+= rightTextWidth;
3022            
3023        if (fSynchronizedScrolling) {
3024            if (fRightCanvas != null) { // canvas is to the right of the text
3025
fRightCanvas.setBounds(x, y, fMarginWidth, height-scrollbarHeight);
3026                x+= fMarginWidth;
3027            }
3028            if (fScrollCanvas != null)
3029                fScrollCanvas.setBounds(x, y, scrollbarWidth, height-scrollbarHeight);
3030        }
3031        
3032        if (fBirdsEyeCanvas != null) {
3033            int verticalScrollbarButtonHeight= scrollbarWidth;
3034            int horizontalScrollbarButtonHeight= scrollbarHeight;
3035            if (fIsCarbon) {
3036                verticalScrollbarButtonHeight+= 2;
3037                horizontalScrollbarButtonHeight= 18;
3038            }
3039            if (fSummaryHeader != null)
3040                fSummaryHeader.setBounds(x+scrollbarWidth, y, BIRDS_EYE_VIEW_WIDTH, verticalScrollbarButtonHeight);
3041            y+= verticalScrollbarButtonHeight;
3042            fBirdsEyeCanvas.setBounds(x+scrollbarWidth, y, BIRDS_EYE_VIEW_WIDTH, height-(2*verticalScrollbarButtonHeight+horizontalScrollbarButtonHeight));
3043        }
3044        
3045        // doesn't work since TextEditors don't have their correct size yet.
3046
updateVScrollBar();
3047        refreshBirdsEyeView();
3048    }
3049                            
3050    /*
3051     * Track selection changes to update the current Diff.
3052     */

3053    private void handleSelectionChanged(MergeSourceViewer tw) {
3054        Point p= tw.getSelectedRange();
3055        Diff d= findDiff(tw, p.x, p.x+p.y);
3056        updateStatus(d);
3057        setCurrentDiff(d, false); // don't select or reveal
3058
}
3059
3060    private static IRegion toRegion(Position position) {
3061        if (position != null)
3062            return new Region(position.getOffset(), position.getLength());
3063        return null;
3064    }
3065    
3066    //---- the differencing
3067

3068    private static int maxWork(IRangeComparator a, IRangeComparator l, IRangeComparator r) {
3069        int ln= l.getRangeCount();
3070        int rn= r.getRangeCount();
3071        if (a != null) {
3072            int an= a.getRangeCount();
3073            return (2 * Math.max(an, ln)) + (2 * Math.max(an, rn));
3074        }
3075        return 2 * Math.max(ln, rn);
3076    }
3077    
3078    /**
3079     * Perform a two level 2- or 3-way diff.
3080     * The first level is based on line comparison, the second level on token comparison.
3081     */

3082    private void doDiff() {
3083                        
3084        ArrayList newAllDiffs = new ArrayList();
3085        fChangeDiffs= new ArrayList();
3086        fCurrentDiff= null;
3087        
3088        IDocument aDoc= null;
3089        IDocument lDoc= fLeft.getDocument();
3090        IDocument rDoc= fRight.getDocument();
3091        if (lDoc == null || rDoc == null)
3092            return;
3093            
3094        Position aRegion= null;
3095        Position lRegion= fLeft.getRegion();
3096        Position rRegion= fRight.getRegion();
3097                
3098        boolean threeWay= isThreeWay();
3099        
3100        if (threeWay && !isIgnoreAncestor()) {
3101            aDoc= fAncestor.getDocument();
3102            aRegion= fAncestor.getRegion();
3103        }
3104        
3105        resetPositions(lDoc);
3106        resetPositions(rDoc);
3107        resetPositions(aDoc);
3108        
3109        fAncestor.resetLineBackground();
3110        fLeft.resetLineBackground();
3111        fRight.resetLineBackground();
3112        
3113        boolean ignoreWhiteSpace= Utilities.getBoolean(getCompareConfiguration(), CompareConfiguration.IGNORE_WHITESPACE, false);
3114        
3115        DocLineComparator sright= new DocLineComparator(rDoc, toRegion(rRegion), ignoreWhiteSpace);
3116        DocLineComparator sleft= new DocLineComparator(lDoc, toRegion(lRegion), ignoreWhiteSpace);
3117        DocLineComparator sancestor= null;
3118        boolean isRight = true;
3119        if (aDoc != null) {
3120            sancestor= new DocLineComparator(aDoc, toRegion(aRegion), ignoreWhiteSpace);
3121            if (isPatchHunk()) {
3122                ITypedElement right = ((ICompareInput)getInput()).getRight();
3123                isRight = right != null && Utilities.getAdapter(right, IHunk.class) != null;
3124                if (isRight) {
3125                    sleft= new DocLineComparator(aDoc, toRegion(aRegion), ignoreWhiteSpace);
3126                } else {
3127                    sright= new DocLineComparator(aDoc, toRegion(aRegion), ignoreWhiteSpace);
3128                }
3129            }
3130        }
3131            
3132        if (!fSubDoc && rRegion != null && lRegion != null) {
3133            // we have to add a diff for the ignored lines
3134

3135            int astart= 0;
3136            int as= 0;
3137            if (aRegion != null) {
3138                astart= aRegion.getOffset();
3139                as= Math.max(0, astart-1);
3140            }
3141            int ys= Math.max(0, lRegion.getOffset()-1);
3142            int ms= Math.max(0, rRegion.getOffset()-1);
3143            
3144            if (as > 0 || ys > 0 || ms > 0) {
3145                Diff diff= new Diff(null, RangeDifference.NOCHANGE,
3146                    aDoc, aRegion, 0, astart,
3147                    lDoc, lRegion, 0, lRegion.getOffset(),
3148                    rDoc, rRegion, 0, rRegion.getOffset());
3149                newAllDiffs.add(diff);
3150            }
3151        }
3152        
3153        final ResourceBundle bundle= getResourceBundle();
3154            
3155        final Object JavaDoc[] result= new Object JavaDoc[1];
3156        final DocLineComparator sa= sancestor, sl= sleft, sr= sright;
3157        IRunnableWithProgress runnable= new IRunnableWithProgress() {
3158            public void run(IProgressMonitor monitor) throws InterruptedException JavaDoc, InvocationTargetException JavaDoc {
3159                String JavaDoc progressTitle= Utilities.getString(bundle, "compareProgressTask.title"); //$NON-NLS-1$
3160
monitor.beginTask(progressTitle, maxWork(sa, sl, sr));
3161                try {
3162                    result[0]= RangeDifferencer.findRanges(monitor, sa, sl, sr);
3163                } catch (OutOfMemoryError JavaDoc ex) {
3164                    System.gc();
3165                    throw new InvocationTargetException JavaDoc(ex);
3166                }
3167                if (monitor.isCanceled()) { // canceled
3168
throw new InterruptedException JavaDoc();
3169                }
3170                monitor.done();
3171            }
3172        };
3173        
3174        RangeDifference[] e= null;
3175        try {
3176            getCompareConfiguration().getContainer().run(true, true, runnable);
3177            e= (RangeDifference[]) result[0];
3178        } catch (InvocationTargetException JavaDoc ex) {
3179            String JavaDoc title= Utilities.getString(bundle, "tooComplexError.title"); //$NON-NLS-1$
3180
String JavaDoc format= Utilities.getString(bundle, "tooComplexError.format"); //$NON-NLS-1$
3181
String JavaDoc msg= MessageFormat.format(format, new Object JavaDoc[] { Integer.toString(PlatformUI.getWorkbench().getProgressService().getLongOperationTime()/1000) } );
3182            MessageDialog.openError(fComposite.getShell(), title, msg);
3183            e= null;
3184        } catch (InterruptedException JavaDoc ex) {
3185            //
3186
}
3187                    
3188        if (e == null) {
3189            // we create a NOCHANGE range for the whole document
3190
Diff diff= new Diff(null, RangeDifference.NOCHANGE,
3191                aDoc, aRegion, 0, aDoc != null ? aDoc.getLength() : 0,
3192                lDoc, lRegion, 0, lDoc.getLength(),
3193                rDoc, rRegion, 0, rDoc.getLength());
3194                
3195            newAllDiffs.add(diff);
3196        } else {
3197            for (int i= 0; i < e.length; i++) {
3198                String JavaDoc a= null, s= null, d= null;
3199                RangeDifference es= e[i];
3200                
3201                int kind= es.kind();
3202                
3203                int ancestorStart= 0;
3204                int ancestorEnd= 0;
3205                if (sancestor != null) {
3206                    ancestorStart= sancestor.getTokenStart(es.ancestorStart());
3207                    ancestorEnd= getTokenEnd2(sancestor, es.ancestorStart(), es.ancestorLength());
3208                }
3209                
3210                int leftStart= sleft.getTokenStart(es.leftStart());
3211                int leftEnd= getTokenEnd2(sleft, es.leftStart(), es.leftLength());
3212                
3213                int rightStart= sright.getTokenStart(es.rightStart());
3214                int rightEnd= getTokenEnd2(sright, es.rightStart(), es.rightLength());
3215                
3216                if (isPatchHunk()) {
3217                    if (isRight)
3218                        leftStart = leftEnd = getHunkStart();
3219                    else
3220                        rightStart = rightEnd = getHunkStart();
3221                }
3222                
3223                Diff diff= new Diff(null, kind,
3224                    aDoc, aRegion, ancestorStart, ancestorEnd,
3225                    lDoc, lRegion, leftStart, leftEnd,
3226                    rDoc, rRegion, rightStart, rightEnd);
3227                
3228                newAllDiffs.add(diff); // remember all range diffs for scrolling
3229

3230                if (ignoreWhiteSpace && !isPatchHunk()) {
3231                    if (sancestor != null)
3232                        a= extract2(aDoc, sancestor, es.ancestorStart(), es.ancestorLength());
3233                    s= extract2(lDoc, sleft, es.leftStart(), es.leftLength());
3234                    d= extract2(rDoc, sright, es.rightStart(), es.rightLength());
3235                
3236                    if ((a == null || a.trim().length() == 0) && s.trim().length() == 0 && d.trim().length() == 0) {
3237                        diff.fIsWhitespace= true;
3238                        continue;
3239                    }
3240                }
3241        
3242                if (useChange(kind)) {
3243                    fChangeDiffs.add(diff); // here we remember only the real diffs
3244
updateDiffBackground(diff);
3245        
3246                    // Only do the token diff for non-hunks
3247
if (!isPatchHunk()) {
3248                        if (s == null)
3249                            s= extract2(lDoc, sleft, es.leftStart(), es.leftLength());
3250                        if (d == null)
3251                            d= extract2(rDoc, sright, es.rightStart(), es.rightLength());
3252                        
3253                        if (s.length() > 0 && d.length() > 0) {
3254                            if (a == null && sancestor != null)
3255                                a= extract2(aDoc, sancestor, es.ancestorStart(), es.ancestorLength());
3256                            if (USE_MERGING_TOKEN_DIFF)
3257                                mergingTokenDiff(diff, aDoc, a, rDoc, d, lDoc, s);
3258                            else
3259                                simpleTokenDiff(diff, aDoc, a, rDoc, d, lDoc, s);
3260                        }
3261                    }
3262                }
3263            }
3264        }
3265        
3266        if (!fSubDoc && rRegion != null && lRegion != null) {
3267            // we have to add a diff for the ignored lines
3268

3269            int aEnd= 0;
3270            int aLen= 0;
3271            if (aRegion != null && aDoc != null) {
3272                aEnd= aRegion.getOffset()+aRegion.getLength();
3273                aLen= aDoc.getLength();
3274            }
3275            Diff diff= new Diff(null, RangeDifference.NOCHANGE,
3276                aDoc, aRegion, aEnd, aLen,
3277                lDoc, lRegion, lRegion.getOffset()+lRegion.getLength(), lDoc.getLength(),
3278                rDoc, rRegion, rRegion.getOffset()+rRegion.getLength(), rDoc.getLength());
3279            newAllDiffs.add(diff);
3280        }
3281        fAllDiffs = newAllDiffs;
3282        invalidateTextPresentation();
3283    }
3284    
3285    private void resetPositions(IDocument doc) {
3286        if (doc == null)
3287            return;
3288        try {
3289            doc.removePositionCategory(DIFF_RANGE_CATEGORY);
3290        } catch (BadPositionCategoryException e) {
3291            // Ignore
3292
}
3293        doc.addPositionCategory(DIFF_RANGE_CATEGORY);
3294    }
3295
3296    private Diff findDiff(char type, int pos) {
3297                                
3298        IDocument aDoc= null;
3299        IDocument lDoc= fLeft.getDocument();
3300        IDocument rDoc= fRight.getDocument();
3301        if (lDoc == null || rDoc == null)
3302            return null;
3303            
3304        Position aRegion= null;
3305        Position lRegion= null;
3306        Position rRegion= null;
3307                
3308        boolean threeWay= isThreeWay();
3309        
3310        if (threeWay && !isIgnoreAncestor())
3311            aDoc= fAncestor.getDocument();
3312
3313        boolean ignoreWhiteSpace= Utilities.getBoolean(getCompareConfiguration(), CompareConfiguration.IGNORE_WHITESPACE, false);
3314        
3315        DocLineComparator sright= new DocLineComparator(rDoc, toRegion(rRegion), ignoreWhiteSpace);
3316        DocLineComparator sleft= new DocLineComparator(lDoc, toRegion(lRegion), ignoreWhiteSpace);
3317        DocLineComparator sancestor= null;
3318        if (aDoc != null)
3319            sancestor= new DocLineComparator(aDoc, toRegion(aRegion), ignoreWhiteSpace);
3320            
3321        final ResourceBundle bundle= getResourceBundle();
3322            
3323        final Object JavaDoc[] result= new Object JavaDoc[1];
3324        final DocLineComparator sa= sancestor, sl= sleft, sr= sright;
3325        IRunnableWithProgress runnable= new IRunnableWithProgress() {
3326            public void run(IProgressMonitor monitor) throws InterruptedException JavaDoc, InvocationTargetException JavaDoc {
3327                String JavaDoc progressTitle= Utilities.getString(bundle, "compareProgressTask.title"); //$NON-NLS-1$
3328
monitor.beginTask(progressTitle, maxWork(sa, sl, sr));
3329                try {
3330                    result[0]= RangeDifferencer.findRanges(monitor, sa, sl, sr);
3331                } catch (OutOfMemoryError JavaDoc ex) {
3332                    System.gc();
3333                    throw new InvocationTargetException JavaDoc(ex);
3334                }
3335                if (monitor.isCanceled()) { // canceled
3336
throw new InterruptedException JavaDoc();
3337                }
3338                monitor.done();
3339            }
3340        };
3341        IProgressService progressService= PlatformUI.getWorkbench().getProgressService();
3342        
3343        RangeDifference[] e= null;
3344        try {
3345            progressService.run(true, true, runnable);
3346            e= (RangeDifference[]) result[0];
3347        } catch (InvocationTargetException JavaDoc ex) {
3348            String JavaDoc title= Utilities.getString(bundle, "tooComplexError.title"); //$NON-NLS-1$
3349
String JavaDoc format= Utilities.getString(bundle, "tooComplexError.format"); //$NON-NLS-1$
3350
String JavaDoc msg= MessageFormat.format(format, new Object JavaDoc[] { Integer.toString(progressService.getLongOperationTime()/1000) } );
3351            MessageDialog.openError(fComposite.getShell(), title, msg);
3352            e= null;
3353        } catch (InterruptedException JavaDoc ex) {
3354            //
3355
}
3356                    
3357        if (e != null) {
3358            for (int i= 0; i < e.length; i++) {
3359                RangeDifference es= e[i];
3360                
3361                int kind= es.kind();
3362                
3363                int ancestorStart= 0;
3364                int ancestorEnd= 0;
3365                if (sancestor != null) {
3366                    ancestorStart= sancestor.getTokenStart(es.ancestorStart());
3367                    ancestorEnd= getTokenEnd2(sancestor, es.ancestorStart(), es.ancestorLength());
3368                }
3369                
3370                int leftStart= sleft.getTokenStart(es.leftStart());
3371                int leftEnd= getTokenEnd2(sleft, es.leftStart(), es.leftLength());
3372                
3373                int rightStart= sright.getTokenStart(es.rightStart());
3374                int rightEnd= getTokenEnd2(sright, es.rightStart(), es.rightLength());
3375                
3376                Diff diff= new Diff(null, kind,
3377                    aDoc, aRegion, ancestorStart, ancestorEnd,
3378                    lDoc, lRegion, leftStart, leftEnd,
3379                    rDoc, rRegion, rightStart, rightEnd);
3380
3381                if (diff.isInRange(type, pos))
3382                    return diff;
3383            }
3384        }
3385        
3386        return null;
3387    }
3388    
3389    /*
3390     * Returns true if kind of change should be shown.
3391     */

3392    private boolean useChange(int kind) {
3393        if (kind == RangeDifference.NOCHANGE)
3394            return false;
3395        if (kind == RangeDifference.ANCESTOR)
3396            return fShowPseudoConflicts;
3397        return true;
3398    }
3399    
3400    private int getTokenEnd(ITokenComparator tc, int start, int count) {
3401        if (count <= 0)
3402            return tc.getTokenStart(start);
3403        int index= start + count - 1;
3404        return tc.getTokenStart(index) + tc.getTokenLength(index);
3405    }
3406    
3407    private static int getTokenEnd2(ITokenComparator tc, int start, int length) {
3408        return tc.getTokenStart(start + length);
3409    }
3410
3411    /*
3412     * Returns the content of lines in the specified range as a String.
3413     * This includes the line separators.
3414     *
3415     * @param doc the document from which to extract the characters
3416     * @param start index of first line
3417     * @param length number of lines
3418     * @return the contents of the specified line range as a String
3419     */

3420    private String JavaDoc extract2(IDocument doc, ITokenComparator tc, int start, int length) {
3421        int count= tc.getRangeCount();
3422        if (length > 0 && count > 0) {
3423            
3424//
3425
// int startPos= tc.getTokenStart(start);
3426
// int endPos= startPos;
3427
//
3428
// if (length > 1)
3429
// endPos= tc.getTokenStart(start + (length-1));
3430
// endPos+= tc.getTokenLength(start + (length-1));
3431
//
3432

3433            int startPos= tc.getTokenStart(start);
3434            int endPos;
3435            
3436            if (length == 1) {
3437                endPos= startPos + tc.getTokenLength(start);
3438            } else {
3439                endPos= tc.getTokenStart(start + length);
3440            }
3441
3442            try {
3443                return doc.get(startPos, endPos - startPos);
3444            } catch (BadLocationException e) {
3445                // silently ignored
3446
}
3447
3448        }
3449        return ""; //$NON-NLS-1$
3450
}
3451
3452    /*
3453     * Performs a token based 3-way diff on the character range specified by the given baseDiff.
3454     */

3455    private void simpleTokenDiff(final Diff baseDiff,
3456                IDocument ancestorDoc, String JavaDoc a,
3457                IDocument rightDoc, String JavaDoc d,
3458                IDocument leftDoc, String JavaDoc s) {
3459
3460        int ancestorStart= 0;
3461        ITokenComparator sa= null;
3462        if (ancestorDoc != null) {
3463            ancestorStart= baseDiff.fAncestorPos.getOffset();
3464            sa= createTokenComparator(a);
3465        }
3466        
3467        int rightStart= baseDiff.fRightPos.getOffset();
3468        ITokenComparator sm= createTokenComparator(d);
3469        
3470        int leftStart= baseDiff.fLeftPos.getOffset();
3471        ITokenComparator sy= createTokenComparator(s);
3472        
3473        RangeDifference[] e= RangeDifferencer.findRanges(sa, sy, sm);
3474        for (int i= 0; i < e.length; i++) {
3475            RangeDifference es= e[i];
3476            int kind= es.kind();
3477            if (kind != RangeDifference.NOCHANGE) {
3478                
3479                int ancestorStart2= ancestorStart;
3480                int ancestorEnd2= ancestorStart;
3481                if (ancestorDoc != null) {
3482                    ancestorStart2 += sa.getTokenStart(es.ancestorStart());
3483                    ancestorEnd2 += getTokenEnd(sa, es.ancestorStart(), es.ancestorLength());
3484                }
3485                
3486                int leftStart2= leftStart + sy.getTokenStart(es.leftStart());
3487                int leftEnd2= leftStart + getTokenEnd(sy, es.leftStart(), es.leftLength());
3488                
3489                int rightStart2= rightStart + sm.getTokenStart(es.rightStart());
3490                int rightEnd2= rightStart + getTokenEnd(sm, es.rightStart(), es.rightLength());
3491                
3492                Diff diff= new Diff(baseDiff, kind,
3493                        ancestorDoc, null, ancestorStart2, ancestorEnd2,
3494                        leftDoc, null, leftStart2, leftEnd2,
3495                        rightDoc, null, rightStart2, rightEnd2);
3496                
3497                // ensure that token diff is smaller than basediff
3498
int leftS= baseDiff.fLeftPos.offset;
3499                int leftE= baseDiff.fLeftPos.offset+baseDiff.fLeftPos.length;
3500                int rightS= baseDiff.fRightPos.offset;
3501                int rightE= baseDiff.fRightPos.offset+baseDiff.fRightPos.length;
3502                if (leftS != leftStart2 || leftE != leftEnd2 ||
3503                            rightS != rightStart2 || rightE != rightEnd2) {
3504                    diff.fIsToken= true;
3505                    // add to base Diff
3506
baseDiff.add(diff);
3507                }
3508            }
3509        }
3510    }
3511    
3512    /*
3513     * Performs a "smart" token based 3-way diff on the character range specified by the given baseDiff.
3514     * It is "smart" because it tries to minimize the number of token diffs by merging them.
3515     */

3516    private void mergingTokenDiff(Diff baseDiff,
3517                IDocument ancestorDoc, String JavaDoc a,
3518                IDocument rightDoc, String JavaDoc d,
3519                IDocument leftDoc, String JavaDoc s) {
3520        ITokenComparator sa= null;
3521        int ancestorStart= 0;
3522        if (ancestorDoc != null) {
3523            sa= createTokenComparator(a);
3524            ancestorStart= baseDiff.fAncestorPos.getOffset();
3525        }
3526        
3527        int rightStart= baseDiff.fRightPos.getOffset();
3528        ITokenComparator sm= createTokenComparator(d);
3529        
3530        int leftStart= baseDiff.fLeftPos.getOffset();
3531        ITokenComparator sy= createTokenComparator(s);
3532        
3533        RangeDifference[] r= RangeDifferencer.findRanges(sa, sy, sm);
3534        for (int i= 0; i < r.length; i++) {
3535            RangeDifference es= r[i];
3536            // determine range of diffs in one line
3537
int start= i;
3538            int leftLine= -1;
3539            int rightLine= -1;
3540            try {
3541                leftLine= leftDoc.getLineOfOffset(leftStart+sy.getTokenStart(es.leftStart()));
3542                rightLine= rightDoc.getLineOfOffset(rightStart+sm.getTokenStart(es.rightStart()));
3543            } catch (BadLocationException e) {
3544                // silently ignored
3545
}
3546            i++;
3547            for (; i < r.length; i++) {
3548                es= r[i];
3549                try {
3550                    if (leftLine != leftDoc.getLineOfOffset(leftStart+sy.getTokenStart(es.leftStart())))
3551                        break;
3552                    if (rightLine != rightDoc.getLineOfOffset(rightStart+sm.getTokenStart(es.rightStart())))
3553                        break;
3554                } catch (BadLocationException e) {
3555                    // silently ignored
3556
}
3557            }
3558            int end= i;
3559            
3560            // find first diff from left
3561
RangeDifference first= null;
3562            for (int ii= start; ii < end; ii++) {
3563                es= r[ii];
3564                if (useChange(es.kind())) {
3565                    first= es;
3566                    break;
3567                }
3568            }
3569            
3570            // find first diff from mine
3571
RangeDifference last= null;
3572            for (int ii= end-1; ii >= start; ii--) {
3573                es= r[ii];
3574                if (useChange(es.kind())) {
3575                    last= es;
3576                    break;
3577                }
3578            }
3579            
3580            if (first != null && last != null) {
3581                
3582                int ancestorStart2= 0;
3583                int ancestorEnd2= 0;
3584                if (ancestorDoc != null) {
3585                    ancestorStart2= ancestorStart+sa.getTokenStart(first.ancestorStart());
3586                    ancestorEnd2= ancestorStart+getTokenEnd(sa, last.ancestorStart(), last.ancestorLength());
3587                }
3588                
3589                int leftStart2= leftStart+sy.getTokenStart(first.leftStart());
3590                int leftEnd2= leftStart+getTokenEnd(sy, last.leftStart(), last.leftLength());
3591                
3592                int rightStart2= rightStart+sm.getTokenStart(first.rightStart());
3593                int rightEnd2= rightStart+getTokenEnd(sm, last.rightStart(), last.rightLength());
3594                Diff diff= new Diff(baseDiff, first.kind(),
3595                            ancestorDoc, null, ancestorStart2, ancestorEnd2,
3596                            leftDoc, null, leftStart2, leftEnd2,
3597                            rightDoc, null, rightStart2, rightEnd2);
3598                diff.fIsToken= true;
3599                baseDiff.add(diff);
3600            }
3601        }
3602    }
3603    
3604    //---- update UI stuff
3605

3606    private void updateControls() {
3607        
3608        boolean leftToRight= false;
3609        boolean rightToLeft= false;
3610        
3611        updateStatus(fCurrentDiff);
3612        updateResolveStatus();
3613
3614        if (fCurrentDiff != null) {
3615            IMergeViewerContentProvider cp= getMergeContentProvider();
3616            if (cp != null) {
3617                if (!isPatchHunk()) {
3618                    rightToLeft= cp.isLeftEditable(getInput());
3619                    leftToRight= cp.isRightEditable(getInput());
3620                }
3621            }
3622        }
3623        
3624        if (fDirectionLabel != null) {
3625            if (fHighlightRanges && fCurrentDiff != null && isThreeWay() && !isIgnoreAncestor()) {
3626                fDirectionLabel.setImage(fCurrentDiff.getImage());
3627            } else {
3628                fDirectionLabel.setImage(null);
3629            }
3630        }
3631        
3632        if (fCopyDiffLeftToRightItem != null)
3633            ((Action)fCopyDiffLeftToRightItem.getAction()).setEnabled(leftToRight);
3634        if (fCopyDiffRightToLeftItem != null)
3635            ((Action)fCopyDiffRightToLeftItem.getAction()).setEnabled(rightToLeft);
3636            
3637        boolean enableNavigation= false;
3638        if (fCurrentDiff == null && fChangeDiffs != null && fChangeDiffs.size() > 0)
3639            enableNavigation= true;
3640        else if (fChangeDiffs != null && fChangeDiffs.size() > 1)
3641            enableNavigation= true;
3642        else if (fCurrentDiff != null && fCurrentDiff.fDiffs != null)
3643            enableNavigation= true;
3644        else if (fCurrentDiff != null && fCurrentDiff.fIsToken)
3645            enableNavigation= true;
3646        
3647        if (fNextDiff != null) {
3648            IAction a= fNextDiff.getAction();
3649            a.setEnabled(enableNavigation || hasNextElement(true));
3650        }
3651        if (fPreviousDiff != null) {
3652            IAction a= fPreviousDiff.getAction();
3653            a.setEnabled(enableNavigation || hasNextElement(false));
3654        }
3655        if (fNextChange != null) {
3656            IAction a= fNextChange.getAction();
3657            a.setEnabled(enableNavigation);
3658        }
3659        if (fPreviousChange != null) {
3660            IAction a= fPreviousChange.getAction();
3661            a.setEnabled(enableNavigation);
3662        }
3663    }
3664    
3665    private void updateResolveStatus() {
3666            
3667        RGB rgb= null;
3668        
3669        if (showResolveUI()) {
3670            // we only show red or green if there is at least one incoming or conflicting change
3671
int incomingOrConflicting= 0;
3672            int unresolvedIncoming= 0;
3673            int unresolvedConflicting= 0;
3674
3675            if (fChangeDiffs != null) {
3676                Iterator e= fChangeDiffs.iterator();
3677                while (e.hasNext()) {
3678                    Diff d= (Diff) e.next();
3679                    if (d.isIncomingOrConflicting() /* && useChange(d.fDirection) && !d.fIsWhitespace */) {
3680                        incomingOrConflicting++;
3681                        if (!d.fResolved) {
3682                            if (d.fDirection == RangeDifference.CONFLICT) {
3683                                unresolvedConflicting++;
3684                                break; // we can stop here because a conflict has the maximum priority
3685
}
3686                            unresolvedIncoming++;
3687                        }
3688                    }
3689                }
3690            }
3691        
3692            if (incomingOrConflicting > 0) {
3693                if (unresolvedConflicting > 0)
3694                    rgb= SELECTED_CONFLICT;
3695                else if (unresolvedIncoming > 0)
3696                    rgb= SELECTED_INCOMING;
3697                else
3698                    rgb= RESOLVED;
3699            }
3700        }
3701        
3702        if (fHeaderPainter.setColor(rgb))
3703            fSummaryHeader.redraw();
3704    }
3705
3706    private void updateStatus(Diff diff) {
3707        
3708        if (! fShowMoreInfo)
3709            return;
3710                    
3711        String JavaDoc diffDescription;
3712        
3713        if (diff == null) {
3714            diffDescription= CompareMessages.TextMergeViewer_diffDescription_noDiff_format;
3715        } else {
3716            
3717            if (diff.fIsToken) // we don't show special info for token diffs
3718
diff= diff.fParent;
3719        
3720            String JavaDoc format= CompareMessages.TextMergeViewer_diffDescription_diff_format;
3721            diffDescription= MessageFormat.format(format,
3722                new String JavaDoc[] {
3723                    getDiffType(diff), // 0: diff type
3724
getDiffNumber(diff), // 1: diff number
3725
getDiffRange(fLeft, diff.fLeftPos), // 2: left start line
3726
getDiffRange(fRight, diff.fRightPos) // 3: left end line
3727
}
3728            );
3729        }
3730        
3731        String JavaDoc format= CompareMessages.TextMergeViewer_statusLine_format;
3732        String JavaDoc s= MessageFormat.format(format,
3733            new String JavaDoc[] {
3734                getCursorPosition(fLeft), // 0: left column
3735
getCursorPosition(fRight), // 1: right column
3736
diffDescription // 2: diff description
3737
}
3738        );
3739    
3740        getCompareConfiguration().getContainer().setStatusMessage(s);
3741    }
3742
3743    private void clearStatus() {
3744        getCompareConfiguration().getContainer().setStatusMessage(null);
3745    }
3746    
3747    private String JavaDoc getDiffType(Diff diff) {
3748        String JavaDoc s= ""; //$NON-NLS-1$
3749
switch(diff.fDirection) {
3750        case RangeDifference.LEFT:
3751            s= CompareMessages.TextMergeViewer_direction_outgoing;
3752            break;
3753        case RangeDifference.RIGHT:
3754            s= CompareMessages.TextMergeViewer_direction_incoming;
3755            break;
3756        case RangeDifference.CONFLICT:
3757            s= CompareMessages.TextMergeViewer_direction_conflicting;
3758            break;
3759        }
3760        String JavaDoc format= CompareMessages.TextMergeViewer_diffType_format;
3761        return MessageFormat.format(format, new String JavaDoc[] { s, diff.changeType() } );
3762    }
3763    
3764    private String JavaDoc getDiffNumber(Diff diff) {
3765        // find the diff's number
3766
int diffNumber= 0;
3767        if (fChangeDiffs != null) {
3768            Iterator e= fChangeDiffs.iterator();
3769            while (e.hasNext()) {
3770                Diff d= (Diff) e.next();
3771                diffNumber++;
3772                if (d == diff)
3773                    break;
3774            }
3775        }
3776        return Integer.toString(diffNumber);
3777    }
3778    
3779    private String JavaDoc getDiffRange(MergeSourceViewer v, Position pos) {
3780        Point p= v.getLineRange(pos, new Point(0, 0));
3781        int startLine= p.x+1;
3782        int endLine= p.x+p.y;
3783        
3784        String JavaDoc format;
3785        if (endLine < startLine)
3786            format= CompareMessages.TextMergeViewer_beforeLine_format;
3787        else
3788            format= CompareMessages.TextMergeViewer_range_format;
3789        return MessageFormat.format(format,
3790                    new String JavaDoc[] { Integer.toString(startLine),
3791                                    Integer.toString(endLine) } );
3792    }
3793    
3794    /*
3795     * Returns a description of the cursor position.
3796     *
3797     * @return a description of the cursor position
3798     */

3799    private String JavaDoc getCursorPosition(MergeSourceViewer v) {
3800        if (v != null) {
3801            StyledText styledText= v.getTextWidget();
3802            
3803            IDocument document= v.getDocument();
3804            if (document != null) {
3805                int offset= v.getVisibleRegion().getOffset();
3806                int caret= offset + styledText.getCaretOffset();
3807                
3808                try {
3809                    
3810                    int line=document.getLineOfOffset(caret);
3811                    
3812                    int lineOffset= document.getLineOffset(line);
3813                    int occurrences= 0;
3814                    for (int i= lineOffset; i < caret; i++)
3815                        if ('\t' == document.getChar(i))
3816                            ++ occurrences;
3817                            
3818                    int tabWidth= styledText.getTabs();
3819                    int column= caret - lineOffset + (tabWidth -1) * occurrences;
3820                    
3821                    String JavaDoc format= CompareMessages.TextMergeViewer_cursorPosition_format;
3822                    return MessageFormat.format(format,
3823                        new String JavaDoc[] { Integer.toString(line + 1), Integer.toString(column + 1) } );
3824                    
3825                } catch (BadLocationException x) {
3826                    // silently ignored
3827
}
3828            }
3829        }
3830        return ""; //$NON-NLS-1$
3831
}
3832
3833    protected void updateHeader() {
3834        
3835        super.updateHeader();
3836                
3837        updateControls();
3838    }
3839
3840    /*
3841     * Creates the two items for copying a difference range from one side to the other
3842     * and adds them to the given toolbar manager.
3843     */

3844    protected void createToolItems(ToolBarManager tbm) {
3845
3846        fHandlerService= CompareHandlerService.createFor(getCompareConfiguration().getContainer(), fLeft.getControl().getShell());
3847        
3848        final String JavaDoc ignoreAncestorActionKey= "action.IgnoreAncestor."; //$NON-NLS-1$
3849
Action ignoreAncestorAction= new Action() {
3850            public void run() {
3851                // First make sure the ancestor is hidden
3852
if (!isIgnoreAncestor())
3853                    getCompareConfiguration().setProperty(ICompareUIConstants.PROP_ANCESTOR_VISIBLE, Boolean.FALSE);
3854                // Then set the property to ignore the ancestor
3855
getCompareConfiguration().setProperty(ICompareUIConstants.PROP_IGNORE_ANCESTOR, Boolean.valueOf(!isIgnoreAncestor()));
3856                Utilities.initToggleAction(this, getResourceBundle(), ignoreAncestorActionKey, isIgnoreAncestor());
3857            }
3858        };
3859        ignoreAncestorAction.setChecked(isIgnoreAncestor());
3860        Utilities.initAction(ignoreAncestorAction, getResourceBundle(), ignoreAncestorActionKey);
3861        Utilities.initToggleAction(ignoreAncestorAction, getResourceBundle(), ignoreAncestorActionKey, isIgnoreAncestor());
3862        
3863        fIgnoreAncestorItem= new ActionContributionItem(ignoreAncestorAction);
3864        fIgnoreAncestorItem.setVisible(false);
3865        tbm.appendToGroup("modes", fIgnoreAncestorItem); //$NON-NLS-1$
3866

3867        tbm.add(new Separator());
3868        
3869        Action a= new Action() {
3870            public void run() {
3871                if (navigate(true, false, false)) {
3872                    endOfDocumentReached(true);
3873                }
3874            }
3875        };
3876        Utilities.initAction(a, getResourceBundle(), "action.NextDiff."); //$NON-NLS-1$
3877
fNextDiff= new ActionContributionItem(a);
3878        tbm.appendToGroup("navigation", fNextDiff); //$NON-NLS-1$
3879
// Don't register this action since it is probably registered by the container
3880

3881        a= new Action() {
3882            public void run() {
3883                if (navigate(false, false, false)) {
3884                    endOfDocumentReached(false);
3885                }
3886            }
3887        };
3888        Utilities.initAction(a, getResourceBundle(), "action.PrevDiff."); //$NON-NLS-1$
3889
fPreviousDiff= new ActionContributionItem(a);
3890        tbm.appendToGroup("navigation", fPreviousDiff); //$NON-NLS-1$
3891
// Don't register this action since it is probably registered by the container
3892

3893        a= new Action() {
3894            public void run() {
3895                if (navigate(true, false, true)) {
3896                    endOfDocumentReached(true);
3897                }
3898            }
3899        };
3900        Utilities.initAction(a, getResourceBundle(), "action.NextChange."); //$NON-NLS-1$
3901
fNextChange= new ActionContributionItem(a);
3902        tbm.appendToGroup("navigation", fNextChange); //$NON-NLS-1$
3903
fHandlerService.registerAction(a, "org.eclipse.compare.selectNextChange"); //$NON-NLS-1$
3904

3905        a= new Action() {
3906            public void run() {
3907                if (navigate(false, false, true)) {
3908                    endOfDocumentReached(false);
3909                }
3910            }
3911        };
3912        Utilities.initAction(a, getResourceBundle(), "action.PrevChange."); //$NON-NLS-1$
3913
fPreviousChange= new ActionContributionItem(a);
3914        tbm.appendToGroup("navigation", fPreviousChange); //$NON-NLS-1$
3915
fHandlerService.registerAction(a, "org.eclipse.compare.selectPreviousChange"); //$NON-NLS-1$
3916

3917        CompareConfiguration cc= getCompareConfiguration();
3918        
3919        if (cc.isRightEditable()) {
3920            a= new Action() {
3921                public void run() {
3922                    copyDiffLeftToRight();
3923                }
3924            };
3925            Utilities.initAction(a, getResourceBundle(), "action.CopyDiffLeftToRight."); //$NON-NLS-1$
3926
fCopyDiffLeftToRightItem= new ActionContributionItem(a);
3927            fCopyDiffLeftToRightItem.setVisible(true);
3928            tbm.appendToGroup("merge", fCopyDiffLeftToRightItem); //$NON-NLS-1$
3929
fHandlerService.registerAction(a, "org.eclipse.compare.copyLeftToRight"); //$NON-NLS-1$
3930
}
3931        
3932        if (cc.isLeftEditable()) {
3933            a= new Action() {
3934                public void run() {
3935                    copyDiffRightToLeft();
3936                }
3937            };
3938            Utilities.initAction(a, getResourceBundle(), "action.CopyDiffRightToLeft."); //$NON-NLS-1$
3939
fCopyDiffRightToLeftItem= new ActionContributionItem(a);
3940            fCopyDiffRightToLeftItem.setVisible(true);
3941            tbm.appendToGroup("merge", fCopyDiffRightToLeftItem); //$NON-NLS-1$
3942
fHandlerService.registerAction(a, "org.eclipse.compare.copyRightToLeft"); //$NON-NLS-1$
3943
}
3944        
3945        fIgnoreWhitespace= ChangePropertyAction.createIgnoreWhiteSpaceAction(getResourceBundle(), getCompareConfiguration());
3946        fIgnoreWhitespace.setActionDefinitionId(ICompareUIConstants.COMMAND_IGNORE_WHITESPACE);
3947        fLeft.addTextAction(fIgnoreWhitespace);
3948        fRight.addTextAction(fIgnoreWhitespace);
3949        fAncestor.addTextAction(fIgnoreWhitespace);
3950        fHandlerService.registerAction(fIgnoreWhitespace, fIgnoreWhitespace.getActionDefinitionId());
3951        
3952        showWhitespaceAction = new ShowWhitespaceAction(new MergeSourceViewer[] {
3953                fLeft, fRight, fAncestor
3954        });
3955        fHandlerService.registerAction(showWhitespaceAction, ITextEditorActionDefinitionIds.SHOW_WHITESPACE_CHARACTERS);
3956        
3957        toggleLineNumbersAction = new TextEditorPropertyAction(CompareMessages.TextMergeViewer_16, new MergeSourceViewer[] {
3958                fLeft, fRight, fAncestor
3959        }, AbstractDecoratedTextEditorPreferenceConstants.EDITOR_LINE_NUMBER_RULER);
3960        fHandlerService.registerAction(toggleLineNumbersAction, ITextEditorActionDefinitionIds.LINENUMBER_TOGGLE);
3961    }
3962    
3963    /* (non-Javadoc)
3964     * @see org.eclipse.compare.contentmergeviewer.ContentMergeViewer#handlePropertyChangeEvent(org.eclipse.jface.util.PropertyChangeEvent)
3965     */

3966    protected void handlePropertyChangeEvent(PropertyChangeEvent event) {
3967        
3968        String JavaDoc key= event.getProperty();
3969        
3970        if (key.equals(CompareConfiguration.IGNORE_WHITESPACE)
3971                || key.equals(ComparePreferencePage.SHOW_PSEUDO_CONFLICTS)) {
3972                    
3973            fShowPseudoConflicts= fPreferenceStore.getBoolean(ComparePreferencePage.SHOW_PSEUDO_CONFLICTS);
3974            
3975            update(true);
3976            selectFirstDiff(true);
3977            
3978// } else if (key.equals(ComparePreferencePage.USE_SPLINES)) {
3979
// fUseSplines= fPreferenceStore.getBoolean(ComparePreferencePage.USE_SPLINES);
3980
// invalidateLines();
3981

3982        } else if (key.equals(ComparePreferencePage.USE_SINGLE_LINE)) {
3983            fUseSingleLine= fPreferenceStore.getBoolean(ComparePreferencePage.USE_SINGLE_LINE);
3984// fUseResolveUI= fUseSingleLine;
3985
fBasicCenterCurve= null;
3986            updateResolveStatus();
3987            invalidateLines();
3988    
3989        } else if (key.equals(ComparePreferencePage.HIGHLIGHT_TOKEN_CHANGES)) {
3990            fHighlightTokenChanges= fPreferenceStore.getBoolean(ComparePreferencePage.HIGHLIGHT_TOKEN_CHANGES);
3991            updateResolveStatus();
3992            updatePresentation(null);
3993            
3994// } else if (key.equals(ComparePreferencePage.USE_RESOLVE_UI)) {
3995
// fUseResolveUI= fPreferenceStore.getBoolean(ComparePreferencePage.USE_RESOLVE_UI);
3996
// updateResolveStatus();
3997
// invalidateLines();
3998

3999        } else if (key.equals(fSymbolicFontName)) {
4000            updateFont();
4001            invalidateLines();
4002
4003        } else if (key.equals(INCOMING_COLOR) || key.equals(OUTGOING_COLOR) || key.equals(CONFLICTING_COLOR) || key.equals(RESOLVED_COLOR)) {
4004            updateColors(null);
4005            invalidateLines();
4006            invalidateTextPresentation();
4007            
4008        } else if (key.equals(ComparePreferencePage.SYNCHRONIZE_SCROLLING)) {
4009            boolean b= fPreferenceStore.getBoolean(ComparePreferencePage.SYNCHRONIZE_SCROLLING);
4010            setSyncScrolling(b);
4011        
4012        } else if (key.equals(ComparePreferencePage.SHOW_MORE_INFO)) {
4013            
4014            boolean b= fPreferenceStore.getBoolean(ComparePreferencePage.SHOW_MORE_INFO);
4015            if (b != fShowMoreInfo) {
4016                fShowMoreInfo= b;
4017                if (fShowMoreInfo)
4018                    updateStatus(fCurrentDiff);
4019                else
4020                    clearStatus();
4021            }
4022            
4023        } else {
4024            super.handlePropertyChangeEvent(event);
4025            
4026            if (key.equals(ICompareUIConstants.PROP_IGNORE_ANCESTOR)) {
4027                update(false);
4028                selectFirstDiff(true);
4029            }
4030        }
4031    }
4032    
4033    private void selectFirstDiff(boolean first) {
4034        
4035        if (fLeft == null || fRight == null) {
4036            return;
4037        }
4038        if (fLeft.getDocument() == null || fRight.getDocument() == null) {
4039            return;
4040        }
4041        
4042        Diff firstDiff= null;
4043        if (first)
4044            firstDiff= findNext(fRight, fChangeDiffs, -1, -1, false);
4045        else
4046            firstDiff= findPrev(fRight, fChangeDiffs, 9999999, 9999999, false);
4047        setCurrentDiff(firstDiff, true);
4048    }
4049    
4050    
4051    
4052    private void setSyncScrolling(boolean newMode) {
4053        if (fSynchronizedScrolling != newMode) {
4054            fSynchronizedScrolling= newMode;
4055            
4056            scrollVertical(0, 0, 0, null);
4057            
4058            // throw away central control (Sash or Canvas)
4059
Control center= getCenterControl();
4060            if (center != null && !center.isDisposed())
4061                center.dispose();
4062            
4063            fLeft.getTextWidget().getVerticalBar().setVisible(!fSynchronizedScrolling);
4064            fRight.getTextWidget().getVerticalBar().setVisible(!fSynchronizedScrolling);
4065    
4066            fComposite.layout(true);
4067        }
4068    }
4069                    
4070    protected void updateToolItems() {
4071        //only update toolbar items if diffs need to be calculated (which
4072
//dictates whether a toolbar gets added at all)
4073
if (!isPatchHunk()){
4074            if (fIgnoreAncestorItem != null)
4075                fIgnoreAncestorItem.setVisible(isThreeWay());
4076            
4077            if (fCopyDiffLeftToRightItem != null) {
4078                IAction a= fCopyDiffLeftToRightItem.getAction();
4079                if (a != null)
4080                    a.setEnabled(a.isEnabled() && !fHasErrors);
4081            }
4082            if (fCopyDiffRightToLeftItem != null) {
4083                IAction a= fCopyDiffRightToLeftItem.getAction();
4084                if (a != null)
4085                    a.setEnabled(a.isEnabled() && !fHasErrors);
4086            }
4087            
4088            super.updateToolItems();
4089        }
4090    }
4091    
4092    //---- painting lines
4093

4094    private void updateLines(IDocument d) {
4095
4096        boolean left= false;
4097        boolean right= false;
4098        
4099        // FIXME: this optimization is incorrect because
4100
// it doesn't take replace operations into account where
4101
// the old and new line count does not differ
4102
if (d == fLeft.getDocument()) {
4103            int l= fLeft.getLineCount();
4104            left= fLeftLineCount != l;
4105            fLeftLineCount= l;
4106        } else if (d == fRight.getDocument()) {
4107            int l= fRight.getLineCount();
4108            right= fRightLineCount != l;
4109            fRightLineCount= l;
4110        }
4111        
4112        if (left || right) {
4113            
4114            if (left) {
4115                if (fLeftCanvas != null)
4116                    fLeftCanvas.redraw();
4117            } else {
4118                if (fRightCanvas != null)
4119                    fRightCanvas.redraw();
4120            }
4121            Control center= getCenterControl();
4122            if (center != null)
4123                center.redraw();
4124
4125            updateVScrollBar();
4126            refreshBirdsEyeView();
4127        }
4128    }
4129    
4130    private void invalidateLines() {
4131        if (isThreeWay()) {
4132            if (Utilities.okToUse(fAncestorCanvas))
4133                fAncestorCanvas.redraw();
4134            if (fAncestor != null && fAncestor.isControlOkToUse())
4135                fAncestor.getTextWidget().redraw();
4136        }
4137        
4138        if (Utilities.okToUse(fLeftCanvas))
4139            fLeftCanvas.redraw();
4140            
4141        if (fLeft != null && fLeft.isControlOkToUse())
4142            fLeft.getTextWidget().redraw();
4143            
4144        if (Utilities.okToUse(getCenterControl()))
4145            getCenterControl().redraw();
4146            
4147        if (fRight != null && fRight.isControlOkToUse())
4148            fRight.getTextWidget().redraw();
4149            
4150        if (Utilities.okToUse(fRightCanvas))
4151            fRightCanvas.redraw();
4152    }
4153    
4154    private boolean showResolveUI() {
4155        if (!fUseResolveUI || !isThreeWay() || isIgnoreAncestor())
4156            return false;
4157        CompareConfiguration cc= getCompareConfiguration();
4158        // we only enable the new resolve UI if exactly one side is editable
4159
boolean l= cc.isLeftEditable();
4160        boolean r= cc.isRightEditable();
4161        //return (l && !r) || (r && !l);
4162
return l || r;
4163    }
4164    
4165    private void paintCenter(Canvas canvas, GC g) {
4166        
4167        Display display= canvas.getDisplay();
4168        
4169        checkForColorUpdate(display);
4170        
4171        if (! fSynchronizedScrolling)
4172            return;
4173
4174        int lineHeight= fLeft.getTextWidget().getLineHeight();
4175        int visibleHeight= fRight.getViewportHeight();
4176
4177        Point size= canvas.getSize();
4178        int x= 0;
4179        int w= size.x;
4180                
4181        g.setBackground(canvas.getBackground());
4182        g.fillRectangle(x+1, 0, w-2, size.y);
4183        
4184        if (!fIsMotif) {
4185            // draw thin line between center ruler and both texts
4186
g.setBackground(display.getSystemColor(SWT.COLOR_WIDGET_NORMAL_SHADOW));
4187            g.fillRectangle(0, 0, 1, size.y);
4188            g.fillRectangle(w-1, 0, 1, size.y);
4189        }
4190            
4191        if (! fHighlightRanges)
4192            return;
4193
4194        boolean showResolveUI= showResolveUI();
4195
4196        if (fChangeDiffs != null) {
4197            int lshift= fLeft.getVerticalScrollOffset();
4198            int rshift= fRight.getVerticalScrollOffset();
4199                    
4200            Point region= new Point(0, 0);
4201        
4202            Iterator e= fChangeDiffs.iterator();
4203            while (e.hasNext()) {
4204                Diff diff= (Diff) e.next();
4205                if (diff.isDeleted())
4206                    continue;
4207                
4208                if (fShowCurrentOnly2 && !isCurrentDiff(diff))
4209                    continue;
4210
4211                fLeft.getLineRange(diff.fLeftPos, region);
4212                int ly= (region.x * lineHeight) + lshift;
4213                int lh= region.y * lineHeight;
4214    
4215                fRight.getLineRange(diff.fRightPos, region);
4216                int ry= (region.x * lineHeight) + rshift;
4217                int rh= region.y * lineHeight;
4218    
4219                if (Math.max(ly+lh, ry+rh) < 0)
4220                    continue;
4221                if (Math.min(ly, ry) >= visibleHeight)
4222                    break;
4223    
4224                fPts[0]= x; fPts[1]= ly; fPts[2]= w; fPts[3]= ry;
4225                fPts[6]= x; fPts[7]= ly+lh; fPts[4]= w; fPts[5]= ry+rh;
4226                
4227                Color fillColor= getColor(display, getFillColor(diff));
4228                Color strokeColor= getColor(display, getStrokeColor(diff));
4229                
4230                if (fUseSingleLine) {
4231                    int w2= 3;
4232
4233                    g.setBackground(fillColor);
4234                    g.fillRectangle(0, ly, w2, lh); // left
4235
g.fillRectangle(w-w2, ry, w2, rh); // right
4236

4237                    g.setLineWidth(0 /* LW */);
4238                    g.setForeground(strokeColor);
4239                    g.drawRectangle(0-1, ly, w2, lh); // left
4240
g.drawRectangle(w-w2, ry, w2, rh); // right
4241

4242                    if (fUseSplines) {
4243                        int[] points= getCenterCurvePoints(w2, ly+lh/2, w-w2, ry+rh/2);
4244                        for (int i= 1; i < points.length; i++)
4245                            g.drawLine(w2+i-1, points[i-1], w2+i, points[i]);
4246                    } else {
4247                        g.drawLine(w2, ly+lh/2, w-w2, ry+rh/2);
4248                    }
4249                } else {
4250                    // two lines
4251
if (fUseSplines) {
4252                        g.setBackground(fillColor);
4253
4254                        g.setLineWidth(0 /* LW */);
4255                        g.setForeground(strokeColor);
4256
4257                        int[] topPoints= getCenterCurvePoints(fPts[0], fPts[1], fPts[2], fPts[3]);
4258                        int[] bottomPoints= getCenterCurvePoints(fPts[6], fPts[7], fPts[4], fPts[5]);
4259                        g.setForeground(fillColor);
4260                        g.drawLine(0, bottomPoints[0], 0, topPoints[0]);
4261                        for (int i= 1; i < bottomPoints.length; i++) {
4262                            g.setForeground(fillColor);
4263                            g.drawLine(i, bottomPoints[i], i, topPoints[i]);
4264                            g.setForeground(strokeColor);
4265                            g.drawLine(i-1, topPoints[i-1], i, topPoints[i]);
4266                            g.drawLine(i-1, bottomPoints[i-1], i, bottomPoints[i]);
4267                        }
4268                    } else {
4269                        g.setBackground(fillColor);
4270                        g.fillPolygon(fPts);
4271
4272                        g.setLineWidth(0 /* LW */);
4273                        g.setForeground(strokeColor);
4274                        g.drawLine(fPts[0], fPts[1], fPts[2], fPts[3]);
4275                        g.drawLine(fPts[6], fPts[7], fPts[4], fPts[5]);
4276                    }
4277                }
4278                
4279                if (fUseSingleLine && showResolveUI && diff.isUnresolvedIncomingOrConflicting()) {
4280                    // draw resolve state
4281
int cx= (w-RESOLVE_SIZE)/2;
4282                    int cy= ((ly+lh/2) + (ry+rh/2) - RESOLVE_SIZE)/2;
4283                    
4284                    g.setBackground(fillColor);
4285                    g.fillRectangle(cx, cy, RESOLVE_SIZE, RESOLVE_SIZE);
4286                    
4287                    g.setForeground(strokeColor);
4288                    g.drawRectangle(cx, cy, RESOLVE_SIZE, RESOLVE_SIZE);
4289                }
4290            }
4291        }
4292    }
4293    
4294    private int[] getCenterCurvePoints(int startx, int starty, int endx, int endy) {
4295        if (fBasicCenterCurve == null)
4296            buildBaseCenterCurve(endx-startx);
4297        double height= endy - starty;
4298        height= height/2;
4299        int width= endx-startx;
4300        int[] points= new int[width];
4301        for (int i= 0; i < width; i++) {
4302            points[i]= (int) (-height * fBasicCenterCurve[i] + height + starty);
4303        }
4304        return points;
4305    }
4306
4307    private void buildBaseCenterCurve(int w) {
4308        double width= w;
4309        fBasicCenterCurve= new double[getCenterWidth()];
4310        for (int i= 0; i < getCenterWidth(); i++) {
4311            double r= i / width;
4312            fBasicCenterCurve[i]= Math.cos(Math.PI * r);
4313        }
4314    }
4315
4316    private void paintSides(GC g, MergeSourceViewer tp, Canvas canvas, boolean right) {
4317        
4318        Display display= canvas.getDisplay();
4319        
4320        int lineHeight= tp.getTextWidget().getLineHeight();
4321        int visibleHeight= tp.getViewportHeight();
4322
4323        Point size= canvas.getSize();
4324        int x= 0;
4325        int w= fMarginWidth;
4326        int w2= w/2;
4327            
4328        g.setBackground(canvas.getBackground());
4329        g.fillRectangle(x, 0, w, size.y);
4330
4331        if (!fIsMotif) {
4332            // draw thin line between ruler and text
4333
g.setBackground(display.getSystemColor(SWT.COLOR_WIDGET_NORMAL_SHADOW));
4334            if (right)
4335                g.fillRectangle(0, 0, 1, size.y);
4336            else
4337                g.fillRectangle(size.x-1, 0, 1, size.y);
4338        }
4339
4340        if (! fHighlightRanges)
4341            return;
4342
4343        if (fChangeDiffs != null) {
4344            int shift= tp.getVerticalScrollOffset() + (2-LW);
4345                
4346            Point region= new Point(0, 0);
4347            Iterator e= fChangeDiffs.iterator();
4348            while (e.hasNext()) {
4349                Diff diff= (Diff) e.next();
4350                if (diff.isDeleted())
4351                    continue;
4352                
4353                if (fShowCurrentOnly2 && !isCurrentDiff(diff))
4354                    continue;
4355
4356                tp.getLineRange(diff.getPosition(tp), region);
4357                int y= (region.x * lineHeight) + shift;
4358                int h= region.y * lineHeight;
4359    
4360                if (y+h < 0)
4361                    continue;
4362                if (y >= visibleHeight)
4363                    break;
4364                    
4365                g.setBackground(getColor(display, getFillColor(diff)));
4366                if (right)
4367                    g.fillRectangle(x, y, w2, h);
4368                else
4369                    g.fillRectangle(x+w2, y, w2, h);
4370    
4371                g.setLineWidth(0 /* LW */);
4372                g.setForeground(getColor(display, getStrokeColor(diff)));
4373                if (right)
4374                    g.drawRectangle(x-1, y-1, w2, h);
4375                else
4376                    g.drawRectangle(x+w2, y-1, w2, h);
4377            }
4378        }
4379    }
4380    
4381    private void paint(PaintEvent event, MergeSourceViewer tp) {
4382        
4383        if (! fHighlightRanges)
4384            return;
4385        if (fChangeDiffs == null)
4386            return;
4387
4388        Control canvas= (Control) event.widget;
4389        GC g= event.gc;
4390        
4391        Display display= canvas.getDisplay();
4392        
4393        int lineHeight= tp.getTextWidget().getLineHeight();
4394        int w= canvas.getSize().x;
4395        int shift= tp.getVerticalScrollOffset() + (2-LW);
4396        int maxh= event.y+event.height; // visibleHeight
4397

4398        //if (fIsMotif)
4399
shift+= fTopInset;
4400                
4401        Point range= new Point(0, 0);
4402                
4403        Iterator e= fChangeDiffs.iterator();
4404        while (e.hasNext()) {
4405            Diff diff= (Diff) e.next();
4406            if (diff.isDeleted())
4407                continue;
4408            
4409            if (fShowCurrentOnly && !isCurrentDiff(diff))
4410                continue;
4411
4412            tp.getLineRange(diff.getPosition(tp), range);
4413            int y= (range.x * lineHeight) + shift;
4414            int h= range.y * lineHeight;
4415            
4416            if (y+h < event.y)
4417                continue;
4418            if (y > maxh)
4419                break;
4420            
4421            g.setBackground(getColor(display, getStrokeColor(diff)));
4422            g.fillRectangle(0, y-1, w, LW);
4423            g.fillRectangle(0, y+h-1, w, LW);
4424        }
4425    }
4426
4427    private RGB getFillColor(Diff diff) {
4428        boolean selected= fCurrentDiff != null && fCurrentDiff.fParent == diff;
4429        RGB selected_fill= getBackground(null);
4430        if (isThreeWay() && !isIgnoreAncestor()) {
4431            switch (diff.fDirection) {
4432            case RangeDifference.RIGHT:
4433                if (fLeftIsLocal)
4434                    return selected ? selected_fill : INCOMING_FILL;
4435                return selected ? selected_fill : OUTGOING_FILL;
4436            case RangeDifference.ANCESTOR:
4437                return selected ? selected_fill : CONFLICT_FILL;
4438            case RangeDifference.LEFT:
4439                if (fLeftIsLocal)
4440                    return selected ? selected_fill : OUTGOING_FILL;
4441                return selected ? selected_fill : INCOMING_FILL;
4442            case RangeDifference.CONFLICT:
4443                return selected ? selected_fill : CONFLICT_FILL;
4444            }
4445            return null;
4446        }
4447        return selected ? selected_fill : OUTGOING_FILL;
4448    }
4449    
4450    private RGB getStrokeColor(Diff diff) {
4451        boolean selected= fCurrentDiff != null && fCurrentDiff.fParent == diff;
4452        
4453        if (isThreeWay() && !isIgnoreAncestor()) {
4454            switch (diff.fDirection) {
4455            case RangeDifference.RIGHT:
4456                if (fLeftIsLocal)
4457                    return selected ? SELECTED_INCOMING : INCOMING;
4458                return selected ? SELECTED_OUTGOING : OUTGOING;
4459            case RangeDifference.ANCESTOR:
4460                return selected ? SELECTED_CONFLICT : CONFLICT;
4461            case RangeDifference.LEFT:
4462                if (fLeftIsLocal)
4463                    return selected ? SELECTED_OUTGOING : OUTGOING;
4464                return selected ? SELECTED_INCOMING : INCOMING;
4465            case RangeDifference.CONFLICT:
4466                return selected ? SELECTED_CONFLICT : CONFLICT;
4467            }
4468            return null;
4469        }
4470        return selected ? SELECTED_OUTGOING : OUTGOING;
4471    }
4472    
4473    private Color getColor(Display display, RGB rgb) {
4474        if (rgb == null)
4475            return null;
4476        if (fColors == null)
4477            fColors= new HashMap(20);
4478        Color c= (Color) fColors.get(rgb);
4479        if (c == null) {
4480            c= new Color(display, rgb);
4481            fColors.put(rgb, c);
4482        }
4483        return c;
4484    }
4485            
4486    static RGB interpolate(RGB fg, RGB bg, double scale) {
4487        if (fg != null && bg != null)
4488            return new RGB(
4489                (int)((1.0-scale) * fg.red + scale * bg.red),
4490                (int)((1.0-scale) * fg.green + scale * bg.green),
4491                (int)((1.0-scale) * fg.blue + scale * bg.blue)
4492            );
4493        if (fg != null)
4494            return fg;
4495        if (bg != null)
4496            return bg;
4497        return new RGB(128, 128, 128); // a gray
4498
}
4499    
4500    //---- Navigating and resolving Diffs
4501

4502    private Diff getNextVisibleDiff(boolean down, boolean deep) {
4503        Diff diff= null;
4504        MergeSourceViewer part= getNavigationPart();
4505        if (part == null)
4506            return null;
4507        Point s = part.getSelectedRange();
4508        for (;;) {
4509            diff = null;
4510            diff = internalGetNextDiff(down, deep, part, s);
4511            if (diff != null && diff.fDirection == RangeDifference.ANCESTOR
4512                    && !isAncestorVisible()) {
4513                Position position = diff.getPosition(part);
4514                s = new Point(position.getOffset(), position.getLength());
4515                diff= null;
4516                continue;
4517            }
4518            break;
4519        }
4520        return diff;
4521    }
4522    
4523    private Diff internalGetNextDiff(boolean down, boolean deep, MergeSourceViewer part, Point s) {
4524        if (fChangeDiffs != null) {
4525            if (down)
4526                return findNext(part, fChangeDiffs, s.x, s.x+s.y, deep);
4527            return findPrev(part, fChangeDiffs, s.x, s.x+s.y, deep);
4528        }
4529        return null;
4530    }
4531    
4532    private MergeSourceViewer getNavigationPart() {
4533        MergeSourceViewer part= fFocusPart;
4534        if (part == null)
4535            part= fRight;
4536        return part;
4537    }
4538
4539    private Diff getWrappedDiff(Diff diff, boolean down) {
4540        if (fChangeDiffs != null && fChangeDiffs.size() > 0) {
4541            if (down)
4542                return (Diff) fChangeDiffs.get(0);
4543            return (Diff) fChangeDiffs.get(fChangeDiffs.size()-1);
4544        }
4545        return null;
4546    }
4547    
4548    /*
4549     * Returns true if end (or beginning) of document reached.
4550     */

4551    private boolean navigate(boolean down, boolean wrap, boolean deep) {
4552        Diff diff= null;
4553        boolean wrapped = false;
4554        for (;;) {
4555            diff = getNextVisibleDiff(down, deep);
4556            if (diff == null && wrap) {
4557                if (wrapped)
4558                    // We've already wrapped once so break out
4559
break;
4560                wrapped = true;
4561                diff = getWrappedDiff(diff, down);
4562            }
4563            if (diff != null)
4564                setCurrentDiff(diff, true, deep);
4565            if (diff != null && diff.fDirection == RangeDifference.ANCESTOR
4566                    && !isAncestorVisible())
4567                continue;
4568            break;
4569        }
4570        return diff == null;
4571    }
4572    
4573    private void endOfDocumentReached(boolean down) {
4574        Control c= getControl();
4575        if (Utilities.okToUse(c)) {
4576            handleEndOfDocumentReached(c.getShell(), down);
4577        }
4578    }
4579    
4580    private void handleEndOfDocumentReached(Shell shell, boolean next) {
4581        boolean hasNextElement = hasNextElement(next);
4582        IPreferenceStore store = CompareUIPlugin.getDefault().getPreferenceStore();
4583        String JavaDoc value = store.getString(ICompareUIConstants.PREF_NAVIGATION_END_ACTION);
4584        if (!value.equals(ICompareUIConstants.PREF_VALUE_PROMPT)) {
4585            // We only want to do the automatic thing if there is something to do
4586
if (hasNextElement || store.getString(ICompareUIConstants.PREF_NAVIGATION_END_ACTION).equals(ICompareUIConstants.PREF_VALUE_LOOP)) {
4587                performEndOfDocumentAction(shell, store, ICompareUIConstants.PREF_NAVIGATION_END_ACTION, next);
4588                return;
4589            }
4590        }
4591        shell.getDisplay().beep();
4592        if (hasNextElement) {
4593            String JavaDoc loopMessage;
4594            String JavaDoc nextMessage;
4595            String JavaDoc message;
4596            String JavaDoc title;
4597            if (next) {
4598                title = CompareMessages.TextMergeViewer_0;
4599                message = CompareMessages.TextMergeViewer_1;
4600                loopMessage = CompareMessages.TextMergeViewer_2;
4601                nextMessage = CompareMessages.TextMergeViewer_3;
4602            } else {
4603                title = CompareMessages.TextMergeViewer_4;
4604                message = CompareMessages.TextMergeViewer_5;
4605                loopMessage = CompareMessages.TextMergeViewer_6;
4606                nextMessage = CompareMessages.TextMergeViewer_7;
4607            }
4608            String JavaDoc[] localLoopOption = new String JavaDoc[] { loopMessage, ICompareUIConstants.PREF_VALUE_LOOP };
4609            String JavaDoc[] nextElementOption = new String JavaDoc[] { nextMessage, ICompareUIConstants.PREF_VALUE_NEXT};
4610            NavigationEndDialog dialog = new NavigationEndDialog(shell,
4611                    title,
4612                    null,
4613                    message,
4614                    new String JavaDoc[][] {
4615                    localLoopOption,
4616                    nextElementOption,
4617            });
4618            int result = dialog.open();
4619            if (result == Window.OK) {
4620                performEndOfDocumentAction(shell, store, ICompareUIConstants.PREF_NAVIGATION_END_ACTION_LOCAL, next);
4621                if (dialog.getToggleState()) {
4622                    store.putValue(ICompareUIConstants.PREF_NAVIGATION_END_ACTION, store.getString(ICompareUIConstants.PREF_NAVIGATION_END_ACTION_LOCAL));
4623                }
4624            }
4625        } else {
4626            String JavaDoc message;
4627            String JavaDoc title;
4628            if (next) {
4629                title = CompareMessages.TextMergeViewer_8;
4630                message = CompareMessages.TextMergeViewer_9;
4631            } else {
4632                title = CompareMessages.TextMergeViewer_10;
4633                message = CompareMessages.TextMergeViewer_11;
4634            }
4635            if (MessageDialog.openQuestion(shell, title, message)) {
4636                selectFirstDiff(next);
4637            }
4638        }
4639    }
4640    
4641    private void performEndOfDocumentAction(Shell shell, IPreferenceStore store, String JavaDoc key, boolean next) {
4642        String JavaDoc value = store.getString(key);
4643        if (value.equals(ICompareUIConstants.PREF_VALUE_NEXT)) {
4644            ICompareNavigator navigator = getCompareConfiguration().getContainer().getNavigator();
4645            if (hasNextElement(next))
4646                navigator.selectChange(next);
4647            else
4648                shell.getDisplay().beep();
4649        } else {
4650            selectFirstDiff(next);
4651        }
4652    }
4653    
4654    private boolean hasNextElement(boolean down) {
4655        ICompareNavigator navigator = getCompareConfiguration().getContainer().getNavigator();
4656        if (navigator instanceof CompareNavigator) {
4657            CompareNavigator n = (CompareNavigator) navigator;
4658            return n.hasChange(down);
4659        }
4660        return false;
4661    }
4662
4663    /*
4664     * Find the Diff that overlaps with the given TextPart's text range.
4665     * If the range doesn't overlap with any range <code>null</code>
4666     * is returned.
4667     */

4668    private Diff findDiff(MergeSourceViewer tp, int rangeStart, int rangeEnd) {
4669        if (fChangeDiffs != null) {
4670            Iterator e= fChangeDiffs.iterator();
4671            while (e.hasNext()) {
4672                Diff diff= (Diff) e.next();
4673                if (diff.overlaps(tp, rangeStart, rangeEnd))
4674                    return diff;
4675            }
4676        }
4677        return null;
4678    }
4679    
4680    private static Diff findNext(MergeSourceViewer tp, List JavaDoc v, int start, int end, boolean deep) {
4681        for (int i= 0; i < v.size(); i++) {
4682            Diff diff= (Diff) v.get(i);
4683            Position p= diff.getPosition(tp);
4684            if (p != null) {
4685                int startOffset= p.getOffset();
4686                if (end < startOffset) // <=
4687
return diff;
4688                if (deep && diff.fDiffs != null) {
4689                    Diff d= null;
4690                    int endOffset= startOffset + p.getLength();
4691                    if (start == startOffset && (end == endOffset || end == endOffset-1)) {
4692                        d= findNext(tp, diff.fDiffs, start-1, start-1, deep);
4693                    } else if (end < endOffset) {
4694                        d= findNext(tp, diff.fDiffs, start, end, deep);
4695                    }
4696                    if (d != null)
4697                        return d;
4698                }
4699            }
4700        }
4701        return null;
4702    }
4703    
4704    private static Diff findPrev(MergeSourceViewer tp, List JavaDoc v, int start, int end, boolean deep) {
4705        for (int i= v.size()-1; i >= 0; i--) {
4706            Diff diff= (Diff) v.get(i);
4707            Position p= diff.getPosition(tp);
4708            if (p != null) {
4709                int startOffset= p.getOffset();
4710                int endOffset= startOffset + p.getLength();
4711                if (start > endOffset) {
4712                    if (deep && diff.fDiffs != null) {
4713                        // If we are going deep, find the last change in the diff
4714
return findPrev(tp, diff.fDiffs, end, end, deep);
4715                    }
4716                    return diff;
4717                }
4718                if (deep && diff.fDiffs != null) {
4719                    Diff d= null;
4720                    if (start == startOffset && end == endOffset) {
4721                        // A whole diff is selected so we'll fall through
4722
// and go the the last change in the previous diff
4723
} else if (start >= startOffset) {
4724                        // If we are at or before the first diff, select the
4725
// entire diff so next and previous are symmetrical
4726
if (isFirstDiff(tp, startOffset, diff.fDiffs)) {
4727                            return diff;
4728                        }
4729                        d= findPrev(tp, diff.fDiffs, start, end, deep);
4730                    }
4731                    if (d != null)
4732                        return d;
4733                }
4734            }
4735        }
4736        return null;
4737    }
4738        
4739    private static boolean isFirstDiff(MergeSourceViewer tp, int startOffset,
4740            ArrayList diffs) {
4741        if (diffs.isEmpty())
4742            return false;
4743        Diff diff = (Diff)diffs.get(0);
4744        Position p= diff.getPosition(tp);
4745        return (p.getOffset() >= startOffset);
4746    }
4747
4748    /*
4749     * Set the currently active Diff and update the toolbars controls and lines.
4750     * If <code>revealAndSelect</code> is <code>true</code> the Diff is revealed and
4751     * selected in both TextParts.
4752     */

4753    private void setCurrentDiff(Diff d, boolean revealAndSelect) {
4754        setCurrentDiff(d, revealAndSelect, false);
4755    }
4756        
4757    /*
4758     * Set the currently active Diff and update the toolbars controls and lines.
4759     * If <code>revealAndSelect</code> is <code>true</code> the Diff is revealed and
4760     * selected in both TextParts.
4761     */

4762    private void setCurrentDiff(Diff d, boolean revealAndSelect, boolean deep) {
4763
4764// if (d == fCurrentDiff)
4765
// return;
4766

4767        if (fCenterButton != null && !fCenterButton.isDisposed())
4768            fCenterButton.setVisible(false);
4769
4770        Diff oldDiff= fCurrentDiff;
4771                    
4772        if (d != null && revealAndSelect) {
4773            
4774            // before we set fCurrentDiff we change the selection
4775
// so that the paint code uses the old background colors
4776
// otherwise selection isn't drawn correctly
4777
if (d.fIsToken || !fHighlightTokenChanges || deep || !d.hasChildren()) {
4778                if (isThreeWay() && !isIgnoreAncestor())
4779                    fAncestor.setSelection(d.fAncestorPos);
4780                fLeft.setSelection(d.fLeftPos);
4781                fRight.setSelection(d.fRightPos);
4782            } else {
4783                if (isThreeWay() && !isIgnoreAncestor())
4784                    fAncestor.setSelection(new Position(d.fAncestorPos.offset, 0));
4785                fLeft.setSelection(new Position(d.fLeftPos.offset, 0));
4786                fRight.setSelection(new Position(d.fRightPos.offset, 0));
4787            }
4788            
4789            // now switch diffs
4790
fCurrentDiff= d;
4791            revealDiff(d, d.fIsToken);
4792        } else {
4793            fCurrentDiff= d;
4794        }
4795        
4796        Diff d1= oldDiff != null ? oldDiff.fParent : null;
4797        Diff d2= fCurrentDiff != null ? fCurrentDiff.fParent : null;
4798        if (d1 != d2) {
4799            updateDiffBackground(d1);
4800            updateDiffBackground(d2);
4801        }
4802        
4803        updateControls();
4804        invalidateLines();
4805        refreshBirdsEyeView();
4806    }
4807    
4808    /*
4809     * Smart determines whether
4810     */

4811    private void revealDiff(Diff d, boolean smart) {
4812        
4813        boolean ancestorIsVisible= false;
4814        boolean leftIsVisible= false;
4815        boolean rightIsVisible= false;
4816
4817        if (smart) {
4818            Point region= new Point(0, 0);
4819            // find the starting line of the diff in all text widgets
4820
int ls= fLeft.getLineRange(d.fLeftPos, region).x;
4821            int rs= fRight.getLineRange(d.fRightPos, region).x;
4822            
4823            if (isThreeWay() && !isIgnoreAncestor()) {
4824                int as= fAncestor.getLineRange(d.fAncestorPos, region).x;
4825                if (as >= fAncestor.getTopIndex() && as <= fAncestor.getBottomIndex())
4826                    ancestorIsVisible= true;
4827            }
4828
4829            if (ls >= fLeft.getTopIndex() && ls <= fLeft.getBottomIndex())
4830                leftIsVisible= true;
4831
4832            if (rs >= fRight.getTopIndex() && rs <= fRight.getBottomIndex())
4833                rightIsVisible= true;
4834        }
4835
4836        // vertical scrolling
4837
if (!leftIsVisible || !rightIsVisible) {
4838            int avpos= 0, lvpos= 0, rvpos= 0;
4839            
4840            MergeSourceViewer allButThis= null;
4841            if (leftIsVisible) {
4842                avpos= lvpos= rvpos= realToVirtualPosition(fLeft, fLeft.getTopIndex());
4843                allButThis= fLeft;
4844            } else if (rightIsVisible) {
4845                avpos= lvpos= rvpos= realToVirtualPosition(fRight, fRight.getTopIndex());
4846                allButThis= fRight;
4847            } else if (ancestorIsVisible) {
4848                avpos= lvpos= rvpos= realToVirtualPosition(fAncestor, fAncestor.getTopIndex());
4849                allButThis= fAncestor;
4850            } else {
4851                if (fAllDiffs != null) {
4852                    int vpos= 0;
4853                    Iterator e= fAllDiffs.iterator();
4854                    for (int i= 0; e.hasNext(); i++) {
4855                        Diff diff= (Diff) e.next();
4856                        if (diff == d)
4857                            break;
4858                        if (fSynchronizedScrolling) {
4859                            vpos+= diff.getMaxDiffHeight();
4860                        } else {
4861                            avpos+= diff.getAncestorHeight();
4862                            lvpos+= diff.getLeftHeight();
4863                            rvpos+= diff.getRightHeight();
4864                        }
4865                    }
4866                    if (fSynchronizedScrolling)
4867                        avpos= lvpos= rvpos= vpos;
4868                }
4869                int delta= fRight.getViewportLines()/4;
4870                avpos-= delta;
4871                if (avpos < 0)
4872                    avpos= 0;
4873                lvpos-= delta;
4874                if (lvpos < 0)
4875                    lvpos= 0;
4876                rvpos-= delta;
4877                if (rvpos < 0)
4878                    rvpos= 0;
4879            }
4880                            
4881            scrollVertical(avpos, lvpos, rvpos, allButThis);
4882            
4883            if (fVScrollBar != null)
4884                fVScrollBar.setSelection(avpos);
4885        }
4886        
4887        // horizontal scrolling
4888
if (d.fIsToken) {
4889            // we only scroll horizontally for token diffs
4890
reveal(fAncestor, d.fAncestorPos);
4891            reveal(fLeft, d.fLeftPos);
4892            reveal(fRight, d.fRightPos);
4893        } else {
4894            // in all other cases we reset the horizontal offset
4895
hscroll(fAncestor);
4896            hscroll(fLeft);
4897            hscroll(fRight);
4898        }
4899    }
4900    
4901    private static void reveal(MergeSourceViewer v, Position p) {
4902        if (v != null && p != null) {
4903            StyledText st= v.getTextWidget();
4904            if (st != null) {
4905                Rectangle r= st.getClientArea();
4906                if (!r.isEmpty()) // workaround for #7320: Next diff scrolls when going into current diff
4907
v.revealRange(p.offset, p.length);
4908            }
4909        }
4910    }
4911    
4912    private static void hscroll(MergeSourceViewer v) {
4913        if (v != null) {
4914            StyledText st= v.getTextWidget();
4915            if (st != null)
4916                st.setHorizontalIndex(0);
4917        }
4918    }
4919    
4920    //--------------------------------------------------------------------------------
4921

4922    void copyAllUnresolved(boolean leftToRight) {
4923        if (fChangeDiffs != null && isThreeWay() && !isIgnoreAncestor()) {
4924            IRewriteTarget target= leftToRight ? fRight.getRewriteTarget() : fLeft.getRewriteTarget();
4925            boolean compoundChangeStarted= false;
4926            Iterator e= fChangeDiffs.iterator();
4927            try {
4928                while (e.hasNext()) {
4929                    Diff diff= (Diff) e.next();
4930                    switch (diff.fDirection) {
4931                    case RangeDifference.LEFT:
4932                        if (leftToRight) {
4933                            if (!compoundChangeStarted) {
4934                                target.beginCompoundChange();
4935                                compoundChangeStarted= true;
4936                            }
4937                            copy(diff, leftToRight);
4938                        }
4939                        break;
4940                    case RangeDifference.RIGHT:
4941                        if (!leftToRight) {
4942                            if (!compoundChangeStarted) {
4943                                target.beginCompoundChange();
4944                                compoundChangeStarted= true;
4945                            }
4946                            copy(diff, leftToRight);
4947                        }
4948                        break;
4949                    default:
4950                        continue;
4951                    }
4952                }
4953            } finally {
4954                if (compoundChangeStarted) {
4955                    target.endCompoundChange();
4956                }
4957            }
4958        }
4959    }
4960    
4961    /*
4962     * Copy whole document from one side to the other.
4963     */

4964    protected void copy(boolean leftToRight) {
4965        if (!validateChange(!leftToRight))
4966            return;
4967        if (showResolveUI()) {
4968            copyAllUnresolved(leftToRight);
4969            invalidateLines();
4970            return;
4971        }
4972                
4973        if (leftToRight) {
4974            if (fLeft.getEnabled()) {
4975                // copy text
4976
String JavaDoc text= fLeft.getTextWidget().getText();
4977                fRight.getTextWidget().setText(text);
4978                fRight.setEnabled(true);
4979            } else {
4980                // delete
4981
fRight.getTextWidget().setText(""); //$NON-NLS-1$
4982
fRight.setEnabled(false);
4983            }
4984            fRightLineCount= fRight.getLineCount();
4985            setRightDirty(true);
4986        } else {
4987            if (fRight.getEnabled()) {
4988                // copy text
4989
String JavaDoc text= fRight.getTextWidget().getText();
4990                fLeft.getTextWidget().setText(text);
4991                fLeft.setEnabled(true);
4992            } else {
4993                // delete
4994
fLeft.getTextWidget().setText(""); //$NON-NLS-1$
4995
fLeft.setEnabled(false);
4996            }
4997            fLeftLineCount= fLeft.getLineCount();
4998            setLeftDirty(true);
4999        }
5000        update(false);
5001        selectFirstDiff(true);
5002    }
5003
5004    private void copyDiffLeftToRight() {
5005        copy(fCurrentDiff, true, false);
5006    }
5007
5008    private void copyDiffRightToLeft() {
5009        copy(fCurrentDiff, false, false);
5010    }
5011        
5012    /*
5013     * Copy the contents of the given diff from one side to the other.
5014     */

5015    private void copy(Diff diff, boolean leftToRight, boolean gotoNext) {
5016        if (copy(diff, leftToRight)) {
5017            if (gotoNext) {
5018                navigate(true, true, false /* don't step in */);
5019            } else {
5020                revealDiff(diff, true);
5021                updateControls();
5022            }
5023        }
5024    }
5025
5026    /*
5027     * Copy the contents of the given diff from one side to the other but
5028     * doesn't reveal anything.
5029     * Returns true if copy was successful.
5030     */

5031    private boolean copy(Diff diff, boolean leftToRight) {
5032        
5033        if (diff != null && !diff.isResolved()) {
5034            if (!validateChange(!leftToRight))
5035                return false;
5036            Position fromPos= null;
5037            Position toPos= null;
5038            IDocument fromDoc= null;
5039            IDocument toDoc= null;
5040
5041            if (leftToRight) {
5042                fRight.setEnabled(true);
5043                fromPos= diff.fLeftPos;
5044                toPos= diff.fRightPos;
5045                fromDoc= fLeft.getDocument();
5046                toDoc= fRight.getDocument();
5047            } else {
5048                fLeft.setEnabled(true);
5049                fromPos= diff.fRightPos;
5050                toPos= diff.fLeftPos;
5051                fromDoc= fRight.getDocument();
5052                toDoc= fLeft.getDocument();
5053            }
5054            
5055            if (fromDoc != null) {
5056                
5057                int fromStart= fromPos.getOffset();
5058                int fromLen= fromPos.getLength();
5059                
5060                int toStart= toPos.getOffset();
5061                int toLen= toPos.getLength();
5062
5063                try {
5064                    String JavaDoc s= null;
5065                                            
5066                    switch (diff.fDirection) {
5067                    case RangeDifference.RIGHT:
5068                    case RangeDifference.LEFT:
5069                        s= fromDoc.get(fromStart, fromLen);
5070                        break;
5071                    case RangeDifference.ANCESTOR:
5072                        break;
5073                    case RangeDifference.CONFLICT:
5074                        if (APPEND_CONFLICT) {
5075                            s= toDoc.get(toStart, toLen);
5076                            s+= fromDoc.get(fromStart, fromLen);
5077                        } else
5078                            s= fromDoc.get(fromStart, fromLen);
5079                        break;
5080                    }
5081                    if (s != null) {
5082                        toDoc.replace(toStart, toLen, s);
5083                        toPos.setOffset(toStart);
5084                        toPos.setLength(s.length());
5085                    }
5086                
5087                } catch (BadLocationException e) {
5088                    // silently ignored
5089
}
5090            }
5091        
5092            diff.setResolved(true);
5093            updateResolveStatus();
5094            
5095            return true;
5096        }
5097        return false;
5098    }
5099
5100    private boolean validateChange(boolean left) {
5101        ContributorInfo info;
5102        if (left)
5103            info = fLeftContributor;
5104        else
5105            info = fRightContributor;
5106        
5107        return info.validateChange();
5108    }
5109
5110    //---- scrolling
5111

5112    /*
5113     * Calculates virtual height (in lines) of views by adding the maximum of corresponding diffs.
5114     */

5115    private int getVirtualHeight() {
5116        int h= 1;
5117        if (fAllDiffs != null) {
5118            Iterator e= fAllDiffs.iterator();
5119            for (int i= 0; e.hasNext(); i++) {
5120                Diff diff= (Diff) e.next();
5121                h+= diff.getMaxDiffHeight();
5122            }
5123        }
5124        return h;
5125    }
5126    
5127    /*
5128     * Calculates height (in lines) of right view by adding the height of the right diffs.
5129     */

5130    private int getRightHeight() {
5131        int h= 1;
5132        if (fAllDiffs != null) {
5133            Iterator e= fAllDiffs.iterator();
5134            for (int i= 0; e.hasNext(); i++) {
5135                Diff diff= (Diff) e.next();
5136                h+= diff.getRightHeight();
5137            }
5138        }
5139        return h;
5140    }
5141    
5142    /*
5143     * The height of the TextEditors in lines.
5144     */

5145    private int getViewportHeight() {
5146        StyledText te= fLeft.getTextWidget();
5147        
5148        int vh= te.getClientArea().height;
5149        if (vh == 0) {
5150            Rectangle trim= te.computeTrim(0, 0, 0, 0);
5151            int scrollbarHeight= trim.height;
5152            
5153            int headerHeight= getHeaderHeight();
5154    
5155            Composite composite= (Composite) getControl();
5156            Rectangle r= composite.getClientArea();
5157                            
5158            vh= r.height-headerHeight-scrollbarHeight;
5159        }
5160
5161        return vh / te.getLineHeight();
5162    }
5163    
5164    /*
5165     * Returns the virtual position for the given view position.
5166     */

5167    private int realToVirtualPosition(MergeSourceViewer w, int vpos) {
5168
5169        if (! fSynchronizedScrolling || fAllDiffs == null)
5170            return vpos;
5171                
5172        int viewPos= 0; // real view position
5173
int virtualPos= 0; // virtual position
5174
Point region= new Point(0, 0);
5175        
5176        Iterator e= fAllDiffs.iterator();
5177        while (e.hasNext()) {
5178            Diff diff= (Diff) e.next();
5179            Position pos= diff.getPosition(w);
5180            w.getLineRange(pos, region);
5181            int realHeight= region.y;
5182            int virtualHeight= diff.getMaxDiffHeight();
5183            if (vpos <= viewPos + realHeight) { // OK, found!
5184
vpos-= viewPos; // make relative to this slot
5185
// now scale position within this slot to virtual slot
5186
if (realHeight <= 0)
5187                    vpos= 0;
5188                else
5189                    vpos= (vpos*virtualHeight)/realHeight;
5190                return virtualPos+vpos;
5191            }
5192            viewPos+= realHeight;
5193            virtualPos+= virtualHeight;
5194        }
5195        return virtualPos;
5196    }
5197        
5198    private void scrollVertical(int avpos, int lvpos, int rvpos, MergeSourceViewer allBut) {
5199                        
5200        int s= 0;
5201        
5202        if (fSynchronizedScrolling) {
5203            s= getVirtualHeight() - rvpos;
5204            int height= fRight.getViewportLines()/4;
5205            if (s < 0)
5206                s= 0;
5207            if (s > height)
5208                s= height;
5209        }
5210
5211        fInScrolling= true;
5212                
5213        if (isThreeWay() && allBut != fAncestor) {
5214            if (fSynchronizedScrolling || allBut == null) {
5215                int y= virtualToRealPosition(fAncestor, avpos+s)-s;
5216                fAncestor.vscroll(y);
5217            }
5218        }
5219
5220        if (allBut != fLeft) {
5221            if (fSynchronizedScrolling || allBut == null) {
5222                int y= virtualToRealPosition(fLeft, lvpos+s)-s;
5223                fLeft.vscroll(y);
5224            }
5225        }
5226
5227        if (allBut != fRight) {
5228            if (fSynchronizedScrolling || allBut == null) {
5229                int y= virtualToRealPosition(fRight, rvpos+s)-s;
5230                fRight.vscroll(y);
5231            }
5232        }
5233        
5234        fInScrolling= false;
5235        
5236        if (isThreeWay() && fAncestorCanvas != null)
5237            fAncestorCanvas.repaint();
5238        
5239        if (fLeftCanvas != null)
5240            fLeftCanvas.repaint();
5241        
5242        Control center= getCenterControl();
5243        if (center instanceof BufferedCanvas)
5244            ((BufferedCanvas)center).repaint();
5245        
5246        if (fRightCanvas != null)
5247            fRightCanvas.repaint();
5248    }
5249        
5250    /*
5251     * Updates Scrollbars with viewports.
5252     */

5253    private void syncViewport(MergeSourceViewer w) {
5254        
5255        if (fInScrolling)
5256            return;
5257
5258        int ix= w.getTopIndex();
5259        int ix2= w.getDocumentRegionOffset();
5260        
5261        int viewPosition= realToVirtualPosition(w, ix-ix2);
5262                
5263        scrollVertical(viewPosition, viewPosition, viewPosition, w); // scroll all but the given views
5264

5265        if (fVScrollBar != null) {
5266            int value= Math.max(0, Math.min(viewPosition, getVirtualHeight() - getViewportHeight()));
5267            fVScrollBar.setSelection(value);
5268            //refreshBirdEyeView();
5269
}
5270    }
5271
5272    /**
5273     */

5274    private void updateVScrollBar() {
5275        
5276        if (Utilities.okToUse(fVScrollBar) && fSynchronizedScrolling) {
5277            int virtualHeight= getVirtualHeight();
5278            int viewPortHeight= getViewportHeight();
5279            int pageIncrement= viewPortHeight-1;
5280            int thumb= (viewPortHeight > virtualHeight) ? virtualHeight : viewPortHeight;
5281                        
5282            fVScrollBar.setPageIncrement(pageIncrement);
5283            fVScrollBar.setMaximum(virtualHeight);
5284            fVScrollBar.setThumb(thumb);
5285        }
5286    }
5287    
5288    /*
5289     * maps given virtual position into a real view position of this view.
5290     */

5291    private int virtualToRealPosition(MergeSourceViewer part, int v) {
5292            
5293        if (! fSynchronizedScrolling || fAllDiffs == null)
5294            return v;
5295                    
5296        int virtualPos= 0;
5297        int viewPos= 0;
5298        Point region= new Point(0, 0);
5299        
5300        Iterator e= fAllDiffs.iterator();
5301        while (e.hasNext()) {
5302            Diff diff= (Diff) e.next();
5303            Position pos= diff.getPosition(part);
5304            int viewHeight= part.getLineRange(pos, region).y;
5305            int virtualHeight= diff.getMaxDiffHeight();
5306            if (v < (virtualPos + virtualHeight)) {
5307                v-= virtualPos; // make relative to this slot
5308
if (viewHeight <= 0) {
5309                    v= 0;
5310                } else {
5311                    v= (int) (v * ((double)viewHeight/virtualHeight));
5312                }
5313                return viewPos+v;
5314            }
5315            virtualPos+= virtualHeight;
5316            viewPos+= viewHeight;
5317        }
5318        return viewPos;
5319    }
5320    
5321    /* (non-Javadoc)
5322     * @see org.eclipse.compare.contentmergeviewer.ContentMergeViewer#flushContent(java.lang.Object, org.eclipse.core.runtime.IProgressMonitor)
5323     */

5324    protected void flushContent(Object JavaDoc oldInput, IProgressMonitor monitor) {
5325                
5326        // check and handle any shared buffers
5327
IMergeViewerContentProvider content= getMergeContentProvider();
5328        Object JavaDoc leftContent = content.getLeftContent(oldInput);
5329        Object JavaDoc rightContent = content.getRightContent(oldInput);
5330        
5331        if (leftContent != null && getCompareConfiguration().isLeftEditable() && isLeftDirty()) {
5332            if (fLeftContributor.hasSharedDocument(leftContent)) {
5333                if (flush(fLeftContributor))
5334                    setLeftDirty(false);
5335            }
5336        }
5337        
5338        if (rightContent != null && getCompareConfiguration().isRightEditable() && isRightDirty()) {
5339            if (fRightContributor.hasSharedDocument(rightContent)) {
5340                if (flush(fRightContributor))
5341                    setRightDirty(false);
5342            }
5343        }
5344        
5345        if (!(content instanceof MergeViewerContentProvider) || isLeftDirty() || isRightDirty()) {
5346            super.flushContent(oldInput, monitor);
5347        }
5348    }
5349    
5350    private boolean flush(final ContributorInfo info) {
5351        try {
5352            return info.flush();
5353        } catch (CoreException e) {
5354            handleException(e);
5355        }
5356        return false;
5357    }
5358
5359    private void handleException(Throwable JavaDoc throwable) {
5360        // TODO: Should show error to the user
5361
if (throwable instanceof InvocationTargetException JavaDoc) {
5362            InvocationTargetException JavaDoc ite = (InvocationTargetException JavaDoc) throwable;
5363            handleException(ite.getTargetException());
5364            return;
5365        }
5366        CompareUIPlugin.log(throwable);
5367    }
5368
5369    /* (non-Javadoc)
5370     * @see org.eclipse.core.runtime.IAdaptable#getAdapter(java.lang.Class)
5371     */

5372    public Object JavaDoc getAdapter(Class JavaDoc adapter) {
5373        if (adapter == IMergeViewerTestAdapter.class) {
5374            return new IMergeViewerTestAdapter() {
5375                public IDocument getDocument(char leg) {
5376                    switch (leg) {
5377                    case LEFT_CONTRIBUTOR:
5378                        return fLeft.getDocument();
5379                    case RIGHT_CONTRIBUTOR:
5380                        return fRight.getDocument();
5381                    case ANCESTOR_CONTRIBUTOR:
5382                        return fAncestor.getDocument();
5383                    }
5384                    return null;
5385                }
5386            };
5387        }
5388        if (adapter == OutlineViewerCreator.class) {
5389            if (fOutlineViewerCreator == null)
5390                fOutlineViewerCreator = new InternalOutlineViewerCreator();
5391            return fOutlineViewerCreator;
5392
5393        }
5394        if (adapter == IFindReplaceTarget.class)
5395            return getFindReplaceTarget();
5396        if (adapter == CompareHandlerService.class)
5397            return fHandlerService;
5398        return null;
5399    }
5400    
5401    /* (non-Javadoc)
5402     * @see org.eclipse.compare.contentmergeviewer.ContentMergeViewer#handleCompareInputChange()
5403     */

5404    protected void handleCompareInputChange() {
5405        try {
5406            beginRefresh();
5407            super.handleCompareInputChange();
5408        } finally {
5409            endRefresh();
5410        }
5411    }
5412
5413    private void beginRefresh() {
5414        isRefreshing = true;
5415        fLeftContributor.cacheSelection(fLeft);
5416        fRightContributor.cacheSelection(fRight);
5417        fAncestorContributor.cacheSelection(fAncestor);
5418        if (fSynchronizedScrolling) {
5419            fSynchronziedScrollPosition = fVScrollBar.getSelection();
5420        }
5421        
5422    }
5423    
5424    private void endRefresh() {
5425        isRefreshing = false;
5426        fLeftContributor.cacheSelection(null);
5427        fRightContributor.cacheSelection(null);
5428        fAncestorContributor.cacheSelection(null);
5429        fSynchronziedScrollPosition = -1;
5430    }
5431
5432    private void synchronizedScrollVertical(int vpos) {
5433        scrollVertical(vpos, vpos, vpos, null);
5434        workaround65205();
5435    }
5436    
5437    private boolean isIgnoreAncestor() {
5438        return Utilities.getBoolean(getCompareConfiguration(), ICompareUIConstants.PROP_IGNORE_ANCESTOR, false);
5439    }
5440    
5441    /* package */ void update(boolean includeControls) {
5442        if (getControl().isDisposed())
5443            return;
5444        if (fHasErrors) {
5445            resetDiffs();
5446        } else {
5447            doDiff();
5448        }
5449        
5450        if (includeControls)
5451            updateControls();
5452        
5453        updateVScrollBar();
5454        updatePresentation(null);
5455    }
5456
5457    private void resetDiffs() {
5458        // clear stuff
5459
fCurrentDiff= null;
5460        fChangeDiffs= null;
5461        fAllDiffs= null;
5462        resetPositions(fLeft.getDocument());
5463        resetPositions(fRight.getDocument());
5464        resetPositions(fAncestor.getDocument());
5465    }
5466
5467    private boolean isPatchHunk() {
5468        return Utilities.isHunk(getInput());
5469    }
5470    
5471    /**
5472     * Return the provided start position of the hunk in the target file.
5473     * @return the provided start position of the hunk in the target file
5474     */

5475    private int getHunkStart() {
5476        Object JavaDoc input = getInput();
5477        if (input != null && input instanceof DiffNode){
5478            ITypedElement right = ((DiffNode) input).getRight();
5479            if (right != null) {
5480                Object JavaDoc element = Utilities.getAdapter(right, IHunk.class);
5481                if (element instanceof IHunk)
5482                    return ((IHunk)element).getStartPosition();
5483            }
5484            ITypedElement left = ((DiffNode) input).getLeft();
5485            if (left != null) {
5486                Object JavaDoc element = Utilities.getAdapter(left, IHunk.class);
5487                if (element instanceof IHunk)
5488                    return ((IHunk)element).getStartPosition();
5489            }
5490        }
5491        return 0;
5492    }
5493    
5494    private IFindReplaceTarget getFindReplaceTarget() {
5495        if (fFindReplaceTarget == null)
5496            fFindReplaceTarget= new FindReplaceTarget();
5497        return fFindReplaceTarget;
5498    }
5499}
5500
Popular Tags