Source code: org/eclipse/swt/widgets/Tree.java
1 /*******************************************************************************
2 * Copyright (c) 2000, 2004 IBM Corporation and others.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the Common Public License v1.0
5 * which accompanies this distribution, and is available at
6 * http://www.eclipse.org/legal/cpl-v10.html
7 *
8 * Contributors:
9 * IBM Corporation - initial API and implementation
10 *******************************************************************************/
11 package org.eclipse.swt.widgets;
12
13
14 import org.eclipse.swt.*;
15 import org.eclipse.swt.internal.gtk.*;
16 import org.eclipse.swt.graphics.*;
17 import org.eclipse.swt.events.*;
18
19 /**
20 * Instances of this class provide a selectable user interface object
21 * that displays a hierarchy of items and issue notificiation when an
22 * item in the hierarchy is selected.
23 * <p>
24 * The item children that may be added to instances of this class
25 * must be of type <code>TreeItem</code>.
26 * </p><p>
27 * Note that although this class is a subclass of <code>Composite</code>,
28 * it does not make sense to add <code>Control</code> children to it,
29 * or set a layout on it.
30 * </p><p>
31 * <dl>
32 * <dt><b>Styles:</b></dt>
33 * <dd>SINGLE, MULTI, CHECK</dd>
34 * <dt><b>Events:</b></dt>
35 * <dd>Selection, DefaultSelection, Collapse, Expand</dd>
36 * </dl>
37 * <p>
38 * Note: Only one of the styles SINGLE and MULTI may be specified.
39 * </p><p>
40 * IMPORTANT: This class is <em>not</em> intended to be subclassed.
41 * </p>
42 */
43 public class Tree extends Composite {
44 long /*int*/ modelHandle, columnHandle, checkRenderer, pixbufRenderer, textRenderer;
45 TreeItem[] items;
46 ImageList imageList;
47
48 static final int TEXT_COLUMN = 0;
49 static final int PIXBUF_COLUMN = 1;
50 static final int FOREGROUND_COLUMN = 2;
51 static final int BACKGROUND_COLUMN = 3;
52 static final int FONT_COLUMN = 4;
53 static final int ID_COLUMN = 5;
54 static final int CHECKED_COLUMN = 6;
55 static final int GRAYED_COLUMN = 7;
56
57 /**
58 * Constructs a new instance of this class given its parent
59 * and a style value describing its behavior and appearance.
60 * <p>
61 * The style value is either one of the style constants defined in
62 * class <code>SWT</code> which is applicable to instances of this
63 * class, or must be built by <em>bitwise OR</em>'ing together
64 * (that is, using the <code>int</code> "|" operator) two or more
65 * of those <code>SWT</code> style constants. The class description
66 * lists the style constants that are applicable to the class.
67 * Style bits are also inherited from superclasses.
68 * </p>
69 *
70 * @param parent a composite control which will be the parent of the new instance (cannot be null)
71 * @param style the style of control to construct
72 *
73 * @exception IllegalArgumentException <ul>
74 * <li>ERROR_NULL_ARGUMENT - if the parent is null</li>
75 * </ul>
76 * @exception SWTException <ul>
77 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li>
78 * <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li>
79 * </ul>
80 *
81 * @see SWT#SINGLE
82 * @see SWT#MULTI
83 * @see SWT#CHECK
84 * @see Widget#checkSubclass
85 * @see Widget#getStyle
86 */
87 public Tree (Composite parent, int style) {
88 super (parent, checkStyle (style));
89 }
90
91 static int checkStyle (int style) {
92 style |= SWT.H_SCROLL | SWT.V_SCROLL;
93 return checkBits (style, SWT.SINGLE, SWT.MULTI, 0, 0, 0, 0);
94 }
95
96 /**
97 * Adds the listener to the collection of listeners who will
98 * be notified when the receiver's selection changes, by sending
99 * it one of the messages defined in the <code>SelectionListener</code>
100 * interface.
101 * <p>
102 * When <code>widgetSelected</code> is called, the item field of the event object is valid.
103 * If the reciever has <code>SWT.CHECK</code> style set and the check selection changes,
104 * the event object detail field contains the value <code>SWT.CHECK</code>.
105 * <code>widgetDefaultSelected</code> is typically called when an item is double-clicked.
106 * The item field of the event object is valid for default selection, but the detail field is not used.
107 * </p>
108 *
109 * @param listener the listener which should be notified
110 *
111 * @exception IllegalArgumentException <ul>
112 * <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
113 * </ul>
114 * @exception SWTException <ul>
115 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
116 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
117 * </ul>
118 *
119 * @see SelectionListener
120 * @see #removeSelectionListener
121 * @see SelectionEvent
122 */
123 public void addSelectionListener(SelectionListener listener) {
124 checkWidget ();
125 if (listener == null) error (SWT.ERROR_NULL_ARGUMENT);
126 TypedListener typedListener = new TypedListener (listener);
127 addListener (SWT.Selection, typedListener);
128 addListener (SWT.DefaultSelection, typedListener);
129 }
130
131 /**
132 * Adds the listener to the collection of listeners who will
133 * be notified when an item in the receiver is expanded or collapsed
134 * by sending it one of the messages defined in the <code>TreeListener</code>
135 * interface.
136 *
137 * @param listener the listener which should be notified
138 *
139 * @exception IllegalArgumentException <ul>
140 * <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
141 * </ul>
142 * @exception SWTException <ul>
143 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
144 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
145 * </ul>
146 *
147 * @see TreeListener
148 * @see #removeTreeListener
149 */
150 public void addTreeListener(TreeListener listener) {
151 checkWidget ();
152 if (listener == null) error (SWT.ERROR_NULL_ARGUMENT);
153 TypedListener typedListener = new TypedListener (listener);
154 addListener (SWT.Expand, typedListener);
155 addListener (SWT.Collapse, typedListener);
156 }
157
158 public Point computeSize (int wHint, int hHint, boolean changed) {
159 checkWidget ();
160 if (wHint != SWT.DEFAULT && wHint < 0) wHint = 0;
161 if (hHint != SWT.DEFAULT && hHint < 0) hHint = 0;
162 Point size = computeNativeSize (handle, wHint, hHint, changed);
163 Rectangle trim = computeTrim (0, 0, size.x, size.y);
164 size.x = trim.width;
165 size.y = trim.height;
166 return size;
167 }
168
169 void createHandle (int index) {
170 state |= HANDLE;
171 fixedHandle = OS.gtk_fixed_new ();
172 if (fixedHandle == 0) error (SWT.ERROR_NO_HANDLES);
173 OS.gtk_fixed_set_has_window (fixedHandle, true);
174 scrolledHandle = OS.gtk_scrolled_window_new (0, 0);
175 if (scrolledHandle == 0) error (SWT.ERROR_NO_HANDLES);
176 /*
177 * Columns:
178 * 0 - text
179 * 1 - pixmap
180 * 2 - foreground
181 * 3 - background
182 * 4 - font
183 * 5 - id
184 * 6 - checked (if needed)
185 * 7 - grayed (if needed)
186 */
187 long /*int*/ [] types = new long /*int*/ [(style & SWT.CHECK) !=0 ? 8 : 6];
188 types [TEXT_COLUMN] = OS.G_TYPE_STRING ();
189 types [PIXBUF_COLUMN] = OS.GDK_TYPE_PIXBUF ();
190 types [FOREGROUND_COLUMN] = OS.GDK_TYPE_COLOR ();
191 types [BACKGROUND_COLUMN] = OS.GDK_TYPE_COLOR ();
192 types [FONT_COLUMN] = OS.PANGO_TYPE_FONT_DESCRIPTION ();
193 types [ID_COLUMN] = OS.G_TYPE_INT ();
194 if ((style & SWT.CHECK) != 0) {
195 types [CHECKED_COLUMN] = OS.G_TYPE_BOOLEAN ();
196 types [GRAYED_COLUMN] = OS.G_TYPE_BOOLEAN ();
197 }
198 modelHandle = OS.gtk_tree_store_newv (types.length, types);
199 if (modelHandle == 0) error (SWT.ERROR_NO_HANDLES);
200 handle = OS.gtk_tree_view_new_with_model (modelHandle);
201 if (handle == 0) error (SWT.ERROR_NO_HANDLES);
202
203 /*
204 * Bug in ATK. For some reason, ATK segments fault if
205 * the GtkTreeView has a column and does not have items.
206 * The fix is to insert the column only when an item is
207 * created.
208 */
209 columnHandle = OS.gtk_tree_view_column_new ();
210 if (columnHandle == 0) error (SWT.ERROR_NO_HANDLES);
211 OS.g_object_ref (columnHandle);
212
213 if ((style & SWT.CHECK) != 0) {
214 checkRenderer = OS.gtk_cell_renderer_toggle_new ();
215 if (checkRenderer == 0) error (SWT.ERROR_NO_HANDLES);
216 OS.gtk_tree_view_column_pack_start (columnHandle, checkRenderer, false);
217 OS.gtk_tree_view_column_add_attribute (columnHandle, checkRenderer, "active", CHECKED_COLUMN);
218
219 /*
220 * Feature in GTK. The inconsistent property only exists in GTK 2.2.x.
221 */
222 if (OS.gtk_major_version () > 2 || (OS.gtk_major_version () == 2 && OS.gtk_minor_version () >= 2)) {
223 OS.gtk_tree_view_column_add_attribute (columnHandle, checkRenderer, "inconsistent", GRAYED_COLUMN);
224 }
225 }
226 pixbufRenderer = OS.gtk_cell_renderer_pixbuf_new ();
227 if (pixbufRenderer == 0) error (SWT.ERROR_NO_HANDLES);
228 OS.gtk_tree_view_column_pack_start (columnHandle, pixbufRenderer, false);
229 OS.gtk_tree_view_column_add_attribute (columnHandle, pixbufRenderer, "pixbuf", PIXBUF_COLUMN);
230 /*
231 * Feature on GTK. When a tree view column contains only one activatable
232 * cell renderer such as a toggle renderer, mouse clicks anywhere in a cell
233 * activate that renderer. The workaround is to set a second cell renderer
234 * to be activatable.
235 */
236 if ((style & SWT.CHECK) != 0) {
237 OS.g_object_set (pixbufRenderer, OS.mode, OS.GTK_CELL_RENDERER_MODE_ACTIVATABLE, 0);
238 }
239 textRenderer = OS.gtk_cell_renderer_text_new ();
240 if (textRenderer == 0) error (SWT.ERROR_NO_HANDLES);
241 OS.gtk_tree_view_column_pack_start (columnHandle, textRenderer, true);
242 OS.gtk_tree_view_column_add_attribute (columnHandle, textRenderer, "text", TEXT_COLUMN);
243 OS.gtk_tree_view_column_add_attribute (columnHandle, textRenderer, "foreground-gdk", FOREGROUND_COLUMN);
244 OS.gtk_tree_view_column_add_attribute (columnHandle, textRenderer, "background-gdk", BACKGROUND_COLUMN);
245 OS.gtk_tree_view_column_add_attribute (columnHandle, textRenderer, "font-desc", FONT_COLUMN);
246 long /*int*/ parentHandle = parent.parentingHandle ();
247 OS.gtk_container_add (parentHandle, fixedHandle);
248 OS.gtk_container_add (fixedHandle, scrolledHandle);
249 OS.gtk_container_add (scrolledHandle, handle);
250 OS.gtk_widget_show (fixedHandle);
251 OS.gtk_widget_show (scrolledHandle);
252 OS.gtk_widget_show (handle);
253
254 int mode = (style & SWT.MULTI) != 0 ? OS.GTK_SELECTION_MULTIPLE : OS.GTK_SELECTION_BROWSE;
255 long /*int*/ selectionHandle = OS.gtk_tree_view_get_selection (handle);
256 OS.gtk_tree_selection_set_mode (selectionHandle, mode);
257 OS.gtk_tree_view_set_headers_visible (handle, false);
258 int hsp = (style & SWT.H_SCROLL) != 0 ? OS.GTK_POLICY_AUTOMATIC : OS.GTK_POLICY_NEVER;
259 int vsp = (style & SWT.V_SCROLL) != 0 ? OS.GTK_POLICY_AUTOMATIC : OS.GTK_POLICY_NEVER;
260 OS.gtk_scrolled_window_set_policy (scrolledHandle, hsp, vsp);
261 if ((style & SWT.BORDER) != 0) OS.gtk_scrolled_window_set_shadow_type (scrolledHandle, OS.GTK_SHADOW_ETCHED_IN);
262 }
263
264 void createItem (TreeItem item, long /*int*/ iter, int index) {
265 long /*int*/ column = OS.gtk_tree_view_get_column (handle, 0);
266 if (column == 0) OS.gtk_tree_view_insert_column (handle, columnHandle, 0);
267 int count = OS.gtk_tree_model_iter_n_children (modelHandle, iter);
268 if (index == -1) index = count;
269 if (!(0 <= index && index <= count)) error (SWT.ERROR_INVALID_RANGE);
270 int id = 0;
271 while (id < items.length && items [id] != null) id++;
272 if (id == items.length) {
273 TreeItem [] newItems = new TreeItem [items.length + 4];
274 System.arraycopy (items, 0, newItems, 0, items.length);
275 items = newItems;
276 }
277 item.handle = OS.g_malloc (OS.GtkTreeIter_sizeof ());
278 if (item.handle == 0) error(SWT.ERROR_NO_HANDLES);
279 if (index == count) {
280 OS.gtk_tree_store_append (modelHandle, item.handle, iter);
281 } else {
282 OS.gtk_tree_store_insert (modelHandle, item.handle, iter, index);
283 }
284 OS.gtk_tree_store_set (modelHandle, item.handle, ID_COLUMN, id, -1);
285 items [id] = item;
286 }
287
288 void createWidget (int index) {
289 super.createWidget (index);
290 items = new TreeItem [4];
291 }
292
293 GdkColor defaultBackground () {
294 return display.COLOR_LIST_BACKGROUND;
295 }
296
297 GdkColor defaultForeground () {
298 return display.COLOR_LIST_FOREGROUND;
299 }
300
301 void deregister () {
302 super.deregister ();
303 display.removeWidget (OS.gtk_tree_view_get_selection (handle));
304 if (checkRenderer != 0) display.removeWidget (checkRenderer);
305 }
306
307 /**
308 * Deselects all selected items in the receiver.
309 *
310 * @exception SWTException <ul>
311 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
312 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
313 * </ul>
314 */
315 public void deselectAll() {
316 checkWidget();
317 long /*int*/ selection = OS.gtk_tree_view_get_selection (handle);
318 OS.g_signal_handlers_block_matched (selection, OS.G_SIGNAL_MATCH_DATA, 0, 0, 0, 0, CHANGED);
319 OS.gtk_tree_selection_unselect_all (selection);
320 OS.g_signal_handlers_unblock_matched (selection, OS.G_SIGNAL_MATCH_DATA, 0, 0, 0, 0, CHANGED);
321 }
322
323 void destroyItem (TreeItem item) {
324 /*
325 * Bug in GTK. GTK segment faults when a root tree item
326 * is destroyed when the tree is expanded and the last leaf of
327 * the root is selected. This only happens in versions earlier
328 * than 2.0.6. The fix is to collapse the tree item being destroyed
329 * when it is a root, before it is destroyed.
330 */
331 if (OS.gtk_major_version () == 2 && OS.gtk_minor_version () == 0 && OS.gtk_micro_version () < 6) {
332 TreeItem [] roots = getItems (0);
333 for (int i = 0; i < roots.length; i++) {
334 if (item == roots [i]) {
335 item.setExpanded (false);
336 break;
337 }
338 }
339 }
340 int [] index = new int [1];
341 releaseItems (item.getItems (), index);
342 releaseItem (item, index);
343 long /*int*/ selection = OS.gtk_tree_view_get_selection (handle);
344 OS.g_signal_handlers_block_matched (selection, OS.G_SIGNAL_MATCH_DATA, 0, 0, 0, 0, CHANGED);
345 OS.gtk_tree_store_remove (modelHandle, item.handle);
346 OS.g_signal_handlers_unblock_matched (selection, OS.G_SIGNAL_MATCH_DATA, 0, 0, 0, 0, CHANGED);
347 int childCount = OS.gtk_tree_model_iter_n_children (modelHandle, 0);
348 if (childCount == 0) removeColumn ();
349 }
350
351 void destroyWidget () {
352 /*
353 * Bug in GTK. Sometimes GTK causes a segment fault when a tree widget is
354 * destroyed and it has outstanding events or idle handlers. This only happens
355 * on versions earlier than 2.0.5. The fix is to flush all outstanding events before
356 * destroying the widget.
357 */
358 if (OS.gtk_major_version () == 2 && OS.gtk_minor_version () == 0 && OS.gtk_micro_version () < 5) {
359 while (OS.gtk_events_pending () != 0) OS.gtk_main_iteration ();
360 }
361 super.destroyWidget ();
362 }
363
364 GdkColor getBackgroundColor () {
365 return getBaseColor ();
366 }
367
368 TreeItem getFocusItem () {
369 long /*int*/ [] path = new long /*int*/ [1];
370 OS.gtk_tree_view_get_cursor (handle, path, null);
371 if (path [0] == 0) return null;
372 TreeItem item = null;
373 long /*int*/ iter = OS.g_malloc (OS.GtkTreeIter_sizeof ());
374 if (OS.gtk_tree_model_get_iter (modelHandle, iter, path [0])) {
375 int [] index = new int [1];
376 OS.gtk_tree_model_get (modelHandle, iter, ID_COLUMN, index, -1);
377 item = items [index [0]];
378 }
379 OS.g_free (iter);
380 OS.gtk_tree_path_free (path [0]);
381 return item;
382 }
383
384 GdkColor getForegroundColor () {
385 return getTextColor ();
386 }
387
388 /**
389 * Returns the item at the given point in the receiver
390 * or null if no such item exists. The point is in the
391 * coordinate system of the receiver.
392 *
393 * @param point the point used to locate the item
394 * @return the item at the given point
395 *
396 * @exception IllegalArgumentException <ul>
397 * <li>ERROR_NULL_ARGUMENT - if the point is null</li>
398 * </ul>
399 * @exception SWTException <ul>
400 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
401 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
402 * </ul>
403 */
404 public TreeItem getItem (Point point) {
405 checkWidget ();
406 long /*int*/ [] path = new long /*int*/ [1];
407 OS.gtk_widget_realize (handle);
408 if (!OS.gtk_tree_view_get_path_at_pos (handle, point.x, point.y, path, null, null, null)) return null;
409 if (path [0] == 0) return null;
410 TreeItem item = null;
411 long /*int*/ iter = OS.g_malloc (OS.GtkTreeIter_sizeof ());
412 if (OS.gtk_tree_model_get_iter (modelHandle, iter, path [0])) {
413 boolean overExpander = false;
414 if (OS.gtk_tree_model_iter_n_children (modelHandle, iter) > 0) {
415 int[] buffer = new int [1];
416 GdkRectangle rect = new GdkRectangle ();
417 OS.gtk_tree_view_get_cell_area (handle, path [0], columnHandle, rect);
418 OS.gtk_widget_style_get (handle, OS.expander_size, buffer, 0);
419 int expanderSize = buffer [0] + TreeItem.EXPANDER_EXTRA_PADDING;
420 overExpander = rect.x - 1 <= point.x && point.x < rect.x + expanderSize;
421 }
422 if (!overExpander) {
423 int [] index = new int [1];
424 OS.gtk_tree_model_get (modelHandle, iter, ID_COLUMN, index, -1);
425 item = items [index [0]];
426 }
427 }
428 OS.g_free (iter);
429 OS.gtk_tree_path_free (path [0]);
430 return item;
431 }
432
433 /**
434 * Returns the number of items contained in the receiver
435 * that are direct item children of the receiver. The
436 * number that is returned is the number of roots in the
437 * tree.
438 *
439 * @return the number of items
440 *
441 * @exception SWTException <ul>
442 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
443 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
444 * </ul>
445 */
446 public int getItemCount () {
447 checkWidget ();
448 return OS.gtk_tree_model_iter_n_children (modelHandle, 0);
449 }
450
451 /**
452 * Returns the height of the area which would be used to
453 * display <em>one</em> of the items in the tree.
454 *
455 * @return the height of one item
456 *
457 * @exception SWTException <ul>
458 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
459 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
460 * </ul>
461 */
462 public int getItemHeight () {
463 checkWidget ();
464 int itemCount = OS.gtk_tree_model_iter_n_children (modelHandle, 0);
465 if (itemCount == 0) {
466 int [] w = new int [1], h = new int [1];
467 OS.gtk_tree_view_insert_column (handle, columnHandle, 0);
468 OS.gtk_tree_view_column_cell_get_size (columnHandle, null, null, null, w, h);
469 OS.gtk_tree_view_remove_column (handle, columnHandle);
470 return h [0];
471 } else {
472 long /*int*/ iter = OS.g_malloc (OS.GtkTreeIter_sizeof ());
473 OS.gtk_tree_model_get_iter_first (modelHandle, iter);
474 long /*int*/ column = OS.gtk_tree_view_get_column (handle, 0);
475 OS.gtk_tree_view_column_cell_set_cell_data (column, modelHandle, iter, false, false);
476 int [] w = new int [1], h = new int [1];
477 OS.gtk_tree_view_column_cell_get_size (column, null, null, null, w, h);
478 OS.g_free (iter);
479 return h [0];
480 }
481 }
482
483 /**
484 * Returns the items contained in the receiver
485 * that are direct item children of the receiver. These
486 * are the roots of the tree.
487 * <p>
488 * Note: This is not the actual structure used by the receiver
489 * to maintain its list of items, so modifying the array will
490 * not affect the receiver.
491 * </p>
492 *
493 * @return the items
494 *
495 * @exception SWTException <ul>
496 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
497 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
498 * </ul>
499 */
500 public TreeItem [] getItems () {
501 checkWidget();
502 return getItems (0);
503 }
504
505 TreeItem [] getItems (long /*int*/ parent) {
506 int length = OS.gtk_tree_model_iter_n_children (modelHandle, parent);
507 TreeItem[] result = new TreeItem [length];
508 if (length == 0) return result;
509 int i = 0;
510 int[] index = new int [1];
511 long /*int*/ iter = OS.g_malloc (OS.GtkTreeIter_sizeof ());
512 boolean valid = OS.gtk_tree_model_iter_children (modelHandle, iter, parent);
513 while (valid) {
514 OS.gtk_tree_model_get (modelHandle, iter, ID_COLUMN, index, -1);
515 result [i++] = items [index [0]];
516 valid = OS.gtk_tree_model_iter_next (modelHandle, iter);
517 }
518 OS.g_free (iter);
519 return result;
520 }
521
522 /**
523 * Returns the receiver's parent item, which must be a
524 * <code>TreeItem</code> or null when the receiver is a
525 * root.
526 *
527 * @return the receiver's parent item
528 *
529 * @exception SWTException <ul>
530 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
531 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
532 * </ul>
533 */
534 public TreeItem getParentItem () {
535 checkWidget ();
536 return null;
537 }
538
539 /**
540 * Returns an array of <code>TreeItem</code>s that are currently
541 * selected in the receiver. An empty array indicates that no
542 * items are selected.
543 * <p>
544 * Note: This is not the actual structure used by the receiver
545 * to maintain its selection, so modifying the array will
546 * not affect the receiver.
547 * </p>
548 * @return an array representing the selection
549 *
550 * @exception SWTException <ul>
551 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
552 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
553 * </ul>
554 */
555 public TreeItem[] getSelection () {
556 checkWidget();
557 if ((style & SWT.MULTI) != 0) {
558 display.treeSelectionLength = 0;
559 display.treeSelection = new int [items.length];
560 long /*int*/ selection = OS.gtk_tree_view_get_selection (handle);
561 OS.gtk_tree_selection_selected_foreach (selection, display.treeSelectionProc, handle);
562 TreeItem [] result = new TreeItem [display.treeSelectionLength];
563 for (int i=0; i<result.length; i++) result [i] = items [display.treeSelection [i]];
564 return result;
565 } else {
566 long /*int*/ iter = OS.g_malloc (OS.GtkTreeIter_sizeof ());
567 long /*int*/ selection = OS.gtk_tree_view_get_selection (handle);
568 boolean hasSelection = OS.gtk_tree_selection_get_selected (selection, null, iter);
569 TreeItem [] result;
570 if (hasSelection) {
571 int [] index = new int [1];
572 OS.gtk_tree_model_get (modelHandle, iter, ID_COLUMN, index, -1);
573 result = new TreeItem []{items [index [0]]};
574 } else {
575 result = new TreeItem [0];
576 }
577 OS.g_free (iter);
578 return result;
579 }
580 }
581
582 /**
583 * Returns the number of selected items contained in the receiver.
584 *
585 * @return the number of selected items
586 *
587 * @exception SWTException <ul>
588 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
589 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
590 * </ul>
591 */
592 public int getSelectionCount () {
593 checkWidget();
594 display.treeSelectionLength = 0;
595 display.treeSelection = null;
596 long /*int*/ selection = OS.gtk_tree_view_get_selection (handle);
597 OS.gtk_tree_selection_selected_foreach (selection, display.treeSelectionProc, handle);
598 return display.treeSelectionLength;
599 }
600
601 /**
602 * Returns the item which is currently at the top of the receiver.
603 * This item can change when items are expanded, collapsed, scrolled
604 * or new items are added or removed.
605 *
606 * @return the item at the top of the receiver
607 *
608 * @exception SWTException <ul>
609 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
610 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
611 * </ul>
612 *
613 * @since 2.1
614 */
615 public TreeItem getTopItem () {
616 checkWidget ();
617 long /*int*/ [] path = new long /*int*/ [1];
618 OS.gtk_widget_realize (handle);
619 if (!OS.gtk_tree_view_get_path_at_pos (handle, 1, 1, path, null, null, null)) return null;
620 if (path [0] == 0) return null;
621 TreeItem item = null;
622 long /*int*/ iter = OS.g_malloc (OS.GtkTreeIter_sizeof());
623 if (OS.gtk_tree_model_get_iter (modelHandle, iter, path [0])) {
624 int [] index = new int [1];
625 OS.gtk_tree_model_get (modelHandle, iter, ID_COLUMN, index, -1);
626 item = items [index [0]];
627 }
628 OS.g_free (iter);
629 OS.gtk_tree_path_free (path [0]);
630 return item;
631 }
632
633 long /*int*/ gtk_changed (long /*int*/ widget) {
634 TreeItem item = getFocusItem ();
635 if (item != null) {
636 Event event = new Event ();
637 event.item = item;
638 postEvent (SWT.Selection, event);
639 }
640 return 0;
641 }
642
643 long /*int*/ gtk_key_press_event (long /*int*/ widget, long /*int*/ eventPtr) {
644 long /*int*/ result = super.gtk_key_press_event (widget, eventPtr);
645 if (result != 0) return result;
646
647 /*
648 * Feature in GTK. When an item is default selected using
649 * the return key, GTK does not issue notification. The fix is
650 * to issue this notification when the return key is pressed.
651 */
652 GdkEventKey keyEvent = new GdkEventKey ();
653 OS.memmove (keyEvent, eventPtr, GdkEventKey.sizeof);
654 int key = keyEvent.keyval;
655 switch (key) {
656 case OS.GDK_Return:
657 case OS.GDK_KP_Enter: {
658 Event event = new Event ();
659 event.item = getFocusItem ();
660 postEvent (SWT.DefaultSelection, event);
661 break;
662 }
663 }
664 return result;
665 }
666
667 long /*int*/ gtk_row_activated (long /*int*/ tree, long /*int*/ path, long /*int*/ column) {
668 if (path == 0) return 0;
669 TreeItem item = null;
670 long /*int*/ iter = OS.g_malloc (OS.GtkTreeIter_sizeof ());
671 if (OS.gtk_tree_model_get_iter (modelHandle, iter, path)) {
672 int [] index = new int [1];
673 OS.gtk_tree_model_get (modelHandle, iter, ID_COLUMN, index, -1);
674 item = items [index [0]];
675 }
676 OS.g_free (iter);
677 Event event = new Event ();
678 event.item = item;
679 postEvent (SWT.DefaultSelection, event);
680 return 0;
681 }
682
683 long /*int*/ gtk_test_collapse_row (long /*int*/ tree, long /*int*/ iter, long /*int*/ path) {
684 int [] index = new int [1];
685 OS.gtk_tree_model_get (modelHandle, iter, ID_COLUMN, index, -1);
686 Event event = new Event ();
687 event.item = items [index [0]];
688 sendEvent (SWT.Collapse, event);
689 if (isDisposed ()) return 0;
690 OS.g_signal_handlers_block_matched (handle, OS.G_SIGNAL_MATCH_DATA, 0, 0, 0, 0, TEST_COLLAPSE_ROW);
691 OS.gtk_tree_view_collapse_row (handle, path);
692 OS.g_signal_handlers_unblock_matched (handle, OS.G_SIGNAL_MATCH_DATA, 0, 0, 0, 0, TEST_COLLAPSE_ROW);
693 return 1;
694 }
695
696 long /*int*/ gtk_test_expand_row (long /*int*/ tree, long /*int*/ iter, long /*int*/ path) {
697 int [] index = new int [1];
698 OS.gtk_tree_model_get (modelHandle, iter, ID_COLUMN, index, -1);
699 Event event = new Event ();
700 event.item = items [index [0]];
701 sendEvent (SWT.Expand, event);
702 if (isDisposed ()) return 0;
703 OS.g_signal_handlers_block_matched (handle, OS.G_SIGNAL_MATCH_DATA, 0, 0, 0, 0, TEST_EXPAND_ROW);
704 OS.gtk_tree_view_expand_row (handle, path, false);
705 OS.g_signal_handlers_unblock_matched (handle, OS.G_SIGNAL_MATCH_DATA, 0, 0, 0, 0, TEST_EXPAND_ROW);
706 return 1;
707 }
708
709 long /*int*/ gtk_toggled (long /*int*/ renderer, long /*int*/ pathStr) {
710 long /*int*/ path = OS.gtk_tree_path_new_from_string (pathStr);
711 if (path == 0) return 0;
712 TreeItem item = null;
713 long /*int*/ iter = OS.g_malloc (OS.GtkTreeIter_sizeof());
714 if (OS.gtk_tree_model_get_iter (modelHandle, iter, path)) {
715 int [] index = new int [1];
716 OS.gtk_tree_model_get (modelHandle, iter, ID_COLUMN, index, -1);
717 item = items [index [0]];
718 }
719 OS.g_free (iter);
720 OS.gtk_tree_path_free (path);
721 item.setChecked (!item.getChecked ());
722 Event event = new Event ();
723 event.detail = SWT.CHECK;
724 event.item = item;
725 postEvent (SWT.Selection, event);
726 return 0;
727 }
728
729 long /*int*/ gtk_button_press_event (long /*int*/ widget, long /*int*/ event) {
730 long /*int*/ result = super.gtk_button_press_event (widget, event);
731 if (result != 0) return result;
732 /*
733 * Bug in GTK. GTK segments fault, if the GtkTreeView widget is
734 * not in focus and all items in the widget are disposed before
735 * it finishes processing a button press. The fix is to give
736 * focus to the widget before it starts processing the event.
737 */
738 if (!OS.GTK_WIDGET_HAS_FOCUS (handle)) {
739 OS.gtk_widget_grab_focus (handle);
740 // widget may be disposed at this point
741 if (isDisposed ()) return 0;
742 }
743 /*
744 * Feature in GTK. In a multi-select tree view, when multiple items are already
745 * selected, the selection state of the item is toggled and the previous selection
746 * is cleared. This is not the desired behaviour when bringing up a popup menu.
747 * Also, when an item is reselected with the right button, the tree view issues
748 * an unwanted selection event. The workaround is to detect that case and not
749 * run the default handler when the item is already part of the current selection.
750 */
751 GdkEventButton gdkEvent = new GdkEventButton ();
752 OS.memmove (gdkEvent, event, GdkEventButton.sizeof);
753 int button = gdkEvent.button;
754 if (button == 3 && gdkEvent.type == OS.GDK_BUTTON_PRESS) {
755 long /*int*/ [] path = new long /*int*/ [1];
756 if (OS.gtk_tree_view_get_path_at_pos (handle, (int)gdkEvent.x, (int)gdkEvent.y, path, null, null, null)) {
757 if (path [0] != 0) {
758 long /*int*/ selection = OS.gtk_tree_view_get_selection (handle);
759 if (OS.gtk_tree_selection_path_is_selected (selection, path [0])) result = 1;
760 OS.gtk_tree_path_free (path [0]);
761 }
762 }
763 }
764
765 /*
766 * Feature in GTK. When the user clicks in a single selection GtkTreeView
767 * and there are no selected items, the first item is selected automatically
768 * before the click is processed, causing two selection events. The is fix
769 * is the set the cursor item to be same as the clicked item to stop the
770 * widget from automatically selecting the first item.
771 */
772 if ((style & SWT.SINGLE) != 0 && getSelectionCount () == 0) {
773 long /*int*/ [] path = new long /*int*/ [1];
774 if (OS.gtk_tree_view_get_path_at_pos (handle, (int)gdkEvent.x, (int)gdkEvent.y, path, null, null, null)) {
775 if (path [0] != 0) {
776 long /*int*/ selection = OS.gtk_tree_view_get_selection (handle);
777 OS.g_signal_handlers_block_matched (selection, OS.G_SIGNAL_MATCH_DATA, 0, 0, 0, 0, CHANGED);
778 OS.gtk_tree_view_set_cursor (handle, path [0], 0, false);
779 OS.g_signal_handlers_unblock_matched (selection, OS.G_SIGNAL_MATCH_DATA, 0, 0, 0, 0, CHANGED);
780 OS.gtk_tree_path_free (path [0]);
781 }
782 }
783 }
784
785 return result;
786 }
787
788 void hookEvents () {
789 super.hookEvents ();
790 long /*int*/ selection = OS.gtk_tree_view_get_selection(handle);
791 OS.g_signal_connect_after (selection, OS.changed, display.windowProc2, CHANGED);
792 OS.g_signal_connect (handle, OS.row_activated, display.windowProc4, ROW_ACTIVATED);
793 OS.g_signal_connect (handle, OS.test_expand_row, display.windowProc4, TEST_EXPAND_ROW);
794 OS.g_signal_connect (handle, OS.test_collapse_row, display.windowProc4, TEST_COLLAPSE_ROW);
795 if (checkRenderer != 0) {
796 OS.g_signal_connect (checkRenderer, OS.toggled, display.windowProc3, TOGGLED);
797 }
798 }
799
800 long /*int*/ paintWindow () {
801 OS.gtk_widget_realize (handle);
802 return OS.gtk_tree_view_get_bin_window (handle);
803 }
804
805 void register () {
806 super.register ();
807 display.addWidget (OS.gtk_tree_view_get_selection (handle), this);
808 if (checkRenderer != 0) display.addWidget (checkRenderer, this);
809 }
810
811 boolean releaseItem (TreeItem item, int [] index) {
812 if (item.isDisposed ()) return false;
813 OS.gtk_tree_model_get (modelHandle, item.handle, ID_COLUMN, index, -1);
814 items [index [0]] = null;
815 return true;
816 }
817
818 void releaseItems (TreeItem [] nodes, int [] index) {
819 for (int i=0; i<nodes.length; i++) {
820 TreeItem item = nodes [i];
821 TreeItem [] sons = item.getItems ();
822 if (sons.length != 0) {
823 releaseItems (sons, index);
824 }
825 if (releaseItem (item, index)) {
826 item.releaseResources ();
827 }
828 }
829 }
830
831 void releaseWidget () {
832 for (int i=0; i<items.length; i++) {
833 TreeItem item = items [i];
834 if (item != null && !item.isDisposed ()) item.releaseResources();
835 }
836 items = null;
837 super.releaseWidget();
838 if (modelHandle != 0) OS.g_object_unref (modelHandle);
839 modelHandle = 0;
840 if (columnHandle != 0) OS.g_object_unref (columnHandle);
841 columnHandle = 0;
842 if (imageList != null) {
843 imageList.dispose ();
844 imageList = null;
845 }
846 }
847
848 /**
849 * Removes all of the items from the receiver.
850 * <p>
851 * @exception SWTException <ul>
852 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
853 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
854 * </ul>
855 */
856 public void removeAll () {
857 checkWidget ();
858 for (int i=0; i<items.length; i++) {
859 TreeItem item = items [i];
860 if (item != null && !item.isDisposed ()) item.releaseResources ();
861 }
862 items = new TreeItem[4];
863 long /*int*/ selection = OS.gtk_tree_view_get_selection (handle);
864 OS.g_signal_handlers_block_matched (selection, OS.G_SIGNAL_MATCH_DATA, 0, 0, 0, 0, CHANGED);
865 OS.gtk_tree_store_clear (modelHandle);
866 OS.g_signal_handlers_unblock_matched (selection, OS.G_SIGNAL_MATCH_DATA, 0, 0, 0, 0, CHANGED);
867 removeColumn ();
868 }
869
870 void removeColumn () {
871 long /*int*/ column = OS.gtk_tree_view_get_column (handle, 0);
872 if (column == 0) return;
873 OS.gtk_tree_view_remove_column (handle, column);
874 }
875
876 /**
877 * Removes the listener from the collection of listeners who will
878 * be notified when the receiver's selection changes.
879 *
880 * @param listener the listener which should no longer be notified
881 *
882 * @exception IllegalArgumentException <ul>
883 * <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
884 * </ul>
885 * @exception SWTException <ul>
886 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
887 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
888 * </ul>
889 *
890 * @see SelectionListener
891 * @see #addSelectionListener
892 */
893 public void removeSelectionListener (SelectionListener listener) {
894 checkWidget ();
895 if (listener == null) error (SWT.ERROR_NULL_ARGUMENT);
896 eventTable.unhook (SWT.Selection, listener);
897 eventTable.unhook (SWT.DefaultSelection, listener);
898 }
899
900 /**
901 * Removes the listener from the collection of listeners who will
902 * be notified when items in the receiver are expanded or collapsed..
903 *
904 * @param listener the listener which should no longer be notified
905 *
906 * @exception IllegalArgumentException <ul>
907 * <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
908 * </ul>
909 * @exception SWTException <ul>
910 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
911 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
912 * </ul>
913 *
914 * @see TreeListener
915 * @see #addTreeListener
916 */
917 public void removeTreeListener(TreeListener listener) {
918 checkWidget ();
919 if (listener == null) error (SWT.ERROR_NULL_ARGUMENT);
920 if (eventTable == null) return;
921 eventTable.unhook (SWT.Expand, listener);
922 eventTable.unhook (SWT.Collapse, listener);
923 }
924
925 /**
926 * Display a mark indicating the point at which an item will be inserted.
927 * The drop insert item has a visual hint to show where a dragged item
928 * will be inserted when dropped on the tree.
929 *
930 * @param item the insert item. Null will clear the insertion mark.
931 * @param before true places the insert mark above 'item'. false places
932 * the insert mark below 'item'.
933 *
934 * @exception IllegalArgumentException <ul>
935 * <li>ERROR_INVALID_ARGUMENT - if the item has been disposed</li>
936 * </ul>
937 * @exception SWTException <ul>
938 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
939 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
940 * </ul>
941 */
942 public void setInsertMark (TreeItem item, boolean before) {
943 checkWidget ();
944 if (item == null) {
945 OS.gtk_tree_view_unset_rows_drag_dest(handle);
946 return;
947 }
948 if (item.isDisposed()) error (SWT.ERROR_INVALID_ARGUMENT);
949 if (item.parent != this) return;
950 Rectangle rect = item.getBounds();
951 long /*int*/ [] path = new long /*int*/ [1];
952 OS.gtk_widget_realize (handle);
953 if (!OS.gtk_tree_view_get_path_at_pos(handle, rect.x, rect.y, path, null, null, null)) return;
954 if (path [0] == 0) return;
955 int position = before ? OS.GTK_TREE_VIEW_DROP_BEFORE : OS.GTK_TREE_VIEW_DROP_AFTER;
956 OS.gtk_tree_view_set_drag_dest_row(handle, path[0], position);
957 OS.gtk_tree_path_free (path [0]);
958 }
959
960 /**
961 * Selects all of the items in the receiver.
962 * <p>
963 * If the receiver is single-select, do nothing.
964 *
965 * @exception SWTException <ul>
966 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
967 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
968 * </ul>
969 */
970 public void selectAll () {
971 checkWidget();
972 if ((style & SWT.SINGLE) != 0) return;
973 long /*int*/ selection = OS.gtk_tree_view_get_selection (handle);
974 OS.g_signal_handlers_block_matched (selection, OS.G_SIGNAL_MATCH_DATA, 0, 0, 0, 0, CHANGED);
975 OS.gtk_tree_selection_select_all (OS.gtk_tree_view_get_selection (handle));
976 OS.g_signal_handlers_unblock_matched (selection, OS.G_SIGNAL_MATCH_DATA, 0, 0, 0, 0, CHANGED);
977 }
978
979 void setBackgroundColor (GdkColor color) {
980 super.setBackgroundColor (color);
981 OS.gtk_widget_modify_base (handle, 0, color);
982 }
983
984 boolean setBounds (int x, int y, int width, int height, boolean move, boolean resize) {
985 boolean result = super.setBounds (x, y, width, height, move, resize);
986 /*
987 * Bug on GTK. The tree view sometimes does not get a paint
988 * event or resizes to a one pixel square when resized in a new
989 * shell that is not visible after any event loop has been run. The
990 * problem is intermittent. It doesn't seem to happen the first time
991 * a new shell is created. The fix is to ensure the tree view is realized
992 * after it has been resized.
993 */
994 OS.gtk_widget_realize (handle);
995 return result;
996 }
997
998 void setForegroundColor (GdkColor color) {
999 super.setForegroundColor (color);
1000 OS.gtk_widget_modify_text (handle, 0, color);
1001}
1002
1003/**
1004 * Sets the receiver's selection to be the given array of items.
1005 * The current selection is cleared before the new items are selected.
1006 * <p>
1007 * Items that are not in the receiver are ignored.
1008 * If the receiver is single-select and multiple items are specified,
1009 * then all items are ignored.
1010 *
1011 * @param items the array of items
1012 *
1013 * @exception IllegalArgumentException <ul>
1014 * <li>ERROR_NULL_ARGUMENT - if the array of items is null</li>
1015 * <li>ERROR_INVALID_ARGUMENT - if one of the items has been disposed</li>
1016 * </ul>
1017 * @exception SWTException <ul>
1018 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1019 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1020 * </ul>
1021 *
1022 * @see Tree#deselectAll()
1023 */
1024public void setSelection (TreeItem [] items) {
1025 checkWidget ();
1026 if (items == null) error (SWT.ERROR_NULL_ARGUMENT);
1027 int length = items.length;
1028 if (length == 0 || ((style & SWT.SINGLE) != 0 && length > 1)) {
1029 deselectAll ();
1030 return;
1031 }
1032 long /*int*/ selection = OS.gtk_tree_view_get_selection (handle);
1033 OS.g_signal_handlers_block_matched (selection, OS.G_SIGNAL_MATCH_DATA, 0, 0, 0, 0, CHANGED);
1034 OS.gtk_tree_selection_unselect_all (selection);
1035 boolean first = true;
1036 for (int i = 0; i < length; i++) {
1037 TreeItem item = items [i];
1038 if (item == null) continue;
1039 if (item.isDisposed ()) break;
1040 if (item.parent != this) continue;
1041 long /*int*/ path = OS.gtk_tree_model_get_path (modelHandle, item.handle);
1042 showItem (path, first);
1043 OS.gtk_tree_selection_select_iter (selection, item.handle);
1044 if ((style & SWT.SINGLE) != 0) {
1045 OS.gtk_tree_view_set_cursor (handle, path, 0, false);
1046 }
1047 OS.gtk_tree_path_free (path);
1048 first = false;
1049 }
1050 OS.g_signal_handlers_unblock_matched (selection, OS.G_SIGNAL_MATCH_DATA, 0, 0, 0, 0, CHANGED);
1051}
1052
1053/**
1054 * Sets the item which is currently at the top of the receiver.
1055 * This item can change when items are expanded, collapsed, scrolled
1056 * or new items are added or removed.
1057 *
1058 * @param item the item to be shown
1059 *
1060 * @exception IllegalArgumentException <ul>
1061 * <li>ERROR_NULL_ARGUMENT - if the item is null</li>
1062 * <li>ERROR_INVALID_ARGUMENT - if the item has been disposed</li>
1063 * </ul>
1064 * @exception SWTException <ul>
1065 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1066 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1067 * </ul>
1068 *
1069 * @see Tree#getTopItem()
1070 *
1071 * @since 2.1
1072 */
1073public void setTopItem (TreeItem item) {
1074 if (item == null) error (SWT.ERROR_NULL_ARGUMENT);
1075 if (item.isDisposed ()) error(SWT.ERROR_INVALID_ARGUMENT);
1076 if (item.parent != this) return;
1077 long /*int*/ path = OS.gtk_tree_model_get_path (modelHandle, item.handle);
1078 showItem (path, false);
1079 OS.gtk_tree_view_scroll_to_cell (handle, path, 0, true, 0, 0);
1080 OS.gtk_tree_path_free (path);
1081}
1082
1083/**
1084 * Shows the selection. If the selection is already showing in the receiver,
1085 * this method simply returns. Otherwise, the items are scrolled until
1086 * the selection is visible.
1087 *
1088 * @exception SWTException <ul>
1089 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1090 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1091 * </ul>
1092 *
1093 * @see Tree#showItem(TreeItem)
1094 */
1095public void showSelection () {
1096 checkWidget();
1097 TreeItem [] items = getSelection ();
1098 if (items.length != 0 && items [0] != null) showItem (items [0]);
1099}
1100
1101void showItem (long /*int*/ path, boolean scroll) {
1102 int depth = OS.gtk_tree_path_get_depth (path);
1103 if (depth > 1) {
1104 int [] indices = new int [depth - 1];
1105 long /*int*/ indicesPtr = OS.gtk_tree_path_get_indices (path);
1106 OS.memmove (indices, indicesPtr, indices.length * 4);
1107 long /*int*/ tempPath = OS.gtk_tree_path_new ();
1108 for (int i=0; i<indices.length; i++) {
1109 OS.gtk_tree_path_append_index (tempPath, indices [i]);
1110 OS.gtk_tree_view_expand_row (handle, tempPath, false);
1111 }
1112 OS.gtk_tree_path_free (tempPath);
1113 }
1114 if (scroll) {
1115 GdkRectangle rect = new GdkRectangle ();
1116 OS.gtk_widget_realize (handle);
1117 OS.gtk_tree_view_get_cell_area (handle, path, 0, rect);
1118 boolean isHidden = rect.y == 0 && rect.height == 0;
1119 if (!isHidden) {
1120 int [] tx = new int [1], ty = new int [1];
1121 OS.gtk_tree_view_widget_to_tree_coords (handle, rect.x, rect.y, tx, ty);
1122 rect.y = ty[0];
1123 GdkRectangle visRect = new GdkRectangle ();
1124 OS.gtk_tree_view_get_visible_rect (handle, visRect);
1125 if (rect.y < visRect.y || rect.y + rect.height > visRect.y + visRect.height) {
1126 isHidden = true;
1127 }
1128 }
1129 if (isHidden) OS.gtk_tree_view_scroll_to_cell (handle, path, 0, depth != 1, 0.5f, 0.0f);
1130 }
1131}
1132
1133/**
1134 * Shows the item. If the item is already showing in the receiver,
1135 * this method simply returns. Otherwise, the items are scrolled
1136 * and expanded until the item is visible.
1137 *
1138 * @param item the item to be shown
1139 *
1140 * @exception IllegalArgumentException <ul>
1141 * <li>ERROR_NULL_ARGUMENT - if the item is null</li>
1142 * <li>ERROR_INVALID_ARGUMENT - if the item has been disposed</li>
1143 * </ul>
1144 * @exception SWTException <ul>
1145 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1146 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1147 * </ul>
1148 *
1149 * @see Tree#showSelection()
1150 */
1151public void showItem (TreeItem item) {
1152 checkWidget ();
1153 if (item == null) error (SWT.ERROR_NULL_ARGUMENT);
1154 if (item.isDisposed ()) error(SWT.ERROR_INVALID_ARGUMENT);
1155 if (item.parent != this) return;
1156 long /*int*/ path = OS.gtk_tree_model_get_path (modelHandle, item.handle);
1157 showItem (path, true);
1158 OS.gtk_tree_path_free (path);
1159}
1160
1161long /*int*/ treeSelectionProc (long /*int*/ model, long /*int*/ path, long /*int*/ iter, int[] selection, int length) {
1162 if (selection != null) {
1163 int [] index = new int [1];
1164 OS.gtk_tree_model_get (modelHandle, iter, ID_COLUMN, index, -1);
1165 selection [(int)/*64*/length] = index [0];
1166 }
1167 return 0;
1168}
1169
1170}