| /******************************************************************************* |
| * 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.net.URL; |
| import com.ibm.icu.text.Collator; |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.Collection; |
| import java.util.HashMap; |
| import java.util.HashSet; |
| import java.util.Iterator; |
| import java.util.Map; |
| import java.util.Set; |
| |
| import org.eclipse.core.commands.Category; |
| import org.eclipse.core.commands.Command; |
| import org.eclipse.core.commands.CommandManager; |
| import org.eclipse.core.commands.ParameterizedCommand; |
| import org.eclipse.core.commands.common.NamedHandleObject; |
| import org.eclipse.core.commands.common.NamedHandleObjectComparator; |
| 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.IStatus; |
| 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.bindings.keys.KeySequenceText; |
| import org.eclipse.jface.bindings.keys.KeyStroke; |
| import org.eclipse.jface.contexts.IContextIds; |
| import org.eclipse.jface.dialogs.ErrorDialog; |
| import org.eclipse.jface.dialogs.IDialogConstants; |
| import org.eclipse.jface.dialogs.MessageDialog; |
| import org.eclipse.jface.preference.PreferencePage; |
| import org.eclipse.jface.resource.DeviceResourceException; |
| import org.eclipse.jface.resource.ImageDescriptor; |
| import org.eclipse.jface.resource.JFaceResources; |
| import org.eclipse.jface.resource.LocalResourceManager; |
| import org.eclipse.jface.util.IPropertyChangeListener; |
| import org.eclipse.jface.util.PropertyChangeEvent; |
| import org.eclipse.jface.viewers.ArrayContentProvider; |
| import org.eclipse.jface.viewers.ComboViewer; |
| import org.eclipse.jface.viewers.ISelection; |
| import org.eclipse.jface.viewers.ISelectionChangedListener; |
| import org.eclipse.jface.viewers.IStructuredSelection; |
| import org.eclipse.jface.viewers.ITableLabelProvider; |
| import org.eclipse.jface.viewers.LabelProvider; |
| import org.eclipse.jface.viewers.NamedHandleObjectLabelProvider; |
| import org.eclipse.jface.viewers.SelectionChangedEvent; |
| import org.eclipse.jface.viewers.StructuredSelection; |
| import org.eclipse.jface.viewers.TreeNode; |
| import org.eclipse.jface.viewers.TreeNodeContentProvider; |
| import org.eclipse.jface.viewers.TreeViewer; |
| import org.eclipse.jface.viewers.Viewer; |
| import org.eclipse.jface.viewers.ViewerComparator; |
| import org.eclipse.swt.SWT; |
| import org.eclipse.swt.events.MouseAdapter; |
| import org.eclipse.swt.events.MouseEvent; |
| import org.eclipse.swt.events.SelectionAdapter; |
| import org.eclipse.swt.events.SelectionEvent; |
| import org.eclipse.swt.graphics.Image; |
| import org.eclipse.swt.graphics.Point; |
| import org.eclipse.swt.graphics.Rectangle; |
| import org.eclipse.swt.layout.GridData; |
| import org.eclipse.swt.layout.GridLayout; |
| import org.eclipse.swt.widgets.Button; |
| import org.eclipse.swt.widgets.Combo; |
| import org.eclipse.swt.widgets.Composite; |
| import org.eclipse.swt.widgets.Control; |
| import org.eclipse.swt.widgets.Label; |
| import org.eclipse.swt.widgets.Menu; |
| import org.eclipse.swt.widgets.MenuItem; |
| import org.eclipse.swt.widgets.Text; |
| import org.eclipse.swt.widgets.Tree; |
| import org.eclipse.swt.widgets.TreeColumn; |
| import org.eclipse.swt.widgets.TreeItem; |
| import org.eclipse.ui.IWorkbench; |
| import org.eclipse.ui.IWorkbenchPreferencePage; |
| import org.eclipse.ui.PlatformUI; |
| import org.eclipse.ui.commands.ICommandService; |
| import org.eclipse.ui.contexts.IContextService; |
| import org.eclipse.ui.dialogs.FilteredTree; |
| import org.eclipse.ui.dialogs.PatternFilter; |
| import org.eclipse.ui.internal.WorkbenchPlugin; |
| import org.eclipse.ui.internal.commands.ICommandImageService; |
| import org.eclipse.ui.internal.util.BundleUtility; |
| import org.eclipse.ui.internal.util.Util; |
| import org.eclipse.ui.keys.IBindingService; |
| |
| /** |
| * <p> |
| * A preference page that is capable of displaying and editing the bindings |
| * between commands and user input events. These are typically things like |
| * keyboard shortcuts. |
| * </p> |
| * <p> |
| * This preference page has four general types of methods. Create methods are |
| * called when the page is first made visisble. They are responsible for |
| * creating all of the widgets, and laying them out within the preference page. |
| * Fill methods populate the contents of the widgets that contain collections of |
| * data from which items can be selected. The select methods respond to |
| * selection events from the user, such as a button press or a table selection. |
| * The update methods update the contents of various widgets based on the |
| * current state of the user interface. For example, the command name label will |
| * always try to match the current select in the binding table. |
| * </p> |
| * |
| * @since 3.2 |
| */ |
| public final class NewKeysPreferencePage extends PreferencePage implements |
| IWorkbenchPreferencePage { |
| |
| /** |
| * A FilteredTree that provides a combo which is used to organize and display |
| * elements in the tree according to the selected criteria. |
| * |
| */ |
| protected class GroupedFilteredTree extends FilteredTree { |
| /** |
| * The combo box containing all of the possible grouping options. This value |
| * may be <code>null</code> if there are no grouping controls, or if the |
| * controls have not yet been created. |
| */ |
| protected Combo groupingCombo; |
| |
| /** |
| * Constructor for GroupedFilteredTree. |
| * |
| * @param parent |
| * @param treeStyle |
| * @param filter |
| */ |
| protected GroupedFilteredTree(Composite parent, int treeStyle, PatternFilter filter){ |
| super(parent, treeStyle, filter); |
| } |
| |
| protected void createControl(final Composite parent, final int treeStyle) { |
| GridData gridData; |
| GridLayout layout; |
| |
| layout = new GridLayout(); |
| // Why doesn't this seem to be working?? |
| layout.marginHeight = 0; |
| layout.marginWidth = 0; |
| setLayout(layout); |
| setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); |
| setFont(parent.getFont()); |
| |
| // Create the filter controls |
| filterComposite = new Composite(this, SWT.NONE); |
| GridLayout filterLayout = new GridLayout(3, false); |
| filterLayout.marginHeight = 0; |
| filterLayout.marginWidth = 0; |
| filterComposite.setLayout(filterLayout); |
| filterComposite.setFont(parent.getFont()); |
| |
| createFilterControls(filterComposite); |
| filterComposite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false)); |
| |
| // Create the grouping control |
| final Control groupingControl = createGroupingControl(filterComposite); |
| groupingControl.setLayoutData(new GridData()); |
| groupingControl.setFont(parent.getFont()); |
| |
| // Create a table tree viewer. |
| final Control treeControl = createTreeControl(this, treeStyle); |
| gridData = new GridData(SWT.FILL, SWT.FILL, true, true); |
| gridData.horizontalSpan = 3; |
| treeControl.setLayoutData(gridData); |
| } |
| |
| /** |
| * <p> |
| * Creates the grouping controls that will appear in the top-right in the |
| * default layout. The default grouping controls are a label and a combo |
| * box. |
| * </p> |
| * <p> |
| * Subclasses may extend or override this method. Before this method |
| * completes, <code>groupingCombo</code> should be initialized. |
| * Subclasses must create a combo box which contains the |
| * possible groupings. |
| * </p> |
| * |
| * @param parent |
| * The composite in which the grouping control should be placed; |
| * must not be <code>null</code>. |
| * @return The composite containing the grouping controls, or the grouping |
| * control itself (if there is only one control). |
| */ |
| protected Control createGroupingControl(final Composite parent) { |
| // Create the composite that will contain the grouping controls. |
| Composite groupingControl = new Composite(parent, SWT.NONE); |
| GridLayout layout = new GridLayout(2, false); |
| layout.marginWidth = 0; |
| layout.marginHeight = 0; |
| groupingControl.setLayout(layout); |
| groupingControl.setFont(parent.getFont()); |
| |
| // The label providing some direction as to what the combo represents. |
| Label groupingLabel = new Label(groupingControl, SWT.NONE); |
| groupingLabel.setText(NewKeysPreferenceMessages.GroupingCombo_Label); |
| groupingLabel.setLayoutData(new GridData()); |
| |
| // The combo box |
| groupingCombo = new Combo(groupingControl, SWT.READ_ONLY); |
| GridData gridData = new GridData(); |
| gridData.grabExcessHorizontalSpace = true; |
| gridData.horizontalAlignment = SWT.FILL; |
| groupingCombo.setLayoutData(gridData); |
| |
| return groupingControl; |
| } |
| |
| /** |
| * Returns the combo box which controls the grouping of the items in the |
| * tree. This combo box is created in |
| * {@link GroupedFilteredTree#createGroupingControl(Composite)}. |
| * |
| * @return The grouping combo. This value may be <code>null</code> if |
| * there is no grouping control, or if the grouping control has not |
| * yet been created. |
| */ |
| public final Combo getGroupingCombo() { |
| return groupingCombo; |
| } |
| } |
| |
| /** |
| * A label provider that simply extracts the command name and the formatted |
| * trigger sequence from a given binding, and matches them to the correct |
| * column. |
| */ |
| private final class BindingLabelProvider extends LabelProvider implements |
| ITableLabelProvider { |
| |
| /** |
| * The index of the column with the button for adding an item. |
| */ |
| private static final int COLUMN_ADD = 2; |
| |
| /** |
| * The index of the column containing the command name. |
| */ |
| private static final int COLUMN_COMMAND = 0; |
| |
| /** |
| * The index of the column with the button for removing an item. |
| */ |
| private static final int COLUMN_REMOVE = 3; |
| |
| /** |
| * The index of the column containing the trigger sequence. |
| */ |
| private static final int COLUMN_TRIGGER_SEQUENCE = 1; |
| |
| /** |
| * The number of columns being displayed. |
| */ |
| private static final int NUMBER_OF_COLUMNS = 4; |
| |
| /** |
| * A resource manager for this preference page. |
| */ |
| private final LocalResourceManager localResourceManager = new LocalResourceManager( |
| JFaceResources.getResources()); |
| |
| public final void dispose() { |
| super.dispose(); |
| localResourceManager.dispose(); |
| } |
| |
| public final Image getColumnImage(final Object element, |
| final int columnIndex) { |
| final Object value = ((TreeNode) element).getValue(); |
| if (value instanceof Binding) { |
| switch (columnIndex) { |
| case COLUMN_COMMAND: |
| final ParameterizedCommand parameterizedCommand = ((Binding) value) |
| .getParameterizedCommand(); |
| if (parameterizedCommand != null) { |
| final String commandId = parameterizedCommand.getId(); |
| final ImageDescriptor imageDescriptor = commandImageService |
| .getImageDescriptor(commandId); |
| if (imageDescriptor == null) { |
| return null; |
| } |
| try { |
| return localResourceManager |
| .createImage(imageDescriptor); |
| } catch (final DeviceResourceException e) { |
| final String message = "Problem retrieving image for a command '" //$NON-NLS-1$ |
| + commandId + '\''; |
| final IStatus status = new Status(IStatus.ERROR, |
| WorkbenchPlugin.PI_WORKBENCH, 0, message, e); |
| WorkbenchPlugin.log(message, status); |
| } |
| } |
| return null; |
| |
| case COLUMN_ADD: |
| return ImageFactory.getImage("plus"); //$NON-NLS-1$ |
| |
| case COLUMN_REMOVE: |
| return ImageFactory.getImage("minus"); //$NON-NLS-1$ |
| } |
| |
| } else if (value instanceof ParameterizedCommand) { |
| switch (columnIndex) { |
| case COLUMN_COMMAND: |
| final ParameterizedCommand parameterizedCommand = (ParameterizedCommand) value; |
| final String commandId = parameterizedCommand.getId(); |
| final ImageDescriptor imageDescriptor = commandImageService |
| .getImageDescriptor(commandId); |
| if (imageDescriptor == null) { |
| return null; |
| } |
| try { |
| return localResourceManager |
| .createImage(imageDescriptor); |
| } catch (final DeviceResourceException e) { |
| final String message = "Problem retrieving image for a command '" //$NON-NLS-1$ |
| + commandId + '\''; |
| final IStatus status = new Status(IStatus.ERROR, |
| WorkbenchPlugin.PI_WORKBENCH, 0, message, e); |
| WorkbenchPlugin.log(message, status); |
| } |
| return null; |
| } |
| |
| } else if ((value instanceof Category) || (value instanceof String)) { |
| switch (columnIndex) { |
| case COLUMN_COMMAND: |
| final URL url = BundleUtility.find(PlatformUI.PLUGIN_ID, |
| ICON_GROUP_OF_BINDINGS); |
| final ImageDescriptor imageDescriptor = ImageDescriptor |
| .createFromURL(url); |
| try { |
| return localResourceManager |
| .createImage(imageDescriptor); |
| } catch (final DeviceResourceException e) { |
| final String message = "Problem retrieving image for groups of bindings: '" //$NON-NLS-1$ |
| + ICON_GROUP_OF_BINDINGS + '\''; |
| final IStatus status = new Status(IStatus.ERROR, |
| WorkbenchPlugin.PI_WORKBENCH, 0, message, e); |
| WorkbenchPlugin.log(message, status); |
| } |
| } |
| |
| } |
| |
| return null; |
| } |
| |
| public final String getColumnText(final Object element, |
| final int columnIndex) { |
| final Object value = ((TreeNode) element).getValue(); |
| if (value instanceof Binding) { |
| final Binding binding = (Binding) value; |
| switch (columnIndex) { |
| case COLUMN_COMMAND: |
| try { |
| return binding.getParameterizedCommand().getName(); |
| } catch (final NotDefinedException e) { |
| return null; |
| } |
| case COLUMN_TRIGGER_SEQUENCE: |
| return binding.getTriggerSequence().format(); |
| default: |
| return null; |
| } |
| } else if (value instanceof Category) { |
| if (columnIndex == COLUMN_COMMAND) { |
| try { |
| return ((Category) value).getName(); |
| } catch (final NotDefinedException e) { |
| return null; |
| } |
| } |
| |
| return null; |
| |
| } else if (value instanceof String) { |
| // This is a context. |
| if (columnIndex == COLUMN_COMMAND) { |
| try { |
| return contextService.getContext((String) value) |
| .getName(); |
| } catch (final NotDefinedException e) { |
| return null; |
| } |
| } |
| |
| return null; |
| } else if (value instanceof ParameterizedCommand) { |
| if (columnIndex == COLUMN_COMMAND) { |
| try { |
| return ((ParameterizedCommand) value).getName(); |
| } catch (final NotDefinedException e) { |
| return null; |
| } |
| } |
| |
| return null; |
| } |
| |
| return null; |
| } |
| } |
| |
| /** |
| * Sorts the bindings in the filtered tree based on the current grouping. |
| */ |
| private final class BindingComparator extends ViewerComparator { |
| |
| public final int category(final Object element) { |
| switch (grouping) { |
| case GROUPING_CATEGORY: |
| // TODO This has to be done with something other than the hash. |
| try { |
| final ParameterizedCommand command = (element instanceof ParameterizedCommand) ? (ParameterizedCommand) element |
| : ((Binding) element).getParameterizedCommand(); |
| return command.getCommand().getCategory().hashCode(); |
| } catch (final NotDefinedException e) { |
| return 0; |
| } |
| case GROUPING_CONTEXT: |
| // TODO This has to be done with something other than the hash. |
| if (element instanceof Binding) { |
| return ((Binding) element).getContextId().hashCode(); |
| } |
| case GROUPING_NONE: |
| default: |
| return 0; |
| } |
| } |
| |
| public final int compare(final Viewer viewer, final Object a, |
| final Object b) { |
| final String selectedText = filteredTree.getGroupingCombo() |
| .getText(); |
| try { |
| if (NewKeysPreferenceMessages.GroupingCombo_Category_Text |
| .equals(selectedText)) { |
| // The tree node values will be Category and Binding |
| // instances. |
| final Object x = ((TreeNode) a).getValue(); |
| final Object y = ((TreeNode) b).getValue(); |
| |
| /* |
| * Check to see if any of the objects can be converted to |
| * parameterized commands. |
| */ |
| ParameterizedCommand commandA = null; |
| if (x instanceof ParameterizedCommand) { |
| commandA = (ParameterizedCommand) x; |
| } else if (x instanceof Binding) { |
| commandA = ((Binding) x).getParameterizedCommand(); |
| } |
| ParameterizedCommand commandB = null; |
| if (y instanceof ParameterizedCommand) { |
| commandB = (ParameterizedCommand) y; |
| } else if (y instanceof Binding) { |
| commandB = ((Binding) y).getParameterizedCommand(); |
| } |
| |
| if ((x instanceof Category) && (y instanceof Category)) { |
| return Util.compare(((Category) x).getName(), |
| ((Category) y).getName()); |
| |
| } else if ((commandA != null) && (commandB != null)) { |
| return Util.compare(commandA, commandB); |
| |
| } else if ((x instanceof Category) && (commandB != null)) { |
| final int compare = Util.compare(((Category) x) |
| .getName(), commandB.getCommand().getCategory() |
| .getName()); |
| return (compare == 0) ? -1 : compare; |
| |
| } else if ((y instanceof Category) && (commandA != null)) { |
| final int compare = Util.compare(((Category) y) |
| .getName(), commandA.getCommand().getCategory() |
| .getName()); |
| return (compare == 0) ? 1 : compare; |
| |
| } |
| |
| } else if (NewKeysPreferenceMessages.GroupingCombo_When_Text |
| .equals(selectedText)) { |
| // The tree node values will be String and Binding |
| // instances. |
| final Object x = ((TreeNode) a).getValue(); |
| final Object y = ((TreeNode) b).getValue(); |
| |
| if ((x instanceof String) && (y instanceof String)) { |
| return Util.compare(contextService.getContext( |
| (String) x).getName(), contextService |
| .getContext((String) y).getName()); |
| |
| } else if ((x instanceof Binding) && (y instanceof Binding)) { |
| return Util.compare(((Binding) x) |
| .getParameterizedCommand(), ((Binding) y) |
| .getParameterizedCommand()); |
| |
| } else if ((x instanceof ParameterizedCommand) |
| && (y instanceof ParameterizedCommand)) { |
| return Util.compare((ParameterizedCommand) x, |
| (ParameterizedCommand) y); |
| |
| } else if ((x instanceof String) && (y instanceof Binding)) { |
| final int compare = Util |
| .compare(contextService.getContext((String) x) |
| .getName(), contextService.getContext( |
| ((Binding) y).getContextId()).getName()); |
| return (compare == 0) ? -1 : compare; |
| |
| } else if ((y instanceof String) && (x instanceof Binding)) { |
| final int compare = Util.compare(contextService |
| .getContext(((Binding) x).getContextId()) |
| .getName(), contextService.getContext( |
| (String) y).getName()); |
| return (compare == 0) ? 1 : compare; |
| |
| } else if ((x instanceof String) |
| && (y instanceof ParameterizedCommand)) { |
| final int compare = Util.compare(contextService |
| .getContext((String) x).getName(), |
| contextService.getContext( |
| IContextIds.CONTEXT_ID_WINDOW) |
| .getName()); |
| return (compare == 0) ? -1 : compare; |
| |
| } else if ((y instanceof String) |
| && (x instanceof ParameterizedCommand)) { |
| final int compare = Util.compare(contextService |
| .getContext(IContextIds.CONTEXT_ID_WINDOW) |
| .getName(), contextService.getContext( |
| (String) y).getName()); |
| return (compare == 0) ? 1 : compare; |
| |
| } else if ((x instanceof Binding) |
| && (y instanceof ParameterizedCommand)) { |
| final ParameterizedCommand commandX = ((Binding) x) |
| .getParameterizedCommand(); |
| final ParameterizedCommand commandY = (ParameterizedCommand) y; |
| final int compare = Util.compare(commandX, commandY); |
| return (compare == 0) ? -1 : compare; |
| |
| } else if ((y instanceof Binding) |
| && (x instanceof ParameterizedCommand)) { |
| final ParameterizedCommand commandY = ((Binding) y) |
| .getParameterizedCommand(); |
| final ParameterizedCommand commandX = (ParameterizedCommand) x; |
| final int compare = Util.compare(commandX, commandY); |
| return (compare == 0) ? 1 : compare; |
| |
| } |
| |
| } else { // (GROUPING_NONE_NAME.equals(selectedText)) |
| /* |
| * The tree node values will be Binding or |
| * ParameterizedCommand instances. |
| */ |
| final Object x = ((TreeNode) a).getValue(); |
| final Object y = ((TreeNode) b).getValue(); |
| final ParameterizedCommand commandX = (x instanceof Binding) ? ((Binding) x) |
| .getParameterizedCommand() |
| : (ParameterizedCommand) x; |
| final ParameterizedCommand commandY = (y instanceof Binding) ? ((Binding) y) |
| .getParameterizedCommand() |
| : (ParameterizedCommand) y; |
| |
| return Util.compare(commandX, commandY); |
| } |
| } catch (final NotDefinedException e) { |
| // This could be made a lot more fine-grained. |
| } |
| |
| return 0; |
| } |
| } |
| |
| /** |
| * A tree node that knows what kind of information to return in the |
| * <code>toString</code> method. This is used for pattern matching. |
| */ |
| private static final class BindingTreeNode extends TreeNode { |
| |
| private BindingTreeNode(final Object object) { |
| super(object); |
| } |
| |
| public final String toString() { |
| final Object value = getValue(); |
| final ParameterizedCommand command; |
| if (value instanceof Binding) { |
| command = ((Binding) value).getParameterizedCommand(); |
| } else if (value instanceof ParameterizedCommand) { |
| command = (ParameterizedCommand) value; |
| } else { |
| return null; |
| } |
| |
| try { |
| return command.getName() |
| + command.getCommand().getDescription(); |
| } catch (final NotDefinedException e) { |
| return null; |
| } |
| } |
| } |
| |
| /** |
| * The constant value for <code>grouping</code> when the bindings should |
| * be grouped by category. |
| */ |
| private static final int GROUPING_CATEGORY = 0; |
| |
| /** |
| * The constant value for <code>grouping</code> when the bindings should |
| * be grouped by context. |
| */ |
| private static final int GROUPING_CONTEXT = 1; |
| |
| /** |
| * The constant value for <code>grouping</code> when the bindings should |
| * not be grouped (i.e., they should be displayed in a flat list). |
| */ |
| private static final int GROUPING_NONE = 2; |
| |
| /** |
| * The path at which the icon for "groups of bindings" is located. |
| */ |
| private static final String ICON_GROUP_OF_BINDINGS = "$nl$/icons/full/obj16/keygroups_obj.gif"; //$NON-NLS-1$ |
| |
| /** |
| * The number of items to show in the bindings table tree. |
| */ |
| private static final int ITEMS_TO_SHOW = 7; |
| |
| /** |
| * A comparator that can be used for display of |
| * <code>NamedHandleObject</code> instances to the end user. |
| */ |
| private static final NamedHandleObjectComparator NAMED_HANDLE_OBJECT_COMPARATOR = new NamedHandleObjectComparator(); |
| |
| /** |
| * Sorts the given array of <code>NamedHandleObject</code> instances based |
| * on their name. This is generally useful if they will be displayed to an |
| * end users. |
| * |
| * @param objects |
| * The objects to be sorted; must not be <code>null</code>. |
| * @return The same array, but sorted in place; never <code>null</code>. |
| */ |
| private static final NamedHandleObject[] sortByName( |
| final NamedHandleObject[] objects) { |
| Arrays.sort(objects, NAMED_HANDLE_OBJECT_COMPARATOR); |
| return objects; |
| } |
| |
| /** |
| * The workbench's binding service. This binding service is used to access |
| * the current set of bindings, and to persist changes. |
| */ |
| private IBindingService bindingService; |
| |
| /** |
| * The text widget containing the key sequence. This value is |
| * <code>null</code> until the controls are created. |
| */ |
| private Text bindingText; |
| |
| /** |
| * The workbench's command image service. This command image service is used |
| * to provide an icon beside each command. |
| */ |
| private ICommandImageService commandImageService; |
| |
| /** |
| * The label containing the name of the currently selected binding's |
| * command. This value is <code>null</code> until the controls are |
| * created. |
| */ |
| private Label commandNameValueLabel; |
| |
| /** |
| * The workbench's command service. This command service is used to access |
| * the list of commands. |
| */ |
| private ICommandService commandService; |
| |
| /** |
| * The workbench's context service. This context service is used to access |
| * the list of contexts. |
| */ |
| private IContextService contextService; |
| |
| /** |
| * The label containing the description of the currently selected binding's |
| * command. This value is <code>null</code> until the controls are |
| * created. |
| */ |
| private Label descriptionValueLabel; |
| |
| /** |
| * The filtered tree containing the list of commands and bindings to edit. |
| */ |
| private GroupedFilteredTree filteredTree; |
| |
| /** |
| * The grouping for the bindings tree. Either there should be no group |
| * (i.e., flat list), or the bindings should be grouped by either category |
| * or context. |
| */ |
| private int grouping = GROUPING_NONE; |
| |
| /** |
| * The key sequence entry widget containing the trigger sequence for the |
| * currently selected binding. This value is <code>null</code> until the |
| * controls are created. |
| */ |
| private KeySequenceText keySequenceText; |
| |
| /** |
| * A binding manager local to this preference page. When the page is |
| * initialized, the current bindings are read out from the binding service |
| * and placed in this manager. This manager is then updated as the user |
| * makes changes. When the user has finished, the contents of this manager |
| * are compared with the contents of the binding service. The changes are |
| * then persisted. |
| */ |
| private BindingManager localChangeManager; |
| |
| /** |
| * The context id of the binding which the user is trying to add. This value |
| * is derived from the binding that is selected at the time the user tried |
| * to add a binding. If this value is <code>null</code>, then the user is |
| * not currently trying to add a binding to a command that already has a |
| * binding. |
| */ |
| private String markedContextId = null; |
| |
| /** |
| * The parameterized command to which the user is currently trying to add a |
| * binding. If this value is <code>null</code>, then the user is not |
| * currently trying to add a binding to a command that already has a |
| * binding. |
| */ |
| private ParameterizedCommand markedParameterizedCommand = null; |
| |
| /** |
| * The combo box containing the list of possible schemes to choose from. |
| * This value is <code>null</code> until the contents are created. |
| */ |
| private ComboViewer schemeCombo = null; |
| |
| /** |
| * The check box controlling whether all commands should be shown in the |
| * filtered tree. This value is <code>null</code> until the contents are |
| * created. |
| */ |
| private Button showAllCheckBox = null; |
| |
| /** |
| * The combo box containing the list of possible contexts to choose from. |
| * This value is <code>null</code> until the contents are create. |
| */ |
| private ComboViewer whenCombo = null; |
| |
| /** |
| * Adds a new binding based on an existing binding. The command and the |
| * context are copied from the existing binding. The scheme id is set to be |
| * the user's personal derivative scheme. The preference page is updated, |
| * and focus is placed in the key sequence field. |
| * |
| * @param binding |
| * The binding to be added; must not be <code>null</code>. |
| */ |
| private final void bindingAdd(final Binding binding) { |
| // Remember the parameterized command and context. |
| markedParameterizedCommand = binding.getParameterizedCommand(); |
| markedContextId = binding.getContextId(); |
| |
| // Update the preference page. |
| update(); |
| |
| // Select the new binding. |
| filteredTree.getViewer().setSelection( |
| new StructuredSelection(new BindingTreeNode( |
| markedParameterizedCommand)), true); |
| bindingText.setFocus(); |
| bindingText.selectAll(); |
| } |
| |
| /** |
| * Removes an existing binding. The preference page is then updated. |
| * |
| * @param binding |
| * The binding to be removed; must not be <code>null</code>. |
| */ |
| private final void bindingRemove(final KeyBinding binding) { |
| final String contextId = binding.getContextId(); |
| final String schemeId = binding.getSchemeId(); |
| final KeySequence triggerSequence = binding.getKeySequence(); |
| localChangeManager.removeBindings(triggerSequence, schemeId, contextId, |
| null, null, null, Binding.USER); |
| |
| // TODO This should be the user's personal scheme. |
| localChangeManager.addBinding(new KeyBinding(triggerSequence, null, |
| schemeId, contextId, null, null, null, Binding.USER)); |
| update(); |
| } |
| |
| /** |
| * Creates the button bar across the bottom of the preference page. This |
| * button bar contains the "Advanced..." button. |
| * |
| * @param parent |
| * The composite in which the button bar should be placed; never |
| * <code>null</code>. |
| * @return The button bar composite; never <code>null</code>. |
| */ |
| private final Control createButtonBar(final Composite parent) { |
| GridLayout layout; |
| GridData gridData; |
| int widthHint; |
| |
| // Create the composite to house the button bar. |
| final Composite buttonBar = new Composite(parent, SWT.NONE); |
| layout = new GridLayout(1, false); |
| layout.marginWidth = 0; |
| buttonBar.setLayout(layout); |
| gridData = new GridData(); |
| gridData.horizontalAlignment = SWT.END; |
| buttonBar.setLayoutData(gridData); |
| |
| // Advanced button. |
| final Button advancedButton = new Button(buttonBar, SWT.PUSH); |
| gridData = new GridData(); |
| widthHint = convertHorizontalDLUsToPixels(IDialogConstants.BUTTON_WIDTH); |
| advancedButton.setText(NewKeysPreferenceMessages.AdvancedButton_Text); |
| gridData.widthHint = Math.max(widthHint, advancedButton.computeSize( |
| SWT.DEFAULT, SWT.DEFAULT, true).x) + 5; |
| advancedButton.setLayoutData(gridData); |
| |
| return buttonBar; |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see org.eclipse.jface.preference.PreferencePage#createContents(org.eclipse.swt.widgets.Composite) |
| */ |
| protected final Control createContents(final Composite parent) { |
| GridLayout layout = null; |
| |
| // Creates a composite to hold all of the page contents. |
| final Composite page = new Composite(parent, SWT.NONE); |
| layout = new GridLayout(1, false); |
| layout.marginWidth = 0; |
| page.setLayout(layout); |
| |
| createSchemeControls(page); |
| createTree(page); |
| createTreeControls(page); |
| createDataControls(page); |
| createButtonBar(page); |
| |
| fill(); |
| update(); |
| |
| return page; |
| } |
| |
| private final Control createDataControls(final Composite parent) { |
| GridLayout layout; |
| GridData gridData; |
| |
| // Creates the data area. |
| final Composite dataArea = new Composite(parent, SWT.NONE); |
| layout = new GridLayout(2, true); |
| layout.marginWidth = 0; |
| dataArea.setLayout(layout); |
| gridData = new GridData(); |
| gridData.grabExcessHorizontalSpace = true; |
| gridData.horizontalAlignment = SWT.FILL; |
| dataArea.setLayoutData(gridData); |
| |
| // LEFT DATA AREA |
| // Creates the left data area. |
| final Composite leftDataArea = new Composite(dataArea, SWT.NONE); |
| layout = new GridLayout(3, false); |
| leftDataArea.setLayout(layout); |
| gridData = new GridData(); |
| gridData.grabExcessHorizontalSpace = true; |
| gridData.verticalAlignment = SWT.TOP; |
| gridData.horizontalAlignment = SWT.FILL; |
| leftDataArea.setLayoutData(gridData); |
| |
| // The command name label. |
| final Label commandNameLabel = new Label(leftDataArea, SWT.NONE); |
| commandNameLabel.setText(NewKeysPreferenceMessages.CommandNameLabel_Text); |
| |
| // The current command name. |
| commandNameValueLabel = new Label(leftDataArea, SWT.NONE); |
| gridData = new GridData(); |
| gridData.grabExcessHorizontalSpace = true; |
| gridData.horizontalSpan = 2; |
| gridData.horizontalAlignment = SWT.FILL; |
| commandNameValueLabel.setLayoutData(gridData); |
| |
| // The binding label. |
| final Label bindingLabel = new Label(leftDataArea, SWT.NONE); |
| bindingLabel.setText(NewKeysPreferenceMessages.BindingLabel_Text); |
| |
| // The key sequence entry widget. |
| bindingText = new Text(leftDataArea, SWT.BORDER); |
| gridData = new GridData(); |
| gridData.grabExcessHorizontalSpace = true; |
| gridData.horizontalAlignment = SWT.FILL; |
| gridData.widthHint = 200; |
| bindingText.setLayoutData(gridData); |
| keySequenceText = new KeySequenceText(bindingText); |
| keySequenceText.setKeyStrokeLimit(4); |
| keySequenceText |
| .addPropertyChangeListener(new IPropertyChangeListener() { |
| public final void propertyChange( |
| final PropertyChangeEvent event) { |
| if (!event.getOldValue().equals(event.getNewValue())) { |
| keySequenceChanged(); |
| } |
| } |
| }); |
| |
| // Button for adding trapped key strokes |
| final Button addKeyButton = new Button(leftDataArea, SWT.LEFT |
| | SWT.ARROW); |
| addKeyButton.setToolTipText(NewKeysPreferenceMessages.AddKeyButton_ToolTipText); |
| gridData = new GridData(); |
| gridData.heightHint = schemeCombo.getCombo().getTextHeight(); |
| addKeyButton.setLayoutData(gridData); |
| |
| // Arrow buttons aren't normally added to the tab list. Let's fix that. |
| final Control[] tabStops = dataArea.getTabList(); |
| final ArrayList newTabStops = new ArrayList(); |
| for (int i = 0; i < tabStops.length; i++) { |
| Control tabStop = tabStops[i]; |
| newTabStops.add(tabStop); |
| if (bindingText.equals(tabStop)) { |
| newTabStops.add(addKeyButton); |
| } |
| } |
| final Control[] newTabStopArray = (Control[]) newTabStops |
| .toArray(new Control[newTabStops.size()]); |
| dataArea.setTabList(newTabStopArray); |
| |
| // Construct the menu to attach to the above button. |
| final Menu addKeyMenu = new Menu(addKeyButton); |
| final Iterator trappedKeyItr = KeySequenceText.TRAPPED_KEYS.iterator(); |
| while (trappedKeyItr.hasNext()) { |
| final KeyStroke trappedKey = (KeyStroke) trappedKeyItr.next(); |
| final MenuItem menuItem = new MenuItem(addKeyMenu, SWT.PUSH); |
| menuItem.setText(trappedKey.format()); |
| menuItem.addSelectionListener(new SelectionAdapter() { |
| |
| public void widgetSelected(SelectionEvent e) { |
| keySequenceText.insert(trappedKey); |
| bindingText.setFocus(); |
| bindingText.setSelection(bindingText.getTextLimit()); |
| } |
| }); |
| } |
| addKeyButton.addSelectionListener(new SelectionAdapter() { |
| |
| public void widgetSelected(SelectionEvent selectionEvent) { |
| Point buttonLocation = addKeyButton.getLocation(); |
| buttonLocation = dataArea.toDisplay(buttonLocation.x, |
| buttonLocation.y); |
| Point buttonSize = addKeyButton.getSize(); |
| addKeyMenu.setLocation(buttonLocation.x, buttonLocation.y |
| + buttonSize.y); |
| addKeyMenu.setVisible(true); |
| } |
| }); |
| |
| // The when label. |
| final Label whenLabel = new Label(leftDataArea, SWT.NONE); |
| whenLabel.setText(NewKeysPreferenceMessages.WhenLabel_Text); |
| |
| // The when combo. |
| whenCombo = new ComboViewer(leftDataArea); |
| gridData = new GridData(); |
| gridData.grabExcessHorizontalSpace = true; |
| gridData.horizontalAlignment = SWT.FILL; |
| gridData.horizontalSpan = 2; |
| whenCombo.getCombo().setLayoutData(gridData); |
| whenCombo.setLabelProvider(new NamedHandleObjectLabelProvider()); |
| whenCombo.setContentProvider(new ArrayContentProvider()); |
| |
| // RIGHT DATA AREA |
| // Creates the right data area. |
| final Composite rightDataArea = new Composite(dataArea, SWT.NONE); |
| layout = new GridLayout(1, false); |
| rightDataArea.setLayout(layout); |
| gridData = new GridData(); |
| gridData.grabExcessHorizontalSpace = true; |
| gridData.verticalAlignment = SWT.TOP; |
| gridData.horizontalAlignment = SWT.FILL; |
| rightDataArea.setLayoutData(gridData); |
| |
| // The description label. |
| final Label descriptionLabel = new Label(rightDataArea, SWT.NONE); |
| descriptionLabel.setText(NewKeysPreferenceMessages.DescriptionLabel_Text); |
| gridData = new GridData(); |
| gridData.grabExcessHorizontalSpace = true; |
| gridData.horizontalAlignment = SWT.FILL; |
| descriptionLabel.setLayoutData(gridData); |
| |
| // The description value. |
| descriptionValueLabel = new Label(rightDataArea, SWT.WRAP); |
| gridData = new GridData(); |
| gridData.horizontalAlignment = SWT.FILL; |
| gridData.verticalAlignment = SWT.FILL; |
| gridData.grabExcessHorizontalSpace = true; |
| gridData.grabExcessVerticalSpace = true; |
| gridData.horizontalIndent = 30; |
| gridData.verticalIndent = 5; |
| gridData.widthHint = 200; |
| descriptionValueLabel.setLayoutData(gridData); |
| |
| return dataArea; |
| } |
| |
| private final Control createSchemeControls(final Composite parent) { |
| GridLayout layout; |
| GridData gridData; |
| int widthHint; |
| |
| // Create a composite to hold the controls. |
| final Composite schemeControls = new Composite(parent, SWT.NONE); |
| layout = new GridLayout(3, false); |
| layout.marginWidth = 0; |
| schemeControls.setLayout(layout); |
| gridData = new GridData(); |
| gridData.grabExcessHorizontalSpace = true; |
| gridData.horizontalAlignment = SWT.FILL; |
| schemeControls.setLayoutData(gridData); |
| |
| // Create the label. |
| final Label schemeLabel = new Label(schemeControls, SWT.NONE); |
| schemeLabel.setText(NewKeysPreferenceMessages.SchemeLabel_Text); |
| |
| // Create the combo. |
| schemeCombo = new ComboViewer(schemeControls); |
| schemeCombo.setLabelProvider(new NamedHandleObjectLabelProvider()); |
| schemeCombo.setContentProvider(new ArrayContentProvider()); |
| gridData = new GridData(); |
| gridData.grabExcessHorizontalSpace = true; |
| gridData.horizontalAlignment = SWT.FILL; |
| schemeCombo.getCombo().setLayoutData(gridData); |
| schemeCombo |
| .addSelectionChangedListener(new ISelectionChangedListener() { |
| public final void selectionChanged( |
| final SelectionChangedEvent event) { |
| selectSchemeCombo(event); |
| } |
| }); |
| |
| // Create the delete button. |
| final Button deleteSchemeButton = new Button(schemeControls, SWT.PUSH); |
| gridData = new GridData(); |
| widthHint = convertHorizontalDLUsToPixels(IDialogConstants.BUTTON_WIDTH); |
| deleteSchemeButton.setText(NewKeysPreferenceMessages.DeleteSchemeButton_Text); |
| gridData.widthHint = Math.max(widthHint, deleteSchemeButton |
| .computeSize(SWT.DEFAULT, SWT.DEFAULT, true).x) + 5; |
| deleteSchemeButton.setLayoutData(gridData); |
| |
| return schemeControls; |
| } |
| |
| private final Control createTree(final Composite parent) { |
| GridData gridData; |
| |
| filteredTree = new GroupedFilteredTree(parent, SWT.SINGLE |
| /* | SWT.FULL_SELECTION */| SWT.BORDER, new PatternFilter()); |
| final GridLayout layout = new GridLayout(2, false); |
| layout.marginWidth = 0; |
| filteredTree.setLayout(layout); |
| gridData = new GridData(); |
| gridData.grabExcessHorizontalSpace = true; |
| gridData.grabExcessVerticalSpace = true; |
| gridData.horizontalAlignment = SWT.FILL; |
| gridData.verticalAlignment = SWT.FILL; |
| filteredTree.setLayoutData(gridData); |
| |
| // Make sure the filtered tree has a height of ITEMS_TO_SHOW |
| final Tree tree = filteredTree.getViewer().getTree(); |
| tree.setHeaderVisible(true); |
| final Object layoutData = tree.getLayoutData(); |
| if (layoutData instanceof GridData) { |
| gridData = (GridData) layoutData; |
| final int itemHeight = tree.getItemHeight(); |
| if (itemHeight > 1) { |
| gridData.heightHint = ITEMS_TO_SHOW * itemHeight; |
| } |
| } |
| |
| // Create the columns for the tree. |
| final TreeColumn commandNameColumn = new TreeColumn(tree, SWT.LEFT, |
| BindingLabelProvider.COLUMN_COMMAND); |
| commandNameColumn.setText(NewKeysPreferenceMessages.CommandNameColumn_Text); |
| final TreeColumn triggerSequenceColumn = new TreeColumn(tree, SWT.LEFT, |
| BindingLabelProvider.COLUMN_TRIGGER_SEQUENCE); |
| triggerSequenceColumn.setText(NewKeysPreferenceMessages.TriggerSequenceColumn_Text); |
| new TreeColumn(tree, SWT.LEFT, BindingLabelProvider.COLUMN_ADD); |
| new TreeColumn(tree, SWT.LEFT, BindingLabelProvider.COLUMN_REMOVE); |
| |
| // Set up the providers for the viewer. |
| final TreeViewer viewer = filteredTree.getViewer(); |
| viewer.setLabelProvider(new BindingLabelProvider()); |
| viewer.setContentProvider(new TreeNodeContentProvider()); |
| viewer.setComparator(new BindingComparator()); |
| |
| /* |
| * Listen for selection changes so that the data controls can be |
| * updated. |
| */ |
| viewer.addSelectionChangedListener(new ISelectionChangedListener() { |
| public final void selectionChanged(final SelectionChangedEvent event) { |
| selectTreeRow(event); |
| } |
| }); |
| tree.addMouseListener(new MouseAdapter() { |
| public final void mouseDown(final MouseEvent event) { |
| selectTreeColumn(event); |
| } |
| }); |
| |
| // Adjust how the filter works. |
| filteredTree.getPatternFilter().setIncludeLeadingWildcard(true); |
| |
| // Set the grouping options. |
| final Combo groupingCombo = filteredTree.getGroupingCombo(); |
| |
| final String[] groupings = { NewKeysPreferenceMessages.GroupingCombo_Category_Text, |
| NewKeysPreferenceMessages.GroupingCombo_When_Text, |
| NewKeysPreferenceMessages.GroupingCombo_None_Text }; |
| final Collator collator = Collator.getInstance(); |
| Arrays.sort(groupings, collator); |
| groupingCombo.setItems(groupings); |
| groupingCombo.setText(NewKeysPreferenceMessages.GroupingCombo_None_Text); |
| groupingCombo.addSelectionListener(new SelectionAdapter() { |
| public void widgetSelected(SelectionEvent e) { |
| update(); |
| } |
| }); |
| |
| return filteredTree; |
| } |
| |
| private final Control createTreeControls(final Composite parent) { |
| GridLayout layout; |
| GridData gridData; |
| int widthHint; |
| |
| // Creates controls related to the tree. |
| final Composite treeControls = new Composite(parent, SWT.NONE); |
| layout = new GridLayout(3, false); |
| layout.marginWidth = 0; |
| treeControls.setLayout(layout); |
| gridData = new GridData(); |
| gridData.grabExcessHorizontalSpace = true; |
| gridData.horizontalAlignment = SWT.FILL; |
| treeControls.setLayoutData(gridData); |
| |
| // Create the show all check box. |
| showAllCheckBox = new Button(treeControls, SWT.CHECK); |
| gridData = new GridData(); |
| gridData.grabExcessHorizontalSpace = true; |
| gridData.horizontalAlignment = SWT.FILL; |
| gridData.verticalAlignment = SWT.TOP; |
| showAllCheckBox.setLayoutData(gridData); |
| showAllCheckBox.setText(NewKeysPreferenceMessages.ShowAllCheckBox_Text); |
| showAllCheckBox.addSelectionListener(new SelectionAdapter() { |
| public void widgetSelected(SelectionEvent e) { |
| updateTree(); |
| } |
| }); |
| |
| // Create the delete binding button. |
| final Button addBindingButton = new Button(treeControls, SWT.PUSH); |
| gridData = new GridData(); |
| widthHint = convertHorizontalDLUsToPixels(IDialogConstants.BUTTON_WIDTH); |
| addBindingButton.setText(NewKeysPreferenceMessages.AddBindingButton_Text); |
| gridData.widthHint = Math.max(widthHint, addBindingButton.computeSize( |
| SWT.DEFAULT, SWT.DEFAULT, true).x) + 5; |
| addBindingButton.setLayoutData(gridData); |
| addBindingButton.addSelectionListener(new SelectionAdapter() { |
| public final void widgetSelected(final SelectionEvent event) { |
| selectAddBindingButton(event); |
| } |
| }); |
| |
| // Create the delete binding button. |
| final Button removeBindingButton = new Button(treeControls, SWT.PUSH); |
| gridData = new GridData(); |
| widthHint = convertHorizontalDLUsToPixels(IDialogConstants.BUTTON_WIDTH); |
| removeBindingButton.setText(NewKeysPreferenceMessages.RemoveBindingButton_Text); |
| gridData.widthHint = Math.max(widthHint, removeBindingButton |
| .computeSize(SWT.DEFAULT, SWT.DEFAULT, true).x) + 5; |
| removeBindingButton.setLayoutData(gridData); |
| removeBindingButton.addSelectionListener(new SelectionAdapter() { |
| public final void widgetSelected(final SelectionEvent event) { |
| selectRemoveBindingButton(event); |
| } |
| }); |
| |
| return treeControls; |
| } |
| |
| /** |
| * Copies all of the information from the workbench into a local change |
| * manager, and then the local change manager is used to populate the |
| * contents of the various widgets on the page. |
| * |
| * The widgets affected by this method are: scheme combo, bindings |
| * table/tree model, and the when combo. |
| */ |
| private final void fill() { |
| // Make an internal binding manager to track changes. |
| localChangeManager = new BindingManager(new ContextManager(), |
| new CommandManager()); |
| final Scheme[] definedSchemes = bindingService.getDefinedSchemes(); |
| try { |
| for (int i = 0; i < definedSchemes.length; i++) { |
| final Scheme scheme = definedSchemes[i]; |
| final Scheme copy = localChangeManager |
| .getScheme(scheme.getId()); |
| copy.define(scheme.getName(), scheme.getDescription(), scheme |
| .getParentId()); |
| } |
| localChangeManager |
| .setActiveScheme(bindingService.getActiveScheme()); |
| } catch (final NotDefinedException e) { |
| throw new Error( |
| "There is a programmer error in the keys preference page"); //$NON-NLS-1$ |
| } |
| localChangeManager.setLocale(bindingService.getLocale()); |
| localChangeManager.setPlatform(bindingService.getPlatform()); |
| localChangeManager.setBindings(bindingService.getBindings()); |
| |
| // Update the scheme combo. |
| schemeCombo |
| .setInput(sortByName(localChangeManager.getDefinedSchemes())); |
| setScheme(localChangeManager.getActiveScheme()); |
| |
| // Update the when combo. |
| whenCombo.setInput(sortByName(contextService.getDefinedContexts())); |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see org.eclipse.ui.IWorkbenchPreferencePage#init(org.eclipse.ui.IWorkbench) |
| */ |
| public final void init(final IWorkbench workbench) { |
| bindingService = (IBindingService) workbench.getService(IBindingService.class); |
| commandImageService = (ICommandImageService) workbench.getService(ICommandImageService.class); |
| commandService = (ICommandService) workbench.getService(ICommandService.class); |
| contextService = (IContextService) workbench.getService(IContextService.class); |
| } |
| |
| /** |
| * Updates the interface as the key sequence has changed. This finds the |
| * selected item. If the selected item is a binding, then it updates the |
| * binding -- either by updating a user binding, or doing the deletion |
| * marker dance with a system binding. If the selected item is a |
| * parameterized command, then a binding is created based on the data |
| * controls. |
| */ |
| private final void keySequenceChanged() { |
| final KeySequence keySequence = keySequenceText.getKeySequence(); |
| if ((keySequence == null) || (!keySequence.isComplete()) |
| || (keySequence.isEmpty())) { |
| return; |
| } |
| |
| ISelection selection = filteredTree.getViewer().getSelection(); |
| if (selection instanceof IStructuredSelection) { |
| IStructuredSelection structuredSelection = (IStructuredSelection) selection; |
| final TreeNode node = (TreeNode) structuredSelection |
| .getFirstElement(); |
| if (node != null) { |
| final Object object = node.getValue(); |
| selection = whenCombo.getSelection(); |
| final String contextId; |
| if (selection instanceof IStructuredSelection) { |
| structuredSelection = (IStructuredSelection) selection; |
| final Object firstElement = structuredSelection |
| .getFirstElement(); |
| if (firstElement == null) { |
| contextId = IContextIds.CONTEXT_ID_WINDOW; |
| } else { |
| contextId = ((Context) firstElement).getId(); |
| } |
| } else { |
| contextId = IContextIds.CONTEXT_ID_WINDOW; |
| } |
| if (object instanceof KeyBinding) { |
| // TODO |
| |
| } else if (object instanceof ParameterizedCommand) { |
| // TODO This should use the user's personal scheme. |
| final KeyBinding binding = new KeyBinding(keySequence, |
| (ParameterizedCommand) object, |
| "org.eclipse.ui.defaultAcceleratorConfiguration", //$NON-NLS-1$ |
| contextId, null, null, null, Binding.USER); |
| localChangeManager.addBinding(binding); |
| update(); |
| |
| filteredTree.getViewer().setSelection( |
| new StructuredSelection( |
| new BindingTreeNode(binding)), true); |
| } |
| } |
| } |
| } |
| |
| /** |
| * 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; |
| final String title = NewKeysPreferenceMessages.PreferenceStoreError_Title; |
| 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); |
| ErrorDialog.openError(schemeCombo.getCombo().getShell(), title, |
| message, status); |
| } |
| |
| protected final void performDefaults() { |
| // Ask the user to confirm |
| final String title = NewKeysPreferenceMessages.RestoreDefaultsMessageBoxText; |
| final String message = NewKeysPreferenceMessages.RestoreDefaultsMessageBoxMessage; |
| final boolean confirmed = MessageDialog.openConfirm(getShell(), title, |
| message); |
| |
| if (confirmed) { |
| // Fix the scheme in the local changes. |
| final String defaultSchemeId = bindingService.getDefaultSchemeId(); |
| final Scheme defaultScheme = localChangeManager |
| .getScheme(defaultSchemeId); |
| try { |
| localChangeManager.setActiveScheme(defaultScheme); |
| } catch (final NotDefinedException e) { |
| // At least we tried.... |
| } |
| |
| // Fix the bindings in the local changes. |
| final Binding[] currentBindings = localChangeManager.getBindings(); |
| final int currentBindingsLength = currentBindings.length; |
| final Set trimmedBindings = new HashSet(); |
| for (int i = 0; i < currentBindingsLength; i++) { |
| final Binding binding = currentBindings[i]; |
| if (binding.getType() != Binding.USER) { |
| trimmedBindings.add(binding); |
| } |
| } |
| final Binding[] trimmedBindingArray = (Binding[]) trimmedBindings |
| .toArray(new Binding[trimmedBindings.size()]); |
| localChangeManager.setBindings(trimmedBindingArray); |
| |
| // Apply the changes. |
| try { |
| bindingService.savePreferences(defaultScheme, |
| trimmedBindingArray); |
| } catch (final IOException e) { |
| logPreferenceStoreException(e); |
| } |
| } |
| |
| setScheme(localChangeManager.getActiveScheme()); |
| super.performDefaults(); |
| } |
| |
| public final boolean performOk() { |
| // Save the preferences. |
| try { |
| bindingService.savePreferences( |
| localChangeManager.getActiveScheme(), localChangeManager |
| .getBindings()); |
| } catch (final IOException e) { |
| logPreferenceStoreException(e); |
| } |
| |
| return super.performOk(); |
| } |
| |
| /** |
| * Handles the selection event on the add binding button. This adds a new |
| * binding based on the current selection. |
| * |
| * @param event |
| * Ignored. |
| */ |
| private final void selectAddBindingButton(final SelectionEvent event) { |
| // Check to make sure we've got a selection. |
| final TreeViewer viewer = filteredTree.getViewer(); |
| final ISelection selection = viewer.getSelection(); |
| if (!(selection instanceof IStructuredSelection)) { |
| return; |
| } |
| |
| final IStructuredSelection structuredSelection = (IStructuredSelection) selection; |
| final Object firstElement = structuredSelection.getFirstElement(); |
| if (firstElement instanceof TreeNode) { |
| final Object value = ((TreeNode) firstElement).getValue(); |
| if (value instanceof KeyBinding) { |
| bindingAdd((KeyBinding) value); |
| } else if (value instanceof ParameterizedCommand) { |
| bindingText.setFocus(); |
| } |
| } |
| } |
| |
| /** |
| * Handles the selection event on the remove binding button. This removes |
| * the selected binding. |
| * |
| * @param event |
| * Ignored. |
| */ |
| private final void selectRemoveBindingButton(final SelectionEvent event) { |
| // Check to make sure we've got a selection. |
| final TreeViewer viewer = filteredTree.getViewer(); |
| final ISelection selection = viewer.getSelection(); |
| if (!(selection instanceof IStructuredSelection)) { |
| return; |
| } |
| |
| final IStructuredSelection structuredSelection = (IStructuredSelection) selection; |
| final Object firstElement = structuredSelection.getFirstElement(); |
| if (firstElement instanceof TreeNode) { |
| final Object value = ((TreeNode) firstElement).getValue(); |
| if (value instanceof KeyBinding) { |
| bindingRemove((KeyBinding) value); |
| } else if (value == markedParameterizedCommand) { |
| markedParameterizedCommand = null; |
| markedContextId = null; |
| update(); |
| } |
| } |
| } |
| |
| /** |
| * Handles a selection event on the scheme combo. If the scheme has changed, |
| * then the local change manager is updated, and the page's contents are |
| * updated as well. |
| * |
| * @param event |
| * The selection event; must not be <code>null</code>. |
| */ |
| private final void selectSchemeCombo(final SelectionChangedEvent event) { |
| final ISelection selection = event.getSelection(); |
| if (selection instanceof IStructuredSelection) { |
| final Object firstElement = ((IStructuredSelection) selection) |
| .getFirstElement(); |
| if (firstElement instanceof Scheme) { |
| final Scheme newScheme = (Scheme) firstElement; |
| if (newScheme != localChangeManager.getActiveScheme()) { |
| try { |
| localChangeManager.setActiveScheme(newScheme); |
| update(); |
| } catch (final NotDefinedException e) { |
| // TODO The scheme wasn't valid. |
| } |
| } |
| } |
| } |
| } |
| |
| /** |
| * Handles a mouse down event on the tree. If the mouse click corresponds |
| * with one of the button images on the right, then add or remove, as |
| * appropriate. |
| * |
| * @param event |
| * The mouse down event; must not be <code>null</code>. |
| */ |
| private final void selectTreeColumn(final MouseEvent event) { |
| final TreeViewer viewer = filteredTree.getViewer(); |
| final Tree tree = viewer.getTree(); |
| final Point point = new Point(event.x, event.y); |
| final TreeItem item = tree.getItem(point); |
| if (item == null) { |
| return; |
| } |
| for (int i = 0; i < BindingLabelProvider.NUMBER_OF_COLUMNS; i++) { |
| final Rectangle rectangle = item.getBounds(i); |
| if (rectangle.contains(point)) { |
| // Check to make sure we're clicking a button. |
| if ((i != BindingLabelProvider.COLUMN_ADD) |
| && (i != BindingLabelProvider.COLUMN_REMOVE)) { |
| return; |
| } |
| |
| // Check to make sure we've got a selection. |
| final ISelection selection = viewer.getSelection(); |
| if (!(selection instanceof IStructuredSelection)) { |
| return; |
| } |
| |
| final IStructuredSelection structuredSelection = (IStructuredSelection) selection; |
| final TreeNode treeNode = (TreeNode) structuredSelection |
| .getFirstElement(); |
| final Object value = treeNode.getValue(); |
| if (value instanceof KeyBinding) { |
| final KeyBinding binding = (KeyBinding) value; |
| if (i == BindingLabelProvider.COLUMN_ADD) { |
| bindingAdd(binding); |
| |
| } else if (i == BindingLabelProvider.COLUMN_REMOVE) { |
| bindingRemove(binding); |
| |
| } |
| } |
| } |
| } |
| } |
| |
| /** |
| * If the row has changed, then update the data controls. |
| */ |
| private final void selectTreeRow(final SelectionChangedEvent event) { |
| updateDataControls(); |
| } |
| |
| /** |
| * Sets the currently selected scheme. Setting the scheme always triggers an |
| * update of the underlying widgets. |
| * |
| * @param scheme |
| * The scheme to select; may be <code>null</code>. |
| */ |
| private final void setScheme(final Scheme scheme) { |
| schemeCombo.setSelection(new StructuredSelection(scheme)); |
| } |
| |
| /** |
| * Updates all of the controls on this preference page in response to a user |
| * interaction. |
| */ |
| private final void update() { |
| updateTree(); |
| updateDataControls(); |
| } |
| |
| /** |
| * Updates the data controls to match the current selection, if any. |
| */ |
| private final void updateDataControls() { |
| final ISelection selection = filteredTree.getViewer().getSelection(); |
| if (selection instanceof IStructuredSelection) { |
| final IStructuredSelection structuredSelection = (IStructuredSelection) selection; |
| final TreeNode node = (TreeNode) structuredSelection |
| .getFirstElement(); |
| if (node != null) { |
| final Object object = node.getValue(); |
| if (object instanceof KeyBinding) { |
| final KeyBinding binding = (KeyBinding) object; |
| try { |
| commandNameValueLabel.setText(binding |
| .getParameterizedCommand().getName()); |
| String description = binding.getParameterizedCommand() |
| .getCommand().getDescription(); |
| if (description == null) { |
| description = Util.ZERO_LENGTH_STRING; |
| } |
| descriptionValueLabel.setText(description); |
| descriptionValueLabel.pack(true); |
| } catch (final NotDefinedException e) { |
| // It's probably okay to just let this one slide. |
| } |
| keySequenceText.setKeySequence(binding.getKeySequence()); |
| whenCombo.setSelection(new StructuredSelection( |
| contextService.getContext(binding.getContextId()))); |
| |
| } else if (object instanceof ParameterizedCommand) { |
| final ParameterizedCommand command = (ParameterizedCommand) object; |
| try { |
| commandNameValueLabel.setText(command.getName()); |
| String description = command.getCommand() |
| .getDescription(); |
| if (description == null) { |
| description = Util.ZERO_LENGTH_STRING; |
| } |
| descriptionValueLabel.setText(description); |
| descriptionValueLabel.pack(true); |
| } catch (final NotDefinedException e) { |
| // It's probably okay to just let this one slide. |
| } |
| keySequenceText.clear(); |
| if (command == markedParameterizedCommand) { |
| whenCombo.setSelection(new StructuredSelection( |
| contextService.getContext(markedContextId))); |
| } else { |
| whenCombo |
| .setSelection(new StructuredSelection( |
| contextService |
| .getContext(IContextIds.CONTEXT_ID_WINDOW))); |
| } |
| } |
| } |
| } |
| } |
| |
| private final void updateTree() { |
| final TreeViewer viewer = filteredTree.getViewer(); |
| final Collection bindings = localChangeManager |
| .getActiveBindingsDisregardingContextFlat(); |
| |
| /* |
| * Add all of the parameterized commands (without bindings) if the show |
| * all check box is selected. |
| */ |
| if (showAllCheckBox.getSelection()) { |
| final Collection commandIds = commandService.getDefinedCommandIds(); |
| final Collection commands = new ArrayList(); |
| final Iterator commandIdItr = commandIds.iterator(); |
| while (commandIdItr.hasNext()) { |
| final String currentCommandId = (String) commandIdItr.next(); |
| final Command currentCommand = commandService |
| .getCommand(currentCommandId); |
| try { |
| commands.addAll(ParameterizedCommand |
| .generateCombinations(currentCommand)); |
| } catch (final NotDefinedException e) { |
| // It is safe to just ignore undefined commands. |
| } |
| } |
| |
| // Remove duplicates. |
| final Iterator commandItr = commands.iterator(); |
| while (commandItr.hasNext()) { |
| final ParameterizedCommand command = (ParameterizedCommand) commandItr |
| .next(); |
| if (localChangeManager |
| .getActiveBindingsDisregardingContextFor(command).length > 0) { |
| commandItr.remove(); |
| } |
| } |
| |
| bindings.addAll(commands); |
| } |
| |
| // Add the marked parameterized command, if any. |
| if (markedParameterizedCommand != null) { |
| bindings.add(markedParameterizedCommand); |
| } |
| |
| // Check the grouping. |
| final String grouping = filteredTree.getGroupingCombo().getText(); |
| if (NewKeysPreferenceMessages.GroupingCombo_Category_Text.equals(grouping)) { |
| // Group all of the bindings by category. |
| final HashMap bindingsByCategory = new HashMap(); |
| final Iterator bindingItr = bindings.iterator(); |
| while (bindingItr.hasNext()) { |
| final Object object = bindingItr.next(); |
| final ParameterizedCommand command; |
| if (object instanceof Binding) { |
| command = ((Binding) object).getParameterizedCommand(); |
| } else { |
| command = (ParameterizedCommand) object; |
| } |
| try { |
| final Category category = command.getCommand() |
| .getCategory(); |
| final Object existing = bindingsByCategory.get(category); |
| if (existing instanceof Collection) { |
| final Collection existingBindings = (Collection) existing; |
| existingBindings.add(object); |
| } else { |
| final Collection newCollection = new ArrayList(); |
| newCollection.add(object); |
| bindingsByCategory.put(category, newCollection); |
| } |
| } catch (final NotDefinedException e) { |
| // Just skip this one. |
| continue; |
| } |
| } |
| |
| // Convert the hash map into nodes. |
| final Iterator entryItr = bindingsByCategory.entrySet().iterator(); |
| final TreeNode[] elements = new TreeNode[bindingsByCategory.size()]; |
| int i = 0; |
| while (entryItr.hasNext()) { |
| final Map.Entry entry = (Map.Entry) entryItr.next(); |
| final TreeNode parentNode = new BindingTreeNode(entry.getKey()); |
| final Collection childValues = (Collection) entry.getValue(); |
| final Iterator childValueItr = childValues.iterator(); |
| final TreeNode[] children = new TreeNode[childValues.size()]; |
| int j = 0; |
| while (childValueItr.hasNext()) { |
| final TreeNode childNode = new BindingTreeNode( |
| childValueItr.next()); |
| childNode.setParent(parentNode); |
| children[j++] = childNode; |
| } |
| parentNode.setChildren(children); |
| elements[i++] = parentNode; |
| } |
| |
| // Set the input. |
| viewer.setInput(elements); |
| |
| } else if (NewKeysPreferenceMessages.GroupingCombo_When_Text.equals(grouping)) { |
| // Group all of the bindings by context. |
| final HashMap bindingsByContextId = new HashMap(); |
| final Iterator bindingItr = bindings.iterator(); |
| while (bindingItr.hasNext()) { |
| final Object binding = bindingItr.next(); |
| final String contextId; |
| if (binding instanceof ParameterizedCommand) { |
| contextId = IContextIds.CONTEXT_ID_WINDOW; |
| } else { |
| contextId = ((Binding) binding).getContextId(); |
| } |
| final Object existing = bindingsByContextId.get(contextId); |
| if (existing instanceof Collection) { |
| final Collection existingBindings = (Collection) existing; |
| existingBindings.add(binding); |
| } else { |
| final Collection newCollection = new ArrayList(); |
| newCollection.add(binding); |
| bindingsByContextId.put(contextId, newCollection); |
| } |
| } |
| |
| // Convert the hash map into nodes. |
| final Iterator entryItr = bindingsByContextId.entrySet().iterator(); |
| final TreeNode[] elements = new TreeNode[bindingsByContextId.size()]; |
| int i = 0; |
| while (entryItr.hasNext()) { |
| final Map.Entry entry = (Map.Entry) entryItr.next(); |
| final TreeNode parentNode = new BindingTreeNode(entry.getKey()); |
| final Collection childValues = (Collection) entry.getValue(); |
| final Iterator childValueItr = childValues.iterator(); |
| final TreeNode[] children = new TreeNode[childValues.size()]; |
| int j = 0; |
| while (childValueItr.hasNext()) { |
| final TreeNode childNode = new BindingTreeNode( |
| childValueItr.next()); |
| childNode.setParent(parentNode); |
| children[j++] = childNode; |
| } |
| parentNode.setChildren(children); |
| elements[i++] = parentNode; |
| } |
| |
| // Set the input. |
| viewer.setInput(elements); |
| |
| } else { |
| // Just a flat list. Convert the flat list into nodes. |
| final Iterator bindingItr = bindings.iterator(); |
| final TreeNode[] elements = new BindingTreeNode[bindings.size()]; |
| int i = 0; |
| while (bindingItr.hasNext()) { |
| elements[i++] = new BindingTreeNode(bindingItr.next()); |
| } |
| |
| // Set the input. |
| viewer.setInput(elements); |
| |
| } |
| |
| // Repack all of the columns. |
| final Tree tree = viewer.getTree(); |
| final TreeColumn[] columns = tree.getColumns(); |
| if (NewKeysPreferenceMessages.GroupingCombo_Category_Text.equals(grouping) |
| || NewKeysPreferenceMessages.GroupingCombo_When_Text.equals(grouping)) { |
| columns[0].setWidth(292); |
| } else { |
| columns[0].setWidth(292); |
| } |
| columns[1].setWidth(234); |
| columns[2].setWidth(22); |
| columns[3].setWidth(22); |
| } |
| } |