2 * Copyright (C) 2016 EDIT
3 * European Distributed Institute of Taxonomy
4 * http://www.e-taxonomy.eu
6 * The contents of this file are subject to the Mozilla Public License Version 1.1
7 * See LICENSE.TXT at the top of this package for the full license terms.
9 package eu
.etaxonomy
.taxeditor
.ui
.dialog
.selection
;
11 import java
.io
.IOException
;
12 import java
.io
.StringReader
;
13 import java
.io
.StringWriter
;
14 import java
.util
.ArrayList
;
15 import java
.util
.Arrays
;
16 import java
.util
.Collections
;
17 import java
.util
.Comparator
;
18 import java
.util
.HashMap
;
19 import java
.util
.HashSet
;
20 import java
.util
.Iterator
;
21 import java
.util
.LinkedHashSet
;
22 import java
.util
.List
;
25 import org
.eclipse
.core
.commands
.AbstractHandler
;
26 import org
.eclipse
.core
.commands
.ExecutionEvent
;
27 import org
.eclipse
.core
.commands
.IHandler
;
28 import org
.eclipse
.core
.runtime
.Assert
;
29 import org
.eclipse
.core
.runtime
.CoreException
;
30 import org
.eclipse
.core
.runtime
.IProgressMonitor
;
31 import org
.eclipse
.core
.runtime
.IStatus
;
32 import org
.eclipse
.core
.runtime
.ListenerList
;
33 import org
.eclipse
.core
.runtime
.NullProgressMonitor
;
34 import org
.eclipse
.core
.runtime
.ProgressMonitorWrapper
;
35 import org
.eclipse
.core
.runtime
.Status
;
36 import org
.eclipse
.core
.runtime
.SubProgressMonitor
;
37 import org
.eclipse
.core
.runtime
.jobs
.Job
;
38 import org
.eclipse
.jface
.action
.Action
;
39 import org
.eclipse
.jface
.action
.ActionContributionItem
;
40 import org
.eclipse
.jface
.action
.IAction
;
41 import org
.eclipse
.jface
.action
.IMenuListener
;
42 import org
.eclipse
.jface
.action
.IMenuManager
;
43 import org
.eclipse
.jface
.action
.LegacyActionTools
;
44 import org
.eclipse
.jface
.action
.MenuManager
;
45 import org
.eclipse
.jface
.dialogs
.IDialogSettings
;
46 import org
.eclipse
.jface
.viewers
.ContentViewer
;
47 import org
.eclipse
.jface
.viewers
.DelegatingStyledCellLabelProvider
.IStyledLabelProvider
;
48 import org
.eclipse
.jface
.viewers
.DoubleClickEvent
;
49 import org
.eclipse
.jface
.viewers
.IColorProvider
;
50 import org
.eclipse
.jface
.viewers
.IContentProvider
;
51 import org
.eclipse
.jface
.viewers
.IDoubleClickListener
;
52 import org
.eclipse
.jface
.viewers
.IFontProvider
;
53 import org
.eclipse
.jface
.viewers
.ILabelDecorator
;
54 import org
.eclipse
.jface
.viewers
.ILabelProvider
;
55 import org
.eclipse
.jface
.viewers
.ILabelProviderListener
;
56 import org
.eclipse
.jface
.viewers
.ILazyContentProvider
;
57 import org
.eclipse
.jface
.viewers
.ISelection
;
58 import org
.eclipse
.jface
.viewers
.ISelectionChangedListener
;
59 import org
.eclipse
.jface
.viewers
.IStructuredContentProvider
;
60 import org
.eclipse
.jface
.viewers
.LabelProvider
;
61 import org
.eclipse
.jface
.viewers
.LabelProviderChangedEvent
;
62 import org
.eclipse
.jface
.viewers
.SelectionChangedEvent
;
63 import org
.eclipse
.jface
.viewers
.StructuredSelection
;
64 import org
.eclipse
.jface
.viewers
.StyledCellLabelProvider
;
65 import org
.eclipse
.jface
.viewers
.StyledString
;
66 import org
.eclipse
.jface
.viewers
.TableViewer
;
67 import org
.eclipse
.jface
.viewers
.Viewer
;
68 import org
.eclipse
.jface
.viewers
.ViewerCell
;
69 import org
.eclipse
.jface
.viewers
.ViewerFilter
;
70 import org
.eclipse
.osgi
.util
.NLS
;
71 import org
.eclipse
.swt
.SWT
;
72 import org
.eclipse
.swt
.accessibility
.ACC
;
73 import org
.eclipse
.swt
.accessibility
.AccessibleAdapter
;
74 import org
.eclipse
.swt
.accessibility
.AccessibleEvent
;
75 import org
.eclipse
.swt
.custom
.CLabel
;
76 import org
.eclipse
.swt
.custom
.ViewForm
;
77 import org
.eclipse
.swt
.events
.KeyAdapter
;
78 import org
.eclipse
.swt
.events
.KeyEvent
;
79 import org
.eclipse
.swt
.events
.ModifyEvent
;
80 import org
.eclipse
.swt
.events
.ModifyListener
;
81 import org
.eclipse
.swt
.events
.MouseAdapter
;
82 import org
.eclipse
.swt
.events
.MouseEvent
;
83 import org
.eclipse
.swt
.events
.SelectionAdapter
;
84 import org
.eclipse
.swt
.events
.SelectionEvent
;
85 import org
.eclipse
.swt
.events
.TraverseEvent
;
86 import org
.eclipse
.swt
.events
.TraverseListener
;
87 import org
.eclipse
.swt
.graphics
.Color
;
88 import org
.eclipse
.swt
.graphics
.Font
;
89 import org
.eclipse
.swt
.graphics
.GC
;
90 import org
.eclipse
.swt
.graphics
.Image
;
91 import org
.eclipse
.swt
.graphics
.Point
;
92 import org
.eclipse
.swt
.graphics
.Rectangle
;
93 import org
.eclipse
.swt
.layout
.GridData
;
94 import org
.eclipse
.swt
.layout
.GridLayout
;
95 import org
.eclipse
.swt
.widgets
.Composite
;
96 import org
.eclipse
.swt
.widgets
.Control
;
97 import org
.eclipse
.swt
.widgets
.Display
;
98 import org
.eclipse
.swt
.widgets
.Event
;
99 import org
.eclipse
.swt
.widgets
.Label
;
100 import org
.eclipse
.swt
.widgets
.Menu
;
101 import org
.eclipse
.swt
.widgets
.Shell
;
102 import org
.eclipse
.swt
.widgets
.Table
;
103 import org
.eclipse
.swt
.widgets
.Text
;
104 import org
.eclipse
.swt
.widgets
.ToolBar
;
105 import org
.eclipse
.swt
.widgets
.ToolItem
;
106 import org
.eclipse
.ui
.ActiveShellExpression
;
107 import org
.eclipse
.ui
.IMemento
;
108 import org
.eclipse
.ui
.IWorkbenchCommandConstants
;
109 import org
.eclipse
.ui
.IWorkbenchPreferenceConstants
;
110 import org
.eclipse
.ui
.PlatformUI
;
111 import org
.eclipse
.ui
.WorkbenchException
;
112 import org
.eclipse
.ui
.XMLMemento
;
113 import org
.eclipse
.ui
.dialogs
.FilteredItemsSelectionDialog
;
114 import org
.eclipse
.ui
.dialogs
.SearchPattern
;
115 import org
.eclipse
.ui
.dialogs
.SelectionStatusDialog
;
116 import org
.eclipse
.ui
.handlers
.IHandlerActivation
;
117 import org
.eclipse
.ui
.handlers
.IHandlerService
;
118 import org
.eclipse
.ui
.internal
.IWorkbenchGraphicConstants
;
119 import org
.eclipse
.ui
.internal
.WorkbenchImages
;
120 import org
.eclipse
.ui
.internal
.WorkbenchMessages
;
121 import org
.eclipse
.ui
.internal
.WorkbenchPlugin
;
122 import org
.eclipse
.ui
.progress
.UIJob
;
123 import org
.eclipse
.ui
.statushandlers
.StatusManager
;
129 * This is a class copied from FilteredItemsSelectionDialog and adapted to the Cdm use case. The original
130 * dialog gets all items when opening the dialog and then apply the filter on all items.
131 * In our case we need a possibility to filter the items already when getting them from the DB, so we adapt the
133 * the abstract method initMOdel was added and the method applyFilter was modified
134 * original description:
135 * Shows a list of items to the user with a text entry field for a string
136 * pattern used to filter the list of items.
141 public abstract class CdmFilteredItemsSelectionDialog
extends SelectionStatusDialog
{
145 private static final String DIALOG_BOUNDS_SETTINGS
= "DialogBoundsSettings"; //$NON-NLS-1$
147 private static final String SHOW_STATUS_LINE
= "ShowStatusLine"; //$NON-NLS-1$
149 private static final String HISTORY_SETTINGS
= "History"; //$NON-NLS-1$
151 private static final String DIALOG_HEIGHT
= "DIALOG_HEIGHT"; //$NON-NLS-1$
153 private static final String DIALOG_WIDTH
= "DIALOG_WIDTH"; //$NON-NLS-1$
156 * Represents an empty selection in the pattern input field (used only for
159 public static final int NONE
= 0;
162 * Pattern input field selection where caret is at the beginning (used only
163 * for initial pattern).
165 public static final int CARET_BEGINNING
= 1;
168 * Represents a full selection in the pattern input field (used only for
171 public static final int FULL_SELECTION
= 2;
173 private Text pattern
;
175 private TableViewer list
;
177 private DetailsContentViewer details
;
180 * It is a duplicate of a field in the CLabel class in DetailsContentViewer.
181 * It is maintained, because the <code>setDetailsLabelProvider()</code>
182 * could be called before content area is created.
184 private ILabelProvider detailsLabelProvider
;
186 private ItemsListLabelProvider itemsListLabelProvider
;
188 private MenuManager menuManager
;
190 private MenuManager contextMenuManager
;
192 private final boolean multi
;
194 private ToolBar toolBar
;
196 private ToolItem toolItem
;
198 private Label progressLabel
;
200 private ToggleStatusLineAction toggleStatusLineAction
;
202 private RemoveHistoryItemAction removeHistoryItemAction
;
204 private ActionContributionItem removeHistoryActionContributionItem
;
206 private IStatus status
;
208 private final RefreshCacheJob refreshCacheJob
;
210 private final RefreshProgressMessageJob refreshProgressMessageJob
= new RefreshProgressMessageJob();
212 private Object
[] currentSelection
;
214 private final ContentProvider contentProvider
;
216 private final FilterHistoryJob filterHistoryJob
;
218 private final FilterJob filterJob
;
220 private ItemsFilter filter
;
222 private List lastCompletedResult
;
224 private ItemsFilter lastCompletedFilter
;
226 private String initialPatternText
;
228 private int selectionMode
;
230 private ItemsListSeparator itemsListSeparator
;
232 private static final String EMPTY_STRING
= ""; //$NON-NLS-1$
234 private boolean refreshWithLastSelection
= false;
236 private IHandlerActivation showViewHandler
;
239 * Creates a new instance of the class.
242 * shell to parent the dialog on
244 * indicates whether dialog allows to select more than one
245 * position in its list of items
247 public CdmFilteredItemsSelectionDialog(Shell shell
, boolean multi
) {
250 filterHistoryJob
= new FilterHistoryJob();
251 filterJob
= new FilterJob();
252 contentProvider
= new ContentProvider();
253 refreshCacheJob
= new RefreshCacheJob();
254 itemsListSeparator
= new ItemsListSeparator(
255 WorkbenchMessages
.FilteredItemsSelectionDialog_separatorLabel
);
256 selectionMode
= NONE
;
260 * Creates a new instance of the class. Created dialog won't allow to select
261 * more than one item.
264 * shell to parent the dialog on
266 public CdmFilteredItemsSelectionDialog(Shell shell
) {
271 * Adds viewer filter to the dialog items list.
276 protected void addListFilter(ViewerFilter filter
) {
277 contentProvider
.addFilter(filter
);
281 * Sets a new label provider for items in the list. If the label provider
282 * also implements {@link
283 * org.eclipse.jface.viewers.DelegatingStyledCellLabelProvider
284 * .IStyledLabelProvider}, the style text labels provided by it will be used
285 * provided that the corresponding preference is set.
287 * @see IWorkbenchPreferenceConstants#USE_COLORED_LABELS
289 * @param listLabelProvider
290 * the label provider for items in the list
292 public void setListLabelProvider(ILabelProvider listLabelProvider
) {
293 getItemsListLabelProvider().setProvider(listLabelProvider
);
297 * Returns the label decorator for selected items in the list.
299 * @return the label decorator for selected items in the list
301 private ILabelDecorator
getListSelectionLabelDecorator() {
302 return getItemsListLabelProvider().getSelectionDecorator();
306 * Sets the label decorator for selected items in the list.
308 * @param listSelectionLabelDecorator
309 * the label decorator for selected items in the list
311 public void setListSelectionLabelDecorator(
312 ILabelDecorator listSelectionLabelDecorator
) {
313 getItemsListLabelProvider().setSelectionDecorator(
314 listSelectionLabelDecorator
);
318 * Returns the item list label provider.
320 * @return the item list label provider
322 private ItemsListLabelProvider
getItemsListLabelProvider() {
323 if (itemsListLabelProvider
== null) {
324 itemsListLabelProvider
= new ItemsListLabelProvider(
325 new LabelProvider(), null);
327 return itemsListLabelProvider
;
331 * Sets label provider for the details field.
333 * For a single selection, the element sent to
334 * {@link ILabelProvider#getImage(Object)} and
335 * {@link ILabelProvider#getText(Object)} is the selected object, for
336 * multiple selection a {@link String} with amount of selected items is the
339 * @see #getSelectedItems() getSelectedItems() can be used to retrieve
340 * selected items and get the items count.
342 * @param detailsLabelProvider
343 * the label provider for the details field
345 public void setDetailsLabelProvider(ILabelProvider detailsLabelProvider
) {
346 this.detailsLabelProvider
= detailsLabelProvider
;
347 if (details
!= null) {
348 details
.setLabelProvider(detailsLabelProvider
);
352 private ILabelProvider
getDetailsLabelProvider() {
353 if (detailsLabelProvider
== null) {
354 detailsLabelProvider
= new LabelProvider();
356 return detailsLabelProvider
;
362 * @see org.eclipse.jface.window.Window#create()
365 public void create() {
371 * Restores dialog using persisted settings. The default implementation
372 * restores the status of the details line and the selection history.
375 * settings used to restore dialog
377 protected void restoreDialog(IDialogSettings settings
) {
378 boolean toggleStatusLine
= true;
380 if (settings
.get(SHOW_STATUS_LINE
) != null) {
381 toggleStatusLine
= settings
.getBoolean(SHOW_STATUS_LINE
);
384 toggleStatusLineAction
.setChecked(toggleStatusLine
);
386 details
.setVisible(toggleStatusLine
);
388 String setting
= settings
.get(HISTORY_SETTINGS
);
389 if (setting
!= null) {
391 IMemento memento
= XMLMemento
.createReadRoot(new StringReader(
393 this.contentProvider
.loadHistory(memento
);
394 } catch (WorkbenchException e
) {
395 // Simply don't restore the settings
401 PlatformUI
.PLUGIN_ID
,
403 WorkbenchMessages
.FilteredItemsSelectionDialog_restoreError
,
412 * @see org.eclipse.jface.window.Window#close()
415 public boolean close() {
416 this.filterJob
.cancel();
417 this.refreshCacheJob
.cancel();
418 this.refreshProgressMessageJob
.cancel();
419 if (showViewHandler
!= null) {
420 IHandlerService service
= PlatformUI
421 .getWorkbench().getService(IHandlerService
.class);
422 service
.deactivateHandler(showViewHandler
);
423 showViewHandler
.getHandler().dispose();
424 showViewHandler
= null;
426 if (menuManager
!= null) {
427 menuManager
.dispose();
429 if (contextMenuManager
!= null) {
430 contextMenuManager
.dispose();
432 storeDialog(getDialogSettings());
433 return super.close();
437 * Stores dialog settings.
440 * settings used to store dialog
442 protected void storeDialog(IDialogSettings settings
) {
443 settings
.put(SHOW_STATUS_LINE
, toggleStatusLineAction
.isChecked());
445 XMLMemento memento
= XMLMemento
.createWriteRoot(HISTORY_SETTINGS
);
446 this.contentProvider
.saveHistory(memento
);
447 StringWriter writer
= new StringWriter();
449 memento
.save(writer
);
450 settings
.put(HISTORY_SETTINGS
, writer
.getBuffer().toString());
451 } catch (IOException e
) {
452 // Simply don't store the settings
458 PlatformUI
.PLUGIN_ID
,
460 WorkbenchMessages
.FilteredItemsSelectionDialog_storeError
,
466 * Create a new header which is labelled by headerLabel.
469 * @return Label the label of the header
471 private Label
createHeader(Composite parent
) {
472 Composite header
= new Composite(parent
, SWT
.NONE
);
474 GridLayout layout
= new GridLayout();
475 layout
.numColumns
= 2;
476 layout
.marginWidth
= 0;
477 layout
.marginHeight
= 0;
478 header
.setLayout(layout
);
480 Label headerLabel
= new Label(header
, SWT
.NONE
);
481 headerLabel
.setText((getMessage() != null && getMessage().trim()
482 .length() > 0) ?
getMessage()
483 : WorkbenchMessages
.FilteredItemsSelectionDialog_patternLabel
);
484 headerLabel
.addTraverseListener(new TraverseListener() {
486 public void keyTraversed(TraverseEvent e
) {
487 if (e
.detail
== SWT
.TRAVERSE_MNEMONIC
&& e
.doit
) {
488 e
.detail
= SWT
.TRAVERSE_NONE
;
494 GridData gd
= new GridData(GridData
.FILL_HORIZONTAL
);
495 headerLabel
.setLayoutData(gd
);
497 createViewMenu(header
);
498 header
.setLayoutData(gd
);
503 * Create the labels for the list and the progress. Return the list label.
508 private Label
createLabels(Composite parent
) {
509 Composite labels
= new Composite(parent
, SWT
.NONE
);
511 GridLayout layout
= new GridLayout();
512 layout
.numColumns
= 2;
513 layout
.marginWidth
= 0;
514 layout
.marginHeight
= 0;
515 labels
.setLayout(layout
);
517 Label listLabel
= new Label(labels
, SWT
.NONE
);
519 .setText(WorkbenchMessages
.FilteredItemsSelectionDialog_listLabel
);
521 listLabel
.addTraverseListener(new TraverseListener() {
523 public void keyTraversed(TraverseEvent e
) {
524 if (e
.detail
== SWT
.TRAVERSE_MNEMONIC
&& e
.doit
) {
525 e
.detail
= SWT
.TRAVERSE_NONE
;
526 getList().getTable().setFocus();
531 GridData gd
= new GridData(GridData
.FILL_HORIZONTAL
);
532 listLabel
.setLayoutData(gd
);
534 progressLabel
= new Label(labels
, SWT
.RIGHT
);
535 progressLabel
.setLayoutData(gd
);
537 labels
.setLayoutData(gd
);
541 private void createViewMenu(Composite parent
) {
542 toolBar
= new ToolBar(parent
, SWT
.FLAT
);
543 toolItem
= new ToolItem(toolBar
, SWT
.PUSH
, 0);
545 GridData data
= new GridData();
546 data
.horizontalAlignment
= GridData
.END
;
547 toolBar
.setLayoutData(data
);
549 toolBar
.addMouseListener(new MouseAdapter() {
551 public void mouseDown(MouseEvent e
) {
556 toolItem
.setImage(WorkbenchImages
557 .getImage(IWorkbenchGraphicConstants
.IMG_LCL_VIEW_MENU
));
559 .setToolTipText(WorkbenchMessages
.FilteredItemsSelectionDialog_menu
);
560 toolItem
.addSelectionListener(new SelectionAdapter() {
562 public void widgetSelected(SelectionEvent e
) {
567 menuManager
= new MenuManager();
569 fillViewMenu(menuManager
);
571 IHandlerService service
= PlatformUI
.getWorkbench()
572 .getService(IHandlerService
.class);
573 IHandler handler
= new AbstractHandler() {
575 public Object
execute(ExecutionEvent event
) {
580 showViewHandler
= service
.activateHandler(
581 IWorkbenchCommandConstants
.WINDOW_SHOW_VIEW_MENU
, handler
,
582 new ActiveShellExpression(getShell()));
586 * Fills the menu of the dialog.
591 protected void fillViewMenu(IMenuManager menuManager
) {
592 toggleStatusLineAction
= new ToggleStatusLineAction();
593 menuManager
.add(toggleStatusLineAction
);
596 private void showViewMenu() {
597 Menu menu
= menuManager
.createContextMenu(getShell());
598 Rectangle bounds
= toolItem
.getBounds();
599 Point topLeft
= new Point(bounds
.x
, bounds
.y
+ bounds
.height
);
600 topLeft
= toolBar
.toDisplay(topLeft
);
601 menu
.setLocation(topLeft
.x
, topLeft
.y
);
602 menu
.setVisible(true);
606 * Hook that allows to add actions to the context menu.
608 * Subclasses may extend in order to add other actions.</p>
610 * @param menuManager the context menu manager
613 protected void fillContextMenu(IMenuManager menuManager
) {
614 List selectedElements
= ((StructuredSelection
)getList().getSelection()).toList();
618 for (Iterator it
= selectedElements
.iterator(); it
.hasNext();) {
620 if (item
instanceof ItemsListSeparator
|| !isHistoryElement(item
)) {
625 if (selectedElements
.size() > 0) {
626 removeHistoryItemAction
.setText(WorkbenchMessages
.FilteredItemsSelectionDialog_removeItemsFromHistoryAction
);
628 menuManager
.add(removeHistoryActionContributionItem
);
633 private void createPopupMenu() {
634 removeHistoryItemAction
= new RemoveHistoryItemAction();
635 removeHistoryActionContributionItem
= new ActionContributionItem(
636 removeHistoryItemAction
);
638 contextMenuManager
= new MenuManager();
639 contextMenuManager
.setRemoveAllWhenShown(true);
640 contextMenuManager
.addMenuListener(new IMenuListener() {
642 public void menuAboutToShow(IMenuManager manager
) {
643 fillContextMenu(manager
);
647 final Table table
= getList().getTable();
648 Menu menu
= contextMenuManager
.createContextMenu(table
);
653 * Creates an extra content area, which will be located above the details.
656 * parent to create the dialog widgets in
657 * @return an extra content area
659 protected abstract Control
createExtendedContentArea(Composite parent
);
664 * @see org.eclipse.jface.dialogs.Dialog#createDialogArea(org.eclipse.swt.widgets.Composite)
667 protected Control
createDialogArea(Composite parent
) {
668 Composite dialogArea
= (Composite
) super.createDialogArea(parent
);
670 Composite content
= new Composite(dialogArea
, SWT
.NONE
);
671 GridData gd
= new GridData(GridData
.FILL_BOTH
);
672 content
.setLayoutData(gd
);
674 GridLayout layout
= new GridLayout();
675 layout
.numColumns
= 1;
676 layout
.marginWidth
= 0;
677 layout
.marginHeight
= 0;
678 content
.setLayout(layout
);
680 final Label headerLabel
= createHeader(content
);
682 pattern
= new Text(content
, SWT
.SINGLE
| SWT
.BORDER
| SWT
.SEARCH
| SWT
.ICON_CANCEL
);
683 pattern
.getAccessible().addAccessibleListener(new AccessibleAdapter() {
685 public void getName(AccessibleEvent e
) {
686 e
.result
= LegacyActionTools
.removeMnemonics(headerLabel
690 gd
= new GridData(GridData
.FILL_HORIZONTAL
);
691 pattern
.setLayoutData(gd
);
693 final Label listLabel
= createLabels(content
);
695 setList(new TableViewer(content
, (multi ? SWT
.MULTI
: SWT
.SINGLE
)
696 | SWT
.BORDER
| SWT
.V_SCROLL
| SWT
.VIRTUAL
));
697 getList().getTable().getAccessible().addAccessibleListener(
698 new AccessibleAdapter() {
700 public void getName(AccessibleEvent e
) {
701 if (e
.childID
== ACC
.CHILDID_SELF
) {
702 e
.result
= LegacyActionTools
703 .removeMnemonics(listLabel
.getText());
707 getList().setContentProvider(contentProvider
);
708 getList().setLabelProvider(getItemsListLabelProvider());
709 getList().setInput(new Object
[0]);
710 getList().setItemCount(contentProvider
.getNumberOfElements());
711 gd
= new GridData(GridData
.FILL_BOTH
);
712 applyDialogFont(getList().getTable());
713 gd
.heightHint
= getList().getTable().getItemHeight() * 15;
714 getList().getTable().setLayoutData(gd
);
718 pattern
.addModifyListener(new ModifyListener() {
720 public void modifyText(ModifyEvent e
) {
725 pattern
.addKeyListener(new KeyAdapter() {
727 public void keyPressed(KeyEvent e
) {
728 if (e
.keyCode
== SWT
.ARROW_DOWN
) {
729 if (getList().getTable().getItemCount() > 0) {
730 getList().getTable().setFocus();
736 getList().addSelectionChangedListener(new ISelectionChangedListener() {
738 public void selectionChanged(SelectionChangedEvent event
) {
739 StructuredSelection selection
= (StructuredSelection
) event
741 handleSelected(selection
);
745 getList().addDoubleClickListener(new IDoubleClickListener() {
747 public void doubleClick(DoubleClickEvent event
) {
752 getList().getTable().addKeyListener(new KeyAdapter() {
754 public void keyPressed(KeyEvent e
) {
756 if (e
.keyCode
== SWT
.DEL
) {
758 List selectedElements
= ((StructuredSelection
) getList()
759 .getSelection()).toList();
762 boolean isSelectedHistory
= true;
764 for (Iterator it
= selectedElements
.iterator(); it
767 if (item
instanceof ItemsListSeparator
768 || !isHistoryElement(item
)) {
769 isSelectedHistory
= false;
773 if (isSelectedHistory
) {
774 removeSelectedItems(selectedElements
);
779 if (e
.keyCode
== SWT
.ARROW_UP
&& (e
.stateMask
& SWT
.SHIFT
) != 0
780 && (e
.stateMask
& SWT
.CTRL
) != 0) {
781 StructuredSelection selection
= (StructuredSelection
) getList()
784 if (selection
.size() == 1) {
785 Object element
= selection
.getFirstElement();
786 if (element
.equals(getList().getElementAt(0))) {
789 if (getList().getElementAt(getList().getTable()
790 .getSelectionIndex() - 1) instanceof ItemsListSeparator
) {
791 getList().getTable().setSelection(
792 getList().getTable().getSelectionIndex() - 1);
794 getList().getTable().notifyListeners(SWT
.Selection
,
800 if (e
.keyCode
== SWT
.ARROW_DOWN
801 && (e
.stateMask
& SWT
.SHIFT
) != 0
802 && (e
.stateMask
& SWT
.CTRL
) != 0) {
805 .getElementAt(getList().getTable().getSelectionIndex() + 1) instanceof ItemsListSeparator
) {
806 getList().getTable().setSelection(
807 getList().getTable().getSelectionIndex() + 1);
809 getList().getTable().notifyListeners(SWT
.Selection
, new Event());
815 createExtendedContentArea(content
);
817 details
= new DetailsContentViewer(content
, SWT
.BORDER
| SWT
.FLAT
);
818 details
.setVisible(toggleStatusLineAction
.isChecked());
819 details
.setContentProvider(new NullContentProvider());
820 details
.setLabelProvider(getDetailsLabelProvider());
822 applyDialogFont(content
);
824 restoreDialog(getDialogSettings());
826 if (initialPatternText
!= null) {
827 pattern
.setText(initialPatternText
);
830 switch (selectionMode
) {
831 case CARET_BEGINNING
:
832 pattern
.setSelection(0, 0);
835 pattern
.setSelection(0, initialPatternText
.length());
839 // apply filter even if pattern is empty (display history)
846 * This method is a hook for subclasses to override default dialog behavior.
847 * The <code>handleDoubleClick()</code> method handles double clicks on
848 * the list of filtered elements.
850 * Current implementation makes double-clicking on the list do the same as
851 * pressing <code>OK</code> button on the dialog.
853 protected void handleDoubleClick() {
858 * Refreshes the details field according to the current selection in the
861 private void refreshDetails() {
862 StructuredSelection selection
= getSelectedItems();
864 switch (selection
.size()) {
866 details
.setInput(null);
869 details
.setInput(selection
.getFirstElement());
875 WorkbenchMessages
.FilteredItemsSelectionDialog_nItemsSelected
,
876 new Integer(selection
.size())));
883 * Handle selection in the items list by updating labels of selected and
884 * unselected items and refresh the details field using the selection.
889 protected void handleSelected(StructuredSelection selection
) {
890 IStatus status
= new Status(IStatus
.OK
, PlatformUI
.PLUGIN_ID
,
891 IStatus
.OK
, EMPTY_STRING
, null);
893 Object
[] lastSelection
= getCurrentSelection();
895 setCurrentSelection(selection
.toArray());
897 if (selection
.size() == 0) {
898 status
= new Status(IStatus
.ERROR
, PlatformUI
.PLUGIN_ID
,
899 IStatus
.ERROR
, EMPTY_STRING
, null);
901 if (lastSelection
!= null
902 && getListSelectionLabelDecorator() != null) {
903 getList().update(lastSelection
, null);
906 setCurrentSelection(null);
909 status
= new Status(IStatus
.ERROR
, PlatformUI
.PLUGIN_ID
,
910 IStatus
.ERROR
, EMPTY_STRING
, null);
912 List items
= selection
.toList();
915 IStatus tempStatus
= null;
917 for (Iterator it
= items
.iterator(); it
.hasNext();) {
918 Object o
= it
.next();
920 if (o
instanceof ItemsListSeparator
) {
925 tempStatus
= validateItem(item
);
927 if (tempStatus
.isOK()) {
928 status
= new Status(IStatus
.OK
, PlatformUI
.PLUGIN_ID
,
929 IStatus
.OK
, EMPTY_STRING
, null);
932 // if any selected element is not valid status is set to
938 if (lastSelection
!= null
939 && getListSelectionLabelDecorator() != null) {
940 getList().update(lastSelection
, null);
943 if (getListSelectionLabelDecorator() != null) {
944 getList().update(getCurrentSelection(), null);
949 updateStatus(status
);
955 * @see org.eclipse.jface.window.Dialog#getDialogBoundsSettings()
958 protected IDialogSettings
getDialogBoundsSettings() {
959 IDialogSettings settings
= getDialogSettings();
960 IDialogSettings section
= settings
.getSection(DIALOG_BOUNDS_SETTINGS
);
961 if (section
== null) {
962 section
= settings
.addNewSection(DIALOG_BOUNDS_SETTINGS
);
963 section
.put(DIALOG_HEIGHT
, 500);
964 section
.put(DIALOG_WIDTH
, 600);
970 * Returns the dialog settings. Returned object can't be null.
972 * @return return dialog settings for this dialog
974 protected abstract IDialogSettings
getDialogSettings();
977 * Refreshes the dialog - has to be called in UI thread.
979 public void refresh() {
980 if (getList() != null && !getList().getTable().isDisposed()) {
982 List lastRefreshSelection
= ((StructuredSelection
) getList()
983 .getSelection()).toList();
984 getList().getTable().deselectAll();
986 getList().setItemCount(contentProvider
.getNumberOfElements());
989 if (getList().getTable().getItemCount() > 0) {
990 // preserve previous selection
991 if (refreshWithLastSelection
&& lastRefreshSelection
!= null
992 && lastRefreshSelection
.size() > 0) {
993 getList().setSelection(new StructuredSelection(
994 lastRefreshSelection
));
996 refreshWithLastSelection
= true;
997 getList().getTable().setSelection(0);
998 getList().getTable().notifyListeners(SWT
.Selection
, new Event());
1001 getList().setSelection(StructuredSelection
.EMPTY
);
1006 scheduleProgressMessageRefresh();
1010 * Updates the progress label.
1015 public void updateProgressLabel() {
1016 scheduleProgressMessageRefresh();
1020 * Notifies the content provider - fires filtering of content provider
1021 * elements. During the filtering, a separator between history and workspace
1024 * This is a long running operation and should be called in a job.
1026 * @param checkDuplicates
1027 * <code>true</code> if data concerning elements duplication
1028 * should be computed - it takes much more time than the standard
1031 * a progress monitor or <code>null</code> if no monitor is
1034 public void reloadCache(boolean checkDuplicates
, IProgressMonitor monitor
) {
1035 if (getList() != null && !getList().getTable().isDisposed()
1036 && contentProvider
!= null) {
1037 contentProvider
.reloadCache(checkDuplicates
, monitor
);
1042 * Schedule refresh job.
1044 public void scheduleRefresh() {
1045 refreshCacheJob
.cancelAll();
1046 refreshCacheJob
.schedule();
1050 * Schedules progress message refresh.
1052 public void scheduleProgressMessageRefresh() {
1053 if (filterJob
.getState() != Job
.RUNNING
1054 && refreshProgressMessageJob
.getState() != Job
.RUNNING
) {
1055 refreshProgressMessageJob
.scheduleProgressRefresh(null);
1062 * @see org.eclipse.ui.dialogs.SelectionStatusDialog#computeResult()
1065 protected void computeResult() {
1067 List selectedElements
= ((StructuredSelection
) getList().getSelection())
1070 List objectsToReturn
= new ArrayList();
1074 for (Iterator it
= selectedElements
.iterator(); it
.hasNext();) {
1077 if (!(item
instanceof ItemsListSeparator
)) {
1078 accessedHistoryItem(item
);
1079 objectsToReturn
.add(item
);
1083 setResult(objectsToReturn
);
1087 * @see org.eclipse.ui.dialogs.SelectionStatusDialog#updateStatus(org.eclipse.core.runtime.IStatus)
1090 protected void updateStatus(IStatus status
) {
1091 this.status
= status
;
1092 super.updateStatus(status
);
1096 * @see Dialog#okPressed()
1099 protected void okPressed() {
1101 && (status
.isOK() || status
.getCode() == IStatus
.INFO
)) {
1107 * Sets the initial pattern used by the filter. This text is copied into the
1108 * selection input on the dialog. A full selection is used in the pattern
1112 * initial pattern for the filter
1113 * @see FilteredItemsSelectionDialog#FULL_SELECTION
1115 public void setInitialPattern(String text
) {
1116 setInitialPattern(text
, FULL_SELECTION
);
1120 * Sets the initial pattern used by the filter. This text is copied into the
1121 * selection input on the dialog. The <code>selectionMode</code> is used
1122 * to choose selection type for the input field.
1125 * initial pattern for the filter
1126 * @param selectionMode
1127 * one of: {@link FilteredItemsSelectionDialog#NONE},
1128 * {@link FilteredItemsSelectionDialog#CARET_BEGINNING},
1129 * {@link FilteredItemsSelectionDialog#FULL_SELECTION}
1131 public void setInitialPattern(String text
, int selectionMode
) {
1132 this.initialPatternText
= text
;
1133 this.selectionMode
= selectionMode
;
1137 * Gets initial pattern.
1139 * @return initial pattern, or <code>null</code> if initial pattern is not
1142 protected String
getInitialPattern() {
1143 return this.initialPatternText
;
1147 * Returns the current selection.
1149 * @return the current selection
1151 protected StructuredSelection
getSelectedItems() {
1153 StructuredSelection selection
= (StructuredSelection
) getList()
1156 List selectedItems
= selection
.toList();
1157 Object itemToRemove
= null;
1159 for (Iterator it
= selection
.iterator(); it
.hasNext();) {
1160 Object item
= it
.next();
1161 if (item
instanceof ItemsListSeparator
) {
1162 itemToRemove
= item
;
1167 if (itemToRemove
== null) {
1168 return new StructuredSelection(selectedItems
);
1170 // Create a new selection without the collision
1171 List newItems
= new ArrayList(selectedItems
);
1172 newItems
.remove(itemToRemove
);
1173 return new StructuredSelection(newItems
);
1178 * Validates the item. When items on the items list are selected or
1179 * deselected, it validates each item in the selection and the dialog status
1180 * depends on all validations.
1183 * an item to be checked
1184 * @return status of the dialog to be set
1186 protected abstract IStatus
validateItem(Object item
);
1189 * Creates an instance of a filter.
1191 * @return a filter for items on the items list. Can be <code>null</code>,
1192 * no filtering will be applied then, causing no item to be shown in
1195 protected abstract ItemsFilter
createFilter();
1199 * Applies the filter created by <code>createFilter()</code> method to the
1200 * items list. When new filter is different than previous one it will cause
1203 protected void applyFilter() {
1204 // to get an already filtered selection of the database we added the initModel() method here.
1206 ItemsFilter newFilter
= createFilter();
1208 // don't apply filtering for patterns which mean the same, for example:
1210 if (filter
!= null && filter
.equalsFilter(newFilter
)) {
1214 filterHistoryJob
.cancel();
1217 this.filter
= newFilter
;
1219 if (this.filter
!= null) {
1220 filterHistoryJob
.schedule();
1225 * Returns comparator to sort items inside content provider. Returned object
1226 * will be probably created as an anonymous class. Parameters passed to the
1227 * <code>compare(java.lang.Object, java.lang.Object)</code> are going to
1228 * be the same type as the one used in the content provider.
1230 * @return comparator to sort items content provider
1232 protected abstract Comparator
getItemsComparator();
1235 * Fills the content provider with matching items.
1237 * @param contentProvider
1238 * collector to add items to.
1239 * {@link FilteredItemsSelectionDialog.AbstractContentProvider#add(Object, FilteredItemsSelectionDialog.ItemsFilter)}
1240 * only adds items that pass the given <code>itemsFilter</code>.
1241 * @param itemsFilter
1243 * @param progressMonitor
1244 * must be used to report search progress. The state of this
1245 * progress monitor reflects the state of the filtering process.
1246 * @throws CoreException
1248 protected abstract void fillContentProvider(
1249 AbstractContentProvider contentProvider
, ItemsFilter itemsFilter
,
1250 IProgressMonitor progressMonitor
) throws CoreException
;
1253 * Removes selected items from history.
1256 * items to be removed
1258 private void removeSelectedItems(List items
) {
1259 for (Iterator iter
= items
.iterator(); iter
.hasNext();) {
1260 Object item
= iter
.next();
1261 removeHistoryItem(item
);
1263 refreshWithLastSelection
= false;
1264 contentProvider
.refresh();
1268 * Removes an item from history.
1272 * @return removed item
1274 protected Object
removeHistoryItem(Object item
) {
1275 return contentProvider
.removeHistoryElement(item
);
1279 * Adds item to history.
1282 * the item to be added
1284 protected void accessedHistoryItem(Object item
) {
1285 contentProvider
.addHistoryElement(item
);
1289 * Returns a history comparator.
1291 * @return decorated comparator
1293 private Comparator
getHistoryComparator() {
1294 return new HistoryComparator();
1298 * Returns the history of selected elements.
1300 * @return history of selected elements, or <code>null</code> if it is not
1303 protected SelectionHistory
getSelectionHistory() {
1304 return this.contentProvider
.getSelectionHistory();
1310 * @param selectionHistory
1313 protected void setSelectionHistory(SelectionHistory selectionHistory
) {
1314 if (this.contentProvider
!= null) {
1315 this.contentProvider
.setSelectionHistory(selectionHistory
);
1320 * Indicates whether the given item is a history item.
1323 * the item to be investigated
1324 * @return <code>true</code> if the given item exists in history,
1325 * <code>false</code> otherwise
1327 public boolean isHistoryElement(Object item
) {
1328 return this.contentProvider
.isHistoryElement(item
);
1332 * Indicates whether the given item is a duplicate.
1335 * the item to be investigated
1336 * @return <code>true</code> if the item is duplicate, <code>false</code>
1339 public boolean isDuplicateElement(Object item
) {
1340 return this.contentProvider
.isDuplicateElement(item
);
1344 * Sets separator label
1346 * @param separatorLabel
1347 * the label showed on separator
1349 public void setSeparatorLabel(String separatorLabel
) {
1350 this.itemsListSeparator
= new ItemsListSeparator(separatorLabel
);
1354 * Returns name for then given object.
1357 * an object from the content provider. Subclasses should pay
1358 * attention to the passed argument. They should either only pass
1359 * objects of a known type (one used in content provider) or make
1360 * sure that passed parameter is the expected one (by type
1361 * checking like <code>instanceof</code> inside the method).
1362 * @return name of the given item
1364 public abstract String
getElementName(Object item
);
1366 private class ToggleStatusLineAction
extends Action
{
1369 * Creates a new instance of the class.
1371 public ToggleStatusLineAction() {
1373 WorkbenchMessages
.FilteredItemsSelectionDialog_toggleStatusAction
,
1374 IAction
.AS_CHECK_BOX
);
1379 details
.setVisible(isChecked());
1384 * Only refreshes UI on the basis of an already sorted and filtered set of
1387 * Standard invocation scenario:
1389 * <li>filtering job (<code>FilterJob</code> class extending
1390 * <code>Job</code> class)</li>
1391 * <li>cache refresh without checking for duplicates (<code>RefreshCacheJob</code>
1392 * class extending <code>Job</code> class)</li>
1393 * <li>UI refresh (<code>RefreshJob</code> class extending
1394 * <code>UIJob</code> class)</li>
1395 * <li>cache refresh with checking for duplicates (<cod>CacheRefreshJob</code>
1396 * class extending <code>Job</code> class)</li>
1397 * <li>UI refresh (<code>RefreshJob</code> class extending <code>UIJob</code>
1400 * The scenario is rather complicated, but it had to be applied, because:
1402 * <li> refreshing cache is rather a long action and cannot be run in the UI -
1403 * cannot be run in a UIJob</li>
1404 * <li> refreshing cache checking for duplicates is twice as long as
1405 * refreshing cache without checking for duplicates; results of the search
1406 * could be displayed earlier</li>
1407 * <li> refreshing the UI have to be run in a UIJob</li>
1410 * @see org.eclipse.ui.dialogs.FilteredItemsSelectionDialog.FilterJob
1411 * @see org.eclipse.ui.dialogs.FilteredItemsSelectionDialog.RefreshJob
1412 * @see org.eclipse.ui.dialogs.FilteredItemsSelectionDialog.RefreshCacheJob
1414 private class RefreshJob
extends UIJob
{
1417 * Creates a new instance of the class.
1419 public RefreshJob() {
1420 super(CdmFilteredItemsSelectionDialog
.this.getParentShell()
1422 WorkbenchMessages
.FilteredItemsSelectionDialog_refreshJob
);
1429 * @see org.eclipse.ui.progress.UIJob#runInUIThread(org.eclipse.core.runtime.IProgressMonitor)
1432 public IStatus
runInUIThread(IProgressMonitor monitor
) {
1433 if (monitor
.isCanceled()) {
1434 return new Status(IStatus
.OK
, WorkbenchPlugin
.PI_WORKBENCH
,
1435 IStatus
.OK
, EMPTY_STRING
, null);
1438 if (CdmFilteredItemsSelectionDialog
.this != null) {
1439 CdmFilteredItemsSelectionDialog
.this.refresh();
1442 return new Status(IStatus
.OK
, PlatformUI
.PLUGIN_ID
, IStatus
.OK
,
1443 EMPTY_STRING
, null);
1449 * Refreshes the progress message cyclically with 500 milliseconds delay.
1450 * <code>RefreshProgressMessageJob</code> is strictly connected with
1451 * <code>GranualProgressMonitor</code> and use it to to get progress
1452 * message and to decide about break of cyclical refresh.
1454 private class RefreshProgressMessageJob
extends UIJob
{
1456 private GranualProgressMonitor progressMonitor
;
1459 * Creates a new instance of the class.
1461 public RefreshProgressMessageJob() {
1463 CdmFilteredItemsSelectionDialog
.this.getParentShell()
1465 WorkbenchMessages
.FilteredItemsSelectionDialog_progressRefreshJob
);
1472 * @see org.eclipse.ui.progress.UIJob#runInUIThread(org.eclipse.core.runtime.IProgressMonitor)
1475 public IStatus
runInUIThread(IProgressMonitor monitor
) {
1477 if (!progressLabel
.isDisposed()) {
1478 progressLabel
.setText(progressMonitor
!= null ? progressMonitor
1479 .getMessage() : EMPTY_STRING
);
1482 if (progressMonitor
== null || progressMonitor
.isDone()) {
1483 return new Status(IStatus
.CANCEL
, PlatformUI
.PLUGIN_ID
,
1484 IStatus
.CANCEL
, EMPTY_STRING
, null);
1487 // Schedule cyclical with 500 milliseconds delay
1490 return new Status(IStatus
.OK
, PlatformUI
.PLUGIN_ID
, IStatus
.OK
,
1491 EMPTY_STRING
, null);
1495 * Schedule progress refresh job.
1497 * @param progressMonitor
1498 * used during refresh progress label
1500 public void scheduleProgressRefresh(
1501 GranualProgressMonitor progressMonitor
) {
1502 this.progressMonitor
= progressMonitor
;
1503 // Schedule with initial delay to avoid flickering when the user
1511 * A job responsible for computing filtered items list presented using
1512 * <code>RefreshJob</code>.
1514 * @see FilteredItemsSelectionDialog.RefreshJob
1517 private class RefreshCacheJob
extends Job
{
1519 private final RefreshJob refreshJob
= new RefreshJob();
1522 * Creates a new instance of the class.
1524 public RefreshCacheJob() {
1526 WorkbenchMessages
.FilteredItemsSelectionDialog_cacheRefreshJob
);
1531 * Stops the job and all sub-jobs.
1533 public void cancelAll() {
1535 refreshJob
.cancel();
1541 * @see org.eclipse.core.runtime.jobs.Job#run(org.eclipse.core.runtime.IProgressMonitor)
1544 protected IStatus
run(IProgressMonitor monitor
) {
1545 if (monitor
.isCanceled()) {
1546 return new Status(IStatus
.CANCEL
, WorkbenchPlugin
.PI_WORKBENCH
,
1547 IStatus
.CANCEL
, EMPTY_STRING
, null);
1550 if (CdmFilteredItemsSelectionDialog
.this != null) {
1551 GranualProgressMonitor wrappedMonitor
= new GranualProgressMonitor(
1553 CdmFilteredItemsSelectionDialog
.this.reloadCache(true,
1557 if (!monitor
.isCanceled()) {
1558 refreshJob
.schedule();
1561 return new Status(IStatus
.OK
, PlatformUI
.PLUGIN_ID
, IStatus
.OK
,
1562 EMPTY_STRING
, null);
1569 * @see org.eclipse.core.runtime.jobs.Job#canceling()
1572 protected void canceling() {
1574 contentProvider
.stopReloadingCache();
1579 private class RemoveHistoryItemAction
extends Action
{
1582 * Creates a new instance of the class.
1584 public RemoveHistoryItemAction() {
1586 WorkbenchMessages
.FilteredItemsSelectionDialog_removeItemsFromHistoryAction
);
1592 * @see org.eclipse.jface.action.Action#run()
1596 List selectedElements
= ((StructuredSelection
) getList().getSelection())
1598 removeSelectedItems(selectedElements
);
1602 protected static boolean showColoredLabels() {
1603 return PlatformUI
.getPreferenceStore().getBoolean(IWorkbenchPreferenceConstants
.USE_COLORED_LABELS
);
1606 private class ItemsListLabelProvider
extends StyledCellLabelProvider
1607 implements ILabelProviderListener
{
1608 private ILabelProvider provider
;
1610 private ILabelDecorator selectionDecorator
;
1612 // Need to keep our own list of listeners
1613 private final ListenerList listeners
= new ListenerList();
1616 * Creates a new instance of the class.
1619 * the label provider for all items, not <code>null</code>
1620 * @param selectionDecorator
1621 * the decorator for selected items, can be <code>null</code>
1623 public ItemsListLabelProvider(ILabelProvider provider
,
1624 ILabelDecorator selectionDecorator
) {
1625 Assert
.isNotNull(provider
);
1626 this.provider
= provider
;
1627 this.selectionDecorator
= selectionDecorator
;
1629 setOwnerDrawEnabled(showColoredLabels() && provider
instanceof IStyledLabelProvider
);
1631 provider
.addListener(this);
1633 if (selectionDecorator
!= null) {
1634 selectionDecorator
.addListener(this);
1639 * Sets new selection decorator.
1641 * @param newSelectionDecorator
1642 * new label decorator for selected items in the list
1644 public void setSelectionDecorator(ILabelDecorator newSelectionDecorator
) {
1645 if (selectionDecorator
!= null) {
1646 selectionDecorator
.removeListener(this);
1647 selectionDecorator
.dispose();
1650 selectionDecorator
= newSelectionDecorator
;
1652 if (selectionDecorator
!= null) {
1653 selectionDecorator
.addListener(this);
1658 * Gets selection decorator.
1660 * @return the label decorator for selected items in the list
1662 public ILabelDecorator
getSelectionDecorator() {
1663 return selectionDecorator
;
1667 * Sets new label provider.
1669 * @param newProvider
1670 * new label provider for items in the list, not
1673 public void setProvider(ILabelProvider newProvider
) {
1674 Assert
.isNotNull(newProvider
);
1675 provider
.removeListener(this);
1678 provider
= newProvider
;
1680 if (provider
!= null) {
1681 provider
.addListener(this);
1684 setOwnerDrawEnabled(showColoredLabels() && provider
instanceof IStyledLabelProvider
);
1687 private Image
getImage(Object element
) {
1688 if (element
instanceof ItemsListSeparator
) {
1689 return WorkbenchImages
1690 .getImage(IWorkbenchGraphicConstants
.IMG_OBJ_SEPARATOR
);
1693 return provider
.getImage(element
);
1696 private boolean isSelected(Object element
) {
1697 if (element
!= null && getCurrentSelection() != null) {
1698 for (int i
= 0; i
< getCurrentSelection().length
; i
++) {
1699 if (element
.equals(getCurrentSelection()[i
])) {
1710 * @see org.eclipse.jface.viewers.ILabelProvider#getText(java.lang.Object)
1712 private String
getText(Object element
) {
1713 if (element
instanceof ItemsListSeparator
) {
1714 return getSeparatorLabel(((ItemsListSeparator
) element
)
1718 String str
= provider
.getText(element
);
1719 if (selectionDecorator
!= null && isSelected(element
)) {
1720 return selectionDecorator
.decorateText(str
.toString(), element
);
1726 private StyledString
getStyledText(Object element
,
1727 IStyledLabelProvider provider
) {
1728 StyledString string
= provider
.getStyledText(element
);
1730 if (selectionDecorator
!= null && isSelected(element
)) {
1731 String decorated
= selectionDecorator
.decorateText(string
1732 .getString(), element
);
1733 return StyledCellLabelProvider
.styleDecoratedString(decorated
, null, string
);
1734 // no need to add colors when element is selected
1740 public void update(ViewerCell cell
) {
1741 Object element
= cell
.getElement();
1743 if (!(element
instanceof ItemsListSeparator
)
1744 && provider
instanceof IStyledLabelProvider
) {
1745 IStyledLabelProvider styledLabelProvider
= (IStyledLabelProvider
) provider
;
1746 StyledString styledString
= getStyledText(element
,
1747 styledLabelProvider
);
1749 cell
.setText(styledString
.getString());
1750 cell
.setStyleRanges(styledString
.getStyleRanges());
1751 cell
.setImage(styledLabelProvider
.getImage(element
));
1753 cell
.setText(getText(element
));
1754 cell
.setImage(getImage(element
));
1756 cell
.setFont(getFont(element
));
1757 cell
.setForeground(getForeground(element
));
1758 cell
.setBackground(getBackground(element
));
1763 private String
getSeparatorLabel(String separatorLabel
) {
1764 Rectangle rect
= getList().getTable().getBounds();
1766 int borderWidth
= getList().getTable().computeTrim(0, 0, 0, 0).width
;
1768 int imageWidth
= WorkbenchImages
.getImage(
1769 IWorkbenchGraphicConstants
.IMG_OBJ_SEPARATOR
).getBounds().width
;
1771 int width
= rect
.width
- borderWidth
- imageWidth
;
1773 GC gc
= new GC(getList().getTable());
1774 gc
.setFont(getList().getTable().getFont());
1776 int fSeparatorWidth
= gc
.getAdvanceWidth('-');
1777 int fMessageLength
= gc
.textExtent(separatorLabel
).x
;
1781 StringBuffer dashes
= new StringBuffer();
1782 int chars
= (((width
- fMessageLength
) / fSeparatorWidth
) / 2) - 2;
1783 for (int i
= 0; i
< chars
; i
++) {
1787 StringBuffer result
= new StringBuffer();
1788 result
.append(dashes
);
1789 result
.append(" " + separatorLabel
+ " "); //$NON-NLS-1$//$NON-NLS-2$
1790 result
.append(dashes
);
1791 return result
.toString().trim();
1797 * @see org.eclipse.jface.viewers.IBaseLabelProvider#addListener(org.eclipse.jface.viewers.ILabelProviderListener)
1800 public void addListener(ILabelProviderListener listener
) {
1801 listeners
.add(listener
);
1807 * @see org.eclipse.jface.viewers.IBaseLabelProvider#dispose()
1810 public void dispose() {
1811 provider
.removeListener(this);
1814 if (selectionDecorator
!= null) {
1815 selectionDecorator
.removeListener(this);
1816 selectionDecorator
.dispose();
1825 * @see org.eclipse.jface.viewers.IBaseLabelProvider#isLabelProperty(java.lang.Object,
1829 public boolean isLabelProperty(Object element
, String property
) {
1830 if (provider
.isLabelProperty(element
, property
)) {
1833 if (selectionDecorator
!= null
1834 && selectionDecorator
.isLabelProperty(element
, property
)) {
1843 * @see org.eclipse.jface.viewers.IBaseLabelProvider#removeListener(org.eclipse.jface.viewers.ILabelProviderListener)
1846 public void removeListener(ILabelProviderListener listener
) {
1847 listeners
.remove(listener
);
1850 private Color
getBackground(Object element
) {
1851 if (element
instanceof ItemsListSeparator
) {
1854 if (provider
instanceof IColorProvider
) {
1855 return ((IColorProvider
) provider
).getBackground(element
);
1860 private Color
getForeground(Object element
) {
1861 if (element
instanceof ItemsListSeparator
) {
1862 return Display
.getCurrent().getSystemColor(
1863 SWT
.COLOR_WIDGET_NORMAL_SHADOW
);
1865 if (provider
instanceof IColorProvider
) {
1866 return ((IColorProvider
) provider
).getForeground(element
);
1871 private Font
getFont(Object element
) {
1872 if (element
instanceof ItemsListSeparator
) {
1875 if (provider
instanceof IFontProvider
) {
1876 return ((IFontProvider
) provider
).getFont(element
);
1884 * @see org.eclipse.jface.viewers.ILabelProviderListener#labelProviderChanged(org.eclipse.jface.viewers.LabelProviderChangedEvent)
1887 public void labelProviderChanged(LabelProviderChangedEvent event
) {
1888 Object
[] l
= listeners
.getListeners();
1889 for (int i
= 0; i
< listeners
.size(); i
++) {
1890 ((ILabelProviderListener
) l
[i
]).labelProviderChanged(event
);
1896 * Used in ItemsListContentProvider, separates history and non-history
1899 protected class ItemsListSeparator
{
1901 private final String name
;
1904 * Creates a new instance of the class.
1907 * the name of the separator
1909 public ItemsListSeparator(String name
) {
1914 * Returns the name of this separator.
1916 * @return the name of the separator
1918 public String
getName() {
1924 * GranualProgressMonitor is used for monitoring progress of filtering
1925 * process. It is used by <code>RefreshProgressMessageJob</code> to
1926 * refresh progress message. State of this monitor illustrates state of
1927 * filtering or cache refreshing process.
1930 private class GranualProgressMonitor
extends ProgressMonitorWrapper
{
1932 private String name
;
1934 private String subName
;
1936 private int totalWork
;
1938 private double worked
;
1940 private boolean done
;
1943 * Creates instance of <code>GranualProgressMonitor</code>.
1946 * progress to be wrapped
1948 public GranualProgressMonitor(IProgressMonitor monitor
) {
1953 * Checks if filtering has been done
1955 * @return true if filtering work has been done false in other way
1957 public boolean isDone() {
1964 * @see org.eclipse.core.runtime.ProgressMonitorWrapper#setTaskName(java.lang.String)
1967 public void setTaskName(String name
) {
1968 super.setTaskName(name
);
1970 this.subName
= null;
1976 * @see org.eclipse.core.runtime.ProgressMonitorWrapper#subTask(java.lang.String)
1979 public void subTask(String name
) {
1980 super.subTask(name
);
1981 this.subName
= name
;
1987 * @see org.eclipse.core.runtime.ProgressMonitorWrapper#beginTask(java.lang.String,
1991 public void beginTask(String name
, int totalWork
) {
1992 super.beginTask(name
, totalWork
);
1993 if (this.name
== null) {
1996 this.totalWork
= totalWork
;
1997 refreshProgressMessageJob
.scheduleProgressRefresh(this);
2003 * @see org.eclipse.core.runtime.ProgressMonitorWrapper#worked(int)
2006 public void worked(int work
) {
2008 internalWorked(work
);
2014 * @see org.eclipse.core.runtime.ProgressMonitorWrapper#done()
2017 public void done() {
2025 * @see org.eclipse.core.runtime.ProgressMonitorWrapper#setCanceled(boolean)
2028 public void setCanceled(boolean b
) {
2030 super.setCanceled(b
);
2036 * @see org.eclipse.core.runtime.ProgressMonitorWrapper#internalWorked(double)
2039 public void internalWorked(double work
) {
2040 worked
= worked
+ work
;
2043 private String
getMessage() {
2045 return ""; //$NON-NLS-1$
2051 message
= subName
== null ?
"" : subName
; //$NON-NLS-1$
2053 message
= subName
== null ? name
2056 WorkbenchMessages
.FilteredItemsSelectionDialog_subtaskProgressMessage
,
2057 new Object
[] { name
, subName
});
2059 if (totalWork
== 0) {
2065 WorkbenchMessages
.FilteredItemsSelectionDialog_taskProgressMessage
,
2069 (int) ((worked
* 100) / totalWork
)) });
2076 * Filters items history and schedule filter job.
2078 private class FilterHistoryJob
extends Job
{
2081 * Filter used during the filtering process.
2083 private ItemsFilter itemsFilter
;
2086 * Creates new instance of receiver.
2088 public FilterHistoryJob() {
2089 super(WorkbenchMessages
.FilteredItemsSelectionDialog_jobLabel
);
2096 * @see org.eclipse.core.runtime.jobs.Job#run(org.eclipse.core.runtime.IProgressMonitor)
2099 protected IStatus
run(IProgressMonitor monitor
) {
2101 this.itemsFilter
= filter
;
2103 contentProvider
.reset();
2105 refreshWithLastSelection
= false;
2107 contentProvider
.addHistoryItems(itemsFilter
);
2109 if (!(lastCompletedFilter
!= null && lastCompletedFilter
2110 .isSubFilter(this.itemsFilter
))) {
2111 contentProvider
.refresh();
2114 filterJob
.schedule();
2116 return Status
.OK_STATUS
;
2122 * Filters items in indicated set and history. During filtering, it
2123 * refreshes the dialog (progress monitor and elements list).
2125 * Depending on the filter, <code>FilterJob</code> decides which kind of
2126 * search will be run inside <code>filterContent</code>. If the last
2127 * filtering is done (last completed filter), is not null, and the new
2128 * filter is a sub-filter ({@link FilteredItemsSelectionDialog.ItemsFilter#isSubFilter(FilteredItemsSelectionDialog.ItemsFilter)})
2129 * of the last, then <code>FilterJob</code> only filters in the cache. If
2130 * it is the first filtering or the new filter isn't a sub-filter of the
2131 * last one, a full search is run.
2133 private class FilterJob
extends Job
{
2136 * Filter used during the filtering process.
2138 protected ItemsFilter itemsFilter
;
2141 * Creates new instance of FilterJob
2143 public FilterJob() {
2144 super(WorkbenchMessages
.FilteredItemsSelectionDialog_jobLabel
);
2151 * @see org.eclipse.core.runtime.jobs.Job#run(org.eclipse.core.runtime.IProgressMonitor)
2154 protected final IStatus
run(IProgressMonitor parent
) {
2155 GranualProgressMonitor monitor
= new GranualProgressMonitor(parent
);
2156 return doRun(monitor
);
2160 * Executes job using the given filtering progress monitor. A hook for
2165 * @return result of the execution
2167 protected IStatus
doRun(GranualProgressMonitor monitor
) {
2169 internalRun(monitor
);
2170 } catch (CoreException e
) {
2174 PlatformUI
.PLUGIN_ID
,
2176 WorkbenchMessages
.FilteredItemsSelectionDialog_jobError
,
2179 return Status
.OK_STATUS
;
2183 * Main method for the job.
2186 * @throws CoreException
2188 private void internalRun(GranualProgressMonitor monitor
)
2189 throws CoreException
{
2191 if (monitor
.isCanceled()) {
2195 this.itemsFilter
= filter
;
2197 if (filter
.getPattern().length() != 0) {
2198 filterContent(monitor
);
2201 if (monitor
.isCanceled()) {
2205 contentProvider
.refresh();
2215 * for monitoring progress
2216 * @throws CoreException
2218 protected void filterContent(GranualProgressMonitor monitor
)
2219 throws CoreException
{
2221 // if (lastCompletedFilter != null
2222 // && lastCompletedFilter.isSubFilter(this.itemsFilter)) {
2224 // int length = lastCompletedResult.size() / 500;
2227 // WorkbenchMessages.FilteredItemsSelectionDialog_cacheSearchJob_taskName,
2230 // for (int pos = 0; pos < lastCompletedResult.size(); pos++) {
2232 // Object item = lastCompletedResult.get(pos);
2233 // if (monitor.isCanceled()) {
2236 // contentProvider.add(item, itemsFilter);
2238 // if ((pos % 500) == 0) {
2239 // monitor.worked(1);
2245 lastCompletedFilter
= null;
2246 lastCompletedResult
= null;
2248 SubProgressMonitor subMonitor
= null;
2249 if (monitor
!= null) {
2252 WorkbenchMessages
.FilteredItemsSelectionDialog_searchJob_taskName
,
2254 subMonitor
= new SubProgressMonitor(monitor
, 95);
2258 fillContentProvider(contentProvider
, itemsFilter
, subMonitor
);
2260 if (monitor
!= null && !monitor
.isCanceled()) {
2262 contentProvider
.rememberResult(itemsFilter
);
2272 * History stores a list of key, object pairs. The list is bounded at a
2273 * certain size. If the list exceeds this size the oldest element is removed
2274 * from the list. An element can be added/renewed with a call to
2275 * <code>accessed(Object)</code>.
2277 * The history can be stored to/loaded from an XML file.
2279 protected static abstract class SelectionHistory
{
2281 private static final String DEFAULT_ROOT_NODE_NAME
= "historyRootNode"; //$NON-NLS-1$
2283 private static final String DEFAULT_INFO_NODE_NAME
= "infoNode"; //$NON-NLS-1$
2285 private static final int MAX_HISTORY_SIZE
= 60;
2287 private final Set historyList
;
2289 private final String rootNodeName
;
2291 private final String infoNodeName
;
2293 private SelectionHistory(String rootNodeName
, String infoNodeName
) {
2295 historyList
= Collections
.synchronizedSet(new LinkedHashSet() {
2297 private static final long serialVersionUID
= 0L;
2302 * @see java.util.LinkedList#add(java.lang.Object)
2305 public boolean add(Object arg0
) {
2306 if (this.size() >= MAX_HISTORY_SIZE
) {
2307 Iterator iterator
= this.iterator();
2311 return super.add(arg0
);
2316 this.rootNodeName
= rootNodeName
;
2317 this.infoNodeName
= infoNodeName
;
2321 * Creates new instance of <code>SelectionHistory</code>.
2323 public SelectionHistory() {
2324 this(DEFAULT_ROOT_NODE_NAME
, DEFAULT_INFO_NODE_NAME
);
2328 * Adds object to history.
2331 * the item to be added to the history
2333 public synchronized void accessed(Object object
) {
2334 historyList
.remove(object
);
2335 historyList
.add(object
);
2339 * Returns <code>true</code> if history contains object.
2342 * the item for which check will be executed
2343 * @return <code>true</code> if history contains object
2344 * <code>false</code> in other way
2346 public synchronized boolean contains(Object object
) {
2347 return historyList
.contains(object
);
2351 * Returns <code>true</code> if history is empty.
2353 * @return <code>true</code> if history is empty
2355 public synchronized boolean isEmpty() {
2356 return historyList
.isEmpty();
2360 * Remove element from history.
2363 * to remove form the history
2364 * @return <code>true</code> if this list contained the specified
2367 public synchronized boolean remove(Object element
) {
2368 return historyList
.remove(element
);
2372 * Load history elements from memento.
2375 * memento from which the history will be retrieved
2377 public void load(IMemento memento
) {
2379 XMLMemento historyMemento
= (XMLMemento
) memento
2380 .getChild(rootNodeName
);
2382 if (historyMemento
== null) {
2386 IMemento
[] mementoElements
= historyMemento
2387 .getChildren(infoNodeName
);
2388 for (int i
= 0; i
< mementoElements
.length
; ++i
) {
2389 IMemento mementoElement
= mementoElements
[i
];
2390 Object object
= restoreItemFromMemento(mementoElement
);
2391 if (object
!= null) {
2392 historyList
.add(object
);
2398 * Save history elements to memento.
2401 * memento to which the history will be added
2403 public void save(IMemento memento
) {
2405 IMemento historyMemento
= memento
.createChild(rootNodeName
);
2407 Object
[] items
= getHistoryItems();
2408 for (int i
= 0; i
< items
.length
; i
++) {
2409 Object item
= items
[i
];
2410 IMemento elementMemento
= historyMemento
2411 .createChild(infoNodeName
);
2412 storeItemToMemento(item
, elementMemento
);
2418 * Gets array of history items.
2420 * @return array of history elements
2422 public synchronized Object
[] getHistoryItems() {
2423 return historyList
.toArray();
2427 * Creates an object using given memento.
2430 * memento used for creating new object
2432 * @return the restored object
2434 protected abstract Object
restoreItemFromMemento(IMemento memento
);
2437 * Store object in <code>IMemento</code>.
2442 * the memento to store to
2444 protected abstract void storeItemToMemento(Object item
, IMemento memento
);
2449 * Filters elements using SearchPattern by comparing the names of items with
2450 * the filter pattern.
2452 protected abstract class ItemsFilter
{
2454 protected SearchPattern patternMatcher
;
2457 * Creates new instance of ItemsFilter.
2459 public ItemsFilter() {
2460 this(new SearchPattern());
2464 * Creates new instance of ItemsFilter.
2466 * @param searchPattern
2467 * the pattern to be used when filtering
2469 public ItemsFilter(SearchPattern searchPattern
) {
2470 patternMatcher
= searchPattern
;
2471 String stringPattern
= ""; //$NON-NLS-1$
2472 if (pattern
!= null && !pattern
.getText().equals("*")) { //$NON-NLS-1$
2473 stringPattern
= pattern
.getText();
2475 patternMatcher
.setPattern(stringPattern
);
2479 * Check if the given filter is a sub-filter of this filter. The default
2480 * implementation checks if the <code>SearchPattern</code> from the
2481 * given filter is a sub-pattern of the one from this filter.
2483 * <i>WARNING: This method is <b>not</b> defined in reading order, i.e.
2484 * <code>a.isSubFilter(b)</code> is <code>true</code> iff
2485 * <code>b</code> is a sub-filter of <code>a</code>, and not
2490 * the filter to be checked, or <code>null</code>
2491 * @return <code>true</code> if the given filter is sub-filter of this
2492 * filter, <code>false</code> if the given filter isn't a
2493 * sub-filter or is <code>null</code>
2495 * @see org.eclipse.ui.dialogs.SearchPattern#isSubPattern(org.eclipse.ui.dialogs.SearchPattern)
2497 public boolean isSubFilter(ItemsFilter filter
) {
2498 if (filter
!= null) {
2499 return this.patternMatcher
.isSubPattern(filter
.patternMatcher
);
2505 * Checks whether the provided filter is equal to the current filter.
2506 * The default implementation checks if <code>SearchPattern</code>
2507 * from current filter is equal to the one from provided filter.
2510 * filter to be checked, or <code>null</code>
2511 * @return <code>true</code> if the given filter is equal to current
2512 * filter, <code>false</code> if given filter isn't equal to
2513 * current one or if it is <code>null</code>
2515 * @see org.eclipse.ui.dialogs.SearchPattern#equalsPattern(org.eclipse.ui.dialogs.SearchPattern)
2517 public boolean equalsFilter(ItemsFilter filter
) {
2519 && filter
.patternMatcher
.equalsPattern(this.patternMatcher
)) {
2526 * Checks whether the pattern's match rule is camel case.
2528 * @return <code>true</code> if pattern's match rule is camel case,
2529 * <code>false</code> otherwise
2531 public boolean isCamelCasePattern() {
2532 return patternMatcher
.getMatchRule() == SearchPattern
.RULE_CAMELCASE_MATCH
;
2536 * Returns the pattern string.
2538 * @return pattern for this filter
2540 * @see SearchPattern#getPattern()
2542 public String
getPattern() {
2543 return patternMatcher
.getPattern();
2547 * Returns the rule to apply for matching keys.
2549 * @return an implementation-specific match rule
2551 * @see SearchPattern#getMatchRule() for match rules returned by the
2552 * default implementation
2554 public int getMatchRule() {
2555 return patternMatcher
.getMatchRule();
2559 * Matches text with filter.
2562 * the text to match with the filter
2563 * @return <code>true</code> if text matches with filter pattern,
2564 * <code>false</code> otherwise
2566 protected boolean matches(String text
) {
2567 return patternMatcher
.matches(text
);
2571 * General method for matching raw name pattern. Checks whether current
2572 * pattern is prefix of name provided item.
2576 * @return <code>true</code> if current pattern is a prefix of name
2577 * provided item, <code>false</code> if item's name is shorter
2578 * than prefix or sequences of characters don't match.
2580 public boolean matchesRawNamePattern(Object item
) {
2581 String prefix
= patternMatcher
.getPattern();
2582 String text
= getElementName(item
);
2588 int textLength
= text
.length();
2589 int prefixLength
= prefix
.length();
2590 if (textLength
< prefixLength
) {
2593 for (int i
= prefixLength
- 1; i
>= 0; i
--) {
2594 if (Character
.toLowerCase(prefix
.charAt(i
)) != Character
2595 .toLowerCase(text
.charAt(i
))) {
2603 * Matches an item against filter conditions.
2606 * @return <code>true<code> if item matches against filter conditions, <code>false</code>
2609 public abstract boolean matchItem(Object item
);
2612 * Checks consistency of an item. Item is inconsistent if was changed or
2616 * @return <code>true</code> if item is consistent, <code>false</code>
2617 * if item is inconsistent
2619 public abstract boolean isConsistentItem(Object item
);
2624 * An interface to content providers for
2625 * <code>FilterItemsSelectionDialog</code>.
2627 protected abstract class AbstractContentProvider
{
2629 * Adds the item to the content provider iff the filter matches the
2630 * item. Otherwise does nothing.
2634 * @param itemsFilter
2637 * @see FilteredItemsSelectionDialog.ItemsFilter#matchItem(Object)
2639 public abstract void add(Object item
, ItemsFilter itemsFilter
);
2643 * Collects filtered elements. Contains one synchronized, sorted set for
2644 * collecting filtered elements. All collected elements are sorted using
2645 * comparator. Comparator is returned by getElementComparator() method.
2646 * Implementation of <code>ItemsFilter</code> is used to filter elements.
2647 * The key function of filter used in to filtering is
2648 * <code>matchElement(Object item)</code>.
2650 * The <code>ContentProvider</code> class also provides item filtering
2651 * methods. The filtering has been moved from the standard TableView
2652 * <code>getFilteredItems()</code> method to content provider, because
2653 * <code>ILazyContentProvider</code> and virtual tables are used. This
2654 * class is responsible for adding a separator below history items and
2655 * marking each items as duplicate if its name repeats more than once on the
2658 private class ContentProvider
extends AbstractContentProvider
implements
2659 IStructuredContentProvider
, ILazyContentProvider
{
2661 private SelectionHistory selectionHistory
;
2664 * Raw result of the searching (unsorted, unfiltered).
2666 * Standard object flow:
2667 * <code>items -> lastSortedItems -> lastFilteredItems</code>
2669 private final Set items
;
2672 * Items that are duplicates.
2674 private final Set duplicates
;
2677 * List of <code>ViewerFilter</code>s to be used during filtering
2679 private List filters
;
2682 * Result of the last filtering.
2684 * Standard object flow:
2685 * <code>items -> lastSortedItems -> lastFilteredItems</code>
2687 private List lastFilteredItems
;
2690 * Result of the last sorting.
2692 * Standard object flow:
2693 * <code>items -> lastSortedItems -> lastFilteredItems</code>
2695 private final List lastSortedItems
;
2698 * Used for <code>getFilteredItems()</code> method canceling (when the
2699 * job that invoked the method was canceled).
2701 * Method canceling could be based (only) on monitor canceling
2702 * unfortunately sometimes the method <code>getFilteredElements()</code>
2703 * could be run with a null monitor, the <code>reset</code> flag have
2704 * to be left intact.
2706 private boolean reset
;
2709 * Creates new instance of <code>ContentProvider</code>.
2711 public ContentProvider() {
2712 this.items
= Collections
.synchronizedSet(new HashSet(2048));
2713 this.duplicates
= Collections
.synchronizedSet(new HashSet(256));
2714 this.lastFilteredItems
= new ArrayList();
2715 this.lastSortedItems
= Collections
.synchronizedList(new ArrayList(
2720 * Sets selection history.
2722 * @param selectionHistory
2723 * The selectionHistory to set.
2725 public void setSelectionHistory(SelectionHistory selectionHistory
) {
2726 this.selectionHistory
= selectionHistory
;
2730 * @return Returns the selectionHistory.
2732 public SelectionHistory
getSelectionHistory() {
2733 return selectionHistory
;
2737 * Removes all content items and resets progress message.
2739 public void reset() {
2742 this.duplicates
.clear();
2743 this.lastSortedItems
.clear();
2747 * Stops reloading cache - <code>getFilteredItems()</code> method.
2749 public void stopReloadingCache() {
2754 * Adds filtered item.
2757 * @param itemsFilter
2760 public void add(Object item
, ItemsFilter itemsFilter
) {
2761 if (itemsFilter
== filter
) {
2762 if (itemsFilter
!= null) {
2763 if (itemsFilter
.matchItem(item
)) {
2764 this.items
.add(item
);
2767 this.items
.add(item
);
2773 * Add all history items to <code>contentProvider</code>.
2775 * @param itemsFilter
2777 public void addHistoryItems(ItemsFilter itemsFilter
) {
2778 if (this.selectionHistory
!= null) {
2779 Object
[] items
= this.selectionHistory
.getHistoryItems();
2780 for (int i
= 0; i
< items
.length
; i
++) {
2781 Object item
= items
[i
];
2782 if (itemsFilter
== filter
) {
2783 if (itemsFilter
!= null) {
2784 if (itemsFilter
.matchItem(item
)) {
2785 if (itemsFilter
.isConsistentItem(item
)) {
2786 this.items
.add(item
);
2788 this.selectionHistory
.remove(item
);
2800 public void refresh() {
2805 * Removes items from history and refreshes the view.
2810 * @return removed item
2812 public Object
removeHistoryElement(Object item
) {
2813 if (this.selectionHistory
!= null) {
2814 this.selectionHistory
.remove(item
);
2816 if (filter
== null || filter
.getPattern().length() == 0) {
2818 duplicates
.remove(item
);
2819 this.lastSortedItems
.remove(item
);
2822 synchronized (lastSortedItems
) {
2823 Collections
.sort(lastSortedItems
, getHistoryComparator());
2829 * Adds item to history and refresh view.
2834 public void addHistoryElement(Object item
) {
2835 if (this.selectionHistory
!= null) {
2836 this.selectionHistory
.accessed(item
);
2838 if (filter
== null || !filter
.matchItem(item
)) {
2839 this.items
.remove(item
);
2840 this.duplicates
.remove(item
);
2841 this.lastSortedItems
.remove(item
);
2843 synchronized (lastSortedItems
) {
2844 Collections
.sort(lastSortedItems
, getHistoryComparator());
2851 * @return <code>true</code> if given item is part of the history
2853 public boolean isHistoryElement(Object item
) {
2854 if (this.selectionHistory
!= null) {
2855 return this.selectionHistory
.contains(item
);
2861 * Sets/unsets given item as duplicate.
2866 * @param isDuplicate
2869 public void setDuplicateElement(Object item
, boolean isDuplicate
) {
2870 if (this.items
.contains(item
)) {
2872 this.duplicates
.add(item
);
2874 this.duplicates
.remove(item
);
2880 * Indicates whether given item is a duplicate.
2884 * @return <code>true</code> if item is duplicate
2886 public boolean isDuplicateElement(Object item
) {
2887 return duplicates
.contains(item
);
2891 * Load history from memento.
2894 * memento from which the history will be retrieved
2896 public void loadHistory(IMemento memento
) {
2897 if (this.selectionHistory
!= null) {
2898 this.selectionHistory
.load(memento
);
2903 * Save history to memento.
2906 * memento to which the history will be added
2908 public void saveHistory(IMemento memento
) {
2909 if (this.selectionHistory
!= null) {
2910 this.selectionHistory
.save(memento
);
2915 * Gets sorted items.
2917 * @return sorted items
2919 private Object
[] getSortedItems() {
2920 if (lastSortedItems
.size() != items
.size()) {
2921 synchronized (lastSortedItems
) {
2922 lastSortedItems
.clear();
2923 lastSortedItems
.addAll(items
);
2924 Collections
.sort(lastSortedItems
, getHistoryComparator());
2927 return lastSortedItems
.toArray();
2931 * Remember result of filtering.
2933 * @param itemsFilter
2935 public void rememberResult(ItemsFilter itemsFilter
) {
2936 List itemsList
= Collections
.synchronizedList(Arrays
2937 .asList(getSortedItems()));
2939 if (itemsFilter
== filter
) {
2940 lastCompletedFilter
= itemsFilter
;
2941 lastCompletedResult
= itemsList
;
2949 * @see org.eclipse.jface.viewers.IStructuredContentProvider#getElements(java.lang.Object)
2952 public Object
[] getElements(Object inputElement
) {
2953 //return items.toArray();
2954 return lastFilteredItems
.toArray();
2957 public int getNumberOfElements() {
2959 return lastFilteredItems
.size();
2965 * @see org.eclipse.jface.viewers.IContentProvider#dispose()
2968 public void dispose() {
2974 * @see org.eclipse.jface.viewers.IContentProvider#inputChanged(org.eclipse.jface.viewers.Viewer,
2975 * java.lang.Object, java.lang.Object)
2978 public void inputChanged(Viewer viewer
, Object oldInput
, Object newInput
) {
2984 * @see org.eclipse.jface.viewers.ILazyContentProvider#updateElement(int)
2987 public void updateElement(int index
) {
2989 CdmFilteredItemsSelectionDialog
.this.getList().replace((lastFilteredItems
2990 .size() > index
) ? lastFilteredItems
.get(index
) : null,
2996 * Main method responsible for getting the filtered items and checking
2997 * for duplicates. It is based on the
2998 * {@link FilteredItemsSelectionDialog.ContentProvider#getFilteredItems(Object, IProgressMonitor)}.
3000 * @param checkDuplicates
3001 * <code>true</code> if data concerning elements
3002 * duplication should be computed - it takes much more time
3003 * than standard filtering
3008 public void reloadCache(boolean checkDuplicates
,
3009 IProgressMonitor monitor
) {
3013 if (monitor
!= null) {
3014 // the work is divided into two actions of the same length
3015 int totalWork
= checkDuplicates ?
200 : 100;
3019 WorkbenchMessages
.FilteredItemsSelectionDialog_cacheRefreshJob
,
3023 // the TableViewer's root (the input) is treated as parent
3025 lastFilteredItems
= Arrays
.asList(getFilteredItems(getList().getInput(),
3026 monitor
!= null ?
new SubProgressMonitor(monitor
, 100)
3029 if (reset
|| (monitor
!= null && monitor
.isCanceled())) {
3030 if (monitor
!= null) {
3036 if (checkDuplicates
) {
3037 checkDuplicates(monitor
);
3039 if (monitor
!= null) {
3044 private void checkDuplicates(IProgressMonitor monitor
) {
3045 synchronized (lastFilteredItems
) {
3046 IProgressMonitor subMonitor
= null;
3047 int reportEvery
= lastFilteredItems
.size() / 20;
3048 if (monitor
!= null) {
3049 subMonitor
= new SubProgressMonitor(monitor
, 100);
3052 WorkbenchMessages
.FilteredItemsSelectionDialog_cacheRefreshJob_checkDuplicates
,
3055 HashMap helperMap
= new HashMap();
3056 for (int i
= 0; i
< lastFilteredItems
.size(); i
++) {
3058 || (subMonitor
!= null && subMonitor
.isCanceled())) {
3061 Object item
= lastFilteredItems
.get(i
);
3063 if (!(item
instanceof ItemsListSeparator
)) {
3064 Object previousItem
= helperMap
.put(
3065 getElementName(item
), item
);
3066 if (previousItem
!= null) {
3067 setDuplicateElement(previousItem
, true);
3068 setDuplicateElement(item
, true);
3070 setDuplicateElement(item
, false);
3074 if (subMonitor
!= null && reportEvery
!= 0
3075 && (i
+ 1) % reportEvery
== 0) {
3076 subMonitor
.worked(1);
3084 * Returns an array of items filtered using the provided
3085 * <code>ViewerFilter</code>s with a separator added.
3090 * progress monitor, can be <code>null</code>
3091 * @return an array of filtered items
3093 protected Object
[] getFilteredItems(Object parent
,
3094 IProgressMonitor monitor
) {
3096 if (monitor
== null) {
3097 monitor
= new NullProgressMonitor();
3102 WorkbenchMessages
.FilteredItemsSelectionDialog_cacheRefreshJob_getFilteredElements
,
3104 if (filters
!= null) {
3105 ticks
/= (filters
.size() + 2);
3110 // get already sorted array
3111 Object
[] filteredElements
= getSortedItems();
3113 monitor
.worked(ticks
);
3115 // filter the elements using provided ViewerFilters
3116 if (filters
!= null && filteredElements
!= null) {
3117 for (Iterator iter
= filters
.iterator(); iter
.hasNext();) {
3118 ViewerFilter f
= (ViewerFilter
) iter
.next();
3119 filteredElements
= f
.filter(getList(), parent
, filteredElements
);
3120 monitor
.worked(ticks
);
3124 if (filteredElements
== null || monitor
.isCanceled()) {
3126 return new Object
[0];
3129 ArrayList preparedElements
= new ArrayList();
3130 boolean hasHistory
= false;
3132 if (filteredElements
.length
> 0) {
3133 if (isHistoryElement(filteredElements
[0])) {
3138 int reportEvery
= filteredElements
.length
/ ticks
;
3141 for (int i
= 0; i
< filteredElements
.length
; i
++) {
3142 Object item
= filteredElements
[i
];
3144 if (hasHistory
&& !isHistoryElement(item
)) {
3145 preparedElements
.add(itemsListSeparator
);
3149 preparedElements
.add(item
);
3151 if (reportEvery
!= 0 && ((i
+ 1) % reportEvery
== 0)) {
3158 return preparedElements
.toArray();
3162 * Adds a filter to this content provider. For an example usage of such
3163 * filters look at the project <code>org.eclipse.ui.ide</code>, class
3164 * <code>org.eclipse.ui.dialogs.FilteredResourcesSelectionDialog.CustomWorkingSetFilter</code>.
3168 * the filter to be added
3170 public void addFilter(ViewerFilter filter
) {
3171 if (filters
== null) {
3172 filters
= new ArrayList();
3174 filters
.add(filter
);
3175 // currently filters are only added when dialog is restored
3176 // if it is changed, refreshing the whole TableViewer should be
3183 * A content provider that does nothing.
3185 private class NullContentProvider
implements IContentProvider
{
3190 * @see org.eclipse.jface.viewers.IContentProvider#dispose()
3193 public void dispose() {
3199 * @see org.eclipse.jface.viewers.IContentProvider#inputChanged(org.eclipse.jface.viewers.Viewer,
3200 * java.lang.Object, java.lang.Object)
3203 public void inputChanged(Viewer viewer
, Object oldInput
, Object newInput
) {
3209 * DetailsContentViewer objects are wrappers for labels.
3210 * DetailsContentViewer provides means to change label's image and text when
3211 * the attached LabelProvider is updated.
3213 private class DetailsContentViewer
extends ContentViewer
{
3215 private final CLabel label
;
3218 * Unfortunately, it was impossible to delegate displaying border to
3219 * label. The <code>ViewForm</code> is used because
3220 * <code>CLabel</code> displays shadow when border is present.
3222 private final ViewForm viewForm
;
3225 * Constructs a new instance of this class given its parent and a style
3226 * value describing its behavior and appearance.
3229 * the parent component
3233 public DetailsContentViewer(Composite parent
, int style
) {
3234 viewForm
= new ViewForm(parent
, style
);
3235 GridData gd
= new GridData(GridData
.FILL_HORIZONTAL
);
3236 gd
.horizontalSpan
= 2;
3237 viewForm
.setLayoutData(gd
);
3238 label
= new CLabel(viewForm
, SWT
.FLAT
);
3239 label
.setFont(parent
.getFont());
3240 viewForm
.setContent(label
);
3245 * Shows/hides the content viewer.
3248 * if the content viewer should be visible.
3250 public void setVisible(boolean visible
) {
3251 GridData gd
= (GridData
) viewForm
.getLayoutData();
3252 gd
.exclude
= !visible
;
3253 viewForm
.getParent().layout();
3259 * @see org.eclipse.jface.viewers.Viewer#inputChanged(java.lang.Object,
3263 protected void inputChanged(Object input
, Object oldInput
) {
3264 if (oldInput
== null) {
3265 if (input
== null) {
3279 * @see org.eclipse.jface.viewers.ContentViewer#handleLabelProviderChanged(org.eclipse.jface.viewers.LabelProviderChangedEvent)
3282 protected void handleLabelProviderChanged(
3283 LabelProviderChangedEvent event
) {
3284 if (event
!= null) {
3285 refresh(event
.getElements());
3292 * @see org.eclipse.jface.viewers.Viewer#getControl()
3295 public Control
getControl() {
3302 * @see org.eclipse.jface.viewers.Viewer#getSelection()
3305 public ISelection
getSelection() {
3313 * @see org.eclipse.jface.viewers.Viewer#refresh()
3316 public void refresh() {
3317 Object input
= this.getInput();
3318 if (input
!= null) {
3319 ILabelProvider labelProvider
= (ILabelProvider
) getLabelProvider();
3320 doRefresh(labelProvider
.getText(input
), labelProvider
3323 doRefresh(null, null);
3328 * Sets the given text and image to the label.
3331 * the new text or null
3335 private void doRefresh(String text
, Image image
) {
3336 if ( text
!= null ) {
3337 text
= LegacyActionTools
.escapeMnemonics(text
);
3339 label
.setText(text
);
3340 label
.setImage(image
);
3346 * @see org.eclipse.jface.viewers.Viewer#setSelection(org.eclipse.jface.viewers.ISelection,
3350 public void setSelection(ISelection selection
, boolean reveal
) {
3355 * Refreshes the label if currently chosen element is on the list.
3358 * list of changed object
3360 private void refresh(Object
[] objs
) {
3361 if (objs
== null || getInput() == null) {
3364 Object input
= getInput();
3365 for (int i
= 0; i
< objs
.length
; i
++) {
3366 if (objs
[i
].equals(input
)) {
3375 * Compares items according to the history.
3377 private class HistoryComparator
implements Comparator
{
3382 * @see java.util.Comparator#compare(java.lang.Object, java.lang.Object)
3385 public int compare(Object o1
, Object o2
) {
3386 boolean h1
= isHistoryElement(o1
);
3387 boolean h2
= isHistoryElement(o2
);
3389 return getItemsComparator().compare(o1
, o2
);
3406 * Get the control where the search pattern is entered. Any filtering should
3407 * be done using an {@link ItemsFilter}. This control should only be
3408 * accessed for listeners that wish to handle events that do not affect
3409 * filtering such as custom traversal.
3411 * @return Control or <code>null</code> if the pattern control has not
3414 public Control
getPatternControl() {
3419 * CDM IMPLEMENTATION
3420 * we have to call the initModel() method in the applyFilter method, so we had to add this abstract method (moved from AbstractFilteredCdmResourceSelectionDialog)
3423 abstract protected void initModel();
3425 public Object
[] getCurrentSelection() {
3426 return currentSelection
;
3429 public void setCurrentSelection(Object
[] currentSelection
) {
3430 this.currentSelection
= currentSelection
;
3433 public TableViewer
getList() {
3437 public void setList(TableViewer list
) {