0001: /*******************************************************************************
0002: * Copyright (c) 2005, 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.IOException;
0013: import java.net.URL;
0014: import java.util.ArrayList;
0015: import java.util.Arrays;
0016: import java.util.Collection;
0017: import java.util.Collections;
0018: import java.util.HashSet;
0019: import java.util.Iterator;
0020: import java.util.List;
0021: import java.util.Locale;
0022: import java.util.Set;
0023:
0024: import org.eclipse.core.commands.Category;
0025: import org.eclipse.core.commands.Command;
0026: import org.eclipse.core.commands.CommandManager;
0027: import org.eclipse.core.commands.ParameterizedCommand;
0028: import org.eclipse.core.commands.common.NamedHandleObject;
0029: import org.eclipse.core.commands.common.NamedHandleObjectComparator;
0030: import org.eclipse.core.commands.common.NotDefinedException;
0031: import org.eclipse.core.commands.contexts.Context;
0032: import org.eclipse.core.commands.contexts.ContextManager;
0033: import org.eclipse.core.commands.util.Tracing;
0034: import org.eclipse.core.databinding.observable.Diffs;
0035: import org.eclipse.core.databinding.observable.IStaleListener;
0036: import org.eclipse.core.databinding.observable.StaleEvent;
0037: import org.eclipse.core.databinding.observable.set.IObservableSet;
0038: import org.eclipse.core.databinding.observable.set.ISetChangeListener;
0039: import org.eclipse.core.databinding.observable.set.ObservableSet;
0040: import org.eclipse.core.databinding.observable.set.SetChangeEvent;
0041: import org.eclipse.core.databinding.observable.set.UnionSet;
0042: import org.eclipse.core.databinding.observable.set.WritableSet;
0043: import org.eclipse.core.databinding.observable.value.IObservableValue;
0044: import org.eclipse.core.databinding.observable.value.IValueChangeListener;
0045: import org.eclipse.core.databinding.observable.value.ValueChangeEvent;
0046: import org.eclipse.core.runtime.IStatus;
0047: import org.eclipse.core.runtime.Status;
0048: import org.eclipse.jface.bindings.Binding;
0049: import org.eclipse.jface.bindings.BindingManager;
0050: import org.eclipse.jface.bindings.Scheme;
0051: import org.eclipse.jface.bindings.keys.KeyBinding;
0052: import org.eclipse.jface.bindings.keys.KeySequence;
0053: import org.eclipse.jface.bindings.keys.KeySequenceText;
0054: import org.eclipse.jface.bindings.keys.KeyStroke;
0055: import org.eclipse.jface.contexts.IContextIds;
0056: import org.eclipse.jface.databinding.swt.SWTObservables;
0057: import org.eclipse.jface.databinding.viewers.ViewersObservables;
0058: import org.eclipse.jface.dialogs.IDialogConstants;
0059: import org.eclipse.jface.dialogs.IDialogSettings;
0060: import org.eclipse.jface.dialogs.MessageDialog;
0061: import org.eclipse.jface.internal.databinding.provisional.swt.ControlUpdater;
0062: import org.eclipse.jface.preference.PreferencePage;
0063: import org.eclipse.jface.resource.DeviceResourceException;
0064: import org.eclipse.jface.resource.ImageDescriptor;
0065: import org.eclipse.jface.resource.JFaceResources;
0066: import org.eclipse.jface.resource.LocalResourceManager;
0067: import org.eclipse.jface.util.IPropertyChangeListener;
0068: import org.eclipse.jface.util.PropertyChangeEvent;
0069: import org.eclipse.jface.viewers.AbstractListViewer;
0070: import org.eclipse.jface.viewers.ArrayContentProvider;
0071: import org.eclipse.jface.viewers.ComboViewer;
0072: import org.eclipse.jface.viewers.IBaseLabelProvider;
0073: import org.eclipse.jface.viewers.ISelection;
0074: import org.eclipse.jface.viewers.ISelectionChangedListener;
0075: import org.eclipse.jface.viewers.IStructuredSelection;
0076: import org.eclipse.jface.viewers.ITableLabelProvider;
0077: import org.eclipse.jface.viewers.ITreeContentProvider;
0078: import org.eclipse.jface.viewers.LabelProvider;
0079: import org.eclipse.jface.viewers.NamedHandleObjectLabelProvider;
0080: import org.eclipse.jface.viewers.SelectionChangedEvent;
0081: import org.eclipse.jface.viewers.StructuredSelection;
0082: import org.eclipse.jface.viewers.TreeViewer;
0083: import org.eclipse.jface.viewers.Viewer;
0084: import org.eclipse.jface.viewers.ViewerComparator;
0085: import org.eclipse.jface.window.Window;
0086: import org.eclipse.swt.SWT;
0087: import org.eclipse.swt.custom.BusyIndicator;
0088: import org.eclipse.swt.events.DisposeEvent;
0089: import org.eclipse.swt.events.DisposeListener;
0090: import org.eclipse.swt.events.FocusEvent;
0091: import org.eclipse.swt.events.FocusListener;
0092: import org.eclipse.swt.events.SelectionAdapter;
0093: import org.eclipse.swt.events.SelectionEvent;
0094: import org.eclipse.swt.events.SelectionListener;
0095: import org.eclipse.swt.graphics.Image;
0096: import org.eclipse.swt.graphics.Point;
0097: import org.eclipse.swt.layout.GridData;
0098: import org.eclipse.swt.layout.GridLayout;
0099: import org.eclipse.swt.widgets.Button;
0100: import org.eclipse.swt.widgets.Combo;
0101: import org.eclipse.swt.widgets.Composite;
0102: import org.eclipse.swt.widgets.Control;
0103: import org.eclipse.swt.widgets.Display;
0104: import org.eclipse.swt.widgets.Label;
0105: import org.eclipse.swt.widgets.Menu;
0106: import org.eclipse.swt.widgets.MenuItem;
0107: import org.eclipse.swt.widgets.Text;
0108: import org.eclipse.swt.widgets.Tree;
0109: import org.eclipse.swt.widgets.TreeColumn;
0110: import org.eclipse.ui.IWorkbench;
0111: import org.eclipse.ui.IWorkbenchPreferencePage;
0112: import org.eclipse.ui.PlatformUI;
0113: import org.eclipse.ui.commands.ICommandService;
0114: import org.eclipse.ui.contexts.IContextService;
0115: import org.eclipse.ui.dialogs.FilteredTree;
0116: import org.eclipse.ui.internal.IWorkbenchHelpContextIds;
0117: import org.eclipse.ui.internal.WorkbenchPlugin;
0118: import org.eclipse.ui.internal.commands.ICommandImageService;
0119: import org.eclipse.ui.internal.misc.Policy;
0120: import org.eclipse.ui.internal.misc.StatusUtil;
0121: import org.eclipse.ui.internal.util.BundleUtility;
0122: import org.eclipse.ui.internal.util.Util;
0123: import org.eclipse.ui.keys.IBindingService;
0124: import org.eclipse.ui.statushandlers.StatusManager;
0125:
0126: /**
0127: * <p>
0128: * A preference page that is capable of displaying and editing the bindings
0129: * between commands and user input events. These are typically things like
0130: * keyboard shortcuts.
0131: * </p>
0132: * <p>
0133: * This preference page has four general types of methods. Create methods are
0134: * called when the page is first made visible. They are responsible for creating
0135: * all of the widgets, and laying them out within the preference page. Fill
0136: * methods populate the contents of the widgets that contain collections of data
0137: * from which items can be selected. The select methods respond to selection
0138: * events from the user, such as a button press or a table selection. The update
0139: * methods update the contents of various widgets based on the current state of
0140: * the user interface. For example, the command name label will always try to
0141: * match the current select in the binding table.
0142: * </p>
0143: *
0144: * @since 3.2
0145: */
0146: public final class NewKeysPreferencePage extends PreferencePage
0147: implements IWorkbenchPreferencePage {
0148:
0149: private static boolean DEBUG = Policy.DEBUG_KEY_BINDINGS;
0150:
0151: private static final String TRACING_COMPONENT = "NewKeysPref"; //$NON-NLS-1$
0152:
0153: /**
0154: * @since 3.3
0155: *
0156: */
0157: private final class ResortColumn extends SelectionAdapter {
0158: private final BindingComparator comparator;
0159: private final TreeColumn treeColumn;
0160: private final Tree tree;
0161: private final int column;
0162:
0163: /**
0164: * @param comparator
0165: * @param commandNameColumn
0166: * @param tree
0167: */
0168: private ResortColumn(BindingComparator comparator,
0169: TreeColumn treeColumn, Tree tree, int column) {
0170: this .comparator = comparator;
0171: this .treeColumn = treeColumn;
0172: this .tree = tree;
0173: this .column = column;
0174: }
0175:
0176: public void widgetSelected(SelectionEvent e) {
0177: if (comparator.getSortColumn() == column) {
0178: comparator.setAscending(!comparator.isAscending());
0179: }
0180: tree.setSortColumn(treeColumn);
0181: comparator.setSortColumn(column);
0182: tree.setSortDirection(comparator.isAscending() ? SWT.UP
0183: : SWT.DOWN);
0184: try {
0185: filteredTree.getViewer().getTree().setRedraw(false);
0186: filteredTree.getViewer().refresh();
0187: } finally {
0188: filteredTree.getViewer().getTree().setRedraw(true);
0189: }
0190: }
0191: }
0192:
0193: private class ObservableSetContentProvider implements
0194: ITreeContentProvider {
0195:
0196: private class KnownElementsSet extends ObservableSet {
0197:
0198: KnownElementsSet(Set wrappedSet) {
0199: super (SWTObservables.getRealm(Display.getDefault()),
0200: wrappedSet, Object.class);
0201: }
0202:
0203: void doFireDiff(Set added, Set removed) {
0204: fireSetChange(Diffs.createSetDiff(added, removed));
0205: }
0206:
0207: void doFireStale(boolean isStale) {
0208: if (isStale) {
0209: fireStale();
0210: } else {
0211: fireChange();
0212: }
0213: }
0214: }
0215:
0216: private IObservableSet readableSet;
0217:
0218: private Viewer viewer;
0219:
0220: /**
0221: * This readableSet returns the same elements as the input readableSet.
0222: * However, it only fires events AFTER the elements have been added or
0223: * removed from the viewer.
0224: */
0225: private KnownElementsSet knownElements;
0226:
0227: private ISetChangeListener listener = new ISetChangeListener() {
0228:
0229: public void handleSetChange(SetChangeEvent event) {
0230: boolean wasStale = knownElements.isStale();
0231: if (isDisposed()) {
0232: return;
0233: }
0234: doDiff(event.diff.getAdditions(), event.diff
0235: .getRemovals(), true);
0236: if (!wasStale && event.getObservableSet().isStale()) {
0237: knownElements.doFireStale(true);
0238: }
0239: }
0240: };
0241:
0242: private IStaleListener staleListener = new IStaleListener() {
0243: public void handleStale(StaleEvent event) {
0244: knownElements.doFireStale(event.getObservable()
0245: .isStale());
0246: }
0247: };
0248:
0249: /**
0250: *
0251: */
0252: public ObservableSetContentProvider() {
0253: readableSet = new ObservableSet(SWTObservables
0254: .getRealm(Display.getDefault()),
0255: Collections.EMPTY_SET, Object.class) {
0256: };
0257: knownElements = new KnownElementsSet(readableSet);
0258: }
0259:
0260: public void dispose() {
0261: setInput(null);
0262: }
0263:
0264: private void doDiff(Set added, Set removed, boolean updateViewer) {
0265: knownElements.doFireDiff(added, Collections.EMPTY_SET);
0266:
0267: if (updateViewer) {
0268: if (added.size() > 20 || removed.size() > 20) {
0269: viewer.refresh();
0270: } else {
0271: Object[] toAdd = added.toArray();
0272: if (viewer instanceof TreeViewer) {
0273: TreeViewer tv = (TreeViewer) viewer;
0274: tv.add(model, toAdd);
0275: } else if (viewer instanceof AbstractListViewer) {
0276: AbstractListViewer lv = (AbstractListViewer) viewer;
0277: lv.add(toAdd);
0278: }
0279: Object[] toRemove = removed.toArray();
0280: if (viewer instanceof TreeViewer) {
0281: TreeViewer tv = (TreeViewer) viewer;
0282: tv.remove(toRemove);
0283: } else if (viewer instanceof AbstractListViewer) {
0284: AbstractListViewer lv = (AbstractListViewer) viewer;
0285: lv.remove(toRemove);
0286: }
0287: }
0288: }
0289: knownElements.doFireDiff(Collections.EMPTY_SET, removed);
0290: }
0291:
0292: public Object[] getElements(Object inputElement) {
0293: return readableSet.toArray();
0294: }
0295:
0296: /**
0297: * Returns the readableSet of elements known to this content provider.
0298: * Items are added to this readableSet before being added to the viewer,
0299: * and they are removed after being removed from the viewer. The
0300: * readableSet is always updated after the viewer. This is intended for
0301: * use by label providers, as it will always return the items that need
0302: * labels.
0303: *
0304: * @return readableSet of items that will need labels
0305: */
0306: public IObservableSet getKnownElements() {
0307: return knownElements;
0308: }
0309:
0310: public void inputChanged(Viewer viewer, Object oldInput,
0311: Object newInput) {
0312: this .viewer = viewer;
0313:
0314: if (newInput != null
0315: && !(newInput instanceof IObservableSet)) {
0316: throw new IllegalArgumentException(
0317: "This content provider only works with input of type IReadableSet"); //$NON-NLS-1$
0318: }
0319:
0320: setInput((IObservableSet) newInput);
0321: }
0322:
0323: private boolean isDisposed() {
0324: return viewer.getControl() == null
0325: || viewer.getControl().isDisposed();
0326: }
0327:
0328: private void setInput(IObservableSet newSet) {
0329: boolean updateViewer = true;
0330: if (newSet == null) {
0331: newSet = new ObservableSet(SWTObservables
0332: .getRealm(Display.getDefault()),
0333: Collections.EMPTY_SET, Object.class) {
0334: };
0335: // don't update the viewer - its input is null
0336: updateViewer = false;
0337: }
0338:
0339: boolean wasStale = false;
0340: if (readableSet != null) {
0341: wasStale = readableSet.isStale();
0342: readableSet.removeSetChangeListener(listener);
0343: readableSet.removeStaleListener(staleListener);
0344: }
0345:
0346: HashSet additions = new HashSet();
0347: HashSet removals = new HashSet();
0348:
0349: additions.addAll(newSet);
0350: additions.removeAll(readableSet);
0351:
0352: removals.addAll(readableSet);
0353: removals.removeAll(newSet);
0354:
0355: readableSet = newSet;
0356:
0357: doDiff(additions, removals, updateViewer);
0358:
0359: if (readableSet != null) {
0360: readableSet.addSetChangeListener(listener);
0361: readableSet.addStaleListener(staleListener);
0362: }
0363:
0364: boolean isStale = (readableSet != null && readableSet
0365: .isStale());
0366: if (isStale != wasStale) {
0367: knownElements.doFireStale(isStale);
0368: }
0369: }
0370:
0371: /*
0372: * (non-Javadoc)
0373: *
0374: * @see org.eclipse.jface.viewers.ITreeContentProvider#getChildren(java.lang.Object)
0375: */
0376: public Object[] getChildren(Object parentElement) {
0377: // TODO Auto-generated method stub
0378: return null;
0379: }
0380:
0381: /*
0382: * (non-Javadoc)
0383: *
0384: * @see org.eclipse.jface.viewers.ITreeContentProvider#getParent(java.lang.Object)
0385: */
0386: public Object getParent(Object element) {
0387: // TODO Auto-generated method stub
0388: return null;
0389: }
0390:
0391: /*
0392: * (non-Javadoc)
0393: *
0394: * @see org.eclipse.jface.viewers.ITreeContentProvider#hasChildren(java.lang.Object)
0395: */
0396: public boolean hasChildren(Object element) {
0397: // TODO Auto-generated method stub
0398: return false;
0399: }
0400:
0401: }
0402:
0403: /**
0404: * A FilteredTree that provides a combo which is used to organize and
0405: * display elements in the tree according to the selected criteria.
0406: *
0407: */
0408: protected class CategoryFilterTree extends FilteredTree {
0409:
0410: private CategoryPatternFilter filter;
0411:
0412: /**
0413: * Constructor for PatternFilteredTree.
0414: *
0415: * @param parent
0416: * @param treeStyle
0417: * @param filter
0418: */
0419: protected CategoryFilterTree(Composite parent, int treeStyle,
0420: CategoryPatternFilter filter) {
0421: super (parent, treeStyle, filter);
0422: this .filter = filter;
0423: }
0424:
0425: public void filterCategories(boolean b) {
0426: filter.filterCategories(b);
0427: textChanged();
0428: }
0429:
0430: public boolean isFilteringCategories() {
0431: return filter.isFilteringCategories();
0432: }
0433: }
0434:
0435: /**
0436: * A label provider that simply extracts the command name and the formatted
0437: * trigger sequence from a given binding, and matches them to the correct
0438: * column.
0439: */
0440: private final class BindingLabelProvider extends LabelProvider
0441: implements ITableLabelProvider {
0442:
0443: /**
0444: * The index of the column containing the command name.
0445: */
0446: private static final int COLUMN_COMMAND = 0;
0447:
0448: /**
0449: * The index of the column containing the trigger sequence.
0450: */
0451: private static final int COLUMN_TRIGGER_SEQUENCE = 1;
0452:
0453: /**
0454: * The index of the column containing the trigger sequence.
0455: */
0456: private static final int COLUMN_WHEN = 2;
0457:
0458: /**
0459: * The index of the column containing the Category.
0460: */
0461: private static final int COLUMN_CATEGORY = 3;
0462:
0463: /**
0464: * The index of the column with the image for User binding
0465: */
0466: private static final int COLUMN_USER = 4;
0467:
0468: /**
0469: * A resource manager for this preference page.
0470: */
0471: private final LocalResourceManager localResourceManager = new LocalResourceManager(
0472: JFaceResources.getResources());
0473:
0474: public final void dispose() {
0475: super .dispose();
0476: localResourceManager.dispose();
0477: }
0478:
0479: public final Image getColumnImage(final Object element,
0480: final int columnIndex) {
0481: final Object value = element;
0482: if (value instanceof Binding) {
0483: switch (columnIndex) {
0484: case COLUMN_COMMAND:
0485: final ParameterizedCommand parameterizedCommand = ((Binding) value)
0486: .getParameterizedCommand();
0487: if (parameterizedCommand != null) {
0488: final String commandId = parameterizedCommand
0489: .getId();
0490: final ImageDescriptor imageDescriptor = commandImageService
0491: .getImageDescriptor(commandId);
0492: if (imageDescriptor == null) {
0493: return null;
0494: }
0495: try {
0496: return localResourceManager
0497: .createImage(imageDescriptor);
0498: } catch (final DeviceResourceException e) {
0499: final String message = "Problem retrieving image for a command '" //$NON-NLS-1$
0500: + commandId + '\'';
0501: final IStatus status = new Status(
0502: IStatus.ERROR,
0503: WorkbenchPlugin.PI_WORKBENCH, 0,
0504: message, e);
0505: WorkbenchPlugin.log(message, status);
0506: }
0507: }
0508: return null;
0509:
0510: case COLUMN_USER:
0511: if (((Binding) value).getType() == Binding.USER)
0512: return ImageFactory.getImage("change"); //$NON-NLS-1$
0513: return ImageFactory.getImage("blank"); //$NON-NLS-1$
0514: }
0515:
0516: } else if (value instanceof ParameterizedCommand) {
0517: switch (columnIndex) {
0518: case COLUMN_COMMAND:
0519: final ParameterizedCommand parameterizedCommand = (ParameterizedCommand) value;
0520: final String commandId = parameterizedCommand
0521: .getId();
0522: final ImageDescriptor imageDescriptor = commandImageService
0523: .getImageDescriptor(commandId);
0524: if (imageDescriptor == null) {
0525: return null;
0526: }
0527: try {
0528: return localResourceManager
0529: .createImage(imageDescriptor);
0530: } catch (final DeviceResourceException e) {
0531: final String message = "Problem retrieving image for a command '" //$NON-NLS-1$
0532: + commandId + '\'';
0533: final IStatus status = new Status(
0534: IStatus.ERROR,
0535: WorkbenchPlugin.PI_WORKBENCH, 0,
0536: message, e);
0537: WorkbenchPlugin.log(message, status);
0538: }
0539: return null;
0540: case COLUMN_USER:
0541: return ImageFactory.getImage("blank"); //$NON-NLS-1$
0542: }
0543:
0544: } else if ((value instanceof Category)
0545: || (value instanceof String)) {
0546: switch (columnIndex) {
0547: case COLUMN_COMMAND:
0548: final URL url = BundleUtility.find(
0549: PlatformUI.PLUGIN_ID,
0550: ICON_GROUP_OF_BINDINGS);
0551: final ImageDescriptor imageDescriptor = ImageDescriptor
0552: .createFromURL(url);
0553: try {
0554: return localResourceManager
0555: .createImage(imageDescriptor);
0556: } catch (final DeviceResourceException e) {
0557: final String message = "Problem retrieving image for groups of bindings: '" //$NON-NLS-1$
0558: + ICON_GROUP_OF_BINDINGS + '\'';
0559: final IStatus status = new Status(
0560: IStatus.ERROR,
0561: WorkbenchPlugin.PI_WORKBENCH, 0,
0562: message, e);
0563: WorkbenchPlugin.log(message, status);
0564: }
0565: }
0566:
0567: }
0568:
0569: return null;
0570: }
0571:
0572: private boolean checkConflict(Binding binding) {
0573: Collection matches = (Collection) localChangeManager
0574: .getActiveBindingsDisregardingContext().get(
0575: binding.getTriggerSequence());
0576: if (matches != null) {
0577: Iterator i = matches.iterator();
0578: while (i.hasNext()) {
0579: Binding b = (Binding) i.next();
0580: if (binding != b
0581: && b.getContextId().equals(
0582: binding.getContextId())
0583: && b.getSchemeId().equals(
0584: binding.getSchemeId())) {
0585: return true;
0586: }
0587: }
0588: }
0589: return false;
0590: }
0591:
0592: public final String getColumnText(final Object element,
0593: final int columnIndex) {
0594: final Object value = element;
0595: if (value instanceof Binding) {
0596: final Binding binding = (Binding) value;
0597: switch (columnIndex) {
0598: case COLUMN_COMMAND:
0599: try {
0600: return binding.getParameterizedCommand()
0601: .getName();
0602:
0603: } catch (final NotDefinedException e) {
0604: return NewKeysPreferenceMessages.Undefined_Command;
0605: }
0606: case COLUMN_TRIGGER_SEQUENCE:
0607: if (checkConflict(binding)) {
0608: return "*" + binding.getTriggerSequence().format(); //$NON-NLS-1$
0609: }
0610: return binding.getTriggerSequence().format();
0611:
0612: case COLUMN_WHEN:
0613: try {
0614: return contextService.getContext(
0615: binding.getContextId()).getName();
0616: } catch (NotDefinedException e1) {
0617: return NewKeysPreferenceMessages.Undefined_Context;
0618: }
0619: case COLUMN_CATEGORY:
0620: try {
0621: return binding.getParameterizedCommand()
0622: .getCommand().getCategory().getName();
0623: } catch (NotDefinedException e) {
0624: return NewKeysPreferenceMessages.Unavailable_Category;
0625: }
0626: default:
0627: return null;
0628: }
0629: } else if (value instanceof Category) {
0630: if (columnIndex == COLUMN_COMMAND) {
0631: try {
0632: return ((Category) value).getName();
0633: } catch (final NotDefinedException e) {
0634: return NewKeysPreferenceMessages.Unavailable_Category;
0635: }
0636: }
0637:
0638: return null;
0639:
0640: } else if (value instanceof String) {
0641: // This is a context.
0642: if (columnIndex == COLUMN_COMMAND) {
0643: try {
0644: return contextService
0645: .getContext((String) value).getName();
0646: } catch (final NotDefinedException e) {
0647: return NewKeysPreferenceMessages.Undefined_Context;
0648: }
0649: }
0650:
0651: return null;
0652: } else if (value instanceof ParameterizedCommand) {
0653: if (columnIndex == COLUMN_COMMAND) {
0654: try {
0655: return ((ParameterizedCommand) value).getName();
0656: } catch (final NotDefinedException e) {
0657: return NewKeysPreferenceMessages.Undefined_Command;
0658: }
0659: }
0660: if (columnIndex == COLUMN_TRIGGER_SEQUENCE)
0661: return ""; //$NON-NLS-1$
0662:
0663: if (columnIndex == COLUMN_WHEN)
0664: return ""; //$NON-NLS-1$
0665:
0666: if (columnIndex == COLUMN_CATEGORY) {
0667: try {
0668: return ((ParameterizedCommand) value)
0669: .getCommand().getCategory().getName();
0670: } catch (NotDefinedException e) {
0671: return NewKeysPreferenceMessages.Unavailable_Category;
0672: }
0673: }
0674: return null;
0675: }
0676:
0677: return null;
0678: }
0679:
0680: /*
0681: * (non-Javadoc)
0682: *
0683: * @see org.eclipse.jface.viewers.LabelProvider#getText(java.lang.Object)
0684: */
0685: public String getText(Object element) {
0686: String rc = getColumnText(element, 0);
0687: if (rc == null) {
0688: super .getText(element);
0689: }
0690: StringBuffer buf = new StringBuffer(rc);
0691: for (int i = 1; i < COLUMN_USER; i++) {
0692: String text = getColumnText(element, i);
0693: if (text != null) {
0694: buf.append(' ');
0695: buf.append(text);
0696: }
0697: }
0698: return buf.toString();
0699: }
0700: }
0701:
0702: /**
0703: * Sorts the bindings in the filtered tree based on the current grouping.
0704: */
0705: private final class BindingComparator extends ViewerComparator {
0706:
0707: private int sortColumn = 0;
0708:
0709: private int lastSortColumn = 0;
0710:
0711: private boolean ascending = true;
0712:
0713: public final int category(final Object element) {
0714: switch (grouping) {
0715: case GROUPING_CATEGORY:
0716: // TODO This has to be done with something other than the hash.
0717: try {
0718: final ParameterizedCommand command = (element instanceof ParameterizedCommand) ? (ParameterizedCommand) element
0719: : ((Binding) element)
0720: .getParameterizedCommand();
0721: return command.getCommand().getCategory()
0722: .hashCode();
0723: } catch (final NotDefinedException e) {
0724: return 0;
0725: }
0726: case GROUPING_CONTEXT:
0727: // TODO This has to be done with something other than the hash.
0728: if (element instanceof Binding) {
0729: return ((Binding) element).getContextId()
0730: .hashCode();
0731: }
0732: case GROUPING_NONE:
0733: default:
0734: return 0;
0735: }
0736: }
0737:
0738: public final int compare(final Viewer viewer, final Object a,
0739: final Object b) {
0740:
0741: int result = compareColumn(viewer, a, b, sortColumn);
0742: if (result == 0 && sortColumn != lastSortColumn) {
0743: result = compareColumn(viewer, a, b, lastSortColumn);
0744: }
0745: return ascending ? result : (-1) * result;
0746: }
0747:
0748: private int compareColumn(final Viewer viewer, final Object a,
0749: final Object b, final int columnNumber) {
0750: if (columnNumber == BindingLabelProvider.COLUMN_USER) {
0751: return sortUser(viewer, a, b);
0752: }
0753: IBaseLabelProvider baseLabel = ((TreeViewer) viewer)
0754: .getLabelProvider();
0755: if (baseLabel instanceof ITableLabelProvider) {
0756: ITableLabelProvider tableProvider = (ITableLabelProvider) baseLabel;
0757: String e1p = tableProvider.getColumnText(a,
0758: columnNumber);
0759: String e2p = tableProvider.getColumnText(b,
0760: columnNumber);
0761: if (e1p != null && e2p != null) {
0762: return getComparator().compare(e1p, e2p);
0763: }
0764: }
0765: return 0;
0766: }
0767:
0768: private int sortUser(final Viewer viewer, final Object a,
0769: final Object b) {
0770: int typeA = (a instanceof Binding ? ((Binding) a).getType()
0771: : Binding.SYSTEM);
0772: int typeB = (b instanceof Binding ? ((Binding) b).getType()
0773: : Binding.SYSTEM);
0774: int result = typeA - typeB;
0775: return result;
0776: }
0777:
0778: /**
0779: * @return Returns the sortColumn.
0780: */
0781: public int getSortColumn() {
0782: return sortColumn;
0783: }
0784:
0785: /**
0786: * @param sortColumn
0787: * The sortColumn to set.
0788: */
0789: public void setSortColumn(int sortColumn) {
0790: lastSortColumn = this .sortColumn;
0791: if (lastSortColumn != sortColumn) {
0792: ascending = true;
0793: }
0794: this .sortColumn = sortColumn;
0795: }
0796:
0797: /**
0798: * @return Returns the ascending.
0799: */
0800: public boolean isAscending() {
0801: return ascending;
0802: }
0803:
0804: /**
0805: * @param ascending
0806: * The ascending to set.
0807: */
0808: public void setAscending(boolean ascending) {
0809: this .ascending = ascending;
0810: }
0811: }
0812:
0813: /**
0814: * The constant value for <code>grouping</code> when the bindings should
0815: * be grouped by category.
0816: */
0817: private static final int GROUPING_CATEGORY = 0;
0818:
0819: /**
0820: * The constant value for <code>grouping</code> when the bindings should
0821: * be grouped by context.
0822: */
0823: private static final int GROUPING_CONTEXT = 1;
0824:
0825: /**
0826: * The constant value for <code>grouping</code> when the bindings should
0827: * not be grouped (i.e., they should be displayed in a flat list).
0828: */
0829: private static final int GROUPING_NONE = 2;
0830:
0831: /**
0832: * The path at which the icon for "groups of bindings" is located.
0833: */
0834: private static final String ICON_GROUP_OF_BINDINGS = "$nl$/icons/full/obj16/keygroups_obj.gif"; //$NON-NLS-1$
0835:
0836: private static final String CONTEXT_ID_ACTION_SETS = "org.eclipse.ui.contexts.actionSet"; //$NON-NLS-1$
0837:
0838: private static final String CONTEXT_ID_INTERNAL = ".internal."; //$NON-NLS-1$
0839:
0840: /**
0841: * The number of items to show in the bindings table tree.
0842: */
0843: private static final int ITEMS_TO_SHOW = 7;
0844:
0845: /**
0846: * A comparator that can be used for display of
0847: * <code>NamedHandleObject</code> instances to the end user.
0848: */
0849: private static final NamedHandleObjectComparator NAMED_HANDLE_OBJECT_COMPARATOR = new NamedHandleObjectComparator();
0850:
0851: public final static String TAG_DIALOG_SECTION = "org.eclipse.ui.preferences.keysPreferencePage"; //$NON-NLS-1$
0852:
0853: private final String TAG_FIELD = "showAllField"; //$NON-NLS-1$
0854:
0855: private static final String TAG_FILTER_ACTION_SETS = "actionSetFilter"; //$NON-NLS-1$
0856:
0857: private static final String TAG_FILTER_INTERNAL = "internalFilter"; //$NON-NLS-1$
0858:
0859: private static final String TAG_FILTER_UNCAT = "uncategorizedFilter"; //$NON-NLS-1$
0860:
0861: /**
0862: * Sorts the given array of <code>NamedHandleObject</code> instances based
0863: * on their name. This is generally useful if they will be displayed to an
0864: * end users.
0865: *
0866: * @param objects
0867: * The objects to be sorted; must not be <code>null</code>.
0868: * @return The same array, but sorted in place; never <code>null</code>.
0869: */
0870: private static final NamedHandleObject[] sortByName(
0871: final NamedHandleObject[] objects) {
0872: Arrays.sort(objects, NAMED_HANDLE_OBJECT_COMPARATOR);
0873: return objects;
0874: }
0875:
0876: /**
0877: * The workbench's binding service. This binding service is used to access
0878: * the current set of bindings, and to persist changes.
0879: */
0880: private IBindingService bindingService;
0881:
0882: /**
0883: * The text widget containing the key sequence. This value is
0884: * <code>null</code> until the controls are created.
0885: */
0886: private Text bindingText;
0887:
0888: /**
0889: * The workbench's command image service. This command image service is used
0890: * to provide an icon beside each command.
0891: */
0892: private ICommandImageService commandImageService;
0893:
0894: /**
0895: * The label containing the name of the currently selected binding's
0896: * command. This value is <code>null</code> until the controls are
0897: * created.
0898: */
0899: private Label commandNameValueLabel;
0900:
0901: /**
0902: * The workbench's command service. This command service is used to access
0903: * the list of commands.
0904: */
0905: private ICommandService commandService;
0906:
0907: /**
0908: * The workbench's context service. This context service is used to access
0909: * the list of contexts.
0910: */
0911: private IContextService contextService;
0912:
0913: /**
0914: * The label containing the description of the currently selected binding's
0915: * command. This value is <code>null</code> until the controls are
0916: * created.
0917: */
0918: private Text descriptionValueText;
0919:
0920: /**
0921: * The filtered tree containing the list of commands and bindings to edit.
0922: */
0923: private CategoryFilterTree filteredTree;
0924:
0925: private CategoryPatternFilter patternFilter;
0926:
0927: /**
0928: * The grouping for the bindings tree. Either there should be no group
0929: * (i.e., flat list), or the bindings should be grouped by either category
0930: * or context.
0931: */
0932: private int grouping = GROUPING_NONE;
0933:
0934: /**
0935: * The key sequence entry widget containing the trigger sequence for the
0936: * currently selected binding. This value is <code>null</code> until the
0937: * controls are created.
0938: */
0939: private KeySequenceText keySequenceText;
0940:
0941: /**
0942: * A binding manager local to this preference page. When the page is
0943: * initialized, the current bindings are read out from the binding service
0944: * and placed in this manager. This manager is then updated as the user
0945: * makes changes. When the user has finished, the contents of this manager
0946: * are compared with the contents of the binding service. The changes are
0947: * then persisted.
0948: */
0949: private BindingManager localChangeManager;
0950:
0951: /**
0952: * The context id of the binding which the user is trying to add. This value
0953: * is derived from the binding that is selected at the time the user tried
0954: * to add a binding. If this value is <code>null</code>, then the user is
0955: * not currently trying to add a binding to a command that already has a
0956: * binding.
0957: */
0958: private String markedContextId = null;
0959:
0960: /**
0961: * The parameterized command to which the user is currently trying to add a
0962: * binding. If this value is <code>null</code>, then the user is not
0963: * currently trying to add a binding to a command that already has a
0964: * binding.
0965: */
0966: private ParameterizedCommand markedParameterizedCommand = null;
0967:
0968: /**
0969: * The combo box containing the list of possible schemes to choose from.
0970: * This value is <code>null</code> until the contents are created.
0971: */
0972: private ComboViewer schemeCombo = null;
0973:
0974: /**
0975: * The check box controlling whether all commands should be shown in the
0976: * filtered tree. This value is <code>null</code> until the contents are
0977: * created.
0978: */
0979: private Button showAllCheckBox = null;
0980:
0981: private boolean filterActionSetContexts = true;
0982:
0983: private boolean filterInternalContexts = true;
0984:
0985: private IObservableSet commandModel;
0986:
0987: private IObservableSet bindingModel;
0988:
0989: private UnionSet model;
0990:
0991: /**
0992: * The combo box containing the list of possible contexts to choose from.
0993: * This value is <code>null</code> until the contents are create.
0994: */
0995: private ComboViewer whenCombo = null;
0996:
0997: /**
0998: * Adds a new binding based on an existing binding. The command and the
0999: * context are copied from the existing binding. The scheme id is set to be
1000: * the user's personal derivative scheme. The preference page is updated,
1001: * and focus is placed in the key sequence field.
1002: *
1003: * @param binding
1004: * The binding to be added; must not be <code>null</code>.
1005: */
1006: private final void bindingAdd(final Binding binding) {
1007: if (!(binding.getParameterizedCommand().getCommand()
1008: .isDefined()))
1009: return;
1010:
1011: // Remember the parameterized command and context.
1012: markedParameterizedCommand = binding.getParameterizedCommand();
1013: markedContextId = binding.getContextId();
1014:
1015: // Update the preference page.
1016: update();
1017:
1018: // Select the new binding.
1019: filteredTree.getViewer().setSelection(
1020: new StructuredSelection(binding
1021: .getParameterizedCommand()), true);
1022: bindingText.setFocus();
1023: bindingText.selectAll();
1024: }
1025:
1026: /**
1027: * Removes an existing binding. The preference page is then updated.
1028: *
1029: * @param binding
1030: * The binding to be removed; must not be <code>null</code>.
1031: */
1032: private final void bindingRemove(final KeyBinding binding) {
1033: ArrayList extraSystemDeletes = new ArrayList();
1034: final String contextId = binding.getContextId();
1035: final String schemeId = binding.getSchemeId();
1036: final KeySequence triggerSequence = binding.getKeySequence();
1037: if (binding.getType() == Binding.USER) {
1038: localChangeManager.removeBinding(binding);
1039: } else {
1040: // TODO This should be the user's personal scheme.
1041: Collection previousConflictMatches = (Collection) localChangeManager
1042: .getActiveBindingsDisregardingContext().get(
1043: binding.getTriggerSequence());
1044: KeyBinding deleteBinding = new KeyBinding(triggerSequence,
1045: null, schemeId, contextId, null, null, null,
1046: Binding.USER);
1047: localChangeManager.addBinding(deleteBinding);
1048: if (previousConflictMatches != null) {
1049: Iterator i = previousConflictMatches.iterator();
1050: while (i.hasNext()) {
1051: Binding b = (Binding) i.next();
1052: if (b != binding && deletes(deleteBinding, b)) {
1053: extraSystemDeletes.add(b);
1054: }
1055: }
1056: }
1057: }
1058:
1059: // update the model
1060: bindingModel.remove(binding);
1061: bindingAdd(binding);
1062: if (!extraSystemDeletes.isEmpty()) {
1063: Iterator i = extraSystemDeletes.iterator();
1064: while (i.hasNext()) {
1065: KeyBinding b = (KeyBinding) i.next();
1066: KeyBinding newBinding = new KeyBinding(b
1067: .getKeySequence(), b.getParameterizedCommand(),
1068: b.getSchemeId(), b.getContextId(), null, null,
1069: null, Binding.USER);
1070: localChangeManager.addBinding(newBinding);
1071:
1072: bindingModel.remove(b);
1073: bindingModel.add(newBinding);
1074: }
1075: }
1076: updateConflicts(binding);
1077: }
1078:
1079: private final void updateConflicts(final Collection bindings) {
1080: Iterator i = bindings.iterator();
1081: while (i.hasNext()) {
1082: final Binding b = (Binding) i.next();
1083: if (b.getParameterizedCommand() != null) {
1084: updateConflicts(b);
1085: }
1086: }
1087: }
1088:
1089: private final void updateConflicts(final Binding binding) {
1090: Collection matches = (Collection) localChangeManager
1091: .getActiveBindingsDisregardingContext().get(
1092: binding.getTriggerSequence());
1093: if (matches != null) {
1094: Iterator i = matches.iterator();
1095: while (i.hasNext()) {
1096: Binding b = (Binding) i.next();
1097: if (binding != b
1098: && b.getContextId().equals(
1099: binding.getContextId())) {
1100: filteredTree.getViewer().update(b, null);
1101: }
1102: }
1103: }
1104: }
1105:
1106: private final void bindingRestore(final KeyBinding binding) {
1107: final ParameterizedCommand cmd = binding
1108: .getParameterizedCommand();
1109: bindingRestore(cmd, false);
1110: }
1111:
1112: private String locale = Locale.getDefault().toString();
1113:
1114: private boolean localMatches(String l) {
1115: if (l == null) {
1116: return true;
1117: }
1118: return Util.equals(locale, l);
1119: }
1120:
1121: private String platform = SWT.getPlatform();
1122:
1123: private boolean platformMatches(String p) {
1124: if (p == null) {
1125: return true;
1126: }
1127: return Util.equals(platform, p);
1128: }
1129:
1130: private final String[] getSchemeIds(String schemeId) {
1131: final List strings = new ArrayList();
1132: while (schemeId != null) {
1133: strings.add(schemeId);
1134: try {
1135: schemeId = bindingService.getScheme(schemeId)
1136: .getParentId();
1137: } catch (final NotDefinedException e) {
1138: return new String[0];
1139: }
1140: }
1141:
1142: return (String[]) strings.toArray(new String[strings.size()]);
1143: }
1144:
1145: private final void bindingRestore(final ParameterizedCommand cmd,
1146: boolean removeCmd) {
1147: Set addSystemAll = new HashSet();
1148: ArrayList removeUser = new ArrayList();
1149: ArrayList removeBinding = new ArrayList();
1150: Binding[] bindings = localChangeManager.getBindings();
1151: for (int i = 0; i < bindings.length; i++) {
1152: final Binding b = bindings[i];
1153: if (b.getParameterizedCommand() == null
1154: && localMatches(b.getLocale())
1155: && platformMatches(b.getPlatform())) {
1156: // flat out, a delete marker
1157: removeBinding.add(b);
1158: } else if (cmd.equals(b.getParameterizedCommand())) {
1159: if (b.getType() == Binding.SYSTEM
1160: && localMatches(b.getLocale())
1161: && platformMatches(b.getPlatform())) {
1162: // a system binding for this command
1163: addSystemAll.add(b);
1164: } else if (b.getType() == Binding.USER) {
1165: // a user binding for this command
1166: removeUser.add(b);
1167: localChangeManager.removeBinding(b);
1168: }
1169: }
1170: }
1171:
1172: if (!addSystemAll.isEmpty()) {
1173: String[] activeSchemeIds = getSchemeIds(getSchemeId());
1174: Binding[] sysArray = (Binding[]) addSystemAll
1175: .toArray(new Binding[addSystemAll.size()]);
1176: for (int k = 0; k < sysArray.length; k++) {
1177: Binding sys = sysArray[k];
1178: boolean deleted = false;
1179: for (Iterator i = removeBinding.iterator(); i.hasNext();) {
1180: Binding del = (Binding) i.next();
1181: if (deletes(del, sys)) {
1182: if (del.getType() == Binding.USER) {
1183: removeUser.add(del);
1184: localChangeManager.removeBinding(del);
1185: } else {
1186: deleted = true;
1187: addSystemAll.remove(sys);
1188: }
1189: }
1190: }
1191: // Check the scheme ids.
1192: final String schemeId = sys.getSchemeId();
1193: boolean found = false;
1194: if (activeSchemeIds != null && !deleted) {
1195: for (int j = 0; j < activeSchemeIds.length; j++) {
1196: if (Util.equals(schemeId, activeSchemeIds[j])) {
1197: found = true;
1198: break;
1199: }
1200: }
1201: }
1202: if (!found && sys.getType() == Binding.SYSTEM) {
1203: addSystemAll.remove(sys);
1204: }
1205: }
1206: }
1207:
1208: bindingModel.addAll(addSystemAll);
1209: bindingModel.removeAll(removeUser);
1210: updateConflicts(addSystemAll);
1211: updateConflicts(removeUser);
1212: if (addSystemAll.isEmpty()) {
1213: commandModel.add(cmd);
1214: filteredTree.getViewer().setSelection(
1215: new StructuredSelection(cmd), true);
1216: } else if (removeCmd) {
1217: commandModel.remove(cmd);
1218: }
1219: if (!addSystemAll.isEmpty()) {
1220: // Select the new binding.
1221: filteredTree.getViewer().setSelection(
1222: new StructuredSelection(addSystemAll.iterator()
1223: .next()), true);
1224: }
1225:
1226: update();
1227: }
1228:
1229: final static boolean deletes(final Binding del,
1230: final Binding binding) {
1231: boolean deletes = true;
1232: deletes &= Util.equals(del.getContextId(), binding
1233: .getContextId());
1234: deletes &= Util.equals(del.getTriggerSequence(), binding
1235: .getTriggerSequence());
1236: if (del.getLocale() != null) {
1237: deletes &= Util
1238: .equals(del.getLocale(), binding.getLocale());
1239: }
1240: if (del.getPlatform() != null) {
1241: deletes &= Util.equals(del.getPlatform(), binding
1242: .getPlatform());
1243: }
1244: deletes &= (binding.getType() == Binding.SYSTEM);
1245: deletes &= Util.equals(del.getParameterizedCommand(), null);
1246:
1247: return deletes;
1248: }
1249:
1250: /**
1251: * Creates the button bar across the bottom of the preference page. This
1252: * button bar contains the "Advanced..." button.
1253: *
1254: * @param parent
1255: * The composite in which the button bar should be placed; never
1256: * <code>null</code>.
1257: * @return The button bar composite; never <code>null</code>.
1258: */
1259: private final Control createButtonBar(final Composite parent) {
1260: GridLayout layout;
1261: GridData gridData;
1262: int widthHint;
1263:
1264: // Create the composite to house the button bar.
1265: final Composite buttonBar = new Composite(parent, SWT.NONE);
1266: layout = new GridLayout(1, false);
1267: layout.marginWidth = 0;
1268: buttonBar.setLayout(layout);
1269: gridData = new GridData();
1270: gridData.horizontalAlignment = SWT.END;
1271: buttonBar.setLayoutData(gridData);
1272:
1273: // Advanced button.
1274: final Button advancedButton = new Button(buttonBar, SWT.PUSH);
1275: gridData = new GridData();
1276: widthHint = convertHorizontalDLUsToPixels(IDialogConstants.BUTTON_WIDTH);
1277: advancedButton
1278: .setText(NewKeysPreferenceMessages.AdvancedButton_Text);
1279: gridData.widthHint = Math.max(widthHint, advancedButton
1280: .computeSize(SWT.DEFAULT, SWT.DEFAULT, true).x) + 5;
1281: advancedButton.setLayoutData(gridData);
1282: advancedButton.addSelectionListener(new SelectionListener() {
1283: public void widgetDefaultSelected(SelectionEvent e) {
1284: }
1285:
1286: public void widgetSelected(SelectionEvent e) {
1287: KeysPreferenceFiltersDialog dialog = new KeysPreferenceFiltersDialog(
1288: getShell());
1289: dialog.setFilterActionSet(filterActionSetContexts);
1290: dialog.setFilterInternal(filterInternalContexts);
1291: dialog.setFilterUncategorized(filteredTree
1292: .isFilteringCategories());
1293: if (dialog.open() == Window.OK) {
1294: filterActionSetContexts = dialog
1295: .getFilterActionSet();
1296: filterInternalContexts = dialog.getFilterInternal();
1297: filteredTree.filterCategories(dialog
1298: .getFilterUncategorized());
1299: whenCombo.setInput(getContexts());
1300: updateDataControls();
1301: }
1302: }
1303: });
1304: return buttonBar;
1305: }
1306:
1307: /*
1308: * (non-Javadoc)
1309: *
1310: * @see org.eclipse.jface.preference.PreferencePage#createContents(org.eclipse.swt.widgets.Composite)
1311: */
1312: protected final Control createContents(final Composite parent) {
1313: PlatformUI.getWorkbench().getHelpSystem().setHelp(parent,
1314: IWorkbenchHelpContextIds.KEYS_PREFERENCE_PAGE);
1315:
1316: GridLayout layout = null;
1317:
1318: long startTime = 0L;
1319: if (DEBUG) {
1320: startTime = System.currentTimeMillis();
1321: }
1322:
1323: IDialogSettings settings = getDialogSettings();
1324: if (settings.get(TAG_FILTER_ACTION_SETS) != null) {
1325: filterActionSetContexts = settings
1326: .getBoolean(TAG_FILTER_ACTION_SETS);
1327: }
1328: if (settings.get(TAG_FILTER_INTERNAL) != null) {
1329: filterInternalContexts = settings
1330: .getBoolean(TAG_FILTER_INTERNAL);
1331: }
1332: patternFilter = new CategoryPatternFilter(true, commandService
1333: .getCategory(null));
1334: if (settings.get(TAG_FILTER_UNCAT) != null) {
1335: patternFilter.filterCategories(settings
1336: .getBoolean(TAG_FILTER_UNCAT));
1337: }
1338:
1339: // Creates a composite to hold all of the page contents.
1340: final Composite page = new Composite(parent, SWT.NONE);
1341: layout = new GridLayout(1, false);
1342: layout.marginWidth = 0;
1343: page.setLayout(layout);
1344:
1345: createSchemeControls(page);
1346: createTree(page);
1347: createTreeControls(page);
1348: createDataControls(page);
1349: createButtonBar(page);
1350:
1351: fill();
1352: update();
1353:
1354: applyDialogFont(page);
1355:
1356: if (DEBUG) {
1357: final long elapsedTime = System.currentTimeMillis()
1358: - startTime;
1359: Tracing.printTrace(TRACING_COMPONENT, "Created page in " //$NON-NLS-1$
1360: + elapsedTime + "ms"); //$NON-NLS-1$
1361: }
1362:
1363: return page;
1364: }
1365:
1366: private final Control createDataControls(final Composite parent) {
1367: GridLayout layout;
1368: GridData gridData;
1369:
1370: // Creates the data area.
1371: final Composite dataArea = new Composite(parent, SWT.NONE);
1372: layout = new GridLayout(2, true);
1373: layout.marginWidth = 0;
1374: dataArea.setLayout(layout);
1375: gridData = new GridData();
1376: gridData.grabExcessHorizontalSpace = true;
1377: gridData.horizontalAlignment = SWT.FILL;
1378: dataArea.setLayoutData(gridData);
1379:
1380: // LEFT DATA AREA
1381: // Creates the left data area.
1382: final Composite leftDataArea = new Composite(dataArea, SWT.NONE);
1383: layout = new GridLayout(3, false);
1384: leftDataArea.setLayout(layout);
1385: gridData = new GridData();
1386: gridData.grabExcessHorizontalSpace = true;
1387: gridData.verticalAlignment = SWT.TOP;
1388: gridData.horizontalAlignment = SWT.FILL;
1389: leftDataArea.setLayoutData(gridData);
1390:
1391: // The command name label.
1392: final Label commandNameLabel = new Label(leftDataArea, SWT.NONE);
1393: commandNameLabel
1394: .setText(NewKeysPreferenceMessages.CommandNameLabel_Text);
1395:
1396: // The current command name.
1397: commandNameValueLabel = new Label(leftDataArea, SWT.NONE);
1398: gridData = new GridData();
1399: gridData.grabExcessHorizontalSpace = true;
1400: gridData.horizontalSpan = 2;
1401: gridData.horizontalAlignment = SWT.FILL;
1402: commandNameValueLabel.setLayoutData(gridData);
1403:
1404: // The binding label.
1405: final Label bindingLabel = new Label(leftDataArea, SWT.NONE);
1406: bindingLabel
1407: .setText(NewKeysPreferenceMessages.BindingLabel_Text);
1408:
1409: // The key sequence entry widget.
1410: bindingText = new Text(leftDataArea, SWT.BORDER);
1411: gridData = new GridData();
1412: gridData.grabExcessHorizontalSpace = true;
1413: gridData.horizontalAlignment = SWT.FILL;
1414: gridData.widthHint = 200;
1415: bindingText.setLayoutData(gridData);
1416:
1417: bindingText.addFocusListener(new FocusListener() {
1418: public void focusGained(FocusEvent e) {
1419: bindingService.setKeyFilterEnabled(false);
1420: }
1421:
1422: public void focusLost(FocusEvent e) {
1423: bindingService.setKeyFilterEnabled(true);
1424: }
1425: });
1426: bindingText.addDisposeListener(new DisposeListener() {
1427: public void widgetDisposed(DisposeEvent e) {
1428: if (!bindingService.isKeyFilterEnabled()) {
1429: bindingService.setKeyFilterEnabled(true);
1430: }
1431: }
1432: });
1433:
1434: keySequenceText = new KeySequenceText(bindingText);
1435: keySequenceText.setKeyStrokeLimit(4);
1436: keySequenceText
1437: .addPropertyChangeListener(new IPropertyChangeListener() {
1438: public final void propertyChange(
1439: final PropertyChangeEvent event) {
1440: if (!event.getOldValue().equals(
1441: event.getNewValue())) {
1442: keySequenceChanged();
1443: }
1444: }
1445: });
1446:
1447: // Button for adding trapped key strokes
1448: final Button addKeyButton = new Button(leftDataArea, SWT.LEFT
1449: | SWT.ARROW);
1450: addKeyButton
1451: .setToolTipText(NewKeysPreferenceMessages.AddKeyButton_ToolTipText);
1452: gridData = new GridData();
1453: gridData.heightHint = schemeCombo.getCombo().getTextHeight();
1454: addKeyButton.setLayoutData(gridData);
1455:
1456: // Arrow buttons aren't normally added to the tab list. Let's fix that.
1457: final Control[] tabStops = dataArea.getTabList();
1458: final ArrayList newTabStops = new ArrayList();
1459: for (int i = 0; i < tabStops.length; i++) {
1460: Control tabStop = tabStops[i];
1461: newTabStops.add(tabStop);
1462: if (bindingText.equals(tabStop)) {
1463: newTabStops.add(addKeyButton);
1464: }
1465: }
1466: final Control[] newTabStopArray = (Control[]) newTabStops
1467: .toArray(new Control[newTabStops.size()]);
1468: dataArea.setTabList(newTabStopArray);
1469:
1470: // Construct the menu to attach to the above button.
1471: final Menu addKeyMenu = new Menu(addKeyButton);
1472: final Iterator trappedKeyItr = KeySequenceText.TRAPPED_KEYS
1473: .iterator();
1474: while (trappedKeyItr.hasNext()) {
1475: final KeyStroke trappedKey = (KeyStroke) trappedKeyItr
1476: .next();
1477: final MenuItem menuItem = new MenuItem(addKeyMenu, SWT.PUSH);
1478: menuItem.setText(trappedKey.format());
1479: menuItem.addSelectionListener(new SelectionAdapter() {
1480:
1481: public void widgetSelected(SelectionEvent e) {
1482: keySequenceText.insert(trappedKey);
1483: bindingText.setFocus();
1484: bindingText
1485: .setSelection(bindingText.getTextLimit());
1486: }
1487: });
1488: }
1489: addKeyButton.addSelectionListener(new SelectionAdapter() {
1490:
1491: public void widgetSelected(SelectionEvent selectionEvent) {
1492: Point buttonLocation = addKeyButton.getLocation();
1493: buttonLocation = dataArea.toDisplay(buttonLocation.x,
1494: buttonLocation.y);
1495: Point buttonSize = addKeyButton.getSize();
1496: addKeyMenu.setLocation(buttonLocation.x,
1497: buttonLocation.y + buttonSize.y);
1498: addKeyMenu.setVisible(true);
1499: }
1500: });
1501:
1502: final IObservableValue selection = ViewersObservables
1503: .observeSingleSelection(filteredTree.getViewer());
1504:
1505: // The when label.
1506: final Label whenLabel = new Label(leftDataArea, SWT.NONE);
1507: whenLabel.setText(NewKeysPreferenceMessages.WhenLabel_Text);
1508:
1509: // The when combo.
1510: whenCombo = new ComboViewer(leftDataArea);
1511: gridData = new GridData();
1512: gridData.grabExcessHorizontalSpace = true;
1513: gridData.horizontalAlignment = SWT.FILL;
1514: gridData.horizontalSpan = 2;
1515: whenCombo.getCombo().setLayoutData(gridData);
1516: whenCombo
1517: .setLabelProvider(new NamedHandleObjectLabelProvider());
1518: whenCombo.setContentProvider(new ArrayContentProvider());
1519: whenCombo.setComparator(new ViewerComparator());
1520: whenCombo
1521: .addPostSelectionChangedListener(new ISelectionChangedListener() {
1522:
1523: public void selectionChanged(
1524: SelectionChangedEvent event) {
1525: updateWhenCombo();
1526: }
1527: });
1528:
1529: whenCombo.getCombo().setVisibleItemCount(20);
1530: whenCombo.getCombo().setVisible(false);
1531: whenLabel.setVisible(false);
1532: selection.addValueChangeListener(new IValueChangeListener() {
1533:
1534: public void handleValueChange(ValueChangeEvent event) {
1535: boolean visible = false;
1536: if (selection.getValue() instanceof KeyBinding) {
1537: visible = true;
1538: }
1539: Combo combo = whenCombo.getCombo();
1540: if (!combo.isDisposed()) {
1541: combo.setVisible(visible);
1542: }
1543: if (!whenLabel.isDisposed()) {
1544: whenLabel.setVisible(visible);
1545: }
1546: }
1547: });
1548:
1549: final Label asterisk = new Label(leftDataArea, SWT.NONE);
1550: asterisk.setText(NewKeysPreferenceMessages.Asterisk_Text);
1551: gridData = new GridData();
1552: gridData.grabExcessHorizontalSpace = true;
1553: gridData.horizontalSpan = 2;
1554: gridData.horizontalAlignment = SWT.FILL;
1555: asterisk.setLayoutData(gridData);
1556:
1557: // RIGHT DATA AREA
1558: // Creates the right data area.
1559: final Composite rightDataArea = new Composite(dataArea,
1560: SWT.NONE);
1561: layout = new GridLayout(1, false);
1562: rightDataArea.setLayout(layout);
1563: gridData = new GridData(SWT.FILL, SWT.FILL, true, true);
1564: rightDataArea.setLayoutData(gridData);
1565:
1566: // The description label.
1567: final Label descriptionLabel = new Label(rightDataArea,
1568: SWT.NONE);
1569: descriptionLabel
1570: .setText(NewKeysPreferenceMessages.DescriptionLabel_Text);
1571: gridData = new GridData();
1572: gridData.grabExcessHorizontalSpace = true;
1573: gridData.horizontalAlignment = SWT.FILL;
1574: descriptionLabel.setLayoutData(gridData);
1575:
1576: // The description value.
1577: descriptionValueText = new Text(rightDataArea, SWT.BORDER
1578: | SWT.MULTI | SWT.READ_ONLY | SWT.WRAP | SWT.V_SCROLL
1579: | SWT.H_SCROLL);
1580: gridData = new GridData(SWT.FILL, SWT.FILL, true, true);
1581: gridData.horizontalIndent = 20;
1582: descriptionValueText.setLayoutData(gridData);
1583: return dataArea;
1584: }
1585:
1586: private final Control createSchemeControls(final Composite parent) {
1587: GridLayout layout;
1588: GridData gridData;
1589:
1590: // Create a composite to hold the controls.
1591: final Composite schemeControls = new Composite(parent, SWT.NONE);
1592: layout = new GridLayout(3, false);
1593: layout.marginWidth = 0;
1594: schemeControls.setLayout(layout);
1595: gridData = new GridData();
1596: gridData.grabExcessHorizontalSpace = true;
1597: gridData.horizontalAlignment = SWT.FILL;
1598: schemeControls.setLayoutData(gridData);
1599:
1600: // Create the label.
1601: final Label schemeLabel = new Label(schemeControls, SWT.NONE);
1602: schemeLabel.setText(NewKeysPreferenceMessages.SchemeLabel_Text);
1603:
1604: // Create the combo.
1605: schemeCombo = new ComboViewer(schemeControls);
1606: schemeCombo
1607: .setLabelProvider(new NamedHandleObjectLabelProvider());
1608: schemeCombo.setContentProvider(new ArrayContentProvider());
1609: gridData = new GridData();
1610: gridData.widthHint = 150;
1611: gridData.horizontalAlignment = SWT.FILL;
1612: schemeCombo.getCombo().setLayoutData(gridData);
1613: schemeCombo
1614: .addSelectionChangedListener(new ISelectionChangedListener() {
1615: public final void selectionChanged(
1616: final SelectionChangedEvent event) {
1617: selectSchemeCombo(event);
1618: }
1619: });
1620:
1621: return schemeControls;
1622: }
1623:
1624: private final Control createTree(final Composite parent) {
1625: GridData gridData;
1626:
1627: filteredTree = new CategoryFilterTree(parent, SWT.SINGLE
1628: | SWT.FULL_SELECTION | SWT.BORDER, patternFilter);
1629: final GridLayout layout = new GridLayout(1, false);
1630: layout.marginWidth = 0;
1631: filteredTree.setLayout(layout);
1632: gridData = new GridData();
1633: gridData.grabExcessHorizontalSpace = true;
1634: gridData.grabExcessVerticalSpace = true;
1635: gridData.horizontalAlignment = SWT.FILL;
1636: gridData.verticalAlignment = SWT.FILL;
1637: filteredTree.setLayoutData(gridData);
1638:
1639: // Make sure the filtered tree has a height of ITEMS_TO_SHOW
1640: final Tree tree = filteredTree.getViewer().getTree();
1641: tree.setHeaderVisible(true);
1642: final Object layoutData = tree.getLayoutData();
1643: if (layoutData instanceof GridData) {
1644: gridData = (GridData) layoutData;
1645: final int itemHeight = tree.getItemHeight();
1646: if (itemHeight > 1) {
1647: gridData.heightHint = ITEMS_TO_SHOW * itemHeight;
1648: }
1649: }
1650:
1651: final BindingComparator comparator = new BindingComparator();
1652: comparator.setSortColumn(0);
1653:
1654: // Create the columns for the tree.
1655:
1656: final TreeColumn commandNameColumn = new TreeColumn(tree,
1657: SWT.LEFT, BindingLabelProvider.COLUMN_COMMAND);
1658: commandNameColumn
1659: .setText(NewKeysPreferenceMessages.CommandNameColumn_Text);
1660: tree.setSortColumn(commandNameColumn);
1661: tree.setSortDirection(comparator.isAscending() ? SWT.UP
1662: : SWT.DOWN);
1663: commandNameColumn.addSelectionListener(new ResortColumn(
1664: comparator, commandNameColumn, tree,
1665: BindingLabelProvider.COLUMN_COMMAND));
1666:
1667: final TreeColumn triggerSequenceColumn = new TreeColumn(tree,
1668: SWT.LEFT, BindingLabelProvider.COLUMN_TRIGGER_SEQUENCE);
1669: triggerSequenceColumn
1670: .setText(NewKeysPreferenceMessages.TriggerSequenceColumn_Text);
1671: triggerSequenceColumn.addSelectionListener(new ResortColumn(
1672: comparator, triggerSequenceColumn, tree,
1673: BindingLabelProvider.COLUMN_TRIGGER_SEQUENCE));
1674:
1675: final TreeColumn whenColumn = new TreeColumn(tree, SWT.LEFT,
1676: BindingLabelProvider.COLUMN_WHEN);
1677: whenColumn.setText(NewKeysPreferenceMessages.WhenColumn_Text);
1678: whenColumn.addSelectionListener(new ResortColumn(comparator,
1679: whenColumn, tree, BindingLabelProvider.COLUMN_WHEN));
1680:
1681: final TreeColumn categoryColumn = new TreeColumn(tree,
1682: SWT.LEFT, BindingLabelProvider.COLUMN_CATEGORY);
1683: categoryColumn
1684: .setText(NewKeysPreferenceMessages.CategoryColumn_Text);
1685: categoryColumn.addSelectionListener(new ResortColumn(
1686: comparator, categoryColumn, tree,
1687: BindingLabelProvider.COLUMN_CATEGORY));
1688:
1689: final TreeColumn userMarker = new TreeColumn(tree, SWT.LEFT,
1690: BindingLabelProvider.COLUMN_USER);
1691: userMarker.setText(NewKeysPreferenceMessages.UserColumn_Text);
1692: userMarker.addSelectionListener(new ResortColumn(comparator,
1693: userMarker, tree, BindingLabelProvider.COLUMN_USER));
1694:
1695: // Set up the providers for the viewer.
1696: final TreeViewer viewer = filteredTree.getViewer();
1697: viewer.setLabelProvider(new BindingLabelProvider());
1698:
1699: viewer.setContentProvider(new ObservableSetContentProvider());
1700:
1701: viewer.setComparator(comparator);
1702:
1703: /*
1704: * Listen for selection changes so that the data controls can be
1705: * updated.
1706: */
1707: viewer
1708: .addSelectionChangedListener(new ISelectionChangedListener() {
1709: public final void selectionChanged(
1710: final SelectionChangedEvent event) {
1711: selectTreeRow(event);
1712: }
1713: });
1714:
1715: // Adjust how the filter works.
1716: filteredTree.getPatternFilter().setIncludeLeadingWildcard(true);
1717: return filteredTree;
1718: }
1719:
1720: private final Control createTreeControls(final Composite parent) {
1721: GridLayout layout;
1722: GridData gridData;
1723: int widthHint;
1724:
1725: // Creates controls related to the tree.
1726: final Composite treeControls = new Composite(parent, SWT.NONE);
1727: layout = new GridLayout(4, false);
1728: layout.marginWidth = 0;
1729: treeControls.setLayout(layout);
1730: gridData = new GridData();
1731: gridData.grabExcessHorizontalSpace = true;
1732: gridData.horizontalAlignment = SWT.FILL;
1733: treeControls.setLayoutData(gridData);
1734:
1735: // Create the show all check box.
1736: showAllCheckBox = new Button(treeControls, SWT.CHECK);
1737: gridData = new GridData();
1738: gridData.grabExcessHorizontalSpace = true;
1739: gridData.horizontalAlignment = SWT.FILL;
1740: gridData.verticalAlignment = SWT.TOP;
1741: showAllCheckBox.setLayoutData(gridData);
1742: showAllCheckBox
1743: .setText(NewKeysPreferenceMessages.ShowAllCheckBox_Text);
1744: IDialogSettings settings = getDialogSettings();
1745: showAllCheckBox.setSelection(settings.getBoolean(TAG_FIELD));
1746: showAllCheckBox.addSelectionListener(new SelectionAdapter() {
1747: public void widgetSelected(SelectionEvent e) {
1748: updateShowAll();
1749: }
1750: });
1751:
1752: final IObservableValue selection = ViewersObservables
1753: .observeSingleSelection(filteredTree.getViewer());
1754:
1755: // Create the delete binding button.
1756: final Button addBindingButton = new Button(treeControls,
1757: SWT.PUSH);
1758: gridData = new GridData();
1759: widthHint = convertHorizontalDLUsToPixels(IDialogConstants.BUTTON_WIDTH);
1760: addBindingButton
1761: .setText(NewKeysPreferenceMessages.AddBindingButton_Text);
1762: gridData.widthHint = Math.max(widthHint, addBindingButton
1763: .computeSize(SWT.DEFAULT, SWT.DEFAULT, true).x) + 5;
1764: addBindingButton.setLayoutData(gridData);
1765: addBindingButton.addSelectionListener(new SelectionAdapter() {
1766: public final void widgetSelected(final SelectionEvent event) {
1767: selectAddBindingButton(event);
1768: }
1769: });
1770: new ControlUpdater(addBindingButton) {
1771: protected void updateControl() {
1772: Object selectedObject = selection.getValue();
1773: addBindingButton
1774: .setEnabled(selectedObject instanceof KeyBinding);
1775: }
1776: };
1777:
1778: // Create the delete binding button.
1779: final Button removeBindingButton = new Button(treeControls,
1780: SWT.PUSH);
1781: gridData = new GridData();
1782: widthHint = convertHorizontalDLUsToPixels(IDialogConstants.BUTTON_WIDTH);
1783: removeBindingButton
1784: .setText(NewKeysPreferenceMessages.RemoveBindingButton_Text);
1785: gridData.widthHint = Math.max(widthHint, removeBindingButton
1786: .computeSize(SWT.DEFAULT, SWT.DEFAULT, true).x) + 5;
1787: removeBindingButton.setLayoutData(gridData);
1788: removeBindingButton
1789: .addSelectionListener(new SelectionAdapter() {
1790: public final void widgetSelected(
1791: final SelectionEvent event) {
1792: selectRemoveBindingButton(event);
1793: }
1794: });
1795: new ControlUpdater(removeBindingButton) {
1796: protected void updateControl() {
1797: Object selectedObject = selection.getValue();
1798: removeBindingButton
1799: .setEnabled(selectedObject instanceof KeyBinding);
1800: }
1801: };
1802:
1803: // Create the delete binding button.
1804: final Button restore = new Button(treeControls, SWT.PUSH);
1805: gridData = new GridData();
1806: widthHint = convertHorizontalDLUsToPixels(IDialogConstants.BUTTON_WIDTH);
1807: restore
1808: .setText(NewKeysPreferenceMessages.RestoreBindingButton_Text);
1809: gridData.widthHint = Math.max(widthHint, restore.computeSize(
1810: SWT.DEFAULT, SWT.DEFAULT, true).x) + 5;
1811: restore.setLayoutData(gridData);
1812: restore.addSelectionListener(new SelectionAdapter() {
1813: public final void widgetSelected(final SelectionEvent event) {
1814: selectRestoreBindingButton(event);
1815: }
1816: });
1817:
1818: return treeControls;
1819: }
1820:
1821: private void updateShowAll() {
1822: BusyIndicator.showWhile(filteredTree.getViewer().getTree()
1823: .getDisplay(), new Runnable() {
1824: public void run() {
1825: try {
1826: filteredTree.getViewer().getTree().setRedraw(false);
1827: fillInCommands();
1828: } finally {
1829: filteredTree.getViewer().getTree().setRedraw(true);
1830: }
1831: }
1832: });
1833: }
1834:
1835: /**
1836: * Copies all of the information from the workbench into a local change
1837: * manager, and then the local change manager is used to populate the
1838: * contents of the various widgets on the page.
1839: *
1840: * The widgets affected by this method are: scheme combo, bindings
1841: * table/tree model, and the when combo.
1842: */
1843: private final void fill() {
1844: // Make an internal binding manager to track changes.
1845: localChangeManager = new BindingManager(new ContextManager(),
1846: new CommandManager());
1847: final Scheme[] definedSchemes = bindingService
1848: .getDefinedSchemes();
1849: try {
1850: for (int i = 0; i < definedSchemes.length; i++) {
1851: final Scheme scheme = definedSchemes[i];
1852: final Scheme copy = localChangeManager.getScheme(scheme
1853: .getId());
1854: copy.define(scheme.getName(), scheme.getDescription(),
1855: scheme.getParentId());
1856: }
1857: localChangeManager.setActiveScheme(bindingService
1858: .getActiveScheme());
1859: } catch (final NotDefinedException e) {
1860: throw new Error(
1861: "There is a programmer error in the keys preference page"); //$NON-NLS-1$
1862: }
1863: localChangeManager.setLocale(bindingService.getLocale());
1864: localChangeManager.setPlatform(bindingService.getPlatform());
1865: localChangeManager.setBindings(bindingService.getBindings());
1866:
1867: // Update the scheme combo.
1868: schemeCombo.setInput(sortByName(localChangeManager
1869: .getDefinedSchemes()));
1870: setScheme(localChangeManager.getActiveScheme());
1871:
1872: // Update the when combo.
1873: whenCombo.setInput(getContexts());
1874:
1875: commandModel = new WritableSet();
1876: bindingModel = new WritableSet();
1877: model = new UnionSet(new IObservableSet[] { bindingModel,
1878: commandModel });
1879:
1880: bindingModel.addAll(localChangeManager
1881: .getActiveBindingsDisregardingContextFlat());
1882: fillInCommands();
1883:
1884: if (DEBUG) {
1885: Tracing.printTrace(TRACING_COMPONENT,
1886: "fill in size: " + model.size()); //$NON-NLS-1$
1887: }
1888:
1889: filteredTree.getViewer().setInput(model);
1890: }
1891:
1892: /**
1893: *
1894: */
1895: private void fillInCommands() {
1896: long startTime = 0L;
1897: if (DEBUG) {
1898: startTime = System.currentTimeMillis();
1899: }
1900: if (showAllCheckBox.getSelection()) {
1901: final Collection commandIds = commandService
1902: .getDefinedCommandIds();
1903: final Collection commands = new HashSet();
1904: final Iterator commandIdItr = commandIds.iterator();
1905: while (commandIdItr.hasNext()) {
1906: final String currentCommandId = (String) commandIdItr
1907: .next();
1908: final Command currentCommand = commandService
1909: .getCommand(currentCommandId);
1910: try {
1911: commands.addAll(ParameterizedCommand
1912: .generateCombinations(currentCommand));
1913: } catch (final NotDefinedException e) {
1914: // It is safe to just ignore undefined commands.
1915: }
1916: }
1917:
1918: // Remove duplicates.
1919: Iterator i = bindingModel.iterator();
1920: while (i.hasNext()) {
1921: commands.remove(((Binding) i.next())
1922: .getParameterizedCommand());
1923: }
1924:
1925: commandModel.addAll(commands);
1926: } else {
1927: commandModel.clear();
1928: }
1929:
1930: if (DEBUG) {
1931: final long elapsedTime = System.currentTimeMillis()
1932: - startTime;
1933: Tracing.printTrace(TRACING_COMPONENT, "fillInCommands in " //$NON-NLS-1$
1934: + elapsedTime + "ms"); //$NON-NLS-1$
1935: }
1936: }
1937:
1938: /*
1939: * (non-Javadoc)
1940: *
1941: * @see org.eclipse.ui.IWorkbenchPreferencePage#init(org.eclipse.ui.IWorkbench)
1942: */
1943: public final void init(final IWorkbench workbench) {
1944: bindingService = (IBindingService) workbench
1945: .getService(IBindingService.class);
1946: commandImageService = (ICommandImageService) workbench
1947: .getService(ICommandImageService.class);
1948: commandService = (ICommandService) workbench
1949: .getService(ICommandService.class);
1950: contextService = (IContextService) workbench
1951: .getService(IContextService.class);
1952: }
1953:
1954: /**
1955: * Updates the interface as the key sequence has changed. This finds the
1956: * selected item. If the selected item is a binding, then it updates the
1957: * binding -- either by updating a user binding, or doing the deletion
1958: * marker dance with a system binding. If the selected item is a
1959: * parameterized command, then a binding is created based on the data
1960: * controls.
1961: */
1962: private final void keySequenceChanged() {
1963: long startTime = 0L;
1964: if (DEBUG) {
1965: startTime = System.currentTimeMillis();
1966: }
1967:
1968: final KeySequence keySequence = keySequenceText
1969: .getKeySequence();
1970: if (!keySequence.isComplete()) {
1971: return;
1972: }
1973:
1974: if ((keySequence == null) || (keySequence.isEmpty())) {
1975: ISelection selection = filteredTree.getViewer()
1976: .getSelection();
1977: if (selection instanceof IStructuredSelection) {
1978: IStructuredSelection structuredSelection = (IStructuredSelection) selection;
1979: final Object node = structuredSelection
1980: .getFirstElement();
1981: if (node instanceof KeyBinding) {
1982: bindingRemove((KeyBinding) node);
1983: }
1984: }
1985: return;
1986: }
1987:
1988: ISelection selection = filteredTree.getViewer().getSelection();
1989: if (selection instanceof IStructuredSelection) {
1990: IStructuredSelection structuredSelection = (IStructuredSelection) selection;
1991: final Object node = structuredSelection.getFirstElement();
1992: if (node != null) {
1993: final Object object = node;
1994: selection = whenCombo.getSelection();
1995: final String contextId;
1996: if (selection instanceof IStructuredSelection) {
1997: structuredSelection = (IStructuredSelection) selection;
1998: final Object firstElement = structuredSelection
1999: .getFirstElement();
2000: if (firstElement == null) {
2001: contextId = IContextIds.CONTEXT_ID_WINDOW;
2002: } else {
2003: contextId = ((Context) firstElement).getId();
2004: }
2005: } else {
2006: contextId = IContextIds.CONTEXT_ID_WINDOW;
2007: }
2008: if (object instanceof KeyBinding) {
2009: KeyBinding keyBinding = (KeyBinding) object;
2010: if (!keyBinding.getContextId().equals(contextId)
2011: || !keyBinding.getKeySequence().equals(
2012: keySequence)) {
2013: final KeyBinding binding = new KeyBinding(
2014: keySequence, keyBinding
2015: .getParameterizedCommand(),
2016: getSchemeId(), contextId, null, null,
2017: null, Binding.USER);
2018:
2019: ArrayList extraSystemDeletes = new ArrayList();
2020: if (keyBinding.getType() == Binding.USER) {
2021: localChangeManager
2022: .removeBinding(keyBinding);
2023: } else {
2024: // TODO This should be the user's personal scheme.
2025: Collection previousConflictMatches = (Collection) localChangeManager
2026: .getActiveBindingsDisregardingContext()
2027: .get(
2028: keyBinding
2029: .getTriggerSequence());
2030: KeyBinding deleteBinding = new KeyBinding(
2031: keyBinding.getKeySequence(), null,
2032: keyBinding.getSchemeId(),
2033: keyBinding.getContextId(), null,
2034: null, null, Binding.USER);
2035: localChangeManager
2036: .addBinding(deleteBinding);
2037: if (previousConflictMatches != null) {
2038: Iterator i = previousConflictMatches
2039: .iterator();
2040: while (i.hasNext()) {
2041: Binding b = (Binding) i.next();
2042: if (b != keyBinding
2043: && deletes(deleteBinding, b)) {
2044: extraSystemDeletes.add(b);
2045: }
2046: }
2047: }
2048: }
2049: localChangeManager.addBinding(binding);
2050: // update the model
2051: bindingModel.remove(keyBinding);
2052: bindingModel.add(binding);
2053: if (!extraSystemDeletes.isEmpty()) {
2054: Iterator i = extraSystemDeletes.iterator();
2055: while (i.hasNext()) {
2056: KeyBinding b = (KeyBinding) i.next();
2057: KeyBinding newBinding = new KeyBinding(
2058: b.getKeySequence(),
2059: b.getParameterizedCommand(), b
2060: .getSchemeId(), b
2061: .getContextId(), null,
2062: null, null, Binding.USER);
2063: localChangeManager
2064: .addBinding(newBinding);
2065:
2066: bindingModel.remove(b);
2067: bindingModel.add(newBinding);
2068: }
2069: }
2070: updateConflicts(keyBinding);
2071: updateConflicts(binding);
2072: // end update the model
2073: update();
2074: filteredTree.getViewer().setSelection(
2075: new StructuredSelection(binding), true);
2076: }
2077: } else if (object instanceof ParameterizedCommand) {
2078: // TODO This should use the user's personal scheme.
2079: final KeyBinding binding = new KeyBinding(
2080: keySequence, (ParameterizedCommand) object,
2081: getSchemeId(), contextId, null, null, null,
2082: Binding.USER);
2083: localChangeManager.addBinding(binding);
2084: // update the model
2085: // end update the model
2086: bindingModel.add(binding);
2087: commandModel.remove(object);
2088: updateConflicts(binding);
2089: update();
2090:
2091: filteredTree.getViewer().setSelection(
2092: new StructuredSelection(binding), true);
2093: }
2094: }
2095: }
2096: if (DEBUG) {
2097: final long elapsedTime = System.currentTimeMillis()
2098: - startTime;
2099: Tracing.printTrace(TRACING_COMPONENT,
2100: "keySequenceChanged in " //$NON-NLS-1$
2101: + elapsedTime + "ms"); //$NON-NLS-1$
2102: }
2103: }
2104:
2105: /**
2106: * Logs the given exception, and opens an error dialog saying that something
2107: * went wrong. The exception is assumed to have something to do with the
2108: * preference store.
2109: *
2110: * @param exception
2111: * The exception to be logged; must not be <code>null</code>.
2112: */
2113: private final void logPreferenceStoreException(
2114: final Throwable exception) {
2115: final String message = NewKeysPreferenceMessages.PreferenceStoreError_Message;
2116: String exceptionMessage = exception.getMessage();
2117: if (exceptionMessage == null) {
2118: exceptionMessage = message;
2119: }
2120: final IStatus status = new Status(IStatus.ERROR,
2121: WorkbenchPlugin.PI_WORKBENCH, 0, exceptionMessage,
2122: exception);
2123: WorkbenchPlugin.log(message, status);
2124: StatusUtil.handleStatus(message, exception, StatusManager.SHOW);
2125: }
2126:
2127: protected final void performDefaults() {
2128:
2129: // Ask the user to confirm
2130: final String title = NewKeysPreferenceMessages.RestoreDefaultsMessageBoxText;
2131: final String message = NewKeysPreferenceMessages.RestoreDefaultsMessageBoxMessage;
2132: final boolean confirmed = MessageDialog.openConfirm(getShell(),
2133: title, message);
2134:
2135: if (confirmed) {
2136: // Fix the scheme in the local changes.
2137: final String defaultSchemeId = bindingService
2138: .getDefaultSchemeId();
2139: final Scheme defaultScheme = localChangeManager
2140: .getScheme(defaultSchemeId);
2141: try {
2142: localChangeManager.setActiveScheme(defaultScheme);
2143: } catch (final NotDefinedException e) {
2144: // At least we tried....
2145: }
2146:
2147: // Fix the bindings in the local changes.
2148: final Binding[] currentBindings = localChangeManager
2149: .getBindings();
2150: final int currentBindingsLength = currentBindings.length;
2151: final Set trimmedBindings = new HashSet();
2152: for (int i = 0; i < currentBindingsLength; i++) {
2153: final Binding binding = currentBindings[i];
2154: if (binding.getType() != Binding.USER) {
2155: trimmedBindings.add(binding);
2156: }
2157: }
2158: final Binding[] trimmedBindingArray = (Binding[]) trimmedBindings
2159: .toArray(new Binding[trimmedBindings.size()]);
2160: localChangeManager.setBindings(trimmedBindingArray);
2161:
2162: // Apply the changes.
2163: try {
2164: bindingService.savePreferences(defaultScheme,
2165: trimmedBindingArray);
2166: } catch (final IOException e) {
2167: logPreferenceStoreException(e);
2168: }
2169: long startTime = 0L;
2170: if (DEBUG) {
2171: startTime = System.currentTimeMillis();
2172: }
2173: busyRefillTree();
2174: if (DEBUG) {
2175: final long elapsedTime = System.currentTimeMillis()
2176: - startTime;
2177: Tracing.printTrace(TRACING_COMPONENT,
2178: "performDefaults:model in " //$NON-NLS-1$
2179: + elapsedTime + "ms"); //$NON-NLS-1$
2180: }
2181: }
2182:
2183: setScheme(localChangeManager.getActiveScheme());
2184:
2185: super .performDefaults();
2186: }
2187:
2188: /**
2189: * We're re-filling the entire tree, both bindings and commands. It's
2190: * loud.
2191: */
2192: private void busyRefillTree() {
2193: if (bindingModel == null) {
2194: // we haven't really been created yet.
2195: return;
2196: }
2197: BusyIndicator.showWhile(filteredTree.getViewer().getTree()
2198: .getDisplay(), new Runnable() {
2199: public void run() {
2200: try {
2201: filteredTree.getViewer().getTree().setRedraw(false);
2202:
2203: bindingModel.clear();
2204: commandModel.clear();
2205: Collection comeBack = localChangeManager
2206: .getActiveBindingsDisregardingContextFlat();
2207: bindingModel.addAll(comeBack);
2208:
2209: // showAllCheckBox.setSelection(false);
2210: fillInCommands();
2211: } finally {
2212: filteredTree.getViewer().getTree().setRedraw(true);
2213: }
2214: }
2215: });
2216: updateDataControls();
2217: }
2218:
2219: public final boolean performOk() {
2220: // Save the preferences.
2221: try {
2222: bindingService.savePreferences(localChangeManager
2223: .getActiveScheme(), localChangeManager
2224: .getBindings());
2225: } catch (final IOException e) {
2226: logPreferenceStoreException(e);
2227: }
2228: saveState(getDialogSettings());
2229: return super .performOk();
2230: }
2231:
2232: /**
2233: * Handles the selection event on the add binding button. This adds a new
2234: * binding based on the current selection.
2235: *
2236: * @param event
2237: * Ignored.
2238: */
2239: private final void selectAddBindingButton(final SelectionEvent event) {
2240: long startTime = 0L;
2241: if (DEBUG) {
2242: startTime = System.currentTimeMillis();
2243: }
2244:
2245: // Check to make sure we've got a selection.
2246: final TreeViewer viewer = filteredTree.getViewer();
2247: final ISelection selection = viewer.getSelection();
2248: if (!(selection instanceof IStructuredSelection)) {
2249: return;
2250: }
2251:
2252: final IStructuredSelection structuredSelection = (IStructuredSelection) selection;
2253: final Object firstElement = structuredSelection
2254: .getFirstElement();
2255: final Object value = firstElement;
2256: if (value instanceof KeyBinding) {
2257: bindingAdd((KeyBinding) value);
2258: } else if (value instanceof ParameterizedCommand) {
2259: bindingText.setFocus();
2260: }
2261:
2262: if (DEBUG) {
2263: final long elapsedTime = System.currentTimeMillis()
2264: - startTime;
2265: Tracing.printTrace(TRACING_COMPONENT,
2266: "selectAddBindingButton in " //$NON-NLS-1$
2267: + elapsedTime + "ms"); //$NON-NLS-1$
2268: }
2269: }
2270:
2271: /**
2272: * Handles the selection event on the remove binding button. This removes
2273: * the selected binding.
2274: *
2275: * @param event
2276: * Ignored.
2277: */
2278: private final void selectRemoveBindingButton(
2279: final SelectionEvent event) {
2280: long startTime = 0L;
2281: if (DEBUG) {
2282: startTime = System.currentTimeMillis();
2283: }
2284: // Check to make sure we've got a selection.
2285: final TreeViewer viewer = filteredTree.getViewer();
2286: final ISelection selection = viewer.getSelection();
2287: if (!(selection instanceof IStructuredSelection)) {
2288: return;
2289: }
2290:
2291: final IStructuredSelection structuredSelection = (IStructuredSelection) selection;
2292: final Object firstElement = structuredSelection
2293: .getFirstElement();
2294: final Object value = firstElement;
2295: if (value instanceof KeyBinding) {
2296: bindingRemove((KeyBinding) value);
2297: } else if (value == markedParameterizedCommand) {
2298: commandModel.remove(markedParameterizedCommand);
2299: markedParameterizedCommand = null;
2300: markedContextId = null;
2301: update();
2302: }
2303: if (DEBUG) {
2304: final long elapsedTime = System.currentTimeMillis()
2305: - startTime;
2306: Tracing.printTrace(TRACING_COMPONENT,
2307: "selectRemoveBindingButton in " //$NON-NLS-1$
2308: + elapsedTime + "ms"); //$NON-NLS-1$
2309: }
2310: }
2311:
2312: private final void selectRestoreBindingButton(
2313: final SelectionEvent event) {
2314: long startTime = 0L;
2315: if (DEBUG) {
2316: startTime = System.currentTimeMillis();
2317: }
2318: // Check to make sure we've got a selection.
2319: final TreeViewer viewer = filteredTree.getViewer();
2320: final ISelection selection = viewer.getSelection();
2321: if (!(selection instanceof IStructuredSelection)) {
2322: return;
2323: }
2324:
2325: final IStructuredSelection structuredSelection = (IStructuredSelection) selection;
2326: final Object firstElement = structuredSelection
2327: .getFirstElement();
2328: final Object value = firstElement;
2329: if (value instanceof KeyBinding) {
2330: bindingRestore((KeyBinding) value);
2331: } else if (value instanceof ParameterizedCommand) {
2332: bindingRestore((ParameterizedCommand) value, true);
2333: }
2334: if (DEBUG) {
2335: final long elapsedTime = System.currentTimeMillis()
2336: - startTime;
2337: Tracing.printTrace(TRACING_COMPONENT,
2338: "selectRestoreBindingButton in " //$NON-NLS-1$
2339: + elapsedTime + "ms"); //$NON-NLS-1$
2340: }
2341: }
2342:
2343: /**
2344: * Handles a selection event on the scheme combo. If the scheme has changed,
2345: * then the local change manager is updated, and the page's contents are
2346: * updated as well.
2347: *
2348: * @param event
2349: * The selection event; must not be <code>null</code>.
2350: */
2351: private final void selectSchemeCombo(
2352: final SelectionChangedEvent event) {
2353: final ISelection selection = event.getSelection();
2354: if (selection instanceof IStructuredSelection) {
2355: final Object firstElement = ((IStructuredSelection) selection)
2356: .getFirstElement();
2357: if (firstElement instanceof Scheme) {
2358: final Scheme newScheme = (Scheme) firstElement;
2359: if (newScheme != localChangeManager.getActiveScheme()) {
2360: try {
2361: localChangeManager.setActiveScheme(newScheme);
2362: busyRefillTree();
2363: } catch (final NotDefinedException e) {
2364: // TODO The scheme wasn't valid.
2365: }
2366: }
2367: }
2368: }
2369: }
2370:
2371: /**
2372: * If the row has changed, then update the data controls.
2373: */
2374: private final void selectTreeRow(final SelectionChangedEvent event) {
2375: updateDataControls();
2376: }
2377:
2378: /**
2379: * Sets the currently selected scheme. Setting the scheme always triggers an
2380: * update of the underlying widgets.
2381: *
2382: * @param scheme
2383: * The scheme to select; may be <code>null</code>.
2384: */
2385: private final void setScheme(final Scheme scheme) {
2386: schemeCombo.setSelection(new StructuredSelection(scheme));
2387: }
2388:
2389: /**
2390: * Updates all of the controls on this preference page in response to a user
2391: * interaction.
2392: */
2393: private final void update() {
2394: updateTree();
2395: updateDataControls();
2396: }
2397:
2398: /**
2399: * Updates the data controls to match the current selection, if any.
2400: */
2401: private final void updateDataControls() {
2402: final ISelection selection = filteredTree.getViewer()
2403: .getSelection();
2404: if (selection instanceof IStructuredSelection) {
2405: final IStructuredSelection structuredSelection = (IStructuredSelection) selection;
2406: final Object node = structuredSelection.getFirstElement();
2407: if (node != null) {
2408: final Object object = node;
2409: if (object instanceof KeyBinding) {
2410: final KeyBinding binding = (KeyBinding) object;
2411: try {
2412: commandNameValueLabel.setText(binding
2413: .getParameterizedCommand().getName());
2414: String description = binding
2415: .getParameterizedCommand().getCommand()
2416: .getDescription();
2417: if (description == null) {
2418: description = Util.ZERO_LENGTH_STRING;
2419: }
2420: descriptionValueText.setText(description);
2421: } catch (final NotDefinedException e) {
2422: // It's probably okay to just let this one slide.
2423: }
2424: whenCombo.setSelection(new StructuredSelection(
2425: contextService.getContext(binding
2426: .getContextId())));
2427: keySequenceText.setKeySequence(binding
2428: .getKeySequence());
2429:
2430: } else if (object instanceof ParameterizedCommand) {
2431: final ParameterizedCommand command = (ParameterizedCommand) object;
2432: try {
2433: commandNameValueLabel
2434: .setText(command.getName());
2435: String description = command.getCommand()
2436: .getDescription();
2437: if (description == null) {
2438: description = Util.ZERO_LENGTH_STRING;
2439: }
2440: descriptionValueText.setText(description);
2441: } catch (final NotDefinedException e) {
2442: // It's probably okay to just let this one slide.
2443: }
2444: keySequenceText.clear();
2445: if (command == markedParameterizedCommand) {
2446: whenCombo.setSelection(new StructuredSelection(
2447: contextService
2448: .getContext(markedContextId)));
2449: } else {
2450: whenCombo
2451: .setSelection(new StructuredSelection(
2452: contextService
2453: .getContext(IContextIds.CONTEXT_ID_WINDOW)));
2454: }
2455: }
2456: } else {
2457: commandNameValueLabel.setText(""); //$NON-NLS-1$
2458: descriptionValueText.setText(""); //$NON-NLS-1$
2459: keySequenceText.clear();
2460: whenCombo.setSelection(null);
2461: }
2462: }
2463: }
2464:
2465: private final void updateTree() {
2466: long startTime = 0L;
2467: if (DEBUG) {
2468: startTime = System.currentTimeMillis();
2469: }
2470:
2471: final TreeViewer viewer = filteredTree.getViewer();
2472:
2473: // Add the marked parameterized command, if any.
2474: if (markedParameterizedCommand != null) {
2475: commandModel.add(markedParameterizedCommand);
2476: markedParameterizedCommand = null;
2477: }
2478:
2479: // Repack all of the columns.
2480: final Tree tree = viewer.getTree();
2481: final TreeColumn[] columns = tree.getColumns();
2482:
2483: columns[BindingLabelProvider.COLUMN_COMMAND].setWidth(240);
2484: columns[BindingLabelProvider.COLUMN_TRIGGER_SEQUENCE]
2485: .setWidth(130);
2486: columns[BindingLabelProvider.COLUMN_WHEN].setWidth(130);
2487: columns[BindingLabelProvider.COLUMN_CATEGORY].setWidth(130);
2488: columns[BindingLabelProvider.COLUMN_USER].setWidth(50);
2489:
2490: if (DEBUG) {
2491: final long elapsedTime = System.currentTimeMillis()
2492: - startTime;
2493: Tracing.printTrace(TRACING_COMPONENT, "Refreshed page in " //$NON-NLS-1$
2494: + elapsedTime + "ms"); //$NON-NLS-1$
2495: }
2496: }
2497:
2498: /**
2499: * Save the state of the receiver.
2500: *
2501: * @param dialogSettings
2502: */
2503: public void saveState(IDialogSettings dialogSettings) {
2504: if (dialogSettings == null) {
2505: return;
2506: }
2507: dialogSettings.put(TAG_FIELD, showAllCheckBox.getSelection());
2508: dialogSettings.put(TAG_FILTER_ACTION_SETS,
2509: filterActionSetContexts);
2510: dialogSettings.put(TAG_FILTER_INTERNAL, filterInternalContexts);
2511: dialogSettings.put(TAG_FILTER_UNCAT, filteredTree
2512: .isFilteringCategories());
2513: }
2514:
2515: protected IDialogSettings getDialogSettings() {
2516: IDialogSettings workbenchSettings = WorkbenchPlugin
2517: .getDefault().getDialogSettings();
2518:
2519: IDialogSettings settings = workbenchSettings
2520: .getSection(TAG_DIALOG_SECTION);
2521:
2522: if (settings == null) {
2523: settings = workbenchSettings
2524: .addNewSection(TAG_DIALOG_SECTION);
2525: }
2526: return settings;
2527: }
2528:
2529: protected Object[] getContexts() {
2530:
2531: Context[] contexts = contextService.getDefinedContexts();
2532: List filteredContexts = new ArrayList();
2533: try {
2534: if (filterActionSetContexts) {
2535: for (int i = 0; i < contexts.length; i++) {
2536: String parentId = contexts[i].getParentId();
2537: boolean check = false;
2538: if (contexts[i].getId().equalsIgnoreCase(
2539: CONTEXT_ID_ACTION_SETS)) {
2540: check = true;
2541: }
2542: while (parentId != null) {
2543: if (parentId
2544: .equalsIgnoreCase(CONTEXT_ID_ACTION_SETS)) {
2545: check = true;
2546: }
2547: parentId = contextService.getContext(parentId)
2548: .getParentId();
2549: }
2550: if (!check) {
2551: filteredContexts.add(contexts[i]);
2552: }
2553: }
2554: } else {
2555: filteredContexts.addAll(Arrays.asList(contexts));
2556: }
2557:
2558: if (filterInternalContexts) {
2559: for (int i = 0; i < filteredContexts.size(); i++) {
2560: if (((Context) filteredContexts.get(i)).getId()
2561: .indexOf(CONTEXT_ID_INTERNAL) != -1) {
2562: filteredContexts.remove(i);
2563: }
2564: }
2565: }
2566:
2567: } catch (NotDefinedException e) {
2568: return contexts;
2569: }
2570:
2571: return filteredContexts.toArray();
2572: }
2573:
2574: private void updateWhenCombo() {
2575: ISelection selection = filteredTree.getViewer().getSelection();
2576: if (selection instanceof IStructuredSelection) {
2577: IStructuredSelection structuredSelection = (IStructuredSelection) selection;
2578: Object node = structuredSelection.getFirstElement();
2579: if (node != null) {
2580: final Object object = node;
2581: selection = whenCombo.getSelection();
2582: final String contextId;
2583: if (selection instanceof IStructuredSelection) {
2584: structuredSelection = (IStructuredSelection) selection;
2585: final Object firstElement = structuredSelection
2586: .getFirstElement();
2587: if (firstElement == null) {
2588: contextId = IContextIds.CONTEXT_ID_WINDOW;
2589: } else {
2590: contextId = ((Context) firstElement).getId();
2591: }
2592: } else {
2593: contextId = IContextIds.CONTEXT_ID_WINDOW;
2594: }
2595: if (object instanceof KeyBinding) {
2596: KeyBinding keyBinding = (KeyBinding) object;
2597: if (!keyBinding.getContextId().equals(contextId)) {
2598: final KeyBinding binding = new KeyBinding(
2599: keyBinding.getKeySequence(), keyBinding
2600: .getParameterizedCommand(),
2601: getSchemeId(), contextId, null, null,
2602: null, Binding.USER);
2603:
2604: if (keyBinding.getType() == Binding.USER) {
2605: localChangeManager
2606: .removeBinding(keyBinding);
2607: } else {
2608: localChangeManager
2609: .addBinding(new KeyBinding(
2610: keyBinding.getKeySequence(),
2611: null, keyBinding
2612: .getSchemeId(),
2613: keyBinding.getContextId(),
2614: null, null, null,
2615: Binding.USER));
2616: }
2617: localChangeManager.addBinding(binding);
2618: // update the model
2619: bindingModel.remove(keyBinding);
2620: bindingModel.add(binding);
2621: updateConflicts(keyBinding);
2622: updateConflicts(binding);
2623: // end update the model
2624: update();
2625: filteredTree.getViewer().setSelection(
2626: new StructuredSelection(binding), true);
2627: }
2628: }
2629: }
2630: }
2631: }
2632:
2633: /*
2634: * (non-Javadoc)
2635: *
2636: * @see org.eclipse.jface.preference.PreferencePage#applyData(java.lang.Object)
2637: */
2638: public void applyData(Object data) {
2639: if (data instanceof Binding && filteredTree != null) {
2640: filteredTree.getViewer().setSelection(
2641: new StructuredSelection(data), true);
2642: }
2643: }
2644:
2645: public String getSchemeId() {
2646: ISelection sel = schemeCombo.getSelection();
2647: if (sel instanceof IStructuredSelection) {
2648: Object o = ((IStructuredSelection) sel).getFirstElement();
2649: if (o instanceof Scheme) {
2650: return ((Scheme) o).getId();
2651: }
2652: }
2653: return IBindingService.DEFAULT_DEFAULT_ACTIVE_SCHEME_ID;
2654: }
2655: }
|