KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > eclipse > compare > structuremergeviewer > DiffTreeViewer


1 /*******************************************************************************
2  * Copyright (c) 2000, 2004 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  *******************************************************************************/

11 package org.eclipse.compare.structuremergeviewer;
12
13 import java.util.Iterator JavaDoc;
14 import java.util.ResourceBundle JavaDoc;
15
16 import org.eclipse.compare.*;
17 import org.eclipse.compare.internal.Utilities;
18 import org.eclipse.jface.action.*;
19 import org.eclipse.jface.util.IPropertyChangeListener;
20 import org.eclipse.jface.util.PropertyChangeEvent;
21 import org.eclipse.jface.viewers.*;
22 import org.eclipse.swt.SWT;
23 import org.eclipse.swt.events.DisposeEvent;
24 import org.eclipse.swt.graphics.Image;
25 import org.eclipse.swt.widgets.*;
26
27 /**
28  * A tree viewer that works on objects implementing
29  * the <code>IDiffContainer</code> and <code>IDiffElement</code> interfaces.
30  * <p>
31  * This class may be instantiated; it is not intended to be subclassed outside
32  * this package.
33  * </p>
34  *
35  * @see IDiffContainer
36  * @see IDiffElement
37  */

38 public class DiffTreeViewer extends TreeViewer {
39     
40     static class DiffViewerComparator extends ViewerComparator {
41     
42         public boolean isSorterProperty(Object JavaDoc element, Object JavaDoc property) {
43             return false;
44         }
45     
46         public int category(Object JavaDoc node) {
47             if (node instanceof DiffNode) {
48                 Object JavaDoc o= ((DiffNode) node).getId();
49                 if (o instanceof DocumentRangeNode)
50                     return ((DocumentRangeNode) o).getTypeCode();
51             }
52             return 0;
53         }
54     }
55
56     class DiffViewerContentProvider implements ITreeContentProvider {
57             
58         public void inputChanged(Viewer viewer, Object JavaDoc oldInput, Object JavaDoc newInput) {
59             // empty implementation
60
}
61     
62         public boolean isDeleted(Object JavaDoc element) {
63             return false;
64         }
65             
66         public void dispose() {
67             inputChanged(DiffTreeViewer.this, getInput(), null);
68         }
69             
70         public Object JavaDoc getParent(Object JavaDoc element) {
71             if (element instanceof IDiffElement)
72                 return ((IDiffElement)element).getParent();
73             return null;
74         }
75         
76         public final boolean hasChildren(Object JavaDoc element) {
77             if (element instanceof IDiffContainer)
78                 return ((IDiffContainer)element).hasChildren();
79             return false;
80         }
81         
82         public final Object JavaDoc[] getChildren(Object JavaDoc element) {
83             if (element instanceof IDiffContainer)
84                 return ((IDiffContainer)element).getChildren();
85             return new Object JavaDoc[0];
86         }
87         
88         public Object JavaDoc[] getElements(Object JavaDoc element) {
89             return getChildren(element);
90         }
91     }
92     
93     /*
94      * Takes care of swapping left and right if fLeftIsLocal
95      * is true.
96      */

97     class DiffViewerLabelProvider extends LabelProvider {
98         
99         public String JavaDoc getText(Object JavaDoc element) {
100             
101             if (element instanceof IDiffElement)
102                 return ((IDiffElement)element).getName();
103                         
104             return Utilities.getString(fBundle, "defaultLabel"); //$NON-NLS-1$
105
}
106     
107         public Image getImage(Object JavaDoc element) {
108             if (element instanceof IDiffElement) {
109                 IDiffElement input= (IDiffElement) element;
110                 
111                 int kind= input.getKind();
112                 if (fLeftIsLocal) {
113                     switch (kind & Differencer.DIRECTION_MASK) {
114                     case Differencer.LEFT:
115                         kind= (kind &~ Differencer.LEFT) | Differencer.RIGHT;
116                         break;
117                     case Differencer.RIGHT:
118                         kind= (kind &~ Differencer.RIGHT) | Differencer.LEFT;
119                         break;
120                     }
121                 }
122                 
123                 return fCompareConfiguration.getImage(input.getImage(), kind);
124             }
125             return null;
126         }
127     }
128
129     static class FilterSame extends ViewerFilter {
130         public boolean select(Viewer viewer, Object JavaDoc parentElement, Object JavaDoc element) {
131             if (element instanceof IDiffElement)
132                 return (((IDiffElement)element).getKind() & Differencer.PSEUDO_CONFLICT) == 0;
133             return true;
134         }
135         public boolean isFilterProperty(Object JavaDoc element, Object JavaDoc property) {
136             return false;
137         }
138     }
139     
140     private ResourceBundle JavaDoc fBundle;
141     private CompareConfiguration fCompareConfiguration;
142     /* package */ boolean fLeftIsLocal;
143     private IPropertyChangeListener fPropertyChangeListener;
144
145     private Action fCopyLeftToRightAction;
146     private Action fCopyRightToLeftAction;
147     private Action fEmptyMenuAction;
148     private Action fExpandAllAction;
149         
150     /**
151      * Creates a new viewer for the given SWT tree control with the specified configuration.
152      *
153      * @param tree the tree control
154      * @param configuration the configuration for this viewer
155      */

156     public DiffTreeViewer(Tree tree, CompareConfiguration configuration) {
157         super(tree);
158         initialize(configuration == null ? new CompareConfiguration() : configuration);
159     }
160     
161     /**
162      * Creates a new viewer under the given SWT parent and with the specified configuration.
163      *
164      * @param parent the SWT control under which to create the viewer
165      * @param configuration the configuration for this viewer
166      */

167     public DiffTreeViewer(Composite parent, CompareConfiguration configuration) {
168         super(new Tree(parent, SWT.MULTI));
169         initialize(configuration == null ? new CompareConfiguration() : configuration);
170     }
171     
172     private void initialize(CompareConfiguration configuration) {
173         
174         Control tree= getControl();
175         
176         INavigatable nav= new INavigatable() {
177             public boolean selectChange(int flag) {
178                 if (flag == INavigatable.FIRST_CHANGE) {
179                     setSelection(StructuredSelection.EMPTY);
180                     flag = INavigatable.NEXT_CHANGE;
181                 } else if (flag == INavigatable.LAST_CHANGE) {
182                     setSelection(StructuredSelection.EMPTY);
183                     flag = INavigatable.PREVIOUS_CHANGE;
184                 }
185                 // Fix for http://dev.eclipse.org/bugs/show_bug.cgi?id=20106
186
return internalNavigate(flag == INavigatable.NEXT_CHANGE, true);
187             }
188             public Object JavaDoc getInput() {
189                 return DiffTreeViewer.this.getInput();
190             }
191             public boolean openSelectedChange() {
192                 internalOpen();
193                 return true;
194             }
195             public boolean hasChange(int changeFlag) {
196                 return getNextItem(changeFlag == INavigatable.NEXT_CHANGE) != null;
197             }
198         };
199         tree.setData(INavigatable.NAVIGATOR_PROPERTY, nav);
200         
201         fLeftIsLocal= Utilities.getBoolean(configuration, "LEFT_IS_LOCAL", false); //$NON-NLS-1$
202

203         tree.setData(CompareUI.COMPARE_VIEWER_TITLE, getTitle());
204
205         Composite parent= tree.getParent();
206         
207         fBundle= ResourceBundle.getBundle("org.eclipse.compare.structuremergeviewer.DiffTreeViewerResources"); //$NON-NLS-1$
208

209         // register for notification with the CompareConfiguration
210
fCompareConfiguration= configuration;
211         if (fCompareConfiguration != null) {
212             fPropertyChangeListener= new IPropertyChangeListener() {
213                 public void propertyChange(PropertyChangeEvent event) {
214                     DiffTreeViewer.this.propertyChange(event);
215                 }
216             };
217             fCompareConfiguration.addPropertyChangeListener(fPropertyChangeListener);
218         }
219     
220         setContentProvider(new DiffViewerContentProvider());
221         setLabelProvider(new DiffViewerLabelProvider());
222         
223         addSelectionChangedListener(
224             new ISelectionChangedListener() {
225                 public void selectionChanged(SelectionChangedEvent se) {
226                     updateActions();
227                 }
228             }
229         );
230                                         
231         setComparator(new DiffViewerComparator());
232         
233         ToolBarManager tbm= CompareViewerPane.getToolBarManager(parent);
234         if (tbm != null) {
235             tbm.removeAll();
236             
237             tbm.add(new Separator("merge")); //$NON-NLS-1$
238
tbm.add(new Separator("modes")); //$NON-NLS-1$
239
tbm.add(new Separator("navigation")); //$NON-NLS-1$
240

241             createToolItems(tbm);
242             updateActions();
243             
244             tbm.update(true);
245         }
246         
247         MenuManager mm= new MenuManager();
248         mm.setRemoveAllWhenShown(true);
249         mm.addMenuListener(
250             new IMenuListener() {
251                 public void menuAboutToShow(IMenuManager mm2) {
252                     fillContextMenu(mm2);
253                     if (mm2.isEmpty()) {
254                         if (fEmptyMenuAction == null) {
255                             fEmptyMenuAction= new Action(Utilities.getString(fBundle, "emptyMenuItem")) { //$NON-NLS-1$
256
// left empty
257
};
258                             fEmptyMenuAction.setEnabled(false);
259                         }
260                         mm2.add(fEmptyMenuAction);
261                     }
262                 }
263             }
264         );
265         tree.setMenu(mm.createContextMenu(tree));
266     }
267             
268     /**
269      * Returns the viewer's name.
270      *
271      * @return the viewer's name
272      */

273     public String JavaDoc getTitle() {
274         String JavaDoc title= Utilities.getString(fBundle, "title", null); //$NON-NLS-1$
275
if (title == null)
276             title= Utilities.getString("DiffTreeViewer.title"); //$NON-NLS-1$
277
return title;
278     }
279     
280     /**
281      * Returns the resource bundle.
282      *
283      * @return the viewer's resource bundle
284      */

285     protected ResourceBundle JavaDoc getBundle() {
286         return fBundle;
287     }
288
289     /**
290      * Returns the compare configuration of this viewer.
291      *
292      * @return the compare configuration of this viewer
293      */

294     public CompareConfiguration getCompareConfiguration() {
295         return fCompareConfiguration;
296     }
297             
298     /**
299      * Called on the viewer disposal.
300      * Unregisters from the compare configuration.
301      * Clients may extend if they have to do additional cleanup.
302      * @param event dispose event that triggered call to this method
303      */

304     protected void handleDispose(DisposeEvent event) {
305         
306         if (fCompareConfiguration != null) {
307             if (fPropertyChangeListener != null)
308                 fCompareConfiguration.removePropertyChangeListener(fPropertyChangeListener);
309             fCompareConfiguration= null;
310         }
311         fPropertyChangeListener= null;
312         
313         super.handleDispose(event);
314     }
315     
316     /**
317      * Tracks property changes of the configuration object.
318      * Clients may extend to track their own property changes.
319      * @param event property change event that triggered call to this method
320      */

321     protected void propertyChange(PropertyChangeEvent event) {
322         // empty default implementation
323
}
324     
325     protected void inputChanged(Object JavaDoc in, Object JavaDoc oldInput) {
326         super.inputChanged(in, oldInput);
327         
328         if (in != oldInput) {
329             initialSelection();
330             updateActions();
331         }
332     }
333     
334     /**
335      * This hook method is called from within <code>inputChanged</code>
336      * after a new input has been set but before any controls are updated.
337      * This default implementation calls <code>navigate(true)</code>
338      * to select and expand the first leaf node.
339      * Clients can override this method and are free to decide whether
340      * they want to call the inherited method.
341      *
342      * @since 2.0
343      */

344     protected void initialSelection() {
345         navigate(true);
346     }
347
348     /**
349      * Overridden to avoid expanding <code>DiffNode</code>s that shouldn't expand.
350      * @param node the node to expand
351      * @param level non-negative level, or <code>ALL_LEVELS</code> to collapse all levels of the tree
352      */

353     protected void internalExpandToLevel(Widget node, int level) {
354                 
355         Object JavaDoc data= node.getData();
356         
357         if (dontExpand(data))
358             return;
359         
360         super.internalExpandToLevel(node, level);
361     }
362     
363     /**
364      * This hook method is called from within <code>internalExpandToLevel</code>
365      * to control whether a given model node should be expanded or not.
366      * This default implementation checks whether the object is a <code>DiffNode</code> and
367      * calls <code>dontExpand()</code> on it.
368      * Clients can override this method and are free to decide whether
369      * they want to call the inherited method.
370      *
371      * @param o the model object to be expanded
372      * @return <code>false</code> if a node should be expanded, <code>true</code> to prevent expanding
373      * @since 2.0
374      */

375     protected boolean dontExpand(Object JavaDoc o) {
376         return o instanceof DiffNode && ((DiffNode)o).dontExpand();
377     }
378     
379     //---- merge action support
380

381     /**
382      * This factory method is called after the viewer's controls have been created.
383      * It installs four actions in the given <code>ToolBarManager</code>. Two actions
384      * allow for copying one side of a <code>DiffNode</code> to the other side.
385      * Two other actions are for navigating from one node to the next (previous).
386      * <p>
387      * Clients can override this method and are free to decide whether they want to call
388      * the inherited method.
389      *
390      * @param toolbarManager the toolbar manager for which to add the actions
391      */

392     protected void createToolItems(ToolBarManager toolbarManager) {
393         
394 // fCopyLeftToRightAction= new Action() {
395
// public void run() {
396
// copySelected(true);
397
// }
398
// };
399
// Utilities.initAction(fCopyLeftToRightAction, fBundle, "action.TakeLeft.");
400
// toolbarManager.appendToGroup("merge", fCopyLeftToRightAction);
401

402 // fCopyRightToLeftAction= new Action() {
403
// public void run() {
404
// copySelected(false);
405
// }
406
// };
407
// Utilities.initAction(fCopyRightToLeftAction, fBundle, "action.TakeRight.");
408
// toolbarManager.appendToGroup("merge", fCopyRightToLeftAction);
409

410 // fNextAction= new Action() {
411
// public void run() {
412
// navigate(true);
413
// }
414
// };
415
// Utilities.initAction(fNextAction, fBundle, "action.NextDiff."); //$NON-NLS-1$
416
// toolbarManager.appendToGroup("navigation", fNextAction); //$NON-NLS-1$
417

418 // fPreviousAction= new Action() {
419
// public void run() {
420
// navigate(false);
421
// }
422
// };
423
// Utilities.initAction(fPreviousAction, fBundle, "action.PrevDiff."); //$NON-NLS-1$
424
// toolbarManager.appendToGroup("navigation", fPreviousAction); //$NON-NLS-1$
425
}
426     
427     /**
428      * This method is called to add actions to the viewer's context menu.
429      * It installs actions for expanding tree nodes, copying one side of a <code>DiffNode</code> to the other side.
430      * Clients can override this method and are free to decide whether they want to call
431      * the inherited method.
432      *
433      * @param manager the menu manager for which to add the actions
434      */

435     protected void fillContextMenu(IMenuManager manager) {
436         if (fExpandAllAction == null) {
437             fExpandAllAction= new Action() {
438                 public void run() {
439                     expandSelection();
440                 }
441             };
442             Utilities.initAction(fExpandAllAction, fBundle, "action.ExpandAll."); //$NON-NLS-1$
443
}
444         
445         boolean enable= false;
446         ISelection selection= getSelection();
447         if (selection instanceof IStructuredSelection) {
448             Iterator JavaDoc elements= ((IStructuredSelection)selection).iterator();
449             while (elements.hasNext()) {
450                 Object JavaDoc element= elements.next();
451                 if (element instanceof IDiffContainer) {
452                     if (((IDiffContainer)element).hasChildren()) {
453                         enable= true;
454                         break;
455                     }
456                 }
457             }
458         }
459         fExpandAllAction.setEnabled(enable);
460
461         manager.add(fExpandAllAction);
462         
463         if (fCopyLeftToRightAction != null)
464             manager.add(fCopyLeftToRightAction);
465         if (fCopyRightToLeftAction != null)
466             manager.add(fCopyRightToLeftAction);
467     }
468
469     /**
470      * Expands to infinity all items in the selection.
471      *
472      * @since 2.0
473      */

474     protected void expandSelection() {
475         ISelection selection= getSelection();
476         if (selection instanceof IStructuredSelection) {
477             Iterator JavaDoc elements= ((IStructuredSelection)selection).iterator();
478             while (elements.hasNext()) {
479                 Object JavaDoc next= elements.next();
480                 expandToLevel(next, ALL_LEVELS);
481             }
482         }
483     }
484
485     /**
486      * Copies one side of all <code>DiffNode</code>s in the current selection to the other side.
487      * Called from the (internal) actions for copying the sides of a <code>DiffNode</code>.
488      * Clients may override.
489      *
490      * @param leftToRight if <code>true</code> the left side is copied to the right side.
491      * If <code>false</code> the right side is copied to the left side
492      */

493     protected void copySelected(boolean leftToRight) {
494         ISelection selection= getSelection();
495         if (selection instanceof IStructuredSelection) {
496             Iterator JavaDoc e= ((IStructuredSelection) selection).iterator();
497             while (e.hasNext()) {
498                 Object JavaDoc element= e.next();
499                 if (element instanceof ICompareInput)
500                     copyOne((ICompareInput) element, leftToRight);
501             }
502         }
503     }
504     
505     /**
506      * Called to copy one side of the given node to the other.
507      * This default implementation delegates the call to <code>ICompareInput.copy(...)</code>.
508      * Clients may override.
509      * @param node the node to copy
510      * @param leftToRight if <code>true</code> the left side is copied to the right side.
511      * If <code>false</code> the right side is copied to the left side
512      */

513     protected void copyOne(ICompareInput node, boolean leftToRight) {
514         
515         node.copy(leftToRight);
516         
517         // update node's image
518
update(new Object JavaDoc[] { node }, null);
519     }
520     
521     /**
522      * Selects the next (or previous) node of the current selection.
523      * If there is no current selection the first (last) node in the tree is selected.
524      * Wraps around at end or beginning.
525      * Clients may override.
526      *
527      * @param next if <code>true</code> the next node is selected, otherwise the previous node
528      */

529     protected void navigate(boolean next) {
530         // Fix for http://dev.eclipse.org/bugs/show_bug.cgi?id=20106
531
internalNavigate(next, false);
532     }
533     
534     //---- private
535

536     /**
537      * Selects the next (or previous) node of the current selection.
538      * If there is no current selection the first (last) node in the tree is selected.
539      * Wraps around at end or beginning.
540      * Clients may override.
541      *
542      * @param next if <code>true</code> the next node is selected, otherwise the previous node
543      * @param fireOpen if <code>true</code> an open event is fired.
544      * @return <code>true</code> if at end (or beginning)
545      */

546     private boolean internalNavigate(boolean next, boolean fireOpen) {
547         Control c= getControl();
548         if (!(c instanceof Tree) || c.isDisposed())
549             return false;
550         TreeItem item = getNextItem(next);
551         if (item != null) {
552             internalSetSelection(item, fireOpen);
553         }
554         return item == null;
555     }
556     
557     private TreeItem getNextItem(boolean next) {
558         Control c= getControl();
559         if (!(c instanceof Tree) || c.isDisposed())
560             return null;
561             
562         Tree tree= (Tree) c;
563         TreeItem item= null;
564         TreeItem children[]= tree.getSelection();
565         if (children != null && children.length > 0)
566             item= children[0];
567         if (item == null) {
568             children= tree.getItems();
569             if (children != null && children.length > 0) {
570                 item= children[0];
571                 if (item != null && item.getItemCount() <= 0) {
572                     return item;
573                 }
574             }
575         }
576             
577         while (true) {
578             item= findNextPrev(item, next);
579             if (item == null)
580                 break;
581             if (item.getItemCount() <= 0)
582                 break;
583         }
584         return item;
585     }
586
587     private TreeItem findNextPrev(TreeItem item, boolean next) {
588         
589         if (item == null)
590             return null;
591         
592         TreeItem children[]= null;
593
594         if (!next) {
595         
596             TreeItem parent= item.getParentItem();
597             if (parent != null)
598                 children= parent.getItems();
599             else
600                 children= item.getParent().getItems();
601             
602             if (children != null && children.length > 0) {
603                 // goto previous child
604
int index= 0;
605                 for (; index < children.length; index++)
606                     if (children[index] == item)
607                         break;
608                 
609                 if (index > 0) {
610                     
611                     item= children[index-1];
612                     
613                     while (true) {
614                         createChildren(item);
615                         int n= item.getItemCount();
616                         if (n <= 0)
617                             break;
618                             
619                         item.setExpanded(true);
620                         item= item.getItems()[n-1];
621                     }
622
623                     // previous
624
return item;
625                 }
626             }
627             
628             // go up
629
item= parent;
630                     
631         } else {
632             item.setExpanded(true);
633             createChildren(item);
634             
635             if (item.getItemCount() > 0) {
636                 // has children: go down
637
children= item.getItems();
638                 return children[0];
639             }
640             
641             while (item != null) {
642                 children= null;
643                 TreeItem parent= item.getParentItem();
644                 if (parent != null)
645                     children= parent.getItems();
646                 else
647                     children= item.getParent().getItems();
648                 
649                 if (children != null && children.length > 0) {
650                     // goto next child
651
int index= 0;
652                     for (; index < children.length; index++)
653                         if (children[index] == item)
654                             break;
655                     
656                     if (index < children.length-1) {
657                         // next
658
return children[index+1];
659                     }
660                 }
661                 
662                 // go up
663
item= parent;
664             }
665         }
666                 
667         return item;
668     }
669     
670     private void internalSetSelection(TreeItem ti, boolean fireOpen) {
671         if (ti != null) {
672             Object JavaDoc data= ti.getData();
673             if (data != null) {
674                 // Fix for http://dev.eclipse.org/bugs/show_bug.cgi?id=20106
675
ISelection selection= new StructuredSelection(data);
676                 setSelection(selection, true);
677                 ISelection currentSelection= getSelection();
678                 if (fireOpen && currentSelection != null && selection.equals(currentSelection)) {
679                     fireOpen(new OpenEvent(this, selection));
680                 }
681             }
682         }
683     }
684             
685     private final boolean isEditable(Object JavaDoc element, boolean left) {
686         if (element instanceof ICompareInput) {
687             ICompareInput diff= (ICompareInput) element;
688             Object JavaDoc side= left ? diff.getLeft() : diff.getRight();
689             if (side == null && diff instanceof IDiffElement) {
690                 IDiffContainer container= ((IDiffElement)diff).getParent();
691                 if (container instanceof ICompareInput) {
692                     ICompareInput parent= (ICompareInput) container;
693                     side= left ? parent.getLeft() : parent.getRight();
694                 }
695             }
696             if (side instanceof IEditableContent)
697                 return ((IEditableContent) side).isEditable();
698         }
699         return false;
700     }
701         
702     private void updateActions() {
703         int leftToRight= 0;
704         int rightToLeft= 0;
705         ISelection selection= getSelection();
706         if (selection instanceof IStructuredSelection) {
707             IStructuredSelection ss= (IStructuredSelection) selection;
708             Iterator JavaDoc e= ss.iterator();
709             while (e.hasNext()) {
710                 Object JavaDoc element= e.next();
711                 if (element instanceof ICompareInput) {
712                     if (isEditable(element, false))
713                         leftToRight++;
714                     if (isEditable(element, true))
715                         rightToLeft++;
716                     if (leftToRight > 0 && rightToLeft > 0)
717                         break;
718                 }
719             }
720             if (fExpandAllAction != null)
721                 fExpandAllAction.setEnabled(selection.isEmpty());
722         }
723         if (fCopyLeftToRightAction != null)
724             fCopyLeftToRightAction.setEnabled(leftToRight > 0);
725         if (fCopyRightToLeftAction != null)
726             fCopyRightToLeftAction.setEnabled(rightToLeft > 0);
727     }
728     
729     /*
730      * Fix for http://dev.eclipse.org/bugs/show_bug.cgi?id=20106
731      */

732     private void internalOpen() {
733         ISelection selection= getSelection();
734         if (selection != null && !selection.isEmpty()) {
735             fireOpen(new OpenEvent(this, selection));
736         }
737     }
738 }
739
740
Popular Tags