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.Spinner; 14 15 16 import org.eclipse.swt.widgets.Composite; 17 import org.eclipse.swt.widgets.TypedListener; 18 import org.eclipse.swt.widgets.Event; 19 import org.eclipse.swt.widgets.Widget; 20 import org.eclipse.swt.SWT; 21 import org.eclipse.swt.internal.gtk.OS; 22 import org.eclipse.swt.graphics.Point; 23 import org.eclipse.swt.graphics.Rectangle; 24 import org.eclipse.swt.events.SelectionListener; 25 import org.eclipse.swt.events.SelectionEvent; 26 import org.eclipse.swt.events.ModifyListener; 27 import org.eclipse.swt.events.VerifyListener; 28 29 import java.lang.all; 30 version(Tango){ 31 import tango.util.Convert; 32 } else { // Phobos 33 import std.conv; 34 } 35 36 /** 37 * Instances of this class are selectable user interface 38 * objects that allow the user to enter and modify numeric 39 * values. 40 * <p> 41 * Note that although this class is a subclass of <code>Composite</code>, 42 * it does not make sense to add children to it, or set a layout on it. 43 * </p><p> 44 * <dl> 45 * <dt><b>Styles:</b></dt> 46 * <dd>READ_ONLY, WRAP</dd> 47 * <dt><b>Events:</b></dt> 48 * <dd>Selection, Modify, Verify</dd> 49 * </dl> 50 * </p><p> 51 * IMPORTANT: This class is <em>not</em> intended to be subclassed. 52 * </p> 53 * 54 * @see <a href="http://www.eclipse.org/swt/snippets/#spinner">Spinner snippets</a> 55 * @see <a href="http://www.eclipse.org/swt/examples.php">SWT Example: ControlExample</a> 56 * @see <a href="http://www.eclipse.org/swt/">Sample code and further information</a> 57 * 58 * @since 3.1 59 */ 60 public class Spinner : Composite { 61 62 alias Composite.computeSize computeSize; 63 alias Composite.createHandle createHandle; 64 alias Composite.setBackgroundColor setBackgroundColor; 65 alias Composite.setCursor setCursor; 66 alias Composite.translateTraversal translateTraversal; 67 68 static const int INNER_BORDER = 2; 69 static const int MIN_ARROW_WIDTH = 6; 70 int lastEventTime = 0; 71 GdkEventKey* gdkEventKey; 72 int fixStart = -1, fixEnd = -1; 73 74 /** 75 * the operating system limit for the number of characters 76 * that the text field in an instance of this class can hold 77 * 78 * @since 3.4 79 */ 80 public const static int LIMIT = 0x7FFFFFFF; 81 82 /** 83 * Constructs a new instance of this class given its parent 84 * and a style value describing its behavior and appearance. 85 * <p> 86 * The style value is either one of the style constants defined in 87 * class <code>SWT</code> which is applicable to instances of this 88 * class, or must be built by <em>bitwise OR</em>'ing together 89 * (that is, using the <code>int</code> "|" operator) two or more 90 * of those <code>SWT</code> style constants. The class description 91 * lists the style constants that are applicable to the class. 92 * Style bits are also inherited from superclasses. 93 * </p> 94 * 95 * @param parent a composite control which will be the parent of the new instance (cannot be null) 96 * @param style the style of control to construct 97 * 98 * @exception IllegalArgumentException <ul> 99 * <li>ERROR_NULL_ARGUMENT - if the parent is null</li> 100 * </ul> 101 * @exception SWTException <ul> 102 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li> 103 * <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li> 104 * </ul> 105 * 106 * @see SWT#READ_ONLY 107 * @see SWT#WRAP 108 * @see Widget#checkSubclass 109 * @see Widget#getStyle 110 */ 111 public this (Composite parent, int style) { 112 super (parent, checkStyle (style)); 113 } 114 115 /** 116 * Adds the listener to the collection of listeners who will 117 * be notified when the receiver's text is modified, by sending 118 * it one of the messages defined in the <code>ModifyListener</code> 119 * interface. 120 * 121 * @param listener the listener which should be notified 122 * 123 * @exception IllegalArgumentException <ul> 124 * <li>ERROR_NULL_ARGUMENT - if the listener is null</li> 125 * </ul> 126 * @exception SWTException <ul> 127 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 128 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 129 * </ul> 130 * 131 * @see ModifyListener 132 * @see #removeModifyListener 133 */ 134 public void addModifyListener (ModifyListener listener) { 135 checkWidget (); 136 if (listener is null) error (SWT.ERROR_NULL_ARGUMENT); 137 TypedListener typedListener = new TypedListener (listener); 138 addListener (SWT.Modify, typedListener); 139 } 140 141 /** 142 * Adds the listener to the collection of listeners who will 143 * be notified when the control is selected by the user, by sending 144 * it one of the messages defined in the <code>SelectionListener</code> 145 * interface. 146 * <p> 147 * <code>widgetSelected</code> is not called for texts. 148 * <code>widgetDefaultSelected</code> is typically called when ENTER is pressed in a single-line text. 149 * </p> 150 * 151 * @param listener the listener which should be notified when the control is selected by the user 152 * 153 * @exception IllegalArgumentException <ul> 154 * <li>ERROR_NULL_ARGUMENT - if the listener is null</li> 155 * </ul> 156 * @exception SWTException <ul> 157 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 158 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 159 * </ul> 160 * 161 * @see SelectionListener 162 * @see #removeSelectionListener 163 * @see SelectionEvent 164 */ 165 public void addSelectionListener(SelectionListener listener) { 166 checkWidget (); 167 if (listener is null) error (SWT.ERROR_NULL_ARGUMENT); 168 TypedListener typedListener = new TypedListener(listener); 169 addListener(SWT.Selection,typedListener); 170 addListener(SWT.DefaultSelection,typedListener); 171 } 172 173 /** 174 * Adds the listener to the collection of listeners who will 175 * be notified when the receiver's text is verified, by sending 176 * it one of the messages defined in the <code>VerifyListener</code> 177 * interface. 178 * 179 * @param listener the listener which should be notified 180 * 181 * @exception IllegalArgumentException <ul> 182 * <li>ERROR_NULL_ARGUMENT - if the listener is null</li> 183 * </ul> 184 * @exception SWTException <ul> 185 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 186 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 187 * </ul> 188 * 189 * @see VerifyListener 190 * @see #removeVerifyListener 191 */ 192 void addVerifyListener (VerifyListener listener) { 193 checkWidget(); 194 if (listener is null) error (SWT.ERROR_NULL_ARGUMENT); 195 TypedListener typedListener = new TypedListener (listener); 196 addListener (SWT.Verify, typedListener); 197 } 198 199 static int checkStyle (int style) { 200 /* 201 * Even though it is legal to create this widget 202 * with scroll bars, they serve no useful purpose 203 * because they do not automatically scroll the 204 * widget's client area. The fix is to clear 205 * the SWT style. 206 */ 207 return style & ~(SWT.H_SCROLL | SWT.V_SCROLL); 208 } 209 210 protected override void checkSubclass () { 211 if (!isValidSubclass ()) error (SWT.ERROR_INVALID_SUBCLASS); 212 } 213 214 public override Point computeSize (int wHint, int hHint, bool changed) { 215 checkWidget (); 216 if (wHint !is SWT.DEFAULT && wHint < 0) wHint = 0; 217 if (hHint !is SWT.DEFAULT && hHint < 0) hHint = 0; 218 int[1] w, h; 219 OS.gtk_widget_realize (handle); 220 auto layout = OS.gtk_entry_get_layout (cast(GtkEntry*)handle); 221 auto adjustment = OS.gtk_spin_button_get_adjustment (cast(GtkSpinButton*)handle); 222 int digits = OS.gtk_spin_button_get_digits (cast(GtkSpinButton*)handle); 223 for (int i = 0; i < digits; i++) adjustment.upper *= 10; 224 String string = to!(String)( (cast(int) adjustment.upper) ); 225 if (digits > 0) { 226 //PROTING_TODO: Efficiency 227 String buffer = string ~ getDecimalSeparator (); 228 ptrdiff_t count = digits - string.length; 229 while (count >= 0) { 230 buffer ~= "0"; 231 count--; 232 } 233 string = buffer; 234 } 235 auto buffer1 = string; 236 auto ptr = OS.pango_layout_get_text (layout); 237 String buffer2 = fromStringz( ptr )._idup(); 238 OS.pango_layout_set_text (layout, buffer1.ptr, cast(int)/*64bit*/buffer1.length); 239 OS.pango_layout_get_size (layout, w.ptr, h.ptr); 240 OS.pango_layout_set_text (layout, buffer2.ptr, cast(int)/*64bit*/buffer2.length); 241 int width = OS.PANGO_PIXELS (w [0]); 242 int height = OS.PANGO_PIXELS (h [0]); 243 width = wHint is SWT.DEFAULT ? width : wHint; 244 height = hHint is SWT.DEFAULT ? height : hHint; 245 Rectangle trim = computeTrim (0, 0, width, height); 246 return new Point (trim.width, trim.height); 247 } 248 249 override public Rectangle computeTrim (int x, int y, int width, int height) { 250 checkWidget (); 251 int xborder = 0, yborder = 0; 252 auto style = OS.gtk_widget_get_style (handle); 253 if ((this.style & SWT.BORDER) !is 0) { 254 xborder += OS.gtk_style_get_xthickness (style); 255 yborder += OS.gtk_style_get_ythickness (style); 256 } 257 xborder += INNER_BORDER; 258 yborder += INNER_BORDER; 259 int property; 260 OS.gtk_widget_style_get1 (handle, OS.interior_focus.ptr, &property); 261 if (property is 0) { 262 OS.gtk_widget_style_get1 (handle, OS.focus_line_width.ptr, &property); 263 xborder += property; 264 yborder += property; 265 } 266 auto fontDesc = OS.gtk_style_get_font_desc (style); 267 int fontSize = OS.pango_font_description_get_size (fontDesc); 268 int arrowSize = Math.max (OS.PANGO_PIXELS (fontSize), MIN_ARROW_WIDTH); 269 arrowSize = arrowSize - arrowSize % 2; 270 Rectangle trim = super.computeTrim (x, y, width, height); 271 trim.x -= xborder; 272 trim.y -= yborder; 273 trim.width += 2 * xborder; 274 trim.height += 2 * yborder; 275 trim.width += arrowSize + (2 * OS.gtk_style_get_xthickness (style)); 276 return new Rectangle (trim.x, trim.y, trim.width, trim.height); 277 } 278 279 /** 280 * Copies the selected text. 281 * <p> 282 * The current selection is copied to the clipboard. 283 * </p> 284 * 285 * @exception SWTException <ul> 286 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 287 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 288 * </ul> 289 */ 290 public void copy () { 291 checkWidget (); 292 OS.gtk_editable_copy_clipboard (cast(GtkEditable*)handle); 293 } 294 295 override void createHandle (int index) { 296 state |= HANDLE | MENU; 297 fixedHandle = cast(GtkWidget*)OS.g_object_new (display.gtk_fixed_get_type (), null); 298 if (fixedHandle is null) error (SWT.ERROR_NO_HANDLES); 299 OS.gtk_fixed_set_has_window (cast(GtkFixed*)fixedHandle, true); 300 auto adjustment = OS.gtk_adjustment_new (0, 0, 100, 1, 10, 0); 301 if (adjustment is null) error (SWT.ERROR_NO_HANDLES); 302 handle = cast(GtkWidget*)OS.gtk_spin_button_new (cast(GtkAdjustment*)adjustment, 1, 0); 303 if (handle is null) error (SWT.ERROR_NO_HANDLES); 304 OS.gtk_container_add (cast(GtkContainer*)fixedHandle, handle); 305 OS.gtk_editable_set_editable (cast(GtkEditable*)handle, (style & SWT.READ_ONLY) is 0); 306 OS.gtk_entry_set_has_frame (cast(GtkEntry*)handle, (style & SWT.BORDER) !is 0); 307 OS.gtk_spin_button_set_wrap (cast(GtkSpinButton*)handle, (style & SWT.WRAP) !is 0); 308 } 309 310 /** 311 * Cuts the selected text. 312 * <p> 313 * The current selection is first copied to the 314 * clipboard and then deleted from the widget. 315 * </p> 316 * 317 * @exception SWTException <ul> 318 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 319 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 320 * </ul> 321 */ 322 public void cut () { 323 checkWidget (); 324 OS.gtk_editable_cut_clipboard (cast(GtkEditable*)handle); 325 } 326 327 override void deregister () { 328 super.deregister (); 329 auto imContext = imContext (); 330 if (imContext !is null) display.removeWidget (cast(GtkWidget*)imContext); 331 } 332 333 override GdkDrawable* eventWindow () { 334 return paintWindow (); 335 } 336 337 override GtkWidget* enterExitHandle () { 338 return fixedHandle; 339 } 340 341 override bool filterKey (int keyval, GdkEventKey* event) { 342 int time = OS.gdk_event_get_time (cast(GdkEvent*)event); 343 if (time !is lastEventTime) { 344 lastEventTime = time; 345 auto imContext = imContext (); 346 if (imContext !is null) { 347 return cast(bool)OS.gtk_im_context_filter_keypress (imContext, event); 348 } 349 } 350 gdkEventKey = event; 351 return false; 352 } 353 354 void fixIM () { 355 /* 356 * The IM filter has to be called one time for each key press event. 357 * When the IM is open the key events are duplicated. The first event 358 * is filtered by SWT and the second event is filtered by GTK. In some 359 * cases the GTK handler does not run (the widget is destroyed, the 360 * application code consumes the event, etc), for these cases the IM 361 * filter has to be called by SWT. 362 */ 363 if (gdkEventKey !is null && gdkEventKey !is cast(GdkEventKey*)-1) { 364 auto imContext = imContext (); 365 if (imContext !is null) { 366 OS.gtk_im_context_filter_keypress (imContext, gdkEventKey); 367 gdkEventKey = cast(GdkEventKey*)-1; 368 return; 369 } 370 } 371 gdkEventKey = null; 372 } 373 374 override GdkColor* getBackgroundColor () { 375 return getBaseColor (); 376 } 377 378 public override int getBorderWidth () { 379 checkWidget(); 380 auto style = OS.gtk_widget_get_style (handle); 381 if ((this.style & SWT.BORDER) !is 0) { 382 return OS.gtk_style_get_xthickness (style); 383 } 384 return 0; 385 } 386 387 override GdkColor* getForegroundColor () { 388 return getTextColor (); 389 } 390 391 /** 392 * Returns the amount that the receiver's value will be 393 * modified by when the up/down arrows are pressed. 394 * 395 * @return the increment 396 * 397 * @exception SWTException <ul> 398 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 399 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 400 * </ul> 401 */ 402 public int getIncrement () { 403 checkWidget (); 404 auto adjustment = OS.gtk_spin_button_get_adjustment (cast(GtkSpinButton*)handle); 405 int digits = OS.gtk_spin_button_get_digits (cast(GtkSpinButton*)handle); 406 auto value = adjustment.step_increment; 407 for (int i = 0; i < digits; i++) value *= 10; 408 return cast(int) (value > 0 ? value + 0.5 : value - 0.5); 409 } 410 411 /** 412 * Returns the maximum value which the receiver will allow. 413 * 414 * @return the maximum 415 * 416 * @exception SWTException <ul> 417 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 418 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 419 * </ul> 420 */ 421 public int getMaximum () { 422 checkWidget (); 423 auto adjustment = OS.gtk_spin_button_get_adjustment (cast(GtkSpinButton*)handle); 424 int digits = OS.gtk_spin_button_get_digits (cast(GtkSpinButton*)handle); 425 auto value = adjustment.upper; 426 for (int i = 0; i < digits; i++) value *= 10; 427 return cast(int) (value > 0 ? value + 0.5 : value - 0.5); 428 } 429 430 /** 431 * Returns the minimum value which the receiver will allow. 432 * 433 * @return the minimum 434 * 435 * @exception SWTException <ul> 436 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 437 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 438 * </ul> 439 */ 440 public int getMinimum () { 441 checkWidget (); 442 auto adjustment = OS.gtk_spin_button_get_adjustment (cast(GtkSpinButton*)handle); 443 int digits = OS.gtk_spin_button_get_digits (cast(GtkSpinButton*)handle); 444 auto value = adjustment.lower; 445 for (int i = 0; i < digits; i++) value *= 10; 446 return cast(int) (value > 0 ? value + 0.5 : value - 0.5); 447 } 448 449 /** 450 * Returns the amount that the receiver's position will be 451 * modified by when the page up/down keys are pressed. 452 * 453 * @return the page increment 454 * 455 * @exception SWTException <ul> 456 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 457 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 458 * </ul> 459 */ 460 public int getPageIncrement () { 461 checkWidget (); 462 auto adjustment = OS.gtk_spin_button_get_adjustment (cast(GtkSpinButton*)handle); 463 int digits = OS.gtk_spin_button_get_digits (cast(GtkSpinButton*)handle); 464 auto value = adjustment.page_increment; 465 for (int i = 0; i < digits; i++) value *= 10; 466 return cast(int) (value > 0 ? value + 0.5 : value - 0.5); 467 } 468 469 /** 470 * Returns the <em>selection</em>, which is the receiver's position. 471 * 472 * @return the selection 473 * 474 * @exception SWTException <ul> 475 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 476 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 477 * </ul> 478 */ 479 public int getSelection () { 480 checkWidget (); 481 auto adjustment = OS.gtk_spin_button_get_adjustment (cast(GtkSpinButton*)handle); 482 int digits = OS.gtk_spin_button_get_digits (cast(GtkSpinButton*)handle); 483 auto value = adjustment.value; 484 for (int i = 0; i < digits; i++) value *= 10; 485 return cast(int) (value > 0 ? value + 0.5 : value - 0.5); 486 } 487 488 /** 489 * Returns a string containing a copy of the contents of the 490 * receiver's text field, or an empty string if there are no 491 * contents. 492 * 493 * @return the receiver's text 494 * 495 * @exception SWTException <ul> 496 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 497 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 498 * </ul> 499 * 500 * @since 3.4 501 */ 502 public String getText () { 503 checkWidget (); 504 auto str = OS.gtk_entry_get_text (handle); 505 return fromStringz(str)._idup(); 506 } 507 508 /** 509 * Returns the maximum number of characters that the receiver's 510 * text field is capable of holding. If this has not been changed 511 * by <code>setTextLimit()</code>, it will be the constant 512 * <code>Spinner.LIMIT</code>. 513 * 514 * @return the text limit 515 * 516 * @exception SWTException <ul> 517 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 518 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 519 * </ul> 520 * 521 * @see #LIMIT 522 * 523 * @since 3.4 524 */ 525 public int getTextLimit () { 526 checkWidget (); 527 int limit = OS.gtk_entry_get_max_length (handle); 528 return limit is 0 ? 0xFFFF : limit; 529 } 530 531 /** 532 * Returns the number of decimal places used by the receiver. 533 * 534 * @return the digits 535 * 536 * @exception SWTException <ul> 537 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 538 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 539 * </ul> 540 */ 541 public int getDigits () { 542 checkWidget (); 543 return OS.gtk_spin_button_get_digits (cast(GtkSpinButton*)handle); 544 } 545 546 String getDecimalSeparator () { 547 auto ptr = OS.localeconv_decimal_point (); 548 return fromStringz( ptr )._idup(); 549 } 550 551 override int gtk_activate (GtkWidget* widget) { 552 postEvent (SWT.DefaultSelection); 553 return 0; 554 } 555 556 override int gtk_changed (GtkWidget* widget) { 557 auto str = OS.gtk_entry_get_text (cast(GtkEntry*)handle); 558 int length = OS.strlen (str); 559 if (length > 0) { 560 char* endptr; 561 double value = OS.g_strtod (str, &endptr); 562 if (endptr is str + length) { 563 auto adjustment = OS.gtk_spin_button_get_adjustment (cast(GtkSpinButton*)handle); 564 if (value !is adjustment.value && adjustment.lower <= value && value <= adjustment.upper) { 565 OS.gtk_spin_button_update (cast(GtkSpinButton*)handle); 566 } 567 } 568 } 569 570 /* 571 * Feature in GTK. When the user types, GTK positions 572 * the caret after sending the changed signal. This 573 * means that application code that attempts to position 574 * the caret during a changed signal will fail. The fix 575 * is to post the modify event when the user is typing. 576 */ 577 bool keyPress = false; 578 auto eventPtr = OS.gtk_get_current_event (); 579 if (eventPtr !is null) { 580 GdkEventKey* gdkEvent = cast(GdkEventKey*)eventPtr; 581 switch (gdkEvent.type) { 582 case OS.GDK_KEY_PRESS: 583 keyPress = true; 584 break; 585 default: 586 } 587 OS.gdk_event_free (eventPtr); 588 } 589 if (keyPress) { 590 postEvent (SWT.Modify); 591 } else { 592 sendEvent (SWT.Modify); 593 } 594 return 0; 595 } 596 597 override 598 int gtk_commit (GtkIMContext* imContext, char* text) { 599 if (text is null) return 0; 600 if (!OS.gtk_editable_get_editable (cast(GtkEditable*)handle)) return 0; 601 char [] chars = fromStringz( text ).dup; 602 if (chars.length is 0) return 0; 603 char [] newChars = sendIMKeyEvent (SWT.KeyDown, null, chars); 604 if (newChars is null) return 0; 605 /* 606 * Feature in GTK. For a GtkEntry, during the insert-text signal, 607 * GTK allows the programmer to change only the caret location, 608 * not the selection. If the programmer changes the selection, 609 * the new selection is lost. The fix is to detect a selection 610 * change and set it after the insert-text signal has completed. 611 */ 612 fixStart = fixEnd = -1; 613 OS.g_signal_handlers_block_matched (imContext, OS.G_SIGNAL_MATCH_DATA, 0, 0, null, null, udCOMMIT); 614 int id = OS.g_signal_lookup (OS.commit.ptr, OS.gtk_im_context_get_type ()); 615 int mask = OS.G_SIGNAL_MATCH_DATA | OS.G_SIGNAL_MATCH_ID; 616 OS.g_signal_handlers_unblock_matched (imContext, mask, id, 0, null, null, cast(void*)handle); 617 if (newChars is chars) { 618 OS.g_signal_emit_by_name1 (imContext, OS.commit.ptr, cast(int)text); 619 } else { 620 OS.g_signal_emit_by_name1 (imContext, OS.commit.ptr, cast(int)toStringz(newChars)); 621 } 622 OS.g_signal_handlers_unblock_matched (imContext, OS.G_SIGNAL_MATCH_DATA, 0, 0, null, null, udCOMMIT); 623 OS.g_signal_handlers_block_matched (imContext, mask, id, 0, null, null, cast(void*)handle); 624 if (fixStart !is -1 && fixEnd !is -1) { 625 OS.gtk_editable_set_position (cast(GtkEditable*)handle, fixStart); 626 OS.gtk_editable_select_region (cast(GtkEditable*)handle, fixStart, fixEnd); 627 } 628 fixStart = fixEnd = -1; 629 return 0; 630 } 631 632 override int gtk_delete_text (GtkWidget* widget, ptrdiff_t start_pos, ptrdiff_t end_pos) { 633 if (!hooks (SWT.Verify) && !filters (SWT.Verify)) return 0; 634 String newText = verifyText ("", cast(int)/*64bit*/start_pos, cast(int)/*64bit*/end_pos); 635 if (newText is null) { 636 OS.g_signal_stop_emission_by_name (handle, OS.delete_text.ptr); 637 } else { 638 if (newText.length > 0) { 639 int pos; 640 pos = cast(int)/*64bit*/end_pos; 641 OS.g_signal_handlers_block_matched (handle, OS.G_SIGNAL_MATCH_DATA, 0, 0, null, null, udCHANGED); 642 OS.g_signal_handlers_block_matched (handle, OS.G_SIGNAL_MATCH_DATA, 0, 0, null, null, udINSERT_TEXT); 643 OS.gtk_editable_insert_text (cast(GtkEditable*)handle, newText.ptr, cast(int)/*64bit*/newText.length, &pos); 644 OS.g_signal_handlers_unblock_matched (handle, OS.G_SIGNAL_MATCH_DATA, 0, 0, null, null, udINSERT_TEXT); 645 OS.g_signal_handlers_unblock_matched (handle, OS.G_SIGNAL_MATCH_DATA, 0, 0, null, null, udCHANGED); 646 OS.gtk_editable_set_position (cast(GtkEditable*)handle, pos); 647 } 648 } 649 return 0; 650 } 651 652 override int gtk_event_after (GtkWidget* widget, GdkEvent* gdkEvent) { 653 if (cursor !is null) gtk_setCursor (cursor.handle); 654 return super.gtk_event_after (widget, gdkEvent); 655 } 656 657 override int gtk_focus_out_event (GtkWidget* widget, GdkEventFocus* event) { 658 fixIM (); 659 return super.gtk_focus_out_event (widget, event); 660 } 661 662 override int gtk_insert_text (GtkEditable* widget, char* new_text, ptrdiff_t new_text_length, ptrdiff_t position) { 663 // if (!hooks (SWT.Verify) && !filters (SWT.Verify)) return 0; 664 if (new_text is null || new_text_length is 0) return 0; 665 String oldText = new_text[ 0 .. new_text_length ]._idup(); 666 int pos; 667 pos = *(cast(int*)position); 668 if (pos is -1) { 669 auto ptr = OS.gtk_entry_get_text (cast(GtkEntry*)handle); 670 pos = cast(int)/*64bit*/OS.g_utf8_strlen (ptr, -1); 671 } 672 String newText = verifyText (oldText, pos, pos); 673 if (newText !is oldText) { 674 int newStart, newEnd; 675 OS.gtk_editable_get_selection_bounds (cast(GtkEditable*)handle, &newStart, &newEnd); 676 if (newText !is null) { 677 if (newStart !is newEnd) { 678 OS.g_signal_handlers_block_matched (handle, OS.G_SIGNAL_MATCH_DATA, 0, 0, null, null, udDELETE_TEXT); 679 OS.g_signal_handlers_block_matched (handle, OS.G_SIGNAL_MATCH_DATA, 0, 0, null, null, udCHANGED); 680 OS.gtk_editable_delete_selection (cast(GtkEditable*)handle); 681 OS.g_signal_handlers_unblock_matched (handle, OS.G_SIGNAL_MATCH_DATA, 0, 0, null, null, udDELETE_TEXT); 682 OS.g_signal_handlers_unblock_matched (handle, OS.G_SIGNAL_MATCH_DATA, 0, 0, null, null, udCHANGED); 683 } 684 OS.g_signal_handlers_block_matched (handle, OS.G_SIGNAL_MATCH_DATA, 0, 0, null, null, udINSERT_TEXT); 685 OS.gtk_editable_insert_text (cast(GtkEditable*)handle, newText.ptr, cast(int)/*64bit*/newText.length, &pos); 686 OS.g_signal_handlers_unblock_matched (handle, OS.G_SIGNAL_MATCH_DATA, 0, 0, null, null, udINSERT_TEXT); 687 newStart = newEnd = pos; 688 } 689 pos = newEnd; 690 if (newStart !is newEnd ) { 691 fixStart = newStart ; 692 fixEnd = newEnd ; 693 } 694 *(cast(int*)position) = pos; 695 OS.g_signal_stop_emission_by_name (handle, OS.insert_text.ptr); 696 } 697 return 0; 698 } 699 700 override int gtk_key_press_event (GtkWidget* widget, GdkEventKey* event) { 701 auto result = super.gtk_key_press_event (widget, event); 702 if (result !is 0) fixIM (); 703 if (gdkEventKey is cast(GdkEventKey*)-1) result = 1; 704 gdkEventKey = null; 705 return result; 706 } 707 708 override int gtk_populate_popup (GtkWidget* widget, GtkWidget* menu) { 709 if ((style & SWT.RIGHT_TO_LEFT) !is 0) { 710 OS.gtk_widget_set_direction (menu, OS.GTK_TEXT_DIR_RTL); 711 display.doSetDirectionProc( menu, OS.GTK_TEXT_DIR_RTL); 712 } 713 return 0; 714 } 715 716 override int gtk_value_changed (int adjustment) { 717 postEvent (SWT.Selection); 718 return 0; 719 } 720 721 override void hookEvents () { 722 super.hookEvents(); 723 OS.g_signal_connect_closure (handle, OS.changed.ptr, display.closures [CHANGED], true); 724 OS.g_signal_connect_closure (handle, OS.insert_text.ptr, display.closures [INSERT_TEXT], false); 725 OS.g_signal_connect_closure (handle, OS.delete_text.ptr, display.closures [DELETE_TEXT], false); 726 OS.g_signal_connect_closure (handle, OS.value_changed.ptr, display.closures [VALUE_CHANGED], false); 727 OS.g_signal_connect_closure (handle, OS.activate.ptr, display.closures [ACTIVATE], false); 728 OS.g_signal_connect_closure (handle, OS.populate_popup.ptr, display.closures [POPULATE_POPUP], false); 729 auto imContext = imContext (); 730 if (imContext !is null) { 731 OS.g_signal_connect_closure (imContext, OS.commit.ptr, display.closures [COMMIT], false); 732 int id = OS.g_signal_lookup (OS.commit.ptr, OS.gtk_im_context_get_type ()); 733 int mask = OS.G_SIGNAL_MATCH_DATA | OS.G_SIGNAL_MATCH_ID; 734 OS.g_signal_handlers_block_matched (imContext, mask, id, 0, null, null, cast(void*)handle); 735 } 736 } 737 738 GtkIMContext* imContext () { 739 return OS.GTK_ENTRY_IM_CONTEXT (cast(GtkEntry*)handle); 740 } 741 742 override GdkDrawable* paintWindow () { 743 auto window = super.paintWindow (); 744 auto children = OS.gdk_window_get_children (window); 745 if (children !is null) window = cast(GdkDrawable*)OS.g_list_data (children); 746 OS.g_list_free (children); 747 return window; 748 } 749 750 /** 751 * Pastes text from clipboard. 752 * <p> 753 * The selected text is deleted from the widget 754 * and new text inserted from the clipboard. 755 * </p> 756 * 757 * @exception SWTException <ul> 758 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 759 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 760 * </ul> 761 */ 762 public void paste () { 763 checkWidget (); 764 OS.gtk_editable_paste_clipboard (cast(GtkEditable*)handle); 765 } 766 767 override void register () { 768 super.register (); 769 auto imContext = imContext (); 770 if (imContext !is null) display.addWidget (cast(GtkWidget*)imContext, this); 771 } 772 773 override void releaseWidget () { 774 super.releaseWidget (); 775 fixIM (); 776 } 777 778 /** 779 * Removes the listener from the collection of listeners who will 780 * be notified when the receiver's text is modified. 781 * 782 * @param listener the listener which should no longer be notified 783 * 784 * @exception IllegalArgumentException <ul> 785 * <li>ERROR_NULL_ARGUMENT - if the listener is null</li> 786 * </ul> 787 * @exception SWTException <ul> 788 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 789 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 790 * </ul> 791 * 792 * @see ModifyListener 793 * @see #addModifyListener 794 */ 795 public void removeModifyListener (ModifyListener listener) { 796 checkWidget (); 797 if (listener is null) error (SWT.ERROR_NULL_ARGUMENT); 798 if (eventTable is null) return; 799 eventTable.unhook (SWT.Modify, listener); 800 } 801 802 /** 803 * Removes the listener from the collection of listeners who will 804 * be notified when the control is selected by the user. 805 * 806 * @param listener the listener which should no longer be notified 807 * 808 * @exception IllegalArgumentException <ul> 809 * <li>ERROR_NULL_ARGUMENT - if the listener is null</li> 810 * </ul> 811 * @exception SWTException <ul> 812 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 813 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 814 * </ul> 815 * 816 * @see SelectionListener 817 * @see #addSelectionListener 818 */ 819 public void removeSelectionListener(SelectionListener listener) { 820 checkWidget (); 821 if (listener is null) error (SWT.ERROR_NULL_ARGUMENT); 822 if (eventTable is null) return; 823 eventTable.unhook(SWT.Selection, listener); 824 eventTable.unhook(SWT.DefaultSelection,listener); 825 } 826 827 /** 828 * Removes the listener from the collection of listeners who will 829 * be notified when the control is verified. 830 * 831 * @param listener the listener which should be notified 832 * 833 * @exception IllegalArgumentException <ul> 834 * <li>ERROR_NULL_ARGUMENT - if the listener is null</li> 835 * </ul> 836 * @exception SWTException <ul> 837 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 838 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 839 * </ul> 840 * 841 * @see VerifyListener 842 * @see #addVerifyListener 843 */ 844 void removeVerifyListener (VerifyListener listener) { 845 checkWidget (); 846 if (listener is null) error (SWT.ERROR_NULL_ARGUMENT); 847 if (eventTable is null) return; 848 eventTable.unhook (SWT.Verify, listener); 849 } 850 851 override void setBackgroundColor (GdkColor* color) { 852 super.setBackgroundColor (color); 853 OS.gtk_widget_modify_base (handle, 0, color); 854 } 855 856 override void gtk_setCursor (GdkCursor* cursor) { 857 GdkCursor* defaultCursor; 858 if (cursor is null) defaultCursor = OS.gdk_cursor_new (OS.GDK_XTERM); 859 super.gtk_setCursor (cursor !is null ? cursor : defaultCursor); 860 if (cursor is null) OS.gdk_cursor_destroy (defaultCursor); 861 } 862 863 override void setFontDescription (PangoFontDescription* font) { 864 super.setFontDescription (font); 865 } 866 867 /** 868 * Sets the amount that the receiver's value will be 869 * modified by when the up/down arrows are pressed to 870 * the argument, which must be at least one. 871 * 872 * @param value the new increment (must be greater than zero) 873 * 874 * @exception SWTException <ul> 875 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 876 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 877 * </ul> 878 */ 879 public void setIncrement (int value) { 880 checkWidget (); 881 if (value < 1) return; 882 auto adjustment = OS.gtk_spin_button_get_adjustment (cast(GtkSpinButton*)handle); 883 double newValue = value; 884 int digits = OS.gtk_spin_button_get_digits (cast(GtkSpinButton*)handle); 885 for (int i = 0; i < digits; i++) newValue /= 10; 886 OS.g_signal_handlers_block_matched (handle, OS.G_SIGNAL_MATCH_DATA, 0, 0, null, null, udVALUE_CHANGED); 887 OS.gtk_spin_button_set_increments (cast(GtkSpinButton*)handle, newValue, adjustment.page_increment); 888 OS.g_signal_handlers_unblock_matched (handle, OS.G_SIGNAL_MATCH_DATA, 0, 0, null, null, udVALUE_CHANGED); 889 } 890 891 /** 892 * Sets the maximum value that the receiver will allow. This new 893 * value will be ignored if it is not greater than the receiver's current 894 * minimum value. If the new maximum is applied then the receiver's 895 * selection value will be adjusted if necessary to fall within its new range. 896 * 897 * @param value the new maximum, which must be greater than the current minimum 898 * 899 * @exception SWTException <ul> 900 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 901 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 902 * </ul> 903 */ 904 public void setMaximum (int value) { 905 checkWidget (); 906 auto adjustment = OS.gtk_spin_button_get_adjustment (cast(GtkSpinButton*)handle); 907 double newValue = value; 908 int digits = OS.gtk_spin_button_get_digits (cast(GtkSpinButton*)handle); 909 for (int i = 0; i < digits; i++) newValue /= 10; 910 if (newValue <= adjustment.lower) return; 911 OS.g_signal_handlers_block_matched (handle, OS.G_SIGNAL_MATCH_DATA, 0, 0, null, null, udVALUE_CHANGED); 912 OS.gtk_spin_button_set_range (cast(GtkSpinButton*)handle, adjustment.lower, newValue); 913 OS.g_signal_handlers_unblock_matched (handle, OS.G_SIGNAL_MATCH_DATA, 0, 0, null, null, udVALUE_CHANGED); 914 } 915 916 /** 917 * Sets the minimum value that the receiver will allow. This new 918 * value will be ignored if it is not less than the receiver's 919 * current maximum value. If the new minimum is applied then the receiver's 920 * selection value will be adjusted if necessary to fall within its new range. 921 * 922 * @param value the new minimum, which must be less than the current maximum 923 * 924 * @exception SWTException <ul> 925 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 926 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 927 * </ul> 928 */ 929 public void setMinimum (int value) { 930 checkWidget (); 931 auto adjustment = OS.gtk_spin_button_get_adjustment (cast(GtkSpinButton*)handle); 932 double newValue = value; 933 int digits = OS.gtk_spin_button_get_digits (cast(GtkSpinButton*)handle); 934 for (int i = 0; i < digits; i++) newValue /= 10; 935 if (newValue >= adjustment.upper) return; 936 OS.g_signal_handlers_block_matched (handle, OS.G_SIGNAL_MATCH_DATA, 0, 0, null, null, udVALUE_CHANGED); 937 OS.gtk_spin_button_set_range (cast(GtkSpinButton*)handle, newValue, adjustment.upper); 938 OS.g_signal_handlers_unblock_matched (handle, OS.G_SIGNAL_MATCH_DATA, 0, 0, null, null, udVALUE_CHANGED); 939 } 940 941 /** 942 * Sets the amount that the receiver's position will be 943 * modified by when the page up/down keys are pressed 944 * to the argument, which must be at least one. 945 * 946 * @param value the page increment (must be greater than zero) 947 * 948 * @exception SWTException <ul> 949 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 950 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 951 * </ul> 952 */ 953 public void setPageIncrement (int value) { 954 checkWidget (); 955 if (value < 1) return; 956 auto adjustment = OS.gtk_spin_button_get_adjustment (cast(GtkSpinButton*)handle); 957 double newValue = value; 958 int digits = OS.gtk_spin_button_get_digits (cast(GtkSpinButton*)handle); 959 for (int i = 0; i < digits; i++) newValue /= 10; 960 OS.g_signal_handlers_block_matched (handle, OS.G_SIGNAL_MATCH_DATA, 0, 0, null, null, udVALUE_CHANGED); 961 OS.gtk_spin_button_set_increments (cast(GtkSpinButton*)handle, adjustment.step_increment, newValue); 962 OS.g_signal_handlers_unblock_matched (handle, OS.G_SIGNAL_MATCH_DATA, 0, 0, null, null, udVALUE_CHANGED); 963 } 964 965 /** 966 * Sets the <em>selection</em>, which is the receiver's 967 * position, to the argument. If the argument is not within 968 * the range specified by minimum and maximum, it will be 969 * adjusted to fall within this range. 970 * 971 * @param value the new selection (must be zero or greater) 972 * 973 * @exception SWTException <ul> 974 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 975 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 976 * </ul> 977 */ 978 public void setSelection (int value) { 979 checkWidget (); 980 double newValue = value; 981 int digits = OS.gtk_spin_button_get_digits (cast(GtkSpinButton*)handle); 982 for (int i = 0; i < digits; i++) newValue /= 10; 983 OS.g_signal_handlers_block_matched (handle, OS.G_SIGNAL_MATCH_DATA, 0, 0, null, null, udVALUE_CHANGED); 984 OS.gtk_spin_button_set_value (cast(GtkSpinButton*)handle, newValue); 985 OS.g_signal_handlers_unblock_matched (handle, OS.G_SIGNAL_MATCH_DATA, 0, 0, null, null, udVALUE_CHANGED); 986 } 987 988 /** 989 * Sets the maximum number of characters that the receiver's 990 * text field is capable of holding to be the argument. 991 * <p> 992 * To reset this value to the default, use <code>setTextLimit(Spinner.LIMIT)</code>. 993 * Specifying a limit value larger than <code>Spinner.LIMIT</code> sets the 994 * receiver's limit to <code>Spinner.LIMIT</code>. 995 * </p> 996 * @param limit new text limit 997 * 998 * @exception IllegalArgumentException <ul> 999 * <li>ERROR_CANNOT_BE_ZERO - if the limit is zero</li> 1000 * </ul> 1001 * @exception SWTException <ul> 1002 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 1003 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 1004 * </ul> 1005 * 1006 * @see #LIMIT 1007 * 1008 * @since 3.4 1009 */ 1010 public void setTextLimit (int limit) { 1011 checkWidget (); 1012 if (limit is 0) error (SWT.ERROR_CANNOT_BE_ZERO); 1013 OS.gtk_entry_set_max_length (handle, limit); 1014 } 1015 1016 /** 1017 * Sets the number of decimal places used by the receiver. 1018 * <p> 1019 * The digit setting is used to allow for floating point values in the receiver. 1020 * For example, to set the selection to a floating point value of 1.37 call setDigits() with 1021 * a value of 2 and setSelection() with a value of 137. Similarly, if getDigits() has a value 1022 * of 2 and getSelection() returns 137 this should be interpreted as 1.37. This applies to all 1023 * numeric APIs. 1024 * </p> 1025 * 1026 * @param value the new digits (must be greater than or equal to zero) 1027 * 1028 * @exception IllegalArgumentException <ul> 1029 * <li>ERROR_INVALID_ARGUMENT - if the value is less than zero</li> 1030 * </ul> 1031 * @exception SWTException <ul> 1032 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 1033 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 1034 * </ul> 1035 */ 1036 public void setDigits (int value) { 1037 checkWidget (); 1038 if (value < 0) error (SWT.ERROR_INVALID_ARGUMENT); 1039 int digits = OS.gtk_spin_button_get_digits (cast(GtkSpinButton*)handle); 1040 if (value is digits) return; 1041 auto adjustment = OS.gtk_spin_button_get_adjustment (cast(GtkSpinButton*)handle); 1042 int diff = Math.abs (value - digits); 1043 int factor = 1; 1044 for (int i = 0; i < diff; i++) factor *= 10; 1045 if (digits > value) { 1046 adjustment.value *= factor; 1047 adjustment.upper *= factor; 1048 adjustment.lower *= factor; 1049 adjustment.step_increment *= factor; 1050 adjustment.page_increment *= factor; 1051 } else { 1052 adjustment.value /= factor; 1053 adjustment.upper /= factor; 1054 adjustment.lower /= factor; 1055 adjustment.step_increment /= factor; 1056 adjustment.page_increment /= factor; 1057 } 1058 OS.gtk_spin_button_set_digits (cast(GtkSpinButton*)handle, value); 1059 } 1060 1061 /** 1062 * Sets the receiver's selection, minimum value, maximum 1063 * value, digits, increment and page increment all at once. 1064 * <p> 1065 * Note: This is similar to setting the values individually 1066 * using the appropriate methods, but may be implemented in a 1067 * more efficient fashion on some platforms. 1068 * </p> 1069 * 1070 * @param selection the new selection value 1071 * @param minimum the new minimum value 1072 * @param maximum the new maximum value 1073 * @param digits the new digits value 1074 * @param increment the new increment value 1075 * @param pageIncrement the new pageIncrement value 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 * @since 3.2 1083 */ 1084 public void setValues (int selection, int minimum, int maximum, int digits, int increment, int pageIncrement) { 1085 checkWidget (); 1086 if (maximum <= minimum) return; 1087 if (digits < 0) return; 1088 if (increment < 1) return; 1089 if (pageIncrement < 1) return; 1090 selection = Math.min (Math.max (minimum, selection), maximum); 1091 double factor = 1; 1092 for (int i = 0; i < digits; i++) factor *= 10; 1093 OS.g_signal_handlers_block_matched (handle, OS.G_SIGNAL_MATCH_DATA, 0, 0, null, null, udVALUE_CHANGED); 1094 OS.gtk_spin_button_set_range (cast(GtkSpinButton*)handle, minimum / factor, maximum / factor); 1095 OS.gtk_spin_button_set_increments (cast(GtkSpinButton*)handle, increment / factor, pageIncrement / factor); 1096 OS.gtk_spin_button_set_value (cast(GtkSpinButton*)handle, selection / factor); 1097 OS.gtk_spin_button_set_digits (cast(GtkSpinButton*)handle, digits); 1098 OS.g_signal_handlers_unblock_matched (handle, OS.G_SIGNAL_MATCH_DATA, 0, 0, null, null, udVALUE_CHANGED); 1099 } 1100 1101 override bool translateTraversal (GdkEventKey* keyEvent) { 1102 int key = keyEvent.keyval; 1103 GtkIMContext* imContext = null; 1104 switch (key) { 1105 case OS.GDK_KP_Enter: 1106 case OS.GDK_Return: { 1107 imContext = this.imContext (); 1108 if (imContext !is null) { 1109 char* preeditString; 1110 OS.gtk_im_context_get_preedit_string (imContext, &preeditString, null, null); 1111 if (preeditString !is null) { 1112 int length = OS.strlen (preeditString); 1113 OS.g_free (preeditString); 1114 if (length !is 0) return false; 1115 } 1116 } 1117 default: 1118 } 1119 } 1120 return super.translateTraversal (keyEvent); 1121 } 1122 1123 String verifyText (String string, int start, int end) { 1124 if (string.length is 0 && start is end) return null; 1125 Event event = new Event (); 1126 event.text = string; 1127 event.start = start; 1128 event.end = end; 1129 auto eventPtr = OS.gtk_get_current_event (); 1130 if (eventPtr !is null) { 1131 GdkEventKey* gdkEvent = cast(GdkEventKey*)eventPtr; 1132 switch (gdkEvent.type) { 1133 case OS.GDK_KEY_PRESS: 1134 setKeyState (event, gdkEvent); 1135 break; 1136 default: 1137 } 1138 OS.gdk_event_free (eventPtr); 1139 } 1140 int index = 0; 1141 if (OS.gtk_spin_button_get_digits (cast(GtkSpinButton*)handle) > 0) { 1142 String decimalSeparator = getDecimalSeparator (); 1143 index = string.indexOf( decimalSeparator ); 1144 if (index !is -1 ) { 1145 string = string.substring( 0, index ) ~ string.substring( index + 1 ); 1146 } 1147 index = 0; 1148 } 1149 if (string.length > 0) { 1150 auto adjustment = OS.gtk_spin_button_get_adjustment (handle); 1151 if (adjustment.lower < 0 && string.charAt (0) is '-') index++; 1152 } 1153 while (index < string.length) { 1154 if (!CharacterIsDigit (string.charAt(index))) break; 1155 index++; 1156 } 1157 event.doit = index is string.length; 1158 /* 1159 * It is possible (but unlikely), that application 1160 * code could have disposed the widget in the verify 1161 * event. If this happens, answer null to cancel 1162 * the operation. 1163 */ 1164 sendEvent (SWT.Verify, event); 1165 if (!event.doit || isDisposed ()) return null; 1166 return event.text; 1167 } 1168 1169 }