3 * Copyright (C) 2016 EDIT
4 * European Distributed Institute of Taxonomy
5 * http://www.e-taxonomy.eu
7 * The contents of this file are subject to the Mozilla Public License Version 1.1
8 * See LICENSE.TXT at the top of this package for the full license terms.
10 package eu
.etaxonomy
.taxeditor
.ui
.dialog
.selection
;
12 import java
.io
.IOException
;
13 import java
.io
.StringReader
;
14 import java
.io
.StringWriter
;
15 import java
.util
.ArrayList
;
16 import java
.util
.Arrays
;
17 import java
.util
.Collections
;
18 import java
.util
.Comparator
;
19 import java
.util
.HashMap
;
20 import java
.util
.HashSet
;
21 import java
.util
.Iterator
;
22 import java
.util
.LinkedHashSet
;
23 import java
.util
.List
;
26 import org
.eclipse
.core
.commands
.AbstractHandler
;
27 import org
.eclipse
.core
.commands
.ExecutionEvent
;
28 import org
.eclipse
.core
.commands
.IHandler
;
29 import org
.eclipse
.core
.runtime
.Assert
;
30 import org
.eclipse
.core
.runtime
.CoreException
;
31 import org
.eclipse
.core
.runtime
.IProgressMonitor
;
32 import org
.eclipse
.core
.runtime
.IStatus
;
33 import org
.eclipse
.core
.runtime
.ListenerList
;
34 import org
.eclipse
.core
.runtime
.NullProgressMonitor
;
35 import org
.eclipse
.core
.runtime
.ProgressMonitorWrapper
;
36 import org
.eclipse
.core
.runtime
.Status
;
37 import org
.eclipse
.core
.runtime
.SubProgressMonitor
;
38 import org
.eclipse
.core
.runtime
.jobs
.Job
;
39 import org
.eclipse
.jface
.action
.Action
;
40 import org
.eclipse
.jface
.action
.ActionContributionItem
;
41 import org
.eclipse
.jface
.action
.IAction
;
42 import org
.eclipse
.jface
.action
.IMenuListener
;
43 import org
.eclipse
.jface
.action
.IMenuManager
;
44 import org
.eclipse
.jface
.action
.LegacyActionTools
;
45 import org
.eclipse
.jface
.action
.MenuManager
;
46 import org
.eclipse
.jface
.dialogs
.IDialogSettings
;
47 import org
.eclipse
.jface
.viewers
.ContentViewer
;
48 import org
.eclipse
.jface
.viewers
.DelegatingStyledCellLabelProvider
.IStyledLabelProvider
;
49 import org
.eclipse
.jface
.viewers
.DoubleClickEvent
;
50 import org
.eclipse
.jface
.viewers
.IColorProvider
;
51 import org
.eclipse
.jface
.viewers
.IContentProvider
;
52 import org
.eclipse
.jface
.viewers
.IDoubleClickListener
;
53 import org
.eclipse
.jface
.viewers
.IFontProvider
;
54 import org
.eclipse
.jface
.viewers
.ILabelDecorator
;
55 import org
.eclipse
.jface
.viewers
.ILabelProvider
;
56 import org
.eclipse
.jface
.viewers
.ILabelProviderListener
;
57 import org
.eclipse
.jface
.viewers
.ILazyContentProvider
;
58 import org
.eclipse
.jface
.viewers
.ISelection
;
59 import org
.eclipse
.jface
.viewers
.ISelectionChangedListener
;
60 import org
.eclipse
.jface
.viewers
.IStructuredContentProvider
;
61 import org
.eclipse
.jface
.viewers
.LabelProvider
;
62 import org
.eclipse
.jface
.viewers
.LabelProviderChangedEvent
;
63 import org
.eclipse
.jface
.viewers
.SelectionChangedEvent
;
64 import org
.eclipse
.jface
.viewers
.StructuredSelection
;
65 import org
.eclipse
.jface
.viewers
.StyledCellLabelProvider
;
66 import org
.eclipse
.jface
.viewers
.StyledString
;
67 import org
.eclipse
.jface
.viewers
.TableViewer
;
68 import org
.eclipse
.jface
.viewers
.Viewer
;
69 import org
.eclipse
.jface
.viewers
.ViewerCell
;
70 import org
.eclipse
.jface
.viewers
.ViewerFilter
;
71 import org
.eclipse
.osgi
.util
.NLS
;
72 import org
.eclipse
.swt
.SWT
;
73 import org
.eclipse
.swt
.accessibility
.ACC
;
74 import org
.eclipse
.swt
.accessibility
.AccessibleAdapter
;
75 import org
.eclipse
.swt
.accessibility
.AccessibleEvent
;
76 import org
.eclipse
.swt
.custom
.CLabel
;
77 import org
.eclipse
.swt
.custom
.ViewForm
;
78 import org
.eclipse
.swt
.events
.KeyAdapter
;
79 import org
.eclipse
.swt
.events
.KeyEvent
;
80 import org
.eclipse
.swt
.events
.ModifyEvent
;
81 import org
.eclipse
.swt
.events
.ModifyListener
;
82 import org
.eclipse
.swt
.events
.MouseAdapter
;
83 import org
.eclipse
.swt
.events
.MouseEvent
;
84 import org
.eclipse
.swt
.events
.SelectionAdapter
;
85 import org
.eclipse
.swt
.events
.SelectionEvent
;
86 import org
.eclipse
.swt
.events
.TraverseEvent
;
87 import org
.eclipse
.swt
.events
.TraverseListener
;
88 import org
.eclipse
.swt
.graphics
.Color
;
89 import org
.eclipse
.swt
.graphics
.Font
;
90 import org
.eclipse
.swt
.graphics
.GC
;
91 import org
.eclipse
.swt
.graphics
.Image
;
92 import org
.eclipse
.swt
.graphics
.Point
;
93 import org
.eclipse
.swt
.graphics
.Rectangle
;
94 import org
.eclipse
.swt
.layout
.GridData
;
95 import org
.eclipse
.swt
.layout
.GridLayout
;
96 import org
.eclipse
.swt
.widgets
.Composite
;
97 import org
.eclipse
.swt
.widgets
.Control
;
98 import org
.eclipse
.swt
.widgets
.Display
;
99 import org
.eclipse
.swt
.widgets
.Event
;
100 import org
.eclipse
.swt
.widgets
.Label
;
101 import org
.eclipse
.swt
.widgets
.Menu
;
102 import org
.eclipse
.swt
.widgets
.Shell
;
103 import org
.eclipse
.swt
.widgets
.Table
;
104 import org
.eclipse
.swt
.widgets
.Text
;
105 import org
.eclipse
.swt
.widgets
.ToolBar
;
106 import org
.eclipse
.swt
.widgets
.ToolItem
;
107 import org
.eclipse
.ui
.ActiveShellExpression
;
108 import org
.eclipse
.ui
.IMemento
;
109 import org
.eclipse
.ui
.IWorkbenchCommandConstants
;
110 import org
.eclipse
.ui
.IWorkbenchPreferenceConstants
;
111 import org
.eclipse
.ui
.PlatformUI
;
112 import org
.eclipse
.ui
.WorkbenchException
;
113 import org
.eclipse
.ui
.XMLMemento
;
114 import org
.eclipse
.ui
.dialogs
.FilteredItemsSelectionDialog
;
115 import org
.eclipse
.ui
.dialogs
.SearchPattern
;
116 import org
.eclipse
.ui
.dialogs
.SelectionStatusDialog
;
117 import org
.eclipse
.ui
.handlers
.IHandlerActivation
;
118 import org
.eclipse
.ui
.handlers
.IHandlerService
;
119 import org
.eclipse
.ui
.internal
.IWorkbenchGraphicConstants
;
120 import org
.eclipse
.ui
.internal
.WorkbenchImages
;
121 import org
.eclipse
.ui
.internal
.WorkbenchMessages
;
122 import org
.eclipse
.ui
.internal
.WorkbenchPlugin
;
123 import org
.eclipse
.ui
.progress
.UIJob
;
124 import org
.eclipse
.ui
.statushandlers
.StatusManager
;
131 public abstract class CdmFilteredItemsSelectionDialog
extends SelectionStatusDialog
{
135 private static final String DIALOG_BOUNDS_SETTINGS
= "DialogBoundsSettings"; //$NON-NLS-1$
137 private static final String SHOW_STATUS_LINE
= "ShowStatusLine"; //$NON-NLS-1$
139 private static final String HISTORY_SETTINGS
= "History"; //$NON-NLS-1$
141 private static final String DIALOG_HEIGHT
= "DIALOG_HEIGHT"; //$NON-NLS-1$
143 private static final String DIALOG_WIDTH
= "DIALOG_WIDTH"; //$NON-NLS-1$
146 * Represents an empty selection in the pattern input field (used only for
149 public static final int NONE
= 0;
152 * Pattern input field selection where caret is at the beginning (used only
153 * for initial pattern).
155 public static final int CARET_BEGINNING
= 1;
158 * Represents a full selection in the pattern input field (used only for
161 public static final int FULL_SELECTION
= 2;
163 private Text pattern
;
165 private TableViewer list
;
167 private DetailsContentViewer details
;
170 * It is a duplicate of a field in the CLabel class in DetailsContentViewer.
171 * It is maintained, because the <code>setDetailsLabelProvider()</code>
172 * could be called before content area is created.
174 private ILabelProvider detailsLabelProvider
;
176 private ItemsListLabelProvider itemsListLabelProvider
;
178 private MenuManager menuManager
;
180 private MenuManager contextMenuManager
;
182 private final boolean multi
;
184 private ToolBar toolBar
;
186 private ToolItem toolItem
;
188 private Label progressLabel
;
190 private ToggleStatusLineAction toggleStatusLineAction
;
192 private RemoveHistoryItemAction removeHistoryItemAction
;
194 private ActionContributionItem removeHistoryActionContributionItem
;
196 private IStatus status
;
198 private final RefreshCacheJob refreshCacheJob
;
200 private final RefreshProgressMessageJob refreshProgressMessageJob
= new RefreshProgressMessageJob();
202 private Object
[] currentSelection
;
204 private final ContentProvider contentProvider
;
206 private final FilterHistoryJob filterHistoryJob
;
208 private final FilterJob filterJob
;
210 private ItemsFilter filter
;
212 private List lastCompletedResult
;
214 private ItemsFilter lastCompletedFilter
;
216 private String initialPatternText
;
218 private int selectionMode
;
220 private ItemsListSeparator itemsListSeparator
;
222 private static final String EMPTY_STRING
= ""; //$NON-NLS-1$
224 private boolean refreshWithLastSelection
= false;
226 private IHandlerActivation showViewHandler
;
229 * Creates a new instance of the class.
232 * shell to parent the dialog on
234 * indicates whether dialog allows to select more than one
235 * position in its list of items
237 public CdmFilteredItemsSelectionDialog(Shell shell
, boolean multi
) {
240 filterHistoryJob
= new FilterHistoryJob();
241 filterJob
= new FilterJob();
242 contentProvider
= new ContentProvider();
243 refreshCacheJob
= new RefreshCacheJob();
244 itemsListSeparator
= new ItemsListSeparator(
245 WorkbenchMessages
.FilteredItemsSelectionDialog_separatorLabel
);
246 selectionMode
= NONE
;
250 * Creates a new instance of the class. Created dialog won't allow to select
251 * more than one item.
254 * shell to parent the dialog on
256 public CdmFilteredItemsSelectionDialog(Shell shell
) {
261 * Adds viewer filter to the dialog items list.
266 protected void addListFilter(ViewerFilter filter
) {
267 contentProvider
.addFilter(filter
);
271 * Sets a new label provider for items in the list. If the label provider
272 * also implements {@link
273 * org.eclipse.jface.viewers.DelegatingStyledCellLabelProvider
274 * .IStyledLabelProvider}, the style text labels provided by it will be used
275 * provided that the corresponding preference is set.
277 * @see IWorkbenchPreferenceConstants#USE_COLORED_LABELS
279 * @param listLabelProvider
280 * the label provider for items in the list
282 public void setListLabelProvider(ILabelProvider listLabelProvider
) {
283 getItemsListLabelProvider().setProvider(listLabelProvider
);
287 * Returns the label decorator for selected items in the list.
289 * @return the label decorator for selected items in the list
291 private ILabelDecorator
getListSelectionLabelDecorator() {
292 return getItemsListLabelProvider().getSelectionDecorator();
296 * Sets the label decorator for selected items in the list.
298 * @param listSelectionLabelDecorator
299 * the label decorator for selected items in the list
301 public void setListSelectionLabelDecorator(
302 ILabelDecorator listSelectionLabelDecorator
) {
303 getItemsListLabelProvider().setSelectionDecorator(
304 listSelectionLabelDecorator
);
308 * Returns the item list label provider.
310 * @return the item list label provider
312 private ItemsListLabelProvider
getItemsListLabelProvider() {
313 if (itemsListLabelProvider
== null) {
314 itemsListLabelProvider
= new ItemsListLabelProvider(
315 new LabelProvider(), null);
317 return itemsListLabelProvider
;
321 * Sets label provider for the details field.
323 * For a single selection, the element sent to
324 * {@link ILabelProvider#getImage(Object)} and
325 * {@link ILabelProvider#getText(Object)} is the selected object, for
326 * multiple selection a {@link String} with amount of selected items is the
329 * @see #getSelectedItems() getSelectedItems() can be used to retrieve
330 * selected items and get the items count.
332 * @param detailsLabelProvider
333 * the label provider for the details field
335 public void setDetailsLabelProvider(ILabelProvider detailsLabelProvider
) {
336 this.detailsLabelProvider
= detailsLabelProvider
;
337 if (details
!= null) {
338 details
.setLabelProvider(detailsLabelProvider
);
342 private ILabelProvider
getDetailsLabelProvider() {
343 if (detailsLabelProvider
== null) {
344 detailsLabelProvider
= new LabelProvider();
346 return detailsLabelProvider
;
352 * @see org.eclipse.jface.window.Window#create()
355 public void create() {
361 * Restores dialog using persisted settings. The default implementation
362 * restores the status of the details line and the selection history.
365 * settings used to restore dialog
367 protected void restoreDialog(IDialogSettings settings
) {
368 boolean toggleStatusLine
= true;
370 if (settings
.get(SHOW_STATUS_LINE
) != null) {
371 toggleStatusLine
= settings
.getBoolean(SHOW_STATUS_LINE
);
374 toggleStatusLineAction
.setChecked(toggleStatusLine
);
376 details
.setVisible(toggleStatusLine
);
378 String setting
= settings
.get(HISTORY_SETTINGS
);
379 if (setting
!= null) {
381 IMemento memento
= XMLMemento
.createReadRoot(new StringReader(
383 this.contentProvider
.loadHistory(memento
);
384 } catch (WorkbenchException e
) {
385 // Simply don't restore the settings
391 PlatformUI
.PLUGIN_ID
,
393 WorkbenchMessages
.FilteredItemsSelectionDialog_restoreError
,
402 * @see org.eclipse.jface.window.Window#close()
405 public boolean close() {
406 this.filterJob
.cancel();
407 this.refreshCacheJob
.cancel();
408 this.refreshProgressMessageJob
.cancel();
409 if (showViewHandler
!= null) {
410 IHandlerService service
= (IHandlerService
) PlatformUI
411 .getWorkbench().getService(IHandlerService
.class);
412 service
.deactivateHandler(showViewHandler
);
413 showViewHandler
.getHandler().dispose();
414 showViewHandler
= null;
416 if (menuManager
!= null) {
417 menuManager
.dispose();
419 if (contextMenuManager
!= null) {
420 contextMenuManager
.dispose();
422 storeDialog(getDialogSettings());
423 return super.close();
427 * Stores dialog settings.
430 * settings used to store dialog
432 protected void storeDialog(IDialogSettings settings
) {
433 settings
.put(SHOW_STATUS_LINE
, toggleStatusLineAction
.isChecked());
435 XMLMemento memento
= XMLMemento
.createWriteRoot(HISTORY_SETTINGS
);
436 this.contentProvider
.saveHistory(memento
);
437 StringWriter writer
= new StringWriter();
439 memento
.save(writer
);
440 settings
.put(HISTORY_SETTINGS
, writer
.getBuffer().toString());
441 } catch (IOException e
) {
442 // Simply don't store the settings
448 PlatformUI
.PLUGIN_ID
,
450 WorkbenchMessages
.FilteredItemsSelectionDialog_storeError
,
456 * Create a new header which is labelled by headerLabel.
459 * @return Label the label of the header
461 private Label
createHeader(Composite parent
) {
462 Composite header
= new Composite(parent
, SWT
.NONE
);
464 GridLayout layout
= new GridLayout();
465 layout
.numColumns
= 2;
466 layout
.marginWidth
= 0;
467 layout
.marginHeight
= 0;
468 header
.setLayout(layout
);
470 Label headerLabel
= new Label(header
, SWT
.NONE
);
471 headerLabel
.setText((getMessage() != null && getMessage().trim()
472 .length() > 0) ?
getMessage()
473 : WorkbenchMessages
.FilteredItemsSelectionDialog_patternLabel
);
474 headerLabel
.addTraverseListener(new TraverseListener() {
476 public void keyTraversed(TraverseEvent e
) {
477 if (e
.detail
== SWT
.TRAVERSE_MNEMONIC
&& e
.doit
) {
478 e
.detail
= SWT
.TRAVERSE_NONE
;
484 GridData gd
= new GridData(GridData
.FILL_HORIZONTAL
);
485 headerLabel
.setLayoutData(gd
);
487 createViewMenu(header
);
488 header
.setLayoutData(gd
);
493 * Create the labels for the list and the progress. Return the list label.
498 private Label
createLabels(Composite parent
) {
499 Composite labels
= new Composite(parent
, SWT
.NONE
);
501 GridLayout layout
= new GridLayout();
502 layout
.numColumns
= 2;
503 layout
.marginWidth
= 0;
504 layout
.marginHeight
= 0;
505 labels
.setLayout(layout
);
507 Label listLabel
= new Label(labels
, SWT
.NONE
);
509 .setText(WorkbenchMessages
.FilteredItemsSelectionDialog_listLabel
);
511 listLabel
.addTraverseListener(new TraverseListener() {
513 public void keyTraversed(TraverseEvent e
) {
514 if (e
.detail
== SWT
.TRAVERSE_MNEMONIC
&& e
.doit
) {
515 e
.detail
= SWT
.TRAVERSE_NONE
;
516 list
.getTable().setFocus();
521 GridData gd
= new GridData(GridData
.FILL_HORIZONTAL
);
522 listLabel
.setLayoutData(gd
);
524 progressLabel
= new Label(labels
, SWT
.RIGHT
);
525 progressLabel
.setLayoutData(gd
);
527 labels
.setLayoutData(gd
);
531 private void createViewMenu(Composite parent
) {
532 toolBar
= new ToolBar(parent
, SWT
.FLAT
);
533 toolItem
= new ToolItem(toolBar
, SWT
.PUSH
, 0);
535 GridData data
= new GridData();
536 data
.horizontalAlignment
= GridData
.END
;
537 toolBar
.setLayoutData(data
);
539 toolBar
.addMouseListener(new MouseAdapter() {
541 public void mouseDown(MouseEvent e
) {
546 toolItem
.setImage(WorkbenchImages
547 .getImage(IWorkbenchGraphicConstants
.IMG_LCL_VIEW_MENU
));
549 .setToolTipText(WorkbenchMessages
.FilteredItemsSelectionDialog_menu
);
550 toolItem
.addSelectionListener(new SelectionAdapter() {
552 public void widgetSelected(SelectionEvent e
) {
557 menuManager
= new MenuManager();
559 fillViewMenu(menuManager
);
561 IHandlerService service
= (IHandlerService
) PlatformUI
.getWorkbench()
562 .getService(IHandlerService
.class);
563 IHandler handler
= new AbstractHandler() {
565 public Object
execute(ExecutionEvent event
) {
570 showViewHandler
= service
.activateHandler(
571 IWorkbenchCommandConstants
.WINDOW_SHOW_VIEW_MENU
, handler
,
572 new ActiveShellExpression(getShell()));
576 * Fills the menu of the dialog.
581 protected void fillViewMenu(IMenuManager menuManager
) {
582 toggleStatusLineAction
= new ToggleStatusLineAction();
583 menuManager
.add(toggleStatusLineAction
);
586 private void showViewMenu() {
587 Menu menu
= menuManager
.createContextMenu(getShell());
588 Rectangle bounds
= toolItem
.getBounds();
589 Point topLeft
= new Point(bounds
.x
, bounds
.y
+ bounds
.height
);
590 topLeft
= toolBar
.toDisplay(topLeft
);
591 menu
.setLocation(topLeft
.x
, topLeft
.y
);
592 menu
.setVisible(true);
596 * Hook that allows to add actions to the context menu.
598 * Subclasses may extend in order to add other actions.</p>
600 * @param menuManager the context menu manager
603 protected void fillContextMenu(IMenuManager menuManager
) {
604 List selectedElements
= ((StructuredSelection
)list
.getSelection()).toList();
608 for (Iterator it
= selectedElements
.iterator(); it
.hasNext();) {
610 if (item
instanceof ItemsListSeparator
|| !isHistoryElement(item
)) {
615 if (selectedElements
.size() > 0) {
616 removeHistoryItemAction
.setText(WorkbenchMessages
.FilteredItemsSelectionDialog_removeItemsFromHistoryAction
);
618 menuManager
.add(removeHistoryActionContributionItem
);
623 private void createPopupMenu() {
624 removeHistoryItemAction
= new RemoveHistoryItemAction();
625 removeHistoryActionContributionItem
= new ActionContributionItem(
626 removeHistoryItemAction
);
628 contextMenuManager
= new MenuManager();
629 contextMenuManager
.setRemoveAllWhenShown(true);
630 contextMenuManager
.addMenuListener(new IMenuListener() {
632 public void menuAboutToShow(IMenuManager manager
) {
633 fillContextMenu(manager
);
637 final Table table
= list
.getTable();
638 Menu menu
= contextMenuManager
.createContextMenu(table
);
643 * Creates an extra content area, which will be located above the details.
646 * parent to create the dialog widgets in
647 * @return an extra content area
649 protected abstract Control
createExtendedContentArea(Composite parent
);
654 * @see org.eclipse.jface.dialogs.Dialog#createDialogArea(org.eclipse.swt.widgets.Composite)
657 protected Control
createDialogArea(Composite parent
) {
658 Composite dialogArea
= (Composite
) super.createDialogArea(parent
);
660 Composite content
= new Composite(dialogArea
, SWT
.NONE
);
661 GridData gd
= new GridData(GridData
.FILL_BOTH
);
662 content
.setLayoutData(gd
);
664 GridLayout layout
= new GridLayout();
665 layout
.numColumns
= 1;
666 layout
.marginWidth
= 0;
667 layout
.marginHeight
= 0;
668 content
.setLayout(layout
);
670 final Label headerLabel
= createHeader(content
);
672 pattern
= new Text(content
, SWT
.SINGLE
| SWT
.BORDER
| SWT
.SEARCH
| SWT
.ICON_CANCEL
);
673 pattern
.getAccessible().addAccessibleListener(new AccessibleAdapter() {
675 public void getName(AccessibleEvent e
) {
676 e
.result
= LegacyActionTools
.removeMnemonics(headerLabel
680 gd
= new GridData(GridData
.FILL_HORIZONTAL
);
681 pattern
.setLayoutData(gd
);
683 final Label listLabel
= createLabels(content
);
685 list
= new TableViewer(content
, (multi ? SWT
.MULTI
: SWT
.SINGLE
)
686 | SWT
.BORDER
| SWT
.V_SCROLL
| SWT
.VIRTUAL
);
687 list
.getTable().getAccessible().addAccessibleListener(
688 new AccessibleAdapter() {
690 public void getName(AccessibleEvent e
) {
691 if (e
.childID
== ACC
.CHILDID_SELF
) {
692 e
.result
= LegacyActionTools
693 .removeMnemonics(listLabel
.getText());
697 list
.setContentProvider(contentProvider
);
698 list
.setLabelProvider(getItemsListLabelProvider());
699 list
.setInput(new Object
[0]);
700 list
.setItemCount(contentProvider
.getNumberOfElements());
701 gd
= new GridData(GridData
.FILL_BOTH
);
702 applyDialogFont(list
.getTable());
703 gd
.heightHint
= list
.getTable().getItemHeight() * 15;
704 list
.getTable().setLayoutData(gd
);
708 pattern
.addModifyListener(new ModifyListener() {
710 public void modifyText(ModifyEvent e
) {
715 pattern
.addKeyListener(new KeyAdapter() {
717 public void keyPressed(KeyEvent e
) {
718 if (e
.keyCode
== SWT
.ARROW_DOWN
) {
719 if (list
.getTable().getItemCount() > 0) {
720 list
.getTable().setFocus();
726 list
.addSelectionChangedListener(new ISelectionChangedListener() {
728 public void selectionChanged(SelectionChangedEvent event
) {
729 StructuredSelection selection
= (StructuredSelection
) event
731 handleSelected(selection
);
735 list
.addDoubleClickListener(new IDoubleClickListener() {
737 public void doubleClick(DoubleClickEvent event
) {
742 list
.getTable().addKeyListener(new KeyAdapter() {
744 public void keyPressed(KeyEvent e
) {
746 if (e
.keyCode
== SWT
.DEL
) {
748 List selectedElements
= ((StructuredSelection
) list
749 .getSelection()).toList();
752 boolean isSelectedHistory
= true;
754 for (Iterator it
= selectedElements
.iterator(); it
757 if (item
instanceof ItemsListSeparator
758 || !isHistoryElement(item
)) {
759 isSelectedHistory
= false;
763 if (isSelectedHistory
) {
764 removeSelectedItems(selectedElements
);
769 if (e
.keyCode
== SWT
.ARROW_UP
&& (e
.stateMask
& SWT
.SHIFT
) != 0
770 && (e
.stateMask
& SWT
.CTRL
) != 0) {
771 StructuredSelection selection
= (StructuredSelection
) list
774 if (selection
.size() == 1) {
775 Object element
= selection
.getFirstElement();
776 if (element
.equals(list
.getElementAt(0))) {
779 if (list
.getElementAt(list
.getTable()
780 .getSelectionIndex() - 1) instanceof ItemsListSeparator
) {
781 list
.getTable().setSelection(
782 list
.getTable().getSelectionIndex() - 1);
784 list
.getTable().notifyListeners(SWT
.Selection
,
790 if (e
.keyCode
== SWT
.ARROW_DOWN
791 && (e
.stateMask
& SWT
.SHIFT
) != 0
792 && (e
.stateMask
& SWT
.CTRL
) != 0) {
795 .getElementAt(list
.getTable().getSelectionIndex() + 1) instanceof ItemsListSeparator
) {
796 list
.getTable().setSelection(
797 list
.getTable().getSelectionIndex() + 1);
799 list
.getTable().notifyListeners(SWT
.Selection
, new Event());
805 createExtendedContentArea(content
);
807 details
= new DetailsContentViewer(content
, SWT
.BORDER
| SWT
.FLAT
);
808 details
.setVisible(toggleStatusLineAction
.isChecked());
809 details
.setContentProvider(new NullContentProvider());
810 details
.setLabelProvider(getDetailsLabelProvider());
812 applyDialogFont(content
);
814 restoreDialog(getDialogSettings());
816 if (initialPatternText
!= null) {
817 pattern
.setText(initialPatternText
);
820 switch (selectionMode
) {
821 case CARET_BEGINNING
:
822 pattern
.setSelection(0, 0);
825 pattern
.setSelection(0, initialPatternText
.length());
829 // apply filter even if pattern is empty (display history)
836 * This method is a hook for subclasses to override default dialog behavior.
837 * The <code>handleDoubleClick()</code> method handles double clicks on
838 * the list of filtered elements.
840 * Current implementation makes double-clicking on the list do the same as
841 * pressing <code>OK</code> button on the dialog.
843 protected void handleDoubleClick() {
848 * Refreshes the details field according to the current selection in the
851 private void refreshDetails() {
852 StructuredSelection selection
= getSelectedItems();
854 switch (selection
.size()) {
856 details
.setInput(null);
859 details
.setInput(selection
.getFirstElement());
865 WorkbenchMessages
.FilteredItemsSelectionDialog_nItemsSelected
,
866 new Integer(selection
.size())));
873 * Handle selection in the items list by updating labels of selected and
874 * unselected items and refresh the details field using the selection.
879 protected void handleSelected(StructuredSelection selection
) {
880 IStatus status
= new Status(IStatus
.OK
, PlatformUI
.PLUGIN_ID
,
881 IStatus
.OK
, EMPTY_STRING
, null);
883 Object
[] lastSelection
= currentSelection
;
885 currentSelection
= selection
.toArray();
887 if (selection
.size() == 0) {
888 status
= new Status(IStatus
.ERROR
, PlatformUI
.PLUGIN_ID
,
889 IStatus
.ERROR
, EMPTY_STRING
, null);
891 if (lastSelection
!= null
892 && getListSelectionLabelDecorator() != null) {
893 list
.update(lastSelection
, null);
896 currentSelection
= null;
899 status
= new Status(IStatus
.ERROR
, PlatformUI
.PLUGIN_ID
,
900 IStatus
.ERROR
, EMPTY_STRING
, null);
902 List items
= selection
.toList();
905 IStatus tempStatus
= null;
907 for (Iterator it
= items
.iterator(); it
.hasNext();) {
908 Object o
= it
.next();
910 if (o
instanceof ItemsListSeparator
) {
915 tempStatus
= validateItem(item
);
917 if (tempStatus
.isOK()) {
918 status
= new Status(IStatus
.OK
, PlatformUI
.PLUGIN_ID
,
919 IStatus
.OK
, EMPTY_STRING
, null);
922 // if any selected element is not valid status is set to
928 if (lastSelection
!= null
929 && getListSelectionLabelDecorator() != null) {
930 list
.update(lastSelection
, null);
933 if (getListSelectionLabelDecorator() != null) {
934 list
.update(currentSelection
, null);
939 updateStatus(status
);
945 * @see org.eclipse.jface.window.Dialog#getDialogBoundsSettings()
948 protected IDialogSettings
getDialogBoundsSettings() {
949 IDialogSettings settings
= getDialogSettings();
950 IDialogSettings section
= settings
.getSection(DIALOG_BOUNDS_SETTINGS
);
951 if (section
== null) {
952 section
= settings
.addNewSection(DIALOG_BOUNDS_SETTINGS
);
953 section
.put(DIALOG_HEIGHT
, 500);
954 section
.put(DIALOG_WIDTH
, 600);
960 * Returns the dialog settings. Returned object can't be null.
962 * @return return dialog settings for this dialog
964 protected abstract IDialogSettings
getDialogSettings();
967 * Refreshes the dialog - has to be called in UI thread.
969 public void refresh() {
970 if (list
!= null && !list
.getTable().isDisposed()) {
972 List lastRefreshSelection
= ((StructuredSelection
) list
973 .getSelection()).toList();
974 list
.getTable().deselectAll();
976 list
.setItemCount(contentProvider
.getNumberOfElements());
979 if (list
.getTable().getItemCount() > 0) {
980 // preserve previous selection
981 if (refreshWithLastSelection
&& lastRefreshSelection
!= null
982 && lastRefreshSelection
.size() > 0) {
983 list
.setSelection(new StructuredSelection(
984 lastRefreshSelection
));
986 refreshWithLastSelection
= true;
987 list
.getTable().setSelection(0);
988 list
.getTable().notifyListeners(SWT
.Selection
, new Event());
991 list
.setSelection(StructuredSelection
.EMPTY
);
996 scheduleProgressMessageRefresh();
1000 * Updates the progress label.
1005 public void updateProgressLabel() {
1006 scheduleProgressMessageRefresh();
1010 * Notifies the content provider - fires filtering of content provider
1011 * elements. During the filtering, a separator between history and workspace
1014 * This is a long running operation and should be called in a job.
1016 * @param checkDuplicates
1017 * <code>true</code> if data concerning elements duplication
1018 * should be computed - it takes much more time than the standard
1021 * a progress monitor or <code>null</code> if no monitor is
1024 public void reloadCache(boolean checkDuplicates
, IProgressMonitor monitor
) {
1025 if (list
!= null && !list
.getTable().isDisposed()
1026 && contentProvider
!= null) {
1027 contentProvider
.reloadCache(checkDuplicates
, monitor
);
1032 * Schedule refresh job.
1034 public void scheduleRefresh() {
1035 refreshCacheJob
.cancelAll();
1036 refreshCacheJob
.schedule();
1040 * Schedules progress message refresh.
1042 public void scheduleProgressMessageRefresh() {
1043 if (filterJob
.getState() != Job
.RUNNING
1044 && refreshProgressMessageJob
.getState() != Job
.RUNNING
) {
1045 refreshProgressMessageJob
.scheduleProgressRefresh(null);
1052 * @see org.eclipse.ui.dialogs.SelectionStatusDialog#computeResult()
1055 protected void computeResult() {
1057 List selectedElements
= ((StructuredSelection
) list
.getSelection())
1060 List objectsToReturn
= new ArrayList();
1064 for (Iterator it
= selectedElements
.iterator(); it
.hasNext();) {
1067 if (!(item
instanceof ItemsListSeparator
)) {
1068 accessedHistoryItem(item
);
1069 objectsToReturn
.add(item
);
1073 setResult(objectsToReturn
);
1077 * @see org.eclipse.ui.dialogs.SelectionStatusDialog#updateStatus(org.eclipse.core.runtime.IStatus)
1080 protected void updateStatus(IStatus status
) {
1081 this.status
= status
;
1082 super.updateStatus(status
);
1086 * @see Dialog#okPressed()
1089 protected void okPressed() {
1091 && (status
.isOK() || status
.getCode() == IStatus
.INFO
)) {
1097 * Sets the initial pattern used by the filter. This text is copied into the
1098 * selection input on the dialog. A full selection is used in the pattern
1102 * initial pattern for the filter
1103 * @see FilteredItemsSelectionDialog#FULL_SELECTION
1105 public void setInitialPattern(String text
) {
1106 setInitialPattern(text
, FULL_SELECTION
);
1110 * Sets the initial pattern used by the filter. This text is copied into the
1111 * selection input on the dialog. The <code>selectionMode</code> is used
1112 * to choose selection type for the input field.
1115 * initial pattern for the filter
1116 * @param selectionMode
1117 * one of: {@link FilteredItemsSelectionDialog#NONE},
1118 * {@link FilteredItemsSelectionDialog#CARET_BEGINNING},
1119 * {@link FilteredItemsSelectionDialog#FULL_SELECTION}
1121 public void setInitialPattern(String text
, int selectionMode
) {
1122 this.initialPatternText
= text
;
1123 this.selectionMode
= selectionMode
;
1127 * Gets initial pattern.
1129 * @return initial pattern, or <code>null</code> if initial pattern is not
1132 protected String
getInitialPattern() {
1133 return this.initialPatternText
;
1137 * Returns the current selection.
1139 * @return the current selection
1141 protected StructuredSelection
getSelectedItems() {
1143 StructuredSelection selection
= (StructuredSelection
) list
1146 List selectedItems
= selection
.toList();
1147 Object itemToRemove
= null;
1149 for (Iterator it
= selection
.iterator(); it
.hasNext();) {
1150 Object item
= it
.next();
1151 if (item
instanceof ItemsListSeparator
) {
1152 itemToRemove
= item
;
1157 if (itemToRemove
== null) {
1158 return new StructuredSelection(selectedItems
);
1160 // Create a new selection without the collision
1161 List newItems
= new ArrayList(selectedItems
);
1162 newItems
.remove(itemToRemove
);
1163 return new StructuredSelection(newItems
);
1168 * Validates the item. When items on the items list are selected or
1169 * deselected, it validates each item in the selection and the dialog status
1170 * depends on all validations.
1173 * an item to be checked
1174 * @return status of the dialog to be set
1176 protected abstract IStatus
validateItem(Object item
);
1179 * Creates an instance of a filter.
1181 * @return a filter for items on the items list. Can be <code>null</code>,
1182 * no filtering will be applied then, causing no item to be shown in
1185 protected abstract ItemsFilter
createFilter();
1188 * Applies the filter created by <code>createFilter()</code> method to the
1189 * items list. When new filter is different than previous one it will cause
1192 protected void applyFilter() {
1194 ItemsFilter newFilter
= createFilter();
1196 // don't apply filtering for patterns which mean the same, for example:
1198 if (filter
!= null && filter
.equalsFilter(newFilter
)) {
1202 filterHistoryJob
.cancel();
1205 this.filter
= newFilter
;
1207 if (this.filter
!= null) {
1208 filterHistoryJob
.schedule();
1213 * Returns comparator to sort items inside content provider. Returned object
1214 * will be probably created as an anonymous class. Parameters passed to the
1215 * <code>compare(java.lang.Object, java.lang.Object)</code> are going to
1216 * be the same type as the one used in the content provider.
1218 * @return comparator to sort items content provider
1220 protected abstract Comparator
getItemsComparator();
1223 * Fills the content provider with matching items.
1225 * @param contentProvider
1226 * collector to add items to.
1227 * {@link FilteredItemsSelectionDialog.AbstractContentProvider#add(Object, FilteredItemsSelectionDialog.ItemsFilter)}
1228 * only adds items that pass the given <code>itemsFilter</code>.
1229 * @param itemsFilter
1231 * @param progressMonitor
1232 * must be used to report search progress. The state of this
1233 * progress monitor reflects the state of the filtering process.
1234 * @throws CoreException
1236 protected abstract void fillContentProvider(
1237 AbstractContentProvider contentProvider
, ItemsFilter itemsFilter
,
1238 IProgressMonitor progressMonitor
) throws CoreException
;
1241 * Removes selected items from history.
1244 * items to be removed
1246 private void removeSelectedItems(List items
) {
1247 for (Iterator iter
= items
.iterator(); iter
.hasNext();) {
1248 Object item
= iter
.next();
1249 removeHistoryItem(item
);
1251 refreshWithLastSelection
= false;
1252 contentProvider
.refresh();
1256 * Removes an item from history.
1260 * @return removed item
1262 protected Object
removeHistoryItem(Object item
) {
1263 return contentProvider
.removeHistoryElement(item
);
1267 * Adds item to history.
1270 * the item to be added
1272 protected void accessedHistoryItem(Object item
) {
1273 contentProvider
.addHistoryElement(item
);
1277 * Returns a history comparator.
1279 * @return decorated comparator
1281 private Comparator
getHistoryComparator() {
1282 return new HistoryComparator();
1286 * Returns the history of selected elements.
1288 * @return history of selected elements, or <code>null</code> if it is not
1291 protected SelectionHistory
getSelectionHistory() {
1292 return this.contentProvider
.getSelectionHistory();
1298 * @param selectionHistory
1301 protected void setSelectionHistory(SelectionHistory selectionHistory
) {
1302 if (this.contentProvider
!= null) {
1303 this.contentProvider
.setSelectionHistory(selectionHistory
);
1308 * Indicates whether the given item is a history item.
1311 * the item to be investigated
1312 * @return <code>true</code> if the given item exists in history,
1313 * <code>false</code> otherwise
1315 public boolean isHistoryElement(Object item
) {
1316 return this.contentProvider
.isHistoryElement(item
);
1320 * Indicates whether the given item is a duplicate.
1323 * the item to be investigated
1324 * @return <code>true</code> if the item is duplicate, <code>false</code>
1327 public boolean isDuplicateElement(Object item
) {
1328 return this.contentProvider
.isDuplicateElement(item
);
1332 * Sets separator label
1334 * @param separatorLabel
1335 * the label showed on separator
1337 public void setSeparatorLabel(String separatorLabel
) {
1338 this.itemsListSeparator
= new ItemsListSeparator(separatorLabel
);
1342 * Returns name for then given object.
1345 * an object from the content provider. Subclasses should pay
1346 * attention to the passed argument. They should either only pass
1347 * objects of a known type (one used in content provider) or make
1348 * sure that passed parameter is the expected one (by type
1349 * checking like <code>instanceof</code> inside the method).
1350 * @return name of the given item
1352 public abstract String
getElementName(Object item
);
1354 private class ToggleStatusLineAction
extends Action
{
1357 * Creates a new instance of the class.
1359 public ToggleStatusLineAction() {
1361 WorkbenchMessages
.FilteredItemsSelectionDialog_toggleStatusAction
,
1362 IAction
.AS_CHECK_BOX
);
1367 details
.setVisible(isChecked());
1372 * Only refreshes UI on the basis of an already sorted and filtered set of
1375 * Standard invocation scenario:
1377 * <li>filtering job (<code>FilterJob</code> class extending
1378 * <code>Job</code> class)</li>
1379 * <li>cache refresh without checking for duplicates (<code>RefreshCacheJob</code>
1380 * class extending <code>Job</code> class)</li>
1381 * <li>UI refresh (<code>RefreshJob</code> class extending
1382 * <code>UIJob</code> class)</li>
1383 * <li>cache refresh with checking for duplicates (<cod>CacheRefreshJob</code>
1384 * class extending <code>Job</code> class)</li>
1385 * <li>UI refresh (<code>RefreshJob</code> class extending <code>UIJob</code>
1388 * The scenario is rather complicated, but it had to be applied, because:
1390 * <li> refreshing cache is rather a long action and cannot be run in the UI -
1391 * cannot be run in a UIJob</li>
1392 * <li> refreshing cache checking for duplicates is twice as long as
1393 * refreshing cache without checking for duplicates; results of the search
1394 * could be displayed earlier</li>
1395 * <li> refreshing the UI have to be run in a UIJob</li>
1398 * @see org.eclipse.ui.dialogs.FilteredItemsSelectionDialog.FilterJob
1399 * @see org.eclipse.ui.dialogs.FilteredItemsSelectionDialog.RefreshJob
1400 * @see org.eclipse.ui.dialogs.FilteredItemsSelectionDialog.RefreshCacheJob
1402 private class RefreshJob
extends UIJob
{
1405 * Creates a new instance of the class.
1407 public RefreshJob() {
1408 super(CdmFilteredItemsSelectionDialog
.this.getParentShell()
1410 WorkbenchMessages
.FilteredItemsSelectionDialog_refreshJob
);
1417 * @see org.eclipse.ui.progress.UIJob#runInUIThread(org.eclipse.core.runtime.IProgressMonitor)
1420 public IStatus
runInUIThread(IProgressMonitor monitor
) {
1421 if (monitor
.isCanceled()) {
1422 return new Status(IStatus
.OK
, WorkbenchPlugin
.PI_WORKBENCH
,
1423 IStatus
.OK
, EMPTY_STRING
, null);
1426 if (CdmFilteredItemsSelectionDialog
.this != null) {
1427 CdmFilteredItemsSelectionDialog
.this.refresh();
1430 return new Status(IStatus
.OK
, PlatformUI
.PLUGIN_ID
, IStatus
.OK
,
1431 EMPTY_STRING
, null);
1437 * Refreshes the progress message cyclically with 500 milliseconds delay.
1438 * <code>RefreshProgressMessageJob</code> is strictly connected with
1439 * <code>GranualProgressMonitor</code> and use it to to get progress
1440 * message and to decide about break of cyclical refresh.
1442 private class RefreshProgressMessageJob
extends UIJob
{
1444 private GranualProgressMonitor progressMonitor
;
1447 * Creates a new instance of the class.
1449 public RefreshProgressMessageJob() {
1451 CdmFilteredItemsSelectionDialog
.this.getParentShell()
1453 WorkbenchMessages
.FilteredItemsSelectionDialog_progressRefreshJob
);
1460 * @see org.eclipse.ui.progress.UIJob#runInUIThread(org.eclipse.core.runtime.IProgressMonitor)
1463 public IStatus
runInUIThread(IProgressMonitor monitor
) {
1465 if (!progressLabel
.isDisposed()) {
1466 progressLabel
.setText(progressMonitor
!= null ? progressMonitor
1467 .getMessage() : EMPTY_STRING
);
1470 if (progressMonitor
== null || progressMonitor
.isDone()) {
1471 return new Status(IStatus
.CANCEL
, PlatformUI
.PLUGIN_ID
,
1472 IStatus
.CANCEL
, EMPTY_STRING
, null);
1475 // Schedule cyclical with 500 milliseconds delay
1478 return new Status(IStatus
.OK
, PlatformUI
.PLUGIN_ID
, IStatus
.OK
,
1479 EMPTY_STRING
, null);
1483 * Schedule progress refresh job.
1485 * @param progressMonitor
1486 * used during refresh progress label
1488 public void scheduleProgressRefresh(
1489 GranualProgressMonitor progressMonitor
) {
1490 this.progressMonitor
= progressMonitor
;
1491 // Schedule with initial delay to avoid flickering when the user
1499 * A job responsible for computing filtered items list presented using
1500 * <code>RefreshJob</code>.
1502 * @see FilteredItemsSelectionDialog.RefreshJob
1505 private class RefreshCacheJob
extends Job
{
1507 private final RefreshJob refreshJob
= new RefreshJob();
1510 * Creates a new instance of the class.
1512 public RefreshCacheJob() {
1514 WorkbenchMessages
.FilteredItemsSelectionDialog_cacheRefreshJob
);
1519 * Stops the job and all sub-jobs.
1521 public void cancelAll() {
1523 refreshJob
.cancel();
1529 * @see org.eclipse.core.runtime.jobs.Job#run(org.eclipse.core.runtime.IProgressMonitor)
1532 protected IStatus
run(IProgressMonitor monitor
) {
1533 if (monitor
.isCanceled()) {
1534 return new Status(IStatus
.CANCEL
, WorkbenchPlugin
.PI_WORKBENCH
,
1535 IStatus
.CANCEL
, EMPTY_STRING
, null);
1538 if (CdmFilteredItemsSelectionDialog
.this != null) {
1539 GranualProgressMonitor wrappedMonitor
= new GranualProgressMonitor(
1541 CdmFilteredItemsSelectionDialog
.this.reloadCache(true,
1545 if (!monitor
.isCanceled()) {
1546 refreshJob
.schedule();
1549 return new Status(IStatus
.OK
, PlatformUI
.PLUGIN_ID
, IStatus
.OK
,
1550 EMPTY_STRING
, null);
1557 * @see org.eclipse.core.runtime.jobs.Job#canceling()
1560 protected void canceling() {
1562 contentProvider
.stopReloadingCache();
1567 private class RemoveHistoryItemAction
extends Action
{
1570 * Creates a new instance of the class.
1572 public RemoveHistoryItemAction() {
1574 WorkbenchMessages
.FilteredItemsSelectionDialog_removeItemsFromHistoryAction
);
1580 * @see org.eclipse.jface.action.Action#run()
1584 List selectedElements
= ((StructuredSelection
) list
.getSelection())
1586 removeSelectedItems(selectedElements
);
1590 private static boolean showColoredLabels() {
1591 return PlatformUI
.getPreferenceStore().getBoolean(IWorkbenchPreferenceConstants
.USE_COLORED_LABELS
);
1594 private class ItemsListLabelProvider
extends StyledCellLabelProvider
1595 implements ILabelProviderListener
{
1596 private ILabelProvider provider
;
1598 private ILabelDecorator selectionDecorator
;
1600 // Need to keep our own list of listeners
1601 private final ListenerList listeners
= new ListenerList();
1604 * Creates a new instance of the class.
1607 * the label provider for all items, not <code>null</code>
1608 * @param selectionDecorator
1609 * the decorator for selected items, can be <code>null</code>
1611 public ItemsListLabelProvider(ILabelProvider provider
,
1612 ILabelDecorator selectionDecorator
) {
1613 Assert
.isNotNull(provider
);
1614 this.provider
= provider
;
1615 this.selectionDecorator
= selectionDecorator
;
1617 setOwnerDrawEnabled(showColoredLabels() && provider
instanceof IStyledLabelProvider
);
1619 provider
.addListener(this);
1621 if (selectionDecorator
!= null) {
1622 selectionDecorator
.addListener(this);
1627 * Sets new selection decorator.
1629 * @param newSelectionDecorator
1630 * new label decorator for selected items in the list
1632 public void setSelectionDecorator(ILabelDecorator newSelectionDecorator
) {
1633 if (selectionDecorator
!= null) {
1634 selectionDecorator
.removeListener(this);
1635 selectionDecorator
.dispose();
1638 selectionDecorator
= newSelectionDecorator
;
1640 if (selectionDecorator
!= null) {
1641 selectionDecorator
.addListener(this);
1646 * Gets selection decorator.
1648 * @return the label decorator for selected items in the list
1650 public ILabelDecorator
getSelectionDecorator() {
1651 return selectionDecorator
;
1655 * Sets new label provider.
1657 * @param newProvider
1658 * new label provider for items in the list, not
1661 public void setProvider(ILabelProvider newProvider
) {
1662 Assert
.isNotNull(newProvider
);
1663 provider
.removeListener(this);
1666 provider
= newProvider
;
1668 if (provider
!= null) {
1669 provider
.addListener(this);
1672 setOwnerDrawEnabled(showColoredLabels() && provider
instanceof IStyledLabelProvider
);
1675 private Image
getImage(Object element
) {
1676 if (element
instanceof ItemsListSeparator
) {
1677 return WorkbenchImages
1678 .getImage(IWorkbenchGraphicConstants
.IMG_OBJ_SEPARATOR
);
1681 return provider
.getImage(element
);
1684 private boolean isSelected(Object element
) {
1685 if (element
!= null && currentSelection
!= null) {
1686 for (int i
= 0; i
< currentSelection
.length
; i
++) {
1687 if (element
.equals(currentSelection
[i
])) {
1698 * @see org.eclipse.jface.viewers.ILabelProvider#getText(java.lang.Object)
1700 private String
getText(Object element
) {
1701 if (element
instanceof ItemsListSeparator
) {
1702 return getSeparatorLabel(((ItemsListSeparator
) element
)
1706 String str
= provider
.getText(element
);
1707 if (selectionDecorator
!= null && isSelected(element
)) {
1708 return selectionDecorator
.decorateText(str
.toString(), element
);
1714 private StyledString
getStyledText(Object element
,
1715 IStyledLabelProvider provider
) {
1716 StyledString string
= provider
.getStyledText(element
);
1718 if (selectionDecorator
!= null && isSelected(element
)) {
1719 String decorated
= selectionDecorator
.decorateText(string
1720 .getString(), element
);
1721 return StyledCellLabelProvider
.styleDecoratedString(decorated
, null, string
);
1722 // no need to add colors when element is selected
1728 public void update(ViewerCell cell
) {
1729 Object element
= cell
.getElement();
1731 if (!(element
instanceof ItemsListSeparator
)
1732 && provider
instanceof IStyledLabelProvider
) {
1733 IStyledLabelProvider styledLabelProvider
= (IStyledLabelProvider
) provider
;
1734 StyledString styledString
= getStyledText(element
,
1735 styledLabelProvider
);
1737 cell
.setText(styledString
.getString());
1738 cell
.setStyleRanges(styledString
.getStyleRanges());
1739 cell
.setImage(styledLabelProvider
.getImage(element
));
1741 cell
.setText(getText(element
));
1742 cell
.setImage(getImage(element
));
1744 cell
.setFont(getFont(element
));
1745 cell
.setForeground(getForeground(element
));
1746 cell
.setBackground(getBackground(element
));
1751 private String
getSeparatorLabel(String separatorLabel
) {
1752 Rectangle rect
= list
.getTable().getBounds();
1754 int borderWidth
= list
.getTable().computeTrim(0, 0, 0, 0).width
;
1756 int imageWidth
= WorkbenchImages
.getImage(
1757 IWorkbenchGraphicConstants
.IMG_OBJ_SEPARATOR
).getBounds().width
;
1759 int width
= rect
.width
- borderWidth
- imageWidth
;
1761 GC gc
= new GC(list
.getTable());
1762 gc
.setFont(list
.getTable().getFont());
1764 int fSeparatorWidth
= gc
.getAdvanceWidth('-');
1765 int fMessageLength
= gc
.textExtent(separatorLabel
).x
;
1769 StringBuffer dashes
= new StringBuffer();
1770 int chars
= (((width
- fMessageLength
) / fSeparatorWidth
) / 2) - 2;
1771 for (int i
= 0; i
< chars
; i
++) {
1775 StringBuffer result
= new StringBuffer();
1776 result
.append(dashes
);
1777 result
.append(" " + separatorLabel
+ " "); //$NON-NLS-1$//$NON-NLS-2$
1778 result
.append(dashes
);
1779 return result
.toString().trim();
1785 * @see org.eclipse.jface.viewers.IBaseLabelProvider#addListener(org.eclipse.jface.viewers.ILabelProviderListener)
1788 public void addListener(ILabelProviderListener listener
) {
1789 listeners
.add(listener
);
1795 * @see org.eclipse.jface.viewers.IBaseLabelProvider#dispose()
1798 public void dispose() {
1799 provider
.removeListener(this);
1802 if (selectionDecorator
!= null) {
1803 selectionDecorator
.removeListener(this);
1804 selectionDecorator
.dispose();
1813 * @see org.eclipse.jface.viewers.IBaseLabelProvider#isLabelProperty(java.lang.Object,
1817 public boolean isLabelProperty(Object element
, String property
) {
1818 if (provider
.isLabelProperty(element
, property
)) {
1821 if (selectionDecorator
!= null
1822 && selectionDecorator
.isLabelProperty(element
, property
)) {
1831 * @see org.eclipse.jface.viewers.IBaseLabelProvider#removeListener(org.eclipse.jface.viewers.ILabelProviderListener)
1834 public void removeListener(ILabelProviderListener listener
) {
1835 listeners
.remove(listener
);
1838 private Color
getBackground(Object element
) {
1839 if (element
instanceof ItemsListSeparator
) {
1842 if (provider
instanceof IColorProvider
) {
1843 return ((IColorProvider
) provider
).getBackground(element
);
1848 private Color
getForeground(Object element
) {
1849 if (element
instanceof ItemsListSeparator
) {
1850 return Display
.getCurrent().getSystemColor(
1851 SWT
.COLOR_WIDGET_NORMAL_SHADOW
);
1853 if (provider
instanceof IColorProvider
) {
1854 return ((IColorProvider
) provider
).getForeground(element
);
1859 private Font
getFont(Object element
) {
1860 if (element
instanceof ItemsListSeparator
) {
1863 if (provider
instanceof IFontProvider
) {
1864 return ((IFontProvider
) provider
).getFont(element
);
1872 * @see org.eclipse.jface.viewers.ILabelProviderListener#labelProviderChanged(org.eclipse.jface.viewers.LabelProviderChangedEvent)
1875 public void labelProviderChanged(LabelProviderChangedEvent event
) {
1876 Object
[] l
= listeners
.getListeners();
1877 for (int i
= 0; i
< listeners
.size(); i
++) {
1878 ((ILabelProviderListener
) l
[i
]).labelProviderChanged(event
);
1884 * Used in ItemsListContentProvider, separates history and non-history
1887 private class ItemsListSeparator
{
1889 private final String name
;
1892 * Creates a new instance of the class.
1895 * the name of the separator
1897 public ItemsListSeparator(String name
) {
1902 * Returns the name of this separator.
1904 * @return the name of the separator
1906 public String
getName() {
1912 * GranualProgressMonitor is used for monitoring progress of filtering
1913 * process. It is used by <code>RefreshProgressMessageJob</code> to
1914 * refresh progress message. State of this monitor illustrates state of
1915 * filtering or cache refreshing process.
1918 private class GranualProgressMonitor
extends ProgressMonitorWrapper
{
1920 private String name
;
1922 private String subName
;
1924 private int totalWork
;
1926 private double worked
;
1928 private boolean done
;
1931 * Creates instance of <code>GranualProgressMonitor</code>.
1934 * progress to be wrapped
1936 public GranualProgressMonitor(IProgressMonitor monitor
) {
1941 * Checks if filtering has been done
1943 * @return true if filtering work has been done false in other way
1945 public boolean isDone() {
1952 * @see org.eclipse.core.runtime.ProgressMonitorWrapper#setTaskName(java.lang.String)
1955 public void setTaskName(String name
) {
1956 super.setTaskName(name
);
1958 this.subName
= null;
1964 * @see org.eclipse.core.runtime.ProgressMonitorWrapper#subTask(java.lang.String)
1967 public void subTask(String name
) {
1968 super.subTask(name
);
1969 this.subName
= name
;
1975 * @see org.eclipse.core.runtime.ProgressMonitorWrapper#beginTask(java.lang.String,
1979 public void beginTask(String name
, int totalWork
) {
1980 super.beginTask(name
, totalWork
);
1981 if (this.name
== null) {
1984 this.totalWork
= totalWork
;
1985 refreshProgressMessageJob
.scheduleProgressRefresh(this);
1991 * @see org.eclipse.core.runtime.ProgressMonitorWrapper#worked(int)
1994 public void worked(int work
) {
1996 internalWorked(work
);
2002 * @see org.eclipse.core.runtime.ProgressMonitorWrapper#done()
2005 public void done() {
2013 * @see org.eclipse.core.runtime.ProgressMonitorWrapper#setCanceled(boolean)
2016 public void setCanceled(boolean b
) {
2018 super.setCanceled(b
);
2024 * @see org.eclipse.core.runtime.ProgressMonitorWrapper#internalWorked(double)
2027 public void internalWorked(double work
) {
2028 worked
= worked
+ work
;
2031 private String
getMessage() {
2033 return ""; //$NON-NLS-1$
2039 message
= subName
== null ?
"" : subName
; //$NON-NLS-1$
2041 message
= subName
== null ? name
2044 WorkbenchMessages
.FilteredItemsSelectionDialog_subtaskProgressMessage
,
2045 new Object
[] { name
, subName
});
2047 if (totalWork
== 0) {
2053 WorkbenchMessages
.FilteredItemsSelectionDialog_taskProgressMessage
,
2057 (int) ((worked
* 100) / totalWork
)) });
2064 * Filters items history and schedule filter job.
2066 private class FilterHistoryJob
extends Job
{
2069 * Filter used during the filtering process.
2071 private ItemsFilter itemsFilter
;
2074 * Creates new instance of receiver.
2076 public FilterHistoryJob() {
2077 super(WorkbenchMessages
.FilteredItemsSelectionDialog_jobLabel
);
2084 * @see org.eclipse.core.runtime.jobs.Job#run(org.eclipse.core.runtime.IProgressMonitor)
2087 protected IStatus
run(IProgressMonitor monitor
) {
2089 this.itemsFilter
= filter
;
2091 contentProvider
.reset();
2093 refreshWithLastSelection
= false;
2095 contentProvider
.addHistoryItems(itemsFilter
);
2097 if (!(lastCompletedFilter
!= null && lastCompletedFilter
2098 .isSubFilter(this.itemsFilter
))) {
2099 contentProvider
.refresh();
2102 filterJob
.schedule();
2104 return Status
.OK_STATUS
;
2110 * Filters items in indicated set and history. During filtering, it
2111 * refreshes the dialog (progress monitor and elements list).
2113 * Depending on the filter, <code>FilterJob</code> decides which kind of
2114 * search will be run inside <code>filterContent</code>. If the last
2115 * filtering is done (last completed filter), is not null, and the new
2116 * filter is a sub-filter ({@link FilteredItemsSelectionDialog.ItemsFilter#isSubFilter(FilteredItemsSelectionDialog.ItemsFilter)})
2117 * of the last, then <code>FilterJob</code> only filters in the cache. If
2118 * it is the first filtering or the new filter isn't a sub-filter of the
2119 * last one, a full search is run.
2121 private class FilterJob
extends Job
{
2124 * Filter used during the filtering process.
2126 protected ItemsFilter itemsFilter
;
2129 * Creates new instance of FilterJob
2131 public FilterJob() {
2132 super(WorkbenchMessages
.FilteredItemsSelectionDialog_jobLabel
);
2139 * @see org.eclipse.core.runtime.jobs.Job#run(org.eclipse.core.runtime.IProgressMonitor)
2142 protected final IStatus
run(IProgressMonitor parent
) {
2143 GranualProgressMonitor monitor
= new GranualProgressMonitor(parent
);
2144 return doRun(monitor
);
2148 * Executes job using the given filtering progress monitor. A hook for
2153 * @return result of the execution
2155 protected IStatus
doRun(GranualProgressMonitor monitor
) {
2157 internalRun(monitor
);
2158 } catch (CoreException e
) {
2162 PlatformUI
.PLUGIN_ID
,
2164 WorkbenchMessages
.FilteredItemsSelectionDialog_jobError
,
2167 return Status
.OK_STATUS
;
2171 * Main method for the job.
2174 * @throws CoreException
2176 private void internalRun(GranualProgressMonitor monitor
)
2177 throws CoreException
{
2179 if (monitor
.isCanceled()) {
2183 this.itemsFilter
= filter
;
2185 if (filter
.getPattern().length() != 0) {
2186 filterContent(monitor
);
2189 if (monitor
.isCanceled()) {
2193 contentProvider
.refresh();
2203 * for monitoring progress
2204 * @throws CoreException
2206 protected void filterContent(GranualProgressMonitor monitor
)
2207 throws CoreException
{
2209 // if (lastCompletedFilter != null
2210 // && lastCompletedFilter.isSubFilter(this.itemsFilter)) {
2212 // int length = lastCompletedResult.size() / 500;
2215 // WorkbenchMessages.FilteredItemsSelectionDialog_cacheSearchJob_taskName,
2218 // for (int pos = 0; pos < lastCompletedResult.size(); pos++) {
2220 // Object item = lastCompletedResult.get(pos);
2221 // if (monitor.isCanceled()) {
2224 // contentProvider.add(item, itemsFilter);
2226 // if ((pos % 500) == 0) {
2227 // monitor.worked(1);
2233 lastCompletedFilter
= null;
2234 lastCompletedResult
= null;
2236 SubProgressMonitor subMonitor
= null;
2237 if (monitor
!= null) {
2240 WorkbenchMessages
.FilteredItemsSelectionDialog_searchJob_taskName
,
2242 subMonitor
= new SubProgressMonitor(monitor
, 95);
2246 fillContentProvider(contentProvider
, itemsFilter
, subMonitor
);
2248 if (monitor
!= null && !monitor
.isCanceled()) {
2250 contentProvider
.rememberResult(itemsFilter
);
2260 * History stores a list of key, object pairs. The list is bounded at a
2261 * certain size. If the list exceeds this size the oldest element is removed
2262 * from the list. An element can be added/renewed with a call to
2263 * <code>accessed(Object)</code>.
2265 * The history can be stored to/loaded from an XML file.
2267 protected static abstract class SelectionHistory
{
2269 private static final String DEFAULT_ROOT_NODE_NAME
= "historyRootNode"; //$NON-NLS-1$
2271 private static final String DEFAULT_INFO_NODE_NAME
= "infoNode"; //$NON-NLS-1$
2273 private static final int MAX_HISTORY_SIZE
= 60;
2275 private final Set historyList
;
2277 private final String rootNodeName
;
2279 private final String infoNodeName
;
2281 private SelectionHistory(String rootNodeName
, String infoNodeName
) {
2283 historyList
= Collections
.synchronizedSet(new LinkedHashSet() {
2285 private static final long serialVersionUID
= 0L;
2290 * @see java.util.LinkedList#add(java.lang.Object)
2293 public boolean add(Object arg0
) {
2294 if (this.size() >= MAX_HISTORY_SIZE
) {
2295 Iterator iterator
= this.iterator();
2299 return super.add(arg0
);
2304 this.rootNodeName
= rootNodeName
;
2305 this.infoNodeName
= infoNodeName
;
2309 * Creates new instance of <code>SelectionHistory</code>.
2311 public SelectionHistory() {
2312 this(DEFAULT_ROOT_NODE_NAME
, DEFAULT_INFO_NODE_NAME
);
2316 * Adds object to history.
2319 * the item to be added to the history
2321 public synchronized void accessed(Object object
) {
2322 historyList
.remove(object
);
2323 historyList
.add(object
);
2327 * Returns <code>true</code> if history contains object.
2330 * the item for which check will be executed
2331 * @return <code>true</code> if history contains object
2332 * <code>false</code> in other way
2334 public synchronized boolean contains(Object object
) {
2335 return historyList
.contains(object
);
2339 * Returns <code>true</code> if history is empty.
2341 * @return <code>true</code> if history is empty
2343 public synchronized boolean isEmpty() {
2344 return historyList
.isEmpty();
2348 * Remove element from history.
2351 * to remove form the history
2352 * @return <code>true</code> if this list contained the specified
2355 public synchronized boolean remove(Object element
) {
2356 return historyList
.remove(element
);
2360 * Load history elements from memento.
2363 * memento from which the history will be retrieved
2365 public void load(IMemento memento
) {
2367 XMLMemento historyMemento
= (XMLMemento
) memento
2368 .getChild(rootNodeName
);
2370 if (historyMemento
== null) {
2374 IMemento
[] mementoElements
= historyMemento
2375 .getChildren(infoNodeName
);
2376 for (int i
= 0; i
< mementoElements
.length
; ++i
) {
2377 IMemento mementoElement
= mementoElements
[i
];
2378 Object object
= restoreItemFromMemento(mementoElement
);
2379 if (object
!= null) {
2380 historyList
.add(object
);
2386 * Save history elements to memento.
2389 * memento to which the history will be added
2391 public void save(IMemento memento
) {
2393 IMemento historyMemento
= memento
.createChild(rootNodeName
);
2395 Object
[] items
= getHistoryItems();
2396 for (int i
= 0; i
< items
.length
; i
++) {
2397 Object item
= items
[i
];
2398 IMemento elementMemento
= historyMemento
2399 .createChild(infoNodeName
);
2400 storeItemToMemento(item
, elementMemento
);
2406 * Gets array of history items.
2408 * @return array of history elements
2410 public synchronized Object
[] getHistoryItems() {
2411 return historyList
.toArray();
2415 * Creates an object using given memento.
2418 * memento used for creating new object
2420 * @return the restored object
2422 protected abstract Object
restoreItemFromMemento(IMemento memento
);
2425 * Store object in <code>IMemento</code>.
2430 * the memento to store to
2432 protected abstract void storeItemToMemento(Object item
, IMemento memento
);
2437 * Filters elements using SearchPattern by comparing the names of items with
2438 * the filter pattern.
2440 protected abstract class ItemsFilter
{
2442 protected SearchPattern patternMatcher
;
2445 * Creates new instance of ItemsFilter.
2447 public ItemsFilter() {
2448 this(new SearchPattern());
2452 * Creates new instance of ItemsFilter.
2454 * @param searchPattern
2455 * the pattern to be used when filtering
2457 public ItemsFilter(SearchPattern searchPattern
) {
2458 patternMatcher
= searchPattern
;
2459 String stringPattern
= ""; //$NON-NLS-1$
2460 if (pattern
!= null && !pattern
.getText().equals("*")) { //$NON-NLS-1$
2461 stringPattern
= pattern
.getText();
2463 patternMatcher
.setPattern(stringPattern
);
2467 * Check if the given filter is a sub-filter of this filter. The default
2468 * implementation checks if the <code>SearchPattern</code> from the
2469 * given filter is a sub-pattern of the one from this filter.
2471 * <i>WARNING: This method is <b>not</b> defined in reading order, i.e.
2472 * <code>a.isSubFilter(b)</code> is <code>true</code> iff
2473 * <code>b</code> is a sub-filter of <code>a</code>, and not
2478 * the filter to be checked, or <code>null</code>
2479 * @return <code>true</code> if the given filter is sub-filter of this
2480 * filter, <code>false</code> if the given filter isn't a
2481 * sub-filter or is <code>null</code>
2483 * @see org.eclipse.ui.dialogs.SearchPattern#isSubPattern(org.eclipse.ui.dialogs.SearchPattern)
2485 public boolean isSubFilter(ItemsFilter filter
) {
2486 if (filter
!= null) {
2487 return this.patternMatcher
.isSubPattern(filter
.patternMatcher
);
2493 * Checks whether the provided filter is equal to the current filter.
2494 * The default implementation checks if <code>SearchPattern</code>
2495 * from current filter is equal to the one from provided filter.
2498 * filter to be checked, or <code>null</code>
2499 * @return <code>true</code> if the given filter is equal to current
2500 * filter, <code>false</code> if given filter isn't equal to
2501 * current one or if it is <code>null</code>
2503 * @see org.eclipse.ui.dialogs.SearchPattern#equalsPattern(org.eclipse.ui.dialogs.SearchPattern)
2505 public boolean equalsFilter(ItemsFilter filter
) {
2507 && filter
.patternMatcher
.equalsPattern(this.patternMatcher
)) {
2514 * Checks whether the pattern's match rule is camel case.
2516 * @return <code>true</code> if pattern's match rule is camel case,
2517 * <code>false</code> otherwise
2519 public boolean isCamelCasePattern() {
2520 return patternMatcher
.getMatchRule() == SearchPattern
.RULE_CAMELCASE_MATCH
;
2524 * Returns the pattern string.
2526 * @return pattern for this filter
2528 * @see SearchPattern#getPattern()
2530 public String
getPattern() {
2531 return patternMatcher
.getPattern();
2535 * Returns the rule to apply for matching keys.
2537 * @return an implementation-specific match rule
2539 * @see SearchPattern#getMatchRule() for match rules returned by the
2540 * default implementation
2542 public int getMatchRule() {
2543 return patternMatcher
.getMatchRule();
2547 * Matches text with filter.
2550 * the text to match with the filter
2551 * @return <code>true</code> if text matches with filter pattern,
2552 * <code>false</code> otherwise
2554 protected boolean matches(String text
) {
2555 return patternMatcher
.matches(text
);
2559 * General method for matching raw name pattern. Checks whether current
2560 * pattern is prefix of name provided item.
2564 * @return <code>true</code> if current pattern is a prefix of name
2565 * provided item, <code>false</code> if item's name is shorter
2566 * than prefix or sequences of characters don't match.
2568 public boolean matchesRawNamePattern(Object item
) {
2569 String prefix
= patternMatcher
.getPattern();
2570 String text
= getElementName(item
);
2576 int textLength
= text
.length();
2577 int prefixLength
= prefix
.length();
2578 if (textLength
< prefixLength
) {
2581 for (int i
= prefixLength
- 1; i
>= 0; i
--) {
2582 if (Character
.toLowerCase(prefix
.charAt(i
)) != Character
2583 .toLowerCase(text
.charAt(i
))) {
2591 * Matches an item against filter conditions.
2594 * @return <code>true<code> if item matches against filter conditions, <code>false</code>
2597 public abstract boolean matchItem(Object item
);
2600 * Checks consistency of an item. Item is inconsistent if was changed or
2604 * @return <code>true</code> if item is consistent, <code>false</code>
2605 * if item is inconsistent
2607 public abstract boolean isConsistentItem(Object item
);
2612 * An interface to content providers for
2613 * <code>FilterItemsSelectionDialog</code>.
2615 protected abstract class AbstractContentProvider
{
2617 * Adds the item to the content provider iff the filter matches the
2618 * item. Otherwise does nothing.
2622 * @param itemsFilter
2625 * @see FilteredItemsSelectionDialog.ItemsFilter#matchItem(Object)
2627 public abstract void add(Object item
, ItemsFilter itemsFilter
);
2631 * Collects filtered elements. Contains one synchronized, sorted set for
2632 * collecting filtered elements. All collected elements are sorted using
2633 * comparator. Comparator is returned by getElementComparator() method.
2634 * Implementation of <code>ItemsFilter</code> is used to filter elements.
2635 * The key function of filter used in to filtering is
2636 * <code>matchElement(Object item)</code>.
2638 * The <code>ContentProvider</code> class also provides item filtering
2639 * methods. The filtering has been moved from the standard TableView
2640 * <code>getFilteredItems()</code> method to content provider, because
2641 * <code>ILazyContentProvider</code> and virtual tables are used. This
2642 * class is responsible for adding a separator below history items and
2643 * marking each items as duplicate if its name repeats more than once on the
2646 private class ContentProvider
extends AbstractContentProvider
implements
2647 IStructuredContentProvider
, ILazyContentProvider
{
2649 private SelectionHistory selectionHistory
;
2652 * Raw result of the searching (unsorted, unfiltered).
2654 * Standard object flow:
2655 * <code>items -> lastSortedItems -> lastFilteredItems</code>
2657 private final Set items
;
2660 * Items that are duplicates.
2662 private final Set duplicates
;
2665 * List of <code>ViewerFilter</code>s to be used during filtering
2667 private List filters
;
2670 * Result of the last filtering.
2672 * Standard object flow:
2673 * <code>items -> lastSortedItems -> lastFilteredItems</code>
2675 private List lastFilteredItems
;
2678 * Result of the last sorting.
2680 * Standard object flow:
2681 * <code>items -> lastSortedItems -> lastFilteredItems</code>
2683 private final List lastSortedItems
;
2686 * Used for <code>getFilteredItems()</code> method canceling (when the
2687 * job that invoked the method was canceled).
2689 * Method canceling could be based (only) on monitor canceling
2690 * unfortunately sometimes the method <code>getFilteredElements()</code>
2691 * could be run with a null monitor, the <code>reset</code> flag have
2692 * to be left intact.
2694 private boolean reset
;
2697 * Creates new instance of <code>ContentProvider</code>.
2699 public ContentProvider() {
2700 this.items
= Collections
.synchronizedSet(new HashSet(2048));
2701 this.duplicates
= Collections
.synchronizedSet(new HashSet(256));
2702 this.lastFilteredItems
= new ArrayList();
2703 this.lastSortedItems
= Collections
.synchronizedList(new ArrayList(
2708 * Sets selection history.
2710 * @param selectionHistory
2711 * The selectionHistory to set.
2713 public void setSelectionHistory(SelectionHistory selectionHistory
) {
2714 this.selectionHistory
= selectionHistory
;
2718 * @return Returns the selectionHistory.
2720 public SelectionHistory
getSelectionHistory() {
2721 return selectionHistory
;
2725 * Removes all content items and resets progress message.
2727 public void reset() {
2730 this.duplicates
.clear();
2731 this.lastSortedItems
.clear();
2735 * Stops reloading cache - <code>getFilteredItems()</code> method.
2737 public void stopReloadingCache() {
2742 * Adds filtered item.
2745 * @param itemsFilter
2748 public void add(Object item
, ItemsFilter itemsFilter
) {
2749 if (itemsFilter
== filter
) {
2750 if (itemsFilter
!= null) {
2751 if (itemsFilter
.matchItem(item
)) {
2752 this.items
.add(item
);
2755 this.items
.add(item
);
2761 * Add all history items to <code>contentProvider</code>.
2763 * @param itemsFilter
2765 public void addHistoryItems(ItemsFilter itemsFilter
) {
2766 if (this.selectionHistory
!= null) {
2767 Object
[] items
= this.selectionHistory
.getHistoryItems();
2768 for (int i
= 0; i
< items
.length
; i
++) {
2769 Object item
= items
[i
];
2770 if (itemsFilter
== filter
) {
2771 if (itemsFilter
!= null) {
2772 if (itemsFilter
.matchItem(item
)) {
2773 if (itemsFilter
.isConsistentItem(item
)) {
2774 this.items
.add(item
);
2776 this.selectionHistory
.remove(item
);
2788 public void refresh() {
2793 * Removes items from history and refreshes the view.
2798 * @return removed item
2800 public Object
removeHistoryElement(Object item
) {
2801 if (this.selectionHistory
!= null) {
2802 this.selectionHistory
.remove(item
);
2804 if (filter
== null || filter
.getPattern().length() == 0) {
2806 duplicates
.remove(item
);
2807 this.lastSortedItems
.remove(item
);
2810 synchronized (lastSortedItems
) {
2811 Collections
.sort(lastSortedItems
, getHistoryComparator());
2817 * Adds item to history and refresh view.
2822 public void addHistoryElement(Object item
) {
2823 if (this.selectionHistory
!= null) {
2824 this.selectionHistory
.accessed(item
);
2826 if (filter
== null || !filter
.matchItem(item
)) {
2827 this.items
.remove(item
);
2828 this.duplicates
.remove(item
);
2829 this.lastSortedItems
.remove(item
);
2831 synchronized (lastSortedItems
) {
2832 Collections
.sort(lastSortedItems
, getHistoryComparator());
2839 * @return <code>true</code> if given item is part of the history
2841 public boolean isHistoryElement(Object item
) {
2842 if (this.selectionHistory
!= null) {
2843 return this.selectionHistory
.contains(item
);
2849 * Sets/unsets given item as duplicate.
2854 * @param isDuplicate
2857 public void setDuplicateElement(Object item
, boolean isDuplicate
) {
2858 if (this.items
.contains(item
)) {
2860 this.duplicates
.add(item
);
2862 this.duplicates
.remove(item
);
2868 * Indicates whether given item is a duplicate.
2872 * @return <code>true</code> if item is duplicate
2874 public boolean isDuplicateElement(Object item
) {
2875 return duplicates
.contains(item
);
2879 * Load history from memento.
2882 * memento from which the history will be retrieved
2884 public void loadHistory(IMemento memento
) {
2885 if (this.selectionHistory
!= null) {
2886 this.selectionHistory
.load(memento
);
2891 * Save history to memento.
2894 * memento to which the history will be added
2896 public void saveHistory(IMemento memento
) {
2897 if (this.selectionHistory
!= null) {
2898 this.selectionHistory
.save(memento
);
2903 * Gets sorted items.
2905 * @return sorted items
2907 private Object
[] getSortedItems() {
2908 if (lastSortedItems
.size() != items
.size()) {
2909 synchronized (lastSortedItems
) {
2910 lastSortedItems
.clear();
2911 lastSortedItems
.addAll(items
);
2912 Collections
.sort(lastSortedItems
, getHistoryComparator());
2915 return lastSortedItems
.toArray();
2919 * Remember result of filtering.
2921 * @param itemsFilter
2923 public void rememberResult(ItemsFilter itemsFilter
) {
2924 List itemsList
= Collections
.synchronizedList(Arrays
2925 .asList(getSortedItems()));
2927 if (itemsFilter
== filter
) {
2928 lastCompletedFilter
= itemsFilter
;
2929 lastCompletedResult
= itemsList
;
2937 * @see org.eclipse.jface.viewers.IStructuredContentProvider#getElements(java.lang.Object)
2940 public Object
[] getElements(Object inputElement
) {
2941 return lastFilteredItems
.toArray();
2944 public int getNumberOfElements() {
2945 return lastFilteredItems
.size();
2951 * @see org.eclipse.jface.viewers.IContentProvider#dispose()
2954 public void dispose() {
2960 * @see org.eclipse.jface.viewers.IContentProvider#inputChanged(org.eclipse.jface.viewers.Viewer,
2961 * java.lang.Object, java.lang.Object)
2964 public void inputChanged(Viewer viewer
, Object oldInput
, Object newInput
) {
2970 * @see org.eclipse.jface.viewers.ILazyContentProvider#updateElement(int)
2973 public void updateElement(int index
) {
2975 CdmFilteredItemsSelectionDialog
.this.list
.replace((lastFilteredItems
2976 .size() > index
) ? lastFilteredItems
.get(index
) : null,
2982 * Main method responsible for getting the filtered items and checking
2983 * for duplicates. It is based on the
2984 * {@link FilteredItemsSelectionDialog.ContentProvider#getFilteredItems(Object, IProgressMonitor)}.
2986 * @param checkDuplicates
2987 * <code>true</code> if data concerning elements
2988 * duplication should be computed - it takes much more time
2989 * than standard filtering
2994 public void reloadCache(boolean checkDuplicates
,
2995 IProgressMonitor monitor
) {
2999 if (monitor
!= null) {
3000 // the work is divided into two actions of the same length
3001 int totalWork
= checkDuplicates ?
200 : 100;
3005 WorkbenchMessages
.FilteredItemsSelectionDialog_cacheRefreshJob
,
3009 // the TableViewer's root (the input) is treated as parent
3011 lastFilteredItems
= Arrays
.asList(getFilteredItems(list
.getInput(),
3012 monitor
!= null ?
new SubProgressMonitor(monitor
, 100)
3015 if (reset
|| (monitor
!= null && monitor
.isCanceled())) {
3016 if (monitor
!= null) {
3022 if (checkDuplicates
) {
3023 checkDuplicates(monitor
);
3025 if (monitor
!= null) {
3030 private void checkDuplicates(IProgressMonitor monitor
) {
3031 synchronized (lastFilteredItems
) {
3032 IProgressMonitor subMonitor
= null;
3033 int reportEvery
= lastFilteredItems
.size() / 20;
3034 if (monitor
!= null) {
3035 subMonitor
= new SubProgressMonitor(monitor
, 100);
3038 WorkbenchMessages
.FilteredItemsSelectionDialog_cacheRefreshJob_checkDuplicates
,
3041 HashMap helperMap
= new HashMap();
3042 for (int i
= 0; i
< lastFilteredItems
.size(); i
++) {
3044 || (subMonitor
!= null && subMonitor
.isCanceled())) {
3047 Object item
= lastFilteredItems
.get(i
);
3049 if (!(item
instanceof ItemsListSeparator
)) {
3050 Object previousItem
= helperMap
.put(
3051 getElementName(item
), item
);
3052 if (previousItem
!= null) {
3053 setDuplicateElement(previousItem
, true);
3054 setDuplicateElement(item
, true);
3056 setDuplicateElement(item
, false);
3060 if (subMonitor
!= null && reportEvery
!= 0
3061 && (i
+ 1) % reportEvery
== 0) {
3062 subMonitor
.worked(1);
3070 * Returns an array of items filtered using the provided
3071 * <code>ViewerFilter</code>s with a separator added.
3076 * progress monitor, can be <code>null</code>
3077 * @return an array of filtered items
3079 protected Object
[] getFilteredItems(Object parent
,
3080 IProgressMonitor monitor
) {
3082 if (monitor
== null) {
3083 monitor
= new NullProgressMonitor();
3088 WorkbenchMessages
.FilteredItemsSelectionDialog_cacheRefreshJob_getFilteredElements
,
3090 if (filters
!= null) {
3091 ticks
/= (filters
.size() + 2);
3096 // get already sorted array
3097 Object
[] filteredElements
= getSortedItems();
3099 monitor
.worked(ticks
);
3101 // filter the elements using provided ViewerFilters
3102 if (filters
!= null && filteredElements
!= null) {
3103 for (Iterator iter
= filters
.iterator(); iter
.hasNext();) {
3104 ViewerFilter f
= (ViewerFilter
) iter
.next();
3105 filteredElements
= f
.filter(list
, parent
, filteredElements
);
3106 monitor
.worked(ticks
);
3110 if (filteredElements
== null || monitor
.isCanceled()) {
3112 return new Object
[0];
3115 ArrayList preparedElements
= new ArrayList();
3116 boolean hasHistory
= false;
3118 if (filteredElements
.length
> 0) {
3119 if (isHistoryElement(filteredElements
[0])) {
3124 int reportEvery
= filteredElements
.length
/ ticks
;
3127 for (int i
= 0; i
< filteredElements
.length
; i
++) {
3128 Object item
= filteredElements
[i
];
3130 if (hasHistory
&& !isHistoryElement(item
)) {
3131 preparedElements
.add(itemsListSeparator
);
3135 preparedElements
.add(item
);
3137 if (reportEvery
!= 0 && ((i
+ 1) % reportEvery
== 0)) {
3144 return preparedElements
.toArray();
3148 * Adds a filter to this content provider. For an example usage of such
3149 * filters look at the project <code>org.eclipse.ui.ide</code>, class
3150 * <code>org.eclipse.ui.dialogs.FilteredResourcesSelectionDialog.CustomWorkingSetFilter</code>.
3154 * the filter to be added
3156 public void addFilter(ViewerFilter filter
) {
3157 if (filters
== null) {
3158 filters
= new ArrayList();
3160 filters
.add(filter
);
3161 // currently filters are only added when dialog is restored
3162 // if it is changed, refreshing the whole TableViewer should be
3169 * A content provider that does nothing.
3171 private class NullContentProvider
implements IContentProvider
{
3176 * @see org.eclipse.jface.viewers.IContentProvider#dispose()
3179 public void dispose() {
3185 * @see org.eclipse.jface.viewers.IContentProvider#inputChanged(org.eclipse.jface.viewers.Viewer,
3186 * java.lang.Object, java.lang.Object)
3189 public void inputChanged(Viewer viewer
, Object oldInput
, Object newInput
) {
3195 * DetailsContentViewer objects are wrappers for labels.
3196 * DetailsContentViewer provides means to change label's image and text when
3197 * the attached LabelProvider is updated.
3199 private class DetailsContentViewer
extends ContentViewer
{
3201 private final CLabel label
;
3204 * Unfortunately, it was impossible to delegate displaying border to
3205 * label. The <code>ViewForm</code> is used because
3206 * <code>CLabel</code> displays shadow when border is present.
3208 private final ViewForm viewForm
;
3211 * Constructs a new instance of this class given its parent and a style
3212 * value describing its behavior and appearance.
3215 * the parent component
3219 public DetailsContentViewer(Composite parent
, int style
) {
3220 viewForm
= new ViewForm(parent
, style
);
3221 GridData gd
= new GridData(GridData
.FILL_HORIZONTAL
);
3222 gd
.horizontalSpan
= 2;
3223 viewForm
.setLayoutData(gd
);
3224 label
= new CLabel(viewForm
, SWT
.FLAT
);
3225 label
.setFont(parent
.getFont());
3226 viewForm
.setContent(label
);
3231 * Shows/hides the content viewer.
3234 * if the content viewer should be visible.
3236 public void setVisible(boolean visible
) {
3237 GridData gd
= (GridData
) viewForm
.getLayoutData();
3238 gd
.exclude
= !visible
;
3239 viewForm
.getParent().layout();
3245 * @see org.eclipse.jface.viewers.Viewer#inputChanged(java.lang.Object,
3249 protected void inputChanged(Object input
, Object oldInput
) {
3250 if (oldInput
== null) {
3251 if (input
== null) {
3265 * @see org.eclipse.jface.viewers.ContentViewer#handleLabelProviderChanged(org.eclipse.jface.viewers.LabelProviderChangedEvent)
3268 protected void handleLabelProviderChanged(
3269 LabelProviderChangedEvent event
) {
3270 if (event
!= null) {
3271 refresh(event
.getElements());
3278 * @see org.eclipse.jface.viewers.Viewer#getControl()
3281 public Control
getControl() {
3288 * @see org.eclipse.jface.viewers.Viewer#getSelection()
3291 public ISelection
getSelection() {
3299 * @see org.eclipse.jface.viewers.Viewer#refresh()
3302 public void refresh() {
3303 Object input
= this.getInput();
3304 if (input
!= null) {
3305 ILabelProvider labelProvider
= (ILabelProvider
) getLabelProvider();
3306 doRefresh(labelProvider
.getText(input
), labelProvider
3309 doRefresh(null, null);
3314 * Sets the given text and image to the label.
3317 * the new text or null
3321 private void doRefresh(String text
, Image image
) {
3322 if ( text
!= null ) {
3323 text
= LegacyActionTools
.escapeMnemonics(text
);
3325 label
.setText(text
);
3326 label
.setImage(image
);
3332 * @see org.eclipse.jface.viewers.Viewer#setSelection(org.eclipse.jface.viewers.ISelection,
3336 public void setSelection(ISelection selection
, boolean reveal
) {
3341 * Refreshes the label if currently chosen element is on the list.
3344 * list of changed object
3346 private void refresh(Object
[] objs
) {
3347 if (objs
== null || getInput() == null) {
3350 Object input
= getInput();
3351 for (int i
= 0; i
< objs
.length
; i
++) {
3352 if (objs
[i
].equals(input
)) {
3361 * Compares items according to the history.
3363 private class HistoryComparator
implements Comparator
{
3368 * @see java.util.Comparator#compare(java.lang.Object, java.lang.Object)
3371 public int compare(Object o1
, Object o2
) {
3372 boolean h1
= isHistoryElement(o1
);
3373 boolean h2
= isHistoryElement(o2
);
3375 return getItemsComparator().compare(o1
, o2
);
3392 * Get the control where the search pattern is entered. Any filtering should
3393 * be done using an {@link ItemsFilter}. This control should only be
3394 * accessed for listeners that wish to handle events that do not affect
3395 * filtering such as custom traversal.
3397 * @return Control or <code>null</code> if the pattern control has not
3400 public Control
getPatternControl() {