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