blob: 1ce29045e81cc3ef19cb0ceb3257ff01e4ad6df4 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2005, 2006 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
*******************************************************************************/
package org.eclipse.ui.internal.keys;
import java.io.IOException;
import java.io.Reader;
import java.io.StringReader;
import java.io.StringWriter;
import java.io.Writer;
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 java.util.StringTokenizer;
import org.eclipse.core.commands.Command;
import org.eclipse.core.commands.ParameterizedCommand;
import org.eclipse.core.commands.common.HandleObject;
import org.eclipse.core.commands.common.NotDefinedException;
import org.eclipse.core.commands.util.Tracing;
import org.eclipse.core.runtime.IConfigurationElement;
import org.eclipse.core.runtime.IExtensionDelta;
import org.eclipse.core.runtime.IExtensionRegistry;
import org.eclipse.core.runtime.IRegistryChangeEvent;
import org.eclipse.core.runtime.Platform;
import org.eclipse.jface.bindings.Binding;
import org.eclipse.jface.bindings.BindingManager;
import org.eclipse.jface.bindings.Scheme;
import org.eclipse.jface.bindings.keys.IKeyLookup;
import org.eclipse.jface.bindings.keys.KeyBinding;
import org.eclipse.jface.bindings.keys.KeyLookupFactory;
import org.eclipse.jface.bindings.keys.KeySequence;
import org.eclipse.jface.bindings.keys.KeyStroke;
import org.eclipse.jface.bindings.keys.ParseException;
import org.eclipse.jface.bindings.keys.SWTKeySupport;
import org.eclipse.jface.contexts.IContextIds;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.jface.util.PropertyChangeEvent;
import org.eclipse.swt.SWT;
import org.eclipse.ui.IMemento;
import org.eclipse.ui.IWorkbenchPreferenceConstants;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.WorkbenchException;
import org.eclipse.ui.XMLMemento;
import org.eclipse.ui.commands.ICommandService;
import org.eclipse.ui.internal.WorkbenchPlugin;
import org.eclipse.ui.internal.misc.Policy;
import org.eclipse.ui.internal.registry.IWorkbenchRegistryConstants;
import org.eclipse.ui.internal.services.PreferencePersistence;
import org.eclipse.ui.keys.IBindingService;
/**
* <p>
* A static class for accessing the registry and the preference store.
* </p>
*
* @since 3.1
*/
public final class BindingPersistence extends PreferencePersistence {
/**
* Whether this class should print out debugging information when it reads
* in data, or writes to the preference store.
*/
private static final boolean DEBUG = Policy.DEBUG_KEY_BINDINGS;
/**
* The index of the active scheme configuration elements in the indexed
* array.
*
* @see BindingPersistence#read()
*/
private static final int INDEX_ACTIVE_SCHEME = 0;
/**
* The index of the binding definition configuration elements in the indexed
* array.
*
* @see BindingPersistence#read()
*/
private static final int INDEX_BINDING_DEFINITIONS = 1;
/**
* The index of the scheme definition configuration elements in the indexed
* array.
*
* @see BindingPersistence#read(BindingManager, ICommandService)
*/
private static final int INDEX_SCHEME_DEFINITIONS = 2;
/**
* The name of the default scope in 2.1.x.
*/
private static final String LEGACY_DEFAULT_SCOPE = "org.eclipse.ui.globalScope"; //$NON-NLS-1$
/**
* A look-up map for 2.1.x style <code>string</code> keys on a
* <code>keyBinding</code> element.
*/
private static final Map r2_1KeysByName = new HashMap();
static {
final IKeyLookup lookup = KeyLookupFactory.getDefault();
r2_1KeysByName.put(IKeyLookup.BACKSPACE_NAME, lookup
.formalKeyLookupInteger(IKeyLookup.BACKSPACE_NAME));
r2_1KeysByName.put(IKeyLookup.TAB_NAME, lookup
.formalKeyLookupInteger(IKeyLookup.TAB_NAME));
r2_1KeysByName.put(IKeyLookup.RETURN_NAME, lookup
.formalKeyLookupInteger(IKeyLookup.RETURN_NAME));
r2_1KeysByName.put(IKeyLookup.ENTER_NAME, lookup
.formalKeyLookupInteger(IKeyLookup.ENTER_NAME));
r2_1KeysByName.put(IKeyLookup.ESCAPE_NAME, lookup
.formalKeyLookupInteger(IKeyLookup.ESCAPE_NAME));
r2_1KeysByName.put(IKeyLookup.ESC_NAME, lookup
.formalKeyLookupInteger(IKeyLookup.ESC_NAME));
r2_1KeysByName.put(IKeyLookup.DELETE_NAME, lookup
.formalKeyLookupInteger(IKeyLookup.DELETE_NAME));
r2_1KeysByName.put(IKeyLookup.SPACE_NAME, lookup
.formalKeyLookupInteger(IKeyLookup.SPACE_NAME));
r2_1KeysByName.put(IKeyLookup.ARROW_UP_NAME, lookup
.formalKeyLookupInteger(IKeyLookup.ARROW_UP_NAME));
r2_1KeysByName.put(IKeyLookup.ARROW_DOWN_NAME, lookup
.formalKeyLookupInteger(IKeyLookup.ARROW_DOWN_NAME));
r2_1KeysByName.put(IKeyLookup.ARROW_LEFT_NAME, lookup
.formalKeyLookupInteger(IKeyLookup.ARROW_LEFT_NAME));
r2_1KeysByName.put(IKeyLookup.ARROW_RIGHT_NAME, lookup
.formalKeyLookupInteger(IKeyLookup.ARROW_RIGHT_NAME));
r2_1KeysByName.put(IKeyLookup.PAGE_UP_NAME, lookup
.formalKeyLookupInteger(IKeyLookup.PAGE_UP_NAME));
r2_1KeysByName.put(IKeyLookup.PAGE_DOWN_NAME, lookup
.formalKeyLookupInteger(IKeyLookup.PAGE_DOWN_NAME));
r2_1KeysByName.put(IKeyLookup.HOME_NAME, lookup
.formalKeyLookupInteger(IKeyLookup.HOME_NAME));
r2_1KeysByName.put(IKeyLookup.END_NAME, lookup
.formalKeyLookupInteger(IKeyLookup.END_NAME));
r2_1KeysByName.put(IKeyLookup.INSERT_NAME, lookup
.formalKeyLookupInteger(IKeyLookup.INSERT_NAME));
r2_1KeysByName.put(IKeyLookup.F1_NAME, lookup
.formalKeyLookupInteger(IKeyLookup.F1_NAME));
r2_1KeysByName.put(IKeyLookup.F2_NAME, lookup
.formalKeyLookupInteger(IKeyLookup.F2_NAME));
r2_1KeysByName.put(IKeyLookup.F3_NAME, lookup
.formalKeyLookupInteger(IKeyLookup.F3_NAME));
r2_1KeysByName.put(IKeyLookup.F4_NAME, lookup
.formalKeyLookupInteger(IKeyLookup.F4_NAME));
r2_1KeysByName.put(IKeyLookup.F5_NAME, lookup
.formalKeyLookupInteger(IKeyLookup.F5_NAME));
r2_1KeysByName.put(IKeyLookup.F6_NAME, lookup
.formalKeyLookupInteger(IKeyLookup.F6_NAME));
r2_1KeysByName.put(IKeyLookup.F7_NAME, lookup
.formalKeyLookupInteger(IKeyLookup.F7_NAME));
r2_1KeysByName.put(IKeyLookup.F8_NAME, lookup
.formalKeyLookupInteger(IKeyLookup.F8_NAME));
r2_1KeysByName.put(IKeyLookup.F9_NAME, lookup
.formalKeyLookupInteger(IKeyLookup.F9_NAME));
r2_1KeysByName.put(IKeyLookup.F10_NAME, lookup
.formalKeyLookupInteger(IKeyLookup.F10_NAME));
r2_1KeysByName.put(IKeyLookup.F11_NAME, lookup
.formalKeyLookupInteger(IKeyLookup.F11_NAME));
r2_1KeysByName.put(IKeyLookup.F12_NAME, lookup
.formalKeyLookupInteger(IKeyLookup.F12_NAME));
}
/**
* Converts a 2.1.x style key sequence (as parsed from the
* <code>string</code> attribute of the <code>keyBinding</code>) to a
* 3.1 key sequence.
*
* @param r21KeySequence
* The sequence of 2.1.x key strokes that should be converted
* into a 3.1 key sequence; never <code>null</code>.
* @return A 3.1 key sequence; never <code>null</code>.
*/
private static final KeySequence convert2_1Sequence(int[] r21KeySequence) {
final int r21KeySequenceLength = r21KeySequence.length;
final KeyStroke[] keyStrokes = new KeyStroke[r21KeySequenceLength];
for (int i = 0; i < r21KeySequenceLength; i++) {
keyStrokes[i] = convert2_1Stroke(r21KeySequence[i]);
}
return KeySequence.getInstance(keyStrokes);
}
/**
* Converts a 2.1.x style key stroke (as parsed from the <code>string</code>
* attribute of the <code>keyBinding</code> to a 3.1 key stroke.
*
* @param r21Stroke
* The 2.1.x stroke to convert; must never be <code>null</code>.
* @return A 3.1 key stroke; never <code>null</code>.
*/
private static final KeyStroke convert2_1Stroke(final int r21Stroke) {
return SWTKeySupport.convertAcceleratorToKeyStroke(r21Stroke);
}
/**
* Returns the default scheme identifier for the currently running
* application.
*
* @return The default scheme identifier (<code>String</code>); never
* <code>null</code>, but may be empty or point to an undefined
* scheme.
*/
static final String getDefaultSchemeId() {
final IPreferenceStore store = PlatformUI.getPreferenceStore();
return store
.getDefaultString(IWorkbenchPreferenceConstants.KEY_CONFIGURATION_ID);
}
/**
* Parses a 2.1.x <code>string</code> attribute of the
* <code>keyBinding</code> element.
*
* @param string
* The string to parse; must not be <code>null</code>.
* @return An array of integer values -- each integer representing a single
* key stroke. This array may be empty, but it is never
* <code>null</code>.
*/
private static final int[] parse2_1Sequence(final String string) {
final StringTokenizer stringTokenizer = new StringTokenizer(string);
final int length = stringTokenizer.countTokens();
final int[] strokes = new int[length];
for (int i = 0; i < length; i++) {
strokes[i] = parse2_1Stroke(stringTokenizer.nextToken());
}
return strokes;
}
/**
* Parses a single 2.1.x key stroke string, as provided by
* <code>parse2_1Sequence</code>.
*
* @param string
* The string to parse; must not be <code>null</code>.
* @return An single integer value representing this key stroke.
*/
private static final int parse2_1Stroke(final String string) {
final StringTokenizer stringTokenizer = new StringTokenizer(string,
KeyStroke.KEY_DELIMITER, true);
// Copy out the tokens so we have random access.
final int size = stringTokenizer.countTokens();
final String[] tokens = new String[size];
for (int i = 0; stringTokenizer.hasMoreTokens(); i++) {
tokens[i] = stringTokenizer.nextToken();
}
int value = 0;
if (size % 2 == 1) {
String token = tokens[size - 1];
final Integer integer = (Integer) r2_1KeysByName.get(token
.toUpperCase());
if (integer != null) {
value = integer.intValue();
} else if (token.length() == 1) {
value = token.toUpperCase().charAt(0);
}
if (value != 0) {
for (int i = 0; i < size - 1; i++) {
token = tokens[i];
if (i % 2 == 0) {
if (token.equalsIgnoreCase(IKeyLookup.CTRL_NAME)) {
if ((value & SWT.CTRL) != 0) {
return 0;
}
value |= SWT.CTRL;
} else if (token.equalsIgnoreCase(IKeyLookup.ALT_NAME)) {
if ((value & SWT.ALT) != 0) {
return 0;
}
value |= SWT.ALT;
} else if (token
.equalsIgnoreCase(IKeyLookup.SHIFT_NAME)) {
if ((value & SWT.SHIFT) != 0) {
return 0;
}
value |= SWT.SHIFT;
} else if (token
.equalsIgnoreCase(IKeyLookup.COMMAND_NAME)) {
if ((value & SWT.COMMAND) != 0) {
return 0;
}
value |= SWT.COMMAND;
} else {
return 0;
}
} else if (!KeyStroke.KEY_DELIMITER.equals(token)) {
return 0;
}
}
}
}
return value;
}
/**
* <p>
* Reads the registry and the preference store, and determines the
* identifier for the scheme that should be active. There is a complicated
* order of priorities for this. The registry will only be read if there is
* no user preference, and the default active scheme id is different than
* the default default active scheme id.
* </p>
* <ol>
* <li>A non-default preference.</li>
* <li>The legacy preference XML memento.</li>
* <li>A default preference value that is different than the default
* default active scheme id.</li>
* <li>The registry.</li>
* <li>The default default active scheme id.</li>
* </ol>
*
* @param configurationElements
* The configuration elements from the commands extension point;
* must not be <code>null</code>.
* @param configurationElementCount
* The number of configuration elements that are really in the
* array.
* @param preferences
* The memento wrapping the commands preference key; may be
* <code>null</code>.
* @param bindingManager
* The binding manager that should be updated with the active
* scheme. This binding manager must already have its schemes
* defined. This value must not be <code>null</code>.
*/
private static final void readActiveScheme(
final IConfigurationElement[] configurationElements,
final int configurationElementCount, final IMemento preferences,
final BindingManager bindingManager) {
// A non-default preference.
final IPreferenceStore store = PlatformUI.getPreferenceStore();
final String defaultActiveSchemeId = store
.getDefaultString(IWorkbenchPreferenceConstants.KEY_CONFIGURATION_ID);
final String preferenceActiveSchemeId = store
.getString(IWorkbenchPreferenceConstants.KEY_CONFIGURATION_ID);
if ((preferenceActiveSchemeId != null)
&& (!preferenceActiveSchemeId.equals(defaultActiveSchemeId))) {
try {
bindingManager.setActiveScheme(bindingManager
.getScheme(preferenceActiveSchemeId));
return;
} catch (final NotDefinedException e) {
// Let's keep looking....
}
}
// A legacy preference XML memento.
if (preferences != null) {
final IMemento[] preferenceMementos = preferences
.getChildren(TAG_ACTIVE_KEY_CONFIGURATION);
int preferenceMementoCount = preferenceMementos.length;
for (int i = preferenceMementoCount - 1; i >= 0; i--) {
final IMemento memento = preferenceMementos[i];
String id = memento.getString(ATT_KEY_CONFIGURATION_ID);
if (id != null) {
try {
bindingManager.setActiveScheme(bindingManager
.getScheme(id));
return;
} catch (final NotDefinedException e) {
// Let's keep looking....
}
}
}
}
// A default preference value that is different than the default.
if ((defaultActiveSchemeId != null && defaultActiveSchemeId.length() > 0)
&& (!defaultActiveSchemeId
.equals(IBindingService.DEFAULT_DEFAULT_ACTIVE_SCHEME_ID))) {
try {
bindingManager.setActiveScheme(bindingManager
.getScheme(defaultActiveSchemeId));
return;
} catch (final NotDefinedException e) {
// Let's keep looking....
}
}
// The registry.
for (int i = configurationElementCount - 1; i >= 0; i--) {
final IConfigurationElement configurationElement = configurationElements[i];
String id = configurationElement
.getAttribute(ATT_KEY_CONFIGURATION_ID);
if (id != null) {
try {
bindingManager
.setActiveScheme(bindingManager.getScheme(id));
return;
} catch (final NotDefinedException e) {
// Let's keep looking....
}
}
id = configurationElement.getAttribute(ATT_VALUE);
if (id != null) {
try {
bindingManager
.setActiveScheme(bindingManager.getScheme(id));
return;
} catch (final NotDefinedException e) {
// Let's keep looking....
}
}
}
// The default default active scheme id.
try {
bindingManager
.setActiveScheme(bindingManager
.getScheme(IBindingService.DEFAULT_DEFAULT_ACTIVE_SCHEME_ID));
} catch (final NotDefinedException e) {
//this is bad - the default default scheme should always exist
throw new Error(
"The default default active scheme id is not defined."); //$NON-NLS-1$
}
}
/**
* Reads all of the binding definitions from the preferences.
*
* @param preferences
* The memento for the commands preferences key.
* @param bindingManager
* The binding manager to which the bindings should be added;
* must not be <code>null</code>.
* @param commandService
* The command service for the workbench; must not be
* <code>null</code>.
*/
private static final void readBindingsFromPreferences(
final IMemento preferences, final BindingManager bindingManager,
final ICommandService commandService) {
List warningsToLog = new ArrayList(1);
if (preferences != null) {
final IMemento[] preferenceMementos = preferences
.getChildren(TAG_KEY_BINDING);
int preferenceMementoCount = preferenceMementos.length;
for (int i = preferenceMementoCount - 1; i >= 0; i--) {
final IMemento memento = preferenceMementos[i];
// Read out the command id.
String commandId = readOptional(memento, ATT_COMMAND_ID);
if (commandId == null) {
commandId = readOptional(memento, ATT_COMMAND);
}
final Command command;
if (commandId != null) {
command = commandService.getCommand(commandId);
} else {
command = null;
}
// Read out the scheme id.
String schemeId = readOptional(memento,
ATT_KEY_CONFIGURATION_ID);
if (schemeId == null) {
schemeId = readRequired(memento, ATT_CONFIGURATION,
warningsToLog,
"Key bindings need a scheme or key configuration"); //$NON-NLS-1$
if (schemeId == null) {
continue;
}
}
// Read out the context id.
String contextId = readOptional(memento, ATT_CONTEXT_ID);
if (contextId == null) {
contextId = readOptional(memento, ATT_SCOPE);
}
if (LEGACY_DEFAULT_SCOPE.equals(contextId)) {
contextId = null;
}
if (contextId == null) {
contextId = IContextIds.CONTEXT_ID_WINDOW;
}
// Read out the key sequence.
String keySequenceText = readOptional(memento, ATT_KEY_SEQUENCE);
KeySequence keySequence = null;
if (keySequenceText == null) {
keySequenceText = readRequired(memento, ATT_STRING,
warningsToLog,
"Key bindings need a key sequence or string"); //$NON-NLS-1$
if (keySequenceText == null) {
continue;
}
// The key sequence is in the old-style format.
keySequence = convert2_1Sequence(parse2_1Sequence(keySequenceText));
} else {
// The key sequence is in the new-style format.
try {
keySequence = KeySequence.getInstance(keySequenceText);
} catch (final ParseException e) {
addWarning(warningsToLog, "Could not parse", null, //$NON-NLS-1$
commandId, "keySequence", keySequenceText); //$NON-NLS-1$
continue;
}
if (keySequence.isEmpty() || !keySequence.isComplete()) {
addWarning(
warningsToLog,
"Key bindings cannot use an empty or incomplete key sequence", //$NON-NLS-1$
null, commandId, "keySequence", keySequence //$NON-NLS-1$
.toString());
continue;
}
}
// Read out the locale and platform.
final String locale = readOptional(memento, ATT_LOCALE);
final String platform = readOptional(memento, ATT_PLATFORM);
// Read out the parameters
final ParameterizedCommand parameterizedCommand;
if (command == null) {
parameterizedCommand = null;
} else {
parameterizedCommand = readParameters(memento,
warningsToLog, command);
}
final Binding binding = new KeyBinding(keySequence,
parameterizedCommand, schemeId, contextId, locale,
platform, null, Binding.USER);
bindingManager.addBinding(binding);
}
}
// If there were any warnings, then log them now.
logWarnings(warningsToLog,
"Warnings while parsing the key bindings from the preference store"); //$NON-NLS-1$
}
/**
* Reads all of the binding definitions from the commands extension point.
*
* @param configurationElements
* The configuration elements in the commands extension point;
* must not be <code>null</code>, but may be empty.
* @param configurationElementCount
* The number of configuration elements that are really in the
* array.
* @param bindingManager
* The binding manager to which the bindings should be added;
* must not be <code>null</code>.
* @param commandService
* The command service for the workbench; must not be
* <code>null</code>.
*/
private static final void readBindingsFromRegistry(
final IConfigurationElement[] configurationElements,
final int configurationElementCount,
final BindingManager bindingManager,
final ICommandService commandService) {
final Collection bindings = new ArrayList(configurationElementCount);
final List warningsToLog = new ArrayList(1);
for (int i = 0; i < configurationElementCount; i++) {
final IConfigurationElement configurationElement = configurationElements[i];
/*
* Read out the command id. Doing this before determining if the key
* binding is actually valid is a bit wasteful. However, it is
* helpful to have the command identifier when logging syntax
* errors.
*/
String commandId = configurationElement
.getAttribute(ATT_COMMAND_ID);
if ((commandId == null) || (commandId.length() == 0)) {
commandId = configurationElement.getAttribute(ATT_COMMAND);
}
if ((commandId != null) && (commandId.length() == 0)) {
commandId = null;
}
final Command command;
if (commandId != null) {
command = commandService.getCommand(commandId);
if (!command.isDefined()) {
// Reference to an undefined command. This is invalid.
addWarning(warningsToLog,
"Cannot bind to an undefined command", //$NON-NLS-1$
configurationElement, commandId);
continue;
}
} else {
command = null;
}
// Read out the scheme id.
String schemeId = configurationElement.getAttribute(ATT_SCHEME_ID);
if ((schemeId == null) || (schemeId.length() == 0)) {
schemeId = configurationElement
.getAttribute(ATT_KEY_CONFIGURATION_ID);
if ((schemeId == null) || (schemeId.length() == 0)) {
schemeId = configurationElement
.getAttribute(ATT_CONFIGURATION);
if ((schemeId == null) || (schemeId.length() == 0)) {
// The scheme id should never be null. This is invalid.
addWarning(warningsToLog, "Key bindings need a scheme", //$NON-NLS-1$
configurationElement, commandId);
continue;
}
}
}
// Read out the context id.
String contextId = configurationElement
.getAttribute(ATT_CONTEXT_ID);
if (LEGACY_DEFAULT_SCOPE.equals(contextId)) {
contextId = null;
} else if ((contextId == null) || (contextId.length() == 0)) {
contextId = configurationElement.getAttribute(ATT_SCOPE);
if (LEGACY_DEFAULT_SCOPE.equals(contextId)) {
contextId = null;
}
}
if ((contextId == null) || (contextId.length() == 0)) {
contextId = IContextIds.CONTEXT_ID_WINDOW;
}
// Read out the key sequence.
KeySequence keySequence = null;
String keySequenceText = configurationElement
.getAttribute(ATT_SEQUENCE);
if ((keySequenceText == null) || (keySequenceText.length() == 0)) {
keySequenceText = configurationElement
.getAttribute(ATT_KEY_SEQUENCE);
}
if ((keySequenceText == null) || (keySequenceText.length() == 0)) {
keySequenceText = configurationElement.getAttribute(ATT_STRING);
if ((keySequenceText == null)
|| (keySequenceText.length() == 0)) {
// The key sequence should never be null. This is pointless
addWarning(
warningsToLog,
"Defining a key binding with no key sequence has no effect", //$NON-NLS-1$
configurationElement, commandId);
continue;
}
// The key sequence is in the old-style format.
try {
keySequence = convert2_1Sequence(parse2_1Sequence(keySequenceText));
} catch (final IllegalArgumentException e) {
addWarning(warningsToLog, "Could not parse key sequence", //$NON-NLS-1$
configurationElement, commandId, "keySequence", //$NON-NLS-1$
keySequenceText);
continue;
}
} else {
// The key sequence is in the new-style format.
try {
keySequence = KeySequence.getInstance(keySequenceText);
} catch (final ParseException e) {
addWarning(warningsToLog, "Could not parse key sequence", //$NON-NLS-1$
configurationElement, commandId, "keySequence", //$NON-NLS-1$
keySequenceText);
continue;
}
if (keySequence.isEmpty() || !keySequence.isComplete()) {
addWarning(
warningsToLog,
"Key bindings should not have an empty or incomplete key sequence", //$NON-NLS-1$
configurationElement, commandId, "keySequence", //$NON-NLS-1$
keySequence.toString());
continue;
}
}
// Read out the locale and platform.
String locale = configurationElement.getAttribute(ATT_LOCALE);
if ((locale != null) && (locale.length() == 0)) {
locale = null;
}
String platform = configurationElement.getAttribute(ATT_PLATFORM);
if ((platform != null) && (platform.length() == 0)) {
platform = null;
}
// Read out the parameters, if any.
final ParameterizedCommand parameterizedCommand;
if (command == null) {
parameterizedCommand = null;
} else {
parameterizedCommand = readParameters(configurationElement,
warningsToLog, command);
}
final Binding binding = new KeyBinding(keySequence,
parameterizedCommand, schemeId, contextId, locale,
platform, null, Binding.SYSTEM);
bindings.add(binding);
}
final Binding[] bindingArray = (Binding[]) bindings
.toArray(new Binding[bindings.size()]);
bindingManager.setBindings(bindingArray);
logWarnings(
warningsToLog,
"Warnings while parsing the key bindings from the 'org.eclipse.ui.commands' extension point"); //$NON-NLS-1$
}
/**
* Reads all of the scheme definitions from the registry.
*
* @param configurationElements
* The configuration elements in the commands extension point;
* must not be <code>null</code>, but may be empty.
* @param configurationElementCount
* The number of configuration elements that are really in the
* array.
* @param bindingManager
* The binding manager to which the schemes should be added; must
* not be <code>null</code>.
*/
private static final void readSchemesFromRegistry(
final IConfigurationElement[] configurationElements,
final int configurationElementCount,
final BindingManager bindingManager) {
// Undefine all the previous handle objects.
final HandleObject[] handleObjects = bindingManager.getDefinedSchemes();
if (handleObjects != null) {
for (int i = 0; i < handleObjects.length; i++) {
handleObjects[i].undefine();
}
}
final List warningsToLog = new ArrayList(1);
for (int i = 0; i < configurationElementCount; i++) {
final IConfigurationElement configurationElement = configurationElements[i];
// Read out the attributes.
final String id = readRequired(configurationElement, ATT_ID,
warningsToLog, "Schemes need an id"); //$NON-NLS-1$
if (id == null) {
continue;
}
final String name = readRequired(configurationElement, ATT_NAME,
warningsToLog, "A scheme needs a name", id); //$NON-NLS-1$
if (name == null) {
continue;
}
final String description = readOptional(configurationElement,
ATT_DESCRIPTION);
String parentId = configurationElement.getAttribute(ATT_PARENT_ID);
if ((parentId != null) && (parentId.length() == 0)) {
parentId = configurationElement.getAttribute(ATT_PARENT);
if ((parentId != null) && (parentId.length() == 0)) {
parentId = null;
}
}
// Define the scheme.
final Scheme scheme = bindingManager.getScheme(id);
scheme.define(name, description, parentId);
}
logWarnings(
warningsToLog,
"Warnings while parsing the key bindings from the 'org.eclipse.ui.bindings', 'org.eclipse.ui.acceleratorConfigurations' and 'org.eclipse.ui.commands' extension point"); //$NON-NLS-1$
}
/**
* Writes the given active scheme and bindings to the preference store. Only
* bindings that are of the <code>Binding.USER</code> type will be
* written; the others will be ignored.
*
* @param activeScheme
* The scheme which should be persisted; may be <code>null</code>.
* @param bindings
* The bindings which should be persisted; may be
* <code>null</code>
* @throws IOException
* If something happens while trying to write to the workbench
* preference store.
*/
static final void write(final Scheme activeScheme, final Binding[] bindings)
throws IOException {
// Print out debugging information, if requested.
if (DEBUG) {
Tracing.printTrace("BINDINGS", "Persisting active scheme '" //$NON-NLS-1$ //$NON-NLS-2$
+ activeScheme.getId() + '\'');
Tracing.printTrace("BINDINGS", "Persisting bindings"); //$NON-NLS-1$ //$NON-NLS-2$
}
// Write the simple preference key to the UI preference store.
writeActiveScheme(activeScheme);
// Build the XML block for writing the bindings and active scheme.
final XMLMemento xmlMemento = XMLMemento
.createWriteRoot(EXTENSION_COMMANDS);
if (activeScheme != null) {
writeActiveSchemeToPreferences(xmlMemento, activeScheme);
}
if (bindings != null) {
final int bindingsLength = bindings.length;
for (int i = 0; i < bindingsLength; i++) {
final Binding binding = bindings[i];
if (binding.getType() == Binding.USER) {
writeBindingToPreferences(xmlMemento, binding);
}
}
}
// Write the XML block to the workbench preference store.
final IPreferenceStore preferenceStore = WorkbenchPlugin.getDefault()
.getPreferenceStore();
final Writer writer = new StringWriter();
try {
xmlMemento.save(writer);
preferenceStore.setValue(EXTENSION_COMMANDS, writer.toString());
} finally {
writer.close();
}
}
/**
* Writes the active scheme to its own preference key. This key is used by
* RCP applications as part of their plug-in customization.
*
* @param scheme
* The scheme to write to the preference store. If the scheme is
* <code>null</code>, then it is removed.
*/
private static final void writeActiveScheme(final Scheme scheme) {
final IPreferenceStore store = PlatformUI.getPreferenceStore();
final String schemeId = (scheme == null) ? null : scheme.getId();
final String defaultSchemeId = store
.getDefaultString(IWorkbenchPreferenceConstants.KEY_CONFIGURATION_ID);
if ((defaultSchemeId == null) ? (scheme != null) : (!defaultSchemeId
.equals(schemeId))) {
store.setValue(IWorkbenchPreferenceConstants.KEY_CONFIGURATION_ID,
scheme.getId());
} else {
store
.setToDefault(IWorkbenchPreferenceConstants.KEY_CONFIGURATION_ID);
}
}
/**
* Writes the active scheme to the memento. If the scheme is
* <code>null</code>, then all schemes in the memento are removed.
*
* @param memento
* The memento to which the scheme should be written; must not be
* <code>null</code>.
* @param scheme
* The scheme that should be written; must not be
* <code>null</code>.
*/
private static final void writeActiveSchemeToPreferences(
final IMemento memento, final Scheme scheme) {
// Add this active scheme, if it is not the default.
final IPreferenceStore store = PlatformUI.getPreferenceStore();
final String schemeId = scheme.getId();
final String defaultSchemeId = store
.getDefaultString(IWorkbenchPreferenceConstants.KEY_CONFIGURATION_ID);
if ((defaultSchemeId == null) ? (schemeId != null) : (!defaultSchemeId
.equals(schemeId))) {
final IMemento child = memento
.createChild(TAG_ACTIVE_KEY_CONFIGURATION);
child.putString(ATT_KEY_CONFIGURATION_ID, schemeId);
}
}
/**
* Writes the binding to the memento. This creates a new child element on
* the memento, and places the properties of the binding as its attributes.
*
* @param parent
* The parent memento for the binding element; must not be
* <code>null</code>.
* @param binding
* The binding to write; must not be <code>null</code>.
*/
private static final void writeBindingToPreferences(final IMemento parent,
final Binding binding) {
final IMemento element = parent.createChild(TAG_KEY_BINDING);
element.putString(ATT_CONTEXT_ID, binding.getContextId());
final ParameterizedCommand parameterizedCommand = binding
.getParameterizedCommand();
final String commandId = (parameterizedCommand == null) ? null
: parameterizedCommand.getId();
element.putString(ATT_COMMAND_ID, commandId);
element.putString(ATT_KEY_CONFIGURATION_ID, binding.getSchemeId());
element.putString(ATT_KEY_SEQUENCE, binding.getTriggerSequence()
.toString());
element.putString(ATT_LOCALE, binding.getLocale());
element.putString(ATT_PLATFORM, binding.getPlatform());
if (parameterizedCommand != null) {
final Map parameterizations = parameterizedCommand
.getParameterMap();
final Iterator parameterizationItr = parameterizations.entrySet()
.iterator();
while (parameterizationItr.hasNext()) {
final Map.Entry entry = (Map.Entry) parameterizationItr.next();
final String id = (String) entry.getKey();
final String value = (String) entry.getValue();
final IMemento parameterElement = element
.createChild(TAG_PARAMETER);
parameterElement.putString(ATT_ID, id);
parameterElement.putString(ATT_VALUE, value);
}
}
}
/**
* The binding manager which should be populated with the values from the
* registry and preference store; must not be <code>null</code>.
*/
private final BindingManager bindingManager;
/**
* The command service for the workbench; must not be <code>null</code>.
*/
private final ICommandService commandService;
/**
* Constructs a new instance of <code>BindingPersistence</code>.
*
* @param bindingManager
* The binding manager which should be populated with the values
* from the registry and preference store; must not be
* <code>null</code>.
* @param commandService
* The command service for the workbench; must not be
* <code>null</code>.
*/
BindingPersistence(final BindingManager bindingManager,
final ICommandService commandService) {
this.bindingManager = bindingManager;
this.commandService = commandService;
}
protected final boolean isChangeImportant(final IRegistryChangeEvent event) {
return false;
}
public boolean bindingsNeedUpdating(final IRegistryChangeEvent event) {
final IExtensionDelta[] acceleratorConfigurationDeltas = event
.getExtensionDeltas(
PlatformUI.PLUGIN_ID,
IWorkbenchRegistryConstants.PL_ACCELERATOR_CONFIGURATIONS);
if (acceleratorConfigurationDeltas.length == 0) {
final IExtensionDelta[] bindingDeltas = event.getExtensionDeltas(
PlatformUI.PLUGIN_ID,
IWorkbenchRegistryConstants.PL_BINDINGS);
if (bindingDeltas.length == 0) {
final IExtensionDelta[] commandDeltas = event
.getExtensionDeltas(PlatformUI.PLUGIN_ID,
IWorkbenchRegistryConstants.PL_COMMANDS);
if (commandDeltas.length == 0) {
final IExtensionDelta[] acceleratorScopeDeltas = event
.getExtensionDeltas(
PlatformUI.PLUGIN_ID,
IWorkbenchRegistryConstants.PL_ACCELERATOR_SCOPES);
if (acceleratorScopeDeltas.length == 0) {
final IExtensionDelta[] contextDeltas = event
.getExtensionDeltas(PlatformUI.PLUGIN_ID,
IWorkbenchRegistryConstants.PL_CONTEXTS);
if (contextDeltas.length == 0) {
final IExtensionDelta[] actionDefinitionDeltas = event
.getExtensionDeltas(
PlatformUI.PLUGIN_ID,
IWorkbenchRegistryConstants.PL_ACTION_DEFINITIONS);
if (actionDefinitionDeltas.length == 0) {
return false;
}
}
}
}
}
}
return true;
}
protected final boolean isChangeImportant(final PropertyChangeEvent event) {
return EXTENSION_COMMANDS.equals(event.getProperty());
}
/**
* Reads all of the binding information from the registry and from the
* preference store.
*/
protected final void read() {
super.read();
reRead();
}
public void reRead() {
// Create the extension registry mementos.
final IExtensionRegistry registry = Platform.getExtensionRegistry();
int activeSchemeElementCount = 0;
int bindingDefinitionCount = 0;
int schemeDefinitionCount = 0;
final IConfigurationElement[][] indexedConfigurationElements = new IConfigurationElement[3][];
// Sort the bindings extension point based on element name.
final IConfigurationElement[] bindingsExtensionPoint = registry
.getConfigurationElementsFor(EXTENSION_BINDINGS);
for (int i = 0; i < bindingsExtensionPoint.length; i++) {
final IConfigurationElement configurationElement = bindingsExtensionPoint[i];
final String name = configurationElement.getName();
// Check if it is a binding definition.
if (TAG_KEY.equals(name)) {
addElementToIndexedArray(configurationElement,
indexedConfigurationElements,
INDEX_BINDING_DEFINITIONS, bindingDefinitionCount++);
} else
// Check to see if it is a scheme definition.
if (TAG_SCHEME.equals(name)) {
addElementToIndexedArray(configurationElement,
indexedConfigurationElements, INDEX_SCHEME_DEFINITIONS,
schemeDefinitionCount++);
}
}
// Sort the commands extension point based on element name.
final IConfigurationElement[] commandsExtensionPoint = registry
.getConfigurationElementsFor(EXTENSION_COMMANDS);
for (int i = 0; i < commandsExtensionPoint.length; i++) {
final IConfigurationElement configurationElement = commandsExtensionPoint[i];
final String name = configurationElement.getName();
// Check if it is a binding definition.
if (TAG_KEY_BINDING.equals(name)) {
addElementToIndexedArray(configurationElement,
indexedConfigurationElements,
INDEX_BINDING_DEFINITIONS, bindingDefinitionCount++);
// Check if it is a scheme defintion.
} else if (TAG_KEY_CONFIGURATION.equals(name)) {
addElementToIndexedArray(configurationElement,
indexedConfigurationElements, INDEX_SCHEME_DEFINITIONS,
schemeDefinitionCount++);
// Check if it is an active scheme identifier.
} else if (TAG_ACTIVE_KEY_CONFIGURATION.equals(name)) {
addElementToIndexedArray(configurationElement,
indexedConfigurationElements, INDEX_ACTIVE_SCHEME,
activeSchemeElementCount++);
}
}
/*
* Sort the accelerator configuration extension point into the scheme
* definitions.
*/
final IConfigurationElement[] acceleratorConfigurationsExtensionPoint = registry
.getConfigurationElementsFor(EXTENSION_ACCELERATOR_CONFIGURATIONS);
for (int i = 0; i < acceleratorConfigurationsExtensionPoint.length; i++) {
final IConfigurationElement configurationElement = acceleratorConfigurationsExtensionPoint[i];
final String name = configurationElement.getName();
// Check if the name matches the accelerator configuration element
if (TAG_ACCELERATOR_CONFIGURATION.equals(name)) {
addElementToIndexedArray(configurationElement,
indexedConfigurationElements, INDEX_SCHEME_DEFINITIONS,
schemeDefinitionCount++);
}
}
// Create the preference memento.
final IPreferenceStore store = WorkbenchPlugin.getDefault()
.getPreferenceStore();
final String preferenceString = store.getString(EXTENSION_COMMANDS);
IMemento preferenceMemento = null;
if ((preferenceString != null) && (preferenceString.length() > 0)) {
final Reader reader = new StringReader(preferenceString);
try {
preferenceMemento = XMLMemento.createReadRoot(reader);
} catch (final WorkbenchException e) {
// Could not initialize the preference memento.
}
}
// Read the scheme definitions.
readSchemesFromRegistry(
indexedConfigurationElements[INDEX_SCHEME_DEFINITIONS],
schemeDefinitionCount, bindingManager);
readActiveScheme(indexedConfigurationElements[INDEX_ACTIVE_SCHEME],
activeSchemeElementCount, preferenceMemento, bindingManager);
readBindingsFromRegistry(
indexedConfigurationElements[INDEX_BINDING_DEFINITIONS],
bindingDefinitionCount, bindingManager, commandService);
readBindingsFromPreferences(preferenceMemento, bindingManager,
commandService);
}
}