/** * <copyright> * * Copyright (c) 2010-2016 Thales Global Services S.A.S 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: * Thales Global Services S.A.S. - initial API and implementation * Stephane Bouchet (Intel Corporation) - Bug #442492 : hide number of differences in the UI * Stephane Bouchet (Intel Corporation) - Bug #489274 : added API viewers creation methods * Jeremy Aubry (Obeo) - Bug #500417 : Cannot call a merge with a given selection programmatically * * </copyright> */ package org.eclipse.emf.diffmerge.ui.viewers; import java.lang.reflect.InvocationTargetException; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.Iterator; import java.util.List; import org.eclipse.compare.CompareEditorInput; import org.eclipse.compare.INavigatable; import org.eclipse.compare.structuremergeviewer.ICompareInput; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.emf.common.util.Logger; import org.eclipse.emf.diffmerge.api.IComparison; import org.eclipse.emf.diffmerge.api.IMatch; import org.eclipse.emf.diffmerge.api.Role; import org.eclipse.emf.diffmerge.api.diff.IDifference; import org.eclipse.emf.diffmerge.api.diff.IPresenceDifference; import org.eclipse.emf.diffmerge.api.diff.IReferenceValuePresence; import org.eclipse.emf.diffmerge.api.diff.IValuePresence; import org.eclipse.emf.diffmerge.diffdata.EComparison; import org.eclipse.emf.diffmerge.diffdata.EElementRelativePresence; import org.eclipse.emf.diffmerge.diffdata.EMatch; import org.eclipse.emf.diffmerge.diffdata.EMergeableDifference; import org.eclipse.emf.diffmerge.diffdata.EValuePresence; import org.eclipse.emf.diffmerge.ui.EMFDiffMergeUIPlugin; import org.eclipse.emf.diffmerge.ui.EMFDiffMergeUIPlugin.ImageID; import org.eclipse.emf.diffmerge.ui.Messages; import org.eclipse.emf.diffmerge.ui.diffuidata.ComparisonSelection; import org.eclipse.emf.diffmerge.ui.diffuidata.MatchAndFeature; import org.eclipse.emf.diffmerge.ui.diffuidata.impl.ComparisonSelectionImpl; import org.eclipse.emf.diffmerge.ui.diffuidata.impl.MatchAndFeatureImpl; import org.eclipse.emf.diffmerge.ui.log.CompareLogEvent; import org.eclipse.emf.diffmerge.ui.log.MergeLogEvent; import org.eclipse.emf.diffmerge.ui.util.DelegatingLabelProvider; import org.eclipse.emf.diffmerge.ui.util.DifferenceKind; import org.eclipse.emf.diffmerge.ui.util.InconsistencyDialog; import org.eclipse.emf.diffmerge.ui.util.UIUtil; import org.eclipse.emf.diffmerge.ui.viewers.FeaturesViewer.FeaturesInput; import org.eclipse.emf.diffmerge.ui.viewers.MergeImpactViewer.ImpactInput; import org.eclipse.emf.diffmerge.ui.viewers.ValuesViewer.ValuesInput; import org.eclipse.emf.diffmerge.util.structures.FArrayList; import org.eclipse.emf.ecore.EObject; import org.eclipse.emf.ecore.EStructuralFeature; import org.eclipse.jface.action.GroupMarker; import org.eclipse.jface.action.MenuManager; import org.eclipse.jface.dialogs.MessageDialog; import org.eclipse.jface.operation.IRunnableWithProgress; import org.eclipse.jface.util.IPropertyChangeListener; import org.eclipse.jface.util.PropertyChangeEvent; import org.eclipse.jface.viewers.ContentViewer; import org.eclipse.jface.viewers.IBaseLabelProvider; import org.eclipse.jface.viewers.ILabelProvider; import org.eclipse.jface.viewers.ISelection; import org.eclipse.jface.viewers.ISelectionChangedListener; import org.eclipse.jface.viewers.ISelectionProvider; import org.eclipse.jface.viewers.IStructuredSelection; import org.eclipse.jface.viewers.ITreeSelection; import org.eclipse.jface.viewers.SelectionChangedEvent; import org.eclipse.jface.viewers.StructuredSelection; import org.eclipse.jface.viewers.TreePath; import org.eclipse.jface.viewers.Viewer; import org.eclipse.jface.viewers.ViewerFilter; import org.eclipse.jface.viewers.ViewerSorter; import org.eclipse.swt.SWT; import org.eclipse.swt.custom.BusyIndicator; import org.eclipse.swt.custom.SashForm; import org.eclipse.swt.events.ControlEvent; import org.eclipse.swt.events.ControlListener; import org.eclipse.swt.events.DisposeEvent; import org.eclipse.swt.events.DisposeListener; import org.eclipse.swt.events.FocusEvent; import org.eclipse.swt.events.FocusListener; import org.eclipse.swt.events.SelectionAdapter; import org.eclipse.swt.events.SelectionEvent; import org.eclipse.swt.graphics.Image; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Control; import org.eclipse.swt.widgets.Item; import org.eclipse.swt.widgets.Menu; import org.eclipse.swt.widgets.MenuItem; import org.eclipse.swt.widgets.Shell; import org.eclipse.swt.widgets.ToolBar; import org.eclipse.swt.widgets.ToolItem; import org.eclipse.ui.IActionBars; import org.eclipse.ui.IWorkbenchActionConstants; import org.eclipse.ui.IWorkbenchPartSite; import org.eclipse.ui.PlatformUI; import org.eclipse.ui.progress.IProgressService; /** * A Viewer for comparisons which is composed of six sub-viewers that show the scopes being * compared, a synthesis of the differences, features of the selected element, and the contents * of the selected feature in each scope. * Input: EMFDiffNode ; Elements: IMatch | IDifference. * @author Olivier Constant */ public class ComparisonViewer extends AbstractComparisonViewer { /** The name of the "filtering state" property */ public static final String PROPERTY_FILTERING = "PROPERTY_FILTERING"; //$NON-NLS-1$ /** The name of the "delete left activation" property */ public static final String PROPERTY_ACTIVATION_DELETE_LEFT = "PROPERTY_ACTIVATION_DELETE_LEFT"; //$NON-NLS-1$ /** The name of the "delete right activation" property */ public static final String PROPERTY_ACTIVATION_DELETE_RIGHT = "PROPERTY_ACTIVATION_DELETE_RIGHT"; //$NON-NLS-1$ /** The name of the "merge to left activation" property */ public static final String PROPERTY_ACTIVATION_MERGE_TO_LEFT = "PROPERTY_ACTIVATION_MERGE_TO_LEFT"; //$NON-NLS-1$ /** The name of the "merge to right activation" property */ public static final String PROPERTY_ACTIVATION_MERGE_TO_RIGHT = "PROPERTY_ACTIVATION_MERGE_TO_RIGHT"; //$NON-NLS-1$ /** The name of the "ignore left activation" property */ public static final String PROPERTY_ACTIVATION_IGNORE_LEFT = "PROPERTY_ACTIVATION_IGNORE_LEFT"; //$NON-NLS-1$ /** The name of the "ignore right activation" property */ public static final String PROPERTY_ACTIVATION_IGNORE_RIGHT = "PROPERTY_ACTIVATION_IGNORE_RIGHT"; //$NON-NLS-1$ /** The synthesis model tree viewer */ protected EnhancedComparisonTreeViewer _viewerSynthesisMain; /** The left model tree viewer */ protected EnhancedComparisonSideViewer _viewerSynthesisLeft; /** The right model tree viewer */ protected EnhancedComparisonSideViewer _viewerSynthesisRight; /** The features viewer */ protected EnhancedFeaturesViewer _viewerFeatures; /** The left values viewer */ protected EnhancedValuesViewer _viewerValuesLeft; /** The right values viewer */ protected EnhancedValuesViewer _viewerValuesRight; /** A filter for move origins */ protected ViewerFilter _filterMoveOrigins; /** A filter for unchanged elements */ protected ViewerFilter _filterUnchangedElements; /** An alphanumeric sorter */ protected ViewerSorter _sorterSynthesis; /** Whether the left and right trees are synchronized with the synthesis tree */ protected boolean _isLeftRightSynced; /** The potentially null last selection */ private ComparisonSelection _lastUserSelection; /** The non-null selection provider covering certain sub-viewers */ protected SelectionBridge.SingleSource _multiViewerSelectionProvider; /** * Constructor * @param parent_p a non-null composite */ public ComparisonViewer(Composite parent_p) { this(parent_p, null); } /** * Constructor * @param parent_p a non-null composite * @param actionBars_p optional action bars */ public ComparisonViewer(Composite parent_p, IActionBars actionBars_p) { super(parent_p, actionBars_p); } /** * Return whether context menus can be contributed via the usual ADDITIONS group * in the given viewer */ protected boolean acceptContextMenuAdditions(Viewer viewer_p) { return true; } /** * Add differences to merge on the given match to the given list according * to the given criteria * @param toMerge_p a non-null, modifiable list * @param match_p a non-null match * @param destination_p a non-null role which is TARGET or REFEREBCE * @param incrementalMode_p whether optional deletions must be skipped */ protected void addDifferencesToMerge(List<IDifference> toMerge_p, IMatch match_p, Role destination_p, boolean incrementalMode_p) { for (IDifference difference : match_p.getAllDifferences()) { if (!getInput().getCategoryManager().isFiltered(difference)) { if (!incrementalMode_p || difference instanceof IPresenceDifference && ((IPresenceDifference)difference).getPresenceRole() != destination_p) toMerge_p.add(difference); } } } /** * Add differences to merge on the given match and its children to the given list according * to the given criteria * @param toMerge_p a non-null, modifiable list * @param match_p a non-null match * @param destination_p a non-null role which is TARGET or REFEREBCE * @param incrementalMode_p whether optional deletions must be skipped */ protected void addDifferencesToMergeRec(List<IDifference> toMerge_p, IMatch match_p, Role destination_p, boolean incrementalMode_p) { addDifferencesToMerge(toMerge_p, match_p, destination_p, incrementalMode_p); for (IMatch child : getInput().getCategoryManager().getChildrenForMerge(match_p)) { addDifferencesToMergeRec(toMerge_p, child, destination_p, incrementalMode_p); } } /** * Convert the given structured selection to a comparison selection * @param selection_p a non-null selection * @return a non-null comparison selection */ protected ComparisonSelection asComparisonSelection(IStructuredSelection selection_p) { Collection<IMatch> matches = new ArrayList<IMatch>(); EComparison comparison = getComparison(); if (comparison != null) { for (Object selected : selection_p.toArray()) { if (selected instanceof EObject) { EObject selectedElement = (EObject)selected; IMatch match = comparison.getMapping().getMatchFor(selectedElement, Role.TARGET); if (match == null) match = comparison.getMapping().getMatchFor(selectedElement, Role.REFERENCE); if (match != null) matches.add(match); } } } ComparisonSelection result = new ComparisonSelectionImpl(matches, null); return result; } /** * Return whether the given situation allows adding to the left * @param originKind_p a non-null kind */ protected boolean canAddToTheLeft(DifferenceKind originKind_p) { final Collection<DifferenceKind> allowed = Arrays.asList( DifferenceKind.CONFLICT, DifferenceKind.MODIFIED, DifferenceKind.FROM_BOTH, DifferenceKind.FROM_RIGHT, DifferenceKind.FROM_RIGHT_ADD, DifferenceKind.FROM_LEFT_DEL); return allowed.contains(originKind_p); } /** * Return whether the given situation allows adding to the right * @param originKind_p a non-null kind */ protected boolean canAddToTheRight(DifferenceKind originKind_p) { final Collection<DifferenceKind> allowed = Arrays.asList( DifferenceKind.CONFLICT, DifferenceKind.MODIFIED, DifferenceKind.FROM_BOTH, DifferenceKind.FROM_LEFT, DifferenceKind.FROM_LEFT_ADD, DifferenceKind.FROM_RIGHT_DEL); return allowed.contains(originKind_p); } /** * @see org.eclipse.emf.diffmerge.ui.viewers.AbstractComparisonViewer#compareInputChanged(org.eclipse.compare.structuremergeviewer.ICompareInput) */ @Override public void compareInputChanged(final ICompareInput source_p) { BusyIndicator.showWhile(getShell().getDisplay(), new Runnable() { /** * @see java.lang.Runnable#run() */ public void run() { if (source_p instanceof EMFDiffNode) { EMFDiffNode node = (EMFDiffNode)source_p; boolean isFiltering = node.getCategoryManager().isUIFiltering(); firePropertyChangeEvent(PROPERTY_FILTERING, Boolean.valueOf(isFiltering)); } refresh(); } }); } /** * @see org.eclipse.emf.diffmerge.ui.viewers.AbstractComparisonViewer#createControls(org.eclipse.swt.widgets.Composite) */ @Override protected Composite createControls(Composite parent_p) { // Non-graphical instance variables initialize(); // Main control SashForm result = new SashForm(parent_p, SWT.VERTICAL); // Upper and lower parts SashForm upperPart = createRowUpper(result); SashForm lowerPart = createRowLower(result); setupColumns(upperPart, lowerPart); result.setWeights(getDefaultRowWeights()); // Tools: buttons and menus setupToolBars(); return result; } /** * Create the "collapse all" item in the given context and return it * @param context_p a non-null object * @return a potentially null item */ protected Item createItemCollapse(ToolBar context_p) { ToolItem result = new ToolItem(context_p, SWT.PUSH); result.setImage(EMFDiffMergeUIPlugin.getDefault().getImage( EMFDiffMergeUIPlugin.ImageID.COLLAPSEALL)); result.setToolTipText(Messages.ComparisonViewer_CollapseTooltip); result.addSelectionListener(new SelectionAdapter() { /** * @see org.eclipse.swt.events.SelectionListener#widgetSelected(org.eclipse.swt.events.SelectionEvent) */ @Override public void widgetSelected(SelectionEvent event_p) { BusyIndicator.showWhile(getShell().getDisplay(), new Runnable() { /** * @see java.lang.Runnable#run() */ public void run() { _viewerSynthesisMain.getInnerViewer().collapseAll(); } }); } }); return result; } /** * Create the "delete" item for the given side in the given context and return it * @param context_p a non-null object * @param onLeft_p whether the side is left * @return a potentially null item */ protected Item createItemDelete(ToolBar context_p, final boolean onLeft_p) { final ToolItem result = new ToolItem(context_p, SWT.PUSH); // Image result.setImage(EMFDiffMergeUIPlugin.getDefault().getImage( EMFDiffMergeUIPlugin.ImageID.DELETE)); // Tool tip result.setToolTipText(onLeft_p? Messages.ComparisonViewer_DeleteLeftTooltip: Messages.ComparisonViewer_DeleteRightTooltip); result.setEnabled(false); // Activation addPropertyChangeListener(new IPropertyChangeListener() { /** * @see org.eclipse.jface.util.IPropertyChangeListener#propertyChange(org.eclipse.jface.util.PropertyChangeEvent) */ public void propertyChange(PropertyChangeEvent event_p) { if (onLeft_p && PROPERTY_ACTIVATION_DELETE_LEFT.equals(event_p.getProperty()) || !onLeft_p && PROPERTY_ACTIVATION_DELETE_RIGHT.equals(event_p.getProperty())) { Object newValue = event_p.getNewValue(); if (newValue instanceof Boolean) result.setEnabled(((Boolean)newValue).booleanValue()); } } }); // Selection result.addSelectionListener(new SelectionAdapter() { /** * @see org.eclipse.swt.events.SelectionListener#widgetSelected(org.eclipse.swt.events.SelectionEvent) */ @Override public void widgetSelected(SelectionEvent event_p) { merge(onLeft_p, false); } }); return result; } /** * Create the "expand all" item in the given context and return it * @param context_p a non-null object * @return a potentially null item */ protected Item createItemExpand(ToolBar context_p) { ToolItem result = new ToolItem(context_p, SWT.PUSH); result.setImage(EMFDiffMergeUIPlugin.getDefault().getImage( EMFDiffMergeUIPlugin.ImageID.EXPANDALL)); result.setToolTipText(Messages.ComparisonViewer_ExpandTooltip); result.addSelectionListener(new SelectionAdapter() { /** * @see org.eclipse.swt.events.SelectionListener#widgetSelected(org.eclipse.swt.events.SelectionEvent) */ @Override public void widgetSelected(SelectionEvent event_p) { BusyIndicator.showWhile(getShell().getDisplay(), new Runnable() { /** * @see java.lang.Runnable#run() */ public void run() { _viewerSynthesisMain.getInnerViewer().expandAll(); } }); } }); return result; } /** * Create the "filter" item in the given context and return it * @param context_p a non-null object * @return a potentially null item */ protected Item createItemFilter(ToolBar context_p) { final ToolItem result = new ToolItem(context_p, SWT.CHECK); result.setImage(EMFDiffMergeUIPlugin.getDefault().getImage( EMFDiffMergeUIPlugin.ImageID.FILTER)); result.setToolTipText(Messages.ComparisonViewer_FilterToolTip); result.addSelectionListener(new SelectionAdapter() { /** The dialog lastly opened */ protected CategoryDialog _lastDialog; { _lastDialog = null; result.addDisposeListener(new DisposeListener() { /** * @see org.eclipse.swt.events.DisposeListener#widgetDisposed(org.eclipse.swt.events.DisposeEvent) */ public void widgetDisposed(DisposeEvent e_p) { if (_lastDialog != null) _lastDialog.close(); } }); } /** * @see org.eclipse.swt.events.SelectionListener#widgetSelected(org.eclipse.swt.events.SelectionEvent) */ @Override public void widgetSelected(SelectionEvent event_p) { if (result.getSelection()) { // Just selected _lastDialog = new CategoryDialog(getShell(), getInput()); _lastDialog.open(); _lastDialog.getShell().addDisposeListener(new DisposeListener() { /** * @see org.eclipse.swt.events.DisposeListener#widgetDisposed(org.eclipse.swt.events.DisposeEvent) */ public void widgetDisposed(DisposeEvent e_p) { // Toggle tool item when closing _lastDialog = null; if (!result.isDisposed()) result.setSelection(false); } }); } else { // Not selected any longer if (_lastDialog != null) _lastDialog.close(); } } }); result.setSelection(false); return result; } /** * Create the "ignore" item for the given side in the given context and return it * @param context_p a non-null object * @param onLeft_p whether the side is left * @return a potentially null item */ protected Item createItemIgnore(ToolBar context_p, final boolean onLeft_p) { final ToolItem result = new ToolItem(context_p, SWT.PUSH); // Image result.setImage(EMFDiffMergeUIPlugin.getDefault().getImage( EMFDiffMergeUIPlugin.ImageID.CHECKED)); // Tool tip result.setToolTipText(onLeft_p? Messages.ComparisonViewer_IgnoreLeftTooltip: Messages.ComparisonViewer_IgnoreRightTooltip); result.setEnabled(false); // Activation addPropertyChangeListener(new IPropertyChangeListener() { /** * @see org.eclipse.jface.util.IPropertyChangeListener#propertyChange(org.eclipse.jface.util.PropertyChangeEvent) */ public void propertyChange(PropertyChangeEvent event_p) { if (onLeft_p && PROPERTY_ACTIVATION_IGNORE_LEFT.equals(event_p.getProperty()) || !onLeft_p && PROPERTY_ACTIVATION_IGNORE_RIGHT.equals(event_p.getProperty())) { Object newValue = event_p.getNewValue(); if (newValue instanceof Boolean) result.setEnabled(((Boolean)newValue).booleanValue()); } } }); // Selection result.addSelectionListener(new SelectionAdapter() { /** * @see org.eclipse.swt.events.SelectionListener#widgetSelected(org.eclipse.swt.events.SelectionEvent) */ @Override public void widgetSelected(SelectionEvent event_p) { ignore(onLeft_p); } }); return result; } /** * Create the "inconsistency" item in the given context and return it * @param context_p a non-null object * @return a potentially null item */ protected Item createItemInconsistency(ToolBar context_p) { final ToolItem result = new ToolItem(context_p, SWT.PUSH); result.setImage(EMFDiffMergeUIPlugin.getDefault().getImage( EMFDiffMergeUIPlugin.ImageID.WARNING)); result.addSelectionListener(new SelectionAdapter() { /** * @see org.eclipse.swt.events.SelectionListener#widgetSelected(org.eclipse.swt.events.SelectionEvent) */ @Override public void widgetSelected(SelectionEvent event_p) { final Shell shell = getShell(); final EComparison comparison = getComparison(); if (shell != null && comparison != null) { shell.getDisplay().syncExec(new Runnable() { /** * @see java.lang.Runnable#run() */ public void run() { InconsistencyDialog dialog = new InconsistencyDialog(shell, comparison); dialog.open(); } }); } } }); result.setDisabledImage(EMFDiffMergeUIPlugin.getDefault().getImage(ImageID.EMPTY)); result.setEnabled(false); addPropertyChangeListener(new IPropertyChangeListener() { /** * @see org.eclipse.jface.util.IPropertyChangeListener#propertyChange(org.eclipse.jface.util.PropertyChangeEvent) */ public void propertyChange(PropertyChangeEvent event_p) { if (PROPERTY_CURRENT_INPUT.equals(event_p.getProperty())) { IComparison comparison = getComparison(); boolean enabled = comparison != null && !comparison.isConsistent(); result.setEnabled(enabled); result.setToolTipText(enabled? Messages.ComparisonViewer_InconsistencyTooltip: null); } } }); return result; } /** * Create the locking item in the given context for the given side and return it * @param context_p a non-null object * @param onLeft_p whether the side is left or right * @return a potentially null item */ protected Item createItemLock(ToolBar context_p, final boolean onLeft_p) { final ToolItem result = new ToolItem(context_p, SWT.CHECK); final Image openLockImage = EMFDiffMergeUIPlugin.getDefault().getImage(EMFDiffMergeUIPlugin.ImageID.LOCK_OPEN); final Image closedLockImage = EMFDiffMergeUIPlugin.getDefault().getImage(EMFDiffMergeUIPlugin.ImageID.LOCK_CLOSED); result.setImage(openLockImage); final String lockedTooltip = Messages.ComparisonViewer_LockTooltip_Locked; final String unlockedTooltip = Messages.ComparisonViewer_LockTooltip_Unlocked; result.setToolTipText(unlockedTooltip); result.addSelectionListener(new SelectionAdapter() { /** * @see org.eclipse.swt.events.SelectionListener#widgetSelected(org.eclipse.swt.events.SelectionEvent) */ @Override public void widgetSelected(SelectionEvent event_p) { boolean editable = !result.getSelection(); getInput().setEditable(editable, onLeft_p); result.setImage(editable? openLockImage: closedLockImage); result.setToolTipText(editable? unlockedTooltip: lockedTooltip); refreshTools(); } }); addPropertyChangeListener(new IPropertyChangeListener() { /** * @see org.eclipse.jface.util.IPropertyChangeListener#propertyChange(org.eclipse.jface.util.PropertyChangeEvent) */ public void propertyChange(PropertyChangeEvent event_p) { if (PROPERTY_CURRENT_INPUT.equals(event_p.getProperty())) { EMFDiffNode input = getInput(); if (input != null) { boolean editable = input.isEditable(onLeft_p); result.setSelection(!editable); result.setImage(editable? openLockImage:closedLockImage); result.setToolTipText(editable? unlockedTooltip: lockedTooltip); result.setEnabled(input.isEditionPossible(onLeft_p)); } } } }); return result; } /** * Create the "log events" item in the given context and return it * @param context_p a non-null object * @return result a potentially null item */ protected Item createItemLogEvents(Menu context_p) { final MenuItem result = new MenuItem(context_p, SWT.CHECK); result.setText(Messages.ComparisonViewer_LogEventsMenuItem); // Initialization addPropertyChangeListener(new IPropertyChangeListener() { /** * @see org.eclipse.jface.util.IPropertyChangeListener#propertyChange(org.eclipse.jface.util.PropertyChangeEvent) */ public void propertyChange(PropertyChangeEvent event_p) { if (PROPERTY_CURRENT_INPUT.equals(event_p.getProperty())) { EMFDiffNode input = getInput(); if (input != null && !result.isDisposed()) result.setSelection(input.isLogEvents()); } } }); // Selection result.addSelectionListener(new SelectionAdapter() { /** * @see org.eclipse.swt.events.SelectionAdapter#widgetSelected(org.eclipse.swt.events.SelectionEvent) */ @Override public void widgetSelected(SelectionEvent e_p) { boolean logEvents = result.getSelection(); EMFDiffNode input = getInput(); if (input != null) { input.setLogEvents(logEvents); if (logEvents) getLogger().log(new CompareLogEvent(getEditingDomain(), getComparison())); } } }); return result; } /** * Create the "merge" item to the given side in the given context and return it * @param toolbar_p a non-null object * @param toLeft_p whether the side is left * @return a potentially null item */ protected Item createItemMerge(ToolBar toolbar_p, final boolean toLeft_p) { final ToolItem result = new ToolItem(toolbar_p, SWT.PUSH); // Image EMFDiffMergeUIPlugin.ImageID imageID = toLeft_p? EMFDiffMergeUIPlugin.ImageID.CHECKOUT_ACTION: EMFDiffMergeUIPlugin.ImageID.CHECKIN_ACTION; result.setImage(EMFDiffMergeUIPlugin.getDefault().getImage(imageID)); // Tool tip result.setToolTipText(toLeft_p? Messages.ComparisonViewer_MergeLeftTooltip: Messages.ComparisonViewer_MergeRightTooltip); result.setEnabled(false); // Activation addPropertyChangeListener(new IPropertyChangeListener() { /** * @see org.eclipse.jface.util.IPropertyChangeListener#propertyChange(org.eclipse.jface.util.PropertyChangeEvent) */ public void propertyChange(PropertyChangeEvent event_p) { if (toLeft_p && PROPERTY_ACTIVATION_MERGE_TO_LEFT.equals(event_p.getProperty()) || !toLeft_p && PROPERTY_ACTIVATION_MERGE_TO_RIGHT.equals(event_p.getProperty())) { Object newValue = event_p.getNewValue(); if (newValue instanceof Boolean) result.setEnabled(((Boolean)newValue).booleanValue()); } } }); // Selection result.addSelectionListener(new SelectionAdapter() { /** * @see org.eclipse.swt.events.SelectionListener#widgetSelected(org.eclipse.swt.events.SelectionEvent) */ @Override public void widgetSelected(SelectionEvent event_p) { merge(toLeft_p, true); } }); return result; } /** * Create the "next" navigation item in the given context and return it * @param toolbar_p a non-null context * @return a potentially null item */ protected Item createItemNavigationNext(ToolBar toolbar_p) { ToolItem result = new ToolItem(toolbar_p, SWT.PUSH); result.setImage(EMFDiffMergeUIPlugin.getDefault().getImage( EMFDiffMergeUIPlugin.ImageID.NEXT_DIFF_NAV)); result.setToolTipText(Messages.ComparisonViewer_NextTooltip); result.addSelectionListener(new SelectionAdapter() { /** * @see org.eclipse.swt.events.SelectionListener#widgetSelected(org.eclipse.swt.events.SelectionEvent) */ @Override public void widgetSelected(SelectionEvent event_p) { navigate(true); } }); return result; } /** * Create the "previous" navigation item in the given context and return it * @param context_p a non-null object * @return a potentially null tool item */ protected Item createItemNavigationPrevious(ToolBar context_p) { ToolItem result = new ToolItem(context_p, SWT.PUSH); result.setImage(EMFDiffMergeUIPlugin.getDefault().getImage( EMFDiffMergeUIPlugin.ImageID.PREV_DIFF_NAV)); result.setToolTipText(Messages.ComparisonViewer_PreviousTooltip); result.addSelectionListener(new SelectionAdapter() { /** * @see org.eclipse.swt.events.SelectionListener#widgetSelected(org.eclipse.swt.events.SelectionEvent) */ @Override public void widgetSelected(SelectionEvent event_p) { navigate(false); } }); return result; } /** * Create the "show all values" item in the given context and return it * @param context_p a non-null object * @return result a potentially null item */ protected Item createItemShowAllFeatures(Menu context_p) { final MenuItem result = new MenuItem(context_p, SWT.RADIO); result.setText(Messages.ComparisonViewer_ShowAllFeatures); result.addSelectionListener(new SelectionAdapter() { /** * @see org.eclipse.swt.events.SelectionAdapter#widgetSelected(org.eclipse.swt.events.SelectionEvent) */ @Override public void widgetSelected(SelectionEvent e_p) { _viewerFeatures.getInnerViewer().setDifferenceAgnostic(true); _viewerValuesLeft.getInnerViewer().setDifferenceAgnostic(true); _viewerValuesRight.getInnerViewer().setDifferenceAgnostic(true); } }); return result; } /** * Create the "show all values" item in the given context and return it * @param context_p a non-null object * @return result a potentially null item */ protected Item createItemShowAllValues(Menu context_p) { final MenuItem result = new MenuItem(context_p, SWT.RADIO); result.setText(Messages.ComparisonViewer_ShowAllValues); result.addSelectionListener(new SelectionAdapter() { /** * @see org.eclipse.swt.events.SelectionAdapter#widgetSelected(org.eclipse.swt.events.SelectionEvent) */ @Override public void widgetSelected(SelectionEvent e_p) { _viewerFeatures.getInnerViewer().setDifferenceAgnostic(false); _viewerValuesLeft.getInnerViewer().setDifferenceAgnostic(true); _viewerValuesRight.getInnerViewer().setDifferenceAgnostic(true); } }); return result; } /** * Create the "show difference numbers per match" item in the given context and return it * @param context_p a non-null object * @return result a potentially null item */ protected Item createItemShowDifferenceNumbers(Menu context_p) { final MenuItem result = new MenuItem(context_p, SWT.CHECK); result.setText(Messages.ComparisonViewer_ShowDifferenceNumbersMenuItem); // Initialization addPropertyChangeListener(new IPropertyChangeListener() { /** * @see org.eclipse.jface.util.IPropertyChangeListener#propertyChange(org.eclipse.jface.util.PropertyChangeEvent) */ public void propertyChange(PropertyChangeEvent event_p) { if (PROPERTY_CURRENT_INPUT.equals(event_p.getProperty())) { EMFDiffNode input = getInput(); if (input != null && !result.isDisposed()) result.setSelection(!input.isHideDifferenceNumbers()); } } }); // Selection result.addSelectionListener(new SelectionAdapter() { /** * @see org.eclipse.swt.events.SelectionAdapter#widgetSelected(org.eclipse.swt.events.SelectionEvent) */ @Override public void widgetSelected(SelectionEvent e_p) { boolean showDiffNumbers = result.getSelection(); EMFDiffNode input = getInput(); if (input != null) { input.setHideDifferenceNumbers(!showDiffNumbers); refresh(); } } }); return result; } /** * Create the "show values on differences" item in the given context and return it * @param context_p a non-null object * @return result a potentially null item */ protected Item createItemShowDiffValues(Menu context_p) { final MenuItem result = new MenuItem(context_p, SWT.RADIO); result.setText(Messages.ComparisonViewer_ShowValueDiffs); result.setSelection(true); result.addSelectionListener(new SelectionAdapter() { /** * @see org.eclipse.swt.events.SelectionAdapter#widgetSelected(org.eclipse.swt.events.SelectionEvent) */ @Override public void widgetSelected(SelectionEvent e_p) { _viewerFeatures.getInnerViewer().setDifferenceAgnostic(false); _viewerValuesLeft.getInnerViewer().setDifferenceAgnostic(false); _viewerValuesRight.getInnerViewer().setDifferenceAgnostic(false); } }); return result; } /** * Create the "show merge impact" item in the given context and return it * @param context_p a non-null object * @return result a potentially null item */ protected Item createItemShowImpact(Menu context_p) { final MenuItem result = new MenuItem(context_p, SWT.CHECK); result.setText(Messages.ComparisonViewer_ImpactMenuItem); // Initialization addPropertyChangeListener(new IPropertyChangeListener() { /** * @see org.eclipse.jface.util.IPropertyChangeListener#propertyChange(org.eclipse.jface.util.PropertyChangeEvent) */ public void propertyChange(PropertyChangeEvent event_p) { if (PROPERTY_CURRENT_INPUT.equals(event_p.getProperty())) { EMFDiffNode input = getInput(); if (input != null && !result.isDisposed()) result.setSelection(input.isShowMergeImpact()); } } }); // Selection result.addSelectionListener(new SelectionAdapter() { /** * @see org.eclipse.swt.events.SelectionAdapter#widgetSelected(org.eclipse.swt.events.SelectionEvent) */ @Override public void widgetSelected(SelectionEvent e_p) { boolean showImpact = result.getSelection(); EMFDiffNode input = getInput(); if (input != null) { input.setShowMergeImpact(showImpact); input.setDefaultShowImpact(showImpact); } } }); return result; } /** * Create the "show uncounted elements" item in the given context and return it * @param context_p a non-null object * @return result a potentially null item */ protected Item createItemShowUncounted(Menu context_p) { final MenuItem result = new MenuItem(context_p, SWT.CHECK); result.setText(Messages.ComparisonViewer_ShowUncountedMenuItem); result.addSelectionListener(new SelectionAdapter() { /** * @see org.eclipse.swt.events.SelectionAdapter#widgetSelected(org.eclipse.swt.events.SelectionEvent) */ @Override public void widgetSelected(SelectionEvent e_p) { if (result.getSelection()) _viewerSynthesisMain.getInnerViewer().removeFilter(_filterUnchangedElements); else _viewerSynthesisMain.getInnerViewer().addFilter(_filterUnchangedElements); } }); return result; } /** * Create the "support undo/redo" item in the given context and return it * @param context_p a non-null object * @return result a potentially null item */ protected Item createItemSupportUndoRedo(Menu context_p) { final MenuItem result = new MenuItem(context_p, SWT.CHECK); result.setText(Messages.ComparisonViewer_SupportUndoRedoMenuItem); // Initialization addPropertyChangeListener(new IPropertyChangeListener() { /** * @see org.eclipse.jface.util.IPropertyChangeListener#propertyChange(org.eclipse.jface.util.PropertyChangeEvent) */ public void propertyChange(PropertyChangeEvent event_p) { if (PROPERTY_CURRENT_INPUT.equals(event_p.getProperty())) { EMFDiffNode input = getInput(); if (input != null && !result.isDisposed()) { result.setSelection(input.isUndoRedoSupported()); result.setEnabled(input.getEditingDomain() != null); } } } }); // Selection result.addSelectionListener(new SelectionAdapter() { /** * @see org.eclipse.swt.events.SelectionAdapter#widgetSelected(org.eclipse.swt.events.SelectionEvent) */ @Override public void widgetSelected(SelectionEvent e_p) { EMFDiffNode input = getInput(); if (input != null) input.setUndoRedoSupported(result.getSelection()); } }); return result; } /** * Create the "sort" item in the given context and return it * @param context_p a non-null object * @return a potentially null item */ protected Item createItemSort(Menu context_p) { final MenuItem result = new MenuItem(context_p, SWT.CHECK); result.setImage(EMFDiffMergeUIPlugin.getDefault().getImage( EMFDiffMergeUIPlugin.ImageID.SORT)); result.setText(Messages.ComparisonViewer_SortTooltip); result.addSelectionListener(new SelectionAdapter() { /** * @see org.eclipse.swt.events.SelectionListener#widgetSelected(org.eclipse.swt.events.SelectionEvent) */ @Override public void widgetSelected(SelectionEvent event_p) { if (result.getSelection()) _viewerSynthesisMain.getInnerViewer().setSorter(_sorterSynthesis); else _viewerSynthesisMain.getInnerViewer().setSorter(null); } }); return result; } /** * Create the "sync" item in the given context and return it * @param context_p a non-null object * @return a potentially null item */ protected Item createItemSync(Menu context_p) { final MenuItem result = new MenuItem(context_p, SWT.CHECK); result.setImage(EMFDiffMergeUIPlugin.getDefault().getImage( EMFDiffMergeUIPlugin.ImageID.SYNCED)); result.setText(Messages.ComparisonViewer_LinkViewsTooltip); result.setSelection(_isLeftRightSynced); result.addSelectionListener(new SelectionAdapter() { /** * @see org.eclipse.swt.events.SelectionListener#widgetSelected(org.eclipse.swt.events.SelectionEvent) */ @Override public void widgetSelected(SelectionEvent event_p) { boolean synced = result.getSelection(); _isLeftRightSynced = synced; if (_isLeftRightSynced) { BusyIndicator.showWhile(getShell().getDisplay(), new Runnable() { /** * @see java.lang.Runnable#run() */ public void run() { IStructuredSelection selection = _viewerSynthesisMain.getSelection(); _viewerSynthesisLeft.setSelection(getSelectionAsSide(selection, true), true); _viewerSynthesisRight.setSelection(getSelectionAsSide(selection, false), true); } }); } } }); return result; } /** * Create the "use custom icons" item in the given context and return it * @param context_p a non-null object * @return result a potentially null item */ protected Item createItemUseCustomIcons(Menu context_p) { final MenuItem result = new MenuItem(context_p, SWT.CHECK); result.setText(Messages.ComparisonViewer_IconsMenuItem); // Initialization addPropertyChangeListener(new IPropertyChangeListener() { /** * @see org.eclipse.jface.util.IPropertyChangeListener#propertyChange(org.eclipse.jface.util.PropertyChangeEvent) */ public void propertyChange(PropertyChangeEvent event_p) { if (PROPERTY_CURRENT_INPUT.equals(event_p.getProperty())) { EMFDiffNode input = getInput(); if (input != null && !result.isDisposed()) result.setSelection(input.usesCustomIcons()); } } }); // Selection result.addSelectionListener(new SelectionAdapter() { /** * @see org.eclipse.swt.events.SelectionAdapter#widgetSelected(org.eclipse.swt.events.SelectionEvent) */ @Override public void widgetSelected(SelectionEvent e_p) { EMFDiffNode input = getInput(); if (input != null) { input.setUseCustomIcons(result.getSelection()); _viewerSynthesisMain.refresh(); _viewerFeatures.refresh(); _viewerValuesLeft.refresh(); _viewerValuesRight.refresh(); } } }); return result; } /** * Create the "Use technical representation" item in the given context and return it * @param context_p a non-null object * @return result a potentially null item */ protected Item createItemUseTechnicalRepresentation(Menu context_p) { final MenuItem result = new MenuItem(context_p, SWT.CHECK); result.setText(Messages.ComparisonViewer_UseTechnicalRepresentation); result.addSelectionListener(new SelectionAdapter() { /** * @see org.eclipse.swt.events.SelectionAdapter#widgetSelected(org.eclipse.swt.events.SelectionEvent) */ @Override public void widgetSelected(SelectionEvent e_p) { _viewerFeatures.getInnerViewer().setTechnical(result.getSelection()); } }); return result; } /** * @see org.eclipse.emf.diffmerge.ui.viewers.AbstractComparisonViewer#createNavigatable() */ @Override protected INavigatable createNavigatable() { INavigatable result = new INavigatable() { /** * @see org.eclipse.compare.INavigatable#getInput() */ public Object getInput() { return ComparisonViewer.this.getInput(); } /** * @see org.eclipse.compare.INavigatable#hasChange(int) */ public boolean hasChange(int changeFlag_p) { return true; } /** * @see org.eclipse.compare.INavigatable#openSelectedChange() */ public boolean openSelectedChange() { return false; } /** * @see org.eclipse.compare.INavigatable#selectChange(int) */ public boolean selectChange(int changeFlag_p) { boolean innerResult = false; switch (changeFlag_p) { case INavigatable.NEXT_CHANGE: innerResult = navigate(true); break; case INavigatable.PREVIOUS_CHANGE: innerResult = navigate(false); break; } return innerResult; } }; return result; } /** * Create and return the lower row of the GUI * @param parent_p a non-null composite * @return a non-null widget */ protected SashForm createRowLower(Composite parent_p) { SashForm result = new SashForm(parent_p, SWT.HORIZONTAL); // Features section _viewerFeatures = createViewerFeatures(result); // Values section _viewerValuesLeft = createViewerValues(result, true); _viewerValuesRight = createViewerValues(result, false); return result; } /** * Create and return the upper row of the GUI * @param parent_p a non-null composite * @return a non-null widget */ protected SashForm createRowUpper(Composite parent_p) { SashForm result = new SashForm(parent_p, SWT.HORIZONTAL); _viewerSynthesisMain = createViewerSynthesis(result); _viewerSynthesisLeft = createViewerSynthesisSide(result, true); _viewerSynthesisRight = createViewerSynthesisSide(result, false); return result; } /** * Create context menus for the given viewer * @param viewer_p a non-null viewer * @param useLocalSelectionProvider_p whether the selection provider of the viewer must be used * @return a potentially null menu manager for the context menus */ protected MenuManager createViewerContextMenus(HeaderViewer<?> viewer_p, boolean useLocalSelectionProvider_p) { MenuManager result = new MenuManager(); result.setRemoveAllWhenShown(true); Control control = viewer_p.getInnerViewer().getControl(); Menu contextMenu = result.createContextMenu(control); control.setMenu(contextMenu); ISelectionProvider selectionProvider = useLocalSelectionProvider_p? viewer_p.getInnerViewer(): getMultiViewerSelectionProvider(); // Diff/merge-specific menu items populateContextMenu(result, viewer_p, selectionProvider); // External contributions if (acceptContextMenuAdditions(viewer_p)) { IWorkbenchPartSite site = getSite(); if (site != null) { result.add(new GroupMarker(IWorkbenchActionConstants.MB_ADDITIONS)); site.registerContextMenu(result, selectionProvider); } } return result; } /** * Create, configure and return a features viewer * @param parent_p a non-null composite * @return a non-null viewer */ protected EnhancedFeaturesViewer createViewerFeatures(Composite parent_p) { final EnhancedFeaturesViewer result = doCreateViewerFeatures(parent_p); // User selection: send to global viewer result.addSWTSelectionListener(new SelectionAdapter() { /** * @see org.eclipse.swt.events.SelectionAdapter#widgetSelected(org.eclipse.swt.events.SelectionEvent) */ @Override public void widgetSelected(SelectionEvent event_p) { IStructuredSelection selection = result.getSelection(); if (selection.size() == 1) { IMatch match = result.getInput() == null? null: result.getInput().getMatch(); if (match instanceof EMatch) { EStructuralFeature feature = (EStructuralFeature)selection.getFirstElement(); MatchAndFeature newInputDetails = new MatchAndFeatureImpl((EMatch)match, feature); setSelection(new ComparisonSelectionImpl( newInputDetails, getDrivingRole()), true, result.getInnerViewer()); } } } }); // Global selection change: update local selection addSelectionChangedListener(new ISelectionChangedListener() { /** * @see org.eclipse.jface.viewers.ISelectionChangedListener#selectionChanged(org.eclipse.jface.viewers.SelectionChangedEvent) */ public void selectionChanged(SelectionChangedEvent event_p) { ISelection rawSelection = event_p.getSelection(); Object source = event_p.getSource(); if (rawSelection instanceof ComparisonSelection && source != result.getInnerViewer()) { ComparisonSelection selection = (ComparisonSelection)rawSelection; if (selection.getSelectedMatches().size() <= 1) { // No more than one match EMatch match = selection.asMatch(); if (match != null) { // One match: new input FeaturesInput newInput = new FeaturesInput(getInput(), match); boolean changeInput = !newInput.equals(result.getInput()); if (changeInput) result.setInput(newInput); // New selection EStructuralFeature feature = selection.asFeature(); if (feature != null) { IStructuredSelection newSelection = new StructuredSelection(feature); result.setSelection(newSelection, true); } else if (changeInput) { // New input and no feature selected: select first feature if any EStructuralFeature firstFeature = result.getInnerViewer().getFirstIn(newInput); if (firstFeature != null) result.setSelection(new StructuredSelection(firstFeature)); } } else { // No match: no input result.setInput(null); } } else { // More than one match: no input result.setInput(null); } } } }); return result; } /** * Create, configure and return the main viewer of the synthesis row * @param parent_p a non-null composite * @return a non-null viewer */ protected EnhancedComparisonTreeViewer createViewerSynthesis(Composite parent_p) { final EnhancedComparisonTreeViewer result = doCreateViewerSynthesis(parent_p); result.getInnerViewer().addFilter(_filterUnchangedElements); result.getInnerViewer().addFilter(_filterMoveOrigins); // Update header when filtering is activated addPropertyChangeListener(new IPropertyChangeListener() { /** * @see org.eclipse.jface.util.IPropertyChangeListener#propertyChange(org.eclipse.jface.util.PropertyChangeEvent) */ public void propertyChange(PropertyChangeEvent event_p) { if (PROPERTY_FILTERING.equals(event_p.getProperty())) { Boolean filtered = (Boolean)event_p.getNewValue(); if (filtered != null) { StringBuilder builder = new StringBuilder(); builder.append(result.getDefaultHeaderText()); if (filtered.booleanValue()) builder.append(Messages.ComparisonViewer_Filtered); result.getTextLabel().setText(builder.toString()); } } } }); // User selection: send to global viewer result.addSWTSelectionListener(new SelectionAdapter() { /** * @see org.eclipse.swt.events.SelectionAdapter#widgetSelected(org.eclipse.swt.events.SelectionEvent) */ @Override public void widgetSelected(SelectionEvent event_p) { IStructuredSelection selection = result.getSelection(); if (!selection.isEmpty()) setSelection(new ComparisonSelectionImpl( selection.toList(), getDrivingRole()), true, result.getInnerViewer()); } }); // Global selection change: update local selection addSelectionChangedListener(new ISelectionChangedListener() { /** * @see org.eclipse.jface.viewers.ISelectionChangedListener#selectionChanged(org.eclipse.jface.viewers.SelectionChangedEvent) */ public void selectionChanged(SelectionChangedEvent event_p) { ISelection rawSelection = event_p.getSelection(); Object source = event_p.getSource(); if (rawSelection instanceof ComparisonSelection && source != result.getInnerViewer()) { ComparisonSelection selection = (ComparisonSelection)rawSelection; // New selection IStructuredSelection newSelection = StructuredSelection.EMPTY; int matchesSize = selection.getSelectedMatches().size(); if (matchesSize > 1) { newSelection = new StructuredSelection(selection.getSelectedMatches()); } else { IMatch match = selection.asMatch(); if (match != null) newSelection = new StructuredSelection(match); } result.setSelection(newSelection, true); } } }); // ... and enable context menus createViewerContextMenus(result, false); return result; } /** * Create, configure and return the viewer in the synthesis row for the given side * @param parent_p a non-null composite * @param isLeftSide_p whether the side is left or right * @return a non-null viewer */ protected EnhancedComparisonSideViewer createViewerSynthesisSide(Composite parent_p, final boolean isLeftSide_p) { final EnhancedComparisonSideViewer result = doCreateViewerSynthesisSide(parent_p, isLeftSide_p); // User selection: send to global viewer result.addSWTSelectionListener(new SelectionAdapter() { /** * @see org.eclipse.swt.events.SelectionListener#widgetSelected(org.eclipse.swt.events.SelectionEvent) */ @Override public void widgetSelected(SelectionEvent event_p) { if (_isLeftRightSynced) { IStructuredSelection selection = result.getSelection(); Role sideRole = getInput().getRoleForSide(isLeftSide_p); IStructuredSelection synthesisSelection = getSelectionAsSynthesis(selection, isLeftSide_p); if (!synthesisSelection.isEmpty()) setSelection(new ComparisonSelectionImpl( synthesisSelection.toList(), sideRole), true, result.getInnerViewer()); } } }); // Global selection change: update local selection addSelectionChangedListener(new ISelectionChangedListener() { /** * @see org.eclipse.jface.viewers.ISelectionChangedListener#selectionChanged(org.eclipse.jface.viewers.SelectionChangedEvent) */ public void selectionChanged(SelectionChangedEvent event_p) { ISelection rawSelection = event_p.getSelection(); Object source = event_p.getSource(); if (rawSelection instanceof ComparisonSelection && source != result.getInnerViewer() && (source != _viewerSynthesisMain || _isLeftRightSynced)) { ComparisonSelection selection = (ComparisonSelection)rawSelection; // New selection IStructuredSelection newSelection = StructuredSelection.EMPTY; int matchesSize = selection.getSelectedMatches().size(); if (matchesSize > 1) { newSelection = new StructuredSelection(selection.getSelectedMatches()); } else { IMatch match = selection.asMatch(); if (match != null) newSelection = new StructuredSelection(match); } result.setSelection(getSelectionAsSide(newSelection, isLeftSide_p), true); } } }); // Register as selection provider ... result.getInnerViewer().getControl().addFocusListener(new FocusListener() { /** * @see org.eclipse.swt.events.FocusListener#focusGained(org.eclipse.swt.events.FocusEvent) */ public void focusGained(FocusEvent e_p) { _multiViewerSelectionProvider.setSource(result.getInnerViewer()); } /** * @see org.eclipse.swt.events.FocusListener#focusLost(org.eclipse.swt.events.FocusEvent) */ public void focusLost(FocusEvent e_p) { _multiViewerSelectionProvider.setSource(ComparisonViewer.this); } }); // ... and enable context menus createViewerContextMenus(result, true); return result; } /** * Create, configure and return the values viewer for the given side * @param parent_p a non-null composite * @param isLeftSide_p whether the side is left or right * @return a non-null viewer */ protected EnhancedValuesViewer createViewerValues(Composite parent_p, final boolean isLeftSide_p) { final EnhancedValuesViewer result = doCreateViewerValues(parent_p, isLeftSide_p); // User selection: send to global viewer result.addSWTSelectionListener(new SelectionAdapter() { /** * @see org.eclipse.swt.events.SelectionListener#widgetSelected(org.eclipse.swt.events.SelectionEvent) */ @Override public void widgetSelected(SelectionEvent event_p) { IStructuredSelection selection = result.getSelection(); if (!selection.isEmpty()) { if (selection.getFirstElement() instanceof EObject) { // Skip attribute values setSelection(new ComparisonSelectionImpl( selection.toList(), getInput().getRoleForSide(isLeftSide_p)), true, result.getInnerViewer()); // One element selected: show it in scope viewer if (selection.size() == 1) { EObject selectedElement = (EObject)selection.getFirstElement(); IMatch match; if (selectedElement instanceof IMatch) { match = (IMatch)selectedElement; } else if (selectedElement instanceof IReferenceValuePresence) { IReferenceValuePresence rvp = (IReferenceValuePresence)selectedElement; boolean containment = rvp.getFeature() != null && rvp.getFeature().isContainment(); match = containment? rvp.getElementMatch(): rvp.getValueMatch(); } else { match = null; } if (match != null) getModelScopeViewer(isLeftSide_p).setSelection( new StructuredSelection(match.get(getInput().getRoleForSide(isLeftSide_p)))); } } } } }); // Global selection change: update local selection addSelectionChangedListener(new ISelectionChangedListener() { /** * @see org.eclipse.jface.viewers.ISelectionChangedListener#selectionChanged(org.eclipse.jface.viewers.SelectionChangedEvent) */ public void selectionChanged(SelectionChangedEvent event_p) { ISelection rawSelection = event_p.getSelection(); Object source = event_p.getSource(); if (rawSelection instanceof ComparisonSelection && source != result.getInnerViewer()) { ComparisonSelection selection = (ComparisonSelection)rawSelection; EStructuralFeature feature = selection.asFeature(); if (feature != null) { // New input MatchAndFeature mnf = new MatchAndFeatureImpl(selection.asMatch(), feature); ValuesInput newInput = new ValuesInput(getInput(), mnf); if (!newInput.equals(result.getInput())) result.setInput(newInput); // New selection List<EValuePresence> values = selection.getSelectedValuePresences(); result.setSelection(new StructuredSelection(values), true); } else { // No feature in selection // Determine whether there is a change of match, triggering a new input and selection ValuesInput newInput = null; if (selection.getSelectedMatches().size() <= 1 && getInput() != null) { // No more than one match EMatch newMatch = selection.asMatch(); if (newMatch != null) { // One match EMatch currentMatch = null; ValuesInput currentInput = result.getInput(); if (currentInput != null && currentInput.getMatchAndFeature() != null) currentMatch = currentInput.getMatchAndFeature().getMatch(); if (newMatch != currentMatch) { // New match is different from current match HeaderViewer<?> rawFeaturesViewer = getFeaturesViewer(); if (rawFeaturesViewer instanceof EnhancedFeaturesViewer) { EnhancedFeaturesViewer featuresViewer = (EnhancedFeaturesViewer)rawFeaturesViewer; FeaturesInput featuresInput = new FeaturesInput(getInput(), newMatch); EStructuralFeature firstFeature = featuresViewer.getInnerViewer().getFirstIn(featuresInput); if (firstFeature != null) { // First feature must be selected newInput = new ValuesInput(getInput(), new MatchAndFeatureImpl(newMatch, firstFeature)); } } } else { // Same match and no feature newInput = currentInput; } } } result.setInput(newInput); } } } }); return result; } /** * Create and return a features viewer * @param parent_p a non-null composite * @return a non-null viewer */ protected EnhancedFeaturesViewer doCreateViewerFeatures(Composite parent_p) { return new EnhancedFeaturesViewer(parent_p); } /** * Create and return the main viewer of the synthesis row * @param parent_p a non-null composite * @return a non-null viewer */ protected EnhancedComparisonTreeViewer doCreateViewerSynthesis(Composite parent_p) { return new EnhancedComparisonTreeViewer(parent_p); } /** * Create and return a viewer in the synthesis row for the given side * @param parent_p a non-null composite * @param isLeftSide_p whether the side is left or right * @return a non-null viewer */ protected EnhancedComparisonSideViewer doCreateViewerSynthesisSide( Composite parent_p, boolean isLeftSide_p) { return new EnhancedComparisonSideViewer(parent_p, isLeftSide_p); } /** * Create a values viewer for the given side * @param parent_p a non-null composite * @param isLeftSide_p whether the side is left or right * @return a non-null viewer */ protected EnhancedValuesViewer doCreateViewerValues(Composite parent_p, boolean isLeftSide_p) { return new EnhancedValuesViewer(parent_p, isLeftSide_p); } /** * Return the default respective weights of the columns (sashes) of the GUI * @return an int array whose size is equal to the number of columns */ protected int[] getDefaultColumnWeights() { return new int[] {3, 2, 2}; } /** * Return the default respective weights of the rows (sashes) of the GUI * @return an int array whose size is equal to the number of rows */ protected int[] getDefaultRowWeights() { return new int[] {5, 2}; } /** * Return the differences to merge from a given list of selected matches and the given * criteria * @param selectedMatches_p a non-null list * @param coverChildren_p whether children of the matches must be covered * @param incrementalMode_p whether optional deletions must be skipped * @return a non-null, potentially empty, unmodifiable list */ protected List<IDifference> getDifferencesToMerge(final List<EMatch> selectedMatches_p, final Role destination_p, final boolean coverChildren_p, final boolean incrementalMode_p) { final List<IDifference> result = new ArrayList<IDifference>(); IProgressService progress = PlatformUI.getWorkbench().getProgressService(); try { progress.busyCursorWhile(new IRunnableWithProgress() { /** * @see org.eclipse.jface.operation.IRunnableWithProgress#run(IProgressMonitor) */ public void run(final IProgressMonitor monitor_p) throws InvocationTargetException, InterruptedException { for (EMatch selectedMatch : selectedMatches_p) { if (coverChildren_p) addDifferencesToMergeRec(result, selectedMatch, destination_p, incrementalMode_p); else addDifferencesToMerge(result, selectedMatch, destination_p, incrementalMode_p); } } }); } catch (Exception e) { // Proceed } return Collections.unmodifiableList(result); } /** * Return the driving role for this viewer * @return a role which is assumed non-null after setInput(Object) has been invoked */ public Role getDrivingRole() { return getInput() == null? null: getInput().getDrivingRole(); } /** * Return the inner viewer for the features of elements with differences * @return a viewer which is non-null if this viewer has been properly initialized */ public HeaderViewer<?> getFeaturesViewer() { return _viewerFeatures; } /** * Return the set of inner viewers of this viewer * @return a non-null collection */ protected Collection<Viewer> getInnerViewers() { return Arrays.<Viewer>asList( _viewerSynthesisMain.getInnerViewer(), _viewerSynthesisLeft.getInnerViewer(), _viewerSynthesisRight.getInnerViewer(), _viewerFeatures.getInnerViewer(), _viewerValuesLeft.getInnerViewer(), _viewerValuesRight.getInnerViewer()); } /** * Return the logger for diff/merge events * @return a non-null logger */ protected Logger getLogger() { return EMFDiffMergeUIPlugin.getDefault().getDiffMergeLogger(); } /** * Return the inner viewer for the model from the given side * @param left_p whether the side is left or right * @return a viewer which is non-null if this viewer has been properly initialized */ public EnhancedComparisonSideViewer getModelScopeViewer(boolean left_p) { EnhancedComparisonSideViewer result = left_p? _viewerSynthesisLeft: _viewerSynthesisRight; return result; } /** * @see org.eclipse.emf.diffmerge.ui.viewers.AbstractComparisonViewer#getMultiViewerSelectionProvider() */ @Override public ISelectionProvider getMultiViewerSelectionProvider() { return _multiViewerSelectionProvider; } /** * Return the set of selected matches from the given selection in the * purpose of performing actions on differences * @param selection_p a non-null selection * @return a non-null, potentially empty list */ protected List<EMatch> getSelectedMatchesForInteractions( final ComparisonSelection selection_p) { List<EMatch> selectedMatches = selection_p.getSelectedMatches(); if (selectedMatches.isEmpty()) { List<EMatch> treePath = selection_p.getSelectedTreePath(); if (!treePath.isEmpty()) selectedMatches = Collections.singletonList(treePath.get(treePath.size()-1)); } return selectedMatches; } /** * @see org.eclipse.jface.viewers.Viewer#getSelection() */ @Override public ComparisonSelection getSelection() { return _lastUserSelection; } /** * Return a variant of the given match-based selection for the given side. * Matches are converted to their elements on the given side. * Elements other than matches are ignored. * @param selection_p a non-null selection * @param onLeft_p whether the desired side is left or right * @return a non-null, potentially empty selection */ protected IStructuredSelection getSelectionAsSide( IStructuredSelection selection_p, boolean onLeft_p) { List<EObject> result = new FArrayList<EObject>(); if (getInput() != null) { Role role = getInput().getRoleForSide(onLeft_p); for (Object selected : selection_p.toArray()) { if (selected instanceof IMatch) { EObject element = ((IMatch)selected).get(role); if (element != null) result.add(element); } } } return new StructuredSelection(result); } /** * Return a variant of the given element-based selection as a * match-based selection. Elements from the given side are converted to * their corresponding matches. Other elements are ignored. * @param selection_p a non-null selection * @param onLeft_p whether the original side is left or right * @return a non-null, potentially empty selection */ protected IStructuredSelection getSelectionAsSynthesis( IStructuredSelection selection_p, boolean onLeft_p) { List<IMatch> result = new FArrayList<IMatch>(); EMFDiffNode input = getInput(); if (input != null) { IComparison comparison = input.getActualComparison(); if (comparison != null) { Role role = input.getRoleForSide(onLeft_p); for (Object selected : selection_p.toArray()) { if (selected instanceof EObject) { IMatch match = comparison.getMapping().getMatchFor( (EObject)selected, role); if (match != null) result.add(match); } } } } return new StructuredSelection(result); } /** * Return the inner viewer for the comparison tree * @return a viewer which is non-null if this viewer has been properly initialized */ public EnhancedComparisonTreeViewer getSynthesisViewer() { return _viewerSynthesisMain; } /** * Return the inner viewer for differences on values for the given side * @param left_p whether the side is left or right * @return a viewer which is non-null if this viewer has been properly initialized */ public EnhancedValuesViewer getValuesViewer(boolean left_p) { EnhancedValuesViewer result = left_p? _viewerValuesLeft: _viewerValuesRight; return result; } /** * @see org.eclipse.emf.diffmerge.ui.viewers.AbstractComparisonViewer#handleDispose() */ @Override protected void handleDispose() { super.handleDispose(); _lastUserSelection = null; _viewerSynthesisLeft = null; _viewerSynthesisRight = null; _viewerValuesLeft = null; _viewerValuesRight = null; _viewerSynthesisMain = null; _viewerFeatures = null; _sorterSynthesis = null; _filterUnchangedElements = null; _filterMoveOrigins = null; } /** * Ignore the current selection * @param onLeft_p whether ignore occurs on the left */ protected void ignore(boolean onLeft_p) { final ComparisonSelection selection = getSelection(); if (selection == null) return; // Should not happen according to ignore tool activation EMFDiffNode input = getInput(); List<EMatch> selectedMatches = getSelectedMatchesForInteractions(selection); // Make choices IgnoreChoiceData choices = new IgnoreChoiceData( input.isDefaultCoverChildren(), false); makeIgnoreChoices(choices, input, selectedMatches); if (!choices.isProceed()) return; // Ignore operation is set to proceed and choices have been made final Collection<IDifference> toIgnore = !selectedMatches.isEmpty()? getDifferencesToMerge( selectedMatches, input.getRoleForSide(onLeft_p), choices.isCoverChildren(), choices.isSideExclusive()): getInput().getCategoryManager().getPendingDifferencesFiltered(selection.asDifferencesToMerge()); if (!toIgnore.isEmpty()) { executeOnModel(new Runnable() { /** * @see java.lang.Runnable#run() */ public void run() { for (IDifference diff : toIgnore) { if (diff instanceof EElementRelativePresence) { EElementRelativePresence presence = (EElementRelativePresence)diff; getUIComparison().getDifferencesToIgnore().add(presence); // Also on symmetrical if any if (diff instanceof EValuePresence) { IValuePresence symmetrical = ((EValuePresence)diff).getSymmetrical(); if (symmetrical instanceof EMergeableDifference) getUIComparison().getDifferencesToIgnore().add( (EMergeableDifference)symmetrical); // Also on symmetrical ownership if any if (diff instanceof IReferenceValuePresence) { IReferenceValuePresence symmetricalOwnership = ((IReferenceValuePresence)diff).getSymmetricalOwnership(); if (symmetricalOwnership instanceof EMergeableDifference) getUIComparison().getDifferencesToIgnore().add( (EMergeableDifference)symmetricalOwnership); } } } } getUIComparison().setLastActionSelection(selection); } }, onLeft_p); firePropertyChangeEvent(CompareEditorInput.DIRTY_STATE, new Boolean(true)); input.updateDifferenceNumbers(); } } /** * Initialize the non-graphical instance variables */ protected void initialize() { _isLeftRightSynced = true; _lastUserSelection = null; _multiViewerSelectionProvider = new SelectionBridge.SingleSource(); _multiViewerSelectionProvider.setSource(this); _sorterSynthesis = new ViewerSorter(); _filterUnchangedElements = new ViewerFilter() { /** * @see org.eclipse.jface.viewers.ViewerFilter#select(org.eclipse.jface.viewers.Viewer, java.lang.Object, java.lang.Object) */ @Override public boolean select(Viewer viewer_p, Object parentElement_p, Object element_p) { EMatch match = (EMatch)element_p; return getInput().getCategoryManager().getDifferenceNumber(match) > 0; } }; _filterMoveOrigins = new ViewerFilter() { /** * @see org.eclipse.jface.viewers.ViewerFilter#select(org.eclipse.jface.viewers.Viewer, java.lang.Object, java.lang.Object) */ @Override public boolean select(Viewer viewer_p, Object parentElement_p, Object element_p) { TreePath path; if (parentElement_p instanceof TreePath) path = ((TreePath)parentElement_p).createChildPath(element_p); else path = new TreePath(new Object[] {element_p}); return !getInput().getCategoryManager().isMoveOrigin(path); } }; } /** * Return whether user interactions must occur to determine how to execute * the ignore operation on the given list of matches in the context of the * given input and the given specification of choices * @param choices_p the non-null specification of the ignore choices * @param input_p a non-null input * @param selectedMatches a non-null, potentially empty list */ protected boolean interactionsRequiredForIgnore(IgnoreChoiceData choices_p, EMFDiffNode input_p, List<EMatch> selectedMatches) { boolean childrenForMerge = false; boolean ownDifferences = false; // Determining whether selected matches have proper differences // and differences in children for (EMatch selectedMatch : selectedMatches) { ownDifferences = ownDifferences || !input_p.getCategoryManager().getDifferenceKind(selectedMatch).isNeutral(); if (childrenForMerge && ownDifferences) break; childrenForMerge = childrenForMerge || input_p.getCategoryManager().hasChildrenForMergeFiltered(selectedMatch); if (childrenForMerge && ownDifferences) break; } if (!ownDifferences && childrenForMerge) // No own difference but differences in children: operation only // makes sense if children are covered choices_p.setCoverChildren(true); return ownDifferences && childrenForMerge; } /** * Return whether user interactions must occur to determine how to execute * the merge operation on the given list of matches in the context of the * given input and the given specification of choices * @param choices_p the non-null specification of the merge choices * @param input_p a non-null input * @param selectedMatches a non-null, potentially empty list */ protected boolean interactionsRequiredForMerge(MergeChoiceData choices_p, EMFDiffNode input_p, List<EMatch> selectedMatches) { boolean result = !selectedMatches.isEmpty(); if (result && selectedMatches.size() == 1) { EMatch selectedMatch = selectedMatches.get(0); if (!input_p.getCategoryManager().hasChildrenForMergeFiltered(selectedMatch)) { DifferenceKind kind = input_p.getCategoryManager().getDifferenceKind(selectedMatch); result = !(kind.isAddition() || kind.isDeletion()); } } return result; } /** * @see org.eclipse.emf.diffmerge.ui.viewers.AbstractComparisonViewer#inputChanged(java.lang.Object, java.lang.Object) */ @Override protected void inputChanged(Object input_p, Object oldInput_p) { super.inputChanged(input_p, oldInput_p); _viewerFeatures.setInput(null); _viewerValuesLeft.setInput(null); _viewerValuesRight.setInput(null); _viewerSynthesisMain.setInput(input_p); _viewerSynthesisLeft.setInput(input_p); _viewerSynthesisRight.setInput(input_p); if (getInput() != null && getInput().isLogEvents()) getLogger().log(new CompareLogEvent(getEditingDomain(), getComparison())); } /** * Set the given specification of ignore choices according to the given * input and set of selected matches * @param choices_p the non-null specification of the ignore choices * @param input_p a non-null diff node input * @param selectedMatches_p the non-null, potentially empty set of matches that have * been selected for merge */ protected void makeIgnoreChoices(IgnoreChoiceData choices_p, EMFDiffNode input_p, List<EMatch> selectedMatches_p) { boolean requiresInteractions = interactionsRequiredForIgnore(choices_p, input_p, selectedMatches_p); if (requiresInteractions) { IgnoreChoicesDialog choicesDialog = new IgnoreChoicesDialog(getShell(), Messages.ComparisonViewer_IgnoreCommandName, choices_p); choicesDialog.open(); if (choices_p.isProceed()) getInput().setDefaultCoverChildren(choices_p.isCoverChildren()); } } /** * Set the given specification of merge choices according to the given * input and set of selected matches * @param choices_p the non-null specification of the merge choices * @param input_p a non-null diff node input * @param selectedMatches_p the non-null, potentially empty set of matches that have * been selected for merge * @param acceptIncrementalMode_p whether the incremental mode is acceptable in this context */ protected void makeMergeChoices(MergeChoiceData choices_p, EMFDiffNode input_p, List<EMatch> selectedMatches_p, boolean acceptIncrementalMode_p) { boolean requiresInteractions = interactionsRequiredForMerge( choices_p, input_p, selectedMatches_p); if (requiresInteractions) { // Group of differences boolean mayAskAboutChildren = false; for (EMatch selectedMatch : selectedMatches_p) { if (input_p.getCategoryManager().getDifferenceKind(selectedMatch) == DifferenceKind.COUNTED) { choices_p.setCoverChildren(true); break; } else if (input_p.getCategoryManager().hasChildrenForMergeFiltered(selectedMatch)) { mayAskAboutChildren = true; break; } } // Choice dialog MergeChoicesDialog choicesDialog = new MergeChoicesDialog(getShell(), Messages.ComparisonViewer_MergeHeader, choices_p, mayAskAboutChildren, acceptIncrementalMode_p); choicesDialog.open(); if (choices_p.isProceed()) { if (mayAskAboutChildren) input_p.setDefaultCoverChildren(choices_p.isCoverChildren()); input_p.setDefaultIncrementalMode(choices_p.isIncrementalMode()); input_p.setDefaultShowImpact(choices_p.isShowImpact()); } } } /** * Merge the current selection to the given side * @param toLeft_p whether destination is left or right * @param acceptIncrementalMode_p whether the incremental mode is acceptable in this context */ protected void merge(boolean toLeft_p, boolean acceptIncrementalMode_p) { merge(toLeft_p, acceptIncrementalMode_p, getSelection()); } /** * Merge the given selection to the given side * @param toLeft_p whether destination is left or right * @param acceptIncrementalMode_p whether the incremental mode is acceptable in this context * @param selection_p the potentially null selection (e.g., set of matches) to merge */ protected void merge(final boolean toLeft_p, boolean acceptIncrementalMode_p, final ComparisonSelection selection_p) { if (selection_p == null) return; // Should not happen according to merge tool activation final EMFDiffNode input = getInput(); // Define the set of selected matches List<EMatch> selectedMatches = getSelectedMatchesForInteractions(selection_p); // Make choices MergeChoiceData choices = new MergeChoiceData(input.isDefaultCoverChildren(), input.isDefaultIncrementalMode() && acceptIncrementalMode_p, input.isDefaultShowImpact()); makeMergeChoices(choices, input, selectedMatches, acceptIncrementalMode_p); if (!choices.isProceed()) return; // Merge is set to proceed and choices have been made final Role destination = input.getRoleForSide(toLeft_p); final Collection<IDifference> toMerge = !selectedMatches.isEmpty()? getDifferencesToMerge( selectedMatches, destination, choices.isCoverChildren(), choices.isIncrementalMode()): input.getCategoryManager().getPendingDifferencesFiltered(selection_p.asDifferencesToMerge()); final Collection<IDifference> merged = new ArrayList<IDifference>(); boolean done = false; if (!toMerge.isEmpty()) { // Merge is possible boolean proceed = true; if (choices.isShowImpact()) { // Show merge impact proceed = showMergeImpact(toMerge, toLeft_p, input); } if (proceed) { // Merge is confirmed executeOnModel(new IRunnableWithProgress() { /** * @see org.eclipse.jface.operation.IRunnableWithProgress#run(org.eclipse.core.runtime.IProgressMonitor) */ public void run(IProgressMonitor monitor_p) throws InvocationTargetException, InterruptedException { merged.addAll(getComparison().merge(toMerge, destination, true, monitor_p)); getUIComparison().setLastActionSelection(selection_p); } }, toLeft_p); done = true; } } else { // Nothing to merge MessageDialog.openInformation(getShell(), Messages.ComparisonViewer_MergeHeader, Messages.ComparisonViewer_NoDiffsToMerge); } if (!merged.isEmpty() && done) { // React to merge input.setModified(true, toLeft_p); firePropertyChangeEvent(CompareEditorInput.DIRTY_STATE, new Boolean(true)); input.updateDifferenceNumbers(); if (input.isLogEvents()) getLogger().log( new MergeLogEvent(getEditingDomain(), getComparison(), merged, toLeft_p)); } } /** * Navigate to the next/previous difference according to the given flag * @param next_p whether navigation must be forward or back * @return whether the operation could not be completed due to the * last/first difference being reached */ protected boolean navigate(boolean next_p) { ComparisonTreeViewer treeViewer = _viewerSynthesisMain.getInnerViewer(); ITreeSelection selection = treeViewer.getSelection(); TreePath current = (selection == null || selection.isEmpty())? TreePath.EMPTY: selection.getPaths()[0]; TreePath newPath = next_p? treeViewer.getNextUserDifference(current): treeViewer.getPreviousUserDifference(current); if (newPath != null) setSelection(new ComparisonSelectionImpl(newPath, getDrivingRole()), true); return newPath == null; } /** * Define the diff/merge-specific content of the contextual menu for * the given viewer and associated (possibly the same) selection provider * @param menuManager_p a non-null menu manager * @param viewer_p a non-null viewer * @param selectionProvider_p a non-null selection provider */ protected void populateContextMenu(MenuManager menuManager_p, Viewer viewer_p, ISelectionProvider selectionProvider_p) { // Nothing by default } /** * @see org.eclipse.jface.viewers.Viewer#refresh() */ @Override public void refresh() { _viewerSynthesisLeft.refresh(); _viewerSynthesisRight.refresh(); _viewerValuesLeft.refresh(); _viewerValuesRight.refresh(); _viewerFeatures.refresh(); _viewerSynthesisMain.refresh(); super.refresh(); } /** * Refresh the tools of the viewer. This behavior is centralized instead of being * delegated to each tool via listeners in order to improve performance. */ @Override protected void refreshTools() { ComparisonSelection selection = getSelection(); EMFDiffNode input = getInput(); // Merge boolean onLeft = false, onRight = false; boolean allowDeletion = false; boolean allowIgnoring = false; if (selection != null && input != null) { allowIgnoring = true; IValuePresence presence = selection.asValuePresence(); if (presence != null && !presence.isMerged()) { // Value presence DifferenceKind kind = input.getCategoryManager().getDifferenceKind(presence); onLeft = canAddToTheRight(kind); onRight = canAddToTheLeft(kind); allowDeletion = input.getCategoryManager().isMany(presence) && !input.getCategoryManager().isOwnership(presence); } else if (selection.asFeature() == null) { List<EMatch> matches = selection.asMatches(); if (!matches.isEmpty()) { // Matches selected if (matches.size() > 1) { // Several matches selected allowDeletion = true; Iterator<EMatch> it = matches.iterator(); while (it.hasNext() && (!onLeft || !onRight || allowDeletion)) { EMatch current = it.next(); DifferenceKind kind = input.getCategoryManager().getDifferenceKind(current); if (kind.isAddition()) { onLeft = onLeft || kind.isLeft(true); onRight = onRight || kind.isRight(true); } else { onLeft = true; onRight = true; allowDeletion = false; } } allowDeletion = allowDeletion && (onLeft != onRight); } else { // Only one match selected IMatch match = matches.get(0); if (input.getCategoryManager().representAsModification(match) || input.getCategoryManager().representAsMove(match) || input.getCategoryManager().getDifferenceKind(match) == DifferenceKind.COUNTED) { // Modification or move or inner differences onLeft = true; onRight = true; allowDeletion = false; } else { // Partial match DifferenceKind kind = input.getCategoryManager().getDifferenceKind(match); onLeft = canAddToTheRight(kind); onRight = canAddToTheLeft(kind); allowDeletion = true; } } } } } if (input != null) { firePropertyChangeEvent( PROPERTY_ACTIVATION_MERGE_TO_RIGHT, new Boolean(input.isEditable(false) && onLeft)); firePropertyChangeEvent( PROPERTY_ACTIVATION_DELETE_LEFT, new Boolean(input.isEditable(true) && onLeft && allowDeletion)); firePropertyChangeEvent( PROPERTY_ACTIVATION_MERGE_TO_LEFT, new Boolean(input.isEditable(true) && onRight)); firePropertyChangeEvent( PROPERTY_ACTIVATION_DELETE_RIGHT, new Boolean(input.isEditable(false) && onRight && allowDeletion)); } firePropertyChangeEvent( PROPERTY_ACTIVATION_IGNORE_LEFT, new Boolean(onLeft && allowIgnoring)); firePropertyChangeEvent( PROPERTY_ACTIVATION_IGNORE_RIGHT, new Boolean(onRight && allowIgnoring)); super.refreshTools(); } /** * Set the "base" label provider for representing model elements * @param labelProvider_p a potentially null label provider, where null stands for default */ public void setDelegateLabelProvider(ILabelProvider labelProvider_p) { for (Viewer viewer : getInnerViewers()) { if (viewer instanceof ContentViewer) { IBaseLabelProvider rawLP = ((ContentViewer)viewer).getLabelProvider(); if (rawLP instanceof DelegatingLabelProvider) { DelegatingLabelProvider delegatingLP = (DelegatingLabelProvider)rawLP; delegatingLP.setDelegate(labelProvider_p); } } } } /** * @see org.eclipse.jface.viewers.Viewer#setSelection(org.eclipse.jface.viewers.ISelection, boolean) */ @Override public void setSelection(ISelection selection_p, boolean reveal_p) { setSelection(selection_p, reveal_p, this); } /** * Set the selection of this viewer, indicating the source of this setting * @see Viewer#setSelection(ISelection, boolean) * @param selection_p a potentially null selection * @param reveal_p whether the selected elements must be revealed * @param source_p the potentially null source of the setting */ protected void setSelection(ISelection selection_p, boolean reveal_p, Viewer source_p) { ComparisonSelection newSelection; if (selection_p instanceof ComparisonSelection && (source_p == this || getInnerViewers().contains(source_p))) newSelection = (ComparisonSelection)selection_p; // Local selection else if (selection_p instanceof IStructuredSelection) newSelection = asComparisonSelection((IStructuredSelection)selection_p); // External selection else newSelection = new ComparisonSelectionImpl(null, null); // Invalid selection _lastUserSelection = newSelection; fireSelectionChanged(new SelectionChangedEvent(source_p, getSelection())); } /** * Configure the columns (sashes) of the UI in terms of weights and synchronization * @param upperRow_p the sash form on the up side * @param lowerRow_p the sash form on the down side */ protected void setupColumns(final SashForm upperRow_p, final SashForm lowerRow_p) { final int[] horizontalWeights = getDefaultColumnWeights(); upperRow_p.setWeights(horizontalWeights); lowerRow_p.setWeights(horizontalWeights); // Synchronize lower row on upper row when the latter is subject to resize Control upperMiddleControl = upperRow_p.getChildren()[1]; upperMiddleControl.addControlListener(new ControlListener() { /** * @see org.eclipse.swt.events.ControlListener#controlResized(org.eclipse.swt.events.ControlEvent) */ public void controlResized(ControlEvent e_p) { int[] weights = upperRow_p.getWeights(); lowerRow_p.setWeights(weights); } /** * @see org.eclipse.swt.events.ControlListener#controlMoved(org.eclipse.swt.events.ControlEvent) */ public void controlMoved(ControlEvent e_p) { // Nothing } }); // Synchronize upper row on lower row when the latter is subject to resize Control lowerMiddleControl = lowerRow_p.getChildren()[1]; lowerMiddleControl.addControlListener(new ControlListener() { /** * @see org.eclipse.swt.events.ControlListener#controlResized(org.eclipse.swt.events.ControlEvent) */ public void controlResized(ControlEvent e_p) { int[] weights = lowerRow_p.getWeights(); upperRow_p.setWeights(weights); } /** * @see org.eclipse.swt.events.ControlListener#controlMoved(org.eclipse.swt.events.ControlEvent) */ public void controlMoved(ControlEvent e_p) { // Nothing } }); } /** * Create and return the menu related to the detailed representation of differences * in the given tool bar * @param toolbar_p a non-null tool bar * @return a potentially null menu */ protected Menu setupMenuDetails(ToolBar toolbar_p) { new ToolItem(toolbar_p, SWT.SEPARATOR); Menu result = UIUtil.createMenuTool(_viewerFeatures.getToolbar()); // Only differences createItemShowDiffValues(result); // All values createItemShowAllValues(result); // All values and features createItemShowAllFeatures(result); // Technical representation new MenuItem(result, SWT.SEPARATOR); createItemUseTechnicalRepresentation(result); return result; } /** * Create and return the synthesis menu in the given tool bar * @param toolbar_p a non-null tool bar * @return a potentially null menu */ protected Menu setupMenuSynthesis(ToolBar toolbar_p) { Menu synthesisMenu = UIUtil.createMenuTool(toolbar_p); // Show all elements in synthesis createItemShowUncounted(synthesisMenu); // Common presentation features new MenuItem(synthesisMenu, SWT.SEPARATOR); createItemSync(synthesisMenu); createItemSort(synthesisMenu); // UI options new MenuItem(synthesisMenu, SWT.SEPARATOR); setupMenuSynthesisMisc(synthesisMenu); return synthesisMenu; } /** * Fill the menu of the synthesis viewer with miscellaneous features * @param synthesisMenu_p a non-null menu */ protected void setupMenuSynthesisMisc(Menu synthesisMenu_p) { createItemUseCustomIcons(synthesisMenu_p); createItemShowDifferenceNumbers(synthesisMenu_p); createItemShowImpact(synthesisMenu_p); new MenuItem(synthesisMenu_p, SWT.SEPARATOR); createItemSupportUndoRedo(synthesisMenu_p); createItemLogEvents(synthesisMenu_p); } /** * Set up the different tool bars */ protected void setupToolBars() { // Tools: upper row setupToolsSynthesis(_viewerSynthesisMain.getToolbar()); setupToolsSynthesisSide(_viewerSynthesisLeft.getToolbar(), true); setupToolsSynthesisSide(_viewerSynthesisRight.getToolbar(), false); // Tools: lower row setupToolsDetails(_viewerFeatures.getToolbar()); setupToolsDetailsSide(_viewerValuesLeft.getToolbar(), true); setupToolsDetailsSide(_viewerValuesRight.getToolbar(), false); // Menus setupMenuSynthesis(_viewerSynthesisMain.getToolbar()); setupMenuDetails(_viewerFeatures.getToolbar()); // Tool refresh on selection change addSelectionChangedListener(new ISelectionChangedListener() { /** * @see org.eclipse.jface.viewers.ISelectionChangedListener#selectionChanged(org.eclipse.jface.viewers.SelectionChangedEvent) */ public void selectionChanged(SelectionChangedEvent event_p) { refreshTools(); } }); refreshTools(); } /** * Set up the navigation tools in the given tool bar */ protected void setupToolsDetails(ToolBar toolbar_p) { // Nothing by default } /** * Set up the tools related to the "details" row in the given tool bar * for the given side * @param toolbar_p a non-null tool bar * @param onLeft_p whether the side is left or right */ protected void setupToolsDetailsSide(ToolBar toolbar_p, boolean onLeft_p) { createItemMerge(toolbar_p, !onLeft_p); createItemIgnore(toolbar_p, onLeft_p); createItemDelete(toolbar_p, onLeft_p); } /** * Set up the "synthesis" tools in the given tool bar * @param toolbar_p a non-null tool bar */ protected void setupToolsSynthesis(ToolBar toolbar_p) { new ToolItem(toolbar_p, SWT.SEPARATOR); createItemInconsistency(toolbar_p); // Next / Previous new ToolItem(toolbar_p, SWT.SEPARATOR); createItemNavigationNext(toolbar_p); createItemNavigationPrevious(toolbar_p); // Expand / Collapse new ToolItem(toolbar_p, SWT.SEPARATOR); createItemExpand(toolbar_p); createItemCollapse(toolbar_p); // Filters and sync new ToolItem(toolbar_p, SWT.SEPARATOR); createItemFilter(toolbar_p); } /** * Set up the tools related to the "synthesis" row in the given tool bar * for the given side * @param toolbar_p a non-null tool bar * @param onLeft_p whether the side is left or right */ protected void setupToolsSynthesisSide(ToolBar toolbar_p, boolean onLeft_p) { createItemLock(toolbar_p, onLeft_p); } /** * Show a UI representing the merge impact and return whether merge is confirmed * @param toMerge_p the non-null collection of differences to merge * @param toLeft_p whether the destination is the left-hand side * @param input_p a non-null object * @return whether to proceed with merge */ protected boolean showMergeImpact(final Collection<IDifference> toMerge_p, final boolean toLeft_p, final EMFDiffNode input_p) { boolean result = true; final ImpactInput mergeInput = new ImpactInput(toMerge_p, toLeft_p, input_p); IProgressService progress = PlatformUI.getWorkbench().getProgressService(); try { progress.busyCursorWhile(new IRunnableWithProgress() { /** * @see org.eclipse.jface.operation.IRunnableWithProgress#run(IProgressMonitor) */ public void run(final IProgressMonitor monitor_p) throws InvocationTargetException, InterruptedException { mergeInput.compute(monitor_p); } }); MergeImpactMessageDialog dialog = new MergeImpactMessageDialog( getShell(), mergeInput, getResourceManager(), _viewerSynthesisMain.getInnerViewer().getLabelProvider()); result = dialog.openAndConfirm(); } catch (Exception exception_p) { // Proceed } return result; } /** * @see org.eclipse.emf.diffmerge.ui.viewers.AbstractComparisonViewer#undoRedoPerformed(boolean) */ @Override protected void undoRedoPerformed(final boolean undo_p) { super.undoRedoPerformed(undo_p); if (getInput() != null) getInput().updateDifferenceNumbers(); } }