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.Menu; 14 15 import org.eclipse.swt.SWT; 16 import org.eclipse.swt.internal.gtk.OS; 17 import org.eclipse.swt.events.MenuListener; 18 import org.eclipse.swt.events.HelpListener; 19 import org.eclipse.swt.graphics.Point; 20 import org.eclipse.swt.graphics.Rectangle; 21 import org.eclipse.swt.widgets.ImageList; 22 import org.eclipse.swt.widgets.TypedListener; 23 import org.eclipse.swt.widgets.Event; 24 import org.eclipse.swt.widgets.MenuItem; 25 import org.eclipse.swt.widgets.Widget; 26 import org.eclipse.swt.widgets.Control; 27 import org.eclipse.swt.widgets.Decorations; 28 import org.eclipse.swt.widgets.Shell; 29 import java.lang.all; 30 31 /** 32 * Instances of this class are user interface objects that contain 33 * menu items. 34 * <dl> 35 * <dt><b>Styles:</b></dt> 36 * <dd>BAR, DROP_DOWN, POP_UP, NO_RADIO_GROUP</dd> 37 * <dd>LEFT_TO_RIGHT, RIGHT_TO_LEFT</dd> 38 * <dt><b>Events:</b></dt> 39 * <dd>Help, Hide, Show </dd> 40 * </dl> 41 * <p> 42 * Note: Only one of BAR, DROP_DOWN and POP_UP may be specified. 43 * Only one of LEFT_TO_RIGHT or RIGHT_TO_LEFT may be specified. 44 * </p><p> 45 * IMPORTANT: This class is <em>not</em> intended to be subclassed. 46 * </p> 47 * 48 * @see <a href="http://www.eclipse.org/swt/snippets/#menu">Menu snippets</a> 49 * @see <a href="http://www.eclipse.org/swt/examples.php">SWT Example: ControlExample</a> 50 * @see <a href="http://www.eclipse.org/swt/">Sample code and further information</a> 51 */ 52 public class Menu : Widget { 53 int x, y; 54 bool hasLocation; 55 MenuItem cascade, selectedItem; 56 Decorations parent; 57 GtkWidget* imItem; 58 GtkWidget* imSeparator; 59 GtkIMContext* imHandle; 60 ImageList imageList; 61 62 /** 63 * Constructs a new instance of this class given its parent, 64 * and sets the style for the instance so that the instance 65 * will be a popup menu on the given parent's shell. 66 * <p> 67 * After constructing a menu, it can be set into its parent 68 * using <code>parent.setMenu(menu)</code>. In this case, the parent may 69 * be any control in the same widget tree as the parent. 70 * </p> 71 * 72 * @param parent a control which will be the parent of the new instance (cannot be null) 73 * 74 * @exception IllegalArgumentException <ul> 75 * <li>ERROR_NULL_ARGUMENT - if the parent is null</li> 76 * </ul> 77 * @exception SWTException <ul> 78 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li> 79 * <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li> 80 * </ul> 81 * 82 * @see SWT#POP_UP 83 * @see Widget#checkSubclass 84 * @see Widget#getStyle 85 */ 86 public this (Control parent) { 87 this (checkNull (parent).menuShell (), SWT.POP_UP); 88 } 89 90 /** 91 * Constructs a new instance of this class given its parent 92 * (which must be a <code>Decorations</code>) and a style value 93 * describing its behavior and appearance. 94 * <p> 95 * The style value is either one of the style constants defined in 96 * class <code>SWT</code> which is applicable to instances of this 97 * class, or must be built by <em>bitwise OR</em>'ing together 98 * (that is, using the <code>int</code> "|" operator) two or more 99 * of those <code>SWT</code> style constants. The class description 100 * lists the style constants that are applicable to the class. 101 * Style bits are also inherited from superclasses. 102 * </p><p> 103 * After constructing a menu or menuBar, it can be set into its parent 104 * using <code>parent.setMenu(menu)</code> or <code>parent.setMenuBar(menuBar)</code>. 105 * </p> 106 * 107 * @param parent a decorations control which will be the parent of the new instance (cannot be null) 108 * @param style the style of menu to construct 109 * 110 * @exception IllegalArgumentException <ul> 111 * <li>ERROR_NULL_ARGUMENT - if the parent is null</li> 112 * </ul> 113 * @exception SWTException <ul> 114 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li> 115 * <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li> 116 * </ul> 117 * 118 * @see SWT#BAR 119 * @see SWT#DROP_DOWN 120 * @see SWT#POP_UP 121 * @see Widget#checkSubclass 122 * @see Widget#getStyle 123 */ 124 public this (Decorations parent, int style) { 125 super (parent, checkStyle (style)); 126 this.parent = parent; 127 createWidget (0); 128 } 129 130 /** 131 * Constructs a new instance of this class given its parent 132 * (which must be a <code>Menu</code>) and sets the style 133 * for the instance so that the instance will be a drop-down 134 * menu on the given parent's parent. 135 * <p> 136 * After constructing a drop-down menu, it can be set into its parentMenu 137 * using <code>parentMenu.setMenu(menu)</code>. 138 * </p> 139 * 140 * @param parentMenu a menu which will be the parent of the new instance (cannot be null) 141 * 142 * @exception IllegalArgumentException <ul> 143 * <li>ERROR_NULL_ARGUMENT - if the parent is null</li> 144 * </ul> 145 * @exception SWTException <ul> 146 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li> 147 * <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li> 148 * </ul> 149 * 150 * @see SWT#DROP_DOWN 151 * @see Widget#checkSubclass 152 * @see Widget#getStyle 153 */ 154 public this (Menu parentMenu) { 155 this (checkNull (parentMenu).parent, SWT.DROP_DOWN); 156 } 157 158 /** 159 * Constructs a new instance of this class given its parent 160 * (which must be a <code>MenuItem</code>) and sets the style 161 * for the instance so that the instance will be a drop-down 162 * menu on the given parent's parent menu. 163 * <p> 164 * After constructing a drop-down menu, it can be set into its parentItem 165 * using <code>parentItem.setMenu(menu)</code>. 166 * </p> 167 * 168 * @param parentItem a menu item which will be the parent of the new instance (cannot be null) 169 * 170 * @exception IllegalArgumentException <ul> 171 * <li>ERROR_NULL_ARGUMENT - if the parent is null</li> 172 * </ul> 173 * @exception SWTException <ul> 174 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li> 175 * <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li> 176 * </ul> 177 * 178 * @see SWT#DROP_DOWN 179 * @see Widget#checkSubclass 180 * @see Widget#getStyle 181 */ 182 public this (MenuItem parentItem) { 183 this (checkNull (parentItem).parent); 184 } 185 186 static Control checkNull (Control control) { 187 if (control is null) SWT.error (SWT.ERROR_NULL_ARGUMENT); 188 return control; 189 } 190 191 static Menu checkNull (Menu menu) { 192 if (menu is null) SWT.error (SWT.ERROR_NULL_ARGUMENT); 193 return menu; 194 } 195 196 static MenuItem checkNull (MenuItem item) { 197 if (item is null) SWT.error (SWT.ERROR_NULL_ARGUMENT); 198 return item; 199 } 200 201 static int checkStyle (int style) { 202 return checkBits (style, SWT.POP_UP, SWT.BAR, SWT.DROP_DOWN, 0, 0, 0); 203 } 204 205 void _setVisible (bool visible) { 206 if (visible is OS.GTK_WIDGET_MAPPED (handle)) return; 207 if (visible) { 208 sendEvent (SWT.Show); 209 if (getItemCount () !is 0) { 210 if ((OS.GTK_VERSION >= OS.buildVERSION (2, 8, 0))) { 211 /* 212 * Feature in GTK. ON_TOP shells will send out 213 * SWT.Deactivate whenever a context menu is shown. 214 * The fix is to prevent the menu from taking focus 215 * when it is being shown in an ON_TOP shell. 216 */ 217 if ((parent._getShell ().style & SWT.ON_TOP) !is 0) { 218 OS.gtk_menu_shell_set_take_focus (cast(GtkMenuShell*)handle, false); 219 } 220 } 221 /* 222 * Bug in GTK. The timestamp passed into gtk_menu_popup is used 223 * to perform an X pointer grab. It cannot be zero, else the grab 224 * will fail. The fix is to ensure that the timestamp of the last 225 * event processed is used. 226 */ 227 getDisplay().doMenuPositionProc( cast(GtkMenu*)handle, hasLocation ); 228 } else { 229 sendEvent (SWT.Hide); 230 } 231 } else { 232 OS.gtk_menu_popdown (cast(GtkMenu*)handle); 233 } 234 } 235 236 void addAccelerators (GtkAccelGroup* accelGroup) { 237 MenuItem [] items = getItems (); 238 for (int i = 0; i < items.length; i++) { 239 MenuItem item = items[i]; 240 item.addAccelerators (accelGroup); 241 } 242 } 243 244 /** 245 * Adds the listener to the collection of listeners who will 246 * be notified when menus are hidden or shown, by sending it 247 * one of the messages defined in the <code>MenuListener</code> 248 * interface. 249 * 250 * @param listener the listener which should be notified 251 * 252 * @exception IllegalArgumentException <ul> 253 * <li>ERROR_NULL_ARGUMENT - if the listener is null</li> 254 * </ul> 255 * @exception SWTException <ul> 256 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 257 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 258 * </ul> 259 * 260 * @see MenuListener 261 * @see #removeMenuListener 262 */ 263 public void addMenuListener (MenuListener listener) { 264 checkWidget(); 265 if (listener is null) error (SWT.ERROR_NULL_ARGUMENT); 266 TypedListener typedListener = new TypedListener (listener); 267 addListener (SWT.Hide,typedListener); 268 addListener (SWT.Show,typedListener); 269 } 270 271 /** 272 * Adds the listener to the collection of listeners who will 273 * be notified when help events are generated for the control, 274 * by sending it one of the messages defined in the 275 * <code>HelpListener</code> interface. 276 * 277 * @param listener the listener which should be notified 278 * 279 * @exception IllegalArgumentException <ul> 280 * <li>ERROR_NULL_ARGUMENT - if the listener is null</li> 281 * </ul> 282 * @exception SWTException <ul> 283 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 284 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 285 * </ul> 286 * 287 * @see HelpListener 288 * @see #removeHelpListener 289 */ 290 public void addHelpListener (HelpListener listener) { 291 checkWidget(); 292 if (listener is null) error (SWT.ERROR_NULL_ARGUMENT); 293 TypedListener typedListener = new TypedListener (listener); 294 addListener (SWT.Help, typedListener); 295 } 296 297 override void createHandle (int index) { 298 state |= HANDLE; 299 if ((style & SWT.BAR) !is 0) { 300 handle = OS.gtk_menu_bar_new (); 301 if (handle is null) error (SWT.ERROR_NO_HANDLES); 302 auto vboxHandle = parent.vboxHandle; 303 OS.gtk_container_add (cast(GtkContainer*)vboxHandle, handle); 304 OS.gtk_box_set_child_packing (cast(GtkBox*)vboxHandle, handle, false, true, 0, OS.GTK_PACK_START); 305 } else { 306 handle = OS.gtk_menu_new (); 307 if (handle is null) error (SWT.ERROR_NO_HANDLES); 308 } 309 } 310 311 void createIMMenu (GtkIMContext* imHandle) { 312 if (this.imHandle is imHandle) return; 313 this.imHandle = imHandle; 314 if (imHandle is null) { 315 if (imItem !is null) { 316 OS.gtk_widget_destroy (imItem); 317 imItem = null; 318 } 319 if (imSeparator !is null) { 320 OS.gtk_widget_destroy (imSeparator); 321 imSeparator = null; 322 } 323 return; 324 } 325 if (imSeparator is null) { 326 imSeparator = OS.gtk_separator_menu_item_new (); 327 OS.gtk_widget_show (imSeparator); 328 OS.gtk_menu_shell_insert (cast(GtkMenuShell*)handle, imSeparator, -1); 329 } 330 if (imItem is null) { 331 char* buffer = toStringz( SWT.getMessage("SWT_InputMethods")); 332 imItem = OS.gtk_image_menu_item_new_with_label (buffer); 333 OS.gtk_widget_show (imItem); 334 OS.gtk_menu_shell_insert (cast(GtkMenuShell*)handle, imItem, -1); 335 } 336 auto imSubmenu = OS.gtk_menu_new (); 337 OS.gtk_im_multicontext_append_menuitems (cast(GtkIMMulticontext*)imHandle, cast(GtkMenuShell*)imSubmenu); 338 OS.gtk_menu_item_set_submenu (cast(GtkMenuItem*)imItem, imSubmenu); 339 } 340 341 override void createWidget (int index) { 342 checkOrientation (parent); 343 super.createWidget (index); 344 parent.addMenu (this); 345 } 346 347 void fixMenus (Decorations newParent) { 348 MenuItem [] items = getItems (); 349 for (int i=0; i<items.length; i++) { 350 items [i].fixMenus (newParent); 351 } 352 parent.removeMenu (this); 353 newParent.addMenu (this); 354 this.parent = newParent; 355 } 356 357 /*public*/ Rectangle getBounds () { 358 checkWidget(); 359 if (!OS.GTK_WIDGET_MAPPED (handle)) { 360 return new Rectangle (0, 0, 0, 0); 361 } 362 auto window = OS.GTK_WIDGET_WINDOW (handle); 363 int origin_x, origin_y; 364 OS.gdk_window_get_origin (window, &origin_x, &origin_y); 365 int x = origin_x + OS.GTK_WIDGET_X (handle); 366 int y = origin_y + OS.GTK_WIDGET_Y (handle); 367 int width = OS.GTK_WIDGET_WIDTH (handle); 368 int height = OS.GTK_WIDGET_HEIGHT (handle); 369 return new Rectangle (x, y, width, height); 370 } 371 372 /** 373 * Returns the default menu item or null if none has 374 * been previously set. 375 * 376 * @return the default menu item. 377 * 378 * </ul> 379 * @exception SWTException <ul> 380 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 381 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 382 * </ul> 383 */ 384 public MenuItem getDefaultItem () { 385 checkWidget(); 386 return null; 387 } 388 389 /** 390 * Returns <code>true</code> if the receiver is enabled, and 391 * <code>false</code> otherwise. A disabled menu is typically 392 * not selectable from the user interface and draws with an 393 * inactive or "grayed" look. 394 * 395 * @return the receiver's enabled state 396 * 397 * @exception SWTException <ul> 398 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 399 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 400 * </ul> 401 * 402 * @see #isEnabled 403 */ 404 public bool getEnabled () { 405 checkWidget(); 406 return OS.GTK_WIDGET_SENSITIVE (handle); 407 } 408 409 /** 410 * Returns the item at the given, zero-relative index in the 411 * receiver. Throws an exception if the index is out of range. 412 * 413 * @param index the index of the item to return 414 * @return the item at the given index 415 * 416 * @exception IllegalArgumentException <ul> 417 * <li>ERROR_INVALID_RANGE - if the index is not between 0 and the number of elements in the list minus 1 (inclusive)</li> 418 * </ul> 419 * @exception SWTException <ul> 420 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 421 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 422 * </ul> 423 */ 424 public MenuItem getItem (int index) { 425 checkWidget(); 426 auto list = OS.gtk_container_get_children (cast(GtkContainer*)handle); 427 if (list is null) error (SWT.ERROR_CANNOT_GET_ITEM); 428 int count = OS.g_list_length (list); 429 if (imSeparator !is null) count--; 430 if (imItem !is null) count--; 431 if (!(0 <= index && index < count)) error (SWT.ERROR_INVALID_RANGE); 432 auto data = cast(GtkWidget*)OS.g_list_nth_data (list, index); 433 OS.g_list_free (list); 434 if (data is null) error (SWT.ERROR_CANNOT_GET_ITEM); 435 return cast(MenuItem) display.getWidget (data); 436 } 437 438 /** 439 * Returns the number of items contained in the receiver. 440 * 441 * @return the number of items 442 * 443 * @exception SWTException <ul> 444 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 445 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 446 * </ul> 447 */ 448 public int getItemCount () { 449 checkWidget(); 450 auto list = OS.gtk_container_get_children (cast(GtkContainer*)handle); 451 if (list is null) return 0; 452 int count = OS.g_list_length (list); 453 OS.g_list_free (list); 454 if (imSeparator !is null) count--; 455 if (imItem !is null) count--; 456 return count; 457 } 458 459 /** 460 * Returns a (possibly empty) array of <code>MenuItem</code>s which 461 * are the items in the receiver. 462 * <p> 463 * Note: This is not the actual structure used by the receiver 464 * to maintain its list of items, so modifying the array will 465 * not affect the receiver. 466 * </p> 467 * 468 * @return the items in the receiver 469 * 470 * @exception SWTException <ul> 471 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 472 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 473 * </ul> 474 */ 475 public MenuItem [] getItems () { 476 checkWidget(); 477 auto list = OS.gtk_container_get_children (cast(GtkContainer*)handle); 478 if (list is null) return new MenuItem [0]; 479 int count = OS.g_list_length (list); 480 if (imSeparator !is null) count--; 481 if (imItem !is null) count--; 482 MenuItem [] items = new MenuItem [count]; 483 int index = 0; 484 for (int i=0; i<count; i++) { 485 auto data = cast(GtkWidget*)OS.g_list_nth_data (list, i); 486 MenuItem item = cast(MenuItem) display.getWidget (data); 487 if (item !is null) items [index++] = item; 488 } 489 OS.g_list_free (list); 490 if (index !is items.length) { 491 MenuItem [] newItems = new MenuItem[index]; 492 System.arraycopy(items, 0, newItems, 0, index); 493 items = newItems; 494 } 495 return items; 496 } 497 498 override String getNameText () { 499 String result = ""; 500 MenuItem [] items = getItems (); 501 ptrdiff_t length_ = items.length; 502 if (length_ > 0) { 503 for (int i=0; i<length_-1; i++) { 504 result = result ~ items [i].getNameText() ~ ", "; 505 } 506 result = result ~ items [length_-1].getNameText (); 507 } 508 return result; 509 } 510 511 /** 512 * Returns the receiver's parent, which must be a <code>Decorations</code>. 513 * 514 * @return the receiver's parent 515 * 516 * @exception SWTException <ul> 517 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 518 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 519 * </ul> 520 */ 521 public Decorations getParent () { 522 checkWidget(); 523 return parent; 524 } 525 526 /** 527 * Returns the receiver's parent item, which must be a 528 * <code>MenuItem</code> or null when the receiver is a 529 * root. 530 * 531 * @return the receiver's parent item 532 * 533 * @exception SWTException <ul> 534 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 535 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 536 * </ul> 537 */ 538 public MenuItem getParentItem () { 539 checkWidget(); 540 return cascade; 541 } 542 543 /** 544 * Returns the receiver's parent item, which must be a 545 * <code>Menu</code> or null when the receiver is a 546 * root. 547 * 548 * @return the receiver's parent item 549 * 550 * @exception SWTException <ul> 551 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 552 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 553 * </ul> 554 */ 555 public Menu getParentMenu () { 556 checkWidget(); 557 if (cascade is null) return null; 558 return cascade.getParent (); 559 } 560 561 /** 562 * Returns the receiver's shell. For all controls other than 563 * shells, this simply returns the control's nearest ancestor 564 * shell. Shells return themselves, even if they are children 565 * of other shells. 566 * 567 * @return the receiver's shell 568 * 569 * @exception SWTException <ul> 570 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 571 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 572 * </ul> 573 * 574 * @see #getParent 575 */ 576 public Shell getShell () { 577 checkWidget(); 578 return parent.getShell (); 579 } 580 581 /** 582 * Returns <code>true</code> if the receiver is visible, and 583 * <code>false</code> otherwise. 584 * <p> 585 * If one of the receiver's ancestors is not visible or some 586 * other condition makes the receiver not visible, this method 587 * may still indicate that it is considered visible even though 588 * it may not actually be showing. 589 * </p> 590 * 591 * @return the receiver's visibility state 592 * 593 * @exception SWTException <ul> 594 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 595 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 596 * </ul> 597 */ 598 public bool getVisible () { 599 checkWidget(); 600 if ((style & SWT.POP_UP) !is 0) { 601 Menu [] popups = display.popups; 602 if (popups !is null) { 603 for (int i=0; i<popups.length; i++) { 604 if (popups [i] is this) return true; 605 } 606 } 607 } 608 return OS.GTK_WIDGET_MAPPED (handle); 609 } 610 611 override int gtk_hide (GtkWidget* widget) { 612 if ((style & SWT.POP_UP) !is 0) { 613 display.activeShell = getShell (); 614 } 615 if (OS.GTK_VERSION >= OS.buildVERSION (2, 6, 0)) { 616 sendEvent (SWT.Hide); 617 } else { 618 /* 619 * Bug in GTK. In GTK 2.4 and earlier 620 * a crash could occur if a menu item 621 * was disposed within gtk_hide. The 622 * workaroud is to post the event instead 623 * of send it on these platforms 624 */ 625 postEvent (SWT.Hide); 626 } 627 return 0; 628 } 629 630 override int gtk_show (GtkWidget* widget) { 631 if ((style & SWT.POP_UP) !is 0) { 632 display.activeShell = getShell (); 633 return 0; 634 } 635 sendEvent (SWT.Show); 636 return 0; 637 } 638 639 640 override int gtk_show_help (GtkWidget* widget, ptrdiff_t helpType) { 641 if (sendHelpEvent (helpType)) { 642 OS.gtk_menu_shell_deactivate (cast(GtkMenuShell*)handle); 643 return 1; 644 } 645 return 0; 646 } 647 648 override void hookEvents () { 649 super.hookEvents (); 650 OS.g_signal_connect_closure_by_id (handle, display.signalIds [SHOW], 0, display.closures [SHOW], false); 651 OS.g_signal_connect_closure_by_id (handle, display.signalIds [HIDE], 0, display.closures [HIDE], false); 652 OS.g_signal_connect_closure_by_id (handle, display.signalIds [SHOW_HELP], 0, display.closures [SHOW_HELP], false); 653 } 654 655 /** 656 * Searches the receiver's list starting at the first item 657 * (index 0) until an item is found that is equal to the 658 * argument, and returns the index of that item. If no item 659 * is found, returns -1. 660 * 661 * @param item the search item 662 * @return the index of the item 663 * 664 * @exception IllegalArgumentException <ul> 665 * <li>ERROR_NULL_ARGUMENT - if the item is null</li> 666 * </ul> 667 * @exception SWTException <ul> 668 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 669 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 670 * </ul> 671 */ 672 public int indexOf (MenuItem item) { 673 checkWidget(); 674 if (item is null) error (SWT.ERROR_NULL_ARGUMENT); 675 MenuItem [] items = getItems (); 676 for (int i=0; i<items.length; i++) { 677 if (items [i] is item) return i; 678 } 679 return -1; 680 } 681 682 /** 683 * Returns <code>true</code> if the receiver is enabled and all 684 * of the receiver's ancestors are enabled, and <code>false</code> 685 * otherwise. A disabled menu is typically not selectable from the 686 * user interface and draws with an inactive or "grayed" look. 687 * 688 * @return the receiver's enabled state 689 * 690 * @exception SWTException <ul> 691 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 692 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 693 * </ul> 694 * 695 * @see #getEnabled 696 */ 697 public bool isEnabled () { 698 checkWidget(); 699 Menu parentMenu = getParentMenu (); 700 if (parentMenu is null) { 701 return getEnabled () && parent.isEnabled (); 702 } 703 return getEnabled () && parentMenu.isEnabled (); 704 } 705 706 /** 707 * Returns <code>true</code> if the receiver is visible and all 708 * of the receiver's ancestors are visible and <code>false</code> 709 * otherwise. 710 * 711 * @return the receiver's visibility state 712 * 713 * @exception SWTException <ul> 714 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 715 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 716 * </ul> 717 * 718 * @see #getVisible 719 */ 720 public bool isVisible () { 721 checkWidget(); 722 return getVisible (); 723 } 724 725 override void menuPositionProc (GtkMenu* menu, int* x, int* y, int* push_in, void* user_data) { 726 /* 727 * Feature in GTK. The menu position function sets the position of the 728 * top-left pixel of the menu. If the menu would be off-screen, GTK will 729 * add a scroll arrow at the bottom and position the first menu entry at 730 * the specified position. The fix is to flip the menu location to be 731 * completely inside the screen. 732 * 733 * NOTE: This code doesn't work for multiple monitors. 734 */ 735 GtkRequisition requisition; 736 OS.gtk_widget_size_request (cast(GtkWidget*)menu, &requisition); 737 int screenHeight = OS.gdk_screen_height (); 738 int reqy = this.y; 739 if (reqy + requisition.height > screenHeight) { 740 reqy = Math.max (0, reqy - requisition.height); 741 } 742 int screenWidth = OS.gdk_screen_width (); 743 int reqx = this.x; 744 if ((style & SWT.RIGHT_TO_LEFT) !is 0) { 745 if (reqx - requisition.width >= 0) reqx -= requisition.width; 746 } else { 747 if (reqx + requisition.width > screenWidth) reqx -= requisition.width; 748 } 749 if (x !is null) *x = reqx; 750 if (y !is null) *y = reqy; 751 if (push_in !is null) *push_in = 1; 752 } 753 754 override void releaseChildren (bool destroy) { 755 MenuItem [] items = getItems (); 756 for (int i=0; i<items.length; i++) { 757 MenuItem item = items [i]; 758 if (item !is null && !item.isDisposed ()) { 759 item.release (false); 760 } 761 } 762 super.releaseChildren (destroy); 763 } 764 765 override void releaseParent () { 766 super.releaseParent (); 767 if (cascade !is null) cascade.setMenu (null); 768 if ((style & SWT.BAR) !is 0 && this is parent.menuBar) { 769 parent.setMenuBar (null); 770 } else { 771 if ((style & SWT.POP_UP) !is 0) { 772 display.removePopup (this); 773 } 774 } 775 } 776 777 override void releaseWidget () { 778 super.releaseWidget (); 779 if (parent !is null) parent.removeMenu (this); 780 parent = null; 781 cascade = null; 782 imItem = null; 783 imSeparator = null; 784 imHandle = null; 785 if (imageList !is null) imageList.dispose (); 786 imageList = null; 787 } 788 789 /** 790 * Removes the listener from the collection of listeners who will 791 * be notified when the menu events are generated for the control. 792 * 793 * @param listener the listener which should no longer be notified 794 * 795 * @exception IllegalArgumentException <ul> 796 * <li>ERROR_NULL_ARGUMENT - if the listener is null</li> 797 * </ul> 798 * @exception SWTException <ul> 799 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 800 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 801 * </ul> 802 * 803 * @see MenuListener 804 * @see #addMenuListener 805 */ 806 public void removeMenuListener (MenuListener listener) { 807 checkWidget(); 808 if (listener is null) error (SWT.ERROR_NULL_ARGUMENT); 809 if (eventTable is null) return; 810 eventTable.unhook (SWT.Hide, listener); 811 eventTable.unhook (SWT.Show, listener); 812 } 813 814 void removeAccelerators (GtkAccelGroup* accelGroup) { 815 MenuItem [] items = getItems (); 816 for (int i = 0; i < items.length; i++) { 817 MenuItem item = items[i]; 818 item.removeAccelerators (accelGroup); 819 } 820 } 821 822 /** 823 * Removes the listener from the collection of listeners who will 824 * be notified when the help events are generated for the control. 825 * 826 * @param listener the listener which should no longer be notified 827 * 828 * @exception IllegalArgumentException <ul> 829 * <li>ERROR_NULL_ARGUMENT - if the listener is null</li> 830 * </ul> 831 * @exception SWTException <ul> 832 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 833 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 834 * </ul> 835 * 836 * @see HelpListener 837 * @see #addHelpListener 838 */ 839 public void removeHelpListener (HelpListener listener) { 840 checkWidget(); 841 if (listener is null) error (SWT.ERROR_NULL_ARGUMENT); 842 if (eventTable is null) return; 843 eventTable.unhook (SWT.Help, listener); 844 } 845 846 bool sendHelpEvent (ptrdiff_t helpType) { 847 if (selectedItem !is null && !selectedItem.isDisposed()) { 848 if (selectedItem.hooks (SWT.Help)) { 849 selectedItem.postEvent (SWT.Help); 850 return true; 851 } 852 } 853 if (hooks (SWT.Help)) { 854 postEvent (SWT.Help); 855 return true; 856 } 857 return parent.sendHelpEvent (helpType); 858 } 859 860 /** 861 * Sets the default menu item to the argument or removes 862 * the default emphasis when the argument is <code>null</code>. 863 * 864 * @param item the default menu item or null 865 * 866 * @exception IllegalArgumentException <ul> 867 * <li>ERROR_INVALID_ARGUMENT - if the menu item has been disposed</li> 868 * </ul> 869 * @exception SWTException <ul> 870 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 871 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 872 * </ul> 873 */ 874 public void setDefaultItem (MenuItem item) { 875 checkWidget(); 876 } 877 878 /** 879 * Enables the receiver if the argument is <code>true</code>, 880 * and disables it otherwise. A disabled menu is typically 881 * not selectable from the user interface and draws with an 882 * inactive or "grayed" look. 883 * 884 * @param enabled the new enabled state 885 * 886 * @exception SWTException <ul> 887 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 888 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 889 * </ul> 890 */ 891 public void setEnabled (bool enabled) { 892 checkWidget(); 893 if (enabled) { 894 OS.GTK_WIDGET_SET_FLAGS (handle, OS.GTK_SENSITIVE); 895 } else { 896 OS.GTK_WIDGET_UNSET_FLAGS (handle, OS.GTK_SENSITIVE); 897 } 898 } 899 900 /** 901 * Sets the location of the receiver, which must be a popup, 902 * to the point specified by the arguments which are relative 903 * to the display. 904 * <p> 905 * Note that this is different from most widgets where the 906 * location of the widget is relative to the parent. 907 * </p><p> 908 * Note that the platform window manager ultimately has control 909 * over the location of popup menus. 910 * </p> 911 * 912 * @param x the new x coordinate for the receiver 913 * @param y the new y coordinate for the receiver 914 * 915 * @exception SWTException <ul> 916 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 917 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 918 * </ul> 919 */ 920 public void setLocation (int x, int y) { 921 checkWidget(); 922 if ((style & (SWT.BAR | SWT.DROP_DOWN)) !is 0) return; 923 this.x = x; 924 this.y = y; 925 hasLocation = true; 926 } 927 928 /** 929 * Sets the location of the receiver, which must be a popup, 930 * to the point specified by the argument which is relative 931 * to the display. 932 * <p> 933 * Note that this is different from most widgets where the 934 * location of the widget is relative to the parent. 935 * </p><p> 936 * Note that the platform window manager ultimately has control 937 * over the location of popup menus. 938 * </p> 939 * 940 * @param location the new location for the receiver 941 * 942 * @exception IllegalArgumentException <ul> 943 * <li>ERROR_NULL_ARGUMENT - if the point is null</li> 944 * </ul> 945 * @exception SWTException <ul> 946 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 947 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 948 * </ul> 949 * 950 * @since 2.1 951 */ 952 public void setLocation (Point location) { 953 checkWidget(); 954 if (location is null) error (SWT.ERROR_NULL_ARGUMENT); 955 setLocation (location.x, location.y); 956 } 957 958 override void setOrientation() { 959 if ((parent.style & SWT.RIGHT_TO_LEFT) !is 0) { 960 if (handle !is null) OS.gtk_widget_set_direction (handle, OS.GTK_TEXT_DIR_RTL); 961 } 962 } 963 964 /** 965 * Marks the receiver as visible if the argument is <code>true</code>, 966 * and marks it invisible otherwise. 967 * <p> 968 * If one of the receiver's ancestors is not visible or some 969 * other condition makes the receiver not visible, marking 970 * it visible may not actually cause it to be displayed. 971 * </p> 972 * 973 * @param visible the new visibility state 974 * 975 * @exception SWTException <ul> 976 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 977 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 978 * </ul> 979 */ 980 public void setVisible (bool visible) { 981 checkWidget(); 982 if ((style & (SWT.BAR | SWT.DROP_DOWN)) !is 0) return; 983 if (visible) { 984 display.addPopup (this); 985 } else { 986 display.removePopup (this); 987 _setVisible (false); 988 } 989 } 990 }