KickJava   Java API By Example, From Geeks To Geeks.

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


1 /*******************************************************************************
2  * Copyright (c) 2000, 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.BufferedWriter JavaDoc;
15 import java.io.FileWriter JavaDoc;
16 import java.io.IOException JavaDoc;
17 import java.io.Writer JavaDoc;
18 import java.util.ArrayList JavaDoc;
19 import java.util.Arrays JavaDoc;
20 import java.util.Collection JavaDoc;
21 import java.util.Collections JavaDoc;
22 import java.util.Comparator JavaDoc;
23 import java.util.HashMap JavaDoc;
24 import java.util.HashSet JavaDoc;
25 import java.util.Iterator JavaDoc;
26 import java.util.List JavaDoc;
27 import java.util.Map JavaDoc;
28 import java.util.ResourceBundle JavaDoc;
29 import java.util.Set JavaDoc;
30
31 import org.eclipse.core.commands.Category;
32 import org.eclipse.core.commands.Command;
33 import org.eclipse.core.commands.CommandManager;
34 import org.eclipse.core.commands.ParameterizedCommand;
35 import org.eclipse.core.commands.common.NotDefinedException;
36 import org.eclipse.core.commands.contexts.Context;
37 import org.eclipse.core.commands.contexts.ContextManager;
38 import org.eclipse.core.runtime.IStatus;
39 import org.eclipse.core.runtime.SafeRunner;
40 import org.eclipse.core.runtime.Status;
41 import org.eclipse.jface.bindings.Binding;
42 import org.eclipse.jface.bindings.BindingManager;
43 import org.eclipse.jface.bindings.Scheme;
44 import org.eclipse.jface.bindings.TriggerSequence;
45 import org.eclipse.jface.bindings.keys.KeyBinding;
46 import org.eclipse.jface.bindings.keys.KeySequence;
47 import org.eclipse.jface.bindings.keys.KeySequenceText;
48 import org.eclipse.jface.bindings.keys.KeyStroke;
49 import org.eclipse.jface.contexts.IContextIds;
50 import org.eclipse.jface.dialogs.IDialogConstants;
51 import org.eclipse.jface.dialogs.MessageDialog;
52 import org.eclipse.jface.preference.IPreferenceStore;
53 import org.eclipse.jface.preference.PreferencePage;
54 import org.eclipse.jface.util.SafeRunnable;
55 import org.eclipse.swt.SWT;
56 import org.eclipse.swt.events.DisposeEvent;
57 import org.eclipse.swt.events.DisposeListener;
58 import org.eclipse.swt.events.FocusEvent;
59 import org.eclipse.swt.events.FocusListener;
60 import org.eclipse.swt.events.ModifyEvent;
61 import org.eclipse.swt.events.ModifyListener;
62 import org.eclipse.swt.events.MouseAdapter;
63 import org.eclipse.swt.events.MouseEvent;
64 import org.eclipse.swt.events.SelectionAdapter;
65 import org.eclipse.swt.events.SelectionEvent;
66 import org.eclipse.swt.events.SelectionListener;
67 import org.eclipse.swt.graphics.Image;
68 import org.eclipse.swt.graphics.Point;
69 import org.eclipse.swt.layout.GridData;
70 import org.eclipse.swt.layout.GridLayout;
71 import org.eclipse.swt.widgets.Button;
72 import org.eclipse.swt.widgets.Combo;
73 import org.eclipse.swt.widgets.Composite;
74 import org.eclipse.swt.widgets.Control;
75 import org.eclipse.swt.widgets.FileDialog;
76 import org.eclipse.swt.widgets.Group;
77 import org.eclipse.swt.widgets.Label;
78 import org.eclipse.swt.widgets.Menu;
79 import org.eclipse.swt.widgets.MenuItem;
80 import org.eclipse.swt.widgets.TabFolder;
81 import org.eclipse.swt.widgets.TabItem;
82 import org.eclipse.swt.widgets.Table;
83 import org.eclipse.swt.widgets.TableColumn;
84 import org.eclipse.swt.widgets.TableItem;
85 import org.eclipse.swt.widgets.Text;
86 import org.eclipse.ui.IWorkbench;
87 import org.eclipse.ui.IWorkbenchPreferencePage;
88 import org.eclipse.ui.PlatformUI;
89 import org.eclipse.ui.activities.IActivityManager;
90 import org.eclipse.ui.commands.ICommandService;
91 import org.eclipse.ui.contexts.IContextService;
92 import org.eclipse.ui.internal.IPreferenceConstants;
93 import org.eclipse.ui.internal.IWorkbenchHelpContextIds;
94 import org.eclipse.ui.internal.WorkbenchPlugin;
95 import org.eclipse.ui.internal.misc.StatusUtil;
96 import org.eclipse.ui.internal.util.PrefUtil;
97 import org.eclipse.ui.internal.util.Util;
98 import org.eclipse.ui.keys.IBindingService;
99 import org.eclipse.ui.statushandlers.StatusManager;
100
101 import com.ibm.icu.text.Collator;
102 import com.ibm.icu.text.MessageFormat;
103
104 /**
105  * The preference page for defining keyboard shortcuts. While some of its
106  * underpinning have been made generic to "bindings" rather than "key bindings",
107  * it will still take some work to remove the link entirely.
108  *
109  * @since 3.0
110  */

