Mercurial > projects > dwt-addons
annotate dwtx/jface/viewers/AbstractTreeViewer.d @ 54:a6683645b0d7
Fix ignored labelprovider for TreeViewer (See WrapperViewerLabelProvider.d), removed the debugging prints
author | Frank Benoit <benoit@tionex.de> |
---|---|
date | Sat, 12 Apr 2008 17:53:06 +0200 |
parents | 28f6c339768e |
children | 46a6e0e6ccd4 |
rev | line source |
---|---|
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 <tom.schindl@bestsolution.at> - bug 153993, bug 167323, bug 175192 | |
11 * Port to the D programming language: | |
12 * Frank Benoit <benoit@tionex.de> | |
13 *******************************************************************************/ | |
14 | |
15 module dwtx.jface.viewers.AbstractTreeViewer; | |
16 | |
17 import dwtx.jface.viewers.ColumnViewer; | |
18 import dwtx.jface.viewers.TreePath; | |
19 import dwtx.jface.viewers.ViewerComparator; | |
20 import dwtx.jface.viewers.ITreeViewerListener; | |
21 import dwtx.jface.viewers.TreeExpansionEvent; | |
22 import dwtx.jface.viewers.CustomHashtable; | |
23 import dwtx.jface.viewers.IContentProvider; | |
24 import dwtx.jface.viewers.ISelection; | |
25 import dwtx.jface.viewers.ColumnViewerEditor; | |
26 import dwtx.jface.viewers.ViewerLabel; | |
27 import dwtx.jface.viewers.ViewerRow; | |
28 import dwtx.jface.viewers.TreePathViewerSorter; | |
29 import dwtx.jface.viewers.ViewerFilter; | |
30 import dwtx.jface.viewers.ViewerColumn; | |
31 import dwtx.jface.viewers.ViewerCell; | |
32 import dwtx.jface.viewers.ITreePathContentProvider; | |
33 import dwtx.jface.viewers.ITreeContentProvider; | |
34 import dwtx.jface.viewers.TreeSelection; | |
35 import dwtx.jface.viewers.DoubleClickEvent; | |
36 import dwtx.jface.viewers.IElementComparer; | |
37 import dwtx.jface.viewers.ITreeSelection; | |
38 import dwtx.jface.viewers.IBaseLabelProvider; | |
39 import dwtx.jface.viewers.ITreePathLabelProvider; | |
40 | |
41 import tango.util.collection.LinkSeq; | |
42 import tango.util.collection.ArraySeq; | |
43 import tango.util.collection.model.Seq; | |
44 import tango.util.collection.model.SeqView; | |
45 | |
46 | |
47 import dwt.DWT; | |
48 import dwt.custom.BusyIndicator; | |
49 import dwt.events.SelectionEvent; | |
50 import dwt.events.SelectionListener; | |
51 import dwt.events.TreeEvent; | |
52 import dwt.events.TreeListener; | |
53 import dwt.graphics.Point; | |
54 import dwt.widgets.Control; | |
55 import dwt.widgets.Item; | |
56 import dwt.widgets.Widget; | |
57 import dwtx.core.runtime.Assert; | |
58 import dwtx.core.runtime.ListenerList; | |
59 import dwtx.jface.util.SafeRunnable; | |
60 | |
61 import dwt.dwthelper.utils; | |
62 import dwt.dwthelper.Runnable; | |
63 | |
64 /** | |
65 * Abstract base implementation for tree-structure-oriented viewers (trees and | |
66 * table trees). | |
67 * <p> | |
68 * Nodes in the tree can be in either an expanded or a collapsed state, | |
69 * depending on whether the children on a node are visible. This class | |
70 * introduces public methods for controlling the expanding and collapsing of | |
71 * nodes. | |
72 * </p> | |
73 * <p> | |
74 * As of 3.2, AbstractTreeViewer supports multiple equal elements (each with a | |
75 * different parent chain) in the tree. This support requires that clients | |
76 * enable the element map by calling <code>setUseHashLookup(true)</code>. | |
77 * </p> | |
78 * <p> | |
79 * Content providers for abstract tree viewers must implement one of the | |
80 * interfaces <code>ITreeContentProvider</code> or (as of 3.2, to support | |
81 * multiple equal elements) <code>ITreePathContentProvider</code>. | |
82 * </p> | |
83 * | |
84 * @see TreeViewer | |
85 */ | |
86 public abstract class AbstractTreeViewer : ColumnViewer { | |
87 alias ColumnViewer.buildLabel buildLabel; | |
43
ea8ff534f622
Fix override and super aliases
Frank Benoit <benoit@tionex.de>
parents:
40
diff
changeset
|
88 alias ColumnViewer.filter filter; |
10 | 89 alias ColumnViewer.setSelection setSelection; |
90 | |
91 /** | |
92 * Constant indicating that all levels of the tree should be expanded or | |
93 * collapsed. | |
94 * | |
95 * @see #expandToLevel(int) | |
96 * @see #collapseToLevel(Object, int) | |
97 */ | |
98 public static const int ALL_LEVELS = -1; | |
99 | |
100 /** | |
101 * List of registered tree listeners (element type: | |
102 * <code>TreeListener</code>). | |
103 */ | |
104 private ListenerList treeListeners; | |
105 | |
106 /** | |
107 * The level to which the tree is automatically expanded each time the | |
108 * viewer's input is changed (that is, by <code>setInput</code>). A value | |
109 * of 0 means that auto-expand is off. | |
110 * | |
111 * @see #setAutoExpandLevel | |
112 */ | |
113 private int expandToLevel_ = 0; | |
114 | |
115 /** | |
116 * Safe runnable used to update an item. | |
117 */ | |
118 class UpdateItemSafeRunnable : SafeRunnable { | |
119 private Object element; | |
120 | |
121 private Item item; | |
122 | |
123 this(Item item, Object element) { | |
124 this.item = item; | |
125 this.element = element; | |
126 } | |
127 | |
128 public void run() { | |
129 doUpdateItem(item, element); | |
130 } | |
131 | |
132 } | |
133 | |
134 /** | |
135 * Creates an abstract tree viewer. The viewer has no input, no content | |
136 * provider, a default label provider, no sorter, no filters, and has | |
137 * auto-expand turned off. | |
138 */ | |
139 protected this() { | |
140 treeListeners = new ListenerList(); | |
141 // do nothing | |
142 } | |
143 | |
144 /** | |
145 * Adds the given child elements to this viewer as children of the given | |
146 * parent element. If this viewer does not have a sorter, the elements are | |
147 * added at the end of the parent's list of children in the order given; | |
148 * otherwise, the elements are inserted at the appropriate positions. | |
149 * <p> | |
150 * This method should be called (by the content provider) when elements have | |
151 * been added to the model, in order to cause the viewer to accurately | |
152 * reflect the model. This method only affects the viewer, not the model. | |
153 * </p> | |
154 * | |
155 * @param parentElementOrTreePath | |
156 * the parent element | |
157 * @param childElements | |
158 * the child elements to add | |
159 */ | |
160 public void add(Object parentElementOrTreePath, Object[] childElements) { | |
161 Assert.isNotNull(parentElementOrTreePath); | |
162 assertElementsNotNull(childElements); | |
163 if (isBusy()) | |
164 return; | |
165 Widget[] widgets = internalFindItems(parentElementOrTreePath); | |
166 // If parent hasn't been realized yet, just ignore the add. | |
167 if (widgets.length is 0) { | |
168 return; | |
169 } | |
170 | |
171 for (int i = 0; i < widgets.length; i++) { | |
172 internalAdd(widgets[i], parentElementOrTreePath, childElements); | |
173 } | |
174 } | |
175 | |
176 /** | |
177 * Find the items for the given element of tree path | |
178 * | |
179 * @param parentElementOrTreePath | |
180 * the element or tree path | |
181 * @return the items for that element | |
182 * | |
183 * @since 3.3 | |
184 */ | |
185 final protected Widget[] internalFindItems(Object parentElementOrTreePath) { | |
186 Widget[] widgets; | |
187 if ( auto path = cast(TreePath) parentElementOrTreePath ) { | |
188 Widget w = internalFindItem(path); | |
189 if (w is null) { | |
190 widgets = null; | |
191 } else { | |
192 widgets = [ w ]; | |
193 } | |
194 } else { | |
195 widgets = findItems(parentElementOrTreePath); | |
196 } | |
197 return widgets; | |
198 } | |
199 | |
200 /** | |
201 * Return the item at the given path or <code>null</code> | |
202 * | |
203 * @param path | |
204 * the path | |
205 * @return {@link Widget} the item at that path | |
206 */ | |
207 private Widget internalFindItem(TreePath path) { | |
208 Widget[] widgets = findItems(path.getLastSegment()); | |
209 for (int i = 0; i < widgets.length; i++) { | |
210 Widget widget = widgets[i]; | |
211 if ( auto item = cast(Item)widget ) { | |
212 TreePath p = getTreePathFromItem(item); | |
213 if (p.opEquals(path)) { | |
214 return widget; | |
215 } | |
216 } | |
217 } | |
218 return null; | |
219 } | |
220 | |
221 /** | |
222 * Adds the given child elements to this viewer as children of the given | |
223 * parent element. | |
224 * <p> | |
225 * EXPERIMENTAL. Not to be used except by JDT. This method was added to | |
226 * support JDT's explorations into grouping by working sets, which requires | |
227 * viewers to support multiple equal elements. See bug 76482 for more | |
228 * details. This support will likely be removed in Eclipse 3.2 in favor of | |
229 * proper support for multiple equal elements. | |
230 * </p> | |
231 * | |
232 * @param widget | |
233 * the widget for the parent element | |
234 * @param parentElementOrTreePath | |
235 * the parent element | |
236 * @param childElements | |
237 * the child elements to add | |
238 * @since 3.1 | |
239 */ | |
240 protected void internalAdd(Widget widget, Object parentElementOrTreePath, | |
241 Object[] childElements) { | |
242 Object parent; | |
243 TreePath path; | |
244 if ( auto path = cast(TreePath) parentElementOrTreePath ) { | |
245 parent = path.getLastSegment(); | |
246 } else { | |
247 parent = parentElementOrTreePath; | |
248 path = null; | |
249 } | |
250 | |
251 // optimization! | |
252 // if the widget is not expanded we just invalidate the subtree | |
253 if ( auto ti = cast(Item)widget ) { | |
254 if (!getExpanded(ti)) { | |
255 bool needDummy = isExpandable(ti, path, parent); | |
256 bool haveDummy = false; | |
257 // remove all children | |
258 Item[] items = getItems(ti); | |
259 for (int i = 0; i < items.length; i++) { | |
260 if (items[i].getData() !is null) { | |
261 disassociate(items[i]); | |
262 items[i].dispose(); | |
263 } else { | |
264 if (needDummy && !haveDummy) { | |
265 haveDummy = true; | |
266 } else { | |
267 items[i].dispose(); | |
268 } | |
269 } | |
270 } | |
271 // append a dummy if necessary | |
272 if (needDummy && !haveDummy) { | |
273 newItem(ti, DWT.NULL, -1); | |
274 } | |
275 return; | |
276 } | |
277 } | |
278 | |
279 if (childElements.length > 0) { | |
280 // TODO: Add filtering back? | |
281 Object[] filtered = filter(parentElementOrTreePath, childElements); | |
282 ViewerComparator comparator = getComparator(); | |
283 if (comparator !is null) { | |
284 if ( auto tpvs = cast(TreePathViewerSorter) comparator ) { | |
285 if (path is null) { | |
286 path = internalGetSorterParentPath(widget, comparator); | |
287 } | |
288 tpvs.sort(this, path, filtered); | |
289 } else { | |
290 comparator.sort(this, filtered); | |
291 } | |
292 } | |
293 createAddedElements(widget, filtered); | |
294 } | |
295 } | |
296 | |
297 /** | |
298 * Filter the children elements. | |
299 * | |
300 * @param parentElementOrTreePath | |
301 * the parent element or path | |
302 * @param elements | |
303 * the child elements | |
304 * @return the filter list of children | |
305 */ | |
306 private Object[] filter(Object parentElementOrTreePath, Object[] elements) { | |
307 ViewerFilter[] filters = getFilters(); | |
308 if (filters !is null) { | |
309 auto filtered = new ArraySeq!(Object); | |
310 filtered.capacity(elements.length); | |
311 for (int i = 0; i < elements.length; i++) { | |
312 bool add = true; | |
313 for (int j = 0; j < filters.length; j++) { | |
314 add = filters[j].select(this, parentElementOrTreePath, | |
315 elements[i]); | |
316 if (!add) { | |
317 break; | |
318 } | |
319 } | |
320 if (add) { | |
321 filtered.append(elements[i]); | |
322 } | |
323 } | |
324 return filtered.toArray(); | |
325 } | |
326 return elements; | |
327 } | |
328 | |
329 /** | |
330 * Create the new elements in the parent widget. If the child already exists | |
331 * do nothing. | |
332 * | |
333 * @param widget | |
334 * @param elements | |
335 * Sorted list of elements to add. | |
336 */ | |
337 private void createAddedElements(Widget widget, Object[] elements) { | |
338 | |
339 if (elements.length is 1) { | |
340 if (opEquals(elements[0], widget.getData())) { | |
341 return; | |
342 } | |
343 } | |
344 | |
345 ViewerComparator comparator = getComparator(); | |
346 TreePath parentPath = internalGetSorterParentPath(widget, comparator); | |
347 Item[] items = getChildren(widget); | |
348 | |
349 // As the items are sorted already we optimize for a | |
350 // start position | |
351 int lastInsertion = 0; | |
352 | |
353 // Optimize for the empty case | |
354 if (items.length is 0) { | |
355 for (int i = 0; i < elements.length; i++) { | |
356 createTreeItem(widget, elements[i], -1); | |
357 } | |
358 return; | |
359 } | |
360 | |
361 for (int i = 0; i < elements.length; i++) { | |
362 bool newItem = true; | |
363 Object element = elements[i]; | |
364 int index; | |
365 if (comparator is null) { | |
366 if (itemExists(items, element)) { | |
367 internalRefresh(element); | |
368 newItem = false; | |
369 } | |
370 index = -1; | |
371 } else { | |
372 lastInsertion = insertionPosition(items, comparator, | |
373 lastInsertion, element, parentPath); | |
374 // As we are only searching the original array we keep track of | |
375 // those positions only | |
376 if (lastInsertion is items.length) { | |
377 index = -1; | |
378 } else {// See if we should just refresh | |
379 while (lastInsertion < items.length | |
380 && internalCompare(comparator, parentPath, element, | |
381 items[lastInsertion].getData()) is 0) { | |
382 // As we cannot assume the sorter is consistent with | |
383 // equals() - therefore we can | |
384 // just check against the item prior to this index (if | |
385 // any) | |
386 if (items[lastInsertion].getData().opEquals(element)) { | |
387 // refresh the element in case it has new children | |
388 internalRefresh(element); | |
389 newItem = false; | |
390 } | |
391 lastInsertion++;// We had an insertion so increment | |
392 } | |
393 // Did we get to the end? | |
394 if (lastInsertion is items.length) { | |
395 index = -1; | |
396 } else { | |
397 index = lastInsertion + i; // Add the index as the | |
398 // array is growing | |
399 } | |
400 } | |
401 } | |
402 if (newItem) { | |
403 createTreeItem(widget, element, index); | |
404 } | |
405 } | |
406 } | |
407 | |
408 /** | |
409 * See if element is the data of one of the elements in items. | |
410 * | |
411 * @param items | |
412 * @param element | |
413 * @return <code>true</code> if the element matches. | |
414 */ | |
415 private bool itemExists(Item[] items, Object element) { | |
416 if (usingElementMap()) { | |
417 Widget[] existingItems = findItems(element); | |
418 // optimization for two common cases | |
419 if (existingItems.length is 0) { | |
420 return false; | |
421 } else if (existingItems.length is 1) { | |
422 if (items.length > 0 && null !is cast(Item)existingItems[0] ) { | |
423 Item existingItem = cast(Item) existingItems[0]; | |
424 return getParentItem(existingItem) is getParentItem(items[0]); | |
425 } | |
426 } | |
427 } | |
428 for (int i = 0; i < items.length; i++) { | |
429 if (items[i].getData().opEquals(element)) { | |
430 return true; | |
431 } | |
432 } | |
433 return false; | |
434 } | |
435 | |
436 /** | |
437 * Returns the index where the item should be inserted. It uses sorter to | |
438 * determine the correct position, if sorter is not assigned, returns the | |
439 * index of the element after the last. | |
440 * | |
441 * @param items | |
442 * the items to search | |
443 * @param comparator | |
444 * The comparator to use. | |
445 * @param lastInsertion | |
446 * the start index to start search for position from this allows | |
447 * optimizing search for multiple elements that are sorted | |
448 * themselves. | |
449 * @param element | |
450 * element to find position for. | |
451 * @param parentPath | |
452 * the tree path for the element's parent or <code>null</code> | |
453 * if the element is a root element or the sorter is not a | |
454 * {@link TreePathViewerSorter} | |
455 * @return the index to use when inserting the element. | |
456 * | |
457 */ | |
458 | |
459 private int insertionPosition(Item[] items, ViewerComparator comparator, | |
460 int lastInsertion, Object element, TreePath parentPath) { | |
461 | |
462 int size = items.length; | |
463 if (comparator is null) { | |
464 return size; | |
465 } | |
466 int min = lastInsertion, max = size - 1; | |
467 | |
468 while (min <= max) { | |
469 int mid = (min + max) / 2; | |
470 Object data = items[mid].getData(); | |
471 int compare = internalCompare(comparator, parentPath, data, element); | |
472 if (compare is 0) { | |
473 return mid;// Return if we already match | |
474 } | |
475 if (compare < 0) { | |
476 min = mid + 1; | |
477 } else { | |
478 max = mid - 1; | |
479 } | |
480 } | |
481 return min; | |
482 | |
483 } | |
484 | |
485 /** | |
486 * Returns the index where the item should be inserted. It uses sorter to | |
487 * determine the correct position, if sorter is not assigned, returns the | |
488 * index of the element after the last. | |
489 * | |
490 * @param parent | |
491 * The parent widget | |
492 * @param sorter | |
493 * The sorter to use. | |
494 * @param startIndex | |
495 * the start index to start search for position from this allows | |
496 * optimizing search for multiple elements that are sorted | |
497 * themselves. | |
498 * @param element | |
499 * element to find position for. | |
500 * @param currentSize | |
501 * the current size of the collection | |
502 * @return the index to use when inserting the element. | |
503 * | |
504 */ | |
505 | |
506 /** | |
507 * Returns the index where the item should be inserted. | |
508 * | |
509 * @param parent | |
510 * The parent widget the element will be inserted into. | |
511 * @param element | |
512 * The element to insert. | |
513 * @return the index of the element | |
514 */ | |
515 protected int indexForElement(Widget parent, Object element) { | |
516 ViewerComparator comparator = getComparator(); | |
517 TreePath parentPath = internalGetSorterParentPath(parent, comparator); | |
518 | |
519 Item[] items = getChildren(parent); | |
520 int count = items.length; | |
521 | |
522 if (comparator is null) { | |
523 return count; | |
524 } | |
525 int min = 0, max = count - 1; | |
526 | |
527 while (min <= max) { | |
528 int mid = (min + max) / 2; | |
529 Object data = items[mid].getData(); | |
530 int compare = internalCompare(comparator, parentPath, data, element); | |
531 if (compare is 0) { | |
532 // find first item > element | |
533 while (compare is 0) { | |
534 ++mid; | |
535 if (mid >= count) { | |
536 break; | |
537 } | |
538 data = items[mid].getData(); | |
539 compare = internalCompare(comparator, parentPath, data, | |
540 element); | |
541 } | |
542 return mid; | |
543 } | |
544 if (compare < 0) { | |
545 min = mid + 1; | |
546 } else { | |
547 max = mid - 1; | |
548 } | |
549 } | |
550 return min; | |
551 } | |
552 | |
553 /** | |
554 * Return the tree path that should be used as the parent path for the given | |
555 * widget and sorter. A <code>null</code> is returned if either the sorter | |
556 * is not a {@link TreePathViewerSorter} or if the parent widget is not an | |
557 * {@link Item} (i.e. is the root of the tree). | |
558 * | |
559 * @param parent | |
560 * the parent widget | |
561 * @param comparator | |
562 * the sorter | |
563 * @return the tree path that should be used as the parent path for the | |
564 * given widget and sorter | |
565 */ | |
566 private TreePath internalGetSorterParentPath(Widget parent, | |
567 ViewerComparator comparator) { | |
568 TreePath path; | |
569 if ( null !is cast(TreePathViewerSorter)comparator | |
570 && null !is cast(Item)parent ) { | |
571 Item item = cast(Item) parent; | |
572 path = getTreePathFromItem(item); | |
573 } else { | |
574 path = null; | |
575 } | |
576 return path; | |
577 } | |
578 | |
579 /** | |
580 * Compare the two elements using the given sorter. If the sorter is a | |
581 * {@link TreePathViewerSorter}, the provided tree path will be used. If | |
582 * the tree path is null and the sorter is a tree path sorter, then the | |
583 * elements are root elements | |
584 * | |
585 * @param comparator | |
586 * the sorter | |
587 * @param parentPath | |
588 * the path of the elements' parent | |
589 * @param e1 | |
590 * the first element | |
591 * @param e2 | |
592 * the second element | |
593 * @return the result of comparing the two elements | |
594 */ | |
595 private int internalCompare(ViewerComparator comparator, | |
596 TreePath parentPath, Object e1, Object e2) { | |
597 if ( auto tpvs = cast(TreePathViewerSorter) comparator ) { | |
598 return tpvs.compare(this, parentPath, e1, e2); | |
599 } | |
600 return comparator.compare(this, e1, e2); | |
601 } | |
602 | |
603 /* | |
604 * (non-Javadoc) | |
605 * | |
606 * @see dwtx.jface.viewers.StructuredViewer#getSortedChildren(java.lang.Object) | |
607 */ | |
43
ea8ff534f622
Fix override and super aliases
Frank Benoit <benoit@tionex.de>
parents:
40
diff
changeset
|
608 protected override Object[] getSortedChildren(Object parentElementOrTreePath) { |
10 | 609 Object[] result = getFilteredChildren(parentElementOrTreePath); |
610 ViewerComparator comparator = getComparator(); | |
611 if (parentElementOrTreePath !is null | |
612 && null !is cast(TreePathViewerSorter) comparator ) { | |
613 TreePathViewerSorter tpvs = cast(TreePathViewerSorter) comparator; | |
614 | |
615 // be sure we're not modifying the original array from the model | |
616 result = result.dup; | |
617 | |
618 TreePath path = null; | |
619 if ( auto p = cast(TreePath) parentElementOrTreePath ) { | |
620 path = p; | |
621 } else { | |
622 Object parent = parentElementOrTreePath; | |
623 Widget w = internalGetWidgetToSelect(parent); | |
624 if (w !is null) { | |
625 path = internalGetSorterParentPath(w, comparator); | |
626 } | |
627 } | |
628 tpvs.sort(this, path, result); | |
629 } else if (comparator !is null) { | |
630 // be sure we're not modifying the original array from the model | |
631 result = result.dup; | |
632 comparator.sort(this, result); | |
633 } | |
634 return result; | |
635 } | |
636 | |
637 /* | |
638 * (non-Javadoc) | |
639 * | |
640 * @see dwtx.jface.viewers.StructuredViewer#getFilteredChildren(java.lang.Object) | |
641 */ | |
43
ea8ff534f622
Fix override and super aliases
Frank Benoit <benoit@tionex.de>
parents:
40
diff
changeset
|
642 protected override Object[] getFilteredChildren(Object parentElementOrTreePath) { |
10 | 643 Object[] result = getRawChildren(parentElementOrTreePath); |
644 ViewerFilter[] filters = getFilters(); | |
645 for (int i = 0; i < filters.length; i++) { | |
646 ViewerFilter filter = filters[i]; | |
647 result = filter.filter(this, parentElementOrTreePath, result); | |
648 } | |
649 return result; | |
650 } | |
651 | |
652 /** | |
653 * Adds the given child element to this viewer as a child of the given | |
654 * parent element. If this viewer does not have a sorter, the element is | |
655 * added at the end of the parent's list of children; otherwise, the element | |
656 * is inserted at the appropriate position. | |
657 * <p> | |
658 * This method should be called (by the content provider) when a single | |
659 * element has been added to the model, in order to cause the viewer to | |
660 * accurately reflect the model. This method only affects the viewer, not | |
661 * the model. Note that there is another method for efficiently processing | |
662 * the simultaneous addition of multiple elements. | |
663 * </p> | |
664 * | |
665 * @param parentElementOrTreePath | |
666 * the parent element or path | |
667 * @param childElement | |
668 * the child element | |
669 */ | |
670 public void add(Object parentElementOrTreePath, Object childElement) { | |
671 add(parentElementOrTreePath, [ childElement ]); | |
672 } | |
673 | |
674 /** | |
675 * Adds the given DWT selection listener to the given DWT control. | |
676 * | |
677 * @param control | |
678 * the DWT control | |
679 * @param listener | |
680 * the DWT selection listener | |
681 * @deprecated | |
682 */ | |
683 protected void addSelectionListener(Control control, | |
684 SelectionListener listener) { | |
685 // do nothing | |
686 } | |
687 | |
688 /** | |
689 * Adds a listener for expand and collapse events in this viewer. Has no | |
690 * effect if an identical listener is already registered. | |
691 * | |
692 * @param listener | |
693 * a tree viewer listener | |
694 */ | |
695 public void addTreeListener(ITreeViewerListener listener) { | |
696 treeListeners.add(cast(Object)listener); | |
697 } | |
698 | |
699 /** | |
700 * Adds the given DWT tree listener to the given DWT control. | |
701 * | |
702 * @param control | |
703 * the DWT control | |
704 * @param listener | |
705 * the DWT tree listener | |
706 */ | |
707 protected abstract void addTreeListener(Control control, | |
708 TreeListener listener); | |
709 | |
710 /* | |
711 * (non-Javadoc) | |
712 * | |
713 * @see StructuredViewer#associate(Object, Item) | |
714 */ | |
43
ea8ff534f622
Fix override and super aliases
Frank Benoit <benoit@tionex.de>
parents:
40
diff
changeset
|
715 protected override void associate(Object element, Item item) { |
10 | 716 Object data = item.getData(); |
717 if (data !is null && data !is element && opEquals(data, element)) { | |
718 // workaround for PR 1FV62BT | |
719 // assumption: elements are equal but not identical | |
720 // -> remove from map but don't touch children | |
721 unmapElement(data, item); | |
722 item.setData(element); | |
723 mapElement(element, item); | |
724 } else { | |
725 // recursively disassociate all | |
726 super.associate(element, item); | |
727 } | |
728 } | |
729 | |
730 /** | |
731 * Collapses all nodes of the viewer's tree, starting with the root. This | |
732 * method is equivalent to <code>collapseToLevel(ALL_LEVELS)</code>. | |
733 */ | |
734 public void collapseAll() { | |
735 Object root = getRoot(); | |
736 if (root !is null) { | |
737 collapseToLevel(root, ALL_LEVELS); | |
738 } | |
739 } | |
740 | |
741 /** | |
742 * Collapses the subtree rooted at the given element or tree path to the | |
743 * given level. | |
744 * | |
745 * @param elementOrTreePath | |
746 * the element or tree path | |
747 * @param level | |
748 * non-negative level, or <code>ALL_LEVELS</code> to collapse | |
749 * all levels of the tree | |
750 */ | |
751 public void collapseToLevel(Object elementOrTreePath, int level) { | |
752 Assert.isNotNull(elementOrTreePath); | |
753 Widget w = internalGetWidgetToSelect(elementOrTreePath); | |
754 if (w !is null) { | |
755 internalCollapseToLevel(w, level); | |
756 } | |
757 } | |
758 | |
759 /** | |
760 * Creates all children for the given widget. | |
761 * <p> | |
762 * The default implementation of this framework method assumes that | |
763 * <code>widget.getData()</code> returns the element corresponding to the | |
764 * node. Note: the node is not visually expanded! You may have to call | |
765 * <code>parent.setExpanded(true)</code>. | |
766 * </p> | |
767 * | |
768 * @param widget | |
769 * the widget | |
770 */ | |
771 protected void createChildren(Widget widget) { | |
772 bool oldBusy = busy; | |
773 busy = true; | |
774 try { | |
54
a6683645b0d7
Fix ignored labelprovider for TreeViewer (See WrapperViewerLabelProvider.d), removed the debugging prints
Frank Benoit <benoit@tionex.de>
parents:
53
diff
changeset
|
775 Item[] tis = getChildren(widget); |
10 | 776 if (tis !is null && tis.length > 0) { |
777 Object data = tis[0].getData(); | |
778 if (data !is null) { | |
779 return; // children already there! | |
780 } | |
781 } | |
782 | |
39 | 783 BusyIndicator.showWhile(widget.getDisplay(), new class(widget,tis) Runnable { |
10 | 784 Widget widget_; |
785 Item[] tis_; | |
39 | 786 this( Widget a, Item[] b){ |
787 widget_ = a; | |
788 tis_=b; | |
10 | 789 } |
790 public void run() { | |
791 // fix for PR 1FW89L7: | |
792 // don't complain and remove all "dummies" ... | |
793 if (tis_ !is null) { | |
794 for (int i = 0; i < tis_.length; i++) { | |
795 if (tis_[i].getData() !is null) { | |
796 disassociate(tis_[i]); | |
797 Assert.isTrue(tis_[i].getData() is null, | |
798 "Second or later child is non -null");//$NON-NLS-1$ | |
799 | |
800 } | |
801 tis_[i].dispose(); | |
802 } | |
803 } | |
804 Object d = widget_.getData(); | |
805 if (d !is null) { | |
806 Object parentElement = d; | |
807 Object[] children; | |
39 | 808 if (isTreePathContentProvider() && (null !is cast(Item)widget_) ) { |
10 | 809 TreePath path = getTreePathFromItem(cast(Item) widget_); |
810 children = getSortedChildren(path); | |
811 } else { | |
812 children = getSortedChildren(parentElement); | |
813 } | |
814 for (int i = 0; i < children.length; i++) { | |
815 createTreeItem(widget_, children[i], -1); | |
816 } | |
817 } | |
818 } | |
819 | |
820 }); | |
821 } finally { | |
822 busy = oldBusy; | |
823 } | |
824 } | |
825 | |
826 /** | |
827 * Creates a single item for the given parent and synchronizes it with the | |
828 * given element. | |
829 * | |
830 * @param parent | |
831 * the parent widget | |
832 * @param element | |
833 * the element | |
834 * @param index | |
835 * if non-negative, indicates the position to insert the item | |
836 * into its parent | |
837 */ | |
838 protected void createTreeItem(Widget parent, Object element, int index) { | |
839 Item item = newItem(parent, DWT.NULL, index); | |
840 updateItem(item, element); | |
841 updatePlus(item, element); | |
842 } | |
843 | |
844 /** | |
845 * The <code>AbstractTreeViewer</code> implementation of this method also | |
846 * recurses over children of the corresponding element. | |
847 */ | |
43
ea8ff534f622
Fix override and super aliases
Frank Benoit <benoit@tionex.de>
parents:
40
diff
changeset
|
848 protected override void disassociate(Item item) { |
10 | 849 super.disassociate(item); |
850 // recursively unmapping the items is only required when | |
851 // the hash map is used. In the other case disposing | |
852 // an item will recursively dispose its children. | |
853 if (usingElementMap()) { | |
854 disassociateChildren(item); | |
855 } | |
856 } | |
857 | |
858 /** | |
859 * Disassociates the children of the given DWT item from their corresponding | |
860 * elements. | |
861 * | |
862 * @param item | |
863 * the widget | |
864 */ | |
865 private void disassociateChildren(Item item) { | |
866 Item[] items = getChildren(item); | |
867 for (int i = 0; i < items.length; i++) { | |
868 if (items[i].getData() !is null) { | |
869 disassociate(items[i]); | |
870 } | |
871 } | |
872 } | |
873 | |
874 /* (non-Javadoc) Method declared on StructuredViewer. */ | |
43
ea8ff534f622
Fix override and super aliases
Frank Benoit <benoit@tionex.de>
parents:
40
diff
changeset
|
875 protected override Widget doFindInputItem(Object element) { |
10 | 876 // compare with root |
877 Object root = getRoot(); | |
878 if (root is null) { | |
879 return null; | |
880 } | |
881 | |
882 if (opEquals(root, element)) { | |
883 return getControl(); | |
884 } | |
885 return null; | |
886 } | |
887 | |
888 /* (non-Javadoc) Method declared on StructuredViewer. */ | |
43
ea8ff534f622
Fix override and super aliases
Frank Benoit <benoit@tionex.de>
parents:
40
diff
changeset
|
889 protected override Widget doFindItem(Object element) { |
10 | 890 // compare with root |
891 Object root = getRoot(); | |
892 if (root is null) { | |
893 return null; | |
894 } | |
895 | |
896 Item[] items = getChildren(getControl()); | |
897 if (items !is null) { | |
898 for (int i = 0; i < items.length; i++) { | |
899 Widget o = internalFindItem(items[i], element); | |
900 if (o !is null) { | |
901 return o; | |
902 } | |
903 } | |
904 } | |
905 return null; | |
906 } | |
907 | |
908 /** | |
909 * Copies the attributes of the given element into the given DWT item. | |
910 * | |
911 * @param item | |
912 * the DWT item | |
913 * @param element | |
914 * the element | |
915 */ | |
916 protected void doUpdateItem(Item item, Object element) { | |
917 if (item.isDisposed()) { | |
918 unmapElement(element, item); | |
919 return; | |
920 } | |
921 | |
922 int columnCount = doGetColumnCount(); | |
923 if (columnCount is 0)// If no columns are created then fake one | |
924 columnCount = 1; | |
925 | |
926 ViewerRow viewerRowFromItem = getViewerRowFromItem(item); | |
927 | |
928 bool isVirtual = (getControl().getStyle() & DWT.VIRTUAL) !is 0; | |
929 | |
930 // If the control is virtual, we cannot use the cached viewer row object. See bug 188663. | |
931 if (isVirtual) { | |
932 viewerRowFromItem = cast(ViewerRow) viewerRowFromItem.clone(); | |
933 } | |
934 | |
935 for (int column = 0; column < columnCount; column++) { | |
936 ViewerColumn columnViewer = getViewerColumn(column); | |
937 ViewerCell cellToUpdate = updateCell(viewerRowFromItem, column, | |
938 element); | |
939 | |
940 // If the control is virtual, we cannot use the cached cell object. See bug 188663. | |
941 if (isVirtual) { | |
942 cellToUpdate = new ViewerCell(cellToUpdate.getViewerRow(), cellToUpdate.getColumnIndex(), element); | |
943 } | |
944 | |
945 columnViewer.refresh(cellToUpdate); | |
946 | |
947 // clear cell (see bug 201280) | |
948 updateCell(null, 0, null); | |
949 | |
950 // As it is possible for user code to run the event | |
951 // loop check here. | |
952 if (item.isDisposed()) { | |
953 unmapElement(element, item); | |
954 return; | |
955 } | |
956 | |
957 } | |
958 } | |
959 | |
960 /** | |
961 * Returns <code>true</code> if the given list and array of items refer to | |
962 * the same model elements. Order is unimportant. | |
963 * <p> | |
964 * This method is not intended to be overridden by subclasses. | |
965 * </p> | |
966 * | |
967 * @param items | |
968 * the list of items | |
969 * @param current | |
970 * the array of items | |
971 * @return <code>true</code> if the refer to the same elements, | |
972 * <code>false</code> otherwise | |
973 * | |
974 * @since 3.1 in TreeViewer, moved to AbstractTreeViewer in 3.3 | |
975 */ | |
976 protected bool isSameSelection(SeqView!(Item) items, Item[] current) { | |
977 // If they are not the same size then they are not equivalent | |
978 int n = items.size(); | |
979 if (n !is current.length) { | |
980 return false; | |
981 } | |
982 | |
983 CustomHashtable itemSet = newHashtable(n * 2 + 1); | |
984 foreach( item; items ){ | |
985 Object element = item.getData(); | |
986 itemSet.put(element, element); | |
987 } | |
988 | |
989 // Go through the items of the current collection | |
990 // If there is a mismatch return false | |
991 for (int i = 0; i < current.length; i++) { | |
992 if (current[i].getData() is null | |
993 || !itemSet.containsKey(current[i].getData())) { | |
994 return false; | |
995 } | |
996 } | |
997 | |
998 return true; | |
999 } | |
1000 | |
1001 | |
1002 | |
1003 /* (non-Javadoc) Method declared on StructuredViewer. */ | |
43
ea8ff534f622
Fix override and super aliases
Frank Benoit <benoit@tionex.de>
parents:
40
diff
changeset
|
1004 protected override void doUpdateItem(Widget widget, Object element, bool fullMap) { |
10 | 1005 bool oldBusy = busy; |
1006 busy = true; | |
1007 try { | |
1008 if ( auto item = cast(Item)widget ) { | |
1009 | |
1010 // ensure that back pointer is correct | |
1011 if (fullMap) { | |
1012 associate(element, item); | |
1013 } else { | |
1014 Object data = item.getData(); | |
1015 if (data !is null) { | |
1016 unmapElement(data, item); | |
1017 } | |
1018 item.setData(element); | |
1019 mapElement(element, item); | |
1020 } | |
1021 | |
1022 // update icon and label | |
1023 SafeRunnable.run(new UpdateItemSafeRunnable(item, element)); | |
1024 } | |
1025 } finally { | |
1026 busy = oldBusy; | |
1027 } | |
1028 } | |
1029 | |
1030 /** | |
1031 * Expands all nodes of the viewer's tree, starting with the root. This | |
1032 * method is equivalent to <code>expandToLevel(ALL_LEVELS)</code>. | |
1033 */ | |
1034 public void expandAll() { | |
1035 expandToLevel(ALL_LEVELS); | |
1036 } | |
1037 | |
1038 /** | |
1039 * Expands the root of the viewer's tree to the given level. | |
1040 * | |
1041 * @param level | |
1042 * non-negative level, or <code>ALL_LEVELS</code> to expand all | |
1043 * levels of the tree | |
1044 */ | |
1045 public void expandToLevel(int level) { | |
1046 expandToLevel(getRoot(), level); | |
1047 } | |
1048 | |
1049 /** | |
1050 * Expands all ancestors of the given element or tree path so that the given | |
1051 * element becomes visible in this viewer's tree control, and then expands | |
1052 * the subtree rooted at the given element to the given level. | |
1053 * | |
1054 * @param elementOrTreePath | |
1055 * the element | |
1056 * @param level | |
1057 * non-negative level, or <code>ALL_LEVELS</code> to expand all | |
1058 * levels of the tree | |
1059 */ | |
1060 public void expandToLevel(Object elementOrTreePath, int level) { | |
1061 if (isBusy()) | |
1062 return; | |
1063 Widget w = internalExpand(elementOrTreePath, true); | |
1064 if (w !is null) { | |
1065 internalExpandToLevel(w, level); | |
1066 } | |
1067 } | |
1068 | |
1069 /** | |
1070 * Fires a tree collapsed event. Only listeners registered at the time this | |
1071 * method is called are notified. | |
1072 * | |
1073 * @param event | |
1074 * the tree expansion event | |
1075 * @see ITreeViewerListener#treeCollapsed | |
1076 */ | |
1077 protected void fireTreeCollapsed(TreeExpansionEvent event) { | |
1078 Object[] listeners = treeListeners.getListeners(); | |
1079 for (int i = 0; i < listeners.length; ++i) { | |
39 | 1080 SafeRunnable.run(new class(event,cast(ITreeViewerListener) listeners[i]) SafeRunnable { |
10 | 1081 TreeExpansionEvent event_; |
1082 ITreeViewerListener l; | |
39 | 1083 this(TreeExpansionEvent a,ITreeViewerListener b){ |
1084 event_=a; | |
1085 l = b; | |
10 | 1086 } |
1087 public void run() { | |
1088 l.treeCollapsed(event_); | |
1089 } | |
1090 }); | |
1091 } | |
1092 } | |
1093 | |
1094 /** | |
1095 * Fires a tree expanded event. Only listeners registered at the time this | |
1096 * method is called are notified. | |
1097 * | |
1098 * @param event | |
1099 * the tree expansion event | |
1100 * @see ITreeViewerListener#treeExpanded | |
1101 */ | |
1102 protected void fireTreeExpanded(TreeExpansionEvent event) { | |
1103 Object[] listeners = treeListeners.getListeners(); | |
1104 for (int i = 0; i < listeners.length; ++i) { | |
39 | 1105 SafeRunnable.run(new class( event, cast(ITreeViewerListener) listeners[i]) SafeRunnable { |
10 | 1106 TreeExpansionEvent event_; |
1107 ITreeViewerListener l; | |
39 | 1108 this(TreeExpansionEvent a,ITreeViewerListener b){ |
1109 event_=a; | |
1110 l = b; | |
10 | 1111 } |
1112 public void run() { | |
1113 l.treeExpanded(event_); | |
1114 } | |
1115 }); | |
1116 } | |
1117 | |
1118 } | |
1119 | |
1120 /** | |
1121 * Returns the auto-expand level. | |
1122 * | |
1123 * @return non-negative level, or <code>ALL_LEVELS</code> if all levels of | |
1124 * the tree are expanded automatically | |
1125 * @see #setAutoExpandLevel | |
1126 */ | |
1127 public int getAutoExpandLevel() { | |
1128 return expandToLevel_; | |
1129 } | |
1130 | |
1131 /** | |
1132 * Returns the DWT child items for the given DWT widget. | |
1133 * | |
1134 * @param widget | |
1135 * the widget | |
1136 * @return the child items | |
1137 */ | |
1138 protected abstract Item[] getChildren(Widget widget); | |
1139 | |
1140 /** | |
1141 * Get the child for the widget at index. Note that the default | |
1142 * implementation is not very efficient and should be overridden if this | |
1143 * class is implemented. | |
1144 * | |
1145 * @param widget | |
1146 * the widget to check | |
1147 * @param index | |
1148 * the index of the widget | |
1149 * @return Item or <code>null</code> if widget is not a type that can | |
1150 * contain items. | |
1151 * | |
1152 * @throws ArrayIndexOutOfBoundsException | |
1153 * if the index is not valid. | |
1154 * @since 3.1 | |
1155 */ | |
1156 protected Item getChild(Widget widget, int index) { | |
1157 return getChildren(widget)[index]; | |
1158 } | |
1159 | |
1160 /** | |
1161 * Returns whether the given DWT item is expanded or collapsed. | |
1162 * | |
1163 * @param item | |
1164 * the item | |
1165 * @return <code>true</code> if the item is considered expanded and | |
1166 * <code>false</code> if collapsed | |
1167 */ | |
1168 protected abstract bool getExpanded(Item item); | |
1169 | |
1170 /** | |
1171 * Returns a list of elements corresponding to expanded nodes in this | |
1172 * viewer's tree, including currently hidden ones that are marked as | |
1173 * expanded but are under a collapsed ancestor. | |
1174 * <p> | |
1175 * This method is typically used when preserving the interesting state of a | |
1176 * viewer; <code>setExpandedElements</code> is used during the restore. | |
1177 * </p> | |
1178 * | |
1179 * @return the array of expanded elements | |
1180 * @see #setExpandedElements | |
1181 */ | |
1182 public Object[] getExpandedElements() { | |
1183 auto items = new ArraySeq!(Item); | |
1184 internalCollectExpandedItems(items, getControl()); | |
1185 auto result = new ArraySeq!(Object); | |
1186 result.capacity(items.size()); | |
1187 foreach ( item; items ) { | |
1188 Object data = item.getData(); | |
1189 if (data !is null) { | |
1190 result.append(data); | |
1191 } | |
1192 } | |
1193 return result.toArray(); | |
1194 } | |
1195 | |
1196 /** | |
1197 * Returns whether the node corresponding to the given element or tree path | |
1198 * is expanded or collapsed. | |
1199 * | |
1200 * @param elementOrTreePath | |
1201 * the element | |
1202 * @return <code>true</code> if the node is expanded, and | |
1203 * <code>false</code> if collapsed | |
1204 */ | |
1205 public bool getExpandedState(Object elementOrTreePath) { | |
1206 Assert.isNotNull(elementOrTreePath); | |
1207 Widget item = internalGetWidgetToSelect(elementOrTreePath); | |
1208 if ( auto i = cast(Item)item ) { | |
1209 return getExpanded(i); | |
1210 } | |
1211 return false; | |
1212 } | |
1213 | |
1214 /** | |
1215 * Returns the number of child items of the given DWT control. | |
1216 * | |
1217 * @param control | |
1218 * the control | |
1219 * @return the number of children | |
1220 */ | |
1221 protected abstract int getItemCount(Control control); | |
1222 | |
1223 /** | |
1224 * Returns the number of child items of the given DWT item. | |
1225 * | |
1226 * @param item | |
1227 * the item | |
1228 * @return the number of children | |
1229 */ | |
1230 protected abstract int getItemCount(Item item); | |
1231 | |
1232 /** | |
1233 * Returns the child items of the given DWT item. | |
1234 * | |
1235 * @param item | |
1236 * the item | |
1237 * @return the child items | |
1238 */ | |
1239 protected abstract Item[] getItems(Item item); | |
1240 | |
1241 /** | |
1242 * Returns the item after the given item in the tree, or <code>null</code> | |
1243 * if there is no next item. | |
1244 * | |
1245 * @param item | |
1246 * the item | |
1247 * @param includeChildren | |
1248 * <code>true</code> if the children are considered in | |
1249 * determining which item is next, and <code>false</code> if | |
1250 * subtrees are ignored | |
1251 * @return the next item, or <code>null</code> if none | |
1252 */ | |
1253 protected Item getNextItem(Item item, bool includeChildren) { | |
1254 if (item is null) { | |
1255 return null; | |
1256 } | |
1257 if (includeChildren && getExpanded(item)) { | |
1258 Item[] children = getItems(item); | |
1259 if (children !is null && children.length > 0) { | |
1260 return children[0]; | |
1261 } | |
1262 } | |
1263 | |
1264 // next item is either next sibling or next sibling of first | |
1265 // parent that has a next sibling. | |
1266 Item parent = getParentItem(item); | |
1267 if (parent is null) { | |
1268 return null; | |
1269 } | |
1270 Item[] siblings = getItems(parent); | |
1271 if (siblings !is null) { | |
1272 if (siblings.length <= 1) { | |
1273 return getNextItem(parent, false); | |
1274 } | |
1275 | |
1276 for (int i = 0; i < siblings.length; i++) { | |
1277 if (siblings[i] is item && i < (siblings.length - 1)) { | |
1278 return siblings[i + 1]; | |
1279 } | |
1280 } | |
1281 } | |
1282 return getNextItem(parent, false); | |
1283 } | |
1284 | |
1285 /** | |
1286 * Returns the parent item of the given item in the tree, or | |
1287 * <code>null</code> if there is no parent item. | |
1288 * | |
1289 * @param item | |
1290 * the item | |
1291 * @return the parent item, or <code>null</code> if none | |
1292 */ | |
1293 protected abstract Item getParentItem(Item item); | |
1294 | |
1295 /** | |
1296 * Returns the item before the given item in the tree, or <code>null</code> | |
1297 * if there is no previous item. | |
1298 * | |
1299 * @param item | |
1300 * the item | |
1301 * @return the previous item, or <code>null</code> if none | |
1302 */ | |
1303 protected Item getPreviousItem(Item item) { | |
1304 // previous item is either right-most visible descendent of previous | |
1305 // sibling or parent | |
1306 Item parent = getParentItem(item); | |
1307 if (parent is null) { | |
1308 return null; | |
1309 } | |
1310 Item[] siblings = getItems(parent); | |
1311 if (siblings.length is 0 || siblings[0] is item) { | |
1312 return parent; | |
1313 } | |
1314 Item previous = siblings[0]; | |
1315 for (int i = 1; i < siblings.length; i++) { | |
1316 if (siblings[i] is item) { | |
1317 return rightMostVisibleDescendent(previous); | |
1318 } | |
1319 previous = siblings[i]; | |
1320 } | |
1321 return null; | |
1322 } | |
1323 | |
1324 /* (non-Javadoc) Method declared on StructuredViewer. */ | |
43
ea8ff534f622
Fix override and super aliases
Frank Benoit <benoit@tionex.de>
parents:
40
diff
changeset
|
1325 protected override Object[] getRawChildren(Object parentElementOrTreePath) { |
10 | 1326 bool oldBusy = busy; |
1327 busy = true; | |
1328 try { | |
1329 Object parent; | |
1330 TreePath path; | |
1331 if ( auto p = cast(TreePath)parentElementOrTreePath ) { | |
1332 path = p; | |
1333 parent = path.getLastSegment(); | |
1334 } else { | |
1335 parent = parentElementOrTreePath; | |
1336 path = null; | |
1337 } | |
1338 if (parent !is null) { | |
1339 if (opEquals(parent, getRoot())) { | |
1340 return super.getRawChildren(parent); | |
1341 } | |
1342 IContentProvider cp = getContentProvider(); | |
1343 if ( auto tpcp = cast(ITreePathContentProvider)cp ) { | |
1344 if (path is null) { | |
1345 // A path was not provided so try and find one | |
1346 Widget w = findItem(parent); | |
1347 if ( auto item = cast(Item)w ) { | |
1348 path = getTreePathFromItem(item); | |
1349 } | |
1350 if (path is null) { | |
1351 path = new TreePath([parent ]); | |
1352 } | |
1353 } | |
1354 Object[] result = tpcp.getChildren(path); | |
1355 if (result !is null) { | |
1356 return result; | |
1357 } | |
1358 } else if ( auto tcp = cast(ITreeContentProvider)cp ) { | |
1359 Object[] result = tcp.getChildren(parent); | |
1360 if (result !is null) { | |
1361 return result; | |
1362 } | |
1363 } | |
1364 } | |
1365 return null; | |
1366 } finally { | |
1367 busy = oldBusy; | |
1368 } | |
1369 } | |
1370 | |
1371 /** | |
1372 * Returns all selected items for the given DWT control. | |
1373 * | |
1374 * @param control | |
1375 * the control | |
1376 * @return the list of selected items | |
1377 */ | |
1378 protected abstract Item[] getSelection(Control control); | |
1379 | |
1380 /* | |
1381 * (non-Javadoc) | |
1382 * | |
1383 * @see dwtx.jface.viewers.StructuredViewer#getSelectionFromWidget() | |
1384 */ | |
43
ea8ff534f622
Fix override and super aliases
Frank Benoit <benoit@tionex.de>
parents:
40
diff
changeset
|
1385 protected override SeqView!(Object) getSelectionFromWidget() { |
10 | 1386 Widget[] items = getSelection(getControl()); |
1387 ArraySeq!(Object) list = new ArraySeq!(Object); | |
1388 list.capacity(items.length); | |
1389 for (int i = 0; i < items.length; i++) { | |
1390 Widget item = items[i]; | |
1391 Object e = item.getData(); | |
1392 if (e !is null) { | |
1393 list.append(e); | |
1394 } | |
1395 } | |
1396 return list; | |
1397 } | |
1398 | |
1399 /* | |
1400 * Overridden in AbstractTreeViewer to fix bug 108102 (code copied from | |
1401 * StructuredViewer to avoid introducing new API) (non-Javadoc) | |
1402 * | |
1403 * @see dwtx.jface.viewers.StructuredViewer#handleDoubleSelect(dwt.events.SelectionEvent) | |
1404 */ | |
43
ea8ff534f622
Fix override and super aliases
Frank Benoit <benoit@tionex.de>
parents:
40
diff
changeset
|
1405 protected override void handleDoubleSelect(SelectionEvent event) { |
10 | 1406 // handle case where an earlier selection listener disposed the control. |
1407 Control control = getControl(); | |
1408 if (control !is null && !control.isDisposed()) { | |
1409 // If the double-clicked element can be obtained from the event, use | |
1410 // it | |
1411 // otherwise get it from the control. Some controls like List do | |
1412 // not have the notion of item. | |
1413 // For details, see bug 90161 [Navigator] DefaultSelecting folders | |
1414 // shouldn't always expand first one | |
1415 ISelection selection; | |
1416 if (event.item !is null && event.item.getData() !is null) { | |
1417 | |
1418 // changes to fix bug 108102 follow | |
1419 TreePath treePath = getTreePathFromItem(cast(Item) event.item); | |
1420 selection = new TreeSelection(treePath); | |
1421 // end of changes | |
1422 | |
1423 } else { | |
1424 selection = getSelection(); | |
1425 updateSelection(selection); | |
1426 } | |
1427 fireDoubleClick(new DoubleClickEvent(this, selection)); | |
1428 } | |
1429 } | |
1430 | |
1431 /** | |
1432 * Handles a tree collapse event from the DWT widget. | |
1433 * | |
1434 * @param event | |
1435 * the DWT tree event | |
1436 */ | |
1437 protected void handleTreeCollapse(TreeEvent event) { | |
1438 if (event.item.getData() !is null) { | |
1439 fireTreeCollapsed(new TreeExpansionEvent(this, event.item.getData())); | |
1440 } | |
1441 } | |
1442 | |
1443 /** | |
1444 * Handles a tree expand event from the DWT widget. | |
1445 * | |
1446 * @param event | |
1447 * the DWT tree event | |
1448 */ | |
1449 protected void handleTreeExpand(TreeEvent event) { | |
1450 createChildren(event.item); | |
1451 if (event.item.getData() !is null) { | |
1452 fireTreeExpanded(new TreeExpansionEvent(this, event.item.getData())); | |
1453 } | |
1454 } | |
1455 | |
1456 /* (non-Javadoc) Method declared on Viewer. */ | |
43
ea8ff534f622
Fix override and super aliases
Frank Benoit <benoit@tionex.de>
parents:
40
diff
changeset
|
1457 protected override void hookControl(Control control) { |
10 | 1458 super.hookControl(control); |
1459 addTreeListener(control, new class TreeListener { | |
1460 public void treeExpanded(TreeEvent event) { | |
1461 handleTreeExpand(event); | |
1462 } | |
1463 | |
1464 public void treeCollapsed(TreeEvent event) { | |
1465 handleTreeCollapse(event); | |
1466 } | |
1467 }); | |
1468 } | |
1469 | |
1470 /* | |
1471 * (non-Javadoc) Method declared on StructuredViewer. Builds the initial | |
1472 * tree and handles the automatic expand feature. | |
1473 */ | |
43
ea8ff534f622
Fix override and super aliases
Frank Benoit <benoit@tionex.de>
parents:
40
diff
changeset
|
1474 protected override void inputChanged(Object input, Object oldInput) { |
10 | 1475 preservingSelection(new class Runnable { |
1476 public void run() { | |
1477 Control tree = getControl(); | |
1478 bool useRedraw = true; | |
1479 // (size > REDRAW_THRESHOLD) || (table.getItemCount() > | |
1480 // REDRAW_THRESHOLD); | |
1481 if (useRedraw) { | |
1482 tree.setRedraw(false); | |
1483 } | |
1484 removeAll(tree); | |
1485 tree.setData(getRoot()); | |
1486 internalInitializeTree(tree); | |
1487 if (useRedraw) { | |
1488 tree.setRedraw(true); | |
1489 } | |
1490 } | |
1491 | |
1492 }); | |
1493 } | |
1494 | |
1495 /** | |
1496 * Initializes the tree with root items, expanding to the appropriate | |
1497 * level if necessary. | |
1498 * | |
1499 * @param tree the tree control | |
1500 * @since 3.3 | |
1501 */ | |
1502 protected void internalInitializeTree(Control tree) { | |
1503 createChildren(tree); | |
1504 internalExpandToLevel(tree, expandToLevel_); | |
1505 } | |
1506 | |
1507 /** | |
1508 * Recursively collapses the subtree rooted at the given widget to the given | |
1509 * level. | |
1510 * <p> | |
1511 * </p> | |
1512 * Note that the default implementation of this method does not call | |
1513 * <code>setRedraw</code>. | |
1514 * | |
1515 * @param widget | |
1516 * the widget | |
1517 * @param level | |
1518 * non-negative level, or <code>ALL_LEVELS</code> to collapse | |
1519 * all levels of the tree | |
1520 */ | |
1521 protected void internalCollapseToLevel(Widget widget, int level) { | |
1522 if (level is ALL_LEVELS || level > 0) { | |
1523 | |
1524 if ( auto i = cast(Item)widget ) { | |
1525 setExpanded(i, false); | |
1526 } | |
1527 | |
1528 if (level is ALL_LEVELS || level > 1) { | |
1529 Item[] children = getChildren(widget); | |
1530 if (children !is null) { | |
1531 int nextLevel = (level is ALL_LEVELS ? ALL_LEVELS | |
1532 : level - 1); | |
1533 for (int i = 0; i < children.length; i++) { | |
1534 internalCollapseToLevel(children[i], nextLevel); | |
1535 } | |
1536 } | |
1537 } | |
1538 } | |
1539 } | |
1540 | |
1541 /** | |
1542 * Recursively collects all expanded items from the given widget. | |
1543 * | |
1544 * @param result | |
1545 * a list (element type: <code>Item</code>) into which to | |
1546 * collect the elements | |
1547 * @param widget | |
1548 * the widget | |
1549 */ | |
1550 private void internalCollectExpandedItems(Seq!(Item) result, Widget widget) { | |
1551 Item[] items = getChildren(widget); | |
1552 for (int i = 0; i < items.length; i++) { | |
1553 Item item = items[i]; | |
1554 if (getExpanded(item)) { | |
1555 result.append(item); | |
1556 } | |
1557 internalCollectExpandedItems(result, item); | |
1558 } | |
1559 } | |
1560 | |
1561 /** | |
1562 * Tries to create a path of tree items for the given element or tree path. | |
1563 * This method recursively walks up towards the root of the tree and in the | |
1564 * case of an element (rather than a tree path) assumes that | |
1565 * <code>getParent</code> returns the correct parent of an element. | |
1566 * | |
1567 * @param elementOrPath | |
1568 * the element | |
1569 * @param expand | |
1570 * <code>true</code> if all nodes on the path should be | |
1571 * expanded, and <code>false</code> otherwise | |
1572 * @return Widget | |
1573 */ | |
1574 protected Widget internalExpand(Object elementOrPath, bool expand) { | |
1575 | |
1576 if (elementOrPath is null) { | |
1577 return null; | |
1578 } | |
1579 | |
1580 Widget w = internalGetWidgetToSelect(elementOrPath); | |
1581 if (w is null) { | |
1582 if (opEquals(elementOrPath, getRoot())) { // stop at root | |
1583 return null; | |
1584 } | |
1585 // my parent has to create me | |
1586 Object parent = getParentElement(elementOrPath); | |
1587 if (parent !is null) { | |
1588 Widget pw = internalExpand(parent, false); | |
1589 if (pw !is null) { | |
1590 // let my parent create me | |
1591 createChildren(pw); | |
1592 Object element = internalToElement(elementOrPath); | |
1593 w = internalFindChild(pw, element); | |
1594 if (expand && null !is cast(Item)pw ) { | |
1595 // expand parent items top-down | |
1596 Item item = cast(Item) pw; | |
1597 auto toExpandList = new LinkSeq!(Item); | |
1598 while (item !is null && !getExpanded(item)) { | |
1599 toExpandList.prepend(item); | |
1600 item = getParentItem(item); | |
1601 } | |
1602 foreach( toExpand; toExpandList ){ | |
1603 setExpanded(toExpand, true); | |
1604 } | |
1605 } | |
1606 } | |
1607 } | |
1608 } | |
1609 return w; | |
1610 } | |
1611 | |
1612 /** | |
1613 * If the argument is a tree path, returns its last segment, otherwise | |
1614 * return the argument | |
1615 * | |
1616 * @param elementOrPath | |
1617 * an element or a tree path | |
1618 * @return the element, or the last segment of the tree path | |
1619 */ | |
1620 private Object internalToElement(Object elementOrPath) { | |
1621 if (auto tp = cast(TreePath)elementOrPath ) { | |
1622 return tp.getLastSegment(); | |
1623 } | |
1624 return elementOrPath; | |
1625 } | |
1626 | |
1627 /** | |
1628 * This method takes a tree path or an element. If the argument is not a | |
1629 * tree path, returns the parent of the given element or <code>null</code> | |
1630 * if the parent is not known. If the argument is a tree path with more than | |
1631 * one segment, returns its parent tree path, otherwise returns | |
1632 * <code>null</code>. | |
1633 * | |
1634 * @param elementOrTreePath | |
1635 * @return the parent element, or parent path, or <code>null</code> | |
1636 * | |
1637 * @since 3.2 | |
1638 */ | |
1639 protected Object getParentElement(Object elementOrTreePath) { | |
1640 if (auto tp = cast(TreePath)elementOrTreePath) { | |
1641 return tp.getParentPath(); | |
1642 } | |
1643 IContentProvider cp = getContentProvider(); | |
1644 if ( auto tpcp = cast(ITreePathContentProvider)cp ) { | |
1645 TreePath[] paths = tpcp.getParents(elementOrTreePath); | |
1646 if (paths.length > 0) { | |
1647 if (paths[0].getSegmentCount() is 0) { | |
1648 return getInput(); | |
1649 } | |
1650 return paths[0].getLastSegment(); | |
1651 } | |
1652 } | |
1653 if ( auto tcp = cast(ITreeContentProvider) cp ) { | |
1654 return tcp.getParent(elementOrTreePath); | |
1655 } | |
1656 return null; | |
1657 } | |
1658 | |
1659 /** | |
1660 * Returns the widget to be selected for the given element or tree path. | |
1661 * | |
1662 * @param elementOrTreePath | |
1663 * the element or tree path to select | |
1664 * @return the widget to be selected, or <code>null</code> if not found | |
1665 * | |
1666 * @since 3.1 | |
1667 */ | |
1668 protected Widget internalGetWidgetToSelect(Object elementOrTreePath) { | |
1669 if ( auto treePath = cast(TreePath) elementOrTreePath ) { | |
1670 if (treePath.getSegmentCount() is 0) { | |
1671 return getControl(); | |
1672 } | |
1673 Widget[] candidates = findItems(treePath.getLastSegment()); | |
1674 for (int i = 0; i < candidates.length; i++) { | |
1675 Widget candidate = candidates[i]; | |
1676 if (!(cast(Item)candidate )) { | |
1677 continue; | |
1678 } | |
1679 if (treePath.opEquals(getTreePathFromItem(cast(Item) candidate), | |
1680 getComparer())) { | |
1681 return candidate; | |
1682 } | |
1683 } | |
1684 return null; | |
1685 } | |
1686 return findItem(elementOrTreePath); | |
1687 } | |
1688 | |
1689 /** | |
1690 * Recursively expands the subtree rooted at the given widget to the given | |
1691 * level. | |
1692 * <p> | |
1693 * </p> | |
1694 * Note that the default implementation of this method does not call | |
1695 * <code>setRedraw</code>. | |
1696 * | |
1697 * @param widget | |
1698 * the widget | |
1699 * @param level | |
1700 * non-negative level, or <code>ALL_LEVELS</code> to collapse | |
1701 * all levels of the tree | |
1702 */ | |
1703 protected void internalExpandToLevel(Widget widget, int level) { | |
1704 if (level is ALL_LEVELS || level > 0) { | |
1705 if ( cast(Item)widget && widget.getData() !is null | |
1706 && !isExpandable(cast(Item) widget, null, widget.getData())) { | |
1707 return; | |
1708 } | |
1709 createChildren(widget); | |
1710 if ( auto i = cast(Item)widget ) { | |
1711 setExpanded(i, true); | |
1712 } | |
1713 if (level is ALL_LEVELS || level > 1) { | |
1714 Item[] children = getChildren(widget); | |
1715 if (children !is null) { | |
1716 int newLevel = (level is ALL_LEVELS ? ALL_LEVELS | |
1717 : level - 1); | |
1718 for (int i = 0; i < children.length; i++) { | |
1719 internalExpandToLevel(children[i], newLevel); | |
1720 } | |
1721 } | |
1722 } | |
1723 } | |
1724 } | |
1725 | |
1726 /** | |
1727 * Non-recursively tries to find the given element as a child of the given | |
1728 * parent (item or tree). | |
1729 * | |
1730 * @param parent | |
1731 * the parent item | |
1732 * @param element | |
1733 * the element | |
1734 * @return Widget | |
1735 */ | |
1736 private Widget internalFindChild(Widget parent, Object element) { | |
1737 Item[] items = getChildren(parent); | |
1738 for (int i = 0; i < items.length; i++) { | |
1739 Item item = items[i]; | |
1740 Object data = item.getData(); | |
1741 if (data !is null && opEquals(data, element)) { | |
1742 return item; | |
1743 } | |
1744 } | |
1745 return null; | |
1746 } | |
1747 | |
1748 /** | |
1749 * Recursively tries to find the given element. | |
1750 * | |
1751 * @param parent | |
1752 * the parent item | |
1753 * @param element | |
1754 * the element | |
1755 * @return Widget | |
1756 */ | |
1757 private Widget internalFindItem(Item parent, Object element) { | |
1758 | |
1759 // compare with node | |
1760 Object data = parent.getData(); | |
1761 if (data !is null) { | |
1762 if (opEquals(data, element)) { | |
1763 return parent; | |
1764 } | |
1765 } | |
1766 // recurse over children | |
1767 Item[] items = getChildren(parent); | |
1768 for (int i = 0; i < items.length; i++) { | |
1769 Item item = items[i]; | |
1770 Widget o = internalFindItem(item, element); | |
1771 if (o !is null) { | |
1772 return o; | |
1773 } | |
1774 } | |
1775 return null; | |
1776 } | |
1777 | |
1778 /* (non-Javadoc) Method declared on StructuredViewer. */ | |
43
ea8ff534f622
Fix override and super aliases
Frank Benoit <benoit@tionex.de>
parents:
40
diff
changeset
|
1779 protected override void internalRefresh(Object element) { |
10 | 1780 internalRefresh(element, true); |
1781 } | |
1782 | |
1783 /* (non-Javadoc) Method declared on StructuredViewer. */ | |
43
ea8ff534f622
Fix override and super aliases
Frank Benoit <benoit@tionex.de>
parents:
40
diff
changeset
|
1784 protected override void internalRefresh(Object element, bool updateLabels) { |
10 | 1785 // If element is null, do a full refresh. |
1786 if (element is null) { | |
1787 internalRefresh(getControl(), getRoot(), true, updateLabels); | |
1788 return; | |
1789 } | |
1790 Widget[] items = findItems(element); | |
1791 if (items.length !is 0) { | |
1792 for (int i = 0; i < items.length; i++) { | |
1793 // pick up structure changes too | |
1794 internalRefresh(items[i], element, true, updateLabels); | |
1795 } | |
1796 } | |
1797 } | |
1798 | |
1799 /** | |
1800 * Refreshes the tree starting at the given widget. | |
1801 * <p> | |
1802 * EXPERIMENTAL. Not to be used except by JDT. This method was added to | |
1803 * support JDT's explorations into grouping by working sets, which requires | |
1804 * viewers to support multiple equal elements. See bug 76482 for more | |
1805 * details. This support will likely be removed in Eclipse 3.2 in favor of | |
1806 * proper support for multiple equal elements. | |
1807 * </p> | |
1808 * | |
1809 * @param widget | |
1810 * the widget | |
1811 * @param element | |
1812 * the element | |
1813 * @param doStruct | |
1814 * <code>true</code> if structural changes are to be picked up, | |
1815 * and <code>false</code> if only label provider changes are of | |
1816 * interest | |
1817 * @param updateLabels | |
1818 * <code>true</code> to update labels for existing elements, | |
1819 * <code>false</code> to only update labels as needed, assuming | |
1820 * that labels for existing elements are unchanged. | |
1821 * @since 3.1 | |
1822 */ | |
1823 protected void internalRefresh(Widget widget, Object element, | |
1824 bool doStruct, bool updateLabels) { | |
1825 | |
1826 if ( auto i = cast(Item)widget ) { | |
1827 if (doStruct) { | |
1828 updatePlus(i, element); | |
1829 } | |
1830 if (updateLabels || !opEquals(element, widget.getData())) { | |
1831 doUpdateItem(widget, element, true); | |
1832 } else { | |
1833 associate(element, cast(Item) widget); | |
1834 } | |
1835 } | |
1836 | |
1837 if (doStruct) { | |
1838 internalRefreshStruct(widget, element, updateLabels); | |
1839 } else { | |
1840 Item[] children = getChildren(widget); | |
1841 if (children !is null) { | |
1842 for (int i = 0; i < children.length; i++) { | |
1843 Widget item = children[i]; | |
1844 Object data = item.getData(); | |
1845 if (data !is null) { | |
1846 internalRefresh(item, data, doStruct, updateLabels); | |
1847 } | |
1848 } | |
1849 } | |
1850 } | |
1851 } | |
1852 | |
1853 /** | |
1854 * Update the structure and recurse. Items are updated in updateChildren, as | |
1855 * needed. | |
1856 * | |
1857 * @param widget | |
1858 * @param element | |
1859 * @param updateLabels | |
1860 */ | |
1861 /* package */void internalRefreshStruct(Widget widget, Object element, | |
1862 bool updateLabels) { | |
1863 updateChildren(widget, element, null, updateLabels); | |
1864 Item[] children = getChildren(widget); | |
1865 if (children !is null) { | |
1866 for (int i = 0; i < children.length; i++) { | |
1867 Widget item = children[i]; | |
1868 Object data = item.getData(); | |
1869 if (data !is null) { | |
1870 internalRefreshStruct(item, data, updateLabels); | |
1871 } | |
1872 } | |
1873 } | |
1874 } | |
1875 | |
1876 /** | |
1877 * Removes the given elements from this viewer. | |
1878 * <p> | |
1879 * EXPERIMENTAL. Not to be used except by JDT. This method was added to | |
1880 * support JDT's explorations into grouping by working sets, which requires | |
1881 * viewers to support multiple equal elements. See bug 76482 for more | |
1882 * details. This support will likely be removed in Eclipse 3.2 in favor of | |
1883 * proper support for multiple equal elements. | |
1884 * </p> | |
1885 * | |
1886 * @param elementsOrPaths | |
1887 * the elements or element paths to remove | |
1888 * @since 3.1 | |
1889 */ | |
1890 protected void internalRemove(Object[] elementsOrPaths) { | |
1891 Object input = getInput(); | |
1892 for (int i = 0; i < elementsOrPaths.length; ++i) { | |
1893 Object element = elementsOrPaths[i]; | |
1894 if (opEquals(element, input)) { | |
1895 setInput(null); | |
1896 return; | |
1897 } | |
1898 Widget[] childItems = internalFindItems(element); | |
1899 for (int j = 0; j < childItems.length; j++) { | |
1900 Widget childItem = childItems[j]; | |
1901 if ( auto it = cast(Item)childItem ) { | |
1902 disassociate(it); | |
1903 childItem.dispose(); | |
1904 } | |
1905 } | |
1906 } | |
1907 } | |
1908 | |
1909 /** | |
1910 * Removes the given elements from this viewer, whenever those elements | |
1911 * appear as children of the given parent. | |
1912 * | |
1913 * @param parent the parent element | |
1914 * @param elements | |
1915 * the elements to remove | |
1916 * @since 3.1 | |
1917 */ | |
1918 protected void internalRemove(Object parent, Object[] elements) { | |
1919 | |
1920 CustomHashtable toRemove = new CustomHashtable(getComparer()); | |
1921 for (int i = 0; i < elements.length; i++) { | |
1922 toRemove.put(elements[i], elements[i]); | |
1923 } | |
1924 | |
1925 // Find each place the parent appears in the tree | |
1926 Widget[] parentItemArray = findItems(parent); | |
1927 for (int i = 0; i < parentItemArray.length; i++) { | |
1928 Widget parentItem = parentItemArray[i]; | |
1929 | |
1930 // Iterate over the child items and remove each one | |
1931 Item[] children = getChildren(parentItem); | |
1932 | |
1933 for (int j = 0; j < children.length; j++) { | |
1934 Item child = children[j]; | |
1935 | |
1936 Object data = child.getData(); | |
1937 if (data !is null && toRemove.containsKey(data)) { | |
1938 disassociate(child); | |
1939 child.dispose(); | |
1940 } | |
1941 } | |
1942 } | |
1943 } | |
1944 | |
1945 /** | |
1946 * Sets the expanded state of all items to correspond to the given set of | |
1947 * expanded elements. | |
1948 * | |
1949 * @param expandedElements | |
1950 * the set (element type: <code>Object</code>) of elements | |
1951 * which are expanded | |
1952 * @param widget | |
1953 * the widget | |
1954 */ | |
1955 private void internalSetExpanded(CustomHashtable expandedElements, | |
1956 Widget widget) { | |
1957 Item[] items = getChildren(widget); | |
1958 for (int i = 0; i < items.length; i++) { | |
1959 Item item = items[i]; | |
1960 Object data = item.getData(); | |
1961 if (data !is null) { | |
1962 // remove the element to avoid an infinite loop | |
1963 // if the same element appears on a child item | |
1964 bool expanded = expandedElements.remove(data) !is null; | |
1965 if (expanded !is getExpanded(item)) { | |
1966 if (expanded) { | |
1967 createChildren(item); | |
1968 } | |
1969 setExpanded(item, expanded); | |
1970 } | |
1971 } | |
1972 if (expandedElements.size() > 0) { | |
1973 internalSetExpanded(expandedElements, item); | |
1974 } | |
1975 } | |
1976 } | |
1977 | |
1978 /** | |
1979 * Sets the expanded state of all items to correspond to the given set of | |
1980 * expanded tree paths. | |
1981 * | |
1982 * @param expandedTreePaths | |
1983 * the set (element type: <code>TreePath</code>) of elements | |
1984 * which are expanded | |
1985 * @param widget | |
1986 * the widget | |
1987 */ | |
1988 private void internalSetExpandedTreePaths( | |
1989 CustomHashtable expandedTreePaths, Widget widget, | |
1990 TreePath currentPath) { | |
1991 Item[] items = getChildren(widget); | |
1992 for (int i = 0; i < items.length; i++) { | |
1993 Item item = items[i]; | |
1994 Object data = item.getData(); | |
1995 TreePath childPath = data is null ? null : currentPath | |
1996 .createChildPath(data); | |
1997 if (data !is null && childPath !is null) { | |
1998 // remove the element to avoid an infinite loop | |
1999 // if the same element appears on a child item | |
2000 bool expanded = expandedTreePaths.remove(childPath) !is null; | |
2001 if (expanded !is getExpanded(item)) { | |
2002 if (expanded) { | |
2003 createChildren(item); | |
2004 } | |
2005 setExpanded(item, expanded); | |
2006 } | |
2007 } | |
2008 internalSetExpandedTreePaths(expandedTreePaths, item, childPath); | |
2009 } | |
2010 } | |
2011 | |
2012 /** | |
2013 * Return whether the tree node representing the given element or path can | |
2014 * be expanded. Clients should query expandability by path if the viewer's | |
2015 * content provider is an {@link ITreePathContentProvider}. | |
2016 * <p> | |
2017 * The default implementation of this framework method calls | |
2018 * <code>hasChildren</code> on this viewer's content provider. It may be | |
2019 * overridden if necessary. | |
2020 * </p> | |
2021 * | |
2022 * @param elementOrTreePath | |
2023 * the element or path | |
2024 * @return <code>true</code> if the tree node representing the given | |
2025 * element can be expanded, or <code>false</code> if not | |
2026 */ | |
2027 public bool isExpandable(Object elementOrTreePath) { | |
2028 Object element; | |
2029 TreePath path; | |
2030 if (auto p = cast(TreePath)elementOrTreePath) { | |
2031 path = p; | |
2032 element = path.getLastSegment(); | |
2033 } else { | |
2034 element = elementOrTreePath; | |
2035 path = null; | |
2036 } | |
2037 IContentProvider cp = getContentProvider(); | |
2038 if ( auto tpcp = cast(ITreePathContentProvider) cp ) { | |
2039 if (path is null) { | |
2040 // A path was not provided so try and find one | |
2041 Widget w = findItem(element); | |
2042 if ( auto item = cast(Item)w ) { | |
2043 path = getTreePathFromItem(item); | |
2044 } | |
2045 if (path is null) { | |
2046 path = new TreePath([ element ]); | |
2047 } | |
2048 } | |
2049 return tpcp.hasChildren(path); | |
2050 } | |
2051 if (auto tcp = cast(ITreeContentProvider)cp ) { | |
2052 return tcp.hasChildren(element); | |
2053 } | |
2054 return false; | |
2055 } | |
2056 | |
2057 /** | |
2058 * Return whether the given element is expandable. | |
2059 * | |
2060 * @param item | |
2061 * the tree item for the element | |
2062 * @param parentPath | |
2063 * the parent path if it is known or <code>null</code> if it | |
2064 * needs to be determines | |
2065 * @param element | |
2066 * the element | |
2067 * @return whether the given element is expandable | |
2068 */ | |
2069 private bool isExpandable(Item item, TreePath parentPath, Object element) { | |
2070 Object elementOrTreePath = element; | |
2071 if (isTreePathContentProvider()) { | |
2072 if (parentPath !is null) { | |
2073 elementOrTreePath = parentPath.createChildPath(element); | |
2074 } else { | |
2075 elementOrTreePath = getTreePathFromItem(item); | |
2076 } | |
2077 } | |
2078 return isExpandable(elementOrTreePath); | |
2079 } | |
2080 | |
2081 /* (non-Javadoc) Method declared on Viewer. */ | |
43
ea8ff534f622
Fix override and super aliases
Frank Benoit <benoit@tionex.de>
parents:
40
diff
changeset
|
2082 protected override void labelProviderChanged() { |
10 | 2083 // we have to walk the (visible) tree and update every item |
2084 Control tree = getControl(); | |
2085 tree.setRedraw(false); | |
2086 // don't pick up structure changes, but do force label updates | |
2087 internalRefresh(tree, getRoot(), false, true); | |
2088 tree.setRedraw(true); | |
2089 } | |
2090 | |
2091 /** | |
2092 * Creates a new item. | |
2093 * | |
2094 * @param parent | |
2095 * the parent widget | |
2096 * @param style | |
2097 * DWT style bits | |
2098 * @param index | |
2099 * if non-negative, indicates the position to insert the item | |
2100 * into its parent | |
2101 * @return the newly-created item | |
2102 */ | |
2103 protected abstract Item newItem(Widget parent, int style, int index); | |
2104 | |
2105 /** | |
2106 * Removes the given elements from this viewer. The selection is updated if | |
2107 * required. | |
2108 * <p> | |
2109 * This method should be called (by the content provider) when elements have | |
2110 * been removed from the model, in order to cause the viewer to accurately | |
2111 * reflect the model. This method only affects the viewer, not the model. | |
2112 * </p> | |
2113 * | |
2114 * @param elementsOrTreePaths | |
2115 * the elements to remove | |
2116 */ | |
2117 public void remove(Object[] elementsOrTreePaths) { | |
2118 assertElementsNotNull(elementsOrTreePaths); | |
2119 if (elementsOrTreePaths.length is 0) { | |
2120 return; | |
2121 } | |
2122 if (isBusy()) | |
2123 return; | |
39 | 2124 preservingSelection(new class(elementsOrTreePaths) Runnable { |
10 | 2125 Object[] elementsOrTreePaths_; |
39 | 2126 this(Object[] a){ |
2127 elementsOrTreePaths_=a; | |
10 | 2128 } |
2129 public void run() { | |
2130 internalRemove(elementsOrTreePaths_); | |
2131 } | |
2132 }); | |
2133 } | |
2134 | |
2135 /** | |
2136 * Removes the given elements from this viewer whenever they appear as | |
2137 * children of the given parent element. If the given elements also appear | |
2138 * as children of some other parent, the other parent will remain unchanged. | |
2139 * The selection is updated if required. | |
2140 * <p> | |
2141 * This method should be called (by the content provider) when elements have | |
2142 * been removed from the model, in order to cause the viewer to accurately | |
2143 * reflect the model. This method only affects the viewer, not the model. | |
2144 * </p> | |
2145 * | |
2146 * @param parent | |
2147 * the parent of the elements to remove | |
2148 * @param elements | |
2149 * the elements to remove | |
2150 * | |
2151 * @since 3.2 | |
2152 */ | |
2153 public void remove(Object parent, Object[] elements) { | |
2154 assertElementsNotNull(elements); | |
2155 if (elements.length is 0) { | |
2156 return; | |
2157 } | |
2158 if (isBusy()) | |
2159 return; | |
39 | 2160 preservingSelection(new class(parent,elements) Runnable { |
10 | 2161 Object parent_; |
2162 Object[] elements_; | |
39 | 2163 this(Object a,Object[] b){ |
2164 parent_=a; | |
2165 elements_=b; | |
10 | 2166 } |
2167 public void run() { | |
2168 internalRemove(parent_, elements_); | |
2169 } | |
2170 }); | |
2171 } | |
2172 | |
2173 /** | |
2174 * Removes the given element from the viewer. The selection is updated if | |
2175 * necessary. | |
2176 * <p> | |
2177 * This method should be called (by the content provider) when a single | |
2178 * element has been removed from the model, in order to cause the viewer to | |
2179 * accurately reflect the model. This method only affects the viewer, not | |
2180 * the model. Note that there is another method for efficiently processing | |
2181 * the simultaneous removal of multiple elements. | |
2182 * </p> | |
2183 * | |
2184 * @param elementsOrTreePaths | |
2185 * the element | |
2186 */ | |
2187 public void remove(Object elementsOrTreePaths) { | |
2188 remove([ elementsOrTreePaths ]); | |
2189 } | |
2190 | |
2191 /** | |
2192 * Removes all items from the given control. | |
2193 * | |
2194 * @param control | |
2195 * the control | |
2196 */ | |
2197 protected abstract void removeAll(Control control); | |
2198 | |
2199 /** | |
2200 * Removes a listener for expand and collapse events in this viewer. Has no | |
2201 * affect if an identical listener is not registered. | |
2202 * | |
2203 * @param listener | |
2204 * a tree viewer listener | |
2205 */ | |
2206 public void removeTreeListener(ITreeViewerListener listener) { | |
2207 treeListeners.remove(cast(Object)listener); | |
2208 } | |
2209 | |
2210 /** | |
2211 * This implementation of reveal() reveals the given element or tree path. | |
2212 */ | |
43
ea8ff534f622
Fix override and super aliases
Frank Benoit <benoit@tionex.de>
parents:
40
diff
changeset
|
2213 public override void reveal(Object elementOrTreePath) { |
10 | 2214 Assert.isNotNull(elementOrTreePath); |
2215 Widget w = internalExpand(elementOrTreePath, true); | |
2216 if ( auto item = cast(Item)w ) { | |
2217 showItem(item); | |
2218 } | |
2219 } | |
2220 | |
2221 /** | |
2222 * Returns the rightmost visible descendent of the given item. Returns the | |
2223 * item itself if it has no children. | |
2224 * | |
2225 * @param item | |
2226 * the item to compute the descendent of | |
2227 * @return the rightmost visible descendent or the item itself if it has no | |
2228 * children | |
2229 */ | |
2230 private Item rightMostVisibleDescendent(Item item) { | |
2231 Item[] children = getItems(item); | |
2232 if (getExpanded(item) && children !is null && children.length > 0) { | |
2233 return rightMostVisibleDescendent(children[children.length - 1]); | |
2234 } | |
2235 return item; | |
2236 } | |
2237 | |
2238 /* (non-Javadoc) Method declared on Viewer. */ | |
43
ea8ff534f622
Fix override and super aliases
Frank Benoit <benoit@tionex.de>
parents:
40
diff
changeset
|
2239 public override Item scrollDown(int x, int y) { |
10 | 2240 Item current = getItem(x, y); |
2241 if (current !is null) { | |
2242 Item next = getNextItem(current, true); | |
2243 showItem(next is null ? current : next); | |
2244 return next; | |
2245 } | |
2246 return null; | |
2247 } | |
2248 | |
2249 /* (non-Javadoc) Method declared on Viewer. */ | |
43
ea8ff534f622
Fix override and super aliases
Frank Benoit <benoit@tionex.de>
parents:
40
diff
changeset
|
2250 public override Item scrollUp(int x, int y) { |
10 | 2251 Item current = getItem(x, y); |
2252 if (current !is null) { | |
2253 Item previous = getPreviousItem(current); | |
2254 showItem(previous is null ? current : previous); | |
2255 return previous; | |
2256 } | |
2257 return null; | |
2258 } | |
2259 | |
2260 /** | |
2261 * Sets the auto-expand level. The value 0 means that there is no | |
2262 * auto-expand; 1 means that top-level elements are expanded, but not their | |
2263 * children; 2 means that top-level elements are expanded, and their | |
2264 * children, but not grandchildren; and so on. | |
2265 * <p> | |
2266 * The value <code>ALL_LEVELS</code> means that all subtrees should be | |
2267 * expanded. | |
2268 * </p> | |
2269 * | |
2270 * @param level | |
2271 * non-negative level, or <code>ALL_LEVELS</code> to expand all | |
2272 * levels of the tree | |
2273 */ | |
2274 public void setAutoExpandLevel(int level) { | |
2275 expandToLevel_ = level; | |
2276 } | |
2277 | |
2278 /** | |
2279 * The <code>AbstractTreeViewer</code> implementation of this method | |
2280 * checks to ensure that the content provider is an | |
2281 * <code>ITreeContentProvider</code>. | |
2282 */ | |
43
ea8ff534f622
Fix override and super aliases
Frank Benoit <benoit@tionex.de>
parents:
40
diff
changeset
|
2283 public override void setContentProvider(IContentProvider provider) { |
10 | 2284 // the actual check is in assertContentProviderType |
2285 super.setContentProvider(provider); | |
2286 } | |
2287 | |
43
ea8ff534f622
Fix override and super aliases
Frank Benoit <benoit@tionex.de>
parents:
40
diff
changeset
|
2288 protected override void assertContentProviderType(IContentProvider provider) { |
10 | 2289 Assert.isTrue(cast(ITreeContentProvider)provider |
2290 || cast(ITreePathContentProvider)provider ); | |
2291 } | |
2292 | |
2293 /** | |
2294 * Sets the expand state of the given item. | |
2295 * | |
2296 * @param item | |
2297 * the item | |
2298 * @param expand | |
2299 * the expand state of the item | |
2300 */ | |
2301 protected abstract void setExpanded(Item item, bool expand); | |
2302 | |
2303 /** | |
2304 * Sets which nodes are expanded in this viewer's tree. The given list | |
2305 * contains the elements that are to be expanded; all other nodes are to be | |
2306 * collapsed. | |
2307 * <p> | |
2308 * This method is typically used when restoring the interesting state of a | |
2309 * viewer captured by an earlier call to <code>getExpandedElements</code>. | |
2310 * </p> | |
2311 * | |
2312 * @param elements | |
2313 * the array of expanded elements | |
2314 * @see #getExpandedElements | |
2315 */ | |
2316 public void setExpandedElements(Object[] elements) { | |
2317 assertElementsNotNull(elements); | |
2318 if (isBusy()) { | |
2319 return; | |
2320 } | |
2321 CustomHashtable expandedElements = newHashtable(elements.length * 2 + 1); | |
2322 for (int i = 0; i < elements.length; ++i) { | |
2323 Object element = elements[i]; | |
2324 // Ensure item exists for element. This will materialize items for | |
2325 // each element and their parents, if possible. This is important | |
2326 // to support expanding of inner tree nodes without necessarily | |
2327 // expanding their parents. | |
2328 internalExpand(element, false); | |
2329 expandedElements.put(element, element); | |
2330 } | |
2331 // this will traverse all existing items, and create children for | |
2332 // elements that need to be expanded. If the tree contains multiple | |
2333 // equal elements, and those are in the set of elements to be expanded, | |
2334 // only the first item found for each element will be expanded. | |
2335 internalSetExpanded(expandedElements, getControl()); | |
2336 } | |
2337 | |
2338 /** | |
2339 * Sets which nodes are expanded in this viewer's tree. The given list | |
2340 * contains the tree paths that are to be expanded; all other nodes are to | |
2341 * be collapsed. | |
2342 * <p> | |
2343 * This method is typically used when restoring the interesting state of a | |
2344 * viewer captured by an earlier call to <code>getExpandedTreePaths</code>. | |
2345 * </p> | |
2346 * | |
2347 * @param treePaths | |
2348 * the array of expanded tree paths | |
2349 * @see #getExpandedTreePaths() | |
2350 * | |
2351 * @since 3.2 | |
2352 */ | |
2353 public void setExpandedTreePaths(TreePath[] treePaths) { | |
2354 assertElementsNotNull(treePaths); | |
2355 if (isBusy()) | |
2356 return; | |
39 | 2357 IElementComparer treePathComparer = new class(getComparer()) IElementComparer { |
10 | 2358 IElementComparer comparer; |
39 | 2359 this(IElementComparer c){ |
2360 comparer = c; | |
10 | 2361 } |
2362 public int opEquals(Object a, Object b) { | |
2363 return (cast(TreePath) a).opEquals((cast(TreePath) b), comparer); | |
2364 } | |
2365 | |
2366 public hash_t toHash(Object element) { | |
2367 return (cast(TreePath) element).toHash(comparer); | |
2368 } | |
2369 }; | |
2370 CustomHashtable expandedTreePaths = new CustomHashtable( | |
2371 treePaths.length * 2 + 1, treePathComparer); | |
2372 for (int i = 0; i < treePaths.length; ++i) { | |
2373 TreePath treePath = treePaths[i]; | |
2374 // Ensure item exists for element. This will materialize items for | |
2375 // each element and their parents, if possible. This is important | |
2376 // to support expanding of inner tree nodes without necessarily | |
2377 // expanding their parents. | |
2378 internalExpand(treePath, false); | |
2379 expandedTreePaths.put(treePath, treePath); | |
2380 } | |
2381 // this will traverse all existing items, and create children for | |
2382 // elements that need to be expanded. If the tree contains multiple | |
2383 // equal elements, and those are in the set of elements to be expanded, | |
2384 // only the first item found for each element will be expanded. | |
2385 internalSetExpandedTreePaths(expandedTreePaths, getControl(), | |
2386 new TreePath(new Object[0])); | |
2387 } | |
2388 | |
2389 /** | |
2390 * Sets whether the node corresponding to the given element or tree path is | |
2391 * expanded or collapsed. | |
2392 * | |
2393 * @param elementOrTreePath | |
2394 * the element | |
2395 * @param expanded | |
2396 * <code>true</code> if the node is expanded, and | |
2397 * <code>false</code> if collapsed | |
2398 */ | |
2399 public void setExpandedState(Object elementOrTreePath, bool expanded) { | |
2400 Assert.isNotNull(elementOrTreePath); | |
2401 if (isBusy()) | |
2402 return; | |
2403 Widget item = internalExpand(elementOrTreePath, false); | |
2404 if ( cast(Item)item ) { | |
2405 if (expanded) { | |
2406 createChildren(item); | |
2407 } | |
2408 setExpanded(cast(Item) item, expanded); | |
2409 } | |
2410 } | |
2411 | |
2412 /** | |
2413 * Sets the selection to the given list of items. | |
2414 * | |
2415 * @param items | |
2416 * list of items (element type: | |
2417 * <code>dwt.widgets.Item</code>) | |
2418 */ | |
2419 protected abstract void setSelection(SeqView!(Item) items); | |
2420 | |
2421 /** | |
2422 * This implementation of setSelectionToWidget accepts a list of elements or | |
2423 * a list of tree paths. | |
2424 */ | |
43
ea8ff534f622
Fix override and super aliases
Frank Benoit <benoit@tionex.de>
parents:
40
diff
changeset
|
2425 protected override void setSelectionToWidget(SeqView!(Object) v, bool reveal) { |
10 | 2426 if (v is null) { |
2427 setSelection(new ArraySeq!(Item)); | |
2428 return; | |
2429 } | |
2430 int size = v.size(); | |
2431 auto newSelection = new ArraySeq!(Item); | |
2432 newSelection.capacity(size); | |
2433 for (int i = 0; i < size; ++i) { | |
2434 Object elementOrTreePath = v.get(i); | |
2435 // Use internalExpand since item may not yet be created. See | |
2436 // 1G6B1AR. | |
2437 Widget w = internalExpand(elementOrTreePath, false); | |
2438 if ( auto it = cast(Item)w ) { | |
2439 newSelection.append(it); | |
2440 } else if (w is null && null !is cast(TreePath)elementOrTreePath ) { | |
2441 TreePath treePath = cast(TreePath) elementOrTreePath; | |
2442 Object element = treePath.getLastSegment(); | |
2443 if (element !is null) { | |
2444 w = internalExpand(element, false); | |
2445 if ( auto it = cast(Item)w ) { | |
2446 newSelection.append(it); | |
2447 } | |
2448 } | |
2449 } | |
2450 } | |
2451 setSelection(newSelection); | |
2452 | |
2453 // Although setting the selection in the control should reveal it, | |
2454 // setSelection may be a no-op if the selection is unchanged, | |
2455 // so explicitly reveal the first item in the selection here. | |
2456 // See bug 100565 for more details. | |
2457 if (reveal && newSelection.size() > 0) { | |
2458 showItem(cast(Item) newSelection.get(0)); | |
2459 } | |
2460 } | |
2461 | |
2462 /** | |
2463 * Shows the given item. | |
2464 * | |
2465 * @param item | |
2466 * the item | |
2467 */ | |
2468 protected abstract void showItem(Item item); | |
2469 | |
2470 /** | |
2471 * Updates the tree items to correspond to the child elements of the given | |
2472 * parent element. If null is passed for the children, this method obtains | |
2473 * them (only if needed). | |
2474 * | |
2475 * @param widget | |
2476 * the widget | |
2477 * @param parent | |
2478 * the parent element | |
2479 * @param elementChildren | |
2480 * the child elements, or null | |
2481 * @deprecated this is no longer called by the framework | |
2482 */ | |
2483 protected void updateChildren(Widget widget, Object parent, | |
2484 Object[] elementChildren) { | |
2485 updateChildren(widget, parent, elementChildren, true); | |
2486 } | |
2487 | |
2488 /** | |
2489 * Updates the tree items to correspond to the child elements of the given | |
2490 * parent element. If null is passed for the children, this method obtains | |
2491 * them (only if needed). | |
2492 * | |
2493 * @param widget | |
2494 * the widget | |
2495 * @param parent | |
2496 * the parent element | |
2497 * @param elementChildren | |
2498 * the child elements, or null | |
2499 * @param updateLabels | |
2500 * <code>true</code> to update labels for existing elements, | |
2501 * <code>false</code> to only update labels as needed, assuming | |
2502 * that labels for existing elements are unchanged. | |
2503 * @since 2.1 | |
2504 */ | |
2505 private void updateChildren(Widget widget, Object parent, | |
2506 Object[] elementChildren, bool updateLabels) { | |
2507 // optimization! prune collapsed subtrees | |
2508 if (auto ti = cast(Item)widget ) { | |
2509 if (!getExpanded(ti)) { | |
2510 // need a dummy node if element is expandable; | |
2511 // but try to avoid recreating the dummy node | |
2512 bool needDummy = isExpandable(ti, null, parent); | |
2513 bool haveDummy = false; | |
2514 // remove all children | |
2515 Item[] items = getItems(ti); | |
2516 for (int i = 0; i < items.length; i++) { | |
2517 if (items[i].getData() !is null) { | |
2518 disassociate(items[i]); | |
2519 items[i].dispose(); | |
2520 } else { | |
2521 if (needDummy && !haveDummy) { | |
2522 haveDummy = true; | |
2523 } else { | |
2524 items[i].dispose(); | |
2525 } | |
2526 } | |
2527 } | |
2528 if (needDummy && !haveDummy) { | |
2529 newItem(ti, DWT.NULL, -1); | |
2530 } | |
2531 | |
2532 return; | |
2533 } | |
2534 } | |
2535 | |
2536 // If the children weren't passed in, get them now since they're needed | |
2537 // below. | |
2538 if (elementChildren is null) { | |
2539 if (isTreePathContentProvider() && null !is cast(Item) widget ) { | |
2540 TreePath path = getTreePathFromItem(cast(Item) widget); | |
2541 elementChildren = getSortedChildren(path); | |
2542 } else { | |
2543 elementChildren = getSortedChildren(parent); | |
2544 } | |
2545 } | |
2546 | |
2547 Control tree = getControl(); | |
2548 | |
2549 // WORKAROUND | |
2550 int oldCnt = -1; | |
2551 if (widget is tree) { | |
2552 oldCnt = getItemCount(tree); | |
2553 } | |
2554 | |
2555 Item[] items = getChildren(widget); | |
2556 | |
2557 // save the expanded elements | |
2558 CustomHashtable expanded = newHashtable(CustomHashtable.DEFAULT_CAPACITY); // assume | |
2559 // num | |
2560 // expanded | |
2561 // is | |
2562 // small | |
2563 for (int i = 0; i < items.length; ++i) { | |
2564 if (getExpanded(items[i])) { | |
2565 Object element = items[i].getData(); | |
2566 if (element !is null) { | |
2567 expanded.put(element, element); | |
2568 } | |
2569 } | |
2570 } | |
2571 | |
2572 int min = Math.min(elementChildren.length, items.length); | |
2573 | |
2574 // dispose of surplus items, optimizing for the case where elements have | |
2575 // been deleted but not reordered, or all elements have been removed. | |
2576 int numItemsToDispose = items.length - min; | |
2577 if (numItemsToDispose > 0) { | |
2578 CustomHashtable children = newHashtable(elementChildren.length * 2); | |
2579 for (int i = 0; i < elementChildren.length; i++) { | |
2580 Object elementChild = elementChildren[i]; | |
2581 children.put(elementChild, elementChild); | |
2582 } | |
2583 int i = 0; | |
2584 while (numItemsToDispose > 0 && i < items.length) { | |
2585 Object data = items[i].getData(); | |
2586 if (data is null || items.length - i <= numItemsToDispose || !children.containsKey(data)) { | |
2587 if (data !is null) { | |
2588 disassociate(items[i]); | |
2589 } | |
2590 items[i].dispose(); | |
2591 if (i + 1 < items.length) { | |
2592 // The components at positions i+1 through | |
2593 // items.length-1 in the source array are copied into | |
2594 // positions i through items.length-2 | |
2595 System.arraycopy(items, i + 1, items, i, items.length - (i+1)); | |
2596 } | |
2597 numItemsToDispose--; | |
2598 } else { | |
2599 i++; | |
2600 } | |
2601 } | |
2602 } | |
2603 | |
2604 // compare first min items, and update item if necessary | |
2605 // need to do it in two passes: | |
2606 // 1: disassociate old items | |
2607 // 2: associate new items | |
2608 // because otherwise a later disassociate can remove a mapping made for | |
2609 // a previous associate, | |
2610 // making the map inconsistent | |
2611 for (int i = 0; i < min; ++i) { | |
2612 Item item = items[i]; | |
2613 Object oldElement = item.getData(); | |
2614 if (oldElement !is null) { | |
2615 Object newElement = elementChildren[i]; | |
2616 if (newElement !is oldElement) { | |
2617 if (opEquals(newElement, oldElement)) { | |
2618 // update the data to be the new element, since | |
2619 // although the elements | |
2620 // may be equal, they may still have different labels | |
2621 // or children | |
2622 Object data = item.getData(); | |
2623 if (data !is null) { | |
2624 unmapElement(data, item); | |
2625 } | |
2626 item.setData(newElement); | |
2627 mapElement(newElement, item); | |
2628 } else { | |
2629 disassociate(item); | |
2630 // Clear the text and image to force a label update | |
2631 item.setImage(null); | |
2632 item.setText("");//$NON-NLS-1$ | |
2633 | |
2634 } | |
2635 } | |
2636 } | |
2637 } | |
2638 | |
2639 for (int i = 0; i < min; ++i) { | |
2640 Item item = items[i]; | |
2641 Object newElement = elementChildren[i]; | |
2642 if (item.getData() is null) { | |
2643 // old and new elements are not equal | |
2644 associate(newElement, item); | |
2645 updatePlus(item, newElement); | |
2646 updateItem(item, newElement); | |
2647 } else { | |
2648 // old and new elements are equal | |
2649 updatePlus(item, newElement); | |
2650 if (updateLabels) { | |
2651 updateItem(item, newElement); | |
2652 } | |
2653 } | |
2654 } | |
2655 | |
2656 // Restore expanded state for items that changed position. | |
2657 // Make sure setExpanded is called after updatePlus, since | |
2658 // setExpanded(false) fails if item has no children. | |
2659 // Need to call setExpanded for both expanded and unexpanded | |
2660 // cases since the expanded state can change either way. | |
2661 // This needs to be done in a second loop, see bug 148025. | |
2662 for (int i = 0; i < min; ++i) { | |
2663 Item item = items[i]; | |
2664 Object newElement = elementChildren[i]; | |
2665 setExpanded(item, expanded.containsKey(newElement)); | |
2666 } | |
2667 | |
2668 // add any remaining elements | |
2669 if (min < elementChildren.length) { | |
2670 for (int i = min; i < elementChildren.length; ++i) { | |
2671 createTreeItem(widget, elementChildren[i], i); | |
2672 } | |
2673 | |
2674 // Need to restore expanded state in a separate pass | |
2675 // because createTreeItem does not return the new item. | |
2676 // Avoid doing this unless needed. | |
2677 if (expanded.size() > 0) { | |
2678 // get the items again, to include the new items | |
2679 items = getChildren(widget); | |
2680 for (int i = min; i < elementChildren.length; ++i) { | |
2681 // Restore expanded state for items that changed position. | |
2682 // Make sure setExpanded is called after updatePlus (called | |
2683 // in createTreeItem), since | |
2684 // setExpanded(false) fails if item has no children. | |
2685 // Only need to call setExpanded if element was expanded | |
2686 // since new items are initially unexpanded. | |
2687 if (expanded.containsKey(elementChildren[i])) { | |
2688 setExpanded(items[i], true); | |
2689 } | |
2690 } | |
2691 } | |
2692 } | |
2693 | |
2694 // WORKAROUND | |
2695 if (widget is tree && oldCnt is 0 && getItemCount(tree) !is 0) { | |
2696 // System.out.println("WORKAROUND setRedraw"); | |
2697 tree.setRedraw(false); | |
2698 tree.setRedraw(true); | |
2699 } | |
2700 } | |
2701 | |
2702 /** | |
2703 * Updates the "+"/"-" icon of the tree node from the given element. It | |
2704 * calls <code>isExpandable</code> to determine whether an element is | |
2705 * expandable. | |
2706 * | |
2707 * @param item | |
2708 * the item | |
2709 * @param element | |
2710 * the element | |
2711 */ | |
2712 protected void updatePlus(Item item, Object element) { | |
2713 bool hasPlus = getItemCount(item) > 0; | |
2714 bool needsPlus = isExpandable(item, null, element); | |
2715 bool removeAll = false; | |
2716 bool addDummy = false; | |
2717 Object data = item.getData(); | |
2718 if (data !is null && opEquals(element, data)) { | |
2719 // item shows same element | |
2720 if (hasPlus !is needsPlus) { | |
2721 if (needsPlus) { | |
2722 addDummy = true; | |
2723 } else { | |
2724 removeAll = true; | |
2725 } | |
2726 } | |
2727 } else { | |
2728 // item shows different element | |
2729 removeAll = true; | |
2730 addDummy = needsPlus; | |
2731 | |
2732 // we cannot maintain expand state so collapse it | |
2733 setExpanded(item, false); | |
2734 } | |
2735 if (removeAll) { | |
2736 // remove all children | |
2737 Item[] items = getItems(item); | |
2738 for (int i = 0; i < items.length; i++) { | |
2739 if (items[i].getData() !is null) { | |
2740 disassociate(items[i]); | |
2741 } | |
2742 items[i].dispose(); | |
2743 } | |
2744 } | |
2745 if (addDummy) { | |
2746 newItem(item, DWT.NULL, -1); // append a dummy | |
2747 } | |
2748 } | |
2749 | |
2750 /** | |
2751 * Gets the expanded elements that are visible to the user. An expanded | |
2752 * element is only visible if the parent is expanded. | |
2753 * | |
2754 * @return the visible expanded elements | |
2755 * @since 2.0 | |
2756 */ | |
2757 public Object[] getVisibleExpandedElements() { | |
2758 auto v = new ArraySeq!(Object); | |
2759 internalCollectVisibleExpanded(v, getControl()); | |
2760 return v.toArray(); | |
2761 } | |
2762 | |
2763 private void internalCollectVisibleExpanded(ArraySeq!(Object) result, Widget widget) { | |
2764 Item[] items = getChildren(widget); | |
2765 for (int i = 0; i < items.length; i++) { | |
2766 Item item = items[i]; | |
2767 if (getExpanded(item)) { | |
2768 Object data = item.getData(); | |
2769 if (data !is null) { | |
2770 result.append(data); | |
2771 } | |
2772 // Only recurse if it is expanded - if | |
2773 // not then the children aren't visible | |
2774 internalCollectVisibleExpanded(result, item); | |
2775 } | |
2776 } | |
2777 } | |
2778 | |
2779 /** | |
2780 * Returns the tree path for the given item. | |
2781 * @param item | |
2782 * @return {@link TreePath} | |
2783 * | |
2784 * @since 3.2 | |
2785 */ | |
2786 protected TreePath getTreePathFromItem(Item item) { | |
2787 auto segments = new LinkSeq!(Object); | |
2788 while (item !is null) { | |
2789 Object segment = item.getData(); | |
2790 Assert.isNotNull(segment); | |
2791 segments.prepend(segment); | |
2792 item = getParentItem(item); | |
2793 } | |
2794 return new TreePath(segments.toArray()); | |
2795 } | |
2796 package TreePath getTreePathFromItem_package(Item item) { | |
2797 return getTreePathFromItem_package(item); | |
2798 } | |
2799 | |
2800 /** | |
2801 * This implementation of getSelection() returns an instance of | |
2802 * ITreeSelection. | |
2803 * | |
2804 * @since 3.2 | |
2805 */ | |
43
ea8ff534f622
Fix override and super aliases
Frank Benoit <benoit@tionex.de>
parents:
40
diff
changeset
|
2806 public override ISelection getSelection() { |
10 | 2807 Control control = getControl(); |
2808 if (control is null || control.isDisposed()) { | |
2809 return TreeSelection.EMPTY; | |
2810 } | |
2811 Widget[] items = getSelection(getControl()); | |
2812 auto list = new ArraySeq!(TreePath); | |
2813 list.capacity(items.length); | |
2814 for (int i = 0; i < items.length; i++) { | |
2815 Widget item = items[i]; | |
2816 if (item.getData() !is null) { | |
2817 list.append(getTreePathFromItem(cast(Item) item)); | |
2818 } | |
2819 } | |
2820 return new TreeSelection( list.toArray(), getComparer()); | |
2821 } | |
2822 | |
43
ea8ff534f622
Fix override and super aliases
Frank Benoit <benoit@tionex.de>
parents:
40
diff
changeset
|
2823 protected override void setSelectionToWidget(ISelection selection, bool reveal) { |
10 | 2824 if ( auto treeSelection = cast(ITreeSelection)selection ) { |
2825 auto list = new ArraySeq!(Object); | |
2826 auto paths = treeSelection.getPaths(); | |
2827 list.capacity(paths.length); | |
2828 foreach( path; paths ){ | |
2829 list.append(path); | |
2830 } | |
2831 setSelectionToWidget(list, reveal); | |
2832 } else { | |
2833 super.setSelectionToWidget(selection, reveal); | |
2834 } | |
2835 } | |
2836 | |
2837 /** | |
2838 * Returns a list of tree paths corresponding to expanded nodes in this | |
2839 * viewer's tree, including currently hidden ones that are marked as | |
2840 * expanded but are under a collapsed ancestor. | |
2841 * <p> | |
2842 * This method is typically used when preserving the interesting state of a | |
2843 * viewer; <code>setExpandedElements</code> is used during the restore. | |
2844 * </p> | |
2845 * | |
2846 * @return the array of expanded tree paths | |
2847 * @see #setExpandedElements | |
2848 * | |
2849 * @since 3.2 | |
2850 */ | |
2851 public TreePath[] getExpandedTreePaths() { | |
2852 auto items = new ArraySeq!(Item); | |
2853 internalCollectExpandedItems(items, getControl()); | |
2854 auto result = new ArraySeq!(TreePath); | |
2855 result.capacity(items.size()); | |
2856 foreach( item; items ){ | |
2857 TreePath treePath = getTreePathFromItem(item); | |
2858 if (treePath !is null) { | |
2859 result.append(treePath); | |
2860 } | |
2861 } | |
2862 return result.toArray(); | |
2863 } | |
2864 | |
2865 private bool isTreePathContentProvider() { | |
2866 return null !is cast(ITreePathContentProvider)getContentProvider() ; | |
2867 } | |
2868 | |
2869 /** | |
2870 * Inserts the given element as a new child element of the given parent | |
2871 * element at the given position. If this viewer has a sorter, the position | |
2872 * is ignored and the element is inserted at the correct position in the | |
2873 * sort order. | |
2874 * <p> | |
2875 * This method should be called (by the content provider) when elements have | |
2876 * been added to the model, in order to cause the viewer to accurately | |
2877 * reflect the model. This method only affects the viewer, not the model. | |
2878 * </p> | |
2879 * | |
2880 * @param parentElementOrTreePath | |
2881 * the parent element, or the tree path to the parent | |
2882 * @param element | |
2883 * the element | |
2884 * @param position | |
2885 * a 0-based position relative to the model, or -1 to indicate | |
2886 * the last position | |
2887 * | |
2888 * @since 3.2 | |
2889 */ | |
2890 public void insert(Object parentElementOrTreePath, Object element, | |
2891 int position) { | |
2892 Assert.isNotNull(parentElementOrTreePath); | |
2893 Assert.isNotNull(element); | |
2894 if (isBusy()) | |
2895 return; | |
2896 if (getComparator() !is null || hasFilters()) { | |
2897 add(parentElementOrTreePath, [ element ]); | |
2898 return; | |
2899 } | |
2900 Widget[] items; | |
2901 if (internalIsInputOrEmptyPath(parentElementOrTreePath)) { | |
2902 items = [ getControl() ]; | |
2903 } else { | |
2904 items = internalFindItems(parentElementOrTreePath); | |
2905 } | |
2906 | |
2907 for (int i = 0; i < items.length; i++) { | |
2908 Widget widget = items[i]; | |
2909 if (auto item = cast(Item)widget ) { | |
2910 | |
2911 Item[] childItems = getChildren(item); | |
2912 if (getExpanded(item) | |
2913 || (childItems.length > 0 && childItems[0].getData() !is null)) { | |
2914 // item has real children, go ahead and add | |
2915 int insertionPosition = position; | |
2916 if (insertionPosition is -1) { | |
2917 insertionPosition = getItemCount(item); | |
2918 } | |
2919 | |
2920 createTreeItem(item, element, insertionPosition); | |
2921 } | |
2922 } else { | |
2923 int insertionPosition = position; | |
2924 if (insertionPosition is -1) { | |
2925 insertionPosition = getItemCount(cast(Control) widget); | |
2926 } | |
2927 | |
2928 createTreeItem(widget, element, insertionPosition); | |
2929 } | |
2930 } | |
2931 } | |
2932 | |
2933 /* | |
2934 * (non-Javadoc) | |
2935 * | |
2936 * @see dwtx.jface.viewers.ColumnViewer#getColumnViewerOwner(int) | |
2937 */ | |
43
ea8ff534f622
Fix override and super aliases
Frank Benoit <benoit@tionex.de>
parents:
40
diff
changeset
|
2938 protected override Widget getColumnViewerOwner(int columnIndex) { |
10 | 2939 // Return null by default |
2940 return null; | |
2941 } | |
2942 | |
2943 /** | |
2944 * This implementation of {@link #getItemAt(Point)} returns null to ensure | |
2945 * API backwards compatibility. Subclasses should override. | |
2946 * | |
2947 * @since 3.3 | |
2948 */ | |
43
ea8ff534f622
Fix override and super aliases
Frank Benoit <benoit@tionex.de>
parents:
40
diff
changeset
|
2949 protected override Item getItemAt(Point point) { |
10 | 2950 return null; |
2951 } | |
2952 | |
2953 /** | |
2954 * This implementation of {@link #createViewerEditor()} returns null to ensure | |
2955 * API backwards compatibility. Subclasses should override. | |
2956 * | |
2957 * @since 3.3 | |
2958 */ | |
43
ea8ff534f622
Fix override and super aliases
Frank Benoit <benoit@tionex.de>
parents:
40
diff
changeset
|
2959 protected override ColumnViewerEditor createViewerEditor() { |
10 | 2960 return null; |
2961 } | |
2962 | |
2963 /** | |
2964 * Returns the number of columns of this viewer. | |
2965 * <p><b>Subclasses should overwrite this method, which has a default | |
2966 * implementation (returning 0) for API backwards compatility reasons</b></p> | |
2967 * | |
2968 * @return the number of columns | |
2969 * | |
2970 * @since 3.3 | |
2971 */ | |
43
ea8ff534f622
Fix override and super aliases
Frank Benoit <benoit@tionex.de>
parents:
40
diff
changeset
|
2972 protected override int doGetColumnCount() { |
10 | 2973 return 0; |
2974 } | |
2975 | |
2976 | |
2977 /** | |
2978 * This implementation of buildLabel handles tree paths as well as elements. | |
2979 * | |
2980 * @param updateLabel | |
2981 * the ViewerLabel to collect the result in | |
2982 * @param elementOrPath | |
2983 * the element or tree path for which a label should be built | |
2984 * | |
2985 * @see dwtx.jface.viewers.StructuredViewer#buildLabel(dwtx.jface.viewers.ViewerLabel, | |
2986 * java.lang.Object) | |
2987 */ | |
43
ea8ff534f622
Fix override and super aliases
Frank Benoit <benoit@tionex.de>
parents:
40
diff
changeset
|
2988 protected override void buildLabel(ViewerLabel updateLabel, Object elementOrPath) { |
10 | 2989 Object element; |
2990 if (auto path = cast(TreePath)elementOrPath ) { | |
2991 IBaseLabelProvider provider = getLabelProvider(); | |
2992 if ( auto pprov = cast(ITreePathLabelProvider) provider ) { | |
2993 buildLabel(updateLabel, path, pprov); | |
2994 return; | |
2995 } | |
2996 element = path.getLastSegment(); | |
2997 } else { | |
2998 element = elementOrPath; | |
2999 } | |
3000 super.buildLabel(updateLabel, element); | |
3001 } | |
3002 | |
3003 /** | |
3004 * Returns true if the given object is either the input or an empty tree path. | |
3005 * | |
3006 * @param elementOrTreePath an element which could either be the viewer's input, or a tree path | |
3007 * | |
3008 * @return <code>true</code> if the given object is either the input or an empty tree path, | |
3009 * <code>false</code> otherwise. | |
3010 * @since 3.3 | |
3011 */ | |
3012 final protected bool internalIsInputOrEmptyPath(Object elementOrTreePath) { | |
3013 if (elementOrTreePath.opEquals(getInput())) | |
3014 return true; | |
3015 if (!(cast(TreePath)elementOrTreePath )) | |
3016 return false; | |
3017 return (cast(TreePath) elementOrTreePath).getSegmentCount() is 0; | |
3018 } | |
3019 | |
3020 /* | |
3021 * Subclasses should implement | |
3022 */ | |
43
ea8ff534f622
Fix override and super aliases
Frank Benoit <benoit@tionex.de>
parents:
40
diff
changeset
|
3023 protected override ViewerRow getViewerRowFromItem(Widget item) { |
10 | 3024 return null; |
3025 } | |
3026 } |