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
;
130 * This is a class copied from FilteredItemsSelectionDialog and adapted to the Cdm use case. The original
131 * dialog gets all items when opening the dialog and then apply the filter on all items.
132 * In our case we need a possibility to filter the items already when getting them from the DB, so we adapt the
134 * the abstract method initMOdel was added and the method applyFilter was modified
135 * original description:
136 * Shows a list of items to the user with a text entry field for a string
137 * pattern used to filter the list of items.
142 public abstract class CdmFilteredItemsSelectionDialog
extends SelectionStatusDialog
{
146 private static final String DIALOG_BOUNDS_SETTINGS
= "DialogBoundsSettings"; //$NON-NLS-1$
148 private static final String SHOW_STATUS_LINE
= "ShowStatusLine"; //$NON-NLS-1$
150 private static final String HISTORY_SETTINGS
= "History"; //$NON-NLS-1$
152 private static final String DIALOG_HEIGHT
= "DIALOG_HEIGHT"; //$NON-NLS-1$
154 private static final String DIALOG_WIDTH
= "DIALOG_WIDTH"; //$NON-NLS-1$
157 * Represents an empty selection in the pattern input field (used only for
160 public static final int NONE
= 0;
163 * Pattern input field selection where caret is at the beginning (used only
164 * for initial pattern).
166 public static final int CARET_BEGINNING
= 1;
169 * Represents a full selection in the pattern input field (used only for
172 public static final int FULL_SELECTION
= 2;
174 private Text pattern
;
176 private TableViewer list
;
178 private DetailsContentViewer details
;
181 * It is a duplicate of a field in the CLabel class in DetailsContentViewer.
182 * It is maintained, because the <code>setDetailsLabelProvider()</code>
183 * could be called before content area is created.
185 private ILabelProvider detailsLabelProvider
;
187 private ItemsListLabelProvider itemsListLabelProvider
;
189 private MenuManager menuManager
;
191 private MenuManager contextMenuManager
;
193 private final boolean multi
;
195 private ToolBar toolBar
;
197 private ToolItem toolItem
;
199 private Label progressLabel
;
201 private ToggleStatusLineAction toggleStatusLineAction
;
203 private RemoveHistoryItemAction removeHistoryItemAction
;
205 private ActionContributionItem removeHistoryActionContributionItem
;
207 private IStatus status
;
209 private final RefreshCacheJob refreshCacheJob
;
211 private final RefreshProgressMessageJob refreshProgressMessageJob
= new RefreshProgressMessageJob();
213 private Object
[] currentSelection
;
215 private final ContentProvider contentProvider
;
217 private final FilterHistoryJob filterHistoryJob
;
219 private final FilterJob filterJob
;
221 private ItemsFilter filter
;
223 private List lastCompletedResult
;
225 private ItemsFilter lastCompletedFilter
;
227 private String initialPatternText
;
229 private int selectionMode
;
231 private ItemsListSeparator itemsListSeparator
;
233 private static final String EMPTY_STRING
= ""; //$NON-NLS-1$
235 private boolean refreshWithLastSelection
= false;
237 private IHandlerActivation showViewHandler
;
240 * Creates a new instance of the class.
243 * shell to parent the dialog on
245 * indicates whether dialog allows to select more than one
246 * position in its list of items
248 public CdmFilteredItemsSelectionDialog(Shell shell
, boolean multi
) {
251 filterHistoryJob
= new FilterHistoryJob();
252 filterJob
= new FilterJob();
253 contentProvider
= new ContentProvider();
254 refreshCacheJob
= new RefreshCacheJob();
255 itemsListSeparator
= new ItemsListSeparator(
256 WorkbenchMessages
.FilteredItemsSelectionDialog_separatorLabel
);
257 selectionMode
= NONE
;
261 * Creates a new instance of the class. Created dialog won't allow to select
262 * more than one item.
265 * shell to parent the dialog on
267 public CdmFilteredItemsSelectionDialog(Shell shell
) {
272 * Adds viewer filter to the dialog items list.
277 protected void addListFilter(ViewerFilter filter
) {
278 contentProvider
.addFilter(filter
);
282 * Sets a new label provider for items in the list. If the label provider
283 * also implements {@link
284 * org.eclipse.jface.viewers.DelegatingStyledCellLabelProvider
285 * .IStyledLabelProvider}, the style text labels provided by it will be used
286 * provided that the corresponding preference is set.
288 * @see IWorkbenchPreferenceConstants#USE_COLORED_LABELS
290 * @param listLabelProvider
291 * the label provider for items in the list
293 public void setListLabelProvider(ILabelProvider listLabelProvider
) {
294 getItemsListLabelProvider().setProvider(listLabelProvider
);
298 * Returns the label decorator for selected items in the list.
300 * @return the label decorator for selected items in the list
302 private ILabelDecorator
getListSelectionLabelDecorator() {
303 return getItemsListLabelProvider().getSelectionDecorator();
307 * Sets the label decorator for selected items in the list.
309 * @param listSelectionLabelDecorator
310 * the label decorator for selected items in the list
312 public void setListSelectionLabelDecorator(
313 ILabelDecorator listSelectionLabelDecorator
) {
314 getItemsListLabelProvider().setSelectionDecorator(
315 listSelectionLabelDecorator
);
319 * Returns the item list label provider.
321 * @return the item list label provider
323 private ItemsListLabelProvider
getItemsListLabelProvider() {
324 if (itemsListLabelProvider
== null) {
325 itemsListLabelProvider
= new ItemsListLabelProvider(
326 new LabelProvider(), null);
328 return itemsListLabelProvider
;
332 * Sets label provider for the details field.
334 * For a single selection, the element sent to
335 * {@link ILabelProvider#getImage(Object)} and
336 * {@link ILabelProvider#getText(Object)} is the selected object, for
337 * multiple selection a {@link String} with amount of selected items is the
340 * @see #getSelectedItems() getSelectedItems() can be used to retrieve
341 * selected items and get the items count.
343 * @param detailsLabelProvider
344 * the label provider for the details field
346 public void setDetailsLabelProvider(ILabelProvider detailsLabelProvider
) {
347 this.detailsLabelProvider
= detailsLabelProvider
;
348 if (details
!= null) {
349 details
.setLabelProvider(detailsLabelProvider
);
353 private ILabelProvider
getDetailsLabelProvider() {
354 if (detailsLabelProvider
== null) {
355 detailsLabelProvider
= new LabelProvider();
357 return detailsLabelProvider
;
363 * @see org.eclipse.jface.window.Window#create()
366 public void create() {
372 * Restores dialog using persisted settings. The default implementation
373 * restores the status of the details line and the selection history.
376 * settings used to restore dialog
378 protected void restoreDialog(IDialogSettings settings
) {
379 boolean toggleStatusLine
= true;
381 if (settings
.get(SHOW_STATUS_LINE
) != null) {
382 toggleStatusLine
= settings
.getBoolean(SHOW_STATUS_LINE
);
385 toggleStatusLineAction
.setChecked(toggleStatusLine
);
387 details
.setVisible(toggleStatusLine
);
389 String setting
= settings
.get(HISTORY_SETTINGS
);
390 if (setting
!= null) {
392 IMemento memento
= XMLMemento
.createReadRoot(new StringReader(
394 this.contentProvider
.loadHistory(memento
);
395 } catch (WorkbenchException e
) {
396 // Simply don't restore the settings
402 PlatformUI
.PLUGIN_ID
,
404 WorkbenchMessages
.FilteredItemsSelectionDialog_restoreError
,
413 * @see org.eclipse.jface.window.Window#close()
416 public boolean close() {
417 this.filterJob
.cancel();
418 this.refreshCacheJob
.cancel();
419 this.refreshProgressMessageJob
.cancel();
420 if (showViewHandler
!= null) {
421 IHandlerService service
= (IHandlerService
) PlatformUI
422 .getWorkbench().getService(IHandlerService
.class);
423 service
.deactivateHandler(showViewHandler
);
424 showViewHandler
.getHandler().dispose();
425 showViewHandler
= null;
427 if (menuManager
!= null) {
428 menuManager
.dispose();
430 if (contextMenuManager
!= null) {
431 contextMenuManager
.dispose();
433 storeDialog(getDialogSettings());
434 return super.close();
438 * Stores dialog settings.
441 * settings used to store dialog
443 protected void storeDialog(IDialogSettings settings
) {
444 settings
.put(SHOW_STATUS_LINE
, toggleStatusLineAction
.isChecked());
446 XMLMemento memento
= XMLMemento
.createWriteRoot(HISTORY_SETTINGS
);
447 this.contentProvider
.saveHistory(memento
);
448 StringWriter writer
= new StringWriter();
450 memento
.save(writer
);
451 settings
.put(HISTORY_SETTINGS
, writer
.getBuffer().toString());
452 } catch (IOException e
) {
453 // Simply don't store the settings
459 PlatformUI
.PLUGIN_ID
,
461 WorkbenchMessages
.FilteredItemsSelectionDialog_storeError
,
467 * Create a new header which is labelled by headerLabel.
470 * @return Label the label of the header
472 private Label
createHeader(Composite parent
) {
473 Composite header
= new Composite(parent
, SWT
.NONE
);
475 GridLayout layout
= new GridLayout();
476 layout
.numColumns
= 2;
477 layout
.marginWidth
= 0;
478 layout
.marginHeight
= 0;
479 header
.setLayout(layout
);
481 Label headerLabel
= new Label(header
, SWT
.NONE
);
482 headerLabel
.setText((getMessage() != null && getMessage().trim()
483 .length() > 0) ?
getMessage()
484 : WorkbenchMessages
.FilteredItemsSelectionDialog_patternLabel
);
485 headerLabel
.addTraverseListener(new TraverseListener() {
487 public void keyTraversed(TraverseEvent e
) {
488 if (e
.detail
== SWT
.TRAVERSE_MNEMONIC
&& e
.doit
) {
489 e
.detail
= SWT
.TRAVERSE_NONE
;
495 GridData gd
= new GridData(GridData
.FILL_HORIZONTAL
);
496 headerLabel
.setLayoutData(gd
);
498 createViewMenu(header
);
499 header
.setLayoutData(gd
);
504 * Create the labels for the list and the progress. Return the list label.
509 private Label
createLabels(Composite parent
) {
510 Composite labels
= new Composite(parent
, SWT
.NONE
);
512 GridLayout layout
= new GridLayout();
513 layout
.numColumns
= 2;
514 layout
.marginWidth
= 0;
515 layout
.marginHeight
= 0;
516 labels
.setLayout(layout
);
518 Label listLabel
= new Label(labels
, SWT
.NONE
);
520 .setText(WorkbenchMessages
.FilteredItemsSelectionDialog_listLabel
);
522 listLabel
.addTraverseListener(new TraverseListener() {
524 public void keyTraversed(TraverseEvent e
) {
525 if (e
.detail
== SWT
.TRAVERSE_MNEMONIC
&& e
.doit
) {
526 e
.detail
= SWT
.TRAVERSE_NONE
;
527 list
.getTable().setFocus();
532 GridData gd
= new GridData(GridData
.FILL_HORIZONTAL
);
533 listLabel
.setLayoutData(gd
);
535 progressLabel
= new Label(labels
, SWT
.RIGHT
);
536 progressLabel
.setLayoutData(gd
);
538 labels
.setLayoutData(gd
);
542 private void createViewMenu(Composite parent
) {
543 toolBar
= new ToolBar(parent
, SWT
.FLAT
);
544 toolItem
= new ToolItem(toolBar
, SWT
.PUSH
, 0);
546 GridData data
= new GridData();
547 data
.horizontalAlignment
= GridData
.END
;
548 toolBar
.setLayoutData(data
);
550 toolBar
.addMouseListener(new MouseAdapter() {
552 public void mouseDown(MouseEvent e
) {
557 toolItem
.setImage(WorkbenchImages
558 .getImage(IWorkbenchGraphicConstants
.IMG_LCL_VIEW_MENU
));
560 .setToolTipText(WorkbenchMessages
.FilteredItemsSelectionDialog_menu
);
561 toolItem
.addSelectionListener(new SelectionAdapter() {
563 public void widgetSelected(SelectionEvent e
) {
568 menuManager
= new MenuManager();
570 fillViewMenu(menuManager
);
572 IHandlerService service
= (IHandlerService
) PlatformUI
.getWorkbench()
573 .getService(IHandlerService
.class);
574 IHandler handler
= new AbstractHandler() {
576 public Object
execute(ExecutionEvent event
) {
581 showViewHandler
= service
.activateHandler(
582 IWorkbenchCommandConstants
.WINDOW_SHOW_VIEW_MENU
, handler
,
583 new ActiveShellExpression(getShell()));
587 * Fills the menu of the dialog.
592 protected void fillViewMenu(IMenuManager menuManager
) {
593 toggleStatusLineAction
= new ToggleStatusLineAction();
594 menuManager
.add(toggleStatusLineAction
);
597 private void showViewMenu() {
598 Menu menu
= menuManager
.createContextMenu(getShell());
599 Rectangle bounds
= toolItem
.getBounds();
600 Point topLeft
= new Point(bounds
.x
, bounds
.y
+ bounds
.height
);
601 topLeft
= toolBar
.toDisplay(topLeft
);
602 menu
.setLocation(topLeft
.x
, topLeft
.y
);
603 menu
.setVisible(true);
607 * Hook that allows to add actions to the context menu.
609 * Subclasses may extend in order to add other actions.</p>
611 * @param menuManager the context menu manager
614 protected void fillContextMenu(IMenuManager menuManager
) {
615 List selectedElements
= ((StructuredSelection
)list
.getSelection()).toList();
619 for (Iterator it
= selectedElements
.iterator(); it
.hasNext();) {
621 if (item
instanceof ItemsListSeparator
|| !isHistoryElement(item
)) {
626 if (selectedElements
.size() > 0) {
627 removeHistoryItemAction
.setText(WorkbenchMessages
.FilteredItemsSelectionDialog_removeItemsFromHistoryAction
);
629 menuManager
.add(removeHistoryActionContributionItem
);
634 private void createPopupMenu() {
635 removeHistoryItemAction
= new RemoveHistoryItemAction();
636 removeHistoryActionContributionItem
= new ActionContributionItem(
637 removeHistoryItemAction
);
639 contextMenuManager
= new MenuManager();
640 contextMenuManager
.setRemoveAllWhenShown(true);
641 contextMenuManager
.addMenuListener(new IMenuListener() {
643 public void menuAboutToShow(IMenuManager manager
) {
644 fillContextMenu(manager
);
648 final Table table
= list
.getTable();
649 Menu menu
= contextMenuManager
.createContextMenu(table
);
654 * Creates an extra content area, which will be located above the details.
657 * parent to create the dialog widgets in
658 * @return an extra content area
660 protected abstract Control
createExtendedContentArea(Composite parent
);
665 * @see org.eclipse.jface.dialogs.Dialog#createDialogArea(org.eclipse.swt.widgets.Composite)
668 protected Control
createDialogArea(Composite parent
) {
669 Composite dialogArea
= (Composite
) super.createDialogArea(parent
);
671 Composite content
= new Composite(dialogArea
, SWT
.NONE
);
672 GridData gd
= new GridData(GridData
.FILL_BOTH
);
673 content
.setLayoutData(gd
);
675 GridLayout layout
= new GridLayout();
676 layout
.numColumns
= 1;
677 layout
.marginWidth
= 0;
678 layout
.marginHeight
= 0;
679 content
.setLayout(layout
);
681 final Label headerLabel
= createHeader(content
);
683 pattern
= new Text(content
, SWT
.SINGLE
| SWT
.BORDER
| SWT
.SEARCH
| SWT
.ICON_CANCEL
);
684 pattern
.getAccessible().addAccessibleListener(new AccessibleAdapter() {
686 public void getName(AccessibleEvent e
) {
687 e
.result
= LegacyActionTools
.removeMnemonics(headerLabel
691 gd
= new GridData(GridData
.FILL_HORIZONTAL
);
692 pattern
.setLayoutData(gd
);
694 final Label listLabel
= createLabels(content
);
696 list
= new TableViewer(content
, (multi ? SWT
.MULTI
: SWT
.SINGLE
)
697 | SWT
.BORDER
| SWT
.V_SCROLL
| SWT
.VIRTUAL
);
698 list
.getTable().getAccessible().addAccessibleListener(
699 new AccessibleAdapter() {
701 public void getName(AccessibleEvent e
) {
702 if (e
.childID
== ACC
.CHILDID_SELF
) {
703 e
.result
= LegacyActionTools
704 .removeMnemonics(listLabel
.getText());
708 list
.setContentProvider(contentProvider
);
709 list
.setLabelProvider(getItemsListLabelProvider());
710 list
.setInput(new Object
[0]);
711 list
.setItemCount(contentProvider
.getNumberOfElements());
712 gd
= new GridData(GridData
.FILL_BOTH
);
713 applyDialogFont(list
.getTable());
714 gd
.heightHint
= list
.getTable().getItemHeight() * 15;
715 list
.getTable().setLayoutData(gd
);
719 pattern
.addModifyListener(new ModifyListener() {
721 public void modifyText(ModifyEvent e
) {
726 pattern
.addKeyListener(new KeyAdapter() {
728 public void keyPressed(KeyEvent e
) {
729 if (e
.keyCode
== SWT
.ARROW_DOWN
) {
730 if (list
.getTable().getItemCount() > 0) {
731 list
.getTable().setFocus();
737 list
.addSelectionChangedListener(new ISelectionChangedListener() {
739 public void selectionChanged(SelectionChangedEvent event
) {
740 StructuredSelection selection
= (StructuredSelection
) event
742 handleSelected(selection
);
746 list
.addDoubleClickListener(new IDoubleClickListener() {
748 public void doubleClick(DoubleClickEvent event
) {
753 list
.getTable().addKeyListener(new KeyAdapter() {
755 public void keyPressed(KeyEvent e
) {
757 if (e
.keyCode
== SWT
.DEL
) {
759 List selectedElements
= ((StructuredSelection
) list
760 .getSelection()).toList();
763 boolean isSelectedHistory
= true;
765 for (Iterator it
= selectedElements
.iterator(); it
768 if (item
instanceof ItemsListSeparator
769 || !isHistoryElement(item
)) {
770 isSelectedHistory
= false;
774 if (isSelectedHistory
) {
775 removeSelectedItems(selectedElements
);
780 if (e
.keyCode
== SWT
.ARROW_UP
&& (e
.stateMask
& SWT
.SHIFT
) != 0
781 && (e
.stateMask
& SWT
.CTRL
) != 0) {
782 StructuredSelection selection
= (StructuredSelection
) list
785 if (selection
.size() == 1) {
786 Object element
= selection
.getFirstElement();
787 if (element
.equals(list
.getElementAt(0))) {
790 if (list
.getElementAt(list
.getTable()
791 .getSelectionIndex() - 1) instanceof ItemsListSeparator
) {
792 list
.getTable().setSelection(
793 list
.getTable().getSelectionIndex() - 1);
795 list
.getTable().notifyListeners(SWT
.Selection
,
801 if (e
.keyCode
== SWT
.ARROW_DOWN
802 && (e
.stateMask
& SWT
.SHIFT
) != 0
803 && (e
.stateMask
& SWT
.CTRL
) != 0) {
806 .getElementAt(list
.getTable().getSelectionIndex() + 1) instanceof ItemsListSeparator
) {
807 list
.getTable().setSelection(
808 list
.getTable().getSelectionIndex() + 1);
810 list
.getTable().notifyListeners(SWT
.Selection
, new Event());
816 createExtendedContentArea(content
);
818 details
= new DetailsContentViewer(content
, SWT
.BORDER
| SWT
.FLAT
);
819 details
.setVisible(toggleStatusLineAction
.isChecked());
820 details
.setContentProvider(new NullContentProvider());
821 details
.setLabelProvider(getDetailsLabelProvider());
823 applyDialogFont(content
);
825 restoreDialog(getDialogSettings());
827 if (initialPatternText
!= null) {
828 pattern
.setText(initialPatternText
);
831 switch (selectionMode
) {
832 case CARET_BEGINNING
:
833 pattern
.setSelection(0, 0);
836 pattern
.setSelection(0, initialPatternText
.length());
840 // apply filter even if pattern is empty (display history)
847 * This method is a hook for subclasses to override default dialog behavior.
848 * The <code>handleDoubleClick()</code> method handles double clicks on
849 * the list of filtered elements.
851 * Current implementation makes double-clicking on the list do the same as
852 * pressing <code>OK</code> button on the dialog.
854 protected void handleDoubleClick() {
859 * Refreshes the details field according to the current selection in the
862 private void refreshDetails() {
863 StructuredSelection selection
= getSelectedItems();
865 switch (selection
.size()) {
867 details
.setInput(null);
870 details
.setInput(selection
.getFirstElement());
876 WorkbenchMessages
.FilteredItemsSelectionDialog_nItemsSelected
,
877 new Integer(selection
.size())));
884 * Handle selection in the items list by updating labels of selected and
885 * unselected items and refresh the details field using the selection.
890 protected void handleSelected(StructuredSelection selection
) {
891 IStatus status
= new Status(IStatus
.OK
, PlatformUI
.PLUGIN_ID
,
892 IStatus
.OK
, EMPTY_STRING
, null);
894 Object
[] lastSelection
= currentSelection
;
896 currentSelection
= selection
.toArray();
898 if (selection
.size() == 0) {
899 status
= new Status(IStatus
.ERROR
, PlatformUI
.PLUGIN_ID
,
900 IStatus
.ERROR
, EMPTY_STRING
, null);
902 if (lastSelection
!= null
903 && getListSelectionLabelDecorator() != null) {
904 list
.update(lastSelection
, null);
907 currentSelection
= null;
910 status
= new Status(IStatus
.ERROR
, PlatformUI
.PLUGIN_ID
,
911 IStatus
.ERROR
, EMPTY_STRING
, null);
913 List items
= selection
.toList();
916 IStatus tempStatus
= null;
918 for (Iterator it
= items
.iterator(); it
.hasNext();) {
919 Object o
= it
.next();
921 if (o
instanceof ItemsListSeparator
) {
926 tempStatus
= validateItem(item
);
928 if (tempStatus
.isOK()) {
929 status
= new Status(IStatus
.OK
, PlatformUI
.PLUGIN_ID
,
930 IStatus
.OK
, EMPTY_STRING
, null);
933 // if any selected element is not valid status is set to
939 if (lastSelection
!= null
940 && getListSelectionLabelDecorator() != null) {
941 list
.update(lastSelection
, null);
944 if (getListSelectionLabelDecorator() != null) {
945 list
.update(currentSelection
, null);
950 updateStatus(status
);
956 * @see org.eclipse.jface.window.Dialog#getDialogBoundsSettings()
959 protected IDialogSettings
getDialogBoundsSettings() {
960 IDialogSettings settings
= getDialogSettings();
961 IDialogSettings section
= settings
.getSection(DIALOG_BOUNDS_SETTINGS
);
962 if (section
== null) {
963 section
= settings
.addNewSection(DIALOG_BOUNDS_SETTINGS
);
964 section
.put(DIALOG_HEIGHT
, 500);
965 section
.put(DIALOG_WIDTH
, 600);
971 * Returns the dialog settings. Returned object can't be null.
973 * @return return dialog settings for this dialog
975 protected abstract IDialogSettings
getDialogSettings();
978 * Refreshes the dialog - has to be called in UI thread.
980 public void refresh() {
981 if (list
!= null && !list
.getTable().isDisposed()) {
983 List lastRefreshSelection
= ((StructuredSelection
) list
984 .getSelection()).toList();
985 list
.getTable().deselectAll();
987 list
.setItemCount(contentProvider
.getNumberOfElements());
990 if (list
.getTable().getItemCount() > 0) {
991 // preserve previous selection
992 if (refreshWithLastSelection
&& lastRefreshSelection
!= null
993 && lastRefreshSelection
.size() > 0) {
994 list
.setSelection(new StructuredSelection(
995 lastRefreshSelection
));
997 refreshWithLastSelection
= true;
998 list
.getTable().setSelection(0);
999 list
.getTable().notifyListeners(SWT
.Selection
, new Event());
1002 list
.setSelection(StructuredSelection
.EMPTY
);
1007 scheduleProgressMessageRefresh();
1011 * Updates the progress label.
1016 public void updateProgressLabel() {
1017 scheduleProgressMessageRefresh();
1021 * Notifies the content provider - fires filtering of content provider
1022 * elements. During the filtering, a separator between history and workspace
1025 * This is a long running operation and should be called in a job.
1027 * @param checkDuplicates
1028 * <code>true</code> if data concerning elements duplication
1029 * should be computed - it takes much more time than the standard
1032 * a progress monitor or <code>null</code> if no monitor is
1035 public void reloadCache(boolean checkDuplicates
, IProgressMonitor monitor
) {
1036 if (list
!= null && !list
.getTable().isDisposed()
1037 && contentProvider
!= null) {
1038 contentProvider
.reloadCache(checkDuplicates
, monitor
);
1043 * Schedule refresh job.
1045 public void scheduleRefresh() {
1046 refreshCacheJob
.cancelAll();
1047 refreshCacheJob
.schedule();
1051 * Schedules progress message refresh.
1053 public void scheduleProgressMessageRefresh() {
1054 if (filterJob
.getState() != Job
.RUNNING
1055 && refreshProgressMessageJob
.getState() != Job
.RUNNING
) {
1056 refreshProgressMessageJob
.scheduleProgressRefresh(null);
1063 * @see org.eclipse.ui.dialogs.SelectionStatusDialog#computeResult()
1066 protected void computeResult() {
1068 List selectedElements
= ((StructuredSelection
) list
.getSelection())
1071 List objectsToReturn
= new ArrayList();
1075 for (Iterator it
= selectedElements
.iterator(); it
.hasNext();) {
1078 if (!(item
instanceof ItemsListSeparator
)) {
1079 accessedHistoryItem(item
);
1080 objectsToReturn
.add(item
);
1084 setResult(objectsToReturn
);
1088 * @see org.eclipse.ui.dialogs.SelectionStatusDialog#updateStatus(org.eclipse.core.runtime.IStatus)
1091 protected void updateStatus(IStatus status
) {
1092 this.status
= status
;
1093 super.updateStatus(status
);
1097 * @see Dialog#okPressed()
1100 protected void okPressed() {
1102 && (status
.isOK() || status
.getCode() == IStatus
.INFO
)) {
1108 * Sets the initial pattern used by the filter. This text is copied into the
1109 * selection input on the dialog. A full selection is used in the pattern
1113 * initial pattern for the filter
1114 * @see FilteredItemsSelectionDialog#FULL_SELECTION
1116 public void setInitialPattern(String text
) {
1117 setInitialPattern(text
, FULL_SELECTION
);
1121 * Sets the initial pattern used by the filter. This text is copied into the
1122 * selection input on the dialog. The <code>selectionMode</code> is used
1123 * to choose selection type for the input field.
1126 * initial pattern for the filter
1127 * @param selectionMode
1128 * one of: {@link FilteredItemsSelectionDialog#NONE},
1129 * {@link FilteredItemsSelectionDialog#CARET_BEGINNING},
1130 * {@link FilteredItemsSelectionDialog#FULL_SELECTION}
1132 public void setInitialPattern(String text
, int selectionMode
) {
1133 this.initialPatternText
= text
;
1134 this.selectionMode
= selectionMode
;
1138 * Gets initial pattern.
1140 * @return initial pattern, or <code>null</code> if initial pattern is not
1143 protected String
getInitialPattern() {
1144 return this.initialPatternText
;
1148 * Returns the current selection.
1150 * @return the current selection
1152 protected StructuredSelection
getSelectedItems() {
1154 StructuredSelection selection
= (StructuredSelection
) list
1157 List selectedItems
= selection
.toList();
1158 Object itemToRemove
= null;
1160 for (Iterator it
= selection
.iterator(); it
.hasNext();) {
1161 Object item
= it
.next();
1162 if (item
instanceof ItemsListSeparator
) {
1163 itemToRemove
= item
;
1168 if (itemToRemove
== null) {
1169 return new StructuredSelection(selectedItems
);
1171 // Create a new selection without the collision
1172 List newItems
= new ArrayList(selectedItems
);
1173 newItems
.remove(itemToRemove
);
1174 return new StructuredSelection(newItems
);
1179 * Validates the item. When items on the items list are selected or
1180 * deselected, it validates each item in the selection and the dialog status
1181 * depends on all validations.
1184 * an item to be checked
1185 * @return status of the dialog to be set
1187 protected abstract IStatus
validateItem(Object item
);
1190 * Creates an instance of a filter.
1192 * @return a filter for items on the items list. Can be <code>null</code>,
1193 * no filtering will be applied then, causing no item to be shown in
1196 protected abstract ItemsFilter
createFilter();
1200 * Applies the filter created by <code>createFilter()</code> method to the
1201 * items list. When new filter is different than previous one it will cause
1204 protected void applyFilter() {
1205 // to get an already filtered selection of the database we added the initModel() method here.
1207 ItemsFilter newFilter
= createFilter();
1209 // don't apply filtering for patterns which mean the same, for example:
1211 if (filter
!= null && filter
.equalsFilter(newFilter
)) {
1215 filterHistoryJob
.cancel();
1218 this.filter
= newFilter
;
1220 if (this.filter
!= null) {
1221 filterHistoryJob
.schedule();
1226 * Returns comparator to sort items inside content provider. Returned object
1227 * will be probably created as an anonymous class. Parameters passed to the
1228 * <code>compare(java.lang.Object, java.lang.Object)</code> are going to
1229 * be the same type as the one used in the content provider.
1231 * @return comparator to sort items content provider
1233 protected abstract Comparator
getItemsComparator();
1236 * Fills the content provider with matching items.
1238 * @param contentProvider
1239 * collector to add items to.
1240 * {@link FilteredItemsSelectionDialog.AbstractContentProvider#add(Object, FilteredItemsSelectionDialog.ItemsFilter)}
1241 * only adds items that pass the given <code>itemsFilter</code>.
1242 * @param itemsFilter
1244 * @param progressMonitor
1245 * must be used to report search progress. The state of this
1246 * progress monitor reflects the state of the filtering process.
1247 * @throws CoreException
1249 protected abstract void fillContentProvider(
1250 AbstractContentProvider contentProvider
, ItemsFilter itemsFilter
,
1251 IProgressMonitor progressMonitor
) throws CoreException
;
1254 * Removes selected items from history.
1257 * items to be removed
1259 private void removeSelectedItems(List items
) {
1260 for (Iterator iter
= items
.iterator(); iter
.hasNext();) {
1261 Object item
= iter
.next();
1262 removeHistoryItem(item
);
1264 refreshWithLastSelection
= false;
1265 contentProvider
.refresh();
1269 * Removes an item from history.
1273 * @return removed item
1275 protected Object
removeHistoryItem(Object item
) {
1276 return contentProvider
.removeHistoryElement(item
);
1280 * Adds item to history.
1283 * the item to be added
1285 protected void accessedHistoryItem(Object item
) {
1286 contentProvider
.addHistoryElement(item
);
1290 * Returns a history comparator.
1292 * @return decorated comparator
1294 private Comparator
getHistoryComparator() {
1295 return new HistoryComparator();
1299 * Returns the history of selected elements.
1301 * @return history of selected elements, or <code>null</code> if it is not
1304 protected SelectionHistory
getSelectionHistory() {
1305 return this.contentProvider
.getSelectionHistory();
1311 * @param selectionHistory
1314 protected void setSelectionHistory(SelectionHistory selectionHistory
) {
1315 if (this.contentProvider
!= null) {
1316 this.contentProvider
.setSelectionHistory(selectionHistory
);
1321 * Indicates whether the given item is a history item.
1324 * the item to be investigated
1325 * @return <code>true</code> if the given item exists in history,
1326 * <code>false</code> otherwise
1328 public boolean isHistoryElement(Object item
) {
1329 return this.contentProvider
.isHistoryElement(item
);
1333 * Indicates whether the given item is a duplicate.
1336 * the item to be investigated
1337 * @return <code>true</code> if the item is duplicate, <code>false</code>
1340 public boolean isDuplicateElement(Object item
) {
1341 return this.contentProvider
.isDuplicateElement(item
);
1345 * Sets separator label
1347 * @param separatorLabel
1348 * the label showed on separator
1350 public void setSeparatorLabel(String separatorLabel
) {
1351 this.itemsListSeparator
= new ItemsListSeparator(separatorLabel
);
1355 * Returns name for then given object.
1358 * an object from the content provider. Subclasses should pay
1359 * attention to the passed argument. They should either only pass
1360 * objects of a known type (one used in content provider) or make
1361 * sure that passed parameter is the expected one (by type
1362 * checking like <code>instanceof</code> inside the method).
1363 * @return name of the given item
1365 public abstract String
getElementName(Object item
);
1367 private class ToggleStatusLineAction
extends Action
{
1370 * Creates a new instance of the class.
1372 public ToggleStatusLineAction() {
1374 WorkbenchMessages
.FilteredItemsSelectionDialog_toggleStatusAction
,
1375 IAction
.AS_CHECK_BOX
);
1380 details
.setVisible(isChecked());
1385 * Only refreshes UI on the basis of an already sorted and filtered set of
1388 * Standard invocation scenario:
1390 * <li>filtering job (<code>FilterJob</code> class extending
1391 * <code>Job</code> class)</li>
1392 * <li>cache refresh without checking for duplicates (<code>RefreshCacheJob</code>
1393 * class extending <code>Job</code> class)</li>
1394 * <li>UI refresh (<code>RefreshJob</code> class extending
1395 * <code>UIJob</code> class)</li>
1396 * <li>cache refresh with checking for duplicates (<cod>CacheRefreshJob</code>
1397 * class extending <code>Job</code> class)</li>
1398 * <li>UI refresh (<code>RefreshJob</code> class extending <code>UIJob</code>
1401 * The scenario is rather complicated, but it had to be applied, because:
1403 * <li> refreshing cache is rather a long action and cannot be run in the UI -
1404 * cannot be run in a UIJob</li>
1405 * <li> refreshing cache checking for duplicates is twice as long as
1406 * refreshing cache without checking for duplicates; results of the search
1407 * could be displayed earlier</li>
1408 * <li> refreshing the UI have to be run in a UIJob</li>
1411 * @see org.eclipse.ui.dialogs.FilteredItemsSelectionDialog.FilterJob
1412 * @see org.eclipse.ui.dialogs.FilteredItemsSelectionDialog.RefreshJob
1413 * @see org.eclipse.ui.dialogs.FilteredItemsSelectionDialog.RefreshCacheJob
1415 private class RefreshJob
extends UIJob
{
1418 * Creates a new instance of the class.
1420 public RefreshJob() {
1421 super(CdmFilteredItemsSelectionDialog
.this.getParentShell()
1423 WorkbenchMessages
.FilteredItemsSelectionDialog_refreshJob
);
1430 * @see org.eclipse.ui.progress.UIJob#runInUIThread(org.eclipse.core.runtime.IProgressMonitor)
1433 public IStatus
runInUIThread(IProgressMonitor monitor
) {
1434 if (monitor
.isCanceled()) {
1435 return new Status(IStatus
.OK
, WorkbenchPlugin
.PI_WORKBENCH
,
1436 IStatus
.OK
, EMPTY_STRING
, null);
1439 if (CdmFilteredItemsSelectionDialog
.this != null) {
1440 CdmFilteredItemsSelectionDialog
.this.refresh();
1443 return new Status(IStatus
.OK
, PlatformUI
.PLUGIN_ID
, IStatus
.OK
,
1444 EMPTY_STRING
, null);
1450 * Refreshes the progress message cyclically with 500 milliseconds delay.
1451 * <code>RefreshProgressMessageJob</code> is strictly connected with
1452 * <code>GranualProgressMonitor</code> and use it to to get progress
1453 * message and to decide about break of cyclical refresh.
1455 private class RefreshProgressMessageJob
extends UIJob
{
1457 private GranualProgressMonitor progressMonitor
;
1460 * Creates a new instance of the class.
1462 public RefreshProgressMessageJob() {
1464 CdmFilteredItemsSelectionDialog
.this.getParentShell()
1466 WorkbenchMessages
.FilteredItemsSelectionDialog_progressRefreshJob
);
1473 * @see org.eclipse.ui.progress.UIJob#runInUIThread(org.eclipse.core.runtime.IProgressMonitor)
1476 public IStatus
runInUIThread(IProgressMonitor monitor
) {
1478 if (!progressLabel
.isDisposed()) {
1479 progressLabel
.setText(progressMonitor
!= null ? progressMonitor
1480 .getMessage() : EMPTY_STRING
);
1483 if (progressMonitor
== null || progressMonitor
.isDone()) {
1484 return new Status(IStatus
.CANCEL
, PlatformUI
.PLUGIN_ID
,
1485 IStatus
.CANCEL
, EMPTY_STRING
, null);
1488 // Schedule cyclical with 500 milliseconds delay
1491 return new Status(IStatus
.OK
, PlatformUI
.PLUGIN_ID
, IStatus
.OK
,
1492 EMPTY_STRING
, null);
1496 * Schedule progress refresh job.
1498 * @param progressMonitor
1499 * used during refresh progress label
1501 public void scheduleProgressRefresh(
1502 GranualProgressMonitor progressMonitor
) {
1503 this.progressMonitor
= progressMonitor
;
1504 // Schedule with initial delay to avoid flickering when the user
1512 * A job responsible for computing filtered items list presented using
1513 * <code>RefreshJob</code>.
1515 * @see FilteredItemsSelectionDialog.RefreshJob
1518 private class RefreshCacheJob
extends Job
{
1520 private final RefreshJob refreshJob
= new RefreshJob();
1523 * Creates a new instance of the class.
1525 public RefreshCacheJob() {
1527 WorkbenchMessages
.FilteredItemsSelectionDialog_cacheRefreshJob
);
1532 * Stops the job and all sub-jobs.
1534 public void cancelAll() {
1536 refreshJob
.cancel();
1542 * @see org.eclipse.core.runtime.jobs.Job#run(org.eclipse.core.runtime.IProgressMonitor)
1545 protected IStatus
run(IProgressMonitor monitor
) {
1546 if (monitor
.isCanceled()) {
1547 return new Status(IStatus
.CANCEL
, WorkbenchPlugin
.PI_WORKBENCH
,
1548 IStatus
.CANCEL
, EMPTY_STRING
, null);
1551 if (CdmFilteredItemsSelectionDialog
.this != null) {
1552 GranualProgressMonitor wrappedMonitor
= new GranualProgressMonitor(
1554 CdmFilteredItemsSelectionDialog
.this.reloadCache(true,
1558 if (!monitor
.isCanceled()) {
1559 refreshJob
.schedule();
1562 return new Status(IStatus
.OK
, PlatformUI
.PLUGIN_ID
, IStatus
.OK
,
1563 EMPTY_STRING
, null);
1570 * @see org.eclipse.core.runtime.jobs.Job#canceling()
1573 protected void canceling() {
1575 contentProvider
.stopReloadingCache();
1580 private class RemoveHistoryItemAction
extends Action
{
1583 * Creates a new instance of the class.
1585 public RemoveHistoryItemAction() {
1587 WorkbenchMessages
.FilteredItemsSelectionDialog_removeItemsFromHistoryAction
);
1593 * @see org.eclipse.jface.action.Action#run()
1597 List selectedElements
= ((StructuredSelection
) list
.getSelection())
1599 removeSelectedItems(selectedElements
);
1603 private static boolean showColoredLabels() {
1604 return PlatformUI
.getPreferenceStore().getBoolean(IWorkbenchPreferenceConstants
.USE_COLORED_LABELS
);
1607 private class ItemsListLabelProvider
extends StyledCellLabelProvider
1608 implements ILabelProviderListener
{
1609 private ILabelProvider provider
;
1611 private ILabelDecorator selectionDecorator
;
1613 // Need to keep our own list of listeners
1614 private final ListenerList listeners
= new ListenerList();
1617 * Creates a new instance of the class.
1620 * the label provider for all items, not <code>null</code>
1621 * @param selectionDecorator
1622 * the decorator for selected items, can be <code>null</code>
1624 public ItemsListLabelProvider(ILabelProvider provider
,
1625 ILabelDecorator selectionDecorator
) {
1626 Assert
.isNotNull(provider
);
1627 this.provider
= provider
;
1628 this.selectionDecorator
= selectionDecorator
;
1630 setOwnerDrawEnabled(showColoredLabels() && provider
instanceof IStyledLabelProvider
);
1632 provider
.addListener(this);
1634 if (selectionDecorator
!= null) {
1635 selectionDecorator
.addListener(this);
1640 * Sets new selection decorator.
1642 * @param newSelectionDecorator
1643 * new label decorator for selected items in the list
1645 public void setSelectionDecorator(ILabelDecorator newSelectionDecorator
) {
1646 if (selectionDecorator
!= null) {
1647 selectionDecorator
.removeListener(this);
1648 selectionDecorator
.dispose();
1651 selectionDecorator
= newSelectionDecorator
;
1653 if (selectionDecorator
!= null) {
1654 selectionDecorator
.addListener(this);
1659 * Gets selection decorator.
1661 * @return the label decorator for selected items in the list
1663 public ILabelDecorator
getSelectionDecorator() {
1664 return selectionDecorator
;
1668 * Sets new label provider.
1670 * @param newProvider
1671 * new label provider for items in the list, not
1674 public void setProvider(ILabelProvider newProvider
) {
1675 Assert
.isNotNull(newProvider
);
1676 provider
.removeListener(this);
1679 provider
= newProvider
;
1681 if (provider
!= null) {
1682 provider
.addListener(this);
1685 setOwnerDrawEnabled(showColoredLabels() && provider
instanceof IStyledLabelProvider
);
1688 private Image
getImage(Object element
) {
1689 if (element
instanceof ItemsListSeparator
) {
1690 return WorkbenchImages
1691 .getImage(IWorkbenchGraphicConstants
.IMG_OBJ_SEPARATOR
);
1694 return provider
.getImage(element
);
1697 private boolean isSelected(Object element
) {
1698 if (element
!= null && currentSelection
!= null) {
1699 for (int i
= 0; i
< currentSelection
.length
; i
++) {
1700 if (element
.equals(currentSelection
[i
])) {
1711 * @see org.eclipse.jface.viewers.ILabelProvider#getText(java.lang.Object)
1713 private String
getText(Object element
) {
1714 if (element
instanceof ItemsListSeparator
) {
1715 return getSeparatorLabel(((ItemsListSeparator
) element
)
1719 String str
= provider
.getText(element
);
1720 if (selectionDecorator
!= null && isSelected(element
)) {
1721 return selectionDecorator
.decorateText(str
.toString(), element
);
1727 private StyledString
getStyledText(Object element
,
1728 IStyledLabelProvider provider
) {
1729 StyledString string
= provider
.getStyledText(element
);
1731 if (selectionDecorator
!= null && isSelected(element
)) {
1732 String decorated
= selectionDecorator
.decorateText(string
1733 .getString(), element
);
1734 return StyledCellLabelProvider
.styleDecoratedString(decorated
, null, string
);
1735 // no need to add colors when element is selected
1741 public void update(ViewerCell cell
) {
1742 Object element
= cell
.getElement();
1744 if (!(element
instanceof ItemsListSeparator
)
1745 && provider
instanceof IStyledLabelProvider
) {
1746 IStyledLabelProvider styledLabelProvider
= (IStyledLabelProvider
) provider
;
1747 StyledString styledString
= getStyledText(element
,
1748 styledLabelProvider
);
1750 cell
.setText(styledString
.getString());
1751 cell
.setStyleRanges(styledString
.getStyleRanges());
1752 cell
.setImage(styledLabelProvider
.getImage(element
));
1754 cell
.setText(getText(element
));
1755 cell
.setImage(getImage(element
));
1757 cell
.setFont(getFont(element
));
1758 cell
.setForeground(getForeground(element
));
1759 cell
.setBackground(getBackground(element
));
1764 private String
getSeparatorLabel(String separatorLabel
) {
1765 Rectangle rect
= list
.getTable().getBounds();
1767 int borderWidth
= list
.getTable().computeTrim(0, 0, 0, 0).width
;
1769 int imageWidth
= WorkbenchImages
.getImage(
1770 IWorkbenchGraphicConstants
.IMG_OBJ_SEPARATOR
).getBounds().width
;
1772 int width
= rect
.width
- borderWidth
- imageWidth
;
1774 GC gc
= new GC(list
.getTable());
1775 gc
.setFont(list
.getTable().getFont());
1777 int fSeparatorWidth
= gc
.getAdvanceWidth('-');
1778 int fMessageLength
= gc
.textExtent(separatorLabel
).x
;
1782 StringBuffer dashes
= new StringBuffer();
1783 int chars
= (((width
- fMessageLength
) / fSeparatorWidth
) / 2) - 2;
1784 for (int i
= 0; i
< chars
; i
++) {
1788 StringBuffer result
= new StringBuffer();
1789 result
.append(dashes
);
1790 result
.append(" " + separatorLabel
+ " "); //$NON-NLS-1$//$NON-NLS-2$
1791 result
.append(dashes
);
1792 return result
.toString().trim();
1798 * @see org.eclipse.jface.viewers.IBaseLabelProvider#addListener(org.eclipse.jface.viewers.ILabelProviderListener)
1801 public void addListener(ILabelProviderListener listener
) {
1802 listeners
.add(listener
);
1808 * @see org.eclipse.jface.viewers.IBaseLabelProvider#dispose()
1811 public void dispose() {
1812 provider
.removeListener(this);
1815 if (selectionDecorator
!= null) {
1816 selectionDecorator
.removeListener(this);
1817 selectionDecorator
.dispose();
1826 * @see org.eclipse.jface.viewers.IBaseLabelProvider#isLabelProperty(java.lang.Object,
1830 public boolean isLabelProperty(Object element
, String property
) {
1831 if (provider
.isLabelProperty(element
, property
)) {
1834 if (selectionDecorator
!= null
1835 && selectionDecorator
.isLabelProperty(element
, property
)) {
1844 * @see org.eclipse.jface.viewers.IBaseLabelProvider#removeListener(org.eclipse.jface.viewers.ILabelProviderListener)
1847 public void removeListener(ILabelProviderListener listener
) {
1848 listeners
.remove(listener
);
1851 private Color
getBackground(Object element
) {
1852 if (element
instanceof ItemsListSeparator
) {
1855 if (provider
instanceof IColorProvider
) {
1856 return ((IColorProvider
) provider
).getBackground(element
);
1861 private Color
getForeground(Object element
) {
1862 if (element
instanceof ItemsListSeparator
) {
1863 return Display
.getCurrent().getSystemColor(
1864 SWT
.COLOR_WIDGET_NORMAL_SHADOW
);
1866 if (provider
instanceof IColorProvider
) {
1867 return ((IColorProvider
) provider
).getForeground(element
);
1872 private Font
getFont(Object element
) {
1873 if (element
instanceof ItemsListSeparator
) {
1876 if (provider
instanceof IFontProvider
) {
1877 return ((IFontProvider
) provider
).getFont(element
);
1885 * @see org.eclipse.jface.viewers.ILabelProviderListener#labelProviderChanged(org.eclipse.jface.viewers.LabelProviderChangedEvent)
1888 public void labelProviderChanged(LabelProviderChangedEvent event
) {
1889 Object
[] l
= listeners
.getListeners();
1890 for (int i
= 0; i
< listeners
.size(); i
++) {
1891 ((ILabelProviderListener
) l
[i
]).labelProviderChanged(event
);
1897 * Used in ItemsListContentProvider, separates history and non-history
1900 private class ItemsListSeparator
{
1902 private final String name
;
1905 * Creates a new instance of the class.
1908 * the name of the separator
1910 public ItemsListSeparator(String name
) {
1915 * Returns the name of this separator.
1917 * @return the name of the separator
1919 public String
getName() {
1925 * GranualProgressMonitor is used for monitoring progress of filtering
1926 * process. It is used by <code>RefreshProgressMessageJob</code> to
1927 * refresh progress message. State of this monitor illustrates state of
1928 * filtering or cache refreshing process.
1931 private class GranualProgressMonitor
extends ProgressMonitorWrapper
{
1933 private String name
;
1935 private String subName
;
1937 private int totalWork
;
1939 private double worked
;
1941 private boolean done
;
1944 * Creates instance of <code>GranualProgressMonitor</code>.
1947 * progress to be wrapped
1949 public GranualProgressMonitor(IProgressMonitor monitor
) {
1954 * Checks if filtering has been done
1956 * @return true if filtering work has been done false in other way
1958 public boolean isDone() {
1965 * @see org.eclipse.core.runtime.ProgressMonitorWrapper#setTaskName(java.lang.String)
1968 public void setTaskName(String name
) {
1969 super.setTaskName(name
);
1971 this.subName
= null;
1977 * @see org.eclipse.core.runtime.ProgressMonitorWrapper#subTask(java.lang.String)
1980 public void subTask(String name
) {
1981 super.subTask(name
);
1982 this.subName
= name
;
1988 * @see org.eclipse.core.runtime.ProgressMonitorWrapper#beginTask(java.lang.String,
1992 public void beginTask(String name
, int totalWork
) {
1993 super.beginTask(name
, totalWork
);
1994 if (this.name
== null) {
1997 this.totalWork
= totalWork
;
1998 refreshProgressMessageJob
.scheduleProgressRefresh(this);
2004 * @see org.eclipse.core.runtime.ProgressMonitorWrapper#worked(int)
2007 public void worked(int work
) {
2009 internalWorked(work
);
2015 * @see org.eclipse.core.runtime.ProgressMonitorWrapper#done()
2018 public void done() {
2026 * @see org.eclipse.core.runtime.ProgressMonitorWrapper#setCanceled(boolean)
2029 public void setCanceled(boolean b
) {
2031 super.setCanceled(b
);
2037 * @see org.eclipse.core.runtime.ProgressMonitorWrapper#internalWorked(double)
2040 public void internalWorked(double work
) {
2041 worked
= worked
+ work
;
2044 private String
getMessage() {
2046 return ""; //$NON-NLS-1$
2052 message
= subName
== null ?
"" : subName
; //$NON-NLS-1$
2054 message
= subName
== null ? name
2057 WorkbenchMessages
.FilteredItemsSelectionDialog_subtaskProgressMessage
,
2058 new Object
[] { name
, subName
});
2060 if (totalWork
== 0) {
2066 WorkbenchMessages
.FilteredItemsSelectionDialog_taskProgressMessage
,
2070 (int) ((worked
* 100) / totalWork
)) });
2077 * Filters items history and schedule filter job.
2079 private class FilterHistoryJob
extends Job
{
2082 * Filter used during the filtering process.
2084 private ItemsFilter itemsFilter
;
2087 * Creates new instance of receiver.
2089 public FilterHistoryJob() {
2090 super(WorkbenchMessages
.FilteredItemsSelectionDialog_jobLabel
);
2097 * @see org.eclipse.core.runtime.jobs.Job#run(org.eclipse.core.runtime.IProgressMonitor)
2100 protected IStatus
run(IProgressMonitor monitor
) {
2102 this.itemsFilter
= filter
;
2104 contentProvider
.reset();
2106 refreshWithLastSelection
= false;
2108 contentProvider
.addHistoryItems(itemsFilter
);
2110 if (!(lastCompletedFilter
!= null && lastCompletedFilter
2111 .isSubFilter(this.itemsFilter
))) {
2112 contentProvider
.refresh();
2115 filterJob
.schedule();
2117 return Status
.OK_STATUS
;
2123 * Filters items in indicated set and history. During filtering, it
2124 * refreshes the dialog (progress monitor and elements list).
2126 * Depending on the filter, <code>FilterJob</code> decides which kind of
2127 * search will be run inside <code>filterContent</code>. If the last
2128 * filtering is done (last completed filter), is not null, and the new
2129 * filter is a sub-filter ({@link FilteredItemsSelectionDialog.ItemsFilter#isSubFilter(FilteredItemsSelectionDialog.ItemsFilter)})
2130 * of the last, then <code>FilterJob</code> only filters in the cache. If
2131 * it is the first filtering or the new filter isn't a sub-filter of the
2132 * last one, a full search is run.
2134 private class FilterJob
extends Job
{
2137 * Filter used during the filtering process.
2139 protected ItemsFilter itemsFilter
;
2142 * Creates new instance of FilterJob
2144 public FilterJob() {
2145 super(WorkbenchMessages
.FilteredItemsSelectionDialog_jobLabel
);
2152 * @see org.eclipse.core.runtime.jobs.Job#run(org.eclipse.core.runtime.IProgressMonitor)
2155 protected final IStatus
run(IProgressMonitor parent
) {
2156 GranualProgressMonitor monitor
= new GranualProgressMonitor(parent
);
2157 return doRun(monitor
);
2161 * Executes job using the given filtering progress monitor. A hook for
2166 * @return result of the execution
2168 protected IStatus
doRun(GranualProgressMonitor monitor
) {
2170 internalRun(monitor
);
2171 } catch (CoreException e
) {
2175 PlatformUI
.PLUGIN_ID
,
2177 WorkbenchMessages
.FilteredItemsSelectionDialog_jobError
,
2180 return Status
.OK_STATUS
;
2184 * Main method for the job.
2187 * @throws CoreException
2189 private void internalRun(GranualProgressMonitor monitor
)
2190 throws CoreException
{
2192 if (monitor
.isCanceled()) {
2196 this.itemsFilter
= filter
;
2198 if (filter
.getPattern().length() != 0) {
2199 filterContent(monitor
);
2202 if (monitor
.isCanceled()) {
2206 contentProvider
.refresh();
2216 * for monitoring progress
2217 * @throws CoreException
2219 protected void filterContent(GranualProgressMonitor monitor
)
2220 throws CoreException
{
2222 // if (lastCompletedFilter != null
2223 // && lastCompletedFilter.isSubFilter(this.itemsFilter)) {
2225 // int length = lastCompletedResult.size() / 500;
2228 // WorkbenchMessages.FilteredItemsSelectionDialog_cacheSearchJob_taskName,
2231 // for (int pos = 0; pos < lastCompletedResult.size(); pos++) {
2233 // Object item = lastCompletedResult.get(pos);
2234 // if (monitor.isCanceled()) {
2237 // contentProvider.add(item, itemsFilter);
2239 // if ((pos % 500) == 0) {
2240 // monitor.worked(1);
2246 lastCompletedFilter
= null;
2247 lastCompletedResult
= null;
2249 SubProgressMonitor subMonitor
= null;
2250 if (monitor
!= null) {
2253 WorkbenchMessages
.FilteredItemsSelectionDialog_searchJob_taskName
,
2255 subMonitor
= new SubProgressMonitor(monitor
, 95);
2259 fillContentProvider(contentProvider
, itemsFilter
, subMonitor
);
2261 if (monitor
!= null && !monitor
.isCanceled()) {
2263 contentProvider
.rememberResult(itemsFilter
);
2273 * History stores a list of key, object pairs. The list is bounded at a
2274 * certain size. If the list exceeds this size the oldest element is removed
2275 * from the list. An element can be added/renewed with a call to
2276 * <code>accessed(Object)</code>.
2278 * The history can be stored to/loaded from an XML file.
2280 protected static abstract class SelectionHistory
{
2282 private static final String DEFAULT_ROOT_NODE_NAME
= "historyRootNode"; //$NON-NLS-1$
2284 private static final String DEFAULT_INFO_NODE_NAME
= "infoNode"; //$NON-NLS-1$
2286 private static final int MAX_HISTORY_SIZE
= 60;
2288 private final Set historyList
;
2290 private final String rootNodeName
;
2292 private final String infoNodeName
;
2294 private SelectionHistory(String rootNodeName
, String infoNodeName
) {
2296 historyList
= Collections
.synchronizedSet(new LinkedHashSet() {
2298 private static final long serialVersionUID
= 0L;
2303 * @see java.util.LinkedList#add(java.lang.Object)
2306 public boolean add(Object arg0
) {
2307 if (this.size() >= MAX_HISTORY_SIZE
) {
2308 Iterator iterator
= this.iterator();
2312 return super.add(arg0
);
2317 this.rootNodeName
= rootNodeName
;
2318 this.infoNodeName
= infoNodeName
;
2322 * Creates new instance of <code>SelectionHistory</code>.
2324 public SelectionHistory() {
2325 this(DEFAULT_ROOT_NODE_NAME
, DEFAULT_INFO_NODE_NAME
);
2329 * Adds object to history.
2332 * the item to be added to the history
2334 public synchronized void accessed(Object object
) {
2335 historyList
.remove(object
);
2336 historyList
.add(object
);
2340 * Returns <code>true</code> if history contains object.
2343 * the item for which check will be executed
2344 * @return <code>true</code> if history contains object
2345 * <code>false</code> in other way
2347 public synchronized boolean contains(Object object
) {
2348 return historyList
.contains(object
);
2352 * Returns <code>true</code> if history is empty.
2354 * @return <code>true</code> if history is empty
2356 public synchronized boolean isEmpty() {
2357 return historyList
.isEmpty();
2361 * Remove element from history.
2364 * to remove form the history
2365 * @return <code>true</code> if this list contained the specified
2368 public synchronized boolean remove(Object element
) {
2369 return historyList
.remove(element
);
2373 * Load history elements from memento.
2376 * memento from which the history will be retrieved
2378 public void load(IMemento memento
) {
2380 XMLMemento historyMemento
= (XMLMemento
) memento
2381 .getChild(rootNodeName
);
2383 if (historyMemento
== null) {
2387 IMemento
[] mementoElements
= historyMemento
2388 .getChildren(infoNodeName
);
2389 for (int i
= 0; i
< mementoElements
.length
; ++i
) {
2390 IMemento mementoElement
= mementoElements
[i
];
2391 Object object
= restoreItemFromMemento(mementoElement
);
2392 if (object
!= null) {
2393 historyList
.add(object
);
2399 * Save history elements to memento.
2402 * memento to which the history will be added
2404 public void save(IMemento memento
) {
2406 IMemento historyMemento
= memento
.createChild(rootNodeName
);
2408 Object
[] items
= getHistoryItems();
2409 for (int i
= 0; i
< items
.length
; i
++) {
2410 Object item
= items
[i
];
2411 IMemento elementMemento
= historyMemento
2412 .createChild(infoNodeName
);
2413 storeItemToMemento(item
, elementMemento
);
2419 * Gets array of history items.
2421 * @return array of history elements
2423 public synchronized Object
[] getHistoryItems() {
2424 return historyList
.toArray();
2428 * Creates an object using given memento.
2431 * memento used for creating new object
2433 * @return the restored object
2435 protected abstract Object
restoreItemFromMemento(IMemento memento
);
2438 * Store object in <code>IMemento</code>.
2443 * the memento to store to
2445 protected abstract void storeItemToMemento(Object item
, IMemento memento
);
2450 * Filters elements using SearchPattern by comparing the names of items with
2451 * the filter pattern.
2453 protected abstract class ItemsFilter
{
2455 protected SearchPattern patternMatcher
;
2458 * Creates new instance of ItemsFilter.
2460 public ItemsFilter() {
2461 this(new SearchPattern());
2465 * Creates new instance of ItemsFilter.
2467 * @param searchPattern
2468 * the pattern to be used when filtering
2470 public ItemsFilter(SearchPattern searchPattern
) {
2471 patternMatcher
= searchPattern
;
2472 String stringPattern
= ""; //$NON-NLS-1$
2473 if (pattern
!= null && !pattern
.getText().equals("*")) { //$NON-NLS-1$
2474 stringPattern
= pattern
.getText();
2476 patternMatcher
.setPattern(stringPattern
);
2480 * Check if the given filter is a sub-filter of this filter. The default
2481 * implementation checks if the <code>SearchPattern</code> from the
2482 * given filter is a sub-pattern of the one from this filter.
2484 * <i>WARNING: This method is <b>not</b> defined in reading order, i.e.
2485 * <code>a.isSubFilter(b)</code> is <code>true</code> iff
2486 * <code>b</code> is a sub-filter of <code>a</code>, and not
2491 * the filter to be checked, or <code>null</code>
2492 * @return <code>true</code> if the given filter is sub-filter of this
2493 * filter, <code>false</code> if the given filter isn't a
2494 * sub-filter or is <code>null</code>
2496 * @see org.eclipse.ui.dialogs.SearchPattern#isSubPattern(org.eclipse.ui.dialogs.SearchPattern)
2498 public boolean isSubFilter(ItemsFilter filter
) {
2499 if (filter
!= null) {
2500 return this.patternMatcher
.isSubPattern(filter
.patternMatcher
);
2506 * Checks whether the provided filter is equal to the current filter.
2507 * The default implementation checks if <code>SearchPattern</code>
2508 * from current filter is equal to the one from provided filter.
2511 * filter to be checked, or <code>null</code>
2512 * @return <code>true</code> if the given filter is equal to current
2513 * filter, <code>false</code> if given filter isn't equal to
2514 * current one or if it is <code>null</code>
2516 * @see org.eclipse.ui.dialogs.SearchPattern#equalsPattern(org.eclipse.ui.dialogs.SearchPattern)
2518 public boolean equalsFilter(ItemsFilter filter
) {
2520 && filter
.patternMatcher
.equalsPattern(this.patternMatcher
)) {
2527 * Checks whether the pattern's match rule is camel case.
2529 * @return <code>true</code> if pattern's match rule is camel case,
2530 * <code>false</code> otherwise
2532 public boolean isCamelCasePattern() {
2533 return patternMatcher
.getMatchRule() == SearchPattern
.RULE_CAMELCASE_MATCH
;
2537 * Returns the pattern string.
2539 * @return pattern for this filter
2541 * @see SearchPattern#getPattern()
2543 public String
getPattern() {
2544 return patternMatcher
.getPattern();
2548 * Returns the rule to apply for matching keys.
2550 * @return an implementation-specific match rule
2552 * @see SearchPattern#getMatchRule() for match rules returned by the
2553 * default implementation
2555 public int getMatchRule() {
2556 return patternMatcher
.getMatchRule();
2560 * Matches text with filter.
2563 * the text to match with the filter
2564 * @return <code>true</code> if text matches with filter pattern,
2565 * <code>false</code> otherwise
2567 protected boolean matches(String text
) {
2568 return patternMatcher
.matches(text
);
2572 * General method for matching raw name pattern. Checks whether current
2573 * pattern is prefix of name provided item.
2577 * @return <code>true</code> if current pattern is a prefix of name
2578 * provided item, <code>false</code> if item's name is shorter
2579 * than prefix or sequences of characters don't match.
2581 public boolean matchesRawNamePattern(Object item
) {
2582 String prefix
= patternMatcher
.getPattern();
2583 String text
= getElementName(item
);
2589 int textLength
= text
.length();
2590 int prefixLength
= prefix
.length();
2591 if (textLength
< prefixLength
) {
2594 for (int i
= prefixLength
- 1; i
>= 0; i
--) {
2595 if (Character
.toLowerCase(prefix
.charAt(i
)) != Character
2596 .toLowerCase(text
.charAt(i
))) {
2604 * Matches an item against filter conditions.
2607 * @return <code>true<code> if item matches against filter conditions, <code>false</code>
2610 public abstract boolean matchItem(Object item
);
2613 * Checks consistency of an item. Item is inconsistent if was changed or
2617 * @return <code>true</code> if item is consistent, <code>false</code>
2618 * if item is inconsistent
2620 public abstract boolean isConsistentItem(Object item
);
2625 * An interface to content providers for
2626 * <code>FilterItemsSelectionDialog</code>.
2628 protected abstract class AbstractContentProvider
{
2630 * Adds the item to the content provider iff the filter matches the
2631 * item. Otherwise does nothing.
2635 * @param itemsFilter
2638 * @see FilteredItemsSelectionDialog.ItemsFilter#matchItem(Object)
2640 public abstract void add(Object item
, ItemsFilter itemsFilter
);
2644 * Collects filtered elements. Contains one synchronized, sorted set for
2645 * collecting filtered elements. All collected elements are sorted using
2646 * comparator. Comparator is returned by getElementComparator() method.
2647 * Implementation of <code>ItemsFilter</code> is used to filter elements.
2648 * The key function of filter used in to filtering is
2649 * <code>matchElement(Object item)</code>.
2651 * The <code>ContentProvider</code> class also provides item filtering
2652 * methods. The filtering has been moved from the standard TableView
2653 * <code>getFilteredItems()</code> method to content provider, because
2654 * <code>ILazyContentProvider</code> and virtual tables are used. This
2655 * class is responsible for adding a separator below history items and
2656 * marking each items as duplicate if its name repeats more than once on the
2659 private class ContentProvider
extends AbstractContentProvider
implements
2660 IStructuredContentProvider
, ILazyContentProvider
{
2662 private SelectionHistory selectionHistory
;
2665 * Raw result of the searching (unsorted, unfiltered).
2667 * Standard object flow:
2668 * <code>items -> lastSortedItems -> lastFilteredItems</code>
2670 private final Set items
;
2673 * Items that are duplicates.
2675 private final Set duplicates
;
2678 * List of <code>ViewerFilter</code>s to be used during filtering
2680 private List filters
;
2683 * Result of the last filtering.
2685 * Standard object flow:
2686 * <code>items -> lastSortedItems -> lastFilteredItems</code>
2688 private List lastFilteredItems
;
2691 * Result of the last sorting.
2693 * Standard object flow:
2694 * <code>items -> lastSortedItems -> lastFilteredItems</code>
2696 private final List lastSortedItems
;
2699 * Used for <code>getFilteredItems()</code> method canceling (when the
2700 * job that invoked the method was canceled).
2702 * Method canceling could be based (only) on monitor canceling
2703 * unfortunately sometimes the method <code>getFilteredElements()</code>
2704 * could be run with a null monitor, the <code>reset</code> flag have
2705 * to be left intact.
2707 private boolean reset
;
2710 * Creates new instance of <code>ContentProvider</code>.
2712 public ContentProvider() {
2713 this.items
= Collections
.synchronizedSet(new HashSet(2048));
2714 this.duplicates
= Collections
.synchronizedSet(new HashSet(256));
2715 this.lastFilteredItems
= new ArrayList();
2716 this.lastSortedItems
= Collections
.synchronizedList(new ArrayList(
2721 * Sets selection history.
2723 * @param selectionHistory
2724 * The selectionHistory to set.
2726 public void setSelectionHistory(SelectionHistory selectionHistory
) {
2727 this.selectionHistory
= selectionHistory
;
2731 * @return Returns the selectionHistory.
2733 public SelectionHistory
getSelectionHistory() {
2734 return selectionHistory
;
2738 * Removes all content items and resets progress message.
2740 public void reset() {
2743 this.duplicates
.clear();
2744 this.lastSortedItems
.clear();
2748 * Stops reloading cache - <code>getFilteredItems()</code> method.
2750 public void stopReloadingCache() {
2755 * Adds filtered item.
2758 * @param itemsFilter
2761 public void add(Object item
, ItemsFilter itemsFilter
) {
2762 if (itemsFilter
== filter
) {
2763 if (itemsFilter
!= null) {
2764 if (itemsFilter
.matchItem(item
)) {
2765 this.items
.add(item
);
2768 this.items
.add(item
);
2774 * Add all history items to <code>contentProvider</code>.
2776 * @param itemsFilter
2778 public void addHistoryItems(ItemsFilter itemsFilter
) {
2779 if (this.selectionHistory
!= null) {
2780 Object
[] items
= this.selectionHistory
.getHistoryItems();
2781 for (int i
= 0; i
< items
.length
; i
++) {
2782 Object item
= items
[i
];
2783 if (itemsFilter
== filter
) {
2784 if (itemsFilter
!= null) {
2785 if (itemsFilter
.matchItem(item
)) {
2786 if (itemsFilter
.isConsistentItem(item
)) {
2787 this.items
.add(item
);
2789 this.selectionHistory
.remove(item
);
2801 public void refresh() {
2806 * Removes items from history and refreshes the view.
2811 * @return removed item
2813 public Object
removeHistoryElement(Object item
) {
2814 if (this.selectionHistory
!= null) {
2815 this.selectionHistory
.remove(item
);
2817 if (filter
== null || filter
.getPattern().length() == 0) {
2819 duplicates
.remove(item
);
2820 this.lastSortedItems
.remove(item
);
2823 synchronized (lastSortedItems
) {
2824 Collections
.sort(lastSortedItems
, getHistoryComparator());
2830 * Adds item to history and refresh view.
2835 public void addHistoryElement(Object item
) {
2836 if (this.selectionHistory
!= null) {
2837 this.selectionHistory
.accessed(item
);
2839 if (filter
== null || !filter
.matchItem(item
)) {
2840 this.items
.remove(item
);
2841 this.duplicates
.remove(item
);
2842 this.lastSortedItems
.remove(item
);
2844 synchronized (lastSortedItems
) {
2845 Collections
.sort(lastSortedItems
, getHistoryComparator());
2852 * @return <code>true</code> if given item is part of the history
2854 public boolean isHistoryElement(Object item
) {
2855 if (this.selectionHistory
!= null) {
2856 return this.selectionHistory
.contains(item
);
2862 * Sets/unsets given item as duplicate.
2867 * @param isDuplicate
2870 public void setDuplicateElement(Object item
, boolean isDuplicate
) {
2871 if (this.items
.contains(item
)) {
2873 this.duplicates
.add(item
);
2875 this.duplicates
.remove(item
);
2881 * Indicates whether given item is a duplicate.
2885 * @return <code>true</code> if item is duplicate
2887 public boolean isDuplicateElement(Object item
) {
2888 return duplicates
.contains(item
);
2892 * Load history from memento.
2895 * memento from which the history will be retrieved
2897 public void loadHistory(IMemento memento
) {
2898 if (this.selectionHistory
!= null) {
2899 this.selectionHistory
.load(memento
);
2904 * Save history to memento.
2907 * memento to which the history will be added
2909 public void saveHistory(IMemento memento
) {
2910 if (this.selectionHistory
!= null) {
2911 this.selectionHistory
.save(memento
);
2916 * Gets sorted items.
2918 * @return sorted items
2920 private Object
[] getSortedItems() {
2921 if (lastSortedItems
.size() != items
.size()) {
2922 synchronized (lastSortedItems
) {
2923 lastSortedItems
.clear();
2924 lastSortedItems
.addAll(items
);
2925 Collections
.sort(lastSortedItems
, getHistoryComparator());
2928 return lastSortedItems
.toArray();
2932 * Remember result of filtering.
2934 * @param itemsFilter
2936 public void rememberResult(ItemsFilter itemsFilter
) {
2937 List itemsList
= Collections
.synchronizedList(Arrays
2938 .asList(getSortedItems()));
2940 if (itemsFilter
== filter
) {
2941 lastCompletedFilter
= itemsFilter
;
2942 lastCompletedResult
= itemsList
;
2950 * @see org.eclipse.jface.viewers.IStructuredContentProvider#getElements(java.lang.Object)
2953 public Object
[] getElements(Object inputElement
) {
2954 //return items.toArray();
2955 return lastFilteredItems
.toArray();
2958 public int getNumberOfElements() {
2960 return lastFilteredItems
.size();
2966 * @see org.eclipse.jface.viewers.IContentProvider#dispose()
2969 public void dispose() {
2975 * @see org.eclipse.jface.viewers.IContentProvider#inputChanged(org.eclipse.jface.viewers.Viewer,
2976 * java.lang.Object, java.lang.Object)
2979 public void inputChanged(Viewer viewer
, Object oldInput
, Object newInput
) {
2985 * @see org.eclipse.jface.viewers.ILazyContentProvider#updateElement(int)
2988 public void updateElement(int index
) {
2990 CdmFilteredItemsSelectionDialog
.this.list
.replace((lastFilteredItems
2991 .size() > index
) ? lastFilteredItems
.get(index
) : null,
2997 * Main method responsible for getting the filtered items and checking
2998 * for duplicates. It is based on the
2999 * {@link FilteredItemsSelectionDialog.ContentProvider#getFilteredItems(Object, IProgressMonitor)}.
3001 * @param checkDuplicates
3002 * <code>true</code> if data concerning elements
3003 * duplication should be computed - it takes much more time
3004 * than standard filtering
3009 public void reloadCache(boolean checkDuplicates
,
3010 IProgressMonitor monitor
) {
3014 if (monitor
!= null) {
3015 // the work is divided into two actions of the same length
3016 int totalWork
= checkDuplicates ?
200 : 100;
3020 WorkbenchMessages
.FilteredItemsSelectionDialog_cacheRefreshJob
,
3024 // the TableViewer's root (the input) is treated as parent
3026 lastFilteredItems
= Arrays
.asList(getFilteredItems(list
.getInput(),
3027 monitor
!= null ?
new SubProgressMonitor(monitor
, 100)
3030 if (reset
|| (monitor
!= null && monitor
.isCanceled())) {
3031 if (monitor
!= null) {
3037 if (checkDuplicates
) {
3038 checkDuplicates(monitor
);
3040 if (monitor
!= null) {
3045 private void checkDuplicates(IProgressMonitor monitor
) {
3046 synchronized (lastFilteredItems
) {
3047 IProgressMonitor subMonitor
= null;
3048 int reportEvery
= lastFilteredItems
.size() / 20;
3049 if (monitor
!= null) {
3050 subMonitor
= new SubProgressMonitor(monitor
, 100);
3053 WorkbenchMessages
.FilteredItemsSelectionDialog_cacheRefreshJob_checkDuplicates
,
3056 HashMap helperMap
= new HashMap();
3057 for (int i
= 0; i
< lastFilteredItems
.size(); i
++) {
3059 || (subMonitor
!= null && subMonitor
.isCanceled())) {
3062 Object item
= lastFilteredItems
.get(i
);
3064 if (!(item
instanceof ItemsListSeparator
)) {
3065 Object previousItem
= helperMap
.put(
3066 getElementName(item
), item
);
3067 if (previousItem
!= null) {
3068 setDuplicateElement(previousItem
, true);
3069 setDuplicateElement(item
, true);
3071 setDuplicateElement(item
, false);
3075 if (subMonitor
!= null && reportEvery
!= 0
3076 && (i
+ 1) % reportEvery
== 0) {
3077 subMonitor
.worked(1);
3085 * Returns an array of items filtered using the provided
3086 * <code>ViewerFilter</code>s with a separator added.
3091 * progress monitor, can be <code>null</code>
3092 * @return an array of filtered items
3094 protected Object
[] getFilteredItems(Object parent
,
3095 IProgressMonitor monitor
) {
3097 if (monitor
== null) {
3098 monitor
= new NullProgressMonitor();
3103 WorkbenchMessages
.FilteredItemsSelectionDialog_cacheRefreshJob_getFilteredElements
,
3105 if (filters
!= null) {
3106 ticks
/= (filters
.size() + 2);
3111 // get already sorted array
3112 Object
[] filteredElements
= getSortedItems();
3114 monitor
.worked(ticks
);
3116 // filter the elements using provided ViewerFilters
3117 if (filters
!= null && filteredElements
!= null) {
3118 for (Iterator iter
= filters
.iterator(); iter
.hasNext();) {
3119 ViewerFilter f
= (ViewerFilter
) iter
.next();
3120 filteredElements
= f
.filter(list
, parent
, filteredElements
);
3121 monitor
.worked(ticks
);
3125 if (filteredElements
== null || monitor
.isCanceled()) {
3127 return new Object
[0];
3130 ArrayList preparedElements
= new ArrayList();
3131 boolean hasHistory
= false;
3133 if (filteredElements
.length
> 0) {
3134 if (isHistoryElement(filteredElements
[0])) {
3139 int reportEvery
= filteredElements
.length
/ ticks
;
3142 for (int i
= 0; i
< filteredElements
.length
; i
++) {
3143 Object item
= filteredElements
[i
];
3145 if (hasHistory
&& !isHistoryElement(item
)) {
3146 preparedElements
.add(itemsListSeparator
);
3150 preparedElements
.add(item
);
3152 if (reportEvery
!= 0 && ((i
+ 1) % reportEvery
== 0)) {
3159 return preparedElements
.toArray();
3163 * Adds a filter to this content provider. For an example usage of such
3164 * filters look at the project <code>org.eclipse.ui.ide</code>, class
3165 * <code>org.eclipse.ui.dialogs.FilteredResourcesSelectionDialog.CustomWorkingSetFilter</code>.
3169 * the filter to be added
3171 public void addFilter(ViewerFilter filter
) {
3172 if (filters
== null) {
3173 filters
= new ArrayList();
3175 filters
.add(filter
);
3176 // currently filters are only added when dialog is restored
3177 // if it is changed, refreshing the whole TableViewer should be
3184 * A content provider that does nothing.
3186 private class NullContentProvider
implements IContentProvider
{
3191 * @see org.eclipse.jface.viewers.IContentProvider#dispose()
3194 public void dispose() {
3200 * @see org.eclipse.jface.viewers.IContentProvider#inputChanged(org.eclipse.jface.viewers.Viewer,
3201 * java.lang.Object, java.lang.Object)
3204 public void inputChanged(Viewer viewer
, Object oldInput
, Object newInput
) {
3210 * DetailsContentViewer objects are wrappers for labels.
3211 * DetailsContentViewer provides means to change label's image and text when
3212 * the attached LabelProvider is updated.
3214 private class DetailsContentViewer
extends ContentViewer
{
3216 private final CLabel label
;
3219 * Unfortunately, it was impossible to delegate displaying border to
3220 * label. The <code>ViewForm</code> is used because
3221 * <code>CLabel</code> displays shadow when border is present.
3223 private final ViewForm viewForm
;
3226 * Constructs a new instance of this class given its parent and a style
3227 * value describing its behavior and appearance.
3230 * the parent component
3234 public DetailsContentViewer(Composite parent
, int style
) {
3235 viewForm
= new ViewForm(parent
, style
);
3236 GridData gd
= new GridData(GridData
.FILL_HORIZONTAL
);
3237 gd
.horizontalSpan
= 2;
3238 viewForm
.setLayoutData(gd
);
3239 label
= new CLabel(viewForm
, SWT
.FLAT
);
3240 label
.setFont(parent
.getFont());
3241 viewForm
.setContent(label
);
3246 * Shows/hides the content viewer.
3249 * if the content viewer should be visible.
3251 public void setVisible(boolean visible
) {
3252 GridData gd
= (GridData
) viewForm
.getLayoutData();
3253 gd
.exclude
= !visible
;
3254 viewForm
.getParent().layout();
3260 * @see org.eclipse.jface.viewers.Viewer#inputChanged(java.lang.Object,
3264 protected void inputChanged(Object input
, Object oldInput
) {
3265 if (oldInput
== null) {
3266 if (input
== null) {
3280 * @see org.eclipse.jface.viewers.ContentViewer#handleLabelProviderChanged(org.eclipse.jface.viewers.LabelProviderChangedEvent)
3283 protected void handleLabelProviderChanged(
3284 LabelProviderChangedEvent event
) {
3285 if (event
!= null) {
3286 refresh(event
.getElements());
3293 * @see org.eclipse.jface.viewers.Viewer#getControl()
3296 public Control
getControl() {
3303 * @see org.eclipse.jface.viewers.Viewer#getSelection()
3306 public ISelection
getSelection() {
3314 * @see org.eclipse.jface.viewers.Viewer#refresh()
3317 public void refresh() {
3318 Object input
= this.getInput();
3319 if (input
!= null) {
3320 ILabelProvider labelProvider
= (ILabelProvider
) getLabelProvider();
3321 doRefresh(labelProvider
.getText(input
), labelProvider
3324 doRefresh(null, null);
3329 * Sets the given text and image to the label.
3332 * the new text or null
3336 private void doRefresh(String text
, Image image
) {
3337 if ( text
!= null ) {
3338 text
= LegacyActionTools
.escapeMnemonics(text
);
3340 label
.setText(text
);
3341 label
.setImage(image
);
3347 * @see org.eclipse.jface.viewers.Viewer#setSelection(org.eclipse.jface.viewers.ISelection,
3351 public void setSelection(ISelection selection
, boolean reveal
) {
3356 * Refreshes the label if currently chosen element is on the list.
3359 * list of changed object
3361 private void refresh(Object
[] objs
) {
3362 if (objs
== null || getInput() == null) {
3365 Object input
= getInput();
3366 for (int i
= 0; i
< objs
.length
; i
++) {
3367 if (objs
[i
].equals(input
)) {
3376 * Compares items according to the history.
3378 private class HistoryComparator
implements Comparator
{
3383 * @see java.util.Comparator#compare(java.lang.Object, java.lang.Object)
3386 public int compare(Object o1
, Object o2
) {
3387 boolean h1
= isHistoryElement(o1
);
3388 boolean h2
= isHistoryElement(o2
);
3390 return getItemsComparator().compare(o1
, o2
);
3407 * Get the control where the search pattern is entered. Any filtering should
3408 * be done using an {@link ItemsFilter}. This control should only be
3409 * accessed for listeners that wish to handle events that do not affect
3410 * filtering such as custom traversal.
3412 * @return Control or <code>null</code> if the pattern control has not
3415 public Control
getPatternControl() {
3420 * CDM IMPLEMENTATION
3421 * we have to call the initModel() method in the applyFilter method, so we had to add this abstract method (moved from AbstractFilteredCdmResourceSelectionDialog)
3424 abstract protected void initModel();