10
|
1 /*******************************************************************************
|
|
2 * Copyright (c) 2000, 2007 IBM Corporation and others.
|
|
3 * All rights reserved. This program and the accompanying materials
|
|
4 * are made available under the terms of the Eclipse Public License v1.0
|
|
5 * which accompanies this distribution, and is available at
|
|
6 * http://www.eclipse.org/legal/epl-v10.html
|
|
7 *
|
|
8 * Contributors:
|
|
9 * IBM Corporation - initial API and implementation
|
|
10 * Tom Schindl - bug 151205
|
|
11 * Port to the D programming language:
|
|
12 * Frank Benoit <benoit@tionex.de>
|
|
13 *******************************************************************************/
|
|
14 module dwtx.jface.viewers.StructuredViewer;
|
|
15
|
|
16 import dwtx.jface.viewers.IBaseLabelProvider;
|
|
17 import dwtx.jface.viewers.IPostSelectionProvider;
|
|
18 import dwtx.jface.viewers.ContentViewer;
|
|
19 import dwtx.jface.viewers.CustomHashtable;
|
|
20 import dwtx.jface.viewers.IElementComparer;
|
|
21 import dwtx.jface.viewers.ViewerComparator;
|
|
22 import dwtx.jface.viewers.ViewerFilter;
|
|
23 import dwtx.jface.viewers.IDoubleClickListener;
|
|
24 import dwtx.jface.viewers.IOpenListener;
|
|
25 import dwtx.jface.viewers.ISelectionChangedListener;
|
|
26 import dwtx.jface.viewers.OpenEvent;
|
|
27 import dwtx.jface.viewers.SelectionChangedEvent;
|
|
28 import dwtx.jface.viewers.ISelection;
|
|
29 import dwtx.jface.viewers.DoubleClickEvent;
|
|
30 import dwtx.jface.viewers.ViewerSorter;
|
|
31 import dwtx.jface.viewers.IContentProvider;
|
|
32 import dwtx.jface.viewers.IColorProvider;
|
|
33 import dwtx.jface.viewers.IFontProvider;
|
|
34 import dwtx.jface.viewers.LabelProviderChangedEvent;
|
|
35 import dwtx.jface.viewers.ViewerLabel;
|
|
36 import dwtx.jface.viewers.TreePath;
|
|
37 import dwtx.jface.viewers.ITreePathLabelProvider;
|
|
38 import dwtx.jface.viewers.IViewerLabelProvider;
|
|
39 import dwtx.jface.viewers.ILabelProvider;
|
|
40 import dwtx.jface.viewers.IStructuredContentProvider;
|
|
41 import dwtx.jface.viewers.StructuredSelection;
|
|
42 import dwtx.jface.viewers.IStructuredSelection;
|
|
43
|
|
44 import tango.util.collection.ArraySeq;
|
|
45 import tango.util.collection.model.Seq;
|
|
46 import tango.util.collection.model.SeqView;
|
|
47
|
|
48 import dwt.custom.TableTreeItem;
|
|
49 import dwt.dnd.DragSource;
|
|
50 import dwt.dnd.DragSourceListener;
|
|
51 import dwt.dnd.DropTarget;
|
|
52 import dwt.dnd.DropTargetListener;
|
|
53 import dwt.dnd.Transfer;
|
|
54 import dwt.events.DisposeEvent;
|
|
55 import dwt.events.SelectionAdapter;
|
|
56 import dwt.events.SelectionEvent;
|
|
57 import dwt.events.SelectionListener;
|
|
58 import dwt.graphics.Color;
|
|
59 import dwt.graphics.Font;
|
|
60 import dwt.widgets.Control;
|
|
61 import dwt.widgets.Item;
|
|
62 import dwt.widgets.TableItem;
|
|
63 import dwt.widgets.TreeItem;
|
|
64 import dwt.widgets.Widget;
|
|
65 import dwtx.core.runtime.Assert;
|
|
66 import dwtx.core.runtime.ListenerList;
|
|
67 import dwtx.jface.util.IOpenEventListener;
|
|
68 import dwtx.jface.util.OpenStrategy;
|
|
69 import dwtx.jface.util.SafeRunnable;
|
|
70
|
|
71 import dwt.dwthelper.utils;
|
|
72 import dwt.dwthelper.Runnable;
|
|
73
|
|
74 /**
|
|
75 * Abstract base implementation for structure-oriented viewers (trees, lists,
|
|
76 * tables). Supports custom sorting, filtering, and rendering.
|
|
77 * <p>
|
|
78 * Any number of viewer filters can be added to this viewer (using
|
|
79 * <code>addFilter</code>). When the viewer receives an update, it asks each
|
|
80 * of its filters if it is out of date, and refilters elements as required.
|
|
81 * </p>
|
|
82 *
|
|
83 * @see ViewerFilter
|
|
84 * @see ViewerComparator
|
|
85 */
|
|
86 public abstract class StructuredViewer : ContentViewer, IPostSelectionProvider {
|
|
87 alias ContentViewer.setSelection setSelection;
|
|
88 /**
|
|
89 * A map from the viewer's model elements to DWT widgets. (key type:
|
|
90 * <code>Object</code>, value type: <code>Widget</code>, or <code>Widget[]</code>).
|
|
91 * <code>null</code> means that the element map is disabled.
|
|
92 */
|
|
93 private CustomHashtable elementMap;
|
|
94
|
|
95 /**
|
|
96 * The comparer to use for comparing elements, or <code>null</code> to use
|
|
97 * the default <code>equals</code> and <code>hashCode</code> methods on
|
|
98 * the element itself.
|
|
99 */
|
|
100 private IElementComparer comparer;
|
|
101
|
|
102 /**
|
|
103 * This viewer's comparator used for sorting. <code>null</code> means there is no comparator.
|
|
104 */
|
|
105 private ViewerComparator sorter;
|
|
106
|
|
107 /**
|
|
108 * This viewer's filters (element type: <code>ViewerFilter</code>).
|
|
109 * <code>null</code> means there are no filters.
|
|
110 */
|
|
111 private Seq!(ViewerFilter) filters;
|
|
112
|
|
113 /**
|
|
114 * Indicates whether a selection change is in progress on this viewer.
|
|
115 *
|
|
116 * @see #setSelection(ISelection, bool)
|
|
117 */
|
|
118 private bool inChange;
|
|
119
|
|
120 /**
|
|
121 * Used while a selection change is in progress on this viewer to indicates
|
|
122 * whether the selection should be restored.
|
|
123 *
|
|
124 * @see #setSelection(ISelection, bool)
|
|
125 */
|
|
126 private bool restoreSelection;
|
|
127
|
|
128 /**
|
|
129 * List of double-click state listeners (element type:
|
|
130 * <code>IDoubleClickListener</code>).
|
|
131 *
|
|
132 * @see #fireDoubleClick
|
|
133 */
|
|
134 private ListenerList doubleClickListeners;
|
|
135
|
|
136 /**
|
|
137 * List of open listeners (element type:
|
|
138 * <code>ISelectionActivateListener</code>).
|
|
139 *
|
|
140 * @see #fireOpen
|
|
141 */
|
|
142 private ListenerList openListeners;
|
|
143
|
|
144 /**
|
|
145 * List of post selection listeners (element type:
|
|
146 * <code>ISelectionActivateListener</code>).
|
|
147 *
|
|
148 * @see #firePostSelectionChanged
|
|
149 */
|
|
150 private ListenerList postSelectionChangedListeners;
|
|
151
|
|
152 /**
|
|
153 * The colorAndFontCollector is an object used by viewers that
|
|
154 * support the IColorProvider, the IFontProvider and/or the
|
|
155 * IViewerLabelProvider for color and font updates.
|
|
156 * Initialize it to have no color or font providing
|
|
157 * initially.
|
|
158 * @since 3.1
|
|
159 */
|
|
160 private ColorAndFontCollector colorAndFontCollector;
|
|
161
|
|
162 /**
|
|
163 * Empty array of widgets.
|
|
164 */
|
|
165 private static Widget[] NO_WIDGETS = null;
|
|
166
|
|
167 /**
|
|
168 * The ColorAndFontCollector is a helper class for viewers
|
|
169 * that have color and font support ad optionally decorators.
|
|
170 * @see IColorDecorator
|
|
171 * @see IFontDecorator
|
|
172 * @see IColorProvider
|
|
173 * @see IFontProvider
|
|
174 * @see IDecoration
|
|
175 */
|
|
176 protected class ColorAndFontCollectorWithProviders : ColorAndFontCollector{
|
|
177
|
|
178 IColorProvider colorProvider;
|
|
179
|
|
180 IFontProvider fontProvider;
|
|
181
|
|
182 /**
|
|
183 * Create a new instance of the receiver using the supplied
|
|
184 * label provider. If it is an IColorProvider or IFontProvider
|
|
185 * set these values up.
|
|
186 * @param provider IBaseLabelProvider
|
|
187 * @see IColorProvider
|
|
188 * @see IFontProvider
|
|
189 */
|
|
190 public this(IBaseLabelProvider provider) {
|
|
191 super();
|
|
192 if ( auto cp = cast(IColorProvider) provider ) {
|
|
193 colorProvider = cp;
|
|
194 }
|
|
195 if ( auto fp = cast(IFontProvider) provider ) {
|
|
196 fontProvider = fp;
|
|
197 }
|
|
198 }
|
|
199
|
|
200
|
|
201 /* (non-Javadoc)
|
|
202 * @see dwtx.jface.viewers.StructuredViewer.ColorAndFontManager#setFontsAndColors(java.lang.Object)
|
|
203 */
|
|
204 public void setFontsAndColors(Object element){
|
|
205
|
|
206 if(fontProvider !is null){
|
|
207 if(font is null) {
|
|
208 font = fontProvider.getFont(element);
|
|
209 }
|
|
210 }
|
|
211
|
|
212 if(colorProvider is null) {
|
|
213 return;
|
|
214 }
|
|
215 //Set the colors if they are not set yet
|
|
216 if(background is null) {
|
|
217 background = colorProvider.getBackground(element);
|
|
218 }
|
|
219
|
|
220 if(foreground is null) {
|
|
221 foreground = colorProvider.getForeground(element);
|
|
222 }
|
|
223 }
|
|
224
|
|
225 /**
|
|
226 * Apply the fonts and colors to the control if
|
|
227 * required.
|
|
228 * @param control
|
|
229 */
|
|
230 public void applyFontsAndColors(TableItem control) {
|
|
231
|
|
232 if(colorProvider is null){
|
|
233 if(usedDecorators){
|
|
234 //If there is no provider only apply set values
|
|
235 if(background !is null) {
|
|
236 control.setBackground(background);
|
|
237 }
|
|
238
|
|
239 if(foreground !is null) {
|
|
240 control.setForeground(foreground);
|
|
241 }
|
|
242 }
|
|
243 }
|
|
244 else{
|
|
245 //Always set the value if there is a provider
|
|
246 control.setBackground(background);
|
|
247 control.setForeground(foreground);
|
|
248 }
|
|
249
|
|
250 if(fontProvider is null){
|
|
251 if(usedDecorators && font !is null) {
|
|
252 control.setFont(font);
|
|
253 }
|
|
254 } else {
|
|
255 control.setFont(font);
|
|
256 }
|
|
257
|
|
258 clear();
|
|
259 }
|
|
260
|
|
261
|
|
262
|
|
263 /* (non-Javadoc)
|
|
264 * @see dwtx.jface.viewers.StructuredViewer.ColorAndFontManager#applyFontsAndColors(dwt.widgets.TreeItem)
|
|
265 */
|
|
266 public void applyFontsAndColors(TreeItem control) {
|
|
267
|
|
268 if(colorProvider is null){
|
|
269 if(usedDecorators){
|
|
270 //If there is no provider only apply set values
|
|
271 if(background !is null) {
|
|
272 control.setBackground(background);
|
|
273 }
|
|
274
|
|
275 if(foreground !is null) {
|
|
276 control.setForeground(foreground);
|
|
277 }
|
|
278 }
|
|
279 }
|
|
280 else{
|
|
281 //Always set the value if there is a provider
|
|
282 control.setBackground(background);
|
|
283 control.setForeground(foreground);
|
|
284 }
|
|
285
|
|
286 if(fontProvider is null){
|
|
287 if(usedDecorators && font !is null) {
|
|
288 control.setFont(font);
|
|
289 }
|
|
290 } else {
|
|
291 control.setFont(font);
|
|
292 }
|
|
293
|
|
294 clear();
|
|
295 }
|
|
296
|
|
297
|
|
298 /* (non-Javadoc)
|
|
299 * @see dwtx.jface.viewers.StructuredViewer.ColorAndFontManager#applyFontsAndColors(dwt.custom.TableTreeItem)
|
|
300 */
|
|
301 public void applyFontsAndColors(TableTreeItem control) {
|
|
302
|
|
303 if(colorProvider is null){
|
|
304 if(usedDecorators){
|
|
305 //If there is no provider only apply set values
|
|
306 if(background !is null) {
|
|
307 control.setBackground(background);
|
|
308 }
|
|
309
|
|
310 if(foreground !is null) {
|
|
311 control.setForeground(foreground);
|
|
312 }
|
|
313 }
|
|
314 }
|
|
315 else{
|
|
316 //Always set the value if there is a provider
|
|
317 control.setBackground(background);
|
|
318 control.setForeground(foreground);
|
|
319 }
|
|
320
|
|
321 if(fontProvider is null){
|
|
322 if(usedDecorators && font !is null) {
|
|
323 control.setFont(font);
|
|
324 }
|
|
325 } else {
|
|
326 control.setFont(font);
|
|
327 }
|
|
328
|
|
329 clear();
|
|
330 }
|
|
331
|
|
332
|
|
333 }
|
|
334
|
|
335 /**
|
|
336 * The ColorAndFontManager collects fonts and colors without a
|
|
337 * a color or font provider.
|
|
338 *
|
|
339 */
|
|
340 protected class ColorAndFontCollector {
|
|
341
|
|
342 Color foreground = null;
|
|
343
|
|
344 Color background = null;
|
|
345
|
|
346 Font font = null;
|
|
347
|
|
348 bool usedDecorators = false;
|
|
349
|
|
350 /**
|
|
351 * Create a new instance of the receiver with
|
|
352 * no color and font provider.
|
|
353 */
|
|
354 public this(){
|
|
355 }
|
|
356
|
|
357
|
|
358 /**
|
|
359 * Clear all of the results.
|
|
360 */
|
|
361 public void clear() {
|
|
362 foreground = null;
|
|
363 background = null;
|
|
364 font = null;
|
|
365 usedDecorators = false;
|
|
366 }
|
|
367
|
|
368
|
|
369 /**
|
|
370 * Set the initial fonts and colors for the element from the
|
|
371 * content providers.
|
|
372 * @param element Object
|
|
373 */
|
|
374 public void setFontsAndColors(Object element){
|
|
375 //Do nothing if there are no providers
|
|
376 }
|
|
377
|
|
378 /**
|
|
379 * Set that decorators were applied.
|
|
380 */
|
|
381 public void setUsedDecorators() {
|
|
382 this.usedDecorators = true;
|
|
383 }
|
|
384
|
|
385 /**
|
|
386 * Apply the fonts and colors to the control if
|
|
387 * required.
|
|
388 * @param control
|
|
389 */
|
|
390 public void applyFontsAndColors(TableItem control) {
|
|
391
|
|
392 if(usedDecorators){
|
|
393 //If there is no provider only apply set values
|
|
394 if(background !is null) {
|
|
395 control.setBackground(background);
|
|
396 }
|
|
397
|
|
398 if(foreground !is null) {
|
|
399 control.setForeground(foreground);
|
|
400 }
|
|
401
|
|
402 if(font !is null) {
|
|
403 control.setFont(font);
|
|
404 }
|
|
405 }
|
|
406 clear();
|
|
407 }
|
|
408
|
|
409 /**
|
|
410 * Apply the fonts and colors to the control if
|
|
411 * required.
|
|
412 * @param control
|
|
413 */
|
|
414 public void applyFontsAndColors(TreeItem control) {
|
|
415 if(usedDecorators){
|
|
416 //If there is no provider only apply set values
|
|
417 if(background !is null) {
|
|
418 control.setBackground(background);
|
|
419 }
|
|
420
|
|
421 if(foreground !is null) {
|
|
422 control.setForeground(foreground);
|
|
423 }
|
|
424
|
|
425 if(font !is null) {
|
|
426 control.setFont(font);
|
|
427 }
|
|
428 }
|
|
429 clear();
|
|
430 }
|
|
431
|
|
432 /**
|
|
433 * Apply the fonts and colors to the control if
|
|
434 * required.
|
|
435 * @param control
|
|
436 */
|
|
437 public void applyFontsAndColors(TableTreeItem control) {
|
|
438 if(usedDecorators){
|
|
439 //If there is no provider only apply set values
|
|
440 if(background !is null) {
|
|
441 control.setBackground(background);
|
|
442 }
|
|
443
|
|
444 if(foreground !is null) {
|
|
445 control.setForeground(foreground);
|
|
446 }
|
|
447
|
|
448 if(font !is null) {
|
|
449 control.setFont(font);
|
|
450 }
|
|
451 }
|
|
452 clear();
|
|
453 }
|
|
454
|
|
455 /**
|
|
456 * Set the background color.
|
|
457 * @param background
|
|
458 */
|
|
459 public void setBackground(Color background) {
|
|
460 this.background = background;
|
|
461 }
|
|
462 /**
|
|
463 * Set the font.
|
|
464 * @param font
|
|
465 */
|
|
466 public void setFont(Font font) {
|
|
467 this.font = font;
|
|
468 }
|
|
469 /**
|
|
470 * Set the foreground color.
|
|
471 * @param foreground
|
|
472 */
|
|
473 public void setForeground(Color foreground) {
|
|
474 this.foreground = foreground;
|
|
475 }
|
|
476
|
|
477
|
|
478 }
|
|
479
|
|
480 /**
|
|
481 * The safe runnable used to update an item.
|
|
482 */
|
|
483 class UpdateItemSafeRunnable : SafeRunnable {
|
|
484 private Widget widget;
|
|
485
|
|
486 private Object element;
|
|
487
|
|
488 private bool fullMap;
|
|
489
|
|
490 this(Widget widget, Object element, bool fullMap) {
|
|
491 this.widget = widget;
|
|
492 this.element = element;
|
|
493 this.fullMap = fullMap;
|
|
494 }
|
|
495
|
|
496 public void run() {
|
|
497 doUpdateItem(widget, element, fullMap);
|
|
498 }
|
|
499 }
|
|
500
|
|
501 /**
|
|
502 * Creates a structured element viewer. The viewer has no input, no content
|
|
503 * provider, a default label provider, no sorter, and no filters.
|
|
504 */
|
|
505 protected this() {
|
|
506 doubleClickListeners = new ListenerList();
|
|
507 openListeners = new ListenerList();
|
|
508 postSelectionChangedListeners = new ListenerList();
|
|
509 colorAndFontCollector = new ColorAndFontCollector();
|
|
510 // do nothing
|
|
511 }
|
|
512
|
|
513 /**
|
|
514 * Adds a listener for double-clicks in this viewer. Has no effect if an
|
|
515 * identical listener is already registered.
|
|
516 *
|
|
517 * @param listener
|
|
518 * a double-click listener
|
|
519 */
|
|
520 public void addDoubleClickListener(IDoubleClickListener listener) {
|
|
521 doubleClickListeners.add(cast(Object)listener);
|
|
522 }
|
|
523
|
|
524 /**
|
|
525 * Adds a listener for selection-open in this viewer. Has no effect if an
|
|
526 * identical listener is already registered.
|
|
527 *
|
|
528 * @param listener
|
|
529 * a double-click listener
|
|
530 */
|
|
531 public void addOpenListener(IOpenListener listener) {
|
|
532 openListeners.add(cast(Object)listener);
|
|
533 }
|
|
534
|
|
535 /*
|
|
536 * (non-Javadoc) Method declared on IPostSelectionProvider.
|
|
537 */
|
|
538 public void addPostSelectionChangedListener(ISelectionChangedListener listener) {
|
|
539 postSelectionChangedListeners.add(cast(Object)listener);
|
|
540 }
|
|
541
|
|
542 /**
|
|
543 * Adds support for dragging items out of this viewer via a user
|
|
544 * drag-and-drop operation.
|
|
545 *
|
|
546 * @param operations
|
|
547 * a bitwise OR of the supported drag and drop operation types (
|
|
548 * <code>DROP_COPY</code>,<code>DROP_LINK</code>, and
|
|
549 * <code>DROP_MOVE</code>)
|
|
550 * @param transferTypes
|
|
551 * the transfer types that are supported by the drag operation
|
|
552 * @param listener
|
|
553 * the callback that will be invoked to set the drag data and to
|
|
554 * cleanup after the drag and drop operation finishes
|
|
555 * @see dwt.dnd.DND
|
|
556 */
|
|
557 public void addDragSupport(int operations, Transfer[] transferTypes, DragSourceListener listener) {
|
|
558
|
|
559 Control myControl = getControl();
|
|
560 final DragSource dragSource = new DragSource(myControl, operations);
|
|
561 dragSource.setTransfer(transferTypes);
|
|
562 dragSource.addDragListener(listener);
|
|
563 }
|
|
564
|
|
565 /**
|
|
566 * Adds support for dropping items into this viewer via a user drag-and-drop
|
|
567 * operation.
|
|
568 *
|
|
569 * @param operations
|
|
570 * a bitwise OR of the supported drag and drop operation types (
|
|
571 * <code>DROP_COPY</code>,<code>DROP_LINK</code>, and
|
|
572 * <code>DROP_MOVE</code>)
|
|
573 * @param transferTypes
|
|
574 * the transfer types that are supported by the drop operation
|
|
575 * @param listener
|
|
576 * the callback that will be invoked after the drag and drop
|
|
577 * operation finishes
|
|
578 * @see dwt.dnd.DND
|
|
579 */
|
|
580 public void addDropSupport(int operations, Transfer[] transferTypes,
|
|
581 DropTargetListener listener) {
|
|
582 Control control = getControl();
|
|
583 DropTarget dropTarget = new DropTarget(control, operations);
|
|
584 dropTarget.setTransfer(transferTypes);
|
|
585 dropTarget.addDropListener(listener);
|
|
586 }
|
|
587
|
|
588 /**
|
|
589 * Adds the given filter to this viewer, and triggers refiltering and
|
|
590 * resorting of the elements. If you want to add more than one filter
|
|
591 * consider using {@link StructuredViewer#setFilters(ViewerFilter[])}.
|
|
592 *
|
|
593 * @param filter
|
|
594 * a viewer filter
|
|
595 * @see StructuredViewer#setFilters(ViewerFilter[])
|
|
596 */
|
|
597 public void addFilter(ViewerFilter filter) {
|
|
598 if (filters is null) {
|
|
599 filters = new ArraySeq!(ViewerFilter);
|
|
600 }
|
|
601 filters.append(filter);
|
|
602 refresh();
|
|
603 }
|
|
604
|
|
605 /**
|
|
606 * Asserts that the given array of elements is itself non- <code>null</code>
|
|
607 * and contains no <code>null</code> elements.
|
|
608 *
|
|
609 * @param elements
|
|
610 * the array to check
|
|
611 */
|
|
612 protected void assertElementsNotNull(Object[] elements) {
|
|
613 // Assert.isNotNull(elements);
|
|
614 for (int i = 0, n = elements.length; i < n; ++i) {
|
|
615 Assert.isNotNull(elements[i]);
|
|
616 }
|
|
617 }
|
|
618
|
|
619 /**
|
|
620 * Associates the given element with the given widget. Sets the given item's
|
|
621 * data to be the element, and maps the element to the item in the element
|
|
622 * map (if enabled).
|
|
623 *
|
|
624 * @param element
|
|
625 * the element
|
|
626 * @param item
|
|
627 * the widget
|
|
628 */
|
|
629 protected void associate(Object element, Item item) {
|
|
630 Object data = item.getData();
|
|
631 if (data !is element) {
|
|
632 if (data !is null) {
|
|
633 disassociate(item);
|
|
634 }
|
|
635 item.setData(element);
|
|
636 }
|
|
637 // Always map the element, even if data is element,
|
|
638 // since unmapAllElements() can leave the map inconsistent
|
|
639 // See bug 2741 for details.
|
|
640 mapElement(element, item);
|
|
641 }
|
|
642
|
|
643 /**
|
|
644 * Disassociates the given DWT item from its corresponding element. Sets the
|
|
645 * item's data to <code>null</code> and removes the element from the
|
|
646 * element map (if enabled).
|
|
647 *
|
|
648 * @param item
|
|
649 * the widget
|
|
650 */
|
|
651 protected void disassociate(Item item) {
|
|
652 Object element = item.getData();
|
|
653 Assert.isNotNull(element);
|
|
654 //Clear the map before we clear the data
|
|
655 unmapElement(element, item);
|
|
656 item.setData(null);
|
|
657 }
|
|
658
|
|
659 /**
|
|
660 * Returns the widget in this viewer's control which represents the given
|
|
661 * element if it is the viewer's input.
|
|
662 * <p>
|
|
663 * This method is internal to the framework; subclassers should not call
|
|
664 * this method.
|
|
665 * </p>
|
|
666 *
|
|
667 * @param element
|
|
668 * @return the corresponding widget, or <code>null</code> if none
|
|
669 */
|
|
670 protected abstract Widget doFindInputItem(Object element);
|
|
671
|
|
672 /**
|
|
673 * Returns the widget in this viewer's control which represent the given
|
|
674 * element. This method searches all the children of the input element.
|
|
675 * <p>
|
|
676 * This method is internal to the framework; subclassers should not call
|
|
677 * this method.
|
|
678 * </p>
|
|
679 *
|
|
680 * @param element
|
|
681 * @return the corresponding widget, or <code>null</code> if none
|
|
682 */
|
|
683 protected abstract Widget doFindItem(Object element);
|
|
684
|
|
685 /**
|
|
686 * Copies the attributes of the given element into the given DWT item. The
|
|
687 * element map is updated according to the value of <code>fullMap</code>.
|
|
688 * If <code>fullMap</code> is <code>true</code> then the current mapping
|
|
689 * from element to widgets is removed and the new mapping is added. If
|
|
690 * full map is <code>false</code> then only the new map gets installed.
|
|
691 * Installing only the new map is necessary in cases where only the order of
|
|
692 * elements changes but not the set of elements.
|
|
693 * <p>
|
|
694 * This method is internal to the framework; subclassers should not call
|
|
695 * this method.
|
|
696 * </p>
|
|
697 *
|
|
698 * @param item
|
|
699 * @param element element
|
|
700 * @param fullMap
|
|
701 * <code>true</code> if mappings are added and removed, and
|
|
702 * <code>false</code> if only the new map gets installed
|
|
703 */
|
|
704 protected abstract void doUpdateItem(Widget item, Object element, bool fullMap);
|
|
705
|
|
706 /**
|
|
707 * Compares two elements for equality. Uses the element comparer if one has
|
|
708 * been set, otherwise uses the default <code>equals</code> method on the
|
|
709 * elements themselves.
|
|
710 *
|
|
711 * @param elementA
|
|
712 * the first element
|
|
713 * @param elementB
|
|
714 * the second element
|
|
715 * @return whether elementA is equal to elementB
|
|
716 */
|
|
717 protected int opEquals(Object elementA, Object elementB) {
|
|
718 if (comparer is null) {
|
|
719 return elementA is null ? elementB is null : elementA.opEquals(elementB);
|
|
720 } else {
|
|
721 return elementA is null ? elementB is null : comparer.opEquals(elementA, elementB);
|
|
722 }
|
|
723 }
|
|
724
|
|
725 /**
|
|
726 * Returns the result of running the given elements through the filters.
|
|
727 *
|
|
728 * @param elements
|
|
729 * the elements to filter
|
|
730 * @return only the elements which all filters accept
|
|
731 */
|
|
732 protected Object[] filter(Object[] elements) {
|
|
733 if (filters !is null) {
|
|
734 ArraySeq!(Object) filtered = new ArraySeq!(Object);
|
|
735 filtered.capacity(elements.length);
|
|
736 Object root = getRoot();
|
|
737 for (int i = 0; i < elements.length; i++) {
|
|
738 bool add = true;
|
|
739 for (int j = 0; j < filters.size(); j++) {
|
|
740 add = (cast(ViewerFilter) filters.get(j)).select(this, root, elements[i]);
|
|
741 if (!add) {
|
|
742 break;
|
|
743 }
|
|
744 }
|
|
745 if (add) {
|
|
746 filtered.append(elements[i]);
|
|
747 }
|
|
748 }
|
|
749 return filtered.toArray();
|
|
750 }
|
|
751 return elements;
|
|
752 }
|
|
753
|
|
754 /**
|
|
755 * Finds the widget which represents the given element.
|
|
756 * <p>
|
|
757 * The default implementation of this method tries first to find the widget
|
|
758 * for the given element assuming that it is the viewer's input; this is
|
|
759 * done by calling <code>doFindInputItem</code>. If it is not found
|
|
760 * there, it is looked up in the internal element map provided that this
|
|
761 * feature has been enabled. If the element map is disabled, the widget is
|
|
762 * found via <code>doFindInputItem</code>.
|
|
763 * </p>
|
|
764 *
|
|
765 * @param element
|
|
766 * the element
|
|
767 * @return the corresponding widget, or <code>null</code> if none
|
|
768 */
|
|
769 protected final Widget findItem(Object element) {
|
|
770 Widget[] result = findItems(element);
|
|
771 return result.length is 0 ? null : result[0];
|
|
772 }
|
|
773
|
|
774 /**
|
|
775 * Finds the widgets which represent the given element. The returned array
|
|
776 * must not be changed by clients; it might change upon calling other
|
|
777 * methods on this viewer.
|
|
778 * <p>
|
|
779 * This method was introduced to support multiple equal elements in a viewer
|
|
780 * (@see {@link AbstractTreeViewer}). Multiple equal elements are only
|
|
781 * supported if the element map is enabled by calling
|
|
782 * {@link #setUseHashlookup(bool)} and passing <code>true</code>.
|
|
783 * </p>
|
|
784 * <p>
|
|
785 * The default implementation of this method tries first to find the widget
|
|
786 * for the given element assuming that it is the viewer's input; this is
|
|
787 * done by calling <code>doFindInputItem</code>. If it is not found
|
|
788 * there, the widgets are looked up in the internal element map provided
|
|
789 * that this feature has been enabled. If the element map is disabled, the
|
|
790 * widget is found via <code>doFindInputItem</code>.
|
|
791 * </p>
|
|
792 *
|
|
793 * @param element
|
|
794 * the element
|
|
795 * @return the corresponding widgets
|
|
796 *
|
|
797 * @since 3.2
|
|
798 */
|
|
799 protected final Widget[] findItems(Object element) {
|
|
800 Widget result = doFindInputItem(element);
|
|
801 if (result !is null) {
|
|
802 return [ result ];
|
|
803 }
|
|
804 // if we have an element map use it, otherwise search for the item.
|
|
805 if (usingElementMap()) {
|
|
806 Object widgetOrWidgets = elementMap.get(element);
|
|
807 if (widgetOrWidgets is null) {
|
|
808 return NO_WIDGETS;
|
|
809 } else if ( auto w = cast(Widget) widgetOrWidgets ) {
|
|
810 return [w];
|
|
811 } else {
|
|
812 return (cast(ArrayWrapperT!(Widget))widgetOrWidgets).array;
|
|
813 }
|
|
814 }
|
|
815 result = doFindItem(element);
|
|
816 return result is null ? NO_WIDGETS : [ result ];
|
|
817 }
|
|
818
|
|
819 /**
|
|
820 * Notifies any double-click listeners that a double-click has been
|
|
821 * received. Only listeners registered at the time this method is called are
|
|
822 * notified.
|
|
823 *
|
|
824 * @param event
|
|
825 * a double-click event
|
|
826 *
|
|
827 * @see IDoubleClickListener#doubleClick
|
|
828 */
|
|
829 protected void fireDoubleClick(DoubleClickEvent event) {
|
|
830 Object[] listeners = doubleClickListeners.getListeners();
|
|
831 for (int i = 0; i < listeners.length; ++i) {
|
39
|
832 SafeRunnable.run(new class(cast(IDoubleClickListener) listeners[i]) SafeRunnable {
|
10
|
833 IDoubleClickListener l;
|
39
|
834 this(IDoubleClickListener a){
|
|
835 l = a;
|
10
|
836 }
|
|
837 public void run() {
|
|
838 l.doubleClick(event);
|
|
839 }
|
|
840 });
|
|
841 }
|
|
842 }
|
|
843 package void fireDoubleClick_package(DoubleClickEvent event) {
|
|
844 fireDoubleClick(event);
|
|
845 }
|
|
846
|
|
847 /**
|
|
848 * Notifies any open event listeners that a open event has been received.
|
|
849 * Only listeners registered at the time this method is called are notified.
|
|
850 *
|
|
851 * @param event
|
|
852 * a double-click event
|
|
853 *
|
|
854 * @see IOpenListener#open(OpenEvent)
|
|
855 */
|
|
856 protected void fireOpen(OpenEvent event) {
|
|
857 Object[] listeners = openListeners.getListeners();
|
|
858 for (int i = 0; i < listeners.length; ++i) {
|
39
|
859 SafeRunnable.run(new class(cast(IOpenListener) listeners[i]) SafeRunnable {
|
10
|
860 IOpenListener l;
|
39
|
861 this(IOpenListener a){
|
|
862 l = a;
|
10
|
863 }
|
|
864 public void run() {
|
|
865 l.open(event);
|
|
866 }
|
|
867 });
|
|
868 }
|
|
869 }
|
|
870 package void fireOpen_package(OpenEvent event) {
|
|
871 fireOpen(event);
|
|
872 }
|
|
873
|
|
874 /**
|
|
875 * Notifies any post selection listeners that a post selection event has
|
|
876 * been received. Only listeners registered at the time this method is
|
|
877 * called are notified.
|
|
878 *
|
|
879 * @param event
|
|
880 * a selection changed event
|
|
881 *
|
|
882 * @see #addPostSelectionChangedListener(ISelectionChangedListener)
|
|
883 */
|
|
884 protected void firePostSelectionChanged(SelectionChangedEvent event) {
|
|
885 Object[] listeners = postSelectionChangedListeners.getListeners();
|
|
886 for (int i = 0; i < listeners.length; ++i) {
|
39
|
887 SafeRunnable.run(new class(cast(ISelectionChangedListener) listeners[i]) SafeRunnable {
|
10
|
888 ISelectionChangedListener l;
|
39
|
889 this(ISelectionChangedListener a){
|
|
890 l = a;
|
10
|
891 }
|
|
892 public void run() {
|
|
893 l.selectionChanged(event);
|
|
894 }
|
|
895 });
|
|
896 }
|
|
897 }
|
|
898
|
|
899 /**
|
|
900 * Returns the comparer to use for comparing elements, or
|
|
901 * <code>null</code> if none has been set. If specified,
|
|
902 * the viewer uses this to compare and hash elements rather
|
|
903 * than the elements' own equals and hashCode methods.
|
|
904 *
|
|
905 * @return the comparer to use for comparing elements or
|
|
906 * <code>null</code>
|
|
907 */
|
|
908 public IElementComparer getComparer() {
|
|
909 return comparer;
|
|
910 }
|
|
911
|
|
912 /**
|
|
913 * Returns the filtered array of children of the given element. The
|
|
914 * resulting array must not be modified, as it may come directly from the
|
|
915 * model's internal state.
|
|
916 *
|
|
917 * @param parent
|
|
918 * the parent element
|
|
919 * @return a filtered array of child elements
|
|
920 */
|
|
921 protected Object[] getFilteredChildren(Object parent) {
|
|
922 Object[] result = getRawChildren(parent);
|
|
923 if (filters !is null) {
|
|
924 foreach (f;filters) {
|
|
925 result = f.filter(this, parent, result);
|
|
926 }
|
|
927 }
|
|
928 return result;
|
|
929 }
|
|
930
|
|
931 /**
|
|
932 * Returns this viewer's filters.
|
|
933 *
|
|
934 * @return an array of viewer filters
|
|
935 * @see StructuredViewer#setFilters(ViewerFilter[])
|
|
936 */
|
|
937 public ViewerFilter[] getFilters() {
|
|
938 if (filters is null) {
|
|
939 return new ViewerFilter[0];
|
|
940 }
|
|
941 return filters.toArray();
|
|
942 }
|
|
943
|
|
944 /**
|
|
945 * Returns the item at the given display-relative coordinates, or
|
|
946 * <code>null</code> if there is no item at that location or
|
|
947 * the underlying DWT-Control is not made up of {@link Item}
|
|
948 * (e.g {@link ListViewer})
|
|
949 * <p>
|
|
950 * The default implementation of this method returns <code>null</code>.
|
|
951 * </p>
|
|
952 *
|
|
953 * @param x
|
|
954 * horizontal coordinate
|
|
955 * @param y
|
|
956 * vertical coordinate
|
|
957 * @return the item, or <code>null</code> if there is no item at the given
|
|
958 * coordinates
|
|
959 * @deprecated This method is deprecated in 3.3 in favor of {@link ColumnViewer#getItemAt(dwt.graphics.Point)}.
|
|
960 * Viewers who are not subclasses of {@link ColumnViewer} should consider using a
|
|
961 * widget relative implementation like {@link ColumnViewer#getItemAt(dwt.graphics.Point)}.
|
|
962 *
|
|
963 */
|
|
964 protected Item getItem(int x, int y) {
|
|
965 return null;
|
|
966 }
|
|
967
|
|
968 /**
|
|
969 * Returns the children of the given parent without sorting and filtering
|
|
970 * them. The resulting array must not be modified, as it may come directly
|
|
971 * from the model's internal state.
|
|
972 * <p>
|
|
973 * Returns an empty array if the given parent is <code>null</code>.
|
|
974 * </p>
|
|
975 *
|
|
976 * @param parent
|
|
977 * the parent element
|
|
978 * @return the child elements
|
|
979 */
|
|
980 protected Object[] getRawChildren(Object parent) {
|
|
981 Object[] result = null;
|
|
982 if (parent !is null) {
|
|
983 IStructuredContentProvider cp = cast(IStructuredContentProvider) getContentProvider();
|
|
984 if (cp !is null) {
|
|
985 result = cp.getElements(parent);
|
|
986 assertElementsNotNull(result);
|
|
987 }
|
|
988 }
|
|
989 return (result !is null) ? result : null;
|
|
990 }
|
|
991
|
|
992 /**
|
|
993 * Returns the root element.
|
|
994 * <p>
|
|
995 * The default implementation of this framework method forwards to
|
|
996 * <code>getInput</code>. Override if the root element is different from
|
|
997 * the viewer's input element.
|
|
998 * </p>
|
|
999 *
|
|
1000 * @return the root element, or <code>null</code> if none
|
|
1001 */
|
|
1002 protected Object getRoot() {
|
|
1003 return getInput();
|
|
1004 }
|
|
1005
|
|
1006 /**
|
|
1007 * The <code>StructuredViewer</code> implementation of this method returns
|
|
1008 * the result as an <code>IStructuredSelection</code>.
|
|
1009 * <p>
|
|
1010 * Subclasses do not typically override this method, but implement
|
|
1011 * <code>getSelectionFromWidget(List)</code> instead.
|
|
1012 * <p>
|
|
1013 * @return ISelection
|
|
1014 */
|
|
1015 public ISelection getSelection() {
|
|
1016 Control control = getControl();
|
|
1017 if (control is null || control.isDisposed()) {
|
|
1018 return StructuredSelection.EMPTY;
|
|
1019 }
|
|
1020 auto list = getSelectionFromWidget();
|
|
1021 return new StructuredSelection(list);
|
|
1022 }
|
|
1023
|
|
1024 /**
|
|
1025 * Retrieves the selection, as a <code>List</code>, from the underlying
|
|
1026 * widget.
|
|
1027 *
|
|
1028 * @return the list of selected elements
|
|
1029 */
|
|
1030 protected abstract SeqView!(Object) getSelectionFromWidget();
|
|
1031 package SeqView!(Object) getSelectionFromWidget_package(){
|
|
1032 return getSelectionFromWidget();
|
|
1033 }
|
|
1034
|
|
1035 /**
|
|
1036 * Returns the sorted and filtered set of children of the given element. The
|
|
1037 * resulting array must not be modified, as it may come directly from the
|
|
1038 * model's internal state.
|
|
1039 *
|
|
1040 * @param parent
|
|
1041 * the parent element
|
|
1042 * @return a sorted and filtered array of child elements
|
|
1043 */
|
|
1044 protected Object[] getSortedChildren(Object parent) {
|
|
1045 Object[] result = getFilteredChildren(parent);
|
|
1046 if (sorter !is null) {
|
|
1047 // be sure we're not modifying the original array from the model
|
|
1048 result = result.dup;
|
|
1049 sorter.sort(this, result);
|
|
1050 }
|
|
1051 return result;
|
|
1052 }
|
|
1053
|
|
1054 /**
|
|
1055 * Returns this viewer's sorter, or <code>null</code> if it does not have
|
|
1056 * one. If this viewer has a comparator that was set via
|
|
1057 * <code>setComparator(ViewerComparator)</code> then this method will return
|
|
1058 * <code>null</code> if the comparator is not an instance of ViewerSorter.
|
|
1059 * <p>
|
|
1060 * It is recommended to use <code>getComparator()</code> instead.
|
|
1061 * </p>
|
|
1062 *
|
|
1063 * @return a viewer sorter, or <code>null</code> if none or if the comparator is
|
|
1064 * not an instance of ViewerSorter
|
|
1065 */
|
|
1066 public ViewerSorter getSorter() {
|
|
1067 if ( auto vs = cast(ViewerSorter)sorter )
|
|
1068 return vs;
|
|
1069 return null;
|
|
1070 }
|
|
1071
|
|
1072 /**
|
|
1073 * Return this viewer's comparator used to sort elements.
|
|
1074 * This method should be used instead of <code>getSorter()</code>.
|
|
1075 *
|
|
1076 * @return a viewer comparator, or <code>null</code> if none
|
|
1077 *
|
|
1078 * @since 3.2
|
|
1079 */
|
|
1080 public ViewerComparator getComparator(){
|
|
1081 return sorter;
|
|
1082 }
|
|
1083
|
|
1084 /**
|
|
1085 * Handles a double-click select event from the widget.
|
|
1086 * <p>
|
|
1087 * This method is internal to the framework; subclassers should not call
|
|
1088 * this method.
|
|
1089 * </p>
|
|
1090 *
|
|
1091 * @param event
|
|
1092 * the DWT selection event
|
|
1093 */
|
|
1094 protected void handleDoubleSelect(SelectionEvent event) {
|
|
1095 // This method is reimplemented in AbstractTreeViewer to fix bug 108102.
|
|
1096
|
|
1097 // handle case where an earlier selection listener disposed the control.
|
|
1098 Control control = getControl();
|
|
1099 if (control !is null && !control.isDisposed()) {
|
|
1100 // If the double-clicked element can be obtained from the event, use it
|
|
1101 // otherwise get it from the control. Some controls like List do
|
|
1102 // not have the notion of item.
|
|
1103 // For details, see bug 90161 [Navigator] DefaultSelecting folders shouldn't always expand first one
|
|
1104 ISelection selection;
|
|
1105 if (event.item !is null && event.item.getData() !is null) {
|
|
1106 selection = new StructuredSelection(event.item.getData());
|
|
1107 }
|
|
1108 else {
|
|
1109 selection = getSelection();
|
|
1110 updateSelection(selection);
|
|
1111 }
|
|
1112 fireDoubleClick(new DoubleClickEvent(this, selection));
|
|
1113 }
|
|
1114 }
|
|
1115
|
|
1116 /**
|
|
1117 * Handles an open event from the OpenStrategy.
|
|
1118 * <p>
|
|
1119 * This method is internal to the framework; subclassers should not call
|
|
1120 * this method.
|
|
1121 * </p>
|
|
1122 *
|
|
1123 * @param event
|
|
1124 * the DWT selection event
|
|
1125 */
|
|
1126 protected void handleOpen(SelectionEvent event) {
|
|
1127 Control control = getControl();
|
|
1128 if (control !is null && !control.isDisposed()) {
|
|
1129 ISelection selection = getSelection();
|
|
1130 fireOpen(new OpenEvent(this, selection));
|
|
1131 }
|
|
1132 }
|
|
1133
|
|
1134 /**
|
|
1135 * Handles an invalid selection.
|
|
1136 * <p>
|
|
1137 * This framework method is called if a model change picked up by a viewer
|
|
1138 * results in an invalid selection. For instance if an element contained in
|
|
1139 * the selection has been removed from the viewer, the viewer is free to
|
|
1140 * either remove the element from the selection or to pick another element
|
|
1141 * as its new selection. The default implementation of this method calls
|
|
1142 * <code>updateSelection</code>. Subclasses may override it to implement
|
|
1143 * a different strategy for picking a new selection when the old selection
|
|
1144 * becomes invalid.
|
|
1145 * </p>
|
|
1146 *
|
|
1147 * @param invalidSelection
|
|
1148 * the selection before the viewer was updated
|
|
1149 * @param newSelection
|
|
1150 * the selection after the update, or <code>null</code> if none
|
|
1151 */
|
|
1152 protected void handleInvalidSelection(ISelection invalidSelection, ISelection newSelection) {
|
|
1153 updateSelection(newSelection);
|
|
1154 SelectionChangedEvent event = new SelectionChangedEvent(this, newSelection);
|
|
1155 firePostSelectionChanged(event);
|
|
1156 }
|
|
1157
|
|
1158 /**
|
|
1159 * The <code>StructuredViewer</code> implementation of this
|
|
1160 * <code>ContentViewer</code> method calls <code>update</code> if the
|
|
1161 * event specifies that the label of a given element has changed, otherwise
|
|
1162 * it calls super. Subclasses may reimplement or extend.
|
|
1163 * </p>
|
|
1164 * @param event the event that generated this update
|
|
1165 */
|
|
1166 protected void handleLabelProviderChanged(LabelProviderChangedEvent event) {
|
|
1167 Object[] elements = event.getElements();
|
|
1168 if (elements !is null) {
|
|
1169 update(elements, null);
|
|
1170 } else {
|
|
1171 super.handleLabelProviderChanged(event);
|
|
1172 }
|
|
1173 }
|
|
1174 package void handleLabelProviderChanged_package(LabelProviderChangedEvent event) {
|
|
1175 handleLabelProviderChanged(event);
|
|
1176 }
|
|
1177
|
|
1178 /**
|
|
1179 * Handles a select event from the widget.
|
|
1180 * <p>
|
|
1181 * This method is internal to the framework; subclassers should not call
|
|
1182 * this method.
|
|
1183 * </p>
|
|
1184 *
|
|
1185 * @param event
|
|
1186 * the DWT selection event
|
|
1187 */
|
|
1188 protected void handleSelect(SelectionEvent event) {
|
|
1189 // handle case where an earlier selection listener disposed the control.
|
|
1190 Control control = getControl();
|
|
1191 if (control !is null && !control.isDisposed()) {
|
|
1192 updateSelection(getSelection());
|
|
1193 }
|
|
1194 }
|
|
1195
|
|
1196 /**
|
|
1197 * Handles a post select event from the widget.
|
|
1198 * <p>
|
|
1199 * This method is internal to the framework; subclassers should not call
|
|
1200 * this method.
|
|
1201 * </p>
|
|
1202 *
|
|
1203 * @param e the DWT selection event
|
|
1204 */
|
|
1205 protected void handlePostSelect(SelectionEvent e) {
|
|
1206 SelectionChangedEvent event = new SelectionChangedEvent(this, getSelection());
|
|
1207 firePostSelectionChanged(event);
|
|
1208 }
|
|
1209
|
|
1210 /*
|
|
1211 * (non-Javadoc) Method declared on Viewer.
|
|
1212 */
|
|
1213 protected void hookControl(Control control) {
|
|
1214 super.hookControl(control);
|
|
1215 OpenStrategy handler = new OpenStrategy(control);
|
|
1216 handler.addSelectionListener(new class SelectionListener {
|
|
1217 public void widgetSelected(SelectionEvent e) {
|
|
1218 // On Windows, selection events may happen during a refresh.
|
|
1219 // Ignore these events if we are currently in preservingSelection().
|
|
1220 // See bug 184441.
|
|
1221 if (!inChange) {
|
|
1222 handleSelect(e);
|
|
1223 }
|
|
1224 }
|
|
1225
|
|
1226 public void widgetDefaultSelected(SelectionEvent e) {
|
|
1227 handleDoubleSelect(e);
|
|
1228 }
|
|
1229 });
|
|
1230 handler.addPostSelectionListener(new class SelectionAdapter {
|
|
1231 public void widgetSelected(SelectionEvent e) {
|
|
1232 handlePostSelect(e);
|
|
1233 }
|
|
1234 });
|
|
1235 handler.addOpenListener(new class IOpenEventListener {
|
|
1236 public void handleOpen(SelectionEvent e) {
|
|
1237 this.outer.handleOpen(e);
|
|
1238 }
|
|
1239 });
|
|
1240 }
|
|
1241
|
|
1242 /**
|
|
1243 * Returns whether this viewer has any filters.
|
|
1244 * @return bool
|
|
1245 */
|
|
1246 protected bool hasFilters() {
|
|
1247 return filters !is null && filters.size() > 0;
|
|
1248 }
|
|
1249
|
|
1250 /**
|
|
1251 * Refreshes this viewer starting at the given element.
|
|
1252 *
|
|
1253 * @param element
|
|
1254 * the element
|
|
1255 */
|
|
1256 protected abstract void internalRefresh(Object element);
|
|
1257
|
|
1258 /**
|
|
1259 * Refreshes this viewer starting at the given element. Labels are updated
|
|
1260 * as described in <code>refresh(bool updateLabels)</code>.
|
|
1261 * <p>
|
|
1262 * The default implementation simply calls
|
|
1263 * <code>internalRefresh(element)</code>, ignoring
|
|
1264 * <code>updateLabels</code>.
|
|
1265 * <p>
|
|
1266 * If this method is overridden to do the actual refresh, then
|
|
1267 * <code>internalRefresh(Object element)</code> should simply call
|
|
1268 * <code>internalRefresh(element, true)</code>.
|
|
1269 *
|
|
1270 * @param element
|
|
1271 * the element
|
|
1272 * @param updateLabels
|
|
1273 * <code>true</code> to update labels for existing elements,
|
|
1274 * <code>false</code> to only update labels as needed, assuming
|
|
1275 * that labels for existing elements are unchanged.
|
|
1276 *
|
|
1277 * @since 2.0
|
|
1278 */
|
|
1279 protected void internalRefresh(Object element, bool updateLabels) {
|
|
1280 internalRefresh(element);
|
|
1281 }
|
|
1282
|
|
1283 /**
|
|
1284 * Adds the element item pair to the element map.
|
|
1285 * <p>
|
|
1286 * This method is internal to the framework; subclassers should not call
|
|
1287 * this method.
|
|
1288 * </p>
|
|
1289 *
|
|
1290 * @param element
|
|
1291 * the element
|
|
1292 * @param item
|
|
1293 * the corresponding widget
|
|
1294 */
|
|
1295 protected void mapElement(Object element, Widget item) {
|
|
1296 if (elementMap !is null) {
|
|
1297 Object widgetOrWidgets = elementMap.get(element);
|
|
1298 if (widgetOrWidgets is null) {
|
|
1299 elementMap.put(element, item);
|
|
1300 } else if ( auto w = cast(Widget)widgetOrWidgets ) {
|
|
1301 if (widgetOrWidgets !is item) {
|
|
1302 elementMap.put(element, new ArrayWrapperObject([ cast(Object)
|
|
1303 w, item ]));
|
|
1304 }
|
|
1305 } else {
|
|
1306 Widget[] widgets = (cast(ArrayWrapperT!(Widget)) widgetOrWidgets).array;
|
|
1307 int indexOfItem = -1;
|
|
1308 foreach( idx, w; widgets ){
|
|
1309 if( w == item ){
|
|
1310 indexOfItem = idx;
|
|
1311 break;
|
|
1312 }
|
|
1313 }
|
|
1314 if (indexOfItem is -1) {
|
|
1315 int length_ = widgets.length;
|
|
1316 System.arraycopy(widgets, 0,
|
|
1317 widgets = new Widget[length_ + 1], 0, length_);
|
|
1318 widgets[length_] = item;
|
|
1319 elementMap.put(element, new ArrayWrapperObject(widgets));
|
|
1320 }
|
|
1321 }
|
|
1322 }
|
|
1323 }
|
|
1324
|
|
1325 /**
|
|
1326 * Determines whether a change to the given property of the given element
|
|
1327 * would require refiltering and/or resorting.
|
|
1328 * <p>
|
|
1329 * This method is internal to the framework; subclassers should not call
|
|
1330 * this method.
|
|
1331 * </p>
|
|
1332 *
|
|
1333 * @param element
|
|
1334 * the element
|
|
1335 * @param property
|
|
1336 * the property
|
|
1337 * @return <code>true</code> if refiltering is required, and
|
|
1338 * <code>false</code> otherwise
|
|
1339 */
|
|
1340 protected bool needsRefilter(Object element, String property) {
|
|
1341 if (sorter !is null && sorter.isSorterProperty(element, property)) {
|
|
1342 return true;
|
|
1343 }
|
|
1344
|
|
1345 if (filters !is null) {
|
|
1346 foreach( filter; filters ){
|
|
1347 if (filter.isFilterProperty(element, property)) {
|
|
1348 return true;
|
|
1349 }
|
|
1350 }
|
|
1351 }
|
|
1352 return false;
|
|
1353 }
|
|
1354
|
|
1355 /**
|
|
1356 * Returns a new hashtable using the given capacity and this viewer's element comparer.
|
|
1357 *
|
|
1358 * @param capacity the initial capacity of the hashtable
|
|
1359 * @return a new hashtable
|
|
1360 *
|
|
1361 * @since 3.0
|
|
1362 */
|
|
1363 CustomHashtable newHashtable(int capacity) {
|
|
1364 return new CustomHashtable(capacity, getComparer());
|
|
1365 }
|
|
1366
|
|
1367 /**
|
|
1368 * Attempts to preserves the current selection across a run of the given
|
|
1369 * code.
|
|
1370 * <p>
|
|
1371 * The default implementation of this method:
|
|
1372 * <ul>
|
|
1373 * <li>discovers the old selection (via <code>getSelection</code>)</li>
|
|
1374 * <li>runs the given runnable</li>
|
|
1375 * <li>attempts to restore the old selection (using
|
|
1376 * <code>setSelectionToWidget</code></li>
|
|
1377 * <li>rediscovers the resulting selection (via <code>getSelection</code>)
|
|
1378 * </li>
|
|
1379 * <li>calls <code>handleInvalidSelection</code> if the selection did not
|
|
1380 * take</li>
|
|
1381 * <li>calls <code>postUpdateHook</code></li>
|
|
1382 * </ul>
|
|
1383 * </p>
|
|
1384 *
|
|
1385 * @param updateCode
|
|
1386 * the code to run
|
|
1387 */
|
|
1388 protected void preservingSelection(Runnable updateCode) {
|
|
1389 preservingSelection(updateCode, false);
|
|
1390 }
|
|
1391
|
|
1392 /**
|
|
1393 * Attempts to preserves the current selection across a run of the given
|
|
1394 * code, with a best effort to avoid scrolling if <code>reveal</code> is false,
|
|
1395 * or to reveal the selection if <code>reveal</code> is true.
|
|
1396 * <p>
|
|
1397 * The default implementation of this method:
|
|
1398 * <ul>
|
|
1399 * <li>discovers the old selection (via <code>getSelection</code>)</li>
|
|
1400 * <li>runs the given runnable</li>
|
|
1401 * <li>attempts to restore the old selection (using
|
|
1402 * <code>setSelectionToWidget</code></li>
|
|
1403 * <li>rediscovers the resulting selection (via <code>getSelection</code>)
|
|
1404 * </li>
|
|
1405 * <li>calls <code>handleInvalidSelection</code> if the selection did not
|
|
1406 * take</li>
|
|
1407 * <li>calls <code>postUpdateHook</code></li>
|
|
1408 * </ul>
|
|
1409 * </p>
|
|
1410 *
|
|
1411 * @param updateCode
|
|
1412 * the code to run
|
|
1413 * @param reveal
|
|
1414 * <code>true</code> if the selection should be made visible,
|
|
1415 * <code>false</code> if scrolling should be avoided
|
|
1416 * @since 3.3
|
|
1417 */
|
|
1418 void preservingSelection(Runnable updateCode, bool reveal) {
|
|
1419
|
|
1420 ISelection oldSelection = null;
|
|
1421 try {
|
|
1422 // preserve selection
|
|
1423 oldSelection = getSelection();
|
|
1424 inChange = restoreSelection = true;
|
|
1425
|
|
1426 // perform the update
|
|
1427 updateCode.run();
|
|
1428
|
|
1429 } finally {
|
|
1430 inChange = false;
|
|
1431
|
|
1432 // restore selection
|
|
1433 if (restoreSelection) {
|
|
1434 setSelectionToWidget(oldSelection, reveal);
|
|
1435 }
|
|
1436
|
|
1437 // send out notification if old and new differ
|
|
1438 ISelection newSelection = getSelection();
|
|
1439 if (!(cast(Object)newSelection).opEquals(cast(Object)oldSelection)) {
|
|
1440 handleInvalidSelection(oldSelection, newSelection);
|
|
1441 }
|
|
1442 }
|
|
1443 }
|
|
1444
|
|
1445 /*
|
|
1446 * Non-Javadoc. Method declared on Viewer.
|
|
1447 */
|
|
1448 public void refresh() {
|
|
1449 refresh(getRoot());
|
|
1450 }
|
|
1451
|
|
1452 /**
|
|
1453 * Refreshes this viewer with information freshly obtained from this
|
|
1454 * viewer's model. If <code>updateLabels</code> is <code>true</code>
|
|
1455 * then labels for otherwise unaffected elements are updated as well.
|
|
1456 * Otherwise, it assumes labels for existing elements are unchanged, and
|
|
1457 * labels are only obtained as needed (for example, for new elements).
|
|
1458 * <p>
|
|
1459 * Calling <code>refresh(true)</code> has the same effect as
|
|
1460 * <code>refresh()</code>.
|
|
1461 * <p>
|
|
1462 * Note that the implementation may still obtain labels for existing
|
|
1463 * elements even if <code>updateLabels</code> is false. The intent is
|
|
1464 * simply to allow optimization where possible.
|
|
1465 *
|
|
1466 * @param updateLabels
|
|
1467 * <code>true</code> to update labels for existing elements,
|
|
1468 * <code>false</code> to only update labels as needed, assuming
|
|
1469 * that labels for existing elements are unchanged.
|
|
1470 *
|
|
1471 * @since 2.0
|
|
1472 */
|
|
1473 public void refresh(bool updateLabels) {
|
|
1474 refresh(getRoot(), updateLabels);
|
|
1475 }
|
|
1476
|
|
1477 /**
|
|
1478 * Refreshes this viewer starting with the given element.
|
|
1479 * <p>
|
|
1480 * Unlike the <code>update</code> methods, this handles structural changes
|
|
1481 * to the given element (e.g. addition or removal of children). If only the
|
|
1482 * given element needs updating, it is more efficient to use the
|
|
1483 * <code>update</code> methods.
|
|
1484 * </p>
|
|
1485 *
|
|
1486 * @param element
|
|
1487 * the element
|
|
1488 */
|
|
1489 public void refresh(Object element) {
|
39
|
1490 preservingSelection(new class(element) Runnable {
|
10
|
1491 Object element_;
|
39
|
1492 this(Object a){
|
|
1493 element_ = a;
|
10
|
1494 }
|
|
1495 public void run() {
|
|
1496 internalRefresh(element_);
|
|
1497 }
|
|
1498 });
|
|
1499 }
|
|
1500
|
|
1501 /**
|
|
1502 * Refreshes this viewer starting with the given element. Labels are updated
|
|
1503 * as described in <code>refresh(bool updateLabels)</code>.
|
|
1504 * <p>
|
|
1505 * Unlike the <code>update</code> methods, this handles structural changes
|
|
1506 * to the given element (e.g. addition or removal of children). If only the
|
|
1507 * given element needs updating, it is more efficient to use the
|
|
1508 * <code>update</code> methods.
|
|
1509 * </p>
|
|
1510 *
|
|
1511 * @param element
|
|
1512 * the element
|
|
1513 * @param updateLabels
|
|
1514 * <code>true</code> to update labels for existing elements,
|
|
1515 * <code>false</code> to only update labels as needed, assuming
|
|
1516 * that labels for existing elements are unchanged.
|
|
1517 *
|
|
1518 * @since 2.0
|
|
1519 */
|
|
1520 public void refresh(Object element, bool updateLabels) {
|
|
1521 preservingSelection(new class Runnable {
|
|
1522 Object element_;
|
|
1523 bool updateLabels_;
|
|
1524 this(){
|
|
1525 element_ = element;
|
|
1526 updateLabels_ = updateLabels;
|
|
1527 }
|
|
1528 public void run() {
|
|
1529 internalRefresh(element_, updateLabels_);
|
|
1530 }
|
|
1531 });
|
|
1532 }
|
|
1533
|
|
1534 /**
|
|
1535 *
|
|
1536 * Refreshes the given TableItem with the given element. Calls
|
|
1537 * <code>doUpdateItem(..., false)</code>.
|
|
1538 * <p>
|
|
1539 * This method is internal to the framework; subclassers should not call
|
|
1540 * this method.
|
|
1541 * </p>
|
|
1542 * @param widget
|
|
1543 * the widget
|
|
1544 * @param element
|
|
1545 * the element
|
|
1546 */
|
|
1547 protected final void refreshItem(Widget widget, Object element) {
|
|
1548 SafeRunnable.run(new UpdateItemSafeRunnable(widget, element, true));
|
|
1549 }
|
|
1550
|
|
1551 /**
|
|
1552 * Removes the given open listener from this viewer. Has no affect if an
|
|
1553 * identical listener is not registered.
|
|
1554 *
|
|
1555 * @param listener
|
|
1556 * a double-click listener
|
|
1557 */
|
|
1558 public void removeOpenListener(IOpenListener listener) {
|
|
1559 openListeners.remove(cast(Object)listener);
|
|
1560 }
|
|
1561
|
|
1562 /*
|
|
1563 * (non-Javadoc) Method declared on IPostSelectionProvider.
|
|
1564 */
|
|
1565 public void removePostSelectionChangedListener(ISelectionChangedListener listener) {
|
|
1566 postSelectionChangedListeners.remove(cast(Object)listener);
|
|
1567 }
|
|
1568
|
|
1569 /**
|
|
1570 * Removes the given double-click listener from this viewer. Has no affect
|
|
1571 * if an identical listener is not registered.
|
|
1572 *
|
|
1573 * @param listener
|
|
1574 * a double-click listener
|
|
1575 */
|
|
1576 public void removeDoubleClickListener(IDoubleClickListener listener) {
|
|
1577 doubleClickListeners.remove(cast(Object)listener);
|
|
1578 }
|
|
1579
|
|
1580 /**
|
|
1581 * Removes the given filter from this viewer, and triggers refiltering and
|
|
1582 * resorting of the elements if required. Has no effect if the identical
|
|
1583 * filter is not registered. If you want to remove more than one filter
|
|
1584 * consider using {@link StructuredViewer#setFilters(ViewerFilter[])}.
|
|
1585 *
|
|
1586 * @param filter
|
|
1587 * a viewer filter
|
|
1588 * @see StructuredViewer#setFilters(ViewerFilter[])
|
|
1589 */
|
|
1590 public void removeFilter(ViewerFilter filter) {
|
|
1591 Assert.isNotNull(filter);
|
|
1592 if (filters !is null) {
|
|
1593 // Note: can't use List.remove(Object). Use identity comparison
|
|
1594 // instead.
|
|
1595 int delIdx = 0;
|
|
1596 foreach( o; filters ){
|
|
1597 if (o is filter) {
|
|
1598 filters.removeAt(delIdx);
|
|
1599 refresh();
|
|
1600 if (filters.size() is 0) {
|
|
1601 filters = null;
|
|
1602 }
|
|
1603 return;
|
|
1604 }
|
|
1605 delIdx++;
|
|
1606 }
|
|
1607 }
|
|
1608 }
|
|
1609
|
|
1610 /**
|
|
1611 * Sets the filters, replacing any previous filters, and triggers
|
|
1612 * refiltering and resorting of the elements.
|
|
1613 *
|
|
1614 * @param filters
|
|
1615 * an array of viewer filters
|
|
1616 * @since 3.3
|
|
1617 */
|
|
1618 public void setFilters(ViewerFilter[] filters) {
|
|
1619 if (filters.length is 0) {
|
|
1620 resetFilters();
|
|
1621 } else {
|
|
1622 this.filters = new ArraySeq!(ViewerFilter);
|
|
1623 foreach( f; filters ){
|
|
1624 this.filters.append(f);
|
|
1625 }
|
|
1626 refresh();
|
|
1627 }
|
|
1628 }
|
|
1629
|
|
1630 /**
|
|
1631 * Discards this viewer's filters and triggers refiltering and resorting of
|
|
1632 * the elements.
|
|
1633 */
|
|
1634 public void resetFilters() {
|
|
1635 if (filters !is null) {
|
|
1636 filters = null;
|
|
1637 refresh();
|
|
1638 }
|
|
1639 }
|
|
1640
|
|
1641 /**
|
|
1642 * Ensures that the given element is visible, scrolling the viewer if
|
|
1643 * necessary. The selection is unchanged.
|
|
1644 *
|
|
1645 * @param element
|
|
1646 * the element to reveal
|
|
1647 */
|
|
1648 public abstract void reveal(Object element);
|
|
1649
|
|
1650 /*
|
|
1651 * (non-Javadoc)
|
|
1652 * @see dwtx.jface.viewers.ContentViewer#setContentProvider(dwtx.jface.viewers.IContentProvider)
|
|
1653 */
|
|
1654 public void setContentProvider(IContentProvider provider) {
|
|
1655 assertContentProviderType(provider);
|
|
1656 super.setContentProvider(provider);
|
|
1657 }
|
|
1658
|
|
1659 /**
|
|
1660 * Assert that the content provider is of one of the
|
|
1661 * supported types.
|
|
1662 * @param provider
|
|
1663 */
|
|
1664 protected void assertContentProviderType(IContentProvider provider) {
|
|
1665 Assert.isTrue( null !is cast(IStructuredContentProvider)provider );
|
|
1666 }
|
|
1667
|
|
1668 /*
|
|
1669 * (non-Javadoc)
|
|
1670 * @see dwtx.jface.viewers.Viewer#setInput(java.lang.Object)
|
|
1671 */
|
|
1672 public final void setInput(Object input) {
|
|
1673
|
|
1674 try {
|
|
1675 // fInChange= true;
|
|
1676
|
|
1677 unmapAllElements();
|
|
1678
|
|
1679 super.setInput(input);
|
|
1680
|
|
1681 } finally {
|
|
1682 // fInChange= false;
|
|
1683 }
|
|
1684 }
|
|
1685
|
|
1686 /*
|
|
1687 * (non-Javadoc)
|
|
1688 * @see dwtx.jface.viewers.Viewer#setSelection(dwtx.jface.viewers.ISelection, bool)
|
|
1689 */
|
|
1690 public void setSelection(ISelection selection, bool reveal) {
|
|
1691 /**
|
|
1692 * <p>
|
|
1693 * If the new selection differs from the current selection the hook
|
|
1694 * <code>updateSelection</code> is called.
|
|
1695 * </p>
|
|
1696 * <p>
|
|
1697 * If <code>setSelection</code> is called from within
|
|
1698 * <code>preserveSelection</code>, the call to
|
|
1699 * <code>updateSelection</code> is delayed until the end of
|
|
1700 * <code>preserveSelection</code>.
|
|
1701 * </p>
|
|
1702 * <p>
|
|
1703 * Subclasses do not typically override this method, but implement
|
|
1704 * <code>setSelectionToWidget</code> instead.
|
|
1705 * </p>
|
|
1706 */
|
|
1707 Control control = getControl();
|
|
1708 if (control is null || control.isDisposed()) {
|
|
1709 return;
|
|
1710 }
|
|
1711 if (!inChange) {
|
|
1712 setSelectionToWidget(selection, reveal);
|
|
1713 ISelection sel = getSelection();
|
|
1714 updateSelection(sel);
|
|
1715 firePostSelectionChanged(new SelectionChangedEvent(this, sel));
|
|
1716 } else {
|
|
1717 restoreSelection = false;
|
|
1718 setSelectionToWidget(selection, reveal);
|
|
1719 }
|
|
1720 }
|
|
1721
|
|
1722 /**
|
|
1723 * Parlays the given list of selected elements into selections on this
|
|
1724 * viewer's control.
|
|
1725 * <p>
|
|
1726 * Subclasses should override to set their selection based on the given list
|
|
1727 * of elements.
|
|
1728 * </p>
|
|
1729 *
|
|
1730 * @param l
|
|
1731 * list of selected elements (element type: <code>Object</code>)
|
|
1732 * or <code>null</code> if the selection is to be cleared
|
|
1733 * @param reveal
|
|
1734 * <code>true</code> if the selection is to be made visible,
|
|
1735 * and <code>false</code> otherwise
|
|
1736 */
|
|
1737 protected abstract void setSelectionToWidget(SeqView!(Object) l, bool reveal);
|
|
1738
|
|
1739 /**
|
|
1740 * Converts the selection to a <code>List</code> and calls
|
|
1741 * <code>setSelectionToWidget(List, bool)</code>. The selection is
|
|
1742 * expected to be an <code>IStructuredSelection</code> of elements. If
|
|
1743 * not, the selection is cleared.
|
|
1744 * <p>
|
|
1745 * Subclasses do not typically override this method, but implement
|
|
1746 * <code>setSelectionToWidget(List, bool)</code> instead.
|
|
1747 *
|
|
1748 * @param selection
|
|
1749 * an IStructuredSelection of elements
|
|
1750 * @param reveal
|
|
1751 * <code>true</code> to reveal the first element in the
|
|
1752 * selection, or <code>false</code> otherwise
|
|
1753 */
|
|
1754 protected void setSelectionToWidget(ISelection selection, bool reveal) {
|
|
1755 if ( auto ss = cast(IStructuredSelection) selection ) {
|
|
1756 setSelectionToWidget(ss.toList(), reveal);
|
|
1757 } else {
|
|
1758 setSelectionToWidget(cast(SeqView!(Object)) null, reveal);
|
|
1759 }
|
|
1760 }
|
|
1761
|
|
1762 /**
|
|
1763 * Sets this viewer's sorter and triggers refiltering and resorting of this
|
|
1764 * viewer's element. Passing <code>null</code> turns sorting off.
|
|
1765 * <p>
|
|
1766 * It is recommended to use <code>setComparator()</code> instead.
|
|
1767 * </p>
|
|
1768 *
|
|
1769 * @param sorter
|
|
1770 * a viewer sorter, or <code>null</code> if none
|
|
1771 */
|
|
1772 public void setSorter(ViewerSorter sorter) {
|
|
1773 if (this.sorter !is sorter) {
|
|
1774 this.sorter = sorter;
|
|
1775 refresh();
|
|
1776 }
|
|
1777 }
|
|
1778
|
|
1779 /**
|
|
1780 * Sets this viewer's comparator to be used for sorting elements, and triggers refiltering and
|
|
1781 * resorting of this viewer's element. <code>null</code> turns sorting off.
|
|
1782 * To get the viewer's comparator, call <code>getComparator()</code>.
|
|
1783 * <p>
|
|
1784 * IMPORTANT: This method was introduced in 3.2. If a reference to this viewer object
|
|
1785 * is passed to clients who call <code>getSorter()<code>, null may be returned from
|
|
1786 * from that method even though the viewer is sorting its elements using the
|
|
1787 * viewer's comparator.
|
|
1788 * </p>
|
|
1789 *
|
|
1790 * @param comparator a viewer comparator, or <code>null</code> if none
|
|
1791 *
|
|
1792 * @since 3.2
|
|
1793 */
|
|
1794 public void setComparator(ViewerComparator comparator){
|
|
1795 if (this.sorter !is comparator){
|
|
1796 this.sorter = comparator;
|
|
1797 refresh();
|
|
1798 }
|
|
1799 }
|
|
1800
|
|
1801 /**
|
|
1802 * Configures whether this structured viewer uses an internal hash table to
|
|
1803 * speeds up the mapping between elements and DWT items. This must be called
|
|
1804 * before the viewer is given an input (via <code>setInput</code>).
|
|
1805 *
|
|
1806 * @param enable
|
|
1807 * <code>true</code> to enable hash lookup, and
|
|
1808 * <code>false</code> to disable it
|
|
1809 */
|
|
1810 public void setUseHashlookup(bool enable) {
|
|
1811 Assert.isTrue(getInput() is null,
|
|
1812 "Can only enable the hash look up before input has been set");//$NON-NLS-1$
|
|
1813 if (enable) {
|
|
1814 elementMap = newHashtable(CustomHashtable.DEFAULT_CAPACITY);
|
|
1815 } else {
|
|
1816 elementMap = null;
|
|
1817 }
|
|
1818 }
|
|
1819
|
|
1820 /**
|
|
1821 * Sets the comparer to use for comparing elements, or <code>null</code>
|
|
1822 * to use the default <code>equals</code> and <code>hashCode</code>
|
|
1823 * methods on the elements themselves.
|
|
1824 *
|
|
1825 * @param comparer
|
|
1826 * the comparer to use for comparing elements or
|
|
1827 * <code>null</code>
|
|
1828 */
|
|
1829 public void setComparer(IElementComparer comparer) {
|
|
1830 this.comparer = comparer;
|
|
1831 if (elementMap !is null) {
|
|
1832 elementMap = new CustomHashtable(elementMap, comparer);
|
|
1833 }
|
|
1834 }
|
|
1835
|
|
1836 /**
|
|
1837 * Hook for testing.
|
|
1838 * @param element
|
|
1839 * @return Widget
|
|
1840 */
|
|
1841 public Widget testFindItem(Object element) {
|
|
1842 return findItem(element);
|
|
1843 }
|
|
1844
|
|
1845 /**
|
|
1846 * Hook for testing.
|
|
1847 * @param element
|
|
1848 * @return Widget[]
|
|
1849 * @since 3.2
|
|
1850 */
|
|
1851 public Widget[] testFindItems(Object element) {
|
|
1852 return findItems(element);
|
|
1853 }
|
|
1854
|
|
1855 /**
|
|
1856 * Removes all elements from the map.
|
|
1857 * <p>
|
|
1858 * This method is internal to the framework; subclassers should not call
|
|
1859 * this method.
|
|
1860 * </p>
|
|
1861 */
|
|
1862 protected void unmapAllElements() {
|
|
1863 if (elementMap !is null) {
|
|
1864 elementMap = newHashtable(CustomHashtable.DEFAULT_CAPACITY);
|
|
1865 }
|
|
1866 }
|
|
1867
|
|
1868 /**
|
|
1869 * Removes the given element from the internal element to widget map. Does
|
|
1870 * nothing if mapping is disabled. If mapping is enabled, the given element
|
|
1871 * must be present.
|
|
1872 * <p>
|
|
1873 * This method is internal to the framework; subclassers should not call
|
|
1874 * this method.
|
|
1875 * </p>
|
|
1876 *
|
|
1877 * @param element
|
|
1878 * the element
|
|
1879 */
|
|
1880 protected void unmapElement(Object element) {
|
|
1881 if (elementMap !is null) {
|
|
1882 elementMap.remove(element);
|
|
1883 }
|
|
1884 }
|
|
1885
|
|
1886 /**
|
|
1887 * Removes the given association from the internal element to widget map.
|
|
1888 * Does nothing if mapping is disabled, or if the given element does not map
|
|
1889 * to the given item.
|
|
1890 * <p>
|
|
1891 * This method is internal to the framework; subclassers should not call
|
|
1892 * this method.
|
|
1893 * </p>
|
|
1894 *
|
|
1895 * @param element
|
|
1896 * the element
|
|
1897 * @param item the item to unmap
|
|
1898 * @since 2.0
|
|
1899 */
|
|
1900 protected void unmapElement(Object element, Widget item) {
|
|
1901 // double-check that the element actually maps to the given item before
|
|
1902 // unmapping it
|
|
1903 if (elementMap !is null) {
|
|
1904 Object widgetOrWidgets = elementMap.get(element);
|
|
1905 if (widgetOrWidgets is null) {
|
|
1906 // item was not mapped, return
|
|
1907 return;
|
|
1908 } else if ( auto w = cast(Widget) widgetOrWidgets ) {
|
|
1909 if (item is widgetOrWidgets) {
|
|
1910 elementMap.remove(element);
|
|
1911 }
|
|
1912 } else {
|
|
1913 Widget[] widgets = ( cast(ArrayWrapperT!(Widget)) widgetOrWidgets).array;
|
|
1914 int indexOfItem = -1;
|
|
1915 foreach( idx, w; widgets ){
|
|
1916 if( w == item ){
|
|
1917 indexOfItem = idx;
|
|
1918 break;
|
|
1919 }
|
|
1920 }
|
|
1921 if (indexOfItem is -1) {
|
|
1922 return;
|
|
1923 }
|
|
1924 int length = widgets.length;
|
|
1925 if (indexOfItem is 0) {
|
|
1926 if(length is 1) {
|
|
1927 elementMap.remove(element);
|
|
1928 } else {
|
|
1929 Widget[] updatedWidgets = new Widget[length - 1];
|
|
1930 System.arraycopy(widgets, 1, updatedWidgets, 0, length -1 );
|
|
1931 elementMap.put(element, new ArrayWrapperObject( updatedWidgets));
|
|
1932 }
|
|
1933 } else {
|
|
1934 Widget[] updatedWidgets = new Widget[length - 1];
|
|
1935 System.arraycopy(widgets, 0, updatedWidgets, 0, indexOfItem);
|
|
1936 System.arraycopy(widgets, indexOfItem + 1, updatedWidgets, indexOfItem, length - indexOfItem - 1);
|
|
1937 elementMap.put(element, new ArrayWrapperObject(updatedWidgets));
|
|
1938 }
|
|
1939 }
|
|
1940 }
|
|
1941 }
|
|
1942
|
|
1943 /**
|
|
1944 * Updates the given elements' presentation when one or more of their
|
|
1945 * properties change. Only the given elements are updated.
|
|
1946 * <p>
|
|
1947 * This does not handle structural changes (e.g. addition or removal of
|
|
1948 * elements), and does not update any other related elements (e.g. child
|
|
1949 * elements). To handle structural changes, use the <code>refresh</code>
|
|
1950 * methods instead.
|
|
1951 * </p>
|
|
1952 * <p>
|
|
1953 * This should be called when an element has changed in the model, in order
|
|
1954 * to have the viewer accurately reflect the model. This method only affects
|
|
1955 * the viewer, not the model.
|
|
1956 * </p>
|
|
1957 * <p>
|
|
1958 * Specifying which properties are affected may allow the viewer to optimize
|
|
1959 * the update. For example, if the label provider is not affected by changes
|
|
1960 * to any of these properties, an update may not actually be required.
|
|
1961 * Specifying <code>properties</code> as <code>null</code> forces a full
|
|
1962 * update of the given elements.
|
|
1963 * </p>
|
|
1964 * <p>
|
|
1965 * If the viewer has a sorter which is affected by a change to one of the
|
|
1966 * properties, the elements' positions are updated to maintain the sort
|
|
1967 * order. Note that resorting does not happen if <code>properties</code>
|
|
1968 * is <code>null</code>.
|
|
1969 * </p>
|
|
1970 * <p>
|
|
1971 * If the viewer has a filter which is affected by a change to one of the
|
|
1972 * properties, elements may appear or disappear if the change affects
|
|
1973 * whether or not they are filtered out.
|
|
1974 * </p>
|
|
1975 *
|
|
1976 * @param elements
|
|
1977 * the elements
|
|
1978 * @param properties
|
|
1979 * the properties that have changed, or <code>null</code> to
|
|
1980 * indicate unknown
|
|
1981 */
|
|
1982 public void update(Object[] elements, String[] properties) {
|
|
1983 for (int i = 0; i < elements.length; ++i) {
|
|
1984 update(elements[i], properties);
|
|
1985 }
|
|
1986 }
|
|
1987
|
|
1988 /**
|
|
1989 * Updates the given element's presentation when one or more of its
|
|
1990 * properties changes. Only the given element is updated.
|
|
1991 * <p>
|
|
1992 * This does not handle structural changes (e.g. addition or removal of
|
|
1993 * elements), and does not update any other related elements (e.g. child
|
|
1994 * elements). To handle structural changes, use the <code>refresh</code>
|
|
1995 * methods instead.
|
|
1996 * </p>
|
|
1997 * <p>
|
|
1998 * This should be called when an element has changed in the model, in order
|
|
1999 * to have the viewer accurately reflect the model. This method only affects
|
|
2000 * the viewer, not the model.
|
|
2001 * </p>
|
|
2002 * <p>
|
|
2003 * Specifying which properties are affected may allow the viewer to optimize
|
|
2004 * the update. For example, if the label provider is not affected by changes
|
|
2005 * to any of these properties, an update may not actually be required.
|
|
2006 * Specifying <code>properties</code> as <code>null</code> forces a full
|
|
2007 * update of the element.
|
|
2008 * </p>
|
|
2009 * <p>
|
|
2010 * If the viewer has a sorter which is affected by a change to one of the
|
|
2011 * properties, the element's position is updated to maintain the sort order.
|
|
2012 * Note that resorting does not happen if <code>properties</code> is
|
|
2013 * <code>null</code>.
|
|
2014 * </p>
|
|
2015 * <p>
|
|
2016 * If the viewer has a filter which is affected by a change to one of the
|
|
2017 * properties, the element may appear or disappear if the change affects
|
|
2018 * whether or not the element is filtered out.
|
|
2019 * </p>
|
|
2020 *
|
|
2021 * @param element
|
|
2022 * the element
|
|
2023 * @param properties
|
|
2024 * the properties that have changed, or <code>null</code> to
|
|
2025 * indicate unknown
|
|
2026 */
|
|
2027 public void update(Object element, String[] properties) {
|
|
2028 Assert.isNotNull(element);
|
|
2029 Widget[] items = findItems(element);
|
|
2030
|
|
2031 for (int i = 0; i < items.length; i++) {
|
|
2032 internalUpdate(items[i], element, properties);
|
|
2033 }
|
|
2034 }
|
|
2035
|
|
2036 /**
|
|
2037 * Updates the given element's presentation when one or more of its
|
|
2038 * properties changes. Only the given element is updated.
|
|
2039 * <p>
|
|
2040 * EXPERIMENTAL. Not to be used except by JDT.
|
|
2041 * This method was added to support JDT's explorations
|
|
2042 * into grouping by working sets, which requires viewers to support multiple
|
|
2043 * equal elements. See bug 76482 for more details. This support will
|
|
2044 * likely be removed in Eclipse 3.3 in favor of proper support for
|
|
2045 * multiple equal elements (which was implemented for AbtractTreeViewer in 3.2).
|
|
2046 * </p>
|
|
2047 * @param widget
|
|
2048 * the widget for the element
|
|
2049 * @param element
|
|
2050 * the element
|
|
2051 * @param properties
|
|
2052 * the properties that have changed, or <code>null</code> to
|
|
2053 * indicate unknown
|
|
2054 */
|
|
2055 protected void internalUpdate(Widget widget, Object element, String[] properties) {
|
|
2056 bool needsRefilter_ = false;
|
|
2057 if (properties !is null) {
|
|
2058 for (int i = 0; i < properties.length; ++i) {
|
|
2059 needsRefilter_ = needsRefilter(element, properties[i]);
|
|
2060 if (needsRefilter_) {
|
|
2061 break;
|
|
2062 }
|
|
2063 }
|
|
2064 }
|
|
2065 if (needsRefilter_) {
|
|
2066 preservingSelection(new class Runnable {
|
|
2067 public void run() {
|
|
2068 internalRefresh(getRoot());
|
|
2069 }
|
|
2070 });
|
|
2071 return;
|
|
2072 }
|
|
2073
|
|
2074 bool needsUpdate;
|
|
2075 if (properties is null) {
|
|
2076 needsUpdate = true;
|
|
2077 } else {
|
|
2078 needsUpdate = false;
|
|
2079 IBaseLabelProvider labelProvider = getLabelProvider();
|
|
2080 for (int i = 0; i < properties.length; ++i) {
|
|
2081 needsUpdate = labelProvider.isLabelProperty(element, properties[i]);
|
|
2082 if (needsUpdate) {
|
|
2083 break;
|
|
2084 }
|
|
2085 }
|
|
2086 }
|
|
2087 if (needsUpdate) {
|
|
2088 updateItem(widget, element);
|
|
2089 }
|
|
2090 }
|
|
2091
|
|
2092 /**
|
|
2093 * Copies attributes of the given element into the given widget.
|
|
2094 * <p>
|
|
2095 * This method is internal to the framework; subclassers should not call
|
|
2096 * this method. Calls <code>doUpdateItem(widget, element, true)</code>.
|
|
2097 * </p>
|
|
2098 *
|
|
2099 * @param widget
|
|
2100 * the widget
|
|
2101 * @param element
|
|
2102 * the element
|
|
2103 */
|
|
2104 protected final void updateItem(Widget widget, Object element) {
|
|
2105 SafeRunnable.run(new UpdateItemSafeRunnable(widget, element, true));
|
|
2106 }
|
|
2107
|
|
2108 /**
|
|
2109 * Updates the selection of this viewer.
|
|
2110 * <p>
|
|
2111 * This framework method should be called when the selection in the viewer
|
|
2112 * widget changes.
|
|
2113 * </p>
|
|
2114 * <p>
|
|
2115 * The default implementation of this method notifies all selection change
|
|
2116 * listeners recorded in an internal state variable. Overriding this method
|
|
2117 * is generally not required; however, if overriding in a subclass,
|
|
2118 * <code>super.updateSelection</code> must be invoked.
|
|
2119 * </p>
|
|
2120 *
|
|
2121 * @param selection
|
|
2122 * the selection, or <code>null</code> if none
|
|
2123 */
|
|
2124 protected void updateSelection(ISelection selection) {
|
|
2125 SelectionChangedEvent event = new SelectionChangedEvent(this, selection);
|
|
2126 fireSelectionChanged(event);
|
|
2127 }
|
|
2128
|
|
2129 /**
|
|
2130 * Returns whether this structured viewer is configured to use an internal
|
|
2131 * map to speed up the mapping between elements and DWT items.
|
|
2132 * <p>
|
|
2133 * The default implementation of this framework method checks whether the
|
|
2134 * internal map has been initialized.
|
|
2135 * </p>
|
|
2136 *
|
|
2137 * @return <code>true</code> if the element map is enabled, and
|
|
2138 * <code>false</code> if disabled
|
|
2139 */
|
|
2140 protected bool usingElementMap() {
|
|
2141 return elementMap !is null;
|
|
2142 }
|
|
2143
|
|
2144 /* (non-Javadoc)
|
|
2145 * @see dwtx.jface.viewers.ContentViewer#setLabelProvider(dwtx.jface.viewers.IBaseLabelProvider)
|
|
2146 */
|
|
2147 public void setLabelProvider(IBaseLabelProvider labelProvider) {
|
|
2148 if ( null !is cast(IColorProvider)labelProvider || null !is cast(IFontProvider)labelProvider ) {
|
|
2149 colorAndFontCollector = new ColorAndFontCollectorWithProviders(labelProvider);
|
|
2150 } else {
|
|
2151 colorAndFontCollector = new ColorAndFontCollector();
|
|
2152 }
|
|
2153 super.setLabelProvider(labelProvider);
|
|
2154
|
|
2155 }
|
|
2156
|
|
2157 /**
|
|
2158 * Build a label up for the element using the supplied label provider.
|
|
2159 * @param updateLabel The ViewerLabel to collect the result in
|
|
2160 * @param element The element being decorated.
|
|
2161 */
|
|
2162 protected void buildLabel(ViewerLabel updateLabel, Object element){
|
|
2163
|
|
2164 if ( auto vlp = cast(IViewerLabelProvider)getLabelProvider() ) {
|
|
2165 IViewerLabelProvider itemProvider = cast(IViewerLabelProvider) getLabelProvider();
|
|
2166 itemProvider.updateLabel(updateLabel, element);
|
|
2167
|
|
2168 colorAndFontCollector.setUsedDecorators();
|
|
2169
|
|
2170 if(updateLabel.hasNewBackground()) {
|
|
2171 colorAndFontCollector.setBackground(updateLabel.getBackground());
|
|
2172 }
|
|
2173
|
|
2174 if(updateLabel.hasNewForeground()) {
|
|
2175 colorAndFontCollector.setForeground(updateLabel.getForeground());
|
|
2176 }
|
|
2177
|
|
2178 if(updateLabel.hasNewFont()) {
|
|
2179 colorAndFontCollector.setFont(updateLabel.getFont());
|
|
2180 }
|
|
2181 return;
|
|
2182
|
|
2183 }
|
|
2184
|
|
2185 if( cast(ILabelProvider) getLabelProvider() ){
|
|
2186 ILabelProvider labelProvider = cast(ILabelProvider) getLabelProvider();
|
|
2187 updateLabel.setText(labelProvider.getText(element));
|
|
2188 updateLabel.setImage(labelProvider.getImage(element));
|
|
2189 }
|
|
2190
|
|
2191 }
|
|
2192
|
|
2193 /**
|
|
2194 * Build a label up for the element using the supplied label provider.
|
|
2195 * @param updateLabel The ViewerLabel to collect the result in
|
|
2196 * @param element The element being decorated.
|
|
2197 * @param labelProvider ILabelProvider the labelProvider for the receiver.
|
|
2198 */
|
|
2199 void buildLabel(ViewerLabel updateLabel, Object element, IViewerLabelProvider labelProvider){
|
|
2200
|
|
2201 labelProvider.updateLabel(updateLabel, element);
|
|
2202
|
|
2203 colorAndFontCollector.setUsedDecorators();
|
|
2204
|
|
2205 if(updateLabel.hasNewBackground()) {
|
|
2206 colorAndFontCollector.setBackground(updateLabel.getBackground());
|
|
2207 }
|
|
2208
|
|
2209 if(updateLabel.hasNewForeground()) {
|
|
2210 colorAndFontCollector.setForeground(updateLabel.getForeground());
|
|
2211 }
|
|
2212
|
|
2213 if(updateLabel.hasNewFont()) {
|
|
2214 colorAndFontCollector.setFont(updateLabel.getFont());
|
|
2215 }
|
|
2216
|
|
2217 }
|
|
2218
|
|
2219 /**
|
|
2220 * Build a label up for the element using the supplied label provider.
|
|
2221 * @param updateLabel The ViewerLabel to collect the result in
|
|
2222 * @param elementPath The path of the element being decorated.
|
|
2223 * @param labelProvider ILabelProvider the labelProvider for the receiver.
|
|
2224 */
|
|
2225 void buildLabel(ViewerLabel updateLabel, TreePath elementPath,ITreePathLabelProvider labelProvider){
|
|
2226
|
|
2227 labelProvider.updateLabel(updateLabel, elementPath);
|
|
2228
|
|
2229 colorAndFontCollector.setUsedDecorators();
|
|
2230
|
|
2231 if(updateLabel.hasNewBackground()) {
|
|
2232 colorAndFontCollector.setBackground(updateLabel.getBackground());
|
|
2233 }
|
|
2234
|
|
2235 if(updateLabel.hasNewForeground()) {
|
|
2236 colorAndFontCollector.setForeground(updateLabel.getForeground());
|
|
2237 }
|
|
2238
|
|
2239 if(updateLabel.hasNewFont()) {
|
|
2240 colorAndFontCollector.setFont(updateLabel.getFont());
|
|
2241 }
|
|
2242
|
|
2243 }
|
|
2244
|
|
2245 /**
|
|
2246 * Build a label up for the element using the supplied label provider.
|
|
2247 * @param updateLabel The ViewerLabel to collect the result in
|
|
2248 * @param element The element being decorated.
|
|
2249 * @param labelProvider ILabelProvider the labelProvider for the receiver.
|
|
2250 */
|
|
2251 void buildLabel(ViewerLabel updateLabel, Object element,ILabelProvider labelProvider){
|
|
2252 updateLabel.setText(labelProvider.getText(element));
|
|
2253 updateLabel.setImage(labelProvider.getImage(element));
|
|
2254 }
|
|
2255
|
|
2256 /**
|
|
2257 * Get the ColorAndFontCollector for the receiver.
|
|
2258 * @return ColorAndFontCollector
|
|
2259 * @since 3.1
|
|
2260 */
|
|
2261 protected ColorAndFontCollector getColorAndFontCollector() {
|
|
2262 return colorAndFontCollector;
|
|
2263 }
|
|
2264
|
|
2265 protected void handleDispose(DisposeEvent event) {
|
|
2266 super.handleDispose(event);
|
|
2267 sorter = null;
|
|
2268 comparer = null;
|
|
2269 if (filters !is null)
|
|
2270 filters.clear();
|
|
2271 elementMap = newHashtable(1);
|
|
2272 openListeners.clear();
|
|
2273 doubleClickListeners.clear();
|
|
2274 colorAndFontCollector.clear();
|
|
2275 postSelectionChangedListeners.clear();
|
|
2276 }
|
|
2277
|
|
2278 }
|