Project

General

Profile

Download (121 KB) Statistics
| Branch: | Tag: | Revision:
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
 * This is a class copied from FilteredItemsSelectionDialog and adapted to the Cdm use case. The original
131
 * dialog gets all items when opening the dialog and then apply the filter on all items.
132
 * In our case we need a possibility to filter the items already when getting them from the DB, so we adapt the
133
 * original class
134
 * the abstract method initMOdel was added and the method applyFilter was modified
135
 * original description:
136
 * Shows a list of items to the user with a text entry field for a string
137
 * pattern used to filter the list of items.
138
 *
139
 *
140
 */
141

    
142
public abstract class CdmFilteredItemsSelectionDialog extends SelectionStatusDialog {
143

    
144

    
145

    
146
        private static final String DIALOG_BOUNDS_SETTINGS = "DialogBoundsSettings"; //$NON-NLS-1$
147

    
148
        private static final String SHOW_STATUS_LINE = "ShowStatusLine"; //$NON-NLS-1$
149

    
150
        private static final String HISTORY_SETTINGS = "History"; //$NON-NLS-1$
151

    
152
        private static final String DIALOG_HEIGHT = "DIALOG_HEIGHT"; //$NON-NLS-1$
153

    
154
        private static final String DIALOG_WIDTH = "DIALOG_WIDTH"; //$NON-NLS-1$
155

    
156
        /**
157
         * Represents an empty selection in the pattern input field (used only for
158
         * initial pattern).
159
         */
160
        public static final int NONE = 0;
161

    
162
        /**
163
         * Pattern input field selection where caret is at the beginning (used only
164
         * for initial pattern).
165
         */
166
        public static final int CARET_BEGINNING = 1;
167

    
168
        /**
169
         * Represents a full selection in the pattern input field (used only for
170
         * initial pattern).
171
         */
172
        public static final int FULL_SELECTION = 2;
173

    
174
        private Text pattern;
175

    
176
        private TableViewer list;
177

    
178
        private DetailsContentViewer details;
179

    
180
        /**
181
         * It is a duplicate of a field in the CLabel class in DetailsContentViewer.
182
         * It is maintained, because the <code>setDetailsLabelProvider()</code>
183
         * could be called before content area is created.
184
         */
185
        private ILabelProvider detailsLabelProvider;
186

    
187
        private ItemsListLabelProvider itemsListLabelProvider;
188

    
189
        private MenuManager menuManager;
190

    
191
        private MenuManager contextMenuManager;
192

    
193
        private final boolean multi;
194

    
195
        private ToolBar toolBar;
196

    
197
        private ToolItem toolItem;
198

    
199
        private Label progressLabel;
200

    
201
        private ToggleStatusLineAction toggleStatusLineAction;
202

    
203
        private RemoveHistoryItemAction removeHistoryItemAction;
204

    
205
        private ActionContributionItem removeHistoryActionContributionItem;
206

    
207
        private IStatus status;
208

    
209
        private final RefreshCacheJob refreshCacheJob;
210

    
211
        private final RefreshProgressMessageJob refreshProgressMessageJob = new RefreshProgressMessageJob();
212

    
213
        private Object[] currentSelection;
214

    
215
        private final ContentProvider contentProvider;
216

    
217
        private final FilterHistoryJob filterHistoryJob;
218

    
219
        private final FilterJob filterJob;
220

    
221
        private ItemsFilter filter;
222

    
223
        private List lastCompletedResult;
224

    
225
        private ItemsFilter lastCompletedFilter;
226

    
227
        private String initialPatternText;
228

    
229
        private int selectionMode;
230

    
231
        private ItemsListSeparator itemsListSeparator;
232

    
233
        private static final String EMPTY_STRING = ""; //$NON-NLS-1$
234

    
235
        private boolean refreshWithLastSelection = false;
236

    
237
        private IHandlerActivation showViewHandler;
238

    
239
        /**
240
         * Creates a new instance of the class.
241
         *
242
         * @param shell
243
         *            shell to parent the dialog on
244
         * @param multi
245
         *            indicates whether dialog allows to select more than one
246
         *            position in its list of items
247
         */
248
        public CdmFilteredItemsSelectionDialog(Shell shell, boolean multi) {
249
            super(shell);
250
            this.multi = multi;
251
            filterHistoryJob = new FilterHistoryJob();
252
            filterJob = new FilterJob();
253
            contentProvider = new ContentProvider();
254
            refreshCacheJob = new RefreshCacheJob();
255
            itemsListSeparator = new ItemsListSeparator(
256
                    WorkbenchMessages.FilteredItemsSelectionDialog_separatorLabel);
257
            selectionMode = NONE;
258
        }
259

    
260
        /**
261
         * Creates a new instance of the class. Created dialog won't allow to select
262
         * more than one item.
263
         *
264
         * @param shell
265
         *            shell to parent the dialog on
266
         */
267
        public CdmFilteredItemsSelectionDialog(Shell shell) {
268
            this(shell, false);
269
        }
270

    
271
        /**
272
         * Adds viewer filter to the dialog items list.
273
         *
274
         * @param filter
275
         *            the new filter
276
         */
277
        protected void addListFilter(ViewerFilter filter) {
278
            contentProvider.addFilter(filter);
279
        }
280

    
281
        /**
282
         * Sets a new label provider for items in the list. If the label provider
283
         * also implements {@link
284
         * org.eclipse.jface.viewers.DelegatingStyledCellLabelProvider
285
         * .IStyledLabelProvider}, the style text labels provided by it will be used
286
         * provided that the corresponding preference is set.
287
         *
288
         * @see IWorkbenchPreferenceConstants#USE_COLORED_LABELS
289
         *
290
         * @param listLabelProvider
291
         *      the label provider for items in the list
292
         */
293
        public void setListLabelProvider(ILabelProvider listLabelProvider) {
294
            getItemsListLabelProvider().setProvider(listLabelProvider);
295
        }
296

    
297
        /**
298
         * Returns the label decorator for selected items in the list.
299
         *
300
         * @return the label decorator for selected items in the list
301
         */
302
        private ILabelDecorator getListSelectionLabelDecorator() {
303
            return getItemsListLabelProvider().getSelectionDecorator();
304
        }
305

    
306
        /**
307
         * Sets the label decorator for selected items in the list.
308
         *
309
         * @param listSelectionLabelDecorator
310
         *            the label decorator for selected items in the list
311
         */
312
        public void setListSelectionLabelDecorator(
313
                ILabelDecorator listSelectionLabelDecorator) {
314
            getItemsListLabelProvider().setSelectionDecorator(
315
                    listSelectionLabelDecorator);
316
        }
317

    
318
        /**
319
         * Returns the item list label provider.
320
         *
321
         * @return the item list label provider
322
         */
323
        private ItemsListLabelProvider getItemsListLabelProvider() {
324
            if (itemsListLabelProvider == null) {
325
                itemsListLabelProvider = new ItemsListLabelProvider(
326
                        new LabelProvider(), null);
327
            }
328
            return itemsListLabelProvider;
329
        }
330

    
331
        /**
332
         * Sets label provider for the details field.
333
         *
334
         * For a single selection, the element sent to
335
         * {@link ILabelProvider#getImage(Object)} and
336
         * {@link ILabelProvider#getText(Object)} is the selected object, for
337
         * multiple selection a {@link String} with amount of selected items is the
338
         * element.
339
         *
340
         * @see #getSelectedItems() getSelectedItems() can be used to retrieve
341
         *      selected items and get the items count.
342
         *
343
         * @param detailsLabelProvider
344
         *            the label provider for the details field
345
         */
346
        public void setDetailsLabelProvider(ILabelProvider detailsLabelProvider) {
347
            this.detailsLabelProvider = detailsLabelProvider;
348
            if (details != null) {
349
                details.setLabelProvider(detailsLabelProvider);
350
            }
351
        }
352

    
353
        private ILabelProvider getDetailsLabelProvider() {
354
            if (detailsLabelProvider == null) {
355
                detailsLabelProvider = new LabelProvider();
356
            }
357
            return detailsLabelProvider;
358
        }
359

    
360
        /*
361
         * (non-Javadoc)
362
         *
363
         * @see org.eclipse.jface.window.Window#create()
364
         */
365
        @Override
366
        public void create() {
367
            super.create();
368
            pattern.setFocus();
369
        }
370

    
371
        /**
372
         * Restores dialog using persisted settings. The default implementation
373
         * restores the status of the details line and the selection history.
374
         *
375
         * @param settings
376
         *            settings used to restore dialog
377
         */
378
        protected void restoreDialog(IDialogSettings settings) {
379
            boolean toggleStatusLine = true;
380

    
381
            if (settings.get(SHOW_STATUS_LINE) != null) {
382
                toggleStatusLine = settings.getBoolean(SHOW_STATUS_LINE);
383
            }
384

    
385
            toggleStatusLineAction.setChecked(toggleStatusLine);
386

    
387
            details.setVisible(toggleStatusLine);
388

    
389
            String setting = settings.get(HISTORY_SETTINGS);
390
            if (setting != null) {
391
                try {
392
                    IMemento memento = XMLMemento.createReadRoot(new StringReader(
393
                            setting));
394
                    this.contentProvider.loadHistory(memento);
395
                } catch (WorkbenchException e) {
396
                    // Simply don't restore the settings
397
                    StatusManager
398
                            .getManager()
399
                            .handle(
400
                                    new Status(
401
                                            IStatus.ERROR,
402
                                            PlatformUI.PLUGIN_ID,
403
                                            IStatus.ERROR,
404
                                            WorkbenchMessages.FilteredItemsSelectionDialog_restoreError,
405
                                            e));
406
                }
407
            }
408
        }
409

    
410
        /*
411
         * (non-Javadoc)
412
         *
413
         * @see org.eclipse.jface.window.Window#close()
414
         */
415
        @Override
416
        public boolean close() {
417
            this.filterJob.cancel();
418
            this.refreshCacheJob.cancel();
419
            this.refreshProgressMessageJob.cancel();
420
            if (showViewHandler != null) {
421
                IHandlerService service = (IHandlerService) PlatformUI
422
                        .getWorkbench().getService(IHandlerService.class);
423
                service.deactivateHandler(showViewHandler);
424
                showViewHandler.getHandler().dispose();
425
                showViewHandler = null;
426
            }
427
            if (menuManager != null) {
428
                menuManager.dispose();
429
            }
430
            if (contextMenuManager != null) {
431
                contextMenuManager.dispose();
432
            }
433
            storeDialog(getDialogSettings());
434
            return super.close();
435
        }
436

    
437
        /**
438
         * Stores dialog settings.
439
         *
440
         * @param settings
441
         *            settings used to store dialog
442
         */
443
        protected void storeDialog(IDialogSettings settings) {
444
            settings.put(SHOW_STATUS_LINE, toggleStatusLineAction.isChecked());
445

    
446
            XMLMemento memento = XMLMemento.createWriteRoot(HISTORY_SETTINGS);
447
            this.contentProvider.saveHistory(memento);
448
            StringWriter writer = new StringWriter();
449
            try {
450
                memento.save(writer);
451
                settings.put(HISTORY_SETTINGS, writer.getBuffer().toString());
452
            } catch (IOException e) {
453
                // Simply don't store the settings
454
                StatusManager
455
                        .getManager()
456
                        .handle(
457
                                new Status(
458
                                        IStatus.ERROR,
459
                                        PlatformUI.PLUGIN_ID,
460
                                        IStatus.ERROR,
461
                                        WorkbenchMessages.FilteredItemsSelectionDialog_storeError,
462
                                        e));
463
            }
464
        }
465

    
466
        /**
467
         * Create a new header which is labelled by headerLabel.
468
         *
469
         * @param parent
470
         * @return Label the label of the header
471
         */
472
        private Label createHeader(Composite parent) {
473
            Composite header = new Composite(parent, SWT.NONE);
474

    
475
            GridLayout layout = new GridLayout();
476
            layout.numColumns = 2;
477
            layout.marginWidth = 0;
478
            layout.marginHeight = 0;
479
            header.setLayout(layout);
480

    
481
            Label headerLabel = new Label(header, SWT.NONE);
482
            headerLabel.setText((getMessage() != null && getMessage().trim()
483
                    .length() > 0) ? getMessage()
484
                    : WorkbenchMessages.FilteredItemsSelectionDialog_patternLabel);
485
            headerLabel.addTraverseListener(new TraverseListener() {
486
                @Override
487
                public void keyTraversed(TraverseEvent e) {
488
                    if (e.detail == SWT.TRAVERSE_MNEMONIC && e.doit) {
489
                        e.detail = SWT.TRAVERSE_NONE;
490
                        pattern.setFocus();
491
                    }
492
                }
493
            });
494

    
495
            GridData gd = new GridData(GridData.FILL_HORIZONTAL);
496
            headerLabel.setLayoutData(gd);
497

    
498
            createViewMenu(header);
499
            header.setLayoutData(gd);
500
            return headerLabel;
501
        }
502

    
503
        /**
504
         * Create the labels for the list and the progress. Return the list label.
505
         *
506
         * @param parent
507
         * @return Label
508
         */
509
        private Label createLabels(Composite parent) {
510
            Composite labels = new Composite(parent, SWT.NONE);
511

    
512
            GridLayout layout = new GridLayout();
513
            layout.numColumns = 2;
514
            layout.marginWidth = 0;
515
            layout.marginHeight = 0;
516
            labels.setLayout(layout);
517

    
518
            Label listLabel = new Label(labels, SWT.NONE);
519
            listLabel
520
                    .setText(WorkbenchMessages.FilteredItemsSelectionDialog_listLabel);
521

    
522
            listLabel.addTraverseListener(new TraverseListener() {
523
                @Override
524
                public void keyTraversed(TraverseEvent e) {
525
                    if (e.detail == SWT.TRAVERSE_MNEMONIC && e.doit) {
526
                        e.detail = SWT.TRAVERSE_NONE;
527
                        getList().getTable().setFocus();
528
                    }
529
                }
530
            });
531

    
532
            GridData gd = new GridData(GridData.FILL_HORIZONTAL);
533
            listLabel.setLayoutData(gd);
534

    
535
            progressLabel = new Label(labels, SWT.RIGHT);
536
            progressLabel.setLayoutData(gd);
537

    
538
            labels.setLayoutData(gd);
539
            return listLabel;
540
        }
541

    
542
        private void createViewMenu(Composite parent) {
543
            toolBar = new ToolBar(parent, SWT.FLAT);
544
            toolItem = new ToolItem(toolBar, SWT.PUSH, 0);
545

    
546
            GridData data = new GridData();
547
            data.horizontalAlignment = GridData.END;
548
            toolBar.setLayoutData(data);
549

    
550
            toolBar.addMouseListener(new MouseAdapter() {
551
                @Override
552
                public void mouseDown(MouseEvent e) {
553
                    showViewMenu();
554
                }
555
            });
556

    
557
            toolItem.setImage(WorkbenchImages
558
                    .getImage(IWorkbenchGraphicConstants.IMG_LCL_VIEW_MENU));
559
            toolItem
560
                    .setToolTipText(WorkbenchMessages.FilteredItemsSelectionDialog_menu);
561
            toolItem.addSelectionListener(new SelectionAdapter() {
562
                @Override
563
                public void widgetSelected(SelectionEvent e) {
564
                    showViewMenu();
565
                }
566
            });
567

    
568
            menuManager = new MenuManager();
569

    
570
            fillViewMenu(menuManager);
571

    
572
            IHandlerService service = (IHandlerService) PlatformUI.getWorkbench()
573
                    .getService(IHandlerService.class);
574
            IHandler handler = new AbstractHandler() {
575
                @Override
576
                public Object execute(ExecutionEvent event) {
577
                    showViewMenu();
578
                    return null;
579
                }
580
            };
581
            showViewHandler = service.activateHandler(
582
                    IWorkbenchCommandConstants.WINDOW_SHOW_VIEW_MENU, handler,
583
                    new ActiveShellExpression(getShell()));
584
        }
585

    
586
        /**
587
         * Fills the menu of the dialog.
588
         *
589
         * @param menuManager
590
         *            the menu manager
591
         */
592
        protected void fillViewMenu(IMenuManager menuManager) {
593
            toggleStatusLineAction = new ToggleStatusLineAction();
594
            menuManager.add(toggleStatusLineAction);
595
        }
596

    
597
        private void showViewMenu() {
598
            Menu menu = menuManager.createContextMenu(getShell());
599
            Rectangle bounds = toolItem.getBounds();
600
            Point topLeft = new Point(bounds.x, bounds.y + bounds.height);
601
            topLeft = toolBar.toDisplay(topLeft);
602
            menu.setLocation(topLeft.x, topLeft.y);
603
            menu.setVisible(true);
604
        }
605

    
606
        /**
607
         * Hook that allows to add actions to the context menu.
608
         * <p>
609
         * Subclasses may extend in order to add other actions.</p>
610
         *
611
         * @param menuManager the context menu manager
612
         * @since 3.5
613
         */
614
        protected void fillContextMenu(IMenuManager menuManager) {
615
            List selectedElements= ((StructuredSelection)getList().getSelection()).toList();
616

    
617
            Object item= null;
618

    
619
            for (Iterator it= selectedElements.iterator(); it.hasNext();) {
620
                item= it.next();
621
                if (item instanceof ItemsListSeparator || !isHistoryElement(item)) {
622
                    return;
623
                }
624
            }
625

    
626
            if (selectedElements.size() > 0) {
627
                removeHistoryItemAction.setText(WorkbenchMessages.FilteredItemsSelectionDialog_removeItemsFromHistoryAction);
628

    
629
                menuManager.add(removeHistoryActionContributionItem);
630

    
631
            }
632
        }
633

    
634
        private void createPopupMenu() {
635
            removeHistoryItemAction = new RemoveHistoryItemAction();
636
            removeHistoryActionContributionItem = new ActionContributionItem(
637
                    removeHistoryItemAction);
638

    
639
            contextMenuManager = new MenuManager();
640
            contextMenuManager.setRemoveAllWhenShown(true);
641
            contextMenuManager.addMenuListener(new IMenuListener() {
642
                @Override
643
                public void menuAboutToShow(IMenuManager manager) {
644
                    fillContextMenu(manager);
645
                }
646
            });
647

    
648
            final Table table = getList().getTable();
649
            Menu menu= contextMenuManager.createContextMenu(table);
650
            table.setMenu(menu);
651
        }
652

    
653
        /**
654
         * Creates an extra content area, which will be located above the details.
655
         *
656
         * @param parent
657
         *            parent to create the dialog widgets in
658
         * @return an extra content area
659
         */
660
        protected abstract Control createExtendedContentArea(Composite parent);
661

    
662
        /*
663
         * (non-Javadoc)
664
         *
665
         * @see org.eclipse.jface.dialogs.Dialog#createDialogArea(org.eclipse.swt.widgets.Composite)
666
         */
667
        @Override
668
        protected Control createDialogArea(Composite parent) {
669
            Composite dialogArea = (Composite) super.createDialogArea(parent);
670

    
671
            Composite content = new Composite(dialogArea, SWT.NONE);
672
            GridData gd = new GridData(GridData.FILL_BOTH);
673
            content.setLayoutData(gd);
674

    
675
            GridLayout layout = new GridLayout();
676
            layout.numColumns = 1;
677
            layout.marginWidth = 0;
678
            layout.marginHeight = 0;
679
            content.setLayout(layout);
680

    
681
            final Label headerLabel = createHeader(content);
682

    
683
            pattern = new Text(content, SWT.SINGLE | SWT.BORDER | SWT.SEARCH | SWT.ICON_CANCEL);
684
            pattern.getAccessible().addAccessibleListener(new AccessibleAdapter() {
685
                @Override
686
                public void getName(AccessibleEvent e) {
687
                    e.result = LegacyActionTools.removeMnemonics(headerLabel
688
                            .getText());
689
                }
690
            });
691
            gd = new GridData(GridData.FILL_HORIZONTAL);
692
            pattern.setLayoutData(gd);
693

    
694
            final Label listLabel = createLabels(content);
695

    
696
            setList(new TableViewer(content, (multi ? SWT.MULTI : SWT.SINGLE)
697
                    | SWT.BORDER | SWT.V_SCROLL | SWT.VIRTUAL));
698
            getList().getTable().getAccessible().addAccessibleListener(
699
                    new AccessibleAdapter() {
700
                        @Override
701
                        public void getName(AccessibleEvent e) {
702
                            if (e.childID == ACC.CHILDID_SELF) {
703
                                e.result = LegacyActionTools
704
                                        .removeMnemonics(listLabel.getText());
705
                            }
706
                        }
707
                    });
708
            getList().setContentProvider(contentProvider);
709
            getList().setLabelProvider(getItemsListLabelProvider());
710
            getList().setInput(new Object[0]);
711
            getList().setItemCount(contentProvider.getNumberOfElements());
712
            gd = new GridData(GridData.FILL_BOTH);
713
            applyDialogFont(getList().getTable());
714
            gd.heightHint= getList().getTable().getItemHeight() * 15;
715
            getList().getTable().setLayoutData(gd);
716

    
717
            createPopupMenu();
718

    
719
            pattern.addModifyListener(new ModifyListener() {
720
                @Override
721
                public void modifyText(ModifyEvent e) {
722
                    applyFilter();
723
                }
724
            });
725

    
726
            pattern.addKeyListener(new KeyAdapter() {
727
                @Override
728
                public void keyPressed(KeyEvent e) {
729
                    if (e.keyCode == SWT.ARROW_DOWN) {
730
                        if (getList().getTable().getItemCount() > 0) {
731
                            getList().getTable().setFocus();
732
                        }
733
                    }
734
                }
735
            });
736

    
737
            getList().addSelectionChangedListener(new ISelectionChangedListener() {
738
                @Override
739
                public void selectionChanged(SelectionChangedEvent event) {
740
                    StructuredSelection selection = (StructuredSelection) event
741
                            .getSelection();
742
                    handleSelected(selection);
743
                }
744
            });
745

    
746
            getList().addDoubleClickListener(new IDoubleClickListener() {
747
                @Override
748
                public void doubleClick(DoubleClickEvent event) {
749
                    handleDoubleClick();
750
                }
751
            });
752

    
753
            getList().getTable().addKeyListener(new KeyAdapter() {
754
                @Override
755
                public void keyPressed(KeyEvent e) {
756

    
757
                    if (e.keyCode == SWT.DEL) {
758

    
759
                        List selectedElements = ((StructuredSelection) getList()
760
                                .getSelection()).toList();
761

    
762
                        Object item = null;
763
                        boolean isSelectedHistory = true;
764

    
765
                        for (Iterator it = selectedElements.iterator(); it
766
                                .hasNext();) {
767
                            item = it.next();
768
                            if (item instanceof ItemsListSeparator
769
                                    || !isHistoryElement(item)) {
770
                                isSelectedHistory = false;
771
                                break;
772
                            }
773
                        }
774
                        if (isSelectedHistory) {
775
                            removeSelectedItems(selectedElements);
776
                        }
777

    
778
                    }
779

    
780
                    if (e.keyCode == SWT.ARROW_UP && (e.stateMask & SWT.SHIFT) != 0
781
                            && (e.stateMask & SWT.CTRL) != 0) {
782
                        StructuredSelection selection = (StructuredSelection) getList()
783
                                .getSelection();
784

    
785
                        if (selection.size() == 1) {
786
                            Object element = selection.getFirstElement();
787
                            if (element.equals(getList().getElementAt(0))) {
788
                                pattern.setFocus();
789
                            }
790
                            if (getList().getElementAt(getList().getTable()
791
                                    .getSelectionIndex() - 1) instanceof ItemsListSeparator) {
792
                                getList().getTable().setSelection(
793
                                        getList().getTable().getSelectionIndex() - 1);
794
                            }
795
                            getList().getTable().notifyListeners(SWT.Selection,
796
                                    new Event());
797

    
798
                        }
799
                    }
800

    
801
                    if (e.keyCode == SWT.ARROW_DOWN
802
                            && (e.stateMask & SWT.SHIFT) != 0
803
                            && (e.stateMask & SWT.CTRL) != 0) {
804

    
805
                        if (getList()
806
                                .getElementAt(getList().getTable().getSelectionIndex() + 1) instanceof ItemsListSeparator) {
807
                            getList().getTable().setSelection(
808
                                    getList().getTable().getSelectionIndex() + 1);
809
                        }
810
                        getList().getTable().notifyListeners(SWT.Selection, new Event());
811
                    }
812

    
813
                }
814
            });
815

    
816
            createExtendedContentArea(content);
817

    
818
            details = new DetailsContentViewer(content, SWT.BORDER | SWT.FLAT);
819
            details.setVisible(toggleStatusLineAction.isChecked());
820
            details.setContentProvider(new NullContentProvider());
821
            details.setLabelProvider(getDetailsLabelProvider());
822

    
823
            applyDialogFont(content);
824

    
825
            restoreDialog(getDialogSettings());
826

    
827
            if (initialPatternText != null) {
828
                pattern.setText(initialPatternText);
829
            }
830

    
831
            switch (selectionMode) {
832
            case CARET_BEGINNING:
833
                pattern.setSelection(0, 0);
834
                break;
835
            case FULL_SELECTION:
836
                pattern.setSelection(0, initialPatternText.length());
837
                break;
838
            }
839

    
840
            // apply filter even if pattern is empty (display history)
841
            applyFilter();
842

    
843
            return dialogArea;
844
        }
845

    
846
        /**
847
         * This method is a hook for subclasses to override default dialog behavior.
848
         * The <code>handleDoubleClick()</code> method handles double clicks on
849
         * the list of filtered elements.
850
         * <p>
851
         * Current implementation makes double-clicking on the list do the same as
852
         * pressing <code>OK</code> button on the dialog.
853
         */
854
        protected void handleDoubleClick() {
855
            okPressed();
856
        }
857

    
858
        /**
859
         * Refreshes the details field according to the current selection in the
860
         * items list.
861
         */
862
        private void refreshDetails() {
863
            StructuredSelection selection = getSelectedItems();
864

    
865
            switch (selection.size()) {
866
            case 0:
867
                details.setInput(null);
868
                break;
869
            case 1:
870
                details.setInput(selection.getFirstElement());
871
                break;
872
            default:
873
                details
874
                        .setInput(NLS
875
                                .bind(
876
                                        WorkbenchMessages.FilteredItemsSelectionDialog_nItemsSelected,
877
                                        new Integer(selection.size())));
878
                break;
879
            }
880

    
881
        }
882

    
883
        /**
884
         * Handle selection in the items list by updating labels of selected and
885
         * unselected items and refresh the details field using the selection.
886
         *
887
         * @param selection
888
         *            the new selection
889
         */
890
        protected void handleSelected(StructuredSelection selection) {
891
            IStatus status = new Status(IStatus.OK, PlatformUI.PLUGIN_ID,
892
                    IStatus.OK, EMPTY_STRING, null);
893

    
894
            Object[] lastSelection = getCurrentSelection();
895

    
896
            setCurrentSelection(selection.toArray());
897

    
898
            if (selection.size() == 0) {
899
                status = new Status(IStatus.ERROR, PlatformUI.PLUGIN_ID,
900
                        IStatus.ERROR, EMPTY_STRING, null);
901

    
902
                if (lastSelection != null
903
                        && getListSelectionLabelDecorator() != null) {
904
                    getList().update(lastSelection, null);
905
                }
906

    
907
                setCurrentSelection(null);
908

    
909
            } else {
910
                status = new Status(IStatus.ERROR, PlatformUI.PLUGIN_ID,
911
                        IStatus.ERROR, EMPTY_STRING, null);
912

    
913
                List items = selection.toList();
914

    
915
                Object item = null;
916
                IStatus tempStatus = null;
917

    
918
                for (Iterator it = items.iterator(); it.hasNext();) {
919
                    Object o = it.next();
920

    
921
                    if (o instanceof ItemsListSeparator) {
922
                        continue;
923
                    }
924

    
925
                    item = o;
926
                    tempStatus = validateItem(item);
927

    
928
                    if (tempStatus.isOK()) {
929
                        status = new Status(IStatus.OK, PlatformUI.PLUGIN_ID,
930
                                IStatus.OK, EMPTY_STRING, null);
931
                    } else {
932
                        status = tempStatus;
933
                        // if any selected element is not valid status is set to
934
                        // ERROR
935
                        break;
936
                    }
937
                }
938

    
939
                if (lastSelection != null
940
                        && getListSelectionLabelDecorator() != null) {
941
                    getList().update(lastSelection, null);
942
                }
943

    
944
                if (getListSelectionLabelDecorator() != null) {
945
                    getList().update(getCurrentSelection(), null);
946
                }
947
            }
948

    
949
            refreshDetails();
950
            updateStatus(status);
951
        }
952

    
953
        /*
954
         * (non-Javadoc)
955
         *
956
         * @see org.eclipse.jface.window.Dialog#getDialogBoundsSettings()
957
         */
958
        @Override
959
        protected IDialogSettings getDialogBoundsSettings() {
960
            IDialogSettings settings = getDialogSettings();
961
            IDialogSettings section = settings.getSection(DIALOG_BOUNDS_SETTINGS);
962
            if (section == null) {
963
                section = settings.addNewSection(DIALOG_BOUNDS_SETTINGS);
964
                section.put(DIALOG_HEIGHT, 500);
965
                section.put(DIALOG_WIDTH, 600);
966
            }
967
            return section;
968
        }
969

    
970
        /**
971
         * Returns the dialog settings. Returned object can't be null.
972
         *
973
         * @return return dialog settings for this dialog
974
         */
975
        protected abstract IDialogSettings getDialogSettings();
976

    
977
        /**
978
         * Refreshes the dialog - has to be called in UI thread.
979
         */
980
        public void refresh() {
981
            if (getList() != null && !getList().getTable().isDisposed()) {
982

    
983
                List lastRefreshSelection = ((StructuredSelection) getList()
984
                        .getSelection()).toList();
985
                getList().getTable().deselectAll();
986

    
987
                getList().setItemCount(contentProvider.getNumberOfElements());
988
                getList().refresh();
989

    
990
                if (getList().getTable().getItemCount() > 0) {
991
                    // preserve previous selection
992
                    if (refreshWithLastSelection && lastRefreshSelection != null
993
                            && lastRefreshSelection.size() > 0) {
994
                        getList().setSelection(new StructuredSelection(
995
                                lastRefreshSelection));
996
                    } else {
997
                        refreshWithLastSelection = true;
998
                        getList().getTable().setSelection(0);
999
                        getList().getTable().notifyListeners(SWT.Selection, new Event());
1000
                    }
1001
                } else {
1002
                    getList().setSelection(StructuredSelection.EMPTY);
1003
                }
1004

    
1005
            }
1006

    
1007
            scheduleProgressMessageRefresh();
1008
        }
1009

    
1010
        /**
1011
         * Updates the progress label.
1012
         *
1013
         * @deprecated
1014
         */
1015
        @Deprecated
1016
        public void updateProgressLabel() {
1017
            scheduleProgressMessageRefresh();
1018
        }
1019

    
1020
        /**
1021
         * Notifies the content provider - fires filtering of content provider
1022
         * elements. During the filtering, a separator between history and workspace
1023
         * matches is added.
1024
         * <p>
1025
         * This is a long running operation and should be called in a job.
1026
         *
1027
         * @param checkDuplicates
1028
         *            <code>true</code> if data concerning elements duplication
1029
         *            should be computed - it takes much more time than the standard
1030
         *            filtering
1031
         * @param monitor
1032
         *            a progress monitor or <code>null</code> if no monitor is
1033
         *            available
1034
         */
1035
        public void reloadCache(boolean checkDuplicates, IProgressMonitor monitor) {
1036
            if (getList() != null && !getList().getTable().isDisposed()
1037
                    && contentProvider != null) {
1038
                contentProvider.reloadCache(checkDuplicates, monitor);
1039
            }
1040
        }
1041

    
1042
        /**
1043
         * Schedule refresh job.
1044
         */
1045
        public void scheduleRefresh() {
1046
            refreshCacheJob.cancelAll();
1047
            refreshCacheJob.schedule();
1048
        }
1049

    
1050
        /**
1051
         * Schedules progress message refresh.
1052
         */
1053
        public void scheduleProgressMessageRefresh() {
1054
            if (filterJob.getState() != Job.RUNNING
1055
                    && refreshProgressMessageJob.getState() != Job.RUNNING) {
1056
                refreshProgressMessageJob.scheduleProgressRefresh(null);
1057
            }
1058
        }
1059

    
1060
        /*
1061
         * (non-Javadoc)
1062
         *
1063
         * @see org.eclipse.ui.dialogs.SelectionStatusDialog#computeResult()
1064
         */
1065
        @Override
1066
        protected void computeResult() {
1067

    
1068
            List selectedElements = ((StructuredSelection) getList().getSelection())
1069
                    .toList();
1070

    
1071
            List objectsToReturn = new ArrayList();
1072

    
1073
            Object item = null;
1074

    
1075
            for (Iterator it = selectedElements.iterator(); it.hasNext();) {
1076
                item = it.next();
1077

    
1078
                if (!(item instanceof ItemsListSeparator)) {
1079
                    accessedHistoryItem(item);
1080
                    objectsToReturn.add(item);
1081
                }
1082
            }
1083

    
1084
            setResult(objectsToReturn);
1085
        }
1086

    
1087
        /*
1088
         * @see org.eclipse.ui.dialogs.SelectionStatusDialog#updateStatus(org.eclipse.core.runtime.IStatus)
1089
         */
1090
        @Override
1091
        protected void updateStatus(IStatus status) {
1092
            this.status = status;
1093
            super.updateStatus(status);
1094
        }
1095

    
1096
        /*
1097
         * @see Dialog#okPressed()
1098
         */
1099
        @Override
1100
        protected void okPressed() {
1101
            if (status != null
1102
                    && (status.isOK() || status.getCode() == IStatus.INFO)) {
1103
                super.okPressed();
1104
            }
1105
        }
1106

    
1107
        /**
1108
         * Sets the initial pattern used by the filter. This text is copied into the
1109
         * selection input on the dialog. A full selection is used in the pattern
1110
         * input field.
1111
         *
1112
         * @param text
1113
         *            initial pattern for the filter
1114
         * @see FilteredItemsSelectionDialog#FULL_SELECTION
1115
         */
1116
        public void setInitialPattern(String text) {
1117
            setInitialPattern(text, FULL_SELECTION);
1118
        }
1119

    
1120
        /**
1121
         * Sets the initial pattern used by the filter. This text is copied into the
1122
         * selection input on the dialog. The <code>selectionMode</code> is used
1123
         * to choose selection type for the input field.
1124
         *
1125
         * @param text
1126
         *            initial pattern for the filter
1127
         * @param selectionMode
1128
         *            one of: {@link FilteredItemsSelectionDialog#NONE},
1129
         *            {@link FilteredItemsSelectionDialog#CARET_BEGINNING},
1130
         *            {@link FilteredItemsSelectionDialog#FULL_SELECTION}
1131
         */
1132
        public void setInitialPattern(String text, int selectionMode) {
1133
            this.initialPatternText = text;
1134
            this.selectionMode = selectionMode;
1135
        }
1136

    
1137
        /**
1138
         * Gets initial pattern.
1139
         *
1140
         * @return initial pattern, or <code>null</code> if initial pattern is not
1141
         *         set
1142
         */
1143
        protected String getInitialPattern() {
1144
            return this.initialPatternText;
1145
        }
1146

    
1147
        /**
1148
         * Returns the current selection.
1149
         *
1150
         * @return the current selection
1151
         */
1152
        protected StructuredSelection getSelectedItems() {
1153

    
1154
            StructuredSelection selection = (StructuredSelection) getList()
1155
                    .getSelection();
1156

    
1157
            List selectedItems = selection.toList();
1158
            Object itemToRemove = null;
1159

    
1160
            for (Iterator it = selection.iterator(); it.hasNext();) {
1161
                Object item = it.next();
1162
                if (item instanceof ItemsListSeparator) {
1163
                    itemToRemove = item;
1164
                    break;
1165
                }
1166
            }
1167

    
1168
            if (itemToRemove == null) {
1169
                return new StructuredSelection(selectedItems);
1170
            }
1171
            // Create a new selection without the collision
1172
            List newItems = new ArrayList(selectedItems);
1173
            newItems.remove(itemToRemove);
1174
            return new StructuredSelection(newItems);
1175

    
1176
        }
1177

    
1178
        /**
1179
         * Validates the item. When items on the items list are selected or
1180
         * deselected, it validates each item in the selection and the dialog status
1181
         * depends on all validations.
1182
         *
1183
         * @param item
1184
         *            an item to be checked
1185
         * @return status of the dialog to be set
1186
         */
1187
        protected abstract IStatus validateItem(Object item);
1188

    
1189
        /**
1190
         * Creates an instance of a filter.
1191
         *
1192
         * @return a filter for items on the items list. Can be <code>null</code>,
1193
         *         no filtering will be applied then, causing no item to be shown in
1194
         *         the list.
1195
         */
1196
        protected abstract ItemsFilter createFilter();
1197

    
1198
        /**
1199
         *
1200
         * Applies the filter created by <code>createFilter()</code> method to the
1201
         * items list. When new filter is different than previous one it will cause
1202
         * refiltering.
1203
         */
1204
        protected void applyFilter() {
1205
            // to get an already filtered selection of the database we added the initModel() method here.
1206
            initModel();
1207
            ItemsFilter newFilter = createFilter();
1208

    
1209
            // don't apply filtering for patterns which mean the same, for example:
1210
            // *a**b and ***a*b
1211
            if (filter != null && filter.equalsFilter(newFilter)) {
1212
                return;
1213
            }
1214

    
1215
            filterHistoryJob.cancel();
1216
            filterJob.cancel();
1217

    
1218
            this.filter = newFilter;
1219

    
1220
            if (this.filter != null) {
1221
                filterHistoryJob.schedule();
1222
            }
1223
        }
1224

    
1225
        /**
1226
         * Returns comparator to sort items inside content provider. Returned object
1227
         * will be probably created as an anonymous class. Parameters passed to the
1228
         * <code>compare(java.lang.Object, java.lang.Object)</code> are going to
1229
         * be the same type as the one used in the content provider.
1230
         *
1231
         * @return comparator to sort items content provider
1232
         */
1233
        protected abstract Comparator getItemsComparator();
1234

    
1235
        /**
1236
         * Fills the content provider with matching items.
1237
         *
1238
         * @param contentProvider
1239
         *            collector to add items to.
1240
         *            {@link FilteredItemsSelectionDialog.AbstractContentProvider#add(Object, FilteredItemsSelectionDialog.ItemsFilter)}
1241
         *            only adds items that pass the given <code>itemsFilter</code>.
1242
         * @param itemsFilter
1243
         *            the items filter
1244
         * @param progressMonitor
1245
         *            must be used to report search progress. The state of this
1246
         *            progress monitor reflects the state of the filtering process.
1247
         * @throws CoreException
1248
         */
1249
        protected abstract void fillContentProvider(
1250
                AbstractContentProvider contentProvider, ItemsFilter itemsFilter,
1251
                IProgressMonitor progressMonitor) throws CoreException;
1252

    
1253
        /**
1254
         * Removes selected items from history.
1255
         *
1256
         * @param items
1257
         *            items to be removed
1258
         */
1259
        private void removeSelectedItems(List items) {
1260
            for (Iterator iter = items.iterator(); iter.hasNext();) {
1261
                Object item = iter.next();
1262
                removeHistoryItem(item);
1263
            }
1264
            refreshWithLastSelection = false;
1265
            contentProvider.refresh();
1266
        }
1267

    
1268
        /**
1269
         * Removes an item from history.
1270
         *
1271
         * @param item
1272
         *            an item to remove
1273
         * @return removed item
1274
         */
1275
        protected Object removeHistoryItem(Object item) {
1276
            return contentProvider.removeHistoryElement(item);
1277
        }
1278

    
1279
        /**
1280
         * Adds item to history.
1281
         *
1282
         * @param item
1283
         *            the item to be added
1284
         */
1285
        protected void accessedHistoryItem(Object item) {
1286
            contentProvider.addHistoryElement(item);
1287
        }
1288

    
1289
        /**
1290
         * Returns a history comparator.
1291
         *
1292
         * @return decorated comparator
1293
         */
1294
        private Comparator getHistoryComparator() {
1295
            return new HistoryComparator();
1296
        }
1297

    
1298
        /**
1299
         * Returns the history of selected elements.
1300
         *
1301
         * @return history of selected elements, or <code>null</code> if it is not
1302
         *         set
1303
         */
1304
        protected SelectionHistory getSelectionHistory() {
1305
            return this.contentProvider.getSelectionHistory();
1306
        }
1307

    
1308
        /**
1309
         * Sets new history.
1310
         *
1311
         * @param selectionHistory
1312
         *            the history
1313
         */
1314
        protected void setSelectionHistory(SelectionHistory selectionHistory) {
1315
            if (this.contentProvider != null) {
1316
                this.contentProvider.setSelectionHistory(selectionHistory);
1317
            }
1318
        }
1319

    
1320
        /**
1321
         * Indicates whether the given item is a history item.
1322
         *
1323
         * @param item
1324
         *            the item to be investigated
1325
         * @return <code>true</code> if the given item exists in history,
1326
         *         <code>false</code> otherwise
1327
         */
1328
        public boolean isHistoryElement(Object item) {
1329
            return this.contentProvider.isHistoryElement(item);
1330
        }
1331

    
1332
        /**
1333
         * Indicates whether the given item is a duplicate.
1334
         *
1335
         * @param item
1336
         *            the item to be investigated
1337
         * @return <code>true</code> if the item is duplicate, <code>false</code>
1338
         *         otherwise
1339
         */
1340
        public boolean isDuplicateElement(Object item) {
1341
            return this.contentProvider.isDuplicateElement(item);
1342
        }
1343

    
1344
        /**
1345
         * Sets separator label
1346
         *
1347
         * @param separatorLabel
1348
         *            the label showed on separator
1349
         */
1350
        public void setSeparatorLabel(String separatorLabel) {
1351
            this.itemsListSeparator = new ItemsListSeparator(separatorLabel);
1352
        }
1353

    
1354
        /**
1355
         * Returns name for then given object.
1356
         *
1357
         * @param item
1358
         *            an object from the content provider. Subclasses should pay
1359
         *            attention to the passed argument. They should either only pass
1360
         *            objects of a known type (one used in content provider) or make
1361
         *            sure that passed parameter is the expected one (by type
1362
         *            checking like <code>instanceof</code> inside the method).
1363
         * @return name of the given item
1364
         */
1365
        public abstract String getElementName(Object item);
1366

    
1367
        private class ToggleStatusLineAction extends Action {
1368

    
1369
            /**
1370
             * Creates a new instance of the class.
1371
             */
1372
            public ToggleStatusLineAction() {
1373
                super(
1374
                        WorkbenchMessages.FilteredItemsSelectionDialog_toggleStatusAction,
1375
                        IAction.AS_CHECK_BOX);
1376
            }
1377

    
1378
            @Override
1379
            public void run() {
1380
                details.setVisible(isChecked());
1381
            }
1382
        }
1383

    
1384
        /**
1385
         * Only refreshes UI on the basis of an already sorted and filtered set of
1386
         * items.
1387
         * <p>
1388
         * Standard invocation scenario:
1389
         * <ol>
1390
         * <li>filtering job (<code>FilterJob</code> class extending
1391
         * <code>Job</code> class)</li>
1392
         * <li>cache refresh without checking for duplicates (<code>RefreshCacheJob</code>
1393
         * class extending <code>Job</code> class)</li>
1394
         * <li>UI refresh (<code>RefreshJob</code> class extending
1395
         * <code>UIJob</code> class)</li>
1396
         * <li>cache refresh with checking for duplicates (<cod>CacheRefreshJob</code>
1397
         * class extending <code>Job</code> class)</li>
1398
         * <li>UI refresh (<code>RefreshJob</code> class extending <code>UIJob</code>
1399
         * class)</li>
1400
         * </ol>
1401
         * The scenario is rather complicated, but it had to be applied, because:
1402
         * <ul>
1403
         * <li> refreshing cache is rather a long action and cannot be run in the UI -
1404
         * cannot be run in a UIJob</li>
1405
         * <li> refreshing cache checking for duplicates is twice as long as
1406
         * refreshing cache without checking for duplicates; results of the search
1407
         * could be displayed earlier</li>
1408
         * <li> refreshing the UI have to be run in a UIJob</li>
1409
         * </ul>
1410
         *
1411
         * @see org.eclipse.ui.dialogs.FilteredItemsSelectionDialog.FilterJob
1412
         * @see org.eclipse.ui.dialogs.FilteredItemsSelectionDialog.RefreshJob
1413
         * @see org.eclipse.ui.dialogs.FilteredItemsSelectionDialog.RefreshCacheJob
1414
         */
1415
        private class RefreshJob extends UIJob {
1416

    
1417
            /**
1418
             * Creates a new instance of the class.
1419
             */
1420
            public RefreshJob() {
1421
                super(CdmFilteredItemsSelectionDialog.this.getParentShell()
1422
                        .getDisplay(),
1423
                        WorkbenchMessages.FilteredItemsSelectionDialog_refreshJob);
1424
                setSystem(true);
1425
            }
1426

    
1427
            /*
1428
             * (non-Javadoc)
1429
             *
1430
             * @see org.eclipse.ui.progress.UIJob#runInUIThread(org.eclipse.core.runtime.IProgressMonitor)
1431
             */
1432
            @Override
1433
            public IStatus runInUIThread(IProgressMonitor monitor) {
1434
                if (monitor.isCanceled()) {
1435
                    return new Status(IStatus.OK, WorkbenchPlugin.PI_WORKBENCH,
1436
                            IStatus.OK, EMPTY_STRING, null);
1437
                }
1438

    
1439
                if (CdmFilteredItemsSelectionDialog.this != null) {
1440
                    CdmFilteredItemsSelectionDialog.this.refresh();
1441
                }
1442

    
1443
                return new Status(IStatus.OK, PlatformUI.PLUGIN_ID, IStatus.OK,
1444
                        EMPTY_STRING, null);
1445
            }
1446

    
1447
        }
1448

    
1449
        /**
1450
         * Refreshes the progress message cyclically with 500 milliseconds delay.
1451
         * <code>RefreshProgressMessageJob</code> is strictly connected with
1452
         * <code>GranualProgressMonitor</code> and use it to to get progress
1453
         * message and to decide about break of cyclical refresh.
1454
         */
1455
        private class RefreshProgressMessageJob extends UIJob {
1456

    
1457
            private GranualProgressMonitor progressMonitor;
1458

    
1459
            /**
1460
             * Creates a new instance of the class.
1461
             */
1462
            public RefreshProgressMessageJob() {
1463
                super(
1464
                        CdmFilteredItemsSelectionDialog.this.getParentShell()
1465
                                .getDisplay(),
1466
                        WorkbenchMessages.FilteredItemsSelectionDialog_progressRefreshJob);
1467
                setSystem(true);
1468
            }
1469

    
1470
            /*
1471
             * (non-Javadoc)
1472
             *
1473
             * @see org.eclipse.ui.progress.UIJob#runInUIThread(org.eclipse.core.runtime.IProgressMonitor)
1474
             */
1475
            @Override
1476
            public IStatus runInUIThread(IProgressMonitor monitor) {
1477

    
1478
                if (!progressLabel.isDisposed()) {
1479
                    progressLabel.setText(progressMonitor != null ? progressMonitor
1480
                            .getMessage() : EMPTY_STRING);
1481
                }
1482

    
1483
                if (progressMonitor == null || progressMonitor.isDone()) {
1484
                    return new Status(IStatus.CANCEL, PlatformUI.PLUGIN_ID,
1485
                            IStatus.CANCEL, EMPTY_STRING, null);
1486
                }
1487

    
1488
                // Schedule cyclical with 500 milliseconds delay
1489
                schedule(500);
1490

    
1491
                return new Status(IStatus.OK, PlatformUI.PLUGIN_ID, IStatus.OK,
1492
                        EMPTY_STRING, null);
1493
            }
1494

    
1495
            /**
1496
             * Schedule progress refresh job.
1497
             *
1498
             * @param progressMonitor
1499
             *            used during refresh progress label
1500
             */
1501
            public void scheduleProgressRefresh(
1502
                    GranualProgressMonitor progressMonitor) {
1503
                this.progressMonitor = progressMonitor;
1504
                // Schedule with initial delay to avoid flickering when the user
1505
                // types quickly
1506
                schedule(200);
1507
            }
1508

    
1509
        }
1510

    
1511
        /**
1512
         * A job responsible for computing filtered items list presented using
1513
         * <code>RefreshJob</code>.
1514
         *
1515
         * @see FilteredItemsSelectionDialog.RefreshJob
1516
         *
1517
         */
1518
        private class RefreshCacheJob extends Job {
1519

    
1520
            private final RefreshJob refreshJob = new RefreshJob();
1521

    
1522
            /**
1523
             * Creates a new instance of the class.
1524
             */
1525
            public RefreshCacheJob() {
1526
                super(
1527
                        WorkbenchMessages.FilteredItemsSelectionDialog_cacheRefreshJob);
1528
                setSystem(true);
1529
            }
1530

    
1531
            /**
1532
             * Stops the job and all sub-jobs.
1533
             */
1534
            public void cancelAll() {
1535
                cancel();
1536
                refreshJob.cancel();
1537
            }
1538

    
1539
            /*
1540
             * (non-Javadoc)
1541
             *
1542
             * @see org.eclipse.core.runtime.jobs.Job#run(org.eclipse.core.runtime.IProgressMonitor)
1543
             */
1544
            @Override
1545
            protected IStatus run(IProgressMonitor monitor) {
1546
                if (monitor.isCanceled()) {
1547
                    return new Status(IStatus.CANCEL, WorkbenchPlugin.PI_WORKBENCH,
1548
                            IStatus.CANCEL, EMPTY_STRING, null);
1549
                }
1550

    
1551
                if (CdmFilteredItemsSelectionDialog.this != null) {
1552
                    GranualProgressMonitor wrappedMonitor = new GranualProgressMonitor(
1553
                            monitor);
1554
                    CdmFilteredItemsSelectionDialog.this.reloadCache(true,
1555
                            wrappedMonitor);
1556
                }
1557

    
1558
                if (!monitor.isCanceled()) {
1559
                    refreshJob.schedule();
1560
                }
1561

    
1562
                return new Status(IStatus.OK, PlatformUI.PLUGIN_ID, IStatus.OK,
1563
                        EMPTY_STRING, null);
1564

    
1565
            }
1566

    
1567
            /*
1568
             * (non-Javadoc)
1569
             *
1570
             * @see org.eclipse.core.runtime.jobs.Job#canceling()
1571
             */
1572
            @Override
1573
            protected void canceling() {
1574
                super.canceling();
1575
                contentProvider.stopReloadingCache();
1576
            }
1577

    
1578
        }
1579

    
1580
        private class RemoveHistoryItemAction extends Action {
1581

    
1582
            /**
1583
             * Creates a new instance of the class.
1584
             */
1585
            public RemoveHistoryItemAction() {
1586
                super(
1587
                        WorkbenchMessages.FilteredItemsSelectionDialog_removeItemsFromHistoryAction);
1588
            }
1589

    
1590
            /*
1591
             * (non-Javadoc)
1592
             *
1593
             * @see org.eclipse.jface.action.Action#run()
1594
             */
1595
            @Override
1596
            public void run() {
1597
                List selectedElements = ((StructuredSelection) getList().getSelection())
1598
                        .toList();
1599
                removeSelectedItems(selectedElements);
1600
            }
1601
        }
1602

    
1603
        protected static boolean showColoredLabels() {
1604
            return PlatformUI.getPreferenceStore().getBoolean(IWorkbenchPreferenceConstants.USE_COLORED_LABELS);
1605
        }
1606

    
1607
        private class ItemsListLabelProvider extends StyledCellLabelProvider
1608
                implements ILabelProviderListener {
1609
            private ILabelProvider provider;
1610

    
1611
            private ILabelDecorator selectionDecorator;
1612

    
1613
            // Need to keep our own list of listeners
1614
            private final ListenerList listeners = new ListenerList();
1615

    
1616
            /**
1617
             * Creates a new instance of the class.
1618
             *
1619
             * @param provider
1620
             *            the label provider for all items, not <code>null</code>
1621
             * @param selectionDecorator
1622
             *            the decorator for selected items, can be <code>null</code>
1623
             */
1624
            public ItemsListLabelProvider(ILabelProvider provider,
1625
                    ILabelDecorator selectionDecorator) {
1626
                Assert.isNotNull(provider);
1627
                this.provider = provider;
1628
                this.selectionDecorator = selectionDecorator;
1629

    
1630
                setOwnerDrawEnabled(showColoredLabels() && provider instanceof IStyledLabelProvider);
1631

    
1632
                provider.addListener(this);
1633

    
1634
                if (selectionDecorator != null) {
1635
                    selectionDecorator.addListener(this);
1636
                }
1637
            }
1638

    
1639
            /**
1640
             * Sets new selection decorator.
1641
             *
1642
             * @param newSelectionDecorator
1643
             *            new label decorator for selected items in the list
1644
             */
1645
            public void setSelectionDecorator(ILabelDecorator newSelectionDecorator) {
1646
                if (selectionDecorator != null) {
1647
                    selectionDecorator.removeListener(this);
1648
                    selectionDecorator.dispose();
1649
                }
1650

    
1651
                selectionDecorator = newSelectionDecorator;
1652

    
1653
                if (selectionDecorator != null) {
1654
                    selectionDecorator.addListener(this);
1655
                }
1656
            }
1657

    
1658
            /**
1659
             * Gets selection decorator.
1660
             *
1661
             * @return the label decorator for selected items in the list
1662
             */
1663
            public ILabelDecorator getSelectionDecorator() {
1664
                return selectionDecorator;
1665
            }
1666

    
1667
            /**
1668
             * Sets new label provider.
1669
             *
1670
             * @param newProvider
1671
             *            new label provider for items in the list, not
1672
             *            <code>null</code>
1673
             */
1674
            public void setProvider(ILabelProvider newProvider) {
1675
                Assert.isNotNull(newProvider);
1676
                provider.removeListener(this);
1677
                provider.dispose();
1678

    
1679
                provider = newProvider;
1680

    
1681
                if (provider != null) {
1682
                    provider.addListener(this);
1683
                }
1684

    
1685
                setOwnerDrawEnabled(showColoredLabels() && provider instanceof IStyledLabelProvider);
1686
            }
1687

    
1688
            private Image getImage(Object element) {
1689
                if (element instanceof ItemsListSeparator) {
1690
                    return WorkbenchImages
1691
                            .getImage(IWorkbenchGraphicConstants.IMG_OBJ_SEPARATOR);
1692
                }
1693

    
1694
                return provider.getImage(element);
1695
            }
1696

    
1697
            private boolean isSelected(Object element) {
1698
                if (element != null && getCurrentSelection() != null) {
1699
                    for (int i = 0; i < getCurrentSelection().length; i++) {
1700
                        if (element.equals(getCurrentSelection()[i])) {
1701
                            return true;
1702
                        }
1703
                    }
1704
                }
1705
                return false;
1706
            }
1707

    
1708
            /*
1709
             * (non-Javadoc)
1710
             *
1711
             * @see org.eclipse.jface.viewers.ILabelProvider#getText(java.lang.Object)
1712
             */
1713
            private String getText(Object element) {
1714
                if (element instanceof ItemsListSeparator) {
1715
                    return getSeparatorLabel(((ItemsListSeparator) element)
1716
                            .getName());
1717
                }
1718

    
1719
                String str = provider.getText(element);
1720
                if (selectionDecorator != null && isSelected(element)) {
1721
                    return selectionDecorator.decorateText(str.toString(), element);
1722
                }
1723

    
1724
                return str;
1725
            }
1726

    
1727
            private StyledString getStyledText(Object element,
1728
                    IStyledLabelProvider provider) {
1729
                StyledString string = provider.getStyledText(element);
1730

    
1731
                if (selectionDecorator != null && isSelected(element)) {
1732
                    String decorated = selectionDecorator.decorateText(string
1733
                            .getString(), element);
1734
                    return StyledCellLabelProvider.styleDecoratedString(decorated, null, string);
1735
                    // no need to add colors when element is selected
1736
                }
1737
                return string;
1738
            }
1739

    
1740
            @Override
1741
            public void update(ViewerCell cell) {
1742
                Object element = cell.getElement();
1743

    
1744
                if (!(element instanceof ItemsListSeparator)
1745
                        && provider instanceof IStyledLabelProvider) {
1746
                    IStyledLabelProvider styledLabelProvider = (IStyledLabelProvider) provider;
1747
                    StyledString styledString = getStyledText(element,
1748
                            styledLabelProvider);
1749

    
1750
                    cell.setText(styledString.getString());
1751
                    cell.setStyleRanges(styledString.getStyleRanges());
1752
                    cell.setImage(styledLabelProvider.getImage(element));
1753
                } else {
1754
                    cell.setText(getText(element));
1755
                    cell.setImage(getImage(element));
1756
                }
1757
                cell.setFont(getFont(element));
1758
                cell.setForeground(getForeground(element));
1759
                cell.setBackground(getBackground(element));
1760

    
1761
                super.update(cell);
1762
            }
1763

    
1764
            private String getSeparatorLabel(String separatorLabel) {
1765
                Rectangle rect = getList().getTable().getBounds();
1766

    
1767
                int borderWidth = getList().getTable().computeTrim(0, 0, 0, 0).width;
1768

    
1769
                int imageWidth = WorkbenchImages.getImage(
1770
                        IWorkbenchGraphicConstants.IMG_OBJ_SEPARATOR).getBounds().width;
1771

    
1772
                int width = rect.width - borderWidth - imageWidth;
1773

    
1774
                GC gc = new GC(getList().getTable());
1775
                gc.setFont(getList().getTable().getFont());
1776

    
1777
                int fSeparatorWidth = gc.getAdvanceWidth('-');
1778
                int fMessageLength = gc.textExtent(separatorLabel).x;
1779

    
1780
                gc.dispose();
1781

    
1782
                StringBuffer dashes = new StringBuffer();
1783
                int chars = (((width - fMessageLength) / fSeparatorWidth) / 2) - 2;
1784
                for (int i = 0; i < chars; i++) {
1785
                    dashes.append('-');
1786
                }
1787

    
1788
                StringBuffer result = new StringBuffer();
1789
                result.append(dashes);
1790
                result.append(" " + separatorLabel + " "); //$NON-NLS-1$//$NON-NLS-2$
1791
                result.append(dashes);
1792
                return result.toString().trim();
1793
            }
1794

    
1795
            /*
1796
             * (non-Javadoc)
1797
             *
1798
             * @see org.eclipse.jface.viewers.IBaseLabelProvider#addListener(org.eclipse.jface.viewers.ILabelProviderListener)
1799
             */
1800
            @Override
1801
            public void addListener(ILabelProviderListener listener) {
1802
                listeners.add(listener);
1803
            }
1804

    
1805
            /*
1806
             * (non-Javadoc)
1807
             *
1808
             * @see org.eclipse.jface.viewers.IBaseLabelProvider#dispose()
1809
             */
1810
            @Override
1811
            public void dispose() {
1812
                provider.removeListener(this);
1813
                provider.dispose();
1814

    
1815
                if (selectionDecorator != null) {
1816
                    selectionDecorator.removeListener(this);
1817
                    selectionDecorator.dispose();
1818
                }
1819

    
1820
                super.dispose();
1821
            }
1822

    
1823
            /*
1824
             * (non-Javadoc)
1825
             *
1826
             * @see org.eclipse.jface.viewers.IBaseLabelProvider#isLabelProperty(java.lang.Object,
1827
             *      java.lang.String)
1828
             */
1829
            @Override
1830
            public boolean isLabelProperty(Object element, String property) {
1831
                if (provider.isLabelProperty(element, property)) {
1832
                    return true;
1833
                }
1834
                if (selectionDecorator != null
1835
                        && selectionDecorator.isLabelProperty(element, property)) {
1836
                    return true;
1837
                }
1838
                return false;
1839
            }
1840

    
1841
            /*
1842
             * (non-Javadoc)
1843
             *
1844
             * @see org.eclipse.jface.viewers.IBaseLabelProvider#removeListener(org.eclipse.jface.viewers.ILabelProviderListener)
1845
             */
1846
            @Override
1847
            public void removeListener(ILabelProviderListener listener) {
1848
                listeners.remove(listener);
1849
            }
1850

    
1851
            private Color getBackground(Object element) {
1852
                if (element instanceof ItemsListSeparator) {
1853
                    return null;
1854
                }
1855
                if (provider instanceof IColorProvider) {
1856
                    return ((IColorProvider) provider).getBackground(element);
1857
                }
1858
                return null;
1859
            }
1860

    
1861
            private Color getForeground(Object element) {
1862
                if (element instanceof ItemsListSeparator) {
1863
                    return Display.getCurrent().getSystemColor(
1864
                            SWT.COLOR_WIDGET_NORMAL_SHADOW);
1865
                }
1866
                if (provider instanceof IColorProvider) {
1867
                    return ((IColorProvider) provider).getForeground(element);
1868
                }
1869
                return null;
1870
            }
1871

    
1872
            private Font getFont(Object element) {
1873
                if (element instanceof ItemsListSeparator) {
1874
                    return null;
1875
                }
1876
                if (provider instanceof IFontProvider) {
1877
                    return ((IFontProvider) provider).getFont(element);
1878
                }
1879
                return null;
1880
            }
1881

    
1882
            /*
1883
             * (non-Javadoc)
1884
             *
1885
             * @see org.eclipse.jface.viewers.ILabelProviderListener#labelProviderChanged(org.eclipse.jface.viewers.LabelProviderChangedEvent)
1886
             */
1887
            @Override
1888
            public void labelProviderChanged(LabelProviderChangedEvent event) {
1889
                Object[] l = listeners.getListeners();
1890
                for (int i = 0; i < listeners.size(); i++) {
1891
                    ((ILabelProviderListener) l[i]).labelProviderChanged(event);
1892
                }
1893
            }
1894
        }
1895

    
1896
        /**
1897
         * Used in ItemsListContentProvider, separates history and non-history
1898
         * items.
1899
         */
1900
        protected class ItemsListSeparator {
1901

    
1902
            private final String name;
1903

    
1904
            /**
1905
             * Creates a new instance of the class.
1906
             *
1907
             * @param name
1908
             *            the name of the separator
1909
             */
1910
            public ItemsListSeparator(String name) {
1911
                this.name = name;
1912
            }
1913

    
1914
            /**
1915
             * Returns the name of this separator.
1916
             *
1917
             * @return the name of the separator
1918
             */
1919
            public String getName() {
1920
                return name;
1921
            }
1922
        }
1923

    
1924
        /**
1925
         * GranualProgressMonitor is used for monitoring progress of filtering
1926
         * process. It is used by <code>RefreshProgressMessageJob</code> to
1927
         * refresh progress message. State of this monitor illustrates state of
1928
         * filtering or cache refreshing process.
1929
         *
1930
         */
1931
        private class GranualProgressMonitor extends ProgressMonitorWrapper {
1932

    
1933
            private String name;
1934

    
1935
            private String subName;
1936

    
1937
            private int totalWork;
1938

    
1939
            private double worked;
1940

    
1941
            private boolean done;
1942

    
1943
            /**
1944
             * Creates instance of <code>GranualProgressMonitor</code>.
1945
             *
1946
             * @param monitor
1947
             *            progress to be wrapped
1948
             */
1949
            public GranualProgressMonitor(IProgressMonitor monitor) {
1950
                super(monitor);
1951
            }
1952

    
1953
            /**
1954
             * Checks if filtering has been done
1955
             *
1956
             * @return true if filtering work has been done false in other way
1957
             */
1958
            public boolean isDone() {
1959
                return done;
1960
            }
1961

    
1962
            /*
1963
             * (non-Javadoc)
1964
             *
1965
             * @see org.eclipse.core.runtime.ProgressMonitorWrapper#setTaskName(java.lang.String)
1966
             */
1967
            @Override
1968
            public void setTaskName(String name) {
1969
                super.setTaskName(name);
1970
                this.name = name;
1971
                this.subName = null;
1972
            }
1973

    
1974
            /*
1975
             * (non-Javadoc)
1976
             *
1977
             * @see org.eclipse.core.runtime.ProgressMonitorWrapper#subTask(java.lang.String)
1978
             */
1979
            @Override
1980
            public void subTask(String name) {
1981
                super.subTask(name);
1982
                this.subName = name;
1983
            }
1984

    
1985
            /*
1986
             * (non-Javadoc)
1987
             *
1988
             * @see org.eclipse.core.runtime.ProgressMonitorWrapper#beginTask(java.lang.String,
1989
             *      int)
1990
             */
1991
            @Override
1992
            public void beginTask(String name, int totalWork) {
1993
                super.beginTask(name, totalWork);
1994
                if (this.name == null) {
1995
                    this.name = name;
1996
                }
1997
                this.totalWork = totalWork;
1998
                refreshProgressMessageJob.scheduleProgressRefresh(this);
1999
            }
2000

    
2001
            /*
2002
             * (non-Javadoc)
2003
             *
2004
             * @see org.eclipse.core.runtime.ProgressMonitorWrapper#worked(int)
2005
             */
2006
            @Override
2007
            public void worked(int work) {
2008
                super.worked(work);
2009
                internalWorked(work);
2010
            }
2011

    
2012
            /*
2013
             * (non-Javadoc)
2014
             *
2015
             * @see org.eclipse.core.runtime.ProgressMonitorWrapper#done()
2016
             */
2017
            @Override
2018
            public void done() {
2019
                done = true;
2020
                super.done();
2021
            }
2022

    
2023
            /*
2024
             * (non-Javadoc)
2025
             *
2026
             * @see org.eclipse.core.runtime.ProgressMonitorWrapper#setCanceled(boolean)
2027
             */
2028
            @Override
2029
            public void setCanceled(boolean b) {
2030
                done = b;
2031
                super.setCanceled(b);
2032
            }
2033

    
2034
            /*
2035
             * (non-Javadoc)
2036
             *
2037
             * @see org.eclipse.core.runtime.ProgressMonitorWrapper#internalWorked(double)
2038
             */
2039
            @Override
2040
            public void internalWorked(double work) {
2041
                worked = worked + work;
2042
            }
2043

    
2044
            private String getMessage() {
2045
                if (done) {
2046
                    return ""; //$NON-NLS-1$
2047
                }
2048

    
2049
                String message;
2050

    
2051
                if (name == null) {
2052
                    message = subName == null ? "" : subName; //$NON-NLS-1$
2053
                } else {
2054
                    message = subName == null ? name
2055
                            : NLS
2056
                                    .bind(
2057
                                            WorkbenchMessages.FilteredItemsSelectionDialog_subtaskProgressMessage,
2058
                                            new Object[] { name, subName });
2059
                }
2060
                if (totalWork == 0) {
2061
                    return message;
2062
                }
2063

    
2064
                return NLS
2065
                        .bind(
2066
                                WorkbenchMessages.FilteredItemsSelectionDialog_taskProgressMessage,
2067
                                new Object[] {
2068
                                        message,
2069
                                        new Integer(
2070
                                                (int) ((worked * 100) / totalWork)) });
2071

    
2072
            }
2073

    
2074
        }
2075

    
2076
        /**
2077
         * Filters items history and schedule filter job.
2078
         */
2079
        private class FilterHistoryJob extends Job {
2080

    
2081
            /**
2082
             * Filter used during the filtering process.
2083
             */
2084
            private ItemsFilter itemsFilter;
2085

    
2086
            /**
2087
             * Creates new instance of receiver.
2088
             */
2089
            public FilterHistoryJob() {
2090
                super(WorkbenchMessages.FilteredItemsSelectionDialog_jobLabel);
2091
                setSystem(true);
2092
            }
2093

    
2094
            /*
2095
             * (non-Javadoc)
2096
             *
2097
             * @see org.eclipse.core.runtime.jobs.Job#run(org.eclipse.core.runtime.IProgressMonitor)
2098
             */
2099
            @Override
2100
            protected IStatus run(IProgressMonitor monitor) {
2101

    
2102
                this.itemsFilter = filter;
2103

    
2104
                contentProvider.reset();
2105

    
2106
                refreshWithLastSelection = false;
2107

    
2108
                contentProvider.addHistoryItems(itemsFilter);
2109

    
2110
                if (!(lastCompletedFilter != null && lastCompletedFilter
2111
                        .isSubFilter(this.itemsFilter))) {
2112
                    contentProvider.refresh();
2113
                }
2114

    
2115
                filterJob.schedule();
2116

    
2117
                return Status.OK_STATUS;
2118
            }
2119

    
2120
        }
2121

    
2122
        /**
2123
         * Filters items in indicated set and history. During filtering, it
2124
         * refreshes the dialog (progress monitor and elements list).
2125
         *
2126
         * Depending on the filter, <code>FilterJob</code> decides which kind of
2127
         * search will be run inside <code>filterContent</code>. If the last
2128
         * filtering is done (last completed filter), is not null, and the new
2129
         * filter is a sub-filter ({@link FilteredItemsSelectionDialog.ItemsFilter#isSubFilter(FilteredItemsSelectionDialog.ItemsFilter)})
2130
         * of the last, then <code>FilterJob</code> only filters in the cache. If
2131
         * it is the first filtering or the new filter isn't a sub-filter of the
2132
         * last one, a full search is run.
2133
         */
2134
        private class FilterJob extends Job {
2135

    
2136
            /**
2137
             * Filter used during the filtering process.
2138
             */
2139
            protected ItemsFilter itemsFilter;
2140

    
2141
            /**
2142
             * Creates new instance of FilterJob
2143
             */
2144
            public FilterJob() {
2145
                super(WorkbenchMessages.FilteredItemsSelectionDialog_jobLabel);
2146
                setSystem(true);
2147
            }
2148

    
2149
            /*
2150
             * (non-Javadoc)
2151
             *
2152
             * @see org.eclipse.core.runtime.jobs.Job#run(org.eclipse.core.runtime.IProgressMonitor)
2153
             */
2154
            @Override
2155
            protected final IStatus run(IProgressMonitor parent) {
2156
                GranualProgressMonitor monitor = new GranualProgressMonitor(parent);
2157
                return doRun(monitor);
2158
            }
2159

    
2160
            /**
2161
             * Executes job using the given filtering progress monitor. A hook for
2162
             * subclasses.
2163
             *
2164
             * @param monitor
2165
             *            progress monitor
2166
             * @return result of the execution
2167
             */
2168
            protected IStatus doRun(GranualProgressMonitor monitor) {
2169
                try {
2170
                    internalRun(monitor);
2171
                } catch (CoreException e) {
2172
                    cancel();
2173
                    return new Status(
2174
                            IStatus.ERROR,
2175
                            PlatformUI.PLUGIN_ID,
2176
                            IStatus.ERROR,
2177
                            WorkbenchMessages.FilteredItemsSelectionDialog_jobError,
2178
                            e);
2179
                }
2180
                return Status.OK_STATUS;
2181
            }
2182

    
2183
            /**
2184
             * Main method for the job.
2185
             *
2186
             * @param monitor
2187
             * @throws CoreException
2188
             */
2189
            private void internalRun(GranualProgressMonitor monitor)
2190
                    throws CoreException {
2191
                try {
2192
                    if (monitor.isCanceled()) {
2193
                        return;
2194
                    }
2195

    
2196
                    this.itemsFilter = filter;
2197

    
2198
                    if (filter.getPattern().length() != 0) {
2199
                        filterContent(monitor);
2200
                    }
2201

    
2202
                    if (monitor.isCanceled()) {
2203
                        return;
2204
                    }
2205

    
2206
                    contentProvider.refresh();
2207
                } finally {
2208
                    monitor.done();
2209
                }
2210
            }
2211

    
2212
            /**
2213
             * Filters items.
2214
             *
2215
             * @param monitor
2216
             *            for monitoring progress
2217
             * @throws CoreException
2218
             */
2219
            protected void filterContent(GranualProgressMonitor monitor)
2220
                    throws CoreException {
2221

    
2222
//                if (lastCompletedFilter != null
2223
//                        && lastCompletedFilter.isSubFilter(this.itemsFilter)) {
2224
//
2225
//                    int length = lastCompletedResult.size() / 500;
2226
//                    monitor
2227
//                            .beginTask(
2228
//                                    WorkbenchMessages.FilteredItemsSelectionDialog_cacheSearchJob_taskName,
2229
//                                    length);
2230
//
2231
//                    for (int pos = 0; pos < lastCompletedResult.size(); pos++) {
2232
//
2233
//                        Object item = lastCompletedResult.get(pos);
2234
//                        if (monitor.isCanceled()) {
2235
//                            break;
2236
//                        }
2237
//                        contentProvider.add(item, itemsFilter);
2238
//
2239
//                        if ((pos % 500) == 0) {
2240
//                            monitor.worked(1);
2241
//                        }
2242
//                    }
2243
//
2244
//                } else {
2245

    
2246
                    lastCompletedFilter = null;
2247
                    lastCompletedResult = null;
2248

    
2249
                    SubProgressMonitor subMonitor = null;
2250
                    if (monitor != null) {
2251
                        monitor
2252
                                .beginTask(
2253
                                        WorkbenchMessages.FilteredItemsSelectionDialog_searchJob_taskName,
2254
                                        100);
2255
                        subMonitor = new SubProgressMonitor(monitor, 95);
2256

    
2257
                    }
2258

    
2259
                    fillContentProvider(contentProvider, itemsFilter, subMonitor);
2260

    
2261
                    if (monitor != null && !monitor.isCanceled()) {
2262
                        monitor.worked(2);
2263
                        contentProvider.rememberResult(itemsFilter);
2264
                        monitor.worked(3);
2265
                    }
2266
                //}
2267

    
2268
            }
2269

    
2270
        }
2271

    
2272
        /**
2273
         * History stores a list of key, object pairs. The list is bounded at a
2274
         * certain size. If the list exceeds this size the oldest element is removed
2275
         * from the list. An element can be added/renewed with a call to
2276
         * <code>accessed(Object)</code>.
2277
         * <p>
2278
         * The history can be stored to/loaded from an XML file.
2279
         */
2280
        protected static abstract class SelectionHistory {
2281

    
2282
            private static final String DEFAULT_ROOT_NODE_NAME = "historyRootNode"; //$NON-NLS-1$
2283

    
2284
            private static final String DEFAULT_INFO_NODE_NAME = "infoNode"; //$NON-NLS-1$
2285

    
2286
            private static final int MAX_HISTORY_SIZE = 60;
2287

    
2288
            private final Set historyList;
2289

    
2290
            private final String rootNodeName;
2291

    
2292
            private final String infoNodeName;
2293

    
2294
            private SelectionHistory(String rootNodeName, String infoNodeName) {
2295

    
2296
                historyList = Collections.synchronizedSet(new LinkedHashSet() {
2297

    
2298
                    private static final long serialVersionUID = 0L;
2299

    
2300
                    /*
2301
                     * (non-Javadoc)
2302
                     *
2303
                     * @see java.util.LinkedList#add(java.lang.Object)
2304
                     */
2305
                    @Override
2306
                    public boolean add(Object arg0) {
2307
                        if (this.size() >= MAX_HISTORY_SIZE) {
2308
                            Iterator iterator = this.iterator();
2309
                            iterator.next();
2310
                            iterator.remove();
2311
                        }
2312
                        return super.add(arg0);
2313
                    }
2314

    
2315
                });
2316

    
2317
                this.rootNodeName = rootNodeName;
2318
                this.infoNodeName = infoNodeName;
2319
            }
2320

    
2321
            /**
2322
             * Creates new instance of <code>SelectionHistory</code>.
2323
             */
2324
            public SelectionHistory() {
2325
                this(DEFAULT_ROOT_NODE_NAME, DEFAULT_INFO_NODE_NAME);
2326
            }
2327

    
2328
            /**
2329
             * Adds object to history.
2330
             *
2331
             * @param object
2332
             *            the item to be added to the history
2333
             */
2334
            public synchronized void accessed(Object object) {
2335
                historyList.remove(object);
2336
                historyList.add(object);
2337
            }
2338

    
2339
            /**
2340
             * Returns <code>true</code> if history contains object.
2341
             *
2342
             * @param object
2343
             *            the item for which check will be executed
2344
             * @return <code>true</code> if history contains object
2345
             *         <code>false</code> in other way
2346
             */
2347
            public synchronized boolean contains(Object object) {
2348
                return historyList.contains(object);
2349
            }
2350

    
2351
            /**
2352
             * Returns <code>true</code> if history is empty.
2353
             *
2354
             * @return <code>true</code> if history is empty
2355
             */
2356
            public synchronized boolean isEmpty() {
2357
                return historyList.isEmpty();
2358
            }
2359

    
2360
            /**
2361
             * Remove element from history.
2362
             *
2363
             * @param element
2364
             *            to remove form the history
2365
             * @return <code>true</code> if this list contained the specified
2366
             *         element
2367
             */
2368
            public synchronized boolean remove(Object element) {
2369
                return historyList.remove(element);
2370
            }
2371

    
2372
            /**
2373
             * Load history elements from memento.
2374
             *
2375
             * @param memento
2376
             *            memento from which the history will be retrieved
2377
             */
2378
            public void load(IMemento memento) {
2379

    
2380
                XMLMemento historyMemento = (XMLMemento) memento
2381
                        .getChild(rootNodeName);
2382

    
2383
                if (historyMemento == null) {
2384
                    return;
2385
                }
2386

    
2387
                IMemento[] mementoElements = historyMemento
2388
                        .getChildren(infoNodeName);
2389
                for (int i = 0; i < mementoElements.length; ++i) {
2390
                    IMemento mementoElement = mementoElements[i];
2391
                    Object object = restoreItemFromMemento(mementoElement);
2392
                    if (object != null) {
2393
                        historyList.add(object);
2394
                    }
2395
                }
2396
            }
2397

    
2398
            /**
2399
             * Save history elements to memento.
2400
             *
2401
             * @param memento
2402
             *            memento to which the history will be added
2403
             */
2404
            public void save(IMemento memento) {
2405

    
2406
                IMemento historyMemento = memento.createChild(rootNodeName);
2407

    
2408
                Object[] items = getHistoryItems();
2409
                for (int i = 0; i < items.length; i++) {
2410
                    Object item = items[i];
2411
                    IMemento elementMemento = historyMemento
2412
                            .createChild(infoNodeName);
2413
                    storeItemToMemento(item, elementMemento);
2414
                }
2415

    
2416
            }
2417

    
2418
            /**
2419
             * Gets array of history items.
2420
             *
2421
             * @return array of history elements
2422
             */
2423
            public synchronized Object[] getHistoryItems() {
2424
                return historyList.toArray();
2425
            }
2426

    
2427
            /**
2428
             * Creates an object using given memento.
2429
             *
2430
             * @param memento
2431
             *            memento used for creating new object
2432
             *
2433
             * @return the restored object
2434
             */
2435
            protected abstract Object restoreItemFromMemento(IMemento memento);
2436

    
2437
            /**
2438
             * Store object in <code>IMemento</code>.
2439
             *
2440
             * @param item
2441
             *            the item to store
2442
             * @param memento
2443
             *            the memento to store to
2444
             */
2445
            protected abstract void storeItemToMemento(Object item, IMemento memento);
2446

    
2447
        }
2448

    
2449
        /**
2450
         * Filters elements using SearchPattern by comparing the names of items with
2451
         * the filter pattern.
2452
         */
2453
        protected abstract class ItemsFilter {
2454

    
2455
            protected SearchPattern patternMatcher;
2456

    
2457
            /**
2458
             * Creates new instance of ItemsFilter.
2459
             */
2460
            public ItemsFilter() {
2461
                this(new SearchPattern());
2462
            }
2463

    
2464
            /**
2465
             * Creates new instance of ItemsFilter.
2466
             *
2467
             * @param searchPattern
2468
             *            the pattern to be used when filtering
2469
             */
2470
            public ItemsFilter(SearchPattern searchPattern) {
2471
                patternMatcher = searchPattern;
2472
                String stringPattern = ""; //$NON-NLS-1$
2473
                if (pattern != null && !pattern.getText().equals("*")) { //$NON-NLS-1$
2474
                    stringPattern = pattern.getText();
2475
                }
2476
                patternMatcher.setPattern(stringPattern);
2477
            }
2478

    
2479
            /**
2480
             * Check if the given filter is a sub-filter of this filter. The default
2481
             * implementation checks if the <code>SearchPattern</code> from the
2482
             * given filter is a sub-pattern of the one from this filter.
2483
             * <p>
2484
             * <i>WARNING: This method is <b>not</b> defined in reading order, i.e.
2485
             * <code>a.isSubFilter(b)</code> is <code>true</code> iff
2486
             * <code>b</code> is a sub-filter of <code>a</code>, and not
2487
             * vice-versa. </i>
2488
             * </p>
2489
             *
2490
             * @param filter
2491
             *            the filter to be checked, or <code>null</code>
2492
             * @return <code>true</code> if the given filter is sub-filter of this
2493
             *         filter, <code>false</code> if the given filter isn't a
2494
             *         sub-filter or is <code>null</code>
2495
             *
2496
             * @see org.eclipse.ui.dialogs.SearchPattern#isSubPattern(org.eclipse.ui.dialogs.SearchPattern)
2497
             */
2498
            public boolean isSubFilter(ItemsFilter filter) {
2499
                if (filter != null) {
2500
                    return this.patternMatcher.isSubPattern(filter.patternMatcher);
2501
                }
2502
                return false;
2503
            }
2504

    
2505
            /**
2506
             * Checks whether the provided filter is equal to the current filter.
2507
             * The default implementation checks if <code>SearchPattern</code>
2508
             * from current filter is equal to the one from provided filter.
2509
             *
2510
             * @param filter
2511
             *            filter to be checked, or <code>null</code>
2512
             * @return <code>true</code> if the given filter is equal to current
2513
             *         filter, <code>false</code> if given filter isn't equal to
2514
             *         current one or if it is <code>null</code>
2515
             *
2516
             * @see org.eclipse.ui.dialogs.SearchPattern#equalsPattern(org.eclipse.ui.dialogs.SearchPattern)
2517
             */
2518
            public boolean equalsFilter(ItemsFilter filter) {
2519
                if (filter != null
2520
                        && filter.patternMatcher.equalsPattern(this.patternMatcher)) {
2521
                    return true;
2522
                }
2523
                return false;
2524
            }
2525

    
2526
            /**
2527
             * Checks whether the pattern's match rule is camel case.
2528
             *
2529
             * @return <code>true</code> if pattern's match rule is camel case,
2530
             *         <code>false</code> otherwise
2531
             */
2532
            public boolean isCamelCasePattern() {
2533
                return patternMatcher.getMatchRule() == SearchPattern.RULE_CAMELCASE_MATCH;
2534
            }
2535

    
2536
            /**
2537
             * Returns the pattern string.
2538
             *
2539
             * @return pattern for this filter
2540
             *
2541
             * @see SearchPattern#getPattern()
2542
             */
2543
            public String getPattern() {
2544
                return patternMatcher.getPattern();
2545
            }
2546

    
2547
            /**
2548
             * Returns the rule to apply for matching keys.
2549
             *
2550
             * @return an implementation-specific match rule
2551
             *
2552
             * @see SearchPattern#getMatchRule() for match rules returned by the
2553
             *      default implementation
2554
             */
2555
            public int getMatchRule() {
2556
                return patternMatcher.getMatchRule();
2557
            }
2558

    
2559
            /**
2560
             * Matches text with filter.
2561
             *
2562
             * @param text
2563
             *            the text to match with the filter
2564
             * @return <code>true</code> if text matches with filter pattern,
2565
             *         <code>false</code> otherwise
2566
             */
2567
            protected boolean matches(String text) {
2568
                return patternMatcher.matches(text);
2569
            }
2570

    
2571
            /**
2572
             * General method for matching raw name pattern. Checks whether current
2573
             * pattern is prefix of name provided item.
2574
             *
2575
             * @param item
2576
             *            item to check
2577
             * @return <code>true</code> if current pattern is a prefix of name
2578
             *         provided item, <code>false</code> if item's name is shorter
2579
             *         than prefix or sequences of characters don't match.
2580
             */
2581
            public boolean matchesRawNamePattern(Object item) {
2582
                String prefix = patternMatcher.getPattern();
2583
                String text = getElementName(item);
2584

    
2585
                if (text == null) {
2586
                    return false;
2587
                }
2588

    
2589
                int textLength = text.length();
2590
                int prefixLength = prefix.length();
2591
                if (textLength < prefixLength) {
2592
                    return false;
2593
                }
2594
                for (int i = prefixLength - 1; i >= 0; i--) {
2595
                    if (Character.toLowerCase(prefix.charAt(i)) != Character
2596
                            .toLowerCase(text.charAt(i))) {
2597
                        return false;
2598
                    }
2599
                }
2600
                return true;
2601
            }
2602

    
2603
            /**
2604
             * Matches an item against filter conditions.
2605
             *
2606
             * @param item
2607
             * @return <code>true<code> if item matches against filter conditions, <code>false</code>
2608
             *         otherwise
2609
             */
2610
            public abstract boolean matchItem(Object item);
2611

    
2612
            /**
2613
             * Checks consistency of an item. Item is inconsistent if was changed or
2614
             * removed.
2615
             *
2616
             * @param item
2617
             * @return <code>true</code> if item is consistent, <code>false</code>
2618
             *         if item is inconsistent
2619
             */
2620
            public abstract boolean isConsistentItem(Object item);
2621

    
2622
        }
2623

    
2624
        /**
2625
         * An interface to content providers for
2626
         * <code>FilterItemsSelectionDialog</code>.
2627
         */
2628
        protected abstract class AbstractContentProvider {
2629
            /**
2630
             * Adds the item to the content provider iff the filter matches the
2631
             * item. Otherwise does nothing.
2632
             *
2633
             * @param item
2634
             *            the item to add
2635
             * @param itemsFilter
2636
             *            the filter
2637
             *
2638
             * @see FilteredItemsSelectionDialog.ItemsFilter#matchItem(Object)
2639
             */
2640
            public abstract void add(Object item, ItemsFilter itemsFilter);
2641
        }
2642

    
2643
        /**
2644
         * Collects filtered elements. Contains one synchronized, sorted set for
2645
         * collecting filtered elements. All collected elements are sorted using
2646
         * comparator. Comparator is returned by getElementComparator() method.
2647
         * Implementation of <code>ItemsFilter</code> is used to filter elements.
2648
         * The key function of filter used in to filtering is
2649
         * <code>matchElement(Object item)</code>.
2650
         * <p>
2651
         * The <code>ContentProvider</code> class also provides item filtering
2652
         * methods. The filtering has been moved from the standard TableView
2653
         * <code>getFilteredItems()</code> method to content provider, because
2654
         * <code>ILazyContentProvider</code> and virtual tables are used. This
2655
         * class is responsible for adding a separator below history items and
2656
         * marking each items as duplicate if its name repeats more than once on the
2657
         * filtered list.
2658
         */
2659
        private class ContentProvider extends AbstractContentProvider implements
2660
                IStructuredContentProvider, ILazyContentProvider {
2661

    
2662
            private SelectionHistory selectionHistory;
2663

    
2664
            /**
2665
             * Raw result of the searching (unsorted, unfiltered).
2666
             * <p>
2667
             * Standard object flow:
2668
             * <code>items -> lastSortedItems -> lastFilteredItems</code>
2669
             */
2670
            private final Set items;
2671

    
2672
            /**
2673
             * Items that are duplicates.
2674
             */
2675
            private final Set duplicates;
2676

    
2677
            /**
2678
             * List of <code>ViewerFilter</code>s to be used during filtering
2679
             */
2680
            private List filters;
2681

    
2682
            /**
2683
             * Result of the last filtering.
2684
             * <p>
2685
             * Standard object flow:
2686
             * <code>items -> lastSortedItems -> lastFilteredItems</code>
2687
             */
2688
            private List lastFilteredItems;
2689

    
2690
            /**
2691
             * Result of the last sorting.
2692
             * <p>
2693
             * Standard object flow:
2694
             * <code>items -> lastSortedItems -> lastFilteredItems</code>
2695
             */
2696
            private final List lastSortedItems;
2697

    
2698
            /**
2699
             * Used for <code>getFilteredItems()</code> method canceling (when the
2700
             * job that invoked the method was canceled).
2701
             * <p>
2702
             * Method canceling could be based (only) on monitor canceling
2703
             * unfortunately sometimes the method <code>getFilteredElements()</code>
2704
             * could be run with a null monitor, the <code>reset</code> flag have
2705
             * to be left intact.
2706
             */
2707
            private boolean reset;
2708

    
2709
            /**
2710
             * Creates new instance of <code>ContentProvider</code>.
2711
             */
2712
            public ContentProvider() {
2713
                this.items = Collections.synchronizedSet(new HashSet(2048));
2714
                this.duplicates = Collections.synchronizedSet(new HashSet(256));
2715
                this.lastFilteredItems = new ArrayList();
2716
                this.lastSortedItems = Collections.synchronizedList(new ArrayList(
2717
                        2048));
2718
            }
2719

    
2720
            /**
2721
             * Sets selection history.
2722
             *
2723
             * @param selectionHistory
2724
             *            The selectionHistory to set.
2725
             */
2726
            public void setSelectionHistory(SelectionHistory selectionHistory) {
2727
                this.selectionHistory = selectionHistory;
2728
            }
2729

    
2730
            /**
2731
             * @return Returns the selectionHistory.
2732
             */
2733
            public SelectionHistory getSelectionHistory() {
2734
                return selectionHistory;
2735
            }
2736

    
2737
            /**
2738
             * Removes all content items and resets progress message.
2739
             */
2740
            public void reset() {
2741
                reset = true;
2742
                this.items.clear();
2743
                this.duplicates.clear();
2744
                this.lastSortedItems.clear();
2745
            }
2746

    
2747
            /**
2748
             * Stops reloading cache - <code>getFilteredItems()</code> method.
2749
             */
2750
            public void stopReloadingCache() {
2751
                reset = true;
2752
            }
2753

    
2754
            /**
2755
             * Adds filtered item.
2756
             *
2757
             * @param item
2758
             * @param itemsFilter
2759
             */
2760
            @Override
2761
            public void add(Object item, ItemsFilter itemsFilter) {
2762
                if (itemsFilter == filter) {
2763
                    if (itemsFilter != null) {
2764
                        if (itemsFilter.matchItem(item)) {
2765
                            this.items.add(item);
2766
                        }
2767
                    } else {
2768
                        this.items.add(item);
2769
                    }
2770
                }
2771
            }
2772

    
2773
            /**
2774
             * Add all history items to <code>contentProvider</code>.
2775
             *
2776
             * @param itemsFilter
2777
             */
2778
            public void addHistoryItems(ItemsFilter itemsFilter) {
2779
                if (this.selectionHistory != null) {
2780
                    Object[] items = this.selectionHistory.getHistoryItems();
2781
                    for (int i = 0; i < items.length; i++) {
2782
                        Object item = items[i];
2783
                        if (itemsFilter == filter) {
2784
                            if (itemsFilter != null) {
2785
                                if (itemsFilter.matchItem(item)) {
2786
                                    if (itemsFilter.isConsistentItem(item)) {
2787
                                        this.items.add(item);
2788
                                    } else {
2789
                                        this.selectionHistory.remove(item);
2790
                                    }
2791
                                }
2792
                            }
2793
                        }
2794
                    }
2795
                }
2796
            }
2797

    
2798
            /**
2799
             * Refresh dialog.
2800
             */
2801
            public void refresh() {
2802
                scheduleRefresh();
2803
            }
2804

    
2805
            /**
2806
             * Removes items from history and refreshes the view.
2807
             *
2808
             * @param item
2809
             *            to remove
2810
             *
2811
             * @return removed item
2812
             */
2813
            public Object removeHistoryElement(Object item) {
2814
                if (this.selectionHistory != null) {
2815
                    this.selectionHistory.remove(item);
2816
                }
2817
                if (filter == null || filter.getPattern().length() == 0) {
2818
                    items.remove(item);
2819
                    duplicates.remove(item);
2820
                    this.lastSortedItems.remove(item);
2821
                }
2822

    
2823
                synchronized (lastSortedItems) {
2824
                    Collections.sort(lastSortedItems, getHistoryComparator());
2825
                }
2826
                return item;
2827
            }
2828

    
2829
            /**
2830
             * Adds item to history and refresh view.
2831
             *
2832
             * @param item
2833
             *            to add
2834
             */
2835
            public void addHistoryElement(Object item) {
2836
                if (this.selectionHistory != null) {
2837
                    this.selectionHistory.accessed(item);
2838
                }
2839
                if (filter == null || !filter.matchItem(item)) {
2840
                    this.items.remove(item);
2841
                    this.duplicates.remove(item);
2842
                    this.lastSortedItems.remove(item);
2843
                }
2844
                synchronized (lastSortedItems) {
2845
                    Collections.sort(lastSortedItems, getHistoryComparator());
2846
                }
2847
                this.refresh();
2848
            }
2849

    
2850
            /**
2851
             * @param item
2852
             * @return <code>true</code> if given item is part of the history
2853
             */
2854
            public boolean isHistoryElement(Object item) {
2855
                if (this.selectionHistory != null) {
2856
                    return this.selectionHistory.contains(item);
2857
                }
2858
                return false;
2859
            }
2860

    
2861
            /**
2862
             * Sets/unsets given item as duplicate.
2863
             *
2864
             * @param item
2865
             *            item to change
2866
             *
2867
             * @param isDuplicate
2868
             *            duplicate flag
2869
             */
2870
            public void setDuplicateElement(Object item, boolean isDuplicate) {
2871
                if (this.items.contains(item)) {
2872
                    if (isDuplicate) {
2873
                        this.duplicates.add(item);
2874
                    } else {
2875
                        this.duplicates.remove(item);
2876
                    }
2877
                }
2878
            }
2879

    
2880
            /**
2881
             * Indicates whether given item is a duplicate.
2882
             *
2883
             * @param item
2884
             *            item to check
2885
             * @return <code>true</code> if item is duplicate
2886
             */
2887
            public boolean isDuplicateElement(Object item) {
2888
                return duplicates.contains(item);
2889
            }
2890

    
2891
            /**
2892
             * Load history from memento.
2893
             *
2894
             * @param memento
2895
             *            memento from which the history will be retrieved
2896
             */
2897
            public void loadHistory(IMemento memento) {
2898
                if (this.selectionHistory != null) {
2899
                    this.selectionHistory.load(memento);
2900
                }
2901
            }
2902

    
2903
            /**
2904
             * Save history to memento.
2905
             *
2906
             * @param memento
2907
             *            memento to which the history will be added
2908
             */
2909
            public void saveHistory(IMemento memento) {
2910
                if (this.selectionHistory != null) {
2911
                    this.selectionHistory.save(memento);
2912
                }
2913
            }
2914

    
2915
            /**
2916
             * Gets sorted items.
2917
             *
2918
             * @return sorted items
2919
             */
2920
            private Object[] getSortedItems() {
2921
                if (lastSortedItems.size() != items.size()) {
2922
                    synchronized (lastSortedItems) {
2923
                        lastSortedItems.clear();
2924
                        lastSortedItems.addAll(items);
2925
                        Collections.sort(lastSortedItems, getHistoryComparator());
2926
                    }
2927
                }
2928
                return lastSortedItems.toArray();
2929
            }
2930

    
2931
            /**
2932
             * Remember result of filtering.
2933
             *
2934
             * @param itemsFilter
2935
             */
2936
            public void rememberResult(ItemsFilter itemsFilter) {
2937
                List itemsList = Collections.synchronizedList(Arrays
2938
                        .asList(getSortedItems()));
2939
                // synchronization
2940
                if (itemsFilter == filter) {
2941
                    lastCompletedFilter = itemsFilter;
2942
                    lastCompletedResult = itemsList;
2943
                }
2944

    
2945
            }
2946

    
2947
            /*
2948
             * (non-Javadoc)
2949
             *
2950
             * @see org.eclipse.jface.viewers.IStructuredContentProvider#getElements(java.lang.Object)
2951
             */
2952
            @Override
2953
            public Object[] getElements(Object inputElement) {
2954
                //return items.toArray();
2955
                return lastFilteredItems.toArray();
2956
            }
2957

    
2958
            public int getNumberOfElements() {
2959

    
2960
               return lastFilteredItems.size();
2961
            }
2962

    
2963
            /*
2964
             * (non-Javadoc)
2965
             *
2966
             * @see org.eclipse.jface.viewers.IContentProvider#dispose()
2967
             */
2968
            @Override
2969
            public void dispose() {
2970
            }
2971

    
2972
            /*
2973
             * (non-Javadoc)
2974
             *
2975
             * @see org.eclipse.jface.viewers.IContentProvider#inputChanged(org.eclipse.jface.viewers.Viewer,
2976
             *      java.lang.Object, java.lang.Object)
2977
             */
2978
            @Override
2979
            public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
2980
            }
2981

    
2982
            /*
2983
             * (non-Javadoc)
2984
             *
2985
             * @see org.eclipse.jface.viewers.ILazyContentProvider#updateElement(int)
2986
             */
2987
            @Override
2988
            public void updateElement(int index) {
2989

    
2990
                CdmFilteredItemsSelectionDialog.this.getList().replace((lastFilteredItems
2991
                        .size() > index) ? lastFilteredItems.get(index) : null,
2992
                        index);
2993

    
2994
            }
2995

    
2996
            /**
2997
             * Main method responsible for getting the filtered items and checking
2998
             * for duplicates. It is based on the
2999
             * {@link FilteredItemsSelectionDialog.ContentProvider#getFilteredItems(Object, IProgressMonitor)}.
3000
             *
3001
             * @param checkDuplicates
3002
             *            <code>true</code> if data concerning elements
3003
             *            duplication should be computed - it takes much more time
3004
             *            than standard filtering
3005
             *
3006
             * @param monitor
3007
             *            progress monitor
3008
             */
3009
            public void reloadCache(boolean checkDuplicates,
3010
                    IProgressMonitor monitor) {
3011

    
3012
                reset = false;
3013

    
3014
                if (monitor != null) {
3015
                    // the work is divided into two actions of the same length
3016
                    int totalWork = checkDuplicates ? 200 : 100;
3017

    
3018
                    monitor
3019
                            .beginTask(
3020
                                    WorkbenchMessages.FilteredItemsSelectionDialog_cacheRefreshJob,
3021
                                    totalWork);
3022
                }
3023

    
3024
                // the TableViewer's root (the input) is treated as parent
3025

    
3026
                lastFilteredItems = Arrays.asList(getFilteredItems(getList().getInput(),
3027
                        monitor != null ? new SubProgressMonitor(monitor, 100)
3028
                                : null));
3029

    
3030
                if (reset || (monitor != null && monitor.isCanceled())) {
3031
                    if (monitor != null) {
3032
                        monitor.done();
3033
                    }
3034
                    return;
3035
                }
3036

    
3037
                if (checkDuplicates) {
3038
                    checkDuplicates(monitor);
3039
                }
3040
                if (monitor != null) {
3041
                    monitor.done();
3042
                }
3043
            }
3044

    
3045
            private void checkDuplicates(IProgressMonitor monitor) {
3046
                synchronized (lastFilteredItems) {
3047
                    IProgressMonitor subMonitor = null;
3048
                    int reportEvery = lastFilteredItems.size() / 20;
3049
                    if (monitor != null) {
3050
                        subMonitor = new SubProgressMonitor(monitor, 100);
3051
                        subMonitor
3052
                                .beginTask(
3053
                                        WorkbenchMessages.FilteredItemsSelectionDialog_cacheRefreshJob_checkDuplicates,
3054
                                        5);
3055
                    }
3056
                    HashMap helperMap = new HashMap();
3057
                    for (int i = 0; i < lastFilteredItems.size(); i++) {
3058
                        if (reset
3059
                                || (subMonitor != null && subMonitor.isCanceled())) {
3060
                            return;
3061
                        }
3062
                        Object item = lastFilteredItems.get(i);
3063

    
3064
                        if (!(item instanceof ItemsListSeparator)) {
3065
                            Object previousItem = helperMap.put(
3066
                                    getElementName(item), item);
3067
                            if (previousItem != null) {
3068
                                setDuplicateElement(previousItem, true);
3069
                                setDuplicateElement(item, true);
3070
                            } else {
3071
                                setDuplicateElement(item, false);
3072
                            }
3073
                        }
3074

    
3075
                        if (subMonitor != null && reportEvery != 0
3076
                                && (i + 1) % reportEvery == 0) {
3077
                            subMonitor.worked(1);
3078
                        }
3079
                    }
3080
                    helperMap.clear();
3081
                }
3082
            }
3083

    
3084
            /**
3085
             * Returns an array of items filtered using the provided
3086
             * <code>ViewerFilter</code>s with a separator added.
3087
             *
3088
             * @param parent
3089
             *            the parent
3090
             * @param monitor
3091
             *            progress monitor, can be <code>null</code>
3092
             * @return an array of filtered items
3093
             */
3094
            protected Object[] getFilteredItems(Object parent,
3095
                    IProgressMonitor monitor) {
3096
                int ticks = 100;
3097
                if (monitor == null) {
3098
                    monitor = new NullProgressMonitor();
3099
                }
3100

    
3101
                monitor
3102
                        .beginTask(
3103
                                WorkbenchMessages.FilteredItemsSelectionDialog_cacheRefreshJob_getFilteredElements,
3104
                                ticks);
3105
                if (filters != null) {
3106
                    ticks /= (filters.size() + 2);
3107
                } else {
3108
                    ticks /= 2;
3109
                }
3110

    
3111
                // get already sorted array
3112
                Object[] filteredElements = getSortedItems();
3113

    
3114
                monitor.worked(ticks);
3115

    
3116
                // filter the elements using provided ViewerFilters
3117
                if (filters != null && filteredElements != null) {
3118
                    for (Iterator iter = filters.iterator(); iter.hasNext();) {
3119
                        ViewerFilter f = (ViewerFilter) iter.next();
3120
                        filteredElements = f.filter(getList(), parent, filteredElements);
3121
                        monitor.worked(ticks);
3122
                    }
3123
                }
3124

    
3125
                if (filteredElements == null || monitor.isCanceled()) {
3126
                    monitor.done();
3127
                    return new Object[0];
3128
                }
3129

    
3130
                ArrayList preparedElements = new ArrayList();
3131
                boolean hasHistory = false;
3132

    
3133
                if (filteredElements.length > 0) {
3134
                    if (isHistoryElement(filteredElements[0])) {
3135
                        hasHistory = true;
3136
                    }
3137
                }
3138

    
3139
                int reportEvery = filteredElements.length / ticks;
3140

    
3141
                // add separator
3142
                for (int i = 0; i < filteredElements.length; i++) {
3143
                    Object item = filteredElements[i];
3144

    
3145
                    if (hasHistory && !isHistoryElement(item)) {
3146
                        preparedElements.add(itemsListSeparator);
3147
                        hasHistory = false;
3148
                    }
3149

    
3150
                    preparedElements.add(item);
3151

    
3152
                    if (reportEvery != 0 && ((i + 1) % reportEvery == 0)) {
3153
                        monitor.worked(1);
3154
                    }
3155
                }
3156

    
3157
                monitor.done();
3158

    
3159
                return preparedElements.toArray();
3160
            }
3161

    
3162
            /**
3163
             * Adds a filter to this content provider. For an example usage of such
3164
             * filters look at the project <code>org.eclipse.ui.ide</code>, class
3165
             * <code>org.eclipse.ui.dialogs.FilteredResourcesSelectionDialog.CustomWorkingSetFilter</code>.
3166
             *
3167
             *
3168
             * @param filter
3169
             *            the filter to be added
3170
             */
3171
            public void addFilter(ViewerFilter filter) {
3172
                if (filters == null) {
3173
                    filters = new ArrayList();
3174
                }
3175
                filters.add(filter);
3176
                // currently filters are only added when dialog is restored
3177
                // if it is changed, refreshing the whole TableViewer should be
3178
                // added
3179
            }
3180

    
3181
        }
