comparison dwtx/jface/viewers/TreeViewer.d @ 10:b6c35faf97c8

Viewers
author Frank Benoit <benoit@tionex.de>
date Mon, 31 Mar 2008 00:47:19 +0200
parents
children 644f1334b451
comparison
equal deleted inserted replaced
9:6c14e54dfc11 10:b6c35faf97c8
1 /*******************************************************************************
2 * Copyright (c) 2004, 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> - concept of ViewerRow,
11 * refactoring (bug 153993), bug 167323, 191468
12 * Port to the D programming language:
13 * Frank Benoit <benoit@tionex.de>
14 *******************************************************************************/
15
16 module dwtx.jface.viewers.TreeViewer;
17
18 import dwtx.jface.viewers.AbstractTreeViewer;
19 import dwtx.jface.viewers.TreeViewerRow;
20 import dwtx.jface.viewers.IBaseLabelProvider;
21 import dwtx.jface.viewers.ColumnViewerEditor;
22 import dwtx.jface.viewers.IContentProvider;
23 import dwtx.jface.viewers.TreeSelection;
24 import dwtx.jface.viewers.ViewerRow;
25 import dwtx.jface.viewers.ISelection;
26 import dwtx.jface.viewers.TreeViewerEditor;
27 import dwtx.jface.viewers.ColumnViewerEditorActivationStrategy;
28 import dwtx.jface.viewers.ColumnViewerEditorActivationEvent;
29 import dwtx.jface.viewers.ILazyTreeContentProvider;
30 import dwtx.jface.viewers.ILazyTreePathContentProvider;
31 import dwtx.jface.viewers.TreePath;
32 import dwtx.jface.viewers.TreeExpansionEvent;
33 import dwtx.jface.viewers.ViewerCell;
34
35 import tango.util.collection.LinkSeq;
36 import tango.util.collection.model.Seq;
37 import tango.util.collection.model.SeqView;
38
39 import dwt.DWT;
40 import dwt.events.DisposeEvent;
41 import dwt.events.DisposeListener;
42 import dwt.events.TreeEvent;
43 import dwt.events.TreeListener;
44 import dwt.graphics.Point;
45 import dwt.widgets.Composite;
46 import dwt.widgets.Control;
47 import dwt.widgets.Event;
48 import dwt.widgets.Item;
49 import dwt.widgets.Listener;
50 import dwt.widgets.Tree;
51 import dwt.widgets.TreeItem;
52 import dwt.widgets.Widget;
53 import dwtx.jface.util.Policy;
54
55 import dwt.dwthelper.utils;
56 import dwt.dwthelper.Runnable;
57
58 /**
59 * A concrete viewer based on an DWT <code>Tree</code> control.
60 * <p>
61 * This class is not intended to be subclassed outside the viewer framework. It
62 * is designed to be instantiated with a pre-existing DWT tree control and
63 * configured with a domain-specific content provider, label provider, element
64 * filter (optional), and element sorter (optional).
65 * </p>
66 * <p>
67 * Content providers for tree viewers must implement either the
68 * {@link ITreeContentProvider} interface, (as of 3.2) the
69 * {@link ILazyTreeContentProvider} interface, or (as of 3.3) the
70 * {@link ILazyTreePathContentProvider}. If the content provider is an
71 * <code>ILazyTreeContentProvider</code> or an
72 * <code>ILazyTreePathContentProvider</code>, the underlying Tree must be
73 * created using the {@link DWT#VIRTUAL} style bit, and the tree viewer will not
74 * support sorting or filtering.
75 * </p>
76 */
77 public class TreeViewer : AbstractTreeViewer {
78
79 public alias AbstractTreeViewer.preservingSelection preservingSelection;
80 public alias AbstractTreeViewer.getSelection getSelection;
81 public alias AbstractTreeViewer.setSelection setSelection;
82
83 private static final String VIRTUAL_DISPOSE_KEY = Policy.JFACE
84 ~ ".DISPOSE_LISTENER"; //$NON-NLS-1$
85
86 /**
87 * This viewer's control.
88 */
89 private Tree tree;
90
91 /**
92 * Flag for whether the tree has been disposed of.
93 */
94 private bool treeIsDisposed = false;
95
96 private bool contentProviderIsLazy;
97
98 private bool contentProviderIsTreeBased;
99
100 /**
101 * The row object reused
102 */
103 private TreeViewerRow cachedRow;
104
105 /**
106 * true if we are inside a preservingSelection() call
107 */
108 private bool preservingSelection_;
109
110 /**
111 * Creates a tree viewer on a newly-created tree control under the given
112 * parent. The tree control is created using the DWT style bits
113 * <code>MULTI, H_SCROLL, V_SCROLL,</code> and <code>BORDER</code>. The
114 * viewer has no input, no content provider, a default label provider, no
115 * sorter, and no filters.
116 *
117 * @param parent
118 * the parent control
119 */
120 public this(Composite parent) {
121 this(parent, DWT.MULTI | DWT.H_SCROLL | DWT.V_SCROLL | DWT.BORDER);
122 }
123
124 /**
125 * Creates a tree viewer on a newly-created tree control under the given
126 * parent. The tree control is created using the given DWT style bits. The
127 * viewer has no input, no content provider, a default label provider, no
128 * sorter, and no filters.
129 *
130 * @param parent
131 * the parent control
132 * @param style
133 * the DWT style bits used to create the tree.
134 */
135 public this(Composite parent, int style) {
136 this(new Tree(parent, style));
137 }
138
139 /**
140 * Creates a tree viewer on the given tree control. The viewer has no input,
141 * no content provider, a default label provider, no sorter, and no filters.
142 *
143 * @param tree
144 * the tree control
145 */
146 public this(Tree tree) {
147 super();
148 this.tree = tree;
149 hookControl(tree);
150 }
151
152 /*
153 * (non-Javadoc) Method declared in AbstractTreeViewer.
154 */
155 protected void addTreeListener(Control c, TreeListener listener) {
156 (cast(Tree) c).addTreeListener(listener);
157 }
158
159 /*
160 * (non-Javadoc)
161 *
162 * @see dwtx.jface.viewers.ColumnViewer#getColumnViewerOwner(int)
163 */
164 protected Widget getColumnViewerOwner(int columnIndex) {
165 if (columnIndex < 0 || ( columnIndex > 0 && columnIndex >= getTree().getColumnCount() ) ) {
166 return null;
167 }
168
169 if (getTree().getColumnCount() is 0)// Hang it off the table if it
170 return getTree();
171
172 return getTree().getColumn(columnIndex);
173 }
174
175 /*
176 * (non-Javadoc) Method declared in AbstractTreeViewer.
177 */
178 protected Item[] getChildren(Widget o) {
179 if (auto ti = cast(TreeItem)o ) {
180 return ti.getItems();
181 }
182 if (auto t = cast(Tree)o ) {
183 return t.getItems();
184 }
185 return null;
186 }
187
188 /*
189 * (non-Javadoc) Method declared in Viewer.
190 */
191 public Control getControl() {
192 return tree;
193 }
194
195 /*
196 * (non-Javadoc) Method declared in AbstractTreeViewer.
197 */
198 protected bool getExpanded(Item item) {
199 return (cast(TreeItem) item).getExpanded();
200 }
201
202 /*
203 * (non-Javadoc)
204 *
205 * @see dwtx.jface.viewers.ColumnViewer#getItemAt(dwt.graphics.Point)
206 */
207 protected Item getItemAt(Point p) {
208 TreeItem[] selection = tree.getSelection();
209
210 if( selection.length is 1 ) {
211 int columnCount = tree.getColumnCount();
212
213 for( int i = 0; i < columnCount; i++ ) {
214 if( selection[0].getBounds(i).contains(p) ) {
215 return selection[0];
216 }
217 }
218 }
219
220 return getTree().getItem(p);
221 }
222
223 /*
224 * (non-Javadoc) Method declared in AbstractTreeViewer.
225 */
226 protected int getItemCount(Control widget) {
227 return (cast(Tree) widget).getItemCount();
228 }
229
230 /*
231 * (non-Javadoc) Method declared in AbstractTreeViewer.
232 */
233 protected int getItemCount(Item item) {
234 return (cast(TreeItem) item).getItemCount();
235 }
236
237 /*
238 * (non-Javadoc) Method declared in AbstractTreeViewer.
239 */
240 protected Item[] getItems(Item item) {
241 return (cast(TreeItem) item).getItems();
242 }
243
244 /**
245 * The tree viewer implementation of this <code>Viewer</code> framework
246 * method ensures that the given label provider is an instance of either
247 * <code>ITableLabelProvider</code> or <code>ILabelProvider</code>. If
248 * it is an <code>ITableLabelProvider</code>, then it provides a separate
249 * label text and image for each column. If it is an
250 * <code>ILabelProvider</code>, then it provides only the label text and
251 * image for the first column, and any remaining columns are blank.
252 */
253 public IBaseLabelProvider getLabelProvider() {
254 return super.getLabelProvider();
255 }
256
257 /*
258 * (non-Javadoc) Method declared in AbstractTreeViewer.
259 */
260 protected Item getParentItem(Item item) {
261 return (cast(TreeItem) item).getParentItem();
262 }
263
264 /*
265 * (non-Javadoc) Method declared in AbstractTreeViewer.
266 */
267 protected Item[] getSelection(Control widget) {
268 return (cast(Tree) widget).getSelection();
269 }
270
271 /**
272 * Returns this tree viewer's tree control.
273 *
274 * @return the tree control
275 */
276 public Tree getTree() {
277 return tree;
278 }
279
280 /*
281 * (non-Javadoc)
282 *
283 * @see dwtx.jface.viewers.AbstractTreeViewer#hookControl(dwt.widgets.Control)
284 */
285 protected void hookControl(Control control) {
286 super.hookControl(control);
287 Tree treeControl = cast(Tree) control;
288
289 if ((treeControl.getStyle() & DWT.VIRTUAL) !is 0) {
290 treeControl.addDisposeListener(new class DisposeListener {
291 public void widgetDisposed(DisposeEvent e) {
292 treeIsDisposed = true;
293 unmapAllElements();
294 }
295 });
296 treeControl.addListener(DWT.SetData, new class Listener {
297
298 public void handleEvent(Event event) {
299 if (contentProviderIsLazy) {
300 TreeItem item = cast(TreeItem) event.item;
301 TreeItem parentItem = item.getParentItem();
302 int index = event.index;
303 virtualLazyUpdateWidget(
304 parentItem is null ? cast(Widget) getTree()
305 : parentItem, index);
306 }
307 }
308
309 });
310 }
311 }
312
313 protected ColumnViewerEditor createViewerEditor() {
314 return new TreeViewerEditor(this,null,new ColumnViewerEditorActivationStrategy(this),ColumnViewerEditor.DEFAULT);
315 }
316
317 /*
318 * (non-Javadoc) Method declared in AbstractTreeViewer.
319 */
320 protected Item newItem(Widget parent, int flags, int ix) {
321 TreeItem item;
322
323 if ( cast(TreeItem)parent ) {
324 item = cast(TreeItem) createNewRowPart(getViewerRowFromItem(parent),
325 flags, ix).getItem();
326 } else {
327 item = cast(TreeItem) createNewRowPart(null, flags, ix).getItem();
328 }
329
330 return item;
331 }
332
333 /*
334 * (non-Javadoc) Method declared in AbstractTreeViewer.
335 */
336 protected void removeAll(Control widget) {
337 (cast(Tree) widget).removeAll();
338 }
339
340 /*
341 * (non-Javadoc) Method declared in AbstractTreeViewer.
342 */
343 protected void setExpanded(Item node, bool expand) {
344 (cast(TreeItem) node).setExpanded(expand);
345 if (contentProviderIsLazy) {
346 // force repaints to happen
347 getControl().update();
348 }
349 }
350
351 /*
352 * (non-Javadoc) Method declared in AbstractTreeViewer.
353 */
354 protected void setSelection(SeqView!(Item) items) {
355
356 Item[] current = getSelection(getTree());
357
358 // Don't bother resetting the same selection
359 if (isSameSelection(items, current)) {
360 return;
361 }
362
363 getTree().setSelection( cast(TreeItem[]) items.toArray());
364 }
365
366 /*
367 * (non-Javadoc) Method declared in AbstractTreeViewer.
368 */
369 protected void showItem(Item item) {
370 getTree().showItem(cast(TreeItem) item);
371 }
372
373 /*
374 * (non-Javadoc)
375 *
376 * @see dwtx.jface.viewers.AbstractTreeViewer#getChild(dwt.widgets.Widget,
377 * int)
378 */
379 protected Item getChild(Widget widget, int index) {
380 if (auto ti = cast(TreeItem)widget ) {
381 return ti.getItem(index);
382 }
383 if (auto t = cast(Tree)widget ) {
384 return t.getItem(index);
385 }
386 return null;
387 }
388
389 protected void assertContentProviderType(IContentProvider provider) {
390 if ( null !is cast(ILazyTreeContentProvider)provider
391 || null !is cast(ILazyTreePathContentProvider)provider ) {
392 return;
393 }
394 super.assertContentProviderType(provider);
395 }
396
397 protected Object[] getRawChildren(Object parent) {
398 if (contentProviderIsLazy) {
399 return new Object[0];
400 }
401 return super.getRawChildren(parent);
402 }
403
404 void preservingSelection(Runnable updateCode, bool reveal) {
405 if (preservingSelection_){
406 // avoid preserving the selection if called reentrantly,
407 // see bug 172640
408 updateCode.run();
409 return;
410 }
411 preservingSelection_ = true;
412 try {
413 super.preservingSelection(updateCode, reveal);
414 } finally {
415 preservingSelection_ = false;
416 }
417 }
418
419 /**
420 * For a TreeViewer with a tree with the VIRTUAL style bit set, set the
421 * number of children of the given element or tree path. To set the number
422 * of children of the invisible root of the tree, you can pass the input
423 * object or an empty tree path.
424 *
425 * @param elementOrTreePath
426 * the element, or tree path
427 * @param count
428 *
429 * @since 3.2
430 */
431 public void setChildCount(Object elementOrTreePath, int count) {
432 if (isBusy())
433 return;
434 preservingSelection(new class Runnable {
435 Object elementOrTreePath_;
436 int count_;
437 this(){
438 elementOrTreePath_=elementOrTreePath;
439 count_=count;
440 }
441 public void run() {
442 if (internalIsInputOrEmptyPath(elementOrTreePath_)) {
443 getTree().setItemCount(count_);
444 return;
445 }
446 Widget[] items = internalFindItems(elementOrTreePath_);
447 for (int i = 0; i < items.length; i++) {
448 TreeItem treeItem = cast(TreeItem) items[i];
449 treeItem.setItemCount(count_);
450 }
451 }
452 });
453 }
454
455 /**
456 * For a TreeViewer with a tree with the VIRTUAL style bit set, replace the
457 * given parent's child at index with the given element. If the given parent
458 * is this viewer's input or an empty tree path, this will replace the root
459 * element at the given index.
460 * <p>
461 * This method should be called by implementers of ILazyTreeContentProvider
462 * to populate this viewer.
463 * </p>
464 *
465 * @param parentElementOrTreePath
466 * the parent of the element that should be updated, or the tree
467 * path to that parent
468 * @param index
469 * the index in the parent's children
470 * @param element
471 * the new element
472 *
473 * @see #setChildCount(Object, int)
474 * @see ILazyTreeContentProvider
475 * @see ILazyTreePathContentProvider
476 *
477 * @since 3.2
478 */
479 public void replace(Object parentElementOrTreePath, int index,
480 Object element) {
481 if (isBusy())
482 return;
483 Item[] selectedItems = getSelection(getControl());
484 TreeSelection selection = cast(TreeSelection) getSelection();
485 Widget[] itemsToDisassociate;
486 if (auto tp = cast(TreePath)parentElementOrTreePath ) {
487 TreePath elementPath = tp
488 .createChildPath(element);
489 itemsToDisassociate = internalFindItems(elementPath);
490 } else {
491 itemsToDisassociate = internalFindItems(element);
492 }
493 if (internalIsInputOrEmptyPath(parentElementOrTreePath)) {
494 if (index < tree.getItemCount()) {
495 TreeItem item = tree.getItem(index);
496 selection = adjustSelectionForReplace(selectedItems, selection, item, element, getRoot());
497 // disassociate any different item that represents the
498 // same element under the same parent (the tree)
499 for (int i = 0; i < itemsToDisassociate.length; i++) {
500 if (auto itemToDisassociate = cast(TreeItem)itemsToDisassociate[i]) {
501 if (itemToDisassociate !is item
502 && itemToDisassociate.getParentItem() is null) {
503 int indexToDisassociate = getTree().indexOf(
504 itemToDisassociate);
505 disassociate(itemToDisassociate);
506 getTree().clear(indexToDisassociate, true);
507 }
508 }
509 }
510 Object oldData = item.getData();
511 updateItem(item, element);
512 if (!/+TreeViewer.this.+/opEquals(oldData, element)) {
513 item.clearAll(true);
514 }
515 }
516 } else {
517 Widget[] parentItems = internalFindItems(parentElementOrTreePath);
518 for (int i = 0; i < parentItems.length; i++) {
519 TreeItem parentItem = cast(TreeItem) parentItems[i];
520 if (index < parentItem.getItemCount()) {
521 TreeItem item = parentItem.getItem(index);
522 selection = adjustSelectionForReplace(selectedItems, selection, item, element, parentItem.getData());
523 // disassociate any different item that represents the
524 // same element under the same parent (the tree)
525 for (int j = 0; j < itemsToDisassociate.length; j++) {
526 if ( auto itemToDisassociate = cast(TreeItem)itemsToDisassociate[j] ) {
527 if (itemToDisassociate !is item
528 && itemToDisassociate.getParentItem() is parentItem) {
529 int indexToDisaccociate = parentItem
530 .indexOf(itemToDisassociate);
531 disassociate(itemToDisassociate);
532 parentItem.clear(indexToDisaccociate, true);
533 }
534 }
535 }
536 Object oldData = item.getData();
537 updateItem(item, element);
538 if (!/+TreeViewer.this.+/opEquals(oldData, element)) {
539 item.clearAll(true);
540 }
541 }
542 }
543 }
544 // Restore the selection if we are not already in a nested preservingSelection:
545 if (!preservingSelection_) {
546 setSelectionToWidget(selection, false);
547 // send out notification if old and new differ
548 ISelection newSelection = getSelection();
549 if (!(cast(Object)newSelection).opEquals(cast(Object)selection)) {
550 handleInvalidSelection(selection, newSelection);
551 }
552 }
553 }
554
555 /**
556 * Fix for bug 185673: If the currently replaced item was selected, add it
557 * to the selection that is being restored. Only do this if its getData() is
558 * currently null
559 *
560 * @param selectedItems
561 * @param selection
562 * @param item
563 * @param element
564 * @return
565 */
566 private TreeSelection adjustSelectionForReplace(Item[] selectedItems,
567 TreeSelection selection, TreeItem item, Object element, Object parentElement) {
568 if (item.getData() !is null || selectedItems.length is selection.size()
569 || parentElement is null) {
570 // Don't do anything - we are not seeing an instance of bug 185673
571 return selection;
572 }
573 for (int i = 0; i < selectedItems.length; i++) {
574 if (item is selectedItems[i]) {
575 // The current item was selected, but its data is null.
576 // The data will be replaced by the given element, so to keep
577 // it selected, we have to add it to the selection.
578 TreePath[] originalPaths = selection.getPaths();
579 int length_ = originalPaths.length;
580 TreePath[] paths = new TreePath[length_ + 1];
581 System.arraycopy(originalPaths, 0, paths, 0, length_);
582 // set the element temporarily so that we can call getTreePathFromItem
583 item.setData(element);
584 paths[length_] = getTreePathFromItem(item);
585 item.setData(null);
586 return new TreeSelection(paths, selection.getElementComparer());
587 }
588 }
589 // The item was not selected, return the given selection
590 return selection;
591 }
592
593 public bool isExpandable(Object element) {
594 if (contentProviderIsLazy) {
595 TreeItem treeItem = cast(TreeItem) internalExpand(element, false);
596 if (treeItem is null) {
597 return false;
598 }
599 virtualMaterializeItem(treeItem);
600 return treeItem.getItemCount() > 0;
601 }
602 return super.isExpandable(element);
603 }
604
605 protected Object getParentElement(Object element) {
606 bool oldBusy = busy;
607 busy = true;
608 try {
609 if (contentProviderIsLazy && !contentProviderIsTreeBased && !(cast(TreePath)element )) {
610 ILazyTreeContentProvider lazyTreeContentProvider = cast(ILazyTreeContentProvider) getContentProvider();
611 return lazyTreeContentProvider.getParent(element);
612 }
613 if (contentProviderIsLazy && contentProviderIsTreeBased && !(cast(TreePath)element )) {
614 ILazyTreePathContentProvider lazyTreePathContentProvider = cast(ILazyTreePathContentProvider) getContentProvider();
615 TreePath[] parents = lazyTreePathContentProvider
616 .getParents(element);
617 if (parents !is null && parents.length > 0) {
618 return parents[0];
619 }
620 }
621 return super.getParentElement(element);
622 } finally {
623 busy = oldBusy;
624 }
625 }
626
627 protected void createChildren(Widget widget) {
628 if (contentProviderIsLazy) {
629 Object element = widget.getData();
630 if (element is null && cast(TreeItem)widget ) {
631 // parent has not been materialized
632 virtualMaterializeItem(cast(TreeItem) widget);
633 // try getting the element now that updateElement was called
634 element = widget.getData();
635 }
636 if (element is null) {
637 // give up because the parent is still not materialized
638 return;
639 }
640 Item[] children = getChildren(widget);
641 if (children.length is 1 && children[0].getData() is null) {
642 // found a dummy node
643 virtualLazyUpdateChildCount(widget, children.length);
644 children = getChildren(widget);
645 }
646 // touch all children to make sure they are materialized
647 for (int i = 0; i < children.length; i++) {
648 if (children[i].getData() is null) {
649 virtualLazyUpdateWidget(widget, i);
650 }
651 }
652 return;
653 }
654 super.createChildren(widget);
655 }
656
657 protected void internalAdd(Widget widget, Object parentElement,
658 Object[] childElements) {
659 if (contentProviderIsLazy) {
660 if (auto ti = cast(TreeItem)widget ) {
661 int count = ti.getItemCount() + childElements.length;
662 ti.setItemCount(count);
663 ti.clearAll(false);
664 } else {
665 Tree t = cast(Tree) widget;
666 t.setItemCount(t.getItemCount() + childElements.length);
667 t.clearAll(false);
668 }
669 return;
670 }
671 super.internalAdd(widget, parentElement, childElements);
672 }
673
674 private void virtualMaterializeItem(TreeItem treeItem) {
675 if (treeItem.getData() !is null) {
676 // already materialized
677 return;
678 }
679 if (!contentProviderIsLazy) {
680 return;
681 }
682 int index;
683 Widget parent = treeItem.getParentItem();
684 if (parent is null) {
685 parent = treeItem.getParent();
686 }
687 Object parentElement = parent.getData();
688 if (parentElement !is null) {
689 if ( auto t = cast(Tree)parent ) {
690 index = t.indexOf(treeItem);
691 } else {
692 index = (cast(TreeItem) parent).indexOf(treeItem);
693 }
694 virtualLazyUpdateWidget(parent, index);
695 }
696 }
697
698 /*
699 * (non-Javadoc)
700 *
701 * @see dwtx.jface.viewers.AbstractTreeViewer#internalRefreshStruct(dwt.widgets.Widget,
702 * java.lang.Object, bool)
703 */
704 protected void internalRefreshStruct(Widget widget, Object element,
705 bool updateLabels) {
706 if (contentProviderIsLazy) {
707 // clear all starting with the given widget
708 if (auto t = cast(Tree)widget ) {
709 t.clearAll(true);
710 } else if (cast(TreeItem)widget ) {
711 (cast(TreeItem) widget).clearAll(true);
712 }
713 int index = 0;
714 Widget parent = null;
715 if (auto treeItem = cast(TreeItem)widget ) {
716 parent = treeItem.getParentItem();
717 if (parent is null) {
718 parent = treeItem.getParent();
719 }
720 if (cast(Tree)parent ) {
721 index = (cast(Tree) parent).indexOf(treeItem);
722 } else {
723 index = (cast(TreeItem) parent).indexOf(treeItem);
724 }
725 }
726 virtualRefreshExpandedItems(parent, widget, element, index);
727 return;
728 }
729 super.internalRefreshStruct(widget, element, updateLabels);
730 }
731
732 /**
733 * Traverses the visible (expanded) part of the tree and updates child
734 * counts.
735 *
736 * @param parent the parent of the widget, or <code>null</code> if the widget is the tree
737 * @param widget
738 * @param element
739 * @param index the index of the widget in the children array of its parent, or 0 if the widget is the tree
740 */
741 private void virtualRefreshExpandedItems(Widget parent, Widget widget, Object element, int index) {
742 if ( cast(Tree)widget ) {
743 if (element is null) {
744 (cast(Tree) widget).setItemCount(0);
745 return;
746 }
747 virtualLazyUpdateChildCount(widget, getChildren(widget).length);
748 } else if ((cast(TreeItem) widget).getExpanded()) {
749 // prevent SetData callback
750 (cast(TreeItem)widget).setText(" "); //$NON-NLS-1$
751 virtualLazyUpdateWidget(parent, index);
752 } else {
753 return;
754 }
755 Item[] items = getChildren(widget);
756 for (int i = 0; i < items.length; i++) {
757 Item item = items[i];
758 Object data = item.getData();
759 virtualRefreshExpandedItems(widget, item, data, i);
760 }
761 }
762
763 /*
764 * To unmap elements correctly, we need to register a dispose listener with
765 * the item if the tree is virtual.
766 */
767 protected void mapElement(Object element, Widget item) {
768 super.mapElement(element, item);
769 // make sure to unmap elements if the tree is virtual
770 if ((getTree().getStyle() & DWT.VIRTUAL) !is 0) {
771 // only add a dispose listener if item hasn't already on assigned
772 // because it is reused
773 if (item.getData(VIRTUAL_DISPOSE_KEY) is null) {
774 item.setData(VIRTUAL_DISPOSE_KEY, new ValueWrapperBool(true));
775 item.addDisposeListener(new class DisposeListener {
776 Widget item_;
777 this(){
778 item_=item;
779 }
780 public void widgetDisposed(DisposeEvent e) {
781 if (!treeIsDisposed) {
782 Object data = item_.getData();
783 if (usingElementMap() && data !is null) {
784 unmapElement(data, item_);
785 }
786 }
787 }
788 });
789 }
790 }
791 }
792
793 /*
794 * (non-Javadoc)
795 *
796 * @see dwtx.jface.viewers.ColumnViewer#getRowPartFromItem(dwt.widgets.Widget)
797 */
798 protected ViewerRow getViewerRowFromItem(Widget item) {
799 if( cachedRow is null ) {
800 cachedRow = new TreeViewerRow(cast(TreeItem) item);
801 } else {
802 cachedRow.setItem(cast(TreeItem) item);
803 }
804
805 return cachedRow;
806 }
807
808 /**
809 * Create a new ViewerRow at rowIndex
810 *
811 * @param parent
812 * @param style
813 * @param rowIndex
814 * @return ViewerRow
815 */
816 private ViewerRow createNewRowPart(ViewerRow parent, int style, int rowIndex) {
817 if (parent is null) {
818 if (rowIndex >= 0) {
819 return getViewerRowFromItem(new TreeItem(tree, style, rowIndex));
820 }
821 return getViewerRowFromItem(new TreeItem(tree, style));
822 }
823
824 if (rowIndex >= 0) {
825 return getViewerRowFromItem(new TreeItem(cast(TreeItem) parent.getItem(),
826 DWT.NONE, rowIndex));
827 }
828
829 return getViewerRowFromItem(new TreeItem(cast(TreeItem) parent.getItem(),
830 DWT.NONE));
831 }
832
833 /*
834 * (non-Javadoc)
835 *
836 * @see dwtx.jface.viewers.AbstractTreeViewer#internalInitializeTree(dwt.widgets.Control)
837 */
838 protected void internalInitializeTree(Control widget) {
839 if (contentProviderIsLazy) {
840 if (cast(Tree)widget && widget.getData() !is null) {
841 virtualLazyUpdateChildCount(widget, 0);
842 return;
843 }
844 }
845 super.internalInitializeTree(tree);
846 }
847
848 /*
849 * (non-Javadoc)
850 *
851 * @see dwtx.jface.viewers.AbstractTreeViewer#updatePlus(dwt.widgets.Item,
852 * java.lang.Object)
853 */
854 protected void updatePlus(Item item, Object element) {
855 if (contentProviderIsLazy) {
856 Object data = item.getData();
857 int itemCount = 0;
858 if (data !is null) {
859 // item is already materialized
860 itemCount = (cast(TreeItem) item).getItemCount();
861 }
862 virtualLazyUpdateHasChildren(item, itemCount);
863 } else {
864 super.updatePlus(item, element);
865 }
866 }
867
868 /**
869 * Removes the element at the specified index of the parent. The selection is updated if required.
870 *
871 * @param parentOrTreePath the parent element, the input element, or a tree path to the parent element
872 * @param index child index
873 * @since 3.3
874 */
875 public void remove(Object parentOrTreePath_, int index_) {
876 if (isBusy())
877 return;
878 preservingSelection(new class Runnable {
879 Seq!(TreePath) oldSelection;
880 Object parentOrTreePath;
881 int index;
882 this(){
883 parentOrTreePath=parentOrTreePath_;
884 index=index_;
885 oldSelection = new LinkSeq!(TreePath);
886 foreach( p; (cast(TreeSelection) getSelection()).getPaths()){
887 oldSelection.append( p );
888 }
889 }
890 public void run() {
891 TreePath removedPath = null;
892 if (internalIsInputOrEmptyPath(parentOrTreePath)) {
893 Tree tree = cast(Tree) getControl();
894 if (index < tree.getItemCount()) {
895 TreeItem item = tree.getItem(index);
896 if (item.getData() !is null) {
897 removedPath = getTreePathFromItem(item);
898 disassociate(item);
899 }
900 item.dispose();
901 }
902 } else {
903 Widget[] parentItems = internalFindItems(parentOrTreePath);
904 for (int i = 0; i < parentItems.length; i++) {
905 TreeItem parentItem = cast(TreeItem) parentItems[i];
906 if (index < parentItem.getItemCount()) {
907 TreeItem item = parentItem.getItem(index);
908 if (item.getData() !is null) {
909 removedPath = getTreePathFromItem(item);
910 disassociate(item);
911 }
912 item.dispose();
913 }
914 }
915 }
916 if (removedPath !is null) {
917 bool removed = false;
918 int delIdx = 0;
919 foreach ( path; oldSelection.dup ) {
920 if (path.startsWith(removedPath, getComparer())) {
921 oldSelection.removeAt(delIdx);
922 removed = true;
923 }
924 delIdx++;
925 }
926 if (removed) {
927 setSelection(new TreeSelection(
928 oldSelection.toArray(), getComparer()),
929 false);
930 }
931
932 }
933 }
934 });
935 }
936
937 /* (non-Javadoc)
938 * @see dwtx.jface.viewers.AbstractTreeViewer#handleTreeExpand(dwt.events.TreeEvent)
939 */
940 protected void handleTreeExpand(TreeEvent event) {
941 if (contentProviderIsLazy) {
942 if (event.item.getData() !is null) {
943 Item[] children = getChildren(event.item);
944 if (children.length is 1 && children[0].getData() is null) {
945 // we have a dummy child node, ask for an updated child
946 // count
947 virtualLazyUpdateChildCount(event.item, children.length);
948 }
949 fireTreeExpanded(new TreeExpansionEvent(this, event.item
950 .getData()));
951 }
952 return;
953 }
954 super.handleTreeExpand(event);
955 }
956
957 /* (non-Javadoc)
958 * @see dwtx.jface.viewers.AbstractTreeViewer#setContentProvider(dwtx.jface.viewers.IContentProvider)
959 */
960 public void setContentProvider(IContentProvider provider) {
961 contentProviderIsLazy = (cast(ILazyTreeContentProvider)provider )
962 || (cast(ILazyTreePathContentProvider)provider );
963 contentProviderIsTreeBased = null !is cast(ILazyTreePathContentProvider)provider ;
964 super.setContentProvider(provider);
965 }
966
967 /**
968 * For a TreeViewer with a tree with the VIRTUAL style bit set, inform the
969 * viewer about whether the given element or tree path has children. Avoid
970 * calling this method if the number of children has already been set.
971 *
972 * @param elementOrTreePath
973 * the element, or tree path
974 * @param hasChildren
975 *
976 * @since 3.3
977 */
978 public void setHasChildren(Object elementOrTreePath_, bool hasChildren_) {
979 if (isBusy())
980 return;
981 preservingSelection(new class Runnable {
982 Object elementOrTreePath;
983 bool hasChildren;
984 this(){
985 elementOrTreePath=elementOrTreePath_;
986 hasChildren=hasChildren_;
987 }
988 public void run() {
989 if (internalIsInputOrEmptyPath(elementOrTreePath)) {
990 if (hasChildren) {
991 virtualLazyUpdateChildCount(getTree(),
992 getChildren(getTree()).length);
993 } else {
994 setChildCount(elementOrTreePath, 0);
995 }
996 return;
997 }
998 Widget[] items = internalFindItems(elementOrTreePath);
999 for (int i = 0; i < items.length; i++) {
1000 TreeItem item = cast(TreeItem) items[i];
1001 if (!hasChildren) {
1002 item.setItemCount(0);
1003 } else {
1004 if (!item.getExpanded()) {
1005 item.setItemCount(1);
1006 TreeItem child = item.getItem(0);
1007 if (child.getData() !is null) {
1008 disassociate(child);
1009 }
1010 item.clear(0, true);
1011 } else {
1012 virtualLazyUpdateChildCount(item, item.getItemCount());
1013 }
1014 }
1015 }
1016 }
1017 });
1018 }
1019
1020 /**
1021 * Update the widget at index.
1022 * @param widget
1023 * @param index
1024 */
1025 private void virtualLazyUpdateWidget(Widget widget, int index) {
1026 bool oldBusy = busy;
1027 busy = false;
1028 try {
1029 if (contentProviderIsTreeBased) {
1030 TreePath treePath;
1031 if ( auto i = cast(Item)widget ) {
1032 if (widget.getData() is null) {
1033 // we need to materialize the parent first
1034 // see bug 167668
1035 // however, that would be too risky
1036 // see bug 182782 and bug 182598
1037 // so we just ignore this call altogether
1038 // and don't do this: virtualMaterializeItem((TreeItem) widget);
1039 return;
1040 }
1041 treePath = getTreePathFromItem(i);
1042 } else {
1043 treePath = TreePath.EMPTY;
1044 }
1045 (cast(ILazyTreePathContentProvider) getContentProvider())
1046 .updateElement(treePath, index);
1047 } else {
1048 (cast(ILazyTreeContentProvider) getContentProvider()).updateElement(
1049 widget.getData(), index);
1050 }
1051 } finally {
1052 busy = oldBusy;
1053 }
1054 }
1055
1056 /**
1057 * Update the child count
1058 * @param widget
1059 * @param currentChildCount
1060 */
1061 private void virtualLazyUpdateChildCount(Widget widget, int currentChildCount) {
1062 bool oldBusy = busy;
1063 busy = false;
1064 try {
1065 if (contentProviderIsTreeBased) {
1066 TreePath treePath;
1067 if (cast(Item)widget ) {
1068 treePath = getTreePathFromItem(cast(Item) widget);
1069 } else {
1070 treePath = TreePath.EMPTY;
1071 }
1072 (cast(ILazyTreePathContentProvider) getContentProvider())
1073 .updateChildCount(treePath, currentChildCount);
1074 } else {
1075 (cast(ILazyTreeContentProvider) getContentProvider()).updateChildCount(widget.getData(), currentChildCount);
1076 }
1077 } finally {
1078 busy = oldBusy;
1079 }
1080 }
1081
1082 /**
1083 * Update the item with the current child count.
1084 * @param item
1085 * @param currentChildCount
1086 */
1087 private void virtualLazyUpdateHasChildren(Item item, int currentChildCount) {
1088 bool oldBusy = busy;
1089 busy = false;
1090 try {
1091 if (contentProviderIsTreeBased) {
1092 TreePath treePath;
1093 treePath = getTreePathFromItem(item);
1094 if (currentChildCount is 0) {
1095 // item is not expanded (but may have a plus currently)
1096 (cast(ILazyTreePathContentProvider) getContentProvider())
1097 .updateHasChildren(treePath);
1098 } else {
1099 (cast(ILazyTreePathContentProvider) getContentProvider())
1100 .updateChildCount(treePath, currentChildCount);
1101 }
1102 } else {
1103 (cast(ILazyTreeContentProvider) getContentProvider()).updateChildCount(item.getData(), currentChildCount);
1104 }
1105 } finally {
1106 busy = oldBusy;
1107 }
1108 }
1109
1110 protected void disassociate(Item item) {
1111 if (contentProviderIsLazy) {
1112 // avoid causing a callback:
1113 item.setText(" "); //$NON-NLS-1$
1114 }
1115 super.disassociate(item);
1116 }
1117
1118 protected int doGetColumnCount() {
1119 return tree.getColumnCount();
1120 }
1121
1122 /**
1123 * Sets a new selection for this viewer and optionally makes it visible.
1124 * <p>
1125 * <b>Currently the <code>reveal</code> parameter is not honored because
1126 * {@link Tree} does not provide an API to only select an item without
1127 * scrolling it into view</b>
1128 * </p>
1129 *
1130 * @param selection
1131 * the new selection
1132 * @param reveal
1133 * <code>true</code> if the selection is to be made visible,
1134 * and <code>false</code> otherwise
1135 */
1136 public void setSelection(ISelection selection, bool reveal) {
1137 super.setSelection(selection, reveal);
1138 }
1139
1140 public void editElement(Object element, int column) {
1141 if( cast(TreePath)element ) {
1142 setSelection(new TreeSelection(cast(TreePath) element));
1143 TreeItem[] items = tree.getSelection();
1144
1145 if( items.length is 1 ) {
1146 ViewerRow row = getViewerRowFromItem(items[0]);
1147
1148 if (row !is null) {
1149 ViewerCell cell = row.getCell(column);
1150 if (cell !is null) {
1151 getControl().setRedraw(false);
1152 try {
1153 triggerEditorActivationEvent(new ColumnViewerEditorActivationEvent(cell));
1154 } finally {
1155 getControl().setRedraw(true);
1156 }
1157 }
1158 }
1159 }
1160 } else {
1161 super.editElement(element, column);
1162 }
1163 }
1164 }