Mercurial > projects > dwt-addons
comparison dwtx/jface/viewers/StructuredViewer.d @ 10:b6c35faf97c8
Viewers
author | Frank Benoit <benoit@tionex.de> |
---|---|
date | Mon, 31 Mar 2008 00:47:19 +0200 |
parents | |
children | 644f1334b451 |
comparison
equal
deleted
inserted
replaced
9:6c14e54dfc11 | 10:b6c35faf97c8 |
---|---|
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) { | |
832 SafeRunnable.run(new class SafeRunnable { | |
833 IDoubleClickListener l; | |
834 this(){ | |
835 l = cast(IDoubleClickListener) listeners[i]; | |
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) { | |
859 SafeRunnable.run(new class SafeRunnable { | |
860 IOpenListener l; | |
861 this(){ | |
862 l = cast(IOpenListener) listeners[i]; | |
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) { | |
887 SafeRunnable.run(new class SafeRunnable { | |
888 ISelectionChangedListener l; | |
889 this(){ | |
890 l = cast(ISelectionChangedListener) listeners[i]; | |
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) { | |
1490 preservingSelection(new class Runnable { | |
1491 Object element_; | |
1492 this(){ | |
1493 element_ = element; | |
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 } |