3182

    
3183
        /**
3184
         * A content provider that does nothing.
3185
         */
3186
        private class NullContentProvider implements IContentProvider {
3187

    
3188
            /*
3189
             * (non-Javadoc)
3190
             *
3191
             * @see org.eclipse.jface.viewers.IContentProvider#dispose()
3192
             */
3193
            @Override
3194
            public void dispose() {
3195
            }
3196

    
3197
            /*
3198
             * (non-Javadoc)
3199
             *
3200
             * @see org.eclipse.jface.viewers.IContentProvider#inputChanged(org.eclipse.jface.viewers.Viewer,
3201
             *      java.lang.Object, java.lang.Object)
3202
             */
3203
            @Override
3204
            public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
3205
            }
3206

    
3207
        }
3208

    
3209
        /**
3210
         * DetailsContentViewer objects are wrappers for labels.
3211
         * DetailsContentViewer provides means to change label's image and text when
3212
         * the attached LabelProvider is updated.
3213
         */
3214
        private class DetailsContentViewer extends ContentViewer {
3215

    
3216
            private final CLabel label;
3217

    
3218
            /**
3219
             * Unfortunately, it was impossible to delegate displaying border to
3220
             * label. The <code>ViewForm</code> is used because
3221
             * <code>CLabel</code> displays shadow when border is present.
3222
             */
3223
            private final ViewForm viewForm;
3224

    
3225
            /**
3226
             * Constructs a new instance of this class given its parent and a style
3227
             * value describing its behavior and appearance.
3228
             *
3229
             * @param parent
3230
             *            the parent component
3231
             * @param style
3232
             *            SWT style bits
3233
             */
3234
            public DetailsContentViewer(Composite parent, int style) {
3235
                viewForm = new ViewForm(parent, style);
3236
                GridData gd = new GridData(GridData.FILL_HORIZONTAL);
3237
                gd.horizontalSpan = 2;
3238
                viewForm.setLayoutData(gd);
3239
                label = new CLabel(viewForm, SWT.FLAT);
3240
                label.setFont(parent.getFont());
3241
                viewForm.setContent(label);
3242
                hookControl(label);
3243
            }
3244

    
3245
            /**
3246
             * Shows/hides the content viewer.
3247
             *
3248
             * @param visible
3249
             *            if the content viewer should be visible.
3250
             */
3251
            public void setVisible(boolean visible) {
3252
                GridData gd = (GridData) viewForm.getLayoutData();
3253
                gd.exclude = !visible;
3254
                viewForm.getParent().layout();
3255
            }
3256

    
3257
            /*
3258
             * (non-Javadoc)
3259
             *
3260
             * @see org.eclipse.jface.viewers.Viewer#inputChanged(java.lang.Object,
3261
             *      java.lang.Object)
3262
             */
3263
            @Override
3264
            protected void inputChanged(Object input, Object oldInput) {
3265
                if (oldInput == null) {
3266
                    if (input == null) {
3267
                        return;
3268
                    }
3269
                    refresh();
3270
                    return;
3271
                }
3272

    
3273
                refresh();
3274

    
3275
            }
3276

    
3277
            /*
3278
             * (non-Javadoc)
3279
             *
3280
             * @see org.eclipse.jface.viewers.ContentViewer#handleLabelProviderChanged(org.eclipse.jface.viewers.LabelProviderChangedEvent)
3281
             */
3282
            @Override
3283
            protected void handleLabelProviderChanged(
3284
                    LabelProviderChangedEvent event) {
3285
                if (event != null) {
3286
                    refresh(event.getElements());
3287
                }
3288
            }
3289

    
3290
            /*
3291
             * (non-Javadoc)
3292
             *
3293
             * @see org.eclipse.jface.viewers.Viewer#getControl()
3294
             */
3295
            @Override
3296
            public Control getControl() {
3297
                return label;
3298
            }
3299

    
3300
            /*
3301
             * (non-Javadoc)
3302
             *
3303
             * @see org.eclipse.jface.viewers.Viewer#getSelection()
3304
             */
3305
            @Override
3306
            public ISelection getSelection() {
3307
                // not supported
3308
                return null;
3309
            }
3310

    
3311
            /*
3312
             * (non-Javadoc)
3313
             *
3314
             * @see org.eclipse.jface.viewers.Viewer#refresh()
3315
             */
3316
            @Override
3317
            public void refresh() {
3318
                Object input = this.getInput();
3319
                if (input != null) {
3320
                    ILabelProvider labelProvider = (ILabelProvider) getLabelProvider();
3321
                    doRefresh(labelProvider.getText(input), labelProvider
3322
                            .getImage(input));
3323
                } else {
3324
                    doRefresh(null, null);
3325
                }
3326
            }
3327

    
3328
            /**
3329
             * Sets the given text and image to the label.
3330
             *
3331
             * @param text
3332
             *            the new text or null
3333
             * @param image
3334
             *            the new image
3335
             */
3336
            private void doRefresh(String text, Image image) {
3337
                if ( text != null ) {
3338
                    text = LegacyActionTools.escapeMnemonics(text);
3339
                }
3340
                label.setText(text);
3341
                label.setImage(image);
3342
            }
3343

    
3344
            /*
3345
             * (non-Javadoc)
3346
             *
3347
             * @see org.eclipse.jface.viewers.Viewer#setSelection(org.eclipse.jface.viewers.ISelection,
3348
             *      boolean)
3349
             */
3350
            @Override
3351
            public void setSelection(ISelection selection, boolean reveal) {
3352
                // not supported
3353
            }
3354

    
3355
            /**
3356
             * Refreshes the label if currently chosen element is on the list.
3357
             *
3358
             * @param objs
3359
             *            list of changed object
3360
             */
3361
            private void refresh(Object[] objs) {
3362
                if (objs == null || getInput() == null) {
3363
                    return;
3364
                }
3365
                Object input = getInput();
3366
                for (int i = 0; i < objs.length; i++) {
3367
                    if (objs[i].equals(input)) {
3368
                        refresh();
3369
                        break;
3370
                    }
3371
                }
3372
            }
3373
        }
