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.Text;
14 
15 import java.lang.all;
16 
17 
18 import org.eclipse.swt.SWT;
19 import org.eclipse.swt.internal.gtk.OS;
20 import org.eclipse.swt.graphics.Rectangle;
21 import org.eclipse.swt.events.ModifyListener;
22 import org.eclipse.swt.widgets.TypedListener;
23 import org.eclipse.swt.events.SelectionListener;
24 import org.eclipse.swt.events.SelectionEvent;
25 import org.eclipse.swt.events.VerifyListener;
26 import org.eclipse.swt.widgets.Event;
27 import org.eclipse.swt.widgets.Scrollable;
28 import org.eclipse.swt.widgets.Composite;
29 
30 version(Tango){
31 static import tango.stdc..string;
32 } else { // Phobos
33 }
34 
35 /**
36  * Instances of this class are selectable user interface
37  * objects that allow the user to enter and modify text.
38  * Text controls can be either single or multi-line.
39  * When a text control is created with a border, the
40  * operating system includes a platform specific inset
41  * around the contents of the control.  When created
42  * without a border, an effort is made to remove the
43  * inset such that the preferred size of the control
44  * is the same size as the contents.
45  * <p>
46  * <dl>
47  * <dt><b>Styles:</b></dt>
48  * <dd>CANCEL, CENTER, LEFT, MULTI, PASSWORD, SEARCH, SINGLE, RIGHT, READ_ONLY, WRAP</dd>
49  * <dt><b>Events:</b></dt>
50  * <dd>DefaultSelection, Modify, Verify</dd>
51  * </dl>
52  * <p>
53  * Note: Only one of the styles MULTI and SINGLE may be specified,
54  * and only one of the styles LEFT, CENTER, and RIGHT may be specified.
55  * </p><p>
56  * IMPORTANT: This class is <em>not</em> intended to be subclassed.
57  * </p>
58  *
59  * @see <a href="http://www.eclipse.org/swt/snippets/#text">Text snippets</a>
60  * @see <a href="http://www.eclipse.org/swt/examples.php">SWT Example: ControlExample</a>
61  * @see <a href="http://www.eclipse.org/swt/">Sample code and further information</a>
62  */
63 public class Text : Scrollable {
64 
65     alias Scrollable.computeSize computeSize;
66     alias Scrollable.dragDetect dragDetect;
67     alias Scrollable.setBackgroundColor setBackgroundColor;
68     alias Scrollable.setCursor setCursor;
69     alias Scrollable.setOrientation setOrientation;
70     alias Scrollable.translateTraversal translateTraversal;
71 
72     GtkTextBuffer* bufferHandle;
73     int tabs = 8, lastEventTime = 0;
74     GdkEventKey* gdkEventKey;
75     int fixStart = -1, fixEnd = -1;
76     bool doubleClick;
77     String message = "";
78 
79     static const int INNER_BORDER = 2;
80     //static const int ITER_SIZEOF = GtkTextIter.sizeof;
81 
82     /**
83     * The maximum number of characters that can be entered
84     * into a text widget.
85     * <p>
86     * Note that this value is platform dependent, based upon
87     * the native widget implementation.
88     * </p>
89     */
90     public const static int LIMIT = 0x7FFFFFFF;
91     /**
92     * The delimiter used by multi-line text widgets.  When text
93     * is queried and from the widget, it will be delimited using
94     * this delimiter.
95     */
96     public const static String DELIMITER = "\n";
97     /*
98     * These values can be different on different platforms.
99     * Therefore they are not initialized in the declaration
100     * to stop the compiler from inlining.
101     */
102     // <keinfarbton> avoid static ctor
103     //static {
104     //    LIMIT = 0x7FFFFFFF;
105     //    DELIMITER = "\n";
106     //}
107 
108 /**
109  * Constructs a new instance of this class given its parent
110  * and a style value describing its behavior and appearance.
111  * <p>
112  * The style value is either one of the style constants defined in
113  * class <code>SWT</code> which is applicable to instances of this
114  * class, or must be built by <em>bitwise OR</em>'ing together
115  * (that is, using the <code>int</code> "|" operator) two or more
116  * of those <code>SWT</code> style constants. The class description
117  * lists the style constants that are applicable to the class.
118  * Style bits are also inherited from superclasses.
119  * </p>
120  *
121  * @param parent a composite control which will be the parent of the new instance (cannot be null)
122  * @param style the style of control to construct
123  *
124  * @exception IllegalArgumentException <ul>
125  *    <li>ERROR_NULL_ARGUMENT - if the parent is null</li>
126  * </ul>
127  * @exception SWTException <ul>
128  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li>
129  *    <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li>
130  * </ul>
131  *
132  * @see SWT#SINGLE
133  * @see SWT#MULTI
134  * @see SWT#READ_ONLY
135  * @see SWT#WRAP
136  * @see Widget#checkSubclass
137  * @see Widget#getStyle
138  */
139 public this (Composite parent, int style) {
140     super (parent, checkStyle (style));
141 }
142 
143 static int checkStyle (int style) {
144     if ((style & SWT.SEARCH) !is 0) {
145         style |= SWT.SINGLE | SWT.BORDER;
146         style &= ~SWT.PASSWORD;
147     }
148     style &= ~SWT.SEARCH;
149     if ((style & SWT.SINGLE) !is 0 && (style & SWT.MULTI) !is 0) {
150         style &= ~SWT.MULTI;
151     }
152     style = checkBits (style, SWT.LEFT, SWT.CENTER, SWT.RIGHT, 0, 0, 0);
153     if ((style & SWT.SINGLE) !is 0) style &= ~(SWT.H_SCROLL | SWT.V_SCROLL | SWT.WRAP);
154     if ((style & SWT.WRAP) !is 0) {
155         style |= SWT.MULTI;
156         style &= ~SWT.H_SCROLL;
157     }
158     if ((style & SWT.MULTI) !is 0) style &= ~SWT.PASSWORD;
159     if ((style & (SWT.SINGLE | SWT.MULTI)) !is 0) return style;
160     if ((style & (SWT.H_SCROLL | SWT.V_SCROLL)) !is 0) return style | SWT.MULTI;
161     return style | SWT.SINGLE;
162 }
163 
164 override void createHandle (int index) {
165     state |= HANDLE | MENU;
166     fixedHandle = cast(GtkWidget*) OS.g_object_new (display.gtk_fixed_get_type (), null);
167     if (fixedHandle is null) error (SWT.ERROR_NO_HANDLES);
168     OS.gtk_fixed_set_has_window (cast(GtkFixed*)fixedHandle, true);
169     if ((style & SWT.SINGLE) !is 0) {
170         handle = OS.gtk_entry_new ();
171         if (handle is null) error (SWT.ERROR_NO_HANDLES);
172         OS.gtk_container_add (cast(GtkContainer*)fixedHandle, handle);
173         OS.gtk_editable_set_editable (cast(GtkEditable*)handle, (style & SWT.READ_ONLY) is 0);
174         OS.gtk_entry_set_has_frame (cast(GtkEntry*)handle, (style & SWT.BORDER) !is 0);
175         OS.gtk_entry_set_visibility (cast(GtkEntry*)handle, (style & SWT.PASSWORD) is 0);
176         float alignment = 0.0f;
177         if ((style & SWT.CENTER) !is 0) alignment = 0.5f;
178         if ((style & SWT.RIGHT) !is 0) alignment = 1.0f;
179         if (alignment > 0.0f) {
180             OS.gtk_entry_set_alignment (cast(GtkEntry*)handle, alignment);
181         }
182     } else {
183         scrolledHandle = cast(GtkWidget*) OS.gtk_scrolled_window_new (null, null);
184         if (scrolledHandle is null) error (SWT.ERROR_NO_HANDLES);
185         handle = OS.gtk_text_view_new ();
186         if (handle is null) error (SWT.ERROR_NO_HANDLES);
187         bufferHandle = OS.gtk_text_view_get_buffer (cast(GtkTextView*)handle);
188         if (bufferHandle is null) error (SWT.ERROR_NO_HANDLES);
189         OS.gtk_container_add (cast(GtkContainer*)fixedHandle, scrolledHandle);
190         OS.gtk_container_add (cast(GtkContainer*)scrolledHandle, handle);
191         OS.gtk_text_view_set_editable (cast(GtkTextView*)handle, (style & SWT.READ_ONLY) is 0);
192         if ((style & SWT.WRAP) !is 0) OS.gtk_text_view_set_wrap_mode (cast(GtkTextView*)handle, OS.GTK_WRAP_WORD_CHAR);
193         int hsp = (style & SWT.H_SCROLL) !is 0 ? OS.GTK_POLICY_ALWAYS : OS.GTK_POLICY_NEVER;
194         int vsp = (style & SWT.V_SCROLL) !is 0 ? OS.GTK_POLICY_ALWAYS : OS.GTK_POLICY_NEVER;
195         OS.gtk_scrolled_window_set_policy (cast(GtkScrolledWindow*)scrolledHandle, hsp, vsp);
196         if ((style & SWT.BORDER) !is 0) {
197             OS.gtk_scrolled_window_set_shadow_type (cast(GtkScrolledWindow*)scrolledHandle, OS.GTK_SHADOW_ETCHED_IN);
198         }
199         int just = OS.GTK_JUSTIFY_LEFT;
200         if ((style & SWT.CENTER) !is 0) just = OS.GTK_JUSTIFY_CENTER;
201         if ((style & SWT.RIGHT) !is 0) just = OS.GTK_JUSTIFY_RIGHT;
202         OS.gtk_text_view_set_justification (cast(GtkTextView*)handle, just);
203     }
204 }
205 
206 override void createWidget (int index) {
207     super.createWidget (index);
208     doubleClick = true;
209 }
210 
211 /**
212  * Adds the listener to the collection of listeners who will
213  * be notified when the receiver's text is modified, by sending
214  * it one of the messages defined in the <code>ModifyListener</code>
215  * interface.
216  *
217  * @param listener the listener which should be notified
218  *
219  * @exception IllegalArgumentException <ul>
220  *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
221  * </ul>
222  * @exception SWTException <ul>
223  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
224  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
225  * </ul>
226  *
227  * @see ModifyListener
228  * @see #removeModifyListener
229  */
230 public void addModifyListener (ModifyListener listener) {
231     checkWidget ();
232     if (listener is null) error (SWT.ERROR_NULL_ARGUMENT);
233     TypedListener typedListener = new TypedListener (listener);
234     addListener (SWT.Modify, typedListener);
235 }
236 
237 /**
238  * Adds the listener to the collection of listeners who will
239  * be notified when the control is selected by the user, by sending
240  * it one of the messages defined in the <code>SelectionListener</code>
241  * interface.
242  * <p>
243  * <code>widgetSelected</code> is not called for texts.
244  * <code>widgetDefaultSelected</code> is typically called when ENTER is pressed in a single-line text,
245  * or when ENTER is pressed in a search text. If the receiver has the <code>SWT.SEARCH | SWT.CANCEL</code> style
246  * and the user cancels the search, the event object detail field contains the value <code>SWT.CANCEL</code>.
247  * </p>
248  *
249  * @param listener the listener which should be notified when the control is selected by the user
250  *
251  * @exception IllegalArgumentException <ul>
252  *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
253  * </ul>
254  * @exception SWTException <ul>
255  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
256  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
257  * </ul>
258  *
259  * @see SelectionListener
260  * @see #removeSelectionListener
261  * @see SelectionEvent
262  */
263 public void addSelectionListener(SelectionListener listener) {
264     checkWidget ();
265     if (listener is null) error (SWT.ERROR_NULL_ARGUMENT);
266     TypedListener typedListener = new TypedListener(listener);
267     addListener(SWT.Selection,typedListener);
268     addListener(SWT.DefaultSelection,typedListener);
269 }
270 
271 /**
272  * Adds the listener to the collection of listeners who will
273  * be notified when the receiver's text is verified, by sending
274  * it one of the messages defined in the <code>VerifyListener</code>
275  * interface.
276  *
277  * @param listener the listener which should be notified
278  *
279  * @exception IllegalArgumentException <ul>
280  *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
281  * </ul>
282  * @exception SWTException <ul>
283  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
284  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
285  * </ul>
286  *
287  * @see VerifyListener
288  * @see #removeVerifyListener
289  */
290 public void addVerifyListener (VerifyListener listener) {
291     checkWidget();
292     if (listener is null) error (SWT.ERROR_NULL_ARGUMENT);
293     TypedListener typedListener = new TypedListener (listener);
294     addListener (SWT.Verify, typedListener);
295 }
296 
297 /**
298  * Appends a string.
299  * <p>
300  * The new text is appended to the text at
301  * the end of the widget.
302  * </p>
303  *
304  * @param string the string to be appended
305  *
306  * @exception SWTException <ul>
307  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
308  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
309  * </ul>
310  */
311 public void append (String string) {
312     checkWidget ();
313     // SWT extension: allow null for zero length string
314     //if (string is null) error (SWT.ERROR_NULL_ARGUMENT);
315     if ((style & SWT.SINGLE) !is 0) {
316         int dummy = -1;
317         OS.gtk_editable_insert_text (cast(GtkEditable*)handle, string.ptr, cast(int)/*64bit*/string.length, &dummy );
318         OS.gtk_editable_set_position (cast(GtkEditable*)handle, -1);
319     } else {
320         GtkTextIter position;
321         OS.gtk_text_buffer_get_end_iter (bufferHandle, &position);
322         OS.gtk_text_buffer_insert (bufferHandle, &position, string.ptr, cast(int)/*64bit*/string.length);
323         OS.gtk_text_buffer_place_cursor (bufferHandle, &position);
324         auto mark = OS.gtk_text_buffer_get_insert (bufferHandle);
325         OS.gtk_text_view_scroll_mark_onscreen (cast(GtkTextView*)handle, mark);
326     }
327 }
328 
329 /**
330  * Clears the selection.
331  *
332  * @exception SWTException <ul>
333  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
334  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
335  * </ul>
336  */
337 public void clearSelection () {
338     checkWidget ();
339     if ((style & SWT.SINGLE) !is 0) {
340         int position = OS.gtk_editable_get_position (cast(GtkEditable*)handle);
341         OS.gtk_editable_select_region (cast(GtkEditable*)handle, position, position);
342     } else {
343         GtkTextIter position;
344         auto insertMark = OS.gtk_text_buffer_get_insert (bufferHandle);
345         auto selectionMark = OS.gtk_text_buffer_get_selection_bound (bufferHandle);
346         OS.gtk_text_buffer_get_iter_at_mark (bufferHandle, &position, insertMark);
347         OS.gtk_text_buffer_move_mark (bufferHandle, selectionMark, &position);
348         OS.gtk_text_buffer_move_mark (bufferHandle, insertMark, &position);
349     }
350 }
351 
352 override public Point computeSize (int wHint, int hHint, bool changed) {
353     checkWidget ();
354     if (wHint !is SWT.DEFAULT && wHint < 0) wHint = 0;
355     if (hHint !is SWT.DEFAULT && hHint < 0) hHint = 0;
356     int w , h;
357     if ((style & SWT.SINGLE) !is 0) {
358         OS.gtk_widget_realize (handle);
359         auto layout = OS.gtk_entry_get_layout (cast(GtkEntry*)handle);
360         OS.pango_layout_get_size (layout, &w, &h);
361     } else {
362         GtkTextIter start;
363         GtkTextIter end;
364         OS.gtk_text_buffer_get_bounds (bufferHandle, &start, &end);
365         auto text = OS.gtk_text_buffer_get_text (bufferHandle, &start, &end, true);
366         auto layout = OS.gtk_widget_create_pango_layout (handle, text);
367         OS.g_free (text);
368         OS.pango_layout_set_width (layout, wHint * OS.PANGO_SCALE);
369         OS.pango_layout_get_size (layout, &w, &h);
370         OS.g_object_unref (layout);
371     }
372     int width = OS.PANGO_PIXELS (w);
373     int height = OS.PANGO_PIXELS (h);
374     //This code is intentionally commented
375 //  if ((style & SWT.SEARCH) !is 0 && message.length () !is 0) {
376 //      GC gc = new GC (this);
377 //      Point size = gc.stringExtent (message);
378 //      width = Math.max (width, size.x);
379 //      gc.dispose ();
380 //  }
381     if (width is 0) width = DEFAULT_WIDTH;
382     if (height is 0) height = DEFAULT_HEIGHT;
383     width = wHint is SWT.DEFAULT ? width : wHint;
384     height = hHint is SWT.DEFAULT ? height : hHint;
385     Rectangle trim = computeTrim (0, 0, width, height);
386     return new Point (trim.width, trim.height);
387 }
388 
389 override public Rectangle computeTrim (int x, int y, int width, int height) {
390     checkWidget ();
391     Rectangle trim = super.computeTrim (x, y, width, height);
392     int xborder = 0, yborder = 0;
393     if ((style & SWT.SINGLE) !is 0) {
394         if ((style & SWT.BORDER) !is 0) {
395             auto style = OS.gtk_widget_get_style (handle);
396             xborder += OS.gtk_style_get_xthickness (style);
397             yborder += OS.gtk_style_get_ythickness (style);
398         }
399         xborder += INNER_BORDER;
400         yborder += INNER_BORDER;
401     } else {
402         int borderWidth = OS.gtk_container_get_border_width (cast(GtkContainer*)handle);
403         xborder += borderWidth;
404         yborder += borderWidth;
405     }
406     int property;
407     OS.gtk_widget_style_get1 (handle, OS.interior_focus.ptr, &property);
408     if (property is 0) {
409         OS.gtk_widget_style_get1 (handle, OS.focus_line_width.ptr, &property);
410         xborder += property;
411         yborder += property;
412     }
413     trim.x -= xborder;
414     trim.y -= yborder;
415     trim.width += 2 * xborder;
416     trim.height += 2 * yborder;
417     return new Rectangle (trim.x, trim.y, trim.width, trim.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 public void copy () {
432     checkWidget ();
433     if ((style & SWT.SINGLE) !is 0) {
434         OS.gtk_editable_copy_clipboard (cast(GtkEditable*)handle);
435     } else {
436         auto clipboard = OS.gtk_clipboard_get (cast(void*)OS.GDK_NONE);
437         OS.gtk_text_buffer_copy_clipboard (bufferHandle, clipboard);
438     }
439 }
440 
441 /**
442  * Cuts the selected text.
443  * <p>
444  * The current selection is first copied to the
445  * clipboard and then deleted from the widget.
446  * </p>
447  *
448  * @exception SWTException <ul>
449  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
450  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
451  * </ul>
452  */
453 public void cut () {
454     checkWidget ();
455     if ((style & SWT.SINGLE) !is 0) {
456         OS.gtk_editable_cut_clipboard (cast(GtkEditable*)handle);
457     } else {
458         auto clipboard = OS.gtk_clipboard_get (cast(void*)OS.GDK_NONE);
459         OS.gtk_text_buffer_cut_clipboard (bufferHandle, clipboard, OS.gtk_text_view_get_editable (cast(GtkTextView*)handle));
460     }
461 }
462 
463 override void deregister () {
464     super.deregister ();
465     if (bufferHandle !is null) display.removeWidget (cast(GtkWidget*)bufferHandle);
466     auto imContext = imContext ();
467     if (imContext !is null) display.removeWidget (cast(GtkWidget*)imContext);
468 }
469 
470 override bool dragDetect (int x, int y, bool filter, bool* consume) {
471     if (filter) {
472         int start = 0, end = 0;
473         if ((style & SWT.SINGLE) !is 0) {
474             int s, e;
475             OS.gtk_editable_get_selection_bounds (cast(GtkEditable*)handle, &s, &e);
476             start = s;
477             end = e;
478         } else {
479             GtkTextIter s;
480             GtkTextIter e;
481             OS.gtk_text_buffer_get_selection_bounds (bufferHandle, &s, &e);
482             start = OS.gtk_text_iter_get_offset (&s);
483             end = OS.gtk_text_iter_get_offset (&e);
484         }
485         if (start !is end) {
486             if (end < start) {
487                 int temp = end;
488                 end = start;
489                 start = temp;
490             }
491             ptrdiff_t position = -1;
492             if ((style & SWT.SINGLE) !is 0) {
493                 int index;
494                 int trailing;
495                 auto layout = OS.gtk_entry_get_layout (cast(GtkEntry*)handle);
496                 OS.pango_layout_xy_to_index (layout, x * OS.PANGO_SCALE, y * OS.PANGO_SCALE, &index, &trailing);
497                 auto ptr = OS.pango_layout_get_text (layout);
498                 position = OS.g_utf8_pointer_to_offset (ptr, ptr + index) + trailing;
499             } else {
500                 GtkTextIter p;
501                 OS.gtk_text_view_get_iter_at_location (cast(GtkTextView*)handle, &p, x, y);
502                 position = OS.gtk_text_iter_get_offset (&p);
503             }
504             if (start <= position && position < end) {
505                 if (super.dragDetect (x, y, filter, consume)) {
506                     if (consume !is null) consume [0] = true;
507                     return true;
508                 }
509             }
510         }
511         return false;
512     }
513     return super.dragDetect (x, y, filter, consume);
514 }
515 
516 override GdkDrawable* eventWindow () {
517     return paintWindow ();
518 }
519 
520 override bool filterKey (int keyval, GdkEventKey* event) {
521     int time = OS.gdk_event_get_time (cast(GdkEvent*)event);
522     if (time !is lastEventTime) {
523         lastEventTime = time;
524         auto imContext = imContext ();
525         if (imContext !is null) {
526             return cast(bool)OS.gtk_im_context_filter_keypress (imContext, event);
527         }
528     }
529     gdkEventKey = event;
530     return false;
531 }
532 
533 void fixIM () {
534     /*
535     *  The IM filter has to be called one time for each key press event.
536     *  When the IM is open the key events are duplicated. The first event
537     *  is filtered by SWT and the second event is filtered by GTK.  In some
538     *  cases the GTK handler does not run (the widget is destroyed, the
539     *  application code consumes the event, etc), for these cases the IM
540     *  filter has to be called by SWT.
541     */
542     if (gdkEventKey !is null && gdkEventKey !is cast(GdkEventKey*)-1) {
543         auto imContext = imContext ();
544         if (imContext !is null) {
545             OS.gtk_im_context_filter_keypress (imContext, gdkEventKey);
546             gdkEventKey = cast(GdkEventKey*)-1;
547             return;
548         }
549     }
550     gdkEventKey = null;
551 }
552 
553 override GdkColor* getBackgroundColor () {
554     return getBaseColor ();
555 }
556 
557 public override int getBorderWidth () {
558     checkWidget();
559     if ((style & SWT.MULTI) !is 0) return super.getBorderWidth ();
560     auto style = OS.gtk_widget_get_style (handle);
561     if ((this.style & SWT.BORDER) !is 0) {
562          return OS.gtk_style_get_xthickness (style);
563     }
564     return 0;
565 }
566 
567 /**
568  * Returns the line number of the caret.
569  * <p>
570  * The line number of the caret is returned.
571  * </p>
572  *
573  * @return the line number
574  *
575  * @exception SWTException <ul>
576  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
577  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
578  * </ul>
579  */
580 public int getCaretLineNumber () {
581     checkWidget ();
582     if ((style & SWT.SINGLE) !is 0) return 1;
583     GtkTextIter position;
584     auto mark = OS.gtk_text_buffer_get_insert (bufferHandle);
585     OS.gtk_text_buffer_get_iter_at_mark (bufferHandle, &position, mark);
586     return OS.gtk_text_iter_get_line (&position);
587 }
588 
589 /**
590  * Returns a point describing the receiver's location relative
591  * to its parent (or its display if its parent is null).
592  * <p>
593  * The location of the caret is returned.
594  * </p>
595  *
596  * @return a point, the location of the caret
597  *
598  * @exception SWTException <ul>
599  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
600  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
601  * </ul>
602  */
603 public Point getCaretLocation () {
604     checkWidget ();
605     if ((style & SWT.SINGLE) !is 0) {
606         int index = OS.gtk_editable_get_position (cast(GtkEditable*)handle);
607         if (OS.GTK_VERSION >= OS.buildVERSION (2, 6, 0)) {
608             index = OS.gtk_entry_text_index_to_layout_index (cast(GtkEntry*)handle, index);
609         }
610         int offset_x, offset_y;
611         OS.gtk_entry_get_layout_offsets (cast(GtkEntry*)handle, &offset_x, &offset_y);
612         auto layout = OS.gtk_entry_get_layout (cast(GtkEntry*)handle);
613         PangoRectangle pos;
614         OS.pango_layout_index_to_pos (layout, index, &pos);
615         int x = offset_x + OS.PANGO_PIXELS (pos.x) - getBorderWidth ();
616         int y = offset_y + OS.PANGO_PIXELS (pos.y);
617         return new Point (x, y);
618     }
619     GtkTextIter position;
620     auto mark = OS.gtk_text_buffer_get_insert (bufferHandle);
621     OS.gtk_text_buffer_get_iter_at_mark (bufferHandle, &position, mark);
622     GdkRectangle rect;
623     OS.gtk_text_view_get_iter_location (cast(GtkTextView*)handle, &position, &rect);
624     int x;
625     int y;
626     OS.gtk_text_view_buffer_to_window_coords (cast(GtkTextView*)handle, OS.GTK_TEXT_WINDOW_TEXT, rect.x, rect.y, &x, &y);
627     return new Point (x, y);
628 }
629 
630 /**
631  * Returns the character position of the caret.
632  * <p>
633  * Indexing is zero based.
634  * </p>
635  *
636  * @return the position of the caret
637  *
638  * @exception SWTException <ul>
639  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
640  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
641  * </ul>
642  */
643 public int getCaretPosition () {
644     checkWidget ();
645     if ((style & SWT.SINGLE) !is 0)  {
646         return OS.gtk_editable_get_position (cast(GtkEditable*)handle);
647     }
648     GtkTextIter position;
649     auto mark = OS.gtk_text_buffer_get_insert (bufferHandle);
650     OS.gtk_text_buffer_get_iter_at_mark (bufferHandle, &position, mark);
651     return OS.gtk_text_iter_get_offset (&position);
652 }
653 
654 /**
655  * Returns the number of characters.
656  *
657  * @return number of characters in the widget
658  *
659  * @exception SWTException <ul>
660  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
661  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
662  * </ul>
663  */
664 public int getCharCount () {
665     checkWidget ();
666     if ((style & SWT.SINGLE) !is 0) {
667         auto ptr = OS.gtk_entry_get_text (cast(GtkEntry*)handle);
668         return cast(int)/*64bit*/OS.g_utf8_strlen (ptr, -1);
669     }
670     return OS.gtk_text_buffer_get_char_count (bufferHandle);
671 }
672 
673 /**
674  * Returns the double click enabled flag.
675  * <p>
676  * The double click flag enables or disables the
677  * default action of the text widget when the user
678  * double clicks.
679  * </p>
680  *
681  * @return whether or not double click is enabled
682  *
683  * @exception SWTException <ul>
684  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
685  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
686  * </ul>
687  */
688 public bool getDoubleClickEnabled () {
689     checkWidget ();
690     return doubleClick;
691 }
692 
693 /**
694  * Returns the echo character.
695  * <p>
696  * The echo character is the character that is
697  * displayed when the user enters text or the
698  * text is changed by the programmer.
699  * </p>
700  *
701  * @return the echo character
702  *
703  * @exception SWTException <ul>
704  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
705  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
706  * </ul>
707  *
708  * @see #setEchoChar
709  */
710 public char getEchoChar () {
711     checkWidget ();
712     if ((style & SWT.SINGLE) !is 0) {
713         if (!OS.gtk_entry_get_visibility (cast(GtkEntry*)handle)) {
714             return cast(char) OS.gtk_entry_get_invisible_char (cast(GtkEntry*)handle);
715         }
716     }
717     return '\0';
718 }
719 
720 /**
721  * Returns the editable state.
722  *
723  * @return whether or not the receiver is editable
724  *
725  * @exception SWTException <ul>
726  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
727  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
728  * </ul>
729  */
730 public bool getEditable () {
731     checkWidget ();
732     if ((style & SWT.SINGLE) !is 0) {
733         return cast(bool)OS.gtk_editable_get_editable (cast(GtkEditable*)handle);
734     }
735     return cast(bool)OS.gtk_text_view_get_editable (cast(GtkTextView*)handle);
736 }
737 
738 override GdkColor* getForegroundColor () {
739     return getTextColor ();
740 }
741 
742 /**
743  * Returns the number of lines.
744  *
745  * @return the number of lines in the widget
746  *
747  * @exception SWTException <ul>
748  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
749  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
750  * </ul>
751  */
752 public int getLineCount () {
753     checkWidget ();
754     if ((style & SWT.SINGLE) !is 0) return 1;
755     return OS.gtk_text_buffer_get_line_count (bufferHandle);
756 }
757 
758 /**
759  * Returns the line delimiter.
760  *
761  * @return a string that is the line delimiter
762  *
763  * @exception SWTException <ul>
764  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
765  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
766  * </ul>
767  *
768  * @see #DELIMITER
769  */
770 public String getLineDelimiter () {
771     checkWidget ();
772     return "\n";
773 }
774 
775 /**
776  * Returns the height of a line.
777  *
778  * @return the height of a row of text
779  *
780  * @exception SWTException <ul>
781  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
782  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
783  * </ul>
784  */
785 public int getLineHeight () {
786     checkWidget ();
787     return fontHeight (getFontDescription (), handle);
788 }
789 
790 /**
791  * Returns the widget message. When the widget is created
792  * with the style <code>SWT.SEARCH</code>, the message text
793  * is displayed as a hint for the user, indicating the
794  * purpose of the field.
795  * <p>
796  * Note: This operation is a <em>HINT</em> and is not
797  * supported on platforms that do not have this concept.
798  * </p>
799  *
800  * @return the widget message
801  *
802  * @exception SWTException <ul>
803  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
804  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
805  * </ul>
806  *
807  * @since 3.3
808  */
809 public String getMessage () {
810     checkWidget ();
811     return message;
812 }
813 
814 /**
815  * Returns the orientation of the receiver, which will be one of the
816  * constants <code>SWT.LEFT_TO_RIGHT</code> or <code>SWT.RIGHT_TO_LEFT</code>.
817  *
818  * @return the orientation style
819  *
820  * @exception SWTException <ul>
821  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
822  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
823  * </ul>
824  *
825  * @since 2.1.2
826  */
827 public int getOrientation () {
828     checkWidget();
829     return style & (SWT.LEFT_TO_RIGHT | SWT.RIGHT_TO_LEFT);
830 }
831 
832 /*public*/ int getPosition (Point point) {
833     checkWidget ();
834     if (point is null) error (SWT.ERROR_NULL_ARGUMENT);
835     ptrdiff_t position = -1;
836     if ((style & SWT.SINGLE) !is 0) {
837         int index;
838         int trailing;
839         auto layout = OS.gtk_entry_get_layout (cast(GtkEntry*)handle);
840         OS.pango_layout_xy_to_index (layout, point.x * OS.PANGO_SCALE, point.y * OS.PANGO_SCALE, &index, &trailing);
841         auto ptr = OS.pango_layout_get_text (layout);
842         position = OS.g_utf8_pointer_to_offset (ptr, ptr + index) + trailing;
843     } else {
844         GtkTextIter p;
845         OS.gtk_text_view_get_iter_at_location (cast(GtkTextView*)handle, &p, point.x, point.y);
846         position = OS.gtk_text_iter_get_offset (&p);
847     }
848     return cast(int)/*64bit*/position;
849 }
850 
851 /**
852  * Returns a <code>Point</code> whose x coordinate is the
853  * character position representing the start of the selected
854  * text, and whose y coordinate is the character position
855  * representing the end of the selection. An "empty" selection
856  * is indicated by the x and y coordinates having the same value.
857  * <p>
858  * Indexing is zero based.  The range of a selection is from
859  * 0..N where N is the number of characters in the widget.
860  * </p>
861  *
862  * @return a point representing the selection start and end
863  *
864  * @exception SWTException <ul>
865  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
866  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
867  * </ul>
868  */
869 public Point getSelection () {
870     checkWidget ();
871     if ((style & SWT.SINGLE) !is 0) {
872         int start;
873         int end;
874         OS.gtk_editable_get_selection_bounds (cast(GtkEditable*)handle, &start, &end);
875         return new Point (start, end);
876     }
877     GtkTextIter start;
878     GtkTextIter end;
879     OS.gtk_text_buffer_get_selection_bounds (bufferHandle, &start, &end);
880     return new Point (OS.gtk_text_iter_get_offset (&start), OS.gtk_text_iter_get_offset (&end));
881 }
882 
883 /**
884  * Returns the number of selected characters.
885  *
886  * @return the number of selected characters.
887  *
888  * @exception SWTException <ul>
889  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
890  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
891  * </ul>
892  */
893 public int getSelectionCount () {
894     checkWidget ();
895     Point selection = getSelection ();
896     return Math.abs (selection.y - selection.x);
897 }
898 
899 /**
900  * Gets the selected text, or an empty string if there is no current selection.
901  *
902  * @return the selected text
903  *
904  * @exception SWTException <ul>
905  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
906  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
907  * </ul>
908  */
909 public String getSelectionText () {
910     checkWidget ();
911     Point selection = getSelection ();
912     return getText ()[ selection.x .. selection.y ];
913 }
914 
915 /**
916  * Returns the number of tabs.
917  * <p>
918  * Tab stop spacing is specified in terms of the
919  * space (' ') character.  The width of a single
920  * tab stop is the pixel width of the spaces.
921  * </p>
922  *
923  * @return the number of tab characters
924  *
925  * @exception SWTException <ul>
926  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
927  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
928  * </ul>
929  */
930 public int getTabs () {
931     checkWidget ();
932     return tabs;
933 }
934 
935 int getTabWidth (int tabs) {
936     auto layout = OS.gtk_widget_create_pango_layout (handle, " ".ptr );
937     int width;
938     int height;
939     OS.pango_layout_get_size (layout, &width, &height);
940     OS.g_object_unref (layout);
941     return width * tabs;
942 }
943 
944 /**
945  * Returns the widget text.
946  * <p>
947  * The text for a text widget is the characters in the widget, or
948  * an empty string if this has never been set.
949  * </p>
950  *
951  * @return the widget text
952  *
953  * @exception SWTException <ul>
954  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
955  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
956  * </ul>
957  */
958 public String getText () {
959     checkWidget ();
960     char* address;
961     if ((style & SWT.SINGLE) !is 0) {
962         address = OS.gtk_entry_get_text (cast(GtkEntry*)handle);
963     } else {
964         GtkTextIter start;
965         GtkTextIter end;
966         OS.gtk_text_buffer_get_bounds (bufferHandle, &start, &end);
967         address = OS.gtk_text_buffer_get_text (bufferHandle, &start, &end, true);
968     }
969     if (address is null) return "";
970     String res = fromStringz( address )._idup();
971     if ((style & SWT.MULTI) !is 0) OS.g_free (address);
972     return res;
973 }
974 
975 /**
976  * Returns a range of text.  Returns an empty string if the
977  * start of the range is greater than the end.
978  * <p>
979  * Indexing is zero based.  The range of
980  * a selection is from 0..N-1 where N is
981  * the number of characters in the widget.
982  * </p>
983  *
984  * @param start the start of the range
985  * @param end the end of the range
986  * @return the range of text
987  *
988  * @exception SWTException <ul>
989  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
990  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
991  * </ul>
992  */
993 public String getText (int start, int end) {
994     checkWidget ();
995     if (!(start <= end && 0 <= end)) return "";
996     start = Math.max (0, start);
997     char* address;
998     if ((style & SWT.SINGLE) !is 0) {
999         address = OS.gtk_editable_get_chars (cast(GtkEditable*)handle, start, end + 1);
1000     } else {
1001         int length = OS.gtk_text_buffer_get_char_count (bufferHandle);
1002         end = Math.min (end, length - 1);
1003         GtkTextIter startIter;
1004         GtkTextIter endIter;
1005         OS.gtk_text_buffer_get_iter_at_offset (bufferHandle, &startIter, start);
1006         OS.gtk_text_buffer_get_iter_at_offset (bufferHandle, &endIter, end + 1);
1007         address = OS.gtk_text_buffer_get_text (bufferHandle, &startIter, &endIter, true);
1008     }
1009     if (address is null) error (SWT.ERROR_CANNOT_GET_TEXT);
1010     String res = fromStringz( address )._idup();
1011     OS.g_free (address);
1012     return res;
1013 }
1014 
1015 /**
1016  * Returns the maximum number of characters that the receiver is capable of holding.
1017  * <p>
1018  * If this has not been changed by <code>setTextLimit()</code>,
1019  * it will be the constant <code>Text.LIMIT</code>.
1020  * </p>
1021  *
1022  * @return the text limit
1023  *
1024  * @exception SWTException <ul>
1025  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1026  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1027  * </ul>
1028  *
1029  * @see #LIMIT
1030  */
1031 public int getTextLimit () {
1032     checkWidget ();
1033     if ((style & SWT.MULTI) !is 0) return LIMIT;
1034     int limit = OS.gtk_entry_get_max_length (cast(GtkEntry*)handle);
1035     return limit is 0 ? 0xFFFF : limit;
1036 }
1037 
1038 /**
1039  * Returns the zero-relative index of the line which is currently
1040  * at the top of the receiver.
1041  * <p>
1042  * This index can change when lines are scrolled or new lines are added or removed.
1043  * </p>
1044  *
1045  * @return the index of the top line
1046  *
1047  * @exception SWTException <ul>
1048  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1049  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1050  * </ul>
1051  */
1052 public int getTopIndex () {
1053     checkWidget ();
1054     if ((style & SWT.SINGLE) !is 0) return 0;
1055     GtkTextIter position;
1056     GdkRectangle rect;
1057     OS.gtk_text_view_get_visible_rect (cast(GtkTextView*)handle, &rect);
1058     OS.gtk_text_view_get_line_at_y (cast(GtkTextView*)handle, &position, rect.y, null);
1059     return OS.gtk_text_iter_get_line (&position);
1060 }
1061 
1062 /**
1063  * Returns the top pixel.
1064  * <p>
1065  * The top pixel is the pixel position of the line
1066  * that is currently at the top of the widget.  On
1067  * some platforms, a text widget can be scrolled by
1068  * pixels instead of lines so that a partial line
1069  * is displayed at the top of the widget.
1070  * </p><p>
1071  * The top pixel changes when the widget is scrolled.
1072  * The top pixel does not include the widget trimming.
1073  * </p>
1074  *
1075  * @return the pixel position of the top line
1076  *
1077  * @exception SWTException <ul>
1078  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1079  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1080  * </ul>
1081  */
1082 public int getTopPixel () {
1083     checkWidget ();
1084     if ((style & SWT.SINGLE) !is 0) return 0;
1085     GtkTextIter position;
1086     GdkRectangle rect;
1087     OS.gtk_text_view_get_visible_rect (cast(GtkTextView*)handle, &rect);
1088     int lineTop;
1089     OS.gtk_text_view_get_line_at_y (cast(GtkTextView*)handle, &position, rect.y, &lineTop);
1090     return lineTop;
1091 }
1092 
1093 override int gtk_activate (GtkWidget* widget) {
1094     postEvent (SWT.DefaultSelection);
1095     return 0;
1096 }
1097 
1098 override int gtk_button_press_event (GtkWidget* widget, GdkEventButton* gdkEvent) {
1099     auto result = super.gtk_button_press_event (widget, gdkEvent);
1100     if (result !is 0) return result;
1101     if (!doubleClick) {
1102         switch (gdkEvent.type) {
1103             case OS.GDK_2BUTTON_PRESS:
1104             case OS.GDK_3BUTTON_PRESS:
1105                 return 1;
1106             default:
1107         }
1108     }
1109     return result;
1110 }
1111 
1112 
1113 override int gtk_changed (GtkWidget* widget) {
1114     /*
1115     * Feature in GTK.  When the user types, GTK positions
1116     * the caret after sending the changed signal.  This
1117     * means that application code that attempts to position
1118     * the caret during a changed signal will fail.  The fix
1119     * is to post the modify event when the user is typing.
1120     */
1121     bool keyPress = false;
1122     auto eventPtr = OS.gtk_get_current_event ();
1123     if (eventPtr !is null) {
1124         GdkEventKey* gdkEvent = cast(GdkEventKey*)eventPtr;
1125         switch (gdkEvent.type) {
1126             case OS.GDK_KEY_PRESS:
1127                 keyPress = true;
1128                 break;
1129             default:
1130         }
1131         OS.gdk_event_free (eventPtr);
1132     }
1133     if (keyPress) {
1134         postEvent (SWT.Modify);
1135     } else {
1136         sendEvent (SWT.Modify);
1137     }
1138     return 0;
1139 }
1140 
1141 override int gtk_commit (GtkIMContext* imcontext, char* text) {
1142     if (text is null) return 0;
1143     if ((style & SWT.SINGLE) !is 0) {
1144         if (!OS.gtk_editable_get_editable (cast(GtkEditable*)handle)) return 0;
1145     }
1146     char [] chars = fromStringz( text ).dup;
1147     if (chars.length is 0) return 0;
1148     char [] newChars = sendIMKeyEvent (SWT.KeyDown, null, chars);
1149     if (newChars is null) return 0;
1150     /*
1151      * Feature in GTK.  For a GtkEntry, during the insert-text signal,
1152      * GTK allows the programmer to change only the caret location,
1153      * not the selection.  If the programmer changes the selection,
1154      * the new selection is lost.  The fix is to detect a selection
1155      * change and set it after the insert-text signal has completed.
1156      */
1157     fixStart = fixEnd = -1;
1158     OS.g_signal_handlers_block_matched (imContext, OS.G_SIGNAL_MATCH_DATA, 0, 0, null, null, udCOMMIT);
1159     int id = OS.g_signal_lookup (OS.commit.ptr, OS.gtk_im_context_get_type ());
1160     int mask =  OS.G_SIGNAL_MATCH_DATA | OS.G_SIGNAL_MATCH_ID;
1161     OS.g_signal_handlers_unblock_matched (imContext, mask, id, 0, null, null, cast(void*)handle);
1162     if (newChars is chars) {
1163         OS.g_signal_emit_by_name1 (imContext, OS.commit.ptr, cast(int)text);
1164     } else {
1165         OS.g_signal_emit_by_name1 (imContext, OS.commit.ptr, cast(int)toStringz(newChars) );
1166     }
1167     OS.g_signal_handlers_unblock_matched (imContext, OS.G_SIGNAL_MATCH_DATA, 0, 0, null, null, udCOMMIT );
1168     OS.g_signal_handlers_block_matched (imContext, mask, id, 0, null, null, cast(void*)handle);
1169     if ((style & SWT.SINGLE) !is 0) {
1170         if (fixStart !is -1 && fixEnd !is -1) {
1171             OS.gtk_editable_set_position (cast(GtkEditable*)handle, fixStart);
1172             OS.gtk_editable_select_region (cast(GtkEditable*)handle, fixStart, fixEnd);
1173         }
1174     }
1175     fixStart = fixEnd = -1;
1176     return 0;
1177 }
1178 
1179 override int gtk_delete_range (GtkWidget* widget, ptrdiff_t iter1, ptrdiff_t iter2) {
1180     if (!hooks (SWT.Verify) && !filters (SWT.Verify)) return 0;
1181     GtkTextIter startIter = *cast(GtkTextIter*)iter1;
1182     GtkTextIter endIter = *cast(GtkTextIter*)iter2;
1183     int start = OS.gtk_text_iter_get_offset (&startIter);
1184     int end = OS.gtk_text_iter_get_offset (&endIter);
1185     String newText = verifyText ("", start, end);
1186     if (newText is null) {
1187         /* Remember the selection when the text was deleted */
1188         OS.gtk_text_buffer_get_selection_bounds (bufferHandle, &startIter, &endIter);
1189         start = OS.gtk_text_iter_get_offset (&startIter);
1190         end = OS.gtk_text_iter_get_offset (&endIter);
1191         if (start !is end) {
1192             fixStart = start;
1193             fixEnd = end;
1194         }
1195         OS.g_signal_stop_emission_by_name (bufferHandle, OS.delete_range.ptr);
1196     } else {
1197         if (newText.length > 0) {
1198             OS.g_signal_handlers_block_matched (bufferHandle, OS.G_SIGNAL_MATCH_DATA, 0, 0, null, null, udCHANGED);
1199             OS.g_signal_handlers_block_matched (bufferHandle, OS.G_SIGNAL_MATCH_DATA, 0, 0, null, null, udDELETE_RANGE);
1200             OS.gtk_text_buffer_delete (bufferHandle, &startIter, &endIter);
1201             OS.g_signal_handlers_unblock_matched (bufferHandle, OS.G_SIGNAL_MATCH_DATA, 0, 0, null, null, udDELETE_RANGE);
1202             OS.g_signal_handlers_unblock_matched (bufferHandle, OS.G_SIGNAL_MATCH_DATA, 0, 0, null, null, udCHANGED);
1203             OS.g_signal_handlers_block_matched (bufferHandle, OS.G_SIGNAL_MATCH_DATA, 0, 0, null, null, udTEXT_BUFFER_INSERT_TEXT);
1204             OS.gtk_text_buffer_insert (bufferHandle, &startIter, newText.ptr, cast(int)/*64bit*/newText.length);
1205             OS.g_signal_handlers_unblock_matched (bufferHandle, OS.G_SIGNAL_MATCH_DATA, 0, 0, null, null, udTEXT_BUFFER_INSERT_TEXT);
1206             OS.g_signal_stop_emission_by_name (bufferHandle, OS.delete_range.ptr);
1207         }
1208     }
1209     return 0;
1210 }
1211 
1212 override int gtk_delete_text (GtkWidget* widget, ptrdiff_t start_pos, ptrdiff_t end_pos) {
1213     if (!hooks (SWT.Verify) && !filters (SWT.Verify)) return 0;
1214     String newText = verifyText ("", cast(int)/*64bit*/start_pos, cast(int)/*64bit*/end_pos);
1215     if (newText is null) {
1216         /* Remember the selection when the text was deleted */
1217         int newStart, newEnd;
1218         OS.gtk_editable_get_selection_bounds (cast(GtkEditable*)handle, &newStart, &newEnd);
1219         if (newStart !is newEnd) {
1220             fixStart = newStart;
1221             fixEnd = newEnd;
1222         }
1223         OS.g_signal_stop_emission_by_name (handle, OS.delete_text.ptr);
1224     } else {
1225         if (newText.length > 0) {
1226             int pos;
1227             pos = cast(int)/*64bit*/end_pos;
1228             OS.g_signal_handlers_block_matched (handle, OS.G_SIGNAL_MATCH_DATA, 0, 0, null, null, udCHANGED);
1229             OS.g_signal_handlers_block_matched (handle, OS.G_SIGNAL_MATCH_DATA, 0, 0, null, null, udINSERT_TEXT);
1230             OS.gtk_editable_insert_text (cast(GtkEditable*)handle, newText.ptr, cast(int)/*64bit*/newText.length, &pos);
1231             OS.g_signal_handlers_unblock_matched (handle, OS.G_SIGNAL_MATCH_DATA, 0, 0, null, null, udINSERT_TEXT);
1232             OS.g_signal_handlers_unblock_matched (handle, OS.G_SIGNAL_MATCH_DATA, 0, 0, null, null, udCHANGED);
1233             OS.gtk_editable_set_position (cast(GtkEditable*)handle, pos);
1234         }
1235     }
1236     return 0;
1237 }
1238 
1239 override int gtk_event_after (GtkWidget* widget, GdkEvent* event) {
1240     if (cursor !is null) gtk_setCursor (cursor.handle);
1241     /*
1242     * Feature in GTK.  The gtk-entry-select-on-focus property is a global
1243     * setting.  Return it to its default value after the GtkEntry has done
1244     * its focus in processing so that other widgets (such as the combo)
1245     * use the correct value.
1246     */
1247     if ((style & SWT.SINGLE) !is 0 && display.entrySelectOnFocus) {
1248         switch (event.type) {
1249             case OS.GDK_FOCUS_CHANGE:
1250                 GdkEventFocus* gdkEventFocus = cast(GdkEventFocus*)event;
1251                 if (gdkEventFocus.in_ is 0) {
1252                     auto settings = OS.gtk_settings_get_default ();
1253                     OS.g_object_set1 (settings, OS.gtk_entry_select_on_focus.ptr, true );
1254                 }
1255                 break;
1256             default:
1257         }
1258     }
1259     return super.gtk_event_after (widget, event);
1260 }
1261 
1262 override int gtk_focus_out_event (GtkWidget* widget, GdkEventFocus* event) {
1263     fixIM ();
1264     return super.gtk_focus_out_event (widget, event);
1265 }
1266 
1267 override int gtk_grab_focus (GtkWidget* widget) {
1268     auto result = super.gtk_grab_focus (widget);
1269     /*
1270     * Feature in GTK.  GtkEntry widgets select their text on focus in,
1271     * clearing the previous selection.  This behavior is controlled by
1272     * the gtk-entry-select-on-focus property.  The fix is to disable
1273     * this property when a GtkEntry is given focus and restore it after
1274     * the entry has done focus in processing.
1275     */
1276     if ((style & SWT.SINGLE) !is 0 && display.entrySelectOnFocus) {
1277         auto settings = OS.gtk_settings_get_default ();
1278         OS.g_object_set1 (settings, OS.gtk_entry_select_on_focus.ptr, false );
1279     }
1280     return result;
1281 }
1282 
1283 override int gtk_insert_text (GtkEditable* widget, char* new_text, ptrdiff_t new_text_length, ptrdiff_t position) {
1284     if (!hooks (SWT.Verify) && !filters (SWT.Verify)) return 0;
1285     if (new_text is null || new_text_length is 0) return 0;
1286     String oldText = (cast(char*)new_text)[ 0 .. new_text_length ]._idup();
1287     int pos;
1288     pos = *cast(int*)position;
1289     if (pos is -1) {
1290         auto ptr = OS.gtk_entry_get_text (cast(GtkEntry*)handle);
1291         pos = cast(int)/*64bit*/OS.g_utf8_strlen (ptr, -1);
1292     }
1293     /* Use the selection when the text was deleted */
1294     int start = pos, end = pos;
1295     if (fixStart !is -1 && fixEnd !is -1) {
1296         start = pos = fixStart;
1297         end = fixEnd;
1298         fixStart = fixEnd = -1;
1299     }
1300     String newText = verifyText (oldText, start, end);
1301     if (newText !is oldText) {
1302         int newStart, newEnd;
1303         OS.gtk_editable_get_selection_bounds (cast(GtkEditable*)handle, &newStart, &newEnd);
1304         if (newText !is null) {
1305             if (newStart !is newEnd) {
1306                 OS.g_signal_handlers_block_matched (handle, OS.G_SIGNAL_MATCH_DATA, 0, 0, null, null, udDELETE_TEXT);
1307                 OS.g_signal_handlers_block_matched (handle, OS.G_SIGNAL_MATCH_DATA, 0, 0, null, null, udCHANGED);
1308                 OS.gtk_editable_delete_selection (cast(GtkEditable*)handle);
1309                 OS.g_signal_handlers_unblock_matched (handle, OS.G_SIGNAL_MATCH_DATA, 0, 0, null, null, udDELETE_TEXT);
1310                 OS.g_signal_handlers_unblock_matched (handle, OS.G_SIGNAL_MATCH_DATA, 0, 0, null, null, udCHANGED);
1311             }
1312             OS.g_signal_handlers_block_matched (handle, OS.G_SIGNAL_MATCH_DATA, 0, 0, null, null, udINSERT_TEXT);
1313             OS.gtk_editable_insert_text (cast(GtkEditable*)handle, newText.ptr, cast(int)/*64bit*/newText.length, &pos);
1314             OS.g_signal_handlers_unblock_matched (handle, OS.G_SIGNAL_MATCH_DATA, 0, 0, null, null, udINSERT_TEXT);
1315             newStart = newEnd = pos;
1316         }
1317         pos  = newEnd;
1318         if (newStart !is newEnd) {
1319             fixStart = newStart;
1320             fixEnd = newEnd;
1321         }
1322         *cast(int*)position = pos;
1323         OS.g_signal_stop_emission_by_name (handle, OS.insert_text.ptr);
1324     }
1325     return 0;
1326 }
1327 
1328 override int gtk_key_press_event (GtkWidget* widget, GdkEventKey* event) {
1329     auto result = super.gtk_key_press_event (widget, event);
1330     if (result !is 0) fixIM ();
1331     if (gdkEventKey is cast(GdkEventKey*)-1) result = 1;
1332     gdkEventKey = null;
1333     return result;
1334 }
1335 
1336 override int gtk_populate_popup (GtkWidget* widget, GtkWidget* menu) {
1337     if ((style & SWT.RIGHT_TO_LEFT) !is 0) {
1338         OS.gtk_widget_set_direction (menu, OS.GTK_TEXT_DIR_RTL);
1339         display.doSetDirectionProc (menu, OS.GTK_TEXT_DIR_RTL);
1340     }
1341     return 0;
1342 }
1343 
1344 override int gtk_text_buffer_insert_text (GtkTextBuffer *widget, GtkTextIter *iter, char *text, ptrdiff_t len) {
1345     if (!hooks (SWT.Verify) && !filters (SWT.Verify)) return 0;
1346     GtkTextIter position = *iter;
1347     /* Use the selection when the text was deleted */
1348     int start = OS.gtk_text_iter_get_offset (&position), end = start;
1349     if (fixStart !is -1 && fixEnd !is -1) {
1350         start = fixStart;
1351         end = fixEnd;
1352         fixStart = fixEnd = -1;
1353     }
1354     String oldText = cast(String)text[ 0 .. len ];
1355     String newText = verifyText (oldText, start, end);
1356     if (newText is null) {
1357         OS.g_signal_stop_emission_by_name (bufferHandle, OS.insert_text.ptr);
1358     } else {
1359         if (newText !is oldText) {
1360             OS.g_signal_handlers_block_matched (bufferHandle, OS.G_SIGNAL_MATCH_DATA, 0, 0, null, null, udTEXT_BUFFER_INSERT_TEXT);
1361             OS.gtk_text_buffer_insert (bufferHandle, iter, newText.ptr, cast(int)/*64bit*/newText.length);
1362             OS.g_signal_handlers_unblock_matched (bufferHandle, OS.G_SIGNAL_MATCH_DATA, 0, 0, null, null, udTEXT_BUFFER_INSERT_TEXT);
1363             OS.g_signal_stop_emission_by_name (bufferHandle, OS.insert_text.ptr);
1364         }
1365     }
1366     return 0;
1367 }
1368 
1369 override void hookEvents () {
1370     super.hookEvents();
1371     if ((style & SWT.SINGLE) !is 0) {
1372         OS.g_signal_connect_closure (handle, OS.changed.ptr, display.closures [CHANGED], true);
1373         OS.g_signal_connect_closure (handle, OS.insert_text.ptr, display.closures [INSERT_TEXT], false);
1374         OS.g_signal_connect_closure (handle, OS.delete_text.ptr, display.closures [DELETE_TEXT], false);
1375         OS.g_signal_connect_closure (handle, OS.activate.ptr, display.closures [ACTIVATE], false);
1376         OS.g_signal_connect_closure (handle, OS.grab_focus.ptr, display.closures [GRAB_FOCUS], false);
1377         OS.g_signal_connect_closure (handle, OS.populate_popup.ptr, display.closures [POPULATE_POPUP], false);
1378     } else {
1379         OS.g_signal_connect_closure (bufferHandle, OS.changed.ptr, display.closures [CHANGED], false);
1380         OS.g_signal_connect_closure (bufferHandle, OS.insert_text.ptr, display.closures [TEXT_BUFFER_INSERT_TEXT], false);
1381         OS.g_signal_connect_closure (bufferHandle, OS.delete_range.ptr, display.closures [DELETE_RANGE], false);
1382         OS.g_signal_connect_closure (handle, OS.populate_popup.ptr, display.closures [POPULATE_POPUP], false);
1383     }
1384     auto imContext = imContext ();
1385     if (imContext !is null) {
1386         OS.g_signal_connect_closure (imContext, OS.commit.ptr, display.closures [COMMIT], false);
1387         int id = OS.g_signal_lookup (OS.commit.ptr, OS.gtk_im_context_get_type ());
1388         int mask =  OS.G_SIGNAL_MATCH_DATA | OS.G_SIGNAL_MATCH_ID;
1389         OS.g_signal_handlers_block_matched (imContext, mask, id, 0, null, null, cast(void*)handle);
1390     }
1391 }
1392 
1393 GtkIMContext* imContext () {
1394     if ((style & SWT.SINGLE) !is 0) {
1395         return OS.gtk_editable_get_editable (handle) ? OS.GTK_ENTRY_IM_CONTEXT (handle) : null;
1396     }
1397     return OS.GTK_TEXTVIEW_IM_CONTEXT (cast(GtkTextView*)handle);
1398 }
1399 
1400 /**
1401  * Inserts a string.
1402  * <p>
1403  * The old selection is replaced with the new text.
1404  * </p>
1405  *
1406  * @param string the string
1407  *
1408  * @exception SWTException <ul>
1409  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1410  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1411  * </ul>
1412  */
1413 public void insert (String string) {
1414     checkWidget ();
1415     // SWT extension: allow null for zero length string
1416     //if (string is null) error (SWT.ERROR_NULL_ARGUMENT);
1417     if ((style & SWT.SINGLE) !is 0) {
1418         int start, end;
1419         OS.gtk_editable_get_selection_bounds (cast(GtkEditable*)handle, &start, &end);
1420         OS.gtk_editable_delete_selection (cast(GtkEditable*)handle);
1421         OS.gtk_editable_insert_text (cast(GtkEditable*)handle, string.ptr, cast(int)/*64bit*/string.length, &start);
1422         OS.gtk_editable_set_position (cast(GtkEditable*)handle, start);
1423     } else {
1424         GtkTextIter start;
1425         GtkTextIter end;
1426         if (OS.gtk_text_buffer_get_selection_bounds (bufferHandle, &start, &end)) {
1427             OS.gtk_text_buffer_delete (bufferHandle, &start, &end);
1428         }
1429         OS.gtk_text_buffer_insert (bufferHandle, &start, string.ptr, cast(int)/*64bit*/string.length);
1430         OS.gtk_text_buffer_place_cursor (bufferHandle, &start);
1431         auto mark = OS.gtk_text_buffer_get_insert (bufferHandle);
1432         OS.gtk_text_view_scroll_mark_onscreen (cast(GtkTextView*)handle, mark);
1433     }
1434 }
1435 
1436 override GdkDrawable* paintWindow () {
1437     if ((style & SWT.SINGLE) !is 0) {
1438         auto window = super.paintWindow ();
1439         auto children = OS.gdk_window_get_children (window);
1440         if (children !is null) window = cast(GdkDrawable*) OS.g_list_data (children);
1441         OS.g_list_free (children);
1442         return window;
1443     }
1444     OS.gtk_widget_realize (handle);
1445     return OS.gtk_text_view_get_window (cast(GtkTextView*)handle, OS.GTK_TEXT_WINDOW_TEXT);
1446 }
1447 
1448 /**
1449  * Pastes text from clipboard.
1450  * <p>
1451  * The selected text is deleted from the widget
1452  * and new text inserted from the clipboard.
1453  * </p>
1454  *
1455  * @exception SWTException <ul>
1456  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1457  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1458  * </ul>
1459  */
1460 public void paste () {
1461     checkWidget ();
1462     if ((style & SWT.SINGLE) !is 0) {
1463         OS.gtk_editable_paste_clipboard (cast(GtkEditable*)handle);
1464     } else {
1465         auto clipboard = OS.gtk_clipboard_get (cast(void*)OS.GDK_NONE);
1466         OS.gtk_text_buffer_paste_clipboard (bufferHandle, clipboard, null, OS.gtk_text_view_get_editable (cast(GtkTextView*)handle));
1467     }
1468 }
1469 
1470 override void register () {
1471     super.register ();
1472     if (bufferHandle !is null) display.addWidget (cast(GtkWidget*)bufferHandle, this);
1473     auto imContext = imContext ();
1474     if (imContext !is null) display.addWidget (cast(GtkWidget*)imContext, this);
1475 }
1476 
1477 override void releaseWidget () {
1478     super.releaseWidget ();
1479     fixIM ();
1480     if (OS.GTK_VERSION < OS.buildVERSION (2, 6, 0)) {
1481         /*
1482         * Bug in GTK.  Any text copied into the clipboard will be lost when
1483         * the GtkTextView is destroyed.  The fix is to paste the contents as
1484         * the widget is being destroyed to reference the text buffer, keeping
1485         * it around until ownership of the clipboard is lost.
1486         */
1487         if ((style & SWT.MULTI) !is 0) {
1488             auto clipboard = OS.gtk_clipboard_get (cast(void*)OS.GDK_NONE);
1489             OS.gtk_text_buffer_paste_clipboard (bufferHandle, clipboard, null, OS.gtk_text_view_get_editable (cast(GtkTextView*)handle));
1490         }
1491     }
1492     message = null;
1493 }
1494 
1495 /**
1496  * Removes the listener from the collection of listeners who will
1497  * be notified when the receiver's text is modified.
1498  *
1499  * @param listener the listener which should no longer be notified
1500  *
1501  * @exception IllegalArgumentException <ul>
1502  *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
1503  * </ul>
1504  * @exception SWTException <ul>
1505  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1506  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1507  * </ul>
1508  *
1509  * @see ModifyListener
1510  * @see #addModifyListener
1511  */
1512 public void removeModifyListener (ModifyListener listener) {
1513     checkWidget ();
1514     if (listener is null) error (SWT.ERROR_NULL_ARGUMENT);
1515     if (eventTable is null) return;
1516     eventTable.unhook (SWT.Modify, listener);
1517 }
1518 
1519 /**
1520  * Removes the listener from the collection of listeners who will
1521  * be notified when the control is selected by the user.
1522  *
1523  * @param listener the listener which should no longer be notified
1524  *
1525  * @exception IllegalArgumentException <ul>
1526  *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
1527  * </ul>
1528  * @exception SWTException <ul>
1529  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1530  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1531  * </ul>
1532  *
1533  * @see SelectionListener
1534  * @see #addSelectionListener
1535  */
1536 public void removeSelectionListener(SelectionListener listener) {
1537     checkWidget ();
1538     if (listener is null) error (SWT.ERROR_NULL_ARGUMENT);
1539     if (eventTable is null) return;
1540     eventTable.unhook(SWT.Selection, listener);
1541     eventTable.unhook(SWT.DefaultSelection,listener);
1542 }
1543 
1544 /**
1545  * Removes the listener from the collection of listeners who will
1546  * be notified when the control is verified.
1547  *
1548  * @param listener the listener which should no longer be notified
1549  *
1550  * @exception IllegalArgumentException <ul>
1551  *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
1552  * </ul>
1553  * @exception SWTException <ul>
1554  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1555  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1556  * </ul>
1557  *
1558  * @see VerifyListener
1559  * @see #addVerifyListener
1560  */
1561 public void removeVerifyListener (VerifyListener listener) {
1562     checkWidget ();
1563     if (listener is null) error (SWT.ERROR_NULL_ARGUMENT);
1564     if (eventTable is null) return;
1565     eventTable.unhook (SWT.Verify, listener);
1566 }
1567 
1568 /**
1569  * Selects all the text in the receiver.
1570  *
1571  * @exception SWTException <ul>
1572  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1573  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1574  * </ul>
1575  */
1576 public void selectAll () {
1577     checkWidget ();
1578     if ((style & SWT.SINGLE) !is 0) {
1579         OS.gtk_editable_select_region (cast(GtkEditable*)handle, 0, -1);
1580     } else {
1581         GtkTextIter start;
1582         GtkTextIter end;
1583         OS.gtk_text_buffer_get_iter_at_offset (bufferHandle, &start, 0);
1584         OS.gtk_text_buffer_get_end_iter (bufferHandle, &end);
1585         auto insertMark = OS.gtk_text_buffer_get_insert (bufferHandle);
1586         auto selectionMark = OS.gtk_text_buffer_get_selection_bound (bufferHandle);
1587         OS.gtk_text_buffer_move_mark (bufferHandle, selectionMark, &start);
1588         OS.gtk_text_buffer_move_mark (bufferHandle, insertMark, &end);
1589     }
1590 }
1591 
1592 override void setBackgroundColor (GdkColor* color) {
1593     super.setBackgroundColor (color);
1594     OS.gtk_widget_modify_base (handle, 0, color);
1595 }
1596 
1597 override void gtk_setCursor (GdkCursor* cursor) {
1598     GdkCursor* defaultCursor;
1599     if (cursor is null) defaultCursor = OS.gdk_cursor_new (OS.GDK_XTERM);
1600     super.gtk_setCursor (cursor !is null ? cursor : defaultCursor);
1601     if (cursor is null) OS.gdk_cursor_destroy (defaultCursor);
1602 }
1603 
1604 /**
1605  * Sets the double click enabled flag.
1606  * <p>
1607  * The double click flag enables or disables the
1608  * default action of the text widget when the user
1609  * double clicks.
1610  * </p><p>
1611  * Note: This operation is a hint and is not supported on
1612  * platforms that do not have this concept.
1613  * </p>
1614  *
1615  * @param doubleClick the new double click flag
1616  *
1617  * @exception SWTException <ul>
1618  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1619  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1620  * </ul>
1621  */
1622 public void setDoubleClickEnabled (bool doubleClick) {
1623     checkWidget ();
1624     this.doubleClick = doubleClick;
1625 }
1626 
1627 /**
1628  * Sets the echo character.
1629  * <p>
1630  * The echo character is the character that is
1631  * displayed when the user enters text or the
1632  * text is changed by the programmer. Setting
1633  * the echo character to '\0' clears the echo
1634  * character and redraws the original text.
1635  * If for any reason the echo character is invalid,
1636  * or if the platform does not allow modification
1637  * of the echo character, the default echo character
1638  * for the platform is used.
1639  * </p>
1640  *
1641  * @param echo the new echo character
1642  *
1643  * @exception SWTException <ul>
1644  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1645  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1646  * </ul>
1647  */
1648 public void setEchoChar (char echo) {
1649     checkWidget ();
1650     if ((style & SWT.SINGLE) !is 0) {
1651         OS.gtk_entry_set_visibility (cast(GtkEntry*)handle, echo is '\0');
1652         OS.gtk_entry_set_invisible_char (cast(GtkEntry*)handle, echo);
1653     }
1654 }
1655 
1656 /**
1657  * Sets the editable state.
1658  *
1659  * @param editable the new editable state
1660  *
1661  * @exception SWTException <ul>
1662  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1663  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1664  * </ul>
1665  */
1666 public void setEditable (bool editable) {
1667     checkWidget ();
1668     style &= ~SWT.READ_ONLY;
1669     if (!editable) style |= SWT.READ_ONLY;
1670     if ((style & SWT.SINGLE) !is 0) {
1671         OS.gtk_editable_set_editable (cast(GtkEditable*)handle, editable);
1672     } else {
1673         OS.gtk_text_view_set_editable (cast(GtkTextView*)handle, editable);
1674     }
1675 }
1676 
1677 override void setFontDescription (PangoFontDescription* font) {
1678     super.setFontDescription (font);
1679     setTabStops (tabs);
1680 }
1681 
1682 /**
1683  * Sets the widget message. When the widget is created
1684  * with the style <code>SWT.SEARCH</code>, the message text
1685  * is displayed as a hint for the user, indicating the
1686  * purpose of the field.
1687  * <p>
1688  * Note: This operation is a <em>HINT</em> and is not
1689  * supported on platforms that do not have this concept.
1690  * </p>
1691  *
1692  * @param message the new message
1693  *
1694  * @exception IllegalArgumentException <ul>
1695  *    <li>ERROR_NULL_ARGUMENT - if the message is null</li>
1696  * </ul>
1697  * @exception SWTException <ul>
1698  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1699  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1700  * </ul>
1701  *
1702  * @since 3.3
1703  */
1704 public void setMessage (String message) {
1705     checkWidget ();
1706     // SWT extension: allow null for zero length string
1707     //if (message is null) error (SWT.ERROR_NULL_ARGUMENT);
1708     this.message = message;
1709 }
1710 
1711 /**
1712  * Sets the orientation of the receiver, which must be one
1713  * of the constants <code>SWT.LEFT_TO_RIGHT</code> or <code>SWT.RIGHT_TO_LEFT</code>.
1714  * <p>
1715  * Note: This operation is a hint and is not supported on
1716  * platforms that do not have this concept.
1717  * </p>
1718  *
1719  * @param orientation new orientation style
1720  *
1721  * @exception SWTException <ul>
1722  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1723  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1724  * </ul>
1725  *
1726  * @since 2.1.2
1727  */
1728 public void setOrientation (int orientation) {
1729     checkWidget();
1730 }
1731 
1732 /**
1733  * Sets the selection.
1734  * <p>
1735  * Indexing is zero based.  The range of
1736  * a selection is from 0..N where N is
1737  * the number of characters in the widget.
1738  * </p><p>
1739  * Text selections are specified in terms of
1740  * caret positions.  In a text widget that
1741  * contains N characters, there are N+1 caret
1742  * positions, ranging from 0..N.  This differs
1743  * from other functions that address character
1744  * position such as getText () that use the
1745  * regular array indexing rules.
1746  * </p>
1747  *
1748  * @param start new caret position
1749  *
1750  * @exception SWTException <ul>
1751  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1752  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1753  * </ul>
1754  */
1755 public void setSelection (int start) {
1756     checkWidget ();
1757     if ((style & SWT.SINGLE) !is 0) {
1758         OS.gtk_editable_set_position (cast(GtkEditable*)handle, start);
1759     } else {
1760         GtkTextIter position;
1761         OS.gtk_text_buffer_get_iter_at_offset (bufferHandle, &position, start);
1762         OS.gtk_text_buffer_place_cursor (bufferHandle, &position);
1763         auto mark = OS.gtk_text_buffer_get_insert (bufferHandle);
1764         OS.gtk_text_view_scroll_mark_onscreen (cast(GtkTextView*)handle, mark);
1765     }
1766 }
1767 
1768 /**
1769  * Sets the selection to the range specified
1770  * by the given start and end indices.
1771  * <p>
1772  * Indexing is zero based.  The range of
1773  * a selection is from 0..N where N is
1774  * the number of characters in the widget.
1775  * </p><p>
1776  * Text selections are specified in terms of
1777  * caret positions.  In a text widget that
1778  * contains N characters, there are N+1 caret
1779  * positions, ranging from 0..N.  This differs
1780  * from other functions that address character
1781  * position such as getText () that use the
1782  * usual array indexing rules.
1783  * </p>
1784  *
1785  * @param start the start of the range
1786  * @param end the end of the range
1787  *
1788  * @exception SWTException <ul>
1789  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1790  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1791  * </ul>
1792  */
1793 public void setSelection (int start, int end) {
1794     checkWidget ();
1795     if ((style & SWT.SINGLE) !is 0) {
1796         OS.gtk_editable_set_position (cast(GtkEditable*)handle, start);
1797         OS.gtk_editable_select_region (cast(GtkEditable*)handle, start, end);
1798     } else {
1799         GtkTextIter startIter;
1800         GtkTextIter endIter;
1801         OS.gtk_text_buffer_get_iter_at_offset (bufferHandle, &startIter, start);
1802         OS.gtk_text_buffer_get_iter_at_offset (bufferHandle, &endIter, end);
1803         auto insertMark = OS.gtk_text_buffer_get_insert (bufferHandle);
1804         auto selectionMark = OS.gtk_text_buffer_get_selection_bound (bufferHandle);
1805         OS.gtk_text_buffer_move_mark (bufferHandle, selectionMark, &startIter);
1806         OS.gtk_text_buffer_move_mark (bufferHandle, insertMark, &endIter);
1807     }
1808 }
1809 
1810 /**
1811  * Sets the selection to the range specified
1812  * by the given point, where the x coordinate
1813  * represents the start index and the y coordinate
1814  * represents the end index.
1815  * <p>
1816  * Indexing is zero based.  The range of
1817  * a selection is from 0..N where N is
1818  * the number of characters in the widget.
1819  * </p><p>
1820  * Text selections are specified in terms of
1821  * caret positions.  In a text widget that
1822  * contains N characters, there are N+1 caret
1823  * positions, ranging from 0..N.  This differs
1824  * from other functions that address character
1825  * position such as getText () that use the
1826  * usual array indexing rules.
1827  * </p>
1828  *
1829  * @param selection the point
1830  *
1831  * @exception IllegalArgumentException <ul>
1832  *    <li>ERROR_NULL_ARGUMENT - if the point is null</li>
1833  * </ul>
1834  * @exception SWTException <ul>
1835  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1836  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1837  * </ul>
1838  */
1839 public void setSelection (Point selection) {
1840     checkWidget ();
1841     if (selection is null) error (SWT.ERROR_NULL_ARGUMENT);
1842     setSelection (selection.x, selection.y);
1843 }
1844 
1845 /**
1846  * Sets the number of tabs.
1847  * <p>
1848  * Tab stop spacing is specified in terms of the
1849  * space (' ') character.  The width of a single
1850  * tab stop is the pixel width of the spaces.
1851  * </p>
1852  *
1853  * @param tabs the number of tabs
1854  *
1855  * </ul>
1856  * @exception SWTException <ul>
1857  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1858  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1859  * </ul>
1860  */
1861 public void setTabs (int tabs) {
1862     checkWidget ();
1863     if (tabs < 0) return;
1864     setTabStops (this.tabs = tabs);
1865 }
1866 
1867 void setTabStops (int tabs) {
1868     if ((style & SWT.SINGLE) !is 0) return;
1869     int tabWidth = getTabWidth (tabs);
1870     auto tabArray = OS.pango_tab_array_new (1, false);
1871     OS.pango_tab_array_set_tab (tabArray, 0, OS.PANGO_TAB_LEFT, tabWidth);
1872     OS.gtk_text_view_set_tabs (cast(GtkTextView*)handle, tabArray);
1873     OS.pango_tab_array_free (tabArray);
1874 }
1875 
1876 /**
1877  * Sets the contents of the receiver to the given string. If the receiver has style
1878  * SINGLE and the argument contains multiple lines of text, the result of this
1879  * operation is undefined and may vary from platform to platform.
1880  *
1881  * @param string the new text
1882  *
1883  * @exception SWTException <ul>
1884  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1885  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1886  * </ul>
1887  */
1888 public void setText (String string) {
1889     checkWidget ();
1890     // SWT extension: allow null for zero length string
1891     //if (string is null) error (SWT.ERROR_NULL_ARGUMENT);
1892     /*
1893     * Feature in gtk.  When text is set in gtk, separate events are fired for the deletion and
1894     * insertion of the text.  This is not wrong, but is inconsistent with other platforms.  The
1895     * fix is to block the firing of these events and fire them ourselves in a consistent manner.
1896     */
1897     if (hooks (SWT.Verify) || filters (SWT.Verify)) {
1898         string = verifyText (string, 0, getCharCount ());
1899         if (string is null) return;
1900     }
1901     if ((style & SWT.SINGLE) !is 0) {
1902         OS.g_signal_handlers_block_matched (handle, OS.G_SIGNAL_MATCH_DATA, 0, 0, null, null, udCHANGED);
1903         OS.g_signal_handlers_block_matched (handle, OS.G_SIGNAL_MATCH_DATA, 0, 0, null, null, udDELETE_TEXT);
1904         OS.g_signal_handlers_block_matched (handle, OS.G_SIGNAL_MATCH_DATA, 0, 0, null, null, udINSERT_TEXT);
1905         OS.gtk_entry_set_text (cast(GtkEntry*)handle, toStringz(string) );
1906         OS.g_signal_handlers_unblock_matched (handle, OS.G_SIGNAL_MATCH_DATA, 0, 0, null, null, udCHANGED);
1907         OS.g_signal_handlers_unblock_matched (handle, OS.G_SIGNAL_MATCH_DATA, 0, 0, null, null, udDELETE_TEXT);
1908         OS.g_signal_handlers_unblock_matched (handle, OS.G_SIGNAL_MATCH_DATA, 0, 0, null, null, udINSERT_TEXT);
1909     } else {
1910         GtkTextIter position;
1911         OS.g_signal_handlers_block_matched (bufferHandle, OS.G_SIGNAL_MATCH_DATA, 0, 0, null, null, udCHANGED);
1912         OS.g_signal_handlers_block_matched (bufferHandle, OS.G_SIGNAL_MATCH_DATA, 0, 0, null, null, udDELETE_RANGE);
1913         OS.g_signal_handlers_block_matched (bufferHandle, OS.G_SIGNAL_MATCH_DATA, 0, 0, null, null, udTEXT_BUFFER_INSERT_TEXT);
1914         OS.gtk_text_buffer_set_text (bufferHandle, string.ptr, cast(int)/*64bit*/string.length);
1915         OS.g_signal_handlers_unblock_matched (bufferHandle, OS.G_SIGNAL_MATCH_DATA, 0, 0, null, null, udCHANGED);
1916         OS.g_signal_handlers_unblock_matched (bufferHandle, OS.G_SIGNAL_MATCH_DATA, 0, 0, null, null, udDELETE_RANGE);
1917         OS.g_signal_handlers_unblock_matched (bufferHandle, OS.G_SIGNAL_MATCH_DATA, 0, 0, null, null, udTEXT_BUFFER_INSERT_TEXT);
1918         OS.gtk_text_buffer_get_iter_at_offset (bufferHandle, &position, 0);
1919         OS.gtk_text_buffer_place_cursor (bufferHandle, &position);
1920         auto mark = OS.gtk_text_buffer_get_insert (bufferHandle);
1921         OS.gtk_text_view_scroll_mark_onscreen (cast(GtkTextView*)handle, mark);
1922     }
1923     sendEvent (SWT.Modify);
1924 }
1925 
1926 /**
1927  * Sets the maximum number of characters that the receiver
1928  * is capable of holding to be the argument.
1929  * <p>
1930  * Instead of trying to set the text limit to zero, consider
1931  * creating a read-only text widget.
1932  * </p><p>
1933  * To reset this value to the default, use <code>setTextLimit(Text.LIMIT)</code>.
1934  * Specifying a limit value larger than <code>Text.LIMIT</code> sets the
1935  * receiver's limit to <code>Text.LIMIT</code>.
1936  * </p>
1937  *
1938  * @param limit new text limit
1939  *
1940  * @exception IllegalArgumentException <ul>
1941  *    <li>ERROR_CANNOT_BE_ZERO - if the limit is zero</li>
1942  * </ul>
1943  * @exception SWTException <ul>
1944  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1945  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1946  * </ul>
1947  *
1948  * @see #LIMIT
1949  */
1950 public void setTextLimit (int limit) {
1951     checkWidget ();
1952     if (limit is 0) error (SWT.ERROR_CANNOT_BE_ZERO);
1953     if ((style & SWT.SINGLE) !is 0) OS.gtk_entry_set_max_length (cast(GtkEntry*)handle, limit);
1954 }
1955 
1956 /**
1957  * Sets the zero-relative index of the line which is currently
1958  * at the top of the receiver. This index can change when lines
1959  * are scrolled or new lines are added and removed.
1960  *
1961  * @param index the index of the top item
1962  *
1963  * @exception SWTException <ul>
1964  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1965  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1966  * </ul>
1967  */
1968 public void setTopIndex (int index) {
1969     checkWidget ();
1970     if ((style & SWT.SINGLE) !is 0) return;
1971     GtkTextIter position;
1972     OS.gtk_text_buffer_get_iter_at_line (bufferHandle, &position, index);
1973     OS.gtk_text_view_scroll_to_iter (cast(GtkTextView*)handle, &position, 0, true, 0, 0);
1974 }
1975 
1976 /**
1977  * Shows the selection.
1978  * <p>
1979  * If the selection is already showing
1980  * in the receiver, this method simply returns.  Otherwise,
1981  * lines are scrolled until the selection is visible.
1982  * </p>
1983  *
1984  * @exception SWTException <ul>
1985  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1986  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1987  * </ul>
1988  */
1989 public void showSelection () {
1990     checkWidget ();
1991     if ((style & SWT.SINGLE) !is 0) return;
1992     auto mark = OS.gtk_text_buffer_get_selection_bound (bufferHandle);
1993     OS.gtk_text_view_scroll_mark_onscreen (cast(GtkTextView*)handle, mark);
1994     mark = OS.gtk_text_buffer_get_insert (bufferHandle);
1995     OS.gtk_text_view_scroll_mark_onscreen (cast(GtkTextView*)handle, mark);
1996 }
1997 
1998 override bool translateTraversal (GdkEventKey* keyEvent) {
1999     int key = keyEvent.keyval;
2000     GtkIMContext* imContext = null;
2001     switch (key) {
2002         case OS.GDK_KP_Enter:
2003         case OS.GDK_Return: {
2004             imContext =  this.imContext ();
2005             if (imContext !is null) {
2006                 char* preeditString;
2007                 OS.gtk_im_context_get_preedit_string (imContext, &preeditString, null, null);
2008                 if (preeditString !is null) {
2009                     int length = OS.strlen (preeditString);
2010                     OS.g_free (preeditString);
2011                     if (length !is 0) return false;
2012                 }
2013             }
2014             default:
2015         }
2016     }
2017     return super.translateTraversal (keyEvent);
2018 }
2019 
2020 override int traversalCode (int key, GdkEventKey* event) {
2021     int bits = super.traversalCode (key, event);
2022     if ((style & SWT.READ_ONLY) !is 0)  return bits;
2023     if ((style & SWT.MULTI) !is 0) {
2024         bits &= ~SWT.TRAVERSE_RETURN;
2025         if (key is OS.GDK_Tab && event !is null) {
2026             bool next = (event.state & OS.GDK_SHIFT_MASK) is 0;
2027             if (next && (event.state & OS.GDK_CONTROL_MASK) is 0) {
2028                 bits &= ~(SWT.TRAVERSE_TAB_NEXT | SWT.TRAVERSE_TAB_PREVIOUS);
2029             }
2030         }
2031     }
2032     return bits;
2033 }
2034 
2035 String verifyText (String string, int start, int end) {
2036     if (string.length is 0 && start is end) return null;
2037     Event event = new Event ();
2038     event.text = string;
2039     event.start = start;
2040     event.end = end;
2041     auto eventPtr = OS.gtk_get_current_event ();
2042     if (eventPtr !is null) {
2043         GdkEventKey* gdkEvent = cast(GdkEventKey*)eventPtr;
2044         switch (gdkEvent.type) {
2045             case OS.GDK_KEY_PRESS:
2046                 setKeyState (event, gdkEvent);
2047                 break;
2048             default:
2049         }
2050         OS.gdk_event_free (eventPtr);
2051     }
2052     /*
2053      * It is possible (but unlikely), that application
2054      * code could have disposed the widget in the verify
2055      * event.  If this happens, answer null to cancel
2056      * the operation.
2057      */
2058     sendEvent (SWT.Verify, event);
2059     if (!event.doit || isDisposed ()) return null;
2060     return event.text;
2061 }
2062 
2063 }