Skip to content

Commit b6af990

Browse files
committed
Add activity support for keybindings contributions
Currently Eclipse processes key bindings from "disabled" activities, activity support for key bindings was broken during e4 transition in 2010. This change re-implements activity support for keybindings contributions. - Don't show filtered key bindings in Keys preference page - Hide bindings for filtered keys to BindingTableManager - Fail command execution for filtered activity - Update bindings state if enabled activities change Fixes #2859
1 parent b19891c commit b6af990

File tree

16 files changed

+207
-64
lines changed

16 files changed

+207
-64
lines changed

bundles/org.eclipse.e4.ui.bindings/META-INF/MANIFEST.MF

+4-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
Manifest-Version: 1.0
22
Bundle-ManifestVersion: 2
33
Bundle-SymbolicName: org.eclipse.e4.ui.bindings;singleton:=true
4-
Bundle-Version: 0.14.500.qualifier
4+
Bundle-Version: 0.14.600.qualifier
55
Bundle-Name: %pluginName
66
Bundle-Vendor: %providerName
77
Bundle-Localization: plugin
@@ -20,7 +20,9 @@ Require-Bundle: org.eclipse.swt;bundle-version="[3.6.0,4.0.0)",
2020
org.eclipse.core.commands;bundle-version="[3.5.0,4.0.0)",
2121
org.eclipse.e4.core.contexts;bundle-version="1.0.0",
2222
org.eclipse.e4.core.di;bundle-version="1.1.0",
23-
org.eclipse.e4.ui.services;bundle-version="1.0.0"
23+
org.eclipse.e4.ui.services;bundle-version="1.0.0",
24+
org.eclipse.e4.core.services;bundle-version="2.5.100",
25+
org.eclipse.e4.ui.model.workbench
2426
Export-Package: org.eclipse.e4.ui.bindings;
2527
x-friends:="org.eclipse.e4.ui.workbench,
2628
org.eclipse.e4.ui.workbench.renderers.swt,

bundles/org.eclipse.e4.ui.bindings/src/org/eclipse/e4/ui/bindings/internal/BindingTable.java

