Mercurial > projects > dwt-addons
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 } |