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
|
|
88 alias ColumnViewer.buildLabel buildLabel;
|
|
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 */
|
|
608 protected Object[] getSortedChildren(Object parentElementOrTreePath) {
|
|
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 */
|
|
642 protected Object[] getFilteredChildren(Object parentElementOrTreePath) {
|
|
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 */
|
|
715 protected void associate(Object element, Item item) {
|
|
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 {
|
|
775 final Item[] tis = getChildren(widget);
|
|
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
|
|
783 BusyIndicator.showWhile(widget.getDisplay(), new class Runnable {
|
|
784 Widget widget_;
|
|
785 Item[] tis_;
|
|
786 this(){
|
|
787 widget_ = widget;
|
|
788 tis_=tis;
|
|
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;
|
|
808 if (isTreePathContentProvider() && null !is cast(Item)widget_ ) {
|
|
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 */
|
|
848 protected void disassociate(Item item) {
|
|
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. */
|
|
875 protected Widget doFindInputItem(Object element) {
|
|
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. */
|
|
889 protected Widget doFindItem(Object element) {
|
|
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. */
|
|
1004 protected void doUpdateItem(Widget widget, Object element, bool fullMap) {
|
|
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) {
|
|
1080 SafeRunnable.run(new class SafeRunnable {
|
|
1081 TreeExpansionEvent event_;
|
|
1082 ITreeViewerListener l;
|
|
1083 this(){
|
|
1084 event_=event;
|
|
1085 l = cast(ITreeViewerListener) listeners[i];
|
|
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) {
|
|
1105 SafeRunnable.run(new class SafeRunnable {
|
|
1106 TreeExpansionEvent event_;
|
|
1107 ITreeViewerListener l;
|
|
1108 this(){
|
|
1109 event_=event;
|
|
1110 l = cast(ITreeViewerListener) listeners[i];
|
|
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. */
|
|
1325 protected Object[] getRawChildren(Object parentElementOrTreePath) {
|
|
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 */
|
|
1385 protected SeqView!(Object) getSelectionFromWidget() {
|
|
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 */
|
|
1405 protected void handleDoubleSelect(SelectionEvent event) {
|
|
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. */
|
|
1457 protected void hookControl(Control control) {
|
|
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 */
|
|
1474 protected void inputChanged(Object input, Object oldInput) {
|
|
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. */
|
|
1779 protected void internalRefresh(Object element) {
|
|
1780 internalRefresh(element, true);
|
|
1781 }
|
|
1782
|
|
1783 /* (non-Javadoc) Method declared on StructuredViewer. */
|
|
1784 protected void internalRefresh(Object element, bool updateLabels) {
|
|
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. */
|
|
2082 protected void labelProviderChanged() {
|
|
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;
|
|
2124 preservingSelection(new class Runnable {
|
|
2125 Object[] elementsOrTreePaths_;
|
|
2126 this(){
|
|
2127 elementsOrTreePaths_=elementsOrTreePaths;
|
|
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;
|
|
2160 preservingSelection(new class Runnable {
|
|
2161 Object parent_;
|
|
2162 Object[] elements_;
|
|
2163 this(){
|
|
2164 parent_=parent;
|
|
2165 elements_=elements;
|
|
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 */
|
|
2213 public void reveal(Object elementOrTreePath) {
|
|
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. */
|
|
2239 public Item scrollDown(int x, int y) {
|
|
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. */
|
|
2250 public Item scrollUp(int x, int y) {
|
|
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 */
|
|
2283 public void setContentProvider(IContentProvider provider) {
|
|
2284 // the actual check is in assertContentProviderType
|
|
2285 super.setContentProvider(provider);
|
|
2286 }
|
|
2287
|
|
2288 protected void assertContentProviderType(IContentProvider provider) {
|
|
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;
|
|
2357 IElementComparer treePathComparer = new class IElementComparer {
|
|
2358 IElementComparer comparer;
|
|
2359 this(){
|
|
2360 comparer = getComparer();
|
|
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 */
|
|
2425 protected void setSelectionToWidget(SeqView!(Object) v, bool reveal) {
|
|
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 */
|
|
2806 public ISelection getSelection() {
|
|
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
|
|
2823 protected void setSelectionToWidget(ISelection selection, bool reveal) {
|
|
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 */
|
|
2938 protected Widget getColumnViewerOwner(int columnIndex) {
|
|
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 */
|
|
2949 protected Item getItemAt(Point point) {
|
|
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 */
|
|
2959 protected ColumnViewerEditor createViewerEditor() {
|
|
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 */
|
|
2972 protected int doGetColumnCount() {
|
|
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 */
|
|
2988 protected void buildLabel(ViewerLabel updateLabel, Object elementOrPath) {
|
|
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 */
|
|
3023 protected ViewerRow getViewerRowFromItem(Widget item) {
|
|
3024 return null;
|
|
3025 }
|
|
3026 }
|