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