+60-8
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,12 @@
2020
import java.util.Comparator;
2121
import java.util.HashMap;
2222
import java.util.Iterator;
23+
import java.util.List;
2324
import java.util.Map;
2425
import org.eclipse.core.commands.ParameterizedCommand;
2526
import org.eclipse.core.commands.contexts.Context;
27+
import org.eclipse.e4.core.services.contributions.IContributionFactory;
28+
import org.eclipse.e4.ui.model.application.MApplication;
2629
import org.eclipse.jface.bindings.Binding;
2730
import org.eclipse.jface.bindings.Trigger;
2831
import org.eclipse.jface.bindings.TriggerSequence;
@@ -130,9 +133,15 @@ private final int countStrokes(final Trigger[] triggers) {
130133
private Map<TriggerSequence, ArrayList<Binding>> bindingsByPrefix = new HashMap<>();
131134
private Map<TriggerSequence, ArrayList<Binding>> conflicts = new HashMap<>();
132135
private Map<TriggerSequence, ArrayList<Binding>> orderedBindingsByTrigger = new HashMap<>();
136+
private final Map<Binding, Boolean> activeBindings = new HashMap<>();
133137

134-
public BindingTable(Context context) {
138+
private IContributionFactory contributionFactory;
139+
140+
private MApplication application;
141+
142+
public BindingTable(Context context, MApplication application) {
135143
tableId = context;
144+
this.application = application;
136145
}
137146

138147
public Context getTableId() {
@@ -254,6 +263,7 @@ public void removeBinding(Binding binding) {
254263
evaluateOrderedBindings(binding.getTriggerSequence(), null);
255264
}
256265
}
266+
activeBindings.remove(binding);
257267
}
258268

259269
private void evaluateOrderedBindings(TriggerSequence sequence, Binding binding) {
@@ -309,34 +319,76 @@ private void evaluateOrderedBindings(TriggerSequence sequence, Binding binding)
309319
}
310320

311321
public Binding getPerfectMatch(TriggerSequence trigger) {
312-
return bindingsByTrigger.get(trigger);
322+
Binding binding = bindingsByTrigger.get(trigger);
323+
if (isActive(binding)) {
324+
return binding;
325+
}
326+
return null;
313327
}
314328

315329
public Binding getBestSequenceFor(ParameterizedCommand command) {
316330
ArrayList<Binding> sequences = bindingsByCommand.get(command);
317-
if (sequences != null && sequences.size() > 0) {
318-
return sequences.get(0);
331+
if (sequences != null) {
332+
for (Binding binding : sequences) {
333+
if (isActive(binding)) {
334+
return binding;
335+
}
336+
}
319337
}
320338
return null;
321339
}
322340

323341
@SuppressWarnings("unchecked")
324342
public Collection<Binding> getSequencesFor(ParameterizedCommand command) {
325343
ArrayList<Binding> triggers = bindingsByCommand.get(command);
326-
return (Collection<Binding>) (triggers == null ? Collections.emptyList() : triggers.clone());
344+
return (Collection<Binding>) (triggers == null ? Collections.emptyList() : getActive(triggers));
327345
}
328346

329347
public Collection<Binding> getPartialMatches(TriggerSequence sequence) {
330-
return bindingsByPrefix.get(sequence);
348+
return getActive(bindingsByPrefix.get(sequence));
331349
}
332350

333351
public boolean isPartialMatch(TriggerSequence seq) {
334352
ArrayList<Binding> values = bindingsByPrefix.get(seq);
335-
return values != null && !values.isEmpty();
353+
return values != null && !getActive(values).isEmpty();
336354
}
337355

338356
public Collection<Binding> getBindings() {
339-
return Collections.unmodifiableCollection(bindings);
357+
return Collections.unmodifiableCollection(getActive(bindings));
358+
}
359+
360+
List<Binding> getActive(List<Binding> bindings) {
361+
return bindings == null ? null : bindings.stream().filter(b -> isActive(b)).toList();
340362
}
341363

364+
private boolean isActive(final Binding binding) {
365+
if (binding == null) {
366+
return false;
367+
}
368+
Boolean cachedValue = activeBindings.get(binding);
369+
if (cachedValue != null) {
370+
return cachedValue;
371+
}
372+
ParameterizedCommand command = binding.getParameterizedCommand();
373+
if (command == null) {
374+
// Binding without command is "unbound", so can't be active
375+
// We don't cache in case command will be added in preferences
376+
return false;
377+
}
378+
String identifierId = command.getId();
379+
if (contributionFactory == null) {
380+
contributionFactory = application.getContext().get(IContributionFactory.class);
381+
}
382+
if (contributionFactory == null) {
383+
// Something went wrong, let assume binding is active
384+
return true;
385+
}
386+
boolean currentValue = contributionFactory.isEnabled(identifierId);
387+
activeBindings.put(binding, currentValue);
388+
return currentValue;
389+
}
390+
391+
public void activitiesChanged() {
392+
activeBindings.clear();
393+
}
342394
}

bundles/org.eclipse.e4.ui.bindings/src/org/eclipse/e4/ui/bindings/internal/BindingTableManager.java

+9
Original file line numberDiff line numberDiff line change
@@ -257,4 +257,13 @@ private final int compareSchemes(final String schemeId1, final String schemeId2)
257257
}
258258
return 0;
259259
}
260+
261+
public void activitiesChanged() {
262+
for (Context ctx : definedTables.getContexts()) {
263+
BindingTable table = getTable(ctx.getId());
264+
if (table != null) {
265+
table.activitiesChanged();
266+
}
267+
}
268+
}
260269
}

bundles/org.eclipse.e4.ui.bindings/src/org/eclipse/e4/ui/bindings/keys/KeyBindingDispatcher.java

