KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > eclipse > ui > internal > keys > NewKeysPreferencePage


1 /*******************************************************************************
2  * Copyright (c) 2005, 2007 IBM Corporation and others.
3  * All rights reserved. This program and the accompanying materials
4  * are made available under the terms of the Eclipse Public License v1.0
5  * which accompanies this distribution, and is available at
6  * http://www.eclipse.org/legal/epl-v10.html
7  *
8  * Contributors:
9  * IBM Corporation - initial API and implementation
10  *******************************************************************************/

11
12 package org.eclipse.ui.internal.keys;
13
14 import java.io.IOException JavaDoc;
15 import java.net.URL JavaDoc;
16 import java.util.ArrayList JavaDoc;
17 import java.util.Arrays JavaDoc;
18 import java.util.Collection JavaDoc;
19 import java.util.Collections JavaDoc;
20 import java.util.HashSet JavaDoc;
21 import java.util.Iterator JavaDoc;
22 import java.util.List JavaDoc;
23 import java.util.Locale JavaDoc;
24 import java.util.Set JavaDoc;
25
26 import org.eclipse.core.commands.Category;
27 import org.eclipse.core.commands.Command;
28 import org.eclipse.core.commands.CommandManager;
29 import org.eclipse.core.commands.ParameterizedCommand;
30 import org.eclipse.core.commands.common.NamedHandleObject;
31 import org.eclipse.core.commands.common.NamedHandleObjectComparator;
32 import org.eclipse.core.commands.common.NotDefinedException;
33 import org.eclipse.core.commands.contexts.Context;
34 import org.eclipse.core.commands.contexts.ContextManager;
35 import org.eclipse.core.commands.util.Tracing;
36 import org.eclipse.core.databinding.observable.Diffs;
37 import org.eclipse.core.databinding.observable.IStaleListener;
38 import org.eclipse.core.databinding.observable.StaleEvent;
39 import org.eclipse.core.databinding.observable.set.IObservableSet;
40 import org.eclipse.core.databinding.observable.set.ISetChangeListener;
41 import org.eclipse.core.databinding.observable.set.ObservableSet;
42 import org.eclipse.core.databinding.observable.set.SetChangeEvent;
43 import org.eclipse.core.databinding.observable.set.UnionSet;
44 import org.eclipse.core.databinding.observable.set.WritableSet;
45 import org.eclipse.core.databinding.observable.value.IObservableValue;
46 import org.eclipse.core.databinding.observable.value.IValueChangeListener;
47 import org.eclipse.core.databinding.observable.value.ValueChangeEvent;
48 import org.eclipse.core.runtime.IStatus;
49 import org.eclipse.core.runtime.Status;
50 import org.eclipse.jface.bindings.Binding;
51 import org.eclipse.jface.bindings.BindingManager;
52 import org.eclipse.jface.bindings.Scheme;
53 import org.eclipse.jface.bindings.keys.KeyBinding;
54 import org.eclipse.jface.bindings.keys.KeySequence;
55 import org.eclipse.jface.bindings.keys.KeySequenceText;
56 import org.eclipse.jface.bindings.keys.KeyStroke;
57 import org.eclipse.jface.contexts.IContextIds;
58 import org.eclipse.jface.databinding.swt.SWTObservables;
59 import org.eclipse.jface.databinding.viewers.ViewersObservables;
60 import org.eclipse.jface.dialogs.IDialogConstants;
61 import org.eclipse.jface.dialogs.IDialogSettings;
62 import org.eclipse.jface.dialogs.MessageDialog;
63 import org.eclipse.jface.internal.databinding.provisional.swt.ControlUpdater;
64 import org.eclipse.jface.preference.PreferencePage;
65 import org.eclipse.jface.resource.DeviceResourceException;
66 import org.eclipse.jface.resource.ImageDescriptor;
67 import org.eclipse.jface.resource.JFaceResources;
68 import org.eclipse.jface.resource.LocalResourceManager;
69 import org.eclipse.jface.util.IPropertyChangeListener;
70 import org.eclipse.jface.util.PropertyChangeEvent;
71 import org.eclipse.jface.viewers.AbstractListViewer;
72 import org.eclipse.jface.viewers.ArrayContentProvider;
73 import org.eclipse.jface.viewers.ComboViewer;
74 import org.eclipse.jface.viewers.IBaseLabelProvider;
75 import org.eclipse.jface.viewers.ISelection;
76 import org.eclipse.jface.viewers.ISelectionChangedListener;
77 import org.eclipse.jface.viewers.IStructuredSelection;
78 import org.eclipse.jface.viewers.ITableLabelProvider;
79 import org.eclipse.jface.viewers.ITreeContentProvider;
80 import org.eclipse.jface.viewers.LabelProvider;
81 import org.eclipse.jface.viewers.NamedHandleObjectLabelProvider;
82 import org.eclipse.jface.viewers.SelectionChangedEvent;
83 import org.eclipse.jface.viewers.StructuredSelection;
84 import org.eclipse.jface.viewers.TreeViewer;
85 import org.eclipse.jface.viewers.Viewer;
86 import org.eclipse.jface.viewers.ViewerComparator;
87 import org.eclipse.jface.window.Window;
88 import org.eclipse.swt.SWT;
89 import org.eclipse.swt.custom.BusyIndicator;
90 import org.eclipse.swt.events.DisposeEvent;
91 import org.eclipse.swt.events.DisposeListener;
92 import org.eclipse.swt.events.FocusEvent;
93 import org.eclipse.swt.events.FocusListener;
94 import org.eclipse.swt.events.SelectionAdapter;
95 import org.eclipse.swt.events.SelectionEvent;
96 import org.eclipse.swt.events.SelectionListener;
97 import org.eclipse.swt.graphics.Image;
98 import org.eclipse.swt.graphics.Point;
99 import org.eclipse.swt.layout.GridData;
100 import org.eclipse.swt.layout.GridLayout;
101 import org.eclipse.swt.widgets.Button;
102 import org.eclipse.swt.widgets.Combo;
103 import org.eclipse.swt.widgets.Composite;
104 import org.eclipse.swt.widgets.Control;
105 import org.eclipse.swt.widgets.Display;
106 import org.eclipse.swt.widgets.Label;
107 import org.eclipse.swt.widgets.Menu;
108 import org.eclipse.swt.widgets.MenuItem;
109 import org.eclipse.swt.widgets.Text;
110 import org.eclipse.swt.widgets.Tree;
111 import org.eclipse.swt.widgets.TreeColumn;
112 import org.eclipse.ui.IWorkbench;
113 import org.eclipse.ui.IWorkbenchPreferencePage;
114 import org.eclipse.ui.PlatformUI;
115 import org.eclipse.ui.commands.ICommandService;
116 import org.eclipse.ui.contexts.IContextService;
117 import org.eclipse.ui.dialogs.FilteredTree;
118 import org.eclipse.ui.internal.IWorkbenchHelpContextIds;
119 import org.eclipse.ui.internal.WorkbenchPlugin;
120 import org.eclipse.ui.internal.commands.ICommandImageService;
121 import org.eclipse.ui.internal.misc.Policy;
122 import org.eclipse.ui.internal.misc.StatusUtil;
123 import org.eclipse.ui.internal.util.BundleUtility;
124 import org.eclipse.ui.internal.util.Util;
125 import org.eclipse.ui.keys.IBindingService;
126 import org.eclipse.ui.statushandlers.StatusManager;
127
128 /**
129  * <p>
130  * A preference page that is capable of displaying and editing the bindings
131  * between commands and user input events. These are typically things like
132  * keyboard shortcuts.
133  * </p>
134  * <p>
135  * This preference page has four general types of methods. Create methods are
136  * called when the page is first made visible. They are responsible for creating
137  * all of the widgets, and laying them out within the preference page. Fill
138  * methods populate the contents of the widgets that contain collections of data
139  * from which items can be selected. The select methods respond to selection
140  * events from the user, such as a button press or a table selection. The update
141  * methods update the contents of various widgets based on the current state of
142  * the user interface. For example, the command name label will always try to
143  * match the current select in the binding table.
144  * </p>
145  *
146  * @since 3.2
147  */

