View | Details | Raw Unified | Return to bug 38782 | Differences between
and this patch

Collapse All | Expand All

(-)AbstractTreeViewer.java (-1341 / +1368 lines)
Lines 12-24 Link Here
12
package org.eclipse.jface.viewers;
12
package org.eclipse.jface.viewers;
13
13
14
import java.util.ArrayList;
14
import java.util.ArrayList;
15
import java.util.Arrays;
16
import java.util.HashSet;
15
import java.util.HashSet;
17
import java.util.Iterator;
16
import java.util.Iterator;
18
import java.util.List;
17
import java.util.List;
19
18
20
import org.eclipse.core.runtime.Platform;
19
import org.eclipse.core.runtime.Platform;
21
20
import org.eclipse.jface.util.Assert;
21
import org.eclipse.jface.util.ListenerList;
22
import org.eclipse.jface.util.SafeRunnable;
22
import org.eclipse.swt.SWT;
23
import org.eclipse.swt.SWT;
23
import org.eclipse.swt.custom.BusyIndicator;
24
import org.eclipse.swt.custom.BusyIndicator;
24
import org.eclipse.swt.events.SelectionListener;
25
import org.eclipse.swt.events.SelectionListener;
Lines 28-37 Link Here
28
import org.eclipse.swt.widgets.Item;
29
import org.eclipse.swt.widgets.Item;
29
import org.eclipse.swt.widgets.Widget;
30
import org.eclipse.swt.widgets.Widget;
30
31
31
import org.eclipse.jface.util.Assert;
32
import org.eclipse.jface.util.ListenerList;
33
import org.eclipse.jface.util.SafeRunnable;
34
35
/**
32
/**
36
 * Abstract base implementation for tree-structure-oriented viewers
33
 * Abstract base implementation for tree-structure-oriented viewers
37
 * (trees and table trees).
34
 * (trees and table trees).
Lines 57-69 Link Here
57
	 * @see #expandToLevel
54
	 * @see #expandToLevel
58
	 * @see #collapseToLevel
55
	 * @see #collapseToLevel
59
	 */
56
	 */
60
	public static final int ALL_LEVELS= -1;
57
	public static final int ALL_LEVELS = -1;
61
58
62
	/**
59
	/**
63
	 * List of registered tree listeners (element type: <code>TreeListener</code>).
60
	 * List of registered tree listeners (element type: <code>TreeListener</code>).
64
	 */
61
	 */
65
	private ListenerList treeListeners = new ListenerList(1);
62
	private ListenerList treeListeners = new ListenerList(1);
66
		
63
67
	/**
64
	/**
68
	 * The level to which the tree is automatically expanded each time
65
	 * The level to which the tree is automatically expanded each time
69
	 * the viewer's input is changed (that is, by <code>setInput</code>).
66
	 * the viewer's input is changed (that is, by <code>setInput</code>).
Lines 72-1522 Link Here
72
	 * @see #setAutoExpandLevel
69
	 * @see #setAutoExpandLevel
73
	 */
70
	 */
74
	private int expandToLevel = 0;
71
	private int expandToLevel = 0;
75
	
72
76
	/**
73
	/**
77
	 * The safe runnable used to call the label provider.
74
	 * The safe runnable used to call the label provider.
78
	 */
75
	 */
79
	private	UpdateItemSafeRunnable safeUpdateItem = new UpdateItemSafeRunnable();
76
	private UpdateItemSafeRunnable safeUpdateItem =
80
	
77
		new UpdateItemSafeRunnable();
78
81
	class UpdateItemSafeRunnable extends SafeRunnable {
79
	class UpdateItemSafeRunnable extends SafeRunnable {
82
		Object element;
80
		Object element;
83
		Item item;
81
		Item item;
84
		boolean exception = false;
82
		boolean exception = false;
85
		public void run() {
83
		public void run() {
86
			if(exception) return;
84
			if (exception)
87
			doUpdateItem(item,element);
85
				return;
86
			doUpdateItem(item, element);
88
		}
87
		}
89
		public void handleException(Throwable e) {
88
		public void handleException(Throwable e) {
90
			super.handleException(e);
89
			super.handleException(e);
91
			//If and unexpected exception happens, remove it
90
			//If and unexpected exception happens, remove it
92
			//to make sure the application keeps running.
91
			//to make sure the application keeps running.
93
			exception = true;
92
			exception = true;
94
		}		
95
	} 	
96
	
97
/**
98
 * Creates an abstract tree viewer. The viewer has no input, no content provider, a
99
 * default label provider, no sorter, no filters, and has auto-expand turned off.
100
 */
101
protected AbstractTreeViewer() {
102
}
103
/**
104
 * Adds the given child elements to this viewer as children of the given parent element.
105
 * If this viewer does not have a sorter, the elements are added at the end of the 
106
 * parent's list of children in the order given; otherwise, the elements are inserted
107
 * at the appropriate positions.
108
 * <p>
109
 * This method should be called (by the content provider) when elements 
110
 * have been added to the model, in order to cause the viewer to accurately
111
 * reflect the model. This method only affects the viewer, not the model.
112
 * </p>
113
 *
114
 * @param parentElement the parent element
115
 * @param childElements the child elements to add
116
 */
117
public void add(Object parentElement, Object[] childElements) {
118
	Assert.isNotNull(parentElement);
119
	Assert.isNotNull(childElements);
120
	Widget widget = findItem(parentElement);
121
	// If parent hasn't been realized yet, just ignore the add.
122
	if (widget == null)
123
		return;
124
125
	Control tree = getControl();
126
	
127
	// optimization!
128
	// if the widget is not expanded we just invalidate the subtree
129
	if (widget instanceof Item) {
130
		Item ti= (Item) widget;
131
		if (!getExpanded(ti)) {
132
			boolean needDummy = isExpandable(parentElement);
133
			boolean haveDummy = false;
134
			// remove all children
135
			Item[] items= getItems(ti);
136
			for (int i = 0; i < items.length; i++) {
137
				if (items[i].getData() != null) {
138
					disassociate(items[i]);
139
					items[i].dispose();
140
				}
141
				else {
142
					if (needDummy && !haveDummy) {
143
						haveDummy = true;
144
					}
145
					else {
146
						items[i].dispose();
147
					}
148
				}
149
			}
150
			// append a dummy if necessary
151
			if (needDummy && !haveDummy) {
152
				newItem(ti, SWT.NULL, -1);
153
			} else {
154
				// XXX: Workaround (PR missing)
155
				tree.redraw();
156
			}	
157
			
158
			return;
159
		}
93
		}
160
	}
94
	}
161
95
162
	if (childElements.length > 0) {	
96
	/**
163
		List children = Arrays.asList(getSortedChildren(parentElement));
97
	 * Creates an abstract tree viewer. The viewer has no input, no content provider, a
164
		for (int cc = 0; cc < childElements.length; cc++) {
98
	 * default label provider, no sorter, no filters, and has auto-expand turned off.
165
			
99
	 */
166
			int ix = children.indexOf(childElements[cc]);
100
	protected AbstractTreeViewer() {
167
			if (ix < 0)	// child not found: ignore
168
				continue;
169
170
			Item[] ch = getChildren(widget);
171
			if (ch.length + 1 == children.size()) {
172
				createTreeItem(widget, childElements[cc], ix);	// insert child at position
173
				if (ch.length == 0) {
174
					//System.out.println("WORKAROUND setRedraw");
175
					tree.setRedraw(false);	// WORKAROUND
176
					tree.setRedraw(true);	// WORKAROUND
177
				}
178
				continue;
179
			}
180
			
181
			// couldn't handle this child:
182
			// skip other children and do general case
183
			// call refresh rather than internalRefresh to preserve selection
184
			refresh(parentElement);
185
			return;
186
		}
187
	}
101
	}
188
}
102
	/**
189
/**
103
		 * Return the current children of the parent element with the
190
 * Adds the given child element to this viewer as a child of the given parent element.
104
		 * newChildren added in. This method
191
 * If this viewer does not have a sorter, the element is added at the end of the 
105
		 * does not invoke the content provider but rather asks the
192
 * parent's list of children; otherwise, the element is inserted at the appropriate position.
106
		 * widget.
193
 * <p>
107
		 * @param parentElement
194
 * This method should be called (by the content provider) when a single element 
108
		 * @param newChildren Object[]. The children to be added to the
195
 * has been added to the model, in order to cause the viewer to accurately
109
		 *  view.
196
 * reflect the model. This method only affects the viewer, not the model.
110
		 * @return Collection
197
 * Note that there is another method for efficiently processing the simultaneous
111
		 */
198
 * addition of multiple elements.
112
	private Object[] getMergedChildren(
199
 * </p>
113
		Widget parentElement,
200
 *
114
		Object[] newChildren) {
201
 * @param parentElement the parent element
115
		Item[] items = getChildren(parentElement);
202
 * @param childElement the child element
116
		Object[] result = new Object[items.length + newChildren.length];
203
 */
117
		for (int i = 0; i < items.length; i++) {
204
public void add(Object parentElement, Object childElement) {
118
			result[i] = items[i].getData();
205
	add(parentElement, new Object[] { childElement });
119
		}
206
}
207
/**
208
 * Adds the given SWT selection listener to the given SWT control.
209
 *
210
 * @param control the SWT control
211
 * @param listener the SWT selection listener
212
 * 
213
 * @deprecated
214
 */
215
protected void addSelectionListener(Control control, SelectionListener listener) {}
216
/**
217
 * Adds a listener for expand and collapse events in this viewer.
218
 * Has no effect if an identical listener is already registered.
219
 *
220
 * @param listener a tree viewer listener
221
 */
222
public void addTreeListener(ITreeViewerListener listener) {
223
	treeListeners.add(listener);
224
}
225
/**
226
 * Adds the given SWT tree listener to the given SWT control.
227
 *
228
 * @param control the SWT control
229
 * @param listener the SWT tree listener
230
 */
231
protected abstract void addTreeListener(Control control, TreeListener listener);
232
120
233
/* (non-Javadoc)
121
		System.arraycopy(
234
 * @see StructuredViewer#associate(Object, Item)
122
			newChildren,
235
 */
123
			0,
236
protected void associate(Object element, Item item) {
124
			result,
237
	Object data = item.getData();
125
			items.length,
238
	if (data != null && data != element && equals(data, element)) {
126
			newChildren.length);
239
		// workaround for PR 1FV62BT
127
		return result;
240
		// assumption: elements are equal but not identical
241
		// -> remove from map but don't touch children
242
		unmapElement(data, item);
243
		item.setData(element);
244
		mapElement(element, item);
245
	} else {
246
		// recursively disassociate all
247
		super.associate(element, item);
248
	}
128
	}
249
}
250
129
251
/**
130
	/**
252
 * Collapses all nodes of the viewer's tree, starting with the root.
131
	 * Return the index of element in the array elements.
253
 * This method is equivalent to <code>collapseToLevel(ALL_LEVELS)</code>.
132
	 * @param element
254
 */
133
	 * @param elements
