Merge branch 'release/5.0.0'
[taxeditor.git] / eu.etaxonomy.taxeditor.store / src / main / java / eu / etaxonomy / taxeditor / ui / dialog / selection / CdmFilteredItemsSelectionDialog.java
1 /**
2 * Copyright (C) 2016 EDIT
3 * European Distributed Institute of Taxonomy
4 * http://www.e-taxonomy.eu
5 *
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.
8 */
9 package eu.etaxonomy.taxeditor.ui.dialog.selection;
10
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;
23 import java.util.Set;
24
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;
124
125 /**
126 * @author k.luther
127 * @date 10.06.2016
128 *
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
132 * original class
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.
137 *
138 *
139 */
140
141 public abstract class CdmFilteredItemsSelectionDialog extends SelectionStatusDialog {
142
143
144
145 private static final String DIALOG_BOUNDS_SETTINGS = "DialogBoundsSettings"; //$NON-NLS-1$
146
147 private static final String SHOW_STATUS_LINE = "ShowStatusLine"; //$NON-NLS-1$
148
149 private static final String HISTORY_SETTINGS = "History"; //$NON-NLS-1$
150
151 private static final String DIALOG_HEIGHT = "DIALOG_HEIGHT"; //$NON-NLS-1$
152
153 private static final String DIALOG_WIDTH = "DIALOG_WIDTH"; //$NON-NLS-1$
154
155 /**
156 * Represents an empty selection in the pattern input field (used only for
157 * initial pattern).
158 */
159 public static final int NONE = 0;
160
161 /**
162 * Pattern input field selection where caret is at the beginning (used only
163 * for initial pattern).
164 */
165 public static final int CARET_BEGINNING = 1;
166
167 /**
168 * Represents a full selection in the pattern input field (used only for
169 * initial pattern).
170 */
171 public static final int FULL_SELECTION = 2;
172
173 private Text pattern;
174
175 private TableViewer list;
176
177 private DetailsContentViewer details;
178
179 /**
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.
183 */
184 private ILabelProvider detailsLabelProvider;
185
186 private ItemsListLabelProvider itemsListLabelProvider;
187
188 private MenuManager menuManager;
189
190 private MenuManager contextMenuManager;
191
192 private final boolean multi;
193
194 private ToolBar toolBar;
195
196 private ToolItem toolItem;
197
198 private Label progressLabel;
199
200 private ToggleStatusLineAction toggleStatusLineAction;
201
202 private RemoveHistoryItemAction removeHistoryItemAction;
203
204 private ActionContributionItem removeHistoryActionContributionItem;
205
206 private IStatus status;
207
208 private final RefreshCacheJob refreshCacheJob;
209
210 private final RefreshProgressMessageJob refreshProgressMessageJob = new RefreshProgressMessageJob();
211
212 private Object[] currentSelection;
213
214 private final ContentProvider contentProvider;
215
216 private final FilterHistoryJob filterHistoryJob;
217
218 private final FilterJob filterJob;
219
220 private ItemsFilter filter;
221
222 private List lastCompletedResult;
223
224 private ItemsFilter lastCompletedFilter;
225
226 private String initialPatternText;
227
228 private int selectionMode;
229
230 private ItemsListSeparator itemsListSeparator;
231
232 private static final String EMPTY_STRING = ""; //$NON-NLS-1$
233
234 private boolean refreshWithLastSelection = false;
235
236 private IHandlerActivation showViewHandler;
237
238 /**
239 * Creates a new instance of the class.
240 *
241 * @param shell
242 * shell to parent the dialog on
243 * @param multi
244 * indicates whether dialog allows to select more than one
245 * position in its list of items
246 */
247 public CdmFilteredItemsSelectionDialog(Shell shell, boolean multi) {
248 super(shell);
249 this.multi = 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;
257 }
258
259 /**
260 * Creates a new instance of the class. Created dialog won't allow to select
261 * more than one item.
262 *
263 * @param shell
264 * shell to parent the dialog on
265 */
266 public CdmFilteredItemsSelectionDialog(Shell shell) {
267 this(shell, false);
268 }
269
270 /**
271 * Adds viewer filter to the dialog items list.
272 *
273 * @param filter
274 * the new filter
275 */
276 protected void addListFilter(ViewerFilter filter) {
277 contentProvider.addFilter(filter);
278 }
279
280 /**
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.
286 *
287 * @see IWorkbenchPreferenceConstants#USE_COLORED_LABELS
288 *
289 * @param listLabelProvider
290 * the label provider for items in the list
291 */
292 public void setListLabelProvider(ILabelProvider listLabelProvider) {
293 getItemsListLabelProvider().setProvider(listLabelProvider);
294 }
295
296 /**
297 * Returns the label decorator for selected items in the list.
298 *
299 * @return the label decorator for selected items in the list
300 */
301 private ILabelDecorator getListSelectionLabelDecorator() {
302 return getItemsListLabelProvider().getSelectionDecorator();
303 }
304
305 /**
306 * Sets the label decorator for selected items in the list.
307 *
308 * @param listSelectionLabelDecorator
309 * the label decorator for selected items in the list
310 */
311 public void setListSelectionLabelDecorator(
312 ILabelDecorator listSelectionLabelDecorator) {
313 getItemsListLabelProvider().setSelectionDecorator(
314 listSelectionLabelDecorator);
315 }
316
317 /**
318 * Returns the item list label provider.
319 *
320 * @return the item list label provider
321 */
322 private ItemsListLabelProvider getItemsListLabelProvider() {
323 if (itemsListLabelProvider == null) {
324 itemsListLabelProvider = new ItemsListLabelProvider(
325 new LabelProvider(), null);
326 }
327 return itemsListLabelProvider;
328 }
329
330 /**
331 * Sets label provider for the details field.
332 *
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
337 * element.
338 *
339 * @see #getSelectedItems() getSelectedItems() can be used to retrieve
340 * selected items and get the items count.
341 *
342 * @param detailsLabelProvider
343 * the label provider for the details field
344 */
345 public void setDetailsLabelProvider(ILabelProvider detailsLabelProvider) {
346 this.detailsLabelProvider = detailsLabelProvider;
347 if (details != null) {
348 details.setLabelProvider(detailsLabelProvider);
349 }
350 }
351
352 private ILabelProvider getDetailsLabelProvider() {
353 if (detailsLabelProvider == null) {
354 detailsLabelProvider = new LabelProvider();
355 }
356 return detailsLabelProvider;
357 }
358
359 /*
360 * (non-Javadoc)
361 *
362 * @see org.eclipse.jface.window.Window#create()
363 */
364 @Override
365 public void create() {
366 super.create();
367 pattern.setFocus();
368 }
369
370 /**
371 * Restores dialog using persisted settings. The default implementation
372 * restores the status of the details line and the selection history.
373 *
374 * @param settings
375 * settings used to restore dialog
376 */
377 protected void restoreDialog(IDialogSettings settings) {
378 boolean toggleStatusLine = true;
379
380 if (settings.get(SHOW_STATUS_LINE) != null) {
381 toggleStatusLine = settings.getBoolean(SHOW_STATUS_LINE);
382 }
383
384 toggleStatusLineAction.setChecked(toggleStatusLine);
385
386 details.setVisible(toggleStatusLine);
387
388 String setting = settings.get(HISTORY_SETTINGS);
389 if (setting != null) {
390 try {
391 IMemento memento = XMLMemento.createReadRoot(new StringReader(
392 setting));
393 this.contentProvider.loadHistory(memento);
394 } catch (WorkbenchException e) {
395 // Simply don't restore the settings
396 StatusManager
397 .getManager()
398 .handle(
399 new Status(
400 IStatus.ERROR,
401 PlatformUI.PLUGIN_ID,
402 IStatus.ERROR,
403 WorkbenchMessages.FilteredItemsSelectionDialog_restoreError,
404 e));
405 }
406 }
407 }
408
409 /*
410 * (non-Javadoc)
411 *
412 * @see org.eclipse.jface.window.Window#close()
413 */
414 @Override
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;
425 }
426 if (menuManager != null) {
427 menuManager.dispose();
428 }
429 if (contextMenuManager != null) {
430 contextMenuManager.dispose();
431 }
432 storeDialog(getDialogSettings());
433 return super.close();
434 }
435
436 /**
437 * Stores dialog settings.
438 *
439 * @param settings
440 * settings used to store dialog
441 */
442 protected void storeDialog(IDialogSettings settings) {
443 settings.put(SHOW_STATUS_LINE, toggleStatusLineAction.isChecked());
444
445 XMLMemento memento = XMLMemento.createWriteRoot(HISTORY_SETTINGS);
446 this.contentProvider.saveHistory(memento);
447 StringWriter writer = new StringWriter();
448 try {
449 memento.save(writer);
450 settings.put(HISTORY_SETTINGS, writer.getBuffer().toString());
451 } catch (IOException e) {
452 // Simply don't store the settings
453 StatusManager
454 .getManager()
455 .handle(
456 new Status(
457 IStatus.ERROR,
458 PlatformUI.PLUGIN_ID,
459 IStatus.ERROR,
460 WorkbenchMessages.FilteredItemsSelectionDialog_storeError,
461 e));
462 }
463 }
464
465 /**
466 * Create a new header which is labelled by headerLabel.
467 *
468 * @param parent
469 * @return Label the label of the header
470 */
471 private Label createHeader(Composite parent) {
472 Composite header = new Composite(parent, SWT.NONE);
473
474 GridLayout layout = new GridLayout();
475 layout.numColumns = 2;
476 layout.marginWidth = 0;
477 layout.marginHeight = 0;
478 header.setLayout(layout);
479
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() {
485 @Override
486 public void keyTraversed(TraverseEvent e) {
487 if (e.detail == SWT.TRAVERSE_MNEMONIC && e.doit) {
488 e.detail = SWT.TRAVERSE_NONE;
489 pattern.setFocus();
490 }
491 }
492 });
493
494 GridData gd = new GridData(GridData.FILL_HORIZONTAL);
495 headerLabel.setLayoutData(gd);
496
497 createViewMenu(header);
498 header.setLayoutData(gd);
499 return headerLabel;
500 }
501
502 /**
503 * Create the labels for the list and the progress. Return the list label.
504 *
505 * @param parent
506 * @return Label
507 */
508 private Label createLabels(Composite parent) {
509 Composite labels = new Composite(parent, SWT.NONE);
510
511 GridLayout layout = new GridLayout();
512 layout.numColumns = 2;
513 layout.marginWidth = 0;
514 layout.marginHeight = 0;
515 labels.setLayout(layout);
516
517 Label listLabel = new Label(labels, SWT.NONE);
518 listLabel
519 .setText(WorkbenchMessages.FilteredItemsSelectionDialog_listLabel);
520
521 listLabel.addTraverseListener(new TraverseListener() {
522 @Override
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();
527 }
528 }
529 });
530
531 GridData gd = new GridData(GridData.FILL_HORIZONTAL);
532 listLabel.setLayoutData(gd);
533
534 progressLabel = new Label(labels, SWT.RIGHT);
535 progressLabel.setLayoutData(gd);
536
537 labels.setLayoutData(gd);
538 return listLabel;
539 }
540
541 private void createViewMenu(Composite parent) {
542 toolBar = new ToolBar(parent, SWT.FLAT);
543 toolItem = new ToolItem(toolBar, SWT.PUSH, 0);
544
545 GridData data = new GridData();
546 data.horizontalAlignment = GridData.END;
547 toolBar.setLayoutData(data);
548
549 toolBar.addMouseListener(new MouseAdapter() {
550 @Override
551 public void mouseDown(MouseEvent e) {
552 showViewMenu();
553 }
554 });
555
556 toolItem.setImage(WorkbenchImages
557 .getImage(IWorkbenchGraphicConstants.IMG_LCL_VIEW_MENU));
558 toolItem
559 .setToolTipText(WorkbenchMessages.FilteredItemsSelectionDialog_menu);
560 toolItem.addSelectionListener(new SelectionAdapter() {
561 @Override
562 public void widgetSelected(SelectionEvent e) {
563 showViewMenu();
564 }
565 });
566
567 menuManager = new MenuManager();
568
569 fillViewMenu(menuManager);
570
571 IHandlerService service = PlatformUI.getWorkbench()
572 .getService(IHandlerService.class);
573 IHandler handler = new AbstractHandler() {
574 @Override
575 public Object execute(ExecutionEvent event) {
576 showViewMenu();
577 return null;
578 }
579 };
580 showViewHandler = service.activateHandler(
581 IWorkbenchCommandConstants.WINDOW_SHOW_VIEW_MENU, handler,
582 new ActiveShellExpression(getShell()));
583 }
584
585 /**
586 * Fills the menu of the dialog.
587 *
588 * @param menuManager
589 * the menu manager
590 */
591 protected void fillViewMenu(IMenuManager menuManager) {
592 toggleStatusLineAction = new ToggleStatusLineAction();
593 menuManager.add(toggleStatusLineAction);
594 }
595
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);
603 }
604
605 /**
606 * Hook that allows to add actions to the context menu.
607 * <p>
608 * Subclasses may extend in order to add other actions.</p>
609 *
610 * @param menuManager the context menu manager
611 * @since 3.5
612 */
613 protected void fillContextMenu(IMenuManager menuManager) {
614 List selectedElements= ((StructuredSelection)getList().getSelection()).toList();
615
616 Object item= null;
617
618 for (Iterator it= selectedElements.iterator(); it.hasNext();) {
619 item= it.next();
620 if (item instanceof ItemsListSeparator || !isHistoryElement(item)) {
621 return;
622 }
623 }
624
625 if (selectedElements.size() > 0) {
626 removeHistoryItemAction.setText(WorkbenchMessages.FilteredItemsSelectionDialog_removeItemsFromHistoryAction);
627
628 menuManager.add(removeHistoryActionContributionItem);
629
630 }
631 }
632
633 private void createPopupMenu() {
634 removeHistoryItemAction = new RemoveHistoryItemAction();
635 removeHistoryActionContributionItem = new ActionContributionItem(
636 removeHistoryItemAction);
637
638 contextMenuManager = new MenuManager();
639 contextMenuManager.setRemoveAllWhenShown(true);
640 contextMenuManager.addMenuListener(new IMenuListener() {
641 @Override
642 public void menuAboutToShow(IMenuManager manager) {
643 fillContextMenu(manager);
644 }
645 });
646
647 final Table table = getList().getTable();
648 Menu menu= contextMenuManager.createContextMenu(table);
649 table.setMenu(menu);
650 }
651
652 /**
653 * Creates an extra content area, which will be located above the details.
654 *
655 * @param parent
656 * parent to create the dialog widgets in
657 * @return an extra content area
658 */
659 protected abstract Control createExtendedContentArea(Composite parent);
660
661 /*
662 * (non-Javadoc)
663 *
664 * @see org.eclipse.jface.dialogs.Dialog#createDialogArea(org.eclipse.swt.widgets.Composite)
665 */
666 @Override
667 protected Control createDialogArea(Composite parent) {
668 Composite dialogArea = (Composite) super.createDialogArea(parent);
669
670 Composite content = new Composite(dialogArea, SWT.NONE);
671 GridData gd = new GridData(GridData.FILL_BOTH);
672 content.setLayoutData(gd);
673
674 GridLayout layout = new GridLayout();
675 layout.numColumns = 1;
676 layout.marginWidth = 0;
677 layout.marginHeight = 0;
678 content.setLayout(layout);
679
680 final Label headerLabel = createHeader(content);
681
682 pattern = new Text(content, SWT.SINGLE | SWT.BORDER | SWT.SEARCH | SWT.ICON_CANCEL);
683 pattern.getAccessible().addAccessibleListener(new AccessibleAdapter() {
684 @Override
685 public void getName(AccessibleEvent e) {
686 e.result = LegacyActionTools.removeMnemonics(headerLabel
687 .getText());
688 }
689 });
690 gd = new GridData(GridData.FILL_HORIZONTAL);
691 pattern.setLayoutData(gd);
692
693 final Label listLabel = createLabels(content);
694
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() {
699 @Override
700 public void getName(AccessibleEvent e) {
701 if (e.childID == ACC.CHILDID_SELF) {
702 e.result = LegacyActionTools
703 .removeMnemonics(listLabel.getText());
704 }
705 }
706 });
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);
715
716 createPopupMenu();
717
718 pattern.addModifyListener(new ModifyListener() {
719 @Override
720 public void modifyText(ModifyEvent e) {
721 applyFilter();
722 }
723 });
724
725 pattern.addKeyListener(new KeyAdapter() {
726 @Override
727 public void keyPressed(KeyEvent e) {
728 if (e.keyCode == SWT.ARROW_DOWN) {
729 if (getList().getTable().getItemCount() > 0) {
730 getList().getTable().setFocus();
731 }
732 }
733 }
734 });
735
736 getList().addSelectionChangedListener(new ISelectionChangedListener() {
737 @Override
738 public void selectionChanged(SelectionChangedEvent event) {
739 StructuredSelection selection = (StructuredSelection) event
740 .getSelection();
741 handleSelected(selection);
742 }
743 });
744
745 getList().addDoubleClickListener(new IDoubleClickListener() {
746 @Override
747 public void doubleClick(DoubleClickEvent event) {
748 handleDoubleClick();
749 }
750 });
751
752 getList().getTable().addKeyListener(new KeyAdapter() {
753 @Override
754 public void keyPressed(KeyEvent e) {
755
756 if (e.keyCode == SWT.DEL) {
757
758 List selectedElements = ((StructuredSelection) getList()
759 .getSelection()).toList();
760
761 Object item = null;
762 boolean isSelectedHistory = true;
763
764 for (Iterator it = selectedElements.iterator(); it
765 .hasNext();) {
766 item = it.next();
767 if (item instanceof ItemsListSeparator
768 || !isHistoryElement(item)) {
769 isSelectedHistory = false;
770 break;
771 }
772 }
773 if (isSelectedHistory) {
774 removeSelectedItems(selectedElements);
775 }
776
777 }
778
779 if (e.keyCode == SWT.ARROW_UP && (e.stateMask & SWT.SHIFT) != 0
780 && (e.stateMask & SWT.CTRL) != 0) {
781 StructuredSelection selection = (StructuredSelection) getList()
782 .getSelection();
783
784 if (selection.size() == 1) {
785 Object element = selection.getFirstElement();
786 if (element.equals(getList().getElementAt(0))) {
787 pattern.setFocus();
788 }
789 if (getList().getElementAt(getList().getTable()
790 .getSelectionIndex() - 1) instanceof ItemsListSeparator) {
791 getList().getTable().setSelection(
792 getList().getTable().getSelectionIndex() - 1);
793 }
794 getList().getTable().notifyListeners(SWT.Selection,
795 new Event());
796
797 }
798 }
799
800 if (e.keyCode == SWT.ARROW_DOWN
801 && (e.stateMask & SWT.SHIFT) != 0
802 && (e.stateMask & SWT.CTRL) != 0) {
803
804 if (getList()
805 .getElementAt(getList().getTable().getSelectionIndex() + 1) instanceof ItemsListSeparator) {
806 getList().getTable().setSelection(
807 getList().getTable().getSelectionIndex() + 1);
808 }
809 getList().getTable().notifyListeners(SWT.Selection, new Event());
810 }
811
812 }
813 });
814
815 createExtendedContentArea(content);
816
817 details = new DetailsContentViewer(content, SWT.BORDER | SWT.FLAT);
818 details.setVisible(toggleStatusLineAction.isChecked());
819 details.setContentProvider(new NullContentProvider());
820 details.setLabelProvider(getDetailsLabelProvider());
821
822 applyDialogFont(content);
823
824 restoreDialog(getDialogSettings());
825
826 if (initialPatternText != null) {
827 pattern.setText(initialPatternText);
828 }
829
830 switch (selectionMode) {
831 case CARET_BEGINNING:
832 pattern.setSelection(0, 0);
833 break;
834 case FULL_SELECTION:
835 pattern.setSelection(0, initialPatternText.length());
836 break;
837 }
838
839 // apply filter even if pattern is empty (display history)
840 applyFilter();
841
842 return dialogArea;
843 }
844
845 /**
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.
849 * <p>
850 * Current implementation makes double-clicking on the list do the same as
851 * pressing <code>OK</code> button on the dialog.
852 */
853 protected void handleDoubleClick() {
854 okPressed();
855 }
856
857 /**
858 * Refreshes the details field according to the current selection in the
859 * items list.
860 */
861 private void refreshDetails() {
862 StructuredSelection selection = getSelectedItems();
863
864 switch (selection.size()) {
865 case 0:
866 details.setInput(null);
867 break;
868 case 1:
869 details.setInput(selection.getFirstElement());
870 break;
871 default:
872 details
873 .setInput(NLS
874 .bind(
875 WorkbenchMessages.FilteredItemsSelectionDialog_nItemsSelected,
876 new Integer(selection.size())));
877 break;
878 }
879
880 }
881
882 /**
883 * Handle selection in the items list by updating labels of selected and
884 * unselected items and refresh the details field using the selection.
885 *
886 * @param selection
887 * the new selection
888 */
889 protected void handleSelected(StructuredSelection selection) {
890 IStatus status = new Status(IStatus.OK, PlatformUI.PLUGIN_ID,
891 IStatus.OK, EMPTY_STRING, null);
892
893 Object[] lastSelection = getCurrentSelection();
894
895 setCurrentSelection(selection.toArray());
896
897 if (selection.size() == 0) {
898 status = new Status(IStatus.ERROR, PlatformUI.PLUGIN_ID,
899 IStatus.ERROR, EMPTY_STRING, null);
900
901 if (lastSelection != null
902 && getListSelectionLabelDecorator() != null) {
903 getList().update(lastSelection, null);
904 }
905
906 setCurrentSelection(null);
907
908 } else {
909 status = new Status(IStatus.ERROR, PlatformUI.PLUGIN_ID,
910 IStatus.ERROR, EMPTY_STRING, null);
911
912 List items = selection.toList();
913
914 Object item = null;
915 IStatus tempStatus = null;
916
917 for (Iterator it = items.iterator(); it.hasNext();) {
918 Object o = it.next();
919
920 if (o instanceof ItemsListSeparator) {
921 continue;
922 }
923
924 item = o;
925 tempStatus = validateItem(item);
926
927 if (tempStatus.isOK()) {
928 status = new Status(IStatus.OK, PlatformUI.PLUGIN_ID,
929 IStatus.OK, EMPTY_STRING, null);
930 } else {
931 status = tempStatus;
932 // if any selected element is not valid status is set to
933 // ERROR
934 break;
935 }
936 }
937
938 if (lastSelection != null
939 && getListSelectionLabelDecorator() != null) {
940 getList().update(lastSelection, null);
941 }
942
943 if (getListSelectionLabelDecorator() != null) {
944 getList().update(getCurrentSelection(), null);
945 }
946 }
947
948 refreshDetails();
949 updateStatus(status);
950 }
951
952 /*
953 * (non-Javadoc)
954 *
955 * @see org.eclipse.jface.window.Dialog#getDialogBoundsSettings()
956 */
957 @Override
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);
965 }
966 return section;
967 }
968
969 /**
970 * Returns the dialog settings. Returned object can't be null.
971 *
972 * @return return dialog settings for this dialog
973 */
974 protected abstract IDialogSettings getDialogSettings();
975
976 /**
977 * Refreshes the dialog - has to be called in UI thread.
978 */
979 public void refresh() {
980 if (getList() != null && !getList().getTable().isDisposed()) {
981
982 List lastRefreshSelection = ((StructuredSelection) getList()
983 .getSelection()).toList();
984 getList().getTable().deselectAll();
985
986 getList().setItemCount(contentProvider.getNumberOfElements());
987 getList().refresh();
988
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));
995 } else {
996 refreshWithLastSelection = true;
997 getList().getTable().setSelection(0);
998 getList().getTable().notifyListeners(SWT.Selection, new Event());
999 }
1000 } else {
1001 getList().setSelection(StructuredSelection.EMPTY);
1002 }
1003
1004 }
1005
1006 scheduleProgressMessageRefresh();
1007 }
1008
1009 /**
1010 * Updates the progress label.
1011 *
1012 * @deprecated
1013 */
1014 @Deprecated
1015 public void updateProgressLabel() {
1016 scheduleProgressMessageRefresh();
1017 }
1018
1019 /**
1020 * Notifies the content provider - fires filtering of content provider
1021 * elements. During the filtering, a separator between history and workspace
1022 * matches is added.
1023 * <p>
1024 * This is a long running operation and should be called in a job.
1025 *
1026 * @param checkDuplicates
1027 * <code>true</code> if data concerning elements duplication
1028 * should be computed - it takes much more time than the standard
1029 * filtering
1030 * @param monitor
1031 * a progress monitor or <code>null</code> if no monitor is
1032 * available
1033 */
1034 public void reloadCache(boolean checkDuplicates, IProgressMonitor monitor) {
1035 if (getList() != null && !getList().getTable().isDisposed()
1036 && contentProvider != null) {
1037 contentProvider.reloadCache(checkDuplicates, monitor);
1038 }
1039 }
1040
1041 /**
1042 * Schedule refresh job.
1043 */
1044 public void scheduleRefresh() {
1045 refreshCacheJob.cancelAll();
1046 refreshCacheJob.schedule();
1047 }
1048
1049 /**
1050 * Schedules progress message refresh.
1051 */
1052 public void scheduleProgressMessageRefresh() {
1053 if (filterJob.getState() != Job.RUNNING
1054 && refreshProgressMessageJob.getState() != Job.RUNNING) {
1055 refreshProgressMessageJob.scheduleProgressRefresh(null);
1056 }
1057 }
1058
1059 /*
1060 * (non-Javadoc)
1061 *
1062 * @see org.eclipse.ui.dialogs.SelectionStatusDialog#computeResult()
1063 */
1064 @Override
1065 protected void computeResult() {
1066
1067 List selectedElements = ((StructuredSelection) getList().getSelection())
1068 .toList();
1069
1070 List objectsToReturn = new ArrayList();
1071
1072 Object item = null;
1073
1074 for (Iterator it = selectedElements.iterator(); it.hasNext();) {
1075 item = it.next();
1076
1077 if (!(item instanceof ItemsListSeparator)) {
1078 accessedHistoryItem(item);
1079 objectsToReturn.add(item);
1080 }
1081 }
1082
1083 setResult(objectsToReturn);
1084 }
1085
1086 /*
1087 * @see org.eclipse.ui.dialogs.SelectionStatusDialog#updateStatus(org.eclipse.core.runtime.IStatus)
1088 */
1089 @Override
1090 protected void updateStatus(IStatus status) {
1091 this.status = status;
1092 super.updateStatus(status);
1093 }
1094
1095 /*
1096 * @see Dialog#okPressed()
1097 */
1098 @Override
1099 protected void okPressed() {
1100 if (status != null
1101 && (status.isOK() || status.getCode() == IStatus.INFO)) {
1102 super.okPressed();
1103 }
1104 }
1105
1106 /**
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
1109 * input field.
1110 *
1111 * @param text
1112 * initial pattern for the filter
1113 * @see FilteredItemsSelectionDialog#FULL_SELECTION
1114 */
1115 public void setInitialPattern(String text) {
1116 setInitialPattern(text, FULL_SELECTION);
1117 }
1118
1119 /**
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.
1123 *
1124 * @param text
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}
1130 */
1131 public void setInitialPattern(String text, int selectionMode) {
1132 this.initialPatternText = text;
1133 this.selectionMode = selectionMode;
1134 }
1135
1136 /**
1137 * Gets initial pattern.
1138 *
1139 * @return initial pattern, or <code>null</code> if initial pattern is not
1140 * set
1141 */
1142 protected String getInitialPattern() {
1143 return this.initialPatternText;
1144 }
1145
1146 /**
1147 * Returns the current selection.
1148 *
1149 * @return the current selection
1150 */
1151 protected StructuredSelection getSelectedItems() {
1152
1153 StructuredSelection selection = (StructuredSelection) getList()
1154 .getSelection();
1155
1156 List selectedItems = selection.toList();
1157 Object itemToRemove = null;
1158
1159 for (Iterator it = selection.iterator(); it.hasNext();) {
1160 Object item = it.next();
1161 if (item instanceof ItemsListSeparator) {
1162 itemToRemove = item;
1163 break;
1164 }
1165 }
1166
1167 if (itemToRemove == null) {
1168 return new StructuredSelection(selectedItems);
1169 }
1170 // Create a new selection without the collision
1171 List newItems = new ArrayList(selectedItems);
1172 newItems.remove(itemToRemove);
1173 return new StructuredSelection(newItems);
1174
1175 }
1176
1177 /**
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.
1181 *
1182 * @param item
1183 * an item to be checked
1184 * @return status of the dialog to be set
1185 */
1186 protected abstract IStatus validateItem(Object item);
1187
1188 /**
1189 * Creates an instance of a filter.
1190 *
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
1193 * the list.
1194 */
1195 protected abstract ItemsFilter createFilter();
1196
1197 /**
1198 *
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
1201 * refiltering.
1202 */
1203 protected void applyFilter() {
1204 // to get an already filtered selection of the database we added the initModel() method here.
1205 initModel();
1206 ItemsFilter newFilter = createFilter();
1207
1208 // don't apply filtering for patterns which mean the same, for example:
1209 // *a**b and ***a*b
1210 if (filter != null && filter.equalsFilter(newFilter)) {
1211 return;
1212 }
1213
1214 filterHistoryJob.cancel();
1215 filterJob.cancel();
1216
1217 this.filter = newFilter;
1218
1219 if (this.filter != null) {
1220 filterHistoryJob.schedule();
1221 }
1222 }
1223
1224 /**
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.
1229 *
1230 * @return comparator to sort items content provider
1231 */
1232 protected abstract Comparator getItemsComparator();
1233
1234 /**
1235 * Fills the content provider with matching items.
1236 *
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
1242 * the items filter
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
1247 */
1248 protected abstract void fillContentProvider(
1249 AbstractContentProvider contentProvider, ItemsFilter itemsFilter,
1250 IProgressMonitor progressMonitor) throws CoreException;
1251
1252 /**
1253 * Removes selected items from history.
1254 *
1255 * @param items
1256 * items to be removed
1257 */
1258 private void removeSelectedItems(List items) {
1259 for (Iterator iter = items.iterator(); iter.hasNext();) {
1260 Object item = iter.next();
1261 removeHistoryItem(item);
1262 }
1263 refreshWithLastSelection = false;
1264 contentProvider.refresh();
1265 }
1266
1267 /**
1268 * Removes an item from history.
1269 *
1270 * @param item
1271 * an item to remove
1272 * @return removed item
1273 */
1274 protected Object removeHistoryItem(Object item) {
1275 return contentProvider.removeHistoryElement(item);
1276 }
1277
1278 /**
1279 * Adds item to history.
1280 *
1281 * @param item
1282 * the item to be added
1283 */
1284 protected void accessedHistoryItem(Object item) {
1285 contentProvider.addHistoryElement(item);
1286 }
1287
1288 /**
1289 * Returns a history comparator.
1290 *
1291 * @return decorated comparator
1292 */
1293 private Comparator getHistoryComparator() {
1294 return new HistoryComparator();
1295 }
1296
1297 /**
1298 * Returns the history of selected elements.
1299 *
1300 * @return history of selected elements, or <code>null</code> if it is not
1301 * set
1302 */
1303 protected SelectionHistory getSelectionHistory() {
1304 return this.contentProvider.getSelectionHistory();
1305 }
1306
1307 /**
1308 * Sets new history.
1309 *
1310 * @param selectionHistory
1311 * the history
1312 */
1313 protected void setSelectionHistory(SelectionHistory selectionHistory) {
1314 if (this.contentProvider != null) {
1315 this.contentProvider.setSelectionHistory(selectionHistory);
1316 }
1317 }
1318
1319 /**
1320 * Indicates whether the given item is a history item.
1321 *
1322 * @param item
1323 * the item to be investigated
1324 * @return <code>true</code> if the given item exists in history,
1325 * <code>false</code> otherwise
1326 */
1327 public boolean isHistoryElement(Object item) {
1328 return this.contentProvider.isHistoryElement(item);
1329 }
1330
1331 /**
1332 * Indicates whether the given item is a duplicate.
1333 *
1334 * @param item
1335 * the item to be investigated
1336 * @return <code>true</code> if the item is duplicate, <code>false</code>
1337 * otherwise
1338 */
1339 public boolean isDuplicateElement(Object item) {
1340 return this.contentProvider.isDuplicateElement(item);
1341 }
1342
1343 /**
1344 * Sets separator label
1345 *
1346 * @param separatorLabel
1347 * the label showed on separator
1348 */
1349 public void setSeparatorLabel(String separatorLabel) {
1350 this.itemsListSeparator = new ItemsListSeparator(separatorLabel);
1351 }
1352
1353 /**
1354 * Returns name for then given object.
1355 *
1356 * @param item
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
1363 */
1364 public abstract String getElementName(Object item);
1365
1366 private class ToggleStatusLineAction extends Action {
1367
1368 /**
1369 * Creates a new instance of the class.
1370 */
1371 public ToggleStatusLineAction() {
1372 super(
1373 WorkbenchMessages.FilteredItemsSelectionDialog_toggleStatusAction,
1374 IAction.AS_CHECK_BOX);
1375 }
1376
1377 @Override
1378 public void run() {
1379 details.setVisible(isChecked());
1380 }
1381 }
1382
1383 /**
1384 * Only refreshes UI on the basis of an already sorted and filtered set of
1385 * items.
1386 * <p>
1387 * Standard invocation scenario:
1388 * <ol>
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>
1398 * class)</li>
1399 * </ol>
1400 * The scenario is rather complicated, but it had to be applied, because:
1401 * <ul>
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>
1408 * </ul>
1409 *
1410 * @see org.eclipse.ui.dialogs.FilteredItemsSelectionDialog.FilterJob
1411 * @see org.eclipse.ui.dialogs.FilteredItemsSelectionDialog.RefreshJob
1412 * @see org.eclipse.ui.dialogs.FilteredItemsSelectionDialog.RefreshCacheJob
1413 */
1414 private class RefreshJob extends UIJob {
1415
1416 /**
1417 * Creates a new instance of the class.
1418 */
1419 public RefreshJob() {
1420 super(CdmFilteredItemsSelectionDialog.this.getParentShell()
1421 .getDisplay(),
1422 WorkbenchMessages.FilteredItemsSelectionDialog_refreshJob);
1423 setSystem(true);
1424 }
1425
1426 /*
1427 * (non-Javadoc)
1428 *
1429 * @see org.eclipse.ui.progress.UIJob#runInUIThread(org.eclipse.core.runtime.IProgressMonitor)
1430 */
1431 @Override
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);
1436 }
1437
1438 if (CdmFilteredItemsSelectionDialog.this != null) {
1439 CdmFilteredItemsSelectionDialog.this.refresh();
1440 }
1441
1442 return new Status(IStatus.OK, PlatformUI.PLUGIN_ID, IStatus.OK,
1443 EMPTY_STRING, null);
1444 }
1445
1446 }
1447
1448 /**
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.
1453 */
1454 private class RefreshProgressMessageJob extends UIJob {
1455
1456 private GranualProgressMonitor progressMonitor;
1457
1458 /**
1459 * Creates a new instance of the class.
1460 */
1461 public RefreshProgressMessageJob() {
1462 super(
1463 CdmFilteredItemsSelectionDialog.this.getParentShell()
1464 .getDisplay(),
1465 WorkbenchMessages.FilteredItemsSelectionDialog_progressRefreshJob);
1466 setSystem(true);
1467 }
1468
1469 /*
1470 * (non-Javadoc)
1471 *
1472 * @see org.eclipse.ui.progress.UIJob#runInUIThread(org.eclipse.core.runtime.IProgressMonitor)
1473 */
1474 @Override
1475 public IStatus runInUIThread(IProgressMonitor monitor) {
1476
1477 if (!progressLabel.isDisposed()) {
1478 progressLabel.setText(progressMonitor != null ? progressMonitor
1479 .getMessage() : EMPTY_STRING);
1480 }
1481
1482 if (progressMonitor == null || progressMonitor.isDone()) {
1483 return new Status(IStatus.CANCEL, PlatformUI.PLUGIN_ID,
1484 IStatus.CANCEL, EMPTY_STRING, null);
1485 }
1486
1487 // Schedule cyclical with 500 milliseconds delay
1488 schedule(500);
1489
1490 return new Status(IStatus.OK, PlatformUI.PLUGIN_ID, IStatus.OK,
1491 EMPTY_STRING, null);
1492 }
1493
1494 /**
1495 * Schedule progress refresh job.
1496 *
1497 * @param progressMonitor
1498 * used during refresh progress label
1499 */
1500 public void scheduleProgressRefresh(
1501 GranualProgressMonitor progressMonitor) {
1502 this.progressMonitor = progressMonitor;
1503 // Schedule with initial delay to avoid flickering when the user
1504 // types quickly
1505 schedule(200);
1506 }
1507
1508 }
1509
1510 /**
1511 * A job responsible for computing filtered items list presented using
1512 * <code>RefreshJob</code>.
1513 *
1514 * @see FilteredItemsSelectionDialog.RefreshJob
1515 *
1516 */
1517 private class RefreshCacheJob extends Job {
1518
1519 private final RefreshJob refreshJob = new RefreshJob();
1520
1521 /**
1522 * Creates a new instance of the class.
1523 */
1524 public RefreshCacheJob() {
1525 super(
1526 WorkbenchMessages.FilteredItemsSelectionDialog_cacheRefreshJob);
1527 setSystem(true);
1528 }
1529
1530 /**
1531 * Stops the job and all sub-jobs.
1532 */
1533 public void cancelAll() {
1534 cancel();
1535 refreshJob.cancel();
1536 }
1537
1538 /*
1539 * (non-Javadoc)
1540 *
1541 * @see org.eclipse.core.runtime.jobs.Job#run(org.eclipse.core.runtime.IProgressMonitor)
1542 */
1543 @Override
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);
1548 }
1549
1550 if (CdmFilteredItemsSelectionDialog.this != null) {
1551 GranualProgressMonitor wrappedMonitor = new GranualProgressMonitor(
1552 monitor);
1553 CdmFilteredItemsSelectionDialog.this.reloadCache(true,
1554 wrappedMonitor);
1555 }
1556
1557 if (!monitor.isCanceled()) {
1558 refreshJob.schedule();
1559 }
1560
1561 return new Status(IStatus.OK, PlatformUI.PLUGIN_ID, IStatus.OK,
1562 EMPTY_STRING, null);
1563
1564 }
1565
1566 /*
1567 * (non-Javadoc)
1568 *
1569 * @see org.eclipse.core.runtime.jobs.Job#canceling()
1570 */
1571 @Override
1572 protected void canceling() {
1573 super.canceling();
1574 contentProvider.stopReloadingCache();
1575 }
1576
1577 }
1578
1579 private class RemoveHistoryItemAction extends Action {
1580
1581 /**
1582 * Creates a new instance of the class.
1583 */
1584 public RemoveHistoryItemAction() {
1585 super(
1586 WorkbenchMessages.FilteredItemsSelectionDialog_removeItemsFromHistoryAction);
1587 }
1588
1589 /*
1590 * (non-Javadoc)
1591 *
1592 * @see org.eclipse.jface.action.Action#run()
1593 */
1594 @Override
1595 public void run() {
1596 List selectedElements = ((StructuredSelection) getList().getSelection())
1597 .toList();
1598 removeSelectedItems(selectedElements);
1599 }
1600 }
1601
1602 protected static boolean showColoredLabels() {
1603 return PlatformUI.getPreferenceStore().getBoolean(IWorkbenchPreferenceConstants.USE_COLORED_LABELS);
1604 }
1605
1606 private class ItemsListLabelProvider extends StyledCellLabelProvider
1607 implements ILabelProviderListener {
1608 private ILabelProvider provider;
1609
1610 private ILabelDecorator selectionDecorator;
1611
1612 // Need to keep our own list of listeners
1613 private final ListenerList listeners = new ListenerList();
1614
1615 /**
1616 * Creates a new instance of the class.
1617 *
1618 * @param provider
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>
1622 */
1623 public ItemsListLabelProvider(ILabelProvider provider,
1624 ILabelDecorator selectionDecorator) {
1625 Assert.isNotNull(provider);
1626 this.provider = provider;
1627 this.selectionDecorator = selectionDecorator;
1628
1629 setOwnerDrawEnabled(showColoredLabels() && provider instanceof IStyledLabelProvider);
1630
1631 provider.addListener(this);
1632
1633 if (selectionDecorator != null) {
1634 selectionDecorator.addListener(this);
1635 }
1636 }
1637
1638 /**
1639 * Sets new selection decorator.
1640 *
1641 * @param newSelectionDecorator
1642 * new label decorator for selected items in the list
1643 */
1644 public void setSelectionDecorator(ILabelDecorator newSelectionDecorator) {
1645 if (selectionDecorator != null) {
1646 selectionDecorator.removeListener(this);
1647 selectionDecorator.dispose();
1648 }
1649
1650 selectionDecorator = newSelectionDecorator;
1651
1652 if (selectionDecorator != null) {
1653 selectionDecorator.addListener(this);
1654 }
1655 }
1656
1657 /**
1658 * Gets selection decorator.
1659 *
1660 * @return the label decorator for selected items in the list
1661 */
1662 public ILabelDecorator getSelectionDecorator() {
1663 return selectionDecorator;
1664 }
1665
1666 /**
1667 * Sets new label provider.
1668 *
1669 * @param newProvider
1670 * new label provider for items in the list, not
1671 * <code>null</code>
1672 */
1673 public void setProvider(ILabelProvider newProvider) {
1674 Assert.isNotNull(newProvider);
1675 provider.removeListener(this);
1676 provider.dispose();
1677
1678 provider = newProvider;
1679
1680 if (provider != null) {
1681 provider.addListener(this);
1682 }
1683
1684 setOwnerDrawEnabled(showColoredLabels() && provider instanceof IStyledLabelProvider);
1685 }
1686
1687 private Image getImage(Object element) {
1688 if (element instanceof ItemsListSeparator) {
1689 return WorkbenchImages
1690 .getImage(IWorkbenchGraphicConstants.IMG_OBJ_SEPARATOR);
1691 }
1692
1693 return provider.getImage(element);
1694 }
1695
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])) {
1700 return true;
1701 }
1702 }
1703 }
1704 return false;
1705 }
1706
1707 /*
1708 * (non-Javadoc)
1709 *
1710 * @see org.eclipse.jface.viewers.ILabelProvider#getText(java.lang.Object)
1711 */
1712 private String getText(Object element) {
1713 if (element instanceof ItemsListSeparator) {
1714 return getSeparatorLabel(((ItemsListSeparator) element)
1715 .getName());
1716 }
1717
1718 String str = provider.getText(element);
1719 if (selectionDecorator != null && isSelected(element)) {
1720 return selectionDecorator.decorateText(str.toString(), element);
1721 }
1722
1723 return str;
1724 }
1725
1726 private StyledString getStyledText(Object element,
1727 IStyledLabelProvider provider) {
1728 StyledString string = provider.getStyledText(element);
1729
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
1735 }
1736 return string;
1737 }
1738
1739 @Override
1740 public void update(ViewerCell cell) {
1741 Object element = cell.getElement();
1742
1743 if (!(element instanceof ItemsListSeparator)
1744 && provider instanceof IStyledLabelProvider) {
1745 IStyledLabelProvider styledLabelProvider = (IStyledLabelProvider) provider;
1746 StyledString styledString = getStyledText(element,
1747 styledLabelProvider);
1748
1749 cell.setText(styledString.getString());
1750 cell.setStyleRanges(styledString.getStyleRanges());
1751 cell.setImage(styledLabelProvider.getImage(element));
1752 } else {
1753 cell.setText(getText(element));
1754 cell.setImage(getImage(element));
1755 }
1756 cell.setFont(getFont(element));
1757 cell.setForeground(getForeground(element));
1758 cell.setBackground(getBackground(element));
1759
1760 super.update(cell);
1761 }
1762
1763 private String getSeparatorLabel(String separatorLabel) {
1764 Rectangle rect = getList().getTable().getBounds();
1765
1766 int borderWidth = getList().getTable().computeTrim(0, 0, 0, 0).width;
1767
1768 int imageWidth = WorkbenchImages.getImage(
1769 IWorkbenchGraphicConstants.IMG_OBJ_SEPARATOR).getBounds().width;
1770
1771 int width = rect.width - borderWidth - imageWidth;
1772
1773 GC gc = new GC(getList().getTable());
1774 gc.setFont(getList().getTable().getFont());
1775
1776 int fSeparatorWidth = gc.getAdvanceWidth('-');
1777 int fMessageLength = gc.textExtent(separatorLabel).x;
1778
1779 gc.dispose();
1780
1781 StringBuffer dashes = new StringBuffer();
1782 int chars = (((width - fMessageLength) / fSeparatorWidth) / 2) - 2;
1783 for (int i = 0; i < chars; i++) {
1784 dashes.append('-');
1785 }
1786
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();
1792 }
1793
1794 /*
1795 * (non-Javadoc)
1796 *
1797 * @see org.eclipse.jface.viewers.IBaseLabelProvider#addListener(org.eclipse.jface.viewers.ILabelProviderListener)
1798 */
1799 @Override
1800 public void addListener(ILabelProviderListener listener) {
1801 listeners.add(listener);
1802 }
1803
1804 /*
1805 * (non-Javadoc)
1806 *
1807 * @see org.eclipse.jface.viewers.IBaseLabelProvider#dispose()
1808 */
1809 @Override
1810 public void dispose() {
1811 provider.removeListener(this);
1812 provider.dispose();
1813
1814 if (selectionDecorator != null) {
1815 selectionDecorator.removeListener(this);
1816 selectionDecorator.dispose();
1817 }
1818
1819 super.dispose();
1820 }
1821
1822 /*
1823 * (non-Javadoc)
1824 *
1825 * @see org.eclipse.jface.viewers.IBaseLabelProvider#isLabelProperty(java.lang.Object,
1826 * java.lang.String)
1827 */
1828 @Override
1829 public boolean isLabelProperty(Object element, String property) {
1830 if (provider.isLabelProperty(element, property)) {
1831 return true;
1832 }
1833 if (selectionDecorator != null
1834 && selectionDecorator.isLabelProperty(element, property)) {
1835 return true;
1836 }
1837 return false;
1838 }
1839
1840 /*
1841 * (non-Javadoc)
1842 *
1843 * @see org.eclipse.jface.viewers.IBaseLabelProvider#removeListener(org.eclipse.jface.viewers.ILabelProviderListener)
1844 */
1845 @Override
1846 public void removeListener(ILabelProviderListener listener) {
1847 listeners.remove(listener);
1848 }
1849
1850 private Color getBackground(Object element) {
1851 if (element instanceof ItemsListSeparator) {
1852 return null;
1853 }
1854 if (provider instanceof IColorProvider) {
1855 return ((IColorProvider) provider).getBackground(element);
1856 }
1857 return null;
1858 }
1859
1860 private Color getForeground(Object element) {
1861 if (element instanceof ItemsListSeparator) {
1862 return Display.getCurrent().getSystemColor(
1863 SWT.COLOR_WIDGET_NORMAL_SHADOW);
1864 }
1865 if (provider instanceof IColorProvider) {
1866 return ((IColorProvider) provider).getForeground(element);
1867 }
1868 return null;
1869 }
1870
1871 private Font getFont(Object element) {
1872 if (element instanceof ItemsListSeparator) {
1873 return null;
1874 }
1875 if (provider instanceof IFontProvider) {
1876 return ((IFontProvider) provider).getFont(element);
1877 }
1878 return null;
1879 }
1880
1881 /*
1882 * (non-Javadoc)
1883 *
1884 * @see org.eclipse.jface.viewers.ILabelProviderListener#labelProviderChanged(org.eclipse.jface.viewers.LabelProviderChangedEvent)
1885 */
1886 @Override
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);
1891 }
1892 }
1893 }
1894
1895 /**
1896 * Used in ItemsListContentProvider, separates history and non-history
1897 * items.
1898 */
1899 protected class ItemsListSeparator {
1900
1901 private final String name;
1902
1903 /**
1904 * Creates a new instance of the class.
1905 *
1906 * @param name
1907 * the name of the separator
1908 */
1909 public ItemsListSeparator(String name) {
1910 this.name = name;
1911 }
1912
1913 /**
1914 * Returns the name of this separator.
1915 *
1916 * @return the name of the separator
1917 */
1918 public String getName() {
1919 return name;
1920 }
1921 }
1922
1923 /**
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.
1928 *
1929 */
1930 private class GranualProgressMonitor extends ProgressMonitorWrapper {
1931
1932 private String name;
1933
1934 private String subName;
1935
1936 private int totalWork;
1937
1938 private double worked;
1939
1940 private boolean done;
1941
1942 /**
1943 * Creates instance of <code>GranualProgressMonitor</code>.
1944 *
1945 * @param monitor
1946 * progress to be wrapped
1947 */
1948 public GranualProgressMonitor(IProgressMonitor monitor) {
1949 super(monitor);
1950 }
1951
1952 /**
1953 * Checks if filtering has been done
1954 *
1955 * @return true if filtering work has been done false in other way
1956 */
1957 public boolean isDone() {
1958 return done;
1959 }
1960
1961 /*
1962 * (non-Javadoc)
1963 *
1964 * @see org.eclipse.core.runtime.ProgressMonitorWrapper#setTaskName(java.lang.String)
1965 */
1966 @Override
1967 public void setTaskName(String name) {
1968 super.setTaskName(name);
1969 this.name = name;
1970 this.subName = null;
1971 }
1972
1973 /*
1974 * (non-Javadoc)
1975 *
1976 * @see org.eclipse.core.runtime.ProgressMonitorWrapper#subTask(java.lang.String)
1977 */
1978 @Override
1979 public void subTask(String name) {
1980 super.subTask(name);
1981 this.subName = name;
1982 }
1983
1984 /*
1985 * (non-Javadoc)
1986 *
1987 * @see org.eclipse.core.runtime.ProgressMonitorWrapper#beginTask(java.lang.String,
1988 * int)
1989 */
1990 @Override
1991 public void beginTask(String name, int totalWork) {
1992 super.beginTask(name, totalWork);
1993 if (this.name == null) {
1994 this.name = name;
1995 }
1996 this.totalWork = totalWork;
1997 refreshProgressMessageJob.scheduleProgressRefresh(this);
1998 }
1999
2000 /*
2001 * (non-Javadoc)
2002 *
2003 * @see org.eclipse.core.runtime.ProgressMonitorWrapper#worked(int)
2004 */
2005 @Override
2006 public void worked(int work) {
2007 super.worked(work);
2008 internalWorked(work);
2009 }
2010
2011 /*
2012 * (non-Javadoc)
2013 *
2014 * @see org.eclipse.core.runtime.ProgressMonitorWrapper#done()
2015 */
2016 @Override
2017 public void done() {
2018 done = true;
2019 super.done();
2020 }
2021
2022 /*
2023 * (non-Javadoc)
2024 *
2025 * @see org.eclipse.core.runtime.ProgressMonitorWrapper#setCanceled(boolean)
2026 */
2027 @Override
2028 public void setCanceled(boolean b) {
2029 done = b;
2030 super.setCanceled(b);
2031 }
2032
2033 /*
2034 * (non-Javadoc)
2035 *
2036 * @see org.eclipse.core.runtime.ProgressMonitorWrapper#internalWorked(double)
2037 */
2038 @Override
2039 public void internalWorked(double work) {
2040 worked = worked + work;
2041 }
2042
2043 private String getMessage() {
2044 if (done) {
2045 return ""; //$NON-NLS-1$
2046 }
2047
2048 String message;
2049
2050 if (name == null) {
2051 message = subName == null ? "" : subName; //$NON-NLS-1$
2052 } else {
2053 message = subName == null ? name
2054 : NLS
2055 .bind(
2056 WorkbenchMessages.FilteredItemsSelectionDialog_subtaskProgressMessage,
2057 new Object[] { name, subName });
2058 }
2059 if (totalWork == 0) {
2060 return message;
2061 }
2062
2063 return NLS
2064 .bind(
2065 WorkbenchMessages.FilteredItemsSelectionDialog_taskProgressMessage,
2066 new Object[] {
2067 message,
2068 new Integer(
2069 (int) ((worked * 100) / totalWork)) });
2070
2071 }
2072
2073 }
2074
2075 /**
2076 * Filters items history and schedule filter job.
2077 */
2078 private class FilterHistoryJob extends Job {
2079
2080 /**
2081 * Filter used during the filtering process.
2082 */
2083 private ItemsFilter itemsFilter;
2084
2085 /**
2086 * Creates new instance of receiver.
2087 */
2088 public FilterHistoryJob() {
2089 super(WorkbenchMessages.FilteredItemsSelectionDialog_jobLabel);
2090 setSystem(true);
2091 }
2092
2093 /*
2094 * (non-Javadoc)
2095 *
2096 * @see org.eclipse.core.runtime.jobs.Job#run(org.eclipse.core.runtime.IProgressMonitor)
2097 */
2098 @Override
2099 protected IStatus run(IProgressMonitor monitor) {
2100
2101 this.itemsFilter = filter;
2102
2103 contentProvider.reset();
2104
2105 refreshWithLastSelection = false;
2106
2107 contentProvider.addHistoryItems(itemsFilter);
2108
2109 if (!(lastCompletedFilter != null && lastCompletedFilter
2110 .isSubFilter(this.itemsFilter))) {
2111 contentProvider.refresh();
2112 }
2113
2114 filterJob.schedule();
2115
2116 return Status.OK_STATUS;
2117 }
2118
2119 }
2120
2121 /**
2122 * Filters items in indicated set and history. During filtering, it
2123 * refreshes the dialog (progress monitor and elements list).
2124 *
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.
2132 */
2133 private class FilterJob extends Job {
2134
2135 /**
2136 * Filter used during the filtering process.
2137 */
2138 protected ItemsFilter itemsFilter;
2139
2140 /**
2141 * Creates new instance of FilterJob
2142 */
2143 public FilterJob() {
2144 super(WorkbenchMessages.FilteredItemsSelectionDialog_jobLabel);
2145 setSystem(true);
2146 }
2147
2148 /*
2149 * (non-Javadoc)
2150 *
2151 * @see org.eclipse.core.runtime.jobs.Job#run(org.eclipse.core.runtime.IProgressMonitor)
2152 */
2153 @Override
2154 protected final IStatus run(IProgressMonitor parent) {
2155 GranualProgressMonitor monitor = new GranualProgressMonitor(parent);
2156 return doRun(monitor);
2157 }
2158
2159 /**
2160 * Executes job using the given filtering progress monitor. A hook for
2161 * subclasses.
2162 *
2163 * @param monitor
2164 * progress monitor
2165 * @return result of the execution
2166 */
2167 protected IStatus doRun(GranualProgressMonitor monitor) {
2168 try {
2169 internalRun(monitor);
2170 } catch (CoreException e) {
2171 cancel();
2172 return new Status(
2173 IStatus.ERROR,
2174 PlatformUI.PLUGIN_ID,
2175 IStatus.ERROR,
2176 WorkbenchMessages.FilteredItemsSelectionDialog_jobError,
2177 e);
2178 }
2179 return Status.OK_STATUS;
2180 }
2181
2182 /**
2183 * Main method for the job.
2184 *
2185 * @param monitor
2186 * @throws CoreException
2187 */
2188 private void internalRun(GranualProgressMonitor monitor)
2189 throws CoreException {
2190 try {
2191 if (monitor.isCanceled()) {
2192 return;
2193 }
2194
2195 this.itemsFilter = filter;
2196
2197 if (filter.getPattern().length() != 0) {
2198 filterContent(monitor);
2199 }
2200
2201 if (monitor.isCanceled()) {
2202 return;
2203 }
2204
2205 contentProvider.refresh();
2206 } finally {
2207 monitor.done();
2208 }
2209 }
2210
2211 /**
2212 * Filters items.
2213 *
2214 * @param monitor
2215 * for monitoring progress
2216 * @throws CoreException
2217 */
2218 protected void filterContent(GranualProgressMonitor monitor)
2219 throws CoreException {
2220
2221 // if (lastCompletedFilter != null
2222 // && lastCompletedFilter.isSubFilter(this.itemsFilter)) {
2223 //
2224 // int length = lastCompletedResult.size() / 500;
2225 // monitor
2226 // .beginTask(
2227 // WorkbenchMessages.FilteredItemsSelectionDialog_cacheSearchJob_taskName,
2228 // length);
2229 //
2230 // for (int pos = 0; pos < lastCompletedResult.size(); pos++) {
2231 //
2232 // Object item = lastCompletedResult.get(pos);
2233 // if (monitor.isCanceled()) {
2234 // break;
2235 // }
2236 // contentProvider.add(item, itemsFilter);
2237 //
2238 // if ((pos % 500) == 0) {
2239 // monitor.worked(1);
2240 // }
2241 // }
2242 //
2243 // } else {
2244
2245 lastCompletedFilter = null;
2246 lastCompletedResult = null;
2247
2248 SubProgressMonitor subMonitor = null;
2249 if (monitor != null) {
2250 monitor
2251 .beginTask(
2252 WorkbenchMessages.FilteredItemsSelectionDialog_searchJob_taskName,
2253 100);
2254 subMonitor = new SubProgressMonitor(monitor, 95);
2255
2256 }
2257
2258 fillContentProvider(contentProvider, itemsFilter, subMonitor);
2259
2260 if (monitor != null && !monitor.isCanceled()) {
2261 monitor.worked(2);
2262 contentProvider.rememberResult(itemsFilter);
2263 monitor.worked(3);
2264 }
2265 //}
2266
2267 }
2268
2269 }
2270
2271 /**
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>.
2276 * <p>
2277 * The history can be stored to/loaded from an XML file.
2278 */
2279 protected static abstract class SelectionHistory {
2280
2281 private static final String DEFAULT_ROOT_NODE_NAME = "historyRootNode"; //$NON-NLS-1$
2282
2283 private static final String DEFAULT_INFO_NODE_NAME = "infoNode"; //$NON-NLS-1$
2284
2285 private static final int MAX_HISTORY_SIZE = 60;
2286
2287 private final Set historyList;
2288
2289 private final String rootNodeName;
2290
2291 private final String infoNodeName;
2292
2293 private SelectionHistory(String rootNodeName, String infoNodeName) {
2294
2295 historyList = Collections.synchronizedSet(new LinkedHashSet() {
2296
2297 private static final long serialVersionUID = 0L;
2298
2299 /*
2300 * (non-Javadoc)
2301 *
2302 * @see java.util.LinkedList#add(java.lang.Object)
2303 */
2304 @Override
2305 public boolean add(Object arg0) {
2306 if (this.size() >= MAX_HISTORY_SIZE) {
2307 Iterator iterator = this.iterator();
2308 iterator.next();
2309 iterator.remove();
2310 }
2311 return super.add(arg0);
2312 }
2313
2314 });
2315
2316 this.rootNodeName = rootNodeName;
2317 this.infoNodeName = infoNodeName;
2318 }
2319
2320 /**
2321 * Creates new instance of <code>SelectionHistory</code>.
2322 */
2323 public SelectionHistory() {
2324 this(DEFAULT_ROOT_NODE_NAME, DEFAULT_INFO_NODE_NAME);
2325 }
2326
2327 /**
2328 * Adds object to history.
2329 *
2330 * @param object
2331 * the item to be added to the history
2332 */
2333 public synchronized void accessed(Object object) {
2334 historyList.remove(object);
2335 historyList.add(object);
2336 }
2337
2338 /**
2339 * Returns <code>true</code> if history contains object.
2340 *
2341 * @param 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
2345 */
2346 public synchronized boolean contains(Object object) {
2347 return historyList.contains(object);
2348 }
2349
2350 /**
2351 * Returns <code>true</code> if history is empty.
2352 *
2353 * @return <code>true</code> if history is empty
2354 */
2355 public synchronized boolean isEmpty() {
2356 return historyList.isEmpty();
2357 }
2358
2359 /**
2360 * Remove element from history.
2361 *
2362 * @param element
2363 * to remove form the history
2364 * @return <code>true</code> if this list contained the specified
2365 * element
2366 */
2367 public synchronized boolean remove(Object element) {
2368 return historyList.remove(element);
2369 }
2370
2371 /**
2372 * Load history elements from memento.
2373 *
2374 * @param memento
2375 * memento from which the history will be retrieved
2376 */
2377 public void load(IMemento memento) {
2378
2379 XMLMemento historyMemento = (XMLMemento) memento
2380 .getChild(rootNodeName);
2381
2382 if (historyMemento == null) {
2383 return;
2384 }
2385
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);
2393 }
2394 }
2395 }
2396
2397 /**
2398 * Save history elements to memento.
2399 *
2400 * @param memento
2401 * memento to which the history will be added
2402 */
2403 public void save(IMemento memento) {
2404
2405 IMemento historyMemento = memento.createChild(rootNodeName);
2406
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);
2413 }
2414
2415 }
2416
2417 /**
2418 * Gets array of history items.
2419 *
2420 * @return array of history elements
2421 */
2422 public synchronized Object[] getHistoryItems() {
2423 return historyList.toArray();
2424 }
2425
2426 /**
2427 * Creates an object using given memento.
2428 *
2429 * @param memento
2430 * memento used for creating new object
2431 *
2432 * @return the restored object
2433 */
2434 protected abstract Object restoreItemFromMemento(IMemento memento);
2435
2436 /**
2437 * Store object in <code>IMemento</code>.
2438 *
2439 * @param item
2440 * the item to store
2441 * @param memento
2442 * the memento to store to
2443 */
2444 protected abstract void storeItemToMemento(Object item, IMemento memento);
2445
2446 }
2447
2448 /**
2449 * Filters elements using SearchPattern by comparing the names of items with
2450 * the filter pattern.
2451 */
2452 protected abstract class ItemsFilter {
2453
2454 protected SearchPattern patternMatcher;
2455
2456 /**
2457 * Creates new instance of ItemsFilter.
2458 */
2459 public ItemsFilter() {
2460 this(new SearchPattern());
2461 }
2462
2463 /**
2464 * Creates new instance of ItemsFilter.
2465 *
2466 * @param searchPattern
2467 * the pattern to be used when filtering
2468 */
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();
2474 }
2475 patternMatcher.setPattern(stringPattern);
2476 }
2477
2478 /**
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.
2482 * <p>
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
2486 * vice-versa. </i>
2487 * </p>
2488 *
2489 * @param filter
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>
2494 *
2495 * @see org.eclipse.ui.dialogs.SearchPattern#isSubPattern(org.eclipse.ui.dialogs.SearchPattern)
2496 */
2497 public boolean isSubFilter(ItemsFilter filter) {
2498 if (filter != null) {
2499 return this.patternMatcher.isSubPattern(filter.patternMatcher);
2500 }
2501 return false;
2502 }
2503
2504 /**
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.
2508 *
2509 * @param 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>
2514 *
2515 * @see org.eclipse.ui.dialogs.SearchPattern#equalsPattern(org.eclipse.ui.dialogs.SearchPattern)
2516 */
2517 public boolean equalsFilter(ItemsFilter filter) {
2518 if (filter != null
2519 && filter.patternMatcher.equalsPattern(this.patternMatcher)) {
2520 return true;
2521 }
2522 return false;
2523 }
2524
2525 /**
2526 * Checks whether the pattern's match rule is camel case.
2527 *
2528 * @return <code>true</code> if pattern's match rule is camel case,
2529 * <code>false</code> otherwise
2530 */
2531 public boolean isCamelCasePattern() {
2532 return patternMatcher.getMatchRule() == SearchPattern.RULE_CAMELCASE_MATCH;
2533 }
2534
2535 /**
2536 * Returns the pattern string.
2537 *
2538 * @return pattern for this filter
2539 *
2540 * @see SearchPattern#getPattern()
2541 */
2542 public String getPattern() {
2543 return patternMatcher.getPattern();
2544 }
2545
2546 /**
2547 * Returns the rule to apply for matching keys.
2548 *
2549 * @return an implementation-specific match rule
2550 *
2551 * @see SearchPattern#getMatchRule() for match rules returned by the
2552 * default implementation
2553 */
2554 public int getMatchRule() {
2555 return patternMatcher.getMatchRule();
2556 }
2557
2558 /**
2559 * Matches text with filter.
2560 *
2561 * @param text
2562 * the text to match with the filter
2563 * @return <code>true</code> if text matches with filter pattern,
2564 * <code>false</code> otherwise
2565 */
2566 protected boolean matches(String text) {
2567 return patternMatcher.matches(text);
2568 }
2569
2570 /**
2571 * General method for matching raw name pattern. Checks whether current
2572 * pattern is prefix of name provided item.
2573 *
2574 * @param item
2575 * item to check
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.
2579 */
2580 public boolean matchesRawNamePattern(Object item) {
2581 String prefix = patternMatcher.getPattern();
2582 String text = getElementName(item);
2583
2584 if (text == null) {
2585 return false;
2586 }
2587
2588 int textLength = text.length();
2589 int prefixLength = prefix.length();
2590 if (textLength < prefixLength) {
2591 return false;
2592 }
2593 for (int i = prefixLength - 1; i >= 0; i--) {
2594 if (Character.toLowerCase(prefix.charAt(i)) != Character
2595 .toLowerCase(text.charAt(i))) {
2596 return false;
2597 }
2598 }
2599 return true;
2600 }
2601
2602 /**
2603 * Matches an item against filter conditions.
2604 *
2605 * @param item
2606 * @return <code>true<code> if item matches against filter conditions, <code>false</code>
2607 * otherwise
2608 */
2609 public abstract boolean matchItem(Object item);
2610
2611 /**
2612 * Checks consistency of an item. Item is inconsistent if was changed or
2613 * removed.
2614 *
2615 * @param item
2616 * @return <code>true</code> if item is consistent, <code>false</code>
2617 * if item is inconsistent
2618 */
2619 public abstract boolean isConsistentItem(Object item);
2620
2621 }
2622
2623 /**
2624 * An interface to content providers for
2625 * <code>FilterItemsSelectionDialog</code>.
2626 */
2627 protected abstract class AbstractContentProvider {
2628 /**
2629 * Adds the item to the content provider iff the filter matches the
2630 * item. Otherwise does nothing.
2631 *
2632 * @param item
2633 * the item to add
2634 * @param itemsFilter
2635 * the filter
2636 *
2637 * @see FilteredItemsSelectionDialog.ItemsFilter#matchItem(Object)
2638 */
2639 public abstract void add(Object item, ItemsFilter itemsFilter);
2640 }
2641
2642 /**
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>.
2649 * <p>
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
2656 * filtered list.
2657 */
2658 private class ContentProvider extends AbstractContentProvider implements
2659 IStructuredContentProvider, ILazyContentProvider {
2660
2661 private SelectionHistory selectionHistory;
2662
2663 /**
2664 * Raw result of the searching (unsorted, unfiltered).
2665 * <p>
2666 * Standard object flow:
2667 * <code>items -> lastSortedItems -> lastFilteredItems</code>
2668 */
2669 private final Set items;
2670
2671 /**
2672 * Items that are duplicates.
2673 */
2674 private final Set duplicates;
2675
2676 /**
2677 * List of <code>ViewerFilter</code>s to be used during filtering
2678 */
2679 private List filters;
2680
2681 /**
2682 * Result of the last filtering.
2683 * <p>
2684 * Standard object flow:
2685 * <code>items -> lastSortedItems -> lastFilteredItems</code>
2686 */
2687 private List lastFilteredItems;
2688
2689 /**
2690 * Result of the last sorting.
2691 * <p>
2692 * Standard object flow:
2693 * <code>items -> lastSortedItems -> lastFilteredItems</code>
2694 */
2695 private final List lastSortedItems;
2696
2697 /**
2698 * Used for <code>getFilteredItems()</code> method canceling (when the
2699 * job that invoked the method was canceled).
2700 * <p>
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.
2705 */
2706 private boolean reset;
2707
2708 /**
2709 * Creates new instance of <code>ContentProvider</code>.
2710 */
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(
2716 2048));
2717 }
2718
2719 /**
2720 * Sets selection history.
2721 *
2722 * @param selectionHistory
2723 * The selectionHistory to set.
2724 */
2725 public void setSelectionHistory(SelectionHistory selectionHistory) {
2726 this.selectionHistory = selectionHistory;
2727 }
2728
2729 /**
2730 * @return Returns the selectionHistory.
2731 */
2732 public SelectionHistory getSelectionHistory() {
2733 return selectionHistory;
2734 }
2735
2736 /**
2737 * Removes all content items and resets progress message.
2738 */
2739 public void reset() {
2740 reset = true;
2741 this.items.clear();
2742 this.duplicates.clear();
2743 this.lastSortedItems.clear();
2744 }
2745
2746 /**
2747 * Stops reloading cache - <code>getFilteredItems()</code> method.
2748 */
2749 public void stopReloadingCache() {
2750 reset = true;
2751 }
2752
2753 /**
2754 * Adds filtered item.
2755 *
2756 * @param item
2757 * @param itemsFilter
2758 */
2759 @Override
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);
2765 }
2766 } else {
2767 this.items.add(item);
2768 }
2769 }
2770 }
2771
2772 /**
2773 * Add all history items to <code>contentProvider</code>.
2774 *
2775 * @param itemsFilter
2776 */
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);
2787 } else {
2788 this.selectionHistory.remove(item);
2789 }
2790 }
2791 }
2792 }
2793 }
2794 }
2795 }
2796
2797 /**
2798 * Refresh dialog.
2799 */
2800 public void refresh() {
2801 scheduleRefresh();
2802 }
2803
2804 /**
2805 * Removes items from history and refreshes the view.
2806 *
2807 * @param item
2808 * to remove
2809 *
2810 * @return removed item
2811 */
2812 public Object removeHistoryElement(Object item) {
2813 if (this.selectionHistory != null) {
2814 this.selectionHistory.remove(item);
2815 }
2816 if (filter == null || filter.getPattern().length() == 0) {
2817 items.remove(item);
2818 duplicates.remove(item);
2819 this.lastSortedItems.remove(item);
2820 }
2821
2822 synchronized (lastSortedItems) {
2823 Collections.sort(lastSortedItems, getHistoryComparator());
2824 }
2825 return item;
2826 }
2827
2828 /**
2829 * Adds item to history and refresh view.
2830 *
2831 * @param item
2832 * to add
2833 */
2834 public void addHistoryElement(Object item) {
2835 if (this.selectionHistory != null) {
2836 this.selectionHistory.accessed(item);
2837 }
2838 if (filter == null || !filter.matchItem(item)) {
2839 this.items.remove(item);
2840 this.duplicates.remove(item);
2841 this.lastSortedItems.remove(item);
2842 }
2843 synchronized (lastSortedItems) {
2844 Collections.sort(lastSortedItems, getHistoryComparator());
2845 }
2846 this.refresh();
2847 }
2848
2849 /**
2850 * @param item
2851 * @return <code>true</code> if given item is part of the history
2852 */
2853 public boolean isHistoryElement(Object item) {
2854 if (this.selectionHistory != null) {
2855 return this.selectionHistory.contains(item);
2856 }
2857 return false;
2858 }
2859
2860 /**
2861 * Sets/unsets given item as duplicate.
2862 *
2863 * @param item
2864 * item to change
2865 *
2866 * @param isDuplicate
2867 * duplicate flag
2868 */
2869 public void setDuplicateElement(Object item, boolean isDuplicate) {
2870 if (this.items.contains(item)) {
2871 if (isDuplicate) {
2872 this.duplicates.add(item);
2873 } else {
2874 this.duplicates.remove(item);
2875 }
2876 }
2877 }
2878
2879 /**
2880 * Indicates whether given item is a duplicate.
2881 *
2882 * @param item
2883 * item to check
2884 * @return <code>true</code> if item is duplicate
2885 */
2886 public boolean isDuplicateElement(Object item) {
2887 return duplicates.contains(item);
2888 }
2889
2890 /**
2891 * Load history from memento.
2892 *
2893 * @param memento
2894 * memento from which the history will be retrieved
2895 */
2896 public void loadHistory(IMemento memento) {
2897 if (this.selectionHistory != null) {
2898 this.selectionHistory.load(memento);
2899 }
2900 }
2901
2902 /**
2903 * Save history to memento.
2904 *
2905 * @param memento
2906 * memento to which the history will be added
2907 */
2908 public void saveHistory(IMemento memento) {
2909 if (this.selectionHistory != null) {
2910 this.selectionHistory.save(memento);
2911 }
2912 }
2913
2914 /**
2915 * Gets sorted items.
2916 *
2917 * @return sorted items
2918 */
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());
2925 }
2926 }
2927 return lastSortedItems.toArray();
2928 }
2929
2930 /**
2931 * Remember result of filtering.
2932 *
2933 * @param itemsFilter
2934 */
2935 public void rememberResult(ItemsFilter itemsFilter) {
2936 List itemsList = Collections.synchronizedList(Arrays
2937 .asList(getSortedItems()));
2938 // synchronization
2939 if (itemsFilter == filter) {
2940 lastCompletedFilter = itemsFilter;
2941 lastCompletedResult = itemsList;
2942 }
2943
2944 }
2945
2946 /*
2947 * (non-Javadoc)
2948 *
2949 * @see org.eclipse.jface.viewers.IStructuredContentProvider#getElements(java.lang.Object)
2950 */
2951 @Override
2952 public Object[] getElements(Object inputElement) {
2953 //return items.toArray();
2954 return lastFilteredItems.toArray();
2955 }
2956
2957 public int getNumberOfElements() {
2958
2959 return lastFilteredItems.size();
2960 }
2961
2962 /*
2963 * (non-Javadoc)
2964 *
2965 * @see org.eclipse.jface.viewers.IContentProvider#dispose()
2966 */
2967 @Override
2968 public void dispose() {
2969 }
2970
2971 /*
2972 * (non-Javadoc)
2973 *
2974 * @see org.eclipse.jface.viewers.IContentProvider#inputChanged(org.eclipse.jface.viewers.Viewer,
2975 * java.lang.Object, java.lang.Object)
2976 */
2977 @Override
2978 public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
2979 }
2980
2981 /*
2982 * (non-Javadoc)
2983 *
2984 * @see org.eclipse.jface.viewers.ILazyContentProvider#updateElement(int)
2985 */
2986 @Override
2987 public void updateElement(int index) {
2988
2989 CdmFilteredItemsSelectionDialog.this.getList().replace((lastFilteredItems
2990 .size() > index) ? lastFilteredItems.get(index) : null,
2991 index);
2992
2993 }
2994
2995 /**
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)}.
2999 *
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
3004 *
3005 * @param monitor
3006 * progress monitor
3007 */
3008 public void reloadCache(boolean checkDuplicates,
3009 IProgressMonitor monitor) {
3010
3011 reset = false;
3012
3013 if (monitor != null) {
3014 // the work is divided into two actions of the same length
3015 int totalWork = checkDuplicates ? 200 : 100;
3016
3017 monitor
3018 .beginTask(
3019 WorkbenchMessages.FilteredItemsSelectionDialog_cacheRefreshJob,
3020 totalWork);
3021 }
3022
3023 // the TableViewer's root (the input) is treated as parent
3024
3025 lastFilteredItems = Arrays.asList(getFilteredItems(getList().getInput(),
3026 monitor != null ? new SubProgressMonitor(monitor, 100)
3027 : null));
3028
3029 if (reset || (monitor != null && monitor.isCanceled())) {
3030 if (monitor != null) {
3031 monitor.done();
3032 }
3033 return;
3034 }
3035
3036 if (checkDuplicates) {
3037 checkDuplicates(monitor);
3038 }
3039 if (monitor != null) {
3040 monitor.done();
3041 }
3042 }
3043
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);
3050 subMonitor
3051 .beginTask(
3052 WorkbenchMessages.FilteredItemsSelectionDialog_cacheRefreshJob_checkDuplicates,
3053 5);
3054 }
3055 HashMap helperMap = new HashMap();
3056 for (int i = 0; i < lastFilteredItems.size(); i++) {
3057 if (reset
3058 || (subMonitor != null && subMonitor.isCanceled())) {
3059 return;
3060 }
3061 Object item = lastFilteredItems.get(i);
3062
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);
3069 } else {
3070 setDuplicateElement(item, false);
3071 }
3072 }
3073
3074 if (subMonitor != null && reportEvery != 0
3075 && (i + 1) % reportEvery == 0) {
3076 subMonitor.worked(1);
3077 }
3078 }
3079 helperMap.clear();
3080 }
3081 }
3082
3083 /**
3084 * Returns an array of items filtered using the provided
3085 * <code>ViewerFilter</code>s with a separator added.
3086 *
3087 * @param parent
3088 * the parent
3089 * @param monitor
3090 * progress monitor, can be <code>null</code>
3091 * @return an array of filtered items
3092 */
3093 protected Object[] getFilteredItems(Object parent,
3094 IProgressMonitor monitor) {
3095 int ticks = 100;
3096 if (monitor == null) {
3097 monitor = new NullProgressMonitor();
3098 }
3099
3100 monitor
3101 .beginTask(
3102 WorkbenchMessages.FilteredItemsSelectionDialog_cacheRefreshJob_getFilteredElements,
3103 ticks);
3104 if (filters != null) {
3105 ticks /= (filters.size() + 2);
3106 } else {
3107 ticks /= 2;
3108 }
3109
3110 // get already sorted array
3111 Object[] filteredElements = getSortedItems();
3112
3113 monitor.worked(ticks);
3114
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);
3121 }
3122 }
3123
3124 if (filteredElements == null || monitor.isCanceled()) {
3125 monitor.done();
3126 return new Object[0];
3127 }
3128
3129 ArrayList preparedElements = new ArrayList();
3130 boolean hasHistory = false;
3131
3132 if (filteredElements.length > 0) {
3133 if (isHistoryElement(filteredElements[0])) {
3134 hasHistory = true;
3135 }
3136 }
3137
3138 int reportEvery = filteredElements.length / ticks;
3139
3140 // add separator
3141 for (int i = 0; i < filteredElements.length; i++) {
3142 Object item = filteredElements[i];
3143
3144 if (hasHistory && !isHistoryElement(item)) {
3145 preparedElements.add(itemsListSeparator);
3146 hasHistory = false;
3147 }
3148
3149 preparedElements.add(item);
3150
3151 if (reportEvery != 0 && ((i + 1) % reportEvery == 0)) {
3152 monitor.worked(1);
3153 }
3154 }
3155
3156 monitor.done();
3157
3158 return preparedElements.toArray();
3159 }
3160
3161 /**
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.CustomDescriptiveDataSetFilter</code>.
3165 *
3166 *
3167 * @param filter
3168 * the filter to be added
3169 */
3170 public void addFilter(ViewerFilter filter) {
3171 if (filters == null) {
3172 filters = new ArrayList();
3173 }
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
3177 // added
3178 }
3179
3180 }
3181
3182 /**
3183 * A content provider that does nothing.
3184 */
3185 private class NullContentProvider implements IContentProvider {
3186
3187 /*
3188 * (non-Javadoc)
3189 *
3190 * @see org.eclipse.jface.viewers.IContentProvider#dispose()
3191 */
3192 @Override
3193 public void dispose() {
3194 }
3195
3196 /*
3197 * (non-Javadoc)
3198 *
3199 * @see org.eclipse.jface.viewers.IContentProvider#inputChanged(org.eclipse.jface.viewers.Viewer,
3200 * java.lang.Object, java.lang.Object)
3201 */
3202 @Override
3203 public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
3204 }
3205
3206 }
3207
3208 /**
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.
3212 */
3213 private class DetailsContentViewer extends ContentViewer {
3214
3215 private final CLabel label;
3216
3217 /**
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.
3221 */
3222 private final ViewForm viewForm;
3223
3224 /**
3225 * Constructs a new instance of this class given its parent and a style
3226 * value describing its behavior and appearance.
3227 *
3228 * @param parent
3229 * the parent component
3230 * @param style
3231 * SWT style bits
3232 */
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);
3241 hookControl(label);
3242 }
3243
3244 /**
3245 * Shows/hides the content viewer.
3246 *
3247 * @param visible
3248 * if the content viewer should be visible.
3249 */
3250 public void setVisible(boolean visible) {
3251 GridData gd = (GridData) viewForm.getLayoutData();
3252 gd.exclude = !visible;
3253 viewForm.getParent().layout();
3254 }
3255
3256 /*
3257 * (non-Javadoc)
3258 *
3259 * @see org.eclipse.jface.viewers.Viewer#inputChanged(java.lang.Object,
3260 * java.lang.Object)
3261 */
3262 @Override
3263 protected void inputChanged(Object input, Object oldInput) {
3264 if (oldInput == null) {
3265 if (input == null) {
3266 return;
3267 }
3268 refresh();
3269 return;
3270 }
3271
3272 refresh();
3273
3274 }
3275
3276 /*
3277 * (non-Javadoc)
3278 *
3279 * @see org.eclipse.jface.viewers.ContentViewer#handleLabelProviderChanged(org.eclipse.jface.viewers.LabelProviderChangedEvent)
3280 */
3281 @Override
3282 protected void handleLabelProviderChanged(
3283 LabelProviderChangedEvent event) {
3284 if (event != null) {
3285 refresh(event.getElements());
3286 }
3287 }
3288
3289 /*
3290 * (non-Javadoc)
3291 *
3292 * @see org.eclipse.jface.viewers.Viewer#getControl()
3293 */
3294 @Override
3295 public Control getControl() {
3296 return label;
3297 }
3298
3299 /*
3300 * (non-Javadoc)
3301 *
3302 * @see org.eclipse.jface.viewers.Viewer#getSelection()
3303 */
3304 @Override
3305 public ISelection getSelection() {
3306 // not supported
3307 return null;
3308 }
3309
3310 /*
3311 * (non-Javadoc)
3312 *
3313 * @see org.eclipse.jface.viewers.Viewer#refresh()
3314 */
3315 @Override
3316 public void refresh() {
3317 Object input = this.getInput();
3318 if (input != null) {
3319 ILabelProvider labelProvider = (ILabelProvider) getLabelProvider();
3320 doRefresh(labelProvider.getText(input), labelProvider
3321 .getImage(input));
3322 } else {
3323 doRefresh(null, null);
3324 }
3325 }
3326
3327 /**
3328 * Sets the given text and image to the label.
3329 *
3330 * @param text
3331 * the new text or null
3332 * @param image
3333 * the new image
3334 */
3335 private void doRefresh(String text, Image image) {
3336 if ( text != null ) {
3337 text = LegacyActionTools.escapeMnemonics(text);
3338 }
3339 label.setText(text);
3340 label.setImage(image);
3341 }
3342
3343 /*
3344 * (non-Javadoc)
3345 *
3346 * @see org.eclipse.jface.viewers.Viewer#setSelection(org.eclipse.jface.viewers.ISelection,
3347 * boolean)
3348 */
3349 @Override
3350 public void setSelection(ISelection selection, boolean reveal) {
3351 // not supported
3352 }
3353
3354 /**
3355 * Refreshes the label if currently chosen element is on the list.
3356 *
3357 * @param objs
3358 * list of changed object
3359 */
3360 private void refresh(Object[] objs) {
3361 if (objs == null || getInput() == null) {
3362 return;
3363 }
3364 Object input = getInput();
3365 for (int i = 0; i < objs.length; i++) {
3366 if (objs[i].equals(input)) {
3367 refresh();
3368 break;
3369 }
3370 }
3371 }
3372 }
3373
3374 /**
3375 * Compares items according to the history.
3376 */
3377 private class HistoryComparator implements Comparator {
3378
3379 /*
3380 * (non-Javadoc)
3381 *
3382 * @see java.util.Comparator#compare(java.lang.Object, java.lang.Object)
3383 */
3384 @Override
3385 public int compare(Object o1, Object o2) {
3386 boolean h1 = isHistoryElement(o1);
3387 boolean h2 = isHistoryElement(o2);
3388 if (h1 == h2) {
3389 return getItemsComparator().compare(o1, o2);
3390 }
3391
3392 if (h1) {
3393 return -2;
3394 }
3395 if (h2) {
3396 return +2;
3397 }
3398
3399 return 0;
3400 }
3401
3402 }
3403
3404
3405 /**
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.
3410 *
3411 * @return Control or <code>null</code> if the pattern control has not
3412 * been created.
3413 */
3414 public Control getPatternControl() {
3415 return pattern;
3416 }
3417
3418 /**
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)
3421 * <p>initModel</p>
3422 */
3423 abstract protected void initModel();
3424
3425 public Object[] getCurrentSelection() {
3426 return currentSelection;
3427 }
3428
3429 public void setCurrentSelection(Object[] currentSelection) {
3430 this.currentSelection = currentSelection;
3431 }
3432
3433 public TableViewer getList() {
3434 return list;
3435 }
3436
3437 public void setList(TableViewer list) {
3438 this.list = list;
3439 }
3440
3441 }
3442
3443