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.MenuItem; 14 15 16 import org.eclipse.swt.widgets.Menu; 17 import org.eclipse.swt.widgets.Item; 18 import org.eclipse.swt.widgets.Decorations; 19 import org.eclipse.swt.graphics.Rectangle; 20 import org.eclipse.swt.graphics.Image; 21 import org.eclipse.swt.widgets.ImageList; 22 import org.eclipse.swt.SWT; 23 import org.eclipse.swt.internal.gtk.OS; 24 import org.eclipse.swt.events.ArmListener; 25 import org.eclipse.swt.events.HelpListener; 26 import org.eclipse.swt.events.SelectionListener; 27 import org.eclipse.swt.events.SelectionEvent; 28 import org.eclipse.swt.widgets.TypedListener; 29 import org.eclipse.swt.widgets.Event; 30 import org.eclipse.swt.widgets.Display; 31 32 import java.lang.all; 33 34 /** 35 * Instances of this class represent a selectable user interface object 36 * that issues notification when pressed and released. 37 * <dl> 38 * <dt><b>Styles:</b></dt> 39 * <dd>CHECK, CASCADE, PUSH, RADIO, SEPARATOR</dd> 40 * <dt><b>Events:</b></dt> 41 * <dd>Arm, Help, Selection</dd> 42 * </dl> 43 * <p> 44 * Note: Only one of the styles CHECK, CASCADE, PUSH, RADIO and SEPARATOR 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/">Sample code and further information</a> 51 */ 52 public class MenuItem : Item { 53 Menu parent, menu; 54 GtkAccelGroup* groupHandle; 55 int accelerator; 56 57 /** 58 * Constructs a new instance of this class given its parent 59 * (which must be a <code>Menu</code>) and a style value 60 * describing its behavior and appearance. The item is added 61 * to the end of the items maintained by its parent. 62 * <p> 63 * The style value is either one of the style constants defined in 64 * class <code>SWT</code> which is applicable to instances of this 65 * class, or must be built by <em>bitwise OR</em>'ing together 66 * (that is, using the <code>int</code> "|" operator) two or more 67 * of those <code>SWT</code> style constants. The class description 68 * lists the style constants that are applicable to the class. 69 * Style bits are also inherited from superclasses. 70 * </p> 71 * 72 * @param parent a menu control which will be the parent of the new instance (cannot be null) 73 * @param style the style of control to construct 74 * 75 * @exception IllegalArgumentException <ul> 76 * <li>ERROR_NULL_ARGUMENT - if the parent is null</li> 77 * </ul> 78 * @exception SWTException <ul> 79 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li> 80 * <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li> 81 * </ul> 82 * 83 * @see SWT#CHECK 84 * @see SWT#CASCADE 85 * @see SWT#PUSH 86 * @see SWT#RADIO 87 * @see SWT#SEPARATOR 88 * @see Widget#checkSubclass 89 * @see Widget#getStyle 90 */ 91 public this (Menu parent, int style) { 92 super (parent, checkStyle (style)); 93 this.parent = parent; 94 createWidget (parent.getItemCount ()); 95 } 96 97 /** 98 * Constructs a new instance of this class given its parent 99 * (which must be a <code>Menu</code>), a style value 100 * describing its behavior and appearance, and the index 101 * at which to place it in the items maintained by its parent. 102 * <p> 103 * The style value is either one of the style constants defined in 104 * class <code>SWT</code> which is applicable to instances of this 105 * class, or must be built by <em>bitwise OR</em>'ing together 106 * (that is, using the <code>int</code> "|" operator) two or more 107 * of those <code>SWT</code> style constants. The class description 108 * lists the style constants that are applicable to the class. 109 * Style bits are also inherited from superclasses. 110 * </p> 111 * 112 * @param parent a menu control which will be the parent of the new instance (cannot be null) 113 * @param style the style of control to construct 114 * @param index the zero-relative index to store the receiver in its parent 115 * 116 * @exception IllegalArgumentException <ul> 117 * <li>ERROR_NULL_ARGUMENT - if the parent is null</li> 118 * <li>ERROR_INVALID_RANGE - if the index is not between 0 and the number of elements in the parent (inclusive)</li> 119 * </ul> 120 * @exception SWTException <ul> 121 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li> 122 * <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li> 123 * </ul> 124 * 125 * @see SWT#CHECK 126 * @see SWT#CASCADE 127 * @see SWT#PUSH 128 * @see SWT#RADIO 129 * @see SWT#SEPARATOR 130 * @see Widget#checkSubclass 131 * @see Widget#getStyle 132 */ 133 public this (Menu parent, int style, int index) { 134 super (parent, checkStyle (style)); 135 this.parent = parent; 136 int count = parent.getItemCount (); 137 if (!(0 <= index && index <= count)) { 138 error (SWT.ERROR_INVALID_RANGE); 139 } 140 createWidget (index); 141 } 142 143 void addAccelerator (GtkAccelGroup* accelGroup) { 144 updateAccelerator (accelGroup, true); 145 } 146 147 void addAccelerators (GtkAccelGroup* accelGroup) { 148 addAccelerator (accelGroup); 149 if (menu !is null) menu.addAccelerators (accelGroup); 150 } 151 152 /** 153 * Adds the listener to the collection of listeners who will 154 * be notified when the arm events are generated for the control, by sending 155 * it one of the messages defined in the <code>ArmListener</code> 156 * interface. 157 * 158 * @param listener the listener which should be notified 159 * 160 * @exception IllegalArgumentException <ul> 161 * <li>ERROR_NULL_ARGUMENT - if the listener is null</li> 162 * </ul> 163 * @exception SWTException <ul> 164 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 165 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 166 * </ul> 167 * 168 * @see ArmListener 169 * @see #removeArmListener 170 */ 171 public void addArmListener (ArmListener listener) { 172 checkWidget(); 173 if (listener is null) error (SWT.ERROR_NULL_ARGUMENT); 174 TypedListener typedListener = new TypedListener (listener); 175 addListener (SWT.Arm, typedListener); 176 } 177 178 /** 179 * Adds the listener to the collection of listeners who will 180 * be notified when the help events are generated for the control, by sending 181 * it one of the messages defined in the <code>HelpListener</code> 182 * interface. 183 * 184 * @param listener the listener which should be notified 185 * 186 * @exception IllegalArgumentException <ul> 187 * <li>ERROR_NULL_ARGUMENT - if the listener is null</li> 188 * </ul> 189 * @exception SWTException <ul> 190 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 191 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 192 * </ul> 193 * 194 * @see HelpListener 195 * @see #removeHelpListener 196 */ 197 public void addHelpListener (HelpListener listener) { 198 checkWidget(); 199 if (listener is null) error (SWT.ERROR_NULL_ARGUMENT); 200 TypedListener typedListener = new TypedListener (listener); 201 addListener (SWT.Help, typedListener); 202 } 203 204 /** 205 * Adds the listener to the collection of listeners who will 206 * be notified when the menu item is selected by the user, by sending 207 * it one of the messages defined in the <code>SelectionListener</code> 208 * interface. 209 * <p> 210 * When <code>widgetSelected</code> is called, the stateMask field of the event object is valid. 211 * <code>widgetDefaultSelected</code> is not called. 212 * </p> 213 * 214 * @param listener the listener which should be notified when the menu item is selected by the user 215 * 216 * @exception IllegalArgumentException <ul> 217 * <li>ERROR_NULL_ARGUMENT - if the listener is null</li> 218 * </ul> 219 * @exception SWTException <ul> 220 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 221 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 222 * </ul> 223 * 224 * @see SelectionListener 225 * @see #removeSelectionListener 226 * @see SelectionEvent 227 */ 228 public void addSelectionListener (SelectionListener listener) { 229 checkWidget(); 230 if (listener is null) error (SWT.ERROR_NULL_ARGUMENT); 231 TypedListener typedListener = new TypedListener(listener); 232 addListener (SWT.Selection,typedListener); 233 addListener (SWT.DefaultSelection,typedListener); 234 } 235 236 static int checkStyle (int style) { 237 return checkBits (style, SWT.PUSH, SWT.CHECK, SWT.RADIO, SWT.SEPARATOR, SWT.CASCADE, 0); 238 } 239 240 protected override void checkSubclass () { 241 if (!isValidSubclass ()) error (SWT.ERROR_INVALID_SUBCLASS); 242 } 243 244 override void createHandle (int index) { 245 state |= HANDLE; 246 String buffer = "\0"; 247 int bits = SWT.CHECK | SWT.RADIO | SWT.PUSH | SWT.SEPARATOR; 248 switch (style & bits) { 249 case SWT.SEPARATOR: 250 handle = OS.gtk_separator_menu_item_new (); 251 break; 252 case SWT.RADIO: 253 /* 254 * Feature in GTK. In GTK, radio button must always be part of 255 * a radio button group. In a GTK radio group, one button is always 256 * selected. This means that it is not possible to have a single 257 * radio button that is unselected. This is necessary to allow 258 * applications to implement their own radio behavior or use radio 259 * buttons outside of radio groups. The fix is to create a hidden 260 * radio button for each radio button we create and add them 261 * to the same group. This allows the visible button to be 262 * unselected. 263 */ 264 groupHandle = cast(GtkAccelGroup*) OS.gtk_radio_menu_item_new (null); 265 if (groupHandle is null) error (SWT.ERROR_NO_HANDLES); 266 OS.g_object_ref (groupHandle); 267 OS.gtk_object_sink (cast(GtkObject*)groupHandle); 268 auto group = OS.gtk_radio_menu_item_get_group (cast(GtkRadioMenuItem*) groupHandle); 269 handle = OS.gtk_radio_menu_item_new_with_label (group, buffer.ptr); 270 break; 271 case SWT.CHECK: 272 handle = OS.gtk_check_menu_item_new_with_label (buffer.ptr); 273 break; 274 case SWT.PUSH: 275 default: 276 handle = OS.gtk_image_menu_item_new_with_label (buffer.ptr); 277 break; 278 } 279 if (handle is null) error (SWT.ERROR_NO_HANDLES); 280 if ((style & SWT.SEPARATOR) is 0) { 281 auto label = OS.gtk_bin_get_child (cast(GtkBin*)handle); 282 OS.gtk_accel_label_set_accel_widget (cast(GtkAccelLabel*)label, null); 283 } 284 auto parentHandle = parent.handle; 285 bool enabled = OS.GTK_WIDGET_SENSITIVE (parentHandle); 286 if (!enabled) OS.GTK_WIDGET_SET_FLAGS (parentHandle, OS.GTK_SENSITIVE); 287 OS.gtk_menu_shell_insert (cast(GtkMenuShell*)parentHandle, handle, index); 288 if (!enabled) OS.GTK_WIDGET_UNSET_FLAGS (parentHandle, OS.GTK_SENSITIVE); 289 OS.gtk_widget_show (handle); 290 } 291 292 void fixMenus (Decorations newParent) { 293 if (menu !is null) menu.fixMenus (newParent); 294 } 295 296 /** 297 * Returns the widget accelerator. An accelerator is the bit-wise 298 * OR of zero or more modifier masks and a key. Examples: 299 * <code>SWT.CONTROL | SWT.SHIFT | 'T', SWT.ALT | SWT.F2</code>. 300 * The default value is zero, indicating that the menu item does 301 * not have an accelerator. 302 * 303 * @return the accelerator or 0 304 * 305 * </ul> 306 * @exception SWTException <ul> 307 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 308 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 309 * </ul> 310 */ 311 public int getAccelerator () { 312 checkWidget(); 313 return accelerator; 314 } 315 316 GtkAccelGroup* getAccelGroup () { 317 Menu menu = parent; 318 while (menu !is null && menu.cascade !is null) { 319 menu = menu.cascade.parent; 320 } 321 if (menu is null) return null; 322 Decorations shell = menu.parent; 323 return shell.menuBar is menu ? shell.accelGroup : null; 324 } 325 326 /*public*/ Rectangle getBounds () { 327 checkWidget(); 328 if (!OS.GTK_WIDGET_MAPPED (handle)) { 329 return new Rectangle (0, 0, 0, 0); 330 } 331 int x = OS.GTK_WIDGET_X (handle); 332 int y = OS.GTK_WIDGET_Y (handle); 333 int width = OS.GTK_WIDGET_WIDTH (handle); 334 int height = OS.GTK_WIDGET_HEIGHT (handle); 335 return new Rectangle (x, y, width, height); 336 } 337 338 /** 339 * Returns <code>true</code> if the receiver is enabled, and 340 * <code>false</code> otherwise. A disabled menu item is typically 341 * not selectable from the user interface and draws with an 342 * inactive or "grayed" look. 343 * 344 * @return the receiver's enabled state 345 * 346 * @exception SWTException <ul> 347 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 348 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 349 * </ul> 350 * 351 * @see #isEnabled 352 */ 353 public bool getEnabled () { 354 checkWidget(); 355 return OS.GTK_WIDGET_SENSITIVE (handle); 356 } 357 358 /** 359 * Returns the receiver's cascade menu if it has one or null 360 * if it does not. Only <code>CASCADE</code> menu items can have 361 * a pull down menu. The sequence of key strokes, button presses 362 * and/or button releases that are used to request a pull down 363 * menu is platform specific. 364 * 365 * @return the receiver's menu 366 * 367 * @exception SWTException <ul> 368 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 369 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 370 * </ul> 371 */ 372 public Menu getMenu () { 373 checkWidget(); 374 return menu; 375 } 376 377 override 378 String getNameText () { 379 if ((style & SWT.SEPARATOR) !is 0) return "|"; 380 return super.getNameText (); 381 } 382 383 /** 384 * Returns the receiver's parent, which must be a <code>Menu</code>. 385 * 386 * @return the receiver's parent 387 * 388 * @exception SWTException <ul> 389 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 390 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 391 * </ul> 392 */ 393 public Menu getParent () { 394 checkWidget(); 395 return parent; 396 } 397 398 /** 399 * Returns <code>true</code> if the receiver is selected, 400 * and false otherwise. 401 * <p> 402 * When the receiver is of type <code>CHECK</code> or <code>RADIO</code>, 403 * it is selected when it is checked. 404 * 405 * @return the selection state 406 * 407 * @exception SWTException <ul> 408 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 409 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 410 * </ul> 411 */ 412 public bool getSelection () { 413 checkWidget(); 414 if ((style & (SWT.CHECK | SWT.RADIO)) is 0) return false; 415 return cast(bool)OS.gtk_check_menu_item_get_active(cast(GtkCheckMenuItem*)handle); 416 } 417 418 override int gtk_activate (GtkWidget* widget) { 419 if ((style & SWT.CASCADE) !is 0 && menu !is null) return 0; 420 /* 421 * Bug in GTK. When an ancestor menu is disabled and 422 * the user types an accelerator key, GTK delivers the 423 * the activate signal even though the menu item cannot 424 * be invoked using the mouse. The fix is to ignore 425 * activate signals when an ancestor menu is disabled. 426 */ 427 if (!isEnabled ()) return 0; 428 Event event = new Event (); 429 auto ptr = OS.gtk_get_current_event (); 430 if (ptr !is null) { 431 GdkEvent* gdkEvent = ptr; 432 switch (gdkEvent.type) { 433 case OS.GDK_KEY_PRESS: 434 case OS.GDK_KEY_RELEASE: 435 case OS.GDK_BUTTON_PRESS: 436 case OS.GDK_2BUTTON_PRESS: 437 case OS.GDK_BUTTON_RELEASE: { 438 int state; 439 OS.gdk_event_get_state (ptr, &state); 440 setInputState (event, state); 441 break; 442 } 443 default: 444 } 445 OS.gdk_event_free (ptr); 446 } 447 if ((style & SWT.RADIO) !is 0) { 448 if ((parent.getStyle () & SWT.NO_RADIO_GROUP) is 0) { 449 selectRadio (); 450 } 451 } 452 postEvent (SWT.Selection, event); 453 return 0; 454 } 455 456 override int gtk_select (int item) { 457 parent.selectedItem = this; 458 sendEvent (SWT.Arm); 459 return 0; 460 } 461 462 override int gtk_show_help (GtkWidget* widget, ptrdiff_t helpType) { 463 bool handled = hooks (SWT.Help); 464 if (handled) { 465 postEvent (SWT.Help); 466 } else { 467 handled = parent.sendHelpEvent (helpType); 468 } 469 if (handled) { 470 OS.gtk_menu_shell_deactivate (cast(GtkMenuShell*)parent.handle); 471 return 1; 472 } 473 return 0; 474 } 475 476 override void hookEvents () { 477 super.hookEvents (); 478 OS.g_signal_connect_closure (handle, OS.activate.ptr, display.closures [ACTIVATE], false); 479 OS.g_signal_connect_closure (handle, OS.select.ptr, display.closures [SELECT], false); 480 OS.g_signal_connect_closure_by_id (handle, display.signalIds [SHOW_HELP], 0, display.closures [SHOW_HELP], false); 481 } 482 483 /** 484 * Returns <code>true</code> if the receiver is enabled and all 485 * of the receiver's ancestors are enabled, and <code>false</code> 486 * otherwise. A disabled menu item is typically not selectable from the 487 * user interface and draws with an inactive or "grayed" look. 488 * 489 * @return the receiver's enabled state 490 * 491 * @exception SWTException <ul> 492 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 493 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 494 * </ul> 495 * 496 * @see #getEnabled 497 */ 498 public bool isEnabled () { 499 return getEnabled () && parent.isEnabled (); 500 } 501 502 override void releaseChildren (bool destroy) { 503 if (menu !is null) { 504 menu.release (false); 505 menu = null; 506 } 507 super.releaseChildren (destroy); 508 } 509 510 override void releaseParent () { 511 super.releaseParent (); 512 if (menu !is null) { 513 if (menu.selectedItem is this) menu.selectedItem = null; 514 menu.dispose (); 515 } 516 menu = null; 517 } 518 519 override void releaseWidget () { 520 super.releaseWidget (); 521 auto accelGroup = getAccelGroup (); 522 if (accelGroup !is null) removeAccelerator (accelGroup); 523 if (groupHandle !is null) OS.g_object_unref (groupHandle); 524 groupHandle = null; 525 accelerator = 0; 526 parent = null; 527 } 528 529 void removeAccelerator (GtkAccelGroup* accelGroup) { 530 updateAccelerator (accelGroup, false); 531 } 532 533 void removeAccelerators (GtkAccelGroup* accelGroup) { 534 removeAccelerator (accelGroup); 535 if (menu !is null) menu.removeAccelerators (accelGroup); 536 } 537 538 /** 539 * Removes the listener from the collection of listeners who will 540 * be notified when the arm events are generated for the control. 541 * 542 * @param listener the listener which should no longer be notified 543 * 544 * @exception IllegalArgumentException <ul> 545 * <li>ERROR_NULL_ARGUMENT - if the listener is null</li> 546 * </ul> 547 * @exception SWTException <ul> 548 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 549 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 550 * </ul> 551 * 552 * @see ArmListener 553 * @see #addArmListener 554 */ 555 public void removeArmListener (ArmListener listener) { 556 checkWidget(); 557 if (listener is null) error (SWT.ERROR_NULL_ARGUMENT); 558 if (eventTable is null) return; 559 eventTable.unhook (SWT.Arm, listener); 560 } 561 562 /** 563 * Removes the listener from the collection of listeners who will 564 * be notified when the help events are generated for the control. 565 * 566 * @param listener the listener which should no longer be notified 567 * 568 * @exception IllegalArgumentException <ul> 569 * <li>ERROR_NULL_ARGUMENT - if the listener is null</li> 570 * </ul> 571 * @exception SWTException <ul> 572 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 573 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 574 * </ul> 575 * 576 * @see HelpListener 577 * @see #addHelpListener 578 */ 579 public void removeHelpListener (HelpListener listener) { 580 checkWidget(); 581 if (listener is null) error (SWT.ERROR_NULL_ARGUMENT); 582 if (eventTable is null) return; 583 eventTable.unhook (SWT.Help, listener); 584 } 585 586 /** 587 * Removes the listener from the collection of listeners who will 588 * be notified when the control is selected by the user. 589 * 590 * @param listener the listener which should no longer be notified 591 * 592 * @exception IllegalArgumentException <ul> 593 * <li>ERROR_NULL_ARGUMENT - if the listener is null</li> 594 * </ul> 595 * @exception SWTException <ul> 596 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 597 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 598 * </ul> 599 * 600 * @see SelectionListener 601 * @see #addSelectionListener 602 */ 603 public void removeSelectionListener (SelectionListener listener) { 604 checkWidget(); 605 if (listener is null) error (SWT.ERROR_NULL_ARGUMENT); 606 if (eventTable is null) return; 607 eventTable.unhook (SWT.Selection, listener); 608 eventTable.unhook (SWT.DefaultSelection,listener); 609 } 610 void selectRadio () { 611 int index = 0; 612 MenuItem [] items = parent.getItems (); 613 while (index < items.length && items [index] !is this) index++; 614 int i = index - 1; 615 while (i >= 0 && items [i].setRadioSelection (false)) --i; 616 int j = index + 1; 617 while (j < items.length && items [j].setRadioSelection (false)) j++; 618 setSelection (true); 619 } 620 /** 621 * Sets the widget accelerator. An accelerator is the bit-wise 622 * OR of zero or more modifier masks and a key. Examples: 623 * <code>SWT.MOD1 | SWT.MOD2 | 'T', SWT.MOD3 | SWT.F2</code>. 624 * <code>SWT.CONTROL | SWT.SHIFT | 'T', SWT.ALT | SWT.F2</code>. 625 * The default value is zero, indicating that the menu item does 626 * not have an accelerator. 627 * 628 * @param accelerator an integer that is the bit-wise OR of masks and a key 629 * 630 * </ul> 631 * @exception SWTException <ul> 632 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 633 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 634 * </ul> 635 */ 636 public void setAccelerator (int accelerator) { 637 checkWidget(); 638 if (this.accelerator is accelerator) return; 639 auto accelGroup = getAccelGroup (); 640 if (accelGroup !is null) removeAccelerator (accelGroup); 641 this.accelerator = accelerator; 642 if (accelGroup !is null) addAccelerator (accelGroup); 643 } 644 645 /** 646 * Enables the receiver if the argument is <code>true</code>, 647 * and disables it otherwise. A disabled menu item is typically 648 * not selectable from the user interface and draws with an 649 * inactive or "grayed" look. 650 * 651 * @param enabled the new enabled state 652 * 653 * @exception SWTException <ul> 654 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 655 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 656 * </ul> 657 */ 658 public void setEnabled (bool enabled) { 659 checkWidget(); 660 if (OS.GTK_WIDGET_SENSITIVE (handle) is enabled) return; 661 auto accelGroup = getAccelGroup (); 662 if (accelGroup !is null) removeAccelerator (accelGroup); 663 OS.gtk_widget_set_sensitive (handle, enabled); 664 if (accelGroup !is null) addAccelerator (accelGroup); 665 } 666 667 /** 668 * Sets the image the receiver will display to the argument. 669 * <p> 670 * Note: This operation is a hint and is not supported on 671 * platforms that do not have this concept (for example, Windows NT). 672 * </p> 673 * 674 * @param image the image to display 675 * 676 * @exception SWTException <ul> 677 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 678 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 679 * </ul> 680 */ 681 public override void setImage (Image image) { 682 checkWidget(); 683 if ((style & SWT.SEPARATOR) !is 0) return; 684 super.setImage (image); 685 if (!OS.GTK_IS_IMAGE_MENU_ITEM (cast(GTypeInstance*)handle)) return; 686 if (image !is null) { 687 ImageList imageList = parent.imageList; 688 if (imageList is null) imageList = parent.imageList = new ImageList (); 689 int imageIndex = imageList.indexOf (image); 690 if (imageIndex is -1) { 691 imageIndex = imageList.add (image); 692 } else { 693 imageList.put (imageIndex, image); 694 } 695 auto pixbuf = imageList.getPixbuf (imageIndex); 696 auto imageHandle = OS.gtk_image_new_from_pixbuf (pixbuf); 697 OS.gtk_image_menu_item_set_image (cast(GtkImageMenuItem*)handle, imageHandle); 698 OS.gtk_widget_show (imageHandle); 699 } else { 700 OS.gtk_image_menu_item_set_image (cast(GtkImageMenuItem*)handle, null); 701 } 702 } 703 704 /** 705 * Sets the receiver's pull down menu to the argument. 706 * Only <code>CASCADE</code> menu items can have a 707 * pull down menu. The sequence of key strokes, button presses 708 * and/or button releases that are used to request a pull down 709 * menu is platform specific. 710 * <p> 711 * Note: Disposing of a menu item that has a pull down menu 712 * will dispose of the menu. To avoid this behavior, set the 713 * menu to null before the menu item is disposed. 714 * </p> 715 * 716 * @param menu the new pull down menu 717 * 718 * @exception IllegalArgumentException <ul> 719 * <li>ERROR_MENU_NOT_DROP_DOWN - if the menu is not a drop down menu</li> 720 * <li>ERROR_MENUITEM_NOT_CASCADE - if the menu item is not a <code>CASCADE</code></li> 721 * <li>ERROR_INVALID_ARGUMENT - if the menu has been disposed</li> 722 * <li>ERROR_INVALID_PARENT - if the menu is not in the same widget tree</li> 723 * </ul> 724 * @exception SWTException <ul> 725 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 726 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 727 * </ul> 728 */ 729 public void setMenu (Menu menu) { 730 checkWidget (); 731 732 /* Check to make sure the new menu is valid */ 733 if ((style & SWT.CASCADE) is 0) { 734 error (SWT.ERROR_MENUITEM_NOT_CASCADE); 735 } 736 if (menu !is null) { 737 if ((menu.style & SWT.DROP_DOWN) is 0) { 738 error (SWT.ERROR_MENU_NOT_DROP_DOWN); 739 } 740 if (menu.parent !is parent.parent) { 741 error (SWT.ERROR_INVALID_PARENT); 742 } 743 } 744 745 /* Assign the new menu */ 746 Menu oldMenu = this.menu; 747 if (oldMenu is menu) return; 748 auto accelGroup = getAccelGroup (); 749 if (accelGroup !is null) removeAccelerators (accelGroup); 750 if (oldMenu !is null) { 751 oldMenu.cascade = null; 752 /* 753 * Add a reference to the menu we are about 754 * to replace or GTK will destroy it. 755 */ 756 OS.g_object_ref (oldMenu.handle); 757 OS.gtk_menu_item_remove_submenu (cast(GtkMenuItem*)handle); 758 } 759 if ((this.menu = menu) !is null) { 760 menu.cascade = this; 761 OS.gtk_menu_item_set_submenu (cast(GtkMenuItem*)handle, menu.handle); 762 } 763 if (accelGroup !is null) addAccelerators (accelGroup); 764 } 765 766 override void setOrientation() { 767 if ((parent.style & SWT.RIGHT_TO_LEFT) !is 0) { 768 if (handle !is null) { 769 OS.gtk_widget_set_direction (handle, OS.GTK_TEXT_DIR_RTL); 770 OS.gtk_container_forall (cast(GtkContainer*)handle, cast(GtkCallback)&Display.setDirectionProcFunc, cast(void*)OS.GTK_TEXT_DIR_RTL); 771 } 772 } 773 } 774 775 bool setRadioSelection (bool value) { 776 if ((style & SWT.RADIO) is 0) return false; 777 if (getSelection () !is value) { 778 setSelection (value); 779 postEvent (SWT.Selection); 780 } 781 return true; 782 } 783 784 /** 785 * Sets the selection state of the receiver. 786 * <p> 787 * When the receiver is of type <code>CHECK</code> or <code>RADIO</code>, 788 * it is selected when it is checked. 789 * 790 * @param selected the new selection state 791 * 792 * @exception SWTException <ul> 793 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 794 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 795 * </ul> 796 */ 797 public void setSelection (bool selected) { 798 checkWidget(); 799 if ((style & (SWT.CHECK | SWT.RADIO)) is 0) return; 800 OS.g_signal_handlers_block_matched (handle, OS.G_SIGNAL_MATCH_DATA, 0, 0, null, null, udACTIVATE); 801 OS.gtk_check_menu_item_set_active (cast(GtkCheckMenuItem*)handle, selected); 802 if ((style & SWT.RADIO) !is 0) OS.gtk_check_menu_item_set_active (cast(GtkCheckMenuItem*)groupHandle, !selected); 803 OS.g_signal_handlers_unblock_matched (handle, OS.G_SIGNAL_MATCH_DATA, 0, 0, null, null, udACTIVATE); 804 } 805 806 /** 807 * Sets the receiver's text. The string may include 808 * the mnemonic character and accelerator text. 809 * <p> 810 * Mnemonics are indicated by an '&' that causes the next 811 * character to be the mnemonic. When the user presses a 812 * key sequence that matches the mnemonic, a selection 813 * event occurs. On most platforms, the mnemonic appears 814 * underlined but may be emphasised in a platform specific 815 * manner. The mnemonic indicator character '&' can be 816 * escaped by doubling it in the string, causing a single 817 * '&' to be displayed. 818 * </p> 819 * <p> 820 * Accelerator text is indicated by the '\t' character. 821 * On platforms that support accelerator text, the text 822 * that follows the '\t' character is displayed to the user, 823 * typically indicating the key stroke that will cause 824 * the item to become selected. On most platforms, the 825 * accelerator text appears right aligned in the menu. 826 * Setting the accelerator text does not install the 827 * accelerator key sequence. The accelerator key sequence 828 * is installed using #setAccelerator. 829 * </p> 830 * 831 * @param string the new text 832 * 833 * @exception SWTException <ul> 834 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 835 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 836 * </ul> 837 * 838 * @see #setAccelerator 839 */ 840 public override void setText (String string) { 841 checkWidget(); 842 // SWT extension: allow null for zero length string 843 //if (string is null) error (SWT.ERROR_NULL_ARGUMENT); 844 if ((style & SWT.SEPARATOR) !is 0) return; 845 if (text.equals(string)) return; 846 super.setText (string); 847 String accelString = ""; 848 int index = string.indexOf ('\t'); 849 if (index !is -1) { 850 bool isRTL = (parent.style & SWT.RIGHT_TO_LEFT) !is 0; 851 accelString = (isRTL? "" : " ") ~ string.substring (index+1, cast(int)/*64bit*/string.length) ~ (isRTL? " " : ""); 852 string = string.substring (0, index); 853 } 854 char [] chars = fixMnemonic (string); 855 auto label = OS.gtk_bin_get_child (cast(GtkBin*)handle); 856 OS.gtk_label_set_text_with_mnemonic (cast(GtkLabel*)label, chars.toStringzValidPtr() ); 857 858 auto ptr = cast(char*)OS.g_malloc (accelString.length + 1); 859 ptr[ 0 .. accelString.length ] = accelString; 860 ptr[ accelString.length ] = '\0'; 861 862 auto oldPtr = OS.GTK_ACCEL_LABEL_GET_ACCEL_STRING (cast(GtkAccelLabel*)label); 863 OS.GTK_ACCEL_LABEL_SET_ACCEL_STRING (cast(GtkAccelLabel*)label, ptr); 864 if (oldPtr !is null) OS.g_free (oldPtr); 865 } 866 867 void updateAccelerator (GtkAccelGroup* accelGroup, bool add) { 868 if (accelerator is 0 || !getEnabled ()) return; 869 if ((accelerator & SWT.COMMAND) !is 0) return; 870 int mask = 0; 871 if ((accelerator & SWT.ALT) !is 0) mask |= OS.GDK_MOD1_MASK; 872 if ((accelerator & SWT.SHIFT) !is 0) mask |= OS.GDK_SHIFT_MASK; 873 if ((accelerator & SWT.CONTROL) !is 0) mask |= OS.GDK_CONTROL_MASK; 874 int keysym = accelerator & SWT.KEY_MASK; 875 int newKey = Display.untranslateKey (keysym); 876 if (newKey !is 0) { 877 keysym = newKey; 878 } else { 879 switch (keysym) { 880 case '\r': keysym = OS.GDK_Return; break; 881 default: keysym = Display.wcsToMbcs (cast(char) keysym); 882 } 883 } 884 /* When accel_key is zero, it causes GTK warnings */ 885 if (keysym !is 0) { 886 if (add) { 887 OS.gtk_widget_add_accelerator (handle, OS.activate.ptr, accelGroup, keysym, mask, OS.GTK_ACCEL_VISIBLE); 888 } else { 889 OS.gtk_widget_remove_accelerator (handle, accelGroup, keysym, mask); 890 } 891 } 892 } 893 }