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.List;
14 
15 
16 import org.eclipse.swt.SWT;
17 import org.eclipse.swt.internal.gtk.OS;
18 import org.eclipse.swt.graphics.Point;
19 import org.eclipse.swt.graphics.Rectangle;
20 import org.eclipse.swt.events.SelectionListener;
21 import org.eclipse.swt.events.SelectionEvent;
22 import org.eclipse.swt.widgets.Scrollable;
23 import org.eclipse.swt.widgets.Composite;
24 import org.eclipse.swt.widgets.TypedListener;
25 import org.eclipse.swt.widgets.Display;
26 import java.lang.all;
27 
28 /**
29  * Instances of this class represent a selectable user interface
30  * object that displays a list of strings and issues notification
31  * when a string is selected.  A list may be single or multi select.
32  * <p>
33  * <dl>
34  * <dt><b>Styles:</b></dt>
35  * <dd>SINGLE, MULTI</dd>
36  * <dt><b>Events:</b></dt>
37  * <dd>Selection, DefaultSelection</dd>
38  * </dl>
39  * <p>
40  * Note: Only one of SINGLE and MULTI may be specified.
41  * </p><p>
42  * IMPORTANT: This class is <em>not</em> intended to be subclassed.
43  * </p>
44  *
45  * @see <a href="http://www.eclipse.org/swt/snippets/#list">List snippets</a>
46  * @see <a href="http://www.eclipse.org/swt/examples.php">SWT Example: ControlExample</a>
47  * @see <a href="http://www.eclipse.org/swt/">Sample code and further information</a>
48  */
49 public class List : Scrollable {
50 
51     alias Scrollable.computeSize computeSize;
52     alias Scrollable.dragDetect dragDetect;
53     alias Scrollable.setBackgroundColor setBackgroundColor;
54     alias Scrollable.setBounds setBounds;
55 
56     GtkWidget* modelHandle;
57 
58     static const int TEXT_COLUMN = 0;
59     CallbackData treeSelectionProcCallbackData;
60 
61 /**
62  * Constructs a new instance of this class given its parent
63  * and a style value describing its behavior and appearance.
64  * <p>
65  * The style value is either one of the style constants defined in
66  * class <code>SWT</code> which is applicable to instances of this
67  * class, or must be built by <em>bitwise OR</em>'ing together
68  * (that is, using the <code>int</code> "|" operator) two or more
69  * of those <code>SWT</code> style constants. The class description
70  * lists the style constants that are applicable to the class.
71  * Style bits are also inherited from superclasses.
72  * </p>
73  *
74  * @param parent a composite control which will be the parent of the new instance (cannot be null)
75  * @param style the style of control to construct
76  *
77  * @exception IllegalArgumentException <ul>
78  *    <li>ERROR_NULL_ARGUMENT - if the parent is null</li>
79  * </ul>
80  * @exception SWTException <ul>
81  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li>
82  *    <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li>
83  * </ul>
84  *
85  * @see SWT#SINGLE
86  * @see SWT#MULTI
87  * @see Widget#checkSubclass
88  * @see Widget#getStyle
89  */
90 public this (Composite parent, int style) {
91     super (parent, checkStyle (style));
92 }
93 
94 /**
95  * Adds the argument to the end of the receiver's list.
96  *
97  * @param string the new item
98  *
99  * @exception SWTException <ul>
100  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
101  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
102  * </ul>
103  *
104  * @see #add(String,int)
105  */
106 public void add (String string) {
107     checkWidget();
108     // SWT extension: allow null for zero length string
109     //if (string is null) error (SWT.ERROR_NULL_ARGUMENT);
110     char* buffer = string.toStringzValidPtr();
111     GtkTreeIter iter;
112     OS.gtk_list_store_append (cast(GtkListStore*)modelHandle, &iter);
113     OS.gtk_list_store_set1 (cast(GtkListStore*)modelHandle, &iter, TEXT_COLUMN, buffer);
114 }
115 
116 /**
117  * Adds the argument to the receiver's list at the given
118  * zero-relative index.
119  * <p>
120  * Note: To add an item at the end of the list, use the
121  * result of calling <code>getItemCount()</code> as the
122  * index or use <code>add(String)</code>.
123  * </p>
124  *
125  * @param string the new item
126  * @param index the index for the item
127  *
128  * @exception IllegalArgumentException <ul>
129  *    <li>ERROR_INVALID_RANGE - if the index is not between 0 and the number of elements in the list (inclusive)</li>
130  * </ul>
131  * @exception SWTException <ul>
132  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
133  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
134  * </ul>
135  *
136  * @see #add(String)
137  */
138 public void add (String string, int index) {
139     checkWidget();
140     // SWT extension: allow null for zero length string
141     //if (string is null) error (SWT.ERROR_NULL_ARGUMENT);
142     int count = OS.gtk_tree_model_iter_n_children (cast(GtkTreeStore*)modelHandle, null);
143     if (!(0 <= index && index <= count)) {
144         error (SWT.ERROR_INVALID_RANGE);
145     }
146     char* buffer = string.toStringzValidPtr();
147     GtkTreeIter iter;
148     /*
149     * Feature in GTK.  It is much faster to append to a list store
150     * than to insert at the end using gtk_list_store_insert().
151     */
152     if (index is count) {
153         OS.gtk_list_store_append (cast(GtkListStore*)modelHandle, &iter);
154     } else {
155         OS.gtk_list_store_insert (cast(GtkListStore*)modelHandle, &iter, index);
156     }
157     OS.gtk_list_store_set1 (cast(GtkListStore*)modelHandle, &iter, TEXT_COLUMN, buffer);
158 }
159 
160 /**
161  * Adds the listener to the collection of listeners who will
162  * be notified when the user changes the receiver's selection, by sending
163  * it one of the messages defined in the <code>SelectionListener</code>
164  * interface.
165  * <p>
166  * <code>widgetSelected</code> is called when the selection changes.
167  * <code>widgetDefaultSelected</code> is typically called when an item is double-clicked.
168  * </p>
169  *
170  * @param listener the listener which should be notified when the user changes the receiver's selection
171  *
172  * @exception IllegalArgumentException <ul>
173  *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
174  * </ul>
175  * @exception SWTException <ul>
176  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
177  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
178  * </ul>
179  *
180  * @see SelectionListener
181  * @see #removeSelectionListener
182  * @see SelectionEvent
183  */
184 public void addSelectionListener(SelectionListener listener) {
185     checkWidget();
186     if (listener is null) error (SWT.ERROR_NULL_ARGUMENT);
187     TypedListener typedListener = new TypedListener (listener);
188     addListener (SWT.Selection,typedListener);
189     addListener (SWT.DefaultSelection,typedListener);
190 }
191 
192 static int checkStyle (int style) {
193     return checkBits (style, SWT.SINGLE, SWT.MULTI, 0, 0, 0, 0);
194 }
195 
196 override void createHandle (int index) {
197     state |= HANDLE;
198     fixedHandle = cast(GtkWidget*)OS.g_object_new (display.gtk_fixed_get_type (), null);
199     if (fixedHandle is null) error (SWT.ERROR_NO_HANDLES);
200     OS.gtk_fixed_set_has_window (cast(GtkFixed*)fixedHandle, true);
201     scrolledHandle = cast(GtkWidget*)OS.gtk_scrolled_window_new (null, null);
202     if (scrolledHandle is null) error (SWT.ERROR_NO_HANDLES);
203     /*
204     * Columns:
205     * 0 - text
206     */
207     size_t[] types = [OS.G_TYPE_STRING ()];
208     modelHandle = cast(GtkWidget*)OS.gtk_list_store_newv (cast(int)/*64bit*/types.length, types.ptr);
209     if (modelHandle is null) error (SWT.ERROR_NO_HANDLES);
210     handle = OS.gtk_tree_view_new_with_model (modelHandle);
211     if (handle is null) error (SWT.ERROR_NO_HANDLES);
212     auto textRenderer = OS.gtk_cell_renderer_text_new ();
213     if (textRenderer is null) error (SWT.ERROR_NO_HANDLES);
214     auto columnHandle = OS.gtk_tree_view_column_new ();
215     if (columnHandle is null) error (SWT.ERROR_NO_HANDLES);
216     OS.gtk_tree_view_column_pack_start (cast(GtkTreeViewColumn*)columnHandle, textRenderer, true);
217     OS.gtk_tree_view_column_add_attribute (cast(GtkTreeViewColumn*)columnHandle, textRenderer, OS.text.ptr, TEXT_COLUMN);
218     OS.gtk_tree_view_insert_column (cast(GtkTreeView*)handle, columnHandle, index);
219     OS.gtk_container_add (cast(GtkContainer*)fixedHandle, scrolledHandle);
220     OS.gtk_container_add (cast(GtkContainer*)scrolledHandle, handle);
221 
222     int mode = (style & SWT.MULTI) !is 0 ? OS.GTK_SELECTION_MULTIPLE : OS.GTK_SELECTION_BROWSE;
223     auto selectionHandle = OS.gtk_tree_view_get_selection (cast(GtkTreeView*)handle);
224     OS.gtk_tree_selection_set_mode (selectionHandle, mode);
225     OS.gtk_tree_view_set_headers_visible (cast(GtkTreeView*)handle, false);
226     int hsp = (style & SWT.H_SCROLL) !is 0 ? OS.GTK_POLICY_AUTOMATIC : OS.GTK_POLICY_NEVER;
227     int vsp = (style & SWT.V_SCROLL) !is 0 ? OS.GTK_POLICY_AUTOMATIC : OS.GTK_POLICY_NEVER;
228     OS.gtk_scrolled_window_set_policy (cast(GtkScrolledWindow*)scrolledHandle, hsp, vsp);
229     if ((style & SWT.BORDER) !is 0) OS.gtk_scrolled_window_set_shadow_type (cast(GtkScrolledWindow*)scrolledHandle, OS.GTK_SHADOW_ETCHED_IN);
230     /*
231     * Bug in GTK. When a treeview is the child of an override shell,
232     * and if the user has ever invokes the interactive search field,
233     * and the treeview is disposed on a focus out event, it segment
234     * faults. The fix is to disable the search field in an override
235     * shell.
236     */
237     if ((getShell ().style & SWT.ON_TOP) !is 0) {
238         /*
239         * Bug in GTK. Until GTK 2.6.5, calling gtk_tree_view_set_enable_search(FALSE)
240         * would prevent the user from being able to type in text to search the tree.
241         * After 2.6.5, GTK introduced Ctrl+F as being the key binding for interactive
242         * search. This meant that even if FALSE was passed to enable_search, the user
243         * can still bring up the search pop up using the keybinding. GTK also introduced
244         * the notion of passing a -1 to gtk_set_search_column to disable searching
245         * (including the search key binding).  The fix is to use the right calls
246         * for the right version.
247         */
248         if (OS.GTK_VERSION >= OS.buildVERSION (2, 6, 5)) {
249             OS.gtk_tree_view_set_search_column (cast(GtkTreeView*)handle, -1);
250         } else {
251             OS.gtk_tree_view_set_enable_search (cast(GtkTreeView*)handle, false);
252         }
253     }
254 }
255 
256 public override Point computeSize (int wHint, int hHint, bool changed) {
257     checkWidget ();
258     if (wHint !is SWT.DEFAULT && wHint < 0) wHint = 0;
259     if (hHint !is SWT.DEFAULT && hHint < 0) hHint = 0;
260     Point size = computeNativeSize (handle, wHint, hHint, changed);
261     Rectangle trim = computeTrim (0, 0, size.x, size.y);
262     size.x = trim.width;
263     size.y = trim.height;
264     return size;
265 }
266 
267 override void deregister() {
268     super.deregister ();
269     display.removeWidget (cast(GtkWidget*)OS.gtk_tree_view_get_selection (cast(GtkTreeView*)handle));
270 }
271 
272 /**
273  * Deselects the item at the given zero-relative index in the receiver.
274  * If the item at the index was already deselected, it remains
275  * deselected. Indices that are out of range are ignored.
276  *
277  * @param index the index of the item to deselect
278  *
279  * @exception SWTException <ul>
280  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
281  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
282  * </ul>
283  */
284 public void deselect (int index) {
285     checkWidget();
286     if (!(0 <= index && index < OS.gtk_tree_model_iter_n_children (cast(GtkTreeStore*)modelHandle, null)))  return;
287     GtkTreeIter iter;
288     auto selection = OS.gtk_tree_view_get_selection (cast(GtkTreeView*)handle);
289     OS.g_signal_handlers_block_matched (selection, OS.G_SIGNAL_MATCH_DATA, 0, 0, null, null, udCHANGED);
290     OS.gtk_tree_model_iter_nth_child (cast(GtkTreeStore*)modelHandle, &iter, null, index);
291     OS.gtk_tree_selection_unselect_iter (selection, &iter);
292     OS.g_signal_handlers_unblock_matched (selection, OS.G_SIGNAL_MATCH_DATA, 0, 0, null, null, udCHANGED);
293 }
294 
295 /**
296  * Deselects the items at the given zero-relative indices in the receiver.
297  * If the item at the given zero-relative index in the receiver
298  * is selected, it is deselected.  If the item at the index
299  * was not selected, it remains deselected.  The range of the
300  * indices is inclusive. Indices that are out of range are ignored.
301  *
302  * @param start the start index of the items to deselect
303  * @param end the end index of the items to deselect
304  *
305  * @exception SWTException <ul>
306  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
307  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
308  * </ul>
309  */
310 public void deselect (int start, int end) {
311     checkWidget();
312     if (start < 0 && end < 0) return;
313     int count = OS.gtk_tree_model_iter_n_children (cast(GtkTreeStore*)modelHandle, null);
314     if (start >= count && end >= count) return;
315     start = Math.min (count - 1, Math.max (0, start));
316     end = Math.min (count - 1, Math.max (0, end));
317     GtkTreeIter iter;
318     auto selection = OS.gtk_tree_view_get_selection (cast(GtkTreeView*)handle);
319     OS.g_signal_handlers_block_matched (selection, OS.G_SIGNAL_MATCH_DATA, 0, 0, null, null, udCHANGED);
320     for (int index=start; index<=end; index++) {
321         OS.gtk_tree_model_iter_nth_child (cast(GtkTreeStore*)modelHandle, &iter, null, index);
322         OS.gtk_tree_selection_unselect_iter (selection, &iter);
323     }
324     OS.g_signal_handlers_unblock_matched (selection, OS.G_SIGNAL_MATCH_DATA, 0, 0, null, null, udCHANGED);
325 }
326 
327 /**
328  * Deselects the items at the given zero-relative indices in the receiver.
329  * If the item at the given zero-relative index in the receiver
330  * is selected, it is deselected.  If the item at the index
331  * was not selected, it remains deselected. Indices that are out
332  * of range and duplicate indices are ignored.
333  *
334  * @param indices the array of indices for the items to deselect
335  *
336  * @exception SWTException <ul>
337  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
338  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
339  * </ul>
340  */
341 public void deselect (int [] indices) {
342     checkWidget();
343     // SWT extension: allow null for zero length string
344     //if (indices is null) error (SWT.ERROR_NULL_ARGUMENT);
345     GtkTreeIter iter;
346     int count = OS.gtk_tree_model_iter_n_children (cast(GtkTreeStore*)modelHandle, null);
347     auto selection = OS.gtk_tree_view_get_selection (cast(GtkTreeView*)handle);
348     OS.g_signal_handlers_block_matched (selection, OS.G_SIGNAL_MATCH_DATA, 0, 0, null, null, udCHANGED);
349     for (int i=0; i<indices.length; i++) {
350         int index = indices [i];
351         if (index < 0 || index > count - 1) continue;
352         OS.gtk_tree_model_iter_nth_child (cast(GtkTreeStore*)modelHandle, &iter, null, index);
353         OS.gtk_tree_selection_unselect_iter (selection, &iter);
354     }
355     OS.g_signal_handlers_unblock_matched (selection, OS.G_SIGNAL_MATCH_DATA, 0, 0, null, null, udCHANGED);
356 }
357 
358 /**
359  * Deselects all selected items in the receiver.
360  *
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 public void deselectAll () {
367     checkWidget();
368     auto selection = OS.gtk_tree_view_get_selection (cast(GtkTreeView*)handle);
369     OS.g_signal_handlers_block_matched (selection, OS.G_SIGNAL_MATCH_DATA, 0, 0, null, null, udCHANGED);
370     OS.gtk_tree_selection_unselect_all (selection);
371     OS.g_signal_handlers_unblock_matched (selection, OS.G_SIGNAL_MATCH_DATA, 0, 0, null, null, udCHANGED);
372 }
373 
374 override bool dragDetect (int x, int y, bool filter, bool* consume) {
375     bool selected = false;
376     if (filter) {
377         GtkTreePath* path;
378         if (OS.gtk_tree_view_get_path_at_pos (cast(GtkTreeView*)handle, x, y, &path, null, null, null)) {
379             if (path !is null) {
380                 auto selection = OS.gtk_tree_view_get_selection (cast(GtkTreeView*)handle);
381                 if (OS.gtk_tree_selection_path_is_selected (selection, path)) selected = true;
382                 OS.gtk_tree_path_free (path);
383             }
384         } else {
385             return false;
386         }
387     }
388     bool dragDetect = super.dragDetect (x, y, filter, consume);
389     if (dragDetect && selected && consume !is null) *consume = true;
390     return dragDetect;
391 }
392 
393 override GdkDrawable* eventWindow () {
394     return paintWindow ();
395 }
396 
397 override GdkColor* getBackgroundColor () {
398     return getBaseColor ();
399 }
400 
401 /**
402  * Returns the zero-relative index of the item which currently
403  * has the focus in the receiver, or -1 if no item has focus.
404  *
405  * @return the index of the selected item
406  *
407  * @exception SWTException <ul>
408  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
409  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
410  * </ul>
411  */
412 public int getFocusIndex () {
413     checkWidget();
414     GtkTreePath * path;
415     OS.gtk_tree_view_get_cursor (cast(GtkTreeView*)handle, &path, null);
416     if (path is null) return -1;
417     int* indices = OS.gtk_tree_path_get_indices (path);
418     int index;
419     if (indices !is null) index = indices[0];
420     OS.gtk_tree_path_free (path);
421     return index;
422 }
423 
424 override
425 GdkColor* getForegroundColor () {
426     return getTextColor ();
427 }
428 
429 /**
430  * Returns the item at the given, zero-relative index in the
431  * receiver. Throws an exception if the index is out of range.
432  *
433  * @param index the index of the item to return
434  * @return the item at the given index
435  *
436  * @exception IllegalArgumentException <ul>
437  *    <li>ERROR_INVALID_RANGE - if the index is not between 0 and the number of elements in the list minus 1 (inclusive)</li>
438  * </ul>
439  * @exception SWTException <ul>
440  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
441  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
442  * </ul>
443  */
444 public String getItem (int index) {
445     checkWidget();
446     if (!(0 <= index && index < OS.gtk_tree_model_iter_n_children (cast(GtkTreeStore*)modelHandle, null)))  {
447         error (SWT.ERROR_INVALID_RANGE);
448     }
449     char* ptr;
450     GtkTreeIter iter;
451     OS.gtk_tree_model_iter_nth_child (cast(GtkTreeStore*)modelHandle, &iter, null, index);
452     OS.gtk_tree_model_get1 (cast(GtkTreeStore*)modelHandle, &iter, 0, cast(void**)&ptr );
453     if (ptr is null) return null;
454     String res = fromStringz( ptr )._idup();
455     OS.g_free (ptr);
456     return res;
457 }
458 
459 /**
460  * Returns the number of items contained in the receiver.
461  *
462  * @return the number of items
463  *
464  * @exception SWTException <ul>
465  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
466  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
467  * </ul>
468  */
469 public int getItemCount () {
470     checkWidget();
471     return OS.gtk_tree_model_iter_n_children (cast(GtkTreeStore*)modelHandle, null);
472 }
473 
474 /**
475  * Returns the height of the area which would be used to
476  * display <em>one</em> of the items in the list.
477  *
478  * @return the height of one item
479  *
480  * @exception SWTException <ul>
481  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
482  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
483  * </ul>
484  */
485 public int getItemHeight () {
486     checkWidget();
487     int itemCount = OS.gtk_tree_model_iter_n_children (cast(GtkTreeStore*)modelHandle, null);
488     auto column = OS.gtk_tree_view_get_column (cast(GtkTreeView*)handle, 0);
489     if (itemCount is 0) {
490         int w, h;
491         OS.gtk_tree_view_column_cell_get_size (cast(GtkTreeViewColumn*)column, null, null, null, &w, &h);
492         return h;
493     } else {
494         GtkTreeIter iter;
495         OS.gtk_tree_model_get_iter_first (cast(GtkTreeStore*)modelHandle, &iter);
496         OS.gtk_tree_view_column_cell_set_cell_data (cast(GtkTreeViewColumn*)column, modelHandle, &iter, false, false);
497         int w, h;
498         OS.gtk_tree_view_column_cell_get_size (cast(GtkTreeViewColumn*)column, null, null, null, &w, &h);
499         return h;
500     }
501 }
502 
503 /**
504  * Returns a (possibly empty) array of <code>String</code>s which
505  * are the items in the receiver.
506  * <p>
507  * Note: This is not the actual structure used by the receiver
508  * to maintain its list of items, so modifying the array will
509  * not affect the receiver.
510  * </p>
511  *
512  * @return the items in the receiver's list
513  *
514  * @exception SWTException <ul>
515  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
516  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
517  * </ul>
518  */
519 public String [] getItems () {
520     checkWidget();
521     int count = OS.gtk_tree_model_iter_n_children (cast(GtkTreeStore*)modelHandle, null);
522     char* ptr;
523     String [] result = new String[]( count );
524     GtkTreeIter iter;
525     for (int index=0; index<count; index++) {
526         OS.gtk_tree_model_iter_nth_child (cast(GtkTreeStore*)modelHandle, &iter, null, index);
527         OS.gtk_tree_model_get1 (cast(GtkTreeStore*)modelHandle, &iter, 0, cast(void**)&ptr);
528         if (ptr !is null) {
529             String res = fromStringz( ptr )._idup();
530             OS.g_free (ptr);
531             result [index] = res;
532         }
533     }
534     return result;
535 }
536 
537 /**
538  * Returns an array of <code>String</code>s that are currently
539  * selected in the receiver.  The order of the items is unspecified.
540  * An empty array indicates that no items are selected.
541  * <p>
542  * Note: This is not the actual structure used by the receiver
543  * to maintain its selection, so modifying the array will
544  * not affect the receiver.
545  * </p>
546  * @return an array representing the selection
547  *
548  * @exception SWTException <ul>
549  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
550  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
551  * </ul>
552  */
553 public String [] getSelection () {
554     checkWidget();
555     int [] indices = getSelectionIndices ();
556     String [] result = new String[](indices.length);
557     for (int i=0; i<indices.length; i++) {
558         result [i] = getItem (indices [i]);
559     }
560     return result;
561 }
562 
563 /**
564  * Returns the number of selected items contained in the receiver.
565  *
566  * @return the number of selected items
567  *
568  * @exception SWTException <ul>
569  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
570  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
571  * </ul>
572  */
573 public int getSelectionCount () {
574     checkWidget();
575     auto selection = OS.gtk_tree_view_get_selection (cast(GtkTreeView*)handle);
576     if (OS.GTK_VERSION < OS.buildVERSION (2, 2, 0)) {
577         display.treeSelectionLength = 0;
578         display.treeSelection = null;
579         display.doTreeSelectionProcConnect( &treeSelectionProcCallbackData, handle, selection );
580         return display.treeSelectionLength;
581     }
582     return OS.gtk_tree_selection_count_selected_rows (selection);
583 }
584 
585 /**
586  * Returns the zero-relative index of the item which is currently
587  * selected in the receiver, or -1 if no item is selected.
588  *
589  * @return the index of the selected item or -1
590  *
591  * @exception SWTException <ul>
592  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
593  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
594  * </ul>
595  */
596 public int getSelectionIndex () {
597     checkWidget();
598     auto selection = OS.gtk_tree_view_get_selection (cast(GtkTreeView*)handle);
599     if (OS.GTK_VERSION < OS.buildVERSION (2, 2, 0)) {
600         int itemCount = OS.gtk_tree_model_iter_n_children (cast(GtkTreeStore*)modelHandle, null);
601         display.treeSelectionLength  = 0;
602         display.treeSelection = new int [itemCount];
603         display.doTreeSelectionProcConnect( &treeSelectionProcCallbackData, handle, selection );
604         if (display.treeSelectionLength is 0) return -1;
605         return display.treeSelection [0];
606     }
607     /*
608     * Bug in GTK.  gtk_tree_selection_get_selected_rows() segmentation faults
609     * in versions smaller than 2.2.4 if the model is NULL.  The fix is
610     * to give a valid pointer instead.
611     */
612     int dummy;
613     int* model = OS.GTK_VERSION < OS.buildVERSION (2, 2, 4) ? &dummy : null;
614     auto list = OS.gtk_tree_selection_get_selected_rows (selection, cast(void**)model);
615     if (list !is null) {
616         int count = OS.g_list_length (list);
617         int index;
618         for (int i=0; i<count; i++) {
619             auto data = OS.g_list_nth_data (list, i);
620             auto indices = OS.gtk_tree_path_get_indices (data);
621             if (indices !is null) {
622                 index = indices[0];
623                 break;
624             }
625         }
626         OS.g_list_free (list);
627         return index;
628     }
629     return -1;
630 }
631 
632 /**
633  * Returns the zero-relative indices of the items which are currently
634  * selected in the receiver.  The order of the indices is unspecified.
635  * The array is empty if no items are selected.
636  * <p>
637  * Note: This is not the actual structure used by the receiver
638  * to maintain its selection, so modifying the array will
639  * not affect the receiver.
640  * </p>
641  * @return the array of indices of the selected items
642  *
643  * @exception SWTException <ul>
644  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
645  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
646  * </ul>
647  */
648 public int [] getSelectionIndices () {
649     checkWidget();
650     auto selection = OS.gtk_tree_view_get_selection (cast(GtkTreeView*)handle);
651     if (OS.GTK_VERSION < OS.buildVERSION (2, 2, 0)) {
652         int itemCount = OS.gtk_tree_model_iter_n_children (cast(GtkTreeStore*)modelHandle, null);
653         display.treeSelectionLength  = 0;
654         display.treeSelection = new int [itemCount];
655         display.doTreeSelectionProcConnect( &treeSelectionProcCallbackData, handle, selection );
656         int [] result = new int [display.treeSelectionLength];
657         for (int i = 0; i < display.treeSelectionLength; i++) {
658             result[i] = display.treeSelection[i];
659         }
660         return result;
661     }
662     /*
663     * Bug in GTK.  gtk_tree_selection_get_selected_rows() segmentation faults
664     * in versions smaller than 2.2.4 if the model is NULL.  The fix is
665     * to give a valid pointer instead.
666     */
667     int dummy;
668     int* model = OS.GTK_VERSION < OS.buildVERSION (2, 2, 4) ? &dummy : null;
669     auto list = OS.gtk_tree_selection_get_selected_rows (selection, cast(void**)model);
670     if (list !is null) {
671         int count = OS.g_list_length (list);
672         int [] treeSelection = new int [count];
673         int len = 0;
674         for (int i=0; i<count; i++) {
675             auto data = OS.g_list_nth_data (list, i);
676             auto indices = OS.gtk_tree_path_get_indices (data);
677             if (indices !is null) {
678                 treeSelection [len] = indices [0];
679                 len++;
680             }
681         }
682         OS.g_list_free (list);
683         int [] result = treeSelection[0..len].dup;
684         return result;
685     }
686     return [0];
687 }
688 
689 /**
690  * Returns the zero-relative index of the item which is currently
691  * at the top of the receiver. This index can change when items are
692  * scrolled or new items are added or removed.
693  *
694  * @return the index of the top item
695  *
696  * @exception SWTException <ul>
697  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
698  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
699  * </ul>
700  */
701 public int getTopIndex () {
702     checkWidget();
703     GtkTreePath* path;
704     OS.gtk_widget_realize (handle);
705     if (!OS.gtk_tree_view_get_path_at_pos (cast(GtkTreeView*)handle, 1, 1, &path, null, null, null)) return 0;
706     if (path is null) return 0;
707     auto indices = OS.gtk_tree_path_get_indices (path);
708     int index;
709     if (indices !is null) index = indices[0];
710     OS.gtk_tree_path_free (path);
711     return index;
712 }
713 
714 override int gtk_changed (GtkWidget* widget) {
715     postEvent (SWT.Selection);
716     return 0;
717 }
718 
719 override int gtk_button_press_event (GtkWidget* widget, GdkEventButton* gdkEvent) {
720     auto result = super.gtk_button_press_event (widget, gdkEvent);
721     if (result !is 0) return result;
722     /*
723     * Feature in GTK.  In a multi-select list view, when multiple items are already
724     * selected, the selection state of the item is toggled and the previous selection
725     * is cleared. This is not the desired behaviour when bringing up a popup menu.
726     * Also, when an item is reselected with the right button, the tree view issues
727     * an unwanted selection event. The workaround is to detect that case and not
728     * run the default handler when the item is already part of the current selection.
729     */
730     int button = gdkEvent.button;
731     if (button is 3 && gdkEvent.type is OS.GDK_BUTTON_PRESS) {
732         GtkTreePath* path;
733         if (OS.gtk_tree_view_get_path_at_pos (cast(GtkTreeView*)handle, cast(int)gdkEvent.x, cast(int)gdkEvent.y, &path, null, null, null)) {
734             if (path !is null) {
735                 auto selection = OS.gtk_tree_view_get_selection (cast(GtkTreeView*)handle);
736                 if (OS.gtk_tree_selection_path_is_selected (selection, path)) result = 1;
737                 OS.gtk_tree_path_free (path);
738             }
739         }
740     }
741 
742     /*
743     * Feature in GTK.  When the user clicks in a single selection GtkTreeView
744     * and there are no selected items, the first item is selected automatically
745     * before the click is processed, causing two selection events.  The is fix
746     * is the set the cursor item to be same as the clicked item to stop the
747     * widget from automatically selecting the first item.
748     */
749     if ((style & SWT.SINGLE) !is 0 && getSelectionCount () is 0) {
750         GtkTreePath* path;
751         if (OS.gtk_tree_view_get_path_at_pos (cast(GtkTreeView*)handle, cast(int)gdkEvent.x, cast(int)gdkEvent.y, &path, null, null, null)) {
752             if (path  !is null) {
753                 auto selection = OS.gtk_tree_view_get_selection (cast(GtkTreeView*)handle);
754                 OS.g_signal_handlers_block_matched (selection, OS.G_SIGNAL_MATCH_DATA, 0, 0, null, null, udCHANGED);
755                 OS.gtk_tree_view_set_cursor (cast(GtkTreeView*)handle, path, null, false);
756                 OS.g_signal_handlers_unblock_matched (selection, OS.G_SIGNAL_MATCH_DATA, 0, 0, null, null, udCHANGED);
757                 OS.gtk_tree_path_free (path);
758             }
759         }
760     }
761     /*
762     * Bug in GTK. GTK segments fault, if the GtkTreeView widget is
763     * not in focus and all items in the widget are disposed before
764     * it finishes processing a button press.  The fix is to give
765     * focus to the widget before it starts processing the event.
766     */
767     if (!OS.GTK_WIDGET_HAS_FOCUS (handle)) {
768         OS.gtk_widget_grab_focus (handle);
769     }
770     return result;
771 }
772 
773 override int gtk_key_press_event (GtkWidget* widget, GdkEventKey* keyEvent) {
774     auto result = super.gtk_key_press_event (widget, keyEvent);
775     if (result !is 0) return result;
776     if (OS.GTK_VERSION < OS.buildVERSION (2, 2 ,0)) {
777         /*
778         * Feature in GTK 2.0.x.  When an item is default selected using
779         * the return key, GTK does not issue notification. The fix is
780         * to issue this notification when the return key is pressed.
781         */
782         int key = keyEvent.keyval;
783         switch (key) {
784             case OS.GDK_Return:
785             case OS.GDK_KP_Enter: {
786                 postEvent (SWT.DefaultSelection);
787                 break;
788             }
789             default:
790         }
791     }
792     return result;
793 }
794 
795 override int gtk_popup_menu (GtkWidget* widget) {
796     auto result = super.gtk_popup_menu (widget);
797     /*
798     * Bug in GTK.  The context menu for the typeahead in GtkTreeViewer
799     * opens in the bottom right corner of the screen when Shift+F10
800     * is pressed and the typeahead window was not visible.  The fix is
801     * to prevent the context menu from opening by stopping the default
802     * handler.
803     *
804     * NOTE: The bug only happens in GTK 2.6.5 and lower.
805     */
806     return OS.GTK_VERSION < OS.buildVERSION (2, 6, 5) ? 1 : result;
807 }
808 
809 override void gtk_row_activated (GtkTreeView* tree, GtkTreePath* path, GtkTreeViewColumn* column){
810     postEvent (SWT.DefaultSelection);
811 }
812 
813 override void hookEvents () {
814     super.hookEvents();
815     auto selection = OS.gtk_tree_view_get_selection(cast(GtkTreeView*)handle);
816     OS.g_signal_connect_closure (selection, OS.changed.ptr, display.closures [CHANGED], false);
817     OS.g_signal_connect_closure (handle, OS.row_activated.ptr, display.closures [ROW_ACTIVATED], false);
818 }
819 
820 /**
821  * Gets the index of an item.
822  * <p>
823  * The list is searched starting at 0 until an
824  * item is found that is equal to the search item.
825  * If no item is found, -1 is returned.  Indexing
826  * is zero based.
827  *
828  * @param string the search item
829  * @return the index of the item
830  *
831  * @exception SWTException <ul>
832  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
833  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
834  * </ul>
835  */
836 public int indexOf (String string) {
837     checkWidget();
838     return indexOf (string, 0);
839 }
840 
841 /**
842  * Searches the receiver's list starting at the given,
843  * zero-relative index until an item is found that is equal
844  * to the argument, and returns the index of that item. If
845  * no item is found or the starting index is out of range,
846  * returns -1.
847  *
848  * @param string the search item
849  * @param start the zero-relative index at which to start the search
850  * @return the index of the item
851  *
852  * @exception SWTException <ul>
853  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
854  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
855  * </ul>
856  */
857 public int indexOf (String string, int start) {
858     checkWidget();
859     // SWT extension: allow null for zero length string
860     //if (string is null) error (SWT.ERROR_NULL_ARGUMENT);
861     String [] items = getItems ();
862     for (int i=start; i<items.length; i++) {
863         if (items [i].equals (string)) return i;
864     }
865     return -1;
866 }
867 
868 /**
869  * Returns <code>true</code> if the item is selected,
870  * and <code>false</code> otherwise.  Indices out of
871  * range are ignored.
872  *
873  * @param index the index of the item
874  * @return the selection state of the item at the index
875  *
876  * @exception SWTException <ul>
877  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
878  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
879  * </ul>
880  */
881 public bool isSelected (int index) {
882     checkWidget();
883     auto selection = OS.gtk_tree_view_get_selection (cast(GtkTreeView*)handle);
884     char* buffer = toStringz(Integer.toString(index));
885     auto path = OS.gtk_tree_path_new_from_string (buffer);
886     bool answer = cast(bool)OS.gtk_tree_selection_path_is_selected (selection, path);
887     OS.gtk_tree_path_free (path);
888     return answer;
889 }
890 
891 override GdkDrawable* paintWindow () {
892     OS.gtk_widget_realize (handle);
893     return OS.gtk_tree_view_get_bin_window (cast(GtkTreeView*)handle);
894 }
895 
896 override void register () {
897     super.register ();
898     display.addWidget (cast(GtkWidget*)OS.gtk_tree_view_get_selection (cast(GtkTreeView*)handle), this);
899 }
900 
901 override void releaseWidget () {
902     super.releaseWidget ();
903     if (modelHandle !is null) OS.g_object_unref (modelHandle);
904     modelHandle = null;
905 }
906 
907 /**
908  * Removes the item from the receiver at the given
909  * zero-relative index.
910  *
911  * @param index the index for the item
912  *
913  * @exception IllegalArgumentException <ul>
914  *    <li>ERROR_INVALID_RANGE - if the index is not between 0 and the number of elements in the list minus 1 (inclusive)</li>
915  * </ul>
916  * @exception SWTException <ul>
917  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
918  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
919  * </ul>
920  */
921 public void remove (int index) {
922     checkWidget();
923     if (!(0 <= index && index < OS.gtk_tree_model_iter_n_children (cast(GtkTreeStore*)modelHandle, null)))  {
924         error (SWT.ERROR_INVALID_RANGE);
925     }
926     GtkTreeIter iter;
927     OS.gtk_tree_model_iter_nth_child (cast(GtkTreeStore*)modelHandle, &iter, null, index);
928     auto selection = OS.gtk_tree_view_get_selection (cast(GtkTreeView*)handle);
929     OS.g_signal_handlers_block_matched (selection, OS.G_SIGNAL_MATCH_DATA, 0, 0, null, null, udCHANGED);
930     OS.gtk_list_store_remove (cast(GtkListStore*)modelHandle, &iter);
931     OS.g_signal_handlers_unblock_matched (selection, OS.G_SIGNAL_MATCH_DATA, 0, 0, null, null, udCHANGED);
932 }
933 
934 /**
935  * Removes the items from the receiver which are
936  * between the given zero-relative start and end
937  * indices (inclusive).
938  *
939  * @param start the start of the range
940  * @param end the end of the range
941  *
942  * @exception IllegalArgumentException <ul>
943  *    <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>
944  * </ul>
945  * @exception SWTException <ul>
946  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
947  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
948  * </ul>
949  */
950 public void remove (int start, int end) {
951     checkWidget();
952     if (start > end) return;
953     int count =  OS.gtk_tree_model_iter_n_children (cast(GtkTreeStore*)modelHandle, null);
954     if (!(0 <= start && start <= end && end < count)) {
955         error (SWT.ERROR_INVALID_RANGE);
956     }
957     GtkTreeIter iter;
958     auto selection = OS.gtk_tree_view_get_selection (cast(GtkTreeView*)handle);
959     OS.g_signal_handlers_block_matched (selection, OS.G_SIGNAL_MATCH_DATA, 0, 0, null, null, udCHANGED);
960     for (int index=end; index>=start; index--) {
961         OS.gtk_tree_model_iter_nth_child (cast(GtkTreeStore*)modelHandle, &iter, null, index);
962         OS.gtk_list_store_remove (cast(GtkListStore*)modelHandle, &iter);
963     }
964     OS.g_signal_handlers_unblock_matched (selection, OS.G_SIGNAL_MATCH_DATA, 0, 0, null, null, udCHANGED);
965 }
966 
967 /**
968  * Searches the receiver's list starting at the first item
969  * until an item is found that is equal to the argument,
970  * and removes that item from the list.
971  *
972  * @param string the item to remove
973  *
974  * @exception IllegalArgumentException <ul>
975  *    <li>ERROR_INVALID_ARGUMENT - if the string is not found in the list</li>
976  * </ul>
977  * @exception SWTException <ul>
978  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
979  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
980  * </ul>
981  */
982 public void remove (String string) {
983     checkWidget();
984     // SWT extension: allow null for zero length string
985     //if (string is null) error (SWT.ERROR_NULL_ARGUMENT);
986     int index = indexOf (string, 0);
987     if (index is -1) error (SWT.ERROR_INVALID_ARGUMENT);
988     remove (index);
989 }
990 
991 /**
992  * Removes the items from the receiver at the given
993  * zero-relative indices.
994  *
995  * @param indices the array of indices of the items
996  *
997  * @exception IllegalArgumentException <ul>
998  *    <li>ERROR_INVALID_RANGE - if the index is not between 0 and the number of elements in the list minus 1 (inclusive)</li>
999  * </ul>
1000  * @exception SWTException <ul>
1001  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1002  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1003  * </ul>
1004  */
1005 public void remove (int [] indices) {
1006     checkWidget();
1007     // SWT extension: allow null for zero length string
1008     //if (indices is null) error (SWT.ERROR_NULL_ARGUMENT);
1009     if (indices.length is 0) return;
1010     int [] newIndices = new int []( indices.length );
1011     System.arraycopy (indices, 0, newIndices, 0, indices.length);
1012     sort (newIndices);
1013     int start = newIndices [newIndices.length - 1], end = newIndices [0];
1014     int count = getItemCount();
1015     if (!(0 <= start && start <= end && end < count)) {
1016         error (SWT.ERROR_INVALID_RANGE);
1017     }
1018     GtkTreeIter iter;
1019     auto selection = OS.gtk_tree_view_get_selection (cast(GtkTreeView*)handle);
1020     OS.g_signal_handlers_block_matched (selection, OS.G_SIGNAL_MATCH_DATA, 0, 0, null, null, udCHANGED);
1021     int last = -1;
1022     for (int i=0; i<newIndices.length; i++) {
1023         int index = newIndices [i];
1024         if (index !is last) {
1025             OS.gtk_tree_model_iter_nth_child (cast(GtkTreeStore*)modelHandle, &iter, null, index);
1026             OS.gtk_list_store_remove (cast(GtkListStore*)modelHandle, &iter);
1027             last = index;
1028         }
1029     }
1030     OS.g_signal_handlers_unblock_matched (selection, OS.G_SIGNAL_MATCH_DATA, 0, 0, null, null, udCHANGED);
1031 }
1032 
1033 /**
1034  * Removes all of the items from the receiver.
1035  * <p>
1036  * @exception SWTException <ul>
1037  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1038  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1039  * </ul>
1040  */
1041 public void removeAll () {
1042     checkWidget();
1043     auto selection = OS.gtk_tree_view_get_selection (cast(GtkTreeView*)handle);
1044     OS.g_signal_handlers_block_matched (selection, OS.G_SIGNAL_MATCH_DATA, 0, 0, null, null, udCHANGED);
1045     OS.gtk_list_store_clear (cast(GtkListStore*)modelHandle);
1046     OS.g_signal_handlers_unblock_matched (selection, OS.G_SIGNAL_MATCH_DATA, 0, 0, null, null, udCHANGED);
1047 }
1048 
1049 /**
1050  * Removes the listener from the collection of listeners who will
1051  * be notified when the user changes the receiver's selection.
1052  *
1053  * @param listener the listener which should no longer be notified
1054  *
1055  * @exception IllegalArgumentException <ul>
1056  *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
1057  * </ul>
1058  * @exception SWTException <ul>
1059  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1060  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1061  * </ul>
1062  *
1063  * @see SelectionListener
1064  * @see #addSelectionListener
1065  */
1066 public void removeSelectionListener(SelectionListener listener) {
1067     checkWidget();
1068     if (listener is null) error (SWT.ERROR_NULL_ARGUMENT);
1069     if (eventTable is null) return;
1070     eventTable.unhook (SWT.Selection, listener);
1071     eventTable.unhook (SWT.DefaultSelection,listener);
1072 }
1073 
1074 /**
1075  * Selects the item at the given zero-relative index in the receiver's
1076  * list.  If the item at the index was already selected, it remains
1077  * selected. Indices that are out of range are ignored.
1078  *
1079  * @param index the index of the item to select
1080  *
1081  * @exception SWTException <ul>
1082  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1083  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1084  * </ul>
1085  */
1086 public void select (int index) {
1087     checkWidget();
1088     if (!(0 <= index && index < OS.gtk_tree_model_iter_n_children (cast(GtkTreeStore*)modelHandle, null)))  return;
1089     GtkTreeIter iter;
1090     auto selection = OS.gtk_tree_view_get_selection (cast(GtkTreeView*)handle);
1091     OS.g_signal_handlers_block_matched (selection, OS.G_SIGNAL_MATCH_DATA, 0, 0, null, null, udCHANGED);
1092     OS.gtk_tree_model_iter_nth_child (cast(GtkTreeStore*)modelHandle, &iter, null, index);
1093     OS.gtk_tree_selection_select_iter (selection, &iter);
1094     if ((style & SWT.SINGLE) !is 0) {
1095         auto path = OS.gtk_tree_model_get_path (cast(GtkTreeStore*)modelHandle, &iter);
1096         OS.gtk_tree_view_set_cursor (cast(GtkTreeView*)handle, path, null, false);
1097         OS.gtk_tree_path_free (path);
1098     }
1099     OS.g_signal_handlers_unblock_matched (selection, OS.G_SIGNAL_MATCH_DATA, 0, 0, null, null, udCHANGED);
1100 }
1101 
1102 /**
1103  * Selects the items in the range specified by the given zero-relative
1104  * indices in the receiver. The range of indices is inclusive.
1105  * The current selection is not cleared before the new items are selected.
1106  * <p>
1107  * If an item in the given range is not selected, it is selected.
1108  * If an item in the given range was already selected, it remains selected.
1109  * Indices that are out of range are ignored and no items will be selected
1110  * if start is greater than end.
1111  * If the receiver is single-select and there is more than one item in the
1112  * given range, then all indices are ignored.
1113  *
1114  * @param start the start of the range
1115  * @param end the end of the range
1116  *
1117  * @exception SWTException <ul>
1118  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1119  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1120  * </ul>
1121  *
1122  * @see List#setSelection(int,int)
1123  */
1124 public void select (int start, int end) {
1125     checkWidget ();
1126     if (end < 0 || start > end || ((style & SWT.SINGLE) !is 0 && start !is end)) return;
1127     int count = OS.gtk_tree_model_iter_n_children (cast(GtkTreeStore*)modelHandle, null);
1128     if (count is 0 || start >= count) return;
1129     start = Math.max (0, start);
1130     end = Math.min (end, count - 1);
1131     GtkTreeIter iter;
1132     auto selection = OS.gtk_tree_view_get_selection (cast(GtkTreeView*)handle);
1133     OS.g_signal_handlers_block_matched (selection, OS.G_SIGNAL_MATCH_DATA, 0, 0, null, null, udCHANGED);
1134     for (int index=start; index<=end; index++) {
1135         OS.gtk_tree_model_iter_nth_child (cast(GtkTreeStore*)modelHandle, &iter, null, index);
1136         OS.gtk_tree_selection_select_iter (selection, &iter);
1137         if ((style & SWT.SINGLE) !is 0) {
1138             auto path = OS.gtk_tree_model_get_path (cast(GtkTreeStore*)modelHandle, &iter);
1139             OS.gtk_tree_view_set_cursor (cast(GtkTreeView*)handle, path, null, false);
1140             OS.gtk_tree_path_free (path);
1141         }
1142     }
1143     OS.g_signal_handlers_unblock_matched (selection, OS.G_SIGNAL_MATCH_DATA, 0, 0, null, null, udCHANGED);
1144 }
1145 
1146 /**
1147  * Selects the items at the given zero-relative indices in the receiver.
1148  * The current selection is not cleared before the new items are selected.
1149  * <p>
1150  * If the item at a given index is not selected, it is selected.
1151  * If the item at a given index was already selected, it remains selected.
1152  * Indices that are out of range and duplicate indices are ignored.
1153  * If the receiver is single-select and multiple indices are specified,
1154  * then all indices are ignored.
1155  *
1156  * @param indices the array of indices for the items to select
1157  *
1158  * @exception SWTException <ul>
1159  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1160  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1161  * </ul>
1162  *
1163  * @see List#setSelection(int[])
1164  */
1165 public void select (int [] indices) {
1166     checkWidget ();
1167     // SWT extension: allow null for zero length string
1168     //if (indices is null) error (SWT.ERROR_NULL_ARGUMENT);
1169     ptrdiff_t length = indices.length;
1170     if (length is 0 || ((style & SWT.SINGLE) !is 0 && length > 1)) return;
1171     GtkTreeIter iter;
1172     int count = OS.gtk_tree_model_iter_n_children (cast(GtkTreeStore*)modelHandle, null);
1173     auto selection = OS.gtk_tree_view_get_selection (cast(GtkTreeView*)handle);
1174     OS.g_signal_handlers_block_matched (selection, OS.G_SIGNAL_MATCH_DATA, 0, 0, null, null, udCHANGED);
1175     for (int i=0; i<length; i++) {
1176         int index = indices [i];
1177         if (!(0 <= index && index < count)) continue;
1178         OS.gtk_tree_model_iter_nth_child (cast(GtkTreeStore*)modelHandle, &iter, null, index);
1179         OS.gtk_tree_selection_select_iter (selection, &iter);
1180         if ((style & SWT.SINGLE) !is 0) {
1181             auto path = OS.gtk_tree_model_get_path (cast(GtkTreeStore*)modelHandle, &iter);
1182             OS.gtk_tree_view_set_cursor (cast(GtkTreeView*)handle, path, null, false);
1183             OS.gtk_tree_path_free (path);
1184         }
1185     }
1186     OS.g_signal_handlers_unblock_matched (selection, OS.G_SIGNAL_MATCH_DATA, 0, 0, null, null, udCHANGED);
1187 }
1188 
1189 /**
1190  * Selects all of the items in the receiver.
1191  * <p>
1192  * If the receiver is single-select, do nothing.
1193  *
1194  * @exception SWTException <ul>
1195  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1196  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1197  * </ul>
1198  */
1199 public void selectAll () {
1200     checkWidget();
1201     if ((style & SWT.SINGLE) !is 0) return;
1202     auto selection = OS.gtk_tree_view_get_selection (cast(GtkTreeView*)handle);
1203     OS.g_signal_handlers_block_matched (selection, OS.G_SIGNAL_MATCH_DATA, 0, 0, null, null, udCHANGED);
1204     OS.gtk_tree_selection_select_all (selection);
1205     OS.g_signal_handlers_unblock_matched (selection, OS.G_SIGNAL_MATCH_DATA, 0, 0, null, null, udCHANGED);
1206 }
1207 
1208 void selectFocusIndex (int index) {
1209     /*
1210     * Note that this method both selects and sets the focus to the
1211     * specified index, so any previous selection in the list will be lost.
1212     * gtk does not provide a way to just set focus to a specified list item.
1213     */
1214     int count = OS.gtk_tree_model_iter_n_children (cast(GtkTreeStore*)modelHandle, null);
1215     if (!(0 <= index && index < count))  return;
1216     GtkTreeIter iter;
1217     OS.gtk_tree_model_iter_nth_child (cast(GtkTreeStore*)modelHandle, &iter, null, index);
1218     auto path = OS.gtk_tree_model_get_path (cast(GtkTreeStore*)modelHandle, &iter);
1219     auto selection = OS.gtk_tree_view_get_selection (cast(GtkTreeView*)handle);
1220     OS.g_signal_handlers_block_matched (selection, OS.G_SIGNAL_MATCH_DATA, 0, 0, null, null, udCHANGED);
1221     OS.gtk_tree_view_set_cursor (cast(GtkTreeView*)handle, path, null, false);
1222     /*
1223     * Bug in GTK. For some reason, when an event loop is run from
1224     * within a key pressed handler and a dialog is displayed that
1225     * contains a GtkTreeView,  gtk_tree_view_set_cursor() does
1226     * not set the cursor or select the item.  The fix is to select the
1227     * item with gtk_tree_selection_select_iter() as well.
1228     *
1229     * NOTE: This happens in GTK 2.2.1 and is fixed in GTK 2.2.4.
1230     */
1231     OS.gtk_tree_selection_select_iter (selection, &iter);
1232     OS.g_signal_handlers_unblock_matched (selection, OS.G_SIGNAL_MATCH_DATA, 0, 0, null, null, udCHANGED);
1233     OS.gtk_tree_path_free (path);
1234 }
1235 
1236 override void setBackgroundColor (GdkColor* color) {
1237     super.setBackgroundColor (color);
1238     OS.gtk_widget_modify_base (handle, 0, color);
1239 }
1240 
1241 override int setBounds (int x, int y, int width, int height, bool move, bool resize) {
1242     int result = super.setBounds (x, y, width, height, move, resize);
1243     /*
1244     * Bug on GTK.  The tree view sometimes does not get a paint
1245     * event or resizes to a one pixel square when resized in a new
1246     * shell that is not visible after any event loop has been run.  The
1247     * problem is intermittent. It doesn't seem to happen the first time
1248     * a new shell is created. The fix is to ensure the tree view is realized
1249     * after it has been resized.
1250     */
1251     OS.gtk_widget_realize (handle);
1252     /*
1253     * Bug in GTK.  An empty GtkTreeView fails to repaint the focus rectangle
1254     * correctly when resized on versions before 2.6.0.  The fix is to force
1255     * the widget to redraw.
1256     */
1257     if (OS.GTK_VERSION < OS.buildVERSION (2, 6, 0) && OS.gtk_tree_model_iter_n_children (cast(GtkTreeStore*)modelHandle, null) is 0) {
1258         redraw (false);
1259     }
1260     return result;
1261 }
1262 
1263 /**
1264  * Sets the text of the item in the receiver's list at the given
1265  * zero-relative index to the string argument.
1266  *
1267  * @param index the index for the item
1268  * @param string the new text for the item
1269  *
1270  * @exception IllegalArgumentException <ul>
1271  *    <li>ERROR_INVALID_RANGE - if the index is not between 0 and the number of elements in the list minus 1 (inclusive)</li>
1272  * </ul>
1273  * @exception SWTException <ul>
1274  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1275  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1276  * </ul>
1277  */
1278 public void setItem (int index, String string) {
1279     checkWidget();
1280     // SWT extension: allow null for zero length string
1281     //if (string is null) error (SWT.ERROR_NULL_ARGUMENT);
1282     if (!(0 <= index && index < OS.gtk_tree_model_iter_n_children (cast(GtkTreeStore*)modelHandle, null)))  {
1283         error (SWT.ERROR_INVALID_RANGE);
1284     }
1285     GtkTreeIter iter;
1286     OS.gtk_tree_model_iter_nth_child (cast(GtkTreeStore*)modelHandle, &iter, null, index);
1287     char* buffer = string.toStringzValidPtr();
1288     OS.gtk_list_store_set1 (cast(GtkListStore*)modelHandle, &iter, TEXT_COLUMN, buffer);
1289 }
1290 
1291 /**
1292  * Sets the receiver's items to be the given array of items.
1293  *
1294  * @param items the array of items
1295  *
1296  * @exception IllegalArgumentException <ul>
1297  *    <li>ERROR_INVALID_ARGUMENT - if an item in the items array is null</li>
1298  * </ul>
1299  * @exception SWTException <ul>
1300  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1301  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1302  * </ul>
1303  */
1304 public void setItems (String [] items) {
1305     checkWidget();
1306     // SWT extension, allow null a null length list.
1307     //if (items is null) error (SWT.ERROR_NULL_ARGUMENT);
1308     for (int i=0; i<items.length; i++) {
1309         if (items [i] is null) error (SWT.ERROR_INVALID_ARGUMENT);
1310     }
1311     auto selection = OS.gtk_tree_view_get_selection (cast(GtkTreeView*)handle);
1312     OS.g_signal_handlers_block_matched (selection, OS.G_SIGNAL_MATCH_DATA, 0, 0, null, null, udCHANGED);
1313     OS.gtk_list_store_clear (cast(GtkListStore*)modelHandle);
1314     OS.g_signal_handlers_unblock_matched (selection, OS.G_SIGNAL_MATCH_DATA, 0, 0, null, null, udCHANGED);
1315     GtkTreeIter iter;
1316     for (int i=0; i<items.length; i++) {
1317         String string = items [i];
1318         char* buffer = toStringz(string);
1319         OS.gtk_list_store_append (cast(GtkListStore*)modelHandle, &iter);
1320         OS.gtk_list_store_set1 (cast(GtkListStore*)modelHandle, &iter, TEXT_COLUMN, buffer);
1321     }
1322 }
1323 
1324 /**
1325  * Selects the item at the given zero-relative index in the receiver.
1326  * If the item at the index was already selected, it remains selected.
1327  * The current selection is first cleared, then the new item is selected.
1328  * Indices that are out of range are ignored.
1329  *
1330  * @param index the index of the item to select
1331  *
1332  * @exception SWTException <ul>
1333  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1334  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1335  * </ul>
1336  * @see List#deselectAll()
1337  * @see List#select(int)
1338  */
1339 public void setSelection (int index) {
1340     checkWidget ();
1341     deselectAll ();
1342     selectFocusIndex (index);
1343     showSelection ();
1344 }
1345 
1346 /**
1347  * Selects the items in the range specified by the given zero-relative
1348  * indices in the receiver. The range of indices is inclusive.
1349  * The current selection is cleared before the new items are selected.
1350  * <p>
1351  * Indices that are out of range are ignored and no items will be selected
1352  * if start is greater than end.
1353  * If the receiver is single-select and there is more than one item in the
1354  * given range, then all indices are ignored.
1355  *
1356  * @param start the start index of the items to select
1357  * @param end the end index of the items to select
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  * @see List#deselectAll()
1365  * @see List#select(int,int)
1366  */
1367 public void setSelection (int start, int end) {
1368     checkWidget ();
1369     deselectAll ();
1370     if (end < 0 || start > end || ((style & SWT.SINGLE) !is 0 && start !is end)) return;
1371     int count = OS.gtk_tree_model_iter_n_children (cast(GtkTreeStore*)modelHandle, null);
1372     if (count is 0 || start >= count) return;
1373     start = Math.max (0, start);
1374     end = Math.min (end, count - 1);
1375     selectFocusIndex (start);
1376     if ((style & SWT.MULTI) !is 0) {
1377         select (start, end);
1378     }
1379     showSelection ();
1380 }
1381 
1382 /**
1383  * Selects the items at the given zero-relative indices in the receiver.
1384  * The current selection is cleared before the new items are selected.
1385  * <p>
1386  * Indices that are out of range and duplicate indices are ignored.
1387  * If the receiver is single-select and multiple indices are specified,
1388  * then all indices are ignored.
1389  *
1390  * @param indices the indices of the items to select
1391  *
1392  * @exception SWTException <ul>
1393  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1394  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1395  * </ul>
1396  *
1397  * @see List#deselectAll()
1398  * @see List#select(int[])
1399  */
1400 public void setSelection(int[] indices) {
1401     checkWidget ();
1402     // SWT extension: allow null for zero length string
1403     //if (indices is null) error (SWT.ERROR_NULL_ARGUMENT);
1404     deselectAll ();
1405     ptrdiff_t length = indices.length;
1406     if (length is 0 || ((style & SWT.SINGLE) !is 0 && length > 1)) return;
1407     selectFocusIndex (indices [0]);
1408     if ((style & SWT.MULTI) !is 0) {
1409         select (indices);
1410     }
1411     showSelection ();
1412 }
1413 
1414 /**
1415  * Sets the receiver's selection to be the given array of items.
1416  * The current selection is cleared before the new items are selected.
1417  * <p>
1418  * Items that are not in the receiver are ignored.
1419  * If the receiver is single-select and multiple items are specified,
1420  * then all items are ignored.
1421  *
1422  * @param items the array of items
1423  *
1424  * @exception SWTException <ul>
1425  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1426  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1427  * </ul>
1428  *
1429  * @see List#deselectAll()
1430  * @see List#select(int[])
1431  * @see List#setSelection(int[])
1432  */
1433 public void setSelection (String [] items) {
1434     checkWidget ();
1435     // SWT extension: allow null for zero length string
1436     //if (items is null) error (SWT.ERROR_NULL_ARGUMENT);
1437     deselectAll ();
1438     ptrdiff_t length = items.length;
1439     if (length is 0 || ((style & SWT.SINGLE) !is 0 && length > 1)) return;
1440     bool first = true;
1441     for (int i = 0; i < length; i++) {
1442         int index = 0;
1443         String string = items [i];
1444         if (string !is null) {
1445             while ((index = indexOf (string, index)) !is -1) {
1446                 if ((style & SWT.MULTI) !is 0) {
1447                     if (first) {
1448                         first = false;
1449                         selectFocusIndex (index);
1450                     } else {
1451                         select (index);
1452                     }
1453                 } else {
1454                     selectFocusIndex (index);
1455                     break;
1456                 }
1457                 index++;
1458             }
1459         }
1460     }
1461     showSelection ();
1462 }
1463 
1464 /**
1465  * Sets the zero-relative index of the item which is currently
1466  * at the top of the receiver. This index can change when items
1467  * are scrolled or new items are added and removed.
1468  *
1469  * @param index the index of the top item
1470  *
1471  * @exception SWTException <ul>
1472  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1473  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1474  * </ul>
1475  */
1476 public void setTopIndex (int index) {
1477     checkWidget();
1478     if (!(0 <= index && index < OS.gtk_tree_model_iter_n_children (cast(GtkTreeStore*)modelHandle, null))) return;
1479     GtkTreeIter iter;
1480     OS.gtk_tree_model_iter_nth_child (cast(GtkTreeStore*)modelHandle, &iter, null, index);
1481     auto path = OS.gtk_tree_model_get_path (cast(GtkTreeStore*)modelHandle, &iter);
1482     OS.gtk_tree_view_scroll_to_cell (cast(GtkTreeView*)handle, path, null, true, 0, 0);
1483     if (OS.GTK_VERSION < OS.buildVERSION (2, 8, 0)) {
1484         /*
1485         * Bug in GTK.  According to the documentation, gtk_tree_view_scroll_to_cell
1486         * should vertically scroll the cell to the top if use_align is true and row_align is 0.
1487         * However, prior to version 2.8 it does not scroll at all.  The fix is to determine
1488         * the new location and use gtk_tree_view_scroll_to_point.
1489         * If the widget is a pinhead, calling gtk_tree_view_scroll_to_point
1490         * will have no effect. Therefore, it is still neccessary to call
1491         * gtk_tree_view_scroll_to_cell.
1492         */
1493         OS.gtk_widget_realize (handle);
1494         GdkRectangle cellRect;
1495         OS.gtk_tree_view_get_cell_area (cast(GtkTreeView*)handle, path, null, &cellRect);
1496         int tx, ty;
1497         OS.gtk_tree_view_widget_to_tree_coords(cast(GtkTreeView*)handle, cellRect.x, cellRect.y, &tx, &ty);
1498         OS.gtk_tree_view_scroll_to_point (cast(GtkTreeView*)handle, -1, ty);
1499     }
1500     OS.gtk_tree_path_free (path);
1501 }
1502 
1503 /**
1504  * Shows the selection.  If the selection is already showing in the receiver,
1505  * this method simply returns.  Otherwise, the items are scrolled until
1506  * the selection is visible.
1507  *
1508  * @exception SWTException <ul>
1509  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1510  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1511  * </ul>
1512  */
1513 public void showSelection () {
1514     checkWidget();
1515     int index = getSelectionIndex ();
1516     if (index is -1) return;
1517     GtkTreeIter iter;
1518     OS.gtk_tree_model_iter_nth_child (cast(GtkTreeStore*)modelHandle, &iter, null, index);
1519     auto path = OS.gtk_tree_model_get_path (cast(GtkTreeStore*)modelHandle, &iter);
1520     /*
1521     * This code intentionally commented.
1522     * Bug in GTK.  According to the documentation, gtk_tree_view_scroll_to_cell
1523     * should scroll the minimum amount to show the cell if use_align is false.
1524     * However, what actually happens is the cell is scrolled to the top.
1525     * The fix is to determine the new location and use gtk_tree_view_scroll_to_point.
1526     * If the widget is a pinhead, calling gtk_tree_view_scroll_to_point
1527     * will have no effect. Therefore, it is still neccessary to
1528     * call gtk_tree_view_scroll_to_cell.
1529     */
1530 //  OS.gtk_tree_view_scroll_to_cell (handle, path, 0, false, 0, 0);
1531     OS.gtk_widget_realize (handle);
1532     GdkRectangle visibleRect;
1533     OS.gtk_tree_view_get_visible_rect (cast(GtkTreeView*)handle, &visibleRect);
1534     GdkRectangle cellRect;
1535     OS.gtk_tree_view_get_cell_area (cast(GtkTreeView*)handle, path, null, &cellRect);
1536     int tx, ty;
1537     OS.gtk_tree_view_widget_to_tree_coords(cast(GtkTreeView*)handle, cellRect.x, cellRect.y, &tx, &ty);
1538     if (ty < visibleRect.y ) {
1539         OS.gtk_tree_view_scroll_to_cell (cast(GtkTreeView*)handle, path, null, true, 0f, 0f);
1540         OS.gtk_tree_view_scroll_to_point (cast(GtkTreeView*)handle, -1, ty);
1541     } else {
1542         int height = Math.min (visibleRect.height, cellRect.height);
1543         if (ty + height > visibleRect.y + visibleRect.height) {
1544             OS.gtk_tree_view_scroll_to_cell (cast(GtkTreeView*)handle, path, null, true, 1f, 0f);
1545             ty += cellRect.height - visibleRect.height;
1546             OS.gtk_tree_view_scroll_to_point (cast(GtkTreeView*)handle, -1, ty);
1547         }
1548     }
1549     OS.gtk_tree_path_free (path);
1550 }
1551 
1552 override void treeSelectionProc (GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, int[] selection, int length_) {
1553     if (selection !is null) {
1554         auto indices = OS.gtk_tree_path_get_indices (path);
1555         if (indices !is null) {
1556             selection [length_] = indices[0];
1557         }
1558     }
1559 }
1560 
1561 }