Skip to main content
summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPhilip Langer2017-11-14 10:51:13 +0000
committerLaurent Goubet2018-05-01 09:57:07 +0000
commit141a471032a46187f92b774b3159a215f089eb41 (patch)
tree0b613a8bc51977e25ff7a12a762a2c045eb24ffd
parentd533a40c7c1aa0a4c11fee5d1fd8999f56792c75 (diff)
downloadorg.eclipse.emf.compare-141a471032a46187f92b774b3159a215f089eb41.tar.gz
org.eclipse.emf.compare-141a471032a46187f92b774b3159a215f089eb41.tar.xz
org.eclipse.emf.compare-141a471032a46187f92b774b3159a215f089eb41.zip
Support key bindings for commonly used merge/navigate actions
The base compare framework already defines commands for what we need, but no key bindings. We can reuse those so that the same bindings work everywhere without being defined multiple times. We'd like to register them in the plugin.xml but using natural key bindings such as ALT+UP_ARRAY caused logged conflict warnings. We avoided this by dynamically creating the bindings on demand. Change-Id: Id52e19a841b1789ea54156d0d466785ffabd84e7 Signed-off-by: Philip Langer <planger@eclipsesource.com>
-rw-r--r--plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/structuremergeviewer/CompareToolBar.java217
-rw-r--r--plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/structuremergeviewer/EMFCompareStructureMergeViewer.java2
-rw-r--r--plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/structuremergeviewer/actions/SelectDiffAction.java3
3 files changed, 216 insertions, 6 deletions
diff --git a/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/structuremergeviewer/CompareToolBar.java b/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/structuremergeviewer/CompareToolBar.java
index ef7af9a7d..a247a868e 100644
--- a/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/structuremergeviewer/CompareToolBar.java
+++ b/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/structuremergeviewer/CompareToolBar.java
@@ -16,13 +16,23 @@ package org.eclipse.emf.compare.ide.ui.internal.structuremergeviewer;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.collect.Lists.newArrayListWithCapacity;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
import com.google.common.eventbus.Subscribe;
+import java.io.IOException;
import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
import java.util.EnumSet;
import java.util.List;
+import java.util.Map;
+import org.eclipse.compare.CompareViewerPane;
import org.eclipse.compare.INavigatable;
+import org.eclipse.core.commands.Command;
+import org.eclipse.core.commands.ParameterizedCommand;
+import org.eclipse.emf.compare.ide.ui.internal.EMFCompareIDEUIPlugin;
import org.eclipse.emf.compare.ide.ui.internal.configuration.EMFCompareConfiguration;
import org.eclipse.emf.compare.ide.ui.internal.contentmergeviewer.MirrorUtil;
import org.eclipse.emf.compare.ide.ui.internal.structuremergeviewer.actions.CollapseAllModelAction;
@@ -32,6 +42,7 @@ import org.eclipse.emf.compare.ide.ui.internal.structuremergeviewer.actions.Merg
import org.eclipse.emf.compare.ide.ui.internal.structuremergeviewer.actions.MergeAllNonConflictingAction;
import org.eclipse.emf.compare.ide.ui.internal.structuremergeviewer.actions.SaveComparisonModelAction;
import org.eclipse.emf.compare.ide.ui.internal.structuremergeviewer.actions.SelectDiffAction;
+import org.eclipse.emf.compare.ide.ui.internal.util.CompareHandlerService;
import org.eclipse.emf.compare.internal.merge.MergeMode;
import org.eclipse.emf.compare.merge.IMerger;
import org.eclipse.emf.compare.rcp.EMFCompareRCPPlugin;
@@ -45,9 +56,18 @@ import org.eclipse.emf.compare.rcp.ui.internal.structuremergeviewer.actions.Grou
import org.eclipse.emf.compare.rcp.ui.internal.structuremergeviewer.filters.StructureMergeViewerFilter;
import org.eclipse.emf.compare.rcp.ui.internal.structuremergeviewer.groups.StructureMergeViewerGrouper;
import org.eclipse.emf.compare.rcp.ui.internal.util.SWTUtil;
+import org.eclipse.jface.action.Action;
+import org.eclipse.jface.action.ActionContributionItem;
+import org.eclipse.jface.action.IAction;
+import org.eclipse.jface.action.IContributionItem;
import org.eclipse.jface.action.IMenuCreator;
import org.eclipse.jface.action.Separator;
import org.eclipse.jface.action.ToolBarManager;
+import org.eclipse.jface.bindings.Binding;
+import org.eclipse.jface.bindings.TriggerSequence;
+import org.eclipse.jface.bindings.keys.KeyBinding;
+import org.eclipse.jface.bindings.keys.KeySequence;
+import org.eclipse.jface.bindings.keys.ParseException;
import org.eclipse.jface.util.IPropertyChangeListener;
import org.eclipse.jface.util.PropertyChangeEvent;
import org.eclipse.jface.viewers.AbstractTreeViewer;
@@ -55,16 +75,36 @@ import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.swt.events.DisposeEvent;
import org.eclipse.swt.events.DisposeListener;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.ToolBar;
import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.commands.ICommandService;
+import org.eclipse.ui.keys.IBindingService;
import org.eclipse.ui.menus.IMenuService;
-import org.eclipse.ui.services.IServiceLocator;
/**
* @author <a href="mailto:mikael.barbero@obeo.fr">Mikael Barbero</a>
*/
public class CompareToolBar implements ISelectionChangedListener, IPropertyChangeListener {
+ private static final String COMPARE_EDITOR_SCOPE_ID = "org.eclipse.compare.compareEditorScope"; //$NON-NLS-1$
+
+ private static final String DEFAULT_ACCELERATOR_SCHEME_ID = "org.eclipse.ui.defaultAcceleratorConfiguration"; //$NON-NLS-1$
+
+ private static final Map<String, String> COMPARE_BINDINGS;
+
+ static {
+ Map<String, String> bindings = Maps.newHashMap();
+ bindings.put("org.eclipse.compare.selectNextChange", "M3+ARROW_DOWN"); //$NON-NLS-1$//$NON-NLS-2$
+ bindings.put("org.eclipse.compare.selectPreviousChange", "M3+ARROW_UP"); //$NON-NLS-1$//$NON-NLS-2$
+ bindings.put("org.eclipse.compare.copyLeftToRight", "M3+ARROW_RIGHT"); //$NON-NLS-1$//$NON-NLS-2$
+ bindings.put("org.eclipse.compare.copyRightToLeft", "M3+ARROW_LEFT"); //$NON-NLS-1$//$NON-NLS-2$
+ bindings.put("org.eclipse.compare.copyAllLeftToRight", "M2+M3+ARROW_RIGHT"); //$NON-NLS-1$//$NON-NLS-2$
+ bindings.put("org.eclipse.compare.copyAllRightToLeft", "M2+M3+ARROW_LEFT"); //$NON-NLS-1$//$NON-NLS-2$
+ COMPARE_BINDINGS = Collections.unmodifiableMap(bindings);
+ }
+
private final GroupActionMenu groupActionMenu;
private final FilterActionMenu filterActionMenu;
@@ -119,15 +159,15 @@ public class CompareToolBar implements ISelectionChangedListener, IPropertyChang
}
}
- public final void initToolbar(AbstractTreeViewer viewer, INavigatable nav) {
+ public final void initToolbar(final AbstractTreeViewer viewer, INavigatable nav,
+ CompareHandlerService handlerService) {
if (!this.doOnce) {
compareConfiguration.getEventBus().register(this);
compareConfiguration.addPropertyChangeListener(this);
// Add extension point contributions to the structure merge viewer toolbar
if (PlatformUI.isWorkbenchRunning()) {
- IServiceLocator workbench = PlatformUI.getWorkbench();
- final IMenuService menuService = (IMenuService)workbench.getService(IMenuService.class);
+ final IMenuService menuService = getService(IMenuService.class);
if (menuService != null) {
menuService.populateContributionManager(toolbarManager,
"toolbar:org.eclipse.emf.compare.structuremergeviewer.toolbar"); //$NON-NLS-1$
@@ -140,6 +180,8 @@ public class CompareToolBar implements ISelectionChangedListener, IPropertyChang
});
}
}
+
+ registerCompareBindings();
}
boolean leftEditable = compareConfiguration.isLeftEditable();
@@ -205,16 +247,126 @@ public class CompareToolBar implements ISelectionChangedListener, IPropertyChang
}
setMirrored(MirrorUtil.isMirrored(compareConfiguration));
+ Arrays.stream(toolbarManager.getItems())
+ .filter(item -> item instanceof ActionContributionItem
+ && ((ActionContributionItem)item).getAction().getActionDefinitionId() != null)
+ .map(ActionContributionItem.class::cast)
+ .forEach(item -> registerBinding(item, viewer, handlerService));
+
toolbarManager.update(true);
this.doOnce = true;
}
+
+ }
+
+ private void registerCompareBindings() {
+ // Dynamically register the key bindings, but only if the active scheme is the default scheme.
+ final IBindingService bindingService = getService(IBindingService.class);
+ if (isDefaultAcceleratorSchemeActive()) {
+ // We need the command service to work with commands.
+ ICommandService commandService = getService(ICommandService.class);
+ List<KeyBinding> newBindings = Lists.newArrayList();
+ // Iterate over our static bindings map.
+ for (Map.Entry<String, String> entry : COMPARE_BINDINGS.entrySet()) {
+ String id = entry.getKey();
+ // If there is no active binding for this command ID...
+ if (bindingService.getActiveBindingsFor(id).length == 0) {
+ // Fetch the command and create a parameterized command for it.
+ Command command = commandService.getCommand(id);
+ ParameterizedCommand parameterizedCommand = ParameterizedCommand.generateCommand(command,
+ Collections.emptyMap());
+ // Parse our key sequence.
+ KeySequence keySequence;
+ try {
+ keySequence = KeySequence.getInstance(entry.getValue());
+ } catch (ParseException e) {
+ // Can't happen because our static constants are definitely valid.
+ continue;
+ }
+ // Create the binding for the default scheme and with compare editor scope.
+ // Note that we create it with USER type so that it will have precedence and will
+ // not result in key binding warnings in the log when the compare editor is
+ // opened.
+ KeyBinding binding = new KeyBinding(keySequence, parameterizedCommand,
+ DEFAULT_ACCELERATOR_SCHEME_ID, COMPARE_EDITOR_SCOPE_ID, null, null, null,
+ Binding.USER);
+ newBindings.add(binding);
+ }
+ }
+
+ // If we've generated new bindings...
+ if (!newBindings.isEmpty()) {
+ // Add them to the existing bindings.
+ ArrayList<Binding> bindings = Lists.newArrayList(bindingService.getBindings());
+ bindings.addAll(newBindings);
+ try {
+ // Save them all to the preference store to make them available.
+ bindingService.savePreferences(bindingService.getActiveScheme(),
+ bindings.toArray(new Binding[bindings.size()]));
+ } catch (IOException e) {
+ EMFCompareIDEUIPlugin.getDefault().log(e);
+ }
+ }
+ }
+ }
+
+ private boolean isDefaultAcceleratorSchemeActive() {
+ final IBindingService bindingService = getService(IBindingService.class);
+ return bindingService.getActiveScheme() != null
+ && DEFAULT_ACCELERATOR_SCHEME_ID.equals(bindingService.getActiveScheme().getId());
+ }
+
+ @SuppressWarnings("cast")
+ private <T> T getService(Class<T> serviceClass) {
+ return (T)PlatformUI.getWorkbench().getService(serviceClass);
+ }
+
+ private void registerBinding(ActionContributionItem item, AbstractTreeViewer viewer,
+ CompareHandlerService handlerService) {
+ final IAction action = item.getAction();
+ final String actionDefinitionId = action.getActionDefinitionId();
+ final Action actionWrapper = new ActionWrapper(action, viewer);
+ // Set the action definition ID so that it's known during registration.
+ actionWrapper.setActionDefinitionId(actionDefinitionId);
+ // Register it globally, because if we just register it as an action, then we'll get
+ // logged errors about conflicting bindings when the bottom viewers also register this
+ // same action definition ID.
+ handlerService.setGlobalActionHandler(actionDefinitionId, actionWrapper);
+
+ // If we have a binding service...
+ final IBindingService bindingService = getService(IBindingService.class);
+ if (bindingService != null) {
+ // Determine the trigger sequences associated with this action definition ID.
+ for (TriggerSequence triggerSequence : bindingService.getActiveBindingsFor(actionDefinitionId)) {
+ // Look up the binding for that key sequence.
+ Binding binding = bindingService.getPerfectMatch(triggerSequence);
+ if (binding != null) {
+ // Extract the command for that binding.
+ ParameterizedCommand parameterizedCommand = binding.getParameterizedCommand();
+ // If that command ID is different from this action definition ID,
+ // then the key sequence will not invoke our registered action.
+ // This seems like a bug in the platform's lookup mechanism.
+ String id = parameterizedCommand.getId();
+ if (!actionDefinitionId.equals(id)) {
+ // To work around that misbehavior, register our action with that ID.
+ actionWrapper.setActionDefinitionId(id);
+ handlerService.setGlobalActionHandler(id, actionWrapper);
+ }
+ }
+ }
+ }
}
private MergeAction createMergeAction(MergeMode mergeMode, EMFCompareConfiguration cc, INavigatable nav) {
IMerger.Registry mergerRegistry = EMFCompareRCPPlugin.getDefault().getMergerRegistry();
MergeAction mergeAction = new MergeAction(cc, mergerRegistry, mergeMode, nav);
mergeActions.add(mergeAction);
+ if (mergeMode == MergeMode.RIGHT_TO_LEFT || mergeMode == MergeMode.ACCEPT) {
+ mergeAction.setActionDefinitionId("org.eclipse.compare.copyRightToLeft"); //$NON-NLS-1$
+ } else {
+ mergeAction.setActionDefinitionId("org.eclipse.compare.copyLeftToRight"); //$NON-NLS-1$
+ }
return mergeAction;
}
@@ -223,6 +375,11 @@ public class CompareToolBar implements ISelectionChangedListener, IPropertyChang
MergeAllNonConflictingAction mergeAction = new MergeAllNonConflictingAction(cc, cc.getComparison(),
mergerRegistry, mergeMode);
mergeAllNonConflictingActions.add(mergeAction);
+ if (mergeMode == MergeMode.RIGHT_TO_LEFT || mergeMode == MergeMode.ACCEPT) {
+ mergeAction.setActionDefinitionId("org.eclipse.compare.copyAllRightToLeft"); //$NON-NLS-1$
+ } else {
+ mergeAction.setActionDefinitionId("org.eclipse.compare.copyAllLeftToRight"); //$NON-NLS-1$
+ }
return mergeAction;
}
@@ -327,4 +484,56 @@ public class CompareToolBar implements ISelectionChangedListener, IPropertyChang
}
}
+ private final class ActionWrapper extends Action {
+
+ private final AbstractTreeViewer viewer;
+
+ private final IAction action;
+
+ private ActionWrapper(IAction action, AbstractTreeViewer viewer) {
+ super(action.getText());
+ this.viewer = viewer;
+ this.action = action;
+ }
+
+ @Override
+ public void run() {
+ Composite composite = viewer.getControl().getParent();
+ Control focusControl = composite.getDisplay().getFocusControl();
+ if (focusControl != null) {
+ // Traverse the parents of the focus control.
+ Composite parent = focusControl.getParent();
+ while (parent != null && parent != composite) {
+ // If there is a tool bar manager in the bottom viewer...
+ ToolBarManager toolBarManager = CompareViewerPane.getToolBarManager(parent);
+ if (toolBarManager != null) {
+ // Iterate over action contribution items of that tool bar manager.
+ for (IContributionItem otherContributionItem : toolBarManager.getItems()) {
+ if (otherContributionItem instanceof ActionContributionItem) {
+ ActionContributionItem otherActionContributionItem = (ActionContributionItem)otherContributionItem;
+ IAction contributedAction = otherActionContributionItem.getAction();
+ String actionDefinitionId = contributedAction.getActionDefinitionId();
+ // If the contributed action's action definition ID is equal to this action
+ // definition ID...
+ if (action.getActionDefinitionId().equals(actionDefinitionId)) {
+ // Run that action, if it is enabled, and then we're done.
+ if (contributedAction.isEnabled()) {
+ contributedAction.run();
+ }
+ return;
+ }
+ }
+ }
+ }
+ parent = parent.getParent();
+ }
+ }
+ // We didn't find a contributed item, so run the action for this viewer's tool bar, if it is
+ // enabled.
+ if (action.isEnabled()) {
+ action.run();
+ }
+ }
+ }
+
}
diff --git a/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/structuremergeviewer/EMFCompareStructureMergeViewer.java b/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/structuremergeviewer/EMFCompareStructureMergeViewer.java
index 82ff2bef3..1c877e7b1 100644
--- a/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/structuremergeviewer/EMFCompareStructureMergeViewer.java
+++ b/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/structuremergeviewer/EMFCompareStructureMergeViewer.java
@@ -472,7 +472,7 @@ public class EMFCompareStructureMergeViewer extends AbstractStructuredViewerWrap
if (!monitor.isCanceled()) {
SWTUtil.safeSyncExec(new Runnable() {
public void run() {
- toolBar.initToolbar(getViewer(), navigatable);
+ toolBar.initToolbar(getViewer(), navigatable, fHandlerService);
toolBar.setEnabled(false);
}
});
diff --git a/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/structuremergeviewer/actions/SelectDiffAction.java b/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/structuremergeviewer/actions/SelectDiffAction.java
index 1cb053efb..4238ad2c5 100644
--- a/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/structuremergeviewer/actions/SelectDiffAction.java
+++ b/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/structuremergeviewer/actions/SelectDiffAction.java
@@ -40,7 +40,6 @@ public class SelectDiffAction extends Action {
this.navigatable = navigatable;
this.changeFlag = changeFlag;
initToolTipAndImage();
- activateActionHandler();
}
protected void initToolTipAndImage() {
@@ -49,11 +48,13 @@ public class SelectDiffAction extends Action {
setToolTipText(EMFCompareIDEUIMessages.getString("previous.diff.tooltip")); //$NON-NLS-1$
setImageDescriptor(AbstractUIPlugin.imageDescriptorFromPlugin(EMFCompareIDEUIPlugin.PLUGIN_ID,
"icons/full/toolb16/prev_diff.gif")); //$NON-NLS-1$
+ setActionDefinitionId("org.eclipse.compare.selectPreviousChange"); //$NON-NLS-1$
break;
case INavigatable.NEXT_CHANGE:
setToolTipText(EMFCompareIDEUIMessages.getString("next.diff.tooltip")); //$NON-NLS-1$
setImageDescriptor(AbstractUIPlugin.imageDescriptorFromPlugin(EMFCompareIDEUIPlugin.PLUGIN_ID,
"icons/full/toolb16/next_diff.gif")); //$NON-NLS-1$
+ setActionDefinitionId("org.eclipse.compare.selectNextChange"); //$NON-NLS-1$
break;
case Navigatable.PREVIOUS_UNRESOLVED_CHANGE:

Back to the top