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 }