+20-1
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
import org.eclipse.core.commands.Command;
2626
import org.eclipse.core.commands.ExecutionException;
2727
import org.eclipse.core.commands.IHandler;
28+
import org.eclipse.core.commands.NotEnabledException;
2829
import org.eclipse.core.commands.ParameterizedCommand;
2930
import org.eclipse.core.commands.common.CommandException;
3031
import org.eclipse.core.commands.contexts.ContextManager;
@@ -33,6 +34,7 @@
3334
import org.eclipse.e4.core.contexts.EclipseContextFactory;
3435
import org.eclipse.e4.core.contexts.IEclipseContext;
3536
import org.eclipse.e4.core.di.annotations.Optional;
37+
import org.eclipse.e4.core.services.contributions.IContributionFactory;
3638
import org.eclipse.e4.core.services.log.Logger;
3739
import org.eclipse.e4.ui.bindings.EBindingService;
3840
import org.eclipse.e4.ui.bindings.internal.KeyAssistDialog;
@@ -63,7 +65,13 @@
6365
*/
6466
public class KeyBindingDispatcher {
6567

66-
private KeyAssistDialog keyAssistDialog = null;
68+
private KeyAssistDialog keyAssistDialog;
69+
70+
private IContributionFactory contributionFactory;
71+
72+
public KeyBindingDispatcher() {
73+
super();
74+
}
6775

6876
/**
6977
* A display filter for handling key bindings. This filter can either be enabled or disabled. If
@@ -280,6 +288,10 @@ public final boolean executeCommand(final ParameterizedCommand parameterizedComm
280288
// Reset the key binding state (close window, clear status line, etc.)
281289
resetState(false);
282290

291+
if (!isActive(parameterizedCommand)) {
292+
throw new NotEnabledException("Command should have been disabled via activity: " + parameterizedCommand); //$NON-NLS-1$
293+
}
294+
283295
final EHandlerService handlerService = getHandlerService();
284296
final Command command = parameterizedCommand.getCommand();
285297

@@ -620,6 +632,12 @@ public boolean press(List<KeyStroke> potentialKeyStrokes, Event event) {
620632
return !sequenceBeforeKeyStroke.isEmpty();
621633
}
622634

635+
private boolean isActive(final ParameterizedCommand command) {
636+
String identifierId = command.getId();
637+
boolean enabled = contributionFactory.isEnabled(identifierId);
638+
return enabled;
639+
}
640+
623641
/**
624642
* <p>
625643
* Actually performs the processing of the key event by interacting with the
@@ -687,6 +705,7 @@ final public KeySequence getBuffer() {
687705
@Inject
688706
public void setContext(IEclipseContext context) {
689707
this.context = context;
708+
contributionFactory = context.get(IContributionFactory.class);
690709
}
691710

692711
/**

bundles/org.eclipse.e4.ui.workbench.swt/META-INF/MANIFEST.MF

+6-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
Manifest-Version: 1.0
22
Bundle-ManifestVersion: 2
33
Bundle-SymbolicName: org.eclipse.e4.ui.workbench.swt;singleton:=true
4-
Bundle-Version: 0.17.700.qualifier
4+
Bundle-Version: 0.17.800.qualifier
55
Bundle-Name: %pluginName
66
Bundle-Vendor: %providerName
77
Bundle-Localization: plugin
@@ -32,7 +32,11 @@ Require-Bundle: org.eclipse.e4.ui.workbench;bundle-version="0.10.0",
3232
Require-Capability: osgi.extender; filter:="(&(osgi.extender=osgi.component)(version>=1.2)(!(version>=2.0)))"
3333
Bundle-ActivationPolicy: lazy
3434
Bundle-RequiredExecutionEnvironment: JavaSE-17
35-
Export-Package: org.eclipse.e4.ui.internal.workbench.swt;x-friends:="org.eclipse.e4.ui.workbench.addons.swt,org.eclipse.e4.ui.workbench.renderers.swt,org.eclipse.ui.workbench",
35+
Export-Package: org.eclipse.e4.ui.internal.workbench.swt;
36+
x-friends:="org.eclipse.e4.ui.workbench.addons.swt,
37+
org.eclipse.e4.ui.workbench.renderers.swt,
38+
org.eclipse.ui.workbench,
39+
org.eclipse.e4.ui.bindings.tests",
3640
org.eclipse.e4.ui.internal.workbench.swt.handlers;x-internal:=true,
3741
org.eclipse.e4.ui.workbench.swt,
3842
org.eclipse.e4.ui.workbench.swt.factories;x-friends:="org.eclipse.e4.ui.workbench.renderers.swt,org.eclipse.ui.workbench",

bundles/org.eclipse.e4.ui.workbench.swt/src/org/eclipse/e4/ui/workbench/swt/util/BindingProcessingAddon.java

+8-2
Original file line numberDiff line numberDiff line change
@@ -158,7 +158,7 @@ private void defineBindingTable(MBindingTable bindingTable) {
158158
final Context bindingContext = contextManager.getContext(bindingTable.getBindingContext().getElementId());
159159
BindingTable table = bindingTables.getTable(bindingTable.getBindingContext().getElementId());
160160
if (table == null) {
161-
table = new BindingTable(bindingContext);
161+
table = new BindingTable(bindingContext, application);
162162
bindingTables.addTable(table);
163163
}
164164
for (MKeyBinding binding : bindingTable.getBindings()) {
@@ -290,7 +290,7 @@ private void subscribeBindingTableContainerTopicBindingTables(
290290
if (newObj instanceof MBindingTable) {
291291
MBindingTable bt = (MBindingTable) newObj;
292292
final Context bindingContext = contextManager.getContext(bt.getBindingContext().getElementId());
293-
final BindingTable table = new BindingTable(bindingContext);
293+
final BindingTable table = new BindingTable(bindingContext, application);
294294
bindingTables.addTable(table);
295295
List<MKeyBinding> bindings = bt.getBindings();
296296
for (MKeyBinding binding : bindings) {
@@ -438,4 +438,10 @@ private void subscribeContextTopicContext(@UIEventTopic(UIEvents.Context.TOPIC_C
438438
activateContexts(elementObj);
439439
}
440440

441+
@Inject
442+
@Optional
443+
private void subscribeActivitiesChangedTopic(@UIEventTopic(UIEvents.UILifeCycle.ACTIVITIES_CHANGED) Event event) {
444+
bindingTables.activitiesChanged();
445+
}
446+
441447
}

bundles/org.eclipse.e4.ui.workbench/META-INF/MANIFEST.MF

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
Manifest-Version: 1.0
22
Bundle-ManifestVersion: 2
33
Bundle-SymbolicName: org.eclipse.e4.ui.workbench;singleton:=true
4-
Bundle-Version: 1.17.0.qualifier
4+
Bundle-Version: 1.18.0.qualifier
55
Bundle-Name: %pluginName
66
Bundle-Vendor: %providerName
77
Bundle-Localization: plugin

bundles/org.eclipse.e4.ui.workbench/src/org/eclipse/e4/ui/workbench/UIEvents.java

+7
Original file line numberDiff line numberDiff line change
@@ -348,6 +348,13 @@ public interface UILifeCycle {
348348
*/
349349
String THEME_DEFINITION_CHANGED = TOPIC + TOPIC_SEP
350350
+ "themeDefinitionChanged"; //$NON-NLS-1$
351+
352+
/**
353+
* Sent when activities changed (activity enabled/disabled)
354+
*
355+
* @since 1.18
356+
*/
357+
String ACTIVITIES_CHANGED = TOPIC + TOPIC_SEP + "activitiesChanged"; //$NON-NLS-1$
351358
}
352359

