| /******************************************************************************* |
| * Copyright (c) 2000, 2007 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.BufferedWriter; |
| import java.io.FileWriter; |
| import java.io.IOException; |
| import java.io.Writer; |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.Collection; |
| import java.util.Collections; |
| import java.util.Comparator; |
| import java.util.HashMap; |
| import java.util.HashSet; |
| import java.util.Iterator; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.ResourceBundle; |
| 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.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.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.TriggerSequence; |
| 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.IDialogConstants; |
| import org.eclipse.jface.dialogs.MessageDialog; |
| import org.eclipse.jface.preference.IPreferenceStore; |
| import org.eclipse.jface.preference.PreferencePage; |
| import org.eclipse.jface.util.SafeRunnable; |
| import org.eclipse.swt.SWT; |
| import org.eclipse.swt.events.DisposeEvent; |
| import org.eclipse.swt.events.DisposeListener; |
| import org.eclipse.swt.events.FocusEvent; |
| import org.eclipse.swt.events.FocusListener; |
| import org.eclipse.swt.events.ModifyEvent; |
| import org.eclipse.swt.events.ModifyListener; |
| 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.events.SelectionListener; |
| import org.eclipse.swt.graphics.Image; |
| import org.eclipse.swt.graphics.Point; |
| 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.FileDialog; |
| import org.eclipse.swt.widgets.Group; |
| import org.eclipse.swt.widgets.Label; |
| import org.eclipse.swt.widgets.Menu; |
| import org.eclipse.swt.widgets.MenuItem; |
| import org.eclipse.swt.widgets.TabFolder; |
| import org.eclipse.swt.widgets.TabItem; |
| import org.eclipse.swt.widgets.Table; |
| import org.eclipse.swt.widgets.TableColumn; |
| import org.eclipse.swt.widgets.TableItem; |
| import org.eclipse.swt.widgets.Text; |
| import org.eclipse.ui.IWorkbench; |
| import org.eclipse.ui.IWorkbenchPreferencePage; |
| import org.eclipse.ui.PlatformUI; |
| import org.eclipse.ui.activities.IActivityManager; |
| import org.eclipse.ui.commands.ICommandService; |
| import org.eclipse.ui.contexts.IContextService; |
| import org.eclipse.ui.internal.IPreferenceConstants; |
| import org.eclipse.ui.internal.IWorkbenchHelpContextIds; |
| import org.eclipse.ui.internal.WorkbenchPlugin; |
| import org.eclipse.ui.internal.misc.StatusUtil; |
| import org.eclipse.ui.internal.util.PrefUtil; |
| import org.eclipse.ui.internal.util.Util; |
| import org.eclipse.ui.keys.IBindingService; |
| import org.eclipse.ui.statushandlers.StatusManager; |
| |
| import com.ibm.icu.text.Collator; |
| import com.ibm.icu.text.MessageFormat; |
| |
| /** |
| * The preference page for defining keyboard shortcuts. While some of its |
| * underpinning have been made generic to "bindings" rather than "key bindings", |
| * it will still take some work to remove the link entirely. |
| * |
| * @since 3.0 |
| */ |
| public final class KeysPreferencePage extends PreferencePage implements |
| IWorkbenchPreferencePage { |
| |
| /** |
| * A selection listener to be used on the columns in the table on the view |
| * tab. This selection listener modifies the sort order so that the |
| * appropriate column is in the first position. |
| * |
| * @since 3.1 |
| */ |
| private class SortOrderSelectionListener extends SelectionAdapter { |
| |
| /** |
| * The column to be put in the first position. This value should be one |
| * of the constants defined by <code>SORT_COLUMN_</code>. |
| */ |
| private final int columnSelected; |
| |
| /** |
| * Constructs a new instance of <code>SortOrderSelectionListener</code>. |
| * |
| * @param columnSelected |
| * The column to be given first priority in the sort order; |
| * this value should be one of the constants defined as |
| * <code>SORT_COLUMN_</code>. |
| */ |
| private SortOrderSelectionListener(final int columnSelected) { |
| this.columnSelected = columnSelected; |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see org.eclipse.swt.events.SelectionListener#widgetSelected(org.eclipse.swt.events.SelectionEvent) |
| */ |
| public void widgetSelected(SelectionEvent e) { |
| // Change the column titles. |
| final int oldSortIndex = sortOrder[0]; |
| final TableColumn oldSortColumn = tableBindings |
| .getColumn(oldSortIndex); |
| oldSortColumn.setText(UNSORTED_COLUMN_NAMES[oldSortIndex]); |
| final TableColumn newSortColumn = tableBindings |
| .getColumn(columnSelected); |
| newSortColumn.setText(SORTED_COLUMN_NAMES[columnSelected]); |
| |
| // Change the sort order. |
| boolean columnPlaced = false; |
| boolean enoughRoom = false; |
| int bumpedColumn = -1; |
| for (int i = 0; i < sortOrder.length; i++) { |
| if (sortOrder[i] == columnSelected) { |
| /* |
| * We've found the place where the column existing in the |
| * old sort order. No matter what at this point, we have |
| * completed the reshuffling. |
| */ |
| enoughRoom = true; |
| if (bumpedColumn != -1) { |
| // We have already started bumping things around, so |
| // drop the last bumped column here. |
| sortOrder[i] = bumpedColumn; |
| } else { |
| // The order has not changed. |
| columnPlaced = true; |
| } |
| break; |
| |
| } else if (columnPlaced) { |
| // We are currently bumping, so just bump another. |
| int temp = sortOrder[i]; |
| sortOrder[i] = bumpedColumn; |
| bumpedColumn = temp; |
| |
| } else { |
| /* |
| * We are not currently bumping, so drop the column and |
| * start bumping. |
| */ |
| bumpedColumn = sortOrder[i]; |
| sortOrder[i] = columnSelected; |
| columnPlaced = true; |
| } |
| } |
| |
| // Grow the sort order. |
| if (!enoughRoom) { |
| final int[] newSortOrder = new int[sortOrder.length + 1]; |
| System.arraycopy(sortOrder, 0, newSortOrder, 0, |
| sortOrder.length); |
| newSortOrder[sortOrder.length] = bumpedColumn; |
| sortOrder = newSortOrder; |
| } |
| |
| // Update the view tab. |
| updateViewTab(); |
| } |
| } |
| |
| /** |
| * The data key for the binding stored on an SWT widget. The key is a |
| * fully-qualified name, but in reverse order. This is so that the equals |
| * method will detect misses faster. |
| */ |
| private static final String BINDING_KEY = "Binding.bindings.jface.eclipse.org"; //$NON-NLS-1$ |
| |
| /** |
| * The image associate with a binding that exists as part of the system |
| * definition. |
| */ |
| private static final Image IMAGE_BLANK = ImageFactory.getImage("blank"); //$NON-NLS-1$ |
| |
| /** |
| * The image associated with a binding changed by the user. |
| */ |
| private static final Image IMAGE_CHANGE = ImageFactory.getImage("change"); //$NON-NLS-1$ |
| |
| /** |
| * The data key at which the <code>Binding</code> instance for a table |
| * item is stored. |
| */ |
| private static final String ITEM_DATA_KEY = "org.eclipse.jface.bindings"; //$NON-NLS-1$ |
| |
| /** |
| * The number of items to show in the combo boxes. |
| */ |
| private static final int ITEMS_TO_SHOW = 9; |
| |
| /** |
| * The resource bundle from which translations can be retrieved. |
| */ |
| private static final ResourceBundle RESOURCE_BUNDLE = ResourceBundle |
| .getBundle(KeysPreferencePage.class.getName()); |
| |
| /** |
| * The total number of columns on the view tab. |
| */ |
| private static final int VIEW_TOTAL_COLUMNS = 4; |
| |
| /** |
| * The translated names for the columns when they are the primary sort key |
| * (e.g., ">Category<"). |
| */ |
| private static final String[] SORTED_COLUMN_NAMES = new String[VIEW_TOTAL_COLUMNS]; |
| |
| /** |
| * The index of the modify tab. |
| * |
| * @since 3.1 |
| */ |
| private static final int TAB_INDEX_MODIFY = 1; |
| |
| /** |
| * The translated names for the columns when they are not the primary sort |
| * key (e.g., "Category"). |
| */ |
| private static final String[] UNSORTED_COLUMN_NAMES = new String[VIEW_TOTAL_COLUMNS]; |
| |
| /** |
| * The index of the column on the view tab containing the category name. |
| */ |
| private static final int VIEW_CATEGORY_COLUMN_INDEX = 0; |
| |
| /** |
| * The index of the column on the view tab containing the command name. |
| */ |
| private static final int VIEW_COMMAND_COLUMN_INDEX = 1; |
| |
| /** |
| * The index of the column on the view tab containing the context name. |
| */ |
| private static final int VIEW_CONTEXT_COLUMN_INDEX = 3; |
| |
| /** |
| * The index of the column on the view tab containing the key sequence. |
| */ |
| private static final int VIEW_KEY_SEQUENCE_COLUMN_INDEX = 2; |
| |
| static { |
| UNSORTED_COLUMN_NAMES[VIEW_CATEGORY_COLUMN_INDEX] = Util |
| .translateString(RESOURCE_BUNDLE, "tableColumnCategory"); //$NON-NLS-1$ |
| UNSORTED_COLUMN_NAMES[VIEW_COMMAND_COLUMN_INDEX] = Util |
| .translateString(RESOURCE_BUNDLE, "tableColumnCommand"); //$NON-NLS-1$ |
| UNSORTED_COLUMN_NAMES[VIEW_KEY_SEQUENCE_COLUMN_INDEX] = Util |
| .translateString(RESOURCE_BUNDLE, "tableColumnKeySequence"); //$NON-NLS-1$ |
| UNSORTED_COLUMN_NAMES[VIEW_CONTEXT_COLUMN_INDEX] = Util |
| .translateString(RESOURCE_BUNDLE, "tableColumnContext"); //$NON-NLS-1$ |
| |
| SORTED_COLUMN_NAMES[VIEW_CATEGORY_COLUMN_INDEX] = Util.translateString( |
| RESOURCE_BUNDLE, "tableColumnCategorySorted"); //$NON-NLS-1$ |
| SORTED_COLUMN_NAMES[VIEW_COMMAND_COLUMN_INDEX] = Util.translateString( |
| RESOURCE_BUNDLE, "tableColumnCommandSorted"); //$NON-NLS-1$ |
| SORTED_COLUMN_NAMES[VIEW_KEY_SEQUENCE_COLUMN_INDEX] = Util |
| .translateString(RESOURCE_BUNDLE, |
| "tableColumnKeySequenceSorted"); //$NON-NLS-1$ |
| SORTED_COLUMN_NAMES[VIEW_CONTEXT_COLUMN_INDEX] = Util.translateString( |
| RESOURCE_BUNDLE, "tableColumnContextSorted"); //$NON-NLS-1$ |
| } |
| |
| /** |
| * The workbench's activity manager. This activity manager is used to see if |
| * certain commands should be filtered from the user interface. |
| */ |
| private IActivityManager activityManager; |
| |
| /** |
| * 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 add button located on the bottom left of the preference page. This |
| * button adds the current trigger sequence to the currently selected |
| * command. |
| */ |
| private Button buttonAdd; |
| |
| /** |
| * The remove button located on the bottom left of the preference page. This |
| * button removes the current trigger sequence from the current command. |
| */ |
| private Button buttonRemove; |
| |
| /** |
| * The restore button located on the bottom left of the preference page. |
| * This button attempts to restore the currently trigger sequence to its |
| * initial (i.e., Binding.SYSTEM) state -- undoing all user modifications. |
| */ |
| private Button buttonRestore; |
| |
| /** |
| * A map of all the category identifiers indexed by the names that appear in |
| * the user interface. This look-up table is built during initialization. |
| */ |
| private Map categoryIdsByUniqueName; |
| |
| /** |
| * A map of all the category names in the user interface indexed by their |
| * identifiers. This look-up table is built during initialization. |
| */ |
| private Map categoryUniqueNamesById; |
| |
| /** |
| * The combo box containing the list of all categories for commands. |
| */ |
| private Combo comboCategory; |
| |
| /** |
| * The combo box containing the list of commands relevent for the currently |
| * selected category. |
| */ |
| private Combo comboCommand; |
| |
| /** |
| * The combo box containing the list of contexts in the system. |
| */ |
| private Combo comboContext; |
| |
| /** |
| * The combo box containing the list of schemes in the system. |
| */ |
| private Combo comboScheme; |
| |
| /** |
| * A map of all the command identifiers indexed by the categories to which |
| * they belong. This look-up table is built during initialization. |
| */ |
| private Map commandIdsByCategoryId; |
| |
| /** |
| * The parameterized commands corresponding to the current contents of |
| * <code>comboCommand</code>. The commands in this array are in the same |
| * order as in the combo. This value can be <code>null</code> if nothing |
| * is selected in the combo. |
| */ |
| private ParameterizedCommand[] commands = null; |
| |
| /** |
| * The workbench's command service. This command service is used to access |
| * the list of commands. |
| */ |
| private ICommandService commandService; |
| |
| /** |
| * A map of all the context identifiers indexed by the names that appear in |
| * the user interface. This look-up table is built during initialization. |
| */ |
| private Map contextIdsByUniqueName; |
| |
| /** |
| * The workbench's context service. This context service is used to access |
| * the list of contexts. |
| */ |
| private IContextService contextService; |
| |
| /** |
| * A map of all the category names in the user interface indexed by their |
| * identifiers. This look-up table is built during initialization. |
| */ |
| private Map contextUniqueNamesById; |
| |
| /** |
| * The workbench's help system. This is used to register the page with the |
| * help system. |
| * |
| * TODO Add a help context |
| */ |
| // private IWorkbenchHelpSystem helpSystem; |
| /** |
| * This is the label next to the table showing the bindings matching a |
| * particular command. The label is disabled if there isn't a selected |
| * command identifier. |
| */ |
| private Label labelBindingsForCommand; |
| |
| /** |
| * This is the label next to the table showing the bindings matching a |
| * particular trigger sequence. The label is disabled if there isn't a |
| * current key sequence. |
| */ |
| private Label labelBindingsForTriggerSequence; |
| |
| /** |
| * The label next to the context combo box. This label indicates whether the |
| * context is a child of another context. If the current context is not a |
| * child, then this label is blank. |
| */ |
| private Label labelContextExtends; |
| |
| /** |
| * The label next to the scheme combo box. This label indicates whether the |
| * scheme is a child of another scheme. If the current scheme is not a |
| * child, then this label is blank. |
| */ |
| private Label labelSchemeExtends; |
| |
| /** |
| * 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 final BindingManager localChangeManager = new BindingManager( |
| new ContextManager(), new CommandManager()); |
| |
| /** |
| * A map of all the scheme identifiers indexed by the names that appear in |
| * the user interface. This look-up table is built during initialization. |
| */ |
| private Map schemeIdsByUniqueName; |
| |
| /** |
| * A map of all the scheme names in the user interface indexed by their |
| * identifiers. This look-up table is built during initialization. |
| */ |
| private Map schemeUniqueNamesById; |
| |
| /** |
| * The sort order to be used on the view tab to display all of the key |
| * bindings. This sort order can be changed by the user. This array is never |
| * <code>null</code>, but may be empty. |
| */ |
| private int[] sortOrder = { VIEW_CATEGORY_COLUMN_INDEX, |
| VIEW_COMMAND_COLUMN_INDEX, VIEW_KEY_SEQUENCE_COLUMN_INDEX, |
| VIEW_CONTEXT_COLUMN_INDEX }; |
| |
| /** |
| * The top-most tab folder for the preference page -- containing a view and |
| * a modify tab. |
| */ |
| private TabFolder tabFolder; |
| |
| /** |
| * A table of the key bindings currently defined. This table appears on the |
| * view tab; it is intended to be an easy way for users to learn the key |
| * bindings in Eclipse. This value is only <code>null</code> until the |
| * controls are first created. |
| */ |
| private Table tableBindings; |
| |
| /** |
| * The table containing all of the bindings matching the selected command. |
| */ |
| private Table tableBindingsForCommand; |
| |
| /** |
| * The table containing all of the bindings matching the current trigger |
| * sequence. |
| */ |
| private Table tableBindingsForTriggerSequence; |
| |
| /** |
| * The text widget where keys are entered. This widget is managed by |
| * <code>textTriggerSequenceManager</code>, which provides its special |
| * behaviour. |
| */ |
| private Text textTriggerSequence; |
| |
| /** |
| * The manager for the text widget that traps incoming key events. This |
| * manager should be used to access the widget, rather than accessing the |
| * widget directly. |
| */ |
| private KeySequenceText textTriggerSequenceManager; |
| |
| |
| /* (non-Javadoc) |
| * @see org.eclipse.jface.preference.PreferencePage#applyData(java.lang.Object) |
| */ |
| public void applyData(Object data) { |
| if(data instanceof Binding) { |
| editBinding((Binding) data); |
| } |
| } |
| protected final Control createContents(final Composite parent) { |
| |
| PlatformUI.getWorkbench().getHelpSystem() |
| .setHelp(parent, IWorkbenchHelpContextIds.KEYS_PREFERENCE_PAGE); |
| |
| tabFolder = new TabFolder(parent, SWT.NULL); |
| |
| // View tab |
| final TabItem viewTab = new TabItem(tabFolder, SWT.NULL); |
| viewTab.setText(Util.translateString(RESOURCE_BUNDLE, "viewTab.Text")); //$NON-NLS-1$ |
| viewTab.setControl(createViewTab(tabFolder)); |
| |
| // Modify tab |
| final TabItem modifyTab = new TabItem(tabFolder, SWT.NULL); |
| modifyTab.setText(Util.translateString(RESOURCE_BUNDLE, |
| "modifyTab.Text")); //$NON-NLS-1$ |
| modifyTab.setControl(createModifyTab(tabFolder)); |
| |
| // Do some fancy stuff. |
| applyDialogFont(tabFolder); |
| final IPreferenceStore store = getPreferenceStore(); |
| final int selectedTab = store |
| .getInt(IPreferenceConstants.KEYS_PREFERENCE_SELECTED_TAB); |
| if ((tabFolder.getItemCount() > selectedTab) && (selectedTab > 0)) { |
| tabFolder.setSelection(selectedTab); |
| } |
| |
| return tabFolder; |
| } |
| |
| /** |
| * Creates the tab that allows the user to change the keyboard shortcuts. |
| * |
| * @param parent |
| * The tab folder in which the tab should be created; must not be |
| * <code>null</code>. |
| * @return The composite which represents the contents of the tab; never |
| * <code>null</code>. |
| */ |
| private final Composite createModifyTab(final TabFolder parent) { |
| final Composite composite = new Composite(parent, SWT.NULL); |
| composite.setLayout(new GridLayout()); |
| GridData gridData = new GridData(GridData.FILL_BOTH); |
| composite.setLayoutData(gridData); |
| final Composite compositeKeyConfiguration = new Composite(composite, |
| SWT.NULL); |
| GridLayout gridLayout = new GridLayout(); |
| gridLayout.numColumns = 3; |
| compositeKeyConfiguration.setLayout(gridLayout); |
| gridData = new GridData(GridData.FILL_HORIZONTAL); |
| compositeKeyConfiguration.setLayoutData(gridData); |
| final Label labelKeyConfiguration = new Label( |
| compositeKeyConfiguration, SWT.LEFT); |
| labelKeyConfiguration.setText(Util.translateString(RESOURCE_BUNDLE, |
| "labelScheme")); //$NON-NLS-1$ |
| comboScheme = new Combo(compositeKeyConfiguration, SWT.READ_ONLY); |
| gridData = new GridData(); |
| gridData.widthHint = 200; |
| comboScheme.setLayoutData(gridData); |
| comboScheme.setVisibleItemCount(ITEMS_TO_SHOW); |
| |
| comboScheme.addSelectionListener(new SelectionAdapter() { |
| public final void widgetSelected(final SelectionEvent e) { |
| selectedComboScheme(); |
| } |
| }); |
| |
| labelSchemeExtends = new Label(compositeKeyConfiguration, SWT.LEFT); |
| gridData = new GridData(GridData.FILL_HORIZONTAL); |
| labelSchemeExtends.setLayoutData(gridData); |
| final Control spacer = new Composite(composite, SWT.NULL); |
| gridData = new GridData(); |
| gridData.heightHint = 10; |
| gridData.widthHint = 10; |
| spacer.setLayoutData(gridData); |
| final Group groupCommand = new Group(composite, SWT.SHADOW_NONE); |
| gridLayout = new GridLayout(); |
| gridLayout.numColumns = 3; |
| groupCommand.setLayout(gridLayout); |
| gridData = new GridData(GridData.FILL_BOTH); |
| groupCommand.setLayoutData(gridData); |
| groupCommand.setText(Util.translateString(RESOURCE_BUNDLE, |
| "groupCommand")); //$NON-NLS-1$ |
| final Label labelCategory = new Label(groupCommand, SWT.LEFT); |
| gridData = new GridData(); |
| labelCategory.setLayoutData(gridData); |
| labelCategory.setText(Util.translateString(RESOURCE_BUNDLE, |
| "labelCategory")); //$NON-NLS-1$ |
| comboCategory = new Combo(groupCommand, SWT.READ_ONLY); |
| gridData = new GridData(); |
| gridData.horizontalSpan = 2; |
| gridData.widthHint = 200; |
| comboCategory.setLayoutData(gridData); |
| comboCategory.setVisibleItemCount(ITEMS_TO_SHOW); |
| |
| comboCategory.addSelectionListener(new SelectionAdapter() { |
| public final void widgetSelected(final SelectionEvent e) { |
| update(); |
| } |
| }); |
| |
| final Label labelCommand = new Label(groupCommand, SWT.LEFT); |
| gridData = new GridData(); |
| labelCommand.setLayoutData(gridData); |
| labelCommand.setText(Util.translateString(RESOURCE_BUNDLE, |
| "labelCommand")); //$NON-NLS-1$ |
| comboCommand = new Combo(groupCommand, SWT.READ_ONLY); |
| gridData = new GridData(); |
| gridData.horizontalSpan = 2; |
| gridData.widthHint = 300; |
| comboCommand.setLayoutData(gridData); |
| comboCommand.setVisibleItemCount(9); |
| |
| comboCommand.addSelectionListener(new SelectionAdapter() { |
| public final void widgetSelected(final SelectionEvent e) { |
| update(); |
| } |
| }); |
| |
| labelBindingsForCommand = new Label(groupCommand, SWT.LEFT); |
| gridData = new GridData(GridData.VERTICAL_ALIGN_BEGINNING); |
| gridData.verticalAlignment = GridData.FILL_VERTICAL; |
| labelBindingsForCommand.setLayoutData(gridData); |
| labelBindingsForCommand.setText(Util.translateString(RESOURCE_BUNDLE, |
| "labelAssignmentsForCommand")); //$NON-NLS-1$ |
| tableBindingsForCommand = new Table(groupCommand, SWT.BORDER |
| | SWT.FULL_SELECTION | SWT.H_SCROLL | SWT.V_SCROLL); |
| tableBindingsForCommand.setHeaderVisible(true); |
| gridData = new GridData(GridData.FILL_BOTH); |
| gridData.heightHint = 60; |
| gridData.horizontalSpan = 2; |
| gridData.widthHint = "carbon".equals(SWT.getPlatform()) ? 620 : 520; //$NON-NLS-1$ |
| tableBindingsForCommand.setLayoutData(gridData); |
| TableColumn tableColumnDelta = new TableColumn(tableBindingsForCommand, |
| SWT.NULL, 0); |
| tableColumnDelta.setResizable(false); |
| tableColumnDelta.setText(Util.ZERO_LENGTH_STRING); |
| tableColumnDelta.setWidth(20); |
| TableColumn tableColumnContext = new TableColumn( |
| tableBindingsForCommand, SWT.NULL, 1); |
| tableColumnContext.setResizable(true); |
| tableColumnContext.setText(Util.translateString(RESOURCE_BUNDLE, |
| "tableColumnContext")); //$NON-NLS-1$ |
| tableColumnContext.pack(); |
| tableColumnContext.setWidth(200); |
| final TableColumn tableColumnKeySequence = new TableColumn( |
| tableBindingsForCommand, SWT.NULL, 2); |
| tableColumnKeySequence.setResizable(true); |
| tableColumnKeySequence.setText(Util.translateString(RESOURCE_BUNDLE, |
| "tableColumnKeySequence")); //$NON-NLS-1$ |
| tableColumnKeySequence.pack(); |
| tableColumnKeySequence.setWidth(300); |
| |
| tableBindingsForCommand.addMouseListener(new MouseAdapter() { |
| |
| public void mouseDoubleClick(MouseEvent mouseEvent) { |
| update(); |
| } |
| }); |
| |
| tableBindingsForCommand.addSelectionListener(new SelectionAdapter() { |
| |
| public void widgetSelected(SelectionEvent selectionEvent) { |
| selectedTableBindingsForCommand(); |
| } |
| }); |
| |
| final Group groupKeySequence = new Group(composite, SWT.SHADOW_NONE); |
| gridLayout = new GridLayout(); |
| gridLayout.numColumns = 4; |
| groupKeySequence.setLayout(gridLayout); |
| gridData = new GridData(GridData.FILL_BOTH); |
| groupKeySequence.setLayoutData(gridData); |
| groupKeySequence.setText(Util.translateString(RESOURCE_BUNDLE, |
| "groupKeySequence")); //$NON-NLS-1$ |
| final Label labelKeySequence = new Label(groupKeySequence, SWT.LEFT); |
| gridData = new GridData(); |
| labelKeySequence.setLayoutData(gridData); |
| labelKeySequence.setText(Util.translateString(RESOURCE_BUNDLE, |
| "labelKeySequence")); //$NON-NLS-1$ |
| |
| // The text widget into which the key strokes will be entered. |
| textTriggerSequence = new Text(groupKeySequence, SWT.BORDER); |
| // On MacOS X, this font will be changed by KeySequenceText |
| textTriggerSequence.setFont(groupKeySequence.getFont()); |
| gridData = new GridData(); |
| gridData.horizontalSpan = 2; |
| gridData.widthHint = 300; |
| textTriggerSequence.setLayoutData(gridData); |
| textTriggerSequence.addModifyListener(new ModifyListener() { |
| public void modifyText(ModifyEvent e) { |
| update(); |
| } |
| }); |
| textTriggerSequence.addFocusListener(new FocusListener() { |
| public void focusGained(FocusEvent e) { |
| bindingService.setKeyFilterEnabled(false); |
| } |
| |
| public void focusLost(FocusEvent e) { |
| bindingService.setKeyFilterEnabled(true); |
| } |
| }); |
| textTriggerSequence.addDisposeListener(new DisposeListener() { |
| public void widgetDisposed(DisposeEvent e) { |
| if (!bindingService.isKeyFilterEnabled()) { |
| bindingService.setKeyFilterEnabled(true); |
| } |
| } |
| }); |
| |
| // The manager for the key sequence text widget. |
| textTriggerSequenceManager = new KeySequenceText(textTriggerSequence); |
| textTriggerSequenceManager.setKeyStrokeLimit(4); |
| |
| // Button for adding trapped key strokes |
| final Button buttonAddKey = new Button(groupKeySequence, SWT.LEFT |
| | SWT.ARROW); |
| buttonAddKey.setToolTipText(Util.translateString(RESOURCE_BUNDLE, |
| "buttonAddKey.ToolTipText")); //$NON-NLS-1$ |
| gridData = new GridData(); |
| gridData.heightHint = comboCategory.getTextHeight(); |
| buttonAddKey.setLayoutData(gridData); |
| |
| // Arrow buttons aren't normally added to the tab list. Let's fix that. |
| final Control[] tabStops = groupKeySequence.getTabList(); |
| final ArrayList newTabStops = new ArrayList(); |
| for (int i = 0; i < tabStops.length; i++) { |
| Control tabStop = tabStops[i]; |
| newTabStops.add(tabStop); |
| if (textTriggerSequence.equals(tabStop)) { |
| newTabStops.add(buttonAddKey); |
| } |
| } |
| final Control[] newTabStopArray = (Control[]) newTabStops |
| .toArray(new Control[newTabStops.size()]); |
| groupKeySequence.setTabList(newTabStopArray); |
| |
| // Construct the menu to attach to the above button. |
| final Menu menuButtonAddKey = new Menu(buttonAddKey); |
| final Iterator trappedKeyItr = KeySequenceText.TRAPPED_KEYS.iterator(); |
| while (trappedKeyItr.hasNext()) { |
| final KeyStroke trappedKey = (KeyStroke) trappedKeyItr.next(); |
| final MenuItem menuItem = new MenuItem(menuButtonAddKey, SWT.PUSH); |
| menuItem.setText(trappedKey.format()); |
| menuItem.addSelectionListener(new SelectionAdapter() { |
| |
| public void widgetSelected(SelectionEvent e) { |
| textTriggerSequenceManager.insert(trappedKey); |
| textTriggerSequence.setFocus(); |
| textTriggerSequence.setSelection(textTriggerSequence |
| .getTextLimit()); |
| } |
| }); |
| } |
| buttonAddKey.addSelectionListener(new SelectionAdapter() { |
| |
| public void widgetSelected(SelectionEvent selectionEvent) { |
| Point buttonLocation = buttonAddKey.getLocation(); |
| buttonLocation = groupKeySequence.toDisplay(buttonLocation.x, |
| buttonLocation.y); |
| Point buttonSize = buttonAddKey.getSize(); |
| menuButtonAddKey.setLocation(buttonLocation.x, buttonLocation.y |
| + buttonSize.y); |
| menuButtonAddKey.setVisible(true); |
| } |
| }); |
| |
| labelBindingsForTriggerSequence = new Label(groupKeySequence, SWT.LEFT); |
| gridData = new GridData(GridData.VERTICAL_ALIGN_BEGINNING); |
| gridData.verticalAlignment = GridData.FILL_VERTICAL; |
| labelBindingsForTriggerSequence.setLayoutData(gridData); |
| labelBindingsForTriggerSequence.setText(Util.translateString( |
| RESOURCE_BUNDLE, "labelAssignmentsForKeySequence")); //$NON-NLS-1$ |
| tableBindingsForTriggerSequence = new Table(groupKeySequence, |
| SWT.BORDER | SWT.FULL_SELECTION | SWT.H_SCROLL | SWT.V_SCROLL); |
| tableBindingsForTriggerSequence.setHeaderVisible(true); |
| gridData = new GridData(GridData.FILL_BOTH); |
| gridData.heightHint = 60; |
| gridData.horizontalSpan = 3; |
| gridData.widthHint = "carbon".equals(SWT.getPlatform()) ? 620 : 520; //$NON-NLS-1$ |
| tableBindingsForTriggerSequence.setLayoutData(gridData); |
| tableColumnDelta = new TableColumn(tableBindingsForTriggerSequence, |
| SWT.NULL, 0); |
| tableColumnDelta.setResizable(false); |
| tableColumnDelta.setText(Util.ZERO_LENGTH_STRING); |
| tableColumnDelta.setWidth(20); |
| tableColumnContext = new TableColumn(tableBindingsForTriggerSequence, |
| SWT.NULL, 1); |
| tableColumnContext.setResizable(true); |
| tableColumnContext.setText(Util.translateString(RESOURCE_BUNDLE, |
| "tableColumnContext")); //$NON-NLS-1$ |
| tableColumnContext.pack(); |
| tableColumnContext.setWidth(200); |
| final TableColumn tableColumnCommand = new TableColumn( |
| tableBindingsForTriggerSequence, SWT.NULL, 2); |
| tableColumnCommand.setResizable(true); |
| tableColumnCommand.setText(Util.translateString(RESOURCE_BUNDLE, |
| "tableColumnCommand")); //$NON-NLS-1$ |
| tableColumnCommand.pack(); |
| tableColumnCommand.setWidth(300); |
| |
| tableBindingsForTriggerSequence.addMouseListener(new MouseAdapter() { |
| |
| public void mouseDoubleClick(MouseEvent mouseEvent) { |
| update(); |
| } |
| }); |
| |
| tableBindingsForTriggerSequence |
| .addSelectionListener(new SelectionAdapter() { |
| |
| public void widgetSelected(SelectionEvent selectionEvent) { |
| selectedTableBindingsForTriggerSequence(); |
| } |
| }); |
| |
| final Composite compositeContext = new Composite(composite, SWT.NULL); |
| gridLayout = new GridLayout(); |
| gridLayout.numColumns = 3; |
| compositeContext.setLayout(gridLayout); |
| gridData = new GridData(GridData.FILL_HORIZONTAL); |
| compositeContext.setLayoutData(gridData); |
| final Label labelContext = new Label(compositeContext, SWT.LEFT); |
| labelContext.setText(Util.translateString(RESOURCE_BUNDLE, |
| "labelContext")); //$NON-NLS-1$ |
| comboContext = new Combo(compositeContext, SWT.READ_ONLY); |
| gridData = new GridData(); |
| gridData.widthHint = 250; |
| comboContext.setLayoutData(gridData); |
| comboContext.setVisibleItemCount(ITEMS_TO_SHOW); |
| |
| comboContext.addSelectionListener(new SelectionAdapter() { |
| public final void widgetSelected(final SelectionEvent e) { |
| update(); |
| } |
| }); |
| |
| labelContextExtends = new Label(compositeContext, SWT.LEFT); |
| gridData = new GridData(GridData.FILL_HORIZONTAL); |
| labelContextExtends.setLayoutData(gridData); |
| final Composite compositeButton = new Composite(composite, SWT.NULL); |
| gridLayout = new GridLayout(); |
| gridLayout.marginHeight = 20; |
| gridLayout.marginWidth = 0; |
| gridLayout.numColumns = 3; |
| compositeButton.setLayout(gridLayout); |
| gridData = new GridData(); |
| compositeButton.setLayoutData(gridData); |
| buttonAdd = new Button(compositeButton, SWT.CENTER | SWT.PUSH); |
| gridData = new GridData(); |
| int widthHint = convertHorizontalDLUsToPixels(IDialogConstants.BUTTON_WIDTH); |
| buttonAdd.setText(Util.translateString(RESOURCE_BUNDLE, "buttonAdd")); //$NON-NLS-1$ |
| gridData.widthHint = Math.max(widthHint, buttonAdd.computeSize( |
| SWT.DEFAULT, SWT.DEFAULT, true).x) + 5; |
| buttonAdd.setLayoutData(gridData); |
| |
| buttonAdd.addSelectionListener(new SelectionAdapter() { |
| |
| public void widgetSelected(SelectionEvent selectionEvent) { |
| selectedButtonAdd(); |
| } |
| }); |
| |
| buttonRemove = new Button(compositeButton, SWT.CENTER | SWT.PUSH); |
| gridData = new GridData(); |
| widthHint = convertHorizontalDLUsToPixels(IDialogConstants.BUTTON_WIDTH); |
| buttonRemove.setText(Util.translateString(RESOURCE_BUNDLE, |
| "buttonRemove")); //$NON-NLS-1$ |
| gridData.widthHint = Math.max(widthHint, buttonRemove.computeSize( |
| SWT.DEFAULT, SWT.DEFAULT, true).x) + 5; |
| buttonRemove.setLayoutData(gridData); |
| |
| buttonRemove.addSelectionListener(new SelectionAdapter() { |
| |
| public void widgetSelected(SelectionEvent selectionEvent) { |
| selectedButtonRemove(); |
| } |
| }); |
| |
| buttonRestore = new Button(compositeButton, SWT.CENTER | SWT.PUSH); |
| gridData = new GridData(); |
| widthHint = convertHorizontalDLUsToPixels(IDialogConstants.BUTTON_WIDTH); |
| buttonRestore.setText(Util.translateString(RESOURCE_BUNDLE, |
| "buttonRestore")); //$NON-NLS-1$ |
| gridData.widthHint = Math.max(widthHint, buttonRestore.computeSize( |
| SWT.DEFAULT, SWT.DEFAULT, true).x) + 5; |
| buttonRestore.setLayoutData(gridData); |
| |
| buttonRestore.addSelectionListener(new SelectionAdapter() { |
| |
| public void widgetSelected(SelectionEvent selectionEvent) { |
| selectedButtonRestore(); |
| } |
| }); |
| |
| return composite; |
| } |
| |
| /** |
| * Creates a tab on the main page for displaying an uneditable list of the |
| * current key bindings. This is intended as a discovery tool for new users. |
| * It shows all of the key bindings for the current key configuration, |
| * platform and locale. |
| * |
| * @param parent |
| * The tab folder in which the tab should be created; must not be |
| * <code>null</code>. |
| * @return The newly created composite containing all of the controls; never |
| * <code>null</code>. |
| * @since 3.1 |
| */ |
| private final Composite createViewTab(final TabFolder parent) { |
| GridData gridData = null; |
| int widthHint; |
| |
| // Create the composite for the tab. |
| final Composite composite = new Composite(parent, SWT.NONE); |
| composite.setLayoutData(new GridData(GridData.FILL_BOTH)); |
| composite.setLayout(new GridLayout()); |
| |
| // Place a table inside the tab. |
| tableBindings = new Table(composite, SWT.BORDER | SWT.FULL_SELECTION |
| | SWT.H_SCROLL | SWT.V_SCROLL); |
| tableBindings.setHeaderVisible(true); |
| gridData = new GridData(GridData.FILL_BOTH); |
| gridData.heightHint = 400; |
| gridData.horizontalSpan = 2; |
| tableBindings.setLayoutData(gridData); |
| final TableColumn tableColumnCategory = new TableColumn(tableBindings, |
| SWT.NONE, VIEW_CATEGORY_COLUMN_INDEX); |
| tableColumnCategory |
| .setText(SORTED_COLUMN_NAMES[VIEW_CATEGORY_COLUMN_INDEX]); |
| tableColumnCategory |
| .addSelectionListener(new SortOrderSelectionListener( |
| VIEW_CATEGORY_COLUMN_INDEX)); |
| final TableColumn tableColumnCommand = new TableColumn(tableBindings, |
| SWT.NONE, VIEW_COMMAND_COLUMN_INDEX); |
| tableColumnCommand |
| .setText(UNSORTED_COLUMN_NAMES[VIEW_COMMAND_COLUMN_INDEX]); |
| tableColumnCommand.addSelectionListener(new SortOrderSelectionListener( |
| VIEW_COMMAND_COLUMN_INDEX)); |
| final TableColumn tableColumnKeySequence = new TableColumn( |
| tableBindings, SWT.NONE, VIEW_KEY_SEQUENCE_COLUMN_INDEX); |
| tableColumnKeySequence |
| .setText(UNSORTED_COLUMN_NAMES[VIEW_KEY_SEQUENCE_COLUMN_INDEX]); |
| tableColumnKeySequence |
| .addSelectionListener(new SortOrderSelectionListener( |
| VIEW_KEY_SEQUENCE_COLUMN_INDEX)); |
| final TableColumn tableColumnContext = new TableColumn(tableBindings, |
| SWT.NONE, VIEW_CONTEXT_COLUMN_INDEX); |
| tableColumnContext |
| .setText(UNSORTED_COLUMN_NAMES[VIEW_CONTEXT_COLUMN_INDEX]); |
| tableColumnContext.addSelectionListener(new SortOrderSelectionListener( |
| VIEW_CONTEXT_COLUMN_INDEX)); |
| tableBindings.addSelectionListener(new SelectionAdapter() { |
| public final void widgetDefaultSelected(final SelectionEvent e) { |
| selectedTableKeyBindings(); |
| } |
| }); |
| |
| // A composite for the buttons. |
| final Composite buttonBar = new Composite(composite, SWT.NONE); |
| buttonBar.setLayout(new GridLayout(2, false)); |
| gridData = new GridData(); |
| gridData.horizontalAlignment = GridData.END; |
| buttonBar.setLayoutData(gridData); |
| |
| // A button for editing the current selection. |
| final Button editButton = new Button(buttonBar, SWT.PUSH); |
| gridData = new GridData(); |
| widthHint = convertHorizontalDLUsToPixels(IDialogConstants.BUTTON_WIDTH); |
| editButton.setText(Util.translateString(RESOURCE_BUNDLE, "buttonEdit")); //$NON-NLS-1$ |
| gridData.widthHint = Math.max(widthHint, editButton.computeSize( |
| SWT.DEFAULT, SWT.DEFAULT, true).x) + 5; |
| editButton.setLayoutData(gridData); |
| editButton.addSelectionListener(new SelectionListener() { |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see org.eclipse.swt.events.SelectionListener#widgetDefaultSelected(org.eclipse.swt.events.SelectionEvent) |
| */ |
| public final void widgetDefaultSelected(final SelectionEvent event) { |
| selectedTableKeyBindings(); |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see org.eclipse.swt.events.SelectionListener#widgetSelected(org.eclipse.swt.events.SelectionEvent) |
| */ |
| public void widgetSelected(SelectionEvent e) { |
| widgetDefaultSelected(e); |
| } |
| }); |
| |
| // A button for exporting the contents to a file. |
| final Button buttonExport = new Button(buttonBar, SWT.PUSH); |
| gridData = new GridData(); |
| widthHint = convertHorizontalDLUsToPixels(IDialogConstants.BUTTON_WIDTH); |
| buttonExport.setText(Util.translateString(RESOURCE_BUNDLE, |
| "buttonExport")); //$NON-NLS-1$ |
| gridData.widthHint = Math.max(widthHint, buttonExport.computeSize( |
| SWT.DEFAULT, SWT.DEFAULT, true).x) + 5; |
| buttonExport.setLayoutData(gridData); |
| buttonExport.addSelectionListener(new SelectionListener() { |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see org.eclipse.swt.events.SelectionListener#widgetDefaultSelected(org.eclipse.swt.events.SelectionEvent) |
| */ |
| public final void widgetDefaultSelected(final SelectionEvent event) { |
| selectedButtonExport(); |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see org.eclipse.swt.events.SelectionListener#widgetSelected(org.eclipse.swt.events.SelectionEvent) |
| */ |
| public void widgetSelected(SelectionEvent e) { |
| widgetDefaultSelected(e); |
| } |
| }); |
| |
| return composite; |
| } |
| |
| protected IPreferenceStore doGetPreferenceStore() { |
| return PrefUtil.getInternalPreferenceStore(); |
| } |
| |
| /** |
| * Allows the user to change the key bindings for a particular command. |
| * Switches the tab to the modify tab, and then selects the category and |
| * command that corresponds with the given command name. It then selects the |
| * given key sequence and gives focus to the key sequence text widget. |
| * |
| * @param binding |
| * The binding to be edited; if <code>null</code>, then just |
| * switch to the modify tab. If the <code>binding</code> does |
| * not correspond to anything in the keys preference page, then |
| * this also just switches to the modify tab. |
| * @since 3.1 |
| */ |
| public final void editBinding(final Binding binding) { |
| // Switch to the modify tab. |
| tabFolder.setSelection(TAB_INDEX_MODIFY); |
| |
| // If there is no command name, stop here. |
| if (binding == null) { |
| return; |
| } |
| |
| /* |
| * Get the corresponding category and command names. If either is |
| * undefined, then we can just stop now. We won't be able to find their |
| * name. |
| */ |
| final ParameterizedCommand command = binding.getParameterizedCommand(); |
| String categoryName = null; |
| String commandName = null; |
| try { |
| categoryName = command.getCommand().getCategory().getName(); |
| commandName = command.getName(); |
| } catch (final NotDefinedException e) { |
| return; // no name |
| } |
| |
| // Update the category combo box. |
| final String[] categoryNames = comboCategory.getItems(); |
| int i = 0; |
| for (; i < categoryNames.length; i++) { |
| if (categoryName.equals(categoryNames[i])) { |
| break; |
| } |
| } |
| if (i >= comboCategory.getItemCount()) { |
| // Couldn't find the category, so abort. |
| return; |
| } |
| comboCategory.select(i); |
| |
| // Update the commands combo box. |
| updateComboCommand(); |
| |
| // Update the command combo box. |
| final String[] commandNames = comboCommand.getItems(); |
| int j = 0; |
| for (; j < commandNames.length; j++) { |
| if (commandName.equals(commandNames[j])) { |
| if (comboCommand.getSelectionIndex() != j) { |
| comboCommand.select(j); |
| } |
| break; |
| } |
| } |
| if (j >= comboCommand.getItemCount()) { |
| // Couldn't find the command, so just select the first and then stop |
| if (comboCommand.getSelectionIndex() != 0) { |
| comboCommand.select(0); |
| } |
| update(); |
| return; |
| } |
| |
| /* |
| * Update and validate the state of the modify tab in response to these |
| * selection changes. |
| */ |
| update(); |
| |
| // Select the right key binding, if possible. |
| final TableItem[] items = tableBindingsForCommand.getItems(); |
| int k = 0; |
| for (; k < items.length; k++) { |
| final String currentKeySequence = items[k].getText(2); |
| if (binding.getTriggerSequence().format() |
| .equals(currentKeySequence)) { |
| break; |
| } |
| } |
| if (k < tableBindingsForCommand.getItemCount()) { |
| tableBindingsForCommand.select(k); |
| tableBindingsForCommand.notifyListeners(SWT.Selection, null); |
| textTriggerSequence.setFocus(); |
| } |
| } |
| |
| /** |
| * Returns the identifier for the currently selected category. |
| * |
| * @return The selected category; <code>null</code> if none. |
| */ |
| private final String getCategoryId() { |
| return !commandIdsByCategoryId.containsKey(null) |
| || comboCategory.getSelectionIndex() > 0 ? (String) categoryIdsByUniqueName |
| .get(comboCategory.getText()) |
| : null; |
| } |
| |
| /** |
| * Returns the identifier for the currently selected context. |
| * |
| * @return The selected context; <code>null</code> if none. |
| */ |
| private final String getContextId() { |
| return comboContext.getSelectionIndex() >= 0 ? (String) contextIdsByUniqueName |
| .get(comboContext.getText()) |
| : null; |
| } |
| |
| /** |
| * Returns the current trigger sequence. |
| * |
| * @return The trigger sequence; may be empty, but never <code>null</code>. |
| */ |
| private final KeySequence getKeySequence() { |
| return textTriggerSequenceManager.getKeySequence(); |
| } |
| |
| /** |
| * Returns the currently-selected fully-parameterized command. |
| * |
| * @return The selected fully-parameterized command; <code>null</code> if |
| * none. |
| */ |
| private final ParameterizedCommand getParameterizedCommand() { |
| final int selectionIndex = comboCommand.getSelectionIndex(); |
| if ((selectionIndex >= 0) && (commands != null) |
| && (selectionIndex < commands.length)) { |
| return commands[selectionIndex]; |
| } |
| |
| return null; |
| } |
| |
| /** |
| * Returns the identifier for the currently selected scheme. |
| * |
| * @return The selected scheme; <code>null</code> if none. |
| */ |
| private final String getSchemeId() { |
| return comboScheme.getSelectionIndex() >= 0 ? (String) schemeIdsByUniqueName |
| .get(comboScheme.getText()) |
| : null; |
| } |
| |
| public final void init(final IWorkbench workbench) { |
| activityManager = workbench.getActivitySupport().getActivityManager(); |
| bindingService = (IBindingService) workbench.getService(IBindingService.class); |
| commandService = (ICommandService) workbench.getService(ICommandService.class); |
| contextService = (IContextService) workbench.getService(IContextService.class); |
| } |
| |
| /** |
| * Checks whether the activity manager knows anything about this command |
| * identifier. If the activity manager is currently filtering this command, |
| * then it does not appear in the user interface. |
| * |
| * @param command |
| * The command which should be checked against the activities; |
| * must not be <code>null</code>. |
| * @return <code>true</code> if the command identifier is not filtered; |
| * <code>false</code> if it is |
| */ |
| private final boolean isActive(final Command command) { |
| return activityManager.getIdentifier(command.getId()).isEnabled(); |
| } |
| |
| /** |
| * 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 = Util.translateString(RESOURCE_BUNDLE, |
| "PreferenceStoreError.Message"); //$NON-NLS-1$ |
| 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); |
| } |
| |
| public final boolean performCancel() { |
| // Save the selected tab for future reference. |
| persistSelectedTab(); |
| |
| return super.performCancel(); |
| } |
| |
| protected final void performDefaults() { |
| // Ask the user to confirm |
| final String title = Util.translateString(RESOURCE_BUNDLE, |
| "restoreDefaultsMessageBoxText"); //$NON-NLS-1$ |
| final String message = Util.translateString(RESOURCE_BUNDLE, |
| "restoreDefaultsMessageBoxMessage"); //$NON-NLS-1$ |
| 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()); // update the scheme |
| update(true); |
| super.performDefaults(); |
| } |
| |
| public final boolean performOk() { |
| // Save the preferences. |
| try { |
| bindingService.savePreferences( |
| localChangeManager.getActiveScheme(), localChangeManager |
| .getBindings()); |
| } catch (final IOException e) { |
| logPreferenceStoreException(e); |
| } |
| |
| // Save the selected tab for future reference. |
| persistSelectedTab(); |
| |
| return super.performOk(); |
| } |
| |
| /** |
| * Remembers the currently selected tab for when the preference page next |
| * opens. |
| */ |
| private final void persistSelectedTab() { |
| final IPreferenceStore store = getPreferenceStore(); |
| store.setValue(IPreferenceConstants.KEYS_PREFERENCE_SELECTED_TAB, |
| tabFolder.getSelectionIndex()); |
| } |
| |
| /** |
| * Handles the selection event on the add button. This removes all |
| * user-defined bindings matching the given key sequence, scheme and |
| * context. It then adds a new binding with the current selections. |
| */ |
| private final void selectedButtonAdd() { |
| final ParameterizedCommand command = getParameterizedCommand(); |
| final String contextId = getContextId(); |
| final String schemeId = getSchemeId(); |
| final KeySequence keySequence = getKeySequence(); |
| localChangeManager.removeBindings(keySequence, schemeId, contextId, |
| null, null, null, Binding.USER); |
| localChangeManager.addBinding(new KeyBinding(keySequence, command, |
| schemeId, contextId, null, null, null, Binding.USER)); |
| update(true); |
| } |
| |
| /** |
| * Provides a facility for exporting the viewable list of key bindings to a |
| * file. Currently, this only supports exporting to a list of |
| * comma-separated values. The user is prompted for which file should |
| * receive our bounty. |
| * |
| * @since 3.1 |
| */ |
| private final void selectedButtonExport() { |
| final FileDialog fileDialog = new FileDialog(getShell(), SWT.SAVE); |
| fileDialog.setFilterExtensions(new String[] { "*.csv" }); //$NON-NLS-1$ |
| fileDialog.setFilterNames(new String[] { Util.translateString( |
| RESOURCE_BUNDLE, "csvFilterName") }); //$NON-NLS-1$ |
| 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 FileWriter(filePath)); |
| final TableItem[] items = tableBindings.getItems(); |
| final int numColumns = tableBindings.getColumnCount(); |
| for (int i = 0; i < items.length; i++) { |
| final TableItem item = items[i]; |
| for (int j = 0; j < numColumns; j++) { |
| String buf = Util.replaceAll(item.getText(j), "\"", //$NON-NLS-1$ |
| "\"\""); //$NON-NLS-1$ |
| fileWriter.write("\"" + buf + "\""); //$NON-NLS-1$//$NON-NLS-2$ |
| if (j < numColumns - 1) { |
| fileWriter.write(','); |
| } |
| } |
| fileWriter.write(System.getProperty("line.separator")); //$NON-NLS-1$ |
| } |
| |
| } finally { |
| if (fileWriter != null) { |
| try { |
| fileWriter.close(); |
| } catch (final IOException e) { |
| // At least I tried. |
| } |
| } |
| |
| } |
| } |
| }; |
| SafeRunner.run(runnable); |
| } |
| |
| /** |
| * Handles the selection event on the remove button. This removes all |
| * user-defined bindings matching the given key sequence, scheme and |
| * context. It then adds a new deletion binding for the selected trigger |
| * sequence. |
| */ |
| private final void selectedButtonRemove() { |
| final String contextId = getContextId(); |
| final String schemeId = getSchemeId(); |
| final KeySequence keySequence = getKeySequence(); |
| localChangeManager.removeBindings(keySequence, schemeId, contextId, |
| null, null, null, Binding.USER); |
| localChangeManager.addBinding(new KeyBinding(keySequence, null, |
| schemeId, contextId, null, null, null, Binding.USER)); |
| update(true); |
| } |
| |
| /** |
| * Handles the selection event on the restore button. This removes all |
| * user-defined bindings matching the given key sequence, scheme and |
| * context. |
| */ |
| private final void selectedButtonRestore() { |
| String contextId = getContextId(); |
| String schemeId = getSchemeId(); |
| KeySequence keySequence = getKeySequence(); |
| localChangeManager.removeBindings(keySequence, schemeId, contextId, |
| null, null, null, Binding.USER); |
| update(true); |
| } |
| |
| /** |
| * Updates the local managers active scheme, and then updates the interface. |
| */ |
| private final void selectedComboScheme() { |
| final String activeSchemeId = getSchemeId(); |
| final Scheme activeScheme = localChangeManager |
| .getScheme(activeSchemeId); |
| try { |
| localChangeManager.setActiveScheme(activeScheme); |
| } catch (final NotDefinedException e) { |
| // Oh, well. |
| } |
| update(true); |
| } |
| |
| /** |
| * Handles the selection event on the table containing the bindings for a |
| * particular command. This updates the context and trigger sequence based |
| * on the selected binding. |
| */ |
| private final void selectedTableBindingsForCommand() { |
| final int selection = tableBindingsForCommand.getSelectionIndex(); |
| if ((selection >= 0) |
| && (selection < tableBindingsForCommand.getItemCount())) { |
| final TableItem item = tableBindingsForCommand.getItem(selection); |
| final KeyBinding binding = (KeyBinding) item.getData(ITEM_DATA_KEY); |
| setContextId(binding.getContextId()); |
| setKeySequence(binding.getKeySequence()); |
| } |
| |
| update(); |
| } |
| |
| /** |
| * Handles the selection event on the table containing the bindings for a |
| * particular trigger sequence. This updates the context based on the |
| * selected binding. |
| */ |
| private final void selectedTableBindingsForTriggerSequence() { |
| final int selection = tableBindingsForTriggerSequence |
| .getSelectionIndex(); |
| if ((selection >= 0) |
| && (selection < tableBindingsForTriggerSequence.getItemCount())) { |
| final TableItem item = tableBindingsForTriggerSequence |
| .getItem(selection); |
| final Binding binding = (Binding) item.getData(ITEM_DATA_KEY); |
| setContextId(binding.getContextId()); |
| } |
| |
| update(); |
| } |
| |
| /** |
| * Responds to some kind of trigger on the View tab by taking the current |
| * selection on the key bindings table and selecting the appropriate items |
| * in the Modify tab. |
| * |
| * @since 3.1 |
| */ |
| private final void selectedTableKeyBindings() { |
| final int selectionIndex = tableBindings.getSelectionIndex(); |
| if (selectionIndex != -1) { |
| final TableItem item = tableBindings.getItem(selectionIndex); |
| final Binding binding = (Binding) item.getData(BINDING_KEY); |
| editBinding(binding); |
| |
| } else { |
| editBinding(null); |
| } |
| } |
| |
| /** |
| * Changes the selected context name in the context combo box. The context |
| * selected is either the one matching the identifier provided (if |
| * possible), or the default context identifier. If no matching name can be |
| * found in the combo, then the first item is selected. |
| * |
| * @param contextId |
| * The context identifier for the context to be selected in the |
| * combo box; may be <code>null</code>. |
| */ |
| private final void setContextId(final String contextId) { |
| // Clear the current selection. |
| comboContext.clearSelection(); |
| comboContext.deselectAll(); |
| |
| // Figure out which name to look for. |
| String contextName = (String) contextUniqueNamesById.get(contextId); |
| if (contextName == null) { |
| contextName = (String) contextUniqueNamesById |
| .get(IContextIds.CONTEXT_ID_WINDOW); |
| } |
| if (contextName == null) { |
| contextName = Util.ZERO_LENGTH_STRING; |
| } |
| |
| // Scan the list for the selection we're looking for. |
| final String[] items = comboContext.getItems(); |
| boolean found = false; |
| for (int i = 0; i < items.length; i++) { |
| if (contextName.equals(items[i])) { |
| comboContext.select(i); |
| found = true; |
| break; |
| } |
| } |
| |
| // If we didn't find an item, then set the first item as selected. |
| if ((!found) && (items.length > 0)) { |
| comboContext.select(0); |
| } |
| } |
| |
| /** |
| * Sets the current trigger sequence. |
| * |
| * @param keySequence |
| * The trigger sequence; may be <code>null</code>. |
| */ |
| private final void setKeySequence(final KeySequence keySequence) { |
| textTriggerSequenceManager.setKeySequence(keySequence); |
| } |
| |
| /** |
| * Changes the selection in the command combo box. |
| * |
| * @param command |
| * The fully-parameterized command to select; may be |
| * <code>null</code>. |
| */ |
| private final void setParameterizedCommand( |
| final ParameterizedCommand command) { |
| int i = 0; |
| if (commands != null) { |
| final int commandCount = commands.length; |
| for (; i < commandCount; i++) { |
| if (commands[i].equals(command)) { |
| if ((comboCommand.getSelectionIndex() != i) |
| && (i < comboCommand.getItemCount())) { |
| comboCommand.select(i); |
| } |
| break; |
| } |
| } |
| if ((i >= comboCommand.getItemCount()) |
| && (comboCommand.getSelectionIndex() != 0)) { |
| comboCommand.select(0); |
| } |
| } |
| } |
| |
| /** |
| * Sets the currently selected scheme |
| * |
| * @param scheme |
| * The scheme to select; may be <code>null</code>. |
| */ |
| private final void setScheme(final Scheme scheme) { |
| comboScheme.clearSelection(); |
| comboScheme.deselectAll(); |
| final String schemeUniqueName = (String) schemeUniqueNamesById |
| .get(scheme.getId()); |
| |
| if (schemeUniqueName != null) { |
| final String items[] = comboScheme.getItems(); |
| |
| for (int i = 0; i < items.length; i++) { |
| if (schemeUniqueName.equals(items[i])) { |
| comboScheme.select(i); |
| break; |
| } |
| } |
| } |
| } |
| |
| /** |
| * Builds the internal look-up tables before allowing the page to become |
| * visible. |
| */ |
| public final void setVisible(final boolean visible) { |
| if (visible == true) { |
| Map contextsByName = new HashMap(); |
| |
| for (Iterator iterator = contextService.getDefinedContextIds() |
| .iterator(); iterator.hasNext();) { |
| Context context = contextService.getContext((String) iterator |
| .next()); |
| try { |
| String name = context.getName(); |
| Collection contexts = (Collection) contextsByName.get(name); |
| |
| if (contexts == null) { |
| contexts = new HashSet(); |
| contextsByName.put(name, contexts); |
| } |
| |
| contexts.add(context); |
| } catch (final NotDefinedException e) { |
| // Do nothing. |
| } |
| } |
| |
| Map commandsByName = new HashMap(); |
| |
| for (Iterator iterator = commandService.getDefinedCommandIds() |
| .iterator(); iterator.hasNext();) { |
| Command command = commandService.getCommand((String) iterator |
| .next()); |
| if (!isActive(command)) { |
| continue; |
| } |
| |
| try { |
| String name = command.getName(); |
| Collection commands = (Collection) commandsByName.get(name); |
| |
| if (commands == null) { |
| commands = new HashSet(); |
| commandsByName.put(name, commands); |
| } |
| |
| commands.add(command); |
| } catch (NotDefinedException eNotDefined) { |
| // Do nothing |
| } |
| } |
| |
| // moved here to allow us to remove any empty categories |
| commandIdsByCategoryId = new HashMap(); |
| |
| for (Iterator iterator = commandService.getDefinedCommandIds() |
| .iterator(); iterator.hasNext();) { |
| final Command command = commandService |
| .getCommand((String) iterator.next()); |
| if (!isActive(command)) { |
| continue; |
| } |
| |
| try { |
| String categoryId = command.getCategory().getId(); |
| Collection commandIds = (Collection) commandIdsByCategoryId |
| .get(categoryId); |
| |
| if (commandIds == null) { |
| commandIds = new HashSet(); |
| commandIdsByCategoryId.put(categoryId, commandIds); |
| } |
| |
| commandIds.add(command.getId()); |
| } catch (NotDefinedException eNotDefined) { |
| // Do nothing |
| } |
| } |
| |
| Map categoriesByName = new HashMap(); |
| |
| for (Iterator iterator = commandService.getDefinedCategoryIds() |
| .iterator(); iterator.hasNext();) { |
| Category category = commandService |
| .getCategory((String) iterator.next()); |
| |
| try { |
| if (commandIdsByCategoryId.containsKey(category.getId())) { |
| String name = category.getName(); |
| Collection categories = (Collection) categoriesByName |
| .get(name); |
| |
| if (categories == null) { |
| categories = new HashSet(); |
| categoriesByName.put(name, categories); |
| } |
| |
| categories.add(category); |
| } |
| } catch (NotDefinedException eNotDefined) { |
| // Do nothing |
| } |
| } |
| |
| Map schemesByName = new HashMap(); |
| |
| final Scheme[] definedSchemes = bindingService.getDefinedSchemes(); |
| for (int i = 0; i < definedSchemes.length; i++) { |
| final Scheme scheme = definedSchemes[i]; |
| try { |
| String name = scheme.getName(); |
| Collection schemes = (Collection) schemesByName.get(name); |
| |
| if (schemes == null) { |
| schemes = new HashSet(); |
| schemesByName.put(name, schemes); |
| } |
| |
| schemes.add(scheme); |
| } catch (final NotDefinedException e) { |
| // Do nothing. |
| } |
| } |
| |
| contextIdsByUniqueName = new HashMap(); |
| contextUniqueNamesById = new HashMap(); |
| |
| for (Iterator iterator = contextsByName.entrySet().iterator(); iterator |
| .hasNext();) { |
| Map.Entry entry = (Map.Entry) iterator.next(); |
| String name = (String) entry.getKey(); |
| Set contexts = (Set) entry.getValue(); |
| Iterator iterator2 = contexts.iterator(); |
| |
| if (contexts.size() == 1) { |
| Context context = (Context) iterator2.next(); |
| contextIdsByUniqueName.put(name, context.getId()); |
| contextUniqueNamesById.put(context.getId(), name); |
| } else { |
| while (iterator2.hasNext()) { |
| Context context = (Context) iterator2.next(); |
| String uniqueName = MessageFormat.format( |
| Util.translateString(RESOURCE_BUNDLE, |
| "uniqueName"), new Object[] { name, //$NON-NLS-1$ |
| context.getId() }); |
| contextIdsByUniqueName.put(uniqueName, context.getId()); |
| contextUniqueNamesById.put(context.getId(), uniqueName); |
| } |
| } |
| } |
| |
| categoryIdsByUniqueName = new HashMap(); |
| categoryUniqueNamesById = new HashMap(); |
| |
| for (Iterator iterator = categoriesByName.entrySet().iterator(); iterator |
| .hasNext();) { |
| Map.Entry entry = (Map.Entry) iterator.next(); |
| String name = (String) entry.getKey(); |
| Set categories = (Set) entry.getValue(); |
| Iterator iterator2 = categories.iterator(); |
| |
| if (categories.size() == 1) { |
| Category category = (Category) iterator2.next(); |
| categoryIdsByUniqueName.put(name, category.getId()); |
| categoryUniqueNamesById.put(category.getId(), name); |
| } else { |
| while (iterator2.hasNext()) { |
| Category category = (Category) iterator2.next(); |
| String uniqueName = MessageFormat.format( |
| Util.translateString(RESOURCE_BUNDLE, |
| "uniqueName"), new Object[] { name, //$NON-NLS-1$ |
| category.getId() }); |
| categoryIdsByUniqueName.put(uniqueName, category |
| .getId()); |
| categoryUniqueNamesById.put(category.getId(), |
| uniqueName); |
| } |
| } |
| } |
| |
| schemeIdsByUniqueName = new HashMap(); |
| schemeUniqueNamesById = new HashMap(); |
| |
| for (Iterator iterator = schemesByName.entrySet().iterator(); iterator |
| .hasNext();) { |
| Map.Entry entry = (Map.Entry) iterator.next(); |
| String name = (String) entry.getKey(); |
| Set keyConfigurations = (Set) entry.getValue(); |
| Iterator iterator2 = keyConfigurations.iterator(); |
| |
| if (keyConfigurations.size() == 1) { |
| Scheme scheme = (Scheme) iterator2.next(); |
| schemeIdsByUniqueName.put(name, scheme.getId()); |
| schemeUniqueNamesById.put(scheme.getId(), name); |
| } else { |
| while (iterator2.hasNext()) { |
| Scheme scheme = (Scheme) iterator2.next(); |
| String uniqueName = MessageFormat.format( |
| Util.translateString(RESOURCE_BUNDLE, |
| "uniqueName"), new Object[] { name, //$NON-NLS-1$ |
| scheme.getId() }); |
| schemeIdsByUniqueName.put(uniqueName, scheme.getId()); |
| schemeUniqueNamesById.put(scheme.getId(), uniqueName); |
| } |
| } |
| } |
| |
| Scheme activeScheme = bindingService.getActiveScheme(); |
| |
| // Make an internal copy of the binding manager, for local changes. |
| 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()); |
| |
| // Populate the category combo box. |
| List categoryNames = new ArrayList(categoryIdsByUniqueName.keySet()); |
| Collections.sort(categoryNames, Collator.getInstance()); |
| if (commandIdsByCategoryId.containsKey(null)) { |
| categoryNames.add(0, Util.translateString(RESOURCE_BUNDLE, |
| "other")); //$NON-NLS-1$ |
| } |
| comboCategory.setItems((String[]) categoryNames |
| .toArray(new String[categoryNames.size()])); |
| comboCategory.clearSelection(); |
| comboCategory.deselectAll(); |
| if (commandIdsByCategoryId.containsKey(null) |
| || !categoryNames.isEmpty()) { |
| comboCategory.select(0); |
| } |
| |
| // Populate the scheme combo box. |
| List schemeNames = new ArrayList(schemeIdsByUniqueName.keySet()); |
| Collections.sort(schemeNames, Collator.getInstance()); |
| comboScheme.setItems((String[]) schemeNames |
| .toArray(new String[schemeNames.size()])); |
| setScheme(activeScheme); |
| |
| // Update the entire page. |
| update(true); |
| } |
| |
| super.setVisible(visible); |
| } |
| |
| /** |
| * Updates the entire preference page -- except the view tab -- based on |
| * current selection sate. This preference page is written so that |
| * everything can be made consistent simply by inspecting the state of its |
| * widgets. A change is triggered by the user, and an event is fired. The |
| * event triggers an update. It is possible for extra work to be done by |
| * this page before calling update. |
| */ |
| private final void update() { |
| update(false); |
| } |
| |
| /** |
| * Updates the entire preference page based on current changes. This |
| * preference page is written so that everything can be made consistent |
| * simply by inspecting the state of its widgets. A change is triggered by |
| * the user, and an event is fired. The event triggers an update. It is |
| * possible for extra work to be done by this page before calling update. |
| * |
| * @param updateViewTab |
| * Whether the view tab should be updated as well. |
| */ |
| private final void update(final boolean updateViewTab) { |
| if (updateViewTab) { |
| updateViewTab(); |
| } |
| updateComboCommand(); |
| updateComboContext(); |
| final TriggerSequence triggerSequence = getKeySequence(); |
| updateTableBindingsForTriggerSequence(triggerSequence); |
| final ParameterizedCommand command = getParameterizedCommand(); |
| updateTableBindingsForCommand(command); |
| final String contextId = getContextId(); |
| updateSelection(tableBindingsForTriggerSequence, contextId, |
| triggerSequence); |
| updateSelection(tableBindingsForCommand, contextId, triggerSequence); |
| updateLabelSchemeExtends(); |
| updateLabelContextExtends(); |
| updateEnabled(triggerSequence, command); |
| } |
| |
| /** |
| * Updates the contents of the commands combo box, based on the current |
| * selection in the category combo box. |
| */ |
| private final void updateComboCommand() { |
| // Remember the current selection, so we can restore it later. |
| final ParameterizedCommand command = getParameterizedCommand(); |
| |
| // Figure out where command identifiers apply to the selected category. |
| final String categoryId = getCategoryId(); |
| Set commandIds = (Set) commandIdsByCategoryId.get(categoryId); |
| if (commandIds==null) { |
| commandIds = Collections.EMPTY_SET; |
| } |
| |
| /* |
| * Generate an array of parameterized commands based on these |
| * identifiers. The parameterized commands will be sorted based on their |
| * names. |
| */ |
| List 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. |
| } |
| } |
| |
| // sort the commands with a collator, so they appear in the |
| // combo correctly |
| commands = sortParameterizedCommands(commands); |
| |
| final int commandCount = commands.size(); |
| this.commands = (ParameterizedCommand[]) commands |
| .toArray(new ParameterizedCommand[commandCount]); |
| |
| /* |
| * Generate an array of command names based on this array of |
| * parameterized commands. |
| */ |
| final String[] commandNames = new String[commandCount]; |
| for (int i = 0; i < commandCount; i++) { |
| try { |
| commandNames[i] = this.commands[i].getName(); |
| } catch (final NotDefinedException e) { |
| throw new Error( |
| "Concurrent modification of the command's defined state"); //$NON-NLS-1$ |
| } |
| } |
| |
| /* |
| * Copy the command names into the combo box, but only if they've |
| * changed. We do this to try to avoid unnecessary calls out to the |
| * operating system, as well as to defend against bugs in SWT's event |
| * mechanism. |
| */ |
| final String[] currentItems = comboCommand.getItems(); |
| if (!Arrays.equals(currentItems, commandNames)) { |
| comboCommand.setItems(commandNames); |
| } |
| |
| // Try to restore the selection. |
| setParameterizedCommand(command); |
| |
| /* |
| * Just to be extra careful, make sure that we have a selection at this |
| * point. This line could probably be removed, but it makes the code a |
| * bit more robust. |
| */ |
| if ((comboCommand.getSelectionIndex() == -1) && (commandCount > 0)) { |
| comboCommand.select(0); |
| } |
| } |
| |
| /** |
| * Sort the commands using the correct language. |
| * @param commands the List of ParameterizedCommands |
| * @return The sorted List |
| */ |
| private List sortParameterizedCommands(List commands) { |
| final Collator collator = Collator.getInstance(); |
| |
| // this comparator is based on the ParameterizedCommands#compareTo(*) |
| // method, but uses the collator. |
| Comparator comparator = new Comparator() { |
| public int compare(Object o1, Object o2) { |
| String name1 = null; |
| String name2 = null; |
| try { |
| name1 = ((ParameterizedCommand) o1).getName(); |
| } catch (NotDefinedException e) { |
| return -1; |
| } |
| try { |
| name2 = ((ParameterizedCommand) o2).getName(); |
| } catch (NotDefinedException e) { |
| return 1; |
| } |
| int rc = collator.compare(name1, name2); |
| if (rc != 0) { |
| return rc; |
| } |
| |
| String id1 = ((ParameterizedCommand) o1).getId(); |
| String id2 = ((ParameterizedCommand) o2).getId(); |
| return collator.compare(id1, id2); |
| } |
| }; |
| Collections.sort(commands, comparator); |
| return commands; |
| } |
| |
| /** |
| * Updates the contents of the context combo box, as well as its selection. |
| */ |
| private final void updateComboContext() { |
| final String contextId = getContextId(); |
| final Map contextIdsByName = new HashMap(contextIdsByUniqueName); |
| |
| final List contextNames = new ArrayList(contextIdsByName.keySet()); |
| Collections.sort(contextNames, Collator.getInstance()); |
| |
| comboContext.setItems((String[]) contextNames |
| .toArray(new String[contextNames.size()])); |
| setContextId(contextId); |
| |
| if (comboContext.getSelectionIndex() == -1 && !contextNames.isEmpty()) { |
| comboContext.select(0); |
| } |
| } |
| |
| /** |
| * Updates the enabled state of the various widgets on this page. The |
| * decision is based on the current trigger sequence and the currently |
| * selected command. |
| * |
| * @param triggerSequence |
| * The current trigger sequence; may be empty, but never |
| * <code>null</code>. |
| * @param command |
| * The currently selected command, if any; <code>null</code> |
| * otherwise. |
| */ |
| private final void updateEnabled(final TriggerSequence triggerSequence, |
| final ParameterizedCommand command) { |
| final boolean commandSelected = command != null; |
| labelBindingsForCommand.setEnabled(commandSelected); |
| tableBindingsForCommand.setEnabled(commandSelected); |
| |
| final boolean triggerSequenceSelected = !triggerSequence.isEmpty(); |
| labelBindingsForTriggerSequence.setEnabled(triggerSequenceSelected); |
| tableBindingsForTriggerSequence.setEnabled(triggerSequenceSelected); |
| |
| /* |
| * TODO Do some better button enablement. |
| */ |
| final boolean buttonsEnabled = commandSelected |
| && triggerSequenceSelected; |
| buttonAdd.setEnabled(buttonsEnabled); |
| buttonRemove.setEnabled(buttonsEnabled); |
| buttonRestore.setEnabled(buttonsEnabled); |
| } |
| |
| /** |
| * Updates the label next to the context that says "extends" if the context |
| * is a child of another context. If the context is not a child of another |
| * context, then the label is simply blank. |
| */ |
| private final void updateLabelContextExtends() { |
| final String contextId = getContextId(); |
| |
| if (contextId != null) { |
| final Context context = contextService.getContext(getContextId()); |
| if (context.isDefined()) { |
| try { |
| final String parentId = context.getParentId(); |
| if (parentId != null) { |
| final String name = (String) contextUniqueNamesById |
| .get(parentId); |
| if (name != null) { |
| labelContextExtends.setText(MessageFormat.format( |
| Util.translateString(RESOURCE_BUNDLE, |
| "extends"), //$NON-NLS-1$ |
| new Object[] { name })); |
| return; |
| } |
| } |
| } catch (final NotDefinedException e) { |
| // Do nothing |
| } |
| } |
| } |
| |
| labelContextExtends.setText(Util.ZERO_LENGTH_STRING); |
| } |
| |
| /** |
| * Updates the label next to the scheme that says "extends" if the scheme is |
| * a child of another scheme. If the scheme is not a child of another |
| * scheme, then the label is simply blank. |
| */ |
| private final void updateLabelSchemeExtends() { |
| final String schemeId = getSchemeId(); |
| |
| if (schemeId != null) { |
| final Scheme scheme = bindingService.getScheme(schemeId); |
| try { |
| final String name = (String) schemeUniqueNamesById.get(scheme |
| .getParentId()); |
| if (name != null) { |
| labelSchemeExtends.setText(MessageFormat.format(Util |
| .translateString(RESOURCE_BUNDLE, "extends"), //$NON-NLS-1$ |
| new Object[] { name })); |
| return; |
| } |
| } catch (final NotDefinedException e) { |
| // Do nothing |
| } |
| } |
| |
| labelSchemeExtends.setText(Util.ZERO_LENGTH_STRING); |
| } |
| |
| /** |
| * Tries to select the correct entry in table based on the currently |
| * selected context and trigger sequence. If the table hasn't really |
| * changed, then this method is essentially trying to restore the selection. |
| * If it has changed, then it is trying to select the most entry based on |
| * the context. |
| * |
| * @param table |
| * The table to be changed; must not be <code>null</code>. |
| * @param contextId |
| * The currently selected context; should not be |
| * <code>null</code>. |
| * @param triggerSequence |
| * The current trigger sequence; should not be <code>null</code>. |
| */ |
| private final void updateSelection(final Table table, |
| final String contextId, final TriggerSequence triggerSequence) { |
| if (table.getSelectionCount() > 1) { |
| table.deselectAll(); |
| } |
| |
| final TableItem[] items = table.getItems(); |
| int selection = -1; |
| for (int i = 0; i < items.length; i++) { |
| final Binding binding = (Binding) items[i].getData(ITEM_DATA_KEY); |
| if ((Util.equals(contextId, binding.getContextId())) |
| && (Util.equals(triggerSequence, binding |
| .getTriggerSequence()))) { |
| selection = i; |
| break; |
| } |
| } |
| |
| if (selection != -1) { |
| table.select(selection); |
| } |
| } |
| |
| /** |
| * Updates the contents of the table showing the bindings for the currently |
| * selected command. The selection is destroyed by this process. |
| * |
| * @param parameterizedCommand |
| * The currently selected fully-parameterized command; may be |
| * <code>null</code>. |
| */ |
| private final void updateTableBindingsForCommand( |
| final ParameterizedCommand parameterizedCommand) { |
| // Clear the table of existing items. |
| tableBindingsForCommand.removeAll(); |
| |
| // Add each of the bindings, if the command identifier matches. |
| final Collection bindings = localChangeManager |
| .getActiveBindingsDisregardingContextFlat(); |
| final Iterator bindingItr = bindings.iterator(); |
| while (bindingItr.hasNext()) { |
| final Binding binding = (Binding) bindingItr.next(); |
| if (!Util.equals(parameterizedCommand, binding |
| .getParameterizedCommand())) { |
| continue; // binding does not match |
| } |
| |
| final TableItem tableItem = new TableItem(tableBindingsForCommand, |
| SWT.NULL); |
| tableItem.setData(ITEM_DATA_KEY, binding); |
| |
| /* |
| * Set the associated image based on the type of binding. Either it |
| * is a user binding or a system binding. |
| * |
| * TODO Identify more image types. |
| */ |
| if (binding.getType() == Binding.SYSTEM) { |
| tableItem.setImage(0, IMAGE_BLANK); |
| } else { |
| tableItem.setImage(0, IMAGE_CHANGE); |
| } |
| |
| String contextName = (String) contextUniqueNamesById.get(binding |
| .getContextId()); |
| if (contextName == null) { |
| contextName = Util.ZERO_LENGTH_STRING; |
| } |
| tableItem.setText(1, contextName); |
| tableItem.setText(2, binding.getTriggerSequence().format()); |
| } |
| } |
| |
| /** |
| * Updates the contents of the table showing the bindings for the current |
| * trigger sequence. The selection is destroyed by this process. |
| * |
| * @param triggerSequence |
| * The current trigger sequence; may be <code>null</code> or |
| * empty. |
| */ |
| private final void updateTableBindingsForTriggerSequence( |
| final TriggerSequence triggerSequence) { |
| // Clear the table of its existing items. |
| tableBindingsForTriggerSequence.removeAll(); |
| |
| // Get the collection of bindings for the current command. |
| final Map activeBindings = localChangeManager |
| .getActiveBindingsDisregardingContext(); |
| final Collection bindings = (Collection) activeBindings |
| .get(triggerSequence); |
| if (bindings == null) { |
| return; |
| } |
| |
| // Add each of the bindings. |
| final Iterator bindingItr = bindings.iterator(); |
| while (bindingItr.hasNext()) { |
| final Binding binding = (Binding) bindingItr.next(); |
| final Context context = contextService.getContext(binding |
| .getContextId()); |
| final ParameterizedCommand parameterizedCommand = binding |
| .getParameterizedCommand(); |
| final Command command = parameterizedCommand.getCommand(); |
| if ((!context.isDefined()) && (!command.isDefined())) { |
| continue; |
| } |
| |
| final TableItem tableItem = new TableItem( |
| tableBindingsForTriggerSequence, SWT.NULL); |
| tableItem.setData(ITEM_DATA_KEY, binding); |
| |
| /* |
| * Set the associated image based on the type of binding. Either it |
| * is a user binding or a system binding. |
| * |
| * TODO Identify more image types. |
| */ |
| if (binding.getType() == Binding.SYSTEM) { |
| tableItem.setImage(0, IMAGE_BLANK); |
| } else { |
| tableItem.setImage(0, IMAGE_CHANGE); |
| } |
| |
| try { |
| tableItem.setText(1, context.getName()); |
| tableItem.setText(2, parameterizedCommand.getName()); |
| } catch (final NotDefinedException e) { |
| throw new Error( |
| "Context or command became undefined on a non-UI thread while the UI thread was processing."); //$NON-NLS-1$ |
| } |
| } |
| } |
| |
| /** |
| * Updates the contents of the view tab. This queries the command manager |
| * for a list of key sequence binding definitions, and these definitions are |
| * then added to the table. |
| * |
| * @since 3.1 |
| */ |
| private final void updateViewTab() { |
| // Clear out the existing table contents. |
| tableBindings.removeAll(); |
| |
| // Get a sorted list of key binding contents. |
| final List bindings = new ArrayList(localChangeManager |
| .getActiveBindingsDisregardingContextFlat()); |
| Collections.sort(bindings, new Comparator() { |
| /** |
| * Compares two instances of <code>Binding</code> based on the |
| * current sort order. |
| * |
| * @param object1 |
| * The first object to compare; must be an instance of |
| * <code>Binding</code> (i.e., not <code>null</code>). |
| * @param object2 |
| * The second object to compare; must be an instance of |
| * <code>Binding</code> (i.e., not <code>null</code>). |
| * @return The integer value representing the comparison. The |
| * comparison is based on the current sort order. |
| * @since 3.1 |
| */ |
| public final int compare(final Object object1, final Object object2) { |
| final Binding binding1 = (Binding) object1; |
| final Binding binding2 = (Binding) object2; |
| |
| /* |
| * Get the category name, command name, formatted key sequence |
| * and context name for the first binding. |
| */ |
| final Command command1 = binding1.getParameterizedCommand() |
| .getCommand(); |
| String categoryName1 = Util.ZERO_LENGTH_STRING; |
| String commandName1 = Util.ZERO_LENGTH_STRING; |
| try { |
| commandName1 = command1.getName(); |
| categoryName1 = command1.getCategory().getName(); |
| } catch (final NotDefinedException e) { |
| // Just use the zero-length string. |
| } |
| final String triggerSequence1 = binding1.getTriggerSequence() |
| .format(); |
| final String contextId1 = binding1.getContextId(); |
| String contextName1 = Util.ZERO_LENGTH_STRING; |
| if (contextId1 != null) { |
| final Context context = contextService |
| .getContext(contextId1); |
| try { |
| contextName1 = context.getName(); |
| } catch (final org.eclipse.core.commands.common.NotDefinedException e) { |
| // Just use the zero-length string. |
| } |
| } |
| |
| /* |
| * Get the category name, command name, formatted key sequence |
| * and context name for the first binding. |
| */ |
| final Command command2 = binding2.getParameterizedCommand() |
| .getCommand(); |
| String categoryName2 = Util.ZERO_LENGTH_STRING; |
| String commandName2 = Util.ZERO_LENGTH_STRING; |
| try { |
| commandName2 = command2.getName(); |
| categoryName2 = command2.getCategory().getName(); |
| } catch (final org.eclipse.core.commands.common.NotDefinedException e) { |
| // Just use the zero-length string. |
| } |
| final String keySequence2 = binding2.getTriggerSequence() |
| .format(); |
| final String contextId2 = binding2.getContextId(); |
| String contextName2 = Util.ZERO_LENGTH_STRING; |
| if (contextId2 != null) { |
| final Context context = contextService |
| .getContext(contextId2); |
| try { |
| contextName2 = context.getName(); |
| } catch (final org.eclipse.core.commands.common.NotDefinedException e) { |
| // Just use the zero-length string. |
| } |
| } |
| |
| // Compare the items in the current sort order. |
| int compare = 0; |
| for (int i = 0; i < sortOrder.length; i++) { |
| switch (sortOrder[i]) { |
| case VIEW_CATEGORY_COLUMN_INDEX: |
| compare = Util.compare(categoryName1, categoryName2); |
| if (compare != 0) { |
| return compare; |
| } |
| break; |
| case VIEW_COMMAND_COLUMN_INDEX: |
| compare = Util.compare(commandName1, commandName2); |
| if (compare != 0) { |
| return compare; |
| } |
| break; |
| case VIEW_KEY_SEQUENCE_COLUMN_INDEX: |
| compare = Util.compare(triggerSequence1, keySequence2); |
| if (compare != 0) { |
| return compare; |
| } |
| break; |
| case VIEW_CONTEXT_COLUMN_INDEX: |
| compare = Util.compare(contextName1, contextName2); |
| if (compare != 0) { |
| return compare; |
| } |
| break; |
| default: |
| throw new Error( |
| "Programmer error: added another sort column without modifying the comparator."); //$NON-NLS-1$ |
| } |
| } |
| |
| return compare; |
| } |
| |
| /** |
| * @see Object#equals(java.lang.Object) |
| */ |
| public final boolean equals(final Object object) { |
| return super.equals(object); |
| } |
| }); |
| |
| // Add a table item for each item in the list. |
| final Iterator keyBindingItr = bindings.iterator(); |
| while (keyBindingItr.hasNext()) { |
| final Binding binding = (Binding) keyBindingItr.next(); |
| |
| // Get the command and category name. |
| final ParameterizedCommand command = binding |
| .getParameterizedCommand(); |
| String commandName = Util.ZERO_LENGTH_STRING; |
| String categoryName = Util.ZERO_LENGTH_STRING; |
| try { |
| commandName = command.getName(); |
| categoryName = command.getCommand().getCategory().getName(); |
| } catch (final org.eclipse.core.commands.common.NotDefinedException e) { |
| // Just use the zero-length string. |
| } |
| |
| // Ignore items with a meaningless command name. |
| if ((commandName == null) || (commandName.length() == 0)) { |
| continue; |
| } |
| |
| // Get the context name. |
| final String contextId = binding.getContextId(); |
| String contextName = Util.ZERO_LENGTH_STRING; |
| if (contextId != null) { |
| final Context context = contextService.getContext(contextId); |
| try { |
| contextName = context.getName(); |
| } catch (final org.eclipse.core.commands.common.NotDefinedException e) { |
| // Just use the zero-length string. |
| } |
| } |
| |
| // Create the table item. |
| final TableItem item = new TableItem(tableBindings, SWT.NONE); |
| item.setText(VIEW_CATEGORY_COLUMN_INDEX, categoryName); |
| item.setText(VIEW_COMMAND_COLUMN_INDEX, commandName); |
| item.setText(VIEW_KEY_SEQUENCE_COLUMN_INDEX, binding |
| .getTriggerSequence().format()); |
| item.setText(VIEW_CONTEXT_COLUMN_INDEX, contextName); |
| item.setData(BINDING_KEY, binding); |
| } |
| |
| // Pack the columns. |
| for (int i = 0; i < tableBindings.getColumnCount(); i++) { |
| tableBindings.getColumn(i).pack(); |
| } |
| } |
| |
| |
| } |