/**
* Copyright (c) 2009, 2010 Mark Feber, MulgaSoft
*
* 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
*/
package com.mulgasoft.emacsplus;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.eclipse.core.commands.Command;
import org.eclipse.core.commands.IExecutionListener;
import org.eclipse.core.commands.ParameterizedCommand;
import org.eclipse.jface.bindings.Binding;
import org.eclipse.jface.bindings.keys.KeyBinding;
import org.eclipse.jface.bindings.keys.KeySequence;
import org.eclipse.jface.bindings.keys.ParseException;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.commands.ICommandService;
import org.eclipse.ui.contexts.IContextService;
import org.eclipse.ui.internal.keys.BindingService;
import org.eclipse.ui.keys.IBindingService;
/**
* Determine which plugin specific bindings should be added once Emacs+ has started.
* Currently: java, javascript, pde, c++
*
* @author Mark Feber - initial API and implementation
*/
@SuppressWarnings("restriction") // for cast to org.eclipse.ui.internal.keys.BindingService since addBinding is not yet API
public class DynamicInitializer {
private DynamicInitializer() {}
public static class AddBindingsOnce {
public static boolean bind = addOnce();
}
private static boolean addOnce() {
List<String>emacsIds = EmacsPlusActivator.getDefault().getLoadedList();
if (EmacsPlusUtils.isMac()) {
addBindings(macBindings);
// kludge this until I find a better way to determine if the optional or other plugins are loaded
if (emacsIds.contains(EmacsPlusUtils.EMP_MACCMD_OPT_STR) ) {
addBindings(cmdBindings);
}
} else {
addBindings(nomacBindings);
if (emacsIds.contains(EmacsPlusUtils.EMP_OPT_STR) ) {
addBindings(altBindings);
}
}
addBindings(restBindings);
return true;
}
public static void initialize() {
@SuppressWarnings("unused") // this ensures bindings are added only once
boolean initKeys = DynamicInitializer.AddBindingsOnce.bind;
}
/**
* For each, if the command exists in the current system, add the new binding to the Emacs+ scheme
*
* @param editor
* @param bindingResult
*/
private static void addBindings(Set<MinderBinder> bindings) {
IBindingService service = ((IBindingService) PlatformUI.getWorkbench().getService(IBindingService.class));
if (service instanceof BindingService) {
BindingService bindingSvc = (BindingService) service;
IContextService contextService = (IContextService) PlatformUI.getWorkbench().getService(IContextService.class);
@SuppressWarnings("unchecked") // eclipse never parameterized their api
Collection<String> contexts = contextService.getDefinedContextIds();
ICommandService ics = ((ICommandService) PlatformUI.getWorkbench().getService(ICommandService.class));
for (MinderBinder mb : bindings) {
try {
// check first, as getCommand will create it if it doesn't already exist
if (contexts.contains(mb.getContextId()) && ics.getDefinedCommandIds().contains(mb.getCommandId())) {
Command cmd = ics.getCommand(mb.getCommandId());
if (mb.getEnhancer() != null) {
// enhance the pre-defined command with some Emacs+ behavior
cmd.addExecutionListener(mb.getEnhancer());
}
Binding binding = new KeyBinding(mb.getTrigger(), new ParameterizedCommand(cmd, null),
mb.getSchemeId(), mb.getContextId(), null, null, null, Binding.SYSTEM); // Binding.USER
// this call is scheduled for API promotion sometime (after Helios)
bindingSvc.addBinding(binding);
}
} catch (ParseException e) {
e.printStackTrace(); // won't happen
} catch (Exception e) {
e.printStackTrace(); // won't happen
}
}
}
}
private static class MinderBinder {
String commandId;
String contextId;
String schemeId = EmacsPlusUtils.EMP_SCHEMEID;
String keyString;
KeySequence trigger;
IExecutionListener enhancer;
public MinderBinder(String commandId, String contextId, String keyString) {
this(commandId,contextId,keyString,null);
}
public MinderBinder(String commandId, String contextId, String keyString, IExecutionListener enhancer) {
this.commandId = commandId;
this.contextId = contextId;
this.keyString = keyString;
this.enhancer = enhancer;
}
public String getCommandId() {
return commandId;
}
public String getContextId() {
return contextId;
}
public String getSchemeId() {
return schemeId;
}
public IExecutionListener getEnhancer() {
return enhancer;
}
/**
* @throws ParseException
*/
public KeySequence getTrigger() throws ParseException {
if (trigger == null) {
trigger = KeySequence.getInstance(keyString);
}
return trigger;
}
}
private static final String WINDOW = "org.eclipse.ui.contexts.window"; //$NON-NLS-1$
private static final String JEDITOR = "org.eclipse.jdt.ui.javaEditorScope"; //$NON-NLS-1$
private static final String JSEDITOR = "org.eclipse.wst.jsdt.ui.javaEditorScope"; //$NON-NLS-1$
private static final String JSVEDITOR = "org.eclipse.wst.jsdt.ui.javascriptViewScope"; //$NON-NLS-1$
private static final String CEDITOR = "org.eclipse.cdt.ui.cEditorScope"; //$NON-NLS-1$
private static final String PEDITOR = "org.eclipse.pde.ui.pdeEditorContext"; //$NON-NLS-1$
private static final String RBYEDITOR = "org.rubypeople.rdt.ui.rubyEditorScope"; //$NON-NLS-1$
private static final String XEDITOR = "org.eclipse.wst.sse.ui.structuredTextEditorScope"; //$NON-NLS-1$
@SuppressWarnings("serial")
private static final Set<MinderBinder> restBindings = new HashSet<MinderBinder>() {{
// java bindings
add(new MinderBinder("org.eclipse.jdt.ui.edit.text.java.show.outline",JEDITOR,"CTRL+]")); //$NON-NLS-1$ //$NON-NLS-2$
add(new MinderBinder("org.eclipse.jdt.ui.edit.text.java.open.hierarchy",JEDITOR,"CTRL+[")); //$NON-NLS-1$ //$NON-NLS-2$
add(new MinderBinder("org.eclipse.jdt.ui.edit.text.java.toggle.comment",JEDITOR,"ESC ;")); //$NON-NLS-1$ //$NON-NLS-2$
add(new MinderBinder("com.mulgasoft.emacsplus.appendcomment",JEDITOR,"CTRL+;")); //$NON-NLS-1$ //$NON-NLS-2$
add(new MinderBinder("com.mulgasoft.emacsplus.preappendcomment",JEDITOR,"ESC P")); //$NON-NLS-1$ //$NON-NLS-2$
add(new MinderBinder("com.mulgasoft.emacsplus.preappendcomment",JEDITOR,"M3+P")); //$NON-NLS-1$ //$NON-NLS-2$
add(new MinderBinder("com.mulgasoft.emacsplus.postappendcomment",JEDITOR,"ESC N")); //$NON-NLS-1$ //$NON-NLS-2$
add(new MinderBinder("org.eclipse.jdt.ui.edit.text.java.open.editor",JEDITOR,"ESC .",MarkUtils.getTagListener())); //$NON-NLS-1$ //$NON-NLS-2$
// javascript bindings
add(new MinderBinder("org.eclipse.wst.jsdt.ui.edit.text.java.show.outline", JSEDITOR,"CTRL+]")); //$NON-NLS-1$ //$NON-NLS-2$
add(new MinderBinder("org.eclipse.wst.jsdt.ui.edit.text.java.open.hierarchy",JSEDITOR,"CTRL+[")); //$NON-NLS-1$ //$NON-NLS-2$
add(new MinderBinder("org.eclipse.wst.jsdt.ui.edit.text.java.toggle.comment",JSEDITOR,"ESC ;")); //$NON-NLS-1$ //$NON-NLS-2$
add(new MinderBinder("com.mulgasoft.emacsplus.appendcomment",JSEDITOR,"CTRL+;")); //$NON-NLS-1$ //$NON-NLS-2$
add(new MinderBinder("com.mulgasoft.emacsplus.preappendcomment",JSEDITOR,"ESC P")); //$NON-NLS-1$ //$NON-NLS-2$
add(new MinderBinder("com.mulgasoft.emacsplus.preappendcomment",JSEDITOR,"M3+P")); //$NON-NLS-1$ //$NON-NLS-2$
add(new MinderBinder("com.mulgasoft.emacsplus.postappendcomment",JSEDITOR,"ESC N")); //$NON-NLS-1$ //$NON-NLS-2$
add(new MinderBinder("org.eclipse.wst.jsdt.ui.edit.text.java.open.editor",JSVEDITOR,"ESC .",MarkUtils.getTagListener())); //$NON-NLS-1$ //$NON-NLS-2$
// pde bindings
add(new MinderBinder("org.eclipse.jdt.ui.edit.text.java.toggle.comment",PEDITOR,"ESC ;")); //$NON-NLS-1$ //$NON-NLS-2$
add(new MinderBinder("org.eclipse.pde.ui.quickOutline",PEDITOR,"CTRL+]")); //$NON-NLS-1$ //$NON-NLS-2$
// web tools xml bindings
add(new MinderBinder("org.eclipse.wst.sse.ui.toggle.comment",XEDITOR,"ESC ;")); //$NON-NLS-1$ //$NON-NLS-2$
// c++ bindings
add(new MinderBinder("org.eclipse.cdt.ui.edit.open.outline",CEDITOR,"CTRL+]")); //$NON-NLS-1$ //$NON-NLS-2$
add(new MinderBinder("org.eclipse.cdt.ui.edit.text.c.toggle.comment",CEDITOR,"ESC ;")); //$NON-NLS-1$ //$NON-NLS-2$
add(new MinderBinder("org.eclipse.cdt.ui.edit.open.quick.type.hierarchy",CEDITOR,"CTRL+[")); //$NON-NLS-1$ //$NON-NLS-2$
add(new MinderBinder("org.eclipse.cdt.ui.edit.opendecl",CEDITOR,"ESC .",MarkUtils.getTagListener())); //$NON-NLS-1$ //$NON-NLS-2$
// ruby bindings (aptana)
add(new MinderBinder("org.rubypeople.rdt.ui.edit.text.ruby.toggle.comment",RBYEDITOR,"ESC ;")); //$NON-NLS-1$ //$NON-NLS-2$
add(new MinderBinder("org.rubypeople.rdt.ui.edit.text.ruby.open.editor",RBYEDITOR,"ESC .",MarkUtils.getTagListener())); //$NON-NLS-1$ //$NON-NLS-2$
}};
@SuppressWarnings("serial")
private static final Set<MinderBinder> altBindings = new HashSet<MinderBinder>() {{
// java bindings
add(new MinderBinder("org.eclipse.jdt.ui.edit.text.java.select.enclosing",JEDITOR,"CTRL+M3+U")); //$NON-NLS-1$ //$NON-NLS-2$
add(new MinderBinder("org.eclipse.jdt.ui.edit.text.java.toggle.comment",JEDITOR,"M3+;")); //$NON-NLS-1$ //$NON-NLS-2$
add(new MinderBinder("org.eclipse.jdt.ui.edit.text.java.open.editor",JEDITOR,"M3+.",MarkUtils.getTagListener())); //$NON-NLS-1$ //$NON-NLS-2$
// javascript bindings
add(new MinderBinder("org.eclipse.wst.jsdt.ui.edit.text.java.select.enclosing",JSEDITOR,"CTRL+M3+U")); //$NON-NLS-1$ //$NON-NLS-2$
add(new MinderBinder("org.eclipse.wst.jsdt.ui.edit.text.java.toggle.comment",JSEDITOR,"M3+;")); //$NON-NLS-1$ //$NON-NLS-2$
add(new MinderBinder("org.eclipse.wst.jsdt.ui.edit.text.java.open.editor",JSVEDITOR,"M3+.",MarkUtils.getTagListener())); //$NON-NLS-1$ //$NON-NLS-2$
// pde bindings
add(new MinderBinder("org.eclipse.jdt.ui.edit.text.java.toggle.comment",PEDITOR,"M3+;")); //$NON-NLS-1$ //$NON-NLS-2$
// web tools xml bindings
add(new MinderBinder("org.eclipse.wst.sse.ui.toggle.comment",XEDITOR,"M3+;")); //$NON-NLS-1$ //$NON-NLS-2$
// c++ bindings
add(new MinderBinder("org.eclipse.cdt.ui.edit.text.c.toggle.comment",CEDITOR,"M3+;")); //$NON-NLS-1$ //$NON-NLS-2$
add(new MinderBinder("org.eclipse.cdt.ui.edit.opendecl",CEDITOR,"M3+.",MarkUtils.getTagListener())); //$NON-NLS-1$ //$NON-NLS-2$
// ruby bindings (aptana)
add(new MinderBinder("org.rubypeople.rdt.ui.edit.text.ruby.toggle.comment",RBYEDITOR,"M3+;")); //$NON-NLS-1$ //$NON-NLS-2$
add(new MinderBinder("org.rubypeople.rdt.ui.edit.text.ruby.open.editor",RBYEDITOR,"M3+.",MarkUtils.getTagListener())); //$NON-NLS-1$ //$NON-NLS-2$
}};
@SuppressWarnings("serial")
private static final Set<MinderBinder> nomacBindings = new HashSet<MinderBinder>() {{
// don't add on Mac OS X as it conflicts with <Option>+n non-spacing-mark ñ
add(new MinderBinder("com.mulgasoft.emacsplus.postappendcomment",JSEDITOR,"M3+N")); //$NON-NLS-1$ //$NON-NLS-2$
add(new MinderBinder("com.mulgasoft.emacsplus.postappendcomment",JEDITOR,"M3+N")); //$NON-NLS-1$ //$NON-NLS-2$
}};
@SuppressWarnings("serial")
private static final Set<MinderBinder> macBindings = new HashSet<MinderBinder>() {{
// NB: adding this to a plugin.xml doesn't work, so add it here
// TODO does a similar command exist on other platforms?
add(new MinderBinder("org.eclipse.ui.cocoa.fullscreenWindow",WINDOW,"CTRL+X CTRL+M")); //$NON-NLS-1$ //$NON-NLS-2$
}};
@SuppressWarnings("serial")
private static final Set<MinderBinder> cmdBindings = new HashSet<MinderBinder>() {{
// java bindings
add(new MinderBinder("org.eclipse.jdt.ui.edit.text.java.select.enclosing",JEDITOR,"CTRL+COMMAND+U")); //$NON-NLS-1$ //$NON-NLS-2$
add(new MinderBinder("org.eclipse.jdt.ui.edit.text.java.toggle.comment",JEDITOR,"COMMAND+;")); //$NON-NLS-1$ //$NON-NLS-2$
add(new MinderBinder("org.eclipse.jdt.ui.edit.text.java.open.editor",JEDITOR,"COMMAND+.",MarkUtils.getTagListener())); //$NON-NLS-1$ //$NON-NLS-2$
add(new MinderBinder("com.mulgasoft.emacsplus.preappendcomment",JEDITOR,"COMMAND+P")); //$NON-NLS-1$ //$NON-NLS-2$
add(new MinderBinder("com.mulgasoft.emacsplus.postappendcomment",JEDITOR,"COMMAND+N")); //$NON-NLS-1$ //$NON-NLS-2$
// javascript bindings
add(new MinderBinder("org.eclipse.wst.jsdt.ui.edit.text.java.select.enclosing",JSEDITOR,"CTRL+COMMAND+U")); //$NON-NLS-1$ //$NON-NLS-2$
add(new MinderBinder("org.eclipse.wst.jsdt.ui.edit.text.java.toggle.comment",JSEDITOR,"COMMAND+;")); //$NON-NLS-1$ //$NON-NLS-2$
add(new MinderBinder("com.mulgasoft.emacsplus.preappendcomment",JSEDITOR,"COMMAND+P")); //$NON-NLS-1$ //$NON-NLS-2$
add(new MinderBinder("com.mulgasoft.emacsplus.postappendcomment",JSEDITOR,"COMMAND+N")); //$NON-NLS-1$ //$NON-NLS-2$
add(new MinderBinder("org.eclipse.wst.jsdt.ui.edit.text.java.open.editor",JSVEDITOR,"COMMAND+.",MarkUtils.getTagListener())); //$NON-NLS-1$ //$NON-NLS-2$
// pde bindings
add(new MinderBinder("org.eclipse.jdt.ui.edit.text.java.toggle.comment",PEDITOR,"COMMAND+;")); //$NON-NLS-1$ //$NON-NLS-2$
// web tools xml bindings
add(new MinderBinder("org.eclipse.wst.sse.ui.toggle.comment",XEDITOR,"COMMAND+;")); //$NON-NLS-1$ //$NON-NLS-2$
// c++ bindings
add(new MinderBinder("org.eclipse.cdt.ui.edit.text.c.toggle.comment",CEDITOR,"COMMAND+;")); //$NON-NLS-1$ //$NON-NLS-2$
add(new MinderBinder("org.eclipse.cdt.ui.edit.opendecl",CEDITOR,"COMMAND+.",MarkUtils.getTagListener())); //$NON-NLS-1$ //$NON-NLS-2$
// ruby bindings (aptana)
add(new MinderBinder("org.rubypeople.rdt.ui.edit.text.ruby.toggle.comment",RBYEDITOR,"COMMAND+;")); //$NON-NLS-1$ //$NON-NLS-2$
add(new MinderBinder("org.rubypeople.rdt.ui.edit.text.ruby.open.editor",RBYEDITOR,"COMMAND+.",MarkUtils.getTagListener())); //$NON-NLS-1$ //$NON-NLS-2$
}};
}