KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > eclipse > ui > dialogs > FilteredItemsSelectionDialog


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

11 package org.eclipse.ui.dialogs;
12
13 import java.io.IOException JavaDoc;
14 import java.io.StringReader JavaDoc;
15 import java.io.StringWriter JavaDoc;
16 import java.util.ArrayList JavaDoc;
17 import java.util.Arrays JavaDoc;
18 import java.util.Collections JavaDoc;
19 import java.util.Comparator JavaDoc;
20 import java.util.HashMap JavaDoc;
21 import java.util.HashSet JavaDoc;
22 import java.util.Iterator JavaDoc;
23 import java.util.LinkedList JavaDoc;
24 import java.util.List JavaDoc;
25 import java.util.Set JavaDoc;
26
27 import org.eclipse.core.runtime.Assert;
28 import org.eclipse.core.runtime.CoreException;
29 import org.eclipse.core.runtime.IProgressMonitor;
30 import org.eclipse.core.runtime.IStatus;
31 import org.eclipse.core.runtime.ListenerList;
32 import org.eclipse.core.runtime.ProgressMonitorWrapper;
33 import org.eclipse.core.runtime.Status;
34 import org.eclipse.core.runtime.SubProgressMonitor;
35 import org.eclipse.core.runtime.jobs.Job;
36 import org.eclipse.jface.action.Action;
37 import org.eclipse.jface.action.ActionContributionItem;
38 import org.eclipse.jface.action.IAction;
39 import org.eclipse.jface.action.IMenuListener;
40 import org.eclipse.jface.action.IMenuManager;
41 import org.eclipse.jface.action.MenuManager;
42 import org.eclipse.jface.dialogs.IDialogSettings;
43 import org.eclipse.jface.viewers.ContentViewer;
44 import org.eclipse.jface.viewers.DoubleClickEvent;
45 import org.eclipse.jface.viewers.IColorProvider;
46 import org.eclipse.jface.viewers.IContentProvider;
47 import org.eclipse.jface.viewers.IDoubleClickListener;
48 import org.eclipse.jface.viewers.IFontProvider;
49 import org.eclipse.jface.viewers.ILabelDecorator;
50 import org.eclipse.jface.viewers.ILabelProvider;
51 import org.eclipse.jface.viewers.ILabelProviderListener;
52 import org.eclipse.jface.viewers.ILazyContentProvider;
53 import org.eclipse.jface.viewers.ISelection;
54 import org.eclipse.jface.viewers.ISelectionChangedListener;
55 import org.eclipse.jface.viewers.IStructuredContentProvider;
56 import org.eclipse.jface.viewers.LabelProvider;
57 import org.eclipse.jface.viewers.LabelProviderChangedEvent;
58 import org.eclipse.jface.viewers.SelectionChangedEvent;
59 import org.eclipse.jface.viewers.StructuredSelection;
60 import org.eclipse.jface.viewers.TableViewer;
61 import org.eclipse.jface.viewers.Viewer;
62 import org.eclipse.jface.viewers.ViewerFilter;
63 import org.eclipse.osgi.util.NLS;
64 import org.eclipse.swt.SWT;
65 import org.eclipse.swt.custom.CLabel;
66 import org.eclipse.swt.custom.ViewForm;
67 import org.eclipse.swt.events.KeyAdapter;
68 import org.eclipse.swt.events.KeyEvent;
69 import org.eclipse.swt.events.ModifyEvent;
70 import org.eclipse.swt.events.ModifyListener;
71 import org.eclipse.swt.events.MouseAdapter;
72 import org.eclipse.swt.events.MouseEvent;
73 import org.eclipse.swt.events.SelectionAdapter;
74 import org.eclipse.swt.events.SelectionEvent;
75 import org.eclipse.swt.events.TraverseEvent;
76 import org.eclipse.swt.events.TraverseListener;
77 import org.eclipse.swt.graphics.Color;
78 import org.eclipse.swt.graphics.Font;
79 import org.eclipse.swt.graphics.GC;
80 import org.eclipse.swt.graphics.Image;
81 import org.eclipse.swt.graphics.Point;
82 import org.eclipse.swt.graphics.Rectangle;
83 import org.eclipse.swt.layout.GridData;
84 import org.eclipse.swt.layout.GridLayout;
85 import org.eclipse.swt.widgets.Composite;
86 import org.eclipse.swt.widgets.Control;
87 import org.eclipse.swt.widgets.Display;
88 import org.eclipse.swt.widgets.Event;
89 import org.eclipse.swt.widgets.Label;
90 import org.eclipse.swt.widgets.Menu;
91 import org.eclipse.swt.widgets.Shell;
92 import org.eclipse.swt.widgets.Text;
93 import org.eclipse.swt.widgets.ToolBar;
94 import org.eclipse.swt.widgets.ToolItem;
95 import org.eclipse.ui.IMemento;
96 import org.eclipse.ui.PlatformUI;
97 import org.eclipse.ui.WorkbenchException;
98 import org.eclipse.ui.XMLMemento;
99 import org.eclipse.ui.internal.IWorkbenchGraphicConstants;
100 import org.eclipse.ui.internal.WorkbenchImages;
101 import org.eclipse.ui.internal.WorkbenchMessages;
102 import org.eclipse.ui.internal.WorkbenchPlugin;
103 import org.eclipse.ui.progress.UIJob;
104 import org.eclipse.ui.statushandlers.StatusManager;
105
106 /**
107  * Shows a list of items to the user with a text entry field for a string
108  * pattern used to filter the list of items.
109  *
110  * @since 3.3
111  */