3374

    
3375
        /**
3376
         * Compares items according to the history.
3377
         */
3378
        private class HistoryComparator implements Comparator {
3379

    
3380
            /*
3381
             * (non-Javadoc)
3382
             *
3383
             * @see java.util.Comparator#compare(java.lang.Object, java.lang.Object)
3384
             */
3385
            @Override
3386
            public int compare(Object o1, Object o2) {
3387
                boolean h1 = isHistoryElement(o1);
3388
                boolean h2 = isHistoryElement(o2);
3389
                if (h1 == h2) {
3390
                    return getItemsComparator().compare(o1, o2);
3391
                }
3392

    
3393
                if (h1) {
3394
                    return -2;
3395
                }
3396
                if (h2) {
3397
                    return +2;
3398
                }
3399

    
3400
                return 0;
3401
            }
3402

    
3403
        }
3404

    
3405

    
3406
        /**
3407
         * Get the control where the search pattern is entered. Any filtering should
3408
         * be done using an {@link ItemsFilter}. This control should only be
3409
         * accessed for listeners that wish to handle events that do not affect
3410
         * filtering such as custom traversal.
3411
         *
3412
         * @return Control or <code>null</code> if the pattern control has not
3413
         *         been created.
3414
         */
3415
        public Control getPatternControl() {
3416
            return pattern;
3417
        }
3418

    
3419
        /**
3420
         * CDM IMPLEMENTATION
3421
         * we have to call the initModel() method in the applyFilter method, so we had to add this abstract method (moved from AbstractFilteredCdmResourceSelectionDialog)
3422
         * <p>initModel</p>
3423
         */
3424
        abstract protected void initModel();
3425

    
3426
		public Object[] getCurrentSelection() {
3427
			return currentSelection;
3428
		}
3429

    
3430
		public void setCurrentSelection(Object[] currentSelection) {
3431
			this.currentSelection = currentSelection;
3432
		}
3433

    
3434
		public TableViewer getList() {
3435
			return list;
3436
		}
3437

    
3438
		public void setList(TableViewer list) {
3439
			this.list = list;
3440
		}
3441

    
3442
    }
3443

    
3444

    
(5-5/34)