353360
/**

bundles/org.eclipse.ui.workbench/eclipseui/org/eclipse/ui/internal/activities/MutableActivityManager.java

+15
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@
3434
import org.eclipse.core.runtime.Status;
3535
import org.eclipse.core.runtime.jobs.IJobFunction;
3636
import org.eclipse.core.runtime.jobs.Job;
37+
import org.eclipse.e4.core.services.events.IEventBroker;
38+
import org.eclipse.e4.ui.workbench.UIEvents;
3739
import org.eclipse.jface.util.IPropertyChangeListener;
3840
import org.eclipse.ui.PlatformUI;
3941
import org.eclipse.ui.activities.ActivityEvent;
@@ -105,6 +107,8 @@ public final class MutableActivityManager extends AbstractActivityManager
105107

106108
private Map<ActivityDefinition, IEvaluationReference> refsByActivityDefinition = new HashMap<>();
107109

110+
private IEventBroker eventBroker;
111+
108112
/**
109113
* Create a new instance of this class using the platform extension registry.
110114
*/
@@ -129,6 +133,8 @@ public MutableActivityManager(ITriggerPointAdvisor triggerPointAdvisor, IActivit
129133
this.activityRegistry.addActivityRegistryListener(activityRegistryListener);
130134

131135
readRegistry(true);
136+
137+
eventBroker = PlatformUI.getWorkbench().getService(IEventBroker.class);
132138
}
133139

