/******************************************************************************* * Copyright (c) 2010, 2016 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * IBM Corporation - initial API and implementation * Lars Vogel <Lars.Vogel@gmail.com> - Bug 429575 ******************************************************************************/ package org.eclipse.e4.ui.workbench.swt.util; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import javax.annotation.PostConstruct; import javax.annotation.PreDestroy; import javax.inject.Inject; import org.eclipse.core.commands.ParameterizedCommand; import org.eclipse.core.commands.common.NotDefinedException; import org.eclipse.core.commands.contexts.Context; import org.eclipse.core.commands.contexts.ContextManager; import org.eclipse.core.runtime.Assert; import org.eclipse.e4.core.commands.ECommandService; import org.eclipse.e4.core.contexts.IEclipseContext; import org.eclipse.e4.core.di.annotations.Optional; import org.eclipse.e4.core.services.events.IEventBroker; import org.eclipse.e4.ui.bindings.EBindingService; import org.eclipse.e4.ui.bindings.internal.BindingTable; import org.eclipse.e4.ui.bindings.internal.BindingTableManager; import org.eclipse.e4.ui.internal.workbench.Activator; import org.eclipse.e4.ui.internal.workbench.swt.Policy; import org.eclipse.e4.ui.internal.workbench.swt.WorkbenchSWTActivator; import org.eclipse.e4.ui.model.application.MApplication; import org.eclipse.e4.ui.model.application.commands.MBindingContext; import org.eclipse.e4.ui.model.application.commands.MBindingTable; import org.eclipse.e4.ui.model.application.commands.MBindings; import org.eclipse.e4.ui.model.application.commands.MCommand; import org.eclipse.e4.ui.model.application.commands.MKeyBinding; import org.eclipse.e4.ui.model.application.commands.MParameter; import org.eclipse.e4.ui.model.application.ui.MContext; import org.eclipse.e4.ui.model.application.ui.MElementContainer; import org.eclipse.e4.ui.model.application.ui.MUIElement; import org.eclipse.e4.ui.services.EContextService; import org.eclipse.e4.ui.workbench.UIEvents; import org.eclipse.emf.ecore.EObject; import org.eclipse.emf.ecore.util.EcoreUtil; import org.eclipse.jface.bindings.Binding; import org.eclipse.jface.bindings.BindingManager; import org.eclipse.jface.bindings.Scheme; import org.eclipse.jface.bindings.TriggerSequence; import org.osgi.service.event.EventHandler; import org.osgi.service.log.LogService; /** * Process contexts in the model, feeding them into the command service. */ public class BindingProcessingAddon { private static final String[] DEFAULT_SCHEMES = { "org.eclipse.ui.defaultAcceleratorConfiguration" }; @Inject private MApplication application; @Inject private IEventBroker broker; @Inject private ContextManager contextManager; @Inject private BindingTableManager bindingTables; @Inject @Optional private BindingManager bindingManager; @Inject private ECommandService commandService; @Inject private EBindingService bindingService; private EventHandler additionHandler; private EventHandler contextHandler; @PostConstruct public void init() { String[] schemes = DEFAULT_SCHEMES; if (bindingManager != null) { final Scheme activeScheme = bindingManager.getActiveScheme(); if (activeScheme != null) { schemes = getSchemeIds(activeScheme.getId()); } } bindingTables.setActiveSchemes(schemes); defineBindingTables(); activateContexts(application); registerModelListeners(); } private final String[] getSchemeIds(String schemeId) { final List<String> strings = new ArrayList<>(); while (schemeId != null) { strings.add(schemeId); try { schemeId = getScheme(schemeId).getParentId(); } catch (final NotDefinedException e) { return new String[0]; } } return strings.toArray(new String[strings.size()]); } private final Scheme getScheme(String schemeId) { return bindingManager.getScheme(schemeId); } private void activateContexts(Object me) { if (me instanceof MBindings) { MContext contextModel = (MContext) me; MBindings container = (MBindings) me; List<MBindingContext> bindingContexts = container .getBindingContexts(); IEclipseContext context = contextModel.getContext(); if (context != null && !bindingContexts.isEmpty()) { EContextService cs = context.get(EContextService.class); for (MBindingContext element : bindingContexts) { cs.activateContext(element.getElementId()); } } } if (me instanceof MElementContainer) { @SuppressWarnings("unchecked") List<MUIElement> children = ((MElementContainer<MUIElement>) me).getChildren(); Iterator<MUIElement> i = children.iterator(); while (i.hasNext()) { MUIElement e = i.next(); activateContexts(e); } } } private void defineBindingTables() { if (Policy.DEBUG_CMDS) { WorkbenchSWTActivator.trace(Policy.DEBUG_CMDS_FLAG, "Initialize binding tables from model", null); //$NON-NLS-1$ } for (MBindingTable bindingTable : application.getBindingTables()) { defineBindingTable(bindingTable); } } /** * @param bindingTable */ private void defineBindingTable(MBindingTable bindingTable) { Assert.isNotNull( bindingTable.getBindingContext(), "Binding context referred to by the binding table \"" + bindingTable.getElementId() + "\""); //$NON-NLS-1$ Assert.isNotNull(bindingTable.getBindingContext().getElementId(), "Element ID of binding table \"" + bindingTable.getElementId() + "\"."); //$NON-NLS-1$ final Context bindingContext = contextManager.getContext(bindingTable.getBindingContext().getElementId()); BindingTable table = bindingTables.getTable(bindingTable.getBindingContext().getElementId()); if (table == null) { table = new BindingTable(bindingContext); bindingTables.addTable(table); } for (MKeyBinding binding : bindingTable.getBindings()) { defineBinding(table, bindingContext, binding); } } /** * @param bindingTable * @param binding */ private void defineBinding(BindingTable bindingTable, Context bindingContext, MKeyBinding binding) { Binding keyBinding = createBinding(bindingContext, binding.getCommand(), binding.getParameters(), binding.getKeySequence(), binding); if (keyBinding != null && !binding.getTags().contains(EBindingService.DELETED_BINDING_TAG)) { bindingTable.addBinding(keyBinding); } } private Binding createBinding(Context bindingContext, MCommand cmdModel, List<MParameter> modelParms, String keySequence, MKeyBinding binding) { Binding keyBinding = null; if (binding.getTransientData().get(EBindingService.MODEL_TO_BINDING_KEY) != null) { try { keyBinding = (Binding) binding.getTransientData().get(EBindingService.MODEL_TO_BINDING_KEY); return keyBinding; } catch (ClassCastException cce) { System.err.println( "Invalid type stored in transient data with the key " + EBindingService.MODEL_TO_BINDING_KEY); return null; } } if (cmdModel == null) { Activator.log(LogService.LOG_ERROR, "binding with no command: " + binding); //$NON-NLS-1$ return null; } Map<String, Object> parameters = null; if (modelParms != null && !modelParms.isEmpty()) { parameters = new HashMap<>(); for (MParameter mParm : modelParms) { parameters.put(mParm.getName(), mParm.getValue()); } } ParameterizedCommand cmd = commandService.createCommand(cmdModel.getElementId(), parameters); TriggerSequence sequence = null; sequence = bindingService.createSequence(keySequence); if (cmd == null) { System.err.println("Failed to find command for binding: " + binding); //$NON-NLS-1$ } else if (sequence == null) { System.err.println("Failed to map binding: " + binding); //$NON-NLS-1$ } else { try { String schemeId = null; String locale = null; String platform = null; Map<String, String> attrs = new HashMap<>(); List<String> tags = binding.getTags(); for (String tag : tags) { // remember to skip the ':' in each tag! if (tag.startsWith(EBindingService.SCHEME_ID_ATTR_TAG)) { schemeId = tag.substring(9); attrs.put(EBindingService.SCHEME_ID_ATTR_TAG, schemeId); } else if (tag.startsWith(EBindingService.LOCALE_ATTR_TAG)) { locale = tag.substring(7); attrs.put(EBindingService.LOCALE_ATTR_TAG, locale); } else if (tag.startsWith(EBindingService.PLATFORM_ATTR_TAG)) { platform = tag.substring(9); attrs.put(EBindingService.PLATFORM_ATTR_TAG, platform); } else if (tag.startsWith(EBindingService.TYPE_ATTR_TAG)) { // system bindings won't pass this attr attrs.put(EBindingService.TYPE_ATTR_TAG, "user"); } } keyBinding = bindingService.createBinding(sequence, cmd, bindingContext.getId(), attrs); binding.getTransientData().put(EBindingService.MODEL_TO_BINDING_KEY, keyBinding); } catch (IllegalArgumentException e) { if (Policy.DEBUG_MENUS) { WorkbenchSWTActivator.trace(Policy.DEBUG_MENUS_FLAG, "failed to create: " + binding, e); //$NON-NLS-1$ } return null; } } return keyBinding; } private void updateBinding(MKeyBinding binding, boolean add, Object eObj) { Object parentObj = ((EObject) binding).eContainer(); if (!(parentObj instanceof MBindingTable)) { // the link will already be broken for removes, so we'll try this if (eObj instanceof MBindingTable) { parentObj = eObj; } } if (parentObj == null) { return; } MBindingTable bt = (MBindingTable) parentObj; final Context bindingContext = contextManager.getContext(bt.getBindingContext().getElementId()); BindingTable table = bindingTables.getTable(bindingContext.getId()); if (table == null) { Activator.log(LogService.LOG_ERROR, "Trying to create \'" + binding //$NON-NLS-1$ + "\' without binding table " + bindingContext.getId()); //$NON-NLS-1$ return; } Binding keyBinding = createBinding(bindingContext, binding.getCommand(), binding.getParameters(), binding.getKeySequence(), binding); if (keyBinding != null) { if (add) { table.addBinding(keyBinding); } else { table.removeBinding(keyBinding); } } } @PreDestroy public void dispose() { unregisterModelListeners(); } private void registerModelListeners() { additionHandler = event -> { Object elementObj = event .getProperty(UIEvents.EventTags.ELEMENT); if (elementObj instanceof MApplication) { if (UIEvents.isADD(event)) { for (Object newObj1 : UIEvents.asIterable(event, UIEvents.EventTags.NEW_VALUE)) { if (newObj1 instanceof MBindingTable) { MBindingTable bt = (MBindingTable) newObj1; final Context bindingContext = contextManager .getContext(bt.getBindingContext() .getElementId()); final BindingTable table = new BindingTable( bindingContext); bindingTables.addTable(table); List<MKeyBinding> bindings = bt.getBindings(); for (MKeyBinding binding1 : bindings) { Binding keyBinding = createBinding( bindingContext, binding1.getCommand(), binding1.getParameters(), binding1.getKeySequence(), binding1); if (keyBinding != null) { table.addBinding(keyBinding); } } } } } } else if (elementObj instanceof MBindingTable) { // adding a binding if (UIEvents.isADD(event)) { for (Object newObj2 : UIEvents.asIterable(event, UIEvents.EventTags.NEW_VALUE)) { if (newObj2 instanceof MKeyBinding) { MKeyBinding binding2 = (MKeyBinding) newObj2; updateBinding(binding2, true, elementObj); } } } // removing a binding else if (UIEvents.isREMOVE(event)) { for (Object oldObj1 : UIEvents.asIterable(event, UIEvents.EventTags.OLD_VALUE)) { if (oldObj1 instanceof MKeyBinding) { MKeyBinding binding3 = (MKeyBinding) oldObj1; updateBinding(binding3, false, elementObj); } } } } else if (elementObj instanceof MKeyBinding) { MKeyBinding binding4 = (MKeyBinding) elementObj; String attrName = (String) event .getProperty(UIEvents.EventTags.ATTNAME); // System.out.println("MKeyBinding." + attrName + ": " // + event.getProperty(UIEvents.EventTags.TYPE)); if (UIEvents.isSET(event)) { Object oldObj2 = event .getProperty(UIEvents.EventTags.OLD_VALUE); if (UIEvents.KeyBinding.COMMAND.equals(attrName)) { MKeyBinding oldBinding1 = (MKeyBinding) EcoreUtil .copy((EObject) binding4); oldBinding1.setCommand((MCommand) oldObj2); updateBinding(oldBinding1, false, ((EObject) binding4).eContainer()); updateBinding(binding4, true, null); } else if (UIEvents.KeySequence.KEYSEQUENCE .equals(attrName)) { MKeyBinding oldBinding2 = (MKeyBinding) EcoreUtil .copy((EObject) binding4); oldBinding2.setKeySequence((String) oldObj2); updateBinding(oldBinding2, false, ((EObject) binding4).eContainer()); updateBinding(binding4, true, null); } } else if (UIEvents.KeyBinding.PARAMETERS.equals(attrName)) { if (UIEvents.isADD(event)) { Object newObj3 = event .getProperty(UIEvents.EventTags.NEW_VALUE); MKeyBinding oldBinding3 = (MKeyBinding) EcoreUtil .copy((EObject) binding4); if (UIEvents.EventTypes.ADD_MANY.equals(event .getProperty(UIEvents.EventTags.TYPE))) { oldBinding3.getParameters().removeAll( (Collection<?>) newObj3); } else { oldBinding3.getParameters().remove(newObj3); } updateBinding(oldBinding3, false, ((EObject) binding4).eContainer()); updateBinding(binding4, true, null); } else if (UIEvents.isREMOVE(event)) { Object oldObj3 = event .getProperty(UIEvents.EventTags.OLD_VALUE); MKeyBinding oldBinding4 = (MKeyBinding) EcoreUtil .copy((EObject) binding4); if (UIEvents.EventTypes.REMOVE_MANY.equals(event .getProperty(UIEvents.EventTags.TYPE))) { @SuppressWarnings("unchecked") Collection<MParameter> parms = (Collection<MParameter>) oldObj3; oldBinding4.getParameters().addAll(parms); } else { oldBinding4.getParameters().add( (MParameter) oldObj3); } updateBinding(oldBinding4, false, ((EObject) binding4).eContainer()); updateBinding(binding4, true, null); } } // if we've updated the tags for an MKeyBinding else if (UIEvents.ApplicationElement.TAGS.equals(attrName)) { List<String> tags = binding4.getTags(); // if we added a deleted tag to the MKeyBinding, then // remove it from the runtime binding tables if (tags.contains(EBindingService.DELETED_BINDING_TAG)) { updateBinding(binding4, false, elementObj); } // else we're adding the binding to the runtime tables else { updateBinding(binding4, true, elementObj); } } } }; broker.subscribe(UIEvents.BindingTableContainer.TOPIC_BINDINGTABLES, additionHandler); broker.subscribe(UIEvents.BindingTable.TOPIC_BINDINGS, additionHandler); broker.subscribe(UIEvents.KeyBinding.TOPIC_COMMAND, additionHandler); broker.subscribe(UIEvents.KeyBinding.TOPIC_PARAMETERS, additionHandler); broker.subscribe(UIEvents.KeySequence.TOPIC_KEYSEQUENCE, additionHandler); broker.subscribe(UIEvents.ApplicationElement.TOPIC_TAGS, additionHandler); contextHandler = event -> { Object elementObj = event.getProperty(UIEvents.EventTags.ELEMENT); Object newObj = event.getProperty(UIEvents.EventTags.NEW_VALUE); if (UIEvents.EventTypes.SET.equals(event.getProperty(UIEvents.EventTags.TYPE)) && newObj instanceof IEclipseContext) { activateContexts(elementObj); } }; broker.subscribe(UIEvents.Context.TOPIC_CONTEXT, contextHandler); } private void unregisterModelListeners() { broker.unsubscribe(additionHandler); broker.unsubscribe(additionHandler); broker.unsubscribe(additionHandler); broker.unsubscribe(additionHandler); broker.unsubscribe(additionHandler); broker.unsubscribe(contextHandler); } }