112 public abstract class FilteredItemsSelectionDialog extends
113         SelectionStatusDialog {
114
115     private static final String JavaDoc DIALOG_BOUNDS_SETTINGS = "DialogBoundsSettings"; //$NON-NLS-1$
116

117     private static final String JavaDoc SHOW_STATUS_LINE = "ShowStatusLine"; //$NON-NLS-1$
118

119     private static final String JavaDoc HISTORY_SETTINGS = "History"; //$NON-NLS-1$
120

121     private static final String JavaDoc DIALOG_HEIGHT = "DIALOG_HEIGHT"; //$NON-NLS-1$
122

123     private static final String JavaDoc DIALOG_WIDTH = "DIALOG_WIDTH"; //$NON-NLS-1$
124

125     /**
126      * Represents an empty selection in the pattern input field (used only for
127      * initial pattern).
128      */

129     public static final int NONE = 0;
130
131     /**
132      * Pattern input field selection where caret is at the beginning (used only
133      * for initial pattern).
134      */

135     public static final int CARET_BEGINNING = 1;
136
137     /**
138      * Represents a full selection in the pattern input field (used only for
139      * initial pattern).
140      */

141     public static final int FULL_SELECTION = 2;
142
143     private Text pattern;
144
145     private TableViewer list;
146
147     private DetailsContentViewer details;
148
149     /**
150      * It is a duplicate of a field in the CLabel class in DetailsContentViewer.
151      * It is maintained, because the <code>setDetailsLabelProvider()</code>
152      * could be called before content area is created.
153      */

154     private ILabelProvider detailsLabelProvider;
155
156     private ItemsListLabelProvider itemsListLabelProvider;
157
158     private MenuManager menuManager;
159
160     private boolean multi;
161
162     private ToolBar toolBar;
163
164     private ToolItem toolItem;
165
166     private Label progressLabel;
167
168     private ToggleStatusLineAction toggleStatusLineAction;
169
170     private RemoveHistoryItemAction removeHistoryItemAction;
171
172     private ActionContributionItem removeHistoryActionContributionItem;
173
174     private IStatus status;
175
176     private RefreshCacheJob refreshCacheJob;
177
178     private RefreshProgressMessageJob refreshProgressMessageJob = new RefreshProgressMessageJob();
179
180     private Object JavaDoc[] lastSelection;
181
182     private ContentProvider contentProvider;
183
184     private FilterHistoryJob filterHistoryJob;
185
186     private FilterJob filterJob;
187
188     private ItemsFilter filter;
189
190     private List JavaDoc lastCompletedResult;
191
192     private ItemsFilter lastCompletedFilter;
193
194     private String JavaDoc initialPatternText;
195
196     private int selectionMode;
197
198     private ItemsListSeparator itemsListSeparator;
199
200     private static final String JavaDoc EMPTY_STRING = ""; //$NON-NLS-1$
201

202     private boolean refreshWithLastSelection = false;
203
204     /**
205      * Creates a new instance of the class.
206      *
207      * @param shell
208      * shell to parent the dialog on
209      * @param multi
210      * indicates whether dialog allows to select more than one
211      * position in its list of items
212      */

213     public FilteredItemsSelectionDialog(Shell shell, boolean multi) {
214         super(shell);
215         setShellStyle(getShellStyle() | SWT.RESIZE);
216         this.multi = multi;
217         filterHistoryJob = new FilterHistoryJob();
218         filterJob = new FilterJob();
219         contentProvider = new ContentProvider();
220         refreshCacheJob = new RefreshCacheJob();
221         itemsListSeparator = new ItemsListSeparator(
222                 WorkbenchMessages.FilteredItemsSelectionDialog_separatorLabel);
223         selectionMode = NONE;
224     }
225
226     /**
227      * Creates a new instance of the class. Created dialog won't allow to select
228      * more than one item.
229      *
230      * @param shell
231      * shell to parent the dialog on
232      */

233     public FilteredItemsSelectionDialog(Shell shell) {
234         this(shell, false);
235     }
236
237     /**
238      * Adds viewer filter to the dialog items list.
239      *
240      * @param filter
241      * the new filter
242      */

243     protected void addListFilter(ViewerFilter filter) {
244         contentProvider.addFilter(filter);
245     }
246
247     /**
248      * Sets a new label provider for items in the list.
249      *
250      * @param listLabelProvider
251      * the label provider for items in the list
252      */

253     public void setListLabelProvider(ILabelProvider listLabelProvider) {
254         getItemsListLabelProvider().setProvider(listLabelProvider);
255     }
256
257     /**
258      * Returns the label decorator for selected items in the list.
259      *
260      * @return the label decorator for selected items in the list
261      */

262     private ILabelDecorator getListSelectionLabelDecorator() {
263         return getItemsListLabelProvider().getSelectionDecorator();
264     }
265
266     /**
267      * Sets the label decorator for selected items in the list.
268      *
269      * @param listSelectionLabelDecorator
270      * the label decorator for selected items in the list
271      */

272     public void setListSelectionLabelDecorator(
273             ILabelDecorator listSelectionLabelDecorator) {
274         getItemsListLabelProvider().setSelectionDecorator(
275                 listSelectionLabelDecorator);
276     }
277
278     /**
279      * Returns the item list label provider.
280      *
281      * @return the item list label provider
282      */

283     private ItemsListLabelProvider getItemsListLabelProvider() {
284         if (itemsListLabelProvider == null) {
285             itemsListLabelProvider = new ItemsListLabelProvider(
286                     new LabelProvider(), null);
287         }
288         return itemsListLabelProvider;
289     }
290
291     /**
292      * Sets label provider for the details field.
293      *
294      * For a single selection, the element sent to
295      * {@link ILabelProvider#getImage(Object)} and
296      * {@link ILabelProvider#getText(Object)} is the selected object, for
297      * multiple selection a {@link String} with amount of selected items is the
298      * element.
299      *
300      * @see #getSelectedItems() getSelectedItems() can be used to retrieve
301      * selected items and get the items count.
302      *
303      * @param detailsLabelProvider
304      * the label provider for the details field
305      */

306     public void setDetailsLabelProvider(ILabelProvider detailsLabelProvider) {
307         this.detailsLabelProvider = detailsLabelProvider;
308         if (details != null) {
309             details.setLabelProvider(detailsLabelProvider);
310         }
311     }
312
313     private ILabelProvider getDetailsLabelProvider() {
314         if (detailsLabelProvider == null) {
315             detailsLabelProvider = new LabelProvider();
316         }
317         return detailsLabelProvider;
318     }
319
320     /*
321      * (non-Javadoc)
322      *
323      * @see org.eclipse.jface.window.Window#create()
324      */

325     public void create() {
326         super.create();
327         pattern.setFocus();
328     }
329
330     /**
331      * Restores dialog using persisted settings. The default implementation
332      * restores the status of the details line and the selection history.
333      *
334      * @param settings
335      * settings used to restore dialog
336      */

337     protected void restoreDialog(IDialogSettings settings) {
338         boolean toggleStatusLine = true;
339
340         if (settings.get(SHOW_STATUS_LINE) != null) {
341             toggleStatusLine = settings.getBoolean(SHOW_STATUS_LINE);
342         }
343
344         toggleStatusLineAction.setChecked(toggleStatusLine);
345
346         details.setVisible(toggleStatusLine);
347
348         String JavaDoc setting = settings.get(HISTORY_SETTINGS);
349         if (setting != null) {
350             try {
351                 IMemento memento = XMLMemento.createReadRoot(new StringReader JavaDoc(
352                         setting));
353                 this.contentProvider.loadHistory(memento);
354             } catch (WorkbenchException e) {
355                 // Simply don't restore the settings
356
StatusManager
357                         .getManager()
358                         .handle(
359                                 new Status(
360                                         IStatus.ERROR,
361                                         PlatformUI.PLUGIN_ID,
362                                         IStatus.ERROR,
363                                         WorkbenchMessages.FilteredItemsSelectionDialog_restoreError,
364                                         e));
365             }
366         }
367     }
368
369     /*
370      * (non-Javadoc)
371      *
372      * @see org.eclipse.jface.window.Window#close()
373      */

374     public boolean close() {
375         this.filterJob.cancel();
376         this.refreshCacheJob.cancel();
377         this.refreshProgressMessageJob.cancel();
378         storeDialog(getDialogSettings());
379         return super.close();
380     }
381
382     /**
383      * Stores dialog settings.
384      *
385      * @param settings
386      * settings used to store dialog
387      */

388     protected void storeDialog(IDialogSettings settings) {
389         settings.put(SHOW_STATUS_LINE, toggleStatusLineAction.isChecked());
390
391         XMLMemento memento = XMLMemento.createWriteRoot(HISTORY_SETTINGS);
392         this.contentProvider.saveHistory(memento);
393         StringWriter JavaDoc writer = new StringWriter JavaDoc();
394         try {
395             memento.save(writer);
396             settings.put(HISTORY_SETTINGS, writer.getBuffer().toString());
397         } catch (IOException JavaDoc e) {
398             // Simply don't store the settings
399
StatusManager
400                     .getManager()
401                     .handle(
402                             new Status(
403                                     IStatus.ERROR,
404                                     PlatformUI.PLUGIN_ID,
405                                     IStatus.ERROR,
406                                     WorkbenchMessages.FilteredItemsSelectionDialog_storeError,
407                                     e));
408         }
409     }
410
411     private void createHeader(Composite parent) {
412         Composite header = new Composite(parent, SWT.NONE);
413
414         GridLayout layout = new GridLayout();
415         layout.numColumns = 2;
416         layout.marginWidth = 0;
417         layout.marginHeight = 0;
418         header.setLayout(layout);
419
420         Label label = new Label(header, SWT.NONE);
421         label
422                 .setText((getMessage() != null && getMessage().trim().length() > 0) ? getMessage()
423                         : WorkbenchMessages.FilteredItemsSelectionDialog_patternLabel);
424         label.addTraverseListener(new TraverseListener() {
425             public void keyTraversed(TraverseEvent e) {
426                 if (e.detail == SWT.TRAVERSE_MNEMONIC && e.doit) {
427                     e.detail = SWT.TRAVERSE_NONE;
428                     pattern.setFocus();
429                 }
430             }
431         });
432
433         GridData gd = new GridData(GridData.FILL_HORIZONTAL);
434         label.setLayoutData(gd);
435
436         createViewMenu(header);
437         header.setLayoutData(gd);
438     }
439
440     private void createLabels(Composite parent) {
441         Composite labels = new Composite(parent, SWT.NONE);
442
443         GridLayout layout = new GridLayout();
444         layout.numColumns = 2;
445         layout.marginWidth = 0;
446         layout.marginHeight = 0;
447         labels.setLayout(layout);
448
449         Label listLabel = new Label(labels, SWT.NONE);
450         listLabel
451                 .setText(WorkbenchMessages.FilteredItemsSelectionDialog_listLabel);
452
453         listLabel.addTraverseListener(new TraverseListener() {
454             public void keyTraversed(TraverseEvent e) {
455                 if (e.detail == SWT.TRAVERSE_MNEMONIC && e.doit) {
456                     e.detail = SWT.TRAVERSE_NONE;
457                     list.getTable().setFocus();
458                 }
459             }
460         });
461
462         GridData gd = new GridData(GridData.FILL_HORIZONTAL);
463         listLabel.setLayoutData(gd);
464
465         progressLabel = new Label(labels, SWT.RIGHT);
466         progressLabel.setLayoutData(gd);
467
468         labels.setLayoutData(gd);
469     }
470
471     private void createViewMenu(Composite parent) {
472         toolBar = new ToolBar(parent, SWT.FLAT);
473         toolItem = new ToolItem(toolBar, SWT.PUSH, 0);
474
475         GridData data = new GridData();
476         data.horizontalAlignment = GridData.END;
477         toolBar.setLayoutData(data);
478
479         toolBar.addMouseListener(new MouseAdapter() {
480             public void mouseDown(MouseEvent e) {
481                 showViewMenu();
482             }
483         });
484
485         toolItem.setImage(WorkbenchImages
486                 .getImage(IWorkbenchGraphicConstants.IMG_LCL_VIEW_MENU));
487         toolItem
488                 .setToolTipText(WorkbenchMessages.FilteredItemsSelectionDialog_menu);
489         toolItem.addSelectionListener(new SelectionAdapter() {
490             public void widgetSelected(SelectionEvent e) {
491                 showViewMenu();
492             }
493         });
494
495         menuManager = new MenuManager();
496
497         fillViewMenu(menuManager);
498     }
499
500     /**
501      * Fills the menu of the dialog.
502      *
503      * @param menuManager
504      * the menu manager
505      */

506     protected void fillViewMenu(IMenuManager menuManager) {
507         toggleStatusLineAction = new ToggleStatusLineAction();
508         menuManager.add(toggleStatusLineAction);
509     }
510
511     private void showViewMenu() {
512         Menu menu = menuManager.createContextMenu(getShell());
513         Rectangle bounds = toolItem.getBounds();
514         Point topLeft = new Point(bounds.x, bounds.y + bounds.height);
515         topLeft = toolBar.toDisplay(topLeft);
516         menu.setLocation(topLeft.x, topLeft.y);
517         menu.setVisible(true);
518     }
519
520     private void createPopupMenu() {
521         removeHistoryItemAction = new RemoveHistoryItemAction();
522         removeHistoryActionContributionItem = new ActionContributionItem(
523                 removeHistoryItemAction);
524
525         MenuManager manager = new MenuManager();
526         manager.add(removeHistoryActionContributionItem);
527         manager.addMenuListener(new IMenuListener() {
528             public void menuAboutToShow(IMenuManager manager) {
529                 List JavaDoc selectedElements = ((StructuredSelection) list
530                         .getSelection()).toList();
531
532                 Object JavaDoc item = null;
533
534                 manager.remove(removeHistoryActionContributionItem);
535
536                 for (Iterator JavaDoc it = selectedElements.iterator(); it.hasNext();) {
537                     item = it.next();
538                     if (item instanceof ItemsListSeparator
539                             || !isHistoryElement(item)) {
540                         return;
541                     }
542                 }
543
544                 if (selectedElements.size() > 0) {
545                     removeHistoryItemAction
546                             .setText(WorkbenchMessages.FilteredItemsSelectionDialog_removeItemsFromHistoryAction);
547
548                     manager.add(removeHistoryActionContributionItem);
549
550                 }
551             }
552         });
553
554         Menu menu = manager.createContextMenu(getShell());
555         list.getTable().setMenu(menu);
556     }
557
558     /**
559      * Creates an extra content area, which will be located above the details.
560      *
561      * @param parent
562      * parent to create the dialog widgets in
563      * @return an extra content area
564      */

565     protected abstract Control createExtendedContentArea(Composite parent);
566
567     /*
568      * (non-Javadoc)
569      *
570      * @see org.eclipse.jface.dialogs.Dialog#createDialogArea(org.eclipse.swt.widgets.Composite)
571      */

572     protected Control createDialogArea(Composite parent) {
573         Composite dialogArea = (Composite) super.createDialogArea(parent);
574
575         Composite content = new Composite(dialogArea, SWT.NONE);
576         GridData gd = new GridData(GridData.FILL_BOTH);
577         content.setLayoutData(gd);
578
579         GridLayout layout = new GridLayout();
580         layout.numColumns = 1;
581         layout.marginWidth = 0;
582         layout.marginHeight = 0;
583         content.setLayout(layout);
584
585         createHeader(content);
586
587         pattern = new Text(content, SWT.SINGLE | SWT.BORDER);
588         gd = new GridData(GridData.FILL_HORIZONTAL);
589         pattern.setLayoutData(gd);
590
591         createLabels(content);
592
593         list = new TableViewer(content, (multi ? SWT.MULTI : SWT.SINGLE)
594                 | SWT.BORDER | SWT.V_SCROLL | SWT.VIRTUAL);
595         list.setContentProvider(contentProvider);
596         list.setLabelProvider(getItemsListLabelProvider());
597         list.setInput(new Object JavaDoc[0]);
598         list.setItemCount(contentProvider.getElements(null).length);
599         gd = new GridData(GridData.FILL_BOTH);
600         list.getTable().setLayoutData(gd);
601
602         createPopupMenu();
603
604         pattern.addModifyListener(new ModifyListener() {
605             public void modifyText(ModifyEvent e) {
606                 applyFilter();
607             }
608         });
609
610         pattern.addKeyListener(new KeyAdapter() {
611             public void keyPressed(KeyEvent e) {
612                 if (e.keyCode == SWT.ARROW_DOWN) {
613                     if (list.getTable().getItemCount() > 0) {
614                         list.getTable().setFocus();
615                     }
616                 }
617             }
618         });
619
620         list.addSelectionChangedListener(new ISelectionChangedListener() {
621             public void selectionChanged(SelectionChangedEvent event) {
622                 StructuredSelection selection = (StructuredSelection) event
623                         .getSelection();
624                 handleSelected(selection);
625             }
626         });
627
628         list.addDoubleClickListener(new IDoubleClickListener() {
629             public void doubleClick(DoubleClickEvent event) {
630                 handleDoubleClick();
631             }
632         });
633
634         list.getTable().addKeyListener(new KeyAdapter() {
635             public void keyPressed(KeyEvent e) {
636
637                 if (e.keyCode == SWT.DEL) {
638
639                     List JavaDoc selectedElements = ((StructuredSelection) list
640                             .getSelection()).toList();
641
642                     Object JavaDoc item = null;
643                     boolean isSelectedHistory = true;
644
645                     for (Iterator JavaDoc it = selectedElements.iterator(); it
646                             .hasNext();) {
647                         item = it.next();
648                         if (item instanceof ItemsListSeparator
649                                 || !isHistoryElement(item)) {
650                             isSelectedHistory = false;
651                             break;
652                         }
653                     }
654                     if (isSelectedHistory)
655                         removeSelectedItems(selectedElements);
656
657                 }
658
659                 if (e.keyCode == SWT.ARROW_UP && (e.stateMask & SWT.SHIFT) != 0
660                         && (e.stateMask & SWT.CTRL) != 0) {
661                     StructuredSelection selection = (StructuredSelection) list
662                             .getSelection();
663
664                     if (selection.size() == 1) {
665                         Object JavaDoc element = selection.getFirstElement();
666                         if (element.equals(list.getElementAt(0))) {
667                             pattern.setFocus();
668                         }
669                         if (list.getElementAt(list.getTable()
670                                 .getSelectionIndex() - 1) instanceof ItemsListSeparator)
671                             list.getTable().setSelection(
672                                     list.getTable().getSelectionIndex() - 1);
673                         list.getTable().notifyListeners(SWT.Selection,
674                                 new Event());
675
676                     }
677                 }
678
679                 if (e.keyCode == SWT.ARROW_DOWN
680                         && (e.stateMask & SWT.SHIFT) != 0
681                         && (e.stateMask & SWT.CTRL) != 0) {
682
683                     if (list
684                             .getElementAt(list.getTable().getSelectionIndex() + 1) instanceof ItemsListSeparator)
685                         list.getTable().setSelection(
686                                 list.getTable().getSelectionIndex() + 1);
687                     list.getTable().notifyListeners(SWT.Selection, new Event());
688                 }
689
690             }
691         });
692
693         createExtendedContentArea(content);
694
695         details = new DetailsContentViewer(content, SWT.BORDER | SWT.FLAT);
696         details.setVisible(toggleStatusLineAction.isChecked());
697         details.setContentProvider(new NullContentProvider());
698         details.setLabelProvider(getDetailsLabelProvider());
699
700         applyDialogFont(content);
701
702         restoreDialog(getDialogSettings());
703
704         if (initialPatternText != null) {
705             pattern.setText(initialPatternText);
706         }
707
708         switch (selectionMode) {
709         case CARET_BEGINNING:
710             pattern.setSelection(0, 0);
711             break;
712         case FULL_SELECTION:
713             pattern.setSelection(0, initialPatternText.length());
714             break;
715         }
716
717         // apply filter even if pattern is empty (display history)
718
applyFilter();
719
720         return dialogArea;
721     }
722
723     /**
724      * This method is a hook for subclasses to override default dialog behavior.
725      * The <code>handleDoubleClick()</code> method handles double clicks on
726      * the list of filtered elements.
727      * <p>
728      * Current implementation makes double-clicking on the list do the same as
729      * pressing <code>OK</code> button on the dialog.
730      */

731     protected void handleDoubleClick() {
732         okPressed();
733     }
734
735     /**
736      * Refreshes the details field according to the current selection in the
737      * items list.
738      */

739     private void refreshDetails() {
740         StructuredSelection selection = getSelectedItems();
741
742         switch (selection.size()) {
743         case 0:
744             details.setInput(null);
745             break;
746         case 1:
747             details.setInput(selection.getFirstElement());
748             break;
749         default:
750             details
751                     .setInput(NLS
752                             .bind(
753                                     WorkbenchMessages.FilteredItemsSelectionDialog_nItemsSelected,
754                                     new Integer JavaDoc(selection.size())));
755             break;
756         }
757
758     }
759
760     /**
761      * Handle selection in the items list by updating labels of selected and
762      * unselected items and refresh the details field using the selection.
763      *
764      * @param selection
765      * the new selection
766      */

767     protected void handleSelected(StructuredSelection selection) {
768         IStatus status = new Status(IStatus.OK, PlatformUI.PLUGIN_ID,
769                 IStatus.OK, EMPTY_STRING, null);
770
771         if (selection.size() == 0) {
772             status = new Status(IStatus.ERROR, PlatformUI.PLUGIN_ID,
773                     IStatus.ERROR, EMPTY_STRING, null);
774
775             if (lastSelection != null
776                     && getListSelectionLabelDecorator() != null) {
777                 list.update(lastSelection, null);
778             }
779
780             lastSelection = null;
781
782         } else {
783             status = new Status(IStatus.ERROR, PlatformUI.PLUGIN_ID,
784                     IStatus.ERROR, EMPTY_STRING, null);
785
786             List JavaDoc items = selection.toList();
787
788             Object JavaDoc item = null;
789             IStatus tempStatus = null;
790
791             for (Iterator JavaDoc it = items.iterator(); it.hasNext();) {
792                 Object JavaDoc o = it.next();
793
794                 if (o instanceof ItemsListSeparator) {
795                     continue;
796                 }
797
798                 item = o;
799                 tempStatus = validateItem(item);
800
801                 if (tempStatus.isOK()) {
802                     status = new Status(IStatus.OK, PlatformUI.PLUGIN_ID,
803                             IStatus.OK, EMPTY_STRING, null);
804                 } else {
805                     status = tempStatus;
806                     // if any selected element is not valid status is set to
807
// ERROR
808
break;
809                 }
810             }
811
812             if (lastSelection != null
813                     && getListSelectionLabelDecorator() != null) {
814                 list.update(lastSelection, null);
815             }
816
817             if (getListSelectionLabelDecorator() != null) {
818                 list.update(items.toArray(), null);
819             }
820
821             lastSelection = items.toArray();
822         }
823
824         refreshDetails();
825         updateStatus(status);
826     }
827
828     /*
829      * (non-Javadoc)
830      *
831      * @see org.eclipse.jface.window.Dialog#getDialogBoundsSettings()
832      */

833     protected IDialogSettings getDialogBoundsSettings() {
834         IDialogSettings settings = getDialogSettings();
835         IDialogSettings section = settings.getSection(DIALOG_BOUNDS_SETTINGS);
836         if (section == null) {
837             section = settings.addNewSection(DIALOG_BOUNDS_SETTINGS);
838             section.put(DIALOG_HEIGHT, 500);
839             section.put(DIALOG_WIDTH, 600);
840         }
841         return section;
842     }
843
844     /**
845      * Returns the dialog settings. Returned object can't be null.
846      *
847      * @return return dialog settings for this dialog
848      */

849     protected abstract IDialogSettings getDialogSettings();
850
851     /**
852      * Refreshes the dialog - has to be called in UI thread.
853      */

854     public void refresh() {
855         if (list != null && !list.getTable().isDisposed()) {
856
857             List JavaDoc lastRefreshSelection = ((StructuredSelection) list
858                     .getSelection()).toList();
859
860             list.setItemCount(contentProvider.getElements(null).length);
861             list.refresh();
862
863             if (list.getTable().getItemCount() > 0) {
864                 // preserve previous selection
865
if (refreshWithLastSelection && lastRefreshSelection != null
866                         && lastRefreshSelection.size() > 0) {
867                     list.setSelection(new StructuredSelection(
868                             lastRefreshSelection));
869                 } else {
870                     refreshWithLastSelection = true;
871                     list.getTable().setSelection(0);
872                     list.getTable().notifyListeners(SWT.Selection, new Event());
873                 }
874             } else {
875                 list.setSelection(StructuredSelection.EMPTY);
876             }
877
878         }
879
880         scheduleProgressMessageRefresh();
881     }
882
883     /**
884      * Updates the progress label.
885      *
886      * @deprecated
887      */

888     public void updateProgressLabel() {
889         scheduleProgressMessageRefresh();
890     }
891
892     /**
893      * Notifies the content provider - fires filtering of content provider
894      * elements. During the filtering, a separator between history and workspace
895      * matches is added.
896      * <p>
897      * This is a long running operation and should be called in a job.
898      *
899      * @param checkDuplicates
900      * <code>true</code> if data concerning elements duplication
901      * should be computed - it takes much more time than the standard
902      * filtering
903      * @param monitor
904      * a progress monitor or <code>null</code> if no monitor is
905      * available
906      */

907     public void reloadCache(boolean checkDuplicates, IProgressMonitor monitor) {
908         if (list != null && !list.getTable().isDisposed()
909                 && contentProvider != null) {
910             contentProvider.reloadCache(checkDuplicates, monitor);
911         }
912     }
913
914     /**
915      * Schedule refresh job.
916      */

917     public void scheduleRefresh() {
918         refreshCacheJob.cancelAll();
919         refreshCacheJob.schedule();
920     }
921
922     /**
923      * Schedules progress message refresh.
924      */

925     public void scheduleProgressMessageRefresh() {
926         if (filterJob.getState() != Job.RUNNING
927                 && refreshProgressMessageJob.getState() != Job.RUNNING)
928             refreshProgressMessageJob.scheduleProgressRefresh(null);
929     }
930
931     /*
932      * (non-Javadoc)
933      *
934      * @see org.eclipse.ui.dialogs.SelectionStatusDialog#computeResult()
935      */

936     protected void computeResult() {
937
938         List JavaDoc selectedElements = ((StructuredSelection) list.getSelection())
939                 .toList();
940
941         List JavaDoc objectsToReturn = new ArrayList JavaDoc();
942
943         Object JavaDoc item = null;
944
945         for (Iterator JavaDoc it = selectedElements.iterator(); it.hasNext();) {
946             item = it.next();
947
948             if (!(item instanceof ItemsListSeparator)) {
949                 accessedHistoryItem(item);
950                 objectsToReturn.add(item);
951             }
952         }
953
954         setResult(objectsToReturn);
955     }
956
957     /*
958      * @see org.eclipse.ui.dialogs.SelectionStatusDialog#updateStatus(org.eclipse.core.runtime.IStatus)
959      */

960     protected void updateStatus(IStatus status) {
961         this.status = status;
962         super.updateStatus(status);
963     }
964
965     /*
966      * @see Dialog#okPressed()
967      */

968     protected void okPressed() {
969         if (status != null
970                 && (status.isOK() || status.getCode() == IStatus.INFO)) {
971             super.okPressed();
972         }
973     }
974
975     /**
976      * Sets the initial pattern used by the filter. This text is copied into the
977      * selection input on the dialog. A full selection is used in the pattern
978      * input field.
979      *
980      * @param text
981      * initial pattern for the filter
982      * @see FilteredItemsSelectionDialog#FULL_SELECTION
983      */

984     public void setInitialPattern(String JavaDoc text) {
985         setInitialPattern(text, FULL_SELECTION);
986     }
987
988     /**
989      * Sets the initial pattern used by the filter. This text is copied into the
990      * selection input on the dialog. The <code>selectionMode</code> is used
991      * to choose selection type for the input field.
992      *
993      * @param text
994      * initial pattern for the filter
995      * @param selectionMode
996      * one of: {@link FilteredItemsSelectionDialog#NONE},
997      * {@link FilteredItemsSelectionDialog#CARET_BEGINNING},
998      * {@link FilteredItemsSelectionDialog#FULL_SELECTION}
999      */

1000    public void setInitialPattern(String JavaDoc text, int selectionMode) {
1001        this.initialPatternText = text;
1002        this.selectionMode = selectionMode;
1003    }
1004
1005    /**
1006     * Gets initial pattern.
1007     *
1008     * @return initial pattern, or <code>null</code> if initial pattern is not
1009     * set
1010     */

1011    protected String JavaDoc getInitialPattern() {
1012        return this.initialPatternText;
1013    }
1014
1015    /**
1016     * Returns the current selection.
1017     *
1018     * @return the current selection
1019     */

1020    protected StructuredSelection getSelectedItems() {
1021
1022        StructuredSelection selection = (StructuredSelection) list
1023                .getSelection();
1024
1025        List JavaDoc selectedItems = selection.toList();
1026        Object JavaDoc itemToRemove = null;
1027
1028        for (Iterator JavaDoc it = selection.iterator(); it.hasNext();) {
1029            Object JavaDoc item = it.next();
1030            if (item instanceof ItemsListSeparator) {
1031                itemToRemove = item;
1032                break;
1033            }
1034        }
1035
1036        if (itemToRemove == null)
1037            return new StructuredSelection(selectedItems);
1038        // Create a new selection without the collision
1039
List JavaDoc newItems = new ArrayList JavaDoc(selectedItems);
1040        newItems.remove(itemToRemove);
1041        return new StructuredSelection(newItems);
1042
1043    }
1044
1045    /**
1046     * Validates the item. When items on the items list are selected or
1047     * deselected, it validates each item in the selection and the dialog status
1048     * depends on all validations.
1049     *
1050     * @param item
1051     * an item to be checked
1052     * @return status of the dialog to be set
1053     */

1054    protected abstract IStatus validateItem(Object JavaDoc item);
1055
1056    /**
1057     * Creates an instance of a filter.
1058     *
1059     * @return a filter for items on the items list. Can be <code>null</code>,
1060     * no filtering will be applied then, causing no item to be shown in
1061     * the list.
1062     */

1063    protected abstract ItemsFilter createFilter();
1064
1065    /**
1066     * Applies the filter created by <code>createFilter()</code> method to the
1067     * items list. When new filter is different than previous one it will cause
1068     * refiltering.
1069     */

1070    protected void applyFilter() {
1071
1072        ItemsFilter newFilter = createFilter();
1073
1074        // don't apply filtering for patterns which mean the same, for example:
1075
// *a**b and ***a*b
1076
if (filter != null && filter.equalsFilter(newFilter)) {
1077            return;
1078        }
1079
1080        filterHistoryJob.cancel();
1081        filterJob.cancel();
1082
1083        this.filter = newFilter;
1084
1085        if (this.filter != null) {
1086            filterHistoryJob.schedule();
1087        }
1088    }
1089
1090    /**
1091     * Returns comparator to sort items inside content provider. Returned object
1092     * will be probably created as an anonymous class. Parameters passed to the
1093     * <code>compare(java.lang.Object, java.lang.Object)</code> are going to
1094     * be the same type as the one used in the content provider.
1095     *
1096     * @return comparator to sort items content provider
1097     */

1098    protected abstract Comparator JavaDoc getItemsComparator();
1099
1100    /**
1101     * Fills the content provider with matching items.
1102     *
1103     * @param contentProvider
1104     * collector to add items to.
1105     * {@link FilteredItemsSelectionDialog.AbstractContentProvider#add(Object, FilteredItemsSelectionDialog.ItemsFilter)}
1106     * only adds items that pass the given <code>itemsFilter</code>.
1107     * @param itemsFilter
1108     * the items filter
1109     * @param progressMonitor
1110     * must be used to report search progress. The state of this
1111     * progress monitor reflects the state of the filtering process.
1112     * @throws CoreException
1113     */

1114    protected abstract void fillContentProvider(
1115            AbstractContentProvider contentProvider, ItemsFilter itemsFilter,
1116            IProgressMonitor progressMonitor) throws CoreException;
1117
1118    /**
1119     * Removes selected items from history.
1120     *
1121     * @param items
1122     * items to be removed
1123     */

1124    private void removeSelectedItems(List JavaDoc items) {
1125        for (Iterator JavaDoc iter = items.iterator(); iter.hasNext();) {
1126            Object JavaDoc item = iter.next();
1127            removeHistoryItem(item);
1128        }
1129        refreshWithLastSelection = false;
1130        contentProvider.refresh();
1131    }
1132
1133    /**
1134     * Removes an item from history.
1135     *
1136     * @param item
1137     * an item to remove
1138     * @return removed item
1139     */

1140    protected Object JavaDoc removeHistoryItem(Object JavaDoc item) {
1141        return contentProvider.removeHistoryElement(item);
1142    }
1143
1144    /**
1145     * Adds item to history.
1146     *
1147     * @param item
1148     * the item to be added
1149     */

1150    protected void accessedHistoryItem(Object JavaDoc item) {
1151        contentProvider.addHistoryElement(item);
1152    }
1153
1154    /**
1155     * Returns a history comparator.
1156     *
1157     * @return decorated comparator
1158     */

1159    private Comparator JavaDoc getHistoryComparator() {
1160        return new HistoryComparator();
1161    }
1162
1163    /**
1164     * Returns the history of selected elements.
1165     *
1166     * @return history of selected elements, or <code>null</code> if it is not
1167     * set
1168     */

1169    protected SelectionHistory getSelectionHistory() {
1170        return this.contentProvider.getSelectionHistory();
1171    }
1172
1173    /**
1174     * Sets new history.
1175     *
1176     * @param selectionHistory
1177     * the history
1178     */

1179    protected void setSelectionHistory(SelectionHistory selectionHistory) {
1180        if (this.contentProvider != null)
1181            this.contentProvider.setSelectionHistory(selectionHistory);
1182    }
1183
1184    /**
1185     * Indicates whether the given item is a history item.
1186     *
1187     * @param item
1188     * the item to be investigated
1189     * @return <code>true</code> if the given item exists in history,
1190     * <code>false</code> otherwise
1191     */

1192    public boolean isHistoryElement(Object JavaDoc item) {
1193        return this.contentProvider.isHistoryElement(item);
1194    }
1195
1196    /**
1197     * Indicates whether the given item is a duplicate.
1198     *
1199     * @param item
1200     * the item to be investigated
1201     * @return <code>true</code> if the item is duplicate, <code>false</code>
1202     * otherwise
1203     */

1204    public boolean isDuplicateElement(Object JavaDoc item) {
1205        return this.contentProvider.isDuplicateElement(item);
1206    }
1207
1208    /**
1209     * Sets separator label
1210     *
1211     * @param separatorLabel
1212     * the label showed on separator
1213     */

1214    public void setSeparatorLabel(String JavaDoc separatorLabel) {
1215        this.itemsListSeparator = new ItemsListSeparator(separatorLabel);
1216    }
1217
1218    /**
1219     * Returns name for then given object.
1220     *
1221     * @param item
1222     * an object from the content provider. Subclasses should pay
1223     * attention to the passed argument. They should either only pass
1224     * objects of a known type (one used in content provider) or make
1225     * sure that passed parameter is the expected one (by type
1226     * checking like <code>instanceof</code> inside the method).
1227     * @return name of the given item
1228     */

1229    public abstract String JavaDoc getElementName(Object JavaDoc item);
1230
1231    private class ToggleStatusLineAction extends Action {
1232
1233        /**
1234         * Creates a new instance of the class.
1235         */

1236        public ToggleStatusLineAction() {
1237            super(
1238                    WorkbenchMessages.FilteredItemsSelectionDialog_toggleStatusAction,
1239                    IAction.AS_CHECK_BOX);
1240        }
1241
1242        public void run() {
1243            details.setVisible(isChecked());
1244        }
1245    }
1246
1247    /**
1248     * Only refreshes UI on the basis of an already sorted and filtered set of
1249     * items.
1250     * <p>
1251     * Standard invocation scenario:
1252     * <ol>
1253     * <li>filtering job (<code>FilterJob</code> class extending
1254     * <code>Job</code> class)</li>
1255     * <li>cache refresh without checking for duplicates (<code>RefreshCacheJob</code>
1256     * class extending <code>Job</code> class)</li>
1257     * <li>UI refresh (<code>RefreshJob</code> class extending
1258     * <code>UIJob</code> class)</li>
1259     * <li>cache refresh with checking for duplicates (<cod>CacheRefreshJob</code>
1260     * class extending <code>Job</code> class)</li>
1261     * <li>UI refresh (<code>RefreshJob</code> class extending <code>UIJob</code>
1262     * class)</li>
1263     * </ol>
1264     * The scenario is rather complicated, but it had to be applied, because:
1265     * <ul>
1266     * <li> refreshing cache is rather a long action and cannot be run in the UI -
1267     * cannot be run in a UIJob</li>
1268     * <li> refreshing cache checking for duplicates is twice as long as
1269     * refreshing cache without checking for duplicates; results of the search
1270     * could be displayed earlier</li>
1271     * <li> refreshing the UI have to be run in a UIJob</li>
1272     * </ul>
1273     *
1274     * @see org.eclipse.ui.dialogs.FilteredItemsSelectionDialog.FilterJob
1275     * @see org.eclipse.ui.dialogs.FilteredItemsSelectionDialog.RefreshJob
1276     * @see org.eclipse.ui.dialogs.FilteredItemsSelectionDialog.RefreshCacheJob
1277     */

1278    private class RefreshJob extends UIJob {
1279
1280        /**
1281         * Creates a new instance of the class.
1282         */

1283        public RefreshJob() {
1284            super(FilteredItemsSelectionDialog.this.getParentShell()
1285                    .getDisplay(),
1286                    WorkbenchMessages.FilteredItemsSelectionDialog_refreshJob);
1287            setSystem(true);
1288        }
1289
1290        /*
1291         * (non-Javadoc)
1292         *
1293         * @see org.eclipse.ui.progress.UIJob#runInUIThread(org.eclipse.core.runtime.IProgressMonitor)
1294         */

1295        public IStatus runInUIThread(IProgressMonitor monitor) {
1296            if (monitor.isCanceled())
1297                return new Status(IStatus.OK, WorkbenchPlugin.PI_WORKBENCH,
1298                        IStatus.OK, EMPTY_STRING, null);
1299
1300            if (FilteredItemsSelectionDialog.this != null) {
1301                FilteredItemsSelectionDialog.this.refresh();
1302            }
1303
1304            return new Status(IStatus.OK, PlatformUI.PLUGIN_ID, IStatus.OK,
1305                    EMPTY_STRING, null);
1306        }
1307
1308    }
1309
1310    /**
1311     * Refreshes the progress message cyclically with 500 milliseconds delay.
1312     * <code>RefreshProgressMessageJob</code> is strictly connected with
1313     * <code>GranualProgressMonitor</code> and use it to to get progress
1314     * message and to decide about break of cyclical refresh.
1315     */

1316    private class RefreshProgressMessageJob extends UIJob {
1317
1318        private GranualProgressMonitor progressMonitor;
1319
1320        /**
1321         * Creates a new instance of the class.
1322         */

1323        public RefreshProgressMessageJob() {
1324            super(
1325                    FilteredItemsSelectionDialog.this.getParentShell()
1326                            .getDisplay(),
1327                    WorkbenchMessages.FilteredItemsSelectionDialog_progressRefreshJob);
1328            setSystem(true);
1329        }
1330
1331        /*
1332         * (non-Javadoc)
1333         *
1334         * @see org.eclipse.ui.progress.UIJob#runInUIThread(org.eclipse.core.runtime.IProgressMonitor)
1335         */

1336        public IStatus runInUIThread(IProgressMonitor monitor) {
1337
1338            if (!progressLabel.isDisposed())
1339                progressLabel.setText(progressMonitor != null ? progressMonitor
1340                        .getMessage() : EMPTY_STRING);
1341
1342            if (progressMonitor == null || progressMonitor.isDone()) {
1343                return new Status(IStatus.CANCEL, PlatformUI.PLUGIN_ID,
1344                        IStatus.CANCEL, EMPTY_STRING, null);
1345            }
1346
1347            // Schedule cyclical with 500 milliseconds delay
1348
schedule(500);
1349
1350            return new Status(IStatus.OK, PlatformUI.PLUGIN_ID, IStatus.OK,
1351                    EMPTY_STRING, null);
1352        }
1353
1354        /**
1355         * Schedule progress refresh job.
1356         *
1357         * @param progressMonitor
1358         * used during refresh progress label
1359         */

1360        public void scheduleProgressRefresh(
1361                GranualProgressMonitor progressMonitor) {
1362            this.progressMonitor = progressMonitor;
1363            // Schedule with initial delay to avoid flickering when the user
1364
// types quickly
1365
schedule(200);
1366        }
1367
1368    }
1369
1370    /**
1371     * A job responsible for computing filtered items list presented using
1372     * <code>RefreshJob</code>.
1373     *
1374     * @see RefreshJob
1375     *
1376     */

1377    private class RefreshCacheJob extends Job {
1378
1379        private RefreshJob refreshJob = new RefreshJob();
1380
1381        /**
1382         * Creates a new instance of the class.
1383         */

1384        public RefreshCacheJob() {
1385            super(
1386                    WorkbenchMessages.FilteredItemsSelectionDialog_cacheRefreshJob);
1387            setSystem(true);
1388        }
1389
1390        /**
1391         * Stops the job and all sub-jobs.
1392         */

1393        public void cancelAll() {
1394            cancel();
1395            refreshJob.cancel();
1396        }
1397
1398        /*
1399         * (non-Javadoc)
1400         *
1401         * @see org.eclipse.core.runtime.jobs.Job#run(org.eclipse.core.runtime.IProgressMonitor)
1402         */

1403        protected IStatus run(IProgressMonitor monitor) {
1404            if (monitor.isCanceled()) {
1405                return new Status(IStatus.CANCEL, WorkbenchPlugin.PI_WORKBENCH,
1406                        IStatus.CANCEL, EMPTY_STRING, null);
1407            }
1408
1409            if (FilteredItemsSelectionDialog.this != null) {
1410                GranualProgressMonitor wrappedMonitor = new GranualProgressMonitor(
1411                        monitor);
1412                FilteredItemsSelectionDialog.this.reloadCache(true,
1413                        wrappedMonitor);
1414            }
1415
1416            if (!monitor.isCanceled()) {
1417                refreshJob.schedule();
1418            }
1419
1420            return new Status(IStatus.OK, PlatformUI.PLUGIN_ID, IStatus.OK,
1421                    EMPTY_STRING, null);
1422
1423        }
1424
1425        /*
1426         * (non-Javadoc)
1427         *
1428         * @see org.eclipse.core.runtime.jobs.Job#canceling()
1429         */

1430        protected void canceling() {
1431            super.canceling();
1432            contentProvider.stopReloadingCache();
1433        }
1434
1435    }
1436
1437    private class RemoveHistoryItemAction extends Action {
1438
1439        /**
1440         * Creates a new instance of the class.
1441         */

1442        public RemoveHistoryItemAction() {
1443            super(
1444                    WorkbenchMessages.FilteredItemsSelectionDialog_removeItemsFromHistoryAction);
1445        }
1446
1447        /*
1448         * (non-Javadoc)
1449         *
1450         * @see org.eclipse.jface.action.Action#run()
1451         */

1452        public void run() {
1453            List JavaDoc selectedElements = ((StructuredSelection) list.getSelection())
1454                    .toList();
1455            removeSelectedItems(selectedElements);
1456        }
1457    }
1458
1459    private class ItemsListLabelProvider extends LabelProvider implements
1460            IColorProvider, IFontProvider, ILabelProviderListener {
1461        private ILabelProvider provider;
1462
1463        private ILabelDecorator selectionDecorator;
1464
1465        // Need to keep our own list of listeners
1466
private ListenerList listeners = new ListenerList();
1467
1468        /**
1469         * Creates a new instance of the class.
1470         *
1471         * @param provider
1472         * the label provider for all items, not <code>null</code>
1473         * @param selectionDecorator
1474         * the decorator for selected items, can be <code>null</code>
1475         */

1476        public ItemsListLabelProvider(ILabelProvider provider,
1477                ILabelDecorator selectionDecorator) {
1478            Assert.isNotNull(provider);
1479            this.provider = provider;
1480            this.selectionDecorator = selectionDecorator;
1481
1482            provider.addListener(this);
1483
1484            if (selectionDecorator != null) {
1485                selectionDecorator.addListener(this);
1486            }
1487        }
1488
1489        /**
1490         * Sets new selection decorator.
1491         *
1492         * @param newSelectionDecorator
1493         * new label decorator for selected items in the list
1494         */

1495        public void setSelectionDecorator(ILabelDecorator newSelectionDecorator) {
1496            if (selectionDecorator != null) {
1497                selectionDecorator.removeListener(this);
1498                selectionDecorator.dispose();
1499            }
1500
1501            selectionDecorator = newSelectionDecorator;
1502
1503            if (selectionDecorator != null) {
1504                selectionDecorator.addListener(this);
1505            }
1506        }
1507
1508        /**
1509         * Gets selection decorator.
1510         *
1511         * @return the label decorator for selected items in the list
1512         */

1513        public ILabelDecorator getSelectionDecorator() {
1514            return selectionDecorator;
1515        }
1516
1517        /**
1518         * Sets new label provider.
1519         *
1520         * @param newProvider
1521         * new label provider for items in the list, not
1522         * <code>null</code>
1523         */

1524        public void setProvider(ILabelProvider newProvider) {
1525            Assert.isNotNull(newProvider);
1526            provider.removeListener(this);
1527            provider.dispose();
1528
1529            provider = newProvider;
1530
1531            if (provider != null) {
1532                provider.addListener(this);
1533            }
1534        }
1535
1536        /**
1537         * Gets the label provider.
1538         *
1539         * @return the label provider for items in the list
1540         */

1541        public ILabelProvider getProvider() {
1542            return provider;
1543        }
1544
1545        /*
1546         * (non-Javadoc)
1547         *
1548         * @see org.eclipse.jface.viewers.ILabelProvider#getImage(java.lang.Object)
1549         */

1550        public Image getImage(Object JavaDoc element) {
1551            if (element instanceof ItemsListSeparator) {
1552                return WorkbenchImages
1553                        .getImage(IWorkbenchGraphicConstants.IMG_OBJ_SEPARATOR);
1554            }
1555
1556            return provider.getImage(element);
1557        }
1558
1559        /*
1560         * (non-Javadoc)
1561         *
1562         * @see org.eclipse.jface.viewers.ILabelProvider#getText(java.lang.Object)
1563         */

1564        public String JavaDoc getText(Object JavaDoc element) {
1565            if (element instanceof ItemsListSeparator) {
1566                return getSeparatorLabel(((ItemsListSeparator) element)
1567                        .getName());
1568            }
1569
1570            String JavaDoc str = provider.getText(element);
1571
1572            if (selectionDecorator != null && element != null) {
1573
1574                // ((StructuredSelection)list.getSelection()).toList().contains(element))
1575
// cannot be used - virtual tables produce cycles in
1576
// update item - get selection invocation scenarios
1577

1578                int[] selectionIndices = list.getTable().getSelectionIndices();
1579                List JavaDoc elements = Arrays
1580                        .asList(contentProvider.getElements(null));
1581                for (int i = 0; i < selectionIndices.length; i++) {
1582                    if (elements.size() > selectionIndices[i]
1583                            && element
1584                                    .equals(elements.get(selectionIndices[i]))) {
1585                        str = selectionDecorator.decorateText(str, element);
1586                        break;
1587                    }
1588                }
1589            }
1590            return str;
1591        }
1592
1593        private String JavaDoc getSeparatorLabel(String JavaDoc separatorLabel) {
1594            Rectangle rect = list.getTable().getBounds();
1595
1596            int borderWidth = list.getTable().computeTrim(0, 0, 0, 0).width;
1597
1598            int imageWidth = WorkbenchImages.getImage(
1599                    IWorkbenchGraphicConstants.IMG_OBJ_SEPARATOR).getBounds().width;
1600
1601            int width = rect.width - borderWidth - imageWidth;
1602
1603            GC gc = new GC(list.getTable());
1604            gc.setFont(list.getTable().getFont());
1605
1606            int fSeparatorWidth = gc.getAdvanceWidth('-');
1607            int fMessageLength = gc.textExtent(separatorLabel).x;
1608
1609            gc.dispose();
1610
1611            StringBuffer JavaDoc dashes = new StringBuffer JavaDoc();
1612            int chars = (((width - fMessageLength) / fSeparatorWidth) / 2) - 2;
1613            for (int i = 0; i < chars; i++) {
1614                dashes.append('-');
1615            }
1616
1617            StringBuffer JavaDoc result = new StringBuffer JavaDoc();
1618            result.append(dashes);
1619            result.append(" " + separatorLabel + " "); //$NON-NLS-1$//$NON-NLS-2$
1620
result.append(dashes);
1621            return result.toString().trim();
1622        }
1623
1624        /*
1625         * (non-Javadoc)
1626         *
1627         * @see org.eclipse.jface.viewers.IBaseLabelProvider#addListener(org.eclipse.jface.viewers.ILabelProviderListener)
1628         */

1629        public void addListener(ILabelProviderListener listener) {
1630            listeners.add(listener);
1631        }
1632
1633        /*
1634         * (non-Javadoc)
1635         *
1636         * @see org.eclipse.jface.viewers.IBaseLabelProvider#dispose()
1637         */

1638        public void dispose() {
1639            provider.removeListener(this);
1640            provider.dispose();
1641
1642            if (selectionDecorator != null) {
1643                selectionDecorator.removeListener(this);
1644                selectionDecorator.dispose();
1645            }
1646
1647            super.dispose();
1648        }
1649
1650        /*
1651         * (non-Javadoc)
1652         *
1653         * @see org.eclipse.jface.viewers.IBaseLabelProvider#isLabelProperty(java.lang.Object,
1654         * java.lang.String)
1655         */

1656        public boolean isLabelProperty(Object JavaDoc element, String JavaDoc property) {
1657            if (provider.isLabelProperty(element, property)) {
1658                return true;
1659            }
1660            if (selectionDecorator != null
1661                    && selectionDecorator.isLabelProperty(element, property)) {
1662                return true;
1663            }
1664            return false;
1665        }
1666
1667        /*
1668         * (non-Javadoc)
1669         *
1670         * @see org.eclipse.jface.viewers.IBaseLabelProvider#removeListener(org.eclipse.jface.viewers.ILabelProviderListener)
1671         */

1672        public void removeListener(ILabelProviderListener listener) {
1673            listeners.remove(listener);
1674        }
1675
1676        /*
1677         * (non-Javadoc)
1678         *
1679         * @see org.eclipse.jface.viewers.IColorProvider#getBackground(java.lang.Object)
1680         */

1681        public Color getBackground(Object JavaDoc element) {
1682            if (element instanceof ItemsListSeparator) {
1683                return null;
1684            }
1685            if (provider instanceof IColorProvider) {
1686                return ((IColorProvider) provider).getBackground(element);
1687            }
1688            return null;
1689        }
1690
1691        /*
1692         * (non-Javadoc)
1693         *
1694         * @see org.eclipse.jface.viewers.IColorProvider#getForeground(java.lang.Object)
1695         */

1696        public Color getForeground(Object JavaDoc element) {
1697            if (element instanceof ItemsListSeparator) {
1698                return Display.getCurrent().getSystemColor(
1699                        SWT.COLOR_WIDGET_NORMAL_SHADOW);
1700            }
1701            if (provider instanceof IColorProvider) {
1702                return ((IColorProvider) provider).getForeground(element);
1703            }
1704            return null;
1705        }
1706
1707        /*
1708         * (non-Javadoc)
1709         *
1710         * @see org.eclipse.jface.viewers.IFontProvider#getFont(java.lang.Object)
1711         */

1712        public Font getFont(Object JavaDoc element) {
1713            if (element instanceof ItemsListSeparator) {
1714                return null;
1715            }
1716            if (provider instanceof IFontProvider) {
1717                return ((IFontProvider) provider).getFont(element);
1718            }
1719            return null;
1720        }
1721
1722        /*
1723         * (non-Javadoc)
1724         *
1725         * @see org.eclipse.jface.viewers.ILabelProviderListener#labelProviderChanged(org.eclipse.jface.viewers.LabelProviderChangedEvent)
1726         */

1727        public void labelProviderChanged(LabelProviderChangedEvent event) {
1728            Object JavaDoc[] l = listeners.getListeners();
1729            for (int i = 0; i < listeners.size(); i++) {
1730                ((ILabelProviderListener) l[i]).labelProviderChanged(event);
1731            }
1732        }
1733    }
1734
1735    /**
1736     * Used in ItemsListContentProvider, separates history and non-history
1737     * items.
1738     */

1739    private class ItemsListSeparator {
1740
1741        private String JavaDoc name;
1742
1743        /**
1744         * Creates a new instance of the class.
1745         *
1746         * @param name
1747         * the name of the separator
1748         */

1749        public ItemsListSeparator(String JavaDoc name) {
1750            this.name = name;
1751        }
1752
1753        /**
1754         * Returns the name of this separator.
1755         *
1756         * @return the name of the separator
1757         */

1758        public String JavaDoc getName() {
1759            return name;
1760        }
1761    }
1762
1763    /**
1764     * GranualProgressMonitor is used for monitoring progress of filtering
1765     * process. It is used by <code>RefreshProgressMessageJob</code> to
1766     * refresh progress message. State of this monitor illustrates state of
1767     * filtering or cache refreshing process.
1768     *
1769     * @see GranualProgressMonitor#internalWorked(double)
1770     */

1771    private class GranualProgressMonitor extends ProgressMonitorWrapper {
1772
1773        private String JavaDoc name;
1774
1775        private String JavaDoc subName;
1776
1777        private int totalWork;
1778
1779        private double worked;
1780
1781        private boolean done;
1782
1783        /**
1784         * Creates instance of <code>GranualProgressMonitor</code>.
1785         *
1786         * @param monitor
1787         * progress to be wrapped
1788         */

1789        public GranualProgressMonitor(IProgressMonitor monitor) {
1790            super(monitor);
1791        }
1792
1793        /**
1794         * Checks if filtering has been done
1795         *
1796         * @return true if filtering work has been done false in other way
1797         */

1798        public boolean isDone() {
1799            return done;
1800        }
1801
1802        /*
1803         * (non-Javadoc)
1804         *
1805         * @see org.eclipse.core.runtime.ProgressMonitorWrapper#setTaskName(java.lang.String)
1806         */

1807        public void setTaskName(String JavaDoc name) {
1808            super.setTaskName(name);
1809            this.name = name;
1810            this.subName = null;
1811        }
1812
1813        /*
1814         * (non-Javadoc)
1815         *
1816         * @see org.eclipse.core.runtime.ProgressMonitorWrapper#subTask(java.lang.String)
1817         */

1818        public void subTask(String JavaDoc name) {
1819            super.subTask(name);
1820            this.subName = name;
1821        }
1822
1823        /*
1824         * (non-Javadoc)
1825         *
1826         * @see org.eclipse.core.runtime.ProgressMonitorWrapper#beginTask(java.lang.String,
1827         * int)
1828         */

1829        public void beginTask(String JavaDoc name, int totalWork) {
1830            super.beginTask(name, totalWork);
1831            if (this.name == null)
1832                this.name = name;
1833            this.totalWork = totalWork;
1834            refreshProgressMessageJob.scheduleProgressRefresh(this);
1835        }
1836
1837        /*
1838         * (non-Javadoc)
1839         *
1840         * @see org.eclipse.core.runtime.ProgressMonitorWrapper#worked(int)
1841         */

1842        public void worked(int work) {
1843            super.worked(work);
1844            internalWorked(work);
1845        }
1846
1847        /*
1848         * (non-Javadoc)
1849         *
1850         * @see org.eclipse.core.runtime.ProgressMonitorWrapper#done()
1851         */

1852        public void done() {
1853            done = true;
1854            super.done();
1855        }
1856
1857        /*
1858         * (non-Javadoc)
1859         *
1860         * @see org.eclipse.core.runtime.ProgressMonitorWrapper#setCanceled(boolean)
1861         */

1862        public void setCanceled(boolean b) {
1863            done = b;
1864            super.setCanceled(b);
1865        }
1866
1867        /*
1868         * (non-Javadoc)
1869         *
1870         * @see org.eclipse.core.runtime.ProgressMonitorWrapper#internalWorked(double)
1871         */

1872        public void internalWorked(double work) {
1873            worked = worked + work;
1874        }
1875
1876        private String JavaDoc getMessage() {
1877            if (done)
1878                return ""; //$NON-NLS-1$
1879

1880            String JavaDoc message;
1881
1882            if (name == null) {
1883                message = subName == null ? "" : subName; //$NON-NLS-1$
1884
} else {
1885                message = subName == null ? name
1886                        : NLS
1887                                .bind(
1888                                        WorkbenchMessages.FilteredItemsSelectionDialog_subtaskProgressMessage,
1889                                        new Object JavaDoc[] { name, subName });
1890            }
1891            if (totalWork == 0)
1892                return message;
1893
1894            return NLS
1895                    .bind(
1896                            WorkbenchMessages.FilteredItemsSelectionDialog_taskProgressMessage,
1897                            new Object JavaDoc[] {
1898                                    message,
1899                                    new Integer JavaDoc(
1900                                            (int) ((worked * 100) / totalWork)) });
1901
1902        }
1903
1904    }
1905
1906    /**
1907     * Filters items history and schedule filter job.
1908     */

1909    private class FilterHistoryJob extends Job {
1910
1911        /**
1912         * Filter used during the filtering process.
1913         */

1914        private ItemsFilter itemsFilter;
1915
1916        /**
1917         * Creates new instance of receiver.
1918         */

1919        public FilterHistoryJob() {
1920            super(WorkbenchMessages.FilteredItemsSelectionDialog_jobLabel);
1921            setSystem(true);
1922        }
1923
1924        /*
1925         * (non-Javadoc)
1926         *
1927         * @see org.eclipse.core.runtime.jobs.Job#run(org.eclipse.core.runtime.IProgressMonitor)
1928         */

1929        protected IStatus run(IProgressMonitor monitor) {
1930
1931            this.itemsFilter = filter;
1932
1933            contentProvider.reset();
1934
1935            refreshWithLastSelection = false;
1936
1937            contentProvider.addHistoryItems(itemsFilter);
1938
1939            if (!(lastCompletedFilter != null && lastCompletedFilter
1940                            .isSubFilter(this.itemsFilter)))
1941                contentProvider.refresh();
1942
1943            filterJob.schedule();
1944
1945            return Status.OK_STATUS;
1946        }
1947
1948    }
1949
1950    /**
1951     * Filters items in indicated set and history. During filtering, it
1952     * refreshes the dialog (progress monitor and elements list).
1953     *
1954     * Depending on the filter, <code>FilterJob</code> decides which kind of
1955     * search will be run inside <code>filterContent</code>. If the last
1956     * filtering is done (last completed filter), is not null, and the new
1957     * filter is a sub-filter ({@link FilteredItemsSelectionDialog.ItemsFilter#isSubFilter(FilteredItemsSelectionDialog.ItemsFilter)})
1958     * of the last, then <code>FilterJob</code> only filters in the cache. If
1959     * it is the first filtering or the new filter isn't a sub-filter of the
1960     * last one, a full search is run.
1961     */

1962    private class FilterJob extends Job {
1963
1964        /**
1965         * Filter used during the filtering process.
1966         */

1967        protected ItemsFilter itemsFilter;
1968
1969        /**
1970         * Creates new instance of FilterJob
1971         */

1972        public FilterJob() {
1973            super(WorkbenchMessages.FilteredItemsSelectionDialog_jobLabel);
1974            setSystem(true);
1975        }
1976
1977        /*
1978         * (non-Javadoc)
1979         *
1980         * @see org.eclipse.core.runtime.jobs.Job#run(org.eclipse.core.runtime.IProgressMonitor)
1981         */

1982        protected final IStatus run(IProgressMonitor parent) {
1983            GranualProgressMonitor monitor = new GranualProgressMonitor(parent);
1984            return doRun(monitor);
1985        }
1986
1987        /**
1988         * Executes job using the given filtering progress monitor. A hook for
1989         * subclasses.
1990         *
1991         * @param monitor
1992         * progress monitor
1993         * @return result of the execution
1994         */

1995        protected IStatus doRun(GranualProgressMonitor monitor) {
1996            try {
1997                internalRun(monitor);
1998            } catch (CoreException e) {
1999                cancel();
2000                return new Status(
2001                        IStatus.ERROR,
2002                        PlatformUI.PLUGIN_ID,
2003                        IStatus.ERROR,
2004                        WorkbenchMessages.FilteredItemsSelectionDialog_jobError,
2005                        e);
2006            }
2007            return Status.OK_STATUS;
2008        }
2009
2010        /**
2011         * Main method for the job.
2012         *
2013         * @param monitor
2014         * @throws CoreException
2015         */

2016        private void internalRun(GranualProgressMonitor monitor)
2017                throws CoreException {
2018            try {
2019                if (monitor.isCanceled())
2020                    return;
2021
2022                this.itemsFilter = filter;
2023
2024                if (filter.getPattern().length() != 0) {
2025                    filterContent(monitor);
2026                }
2027
2028                if (monitor.isCanceled())
2029                    return;
2030
2031                contentProvider.refresh();
2032            } finally {
2033                monitor.done();
2034            }
2035        }
2036
2037        /**
2038         * Filters items.
2039         *
2040         * @param monitor
2041         * for monitoring progress
2042         * @throws CoreException
2043         */

2044        protected void filterContent(GranualProgressMonitor monitor)
2045                throws CoreException {
2046
2047            if (lastCompletedFilter != null
2048                    && lastCompletedFilter.isSubFilter(this.itemsFilter)) {
2049
2050                int length = lastCompletedResult.size() / 500;
2051                monitor
2052                        .beginTask(
2053                                WorkbenchMessages.FilteredItemsSelectionDialog_cacheSearchJob_taskName,
2054                                length);
2055
2056                for (int pos = 0; pos < lastCompletedResult.size(); pos++) {
2057
2058                    Object JavaDoc item = lastCompletedResult.get(pos);
2059                    if (monitor.isCanceled())
2060                        break;
2061                    contentProvider.add(item, itemsFilter);
2062
2063                    if ((pos % 500) == 0) {
2064                        monitor.worked(1);
2065                    }
2066                }
2067
2068            } else {
2069
2070                lastCompletedFilter = null;
2071                lastCompletedResult = null;
2072
2073                SubProgressMonitor subMonitor = null;
2074                if (monitor != null) {
2075                    monitor
2076                            .beginTask(
2077                                    WorkbenchMessages.FilteredItemsSelectionDialog_searchJob_taskName,
2078                                    100);
2079                    subMonitor = new SubProgressMonitor(monitor, 95);
2080
2081                }
2082
2083                fillContentProvider(contentProvider, itemsFilter, subMonitor);
2084
2085                if (monitor != null && !monitor.isCanceled()) {
2086                    monitor.worked(2);
2087                    contentProvider.rememberResult(itemsFilter);
2088                    monitor.worked(3);
2089                }
2090            }
2091
2092        }
2093
2094    }
2095
2096    /**
2097     * History stores a list of key, object pairs. The list is bounded at a
2098     * certain size. If the list exceeds this size the oldest element is removed
2099     * from the list. An element can be added/renewed with a call to
2100     * <code>accessed(Object)</code>.
2101     * <p>
2102     * The history can be stored to/loaded from an XML file.
2103     */

2104    protected static abstract class SelectionHistory {
2105
2106        private static final String JavaDoc DEFAULT_ROOT_NODE_NAME = "historyRootNode"; //$NON-NLS-1$
2107

2108        private static final String JavaDoc DEFAULT_INFO_NODE_NAME = "infoNode"; //$NON-NLS-1$
2109

2110        private static final int MAX_HISTORY_SIZE = 60;
2111
2112        private final List JavaDoc historyList;
2113
2114        private final String JavaDoc rootNodeName;
2115
2116        private final String JavaDoc infoNodeName;
2117
2118        private SelectionHistory(String JavaDoc rootNodeName, String JavaDoc infoNodeName) {
2119
2120            historyList = Collections.synchronizedList(new LinkedList JavaDoc() {
2121
2122                private static final long serialVersionUID = 0L;
2123
2124                /*
2125                 * (non-Javadoc)
2126                 *
2127                 * @see java.util.LinkedList#add(java.lang.Object)
2128                 */

2129                public boolean add(Object JavaDoc arg0) {
2130                    if (this.size() > MAX_HISTORY_SIZE)
2131                        this.removeFirst();
2132                    if (!this.contains(arg0))
2133                        return super.add(arg0);
2134                    return false;
2135                }
2136
2137            });
2138
2139            this.rootNodeName = rootNodeName;
2140            this.infoNodeName = infoNodeName;
2141        }
2142
2143        /**
2144         * Creates new instance of <code>SelectionHistory</code>.
2145         */

2146        public SelectionHistory() {
2147            this(DEFAULT_ROOT_NODE_NAME, DEFAULT_INFO_NODE_NAME);
2148        }
2149
2150        /**
2151         * Adds object to history.
2152         *
2153         * @param object
2154         * the item to be added to the history
2155         */

2156        public synchronized void accessed(Object JavaDoc object) {
2157            historyList.add(object);
2158        }
2159
2160        /**
2161         * Returns <code>true</code> if history contains object.
2162         *
2163         * @param object
2164         * the item for which check will be executed
2165         * @return <code>true</code> if history contains object
2166         * <code>false</code> in other way
2167         */

2168        public synchronized boolean contains(Object JavaDoc object) {
2169            return historyList.contains(object);
2170        }
2171
2172        /**
2173         * Returns <code>true</code> if history is empty.
2174         *
2175         * @return <code>true</code> if history is empty
2176         */

2177        public synchronized boolean isEmpty() {
2178            return historyList.isEmpty();
2179        }
2180
2181        /**
2182         * Remove element from history.
2183         *
2184         * @param element
2185         * to remove form the history
2186         * @return <code>true</code> if this list contained the specified
2187         * element
2188         */

2189        public synchronized boolean remove(Object JavaDoc element) {
2190            return historyList.remove(element);
2191        }
2192
2193        /**
2194         * Load history elements from memento.
2195         *
2196         * @param memento
2197         * memento from which the history will be retrieved
2198         */

2199        public void load(IMemento memento) {
2200
2201            XMLMemento historyMemento = (XMLMemento) memento
2202                    .getChild(rootNodeName);
2203
2204            if (historyMemento == null) {
2205                return;
2206            }
2207
2208            IMemento[] mementoElements = historyMemento
2209                    .getChildren(infoNodeName);
2210            for (int i = 0; i < mementoElements.length; ++i) {
2211                IMemento mementoElement = mementoElements[i];
2212                Object JavaDoc object = restoreItemFromMemento(mementoElement);
2213                if (object != null) {
2214                    historyList.add(object);
2215                }
2216            }
2217        }
2218
2219        /**
2220         * Save history elements to memento.
2221         *
2222         * @param memento
2223         * memento to which the history will be added
2224         */

2225        public void save(IMemento memento) {
2226
2227            IMemento historyMemento = memento.createChild(rootNodeName);
2228
2229            Object JavaDoc[] items = getHistoryItems();
2230            for (int i = 0; i < items.length; i++) {
2231                Object JavaDoc item = items[i];
2232                IMemento elementMemento = historyMemento
2233                        .createChild(infoNodeName);
2234                storeItemToMemento(item, elementMemento);
2235            }
2236
2237        }
2238
2239        /**
2240         * Gets array of history items.
2241         *
2242         * @return array of history elements
2243         */

2244        public synchronized Object JavaDoc[] getHistoryItems() {
2245            return historyList.toArray();
2246        }
2247
2248        /**
2249         * Creates an object using given memento.
2250         *
2251         * @param memento
2252         * memento used for creating new object
2253         *
2254         * @return the restored object
2255         */

2256        protected abstract Object JavaDoc restoreItemFromMemento(IMemento memento);
2257
2258        /**
2259         * Store object in <code>IMemento</code>.
2260         *
2261         * @param item
2262         * the item to store
2263         * @param memento
2264         * the memento to store to
2265         */

2266        protected abstract void storeItemToMemento(Object JavaDoc item, IMemento memento);
2267
2268    }
2269
2270    /**
2271     * Filters elements using SearchPattern by comparing the names of items with
2272     * the filter pattern.
2273     */

2274    protected abstract class ItemsFilter {
2275
2276        protected SearchPattern patternMatcher;
2277
2278        /**
2279         * Creates new instance of ItemsFilter.
2280         */

2281        public ItemsFilter() {
2282            this(new SearchPattern());
2283        }
2284
2285        /**
2286         * Creates new instance of ItemsFilter.
2287         *
2288         * @param searchPattern
2289         * the pattern to be used when filtering
2290         */

2291        public ItemsFilter(SearchPattern searchPattern) {
2292            patternMatcher = searchPattern;
2293            String JavaDoc stringPattern = ""; //$NON-NLS-1$
2294
if (pattern != null && !pattern.getText().equals("*")) { //$NON-NLS-1$
2295
stringPattern = pattern.getText();
2296            }
2297            patternMatcher.setPattern(stringPattern);
2298        }
2299
2300        /**
2301         * Check if the given filter is a sub-filter of current filter. The
2302         * default implementation checks if the <code>SearchPattern</code>
2303         * from the current filter is a sub-pattern of the one from the provided
2304         * filter.
2305         *
2306         * @param filter
2307         * the filter to be checked, or <code>null</code>
2308         * @return <code>true</code> if the given filter is sub-filter of the
2309         * current, <code>false</code> if the given filter isn't a
2310         * sub-filter or is <code>null</code>
2311         *
2312         * @see org.eclipse.ui.dialogs.SearchPattern#isSubPattern(org.eclipse.ui.dialogs.SearchPattern)
2313         */

2314        public boolean isSubFilter(ItemsFilter filter) {
2315            if (filter != null) {
2316                return this.patternMatcher.isSubPattern(filter.patternMatcher);
2317            }
2318            return false;
2319        }
2320
2321        /**
2322         * Checks whether the provided filter is equal to the current filter.
2323         * The default implementation checks if <code>SearchPattern</code>
2324         * from current filter is equal to the one from provided filter.
2325         *
2326         * @param filter
2327         * filter to be checked, or <code>null</code>
2328         * @return <code>true</code> if the given filter is equal to current
2329         * filter, <code>false</code> if given filter isn't equal to
2330         * current one or if it is <code>null</code>
2331         *
2332         * @see org.eclipse.ui.dialogs.SearchPattern#equalsPattern(org.eclipse.ui.dialogs.SearchPattern)
2333         */

2334        public boolean equalsFilter(ItemsFilter filter) {
2335            if (filter != null
2336                    && filter.patternMatcher.equalsPattern(this.patternMatcher)) {
2337                return true;
2338            }
2339            return false;
2340        }
2341
2342        /**
2343         * Checks whether the pattern's match rule is camel case.
2344         *
2345         * @return <code>true</code> if pattern's match rule is camel case,
2346         * <code>false</code> otherwise
2347         */

2348        public boolean isCamelCasePattern() {
2349            return patternMatcher.getMatchRule() == SearchPattern.RULE_CAMELCASE_MATCH;
2350        }
2351
2352        /**
2353         * Returns the pattern string.
2354         *
2355         * @return pattern for this filter
2356         *
2357         * @see SearchPattern#getPattern()
2358         */

2359        public String JavaDoc getPattern() {
2360            return patternMatcher.getPattern();
2361        }
2362
2363        /**
2364         * Returns the rule to apply for matching keys.
2365         *
2366         * @return match rule
2367         *
2368         * @see SearchPattern#getMatchRule()
2369         */

2370        public int getMatchRule() {
2371            return patternMatcher.getMatchRule();
2372        }
2373
2374        /**
2375         * Matches text with filter.
2376         *
2377         * @param text
2378         * @return <code>true</code> if text matches with filter pattern,
2379         * <code>false</code> otherwise
2380         */

2381        protected boolean matches(String JavaDoc text) {
2382            return patternMatcher.matches(text);
2383        }
2384
2385        /**
2386         * General method for matching raw name pattern. Checks whether current
2387         * pattern is prefix of name provided item.
2388         *
2389         * @param item
2390         * item to check
2391         * @return <code>true</code> if current pattern is a prefix of name
2392         * provided item, <code>false</code> if item's name is shorter
2393         * than prefix or sequences of characters don't match.
2394         */

2395        public boolean matchesRawNamePattern(Object JavaDoc item) {
2396            String JavaDoc prefix = patternMatcher.getPattern();
2397            String JavaDoc text = getElementName(item);
2398
2399            if (text == null)
2400                return false;
2401
2402            int textLength = text.length();
2403            int prefixLength = prefix.length();
2404            if (textLength < prefixLength) {
2405                return false;
2406            }
2407            for (int i = prefixLength - 1; i >= 0; i--) {
2408                if (Character.toLowerCase(prefix.charAt(i)) != Character
2409                        .toLowerCase(text.charAt(i)))
2410                    return false;
2411            }
2412            return true;
2413        }
2414
2415        /**
2416         * Matches an item against filter conditions.
2417         *
2418         * @param item
2419         * @return <code>true<code> if item matches against filter conditions, <code>false</code>
2420         * otherwise
2421         */

2422        public abstract boolean matchItem(Object JavaDoc item);
2423
2424        /**
2425         * Checks consistency of an item. Item is inconsistent if was changed or
2426         * removed.
2427         *
2428         * @param item
2429         * @return <code>true</code> if item is consistent, <code>false</code>
2430         * if item is inconsistent
2431         */

2432        public abstract boolean isConsistentItem(Object JavaDoc item);
2433
2434    }
2435
2436    /**
2437     * An interface to content providers for
2438     * <code>FilterItemsSelectionDialog</code>.
2439     */

2440    protected abstract class AbstractContentProvider {
2441        /**
2442         * Adds the item to the content provider iff the filter matches the
2443         * item. Otherwise does nothing.
2444         *
2445         * @param item
2446         * the item to add
2447         * @param itemsFilter
2448         * the filter
2449         *
2450         * @see FilteredItemsSelectionDialog.ItemsFilter#matchItem(Object)
2451         */

2452        public abstract void add(Object JavaDoc item, ItemsFilter itemsFilter);
2453    }
2454
2455    /**
2456     * Collects filtered elements. Contains one synchronized, sorted set for
2457     * collecting filtered elements. All collected elements are sorted using
2458     * comparator. Comparator is returned by getElementComparator() method.
2459     * Implementation of <code>ItemsFilter</code> is used to filter elements.
2460     * The key function of filter used in to filtering is
2461     * <code>matchElement(Object item)</code>.
2462     * <p>
2463     * The <code>ContentProvider</code> class also provides item filtering
2464     * methods. The filtering has been moved from the standard TableView
2465     * <code>getFilteredItems()</code> method to content provider, because
2466     * <code>ILazyContentProvider</code> and virtual tables are used. This
2467     * class is responsible for adding a separator below history items and
2468     * marking each items as duplicate if its name repeats more than once on the
2469     * filtered list.
2470     */

2471    private class ContentProvider extends AbstractContentProvider implements
2472            IStructuredContentProvider, ILazyContentProvider {
2473
2474        private SelectionHistory selectionHistory;
2475
2476        /**
2477         * Raw result of the searching (unsorted, unfiltered).
2478         * <p>
2479         * Standard object flow:
2480         * <code>items -> lastSortedItems -> lastFilteredItems</code>
2481         */

2482        private Set JavaDoc items;
2483
2484        /**
2485         * Items that are duplicates.
2486         */

2487        private Set JavaDoc duplicates;
2488
2489        /**
2490         * List of <code>ViewerFilter</code>s to be used during filtering
2491         */

2492        private List JavaDoc filters;
2493
2494        /**
2495         * Result of the last filtering.
2496         * <p>
2497         * Standard object flow:
2498         * <code>items -> lastSortedItems -> lastFilteredItems</code>
2499         */

2500        private List JavaDoc lastFilteredItems;
2501
2502        /**
2503         * Result of the last sorting.
2504         * <p>
2505         * Standard object flow:
2506         * <code>items -> lastSortedItems -> lastFilteredItems</code>
2507         */

2508        private List JavaDoc lastSortedItems;
2509
2510        /**
2511         * Used for <code>getFilteredItems()</code> method canceling (when the
2512         * job that invoked the method was canceled).
2513         * <p>
2514         * Method canceling could be based (only) on monitor canceling
2515         * unfortunately sometimes the method <code>getFilteredElements()</code>
2516         * could be run with a null monitor, the <code>reset</code> flag have
2517         * to be left intact.
2518         */

2519        private boolean reset;
2520
2521        /**
2522         * Creates new instance of <code>ContentProvider</code>.
2523         *
2524         * @param selectionHistory
2525         */

2526        public ContentProvider(SelectionHistory selectionHistory) {
2527            this();
2528            this.selectionHistory = selectionHistory;
2529        }
2530
2531        /**
2532         * Creates new instance of <code>ContentProvider</code>.
2533         */

2534        public ContentProvider() {
2535            this.items = Collections.synchronizedSet(new HashSet JavaDoc(2048));
2536            this.duplicates = Collections.synchronizedSet(new HashSet JavaDoc(256));
2537            this.lastFilteredItems = new ArrayList JavaDoc();
2538            this.lastSortedItems = Collections.synchronizedList(new ArrayList JavaDoc(
2539                    2048));
2540        }
2541
2542        /**
2543         * Sets selection history.
2544         *
2545         * @param selectionHistory
2546         * The selectionHistory to set.
2547         */

2548        public void setSelectionHistory(SelectionHistory selectionHistory) {
2549            this.selectionHistory = selectionHistory;
2550        }
2551
2552        /**
2553         * @return Returns the selectionHistory.
2554         */

2555        public SelectionHistory getSelectionHistory() {
2556            return selectionHistory;
2557        }
2558
2559        /**
2560         * Removes all content items and resets progress message.
2561         */

2562        public void reset() {
2563            reset = true;
2564            this.items.clear();
2565            this.duplicates.clear();
2566            this.lastSortedItems.clear();
2567        }
2568
2569        /**
2570         * Stops reloading cache - <code>getFilteredItems()</code> method.
2571         */

2572        public void stopReloadingCache() {
2573            reset = true;
2574        }
2575
2576        /**
2577         * Adds filtered item.
2578         *
2579         * @param item
2580         * @param itemsFilter
2581         */

2582        public void add(Object JavaDoc item, ItemsFilter itemsFilter) {
2583            if (itemsFilter == filter) {
2584                if (itemsFilter != null) {
2585                    if (itemsFilter.matchItem(item)) {
2586                        this.items.add(item);
2587                    }
2588                } else {
2589                    this.items.add(item);
2590                }
2591            }
2592        }
2593
2594        /**
2595         * Add all history items to <code>contentProvider</code>.
2596         *
2597         * @param itemsFilter
2598         */

2599        public void addHistoryItems(ItemsFilter itemsFilter) {
2600            if (this.selectionHistory != null) {
2601                Object JavaDoc[] items = this.selectionHistory.getHistoryItems();
2602                for (int i = 0; i < items.length; i++) {
2603                    Object JavaDoc item = items[i];
2604                    if (itemsFilter == filter) {
2605                        if (itemsFilter != null) {
2606                            if (itemsFilter.matchItem(item)) {
2607                                if (itemsFilter.isConsistentItem(item)) {
2608                                    this.items.add(item);
2609                                } else {
2610                                    this.selectionHistory.remove(item);
2611                                }
2612                            }
2613                        }
2614                    }
2615                }
2616            }
2617        }
2618
2619        /**
2620         * Refresh dialog.
2621         */

2622        public void refresh() {
2623            scheduleRefresh();
2624        }
2625
2626        /**
2627         * Removes items from history and refreshes the view.
2628         *
2629         * @param item
2630         * to remove
2631         *
2632         * @return removed item
2633         */

2634        public Object JavaDoc removeHistoryElement(Object JavaDoc item) {
2635            if (this.selectionHistory != null)
2636                this.selectionHistory.remove(item);
2637            if (filter == null || filter.getPattern().length() == 0) {
2638                items.remove(item);
2639                duplicates.remove(item);
2640                this.lastSortedItems.remove(item);
2641            }
2642
2643            synchronized (lastSortedItems) {
2644                Collections.sort(lastSortedItems, getHistoryComparator());
2645            }
2646            return item;
2647        }
2648
2649        /**
2650         * Adds item to history and refresh view.
2651         *
2652         * @param item
2653         * to add
2654         */

2655        public void addHistoryElement(Object JavaDoc item) {
2656            if (this.selectionHistory != null)
2657                this.selectionHistory.accessed(item);
2658            if (filter == null || !filter.matchItem(item)) {
2659                this.items.remove(item);
2660                this.duplicates.remove(item);
2661                this.lastSortedItems.remove(item);
2662            }
2663            synchronized (lastSortedItems) {
2664                Collections.sort(lastSortedItems, getHistoryComparator());
2665            }
2666            this.refresh();
2667        }
2668
2669        /**
2670         * @param item
2671         * @return <code>true</code> if given item is part of the history
2672         */

2673        public boolean isHistoryElement(Object JavaDoc item) {
2674            if (this.selectionHistory != null) {
2675                return this.selectionHistory.contains(item);
2676            }
2677            return false;
2678        }
2679
2680        /**
2681         * Sets/unsets given item as duplicate.
2682         *
2683         * @param item
2684         * item to change
2685         *
2686         * @param isDuplicate
2687         * duplicate flag
2688         */

2689        public void setDuplicateElement(Object JavaDoc item, boolean isDuplicate) {
2690            if (this.items.contains(item)) {
2691                if (isDuplicate)
2692                    this.duplicates.add(item);
2693                else
2694                    this.duplicates.remove(item);
2695            }
2696        }
2697
2698        /**
2699         * Indicates whether given item is a duplicate.
2700         *
2701         * @param item
2702         * item to check
2703         * @return <code>true</code> if item is duplicate
2704         */

2705        public boolean isDuplicateElement(Object JavaDoc item) {
2706            return duplicates.contains(item);
2707        }
2708
2709        /**
2710         * Load history from memento.
2711         *
2712         * @param memento
2713         * memento from which the history will be retrieved
2714         */

2715        public void loadHistory(IMemento memento) {
2716            if (this.selectionHistory != null)
2717                this.selectionHistory.load(memento);
2718        }
2719
2720        /**
2721         * Save history to memento.
2722         *
2723         * @param memento
2724         * memento to which the history will be added
2725         */

2726        public void saveHistory(IMemento memento) {
2727            if (this.selectionHistory != null)
2728                this.selectionHistory.save(memento);
2729        }
2730
2731        /**
2732         * Gets sorted items.
2733         *
2734         * @return sorted items
2735         */

2736        private Object JavaDoc[] getSortedItems() {
2737            if (lastSortedItems.size() != items.size()) {
2738                synchronized (lastSortedItems) {
2739                    lastSortedItems.clear();
2740                    lastSortedItems.addAll(items);
2741                    Collections.sort(lastSortedItems, getHistoryComparator());
2742                }
2743            }
2744            return lastSortedItems.toArray();
2745        }
2746
2747        /**
2748         * Remember result of filtering.
2749         *
2750         * @param itemsFilter
2751         */

2752        public void rememberResult(ItemsFilter itemsFilter) {
2753            List JavaDoc itemsList = Collections.synchronizedList(Arrays
2754                    .asList(getSortedItems()));
2755            // synchronization
2756
if (itemsFilter == filter) {
2757                lastCompletedFilter = itemsFilter;
2758                lastCompletedResult = itemsList;
2759            }
2760
2761        }
2762
2763        /*
2764         * (non-Javadoc)
2765         *
2766         * @see org.eclipse.jface.viewers.IStructuredContentProvider#getElements(java.lang.Object)
2767         */

2768        public Object JavaDoc[] getElements(Object JavaDoc inputElement) {
2769            return lastFilteredItems.toArray();
2770        }
2771
2772        /*
2773         * (non-Javadoc)
2774         *
2775         * @see org.eclipse.jface.viewers.IContentProvider#dispose()
2776         */

2777        public void dispose() {
2778        }
2779
2780        /*
2781         * (non-Javadoc)
2782         *
2783         * @see org.eclipse.jface.viewers.IContentProvider#inputChanged(org.eclipse.jface.viewers.Viewer,
2784         * java.lang.Object, java.lang.Object)
2785         */

2786        public void inputChanged(Viewer viewer, Object JavaDoc oldInput, Object JavaDoc newInput) {
2787        }
2788
2789        /*
2790         * (non-Javadoc)
2791         *
2792         * @see org.eclipse.jface.viewers.ILazyContentProvider#updateElement(int)
2793         */

2794        public void updateElement(int index) {
2795
2796            FilteredItemsSelectionDialog.this.list.replace((lastFilteredItems
2797                    .size() > index) ? lastFilteredItems.get(index) : null,
2798                    index);
2799
2800        }
2801
2802        /**
2803         * Main method responsible for getting the filtered items and checking
2804         * for duplicates. It is based on the
2805         * {@link ContentProvider#getFilteredItems(Object, IProgressMonitor)}.
2806         *
2807         * @param checkDuplicates
2808         * <code>true</code> if data concerning elements
2809         * duplication should be computed - it takes much more time
2810         * than standard filtering
2811         *
2812         * @param monitor
2813         * progress monitor
2814         */

2815        public void reloadCache(boolean checkDuplicates,
2816                IProgressMonitor monitor) {
2817
2818            reset = false;
2819
2820            if (monitor != null) {
2821                // the work is divided into two actions of the same length
2822
int totalWork = checkDuplicates ? 200 : 100;
2823
2824                monitor
2825                        .beginTask(
2826                                WorkbenchMessages.FilteredItemsSelectionDialog_cacheRefreshJob,
2827                                totalWork);
2828            }
2829
2830            // the TableViewer's root (the input) is treated as parent
2831

2832            lastFilteredItems = Arrays.asList(getFilteredItems(list.getInput(),
2833                    monitor != null ? new SubProgressMonitor(monitor, 100)
2834                            : null));
2835
2836            if (reset || (monitor != null && monitor.isCanceled())) {
2837                if (monitor != null)
2838                    monitor.done();
2839                return;
2840            }
2841
2842            if (checkDuplicates) {
2843                checkDuplicates(monitor);
2844            }
2845            if (monitor != null)
2846                monitor.done();
2847        }
2848
2849        private void checkDuplicates(IProgressMonitor monitor) {
2850            synchronized (lastFilteredItems) {
2851                IProgressMonitor subMonitor = null;
2852                int reportEvery = lastFilteredItems.size() / 20;
2853                if (monitor != null) {
2854                    subMonitor = new SubProgressMonitor(monitor, 100);
2855                    subMonitor
2856                            .beginTask(
2857                                    WorkbenchMessages.FilteredItemsSelectionDialog_cacheRefreshJob_checkDuplicates,
2858                                    5);
2859                }
2860                HashMap JavaDoc helperMap = new HashMap JavaDoc();
2861                for (int i = 0; i < lastFilteredItems.size(); i++) {
2862                    if (reset
2863                            || (subMonitor != null && subMonitor.isCanceled()))
2864                        return;
2865                    Object JavaDoc item = lastFilteredItems.get(i);
2866
2867                    if (!(item instanceof ItemsListSeparator)) {
2868                        Object JavaDoc previousItem = helperMap.put(
2869                                getElementName(item), item);
2870                        if (previousItem != null) {
2871                            setDuplicateElement(previousItem, true);
2872                            setDuplicateElement(item, true);
2873                        } else {
2874                            setDuplicateElement(item, false);
2875                        }
2876                    }
2877
2878                    if (subMonitor != null && reportEvery != 0
2879                            && (i + 1) % reportEvery == 0)
2880                        subMonitor.worked(1);
2881                }
2882                helperMap.clear();
2883            }
2884        }
2885
2886        /**
2887         * Returns an array of items filtered using the provided
2888         * <code>ViewerFilter</code>s with a separator added.
2889         *
2890         * @param parent
2891         * the parent
2892         * @param monitor
2893         * progress monitor, can be <code>null</code>
2894         * @return an array of filtered items
2895         */

2896        protected Object JavaDoc[] getFilteredItems(Object JavaDoc parent,
2897                IProgressMonitor monitor) {
2898            int ticks = 100;
2899
2900            if (monitor != null) {
2901                monitor
2902                        .beginTask(
2903                                WorkbenchMessages.FilteredItemsSelectionDialog_cacheRefreshJob_getFilteredElements,
2904                                ticks);
2905                if (filters != null) {
2906                    ticks /= (filters.size() + 2);
2907                } else {
2908                    ticks /= 2;
2909                }
2910            }
2911
2912            // get already sorted array
2913
Object JavaDoc[] filteredElements = getSortedItems();
2914
2915            if (monitor != null) {
2916                monitor.worked(ticks);
2917            }
2918
2919            // filter the elements using provided ViewerFilters
2920
if (filters != null && filteredElements != null) {
2921                for (Iterator JavaDoc iter = filters.iterator(); iter.hasNext();) {
2922                    ViewerFilter f = (ViewerFilter) iter.next();
2923                    filteredElements = f.filter(list, parent, filteredElements);
2924                    if (monitor != null)
2925                        monitor.worked(ticks);
2926                }
2927            }
2928
2929            if (filteredElements == null || monitor.isCanceled()) {
2930                if (monitor != null)
2931                    monitor.done();
2932                return new Object JavaDoc[0];
2933            }
2934
2935            ArrayList JavaDoc preparedElements = new ArrayList JavaDoc();
2936            boolean hasHistory = false;
2937
2938            if (filteredElements.length > 0) {
2939                if (isHistoryElement(filteredElements[0])) {
2940                    hasHistory = true;
2941                }
2942            }
2943
2944            int reportEvery = filteredElements.length / ticks;
2945
2946            // add separator
2947
for (int i = 0; i < filteredElements.length; i++) {
2948                Object JavaDoc item = filteredElements[i];
2949
2950                if (hasHistory && !isHistoryElement(item)) {
2951                    preparedElements.add(itemsListSeparator);
2952                    hasHistory = false;
2953                }
2954
2955                preparedElements.add(item);
2956
2957                if (monitor != null && reportEvery != 0
2958                        && ((i + 1) % reportEvery == 0))
2959                    monitor.worked(1);
2960            }
2961
2962            if (monitor != null)
2963                monitor.done();
2964
2965            return preparedElements.toArray();
2966        }
2967
2968        /**
2969         * Adds a filter to this content provider. For an example usage of such
2970         * filters look at the project <code>org.eclipse.ui.ide</code>, class
2971         * <code>org.eclipse.ui.dialogs.FilteredResourcesSelectionDialog.CustomWorkingSetFilter</code>.
2972         *
2973         *
2974         * @param filter
2975         * the filter to be added
2976         */

2977        public void addFilter(ViewerFilter filter) {
2978            if (filters == null) {
2979                filters = new ArrayList JavaDoc();
2980            }
2981            filters.add(filter);
2982            // currently filters are only added when dialog is restored
2983
// if it is changed, refreshing the whole TableViewer should be
2984
// added
2985
}
2986
2987    }
2988
2989    /**
2990     * A content provider that does nothing.
2991     */

2992    private class NullContentProvider implements IContentProvider {
2993
2994        /*
2995         * (non-Javadoc)
2996         *
2997         * @see org.eclipse.jface.viewers.IContentProvider#dispose()
2998         */

2999        public void dispose() {
3000        }
3001
3002        /*
3003         * (non-Javadoc)
3004         *
3005         * @see org.eclipse.jface.viewers.IContentProvider#inputChanged(org.eclipse.jface.viewers.Viewer,
3006         * java.lang.Object, java.lang.Object)
3007         */

3008        public void inputChanged(Viewer viewer, Object JavaDoc oldInput, Object JavaDoc newInput) {
3009        }
3010
3011    }
3012
3013    /**
3014     * DetailsContentViewer objects are wrappers for labels.
3015     * DetailsContentViewer provides means to change label's image and text when
3016     * the attached LabelProvider is updated.
3017     */

3018    private class DetailsContentViewer extends ContentViewer {
3019
3020        private CLabel label;
3021
3022        /**
3023         * Unfortunately, it was impossible to delegate displaying border to
3024         * label. The <code>ViewForm</code> is used because
3025         * <code>CLabel</code> displays shadow when border is present.
3026         */

3027        private ViewForm viewForm;
3028
3029        /**
3030         * Constructs a new instance of this class given its parent and a style
3031         * value describing its behavior and appearance.
3032         *
3033         * @param parent
3034         * the parent component
3035         * @param style
3036         * SWT style bits
3037         */

3038        public DetailsContentViewer(Composite parent, int style) {
3039            viewForm = new ViewForm(parent, style);
3040            GridData gd = new GridData(GridData.FILL_HORIZONTAL);
3041            gd.horizontalSpan = 2;
3042            viewForm.setLayoutData(gd);
3043            label = new CLabel(viewForm, SWT.FLAT);
3044            label.setFont(parent.getFont());
3045            viewForm.setContent(label);
3046            hookControl(label);
3047        }
3048
3049        /**
3050         * Shows/hides the content viewer.
3051         *
3052         * @param visible
3053         * if the content viewer should be visible.
3054         */

3055        public void setVisible(boolean visible) {
3056            GridData gd = (GridData) viewForm.getLayoutData();
3057            gd.exclude = !visible;
3058            viewForm.getParent().layout();
3059        }
3060
3061        /*
3062         * (non-Javadoc)
3063         *
3064         * @see org.eclipse.jface.viewers.Viewer#inputChanged(java.lang.Object,
3065         * java.lang.Object)
3066         */

3067        protected void inputChanged(Object JavaDoc input, Object JavaDoc oldInput) {
3068            if (oldInput == null) {
3069                if (input == null) {
3070                    return;
3071                }
3072                refresh();
3073                return;
3074            }
3075
3076            refresh();
3077
3078        }
3079
3080        /*
3081         * (non-Javadoc)
3082         *
3083         * @see org.eclipse.jface.viewers.ContentViewer#handleLabelProviderChanged(org.eclipse.jface.viewers.LabelProviderChangedEvent)
3084         */

3085        protected void handleLabelProviderChanged(
3086                LabelProviderChangedEvent event) {
3087            if (event != null) {
3088                refresh(event.getElements());
3089            }
3090        }
3091
3092        /*
3093         * (non-Javadoc)
3094         *
3095         * @see org.eclipse.jface.viewers.Viewer#getControl()
3096         */

3097        public Control getControl() {
3098            return label;
3099        }
3100
3101        /*
3102         * (non-Javadoc)
3103         *
3104         * @see org.eclipse.jface.viewers.Viewer#getSelection()
3105         */

3106        public ISelection getSelection() {
3107            // not supported
3108
return null;
3109        }
3110
3111        /*
3112         * (non-Javadoc)
3113         *
3114         * @see org.eclipse.jface.viewers.Viewer#refresh()
3115         */

3116        public void refresh() {
3117            Object JavaDoc input = this.getInput();
3118            if (input != null) {
3119                ILabelProvider labelProvider = (ILabelProvider) getLabelProvider();
3120                doRefresh(labelProvider.getText(input), labelProvider
3121                        .getImage(input));
3122            } else {
3123                doRefresh(null, null);
3124            }
3125        }
3126
3127        /**
3128         * Sets the given text and image to the label.
3129         *
3130         * @param text
3131         * the new text or null
3132         * @param image
3133         * the new image
3134         */

3135        private void doRefresh(String JavaDoc text, Image image) {
3136            label.setText(text);
3137            label.setImage(image);
3138        }
3139
3140        /*
3141         * (non-Javadoc)
3142         *
3143         * @see org.eclipse.jface.viewers.Viewer#setSelection(org.eclipse.jface.viewers.ISelection,
3144         * boolean)
3145         */

3146        public void setSelection(ISelection selection, boolean reveal) {
3147            // not supported
3148
}
3149
3150        /**
3151         * Refreshes the label if currently chosen element is on the list.
3152         *
3153         * @param objs
3154         * list of changed object
3155         */

3156        private void refresh(Object JavaDoc[] objs) {
3157            if (objs == null || getInput() == null) {
3158                return;
3159            }
3160            Object JavaDoc input = getInput();
3161            for (int i = 0; i < objs.length; i++) {
3162                if (objs[i].equals(input)) {
3163                    refresh();
3164                    break;
3165                }
3166            }
3167        }
3168    }
3169
3170    /**
3171     * Compares items using camel case method.
3172     */

3173    private class CamelCaseComparator implements Comparator JavaDoc {
3174
3175        /*
3176         * (non-Javadoc)
3177         *
3178         * @see java.util.Comparator#compare(java.lang.Object, java.lang.Object)
3179         */

3180        public int compare(Object JavaDoc o1, Object JavaDoc o2) {
3181
3182            int leftCategory = getCamelCaseCategory(o1);
3183            int rightCategory = getCamelCaseCategory(o2);
3184            if (leftCategory < rightCategory)
3185                return -1;
3186            if (leftCategory > rightCategory)
3187                return +1;
3188
3189            return getItemsComparator().compare(o1, o2);
3190        }
3191
3192        private int getCamelCaseCategory(Object JavaDoc item) {
3193            if (filter == null)
3194                return 0;
3195            if (!filter.isCamelCasePattern())
3196                return 0;
3197            return filter.matchesRawNamePattern(item) ? 0 : 1;
3198        }
3199    }
3200
3201    /**
3202     * Compares items according to the history.
3203     */

3204    private class HistoryComparator implements Comparator JavaDoc {
3205
3206        private CamelCaseComparator camelCaseComparator;
3207
3208        /**
3209         *
3210         */

3211        public HistoryComparator() {
3212            this.camelCaseComparator = new CamelCaseComparator();
3213        }
3214
3215        /*
3216         * (non-Javadoc)
3217         *
3218         * @see java.util.Comparator#compare(java.lang.Object, java.lang.Object)
3219         */

3220        public int compare(Object JavaDoc o1, Object JavaDoc o2) {
3221            if ((isHistoryElement(o1) && isHistoryElement(o2))
3222                    || (!isHistoryElement(o1) && !isHistoryElement(o2)))
3223                return this.camelCaseComparator.compare(o1, o2);
3224
3225            if (isHistoryElement(o1))
3226                return -2;
3227            if (isHistoryElement(o2))
3228                return +2;
3229
3230            return 0;
3231        }
3232
3233    }
3234
3235    /**
3236     * Get the control where the search pattern is entered. Any filtering should
3237     * be done using an {@link ItemsFilter}. This control should only be
3238     * accessed for listeners that wish to handle events that do not affect
3239     * filtering such as custom traversal.
3240     *
3241     * @return Control or <code>null</code> if the pattern control has not
3242     * been created.
3243     */

3244    public Control getPatternControl() {
3245        return pattern;
3246    }
3247
3248}
3249
Popular Tags