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