1 /*******************************************************************************
2 * Copyright (c) 2000, 2014 IBM Corporation and others.
4 * This program and the accompanying materials
5 * are made available under the terms of the Eclipse Public License 2.0
6 * which accompanies this distribution, and is available at
7 * https://www.eclipse.org/legal/epl-2.0/
9 * SPDX-License-Identifier: EPL-2.0
12 * IBM Corporation - initial API and implementation
13 *******************************************************************************/
14 package org.eclipse.swt.widgets;
17 import org.eclipse.swt.*;
18 import org.eclipse.swt.events.*;
19 import org.eclipse.swt.graphics.*;
20 import org.eclipse.swt.internal.*;
21 import org.eclipse.swt.internal.win32.*;
24 * Instances of this class are user interface objects that contain
27 * <dt><b>Styles:</b></dt>
28 * <dd>BAR, DROP_DOWN, POP_UP, NO_RADIO_GROUP</dd>
29 * <dd>LEFT_TO_RIGHT, RIGHT_TO_LEFT</dd>
30 * <dt><b>Events:</b></dt>
31 * <dd>Help, Hide, Show </dd>
34 * Note: Only one of BAR, DROP_DOWN and POP_UP may be specified.
35 * Only one of LEFT_TO_RIGHT or RIGHT_TO_LEFT may be specified.
37 * IMPORTANT: This class is <em>not</em> intended to be subclassed.
40 * @see <a href="http://www.eclipse.org/swt/snippets/#menu">Menu snippets</a>
41 * @see <a href="http://www.eclipse.org/swt/examples.php">SWT Example: ControlExample</a>
42 * @see <a href="http://www.eclipse.org/swt/">Sample code and further information</a>
43 * @noextend This class is not intended to be subclassed by clients.
45 public class Menu extends Widget {
47 * the handle to the OS resource
48 * (Warning: This field is platform dependent)
50 * <b>IMPORTANT:</b> This field is <em>not</em> part of the SWT
51 * public API. It is marked public only so that it can be shared
52 * within the packages provided by SWT. It is not available on all
53 * platforms and should never be accessed from application code.
56 * @noreference This field is not intended to be referenced by clients.
63 int foreground = -1, background = -1;
64 Image backgroundImage;
68 MenuItem selectedMenuItem;
70 /* Timer ID for MenuItem ToolTip */
71 static final int ID_TOOLTIP_TIMER = 110;
74 * Constructs a new instance of this class given its parent,
75 * and sets the style for the instance so that the instance
76 * will be a popup menu on the given parent's shell.
78 * After constructing a menu, it can be set into its parent
79 * using <code>parent.setMenu(menu)</code>. In this case, the parent may
80 * be any control in the same widget tree as the parent.
83 * @param parent a control which will be the parent of the new instance (cannot be null)
85 * @exception IllegalArgumentException <ul>
86 * <li>ERROR_NULL_ARGUMENT - if the parent is null</li>
88 * @exception SWTException <ul>
89 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li>
90 * <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li>
94 * @see Widget#checkSubclass
95 * @see Widget#getStyle
97 public Menu (Control parent) {
98 this (checkNull (parent).menuShell (), SWT.POP_UP);
102 * Constructs a new instance of this class given its parent
103 * (which must be a <code>Decorations</code>) and a style value
104 * describing its behavior and appearance.
106 * The style value is either one of the style constants defined in
107 * class <code>SWT</code> which is applicable to instances of this
108 * class, or must be built by <em>bitwise OR</em>'ing together
109 * (that is, using the <code>int</code> "|" operator) two or more
110 * of those <code>SWT</code> style constants. The class description
111 * lists the style constants that are applicable to the class.
112 * Style bits are also inherited from superclasses.
114 * After constructing a menu or menuBar, it can be set into its parent
115 * using <code>parent.setMenu(menu)</code> or <code>parent.setMenuBar(menuBar)</code>.
118 * @param parent a decorations control which will be the parent of the new instance (cannot be null)
119 * @param style the style of menu to construct
121 * @exception IllegalArgumentException <ul>
122 * <li>ERROR_NULL_ARGUMENT - if the parent is null</li>
124 * @exception SWTException <ul>
125 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li>
126 * <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li>
132 * @see SWT#NO_RADIO_GROUP
133 * @see SWT#LEFT_TO_RIGHT
134 * @see SWT#RIGHT_TO_LEFT
135 * @see Widget#checkSubclass
136 * @see Widget#getStyle
138 public Menu (Decorations parent, int style) {
139 this (parent, checkStyle (style), 0);
143 * Constructs a new instance of this class given its parent
144 * (which must be a <code>Menu</code>) and sets the style
145 * for the instance so that the instance will be a drop-down
146 * menu on the given parent's parent.
148 * After constructing a drop-down menu, it can be set into its parentMenu
149 * using <code>parentMenu.setMenu(menu)</code>.
152 * @param parentMenu a menu which will be the parent of the new instance (cannot be null)
154 * @exception IllegalArgumentException <ul>
155 * <li>ERROR_NULL_ARGUMENT - if the parent is null</li>
157 * @exception SWTException <ul>
158 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li>
159 * <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li>
163 * @see Widget#checkSubclass
164 * @see Widget#getStyle
166 public Menu (Menu parentMenu) {
167 this (checkNull (parentMenu).parent, SWT.DROP_DOWN);
171 * Constructs a new instance of this class given its parent
172 * (which must be a <code>MenuItem</code>) and sets the style
173 * for the instance so that the instance will be a drop-down
174 * menu on the given parent's parent menu.
176 * After constructing a drop-down menu, it can be set into its parentItem
177 * using <code>parentItem.setMenu(menu)</code>.
180 * @param parentItem a menu item which will be the parent of the new instance (cannot be null)
182 * @exception IllegalArgumentException <ul>
183 * <li>ERROR_NULL_ARGUMENT - if the parent is null</li>
185 * @exception SWTException <ul>
186 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li>
187 * <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li>
191 * @see Widget#checkSubclass
192 * @see Widget#getStyle
194 public Menu (MenuItem parentItem) {
195 this (checkNull (parentItem).parent);
198 Menu (Decorations parent, int style, long handle) {
199 super (parent, checkStyle (style));
200 this.parent = parent;
201 this.handle = handle;
203 * Bug in IBM JVM 1.3.1. For some reason, when the checkOrientation() is
204 * called from createWidget(), the JVM issues this error:
206 * JVM Exception 0x2 (subcode 0x0) occurred in thread "main" (TID:0x9F19D8)
208 * In addition, on Windows XP, a dialog appears with following error message,
209 * indicating that the problem may be in the JIT:
211 * AppName: java.exe AppVer: 0.0.0.0 ModName: jitc.dll
212 * ModVer: 0.0.0.0 Offset: 000b6912
214 * The fix is to call checkOrientation() from here.
216 checkOrientation (parent);
220 void _setVisible (boolean visible) {
221 if ((style & (SWT.BAR | SWT.DROP_DOWN)) != 0) return;
222 long hwndParent = parent.handle;
224 int flags = OS.TPM_LEFTBUTTON;
225 if (OS.GetKeyState (OS.VK_LBUTTON) >= 0) flags |= OS.TPM_RIGHTBUTTON;
226 if ((style & SWT.RIGHT_TO_LEFT) != 0) flags |= OS.TPM_RIGHTALIGN;
227 if ((parent.style & SWT.MIRRORED) != 0) {
228 flags &= ~OS.TPM_RIGHTALIGN;
229 if ((style & SWT.LEFT_TO_RIGHT) != 0) flags |= OS.TPM_RIGHTALIGN;
233 int pos = OS.GetMessagePos ();
234 nX = OS.GET_X_LPARAM (pos);
235 nY = OS.GET_Y_LPARAM (pos);
238 Display display = this.display;
239 display.sendPreExternalEventDispatchEvent ();
241 * Feature in Windows. It is legal use TrackPopupMenu()
242 * to display an empty menu as long as menu items are added
243 * inside of WM_INITPOPUPMENU. If no items are added, then
244 * TrackPopupMenu() fails and does not send an indication
245 * that the menu has been closed. This is not strictly a
246 * bug but leads to unwanted behavior when application code
247 * assumes that every WM_INITPOPUPMENU will eventually result
248 * in a WM_MENUSELECT, wParam=MAKEWPARAM (0, 0xFFFF), lParam=0 to
249 * indicate that the menu has been closed. The fix is to detect
250 * the case when TrackPopupMenu() fails and the number of items in
251 * the menu is zero and issue a fake WM_MENUSELECT.
253 boolean success = OS.TrackPopupMenu (handle, flags, nX, nY, 0, hwndParent, null);
254 // widget could be disposed at this point
255 display.sendPostExternalEventDispatchEvent ();
256 if (!success && OS.GetMenuItemCount (handle) == 0) {
257 OS.SendMessage (hwndParent, OS.WM_MENUSELECT, OS.MAKEWPARAM (0, 0xFFFF), 0);
260 OS.SendMessage (hwndParent, OS.WM_CANCELMODE, 0, 0);
263 * Bug in Windows. After closing a popup menu, the accessibility focus
264 * is not returned to the focus control. This causes confusion for AT users.
265 * The fix is to explicitly set the accessibility focus back to the focus control.
267 long hFocus = OS.GetFocus();
269 OS.NotifyWinEvent (OS.EVENT_OBJECT_FOCUS, hFocus, OS.OBJID_CLIENT, 0);
274 * Adds the listener to the collection of listeners who will
275 * be notified when help events are generated for the control,
276 * by sending it one of the messages defined in the
277 * <code>HelpListener</code> interface.
279 * @param listener the listener which should be notified
281 * @exception IllegalArgumentException <ul>
282 * <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
284 * @exception SWTException <ul>
285 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
286 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
290 * @see #removeHelpListener
292 public void addHelpListener (HelpListener listener) {
294 if (listener == null) error (SWT.ERROR_NULL_ARGUMENT);
295 TypedListener typedListener = new TypedListener (listener);
296 addListener (SWT.Help, typedListener);
300 * Adds the listener to the collection of listeners who will
301 * be notified when menus are hidden or shown, by sending it
302 * one of the messages defined in the <code>MenuListener</code>
305 * @param listener the listener which should be notified
307 * @exception IllegalArgumentException <ul>
308 * <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
310 * @exception SWTException <ul>
311 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
312 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
316 * @see #removeMenuListener
318 public void addMenuListener (MenuListener listener) {
320 if (listener == null) error (SWT.ERROR_NULL_ARGUMENT);
321 TypedListener typedListener = new TypedListener (listener);
322 addListener (SWT.Hide,typedListener);
323 addListener (SWT.Show,typedListener);
326 static Control checkNull (Control control) {
327 if (control == null) SWT.error (SWT.ERROR_NULL_ARGUMENT);
331 static Menu checkNull (Menu menu) {
332 if (menu == null) SWT.error (SWT.ERROR_NULL_ARGUMENT);
336 static MenuItem checkNull (MenuItem item) {
337 if (item == null) SWT.error (SWT.ERROR_NULL_ARGUMENT);
341 static int checkStyle (int style) {
342 return checkBits (style, SWT.POP_UP, SWT.BAR, SWT.DROP_DOWN, 0, 0, 0);
345 void createHandle () {
346 if (handle != 0) return;
347 if ((style & SWT.BAR) != 0) {
348 handle = OS.CreateMenu ();
350 handle = OS.CreatePopupMenu ();
352 if (handle == 0) error (SWT.ERROR_NO_HANDLES);
355 void createItem (MenuItem item, int index) {
356 int count = OS.GetMenuItemCount (handle);
357 if (!(0 <= index && index <= count)) error (SWT.ERROR_INVALID_RANGE);
358 display.addMenuItem (item);
360 * Bug in Windows. For some reason, when InsertMenuItem()
361 * is used to insert an item without text, it is not possible
362 * to use SetMenuItemInfo() to set the text at a later time.
363 * The fix is to insert the item with some text.
365 * Feature in Windows. When an empty string is used instead
366 * of a space and InsertMenuItem() is used to set a submenu
367 * before setting text to a non-empty string, the menu item
368 * becomes unexpectedly disabled. The fix is to insert a
371 long hHeap = OS.GetProcessHeap ();
372 long pszText = OS.HeapAlloc (hHeap, OS.HEAP_ZERO_MEMORY, 4);
373 OS.MoveMemory (pszText, new char [] {' ', '\0'}, 4);
374 MENUITEMINFO info = new MENUITEMINFO ();
375 info.cbSize = MENUITEMINFO.sizeof;
376 info.fMask = OS.MIIM_ID | OS.MIIM_TYPE | OS.MIIM_DATA;
378 info.dwItemData = item.id;
379 info.fType = item.widgetStyle ();
380 info.dwTypeData = pszText;
381 boolean success = OS.InsertMenuItem (handle, index, true, info);
382 if (pszText != 0) OS.HeapFree (hHeap, 0, pszText);
384 display.removeMenuItem (item);
385 error (SWT.ERROR_ITEM_NOT_ADDED);
390 void createWidget () {
392 * Bug in IBM JVM 1.3.1. For some reason, when the following code is called
393 * from this method, the JVM issues this error:
395 * JVM Exception 0x2 (subcode 0x0) occurred in thread "main" (TID:0x9F19D8)
397 * In addition, on Windows XP, a dialog appears with following error message,
398 * indicating that the problem may be in the JIT:
400 * AppName: java.exe AppVer: 0.0.0.0 ModName: jitc.dll
401 * ModVer: 0.0.0.0 Offset: 000b6912
403 * The fix is to move the code to the caller of this method.
405 // checkOrientation (parent);
407 parent.addMenu (this);
410 int defaultBackground () {
411 return OS.GetSysColor (OS.COLOR_MENU);
414 int defaultForeground () {
415 return OS.GetSysColor (OS.COLOR_MENUTEXT);
418 void destroyAccelerators () {
419 parent.destroyAccelerators ();
422 void destroyItem (MenuItem item) {
423 if (!OS.DeleteMenu (handle, item.id, OS.MF_BYCOMMAND)) {
424 error (SWT.ERROR_ITEM_NOT_REMOVED);
430 void destroyWidget () {
431 MenuItem cascade = this.cascade;
434 if (cascade != null) {
435 cascade.setMenu (null, true);
437 if (hMenu != 0) OS.DestroyMenu (hMenu);
441 void fixMenus (Decorations newParent) {
445 MenuItem [] items = getItems ();
446 for (int i=0; i<items.length; i++) {
447 items [i].fixMenus (newParent);
449 parent.removeMenu (this);
450 newParent.addMenu (this);
451 this.parent = newParent;
455 * Returns the receiver's background color.
457 * @return the background color
459 * @exception SWTException <ul>
460 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
461 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
466 /*public*/ Color getBackground () {
468 return Color.win32_new (display, background != -1 ? background : defaultBackground ());
472 * Returns the receiver's background image.
474 * @return the background image
476 * @exception SWTException <ul>
477 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
478 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
483 /*public*/ Image getBackgroundImage () {
485 return backgroundImage;
489 * Returns a rectangle describing the receiver's size and location
490 * relative to its parent (or its display if its parent is null),
491 * unless the receiver is a menu or a shell. In this case, the
492 * location is relative to the display.
494 * Note that the bounds of a menu or menu item are undefined when
495 * the menu is not visible. This is because most platforms compute
496 * the bounds of a menu dynamically just before it is displayed.
499 * @return the receiver's bounding rectangle
501 * @exception SWTException <ul>
502 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
503 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
508 /*public*/ Rectangle getBounds () {
510 if ((style & SWT.BAR) != 0) {
511 if (parent.menuBar != this) {
512 return new Rectangle (0, 0, 0, 0);
514 long hwndShell = parent.handle;
515 MENUBARINFO info = new MENUBARINFO ();
516 info.cbSize = MENUBARINFO.sizeof;
517 if (OS.GetMenuBarInfo (hwndShell, OS.OBJID_MENU, 0, info)) {
518 int width = info.right - info.left;
519 int height = info.bottom - info.top;
520 return new Rectangle (info.left, info.top, width, height);
523 int count = OS.GetMenuItemCount (handle);
525 RECT rect1 = new RECT ();
526 if (OS.GetMenuItemRect (0, handle, 0, rect1)) {
527 RECT rect2 = new RECT ();
528 if (OS.GetMenuItemRect (0, handle, count - 1, rect2)) {
529 int x = rect1.left - 2, y = rect1.top - 2;
530 int width = (rect2.right - rect2.left) + 4;
531 int height = (rect2.bottom - rect1.top) + 4;
532 return new Rectangle (x, y, width, height);
537 return new Rectangle (0, 0, 0, 0);
541 * Returns the default menu item or null if none has
542 * been previously set.
544 * @return the default menu item.
546 * @exception SWTException <ul>
547 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
548 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
551 public MenuItem getDefaultItem () {
553 int id = OS.GetMenuDefaultItem (handle, OS.MF_BYCOMMAND, OS.GMDI_USEDISABLED);
554 if (id == -1) return null;
555 MENUITEMINFO info = new MENUITEMINFO ();
556 info.cbSize = MENUITEMINFO.sizeof;
557 info.fMask = OS.MIIM_ID;
558 if (OS.GetMenuItemInfo (handle, id, false, info)) {
559 return display.getMenuItem (info.wID);
565 * Returns <code>true</code> if the receiver is enabled, and
566 * <code>false</code> otherwise. A disabled menu is typically
567 * not selectable from the user interface and draws with an
568 * inactive or "grayed" look.
570 * @return the receiver's enabled state
572 * @exception SWTException <ul>
573 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
574 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
579 public boolean getEnabled () {
581 return (state & DISABLED) == 0;
585 * Returns the foreground color that the receiver will use to draw.
587 * @return the receiver's foreground color
589 * @exception SWTException <ul>
590 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
591 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
594 /*public*/ Color getForeground () {
596 return Color.win32_new (display, foreground != -1 ? foreground : defaultForeground ());
600 * Returns the item at the given, zero-relative index in the
601 * receiver. Throws an exception if the index is out of range.
603 * @param index the index of the item to return
604 * @return the item at the given index
606 * @exception IllegalArgumentException <ul>
607 * <li>ERROR_INVALID_RANGE - if the index is not between 0 and the number of elements in the list minus 1 (inclusive)</li>
609 * @exception SWTException <ul>
610 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
611 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
614 public MenuItem getItem (int index) {
617 MENUITEMINFO info = new MENUITEMINFO ();
618 info.cbSize = MENUITEMINFO.sizeof;
619 info.fMask = OS.MIIM_DATA;
620 if (!OS.GetMenuItemInfo (handle, index, true, info)) {
621 error (SWT.ERROR_INVALID_RANGE);
623 id = (int)info.dwItemData;
624 return display.getMenuItem (id);
628 * Returns the number of items contained in the receiver.
630 * @return the number of items
632 * @exception SWTException <ul>
633 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
634 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
637 public int getItemCount () {
639 return OS.GetMenuItemCount (handle);
643 * Returns a (possibly empty) array of <code>MenuItem</code>s which
644 * are the items in the receiver.
646 * Note: This is not the actual structure used by the receiver
647 * to maintain its list of items, so modifying the array will
648 * not affect the receiver.
651 * @return the items in the receiver
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>
658 public MenuItem [] getItems () {
660 int index = 0, count = 0;
661 int length = OS.GetMenuItemCount (handle);
663 int error = OS.GetLastError();
664 SWT.error(SWT.ERROR_CANNOT_GET_COUNT, null, " [GetLastError=0x" + Integer.toHexString(error) + "]");//$NON-NLS-1$ $NON-NLS-2$
666 MenuItem [] items = new MenuItem [length];
667 MENUITEMINFO info = new MENUITEMINFO ();
668 info.cbSize = MENUITEMINFO.sizeof;
669 info.fMask = OS.MIIM_DATA;
670 while (OS.GetMenuItemInfo (handle, index, true, info)) {
671 if (count == items.length) {
672 MenuItem [] newItems = new MenuItem [count + 4];
673 System.arraycopy (items, 0, newItems, 0, count);
676 MenuItem item = display.getMenuItem ((int)info.dwItemData);
677 if (item != null) items [count++] = item;
680 if (count == items.length) return items;
681 MenuItem [] result = new MenuItem [count];
682 System.arraycopy (items, 0, result, 0, count);
687 String getNameText () {
689 MenuItem [] items = getItems ();
690 int length = items.length;
692 for (int i=0; i<=length-1; i++) {
693 result += (items [i] == null ? "null" : items [i].getNameText())
694 + (i < (length - 1) ? ", " : "");
701 * Returns the orientation of the receiver, which will be one of the
702 * constants <code>SWT.LEFT_TO_RIGHT</code> or <code>SWT.RIGHT_TO_LEFT</code>.
704 * @return the orientation style
706 * @exception SWTException <ul>
707 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
708 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
713 public int getOrientation () {
715 return style & (SWT.LEFT_TO_RIGHT | SWT.RIGHT_TO_LEFT);
719 * Returns the receiver's parent, which must be a <code>Decorations</code>.
721 * @return the receiver's parent
723 * @exception SWTException <ul>
724 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
725 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
728 public Decorations getParent () {
734 * Returns the receiver's parent item, which must be a
735 * <code>MenuItem</code> or null when the receiver is a
738 * @return the receiver's parent item
740 * @exception SWTException <ul>
741 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
742 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
745 public MenuItem getParentItem () {
751 * Returns the receiver's parent item, which must be a
752 * <code>Menu</code> or null when the receiver is a
755 * @return the receiver's parent item
757 * @exception SWTException <ul>
758 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
759 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
762 public Menu getParentMenu () {
764 if (cascade != null) return cascade.parent;
769 * Returns the receiver's shell. For all controls other than
770 * shells, this simply returns the control's nearest ancestor
771 * shell. Shells return themselves, even if they are children
772 * of other shells. Returns null if receiver or its ancestor
773 * is the application menubar.
775 * @return the receiver's shell or null
777 * @exception SWTException <ul>
778 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
779 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
784 public Shell getShell () {
786 return parent.getShell ();
790 * Returns <code>true</code> if the receiver is visible, and
791 * <code>false</code> otherwise.
793 * If one of the receiver's ancestors is not visible or some
794 * other condition makes the receiver not visible, this method
795 * may still indicate that it is considered visible even though
796 * it may not actually be showing.
799 * @return the receiver's visibility state
801 * @exception SWTException <ul>
802 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
803 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
806 public boolean getVisible () {
808 if ((style & SWT.BAR) != 0) {
809 return this == parent.menuShell ().menuBar;
811 if ((style & SWT.POP_UP) != 0) {
812 Menu [] popups = display.popups;
813 if (popups == null) return false;
814 for (int i=0; i<popups.length; i++) {
815 if (popups [i] == this) return true;
818 Shell shell = getShell ();
819 Menu menu = shell.activeMenu;
820 while (menu != null && menu != this) {
821 menu = menu.getParentMenu ();
826 void hideCurrentToolTip () {
827 if (this.selectedMenuItem != null) {
828 selectedMenuItem.hideToolTip ();
833 * Searches the receiver's list starting at the first item
834 * (index 0) until an item is found that is equal to the
835 * argument, and returns the index of that item. If no item
836 * is found, returns -1.
838 * @param item the search item
839 * @return the index of the item
841 * @exception IllegalArgumentException <ul>
842 * <li>ERROR_NULL_ARGUMENT - if the item is null</li>
844 * @exception SWTException <ul>
845 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
846 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
849 public int indexOf (MenuItem item) {
851 if (item == null) error (SWT.ERROR_NULL_ARGUMENT);
852 if (item.isDisposed()) error(SWT.ERROR_INVALID_ARGUMENT);
853 if (item.parent != this) return -1;
855 MENUITEMINFO info = new MENUITEMINFO ();
856 info.cbSize = MENUITEMINFO.sizeof;
857 info.fMask = OS.MIIM_DATA;
858 while (OS.GetMenuItemInfo (handle, index, true, info)) {
859 if (info.dwItemData == item.id) return index;
866 * Returns <code>true</code> if the receiver is enabled and all
867 * of the receiver's ancestors are enabled, and <code>false</code>
868 * otherwise. A disabled menu is typically not selectable from the
869 * user interface and draws with an inactive or "grayed" look.
871 * @return the receiver's enabled state
873 * @exception SWTException <ul>
874 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
875 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
880 public boolean isEnabled () {
882 Menu parentMenu = getParentMenu ();
883 if (parentMenu == null) {
884 return getEnabled () && parent.isEnabled ();
886 return getEnabled () && parentMenu.isEnabled ();
890 * Returns <code>true</code> if the receiver is visible and all
891 * of the receiver's ancestors are visible and <code>false</code>
894 * @return the receiver's visibility state
896 * @exception SWTException <ul>
897 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
898 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
903 public boolean isVisible () {
905 return getVisible ();
909 if (!isVisible ()) return;
910 if ((style & SWT.BAR) != 0) {
911 display.addBar (this);
918 void releaseHandle () {
919 super.releaseHandle ();
925 void releaseChildren (boolean destroy) {
926 MenuItem [] items = getItems ();
927 for (int i=0; i<items.length; i++) {
928 MenuItem item = items [i];
929 if (item != null && !item.isDisposed ()) {
930 item.release (false);
933 super.releaseChildren (destroy);
937 void releaseParent () {
938 super.releaseParent ();
939 if ((style & SWT.BAR) != 0) {
940 display.removeBar (this);
941 if (this == parent.menuBar) {
942 parent.setMenuBar (null);
945 if ((style & SWT.POP_UP) != 0) {
946 display.removePopup (this);
952 void releaseWidget () {
953 super.releaseWidget ();
954 backgroundImage = null;
955 if (hBrush != 0) OS.DeleteObject (hBrush);
957 if (parent != null) parent.removeMenu (this);
962 * Removes the listener from the collection of listeners who will
963 * be notified when the help events are generated for the control.
965 * @param listener the listener which should no longer be notified
967 * @exception IllegalArgumentException <ul>
968 * <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
970 * @exception SWTException <ul>
971 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
972 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
976 * @see #addHelpListener
978 public void removeHelpListener (HelpListener listener) {
980 if (listener == null) error (SWT.ERROR_NULL_ARGUMENT);
981 if (eventTable == null) return;
982 eventTable.unhook (SWT.Help, listener);
986 * Removes the listener from the collection of listeners who will
987 * be notified when the menu events are generated for the control.
989 * @param listener the listener which should no longer be notified
991 * @exception IllegalArgumentException <ul>
992 * <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
994 * @exception SWTException <ul>
995 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
996 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1000 * @see #addMenuListener
1002 public void removeMenuListener (MenuListener listener) {
1004 if (listener == null) error (SWT.ERROR_NULL_ARGUMENT);
1005 if (eventTable == null) return;
1006 eventTable.unhook (SWT.Hide, listener);
1007 eventTable.unhook (SWT.Show, listener);
1011 void reskinChildren (int flags) {
1012 MenuItem [] items = getItems ();
1013 for (int i=0; i<items.length; i++) {
1014 MenuItem item = items [i];
1015 item.reskin (flags);
1017 super.reskinChildren (flags);
1021 * Sets the receiver's background color to the color specified
1022 * by the argument, or to the default system color for the control
1023 * if the argument is null.
1025 * @param color the new color (or null)
1027 * @exception IllegalArgumentException <ul>
1028 * <li>ERROR_INVALID_ARGUMENT - if the argument has been disposed</li>
1030 * @exception SWTException <ul>
1031 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1032 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1037 /*public*/ void setBackground (Color color) {
1040 if (color != null) {
1041 if (color.isDisposed()) error(SWT.ERROR_INVALID_ARGUMENT);
1042 pixel = color.handle;
1044 if (pixel == background) return;
1046 updateBackground ();
1050 * Sets the receiver's background image to the image specified
1051 * by the argument, or to the default system color for the control
1052 * if the argument is null. The background image is tiled to fill
1053 * the available space.
1055 * @param image the new image (or null)
1057 * @exception IllegalArgumentException <ul>
1058 * <li>ERROR_INVALID_ARGUMENT - if the argument has been disposed</li>
1059 * <li>ERROR_INVALID_ARGUMENT - if the argument is not a bitmap</li>
1061 * @exception SWTException <ul>
1062 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1063 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1068 /*public*/ void setBackgroundImage (Image image) {
1070 if (image != null) {
1071 if (image.isDisposed ()) error (SWT.ERROR_INVALID_ARGUMENT);
1072 if (image.type != SWT.BITMAP) error (SWT.ERROR_INVALID_ARGUMENT);
1074 if (backgroundImage == image) return;
1075 backgroundImage = image;
1076 updateBackground ();
1080 * Sets the receiver's foreground color to the color specified
1081 * by the argument, or to the default system color for the control
1082 * if the argument is null.
1084 * @param color the new color (or null)
1086 * @exception IllegalArgumentException <ul>
1087 * <li>ERROR_INVALID_ARGUMENT - if the argument has been disposed</li>
1089 * @exception SWTException <ul>
1090 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1091 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1096 /*public*/ void setForeground (Color color) {
1099 if (color != null) {
1100 if (color.isDisposed()) error(SWT.ERROR_INVALID_ARGUMENT);
1101 pixel = color.handle;
1103 if (pixel == foreground) return;
1105 updateForeground ();
1109 * Sets the default menu item to the argument or removes
1110 * the default emphasis when the argument is <code>null</code>.
1112 * @param item the default menu item or null
1114 * @exception IllegalArgumentException <ul>
1115 * <li>ERROR_INVALID_ARGUMENT - if the menu item has been disposed</li>
1117 * @exception SWTException <ul>
1118 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1119 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1122 public void setDefaultItem (MenuItem item) {
1126 if (item.isDisposed()) error(SWT.ERROR_INVALID_ARGUMENT);
1127 if (item.parent != this) return;
1130 int oldID = OS.GetMenuDefaultItem (handle, OS.MF_BYCOMMAND, OS.GMDI_USEDISABLED);
1131 if (newID == oldID) return;
1132 OS.SetMenuDefaultItem (handle, newID, OS.MF_BYCOMMAND);
1137 * Enables the receiver if the argument is <code>true</code>,
1138 * and disables it otherwise. A disabled menu is typically
1139 * not selectable from the user interface and draws with an
1140 * inactive or "grayed" look.
1142 * @param enabled the new enabled state
1144 * @exception SWTException <ul>
1145 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1146 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1149 public void setEnabled (boolean enabled) {
1152 if (!enabled) state |= DISABLED;
1156 * Sets the location of the receiver, which must be a popup,
1157 * to the point specified by the arguments which are relative
1160 * Note that this is different from most widgets where the
1161 * location of the widget is relative to the parent.
1163 * Also note that the actual location of the menu is dependent
1164 * on platform specific behavior. For example: on Linux with
1165 * Wayland this operation is a hint due to lack of global
1169 * @param x the new x coordinate for the receiver
1170 * @param y the new y coordinate for the receiver
1172 * @exception SWTException <ul>
1173 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1174 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1177 public void setLocation (int x, int y) {
1179 setLocationInPixels(DPIUtil.autoScaleUp(x), DPIUtil.autoScaleUp(y));
1182 void setLocationInPixels (int x, int y) {
1183 if ((style & (SWT.BAR | SWT.DROP_DOWN)) != 0) return;
1190 * Sets the location of the receiver, which must be a popup,
1191 * to the point specified by the argument which is relative
1194 * Note that this is different from most widgets where the
1195 * location of the widget is relative to the parent.
1197 * Note that the platform window manager ultimately has control
1198 * over the location of popup menus.
1201 * @param location the new location for the receiver
1203 * @exception IllegalArgumentException <ul>
1204 * <li>ERROR_NULL_ARGUMENT - if the point is null</li>
1206 * @exception SWTException <ul>
1207 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1208 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1213 public void setLocation (Point location) {
1215 if (location == null) error (SWT.ERROR_NULL_ARGUMENT);
1216 location = DPIUtil.autoScaleUp(location);
1217 setLocationInPixels(location.x, location.y);
1221 * Sets the orientation of the receiver, which must be one
1222 * of the constants <code>SWT.LEFT_TO_RIGHT</code> or <code>SWT.RIGHT_TO_LEFT</code>.
1225 * @param orientation new orientation style
1227 * @exception SWTException <ul>
1228 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1229 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1234 public void setOrientation (int orientation) {
1236 if ((style & (SWT.BAR | SWT.DROP_DOWN)) != 0) return;
1237 _setOrientation (orientation);
1240 void _setOrientation (int orientation) {
1241 int flags = SWT.RIGHT_TO_LEFT | SWT.LEFT_TO_RIGHT;
1242 if ((orientation & flags) == 0 || (orientation & flags) == flags) return;
1244 style |= orientation & flags;
1245 style &= ~SWT.FLIP_TEXT_DIRECTION;
1246 MenuItem [] itms = getItems ();
1247 for (int i=0; i<itms.length; i++) {
1248 itms [i].setOrientation (orientation);
1253 * Marks the receiver as visible if the argument is <code>true</code>,
1254 * and marks it invisible otherwise.
1256 * If one of the receiver's ancestors is not visible or some
1257 * other condition makes the receiver not visible, marking
1258 * it visible may not actually cause it to be displayed.
1261 * @param visible the new visibility state
1263 * @exception SWTException <ul>
1264 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1265 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1268 public void setVisible (boolean visible) {
1270 if ((style & (SWT.BAR | SWT.DROP_DOWN)) != 0) return;
1272 display.addPopup (this);
1274 display.removePopup (this);
1275 _setVisible (false);
1280 if ((style & SWT.BAR) != 0) {
1281 if (this == parent.menuBar) OS.DrawMenuBar (parent.handle);
1284 boolean hasCheck = false, hasImage = false;
1285 MenuItem [] items = getItems ();
1286 for (int i=0; i<items.length; i++) {
1287 MenuItem item = items [i];
1288 if (item.image != null) {
1289 if ((hasImage = true) && hasCheck) break;
1291 if ((item.style & (SWT.CHECK | SWT.RADIO)) != 0) {
1292 if ((hasCheck = true) && hasImage) break;
1296 /* Update the menu to hide or show the space for bitmaps */
1297 MENUINFO lpcmi = new MENUINFO ();
1298 lpcmi.cbSize = MENUINFO.sizeof;
1299 lpcmi.fMask = OS.MIM_STYLE;
1300 OS.GetMenuInfo (handle, lpcmi);
1301 if (hasImage && !hasCheck) {
1302 lpcmi.dwStyle |= OS.MNS_CHECKORBMP;
1304 lpcmi.dwStyle &= ~OS.MNS_CHECKORBMP;
1306 OS.SetMenuInfo (handle, lpcmi);
1309 void updateBackground () {
1310 if (hBrush != 0) OS.DeleteObject (hBrush);
1312 if (backgroundImage != null) {
1313 hBrush = OS.CreatePatternBrush (backgroundImage.handle);
1315 if (background != -1) hBrush = OS.CreateSolidBrush (background);
1317 MENUINFO lpcmi = new MENUINFO ();
1318 lpcmi.cbSize = MENUINFO.sizeof;
1319 lpcmi.fMask = OS.MIM_BACKGROUND;
1320 lpcmi.hbrBack = hBrush;
1321 OS.SetMenuInfo (handle, lpcmi);
1324 void updateForeground () {
1325 MENUITEMINFO info = new MENUITEMINFO ();
1326 info.cbSize = MENUITEMINFO.sizeof;
1328 while (OS.GetMenuItemInfo (handle, index, true, info)) {
1329 info.fMask = OS.MIIM_BITMAP;
1330 info.hbmpItem = OS.HBMMENU_CALLBACK;
1331 OS.SetMenuItemInfo (handle, index, true, info);
1337 LRESULT wmTimer (long wParam, long lParam) {
1338 if (wParam == ID_TOOLTIP_TIMER) {
1339 POINT pt = new POINT ();
1340 OS.GetCursorPos (pt);
1341 if (selectedMenuItem != null) {
1342 RECT rect = new RECT ();
1343 boolean success = OS.GetMenuItemRect (0, selectedMenuItem.parent.handle, selectedMenuItem.index, rect);
1344 if (!success) return null;
1345 if (OS.PtInRect (rect, pt)) {
1346 // Mouse cursor is within the bounds of menu item
1347 selectedMenuItem.showTooltip (pt.x, pt.y + OS.GetSystemMetrics(OS.SM_CYCURSOR) / 2 + 5);
1350 * Mouse cursor is outside the bounds of the menu item:
1351 * Keyboard or mnemonic was used to select menu item
1353 selectedMenuItem.showTooltip ((rect.right + rect.left) / 2, rect.bottom + 5);