255
public void collapseAll() {
134
	 */
256
	collapseToLevel(getRoot(), ALL_LEVELS);
135
	private int indexOf(Object element, Object[] elements) {
257
}
136
		for (int i = 0; i < elements.length; i++) {
258
/**
137
			if (elements[i].equals(element))
259
 * Collapses the subtree rooted at the given element to the given level.
138
				return i;
260
 *
139
		}
261
 * @param element the element
140
		return -1;
262
 * @param level non-negative level, or <code>ALL_LEVELS</code> to collapse
263
 *  all levels of the tree
264
 */
265
public void collapseToLevel(Object element, int level) {
266
	Widget w = findItem(element);
267
	if (w != null)
268
		internalCollapseToLevel(w, level);
269
}
270
/**
271
 * Creates all children for the given widget.
272
 * <p>
273
 * The default implementation of this framework method assumes 
274
 * that <code>widget.getData()</code> returns the element corresponding
275
 * to the node. Note: the node is not visually expanded! You may have to 
276
 * call <code>parent.setExpanded(true)</code>.
277
 * </p>
278
 *
279
 * @param widget the widget
280
 */
281
protected void createChildren(final Widget widget) {
282
	final Item[] tis = getChildren(widget);
283
	if (tis != null && tis.length > 0) {
284
		Object data = tis[0].getData();
285
		if (data != null)
286
			return; // children already there!
287
	}
141
	}
142
	/**
143
	 * Adds the given child elements to this viewer as children of the given parent element.
144
	 * If this viewer does not have a sorter, the elements are added at the end of the 
145
	 * parent's list of children in the order given; otherwise, the elements are inserted
146
	 * at the appropriate positions.
147
	 * <p>
148
	 * This method should be called (by the content provider) when elements 
149
	 * have been added to the model, in order to cause the viewer to accurately
150
	 * reflect the model. This method only affects the viewer, not the model.
151
	 * </p>
152
	 *
153
	 * @param parentElement the parent element
154
	 * @param childElements the child elements to add
155
	 */
156
	public void add(Object parentElement, Object[] childElements) {
157
		Assert.isNotNull(parentElement);
158
		Assert.isNotNull(childElements);
159
		Widget widget = findItem(parentElement);
160
		// If parent hasn't been realized yet, just ignore the add.
161
		if (widget == null)
162
			return;
288
163
289
	BusyIndicator.showWhile(widget.getDisplay(), new Runnable() {
164
		Object[] newElements;
290
		public void run() {
165
		//If it is not an item we have no children to worry about
291
			// fix for PR 1FW89L7:
166
		if (widget instanceof Item)
292
			// don't complain and remove all "dummies" ...
167
			newElements =
293
			if (tis != null) {
168
				filter(getMergedChildren((Item) widget, childElements));
294
				for (int i = 0; i < tis.length; i++) {
169
		else
295
					tis[i].dispose();
170
			newElements = childElements;
296
				}
171
297
			}
172
		getSorter().sort(this, newElements);
298
			Object d = widget.getData();
173
299
			if (d != null) {
174
		Item[] ch = getChildren(widget);
300
				Object parentElement = d;
301
				Object[] children = getSortedChildren(parentElement);
302
				for (int i = 0; i < children.length; i++) {
303
					createTreeItem(widget, children[i], -1);
304
				}
305
			}
306
		}
307
	});	
308
}
309
/**
310
 * Creates a single item for the given parent and synchronizes it with
311
 * the given element.
312
 *
313
 * @param parent the parent widget
314
 * @param element the element
315
 * @param index if non-negative, indicates the position to insert the item 
316
 *    into its parent
317
 */
318
protected void createTreeItem(Widget parent, Object element, int index) {
319
	Item item = newItem(parent, SWT.NULL, index);
320
	updateItem(item, element);
321
	updatePlus(item, element);
322
}
323
/**
324
 * The <code>AbstractTreeViewer</code> implementation of this method
325
 * also recurses over children of the corresponding element.
326
 */
327
protected void disassociate(Item item) {
328
	super.disassociate(item);
329
	// recursively unmapping the items is only required when
330
	// the hash map is used. In the other case disposing
331
	// an item will recursively dispose its children.
332
	if (usingElementMap())
333
		disassociateChildren(item);
334
}
335
/**
336
 * Disassociates the children of the given SWT item from their
337
 * corresponding elements.
338
 *
339
 * @param item the widget
340
 */
341
private void disassociateChildren(Item item) {
342
	Item[] items = getChildren(item);
343
	for (int i = 0; i < items.length; i++) {
344
		if (items[i].getData() != null)
345
			disassociate(items[i]);
346
	}
347
}
348
/* (non-Javadoc)
349
 * Method declared on StructuredViewer.
350
 */
351
protected Widget doFindInputItem(Object element) {
352
	// compare with root
353
	Object root= getRoot();
354
	if (root == null)
355
		return null;
356
		
175
		
357
	if (equals(root, element))
176
		/**
358
		return getControl();
177
		 * Go through the new elements as we need to 
359
	return null;
178
		 * create them in order to avoid ArrayIndexOutOfBounds
360
}
179
		 * exceptions.
361
/* (non-Javadoc)
180
		 */
362
 * Method declared on StructuredViewer.
181
		for (int i = 0; i < newElements.length; i++) {
363
 */
182
364
protected Widget doFindItem(Object element) {
183
			//Check if this is a new one if not continue
365
	// compare with root
184
			if(indexOf(newElements[i],childElements) < 0)
366
	Object root= getRoot();
185
				continue;		
367
	if (root == null)
186
368
		return null;
187
			createTreeItem(widget, newElements[i], i);
369
						
188
			continue;
370
	Item[] items= getChildren(getControl());
371
	if (items != null) {
372
		for (int i= 0; i < items.length; i++) {
373
			Widget o= internalFindItem(items[i], element);
374
			if (o != null) 
375
				return o;
376
		}
189
		}
377
	}
190
	}
378
	return null;
191
	/**
379
}
192
	 * Adds the given child element to this viewer as a child of the given parent element.
380
/**
193
	 * If this viewer does not have a sorter, the element is added at the end of the 
381
 * Copies the attributes of the given element into the given SWT item.
194
	 * parent's list of children; otherwise, the element is inserted at the appropriate position.
382
 *
195
	 * <p>
383
 * @param item the SWT item
196
	 * This method should be called (by the content provider) when a single element 
384
 * @param element the element
197
	 * has been added to the model, in order to cause the viewer to accurately
385
 */
198
	 * reflect the model. This method only affects the viewer, not the model.
386
protected abstract void doUpdateItem(Item item, Object element);
199
	 * Note that there is another method for efficiently processing the simultaneous
387
/* (non-Javadoc)
200
	 * addition of multiple elements.
388
 * Method declared on StructuredViewer.
201
	 * </p>
389
 */
202
	 *
390
protected void doUpdateItem(Widget widget, Object element, boolean fullMap) {
203
	 * @param parentElement the parent element
391
	if (widget instanceof Item) {
204
	 * @param childElement the child element
392
		Item item = (Item) widget;
205
	 */
393
206
	public void add(Object parentElement, Object childElement) {
394
		// ensure that backpointer is correct
207
		add(parentElement, new Object[] { childElement });
395
		if (fullMap) {
208
	}
396
			associate(element, item);
209
	/**
397
		} else {
210
	 * Adds the given SWT selection listener to the given SWT control.
211
	 *
212
	 * @param control the SWT control
213
	 * @param listener the SWT selection listener
214
	 * 
215
	 * @deprecated
216
	 */
217
	protected void addSelectionListener(
218
		Control control,
219
		SelectionListener listener) {
220
	}
221
	/**
222
	 * Adds a listener for expand and collapse events in this viewer.
223
	 * Has no effect if an identical listener is already registered.
224
	 *
225
	 * @param listener a tree viewer listener
226
	 */
227
	public void addTreeListener(ITreeViewerListener listener) {
228
		treeListeners.add(listener);
229
	}
230
	/**
231
	 * Adds the given SWT tree listener to the given SWT control.
232
	 *
233
	 * @param control the SWT control
234
	 * @param listener the SWT tree listener
235
	 */
236
	protected abstract void addTreeListener(
237
		Control control,
238
		TreeListener listener);
239
240
	/* (non-Javadoc)
241
	 * @see StructuredViewer#associate(Object, Item)
242
	 */
243
	protected void associate(Object element, Item item) {
244
		Object data = item.getData();
245
		if (data != null && data != element && equals(data, element)) {
246
			// workaround for PR 1FV62BT
247
			// assumption: elements are equal but not identical
248
			// -> remove from map but don't touch children
249
			unmapElement(data, item);
398
			item.setData(element);
250
			item.setData(element);
399
			mapElement(element, item);
251
			mapElement(element, item);
252
		} else {
253
			// recursively disassociate all
254
			super.associate(element, item);
400
		}
255
		}
256
	}
401
257
402
		// update icon and label
258
	/**
403
		safeUpdateItem.item = item;
259
	 * Collapses all nodes of the viewer's tree, starting with the root.
404
		safeUpdateItem.element = element;
260
	 * This method is equivalent to <code>collapseToLevel(ALL_LEVELS)</code>.
405
		try {
261
	 */
406
			Platform.run(safeUpdateItem);
262
	public void collapseAll() {
407
		}
263
		collapseToLevel(getRoot(), ALL_LEVELS);
408
		finally {
409
			safeUpdateItem.item = null;
410
			safeUpdateItem.element = null;
411
		}
412
	}
264
	}
413
}
265
	/**
414
/**
266
	 * Collapses the subtree rooted at the given element to the given level.
415
 * Expands all nodes of the viewer's tree, starting with the root.
267
	 *
416
 * This method is equivalent to <code>expandToLevel(ALL_LEVELS)</code>.
268
	 * @param element the element
417
 */
269
	 * @param level non-negative level, or <code>ALL_LEVELS</code> to collapse
418
public void expandAll() {
270
	 *  all levels of the tree
419
	expandToLevel(ALL_LEVELS);
271
	 */
420
}
272
	public void collapseToLevel(Object element, int level) {
421
/**
273
		Widget w = findItem(element);
422
 * Expands the root of the viewer's tree to the given level.
274
		if (w != null)
423
 *
275
			internalCollapseToLevel(w, level);
424
 * @param level non-negative level, or <code>ALL_LEVELS</code> to expand
425
 *  all levels of the tree
426
 */
427
public void expandToLevel(int level) {
428
	expandToLevel(getRoot(), level);
429
}
430
/**
431
 * Expands all ancestors of the given element so that the given element
432
 * becomes visible in this viewer's tree control, and then expands the
433
 * subtree rooted at the given element to the given level.
434
 *
435
 * @param element the element
436
 * @param level non-negative level, or <code>ALL_LEVELS</code> to expand
437
 *  all levels of the tree
438
 */
439
public void expandToLevel(Object element, int level) {
440
	Widget w = internalExpand(element, true);
441
	if (w != null)
442
		internalExpandToLevel(w, level);
443
}
444
/**
445
 * Fires a tree collapsed event.
446
 * Only listeners registered at the time this method is called are notified.
447
 *
448
 * @param event the tree expansion event
449
 *
450
 * @see ITreeViewerListener#treeCollapsed
451
 */
452
protected void fireTreeCollapsed(final TreeExpansionEvent event) {
453
	Object[] listeners = treeListeners.getListeners();
454
	for (int i = 0; i < listeners.length; ++i) {
455
		final ITreeViewerListener l = (ITreeViewerListener)listeners[i];
456
		Platform.run(new SafeRunnable() {
457
			public void run() {
458
				l.treeCollapsed(event);
459
			}
460
			public void handleException(Throwable e) {
461
				super.handleException(e);
462
				//If and unexpected exception happens, remove it
463
				//to make sure the application keeps running.
464
				removeTreeListener(l);
465
			}
466
		});	
467
	}
276
	}
468
}
277
	/**
469
/**
278
	 * Creates all children for the given widget.
470
 * Fires a tree expanded event.
279
	 * <p>
471
 * Only listeners registered at the time this method is called are notified.
280
	 * The default implementation of this framework method assumes 
472
 *
281
	 * that <code>widget.getData()</code> returns the element corresponding
473
 * @param event the tree expansion event
282
	 * to the node. Note: the node is not visually expanded! You may have to 
474
 *
283
	 * call <code>parent.setExpanded(true)</code>.
475
 * @see ITreeViewerListener#treeExpanded
284
	 * </p>
476
 */
285
	 *
477
protected void fireTreeExpanded(final TreeExpansionEvent event) {
286
	 * @param widget the widget
478
	Object[] listeners = treeListeners.getListeners();
287
	 */
479
	for (int i = 0; i < listeners.length; ++i) {
288
	protected void createChildren(final Widget widget) {
480
		final ITreeViewerListener l = (ITreeViewerListener)listeners[i];
289
		final Item[] tis = getChildren(widget);
481
		Platform.run(new SafeRunnable() {
290
		if (tis != null && tis.length > 0) {
291
			Object data = tis[0].getData();
292
			if (data != null)
293
				return; // children already there!
294
		}
295
296
		BusyIndicator.showWhile(widget.getDisplay(), new Runnable() {
482
			public void run() {
297
			public void run() {
483
				l.treeExpanded(event);
298
				// fix for PR 1FW89L7:
299
				// don't complain and remove all "dummies" ...
300
				if (tis != null) {
301
					for (int i = 0; i < tis.length; i++) {
302
						tis[i].dispose();
303
					}
304
				}
305
				Object d = widget.getData();
306
				if (d != null) {
307
					Object parentElement = d;
308
					Object[] children = getSortedChildren(parentElement);
309
					for (int i = 0; i < children.length; i++) {
310
						createTreeItem(widget, children[i], -1);
311
					}
312
				}
484
			}
313
			}
485
			public void handleException(Throwable e) {
314
		});
486
				super.handleException(e);
487
				//If and unexpected exception happens, remove it
488
				//to make sure the application keeps running.
489
				removeTreeListener(l);
490
			}
491
		});	
492
	}	
493
	
494
}
495
/**
496
 * Returns the auto-expand level.
497
 *
498
 * @return non-negative level, or <code>ALL_LEVELS</code> if
499
 *  all levels of the tree are expanded automatically
500
 * @see #setAutoExpandLevel
501
 */
502
public int getAutoExpandLevel() {
503
	return expandToLevel;
504
}
505
/**
506
 * Returns the SWT child items for the given SWT widget.
507
 *
508
 * @param widget the widget
509
 * @return the child items
510
 */
511
protected abstract Item[] getChildren(Widget widget);
512
/**
513
 * Returns whether the given SWT item is expanded or collapsed.
514
 *
515
 * @param item the item
516
 * @return <code>true</code> if the item is considered expanded
517
 * and <code>false</code> if collapsed
518
 */
519
protected abstract boolean getExpanded(Item item);
520
/**
521
 * Returns a list of elements corresponding to expanded nodes in this
522
 * viewer's tree, including currently hidden ones that are marked as
523
 * expanded but are under a collapsed ancestor.
524
 * <p>
525
 * This method is typically used when preserving the interesting
526
 * state of a viewer; <code>setExpandedElements</code> is used during the restore.
527
 * </p>
528
 *
529
 * @return the array of expanded elements
530
 *
531
 * @see #setExpandedElements
532
 */
533
public Object[] getExpandedElements() {
534
	ArrayList v= new ArrayList();
535
	internalCollectExpanded(v, getControl());
536
	return v.toArray();
537
}
538
/**
539
 * Returns whether the node corresponding to the given element is expanded or collapsed.
540
 *
541
 * @param element the element
542
 * @return <code>true</code> if the node is expanded, and <code>false</code> if collapsed
543
 */
544
public boolean getExpandedState(Object element) {
545
	Widget item = findItem(element);
546
	if (item instanceof Item)
547
		return getExpanded((Item) item);
548
	return false;
549
}
550
/**
551
 * Returns the number of child items of the given SWT control.
552
 *
553
 * @param control the control
554
 * @return the number of children
555
 */
556
protected abstract int getItemCount(Control control);
557
/**
558
 * Returns the number of child items of the given SWT item.
559
 *
560
 * @param item the item
561
 * @return the number of children
562
 */
563
protected abstract int getItemCount(Item item);
564
/**
565
 * Returns the child items of the given SWT item.
566
 *
567
 * @param item the item
568
 * @return the child items
569
 */
570
protected abstract Item[] getItems(Item item);
571
/**
572
 * Returns the item after the given item in the tree, or
573
 * <code>null</code> if there is no next item.
574
 *
575
 * @param item the item
576
 * @param includeChildren <code>true</code> if the children are 
577
 *  considered in determining which item is next, and <code>false</code>
578
 *  if subtrees are ignored
579
 * @return the next item, or <code>null</code> if none
580
 */
581
protected Item getNextItem(Item item, boolean includeChildren) {
582
	if (item == null) {
583
		return null;
584
	}
315
	}
585
	if (includeChildren && getExpanded(item)) {
316
	/**
586
		Item[] children = getItems(item);
317
	 * Creates a single item for the given parent and synchronizes it with
587
		if (children != null && children.length > 0) {
318
	 * the given element.
588
			return children[0];
319
	 *
320
	 * @param parent the parent widget
321
	 * @param element the element
322
	 * @param index if non-negative, indicates the position to insert the item 
323
	 *    into its parent
324
	 */
325
	protected void createTreeItem(Widget parent, Object element, int index) {
326
		Item item = newItem(parent, SWT.NULL, index);
327
		updateItem(item, element);
328
		updatePlus(item, element);
329
	}
330
	/**
331
	 * The <code>AbstractTreeViewer</code> implementation of this method
332
	 * also recurses over children of the corresponding element.
333
	 */
334
	protected void disassociate(Item item) {
335
		super.disassociate(item);
336
		// recursively unmapping the items is only required when
337
		// the hash map is used. In the other case disposing
338
		// an item will recursively dispose its children.
339
		if (usingElementMap())
340
			disassociateChildren(item);
341
	}
342
	/**
343
	 * Disassociates the children of the given SWT item from their
344
	 * corresponding elements.
345
	 *
346
	 * @param item the widget
347
	 */
348
	private void disassociateChildren(Item item) {
349
		Item[] items = getChildren(item);
350
		for (int i = 0; i < items.length; i++) {
351
			if (items[i].getData() != null)
352
				disassociate(items[i]);
589
		}
353
		}
590
	}
354
	}
355
	/* (non-Javadoc)
356
	 * Method declared on StructuredViewer.
357
	 */
358
	protected Widget doFindInputItem(Object element) {
359
		// compare with root
360
		Object root = getRoot();
361
		if (root == null)
362
			return null;
591
363
592
	//next item is either next sibling or next sibling of first
364
		if (equals(root, element))
593
	//parent that has a next sibling.
365
			return getControl();
594
	Item parent = getParentItem(item);
595
	if (parent == null) {
596
		return null;
366
		return null;
597
	}
367
	}
598
	Item[] siblings = getItems(parent);
368
	/* (non-Javadoc)
599
	if (siblings != null && siblings.length <= 1) {
369
	 * Method declared on StructuredViewer.
600
		return getNextItem(parent, false);
370
	 */
601
	}
371
	protected Widget doFindItem(Object element) {
602
	for (int i = 0; i < siblings.length; i++) {
372
		// compare with root
603
		if (siblings[i] == item && i < (siblings.length - 1)) {
373
		Object root = getRoot();
604
			return siblings[i+1];
374
		if (root == null)
375
			return null;
376
377
		Item[] items = getChildren(getControl());
378
		if (items != null) {
379
			for (int i = 0; i < items.length; i++) {
380
				Widget o = internalFindItem(items[i], element);
381
				if (o != null)
382
					return o;
383
			}
605
		}
384
		}
606
	}
607
	return getNextItem(parent, false);
608
}
609
/**
610
 * Returns the parent item of the given item in the tree, or
611
 * <code>null</code> if there is parent item.
612
 *
613
 * @param item the item
614
 * @return the parent item, or <code>null</code> if none
615
 */
616
protected abstract Item getParentItem(Item item);
617
/**
618
 * Returns the item before the given item in the tree, or
619
 * <code>null</code> if there is no previous item.
620
 *
621
 * @param item the item
622
 * @return the previous item, or <code>null</code> if none
623
 */
624
protected Item getPreviousItem(Item item) {
625
	//previous item is either right-most visible descendent of previous 
626
	//sibling or parent
627
	Item parent = getParentItem(item);
628
	if (parent == null) {
629
		return null;
385
		return null;
630
	}
386
	}
631
	Item[] siblings = getItems(parent);
387
	/**
632
	if (siblings.length == 0 || siblings[0] == item) {
388
	 * Copies the attributes of the given element into the given SWT item.
633
		return parent;
389
	 *
634
	}
390
	 * @param item the SWT item
635
	Item previous = siblings[0];
391
	 * @param element the element
636
	for (int i = 1; i < siblings.length; i++) {
392
	 */
637
		if (siblings[i] == item) {
393
	protected abstract void doUpdateItem(Item item, Object element);
638
			return rightMostVisibleDescendent(previous);
394
	/* (non-Javadoc)
639
		}
395
	 * Method declared on StructuredViewer.
640
		previous = siblings[i];
396
	 */
641
	}
397
	protected void doUpdateItem(
642
	return null;
398
		Widget widget,
643
}
399
		Object element,
644
/* (non-Javadoc)
400
		boolean fullMap) {
645
 * Method declared on StructuredViewer.
401
		if (widget instanceof Item) {
646
 */
402
			Item item = (Item) widget;
647
protected Object[] getRawChildren(Object parent) {
403
648
	if (parent != null) {
404
			// ensure that backpointer is correct
649
		if (equals(parent, getRoot()))
405
			if (fullMap) {
650
			return super.getRawChildren(parent);
406
				associate(element, item);
651
		ITreeContentProvider cp = (ITreeContentProvider) getContentProvider();
407
			} else {
652
		if (cp != null) {
408
				item.setData(element);
653
			Object[] result = cp.getChildren(parent);
409
				mapElement(element, item);
654
			if (result != null)
410
			}
655
				return result;
411
412
			// update icon and label
413
			safeUpdateItem.item = item;
414
			safeUpdateItem.element = element;
415
			try {
416
				Platform.run(safeUpdateItem);
417
			} finally {
418
				safeUpdateItem.item = null;
419
				safeUpdateItem.element = null;
420
			}
656
		}
421
		}
657
	}
422
	}
