0001: /*******************************************************************************
0002: * Copyright (c) 2000, 2007 IBM Corporation and others.
0003: * All rights reserved. This program and the accompanying materials
0004: * are made available under the terms of the Eclipse Public License v1.0
0005: * which accompanies this distribution, and is available at
0006: * http://www.eclipse.org/legal/epl-v10.html
0007: *
0008: * Contributors:
0009: * IBM Corporation - initial API and implementation
0010: *******************************************************************************/package org.eclipse.ui.internal.keys;
0011:
0012: import java.io.BufferedWriter;
0013: import java.io.FileWriter;
0014: import java.io.IOException;
0015: import java.io.Writer;
0016: import java.util.ArrayList;
0017: import java.util.Arrays;
0018: import java.util.Collection;
0019: import java.util.Collections;
0020: import java.util.Comparator;
0021: import java.util.HashMap;
0022: import java.util.HashSet;
0023: import java.util.Iterator;
0024: import java.util.List;
0025: import java.util.Map;
0026: import java.util.ResourceBundle;
0027: import java.util.Set;
0028:
0029: import org.eclipse.core.commands.Category;
0030: import org.eclipse.core.commands.Command;
0031: import org.eclipse.core.commands.CommandManager;
0032: import org.eclipse.core.commands.ParameterizedCommand;
0033: import org.eclipse.core.commands.common.NotDefinedException;
0034: import org.eclipse.core.commands.contexts.Context;
0035: import org.eclipse.core.commands.contexts.ContextManager;
0036: import org.eclipse.core.runtime.IStatus;
0037: import org.eclipse.core.runtime.SafeRunner;
0038: import org.eclipse.core.runtime.Status;
0039: import org.eclipse.jface.bindings.Binding;
0040: import org.eclipse.jface.bindings.BindingManager;
0041: import org.eclipse.jface.bindings.Scheme;
0042: import org.eclipse.jface.bindings.TriggerSequence;
0043: import org.eclipse.jface.bindings.keys.KeyBinding;
0044: import org.eclipse.jface.bindings.keys.KeySequence;
0045: import org.eclipse.jface.bindings.keys.KeySequenceText;
0046: import org.eclipse.jface.bindings.keys.KeyStroke;
0047: import org.eclipse.jface.contexts.IContextIds;
0048: import org.eclipse.jface.dialogs.IDialogConstants;
0049: import org.eclipse.jface.dialogs.MessageDialog;
0050: import org.eclipse.jface.preference.IPreferenceStore;
0051: import org.eclipse.jface.preference.PreferencePage;
0052: import org.eclipse.jface.util.SafeRunnable;
0053: import org.eclipse.swt.SWT;
0054: import org.eclipse.swt.events.DisposeEvent;
0055: import org.eclipse.swt.events.DisposeListener;
0056: import org.eclipse.swt.events.FocusEvent;
0057: import org.eclipse.swt.events.FocusListener;
0058: import org.eclipse.swt.events.ModifyEvent;
0059: import org.eclipse.swt.events.ModifyListener;
0060: import org.eclipse.swt.events.MouseAdapter;
0061: import org.eclipse.swt.events.MouseEvent;
0062: import org.eclipse.swt.events.SelectionAdapter;
0063: import org.eclipse.swt.events.SelectionEvent;
0064: import org.eclipse.swt.events.SelectionListener;
0065: import org.eclipse.swt.graphics.Image;
0066: import org.eclipse.swt.graphics.Point;
0067: import org.eclipse.swt.layout.GridData;
0068: import org.eclipse.swt.layout.GridLayout;
0069: import org.eclipse.swt.widgets.Button;
0070: import org.eclipse.swt.widgets.Combo;
0071: import org.eclipse.swt.widgets.Composite;
0072: import org.eclipse.swt.widgets.Control;
0073: import org.eclipse.swt.widgets.FileDialog;
0074: import org.eclipse.swt.widgets.Group;
0075: import org.eclipse.swt.widgets.Label;
0076: import org.eclipse.swt.widgets.Menu;
0077: import org.eclipse.swt.widgets.MenuItem;
0078: import org.eclipse.swt.widgets.TabFolder;
0079: import org.eclipse.swt.widgets.TabItem;
0080: import org.eclipse.swt.widgets.Table;
0081: import org.eclipse.swt.widgets.TableColumn;
0082: import org.eclipse.swt.widgets.TableItem;
0083: import org.eclipse.swt.widgets.Text;
0084: import org.eclipse.ui.IWorkbench;
0085: import org.eclipse.ui.IWorkbenchPreferencePage;
0086: import org.eclipse.ui.PlatformUI;
0087: import org.eclipse.ui.activities.IActivityManager;
0088: import org.eclipse.ui.commands.ICommandService;
0089: import org.eclipse.ui.contexts.IContextService;
0090: import org.eclipse.ui.internal.IPreferenceConstants;
0091: import org.eclipse.ui.internal.IWorkbenchHelpContextIds;
0092: import org.eclipse.ui.internal.WorkbenchPlugin;
0093: import org.eclipse.ui.internal.misc.StatusUtil;
0094: import org.eclipse.ui.internal.util.PrefUtil;
0095: import org.eclipse.ui.internal.util.Util;
0096: import org.eclipse.ui.keys.IBindingService;
0097: import org.eclipse.ui.statushandlers.StatusManager;
0098:
0099: import com.ibm.icu.text.Collator;
0100: import com.ibm.icu.text.MessageFormat;
0101:
0102: /**
0103: * The preference page for defining keyboard shortcuts. While some of its
0104: * underpinning have been made generic to "bindings" rather than "key bindings",
0105: * it will still take some work to remove the link entirely.
0106: *
0107: * @since 3.0
0108: */
0109: public final class KeysPreferencePage extends PreferencePage implements
0110: IWorkbenchPreferencePage {
0111:
0112: /**
0113: * A selection listener to be used on the columns in the table on the view
0114: * tab. This selection listener modifies the sort order so that the
0115: * appropriate column is in the first position.
0116: *
0117: * @since 3.1
0118: */
0119: private class SortOrderSelectionListener extends SelectionAdapter {
0120:
0121: /**
0122: * The column to be put in the first position. This value should be one
0123: * of the constants defined by <code>SORT_COLUMN_</code>.
0124: */
0125: private final int columnSelected;
0126:
0127: /**
0128: * Constructs a new instance of <code>SortOrderSelectionListener</code>.
0129: *
0130: * @param columnSelected
0131: * The column to be given first priority in the sort order;
0132: * this value should be one of the constants defined as
0133: * <code>SORT_COLUMN_</code>.
0134: */
0135: private SortOrderSelectionListener(final int columnSelected) {
0136: this .columnSelected = columnSelected;
0137: }
0138:
0139: /*
0140: * (non-Javadoc)
0141: *
0142: * @see org.eclipse.swt.events.SelectionListener#widgetSelected(org.eclipse.swt.events.SelectionEvent)
0143: */
0144: public void widgetSelected(SelectionEvent e) {
0145: // Change the column titles.
0146: final int oldSortIndex = sortOrder[0];
0147: final TableColumn oldSortColumn = tableBindings
0148: .getColumn(oldSortIndex);
0149: oldSortColumn.setText(UNSORTED_COLUMN_NAMES[oldSortIndex]);
0150: final TableColumn newSortColumn = tableBindings
0151: .getColumn(columnSelected);
0152: newSortColumn.setText(SORTED_COLUMN_NAMES[columnSelected]);
0153:
0154: // Change the sort order.
0155: boolean columnPlaced = false;
0156: boolean enoughRoom = false;
0157: int bumpedColumn = -1;
0158: for (int i = 0; i < sortOrder.length; i++) {
0159: if (sortOrder[i] == columnSelected) {
0160: /*
0161: * We've found the place where the column existing in the
0162: * old sort order. No matter what at this point, we have
0163: * completed the reshuffling.
0164: */
0165: enoughRoom = true;
0166: if (bumpedColumn != -1) {
0167: // We have already started bumping things around, so
0168: // drop the last bumped column here.
0169: sortOrder[i] = bumpedColumn;
0170: } else {
0171: // The order has not changed.
0172: columnPlaced = true;
0173: }
0174: break;
0175:
0176: } else if (columnPlaced) {
0177: // We are currently bumping, so just bump another.
0178: int temp = sortOrder[i];
0179: sortOrder[i] = bumpedColumn;
0180: bumpedColumn = temp;
0181:
0182: } else {
0183: /*
0184: * We are not currently bumping, so drop the column and
0185: * start bumping.
0186: */
0187: bumpedColumn = sortOrder[i];
0188: sortOrder[i] = columnSelected;
0189: columnPlaced = true;
0190: }
0191: }
0192:
0193: // Grow the sort order.
0194: if (!enoughRoom) {
0195: final int[] newSortOrder = new int[sortOrder.length + 1];
0196: System.arraycopy(sortOrder, 0, newSortOrder, 0,
0197: sortOrder.length);
0198: newSortOrder[sortOrder.length] = bumpedColumn;
0199: sortOrder = newSortOrder;
0200: }
0201:
0202: // Update the view tab.
0203: updateViewTab();
0204: }
0205: }
0206:
0207: /**
0208: * The data key for the binding stored on an SWT widget. The key is a
0209: * fully-qualified name, but in reverse order. This is so that the equals
0210: * method will detect misses faster.
0211: */
0212: private static final String BINDING_KEY = "Binding.bindings.jface.eclipse.org"; //$NON-NLS-1$
0213:
0214: /**
0215: * The image associate with a binding that exists as part of the system
0216: * definition.
0217: */
0218: private static final Image IMAGE_BLANK = ImageFactory
0219: .getImage("blank"); //$NON-NLS-1$
0220:
0221: /**
0222: * The image associated with a binding changed by the user.
0223: */
0224: private static final Image IMAGE_CHANGE = ImageFactory
0225: .getImage("change"); //$NON-NLS-1$
0226:
0227: /**
0228: * The data key at which the <code>Binding</code> instance for a table
0229: * item is stored.
0230: */
0231: private static final String ITEM_DATA_KEY = "org.eclipse.jface.bindings"; //$NON-NLS-1$
0232:
0233: /**
0234: * The number of items to show in the combo boxes.
0235: */
0236: private static final int ITEMS_TO_SHOW = 9;
0237:
0238: /**
0239: * The resource bundle from which translations can be retrieved.
0240: */
0241: private static final ResourceBundle RESOURCE_BUNDLE = ResourceBundle
0242: .getBundle(KeysPreferencePage.class.getName());
0243:
0244: /**
0245: * The total number of columns on the view tab.
0246: */
0247: private static final int VIEW_TOTAL_COLUMNS = 4;
0248:
0249: /**
0250: * The translated names for the columns when they are the primary sort key
0251: * (e.g., ">Category<").
0252: */
0253: private static final String[] SORTED_COLUMN_NAMES = new String[VIEW_TOTAL_COLUMNS];
0254:
0255: /**
0256: * The index of the modify tab.
0257: *
0258: * @since 3.1
0259: */
0260: private static final int TAB_INDEX_MODIFY = 1;
0261:
0262: /**
0263: * The translated names for the columns when they are not the primary sort
0264: * key (e.g., "Category").
0265: */
0266: private static final String[] UNSORTED_COLUMN_NAMES = new String[VIEW_TOTAL_COLUMNS];
0267:
0268: /**
0269: * The index of the column on the view tab containing the category name.
0270: */
0271: private static final int VIEW_CATEGORY_COLUMN_INDEX = 0;
0272:
0273: /**
0274: * The index of the column on the view tab containing the command name.
0275: */
0276: private static final int VIEW_COMMAND_COLUMN_INDEX = 1;
0277:
0278: /**
0279: * The index of the column on the view tab containing the context name.
0280: */
0281: private static final int VIEW_CONTEXT_COLUMN_INDEX = 3;
0282:
0283: /**
0284: * The index of the column on the view tab containing the key sequence.
0285: */
0286: private static final int VIEW_KEY_SEQUENCE_COLUMN_INDEX = 2;
0287:
0288: static {
0289: UNSORTED_COLUMN_NAMES[VIEW_CATEGORY_COLUMN_INDEX] = Util
0290: .translateString(RESOURCE_BUNDLE, "tableColumnCategory"); //$NON-NLS-1$
0291: UNSORTED_COLUMN_NAMES[VIEW_COMMAND_COLUMN_INDEX] = Util
0292: .translateString(RESOURCE_BUNDLE, "tableColumnCommand"); //$NON-NLS-1$
0293: UNSORTED_COLUMN_NAMES[VIEW_KEY_SEQUENCE_COLUMN_INDEX] = Util
0294: .translateString(RESOURCE_BUNDLE,
0295: "tableColumnKeySequence"); //$NON-NLS-1$
0296: UNSORTED_COLUMN_NAMES[VIEW_CONTEXT_COLUMN_INDEX] = Util
0297: .translateString(RESOURCE_BUNDLE, "tableColumnContext"); //$NON-NLS-1$
0298:
0299: SORTED_COLUMN_NAMES[VIEW_CATEGORY_COLUMN_INDEX] = Util
0300: .translateString(RESOURCE_BUNDLE,
0301: "tableColumnCategorySorted"); //$NON-NLS-1$
0302: SORTED_COLUMN_NAMES[VIEW_COMMAND_COLUMN_INDEX] = Util
0303: .translateString(RESOURCE_BUNDLE,
0304: "tableColumnCommandSorted"); //$NON-NLS-1$
0305: SORTED_COLUMN_NAMES[VIEW_KEY_SEQUENCE_COLUMN_INDEX] = Util
0306: .translateString(RESOURCE_BUNDLE,
0307: "tableColumnKeySequenceSorted"); //$NON-NLS-1$
0308: SORTED_COLUMN_NAMES[VIEW_CONTEXT_COLUMN_INDEX] = Util
0309: .translateString(RESOURCE_BUNDLE,
0310: "tableColumnContextSorted"); //$NON-NLS-1$
0311: }
0312:
0313: /**
0314: * The workbench's activity manager. This activity manager is used to see if
0315: * certain commands should be filtered from the user interface.
0316: */
0317: private IActivityManager activityManager;
0318:
0319: /**
0320: * The workbench's binding service. This binding service is used to access
0321: * the current set of bindings, and to persist changes.
0322: */
0323: private IBindingService bindingService;
0324:
0325: /**
0326: * The add button located on the bottom left of the preference page. This
0327: * button adds the current trigger sequence to the currently selected
0328: * command.
0329: */
0330: private Button buttonAdd;
0331:
0332: /**
0333: * The remove button located on the bottom left of the preference page. This
0334: * button removes the current trigger sequence from the current command.
0335: */
0336: private Button buttonRemove;
0337:
0338: /**
0339: * The restore button located on the bottom left of the preference page.
0340: * This button attempts to restore the currently trigger sequence to its
0341: * initial (i.e., Binding.SYSTEM) state -- undoing all user modifications.
0342: */
0343: private Button buttonRestore;
0344:
0345: /**
0346: * A map of all the category identifiers indexed by the names that appear in
0347: * the user interface. This look-up table is built during initialization.
0348: */
0349: private Map categoryIdsByUniqueName;
0350:
0351: /**
0352: * A map of all the category names in the user interface indexed by their
0353: * identifiers. This look-up table is built during initialization.
0354: */
0355: private Map categoryUniqueNamesById;
0356:
0357: /**
0358: * The combo box containing the list of all categories for commands.
0359: */
0360: private Combo comboCategory;
0361:
0362: /**
0363: * The combo box containing the list of commands relevent for the currently
0364: * selected category.
0365: */
0366: private Combo comboCommand;
0367:
0368: /**
0369: * The combo box containing the list of contexts in the system.
0370: */
0371: private Combo comboContext;
0372:
0373: /**
0374: * The combo box containing the list of schemes in the system.
0375: */
0376: private Combo comboScheme;
0377:
0378: /**
0379: * A map of all the command identifiers indexed by the categories to which
0380: * they belong. This look-up table is built during initialization.
0381: */
0382: private Map commandIdsByCategoryId;
0383:
0384: /**
0385: * The parameterized commands corresponding to the current contents of
0386: * <code>comboCommand</code>. The commands in this array are in the same
0387: * order as in the combo. This value can be <code>null</code> if nothing
0388: * is selected in the combo.
0389: */
0390: private ParameterizedCommand[] commands = null;
0391:
0392: /**
0393: * The workbench's command service. This command service is used to access
0394: * the list of commands.
0395: */
0396: private ICommandService commandService;
0397:
0398: /**
0399: * A map of all the context identifiers indexed by the names that appear in
0400: * the user interface. This look-up table is built during initialization.
0401: */
0402: private Map contextIdsByUniqueName;
0403:
0404: /**
0405: * The workbench's context service. This context service is used to access
0406: * the list of contexts.
0407: */
0408: private IContextService contextService;
0409:
0410: /**
0411: * A map of all the category names in the user interface indexed by their
0412: * identifiers. This look-up table is built during initialization.
0413: */
0414: private Map contextUniqueNamesById;
0415:
0416: /**
0417: * The workbench's help system. This is used to register the page with the
0418: * help system.
0419: *
0420: * TODO Add a help context
0421: */
0422: // private IWorkbenchHelpSystem helpSystem;
0423: /**
0424: * This is the label next to the table showing the bindings matching a
0425: * particular command. The label is disabled if there isn't a selected
0426: * command identifier.
0427: */
0428: private Label labelBindingsForCommand;
0429:
0430: /**
0431: * This is the label next to the table showing the bindings matching a
0432: * particular trigger sequence. The label is disabled if there isn't a
0433: * current key sequence.
0434: */
0435: private Label labelBindingsForTriggerSequence;
0436:
0437: /**
0438: * The label next to the context combo box. This label indicates whether the
0439: * context is a child of another context. If the current context is not a
0440: * child, then this label is blank.
0441: */
0442: private Label labelContextExtends;
0443:
0444: /**
0445: * The label next to the scheme combo box. This label indicates whether the
0446: * scheme is a child of another scheme. If the current scheme is not a
0447: * child, then this label is blank.
0448: */
0449: private Label labelSchemeExtends;
0450:
0451: /**
0452: * A binding manager local to this preference page. When the page is
0453: * initialized, the current bindings are read out from the binding service
0454: * and placed in this manager. This manager is then updated as the user
0455: * makes changes. When the user has finished, the contents of this manager
0456: * are compared with the contents of the binding service. The changes are
0457: * then persisted.
0458: */
0459: private final BindingManager localChangeManager = new BindingManager(
0460: new ContextManager(), new CommandManager());
0461:
0462: /**
0463: * A map of all the scheme identifiers indexed by the names that appear in
0464: * the user interface. This look-up table is built during initialization.
0465: */
0466: private Map schemeIdsByUniqueName;
0467:
0468: /**
0469: * A map of all the scheme names in the user interface indexed by their
0470: * identifiers. This look-up table is built during initialization.
0471: */
0472: private Map schemeUniqueNamesById;
0473:
0474: /**
0475: * The sort order to be used on the view tab to display all of the key
0476: * bindings. This sort order can be changed by the user. This array is never
0477: * <code>null</code>, but may be empty.
0478: */
0479: private int[] sortOrder = { VIEW_CATEGORY_COLUMN_INDEX,
0480: VIEW_COMMAND_COLUMN_INDEX, VIEW_KEY_SEQUENCE_COLUMN_INDEX,
0481: VIEW_CONTEXT_COLUMN_INDEX };
0482:
0483: /**
0484: * The top-most tab folder for the preference page -- containing a view and
0485: * a modify tab.
0486: */
0487: private TabFolder tabFolder;
0488:
0489: /**
0490: * A table of the key bindings currently defined. This table appears on the
0491: * view tab; it is intended to be an easy way for users to learn the key
0492: * bindings in Eclipse. This value is only <code>null</code> until the
0493: * controls are first created.
0494: */
0495: private Table tableBindings;
0496:
0497: /**
0498: * The table containing all of the bindings matching the selected command.
0499: */
0500: private Table tableBindingsForCommand;
0501:
0502: /**
0503: * The table containing all of the bindings matching the current trigger
0504: * sequence.
0505: */
0506: private Table tableBindingsForTriggerSequence;
0507:
0508: /**
0509: * The text widget where keys are entered. This widget is managed by
0510: * <code>textTriggerSequenceManager</code>, which provides its special
0511: * behaviour.
0512: */
0513: private Text textTriggerSequence;
0514:
0515: /**
0516: * The manager for the text widget that traps incoming key events. This
0517: * manager should be used to access the widget, rather than accessing the
0518: * widget directly.
0519: */
0520: private KeySequenceText textTriggerSequenceManager;
0521:
0522: /* (non-Javadoc)
0523: * @see org.eclipse.jface.preference.PreferencePage#applyData(java.lang.Object)
0524: */
0525: public void applyData(Object data) {
0526: if (data instanceof Binding) {
0527: editBinding((Binding) data);
0528: }
0529: }
0530:
0531: protected final Control createContents(final Composite parent) {
0532:
0533: PlatformUI.getWorkbench().getHelpSystem().setHelp(parent,
0534: IWorkbenchHelpContextIds.KEYS_PREFERENCE_PAGE);
0535:
0536: tabFolder = new TabFolder(parent, SWT.NULL);
0537:
0538: // View tab
0539: final TabItem viewTab = new TabItem(tabFolder, SWT.NULL);
0540: viewTab.setText(Util.translateString(RESOURCE_BUNDLE,
0541: "viewTab.Text")); //$NON-NLS-1$
0542: viewTab.setControl(createViewTab(tabFolder));
0543:
0544: // Modify tab
0545: final TabItem modifyTab = new TabItem(tabFolder, SWT.NULL);
0546: modifyTab.setText(Util.translateString(RESOURCE_BUNDLE,
0547: "modifyTab.Text")); //$NON-NLS-1$
0548: modifyTab.setControl(createModifyTab(tabFolder));
0549:
0550: // Do some fancy stuff.
0551: applyDialogFont(tabFolder);
0552: final IPreferenceStore store = getPreferenceStore();
0553: final int selectedTab = store
0554: .getInt(IPreferenceConstants.KEYS_PREFERENCE_SELECTED_TAB);
0555: if ((tabFolder.getItemCount() > selectedTab)
0556: && (selectedTab > 0)) {
0557: tabFolder.setSelection(selectedTab);
0558: }
0559:
0560: return tabFolder;
0561: }
0562:
0563: /**
0564: * Creates the tab that allows the user to change the keyboard shortcuts.
0565: *
0566: * @param parent
0567: * The tab folder in which the tab should be created; must not be
0568: * <code>null</code>.
0569: * @return The composite which represents the contents of the tab; never
0570: * <code>null</code>.
0571: */
0572: private final Composite createModifyTab(final TabFolder parent) {
0573: final Composite composite = new Composite(parent, SWT.NULL);
0574: composite.setLayout(new GridLayout());
0575: GridData gridData = new GridData(GridData.FILL_BOTH);
0576: composite.setLayoutData(gridData);
0577: final Composite compositeKeyConfiguration = new Composite(
0578: composite, SWT.NULL);
0579: GridLayout gridLayout = new GridLayout();
0580: gridLayout.numColumns = 3;
0581: compositeKeyConfiguration.setLayout(gridLayout);
0582: gridData = new GridData(GridData.FILL_HORIZONTAL);
0583: compositeKeyConfiguration.setLayoutData(gridData);
0584: final Label labelKeyConfiguration = new Label(
0585: compositeKeyConfiguration, SWT.LEFT);
0586: labelKeyConfiguration.setText(Util.translateString(
0587: RESOURCE_BUNDLE, "labelScheme")); //$NON-NLS-1$
0588: comboScheme = new Combo(compositeKeyConfiguration,
0589: SWT.READ_ONLY);
0590: gridData = new GridData();
0591: gridData.widthHint = 200;
0592: comboScheme.setLayoutData(gridData);
0593: comboScheme.setVisibleItemCount(ITEMS_TO_SHOW);
0594:
0595: comboScheme.addSelectionListener(new SelectionAdapter() {
0596: public final void widgetSelected(final SelectionEvent e) {
0597: selectedComboScheme();
0598: }
0599: });
0600:
0601: labelSchemeExtends = new Label(compositeKeyConfiguration,
0602: SWT.LEFT);
0603: gridData = new GridData(GridData.FILL_HORIZONTAL);
0604: labelSchemeExtends.setLayoutData(gridData);
0605: final Control spacer = new Composite(composite, SWT.NULL);
0606: gridData = new GridData();
0607: gridData.heightHint = 10;
0608: gridData.widthHint = 10;
0609: spacer.setLayoutData(gridData);
0610: final Group groupCommand = new Group(composite, SWT.SHADOW_NONE);
0611: gridLayout = new GridLayout();
0612: gridLayout.numColumns = 3;
0613: groupCommand.setLayout(gridLayout);
0614: gridData = new GridData(GridData.FILL_BOTH);
0615: groupCommand.setLayoutData(gridData);
0616: groupCommand.setText(Util.translateString(RESOURCE_BUNDLE,
0617: "groupCommand")); //$NON-NLS-1$
0618: final Label labelCategory = new Label(groupCommand, SWT.LEFT);
0619: gridData = new GridData();
0620: labelCategory.setLayoutData(gridData);
0621: labelCategory.setText(Util.translateString(RESOURCE_BUNDLE,
0622: "labelCategory")); //$NON-NLS-1$
0623: comboCategory = new Combo(groupCommand, SWT.READ_ONLY);
0624: gridData = new GridData();
0625: gridData.horizontalSpan = 2;
0626: gridData.widthHint = 200;
0627: comboCategory.setLayoutData(gridData);
0628: comboCategory.setVisibleItemCount(ITEMS_TO_SHOW);
0629:
0630: comboCategory.addSelectionListener(new SelectionAdapter() {
0631: public final void widgetSelected(final SelectionEvent e) {
0632: update();
0633: }
0634: });
0635:
0636: final Label labelCommand = new Label(groupCommand, SWT.LEFT);
0637: gridData = new GridData();
0638: labelCommand.setLayoutData(gridData);
0639: labelCommand.setText(Util.translateString(RESOURCE_BUNDLE,
0640: "labelCommand")); //$NON-NLS-1$
0641: comboCommand = new Combo(groupCommand, SWT.READ_ONLY);
0642: gridData = new GridData();
0643: gridData.horizontalSpan = 2;
0644: gridData.widthHint = 300;
0645: comboCommand.setLayoutData(gridData);
0646: comboCommand.setVisibleItemCount(9);
0647:
0648: comboCommand.addSelectionListener(new SelectionAdapter() {
0649: public final void widgetSelected(final SelectionEvent e) {
0650: update();
0651: }
0652: });
0653:
0654: labelBindingsForCommand = new Label(groupCommand, SWT.LEFT);
0655: gridData = new GridData(GridData.VERTICAL_ALIGN_BEGINNING);
0656: gridData.verticalAlignment = GridData.FILL_VERTICAL;
0657: labelBindingsForCommand.setLayoutData(gridData);
0658: labelBindingsForCommand.setText(Util.translateString(
0659: RESOURCE_BUNDLE, "labelAssignmentsForCommand")); //$NON-NLS-1$
0660: tableBindingsForCommand = new Table(groupCommand, SWT.BORDER
0661: | SWT.FULL_SELECTION | SWT.H_SCROLL | SWT.V_SCROLL);
0662: tableBindingsForCommand.setHeaderVisible(true);
0663: gridData = new GridData(GridData.FILL_BOTH);
0664: gridData.heightHint = 60;
0665: gridData.horizontalSpan = 2;
0666: gridData.widthHint = "carbon".equals(SWT.getPlatform()) ? 620 : 520; //$NON-NLS-1$
0667: tableBindingsForCommand.setLayoutData(gridData);
0668: TableColumn tableColumnDelta = new TableColumn(
0669: tableBindingsForCommand, SWT.NULL, 0);
0670: tableColumnDelta.setResizable(false);
0671: tableColumnDelta.setText(Util.ZERO_LENGTH_STRING);
0672: tableColumnDelta.setWidth(20);
0673: TableColumn tableColumnContext = new TableColumn(
0674: tableBindingsForCommand, SWT.NULL, 1);
0675: tableColumnContext.setResizable(true);
0676: tableColumnContext.setText(Util.translateString(
0677: RESOURCE_BUNDLE, "tableColumnContext")); //$NON-NLS-1$
0678: tableColumnContext.pack();
0679: tableColumnContext.setWidth(200);
0680: final TableColumn tableColumnKeySequence = new TableColumn(
0681: tableBindingsForCommand, SWT.NULL, 2);
0682: tableColumnKeySequence.setResizable(true);
0683: tableColumnKeySequence.setText(Util.translateString(
0684: RESOURCE_BUNDLE, "tableColumnKeySequence")); //$NON-NLS-1$
0685: tableColumnKeySequence.pack();
0686: tableColumnKeySequence.setWidth(300);
0687:
0688: tableBindingsForCommand.addMouseListener(new MouseAdapter() {
0689:
0690: public void mouseDoubleClick(MouseEvent mouseEvent) {
0691: update();
0692: }
0693: });
0694:
0695: tableBindingsForCommand
0696: .addSelectionListener(new SelectionAdapter() {
0697:
0698: public void widgetSelected(
0699: SelectionEvent selectionEvent) {
0700: selectedTableBindingsForCommand();
0701: }
0702: });
0703:
0704: final Group groupKeySequence = new Group(composite,
0705: SWT.SHADOW_NONE);
0706: gridLayout = new GridLayout();
0707: gridLayout.numColumns = 4;
0708: groupKeySequence.setLayout(gridLayout);
0709: gridData = new GridData(GridData.FILL_BOTH);
0710: groupKeySequence.setLayoutData(gridData);
0711: groupKeySequence.setText(Util.translateString(RESOURCE_BUNDLE,
0712: "groupKeySequence")); //$NON-NLS-1$
0713: final Label labelKeySequence = new Label(groupKeySequence,
0714: SWT.LEFT);
0715: gridData = new GridData();
0716: labelKeySequence.setLayoutData(gridData);
0717: labelKeySequence.setText(Util.translateString(RESOURCE_BUNDLE,
0718: "labelKeySequence")); //$NON-NLS-1$
0719:
0720: // The text widget into which the key strokes will be entered.
0721: textTriggerSequence = new Text(groupKeySequence, SWT.BORDER);
0722: // On MacOS X, this font will be changed by KeySequenceText
0723: textTriggerSequence.setFont(groupKeySequence.getFont());
0724: gridData = new GridData();
0725: gridData.horizontalSpan = 2;
0726: gridData.widthHint = 300;
0727: textTriggerSequence.setLayoutData(gridData);
0728: textTriggerSequence.addModifyListener(new ModifyListener() {
0729: public void modifyText(ModifyEvent e) {
0730: update();
0731: }
0732: });
0733: textTriggerSequence.addFocusListener(new FocusListener() {
0734: public void focusGained(FocusEvent e) {
0735: bindingService.setKeyFilterEnabled(false);
0736: }
0737:
0738: public void focusLost(FocusEvent e) {
0739: bindingService.setKeyFilterEnabled(true);
0740: }
0741: });
0742: textTriggerSequence.addDisposeListener(new DisposeListener() {
0743: public void widgetDisposed(DisposeEvent e) {
0744: if (!bindingService.isKeyFilterEnabled()) {
0745: bindingService.setKeyFilterEnabled(true);
0746: }
0747: }
0748: });
0749:
0750: // The manager for the key sequence text widget.
0751: textTriggerSequenceManager = new KeySequenceText(
0752: textTriggerSequence);
0753: textTriggerSequenceManager.setKeyStrokeLimit(4);
0754:
0755: // Button for adding trapped key strokes
0756: final Button buttonAddKey = new Button(groupKeySequence,
0757: SWT.LEFT | SWT.ARROW);
0758: buttonAddKey.setToolTipText(Util.translateString(
0759: RESOURCE_BUNDLE, "buttonAddKey.ToolTipText")); //$NON-NLS-1$
0760: gridData = new GridData();
0761: gridData.heightHint = comboCategory.getTextHeight();
0762: buttonAddKey.setLayoutData(gridData);
0763:
0764: // Arrow buttons aren't normally added to the tab list. Let's fix that.
0765: final Control[] tabStops = groupKeySequence.getTabList();
0766: final ArrayList newTabStops = new ArrayList();
0767: for (int i = 0; i < tabStops.length; i++) {
0768: Control tabStop = tabStops[i];
0769: newTabStops.add(tabStop);
0770: if (textTriggerSequence.equals(tabStop)) {
0771: newTabStops.add(buttonAddKey);
0772: }
0773: }
0774: final Control[] newTabStopArray = (Control[]) newTabStops
0775: .toArray(new Control[newTabStops.size()]);
0776: groupKeySequence.setTabList(newTabStopArray);
0777:
0778: // Construct the menu to attach to the above button.
0779: final Menu menuButtonAddKey = new Menu(buttonAddKey);
0780: final Iterator trappedKeyItr = KeySequenceText.TRAPPED_KEYS
0781: .iterator();
0782: while (trappedKeyItr.hasNext()) {
0783: final KeyStroke trappedKey = (KeyStroke) trappedKeyItr
0784: .next();
0785: final MenuItem menuItem = new MenuItem(menuButtonAddKey,
0786: SWT.PUSH);
0787: menuItem.setText(trappedKey.format());
0788: menuItem.addSelectionListener(new SelectionAdapter() {
0789:
0790: public void widgetSelected(SelectionEvent e) {
0791: textTriggerSequenceManager.insert(trappedKey);
0792: textTriggerSequence.setFocus();
0793: textTriggerSequence
0794: .setSelection(textTriggerSequence
0795: .getTextLimit());
0796: }
0797: });
0798: }
0799: buttonAddKey.addSelectionListener(new SelectionAdapter() {
0800:
0801: public void widgetSelected(SelectionEvent selectionEvent) {
0802: Point buttonLocation = buttonAddKey.getLocation();
0803: buttonLocation = groupKeySequence.toDisplay(
0804: buttonLocation.x, buttonLocation.y);
0805: Point buttonSize = buttonAddKey.getSize();
0806: menuButtonAddKey.setLocation(buttonLocation.x,
0807: buttonLocation.y + buttonSize.y);
0808: menuButtonAddKey.setVisible(true);
0809: }
0810: });
0811:
0812: labelBindingsForTriggerSequence = new Label(groupKeySequence,
0813: SWT.LEFT);
0814: gridData = new GridData(GridData.VERTICAL_ALIGN_BEGINNING);
0815: gridData.verticalAlignment = GridData.FILL_VERTICAL;
0816: labelBindingsForTriggerSequence.setLayoutData(gridData);
0817: labelBindingsForTriggerSequence.setText(Util.translateString(
0818: RESOURCE_BUNDLE, "labelAssignmentsForKeySequence")); //$NON-NLS-1$
0819: tableBindingsForTriggerSequence = new Table(groupKeySequence,
0820: SWT.BORDER | SWT.FULL_SELECTION | SWT.H_SCROLL
0821: | SWT.V_SCROLL);
0822: tableBindingsForTriggerSequence.setHeaderVisible(true);
0823: gridData = new GridData(GridData.FILL_BOTH);
0824: gridData.heightHint = 60;
0825: gridData.horizontalSpan = 3;
0826: gridData.widthHint = "carbon".equals(SWT.getPlatform()) ? 620 : 520; //$NON-NLS-1$
0827: tableBindingsForTriggerSequence.setLayoutData(gridData);
0828: tableColumnDelta = new TableColumn(
0829: tableBindingsForTriggerSequence, SWT.NULL, 0);
0830: tableColumnDelta.setResizable(false);
0831: tableColumnDelta.setText(Util.ZERO_LENGTH_STRING);
0832: tableColumnDelta.setWidth(20);
0833: tableColumnContext = new TableColumn(
0834: tableBindingsForTriggerSequence, SWT.NULL, 1);
0835: tableColumnContext.setResizable(true);
0836: tableColumnContext.setText(Util.translateString(
0837: RESOURCE_BUNDLE, "tableColumnContext")); //$NON-NLS-1$
0838: tableColumnContext.pack();
0839: tableColumnContext.setWidth(200);
0840: final TableColumn tableColumnCommand = new TableColumn(
0841: tableBindingsForTriggerSequence, SWT.NULL, 2);
0842: tableColumnCommand.setResizable(true);
0843: tableColumnCommand.setText(Util.translateString(
0844: RESOURCE_BUNDLE, "tableColumnCommand")); //$NON-NLS-1$
0845: tableColumnCommand.pack();
0846: tableColumnCommand.setWidth(300);
0847:
0848: tableBindingsForTriggerSequence
0849: .addMouseListener(new MouseAdapter() {
0850:
0851: public void mouseDoubleClick(MouseEvent mouseEvent) {
0852: update();
0853: }
0854: });
0855:
0856: tableBindingsForTriggerSequence
0857: .addSelectionListener(new SelectionAdapter() {
0858:
0859: public void widgetSelected(
0860: SelectionEvent selectionEvent) {
0861: selectedTableBindingsForTriggerSequence();
0862: }
0863: });
0864:
0865: final Composite compositeContext = new Composite(composite,
0866: SWT.NULL);
0867: gridLayout = new GridLayout();
0868: gridLayout.numColumns = 3;
0869: compositeContext.setLayout(gridLayout);
0870: gridData = new GridData(GridData.FILL_HORIZONTAL);
0871: compositeContext.setLayoutData(gridData);
0872: final Label labelContext = new Label(compositeContext, SWT.LEFT);
0873: labelContext.setText(Util.translateString(RESOURCE_BUNDLE,
0874: "labelContext")); //$NON-NLS-1$
0875: comboContext = new Combo(compositeContext, SWT.READ_ONLY);
0876: gridData = new GridData();
0877: gridData.widthHint = 250;
0878: comboContext.setLayoutData(gridData);
0879: comboContext.setVisibleItemCount(ITEMS_TO_SHOW);
0880:
0881: comboContext.addSelectionListener(new SelectionAdapter() {
0882: public final void widgetSelected(final SelectionEvent e) {
0883: update();
0884: }
0885: });
0886:
0887: labelContextExtends = new Label(compositeContext, SWT.LEFT);
0888: gridData = new GridData(GridData.FILL_HORIZONTAL);
0889: labelContextExtends.setLayoutData(gridData);
0890: final Composite compositeButton = new Composite(composite,
0891: SWT.NULL);
0892: gridLayout = new GridLayout();
0893: gridLayout.marginHeight = 20;
0894: gridLayout.marginWidth = 0;
0895: gridLayout.numColumns = 3;
0896: compositeButton.setLayout(gridLayout);
0897: gridData = new GridData();
0898: compositeButton.setLayoutData(gridData);
0899: buttonAdd = new Button(compositeButton, SWT.CENTER | SWT.PUSH);
0900: gridData = new GridData();
0901: int widthHint = convertHorizontalDLUsToPixels(IDialogConstants.BUTTON_WIDTH);
0902: buttonAdd.setText(Util.translateString(RESOURCE_BUNDLE,
0903: "buttonAdd")); //$NON-NLS-1$
0904: gridData.widthHint = Math.max(widthHint, buttonAdd.computeSize(
0905: SWT.DEFAULT, SWT.DEFAULT, true).x) + 5;
0906: buttonAdd.setLayoutData(gridData);
0907:
0908: buttonAdd.addSelectionListener(new SelectionAdapter() {
0909:
0910: public void widgetSelected(SelectionEvent selectionEvent) {
0911: selectedButtonAdd();
0912: }
0913: });
0914:
0915: buttonRemove = new Button(compositeButton, SWT.CENTER
0916: | SWT.PUSH);
0917: gridData = new GridData();
0918: widthHint = convertHorizontalDLUsToPixels(IDialogConstants.BUTTON_WIDTH);
0919: buttonRemove.setText(Util.translateString(RESOURCE_BUNDLE,
0920: "buttonRemove")); //$NON-NLS-1$
0921: gridData.widthHint = Math.max(widthHint, buttonRemove
0922: .computeSize(SWT.DEFAULT, SWT.DEFAULT, true).x) + 5;
0923: buttonRemove.setLayoutData(gridData);
0924:
0925: buttonRemove.addSelectionListener(new SelectionAdapter() {
0926:
0927: public void widgetSelected(SelectionEvent selectionEvent) {
0928: selectedButtonRemove();
0929: }
0930: });
0931:
0932: buttonRestore = new Button(compositeButton, SWT.CENTER
0933: | SWT.PUSH);
0934: gridData = new GridData();
0935: widthHint = convertHorizontalDLUsToPixels(IDialogConstants.BUTTON_WIDTH);
0936: buttonRestore.setText(Util.translateString(RESOURCE_BUNDLE,
0937: "buttonRestore")); //$NON-NLS-1$
0938: gridData.widthHint = Math.max(widthHint, buttonRestore
0939: .computeSize(SWT.DEFAULT, SWT.DEFAULT, true).x) + 5;
0940: buttonRestore.setLayoutData(gridData);
0941:
0942: buttonRestore.addSelectionListener(new SelectionAdapter() {
0943:
0944: public void widgetSelected(SelectionEvent selectionEvent) {
0945: selectedButtonRestore();
0946: }
0947: });
0948:
0949: return composite;
0950: }
0951:
0952: /**
0953: * Creates a tab on the main page for displaying an uneditable list of the
0954: * current key bindings. This is intended as a discovery tool for new users.
0955: * It shows all of the key bindings for the current key configuration,
0956: * platform and locale.
0957: *
0958: * @param parent
0959: * The tab folder in which the tab should be created; must not be
0960: * <code>null</code>.
0961: * @return The newly created composite containing all of the controls; never
0962: * <code>null</code>.
0963: * @since 3.1
0964: */
0965: private final Composite createViewTab(final TabFolder parent) {
0966: GridData gridData = null;
0967: int widthHint;
0968:
0969: // Create the composite for the tab.
0970: final Composite composite = new Composite(parent, SWT.NONE);
0971: composite.setLayoutData(new GridData(GridData.FILL_BOTH));
0972: composite.setLayout(new GridLayout());
0973:
0974: // Place a table inside the tab.
0975: tableBindings = new Table(composite, SWT.BORDER
0976: | SWT.FULL_SELECTION | SWT.H_SCROLL | SWT.V_SCROLL);
0977: tableBindings.setHeaderVisible(true);
0978: gridData = new GridData(GridData.FILL_BOTH);
0979: gridData.heightHint = 400;
0980: gridData.horizontalSpan = 2;
0981: tableBindings.setLayoutData(gridData);
0982: final TableColumn tableColumnCategory = new TableColumn(
0983: tableBindings, SWT.NONE, VIEW_CATEGORY_COLUMN_INDEX);
0984: tableColumnCategory
0985: .setText(SORTED_COLUMN_NAMES[VIEW_CATEGORY_COLUMN_INDEX]);
0986: tableColumnCategory
0987: .addSelectionListener(new SortOrderSelectionListener(
0988: VIEW_CATEGORY_COLUMN_INDEX));
0989: final TableColumn tableColumnCommand = new TableColumn(
0990: tableBindings, SWT.NONE, VIEW_COMMAND_COLUMN_INDEX);
0991: tableColumnCommand
0992: .setText(UNSORTED_COLUMN_NAMES[VIEW_COMMAND_COLUMN_INDEX]);
0993: tableColumnCommand
0994: .addSelectionListener(new SortOrderSelectionListener(
0995: VIEW_COMMAND_COLUMN_INDEX));
0996: final TableColumn tableColumnKeySequence = new TableColumn(
0997: tableBindings, SWT.NONE, VIEW_KEY_SEQUENCE_COLUMN_INDEX);
0998: tableColumnKeySequence
0999: .setText(UNSORTED_COLUMN_NAMES[VIEW_KEY_SEQUENCE_COLUMN_INDEX]);
1000: tableColumnKeySequence
1001: .addSelectionListener(new SortOrderSelectionListener(
1002: VIEW_KEY_SEQUENCE_COLUMN_INDEX));
1003: final TableColumn tableColumnContext = new TableColumn(
1004: tableBindings, SWT.NONE, VIEW_CONTEXT_COLUMN_INDEX);
1005: tableColumnContext
1006: .setText(UNSORTED_COLUMN_NAMES[VIEW_CONTEXT_COLUMN_INDEX]);
1007: tableColumnContext
1008: .addSelectionListener(new SortOrderSelectionListener(
1009: VIEW_CONTEXT_COLUMN_INDEX));
1010: tableBindings.addSelectionListener(new SelectionAdapter() {
1011: public final void widgetDefaultSelected(
1012: final SelectionEvent e) {
1013: selectedTableKeyBindings();
1014: }
1015: });
1016:
1017: // A composite for the buttons.
1018: final Composite buttonBar = new Composite(composite, SWT.NONE);
1019: buttonBar.setLayout(new GridLayout(2, false));
1020: gridData = new GridData();
1021: gridData.horizontalAlignment = GridData.END;
1022: buttonBar.setLayoutData(gridData);
1023:
1024: // A button for editing the current selection.
1025: final Button editButton = new Button(buttonBar, SWT.PUSH);
1026: gridData = new GridData();
1027: widthHint = convertHorizontalDLUsToPixels(IDialogConstants.BUTTON_WIDTH);
1028: editButton.setText(Util.translateString(RESOURCE_BUNDLE,
1029: "buttonEdit")); //$NON-NLS-1$
1030: gridData.widthHint = Math.max(widthHint, editButton
1031: .computeSize(SWT.DEFAULT, SWT.DEFAULT, true).x) + 5;
1032: editButton.setLayoutData(gridData);
1033: editButton.addSelectionListener(new SelectionListener() {
1034:
1035: /*
1036: * (non-Javadoc)
1037: *
1038: * @see org.eclipse.swt.events.SelectionListener#widgetDefaultSelected(org.eclipse.swt.events.SelectionEvent)
1039: */
1040: public final void widgetDefaultSelected(
1041: final SelectionEvent event) {
1042: selectedTableKeyBindings();
1043: }
1044:
1045: /*
1046: * (non-Javadoc)
1047: *
1048: * @see org.eclipse.swt.events.SelectionListener#widgetSelected(org.eclipse.swt.events.SelectionEvent)
1049: */
1050: public void widgetSelected(SelectionEvent e) {
1051: widgetDefaultSelected(e);
1052: }
1053: });
1054:
1055: // A button for exporting the contents to a file.
1056: final Button buttonExport = new Button(buttonBar, SWT.PUSH);
1057: gridData = new GridData();
1058: widthHint = convertHorizontalDLUsToPixels(IDialogConstants.BUTTON_WIDTH);
1059: buttonExport.setText(Util.translateString(RESOURCE_BUNDLE,
1060: "buttonExport")); //$NON-NLS-1$
1061: gridData.widthHint = Math.max(widthHint, buttonExport
1062: .computeSize(SWT.DEFAULT, SWT.DEFAULT, true).x) + 5;
1063: buttonExport.setLayoutData(gridData);
1064: buttonExport.addSelectionListener(new SelectionListener() {
1065:
1066: /*
1067: * (non-Javadoc)
1068: *
1069: * @see org.eclipse.swt.events.SelectionListener#widgetDefaultSelected(org.eclipse.swt.events.SelectionEvent)
1070: */
1071: public final void widgetDefaultSelected(
1072: final SelectionEvent event) {
1073: selectedButtonExport();
1074: }
1075:
1076: /*
1077: * (non-Javadoc)
1078: *
1079: * @see org.eclipse.swt.events.SelectionListener#widgetSelected(org.eclipse.swt.events.SelectionEvent)
1080: */
1081: public void widgetSelected(SelectionEvent e) {
1082: widgetDefaultSelected(e);
1083: }
1084: });
1085:
1086: return composite;
1087: }
1088:
1089: protected IPreferenceStore doGetPreferenceStore() {
1090: return PrefUtil.getInternalPreferenceStore();
1091: }
1092:
1093: /**
1094: * Allows the user to change the key bindings for a particular command.
1095: * Switches the tab to the modify tab, and then selects the category and
1096: * command that corresponds with the given command name. It then selects the
1097: * given key sequence and gives focus to the key sequence text widget.
1098: *
1099: * @param binding
1100: * The binding to be edited; if <code>null</code>, then just
1101: * switch to the modify tab. If the <code>binding</code> does
1102: * not correspond to anything in the keys preference page, then
1103: * this also just switches to the modify tab.
1104: * @since 3.1
1105: */
1106: public final void editBinding(final Binding binding) {
1107: // Switch to the modify tab.
1108: tabFolder.setSelection(TAB_INDEX_MODIFY);
1109:
1110: // If there is no command name, stop here.
1111: if (binding == null) {
1112: return;
1113: }
1114:
1115: /*
1116: * Get the corresponding category and command names. If either is
1117: * undefined, then we can just stop now. We won't be able to find their
1118: * name.
1119: */
1120: final ParameterizedCommand command = binding
1121: .getParameterizedCommand();
1122: String categoryName = null;
1123: String commandName = null;
1124: try {
1125: categoryName = command.getCommand().getCategory().getName();
1126: commandName = command.getName();
1127: } catch (final NotDefinedException e) {
1128: return; // no name
1129: }
1130:
1131: // Update the category combo box.
1132: final String[] categoryNames = comboCategory.getItems();
1133: int i = 0;
1134: for (; i < categoryNames.length; i++) {
1135: if (categoryName.equals(categoryNames[i])) {
1136: break;
1137: }
1138: }
1139: if (i >= comboCategory.getItemCount()) {
1140: // Couldn't find the category, so abort.
1141: return;
1142: }
1143: comboCategory.select(i);
1144:
1145: // Update the commands combo box.
1146: updateComboCommand();
1147:
1148: // Update the command combo box.
1149: final String[] commandNames = comboCommand.getItems();
1150: int j = 0;
1151: for (; j < commandNames.length; j++) {
1152: if (commandName.equals(commandNames[j])) {
1153: if (comboCommand.getSelectionIndex() != j) {
1154: comboCommand.select(j);
1155: }
1156: break;
1157: }
1158: }
1159: if (j >= comboCommand.getItemCount()) {
1160: // Couldn't find the command, so just select the first and then stop
1161: if (comboCommand.getSelectionIndex() != 0) {
1162: comboCommand.select(0);
1163: }
1164: update();
1165: return;
1166: }
1167:
1168: /*
1169: * Update and validate the state of the modify tab in response to these
1170: * selection changes.
1171: */
1172: update();
1173:
1174: // Select the right key binding, if possible.
1175: final TableItem[] items = tableBindingsForCommand.getItems();
1176: int k = 0;
1177: for (; k < items.length; k++) {
1178: final String currentKeySequence = items[k].getText(2);
1179: if (binding.getTriggerSequence().format().equals(
1180: currentKeySequence)) {
1181: break;
1182: }
1183: }
1184: if (k < tableBindingsForCommand.getItemCount()) {
1185: tableBindingsForCommand.select(k);
1186: tableBindingsForCommand
1187: .notifyListeners(SWT.Selection, null);
1188: textTriggerSequence.setFocus();
1189: }
1190: }
1191:
1192: /**
1193: * Returns the identifier for the currently selected category.
1194: *
1195: * @return The selected category; <code>null</code> if none.
1196: */
1197: private final String getCategoryId() {
1198: return !commandIdsByCategoryId.containsKey(null)
1199: || comboCategory.getSelectionIndex() > 0 ? (String) categoryIdsByUniqueName
1200: .get(comboCategory.getText())
1201: : null;
1202: }
1203:
1204: /**
1205: * Returns the identifier for the currently selected context.
1206: *
1207: * @return The selected context; <code>null</code> if none.
1208: */
1209: private final String getContextId() {
1210: return comboContext.getSelectionIndex() >= 0 ? (String) contextIdsByUniqueName
1211: .get(comboContext.getText())
1212: : null;
1213: }
1214:
1215: /**
1216: * Returns the current trigger sequence.
1217: *
1218: * @return The trigger sequence; may be empty, but never <code>null</code>.
1219: */
1220: private final KeySequence getKeySequence() {
1221: return textTriggerSequenceManager.getKeySequence();
1222: }
1223:
1224: /**
1225: * Returns the currently-selected fully-parameterized command.
1226: *
1227: * @return The selected fully-parameterized command; <code>null</code> if
1228: * none.
1229: */
1230: private final ParameterizedCommand getParameterizedCommand() {
1231: final int selectionIndex = comboCommand.getSelectionIndex();
1232: if ((selectionIndex >= 0) && (commands != null)
1233: && (selectionIndex < commands.length)) {
1234: return commands[selectionIndex];
1235: }
1236:
1237: return null;
1238: }
1239:
1240: /**
1241: * Returns the identifier for the currently selected scheme.
1242: *
1243: * @return The selected scheme; <code>null</code> if none.
1244: */
1245: private final String getSchemeId() {
1246: return comboScheme.getSelectionIndex() >= 0 ? (String) schemeIdsByUniqueName
1247: .get(comboScheme.getText())
1248: : null;
1249: }
1250:
1251: public final void init(final IWorkbench workbench) {
1252: activityManager = workbench.getActivitySupport()
1253: .getActivityManager();
1254: bindingService = (IBindingService) workbench
1255: .getService(IBindingService.class);
1256: commandService = (ICommandService) workbench
1257: .getService(ICommandService.class);
1258: contextService = (IContextService) workbench
1259: .getService(IContextService.class);
1260: }
1261:
1262: /**
1263: * Checks whether the activity manager knows anything about this command
1264: * identifier. If the activity manager is currently filtering this command,
1265: * then it does not appear in the user interface.
1266: *
1267: * @param command
1268: * The command which should be checked against the activities;
1269: * must not be <code>null</code>.
1270: * @return <code>true</code> if the command identifier is not filtered;
1271: * <code>false</code> if it is
1272: */
1273: private final boolean isActive(final Command command) {
1274: return activityManager.getIdentifier(command.getId())
1275: .isEnabled();
1276: }
1277:
1278: /**
1279: * Logs the given exception, and opens an error dialog saying that something
1280: * went wrong. The exception is assumed to have something to do with the
1281: * preference store.
1282: *
1283: * @param exception
1284: * The exception to be logged; must not be <code>null</code>.
1285: */
1286: private final void logPreferenceStoreException(
1287: final Throwable exception) {
1288: final String message = Util.translateString(RESOURCE_BUNDLE,
1289: "PreferenceStoreError.Message"); //$NON-NLS-1$
1290: String exceptionMessage = exception.getMessage();
1291: if (exceptionMessage == null) {
1292: exceptionMessage = message;
1293: }
1294: final IStatus status = new Status(IStatus.ERROR,
1295: WorkbenchPlugin.PI_WORKBENCH, 0, exceptionMessage,
1296: exception);
1297: WorkbenchPlugin.log(message, status);
1298: StatusUtil.handleStatus(message, exception, StatusManager.SHOW);
1299: }
1300:
1301: public final boolean performCancel() {
1302: // Save the selected tab for future reference.
1303: persistSelectedTab();
1304:
1305: return super .performCancel();
1306: }
1307:
1308: protected final void performDefaults() {
1309: // Ask the user to confirm
1310: final String title = Util.translateString(RESOURCE_BUNDLE,
1311: "restoreDefaultsMessageBoxText"); //$NON-NLS-1$
1312: final String message = Util.translateString(RESOURCE_BUNDLE,
1313: "restoreDefaultsMessageBoxMessage"); //$NON-NLS-1$
1314: final boolean confirmed = MessageDialog.openConfirm(getShell(),
1315: title, message);
1316:
1317: if (confirmed) {
1318: // Fix the scheme in the local changes.
1319: final String defaultSchemeId = bindingService
1320: .getDefaultSchemeId();
1321: final Scheme defaultScheme = localChangeManager
1322: .getScheme(defaultSchemeId);
1323: try {
1324: localChangeManager.setActiveScheme(defaultScheme);
1325: } catch (final NotDefinedException e) {
1326: // At least we tried....
1327: }
1328:
1329: // Fix the bindings in the local changes.
1330: final Binding[] currentBindings = localChangeManager
1331: .getBindings();
1332: final int currentBindingsLength = currentBindings.length;
1333: final Set trimmedBindings = new HashSet();
1334: for (int i = 0; i < currentBindingsLength; i++) {
1335: final Binding binding = currentBindings[i];
1336: if (binding.getType() != Binding.USER) {
1337: trimmedBindings.add(binding);
1338: }
1339: }
1340: final Binding[] trimmedBindingArray = (Binding[]) trimmedBindings
1341: .toArray(new Binding[trimmedBindings.size()]);
1342: localChangeManager.setBindings(trimmedBindingArray);
1343:
1344: // Apply the changes.
1345: try {
1346: bindingService.savePreferences(defaultScheme,
1347: trimmedBindingArray);
1348: } catch (final IOException e) {
1349: logPreferenceStoreException(e);
1350: }
1351: }
1352:
1353: setScheme(localChangeManager.getActiveScheme()); // update the scheme
1354: update(true);
1355: super .performDefaults();
1356: }
1357:
1358: public final boolean performOk() {
1359: // Save the preferences.
1360: try {
1361: bindingService.savePreferences(localChangeManager
1362: .getActiveScheme(), localChangeManager
1363: .getBindings());
1364: } catch (final IOException e) {
1365: logPreferenceStoreException(e);
1366: }
1367:
1368: // Save the selected tab for future reference.
1369: persistSelectedTab();
1370:
1371: return super .performOk();
1372: }
1373:
1374: /**
1375: * Remembers the currently selected tab for when the preference page next
1376: * opens.
1377: */
1378: private final void persistSelectedTab() {
1379: final IPreferenceStore store = getPreferenceStore();
1380: store.setValue(
1381: IPreferenceConstants.KEYS_PREFERENCE_SELECTED_TAB,
1382: tabFolder.getSelectionIndex());
1383: }
1384:
1385: /**
1386: * Handles the selection event on the add button. This removes all
1387: * user-defined bindings matching the given key sequence, scheme and
1388: * context. It then adds a new binding with the current selections.
1389: */
1390: private final void selectedButtonAdd() {
1391: final ParameterizedCommand command = getParameterizedCommand();
1392: final String contextId = getContextId();
1393: final String schemeId = getSchemeId();
1394: final KeySequence keySequence = getKeySequence();
1395: localChangeManager.removeBindings(keySequence, schemeId,
1396: contextId, null, null, null, Binding.USER);
1397: localChangeManager.addBinding(new KeyBinding(keySequence,
1398: command, schemeId, contextId, null, null, null,
1399: Binding.USER));
1400: update(true);
1401: }
1402:
1403: /**
1404: * Provides a facility for exporting the viewable list of key bindings to a
1405: * file. Currently, this only supports exporting to a list of
1406: * comma-separated values. The user is prompted for which file should
1407: * receive our bounty.
1408: *
1409: * @since 3.1
1410: */
1411: private final void selectedButtonExport() {
1412: final FileDialog fileDialog = new FileDialog(getShell(),
1413: SWT.SAVE);
1414: fileDialog.setFilterExtensions(new String[] { "*.csv" }); //$NON-NLS-1$
1415: fileDialog.setFilterNames(new String[] { Util.translateString(
1416: RESOURCE_BUNDLE, "csvFilterName") }); //$NON-NLS-1$
1417: final String filePath = fileDialog.open();
1418: if (filePath == null) {
1419: return;
1420: }
1421:
1422: final SafeRunnable runnable = new SafeRunnable() {
1423: public final void run() throws IOException {
1424: Writer fileWriter = null;
1425: try {
1426: fileWriter = new BufferedWriter(new FileWriter(
1427: filePath));
1428: final TableItem[] items = tableBindings.getItems();
1429: final int numColumns = tableBindings
1430: .getColumnCount();
1431: for (int i = 0; i < items.length; i++) {
1432: final TableItem item = items[i];
1433: for (int j = 0; j < numColumns; j++) {
1434: String buf = Util.replaceAll(item
1435: .getText(j), "\"", //$NON-NLS-1$
1436: "\"\""); //$NON-NLS-1$
1437: fileWriter.write("\"" + buf + "\""); //$NON-NLS-1$//$NON-NLS-2$
1438: if (j < numColumns - 1) {
1439: fileWriter.write(',');
1440: }
1441: }
1442: fileWriter.write(System
1443: .getProperty("line.separator")); //$NON-NLS-1$
1444: }
1445:
1446: } finally {
1447: if (fileWriter != null) {
1448: try {
1449: fileWriter.close();
1450: } catch (final IOException e) {
1451: // At least I tried.
1452: }
1453: }
1454:
1455: }
1456: }
1457: };
1458: SafeRunner.run(runnable);
1459: }
1460:
1461: /**
1462: * Handles the selection event on the remove button. This removes all
1463: * user-defined bindings matching the given key sequence, scheme and
1464: * context. It then adds a new deletion binding for the selected trigger
1465: * sequence.
1466: */
1467: private final void selectedButtonRemove() {
1468: final String contextId = getContextId();
1469: final String schemeId = getSchemeId();
1470: final KeySequence keySequence = getKeySequence();
1471: localChangeManager.removeBindings(keySequence, schemeId,
1472: contextId, null, null, null, Binding.USER);
1473: localChangeManager.addBinding(new KeyBinding(keySequence, null,
1474: schemeId, contextId, null, null, null, Binding.USER));
1475: update(true);
1476: }
1477:
1478: /**
1479: * Handles the selection event on the restore button. This removes all
1480: * user-defined bindings matching the given key sequence, scheme and
1481: * context.
1482: */
1483: private final void selectedButtonRestore() {
1484: String contextId = getContextId();
1485: String schemeId = getSchemeId();
1486: KeySequence keySequence = getKeySequence();
1487: localChangeManager.removeBindings(keySequence, schemeId,
1488: contextId, null, null, null, Binding.USER);
1489: update(true);
1490: }
1491:
1492: /**
1493: * Updates the local managers active scheme, and then updates the interface.
1494: */
1495: private final void selectedComboScheme() {
1496: final String activeSchemeId = getSchemeId();
1497: final Scheme activeScheme = localChangeManager
1498: .getScheme(activeSchemeId);
1499: try {
1500: localChangeManager.setActiveScheme(activeScheme);
1501: } catch (final NotDefinedException e) {
1502: // Oh, well.
1503: }
1504: update(true);
1505: }
1506:
1507: /**
1508: * Handles the selection event on the table containing the bindings for a
1509: * particular command. This updates the context and trigger sequence based
1510: * on the selected binding.
1511: */
1512: private final void selectedTableBindingsForCommand() {
1513: final int selection = tableBindingsForCommand
1514: .getSelectionIndex();
1515: if ((selection >= 0)
1516: && (selection < tableBindingsForCommand.getItemCount())) {
1517: final TableItem item = tableBindingsForCommand
1518: .getItem(selection);
1519: final KeyBinding binding = (KeyBinding) item
1520: .getData(ITEM_DATA_KEY);
1521: setContextId(binding.getContextId());
1522: setKeySequence(binding.getKeySequence());
1523: }
1524:
1525: update();
1526: }
1527:
1528: /**
1529: * Handles the selection event on the table containing the bindings for a
1530: * particular trigger sequence. This updates the context based on the
1531: * selected binding.
1532: */
1533: private final void selectedTableBindingsForTriggerSequence() {
1534: final int selection = tableBindingsForTriggerSequence
1535: .getSelectionIndex();
1536: if ((selection >= 0)
1537: && (selection < tableBindingsForTriggerSequence
1538: .getItemCount())) {
1539: final TableItem item = tableBindingsForTriggerSequence
1540: .getItem(selection);
1541: final Binding binding = (Binding) item
1542: .getData(ITEM_DATA_KEY);
1543: setContextId(binding.getContextId());
1544: }
1545:
1546: update();
1547: }
1548:
1549: /**
1550: * Responds to some kind of trigger on the View tab by taking the current
1551: * selection on the key bindings table and selecting the appropriate items
1552: * in the Modify tab.
1553: *
1554: * @since 3.1
1555: */
1556: private final void selectedTableKeyBindings() {
1557: final int selectionIndex = tableBindings.getSelectionIndex();
1558: if (selectionIndex != -1) {
1559: final TableItem item = tableBindings
1560: .getItem(selectionIndex);
1561: final Binding binding = (Binding) item.getData(BINDING_KEY);
1562: editBinding(binding);
1563:
1564: } else {
1565: editBinding(null);
1566: }
1567: }
1568:
1569: /**
1570: * Changes the selected context name in the context combo box. The context
1571: * selected is either the one matching the identifier provided (if
1572: * possible), or the default context identifier. If no matching name can be
1573: * found in the combo, then the first item is selected.
1574: *
1575: * @param contextId
1576: * The context identifier for the context to be selected in the
1577: * combo box; may be <code>null</code>.
1578: */
1579: private final void setContextId(final String contextId) {
1580: // Clear the current selection.
1581: comboContext.clearSelection();
1582: comboContext.deselectAll();
1583:
1584: // Figure out which name to look for.
1585: String contextName = (String) contextUniqueNamesById
1586: .get(contextId);
1587: if (contextName == null) {
1588: contextName = (String) contextUniqueNamesById
1589: .get(IContextIds.CONTEXT_ID_WINDOW);
1590: }
1591: if (contextName == null) {
1592: contextName = Util.ZERO_LENGTH_STRING;
1593: }
1594:
1595: // Scan the list for the selection we're looking for.
1596: final String[] items = comboContext.getItems();
1597: boolean found = false;
1598: for (int i = 0; i < items.length; i++) {
1599: if (contextName.equals(items[i])) {
1600: comboContext.select(i);
1601: found = true;
1602: break;
1603: }
1604: }
1605:
1606: // If we didn't find an item, then set the first item as selected.
1607: if ((!found) && (items.length > 0)) {
1608: comboContext.select(0);
1609: }
1610: }
1611:
1612: /**
1613: * Sets the current trigger sequence.
1614: *
1615: * @param keySequence
1616: * The trigger sequence; may be <code>null</code>.
1617: */
1618: private final void setKeySequence(final KeySequence keySequence) {
1619: textTriggerSequenceManager.setKeySequence(keySequence);
1620: }
1621:
1622: /**
1623: * Changes the selection in the command combo box.
1624: *
1625: * @param command
1626: * The fully-parameterized command to select; may be
1627: * <code>null</code>.
1628: */
1629: private final void setParameterizedCommand(
1630: final ParameterizedCommand command) {
1631: int i = 0;
1632: if (commands != null) {
1633: final int commandCount = commands.length;
1634: for (; i < commandCount; i++) {
1635: if (commands[i].equals(command)) {
1636: if ((comboCommand.getSelectionIndex() != i)
1637: && (i < comboCommand.getItemCount())) {
1638: comboCommand.select(i);
1639: }
1640: break;
1641: }
1642: }
1643: if ((i >= comboCommand.getItemCount())
1644: && (comboCommand.getSelectionIndex() != 0)) {
1645: comboCommand.select(0);
1646: }
1647: }
1648: }
1649:
1650: /**
1651: * Sets the currently selected scheme
1652: *
1653: * @param scheme
1654: * The scheme to select; may be <code>null</code>.
1655: */
1656: private final void setScheme(final Scheme scheme) {
1657: comboScheme.clearSelection();
1658: comboScheme.deselectAll();
1659: final String schemeUniqueName = (String) schemeUniqueNamesById
1660: .get(scheme.getId());
1661:
1662: if (schemeUniqueName != null) {
1663: final String items[] = comboScheme.getItems();
1664:
1665: for (int i = 0; i < items.length; i++) {
1666: if (schemeUniqueName.equals(items[i])) {
1667: comboScheme.select(i);
1668: break;
1669: }
1670: }
1671: }
1672: }
1673:
1674: /**
1675: * Builds the internal look-up tables before allowing the page to become
1676: * visible.
1677: */
1678: public final void setVisible(final boolean visible) {
1679: if (visible == true) {
1680: Map contextsByName = new HashMap();
1681:
1682: for (Iterator iterator = contextService
1683: .getDefinedContextIds().iterator(); iterator
1684: .hasNext();) {
1685: Context context = contextService
1686: .getContext((String) iterator.next());
1687: try {
1688: String name = context.getName();
1689: Collection contexts = (Collection) contextsByName
1690: .get(name);
1691:
1692: if (contexts == null) {
1693: contexts = new HashSet();
1694: contextsByName.put(name, contexts);
1695: }
1696:
1697: contexts.add(context);
1698: } catch (final NotDefinedException e) {
1699: // Do nothing.
1700: }
1701: }
1702:
1703: Map commandsByName = new HashMap();
1704:
1705: for (Iterator iterator = commandService
1706: .getDefinedCommandIds().iterator(); iterator
1707: .hasNext();) {
1708: Command command = commandService
1709: .getCommand((String) iterator.next());
1710: if (!isActive(command)) {
1711: continue;
1712: }
1713:
1714: try {
1715: String name = command.getName();
1716: Collection commands = (Collection) commandsByName
1717: .get(name);
1718:
1719: if (commands == null) {
1720: commands = new HashSet();
1721: commandsByName.put(name, commands);
1722: }
1723:
1724: commands.add(command);
1725: } catch (NotDefinedException eNotDefined) {
1726: // Do nothing
1727: }
1728: }
1729:
1730: // moved here to allow us to remove any empty categories
1731: commandIdsByCategoryId = new HashMap();
1732:
1733: for (Iterator iterator = commandService
1734: .getDefinedCommandIds().iterator(); iterator
1735: .hasNext();) {
1736: final Command command = commandService
1737: .getCommand((String) iterator.next());
1738: if (!isActive(command)) {
1739: continue;
1740: }
1741:
1742: try {
1743: String categoryId = command.getCategory().getId();
1744: Collection commandIds = (Collection) commandIdsByCategoryId
1745: .get(categoryId);
1746:
1747: if (commandIds == null) {
1748: commandIds = new HashSet();
1749: commandIdsByCategoryId.put(categoryId,
1750: commandIds);
1751: }
1752:
1753: commandIds.add(command.getId());
1754: } catch (NotDefinedException eNotDefined) {
1755: // Do nothing
1756: }
1757: }
1758:
1759: Map categoriesByName = new HashMap();
1760:
1761: for (Iterator iterator = commandService
1762: .getDefinedCategoryIds().iterator(); iterator
1763: .hasNext();) {
1764: Category category = commandService
1765: .getCategory((String) iterator.next());
1766:
1767: try {
1768: if (commandIdsByCategoryId.containsKey(category
1769: .getId())) {
1770: String name = category.getName();
1771: Collection categories = (Collection) categoriesByName
1772: .get(name);
1773:
1774: if (categories == null) {
1775: categories = new HashSet();
1776: categoriesByName.put(name, categories);
1777: }
1778:
1779: categories.add(category);
1780: }
1781: } catch (NotDefinedException eNotDefined) {
1782: // Do nothing
1783: }
1784: }
1785:
1786: Map schemesByName = new HashMap();
1787:
1788: final Scheme[] definedSchemes = bindingService
1789: .getDefinedSchemes();
1790: for (int i = 0; i < definedSchemes.length; i++) {
1791: final Scheme scheme = definedSchemes[i];
1792: try {
1793: String name = scheme.getName();
1794: Collection schemes = (Collection) schemesByName
1795: .get(name);
1796:
1797: if (schemes == null) {
1798: schemes = new HashSet();
1799: schemesByName.put(name, schemes);
1800: }
1801:
1802: schemes.add(scheme);
1803: } catch (final NotDefinedException e) {
1804: // Do nothing.
1805: }
1806: }
1807:
1808: contextIdsByUniqueName = new HashMap();
1809: contextUniqueNamesById = new HashMap();
1810:
1811: for (Iterator iterator = contextsByName.entrySet()
1812: .iterator(); iterator.hasNext();) {
1813: Map.Entry entry = (Map.Entry) iterator.next();
1814: String name = (String) entry.getKey();
1815: Set contexts = (Set) entry.getValue();
1816: Iterator iterator2 = contexts.iterator();
1817:
1818: if (contexts.size() == 1) {
1819: Context context = (Context) iterator2.next();
1820: contextIdsByUniqueName.put(name, context.getId());
1821: contextUniqueNamesById.put(context.getId(), name);
1822: } else {
1823: while (iterator2.hasNext()) {
1824: Context context = (Context) iterator2.next();
1825: String uniqueName = MessageFormat
1826: .format(
1827: Util.translateString(
1828: RESOURCE_BUNDLE,
1829: "uniqueName"), new Object[] { name, //$NON-NLS-1$
1830: context.getId() });
1831: contextIdsByUniqueName.put(uniqueName, context
1832: .getId());
1833: contextUniqueNamesById.put(context.getId(),
1834: uniqueName);
1835: }
1836: }
1837: }
1838:
1839: categoryIdsByUniqueName = new HashMap();
1840: categoryUniqueNamesById = new HashMap();
1841:
1842: for (Iterator iterator = categoriesByName.entrySet()
1843: .iterator(); iterator.hasNext();) {
1844: Map.Entry entry = (Map.Entry) iterator.next();
1845: String name = (String) entry.getKey();
1846: Set categories = (Set) entry.getValue();
1847: Iterator iterator2 = categories.iterator();
1848:
1849: if (categories.size() == 1) {
1850: Category category = (Category) iterator2.next();
1851: categoryIdsByUniqueName.put(name, category.getId());
1852: categoryUniqueNamesById.put(category.getId(), name);
1853: } else {
1854: while (iterator2.hasNext()) {
1855: Category category = (Category) iterator2.next();
1856: String uniqueName = MessageFormat
1857: .format(
1858: Util.translateString(
1859: RESOURCE_BUNDLE,
1860: "uniqueName"), new Object[] { name, //$NON-NLS-1$
1861: category.getId() });
1862: categoryIdsByUniqueName.put(uniqueName,
1863: category.getId());
1864: categoryUniqueNamesById.put(category.getId(),
1865: uniqueName);
1866: }
1867: }
1868: }
1869:
1870: schemeIdsByUniqueName = new HashMap();
1871: schemeUniqueNamesById = new HashMap();
1872:
1873: for (Iterator iterator = schemesByName.entrySet()
1874: .iterator(); iterator.hasNext();) {
1875: Map.Entry entry = (Map.Entry) iterator.next();
1876: String name = (String) entry.getKey();
1877: Set keyConfigurations = (Set) entry.getValue();
1878: Iterator iterator2 = keyConfigurations.iterator();
1879:
1880: if (keyConfigurations.size() == 1) {
1881: Scheme scheme = (Scheme) iterator2.next();
1882: schemeIdsByUniqueName.put(name, scheme.getId());
1883: schemeUniqueNamesById.put(scheme.getId(), name);
1884: } else {
1885: while (iterator2.hasNext()) {
1886: Scheme scheme = (Scheme) iterator2.next();
1887: String uniqueName = MessageFormat
1888: .format(
1889: Util.translateString(
1890: RESOURCE_BUNDLE,
1891: "uniqueName"), new Object[] { name, //$NON-NLS-1$
1892: scheme.getId() });
1893: schemeIdsByUniqueName.put(uniqueName, scheme
1894: .getId());
1895: schemeUniqueNamesById.put(scheme.getId(),
1896: uniqueName);
1897: }
1898: }
1899: }
1900:
1901: Scheme activeScheme = bindingService.getActiveScheme();
1902:
1903: // Make an internal copy of the binding manager, for local changes.
1904: try {
1905: for (int i = 0; i < definedSchemes.length; i++) {
1906: final Scheme scheme = definedSchemes[i];
1907: final Scheme copy = localChangeManager
1908: .getScheme(scheme.getId());
1909: copy.define(scheme.getName(), scheme
1910: .getDescription(), scheme.getParentId());
1911: }
1912: localChangeManager.setActiveScheme(bindingService
1913: .getActiveScheme());
1914: } catch (final NotDefinedException e) {
1915: throw new Error(
1916: "There is a programmer error in the keys preference page"); //$NON-NLS-1$
1917: }
1918: localChangeManager.setLocale(bindingService.getLocale());
1919: localChangeManager
1920: .setPlatform(bindingService.getPlatform());
1921: localChangeManager
1922: .setBindings(bindingService.getBindings());
1923:
1924: // Populate the category combo box.
1925: List categoryNames = new ArrayList(categoryIdsByUniqueName
1926: .keySet());
1927: Collections.sort(categoryNames, Collator.getInstance());
1928: if (commandIdsByCategoryId.containsKey(null)) {
1929: categoryNames.add(0, Util.translateString(
1930: RESOURCE_BUNDLE, "other")); //$NON-NLS-1$
1931: }
1932: comboCategory.setItems((String[]) categoryNames
1933: .toArray(new String[categoryNames.size()]));
1934: comboCategory.clearSelection();
1935: comboCategory.deselectAll();
1936: if (commandIdsByCategoryId.containsKey(null)
1937: || !categoryNames.isEmpty()) {
1938: comboCategory.select(0);
1939: }
1940:
1941: // Populate the scheme combo box.
1942: List schemeNames = new ArrayList(schemeIdsByUniqueName
1943: .keySet());
1944: Collections.sort(schemeNames, Collator.getInstance());
1945: comboScheme.setItems((String[]) schemeNames
1946: .toArray(new String[schemeNames.size()]));
1947: setScheme(activeScheme);
1948:
1949: // Update the entire page.
1950: update(true);
1951: }
1952:
1953: super .setVisible(visible);
1954: }
1955:
1956: /**
1957: * Updates the entire preference page -- except the view tab -- based on
1958: * current selection sate. This preference page is written so that
1959: * everything can be made consistent simply by inspecting the state of its
1960: * widgets. A change is triggered by the user, and an event is fired. The
1961: * event triggers an update. It is possible for extra work to be done by
1962: * this page before calling update.
1963: */
1964: private final void update() {
1965: update(false);
1966: }
1967:
1968: /**
1969: * Updates the entire preference page based on current changes. This
1970: * preference page is written so that everything can be made consistent
1971: * simply by inspecting the state of its widgets. A change is triggered by
1972: * the user, and an event is fired. The event triggers an update. It is
1973: * possible for extra work to be done by this page before calling update.
1974: *
1975: * @param updateViewTab
1976: * Whether the view tab should be updated as well.
1977: */
1978: private final void update(final boolean updateViewTab) {
1979: if (updateViewTab) {
1980: updateViewTab();
1981: }
1982: updateComboCommand();
1983: updateComboContext();
1984: final TriggerSequence triggerSequence = getKeySequence();
1985: updateTableBindingsForTriggerSequence(triggerSequence);
1986: final ParameterizedCommand command = getParameterizedCommand();
1987: updateTableBindingsForCommand(command);
1988: final String contextId = getContextId();
1989: updateSelection(tableBindingsForTriggerSequence, contextId,
1990: triggerSequence);
1991: updateSelection(tableBindingsForCommand, contextId,
1992: triggerSequence);
1993: updateLabelSchemeExtends();
1994: updateLabelContextExtends();
1995: updateEnabled(triggerSequence, command);
1996: }
1997:
1998: /**
1999: * Updates the contents of the commands combo box, based on the current
2000: * selection in the category combo box.
2001: */
2002: private final void updateComboCommand() {
2003: // Remember the current selection, so we can restore it later.
2004: final ParameterizedCommand command = getParameterizedCommand();
2005:
2006: // Figure out where command identifiers apply to the selected category.
2007: final String categoryId = getCategoryId();
2008: Set commandIds = (Set) commandIdsByCategoryId.get(categoryId);
2009: if (commandIds == null) {
2010: commandIds = Collections.EMPTY_SET;
2011: }
2012:
2013: /*
2014: * Generate an array of parameterized commands based on these
2015: * identifiers. The parameterized commands will be sorted based on their
2016: * names.
2017: */
2018: List commands = new ArrayList();
2019: final Iterator commandIdItr = commandIds.iterator();
2020: while (commandIdItr.hasNext()) {
2021: final String currentCommandId = (String) commandIdItr
2022: .next();
2023: final Command currentCommand = commandService
2024: .getCommand(currentCommandId);
2025: try {
2026: commands.addAll(ParameterizedCommand
2027: .generateCombinations(currentCommand));
2028: } catch (final NotDefinedException e) {
2029: // It is safe to just ignore undefined commands.
2030: }
2031: }
2032:
2033: // sort the commands with a collator, so they appear in the
2034: // combo correctly
2035: commands = sortParameterizedCommands(commands);
2036:
2037: final int commandCount = commands.size();
2038: this .commands = (ParameterizedCommand[]) commands
2039: .toArray(new ParameterizedCommand[commandCount]);
2040:
2041: /*
2042: * Generate an array of command names based on this array of
2043: * parameterized commands.
2044: */
2045: final String[] commandNames = new String[commandCount];
2046: for (int i = 0; i < commandCount; i++) {
2047: try {
2048: commandNames[i] = this .commands[i].getName();
2049: } catch (final NotDefinedException e) {
2050: throw new Error(
2051: "Concurrent modification of the command's defined state"); //$NON-NLS-1$
2052: }
2053: }
2054:
2055: /*
2056: * Copy the command names into the combo box, but only if they've
2057: * changed. We do this to try to avoid unnecessary calls out to the
2058: * operating system, as well as to defend against bugs in SWT's event
2059: * mechanism.
2060: */
2061: final String[] currentItems = comboCommand.getItems();
2062: if (!Arrays.equals(currentItems, commandNames)) {
2063: comboCommand.setItems(commandNames);
2064: }
2065:
2066: // Try to restore the selection.
2067: setParameterizedCommand(command);
2068:
2069: /*
2070: * Just to be extra careful, make sure that we have a selection at this
2071: * point. This line could probably be removed, but it makes the code a
2072: * bit more robust.
2073: */
2074: if ((comboCommand.getSelectionIndex() == -1)
2075: && (commandCount > 0)) {
2076: comboCommand.select(0);
2077: }
2078: }
2079:
2080: /**
2081: * Sort the commands using the correct language.
2082: * @param commands the List of ParameterizedCommands
2083: * @return The sorted List
2084: */
2085: private List sortParameterizedCommands(List commands) {
2086: final Collator collator = Collator.getInstance();
2087:
2088: // this comparator is based on the ParameterizedCommands#compareTo(*)
2089: // method, but uses the collator.
2090: Comparator comparator = new Comparator() {
2091: public int compare(Object o1, Object o2) {
2092: String name1 = null;
2093: String name2 = null;
2094: try {
2095: name1 = ((ParameterizedCommand) o1).getName();
2096: } catch (NotDefinedException e) {
2097: return -1;
2098: }
2099: try {
2100: name2 = ((ParameterizedCommand) o2).getName();
2101: } catch (NotDefinedException e) {
2102: return 1;
2103: }
2104: int rc = collator.compare(name1, name2);
2105: if (rc != 0) {
2106: return rc;
2107: }
2108:
2109: String id1 = ((ParameterizedCommand) o1).getId();
2110: String id2 = ((ParameterizedCommand) o2).getId();
2111: return collator.compare(id1, id2);
2112: }
2113: };
2114: Collections.sort(commands, comparator);
2115: return commands;
2116: }
2117:
2118: /**
2119: * Updates the contents of the context combo box, as well as its selection.
2120: */
2121: private final void updateComboContext() {
2122: final String contextId = getContextId();
2123: final Map contextIdsByName = new HashMap(contextIdsByUniqueName);
2124:
2125: final List contextNames = new ArrayList(contextIdsByName
2126: .keySet());
2127: Collections.sort(contextNames, Collator.getInstance());
2128:
2129: comboContext.setItems((String[]) contextNames
2130: .toArray(new String[contextNames.size()]));
2131: setContextId(contextId);
2132:
2133: if (comboContext.getSelectionIndex() == -1
2134: && !contextNames.isEmpty()) {
2135: comboContext.select(0);
2136: }
2137: }
2138:
2139: /**
2140: * Updates the enabled state of the various widgets on this page. The
2141: * decision is based on the current trigger sequence and the currently
2142: * selected command.
2143: *
2144: * @param triggerSequence
2145: * The current trigger sequence; may be empty, but never
2146: * <code>null</code>.
2147: * @param command
2148: * The currently selected command, if any; <code>null</code>
2149: * otherwise.
2150: */
2151: private final void updateEnabled(
2152: final TriggerSequence triggerSequence,
2153: final ParameterizedCommand command) {
2154: final boolean commandSelected = command != null;
2155: labelBindingsForCommand.setEnabled(commandSelected);
2156: tableBindingsForCommand.setEnabled(commandSelected);
2157:
2158: final boolean triggerSequenceSelected = !triggerSequence
2159: .isEmpty();
2160: labelBindingsForTriggerSequence
2161: .setEnabled(triggerSequenceSelected);
2162: tableBindingsForTriggerSequence
2163: .setEnabled(triggerSequenceSelected);
2164:
2165: /*
2166: * TODO Do some better button enablement.
2167: */
2168: final boolean buttonsEnabled = commandSelected
2169: && triggerSequenceSelected;
2170: buttonAdd.setEnabled(buttonsEnabled);
2171: buttonRemove.setEnabled(buttonsEnabled);
2172: buttonRestore.setEnabled(buttonsEnabled);
2173: }
2174:
2175: /**
2176: * Updates the label next to the context that says "extends" if the context
2177: * is a child of another context. If the context is not a child of another
2178: * context, then the label is simply blank.
2179: */
2180: private final void updateLabelContextExtends() {
2181: final String contextId = getContextId();
2182:
2183: if (contextId != null) {
2184: final Context context = contextService
2185: .getContext(getContextId());
2186: if (context.isDefined()) {
2187: try {
2188: final String parentId = context.getParentId();
2189: if (parentId != null) {
2190: final String name = (String) contextUniqueNamesById
2191: .get(parentId);
2192: if (name != null) {
2193: labelContextExtends.setText(MessageFormat
2194: .format(
2195: Util.translateString(
2196: RESOURCE_BUNDLE,
2197: "extends"), //$NON-NLS-1$
2198: new Object[] { name }));
2199: return;
2200: }
2201: }
2202: } catch (final NotDefinedException e) {
2203: // Do nothing
2204: }
2205: }
2206: }
2207:
2208: labelContextExtends.setText(Util.ZERO_LENGTH_STRING);
2209: }
2210:
2211: /**
2212: * Updates the label next to the scheme that says "extends" if the scheme is
2213: * a child of another scheme. If the scheme is not a child of another
2214: * scheme, then the label is simply blank.
2215: */
2216: private final void updateLabelSchemeExtends() {
2217: final String schemeId = getSchemeId();
2218:
2219: if (schemeId != null) {
2220: final Scheme scheme = bindingService.getScheme(schemeId);
2221: try {
2222: final String name = (String) schemeUniqueNamesById
2223: .get(scheme.getParentId());
2224: if (name != null) {
2225: labelSchemeExtends.setText(MessageFormat.format(
2226: Util.translateString(RESOURCE_BUNDLE,
2227: "extends"), //$NON-NLS-1$
2228: new Object[] { name }));
2229: return;
2230: }
2231: } catch (final NotDefinedException e) {
2232: // Do nothing
2233: }
2234: }
2235:
2236: labelSchemeExtends.setText(Util.ZERO_LENGTH_STRING);
2237: }
2238:
2239: /**
2240: * Tries to select the correct entry in table based on the currently
2241: * selected context and trigger sequence. If the table hasn't really
2242: * changed, then this method is essentially trying to restore the selection.
2243: * If it has changed, then it is trying to select the most entry based on
2244: * the context.
2245: *
2246: * @param table
2247: * The table to be changed; must not be <code>null</code>.
2248: * @param contextId
2249: * The currently selected context; should not be
2250: * <code>null</code>.
2251: * @param triggerSequence
2252: * The current trigger sequence; should not be <code>null</code>.
2253: */
2254: private final void updateSelection(final Table table,
2255: final String contextId,
2256: final TriggerSequence triggerSequence) {
2257: if (table.getSelectionCount() > 1) {
2258: table.deselectAll();
2259: }
2260:
2261: final TableItem[] items = table.getItems();
2262: int selection = -1;
2263: for (int i = 0; i < items.length; i++) {
2264: final Binding binding = (Binding) items[i]
2265: .getData(ITEM_DATA_KEY);
2266: if ((Util.equals(contextId, binding.getContextId()))
2267: && (Util.equals(triggerSequence, binding
2268: .getTriggerSequence()))) {
2269: selection = i;
2270: break;
2271: }
2272: }
2273:
2274: if (selection != -1) {
2275: table.select(selection);
2276: }
2277: }
2278:
2279: /**
2280: * Updates the contents of the table showing the bindings for the currently
2281: * selected command. The selection is destroyed by this process.
2282: *
2283: * @param parameterizedCommand
2284: * The currently selected fully-parameterized command; may be
2285: * <code>null</code>.
2286: */
2287: private final void updateTableBindingsForCommand(
2288: final ParameterizedCommand parameterizedCommand) {
2289: // Clear the table of existing items.
2290: tableBindingsForCommand.removeAll();
2291:
2292: // Add each of the bindings, if the command identifier matches.
2293: final Collection bindings = localChangeManager
2294: .getActiveBindingsDisregardingContextFlat();
2295: final Iterator bindingItr = bindings.iterator();
2296: while (bindingItr.hasNext()) {
2297: final Binding binding = (Binding) bindingItr.next();
2298: if (!Util.equals(parameterizedCommand, binding
2299: .getParameterizedCommand())) {
2300: continue; // binding does not match
2301: }
2302:
2303: final TableItem tableItem = new TableItem(
2304: tableBindingsForCommand, SWT.NULL);
2305: tableItem.setData(ITEM_DATA_KEY, binding);
2306:
2307: /*
2308: * Set the associated image based on the type of binding. Either it
2309: * is a user binding or a system binding.
2310: *
2311: * TODO Identify more image types.
2312: */
2313: if (binding.getType() == Binding.SYSTEM) {
2314: tableItem.setImage(0, IMAGE_BLANK);
2315: } else {
2316: tableItem.setImage(0, IMAGE_CHANGE);
2317: }
2318:
2319: String contextName = (String) contextUniqueNamesById
2320: .get(binding.getContextId());
2321: if (contextName == null) {
2322: contextName = Util.ZERO_LENGTH_STRING;
2323: }
2324: tableItem.setText(1, contextName);
2325: tableItem.setText(2, binding.getTriggerSequence().format());
2326: }
2327: }
2328:
2329: /**
2330: * Updates the contents of the table showing the bindings for the current
2331: * trigger sequence. The selection is destroyed by this process.
2332: *
2333: * @param triggerSequence
2334: * The current trigger sequence; may be <code>null</code> or
2335: * empty.
2336: */
2337: private final void updateTableBindingsForTriggerSequence(
2338: final TriggerSequence triggerSequence) {
2339: // Clear the table of its existing items.
2340: tableBindingsForTriggerSequence.removeAll();
2341:
2342: // Get the collection of bindings for the current command.
2343: final Map activeBindings = localChangeManager
2344: .getActiveBindingsDisregardingContext();
2345: final Collection bindings = (Collection) activeBindings
2346: .get(triggerSequence);
2347: if (bindings == null) {
2348: return;
2349: }
2350:
2351: // Add each of the bindings.
2352: final Iterator bindingItr = bindings.iterator();
2353: while (bindingItr.hasNext()) {
2354: final Binding binding = (Binding) bindingItr.next();
2355: final Context context = contextService.getContext(binding
2356: .getContextId());
2357: final ParameterizedCommand parameterizedCommand = binding
2358: .getParameterizedCommand();
2359: final Command command = parameterizedCommand.getCommand();
2360: if ((!context.isDefined()) && (!command.isDefined())) {
2361: continue;
2362: }
2363:
2364: final TableItem tableItem = new TableItem(
2365: tableBindingsForTriggerSequence, SWT.NULL);
2366: tableItem.setData(ITEM_DATA_KEY, binding);
2367:
2368: /*
2369: * Set the associated image based on the type of binding. Either it
2370: * is a user binding or a system binding.
2371: *
2372: * TODO Identify more image types.
2373: */
2374: if (binding.getType() == Binding.SYSTEM) {
2375: tableItem.setImage(0, IMAGE_BLANK);
2376: } else {
2377: tableItem.setImage(0, IMAGE_CHANGE);
2378: }
2379:
2380: try {
2381: tableItem.setText(1, context.getName());
2382: tableItem.setText(2, parameterizedCommand.getName());
2383: } catch (final NotDefinedException e) {
2384: throw new Error(
2385: "Context or command became undefined on a non-UI thread while the UI thread was processing."); //$NON-NLS-1$
2386: }
2387: }
2388: }
2389:
2390: /**
2391: * Updates the contents of the view tab. This queries the command manager
2392: * for a list of key sequence binding definitions, and these definitions are
2393: * then added to the table.
2394: *
2395: * @since 3.1
2396: */
2397: private final void updateViewTab() {
2398: // Clear out the existing table contents.
2399: tableBindings.removeAll();
2400:
2401: // Get a sorted list of key binding contents.
2402: final List bindings = new ArrayList(localChangeManager
2403: .getActiveBindingsDisregardingContextFlat());
2404: Collections.sort(bindings, new Comparator() {
2405: /**
2406: * Compares two instances of <code>Binding</code> based on the
2407: * current sort order.
2408: *
2409: * @param object1
2410: * The first object to compare; must be an instance of
2411: * <code>Binding</code> (i.e., not <code>null</code>).
2412: * @param object2
2413: * The second object to compare; must be an instance of
2414: * <code>Binding</code> (i.e., not <code>null</code>).
2415: * @return The integer value representing the comparison. The
2416: * comparison is based on the current sort order.
2417: * @since 3.1
2418: */
2419: public final int compare(final Object object1,
2420: final Object object2) {
2421: final Binding binding1 = (Binding) object1;
2422: final Binding binding2 = (Binding) object2;
2423:
2424: /*
2425: * Get the category name, command name, formatted key sequence
2426: * and context name for the first binding.
2427: */
2428: final Command command1 = binding1
2429: .getParameterizedCommand().getCommand();
2430: String categoryName1 = Util.ZERO_LENGTH_STRING;
2431: String commandName1 = Util.ZERO_LENGTH_STRING;
2432: try {
2433: commandName1 = command1.getName();
2434: categoryName1 = command1.getCategory().getName();
2435: } catch (final NotDefinedException e) {
2436: // Just use the zero-length string.
2437: }
2438: final String triggerSequence1 = binding1
2439: .getTriggerSequence().format();
2440: final String contextId1 = binding1.getContextId();
2441: String contextName1 = Util.ZERO_LENGTH_STRING;
2442: if (contextId1 != null) {
2443: final Context context = contextService
2444: .getContext(contextId1);
2445: try {
2446: contextName1 = context.getName();
2447: } catch (final org.eclipse.core.commands.common.NotDefinedException e) {
2448: // Just use the zero-length string.
2449: }
2450: }
2451:
2452: /*
2453: * Get the category name, command name, formatted key sequence
2454: * and context name for the first binding.
2455: */
2456: final Command command2 = binding2
2457: .getParameterizedCommand().getCommand();
2458: String categoryName2 = Util.ZERO_LENGTH_STRING;
2459: String commandName2 = Util.ZERO_LENGTH_STRING;
2460: try {
2461: commandName2 = command2.getName();
2462: categoryName2 = command2.getCategory().getName();
2463: } catch (final org.eclipse.core.commands.common.NotDefinedException e) {
2464: // Just use the zero-length string.
2465: }
2466: final String keySequence2 = binding2
2467: .getTriggerSequence().format();
2468: final String contextId2 = binding2.getContextId();
2469: String contextName2 = Util.ZERO_LENGTH_STRING;
2470: if (contextId2 != null) {
2471: final Context context = contextService
2472: .getContext(contextId2);
2473: try {
2474: contextName2 = context.getName();
2475: } catch (final org.eclipse.core.commands.common.NotDefinedException e) {
2476: // Just use the zero-length string.
2477: }
2478: }
2479:
2480: // Compare the items in the current sort order.
2481: int compare = 0;
2482: for (int i = 0; i < sortOrder.length; i++) {
2483: switch (sortOrder[i]) {
2484: case VIEW_CATEGORY_COLUMN_INDEX:
2485: compare = Util.compare(categoryName1,
2486: categoryName2);
2487: if (compare != 0) {
2488: return compare;
2489: }
2490: break;
2491: case VIEW_COMMAND_COLUMN_INDEX:
2492: compare = Util.compare(commandName1,
2493: commandName2);
2494: if (compare != 0) {
2495: return compare;
2496: }
2497: break;
2498: case VIEW_KEY_SEQUENCE_COLUMN_INDEX:
2499: compare = Util.compare(triggerSequence1,
2500: keySequence2);
2501: if (compare != 0) {
2502: return compare;
2503: }
2504: break;
2505: case VIEW_CONTEXT_COLUMN_INDEX:
2506: compare = Util.compare(contextName1,
2507: contextName2);
2508: if (compare != 0) {
2509: return compare;
2510: }
2511: break;
2512: default:
2513: throw new Error(
2514: "Programmer error: added another sort column without modifying the comparator."); //$NON-NLS-1$
2515: }
2516: }
2517:
2518: return compare;
2519: }
2520:
2521: /**
2522: * @see Object#equals(java.lang.Object)
2523: */
2524: public final boolean equals(final Object object) {
2525: return super .equals(object);
2526: }
2527: });
2528:
2529: // Add a table item for each item in the list.
2530: final Iterator keyBindingItr = bindings.iterator();
2531: while (keyBindingItr.hasNext()) {
2532: final Binding binding = (Binding) keyBindingItr.next();
2533:
2534: // Get the command and category name.
2535: final ParameterizedCommand command = binding
2536: .getParameterizedCommand();
2537: String commandName = Util.ZERO_LENGTH_STRING;
2538: String categoryName = Util.ZERO_LENGTH_STRING;
2539: try {
2540: commandName = command.getName();
2541: categoryName = command.getCommand().getCategory()
2542: .getName();
2543: } catch (final org.eclipse.core.commands.common.NotDefinedException e) {
2544: // Just use the zero-length string.
2545: }
2546:
2547: // Ignore items with a meaningless command name.
2548: if ((commandName == null) || (commandName.length() == 0)) {
2549: continue;
2550: }
2551:
2552: // Get the context name.
2553: final String contextId = binding.getContextId();
2554: String contextName = Util.ZERO_LENGTH_STRING;
2555: if (contextId != null) {
2556: final Context context = contextService
2557: .getContext(contextId);
2558: try {
2559: contextName = context.getName();
2560: } catch (final org.eclipse.core.commands.common.NotDefinedException e) {
2561: // Just use the zero-length string.
2562: }
2563: }
2564:
2565: // Create the table item.
2566: final TableItem item = new TableItem(tableBindings,
2567: SWT.NONE);
2568: item.setText(VIEW_CATEGORY_COLUMN_INDEX, categoryName);
2569: item.setText(VIEW_COMMAND_COLUMN_INDEX, commandName);
2570: item.setText(VIEW_KEY_SEQUENCE_COLUMN_INDEX, binding
2571: .getTriggerSequence().format());
2572: item.setText(VIEW_CONTEXT_COLUMN_INDEX, contextName);
2573: item.setData(BINDING_KEY, binding);
2574: }
2575:
2576: // Pack the columns.
2577: for (int i = 0; i < tableBindings.getColumnCount(); i++) {
2578: tableBindings.getColumn(i).pack();
2579: }
2580: }
2581:
2582: }
|