blob: dd71efc428f50410e036dfe13c297137aa461f63 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2007, 2009 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.model;
import java.io.BufferedWriter;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.util.Map;
import java.util.ResourceBundle;
import org.eclipse.core.commands.CommandManager;
import org.eclipse.core.commands.ParameterizedCommand;
import org.eclipse.core.commands.common.NotDefinedException;
import org.eclipse.core.commands.contexts.ContextManager;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.ListenerList;
import org.eclipse.core.runtime.SafeRunner;
import org.eclipse.core.runtime.Status;
import org.eclipse.jface.bindings.Binding;
import org.eclipse.jface.bindings.BindingManager;
import org.eclipse.jface.bindings.Scheme;
import org.eclipse.jface.bindings.keys.KeyBinding;
import org.eclipse.jface.bindings.keys.KeySequence;
import org.eclipse.jface.util.IPropertyChangeListener;
import org.eclipse.jface.util.PropertyChangeEvent;
import org.eclipse.jface.util.SafeRunnable;
import org.eclipse.swt.SWT;
import org.eclipse.swt.widgets.FileDialog;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.ui.contexts.IContextService;
import org.eclipse.ui.internal.WorkbenchPlugin;
import org.eclipse.ui.internal.keys.KeysPreferencePage;
import org.eclipse.ui.internal.keys.NewKeysPreferenceMessages;
import org.eclipse.ui.internal.misc.StatusUtil;
import org.eclipse.ui.internal.util.Util;
import org.eclipse.ui.keys.IBindingService;
import org.eclipse.ui.services.IServiceLocator;
import org.eclipse.ui.statushandlers.StatusManager;
/**
* @since 3.4
*
*/
public class KeyController {
private static final String DELIMITER = ","; //$NON-NLS-1$
private static final String ESCAPED_QUOTE = "\""; //$NON-NLS-1$
private static final String REPLACEMENT = "\"\""; //$NON-NLS-1$
/**
* The resource bundle from which translations can be retrieved.
*/
private static final ResourceBundle RESOURCE_BUNDLE = ResourceBundle
.getBundle(KeysPreferencePage.class.getName());
private ListenerList eventManager = null;
private BindingManager fBindingManager;
private ContextModel contextModel;
private SchemeModel fSchemeModel;
private BindingModel bindingModel;
private boolean notifying = true;
private ConflictModel conflictModel;
private IServiceLocator serviceLocator;
private ListenerList getEventManager() {
if (eventManager == null) {
eventManager = new ListenerList(ListenerList.IDENTITY);
}
return eventManager;
}
public void setNotifying(boolean b) {
notifying = b;
}
public boolean isNotifying() {
return notifying;
}
public void firePropertyChange(Object source, String propId, Object oldVal,
Object newVal) {
if (!isNotifying()) {
return;
}
if (Util.equals(oldVal, newVal)) {
return;
}
Object[] listeners = getEventManager().getListeners();
PropertyChangeEvent event = new PropertyChangeEvent(source, propId,
oldVal, newVal);
for (int i = 0; i < listeners.length; i++) {
((IPropertyChangeListener) listeners[i]).propertyChange(event);
}
}
public void addPropertyChangeListener(IPropertyChangeListener listener) {
getEventManager().add(listener);
}
public void removePropertyChangeListener(IPropertyChangeListener listener) {
getEventManager().remove(listener);
}
public void init(IServiceLocator locator) {
getEventManager().clear();
this.serviceLocator = locator;
fBindingManager = loadModelBackend(serviceLocator);
contextModel = new ContextModel(this);
contextModel.init(serviceLocator);
fSchemeModel = new SchemeModel(this);
fSchemeModel.init(fBindingManager);
bindingModel = new BindingModel(this);
bindingModel.init(serviceLocator, fBindingManager, contextModel);
conflictModel = new ConflictModel(this);
conflictModel.init(fBindingManager, bindingModel);
addSetContextListener();
addSetBindingListener();
addSetConflictListener();
addSetKeySequenceListener();
addSetSchemeListener();
addSetModelObjectListener();
}
private static BindingManager loadModelBackend(IServiceLocator locator) {
IBindingService bindingService = (IBindingService) locator
.getService(IBindingService.class);
BindingManager bindingManager = new BindingManager(
new ContextManager(), new CommandManager());
final Scheme[] definedSchemes = bindingService.getDefinedSchemes();
try {
Scheme modelActiveScheme = null;
for (int i = 0; i < definedSchemes.length; i++) {
final Scheme scheme = definedSchemes[i];
final Scheme copy = bindingManager.getScheme(scheme.getId());
copy.define(scheme.getName(), scheme.getDescription(), scheme
.getParentId());
if (definedSchemes[i] == bindingService.getActiveScheme()) {
modelActiveScheme = copy;
}
}
bindingManager.setActiveScheme(modelActiveScheme);
} catch (final NotDefinedException e) {
StatusManager.getManager().handle(
new Status(IStatus.WARNING, WorkbenchPlugin.PI_WORKBENCH,
"Keys page found an undefined scheme", e)); //$NON-NLS-1$
}
bindingManager.setLocale(bindingService.getLocale());
bindingManager.setPlatform(bindingService.getPlatform());
bindingManager.setBindings(bindingService.getBindings());
return bindingManager;
}
public ContextModel getContextModel() {
return contextModel;
}
public SchemeModel getSchemeModel() {
return fSchemeModel;
}
public BindingModel getBindingModel() {
return bindingModel;
}
public ConflictModel getConflictModel() {
return conflictModel;
}
private void addSetContextListener() {
addPropertyChangeListener(new IPropertyChangeListener() {
public void propertyChange(PropertyChangeEvent event) {
if (event.getSource() == contextModel
&& CommonModel.PROP_SELECTED_ELEMENT.equals(event
.getProperty())) {
updateBindingContext((ContextElement) event.getNewValue());
}
}
});
}
private void addSetBindingListener() {
addPropertyChangeListener(new IPropertyChangeListener() {
public void propertyChange(PropertyChangeEvent event) {
if (event.getSource() == bindingModel
&& CommonModel.PROP_SELECTED_ELEMENT.equals(event
.getProperty())) {
BindingElement binding = (BindingElement) event
.getNewValue();
if (binding == null) {
conflictModel.setSelectedElement(null);
return;
}
conflictModel.setSelectedElement(binding);
ContextElement context = binding.getContext();
if (context != null) {
contextModel.setSelectedElement(context);
}
}
}
});
}
private void addSetConflictListener() {
addPropertyChangeListener(new IPropertyChangeListener() {
public void propertyChange(PropertyChangeEvent event) {
if (event.getSource() == conflictModel
&& CommonModel.PROP_SELECTED_ELEMENT.equals(event
.getProperty())) {
if (event.getNewValue() != null) {
bindingModel.setSelectedElement((ModelElement) event
.getNewValue());
}
}
}
});
}
private void addSetKeySequenceListener() {
addPropertyChangeListener(new IPropertyChangeListener() {
public void propertyChange(PropertyChangeEvent event) {
if (BindingElement.PROP_TRIGGER.equals(event.getProperty())) {
updateTrigger((BindingElement) event.getSource(),
(KeySequence) event.getOldValue(),
(KeySequence) event.getNewValue());
}
}
});
}
private void addSetModelObjectListener() {
addPropertyChangeListener(new IPropertyChangeListener() {
public void propertyChange(PropertyChangeEvent event) {
if (event.getSource() instanceof BindingElement
&& ModelElement.PROP_MODEL_OBJECT.equals(event
.getProperty())) {
if (event.getNewValue() != null) {
BindingElement element = (BindingElement) event
.getSource();
Object oldValue = event.getOldValue();
Object newValue = event.getNewValue();
if (oldValue instanceof Binding
&& newValue instanceof Binding) {
conflictModel.updateConflictsFor(element,
((Binding) oldValue).getTriggerSequence(),
((Binding) newValue).getTriggerSequence(),
false);
} else {
conflictModel.updateConflictsFor(element, false);
}
ContextElement context = element.getContext();
if (context != null) {
contextModel.setSelectedElement(context);
}
}
}
}
});
}
private void addSetSchemeListener() {
addPropertyChangeListener(new IPropertyChangeListener() {
public void propertyChange(PropertyChangeEvent event) {
if (event.getSource() == fSchemeModel
&& CommonModel.PROP_SELECTED_ELEMENT.equals(event
.getProperty())) {
changeScheme((SchemeElement) event.getOldValue(),
(SchemeElement) event.getNewValue());
}
}
});
}
/**
* @param oldScheme
* @param newScheme
*/
protected void changeScheme(SchemeElement oldScheme, SchemeElement newScheme) {
if (newScheme == null
|| newScheme.getModelObject() == fBindingManager
.getActiveScheme()) {
return;
}
try {
fBindingManager
.setActiveScheme((Scheme) newScheme.getModelObject());
bindingModel.refresh(contextModel);
bindingModel.setSelectedElement(null);
} catch (NotDefinedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
private void updateBindingContext(ContextElement context) {
if (context == null) {
return;
}
BindingElement activeBinding = (BindingElement) bindingModel
.getSelectedElement();
if (activeBinding == null) {
return;
}
String activeSchemeId = fSchemeModel.getSelectedElement().getId();
Object obj = activeBinding.getModelObject();
if (obj instanceof KeyBinding) {
KeyBinding keyBinding = (KeyBinding) obj;
if (!keyBinding.getContextId().equals(context.getId())) {
final KeyBinding binding = new KeyBinding(keyBinding
.getKeySequence(),
keyBinding.getParameterizedCommand(), activeSchemeId,
context.getId(), null, null, null, Binding.USER);
if (keyBinding.getType() == Binding.USER) {
fBindingManager.removeBinding(keyBinding);
} else {
fBindingManager.addBinding(new KeyBinding(keyBinding
.getKeySequence(), null, keyBinding.getSchemeId(),
keyBinding.getContextId(), null, null, null,
Binding.USER));
}
bindingModel.getBindingToElement().remove(
activeBinding.getModelObject());
fBindingManager.addBinding(binding);
activeBinding.fill(binding, contextModel);
bindingModel.getBindingToElement().put(binding, activeBinding);
}
}
}
/**
* @param activeBinding
* @param oldSequence
* @param keySequence
*/
public void updateTrigger(BindingElement activeBinding,
KeySequence oldSequence, KeySequence keySequence) {
if (activeBinding == null) {
return;
}
Object obj = activeBinding.getModelObject();
if (obj instanceof KeyBinding) {
KeyBinding keyBinding = (KeyBinding) obj;
if (!keyBinding.getKeySequence().equals(keySequence)) {
if (keySequence != null && !keySequence.isEmpty()) {
String activeSchemeId = fSchemeModel.getSelectedElement()
.getId();
ModelElement selectedElement = contextModel
.getSelectedElement();
String activeContextId = selectedElement == null ? IContextService.CONTEXT_ID_WINDOW
: selectedElement.getId();
final KeyBinding binding = new KeyBinding(keySequence,
keyBinding.getParameterizedCommand(),
activeSchemeId, activeContextId, null, null, null,
Binding.USER);
Map bindingToElement = bindingModel.getBindingToElement();
bindingToElement.remove(keyBinding);
if (keyBinding.getType() == Binding.USER) {
fBindingManager.removeBinding(keyBinding);
} else {
fBindingManager.addBinding(new KeyBinding(keyBinding
.getKeySequence(), null, keyBinding
.getSchemeId(), keyBinding.getContextId(),
null, null, null, Binding.USER));
}
fBindingManager.addBinding(binding);
activeBinding.fill(binding, contextModel);
bindingModel.getBindingToElement().put(binding,
activeBinding);
// Remove binding for any system conflicts
Object[] keys = bindingToElement.keySet().toArray();
for (int i = 0; i < keys.length; i++) {
Binding bindingKey = (Binding) keys[i];
if (oldSequence.equals(bindingKey.getTriggerSequence())
&& bindingKey.getType() == Binding.SYSTEM) {
BindingElement be = (BindingElement) bindingToElement
.get(bindingKey);
bindingModel.remove(be);
}
}
bindingModel.setSelectedElement(activeBinding);
} else {
bindingModel.getBindingToElement().remove(keyBinding);
if (keyBinding.getType() == Binding.USER) {
fBindingManager.removeBinding(keyBinding);
} else {
fBindingManager.addBinding(new KeyBinding(keyBinding
.getKeySequence(), null, keyBinding
.getSchemeId(), keyBinding.getContextId(),
null, null, null, Binding.USER));
}
activeBinding.fill(keyBinding.getParameterizedCommand());
}
}
} else if (obj instanceof ParameterizedCommand) {
ParameterizedCommand cmd = (ParameterizedCommand) obj;
if (keySequence != null && !keySequence.isEmpty()) {
String activeSchemeId = fSchemeModel.getSelectedElement()
.getId();
ModelElement selectedElement = contextModel
.getSelectedElement();
String activeContextId = selectedElement == null ? IContextService.CONTEXT_ID_WINDOW
: selectedElement.getId();
final KeyBinding binding = new KeyBinding(keySequence, cmd,
activeSchemeId, activeContextId, null, null, null,
Binding.USER);
fBindingManager.addBinding(binding);
activeBinding.fill(binding, contextModel);
bindingModel.getBindingToElement().put(binding, activeBinding);
}
}
}
/**
* Replaces all the current bindings with the bindings in the local copy of
* the binding manager.
*
* @param bindingService
* The binding service that saves the changes made to the local
* copy of the binding manager
*/
public void saveBindings(IBindingService bindingService) {
try {
bindingService.savePreferences(fBindingManager.getActiveScheme(),
fBindingManager.getBindings());
} catch (IOException e) {
logPreferenceStoreException(e);
}
}
/**
* Logs the given exception, and opens an error dialog saying that something
* went wrong. The exception is assumed to have something to do with the
* preference store.
*
* @param exception
* The exception to be logged; must not be <code>null</code>.
*/
private final void logPreferenceStoreException(final Throwable exception) {
final String message = NewKeysPreferenceMessages.PreferenceStoreError_Message;
String exceptionMessage = exception.getMessage();
if (exceptionMessage == null) {
exceptionMessage = message;
}
final IStatus status = new Status(IStatus.ERROR,
WorkbenchPlugin.PI_WORKBENCH, 0, exceptionMessage, exception);
WorkbenchPlugin.log(message, status);
StatusUtil.handleStatus(message, exception, StatusManager.SHOW);
}
/**
* Filters contexts for the When Combo.
*
* @param actionSets
* <code>true</code> to filter action set contexts
* @param internal
* <code>false</code> to filter internal contexts
*
*/
public void filterContexts(boolean actionSets, boolean internal) {
contextModel.filterContexts(actionSets, internal);
}
/**
* Sets the bindings to default.
*
* @param bindingService
*/
public void setDefaultBindings(IBindingService bindingService) {
// Fix the scheme in the local changes.
final String defaultSchemeId = bindingService.getDefaultSchemeId();
final Scheme defaultScheme = fBindingManager.getScheme(defaultSchemeId);
try {
fBindingManager.setActiveScheme(defaultScheme);
} catch (final NotDefinedException e) {
// At least we tried....
}
// Restore any User defined bindings
Binding[] bindings = fBindingManager.getBindings();
for (int i = 0; i < bindings.length; i++) {
if (bindings[i].getType() == Binding.USER) {
fBindingManager.removeBinding(bindings[i]);
}
}
bindingModel.refresh(contextModel);
saveBindings(bindingService);
}
public void exportCSV(Shell shell) {
final FileDialog fileDialog = new FileDialog(shell, SWT.SAVE
| SWT.SHEET);
fileDialog.setFilterExtensions(new String[] { "*.csv" }); //$NON-NLS-1$
fileDialog.setFilterNames(new String[] { Util.translateString(
RESOURCE_BUNDLE, "csvFilterName") }); //$NON-NLS-1$
fileDialog.setOverwrite(true);
final String filePath = fileDialog.open();
if (filePath == null) {
return;
}
final SafeRunnable runnable = new SafeRunnable() {
public final void run() throws IOException {
Writer fileWriter = null;
try {
fileWriter = new BufferedWriter(new OutputStreamWriter(
new FileOutputStream(filePath), "UTF-8")); //$NON-NLS-1$
final Object[] bindingElements = bindingModel.getBindings()
.toArray();
for (int i = 0; i < bindingElements.length; i++) {
final BindingElement be = (BindingElement) bindingElements[i];
if (be.getTrigger() == null
|| be.getTrigger().isEmpty()) {
continue;
}
StringBuffer buffer = new StringBuffer();
buffer.append(ESCAPED_QUOTE
+ Util.replaceAll(be.getCategory(),
ESCAPED_QUOTE, REPLACEMENT)
+ ESCAPED_QUOTE + DELIMITER);
buffer.append(ESCAPED_QUOTE + be.getName()
+ ESCAPED_QUOTE + DELIMITER);
buffer.append(ESCAPED_QUOTE + be.getTrigger().format()
+ ESCAPED_QUOTE + DELIMITER);
buffer.append(ESCAPED_QUOTE + be.getContext().getName()
+ ESCAPED_QUOTE);
buffer.append(System.getProperty("line.separator")); //$NON-NLS-1$
fileWriter.write(buffer.toString());
}
} finally {
if (fileWriter != null) {
try {
fileWriter.close();
} catch (final IOException e) {
// At least I tried.
}
}
}
}
};
SafeRunner.run(runnable);
}
}