658
	return new Object[0];
423
	/**
659
}
424
	 * Expands all nodes of the viewer's tree, starting with the root.
660
/**
425
	 * This method is equivalent to <code>expandToLevel(ALL_LEVELS)</code>.
661
 * Returns all selected items for the given SWT control.
426
	 */
662
 *
427
	public void expandAll() {
663
 * @param control the control
428
		expandToLevel(ALL_LEVELS);
664
 * @return the list of selected items
665
 */
666
protected abstract Item[] getSelection(Control control);
667
/* (non-Javadoc)
668
 * Method declared on StructuredViewer.
669
 */
670
protected List getSelectionFromWidget() {
671
	Widget[] items = getSelection(getControl());
672
	ArrayList list = new ArrayList(items.length);
673
	for (int i = 0; i < items.length; i++) {
674
		Widget item = items[i];
675
		Object e = item.getData();
676
		if (e != null)
677
			list.add(e);
678
	}
429
	}
679
	return list;
430
	/**
680
}
431
	 * Expands the root of the viewer's tree to the given level.
681
/**
432
	 *
682
 * Handles a tree collapse event from the SWT widget.
433
	 * @param level non-negative level, or <code>ALL_LEVELS</code> to expand
683
 *
434
	 *  all levels of the tree
684
 * @param event the SWT tree event
435
	 */
685
 */
436
	public void expandToLevel(int level) {
686
protected void handleTreeCollapse(TreeEvent event) {
437
		expandToLevel(getRoot(), level);
687
	if (event.item.getData() != null) {
688
		fireTreeCollapsed(new TreeExpansionEvent(this, event.item.getData()));
689
	}
438
	}
690
}
439
	/**
691
/**
440
	 * Expands all ancestors of the given element so that the given element
692
 * Handles a tree expand event from the SWT widget.
441
	 * becomes visible in this viewer's tree control, and then expands the
693
 *
442
	 * subtree rooted at the given element to the given level.
694
 * @param event the SWT tree event
443
	 *
695
 */
444
	 * @param element the element
696
protected void handleTreeExpand(TreeEvent event) {
445
	 * @param level non-negative level, or <code>ALL_LEVELS</code> to expand
697
	createChildren(event.item);
446
	 *  all levels of the tree
698
	if (event.item.getData() != null) {
447
	 */
699
		fireTreeExpanded(new TreeExpansionEvent(this, event.item.getData()));
448
	public void expandToLevel(Object element, int level) {
700
	}
449
		Widget w = internalExpand(element, true);
701
}
450
		if (w != null)
702
/* (non-Javadoc)
451
			internalExpandToLevel(w, level);
703
 * Method declared on Viewer.
704
 */
705
protected void hookControl(Control control) {
706
	super.hookControl(control);
707
	addTreeListener(control,new TreeListener() {
708
		public void treeExpanded(TreeEvent event) {
709
			handleTreeExpand(event);
710
		}
711
		public void treeCollapsed(TreeEvent event) {
712
			handleTreeCollapse(event);
713
		}
714
	});
715
}
716
/* (non-Javadoc)
717
 * Method declared on StructuredViewer.
718
 * Builds the initial tree and handles the automatic expand feature.
719
 */
720
protected void inputChanged(Object input, Object oldInput) {
721
	preservingSelection(new Runnable() {
722
		public void run() {
723
			Control tree = getControl();
724
		    boolean useRedraw = true; // (size > REDRAW_THRESHOLD) || (table.getItemCount() > REDRAW_THRESHOLD);
725
		    if (useRedraw)
726
		        tree.setRedraw(false);
727
			removeAll(tree);
728
			tree.setData(getRoot());
729
			createChildren(tree);
730
			internalExpandToLevel(tree, expandToLevel);
731
		    if (useRedraw)
732
		        tree.setRedraw(true);
733
		}
734
	});
735
}
736
/**
737
 * Recursively collapses the subtree rooted at the given widget to the
738
 * given level.
739
 * <p>
740
 * </p>
741
 * Note that the default implementation of this method does not call
742
 * <code>setRedraw</code>.
743
 *
744
 * @param widget the widget
745
 * @param level non-negative level, or <code>ALL_LEVELS</code> to collapse
746
 *  all levels of the tree
747
 */
748
protected void internalCollapseToLevel(Widget widget, int level) {
749
	if (level == ALL_LEVELS || level > 0) {
750
751
		if (widget instanceof Item)
752
			setExpanded((Item) widget, false);
753
754
		if (level == ALL_LEVELS || level > 1) {
755
			Item[] children = getChildren(widget);
756
			if (children != null) {
757
				int nextLevel = (level == ALL_LEVELS ? ALL_LEVELS : level - 1);
758
				for (int i = 0; i < children.length; i++)
759
					internalCollapseToLevel(children[i], nextLevel);
760
			}
761
		}
762
	}
452
	}
763
}
453
	/**
764
/**
454
	 * Fires a tree collapsed event.
765
 * Recursively collects all expanded elements from the given widget.
455
	 * Only listeners registered at the time this method is called are notified.
766
 *
456
	 *
767
 * @param result a list (element type: <code>Object</code>) into which
457
	 * @param event the tree expansion event
768
 *    to collect the elements
458
	 *
769
 * @param widget the widget
459
	 * @see ITreeViewerListener#treeCollapsed
770
 */
460
	 */
771
private void internalCollectExpanded(List result, Widget widget) {
461
	protected void fireTreeCollapsed(final TreeExpansionEvent event) {
772
	Item[] items = getChildren(widget);
462
		Object[] listeners = treeListeners.getListeners();
773
	for (int i = 0; i < items.length; i++) {
463
		for (int i = 0; i < listeners.length; ++i) {
774
		Item item = items[i];
464
			final ITreeViewerListener l = (ITreeViewerListener) listeners[i];
775
		if (getExpanded(item)) {
465
			Platform.run(new SafeRunnable() {
776
			Object data = item.getData();
466
				public void run() {
777
			if (data != null)
467
					l.treeCollapsed(event);
778
				result.add(data);
468
				}
469
				public void handleException(Throwable e) {
470
					super.handleException(e);
471
					//If and unexpected exception happens, remove it
472
					//to make sure the application keeps running.
473
					removeTreeListener(l);
474
				}
475
			});
779
		}
476
		}
780
		internalCollectExpanded(result, item);
781
	}
477
	}
782
}
478
	/**
783
/**
479
	 * Fires a tree expanded event.
784
 * Tries to create a path of tree items for the given element.
480
	 * Only listeners registered at the time this method is called are notified.
785
 * This method recursively walks up towards the root of the tree
481
	 *
786
 * and assumes that <code>getParent</code> returns the correct
482
	 * @param event the tree expansion event
787
 * parent of an element.
483
	 *
788
 *
484
	 * @see ITreeViewerListener#treeExpanded
789
 * @param element the element
485
	 */
790
 * @param expand <code>true</code> if all nodes on the path should be expanded, 
486
	protected void fireTreeExpanded(final TreeExpansionEvent event) {
791
 *    and <code>false</code> otherwise
487
		Object[] listeners = treeListeners.getListeners();
792
 */
488
		for (int i = 0; i < listeners.length; ++i) {
793
protected Widget internalExpand(Object element, boolean expand) {
489
			final ITreeViewerListener l = (ITreeViewerListener) listeners[i];
794
490
			Platform.run(new SafeRunnable() {
795
	if (element == null)
491
				public void run() {
796
		return null;
492
					l.treeExpanded(event);
797
798
	Widget w= findItem(element);
799
	if (w == null) {	
800
		if (equals(element, getRoot())) {	// stop at root
801
			return null;
802
		}
803
		// my parent has to create me
804
		ITreeContentProvider cp = (ITreeContentProvider) getContentProvider();
805
		if (cp == null) {
806
			return null;
807
		}
808
		Object parent= cp.getParent(element);
809
		if (parent != null) {
810
			Widget pw= internalExpand(parent, expand);
811
			if (pw != null) {
812
				// let my parent create me
813
				createChildren(pw);
814
				// expand parent and find me
815
				if (pw instanceof Item) {
816
					Item item= (Item)pw;
817
					if (expand)
818
						setExpanded(item, true);
819
					w= internalFindChild(item, element);
820
				}
493
				}
821
			}
494
				public void handleException(Throwable e) {
495
					super.handleException(e);
496
					//If and unexpected exception happens, remove it
497
					//to make sure the application keeps running.
498
					removeTreeListener(l);
499
				}
500
			});
822
		}
501
		}
502
823
	}
503
	}
824
	return w;
504
	/**
825
}
505
	 * Returns the auto-expand level.
826
/**
506
	 *
827
 * Recursively expands the subtree rooted at the given widget to the
507
	 * @return non-negative level, or <code>ALL_LEVELS</code> if
828
 * given level.
508
	 *  all levels of the tree are expanded automatically
829
 * <p>
509
	 * @see #setAutoExpandLevel
830
 * </p>
510
	 */
831
 * Note that the default implementation of this method does not call
511
	public int getAutoExpandLevel() {
832
 * <code>setRedraw</code>.
512
		return expandToLevel;
833
 *
834
 * @param widget the widget
835
 * @param level non-negative level, or <code>ALL_LEVELS</code> to collapse
836
 *  all levels of the tree
837
 */
838
protected void internalExpandToLevel(Widget widget, int level) {
839
	if (level == ALL_LEVELS || level > 0) {
840
		createChildren(widget);
841
		if (widget instanceof Item)
842
			setExpanded((Item) widget, true);
843
		if (level == ALL_LEVELS || level > 1) {
844
			Item[] children = getChildren(widget);
845
			if (children != null) {
846
				int newLevel = (level == ALL_LEVELS ? ALL_LEVELS : level - 1);
847
				for (int i = 0; i < children.length; i++)
848
					internalExpandToLevel(children[i], newLevel);
849
			}
850
		}
851
	}
513
	}
852
}
514
	/**
853
/**
515
	 * Returns the SWT child items for the given SWT widget.
854
 * Non-recursively tries to find the given element as a child of the given parent item.
516
	 *
855
 *
517
	 * @param widget the widget
856
 * @param parent the parent item
518
	 * @return the child items
857
 * @param element the element
519
	 */
858
 */
520
	protected abstract Item[] getChildren(Widget widget);
859
private Widget internalFindChild(Item parent, Object element) {
521
	/**
860
	Item[] items = getChildren(parent);
522
	 * Returns whether the given SWT item is expanded or collapsed.
861
	for (int i = 0; i < items.length; i++) {
523
	 *
862
		Item item = items[i];
524
	 * @param item the item
863
		Object data = item.getData();
525
	 * @return <code>true</code> if the item is considered expanded
864
		if (data != null && equals(data, element))
526
	 * and <code>false</code> if collapsed
865
			return item;
527
	 */
528
	protected abstract boolean getExpanded(Item item);
529
	/**
530
	 * Returns a list of elements corresponding to expanded nodes in this
531
	 * viewer's tree, including currently hidden ones that are marked as
532
	 * expanded but are under a collapsed ancestor.
533
	 * <p>
534
	 * This method is typically used when preserving the interesting
535
	 * state of a viewer; <code>setExpandedElements</code> is used during the restore.
536
	 * </p>
537
	 *
538
	 * @return the array of expanded elements
539
	 *
540
	 * @see #setExpandedElements
541
	 */
542
	public Object[] getExpandedElements() {
543
		ArrayList v = new ArrayList();
544
		internalCollectExpanded(v, getControl());
545
		return v.toArray();
866
	}
546
	}
867
	return null;
547
	/**
868
}
548
	 * Returns whether the node corresponding to the given element is expanded or collapsed.
869
/**
549
	 *
870
 * Recursively tries to find the given element.
550
	 * @param element the element
871
 *
551
	 * @return <code>true</code> if the node is expanded, and <code>false</code> if collapsed
872
 * @param parent the parent item
552
	 */
873
 * @param element the element
553
	public boolean getExpandedState(Object element) {
874
 */
554
		Widget item = findItem(element);
875
private Widget internalFindItem(Item parent, Object element) {
555
		if (item instanceof Item)
556
			return getExpanded((Item) item);
557
		return false;
558
	}
559
	/**
560
	 * Returns the number of child items of the given SWT control.
561
	 *
562
	 * @param control the control
563
	 * @return the number of children
564
	 */
565
	protected abstract int getItemCount(Control control);
566
	/**
567
	 * Returns the number of child items of the given SWT item.
568
	 *
569
	 * @param item the item
570
	 * @return the number of children
571
	 */
572
	protected abstract int getItemCount(Item item);
573
	/**
574
	 * Returns the child items of the given SWT item.
575
	 *
576
	 * @param item the item
577
	 * @return the child items
578
	 */
579
	protected abstract Item[] getItems(Item item);
580
	/**
581
	 * Returns the item after the given item in the tree, or
582
	 * <code>null</code> if there is no next item.
583
	 *
584
	 * @param item the item
585
	 * @param includeChildren <code>true</code> if the children are 
586
	 *  considered in determining which item is next, and <code>false</code>
587
	 *  if subtrees are ignored
588
	 * @return the next item, or <code>null</code> if none
589
	 */
590
	protected Item getNextItem(Item item, boolean includeChildren) {
591
		if (item == null) {
592
			return null;
593
		}
594
		if (includeChildren && getExpanded(item)) {
595
			Item[] children = getItems(item);
596
			if (children != null && children.length > 0) {
597
				return children[0];
598
			}
599
		}
876
600
877
	// compare with node
601
		//next item is either next sibling or next sibling of first
878
	Object data= parent.getData();
602
		//parent that has a next sibling.
879
	if (data != null) {
603
		Item parent = getParentItem(item);
880
		if (equals(data, element))
604
		if (parent == null) {
605
			return null;
606
		}
607
		Item[] siblings = getItems(parent);
608
		if (siblings != null && siblings.length <= 1) {
609
			return getNextItem(parent, false);
610
		}
611
		for (int i = 0; i < siblings.length; i++) {
612
			if (siblings[i] == item && i < (siblings.length - 1)) {
613
				return siblings[i + 1];
614
			}
615
		}
616
		return getNextItem(parent, false);
617
	}
618
	/**
619
	 * Returns the parent item of the given item in the tree, or
620
	 * <code>null</code> if there is parent item.
621
	 *
622
	 * @param item the item
623
	 * @return the parent item, or <code>null</code> if none
624
	 */
625
	protected abstract Item getParentItem(Item item);
626
	/**
627
	 * Returns the item before the given item in the tree, or
628
	 * <code>null</code> if there is no previous item.
629
	 *
630
	 * @param item the item
631
	 * @return the previous item, or <code>null</code> if none
632
	 */
633
	protected Item getPreviousItem(Item item) {
634
		//previous item is either right-most visible descendent of previous 
635
		//sibling or parent
636
		Item parent = getParentItem(item);
637
		if (parent == null) {
638
			return null;
639
		}
640
		Item[] siblings = getItems(parent);
641
		if (siblings.length == 0 || siblings[0] == item) {
881
			return parent;
642
			return parent;
643
		}
644
		Item previous = siblings[0];
645
		for (int i = 1; i < siblings.length; i++) {
646
			if (siblings[i] == item) {
647
				return rightMostVisibleDescendent(previous);
648
			}
649
			previous = siblings[i];
650
		}
651
		return null;
882
	}
652
	}
883
	// recurse over children
653
	/* (non-Javadoc)
884
	Item[] items= getChildren(parent);
654
	 * Method declared on StructuredViewer.
885
	for (int i= 0; i < items.length; i++) {
655
	 */
886
		Item item= items[i];
656
	protected Object[] getRawChildren(Object parent) {
887
		Widget o= internalFindItem(item, element);
657
		if (parent != null) {
888
		if (o != null)
658
			if (equals(parent, getRoot()))
889
			return o;
659
				return super.getRawChildren(parent);
890
	}
660
			ITreeContentProvider cp =
891
	return null;
661
				(ITreeContentProvider) getContentProvider();
892
}
662
			if (cp != null) {
893
/* (non-Javadoc)
663
				Object[] result = cp.getChildren(parent);
894
 * Method declared on StructuredViewer.
664
				if (result != null)
895
 */
665
					return result;
896
protected void internalRefresh(Object element) {
666
			}
897
	internalRefresh(element, true);
667
		}
898
}
668
		return new Object[0];
899
/* (non-Javadoc)
900
 * Method declared on StructuredViewer.
901
 */
902
protected void internalRefresh(Object element, boolean updateLabels) {
903
	// If element is null, do a full refresh.
904
	if (element == null) {
905
		internalRefresh(getControl(), getRoot(), true, updateLabels);
906
		return;
907
	}
908
	Widget item = findItem(element);
909
	if (item != null) {
910
		// pick up structure changes too
911
		internalRefresh(item, element, true, updateLabels);
912
	}
669
	}
913
}
670
	/**
914
/**
671
	 * Returns all selected items for the given SWT control.
915
 * Refreshes the tree starting at the given widget.
672
	 *
916
 *
673
	 * @param control the control
917
 * @param widget the widget
674
	 * @return the list of selected items
918
 * @param element the element
675
	 */
919
 * @param doStruct <code>true</code> if structural changes are to be picked up,
676
	protected abstract Item[] getSelection(Control control);
920
 *   and <code>false</code> if only label provider changes are of interest
677
	/* (non-Javadoc)
921
 * @param updateLabels <code>true</code> to update labels for existing elements,
678
	 * Method declared on StructuredViewer.
922
 *   <code>false</code> to only update labels as needed, assuming that labels
679
	 */
923
 *   for existing elements are unchanged.
680
	protected List getSelectionFromWidget() {
924
 */
681
		Widget[] items = getSelection(getControl());
925
private void internalRefresh(Widget widget, Object element, boolean doStruct, boolean updateLabels) {
682
		ArrayList list = new ArrayList(items.length);
926
	
683
		for (int i = 0; i < items.length; i++) {
927
	if (widget instanceof Item) {
684
			Widget item = items[i];
928
		if (doStruct) {
685
			Object e = item.getData();
929
			updatePlus((Item)widget, element);
686
			if (e != null)
930
		}	
687
				list.add(e);
931
		if (updateLabels || !equals(element, widget.getData())) {
932
			doUpdateItem(widget, element, true);
933
		}
688
		}
934
		else {
689
		return list;
935
			associate(element, (Item) widget);
690
	}
691
	/**
692
	 * Handles a tree collapse event from the SWT widget.
693
	 *
694
	 * @param event the SWT tree event
695
	 */
696
	protected void handleTreeCollapse(TreeEvent event) {
697
		if (event.item.getData() != null) {
698
			fireTreeCollapsed(
699
				new TreeExpansionEvent(this, event.item.getData()));
936
		}
700
		}
937
	}
701
	}
702
	/**
703
	 * Handles a tree expand event from the SWT widget.
704
	 *
705
	 * @param event the SWT tree event
706
	 */
707
	protected void handleTreeExpand(TreeEvent event) {
708
		createChildren(event.item);
709
		if (event.item.getData() != null) {
710
			fireTreeExpanded(
711
				new TreeExpansionEvent(this, event.item.getData()));
712
		}
713
	}
714
	/* (non-Javadoc)
715
	 * Method declared on Viewer.
716
	 */
717
	protected void hookControl(Control control) {
718
		super.hookControl(control);
719
		addTreeListener(control, new TreeListener() {
720
			public void treeExpanded(TreeEvent event) {
721
				handleTreeExpand(event);
722
			}
723
			public void treeCollapsed(TreeEvent event) {
724
				handleTreeCollapse(event);
725
			}
726
		});
727
	}
728
	/* (non-Javadoc)
729
	 * Method declared on StructuredViewer.
730
	 * Builds the initial tree and handles the automatic expand feature.
731
	 */
732
	protected void inputChanged(Object input, Object oldInput) {
733
		preservingSelection(new Runnable() {
734
			public void run() {
735
				Control tree = getControl();
736
				boolean useRedraw = true;
737
				// (size > REDRAW_THRESHOLD) || (table.getItemCount() > REDRAW_THRESHOLD);
738
				if (useRedraw)
739
					tree.setRedraw(false);
740
				removeAll(tree);
741
				tree.setData(getRoot());
742
				createChildren(tree);
743
				internalExpandToLevel(tree, expandToLevel);
744
				if (useRedraw)
745
					tree.setRedraw(true);
746
			}
747
		});
748
	}
749
	/**
750
	 * Recursively collapses the subtree rooted at the given widget to the
751
	 * given level.
752
	 * <p>
753
	 * </p>
754
	 * Note that the default implementation of this method does not call
755
	 * <code>setRedraw</code>.
756
	 *
757
	 * @param widget the widget
758
	 * @param level non-negative level, or <code>ALL_LEVELS</code> to collapse
759
	 *  all levels of the tree
760
	 */
761
	protected void internalCollapseToLevel(Widget widget, int level) {
762
		if (level == ALL_LEVELS || level > 0) {
763
764
			if (widget instanceof Item)
765
				setExpanded((Item) widget, false);
938
766
939
	if (doStruct) {
767
			if (level == ALL_LEVELS || level > 1) {
940
		internalRefreshStruct(widget, element, updateLabels);
768
				Item[] children = getChildren(widget);
769
				if (children != null) {
770
					int nextLevel =
771
						(level == ALL_LEVELS ? ALL_LEVELS : level - 1);
772
					for (int i = 0; i < children.length; i++)
773
						internalCollapseToLevel(children[i], nextLevel);
774
				}
775
			}
776
		}
941
	}
777
	}
942
	else {
778
	/**
943
		Item[] children= getChildren(widget);
779
	 * Recursively collects all expanded elements from the given widget.
944
		if (children != null) {
780
	 *
945
			for (int i= 0; i < children.length; i++) {
781
	 * @param result a list (element type: <code>Object</code>) into which
946
				Widget item= children[i];
782
	 *    to collect the elements
947
				Object data= item.getData();
783
	 * @param widget the widget
784
	 */
785
	private void internalCollectExpanded(List result, Widget widget) {
786
		Item[] items = getChildren(widget);
787
		for (int i = 0; i < items.length; i++) {
788
			Item item = items[i];
789
			if (getExpanded(item)) {
790
				Object data = item.getData();
948
				if (data != null)
791
				if (data != null)
949
					internalRefresh(item, data, doStruct, updateLabels);
792
					result.add(data);
950
			}
793
			}
794
			internalCollectExpanded(result, item);
951
		}
795
		}
952
	}
796
	}
