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