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.Combo;
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.events.SelectionListener;
20 import org.eclipse.swt.events.SelectionEvent;
21 import org.eclipse.swt.events.ModifyListener;
22 import org.eclipse.swt.events.VerifyListener;
23 import org.eclipse.swt.widgets.Shell;
24 import org.eclipse.swt.widgets.Composite;
25 import org.eclipse.swt.widgets.Event;
26 import org.eclipse.swt.widgets.TypedListener;
27 
28 import java.lang.all;
29 
30 /**
31  * Instances of this class are controls that allow the user
32  * to choose an item from a list of items, or optionally
33  * enter a new value by typing it into an editable text
34  * field. Often, <code>Combo</code>s are used in the same place
35  * where a single selection <code>List</code> widget could
36  * be used but space is limited. A <code>Combo</code> takes
37  * less space than a <code>List</code> widget and shows
38  * similar information.
39  * <p>
40  * Note: Since <code>Combo</code>s can contain both a list
41  * and an editable text field, it is possible to confuse methods
42  * which access one versus the other (compare for example,
43  * <code>clearSelection()</code> and <code>deselectAll()</code>).
44  * The API documentation is careful to indicate either "the
45  * receiver's list" or the "the receiver's text field" to
46  * distinguish between the two cases.
47  * </p><p>
48  * Note that although this class is a subclass of <code>Composite</code>,
49  * it does not make sense to add children to it, or set a layout on it.
50  * </p>
51  * <dl>
52  * <dt><b>Styles:</b></dt>
53  * <dd>DROP_DOWN, READ_ONLY, SIMPLE</dd>
54  * <dt><b>Events:</b></dt>
55  * <dd>DefaultSelection, Modify, Selection, Verify</dd>
56  * </dl>
57  * <p>
58  * Note: Only one of the styles DROP_DOWN and SIMPLE may be specified.
59  * </p><p>
60  * IMPORTANT: This class is <em>not</em> intended to be subclassed.
61  * </p>
62  *
63  * @see List
64  * @see <a href="http://www.eclipse.org/swt/snippets/#combo">Combo snippets</a>
65  * @see <a href="http://www.eclipse.org/swt/examples.php">SWT Example: ControlExample</a>
66  * @see <a href="http://www.eclipse.org/swt/">Sample code and further information</a>
67  */
68 public class Combo : Composite {
69 
70     alias Composite.computeSize computeSize;
71     alias Composite.createHandle createHandle;
72     alias Composite.dragDetect dragDetect;
73     alias Composite.gtk_button_press_event gtk_button_press_event;
74     alias Composite.setBackgroundColor setBackgroundColor;
75     alias Composite.setBounds setBounds;
76     alias Composite.setForegroundColor setForegroundColor;
77     alias Composite.setToolTipText setToolTipText;
78     alias Composite.translateTraversal translateTraversal;
79 
80     GtkWidget* buttonHandle, entryHandle, listHandle, textRenderer, cellHandle, popupHandle;
81     int lastEventTime, visibleCount = 5;
82     GdkEventKey* gdkEventKey;
83     int fixStart = -1, fixEnd = -1;
84     String[] items;
85     bool ignoreSelect, lockText;
86 
87     static const int INNER_BORDER = 2;
88 
89     /**
90      * the operating system limit for the number of characters
91      * that the text field in an instance of this class can hold
92      */
93     public const static int LIMIT = 0xFFFF;
94 
95     /*
96     * These values can be different on different platforms.
97     * Therefore they are not initialized in the declaration
98     * to stop the compiler from inlining.
99     */
100     //static {
101     //    LIMIT = 0xFFFF;
102     //}
103 
104 /**
105  * Constructs a new instance of this class given its parent
106  * and a style value describing its behavior and appearance.
107  * <p>
108  * The style value is either one of the style constants defined in
109  * class <code>SWT</code> which is applicable to instances of this
110  * class, or must be built by <em>bitwise OR</em>'ing together
111  * (that is, using the <code>int</code> "|" operator) two or more
112  * of those <code>SWT</code> style constants. The class description
113  * lists the style constants that are applicable to the class.
114  * Style bits are also inherited from superclasses.
115  * </p>
116  *
117  * @param parent a composite control which will be the parent of the new instance (cannot be null)
118  * @param style the style of control to construct
119  *
120  * @exception IllegalArgumentException <ul>
121  *    <li>ERROR_NULL_ARGUMENT - if the parent is null</li>
122  * </ul>
123  * @exception SWTException <ul>
124  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li>
125  *    <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li>
126  * </ul>
127  *
128  * @see SWT#DROP_DOWN
129  * @see SWT#READ_ONLY
130  * @see SWT#SIMPLE
131  * @see Widget#checkSubclass
132  * @see Widget#getStyle
133  */
134 public this (Composite parent, int style) {
135     super (parent, checkStyle (style));
136 }
137 
138 /**
139  * Adds the argument to the end of the receiver's list.
140  *
141  * @param string the new item
142  *
143  * @exception SWTException <ul>
144  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
145  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
146  * </ul>
147  *
148  * @see #add(String,int)
149  */
150 public void add (String string) {
151     checkWidget();
152     // SWT extension: allow null for zero length string
153     //if (string is null) error (SWT.ERROR_NULL_ARGUMENT);
154     add (string, cast(int)/*64bit*/items.length);
155 }
156 
157 /**
158  * Adds the argument to the receiver's list at the given
159  * zero-relative index.
160  * <p>
161  * Note: To add an item at the end of the list, use the
162  * result of calling <code>getItemCount()</code> as the
163  * index or use <code>add(String)</code>.
164  * </p>
165  *
166  * @param string the new item
167  * @param index the index for the item
168  *
169  * @exception IllegalArgumentException <ul>
170  *    <li>ERROR_INVALID_RANGE - if the index is not between 0 and the number of elements in the list (inclusive)</li>
171  * </ul>
172  * @exception SWTException <ul>
173  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
174  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
175  * </ul>
176  *
177  * @see #add(String)
178  */
179 public void add (String string, int index) {
180     checkWidget();
181     // SWT extension: allow null for zero length string
182     //if (string is null) error (SWT.ERROR_NULL_ARGUMENT);
183     if (!(0 <= index && index <= items.length)) {
184         error (SWT.ERROR_INVALID_RANGE);
185     }
186     String [] newItems = new String[]( items.length + 1 );
187     System.arraycopy (items, 0, newItems, 0, index);
188     newItems [index] = string;
189     System.arraycopy (items, index, newItems, index + 1, items.length - index);
190     items = newItems;
191     char* buffer = string.toStringzValidPtr();
192     if (OS.GTK_VERSION >= OS.buildVERSION (2, 4, 0)) {
193         OS.gtk_combo_box_insert_text (handle, index, buffer);
194         if ((style & SWT.RIGHT_TO_LEFT) !is 0 && popupHandle !is null) {
195             display.doSetDirectionProc( popupHandle, OS.GTK_TEXT_DIR_RTL);
196         }
197     } else {
198         /*
199         * Feature in GTK. When the list is empty and the first item
200         * is added, the combo box selects that item replacing the
201         * text in the entry field.  The fix is to avoid this by
202         * stopping the "delete" and "insert_text" signal emission.
203         */
204         ignoreSelect = lockText = true;
205         auto item = OS.gtk_list_item_new_with_label (buffer);
206         auto label = OS.gtk_bin_get_child (item);
207         setForegroundColor (label, getForegroundColor ());
208         OS.gtk_widget_modify_font (label, getFontDescription ());
209         OS.gtk_widget_set_direction (label, OS.gtk_widget_get_direction (handle));
210         OS.gtk_widget_show (item);
211         auto items = OS.g_list_append (null, item);
212         OS.gtk_list_insert_items (listHandle, items, index);
213         ignoreSelect = lockText = false;
214     }
215 }
216 
217 /**
218  * Adds the listener to the collection of listeners who will
219  * be notified when the receiver's text is modified, by sending
220  * it one of the messages defined in the <code>ModifyListener</code>
221  * interface.
222  *
223  * @param listener the listener which should be notified
224  *
225  * @exception IllegalArgumentException <ul>
226  *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
227  * </ul>
228  * @exception SWTException <ul>
229  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
230  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
231  * </ul>
232  *
233  * @see ModifyListener
234  * @see #removeModifyListener
235  */
236 public void addModifyListener (ModifyListener listener) {
237     checkWidget();
238     if (listener is null) error (SWT.ERROR_NULL_ARGUMENT);
239     TypedListener typedListener = new TypedListener (listener);
240     addListener (SWT.Modify, typedListener);
241 }
242 
243 /**
244  * Adds the listener to the collection of listeners who will
245  * be notified when the user changes the receiver's selection, by sending
246  * it one of the messages defined in the <code>SelectionListener</code>
247  * interface.
248  * <p>
249  * <code>widgetSelected</code> is called when the user changes the combo's list selection.
250  * <code>widgetDefaultSelected</code> is typically called when ENTER is pressed the combo's text area.
251  * </p>
252  *
253  * @param listener the listener which should be notified
254  *
255  * @exception IllegalArgumentException <ul>
256  *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
257  * </ul>
258  * @exception SWTException <ul>
259  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
260  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
261  * </ul>
262  *
263  * @see SelectionListener
264  * @see #removeSelectionListener
265  * @see SelectionEvent
266  */
267 public void addSelectionListener(SelectionListener listener) {
268     checkWidget();
269     if (listener is null) error (SWT.ERROR_NULL_ARGUMENT);
270     TypedListener typedListener = new TypedListener (listener);
271     addListener (SWT.Selection,typedListener);
272     addListener (SWT.DefaultSelection,typedListener);
273 }
274 
275 /**
276  * Adds the listener to the collection of listeners who will
277  * be notified when the receiver's text is verified, by sending
278  * it one of the messages defined in the <code>VerifyListener</code>
279  * interface.
280  *
281  * @param listener the listener which should be notified
282  *
283  * @exception IllegalArgumentException <ul>
284  *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
285  * </ul>
286  * @exception SWTException <ul>
287  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
288  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
289  * </ul>
290  *
291  * @see VerifyListener
292  * @see #removeVerifyListener
293  *
294  * @since 3.1
295  */
296 public void addVerifyListener (VerifyListener listener) {
297     checkWidget ();
298     if (listener is null) error (SWT.ERROR_NULL_ARGUMENT);
299     TypedListener typedListener = new TypedListener (listener);
300     addListener (SWT.Verify, typedListener);
301 }
302 
303 static int checkStyle (int style) {
304     /*
305     * Feature in Windows.  It is not possible to create
306     * a combo box that has a border using Windows style
307     * bits.  All combo boxes draw their own border and
308     * do not use the standard Windows border styles.
309     * Therefore, no matter what style bits are specified,
310     * clear the BORDER bits so that the SWT style will
311     * match the Windows widget.
312     *
313     * The Windows behavior is currently implemented on
314     * all platforms.
315     */
316     style &= ~SWT.BORDER;
317 
318     /*
319     * Even though it is legal to create this widget
320     * with scroll bars, they serve no useful purpose
321     * because they do not automatically scroll the
322     * widget's client area.  The fix is to clear
323     * the SWT style.
324     */
325     style &= ~(SWT.H_SCROLL | SWT.V_SCROLL);
326     style = checkBits (style, SWT.DROP_DOWN, SWT.SIMPLE, 0, 0, 0, 0);
327     if ((style & SWT.SIMPLE) !is 0) return style & ~SWT.READ_ONLY;
328     return style;
329 }
330 
331 protected override void checkSubclass () {
332     if (!isValidSubclass ()) error (SWT.ERROR_INVALID_SUBCLASS);
333 }
334 
335 /**
336  * Sets the selection in the receiver's text field to an empty
337  * selection starting just before the first character. If the
338  * text field is editable, this has the effect of placing the
339  * i-beam at the start of the text.
340  * <p>
341  * Note: To clear the selected items in the receiver's list,
342  * use <code>deselectAll()</code>.
343  * </p>
344  *
345  * @exception SWTException <ul>
346  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
347  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
348  * </ul>
349  *
350  * @see #deselectAll
351  */
352 public void clearSelection () {
353     checkWidget();
354     if (entryHandle !is null) {
355         int position = OS.gtk_editable_get_position (entryHandle);
356         OS.gtk_editable_select_region (entryHandle, position, position);
357     }
358 }
359 
360 void clearText () {
361     if (OS.GTK_VERSION >= OS.buildVERSION (2, 4, 0)) {
362         OS.g_signal_handlers_block_matched (handle, OS.G_SIGNAL_MATCH_DATA, 0, 0, null, null, udCHANGED);
363         if ((style & SWT.READ_ONLY) !is 0) {
364             int index = OS.gtk_combo_box_get_active (handle);
365             if (index !is -1) {
366                 auto modelHandle = OS.gtk_combo_box_get_model (handle);
367                 char* ptr;
368                 GtkTreeIter iter;
369                 OS.gtk_tree_model_iter_nth_child (modelHandle, &iter, null, index);
370                 OS.gtk_tree_model_get1 (modelHandle, &iter, 0, cast(void**)&ptr );
371                 if (fromStringz(ptr).length > 0) postEvent (SWT.Modify);
372                 OS.g_free (ptr);
373             }
374         } else {
375             char dummy = '\0';
376             OS.gtk_entry_set_text (entryHandle, &dummy );
377         }
378         OS.gtk_combo_box_set_active (handle, -1);
379         OS.g_signal_handlers_unblock_matched (handle, OS.G_SIGNAL_MATCH_DATA, 0, 0, null, null, udCHANGED);
380     }
381 }
382 
383 public override Point computeSize (int wHint, int hHint, bool changed) {
384     checkWidget ();
385     if (OS.GTK_VERSION >= OS.buildVERSION (2, 4, 0)) {
386         return computeNativeSize (handle, wHint, hHint, changed);
387     }
388     if (wHint !is SWT.DEFAULT && wHint < 0) wHint = 0;
389     if (hHint !is SWT.DEFAULT && hHint < 0) hHint = 0;
390     int w, h;
391     OS.gtk_widget_realize (entryHandle);
392     auto layout = OS.gtk_entry_get_layout (entryHandle);
393     OS.pango_layout_get_size (layout, &w, &h);
394     int xborder = INNER_BORDER, yborder = INNER_BORDER;
395     auto style = OS.gtk_widget_get_style (entryHandle);
396     xborder += OS.gtk_style_get_xthickness (style);
397     yborder += OS.gtk_style_get_ythickness (style);
398     int property;
399     OS.gtk_widget_style_get1 (entryHandle, OS.interior_focus.ptr, &property);
400     if (property is 0) {
401         OS.gtk_widget_style_get1 (entryHandle, OS.focus_line_width.ptr, &property);
402         xborder += property ;
403         yborder += property ;
404     }
405     int width = OS.PANGO_PIXELS (w ) + xborder  * 2;
406     int height = OS.PANGO_PIXELS (h ) + yborder  * 2;
407 
408     GtkRequisition arrowRequesition;
409     OS.gtk_widget_size_request (buttonHandle, &arrowRequesition);
410     GtkRequisition listRequesition;
411     auto listParent = OS.gtk_widget_get_parent (listHandle);
412     OS.gtk_widget_size_request (listParent !is null ? listParent : listHandle, &listRequesition);
413 
414     width = (Math.max (listRequesition.width, width) + arrowRequesition.width + 4);
415     width = wHint is SWT.DEFAULT ? width : wHint;
416     height = hHint is SWT.DEFAULT ? height : hHint;
417     return new Point (width, height);
418 }
419 
420 /**
421  * Copies the selected text.
422  * <p>
423  * The current selection is copied to the clipboard.
424  * </p>
425  *
426  * @exception SWTException <ul>
427  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
428  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
429  * </ul>
430  *
431  * @since 2.1
432  */
433 public void copy () {
434     checkWidget ();
435     if (entryHandle !is null) OS.gtk_editable_copy_clipboard (entryHandle);
436 }
437 
438 override void createHandle (int index) {
439     state |= HANDLE | MENU;
440     fixedHandle = cast(GtkWidget*)OS.g_object_new (display.gtk_fixed_get_type (), null);
441     if (fixedHandle is null) error (SWT.ERROR_NO_HANDLES);
442     OS.gtk_fixed_set_has_window (fixedHandle, true);
443     if (OS.GTK_VERSION >= OS.buildVERSION (2, 4, 0)) {
444         auto oldList = OS.gtk_window_list_toplevels ();
445         if ((style & SWT.READ_ONLY) !is 0) {
446             handle = OS.gtk_combo_box_new_text ();
447             if (handle is null) error (SWT.ERROR_NO_HANDLES);
448             cellHandle = OS.gtk_bin_get_child (handle);
449             if (cellHandle is null) error (SWT.ERROR_NO_HANDLES);
450         } else {
451             handle = OS.gtk_combo_box_entry_new_text ();
452             if (handle is null) error (SWT.ERROR_NO_HANDLES);
453             entryHandle = OS.gtk_bin_get_child (handle);
454             if (entryHandle is null) error (SWT.ERROR_NO_HANDLES);
455         }
456         popupHandle = findPopupHandle (oldList);
457         OS.gtk_container_add (fixedHandle, handle);
458         textRenderer = cast(GtkWidget*)OS.gtk_cell_renderer_text_new ();
459         if (textRenderer is null) error (SWT.ERROR_NO_HANDLES);
460         /*
461         * Feature in GTK. In order to make a read only combo box the same
462         * height as an editable combo box the ypad must be set to 0. In
463         * versions 2.4.x of GTK, a pad of 0 will clip some letters. The
464         * fix is to set the pad to 1.
465         */
466         int pad = 0;
467         if (OS.GTK_VERSION < OS.buildVERSION(2, 6, 0)) pad = 1;
468         OS.g_object_set1 (textRenderer, OS.ypad.ptr, pad);
469         /*
470         * Feature in GTK.  In version 2.4.9 of GTK, a warning is issued
471         * when a call to gtk_cell_layout_clear() is made. The fix is to hide
472         * the warning.
473         */
474         bool warnings = display.getWarnings ();
475         display.setWarnings (false);
476         OS.gtk_cell_layout_clear (handle);
477         display.setWarnings (warnings);
478         OS.gtk_cell_layout_pack_start (handle, textRenderer, true);
479         OS.gtk_cell_layout_set_attributes1 (handle, textRenderer, OS.text.ptr, null);
480 
481         /*
482         * Feature in GTK.  There is no API to query the button
483         * handle from a combo box although it is possible to get the
484         * text field.  The button handle is needed to hook events.  The
485         * fix is to walk the combo tree and find the first child that is
486         * an instance of button.
487         */
488         display.allChildrenCollect (handle, 0);
489         if (display.allChildren !is null) {
490             auto list = display.allChildren;
491             while (list !is null) {
492                 auto widget = OS.g_list_data (list);
493                 if (OS.GTK_IS_BUTTON (cast(GTypeInstance*)widget)) {
494                     buttonHandle = cast(GtkWidget*)widget;
495                     break;
496                 }
497                 list = OS.g_list_next (list);
498             }
499             OS.g_list_free (display.allChildren);
500             display.allChildren = null;
501         }
502         /*
503         * Feature in GTK. By default, read only combo boxes
504         * process the RETURN key rather than allowing the
505         * default button to process the key. The fix is to
506         * clear the GTK_RECEIVES_DEFAULT flag.
507         */
508         if ((style & SWT.READ_ONLY) !is 0 && buttonHandle !is null) {
509             OS.GTK_WIDGET_UNSET_FLAGS (buttonHandle, OS.GTK_RECEIVES_DEFAULT);
510         }
511     } else {
512         handle = OS.gtk_combo_new ();
513         if (handle is null) error (SWT.ERROR_NO_HANDLES);
514         OS.gtk_container_add (fixedHandle, handle);
515         GtkCombo* combo = cast(GtkCombo*)handle;
516         entryHandle = combo.entry;
517         listHandle = combo.list;
518 
519         if (OS.GTK_VERSION < OS.buildVERSION (2, 4, 0)) {
520             GtkWidget* parentHandle = null;
521             auto temp = listHandle;
522             while ((temp = OS.gtk_widget_get_parent(temp)) !is null) {
523                 parentHandle = temp;
524             }
525             popupHandle = parentHandle;
526             if (popupHandle !is null) {
527                 GtkWidget* modalGroup = getShell().modalGroup;
528                 if (modalGroup !is null) {
529                     OS.gtk_window_group_add_window (modalGroup, popupHandle);
530                 }
531             }
532         }
533         /*
534         * Feature in GTK.  There is no API to query the arrow
535         * handle from a combo box although it is possible to
536         * get the list and text field.  The arrow handle is needed
537         * to hook events.  The fix is to find the first child that is
538         * not the entry or list and assume this is the arrow handle.
539         */
540         auto list = OS.gtk_container_get_children (handle);
541         if (list !is null) {
542             int i = 0, count = OS.g_list_length (list);
543             while (i<count) {
544                 auto childHandle = OS.g_list_nth_data (list, i);
545                 if (childHandle !is entryHandle && childHandle !is listHandle) {
546                     buttonHandle = cast(GtkWidget*)childHandle;
547                     break;
548                 }
549                 i++;
550             }
551             OS.g_list_free (list);
552         }
553 
554         bool editable = (style & SWT.READ_ONLY) is 0;
555         OS.gtk_editable_set_editable (entryHandle, editable);
556         OS.gtk_combo_disable_activate (handle);
557         OS.gtk_combo_set_case_sensitive (handle, true);
558     }
559 }
560 
561 /**
562  * Cuts the selected text.
563  * <p>
564  * The current selection is first copied to the
565  * clipboard and then deleted from the widget.
566  * </p>
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  * @since 2.1
574  */
575 public void cut () {
576     checkWidget ();
577     if (entryHandle !is null) OS.gtk_editable_cut_clipboard (entryHandle);
578 }
579 
580 override void deregister () {
581     super.deregister ();
582     if (buttonHandle !is null) display.removeWidget (buttonHandle);
583     if (entryHandle !is null) display.removeWidget (entryHandle);
584     if (listHandle !is null) display.removeWidget (listHandle);
585     auto imContext = imContext ();
586     if (imContext !is null) display.removeWidget (cast(GtkWidget*)imContext);
587 }
588 
589 override bool filterKey (int keyval, GdkEventKey* event) {
590     int time = OS.gdk_event_get_time (cast(GdkEvent*)event);
591     if (time !is lastEventTime) {
592         lastEventTime = time;
593         auto imContext = imContext ();
594         if (imContext !is null) {
595             return cast(bool)OS.gtk_im_context_filter_keypress (imContext, event);
596         }
597     }
598     gdkEventKey = event;
599     return false;
600 }
601 
602 GtkWidget* findPopupHandle (GList* oldList) {
603     GtkWidget* hdl = null;
604     GList* currentList = OS.gtk_window_list_toplevels();
605     GList* oldFromList = oldList;
606     GList* newFromList = currentList;
607     bool isFound;
608     while (newFromList !is null) {
609         void* newToplevel = OS.g_list_data(newFromList);
610         isFound = false;
611         oldFromList = oldList;
612         while (oldFromList !is null) {
613             void* oldToplevel = OS.g_list_data(oldFromList);
614             if (newToplevel is oldToplevel) {
615                 isFound = true;
616                 break;
617             }
618             oldFromList = OS.g_list_next(oldFromList);
619         }
620         if (!isFound) {
621             hdl = cast(GtkWidget*)newToplevel;
622             break;
623         }
624         newFromList = OS.g_list_next(newFromList);
625     }
626     OS.g_list_free(oldList);
627     OS.g_list_free(currentList);
628     return hdl;
629 }
630 
631 override void fixModal (GtkWidget* group, GtkWidget* modalGroup) {
632     if (popupHandle !is null) {
633         if (group !is null) {
634             OS.gtk_window_group_add_window (group, popupHandle);
635         } else {
636             if (modalGroup !is null) {
637                 OS.gtk_window_group_remove_window (modalGroup, popupHandle);
638             }
639         }
640     }
641 }
642 
643 void fixIM () {
644     /*
645     *  The IM filter has to be called one time for each key press event.
646     *  When the IM is open the key events are duplicated. The first event
647     *  is filtered by SWT and the second event is filtered by GTK.  In some
648     *  cases the GTK handler does not run (the widget is destroyed, the
649     *  application code consumes the event, etc), for these cases the IM
650     *  filter has to be called by SWT.
651     */
652     if (gdkEventKey !is null && gdkEventKey !is cast(GdkEventKey*)-1) {
653         auto imContext = imContext ();
654         if (imContext !is null) {
655             OS.gtk_im_context_filter_keypress (imContext, gdkEventKey);
656             gdkEventKey = cast(GdkEventKey*)-1;
657             return;
658         }
659     }
660     gdkEventKey = null;
661 }
662 
663 override GtkWidget* fontHandle () {
664     if (entryHandle !is null) return entryHandle;
665     return super.fontHandle ();
666 }
667 
668 override GtkWidget* focusHandle () {
669     if (OS.GTK_VERSION >= OS.buildVERSION (2, 4, 0)) {
670         if ((style & SWT.READ_ONLY) !is 0 && buttonHandle !is null) return buttonHandle;
671     }
672     if (entryHandle !is null) return entryHandle;
673     return super.focusHandle ();
674 }
675 
676 override bool hasFocus () {
677     if (super.hasFocus ()) return true;
678     if (entryHandle !is null && OS.GTK_WIDGET_HAS_FOCUS (entryHandle)) return true;
679     if (listHandle !is null && OS.GTK_WIDGET_HAS_FOCUS (listHandle)) return true;
680     return false;
681 }
682 
683 override void hookEvents () {
684     super.hookEvents ();
685     if (OS.GTK_VERSION >= OS.buildVERSION(2, 4, 0)) {
686         OS.g_signal_connect_closure (handle, OS.changed.ptr, display.closures [CHANGED], true);
687     }
688 
689     if (entryHandle !is null) {
690         OS.g_signal_connect_closure (entryHandle, OS.changed.ptr, display.closures [CHANGED], true);
691         OS.g_signal_connect_closure (entryHandle, OS.insert_text.ptr, display.closures [INSERT_TEXT], false);
692         OS.g_signal_connect_closure (entryHandle, OS.delete_text.ptr, display.closures [DELETE_TEXT], false);
693         OS.g_signal_connect_closure (entryHandle, OS.activate.ptr, display.closures [ACTIVATE], false);
694         OS.g_signal_connect_closure (entryHandle, OS.populate_popup.ptr, display.closures [POPULATE_POPUP], false);
695     }
696     int eventMask = OS.GDK_POINTER_MOTION_MASK | OS.GDK_BUTTON_PRESS_MASK |
697         OS.GDK_BUTTON_RELEASE_MASK;
698     GtkWidget*[] handles = [ buttonHandle, entryHandle, listHandle ];
699     for (int i=0; i<handles.length; i++) {
700         auto eventHandle = handles [i];
701         if (eventHandle !is null) {
702             /* Connect the mouse signals */
703             OS.gtk_widget_add_events (eventHandle, eventMask);
704             OS.g_signal_connect_closure_by_id (eventHandle, display.signalIds [BUTTON_PRESS_EVENT], 0, display.closures [BUTTON_PRESS_EVENT], false);
705             OS.g_signal_connect_closure_by_id (eventHandle, display.signalIds [BUTTON_RELEASE_EVENT], 0, display.closures [BUTTON_RELEASE_EVENT], false);
706             OS.g_signal_connect_closure_by_id (eventHandle, display.signalIds [MOTION_NOTIFY_EVENT], 0, display.closures [MOTION_NOTIFY_EVENT], false);
707             /*
708             * Feature in GTK.  Events such as mouse move are propagated up
709             * the widget hierarchy and are seen by the parent.  This is the
710             * correct GTK behavior but not correct for SWT.  The fix is to
711             * hook a signal after and stop the propagation using a negative
712             * event number to distinguish this case.
713             */
714             OS.g_signal_connect_closure_by_id (eventHandle, display.signalIds [BUTTON_PRESS_EVENT], 0, display.closures [BUTTON_PRESS_EVENT_INVERSE], true);
715             OS.g_signal_connect_closure_by_id (eventHandle, display.signalIds [BUTTON_RELEASE_EVENT], 0, display.closures [BUTTON_RELEASE_EVENT_INVERSE], true);
716             OS.g_signal_connect_closure_by_id (eventHandle, display.signalIds [MOTION_NOTIFY_EVENT], 0, display.closures [MOTION_NOTIFY_EVENT_INVERSE], true);
717 
718             /* Connect the event_after signal for both key and mouse */
719             if (eventHandle !is focusHandle ()) {
720                 OS.g_signal_connect_closure_by_id (eventHandle, display.signalIds [EVENT_AFTER], 0, display.closures [EVENT_AFTER], false);
721             }
722         }
723     }
724     auto imContext = imContext ();
725     if (imContext !is null) {
726         OS.g_signal_connect_closure (imContext, OS.commit.ptr, display.closures [COMMIT], false);
727         int id = OS.g_signal_lookup (OS.commit.ptr, OS.gtk_im_context_get_type ());
728         int blockMask =  OS.G_SIGNAL_MATCH_DATA | OS.G_SIGNAL_MATCH_ID;
729         OS.g_signal_handlers_block_matched (imContext, blockMask, id, 0, null, null, entryHandle);
730     }
731 }
732 
733 GtkIMContext* imContext () {
734     return entryHandle !is null ? OS.GTK_ENTRY_IM_CONTEXT (entryHandle) : null;
735 }
736 
737 /**
738  * Deselects the item at the given zero-relative index in the receiver's
739  * list.  If the item at the index was already deselected, it remains
740  * deselected. Indices that are out of range are ignored.
741  *
742  * @param index the index of the item to deselect
743  *
744  * @exception SWTException <ul>
745  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
746  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
747  * </ul>
748  */
749 public void deselect (int index) {
750     checkWidget();
751     if (index < 0 || index >= items.length) return;
752     if (OS.GTK_VERSION >= OS.buildVERSION (2, 4, 0)) {
753         if (OS.gtk_combo_box_get_active (handle) is index) {
754             clearText ();
755         }
756     } else {
757         ignoreSelect = true;
758         auto children = OS.gtk_container_get_children (listHandle);
759         auto item = OS.g_list_nth_data (children, index);
760         bool selected = OS.GTK_WIDGET_STATE (item) is OS.GTK_STATE_SELECTED;
761         if (selected) {
762             OS.gtk_list_unselect_all (listHandle);
763             OS.gtk_entry_set_text (entryHandle, "".ptr );
764         }
765         OS.g_list_free (children);
766         ignoreSelect = false;
767     }
768 }
769 
770 /**
771  * Deselects all selected items in the receiver's list.
772  * <p>
773  * Note: To clear the selection in the receiver's text field,
774  * use <code>clearSelection()</code>.
775  * </p>
776  *
777  * @exception SWTException <ul>
778  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
779  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
780  * </ul>
781  *
782  * @see #clearSelection
783  */
784 public void deselectAll () {
785     checkWidget();
786     if (OS.GTK_VERSION >= OS.buildVERSION (2, 4, 0)) {
787         clearText ();
788     } else {
789         ignoreSelect = true;
790         OS.gtk_list_unselect_all (listHandle);
791         OS.gtk_entry_set_text (entryHandle, "".ptr );
792         ignoreSelect = false;
793     }
794 }
795 
796 
797 override bool dragDetect(int x, int y, bool filter, bool* consume) {
798     if (filter && entryHandle !is null) {
799         int index;
800         int trailing;
801         auto layout = OS.gtk_entry_get_layout (entryHandle);
802         OS.pango_layout_xy_to_index (layout, x * OS.PANGO_SCALE, y * OS.PANGO_SCALE, &index, &trailing);
803         auto ptr = OS.pango_layout_get_text (layout);
804         auto position = OS.g_utf8_pointer_to_offset (ptr, ptr + index) + trailing;
805         Point selection = getSelection ();
806         if (selection.x <= position && position < selection.y) {
807             if (super.dragDetect (x, y, filter, consume)) {
808                 if (consume !is null) *consume = true;
809                 return true;
810             }
811         }
812         return false;
813     }
814     return super.dragDetect (x, y, filter, consume);
815 }
816 
817 override GtkWidget* enterExitHandle () {
818     return fixedHandle;
819 }
820 
821 override GdkDrawable* eventWindow () {
822     return paintWindow ();
823 }
824 
825 override GdkColor* getBackgroundColor () {
826     return getBaseColor ();
827 }
828 
829 override GdkColor* getForegroundColor () {
830     return getTextColor ();
831 }
832 
833 /**
834  * Returns the item at the given, zero-relative index in the
835  * receiver's list. Throws an exception if the index is out
836  * of range.
837  *
838  * @param index the index of the item to return
839  * @return the item at the given index
840  *
841  * @exception IllegalArgumentException <ul>
842  *    <li>ERROR_INVALID_RANGE - if the index is not between 0 and the number of elements in the list minus 1 (inclusive)</li>
843  * </ul>
844  * @exception SWTException <ul>
845  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
846  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
847  * </ul>
848  */
849 public String getItem (int index) {
850     checkWidget();
851     if (!(0 <= index && index < items.length)) {
852         error (SWT.ERROR_INVALID_RANGE);
853     }
854     return items [index];
855 }
856 
857 /**
858  * Returns the number of items contained in the receiver's list.
859  *
860  * @return the number of items
861  *
862  * @exception SWTException <ul>
863  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
864  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
865  * </ul>
866  */
867 public int getItemCount () {
868     checkWidget();
869     return cast(int)/*64bit*/items.length;
870 }
871 
872 /**
873  * Returns the height of the area which would be used to
874  * display <em>one</em> of the items in the receiver's list.
875  *
876  * @return the height of one item
877  *
878  * @exception SWTException <ul>
879  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
880  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
881  * </ul>
882  */
883 public int getItemHeight () {
884     checkWidget();
885     return fontHeight (getFontDescription (), listHandle !is null ? listHandle : handle);
886 }
887 
888 /**
889  * Returns a (possibly empty) array of <code>String</code>s which are
890  * the items in the receiver's list.
891  * <p>
892  * Note: This is not the actual structure used by the receiver
893  * to maintain its list of items, so modifying the array will
894  * not affect the receiver.
895  * </p>
896  *
897  * @return the items in the receiver's list
898  *
899  * @exception SWTException <ul>
900  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
901  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
902  * </ul>
903  */
904 public String [] getItems () {
905     checkWidget();
906     String [] result = new String[](items.length);
907     System.arraycopy (items, 0, result, 0, items.length);
908     return result;
909 }
910 
911 /**
912  * Returns <code>true</code> if the receiver's list is visible,
913  * and <code>false</code> otherwise.
914  * <p>
915  * If one of the receiver's ancestors is not visible or some
916  * other condition makes the receiver not visible, this method
917  * may still indicate that it is considered visible even though
918  * it may not actually be showing.
919  * </p>
920  *
921  * @return the receiver's list's visibility state
922  *
923  * @exception SWTException <ul>
924  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
925  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
926  * </ul>
927  *
928  * @since 3.4
929  */
930 public bool getListVisible () {
931     checkWidget ();
932     if (OS.GTK_VERSION >= OS.buildVERSION (2, 4, 0)) {
933         return popupHandle !is null && OS.GTK_WIDGET_VISIBLE (popupHandle);
934     }
935     return false;
936 }
937 
938 override
939 String getNameText () {
940     return getText ();
941 }
942 
943 /**
944  * Returns the orientation of the receiver.
945  *
946  * @return the orientation style
947  *
948  * @exception SWTException <ul>
949  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
950  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
951  * </ul>
952  *
953  * @since 2.1.2
954  */
955 public int getOrientation () {
956     checkWidget();
957     return style & (SWT.LEFT_TO_RIGHT | SWT.RIGHT_TO_LEFT);
958 }
959 
960 /**
961  * Returns a <code>Point</code> whose x coordinate is the
962  * character position representing the start of the selection
963  * in the receiver's text field, and whose y coordinate is the
964  * character position representing the end of the selection.
965  * An "empty" selection is indicated by the x and y coordinates
966  * having the same value.
967  * <p>
968  * Indexing is zero based.  The range of a selection is from
969  * 0..N where N is the number of characters in the widget.
970  * </p>
971  *
972  * @return a point representing the selection start and end
973  *
974  * @exception SWTException <ul>
975  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
976  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
977  * </ul>
978  */
979 public Point getSelection () {
980     checkWidget ();
981     if ((style & SWT.READ_ONLY) !is 0) {
982         size_t length = 0;
983         if (OS.GTK_VERSION >= OS.buildVERSION (2, 4, 0)) {
984             int index = OS.gtk_combo_box_get_active (handle);
985             if (index !is -1) length = getItem (index).length;
986         } else {
987             auto str = OS.gtk_entry_get_text (entryHandle);
988             if (str !is null) length = OS.g_utf8_strlen (str, -1);
989         }
990         return new Point (0, cast(int)/*64bit*/length);
991     }
992     int start;
993     int end;
994     if (entryHandle !is null) {
995         OS.gtk_editable_get_selection_bounds (entryHandle, &start, &end);
996     }
997     return new Point(start, end);
998 }
999 
1000 /**
1001  * Returns the zero-relative index of the item which is currently
1002  * selected in the receiver's list, or -1 if no item is selected.
1003  *
1004  * @return the index of the selected item
1005  *
1006  * @exception SWTException <ul>
1007  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1008  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1009  * </ul>
1010  */
1011 public int getSelectionIndex () {
1012     checkWidget();
1013     if (OS.GTK_VERSION >= OS.buildVERSION (2, 4, 0)) {
1014         return OS.gtk_combo_box_get_active (handle);
1015     }
1016     int index = 0, result = -1;
1017     auto children = OS.gtk_container_get_children (listHandle);
1018     auto temp = children;
1019     while (temp !is null) {
1020         auto item = OS.g_list_data (temp);
1021         if (OS.GTK_WIDGET_STATE (item) is OS.GTK_STATE_SELECTED) {
1022             result = index;
1023             break;
1024         }
1025         index++;
1026         temp = OS.g_list_next (temp);
1027     }
1028     OS.g_list_free (children);
1029     return result;
1030 }
1031 
1032 /**
1033  * Returns a string containing a copy of the contents of the
1034  * receiver's text field, or an empty string if there are no
1035  * contents.
1036  *
1037  * @return the receiver's text
1038  *
1039  * @exception SWTException <ul>
1040  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1041  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1042  * </ul>
1043  */
1044 public String getText () {
1045     checkWidget();
1046     if (entryHandle !is null) {
1047         auto str = OS.gtk_entry_get_text (entryHandle);
1048         if (str is null) return "";
1049         return fromStringz(str)._idup();
1050     } else {
1051         int index = OS.gtk_combo_box_get_active (handle);
1052         return index !is -1 ? getItem (index) : "";
1053     }
1054 }
1055 
1056 String getText (int start, int stop) {
1057     /*
1058     * NOTE: The current implementation uses substring ()
1059     * which can reference a potentially large character
1060     * array.
1061     */
1062     return getText ()[ start .. stop - 1];
1063 }
1064 
1065 /**
1066  * Returns the height of the receivers's text field.
1067  *
1068  * @return the text height
1069  *
1070  * @exception SWTException <ul>
1071  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1072  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1073  * </ul>
1074  */
1075 public int getTextHeight () {
1076     checkWidget();
1077     GtkRequisition requisition;
1078     gtk_widget_size_request (handle, &requisition);
1079     return OS.GTK_WIDGET_REQUISITION_HEIGHT (handle);
1080 }
1081 
1082 /**
1083  * Returns the maximum number of characters that the receiver's
1084  * text field is capable of holding. If this has not been changed
1085  * by <code>setTextLimit()</code>, it will be the constant
1086  * <code>Combo.LIMIT</code>.
1087  *
1088  * @return the text limit
1089  *
1090  * @exception SWTException <ul>
1091  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1092  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1093  * </ul>
1094  *
1095  * @see #LIMIT
1096  */
1097 public int getTextLimit () {
1098     checkWidget();
1099     int limit = entryHandle !is null ? OS.gtk_entry_get_max_length (entryHandle) : 0;
1100     return limit is 0 ? LIMIT : limit;
1101 }
1102 
1103 /**
1104  * Gets the number of items that are visible in the drop
1105  * down portion of the receiver's list.
1106  * <p>
1107  * Note: This operation is a hint and is not supported on
1108  * platforms that do not have this concept.
1109  * </p>
1110  *
1111  * @return the number of items that are visible
1112  *
1113  * @exception SWTException <ul>
1114  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1115  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1116  * </ul>
1117  *
1118  * @since 3.0
1119  */
1120 public int getVisibleItemCount () {
1121     checkWidget ();
1122     return visibleCount;
1123 }
1124 
1125 override int gtk_activate (GtkWidget* widget) {
1126     postEvent (SWT.DefaultSelection);
1127     return 0;
1128 }
1129 
1130 override int gtk_button_press_event (GtkWidget* widget, GdkEventButton* event) {
1131     /*
1132     * Feature in GTK. Depending on where the user clicks, GTK prevents
1133     * the left mouse button event from being propagated. The fix is to
1134     * send the mouse event from the event_after handler.
1135     */
1136     if (OS.GTK_VERSION >= OS.buildVERSION (2, 4, 0)) {
1137         GdkEventButton* gdkEvent = event;
1138         if (gdkEvent.type is OS.GDK_BUTTON_PRESS && gdkEvent.button is 1 && (style & SWT.READ_ONLY) !is 0) {
1139             return gtk_button_press_event(widget, event, false);
1140         }
1141 
1142     }
1143     return super.gtk_button_press_event (widget, event);
1144 }
1145 
1146 override int gtk_changed (GtkWidget* widget) {
1147     if (OS.GTK_VERSION >= OS.buildVERSION (2, 4, 0)) {
1148         if (widget is handle) {
1149             if (entryHandle is null) {
1150                 sendEvent(SWT.Modify);
1151                 if (isDisposed ()) return 0;
1152             }
1153             /*
1154             * Feature in GTK.  GTK emits a changed signal whenever
1155             * the contents of a combo box are altered by typing or
1156             * by selecting an item in the list, but the event should
1157             * only be sent when the list is selected. The fix is to
1158             * only send out a selection event when there is a selected
1159             * item.
1160             *
1161             * NOTE: This code relies on GTK clearing the selected
1162             * item and not matching the item as the user types.
1163             */
1164             int index = OS.gtk_combo_box_get_active (handle);
1165             if (index !is -1) postEvent (SWT.Selection);
1166             return 0;
1167         }
1168     } else {
1169         if (!ignoreSelect) {
1170             auto ptr = OS.gtk_entry_get_text (entryHandle);
1171             String text = fromStringz(ptr)._idup();
1172             for (int i = 0; i < items.length; i++) {
1173                 if (items [i] ==/*eq*/ text) {
1174                     postEvent (SWT.Selection);
1175                     break;
1176                 }
1177             }
1178         }
1179     }
1180     /*
1181     * Feature in GTK.  When the user types, GTK positions
1182     * the caret after sending the changed signal.  This
1183     * means that application code that attempts to position
1184     * the caret during a changed signal will fail.  The fix
1185     * is to post the modify event when the user is typing.
1186     */
1187     bool keyPress = false;
1188     auto eventPtr = OS.gtk_get_current_event ();
1189     if (eventPtr !is null) {
1190         GdkEventKey* gdkEvent = cast(GdkEventKey*)eventPtr;
1191         switch (gdkEvent.type) {
1192             case OS.GDK_KEY_PRESS:
1193                 keyPress = true;
1194                 break;
1195             default:
1196         }
1197         OS.gdk_event_free (eventPtr);
1198     }
1199     if (keyPress) {
1200         postEvent (SWT.Modify);
1201     } else {
1202         sendEvent (SWT.Modify);
1203     }
1204     return 0;
1205 }
1206 
1207 override int gtk_commit (GtkIMContext* imContext, char* text) {
1208     if (text is null) return 0;
1209     if (!OS.gtk_editable_get_editable (entryHandle)) return 0;
1210     char [] chars = fromStringz(text);
1211     if (chars.length is 0) return 0;
1212     char [] newChars = sendIMKeyEvent (SWT.KeyDown, null, chars);
1213     if (newChars is null) return 0;
1214     /*
1215     * Feature in GTK.  For a GtkEntry, during the insert-text signal,
1216     * GTK allows the programmer to change only the caret location,
1217     * not the selection.  If the programmer changes the selection,
1218     * the new selection is lost.  The fix is to detect a selection
1219     * change and set it after the insert-text signal has completed.
1220     */
1221     fixStart = fixEnd = -1;
1222     OS.g_signal_handlers_block_matched (imContext, OS.G_SIGNAL_MATCH_DATA, 0, 0, null, null, udCOMMIT);
1223     int id = OS.g_signal_lookup (OS.commit.ptr, OS.gtk_im_context_get_type ());
1224     int mask =  OS.G_SIGNAL_MATCH_DATA | OS.G_SIGNAL_MATCH_ID;
1225     OS.g_signal_handlers_unblock_matched (imContext, mask, id, 0, null, null, entryHandle);
1226     if (newChars is chars) {
1227         OS.g_signal_emit_by_name1 (imContext, OS.commit.ptr, cast(int)text);
1228     } else {
1229         OS.g_signal_emit_by_name1 (imContext, OS.commit.ptr, cast(int)toStringz(newChars));
1230     }
1231     OS.g_signal_handlers_unblock_matched (imContext, OS.G_SIGNAL_MATCH_DATA, 0, 0, null, null, udCOMMIT);
1232     OS.g_signal_handlers_block_matched (imContext, mask, id, 0, null, null, entryHandle);
1233     if (fixStart !is -1 && fixEnd !is -1) {
1234         OS.gtk_editable_set_position (entryHandle, fixStart);
1235         OS.gtk_editable_select_region (entryHandle, fixStart, fixEnd);
1236     }
1237     fixStart = fixEnd = -1;
1238     return 0;
1239 }
1240 
1241 override int gtk_delete_text (GtkWidget* widget, ptrdiff_t start_pos, ptrdiff_t end_pos) {
1242     if (lockText) {
1243         OS.gtk_list_unselect_item (listHandle, 0);
1244         OS.g_signal_stop_emission_by_name (entryHandle, OS.delete_text.ptr);
1245         return 0;
1246     }
1247     if (!hooks (SWT.Verify) && !filters (SWT.Verify)) return 0;
1248     String newText = verifyText ("", cast(int)/*64bit*/start_pos, cast(int)/*64bit*/end_pos);
1249     if (newText is null) {
1250         OS.g_signal_stop_emission_by_name (entryHandle, OS.delete_text.ptr);
1251     } else {
1252         if (newText.length > 0) {
1253             int pos;
1254             pos = cast(int)/*64bit*/end_pos;
1255             OS.g_signal_handlers_block_matched (entryHandle, OS.G_SIGNAL_MATCH_DATA, 0, 0, null, null, udCHANGED);
1256             OS.g_signal_handlers_block_matched (entryHandle, OS.G_SIGNAL_MATCH_DATA, 0, 0, null, null, udINSERT_TEXT);
1257             OS.gtk_editable_insert_text (entryHandle, newText.ptr, cast(int)/*64bit*/newText.length, &pos);
1258             OS.g_signal_handlers_unblock_matched (entryHandle, OS.G_SIGNAL_MATCH_DATA, 0, 0, null, null, udINSERT_TEXT);
1259             OS.g_signal_handlers_unblock_matched (entryHandle, OS.G_SIGNAL_MATCH_DATA, 0, 0, null, null, udCHANGED);
1260             OS.gtk_editable_set_position (entryHandle, pos );
1261         }
1262     }
1263     return 0;
1264 }
1265 
1266 override int gtk_event_after (GtkWidget* widget, GdkEvent* event) {
1267     /*
1268     * Feature in GTK. Depending on where the user clicks, GTK prevents
1269     * the left mouse button event from being propagated. The fix is to
1270     * send the mouse event from the event_after handler.
1271     *
1272     * Feature in GTK. When the user clicks anywhere in an editable
1273     * combo box, a single focus event should be issued, despite the
1274     * fact that focus might switch between the drop down button and
1275     * the text field. The fix is to use gtk_combo_box_set_focus_on_click ()
1276     * to eat all focus events while focus is in the combo box. When the
1277     * user clicks on the drop down button focus is assigned to the text
1278     * field.
1279     */
1280     if (OS.GTK_VERSION >= OS.buildVERSION (2, 4, 0)) {
1281         switch (event.type) {
1282             case OS.GDK_BUTTON_PRESS: {
1283                 GdkEventButton* gdkEventButton = cast(GdkEventButton*)event;
1284                 if (gdkEventButton.button is 1) {
1285                     if ((style & SWT.READ_ONLY) !is 0 && !sendMouseEvent (SWT.MouseDown, gdkEventButton.button, display.clickCount, 0, false, gdkEventButton.time, gdkEventButton.x_root, gdkEventButton.y_root, false, gdkEventButton.state)) {
1286                         return 1;
1287                     }
1288                     if (OS.GTK_VERSION >= OS.buildVERSION (2, 6, 0)) {
1289                         if ((style & SWT.READ_ONLY) is 0 && widget is buttonHandle) {
1290                             OS.gtk_widget_grab_focus (entryHandle);
1291                         }
1292                     }
1293                 }
1294                 break;
1295             }
1296             case OS.GDK_FOCUS_CHANGE: {
1297                 if (OS.GTK_VERSION >= OS.buildVERSION (2, 6, 0)) {
1298                     if ((style & SWT.READ_ONLY) is 0) {
1299                         GdkEventFocus* gdkEventFocus = cast(GdkEventFocus*)event;
1300                         if (gdkEventFocus.in_ !is 0) {
1301                             OS.gtk_combo_box_set_focus_on_click (handle, false);
1302                         } else {
1303                             OS.gtk_combo_box_set_focus_on_click (handle, true);
1304                         }
1305                     }
1306                 }
1307                 break;
1308             }
1309             default:
1310         }
1311     }
1312     return super.gtk_event_after(widget, event);
1313 }
1314 
1315 override int gtk_focus_out_event (GtkWidget* widget, GdkEventFocus* event) {
1316     fixIM ();
1317     return super.gtk_focus_out_event (widget, event);
1318 }
1319 
1320 override int gtk_insert_text (GtkEditable* widget, char* new_text, ptrdiff_t new_text_length, ptrdiff_t position) {
1321     if (lockText) {
1322         OS.gtk_list_unselect_item (listHandle, 0);
1323         OS.g_signal_stop_emission_by_name (entryHandle, OS.insert_text.ptr);
1324         return 0;
1325     }
1326     if (!hooks (SWT.Verify) && !filters (SWT.Verify)) return 0;
1327     if (new_text is null || new_text_length is 0) return 0;
1328     String oldText = new_text[0..new_text_length]._idup();
1329     int pos;
1330     pos = cast(int)/*64bit*/position;
1331     if (pos is -1) {
1332         auto ptr = OS.gtk_entry_get_text (entryHandle);
1333         pos = cast(int)/*64bit*/fromStringz(ptr).length;
1334     }
1335     String newText = verifyText (oldText, pos, pos);
1336     if (newText !is oldText) {
1337         int newStart, newEnd;
1338         OS.gtk_editable_get_selection_bounds (entryHandle, &newStart, &newEnd);
1339         if (newText !is null) {
1340             if (newStart !is newEnd) {
1341                 OS.g_signal_handlers_block_matched (entryHandle, OS.G_SIGNAL_MATCH_DATA, 0, 0, null, null, udDELETE_TEXT);
1342                 OS.g_signal_handlers_block_matched (entryHandle, OS.G_SIGNAL_MATCH_DATA, 0, 0, null, null, udCHANGED);
1343                 OS.gtk_editable_delete_selection (entryHandle);
1344                 OS.g_signal_handlers_unblock_matched (entryHandle, OS.G_SIGNAL_MATCH_DATA, 0, 0, null, null, udDELETE_TEXT);
1345                 OS.g_signal_handlers_unblock_matched (entryHandle, OS.G_SIGNAL_MATCH_DATA, 0, 0, null, null, udCHANGED);
1346             }
1347             OS.g_signal_handlers_block_matched (entryHandle, OS.G_SIGNAL_MATCH_DATA, 0, 0, null, null, udINSERT_TEXT);
1348             OS.gtk_editable_insert_text (entryHandle, newText.ptr, cast(int)/*64bit*/newText.length, &pos);
1349             OS.g_signal_handlers_unblock_matched (entryHandle, OS.G_SIGNAL_MATCH_DATA, 0, 0, null, null, udINSERT_TEXT);
1350             newStart = newEnd = pos;
1351         }
1352         pos = newEnd;
1353         if (newStart !is newEnd) {
1354             fixStart = newStart;
1355             fixEnd = newEnd;
1356         }
1357         position = pos;
1358         OS.g_signal_stop_emission_by_name (entryHandle, OS.insert_text.ptr);
1359     }
1360     return 0;
1361 }
1362 
1363 override int gtk_key_press_event (GtkWidget* widget, GdkEventKey* event) {
1364     auto result = super.gtk_key_press_event (widget, event);
1365     if (result !is 0) fixIM ();
1366     if (gdkEventKey is cast(GdkEventKey*)-1) result = 1;
1367     gdkEventKey = null;
1368     if (OS.GTK_VERSION >= OS.buildVERSION (2, 4, 0) && (style & SWT.READ_ONLY) is 0) {
1369         GdkEventKey* keyEvent = cast(GdkEventKey*)event;
1370         int oldIndex = OS.gtk_combo_box_get_active (handle);
1371         int newIndex = oldIndex;
1372         int key = keyEvent.keyval;
1373         switch (key) {
1374             case OS.GDK_Down:
1375             case OS.GDK_KP_Down:
1376                  if (oldIndex !is (items.length - 1)) {
1377                     newIndex = oldIndex + 1;
1378                  }
1379                  break;
1380             case OS.GDK_Up:
1381             case OS.GDK_KP_Up:
1382                 if (oldIndex !is -1 && oldIndex !is 0) {
1383                     newIndex = oldIndex - 1;
1384                 }
1385                 break;
1386             /*
1387             * Feature in GTK. In gtk_combo_box, the PageUp and PageDown keys
1388             * go the first and last items in the list rather than scrolling
1389             * a page at a time. The fix is to emulate this behavior for
1390             * gtk_combo_box_entry.
1391             */
1392             case OS.GDK_Page_Up:
1393             case OS.GDK_KP_Page_Up:
1394                 newIndex = 0;
1395                 break;
1396             case OS.GDK_Page_Down:
1397             case OS.GDK_KP_Page_Down:
1398                 newIndex = cast(int)/*64bit*/items.length - 1;
1399                 break;
1400             default:
1401         }
1402         if (newIndex !is oldIndex) {
1403             OS.g_signal_handlers_block_matched (handle, OS.G_SIGNAL_MATCH_DATA, 0, 0, null, null, udCHANGED);
1404             OS.gtk_combo_box_set_active (handle, newIndex);
1405             OS.g_signal_handlers_unblock_matched (handle, OS.G_SIGNAL_MATCH_DATA, 0, 0, null, null, udCHANGED);
1406             return 1;
1407         }
1408     }
1409     return result;
1410 }
1411 
1412 override int gtk_populate_popup (GtkWidget* widget, GtkWidget* menu) {
1413     if ((style & SWT.RIGHT_TO_LEFT) !is 0) {
1414         OS.gtk_widget_set_direction (menu, OS.GTK_TEXT_DIR_RTL);
1415         display.doSetDirectionProc(menu, OS.GTK_TEXT_DIR_RTL);
1416     }
1417     return 0;
1418 }
1419 
1420 /**
1421  * Searches the receiver's list starting at the first item
1422  * (index 0) until an item is found that is equal to the
1423  * argument, and returns the index of that item. If no item
1424  * is found, returns -1.
1425  *
1426  * @param string the search item
1427  * @return the index of the item
1428  *
1429  * @exception SWTException <ul>
1430  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1431  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1432  * </ul>
1433  */
1434 public int indexOf (String string) {
1435     checkWidget();
1436     return indexOf (string, 0);
1437 }
1438 
1439 /**
1440  * Searches the receiver's list starting at the given,
1441  * zero-relative index until an item is found that is equal
1442  * to the argument, and returns the index of that item. If
1443  * no item is found or the starting index is out of range,
1444  * returns -1.
1445  *
1446  * @param string the search item
1447  * @param start the zero-relative index at which to begin the search
1448  * @return the index of the item
1449  *
1450  * @exception SWTException <ul>
1451  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1452  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1453  * </ul>
1454  */
1455 public int indexOf (String string, int start) {
1456     checkWidget();
1457     // SWT extension: allow null for zero length string
1458     //if (string is null) error (SWT.ERROR_NULL_ARGUMENT);
1459     if (!(0 <= start && start < items.length)) return -1;
1460     for (int i=start; i<items.length; i++) {
1461         if (string.equals (items [i])) return i;
1462     }
1463     return -1;
1464 }
1465 
1466 override bool isFocusHandle(GtkWidget* widget) {
1467     if (OS.GTK_VERSION >= OS.buildVERSION (2, 4, 0)) {
1468         if (buttonHandle !is null && widget is buttonHandle) return true;
1469         if (entryHandle !is null && widget is entryHandle) return true;
1470     }
1471     return super.isFocusHandle (widget);
1472 }
1473 
1474 override GdkDrawable* paintWindow () {
1475     auto childHandle =  entryHandle !is null ? entryHandle : handle;
1476     OS.gtk_widget_realize (childHandle);
1477     auto window = OS.GTK_WIDGET_WINDOW (childHandle);
1478     if (OS.GTK_VERSION >= OS.buildVERSION (2, 4, 0)) {
1479         if ((style & SWT.READ_ONLY) !is 0) return window;
1480     }
1481     auto children = OS.gdk_window_get_children (window);
1482     if (children !is null) window = cast(GdkDrawable*)OS.g_list_data (children);
1483     OS.g_list_free (children);
1484     return window;
1485 }
1486 
1487 /**
1488  * Pastes text from clipboard.
1489  * <p>
1490  * The selected text is deleted from the widget
1491  * and new text inserted from the clipboard.
1492  * </p>
1493  *
1494  * @exception SWTException <ul>
1495  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1496  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1497  * </ul>
1498  *
1499  * @since 2.1
1500  */
1501 public void paste () {
1502     checkWidget ();
1503     if (entryHandle !is null) OS.gtk_editable_paste_clipboard (entryHandle);
1504 }
1505 
1506 override GtkWidget* parentingHandle() {
1507     return fixedHandle;
1508 }
1509 
1510 override void register () {
1511     super.register ();
1512     if (buttonHandle !is null) display.addWidget (buttonHandle, this);
1513     if (entryHandle !is null) display.addWidget (entryHandle, this);
1514     if (listHandle !is null) display.addWidget (listHandle, this);
1515     auto imContext = imContext ();
1516     if (imContext !is null) display.addWidget (cast(GtkWidget*)imContext, this);
1517 }
1518 
1519 override void releaseHandle () {
1520     super.releaseHandle ();
1521     buttonHandle = entryHandle = listHandle = null;
1522 }
1523 
1524 override void releaseWidget () {
1525     super.releaseWidget ();
1526     textRenderer = null;
1527     fixIM ();
1528 }
1529 
1530 /**
1531  * Removes the item from the receiver's list at the given
1532  * zero-relative index.
1533  *
1534  * @param index the index for the item
1535  *
1536  * @exception IllegalArgumentException <ul>
1537  *    <li>ERROR_INVALID_RANGE - if the index is not between 0 and the number of elements in the list minus 1 (inclusive)</li>
1538  * </ul>
1539  * @exception SWTException <ul>
1540  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1541  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1542  * </ul>
1543  */
1544 public void remove (int index) {
1545     checkWidget();
1546     if (!(0 <= index && index < items.length)) {
1547         error (SWT.ERROR_INVALID_RANGE);
1548     }
1549     String [] oldItems = items;
1550     String [] newItems = new String[]( oldItems.length - 1 );
1551     System.arraycopy (oldItems, 0, newItems, 0, index);
1552     System.arraycopy (oldItems, index + 1, newItems, index, oldItems.length - index - 1);
1553     items = newItems;
1554     if (OS.GTK_VERSION >= OS.buildVERSION (2, 4, 0)) {
1555         if (OS.gtk_combo_box_get_active (handle) is index) clearText ();
1556         OS.gtk_combo_box_remove_text (handle, index);
1557     } else {
1558         ignoreSelect = true;
1559         auto children = OS.gtk_container_get_children (listHandle);
1560         auto item = OS.g_list_nth_data (children, index);
1561         bool selected = OS.GTK_WIDGET_STATE (item) is OS.GTK_STATE_SELECTED;
1562         auto items = OS.g_list_append (null, item);
1563         OS.gtk_list_remove_items (listHandle, items);
1564         OS.g_list_free (items);
1565         OS.g_list_free (children);
1566         if (selected) {
1567             OS.gtk_entry_set_text (entryHandle, "".ptr);
1568         }
1569         ignoreSelect = false;
1570     }
1571 }
1572 
1573 /**
1574  * Removes the items from the receiver's list which are
1575  * between the given zero-relative start and end
1576  * indices (inclusive).
1577  *
1578  * @param start the start of the range
1579  * @param end the end of the range
1580  *
1581  * @exception IllegalArgumentException <ul>
1582  *    <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>
1583  * </ul>
1584  * @exception SWTException <ul>
1585  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1586  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1587  * </ul>
1588  */
1589 public void remove (int start, int end) {
1590     checkWidget();
1591     if (start > end) return;
1592     if (!(0 <= start && start <= end && end < items.length)) {
1593         error (SWT.ERROR_INVALID_RANGE);
1594     }
1595     String [] oldItems = items;
1596     String [] newItems = new String[](oldItems.length - (end - start + 1));
1597     System.arraycopy (oldItems, 0, newItems, 0, start);
1598     System.arraycopy (oldItems, end + 1, newItems, start, oldItems.length - end - 1);
1599     items = newItems;
1600     if (OS.GTK_VERSION >= OS.buildVERSION (2, 4, 0)) {
1601         int index = OS.gtk_combo_box_get_active (handle);
1602         if (start <= index && index <= end) clearText();
1603         for (int i = end; i >= start; i--) {
1604             OS.gtk_combo_box_remove_text (handle, i);
1605         }
1606     } else {
1607         bool selected = false;
1608         ignoreSelect = true;
1609         GList* items;
1610         auto children = OS.gtk_container_get_children (listHandle);
1611         for (int i = start; i <= end; i++) {
1612             auto item = OS.g_list_nth_data (children, i);
1613             selected |= OS.GTK_WIDGET_STATE (item) is OS.GTK_STATE_SELECTED;
1614             items = OS.g_list_append (items, item);
1615         }
1616         OS.gtk_list_remove_items (listHandle, items);
1617         OS.g_list_free (items);
1618         OS.g_list_free (children);
1619         if (selected) {
1620             OS.gtk_entry_set_text (entryHandle, "".ptr );
1621         }
1622         ignoreSelect = false;
1623     }
1624 }
1625 
1626 /**
1627  * Searches the receiver's list starting at the first item
1628  * until an item is found that is equal to the argument,
1629  * and removes that item from the list.
1630  *
1631  * @param string the item to remove
1632  *
1633  * @exception IllegalArgumentException <ul>
1634  *    <li>ERROR_INVALID_ARGUMENT - if the string is not found in the list</li>
1635  * </ul>
1636  * @exception SWTException <ul>
1637  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1638  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1639  * </ul>
1640  */
1641 public void remove (String string) {
1642     checkWidget();
1643     // SWT extension: allow null for zero length string
1644     //if (string is null) error (SWT.ERROR_NULL_ARGUMENT);
1645     int index = indexOf (string, 0);
1646     if (index is -1) error (SWT.ERROR_INVALID_ARGUMENT);
1647     remove (index);
1648 }
1649 
1650 /**
1651  * Removes all of the items from the receiver's list and clear the
1652  * contents of receiver's text field.
1653  * <p>
1654  * @exception SWTException <ul>
1655  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1656  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1657  * </ul>
1658  */
1659 public void removeAll () {
1660     checkWidget();
1661     auto count = items.length;
1662     items = null;
1663     if (OS.GTK_VERSION >= OS.buildVERSION (2, 4, 0)) {
1664         clearText ();
1665         for (ptrdiff_t i = count - 1; i >= 0; i--) {
1666             OS.gtk_combo_box_remove_text (handle, cast(int)/*64bit*/i);
1667         }
1668     } else {
1669         ignoreSelect = true;
1670         OS.gtk_list_clear_items (listHandle, 0, -1);
1671         OS.gtk_entry_set_text (entryHandle, "".ptr);
1672         ignoreSelect = false;
1673     }
1674 }
1675 
1676 /**
1677  * Removes the listener from the collection of listeners who will
1678  * be notified when the receiver's text is modified.
1679  *
1680  * @param listener the listener which should no longer be notified
1681  *
1682  * @exception IllegalArgumentException <ul>
1683  *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
1684  * </ul>
1685  * @exception SWTException <ul>
1686  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1687  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1688  * </ul>
1689  *
1690  * @see ModifyListener
1691  * @see #addModifyListener
1692  */
1693 public void removeModifyListener (ModifyListener listener) {
1694     checkWidget();
1695     if (listener is null) error (SWT.ERROR_NULL_ARGUMENT);
1696     if (eventTable is null) return;
1697     eventTable.unhook (SWT.Modify, listener);
1698 }
1699 
1700 /**
1701  * Removes the listener from the collection of listeners who will
1702  * be notified when the user changes the receiver's selection.
1703  *
1704  * @param listener the listener which should no longer be notified
1705  *
1706  * @exception IllegalArgumentException <ul>
1707  *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
1708  * </ul>
1709  * @exception SWTException <ul>
1710  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1711  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1712  * </ul>
1713  *
1714  * @see SelectionListener
1715  * @see #addSelectionListener
1716  */
1717 public void removeSelectionListener (SelectionListener listener) {
1718     checkWidget();
1719     if (listener is null) error (SWT.ERROR_NULL_ARGUMENT);
1720     if (eventTable is null) return;
1721     eventTable.unhook (SWT.Selection, listener);
1722     eventTable.unhook (SWT.DefaultSelection,listener);
1723 }
1724 
1725 /**
1726  * Removes the listener from the collection of listeners who will
1727  * be notified when the control is verified.
1728  *
1729  * @param listener the listener which should no longer be notified
1730  *
1731  * @exception IllegalArgumentException <ul>
1732  *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
1733  * </ul>
1734  * @exception SWTException <ul>
1735  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1736  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1737  * </ul>
1738  *
1739  * @see VerifyListener
1740  * @see #addVerifyListener
1741  *
1742  * @since 3.1
1743  */
1744 public void removeVerifyListener (VerifyListener listener) {
1745     checkWidget ();
1746     if (listener is null) error (SWT.ERROR_NULL_ARGUMENT);
1747     if (eventTable is null) return;
1748     eventTable.unhook (SWT.Verify, listener);
1749 }
1750 
1751 /**
1752  * Selects the item at the given zero-relative index in the receiver's
1753  * list.  If the item at the index was already selected, it remains
1754  * selected. Indices that are out of range are ignored.
1755  *
1756  * @param index the index of the item to select
1757  *
1758  * @exception SWTException <ul>
1759  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1760  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1761  * </ul>
1762  */
1763 public void select (int index) {
1764     checkWidget();
1765     if (index < 0 || index >= items.length) return;
1766     if (OS.GTK_VERSION >= OS.buildVERSION (2, 4, 0)) {
1767         OS.g_signal_handlers_block_matched (handle, OS.G_SIGNAL_MATCH_DATA, 0, 0, null, null, udCHANGED);
1768         OS.gtk_combo_box_set_active (handle, index);
1769         OS.g_signal_handlers_unblock_matched (handle, OS.G_SIGNAL_MATCH_DATA, 0, 0, null, null, udCHANGED);
1770         if ((style & SWT.READ_ONLY) !is 0) {
1771             /*
1772             * Feature in GTK. Read Only combo boxes do not get a chance to send out a
1773             * Modify event in the gtk_changed callback. The fix is to send a Modify event
1774             * here.
1775             */
1776             sendEvent (SWT.Modify);
1777         }
1778     } else {
1779         ignoreSelect = true;
1780         OS.gtk_list_select_item (listHandle, index);
1781         ignoreSelect = false;
1782     }
1783 }
1784 
1785 override void setBackgroundColor (GdkColor* color) {
1786     super.setBackgroundColor (color);
1787     if (OS.GTK_VERSION >= OS.buildVERSION (2, 4, 0)) {
1788         if (entryHandle !is null) OS.gtk_widget_modify_base (entryHandle, 0, color);
1789         OS.g_object_set1 (textRenderer, OS.background_gdk.ptr, cast(int)color);
1790     } else {
1791         OS.gtk_widget_modify_base (entryHandle, 0, color);
1792         if (listHandle !is null) OS.gtk_widget_modify_base (listHandle, 0, color);
1793     }
1794 }
1795 
1796 override int setBounds (int x, int y, int width, int height, bool move, bool resize) {
1797     int newHeight = height;
1798     if (resize) newHeight = Math.max (getTextHeight (), height);
1799     return super.setBounds (x, y, width, newHeight, move, resize);
1800 }
1801 
1802 override void setFontDescription (PangoFontDescription* font) {
1803     super.setFontDescription (font);
1804     if (OS.GTK_VERSION >= OS.buildVERSION (2, 4, 0)) {
1805         if (entryHandle !is null) OS.gtk_widget_modify_font (entryHandle, font);
1806         OS.g_object_set1 (textRenderer, OS.font_desc.ptr, cast(int)font);
1807         if ((style & SWT.READ_ONLY) !is 0) {
1808             /*
1809             * Bug in GTK.  Setting the font can leave the combo box with an
1810             * invalid minimum size.  The fix is to temporarily change the
1811             * selected item to force the combo box to resize.
1812             */
1813             int index = OS.gtk_combo_box_get_active (handle);
1814             OS.g_signal_handlers_block_matched (handle, OS.G_SIGNAL_MATCH_DATA, 0, 0, null, null, udCHANGED);
1815             OS.gtk_combo_box_set_active (handle, -1);
1816             OS.gtk_combo_box_set_active (handle, index);
1817             OS.g_signal_handlers_unblock_matched (handle, OS.G_SIGNAL_MATCH_DATA, 0, 0, null, null, udCHANGED);
1818         }
1819     } else {
1820         OS.gtk_widget_modify_font (entryHandle, font);
1821         if (listHandle !is null) {
1822             OS.gtk_widget_modify_font (listHandle, font);
1823             auto itemsList = OS.gtk_container_get_children (listHandle);
1824             if (itemsList !is null) {
1825                 int count = OS.g_list_length (itemsList);
1826                 for (int i=count - 1; i>=0; i--) {
1827                     auto widget = OS.gtk_bin_get_child (OS.g_list_nth_data (itemsList, i));
1828                     OS.gtk_widget_modify_font (widget, font);
1829                 }
1830                 OS.g_list_free (itemsList);
1831             }
1832         }
1833     }
1834 }
1835 
1836 override void setForegroundColor (GdkColor* color) {
1837     super.setForegroundColor (color);
1838     if (OS.GTK_VERSION >= OS.buildVERSION (2, 4, 0)) {
1839         if (entryHandle !is null) setForegroundColor (entryHandle, color);
1840         OS.g_object_set1 (textRenderer, OS.foreground_gdk.ptr, cast(int)color);
1841     } else {
1842         setForegroundColor (entryHandle, color);
1843         if (listHandle !is null) {
1844             setForegroundColor (listHandle, color);
1845             auto itemsList = OS.gtk_container_get_children (listHandle);
1846             if (itemsList !is null) {
1847                 int count = OS.g_list_length (itemsList);
1848                 for (int i=count - 1; i>=0; i--) {
1849                     auto widget = OS.gtk_bin_get_child (OS.g_list_nth_data (itemsList, i));
1850                     setForegroundColor (widget, color);
1851                 }
1852                 OS.g_list_free (itemsList);
1853             }
1854         }
1855     }
1856 }
1857 
1858 /**
1859  * Sets the text of the item in the receiver's list at the given
1860  * zero-relative index to the string argument.
1861  *
1862  * @param index the index for the item
1863  * @param string the new text for the item
1864  *
1865  * @exception IllegalArgumentException <ul>
1866  *    <li>ERROR_INVALID_RANGE - if the index is not between 0 and the number of elements in the list minus 1 (inclusive)</li>
1867  * </ul>
1868  * @exception SWTException <ul>
1869  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1870  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1871  * </ul>
1872  */
1873 public void setItem (int index, String string) {
1874     checkWidget();
1875     // SWT extension: allow null for zero length string
1876     //if (string is null) error (SWT.ERROR_NULL_ARGUMENT);
1877     if (!(0 <= index && index < items.length)) {
1878         error (SWT.ERROR_INVALID_ARGUMENT);
1879     }
1880     items [index] = string;
1881     char* buffer = string.toStringzValidPtr();
1882     if (OS.GTK_VERSION >= OS.buildVERSION (2, 4, 0)) {
1883         OS.gtk_combo_box_remove_text (handle, index);
1884         OS.gtk_combo_box_insert_text (handle, index, buffer);
1885         if ((style & SWT.RIGHT_TO_LEFT) !is 0 && popupHandle !is null) {
1886             display.doSetDirectionProc(popupHandle, OS.GTK_TEXT_DIR_RTL);
1887         }
1888     } else {
1889         ignoreSelect = true;
1890         auto children = OS.gtk_container_get_children (listHandle);
1891         auto item = OS.g_list_nth_data (children, index);
1892         auto label = OS.gtk_bin_get_child (item);
1893         OS.gtk_label_set_text (label, buffer);
1894         OS.g_list_free (children);
1895         ignoreSelect = false;
1896     }
1897 }
1898 
1899 /**
1900  * Sets the receiver's list to be the given array of items.
1901  *
1902  * @param items the array of items
1903  *
1904  * @exception IllegalArgumentException <ul>
1905  *    <li>ERROR_INVALID_ARGUMENT - if an item in the items array is null</li>
1906  * </ul>
1907  * @exception SWTException <ul>
1908  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1909  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1910  * </ul>
1911  */
1912 public void setItems (String [] items) {
1913     checkWidget();
1914     // SWT extension: allow null for zero length string
1915     //if (items is null) error (SWT.ERROR_NULL_ARGUMENT);
1916     for (int i=0; i<items.length; i++) {
1917         if (items [i] is null) error (SWT.ERROR_INVALID_ARGUMENT);
1918     }
1919     auto count = this.items.length;
1920     this.items = new String[](items.length);
1921     System.arraycopy (items, 0, this.items, 0, items.length);
1922     if (OS.GTK_VERSION >= OS.buildVERSION (2, 4, 0)) {
1923         clearText ();
1924         for (ptrdiff_t i = count - 1; i >= 0; i--) {
1925             OS.gtk_combo_box_remove_text (handle, cast(int)/*64bit*/i);
1926         }
1927         for (ptrdiff_t i = 0; i < items.length; i++) {
1928             String string = items [i];
1929             char* buffer = string.toStringzValidPtr();
1930             OS.gtk_combo_box_insert_text (handle, cast(int)/*64bit*/i, buffer);
1931             if ((style & SWT.RIGHT_TO_LEFT) !is 0 && popupHandle !is null) {
1932                 display.doSetDirectionProc(popupHandle, OS.GTK_TEXT_DIR_RTL);
1933             }
1934         }
1935     } else {
1936         lockText = ignoreSelect = true;
1937         OS.gtk_list_clear_items (listHandle, 0, -1);
1938         auto font = getFontDescription ();
1939         GdkColor* color = getForegroundColor ();
1940         int direction = OS.gtk_widget_get_direction (handle);
1941         int i = 0;
1942         while (i < items.length) {
1943             String string = items [i];
1944             char * buffer = string.toStringzValidPtr();
1945             auto item = OS.gtk_list_item_new_with_label (buffer);
1946             auto label = OS.gtk_bin_get_child (item);
1947             setForegroundColor (label, color);
1948             OS.gtk_widget_modify_font (label, font);
1949             OS.gtk_widget_set_direction (label, direction);
1950             OS.gtk_container_add (listHandle, item);
1951             OS.gtk_widget_show (item);
1952             i++;
1953         }
1954         lockText = ignoreSelect = false;
1955         OS.gtk_entry_set_text (entryHandle, "".ptr);
1956     }
1957 }
1958 
1959 /**
1960  * Marks the receiver's list as visible if the argument is <code>true</code>,
1961  * and marks it invisible otherwise.
1962  * <p>
1963  * If one of the receiver's ancestors is not visible or some
1964  * other condition makes the receiver not visible, marking
1965  * it visible may not actually cause it to be displayed.
1966  * </p>
1967  *
1968  * @param visible the new visibility state
1969  *
1970  * @exception SWTException <ul>
1971  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1972  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1973  * </ul>
1974  *
1975  * @since 3.4
1976  */
1977 public void setListVisible (bool visible) {
1978     checkWidget ();
1979     if (OS.GTK_VERSION >= OS.buildVERSION (2, 4, 0)) {
1980         if (visible) {
1981             OS.gtk_combo_box_popup (handle);
1982         } else {
1983             OS.gtk_combo_box_popdown (handle);
1984         }
1985     }
1986 }
1987 
1988 override void setOrientation() {
1989     super.setOrientation();
1990     if ((style & SWT.RIGHT_TO_LEFT) !is 0) {
1991         if (listHandle !is null) OS.gtk_widget_set_direction (listHandle, OS.GTK_TEXT_DIR_RTL);
1992         if (entryHandle !is null) OS.gtk_widget_set_direction (entryHandle, OS.GTK_TEXT_DIR_RTL);
1993         if (cellHandle !is null) OS.gtk_widget_set_direction (cellHandle, OS.GTK_TEXT_DIR_RTL);
1994     }
1995 }
1996 
1997 /**
1998  * Sets the orientation of the receiver, which must be one
1999  * of the constants <code>SWT.LEFT_TO_RIGHT</code> or <code>SWT.RIGHT_TO_LEFT</code>.
2000  * <p>
2001  *
2002  * @param orientation new orientation style
2003  *
2004  * @exception SWTException <ul>
2005  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
2006  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
2007  * </ul>
2008  *
2009  * @since 2.1.2
2010  */
2011 public void setOrientation (int orientation) {
2012     if (OS.GTK_VERSION >= OS.buildVERSION (2, 4, 0)) {
2013         checkWidget();
2014         int flags = SWT.RIGHT_TO_LEFT | SWT.LEFT_TO_RIGHT;
2015         if ((orientation & flags) is 0 || (orientation & flags) is flags) return;
2016         style &= ~flags;
2017         style |= orientation & flags;
2018         int dir = (orientation & SWT.RIGHT_TO_LEFT) !is 0 ? OS.GTK_TEXT_DIR_RTL : OS.GTK_TEXT_DIR_LTR;
2019         OS.gtk_widget_set_direction (fixedHandle, dir);
2020         OS.gtk_widget_set_direction (handle, dir);
2021     	if (entryHandle !is null) OS.gtk_widget_set_direction (entryHandle, dir);
2022     	if (listHandle !is null) {
2023             OS.gtk_widget_set_direction (listHandle, dir);
2024             auto itemsList = OS.gtk_container_get_children (listHandle);
2025             if (itemsList !is null) {
2026                 int count = OS.g_list_length (itemsList);
2027                 for (int i=count - 1; i>=0; i--) {
2028                     auto widget = OS.gtk_bin_get_child (OS.g_list_nth_data (itemsList, i));
2029                     OS.gtk_widget_set_direction (widget, dir);
2030                 }
2031                 OS.g_list_free (itemsList);
2032             }
2033         }
2034         if (cellHandle !is null) OS.gtk_widget_set_direction (cellHandle, dir);
2035         if (popupHandle !is null) display.doSetDirectionProc (popupHandle, dir);
2036     }
2037 }
2038 
2039 /**
2040  * Sets the selection in the receiver's text field to the
2041  * range specified by the argument whose x coordinate is the
2042  * start of the selection and whose y coordinate is the end
2043  * of the selection.
2044  *
2045  * @param selection a point representing the new selection start and end
2046  *
2047  * @exception IllegalArgumentException <ul>
2048  *    <li>ERROR_NULL_ARGUMENT - if the point is null</li>
2049  * </ul>
2050  * @exception SWTException <ul>
2051  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
2052  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
2053  * </ul>
2054  */
2055 public void setSelection (Point selection) {
2056     checkWidget();
2057     if (selection is null) error (SWT.ERROR_NULL_ARGUMENT);
2058     if ((style & SWT.READ_ONLY) !is 0) return;
2059     if (entryHandle !is null) {
2060         OS.gtk_editable_set_position (entryHandle, selection.x);
2061         OS.gtk_editable_select_region (entryHandle, selection.x, selection.y);
2062     }
2063 }
2064 
2065 /**
2066  * Sets the contents of the receiver's text field to the
2067  * given string.
2068  * <p>
2069  * Note: The text field in a <code>Combo</code> is typically
2070  * only capable of displaying a single line of text. Thus,
2071  * setting the text to a string containing line breaks or
2072  * other special characters will probably cause it to
2073  * display incorrectly.
2074  * </p>
2075  *
2076  * @param string the new text
2077  *
2078  * @exception SWTException <ul>
2079  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
2080  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
2081  * </ul>
2082  */
2083 public void setText (String string) {
2084     checkWidget();
2085     // SWT extension: allow null for zero length string
2086     //if (string is null) error (SWT.ERROR_NULL_ARGUMENT);
2087     if ((style & SWT.READ_ONLY) !is 0) {
2088         int index = indexOf (string);
2089         if (index is -1) return;
2090         if (OS.GTK_VERSION >= OS.buildVERSION (2, 4, 0)) {
2091             OS.g_signal_handlers_block_matched (handle, OS.G_SIGNAL_MATCH_DATA, 0, 0, null, null, udCHANGED);
2092             OS.gtk_combo_box_set_active (handle, index);
2093             OS.g_signal_handlers_unblock_matched (handle, OS.G_SIGNAL_MATCH_DATA, 0, 0, null, null, udCHANGED);
2094             /*
2095             * Feature in GTK. Read Only combo boxes do not get a chance to send out a
2096             * Modify event in the gtk_changed callback. The fix is to send a Modify event
2097             * here.
2098             */
2099             sendEvent (SWT.Modify);
2100             return;
2101         }
2102     }
2103     /*
2104     * Feature in gtk.  When text is set in gtk, separate events are fired for the deletion and
2105     * insertion of the text.  This is not wrong, but is inconsistent with other platforms.  The
2106     * fix is to block the firing of these events and fire them ourselves in a consistent manner.
2107     */
2108     if (hooks (SWT.Verify) || filters (SWT.Verify)) {
2109         auto ptr = OS.gtk_entry_get_text (entryHandle);
2110         string = verifyText (string, 0, cast(int)/*64bit*/OS.g_utf8_strlen (ptr, -1));
2111         if (string is null) return;
2112     }
2113     auto buffer = string.toStringzValidPtr();
2114     if (OS.GTK_VERSION >= OS.buildVERSION (2, 4, 0)) {
2115         OS.g_signal_handlers_block_matched (handle, OS.G_SIGNAL_MATCH_DATA, 0, 0, null, null, udCHANGED);
2116     }
2117     OS.g_signal_handlers_block_matched (entryHandle, OS.G_SIGNAL_MATCH_DATA, 0, 0, null, null, udCHANGED);
2118     OS.g_signal_handlers_block_matched (entryHandle, OS.G_SIGNAL_MATCH_DATA, 0, 0, null, null, udDELETE_TEXT);
2119     OS.g_signal_handlers_block_matched (entryHandle, OS.G_SIGNAL_MATCH_DATA, 0, 0, null, null, udINSERT_TEXT);
2120     OS.gtk_entry_set_text (entryHandle, buffer);
2121     if (OS.GTK_VERSION >= OS.buildVERSION (2, 4, 0)) {
2122         OS.g_signal_handlers_unblock_matched (handle, OS.G_SIGNAL_MATCH_DATA, 0, 0, null, null, udCHANGED);
2123     }
2124     OS.g_signal_handlers_unblock_matched (entryHandle, OS.G_SIGNAL_MATCH_DATA, 0, 0, null, null, udCHANGED);
2125     OS.g_signal_handlers_unblock_matched (entryHandle, OS.G_SIGNAL_MATCH_DATA, 0, 0, null, null, udDELETE_TEXT);
2126     OS.g_signal_handlers_unblock_matched (entryHandle, OS.G_SIGNAL_MATCH_DATA, 0, 0, null, null, udINSERT_TEXT);
2127     sendEvent (SWT.Modify);
2128 }
2129 
2130 /**
2131  * Sets the maximum number of characters that the receiver's
2132  * text field is capable of holding to be the argument.
2133  * <p>
2134  * To reset this value to the default, use <code>setTextLimit(Combo.LIMIT)</code>.
2135  * Specifying a limit value larger than <code>Combo.LIMIT</code> sets the
2136  * receiver's limit to <code>Combo.LIMIT</code>.
2137  * </p>
2138  * @param limit new text limit
2139  *
2140  * @exception IllegalArgumentException <ul>
2141  *    <li>ERROR_CANNOT_BE_ZERO - if the limit is zero</li>
2142  * </ul>
2143  * @exception SWTException <ul>
2144  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
2145  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
2146  * </ul>
2147  *
2148  * @see #LIMIT
2149  */
2150 public void setTextLimit (int limit) {
2151     checkWidget();
2152     if (limit is 0) error (SWT.ERROR_CANNOT_BE_ZERO);
2153     if (entryHandle !is null) OS.gtk_entry_set_max_length (entryHandle, limit);
2154 }
2155 
2156 override void setToolTipText (Shell shell, String newString) {
2157     if (entryHandle !is null) shell.setToolTipText (entryHandle, newString);
2158     if (buttonHandle !is null) shell.setToolTipText (buttonHandle, newString);
2159 }
2160 
2161 /**
2162  * Sets the number of items that are visible in the drop
2163  * down portion of the receiver's list.
2164  * <p>
2165  * Note: This operation is a hint and is not supported on
2166  * platforms that do not have this concept.
2167  * </p>
2168  *
2169  * @param count the new number of items to be visible
2170  *
2171  * @exception SWTException <ul>
2172  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
2173  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
2174  * </ul>
2175  *
2176  * @since 3.0
2177  */
2178 public void setVisibleItemCount (int count) {
2179     checkWidget ();
2180     if (count < 0) return;
2181     visibleCount = count;
2182 }
2183 
2184 override bool translateTraversal (GdkEventKey* keyEvent) {
2185     int key = keyEvent.keyval;
2186     switch (key) {
2187         case OS.GDK_KP_Enter:
2188         case OS.GDK_Return: {
2189             auto imContext = imContext ();
2190             if (imContext !is null) {
2191                 char* preeditString;
2192                 OS.gtk_im_context_get_preedit_string (imContext, &preeditString, null, null);
2193                 if (preeditString !is null) {
2194                     auto length = fromStringz(preeditString).length;
2195                     OS.g_free (preeditString);
2196                     if (length !is 0) return false;
2197                 }
2198             }
2199             break;
2200         }
2201         default:
2202             break;
2203     }
2204     return super.translateTraversal (keyEvent);
2205 }
2206 
2207 String verifyText (String string, int start, int end) {
2208     if (string.length is 0 && start is end) return null;
2209     Event event = new Event ();
2210     event.text = string;
2211     event.start = start;
2212     event.end = end;
2213     auto eventPtr = OS.gtk_get_current_event ();
2214     if (eventPtr !is null) {
2215         GdkEventKey* gdkEvent = cast(GdkEventKey*)eventPtr;
2216         switch (gdkEvent.type) {
2217             case OS.GDK_KEY_PRESS:
2218                 setKeyState (event, gdkEvent);
2219                 break;
2220             default:
2221         }
2222         OS.gdk_event_free (eventPtr);
2223     }
2224     /*
2225      * It is possible (but unlikely), that application
2226      * code could have disposed the widget in the verify
2227      * event.  If this happens, answer null to cancel
2228      * the operation.
2229      */
2230     sendEvent (SWT.Verify, event);
2231     if (!event.doit || isDisposed ()) return null;
2232     return event.text;
2233 }
2234 
2235 }