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