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