953
}
797
	/**
798
	 * Tries to create a path of tree items for the given element.
799
	 * This method recursively walks up towards the root of the tree
800
	 * and assumes that <code>getParent</code> returns the correct
801
	 * parent of an element.
802
	 *
803
	 * @param element the element
804
	 * @param expand <code>true</code> if all nodes on the path should be expanded, 
805
	 *    and <code>false</code> otherwise
806
	 */
807
	protected Widget internalExpand(Object element, boolean expand) {
954
808
955
/**
809
		if (element == null)
956
 * Update the structure and recurse.
810
			return null;
957
 * Items are updated in updateChildren, as needed.
811
958
 */
812
		Widget w = findItem(element);
959
private void internalRefreshStruct(Widget widget, Object element, boolean updateLabels) {
813
		if (w == null) {
960
	updateChildren(widget, element, null, updateLabels);
814
			if (equals(element, getRoot())) { // stop at root
961
	Item[] children= getChildren(widget);
815
				return null;
962
	if (children != null) {
816
			}
963
		for (int i= 0; i < children.length; i++) {
817
			// my parent has to create me
964
			Widget item= children[i];
818
			ITreeContentProvider cp =
965
			Object data= item.getData();
819
				(ITreeContentProvider) getContentProvider();
966
			if (data != null)
820
			if (cp == null) {
967
				internalRefreshStruct(item, data, updateLabels);
821
				return null;
822
			}
823
			Object parent = cp.getParent(element);
824
			if (parent != null) {
825
				Widget pw = internalExpand(parent, expand);
826
				if (pw != null) {
827
					// let my parent create me
828
					createChildren(pw);
829
					// expand parent and find me
830
					if (pw instanceof Item) {
831
						Item item = (Item) pw;
832
						if (expand)
833
							setExpanded(item, true);
834
						w = internalFindChild(item, element);
835
					}
836
				}
837
			}
838
		}
839
		return w;
840
	}
841
	/**
842
	 * Recursively expands the subtree rooted at the given widget to the
843
	 * given level.
844
	 * <p>
845
	 * </p>
846
	 * Note that the default implementation of this method does not call
847
	 * <code>setRedraw</code>.
848
	 *
849
	 * @param widget the widget
850
	 * @param level non-negative level, or <code>ALL_LEVELS</code> to collapse
851
	 *  all levels of the tree
852
	 */
853
	protected void internalExpandToLevel(Widget widget, int level) {
854
		if (level == ALL_LEVELS || level > 0) {
855
			createChildren(widget);
856
			if (widget instanceof Item)
857
				setExpanded((Item) widget, true);
858
			if (level == ALL_LEVELS || level > 1) {
859
				Item[] children = getChildren(widget);
860
				if (children != null) {
861
					int newLevel =
862
						(level == ALL_LEVELS ? ALL_LEVELS : level - 1);
863
					for (int i = 0; i < children.length; i++)
864
						internalExpandToLevel(children[i], newLevel);
865
				}
866
			}
968
		}
867
		}
969
	}
868
	}
970
}
869
	/**
870
	 * Non-recursively tries to find the given element as a child of the given parent item.
871
	 *
872
	 * @param parent the parent item
873
	 * @param element the element
874
	 */