134140
@Override
@@ -850,4 +856,13 @@ private Job getUpdateJob() {
850856
return deferredIdentifierJob;
851857
}
852858

859+
@Override
860+
protected void fireActivityManagerChanged(ActivityManagerEvent activityManagerEvent) {
861+
super.fireActivityManagerChanged(activityManagerEvent);
862+
if (eventBroker != null) {
863+
PlatformUI.getWorkbench().getDisplay()
864+
.asyncExec(() -> eventBroker.send(UIEvents.UILifeCycle.ACTIVITIES_CHANGED, null));
865+
}
866+
}
867+
853868
}

bundles/org.eclipse.ui.workbench/eclipseui/org/eclipse/ui/internal/keys/CategoryPatternFilter.java

+16-1
Original file line numberDiff line numberDiff line change
@@ -19,15 +19,20 @@
1919
import org.eclipse.core.commands.common.NotDefinedException;
2020
import org.eclipse.jface.bindings.Binding;
2121
import org.eclipse.jface.viewers.Viewer;
22+
import org.eclipse.ui.activities.IActivityManager;
23+
import org.eclipse.ui.activities.IIdentifier;
2224
import org.eclipse.ui.dialogs.PatternFilter;
2325
import org.eclipse.ui.internal.keys.model.BindingElement;
2426

2527
class CategoryPatternFilter extends PatternFilter {
2628
private boolean filterCategories;
2729
final Category uncategorized;
2830

29-
public CategoryPatternFilter(boolean filterCategories, Category c) {
31+
private IActivityManager activityManager;
32+
33+
public CategoryPatternFilter(boolean filterCategories, Category c, IActivityManager activityManager) {
3034
uncategorized = c;
35+
this.activityManager = activityManager;
3136
filterCategories(filterCategories);
3237
}
3338

@@ -55,10 +60,20 @@ protected boolean isLeafMatch(Viewer viewer, Object element) {
5560
} catch (NotDefinedException e) {
5661
return false;
5762
}
63+
if (!isActive(cmd)) {
64+
return false;
65+
}
5866
}
5967
return super.isLeafMatch(viewer, element);
6068
}
6169

70+
private boolean isActive(final ParameterizedCommand command) {
71+
String identifierId = command.getId();
72+
IIdentifier identifier = activityManager.getIdentifier(identifierId);
73+
boolean enabled = identifier.isEnabled();
74+
return enabled;
75+
}
76+
6277
private ParameterizedCommand getCommand(Object element) {
6378
if (element instanceof BindingElement) {
6479
Object modelObject = ((BindingElement) element).getModelObject();

0 commit comments

Comments
 (0)