Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
Diffstat (limited to 'bundles/org.eclipse.compare/compare/org/eclipse/compare/structuremergeviewer/DiffTreeViewer.java')
-rw-r--r--bundles/org.eclipse.compare/compare/org/eclipse/compare/structuremergeviewer/DiffTreeViewer.java728
1 files changed, 728 insertions, 0 deletions
diff --git a/bundles/org.eclipse.compare/compare/org/eclipse/compare/structuremergeviewer/DiffTreeViewer.java b/bundles/org.eclipse.compare/compare/org/eclipse/compare/structuremergeviewer/DiffTreeViewer.java
new file mode 100644
index 000000000..44d9c907e
--- /dev/null
+++ b/bundles/org.eclipse.compare/compare/org/eclipse/compare/structuremergeviewer/DiffTreeViewer.java
@@ -0,0 +1,728 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2009 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.compare.structuremergeviewer;
+
+import java.util.Iterator;
+import java.util.ResourceBundle;
+
+import org.eclipse.compare.*;
+import org.eclipse.compare.internal.Utilities;
+import org.eclipse.compare.internal.patch.DiffViewerComparator;
+import org.eclipse.jface.action.*;
+import org.eclipse.jface.util.IPropertyChangeListener;
+import org.eclipse.jface.util.PropertyChangeEvent;
+import org.eclipse.jface.viewers.*;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.DisposeEvent;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.widgets.*;
+
+/**
+ * A tree viewer that works on objects implementing
+ * the <code>IDiffContainer</code> and <code>IDiffElement</code> interfaces.
+ * <p>
+ * This class may be instantiated; it is not intended to be subclassed outside
+ * this package.
+ * </p>
+ *
+ * @see IDiffContainer
+ * @see IDiffElement
+ * @noextend This class is not intended to be subclassed by clients.
+ */
+public class DiffTreeViewer extends TreeViewer {
+
+ class DiffViewerContentProvider implements ITreeContentProvider {
+
+ public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
+ // empty implementation
+ }
+
+ public boolean isDeleted(Object element) {
+ return false;
+ }
+
+ public void dispose() {
+ inputChanged(DiffTreeViewer.this, getInput(), null);
+ }
+
+ public Object getParent(Object element) {
+ if (element instanceof IDiffElement)
+ return ((IDiffElement)element).getParent();
+ return null;
+ }
+
+ public final boolean hasChildren(Object element) {
+ if (element instanceof IDiffContainer)
+ return ((IDiffContainer)element).hasChildren();
+ return false;
+ }
+
+ public final Object[] getChildren(Object element) {
+ if (element instanceof IDiffContainer)
+ return ((IDiffContainer)element).getChildren();
+ return new Object[0];
+ }
+
+ public Object[] getElements(Object element) {
+ return getChildren(element);
+ }
+ }
+
+ /*
+ * Takes care of swapping left and right if fLeftIsLocal
+ * is true.
+ */
+ class DiffViewerLabelProvider extends LabelProvider {
+
+ public String getText(Object element) {
+
+ if (element instanceof IDiffElement)
+ return ((IDiffElement)element).getName();
+
+ return Utilities.getString(fBundle, "defaultLabel"); //$NON-NLS-1$
+ }
+
+ public Image getImage(Object element) {
+ if (element instanceof IDiffElement) {
+ IDiffElement input= (IDiffElement) element;
+
+ int kind= input.getKind();
+ if (fLeftIsLocal) {
+ switch (kind & Differencer.DIRECTION_MASK) {
+ case Differencer.LEFT:
+ kind= (kind &~ Differencer.LEFT) | Differencer.RIGHT;
+ break;
+ case Differencer.RIGHT:
+ kind= (kind &~ Differencer.RIGHT) | Differencer.LEFT;
+ break;
+ }
+ }
+
+ return fCompareConfiguration.getImage(input.getImage(), kind);
+ }
+ return null;
+ }
+ }
+
+ static class FilterSame extends ViewerFilter {
+ public boolean select(Viewer viewer, Object parentElement, Object element) {
+ if (element instanceof IDiffElement)
+ return (((IDiffElement)element).getKind() & Differencer.PSEUDO_CONFLICT) == 0;
+ return true;
+ }
+ public boolean isFilterProperty(Object element, Object property) {
+ return false;
+ }
+ }
+
+ private ResourceBundle fBundle;
+ private CompareConfiguration fCompareConfiguration;
+ /* package */ boolean fLeftIsLocal;
+ private IPropertyChangeListener fPropertyChangeListener;
+
+ private Action fCopyLeftToRightAction;
+ private Action fCopyRightToLeftAction;
+ private Action fEmptyMenuAction;
+ private Action fExpandAllAction;
+
+ /**
+ * Creates a new viewer for the given SWT tree control with the specified configuration.
+ *
+ * @param tree the tree control
+ * @param configuration the configuration for this viewer
+ */
+ public DiffTreeViewer(Tree tree, CompareConfiguration configuration) {
+ super(tree);
+ initialize(configuration == null ? new CompareConfiguration() : configuration);
+ }
+
+ /**
+ * Creates a new viewer under the given SWT parent and with the specified configuration.
+ *
+ * @param parent the SWT control under which to create the viewer
+ * @param configuration the configuration for this viewer
+ */
+ public DiffTreeViewer(Composite parent, CompareConfiguration configuration) {
+ super(new Tree(parent, SWT.MULTI));
+ initialize(configuration == null ? new CompareConfiguration() : configuration);
+ }
+
+ private void initialize(CompareConfiguration configuration) {
+
+ Control tree= getControl();
+
+ INavigatable nav= new INavigatable() {
+ public boolean selectChange(int flag) {
+ if (flag == INavigatable.FIRST_CHANGE) {
+ setSelection(StructuredSelection.EMPTY);
+ flag = INavigatable.NEXT_CHANGE;
+ } else if (flag == INavigatable.LAST_CHANGE) {
+ setSelection(StructuredSelection.EMPTY);
+ flag = INavigatable.PREVIOUS_CHANGE;
+ }
+ // Fix for http://dev.eclipse.org/bugs/show_bug.cgi?id=20106
+ return internalNavigate(flag == INavigatable.NEXT_CHANGE, true);
+ }
+ public Object getInput() {
+ return DiffTreeViewer.this.getInput();
+ }
+ public boolean openSelectedChange() {
+ return internalOpen();
+ }
+ public boolean hasChange(int changeFlag) {
+ return getNextItem(changeFlag == INavigatable.NEXT_CHANGE, false) != null;
+ }
+ };
+ tree.setData(INavigatable.NAVIGATOR_PROPERTY, nav);
+
+ fLeftIsLocal= Utilities.getBoolean(configuration, "LEFT_IS_LOCAL", false); //$NON-NLS-1$
+
+ tree.setData(CompareUI.COMPARE_VIEWER_TITLE, getTitle());
+
+ Composite parent= tree.getParent();
+
+ fBundle= ResourceBundle.getBundle("org.eclipse.compare.structuremergeviewer.DiffTreeViewerResources"); //$NON-NLS-1$
+
+ // register for notification with the CompareConfiguration
+ fCompareConfiguration= configuration;
+ if (fCompareConfiguration != null) {
+ fPropertyChangeListener= new IPropertyChangeListener() {
+ public void propertyChange(PropertyChangeEvent event) {
+ DiffTreeViewer.this.propertyChange(event);
+ }
+ };
+ fCompareConfiguration.addPropertyChangeListener(fPropertyChangeListener);
+ }
+
+ setContentProvider(new DiffViewerContentProvider());
+ setLabelProvider(new DiffViewerLabelProvider());
+
+ addSelectionChangedListener(
+ new ISelectionChangedListener() {
+ public void selectionChanged(SelectionChangedEvent se) {
+ updateActions();
+ }
+ }
+ );
+
+ setComparator(new DiffViewerComparator());
+
+ ToolBarManager tbm= CompareViewerPane.getToolBarManager(parent);
+ if (tbm != null) {
+ tbm.removeAll();
+
+ tbm.add(new Separator("merge")); //$NON-NLS-1$
+ tbm.add(new Separator("modes")); //$NON-NLS-1$
+ tbm.add(new Separator("navigation")); //$NON-NLS-1$
+
+ createToolItems(tbm);
+ updateActions();
+
+ tbm.update(true);
+ }
+
+ MenuManager mm= new MenuManager();
+ mm.setRemoveAllWhenShown(true);
+ mm.addMenuListener(
+ new IMenuListener() {
+ public void menuAboutToShow(IMenuManager mm2) {
+ fillContextMenu(mm2);
+ if (mm2.isEmpty()) {
+ if (fEmptyMenuAction == null) {
+ fEmptyMenuAction= new Action(Utilities.getString(fBundle, "emptyMenuItem")) { //$NON-NLS-1$
+ // left empty
+ };
+ fEmptyMenuAction.setEnabled(false);
+ }
+ mm2.add(fEmptyMenuAction);
+ }
+ }
+ }
+ );
+ tree.setMenu(mm.createContextMenu(tree));
+ }
+
+ /**
+ * Returns the viewer's name.
+ *
+ * @return the viewer's name
+ */
+ public String getTitle() {
+ String title= Utilities.getString(fBundle, "title", null); //$NON-NLS-1$
+ if (title == null)
+ title= Utilities.getString("DiffTreeViewer.title"); //$NON-NLS-1$
+ return title;
+ }
+
+ /**
+ * Returns the resource bundle.
+ *
+ * @return the viewer's resource bundle
+ */
+ protected ResourceBundle getBundle() {
+ return fBundle;
+ }
+
+ /**
+ * Returns the compare configuration of this viewer.
+ *
+ * @return the compare configuration of this viewer
+ */
+ public CompareConfiguration getCompareConfiguration() {
+ return fCompareConfiguration;
+ }
+
+ /**
+ * Called on the viewer disposal.
+ * Unregisters from the compare configuration.
+ * Clients may extend if they have to do additional cleanup.
+ * @param event dispose event that triggered call to this method
+ */
+ protected void handleDispose(DisposeEvent event) {
+
+ if (fCompareConfiguration != null) {
+ if (fPropertyChangeListener != null)
+ fCompareConfiguration.removePropertyChangeListener(fPropertyChangeListener);
+ fCompareConfiguration= null;
+ }
+ fPropertyChangeListener= null;
+
+ super.handleDispose(event);
+ }
+
+ /**
+ * Tracks property changes of the configuration object.
+ * Clients may extend to track their own property changes.
+ * @param event property change event that triggered call to this method
+ */
+ protected void propertyChange(PropertyChangeEvent event) {
+ // empty default implementation
+ }
+
+ protected void inputChanged(Object in, Object oldInput) {
+ super.inputChanged(in, oldInput);
+
+ if (in != oldInput) {
+ initialSelection();
+ updateActions();
+ }
+ }
+
+ /**
+ * This hook method is called from within <code>inputChanged</code>
+ * after a new input has been set but before any controls are updated.
+ * This default implementation calls <code>navigate(true)</code>
+ * to select and expand the first leaf node.
+ * Clients can override this method and are free to decide whether
+ * they want to call the inherited method.
+ *
+ * @since 2.0
+ */
+ protected void initialSelection() {
+ navigate(true);
+ }
+
+ /**
+ * Overridden to avoid expanding <code>DiffNode</code>s that shouldn't expand.
+ * @param node the node to expand
+ * @param level non-negative level, or <code>ALL_LEVELS</code> to collapse all levels of the tree
+ */
+ protected void internalExpandToLevel(Widget node, int level) {
+
+ Object data= node.getData();
+
+ if (dontExpand(data))
+ return;
+
+ super.internalExpandToLevel(node, level);
+ }
+
+ /**
+ * This hook method is called from within <code>internalExpandToLevel</code>
+ * to control whether a given model node should be expanded or not.
+ * This default implementation checks whether the object is a <code>DiffNode</code> and
+ * calls <code>dontExpand()</code> on it.
+ * Clients can override this method and are free to decide whether
+ * they want to call the inherited method.
+ *
+ * @param o the model object to be expanded
+ * @return <code>false</code> if a node should be expanded, <code>true</code> to prevent expanding
+ * @since 2.0
+ */
+ protected boolean dontExpand(Object o) {
+ return o instanceof DiffNode && ((DiffNode)o).dontExpand();
+ }
+
+ //---- merge action support
+
+ /**
+ * This factory method is called after the viewer's controls have been created.
+ * It installs four actions in the given <code>ToolBarManager</code>. Two actions
+ * allow for copying one side of a <code>DiffNode</code> to the other side.
+ * Two other actions are for navigating from one node to the next (previous).
+ * <p>
+ * Clients can override this method and are free to decide whether they want to call
+ * the inherited method.
+ *
+ * @param toolbarManager the toolbar manager for which to add the actions
+ */
+ protected void createToolItems(ToolBarManager toolbarManager) {
+
+// fCopyLeftToRightAction= new Action() {
+// public void run() {
+// copySelected(true);
+// }
+// };
+// Utilities.initAction(fCopyLeftToRightAction, fBundle, "action.TakeLeft.");
+// toolbarManager.appendToGroup("merge", fCopyLeftToRightAction);
+
+// fCopyRightToLeftAction= new Action() {
+// public void run() {
+// copySelected(false);
+// }
+// };
+// Utilities.initAction(fCopyRightToLeftAction, fBundle, "action.TakeRight.");
+// toolbarManager.appendToGroup("merge", fCopyRightToLeftAction);
+
+// fNextAction= new Action() {
+// public void run() {
+// navigate(true);
+// }
+// };
+// Utilities.initAction(fNextAction, fBundle, "action.NextDiff."); //$NON-NLS-1$
+// toolbarManager.appendToGroup("navigation", fNextAction); //$NON-NLS-1$
+
+// fPreviousAction= new Action() {
+// public void run() {
+// navigate(false);
+// }
+// };
+// Utilities.initAction(fPreviousAction, fBundle, "action.PrevDiff."); //$NON-NLS-1$
+// toolbarManager.appendToGroup("navigation", fPreviousAction); //$NON-NLS-1$
+ }
+
+ /**
+ * This method is called to add actions to the viewer's context menu.
+ * It installs actions for expanding tree nodes, copying one side of a <code>DiffNode</code> to the other side.
+ * Clients can override this method and are free to decide whether they want to call
+ * the inherited method.
+ *
+ * @param manager the menu manager for which to add the actions
+ */
+ protected void fillContextMenu(IMenuManager manager) {
+ if (fExpandAllAction == null) {
+ fExpandAllAction= new Action() {
+ public void run() {
+ expandSelection();
+ }
+ };
+ Utilities.initAction(fExpandAllAction, fBundle, "action.ExpandAll."); //$NON-NLS-1$
+ }
+
+ boolean enable= false;
+ ISelection selection= getSelection();
+ if (selection instanceof IStructuredSelection) {
+ Iterator elements= ((IStructuredSelection)selection).iterator();
+ while (elements.hasNext()) {
+ Object element= elements.next();
+ if (element instanceof IDiffContainer) {
+ if (((IDiffContainer)element).hasChildren()) {
+ enable= true;
+ break;
+ }
+ }
+ }
+ }
+ fExpandAllAction.setEnabled(enable);
+
+ manager.add(fExpandAllAction);
+
+ if (fCopyLeftToRightAction != null)
+ manager.add(fCopyLeftToRightAction);
+ if (fCopyRightToLeftAction != null)
+ manager.add(fCopyRightToLeftAction);
+ }
+
+ /**
+ * Expands to infinity all items in the selection.
+ *
+ * @since 2.0
+ */
+ protected void expandSelection() {
+ ISelection selection= getSelection();
+ if (selection instanceof IStructuredSelection) {
+ Iterator elements= ((IStructuredSelection)selection).iterator();
+ while (elements.hasNext()) {
+ Object next= elements.next();
+ expandToLevel(next, ALL_LEVELS);
+ }
+ }
+ }
+
+ /**
+ * Copies one side of all <code>DiffNode</code>s in the current selection to the other side.
+ * Called from the (internal) actions for copying the sides of a <code>DiffNode</code>.
+ * Clients may override.
+ *
+ * @param leftToRight if <code>true</code> the left side is copied to the right side.
+ * If <code>false</code> the right side is copied to the left side
+ */
+ protected void copySelected(boolean leftToRight) {
+ ISelection selection= getSelection();
+ if (selection instanceof IStructuredSelection) {
+ Iterator e= ((IStructuredSelection) selection).iterator();
+ while (e.hasNext()) {
+ Object element= e.next();
+ if (element instanceof ICompareInput)
+ copyOne((ICompareInput) element, leftToRight);
+ }
+ }
+ }
+
+ /**
+ * Called to copy one side of the given node to the other.
+ * This default implementation delegates the call to <code>ICompareInput.copy(...)</code>.
+ * Clients may override.
+ * @param node the node to copy
+ * @param leftToRight if <code>true</code> the left side is copied to the right side.
+ * If <code>false</code> the right side is copied to the left side
+ */
+ protected void copyOne(ICompareInput node, boolean leftToRight) {
+
+ node.copy(leftToRight);
+
+ // update node's image
+ update(new Object[] { node }, null);
+ }
+
+ /**
+ * Selects the next (or previous) node of the current selection.
+ * If there is no current selection the first (last) node in the tree is selected.
+ * Wraps around at end or beginning.
+ * Clients may override.
+ *
+ * @param next if <code>true</code> the next node is selected, otherwise the previous node
+ */
+ protected void navigate(boolean next) {
+ // Fix for http://dev.eclipse.org/bugs/show_bug.cgi?id=20106
+ internalNavigate(next, false);
+ }
+
+ //---- private
+
+ /**
+ * Selects the next (or previous) node of the current selection.
+ * If there is no current selection the first (last) node in the tree is selected.
+ * Wraps around at end or beginning.
+ * Clients may override.
+ *
+ * @param next if <code>true</code> the next node is selected, otherwise the previous node
+ * @param fireOpen if <code>true</code> an open event is fired.
+ * @return <code>true</code> if at end (or beginning)
+ */
+ private boolean internalNavigate(boolean next, boolean fireOpen) {
+ Control c= getControl();
+ if (!(c instanceof Tree) || c.isDisposed())
+ return false;
+ TreeItem item = getNextItem(next, true);
+ if (item != null) {
+ internalSetSelection(item, fireOpen);
+ }
+ return item == null;
+ }
+
+ private TreeItem getNextItem(boolean next, boolean expand) {
+ Control c= getControl();
+ if (!(c instanceof Tree) || c.isDisposed())
+ return null;
+
+ Tree tree= (Tree) c;
+ TreeItem item= null;
+ TreeItem children[]= tree.getSelection();
+ if (children != null && children.length > 0)
+ item= children[0];
+ if (item == null) {
+ children= tree.getItems();
+ if (children != null && children.length > 0) {
+ item= children[0];
+ if (item != null && item.getItemCount() <= 0) {
+ return item;
+ }
+ }
+ }
+
+ while (true) {
+ item= findNextPrev(item, next, expand);
+ if (item == null)
+ break;
+ if (item.getItemCount() <= 0)
+ break;
+ }
+ return item;
+ }
+
+ private TreeItem findNextPrev(TreeItem item, boolean next, boolean expand) {
+
+ if (item == null)
+ return null;
+
+ TreeItem children[]= null;
+
+ if (!next) {
+
+ TreeItem parent= item.getParentItem();
+ if (parent != null)
+ children= parent.getItems();
+ else
+ children= item.getParent().getItems();
+
+ if (children != null && children.length > 0) {
+ // goto previous child
+ int index= 0;
+ for (; index < children.length; index++)
+ if (children[index] == item)
+ break;
+
+ if (index > 0) {
+
+ item= children[index-1];
+
+ while (true) {
+ createChildren(item);
+ int n= item.getItemCount();
+ if (n <= 0)
+ break;
+
+ if (expand)
+ item.setExpanded(true);
+ item= item.getItems()[n-1];
+ }
+
+ // previous
+ return item;
+ }
+ }
+
+ // go up
+ item= parent;
+
+ } else {
+ if (expand)
+ item.setExpanded(true);
+ createChildren(item);
+
+ if (item.getItemCount() > 0) {
+ // has children: go down
+ children= item.getItems();
+ return children[0];
+ }
+
+ while (item != null) {
+ children= null;
+ TreeItem parent= item.getParentItem();
+ if (parent != null)
+ children= parent.getItems();
+ else
+ children= item.getParent().getItems();
+
+ if (children != null && children.length > 0) {
+ // goto next child
+ int index= 0;
+ for (; index < children.length; index++)
+ if (children[index] == item)
+ break;
+
+ if (index < children.length-1) {
+ // next
+ return children[index+1];
+ }
+ }
+
+ // go up
+ item= parent;
+ }
+ }
+
+ return item;
+ }
+
+ private void internalSetSelection(TreeItem ti, boolean fireOpen) {
+ if (ti != null) {
+ Object data= ti.getData();
+ if (data != null) {
+ // Fix for http://dev.eclipse.org/bugs/show_bug.cgi?id=20106
+ ISelection selection= new StructuredSelection(data);
+ setSelection(selection, true);
+ ISelection currentSelection= getSelection();
+ if (fireOpen && currentSelection != null && selection.equals(currentSelection)) {
+ fireOpen(new OpenEvent(this, selection));
+ }
+ }
+ }
+ }
+
+ private final boolean isEditable(Object element, boolean left) {
+ if (element instanceof ICompareInput) {
+ ICompareInput diff= (ICompareInput) element;
+ Object side= left ? diff.getLeft() : diff.getRight();
+ if (side == null && diff instanceof IDiffElement) {
+ IDiffContainer container= ((IDiffElement)diff).getParent();
+ if (container instanceof ICompareInput) {
+ ICompareInput parent= (ICompareInput) container;
+ side= left ? parent.getLeft() : parent.getRight();
+ }
+ }
+ if (side instanceof IEditableContent)
+ return ((IEditableContent) side).isEditable();
+ }
+ return false;
+ }
+
+ private void updateActions() {
+ int leftToRight= 0;
+ int rightToLeft= 0;
+ ISelection selection= getSelection();
+ if (selection instanceof IStructuredSelection) {
+ IStructuredSelection ss= (IStructuredSelection) selection;
+ Iterator e= ss.iterator();
+ while (e.hasNext()) {
+ Object element= e.next();
+ if (element instanceof ICompareInput) {
+ if (isEditable(element, false))
+ leftToRight++;
+ if (isEditable(element, true))
+ rightToLeft++;
+ if (leftToRight > 0 && rightToLeft > 0)
+ break;
+ }
+ }
+ if (fExpandAllAction != null)
+ fExpandAllAction.setEnabled(selection.isEmpty());
+ }
+ if (fCopyLeftToRightAction != null)
+ fCopyLeftToRightAction.setEnabled(leftToRight > 0);
+ if (fCopyRightToLeftAction != null)
+ fCopyRightToLeftAction.setEnabled(rightToLeft > 0);
+ }
+
+ /*
+ * Fix for http://dev.eclipse.org/bugs/show_bug.cgi?id=20106
+ */
+ private boolean internalOpen() {
+ ISelection selection= getSelection();
+ if (selection != null && !selection.isEmpty()) {
+ fireOpen(new OpenEvent(this, selection));
+ return true;
+ }
+ return false;
+ }
+}
+

Back to the top