875
	private Widget internalFindChild(Item parent, Object element) {
876
		Item[] items = getChildren(parent);
877
		for (int i = 0; i < items.length; i++) {
878
			Item item = items[i];
879
			Object data = item.getData();
880
			if (data != null && equals(data, element))
881
				return item;
882
		}
883
		return null;
884
	}
885
	/**
886
	 * Recursively tries to find the given element.
887
	 *
888
	 * @param parent the parent item
889
	 * @param element the element
890
	 */
891
	private Widget internalFindItem(Item parent, Object element) {
971
892
972
/**
893
		// compare with node
973
 * Removes the given elements from this viewer.
894
		Object data = parent.getData();
974
 *
895
		if (data != null) {
975
 * @param elements the elements to remove
896
			if (equals(data, element))
976
 */
897
				return parent;
977
private void internalRemove(Object[] elements) {
898
		}
978
	Object input = getInput();
899
		// recurse over children
979
	HashSet parentItems = new HashSet(5);
900
		Item[] items = getChildren(parent);
980
	for (int i = 0; i < elements.length; ++i) {
901
		for (int i = 0; i < items.length; i++) {
981
		if (equals(elements[i], input)) {
902
			Item item = items[i];
982
			setInput(null);
903
			Widget o = internalFindItem(item, element);
904
			if (o != null)
905
				return o;
906
		}
907
		return null;
908
	}
909
	/* (non-Javadoc)
910
	 * Method declared on StructuredViewer.
911
	 */
912
	protected void internalRefresh(Object element) {
913
		internalRefresh(element, true);
914
	}
915
	/* (non-Javadoc)
916
	 * Method declared on StructuredViewer.
917
	 */
918
	protected void internalRefresh(Object element, boolean updateLabels) {
919
		// If element is null, do a full refresh.
920
		if (element == null) {
921
			internalRefresh(getControl(), getRoot(), true, updateLabels);
983
			return;
922
			return;
984
		}
923
		}
985
		Widget childItem = findItem(elements[i]);
924
		Widget item = findItem(element);
986
		if (childItem instanceof Item) {
925
		if (item != null) {
987
			Item parentItem = getParentItem((Item) childItem);
926
			// pick up structure changes too
988
			if (parentItem != null) {
927
			internalRefresh(item, element, true, updateLabels);
989
				parentItems.add(parentItem);
928
		}
990
			}
929
	}
991
			disassociate((Item) childItem);
930
	/**
992
			childItem.dispose();
931
	 * Refreshes the tree starting at the given widget.
993
		}
932
	 *
994
	}
933
	 * @param widget the widget
995
	Control tree = getControl();
934
	 * @param element the element
996
	for (Iterator i = parentItems.iterator(); i.hasNext();) {
935
	 * @param doStruct <code>true</code> if structural changes are to be picked up,
997
		Item parentItem = (Item) i.next();
936
	 *   and <code>false</code> if only label provider changes are of interest
998
		if (!getExpanded(parentItem) && getItemCount(parentItem) == 0) {
937
	 * @param updateLabels <code>true</code> to update labels for existing elements,
999
			// append a dummy if necessary
938
	 *   <code>false</code> to only update labels as needed, assuming that labels
1000
			if (isExpandable(parentItem.getData())) {
939
	 *   for existing elements are unchanged.
1001
				newItem(parentItem, SWT.NULL, -1);
940
	 */
941
	private void internalRefresh(
942
		Widget widget,
943
		Object element,
944
		boolean doStruct,
945
		boolean updateLabels) {
946
947
		if (widget instanceof Item) {
948
			if (doStruct) {
949
				updatePlus((Item) widget, element);
950
			}
951
			if (updateLabels || !equals(element, widget.getData())) {
952
				doUpdateItem(widget, element, true);
1002
			} else {
953
			} else {
1003
				// XXX: Workaround (PR missing)
954
				associate(element, (Item) widget);
1004
				tree.redraw();
955
			}
1005
			}	
956
		}
957
958
		if (doStruct) {
959
			internalRefreshStruct(widget, element, updateLabels);
960
		} else {
961
			Item[] children = getChildren(widget);
962
			if (children != null) {
963
				for (int i = 0; i < children.length; i++) {
964
					Widget item = children[i];
965
					Object data = item.getData();
966
					if (data != null)
967
						internalRefresh(item, data, doStruct, updateLabels);
968
				}
969
			}
1006
		}
970
		}
1007
	}
971
	}
1008
}
972
1009
/**
973
	/**
1010
 * Sets the expanded state of all items to correspond to the given set of expanded elements.
974
	 * Update the structure and recurse.
1011
 *
975
	 * Items are updated in updateChildren, as needed.
1012
 * @param expandedElements the set (element type: <code>Object</code>) of elements which are expanded
976
	 */
1013
 * @param widget the widget
