| /******************************************************************************* |
| * Copyright (c) 2010 Brian de Alwis, 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: |
| * Brian de Alwis - initial API and implementation |
| *******************************************************************************/ |
| package org.eclipse.e4.ui.workbench.renderers.swt.cocoa; |
| |
| import java.util.List; |
| import javax.inject.Inject; |
| import javax.inject.Provider; |
| import org.eclipse.core.runtime.IStatus; |
| import org.eclipse.core.runtime.Status; |
| import org.eclipse.e4.core.di.annotations.Execute; |
| import org.eclipse.e4.core.services.statusreporter.StatusReporter; |
| import org.eclipse.e4.ui.bindings.EBindingService; |
| import org.eclipse.e4.ui.model.application.MAddon; |
| import org.eclipse.e4.ui.model.application.MApplication; |
| import org.eclipse.e4.ui.model.application.MApplicationFactory; |
| 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.MCategory; |
| import org.eclipse.e4.ui.model.application.commands.MCommand; |
| import org.eclipse.e4.ui.model.application.commands.MCommandsFactory; |
| import org.eclipse.e4.ui.model.application.commands.MHandler; |
| import org.eclipse.e4.ui.model.application.commands.MKeyBinding; |
| import org.osgi.framework.Bundle; |
| import org.osgi.framework.FrameworkUtil; |
| |
| /** |
| * A hack to create an IStartup-like equivalent using ModelProcessor. But as the |
| * context provided to a {@code ModelProcessor} is destroyed after this |
| * processor has executed (thus disconnecting any event listeners, etc.), we |
| * create a new context and defer processing to the actual handling is done by |
| * {@link CocoaUIHandler}. |
| */ |
| public class CocoaUIProcessor { |
| /** |
| * Useful constants for referencing classes defined within this |
| * host/fragment |
| */ |
| static final String FRAGMENT_ID = "org.eclipse.e4.ui.workbench.renderers.swt.cocoa"; //$NON-NLS-1$ |
| protected static final String CONTRIBUTOR_URI = "platform:/fragment/" + FRAGMENT_ID; //$NON-NLS-1$ |
| static final String HOST_ID = "org.eclipse.e4.ui.workbench.renderers.swt"; //$NON-NLS-1$ |
| protected static final String CONTRIBUTION_URI_PREFIX = "bundleclass://" + HOST_ID; //$NON-NLS-1$ |
| |
| // constants for close dialog |
| private static final String COMMAND_ID_CLOSE_DIALOG = "org.eclipse.ui.cocoa.closeDialog"; //$NON-NLS-1$ |
| private static final String CLOSE_DIALOG_KEYSEQUENCE = "M1+W"; //$NON-NLS-1$ |
| |
| @Inject |
| protected MApplication app; |
| |
| @Inject |
| protected Provider<StatusReporter> statusReporter; |
| |
| /** |
| * Execute! |
| */ |
| @Execute |
| public void execute() { |
| // hook About, Preferences, etc. |
| installAddon(); |
| |
| // Window > Zoom, etc. |
| installWindowHandlers(); |
| |
| // install close dialog handlers |
| installCloseDialogHandlers(); |
| } |
| |
| /** |
| * Install the addon. |
| */ |
| public void installAddon() { |
| String addonId = CocoaUIHandler.class.getName(); |
| for (MAddon addon : app.getAddons()) { |
| if (addonId.equals(addon.getElementId())) { |
| return; |
| } |
| } |
| |
| MAddon addon = MApplicationFactory.INSTANCE.createAddon(); |
| addon.setContributionURI(getClassURI(CocoaUIHandler.class)); |
| addon.setElementId(addonId); |
| app.getAddons().add(addon); |
| } |
| |
| /** |
| * Install the Cocoa window handlers. Sadly this has to be done here rather |
| * than in a <tt>fragments.e4xmi</tt> as the project |
| * <tt>Application.e4xmi</tt> may (and likely will) use different IDs. |
| */ |
| public void installWindowHandlers() { |
| installHandler( |
| defineCommand( |
| "org.eclipse.ui.category.window", "org.eclipse.ui.cocoa.arrangeWindowsInFront", //$NON-NLS-1$ //$NON-NLS-2$ |
| "%command.arrangeWindows.name", //$NON-NLS-1$ |
| "%command.arrangeWindows.desc", CONTRIBUTOR_URI), //$NON-NLS-1$ |
| ArrangeWindowHandler.class, CONTRIBUTOR_URI); |
| installHandler( |
| defineCommand( |
| "org.eclipse.ui.category.window", "org.eclipse.ui.cocoa.minimizeWindow", //$NON-NLS-1$ //$NON-NLS-2$ |
| "%command.minimize.name", "%command.minimize.desc", CONTRIBUTOR_URI), //$NON-NLS-1$ //$NON-NLS-2$ |
| MinimizeWindowHandler.class, CONTRIBUTOR_URI); |
| |
| MCommand toggleFullscreenCommand = defineCommand( |
| "org.eclipse.ui.category.window", "org.eclipse.ui.cocoa.fullscreenWindow", //$NON-NLS-1$ //$NON-NLS-2$ |
| "%command.fullscreen.name", "%command.fullscreen.desc", CONTRIBUTOR_URI); //$NON-NLS-1$//$NON-NLS-2$ |
| installHandler(toggleFullscreenCommand, FullscreenWindowHandler.class, |
| CONTRIBUTOR_URI); |
| // COMMAND+ALT+F is taken by Force Return |
| installKeybinding( |
| "org.eclipse.ui.contexts.window", "COMMAND+CTRL+F", toggleFullscreenCommand); //$NON-NLS-1$ //$NON-NLS-2$ |
| |
| installHandler( |
| defineCommand( |
| "org.eclipse.ui.category.window", "org.eclipse.ui.cocoa.zoomWindow", //$NON-NLS-1$ //$NON-NLS-2$ |
| "%command.zoom.name", "%command.zoom.desc", CONTRIBUTOR_URI), //$NON-NLS-1$//$NON-NLS-2$ |
| ZoomWindowHandler.class, CONTRIBUTOR_URI); |
| } |
| |
| /** Add the special Cmd-W dialog helper */ |
| private void installCloseDialogHandlers() { |
| MCommand closeDialogCommand = defineCommand( |
| "org.eclipse.ui.category.window", COMMAND_ID_CLOSE_DIALOG, //$NON-NLS-1$ |
| "%command.closeDialog.name", //$NON-NLS-1$ |
| "%command.closeDialog.desc", CONTRIBUTOR_URI);//$NON-NLS-1$ |
| installHandler(closeDialogCommand, CloseDialogHandler.class, |
| CONTRIBUTOR_URI); |
| installKeybinding(EBindingService.DIALOG_CONTEXT_ID, |
| CLOSE_DIALOG_KEYSEQUENCE, closeDialogCommand); |
| } |
| |
| /** |
| * Install a keybinding to the provided command, providing the command is |
| * not already bound to another keybinding. |
| * |
| * @param bindingContextId |
| * the keybinding context |
| * @param keysequence |
| * the key sequence to be bound |
| * @param cmd |
| * the command to be bound |
| */ |
| private void installKeybinding(String bindingContextId, String keysequence, |
| MCommand cmd) { |
| // there is a one-to-one mapping between binding contexts and |
| // binding tables, though binding tables may not necessarily |
| // guaranteed an element id. |
| MBindingTable bindingTable = null; |
| for (MBindingTable table : app.getBindingTables()) { |
| for (MKeyBinding binding : table.getBindings()) { |
| // only perform the binding if cmd not already bound |
| if (binding.getCommand() == cmd) { |
| return; |
| } |
| } |
| if (table.getBindingContext() != null |
| && bindingContextId.equals(table.getBindingContext() |
| .getElementId())) { |
| bindingTable = table; |
| } |
| } |
| |
| if (bindingTable == null) { |
| MBindingContext bindingContext = findBindingContext( |
| app.getBindingContexts(), bindingContextId); |
| if (bindingContext == null) { |
| statusReporter |
| .get() |
| .report(new Status( |
| IStatus.WARNING, |
| CocoaUIProcessor.FRAGMENT_ID, |
| "No binding context exists for " + bindingContextId), //$NON-NLS-1$ |
| StatusReporter.LOG); |
| return; |
| } |
| bindingTable = MCommandsFactory.INSTANCE.createBindingTable(); |
| bindingTable.setBindingContext(bindingContext); |
| bindingTable.setContributorURI(CONTRIBUTOR_URI); |
| app.getBindingTables().add(bindingTable); |
| } |
| |
| MKeyBinding binding = MCommandsFactory.INSTANCE.createKeyBinding(); |
| binding.setCommand(cmd); |
| binding.setKeySequence(keysequence); |
| bindingTable.getBindings().add(binding); |
| } |
| |
| /** |
| * @param bindingContexts |
| * @param bindingContextId |
| * @return |
| */ |
| private MBindingContext findBindingContext( |
| List<MBindingContext> bindingContexts, String bindingContextId) { |
| for (MBindingContext bc : bindingContexts) { |
| if (bindingContextId.equals(bc.getElementId())) { |
| return bc; |
| } |
| MBindingContext result = findBindingContext(bc.getChildren(), |
| bindingContextId); |
| if (result != null) { |
| return result; |
| } |
| } |
| return null; |
| } |
| |
| /** |
| * Configure and install a command handler for the provided command and |
| * handler |
| * |
| * @param handlerClass |
| * @param command |
| */ |
| private void installHandler(MCommand command, Class<?> handlerClass, |
| String contributorURI) { |
| for (MHandler handler : app.getHandlers()) { |
| if (handlerClass.getName().equals(handler.getElementId()) |
| && handler.getCommand() == command) { |
| return; |
| } |
| } |
| |
| MHandler handler = MCommandsFactory.INSTANCE.createHandler(); |
| handler.setContributionURI(getClassURI(handlerClass)); |
| handler.setContributorURI(contributorURI); |
| handler.setElementId(handlerClass.getName()); |
| handler.setCommand(command); |
| app.getHandlers().add(handler); |
| } |
| |
| /** |
| * Find the corresponding command and define if not found. |
| * |
| * @param commandId |
| * @param name |
| * @param description |
| * @return the command |
| */ |
| private MCommand defineCommand(String categoryId, String commandId, |
| String name, String description, String contributorURI) { |
| for (MCommand command : app.getCommands()) { |
| if (commandId.equals(command.getElementId())) { |
| return command; |
| } |
| } |
| MCommand command = MCommandsFactory.INSTANCE.createCommand(); |
| command.setCategory(defineCategory(categoryId)); |
| command.setElementId(commandId); |
| command.setCommandName(name); |
| command.setDescription(description); |
| command.setContributorURI(contributorURI); |
| app.getCommands().add(command); |
| return command; |
| } |
| |
| /** |
| * Find the corresponding category and define if not found. |
| * |
| * @param categoryId |
| * @return the category |
| */ |
| private MCategory defineCategory(String categoryId) { |
| for (MCategory category : app.getCategories()) { |
| if (categoryId.equals(category.getElementId())) { |
| return category; |
| } |
| } |
| MCategory category = MCommandsFactory.INSTANCE.createCategory(); |
| category.setElementId(categoryId); |
| category.setName(categoryId); |
| app.getCategories().add(category); |
| return category; |
| } |
| |
| /** |
| * Return a platform-style URI to reference the provided class |
| * |
| * @param clazz |
| * a class |
| * @return a URI referencing the class |
| * @throws IllegalArgumentException |
| * if the class was not defined from a bundle |
| */ |
| private String getClassURI(Class<?> clazz) { |
| return getBundleURI(clazz) + "/" + clazz.getName(); //$NON-NLS-1$ |
| } |
| |
| /** |
| * Return a platform-style URI to reference the bundle providing |
| * {@code clazz} |
| * |
| * @param clazz |
| * a class |
| * @return a URI referencing the bundle |
| * @throws IllegalArgumentException |
| * if the class was not defined from a bundle |
| */ |
| private String getBundleURI(Class<?> clazz) { |
| Bundle bundle = FrameworkUtil.getBundle(clazz); |
| if (bundle == null) { |
| throw new IllegalArgumentException(clazz.getName()); |
| } |
| return "bundleclass://" + bundle.getSymbolicName(); //$NON-NLS-1$ |
| } |
| |
| } |