1 /*******************************************************************************
2  * Copyright (c) 2000, 2008 IBM Corporation and others.
3  * All rights reserved. This program and the accompanying materials
4  * are made available under the terms of the Eclipse Public License v1.0
5  * which accompanies this distribution, and is available at
6  * http://www.eclipse.org/legal/epl-v10.html
7  *
8  * Contributors:
9  *     IBM Corporation - initial API and implementation
10  * Port to the D programming language:
11  *     Frank Benoit <benoit@tionex.de>
12  *******************************************************************************/
13 module org.eclipse.swt.widgets.Tree;
14 
15 
16 
17 import org.eclipse.swt.SWT;
18 import org.eclipse.swt.SWTException;
19 import org.eclipse.swt.events.SelectionEvent;
20 import org.eclipse.swt.events.SelectionListener;
21 import org.eclipse.swt.events.TreeListener;
22 import org.eclipse.swt.graphics.Color;
23 import org.eclipse.swt.graphics.Font;
24 import org.eclipse.swt.graphics.GC;
25 import org.eclipse.swt.graphics.Image;
26 import org.eclipse.swt.graphics.Point;
27 import org.eclipse.swt.graphics.Rectangle;
28 import org.eclipse.swt.internal.gtk.OS;
29 import org.eclipse.swt.widgets.TreeItem;
30 import org.eclipse.swt.widgets.TreeColumn;
31 import org.eclipse.swt.widgets.Composite;
32 import org.eclipse.swt.widgets.ImageList;
33 import org.eclipse.swt.widgets.Listener;
34 import org.eclipse.swt.widgets.Shell;
35 import org.eclipse.swt.widgets.Decorations;
36 import org.eclipse.swt.widgets.Menu;
37 import org.eclipse.swt.widgets.ScrollBar;
38 import org.eclipse.swt.widgets.Control;
39 import org.eclipse.swt.widgets.Display;
40 import org.eclipse.swt.widgets.Event;
41 import org.eclipse.swt.widgets.TypedListener;
42 import java.lang.all;
43 
44 /**
45  * Instances of this class provide a selectable user interface object
46  * that displays a hierarchy of items and issues notification when an
47  * item in the hierarchy is selected.
48  * <p>
49  * The item children that may be added to instances of this class
50  * must be of type <code>TreeItem</code>.
51  * </p><p>
52  * Style <code>VIRTUAL</code> is used to create a <code>Tree</code> whose
53  * <code>TreeItem</code>s are to be populated by the client on an on-demand basis
54  * instead of up-front.  This can provide significant performance improvements for
55  * trees that are very large or for which <code>TreeItem</code> population is
56  * expensive (for example, retrieving values from an external source).
57  * </p><p>
58  * Here is an example of using a <code>Tree</code> with style <code>VIRTUAL</code>:
59  * <code><pre>
60  *  final Tree tree = new Tree(parent, SWT.VIRTUAL | SWT.BORDER);
61  *  tree.setItemCount(20);
62  *  tree.addListener(SWT.SetData, new Listener() {
63  *      public void handleEvent(Event event) {
64  *          TreeItem item = (TreeItem)event.item;
65  *          TreeItem parentItem = item.getParentItem();
66  *          String text = null;
67  *          if (parentItem is null) {
68  *              text = "node " + tree.indexOf(item);
69  *          } else {
70  *              text = parentItem.getText() + " - " + parentItem.indexOf(item);
71  *          }
72  *          item.setText(text);
73  *          System.out.println(text);
74  *          item.setItemCount(10);
75  *      }
76  *  });
77  * </pre></code>
78  * </p><p>
79  * Note that although this class is a subclass of <code>Composite</code>,
80  * it does not normally make sense to add <code>Control</code> children to
81  * it, or set a layout on it, unless implementing something like a cell
82  * editor.
83  * </p><p>
84  * <dl>
85  * <dt><b>Styles:</b></dt>
86  * <dd>SINGLE, MULTI, CHECK, FULL_SELECTION, VIRTUAL, NO_SCROLL</dd>
87  * <dt><b>Events:</b></dt>
88  * <dd>Selection, DefaultSelection, Collapse, Expand, SetData, MeasureItem, EraseItem, PaintItem</dd>
89  * </dl>
90  * </p><p>
91  * Note: Only one of the styles SINGLE and MULTI may be specified.
92  * </p><p>
93  * IMPORTANT: This class is <em>not</em> intended to be subclassed.
94  * </p>
95  *
96  * @see <a href="http://www.eclipse.org/swt/snippets/#tree">Tree, TreeItem, TreeColumn snippets</a>
97  * @see <a href="http://www.eclipse.org/swt/examples.php">SWT Example: ControlExample</a>
98  * @see <a href="http://www.eclipse.org/swt/">Sample code and further information</a>
99  */
100 public class Tree : Composite {
101 
102     alias Composite.computeSize computeSize;
103     alias Composite.createHandle createHandle;
104     alias Composite.dragDetect dragDetect;
105     alias Composite.mnemonicHit mnemonicHit;
106     alias Composite.mnemonicMatch mnemonicMatch;
107     alias Composite.setBackgroundColor setBackgroundColor;
108     alias Composite.setBounds setBounds;
109 
110     CallbackData treeSelectionCallbackData;
111     GtkTreeStore* modelHandle;
112     GtkCellRenderer* checkRenderer;
113     int columnCount, sortDirection;
114     GtkWidget* ignoreCell;
115     TreeItem[] items;
116     TreeColumn [] columns;
117     TreeColumn sortColumn;
118     TreeItem currentItem;
119     ImageList imageList, headerImageList;
120     bool firstCustomDraw;
121     bool modelChanged;
122     bool expandAll;
123     int drawState, drawFlags;
124     GdkColor* drawForeground;
125     bool ownerDraw, ignoreSize;
126 
127     static const int ID_COLUMN = 0;
128     static const int CHECKED_COLUMN = 1;
129     static const int GRAYED_COLUMN = 2;
130     static const int FOREGROUND_COLUMN = 3;
131     static const int BACKGROUND_COLUMN = 4;
132     static const int FONT_COLUMN = 5;
133     static const int FIRST_COLUMN = FONT_COLUMN + 1;
134     static const int CELL_PIXBUF = 0;
135     static const int CELL_TEXT = 1;
136     static const int CELL_FOREGROUND = 2;
137     static const int CELL_BACKGROUND = 3;
138     static const int CELL_FONT = 4;
139     static const int CELL_TYPES = CELL_FONT + 1;
140 
141 /**
142  * Constructs a new instance of this class given its parent
143  * and a style value describing its behavior and appearance.
144  * <p>
145  * The style value is either one of the style constants defined in
146  * class <code>SWT</code> which is applicable to instances of this
147  * class, or must be built by <em>bitwise OR</em>'ing together
148  * (that is, using the <code>int</code> "|" operator) two or more
149  * of those <code>SWT</code> style constants. The class description
150  * lists the style constants that are applicable to the class.
151  * Style bits are also inherited from superclasses.
152  * </p>
153  *
154  * @param parent a composite control which will be the parent of the new instance (cannot be null)
155  * @param style the style of control to construct
156  *
157  * @exception IllegalArgumentException <ul>
158  *    <li>ERROR_NULL_ARGUMENT - if the parent is null</li>
159  * </ul>
160  * @exception SWTException <ul>
161  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li>
162  *    <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li>
163  * </ul>
164  *
165  * @see SWT#SINGLE
166  * @see SWT#MULTI
167  * @see SWT#CHECK
168  * @see SWT#FULL_SELECTION
169  * @see SWT#VIRTUAL
170  * @see SWT#NO_SCROLL
171  * @see Widget#checkSubclass
172  * @see Widget#getStyle
173  */
174 public this (Composite parent, int style) {
175     super (parent, checkStyle (style));
176 }
177 
178 override void _addListener (int eventType, Listener listener) {
179     super._addListener (eventType, listener);
180     if (!ownerDraw) {
181         switch (eventType) {
182             case SWT.MeasureItem:
183             case SWT.EraseItem:
184             case SWT.PaintItem:
185                 ownerDraw = true;
186                 recreateRenderers ();
187                 break;
188             default:
189         }
190     }
191 }
192 
193 TreeItem _getItem (GtkTreeIter* iter) {
194     int id = getId (iter, true);
195     if (items [id] !is null) return items [id];
196     auto path = OS.gtk_tree_model_get_path (modelHandle, iter);
197     int depth = OS.gtk_tree_path_get_depth (path);
198     int [] indices = new int [depth];
199     indices[] = OS.gtk_tree_path_get_indices (path)[ 0 .. depth ];
200     GtkTreeIter* parentIter;
201     GtkTreeIter parentIterInst;
202     if (depth > 1) {
203         OS.gtk_tree_path_up (path);
204         parentIter = &parentIterInst;
205         OS.gtk_tree_model_get_iter (modelHandle, parentIter, path);
206     }
207     items [id] = new TreeItem (this, parentIter, SWT.NONE, indices [indices.length -1], false);
208     OS.gtk_tree_path_free (path);
209     return items [id];
210 }
211 
212 TreeItem _getItem (GtkTreeIter* parentIter, int index) {
213     GtkTreeIter iter;
214     OS.gtk_tree_model_iter_nth_child(modelHandle, &iter, parentIter, index);
215     int id = getId (&iter, true);
216     if (items [id] !is null) return items [id];
217     return items [id] = new TreeItem (this, parentIter, SWT.NONE, index, false);
218 }
219 
220 int getId (GtkTreeIter* iter, bool queryModel) {
221     if (queryModel) {
222         int value;
223         OS.gtk_tree_model_get1 (modelHandle, iter, ID_COLUMN, cast(void**)&value);
224         if (value  !is -1) return value ;
225     }
226     // find next available id
227     int id = 0;
228     while (id < items.length && items [id] !is null) id++;
229     if (id is items.length) {
230         TreeItem [] newItems = new TreeItem [items.length + 4];
231         System.arraycopy (items, 0, newItems, 0, items.length);
232         items = newItems;
233     }
234     OS.gtk_tree_store_set1 (modelHandle, iter, ID_COLUMN, cast(void*)id );
235     return id;
236 }
237 
238 static int checkStyle (int style) {
239     /*
240     * Feature in Windows.  Even when WS_HSCROLL or
241     * WS_VSCROLL is not specified, Windows creates
242     * trees and tables with scroll bars.  The fix
243     * is to set H_SCROLL and V_SCROLL.
244     *
245     * NOTE: This code appears on all platforms so that
246     * applications have consistent scroll bar behavior.
247     */
248     if ((style & SWT.NO_SCROLL) is 0) {
249         style |= SWT.H_SCROLL | SWT.V_SCROLL;
250     }
251     /* GTK is always FULL_SELECTION */
252     style |= SWT.FULL_SELECTION;
253     return checkBits (style, SWT.SINGLE, SWT.MULTI, 0, 0, 0, 0);
254 }
255 
256 override void cellDataProc (
257     GtkTreeViewColumn *tree_column,
258     GtkCellRenderer *cell,
259     GtkTreeModel *tree_model,
260     GtkTreeIter *iter,
261     void* data)
262 {
263     if (cell is cast(GtkCellRenderer*)ignoreCell) return;
264     TreeItem item = _getItem (iter);
265     if (item !is null) OS.g_object_set_qdata (cast(GObject*)cell, Display.SWT_OBJECT_INDEX2, item.handle);
266     bool isPixbuf = OS.GTK_IS_CELL_RENDERER_PIXBUF (cell);
267     if (!(isPixbuf || OS.GTK_IS_CELL_RENDERER_TEXT (cell))) return;
268     int modelIndex = -1;
269     bool customDraw = false;
270     if (columnCount is 0) {
271         modelIndex = Tree.FIRST_COLUMN;
272         customDraw = firstCustomDraw;
273     } else {
274         TreeColumn column = cast(TreeColumn) display.getWidget (cast(GtkWidget*)tree_column);
275         if (column !is null) {
276             modelIndex = column.modelIndex;
277             customDraw = column.customDraw;
278         }
279     }
280     if (modelIndex is -1) return;
281     bool setData = false;
282     if ((style & SWT.VIRTUAL) !is 0) {
283         /*
284         * Feature in GTK.  On GTK before 2.4, fixed_height_mode is not
285         * supported, and the tree asks for the data of all items.  The
286         * fix is to only provide the data if the row is visible.
287         */
288         if (OS.GTK_VERSION < OS.buildVERSION (2, 3, 2)) {
289             auto path = OS.gtk_tree_model_get_path (tree_model, iter);
290             OS.gtk_widget_realize (handle);
291             GdkRectangle visible;
292             OS.gtk_tree_view_get_visible_rect (handle, &visible);
293             GdkRectangle area;
294             OS.gtk_tree_view_get_cell_area (handle, path, tree_column, &area);
295             OS.gtk_tree_path_free (path);
296             if (area.y + area.height < 0 || area.y + visible.y > visible.y + visible.height ) {
297                 /* Give an image from the image list to make sure the row has
298                 * the correct height.
299                 */
300                 if (imageList !is null && imageList.pixbufs.length > 0) {
301                     if (isPixbuf) OS.g_object_set1 (cell, OS.pixbuf.ptr, cast(ptrdiff_t)imageList.pixbufs [0]);
302                 }
303                 return;
304             }
305         }
306         if (!item.cached) {
307             //lastIndexOf = index [0];
308             setData = checkData (item);
309         }
310     }
311     void* ptr;
312     if (setData) {
313         if (isPixbuf) {
314             ptr = null;
315             OS.gtk_tree_model_get1 (tree_model, iter, modelIndex + CELL_PIXBUF, &ptr);
316             OS.g_object_set1 (cell, OS.pixbuf.ptr, cast(ptrdiff_t)ptr);
317         } else {
318             ptr = null;
319             OS.gtk_tree_model_get1 (tree_model, iter, modelIndex + CELL_TEXT, &ptr);
320             if (ptr !is null) {
321                 OS.g_object_set1 (cell, OS.text.ptr, cast(ptrdiff_t)ptr);
322                 OS.g_free (ptr);
323             }
324         }
325     }
326     if (customDraw) {
327         /*
328          * Bug on GTK. Gtk renders the background on top of the checkbox and pixbuf.
329          * This only happens in version 2.2.1 and earlier. The fix is not to set the background.
330          */
331         if (OS.GTK_VERSION > OS.buildVERSION (2, 2, 1)) {
332             if (!ownerDraw) {
333                 ptr = null;
334                 OS.gtk_tree_model_get1 (tree_model, iter, modelIndex + CELL_BACKGROUND, &ptr);
335                 if (ptr !is null) {
336                     OS.g_object_set1 (cell, OS.cell_background_gdk.ptr, cast(ptrdiff_t)ptr);
337                 }
338             }
339         }
340         if (!isPixbuf) {
341             ptr = null;
342             OS.gtk_tree_model_get1 (tree_model, iter, modelIndex + CELL_FOREGROUND, &ptr);
343             if (ptr !is null) {
344                 OS.g_object_set1 (cell, OS.foreground_gdk.ptr, cast(ptrdiff_t)ptr);
345             }
346             ptr = null;
347             OS.gtk_tree_model_get1 (tree_model, iter, modelIndex + CELL_FONT, &ptr);
348             if (ptr !is null) {
349                 OS.g_object_set1 (cell, OS.font_desc.ptr, cast(ptrdiff_t)ptr);
350             }
351         }
352     }
353     if (setData) {
354         ignoreCell = cast(GtkWidget*)cell;
355         setScrollWidth (tree_column, item);
356         ignoreCell = null;
357     }
358 }
359 
360 bool checkData (TreeItem item) {
361     if (item.cached) return true;
362     if ((style & SWT.VIRTUAL) !is 0) {
363         item.cached = true;
364         TreeItem parentItem = item.getParentItem ();
365         Event event = new Event ();
366         event.item = item;
367         event.index = parentItem is null ? indexOf (item) : parentItem.indexOf (item);
368         int mask = OS.G_SIGNAL_MATCH_DATA | OS.G_SIGNAL_MATCH_ID;
369         int signal_id = OS.g_signal_lookup (OS.row_changed.ptr, OS.gtk_tree_model_get_type ());
370         OS.g_signal_handlers_block_matched (modelHandle, mask, signal_id, 0, null, null, handle);
371         currentItem = item;
372         sendEvent (SWT.SetData, event);
373         currentItem = null;
374         //widget could be disposed at this point
375         if (isDisposed ()) return false;
376         OS.g_signal_handlers_unblock_matched (modelHandle, mask, signal_id, 0, null, null, handle);
377         if (item.isDisposed ()) return false;
378     }
379     return true;
380 }
381 
382 protected override void checkSubclass () {
383     if (!isValidSubclass ()) error (SWT.ERROR_INVALID_SUBCLASS);
384 }
385 
386 /**
387  * Adds the listener to the collection of listeners who will
388  * be notified when the user changes the receiver's selection, by sending
389  * it one of the messages defined in the <code>SelectionListener</code>
390  * interface.
391  * <p>
392  * When <code>widgetSelected</code> is called, the item field of the event object is valid.
393  * If the receiver has the <code>SWT.CHECK</code> style and the check selection changes,
394  * the event object detail field contains the value <code>SWT.CHECK</code>.
395  * <code>widgetDefaultSelected</code> is typically called when an item is double-clicked.
396  * The item field of the event object is valid for default selection, but the detail field is not used.
397  * </p>
398  *
399  * @param listener the listener which should be notified when the user changes the receiver's selection
400  *
401  * @exception IllegalArgumentException <ul>
402  *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
403  * </ul>
404  * @exception SWTException <ul>
405  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
406  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
407  * </ul>
408  *
409  * @see SelectionListener
410  * @see #removeSelectionListener
411  * @see SelectionEvent
412  */
413 public void addSelectionListener (SelectionListener listener) {
414     checkWidget ();
415     if (listener is null) error (SWT.ERROR_NULL_ARGUMENT);
416     TypedListener typedListener = new TypedListener (listener);
417     addListener (SWT.Selection, typedListener);
418     addListener (SWT.DefaultSelection, typedListener);
419 }
420 
421 /**
422  * Adds the listener to the collection of listeners who will
423  * be notified when an item in the receiver is expanded or collapsed
424  * by sending it one of the messages defined in the <code>TreeListener</code>
425  * interface.
426  *
427  * @param listener the listener which should be notified
428  *
429  * @exception IllegalArgumentException <ul>
430  *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
431  * </ul>
432  * @exception SWTException <ul>
433  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
434  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
435  * </ul>
436  *
437  * @see TreeListener
438  * @see #removeTreeListener
439  */
440 public void addTreeListener(TreeListener listener) {
441     checkWidget ();
442     if (listener is null) error (SWT.ERROR_NULL_ARGUMENT);
443     TypedListener typedListener = new TypedListener (listener);
444     addListener (SWT.Expand, typedListener);
445     addListener (SWT.Collapse, typedListener);
446 }
447 
448 int calculateWidth (GtkTreeViewColumn *column, GtkTreeIter* iter, bool recurse) {
449     OS.gtk_tree_view_column_cell_set_cell_data (column, modelHandle, iter, false, false);
450     /*
451     * Bug in GTK.  The width calculated by gtk_tree_view_column_cell_get_size()
452     * always grows in size regardless of the text or images in the table.
453     * The fix is to determine the column width from the cell renderers.
454     */
455     // Code intentionally commented
456     //int [] width = new int [1];
457     //OS.gtk_tree_view_column_cell_get_size (column, null, null, null, width, null);
458     //return width [0];
459 
460     int width = 0;
461     int w;
462     GtkTreePath* path = null;
463 
464     if (OS.gtk_tree_view_get_expander_column (handle) is column) {
465         /* indent */
466         GdkRectangle rect;
467         OS.gtk_widget_realize (handle);
468         path = OS.gtk_tree_model_get_path (modelHandle, iter);
469         OS.gtk_tree_view_get_cell_area (handle, path, column, &rect);
470         width += rect.x;
471         /* expander */
472         OS.gtk_widget_style_get1 (handle, OS.expander_size.ptr, &w);
473         width += w  + TreeItem.EXPANDER_EXTRA_PADDING;
474     }
475     OS.gtk_widget_style_get1(handle, OS.focus_line_width.ptr, &w);
476     width += 2 * w ;
477     auto list = OS.gtk_tree_view_column_get_cell_renderers (column);
478     if (list is null) return 0;
479     auto temp = list;
480     while (temp !is null) {
481         auto renderer = OS.g_list_data (temp);
482         if (renderer !is null) {
483             OS.gtk_cell_renderer_get_size (renderer, handle, null, null, null, &w, null);
484             width += w ;
485         }
486         temp = OS.g_list_next (temp);
487     }
488     OS.g_list_free (list);
489 
490     if (recurse) {
491         if (path is null) path = OS.gtk_tree_model_get_path (modelHandle, iter);
492         bool expanded = OS.gtk_tree_view_row_expanded (handle, path) !is 0;
493         if (expanded) {
494             GtkTreeIter childIter;
495             bool valid = OS.gtk_tree_model_iter_children (modelHandle, &childIter, iter) !is 0;
496             while (valid) {
497                 width = Math.max (width, calculateWidth (column, &childIter, true));
498                 valid = OS.gtk_tree_model_iter_next (modelHandle, &childIter) !is 0;
499             }
500         }
501     }
502 
503     if (path !is null) OS.gtk_tree_path_free (path);
504     return width;
505 }
506 
507 /**
508  * Clears the item at the given zero-relative index in the receiver.
509  * The text, icon and other attributes of the item are set to the default
510  * value.  If the tree was created with the <code>SWT.VIRTUAL</code> style,
511  * these attributes are requested again as needed.
512  *
513  * @param index the index of the item to clear
514  * @param all <code>true</code> if all child items of the indexed item should be
515  * cleared recursively, and <code>false</code> otherwise
516  *
517  * @exception IllegalArgumentException <ul>
518  *    <li>ERROR_INVALID_RANGE - if the index is not between 0 and the number of elements in the list minus 1 (inclusive)</li>
519  * </ul>
520  * @exception SWTException <ul>
521  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
522  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
523  * </ul>
524  *
525  * @see SWT#VIRTUAL
526  * @see SWT#SetData
527  *
528  * @since 3.2
529  */
530 public void clear(int index, bool all) {
531     checkWidget ();
532     clear (null, index, all);
533 }
534 
535 void clear (GtkTreeIter* parentIter, int index, bool all) {
536     GtkTreeIter iter;
537     OS.gtk_tree_model_iter_nth_child(modelHandle, &iter, parentIter, index);
538     int value;
539     OS.gtk_tree_model_get1 (modelHandle, &iter, ID_COLUMN, cast(void**)&value);
540     if (value  !is -1) {
541         TreeItem item = items [value ];
542         item.clear ();
543     }
544     if (all) clearAll (all, &iter);
545 }
546 
547 /**
548  * Clears all the items in the receiver. The text, icon and other
549  * attributes of the items are set to their default values. If the
550  * tree was created with the <code>SWT.VIRTUAL</code> style, these
551  * attributes are requested again as needed.
552  *
553  * @param all <code>true</code> if all child items should be cleared
554  * recursively, and <code>false</code> otherwise
555  *
556  * @exception SWTException <ul>
557  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
558  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
559  * </ul>
560  *
561  * @see SWT#VIRTUAL
562  * @see SWT#SetData
563  *
564  * @since 3.2
565  */
566 public void clearAll (bool all) {
567     checkWidget ();
568     clearAll (all, null);
569 }
570 void clearAll (bool all, GtkTreeIter* parentIter) {
571     int length = OS.gtk_tree_model_iter_n_children (modelHandle, parentIter);
572     if (length is 0) return;
573     GtkTreeIter iter;
574     bool valid = cast(bool)OS.gtk_tree_model_iter_children (modelHandle, &iter, parentIter);
575     int value;
576     while (valid) {
577         OS.gtk_tree_model_get1 (modelHandle, &iter, ID_COLUMN, cast(void**)&value);
578         if (value  !is -1) {
579             TreeItem item = items [value ];
580             item.clear ();
581         }
582         if (all) clearAll (all, &iter);
583         valid = cast(bool)OS.gtk_tree_model_iter_next (modelHandle, &iter);
584     }
585 }
586 
587 public override Point computeSize (int wHint, int hHint, bool changed) {
588     checkWidget ();
589     if (wHint !is SWT.DEFAULT && wHint < 0) wHint = 0;
590     if (hHint !is SWT.DEFAULT && hHint < 0) hHint = 0;
591     Point size = computeNativeSize (handle, wHint, hHint, changed);
592     Rectangle trim = computeTrim (0, 0, size.x, size.y);
593     size.x = trim.width;
594     size.y = trim.height;
595     return size;
596 }
597 
598 void copyModel (void* oldModel, int oldStart, void* newModel, int newStart, size_t [] types, void* oldParent, void* newParent, int modelLength) {
599     GtkTreeIter iter;
600     if (OS.gtk_tree_model_iter_children (oldModel, &iter, oldParent))  {
601         GtkTreeIter*[] oldItems = new GtkTreeIter*[]( OS.gtk_tree_model_iter_n_children (oldModel, oldParent));
602         int oldIndex = 0;
603         do {
604             GtkTreeIter* newItem = cast(GtkTreeIter*)OS.g_malloc (GtkTreeIter.sizeof);
605             if (newItem is null) error (SWT.ERROR_NO_HANDLES);
606             OS.gtk_tree_store_append (newModel, newItem, newParent);
607             int index;
608             OS.gtk_tree_model_get1 (oldModel, &iter, ID_COLUMN, cast(void**)&index);
609             TreeItem item = null;
610             if (index  !is -1) {
611                 item = items [index ];
612                 if (item !is null) {
613                     auto oldItem = cast(GtkTreeIter*)item.handle;
614                     oldItems[oldIndex++] = oldItem;
615                     void* ptr;
616                     for (int j = 0; j < FIRST_COLUMN; j++) {
617                         OS.gtk_tree_model_get1 (oldModel, oldItem, j, &ptr);
618                         OS.gtk_tree_store_set1 (newModel, newItem, j, ptr );
619                         if (types [j] is OS.G_TYPE_STRING ()) OS.g_free ((ptr ));
620                     }
621                     for (int j= 0; j<modelLength - FIRST_COLUMN; j++) {
622                         OS.gtk_tree_model_get1 (oldModel, oldItem, oldStart + j, &ptr);
623                         OS.gtk_tree_store_set1 (newModel, newItem, newStart + j, ptr );
624                         if (types [j] is OS.G_TYPE_STRING ()) OS.g_free ((ptr ));
625                     }
626                 }
627             } else {
628                 OS.gtk_tree_store_set1 (newModel, newItem, ID_COLUMN, cast(void*)-1 );
629             }
630             // recurse through children
631             copyModel(oldModel, oldStart, newModel, newStart, types, &iter, newItem, modelLength);
632 
633             if (item!is null) {
634                 item.handle = cast(GtkWidget*)newItem;
635             } else {
636                 OS.g_free (newItem);
637             }
638         } while (OS.gtk_tree_model_iter_next(oldModel, &iter));
639         for (int i = 0; i < oldItems.length; i++) {
640             auto oldItem = oldItems [i];
641             if (oldItem !is null) {
642                 OS.gtk_tree_store_remove (oldModel, oldItem);
643                 OS.g_free (oldItem);
644             }
645         }
646     }
647 }
648 
649 void createColumn (TreeColumn column, int index) {
650 /*
651 * Bug in ATK. For some reason, ATK segments fault if
652 * the GtkTreeView has a column and does not have items.
653 * The fix is to insert the column only when an item is
654 * created.
655 */
656 
657     int modelIndex = FIRST_COLUMN;
658     if (columnCount !is 0) {
659         int modelLength = OS.gtk_tree_model_get_n_columns (modelHandle);
660         bool [] usedColumns = new bool [modelLength];
661         for (int i=0; i<columnCount; i++) {
662             int columnIndex = columns [i].modelIndex;
663             for (int j = 0; j < CELL_TYPES; j++) {
664                 usedColumns [columnIndex + j] = true;
665             }
666         }
667         while (modelIndex < modelLength) {
668             if (!usedColumns [modelIndex]) break;
669             modelIndex++;
670         }
671         if (modelIndex is modelLength) {
672             auto oldModel = modelHandle;
673             size_t[] types = getColumnTypes (columnCount + 4); // grow by 4 rows at a time
674             auto newModel = OS.gtk_tree_store_newv (cast(int)/*64bit*/types.length, types.ptr);
675             if (newModel is null) error (SWT.ERROR_NO_HANDLES);
676             copyModel (oldModel, FIRST_COLUMN, newModel, FIRST_COLUMN, types, null, null, modelLength);
677             OS.gtk_tree_view_set_model (handle, newModel);
678             OS.g_object_unref (oldModel);
679             modelHandle = newModel;
680         }
681     }
682     auto columnHandle = OS.gtk_tree_view_column_new ();
683     if (columnHandle is null) error (SWT.ERROR_NO_HANDLES);
684     if (index is 0 && columnCount > 0) {
685         TreeColumn checkColumn = columns [0];
686         createRenderers (cast(GtkTreeViewColumn *)checkColumn.handle, checkColumn.modelIndex, false, checkColumn.style);
687     }
688     createRenderers (columnHandle, modelIndex, index is 0, column is null ? 0 : column.style);
689     /*
690     * Use GTK_TREE_VIEW_COLUMN_GROW_ONLY on GTK versions < 2.3.2
691     * because fixed_height_mode is not supported.
692     */
693     bool useVirtual = (style & SWT.VIRTUAL) !is 0 && OS.GTK_VERSION >= OS.buildVERSION (2, 3, 2);
694     if (!useVirtual && columnCount is 0) {
695         OS.gtk_tree_view_column_set_sizing (columnHandle, OS.GTK_TREE_VIEW_COLUMN_GROW_ONLY);
696     } else {
697         OS.gtk_tree_view_column_set_sizing (columnHandle, OS.GTK_TREE_VIEW_COLUMN_FIXED);
698         if (columnCount !is 0) OS.gtk_tree_view_column_set_visible (columnHandle, false);
699     }
700     OS.gtk_tree_view_column_set_resizable (columnHandle, true);
701     OS.gtk_tree_view_column_set_clickable (columnHandle, true);
702     OS.gtk_tree_view_column_set_min_width (columnHandle, 0);
703     OS.gtk_tree_view_insert_column (handle, columnHandle, index);
704     if (column !is null) {
705         column.handle = cast(GtkWidget*)columnHandle;
706         column.modelIndex = modelIndex;
707     }
708     /* Disable searching when using VIRTUAL */
709     if ((style & SWT.VIRTUAL) !is 0) {
710         /*
711         * Bug in GTK. Until GTK 2.6.5, calling gtk_tree_view_set_enable_search(FALSE)
712         * would prevent the user from being able to type in text to search the tree.
713         * After 2.6.5, GTK introduced Ctrl+F as being the key binding for interactive
714         * search. This meant that even if FALSE was passed to enable_search, the user
715         * can still bring up the search pop up using the keybinding. GTK also introduced
716         * the notion of passing a -1 to gtk_set_search_column to disable searching
717         * (including the search key binding).  The fix is to use the right calls
718         * for the right version.
719         */
720         if (OS.GTK_VERSION >= OS.buildVERSION (2, 6, 5)) {
721             OS.gtk_tree_view_set_search_column (handle, -1);
722         } else {
723             OS.gtk_tree_view_set_enable_search (handle, false);
724         }
725     } else {
726         /* Set the search column whenever the model changes */
727         int firstColumn = columnCount is 0 ? FIRST_COLUMN : columns [0].modelIndex;
728         OS.gtk_tree_view_set_search_column (handle, firstColumn + CELL_TEXT);
729     }
730 }
731 
732 override void createHandle (int index) {
733     state |= HANDLE;
734     fixedHandle = cast(GtkWidget*)OS.g_object_new (display.gtk_fixed_get_type (), null);
735     if (fixedHandle is null) error (SWT.ERROR_NO_HANDLES);
736     OS.gtk_fixed_set_has_window (fixedHandle, true);
737     scrolledHandle = cast(GtkWidget*)OS.gtk_scrolled_window_new (null, null);
738     if (scrolledHandle is null) error (SWT.ERROR_NO_HANDLES);
739     size_t[] types = getColumnTypes (1);
740     modelHandle = cast(GtkTreeStore*)OS.gtk_tree_store_newv (cast(int)/*64bit*/types.length, types.ptr);
741     if (modelHandle is null) error (SWT.ERROR_NO_HANDLES);
742     handle = cast(GtkWidget*)OS.gtk_tree_view_new_with_model (modelHandle);
743     if (handle is null) error (SWT.ERROR_NO_HANDLES);
744     if ((style & SWT.CHECK) !is 0) {
745         checkRenderer = cast(GtkCellRenderer*)OS.gtk_cell_renderer_toggle_new ();
746         if (checkRenderer is null) error (SWT.ERROR_NO_HANDLES);
747         OS.g_object_ref (checkRenderer);
748     }
749     createColumn (null, 0);
750     OS.gtk_container_add (fixedHandle, scrolledHandle);
751     OS.gtk_container_add (scrolledHandle, handle);
752 
753     int mode = (style & SWT.MULTI) !is 0 ? OS.GTK_SELECTION_MULTIPLE : OS.GTK_SELECTION_BROWSE;
754     auto selectionHandle = OS.gtk_tree_view_get_selection (handle);
755     OS.gtk_tree_selection_set_mode (selectionHandle, mode);
756     OS.gtk_tree_view_set_headers_visible (handle, false);
757     int hsp = (style & SWT.H_SCROLL) !is 0 ? OS.GTK_POLICY_AUTOMATIC : OS.GTK_POLICY_NEVER;
758     int vsp = (style & SWT.V_SCROLL) !is 0 ? OS.GTK_POLICY_AUTOMATIC : OS.GTK_POLICY_NEVER;
759     OS.gtk_scrolled_window_set_policy (scrolledHandle, hsp, vsp);
760     if ((style & SWT.BORDER) !is 0) OS.gtk_scrolled_window_set_shadow_type (scrolledHandle, OS.GTK_SHADOW_ETCHED_IN);
761     /* Disable searching when using VIRTUAL */
762     if ((style & SWT.VIRTUAL) !is 0) {
763         /* The fixed_height_mode property only exists in GTK 2.3.2 and greater */
764         if (OS.GTK_VERSION >= OS.buildVERSION (2, 3, 2)) {
765             OS.g_object_set1 (handle, OS.fixed_height_mode.ptr, true);
766         }
767         /*
768         * Bug in GTK. Until GTK 2.6.5, calling gtk_tree_view_set_enable_search(FALSE)
769         * would prevent the user from being able to type in text to search the tree.
770         * After 2.6.5, GTK introduced Ctrl+F as being the key binding for interactive
771         * search. This meant that even if FALSE was passed to enable_search, the user
772         * can still bring up the search pop up using the keybinding. GTK also introduced
773         * the notion of passing a -1 to gtk_set_search_column to disable searching
774         * (including the search key binding).  The fix is to use the right calls
775         * for the right version.
776         */
777         if (OS.GTK_VERSION >= OS.buildVERSION (2, 6, 5)) {
778             OS.gtk_tree_view_set_search_column (handle, -1);
779         } else {
780             OS.gtk_tree_view_set_enable_search (handle, false);
781         }
782     }
783 }
784 
785 void createItem (TreeColumn column, int index) {
786     if (!(0 <= index && index <= columnCount)) error (SWT.ERROR_INVALID_RANGE);
787     if (index is 0) {
788         // first column must be left aligned
789         column.style &= ~(SWT.LEFT | SWT.RIGHT | SWT.CENTER);
790         column.style |= SWT.LEFT;
791     }
792     if (columnCount is 0) {
793         column.handle = cast(GtkWidget*)OS.gtk_tree_view_get_column (handle, 0);
794         OS.gtk_tree_view_column_set_sizing (column.handle, OS.GTK_TREE_VIEW_COLUMN_FIXED);
795         OS.gtk_tree_view_column_set_visible (column.handle, false);
796         column.modelIndex = FIRST_COLUMN;
797         createRenderers (cast(GtkTreeViewColumn *)column.handle, column.modelIndex, true, column.style);
798         column.customDraw = firstCustomDraw;
799         firstCustomDraw = false;
800     } else {
801         createColumn (column, index);
802     }
803     auto boxHandle = OS.gtk_hbox_new (false, 3);
804     if (boxHandle is null) error (SWT.ERROR_NO_HANDLES);
805     auto labelHandle = OS.gtk_label_new_with_mnemonic (null);
806     if (labelHandle is null) error (SWT.ERROR_NO_HANDLES);
807     auto imageHandle = OS.gtk_image_new ();
808     if (imageHandle is null) error (SWT.ERROR_NO_HANDLES);
809     OS.gtk_container_add (boxHandle, imageHandle);
810     OS.gtk_container_add (boxHandle, labelHandle);
811     OS.gtk_widget_show (boxHandle);
812     OS.gtk_widget_show (labelHandle);
813     column.labelHandle = labelHandle;
814     column.imageHandle = imageHandle;
815     OS.gtk_tree_view_column_set_widget (column.handle, boxHandle);
816     auto widget = OS.gtk_widget_get_parent (boxHandle);
817     while (widget !is handle) {
818         if (OS.GTK_IS_BUTTON (widget)) {
819             column.buttonHandle = widget;
820             break;
821         }
822         widget = OS.gtk_widget_get_parent (widget);
823     }
824     if (columnCount is columns.length) {
825         TreeColumn [] newColumns = new TreeColumn [columns.length + 4];
826         System.arraycopy (columns, 0, newColumns, 0, columns.length);
827         columns = newColumns;
828     }
829     System.arraycopy (columns, index, columns, index + 1, columnCount++ - index);
830     columns [index] = column;
831     if ((state & FONT) !is 0) {
832         column.setFontDescription (getFontDescription ());
833     }
834     if (columnCount >= 1) {
835         for (int i=0; i<items.length; i++) {
836             TreeItem item = items [i];
837             if (item !is null) {
838                 Font [] cellFont = item.cellFont;
839                 if (cellFont !is null) {
840                     Font [] temp = new Font [columnCount];
841                     System.arraycopy (cellFont, 0, temp, 0, index);
842                     System.arraycopy (cellFont, index, temp, index+1, columnCount-index-1);
843                     item.cellFont = temp;
844                 }
845             }
846         }
847     }
848 }
849 
850 void createItem (TreeItem item, GtkTreeIter* parentIter, int index) {
851     int count = OS.gtk_tree_model_iter_n_children (modelHandle, parentIter);
852     if (index is -1) index = count;
853     if (!(0 <= index && index <= count)) error (SWT.ERROR_INVALID_RANGE);
854     item.handle = cast(GtkWidget*)OS.g_malloc (GtkTreeIter.sizeof);
855     if (item.handle is null) error(SWT.ERROR_NO_HANDLES);
856     /*
857     * Feature in GTK.  It is much faster to append to a tree store
858     * than to insert at the end using gtk_tree_store_insert().
859     */
860     if (index is count) {
861         OS.gtk_tree_store_append (modelHandle, item.handle, parentIter);
862     } else {
863         OS.gtk_tree_store_insert (modelHandle, item.handle, parentIter, index);
864     }
865     int id = getId (cast(GtkTreeIter*) item.handle, false);
866     items [id] = item;
867     modelChanged = true;
868 }
869 
870 void createRenderers (GtkTreeViewColumn* columnHandle, int modelIndex, bool check, int columnStyle) {
871     OS.gtk_tree_view_column_clear (columnHandle);
872     if ((style & SWT.CHECK) !is 0 && check) {
873         OS.gtk_tree_view_column_pack_start (columnHandle, checkRenderer, false);
874         OS.gtk_tree_view_column_add_attribute (columnHandle, checkRenderer, OS.active.ptr, CHECKED_COLUMN);
875         /*
876         * Feature in GTK. The inconsistent property only exists in GTK 2.2.x.
877         */
878         if (OS.GTK_VERSION >= OS.buildVERSION (2, 2, 0)) {
879             OS.gtk_tree_view_column_add_attribute (columnHandle, checkRenderer, OS.inconsistent.ptr, GRAYED_COLUMN);
880         }
881         /*
882         * Bug in GTK. GTK renders the background on top of the checkbox.
883         * This only happens in version 2.2.1 and earlier. The fix is not to set the background.
884         */
885         if (OS.GTK_VERSION > OS.buildVERSION (2, 2, 1)) {
886             if (!ownerDraw) OS.gtk_tree_view_column_add_attribute (columnHandle, checkRenderer, OS.cell_background_gdk.ptr, BACKGROUND_COLUMN);
887         }
888         if (ownerDraw) {
889             display.doCellDataProc( handle, cast(GtkTreeViewColumn*)columnHandle, cast(GtkCellRenderer*)checkRenderer );
890             OS.g_object_set_qdata (cast(GObject*)checkRenderer, Display.SWT_OBJECT_INDEX1, columnHandle);
891         }
892     }
893     auto pixbufRenderer = ownerDraw ? cast(GtkCellRenderer*)OS.g_object_new (display.gtk_cell_renderer_pixbuf_get_type (), null) : OS.gtk_cell_renderer_pixbuf_new ();
894     if (pixbufRenderer is null) error (SWT.ERROR_NO_HANDLES);
895     auto textRenderer = ownerDraw ? cast(GtkCellRenderer*)OS.g_object_new (display.gtk_cell_renderer_text_get_type (), null) : OS.gtk_cell_renderer_text_new ();
896     if (textRenderer is null) error (SWT.ERROR_NO_HANDLES);
897 
898     if (ownerDraw) {
899         OS.g_object_set_qdata (cast(GObject*)pixbufRenderer, Display.SWT_OBJECT_INDEX1, columnHandle);
900         OS.g_object_set_qdata (cast(GObject*)textRenderer, Display.SWT_OBJECT_INDEX1, columnHandle);
901     }
902 
903     /*
904     * Feature in GTK.  When a tree view column contains only one activatable
905     * cell renderer such as a toggle renderer, mouse clicks anywhere in a cell
906     * activate that renderer. The workaround is to set a second  cell renderer
907     * to be activatable.
908     */
909     if ((style & SWT.CHECK) !is 0 && check) {
910         OS.g_object_set1 (pixbufRenderer, OS.mode.ptr, OS.GTK_CELL_RENDERER_MODE_ACTIVATABLE);
911     }
912 
913     /* Set alignment */
914     if ((columnStyle & SWT.RIGHT) !is 0) {
915         OS.g_object_set1_float(textRenderer, OS.xalign.ptr, 1.0f);
916         OS.gtk_tree_view_column_pack_end (columnHandle, textRenderer, true);
917         OS.gtk_tree_view_column_pack_end (columnHandle, pixbufRenderer, false);
918         OS.gtk_tree_view_column_set_alignment (columnHandle, 1f);
919     } else if ((columnStyle & SWT.CENTER) !is 0) {
920         OS.g_object_set1_float(textRenderer, OS.xalign.ptr, 0.5f);
921         OS.gtk_tree_view_column_pack_start (columnHandle, pixbufRenderer, false);
922         OS.gtk_tree_view_column_pack_end (columnHandle, textRenderer, true);
923         OS.gtk_tree_view_column_set_alignment (columnHandle, 0.5f);
924     } else {
925         OS.gtk_tree_view_column_pack_start (columnHandle, pixbufRenderer, false);
926         OS.gtk_tree_view_column_pack_start (columnHandle, textRenderer, true);
927         OS.gtk_tree_view_column_set_alignment (columnHandle, 0f);
928     }
929 
930     /* Add attributes */
931     OS.gtk_tree_view_column_add_attribute (columnHandle, pixbufRenderer, OS.pixbuf.ptr, modelIndex + CELL_PIXBUF);
932     /*
933      * Bug on GTK. Gtk renders the background on top of the pixbuf.
934      * This only happens in version 2.2.1 and earlier. The fix is not to set the background.
935      */
936     if (OS.GTK_VERSION > OS.buildVERSION (2, 2, 1)) {
937         if (!ownerDraw) {
938             OS.gtk_tree_view_column_add_attribute (columnHandle, pixbufRenderer, OS.cell_background_gdk.ptr, BACKGROUND_COLUMN);
939             OS.gtk_tree_view_column_add_attribute (columnHandle, textRenderer, OS.cell_background_gdk.ptr, BACKGROUND_COLUMN);
940         }
941     }
942     OS.gtk_tree_view_column_add_attribute (columnHandle, textRenderer, OS.text.ptr, modelIndex + CELL_TEXT);
943     OS.gtk_tree_view_column_add_attribute (columnHandle, textRenderer, OS.foreground_gdk.ptr, FOREGROUND_COLUMN);
944     OS.gtk_tree_view_column_add_attribute (columnHandle, textRenderer, OS.font_desc.ptr, FONT_COLUMN);
945 
946     bool customDraw = firstCustomDraw;
947     if (columnCount !is 0) {
948         for (int i=0; i<columnCount; i++) {
949             if (columns [i].handle is cast(GtkWidget*)columnHandle) {
950                 customDraw = columns [i].customDraw;
951                 break;
952             }
953         }
954     }
955     if ((style & SWT.VIRTUAL) !is 0 || customDraw || ownerDraw) {
956         display.doCellDataProc( handle, cast(GtkTreeViewColumn*)columnHandle, cast(GtkCellRenderer*)textRenderer );
957         display.doCellDataProc( handle, cast(GtkTreeViewColumn*)columnHandle, cast(GtkCellRenderer*)pixbufRenderer );
958     }
959 }
960 
961 override
962 void createWidget (int index) {
963     super.createWidget (index);
964     items = new TreeItem [4];
965     columns = new TreeColumn [4];
966     columnCount = 0;
967 }
968 
969 GdkColor* defaultBackground () {
970     return display.COLOR_LIST_BACKGROUND;
971 }
972 
973 GdkColor* defaultForeground () {
974     return display.COLOR_LIST_FOREGROUND;
975 }
976 
977 override void deregister () {
978     super.deregister ();
979     display.removeWidget (cast(GtkWidget*)OS.gtk_tree_view_get_selection (handle));
980     if (checkRenderer !is null) display.removeWidget (cast(GtkWidget*)checkRenderer);
981 }
982 
983 /**
984  * Deselects an item in the receiver.  If the item was already
985  * deselected, it remains deselected.
986  *
987  * @param item the item to be deselected
988  *
989  * @exception IllegalArgumentException <ul>
990  *    <li>ERROR_NULL_ARGUMENT - if the item is null</li>
991  *    <li>ERROR_INVALID_ARGUMENT - if the item has been disposed</li>
992  * </ul>
993  * @exception SWTException <ul>
994  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
995  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
996  * </ul>
997  *
998  * @since 3.4
999  */
1000 public void deselect (TreeItem item) {
1001     checkWidget ();
1002     if (item is null) error (SWT.ERROR_NULL_ARGUMENT);
1003     if (item.isDisposed ()) error (SWT.ERROR_INVALID_ARGUMENT);
1004     bool fixColumn = showFirstColumn ();
1005     auto selection = OS.gtk_tree_view_get_selection (handle);
1006     OS.g_signal_handlers_block_matched (selection, OS.G_SIGNAL_MATCH_DATA, 0, 0, null, null, udCHANGED);
1007     OS.gtk_tree_selection_unselect_iter (selection, item.handle);
1008     OS.g_signal_handlers_unblock_matched (selection, OS.G_SIGNAL_MATCH_DATA, 0, 0, null, null, udCHANGED);
1009     if (fixColumn) hideFirstColumn ();
1010 }
1011 
1012 /**
1013  * Deselects all selected items in the receiver.
1014  *
1015  * @exception SWTException <ul>
1016  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1017  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1018  * </ul>
1019  */
1020 public void deselectAll() {
1021     checkWidget();
1022     bool fixColumn = showFirstColumn ();
1023     auto selection = OS.gtk_tree_view_get_selection (handle);
1024     OS.g_signal_handlers_block_matched (selection, OS.G_SIGNAL_MATCH_DATA, 0, 0, null, null, udCHANGED);
1025     OS.gtk_tree_selection_unselect_all (selection);
1026     OS.g_signal_handlers_unblock_matched (selection, OS.G_SIGNAL_MATCH_DATA, 0, 0, null, null, udCHANGED);
1027     if (fixColumn) hideFirstColumn ();
1028 }
1029 
1030 void destroyItem (TreeColumn column) {
1031     int index = 0;
1032     while (index < columnCount) {
1033         if (columns [index] is column) break;
1034         index++;
1035     }
1036     if (index is columnCount) return;
1037     auto columnHandle = column.handle;
1038     if (columnCount is 1) {
1039         firstCustomDraw = column.customDraw;
1040     }
1041     System.arraycopy (columns, index + 1, columns, index, --columnCount - index);
1042     columns [columnCount] = null;
1043     OS.gtk_tree_view_remove_column (handle, columnHandle);
1044     if (columnCount is 0) {
1045         auto oldModel = modelHandle;
1046         size_t[] types = getColumnTypes (1);
1047         auto newModel = OS.gtk_tree_store_newv (cast(int)/*64bit*/types.length, types.ptr);
1048         if (newModel is null) error (SWT.ERROR_NO_HANDLES);
1049         copyModel(oldModel, column.modelIndex, newModel, FIRST_COLUMN, types, null, null, FIRST_COLUMN + CELL_TYPES);
1050         OS.gtk_tree_view_set_model (handle, newModel);
1051         OS.g_object_unref (oldModel);
1052         modelHandle = newModel;
1053         createColumn (null, 0);
1054 
1055     } else {
1056         for (int i=0; i<items.length; i++) {
1057             TreeItem item = items [i];
1058             if (item !is null) {
1059                 auto iter = cast(GtkTreeIter*)item.handle;
1060                 int modelIndex = column.modelIndex;
1061                 OS.gtk_tree_store_set1 (modelHandle, iter, modelIndex + CELL_PIXBUF, null);
1062                 OS.gtk_tree_store_set1 (modelHandle, iter, modelIndex + CELL_TEXT, null);
1063                 OS.gtk_tree_store_set1 (modelHandle, iter, modelIndex + CELL_FOREGROUND, null);
1064                 OS.gtk_tree_store_set1 (modelHandle, iter, modelIndex + CELL_BACKGROUND, null);
1065                 OS.gtk_tree_store_set1 (modelHandle, iter, modelIndex + CELL_FONT, null);
1066 
1067                 Font [] cellFont = item.cellFont;
1068                 if (cellFont !is null) {
1069                     if (columnCount is 0) {
1070                         item.cellFont = null;
1071                     } else {
1072                         Font [] temp = new Font [columnCount];
1073                         System.arraycopy (cellFont, 0, temp, 0, index);
1074                         System.arraycopy (cellFont, index + 1, temp, index, columnCount - index);
1075                         item.cellFont = temp;
1076                     }
1077                 }
1078             }
1079         }
1080         if (index is 0) {
1081             // first column must be left aligned and must show check box
1082             TreeColumn firstColumn = columns [0];
1083             firstColumn.style &= ~(SWT.LEFT | SWT.RIGHT | SWT.CENTER);
1084             firstColumn.style |= SWT.LEFT;
1085             createRenderers (cast(GtkTreeViewColumn*)firstColumn.handle, firstColumn.modelIndex, true, firstColumn.style);
1086         }
1087     }
1088     /* Disable searching when using VIRTUAL */
1089     if ((style & SWT.VIRTUAL) !is 0) {
1090         /*
1091         * Bug in GTK. Until GTK 2.6.5, calling gtk_tree_view_set_enable_search(FALSE)
1092         * would prevent the user from being able to type in text to search the tree.
1093         * After 2.6.5, GTK introduced Ctrl+F as being the key binding for interactive
1094         * search. This meant that even if FALSE was passed to enable_search, the user
1095         * can still bring up the search pop up using the keybinding. GTK also introduced
1096         * the notion of passing a -1 to gtk_set_search_column to disable searching
1097         * (including the search key binding).  The fix is to use the right calls
1098         * for the right version.
1099         */
1100         if (OS.GTK_VERSION >= OS.buildVERSION (2, 6, 5)) {
1101             OS.gtk_tree_view_set_search_column (handle, -1);
1102         } else {
1103             OS.gtk_tree_view_set_enable_search (handle, false);
1104         }
1105     } else {
1106         /* Set the search column whenever the model changes */
1107         int firstColumn = columnCount is 0 ? FIRST_COLUMN : columns [0].modelIndex;
1108         OS.gtk_tree_view_set_search_column (handle, firstColumn + CELL_TEXT);
1109     }
1110 }
1111 
1112 
1113 void destroyItem (TreeItem item) {
1114     /*
1115     * Bug in GTK.  GTK segment faults when a root tree item
1116     * is destroyed when the tree is expanded and the last leaf of
1117     * the root is selected.  This only happens in versions earlier
1118     * than 2.0.6.  The fix is to collapse the tree item being destroyed
1119     * when it is a root, before it is destroyed.
1120     */
1121     if (OS.GTK_VERSION < OS.buildVERSION (2, 0, 6)) {
1122         int length = OS.gtk_tree_model_iter_n_children (modelHandle, null);
1123         if (length > 0) {
1124             GtkTreeIter iter;
1125             bool valid = cast(bool)OS.gtk_tree_model_iter_children (modelHandle, &iter, null);
1126             while (valid) {
1127                 //PORTING_TODO: is this condition reasonable?
1128                 if (item.handle is cast(GtkWidget*)&iter) {
1129                     item.setExpanded (false);
1130                     break;
1131                 }
1132                 valid = cast(bool)OS.gtk_tree_model_iter_next (modelHandle, &iter);
1133             }
1134         }
1135     }
1136     auto selection = OS.gtk_tree_view_get_selection (handle);
1137     OS.g_signal_handlers_block_matched (selection, OS.G_SIGNAL_MATCH_DATA, 0, 0, null, null, udCHANGED);
1138     OS.gtk_tree_store_remove (modelHandle, item.handle);
1139     OS.g_signal_handlers_unblock_matched (selection, OS.G_SIGNAL_MATCH_DATA, 0, 0, null, null, udCHANGED);
1140     modelChanged = true;
1141 }
1142 
1143 override bool dragDetect (int x, int y, bool filter, bool * consume) {
1144     bool selected = false;
1145     if (filter) {
1146         void* path;
1147         if (OS.gtk_tree_view_get_path_at_pos (handle, x, y, &path, null, null, null)) {
1148             if (path  !is null) {
1149                 auto selection = OS.gtk_tree_view_get_selection (handle);
1150                 if (OS.gtk_tree_selection_path_is_selected (selection, path )) selected = true;
1151                 OS.gtk_tree_path_free (path );
1152             }
1153         } else {
1154             return false;
1155         }
1156     }
1157     bool dragDetect = super.dragDetect (x, y, filter, consume);
1158     if (dragDetect && selected && consume !is null) consume [0] = true;
1159     return dragDetect;
1160 }
1161 
1162 override GdkDrawable* eventWindow () {
1163     return paintWindow ();
1164 }
1165 
1166 override void fixChildren (Shell newShell, Shell oldShell, Decorations newDecorations, Decorations oldDecorations, Menu [] menus) {
1167     super.fixChildren (newShell, oldShell, newDecorations, oldDecorations, menus);
1168     for (int i=0; i<columnCount; i++) {
1169         TreeColumn column = columns [i];
1170         if (column.toolTipText !is null) {
1171             column.setToolTipText(oldShell, null);
1172             column.setToolTipText(newShell, column.toolTipText);
1173         }
1174     }
1175 }
1176 
1177 override GdkColor* getBackgroundColor () {
1178     return getBaseColor ();
1179 }
1180 
1181 public override Rectangle getClientArea () {
1182     checkWidget ();
1183     forceResize ();
1184     OS.gtk_widget_realize (handle);
1185     auto fixedWindow = OS.GTK_WIDGET_WINDOW (fixedHandle);
1186     auto binWindow = OS.gtk_tree_view_get_bin_window (handle);
1187     int binX, binY;
1188     OS.gdk_window_get_origin (binWindow, &binX, &binY);
1189     int fixedX, fixedY;
1190     OS.gdk_window_get_origin (fixedWindow, &fixedX, &fixedY);
1191     auto clientHandle = clientHandle ();
1192     int width = (state & ZERO_WIDTH) !is 0 ? 0 : OS.GTK_WIDGET_WIDTH (clientHandle);
1193     int height = (state & ZERO_HEIGHT) !is 0 ? 0 : OS.GTK_WIDGET_HEIGHT (clientHandle);
1194     return new Rectangle (fixedX  - binX , fixedY  - binY , width, height);
1195 }
1196 
1197 override
1198 int getClientWidth () {
1199     int w, h;
1200     OS.gtk_widget_realize (handle);
1201     OS.gdk_drawable_get_size(OS.gtk_tree_view_get_bin_window(handle), &w, &h);
1202     return w;
1203 }
1204 
1205 /**
1206  * Returns the column at the given, zero-relative index in the
1207  * receiver. Throws an exception if the index is out of range.
1208  * Columns are returned in the order that they were created.
1209  * If no <code>TreeColumn</code>s were created by the programmer,
1210  * this method will throw <code>ERROR_INVALID_RANGE</code> despite
1211  * the fact that a single column of data may be visible in the tree.
1212  * This occurs when the programmer uses the tree like a list, adding
1213  * items but never creating a column.
1214  *
1215  * @param index the index of the column to return
1216  * @return the column at the given index
1217  *
1218  * @exception IllegalArgumentException <ul>
1219  *    <li>ERROR_INVALID_RANGE - if the index is not between 0 and the number of elements in the list minus 1 (inclusive)</li>
1220  * </ul>
1221  * @exception SWTException <ul>
1222  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1223  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1224  * </ul>
1225  *
1226  * @see Tree#getColumnOrder()
1227  * @see Tree#setColumnOrder(int[])
1228  * @see TreeColumn#getMoveable()
1229  * @see TreeColumn#setMoveable(bool)
1230  * @see SWT#Move
1231  *
1232  * @since 3.1
1233  */
1234 public TreeColumn getColumn (int index) {
1235     checkWidget();
1236     if (!(0 <= index && index < columnCount)) error (SWT.ERROR_INVALID_RANGE);
1237     return columns [index];
1238 }
1239 
1240 /**
1241  * Returns the number of columns contained in the receiver.
1242  * If no <code>TreeColumn</code>s were created by the programmer,
1243  * this value is zero, despite the fact that visually, one column
1244  * of items may be visible. This occurs when the programmer uses
1245  * the tree like a list, adding items but never creating a column.
1246  *
1247  * @return the number of columns
1248  *
1249  * @exception SWTException <ul>
1250  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1251  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1252  * </ul>
1253  *
1254  * @since 3.1
1255  */
1256 public int getColumnCount () {
1257     checkWidget();
1258     return columnCount;
1259 }
1260 
1261 /**
1262  * Returns an array of zero-relative integers that map
1263  * the creation order of the receiver's items to the
1264  * order in which they are currently being displayed.
1265  * <p>
1266  * Specifically, the indices of the returned array represent
1267  * the current visual order of the items, and the contents
1268  * of the array represent the creation order of the items.
1269  * </p><p>
1270  * Note: This is not the actual structure used by the receiver
1271  * to maintain its list of items, so modifying the array will
1272  * not affect the receiver.
1273  * </p>
1274  *
1275  * @return the current visual order of the receiver's items
1276  *
1277  * @exception SWTException <ul>
1278  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1279  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1280  * </ul>
1281  *
1282  * @see Tree#setColumnOrder(int[])
1283  * @see TreeColumn#getMoveable()
1284  * @see TreeColumn#setMoveable(bool)
1285  * @see SWT#Move
1286  *
1287  * @since 3.2
1288  */
1289 public int [] getColumnOrder () {
1290     checkWidget ();
1291     if (columnCount is 0) return null;
1292     auto list = OS.gtk_tree_view_get_columns (handle);
1293     if (list is null) return null;
1294     int  i = 0, count = OS.g_list_length (list);
1295     int [] order = new int [count];
1296     auto temp = list;
1297     while (temp !is null) {
1298         auto column = OS.g_list_data (temp);
1299         if (column !is null) {
1300             for (int j=0; j<columnCount; j++) {
1301                 if (columns [j].handle is column) {
1302                     order [i++] = j;
1303                     break;
1304                 }
1305             }
1306         }
1307         temp = OS.g_list_next (temp);
1308     }
1309     OS.g_list_free (list);
1310     return order;
1311 }
1312 
1313 size_t[] getColumnTypes (int columnCount) {
1314     size_t[] types = new size_t [FIRST_COLUMN + (columnCount * CELL_TYPES)];
1315     // per row data
1316     types [ID_COLUMN] = OS.G_TYPE_INT ();
1317     types [CHECKED_COLUMN] = OS.G_TYPE_BOOLEAN ();
1318     types [GRAYED_COLUMN] = OS.G_TYPE_BOOLEAN ();
1319     types [FOREGROUND_COLUMN] = OS.GDK_TYPE_COLOR ();
1320     types [BACKGROUND_COLUMN] = OS.GDK_TYPE_COLOR ();
1321     types [FONT_COLUMN] = OS.PANGO_TYPE_FONT_DESCRIPTION ();
1322     // per cell data
1323     for (int i=FIRST_COLUMN; i<types.length; i+=CELL_TYPES) {
1324         types [i + CELL_PIXBUF] = OS.GDK_TYPE_PIXBUF ();
1325         types [i + CELL_TEXT] = OS.G_TYPE_STRING ();
1326         types [i + CELL_FOREGROUND] = OS.GDK_TYPE_COLOR ();
1327         types [i + CELL_BACKGROUND] = OS.GDK_TYPE_COLOR ();
1328         types [i + CELL_FONT] = OS.PANGO_TYPE_FONT_DESCRIPTION ();
1329     }
1330     return types;
1331 }
1332 
1333 /**
1334  * Returns an array of <code>TreeColumn</code>s which are the
1335  * columns in the receiver. Columns are returned in the order
1336  * that they were created.  If no <code>TreeColumn</code>s were
1337  * created by the programmer, the array is empty, despite the fact
1338  * that visually, one column of items may be visible. This occurs
1339  * when the programmer uses the tree like a list, adding items but
1340  * never creating a column.
1341  * <p>
1342  * Note: This is not the actual structure used by the receiver
1343  * to maintain its list of items, so modifying the array will
1344  * not affect the receiver.
1345  * </p>
1346  *
1347  * @return the items in the receiver
1348  *
1349  * @exception SWTException <ul>
1350  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1351  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1352  * </ul>
1353  *
1354  * @see Tree#getColumnOrder()
1355  * @see Tree#setColumnOrder(int[])
1356  * @see TreeColumn#getMoveable()
1357  * @see TreeColumn#setMoveable(bool)
1358  * @see SWT#Move
1359  *
1360  * @since 3.1
1361  */
1362 public TreeColumn [] getColumns () {
1363     checkWidget();
1364     TreeColumn [] result = new TreeColumn [columnCount];
1365     System.arraycopy (columns, 0, result, 0, columnCount);
1366     return result;
1367 }
1368 
1369 TreeItem getFocusItem () {
1370     GtkTreePath* path;
1371     OS.gtk_tree_view_get_cursor (handle, &path, null);
1372     if (path  is null) return null;
1373     TreeItem item = null;
1374     GtkTreeIter iter;
1375     if (OS.gtk_tree_model_get_iter (modelHandle, &iter, path)) {
1376         int index;
1377         OS.gtk_tree_model_get1 (modelHandle, &iter, ID_COLUMN, cast(void**)&index);
1378         if (index  !is -1) item = items [index ]; //TODO should we be creating this item when index is -1?
1379     }
1380     OS.gtk_tree_path_free (path );
1381     return item;
1382 }
1383 
1384 override GdkColor* getForegroundColor () {
1385     return getTextColor ();
1386 }
1387 
1388 /**
1389  * Returns the width in pixels of a grid line.
1390  *
1391  * @return the width of a grid line in pixels
1392  *
1393  * @exception SWTException <ul>
1394  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1395  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1396  * </ul>
1397  *
1398  * @since 3.1
1399  */
1400 public int getGridLineWidth () {
1401     checkWidget();
1402     return 0;
1403 }
1404 
1405 /**
1406  * Returns the height of the receiver's header
1407  *
1408  * @return the height of the header or zero if the header is not visible
1409  *
1410  * @exception SWTException <ul>
1411  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1412  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1413  * </ul>
1414  *
1415  * @since 3.1
1416  */
1417 public int getHeaderHeight () {
1418     checkWidget ();
1419     if (!OS.gtk_tree_view_get_headers_visible (handle)) return 0;
1420     if (columnCount > 0) {
1421         GtkRequisition requisition;
1422         int height = 0;
1423         for (int i=0; i<columnCount; i++) {
1424             auto buttonHandle = columns [i].buttonHandle;
1425             if (buttonHandle !is null) {
1426                 OS.gtk_widget_size_request (buttonHandle, &requisition);
1427                 height = Math.max (height, requisition.height);
1428             }
1429         }
1430         return height;
1431     }
1432     OS.gtk_widget_realize (handle);
1433     auto fixedWindow = OS.GTK_WIDGET_WINDOW (fixedHandle);
1434     auto binWindow = OS.gtk_tree_view_get_bin_window (handle);
1435     int binY ;
1436     OS.gdk_window_get_origin (binWindow, null, &binY);
1437     int fixedY;
1438     OS.gdk_window_get_origin (fixedWindow, null, &fixedY);
1439     return binY  - fixedY;
1440 }
1441 
1442 /**
1443  * Returns <code>true</code> if the receiver's header is visible,
1444  * and <code>false</code> otherwise.
1445  * <p>
1446  * If one of the receiver's ancestors is not visible or some
1447  * other condition makes the receiver not visible, this method
1448  * may still indicate that it is considered visible even though
1449  * it may not actually be showing.
1450  * </p>
1451  *
1452  * @return the receiver's header's visibility state
1453  *
1454  * @exception SWTException <ul>
1455  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1456  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1457  * </ul>
1458  *
1459  * @since 3.1
1460  */
1461 public bool getHeaderVisible () {
1462     checkWidget();
1463     return cast(bool)OS.gtk_tree_view_get_headers_visible (handle);
1464 }
1465 
1466 /**
1467  * Returns the item at the given, zero-relative index in the
1468  * receiver. Throws an exception if the index is out of range.
1469  *
1470  * @param index the index of the item to return
1471  * @return the item at the given index
1472  *
1473  * @exception IllegalArgumentException <ul>
1474  *    <li>ERROR_INVALID_RANGE - if the index is not between 0 and the number of elements in the list minus 1 (inclusive)</li>
1475  * </ul>
1476  * @exception SWTException <ul>
1477  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1478  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1479  * </ul>
1480  *
1481  * @since 3.1
1482  */
1483 public TreeItem getItem (int index) {
1484     checkWidget();
1485     if (!(0 <= index && index < OS.gtk_tree_model_iter_n_children (modelHandle, null)))  {
1486         error (SWT.ERROR_INVALID_RANGE);
1487     }
1488     return _getItem (null, index);
1489 }
1490 
1491 /**
1492  * Returns the item at the given point in the receiver
1493  * or null if no such item exists. The point is in the
1494  * coordinate system of the receiver.
1495  * <p>
1496  * The item that is returned represents an item that could be selected by the user.
1497  * For example, if selection only occurs in items in the first column, then null is
1498  * returned if the point is outside of the item.
1499  * Note that the SWT.FULL_SELECTION style hint, which specifies the selection policy,
1500  * determines the extent of the selection.
1501  * </p>
1502  *
1503  * @param point the point used to locate the item
1504  * @return the item at the given point, or null if the point is not in a selectable item
1505  *
1506  * @exception IllegalArgumentException <ul>
1507  *    <li>ERROR_NULL_ARGUMENT - if the point is null</li>
1508  * </ul>
1509  * @exception SWTException <ul>
1510  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1511  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1512  * </ul>
1513  */
1514 public TreeItem getItem (Point point) {
1515     checkWidget ();
1516     if (point is null) error (SWT.ERROR_NULL_ARGUMENT);
1517     void* path;
1518     OS.gtk_widget_realize (handle);
1519     GtkTreeViewColumn* columnHandle;
1520     if (!OS.gtk_tree_view_get_path_at_pos (handle, point.x, point.y, &path, &columnHandle, null, null)) return null;
1521     if (path  is null) return null;
1522     TreeItem item = null;
1523     GtkTreeIter iter;
1524     if (OS.gtk_tree_model_get_iter (modelHandle, &iter, path )) {
1525         bool overExpander = false;
1526         if (OS.gtk_tree_view_get_expander_column (handle) is columnHandle ) {
1527             int buffer;
1528             GdkRectangle rect;
1529             OS.gtk_tree_view_get_cell_area (handle, path, columnHandle, &rect);
1530             if (OS.GTK_VERSION < OS.buildVERSION (2, 8, 18)) {
1531                 OS.gtk_widget_style_get1 (handle, OS.expander_size.ptr, &buffer);
1532                 int expanderSize = buffer  + TreeItem.EXPANDER_EXTRA_PADDING;
1533                 overExpander = point.x < rect.x + expanderSize;
1534             } else {
1535                 overExpander = point.x < rect.x;
1536             }
1537         }
1538         if (!overExpander) {
1539             item = _getItem (&iter);
1540         }
1541     }
1542     OS.gtk_tree_path_free (path );
1543     return item;
1544 }
1545 
1546 /**
1547  * Returns the number of items contained in the receiver
1548  * that are direct item children of the receiver.  The
1549  * number that is returned is the number of roots in the
1550  * tree.
1551  *
1552  * @return the number of items
1553  *
1554  * @exception SWTException <ul>
1555  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1556  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1557  * </ul>
1558  */
1559 public int getItemCount () {
1560     checkWidget ();
1561     return OS.gtk_tree_model_iter_n_children (modelHandle, null);
1562 }
1563 
1564 /**
1565  * Returns the height of the area which would be used to
1566  * display <em>one</em> of the items in the tree.
1567  *
1568  * @return the height of one item
1569  *
1570  * @exception SWTException <ul>
1571  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1572  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1573  * </ul>
1574  */
1575 public int getItemHeight () {
1576     checkWidget ();
1577     int itemCount = OS.gtk_tree_model_iter_n_children (modelHandle, null);
1578     if (itemCount is 0) {
1579         auto column = OS.gtk_tree_view_get_column (handle, 0);
1580         int w, h;
1581         ignoreSize = true;
1582         OS.gtk_tree_view_column_cell_get_size (column, null, null, null, &w, &h);
1583         ignoreSize = false;
1584         return h ;
1585     } else {
1586         int height = 0;
1587         GtkTreeIter iter;
1588         OS.gtk_tree_model_get_iter_first (modelHandle, &iter);
1589         int columnCount = Math.max (1, this.columnCount);
1590         for (int i=0; i<columnCount; i++) {
1591             auto column = OS.gtk_tree_view_get_column (handle, i);
1592             OS.gtk_tree_view_column_cell_set_cell_data (column, modelHandle, &iter, false, false);
1593             int w, h;
1594             OS.gtk_tree_view_column_cell_get_size (column, null, null, null, &w, &h);
1595             height = Math.max (height, h );
1596         }
1597         return height;
1598     }
1599 }
1600 
1601 /**
1602  * Returns a (possibly empty) array of items contained in the
1603  * receiver that are direct item children of the receiver.  These
1604  * are the roots of the tree.
1605  * <p>
1606  * Note: This is not the actual structure used by the receiver
1607  * to maintain its list of items, so modifying the array will
1608  * not affect the receiver.
1609  * </p>
1610  *
1611  * @return the items
1612  *
1613  * @exception SWTException <ul>
1614  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1615  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1616  * </ul>
1617  */
1618 public TreeItem [] getItems () {
1619     checkWidget();
1620     return getItems (null);
1621 }
1622 
1623 TreeItem [] getItems (GtkTreeIter* parent) {
1624     int length = OS.gtk_tree_model_iter_n_children (modelHandle, parent);
1625     TreeItem[] result = new TreeItem [length];
1626     if (length is 0) return result;
1627     if ((style & SWT.VIRTUAL) !is 0) {
1628         for (int i=0; i<length; i++) {
1629             result [i] = _getItem (parent, i);
1630         }
1631     } else {
1632         int i = 0;
1633         int index;
1634         GtkTreeIter iter;
1635         bool valid = cast(bool)OS.gtk_tree_model_iter_children (modelHandle, &iter, parent);
1636         while (valid) {
1637             OS.gtk_tree_model_get1 (modelHandle, &iter, ID_COLUMN, cast(void**)&index);
1638             result [i++] = items [index ];
1639             valid = cast(bool)OS.gtk_tree_model_iter_next (modelHandle, &iter);
1640         }
1641     }
1642     return result;
1643 }
1644 
1645 /**
1646  * Returns <code>true</code> if the receiver's lines are visible,
1647  * and <code>false</code> otherwise.
1648  * <p>
1649  * If one of the receiver's ancestors is not visible or some
1650  * other condition makes the receiver not visible, this method
1651  * may still indicate that it is considered visible even though
1652  * it may not actually be showing.
1653  * </p>
1654  *
1655  * @return the visibility state of the lines
1656  *
1657  * @exception SWTException <ul>
1658  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1659  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1660  * </ul>
1661  *
1662  * @since 3.1
1663  */
1664 public bool getLinesVisible() {
1665     checkWidget();
1666     return cast(bool)OS.gtk_tree_view_get_rules_hint (handle);
1667 }
1668 
1669 /**
1670  * Returns the receiver's parent item, which must be a
1671  * <code>TreeItem</code> or null when the receiver is a
1672  * root.
1673  *
1674  * @return the receiver's parent item
1675  *
1676  * @exception SWTException <ul>
1677  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1678  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1679  * </ul>
1680  */
1681 public TreeItem getParentItem () {
1682     checkWidget ();
1683     return null;
1684 }
1685 
1686 GtkCellRendererPixbuf* getPixbufRenderer (GtkTreeViewColumn* column) {
1687     auto list = OS.gtk_tree_view_column_get_cell_renderers (column);
1688     if (list is null) return null;
1689     int count = OS.g_list_length (list);
1690     GtkCellRendererPixbuf* pixbufRenderer;
1691     int i = 0;
1692     while (i < count) {
1693         auto renderer = OS.g_list_nth_data (list, i);
1694         if (OS.GTK_IS_CELL_RENDERER_PIXBUF (renderer)) {
1695             pixbufRenderer = cast(GtkCellRendererPixbuf*)renderer;
1696             break;
1697         }
1698         i++;
1699     }
1700     OS.g_list_free (list);
1701     return pixbufRenderer;
1702 }
1703 
1704 /**
1705  * Returns an array of <code>TreeItem</code>s that are currently
1706  * selected in the receiver. The order of the items is unspecified.
1707  * An empty array indicates that no items are selected.
1708  * <p>
1709  * Note: This is not the actual structure used by the receiver
1710  * to maintain its selection, so modifying the array will
1711  * not affect the receiver.
1712  * </p>
1713  * @return an array representing the selection
1714  *
1715  * @exception SWTException <ul>
1716  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1717  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1718  * </ul>
1719  */
1720 public TreeItem[] getSelection () {
1721     checkWidget();
1722     auto selection = OS.gtk_tree_view_get_selection (handle);
1723     if (OS.GTK_VERSION < OS.buildVERSION (2, 2, 0)) {
1724         display.treeSelectionLength  = 0;
1725         display.treeSelection = new int [items.length];
1726         display.doTreeSelectionProcConnect( &treeSelectionCallbackData, handle, selection );
1727         TreeItem [] result = new TreeItem [display.treeSelectionLength];
1728         for (int i=0; i<result.length; i++) result [i] = items [display.treeSelection [i]];
1729         return result;
1730     }
1731     /*
1732     * Bug in GTK.  gtk_tree_selection_get_selected_rows() segmentation faults
1733     * in versions smaller than 2.2.4 if the model is NULL.  The fix is
1734     * to give a valid pointer instead.
1735     */
1736     int dummy;
1737     void* model = OS.GTK_VERSION < OS.buildVERSION (2, 2, 4) ? &dummy : null;
1738     auto list = OS.gtk_tree_selection_get_selected_rows (selection, &model);
1739     if (list !is null) {
1740         int count = OS.g_list_length (list);
1741         TreeItem [] treeSelection = new TreeItem [count];
1742         int length_ = 0;
1743         for (int i=0; i<count; i++) {
1744             auto data = OS.g_list_nth_data (list, i);
1745             GtkTreeIter iter;
1746             if (OS.gtk_tree_model_get_iter (modelHandle, &iter, data)) {
1747                 treeSelection [length_] = _getItem (&iter);
1748                 length_++;
1749             }
1750         }
1751         OS.g_list_free (list);
1752         if (length_ < count) {
1753             TreeItem [] temp = new TreeItem [length_];
1754             System.arraycopy(treeSelection, 0, temp, 0, length_);
1755             treeSelection = temp;
1756         }
1757         return treeSelection;
1758     }
1759     return null;
1760 }
1761 
1762 /**
1763  * Returns the number of selected items contained in the receiver.
1764  *
1765  * @return the number of selected items
1766  *
1767  * @exception SWTException <ul>
1768  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1769  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1770  * </ul>
1771  */
1772 public int getSelectionCount () {
1773     checkWidget();
1774     auto selection = OS.gtk_tree_view_get_selection (handle);
1775     if (OS.GTK_VERSION < OS.buildVERSION (2, 2, 0)) {
1776         display.treeSelectionLength = 0;
1777         display.treeSelection = null;
1778         display.doTreeSelectionProcConnect( &treeSelectionCallbackData, handle, selection );
1779         return display.treeSelectionLength;
1780     }
1781     return OS.gtk_tree_selection_count_selected_rows (selection);
1782 }
1783 
1784 /**
1785  * Returns the column which shows the sort indicator for
1786  * the receiver. The value may be null if no column shows
1787  * the sort indicator.
1788  *
1789  * @return the sort indicator
1790  *
1791  * @exception SWTException <ul>
1792  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1793  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1794  * </ul>
1795  *
1796  * @see #setSortColumn(TreeColumn)
1797  *
1798  * @since 3.2
1799  */
1800 public TreeColumn getSortColumn () {
1801     checkWidget ();
1802     return sortColumn;
1803 }
1804 
1805 /**
1806  * Returns the direction of the sort indicator for the receiver.
1807  * The value will be one of <code>UP</code>, <code>DOWN</code>
1808  * or <code>NONE</code>.
1809  *
1810  * @return the sort direction
1811  *
1812  * @exception SWTException <ul>
1813  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1814  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1815  * </ul>
1816  *
1817  * @see #setSortDirection(int)
1818  *
1819  * @since 3.2
1820  */
1821 public int getSortDirection () {
1822     checkWidget ();
1823     return sortDirection;
1824 }
1825 
1826 GtkCellRendererText* getTextRenderer (GtkTreeViewColumn* column) {
1827     auto list = OS.gtk_tree_view_column_get_cell_renderers (column);
1828     if (list is null) return null;
1829     int count = OS.g_list_length (list);
1830     GtkCellRendererText* textRenderer;
1831     int i = 0;
1832     while (i < count) {
1833         auto renderer = OS.g_list_nth_data (list, i);
1834          if (OS.GTK_IS_CELL_RENDERER_TEXT (renderer)) {
1835             textRenderer = cast(GtkCellRendererText*)renderer;
1836             break;
1837         }
1838         i++;
1839     }
1840     OS.g_list_free (list);
1841     return textRenderer;
1842 }
1843 
1844 /**
1845  * Returns the item which is currently at the top of the receiver.
1846  * This item can change when items are expanded, collapsed, scrolled
1847  * or new items are added or removed.
1848  *
1849  * @return the item at the top of the receiver
1850  *
1851  * @exception SWTException <ul>
1852  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1853  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1854  * </ul>
1855  *
1856  * @since 2.1
1857  */
1858 public TreeItem getTopItem () {
1859     checkWidget ();
1860     void* path;
1861     OS.gtk_widget_realize (handle);
1862     if (!OS.gtk_tree_view_get_path_at_pos (handle, 1, 1, &path, null, null, null)) return null;
1863     if (path  is null) return null;
1864     TreeItem item = null;
1865     GtkTreeIter iter;
1866     if (OS.gtk_tree_model_get_iter (modelHandle, &iter, path )) {
1867         item = _getItem (&iter);
1868     }
1869     OS.gtk_tree_path_free (path );
1870     return item;
1871 }
1872 
1873 override int gtk_button_press_event (GtkWidget* widget, GdkEventButton* gdkEvent) {
1874     if (gdkEvent.window !is OS.gtk_tree_view_get_bin_window (handle)) return 0;
1875     auto result = super.gtk_button_press_event (widget, gdkEvent);
1876     if (result !is 0) return result;
1877     /*
1878     * Feature in GTK.  In a multi-select tree view, when multiple items are already
1879     * selected, the selection state of the item is toggled and the previous selection
1880     * is cleared. This is not the desired behaviour when bringing up a popup menu.
1881     * Also, when an item is reselected with the right button, the tree view issues
1882     * an unwanted selection event. The workaround is to detect that case and not
1883     * run the default handler when the item is already part of the current selection.
1884     */
1885     int button = gdkEvent.button;
1886     if (button is 3 && gdkEvent.type is OS.GDK_BUTTON_PRESS) {
1887         void* path;
1888         if (OS.gtk_tree_view_get_path_at_pos (handle, cast(int)/*64bit*/gdkEvent.x, cast(int)/*64bit*/gdkEvent.y, &path, null, null, null)) {
1889             if (path !is null) {
1890                 auto selection = OS.gtk_tree_view_get_selection (handle);
1891                 if (OS.gtk_tree_selection_path_is_selected (selection, path )) result = 1;
1892                 OS.gtk_tree_path_free (path );
1893             }
1894         }
1895     }
1896 
1897     /*
1898     * Feature in GTK.  When the user clicks in a single selection GtkTreeView
1899     * and there are no selected items, the first item is selected automatically
1900     * before the click is processed, causing two selection events.  The is fix
1901     * is the set the cursor item to be same as the clicked item to stop the
1902     * widget from automatically selecting the first item.
1903     */
1904     if ((style & SWT.SINGLE) !is 0 && getSelectionCount () is 0) {
1905         void* path;
1906         if (OS.gtk_tree_view_get_path_at_pos (handle, cast(int)/*64bit*/gdkEvent.x, cast(int)/*64bit*/gdkEvent.y, &path, null, null, null)) {
1907             if (path  !is null) {
1908                 auto selection = OS.gtk_tree_view_get_selection (handle);
1909                 OS.g_signal_handlers_block_matched (selection, OS.G_SIGNAL_MATCH_DATA, 0, 0, null, null, udCHANGED);
1910                 OS.gtk_tree_view_set_cursor (handle, path , null, false);
1911                 OS.g_signal_handlers_unblock_matched (selection, OS.G_SIGNAL_MATCH_DATA, 0, 0, null, null, udCHANGED);
1912                 OS.gtk_tree_path_free (path );
1913             }
1914         }
1915     }
1916     /*
1917     * Bug in GTK. GTK segments fault, if the GtkTreeView widget is
1918     * not in focus and all items in the widget are disposed before
1919     * it finishes processing a button press.  The fix is to give
1920     * focus to the widget before it starts processing the event.
1921     */
1922     if (!OS.GTK_WIDGET_HAS_FOCUS (handle)) {
1923         OS.gtk_widget_grab_focus (handle);
1924     }
1925     return result;
1926 }
1927 
1928 override int gtk_button_release_event (GtkWidget* widget, GdkEventButton* event) {
1929     auto window = OS.GDK_EVENT_WINDOW (event);
1930     if (window !is OS.gtk_tree_view_get_bin_window (handle)) return 0;
1931     return super.gtk_button_release_event (widget, event);
1932 }
1933 
1934 override int gtk_changed (GtkWidget* widget) {
1935     TreeItem item = getFocusItem ();
1936     if (item !is null) {
1937         Event event = new Event ();
1938         event.item = item;
1939         postEvent (SWT.Selection, event);
1940     }
1941     return 0;
1942 }
1943 
1944 override int gtk_expand_collapse_cursor_row (GtkWidget* widget, ptrdiff_t logical, ptrdiff_t expand, ptrdiff_t open_all) {
1945     // FIXME - this flag is never cleared.  It should be cleared when the expand all operation completes.
1946     if (expand !is 0 && open_all !is 0) expandAll = true;
1947     return 0;
1948 }
1949 
1950 override int gtk_key_press_event (GtkWidget* widget, GdkEventKey* keyEvent) {
1951     auto result = super.gtk_key_press_event (widget, keyEvent);
1952     if (result !is 0) return result;
1953     if (OS.GTK_VERSION < OS.buildVERSION (2, 2 ,0)) {
1954         /*
1955         * Feature in GTK 2.0.x.  When an item is default selected using
1956         * the return key, GTK does not issue notification. The fix is
1957         * to issue this notification when the return key is pressed.
1958         */
1959         int key = keyEvent.keyval;
1960         switch (key) {
1961             case OS.GDK_Return:
1962             case OS.GDK_KP_Enter: {
1963                 Event event = new Event ();
1964                 event.item = getFocusItem ();
1965                 postEvent (SWT.DefaultSelection, event);
1966                 break;
1967             }
1968             default:
1969         }
1970     }
1971     return result;
1972 }
1973 
1974 override int gtk_motion_notify_event (GtkWidget* widget, GdkEventMotion* event) {
1975     auto window = OS.GDK_EVENT_WINDOW (event);
1976     if (window !is OS.gtk_tree_view_get_bin_window (handle)) return 0;
1977     return super.gtk_motion_notify_event (widget, event);
1978 }
1979 
1980 override int gtk_popup_menu (GtkWidget* widget) {
1981     auto result = super.gtk_popup_menu (widget);
1982     /*
1983     * Bug in GTK.  The context menu for the typeahead in GtkTreeViewer
1984     * opens in the bottom right corner of the screen when Shift+F10
1985     * is pressed and the typeahead window was not visible.  The fix is
1986     * to prevent the context menu from opening by stopping the default
1987     * handler.
1988     *
1989     * NOTE: The bug only happens in GTK 2.6.5 and lower.
1990     */
1991     return OS.GTK_VERSION < OS.buildVERSION (2, 6, 5) ? 1 : result;
1992 }
1993 
1994 override void gtk_row_activated (GtkTreeView* tree, GtkTreePath* path, GtkTreeViewColumn* column) {
1995     if (path is null) return;
1996     TreeItem item = null;
1997     GtkTreeIter iter;
1998     if (OS.gtk_tree_model_get_iter (modelHandle, &iter, path)) {
1999         int index;
2000         OS.gtk_tree_model_get1 (modelHandle, &iter, ID_COLUMN, cast(void**)&index);
2001         item = items [index ];
2002     }
2003     Event event = new Event ();
2004     event.item = item;
2005     postEvent (SWT.DefaultSelection, event);
2006     return;
2007 }
2008 
2009 override int gtk_test_collapse_row (
2010     GtkTreeView *tree,
2011     GtkTreeIter *iter,
2012     GtkTreePath *path)
2013 {
2014     int index ;
2015     OS.gtk_tree_model_get1 (modelHandle, iter, ID_COLUMN, cast(void**)&index);
2016     TreeItem item = items [index ];
2017     Event event = new Event ();
2018     event.item = item;
2019     bool oldModelChanged = modelChanged;
2020     modelChanged = false;
2021     sendEvent (SWT.Collapse, event);
2022     /*
2023     * Bug in GTK.  Collapsing the target row during the test_collapse_row
2024     * handler will cause a segmentation fault if the animation code is allowed
2025     * to run.  The fix is to block the animation if the row is already
2026     * collapsed.
2027     */
2028     bool changed = modelChanged || !OS.gtk_tree_view_row_expanded (handle, path);
2029     modelChanged = oldModelChanged;
2030     if (isDisposed () || item.isDisposed ()) return 1;
2031     /*
2032     * Bug in GTK.  Expanding or collapsing a row which has no more
2033     * children causes the model state to become invalid, causing
2034     * GTK to give warnings and behave strangely.  Other changes to
2035     * the model can cause expansion to fail when using the multiple
2036     * expansion keys (such as *).  The fix is to stop the expansion
2037     * if there are model changes.
2038     *
2039     * Note: This callback must return 0 for the collapsing
2040     * animation to occur.
2041     */
2042     if (changed) {
2043         OS.g_signal_handlers_block_matched (handle, OS.G_SIGNAL_MATCH_DATA, 0, 0, null, null, udTEST_COLLAPSE_ROW);
2044         OS.gtk_tree_view_collapse_row (handle, path);
2045         OS.g_signal_handlers_unblock_matched (handle, OS.G_SIGNAL_MATCH_DATA, 0, 0, null, null, udTEST_COLLAPSE_ROW);
2046         return 1;
2047     }
2048     return 0;
2049 }
2050 
2051 override int gtk_test_expand_row (
2052     GtkTreeView *tree,
2053     GtkTreeIter *iter,
2054     GtkTreePath *path)
2055 {
2056     int index ;
2057     OS.gtk_tree_model_get1 (modelHandle, iter, ID_COLUMN, cast(void**)&index);
2058     TreeItem item = items [index ];
2059     Event event = new Event ();
2060     event.item = item;
2061     bool oldModelChanged = modelChanged;
2062     modelChanged = false;
2063     sendEvent (SWT.Expand, event);
2064     /*
2065     * Bug in GTK.  Expanding the target row during the test_expand_row
2066     * handler will cause a segmentation fault if the animation code is allowed
2067     * to run.  The fix is to block the animation if the row is already
2068     * expanded.
2069     */
2070     bool changed = modelChanged || OS.gtk_tree_view_row_expanded (handle, path);
2071     modelChanged = oldModelChanged;
2072     if (isDisposed () || item.isDisposed ()) return 1;
2073     /*
2074     * Bug in GTK.  Expanding or collapsing a row which has no more
2075     * children causes the model state to become invalid, causing
2076     * GTK to give warnings and behave strangely.  Other changes to
2077     * the model can cause expansion to fail when using the multiple
2078     * expansion keys (such as *).  The fix is to stop the expansion
2079     * if there are model changes.
2080     *
2081     * Bug in GTK.  test-expand-row does not get called for each row
2082     * in an expand all operation.  The fix is to block the initial
2083     * expansion and only expand a single level.
2084     *
2085     * Note: This callback must return 0 for the collapsing
2086     * animation to occur.
2087     */
2088     if (changed || expandAll) {
2089         OS.g_signal_handlers_block_matched (handle, OS.G_SIGNAL_MATCH_DATA, 0, 0, null, null, udTEST_EXPAND_ROW);
2090         OS.gtk_tree_view_expand_row (handle, path, false);
2091         OS.g_signal_handlers_unblock_matched (handle, OS.G_SIGNAL_MATCH_DATA, 0, 0, null, null, udTEST_EXPAND_ROW);
2092         return 1;
2093     }
2094     return 0;
2095 }
2096 
2097 override int gtk_toggled (int renderer, char* pathStr) {
2098     auto path = OS.gtk_tree_path_new_from_string (pathStr);
2099     if (path is null) return 0;
2100     TreeItem item = null;
2101     GtkTreeIter iter;
2102     if (OS.gtk_tree_model_get_iter (modelHandle, &iter, path)) {
2103         item = _getItem (&iter);
2104     }
2105     OS.gtk_tree_path_free (path);
2106     if (item !is null) {
2107         item.setChecked (!item.getChecked ());
2108         Event event = new Event ();
2109         event.detail = SWT.CHECK;
2110         event.item = item;
2111         postEvent (SWT.Selection, event);
2112     }
2113     return 0;
2114 }
2115 
2116 override void gtk_widget_size_request (GtkWidget* widget, GtkRequisition* requisition) {
2117     /*
2118      * Bug in GTK.  For some reason, gtk_widget_size_request() fails
2119      * to include the height of the tree view items when there are
2120      * no columns visible.  The fix is to temporarily make one column
2121      * visible.
2122      */
2123     if (columnCount is 0) {
2124         super.gtk_widget_size_request (widget, requisition);
2125         return;
2126     }
2127     auto columns = OS.gtk_tree_view_get_columns (handle), list = columns;
2128     bool fixVisible = columns !is null;
2129     while (list !is null) {
2130         auto column = OS.g_list_data (list);
2131         if (OS.gtk_tree_view_column_get_visible (column)) {
2132             fixVisible = false;
2133             break;
2134         }
2135         list = OS.g_list_next (list);
2136     }
2137     GtkTreeViewColumn* columnHandle;
2138     if (fixVisible) {
2139         columnHandle = cast(GtkTreeViewColumn*)OS.g_list_data (columns);
2140         OS.gtk_tree_view_column_set_visible (columnHandle, true);
2141     }
2142     super.gtk_widget_size_request (widget, requisition);
2143     if (fixVisible) {
2144         OS.gtk_tree_view_column_set_visible (columnHandle, false);
2145     }
2146     if (columns !is null) OS.g_list_free (columns);
2147 }
2148 
2149 void hideFirstColumn () {
2150     auto firstColumn = OS.gtk_tree_view_get_column (handle, 0);
2151     OS.gtk_tree_view_column_set_visible (firstColumn, false);
2152 }
2153 
2154 override void hookEvents () {
2155     super.hookEvents ();
2156     auto selection = OS.gtk_tree_view_get_selection(handle);
2157     OS.g_signal_connect_closure (selection, OS.changed.ptr, display.closures [CHANGED], false);
2158     OS.g_signal_connect_closure (handle, OS.row_activated.ptr, display.closures [ROW_ACTIVATED], false);
2159     OS.g_signal_connect_closure (handle, OS.test_expand_row.ptr, display.closures [TEST_EXPAND_ROW], false);
2160     OS.g_signal_connect_closure (handle, OS.test_collapse_row.ptr, display.closures [TEST_COLLAPSE_ROW], false);
2161     OS.g_signal_connect_closure (handle, OS.expand_collapse_cursor_row.ptr, display.closures [EXPAND_COLLAPSE_CURSOR_ROW], false);
2162     if (checkRenderer !is null) {
2163         OS.g_signal_connect_closure (checkRenderer, OS.toggled.ptr, display.closures [TOGGLED], false);
2164     }
2165 }
2166 
2167 /**
2168  * Searches the receiver's list starting at the first column
2169  * (index 0) until a column is found that is equal to the
2170  * argument, and returns the index of that column. If no column
2171  * is found, returns -1.
2172  *
2173  * @param column the search column
2174  * @return the index of the column
2175  *
2176  * @exception IllegalArgumentException <ul>
2177  *    <li>ERROR_NULL_ARGUMENT - if the column is null</li>
2178  * </ul>
2179  * @exception SWTException <ul>
2180  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
2181  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
2182  * </ul>
2183  *
2184  * @since 3.1
2185  */
2186 public int indexOf (TreeColumn column) {
2187     checkWidget();
2188     if (column is null) error (SWT.ERROR_NULL_ARGUMENT);
2189     for (int i=0; i<columnCount; i++) {
2190         if (columns [i] is column) return i;
2191     }
2192     return -1;
2193 }
2194 
2195 /**
2196  * Searches the receiver's list starting at the first item
2197  * (index 0) until an item is found that is equal to the
2198  * argument, and returns the index of that item. If no item
2199  * is found, returns -1.
2200  *
2201  * @param item the search item
2202  * @return the index of the item
2203  *
2204  * @exception IllegalArgumentException <ul>
2205  *    <li>ERROR_NULL_ARGUMENT - if the item is null</li>
2206  *    <li>ERROR_INVALID_ARGUMENT - if the item has been disposed</li>
2207  * </ul>
2208  * @exception SWTException <ul>
2209  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
2210  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
2211  * </ul>
2212  *
2213  * @since 3.1
2214  */
2215 public int indexOf (TreeItem item) {
2216     checkWidget();
2217     if (item is null) error (SWT.ERROR_NULL_ARGUMENT);
2218     if (item.isDisposed()) error (SWT.ERROR_INVALID_ARGUMENT);
2219     int index = -1;
2220     auto path = OS.gtk_tree_model_get_path (modelHandle, item.handle);
2221     int depth = OS.gtk_tree_path_get_depth (path);
2222     if (depth is 1) {
2223         auto indices = OS.gtk_tree_path_get_indices (path);
2224         if (indices !is null) {
2225             index = indices[0];
2226         }
2227     }
2228     OS.gtk_tree_path_free (path);
2229     return index;
2230 }
2231 
2232 override bool mnemonicHit (wchar key) {
2233     for (int i=0; i<columnCount; i++) {
2234         auto labelHandle = columns [i].labelHandle;
2235         if (labelHandle !is null && mnemonicHit (labelHandle, key)) return true;
2236     }
2237     return false;
2238 }
2239 
2240 override bool mnemonicMatch (wchar key) {
2241     for (int i=0; i<columnCount; i++) {
2242         auto labelHandle = columns [i].labelHandle;
2243         if (labelHandle !is null && mnemonicMatch (labelHandle, key)) return true;
2244     }
2245     return false;
2246 }
2247 
2248 override GdkDrawable* paintWindow () {
2249     OS.gtk_widget_realize (handle);
2250     return OS.gtk_tree_view_get_bin_window (handle);
2251 }
2252 
2253 void recreateRenderers () {
2254     if (checkRenderer !is null) {
2255         display.removeWidget (cast(GtkWidget*)checkRenderer);
2256         OS.g_object_unref (checkRenderer);
2257         checkRenderer = ownerDraw ? cast(GtkCellRenderer*)OS.g_object_new (display.gtk_cell_renderer_toggle_get_type(), null) : OS.gtk_cell_renderer_toggle_new ();
2258         if (checkRenderer is null) error (SWT.ERROR_NO_HANDLES);
2259         OS.g_object_ref (checkRenderer);
2260         display.addWidget (cast(GtkWidget*)checkRenderer, this);
2261         OS.g_signal_connect_closure (checkRenderer, OS.toggled.ptr, display.closures [TOGGLED], false);
2262     }
2263     if (columnCount is 0) {
2264         createRenderers (OS.gtk_tree_view_get_column (handle, 0), Tree.FIRST_COLUMN, true, 0);
2265     } else {
2266         for (int i = 0; i < columnCount; i++) {
2267             TreeColumn column = columns [i];
2268             createRenderers (cast(GtkTreeViewColumn*)column.handle, column.modelIndex, i is 0, column.style);
2269         }
2270     }
2271 }
2272 
2273 override void redrawBackgroundImage () {
2274     Control control = findBackgroundControl ();
2275     if (control !is null && control.backgroundImage !is null) {
2276         redrawWidget (0, 0, 0, 0, true, false, false);
2277     }
2278 }
2279 
2280 override void register () {
2281     super.register ();
2282     display.addWidget (cast(GtkWidget*)OS.gtk_tree_view_get_selection (handle), this);
2283     if (checkRenderer !is null) display.addWidget (cast(GtkWidget*)checkRenderer, this);
2284 }
2285 
2286 void releaseItem (TreeItem item, bool release) {
2287     int index;
2288     OS.gtk_tree_model_get1 (modelHandle, item.handle, ID_COLUMN, cast(void**)&index);
2289     if (index is -1) return;
2290     if (release) item.release (false);
2291     items [index ] = null;
2292 }
2293 
2294 void releaseItems (GtkTreeIter* parentIter) {
2295     int index;
2296     GtkTreeIter iter;
2297     bool valid = cast(bool)OS.gtk_tree_model_iter_children (modelHandle, &iter, parentIter);
2298     while (valid) {
2299         releaseItems (&iter);
2300         if (!isDisposed ()) {
2301             OS.gtk_tree_model_get1 (modelHandle, &iter, ID_COLUMN, cast(void**)&index);
2302             if (index  !is -1) {
2303                 TreeItem item = items [index ];
2304                 if (item !is null) releaseItem (item, true);
2305             }
2306         }
2307         valid = cast(bool)OS.gtk_tree_model_iter_next (modelHandle, &iter);
2308     }
2309 }
2310 
2311 override void releaseChildren (bool destroy) {
2312     if (items !is null) {
2313         for (int i=0; i<items.length; i++) {
2314             TreeItem item = items [i];
2315             if (item !is null && !item.isDisposed ()) {
2316                 item.release (false);
2317             }
2318         }
2319         items = null;
2320     }
2321     if (columns !is null) {
2322         for (int i=0; i<columnCount; i++) {
2323             TreeColumn column = columns [i];
2324             if (column !is null && !column.isDisposed ()) {
2325                 column.release (false);
2326             }
2327         }
2328         columns = null;
2329     }
2330     super.releaseChildren (destroy);
2331 }
2332 
2333 override void releaseWidget () {
2334     super.releaseWidget ();
2335     if (modelHandle !is null) OS.g_object_unref (modelHandle);
2336     modelHandle = null;
2337     if (checkRenderer !is null) OS.g_object_unref (checkRenderer);
2338     checkRenderer = null;
2339     if (imageList !is null) imageList.dispose ();
2340     if (headerImageList !is null) headerImageList.dispose ();
2341     imageList = headerImageList = null;
2342     currentItem = null;
2343 }
2344 
2345 void remove (GtkTreeIter* parentIter, int start, int end) {
2346     if (start > end) return;
2347     int itemCount = OS.gtk_tree_model_iter_n_children (modelHandle, parentIter);
2348     if (!(0 <= start && start <= end && end < itemCount)) {
2349         error (SWT.ERROR_INVALID_RANGE);
2350     }
2351     auto selection = OS.gtk_tree_view_get_selection (handle);
2352     GtkTreeIter iter;
2353     int index = start;
2354     for (int i = start; i <= end; i++) {
2355         OS.gtk_tree_model_iter_nth_child (modelHandle, &iter, parentIter, index);
2356         int value;
2357         OS.gtk_tree_model_get1 (modelHandle, &iter, ID_COLUMN, cast(void**)&value);
2358         TreeItem item = value  !is -1 ? items [value ] : null;
2359         if (item !is null && !item.isDisposed ()) {
2360             item.dispose ();
2361         } else {
2362             OS.g_signal_handlers_block_matched (selection, OS.G_SIGNAL_MATCH_DATA, 0, 0, null, null, udCHANGED);
2363             OS.gtk_tree_store_remove (modelHandle, &iter);
2364             OS.g_signal_handlers_unblock_matched (selection, OS.G_SIGNAL_MATCH_DATA, 0, 0, null, null, udCHANGED);
2365         }
2366     }
2367 }
2368 
2369 /**
2370  * Removes all of the items from the receiver.
2371  *
2372  * @exception SWTException <ul>
2373  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
2374  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
2375  * </ul>
2376  */
2377 public void removeAll () {
2378     checkWidget ();
2379     for (int i=0; i<items.length; i++) {
2380         TreeItem item = items [i];
2381         if (item !is null && !item.isDisposed ()) item.release (false);
2382     }
2383     items = new TreeItem[4];
2384     auto selection = OS.gtk_tree_view_get_selection (handle);
2385     OS.g_signal_handlers_block_matched (selection, OS.G_SIGNAL_MATCH_DATA, 0, 0, null, null, udCHANGED);
2386     OS.gtk_tree_store_clear (modelHandle);
2387     OS.g_signal_handlers_unblock_matched (selection, OS.G_SIGNAL_MATCH_DATA, 0, 0, null, null, udCHANGED);
2388 
2389     /* Disable searching when using VIRTUAL */
2390     if ((style & SWT.VIRTUAL) !is 0) {
2391         /*
2392         * Bug in GTK. Until GTK 2.6.5, calling gtk_tree_view_set_enable_search(FALSE)
2393         * would prevent the user from being able to type in text to search the tree.
2394         * After 2.6.5, GTK introduced Ctrl+F as being the key binding for interactive
2395         * search. This meant that even if FALSE was passed to enable_search, the user
2396         * can still bring up the search pop up using the keybinding. GTK also introduced
2397         * the notion of passing a -1 to gtk_set_search_column to disable searching
2398         * (including the search key binding).  The fix is to use the right calls
2399         * for the right version.
2400         */
2401         if (OS.GTK_VERSION >= OS.buildVERSION (2, 6, 5)) {
2402             OS.gtk_tree_view_set_search_column (handle, -1);
2403         } else {
2404             OS.gtk_tree_view_set_enable_search (handle, false);
2405         }
2406     } else {
2407         /* Set the search column whenever the model changes */
2408         int firstColumn = columnCount is 0 ? FIRST_COLUMN : columns [0].modelIndex;
2409         OS.gtk_tree_view_set_search_column (handle, firstColumn + CELL_TEXT);
2410     }
2411 }
2412 
2413 /**
2414  * Removes the listener from the collection of listeners who will
2415  * be notified when the user changes the receiver's selection.
2416  *
2417  * @param listener the listener which should no longer be notified
2418  *
2419  * @exception IllegalArgumentException <ul>
2420  *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
2421  * </ul>
2422  * @exception SWTException <ul>
2423  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
2424  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
2425  * </ul>
2426  *
2427  * @see SelectionListener
2428  * @see #addSelectionListener
2429  */
2430 public void removeSelectionListener (SelectionListener listener) {
2431     checkWidget ();
2432     if (listener is null) error (SWT.ERROR_NULL_ARGUMENT);
2433     eventTable.unhook (SWT.Selection, listener);
2434     eventTable.unhook (SWT.DefaultSelection, listener);
2435 }
2436 
2437 /**
2438  * Removes the listener from the collection of listeners who will
2439  * be notified when items in the receiver are expanded or collapsed.
2440  *
2441  * @param listener the listener which should no longer be notified
2442  *
2443  * @exception IllegalArgumentException <ul>
2444  *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
2445  * </ul>
2446  * @exception SWTException <ul>
2447  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
2448  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
2449  * </ul>
2450  *
2451  * @see TreeListener
2452  * @see #addTreeListener
2453  */
2454 public void removeTreeListener(TreeListener listener) {
2455     checkWidget ();
2456     if (listener is null) error (SWT.ERROR_NULL_ARGUMENT);
2457     if (eventTable is null) return;
2458     eventTable.unhook (SWT.Expand, listener);
2459     eventTable.unhook (SWT.Collapse, listener);
2460 }
2461 
2462 override void rendererGetSizeProc (
2463     GtkCellRenderer      *cell,
2464     GtkWidget            *widget,
2465     GdkRectangle         *cell_area,
2466     int                  *x_offset,
2467     int                  *y_offset,
2468     int                  *width,
2469     int                  *height)
2470 {
2471     auto g_class = OS.g_type_class_peek_parent (OS.G_OBJECT_GET_CLASS (cell));
2472     GtkCellRendererClass* klass = cast(GtkCellRendererClass*)g_class;
2473     klass.get_size( cell, handle, cell_area, x_offset, y_offset, width, height);
2474     if (!ignoreSize && OS.GTK_IS_CELL_RENDERER_TEXT (cell)) {
2475         auto iter = cast(GtkTreeIter*)OS.g_object_get_qdata (cast(GObject*)cell, Display.SWT_OBJECT_INDEX2);
2476         TreeItem item = null;
2477         if (iter !is null) item = _getItem (iter);
2478         if (item !is null) {
2479             int columnIndex = 0;
2480             if (columnCount > 0) {
2481                 auto columnHandle = cast(GtkTreeViewColumn*)OS.g_object_get_qdata (cast(GObject*)cell, Display.SWT_OBJECT_INDEX1);
2482                 for (int i = 0; i < columnCount; i++) {
2483                     if (columns [i].handle is cast(GtkWidget*)columnHandle) {
2484                         columnIndex = i;
2485                         break;
2486                     }
2487                 }
2488             }
2489             if (hooks (SWT.MeasureItem)) {
2490                 int contentWidth, contentHeight;
2491                 if (width !is null) contentWidth = *width;
2492                 if (height !is null) contentHeight = *height;
2493                 Image image = item.getImage (columnIndex);
2494                 int imageWidth = 0;
2495                 if (image !is null) {
2496                     Rectangle bounds = image.getBounds ();
2497                     imageWidth = bounds.width;
2498                 }
2499                 contentWidth += imageWidth;
2500                 GC gc = new GC (this);
2501                 gc.setFont (item.getFont (columnIndex));
2502                 Event event = new Event ();
2503                 event.item = item;
2504                 event.index = columnIndex;
2505                 event.gc = gc;
2506                 event.width = contentWidth ;
2507                 event.height = contentHeight ;
2508                 sendEvent (SWT.MeasureItem, event);
2509                 gc.dispose ();
2510                 contentWidth  = event.width - imageWidth;
2511                 if (contentHeight < event.height) contentHeight = event.height;
2512                 if (width !is null) *width = contentWidth;
2513                 if (height !is null) *height = contentHeight;
2514             }
2515         }
2516     }
2517 }
2518 
2519 override void rendererRenderProc (
2520     GtkCellRenderer * cell,
2521     GdkDrawable * window,
2522     GtkWidget * widget,
2523     GdkRectangle *background_area,
2524     GdkRectangle *cell_area,
2525     GdkRectangle *expose_area,
2526     int flags)
2527 {
2528     TreeItem item = null;
2529     auto iter = cast(GtkTreeIter*)OS.g_object_get_qdata (cast(GObject*)cell, Display.SWT_OBJECT_INDEX2);
2530     if (iter !is null) item = _getItem (iter);
2531     auto columnHandle = cast(GtkTreeViewColumn*)OS.g_object_get_qdata (cast(GObject*)cell, Display.SWT_OBJECT_INDEX1);
2532     int columnIndex = 0;
2533     if (columnCount > 0) {
2534         for (int i = 0; i < columnCount; i++) {
2535             if (columns [i].handle is cast(GtkWidget*)columnHandle) {
2536                 columnIndex = i;
2537                 break;
2538             }
2539         }
2540     }
2541     if (item !is null) {
2542         if (OS.GTK_IS_CELL_RENDERER_TOGGLE (cell) || (OS.GTK_IS_CELL_RENDERER_PIXBUF (cell) && (columnIndex !is 0 || (style & SWT.CHECK) is 0))) {
2543             drawFlags = flags;
2544             drawState = SWT.FOREGROUND;
2545             void* ptr;
2546             OS.gtk_tree_model_get1 (modelHandle, item.handle, Tree.BACKGROUND_COLUMN, &ptr);
2547             if (ptr is null) {
2548                 int modelIndex = columnCount is 0 ? Tree.FIRST_COLUMN : columns [columnIndex].modelIndex;
2549                 OS.gtk_tree_model_get1 (modelHandle, item.handle, modelIndex + Tree.CELL_BACKGROUND, &ptr);
2550             }
2551             if (ptr !is null) drawState |= SWT.BACKGROUND;
2552             if ((flags & OS.GTK_CELL_RENDERER_SELECTED) !is 0) drawState |= SWT.SELECTED;
2553             if ((flags & OS.GTK_CELL_RENDERER_FOCUSED) !is 0) drawState |= SWT.FOCUSED;
2554 
2555             GdkRectangle rect;
2556             auto path = OS.gtk_tree_model_get_path (modelHandle, iter);
2557             OS.gtk_tree_view_get_background_area (handle, path, columnHandle, &rect);
2558             OS.gtk_tree_path_free (path);
2559 
2560             if ((drawState & SWT.SELECTED) is 0) {
2561                 Control control = findBackgroundControl ();
2562                 if (control !is null && control.backgroundImage !is null) {
2563                     OS.gdk_window_clear_area (window, rect.x, rect.y, rect.width, rect.height);
2564                 }
2565             }
2566 
2567             if (hooks (SWT.EraseItem)) {
2568                 bool wasSelected = false;
2569                 if ((drawState & SWT.SELECTED) !is 0) {
2570                     wasSelected = true;
2571                     OS.gdk_window_clear_area (window, rect.x, rect.y, rect.width, rect.height);
2572                 }
2573                 GC gc = new GC (this);
2574                 if ((drawState & SWT.SELECTED) !is 0) {
2575                     gc.setBackground (display.getSystemColor (SWT.COLOR_LIST_SELECTION));
2576                     gc.setForeground (display.getSystemColor (SWT.COLOR_LIST_SELECTION_TEXT));
2577                 } else {
2578                     gc.setBackground (item.getBackground (columnIndex));
2579                     gc.setForeground (item.getForeground (columnIndex));
2580                 }
2581                 gc.setFont (item.getFont (columnIndex));
2582                 if ((style & SWT.MIRRORED) !is 0) rect.x = getClientWidth () - rect.width - rect.x;
2583                 gc.setClipping (rect.x, rect.y, rect.width, rect.height);
2584                 Event event = new Event ();
2585                 event.item = item;
2586                 event.index = columnIndex;
2587                 event.gc = gc;
2588                 event.x = rect.x;
2589                 event.y = rect.y;
2590                 event.width = rect.width;
2591                 event.height = rect.height;
2592                 event.detail = drawState;
2593                 sendEvent (SWT.EraseItem, event);
2594                 drawForeground = null;
2595                 drawState = event.doit ? event.detail : 0;
2596                 drawFlags &= ~(OS.GTK_CELL_RENDERER_FOCUSED | OS.GTK_CELL_RENDERER_SELECTED);
2597                 if ((drawState & SWT.SELECTED) !is 0) drawFlags |= OS.GTK_CELL_RENDERER_SELECTED;
2598                 if ((drawState & SWT.FOCUSED) !is 0) drawFlags |= OS.GTK_CELL_RENDERER_FOCUSED;
2599                 if ((drawState & SWT.SELECTED) !is 0) {
2600                     auto style = OS.gtk_widget_get_style (widget);
2601                     //TODO - parity and sorted
2602                     OS.gtk_paint_flat_box (style, window, OS.GTK_STATE_SELECTED, OS.GTK_SHADOW_NONE, &rect, widget, "cell_odd".ptr, rect.x, rect.y, rect.width, rect.height);
2603                 } else {
2604                     if (wasSelected) drawForeground = gc.getForeground ().handle;
2605                 }
2606                 gc.dispose();
2607             }
2608         }
2609     }
2610     int result = 0;
2611     if ((drawState & SWT.BACKGROUND) !is 0 && (drawState & SWT.SELECTED) is 0) {
2612         GC gc = new GC (this);
2613         gc.setBackground (item.getBackground (columnIndex));
2614         gc.fillRectangle (background_area.x, background_area.y, background_area.width, background_area.height);
2615         gc.dispose ();
2616     }
2617     if ((drawState & SWT.FOREGROUND) !is 0 || OS.GTK_IS_CELL_RENDERER_TOGGLE (cell)) {
2618         auto g_class = OS.g_type_class_peek_parent (OS.G_OBJECT_GET_CLASS (cell));
2619         GtkCellRendererClass* klass = cast(GtkCellRendererClass*)g_class;
2620         if (drawForeground !is null && OS.GTK_IS_CELL_RENDERER_TEXT (cell)) {
2621             OS.g_object_set1 (cell, OS.foreground_gdk.ptr, cast(ptrdiff_t)drawForeground);
2622         }
2623         klass.render( cell, window, handle, background_area, cell_area, expose_area, drawFlags);
2624     }
2625     if (item !is null) {
2626         if (OS.GTK_IS_CELL_RENDERER_TEXT (cell)) {
2627             if (hooks (SWT.PaintItem)) {
2628                 GdkRectangle rect;
2629                 auto path = OS.gtk_tree_model_get_path (modelHandle, iter);
2630                 OS.gtk_tree_view_get_cell_area (handle, path, columnHandle, &rect);
2631                 OS.gtk_tree_path_free (path);
2632                 if (OS.GTK_VERSION < OS.buildVERSION (2, 8, 18) && OS.gtk_tree_view_get_expander_column (handle) is columnHandle) {
2633                     int buffer;
2634                     OS.gtk_widget_style_get1 (handle, OS.expander_size.ptr, &buffer);
2635                     rect.x += buffer + TreeItem.EXPANDER_EXTRA_PADDING;
2636                     rect.width -= buffer + TreeItem.EXPANDER_EXTRA_PADDING;
2637                     //OS.gtk_widget_style_get (handle, OS.horizontal_separator, buffer, 0);
2638                     //rect.x += buffer[0];
2639                     //rect.width -= buffer [0]; // TODO Is this required for some versions?
2640                 }
2641                 ignoreSize = true;
2642                 int contentX, contentWidth;
2643                 OS.gtk_cell_renderer_get_size (cell, handle, null, null, null, &contentWidth, null);
2644                 OS.gtk_tree_view_column_cell_get_position (columnHandle, cell, &contentX, null);
2645                 ignoreSize = false;
2646                 Image image = item.getImage (columnIndex);
2647                 int imageWidth = 0;
2648                 if (image !is null) {
2649                     Rectangle bounds = image.getBounds ();
2650                     imageWidth = bounds.width;
2651                 }
2652                 contentX -= imageWidth;
2653                 contentWidth += imageWidth;
2654                 GC gc = new GC (this);
2655                 if ((drawState & SWT.SELECTED) !is 0) {
2656                     gc.setBackground (display.getSystemColor (SWT.COLOR_LIST_SELECTION));
2657                     gc.setForeground (display.getSystemColor (SWT.COLOR_LIST_SELECTION_TEXT));
2658                 } else {
2659                     gc.setBackground (item.getBackground (columnIndex));
2660                     Color foreground = drawForeground !is null ? Color.gtk_new (display, drawForeground) : item.getForeground (columnIndex);
2661                     gc.setForeground (foreground);
2662                 }
2663                 gc.setFont (item.getFont (columnIndex));
2664                 if ((style & SWT.MIRRORED) !is 0) rect.x = getClientWidth () - rect.width - rect.x;
2665                 gc.setClipping (rect.x, rect.y, rect.width, rect.height);
2666                 Event event = new Event ();
2667                 event.item = item;
2668                 event.index = columnIndex;
2669                 event.gc = gc;
2670                 event.x = rect.x + contentX;
2671                 event.y = rect.y;
2672                 event.width = contentWidth;
2673                 event.height = rect.height;
2674                 event.detail = drawState;
2675                 sendEvent (SWT.PaintItem, event);
2676                 gc.dispose();
2677             }
2678         }
2679     }
2680     //return result;
2681 }
2682 
2683 void resetCustomDraw () {
2684     if ((style & SWT.VIRTUAL) !is 0 || ownerDraw) return;
2685     int end = Math.max (1, columnCount);
2686     for (int i=0; i<end; i++) {
2687         bool customDraw = columnCount !is 0 ? columns [i].customDraw : firstCustomDraw;
2688         if (customDraw) {
2689             auto column = OS.gtk_tree_view_get_column (handle, i);
2690             auto textRenderer = getTextRenderer (column);
2691             OS.gtk_tree_view_column_set_cell_data_func (column, textRenderer, null, null, null);
2692             if (columnCount !is 0) columns [i].customDraw = false;
2693         }
2694     }
2695     firstCustomDraw = false;
2696 }
2697 
2698 /**
2699  * Display a mark indicating the point at which an item will be inserted.
2700  * The drop insert item has a visual hint to show where a dragged item
2701  * will be inserted when dropped on the tree.
2702  *
2703  * @param item the insert item.  Null will clear the insertion mark.
2704  * @param before true places the insert mark above 'item'. false places
2705  *  the insert mark below 'item'.
2706  *
2707  * @exception IllegalArgumentException <ul>
2708  *    <li>ERROR_INVALID_ARGUMENT - if the item has been disposed</li>
2709  * </ul>
2710  * @exception SWTException <ul>
2711  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
2712  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
2713  * </ul>
2714  */
2715 public void setInsertMark (TreeItem item, bool before) {
2716     checkWidget ();
2717     if (item is null) {
2718         OS.gtk_tree_view_set_drag_dest_row(handle, null, OS.GTK_TREE_VIEW_DROP_BEFORE);
2719         return;
2720     }
2721     if (item.isDisposed()) error (SWT.ERROR_INVALID_ARGUMENT);
2722     if (item.parent !is this) return;
2723     Rectangle rect = item.getBounds();
2724     void* path;
2725     OS.gtk_widget_realize (handle);
2726     if (!OS.gtk_tree_view_get_path_at_pos(handle, rect.x, rect.y, &path, null, null, null)) return;
2727     if (path is null) return;
2728     int position = before ? OS.GTK_TREE_VIEW_DROP_BEFORE : OS.GTK_TREE_VIEW_DROP_AFTER;
2729     OS.gtk_tree_view_set_drag_dest_row(handle, path, position);
2730     OS.gtk_tree_path_free (path );
2731 }
2732 
2733 void setItemCount (GtkTreeIter* parentIter, int count) {
2734     int itemCount = OS.gtk_tree_model_iter_n_children (modelHandle, parentIter);
2735     if (count is itemCount) return;
2736     bool isVirtual = (style & SWT.VIRTUAL) !is 0;
2737     if (!isVirtual) setRedraw (false);
2738     remove (parentIter, count, itemCount - 1);
2739     if (isVirtual) {
2740         for (int i=itemCount; i<count; i++) {
2741             GtkTreeIter iter;
2742             OS.gtk_tree_store_append (modelHandle, &iter, parentIter);
2743             OS.gtk_tree_store_set1 (modelHandle, &iter, ID_COLUMN, cast(void*)-1);
2744         }
2745     } else {
2746         for (int i=itemCount; i<count; i++) {
2747             new TreeItem (this, parentIter, SWT.NONE, i, true);
2748         }
2749     }
2750     if (!isVirtual) setRedraw (true);
2751     modelChanged = true;
2752 }
2753 
2754 /**
2755  * Sets the number of root-level items contained in the receiver.
2756  *
2757  * @param count the number of items
2758  *
2759  * @exception SWTException <ul>
2760  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
2761  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
2762  * </ul>
2763  *
2764  * @since 3.2
2765  */
2766 public void setItemCount (int count) {
2767     checkWidget ();
2768     count = Math.max (0, count);
2769     setItemCount (null, count);
2770 }
2771 
2772 /**
2773  * Selects an item in the receiver.  If the item was already
2774  * selected, it remains selected.
2775  *
2776  * @param item the item to be selected
2777  *
2778  * @exception IllegalArgumentException <ul>
2779  *    <li>ERROR_NULL_ARGUMENT - if the item is null</li>
2780  *    <li>ERROR_INVALID_ARGUMENT - if the item has been disposed</li>
2781  * </ul>
2782  * @exception SWTException <ul>
2783  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
2784  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
2785  * </ul>
2786  *
2787  * @since 3.4
2788  */
2789 public void select (TreeItem item) {
2790     checkWidget ();
2791     if (item is null) error (SWT.ERROR_NULL_ARGUMENT);
2792     if (item.isDisposed ()) error (SWT.ERROR_INVALID_ARGUMENT);
2793     bool fixColumn = showFirstColumn ();
2794     auto selection = OS.gtk_tree_view_get_selection (handle);
2795     OS.g_signal_handlers_block_matched (selection, OS.G_SIGNAL_MATCH_DATA, 0, 0, null, null, udCHANGED);
2796     OS.gtk_tree_selection_select_iter (selection, item.handle);
2797     OS.g_signal_handlers_unblock_matched (selection, OS.G_SIGNAL_MATCH_DATA, 0, 0, null, null, udCHANGED);
2798     if (fixColumn) hideFirstColumn ();
2799 }
2800 
2801 /**
2802  * Selects all of the items in the receiver.
2803  * <p>
2804  * If the receiver is single-select, do nothing.
2805  * </p>
2806  *
2807  * @exception SWTException <ul>
2808  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
2809  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
2810  * </ul>
2811  */
2812 public void selectAll () {
2813     checkWidget();
2814     if ((style & SWT.SINGLE) !is 0) return;
2815     bool fixColumn = showFirstColumn ();
2816     auto selection = OS.gtk_tree_view_get_selection (handle);
2817     OS.g_signal_handlers_block_matched (selection, OS.G_SIGNAL_MATCH_DATA, 0, 0, null, null, udCHANGED);
2818     OS.gtk_tree_selection_select_all (selection);
2819     OS.g_signal_handlers_unblock_matched (selection, OS.G_SIGNAL_MATCH_DATA, 0, 0, null, null, udCHANGED);
2820     if (fixColumn) hideFirstColumn ();
2821 }
2822 
2823 override void setBackgroundColor (GdkColor* color) {
2824     super.setBackgroundColor (color);
2825     OS.gtk_widget_modify_base (handle, 0, color);
2826 }
2827 
2828 override void setBackgroundPixmap (GdkPixmap* pixmap) {
2829     super.setBackgroundPixmap (pixmap);
2830     auto window = paintWindow ();
2831     if (window !is null) OS.gdk_window_set_back_pixmap (window, null, true);
2832 }
2833 
2834 override int setBounds (int x, int y, int width, int height, bool move, bool resize) {
2835     int result = super.setBounds (x, y, width, height, move, resize);
2836     /*
2837     * Bug on GTK.  The tree view sometimes does not get a paint
2838     * event or resizes to a one pixel square when resized in a new
2839     * shell that is not visible after any event loop has been run.  The
2840     * problem is intermittent. It doesn't seem to happen the first time
2841     * a new shell is created. The fix is to ensure the tree view is realized
2842     * after it has been resized.
2843     */
2844     OS.gtk_widget_realize (handle);
2845     /*
2846     * Bug in GTK.  An empty GtkTreeView fails to repaint the focus rectangle
2847     * correctly when resized on versions before 2.6.0.  The fix is to force
2848     * the widget to redraw.
2849     */
2850     if (OS.GTK_VERSION < OS.buildVERSION (2, 6, 0) && OS.gtk_tree_model_iter_n_children (modelHandle, null) is 0) {
2851         redraw (false);
2852     }
2853     return result;
2854 }
2855 
2856 /**
2857  * Sets the order that the items in the receiver should
2858  * be displayed in to the given argument which is described
2859  * in terms of the zero-relative ordering of when the items
2860  * were added.
2861  *
2862  * @param order the new order to display the items
2863  *
2864  * @exception SWTException <ul>
2865  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
2866  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
2867  * </ul>
2868  * @exception IllegalArgumentException <ul>
2869  *    <li>ERROR_INVALID_ARGUMENT - if the item order is not the same length as the number of items</li>
2870  * </ul>
2871  *
2872  * @see Tree#getColumnOrder()
2873  * @see TreeColumn#getMoveable()
2874  * @see TreeColumn#setMoveable(bool)
2875  * @see SWT#Move
2876  *
2877  * @since 3.2
2878  */
2879 public void setColumnOrder (int [] order) {
2880     checkWidget ();
2881     // SWT extension: allow null for zero length string
2882     //if (order is null) error (SWT.ERROR_NULL_ARGUMENT);
2883     if (columnCount is 0) {
2884         if (order.length > 0) error (SWT.ERROR_INVALID_ARGUMENT);
2885         return;
2886     }
2887     if (order.length !is columnCount) error (SWT.ERROR_INVALID_ARGUMENT);
2888     bool [] seen = new bool [columnCount];
2889     for (int i = 0; i<order.length; i++) {
2890         int index = order [i];
2891         if (index < 0 || index >= columnCount) error (SWT.ERROR_INVALID_RANGE);
2892         if (seen [index]) error (SWT.ERROR_INVALID_ARGUMENT);
2893         seen [index] = true;
2894     }
2895     void* baseColumn;
2896     for (int i=0; i<order.length; i++) {
2897         auto column = columns [order [i]].handle;
2898         OS.gtk_tree_view_move_column_after (handle, column, baseColumn);
2899         baseColumn = column;
2900     }
2901 }
2902 
2903 override void setFontDescription (PangoFontDescription* font) {
2904     super.setFontDescription (font);
2905     TreeColumn[] columns = getColumns ();
2906     for (int i = 0; i < columns.length; i++) {
2907         if (columns[i] !is null) {
2908             columns[i].setFontDescription (font);
2909         }
2910     }
2911 }
2912 
2913 /**
2914  * Marks the receiver's header as visible if the argument is <code>true</code>,
2915  * and marks it invisible otherwise.
2916  * <p>
2917  * If one of the receiver's ancestors is not visible or some
2918  * other condition makes the receiver not visible, marking
2919  * it visible may not actually cause it to be displayed.
2920  * </p>
2921  *
2922  * @param show the new visibility state
2923  *
2924  * @exception SWTException <ul>
2925  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
2926  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
2927  * </ul>
2928  *
2929  * @since 3.1
2930  */
2931 public void setHeaderVisible (bool show) {
2932     checkWidget ();
2933     OS.gtk_tree_view_set_headers_visible (handle, show);
2934 }
2935 
2936 /**
2937  * Marks the receiver's lines as visible if the argument is <code>true</code>,
2938  * and marks it invisible otherwise.
2939  * <p>
2940  * If one of the receiver's ancestors is not visible or some
2941  * other condition makes the receiver not visible, marking
2942  * it visible may not actually cause it to be displayed.
2943  * </p>
2944  *
2945  * @param show the new visibility state
2946  *
2947  * @exception SWTException <ul>
2948  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
2949  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
2950  * </ul>
2951  *
2952  * @since 3.1
2953  */
2954 public void setLinesVisible (bool show) {
2955     checkWidget();
2956     OS.gtk_tree_view_set_rules_hint (handle, show);
2957 }
2958 
2959 override void setParentBackground () {
2960     super.setParentBackground ();
2961     auto window = paintWindow ();
2962     if (window !is null) OS.gdk_window_set_back_pixmap (window, null, true);
2963 }
2964 
2965 override void setParentWindow (GtkWidget* widget) {
2966     auto window = eventWindow ();
2967     OS.gtk_widget_set_parent_window (widget, window);
2968 }
2969 
2970 void setScrollWidth (GtkTreeViewColumn* column, TreeItem item) {
2971     if (columnCount !is 0 || currentItem is item) return;
2972     /*
2973     * Use GTK_TREE_VIEW_COLUMN_GROW_ONLY on GTK versions < 2.3.2
2974     * because fixed_height_mode is not supported.
2975     */
2976     if (((style & SWT.VIRTUAL) !is 0) && OS.GTK_VERSION < OS.buildVERSION (2, 3, 2)) return;
2977     int width = OS.gtk_tree_view_column_get_fixed_width (column);
2978     int itemWidth = calculateWidth (column, cast(GtkTreeIter*)item.handle, true);
2979     if (width < itemWidth) {
2980         OS.gtk_tree_view_column_set_fixed_width (column, itemWidth);
2981     }
2982 }
2983 
2984 /**
2985  * Sets the receiver's selection to the given item.
2986  * The current selection is cleared before the new item is selected.
2987  * <p>
2988  * If the item is not in the receiver, then it is ignored.
2989  * </p>
2990  *
2991  * @param item the item to select
2992  *
2993  * @exception IllegalArgumentException <ul>
2994  *    <li>ERROR_NULL_ARGUMENT - if the item is null</li>
2995  *    <li>ERROR_INVALID_ARGUMENT - if the item has been disposed</li>
2996  * </ul>
2997  * @exception SWTException <ul>
2998  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
2999  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
3000  * </ul>
3001  *
3002  * @since 3.2
3003  */
3004 public void setSelection (TreeItem item) {
3005     checkWidget ();
3006     if (item is null) error (SWT.ERROR_NULL_ARGUMENT);
3007     setSelection ([item]);
3008 }
3009 
3010 /**
3011  * Sets the receiver's selection to be the given array of items.
3012  * The current selection is cleared before the new items are selected.
3013  * <p>
3014  * Items that are not in the receiver are ignored.
3015  * If the receiver is single-select and multiple items are specified,
3016  * then all items are ignored.
3017  * </p>
3018  *
3019  * @param items the array of items
3020  *
3021  * @exception IllegalArgumentException <ul>
3022  *    <li>ERROR_INVALID_ARGUMENT - if one of the items has been disposed</li>
3023  * </ul>
3024  * @exception SWTException <ul>
3025  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
3026  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
3027  * </ul>
3028  *
3029  * @see Tree#deselectAll()
3030  */
3031 public void setSelection (TreeItem [] items) {
3032     checkWidget ();
3033     // SWT extension: allow null for zero length string
3034     //if (items is null) error (SWT.ERROR_NULL_ARGUMENT);
3035     deselectAll ();
3036     ptrdiff_t length = items.length;
3037     if (length is 0 || ((style & SWT.SINGLE) !is 0 && length > 1)) return;
3038     bool fixColumn = showFirstColumn ();
3039     auto selection = OS.gtk_tree_view_get_selection (handle);
3040     OS.g_signal_handlers_block_matched (selection, OS.G_SIGNAL_MATCH_DATA, 0, 0, null, null, udCHANGED);
3041     bool first = true;
3042     for (int i = 0; i < length; i++) {
3043         TreeItem item = items [i];
3044         if (item is null) continue;
3045         if (item.isDisposed ()) break;
3046         if (item.parent !is this) continue;
3047         auto path = OS.gtk_tree_model_get_path (modelHandle, item.handle);
3048         showItem (path, false);
3049         if (first) {
3050             OS.gtk_tree_view_set_cursor (handle, path, null, false);
3051         }
3052         OS.gtk_tree_selection_select_iter (selection, item.handle);
3053         OS.gtk_tree_path_free (path);
3054         first = false;
3055     }
3056     OS.g_signal_handlers_unblock_matched (selection, OS.G_SIGNAL_MATCH_DATA, 0, 0, null, null, udCHANGED);
3057     if (fixColumn) hideFirstColumn ();
3058 }
3059 
3060 /**
3061  * Sets the column used by the sort indicator for the receiver. A null
3062  * value will clear the sort indicator.  The current sort column is cleared
3063  * before the new column is set.
3064  *
3065  * @param column the column used by the sort indicator or <code>null</code>
3066  *
3067  * @exception IllegalArgumentException <ul>
3068  *    <li>ERROR_INVALID_ARGUMENT - if the column is disposed</li>
3069  * </ul>
3070  * @exception SWTException <ul>
3071  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
3072  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
3073  * </ul>
3074  *
3075  * @since 3.2
3076  */
3077 public void setSortColumn (TreeColumn column) {
3078     checkWidget ();
3079     if (column !is null && column.isDisposed ()) error (SWT.ERROR_INVALID_ARGUMENT);
3080     if (sortColumn !is null && !sortColumn.isDisposed()) {
3081         OS.gtk_tree_view_column_set_sort_indicator (sortColumn.handle, false);
3082     }
3083     sortColumn = column;
3084     if (sortColumn !is null && sortDirection !is SWT.NONE) {
3085         OS.gtk_tree_view_column_set_sort_indicator (sortColumn.handle, true);
3086         OS.gtk_tree_view_column_set_sort_order (sortColumn.handle, sortDirection is SWT.DOWN ? 0 : 1);
3087     }
3088 }
3089 
3090 /**
3091  * Sets the direction of the sort indicator for the receiver. The value
3092  * can be one of <code>UP</code>, <code>DOWN</code> or <code>NONE</code>.
3093  *
3094  * @param direction the direction of the sort indicator
3095  *
3096  * @exception SWTException <ul>
3097  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
3098  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
3099  * </ul>
3100  *
3101  * @since 3.2
3102  */
3103 public void setSortDirection  (int direction) {
3104     checkWidget ();
3105     if (direction !is SWT.UP && direction !is SWT.DOWN && direction !is SWT.NONE) return;
3106     sortDirection = direction;
3107     if (sortColumn is null || sortColumn.isDisposed ()) return;
3108     if (sortDirection is SWT.NONE) {
3109         OS.gtk_tree_view_column_set_sort_indicator (sortColumn.handle, false);
3110     } else {
3111         OS.gtk_tree_view_column_set_sort_indicator (sortColumn.handle, true);
3112         OS.gtk_tree_view_column_set_sort_order (sortColumn.handle, sortDirection is SWT.DOWN ? 0 : 1);
3113     }
3114 }
3115 
3116 /**
3117  * Sets the item which is currently at the top of the receiver.
3118  * This item can change when items are expanded, collapsed, scrolled
3119  * or new items are added or removed.
3120  *
3121  * @param item the item to be shown
3122  *
3123  * @exception IllegalArgumentException <ul>
3124  *    <li>ERROR_NULL_ARGUMENT - if the item is null</li>
3125  *    <li>ERROR_INVALID_ARGUMENT - if the item has been disposed</li>
3126  * </ul>
3127  * @exception SWTException <ul>
3128  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
3129  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
3130  * </ul>
3131  *
3132  * @see Tree#getTopItem()
3133  *
3134  * @since 2.1
3135  */
3136 public void setTopItem (TreeItem item) {
3137     if (item is null) error (SWT.ERROR_NULL_ARGUMENT);
3138     if (item.isDisposed ()) error(SWT.ERROR_INVALID_ARGUMENT);
3139     if (item.parent !is this) return;
3140     auto path = OS.gtk_tree_model_get_path (modelHandle, item.handle);
3141     showItem (path, false);
3142     OS.gtk_tree_view_scroll_to_cell (handle, path, null, true, 0f, 0f);
3143     if (OS.GTK_VERSION < OS.buildVERSION (2, 8, 0)) {
3144         /*
3145         * Bug in GTK.  According to the documentation, gtk_tree_view_scroll_to_cell
3146         * should vertically scroll the cell to the top if use_align is true and row_align is 0.
3147         * However, prior to version 2.8 it does not scroll at all.  The fix is to determine
3148         * the new location and use gtk_tree_view_scroll_to_point.
3149         * If the widget is a pinhead, calling gtk_tree_view_scroll_to_point
3150         * will have no effect. Therefore, it is still neccessary to call
3151         * gtk_tree_view_scroll_to_cell.
3152         */
3153         OS.gtk_widget_realize (handle);
3154         GdkRectangle cellRect;
3155         OS.gtk_tree_view_get_cell_area (handle, path, null, &cellRect);
3156         int tx, ty;
3157         OS.gtk_tree_view_widget_to_tree_coords(handle, cellRect.x, cellRect.y, &tx, &ty);
3158         OS.gtk_tree_view_scroll_to_point (handle, -1, ty);
3159     }
3160     OS.gtk_tree_path_free (path);
3161 }
3162 
3163 /**
3164  * Shows the column.  If the column is already showing in the receiver,
3165  * this method simply returns.  Otherwise, the columns are scrolled until
3166  * the column is visible.
3167  *
3168  * @param column the column to be shown
3169  *
3170  * @exception IllegalArgumentException <ul>
3171  *    <li>ERROR_NULL_ARGUMENT - if the item is null</li>
3172  *    <li>ERROR_INVALID_ARGUMENT - if the item has been disposed</li>
3173  * </ul>
3174  * @exception SWTException <ul>
3175  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
3176  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
3177  * </ul>
3178  *
3179  * @since 3.1
3180  */
3181 public void showColumn (TreeColumn column) {
3182     checkWidget ();
3183     if (column is null) error (SWT.ERROR_NULL_ARGUMENT);
3184     if (column.isDisposed()) error(SWT.ERROR_INVALID_ARGUMENT);
3185     if (column.parent !is this) return;
3186     /*
3187     * This code is intentionally commented. According to the
3188     * documentation, gtk_tree_view_scroll_to_cell should scroll the
3189     * minimum amount to show the column but instead it scrolls strangely.
3190     */
3191     //OS.gtk_tree_view_scroll_to_cell (handle, 0, column.handle, false, 0, 0);
3192     OS.gtk_widget_realize (handle);
3193     GdkRectangle cellRect;
3194     OS.gtk_tree_view_get_cell_area (handle, null, column.handle, &cellRect);
3195     GdkRectangle visibleRect;
3196     OS.gtk_tree_view_get_visible_rect (handle, &visibleRect);
3197     if (cellRect.x < visibleRect.x) {
3198         OS.gtk_tree_view_scroll_to_point (handle, cellRect.x, -1);
3199     } else {
3200         int width = Math.min (visibleRect.width, cellRect.width);
3201         if (cellRect.x + width > visibleRect.x + visibleRect.width) {
3202             int tree_x = cellRect.x + width - visibleRect.width;
3203             OS.gtk_tree_view_scroll_to_point (handle, tree_x, -1);
3204         }
3205     }
3206 }
3207 
3208 bool showFirstColumn () {
3209     /*
3210     * Bug in GTK.  If no columns are visible, changing the selection
3211     * will fail.  The fix is to temporarily make a column visible.
3212     */
3213     int columnCount = Math.max (1, this.columnCount);
3214     for (int i=0; i<columnCount; i++) {
3215         auto column = OS.gtk_tree_view_get_column (handle, i);
3216         if (OS.gtk_tree_view_column_get_visible (column)) return false;
3217     }
3218     auto firstColumn = OS.gtk_tree_view_get_column (handle, 0);
3219     OS.gtk_tree_view_column_set_visible (firstColumn, true);
3220     return true;
3221 }
3222 
3223 /**
3224  * Shows the selection.  If the selection is already showing in the receiver,
3225  * this method simply returns.  Otherwise, the items are scrolled until
3226  * the selection is visible.
3227  *
3228  * @exception SWTException <ul>
3229  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
3230  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
3231  * </ul>
3232  *
3233  * @see Tree#showItem(TreeItem)
3234  */
3235 public void showSelection () {
3236     checkWidget();
3237     TreeItem [] items = getSelection ();
3238     if (items.length !is 0 && items [0] !is null) showItem (items [0]);
3239 }
3240 
3241 void showItem (GtkTreePath* path, bool scroll) {
3242     int depth = OS.gtk_tree_path_get_depth (path);
3243     if (depth > 1) {
3244         auto indicesPtr = OS.gtk_tree_path_get_indices (path);
3245         int [] indices = indicesPtr[ 0 .. depth - 1];
3246         auto tempPath = OS.gtk_tree_path_new ();
3247         for (int i=0; i<indices.length; i++) {
3248             OS.gtk_tree_path_append_index (tempPath, indices [i]);
3249             OS.gtk_tree_view_expand_row (handle, tempPath, false);
3250         }
3251         OS.gtk_tree_path_free (tempPath);
3252     }
3253     if (scroll) {
3254         OS.gtk_widget_realize (handle);
3255         GdkRectangle cellRect;
3256         OS.gtk_tree_view_get_cell_area (handle, path, null, &cellRect);
3257         bool isHidden = cellRect.y is 0 && cellRect.height is 0;
3258         int tx, ty;
3259         OS.gtk_tree_view_widget_to_tree_coords (handle, cellRect.x, cellRect.y, &tx, &ty);
3260         GdkRectangle visibleRect;
3261         OS.gtk_tree_view_get_visible_rect (handle, &visibleRect);
3262         if (!isHidden) {
3263             if (ty < visibleRect.y || ty + cellRect.height > visibleRect.y + visibleRect.height) {
3264                 isHidden = true;
3265             }
3266         }
3267         if (isHidden) {
3268             /*
3269             * This code intentionally commented.
3270             * Bug in GTK.  According to the documentation, gtk_tree_view_scroll_to_cell
3271             * should scroll the minimum amount to show the cell if use_align is false.
3272             * However, what actually happens is the cell is scrolled to the top.
3273             * The fix is to determine the new location and use gtk_tree_view_scroll_to_point.
3274             * If the widget is a pinhead, calling gtk_tree_view_scroll_to_point
3275             * will have no effect. Therefore, it is still neccessary to
3276             * call gtk_tree_view_scroll_to_cell.
3277             */
3278             //  OS.gtk_tree_view_scroll_to_cell (handle, path, 0, depth !is 1, 0.5f, 0.0f);
3279             if (depth !is 1) {
3280                 OS.gtk_tree_view_scroll_to_cell (handle, path, null, true, 0.5f, 0.0f);
3281             } else {
3282                 if (ty < visibleRect.y ) {
3283                     OS.gtk_tree_view_scroll_to_point (handle, -1, ty);
3284                 } else {
3285                     int height = Math.min (visibleRect.height, cellRect.height);
3286                     if (ty + height > visibleRect.y + visibleRect.height) {
3287                         OS.gtk_tree_view_scroll_to_point (handle, -1, ty + cellRect.height - visibleRect.height);
3288                     }
3289                 }
3290             }
3291         }
3292     }
3293 }
3294 
3295 /**
3296  * Shows the item.  If the item is already showing in the receiver,
3297  * this method simply returns.  Otherwise, the items are scrolled
3298  * and expanded until the item is visible.
3299  *
3300  * @param item the item to be shown
3301  *
3302  * @exception IllegalArgumentException <ul>
3303  *    <li>ERROR_NULL_ARGUMENT - if the item is null</li>
3304  *    <li>ERROR_INVALID_ARGUMENT - if the item has been disposed</li>
3305  * </ul>
3306  * @exception SWTException <ul>
3307  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
3308  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
3309  * </ul>
3310  *
3311  * @see Tree#showSelection()
3312  */
3313 public void showItem (TreeItem item) {
3314     checkWidget ();
3315     if (item is null) error (SWT.ERROR_NULL_ARGUMENT);
3316     if (item.isDisposed ()) error(SWT.ERROR_INVALID_ARGUMENT);
3317     if (item.parent !is this) return;
3318     auto path = OS.gtk_tree_model_get_path (modelHandle, item.handle);
3319     showItem (path, true);
3320     OS.gtk_tree_path_free (path);
3321 }
3322 
3323 override void treeSelectionProc (
3324     GtkTreeModel *model,
3325     GtkTreePath *path,
3326     GtkTreeIter *iter,
3327     int[] selection,
3328     int length_)
3329 {
3330     if (selection !is null) {
3331         int index;
3332         OS.gtk_tree_model_get1 (modelHandle, iter, ID_COLUMN, cast(void**)&index);
3333         selection [length_] = index;
3334     }
3335 }
3336 
3337 override void updateScrollBarValue (ScrollBar bar) {
3338     super.updateScrollBarValue (bar);
3339     /*
3340     * Bug in GTK. Scrolling changes the XWindow position
3341     * and makes the child widgets appear to scroll even
3342     * though when queried their position is unchanged.
3343     * The fix is to queue a resize event for each child to
3344     * force the position to be corrected.
3345     */
3346     auto parentHandle = parentingHandle ();
3347     auto list = OS.gtk_container_get_children (parentHandle);
3348     if (list is null) return;
3349     auto temp = list;
3350     while (temp !is null) {
3351         auto widget = OS.g_list_data (temp);
3352         if (widget !is null) OS.gtk_widget_queue_resize  (widget);
3353         temp = OS.g_list_next (temp);
3354     }
3355     OS.g_list_free (list);
3356 }
3357 
3358 }