111 public final class KeysPreferencePage extends PreferencePage implements
112         IWorkbenchPreferencePage {
113
114     /**
115      * A selection listener to be used on the columns in the table on the view
116      * tab. This selection listener modifies the sort order so that the
117      * appropriate column is in the first position.
118      *
119      * @since 3.1
120      */

121     private class SortOrderSelectionListener extends SelectionAdapter {
122
123         /**
124          * The column to be put in the first position. This value should be one
125          * of the constants defined by <code>SORT_COLUMN_</code>.
126          */

127         private final int columnSelected;
128
129         /**
130          * Constructs a new instance of <code>SortOrderSelectionListener</code>.
131          *
132          * @param columnSelected
133          * The column to be given first priority in the sort order;
134          * this value should be one of the constants defined as
135          * <code>SORT_COLUMN_</code>.
136          */

137         private SortOrderSelectionListener(final int columnSelected) {
138             this.columnSelected = columnSelected;
139         }
140
141         /*
142          * (non-Javadoc)
143          *
144          * @see org.eclipse.swt.events.SelectionListener#widgetSelected(org.eclipse.swt.events.SelectionEvent)
145          */

146         public void widgetSelected(SelectionEvent e) {
147             // Change the column titles.
148
final int oldSortIndex = sortOrder[0];
149             final TableColumn oldSortColumn = tableBindings
150                     .getColumn(oldSortIndex);
151             oldSortColumn.setText(UNSORTED_COLUMN_NAMES[oldSortIndex]);
152             final TableColumn newSortColumn = tableBindings
153                     .getColumn(columnSelected);
154             newSortColumn.setText(SORTED_COLUMN_NAMES[columnSelected]);
155
156             // Change the sort order.
157
boolean columnPlaced = false;
158             boolean enoughRoom = false;
159             int bumpedColumn = -1;
160             for (int i = 0; i < sortOrder.length; i++) {
161                 if (sortOrder[i] == columnSelected) {
162                     /*
163                      * We've found the place where the column existing in the
164                      * old sort order. No matter what at this point, we have
165                      * completed the reshuffling.
166                      */

167                     enoughRoom = true;
168                     if (bumpedColumn != -1) {
169                         // We have already started bumping things around, so
170
// drop the last bumped column here.
171
sortOrder[i] = bumpedColumn;
172                     } else {
173                         // The order has not changed.
174
columnPlaced = true;
175                     }
176                     break;
177
178                 } else if (columnPlaced) {
179                     // We are currently bumping, so just bump another.
180
int temp = sortOrder[i];
181                     sortOrder[i] = bumpedColumn;
182                     bumpedColumn = temp;
183
184                 } else {
185                     /*
186                      * We are not currently bumping, so drop the column and
187                      * start bumping.
188                      */

189                     bumpedColumn = sortOrder[i];
190                     sortOrder[i] = columnSelected;
191                     columnPlaced = true;
192                 }
193             }
194
195             // Grow the sort order.
196
if (!enoughRoom) {
197                 final int[] newSortOrder = new int[sortOrder.length + 1];
198                 System.arraycopy(sortOrder, 0, newSortOrder, 0,
199                         sortOrder.length);
200                 newSortOrder[sortOrder.length] = bumpedColumn;
201                 sortOrder = newSortOrder;
202             }
203
204             // Update the view tab.
205
updateViewTab();
206         }
207     }
208
209     /**
210      * The data key for the binding stored on an SWT widget. The key is a
211      * fully-qualified name, but in reverse order. This is so that the equals
212      * method will detect misses faster.
213      */

214     private static final String JavaDoc BINDING_KEY = "Binding.bindings.jface.eclipse.org"; //$NON-NLS-1$
215

216     /**
217      * The image associate with a binding that exists as part of the system
218      * definition.
219      */

220     private static final Image IMAGE_BLANK = ImageFactory.getImage("blank"); //$NON-NLS-1$
221

222     /**
223      * The image associated with a binding changed by the user.
224      */

225     private static final Image IMAGE_CHANGE = ImageFactory.getImage("change"); //$NON-NLS-1$
226

227     /**
228      * The data key at which the <code>Binding</code> instance for a table
229      * item is stored.
230      */

231     private static final String JavaDoc ITEM_DATA_KEY = "org.eclipse.jface.bindings"; //$NON-NLS-1$
232

233     /**
234      * The number of items to show in the combo boxes.
235      */

236     private static final int ITEMS_TO_SHOW = 9;
237
238     /**
239      * The resource bundle from which translations can be retrieved.
240      */

241     private static final ResourceBundle JavaDoc RESOURCE_BUNDLE = ResourceBundle
242             .getBundle(KeysPreferencePage.class.getName());
243
244     /**
245      * The total number of columns on the view tab.
246      */

247     private static final int VIEW_TOTAL_COLUMNS = 4;
248
249     /**
250      * The translated names for the columns when they are the primary sort key
251      * (e.g., ">Category<").
252      */

253     private static final String JavaDoc[] SORTED_COLUMN_NAMES = new String JavaDoc[VIEW_TOTAL_COLUMNS];
254
255     /**
256      * The index of the modify tab.
257      *
258      * @since 3.1
259      */

260     private static final int TAB_INDEX_MODIFY = 1;
261
262     /**
263      * The translated names for the columns when they are not the primary sort
264      * key (e.g., "Category").
265      */

266     private static final String JavaDoc[] UNSORTED_COLUMN_NAMES = new String JavaDoc[VIEW_TOTAL_COLUMNS];
267
268     /**
269      * The index of the column on the view tab containing the category name.
270      */

271     private static final int VIEW_CATEGORY_COLUMN_INDEX = 0;
272
273     /**
274      * The index of the column on the view tab containing the command name.
275      */

276     private static final int VIEW_COMMAND_COLUMN_INDEX = 1;
277
278     /**
279      * The index of the column on the view tab containing the context name.
280      */

281     private static final int VIEW_CONTEXT_COLUMN_INDEX = 3;
282
283     /**
284      * The index of the column on the view tab containing the key sequence.
285      */

286     private static final int VIEW_KEY_SEQUENCE_COLUMN_INDEX = 2;
287
288     static {
289         UNSORTED_COLUMN_NAMES[VIEW_CATEGORY_COLUMN_INDEX] = Util
290                 .translateString(RESOURCE_BUNDLE, "tableColumnCategory"); //$NON-NLS-1$
291
UNSORTED_COLUMN_NAMES[VIEW_COMMAND_COLUMN_INDEX] = Util
292                 .translateString(RESOURCE_BUNDLE, "tableColumnCommand"); //$NON-NLS-1$
293
UNSORTED_COLUMN_NAMES[VIEW_KEY_SEQUENCE_COLUMN_INDEX] = Util
294                 .translateString(RESOURCE_BUNDLE, "tableColumnKeySequence"); //$NON-NLS-1$
295
UNSORTED_COLUMN_NAMES[VIEW_CONTEXT_COLUMN_INDEX] = Util
296                 .translateString(RESOURCE_BUNDLE, "tableColumnContext"); //$NON-NLS-1$
297

298         SORTED_COLUMN_NAMES[VIEW_CATEGORY_COLUMN_INDEX] = Util.translateString(
299                 RESOURCE_BUNDLE, "tableColumnCategorySorted"); //$NON-NLS-1$
300
SORTED_COLUMN_NAMES[VIEW_COMMAND_COLUMN_INDEX] = Util.translateString(
301                 RESOURCE_BUNDLE, "tableColumnCommandSorted"); //$NON-NLS-1$
302
SORTED_COLUMN_NAMES[VIEW_KEY_SEQUENCE_COLUMN_INDEX] = Util
303                 .translateString(RESOURCE_BUNDLE,
304                         "tableColumnKeySequenceSorted"); //$NON-NLS-1$
305
SORTED_COLUMN_NAMES[VIEW_CONTEXT_COLUMN_INDEX] = Util.translateString(
306                 RESOURCE_BUNDLE, "tableColumnContextSorted"); //$NON-NLS-1$
307
}
308
309     /**
310      * The workbench's activity manager. This activity manager is used to see if
311      * certain commands should be filtered from the user interface.
312      */

313     private IActivityManager activityManager;
314
315     /**
316      * The workbench's binding service. This binding service is used to access
317      * the current set of bindings, and to persist changes.
318      */

319     private IBindingService bindingService;
320
321     /**
322      * The add button located on the bottom left of the preference page. This
323      * button adds the current trigger sequence to the currently selected
324      * command.
325      */

326     private Button buttonAdd;
327
328     /**
329      * The remove button located on the bottom left of the preference page. This
330      * button removes the current trigger sequence from the current command.
331      */

332     private Button buttonRemove;
333
334     /**
335      * The restore button located on the bottom left of the preference page.
336      * This button attempts to restore the currently trigger sequence to its
337      * initial (i.e., Binding.SYSTEM) state -- undoing all user modifications.
338      */

339     private Button buttonRestore;
340
341     /**
342      * A map of all the category identifiers indexed by the names that appear in
343      * the user interface. This look-up table is built during initialization.
344      */

345     private Map JavaDoc categoryIdsByUniqueName;
346
347     /**
348      * A map of all the category names in the user interface indexed by their
349      * identifiers. This look-up table is built during initialization.
350      */

351     private Map JavaDoc categoryUniqueNamesById;
352
353     /**
354      * The combo box containing the list of all categories for commands.
355      */

356     private Combo comboCategory;
357
358     /**
359      * The combo box containing the list of commands relevent for the currently
360      * selected category.
361      */

362     private Combo comboCommand;
363
364     /**
365      * The combo box containing the list of contexts in the system.
366      */

367     private Combo comboContext;
368
369     /**
370      * The combo box containing the list of schemes in the system.
371      */

372     private Combo comboScheme;
373
374     /**
375      * A map of all the command identifiers indexed by the categories to which
376      * they belong. This look-up table is built during initialization.
377      */

378     private Map JavaDoc commandIdsByCategoryId;
379
380     /**
381      * The parameterized commands corresponding to the current contents of
382      * <code>comboCommand</code>. The commands in this array are in the same
383      * order as in the combo. This value can be <code>null</code> if nothing
384      * is selected in the combo.
385      */

386     private ParameterizedCommand[] commands = null;
387
388     /**
389      * The workbench's command service. This command service is used to access
390      * the list of commands.
391      */

392     private ICommandService commandService;
393
394     /**
395      * A map of all the context identifiers indexed by the names that appear in
396      * the user interface. This look-up table is built during initialization.
397      */

398     private Map JavaDoc contextIdsByUniqueName;
399
400     /**
401      * The workbench's context service. This context service is used to access
402      * the list of contexts.
403      */

404     private IContextService contextService;
405
406     /**
407      * A map of all the category names in the user interface indexed by their
408      * identifiers. This look-up table is built during initialization.
409      */

410     private Map JavaDoc contextUniqueNamesById;
411
412     /**
413      * The workbench's help system. This is used to register the page with the
414      * help system.
415      *
416      * TODO Add a help context
417      */

418     // private IWorkbenchHelpSystem helpSystem;
419
/**
420      * This is the label next to the table showing the bindings matching a
421      * particular command. The label is disabled if there isn't a selected
422      * command identifier.
423      */

424     private Label labelBindingsForCommand;
425
426     /**
427      * This is the label next to the table showing the bindings matching a
428      * particular trigger sequence. The label is disabled if there isn't a
429      * current key sequence.
430      */

431     private Label labelBindingsForTriggerSequence;
432
433     /**
434      * The label next to the context combo box. This label indicates whether the
435      * context is a child of another context. If the current context is not a
436      * child, then this label is blank.
437      */

438     private Label labelContextExtends;
439
440     /**
441      * The label next to the scheme combo box. This label indicates whether the
442      * scheme is a child of another scheme. If the current scheme is not a
443      * child, then this label is blank.
444      */

445     private Label labelSchemeExtends;
446
447     /**
448      * A binding manager local to this preference page. When the page is
449      * initialized, the current bindings are read out from the binding service
450      * and placed in this manager. This manager is then updated as the user
451      * makes changes. When the user has finished, the contents of this manager
452      * are compared with the contents of the binding service. The changes are
453      * then persisted.
454      */

455     private final BindingManager localChangeManager = new BindingManager(
456             new ContextManager(), new CommandManager());
457
458     /**
459      * A map of all the scheme identifiers indexed by the names that appear in
460      * the user interface. This look-up table is built during initialization.
461      */

462     private Map JavaDoc schemeIdsByUniqueName;
463
464     /**
465      * A map of all the scheme names in the user interface indexed by their
466      * identifiers. This look-up table is built during initialization.
467      */

468     private Map JavaDoc schemeUniqueNamesById;
469
470     /**
471      * The sort order to be used on the view tab to display all of the key
472      * bindings. This sort order can be changed by the user. This array is never
473      * <code>null</code>, but may be empty.
474      */

475     private int[] sortOrder = { VIEW_CATEGORY_COLUMN_INDEX,
476             VIEW_COMMAND_COLUMN_INDEX, VIEW_KEY_SEQUENCE_COLUMN_INDEX,
477             VIEW_CONTEXT_COLUMN_INDEX };
478
479     /**
480      * The top-most tab folder for the preference page -- containing a view and
481      * a modify tab.
482      */

483     private TabFolder tabFolder;
484
485     /**
486      * A table of the key bindings currently defined. This table appears on the
487      * view tab; it is intended to be an easy way for users to learn the key
488      * bindings in Eclipse. This value is only <code>null</code> until the
489      * controls are first created.
490      */

491     private Table tableBindings;
492
493     /**
494      * The table containing all of the bindings matching the selected command.
495      */

496     private Table tableBindingsForCommand;
497
498     /**
499      * The table containing all of the bindings matching the current trigger
500      * sequence.
501      */

502     private Table tableBindingsForTriggerSequence;
503
504     /**
505      * The text widget where keys are entered. This widget is managed by
506      * <code>textTriggerSequenceManager</code>, which provides its special
507      * behaviour.
508      */

509     private Text textTriggerSequence;
510
511     /**
512      * The manager for the text widget that traps incoming key events. This
513      * manager should be used to access the widget, rather than accessing the
514      * widget directly.
515      */

516     private KeySequenceText textTriggerSequenceManager;
517
518     
519     /* (non-Javadoc)
520      * @see org.eclipse.jface.preference.PreferencePage#applyData(java.lang.Object)
521      */

522     public void applyData(Object JavaDoc data) {
523         if(data instanceof Binding) {
524             editBinding((Binding) data);
525         }
526     }
527     protected final Control createContents(final Composite parent) {
528         
529         PlatformUI.getWorkbench().getHelpSystem()
530             .setHelp(parent, IWorkbenchHelpContextIds.KEYS_PREFERENCE_PAGE);
531         
532         tabFolder = new TabFolder(parent, SWT.NULL);
533
534         // View tab
535
final TabItem viewTab = new TabItem(tabFolder, SWT.NULL);
536         viewTab.setText(Util.translateString(RESOURCE_BUNDLE, "viewTab.Text")); //$NON-NLS-1$
537
viewTab.setControl(createViewTab(tabFolder));
538
539         // Modify tab
540
final TabItem modifyTab = new TabItem(tabFolder, SWT.NULL);
541         modifyTab.setText(Util.translateString(RESOURCE_BUNDLE,
542                 "modifyTab.Text")); //$NON-NLS-1$
543
modifyTab.setControl(createModifyTab(tabFolder));
544
545         // Do some fancy stuff.
546
applyDialogFont(tabFolder);
547         final IPreferenceStore store = getPreferenceStore();
548         final int selectedTab = store
549                 .getInt(IPreferenceConstants.KEYS_PREFERENCE_SELECTED_TAB);
550         if ((tabFolder.getItemCount() > selectedTab) && (selectedTab > 0)) {
551             tabFolder.setSelection(selectedTab);
552         }
553         
554         return tabFolder;
555     }
556
557     /**
558      * Creates the tab that allows the user to change the keyboard shortcuts.
559      *
560      * @param parent
561      * The tab folder in which the tab should be created; must not be
562      * <code>null</code>.
563      * @return The composite which represents the contents of the tab; never
564      * <code>null</code>.
565      */

566     private final Composite createModifyTab(final TabFolder parent) {
567         final Composite composite = new Composite(parent, SWT.NULL);
568         composite.setLayout(new GridLayout());
569         GridData gridData = new GridData(GridData.FILL_BOTH);
570         composite.setLayoutData(gridData);
571         final Composite compositeKeyConfiguration = new Composite(composite,
572                 SWT.NULL);
573         GridLayout gridLayout = new GridLayout();
574         gridLayout.numColumns = 3;
575         compositeKeyConfiguration.setLayout(gridLayout);
576         gridData = new GridData(GridData.FILL_HORIZONTAL);
577         compositeKeyConfiguration.setLayoutData(gridData);
578         final Label labelKeyConfiguration = new Label(
579                 compositeKeyConfiguration, SWT.LEFT);
580         labelKeyConfiguration.setText(Util.translateString(RESOURCE_BUNDLE,
581                 "labelScheme")); //$NON-NLS-1$
582
comboScheme = new Combo(compositeKeyConfiguration, SWT.READ_ONLY);
583         gridData = new GridData();
584         gridData.widthHint = 200;
585         comboScheme.setLayoutData(gridData);
586         comboScheme.setVisibleItemCount(ITEMS_TO_SHOW);
587
588         comboScheme.addSelectionListener(new SelectionAdapter() {
589             public final void widgetSelected(final SelectionEvent e) {
590                 selectedComboScheme();
591             }
592         });
593
594         labelSchemeExtends = new Label(compositeKeyConfiguration, SWT.LEFT);
595         gridData = new GridData(GridData.FILL_HORIZONTAL);
596         labelSchemeExtends.setLayoutData(gridData);
597         final Control spacer = new Composite(composite, SWT.NULL);
598         gridData = new GridData();
599         gridData.heightHint = 10;
600         gridData.widthHint = 10;
601         spacer.setLayoutData(gridData);
602         final Group groupCommand = new Group(composite, SWT.SHADOW_NONE);
603         gridLayout = new GridLayout();
604         gridLayout.numColumns = 3;
605         groupCommand.setLayout(gridLayout);
606         gridData = new GridData(GridData.FILL_BOTH);
607         groupCommand.setLayoutData(gridData);
608         groupCommand.setText(Util.translateString(RESOURCE_BUNDLE,
609                 "groupCommand")); //$NON-NLS-1$
610
final Label labelCategory = new Label(groupCommand, SWT.LEFT);
611         gridData = new GridData();
612         labelCategory.setLayoutData(gridData);
613         labelCategory.setText(Util.translateString(RESOURCE_BUNDLE,
614                 "labelCategory")); //$NON-NLS-1$
615
comboCategory = new Combo(groupCommand, SWT.READ_ONLY);
616         gridData = new GridData();
617         gridData.horizontalSpan = 2;
618         gridData.widthHint = 200;
619         comboCategory.setLayoutData(gridData);
620         comboCategory.setVisibleItemCount(ITEMS_TO_SHOW);
621
622         comboCategory.addSelectionListener(new SelectionAdapter() {
623             public final void widgetSelected(final SelectionEvent e) {
624                 update();
625             }
626         });
627
628         final Label labelCommand = new Label(groupCommand, SWT.LEFT);
629         gridData = new GridData();
630         labelCommand.setLayoutData(gridData);
631         labelCommand.setText(Util.translateString(RESOURCE_BUNDLE,
632                 "labelCommand")); //$NON-NLS-1$
633
comboCommand = new Combo(groupCommand, SWT.READ_ONLY);
634         gridData = new GridData();
635         gridData.horizontalSpan = 2;
636         gridData.widthHint = 300;
637         comboCommand.setLayoutData(gridData);
638         comboCommand.setVisibleItemCount(9);
639
640         comboCommand.addSelectionListener(new SelectionAdapter() {
641             public final void widgetSelected(final SelectionEvent e) {
642                 update();
643             }
644         });
645
646         labelBindingsForCommand = new Label(groupCommand, SWT.LEFT);
647         gridData = new GridData(GridData.VERTICAL_ALIGN_BEGINNING);
648         gridData.verticalAlignment = GridData.FILL_VERTICAL;
649         labelBindingsForCommand.setLayoutData(gridData);
650         labelBindingsForCommand.setText(Util.translateString(RESOURCE_BUNDLE,
651                 "labelAssignmentsForCommand")); //$NON-NLS-1$
652
tableBindingsForCommand = new Table(groupCommand, SWT.BORDER
653                 | SWT.FULL_SELECTION | SWT.H_SCROLL | SWT.V_SCROLL);
654         tableBindingsForCommand.setHeaderVisible(true);
655         gridData = new GridData(GridData.FILL_BOTH);
656         gridData.heightHint = 60;
657         gridData.horizontalSpan = 2;
658         gridData.widthHint = "carbon".equals(SWT.getPlatform()) ? 620 : 520; //$NON-NLS-1$
659
tableBindingsForCommand.setLayoutData(gridData);
660         TableColumn tableColumnDelta = new TableColumn(tableBindingsForCommand,
661                 SWT.NULL, 0);
662         tableColumnDelta.setResizable(false);
663         tableColumnDelta.setText(Util.ZERO_LENGTH_STRING);
664         tableColumnDelta.setWidth(20);
665         TableColumn tableColumnContext = new TableColumn(
666                 tableBindingsForCommand, SWT.NULL, 1);
667         tableColumnContext.setResizable(true);
668         tableColumnContext.setText(Util.translateString(RESOURCE_BUNDLE,
669                 "tableColumnContext")); //$NON-NLS-1$
670
tableColumnContext.pack();
671         tableColumnContext.setWidth(200);
672         final TableColumn tableColumnKeySequence = new TableColumn(
673                 tableBindingsForCommand, SWT.NULL, 2);
674         tableColumnKeySequence.setResizable(true);
675         tableColumnKeySequence.setText(Util.translateString(RESOURCE_BUNDLE,
676                 "tableColumnKeySequence")); //$NON-NLS-1$
677
tableColumnKeySequence.pack();
678         tableColumnKeySequence.setWidth(300);
679
680         tableBindingsForCommand.addMouseListener(new MouseAdapter() {
681
682             public void mouseDoubleClick(MouseEvent mouseEvent) {
683                 update();
684             }
685         });
686
687         tableBindingsForCommand.addSelectionListener(new SelectionAdapter() {
688
689             public void widgetSelected(SelectionEvent selectionEvent) {
690                 selectedTableBindingsForCommand();
691             }
692         });
693
694         final Group groupKeySequence = new Group(composite, SWT.SHADOW_NONE);
695         gridLayout = new GridLayout();
696         gridLayout.numColumns = 4;
697         groupKeySequence.setLayout(gridLayout);
698         gridData = new GridData(GridData.FILL_BOTH);
699         groupKeySequence.setLayoutData(gridData);
700         groupKeySequence.setText(Util.translateString(RESOURCE_BUNDLE,
701                 "groupKeySequence")); //$NON-NLS-1$
702
final Label labelKeySequence = new Label(groupKeySequence, SWT.LEFT);
703         gridData = new GridData();
704         labelKeySequence.setLayoutData(gridData);
705         labelKeySequence.setText(Util.translateString(RESOURCE_BUNDLE,
706                 "labelKeySequence")); //$NON-NLS-1$
707

708         // The text widget into which the key strokes will be entered.
709
textTriggerSequence = new Text(groupKeySequence, SWT.BORDER);
710         // On MacOS X, this font will be changed by KeySequenceText
711
textTriggerSequence.setFont(groupKeySequence.getFont());
712         gridData = new GridData();
713         gridData.horizontalSpan = 2;
714         gridData.widthHint = 300;
715         textTriggerSequence.setLayoutData(gridData);
716         textTriggerSequence.addModifyListener(new ModifyListener() {
717             public void modifyText(ModifyEvent e) {
718                 update();
719             }
720         });
721         textTriggerSequence.addFocusListener(new FocusListener() {
722             public void focusGained(FocusEvent e) {
723                 bindingService.setKeyFilterEnabled(false);
724             }
725
726             public void focusLost(FocusEvent e) {
727                 bindingService.setKeyFilterEnabled(true);
728             }
729         });
730         textTriggerSequence.addDisposeListener(new DisposeListener() {
731             public void widgetDisposed(DisposeEvent e) {
732                 if (!bindingService.isKeyFilterEnabled()) {
733                     bindingService.setKeyFilterEnabled(true);
734                 }
735             }
736         });
737
738         // The manager for the key sequence text widget.
739
textTriggerSequenceManager = new KeySequenceText(textTriggerSequence);
740         textTriggerSequenceManager.setKeyStrokeLimit(4);
741
742         // Button for adding trapped key strokes
743
final Button buttonAddKey = new Button(groupKeySequence, SWT.LEFT
744                 | SWT.ARROW);
745         buttonAddKey.setToolTipText(Util.translateString(RESOURCE_BUNDLE,
746                 "buttonAddKey.ToolTipText")); //$NON-NLS-1$
747
gridData = new GridData();
748         gridData.heightHint = comboCategory.getTextHeight();
749         buttonAddKey.setLayoutData(gridData);
750
751         // Arrow buttons aren't normally added to the tab list. Let's fix that.
752
final Control[] tabStops = groupKeySequence.getTabList();
753         final ArrayList JavaDoc newTabStops = new ArrayList JavaDoc();
754         for (int i = 0; i < tabStops.length; i++) {
755             Control tabStop = tabStops[i];
756             newTabStops.add(tabStop);
757             if (textTriggerSequence.equals(tabStop)) {
758                 newTabStops.add(buttonAddKey);
759             }
760         }
761         final Control[] newTabStopArray = (Control[]) newTabStops
762                 .toArray(new Control[newTabStops.size()]);
763         groupKeySequence.setTabList(newTabStopArray);
764
765         // Construct the menu to attach to the above button.
766
final Menu menuButtonAddKey = new Menu(buttonAddKey);
767         final Iterator JavaDoc trappedKeyItr = KeySequenceText.TRAPPED_KEYS.iterator();
768         while (trappedKeyItr.hasNext()) {
769             final KeyStroke trappedKey = (KeyStroke) trappedKeyItr.next();
770             final MenuItem menuItem = new MenuItem(menuButtonAddKey, SWT.PUSH);
771             menuItem.setText(trappedKey.format());
772             menuItem.addSelectionListener(new SelectionAdapter() {
773
774                 public void widgetSelected(SelectionEvent e) {
775                     textTriggerSequenceManager.insert(trappedKey);
776                     textTriggerSequence.setFocus();
777                     textTriggerSequence.setSelection(textTriggerSequence
778                             .getTextLimit());
779                 }
780             });
781         }
782         buttonAddKey.addSelectionListener(new SelectionAdapter() {
783
784             public void widgetSelected(SelectionEvent selectionEvent) {
785                 Point buttonLocation = buttonAddKey.getLocation();
786                 buttonLocation = groupKeySequence.toDisplay(buttonLocation.x,
787                         buttonLocation.y);
788                 Point buttonSize = buttonAddKey.getSize();
789                 menuButtonAddKey.setLocation(buttonLocation.x, buttonLocation.y
790                         + buttonSize.y);
791                 menuButtonAddKey.setVisible(true);
792             }
793         });
794
795         labelBindingsForTriggerSequence = new Label(groupKeySequence, SWT.LEFT);
796         gridData = new GridData(GridData.VERTICAL_ALIGN_BEGINNING);
797         gridData.verticalAlignment = GridData.FILL_VERTICAL;
798         labelBindingsForTriggerSequence.setLayoutData(gridData);
799         labelBindingsForTriggerSequence.setText(Util.translateString(
800                 RESOURCE_BUNDLE, "labelAssignmentsForKeySequence")); //$NON-NLS-1$
801
tableBindingsForTriggerSequence = new Table(groupKeySequence,
802                 SWT.BORDER | SWT.FULL_SELECTION | SWT.H_SCROLL | SWT.V_SCROLL);
803         tableBindingsForTriggerSequence.setHeaderVisible(true);
804         gridData = new GridData(GridData.FILL_BOTH);
805         gridData.heightHint = 60;
806         gridData.horizontalSpan = 3;
807         gridData.widthHint = "carbon".equals(SWT.getPlatform()) ? 620 : 520; //$NON-NLS-1$
808
tableBindingsForTriggerSequence.setLayoutData(gridData);
809         tableColumnDelta = new TableColumn(tableBindingsForTriggerSequence,
810                 SWT.NULL, 0);
811         tableColumnDelta.setResizable(false);
812         tableColumnDelta.setText(Util.ZERO_LENGTH_STRING);
813         tableColumnDelta.setWidth(20);
814         tableColumnContext = new TableColumn(tableBindingsForTriggerSequence,
815                 SWT.NULL, 1);
816         tableColumnContext.setResizable(true);
817         tableColumnContext.setText(Util.translateString(RESOURCE_BUNDLE,
818                 "tableColumnContext")); //$NON-NLS-1$
819
tableColumnContext.pack();
820         tableColumnContext.setWidth(200);
821         final TableColumn tableColumnCommand = new TableColumn(
822                 tableBindingsForTriggerSequence, SWT.NULL, 2);
823         tableColumnCommand.setResizable(true);
824         tableColumnCommand.setText(Util.translateString(RESOURCE_BUNDLE,
825                 "tableColumnCommand")); //$NON-NLS-1$
826
tableColumnCommand.pack();
827         tableColumnCommand.setWidth(300);
828
829         tableBindingsForTriggerSequence.addMouseListener(new MouseAdapter() {
830
831             public void mouseDoubleClick(MouseEvent mouseEvent) {
832                 update();
833             }
834         });
835
836         tableBindingsForTriggerSequence
837                 .addSelectionListener(new SelectionAdapter() {
838
839                     public void widgetSelected(SelectionEvent selectionEvent) {
840                         selectedTableBindingsForTriggerSequence();
841                     }
842                 });
843
844         final Composite compositeContext = new Composite(composite, SWT.NULL);
845         gridLayout = new GridLayout();
846         gridLayout.numColumns = 3;
847         compositeContext.setLayout(gridLayout);
848         gridData = new GridData(GridData.FILL_HORIZONTAL);
849         compositeContext.setLayoutData(gridData);
850         final Label labelContext = new Label(compositeContext, SWT.LEFT);
851         labelContext.setText(Util.translateString(RESOURCE_BUNDLE,
852                 "labelContext")); //$NON-NLS-1$
853
comboContext = new Combo(compositeContext, SWT.READ_ONLY);
854         gridData = new GridData();
855         gridData.widthHint = 250;
856         comboContext.setLayoutData(gridData);
857         comboContext.setVisibleItemCount(ITEMS_TO_SHOW);
858
859         comboContext.addSelectionListener(new SelectionAdapter() {
860             public final void widgetSelected(final SelectionEvent e) {
861                 update();
862             }
863         });
864
865         labelContextExtends = new Label(compositeContext, SWT.LEFT);
866         gridData = new GridData(GridData.FILL_HORIZONTAL);
867         labelContextExtends.setLayoutData(gridData);
868         final Composite compositeButton = new Composite(composite, SWT.NULL);
869         gridLayout = new GridLayout();
870         gridLayout.marginHeight = 20;
871         gridLayout.marginWidth = 0;
872         gridLayout.numColumns = 3;
873         compositeButton.setLayout(gridLayout);
874         gridData = new GridData();
875         compositeButton.setLayoutData(gridData);
876         buttonAdd = new Button(compositeButton, SWT.CENTER | SWT.PUSH);
877         gridData = new GridData();
878         int widthHint = convertHorizontalDLUsToPixels(IDialogConstants.BUTTON_WIDTH);
879         buttonAdd.setText(Util.translateString(RESOURCE_BUNDLE, "buttonAdd")); //$NON-NLS-1$
880
gridData.widthHint = Math.max(widthHint, buttonAdd.computeSize(
881                 SWT.DEFAULT, SWT.DEFAULT, true).x) + 5;
882         buttonAdd.setLayoutData(gridData);
883
884         buttonAdd.addSelectionListener(new SelectionAdapter() {
885
886             public void widgetSelected(SelectionEvent selectionEvent) {
887                 selectedButtonAdd();
888             }
889         });
890
891         buttonRemove = new Button(compositeButton, SWT.CENTER | SWT.PUSH);
892         gridData = new GridData();
893         widthHint = convertHorizontalDLUsToPixels(IDialogConstants.BUTTON_WIDTH);
894         buttonRemove.setText(Util.translateString(RESOURCE_BUNDLE,
895                 "buttonRemove")); //$NON-NLS-1$
896
gridData.widthHint = Math.max(widthHint, buttonRemove.computeSize(
897                 SWT.DEFAULT, SWT.DEFAULT, true).x) + 5;
898         buttonRemove.setLayoutData(gridData);
899
900         buttonRemove.addSelectionListener(new SelectionAdapter() {
901
902             public void widgetSelected(SelectionEvent selectionEvent) {
903                 selectedButtonRemove();
904             }
905         });
906
907         buttonRestore = new Button(compositeButton, SWT.CENTER | SWT.PUSH);
908         gridData = new GridData();
909         widthHint = convertHorizontalDLUsToPixels(IDialogConstants.BUTTON_WIDTH);
910         buttonRestore.setText(Util.translateString(RESOURCE_BUNDLE,
911                 "buttonRestore")); //$NON-NLS-1$
912
gridData.widthHint = Math.max(widthHint, buttonRestore.computeSize(
913                 SWT.DEFAULT, SWT.DEFAULT, true).x) + 5;
914         buttonRestore.setLayoutData(gridData);
915
916         buttonRestore.addSelectionListener(new SelectionAdapter() {
917
918             public void widgetSelected(SelectionEvent selectionEvent) {
919                 selectedButtonRestore();
920             }
921         });
922
923         return composite;
924     }
925
926     /**
927      * Creates a tab on the main page for displaying an uneditable list of the
928      * current key bindings. This is intended as a discovery tool for new users.
929      * It shows all of the key bindings for the current key configuration,
930      * platform and locale.
931      *
932      * @param parent
933      * The tab folder in which the tab should be created; must not be
934      * <code>null</code>.
935      * @return The newly created composite containing all of the controls; never
936      * <code>null</code>.
937      * @since 3.1
938      */

939     private final Composite createViewTab(final TabFolder parent) {
940         GridData gridData = null;
941         int widthHint;
942
943         // Create the composite for the tab.
944
final Composite composite = new Composite(parent, SWT.NONE);
945         composite.setLayoutData(new GridData(GridData.FILL_BOTH));
946         composite.setLayout(new GridLayout());
947
948         // Place a table inside the tab.
949
tableBindings = new Table(composite, SWT.BORDER | SWT.FULL_SELECTION
950                 | SWT.H_SCROLL | SWT.V_SCROLL);
951         tableBindings.setHeaderVisible(true);
952         gridData = new GridData(GridData.FILL_BOTH);
953         gridData.heightHint = 400;
954         gridData.horizontalSpan = 2;
955         tableBindings.setLayoutData(gridData);
956         final TableColumn tableColumnCategory = new TableColumn(tableBindings,
957                 SWT.NONE, VIEW_CATEGORY_COLUMN_INDEX);
958         tableColumnCategory
959                 .setText(SORTED_COLUMN_NAMES[VIEW_CATEGORY_COLUMN_INDEX]);
960         tableColumnCategory
961                 .addSelectionListener(new SortOrderSelectionListener(
962                         VIEW_CATEGORY_COLUMN_INDEX));
963         final TableColumn tableColumnCommand = new TableColumn(tableBindings,
964                 SWT.NONE, VIEW_COMMAND_COLUMN_INDEX);
965         tableColumnCommand
966                 .setText(UNSORTED_COLUMN_NAMES[VIEW_COMMAND_COLUMN_INDEX]);
967         tableColumnCommand.addSelectionListener(new SortOrderSelectionListener(
968                 VIEW_COMMAND_COLUMN_INDEX));
969         final TableColumn tableColumnKeySequence = new TableColumn(
970                 tableBindings, SWT.NONE, VIEW_KEY_SEQUENCE_COLUMN_INDEX);
971         tableColumnKeySequence
972                 .setText(UNSORTED_COLUMN_NAMES[VIEW_KEY_SEQUENCE_COLUMN_INDEX]);
973         tableColumnKeySequence
974                 .addSelectionListener(new SortOrderSelectionListener(
975                         VIEW_KEY_SEQUENCE_COLUMN_INDEX));
976         final TableColumn tableColumnContext = new TableColumn(tableBindings,
977                 SWT.NONE, VIEW_CONTEXT_COLUMN_INDEX);
978         tableColumnContext
979                 .setText(UNSORTED_COLUMN_NAMES[VIEW_CONTEXT_COLUMN_INDEX]);
980         tableColumnContext.addSelectionListener(new SortOrderSelectionListener(
981                 VIEW_CONTEXT_COLUMN_INDEX));
982         tableBindings.addSelectionListener(new SelectionAdapter() {
983             public final void widgetDefaultSelected(final SelectionEvent e) {
984                 selectedTableKeyBindings();
985             }
986         });
987
988         // A composite for the buttons.
989
final Composite buttonBar = new Composite(composite, SWT.NONE);
990         buttonBar.setLayout(new GridLayout(2, false));
991         gridData = new GridData();
992         gridData.horizontalAlignment = GridData.END;
993         buttonBar.setLayoutData(gridData);
994
995         // A button for editing the current selection.
996
final Button editButton = new Button(buttonBar, SWT.PUSH);
997         gridData = new GridData();
998         widthHint = convertHorizontalDLUsToPixels(IDialogConstants.BUTTON_WIDTH);
999         editButton.setText(Util.translateString(RESOURCE_BUNDLE, "buttonEdit")); //$NON-NLS-1$
1000
gridData.widthHint = Math.max(widthHint, editButton.computeSize(
1001                SWT.DEFAULT, SWT.DEFAULT, true).x) + 5;
1002        editButton.setLayoutData(gridData);
1003        editButton.addSelectionListener(new SelectionListener() {
1004
1005            /*
1006             * (non-Javadoc)
1007             *
1008             * @see org.eclipse.swt.events.SelectionListener#widgetDefaultSelected(org.eclipse.swt.events.SelectionEvent)
1009             */

1010            public final void widgetDefaultSelected(final SelectionEvent event) {
1011                selectedTableKeyBindings();
1012            }
1013
1014            /*
1015             * (non-Javadoc)
1016             *
1017             * @see org.eclipse.swt.events.SelectionListener#widgetSelected(org.eclipse.swt.events.SelectionEvent)
1018             */

1019            public void widgetSelected(SelectionEvent e) {
1020                widgetDefaultSelected(e);
1021            }
1022        });
1023
1024        // A button for exporting the contents to a file.
1025
final Button buttonExport = new Button(buttonBar, SWT.PUSH);
1026        gridData = new GridData();
1027        widthHint = convertHorizontalDLUsToPixels(IDialogConstants.BUTTON_WIDTH);
1028        buttonExport.setText(Util.translateString(RESOURCE_BUNDLE,
1029                "buttonExport")); //$NON-NLS-1$
1030
gridData.widthHint = Math.max(widthHint, buttonExport.computeSize(
1031                SWT.DEFAULT, SWT.DEFAULT, true).x) + 5;
1032        buttonExport.setLayoutData(gridData);
1033        buttonExport.addSelectionListener(new SelectionListener() {
1034
1035            /*
1036             * (non-Javadoc)
1037             *
1038             * @see org.eclipse.swt.events.SelectionListener#widgetDefaultSelected(org.eclipse.swt.events.SelectionEvent)
1039             */

1040            public final void widgetDefaultSelected(final SelectionEvent event) {
1041                selectedButtonExport();
1042            }
1043
1044            /*
1045             * (non-Javadoc)
1046             *
1047             * @see org.eclipse.swt.events.SelectionListener#widgetSelected(org.eclipse.swt.events.SelectionEvent)
1048             */

1049            public void widgetSelected(SelectionEvent e) {
1050                widgetDefaultSelected(e);
1051            }
1052        });
1053
1054        return composite;
1055    }
1056
1057    protected IPreferenceStore doGetPreferenceStore() {
1058        return PrefUtil.getInternalPreferenceStore();
1059    }
1060
1061    /**
1062     * Allows the user to change the key bindings for a particular command.
1063     * Switches the tab to the modify tab, and then selects the category and
1064     * command that corresponds with the given command name. It then selects the
1065     * given key sequence and gives focus to the key sequence text widget.
1066     *
1067     * @param binding
1068     * The binding to be edited; if <code>null</code>, then just
1069     * switch to the modify tab. If the <code>binding</code> does
1070     * not correspond to anything in the keys preference page, then
1071     * this also just switches to the modify tab.
1072     * @since 3.1
1073     */

1074    public final void editBinding(final Binding binding) {
1075        // Switch to the modify tab.
1076
tabFolder.setSelection(TAB_INDEX_MODIFY);
1077
1078        // If there is no command name, stop here.
1079
if (binding == null) {
1080            return;
1081        }
1082
1083        /*
1084         * Get the corresponding category and command names. If either is
1085         * undefined, then we can just stop now. We won't be able to find their
1086         * name.
1087         */

1088        final ParameterizedCommand command = binding.getParameterizedCommand();
1089        String JavaDoc categoryName = null;
1090        String JavaDoc commandName = null;
1091        try {
1092            categoryName = command.getCommand().getCategory().getName();
1093            commandName = command.getName();
1094        } catch (final NotDefinedException e) {
1095            return; // no name
1096
}
1097
1098        // Update the category combo box.
1099
final String JavaDoc[] categoryNames = comboCategory.getItems();
1100        int i = 0;
1101        for (; i < categoryNames.length; i++) {
1102            if (categoryName.equals(categoryNames[i])) {
1103                break;
1104            }
1105        }
1106        if (i >= comboCategory.getItemCount()) {
1107            // Couldn't find the category, so abort.
1108
return;
1109        }
1110        comboCategory.select(i);
1111
1112        // Update the commands combo box.
1113
updateComboCommand();
1114
1115        // Update the command combo box.
1116
final String JavaDoc[] commandNames = comboCommand.getItems();
1117        int j = 0;
1118        for (; j < commandNames.length; j++) {
1119            if (commandName.equals(commandNames[j])) {
1120                if (comboCommand.getSelectionIndex() != j) {
1121                    comboCommand.select(j);
1122                }
1123                break;
1124            }
1125        }
1126        if (j >= comboCommand.getItemCount()) {
1127            // Couldn't find the command, so just select the first and then stop
1128
if (comboCommand.getSelectionIndex() != 0) {
1129                comboCommand.select(0);
1130            }
1131            update();
1132            return;
1133        }
1134
1135        /*
1136         * Update and validate the state of the modify tab in response to these
1137         * selection changes.
1138         */

1139        update();
1140
1141        // Select the right key binding, if possible.
1142
final TableItem[] items = tableBindingsForCommand.getItems();
1143        int k = 0;
1144        for (; k < items.length; k++) {
1145            final String JavaDoc currentKeySequence = items[k].getText(2);
1146            if (binding.getTriggerSequence().format()
1147                    .equals(currentKeySequence)) {
1148                break;
1149            }
1150        }
1151        if (k < tableBindingsForCommand.getItemCount()) {
1152            tableBindingsForCommand.select(k);
1153            tableBindingsForCommand.notifyListeners(SWT.Selection, null);
1154            textTriggerSequence.setFocus();
1155        }
1156    }
1157
1158    /**
1159     * Returns the identifier for the currently selected category.
1160     *
1161     * @return The selected category; <code>null</code> if none.
1162     */

1163    private final String JavaDoc getCategoryId() {
1164        return !commandIdsByCategoryId.containsKey(null)
1165                || comboCategory.getSelectionIndex() > 0 ? (String JavaDoc) categoryIdsByUniqueName
1166                .get(comboCategory.getText())
1167                : null;
1168    }
1169
1170    /**
1171     * Returns the identifier for the currently selected context.
1172     *
1173     * @return The selected context; <code>null</code> if none.
1174     */

1175    private final String JavaDoc getContextId() {
1176        return comboContext.getSelectionIndex() >= 0 ? (String JavaDoc) contextIdsByUniqueName
1177                .get(comboContext.getText())
1178                : null;
1179    }
1180
1181    /**
1182     * Returns the current trigger sequence.
1183     *
1184     * @return The trigger sequence; may be empty, but never <code>null</code>.
1185     */

1186    private final KeySequence getKeySequence() {
1187        return textTriggerSequenceManager.getKeySequence();
1188    }
1189
1190    /**
1191     * Returns the currently-selected fully-parameterized command.
1192     *
1193     * @return The selected fully-parameterized command; <code>null</code> if
1194     * none.
1195     */

1196    private final ParameterizedCommand getParameterizedCommand() {
1197        final int selectionIndex = comboCommand.getSelectionIndex();
1198        if ((selectionIndex >= 0) && (commands != null)
1199                && (selectionIndex < commands.length)) {
1200            return commands[selectionIndex];
1201        }
1202
1203        return null;
1204    }
1205
1206    /**
1207     * Returns the identifier for the currently selected scheme.
1208     *
1209     * @return The selected scheme; <code>null</code> if none.
1210     */

1211    private final String JavaDoc getSchemeId() {
1212        return comboScheme.getSelectionIndex() >= 0 ? (String JavaDoc) schemeIdsByUniqueName
1213                .get(comboScheme.getText())
1214                : null;
1215    }
1216
1217    public final void init(final IWorkbench workbench) {
1218        activityManager = workbench.getActivitySupport().getActivityManager();
1219        bindingService = (IBindingService) workbench.getService(IBindingService.class);
1220        commandService = (ICommandService) workbench.getService(ICommandService.class);
1221        contextService = (IContextService) workbench.getService(IContextService.class);
1222    }
1223
1224    /**
1225     * Checks whether the activity manager knows anything about this command
1226     * identifier. If the activity manager is currently filtering this command,
1227     * then it does not appear in the user interface.
1228     *
1229     * @param command
1230     * The command which should be checked against the activities;
1231     * must not be <code>null</code>.
1232     * @return <code>true</code> if the command identifier is not filtered;
1233     * <code>false</code> if it is
1234     */

1235    private final boolean isActive(final Command command) {
1236        return activityManager.getIdentifier(command.getId()).isEnabled();
1237    }
1238
1239    /**
1240     * Logs the given exception, and opens an error dialog saying that something
1241     * went wrong. The exception is assumed to have something to do with the
1242     * preference store.
1243     *
1244     * @param exception
1245     * The exception to be logged; must not be <code>null</code>.
1246     */

1247    private final void logPreferenceStoreException(final Throwable JavaDoc exception) {
1248        final String JavaDoc message = Util.translateString(RESOURCE_BUNDLE,
1249                "PreferenceStoreError.Message"); //$NON-NLS-1$
1250
String JavaDoc exceptionMessage = exception.getMessage();
1251        if (exceptionMessage == null) {
1252            exceptionMessage = message;
1253        }
1254        final IStatus status = new Status(IStatus.ERROR,
1255                WorkbenchPlugin.PI_WORKBENCH, 0, exceptionMessage, exception);
1256        WorkbenchPlugin.log(message, status);
1257        StatusUtil.handleStatus(message, exception, StatusManager.SHOW);
1258    }
1259
1260    public final boolean performCancel() {
1261        // Save the selected tab for future reference.
1262
persistSelectedTab();
1263
1264        return super.performCancel();
1265    }
1266
1267    protected final void performDefaults() {
1268        // Ask the user to confirm
1269
final String JavaDoc title = Util.translateString(RESOURCE_BUNDLE,
1270                "restoreDefaultsMessageBoxText"); //$NON-NLS-1$
1271
final String JavaDoc message = Util.translateString(RESOURCE_BUNDLE,
1272                "restoreDefaultsMessageBoxMessage"); //$NON-NLS-1$
1273
final boolean confirmed = MessageDialog.openConfirm(getShell(), title,
1274                message);
1275
1276        if (confirmed) {
1277            // Fix the scheme in the local changes.
1278
final String JavaDoc defaultSchemeId = bindingService.getDefaultSchemeId();
1279            final Scheme defaultScheme = localChangeManager
1280                    .getScheme(defaultSchemeId);
1281            try {
1282                localChangeManager.setActiveScheme(defaultScheme);
1283            } catch (final NotDefinedException e) {
1284                // At least we tried....
1285
}
1286
1287            // Fix the bindings in the local changes.
1288
final Binding[] currentBindings = localChangeManager.getBindings();
1289            final int currentBindingsLength = currentBindings.length;
1290            final Set JavaDoc trimmedBindings = new HashSet JavaDoc();
1291            for (int i = 0; i < currentBindingsLength; i++) {
1292                final Binding binding = currentBindings[i];
1293                if (binding.getType() != Binding.USER) {
1294                    trimmedBindings.add(binding);
1295                }
1296            }
1297            final Binding[] trimmedBindingArray = (Binding[]) trimmedBindings
1298                    .toArray(new Binding[trimmedBindings.size()]);
1299            localChangeManager.setBindings(trimmedBindingArray);
1300
1301            // Apply the changes.
1302
try {
1303                bindingService.savePreferences(defaultScheme,
1304                        trimmedBindingArray);
1305            } catch (final IOException JavaDoc e) {
1306                logPreferenceStoreException(e);
1307            }
1308        }
1309
1310        setScheme(localChangeManager.getActiveScheme()); // update the scheme
1311
update(true);
1312        super.performDefaults();
1313    }
1314
1315    public final boolean performOk() {
1316        // Save the preferences.
1317
try {
1318            bindingService.savePreferences(
1319                    localChangeManager.getActiveScheme(), localChangeManager
1320                            .getBindings());
1321        } catch (final IOException JavaDoc e) {
1322            logPreferenceStoreException(e);
1323        }
1324
1325        // Save the selected tab for future reference.
1326
persistSelectedTab();
1327
1328        return super.performOk();
1329    }
1330
1331    /**
1332     * Remembers the currently selected tab for when the preference page next
1333     * opens.
1334     */

1335    private final void persistSelectedTab() {
1336        final IPreferenceStore store = getPreferenceStore();
1337        store.setValue(IPreferenceConstants.KEYS_PREFERENCE_SELECTED_TAB,
1338                tabFolder.getSelectionIndex());
1339    }
1340
1341    /**
1342     * Handles the selection event on the add button. This removes all
1343     * user-defined bindings matching the given key sequence, scheme and
1344     * context. It then adds a new binding with the current selections.
1345     */

1346    private final void selectedButtonAdd() {
1347        final ParameterizedCommand command = getParameterizedCommand();
1348        final String JavaDoc contextId = getContextId();
1349        final String JavaDoc schemeId = getSchemeId();
1350        final KeySequence keySequence = getKeySequence();
1351        localChangeManager.removeBindings(keySequence, schemeId, contextId,
1352                null, null, null, Binding.USER);
1353        localChangeManager.addBinding(new KeyBinding(keySequence, command,
1354                schemeId, contextId, null, null, null, Binding.USER));
1355        update(true);
1356    }
1357
1358    /**
1359     * Provides a facility for exporting the viewable list of key bindings to a
1360     * file. Currently, this only supports exporting to a list of
1361     * comma-separated values. The user is prompted for which file should
1362     * receive our bounty.
1363     *
1364     * @since 3.1
1365     */

1366    private final void selectedButtonExport() {
1367        final FileDialog fileDialog = new FileDialog(getShell(), SWT.SAVE);
1368        fileDialog.setFilterExtensions(new String JavaDoc[] { "*.csv" }); //$NON-NLS-1$
1369
fileDialog.setFilterNames(new String JavaDoc[] { Util.translateString(
1370                RESOURCE_BUNDLE, "csvFilterName") }); //$NON-NLS-1$
1371
final String JavaDoc filePath = fileDialog.open();
1372        if (filePath == null) {
1373            return;
1374        }
1375
1376        final SafeRunnable runnable = new SafeRunnable() {
1377            public final void run() throws IOException JavaDoc {
1378                Writer JavaDoc fileWriter = null;
1379                try {
1380                    fileWriter = new BufferedWriter JavaDoc(new FileWriter JavaDoc(filePath));
1381                    final TableItem[] items = tableBindings.getItems();
1382                    final int numColumns = tableBindings.getColumnCount();
1383                    for (int i = 0; i < items.length; i++) {
1384                        final TableItem item = items[i];
1385                        for (int j = 0; j < numColumns; j++) {
1386                            String JavaDoc buf = Util.replaceAll(item.getText(j), "\"", //$NON-NLS-1$
1387
"\"\""); //$NON-NLS-1$
1388
fileWriter.write("\"" + buf + "\""); //$NON-NLS-1$//$NON-NLS-2$
1389
if (j < numColumns - 1) {
1390                                fileWriter.write(',');
1391                            }
1392                        }
1393                        fileWriter.write(System.getProperty("line.separator")); //$NON-NLS-1$
1394
}
1395
1396                } finally {
1397                    if (fileWriter != null) {
1398                        try {
1399                            fileWriter.close();
1400                        } catch (final IOException JavaDoc e) {
1401                            // At least I tried.
1402
}
1403                    }
1404
1405                }
1406            }
1407        };
1408        SafeRunner.run(runnable);
1409    }
1410    
1411    /**
1412     * Handles the selection event on the remove button. This removes all
1413     * user-defined bindings matching the given key sequence, scheme and
1414     * context. It then adds a new deletion binding for the selected trigger
1415     * sequence.
1416     */

1417    private final void selectedButtonRemove() {
1418        final String JavaDoc contextId = getContextId();
1419        final String JavaDoc schemeId = getSchemeId();
1420        final KeySequence keySequence = getKeySequence();
1421        localChangeManager.removeBindings(keySequence, schemeId, contextId,
1422                null, null, null, Binding.USER);
1423        localChangeManager.addBinding(new KeyBinding(keySequence, null,
1424                schemeId, contextId, null, null, null, Binding.USER));
1425        update(true);
1426    }
1427
1428    /**
1429     * Handles the selection event on the restore button. This removes all
1430     * user-defined bindings matching the given key sequence, scheme and
1431     * context.
1432     */

1433    private final void selectedButtonRestore() {
1434        String JavaDoc contextId = getContextId();
1435        String JavaDoc schemeId = getSchemeId();
1436        KeySequence keySequence = getKeySequence();
1437        localChangeManager.removeBindings(keySequence, schemeId, contextId,
1438                null, null, null, Binding.USER);
1439        update(true);
1440    }
1441
1442    /**
1443     * Updates the local managers active scheme, and then updates the interface.
1444     */

1445    private final void selectedComboScheme() {
1446        final String JavaDoc activeSchemeId = getSchemeId();
1447        final Scheme activeScheme = localChangeManager
1448                .getScheme(activeSchemeId);
1449        try {
1450            localChangeManager.setActiveScheme(activeScheme);
1451        } catch (final NotDefinedException e) {
1452            // Oh, well.
1453
}
1454        update(true);
1455    }
1456
1457    /**
1458     * Handles the selection event on the table containing the bindings for a
1459     * particular command. This updates the context and trigger sequence based
1460     * on the selected binding.
1461     */

1462    private final void selectedTableBindingsForCommand() {
1463        final int selection = tableBindingsForCommand.getSelectionIndex();
1464        if ((selection >= 0)
1465                && (selection < tableBindingsForCommand.getItemCount())) {
1466            final TableItem item = tableBindingsForCommand.getItem(selection);
1467            final KeyBinding binding = (KeyBinding) item.getData(ITEM_DATA_KEY);
1468            setContextId(binding.getContextId());
1469            setKeySequence(binding.getKeySequence());
1470        }
1471
1472        update();
1473    }
1474
1475    /**
1476     * Handles the selection event on the table containing the bindings for a
1477     * particular trigger sequence. This updates the context based on the
1478     * selected binding.
1479     */

1480    private final void selectedTableBindingsForTriggerSequence() {
1481        final int selection = tableBindingsForTriggerSequence
1482                .getSelectionIndex();
1483        if ((selection >= 0)
1484                && (selection < tableBindingsForTriggerSequence.getItemCount())) {
1485            final TableItem item = tableBindingsForTriggerSequence
1486                    .getItem(selection);
1487            final Binding binding = (Binding) item.getData(ITEM_DATA_KEY);
1488            setContextId(binding.getContextId());
1489        }
1490
1491        update();
1492    }
1493
1494    /**
1495     * Responds to some kind of trigger on the View tab by taking the current
1496     * selection on the key bindings table and selecting the appropriate items
1497     * in the Modify tab.
1498     *
1499     * @since 3.1
1500     */

1501    private final void selectedTableKeyBindings() {
1502        final int selectionIndex = tableBindings.getSelectionIndex();
1503        if (selectionIndex != -1) {
1504            final TableItem item = tableBindings.getItem(selectionIndex);
1505            final Binding binding = (Binding) item.getData(BINDING_KEY);
1506            editBinding(binding);
1507
1508        } else {
1509            editBinding(null);
1510        }
1511    }
1512
1513    /**
1514     * Changes the selected context name in the context combo box. The context
1515     * selected is either the one matching the identifier provided (if
1516     * possible), or the default context identifier. If no matching name can be
1517     * found in the combo, then the first item is selected.
1518     *
1519     * @param contextId
1520     * The context identifier for the context to be selected in the
1521     * combo box; may be <code>null</code>.
1522     */

1523    private final void setContextId(final String JavaDoc contextId) {
1524        // Clear the current selection.
1525
comboContext.clearSelection();
1526        comboContext.deselectAll();
1527
1528        // Figure out which name to look for.
1529
String JavaDoc contextName = (String JavaDoc) contextUniqueNamesById.get(contextId);
1530        if (contextName == null) {
1531            contextName = (String JavaDoc) contextUniqueNamesById
1532                    .get(IContextIds.CONTEXT_ID_WINDOW);
1533        }
1534        if (contextName == null) {
1535            contextName = Util.ZERO_LENGTH_STRING;
1536        }
1537
1538        // Scan the list for the selection we're looking for.
1539
final String JavaDoc[] items = comboContext.getItems();
1540        boolean found = false;
1541        for (int i = 0; i < items.length; i++) {
1542            if (contextName.equals(items[i])) {
1543                comboContext.select(i);
1544                found = true;
1545                break;
1546            }
1547        }
1548
1549        // If we didn't find an item, then set the first item as selected.
1550
if ((!found) && (items.length > 0)) {
1551            comboContext.select(0);
1552        }
1553    }
1554
1555    /**
1556     * Sets the current trigger sequence.
1557     *
1558     * @param keySequence
1559     * The trigger sequence; may be <code>null</code>.
1560     */

1561    private final void setKeySequence(final KeySequence keySequence) {
1562        textTriggerSequenceManager.setKeySequence(keySequence);
1563    }
1564
1565    /**
1566     * Changes the selection in the command combo box.
1567     *
1568     * @param command
1569     * The fully-parameterized command to select; may be
1570     * <code>null</code>.
1571     */

1572    private final void setParameterizedCommand(
1573            final ParameterizedCommand command) {
1574        int i = 0;
1575        if (commands != null) {
1576            final int commandCount = commands.length;
1577            for (; i < commandCount; i++) {
1578                if (commands[i].equals(command)) {
1579                    if ((comboCommand.getSelectionIndex() != i)
1580                            && (i < comboCommand.getItemCount())) {
1581                        comboCommand.select(i);
1582                    }
1583                    break;
1584                }
1585            }
1586            if ((i >= comboCommand.getItemCount())
1587                    && (comboCommand.getSelectionIndex() != 0)) {
1588                comboCommand.select(0);
1589            }
1590        }
1591    }
1592
1593    /**
1594     * Sets the currently selected scheme
1595     *
1596     * @param scheme
1597     * The scheme to select; may be <code>null</code>.
1598     */

1599    private final void setScheme(final Scheme scheme) {
1600        comboScheme.clearSelection();
1601        comboScheme.deselectAll();
1602        final String JavaDoc schemeUniqueName = (String JavaDoc) schemeUniqueNamesById
1603                .get(scheme.getId());
1604
1605        if (schemeUniqueName != null) {
1606            final String JavaDoc items[] = comboScheme.getItems();
1607
1608            for (int i = 0; i < items.length; i++) {
1609                if (schemeUniqueName.equals(items[i])) {
1610                    comboScheme.select(i);
1611                    break;
1612                }
1613            }
1614        }
1615    }
1616
1617    /**
1618     * Builds the internal look-up tables before allowing the page to become
1619     * visible.
1620     */

1621    public final void setVisible(final boolean visible) {
1622        if (visible == true) {
1623            Map JavaDoc contextsByName = new HashMap JavaDoc();
1624
1625            for (Iterator JavaDoc iterator = contextService.getDefinedContextIds()
1626                    .iterator(); iterator.hasNext();) {
1627                Context context = contextService.getContext((String JavaDoc) iterator
1628                        .next());
1629                try {
1630                    String JavaDoc name = context.getName();
1631                    Collection JavaDoc contexts = (Collection JavaDoc) contextsByName.get(name);
1632
1633                    if (contexts == null) {
1634                        contexts = new HashSet JavaDoc();
1635                        contextsByName.put(name, contexts);
1636                    }
1637
1638                    contexts.add(context);
1639                } catch (final NotDefinedException e) {
1640                    // Do nothing.
1641
}
1642            }
1643            
1644            Map JavaDoc commandsByName = new HashMap JavaDoc();
1645
1646            for (Iterator JavaDoc iterator = commandService.getDefinedCommandIds()
1647                    .iterator(); iterator.hasNext();) {
1648                Command command = commandService.getCommand((String JavaDoc) iterator
1649                        .next());
1650                if (!isActive(command)) {
1651                    continue;
1652                }
1653
1654                try {
1655                    String JavaDoc name = command.getName();
1656                    Collection JavaDoc commands = (Collection JavaDoc) commandsByName.get(name);
1657
1658                    if (commands == null) {
1659                        commands = new HashSet JavaDoc();
1660                        commandsByName.put(name, commands);
1661                    }
1662
1663                    commands.add(command);
1664                } catch (NotDefinedException eNotDefined) {
1665                    // Do nothing
1666
}
1667            }
1668            
1669            // moved here to allow us to remove any empty categories
1670
commandIdsByCategoryId = new HashMap JavaDoc();
1671
1672            for (Iterator JavaDoc iterator = commandService.getDefinedCommandIds()
1673                    .iterator(); iterator.hasNext();) {
1674                final Command command = commandService
1675                        .getCommand((String JavaDoc) iterator.next());
1676                if (!isActive(command)) {
1677                    continue;
1678                }
1679
1680                try {
1681                    String JavaDoc categoryId = command.getCategory().getId();
1682                    Collection JavaDoc commandIds = (Collection JavaDoc) commandIdsByCategoryId
1683                            .get(categoryId);
1684
1685                    if (commandIds == null) {
1686                        commandIds = new HashSet JavaDoc();
1687                        commandIdsByCategoryId.put(categoryId, commandIds);
1688                    }
1689
1690                    commandIds.add(command.getId());
1691                } catch (NotDefinedException eNotDefined) {
1692                    // Do nothing
1693
}
1694            }
1695
1696            Map JavaDoc categoriesByName = new HashMap JavaDoc();
1697
1698            for (Iterator JavaDoc iterator = commandService.getDefinedCategoryIds()
1699                    .iterator(); iterator.hasNext();) {
1700                Category category = commandService
1701                        .getCategory((String JavaDoc) iterator.next());
1702
1703                try {
1704                    if (commandIdsByCategoryId.containsKey(category.getId())) {
1705                        String JavaDoc name = category.getName();
1706                        Collection JavaDoc categories = (Collection JavaDoc) categoriesByName
1707                                .get(name);
1708
1709                        if (categories == null) {
1710                            categories = new HashSet JavaDoc();
1711                            categoriesByName.put(name, categories);
1712                        }
1713
1714                        categories.add(category);
1715                    }
1716                } catch (NotDefinedException eNotDefined) {
1717                    // Do nothing
1718
}
1719            }
1720
1721            Map JavaDoc schemesByName = new HashMap JavaDoc();
1722
1723            final Scheme[] definedSchemes = bindingService.getDefinedSchemes();
1724            for (int i = 0; i < definedSchemes.length; i++) {
1725                final Scheme scheme = definedSchemes[i];
1726                try {
1727                    String JavaDoc name = scheme.getName();
1728                    Collection JavaDoc schemes = (Collection JavaDoc) schemesByName.get(name);
1729
1730                    if (schemes == null) {
1731                        schemes = new HashSet JavaDoc();
1732                        schemesByName.put(name, schemes);
1733                    }
1734
1735                    schemes.add(scheme);
1736                } catch (final NotDefinedException e) {
1737                    // Do nothing.
1738
}
1739            }
1740
1741            contextIdsByUniqueName = new HashMap JavaDoc();
1742            contextUniqueNamesById = new HashMap JavaDoc();
1743
1744            for (Iterator JavaDoc iterator = contextsByName.entrySet().iterator(); iterator
1745                    .hasNext();) {
1746                Map.Entry JavaDoc entry = (Map.Entry JavaDoc) iterator.next();
1747                String JavaDoc name = (String JavaDoc) entry.getKey();
1748                Set JavaDoc contexts = (Set JavaDoc) entry.getValue();
1749                Iterator JavaDoc iterator2 = contexts.iterator();
1750
1751                if (contexts.size() == 1) {
1752                    Context context = (Context) iterator2.next();
1753                    contextIdsByUniqueName.put(name, context.getId());
1754                    contextUniqueNamesById.put(context.getId(), name);
1755                } else {
1756                    while (iterator2.hasNext()) {
1757                        Context context = (Context) iterator2.next();
1758                        String JavaDoc uniqueName = MessageFormat.format(
1759                                Util.translateString(RESOURCE_BUNDLE,
1760                                        "uniqueName"), new Object JavaDoc[] { name, //$NON-NLS-1$
1761
context.getId() });
1762                        contextIdsByUniqueName.put(uniqueName, context.getId());
1763                        contextUniqueNamesById.put(context.getId(), uniqueName);
1764                    }
1765                }
1766            }
1767
1768            categoryIdsByUniqueName = new HashMap JavaDoc();
1769            categoryUniqueNamesById = new HashMap JavaDoc();
1770
1771            for (Iterator JavaDoc iterator = categoriesByName.entrySet().iterator(); iterator
1772                    .hasNext();) {
1773                Map.Entry JavaDoc entry = (Map.Entry JavaDoc) iterator.next();
1774                String JavaDoc name = (String JavaDoc) entry.getKey();
1775                Set JavaDoc categories = (Set JavaDoc) entry.getValue();
1776                Iterator JavaDoc iterator2 = categories.iterator();
1777
1778                if (categories.size() == 1) {
1779                    Category category = (Category) iterator2.next();
1780                    categoryIdsByUniqueName.put(name, category.getId());
1781                    categoryUniqueNamesById.put(category.getId(), name);
1782                } else {
1783                    while (iterator2.hasNext()) {
1784                        Category category = (Category) iterator2.next();
1785                        String JavaDoc uniqueName = MessageFormat.format(
1786                                Util.translateString(RESOURCE_BUNDLE,
1787                                        "uniqueName"), new Object JavaDoc[] { name, //$NON-NLS-1$
1788
category.getId() });
1789                        categoryIdsByUniqueName.put(uniqueName, category
1790                                .getId());
1791                        categoryUniqueNamesById.put(category.getId(),
1792                                uniqueName);
1793                    }
1794                }
1795            }
1796
1797            schemeIdsByUniqueName = new HashMap JavaDoc();
1798            schemeUniqueNamesById = new HashMap JavaDoc();
1799
1800            for (Iterator JavaDoc iterator = schemesByName.entrySet().iterator(); iterator
1801                    .hasNext();) {
1802                Map.Entry JavaDoc entry = (Map.Entry JavaDoc) iterator.next();
1803                String JavaDoc name = (String JavaDoc) entry.getKey();
1804                Set JavaDoc keyConfigurations = (Set JavaDoc) entry.getValue();
1805                Iterator JavaDoc iterator2 = keyConfigurations.iterator();
1806
1807                if (keyConfigurations.size() == 1) {
1808                    Scheme scheme = (Scheme) iterator2.next();
1809                    schemeIdsByUniqueName.put(name, scheme.getId());
1810                    schemeUniqueNamesById.put(scheme.getId(), name);
1811                } else {
1812                    while (iterator2.hasNext()) {
1813                        Scheme scheme = (Scheme) iterator2.next();
1814                        String JavaDoc uniqueName = MessageFormat.format(
1815                                Util.translateString(RESOURCE_BUNDLE,
1816                                        "uniqueName"), new Object JavaDoc[] { name, //$NON-NLS-1$
1817
scheme.getId() });
1818                        schemeIdsByUniqueName.put(uniqueName, scheme.getId());
1819                        schemeUniqueNamesById.put(scheme.getId(), uniqueName);
1820                    }
1821                }
1822            }
1823
1824            Scheme activeScheme = bindingService.getActiveScheme();
1825
1826            // Make an internal copy of the binding manager, for local changes.
1827
try {
1828                for (int i = 0; i < definedSchemes.length; i++) {
1829                    final Scheme scheme = definedSchemes[i];
1830                    final Scheme copy = localChangeManager.getScheme(scheme
1831                            .getId());
1832                    copy.define(scheme.getName(), scheme.getDescription(),
1833                            scheme.getParentId());
1834                }
1835                localChangeManager.setActiveScheme(bindingService
1836                        .getActiveScheme());
1837            } catch (final NotDefinedException e) {
1838                throw new Error JavaDoc(
1839                        "There is a programmer error in the keys preference page"); //$NON-NLS-1$
1840
}
1841            localChangeManager.setLocale(bindingService.getLocale());
1842            localChangeManager.setPlatform(bindingService.getPlatform());
1843            localChangeManager.setBindings(bindingService.getBindings());
1844
1845            // Populate the category combo box.
1846
List JavaDoc categoryNames = new ArrayList JavaDoc(categoryIdsByUniqueName.keySet());
1847            Collections.sort(categoryNames, Collator.getInstance());
1848            if (commandIdsByCategoryId.containsKey(null)) {
1849                categoryNames.add(0, Util.translateString(RESOURCE_BUNDLE,
1850                        "other")); //$NON-NLS-1$
1851
}
1852            comboCategory.setItems((String JavaDoc[]) categoryNames
1853                    .toArray(new String JavaDoc[categoryNames.size()]));
1854            comboCategory.clearSelection();
1855            comboCategory.deselectAll();
1856            if (commandIdsByCategoryId.containsKey(null)
1857                    || !categoryNames.isEmpty()) {
1858                comboCategory.select(0);
1859            }
1860
1861            // Populate the scheme combo box.
1862
List JavaDoc schemeNames = new ArrayList JavaDoc(schemeIdsByUniqueName.keySet());
1863            Collections.sort(schemeNames, Collator.getInstance());
1864            comboScheme.setItems((String JavaDoc[]) schemeNames
1865                    .toArray(new String JavaDoc[schemeNames.size()]));
1866            setScheme(activeScheme);
1867
1868            // Update the entire page.
1869
update(true);
1870        }
1871
1872        super.setVisible(visible);
1873    }
1874
1875    /**
1876     * Updates the entire preference page -- except the view tab -- based on
1877     * current selection sate. This preference page is written so that
1878     * everything can be made consistent simply by inspecting the state of its
1879     * widgets. A change is triggered by the user, and an event is fired. The
1880     * event triggers an update. It is possible for extra work to be done by
1881     * this page before calling update.
1882     */

1883    private final void update() {
1884        update(false);
1885    }
1886
1887    /**
1888     * Updates the entire preference page based on current changes. This
1889     * preference page is written so that everything can be made consistent
1890     * simply by inspecting the state of its widgets. A change is triggered by
1891     * the user, and an event is fired. The event triggers an update. It is
1892     * possible for extra work to be done by this page before calling update.
1893     *
1894     * @param updateViewTab
1895     * Whether the view tab should be updated as well.
1896     */

1897    private final void update(final boolean updateViewTab) {
1898        if (updateViewTab) {
1899            updateViewTab();
1900        }
1901        updateComboCommand();
1902        updateComboContext();
1903        final TriggerSequence triggerSequence = getKeySequence();
1904        updateTableBindingsForTriggerSequence(triggerSequence);
1905        final ParameterizedCommand command = getParameterizedCommand();
1906        updateTableBindingsForCommand(command);
1907        final String JavaDoc contextId = getContextId();
1908        updateSelection(tableBindingsForTriggerSequence, contextId,
1909                triggerSequence);
1910        updateSelection(tableBindingsForCommand, contextId, triggerSequence);
1911        updateLabelSchemeExtends();
1912        updateLabelContextExtends();
1913        updateEnabled(triggerSequence, command);
1914    }
1915
1916    /**
1917     * Updates the contents of the commands combo box, based on the current
1918     * selection in the category combo box.
1919     */

1920    private final void updateComboCommand() {
1921        // Remember the current selection, so we can restore it later.
1922
final ParameterizedCommand command = getParameterizedCommand();
1923
1924        // Figure out where command identifiers apply to the selected category.
1925
final String JavaDoc categoryId = getCategoryId();
1926        Set JavaDoc commandIds = (Set JavaDoc) commandIdsByCategoryId.get(categoryId);
1927        if (commandIds==null) {
1928            commandIds = Collections.EMPTY_SET;
1929        }
1930
1931        /*
1932         * Generate an array of parameterized commands based on these
1933         * identifiers. The parameterized commands will be sorted based on their
1934         * names.
1935         */

1936        List JavaDoc commands = new ArrayList JavaDoc();
1937        final Iterator JavaDoc commandIdItr = commandIds.iterator();
1938        while (commandIdItr.hasNext()) {
1939            final String JavaDoc currentCommandId = (String JavaDoc) commandIdItr.next();
1940            final Command currentCommand = commandService
1941                    .getCommand(currentCommandId);
1942            try {
1943                commands.addAll(ParameterizedCommand
1944                        .generateCombinations(currentCommand));
1945            } catch (final NotDefinedException e) {
1946                // It is safe to just ignore undefined commands.
1947
}
1948        }
1949        
1950        // sort the commands with a collator, so they appear in the
1951
// combo correctly
1952
commands = sortParameterizedCommands(commands);
1953        
1954        final int commandCount = commands.size();
1955        this.commands = (ParameterizedCommand[]) commands
1956                .toArray(new ParameterizedCommand[commandCount]);
1957
1958        /*
1959         * Generate an array of command names based on this array of
1960         * parameterized commands.
1961         */

1962        final String JavaDoc[] commandNames = new String JavaDoc[commandCount];
1963        for (int i = 0; i < commandCount; i++) {
1964            try {
1965                commandNames[i] = this.commands[i].getName();
1966            } catch (final NotDefinedException e) {
1967                throw new Error JavaDoc(
1968                        "Concurrent modification of the command's defined state"); //$NON-NLS-1$
1969
}
1970        }
1971
1972        /*
1973         * Copy the command names into the combo box, but only if they've
1974         * changed. We do this to try to avoid unnecessary calls out to the
1975         * operating system, as well as to defend against bugs in SWT's event
1976         * mechanism.
1977         */

1978        final String JavaDoc[] currentItems = comboCommand.getItems();
1979        if (!Arrays.equals(currentItems, commandNames)) {
1980            comboCommand.setItems(commandNames);
1981        }
1982
1983        // Try to restore the selection.
1984
setParameterizedCommand(command);
1985
1986        /*
1987         * Just to be extra careful, make sure that we have a selection at this
1988         * point. This line could probably be removed, but it makes the code a
1989         * bit more robust.
1990         */

1991        if ((comboCommand.getSelectionIndex() == -1) && (commandCount > 0)) {
1992            comboCommand.select(0);
1993        }
1994    }
1995    
1996    /**
1997     * Sort the commands using the correct language.
1998     * @param commands the List of ParameterizedCommands
1999     * @return The sorted List
2000     */

2001    private List JavaDoc sortParameterizedCommands(List JavaDoc commands) {
2002        final Collator collator = Collator.getInstance();
2003        
2004        // this comparator is based on the ParameterizedCommands#compareTo(*)
2005
// method, but uses the collator.
2006
Comparator JavaDoc comparator = new Comparator JavaDoc() {
2007            public int compare(Object JavaDoc o1, Object JavaDoc o2) {
2008                String JavaDoc name1 = null;
2009                String JavaDoc name2 = null;
2010                try {
2011                    name1 = ((ParameterizedCommand) o1).getName();
2012                } catch (NotDefinedException e) {
2013                    return -1;
2014                }
2015                try {
2016                    name2 = ((ParameterizedCommand) o2).getName();
2017                } catch (NotDefinedException e) {
2018                    return 1;
2019                }
2020                int rc = collator.compare(name1, name2);
2021                if (rc != 0) {
2022                    return rc;
2023                }
2024
2025                String JavaDoc id1 = ((ParameterizedCommand) o1).getId();
2026                String JavaDoc id2 = ((ParameterizedCommand) o2).getId();
2027                return collator.compare(id1, id2);
2028            }
2029        };
2030        Collections.sort(commands, comparator);
2031        return commands;
2032    }
2033
2034    /**
2035     * Updates the contents of the context combo box, as well as its selection.
2036     */

2037    private final void updateComboContext() {
2038        final String JavaDoc contextId = getContextId();
2039        final Map JavaDoc contextIdsByName = new HashMap JavaDoc(contextIdsByUniqueName);
2040
2041        final List JavaDoc contextNames = new ArrayList JavaDoc(contextIdsByName.keySet());
2042        Collections.sort(contextNames, Collator.getInstance());
2043
2044        comboContext.setItems((String JavaDoc[]) contextNames
2045                .toArray(new String JavaDoc[contextNames.size()]));
2046        setContextId(contextId);
2047
2048        if (comboContext.getSelectionIndex() == -1 && !contextNames.isEmpty()) {
2049            comboContext.select(0);
2050        }
2051    }
2052
2053    /**
2054     * Updates the enabled state of the various widgets on this page. The
2055     * decision is based on the current trigger sequence and the currently
2056     * selected command.
2057     *
2058     * @param triggerSequence
2059     * The current trigger sequence; may be empty, but never
2060     * <code>null</code>.
2061     * @param command
2062     * The currently selected command, if any; <code>null</code>
2063     * otherwise.
2064     */

2065    private final void updateEnabled(final TriggerSequence triggerSequence,
2066            final ParameterizedCommand command) {
2067        final boolean commandSelected = command != null;
2068        labelBindingsForCommand.setEnabled(commandSelected);
2069        tableBindingsForCommand.setEnabled(commandSelected);
2070
2071        final boolean triggerSequenceSelected = !triggerSequence.isEmpty();
2072        labelBindingsForTriggerSequence.setEnabled(triggerSequenceSelected);
2073        tableBindingsForTriggerSequence.setEnabled(triggerSequenceSelected);
2074
2075        /*
2076         * TODO Do some better button enablement.
2077         */

2078        final boolean buttonsEnabled = commandSelected
2079                && triggerSequenceSelected;
2080        buttonAdd.setEnabled(buttonsEnabled);
2081        buttonRemove.setEnabled(buttonsEnabled);
2082        buttonRestore.setEnabled(buttonsEnabled);
2083    }
2084
2085    /**
2086     * Updates the label next to the context that says "extends" if the context
2087     * is a child of another context. If the context is not a child of another
2088     * context, then the label is simply blank.
2089     */

2090    private final void updateLabelContextExtends() {
2091        final String JavaDoc contextId = getContextId();
2092
2093        if (contextId != null) {
2094            final Context context = contextService.getContext(getContextId());
2095            if (context.isDefined()) {
2096                try {
2097                    final String JavaDoc parentId = context.getParentId();
2098                    if (parentId != null) {
2099                        final String JavaDoc name = (String JavaDoc) contextUniqueNamesById
2100                                .get(parentId);
2101                        if (name != null) {
2102                            labelContextExtends.setText(MessageFormat.format(
2103                                    Util.translateString(RESOURCE_BUNDLE,
2104                                            "extends"), //$NON-NLS-1$
2105
new Object JavaDoc[] { name }));
2106                            return;
2107                        }
2108                    }
2109                } catch (final NotDefinedException e) {
2110                    // Do nothing
2111
}
2112            }
2113        }
2114
2115        labelContextExtends.setText(Util.ZERO_LENGTH_STRING);
2116    }
2117
2118    /**
2119     * Updates the label next to the scheme that says "extends" if the scheme is
2120     * a child of another scheme. If the scheme is not a child of another
2121     * scheme, then the label is simply blank.
2122     */

2123    private final void updateLabelSchemeExtends() {
2124        final String JavaDoc schemeId = getSchemeId();
2125
2126        if (schemeId != null) {
2127            final Scheme scheme = bindingService.getScheme(schemeId);
2128            try {
2129                final String JavaDoc name = (String JavaDoc) schemeUniqueNamesById.get(scheme
2130                        .getParentId());
2131                if (name != null) {
2132                    labelSchemeExtends.setText(MessageFormat.format(Util
2133                            .translateString(RESOURCE_BUNDLE, "extends"), //$NON-NLS-1$
2134
new Object JavaDoc[] { name }));
2135                    return;
2136                }
2137            } catch (final NotDefinedException e) {
2138                // Do nothing
2139
}
2140        }
2141
2142        labelSchemeExtends.setText(Util.ZERO_LENGTH_STRING);
2143    }
2144
2145    /**
2146     * Tries to select the correct entry in table based on the currently
2147     * selected context and trigger sequence. If the table hasn't really
2148     * changed, then this method is essentially trying to restore the selection.
2149     * If it has changed, then it is trying to select the most entry based on
2150     * the context.
2151     *
2152     * @param table
2153     * The table to be changed; must not be <code>null</code>.
2154     * @param contextId
2155     * The currently selected context; should not be
2156     * <code>null</code>.
2157     * @param triggerSequence
2158     * The current trigger sequence; should not be <code>null</code>.
2159     */

2160    private final void updateSelection(final Table table,
2161            final String JavaDoc contextId, final TriggerSequence triggerSequence) {
2162        if (table.getSelectionCount() > 1) {
2163            table.deselectAll();
2164        }
2165
2166        final TableItem[] items = table.getItems();
2167        int selection = -1;
2168        for (int i = 0; i < items.length; i++) {
2169            final Binding binding = (Binding) items[i].getData(ITEM_DATA_KEY);
2170            if ((Util.equals(contextId, binding.getContextId()))
2171                    && (Util.equals(triggerSequence, binding
2172                            .getTriggerSequence()))) {
2173                selection = i;
2174                break;
2175            }
2176        }
2177
2178        if (selection != -1) {
2179            table.select(selection);
2180        }
2181    }
2182
2183    /**
2184     * Updates the contents of the table showing the bindings for the currently
2185     * selected command. The selection is destroyed by this process.
2186     *
2187     * @param parameterizedCommand
2188     * The currently selected fully-parameterized command; may be
2189     * <code>null</code>.
2190     */

2191    private final void updateTableBindingsForCommand(
2192            final ParameterizedCommand parameterizedCommand) {
2193        // Clear the table of existing items.
2194
tableBindingsForCommand.removeAll();
2195
2196        // Add each of the bindings, if the command identifier matches.
2197
final Collection JavaDoc bindings = localChangeManager
2198                .getActiveBindingsDisregardingContextFlat();
2199        final Iterator JavaDoc bindingItr = bindings.iterator();
2200        while (bindingItr.hasNext()) {
2201            final Binding binding = (Binding) bindingItr.next();
2202            if (!Util.equals(parameterizedCommand, binding
2203                    .getParameterizedCommand())) {
2204                continue; // binding does not match
2205
}
2206
2207            final TableItem tableItem = new TableItem(tableBindingsForCommand,
2208                    SWT.NULL);
2209            tableItem.setData(ITEM_DATA_KEY, binding);
2210
2211            /*
2212             * Set the associated image based on the type of binding. Either it
2213             * is a user binding or a system binding.
2214             *
2215             * TODO Identify more image types.
2216             */

2217            if (binding.getType() == Binding.SYSTEM) {
2218                tableItem.setImage(0, IMAGE_BLANK);
2219            } else {
2220                tableItem.setImage(0, IMAGE_CHANGE);
2221            }
2222
2223            String JavaDoc contextName = (String JavaDoc) contextUniqueNamesById.get(binding
2224                    .getContextId());
2225            if (contextName == null) {
2226                contextName = Util.ZERO_LENGTH_STRING;
2227            }
2228            tableItem.setText(1, contextName);
2229            tableItem.setText(2, binding.getTriggerSequence().format());
2230        }
2231    }
2232
2233    /**
2234     * Updates the contents of the table showing the bindings for the current
2235     * trigger sequence. The selection is destroyed by this process.
2236     *
2237     * @param triggerSequence
2238     * The current trigger sequence; may be <code>null</code> or
2239     * empty.
2240     */

2241    private final void updateTableBindingsForTriggerSequence(
2242            final TriggerSequence triggerSequence) {
2243        // Clear the table of its existing items.
2244
tableBindingsForTriggerSequence.removeAll();
2245
2246        // Get the collection of bindings for the current command.
2247
final Map JavaDoc activeBindings = localChangeManager
2248                .getActiveBindingsDisregardingContext();
2249        final Collection JavaDoc bindings = (Collection JavaDoc) activeBindings
2250                .get(triggerSequence);
2251        if (bindings == null) {
2252            return;
2253        }
2254
2255        // Add each of the bindings.
2256
final Iterator JavaDoc bindingItr = bindings.iterator();
2257        while (bindingItr.hasNext()) {
2258            final Binding binding = (Binding) bindingItr.next();
2259            final Context context = contextService.getContext(binding
2260                    .getContextId());
2261            final ParameterizedCommand parameterizedCommand = binding
2262                    .getParameterizedCommand();
2263            final Command command = parameterizedCommand.getCommand();
2264            if ((!context.isDefined()) && (!command.isDefined())) {
2265                continue;
2266            }
2267
2268            final TableItem tableItem = new TableItem(
2269                    tableBindingsForTriggerSequence, SWT.NULL);
2270            tableItem.setData(ITEM_DATA_KEY, binding);
2271
2272            /*
2273             * Set the associated image based on the type of binding. Either it
2274             * is a user binding or a system binding.
2275             *
2276             * TODO Identify more image types.
2277             */

2278            if (binding.getType() == Binding.SYSTEM) {
2279                tableItem.setImage(0, IMAGE_BLANK);
2280            } else {
2281                tableItem.setImage(0, IMAGE_CHANGE);
2282            }
2283
2284            try {
2285                tableItem.setText(1, context.getName());
2286                tableItem.setText(2, parameterizedCommand.getName());
2287            } catch (final NotDefinedException e) {
2288                throw new Error JavaDoc(
2289                        "Context or command became undefined on a non-UI thread while the UI thread was processing."); //$NON-NLS-1$
2290
}
2291        }
2292    }
2293
2294    /**
2295     * Updates the contents of the view tab. This queries the command manager
2296     * for a list of key sequence binding definitions, and these definitions are
2297     * then added to the table.
2298     *
2299     * @since 3.1
2300     */

2301    private final void updateViewTab() {
2302        // Clear out the existing table contents.
2303
tableBindings.removeAll();
2304
2305        // Get a sorted list of key binding contents.
2306
final List JavaDoc bindings = new ArrayList JavaDoc(localChangeManager
2307                .getActiveBindingsDisregardingContextFlat());
2308        Collections.sort(bindings, new Comparator JavaDoc() {
2309            /**
2310             * Compares two instances of <code>Binding</code> based on the
2311             * current sort order.
2312             *
2313             * @param object1
2314             * The first object to compare; must be an instance of
2315             * <code>Binding</code> (i.e., not <code>null</code>).
2316             * @param object2
2317             * The second object to compare; must be an instance of
2318             * <code>Binding</code> (i.e., not <code>null</code>).
2319             * @return The integer value representing the comparison. The
2320             * comparison is based on the current sort order.
2321             * @since 3.1
2322             */

2323            public final int compare(final Object JavaDoc object1, final Object JavaDoc object2) {
2324                final Binding binding1 = (Binding) object1;
2325                final Binding binding2 = (Binding) object2;
2326
2327                /*
2328                 * Get the category name, command name, formatted key sequence
2329                 * and context name for the first binding.
2330                 */

2331                final Command command1 = binding1.getParameterizedCommand()
2332                        .getCommand();
2333                String JavaDoc categoryName1 = Util.ZERO_LENGTH_STRING;
2334                String JavaDoc commandName1 = Util.ZERO_LENGTH_STRING;
2335                try {
2336                    commandName1 = command1.getName();
2337                    categoryName1 = command1.getCategory().getName();
2338                } catch (final NotDefinedException e) {
2339                    // Just use the zero-length string.
2340
}
2341                final String JavaDoc triggerSequence1 = binding1.getTriggerSequence()
2342                        .format();
2343                final String JavaDoc contextId1 = binding1.getContextId();
2344                String JavaDoc contextName1 = Util.ZERO_LENGTH_STRING;
2345                if (contextId1 != null) {
2346                    final Context context = contextService
2347                            .getContext(contextId1);
2348                    try {
2349                        contextName1 = context.getName();
2350                    } catch (final org.eclipse.core.commands.common.NotDefinedException e) {
2351                        // Just use the zero-length string.
2352
}
2353                }
2354
2355                /*
2356                 * Get the category name, command name, formatted key sequence
2357                 * and context name for the first binding.
2358                 */

2359                final Command command2 = binding2.getParameterizedCommand()
2360                        .getCommand();
2361                String JavaDoc categoryName2 = Util.ZERO_LENGTH_STRING;
2362                String JavaDoc commandName2 = Util.ZERO_LENGTH_STRING;
2363                try {
2364                    commandName2 = command2.getName();
2365                    categoryName2 = command2.getCategory().getName();
2366                } catch (final org.eclipse.core.commands.common.NotDefinedException e) {
2367                    // Just use the zero-length string.
2368
}
2369                final String JavaDoc keySequence2 = binding2.getTriggerSequence()
2370                        .format();
2371                final String JavaDoc contextId2 = binding2.getContextId();
2372                String JavaDoc contextName2 = Util.ZERO_LENGTH_STRING;
2373                if (contextId2 != null) {
2374                    final Context context = contextService
2375                            .getContext(contextId2);
2376                    try {
2377                        contextName2 = context.getName();
2378                    } catch (final org.eclipse.core.commands.common.NotDefinedException e) {
2379                        // Just use the zero-length string.
2380
}
2381                }
2382
2383                // Compare the items in the current sort order.
2384
int compare = 0;
2385                for (int i = 0; i < sortOrder.length; i++) {
2386                    switch (sortOrder[i]) {
2387                    case VIEW_CATEGORY_COLUMN_INDEX:
2388                        compare = Util.compare(categoryName1, categoryName2);
2389                        if (compare != 0) {
2390                            return compare;
2391                        }
2392                        break;
2393                    case VIEW_COMMAND_COLUMN_INDEX:
2394                        compare = Util.compare(commandName1, commandName2);
2395                        if (compare != 0) {
2396                            return compare;
2397                        }
2398                        break;
2399                    case VIEW_KEY_SEQUENCE_COLUMN_INDEX:
2400                        compare = Util.compare(triggerSequence1, keySequence2);
2401                        if (compare != 0) {
2402                            return compare;
2403                        }
2404                        break;
2405                    case VIEW_CONTEXT_COLUMN_INDEX:
2406                        compare = Util.compare(contextName1, contextName2);
2407                        if (compare != 0) {
2408                            return compare;
2409                        }
2410                        break;
2411                    default:
2412                        throw new Error JavaDoc(
2413                                "Programmer error: added another sort column without modifying the comparator."); //$NON-NLS-1$
2414
}
2415                }
2416
2417                return compare;
2418            }
2419
2420            /**
2421             * @see Object#equals(java.lang.Object)
2422             */

2423            public final boolean equals(final Object JavaDoc object) {
2424                return super.equals(object);
2425            }
2426        });
2427
2428        // Add a table item for each item in the list.
2429
final Iterator JavaDoc keyBindingItr = bindings.iterator();
2430        while (keyBindingItr.hasNext()) {
2431            final Binding binding = (Binding) keyBindingItr.next();
2432
2433            // Get the command and category name.
2434
final ParameterizedCommand command = binding
2435                    .getParameterizedCommand();
2436            String JavaDoc commandName = Util.ZERO_LENGTH_STRING;
2437            String JavaDoc categoryName = Util.ZERO_LENGTH_STRING;
2438            try {
2439                commandName = command.getName();
2440                categoryName = command.getCommand().getCategory().getName();
2441            } catch (final org.eclipse.core.commands.common.NotDefinedException e) {
2442                // Just use the zero-length string.
2443
}
2444
2445            // Ignore items with a meaningless command name.
2446
if ((commandName == null) || (commandName.length() == 0)) {
2447                continue;
2448            }
2449
2450            // Get the context name.
2451
final String JavaDoc contextId = binding.getContextId();
2452            String JavaDoc contextName = Util.ZERO_LENGTH_STRING;
2453            if (contextId != null) {
2454                final Context context = contextService.getContext(contextId);
2455                try {
2456                    contextName = context.getName();
2457                } catch (final org.eclipse.core.commands.common.NotDefinedException e) {
2458                    // Just use the zero-length string.
2459
}
2460            }
2461
2462            // Create the table item.
2463
final TableItem item = new TableItem(tableBindings, SWT.NONE);
2464            item.setText(VIEW_CATEGORY_COLUMN_INDEX, categoryName);
2465            item.setText(VIEW_COMMAND_COLUMN_INDEX, commandName);
2466            item.setText(VIEW_KEY_SEQUENCE_COLUMN_INDEX, binding
2467                    .getTriggerSequence().format());
2468            item.setText(VIEW_CONTEXT_COLUMN_INDEX, contextName);
2469            item.setData(BINDING_KEY, binding);
2470        }
2471
2472        // Pack the columns.
2473
for (int i = 0; i < tableBindings.getColumnCount(); i++) {
2474            tableBindings.getColumn(i).pack();
2475        }
2476    }
2477    
2478    
2479}
2480
Popular Tags