977
	private void internalRefreshStruct(
1014
 */
978
		Widget widget,
1015
private void internalSetExpanded(HashSet expandedElements, Widget widget) {
979
		Object element,
1016
	Item[] items = getChildren(widget);
980
		boolean updateLabels) {
1017
	for (int i = 0; i < items.length; i++) {
981
		updateChildren(widget, element, null, updateLabels);
1018
		Item item = items[i];
982
		Item[] children = getChildren(widget);
1019
		Object data = item.getData();
983
		if (children != null) {
1020
		if (data != null) {
984
			for (int i = 0; i < children.length; i++) {
1021
			// remove the element to avoid an infinite loop
985
				Widget item = children[i];
1022
			// if the same element appears on a child item
986
				Object data = item.getData();
1023
			boolean expanded = expandedElements.remove(data);
987
				if (data != null)
1024
			if (expanded != getExpanded(item)) {
988
					internalRefreshStruct(item, data, updateLabels);
1025
				if (expanded) {
989
			}
1026
					createChildren(item);
990
		}
991
	}
992
993
	/**
994
	 * Removes the given elements from this viewer.
995
	 *
996
	 * @param elements the elements to remove
997
	 */
998
	private void internalRemove(Object[] elements) {
999
		Object input = getInput();
1000
		HashSet parentItems = new HashSet(5);
1001
		for (int i = 0; i < elements.length; ++i) {
1002
			if (equals(elements[i], input)) {
1003
				setInput(null);
1004
				return;
1005
			}
1006
			Widget childItem = findItem(elements[i]);
1007
			if (childItem instanceof Item) {
1008
				Item parentItem = getParentItem((Item) childItem);
1009
				if (parentItem != null) {
1010
					parentItems.add(parentItem);
1011
				}
1012
				disassociate((Item) childItem);
1013
				childItem.dispose();
1014
			}
1015
		}
1016
		Control tree = getControl();
1017
		for (Iterator i = parentItems.iterator(); i.hasNext();) {
1018
			Item parentItem = (Item) i.next();
1019
			if (!getExpanded(parentItem) && getItemCount(parentItem) == 0) {
1020
				// append a dummy if necessary
1021
				if (isExpandable(parentItem.getData())) {
1022
					newItem(parentItem, SWT.NULL, -1);
1023
				} else {
1024
					// XXX: Workaround (PR missing)
1025
					tree.redraw();
1027
				}
1026
				}
1028
				setExpanded(item, expanded);
1029
			}
1027
			}
1030
		}
1028
		}
1031
		internalSetExpanded(expandedElements, item);
1032
	}
1029
	}
1033
}
1030
	/**
1034
/**
1031
	 * Sets the expanded state of all items to correspond to the given set of expanded elements.
1035
 * Return whether the tree node representing the given element
1032
	 *
1036
 * can be expanded.
1033
	 * @param expandedElements the set (element type: <code>Object</code>) of elements which are expanded
1037
 * <p>
1034
	 * @param widget the widget
1038
 * The default implementation of this framework method calls 
1035
	 */
1039
 * <code>hasChildren</code> on this viewer's content provider.
