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.ToolItem; 14 15 import java.lang.all; 16 17 18 19 import org.eclipse.swt.SWT; 20 import org.eclipse.swt.SWTException; 21 import org.eclipse.swt.events.SelectionEvent; 22 import org.eclipse.swt.events.SelectionListener; 23 import org.eclipse.swt.graphics.Image; 24 import org.eclipse.swt.graphics.Rectangle; 25 import org.eclipse.swt.internal.gtk.OS; 26 import org.eclipse.swt.widgets.Item; 27 import org.eclipse.swt.widgets.ToolBar; 28 import org.eclipse.swt.widgets.Control; 29 import org.eclipse.swt.widgets.Shell; 30 import org.eclipse.swt.widgets.TypedListener; 31 import org.eclipse.swt.widgets.Event; 32 import org.eclipse.swt.widgets.ImageList; 33 34 /** 35 * Instances of this class represent a selectable user interface object 36 * that represents a button in a tool bar. 37 * <dl> 38 * <dt><b>Styles:</b></dt> 39 * <dd>PUSH, CHECK, RADIO, SEPARATOR, DROP_DOWN</dd> 40 * <dt><b>Events:</b></dt> 41 * <dd>Selection</dd> 42 * </dl> 43 * <p> 44 * Note: Only one of the styles CHECK, PUSH, RADIO, SEPARATOR and DROP_DOWN 45 * may be specified. 46 * </p><p> 47 * IMPORTANT: This class is <em>not</em> intended to be subclassed. 48 * </p> 49 * 50 * @see <a href="http://www.eclipse.org/swt/snippets/#toolbar">ToolBar, ToolItem snippets</a> 51 * @see <a href="http://www.eclipse.org/swt/">Sample code and further information</a> 52 */ 53 public class ToolItem : Item { 54 55 alias Item.setForegroundColor setForegroundColor; 56 57 GtkWidget* boxHandle, arrowHandle, arrowBoxHandle, separatorHandle, labelHandle, imageHandle; 58 ToolBar parent; 59 Control control; 60 Image hotImage, disabledImage; 61 String toolTipText; 62 bool drawHotImage; 63 64 /** 65 * Constructs a new instance of this class given its parent 66 * (which must be a <code>ToolBar</code>) and a style value 67 * describing its behavior and appearance. The item is added 68 * to the end of the items maintained by its parent. 69 * <p> 70 * The style value is either one of the style constants defined in 71 * class <code>SWT</code> which is applicable to instances of this 72 * class, or must be built by <em>bitwise OR</em>'ing together 73 * (that is, using the <code>int</code> "|" operator) two or more 74 * of those <code>SWT</code> style constants. The class description 75 * lists the style constants that are applicable to the class. 76 * Style bits are also inherited from superclasses. 77 * </p> 78 * 79 * @param parent a composite control which will be the parent of the new instance (cannot be null) 80 * @param style the style of control to construct 81 * 82 * @exception IllegalArgumentException <ul> 83 * <li>ERROR_NULL_ARGUMENT - if the parent is null</li> 84 * </ul> 85 * @exception SWTException <ul> 86 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li> 87 * <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li> 88 * </ul> 89 * 90 * @see SWT#PUSH 91 * @see SWT#CHECK 92 * @see SWT#RADIO 93 * @see SWT#SEPARATOR 94 * @see SWT#DROP_DOWN 95 * @see Widget#checkSubclass 96 * @see Widget#getStyle 97 */ 98 public this (ToolBar parent, int style) { 99 super (parent, checkStyle (style)); 100 this.parent = parent; 101 createWidget (parent.getItemCount ()); 102 } 103 104 /** 105 * Constructs a new instance of this class given its parent 106 * (which must be a <code>ToolBar</code>), a style value 107 * describing its behavior and appearance, and the index 108 * at which to place it in the items maintained by its parent. 109 * <p> 110 * The style value is either one of the style constants defined in 111 * class <code>SWT</code> which is applicable to instances of this 112 * class, or must be built by <em>bitwise OR</em>'ing together 113 * (that is, using the <code>int</code> "|" operator) two or more 114 * of those <code>SWT</code> style constants. The class description 115 * lists the style constants that are applicable to the class. 116 * Style bits are also inherited from superclasses. 117 * </p> 118 * 119 * @param parent a composite control which will be the parent of the new instance (cannot be null) 120 * @param style the style of control to construct 121 * @param index the zero-relative index to store the receiver in its parent 122 * 123 * @exception IllegalArgumentException <ul> 124 * <li>ERROR_NULL_ARGUMENT - if the parent is null</li> 125 * <li>ERROR_INVALID_RANGE - if the index is not between 0 and the number of elements in the parent (inclusive)</li> 126 * </ul> 127 * @exception SWTException <ul> 128 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li> 129 * <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li> 130 * </ul> 131 * 132 * @see SWT#PUSH 133 * @see SWT#CHECK 134 * @see SWT#RADIO 135 * @see SWT#SEPARATOR 136 * @see SWT#DROP_DOWN 137 * @see Widget#checkSubclass 138 * @see Widget#getStyle 139 */ 140 public this (ToolBar parent, int style, int index) { 141 super (parent, checkStyle (style)); 142 this.parent = parent; 143 int count = parent.getItemCount (); 144 if (!(0 <= index && index <= count)) { 145 error (SWT.ERROR_INVALID_RANGE); 146 } 147 createWidget (index); 148 } 149 150 /** 151 * Adds the listener to the collection of listeners who will 152 * be notified when the control is selected by the user, by sending 153 * it one of the messages defined in the <code>SelectionListener</code> 154 * interface. 155 * <p> 156 * When <code>widgetSelected</code> is called when the mouse is over the arrow portion of a drop-down tool, 157 * the event object detail field contains the value <code>SWT.ARROW</code>. 158 * <code>widgetDefaultSelected</code> is not called. 159 * </p> 160 * 161 * @param listener the listener which should be notified when the control is selected by the user, 162 * 163 * @exception IllegalArgumentException <ul> 164 * <li>ERROR_NULL_ARGUMENT - if the listener is null</li> 165 * </ul> 166 * @exception SWTException <ul> 167 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 168 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 169 * </ul> 170 * 171 * @see SelectionListener 172 * @see #removeSelectionListener 173 * @see SelectionEvent 174 */ 175 public void addSelectionListener(SelectionListener listener) { 176 checkWidget(); 177 if (listener is null) error (SWT.ERROR_NULL_ARGUMENT); 178 TypedListener typedListener = new TypedListener (listener); 179 addListener (SWT.Selection,typedListener); 180 addListener (SWT.DefaultSelection,typedListener); 181 } 182 183 static int checkStyle (int style) { 184 return checkBits (style, SWT.PUSH, SWT.CHECK, SWT.RADIO, SWT.SEPARATOR, SWT.DROP_DOWN, 0); 185 } 186 187 protected override void checkSubclass () { 188 if (!isValidSubclass ()) error (SWT.ERROR_INVALID_SUBCLASS); 189 } 190 191 override void createHandle (int index) { 192 state |= HANDLE; 193 if ((style & SWT.SEPARATOR) is 0) { 194 boxHandle = cast(GtkWidget*)((parent.style & SWT.RIGHT) !is 0 ? OS.gtk_hbox_new (false, 0) : OS.gtk_vbox_new (false, 0)); 195 if (boxHandle is null) error (SWT.ERROR_NO_HANDLES); 196 labelHandle = OS.gtk_label_new_with_mnemonic (null); 197 if (labelHandle is null) error (SWT.ERROR_NO_HANDLES); 198 imageHandle = OS.gtk_image_new (); 199 if (imageHandle is null) error (SWT.ERROR_NO_HANDLES); 200 OS.gtk_container_add (boxHandle, imageHandle); 201 OS.gtk_container_add (boxHandle, labelHandle); 202 if ((parent.style & SWT.VERTICAL) !is 0) { 203 // Align text and images to the left 204 OS.gtk_box_set_child_packing (boxHandle, imageHandle, false, false, 0, OS.GTK_PACK_START); 205 OS.gtk_box_set_child_packing (boxHandle, labelHandle, false, false, 2, OS.GTK_PACK_START); 206 } 207 } 208 int bits = SWT.SEPARATOR | SWT.RADIO | SWT.CHECK | SWT.PUSH | SWT.DROP_DOWN; 209 switch (style & bits) { 210 case SWT.SEPARATOR: 211 handle = cast(GtkWidget*)OS.gtk_hbox_new (false, 0); 212 if (handle is null) error (SWT.ERROR_NO_HANDLES); 213 bool isVertical = (parent.style & SWT.VERTICAL) !is 0; 214 separatorHandle = isVertical ? OS.gtk_hseparator_new() : OS.gtk_vseparator_new(); 215 if (separatorHandle is null) error (SWT.ERROR_NO_HANDLES); 216 OS.gtk_widget_set_size_request (separatorHandle, isVertical ? 15 : 6, isVertical ? 6 : 15); 217 OS.gtk_widget_set_size_request (handle, isVertical ? 15 : 6, isVertical ? 6 : 15); 218 OS.gtk_container_add (handle, separatorHandle); 219 break; 220 case SWT.DROP_DOWN: 221 handle = OS.gtk_button_new (); 222 if (handle is null) error (SWT.ERROR_NO_HANDLES); 223 arrowBoxHandle = cast(GtkWidget*)OS.gtk_hbox_new (false, 0); 224 if (arrowBoxHandle is null) error(SWT.ERROR_NO_HANDLES); 225 arrowHandle = OS.gtk_arrow_new (OS.GTK_ARROW_DOWN, OS.GTK_SHADOW_NONE); 226 if (arrowHandle is null) error (SWT.ERROR_NO_HANDLES); 227 OS.gtk_widget_set_size_request (arrowHandle, 8, 6); 228 OS.gtk_container_add (handle, arrowBoxHandle); 229 OS.gtk_container_add (arrowBoxHandle, boxHandle); 230 OS.gtk_container_add (arrowBoxHandle, arrowHandle); 231 break; 232 case SWT.RADIO: 233 /* 234 * This code is intentionally commented. Because GTK 235 * enforces radio behavior in a button group a radio group 236 * is not created for each set of contiguous buttons, each 237 * radio button will not draw unpressed. The fix is to use 238 * toggle buttons instead. 239 */ 240 // handle = OS.gtk_radio_button_new (0); 241 // if (handle is 0) error (SWT.ERROR_NO_HANDLES); 242 // OS.gtk_toggle_button_set_mode (handle, false); 243 // OS.gtk_container_add (handle, boxHandle); 244 // break; 245 case SWT.CHECK: 246 handle = OS.gtk_toggle_button_new (); 247 if (handle is null) error (SWT.ERROR_NO_HANDLES); 248 OS.gtk_toggle_button_set_mode (handle, false); 249 OS.gtk_container_add (handle, boxHandle); 250 break; 251 case SWT.PUSH: 252 default: 253 handle = OS.gtk_button_new (); 254 if (handle is null) error (SWT.ERROR_NO_HANDLES); 255 OS.gtk_container_add (handle, boxHandle); 256 break; 257 } 258 if ((style & SWT.SEPARATOR) is 0) { 259 int relief; 260 OS.gtk_widget_style_get1 (parent.handle, OS.button_relief.ptr, &relief); 261 OS.gtk_button_set_relief (handle, relief ); 262 } 263 OS.GTK_WIDGET_UNSET_FLAGS (handle, OS.GTK_CAN_FOCUS); 264 // This code is intentionally commented. 265 // int fontHandle = parent.fontHandle (); 266 // GdkColor color = new GdkColor (); 267 // int style = OS.gtk_widget_get_style (fontHandle); 268 // OS.gtk_style_get_fg (style, OS.GTK_STATE_NORMAL, color); 269 // int font = OS.gtk_style_get_font_desc (style); 270 // setForegroundColor (color); 271 // setFontDescription (font); 272 if ((parent.state & FOREGROUND) !is 0) { 273 setForegroundColor (parent.getForegroundColor()); 274 } 275 if ((parent.state & FONT) !is 0) { 276 setFontDescription (parent.getFontDescription()); 277 } 278 } 279 280 override void createWidget (int index) { 281 super.createWidget (index); 282 showWidget (index); 283 parent.relayout (); 284 } 285 286 override void deregister() { 287 super.deregister (); 288 if (labelHandle !is null) display.removeWidget (labelHandle); 289 } 290 291 public override void dispose () { 292 if (isDisposed ()) return; 293 ToolBar parent = this.parent; 294 super.dispose (); 295 parent.relayout (); 296 } 297 298 /** 299 * Returns a rectangle describing the receiver's size and location 300 * relative to its parent. 301 * 302 * @return the receiver's bounding rectangle 303 * 304 * @exception SWTException <ul> 305 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 306 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 307 * </ul> 308 */ 309 public Rectangle getBounds () { 310 checkWidget(); 311 parent.forceResize (); 312 auto topHandle = topHandle (); 313 int x, y, width, height; 314 /* 315 * Bug in GTK. Toolbar items are only allocated their minimum size 316 * in versions before 2.4.0. The fix is to use the total size 317 * available minus any borders. 318 */ 319 if (OS.GTK_VERSION < OS.buildVERSION (2, 4, 0) && control !is null && !control.isDisposed ()) { 320 int border = OS.gtk_container_get_border_width (parent.handle); 321 int shadow; 322 OS.gtk_widget_style_get1 (parent.handle, "shadow_type".ptr, &shadow); 323 if (shadow !is OS.GTK_SHADOW_NONE) { 324 border += OS.gtk_style_get_xthickness (OS.gtk_widget_get_style (parent.handle)); 325 } 326 if ((parent.style & SWT.VERTICAL) !is 0) { 327 x = border; 328 y = OS.GTK_WIDGET_Y (topHandle) + border; 329 width = OS.GTK_WIDGET_WIDTH (parent.handle) - border*2; 330 height = OS.GTK_WIDGET_HEIGHT (topHandle); 331 } else { 332 x = OS.GTK_WIDGET_X (topHandle) + border; 333 y = border; 334 width = OS.GTK_WIDGET_WIDTH (topHandle); 335 height = OS.GTK_WIDGET_HEIGHT (parent.handle) - border*2; 336 } 337 } else { 338 x = OS.GTK_WIDGET_X (topHandle); 339 y = OS.GTK_WIDGET_Y (topHandle); 340 width = OS.GTK_WIDGET_WIDTH (topHandle); 341 height = OS.GTK_WIDGET_HEIGHT (topHandle); 342 } 343 if ((parent.style & SWT.MIRRORED) !is 0) x = parent.getClientWidth () - width - x; 344 if ((style & SWT.SEPARATOR) !is 0 && control !is null) height = Math.max (height, 23); 345 return new Rectangle (x, y, width, height); 346 } 347 348 /** 349 * Returns the control that is used to fill the bounds of 350 * the item when the item is a <code>SEPARATOR</code>. 351 * 352 * @return the control 353 * 354 * @exception SWTException <ul> 355 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 356 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 357 * </ul> 358 */ 359 public Control getControl () { 360 checkWidget(); 361 return control; 362 } 363 364 /** 365 * Returns the receiver's disabled image if it has one, or null 366 * if it does not. 367 * <p> 368 * The disabled image is displayed when the receiver is disabled. 369 * </p> 370 * 371 * @return the receiver's disabled image 372 * 373 * @exception SWTException <ul> 374 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 375 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 376 * </ul> 377 */ 378 public Image getDisabledImage () { 379 checkWidget(); 380 return disabledImage; 381 } 382 383 /** 384 * Returns <code>true</code> if the receiver is enabled, and 385 * <code>false</code> otherwise. A disabled control is typically 386 * not selectable from the user interface and draws with an 387 * inactive or "grayed" look. 388 * 389 * @return the receiver's enabled state 390 * 391 * @exception SWTException <ul> 392 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 393 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 394 * </ul> 395 * 396 * @see #isEnabled 397 */ 398 public bool getEnabled () { 399 checkWidget(); 400 auto topHandle = topHandle (); 401 return OS.GTK_WIDGET_SENSITIVE (topHandle); 402 } 403 404 /** 405 * Returns the receiver's hot image if it has one, or null 406 * if it does not. 407 * <p> 408 * The hot image is displayed when the mouse enters the receiver. 409 * </p> 410 * 411 * @return the receiver's hot image 412 * 413 * @exception SWTException <ul> 414 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 415 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 416 * </ul> 417 */ 418 public Image getHotImage () { 419 checkWidget(); 420 return hotImage; 421 } 422 423 /** 424 * Returns the receiver's parent, which must be a <code>ToolBar</code>. 425 * 426 * @return the receiver's parent 427 * 428 * @exception SWTException <ul> 429 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 430 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 431 * </ul> 432 */ 433 public ToolBar getParent () { 434 checkWidget(); 435 if (parent is null) error (SWT.ERROR_WIDGET_DISPOSED); 436 return parent; 437 } 438 439 /** 440 * Returns <code>true</code> if the receiver is selected, 441 * and false otherwise. 442 * <p> 443 * When the receiver is of type <code>CHECK</code> or <code>RADIO</code>, 444 * it is selected when it is checked (which some platforms draw as a 445 * pushed in button). If the receiver is of any other type, this method 446 * returns false. 447 * </p> 448 * 449 * @return the selection state 450 * 451 * @exception SWTException <ul> 452 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 453 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 454 * </ul> 455 */ 456 public bool getSelection () { 457 checkWidget(); 458 if ((style & (SWT.CHECK | SWT.RADIO)) is 0) return false; 459 return cast(bool)OS.gtk_toggle_button_get_active (handle); 460 } 461 462 /** 463 * Returns the receiver's tool tip text, or null if it has not been set. 464 * 465 * @return the receiver's tool tip text 466 * 467 * @exception SWTException <ul> 468 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 469 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 470 * </ul> 471 */ 472 public String getToolTipText () { 473 checkWidget(); 474 return toolTipText; 475 } 476 477 /** 478 * Gets the width of the receiver. 479 * 480 * @return the width 481 * 482 * @exception SWTException <ul> 483 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 484 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 485 * </ul> 486 */ 487 public int getWidth () { 488 checkWidget(); 489 parent.forceResize (); 490 auto topHandle = topHandle (); 491 return OS.GTK_WIDGET_WIDTH (topHandle); 492 } 493 494 override int gtk_button_press_event (GtkWidget* widget, GdkEventButton* gdkEvent) { 495 double x = gdkEvent.x; 496 gdkEvent.x += OS.GTK_WIDGET_X (handle); 497 double y = gdkEvent.y; 498 gdkEvent.y += OS.GTK_WIDGET_Y (handle); 499 auto result = parent.gtk_button_press_event (widget, gdkEvent); 500 gdkEvent.x = x; 501 gdkEvent.y = y; 502 return result; 503 } 504 505 override int gtk_button_release_event (GtkWidget* widget, GdkEventButton* gdkEvent) { 506 double x = gdkEvent.x; 507 gdkEvent.x += OS.GTK_WIDGET_X (handle); 508 double y = gdkEvent.y; 509 gdkEvent.y += OS.GTK_WIDGET_Y (handle); 510 auto result = parent.gtk_button_release_event (widget, gdkEvent); 511 gdkEvent.x = x; 512 gdkEvent.y = y; 513 return result; 514 } 515 516 override int gtk_clicked (GtkWidget* widget) { 517 Event event = new Event (); 518 if ((style & SWT.DROP_DOWN) !is 0) { 519 auto eventPtr = OS.gtk_get_current_event (); 520 if (eventPtr !is null) { 521 GdkEvent* gdkEvent = cast(GdkEvent*)eventPtr; 522 switch (gdkEvent.type) { 523 case OS.GDK_BUTTON_PRESS: 524 case OS.GDK_2BUTTON_PRESS: 525 case OS.GDK_BUTTON_RELEASE: { 526 double x_win = 0; 527 double y_win = 0; 528 OS.gdk_event_get_coords (eventPtr, &x_win, &y_win); 529 int x = OS.GTK_WIDGET_X (arrowHandle) - OS.GTK_WIDGET_X (handle); 530 int width = OS.GTK_WIDGET_WIDTH (arrowHandle); 531 if ((((parent.style & SWT.RIGHT_TO_LEFT) is 0) && x <= cast(int)x_win) 532 || (((parent.style & SWT.RIGHT_TO_LEFT) !is 0) && cast(int)x_win <= x + width)) { 533 event.detail = SWT.ARROW; 534 auto topHandle = topHandle (); 535 event.x = OS.GTK_WIDGET_X (topHandle); 536 if ((parent.style & SWT.MIRRORED) !is 0) event.x = parent.getClientWidth () - OS.GTK_WIDGET_WIDTH (topHandle) - event.x; 537 event.y = OS.GTK_WIDGET_Y (topHandle) + OS.GTK_WIDGET_HEIGHT (topHandle); 538 } 539 break; 540 } 541 default: 542 } 543 OS.gdk_event_free (eventPtr); 544 } 545 } 546 if ((style & SWT.RADIO) !is 0) { 547 if ((parent.getStyle () & SWT.NO_RADIO_GROUP) is 0) { 548 selectRadio (); 549 } 550 } 551 postEvent (SWT.Selection, event); 552 return 0; 553 } 554 555 override int gtk_enter_notify_event (GtkWidget* widget, GdkEventCrossing* event) { 556 parent.gtk_enter_notify_event (widget, event); 557 drawHotImage = (parent.style & SWT.FLAT) !is 0 && hotImage !is null; 558 if (drawHotImage && imageHandle !is null) { 559 ImageList imageList = parent.imageList; 560 if (imageList !is null) { 561 int index = imageList.indexOf (hotImage); 562 if (index !is -1) { 563 auto pixbuf = imageList.getPixbuf (index); 564 OS.gtk_image_set_from_pixbuf (imageHandle, pixbuf); 565 } 566 } 567 } 568 return 0; 569 } 570 571 override int gtk_event_after (GtkWidget* widget, GdkEvent* gdkEvent) { 572 GdkEventButton* gdkEventButton = null; 573 switch (gdkEvent.type) { 574 case OS.GDK_BUTTON_PRESS: { 575 gdkEventButton = cast(GdkEventButton*)gdkEvent; 576 if (gdkEventButton.button is 3) { 577 parent.showMenu (cast(int) gdkEventButton.x_root, cast(int) gdkEventButton.y_root); 578 } 579 break; 580 default: 581 } 582 } 583 return 0; 584 } 585 586 override int gtk_focus_out_event (GtkWidget* widget, GdkEventFocus* event) { 587 OS.GTK_WIDGET_UNSET_FLAGS (handle, OS.GTK_CAN_FOCUS); 588 parent.lastFocus = this; 589 return 0; 590 } 591 592 override int gtk_leave_notify_event (GtkWidget* widget, GdkEventCrossing* event) { 593 parent.gtk_leave_notify_event (widget, event); 594 if (drawHotImage) { 595 drawHotImage = false; 596 if (imageHandle !is null && image !is null) { 597 ImageList imageList = parent.imageList; 598 if (imageList !is null) { 599 int index = imageList.indexOf (image); 600 if (index !is -1) { 601 auto pixbuf = imageList.getPixbuf (index); 602 OS.gtk_image_set_from_pixbuf (imageHandle, pixbuf); 603 } 604 } 605 } 606 } 607 return 0; 608 } 609 610 override int gtk_map (GtkWidget* widget) { 611 parent.fixZOrder (); 612 return 0; 613 } 614 615 override int gtk_mnemonic_activate (GtkWidget* widget, ptrdiff_t arg1) { 616 return parent.gtk_mnemonic_activate (widget, arg1); 617 } 618 619 bool hasFocus () { 620 return OS.GTK_WIDGET_HAS_FOCUS (handle); 621 } 622 623 override void hookEvents () { 624 super.hookEvents (); 625 if ((style & SWT.SEPARATOR) !is 0) return; 626 OS.g_signal_connect_closure (handle, OS.clicked.ptr, display.closures [CLICKED], false); 627 OS.g_signal_connect_closure_by_id (handle, display.signalIds [ENTER_NOTIFY_EVENT], 0, display.closures [ENTER_NOTIFY_EVENT], false); 628 OS.g_signal_connect_closure_by_id (handle, display.signalIds [LEAVE_NOTIFY_EVENT], 0, display.closures [LEAVE_NOTIFY_EVENT], false); 629 if (labelHandle !is null) OS.g_signal_connect_closure_by_id (labelHandle, display.signalIds [MNEMONIC_ACTIVATE], 0, display.closures [MNEMONIC_ACTIVATE], false); 630 631 OS.g_signal_connect_closure_by_id (handle, display.signalIds [FOCUS_OUT_EVENT], 0, display.closures [FOCUS_OUT_EVENT], false); 632 633 /* 634 * Feature in GTK. Usually, GTK widgets propagate all events to their 635 * parent when they are done their own processing. However, in contrast 636 * to other widgets, the buttons that make up the tool items, do not propagate 637 * the mouse up/down events. It is interesting to note that they DO propagate 638 * mouse motion events. The fix is to explicitly forward mouse up/down events 639 * to the parent. 640 */ 641 int mask = 642 OS.GDK_EXPOSURE_MASK | OS.GDK_POINTER_MOTION_MASK | 643 OS.GDK_BUTTON_PRESS_MASK | OS.GDK_BUTTON_RELEASE_MASK | 644 OS.GDK_ENTER_NOTIFY_MASK | OS.GDK_LEAVE_NOTIFY_MASK | 645 OS.GDK_KEY_PRESS_MASK | OS.GDK_KEY_RELEASE_MASK | 646 OS.GDK_FOCUS_CHANGE_MASK; 647 OS.gtk_widget_add_events (handle, mask); 648 OS.g_signal_connect_closure_by_id (handle, display.signalIds [BUTTON_PRESS_EVENT], 0, display.closures [BUTTON_PRESS_EVENT], false); 649 OS.g_signal_connect_closure_by_id (handle, display.signalIds [BUTTON_RELEASE_EVENT], 0, display.closures [BUTTON_RELEASE_EVENT], false); 650 OS.g_signal_connect_closure_by_id (handle, display.signalIds [EVENT_AFTER], 0, display.closures[EVENT_AFTER], false); 651 652 auto topHandle = topHandle (); 653 OS.g_signal_connect_closure_by_id (topHandle, display.signalIds [MAP], 0, display.closures [MAP], true); 654 } 655 656 /** 657 * Returns <code>true</code> if the receiver is enabled and all 658 * of the receiver's ancestors are enabled, and <code>false</code> 659 * otherwise. A disabled control is typically not selectable from the 660 * user interface and draws with an inactive or "grayed" look. 661 * 662 * @return the receiver's enabled state 663 * 664 * @exception SWTException <ul> 665 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 666 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 667 * </ul> 668 * 669 * @see #getEnabled 670 */ 671 public bool isEnabled () { 672 checkWidget(); 673 return getEnabled () && parent.isEnabled (); 674 } 675 676 override void register () { 677 super.register (); 678 if (labelHandle !is null) display.addWidget (labelHandle, this); 679 } 680 681 override void releaseHandle () { 682 super.releaseHandle (); 683 boxHandle = arrowHandle = separatorHandle = labelHandle = imageHandle = null; 684 } 685 686 override void releaseWidget () { 687 super.releaseWidget (); 688 if (parent.lastFocus is this) parent.lastFocus = null; 689 parent = null; 690 control = null; 691 hotImage = disabledImage = null; 692 toolTipText = null; 693 } 694 695 /** 696 * Removes the listener from the collection of listeners who will 697 * be notified when the control is selected by the user. 698 * 699 * @param listener the listener which should no longer be notified 700 * 701 * @exception IllegalArgumentException <ul> 702 * <li>ERROR_NULL_ARGUMENT - if the listener is null</li> 703 * </ul> 704 * @exception SWTException <ul> 705 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 706 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 707 * </ul> 708 * 709 * @see SelectionListener 710 * @see #addSelectionListener 711 */ 712 public void removeSelectionListener(SelectionListener listener) { 713 checkWidget(); 714 if (listener is null) error (SWT.ERROR_NULL_ARGUMENT); 715 if (eventTable is null) return; 716 eventTable.unhook (SWT.Selection, listener); 717 eventTable.unhook (SWT.DefaultSelection,listener); 718 } 719 720 void resizeControl () { 721 if (control !is null && !control.isDisposed ()) { 722 if (separatorHandle !is null) OS.gtk_widget_hide (separatorHandle); 723 /* 724 * Set the size and location of the control 725 * separately to minimize flashing in the 726 * case where the control does not resize 727 * to the size that was requested. This 728 * case can occur when the control is a 729 * combo box. 730 */ 731 Rectangle itemRect = getBounds (); 732 control.setSize (itemRect.width, itemRect.height); 733 OS.gtk_widget_set_size_request (handle, itemRect.width, itemRect.height); 734 Rectangle rect = control.getBounds (); 735 rect.x = itemRect.x + (itemRect.width - rect.width) / 2; 736 rect.y = itemRect.y + (itemRect.height - rect.height) / 2; 737 control.setLocation (rect.x, rect.y); 738 } else { 739 if (separatorHandle !is null) OS.gtk_widget_show (separatorHandle); 740 } 741 } 742 743 void selectRadio () { 744 int index = 0; 745 ToolItem [] items = parent.getItems (); 746 while (index < items.length && items [index] !is this) index++; 747 int i = index - 1; 748 while (i >= 0 && items [i].setRadioSelection (false)) --i; 749 int j = index + 1; 750 while (j < items.length && items [j].setRadioSelection (false)) j++; 751 setSelection (true); 752 } 753 754 /** 755 * Sets the control that is used to fill the bounds of 756 * the item when the item is a <code>SEPARATOR</code>. 757 * 758 * @param control the new control 759 * 760 * @exception IllegalArgumentException <ul> 761 * <li>ERROR_INVALID_ARGUMENT - if the control has been disposed</li> 762 * <li>ERROR_INVALID_PARENT - if the control is not in the same widget tree</li> 763 * </ul> 764 * @exception SWTException <ul> 765 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 766 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 767 * </ul> 768 */ 769 public void setControl (Control control) { 770 checkWidget (); 771 if (control !is null) { 772 if (control.isDisposed()) error (SWT.ERROR_INVALID_ARGUMENT); 773 if (control.parent !is parent) error (SWT.ERROR_INVALID_PARENT); 774 } 775 if ((style & SWT.SEPARATOR) is 0) return; 776 if (this.control is control) return; 777 this.control = control; 778 parent.relayout (); 779 } 780 781 /** 782 * Sets the receiver's disabled image to the argument, which may be 783 * null indicating that no disabled image should be displayed. 784 * <p> 785 * The disabled image is displayed when the receiver is disabled. 786 * </p> 787 * 788 * @param image the disabled image to display on the receiver (may be null) 789 * 790 * @exception IllegalArgumentException <ul> 791 * <li>ERROR_INVALID_ARGUMENT - if the image has been disposed</li> 792 * </ul> 793 * @exception SWTException <ul> 794 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 795 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 796 * </ul> 797 */ 798 public void setDisabledImage (Image image) { 799 checkWidget(); 800 if ((style & SWT.SEPARATOR) !is 0) return; 801 disabledImage = image; 802 } 803 804 /** 805 * Enables the receiver if the argument is <code>true</code>, 806 * and disables it otherwise. 807 * <p> 808 * A disabled control is typically 809 * not selectable from the user interface and draws with an 810 * inactive or "grayed" look. 811 * </p> 812 * 813 * @param enabled the new enabled state 814 * 815 * @exception SWTException <ul> 816 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 817 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 818 * </ul> 819 */ 820 public void setEnabled (bool enabled) { 821 checkWidget(); 822 auto topHandle = topHandle (); 823 OS.gtk_widget_set_sensitive (topHandle, enabled); 824 if (enabled) { 825 /* 826 * Bug in GTK. GtkButton requires an enter notify before it 827 * allows the button to be pressed, but events are dropped when 828 * widgets are insensitive. The fix is to hide and show the 829 * button if the pointer is within its bounds. 830 */ 831 int x, y; 832 OS.gdk_window_get_pointer (parent.paintWindow (), &x, &y, null); 833 if (getBounds ().contains (x, y)) { 834 OS.gtk_widget_hide (handle); 835 OS.gtk_widget_show (handle); 836 } 837 } 838 } 839 840 bool setFocus () { 841 if ((style & SWT.SEPARATOR) !is 0) return false; 842 if (!OS.gtk_widget_get_child_visible (handle)) return false; 843 OS.GTK_WIDGET_SET_FLAGS (handle, OS.GTK_CAN_FOCUS); 844 OS.gtk_widget_grab_focus (handle); 845 bool result = cast(bool)OS.gtk_widget_is_focus (handle); 846 if (!result) OS.GTK_WIDGET_UNSET_FLAGS (handle, OS.GTK_CAN_FOCUS); 847 return result; 848 } 849 850 void setFontDescription (PangoFontDescription* font) { 851 OS.gtk_widget_modify_font (handle, font); 852 if (labelHandle !is null) OS.gtk_widget_modify_font (labelHandle, font); 853 if (imageHandle !is null) OS.gtk_widget_modify_font (imageHandle, font); 854 } 855 856 alias Item.setForegroundColor setForegroundColor; 857 void setForegroundColor (GdkColor* color) { 858 setForegroundColor (handle, color); 859 if (labelHandle !is null) setForegroundColor (labelHandle, color); 860 if (imageHandle !is null) setForegroundColor (imageHandle, color); 861 } 862 863 /** 864 * Sets the receiver's hot image to the argument, which may be 865 * null indicating that no hot image should be displayed. 866 * <p> 867 * The hot image is displayed when the mouse enters the receiver. 868 * </p> 869 * 870 * @param image the hot image to display on the receiver (may be null) 871 * 872 * @exception IllegalArgumentException <ul> 873 * <li>ERROR_INVALID_ARGUMENT - if the image has been disposed</li> 874 * </ul> 875 * @exception SWTException <ul> 876 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 877 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 878 * </ul> 879 */ 880 public void setHotImage (Image image) { 881 checkWidget(); 882 if ((style & SWT.SEPARATOR) !is 0) return; 883 hotImage = image; 884 if (image !is null) { 885 ImageList imageList = parent.imageList; 886 if (imageList is null) imageList = parent.imageList = new ImageList (); 887 int imageIndex = imageList.indexOf (image); 888 if (imageIndex is -1) { 889 imageIndex = imageList.add (image); 890 } else { 891 imageList.put (imageIndex, image); 892 } 893 } 894 } 895 896 public override void setImage (Image image) { 897 checkWidget(); 898 if ((style & SWT.SEPARATOR) !is 0) return; 899 super.setImage (image); 900 if (imageHandle is null) return; 901 if (image !is null) { 902 ImageList imageList = parent.imageList; 903 if (imageList is null) imageList = parent.imageList = new ImageList (); 904 int imageIndex = imageList.indexOf (image); 905 if (imageIndex is -1) { 906 imageIndex = imageList.add (image); 907 } else { 908 imageList.put (imageIndex, image); 909 } 910 auto pixbuf = imageList.getPixbuf (imageIndex); 911 OS.gtk_image_set_from_pixbuf (imageHandle, pixbuf); 912 OS.gtk_widget_show (imageHandle); 913 } else { 914 OS.gtk_image_set_from_pixbuf (imageHandle, null); 915 OS.gtk_widget_hide (imageHandle); 916 } 917 parent.relayout (); 918 } 919 920 override 921 void setOrientation () { 922 if ((parent.style & SWT.RIGHT_TO_LEFT) !is 0) { 923 if (handle !is null) OS.gtk_widget_set_direction (handle, OS.GTK_TEXT_DIR_RTL); 924 if (labelHandle !is null) OS.gtk_widget_set_direction (labelHandle, OS.GTK_TEXT_DIR_RTL); 925 if (imageHandle !is null) OS.gtk_widget_set_direction (imageHandle, OS.GTK_TEXT_DIR_RTL); 926 if (separatorHandle !is null) OS.gtk_widget_set_direction (separatorHandle, OS.GTK_TEXT_DIR_RTL); 927 if (arrowHandle !is null) OS.gtk_widget_set_direction (arrowHandle, OS.GTK_TEXT_DIR_RTL); 928 if (boxHandle !is null) OS.gtk_widget_set_direction (boxHandle, OS.GTK_TEXT_DIR_RTL); 929 if (arrowBoxHandle !is null) OS.gtk_widget_set_direction (arrowBoxHandle, OS.GTK_TEXT_DIR_RTL); 930 } 931 } 932 933 bool setRadioSelection (bool value) { 934 if ((style & SWT.RADIO) is 0) return false; 935 if (getSelection () !is value) { 936 setSelection (value); 937 postEvent (SWT.Selection); 938 } 939 return true; 940 } 941 942 /** 943 * Sets the selection state of the receiver. 944 * <p> 945 * When the receiver is of type <code>CHECK</code> or <code>RADIO</code>, 946 * it is selected when it is checked (which some platforms draw as a 947 * pushed in button). 948 * </p> 949 * 950 * @param selected the new selection state 951 * 952 * @exception SWTException <ul> 953 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 954 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 955 * </ul> 956 */ 957 public void setSelection (bool selected) { 958 checkWidget (); 959 if ((style & (SWT.CHECK | SWT.RADIO)) is 0) return; 960 OS.g_signal_handlers_block_matched (handle, OS.G_SIGNAL_MATCH_DATA, 0, 0, null, null, udCLICKED); 961 OS.gtk_toggle_button_set_active (handle, selected); 962 OS.g_signal_handlers_unblock_matched (handle, OS.G_SIGNAL_MATCH_DATA, 0, 0, null, null, udCLICKED); 963 } 964 965 /** 966 * Sets the receiver's text. The string may include 967 * the mnemonic character. 968 * </p> 969 * <p> 970 * Mnemonics are indicated by an '&' that causes the next 971 * character to be the mnemonic. When the user presses a 972 * key sequence that matches the mnemonic, a selection 973 * event occurs. On most platforms, the mnemonic appears 974 * underlined but may be emphasised in a platform specific 975 * manner. The mnemonic indicator character '&' can be 976 * escaped by doubling it in the string, causing a single 977 * '&' to be displayed. 978 * </p> 979 * 980 * @param string the new text 981 * 982 * @exception SWTException <ul> 983 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 984 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 985 * </ul> 986 */ 987 public override void setText (String string) { 988 checkWidget(); 989 // SWT extension: allow null for zero length string 990 //if (string is null) error (SWT.ERROR_NULL_ARGUMENT); 991 if ((style & SWT.SEPARATOR) !is 0) return; 992 super.setText (string); 993 if (labelHandle is null) return; 994 char [] chars = fixMnemonic (string); 995 OS.gtk_label_set_text_with_mnemonic (labelHandle, chars.toStringzValidPtr()); 996 if (string.length !is 0) { 997 OS.gtk_widget_show (labelHandle); 998 } else { 999 OS.gtk_widget_hide (labelHandle); 1000 } 1001 parent.relayout (); 1002 } 1003 1004 /** 1005 * Sets the receiver's tool tip text to the argument, which 1006 * may be null indicating that no tool tip text should be shown. 1007 * 1008 * @param string the new tool tip text (or null) 1009 * 1010 * @exception SWTException <ul> 1011 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 1012 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 1013 * </ul> 1014 */ 1015 public void setToolTipText (String string) { 1016 checkWidget(); 1017 if (parent.toolTipText is null) { 1018 Shell shell = parent._getShell (); 1019 setToolTipText (shell, string); 1020 } 1021 toolTipText = string; 1022 } 1023 1024 void setToolTipText (Shell shell, String newString) { 1025 shell.setToolTipText (handle, newString); 1026 } 1027 1028 /** 1029 * Sets the width of the receiver, for <code>SEPARATOR</code> ToolItems. 1030 * 1031 * @param width the new width 1032 * 1033 * @exception SWTException <ul> 1034 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 1035 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 1036 * </ul> 1037 */ 1038 public void setWidth (int width) { 1039 checkWidget(); 1040 if ((style & SWT.SEPARATOR) is 0) return; 1041 if (width < 0) return; 1042 bool isVertical = (parent.style & SWT.VERTICAL) !is 0; 1043 OS.gtk_widget_set_size_request (separatorHandle, width, isVertical ? 6 : 15); 1044 OS.gtk_widget_set_size_request (handle, width, isVertical ? 6 : 15); 1045 parent.relayout (); 1046 } 1047 1048 void showWidget (int index) { 1049 if (handle !is null) OS.gtk_widget_show (handle); 1050 if (boxHandle !is null) OS.gtk_widget_show (boxHandle); 1051 if (separatorHandle !is null) OS.gtk_widget_show (separatorHandle); 1052 if (arrowBoxHandle !is null) OS.gtk_widget_show (arrowBoxHandle); 1053 if (arrowHandle !is null) OS.gtk_widget_show (arrowHandle); 1054 OS.gtk_toolbar_insert_widget (parent.handle, handle, null, null, index); 1055 } 1056 }