1 /*******************************************************************************
\r
2 * Copyright (c) 2000, 2010 IBM Corporation and others.
\r
3 * All rights reserved. This program and the accompanying materials
\r
4 * are made available under the terms of the Eclipse Public License v1.0
\r
5 * which accompanies this distribution, and is available at
\r
6 * http://www.eclipse.org/legal/epl-v10.html
\r
9 * IBM Corporation - initial API and implementation
\r
10 * Willian Mitsuda <wmitsuda@gmail.com>
\r
11 * - Fix for bug 196553 - [Dialogs] Support IColorProvider/IFontProvider in FilteredItemsSelectionDialog
\r
12 * Peter Friese <peter.friese@gentleware.com>
\r
13 * - Fix for bug 208602 - [Dialogs] Open Type dialog needs accessible labels
\r
14 * Simon Muschel <smuschel@gmx.de> - bug 258493
\r
15 *******************************************************************************/
\r
16 package org.simantics.utils.ui.dialogs;
\r
18 import java.io.IOException;
\r
19 import java.io.StringReader;
\r
20 import java.io.StringWriter;
\r
21 import java.util.ArrayList;
\r
22 import java.util.Arrays;
\r
23 import java.util.Collections;
\r
24 import java.util.Comparator;
\r
25 import java.util.HashMap;
\r
26 import java.util.HashSet;
\r
27 import java.util.Iterator;
\r
28 import java.util.LinkedHashSet;
\r
29 import java.util.List;
\r
30 import java.util.Set;
\r
32 import org.eclipse.core.commands.AbstractHandler;
\r
33 import org.eclipse.core.commands.ExecutionEvent;
\r
34 import org.eclipse.core.commands.IHandler;
\r
35 import org.eclipse.core.runtime.Assert;
\r
36 import org.eclipse.core.runtime.CoreException;
\r
37 import org.eclipse.core.runtime.IProgressMonitor;
\r
38 import org.eclipse.core.runtime.IStatus;
\r
39 import org.eclipse.core.runtime.ListenerList;
\r
40 import org.eclipse.core.runtime.NullProgressMonitor;
\r
41 import org.eclipse.core.runtime.ProgressMonitorWrapper;
\r
42 import org.eclipse.core.runtime.Status;
\r
43 import org.eclipse.core.runtime.SubProgressMonitor;
\r
44 import org.eclipse.core.runtime.jobs.Job;
\r
45 import org.eclipse.jface.action.Action;
\r
46 import org.eclipse.jface.action.ActionContributionItem;
\r
47 import org.eclipse.jface.action.IAction;
\r
48 import org.eclipse.jface.action.IMenuListener;
\r
49 import org.eclipse.jface.action.IMenuManager;
\r
50 import org.eclipse.jface.action.LegacyActionTools;
\r
51 import org.eclipse.jface.action.MenuManager;
\r
52 import org.eclipse.jface.dialogs.IDialogSettings;
\r
53 import org.eclipse.jface.viewers.ColumnLabelProvider;
\r
54 import org.eclipse.jface.viewers.ContentViewer;
\r
55 import org.eclipse.jface.viewers.DelegatingStyledCellLabelProvider.IStyledLabelProvider;
\r
56 import org.eclipse.jface.viewers.DoubleClickEvent;
\r
57 import org.eclipse.jface.viewers.IColorProvider;
\r
58 import org.eclipse.jface.viewers.IContentProvider;
\r
59 import org.eclipse.jface.viewers.IDoubleClickListener;
\r
60 import org.eclipse.jface.viewers.IFontProvider;
\r
61 import org.eclipse.jface.viewers.ILabelDecorator;
\r
62 import org.eclipse.jface.viewers.ILabelProvider;
\r
63 import org.eclipse.jface.viewers.ILabelProviderListener;
\r
64 import org.eclipse.jface.viewers.ILazyContentProvider;
\r
65 import org.eclipse.jface.viewers.ISelection;
\r
66 import org.eclipse.jface.viewers.ISelectionChangedListener;
\r
67 import org.eclipse.jface.viewers.IStructuredContentProvider;
\r
68 import org.eclipse.jface.viewers.LabelProvider;
\r
69 import org.eclipse.jface.viewers.LabelProviderChangedEvent;
\r
70 import org.eclipse.jface.viewers.SelectionChangedEvent;
\r
71 import org.eclipse.jface.viewers.StructuredSelection;
\r
72 import org.eclipse.jface.viewers.StyledCellLabelProvider;
\r
73 import org.eclipse.jface.viewers.StyledString;
\r
74 import org.eclipse.jface.viewers.TableViewer;
\r
75 import org.eclipse.jface.viewers.TableViewerColumn;
\r
76 import org.eclipse.jface.viewers.Viewer;
\r
77 import org.eclipse.jface.viewers.ViewerCell;
\r
78 import org.eclipse.jface.viewers.ViewerFilter;
\r
79 import org.eclipse.osgi.util.NLS;
\r
80 import org.eclipse.swt.SWT;
\r
81 import org.eclipse.swt.accessibility.ACC;
\r
82 import org.eclipse.swt.accessibility.AccessibleAdapter;
\r
83 import org.eclipse.swt.accessibility.AccessibleEvent;
\r
84 import org.eclipse.swt.custom.CLabel;
\r
85 import org.eclipse.swt.custom.ViewForm;
\r
86 import org.eclipse.swt.events.KeyAdapter;
\r
87 import org.eclipse.swt.events.KeyEvent;
\r
88 import org.eclipse.swt.events.ModifyEvent;
\r
89 import org.eclipse.swt.events.ModifyListener;
\r
90 import org.eclipse.swt.events.MouseAdapter;
\r
91 import org.eclipse.swt.events.MouseEvent;
\r
92 import org.eclipse.swt.events.SelectionAdapter;
\r
93 import org.eclipse.swt.events.SelectionEvent;
\r
94 import org.eclipse.swt.events.TraverseEvent;
\r
95 import org.eclipse.swt.events.TraverseListener;
\r
96 import org.eclipse.swt.graphics.Color;
\r
97 import org.eclipse.swt.graphics.Font;
\r
98 import org.eclipse.swt.graphics.GC;
\r
99 import org.eclipse.swt.graphics.Image;
\r
100 import org.eclipse.swt.graphics.Point;
\r
101 import org.eclipse.swt.graphics.Rectangle;
\r
102 import org.eclipse.swt.layout.GridData;
\r
103 import org.eclipse.swt.layout.GridLayout;
\r
104 import org.eclipse.swt.widgets.Composite;
\r
105 import org.eclipse.swt.widgets.Control;
\r
106 import org.eclipse.swt.widgets.Display;
\r
107 import org.eclipse.swt.widgets.Event;
\r
108 import org.eclipse.swt.widgets.Label;
\r
109 import org.eclipse.swt.widgets.Menu;
\r
110 import org.eclipse.swt.widgets.Shell;
\r
111 import org.eclipse.swt.widgets.Table;
\r
112 import org.eclipse.swt.widgets.TableColumn;
\r
113 import org.eclipse.swt.widgets.Text;
\r
114 import org.eclipse.swt.widgets.ToolBar;
\r
115 import org.eclipse.swt.widgets.ToolItem;
\r
116 import org.eclipse.ui.ActiveShellExpression;
\r
117 import org.eclipse.ui.IMemento;
\r
118 import org.eclipse.ui.IWorkbenchCommandConstants;
\r
119 import org.eclipse.ui.IWorkbenchPreferenceConstants;
\r
120 import org.eclipse.ui.PlatformUI;
\r
121 import org.eclipse.ui.WorkbenchException;
\r
122 import org.eclipse.ui.XMLMemento;
\r
123 import org.eclipse.ui.dialogs.SearchPattern;
\r
124 import org.eclipse.ui.dialogs.SelectionStatusDialog;
\r
125 import org.eclipse.ui.handlers.IHandlerActivation;
\r
126 import org.eclipse.ui.handlers.IHandlerService;
\r
127 import org.eclipse.ui.internal.IWorkbenchGraphicConstants;
\r
128 import org.eclipse.ui.internal.WorkbenchImages;
\r
129 import org.eclipse.ui.internal.WorkbenchMessages;
\r
130 import org.eclipse.ui.internal.WorkbenchPlugin;
\r
131 import org.eclipse.ui.progress.UIJob;
\r
132 import org.eclipse.ui.statushandlers.StatusManager;
\r
135 * Shows a list of items to the user with a text entry field for a string
\r
136 * pattern used to filter the list of items.
\r
138 * This is a copy from org.eclipse.ui.dialogs.FilteredItemsSelectionDialog
\r
139 * with a hook for column creation and a fix to a (possible) bug that
\r
140 * prevented the empty pattern to be handled correctly. This version of the
\r
141 * dialog also has the possibility to force an update of the contents if new
\r
142 * elements are added.
\r
144 * TODO: Clean up warnings.
\r
146 * @author Janne Kauttio (modifications only)
\r
150 @SuppressWarnings({ "restriction", "rawtypes", "unchecked" })
\r
151 public abstract class ColumnFilteredItemsSelectionDialog extends
\r
152 SelectionStatusDialog {
\r
154 private static final String DIALOG_BOUNDS_SETTINGS = "DialogBoundsSettings"; //$NON-NLS-1$
\r
156 private static final String SHOW_STATUS_LINE = "ShowStatusLine"; //$NON-NLS-1$
\r
158 private static final String HISTORY_SETTINGS = "History"; //$NON-NLS-1$
\r
160 private static final String DIALOG_HEIGHT = "DIALOG_HEIGHT"; //$NON-NLS-1$
\r
162 private static final String DIALOG_WIDTH = "DIALOG_WIDTH"; //$NON-NLS-1$
\r
165 * Represents an empty selection in the pattern input field (used only for
\r
166 * initial pattern).
\r
168 public static final int NONE = 0;
\r
171 * Pattern input field selection where caret is at the beginning (used only
\r
172 * for initial pattern).
\r
174 public static final int CARET_BEGINNING = 1;
\r
177 * Represents a full selection in the pattern input field (used only for
\r
178 * initial pattern).
\r
180 public static final int FULL_SELECTION = 2;
\r
182 private Text pattern;
\r
184 private TableViewer viewer;
\r
186 private DetailsContentViewer details;
\r
189 * It is a duplicate of a field in the CLabel class in DetailsContentViewer.
\r
190 * It is maintained, because the <code>setDetailsLabelProvider()</code>
\r
191 * could be called before content area is created.
\r
193 private ILabelProvider detailsLabelProvider;
\r
195 private ItemsListLabelProvider itemsListLabelProvider;
\r
197 private MenuManager menuManager;
\r
199 private MenuManager contextMenuManager;
\r
201 private boolean multi;
\r
203 private ToolBar toolBar;
\r
205 private ToolItem toolItem;
\r
207 private Label progressLabel;
\r
209 private ToggleStatusLineAction toggleStatusLineAction;
\r
211 private RemoveHistoryItemAction removeHistoryItemAction;
\r
213 private ActionContributionItem removeHistoryActionContributionItem;
\r
215 private IStatus status;
\r
217 private RefreshCacheJob refreshCacheJob;
\r
219 private RefreshProgressMessageJob refreshProgressMessageJob = new RefreshProgressMessageJob();
\r
221 private Object[] currentSelection;
\r
223 private ContentProvider contentProvider;
\r
225 private FilterHistoryJob filterHistoryJob;
\r
227 private FilterJob filterJob;
\r
229 private ItemsFilter filter;
\r
231 private List lastCompletedResult;
\r
233 private ItemsFilter lastCompletedFilter;
\r
235 private String initialPatternText;
\r
237 private int selectionMode;
\r
239 private ItemsListSeparator itemsListSeparator;
\r
241 private static final String EMPTY_STRING = ""; //$NON-NLS-1$
\r
243 private boolean refreshWithLastSelection = false;
\r
245 private IHandlerActivation showViewHandler;
\r
248 * Creates a new instance of the class.
\r
251 * shell to parent the dialog on
\r
253 * indicates whether dialog allows to select more than one
\r
254 * position in its list of items
\r
256 public ColumnFilteredItemsSelectionDialog(Shell shell, boolean multi) {
\r
258 this.multi = multi;
\r
259 filterHistoryJob = new FilterHistoryJob();
\r
260 filterJob = new FilterJob();
\r
261 contentProvider = new ContentProvider();
\r
262 refreshCacheJob = new RefreshCacheJob();
\r
263 itemsListSeparator = new ItemsListSeparator(WorkbenchMessages.FilteredItemsSelectionDialog_separatorLabel);
\r
264 selectionMode = NONE;
\r
268 * Creates a new instance of the class. Created dialog won't allow to select
\r
269 * more than one item.
\r
272 * shell to parent the dialog on
\r
274 public ColumnFilteredItemsSelectionDialog(Shell shell) {
\r
275 this(shell, false);
\r
279 * Adds viewer filter to the dialog items list.
\r
284 protected void addListFilter(ViewerFilter filter) {
\r
285 contentProvider.addFilter(filter);
\r
289 * Sets a new label provider for items in the list. If the label provider
\r
290 * also implements {@link
\r
291 * org.eclipse.jface.viewers.DelegatingStyledCellLabelProvider
\r
292 * .IStyledLabelProvider}, the style text labels provided by it will be used
\r
293 * provided that the corresponding preference is set.
\r
295 * @see IWorkbenchPreferenceConstants#USE_COLORED_LABELS
\r
297 * @param listLabelProvider
\r
298 * the label provider for items in the list
\r
300 public void setListLabelProvider(ILabelProvider listLabelProvider) {
\r
301 getItemsListLabelProvider().setProvider(listLabelProvider);
\r
305 * Returns the label decorator for selected items in the list.
\r
307 * @return the label decorator for selected items in the list
\r
309 private ILabelDecorator getListSelectionLabelDecorator() {
\r
310 return getItemsListLabelProvider().getSelectionDecorator();
\r
314 * Sets the label decorator for selected items in the list.
\r
316 * @param listSelectionLabelDecorator
\r
317 * the label decorator for selected items in the list
\r
319 public void setListSelectionLabelDecorator(
\r
320 ILabelDecorator listSelectionLabelDecorator) {
\r
321 getItemsListLabelProvider().setSelectionDecorator(
\r
322 listSelectionLabelDecorator);
\r
326 * Returns the item list label provider.
\r
328 * @return the item list label provider
\r
330 private ItemsListLabelProvider getItemsListLabelProvider() {
\r
331 if (itemsListLabelProvider == null) {
\r
332 itemsListLabelProvider = new ItemsListLabelProvider(
\r
333 new LabelProvider(), null);
\r
335 return itemsListLabelProvider;
\r
339 * Sets label provider for the details field.
\r
341 * For a single selection, the element sent to
\r
342 * {@link ILabelProvider#getImage(Object)} and
\r
343 * {@link ILabelProvider#getText(Object)} is the selected object, for
\r
344 * multiple selection a {@link String} with amount of selected items is the
\r
347 * @see #getSelectedItems() getSelectedItems() can be used to retrieve
\r
348 * selected items and get the items count.
\r
350 * @param detailsLabelProvider
\r
351 * the label provider for the details field
\r
353 public void setDetailsLabelProvider(ILabelProvider detailsLabelProvider) {
\r
354 this.detailsLabelProvider = detailsLabelProvider;
\r
355 if (details != null) {
\r
356 details.setLabelProvider(detailsLabelProvider);
\r
360 private ILabelProvider getDetailsLabelProvider() {
\r
361 if (detailsLabelProvider == null) {
\r
362 detailsLabelProvider = new LabelProvider();
\r
364 return detailsLabelProvider;
\r
370 * @see org.eclipse.jface.window.Window#create()
\r
372 public void create() {
\r
374 pattern.setFocus();
\r
378 * Restores dialog using persisted settings. The default implementation
\r
379 * restores the status of the details line and the selection history.
\r
382 * settings used to restore dialog
\r
384 protected void restoreDialog(IDialogSettings settings) {
\r
385 boolean toggleStatusLine = true;
\r
387 if (settings.get(SHOW_STATUS_LINE) != null) {
\r
388 toggleStatusLine = settings.getBoolean(SHOW_STATUS_LINE);
\r
391 toggleStatusLineAction.setChecked(toggleStatusLine);
\r
393 details.setVisible(toggleStatusLine);
\r
395 String setting = settings.get(HISTORY_SETTINGS);
\r
396 if (setting != null) {
\r
398 IMemento memento = XMLMemento.createReadRoot(new StringReader(setting));
\r
399 this.contentProvider.loadHistory(memento);
\r
400 } catch (WorkbenchException e) {
\r
401 // Simply don't restore the settings
\r
402 StatusManager.getManager().handle(new Status(
\r
404 PlatformUI.PLUGIN_ID,
\r
406 WorkbenchMessages.FilteredItemsSelectionDialog_restoreError,
\r
415 * @see org.eclipse.jface.window.Window#close()
\r
417 public boolean close() {
\r
418 this.filterJob.cancel();
\r
419 this.refreshCacheJob.cancel();
\r
420 this.refreshProgressMessageJob.cancel();
\r
421 if (showViewHandler != null) {
\r
422 IHandlerService service = (IHandlerService) PlatformUI.getWorkbench().getService(IHandlerService.class);
\r
423 service.deactivateHandler(showViewHandler);
\r
424 showViewHandler.getHandler().dispose();
\r
425 showViewHandler = null;
\r
427 if (menuManager != null)
\r
428 menuManager.dispose();
\r
429 if (contextMenuManager != null)
\r
430 contextMenuManager.dispose();
\r
431 storeDialog(getDialogSettings());
\r
432 return super.close();
\r
436 * Stores dialog settings.
\r
439 * settings used to store dialog
\r
441 protected void storeDialog(IDialogSettings settings) {
\r
442 settings.put(SHOW_STATUS_LINE, toggleStatusLineAction.isChecked());
\r
444 XMLMemento memento = XMLMemento.createWriteRoot(HISTORY_SETTINGS);
\r
445 this.contentProvider.saveHistory(memento);
\r
446 StringWriter writer = new StringWriter();
\r
448 memento.save(writer);
\r
449 settings.put(HISTORY_SETTINGS, writer.getBuffer().toString());
\r
450 } catch (IOException e) {
\r
451 // Simply don't store the settings
\r
452 StatusManager.getManager().handle(new Status(
\r
454 PlatformUI.PLUGIN_ID,
\r
456 WorkbenchMessages.FilteredItemsSelectionDialog_storeError,
\r
462 * Create a new header which is labelled by headerLabel.
\r
465 * @return Label the label of the header
\r
467 private Label createHeader(Composite parent) {
\r
468 Composite header = new Composite(parent, SWT.NONE);
\r
470 GridLayout layout = new GridLayout();
\r
471 layout.numColumns = 2;
\r
472 layout.marginWidth = 0;
\r
473 layout.marginHeight = 0;
\r
474 header.setLayout(layout);
\r
476 Label headerLabel = new Label(header, SWT.NONE);
\r
477 headerLabel.setText((getMessage() != null && getMessage().trim().length() > 0) ? getMessage()
\r
478 : WorkbenchMessages.FilteredItemsSelectionDialog_patternLabel);
\r
479 headerLabel.addTraverseListener(new TraverseListener() {
\r
480 public void keyTraversed(TraverseEvent e) {
\r
481 if (e.detail == SWT.TRAVERSE_MNEMONIC && e.doit) {
\r
482 e.detail = SWT.TRAVERSE_NONE;
\r
483 pattern.setFocus();
\r
488 GridData gd = new GridData(GridData.FILL_HORIZONTAL);
\r
489 headerLabel.setLayoutData(gd);
\r
491 createViewMenu(header);
\r
492 header.setLayoutData(gd);
\r
493 return headerLabel;
\r
497 * Create the labels for the list and the progress. Return the list label.
\r
502 private Label createLabels(Composite parent) {
\r
503 Composite labels = new Composite(parent, SWT.NONE);
\r
505 GridLayout layout = new GridLayout();
\r
506 layout.numColumns = 2;
\r
507 layout.marginWidth = 0;
\r
508 layout.marginHeight = 0;
\r
509 labels.setLayout(layout);
\r
511 Label listLabel = new Label(labels, SWT.NONE);
\r
512 listLabel.setText(WorkbenchMessages.FilteredItemsSelectionDialog_listLabel);
\r
514 listLabel.addTraverseListener(new TraverseListener() {
\r
515 public void keyTraversed(TraverseEvent e) {
\r
516 if (e.detail == SWT.TRAVERSE_MNEMONIC && e.doit) {
\r
517 e.detail = SWT.TRAVERSE_NONE;
\r
518 viewer.getTable().setFocus();
\r
523 GridData gd = new GridData(GridData.FILL_HORIZONTAL);
\r
524 listLabel.setLayoutData(gd);
\r
526 progressLabel = new Label(labels, SWT.RIGHT);
\r
527 progressLabel.setLayoutData(gd);
\r
529 labels.setLayoutData(gd);
\r
533 private void createViewMenu(Composite parent) {
\r
534 toolBar = new ToolBar(parent, SWT.FLAT);
\r
535 toolItem = new ToolItem(toolBar, SWT.PUSH, 0);
\r
537 GridData data = new GridData();
\r
538 data.horizontalAlignment = GridData.END;
\r
539 toolBar.setLayoutData(data);
\r
541 toolBar.addMouseListener(new MouseAdapter() {
\r
542 public void mouseDown(MouseEvent e) {
\r
547 toolItem.setImage(WorkbenchImages.getImage(IWorkbenchGraphicConstants.IMG_LCL_VIEW_MENU));
\r
548 toolItem.setToolTipText(WorkbenchMessages.FilteredItemsSelectionDialog_menu);
\r
549 toolItem.addSelectionListener(new SelectionAdapter() {
\r
550 public void widgetSelected(SelectionEvent e) {
\r
555 menuManager = new MenuManager();
\r
557 fillViewMenu(menuManager);
\r
559 IHandlerService service = (IHandlerService) PlatformUI.getWorkbench().getService(IHandlerService.class);
\r
560 IHandler handler = new AbstractHandler() {
\r
561 public Object execute(ExecutionEvent event) {
\r
566 showViewHandler = service.activateHandler(
\r
567 IWorkbenchCommandConstants.WINDOW_SHOW_VIEW_MENU, handler,
\r
568 new ActiveShellExpression(getShell()));
\r
572 * Fills the menu of the dialog.
\r
574 * @param menuManager
\r
577 protected void fillViewMenu(IMenuManager menuManager) {
\r
578 toggleStatusLineAction = new ToggleStatusLineAction();
\r
579 menuManager.add(toggleStatusLineAction);
\r
582 private void showViewMenu() {
\r
583 Menu menu = menuManager.createContextMenu(getShell());
\r
584 Rectangle bounds = toolItem.getBounds();
\r
585 Point topLeft = new Point(bounds.x, bounds.y + bounds.height);
\r
586 topLeft = toolBar.toDisplay(topLeft);
\r
587 menu.setLocation(topLeft.x, topLeft.y);
\r
588 menu.setVisible(true);
\r
592 * Hook that allows to add actions to the context menu.
\r
594 * Subclasses may extend in order to add other actions.</p>
\r
596 * @param menuManager the context menu manager
\r
599 protected void fillContextMenu(IMenuManager menuManager) {
\r
600 List selectedElements= ((StructuredSelection)viewer.getSelection()).toList();
\r
604 for (Iterator it= selectedElements.iterator(); it.hasNext();) {
\r
606 if (item instanceof ItemsListSeparator || !isHistoryElement(item)) {
\r
611 if (selectedElements.size() > 0) {
\r
612 removeHistoryItemAction.setText(WorkbenchMessages.FilteredItemsSelectionDialog_removeItemsFromHistoryAction);
\r
614 menuManager.add(removeHistoryActionContributionItem);
\r
619 private void createPopupMenu() {
\r
620 removeHistoryItemAction = new RemoveHistoryItemAction();
\r
621 removeHistoryActionContributionItem = new ActionContributionItem(removeHistoryItemAction);
\r
623 contextMenuManager = new MenuManager();
\r
624 contextMenuManager.setRemoveAllWhenShown(true);
\r
625 contextMenuManager.addMenuListener(new IMenuListener() {
\r
626 public void menuAboutToShow(IMenuManager manager) {
\r
627 fillContextMenu(manager);
\r
631 final Table table = viewer.getTable();
\r
632 Menu menu= contextMenuManager.createContextMenu(table);
\r
633 table.setMenu(menu);
\r
637 * Creates an extra content area, which will be located above the details.
\r
640 * parent to create the dialog widgets in
\r
641 * @return an extra content area
\r
643 protected abstract Control createExtendedContentArea(Composite parent);
\r
648 * @see org.eclipse.jface.dialogs.Dialog#createDialogArea(org.eclipse.swt.widgets.Composite)
\r
650 protected Control createDialogArea(Composite parent) {
\r
651 Composite dialogArea = (Composite) super.createDialogArea(parent);
\r
653 Composite content = new Composite(dialogArea, SWT.NONE);
\r
654 GridData gd = new GridData(GridData.FILL_BOTH);
\r
655 content.setLayoutData(gd);
\r
657 GridLayout layout = new GridLayout();
\r
658 layout.numColumns = 1;
\r
659 layout.marginWidth = 0;
\r
660 layout.marginHeight = 0;
\r
661 content.setLayout(layout);
\r
663 final Label headerLabel = createHeader(content);
\r
665 pattern = new Text(content, SWT.SINGLE | SWT.BORDER | SWT.SEARCH | SWT.ICON_CANCEL);
\r
666 pattern.getAccessible().addAccessibleListener(new AccessibleAdapter() {
\r
667 public void getName(AccessibleEvent e) {
\r
668 e.result = LegacyActionTools.removeMnemonics(headerLabel.getText());
\r
671 gd = new GridData(GridData.FILL_HORIZONTAL);
\r
672 pattern.setLayoutData(gd);
\r
674 final Label listLabel = createLabels(content);
\r
676 viewer = new TableViewer(content, (multi ? SWT.MULTI : SWT.SINGLE)
\r
677 | SWT.BORDER | SWT.V_SCROLL | SWT.VIRTUAL | SWT.FULL_SELECTION);
\r
678 viewer.getTable().getAccessible().addAccessibleListener(
\r
679 new AccessibleAdapter() {
\r
680 public void getName(AccessibleEvent e) {
\r
681 if (e.childID == ACC.CHILDID_SELF) {
\r
682 e.result = LegacyActionTools.removeMnemonics(listLabel.getText());
\r
686 viewer.setContentProvider(contentProvider);
\r
688 // added column creation hook
\r
689 createColumns(viewer);
\r
691 // show headers etc. if columns were added by the subclass
\r
692 if (viewer.getTable().getColumnCount() > 0) {
\r
693 viewer.getTable().setLinesVisible(true);
\r
694 viewer.getTable().setHeaderVisible(true);
\r
697 viewer.setInput(new Object[0]);
\r
698 viewer.setItemCount(contentProvider.getNumberOfElements());
\r
699 gd = new GridData(GridData.FILL_BOTH);
\r
700 applyDialogFont(viewer.getTable());
\r
701 gd.heightHint= viewer.getTable().getItemHeight() * 15;
\r
702 viewer.getTable().setLayoutData(gd);
\r
706 pattern.addModifyListener(new ModifyListener() {
\r
707 public void modifyText(ModifyEvent e) {
\r
712 pattern.addKeyListener(new KeyAdapter() {
\r
713 public void keyPressed(KeyEvent e) {
\r
714 if (e.keyCode == SWT.ARROW_DOWN) {
\r
715 if (viewer.getTable().getItemCount() > 0) {
\r
716 viewer.getTable().setFocus();
\r
722 viewer.addSelectionChangedListener(new ISelectionChangedListener() {
\r
723 public void selectionChanged(SelectionChangedEvent event) {
\r
724 StructuredSelection selection = (StructuredSelection) event.getSelection();
\r
725 handleSelected(selection);
\r
729 viewer.addDoubleClickListener(new IDoubleClickListener() {
\r
730 public void doubleClick(DoubleClickEvent event) {
\r
731 handleDoubleClick();
\r
735 viewer.getTable().addKeyListener(new KeyAdapter() {
\r
736 public void keyPressed(KeyEvent e) {
\r
738 if (e.keyCode == SWT.DEL) {
\r
740 List selectedElements = ((StructuredSelection) viewer.getSelection()).toList();
\r
742 Object item = null;
\r
743 boolean isSelectedHistory = true;
\r
745 for (Iterator it = selectedElements.iterator(); it.hasNext();) {
\r
747 if (item instanceof ItemsListSeparator || !isHistoryElement(item)) {
\r
748 isSelectedHistory = false;
\r
752 if (isSelectedHistory)
\r
753 removeSelectedItems(selectedElements);
\r
757 if (e.keyCode == SWT.ARROW_UP && (e.stateMask & SWT.SHIFT) != 0
\r
758 && (e.stateMask & SWT.CTRL) != 0) {
\r
759 StructuredSelection selection = (StructuredSelection) viewer.getSelection();
\r
761 if (selection.size() == 1) {
\r
762 Object element = selection.getFirstElement();
\r
763 if (element.equals(viewer.getElementAt(0))) {
\r
764 pattern.setFocus();
\r
766 if (viewer.getElementAt(viewer.getTable().getSelectionIndex() - 1) instanceof ItemsListSeparator)
\r
767 viewer.getTable().setSelection(viewer.getTable().getSelectionIndex() - 1);
\r
768 viewer.getTable().notifyListeners(SWT.Selection, new Event());
\r
773 if (e.keyCode == SWT.ARROW_DOWN
\r
774 && (e.stateMask & SWT.SHIFT) != 0
\r
775 && (e.stateMask & SWT.CTRL) != 0) {
\r
777 if (viewer.getElementAt(viewer.getTable().getSelectionIndex() + 1) instanceof ItemsListSeparator)
\r
778 viewer.getTable().setSelection(viewer.getTable().getSelectionIndex() + 1);
\r
779 viewer.getTable().notifyListeners(SWT.Selection, new Event());
\r
784 createExtendedContentArea(content);
\r
786 details = new DetailsContentViewer(content, SWT.BORDER | SWT.FLAT);
\r
787 details.setVisible(toggleStatusLineAction.isChecked());
\r
788 details.setContentProvider(new NullContentProvider());
\r
789 details.setLabelProvider(getDetailsLabelProvider());
\r
791 applyDialogFont(content);
\r
793 restoreDialog(getDialogSettings());
\r
795 if (initialPatternText != null) {
\r
796 pattern.setText(initialPatternText);
\r
799 switch (selectionMode) {
\r
800 case CARET_BEGINNING:
\r
801 pattern.setSelection(0, 0);
\r
803 case FULL_SELECTION:
\r
804 pattern.setSelection(0, initialPatternText.length());
\r
808 // apply filter even if pattern is empty (display history)
\r
815 * Override this method to add columns to the content area of this dialog.
\r
817 * Subclass implementation of this method should NOT call the super
\r
818 * implementation as this will break the individual column label providers
\r
822 protected void createColumns(TableViewer viewer) {
\r
823 // no columns are added by default, just set the label provider for the viewer
\r
824 viewer.setLabelProvider(getItemsListLabelProvider());
\r
828 * An utility method for adding a column to the TableViewer, should be called
\r
829 * from inside createColumns.
\r
834 * @param labelProvider
\r
836 protected void createColumn(TableViewer viewer, String label, int width, ColumnLabelProvider labelProvider) {
\r
837 TableViewerColumn viewercol = new TableViewerColumn(viewer, SWT.LEFT);
\r
839 TableColumn col = viewercol.getColumn();
\r
840 col.setText(label);
\r
841 col.setWidth(width);
\r
842 col.setResizable(true);
\r
843 col.setMoveable(true);
\r
845 // TODO: should use the local label provider class instead but it
\r
846 // should be made compatible with multiple columns first
\r
847 viewercol.setLabelProvider(labelProvider);
\r
851 * This method is a hook for subclasses to override default dialog behavior.
\r
852 * The <code>handleDoubleClick()</code> method handles double clicks on
\r
853 * the list of filtered elements.
\r
855 * Current implementation makes double-clicking on the list do the same as
\r
856 * pressing <code>OK</code> button on the dialog.
\r
858 protected void handleDoubleClick() {
\r
863 * Refreshes the details field according to the current selection in the
\r
866 private void refreshDetails() {
\r
867 StructuredSelection selection = getSelectedItems();
\r
869 switch (selection.size()) {
\r
871 details.setInput(null);
\r
874 details.setInput(selection.getFirstElement());
\r
877 details.setInput(NLS.bind(
\r
878 WorkbenchMessages.FilteredItemsSelectionDialog_nItemsSelected,
\r
879 new Integer(selection.size())));
\r
886 * Handle selection in the items list by updating labels of selected and
\r
887 * unselected items and refresh the details field using the selection.
\r
890 * the new selection
\r
892 protected void handleSelected(StructuredSelection selection) {
\r
893 IStatus status = new Status(IStatus.OK, PlatformUI.PLUGIN_ID,
\r
894 IStatus.OK, EMPTY_STRING, null);
\r
896 Object[] lastSelection = currentSelection;
\r
898 currentSelection = selection.toArray();
\r
900 if (selection.size() == 0) {
\r
901 status = new Status(IStatus.ERROR, PlatformUI.PLUGIN_ID,
\r
902 IStatus.ERROR, EMPTY_STRING, null);
\r
904 if (lastSelection != null && getListSelectionLabelDecorator() != null) {
\r
905 viewer.update(lastSelection, null);
\r
908 currentSelection = null;
\r
911 status = new Status(IStatus.ERROR, PlatformUI.PLUGIN_ID,
\r
912 IStatus.ERROR, EMPTY_STRING, null);
\r
914 List items = selection.toList();
\r
916 Object item = null;
\r
917 IStatus tempStatus = null;
\r
919 for (Iterator it = items.iterator(); it.hasNext();) {
\r
920 Object o = it.next();
\r
922 if (o instanceof ItemsListSeparator) {
\r
927 tempStatus = validateItem(item);
\r
929 if (tempStatus.isOK()) {
\r
930 status = new Status(IStatus.OK, PlatformUI.PLUGIN_ID,
\r
931 IStatus.OK, EMPTY_STRING, null);
\r
933 status = tempStatus;
\r
934 // if any selected element is not valid status is set to
\r
940 if (lastSelection != null && getListSelectionLabelDecorator() != null) {
\r
941 viewer.update(lastSelection, null);
\r
944 if (getListSelectionLabelDecorator() != null) {
\r
945 viewer.update(currentSelection, null);
\r
950 updateStatus(status);
\r
956 * @see org.eclipse.jface.window.Dialog#getDialogBoundsSettings()
\r
958 protected IDialogSettings getDialogBoundsSettings() {
\r
959 IDialogSettings settings = getDialogSettings();
\r
960 IDialogSettings section = settings.getSection(DIALOG_BOUNDS_SETTINGS);
\r
961 if (section == null) {
\r
962 section = settings.addNewSection(DIALOG_BOUNDS_SETTINGS);
\r
963 section.put(DIALOG_HEIGHT, 500);
\r
964 section.put(DIALOG_WIDTH, 600);
\r
970 * Returns the dialog settings. Returned object can't be null.
\r
972 * @return return dialog settings for this dialog
\r
974 protected abstract IDialogSettings getDialogSettings();
\r
977 * Refreshes the dialog - has to be called in UI thread.
\r
979 public void refresh() {
\r
980 if (viewer != null && !viewer.getTable().isDisposed()) {
\r
982 List lastRefreshSelection = ((StructuredSelection) viewer.getSelection()).toList();
\r
983 viewer.getTable().deselectAll();
\r
985 viewer.setItemCount(contentProvider.getNumberOfElements());
\r
988 if (viewer.getTable().getItemCount() > 0) {
\r
989 // preserve previous selection
\r
990 if (refreshWithLastSelection && lastRefreshSelection != null
\r
991 && lastRefreshSelection.size() > 0) {
\r
992 viewer.setSelection(new StructuredSelection(
\r
993 lastRefreshSelection));
\r
995 refreshWithLastSelection = true;
\r
996 viewer.getTable().setSelection(0);
\r
997 viewer.getTable().notifyListeners(SWT.Selection, new Event());
\r
1000 viewer.setSelection(StructuredSelection.EMPTY);
\r
1005 scheduleProgressMessageRefresh();
\r
1009 * Updates the progress label.
\r
1013 public void updateProgressLabel() {
\r
1014 scheduleProgressMessageRefresh();
\r
1018 * Notifies the content provider - fires filtering of content provider
\r
1019 * elements. During the filtering, a separator between history and workspace
\r
1020 * matches is added.
\r
1022 * This is a long running operation and should be called in a job.
\r
1024 * @param checkDuplicates
\r
1025 * <code>true</code> if data concerning elements duplication
\r
1026 * should be computed - it takes much more time than the standard
\r
1029 * a progress monitor or <code>null</code> if no monitor is
\r
1032 public void reloadCache(boolean checkDuplicates, IProgressMonitor monitor) {
\r
1033 if (viewer != null && !viewer.getTable().isDisposed() && contentProvider != null) {
\r
1034 contentProvider.reloadCache(checkDuplicates, monitor);
\r
1039 * Schedule refresh job.
\r
1041 public void scheduleRefresh() {
\r
1042 refreshCacheJob.cancelAll();
\r
1043 refreshCacheJob.schedule();
\r
1047 * Schedules progress message refresh.
\r
1049 public void scheduleProgressMessageRefresh() {
\r
1050 if (filterJob.getState() != Job.RUNNING && refreshProgressMessageJob.getState() != Job.RUNNING)
\r
1051 refreshProgressMessageJob.scheduleProgressRefresh(null);
\r
1057 * @see org.eclipse.ui.dialogs.SelectionStatusDialog#computeResult()
\r
1059 protected void computeResult() {
\r
1061 List selectedElements = ((StructuredSelection) viewer.getSelection())
\r
1064 List objectsToReturn = new ArrayList();
\r
1066 Object item = null;
\r
1068 for (Iterator it = selectedElements.iterator(); it.hasNext();) {
\r
1071 if (!(item instanceof ItemsListSeparator)) {
\r
1072 accessedHistoryItem(item);
\r
1073 objectsToReturn.add(item);
\r
1077 setResult(objectsToReturn);
\r
1081 * @see org.eclipse.ui.dialogs.SelectionStatusDialog#updateStatus(org.eclipse.core.runtime.IStatus)
\r
1083 protected void updateStatus(IStatus status) {
\r
1084 this.status = status;
\r
1085 super.updateStatus(status);
\r
1089 * @see Dialog#okPressed()
\r
1091 protected void okPressed() {
\r
1092 if (status != null && (status.isOK() || status.getCode() == IStatus.INFO)) {
\r
1093 super.okPressed();
\r
1098 * Sets the initial pattern used by the filter. This text is copied into the
\r
1099 * selection input on the dialog. A full selection is used in the pattern
\r
1103 * initial pattern for the filter
\r
1104 * @see ColumnFilteredItemsSelectionDialog#FULL_SELECTION
\r
1106 public void setInitialPattern(String text) {
\r
1107 setInitialPattern(text, FULL_SELECTION);
\r
1111 * Sets the initial pattern used by the filter. This text is copied into the
\r
1112 * selection input on the dialog. The <code>selectionMode</code> is used
\r
1113 * to choose selection type for the input field.
\r
1116 * initial pattern for the filter
\r
1117 * @param selectionMode
\r
1118 * one of: {@link ColumnFilteredItemsSelectionDialog#NONE},
\r
1119 * {@link ColumnFilteredItemsSelectionDialog#CARET_BEGINNING},
\r
1120 * {@link ColumnFilteredItemsSelectionDialog#FULL_SELECTION}
\r
1122 public void setInitialPattern(String text, int selectionMode) {
\r
1123 this.initialPatternText = text;
\r
1124 this.selectionMode = selectionMode;
\r
1128 * Gets initial pattern.
\r
1130 * @return initial pattern, or <code>null</code> if initial pattern is not
\r
1133 protected String getInitialPattern() {
\r
1134 return this.initialPatternText;
\r
1138 * Returns the current selection.
\r
1140 * @return the current selection
\r
1142 protected StructuredSelection getSelectedItems() {
\r
1144 StructuredSelection selection = (StructuredSelection) viewer.getSelection();
\r
1146 List selectedItems = selection.toList();
\r
1147 Object itemToRemove = null;
\r
1149 for (Iterator it = selection.iterator(); it.hasNext();) {
\r
1150 Object item = it.next();
\r
1151 if (item instanceof ItemsListSeparator) {
\r
1152 itemToRemove = item;
\r
1157 if (itemToRemove == null)
\r
1158 return new StructuredSelection(selectedItems);
\r
1159 // Create a new selection without the collision
\r
1160 List newItems = new ArrayList(selectedItems);
\r
1161 newItems.remove(itemToRemove);
\r
1162 return new StructuredSelection(newItems);
\r
1167 * Validates the item. When items on the items list are selected or
\r
1168 * deselected, it validates each item in the selection and the dialog status
\r
1169 * depends on all validations.
\r
1172 * an item to be checked
\r
1173 * @return status of the dialog to be set
\r
1175 protected abstract IStatus validateItem(Object item);
\r
1178 * Creates an instance of a filter.
\r
1180 * @return a filter for items on the items list. Can be <code>null</code>,
\r
1181 * no filtering will be applied then, causing no item to be shown in
\r
1184 protected abstract ItemsFilter createFilter();
\r
1187 * Applies the filter created by <code>createFilter()</code> method to the
\r
1188 * items list. When new filter is different than previous one it will cause
\r
1191 protected void applyFilter() {
\r
1192 ItemsFilter newFilter = createFilter();
\r
1194 // don't apply filtering for patterns which mean the same, for example:
\r
1195 // *a**b and ***a*b
\r
1196 if (filter != null && filter.equalsFilter(newFilter)) {
\r
1200 filterHistoryJob.cancel();
\r
1201 filterJob.cancel();
\r
1203 this.filter = newFilter;
\r
1205 if (this.filter != null) {
\r
1206 filterHistoryJob.schedule();
\r
1212 * Returns comparator to sort items inside content provider. Returned object
\r
1213 * will be probably created as an anonymous class. Parameters passed to the
\r
1214 * <code>compare(java.lang.Object, java.lang.Object)</code> are going to
\r
1215 * be the same type as the one used in the content provider.
\r
1217 * @return comparator to sort items content provider
\r
1219 protected abstract Comparator getItemsComparator();
\r
1222 * Fills the content provider with matching items.
\r
1224 * @param contentProvider
\r
1225 * collector to add items to.
\r
1226 * {@link ColumnFilteredItemsSelectionDialog.AbstractContentProvider#add(Object, ColumnFilteredItemsSelectionDialog.ItemsFilter)}
\r
1227 * only adds items that pass the given <code>itemsFilter</code>.
\r
1228 * @param itemsFilter
\r
1229 * the items filter
\r
1230 * @param progressMonitor
\r
1231 * must be used to report search progress. The state of this
\r
1232 * progress monitor reflects the state of the filtering process.
\r
1233 * @throws CoreException
\r
1235 protected abstract void fillContentProvider(
\r
1236 AbstractContentProvider contentProvider, ItemsFilter itemsFilter,
\r
1237 IProgressMonitor progressMonitor) throws CoreException;
\r
1240 * Force a refresh of the content provider.
\r
1242 protected void forceRefresh() {
\r
1243 lastCompletedFilter = null;
\r
1244 lastCompletedResult = null;
\r
1245 filterHistoryJob.schedule();
\r
1249 * Removes selected items from history.
\r
1252 * items to be removed
\r
1254 private void removeSelectedItems(List items) {
\r
1255 for (Iterator iter = items.iterator(); iter.hasNext();) {
\r
1256 Object item = iter.next();
\r
1257 removeHistoryItem(item);
\r
1259 refreshWithLastSelection = false;
\r
1260 contentProvider.refresh();
\r
1264 * Removes an item from history.
\r
1267 * an item to remove
\r
1268 * @return removed item
\r
1270 protected Object removeHistoryItem(Object item) {
\r
1271 return contentProvider.removeHistoryElement(item);
\r
1275 * Adds item to history.
\r
1278 * the item to be added
\r
1280 protected void accessedHistoryItem(Object item) {
\r
1281 contentProvider.addHistoryElement(item);
\r
1285 * Returns a history comparator.
\r
1287 * @return decorated comparator
\r
1289 private Comparator getHistoryComparator() {
\r
1290 return new HistoryComparator();
\r
1294 * Returns the history of selected elements.
\r
1296 * @return history of selected elements, or <code>null</code> if it is not
\r
1299 protected SelectionHistory getSelectionHistory() {
\r
1300 return this.contentProvider.getSelectionHistory();
\r
1304 * Sets new history.
\r
1306 * @param selectionHistory
\r
1309 protected void setSelectionHistory(SelectionHistory selectionHistory) {
\r
1310 if (this.contentProvider != null)
\r
1311 this.contentProvider.setSelectionHistory(selectionHistory);
\r
1315 * Indicates whether the given item is a history item.
\r
1318 * the item to be investigated
\r
1319 * @return <code>true</code> if the given item exists in history,
\r
1320 * <code>false</code> otherwise
\r
1322 public boolean isHistoryElement(Object item) {
\r
1323 return this.contentProvider.isHistoryElement(item);
\r
1327 * Indicates whether the given item is a duplicate.
\r
1330 * the item to be investigated
\r
1331 * @return <code>true</code> if the item is duplicate, <code>false</code>
\r
1334 public boolean isDuplicateElement(Object item) {
\r
1335 return this.contentProvider.isDuplicateElement(item);
\r
1339 * Sets separator label
\r
1341 * @param separatorLabel
\r
1342 * the label showed on separator
\r
1344 public void setSeparatorLabel(String separatorLabel) {
\r
1345 this.itemsListSeparator = new ItemsListSeparator(separatorLabel);
\r
1349 * Returns name for then given object.
\r
1352 * an object from the content provider. Subclasses should pay
\r
1353 * attention to the passed argument. They should either only pass
\r
1354 * objects of a known type (one used in content provider) or make
\r
1355 * sure that passed parameter is the expected one (by type
\r
1356 * checking like <code>instanceof</code> inside the method).
\r
1357 * @return name of the given item
\r
1359 public abstract String getElementName(Object item);
\r
1361 private class ToggleStatusLineAction extends Action {
\r
1364 * Creates a new instance of the class.
\r
1366 public ToggleStatusLineAction() {
\r
1367 super(WorkbenchMessages.FilteredItemsSelectionDialog_toggleStatusAction, IAction.AS_CHECK_BOX);
\r
1370 public void run() {
\r
1371 details.setVisible(isChecked());
\r
1376 * Only refreshes UI on the basis of an already sorted and filtered set of
\r
1379 * Standard invocation scenario:
\r
1381 * <li>filtering job (<code>FilterJob</code> class extending
\r
1382 * <code>Job</code> class)</li>
\r
1383 * <li>cache refresh without checking for duplicates (<code>RefreshCacheJob</code>
\r
1384 * class extending <code>Job</code> class)</li>
\r
1385 * <li>UI refresh (<code>RefreshJob</code> class extending
\r
1386 * <code>UIJob</code> class)</li>
\r
1387 * <li>cache refresh with checking for duplicates (<cod>CacheRefreshJob</code>
\r
1388 * class extending <code>Job</code> class)</li>
\r
1389 * <li>UI refresh (<code>RefreshJob</code> class extending <code>UIJob</code>
\r
1392 * The scenario is rather complicated, but it had to be applied, because:
\r
1394 * <li> refreshing cache is rather a long action and cannot be run in the UI -
\r
1395 * cannot be run in a UIJob</li>
\r
1396 * <li> refreshing cache checking for duplicates is twice as long as
\r
1397 * refreshing cache without checking for duplicates; results of the search
\r
1398 * could be displayed earlier</li>
\r
1399 * <li> refreshing the UI have to be run in a UIJob</li>
\r
1402 * @see org.eclipse.ui.dialogs.FilteredItemsSelectionDialog.FilterJob
\r
1403 * @see org.eclipse.ui.dialogs.FilteredItemsSelectionDialog.RefreshJob
\r
1404 * @see org.eclipse.ui.dialogs.FilteredItemsSelectionDialog.RefreshCacheJob
\r
1406 private class RefreshJob extends UIJob {
\r
1409 * Creates a new instance of the class.
\r
1411 public RefreshJob() {
\r
1412 super(ColumnFilteredItemsSelectionDialog.this.getParentShell().getDisplay(),
\r
1413 WorkbenchMessages.FilteredItemsSelectionDialog_refreshJob);
\r
1420 * @see org.eclipse.ui.progress.UIJob#runInUIThread(org.eclipse.core.runtime.IProgressMonitor)
\r
1422 public IStatus runInUIThread(IProgressMonitor monitor) {
\r
1423 if (monitor.isCanceled())
\r
1424 return new Status(IStatus.OK, WorkbenchPlugin.PI_WORKBENCH,
\r
1425 IStatus.OK, EMPTY_STRING, null);
\r
1427 if (ColumnFilteredItemsSelectionDialog.this != null) {
\r
1428 ColumnFilteredItemsSelectionDialog.this.refresh();
\r
1431 return new Status(IStatus.OK, PlatformUI.PLUGIN_ID, IStatus.OK,
\r
1432 EMPTY_STRING, null);
\r
1438 * Refreshes the progress message cyclically with 500 milliseconds delay.
\r
1439 * <code>RefreshProgressMessageJob</code> is strictly connected with
\r
1440 * <code>GranualProgressMonitor</code> and use it to to get progress
\r
1441 * message and to decide about break of cyclical refresh.
\r
1443 private class RefreshProgressMessageJob extends UIJob {
\r
1445 private GranualProgressMonitor progressMonitor;
\r
1448 * Creates a new instance of the class.
\r
1450 public RefreshProgressMessageJob() {
\r
1451 super(ColumnFilteredItemsSelectionDialog.this.getParentShell().getDisplay(),
\r
1452 WorkbenchMessages.FilteredItemsSelectionDialog_progressRefreshJob);
\r
1459 * @see org.eclipse.ui.progress.UIJob#runInUIThread(org.eclipse.core.runtime.IProgressMonitor)
\r
1461 public IStatus runInUIThread(IProgressMonitor monitor) {
\r
1463 if (!progressLabel.isDisposed())
\r
1464 progressLabel.setText(progressMonitor != null ? progressMonitor.getMessage() : EMPTY_STRING);
\r
1466 if (progressMonitor == null || progressMonitor.isDone()) {
\r
1467 return new Status(IStatus.CANCEL, PlatformUI.PLUGIN_ID,
\r
1468 IStatus.CANCEL, EMPTY_STRING, null);
\r
1471 // Schedule cyclical with 500 milliseconds delay
\r
1474 return new Status(IStatus.OK, PlatformUI.PLUGIN_ID, IStatus.OK,
\r
1475 EMPTY_STRING, null);
\r
1479 * Schedule progress refresh job.
\r
1481 * @param progressMonitor
\r
1482 * used during refresh progress label
\r
1484 public void scheduleProgressRefresh(
\r
1485 GranualProgressMonitor progressMonitor) {
\r
1486 this.progressMonitor = progressMonitor;
\r
1487 // Schedule with initial delay to avoid flickering when the user
\r
1495 * A job responsible for computing filtered items list presented using
\r
1496 * <code>RefreshJob</code>.
\r
1498 * @see ColumnFilteredItemsSelectionDialog.RefreshJob
\r
1501 private class RefreshCacheJob extends Job {
\r
1503 private RefreshJob refreshJob = new RefreshJob();
\r
1506 * Creates a new instance of the class.
\r
1508 public RefreshCacheJob() {
\r
1509 super(WorkbenchMessages.FilteredItemsSelectionDialog_cacheRefreshJob);
\r
1514 * Stops the job and all sub-jobs.
\r
1516 public void cancelAll() {
\r
1518 refreshJob.cancel();
\r
1524 * @see org.eclipse.core.runtime.jobs.Job#run(org.eclipse.core.runtime.IProgressMonitor)
\r
1526 protected IStatus run(IProgressMonitor monitor) {
\r
1527 if (monitor.isCanceled()) {
\r
1528 return new Status(IStatus.CANCEL, WorkbenchPlugin.PI_WORKBENCH,
\r
1529 IStatus.CANCEL, EMPTY_STRING, null);
\r
1532 if (ColumnFilteredItemsSelectionDialog.this != null) {
\r
1533 GranualProgressMonitor wrappedMonitor = new GranualProgressMonitor(monitor);
\r
1534 ColumnFilteredItemsSelectionDialog.this.reloadCache(true, wrappedMonitor);
\r
1537 if (!monitor.isCanceled()) {
\r
1538 refreshJob.schedule();
\r
1541 return new Status(IStatus.OK, PlatformUI.PLUGIN_ID, IStatus.OK,
\r
1542 EMPTY_STRING, null);
\r
1549 * @see org.eclipse.core.runtime.jobs.Job#canceling()
\r
1551 protected void canceling() {
\r
1552 super.canceling();
\r
1553 contentProvider.stopReloadingCache();
\r
1558 private class RemoveHistoryItemAction extends Action {
\r
1561 * Creates a new instance of the class.
\r
1563 public RemoveHistoryItemAction() {
\r
1564 super(WorkbenchMessages.FilteredItemsSelectionDialog_removeItemsFromHistoryAction);
\r
1570 * @see org.eclipse.jface.action.Action#run()
\r
1572 public void run() {
\r
1573 List selectedElements = ((StructuredSelection) viewer.getSelection()).toList();
\r
1574 removeSelectedItems(selectedElements);
\r
1578 private static boolean showColoredLabels() {
\r
1579 return PlatformUI.getPreferenceStore().getBoolean(IWorkbenchPreferenceConstants.USE_COLORED_LABELS);
\r
1582 private class ItemsListLabelProvider extends StyledCellLabelProvider
\r
1583 implements ILabelProviderListener {
\r
1584 private ILabelProvider provider;
\r
1586 private ILabelDecorator selectionDecorator;
\r
1588 // Need to keep our own list of listeners
\r
1589 private ListenerList listeners = new ListenerList();
\r
1592 * Creates a new instance of the class.
\r
1595 * the label provider for all items, not <code>null</code>
\r
1596 * @param selectionDecorator
\r
1597 * the decorator for selected items, can be <code>null</code>
\r
1599 public ItemsListLabelProvider(ILabelProvider provider,
\r
1600 ILabelDecorator selectionDecorator) {
\r
1601 Assert.isNotNull(provider);
\r
1602 this.provider = provider;
\r
1603 this.selectionDecorator = selectionDecorator;
\r
1605 setOwnerDrawEnabled(showColoredLabels() && provider instanceof IStyledLabelProvider);
\r
1607 provider.addListener(this);
\r
1609 if (selectionDecorator != null) {
\r
1610 selectionDecorator.addListener(this);
\r
1615 * Sets new selection decorator.
\r
1617 * @param newSelectionDecorator
\r
1618 * new label decorator for selected items in the list
\r
1620 public void setSelectionDecorator(ILabelDecorator newSelectionDecorator) {
\r
1621 if (selectionDecorator != null) {
\r
1622 selectionDecorator.removeListener(this);
\r
1623 selectionDecorator.dispose();
\r
1626 selectionDecorator = newSelectionDecorator;
\r
1628 if (selectionDecorator != null) {
\r
1629 selectionDecorator.addListener(this);
\r
1634 * Gets selection decorator.
\r
1636 * @return the label decorator for selected items in the list
\r
1638 public ILabelDecorator getSelectionDecorator() {
\r
1639 return selectionDecorator;
\r
1643 * Sets new label provider.
\r
1645 * @param newProvider
\r
1646 * new label provider for items in the list, not
\r
1647 * <code>null</code>
\r
1649 public void setProvider(ILabelProvider newProvider) {
\r
1650 Assert.isNotNull(newProvider);
\r
1651 provider.removeListener(this);
\r
1652 provider.dispose();
\r
1654 provider = newProvider;
\r
1656 if (provider != null) {
\r
1657 provider.addListener(this);
\r
1660 setOwnerDrawEnabled(showColoredLabels() && provider instanceof IStyledLabelProvider);
\r
1663 private Image getImage(Object element) {
\r
1664 if (element instanceof ItemsListSeparator) {
\r
1665 return WorkbenchImages.getImage(IWorkbenchGraphicConstants.IMG_OBJ_SEPARATOR);
\r
1668 return provider.getImage(element);
\r
1671 private boolean isSelected(Object element) {
\r
1672 if (element != null && currentSelection != null) {
\r
1673 for (int i = 0; i < currentSelection.length; i++) {
\r
1674 if (element.equals(currentSelection[i]))
\r
1684 * @see org.eclipse.jface.viewers.ILabelProvider#getText(java.lang.Object)
\r
1686 private String getText(Object element) {
\r
1687 if (element instanceof ItemsListSeparator) {
\r
1688 return getSeparatorLabel(((ItemsListSeparator) element).getName());
\r
1691 String str = provider.getText(element);
\r
1692 if (selectionDecorator != null && isSelected(element)) {
\r
1693 return selectionDecorator.decorateText(str.toString(), element);
\r
1699 private StyledString getStyledText(Object element,
\r
1700 IStyledLabelProvider provider) {
\r
1701 StyledString string = provider.getStyledText(element);
\r
1703 if (selectionDecorator != null && isSelected(element)) {
\r
1704 String decorated = selectionDecorator.decorateText(string.getString(), element);
\r
1705 return StyledCellLabelProvider.styleDecoratedString(decorated, null, string);
\r
1706 // no need to add colors when element is selected
\r
1711 public void update(ViewerCell cell) {
\r
1712 Object element = cell.getElement();
\r
1714 if (!(element instanceof ItemsListSeparator) && provider instanceof IStyledLabelProvider) {
\r
1715 IStyledLabelProvider styledLabelProvider = (IStyledLabelProvider) provider;
\r
1716 StyledString styledString = getStyledText(element, styledLabelProvider);
\r
1718 cell.setText(styledString.getString());
\r
1719 cell.setStyleRanges(styledString.getStyleRanges());
\r
1720 cell.setImage(styledLabelProvider.getImage(element));
\r
1722 cell.setText(getText(element));
\r
1723 cell.setImage(getImage(element));
\r
1725 cell.setFont(getFont(element));
\r
1726 cell.setForeground(getForeground(element));
\r
1727 cell.setBackground(getBackground(element));
\r
1729 super.update(cell);
\r
1732 private String getSeparatorLabel(String separatorLabel) {
\r
1733 Rectangle rect = viewer.getTable().getBounds();
\r
1735 int borderWidth = viewer.getTable().computeTrim(0, 0, 0, 0).width;
\r
1737 int imageWidth = WorkbenchImages.getImage(
\r
1738 IWorkbenchGraphicConstants.IMG_OBJ_SEPARATOR).getBounds().width;
\r
1740 int width = rect.width - borderWidth - imageWidth;
\r
1742 GC gc = new GC(viewer.getTable());
\r
1743 gc.setFont(viewer.getTable().getFont());
\r
1745 int fSeparatorWidth = gc.getAdvanceWidth('-');
\r
1746 int fMessageLength = gc.textExtent(separatorLabel).x;
\r
1750 StringBuffer dashes = new StringBuffer();
\r
1751 int chars = (((width - fMessageLength) / fSeparatorWidth) / 2) - 2;
\r
1752 for (int i = 0; i < chars; i++) {
\r
1753 dashes.append('-');
\r
1756 StringBuffer result = new StringBuffer();
\r
1757 result.append(dashes);
\r
1758 result.append(" " + separatorLabel + " "); //$NON-NLS-1$//$NON-NLS-2$
\r
1759 result.append(dashes);
\r
1760 return result.toString().trim();
\r
1766 * @see org.eclipse.jface.viewers.IBaseLabelProvider#addListener(org.eclipse.jface.viewers.ILabelProviderListener)
\r
1768 public void addListener(ILabelProviderListener listener) {
\r
1769 listeners.add(listener);
\r
1775 * @see org.eclipse.jface.viewers.IBaseLabelProvider#dispose()
\r
1777 public void dispose() {
\r
1778 provider.removeListener(this);
\r
1779 provider.dispose();
\r
1781 if (selectionDecorator != null) {
\r
1782 selectionDecorator.removeListener(this);
\r
1783 selectionDecorator.dispose();
\r
1792 * @see org.eclipse.jface.viewers.IBaseLabelProvider#isLabelProperty(java.lang.Object,
\r
1793 * java.lang.String)
\r
1795 public boolean isLabelProperty(Object element, String property) {
\r
1796 if (provider.isLabelProperty(element, property)) {
\r
1799 if (selectionDecorator != null
\r
1800 && selectionDecorator.isLabelProperty(element, property)) {
\r
1809 * @see org.eclipse.jface.viewers.IBaseLabelProvider#removeListener(org.eclipse.jface.viewers.ILabelProviderListener)
\r
1811 public void removeListener(ILabelProviderListener listener) {
\r
1812 listeners.remove(listener);
\r
1815 private Color getBackground(Object element) {
\r
1816 if (element instanceof ItemsListSeparator) {
\r
1819 if (provider instanceof IColorProvider) {
\r
1820 return ((IColorProvider) provider).getBackground(element);
\r
1825 private Color getForeground(Object element) {
\r
1826 if (element instanceof ItemsListSeparator) {
\r
1827 return Display.getCurrent().getSystemColor(
\r
1828 SWT.COLOR_WIDGET_NORMAL_SHADOW);
\r
1830 if (provider instanceof IColorProvider) {
\r
1831 return ((IColorProvider) provider).getForeground(element);
\r
1836 private Font getFont(Object element) {
\r
1837 if (element instanceof ItemsListSeparator) {
\r
1840 if (provider instanceof IFontProvider) {
\r
1841 return ((IFontProvider) provider).getFont(element);
\r
1849 * @see org.eclipse.jface.viewers.ILabelProviderListener#labelProviderChanged(org.eclipse.jface.viewers.LabelProviderChangedEvent)
\r
1851 public void labelProviderChanged(LabelProviderChangedEvent event) {
\r
1852 Object[] l = listeners.getListeners();
\r
1853 for (int i = 0; i < listeners.size(); i++) {
\r
1854 ((ILabelProviderListener) l[i]).labelProviderChanged(event);
\r
1860 * Used in ItemsListContentProvider, separates history and non-history
\r
1863 private class ItemsListSeparator {
\r
1865 private String name;
\r
1868 * Creates a new instance of the class.
\r
1871 * the name of the separator
\r
1873 public ItemsListSeparator(String name) {
\r
1878 * Returns the name of this separator.
\r
1880 * @return the name of the separator
\r
1882 public String getName() {
\r
1888 * GranualProgressMonitor is used for monitoring progress of filtering
\r
1889 * process. It is used by <code>RefreshProgressMessageJob</code> to
\r
1890 * refresh progress message. State of this monitor illustrates state of
\r
1891 * filtering or cache refreshing process.
\r
1894 private class GranualProgressMonitor extends ProgressMonitorWrapper {
\r
1896 private String name;
\r
1898 private String subName;
\r
1900 private int totalWork;
\r
1902 private double worked;
\r
1904 private boolean done;
\r
1907 * Creates instance of <code>GranualProgressMonitor</code>.
\r
1910 * progress to be wrapped
\r
1912 public GranualProgressMonitor(IProgressMonitor monitor) {
\r
1917 * Checks if filtering has been done
\r
1919 * @return true if filtering work has been done false in other way
\r
1921 public boolean isDone() {
\r
1928 * @see org.eclipse.core.runtime.ProgressMonitorWrapper#setTaskName(java.lang.String)
\r
1930 public void setTaskName(String name) {
\r
1931 super.setTaskName(name);
\r
1933 this.subName = null;
\r
1939 * @see org.eclipse.core.runtime.ProgressMonitorWrapper#subTask(java.lang.String)
\r
1941 public void subTask(String name) {
\r
1942 super.subTask(name);
\r
1943 this.subName = name;
\r
1949 * @see org.eclipse.core.runtime.ProgressMonitorWrapper#beginTask(java.lang.String,
\r
1952 public void beginTask(String name, int totalWork) {
\r
1953 super.beginTask(name, totalWork);
\r
1954 if (this.name == null)
\r
1956 this.totalWork = totalWork;
\r
1957 refreshProgressMessageJob.scheduleProgressRefresh(this);
\r
1963 * @see org.eclipse.core.runtime.ProgressMonitorWrapper#worked(int)
\r
1965 public void worked(int work) {
\r
1966 super.worked(work);
\r
1967 internalWorked(work);
\r
1973 * @see org.eclipse.core.runtime.ProgressMonitorWrapper#done()
\r
1975 public void done() {
\r
1983 * @see org.eclipse.core.runtime.ProgressMonitorWrapper#setCanceled(boolean)
\r
1985 public void setCanceled(boolean b) {
\r
1987 super.setCanceled(b);
\r
1993 * @see org.eclipse.core.runtime.ProgressMonitorWrapper#internalWorked(double)
\r
1995 public void internalWorked(double work) {
\r
1996 worked = worked + work;
\r
1999 private String getMessage() {
\r
2001 return ""; //$NON-NLS-1$
\r
2005 if (name == null) {
\r
2006 message = subName == null ? "" : subName; //$NON-NLS-1$
\r
2008 message = subName == null ? name
\r
2009 : NLS.bind(WorkbenchMessages.FilteredItemsSelectionDialog_subtaskProgressMessage,
\r
2010 new Object[] { name, subName });
\r
2012 if (totalWork == 0)
\r
2015 return NLS.bind(WorkbenchMessages.FilteredItemsSelectionDialog_taskProgressMessage,
\r
2016 new Object[] { message, new Integer((int) ((worked * 100) / totalWork)) });
\r
2023 * Filters items history and schedule filter job.
\r
2025 private class FilterHistoryJob extends Job {
\r
2028 * Filter used during the filtering process.
\r
2030 private ItemsFilter itemsFilter;
\r
2033 * Creates new instance of receiver.
\r
2035 public FilterHistoryJob() {
\r
2036 super(WorkbenchMessages.FilteredItemsSelectionDialog_jobLabel);
\r
2043 * @see org.eclipse.core.runtime.jobs.Job#run(org.eclipse.core.runtime.IProgressMonitor)
\r
2045 protected IStatus run(IProgressMonitor monitor) {
\r
2046 this.itemsFilter = filter;
\r
2048 contentProvider.reset();
\r
2050 refreshWithLastSelection = false;
\r
2052 contentProvider.addHistoryItems(itemsFilter);
\r
2054 if (!(lastCompletedFilter != null && lastCompletedFilter.isSubFilter(this.itemsFilter)))
\r
2055 contentProvider.refresh();
\r
2057 filterJob.schedule();
\r
2059 return Status.OK_STATUS;
\r
2065 * Filters items in indicated set and history. During filtering, it
\r
2066 * refreshes the dialog (progress monitor and elements list).
\r
2068 * Depending on the filter, <code>FilterJob</code> decides which kind of
\r
2069 * search will be run inside <code>filterContent</code>. If the last
\r
2070 * filtering is done (last completed filter), is not null, and the new
\r
2071 * filter is a sub-filter ({@link ColumnFilteredItemsSelectionDialog.ItemsFilter#isSubFilter(ColumnFilteredItemsSelectionDialog.ItemsFilter)})
\r
2072 * of the last, then <code>FilterJob</code> only filters in the cache. If
\r
2073 * it is the first filtering or the new filter isn't a sub-filter of the
\r
2074 * last one, a full search is run.
\r
2076 private class FilterJob extends Job {
\r
2079 * Filter used during the filtering process.
\r
2081 protected ItemsFilter itemsFilter;
\r
2084 * Creates new instance of FilterJob
\r
2086 public FilterJob() {
\r
2087 super(WorkbenchMessages.FilteredItemsSelectionDialog_jobLabel);
\r
2094 * @see org.eclipse.core.runtime.jobs.Job#run(org.eclipse.core.runtime.IProgressMonitor)
\r
2096 protected final IStatus run(IProgressMonitor parent) {
\r
2097 GranualProgressMonitor monitor = new GranualProgressMonitor(parent);
\r
2098 return doRun(monitor);
\r
2102 * Executes job using the given filtering progress monitor. A hook for
\r
2106 * progress monitor
\r
2107 * @return result of the execution
\r
2109 protected IStatus doRun(GranualProgressMonitor monitor) {
\r
2111 internalRun(monitor);
\r
2112 } catch (CoreException e) {
\r
2114 return new Status(
\r
2116 PlatformUI.PLUGIN_ID,
\r
2118 WorkbenchMessages.FilteredItemsSelectionDialog_jobError,
\r
2121 return Status.OK_STATUS;
\r
2125 * Main method for the job.
\r
2128 * @throws CoreException
\r
2130 private void internalRun(GranualProgressMonitor monitor)
\r
2131 throws CoreException {
\r
2133 if (monitor.isCanceled())
\r
2136 this.itemsFilter = filter;
\r
2138 // why is the content not filtered if the patter is empty?
\r
2139 // this makes no sense since the search pattern is able to
\r
2140 // handle the empty pattern just fine, and even has settings
\r
2141 // for what to do in this case
\r
2142 //if (filter.getPattern().length() != 0) {
\r
2143 // filterContent(monitor);
\r
2146 filterContent(monitor);
\r
2148 if (monitor.isCanceled())
\r
2151 contentProvider.refresh();
\r
2161 * for monitoring progress
\r
2162 * @throws CoreException
\r
2164 protected void filterContent(GranualProgressMonitor monitor)
\r
2165 throws CoreException {
\r
2167 if (lastCompletedFilter != null
\r
2168 && lastCompletedFilter.isSubFilter(this.itemsFilter)) {
\r
2170 int length = lastCompletedResult.size() / 500;
\r
2171 monitor.beginTask(WorkbenchMessages.FilteredItemsSelectionDialog_cacheSearchJob_taskName, length);
\r
2173 for (int pos = 0; pos < lastCompletedResult.size(); pos++) {
\r
2175 Object item = lastCompletedResult.get(pos);
\r
2176 if (monitor.isCanceled())
\r
2178 contentProvider.add(item, itemsFilter);
\r
2180 if ((pos % 500) == 0) {
\r
2181 monitor.worked(1);
\r
2187 lastCompletedFilter = null;
\r
2188 lastCompletedResult = null;
\r
2190 SubProgressMonitor subMonitor = null;
\r
2191 if (monitor != null) {
\r
2192 monitor.beginTask(WorkbenchMessages.FilteredItemsSelectionDialog_searchJob_taskName, 100);
\r
2193 subMonitor = new SubProgressMonitor(monitor, 95);
\r
2197 fillContentProvider(contentProvider, itemsFilter, subMonitor);
\r
2199 if (monitor != null && !monitor.isCanceled()) {
\r
2200 monitor.worked(2);
\r
2201 contentProvider.rememberResult(itemsFilter);
\r
2202 monitor.worked(3);
\r
2211 * History stores a list of key, object pairs. The list is bounded at a
\r
2212 * certain size. If the list exceeds this size the oldest element is removed
\r
2213 * from the list. An element can be added/renewed with a call to
\r
2214 * <code>accessed(Object)</code>.
\r
2216 * The history can be stored to/loaded from an XML file.
\r
2218 protected static abstract class SelectionHistory {
\r
2220 private static final String DEFAULT_ROOT_NODE_NAME = "historyRootNode"; //$NON-NLS-1$
\r
2222 private static final String DEFAULT_INFO_NODE_NAME = "infoNode"; //$NON-NLS-1$
\r
2224 private static final int MAX_HISTORY_SIZE = 60;
\r
2226 private final Set historyList;
\r
2228 private final String rootNodeName;
\r
2230 private final String infoNodeName;
\r
2232 private SelectionHistory(String rootNodeName, String infoNodeName) {
\r
2234 historyList = Collections.synchronizedSet(new LinkedHashSet() {
\r
2236 private static final long serialVersionUID = 0L;
\r
2241 * @see java.util.LinkedList#add(java.lang.Object)
\r
2243 public boolean add(Object arg0) {
\r
2244 if (this.size() >= MAX_HISTORY_SIZE) {
\r
2245 Iterator iterator = this.iterator();
\r
2247 iterator.remove();
\r
2249 return super.add(arg0);
\r
2254 this.rootNodeName = rootNodeName;
\r
2255 this.infoNodeName = infoNodeName;
\r
2259 * Creates new instance of <code>SelectionHistory</code>.
\r
2261 public SelectionHistory() {
\r
2262 this(DEFAULT_ROOT_NODE_NAME, DEFAULT_INFO_NODE_NAME);
\r
2266 * Adds object to history.
\r
2269 * the item to be added to the history
\r
2271 public synchronized void accessed(Object object) {
\r
2272 historyList.remove(object);
\r
2273 historyList.add(object);
\r
2277 * Returns <code>true</code> if history contains object.
\r
2280 * the item for which check will be executed
\r
2281 * @return <code>true</code> if history contains object
\r
2282 * <code>false</code> in other way
\r
2284 public synchronized boolean contains(Object object) {
\r
2285 return historyList.contains(object);
\r
2289 * Returns <code>true</code> if history is empty.
\r
2291 * @return <code>true</code> if history is empty
\r
2293 public synchronized boolean isEmpty() {
\r
2294 return historyList.isEmpty();
\r
2298 * Remove element from history.
\r
2301 * to remove form the history
\r
2302 * @return <code>true</code> if this list contained the specified
\r
2305 public synchronized boolean remove(Object element) {
\r
2306 return historyList.remove(element);
\r
2310 * Load history elements from memento.
\r
2313 * memento from which the history will be retrieved
\r
2315 public void load(IMemento memento) {
\r
2317 XMLMemento historyMemento = (XMLMemento) memento
\r
2318 .getChild(rootNodeName);
\r
2320 if (historyMemento == null) {
\r
2324 IMemento[] mementoElements = historyMemento
\r
2325 .getChildren(infoNodeName);
\r
2326 for (int i = 0; i < mementoElements.length; ++i) {
\r
2327 IMemento mementoElement = mementoElements[i];
\r
2328 Object object = restoreItemFromMemento(mementoElement);
\r
2329 if (object != null) {
\r
2330 historyList.add(object);
\r
2336 * Save history elements to memento.
\r
2339 * memento to which the history will be added
\r
2341 public void save(IMemento memento) {
\r
2343 IMemento historyMemento = memento.createChild(rootNodeName);
\r
2345 Object[] items = getHistoryItems();
\r
2346 for (int i = 0; i < items.length; i++) {
\r
2347 Object item = items[i];
\r
2348 IMemento elementMemento = historyMemento
\r
2349 .createChild(infoNodeName);
\r
2350 storeItemToMemento(item, elementMemento);
\r
2356 * Gets array of history items.
\r
2358 * @return array of history elements
\r
2360 public synchronized Object[] getHistoryItems() {
\r
2361 return historyList.toArray();
\r
2365 * Creates an object using given memento.
\r
2368 * memento used for creating new object
\r
2370 * @return the restored object
\r
2372 protected abstract Object restoreItemFromMemento(IMemento memento);
\r
2375 * Store object in <code>IMemento</code>.
\r
2378 * the item to store
\r
2380 * the memento to store to
\r
2382 protected abstract void storeItemToMemento(Object item, IMemento memento);
\r
2387 * Filters elements using SearchPattern by comparing the names of items with
\r
2388 * the filter pattern.
\r
2390 protected abstract class ItemsFilter {
\r
2392 protected SearchPattern patternMatcher;
\r
2395 * Creates new instance of ItemsFilter.
\r
2397 public ItemsFilter() {
\r
2398 this(new SearchPattern());
\r
2402 * Creates new instance of ItemsFilter.
\r
2404 * @param searchPattern
\r
2405 * the pattern to be used when filtering
\r
2407 public ItemsFilter(SearchPattern searchPattern) {
\r
2408 patternMatcher = searchPattern;
\r
2409 String stringPattern = ""; //$NON-NLS-1$
\r
2410 if (pattern != null && !pattern.getText().equals("*")) { //$NON-NLS-1$
\r
2411 stringPattern = pattern.getText();
\r
2413 patternMatcher.setPattern(stringPattern);
\r
2417 * Check if the given filter is a sub-filter of this filter. The default
\r
2418 * implementation checks if the <code>SearchPattern</code> from the
\r
2419 * given filter is a sub-pattern of the one from this filter.
\r
2421 * <i>WARNING: This method is <b>not</b> defined in reading order, i.e.
\r
2422 * <code>a.isSubFilter(b)</code> is <code>true</code> iff
\r
2423 * <code>b</code> is a sub-filter of <code>a</code>, and not
\r
2424 * vice-versa. </i>
\r
2428 * the filter to be checked, or <code>null</code>
\r
2429 * @return <code>true</code> if the given filter is sub-filter of this
\r
2430 * filter, <code>false</code> if the given filter isn't a
\r
2431 * sub-filter or is <code>null</code>
\r
2433 * @see org.eclipse.ui.dialogs.SearchPattern#isSubPattern(org.eclipse.ui.dialogs.SearchPattern)
\r
2435 public boolean isSubFilter(ItemsFilter filter) {
\r
2436 if (filter != null) {
\r
2437 return this.patternMatcher.isSubPattern(filter.patternMatcher);
\r
2443 * Checks whether the provided filter is equal to the current filter.
\r
2444 * The default implementation checks if <code>SearchPattern</code>
\r
2445 * from current filter is equal to the one from provided filter.
\r
2448 * filter to be checked, or <code>null</code>
\r
2449 * @return <code>true</code> if the given filter is equal to current
\r
2450 * filter, <code>false</code> if given filter isn't equal to
\r
2451 * current one or if it is <code>null</code>
\r
2453 * @see org.eclipse.ui.dialogs.SearchPattern#equalsPattern(org.eclipse.ui.dialogs.SearchPattern)
\r
2455 public boolean equalsFilter(ItemsFilter filter) {
\r
2456 if (filter != null
\r
2457 && filter.patternMatcher.equalsPattern(this.patternMatcher)) {
\r
2464 * Checks whether the pattern's match rule is camel case.
\r
2466 * @return <code>true</code> if pattern's match rule is camel case,
\r
2467 * <code>false</code> otherwise
\r
2469 public boolean isCamelCasePattern() {
\r
2470 return patternMatcher.getMatchRule() == SearchPattern.RULE_CAMELCASE_MATCH;
\r
2474 * Returns the pattern string.
\r
2476 * @return pattern for this filter
\r
2478 * @see SearchPattern#getPattern()
\r
2480 public String getPattern() {
\r
2481 return patternMatcher.getPattern();
\r
2485 * Returns the rule to apply for matching keys.
\r
2487 * @return an implementation-specific match rule
\r
2489 * @see SearchPattern#getMatchRule() for match rules returned by the
\r
2490 * default implementation
\r
2492 public int getMatchRule() {
\r
2493 return patternMatcher.getMatchRule();
\r
2497 * Matches text with filter.
\r
2500 * the text to match with the filter
\r
2501 * @return <code>true</code> if text matches with filter pattern,
\r
2502 * <code>false</code> otherwise
\r
2504 protected boolean matches(String text) {
\r
2505 return patternMatcher.matches(text);
\r
2509 * General method for matching raw name pattern. Checks whether current
\r
2510 * pattern is prefix of name provided item.
\r
2514 * @return <code>true</code> if current pattern is a prefix of name
\r
2515 * provided item, <code>false</code> if item's name is shorter
\r
2516 * than prefix or sequences of characters don't match.
\r
2518 public boolean matchesRawNamePattern(Object item) {
\r
2519 String prefix = patternMatcher.getPattern();
\r
2520 String text = getElementName(item);
\r
2525 int textLength = text.length();
\r
2526 int prefixLength = prefix.length();
\r
2527 if (textLength < prefixLength) {
\r
2530 for (int i = prefixLength - 1; i >= 0; i--) {
\r
2531 if (Character.toLowerCase(prefix.charAt(i)) != Character
\r
2532 .toLowerCase(text.charAt(i)))
\r
2539 * Matches an item against filter conditions.
\r
2542 * @return <code>true<code> if item matches against filter conditions, <code>false</code>
\r
2545 public abstract boolean matchItem(Object item);
\r
2548 * Checks consistency of an item. Item is inconsistent if was changed or
\r
2552 * @return <code>true</code> if item is consistent, <code>false</code>
\r
2553 * if item is inconsistent
\r
2555 public abstract boolean isConsistentItem(Object item);
\r
2560 * An interface to content providers for
\r
2561 * <code>FilterItemsSelectionDialog</code>.
\r
2563 protected abstract class AbstractContentProvider {
\r
2565 * Adds the item to the content provider iff the filter matches the
\r
2566 * item. Otherwise does nothing.
\r
2570 * @param itemsFilter
\r
2573 * @see ColumnFilteredItemsSelectionDialog.ItemsFilter#matchItem(Object)
\r
2575 public abstract void add(Object item, ItemsFilter itemsFilter);
\r
2579 * Collects filtered elements. Contains one synchronized, sorted set for
\r
2580 * collecting filtered elements. All collected elements are sorted using
\r
2581 * comparator. Comparator is returned by getElementComparator() method.
\r
2582 * Implementation of <code>ItemsFilter</code> is used to filter elements.
\r
2583 * The key function of filter used in to filtering is
\r
2584 * <code>matchElement(Object item)</code>.
\r
2586 * The <code>ContentProvider</code> class also provides item filtering
\r
2587 * methods. The filtering has been moved from the standard TableView
\r
2588 * <code>getFilteredItems()</code> method to content provider, because
\r
2589 * <code>ILazyContentProvider</code> and virtual tables are used. This
\r
2590 * class is responsible for adding a separator below history items and
\r
2591 * marking each items as duplicate if its name repeats more than once on the
\r
2594 private class ContentProvider extends AbstractContentProvider implements
\r
2595 IStructuredContentProvider, ILazyContentProvider {
\r
2597 private SelectionHistory selectionHistory;
\r
2600 * Raw result of the searching (unsorted, unfiltered).
\r
2602 * Standard object flow:
\r
2603 * <code>items -> lastSortedItems -> lastFilteredItems</code>
\r
2605 private Set items;
\r
2608 * Items that are duplicates.
\r
2610 private Set duplicates;
\r
2613 * List of <code>ViewerFilter</code>s to be used during filtering
\r
2615 private List filters;
\r
2618 * Result of the last filtering.
\r
2620 * Standard object flow:
\r
2621 * <code>items -> lastSortedItems -> lastFilteredItems</code>
\r
2623 private List lastFilteredItems;
\r
2626 * Result of the last sorting.
\r
2628 * Standard object flow:
\r
2629 * <code>items -> lastSortedItems -> lastFilteredItems</code>
\r
2631 private List lastSortedItems;
\r
2634 * Used for <code>getFilteredItems()</code> method canceling (when the
\r
2635 * job that invoked the method was canceled).
\r
2637 * Method canceling could be based (only) on monitor canceling
\r
2638 * unfortunately sometimes the method <code>getFilteredElements()</code>
\r
2639 * could be run with a null monitor, the <code>reset</code> flag have
\r
2640 * to be left intact.
\r
2642 private boolean reset;
\r
2645 * Creates new instance of <code>ContentProvider</code>.
\r
2647 public ContentProvider() {
\r
2648 this.items = Collections.synchronizedSet(new HashSet(2048));
\r
2649 this.duplicates = Collections.synchronizedSet(new HashSet(256));
\r
2650 this.lastFilteredItems = new ArrayList();
\r
2651 this.lastSortedItems = Collections.synchronizedList(new ArrayList(2048));
\r
2655 * Sets selection history.
\r
2657 * @param selectionHistory
\r
2658 * The selectionHistory to set.
\r
2660 public void setSelectionHistory(SelectionHistory selectionHistory) {
\r
2661 this.selectionHistory = selectionHistory;
\r
2665 * @return Returns the selectionHistory.
\r
2667 public SelectionHistory getSelectionHistory() {
\r
2668 return selectionHistory;
\r
2672 * Removes all content items and resets progress message.
\r
2674 public void reset() {
\r
2676 this.items.clear();
\r
2677 this.duplicates.clear();
\r
2678 this.lastSortedItems.clear();
\r
2682 * Stops reloading cache - <code>getFilteredItems()</code> method.
\r
2684 public void stopReloadingCache() {
\r
2689 * Adds filtered item.
\r
2692 * @param itemsFilter
\r
2694 public void add(Object item, ItemsFilter itemsFilter) {
\r
2695 if (itemsFilter == filter) {
\r
2696 if (itemsFilter != null) {
\r
2697 if (itemsFilter.matchItem(item)) {
\r
2698 this.items.add(item);
\r
2701 this.items.add(item);
\r
2707 * Add all history items to <code>contentProvider</code>.
\r
2709 * @param itemsFilter
\r
2711 public void addHistoryItems(ItemsFilter itemsFilter) {
\r
2712 if (this.selectionHistory != null) {
\r
2713 Object[] items = this.selectionHistory.getHistoryItems();
\r
2714 for (int i = 0; i < items.length; i++) {
\r
2715 Object item = items[i];
\r
2716 if (itemsFilter == filter) {
\r
2717 if (itemsFilter != null) {
\r
2718 if (itemsFilter.matchItem(item)) {
\r
2719 if (itemsFilter.isConsistentItem(item)) {
\r
2720 this.items.add(item);
\r
2722 this.selectionHistory.remove(item);
\r
2734 public void refresh() {
\r
2735 scheduleRefresh();
\r
2739 * Removes items from history and refreshes the view.
\r
2744 * @return removed item
\r
2746 public Object removeHistoryElement(Object item) {
\r
2747 if (this.selectionHistory != null)
\r
2748 this.selectionHistory.remove(item);
\r
2749 if (filter == null || filter.getPattern().length() == 0) {
\r
2750 items.remove(item);
\r
2751 duplicates.remove(item);
\r
2752 this.lastSortedItems.remove(item);
\r
2755 synchronized (lastSortedItems) {
\r
2756 Collections.sort(lastSortedItems, getHistoryComparator());
\r
2762 * Adds item to history and refresh view.
\r
2767 public void addHistoryElement(Object item) {
\r
2768 if (this.selectionHistory != null)
\r
2769 this.selectionHistory.accessed(item);
\r
2770 if (filter == null || !filter.matchItem(item)) {
\r
2771 this.items.remove(item);
\r
2772 this.duplicates.remove(item);
\r
2773 this.lastSortedItems.remove(item);
\r
2775 synchronized (lastSortedItems) {
\r
2776 Collections.sort(lastSortedItems, getHistoryComparator());
\r
2783 * @return <code>true</code> if given item is part of the history
\r
2785 public boolean isHistoryElement(Object item) {
\r
2786 if (this.selectionHistory != null) {
\r
2787 return this.selectionHistory.contains(item);
\r
2793 * Sets/unsets given item as duplicate.
\r
2798 * @param isDuplicate
\r
2801 public void setDuplicateElement(Object item, boolean isDuplicate) {
\r
2802 if (this.items.contains(item)) {
\r
2804 this.duplicates.add(item);
\r
2806 this.duplicates.remove(item);
\r
2811 * Indicates whether given item is a duplicate.
\r
2815 * @return <code>true</code> if item is duplicate
\r
2817 public boolean isDuplicateElement(Object item) {
\r
2818 return duplicates.contains(item);
\r
2822 * Load history from memento.
\r
2825 * memento from which the history will be retrieved
\r
2827 public void loadHistory(IMemento memento) {
\r
2828 if (this.selectionHistory != null)
\r
2829 this.selectionHistory.load(memento);
\r
2833 * Save history to memento.
\r
2836 * memento to which the history will be added
\r
2838 public void saveHistory(IMemento memento) {
\r
2839 if (this.selectionHistory != null)
\r
2840 this.selectionHistory.save(memento);
\r
2844 * Gets sorted items.
\r
2846 * @return sorted items
\r
2848 private Object[] getSortedItems() {
\r
2849 if (lastSortedItems.size() != items.size()) {
\r
2850 synchronized (lastSortedItems) {
\r
2851 lastSortedItems.clear();
\r
2852 lastSortedItems.addAll(items);
\r
2853 Collections.sort(lastSortedItems, getHistoryComparator());
\r
2856 return lastSortedItems.toArray();
\r
2860 * Remember result of filtering.
\r
2862 * @param itemsFilter
\r
2864 public void rememberResult(ItemsFilter itemsFilter) {
\r
2865 List itemsList = Collections.synchronizedList(Arrays
\r
2866 .asList(getSortedItems()));
\r
2867 // synchronization
\r
2868 if (itemsFilter == filter) {
\r
2869 lastCompletedFilter = itemsFilter;
\r
2870 lastCompletedResult = itemsList;
\r
2878 * @see org.eclipse.jface.viewers.IStructuredContentProvider#getElements(java.lang.Object)
\r
2880 public Object[] getElements(Object inputElement) {
\r
2881 return lastFilteredItems.toArray();
\r
2884 public int getNumberOfElements() {
\r
2885 return lastFilteredItems.size();
\r
2891 * @see org.eclipse.jface.viewers.IContentProvider#dispose()
\r
2893 public void dispose() {
\r
2899 * @see org.eclipse.jface.viewers.IContentProvider#inputChanged(org.eclipse.jface.viewers.Viewer,
\r
2900 * java.lang.Object, java.lang.Object)
\r
2902 public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
\r
2908 * @see org.eclipse.jface.viewers.ILazyContentProvider#updateElement(int)
\r
2910 public void updateElement(int index) {
\r
2912 ColumnFilteredItemsSelectionDialog.this.viewer.replace((lastFilteredItems
\r
2913 .size() > index) ? lastFilteredItems.get(index) : null,
\r
2919 * Main method responsible for getting the filtered items and checking
\r
2920 * for duplicates. It is based on the
\r
2921 * {@link ColumnFilteredItemsSelectionDialog.ContentProvider#getFilteredItems(Object, IProgressMonitor)}.
\r
2923 * @param checkDuplicates
\r
2924 * <code>true</code> if data concerning elements
\r
2925 * duplication should be computed - it takes much more time
\r
2926 * than standard filtering
\r
2929 * progress monitor
\r
2931 public void reloadCache(boolean checkDuplicates,
\r
2932 IProgressMonitor monitor) {
\r
2936 if (monitor != null) {
\r
2937 // the work is divided into two actions of the same length
\r
2938 int totalWork = checkDuplicates ? 200 : 100;
\r
2940 monitor.beginTask(WorkbenchMessages.FilteredItemsSelectionDialog_cacheRefreshJob, totalWork);
\r
2943 // the TableViewer's root (the input) is treated as parent
\r
2945 lastFilteredItems = Arrays.asList(getFilteredItems(viewer.getInput(),
\r
2946 monitor != null ? new SubProgressMonitor(monitor, 100) : null));
\r
2948 if (reset || (monitor != null && monitor.isCanceled())) {
\r
2949 if (monitor != null)
\r
2954 if (checkDuplicates) {
\r
2955 checkDuplicates(monitor);
\r
2957 if (monitor != null)
\r
2961 private void checkDuplicates(IProgressMonitor monitor) {
\r
2962 synchronized (lastFilteredItems) {
\r
2963 IProgressMonitor subMonitor = null;
\r
2964 int reportEvery = lastFilteredItems.size() / 20;
\r
2965 if (monitor != null) {
\r
2966 subMonitor = new SubProgressMonitor(monitor, 100);
\r
2967 subMonitor.beginTask(WorkbenchMessages.FilteredItemsSelectionDialog_cacheRefreshJob_checkDuplicates, 5);
\r
2969 HashMap helperMap = new HashMap();
\r
2970 for (int i = 0; i < lastFilteredItems.size(); i++) {
\r
2971 if (reset || (subMonitor != null && subMonitor.isCanceled()))
\r
2973 Object item = lastFilteredItems.get(i);
\r
2975 if (!(item instanceof ItemsListSeparator)) {
\r
2976 Object previousItem = helperMap.put(getElementName(item), item);
\r
2977 if (previousItem != null) {
\r
2978 setDuplicateElement(previousItem, true);
\r
2979 setDuplicateElement(item, true);
\r
2981 setDuplicateElement(item, false);
\r
2985 if (subMonitor != null && reportEvery != 0
\r
2986 && (i + 1) % reportEvery == 0)
\r
2987 subMonitor.worked(1);
\r
2989 helperMap.clear();
\r
2994 * Returns an array of items filtered using the provided
\r
2995 * <code>ViewerFilter</code>s with a separator added.
\r
3000 * progress monitor, can be <code>null</code>
\r
3001 * @return an array of filtered items
\r
3003 protected Object[] getFilteredItems(Object parent,
\r
3004 IProgressMonitor monitor) {
\r
3006 if (monitor == null) {
\r
3007 monitor = new NullProgressMonitor();
\r
3010 monitor.beginTask(WorkbenchMessages.FilteredItemsSelectionDialog_cacheRefreshJob_getFilteredElements, ticks);
\r
3011 if (filters != null) {
\r
3012 ticks /= (filters.size() + 2);
\r
3017 // get already sorted array
\r
3018 Object[] filteredElements = getSortedItems();
\r
3020 monitor.worked(ticks);
\r
3022 // filter the elements using provided ViewerFilters
\r
3023 if (filters != null && filteredElements != null) {
\r
3024 for (Iterator iter = filters.iterator(); iter.hasNext();) {
\r
3025 ViewerFilter f = (ViewerFilter) iter.next();
\r
3026 filteredElements = f.filter(viewer, parent, filteredElements);
\r
3027 monitor.worked(ticks);
\r
3031 if (filteredElements == null || monitor.isCanceled()) {
\r
3033 return new Object[0];
\r
3036 ArrayList preparedElements = new ArrayList();
\r
3037 boolean hasHistory = false;
\r
3039 if (filteredElements.length > 0) {
\r
3040 if (isHistoryElement(filteredElements[0])) {
\r
3041 hasHistory = true;
\r
3045 int reportEvery = filteredElements.length / ticks;
\r
3048 for (int i = 0; i < filteredElements.length; i++) {
\r
3049 Object item = filteredElements[i];
\r
3051 if (hasHistory && !isHistoryElement(item)) {
\r
3052 preparedElements.add(itemsListSeparator);
\r
3053 hasHistory = false;
\r
3056 preparedElements.add(item);
\r
3058 if (reportEvery != 0 && ((i + 1) % reportEvery == 0)) {
\r
3059 monitor.worked(1);
\r
3065 return preparedElements.toArray();
\r
3069 * Adds a filter to this content provider. For an example usage of such
\r
3070 * filters look at the project <code>org.eclipse.ui.ide</code>, class
\r
3071 * <code>org.eclipse.ui.dialogs.FilteredResourcesSelectionDialog.CustomWorkingSetFilter</code>.
\r
3075 * the filter to be added
\r
3077 public void addFilter(ViewerFilter filter) {
\r
3078 if (filters == null) {
\r
3079 filters = new ArrayList();
\r
3081 filters.add(filter);
\r
3082 // currently filters are only added when dialog is restored
\r
3083 // if it is changed, refreshing the whole TableViewer should be
\r
3090 * A content provider that does nothing.
\r
3092 private class NullContentProvider implements IContentProvider {
\r
3097 * @see org.eclipse.jface.viewers.IContentProvider#dispose()
\r
3099 public void dispose() {
\r
3105 * @see org.eclipse.jface.viewers.IContentProvider#inputChanged(org.eclipse.jface.viewers.Viewer,
\r
3106 * java.lang.Object, java.lang.Object)
\r
3108 public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
\r
3114 * DetailsContentViewer objects are wrappers for labels.
\r
3115 * DetailsContentViewer provides means to change label's image and text when
\r
3116 * the attached LabelProvider is updated.
\r
3118 private class DetailsContentViewer extends ContentViewer {
\r
3120 private CLabel label;
\r
3123 * Unfortunately, it was impossible to delegate displaying border to
\r
3124 * label. The <code>ViewForm</code> is used because
\r
3125 * <code>CLabel</code> displays shadow when border is present.
\r
3127 private ViewForm viewForm;
\r
3130 * Constructs a new instance of this class given its parent and a style
\r
3131 * value describing its behavior and appearance.
\r
3134 * the parent component
\r
3138 public DetailsContentViewer(Composite parent, int style) {
\r
3139 viewForm = new ViewForm(parent, style);
\r
3140 GridData gd = new GridData(GridData.FILL_HORIZONTAL);
\r
3141 gd.horizontalSpan = 2;
\r
3142 viewForm.setLayoutData(gd);
\r
3143 label = new CLabel(viewForm, SWT.FLAT);
\r
3144 label.setFont(parent.getFont());
\r
3145 viewForm.setContent(label);
\r
3146 hookControl(label);
\r
3150 * Shows/hides the content viewer.
\r
3153 * if the content viewer should be visible.
\r
3155 public void setVisible(boolean visible) {
\r
3156 GridData gd = (GridData) viewForm.getLayoutData();
\r
3157 gd.exclude = !visible;
\r
3158 viewForm.getParent().layout();
\r
3164 * @see org.eclipse.jface.viewers.Viewer#inputChanged(java.lang.Object,
\r
3165 * java.lang.Object)
\r
3167 protected void inputChanged(Object input, Object oldInput) {
\r
3168 if (oldInput == null) {
\r
3169 if (input == null) {
\r
3183 * @see org.eclipse.jface.viewers.ContentViewer#handleLabelProviderChanged(org.eclipse.jface.viewers.LabelProviderChangedEvent)
\r
3185 protected void handleLabelProviderChanged(
\r
3186 LabelProviderChangedEvent event) {
\r
3187 if (event != null) {
\r
3188 refresh(event.getElements());
\r
3195 * @see org.eclipse.jface.viewers.Viewer#getControl()
\r
3197 public Control getControl() {
\r
3204 * @see org.eclipse.jface.viewers.Viewer#getSelection()
\r
3206 public ISelection getSelection() {
\r
3214 * @see org.eclipse.jface.viewers.Viewer#refresh()
\r
3216 public void refresh() {
\r
3217 Object input = this.getInput();
\r
3218 if (input != null) {
\r
3219 ILabelProvider labelProvider = (ILabelProvider) getLabelProvider();
\r
3220 doRefresh(labelProvider.getText(input), labelProvider
\r
3221 .getImage(input));
\r
3223 doRefresh(null, null);
\r
3228 * Sets the given text and image to the label.
\r
3231 * the new text or null
\r
3235 private void doRefresh(String text, Image image) {
\r
3236 if ( text != null ) {
\r
3237 text = LegacyActionTools.escapeMnemonics(text);
\r
3239 label.setText(text);
\r
3240 label.setImage(image);
\r
3246 * @see org.eclipse.jface.viewers.Viewer#setSelection(org.eclipse.jface.viewers.ISelection,
\r
3249 public void setSelection(ISelection selection, boolean reveal) {
\r
3254 * Refreshes the label if currently chosen element is on the list.
\r
3257 * list of changed object
\r
3259 private void refresh(Object[] objs) {
\r
3260 if (objs == null || getInput() == null) {
\r
3263 Object input = getInput();
\r
3264 for (int i = 0; i < objs.length; i++) {
\r
3265 if (objs[i].equals(input)) {
\r
3274 * Compares items according to the history.
\r
3276 private class HistoryComparator implements Comparator {
\r
3281 * @see java.util.Comparator#compare(java.lang.Object, java.lang.Object)
\r
3283 public int compare(Object o1, Object o2) {
\r
3284 boolean h1 = isHistoryElement(o1);
\r
3285 boolean h2 = isHistoryElement(o2);
\r
3287 return getItemsComparator().compare(o1, o2);
\r
3301 * Get the control where the search pattern is entered. Any filtering should
\r
3302 * be done using an {@link ItemsFilter}. This control should only be
\r
3303 * accessed for listeners that wish to handle events that do not affect
\r
3304 * filtering such as custom traversal.
\r
3306 * @return Control or <code>null</code> if the pattern control has not
\r
3309 public Control getPatternControl() {
\r