1036
	private void internalSetExpanded(HashSet expandedElements, Widget widget) {
1040
 * It may be overridden if necessary.
1037
		Item[] items = getChildren(widget);
1041
 * </p>
1038
		for (int i = 0; i < items.length; i++) {
1042
 *
1039
			Item item = items[i];
1043
 * @param element the element
1040
			Object data = item.getData();
1044
 * @return <code>true</code> if the tree node representing
1041
			if (data != null) {
1045
 * the given element can be expanded, or <code>false</code> if not
1042
				// remove the element to avoid an infinite loop
1046
 */
1043
				// if the same element appears on a child item
1047
public boolean isExpandable(Object element) {
1044
				boolean expanded = expandedElements.remove(data);
1048
	ITreeContentProvider cp = (ITreeContentProvider) getContentProvider();
1045
				if (expanded != getExpanded(item)) {
1049
	return cp != null && cp.hasChildren(element);
1046
					if (expanded) {
1050
}
1047
						createChildren(item);
1051
/* (non-Javadoc)
1048
					}
1052
 * Method declared on Viewer.
1049
					setExpanded(item, expanded);
1053
 */
1050
				}
1054
protected void labelProviderChanged() {
1051
			}
1055
	// we have to walk the (visible) tree and update every item
1052
			internalSetExpanded(expandedElements, item);
1056
	Control tree= getControl();
1057
	tree.setRedraw(false);
1058
	// don't pick up structure changes, but do force label updates
1059
	internalRefresh(tree, getRoot(), false, true);
1060
	tree.setRedraw(true);
1061
}
1062
/**
1063
 * Creates a new item.
1064
 *
1065
 * @param parent the parent widget
1066
 * @param style SWT style bits
1067
 * @param index if non-negative, indicates the position to insert the item 
1068
 *    into its parent
1069
 * @return the newly-created item 
1070
 */
1071
protected abstract Item newItem(Widget parent, int style, int index);
1072
/**
1073
 * Removes the given elements from this viewer.
1074
 * The selection is updated if required.
1075
 * <p>
1076
 * This method should be called (by the content provider) when elements 
1077
 * have been removed from the model, in order to cause the viewer to accurately
1078
 * reflect the model. This method only affects the viewer, not the model.
1079
 * </p>
1080
 *
1081
 * @param elements the elements to remove
1082
 */
1083
public void remove(final Object[] elements) {
1084
	preservingSelection(new Runnable() {
1085
		public void run() {
1086
			internalRemove(elements);
1087
		}
1053
		}
1088
	});
1089
}
1090
/**
1091
 * Removes the given element from the viewer.
1092
 * The selection is updated if necessary.
1093
 * <p>
1094
 * This method should be called (by the content provider) when a single element 
1095
 * has been removed from the model, in order to cause the viewer to accurately
1096
 * reflect the model. This method only affects the viewer, not the model.
1097
 * Note that there is another method for efficiently processing the simultaneous
1098
 * removal of multiple elements.
1099
 * </p>
1100
 *
1101
 * @param element the element
1102
 */
1103
public void remove(Object element) {
1104
	remove(new Object[] { element });
1105
}
1106
/**
1107
 * Removes all items from the given control.
1108
 *
1109
 * @param control the control
1110
 */
1111
protected abstract void removeAll(Control control);
1112
/**
1113
 * Removes a listener for expand and collapse events in this viewer.
1114
 * Has no affect if an identical listener is not registered.
1115
 *
1116
 * @param listener a tree viewer listener
1117
 */
1118
public void removeTreeListener(ITreeViewerListener listener) {
1119
	treeListeners.remove(listener);
1120
}
1121
/*
1122
 * Non-Javadoc.
1123
 * Method defined on StructuredViewer.
1124
 */
1125
public void reveal(Object element) {
1126
	Widget w = internalExpand(element, true);
1127
	if (w instanceof Item)
1128
		showItem((Item) w);
1129
}
1130
/**
1131
 * Returns the rightmost visible descendent of the given item.
1132
 * Returns the item itself if it has no children.
1133
 *
1134
 * @param item the item to compute the descendent of
1135
 * @return the rightmost visible descendent or the item iself
1136
 *  if it has no children
1137
 */
1138
private Item rightMostVisibleDescendent(Item item) {
1139
	Item[] children = getItems(item);
1140
	if (getExpanded(item) && children != null && children.length > 0) {
1141
		return rightMostVisibleDescendent(children[children.length-1]);
1142
	} else {
1143
		return item;
1144
	}
1145
}
1146
/* (non-Javadoc)
1147
 * Method declared on Viewer.
1148
 */
1149
public Item scrollDown(int x, int y) {
1150
	Item current = getItem(x, y);
1151
	if (current != null) {
1152
		Item next = getNextItem(current, true);
1153
		showItem(next == null ? current : next);
1154
		return next;
1155
	}
1156
	return null;
1157
}
1158
/* (non-Javadoc)
1159
 * Method declared on Viewer.
1160
 */
1161
public Item scrollUp(int x, int y) {
1162
	Item current = getItem(x, y);
1163
	if (current != null) {
1164
		Item previous = getPreviousItem(current);
1165
		showItem(previous == null ? current : previous);
1166
		return previous;
1167
	}
1054
	}
1168
	return null;
1055
	/**
1169
}
1056
	 * Return whether the tree node representing the given element
1170
/**
1057
	 * can be expanded.
1171
 * Sets the auto-expand level.
1058
	 * <p>
1172
 * The value 0 means that there is no auto-expand; 
1059
	 * The default implementation of this framework method calls 
1173
 * 1 means that top-level elements are expanded, but not their children;
1060
	 * <code>hasChildren</code> on this viewer's content provider.
1174
 * 2 means that top-level elements are expanded, and their children, 
1061
	 * It may be overridden if necessary.
1175
 * but not grandchildren; and so on.
1062
	 * </p>
1176
 * <p>
1063
	 *
1177
 * The value <code>ALL_LEVELS</code> means that all subtrees should be
1064
	 * @param element the element
1178
 * expanded.
1065
	 * @return <code>true</code> if the tree node representing
1179
 * </p>
1066
	 * the given element can be expanded, or <code>false</code> if not
1180
 *
1067
	 */
1181
 * @param level non-negative level, or <code>ALL_LEVELS</code> to expand
1068
	public boolean isExpandable(Object element) {
1182
 *  all levels of the tree
1069
		ITreeContentProvider cp = (ITreeContentProvider) getContentProvider();
1183
 */
1070
		return cp != null && cp.hasChildren(element);
1184
public void setAutoExpandLevel(int level) {
1185
	expandToLevel = level;
1186
}
1187
/**
1188
 * The <code>AbstractTreeViewer</code> implementation of this method
1189
 * checks to ensure that the content provider is an <code>ITreeContentProvider</code>.
1190
 */
1191
public void setContentProvider(IContentProvider provider) {
1192
	Assert.isTrue(provider instanceof ITreeContentProvider);
1193
	super.setContentProvider(provider);
1194
}
1195
/**
1196
 * Sets the expand state of the given item.
1197
 *
1198
 * @param item the item
1199
 * @param expand the expand state of the item
1200
 */
1201
protected abstract void setExpanded(Item item, boolean expand);
1202
/**
1203
 * Sets which nodes are expanded in this viewer's tree.
1204
 * The given list contains the elements that are to be expanded;
1205
 * all other nodes are to be collapsed.
1206
 * <p>
1207
 * This method is typically used when restoring the interesting
1208
 * state of a viewer captured by an earlier call to <code>getExpandedElements</code>.
1209
 * </p>
1210
 *
1211
 * @param elements the array of expanded elements
1212
 * @see #getExpandedElements
1213
 */
1214
public void setExpandedElements(Object[] elements) {
1215
	HashSet expandedElements = new HashSet(elements.length*2+1);
1216
	for (int i = 0; i < elements.length; ++i) {
1217
		// Ensure item exists for element
1218
		internalExpand(elements[i], false);
1219
		expandedElements.add(elements[i]);
1220
	}
1071
	}
1221
	internalSetExpanded(expandedElements, getControl());
1072
	/* (non-Javadoc)
1222
}
1073
	 * Method declared on Viewer.
1223
/**
1074
	 */
1224
 * Sets whether the node corresponding to the given element is expanded or collapsed.
1075
	protected void labelProviderChanged() {
1225
 *
1076
		// we have to walk the (visible) tree and update every item
1226
 * @param element the element
1077
		Control tree = getControl();
1227
 * @param expanded <code>true</code> if the node is expanded, and <code>false</code> if collapsed
1078
		tree.setRedraw(false);
1228
 */
1079
		// don't pick up structure changes, but do force label updates
1229
public void setExpandedState(Object element, boolean expanded) {
1080
		internalRefresh(tree, getRoot(), false, true);
1230
	Widget item = internalExpand(element, false);
1081
		tree.setRedraw(true);
1231
	if (item instanceof Item) {
1082
	}
1232
		if (expanded) {
1083
	/**
1233
			createChildren(item);
1084
	 * Creates a new item.
1085
	 *
1086
	 * @param parent the parent widget
1087
	 * @param style SWT style bits
1088
	 * @param index if non-negative, indicates the position to insert the item 
1089
	 *    into its parent
1090
	 * @return the newly-created item 
1091
	 */
1092
	protected abstract Item newItem(Widget parent, int style, int index);
1093
	/**
1094
	 * Removes the given elements from this viewer.
1095
	 * The selection is updated if required.
1096
	 * <p>
1097
	 * This method should be called (by the content provider) when elements 
1098
	 * have been removed from the model, in order to cause the viewer to accurately
1099
	 * reflect the model. This method only affects the viewer, not the model.
1100
	 * </p>
1101
	 *
1102
	 * @param elements the elements to remove
1103
	 */
1104
	public void remove(final Object[] elements) {
1105
		preservingSelection(new Runnable() {
1106
			public void run() {
1107
				internalRemove(elements);
1108
			}
1109
		});
1110
	}
1111
	/**
1112
	 * Removes the given element from the viewer.
1113
	 * The selection is updated if necessary.
1114
	 * <p>
1115
	 * This method should be called (by the content provider) when a single element 
1116
	 * has been removed from the model, in order to cause the viewer to accurately
1117
	 * reflect the model. This method only affects the viewer, not the model.
1118
	 * Note that there is another method for efficiently processing the simultaneous
1119
	 * removal of multiple elements.
1120
	 * </p>
1121
	 *
1122
	 * @param element the element
1123
	 */
1124
	public void remove(Object element) {
1125
		remove(new Object[] { element });
1126
	}
1127
	/**
1128
	 * Removes all items from the given control.
1129
	 *
1130
	 * @param control the control
1131
	 */
1132
	protected abstract void removeAll(Control control);
1133
	/**
1134
	 * Removes a listener for expand and collapse events in this viewer.
1135
	 * Has no affect if an identical listener is not registered.
1136
	 *
1137
	 * @param listener a tree viewer listener
1138
	 */
1139
	public void removeTreeListener(ITreeViewerListener listener) {
1140
		treeListeners.remove(listener);
1141
	}
1142
	/*
1143
	 * Non-Javadoc.
1144
	 * Method defined on StructuredViewer.
1145
	 */
1146
	public void reveal(Object element) {
1147
		Widget w = internalExpand(element, true);
1148
		if (w instanceof Item)
1149
			showItem((Item) w);
1150
	}
1151
	/**
1152
	 * Returns the rightmost visible descendent of the given item.
1153
	 * Returns the item itself if it has no children.
1154
	 *
1155
	 * @param item the item to compute the descendent of
1156
	 * @return the rightmost visible descendent or the item iself
1157
	 *  if it has no children
1158
	 */
1159
	private Item rightMostVisibleDescendent(Item item) {
1160
		Item[] children = getItems(item);
1161
		if (getExpanded(item) && children != null && children.length > 0) {
1162
			return rightMostVisibleDescendent(children[children.length - 1]);
1163
		} else {
1164
			return item;
1234
		}
1165
		}
1235
		setExpanded((Item) item, expanded);
1236
	}
1166
	}
1237
}
1167
	/* (non-Javadoc)
1238
/**
1168
	 * Method declared on Viewer.
1239
 * Sets the selection to the given list of items.
1169
	 */
1240
 *
1170
	public Item scrollDown(int x, int y) {
1241
 * @param items list of items (element type: <code>org.eclipse.swt.widgets.Item</code>)
1171
		Item current = getItem(x, y);
1242
 */
1172
		if (current != null) {
1243
protected abstract void setSelection(List items);
1173
			Item next = getNextItem(current, true);
1244
/* (non-Javadoc)
1174
			showItem(next == null ? current : next);
1245
 * Method declared on StructuredViewer.
1175
			return next;
1246
 */
1176
		}
1247
protected void setSelectionToWidget(List v, boolean reveal) {
1177
		return null;
1248
	if (v == null) {
1249
		setSelection(new ArrayList(0));
1250
		return;
1251
	}
1252
	int size = v.size();
1253
	List newSelection = new ArrayList(size);
1254
	for (int i = 0; i < size; ++i) {
1255
		// Use internalExpand since item may not yet be created.  See 1G6B1AR.
1256
		Widget w = internalExpand(v.get(i), true);
1257
		if (w instanceof Item) {
1258
			newSelection.add(w);
1259
		}
1260
	}
1261
	setSelection(newSelection);
1262
	if (reveal && newSelection.size() > 0) {
1263
		showItem((Item) newSelection.get(0));
1264
	}
1178
	}
1265
}
1179
	/* (non-Javadoc)
1266
/**
1180
	 * Method declared on Viewer.
1267
 * Shows the given item.
1181
	 */
1268
 *
1182
	public Item scrollUp(int x, int y) {
1269
 * @param item the item
1183
		Item current = getItem(x, y);
1270
 */
1184
		if (current != null) {
1271
protected abstract void showItem(Item item);
1185
			Item previous = getPreviousItem(current);
1186
			showItem(previous == null ? current : previous);
1187
			return previous;
1188
		}
1189
		return null;
1190
	}
1191
	/**
1192
	 * Sets the auto-expand level.
1193
	 * The value 0 means that there is no auto-expand; 
1194
	 * 1 means that top-level elements are expanded, but not their children;
1195
	 * 2 means that top-level elements are expanded, and their children, 
1196
	 * but not grandchildren; and so on.
1197
	 * <p>
1198
	 * The value <code>ALL_LEVELS</code> means that all subtrees should be
1199
	 * expanded.
1200
	 * </p>
1201
	 *
1202
	 * @param level non-negative level, or <code>ALL_LEVELS</code> to expand
1203
	 *  all levels of the tree
1204
	 */
1205
	public void setAutoExpandLevel(int level) {
1206
		expandToLevel = level;
1207
	}
1208
	/**
1209
	 * The <code>AbstractTreeViewer</code> implementation of this method
1210
	 * checks to ensure that the content provider is an <code>ITreeContentProvider</code>.
1211
	 */
1212
	public void setContentProvider(IContentProvider provider) {
1213
		Assert.isTrue(provider instanceof ITreeContentProvider);
1214
		super.setContentProvider(provider);
1215
	}
1216
	/**
1217
	 * Sets the expand state of the given item.
1218
	 *
1219
	 * @param item the item
1220
	 * @param expand the expand state of the item
1221
	 */
1222
	protected abstract void setExpanded(Item item, boolean expand);
1223
	/**
1224
	 * Sets which nodes are expanded in this viewer's tree.
1225
	 * The given list contains the elements that are to be expanded;
1226
	 * all other nodes are to be collapsed.
1227
	 * <p>
1228
	 * This method is typically used when restoring the interesting
1229
	 * state of a viewer captured by an earlier call to <code>getExpandedElements</code>.
1230
	 * </p>
1231
	 *
1232
	 * @param elements the array of expanded elements
1233
	 * @see #getExpandedElements
1234
	 */
1235
	public void setExpandedElements(Object[] elements) {
1236
		HashSet expandedElements = new HashSet(elements.length * 2 + 1);
1237
		for (int i = 0; i < elements.length; ++i) {
1238
			// Ensure item exists for element
1239
			internalExpand(elements[i], false);
1240
			expandedElements.add(elements[i]);
1241
		}
1242
		internalSetExpanded(expandedElements, getControl());
1243
	}
1244
	/**
1245
	 * Sets whether the node corresponding to the given element is expanded or collapsed.
1246
	 *
1247
	 * @param element the element
1248
	 * @param expanded <code>true</code> if the node is expanded, and <code>false</code> if collapsed
1249
	 */
1250
	public void setExpandedState(Object element, boolean expanded) {
1251
		Widget item = internalExpand(element, false);
1252
		if (item instanceof Item) {
1253
			if (expanded) {
1254
				createChildren(item);
1255
			}
1256
			setExpanded((Item) item, expanded);
1257
		}
1258
	}
1259
	/**
1260
	 * Sets the selection to the given list of items.
1261
	 *
1262
	 * @param items list of items (element type: <code>org.eclipse.swt.widgets.Item</code>)
1263
	 */
1264
	protected abstract void setSelection(List items);
1265
	/* (non-Javadoc)
1266
	 * Method declared on StructuredViewer.
1267
	 */
1268
	protected void setSelectionToWidget(List v, boolean reveal) {
1269
		if (v == null) {
1270
			setSelection(new ArrayList(0));
1271
			return;
1272
		}
1273
		int size = v.size();
1274
		List newSelection = new ArrayList(size);
1275
		for (int i = 0; i < size; ++i) {
1276
			// Use internalExpand since item may not yet be created.  See 1G6B1AR.
1277
			Widget w = internalExpand(v.get(i), true);
1278
			if (w instanceof Item) {
1279
				newSelection.add(w);
1280
			}
1281
		}
1282
		setSelection(newSelection);
1283
		if (reveal && newSelection.size() > 0) {
1284
			showItem((Item) newSelection.get(0));
1285
		}
1286
	}
1287
	/**
1288
	 * Shows the given item.
1289
	 *
1290
	 * @param item the item
1291
	 */
1292
	protected abstract void showItem(Item item);
1272
1293
1273
/**
1294
	/**
1274
 * Updates the tree items to correspond to the child elements of the given parent element.
1295
	 * Updates the tree items to correspond to the child elements of the given parent element.
1275
 * If null is passed for the children, this method obtains them (only if needed).
1296
	 * If null is passed for the children, this method obtains them (only if needed).
1276
 *
1297
	 *
1277
 * @param widget the widget
1298
	 * @param widget the widget
1278
 * @param parent the parent element
1299
	 * @param parent the parent element
1279
 * @param elementChildren the child elements, or null
1300
	 * @param elementChildren the child elements, or null
1280
 * 
1301
	 * 
1281
 * @deprecated this is no longer called by the framework
1302
	 * @deprecated this is no longer called by the framework
1282
 */
1303
	 */
1283
protected void updateChildren(Widget widget, Object parent, Object[] elementChildren) {
1304
	protected void updateChildren(
1284
	updateChildren(widget, parent, elementChildren, true);
1305
		Widget widget,
1285
}
1306
		Object parent,
1307
		Object[] elementChildren) {
1308
		updateChildren(widget, parent, elementChildren, true);
1309
	}
1286
1310
1287
/**
1311
	/**
1288
 * Updates the tree items to correspond to the child elements of the given parent element.
1312
	 * Updates the tree items to correspond to the child elements of the given parent element.
1289
 * If null is passed for the children, this method obtains them (only if needed).
1313
	 * If null is passed for the children, this method obtains them (only if needed).
1290
 *
1314
	 *
1291
 * @param widget the widget
1315
	 * @param widget the widget
1292
 * @param parent the parent element
1316
	 * @param parent the parent element
1293
 * @param elementChildren the child elements, or null
1317
	 * @param elementChildren the child elements, or null
1294
 * @param updateLabels <code>true</code> to update labels for existing elements,
1318
	 * @param updateLabels <code>true</code> to update labels for existing elements,
1295
 *   <code>false</code> to only update labels as needed, assuming that labels
1319
	 *   <code>false</code> to only update labels as needed, assuming that labels
1296
 *   for existing elements are unchanged.
1320
	 *   for existing elements are unchanged.
1297
 * 
1321
	 * 
1298
 * @since 2.1
1322
	 * @since 2.1
1299
 */
1323
	 */
1300
private void updateChildren(Widget widget, Object parent, Object[] elementChildren, boolean updateLabels) {
1324
	private void updateChildren(
1301
	// optimization! prune collapsed subtrees
1325
		Widget widget,
1302
	if (widget instanceof Item) {
1326
		Object parent,
1303
		Item ti= (Item) widget;
1327
		Object[] elementChildren,
1304
		if (!getExpanded(ti)) {
1328
		boolean updateLabels) {
1305
			// need a dummy node if element is expandable;
1329
		// optimization! prune collapsed subtrees
1306
			// but try to avoid recreating the dummy node
1330
		if (widget instanceof Item) {
1307
			boolean needDummy = isExpandable(parent);
1331
			Item ti = (Item) widget;
1308
			boolean haveDummy = false;
1332
			if (!getExpanded(ti)) {
1309
			// remove all children
1333
				// need a dummy node if element is expandable;
1310
			Item[] items= getItems(ti);
1334
				// but try to avoid recreating the dummy node
1311
			for (int i= 0; i < items.length; i++) {
1335
				boolean needDummy = isExpandable(parent);
1312
				if (items[i].getData() != null) {
1336
				boolean haveDummy = false;
1313
					disassociate(items[i]);
1337
				// remove all children
1314
					items[i].dispose();
1338
				Item[] items = getItems(ti);
1315
				}
1339
				for (int i = 0; i < items.length; i++) {
1316
				else {
1340
					if (items[i].getData() != null) {
1317
					if (needDummy && !haveDummy) {
1341
						disassociate(items[i]);
1318
						haveDummy = true;
1319
					}
1320
					else {
1321
						items[i].dispose();
1342
						items[i].dispose();
1343
					} else {
1344
						if (needDummy && !haveDummy) {
1345
							haveDummy = true;
1346
						} else {
1347
							items[i].dispose();
1348
						}
1322
					}
1349
					}
1323
				}
1350
				}
1351
				if (needDummy && !haveDummy) {
1352
					newItem(ti, SWT.NULL, -1);
1353
				}
1354
1355
				return;
1324
			}
1356
			}
1325
			if (needDummy && !haveDummy) {
1357
		}
1326
				newItem(ti, SWT.NULL, -1);
1358
1359
		// If the children weren't passed in, get them now since they're needed below.
1360
		if (elementChildren == null) {
1361
			elementChildren = getSortedChildren(parent);
1362
		}
1363
1364
		Control tree = getControl();
1365
1366
		// WORKAROUND
1367
		int oldCnt = -1;
1368
		if (widget == tree)
1369
			oldCnt = getItemCount(tree);
1370
1371
		Item[] items = getChildren(widget);
1372
1373
		// save the expanded elements
1374
		HashSet expanded = new HashSet(); // assume num expanded is small
1375
		for (int i = 0; i < items.length; ++i) {
1376
			if (getExpanded(items[i])) {
1377
				Object element = items[i].getData();
1378
				if (element != null) {
1379
					expanded.add(element);
1380
				}
1327
			}
1381
			}
1328
			
1329
			return;
1330
		}
1382
		}
1331
	}
1332
1383
1333
	// If the children weren't passed in, get them now since they're needed below.
1384
		int min = Math.min(elementChildren.length, items.length);
1334
	if (elementChildren == null) {
1385
1335
		elementChildren = getSortedChildren(parent);
1386
		// Note: In the code below, doing disassociate calls before associate calls is important,
1336
	}
1387
		// since a later disassociate can undo an earlier associate, 
1337
	
1388
		// if items are changing position.
1338
	Control tree = getControl();
1389
1339
	
1390
		// dispose of all items beyond the end of the current elements
1340
	// WORKAROUND
1391
		for (int i = items.length; --i >= min;) {
1341
	int oldCnt= -1;
1392
			if (items[i].getData() != null) {
1342
	if (widget == tree)
1393
				disassociate(items[i]);
1343
		oldCnt= getItemCount(tree);
1344
1345
	Item[] items = getChildren(widget);
1346
	
1347
	// save the expanded elements
1348
	HashSet expanded = new HashSet(); // assume num expanded is small
1349
	for (int i = 0; i < items.length; ++i) {
1350
		if (getExpanded(items[i])) {
1351
			Object element = items[i].getData();
1352
			if (element != null) {
1353
				expanded.add(element);
1354
			}
1394
			}
1395
			items[i].dispose();
1355
		}
1396
		}
1356
	}
1397
1357
	
1398
		// compare first min items, and update item if necessary
1358
	int min = Math.min(elementChildren.length, items.length);
1399
		// need to do it in two passes:
1359
1400
		// 1: disassociate old items
1360
	// Note: In the code below, doing disassociate calls before associate calls is important,
1401
		// 2: associate new items
1361
	// since a later disassociate can undo an earlier associate, 
1402
		// because otherwise a later disassociate can remove a mapping made for a previous associate,
1362
	// if items are changing position.
1403
		// making the map inconsistent
1363
	
1404
		for (int i = 0; i < min; ++i) {
1364
	// dispose of all items beyond the end of the current elements
1405
			Item item = items[i];
1365
	for (int i = items.length; --i >= min;) {
1406
			Object oldElement = item.getData();
1366
		if (items[i].getData() != null) {
1407
			if (oldElement != null) {
1367
			disassociate(items[i]);
1408
				Object newElement = elementChildren[i];
1368
		}
1409
				if (newElement != oldElement) {
1369
		items[i].dispose();
1410
					if (equals(newElement, oldElement)) {
1370
	}
1411
						// update the data to be the new element, since although the elements 
1371
	
1412
						// may be equal, they may still have different labels or children
1372
	// compare first min items, and update item if necessary
1413
						item.setData(newElement);
1373
	// need to do it in two passes:
1414
						mapElement(newElement, item);
1374
	// 1: disassociate old items
1415
					} else {
1375
	// 2: associate new items
1416
						disassociate(item);
1376
	// because otherwise a later disassociate can remove a mapping made for a previous associate,
1417
					}
1377
	// making the map inconsistent
1378
	for (int i = 0; i < min; ++i) {
1379
		Item item = items[i];
1380
		Object oldElement = item.getData();
1381
		if (oldElement != null) {
1382
			Object newElement = elementChildren[i];
1383
			if (newElement != oldElement) {
1384
				if (equals(newElement, oldElement)) {
1385
					// update the data to be the new element, since although the elements 
1386
					// may be equal, they may still have different labels or children
1387
					item.setData(newElement);
1388
					mapElement(newElement, item);
1389
				} else {
1390
					disassociate(item);
1391
				}
1418
				}
1392
			}
1419
			}
1393
		}
1420
		}
1394
	}
1421
		for (int i = 0; i < min; ++i) {
1395
	for (int i = 0; i < min; ++i) {
1422
			Item item = items[i];
1396
		Item item = items[i];
1423
			Object newElement = elementChildren[i];
1397
		Object newElement = elementChildren[i];
1424
			if (item.getData() == null) {
1398
		if (item.getData() == null) {
1425
				// old and new elements are not equal
1399
			// old and new elements are not equal
1426
				associate(newElement, item);
1400
			associate(newElement, item);
1427
				updatePlus(item, newElement);
1401
			updatePlus(item, newElement);
1402
			updateItem(item, newElement);
1403
			// Restore expanded state for items that changed position.
1404
			// Make sure setExpanded is called after updatePlus, since
1405
			// setExpanded(false) fails if item has no children.
1406
			// Need to call setExpanded for both expanded and unexpanded cases
1407
			// since the expanded state can change either way.
1408
			setExpanded(item, expanded.contains(newElement));
1409
		}
1410
		else {
1411
			// old and new elements are equal
1412
			updatePlus(item, newElement);
1413
			if (updateLabels) {
1414
				updateItem(item, newElement);
1428
				updateItem(item, newElement);
1429
				// Restore expanded state for items that changed position.
1430
				// Make sure setExpanded is called after updatePlus, since
1431
				// setExpanded(false) fails if item has no children.
1432
				// Need to call setExpanded for both expanded and unexpanded cases
1433
				// since the expanded state can change either way.
1434
				setExpanded(item, expanded.contains(newElement));
1435
			} else {
1436
				// old and new elements are equal
1437
				updatePlus(item, newElement);
1438
				if (updateLabels) {
1439
					updateItem(item, newElement);
1440
				}
1415
			}
1441
			}
1416
		}
1442
		}
1417
	}
1443
1418
	
1444
		// add any remaining elements
1419
	// add any remaining elements
1445
		if (min < elementChildren.length) {
1420
	if (min < elementChildren.length) {
1421
		for (int i = min; i < elementChildren.length; ++i) {
1422
			createTreeItem(widget, elementChildren[i], i);
1423
		}
1424
		
1425
		// Need to restore expanded state in a separate pass 
1426
		// because createTreeItem does not return the new item.
1427
		// Avoid doing this unless needed.
1428
		if (!expanded.isEmpty()) {
1429
			// get the items again, to include the new items
1430
			items = getChildren(widget);
1431
			for (int i = min; i < elementChildren.length; ++i) {
1446
			for (int i = min; i < elementChildren.length; ++i) {
1432
				// Restore expanded state for items that changed position.
1447
				createTreeItem(widget, elementChildren[i], i);
1433
				// Make sure setExpanded is called after updatePlus (called in createTreeItem), since
1448
			}
1434
				// setExpanded(false) fails if item has no children.
1449
1435
				// Only need to call setExpanded if element was expanded
1450
			// Need to restore expanded state in a separate pass 
1436
				// since new items are initially unexpanded.
1451
			// because createTreeItem does not return the new item.
1437
				if (expanded.contains(elementChildren[i])) {
1452
			// Avoid doing this unless needed.
1438
					setExpanded(items[i], true);
1453
			if (!expanded.isEmpty()) {
1454
				// get the items again, to include the new items
1455
				items = getChildren(widget);
1456
				for (int i = min; i < elementChildren.length; ++i) {
1457
					// Restore expanded state for items that changed position.
1458
					// Make sure setExpanded is called after updatePlus (called in createTreeItem), since
1459
					// setExpanded(false) fails if item has no children.
1460
					// Only need to call setExpanded if element was expanded
1461
					// since new items are initially unexpanded.
1462
					if (expanded.contains(elementChildren[i])) {
1463
						setExpanded(items[i], true);
1464
					}
1439
				}
1465
				}
1440
			}
1466
			}
1441
		}
1467
		}
1468
1469
		// WORKAROUND
1470
		if (widget == tree && oldCnt == 0 && getItemCount(tree) != 0) {
1471
			//System.out.println("WORKAROUND setRedraw");
1472
			tree.setRedraw(false);
1473
			tree.setRedraw(true);
1474
		}
1442
	}
1475
	}
1443
	
1476
	/**
1444
	// WORKAROUND
1477
	 * Updates the "+"/"-" icon of the tree node from the given element.
1445
	if (widget == tree && oldCnt == 0 && getItemCount(tree) != 0) {
1478
	 * It calls <code>isExpandable</code> to determine whether 
1446
		//System.out.println("WORKAROUND setRedraw");
1479
	 * an element is expandable.
1447
		tree.setRedraw(false);
1480
	 *
1448
		tree.setRedraw(true);
1481
	 * @param item the item
1449
	}
1482
	 * @param element the element
1450
}
1483
	 */
1451
/**
1484
	protected void updatePlus(Item item, Object element) {
1452
 * Updates the "+"/"-" icon of the tree node from the given element.
1485
		boolean hasPlus = getItemCount(item) > 0;
1453
 * It calls <code>isExpandable</code> to determine whether 
1486
		boolean needsPlus = isExpandable(element);
1454
 * an element is expandable.
1487
		boolean removeAll = false;
1455
 *
1488
		boolean addDummy = false;
1456
 * @param item the item
1489
		Object data = item.getData();
1457
 * @param element the element
1490
		if (data != null && equals(element, data)) {
1458
 */
1491
			// item shows same element
1459
protected void updatePlus(Item item, Object element) {
1492
			if (hasPlus != needsPlus) {
1460
	boolean hasPlus = getItemCount(item) > 0;
1493
				if (needsPlus)
1461
	boolean needsPlus = isExpandable(element);
1494
					addDummy = true;
1462
	boolean removeAll = false;
1495
				else
1463
	boolean addDummy = false;
1496
					removeAll = true;
1464
	Object data = item.getData();
1497
			}
1465
	if (data != null && equals(element, data)) {
1498
		} else {
1466
		// item shows same element
1499
			// item shows different element
1467
		if (hasPlus != needsPlus) {
1500
			removeAll = true;
1468
			if (needsPlus)
1501
			addDummy = needsPlus;
1469
				addDummy = true;
1502
1470
			else
1503
			// we cannot maintain expand state so collapse it
1471
				removeAll = true;
1504
			setExpanded(item, false);
1472
		}
1505
		}
1473
	} else {
1506
		if (removeAll) {
1474
		// item shows different element
1507
			// remove all children
1475
		removeAll = true;
1508
			Item[] items = getItems(item);
1476
		addDummy = needsPlus;
1509
			for (int i = 0; i < items.length; i++) {
1477
1510
				if (items[i].getData() != null)
1478
		// we cannot maintain expand state so collapse it
1511
					disassociate(items[i]);
1479
		setExpanded(item, false);
1512
				items[i].dispose();
1480
	}
1513
			}
1481
	if (removeAll) {
1482
		// remove all children
1483
		Item[] items = getItems(item);
1484
		for (int i = 0; i < items.length; i++) {
1485
			if (items[i].getData() != null)
1486
				disassociate(items[i]);
1487
			items[i].dispose();
1488
		}
1514
		}
1515
		if (addDummy)
1516
			newItem(item, SWT.NULL, -1); // append a dummy
1489
	}
1517
	}
1490
	if (addDummy)
1491
		newItem(item, SWT.NULL, -1); // append a dummy
1492
}
1493
1518
1494
/**
1519
	/**
1495
 * Gets the expanded elements that are visible
1520
	 * Gets the expanded elements that are visible
1496
 * to the user. An expanded element is only
1521
	 * to the user. An expanded element is only
1497
 * visible if the parent is expanded.
1522
	 * visible if the parent is expanded.
1498
 * 
1523
	 * 
1499
 * @return the visible expanded elements
1524
	 * @return the visible expanded elements
1500
 * @since 2.0
1525
	 * @since 2.0
1501
 */
1526
	 */
1502
public Object[] getVisibleExpandedElements() {
1527
	public Object[] getVisibleExpandedElements() {
1503
	ArrayList v= new ArrayList();
1528
		ArrayList v = new ArrayList();
1504
	internalCollectVisibleExpanded(v, getControl());
1529
		internalCollectVisibleExpanded(v, getControl());
1505
	return v.toArray();
1530
		return v.toArray();
1506
}
1531
	}
1507
	
1532
1508
private void internalCollectVisibleExpanded(ArrayList result, Widget widget){
1533
	private void internalCollectVisibleExpanded(
1509
	Item[] items = getChildren(widget);
1534
		ArrayList result,
1510
	for (int i = 0; i < items.length; i++) {
1535
		Widget widget) {
1511
		Item item = items[i];
1536
		Item[] items = getChildren(widget);
1512
		if (getExpanded(item)) {
1537
		for (int i = 0; i < items.length; i++) {
1513
			Object data = item.getData();
1538
			Item item = items[i];
1514
			if (data != null)
1539
			if (getExpanded(item)) {
1515
				result.add(data);
1540
				Object data = item.getData();
1516
			//Only recurse if it is expanded - if
1541
				if (data != null)
1517
			//not then the children aren't visible
1542
					result.add(data);
1518
			internalCollectVisibleExpanded(result, item);
1543
				//Only recurse if it is expanded - if
1544
				//not then the children aren't visible
1545
				internalCollectVisibleExpanded(result, item);
1546
			}
1519
		}
1547
		}
1520
	}
1548
	}
1521
}
1522
}
1549
}

Return to bug 38782