blob: a272d06183207fc1504a16c96bb07f5371e2b703 [file] [log] [blame]
/*******************************************************************************
* 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();
}
}
}