148 public final class NewKeysPreferencePage extends PreferencePage implements
149         IWorkbenchPreferencePage {
150
151     private static boolean DEBUG = Policy.DEBUG_KEY_BINDINGS;
152
153     private static final String JavaDoc TRACING_COMPONENT = "NewKeysPref"; //$NON-NLS-1$
154

155     /**
156      * @since 3.3
157      *
158      */

159     private final class ResortColumn extends SelectionAdapter {
160         private final BindingComparator comparator;
161         private final TreeColumn treeColumn;
162         private final Tree tree;
163         private final int column;
164
165         /**
166          * @param comparator
167          * @param commandNameColumn
168          * @param tree
169          */

170         private ResortColumn(BindingComparator comparator,
171                 TreeColumn treeColumn, Tree tree, int column) {
172             this.comparator = comparator;
173             this.treeColumn = treeColumn;
174             this.tree = tree;
175             this.column = column;
176         }
177
178         public void widgetSelected(SelectionEvent e) {
179             if (comparator.getSortColumn() == column) {
180                 comparator.setAscending(!comparator.isAscending());
181             }
182             tree.setSortColumn(treeColumn);
183             comparator.setSortColumn(column);
184             tree.setSortDirection(comparator.isAscending() ? SWT.UP : SWT.DOWN);
185             try {
186                 filteredTree.getViewer().getTree().setRedraw(false);
187                 filteredTree.getViewer().refresh();
188             } finally {
189                 filteredTree.getViewer().getTree().setRedraw(true);
190             }
191         }
192     }
193
194     private class ObservableSetContentProvider implements ITreeContentProvider {
195
196         private class KnownElementsSet extends ObservableSet {
197
198             KnownElementsSet(Set JavaDoc wrappedSet) {
199                 super(SWTObservables.getRealm(Display.getDefault()),
200                         wrappedSet, Object JavaDoc.class);
201             }
202
203             void doFireDiff(Set JavaDoc added, Set JavaDoc removed) {
204                 fireSetChange(Diffs.createSetDiff(added, removed));
205             }
206
207             void doFireStale(boolean isStale) {
208                 if (isStale) {
209                     fireStale();
210                 } else {
211                     fireChange();
212                 }
213             }
214         }
215
216         private IObservableSet readableSet;
217
218         private Viewer viewer;
219
220         /**
221          * This readableSet returns the same elements as the input readableSet.
222          * However, it only fires events AFTER the elements have been added or
223          * removed from the viewer.
224          */

225         private KnownElementsSet knownElements;
226
227         private ISetChangeListener listener = new ISetChangeListener() {
228
229             public void handleSetChange(SetChangeEvent event) {
230                 boolean wasStale = knownElements.isStale();
231                 if (isDisposed()) {
232                     return;
233                 }
234                 doDiff(event.diff.getAdditions(), event.diff.getRemovals(),
235                         true);
236                 if (!wasStale && event.getObservableSet().isStale()) {
237                     knownElements.doFireStale(true);
238                 }
239             }
240         };
241
242         private IStaleListener staleListener = new IStaleListener() {
243             public void handleStale(StaleEvent event) {
244                 knownElements.doFireStale(event.getObservable().isStale());
245             }
246         };
247
248         /**
249          *
250          */

251         public ObservableSetContentProvider() {
252             readableSet = new ObservableSet(SWTObservables.getRealm(Display
253                     .getDefault()), Collections.EMPTY_SET, Object JavaDoc.class) {
254             };
255             knownElements = new KnownElementsSet(readableSet);
256         }
257
258         public void dispose() {
259             setInput(null);
260         }
261
262         private void doDiff(Set JavaDoc added, Set JavaDoc removed, boolean updateViewer) {
263             knownElements.doFireDiff(added, Collections.EMPTY_SET);
264
265             if (updateViewer) {
266                 if (added.size() > 20 || removed.size() > 20) {
267                     viewer.refresh();
268                 } else {
269                     Object JavaDoc[] toAdd = added.toArray();
270                     if (viewer instanceof TreeViewer) {
271                         TreeViewer tv = (TreeViewer) viewer;
272                         tv.add(model, toAdd);
273                     } else if (viewer instanceof AbstractListViewer) {
274                         AbstractListViewer lv = (AbstractListViewer) viewer;
275                         lv.add(toAdd);
276                     }
277                     Object JavaDoc[] toRemove = removed.toArray();
278                     if (viewer instanceof TreeViewer) {
279                         TreeViewer tv = (TreeViewer) viewer;
280                         tv.remove(toRemove);
281                     } else if (viewer instanceof AbstractListViewer) {
282                         AbstractListViewer lv = (AbstractListViewer) viewer;
283                         lv.remove(toRemove);
284                     }
285                 }
286             }
287             knownElements.doFireDiff(Collections.EMPTY_SET, removed);
288         }
289
290         public Object JavaDoc[] getElements(Object JavaDoc inputElement) {
291             return readableSet.toArray();
292         }
293
294         /**
295          * Returns the readableSet of elements known to this content provider.
296          * Items are added to this readableSet before being added to the viewer,
297          * and they are removed after being removed from the viewer. The
298          * readableSet is always updated after the viewer. This is intended for
299          * use by label providers, as it will always return the items that need
300          * labels.
301          *
302          * @return readableSet of items that will need labels
303          */

304         public IObservableSet getKnownElements() {
305             return knownElements;
306         }
307
308         public void inputChanged(Viewer viewer, Object JavaDoc oldInput, Object JavaDoc newInput) {
309             this.viewer = viewer;
310
311             if (newInput != null && !(newInput instanceof IObservableSet)) {
312                 throw new IllegalArgumentException JavaDoc(
313                         "This content provider only works with input of type IReadableSet"); //$NON-NLS-1$
314
}
315
316             setInput((IObservableSet) newInput);
317         }
318
319         private boolean isDisposed() {
320             return viewer.getControl() == null
321                     || viewer.getControl().isDisposed();
322         }
323
324         private void setInput(IObservableSet newSet) {
325             boolean updateViewer = true;
326             if (newSet == null) {
327                 newSet = new ObservableSet(SWTObservables.getRealm(Display
328                         .getDefault()), Collections.EMPTY_SET, Object JavaDoc.class) {
329                 };
330                 // don't update the viewer - its input is null
331
updateViewer = false;
332             }
333
334             boolean wasStale = false;
335             if (readableSet != null) {
336                 wasStale = readableSet.isStale();
337                 readableSet.removeSetChangeListener(listener);
338                 readableSet.removeStaleListener(staleListener);
339             }
340
341             HashSet JavaDoc additions = new HashSet JavaDoc();
342             HashSet JavaDoc removals = new HashSet JavaDoc();
343
344             additions.addAll(newSet);
345             additions.removeAll(readableSet);
346
347             removals.addAll(readableSet);
348             removals.removeAll(newSet);
349
350             readableSet = newSet;
351
352             doDiff(additions, removals, updateViewer);
353
354             if (readableSet != null) {
355                 readableSet.addSetChangeListener(listener);
356                 readableSet.addStaleListener(staleListener);
357             }
358
359             boolean isStale = (readableSet != null && readableSet.isStale());
360             if (isStale != wasStale) {
361                 knownElements.doFireStale(isStale);
362             }
363         }
364
365         /*
366          * (non-Javadoc)
367          *
368          * @see org.eclipse.jface.viewers.ITreeContentProvider#getChildren(java.lang.Object)
369          */

370         public Object JavaDoc[] getChildren(Object JavaDoc parentElement) {
371             // TODO Auto-generated method stub
372
return null;
373         }
374
375         /*
376          * (non-Javadoc)
377          *
378          * @see org.eclipse.jface.viewers.ITreeContentProvider#getParent(java.lang.Object)
379          */

380         public Object JavaDoc getParent(Object JavaDoc element) {
381             // TODO Auto-generated method stub
382
return null;
383         }
384
385         /*
386          * (non-Javadoc)
387          *
388          * @see org.eclipse.jface.viewers.ITreeContentProvider#hasChildren(java.lang.Object)
389          */

390         public boolean hasChildren(Object JavaDoc element) {
391             // TODO Auto-generated method stub
392
return false;
393         }
394
395     }
396
397     /**
398      * A FilteredTree that provides a combo which is used to organize and
399      * display elements in the tree according to the selected criteria.
400      *
401      */

402     protected class CategoryFilterTree extends FilteredTree {
403
404         private CategoryPatternFilter filter;
405
406         /**
407          * Constructor for PatternFilteredTree.
408          *
409          * @param parent
410          * @param treeStyle
411          * @param filter
412          */

413         protected CategoryFilterTree(Composite parent, int treeStyle,
414                 CategoryPatternFilter filter) {
415             super(parent, treeStyle, filter);
416             this.filter = filter;
417         }
418
419         public void filterCategories(boolean b) {
420             filter.filterCategories(b);
421             textChanged();
422         }
423
424         public boolean isFilteringCategories() {
425             return filter.isFilteringCategories();
426         }
427     }
428
429     /**
430      * A label provider that simply extracts the command name and the formatted
431      * trigger sequence from a given binding, and matches them to the correct
432      * column.
433      */

434     private final class BindingLabelProvider extends LabelProvider implements
435             ITableLabelProvider {
436
437         /**
438          * The index of the column containing the command name.
439          */

440         private static final int COLUMN_COMMAND = 0;
441
442         /**
443          * The index of the column containing the trigger sequence.
444          */

445         private static final int COLUMN_TRIGGER_SEQUENCE = 1;
446
447         /**
448          * The index of the column containing the trigger sequence.
449          */

450         private static final int COLUMN_WHEN = 2;
451
452         /**
453          * The index of the column containing the Category.
454          */

455         private static final int COLUMN_CATEGORY = 3;
456
457         /**
458          * The index of the column with the image for User binding
459          */

460         private static final int COLUMN_USER = 4;
461
462         /**
463          * A resource manager for this preference page.
464          */

465         private final LocalResourceManager localResourceManager = new LocalResourceManager(
466                 JFaceResources.getResources());
467
468         public final void dispose() {
469             super.dispose();
470             localResourceManager.dispose();
471         }
472
473         public final Image getColumnImage(final Object JavaDoc element,
474                 final int columnIndex) {
475             final Object JavaDoc value = element;
476             if (value instanceof Binding) {
477                 switch (columnIndex) {
478                 case COLUMN_COMMAND:
479                     final ParameterizedCommand parameterizedCommand = ((Binding) value)
480                             .getParameterizedCommand();
481                     if (parameterizedCommand != null) {
482                         final String JavaDoc commandId = parameterizedCommand.getId();
483                         final ImageDescriptor imageDescriptor = commandImageService
484                                 .getImageDescriptor(commandId);
485                         if (imageDescriptor == null) {
486                             return null;
487                         }
488                         try {
489                             return localResourceManager
490                                     .createImage(imageDescriptor);
491                         } catch (final DeviceResourceException e) {
492                             final String JavaDoc message = "Problem retrieving image for a command '" //$NON-NLS-1$
493
+ commandId + '\'';
494                             final IStatus status = new Status(IStatus.ERROR,
495                                     WorkbenchPlugin.PI_WORKBENCH, 0, message, e);
496                             WorkbenchPlugin.log(message, status);
497                         }
498                     }
499                     return null;
500
501                 case COLUMN_USER:
502                     if (((Binding) value).getType() == Binding.USER)
503                         return ImageFactory.getImage("change"); //$NON-NLS-1$
504
return ImageFactory.getImage("blank"); //$NON-NLS-1$
505
}
506
507             } else if (value instanceof ParameterizedCommand) {
508                 switch (columnIndex) {
509                 case COLUMN_COMMAND:
510                     final ParameterizedCommand parameterizedCommand = (ParameterizedCommand) value;
511                     final String JavaDoc commandId = parameterizedCommand.getId();
512                     final ImageDescriptor imageDescriptor = commandImageService
513                             .getImageDescriptor(commandId);
514                     if (imageDescriptor == null) {
515                         return null;
516                     }
517                     try {
518                         return localResourceManager
519                                 .createImage(imageDescriptor);
520                     } catch (final DeviceResourceException e) {
521                         final String JavaDoc message = "Problem retrieving image for a command '" //$NON-NLS-1$
522
+ commandId + '\'';
523                         final IStatus status = new Status(IStatus.ERROR,
524                                 WorkbenchPlugin.PI_WORKBENCH, 0, message, e);
525                         WorkbenchPlugin.log(message, status);
526                     }
527                     return null;
528                 case COLUMN_USER:
529                     return ImageFactory.getImage("blank"); //$NON-NLS-1$
530
}
531
532             } else if ((value instanceof Category) || (value instanceof String JavaDoc)) {
533                 switch (columnIndex) {
534                 case COLUMN_COMMAND:
535                     final URL JavaDoc url = BundleUtility.find(PlatformUI.PLUGIN_ID,
536                             ICON_GROUP_OF_BINDINGS);
537                     final ImageDescriptor imageDescriptor = ImageDescriptor
538                             .createFromURL(url);
539                     try {
540                         return localResourceManager
541                                 .createImage(imageDescriptor);
542                     } catch (final DeviceResourceException e) {
543                         final String JavaDoc message = "Problem retrieving image for groups of bindings: '" //$NON-NLS-1$
544
+ ICON_GROUP_OF_BINDINGS + '\'';
545                         final IStatus status = new Status(IStatus.ERROR,
546                                 WorkbenchPlugin.PI_WORKBENCH, 0, message, e);
547                         WorkbenchPlugin.log(message, status);
548                     }
549                 }
550
551             }
552
553             return null;
554         }
555
556         private boolean checkConflict(Binding binding) {
557             Collection JavaDoc matches = (Collection JavaDoc) localChangeManager
558                     .getActiveBindingsDisregardingContext().get(
559                             binding.getTriggerSequence());
560             if (matches != null) {
561                 Iterator JavaDoc i = matches.iterator();
562                 while (i.hasNext()) {
563                     Binding b = (Binding) i.next();
564                     if (binding != b
565                             && b.getContextId().equals(binding.getContextId())
566                             && b.getSchemeId().equals(binding.getSchemeId())) {
567                         return true;
568                     }
569                 }
570             }
571             return false;
572         }
573
574         public final String JavaDoc getColumnText(final Object JavaDoc element,
575                 final int columnIndex) {
576             final Object JavaDoc value = element;
577             if (value instanceof Binding) {
578                 final Binding binding = (Binding) value;
579                 switch (columnIndex) {
580                 case COLUMN_COMMAND:
581                     try {
582                         return binding.getParameterizedCommand().getName();
583
584                     } catch (final NotDefinedException e) {
585                         return NewKeysPreferenceMessages.Undefined_Command;
586                     }
587                 case COLUMN_TRIGGER_SEQUENCE:
588                     if (checkConflict(binding)) {
589                         return "*" + binding.getTriggerSequence().format(); //$NON-NLS-1$
590
}
591                     return binding.getTriggerSequence().format();
592
593                 case COLUMN_WHEN:
594                     try {
595                         return contextService
596                                 .getContext(binding.getContextId()).getName();
597                     } catch (NotDefinedException e1) {
598                         return NewKeysPreferenceMessages.Undefined_Context;
599                     }
600                 case COLUMN_CATEGORY:
601                     try {
602                         return binding.getParameterizedCommand().getCommand()
603                                 .getCategory().getName();
604                     } catch (NotDefinedException e) {
605                         return NewKeysPreferenceMessages.Unavailable_Category;
606                     }
607                 default:
608                     return null;
609                 }
610             } else if (value instanceof Category) {
611                 if (columnIndex == COLUMN_COMMAND) {
612                     try {
613                         return ((Category) value).getName();
614                     } catch (final NotDefinedException e) {
615                         return NewKeysPreferenceMessages.Unavailable_Category;
616                     }
617                 }
618
619                 return null;
620
621             } else if (value instanceof String JavaDoc) {
622                 // This is a context.
623
if (columnIndex == COLUMN_COMMAND) {
624                     try {
625                         return contextService.getContext((String JavaDoc) value)
626                                 .getName();
627                     } catch (final NotDefinedException e) {
628                         return NewKeysPreferenceMessages.Undefined_Context;
629                     }
630                 }
631
632                 return null;
633             } else if (value instanceof ParameterizedCommand) {
634                 if (columnIndex == COLUMN_COMMAND) {
635                     try {
636                         return ((ParameterizedCommand) value).getName();
637                     } catch (final NotDefinedException e) {
638                         return NewKeysPreferenceMessages.Undefined_Command;
639                     }
640                 }
641                 if (columnIndex == COLUMN_TRIGGER_SEQUENCE)
642                     return ""; //$NON-NLS-1$
643

644                 if (columnIndex == COLUMN_WHEN)
645                     return ""; //$NON-NLS-1$
646

647                 if (columnIndex == COLUMN_CATEGORY) {
648                     try {
649                         return ((ParameterizedCommand) value).getCommand()
650                                 .getCategory().getName();
651                     } catch (NotDefinedException e) {
652                         return NewKeysPreferenceMessages.Unavailable_Category;
653                     }
654                 }
655                 return null;
656             }
657
658             return null;
659         }
660
661         /*
662          * (non-Javadoc)
663          *
664          * @see org.eclipse.jface.viewers.LabelProvider#getText(java.lang.Object)
665          */

666         public String JavaDoc getText(Object JavaDoc element) {
667             String JavaDoc rc = getColumnText(element, 0);
668             if (rc == null) {
669                 super.getText(element);
670             }
671             StringBuffer JavaDoc buf = new StringBuffer JavaDoc(rc);
672             for (int i = 1; i < COLUMN_USER; i++) {
673                 String JavaDoc text = getColumnText(element, i);
674                 if (text != null) {
675                     buf.append(' ');
676                     buf.append(text);
677                 }
678             }
679             return buf.toString();
680         }
681     }
682
683     /**
684      * Sorts the bindings in the filtered tree based on the current grouping.
685      */

686     private final class BindingComparator extends ViewerComparator {
687
688         private int sortColumn = 0;
689
690         private int lastSortColumn = 0;
691
692         private boolean ascending = true;
693
694         public final int category(final Object JavaDoc element) {
695             switch (grouping) {
696             case GROUPING_CATEGORY:
697                 // TODO This has to be done with something other than the hash.
698
try {
699                     final ParameterizedCommand command = (element instanceof ParameterizedCommand) ? (ParameterizedCommand) element
700                             : ((Binding) element).getParameterizedCommand();
701                     return command.getCommand().getCategory().hashCode();
702                 } catch (final NotDefinedException e) {
703                     return 0;
704                 }
705             case GROUPING_CONTEXT:
706                 // TODO This has to be done with something other than the hash.
707
if (element instanceof Binding) {
708                     return ((Binding) element).getContextId().hashCode();
709                 }
710             case GROUPING_NONE:
711             default:
712                 return 0;
713             }
714         }
715
716         public final int compare(final Viewer viewer, final Object JavaDoc a,
717                 final Object JavaDoc b) {
718
719             int result = compareColumn(viewer, a, b, sortColumn);
720             if (result == 0 && sortColumn != lastSortColumn) {
721                 result = compareColumn(viewer, a, b, lastSortColumn);
722             }
723             return ascending ? result : (-1) * result;
724         }
725         
726         private int compareColumn(final Viewer viewer, final Object JavaDoc a, final Object JavaDoc b,
727                 final int columnNumber) {
728             if (columnNumber == BindingLabelProvider.COLUMN_USER) {
729                 return sortUser(viewer, a, b);
730             }
731             IBaseLabelProvider baseLabel = ((TreeViewer)viewer).getLabelProvider();
732             if (baseLabel instanceof ITableLabelProvider) {
733                 ITableLabelProvider tableProvider = (ITableLabelProvider) baseLabel;
734                 String JavaDoc e1p = tableProvider.getColumnText(a, columnNumber);
735                 String JavaDoc e2p = tableProvider.getColumnText(b, columnNumber);
736                 if (e1p != null && e2p != null) {
737                     return getComparator().compare(e1p, e2p);
738                 }
739             }
740             return 0;
741         }
742         
743         private int sortUser(final Viewer viewer, final Object JavaDoc a, final Object JavaDoc b) {
744             int typeA = (a instanceof Binding?((Binding)a).getType():Binding.SYSTEM);
745             int typeB = (b instanceof Binding?((Binding)b).getType():Binding.SYSTEM);
746             int result = typeA - typeB;
747             return result;
748         }
749
750         /**
751          * @return Returns the sortColumn.
752          */

753         public int getSortColumn() {
754             return sortColumn;
755         }
756
757         /**
758          * @param sortColumn
759          * The sortColumn to set.
760          */

761         public void setSortColumn(int sortColumn) {
762             lastSortColumn = this.sortColumn;
763             if (lastSortColumn != sortColumn) {
764                 ascending = true;
765             }
766             this.sortColumn = sortColumn;
767         }
768
769         /**
770          * @return Returns the ascending.
771          */

772         public boolean isAscending() {
773             return ascending;
774         }
775
776         /**
777          * @param ascending
778          * The ascending to set.
779          */

780         public void setAscending(boolean ascending) {
781             this.ascending = ascending;
782         }
783     }
784
785     /**
786      * The constant value for <code>grouping</code> when the bindings should
787      * be grouped by category.
788      */

789     private static final int GROUPING_CATEGORY = 0;
790
791     /**
792      * The constant value for <code>grouping</code> when the bindings should
793      * be grouped by context.
794      */

795     private static final int GROUPING_CONTEXT = 1;
796
797     /**
798      * The constant value for <code>grouping</code> when the bindings should
799      * not be grouped (i.e., they should be displayed in a flat list).
800      */

801     private static final int GROUPING_NONE = 2;
802
803     /**
804      * The path at which the icon for "groups of bindings" is located.
805      */

806     private static final String JavaDoc ICON_GROUP_OF_BINDINGS = "$nl$/icons/full/obj16/keygroups_obj.gif"; //$NON-NLS-1$
807

808     private static final String JavaDoc CONTEXT_ID_ACTION_SETS = "org.eclipse.ui.contexts.actionSet"; //$NON-NLS-1$
809

810     private static final String JavaDoc CONTEXT_ID_INTERNAL = ".internal."; //$NON-NLS-1$
811

812     /**
813      * The number of items to show in the bindings table tree.
814      */

815     private static final int ITEMS_TO_SHOW = 7;
816
817     /**
818      * A comparator that can be used for display of
819      * <code>NamedHandleObject</code> instances to the end user.
820      */

821     private static final NamedHandleObjectComparator NAMED_HANDLE_OBJECT_COMPARATOR = new NamedHandleObjectComparator();
822
823     public final static String JavaDoc TAG_DIALOG_SECTION = "org.eclipse.ui.preferences.keysPreferencePage"; //$NON-NLS-1$
824

825     private final String JavaDoc TAG_FIELD = "showAllField"; //$NON-NLS-1$
826

827     private static final String JavaDoc TAG_FILTER_ACTION_SETS = "actionSetFilter"; //$NON-NLS-1$
828

829     private static final String JavaDoc TAG_FILTER_INTERNAL = "internalFilter"; //$NON-NLS-1$
830

831     private static final String JavaDoc TAG_FILTER_UNCAT = "uncategorizedFilter"; //$NON-NLS-1$
832

833     /**
834      * Sorts the given array of <code>NamedHandleObject</code> instances based
835      * on their name. This is generally useful if they will be displayed to an
836      * end users.
837      *
838      * @param objects
839      * The objects to be sorted; must not be <code>null</code>.
840      * @return The same array, but sorted in place; never <code>null</code>.
841      */

842     private static final NamedHandleObject[] sortByName(
843             final NamedHandleObject[] objects) {
844         Arrays.sort(objects, NAMED_HANDLE_OBJECT_COMPARATOR);
845         return objects;
846     }
847
848     /**
849      * The workbench's binding service. This binding service is used to access
850      * the current set of bindings, and to persist changes.
851      */

852     private IBindingService bindingService;
853
854     /**
855      * The text widget containing the key sequence. This value is
856      * <code>null</code> until the controls are created.
857      */

858     private Text bindingText;
859
860     /**
861      * The workbench's command image service. This command image service is used
862      * to provide an icon beside each command.
863      */

864     private ICommandImageService commandImageService;
865
866     /**
867      * The label containing the name of the currently selected binding's
868      * command. This value is <code>null</code> until the controls are
869      * created.
870      */

871     private Label commandNameValueLabel;
872
873     /**
874      * The workbench's command service. This command service is used to access
875      * the list of commands.
876      */

877     private ICommandService commandService;
878
879     /**
880      * The workbench's context service. This context service is used to access
881      * the list of contexts.
882      */

883     private IContextService contextService;
884
885     /**
886      * The label containing the description of the currently selected binding's
887      * command. This value is <code>null</code> until the controls are
888      * created.
889      */

890     private Text descriptionValueText;
891
892     /**
893      * The filtered tree containing the list of commands and bindings to edit.
894      */

895     private CategoryFilterTree filteredTree;
896
897     private CategoryPatternFilter patternFilter;
898
899     /**
900      * The grouping for the bindings tree. Either there should be no group
901      * (i.e., flat list), or the bindings should be grouped by either category
902      * or context.
903      */

904     private int grouping = GROUPING_NONE;
905
906     /**
907      * The key sequence entry widget containing the trigger sequence for the
908      * currently selected binding. This value is <code>null</code> until the
909      * controls are created.
910      */

911     private KeySequenceText keySequenceText;
912
913     /**
914      * A binding manager local to this preference page. When the page is
915      * initialized, the current bindings are read out from the binding service
916      * and placed in this manager. This manager is then updated as the user
917      * makes changes. When the user has finished, the contents of this manager
918      * are compared with the contents of the binding service. The changes are
919      * then persisted.
920      */

921     private BindingManager localChangeManager;
922
923     /**
924      * The context id of the binding which the user is trying to add. This value
925      * is derived from the binding that is selected at the time the user tried
926      * to add a binding. If this value is <code>null</code>, then the user is
927      * not currently trying to add a binding to a command that already has a
928      * binding.
929      */

930     private String JavaDoc markedContextId = null;
931
932     /**
933      * The parameterized command to which the user is currently trying to add a
934      * binding. If this value is <code>null</code>, then the user is not
935      * currently trying to add a binding to a command that already has a
936      * binding.
937      */

938     private ParameterizedCommand markedParameterizedCommand = null;
939
940     /**
941      * The combo box containing the list of possible schemes to choose from.
942      * This value is <code>null</code> until the contents are created.
943      */

944     private ComboViewer schemeCombo = null;
945
946     /**
947      * The check box controlling whether all commands should be shown in the
948      * filtered tree. This value is <code>null</code> until the contents are
949      * created.
950      */

951     private Button showAllCheckBox = null;
952
953     private boolean filterActionSetContexts = true;
954
955     private boolean filterInternalContexts = true;
956
957     private IObservableSet commandModel;
958
959     private IObservableSet bindingModel;
960
961     private UnionSet model;
962
963     /**
964      * The combo box containing the list of possible contexts to choose from.
965      * This value is <code>null</code> until the contents are create.
966      */

967     private ComboViewer whenCombo = null;
968
969     /**
970      * Adds a new binding based on an existing binding. The command and the
971      * context are copied from the existing binding. The scheme id is set to be
972      * the user's personal derivative scheme. The preference page is updated,
973      * and focus is placed in the key sequence field.
974      *
975      * @param binding
976      * The binding to be added; must not be <code>null</code>.
977      */

978     private final void bindingAdd(final Binding binding) {
979         if (!(binding.getParameterizedCommand().getCommand().isDefined()))
980             return;
981
982         // Remember the parameterized command and context.
983
markedParameterizedCommand = binding.getParameterizedCommand();
984         markedContextId = binding.getContextId();
985
986         // Update the preference page.
987
update();
988
989         // Select the new binding.
990
filteredTree.getViewer().setSelection(
991                 new StructuredSelection(binding.getParameterizedCommand()),
992                 true);
993         bindingText.setFocus();
994         bindingText.selectAll();
995     }
996
997     /**
998      * Removes an existing binding. The preference page is then updated.
999      *
1000     * @param binding
1001     * The binding to be removed; must not be <code>null</code>.
1002     */

1003    private final void bindingRemove(final KeyBinding binding) {
1004        ArrayList JavaDoc extraSystemDeletes = new ArrayList JavaDoc();
1005        final String JavaDoc contextId = binding.getContextId();
1006        final String JavaDoc schemeId = binding.getSchemeId();
1007        final KeySequence triggerSequence = binding.getKeySequence();
1008        if (binding.getType() == Binding.USER) {
1009            localChangeManager.removeBinding(binding);
1010        } else {
1011            // TODO This should be the user's personal scheme.
1012
Collection JavaDoc previousConflictMatches = (Collection JavaDoc) localChangeManager
1013                    .getActiveBindingsDisregardingContext().get(
1014                            binding.getTriggerSequence());
1015            KeyBinding deleteBinding = new KeyBinding(triggerSequence, null,
1016                    schemeId, contextId, null, null, null, Binding.USER);
1017            localChangeManager.addBinding(deleteBinding);
1018            if (previousConflictMatches != null) {
1019                Iterator JavaDoc i = previousConflictMatches.iterator();
1020                while (i.hasNext()) {
1021                    Binding b = (Binding) i.next();
1022                    if (b != binding && deletes(deleteBinding, b)) {
1023                        extraSystemDeletes.add(b);
1024                    }
1025                }
1026            }
1027        }
1028
1029        // update the model
1030
bindingModel.remove(binding);
1031        bindingAdd(binding);
1032        if (!extraSystemDeletes.isEmpty()) {
1033            Iterator JavaDoc i = extraSystemDeletes.iterator();
1034            while (i.hasNext()) {
1035                KeyBinding b = (KeyBinding) i.next();
1036                KeyBinding newBinding = new KeyBinding(b.getKeySequence(), b
1037                        .getParameterizedCommand(), b.getSchemeId(), b
1038                        .getContextId(), null, null, null, Binding.USER);
1039                localChangeManager.addBinding(newBinding);
1040
1041                bindingModel.remove(b);
1042                bindingModel.add(newBinding);
1043            }
1044        }
1045        updateConflicts(binding);
1046    }
1047
1048    private final void updateConflicts(final Collection JavaDoc bindings) {
1049        Iterator JavaDoc i = bindings.iterator();
1050        while (i.hasNext()) {
1051            final Binding b = (Binding) i.next();
1052            if (b.getParameterizedCommand()!=null) {
1053                updateConflicts(b);
1054            }
1055        }
1056    }
1057
1058    private final void updateConflicts(final Binding binding) {
1059        Collection JavaDoc matches = (Collection JavaDoc) localChangeManager
1060                .getActiveBindingsDisregardingContext().get(
1061                        binding.getTriggerSequence());
1062        if (matches != null) {
1063            Iterator JavaDoc i = matches.iterator();
1064            while (i.hasNext()) {
1065                Binding b = (Binding) i.next();
1066                if (binding != b
1067                        && b.getContextId().equals(binding.getContextId())) {
1068                    filteredTree.getViewer().update(b, null);
1069                }
1070            }
1071        }
1072    }
1073
1074    private final void bindingRestore(final KeyBinding binding) {
1075        final ParameterizedCommand cmd = binding.getParameterizedCommand();
1076        bindingRestore(cmd, false);
1077    }
1078
1079    private String JavaDoc locale = Locale.getDefault().toString();
1080
1081    private boolean localMatches(String JavaDoc l) {
1082        if (l == null) {
1083            return true;
1084        }
1085        return Util.equals(locale, l);
1086    }
1087
1088    private String JavaDoc platform = SWT.getPlatform();
1089
1090    private boolean platformMatches(String JavaDoc p) {
1091        if (p == null) {
1092            return true;
1093        }
1094        return Util.equals(platform, p);
1095    }
1096    
1097    private final String JavaDoc[] getSchemeIds(String JavaDoc schemeId) {
1098        final List JavaDoc strings = new ArrayList JavaDoc();
1099        while (schemeId != null) {
1100            strings.add(schemeId);
1101            try {
1102                schemeId = bindingService.getScheme(schemeId).getParentId();
1103            } catch (final NotDefinedException e) {
1104                return new String JavaDoc[0];
1105            }
1106        }
1107
1108        return (String JavaDoc[]) strings.toArray(new String JavaDoc[strings.size()]);
1109    }
1110
1111    private final void bindingRestore(final ParameterizedCommand cmd,
1112            boolean removeCmd) {
1113        Set JavaDoc addSystemAll = new HashSet JavaDoc();
1114        ArrayList JavaDoc removeUser = new ArrayList JavaDoc();
1115        ArrayList JavaDoc removeBinding = new ArrayList JavaDoc();
1116        Binding[] bindings = localChangeManager.getBindings();
1117        for (int i = 0; i < bindings.length; i++) {
1118            final Binding b = bindings[i];
1119            if (b.getParameterizedCommand() == null
1120                    && localMatches(b.getLocale())
1121                    && platformMatches(b.getPlatform())) {
1122                // flat out, a delete marker
1123
removeBinding.add(b);
1124            } else if (cmd.equals(b.getParameterizedCommand())) {
1125                if (b.getType() == Binding.SYSTEM
1126                        && localMatches(b.getLocale())
1127                        && platformMatches(b.getPlatform())) {
1128                    // a system binding for this command
1129
addSystemAll.add(b);
1130                } else if (b.getType() == Binding.USER) {
1131                    // a user binding for this command
1132
removeUser.add(b);
1133                    localChangeManager.removeBinding(b);
1134                }
1135            }
1136        }
1137
1138        if (!addSystemAll.isEmpty()) {
1139            String JavaDoc[] activeSchemeIds = getSchemeIds(getSchemeId());
1140            Binding[] sysArray = (Binding[]) addSystemAll
1141                    .toArray(new Binding[addSystemAll.size()]);
1142            for (int k = 0; k < sysArray.length; k++) {
1143                Binding sys = sysArray[k];
1144                boolean deleted = false;
1145                for (Iterator JavaDoc i = removeBinding.iterator(); i.hasNext();) {
1146                    Binding del = (Binding) i.next();
1147                    if (deletes(del, sys)) {
1148                        if (del.getType() == Binding.USER) {
1149                            removeUser.add(del);
1150                            localChangeManager.removeBinding(del);
1151                        } else {
1152                            deleted = true;
1153                            addSystemAll.remove(sys);
1154                        }
1155                    }
1156                }
1157                // Check the scheme ids.
1158
final String JavaDoc schemeId = sys.getSchemeId();
1159                boolean found = false;
1160                if (activeSchemeIds != null && !deleted) {
1161                    for (int j = 0; j < activeSchemeIds.length; j++) {
1162                        if (Util.equals(schemeId, activeSchemeIds[j])) {
1163                            found = true;
1164                            break;
1165                        }
1166                    }
1167                }
1168                if (!found && sys.getType() == Binding.SYSTEM) {
1169                    addSystemAll.remove(sys);
1170                }
1171            }
1172        }
1173        
1174
1175        bindingModel.addAll(addSystemAll);
1176        bindingModel.removeAll(removeUser);
1177        updateConflicts(addSystemAll);
1178        updateConflicts(removeUser);
1179        if (addSystemAll.isEmpty()) {
1180            commandModel.add(cmd);
1181            filteredTree.getViewer().setSelection(new StructuredSelection(cmd),
1182                    true);
1183        } else if (removeCmd) {
1184            commandModel.remove(cmd);
1185        }
1186        if (!addSystemAll.isEmpty()) {
1187            // Select the new binding.
1188
filteredTree.getViewer().setSelection(
1189                    new StructuredSelection(addSystemAll.iterator().next()),
1190                    true);
1191        }
1192
1193        update();
1194    }
1195
1196    final static boolean deletes(final Binding del, final Binding binding) {
1197        boolean deletes = true;
1198        deletes &= Util.equals(del.getContextId(), binding.getContextId());
1199        deletes &= Util.equals(del.getTriggerSequence(), binding
1200                .getTriggerSequence());
1201        if (del.getLocale() != null) {
1202            deletes &= Util.equals(del.getLocale(), binding.getLocale());
1203        }
1204        if (del.getPlatform() != null) {
1205            deletes &= Util.equals(del.getPlatform(), binding.getPlatform());
1206        }
1207        deletes &= (binding.getType() == Binding.SYSTEM);
1208        deletes &= Util.equals(del.getParameterizedCommand(), null);
1209
1210        return deletes;
1211    }
1212
1213    /**
1214     * Creates the button bar across the bottom of the preference page. This
1215     * button bar contains the "Advanced..." button.
1216     *
1217     * @param parent
1218     * The composite in which the button bar should be placed; never
1219     * <code>null</code>.
1220     * @return The button bar composite; never <code>null</code>.
1221     */

1222    private final Control createButtonBar(final Composite parent) {
1223        GridLayout layout;
1224        GridData gridData;
1225        int widthHint;
1226
1227        // Create the composite to house the button bar.
1228
final Composite buttonBar = new Composite(parent, SWT.NONE);
1229        layout = new GridLayout(1, false);
1230        layout.marginWidth = 0;
1231        buttonBar.setLayout(layout);
1232        gridData = new GridData();
1233        gridData.horizontalAlignment = SWT.END;
1234        buttonBar.setLayoutData(gridData);
1235
1236        // Advanced button.
1237
final Button advancedButton = new Button(buttonBar, SWT.PUSH);
1238        gridData = new GridData();
1239        widthHint = convertHorizontalDLUsToPixels(IDialogConstants.BUTTON_WIDTH);
1240        advancedButton.setText(NewKeysPreferenceMessages.AdvancedButton_Text);
1241        gridData.widthHint = Math.max(widthHint, advancedButton.computeSize(
1242                SWT.DEFAULT, SWT.DEFAULT, true).x) + 5;
1243        advancedButton.setLayoutData(gridData);
1244        advancedButton.addSelectionListener(new SelectionListener() {
1245            public void widgetDefaultSelected(SelectionEvent e) {
1246            }
1247
1248            public void widgetSelected(SelectionEvent e) {
1249                KeysPreferenceFiltersDialog dialog = new KeysPreferenceFiltersDialog(
1250                        getShell());
1251                dialog.setFilterActionSet(filterActionSetContexts);
1252                dialog.setFilterInternal(filterInternalContexts);
1253                dialog.setFilterUncategorized(filteredTree.isFilteringCategories());
1254                if (dialog.open() == Window.OK) {
1255                    filterActionSetContexts = dialog.getFilterActionSet();
1256                    filterInternalContexts = dialog.getFilterInternal();
1257                    filteredTree.filterCategories(dialog
1258                            .getFilterUncategorized());
1259                    whenCombo.setInput(getContexts());
1260                    updateDataControls();
1261                }
1262            }
1263        });
1264        return buttonBar;
1265    }
1266
1267    /*
1268     * (non-Javadoc)
1269     *
1270     * @see org.eclipse.jface.preference.PreferencePage#createContents(org.eclipse.swt.widgets.Composite)
1271     */

1272    protected final Control createContents(final Composite parent) {
1273        PlatformUI.getWorkbench().getHelpSystem().setHelp(parent,
1274                IWorkbenchHelpContextIds.KEYS_PREFERENCE_PAGE);
1275        
1276        GridLayout layout = null;
1277
1278        long startTime = 0L;
1279        if (DEBUG) {
1280            startTime = System.currentTimeMillis();
1281        }
1282
1283        IDialogSettings settings = getDialogSettings();
1284        if (settings.get(TAG_FILTER_ACTION_SETS) != null) {
1285            filterActionSetContexts = settings
1286                    .getBoolean(TAG_FILTER_ACTION_SETS);
1287        }
1288        if (settings.get(TAG_FILTER_INTERNAL) != null) {
1289            filterInternalContexts = settings.getBoolean(TAG_FILTER_INTERNAL);
1290        }
1291        patternFilter = new CategoryPatternFilter(
1292                true, commandService.getCategory(null));
1293        if (settings.get(TAG_FILTER_UNCAT) != null) {
1294            patternFilter.filterCategories(settings
1295                    .getBoolean(TAG_FILTER_UNCAT));
1296        }
1297
1298        // Creates a composite to hold all of the page contents.
1299
final Composite page = new Composite(parent, SWT.NONE);
1300        layout = new GridLayout(1, false);
1301        layout.marginWidth = 0;
1302        page.setLayout(layout);
1303
1304        createSchemeControls(page);
1305        createTree(page);
1306        createTreeControls(page);
1307        createDataControls(page);
1308        createButtonBar(page);
1309
1310        fill();
1311        update();
1312
1313        applyDialogFont(page);
1314
1315        if (DEBUG) {
1316            final long elapsedTime = System.currentTimeMillis() - startTime;
1317            Tracing.printTrace(TRACING_COMPONENT, "Created page in " //$NON-NLS-1$
1318
+ elapsedTime + "ms"); //$NON-NLS-1$
1319
}
1320
1321        return page;
1322    }
1323
1324    private final Control createDataControls(final Composite parent) {
1325        GridLayout layout;
1326        GridData gridData;
1327
1328        // Creates the data area.
1329
final Composite dataArea = new Composite(parent, SWT.NONE);
1330        layout = new GridLayout(2, true);
1331        layout.marginWidth = 0;
1332        dataArea.setLayout(layout);
1333        gridData = new GridData();
1334        gridData.grabExcessHorizontalSpace = true;
1335        gridData.horizontalAlignment = SWT.FILL;
1336        dataArea.setLayoutData(gridData);
1337
1338        // LEFT DATA AREA
1339
// Creates the left data area.
1340
final Composite leftDataArea = new Composite(dataArea, SWT.NONE);
1341        layout = new GridLayout(3, false);
1342        leftDataArea.setLayout(layout);
1343        gridData = new GridData();
1344        gridData.grabExcessHorizontalSpace = true;
1345        gridData.verticalAlignment = SWT.TOP;
1346        gridData.horizontalAlignment = SWT.FILL;
1347        leftDataArea.setLayoutData(gridData);
1348
1349        // The command name label.
1350
final Label commandNameLabel = new Label(leftDataArea, SWT.NONE);
1351        commandNameLabel
1352                .setText(NewKeysPreferenceMessages.CommandNameLabel_Text);
1353
1354        // The current command name.
1355
commandNameValueLabel = new Label(leftDataArea, SWT.NONE);
1356        gridData = new GridData();
1357        gridData.grabExcessHorizontalSpace = true;
1358        gridData.horizontalSpan = 2;
1359        gridData.horizontalAlignment = SWT.FILL;
1360        commandNameValueLabel.setLayoutData(gridData);
1361
1362        // The binding label.
1363
final Label bindingLabel = new Label(leftDataArea, SWT.NONE);
1364        bindingLabel.setText(NewKeysPreferenceMessages.BindingLabel_Text);
1365
1366        // The key sequence entry widget.
1367
bindingText = new Text(leftDataArea, SWT.BORDER);
1368        gridData = new GridData();
1369        gridData.grabExcessHorizontalSpace = true;
1370        gridData.horizontalAlignment = SWT.FILL;
1371        gridData.widthHint = 200;
1372        bindingText.setLayoutData(gridData);
1373
1374        bindingText.addFocusListener(new FocusListener() {
1375            public void focusGained(FocusEvent e) {
1376                bindingService.setKeyFilterEnabled(false);
1377            }
1378
1379            public void focusLost(FocusEvent e) {
1380                bindingService.setKeyFilterEnabled(true);
1381            }
1382        });
1383        bindingText.addDisposeListener(new DisposeListener() {
1384            public void widgetDisposed(DisposeEvent e) {
1385                if (!bindingService.isKeyFilterEnabled()) {
1386                    bindingService.setKeyFilterEnabled(true);
1387                }
1388            }
1389        });
1390
1391        keySequenceText = new KeySequenceText(bindingText);
1392        keySequenceText.setKeyStrokeLimit(4);
1393        keySequenceText
1394                .addPropertyChangeListener(new IPropertyChangeListener() {
1395                    public final void propertyChange(
1396                            final PropertyChangeEvent event) {
1397                        if (!event.getOldValue().equals(event.getNewValue())) {
1398                            keySequenceChanged();
1399                        }
1400                    }
1401                });
1402
1403        // Button for adding trapped key strokes
1404
final Button addKeyButton = new Button(leftDataArea, SWT.LEFT
1405                | SWT.ARROW);
1406        addKeyButton
1407                .setToolTipText(NewKeysPreferenceMessages.AddKeyButton_ToolTipText);
1408        gridData = new GridData();
1409        gridData.heightHint = schemeCombo.getCombo().getTextHeight();
1410        addKeyButton.setLayoutData(gridData);
1411
1412        // Arrow buttons aren't normally added to the tab list. Let's fix that.
1413
final Control[] tabStops = dataArea.getTabList();
1414        final ArrayList JavaDoc newTabStops = new ArrayList JavaDoc();
1415        for (int i = 0; i < tabStops.length; i++) {
1416            Control tabStop = tabStops[i];
1417            newTabStops.add(tabStop);
1418            if (bindingText.equals(tabStop)) {
1419                newTabStops.add(addKeyButton);
1420            }
1421        }
1422        final Control[] newTabStopArray = (Control[]) newTabStops
1423                .toArray(new Control[newTabStops.size()]);
1424        dataArea.setTabList(newTabStopArray);
1425
1426        // Construct the menu to attach to the above button.
1427
final Menu addKeyMenu = new Menu(addKeyButton);
1428        final Iterator JavaDoc trappedKeyItr = KeySequenceText.TRAPPED_KEYS.iterator();
1429        while (trappedKeyItr.hasNext()) {
1430            final KeyStroke trappedKey = (KeyStroke) trappedKeyItr.next();
1431            final MenuItem menuItem = new MenuItem(addKeyMenu, SWT.PUSH);
1432            menuItem.setText(trappedKey.format());
1433            menuItem.addSelectionListener(new SelectionAdapter() {
1434
1435                public void widgetSelected(SelectionEvent e) {
1436                    keySequenceText.insert(trappedKey);
1437                    bindingText.setFocus();
1438                    bindingText.setSelection(bindingText.getTextLimit());
1439                }
1440            });
1441        }
1442        addKeyButton.addSelectionListener(new SelectionAdapter() {
1443
1444            public void widgetSelected(SelectionEvent selectionEvent) {
1445                Point buttonLocation = addKeyButton.getLocation();
1446                buttonLocation = dataArea.toDisplay(buttonLocation.x,
1447                        buttonLocation.y);
1448                Point buttonSize = addKeyButton.getSize();
1449                addKeyMenu.setLocation(buttonLocation.x, buttonLocation.y
1450                        + buttonSize.y);
1451                addKeyMenu.setVisible(true);
1452            }
1453        });
1454
1455        final IObservableValue selection = ViewersObservables
1456                .observeSingleSelection(filteredTree.getViewer());
1457
1458        // The when label.
1459
final Label whenLabel = new Label(leftDataArea, SWT.NONE);
1460        whenLabel.setText(NewKeysPreferenceMessages.WhenLabel_Text);
1461
1462        // The when combo.
1463
whenCombo = new ComboViewer(leftDataArea);
1464        gridData = new GridData();
1465        gridData.grabExcessHorizontalSpace = true;
1466        gridData.horizontalAlignment = SWT.FILL;
1467        gridData.horizontalSpan = 2;
1468        whenCombo.getCombo().setLayoutData(gridData);
1469        whenCombo.setLabelProvider(new NamedHandleObjectLabelProvider());
1470        whenCombo.setContentProvider(new ArrayContentProvider());
1471        whenCombo.setComparator(new ViewerComparator());
1472        whenCombo
1473                .addPostSelectionChangedListener(new ISelectionChangedListener() {
1474
1475                    public void selectionChanged(SelectionChangedEvent event) {
1476                        updateWhenCombo();
1477                    }
1478                });
1479
1480        whenCombo.getCombo().setVisibleItemCount(20);
1481        whenCombo.getCombo().setVisible(false);
1482        whenLabel.setVisible(false);
1483        selection.addValueChangeListener(new IValueChangeListener() {
1484
1485            public void handleValueChange(ValueChangeEvent event) {
1486                boolean visible = false;
1487                if (selection.getValue() instanceof KeyBinding) {
1488                    visible = true;
1489                }
1490                Combo combo = whenCombo.getCombo();
1491                if (!combo.isDisposed()) {
1492                    combo.setVisible(visible);
1493                }
1494                if (!whenLabel.isDisposed()) {
1495                    whenLabel.setVisible(visible);
1496                }
1497            }
1498        });
1499        
1500        final Label asterisk = new Label(leftDataArea, SWT.NONE);
1501        asterisk.setText(NewKeysPreferenceMessages.Asterisk_Text);
1502        gridData = new GridData();
1503        gridData.grabExcessHorizontalSpace = true;
1504        gridData.horizontalSpan = 2;
1505        gridData.horizontalAlignment = SWT.FILL;
1506        asterisk.setLayoutData(gridData);
1507
1508        // RIGHT DATA AREA
1509
// Creates the right data area.
1510
final Composite rightDataArea = new Composite(dataArea, SWT.NONE);
1511        layout = new GridLayout(1, false);
1512        rightDataArea.setLayout(layout);
1513        gridData = new GridData(SWT.FILL, SWT.FILL, true, true);
1514        rightDataArea.setLayoutData(gridData);
1515
1516        // The description label.
1517
final Label descriptionLabel = new Label(rightDataArea, SWT.NONE);
1518        descriptionLabel
1519                .setText(NewKeysPreferenceMessages.DescriptionLabel_Text);
1520        gridData = new GridData();
1521        gridData.grabExcessHorizontalSpace = true;
1522        gridData.horizontalAlignment = SWT.FILL;
1523        descriptionLabel.setLayoutData(gridData);
1524
1525        // The description value.
1526
descriptionValueText = new Text(rightDataArea, SWT.BORDER | SWT.MULTI
1527                | SWT.READ_ONLY | SWT.WRAP | SWT.V_SCROLL | SWT.H_SCROLL);
1528        gridData = new GridData(SWT.FILL, SWT.FILL, true, true);
1529        gridData.horizontalIndent = 20;
1530        descriptionValueText.setLayoutData(gridData);
1531        return dataArea;
1532    }
1533
1534    private final Control createSchemeControls(final Composite parent) {
1535        GridLayout layout;
1536        GridData gridData;
1537
1538        // Create a composite to hold the controls.
1539
final Composite schemeControls = new Composite(parent, SWT.NONE);
1540        layout = new GridLayout(3, false);
1541        layout.marginWidth = 0;
1542        schemeControls.setLayout(layout);
1543        gridData = new GridData();
1544        gridData.grabExcessHorizontalSpace = true;
1545        gridData.horizontalAlignment = SWT.FILL;
1546        schemeControls.setLayoutData(gridData);
1547
1548        // Create the label.
1549
final Label schemeLabel = new Label(schemeControls, SWT.NONE);
1550        schemeLabel.setText(NewKeysPreferenceMessages.SchemeLabel_Text);
1551
1552        // Create the combo.
1553
schemeCombo = new ComboViewer(schemeControls);
1554        schemeCombo.setLabelProvider(new NamedHandleObjectLabelProvider());
1555        schemeCombo.setContentProvider(new ArrayContentProvider());
1556        gridData = new GridData();
1557        gridData.widthHint = 150;
1558        gridData.horizontalAlignment = SWT.FILL;
1559        schemeCombo.getCombo().setLayoutData(gridData);
1560        schemeCombo
1561                .addSelectionChangedListener(new ISelectionChangedListener() {
1562                    public final void selectionChanged(
1563                            final SelectionChangedEvent event) {
1564                        selectSchemeCombo(event);
1565                    }
1566                });
1567
1568        return schemeControls;
1569    }
1570
1571    private final Control createTree(final Composite parent) {
1572        GridData gridData;
1573
1574        filteredTree = new CategoryFilterTree(parent, SWT.SINGLE
1575                | SWT.FULL_SELECTION | SWT.BORDER, patternFilter);
1576        final GridLayout layout = new GridLayout(1, false);
1577        layout.marginWidth = 0;
1578        filteredTree.setLayout(layout);
1579        gridData = new GridData();
1580        gridData.grabExcessHorizontalSpace = true;
1581        gridData.grabExcessVerticalSpace = true;
1582        gridData.horizontalAlignment = SWT.FILL;
1583        gridData.verticalAlignment = SWT.FILL;
1584        filteredTree.setLayoutData(gridData);
1585
1586        // Make sure the filtered tree has a height of ITEMS_TO_SHOW
1587
final Tree tree = filteredTree.getViewer().getTree();
1588        tree.setHeaderVisible(true);
1589        final Object JavaDoc layoutData = tree.getLayoutData();
1590        if (layoutData instanceof GridData) {
1591            gridData = (GridData) layoutData;
1592            final int itemHeight = tree.getItemHeight();
1593            if (itemHeight > 1) {
1594                gridData.heightHint = ITEMS_TO_SHOW * itemHeight;
1595            }
1596        }
1597
1598        final BindingComparator comparator = new BindingComparator();
1599        comparator.setSortColumn(0);
1600
1601        // Create the columns for the tree.
1602

1603        final TreeColumn commandNameColumn = new TreeColumn(tree, SWT.LEFT,
1604                BindingLabelProvider.COLUMN_COMMAND);
1605        commandNameColumn
1606                .setText(NewKeysPreferenceMessages.CommandNameColumn_Text);
1607        tree.setSortColumn(commandNameColumn);
1608        tree.setSortDirection(comparator.isAscending() ? SWT.UP : SWT.DOWN);
1609        commandNameColumn.addSelectionListener(new ResortColumn(comparator,
1610                commandNameColumn, tree, BindingLabelProvider.COLUMN_COMMAND));
1611
1612        final TreeColumn triggerSequenceColumn = new TreeColumn(tree, SWT.LEFT,
1613                BindingLabelProvider.COLUMN_TRIGGER_SEQUENCE);
1614        triggerSequenceColumn
1615                .setText(NewKeysPreferenceMessages.TriggerSequenceColumn_Text);
1616        triggerSequenceColumn.addSelectionListener(new ResortColumn(comparator,
1617                triggerSequenceColumn, tree,
1618                BindingLabelProvider.COLUMN_TRIGGER_SEQUENCE));
1619
1620        final TreeColumn whenColumn = new TreeColumn(tree, SWT.LEFT,
1621                BindingLabelProvider.COLUMN_WHEN);
1622        whenColumn.setText(NewKeysPreferenceMessages.WhenColumn_Text);
1623        whenColumn.addSelectionListener(new ResortColumn(comparator,
1624                whenColumn, tree, BindingLabelProvider.COLUMN_WHEN));
1625
1626        final TreeColumn categoryColumn = new TreeColumn(tree, SWT.LEFT,
1627                BindingLabelProvider.COLUMN_CATEGORY);
1628        categoryColumn.setText(NewKeysPreferenceMessages.CategoryColumn_Text);
1629        categoryColumn.addSelectionListener(new ResortColumn(comparator,
1630                categoryColumn, tree, BindingLabelProvider.COLUMN_CATEGORY));
1631
1632        final TreeColumn userMarker = new TreeColumn(tree, SWT.LEFT,
1633                BindingLabelProvider.COLUMN_USER);
1634        userMarker.setText(NewKeysPreferenceMessages.UserColumn_Text);
1635        userMarker.addSelectionListener(new ResortColumn(comparator,
1636                userMarker, tree, BindingLabelProvider.COLUMN_USER));
1637
1638        // Set up the providers for the viewer.
1639
final TreeViewer viewer = filteredTree.getViewer();
1640        viewer.setLabelProvider(new BindingLabelProvider());
1641
1642        viewer.setContentProvider(new ObservableSetContentProvider());
1643
1644        viewer.setComparator(comparator);
1645
1646        /*
1647         * Listen for selection changes so that the data controls can be
1648         * updated.
1649         */

1650        viewer.addSelectionChangedListener(new ISelectionChangedListener() {
1651            public final void selectionChanged(final SelectionChangedEvent event) {
1652                selectTreeRow(event);
1653            }
1654        });
1655
1656        // Adjust how the filter works.
1657
filteredTree.getPatternFilter().setIncludeLeadingWildcard(true);
1658        return filteredTree;
1659    }
1660
1661    private final Control createTreeControls(final Composite parent) {
1662        GridLayout layout;
1663        GridData gridData;
1664        int widthHint;
1665
1666        // Creates controls related to the tree.
1667
final Composite treeControls = new Composite(parent, SWT.NONE);
1668        layout = new GridLayout(4, false);
1669        layout.marginWidth = 0;
1670        treeControls.setLayout(layout);
1671        gridData = new GridData();
1672        gridData.grabExcessHorizontalSpace = true;
1673        gridData.horizontalAlignment = SWT.FILL;
1674        treeControls.setLayoutData(gridData);
1675
1676        // Create the show all check box.
1677
showAllCheckBox = new Button(treeControls, SWT.CHECK);
1678        gridData = new GridData();
1679        gridData.grabExcessHorizontalSpace = true;
1680        gridData.horizontalAlignment = SWT.FILL;
1681        gridData.verticalAlignment = SWT.TOP;
1682        showAllCheckBox.setLayoutData(gridData);
1683        showAllCheckBox.setText(NewKeysPreferenceMessages.ShowAllCheckBox_Text);
1684        IDialogSettings settings = getDialogSettings();
1685        showAllCheckBox.setSelection(settings.getBoolean(TAG_FIELD));
1686        showAllCheckBox.addSelectionListener(new SelectionAdapter() {
1687            public void widgetSelected(SelectionEvent e) {
1688                updateShowAll();
1689            }
1690        });
1691
1692        final IObservableValue selection = ViewersObservables
1693                .observeSingleSelection(filteredTree.getViewer());
1694
1695        // Create the delete binding button.
1696
final Button addBindingButton = new Button(treeControls, SWT.PUSH);
1697        gridData = new GridData();
1698        widthHint = convertHorizontalDLUsToPixels(IDialogConstants.BUTTON_WIDTH);
1699        addBindingButton
1700                .setText(NewKeysPreferenceMessages.AddBindingButton_Text);
1701        gridData.widthHint = Math.max(widthHint, addBindingButton.computeSize(
1702                SWT.DEFAULT, SWT.DEFAULT, true).x) + 5;
1703        addBindingButton.setLayoutData(gridData);
1704        addBindingButton.addSelectionListener(new SelectionAdapter() {
1705            public final void widgetSelected(final SelectionEvent event) {
1706                selectAddBindingButton(event);
1707            }
1708        });
1709        new ControlUpdater(addBindingButton) {
1710            protected void updateControl() {
1711                Object JavaDoc selectedObject = selection.getValue();
1712                addBindingButton
1713                        .setEnabled(selectedObject instanceof KeyBinding);
1714            }
1715        };
1716
1717        // Create the delete binding button.
1718
final Button removeBindingButton = new Button(treeControls, SWT.PUSH);
1719        gridData = new GridData();
1720        widthHint = convertHorizontalDLUsToPixels(IDialogConstants.BUTTON_WIDTH);
1721        removeBindingButton
1722                .setText(NewKeysPreferenceMessages.RemoveBindingButton_Text);
1723        gridData.widthHint = Math.max(widthHint, removeBindingButton
1724                .computeSize(SWT.DEFAULT, SWT.DEFAULT, true).x) + 5;
1725        removeBindingButton.setLayoutData(gridData);
1726        removeBindingButton.addSelectionListener(new SelectionAdapter() {
1727            public final void widgetSelected(final SelectionEvent event) {
1728                selectRemoveBindingButton(event);
1729            }
1730        });
1731        new ControlUpdater(removeBindingButton) {
1732            protected void updateControl() {
1733                Object JavaDoc selectedObject = selection.getValue();
1734                removeBindingButton
1735                        .setEnabled(selectedObject instanceof KeyBinding);
1736            }
1737        };
1738
1739        // Create the delete binding button.
1740
final Button restore = new Button(treeControls, SWT.PUSH);
1741        gridData = new GridData();
1742        widthHint = convertHorizontalDLUsToPixels(IDialogConstants.BUTTON_WIDTH);
1743        restore.setText(NewKeysPreferenceMessages.RestoreBindingButton_Text);
1744        gridData.widthHint = Math.max(widthHint, restore.computeSize(
1745                SWT.DEFAULT, SWT.DEFAULT, true).x) + 5;
1746        restore.setLayoutData(gridData);
1747        restore.addSelectionListener(new SelectionAdapter() {
1748            public final void widgetSelected(final SelectionEvent event) {
1749                selectRestoreBindingButton(event);
1750            }
1751        });
1752
1753        return treeControls;
1754    }
1755
1756    private void updateShowAll() {
1757        BusyIndicator.showWhile(
1758                filteredTree.getViewer().getTree().getDisplay(),
1759                new Runnable JavaDoc() {
1760                    public void run() {
1761                        try {
1762                            filteredTree.getViewer().getTree().setRedraw(false);
1763                            fillInCommands();
1764                        } finally {
1765                            filteredTree.getViewer().getTree().setRedraw(true);
1766                        }
1767                    }
1768                });
1769    }
1770
1771    /**
1772     * Copies all of the information from the workbench into a local change
1773     * manager, and then the local change manager is used to populate the
1774     * contents of the various widgets on the page.
1775     *
1776     * The widgets affected by this method are: scheme combo, bindings
1777     * table/tree model, and the when combo.
1778     */

1779    private final void fill() {
1780        // Make an internal binding manager to track changes.
1781
localChangeManager = new BindingManager(new ContextManager(),
1782                new CommandManager());
1783        final Scheme[] definedSchemes = bindingService.getDefinedSchemes();
1784        try {
1785            for (int i = 0; i < definedSchemes.length; i++) {
1786                final Scheme scheme = definedSchemes[i];
1787                final Scheme copy = localChangeManager
1788                        .getScheme(scheme.getId());
1789                copy.define(scheme.getName(), scheme.getDescription(), scheme
1790                        .getParentId());
1791            }
1792            localChangeManager
1793                    .setActiveScheme(bindingService.getActiveScheme());
1794        } catch (final NotDefinedException e) {
1795            throw new Error JavaDoc(
1796                    "There is a programmer error in the keys preference page"); //$NON-NLS-1$
1797
}
1798        localChangeManager.setLocale(bindingService.getLocale());
1799        localChangeManager.setPlatform(bindingService.getPlatform());
1800        localChangeManager.setBindings(bindingService.getBindings());
1801
1802        // Update the scheme combo.
1803
schemeCombo
1804                .setInput(sortByName(localChangeManager.getDefinedSchemes()));
1805        setScheme(localChangeManager.getActiveScheme());
1806
1807        // Update the when combo.
1808
whenCombo.setInput(getContexts());
1809
1810        commandModel = new WritableSet();
1811        bindingModel = new WritableSet();
1812        model = new UnionSet(
1813                new IObservableSet[] { bindingModel, commandModel });
1814
1815        bindingModel.addAll(localChangeManager
1816                .getActiveBindingsDisregardingContextFlat());
1817        fillInCommands();
1818
1819        if (DEBUG) {
1820            Tracing.printTrace(TRACING_COMPONENT,
1821                    "fill in size: " + model.size()); //$NON-NLS-1$
1822
}
1823
1824        filteredTree.getViewer().setInput(model);
1825    }
1826
1827    /**
1828     *
1829     */

1830    private void fillInCommands() {
1831        long startTime = 0L;
1832        if (DEBUG) {
1833            startTime = System.currentTimeMillis();
1834        }
1835        if (showAllCheckBox.getSelection()) {
1836            final Collection JavaDoc commandIds = commandService.getDefinedCommandIds();
1837            final Collection JavaDoc commands = new HashSet JavaDoc();
1838            final Iterator JavaDoc commandIdItr = commandIds.iterator();
1839            while (commandIdItr.hasNext()) {
1840                final String JavaDoc currentCommandId = (String JavaDoc) commandIdItr.next();
1841                final Command currentCommand = commandService
1842                        .getCommand(currentCommandId);
1843                try {
1844                    commands.addAll(ParameterizedCommand
1845                            .generateCombinations(currentCommand));
1846                } catch (final NotDefinedException e) {
1847                    // It is safe to just ignore undefined commands.
1848
}
1849            }
1850
1851            // Remove duplicates.
1852
Iterator JavaDoc i = bindingModel.iterator();
1853            while (i.hasNext()) {
1854                commands.remove(((Binding) i.next()).getParameterizedCommand());
1855            }
1856
1857            commandModel.addAll(commands);
1858        } else {
1859            commandModel.clear();
1860        }
1861
1862        if (DEBUG) {
1863            final long elapsedTime = System.currentTimeMillis() - startTime;
1864            Tracing.printTrace(TRACING_COMPONENT, "fillInCommands in " //$NON-NLS-1$
1865
+ elapsedTime + "ms"); //$NON-NLS-1$
1866
}
1867    }
1868
1869    /*
1870     * (non-Javadoc)
1871     *
1872     * @see org.eclipse.ui.IWorkbenchPreferencePage#init(org.eclipse.ui.IWorkbench)
1873     */

1874    public final void init(final IWorkbench workbench) {
1875        bindingService = (IBindingService) workbench
1876                .getService(IBindingService.class);
1877        commandImageService = (ICommandImageService) workbench
1878                .getService(ICommandImageService.class);
1879        commandService = (ICommandService) workbench
1880                .getService(ICommandService.class);
1881        contextService = (IContextService) workbench
1882                .getService(IContextService.class);
1883    }
1884
1885    /**
1886     * Updates the interface as the key sequence has changed. This finds the
1887     * selected item. If the selected item is a binding, then it updates the
1888     * binding -- either by updating a user binding, or doing the deletion
1889     * marker dance with a system binding. If the selected item is a
1890     * parameterized command, then a binding is created based on the data
1891     * controls.
1892     */

1893    private final void keySequenceChanged() {
1894        long startTime = 0L;
1895        if (DEBUG) {
1896            startTime = System.currentTimeMillis();
1897        }
1898
1899        final KeySequence keySequence = keySequenceText.getKeySequence();
1900        if (!keySequence.isComplete()) {
1901            return;
1902        }
1903
1904        if ((keySequence == null) || (keySequence.isEmpty())) {
1905            ISelection selection = filteredTree.getViewer().getSelection();
1906            if (selection instanceof IStructuredSelection) {
1907                IStructuredSelection structuredSelection = (IStructuredSelection) selection;
1908                final Object JavaDoc node = structuredSelection.getFirstElement();
1909                if (node instanceof KeyBinding) {
1910                    bindingRemove((KeyBinding) node);
1911                }
1912            }
1913            return;
1914        }
1915
1916        ISelection selection = filteredTree.getViewer().getSelection();
1917        if (selection instanceof IStructuredSelection) {
1918            IStructuredSelection structuredSelection = (IStructuredSelection) selection;
1919            final Object JavaDoc node = structuredSelection.getFirstElement();
1920            if (node != null) {
1921                final Object JavaDoc object = node;
1922                selection = whenCombo.getSelection();
1923                final String JavaDoc contextId;
1924                if (selection instanceof IStructuredSelection) {
1925                    structuredSelection = (IStructuredSelection) selection;
1926                    final Object JavaDoc firstElement = structuredSelection
1927                            .getFirstElement();
1928                    if (firstElement == null) {
1929                        contextId = IContextIds.CONTEXT_ID_WINDOW;
1930                    } else {
1931                        contextId = ((Context) firstElement).getId();
1932                    }
1933                } else {
1934                    contextId = IContextIds.CONTEXT_ID_WINDOW;
1935                }
1936                if (object instanceof KeyBinding) {
1937                    KeyBinding keyBinding = (KeyBinding) object;
1938                    if (!keyBinding.getContextId().equals(contextId)
1939                            || !keyBinding.getKeySequence().equals(keySequence)) {
1940                        final KeyBinding binding = new KeyBinding(
1941                                keySequence,
1942                                keyBinding.getParameterizedCommand(),
1943                                getSchemeId(),
1944                                contextId, null, null, null, Binding.USER);
1945
1946                        ArrayList JavaDoc extraSystemDeletes = new ArrayList JavaDoc();
1947                        if (keyBinding.getType() == Binding.USER) {
1948                            localChangeManager.removeBinding(keyBinding);
1949                        } else {
1950                            // TODO This should be the user's personal scheme.
1951
Collection JavaDoc previousConflictMatches = (Collection JavaDoc) localChangeManager
1952                                    .getActiveBindingsDisregardingContext().get(
1953                                            keyBinding.getTriggerSequence());
1954                            KeyBinding deleteBinding = new KeyBinding(
1955                                    keyBinding.getKeySequence(), null,
1956                                    keyBinding.getSchemeId(), keyBinding
1957                                            .getContextId(), null, null, null,
1958                                    Binding.USER);
1959                            localChangeManager.addBinding(deleteBinding);
1960                            if (previousConflictMatches != null) {
1961                                Iterator JavaDoc i = previousConflictMatches.iterator();
1962                                while (i.hasNext()) {
1963                                    Binding b = (Binding) i.next();
1964                                    if (b != keyBinding && deletes(deleteBinding, b)) {
1965                                        extraSystemDeletes.add(b);
1966                                    }
1967                                }
1968                            }
1969                        }
1970                        localChangeManager.addBinding(binding);
1971                        // update the model
1972
bindingModel.remove(keyBinding);
1973                        bindingModel.add(binding);
1974                        if (!extraSystemDeletes.isEmpty()) {
1975                            Iterator JavaDoc i = extraSystemDeletes.iterator();
1976                            while (i.hasNext()) {
1977                                KeyBinding b = (KeyBinding) i.next();
1978                                KeyBinding newBinding = new KeyBinding(b.getKeySequence(), b
1979                                        .getParameterizedCommand(), b.getSchemeId(), b
1980                                        .getContextId(), null, null, null, Binding.USER);
1981                                localChangeManager.addBinding(newBinding);
1982
1983                                bindingModel.remove(b);
1984                                bindingModel.add(newBinding);
1985                            }
1986                        }
1987                        updateConflicts(keyBinding);
1988                        updateConflicts(binding);
1989                        // end update the model
1990
update();
1991                        filteredTree.getViewer().setSelection(
1992                                new StructuredSelection(binding), true);
1993                    }
1994                } else if (object instanceof ParameterizedCommand) {
1995                    // TODO This should use the user's personal scheme.
1996
final KeyBinding binding = new KeyBinding(keySequence,
1997                            (ParameterizedCommand) object,
1998                            getSchemeId(),
1999                            contextId, null, null, null, Binding.USER);
2000                    localChangeManager.addBinding(binding);
2001                    // update the model
2002
// end update the model
2003
bindingModel.add(binding);
2004                    commandModel.remove(object);
2005                    updateConflicts(binding);
2006                    update();
2007
2008                    filteredTree.getViewer().setSelection(
2009                            new StructuredSelection(binding), true);
2010                }
2011            }
2012        }
2013        if (DEBUG) {
2014            final long elapsedTime = System.currentTimeMillis() - startTime;
2015            Tracing.printTrace(TRACING_COMPONENT, "keySequenceChanged in " //$NON-NLS-1$
2016
+ elapsedTime + "ms"); //$NON-NLS-1$
2017
}
2018    }
2019
2020    /**
2021     * Logs the given exception, and opens an error dialog saying that something
2022     * went wrong. The exception is assumed to have something to do with the
2023     * preference store.
2024     *
2025     * @param exception
2026     * The exception to be logged; must not be <code>null</code>.
2027     */

2028    private final void logPreferenceStoreException(final Throwable JavaDoc exception) {
2029        final String JavaDoc message = NewKeysPreferenceMessages.PreferenceStoreError_Message;
2030        String JavaDoc exceptionMessage = exception.getMessage();
2031        if (exceptionMessage == null) {
2032            exceptionMessage = message;
2033        }
2034        final IStatus status = new Status(IStatus.ERROR,
2035                WorkbenchPlugin.PI_WORKBENCH, 0, exceptionMessage, exception);
2036        WorkbenchPlugin.log(message, status);
2037        StatusUtil.handleStatus(message, exception, StatusManager.SHOW);
2038    }
2039
2040    protected final void performDefaults() {
2041
2042        // Ask the user to confirm
2043
final String JavaDoc title = NewKeysPreferenceMessages.RestoreDefaultsMessageBoxText;
2044        final String JavaDoc message = NewKeysPreferenceMessages.RestoreDefaultsMessageBoxMessage;
2045        final boolean confirmed = MessageDialog.openConfirm(getShell(), title,
2046                message);
2047
2048        if (confirmed) {
2049            // Fix the scheme in the local changes.
2050
final String JavaDoc defaultSchemeId = bindingService.getDefaultSchemeId();
2051            final Scheme defaultScheme = localChangeManager
2052                    .getScheme(defaultSchemeId);
2053            try {
2054                localChangeManager.setActiveScheme(defaultScheme);
2055            } catch (final NotDefinedException e) {
2056                // At least we tried....
2057
}
2058
2059            // Fix the bindings in the local changes.
2060
final Binding[] currentBindings = localChangeManager.getBindings();
2061            final int currentBindingsLength = currentBindings.length;
2062            final Set JavaDoc trimmedBindings = new HashSet JavaDoc();
2063            for (int i = 0; i < currentBindingsLength; i++) {
2064                final Binding binding = currentBindings[i];
2065                if (binding.getType() != Binding.USER) {
2066                    trimmedBindings.add(binding);
2067                }
2068            }
2069            final Binding[] trimmedBindingArray = (Binding[]) trimmedBindings
2070                    .toArray(new Binding[trimmedBindings.size()]);
2071            localChangeManager.setBindings(trimmedBindingArray);
2072
2073            // Apply the changes.
2074
try {
2075                bindingService.savePreferences(defaultScheme,
2076                        trimmedBindingArray);
2077            } catch (final IOException JavaDoc e) {
2078                logPreferenceStoreException(e);
2079            }
2080            long startTime = 0L;
2081            if (DEBUG) {
2082                startTime = System.currentTimeMillis();
2083            }
2084            busyRefillTree();
2085            if (DEBUG) {
2086                final long elapsedTime = System.currentTimeMillis() - startTime;
2087                Tracing.printTrace(TRACING_COMPONENT,
2088                        "performDefaults:model in " //$NON-NLS-1$
2089
+ elapsedTime + "ms"); //$NON-NLS-1$
2090
}
2091        }
2092
2093        setScheme(localChangeManager.getActiveScheme());
2094
2095        super.performDefaults();
2096    }
2097
2098    /**
2099     * We're re-filling the entire tree, both bindings and commands. It's
2100     * loud.
2101     */

2102    private void busyRefillTree() {
2103        if (bindingModel==null) {
2104            // we haven't really been created yet.
2105
return;
2106        }
2107        BusyIndicator.showWhile(filteredTree.getViewer().getTree()
2108                .getDisplay(), new Runnable JavaDoc() {
2109            public void run() {
2110                try {
2111                    filteredTree.getViewer().getTree().setRedraw(false);
2112
2113                    bindingModel.clear();
2114                    commandModel.clear();
2115                    Collection JavaDoc comeBack = localChangeManager
2116                            .getActiveBindingsDisregardingContextFlat();
2117                    bindingModel.addAll(comeBack);
2118
2119                    // showAllCheckBox.setSelection(false);
2120
fillInCommands();
2121                } finally {
2122                    filteredTree.getViewer().getTree().setRedraw(true);
2123                }
2124            }
2125        });
2126        updateDataControls();
2127    }
2128
2129    public final boolean performOk() {
2130        // Save the preferences.
2131
try {
2132            bindingService.savePreferences(
2133                    localChangeManager.getActiveScheme(), localChangeManager
2134                            .getBindings());
2135        } catch (final IOException JavaDoc e) {
2136            logPreferenceStoreException(e);
2137        }
2138        saveState(getDialogSettings());
2139        return super.performOk();
2140    }
2141
2142    /**
2143     * Handles the selection event on the add binding button. This adds a new
2144     * binding based on the current selection.
2145     *
2146     * @param event
2147     * Ignored.
2148     */

2149    private final void selectAddBindingButton(final SelectionEvent event) {
2150        long startTime = 0L;
2151        if (DEBUG) {
2152            startTime = System.currentTimeMillis();
2153        }
2154
2155        // Check to make sure we've got a selection.
2156
final TreeViewer viewer = filteredTree.getViewer();
2157        final ISelection selection = viewer.getSelection();
2158        if (!(selection instanceof IStructuredSelection)) {
2159            return;
2160        }
2161
2162        final IStructuredSelection structuredSelection = (IStructuredSelection) selection;
2163        final Object JavaDoc firstElement = structuredSelection.getFirstElement();
2164        final Object JavaDoc value = firstElement;
2165        if (value instanceof KeyBinding) {
2166            bindingAdd((KeyBinding) value);
2167        } else if (value instanceof ParameterizedCommand) {
2168            bindingText.setFocus();
2169        }
2170
2171        if (DEBUG) {
2172            final long elapsedTime = System.currentTimeMillis() - startTime;
2173            Tracing.printTrace(TRACING_COMPONENT, "selectAddBindingButton in " //$NON-NLS-1$
2174
+ elapsedTime + "ms"); //$NON-NLS-1$
2175
}
2176    }
2177
2178    /**
2179     * Handles the selection event on the remove binding button. This removes
2180     * the selected binding.
2181     *
2182     * @param event
2183     * Ignored.
2184     */

2185    private final void selectRemoveBindingButton(final SelectionEvent event) {
2186        long startTime = 0L;
2187        if (DEBUG) {
2188            startTime = System.currentTimeMillis();
2189        }
2190        // Check to make sure we've got a selection.
2191
final TreeViewer viewer = filteredTree.getViewer();
2192        final ISelection selection = viewer.getSelection();
2193        if (!(selection instanceof IStructuredSelection)) {
2194            return;
2195        }
2196
2197        final IStructuredSelection structuredSelection = (IStructuredSelection) selection;
2198        final Object JavaDoc firstElement = structuredSelection.getFirstElement();
2199        final Object JavaDoc value = firstElement;
2200        if (value instanceof KeyBinding) {
2201            bindingRemove((KeyBinding) value);
2202        } else if (value == markedParameterizedCommand) {
2203            commandModel.remove(markedParameterizedCommand);
2204            markedParameterizedCommand = null;
2205            markedContextId = null;
2206            update();
2207        }
2208        if (DEBUG) {
2209            final long elapsedTime = System.currentTimeMillis() - startTime;
2210            Tracing.printTrace(TRACING_COMPONENT,
2211                    "selectRemoveBindingButton in " //$NON-NLS-1$
2212
+ elapsedTime + "ms"); //$NON-NLS-1$
2213
}
2214    }
2215
2216    private final void selectRestoreBindingButton(final SelectionEvent event) {
2217        long startTime = 0L;
2218        if (DEBUG) {
2219            startTime = System.currentTimeMillis();
2220        }
2221        // Check to make sure we've got a selection.
2222
final TreeViewer viewer = filteredTree.getViewer();
2223        final ISelection selection = viewer.getSelection();
2224        if (!(selection instanceof IStructuredSelection)) {
2225            return;
2226        }
2227
2228        final IStructuredSelection structuredSelection = (IStructuredSelection) selection;
2229        final Object JavaDoc firstElement = structuredSelection.getFirstElement();
2230        final Object JavaDoc value = firstElement;
2231        if (value instanceof KeyBinding) {
2232            bindingRestore((KeyBinding) value);
2233        } else if (value instanceof ParameterizedCommand) {
2234            bindingRestore((ParameterizedCommand) value, true);
2235        }
2236        if (DEBUG) {
2237            final long elapsedTime = System.currentTimeMillis() - startTime;
2238            Tracing.printTrace(TRACING_COMPONENT,
2239                    "selectRestoreBindingButton in " //$NON-NLS-1$
2240
+ elapsedTime + "ms"); //$NON-NLS-1$
2241
}
2242    }
2243
2244    /**
2245     * Handles a selection event on the scheme combo. If the scheme has changed,
2246     * then the local change manager is updated, and the page's contents are
2247     * updated as well.
2248     *
2249     * @param event
2250     * The selection event; must not be <code>null</code>.
2251     */

2252    private final void selectSchemeCombo(final SelectionChangedEvent event) {
2253        final ISelection selection = event.getSelection();
2254        if (selection instanceof IStructuredSelection) {
2255            final Object JavaDoc firstElement = ((IStructuredSelection) selection)
2256                    .getFirstElement();
2257            if (firstElement instanceof Scheme) {
2258                final Scheme newScheme = (Scheme) firstElement;
2259                if (newScheme != localChangeManager.getActiveScheme()) {
2260                    try {
2261                        localChangeManager.setActiveScheme(newScheme);
2262                        busyRefillTree();
2263                    } catch (final NotDefinedException e) {
2264                        // TODO The scheme wasn't valid.
2265
}
2266                }
2267            }
2268        }
2269    }
2270
2271    /**
2272     * If the row has changed, then update the data controls.
2273     */

2274    private final void selectTreeRow(final SelectionChangedEvent event) {
2275        updateDataControls();
2276    }
2277
2278    /**
2279     * Sets the currently selected scheme. Setting the scheme always triggers an
2280     * update of the underlying widgets.
2281     *
2282     * @param scheme
2283     * The scheme to select; may be <code>null</code>.
2284     */

2285    private final void setScheme(final Scheme scheme) {
2286        schemeCombo.setSelection(new StructuredSelection(scheme));
2287    }
2288
2289    /**
2290     * Updates all of the controls on this preference page in response to a user
2291     * interaction.
2292     */

2293    private final void update() {
2294        updateTree();
2295        updateDataControls();
2296    }
2297
2298    /**
2299     * Updates the data controls to match the current selection, if any.
2300     */

2301    private final void updateDataControls() {
2302        final ISelection selection = filteredTree.getViewer().getSelection();
2303        if (selection instanceof IStructuredSelection) {
2304            final IStructuredSelection structuredSelection = (IStructuredSelection) selection;
2305            final Object JavaDoc node = structuredSelection.getFirstElement();
2306            if (node != null) {
2307                final Object JavaDoc object = node;
2308                if (object instanceof KeyBinding) {
2309                    final KeyBinding binding = (KeyBinding) object;
2310                    try {
2311                        commandNameValueLabel.setText(binding
2312                                .getParameterizedCommand().getName());
2313                        String JavaDoc description = binding.getParameterizedCommand()
2314                                .getCommand().getDescription();
2315                        if (description == null) {
2316                            description = Util.ZERO_LENGTH_STRING;
2317                        }
2318                        descriptionValueText.setText(description);
2319                    } catch (final NotDefinedException e) {
2320                        // It's probably okay to just let this one slide.
2321
}
2322                    whenCombo.setSelection(new StructuredSelection(
2323                            contextService.getContext(binding.getContextId())));
2324                    keySequenceText.setKeySequence(binding.getKeySequence());
2325
2326                } else if (object instanceof ParameterizedCommand) {
2327                    final ParameterizedCommand command = (ParameterizedCommand) object;
2328                    try {
2329                        commandNameValueLabel.setText(command.getName());
2330                        String JavaDoc description = command.getCommand()
2331                                .getDescription();
2332                        if (description == null) {
2333                            description = Util.ZERO_LENGTH_STRING;
2334                        }
2335                        descriptionValueText.setText(description);
2336                    } catch (final NotDefinedException e) {
2337                        // It's probably okay to just let this one slide.
2338
}
2339                    keySequenceText.clear();
2340                    if (command == markedParameterizedCommand) {
2341                        whenCombo.setSelection(new StructuredSelection(
2342                                contextService.getContext(markedContextId)));
2343                    } else {
2344                        whenCombo
2345                                .setSelection(new StructuredSelection(
2346                                        contextService
2347                                                .getContext(IContextIds.CONTEXT_ID_WINDOW)));
2348                    }
2349                }
2350            } else {
2351                commandNameValueLabel.setText(""); //$NON-NLS-1$
2352
descriptionValueText.setText(""); //$NON-NLS-1$
2353
keySequenceText.clear();
2354                whenCombo.setSelection(null);
2355            }
2356        }
2357    }
2358
2359    private final void updateTree() {
2360        long startTime = 0L;
2361        if (DEBUG) {
2362            startTime = System.currentTimeMillis();
2363        }
2364
2365        final TreeViewer viewer = filteredTree.getViewer();
2366
2367        // Add the marked parameterized command, if any.
2368
if (markedParameterizedCommand != null) {
2369            commandModel.add(markedParameterizedCommand);
2370            markedParameterizedCommand = null;
2371        }
2372
2373        // Repack all of the columns.
2374
final Tree tree = viewer.getTree();
2375        final TreeColumn[] columns = tree.getColumns();
2376
2377        columns[BindingLabelProvider.COLUMN_COMMAND].setWidth(240);
2378        columns[BindingLabelProvider.COLUMN_TRIGGER_SEQUENCE].setWidth(130);
2379        columns[BindingLabelProvider.COLUMN_WHEN].setWidth(130);
2380        columns[BindingLabelProvider.COLUMN_CATEGORY].setWidth(130);
2381        columns[BindingLabelProvider.COLUMN_USER].setWidth(50);
2382
2383        if (DEBUG) {
2384            final long elapsedTime = System.currentTimeMillis() - startTime;
2385            Tracing.printTrace(TRACING_COMPONENT, "Refreshed page in " //$NON-NLS-1$
2386
+ elapsedTime + "ms"); //$NON-NLS-1$
2387
}
2388    }
2389
2390    /**
2391     * Save the state of the receiver.
2392     *
2393     * @param dialogSettings
2394     */

2395    public void saveState(IDialogSettings dialogSettings) {
2396        if (dialogSettings == null) {
2397            return;
2398        }
2399        dialogSettings.put(TAG_FIELD, showAllCheckBox.getSelection());
2400        dialogSettings.put(TAG_FILTER_ACTION_SETS, filterActionSetContexts);
2401        dialogSettings.put(TAG_FILTER_INTERNAL, filterInternalContexts);
2402        dialogSettings.put(TAG_FILTER_UNCAT, filteredTree.isFilteringCategories());
2403    }
2404
2405    protected IDialogSettings getDialogSettings() {
2406        IDialogSettings workbenchSettings = WorkbenchPlugin.getDefault()
2407                .getDialogSettings();
2408
2409        IDialogSettings settings = workbenchSettings
2410                .getSection(TAG_DIALOG_SECTION);
2411
2412        if (settings == null) {
2413            settings = workbenchSettings.addNewSection(TAG_DIALOG_SECTION);
2414        }
2415        return settings;
2416    }
2417
2418    protected Object JavaDoc[] getContexts() {
2419
2420        Context[] contexts = contextService.getDefinedContexts();
2421        List JavaDoc filteredContexts = new ArrayList JavaDoc();
2422        try {
2423            if (filterActionSetContexts) {
2424                for (int i = 0; i < contexts.length; i++) {
2425                    String JavaDoc parentId = contexts[i].getParentId();
2426                    boolean check = false;
2427                    if (contexts[i].getId().equalsIgnoreCase(
2428                            CONTEXT_ID_ACTION_SETS)) {
2429                        check = true;
2430                    }
2431                    while (parentId != null) {
2432                        if (parentId.equalsIgnoreCase(CONTEXT_ID_ACTION_SETS)) {
2433                            check = true;
2434                        }
2435                        parentId = contextService.getContext(parentId)
2436                                .getParentId();
2437                    }
2438                    if (!check) {
2439                        filteredContexts.add(contexts[i]);
2440                    }
2441                }
2442            } else {
2443                filteredContexts.addAll(Arrays.asList(contexts));
2444            }
2445
2446            if (filterInternalContexts) {
2447                for (int i = 0; i < filteredContexts.size(); i++) {
2448                    if (((Context) filteredContexts.get(i)).getId().indexOf(
2449                            CONTEXT_ID_INTERNAL) != -1) {
2450                        filteredContexts.remove(i);
2451                    }
2452                }
2453            }
2454
2455        } catch (NotDefinedException e) {
2456            return contexts;
2457        }
2458
2459        return filteredContexts.toArray();
2460    }
2461
2462    private void updateWhenCombo() {
2463        ISelection selection = filteredTree.getViewer().getSelection();
2464        if (selection instanceof IStructuredSelection) {
2465            IStructuredSelection structuredSelection = (IStructuredSelection) selection;
2466            Object JavaDoc node = structuredSelection.getFirstElement();
2467            if (node != null) {
2468                final Object JavaDoc object = node;
2469                selection = whenCombo.getSelection();
2470                final String JavaDoc contextId;
2471                if (selection instanceof IStructuredSelection) {
2472                    structuredSelection = (IStructuredSelection) selection;
2473                    final Object JavaDoc firstElement = structuredSelection
2474                            .getFirstElement();
2475                    if (firstElement == null) {
2476                        contextId = IContextIds.CONTEXT_ID_WINDOW;
2477                    } else {
2478                        contextId = ((Context) firstElement).getId();
2479                    }
2480                } else {
2481                    contextId = IContextIds.CONTEXT_ID_WINDOW;
2482                }
2483                if (object instanceof KeyBinding) {
2484                    KeyBinding keyBinding = (KeyBinding) object;
2485                    if (!keyBinding.getContextId().equals(contextId)) {
2486                        final KeyBinding binding = new KeyBinding(
2487                                keyBinding.getKeySequence(),
2488                                keyBinding.getParameterizedCommand(),
2489                                getSchemeId(),
2490                                contextId, null, null, null, Binding.USER);
2491
2492                        if (keyBinding.getType() == Binding.USER) {
2493                            localChangeManager.removeBinding(keyBinding);
2494                        } else {
2495                            localChangeManager.addBinding(new KeyBinding(
2496                                    keyBinding.getKeySequence(), null,
2497                                    keyBinding.getSchemeId(), keyBinding
2498                                            .getContextId(), null, null, null,
2499                                    Binding.USER));
2500                        }
2501                        localChangeManager.addBinding(binding);
2502                        // update the model
2503
bindingModel.remove(keyBinding);
2504                        bindingModel.add(binding);
2505                        updateConflicts(keyBinding);
2506                        updateConflicts(binding);
2507                        // end update the model
2508
update();
2509                        filteredTree.getViewer().setSelection(
2510                                new StructuredSelection(binding), true);
2511                    }
2512                }
2513            }
2514        }
2515    }
2516
2517    /*
2518     * (non-Javadoc)
2519     *
2520     * @see org.eclipse.jface.preference.PreferencePage#applyData(java.lang.Object)
2521     */

2522    public void applyData(Object JavaDoc data) {
2523        if (data instanceof Binding && filteredTree != null) {
2524            filteredTree.getViewer().setSelection(
2525                    new StructuredSelection(data), true);
2526        }
2527    }
2528    
2529    public String JavaDoc getSchemeId() {
2530        ISelection sel = schemeCombo.getSelection();
2531        if (sel instanceof IStructuredSelection) {
2532            Object JavaDoc o = ((IStructuredSelection)sel).getFirstElement();
2533            if (o instanceof Scheme) {
2534                return ((Scheme)o).getId();
2535            }
2536        }
2537        return IBindingService.DEFAULT_DEFAULT_ACTIVE_SCHEME_ID;
2538    }
2539}
2540
Popular Tags