]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.utils.ui/src/org/simantics/utils/ui/dialogs/ColumnFilteredItemsSelectionDialog.java
Sync git svn branch with SVN repository r33269.
[simantics/platform.git] / bundles / org.simantics.utils.ui / src / org / simantics / utils / ui / dialogs / ColumnFilteredItemsSelectionDialog.java
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
7  *\r
8  * Contributors:\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
17 \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
31 \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
133 \r
134 /**\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
137  * \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
143  * \r
144  * TODO: Clean up warnings.\r
145  * \r
146  * @author Janne Kauttio (modifications only)\r
147  * \r
148  * @since 3.3\r
149  */\r
150 @SuppressWarnings({ "restriction", "rawtypes", "unchecked" })\r
151 public abstract class ColumnFilteredItemsSelectionDialog extends\r
152                 SelectionStatusDialog {\r
153 \r
154         private static final String DIALOG_BOUNDS_SETTINGS = "DialogBoundsSettings"; //$NON-NLS-1$\r
155 \r
156         private static final String SHOW_STATUS_LINE = "ShowStatusLine"; //$NON-NLS-1$\r
157 \r
158         private static final String HISTORY_SETTINGS = "History"; //$NON-NLS-1$\r
159 \r
160         private static final String DIALOG_HEIGHT = "DIALOG_HEIGHT"; //$NON-NLS-1$\r
161 \r
162         private static final String DIALOG_WIDTH = "DIALOG_WIDTH"; //$NON-NLS-1$\r
163 \r
164         /**\r
165          * Represents an empty selection in the pattern input field (used only for\r
166          * initial pattern).\r
167          */\r
168         public static final int NONE = 0;\r
169 \r
170         /**\r
171          * Pattern input field selection where caret is at the beginning (used only\r
172          * for initial pattern).\r
173          */\r
174         public static final int CARET_BEGINNING = 1;\r
175 \r
176         /**\r
177          * Represents a full selection in the pattern input field (used only for\r
178          * initial pattern).\r
179          */\r
180         public static final int FULL_SELECTION = 2;\r
181 \r
182         private Text pattern;\r
183 \r
184         private TableViewer viewer;\r
185 \r
186         private DetailsContentViewer details;\r
187 \r
188         /**\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
192          */\r
193         private ILabelProvider detailsLabelProvider;\r
194 \r
195         private ItemsListLabelProvider itemsListLabelProvider;\r
196 \r
197         private MenuManager menuManager;\r
198 \r
199         private MenuManager contextMenuManager;\r
200 \r
201         private boolean multi;\r
202 \r
203         private ToolBar toolBar;\r
204 \r
205         private ToolItem toolItem;\r
206 \r
207         private Label progressLabel;\r
208 \r
209         private ToggleStatusLineAction toggleStatusLineAction;\r
210 \r
211         private RemoveHistoryItemAction removeHistoryItemAction;\r
212 \r
213         private ActionContributionItem removeHistoryActionContributionItem;\r
214 \r
215         private IStatus status;\r
216 \r
217         private RefreshCacheJob refreshCacheJob;\r
218 \r
219         private RefreshProgressMessageJob refreshProgressMessageJob = new RefreshProgressMessageJob();\r
220 \r
221         private Object[] currentSelection;\r
222 \r
223         private ContentProvider contentProvider;\r
224 \r
225         private FilterHistoryJob filterHistoryJob;\r
226 \r
227         private FilterJob filterJob;\r
228 \r
229         private ItemsFilter filter;\r
230 \r
231         private List lastCompletedResult;\r
232 \r
233         private ItemsFilter lastCompletedFilter;\r
234 \r
235         private String initialPatternText;\r
236 \r
237         private int selectionMode;\r
238 \r
239         private ItemsListSeparator itemsListSeparator;\r
240 \r
241         private static final String EMPTY_STRING = ""; //$NON-NLS-1$\r
242 \r
243         private boolean refreshWithLastSelection = false;\r
244 \r
245         private IHandlerActivation showViewHandler;\r
246 \r
247         /**\r
248          * Creates a new instance of the class.\r
249          * \r
250          * @param shell\r
251          *            shell to parent the dialog on\r
252          * @param multi\r
253          *            indicates whether dialog allows to select more than one\r
254          *            position in its list of items\r
255          */\r
256         public ColumnFilteredItemsSelectionDialog(Shell shell, boolean multi) {\r
257                 super(shell);\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
265         }\r
266 \r
267         /**\r
268          * Creates a new instance of the class. Created dialog won't allow to select\r
269          * more than one item.\r
270          * \r
271          * @param shell\r
272          *            shell to parent the dialog on\r
273          */\r
274         public ColumnFilteredItemsSelectionDialog(Shell shell) {\r
275                 this(shell, false);\r
276         }\r
277 \r
278         /**\r
279          * Adds viewer filter to the dialog items list.\r
280          * \r
281          * @param filter\r
282          *            the new filter\r
283          */\r
284         protected void addListFilter(ViewerFilter filter) {\r
285                 contentProvider.addFilter(filter);\r
286         }\r
287 \r
288         /**\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
294          * \r
295          * @see IWorkbenchPreferenceConstants#USE_COLORED_LABELS\r
296          * \r
297          * @param listLabelProvider\r
298          *              the label provider for items in the list\r
299          */\r
300         public void setListLabelProvider(ILabelProvider listLabelProvider) {\r
301                 getItemsListLabelProvider().setProvider(listLabelProvider);\r
302         }\r
303 \r
304         /**\r
305          * Returns the label decorator for selected items in the list.\r
306          * \r
307          * @return the label decorator for selected items in the list\r
308          */\r
309         private ILabelDecorator getListSelectionLabelDecorator() {\r
310                 return getItemsListLabelProvider().getSelectionDecorator();\r
311         }\r
312 \r
313         /**\r
314          * Sets the label decorator for selected items in the list.\r
315          * \r
316          * @param listSelectionLabelDecorator\r
317          *            the label decorator for selected items in the list\r
318          */\r
319         public void setListSelectionLabelDecorator(\r
320                         ILabelDecorator listSelectionLabelDecorator) {\r
321                 getItemsListLabelProvider().setSelectionDecorator(\r
322                                 listSelectionLabelDecorator);\r
323         }\r
324 \r
325         /**\r
326          * Returns the item list label provider.\r
327          * \r
328          * @return the item list label provider\r
329          */\r
330         private ItemsListLabelProvider getItemsListLabelProvider() {\r
331                 if (itemsListLabelProvider == null) {\r
332                         itemsListLabelProvider = new ItemsListLabelProvider(\r
333                                         new LabelProvider(), null);\r
334                 }\r
335                 return itemsListLabelProvider;\r
336         }\r
337 \r
338         /**\r
339          * Sets label provider for the details field.\r
340          * \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
345          * element.\r
346          * \r
347          * @see #getSelectedItems() getSelectedItems() can be used to retrieve\r
348          *      selected items and get the items count.\r
349          * \r
350          * @param detailsLabelProvider\r
351          *            the label provider for the details field\r
352          */\r
353         public void setDetailsLabelProvider(ILabelProvider detailsLabelProvider) {\r
354                 this.detailsLabelProvider = detailsLabelProvider;\r
355                 if (details != null) {\r
356                         details.setLabelProvider(detailsLabelProvider);\r
357                 }\r
358         }\r
359 \r
360         private ILabelProvider getDetailsLabelProvider() {\r
361                 if (detailsLabelProvider == null) {\r
362                         detailsLabelProvider = new LabelProvider();\r
363                 }\r
364                 return detailsLabelProvider;\r
365         }\r
366 \r
367         /*\r
368          * (non-Javadoc)\r
369          * \r
370          * @see org.eclipse.jface.window.Window#create()\r
371          */\r
372         public void create() {\r
373                 super.create();\r
374                 pattern.setFocus();\r
375         }\r
376 \r
377         /**\r
378          * Restores dialog using persisted settings. The default implementation\r
379          * restores the status of the details line and the selection history.\r
380          * \r
381          * @param settings\r
382          *            settings used to restore dialog\r
383          */\r
384         protected void restoreDialog(IDialogSettings settings) {\r
385                 boolean toggleStatusLine = true;\r
386 \r
387                 if (settings.get(SHOW_STATUS_LINE) != null) {\r
388                         toggleStatusLine = settings.getBoolean(SHOW_STATUS_LINE);\r
389                 }\r
390 \r
391                 toggleStatusLineAction.setChecked(toggleStatusLine);\r
392 \r
393                 details.setVisible(toggleStatusLine);\r
394 \r
395                 String setting = settings.get(HISTORY_SETTINGS);\r
396                 if (setting != null) {\r
397                         try {\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
403                                                 IStatus.ERROR,\r
404                                                 PlatformUI.PLUGIN_ID,\r
405                                                 IStatus.ERROR,\r
406                                                 WorkbenchMessages.FilteredItemsSelectionDialog_restoreError,\r
407                                                 e));\r
408                         }\r
409                 }\r
410         }\r
411 \r
412         /*\r
413          * (non-Javadoc)\r
414          * \r
415          * @see org.eclipse.jface.window.Window#close()\r
416          */\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
426                 }\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
433         }\r
434 \r
435         /**\r
436          * Stores dialog settings.\r
437          * \r
438          * @param settings\r
439          *            settings used to store dialog\r
440          */\r
441         protected void storeDialog(IDialogSettings settings) {\r
442                 settings.put(SHOW_STATUS_LINE, toggleStatusLineAction.isChecked());\r
443 \r
444                 XMLMemento memento = XMLMemento.createWriteRoot(HISTORY_SETTINGS);\r
445                 this.contentProvider.saveHistory(memento);\r
446                 StringWriter writer = new StringWriter();\r
447                 try {\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
453                                         IStatus.ERROR,\r
454                                         PlatformUI.PLUGIN_ID,\r
455                                         IStatus.ERROR,\r
456                                         WorkbenchMessages.FilteredItemsSelectionDialog_storeError,\r
457                                         e));\r
458                 }\r
459         }\r
460 \r
461         /**\r
462          * Create a new header which is labelled by headerLabel.\r
463          * \r
464          * @param parent\r
465          * @return Label the label of the header\r
466          */\r
467         private Label createHeader(Composite parent) {\r
468                 Composite header = new Composite(parent, SWT.NONE);\r
469 \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
475 \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
484                                 }\r
485                         }\r
486                 });\r
487 \r
488                 GridData gd = new GridData(GridData.FILL_HORIZONTAL);\r
489                 headerLabel.setLayoutData(gd);\r
490 \r
491                 createViewMenu(header);\r
492                 header.setLayoutData(gd);\r
493                 return headerLabel;\r
494         }\r
495 \r
496         /**\r
497          * Create the labels for the list and the progress. Return the list label.\r
498          * \r
499          * @param parent\r
500          * @return Label\r
501          */\r
502         private Label createLabels(Composite parent) {\r
503                 Composite labels = new Composite(parent, SWT.NONE);\r
504 \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
510 \r
511                 Label listLabel = new Label(labels, SWT.NONE);\r
512                 listLabel.setText(WorkbenchMessages.FilteredItemsSelectionDialog_listLabel);\r
513 \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
519                                 }\r
520                         }\r
521                 });\r
522 \r
523                 GridData gd = new GridData(GridData.FILL_HORIZONTAL);\r
524                 listLabel.setLayoutData(gd);\r
525 \r
526                 progressLabel = new Label(labels, SWT.RIGHT);\r
527                 progressLabel.setLayoutData(gd);\r
528 \r
529                 labels.setLayoutData(gd);\r
530                 return listLabel;\r
531         }\r
532 \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
536 \r
537                 GridData data = new GridData();\r
538                 data.horizontalAlignment = GridData.END;\r
539                 toolBar.setLayoutData(data);\r
540 \r
541                 toolBar.addMouseListener(new MouseAdapter() {\r
542                         public void mouseDown(MouseEvent e) {\r
543                                 showViewMenu();\r
544                         }\r
545                 });\r
546 \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
551                                 showViewMenu();\r
552                         }\r
553                 });\r
554 \r
555                 menuManager = new MenuManager();\r
556 \r
557                 fillViewMenu(menuManager);\r
558 \r
559                 IHandlerService service = (IHandlerService) PlatformUI.getWorkbench().getService(IHandlerService.class);\r
560                 IHandler handler = new AbstractHandler() {\r
561                         public Object execute(ExecutionEvent event) {\r
562                                 showViewMenu();\r
563                                 return null;\r
564                         }\r
565                 };\r
566                 showViewHandler = service.activateHandler(\r
567                                 IWorkbenchCommandConstants.WINDOW_SHOW_VIEW_MENU, handler,\r
568                                 new ActiveShellExpression(getShell()));\r
569         }\r
570 \r
571         /**\r
572          * Fills the menu of the dialog.\r
573          * \r
574          * @param menuManager\r
575          *            the menu manager\r
576          */\r
577         protected void fillViewMenu(IMenuManager menuManager) {\r
578                 toggleStatusLineAction = new ToggleStatusLineAction();\r
579                 menuManager.add(toggleStatusLineAction);\r
580         }\r
581 \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
589         }\r
590 \r
591     /**\r
592      * Hook that allows to add actions to the context menu.\r
593          * <p>\r
594          * Subclasses may extend in order to add other actions.</p>\r
595      * \r
596      * @param menuManager the context menu manager\r
597      * @since 3.5\r
598      */\r
599         protected void fillContextMenu(IMenuManager menuManager) {\r
600                 List selectedElements= ((StructuredSelection)viewer.getSelection()).toList();\r
601 \r
602                 Object item= null;\r
603 \r
604                 for (Iterator it= selectedElements.iterator(); it.hasNext();) {\r
605                         item= it.next();\r
606                         if (item instanceof ItemsListSeparator || !isHistoryElement(item)) {\r
607                                 return;\r
608                         }\r
609                 }\r
610 \r
611                 if (selectedElements.size() > 0) {\r
612                         removeHistoryItemAction.setText(WorkbenchMessages.FilteredItemsSelectionDialog_removeItemsFromHistoryAction);\r
613 \r
614                         menuManager.add(removeHistoryActionContributionItem);\r
615 \r
616                 }\r
617         }\r
618 \r
619         private void createPopupMenu() {\r
620                 removeHistoryItemAction = new RemoveHistoryItemAction();\r
621                 removeHistoryActionContributionItem = new ActionContributionItem(removeHistoryItemAction);\r
622 \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
628                         }\r
629                 });\r
630 \r
631                 final Table table = viewer.getTable();\r
632                 Menu menu= contextMenuManager.createContextMenu(table);\r
633                 table.setMenu(menu);\r
634         }\r
635 \r
636         /**\r
637          * Creates an extra content area, which will be located above the details.\r
638          * \r
639          * @param parent\r
640          *            parent to create the dialog widgets in\r
641          * @return an extra content area\r
642          */\r
643         protected abstract Control createExtendedContentArea(Composite parent);\r
644 \r
645         /*\r
646          * (non-Javadoc)\r
647          * \r
648          * @see org.eclipse.jface.dialogs.Dialog#createDialogArea(org.eclipse.swt.widgets.Composite)\r
649          */\r
650         protected Control createDialogArea(Composite parent) {\r
651                 Composite dialogArea = (Composite) super.createDialogArea(parent);\r
652 \r
653                 Composite content = new Composite(dialogArea, SWT.NONE);\r
654                 GridData gd = new GridData(GridData.FILL_BOTH);\r
655                 content.setLayoutData(gd);\r
656 \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
662 \r
663                 final Label headerLabel = createHeader(content);\r
664 \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
669                         }\r
670                 });\r
671                 gd = new GridData(GridData.FILL_HORIZONTAL);\r
672                 pattern.setLayoutData(gd);\r
673 \r
674                 final Label listLabel = createLabels(content);\r
675 \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
683                                                 }\r
684                                         }\r
685                                 });\r
686                 viewer.setContentProvider(contentProvider);\r
687                 \r
688                 // added column creation hook\r
689                 createColumns(viewer);\r
690                 \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
695                 }\r
696                 \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
703 \r
704                 createPopupMenu();\r
705 \r
706                 pattern.addModifyListener(new ModifyListener() {\r
707                         public void modifyText(ModifyEvent e) {\r
708                                 applyFilter();\r
709                         }\r
710                 });\r
711 \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
717                                         }\r
718                                 }\r
719                         }\r
720                 });\r
721 \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
726                         }\r
727                 });\r
728 \r
729                 viewer.addDoubleClickListener(new IDoubleClickListener() {\r
730                         public void doubleClick(DoubleClickEvent event) {\r
731                                 handleDoubleClick();\r
732                         }\r
733                 });\r
734 \r
735                 viewer.getTable().addKeyListener(new KeyAdapter() {\r
736                         public void keyPressed(KeyEvent e) {\r
737 \r
738                                 if (e.keyCode == SWT.DEL) {\r
739 \r
740                                         List selectedElements = ((StructuredSelection) viewer.getSelection()).toList();\r
741 \r
742                                         Object item = null;\r
743                                         boolean isSelectedHistory = true;\r
744 \r
745                                         for (Iterator it = selectedElements.iterator(); it.hasNext();) {\r
746                                                 item = it.next();\r
747                                                 if (item instanceof ItemsListSeparator || !isHistoryElement(item)) {\r
748                                                         isSelectedHistory = false;\r
749                                                         break;\r
750                                                 }\r
751                                         }\r
752                                         if (isSelectedHistory)\r
753                                                 removeSelectedItems(selectedElements);\r
754 \r
755                                 }\r
756 \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
760 \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
765                                                 }\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
769 \r
770                                         }\r
771                                 }\r
772 \r
773                                 if (e.keyCode == SWT.ARROW_DOWN\r
774                                                 && (e.stateMask & SWT.SHIFT) != 0\r
775                                                 && (e.stateMask & SWT.CTRL) != 0) {\r
776 \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
780                                 }\r
781                         }\r
782                 });\r
783 \r
784                 createExtendedContentArea(content);\r
785 \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
790 \r
791                 applyDialogFont(content);\r
792 \r
793                 restoreDialog(getDialogSettings());\r
794 \r
795                 if (initialPatternText != null) {\r
796                         pattern.setText(initialPatternText);\r
797                 }\r
798 \r
799                 switch (selectionMode) {\r
800                 case CARET_BEGINNING:\r
801                         pattern.setSelection(0, 0);\r
802                         break;\r
803                 case FULL_SELECTION:\r
804                         pattern.setSelection(0, initialPatternText.length());\r
805                         break;\r
806                 }\r
807 \r
808                 // apply filter even if pattern is empty (display history)\r
809                 applyFilter();\r
810 \r
811                 return dialogArea;\r
812         }\r
813         \r
814         /**\r
815          * Override this method to add columns to the content area of this dialog.\r
816          * \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
819          * \r
820          * @param viewer\r
821          */\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
825         }\r
826         \r
827         /**\r
828          * An utility method for adding a column to the TableViewer, should be called\r
829          * from inside createColumns.\r
830          * \r
831          * @param viewer\r
832          * @param label\r
833          * @param width\r
834          * @param labelProvider\r
835          */\r
836         protected void createColumn(TableViewer viewer, String label, int width, ColumnLabelProvider labelProvider) {\r
837                 TableViewerColumn viewercol = new TableViewerColumn(viewer, SWT.LEFT);\r
838                 \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
844                 \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
848         }\r
849 \r
850         /**\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
854          * <p>\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
857          */\r
858         protected void handleDoubleClick() {\r
859                 okPressed();\r
860         }\r
861 \r
862         /**\r
863          * Refreshes the details field according to the current selection in the\r
864          * items list.\r
865          */\r
866         private void refreshDetails() {\r
867                 StructuredSelection selection = getSelectedItems();\r
868 \r
869                 switch (selection.size()) {\r
870                 case 0:\r
871                         details.setInput(null);\r
872                         break;\r
873                 case 1:\r
874                         details.setInput(selection.getFirstElement());\r
875                         break;\r
876                 default:\r
877                         details.setInput(NLS.bind(\r
878                                         WorkbenchMessages.FilteredItemsSelectionDialog_nItemsSelected,\r
879                                         new Integer(selection.size())));\r
880                         break;\r
881                 }\r
882 \r
883         }\r
884 \r
885         /**\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
888          * \r
889          * @param selection\r
890          *            the new selection\r
891          */\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
895 \r
896                 Object[] lastSelection = currentSelection;\r
897 \r
898                 currentSelection = selection.toArray();\r
899 \r
900                 if (selection.size() == 0) {\r
901                         status = new Status(IStatus.ERROR, PlatformUI.PLUGIN_ID,\r
902                                         IStatus.ERROR, EMPTY_STRING, null);\r
903 \r
904                         if (lastSelection != null && getListSelectionLabelDecorator() != null) {\r
905                                 viewer.update(lastSelection, null);\r
906                         }\r
907 \r
908                         currentSelection = null;\r
909 \r
910                 } else {\r
911                         status = new Status(IStatus.ERROR, PlatformUI.PLUGIN_ID,\r
912                                         IStatus.ERROR, EMPTY_STRING, null);\r
913 \r
914                         List items = selection.toList();\r
915 \r
916                         Object item = null;\r
917                         IStatus tempStatus = null;\r
918 \r
919                         for (Iterator it = items.iterator(); it.hasNext();) {\r
920                                 Object o = it.next();\r
921 \r
922                                 if (o instanceof ItemsListSeparator) {\r
923                                         continue;\r
924                                 }\r
925 \r
926                                 item = o;\r
927                                 tempStatus = validateItem(item);\r
928 \r
929                                 if (tempStatus.isOK()) {\r
930                                         status = new Status(IStatus.OK, PlatformUI.PLUGIN_ID,\r
931                                                         IStatus.OK, EMPTY_STRING, null);\r
932                                 } else {\r
933                                         status = tempStatus;\r
934                                         // if any selected element is not valid status is set to\r
935                                         // ERROR\r
936                                         break;\r
937                                 }\r
938                         }\r
939 \r
940                         if (lastSelection != null && getListSelectionLabelDecorator() != null) {\r
941                                 viewer.update(lastSelection, null);\r
942                         }\r
943 \r
944                         if (getListSelectionLabelDecorator() != null) {\r
945                                 viewer.update(currentSelection, null);\r
946                         }\r
947                 }\r
948 \r
949                 refreshDetails();\r
950                 updateStatus(status);\r
951         }\r
952 \r
953         /*\r
954          * (non-Javadoc)\r
955          * \r
956          * @see org.eclipse.jface.window.Dialog#getDialogBoundsSettings()\r
957          */\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
965                 }\r
966                 return section;\r
967         }\r
968 \r
969         /**\r
970          * Returns the dialog settings. Returned object can't be null.\r
971          * \r
972          * @return return dialog settings for this dialog\r
973          */\r
974         protected abstract IDialogSettings getDialogSettings();\r
975 \r
976         /**\r
977          * Refreshes the dialog - has to be called in UI thread.\r
978          */\r
979         public void refresh() {\r
980                 if (viewer != null && !viewer.getTable().isDisposed()) {\r
981 \r
982                         List lastRefreshSelection = ((StructuredSelection) viewer.getSelection()).toList();\r
983                         viewer.getTable().deselectAll();\r
984 \r
985                         viewer.setItemCount(contentProvider.getNumberOfElements());\r
986                         viewer.refresh();\r
987 \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
994                                 } else {\r
995                                         refreshWithLastSelection = true;\r
996                                         viewer.getTable().setSelection(0);\r
997                                         viewer.getTable().notifyListeners(SWT.Selection, new Event());\r
998                                 }\r
999                         } else {\r
1000                                 viewer.setSelection(StructuredSelection.EMPTY);\r
1001                         }\r
1002 \r
1003                 }\r
1004 \r
1005                 scheduleProgressMessageRefresh();\r
1006         }\r
1007 \r
1008         /**\r
1009          * Updates the progress label.\r
1010          * \r
1011          * @deprecated\r
1012          */\r
1013         public void updateProgressLabel() {\r
1014                 scheduleProgressMessageRefresh();\r
1015         }\r
1016 \r
1017         /**\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
1021          * <p>\r
1022          * This is a long running operation and should be called in a job.\r
1023          * \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
1027          *            filtering\r
1028          * @param monitor\r
1029          *            a progress monitor or <code>null</code> if no monitor is\r
1030          *            available\r
1031          */\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
1035                 }\r
1036         }\r
1037 \r
1038         /**\r
1039          * Schedule refresh job.\r
1040          */\r
1041         public void scheduleRefresh() {\r
1042                 refreshCacheJob.cancelAll();\r
1043                 refreshCacheJob.schedule();\r
1044         }\r
1045 \r
1046         /**\r
1047          * Schedules progress message refresh.\r
1048          */\r
1049         public void scheduleProgressMessageRefresh() {\r
1050                 if (filterJob.getState() != Job.RUNNING && refreshProgressMessageJob.getState() != Job.RUNNING)\r
1051                         refreshProgressMessageJob.scheduleProgressRefresh(null);\r
1052         }\r
1053 \r
1054         /*\r
1055          * (non-Javadoc)\r
1056          * \r
1057          * @see org.eclipse.ui.dialogs.SelectionStatusDialog#computeResult()\r
1058          */\r
1059         protected void computeResult() {\r
1060 \r
1061                 List selectedElements = ((StructuredSelection) viewer.getSelection())\r
1062                                 .toList();\r
1063 \r
1064                 List objectsToReturn = new ArrayList();\r
1065 \r
1066                 Object item = null;\r
1067 \r
1068                 for (Iterator it = selectedElements.iterator(); it.hasNext();) {\r
1069                         item = it.next();\r
1070 \r
1071                         if (!(item instanceof ItemsListSeparator)) {\r
1072                                 accessedHistoryItem(item);\r
1073                                 objectsToReturn.add(item);\r
1074                         }\r
1075                 }\r
1076 \r
1077                 setResult(objectsToReturn);\r
1078         }\r
1079 \r
1080         /*\r
1081          * @see org.eclipse.ui.dialogs.SelectionStatusDialog#updateStatus(org.eclipse.core.runtime.IStatus)\r
1082          */\r
1083         protected void updateStatus(IStatus status) {\r
1084                 this.status = status;\r
1085                 super.updateStatus(status);\r
1086         }\r
1087 \r
1088         /*\r
1089          * @see Dialog#okPressed()\r
1090          */\r
1091         protected void okPressed() {\r
1092                 if (status != null && (status.isOK() || status.getCode() == IStatus.INFO)) {\r
1093                         super.okPressed();\r
1094                 }\r
1095         }\r
1096 \r
1097         /**\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
1100          * input field.\r
1101          * \r
1102          * @param text\r
1103          *            initial pattern for the filter\r
1104          * @see ColumnFilteredItemsSelectionDialog#FULL_SELECTION\r
1105          */\r
1106         public void setInitialPattern(String text) {\r
1107                 setInitialPattern(text, FULL_SELECTION);\r
1108         }\r
1109 \r
1110         /**\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
1114          * \r
1115          * @param text\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
1121          */\r
1122         public void setInitialPattern(String text, int selectionMode) {\r
1123                 this.initialPatternText = text;\r
1124                 this.selectionMode = selectionMode;\r
1125         }\r
1126 \r
1127         /**\r
1128          * Gets initial pattern.\r
1129          * \r
1130          * @return initial pattern, or <code>null</code> if initial pattern is not\r
1131          *         set\r
1132          */\r
1133         protected String getInitialPattern() {\r
1134                 return this.initialPatternText;\r
1135         }\r
1136 \r
1137         /**\r
1138          * Returns the current selection.\r
1139          * \r
1140          * @return the current selection\r
1141          */\r
1142         protected StructuredSelection getSelectedItems() {\r
1143 \r
1144                 StructuredSelection selection = (StructuredSelection) viewer.getSelection();\r
1145 \r
1146                 List selectedItems = selection.toList();\r
1147                 Object itemToRemove = null;\r
1148 \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
1153                                 break;\r
1154                         }\r
1155                 }\r
1156 \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
1163 \r
1164         }\r
1165 \r
1166         /**\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
1170          * \r
1171          * @param item\r
1172          *            an item to be checked\r
1173          * @return status of the dialog to be set\r
1174          */\r
1175         protected abstract IStatus validateItem(Object item);\r
1176 \r
1177         /**\r
1178          * Creates an instance of a filter.\r
1179          * \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
1182          *         the list.\r
1183          */\r
1184         protected abstract ItemsFilter createFilter();\r
1185 \r
1186         /**\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
1189          * refiltering.\r
1190          */\r
1191         protected void applyFilter() {\r
1192                 ItemsFilter newFilter = createFilter();\r
1193 \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
1197                         return;\r
1198                 }\r
1199                 \r
1200                 filterHistoryJob.cancel();\r
1201                 filterJob.cancel();\r
1202 \r
1203                 this.filter = newFilter;\r
1204                 \r
1205                 if (this.filter != null) {\r
1206                         filterHistoryJob.schedule();\r
1207                 }\r
1208                 \r
1209         }\r
1210 \r
1211         /**\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
1216          * \r
1217          * @return comparator to sort items content provider\r
1218          */\r
1219         protected abstract Comparator getItemsComparator();\r
1220 \r
1221         /**\r
1222          * Fills the content provider with matching items.\r
1223          * \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
1234          */\r
1235         protected abstract void fillContentProvider(\r
1236                         AbstractContentProvider contentProvider, ItemsFilter itemsFilter,\r
1237                         IProgressMonitor progressMonitor) throws CoreException;\r
1238         \r
1239         /**\r
1240          * Force a refresh of the content provider.\r
1241          */\r
1242         protected void forceRefresh() {\r
1243                 lastCompletedFilter = null;\r
1244                 lastCompletedResult = null;\r
1245                 filterHistoryJob.schedule();\r
1246         }\r
1247 \r
1248         /**\r
1249          * Removes selected items from history.\r
1250          * \r
1251          * @param items\r
1252          *            items to be removed\r
1253          */\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
1258                 }\r
1259                 refreshWithLastSelection = false;\r
1260                 contentProvider.refresh();\r
1261         }\r
1262 \r
1263         /**\r
1264          * Removes an item from history.\r
1265          * \r
1266          * @param item\r
1267          *            an item to remove\r
1268          * @return removed item\r
1269          */\r
1270         protected Object removeHistoryItem(Object item) {\r
1271                 return contentProvider.removeHistoryElement(item);\r
1272         }\r
1273 \r
1274         /**\r
1275          * Adds item to history.\r
1276          * \r
1277          * @param item\r
1278          *            the item to be added\r
1279          */\r
1280         protected void accessedHistoryItem(Object item) {\r
1281                 contentProvider.addHistoryElement(item);\r
1282         }\r
1283 \r
1284         /**\r
1285          * Returns a history comparator.\r
1286          * \r
1287          * @return decorated comparator\r
1288          */\r
1289         private Comparator getHistoryComparator() {\r
1290                 return new HistoryComparator();\r
1291         }\r
1292 \r
1293         /**\r
1294          * Returns the history of selected elements.\r
1295          * \r
1296          * @return history of selected elements, or <code>null</code> if it is not\r
1297          *         set\r
1298          */\r
1299         protected SelectionHistory getSelectionHistory() {\r
1300                 return this.contentProvider.getSelectionHistory();\r
1301         }\r
1302 \r
1303         /**\r
1304          * Sets new history.\r
1305          * \r
1306          * @param selectionHistory\r
1307          *            the history\r
1308          */\r
1309         protected void setSelectionHistory(SelectionHistory selectionHistory) {\r
1310                 if (this.contentProvider != null)\r
1311                         this.contentProvider.setSelectionHistory(selectionHistory);\r
1312         }\r
1313 \r
1314         /**\r
1315          * Indicates whether the given item is a history item.\r
1316          * \r
1317          * @param 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
1321          */\r
1322         public boolean isHistoryElement(Object item) {\r
1323                 return this.contentProvider.isHistoryElement(item);\r
1324         }\r
1325 \r
1326         /**\r
1327          * Indicates whether the given item is a duplicate.\r
1328          * \r
1329          * @param item\r
1330          *            the item to be investigated\r
1331          * @return <code>true</code> if the item is duplicate, <code>false</code>\r
1332          *         otherwise\r
1333          */\r
1334         public boolean isDuplicateElement(Object item) {\r
1335                 return this.contentProvider.isDuplicateElement(item);\r
1336         }\r
1337 \r
1338         /**\r
1339          * Sets separator label\r
1340          * \r
1341          * @param separatorLabel\r
1342          *            the label showed on separator\r
1343          */\r
1344         public void setSeparatorLabel(String separatorLabel) {\r
1345                 this.itemsListSeparator = new ItemsListSeparator(separatorLabel);\r
1346         }\r
1347 \r
1348         /**\r
1349          * Returns name for then given object.\r
1350          * \r
1351          * @param item\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
1358          */\r
1359         public abstract String getElementName(Object item);\r
1360 \r
1361         private class ToggleStatusLineAction extends Action {\r
1362 \r
1363                 /**\r
1364                  * Creates a new instance of the class.\r
1365                  */\r
1366                 public ToggleStatusLineAction() {\r
1367                         super(WorkbenchMessages.FilteredItemsSelectionDialog_toggleStatusAction, IAction.AS_CHECK_BOX);\r
1368                 }\r
1369 \r
1370                 public void run() {\r
1371                         details.setVisible(isChecked());\r
1372                 }\r
1373         }\r
1374 \r
1375         /**\r
1376          * Only refreshes UI on the basis of an already sorted and filtered set of\r
1377          * items.\r
1378          * <p>\r
1379          * Standard invocation scenario:\r
1380          * <ol>\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
1390          * class)</li>\r
1391          * </ol>\r
1392          * The scenario is rather complicated, but it had to be applied, because:\r
1393          * <ul>\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
1400          * </ul>\r
1401          * \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
1405          */\r
1406         private class RefreshJob extends UIJob {\r
1407 \r
1408                 /**\r
1409                  * Creates a new instance of the class.\r
1410                  */\r
1411                 public RefreshJob() {\r
1412                         super(ColumnFilteredItemsSelectionDialog.this.getParentShell().getDisplay(),\r
1413                                         WorkbenchMessages.FilteredItemsSelectionDialog_refreshJob);\r
1414                         setSystem(true);\r
1415                 }\r
1416 \r
1417                 /*\r
1418                  * (non-Javadoc)\r
1419                  * \r
1420                  * @see org.eclipse.ui.progress.UIJob#runInUIThread(org.eclipse.core.runtime.IProgressMonitor)\r
1421                  */\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
1426 \r
1427                         if (ColumnFilteredItemsSelectionDialog.this != null) {\r
1428                                 ColumnFilteredItemsSelectionDialog.this.refresh();\r
1429                         }\r
1430 \r
1431                         return new Status(IStatus.OK, PlatformUI.PLUGIN_ID, IStatus.OK,\r
1432                                         EMPTY_STRING, null);\r
1433                 }\r
1434 \r
1435         }\r
1436 \r
1437         /**\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
1442          */\r
1443         private class RefreshProgressMessageJob extends UIJob {\r
1444 \r
1445                 private GranualProgressMonitor progressMonitor;\r
1446 \r
1447                 /**\r
1448                  * Creates a new instance of the class.\r
1449                  */\r
1450                 public RefreshProgressMessageJob() {\r
1451                         super(ColumnFilteredItemsSelectionDialog.this.getParentShell().getDisplay(),\r
1452                                         WorkbenchMessages.FilteredItemsSelectionDialog_progressRefreshJob);\r
1453                         setSystem(true);\r
1454                 }\r
1455 \r
1456                 /*\r
1457                  * (non-Javadoc)\r
1458                  * \r
1459                  * @see org.eclipse.ui.progress.UIJob#runInUIThread(org.eclipse.core.runtime.IProgressMonitor)\r
1460                  */\r
1461                 public IStatus runInUIThread(IProgressMonitor monitor) {\r
1462 \r
1463                         if (!progressLabel.isDisposed())\r
1464                                 progressLabel.setText(progressMonitor != null ? progressMonitor.getMessage() : EMPTY_STRING);\r
1465 \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
1469                         }\r
1470 \r
1471                         // Schedule cyclical with 500 milliseconds delay\r
1472                         schedule(500);\r
1473 \r
1474                         return new Status(IStatus.OK, PlatformUI.PLUGIN_ID, IStatus.OK,\r
1475                                         EMPTY_STRING, null);\r
1476                 }\r
1477 \r
1478                 /**\r
1479                  * Schedule progress refresh job.\r
1480                  * \r
1481                  * @param progressMonitor\r
1482                  *            used during refresh progress label\r
1483                  */\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
1488                         // types quickly\r
1489                         schedule(200);\r
1490                 }\r
1491 \r
1492         }\r
1493 \r
1494         /**\r
1495          * A job responsible for computing filtered items list presented using\r
1496          * <code>RefreshJob</code>.\r
1497          * \r
1498          * @see ColumnFilteredItemsSelectionDialog.RefreshJob\r
1499          * \r
1500          */\r
1501         private class RefreshCacheJob extends Job {\r
1502 \r
1503                 private RefreshJob refreshJob = new RefreshJob();\r
1504 \r
1505                 /**\r
1506                  * Creates a new instance of the class.\r
1507                  */\r
1508                 public RefreshCacheJob() {\r
1509                         super(WorkbenchMessages.FilteredItemsSelectionDialog_cacheRefreshJob);\r
1510                         setSystem(true);\r
1511                 }\r
1512 \r
1513                 /**\r
1514                  * Stops the job and all sub-jobs.\r
1515                  */\r
1516                 public void cancelAll() {\r
1517                         cancel();\r
1518                         refreshJob.cancel();\r
1519                 }\r
1520 \r
1521                 /*\r
1522                  * (non-Javadoc)\r
1523                  * \r
1524                  * @see org.eclipse.core.runtime.jobs.Job#run(org.eclipse.core.runtime.IProgressMonitor)\r
1525                  */\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
1530                         }\r
1531 \r
1532                         if (ColumnFilteredItemsSelectionDialog.this != null) {\r
1533                                 GranualProgressMonitor wrappedMonitor = new GranualProgressMonitor(monitor);\r
1534                                 ColumnFilteredItemsSelectionDialog.this.reloadCache(true, wrappedMonitor);\r
1535                         }\r
1536 \r
1537                         if (!monitor.isCanceled()) {\r
1538                                 refreshJob.schedule();\r
1539                         }\r
1540 \r
1541                         return new Status(IStatus.OK, PlatformUI.PLUGIN_ID, IStatus.OK,\r
1542                                         EMPTY_STRING, null);\r
1543 \r
1544                 }\r
1545 \r
1546                 /*\r
1547                  * (non-Javadoc)\r
1548                  * \r
1549                  * @see org.eclipse.core.runtime.jobs.Job#canceling()\r
1550                  */\r
1551                 protected void canceling() {\r
1552                         super.canceling();\r
1553                         contentProvider.stopReloadingCache();\r
1554                 }\r
1555 \r
1556         }\r
1557 \r
1558         private class RemoveHistoryItemAction extends Action {\r
1559 \r
1560                 /**\r
1561                  * Creates a new instance of the class.\r
1562                  */\r
1563                 public RemoveHistoryItemAction() {\r
1564                         super(WorkbenchMessages.FilteredItemsSelectionDialog_removeItemsFromHistoryAction);\r
1565                 }\r
1566 \r
1567                 /*\r
1568                  * (non-Javadoc)\r
1569                  * \r
1570                  * @see org.eclipse.jface.action.Action#run()\r
1571                  */\r
1572                 public void run() {\r
1573                         List selectedElements = ((StructuredSelection) viewer.getSelection()).toList();\r
1574                         removeSelectedItems(selectedElements);\r
1575                 }\r
1576         }\r
1577 \r
1578         private static boolean showColoredLabels() {\r
1579                 return PlatformUI.getPreferenceStore().getBoolean(IWorkbenchPreferenceConstants.USE_COLORED_LABELS);\r
1580         }\r
1581 \r
1582         private class ItemsListLabelProvider extends StyledCellLabelProvider\r
1583                         implements ILabelProviderListener {\r
1584                 private ILabelProvider provider;\r
1585 \r
1586                 private ILabelDecorator selectionDecorator;\r
1587 \r
1588                 // Need to keep our own list of listeners\r
1589                 private ListenerList listeners = new ListenerList();\r
1590 \r
1591                 /**\r
1592                  * Creates a new instance of the class.\r
1593                  * \r
1594                  * @param provider\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
1598                  */\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
1604 \r
1605                         setOwnerDrawEnabled(showColoredLabels() && provider instanceof IStyledLabelProvider);\r
1606 \r
1607                         provider.addListener(this);\r
1608 \r
1609                         if (selectionDecorator != null) {\r
1610                                 selectionDecorator.addListener(this);\r
1611                         }\r
1612                 }\r
1613 \r
1614                 /**\r
1615                  * Sets new selection decorator.\r
1616                  * \r
1617                  * @param newSelectionDecorator\r
1618                  *            new label decorator for selected items in the list\r
1619                  */\r
1620                 public void setSelectionDecorator(ILabelDecorator newSelectionDecorator) {\r
1621                         if (selectionDecorator != null) {\r
1622                                 selectionDecorator.removeListener(this);\r
1623                                 selectionDecorator.dispose();\r
1624                         }\r
1625 \r
1626                         selectionDecorator = newSelectionDecorator;\r
1627 \r
1628                         if (selectionDecorator != null) {\r
1629                                 selectionDecorator.addListener(this);\r
1630                         }\r
1631                 }\r
1632 \r
1633                 /**\r
1634                  * Gets selection decorator.\r
1635                  * \r
1636                  * @return the label decorator for selected items in the list\r
1637                  */\r
1638                 public ILabelDecorator getSelectionDecorator() {\r
1639                         return selectionDecorator;\r
1640                 }\r
1641 \r
1642                 /**\r
1643                  * Sets new label provider.\r
1644                  * \r
1645                  * @param newProvider\r
1646                  *            new label provider for items in the list, not\r
1647                  *            <code>null</code>\r
1648                  */\r
1649                 public void setProvider(ILabelProvider newProvider) {\r
1650                         Assert.isNotNull(newProvider);\r
1651                         provider.removeListener(this);\r
1652                         provider.dispose();\r
1653 \r
1654                         provider = newProvider;\r
1655 \r
1656                         if (provider != null) {\r
1657                                 provider.addListener(this);\r
1658                         }\r
1659 \r
1660                         setOwnerDrawEnabled(showColoredLabels() && provider instanceof IStyledLabelProvider);\r
1661                 }\r
1662 \r
1663                 private Image getImage(Object element) {\r
1664                         if (element instanceof ItemsListSeparator) {\r
1665                                 return WorkbenchImages.getImage(IWorkbenchGraphicConstants.IMG_OBJ_SEPARATOR);\r
1666                         }\r
1667 \r
1668                         return provider.getImage(element);\r
1669                 }\r
1670 \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
1675                                                 return true;\r
1676                                 }\r
1677                         }\r
1678                         return false;\r
1679                 }\r
1680 \r
1681                 /*\r
1682                  * (non-Javadoc)\r
1683                  * \r
1684                  * @see org.eclipse.jface.viewers.ILabelProvider#getText(java.lang.Object)\r
1685                  */\r
1686                 private String getText(Object element) {\r
1687                         if (element instanceof ItemsListSeparator) {\r
1688                                 return getSeparatorLabel(((ItemsListSeparator) element).getName());\r
1689                         }\r
1690 \r
1691                         String str = provider.getText(element);\r
1692                         if (selectionDecorator != null && isSelected(element)) {\r
1693                                 return selectionDecorator.decorateText(str.toString(), element);\r
1694                         }\r
1695 \r
1696                         return str;\r
1697                 }\r
1698 \r
1699                 private StyledString getStyledText(Object element,\r
1700                                 IStyledLabelProvider provider) {\r
1701                         StyledString string = provider.getStyledText(element);\r
1702 \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
1707                         }\r
1708                         return string;\r
1709                 }\r
1710 \r
1711                 public void update(ViewerCell cell) {\r
1712                         Object element = cell.getElement();\r
1713 \r
1714                         if (!(element instanceof ItemsListSeparator) && provider instanceof IStyledLabelProvider) {\r
1715                                 IStyledLabelProvider styledLabelProvider = (IStyledLabelProvider) provider;\r
1716                                 StyledString styledString = getStyledText(element, styledLabelProvider);\r
1717 \r
1718                                 cell.setText(styledString.getString());\r
1719                                 cell.setStyleRanges(styledString.getStyleRanges());\r
1720                                 cell.setImage(styledLabelProvider.getImage(element));\r
1721                         } else {\r
1722                                 cell.setText(getText(element));\r
1723                                 cell.setImage(getImage(element));\r
1724                         }\r
1725                         cell.setFont(getFont(element));\r
1726                         cell.setForeground(getForeground(element));\r
1727                         cell.setBackground(getBackground(element));\r
1728 \r
1729                         super.update(cell);\r
1730                 }\r
1731 \r
1732                 private String getSeparatorLabel(String separatorLabel) {\r
1733                         Rectangle rect = viewer.getTable().getBounds();\r
1734 \r
1735                         int borderWidth = viewer.getTable().computeTrim(0, 0, 0, 0).width;\r
1736 \r
1737                         int imageWidth = WorkbenchImages.getImage(\r
1738                                         IWorkbenchGraphicConstants.IMG_OBJ_SEPARATOR).getBounds().width;\r
1739 \r
1740                         int width = rect.width - borderWidth - imageWidth;\r
1741 \r
1742                         GC gc = new GC(viewer.getTable());\r
1743                         gc.setFont(viewer.getTable().getFont());\r
1744 \r
1745                         int fSeparatorWidth = gc.getAdvanceWidth('-');\r
1746                         int fMessageLength = gc.textExtent(separatorLabel).x;\r
1747 \r
1748                         gc.dispose();\r
1749 \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
1754                         }\r
1755 \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
1761                 }\r
1762 \r
1763                 /*\r
1764                  * (non-Javadoc)\r
1765                  * \r
1766                  * @see org.eclipse.jface.viewers.IBaseLabelProvider#addListener(org.eclipse.jface.viewers.ILabelProviderListener)\r
1767                  */\r
1768                 public void addListener(ILabelProviderListener listener) {\r
1769                         listeners.add(listener);\r
1770                 }\r
1771 \r
1772                 /*\r
1773                  * (non-Javadoc)\r
1774                  * \r
1775                  * @see org.eclipse.jface.viewers.IBaseLabelProvider#dispose()\r
1776                  */\r
1777                 public void dispose() {\r
1778                         provider.removeListener(this);\r
1779                         provider.dispose();\r
1780 \r
1781                         if (selectionDecorator != null) {\r
1782                                 selectionDecorator.removeListener(this);\r
1783                                 selectionDecorator.dispose();\r
1784                         }\r
1785 \r
1786                         super.dispose();\r
1787                 }\r
1788 \r
1789                 /*\r
1790                  * (non-Javadoc)\r
1791                  * \r
1792                  * @see org.eclipse.jface.viewers.IBaseLabelProvider#isLabelProperty(java.lang.Object,\r
1793                  *      java.lang.String)\r
1794                  */\r
1795                 public boolean isLabelProperty(Object element, String property) {\r
1796                         if (provider.isLabelProperty(element, property)) {\r
1797                                 return true;\r
1798                         }\r
1799                         if (selectionDecorator != null\r
1800                                         && selectionDecorator.isLabelProperty(element, property)) {\r
1801                                 return true;\r
1802                         }\r
1803                         return false;\r
1804                 }\r
1805 \r
1806                 /*\r
1807                  * (non-Javadoc)\r
1808                  * \r
1809                  * @see org.eclipse.jface.viewers.IBaseLabelProvider#removeListener(org.eclipse.jface.viewers.ILabelProviderListener)\r
1810                  */\r
1811                 public void removeListener(ILabelProviderListener listener) {\r
1812                         listeners.remove(listener);\r
1813                 }\r
1814 \r
1815                 private Color getBackground(Object element) {\r
1816                         if (element instanceof ItemsListSeparator) {\r
1817                                 return null;\r
1818                         }\r
1819                         if (provider instanceof IColorProvider) {\r
1820                                 return ((IColorProvider) provider).getBackground(element);\r
1821                         }\r
1822                         return null;\r
1823                 }\r
1824 \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
1829                         }\r
1830                         if (provider instanceof IColorProvider) {\r
1831                                 return ((IColorProvider) provider).getForeground(element);\r
1832                         }\r
1833                         return null;\r
1834                 }\r
1835 \r
1836                 private Font getFont(Object element) {\r
1837                         if (element instanceof ItemsListSeparator) {\r
1838                                 return null;\r
1839                         }\r
1840                         if (provider instanceof IFontProvider) {\r
1841                                 return ((IFontProvider) provider).getFont(element);\r
1842                         }\r
1843                         return null;\r
1844                 }\r
1845 \r
1846                 /*\r
1847                  * (non-Javadoc)\r
1848                  * \r
1849                  * @see org.eclipse.jface.viewers.ILabelProviderListener#labelProviderChanged(org.eclipse.jface.viewers.LabelProviderChangedEvent)\r
1850                  */\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
1855                         }\r
1856                 }\r
1857         }\r
1858 \r
1859         /**\r
1860          * Used in ItemsListContentProvider, separates history and non-history\r
1861          * items.\r
1862          */\r
1863         private class ItemsListSeparator {\r
1864 \r
1865                 private String name;\r
1866 \r
1867                 /**\r
1868                  * Creates a new instance of the class.\r
1869                  * \r
1870                  * @param name\r
1871                  *            the name of the separator\r
1872                  */\r
1873                 public ItemsListSeparator(String name) {\r
1874                         this.name = name;\r
1875                 }\r
1876 \r
1877                 /**\r
1878                  * Returns the name of this separator.\r
1879                  * \r
1880                  * @return the name of the separator\r
1881                  */\r
1882                 public String getName() {\r
1883                         return name;\r
1884                 }\r
1885         }\r
1886 \r
1887         /**\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
1892          * \r
1893          */\r
1894         private class GranualProgressMonitor extends ProgressMonitorWrapper {\r
1895 \r
1896                 private String name;\r
1897 \r
1898                 private String subName;\r
1899 \r
1900                 private int totalWork;\r
1901 \r
1902                 private double worked;\r
1903 \r
1904                 private boolean done;\r
1905 \r
1906                 /**\r
1907                  * Creates instance of <code>GranualProgressMonitor</code>.\r
1908                  * \r
1909                  * @param monitor\r
1910                  *            progress to be wrapped\r
1911                  */\r
1912                 public GranualProgressMonitor(IProgressMonitor monitor) {\r
1913                         super(monitor);\r
1914                 }\r
1915 \r
1916                 /**\r
1917                  * Checks if filtering has been done\r
1918                  * \r
1919                  * @return true if filtering work has been done false in other way\r
1920                  */\r
1921                 public boolean isDone() {\r
1922                         return done;\r
1923                 }\r
1924 \r
1925                 /*\r
1926                  * (non-Javadoc)\r
1927                  * \r
1928                  * @see org.eclipse.core.runtime.ProgressMonitorWrapper#setTaskName(java.lang.String)\r
1929                  */\r
1930                 public void setTaskName(String name) {\r
1931                         super.setTaskName(name);\r
1932                         this.name = name;\r
1933                         this.subName = null;\r
1934                 }\r
1935 \r
1936                 /*\r
1937                  * (non-Javadoc)\r
1938                  * \r
1939                  * @see org.eclipse.core.runtime.ProgressMonitorWrapper#subTask(java.lang.String)\r
1940                  */\r
1941                 public void subTask(String name) {\r
1942                         super.subTask(name);\r
1943                         this.subName = name;\r
1944                 }\r
1945 \r
1946                 /*\r
1947                  * (non-Javadoc)\r
1948                  * \r
1949                  * @see org.eclipse.core.runtime.ProgressMonitorWrapper#beginTask(java.lang.String,\r
1950                  *      int)\r
1951                  */\r
1952                 public void beginTask(String name, int totalWork) {\r
1953                         super.beginTask(name, totalWork);\r
1954                         if (this.name == null)\r
1955                                 this.name = name;\r
1956                         this.totalWork = totalWork;\r
1957                         refreshProgressMessageJob.scheduleProgressRefresh(this);\r
1958                 }\r
1959 \r
1960                 /*\r
1961                  * (non-Javadoc)\r
1962                  * \r
1963                  * @see org.eclipse.core.runtime.ProgressMonitorWrapper#worked(int)\r
1964                  */\r
1965                 public void worked(int work) {\r
1966                         super.worked(work);\r
1967                         internalWorked(work);\r
1968                 }\r
1969 \r
1970                 /*\r
1971                  * (non-Javadoc)\r
1972                  * \r
1973                  * @see org.eclipse.core.runtime.ProgressMonitorWrapper#done()\r
1974                  */\r
1975                 public void done() {\r
1976                         done = true;\r
1977                         super.done();\r
1978                 }\r
1979 \r
1980                 /*\r
1981                  * (non-Javadoc)\r
1982                  * \r
1983                  * @see org.eclipse.core.runtime.ProgressMonitorWrapper#setCanceled(boolean)\r
1984                  */\r
1985                 public void setCanceled(boolean b) {\r
1986                         done = b;\r
1987                         super.setCanceled(b);\r
1988                 }\r
1989 \r
1990                 /*\r
1991                  * (non-Javadoc)\r
1992                  * \r
1993                  * @see org.eclipse.core.runtime.ProgressMonitorWrapper#internalWorked(double)\r
1994                  */\r
1995                 public void internalWorked(double work) {\r
1996                         worked = worked + work;\r
1997                 }\r
1998 \r
1999                 private String getMessage() {\r
2000                         if (done)\r
2001                                 return ""; //$NON-NLS-1$\r
2002 \r
2003                         String message;\r
2004 \r
2005                         if (name == null) {\r
2006                                 message = subName == null ? "" : subName; //$NON-NLS-1$\r
2007                         } else {\r
2008                                 message = subName == null ? name\r
2009                                                 : NLS.bind(WorkbenchMessages.FilteredItemsSelectionDialog_subtaskProgressMessage,\r
2010                                                                 new Object[] { name, subName });\r
2011                         }\r
2012                         if (totalWork == 0)\r
2013                                 return message;\r
2014 \r
2015                         return NLS.bind(WorkbenchMessages.FilteredItemsSelectionDialog_taskProgressMessage,\r
2016                                         new Object[] { message, new Integer((int) ((worked * 100) / totalWork)) });\r
2017 \r
2018                 }\r
2019 \r
2020         }\r
2021 \r
2022         /**\r
2023          * Filters items history and schedule filter job.\r
2024          */\r
2025         private class FilterHistoryJob extends Job {\r
2026 \r
2027                 /**\r
2028                  * Filter used during the filtering process.\r
2029                  */\r
2030                 private ItemsFilter itemsFilter;\r
2031 \r
2032                 /**\r
2033                  * Creates new instance of receiver.\r
2034                  */\r
2035                 public FilterHistoryJob() {\r
2036                         super(WorkbenchMessages.FilteredItemsSelectionDialog_jobLabel);\r
2037                         setSystem(true);\r
2038                 }\r
2039 \r
2040                 /*\r
2041                  * (non-Javadoc)\r
2042                  * \r
2043                  * @see org.eclipse.core.runtime.jobs.Job#run(org.eclipse.core.runtime.IProgressMonitor)\r
2044                  */\r
2045                 protected IStatus run(IProgressMonitor monitor) {\r
2046                         this.itemsFilter = filter;\r
2047 \r
2048                         contentProvider.reset();\r
2049 \r
2050                         refreshWithLastSelection = false;\r
2051 \r
2052                         contentProvider.addHistoryItems(itemsFilter);\r
2053 \r
2054                         if (!(lastCompletedFilter != null && lastCompletedFilter.isSubFilter(this.itemsFilter)))\r
2055                                 contentProvider.refresh();\r
2056 \r
2057                         filterJob.schedule();\r
2058 \r
2059                         return Status.OK_STATUS;\r
2060                 }\r
2061 \r
2062         }\r
2063 \r
2064         /**\r
2065          * Filters items in indicated set and history. During filtering, it\r
2066          * refreshes the dialog (progress monitor and elements list).\r
2067          * \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
2075          */\r
2076         private class FilterJob extends Job {\r
2077 \r
2078                 /**\r
2079                  * Filter used during the filtering process.\r
2080                  */\r
2081                 protected ItemsFilter itemsFilter;\r
2082 \r
2083                 /**\r
2084                  * Creates new instance of FilterJob\r
2085                  */\r
2086                 public FilterJob() {\r
2087                         super(WorkbenchMessages.FilteredItemsSelectionDialog_jobLabel);\r
2088                         setSystem(true);\r
2089                 }\r
2090 \r
2091                 /*\r
2092                  * (non-Javadoc)\r
2093                  * \r
2094                  * @see org.eclipse.core.runtime.jobs.Job#run(org.eclipse.core.runtime.IProgressMonitor)\r
2095                  */\r
2096                 protected final IStatus run(IProgressMonitor parent) {\r
2097                         GranualProgressMonitor monitor = new GranualProgressMonitor(parent);\r
2098                         return doRun(monitor);\r
2099                 }\r
2100 \r
2101                 /**\r
2102                  * Executes job using the given filtering progress monitor. A hook for\r
2103                  * subclasses.\r
2104                  * \r
2105                  * @param monitor\r
2106                  *            progress monitor\r
2107                  * @return result of the execution\r
2108                  */\r
2109                 protected IStatus doRun(GranualProgressMonitor monitor) {\r
2110                         try {\r
2111                                 internalRun(monitor);\r
2112                         } catch (CoreException e) {\r
2113                                 cancel();\r
2114                                 return new Status(\r
2115                                                 IStatus.ERROR,\r
2116                                                 PlatformUI.PLUGIN_ID,\r
2117                                                 IStatus.ERROR,\r
2118                                                 WorkbenchMessages.FilteredItemsSelectionDialog_jobError,\r
2119                                                 e);\r
2120                         }\r
2121                         return Status.OK_STATUS;\r
2122                 }\r
2123 \r
2124                 /**\r
2125                  * Main method for the job.\r
2126                  * \r
2127                  * @param monitor\r
2128                  * @throws CoreException\r
2129                  */\r
2130                 private void internalRun(GranualProgressMonitor monitor)\r
2131                                 throws CoreException {\r
2132                         try {\r
2133                                 if (monitor.isCanceled())\r
2134                                         return;\r
2135 \r
2136                                 this.itemsFilter = filter;\r
2137 \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
2144                                 //}\r
2145                                 \r
2146                                 filterContent(monitor);\r
2147 \r
2148                                 if (monitor.isCanceled())\r
2149                                         return;\r
2150 \r
2151                                 contentProvider.refresh();\r
2152                         } finally {\r
2153                                 monitor.done();\r
2154                         }\r
2155                 }\r
2156 \r
2157                 /**\r
2158                  * Filters items.\r
2159                  * \r
2160                  * @param monitor\r
2161                  *            for monitoring progress\r
2162                  * @throws CoreException\r
2163                  */\r
2164                 protected void filterContent(GranualProgressMonitor monitor)\r
2165                                 throws CoreException {\r
2166 \r
2167                         if (lastCompletedFilter != null\r
2168                                         && lastCompletedFilter.isSubFilter(this.itemsFilter)) {\r
2169 \r
2170                                 int length = lastCompletedResult.size() / 500;\r
2171                                 monitor.beginTask(WorkbenchMessages.FilteredItemsSelectionDialog_cacheSearchJob_taskName, length);\r
2172 \r
2173                                 for (int pos = 0; pos < lastCompletedResult.size(); pos++) {\r
2174 \r
2175                                         Object item = lastCompletedResult.get(pos);\r
2176                                         if (monitor.isCanceled())\r
2177                                                 break;\r
2178                                         contentProvider.add(item, itemsFilter);\r
2179 \r
2180                                         if ((pos % 500) == 0) {\r
2181                                                 monitor.worked(1);\r
2182                                         }\r
2183                                 }\r
2184 \r
2185                         } else {\r
2186 \r
2187                                 lastCompletedFilter = null;\r
2188                                 lastCompletedResult = null;\r
2189 \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
2194 \r
2195                                 }\r
2196 \r
2197                                 fillContentProvider(contentProvider, itemsFilter, subMonitor);\r
2198 \r
2199                                 if (monitor != null && !monitor.isCanceled()) {\r
2200                                         monitor.worked(2);\r
2201                                         contentProvider.rememberResult(itemsFilter);\r
2202                                         monitor.worked(3);\r
2203                                 }\r
2204                         }\r
2205 \r
2206                 }\r
2207 \r
2208         }\r
2209 \r
2210         /**\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
2215          * <p>\r
2216          * The history can be stored to/loaded from an XML file.\r
2217          */\r
2218         protected static abstract class SelectionHistory {\r
2219 \r
2220                 private static final String DEFAULT_ROOT_NODE_NAME = "historyRootNode"; //$NON-NLS-1$\r
2221 \r
2222                 private static final String DEFAULT_INFO_NODE_NAME = "infoNode"; //$NON-NLS-1$\r
2223 \r
2224                 private static final int MAX_HISTORY_SIZE = 60;\r
2225 \r
2226                 private final Set historyList;\r
2227 \r
2228                 private final String rootNodeName;\r
2229 \r
2230                 private final String infoNodeName;\r
2231 \r
2232                 private SelectionHistory(String rootNodeName, String infoNodeName) {\r
2233 \r
2234                         historyList = Collections.synchronizedSet(new LinkedHashSet() {\r
2235 \r
2236                                 private static final long serialVersionUID = 0L;\r
2237 \r
2238                                 /*\r
2239                                  * (non-Javadoc)\r
2240                                  * \r
2241                                  * @see java.util.LinkedList#add(java.lang.Object)\r
2242                                  */\r
2243                                 public boolean add(Object arg0) {\r
2244                                         if (this.size() >= MAX_HISTORY_SIZE) {\r
2245                                                 Iterator iterator = this.iterator();\r
2246                                                 iterator.next();\r
2247                                                 iterator.remove();\r
2248                                         }\r
2249                                         return super.add(arg0);\r
2250                                 }\r
2251 \r
2252                         });\r
2253 \r
2254                         this.rootNodeName = rootNodeName;\r
2255                         this.infoNodeName = infoNodeName;\r
2256                 }\r
2257 \r
2258                 /**\r
2259                  * Creates new instance of <code>SelectionHistory</code>.\r
2260                  */\r
2261                 public SelectionHistory() {\r
2262                         this(DEFAULT_ROOT_NODE_NAME, DEFAULT_INFO_NODE_NAME);\r
2263                 }\r
2264 \r
2265                 /**\r
2266                  * Adds object to history.\r
2267                  * \r
2268                  * @param object\r
2269                  *            the item to be added to the history\r
2270                  */\r
2271                 public synchronized void accessed(Object object) {\r
2272                         historyList.remove(object);\r
2273                         historyList.add(object);\r
2274                 }\r
2275 \r
2276                 /**\r
2277                  * Returns <code>true</code> if history contains object.\r
2278                  * \r
2279                  * @param 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
2283                  */\r
2284                 public synchronized boolean contains(Object object) {\r
2285                         return historyList.contains(object);\r
2286                 }\r
2287 \r
2288                 /**\r
2289                  * Returns <code>true</code> if history is empty.\r
2290                  * \r
2291                  * @return <code>true</code> if history is empty\r
2292                  */\r
2293                 public synchronized boolean isEmpty() {\r
2294                         return historyList.isEmpty();\r
2295                 }\r
2296 \r
2297                 /**\r
2298                  * Remove element from history.\r
2299                  * \r
2300                  * @param element\r
2301                  *            to remove form the history\r
2302                  * @return <code>true</code> if this list contained the specified\r
2303                  *         element\r
2304                  */\r
2305                 public synchronized boolean remove(Object element) {\r
2306                         return historyList.remove(element);\r
2307                 }\r
2308 \r
2309                 /**\r
2310                  * Load history elements from memento.\r
2311                  * \r
2312                  * @param memento\r
2313                  *            memento from which the history will be retrieved\r
2314                  */\r
2315                 public void load(IMemento memento) {\r
2316 \r
2317                         XMLMemento historyMemento = (XMLMemento) memento\r
2318                                         .getChild(rootNodeName);\r
2319 \r
2320                         if (historyMemento == null) {\r
2321                                 return;\r
2322                         }\r
2323 \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
2331                                 }\r
2332                         }\r
2333                 }\r
2334 \r
2335                 /**\r
2336                  * Save history elements to memento.\r
2337                  * \r
2338                  * @param memento\r
2339                  *            memento to which the history will be added\r
2340                  */\r
2341                 public void save(IMemento memento) {\r
2342 \r
2343                         IMemento historyMemento = memento.createChild(rootNodeName);\r
2344 \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
2351                         }\r
2352 \r
2353                 }\r
2354 \r
2355                 /**\r
2356                  * Gets array of history items.\r
2357                  * \r
2358                  * @return array of history elements\r
2359                  */\r
2360                 public synchronized Object[] getHistoryItems() {\r
2361                         return historyList.toArray();\r
2362                 }\r
2363 \r
2364                 /**\r
2365                  * Creates an object using given memento.\r
2366                  * \r
2367                  * @param memento\r
2368                  *            memento used for creating new object\r
2369                  * \r
2370                  * @return the restored object\r
2371                  */\r
2372                 protected abstract Object restoreItemFromMemento(IMemento memento);\r
2373 \r
2374                 /**\r
2375                  * Store object in <code>IMemento</code>.\r
2376                  * \r
2377                  * @param item\r
2378                  *            the item to store\r
2379                  * @param memento\r
2380                  *            the memento to store to\r
2381                  */\r
2382                 protected abstract void storeItemToMemento(Object item, IMemento memento);\r
2383 \r
2384         }\r
2385 \r
2386         /**\r
2387          * Filters elements using SearchPattern by comparing the names of items with\r
2388          * the filter pattern.\r
2389          */\r
2390         protected abstract class ItemsFilter {\r
2391 \r
2392                 protected SearchPattern patternMatcher;\r
2393 \r
2394                 /**\r
2395                  * Creates new instance of ItemsFilter.\r
2396                  */\r
2397                 public ItemsFilter() {\r
2398                         this(new SearchPattern());\r
2399                 }\r
2400 \r
2401                 /**\r
2402                  * Creates new instance of ItemsFilter.\r
2403                  * \r
2404                  * @param searchPattern\r
2405                  *            the pattern to be used when filtering\r
2406                  */\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
2412                         }\r
2413                         patternMatcher.setPattern(stringPattern);\r
2414                 }\r
2415 \r
2416                 /**\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
2420                  * <p>\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
2425                  * </p>\r
2426                  * \r
2427                  * @param filter\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
2432                  * \r
2433                  * @see org.eclipse.ui.dialogs.SearchPattern#isSubPattern(org.eclipse.ui.dialogs.SearchPattern)\r
2434                  */\r
2435                 public boolean isSubFilter(ItemsFilter filter) {\r
2436                         if (filter != null) {\r
2437                                 return this.patternMatcher.isSubPattern(filter.patternMatcher);\r
2438                         }\r
2439                         return false;\r
2440                 }\r
2441 \r
2442                 /**\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
2446                  * \r
2447                  * @param 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
2452                  * \r
2453                  * @see org.eclipse.ui.dialogs.SearchPattern#equalsPattern(org.eclipse.ui.dialogs.SearchPattern)\r
2454                  */\r
2455                 public boolean equalsFilter(ItemsFilter filter) {\r
2456                         if (filter != null\r
2457                                         && filter.patternMatcher.equalsPattern(this.patternMatcher)) {\r
2458                                 return true;\r
2459                         }\r
2460                         return false;\r
2461                 }\r
2462 \r
2463                 /**\r
2464                  * Checks whether the pattern's match rule is camel case.\r
2465                  * \r
2466                  * @return <code>true</code> if pattern's match rule is camel case,\r
2467                  *         <code>false</code> otherwise\r
2468                  */\r
2469                 public boolean isCamelCasePattern() {\r
2470                         return patternMatcher.getMatchRule() == SearchPattern.RULE_CAMELCASE_MATCH;\r
2471                 }\r
2472 \r
2473                 /**\r
2474                  * Returns the pattern string.\r
2475                  * \r
2476                  * @return pattern for this filter\r
2477                  * \r
2478                  * @see SearchPattern#getPattern()\r
2479                  */\r
2480                 public String getPattern() {\r
2481                         return patternMatcher.getPattern();\r
2482                 }\r
2483 \r
2484                 /**\r
2485                  * Returns the rule to apply for matching keys.\r
2486                  * \r
2487                  * @return an implementation-specific match rule\r
2488                  * \r
2489                  * @see SearchPattern#getMatchRule() for match rules returned by the\r
2490                  *      default implementation\r
2491                  */\r
2492                 public int getMatchRule() {\r
2493                         return patternMatcher.getMatchRule();\r
2494                 }\r
2495 \r
2496                 /**\r
2497                  * Matches text with filter.\r
2498                  * \r
2499                  * @param text\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
2503                  */\r
2504                 protected boolean matches(String text) {\r
2505                         return patternMatcher.matches(text);\r
2506                 }\r
2507 \r
2508                 /**\r
2509                  * General method for matching raw name pattern. Checks whether current\r
2510                  * pattern is prefix of name provided item.\r
2511                  * \r
2512                  * @param item\r
2513                  *            item to check\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
2517                  */\r
2518                 public boolean matchesRawNamePattern(Object item) {\r
2519                         String prefix = patternMatcher.getPattern();\r
2520                         String text = getElementName(item);\r
2521 \r
2522                         if (text == null)\r
2523                                 return false;\r
2524 \r
2525                         int textLength = text.length();\r
2526                         int prefixLength = prefix.length();\r
2527                         if (textLength < prefixLength) {\r
2528                                 return false;\r
2529                         }\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
2533                                         return false;\r
2534                         }\r
2535                         return true;\r
2536                 }\r
2537 \r
2538                 /**\r
2539                  * Matches an item against filter conditions.\r
2540                  * \r
2541                  * @param item\r
2542                  * @return <code>true<code> if item matches against filter conditions, <code>false</code>\r
2543                  *         otherwise\r
2544                  */\r
2545                 public abstract boolean matchItem(Object item);\r
2546 \r
2547                 /**\r
2548                  * Checks consistency of an item. Item is inconsistent if was changed or\r
2549                  * removed.\r
2550                  * \r
2551                  * @param item\r
2552                  * @return <code>true</code> if item is consistent, <code>false</code>\r
2553                  *         if item is inconsistent\r
2554                  */\r
2555                 public abstract boolean isConsistentItem(Object item);\r
2556 \r
2557         }\r
2558 \r
2559         /**\r
2560          * An interface to content providers for\r
2561          * <code>FilterItemsSelectionDialog</code>.\r
2562          */\r
2563         protected abstract class AbstractContentProvider {\r
2564                 /**\r
2565                  * Adds the item to the content provider iff the filter matches the\r
2566                  * item. Otherwise does nothing.\r
2567                  * \r
2568                  * @param item\r
2569                  *            the item to add\r
2570                  * @param itemsFilter\r
2571                  *            the filter\r
2572                  * \r
2573                  * @see ColumnFilteredItemsSelectionDialog.ItemsFilter#matchItem(Object)\r
2574                  */\r
2575                 public abstract void add(Object item, ItemsFilter itemsFilter);\r
2576         }\r
2577 \r
2578         /**\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
2585          * <p>\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
2592          * filtered list.\r
2593          */\r
2594         private class ContentProvider extends AbstractContentProvider implements\r
2595                         IStructuredContentProvider, ILazyContentProvider {\r
2596 \r
2597                 private SelectionHistory selectionHistory;\r
2598 \r
2599                 /**\r
2600                  * Raw result of the searching (unsorted, unfiltered).\r
2601                  * <p>\r
2602                  * Standard object flow:\r
2603                  * <code>items -> lastSortedItems -> lastFilteredItems</code>\r
2604                  */\r
2605                 private Set items;\r
2606 \r
2607                 /**\r
2608                  * Items that are duplicates.\r
2609                  */\r
2610                 private Set duplicates;\r
2611 \r
2612                 /**\r
2613                  * List of <code>ViewerFilter</code>s to be used during filtering\r
2614                  */\r
2615                 private List filters;\r
2616 \r
2617                 /**\r
2618                  * Result of the last filtering.\r
2619                  * <p>\r
2620                  * Standard object flow:\r
2621                  * <code>items -> lastSortedItems -> lastFilteredItems</code>\r
2622                  */\r
2623                 private List lastFilteredItems;\r
2624 \r
2625                 /**\r
2626                  * Result of the last sorting.\r
2627                  * <p>\r
2628                  * Standard object flow:\r
2629                  * <code>items -> lastSortedItems -> lastFilteredItems</code>\r
2630                  */\r
2631                 private List lastSortedItems;\r
2632 \r
2633                 /**\r
2634                  * Used for <code>getFilteredItems()</code> method canceling (when the\r
2635                  * job that invoked the method was canceled).\r
2636                  * <p>\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
2641                  */\r
2642                 private boolean reset;\r
2643 \r
2644                 /**\r
2645                  * Creates new instance of <code>ContentProvider</code>.\r
2646                  */\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
2652                 }\r
2653 \r
2654                 /**\r
2655                  * Sets selection history.\r
2656                  * \r
2657                  * @param selectionHistory\r
2658                  *            The selectionHistory to set.\r
2659                  */\r
2660                 public void setSelectionHistory(SelectionHistory selectionHistory) {\r
2661                         this.selectionHistory = selectionHistory;\r
2662                 }\r
2663 \r
2664                 /**\r
2665                  * @return Returns the selectionHistory.\r
2666                  */\r
2667                 public SelectionHistory getSelectionHistory() {\r
2668                         return selectionHistory;\r
2669                 }\r
2670 \r
2671                 /**\r
2672                  * Removes all content items and resets progress message.\r
2673                  */\r
2674                 public void reset() {\r
2675                         reset = true;\r
2676                         this.items.clear();\r
2677                         this.duplicates.clear();\r
2678                         this.lastSortedItems.clear();\r
2679                 }\r
2680 \r
2681                 /**\r
2682                  * Stops reloading cache - <code>getFilteredItems()</code> method.\r
2683                  */\r
2684                 public void stopReloadingCache() {\r
2685                         reset = true;\r
2686                 }\r
2687 \r
2688                 /**\r
2689                  * Adds filtered item.\r
2690                  * \r
2691                  * @param item\r
2692                  * @param itemsFilter\r
2693                  */\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
2699                                         }\r
2700                                 } else {\r
2701                                         this.items.add(item);\r
2702                                 }\r
2703                         }\r
2704                 }\r
2705 \r
2706                 /**\r
2707                  * Add all history items to <code>contentProvider</code>.\r
2708                  * \r
2709                  * @param itemsFilter\r
2710                  */\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
2721                                                                 } else {\r
2722                                                                         this.selectionHistory.remove(item);\r
2723                                                                 }\r
2724                                                         }\r
2725                                                 }\r
2726                                         }\r
2727                                 }\r
2728                         }\r
2729                 }\r
2730 \r
2731                 /**\r
2732                  * Refresh dialog.\r
2733                  */\r
2734                 public void refresh() {\r
2735                         scheduleRefresh();\r
2736                 }\r
2737 \r
2738                 /**\r
2739                  * Removes items from history and refreshes the view.\r
2740                  * \r
2741                  * @param item\r
2742                  *            to remove\r
2743                  * \r
2744                  * @return removed item\r
2745                  */\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
2753                         }\r
2754 \r
2755                         synchronized (lastSortedItems) {\r
2756                                 Collections.sort(lastSortedItems, getHistoryComparator());\r
2757                         }\r
2758                         return item;\r
2759                 }\r
2760 \r
2761                 /**\r
2762                  * Adds item to history and refresh view.\r
2763                  * \r
2764                  * @param item\r
2765                  *            to add\r
2766                  */\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
2774                         }\r
2775                         synchronized (lastSortedItems) {\r
2776                                 Collections.sort(lastSortedItems, getHistoryComparator());\r
2777                         }\r
2778                         this.refresh();\r
2779                 }\r
2780 \r
2781                 /**\r
2782                  * @param item\r
2783                  * @return <code>true</code> if given item is part of the history\r
2784                  */\r
2785                 public boolean isHistoryElement(Object item) {\r
2786                         if (this.selectionHistory != null) {\r
2787                                 return this.selectionHistory.contains(item);\r
2788                         }\r
2789                         return false;\r
2790                 }\r
2791 \r
2792                 /**\r
2793                  * Sets/unsets given item as duplicate.\r
2794                  * \r
2795                  * @param item\r
2796                  *            item to change\r
2797                  * \r
2798                  * @param isDuplicate\r
2799                  *            duplicate flag\r
2800                  */\r
2801                 public void setDuplicateElement(Object item, boolean isDuplicate) {\r
2802                         if (this.items.contains(item)) {\r
2803                                 if (isDuplicate)\r
2804                                         this.duplicates.add(item);\r
2805                                 else\r
2806                                         this.duplicates.remove(item);\r
2807                         }\r
2808                 }\r
2809 \r
2810                 /**\r
2811                  * Indicates whether given item is a duplicate.\r
2812                  * \r
2813                  * @param item\r
2814                  *            item to check\r
2815                  * @return <code>true</code> if item is duplicate\r
2816                  */\r
2817                 public boolean isDuplicateElement(Object item) {\r
2818                         return duplicates.contains(item);\r
2819                 }\r
2820 \r
2821                 /**\r
2822                  * Load history from memento.\r
2823                  * \r
2824                  * @param memento\r
2825                  *            memento from which the history will be retrieved\r
2826                  */\r
2827                 public void loadHistory(IMemento memento) {\r
2828                         if (this.selectionHistory != null)\r
2829                                 this.selectionHistory.load(memento);\r
2830                 }\r
2831 \r
2832                 /**\r
2833                  * Save history to memento.\r
2834                  * \r
2835                  * @param memento\r
2836                  *            memento to which the history will be added\r
2837                  */\r
2838                 public void saveHistory(IMemento memento) {\r
2839                         if (this.selectionHistory != null)\r
2840                                 this.selectionHistory.save(memento);\r
2841                 }\r
2842 \r
2843                 /**\r
2844                  * Gets sorted items.\r
2845                  * \r
2846                  * @return sorted items\r
2847                  */\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
2854                                 }\r
2855                         }\r
2856                         return lastSortedItems.toArray();\r
2857                 }\r
2858 \r
2859                 /**\r
2860                  * Remember result of filtering.\r
2861                  * \r
2862                  * @param itemsFilter\r
2863                  */\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
2871                         }\r
2872 \r
2873                 }\r
2874 \r
2875                 /*\r
2876                  * (non-Javadoc)\r
2877                  * \r
2878                  * @see org.eclipse.jface.viewers.IStructuredContentProvider#getElements(java.lang.Object)\r
2879                  */\r
2880                 public Object[] getElements(Object inputElement) {\r
2881                         return lastFilteredItems.toArray();\r
2882                 }\r
2883 \r
2884                 public int getNumberOfElements() {\r
2885                         return lastFilteredItems.size();\r
2886                 }\r
2887 \r
2888                 /*\r
2889                  * (non-Javadoc)\r
2890                  * \r
2891                  * @see org.eclipse.jface.viewers.IContentProvider#dispose()\r
2892                  */\r
2893                 public void dispose() {\r
2894                 }\r
2895 \r
2896                 /*\r
2897                  * (non-Javadoc)\r
2898                  * \r
2899                  * @see org.eclipse.jface.viewers.IContentProvider#inputChanged(org.eclipse.jface.viewers.Viewer,\r
2900                  *      java.lang.Object, java.lang.Object)\r
2901                  */\r
2902                 public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {\r
2903                 }\r
2904 \r
2905                 /*\r
2906                  * (non-Javadoc)\r
2907                  * \r
2908                  * @see org.eclipse.jface.viewers.ILazyContentProvider#updateElement(int)\r
2909                  */\r
2910                 public void updateElement(int index) {\r
2911 \r
2912                         ColumnFilteredItemsSelectionDialog.this.viewer.replace((lastFilteredItems\r
2913                                         .size() > index) ? lastFilteredItems.get(index) : null,\r
2914                                         index);\r
2915 \r
2916                 }\r
2917 \r
2918                 /**\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
2922                  * \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
2927                  * \r
2928                  * @param monitor\r
2929                  *            progress monitor\r
2930                  */\r
2931                 public void reloadCache(boolean checkDuplicates,\r
2932                                 IProgressMonitor monitor) {\r
2933 \r
2934                         reset = false;\r
2935 \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
2939 \r
2940                                 monitor.beginTask(WorkbenchMessages.FilteredItemsSelectionDialog_cacheRefreshJob, totalWork);\r
2941                         }\r
2942 \r
2943                         // the TableViewer's root (the input) is treated as parent\r
2944 \r
2945                         lastFilteredItems = Arrays.asList(getFilteredItems(viewer.getInput(),\r
2946                                         monitor != null ? new SubProgressMonitor(monitor, 100) : null));\r
2947 \r
2948                         if (reset || (monitor != null && monitor.isCanceled())) {\r
2949                                 if (monitor != null)\r
2950                                         monitor.done();\r
2951                                 return;\r
2952                         }\r
2953 \r
2954                         if (checkDuplicates) {\r
2955                                 checkDuplicates(monitor);\r
2956                         }\r
2957                         if (monitor != null)\r
2958                                 monitor.done();\r
2959                 }\r
2960 \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
2968                                 }\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
2972                                                 return;\r
2973                                         Object item = lastFilteredItems.get(i);\r
2974 \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
2980                                                 } else {\r
2981                                                         setDuplicateElement(item, false);\r
2982                                                 }\r
2983                                         }\r
2984 \r
2985                                         if (subMonitor != null && reportEvery != 0\r
2986                                                         && (i + 1) % reportEvery == 0)\r
2987                                                 subMonitor.worked(1);\r
2988                                 }\r
2989                                 helperMap.clear();\r
2990                         }\r
2991                 }\r
2992 \r
2993                 /**\r
2994                  * Returns an array of items filtered using the provided\r
2995                  * <code>ViewerFilter</code>s with a separator added.\r
2996                  * \r
2997                  * @param parent\r
2998                  *            the parent\r
2999                  * @param monitor\r
3000                  *            progress monitor, can be <code>null</code>\r
3001                  * @return an array of filtered items\r
3002                  */\r
3003                 protected Object[] getFilteredItems(Object parent,\r
3004                                 IProgressMonitor monitor) {\r
3005                         int ticks = 100;\r
3006                         if (monitor == null) {\r
3007                                 monitor = new NullProgressMonitor();\r
3008                         }\r
3009 \r
3010                         monitor.beginTask(WorkbenchMessages.FilteredItemsSelectionDialog_cacheRefreshJob_getFilteredElements, ticks);\r
3011                         if (filters != null) {\r
3012                                 ticks /= (filters.size() + 2);\r
3013                         } else {\r
3014                                 ticks /= 2;\r
3015                         }\r
3016 \r
3017                         // get already sorted array\r
3018                         Object[] filteredElements = getSortedItems();\r
3019 \r
3020                         monitor.worked(ticks);\r
3021 \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
3028                                 }\r
3029                         }\r
3030 \r
3031                         if (filteredElements == null || monitor.isCanceled()) {\r
3032                                 monitor.done();\r
3033                                 return new Object[0];\r
3034                         }\r
3035 \r
3036                         ArrayList preparedElements = new ArrayList();\r
3037                         boolean hasHistory = false;\r
3038 \r
3039                         if (filteredElements.length > 0) {\r
3040                                 if (isHistoryElement(filteredElements[0])) {\r
3041                                         hasHistory = true;\r
3042                                 }\r
3043                         }\r
3044 \r
3045                         int reportEvery = filteredElements.length / ticks;\r
3046 \r
3047                         // add separator\r
3048                         for (int i = 0; i < filteredElements.length; i++) {\r
3049                                 Object item = filteredElements[i];\r
3050 \r
3051                                 if (hasHistory && !isHistoryElement(item)) {\r
3052                                         preparedElements.add(itemsListSeparator);\r
3053                                         hasHistory = false;\r
3054                                 }\r
3055 \r
3056                                 preparedElements.add(item);\r
3057 \r
3058                                 if (reportEvery != 0 && ((i + 1) % reportEvery == 0)) {\r
3059                                         monitor.worked(1);\r
3060                                 }\r
3061                         }\r
3062 \r
3063                         monitor.done();\r
3064 \r
3065                         return preparedElements.toArray();\r
3066                 }\r
3067 \r
3068                 /**\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
3072                  * \r
3073                  * \r
3074                  * @param filter\r
3075                  *            the filter to be added\r
3076                  */\r
3077                 public void addFilter(ViewerFilter filter) {\r
3078                         if (filters == null) {\r
3079                                 filters = new ArrayList();\r
3080                         }\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
3084                         // added\r
3085                 }\r
3086 \r
3087         }\r
3088 \r
3089         /**\r
3090          * A content provider that does nothing.\r
3091          */\r
3092         private class NullContentProvider implements IContentProvider {\r
3093 \r
3094                 /*\r
3095                  * (non-Javadoc)\r
3096                  * \r
3097                  * @see org.eclipse.jface.viewers.IContentProvider#dispose()\r
3098                  */\r
3099                 public void dispose() {\r
3100                 }\r
3101 \r
3102                 /*\r
3103                  * (non-Javadoc)\r
3104                  * \r
3105                  * @see org.eclipse.jface.viewers.IContentProvider#inputChanged(org.eclipse.jface.viewers.Viewer,\r
3106                  *      java.lang.Object, java.lang.Object)\r
3107                  */\r
3108                 public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {\r
3109                 }\r
3110 \r
3111         }\r
3112 \r
3113         /**\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
3117          */\r
3118         private class DetailsContentViewer extends ContentViewer {\r
3119 \r
3120                 private CLabel label;\r
3121 \r
3122                 /**\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
3126                  */\r
3127                 private ViewForm viewForm;\r
3128 \r
3129                 /**\r
3130                  * Constructs a new instance of this class given its parent and a style\r
3131                  * value describing its behavior and appearance.\r
3132                  * \r
3133                  * @param parent\r
3134                  *            the parent component\r
3135                  * @param style\r
3136                  *            SWT style bits\r
3137                  */\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
3147                 }\r
3148 \r
3149                 /**\r
3150                  * Shows/hides the content viewer.\r
3151                  * \r
3152                  * @param visible\r
3153                  *            if the content viewer should be visible.\r
3154                  */\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
3159                 }\r
3160 \r
3161                 /*\r
3162                  * (non-Javadoc)\r
3163                  * \r
3164                  * @see org.eclipse.jface.viewers.Viewer#inputChanged(java.lang.Object,\r
3165                  *      java.lang.Object)\r
3166                  */\r
3167                 protected void inputChanged(Object input, Object oldInput) {\r
3168                         if (oldInput == null) {\r
3169                                 if (input == null) {\r
3170                                         return;\r
3171                                 }\r
3172                                 refresh();\r
3173                                 return;\r
3174                         }\r
3175 \r
3176                         refresh();\r
3177 \r
3178                 }\r
3179 \r
3180                 /*\r
3181                  * (non-Javadoc)\r
3182                  * \r
3183                  * @see org.eclipse.jface.viewers.ContentViewer#handleLabelProviderChanged(org.eclipse.jface.viewers.LabelProviderChangedEvent)\r
3184                  */\r
3185                 protected void handleLabelProviderChanged(\r
3186                                 LabelProviderChangedEvent event) {\r
3187                         if (event != null) {\r
3188                                 refresh(event.getElements());\r
3189                         }\r
3190                 }\r
3191 \r
3192                 /*\r
3193                  * (non-Javadoc)\r
3194                  * \r
3195                  * @see org.eclipse.jface.viewers.Viewer#getControl()\r
3196                  */\r
3197                 public Control getControl() {\r
3198                         return label;\r
3199                 }\r
3200 \r
3201                 /*\r
3202                  * (non-Javadoc)\r
3203                  * \r
3204                  * @see org.eclipse.jface.viewers.Viewer#getSelection()\r
3205                  */\r
3206                 public ISelection getSelection() {\r
3207                         // not supported\r
3208                         return null;\r
3209                 }\r
3210 \r
3211                 /*\r
3212                  * (non-Javadoc)\r
3213                  * \r
3214                  * @see org.eclipse.jface.viewers.Viewer#refresh()\r
3215                  */\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
3222                         } else {\r
3223                                 doRefresh(null, null);\r
3224                         }\r
3225                 }\r
3226 \r
3227                 /**\r
3228                  * Sets the given text and image to the label.\r
3229                  * \r
3230                  * @param text\r
3231                  *            the new text or null\r
3232                  * @param image\r
3233                  *            the new image\r
3234                  */\r
3235                 private void doRefresh(String text, Image image) {\r
3236                         if ( text != null ) {\r
3237                                 text = LegacyActionTools.escapeMnemonics(text);\r
3238                         }\r
3239                         label.setText(text);\r
3240                         label.setImage(image);\r
3241                 }\r
3242 \r
3243                 /*\r
3244                  * (non-Javadoc)\r
3245                  * \r
3246                  * @see org.eclipse.jface.viewers.Viewer#setSelection(org.eclipse.jface.viewers.ISelection,\r
3247                  *      boolean)\r
3248                  */\r
3249                 public void setSelection(ISelection selection, boolean reveal) {\r
3250                         // not supported\r
3251                 }\r
3252 \r
3253                 /**\r
3254                  * Refreshes the label if currently chosen element is on the list.\r
3255                  * \r
3256                  * @param objs\r
3257                  *            list of changed object\r
3258                  */\r
3259                 private void refresh(Object[] objs) {\r
3260                         if (objs == null || getInput() == null) {\r
3261                                 return;\r
3262                         }\r
3263                         Object input = getInput();\r
3264                         for (int i = 0; i < objs.length; i++) {\r
3265                                 if (objs[i].equals(input)) {\r
3266                                         refresh();\r
3267                                         break;\r
3268                                 }\r
3269                         }\r
3270                 }\r
3271         }\r
3272 \r
3273         /**\r
3274          * Compares items according to the history.\r
3275          */\r
3276         private class HistoryComparator implements Comparator {\r
3277 \r
3278                 /*\r
3279                  * (non-Javadoc)\r
3280                  * \r
3281                  * @see java.util.Comparator#compare(java.lang.Object, java.lang.Object)\r
3282                  */\r
3283                 public int compare(Object o1, Object o2) {\r
3284                         boolean h1 = isHistoryElement(o1);\r
3285                         boolean h2 = isHistoryElement(o2);\r
3286                         if (h1 == h2)\r
3287                                 return getItemsComparator().compare(o1, o2);\r
3288 \r
3289                         if (h1)\r
3290                                 return -2;\r
3291                         if (h2)\r
3292                                 return +2;\r
3293 \r
3294                         return 0;\r
3295                 }\r
3296 \r
3297         }\r
3298         \r
3299 \r
3300         /**\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
3305          * \r
3306          * @return Control or <code>null</code> if the pattern control has not\r
3307          *         been created.\r
3308          */\r
3309         public Control getPatternControl() {\r
3310                 return pattern;\r
3311         }\r
3312 \r
3313 }\r
3314 \r