]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.eclipse.swt.win32.win32.x86_64/src/org/eclipse/swt/widgets/Menu.java
0d55d5aef1371bdb48887e490c93ccbbd09406e4
[simantics/platform.git] / bundles / org.eclipse.swt.win32.win32.x86_64 / src / org / eclipse / swt / widgets / Menu.java
1 /*******************************************************************************
2  * Copyright (c) 2000, 2014 IBM Corporation and others.
3  *
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/
8  *
9  * SPDX-License-Identifier: EPL-2.0
10  *
11  * Contributors:
12  *     IBM Corporation - initial API and implementation
13  *******************************************************************************/
14 package org.eclipse.swt.widgets;
15
16
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.*;
22
23 /**
24  * Instances of this class are user interface objects that contain
25  * menu items.
26  * <dl>
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>
32  * </dl>
33  * <p>
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.
36  * </p><p>
37  * IMPORTANT: This class is <em>not</em> intended to be subclassed.
38  * </p>
39  *
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.
44  */
45 public class Menu extends Widget {
46         /**
47          * the handle to the OS resource
48          * (Warning: This field is platform dependent)
49          * <p>
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.
54          * </p>
55          *
56          * @noreference This field is not intended to be referenced by clients.
57          */
58         public long handle;
59
60         int x, y;
61         long hBrush;
62         int id0, id1;
63         int foreground = -1, background = -1;
64         Image backgroundImage;
65         boolean hasLocation;
66         MenuItem cascade;
67         Decorations parent;
68         MenuItem selectedMenuItem;
69
70         /* Timer ID for MenuItem ToolTip */
71         static final int ID_TOOLTIP_TIMER = 110;
72
73 /**
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.
77  * <p>
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.
81  * </p>
82  *
83  * @param parent a control which will be the parent of the new instance (cannot be null)
84  *
85  * @exception IllegalArgumentException <ul>
86  *    <li>ERROR_NULL_ARGUMENT - if the parent is null</li>
87  * </ul>
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>
91  * </ul>
92  *
93  * @see SWT#POP_UP
94  * @see Widget#checkSubclass
95  * @see Widget#getStyle
96  */
97 public Menu (Control parent) {
98         this (checkNull (parent).menuShell (), SWT.POP_UP);
99 }
100
101 /**
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.
105  * <p>
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.
113  * </p><p>
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>.
116  * </p>
117  *
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
120  *
121  * @exception IllegalArgumentException <ul>
122  *    <li>ERROR_NULL_ARGUMENT - if the parent is null</li>
123  * </ul>
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>
127  * </ul>
128  *
129  * @see SWT#BAR
130  * @see SWT#DROP_DOWN
131  * @see SWT#POP_UP
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
137  */
138 public Menu (Decorations parent, int style) {
139         this (parent, checkStyle (style), 0);
140 }
141
142 /**
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.
147  * <p>
148  * After constructing a drop-down menu, it can be set into its parentMenu
149  * using <code>parentMenu.setMenu(menu)</code>.
150  * </p>
151  *
152  * @param parentMenu a menu which will be the parent of the new instance (cannot be null)
153  *
154  * @exception IllegalArgumentException <ul>
155  *    <li>ERROR_NULL_ARGUMENT - if the parent is null</li>
156  * </ul>
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>
160  * </ul>
161  *
162  * @see SWT#DROP_DOWN
163  * @see Widget#checkSubclass
164  * @see Widget#getStyle
165  */
166 public Menu (Menu parentMenu) {
167         this (checkNull (parentMenu).parent, SWT.DROP_DOWN);
168 }
169
170 /**
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.
175  * <p>
176  * After constructing a drop-down menu, it can be set into its parentItem
177  * using <code>parentItem.setMenu(menu)</code>.
178  * </p>
179  *
180  * @param parentItem a menu item which will be the parent of the new instance (cannot be null)
181  *
182  * @exception IllegalArgumentException <ul>
183  *    <li>ERROR_NULL_ARGUMENT - if the parent is null</li>
184  * </ul>
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>
188  * </ul>
189  *
190  * @see SWT#DROP_DOWN
191  * @see Widget#checkSubclass
192  * @see Widget#getStyle
193  */
194 public Menu (MenuItem parentItem) {
195         this (checkNull (parentItem).parent);
196 }
197
198 Menu (Decorations parent, int style, long handle) {
199         super (parent, checkStyle (style));
200         this.parent = parent;
201         this.handle = handle;
202         /*
203         * Bug in IBM JVM 1.3.1.  For some reason, when the checkOrientation() is
204         * called from createWidget(), the JVM issues this error:
205         *
206         * JVM Exception 0x2 (subcode 0x0) occurred in thread "main" (TID:0x9F19D8)
207         *
208         * In addition, on Windows XP, a dialog appears with following error message,
209         * indicating that the problem may be in the JIT:
210         *
211         * AppName: java.exe      AppVer: 0.0.0.0         ModName: jitc.dll
212         * ModVer: 0.0.0.0        Offset: 000b6912
213         *
214         * The fix is to call checkOrientation() from here.
215         */
216         checkOrientation (parent);
217         createWidget ();
218 }
219
220 void _setVisible (boolean visible) {
221         if ((style & (SWT.BAR | SWT.DROP_DOWN)) != 0) return;
222         long hwndParent = parent.handle;
223         if (visible) {
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;
230                 }
231                 int nX = x, nY = y;
232                 if (!hasLocation) {
233                         int pos = OS.GetMessagePos ();
234                         nX = OS.GET_X_LPARAM (pos);
235                         nY = OS.GET_Y_LPARAM (pos);
236                 }
237                 hasLocation = false;
238                 Display display = this.display;
239                 display.sendPreExternalEventDispatchEvent ();
240                 /*
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.
252                 */
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);
258                 }
259         } else {
260                 OS.SendMessage (hwndParent, OS.WM_CANCELMODE, 0, 0);
261         }
262         /*
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.
266         */
267         long hFocus = OS.GetFocus();
268         if (hFocus != 0) {
269                 OS.NotifyWinEvent (OS.EVENT_OBJECT_FOCUS, hFocus, OS.OBJID_CLIENT, 0);
270         }
271 }
272
273 /**
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.
278  *
279  * @param listener the listener which should be notified
280  *
281  * @exception IllegalArgumentException <ul>
282  *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
283  * </ul>
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>
287  * </ul>
288  *
289  * @see HelpListener
290  * @see #removeHelpListener
291  */
292 public void addHelpListener (HelpListener listener) {
293         checkWidget ();
294         if (listener == null) error (SWT.ERROR_NULL_ARGUMENT);
295         TypedListener typedListener = new TypedListener (listener);
296         addListener (SWT.Help, typedListener);
297 }
298
299 /**
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>
303  * interface.
304  *
305  * @param listener the listener which should be notified
306  *
307  * @exception IllegalArgumentException <ul>
308  *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
309  * </ul>
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>
313  * </ul>
314  *
315  * @see MenuListener
316  * @see #removeMenuListener
317  */
318 public void addMenuListener (MenuListener listener) {
319         checkWidget ();
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);
324 }
325
326 static Control checkNull (Control control) {
327         if (control == null) SWT.error (SWT.ERROR_NULL_ARGUMENT);
328         return control;
329 }
330
331 static Menu checkNull (Menu menu) {
332         if (menu == null) SWT.error (SWT.ERROR_NULL_ARGUMENT);
333         return menu;
334 }
335
336 static MenuItem checkNull (MenuItem item) {
337         if (item == null) SWT.error (SWT.ERROR_NULL_ARGUMENT);
338         return item;
339 }
340
341 static int checkStyle (int style) {
342         return checkBits (style, SWT.POP_UP, SWT.BAR, SWT.DROP_DOWN, 0, 0, 0);
343 }
344
345 void createHandle () {
346         if (handle != 0) return;
347         if ((style & SWT.BAR) != 0) {
348                 handle = OS.CreateMenu ();
349         } else {
350                 handle = OS.CreatePopupMenu ();
351         }
352         if (handle == 0) error (SWT.ERROR_NO_HANDLES);
353 }
354
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);
359         /*
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.
364         *
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
369         * space.
370         */
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;
377         info.wID = item.id;
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);
383         if (!success) {
384                 display.removeMenuItem (item);
385                 error (SWT.ERROR_ITEM_NOT_ADDED);
386         }
387         redraw ();
388 }
389
390 void createWidget () {
391         /*
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:
394         *
395         * JVM Exception 0x2 (subcode 0x0) occurred in thread "main" (TID:0x9F19D8)
396         *
397         * In addition, on Windows XP, a dialog appears with following error message,
398         * indicating that the problem may be in the JIT:
399         *
400         * AppName: java.exe      AppVer: 0.0.0.0         ModName: jitc.dll
401         * ModVer: 0.0.0.0        Offset: 000b6912
402         *
403         * The fix is to move the code to the caller of this method.
404         */
405 //      checkOrientation (parent);
406         createHandle ();
407         parent.addMenu (this);
408 }
409
410 int defaultBackground () {
411         return OS.GetSysColor (OS.COLOR_MENU);
412 }
413
414 int defaultForeground () {
415         return OS.GetSysColor (OS.COLOR_MENUTEXT);
416 }
417
418 void destroyAccelerators () {
419         parent.destroyAccelerators ();
420 }
421
422 void destroyItem (MenuItem item) {
423         if (!OS.DeleteMenu (handle, item.id, OS.MF_BYCOMMAND)) {
424                 error (SWT.ERROR_ITEM_NOT_REMOVED);
425         }
426         redraw ();
427 }
428
429 @Override
430 void destroyWidget () {
431         MenuItem cascade = this.cascade;
432         long hMenu = handle;
433         releaseHandle ();
434         if (cascade != null) {
435                 cascade.setMenu (null, true);
436         } else {
437                 if (hMenu != 0) OS.DestroyMenu (hMenu);
438         }
439 }
440
441 void fixMenus (Decorations newParent) {
442         if (isDisposed()) {
443                 return;
444         }
445         MenuItem [] items = getItems ();
446         for (int i=0; i<items.length; i++) {
447                 items [i].fixMenus (newParent);
448         }
449         parent.removeMenu (this);
450         newParent.addMenu (this);
451         this.parent = newParent;
452 }
453
454 /**
455  * Returns the receiver's background color.
456  *
457  * @return the background color
458  *
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>
462  * </ul>
463  *
464  * @since 3.3
465  */
466 /*public*/ Color getBackground () {
467         checkWidget ();
468         return Color.win32_new (display, background != -1 ? background : defaultBackground ());
469 }
470
471 /**
472  * Returns the receiver's background image.
473  *
474  * @return the background image
475  *
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>
479  * </ul>
480  *
481  * @since 3.3
482  */
483 /*public*/ Image getBackgroundImage () {
484         checkWidget ();
485         return backgroundImage;
486 }
487
488 /**
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.
493  * <p>
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.
497  * </p>
498  *
499  * @return the receiver's bounding rectangle
500  *
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>
504  * </ul>
505  *
506  * @since 3.1
507  */
508 /*public*/ Rectangle getBounds () {
509         checkWidget ();
510         if ((style & SWT.BAR) != 0) {
511                 if (parent.menuBar != this) {
512                         return new Rectangle (0, 0, 0, 0);
513                 }
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);
521                 }
522         } else {
523                 int count = OS.GetMenuItemCount (handle);
524                 if (count != 0) {
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);
533                                 }
534                         }
535                 }
536         }
537         return new Rectangle (0, 0, 0, 0);
538 }
539
540 /**
541  * Returns the default menu item or null if none has
542  * been previously set.
543  *
544  * @return the default menu item.
545  *
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>
549  * </ul>
550  */
551 public MenuItem getDefaultItem () {
552         checkWidget ();
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);
560         }
561         return null;
562 }
563
564 /**
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.
569  *
570  * @return the receiver's enabled state
571  *
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>
575  * </ul>
576  *
577  * @see #isEnabled
578  */
579 public boolean getEnabled () {
580         checkWidget ();
581         return (state & DISABLED) == 0;
582 }
583
584 /**
585  * Returns the foreground color that the receiver will use to draw.
586  *
587  * @return the receiver's foreground color
588  *
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>
592  * </ul>
593  */
594 /*public*/ Color getForeground () {
595         checkWidget ();
596         return Color.win32_new (display, foreground != -1 ? foreground : defaultForeground ());
597 }
598
599 /**
600  * Returns the item at the given, zero-relative index in the
601  * receiver. Throws an exception if the index is out of range.
602  *
603  * @param index the index of the item to return
604  * @return the item at the given index
605  *
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>
608  * </ul>
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>
612  * </ul>
613  */
614 public MenuItem getItem (int index) {
615         checkWidget ();
616         int id = 0;
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);
622         }
623         id = (int)info.dwItemData;
624         return display.getMenuItem (id);
625 }
626
627 /**
628  * Returns the number of items contained in the receiver.
629  *
630  * @return the number of items
631  *
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>
635  * </ul>
636  */
637 public int getItemCount () {
638         checkWidget ();
639         return OS.GetMenuItemCount (handle);
640 }
641
642 /**
643  * Returns a (possibly empty) array of <code>MenuItem</code>s which
644  * are the items in the receiver.
645  * <p>
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.
649  * </p>
650  *
651  * @return the items in the receiver
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 MenuItem [] getItems () {
659         checkWidget ();
660         int index = 0, count = 0;
661         int length = OS.GetMenuItemCount (handle);
662         if (length < 0) {
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$
665         }
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);
674                         items = newItems;
675                 }
676                 MenuItem item = display.getMenuItem ((int)info.dwItemData);
677                 if (item != null) items [count++] = item;
678                 index++;
679         }
680         if (count == items.length) return items;
681         MenuItem [] result = new MenuItem [count];
682         System.arraycopy (items, 0, result, 0, count);
683         return result;
684 }
685
686 @Override
687 String getNameText () {
688         String result = "";
689         MenuItem [] items = getItems ();
690         int length = items.length;
691         if (length > 0) {
692                 for (int i=0; i<=length-1; i++) {
693                         result += (items [i] == null ? "null" : items [i].getNameText())
694                                         + (i < (length - 1) ? ", " : "");
695                 }
696         }
697         return result;
698 }
699
700 /**
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>.
703  *
704  * @return the orientation style
705  *
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>
709  * </ul>
710  *
711  * @since 3.7
712  */
713 public int getOrientation () {
714         checkWidget ();
715         return style & (SWT.LEFT_TO_RIGHT | SWT.RIGHT_TO_LEFT);
716 }
717
718 /**
719  * Returns the receiver's parent, which must be a <code>Decorations</code>.
720  *
721  * @return the receiver's parent
722  *
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>
726  * </ul>
727  */
728 public Decorations getParent () {
729         checkWidget ();
730         return parent;
731 }
732
733 /**
734  * Returns the receiver's parent item, which must be a
735  * <code>MenuItem</code> or null when the receiver is a
736  * root.
737  *
738  * @return the receiver's parent item
739  *
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>
743  * </ul>
744  */
745 public MenuItem getParentItem () {
746         checkWidget ();
747         return cascade;
748 }
749
750 /**
751  * Returns the receiver's parent item, which must be a
752  * <code>Menu</code> or null when the receiver is a
753  * root.
754  *
755  * @return the receiver's parent item
756  *
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>
760  * </ul>
761  */
762 public Menu getParentMenu () {
763         checkWidget ();
764         if (cascade != null) return cascade.parent;
765         return null;
766 }
767
768 /**
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.
774  *
775  * @return the receiver's shell or null
776  *
777  * @exception SWTException <ul>
778  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
779  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
780  * </ul>
781  *
782  * @see #getParent
783  */
784 public Shell getShell () {
785         checkWidget ();
786         return parent.getShell ();
787 }
788
789 /**
790  * Returns <code>true</code> if the receiver is visible, and
791  * <code>false</code> otherwise.
792  * <p>
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.
797  * </p>
798  *
799  * @return the receiver's visibility state
800  *
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>
804  * </ul>
805  */
806 public boolean getVisible () {
807         checkWidget ();
808         if ((style & SWT.BAR) != 0) {
809                 return this == parent.menuShell ().menuBar;
810         }
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;
816                 }
817         }
818         Shell shell = getShell ();
819         Menu menu = shell.activeMenu;
820         while (menu != null && menu != this) {
821                 menu = menu.getParentMenu ();
822         }
823         return this == menu;
824 }
825
826 void hideCurrentToolTip () {
827         if (this.selectedMenuItem != null) {
828                 selectedMenuItem.hideToolTip ();
829         }
830 }
831
832 /**
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.
837  *
838  * @param item the search item
839  * @return the index of the item
840  *
841  * @exception IllegalArgumentException <ul>
842  *    <li>ERROR_NULL_ARGUMENT - if the item is null</li>
843  * </ul>
844  * @exception SWTException <ul>
845  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
846  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
847  * </ul>
848  */
849 public int indexOf (MenuItem item) {
850         checkWidget ();
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;
854         int index = 0;
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;
860                 index++;
861         }
862         return -1;
863 }
864
865 /**
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.
870  *
871  * @return the receiver's enabled state
872  *
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>
876  * </ul>
877  *
878  * @see #getEnabled
879  */
880 public boolean isEnabled () {
881         checkWidget ();
882         Menu parentMenu = getParentMenu ();
883         if (parentMenu == null) {
884                 return getEnabled () && parent.isEnabled ();
885         }
886         return getEnabled () && parentMenu.isEnabled ();
887 }
888
889 /**
890  * Returns <code>true</code> if the receiver is visible and all
891  * of the receiver's ancestors are visible and <code>false</code>
892  * otherwise.
893  *
894  * @return the receiver's visibility state
895  *
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>
899  * </ul>
900  *
901  * @see #getVisible
902  */
903 public boolean isVisible () {
904         checkWidget ();
905         return getVisible ();
906 }
907
908 void redraw () {
909         if (!isVisible ()) return;
910         if ((style & SWT.BAR) != 0) {
911                 display.addBar (this);
912         } else {
913                 update ();
914         }
915 }
916
917 @Override
918 void releaseHandle () {
919         super.releaseHandle ();
920         handle = 0;
921         cascade = null;
922 }
923
924 @Override
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);
931                 }
932         }
933         super.releaseChildren (destroy);
934 }
935
936 @Override
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);
943                 }
944         } else {
945                 if ((style & SWT.POP_UP) != 0) {
946                         display.removePopup (this);
947                 }
948         }
949 }
950
951 @Override
952 void releaseWidget () {
953         super.releaseWidget ();
954         backgroundImage = null;
955         if (hBrush != 0) OS.DeleteObject (hBrush);
956         hBrush = 0;
957         if (parent != null) parent.removeMenu (this);
958         parent = null;
959 }
960
961 /**
962  * Removes the listener from the collection of listeners who will
963  * be notified when the help events are generated for the control.
964  *
965  * @param listener the listener which should no longer be notified
966  *
967  * @exception IllegalArgumentException <ul>
968  *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
969  * </ul>
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>
973  * </ul>
974  *
975  * @see HelpListener
976  * @see #addHelpListener
977  */
978 public void removeHelpListener (HelpListener listener) {
979         checkWidget ();
980         if (listener == null) error (SWT.ERROR_NULL_ARGUMENT);
981         if (eventTable == null) return;
982         eventTable.unhook (SWT.Help, listener);
983 }
984
985 /**
986  * Removes the listener from the collection of listeners who will
987  * be notified when the menu events are generated for the control.
988  *
989  * @param listener the listener which should no longer be notified
990  *
991  * @exception IllegalArgumentException <ul>
992  *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
993  * </ul>
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>
997  * </ul>
998  *
999  * @see MenuListener
1000  * @see #addMenuListener
1001  */
1002 public void removeMenuListener (MenuListener listener) {
1003         checkWidget ();
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);
1008 }
1009
1010 @Override
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);
1016         }
1017         super.reskinChildren (flags);
1018 }
1019
1020 /**
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.
1024  *
1025  * @param color the new color (or null)
1026  *
1027  * @exception IllegalArgumentException <ul>
1028  *    <li>ERROR_INVALID_ARGUMENT - if the argument has been disposed</li>
1029  * </ul>
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>
1033  * </ul>
1034  *
1035  * @since 3.3
1036  */
1037 /*public*/ void setBackground (Color color) {
1038         checkWidget ();
1039         int pixel = -1;
1040         if (color != null) {
1041                 if (color.isDisposed()) error(SWT.ERROR_INVALID_ARGUMENT);
1042                 pixel = color.handle;
1043         }
1044         if (pixel == background) return;
1045         background = pixel;
1046         updateBackground ();
1047 }
1048
1049 /**
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.
1054  *
1055  * @param image the new image (or null)
1056  *
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>
1060  * </ul>
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>
1064  * </ul>
1065  *
1066  * @since 3.3
1067  */
1068 /*public*/ void setBackgroundImage (Image image) {
1069         checkWidget ();
1070         if (image != null) {
1071                 if (image.isDisposed ()) error (SWT.ERROR_INVALID_ARGUMENT);
1072                 if (image.type != SWT.BITMAP) error (SWT.ERROR_INVALID_ARGUMENT);
1073         }
1074         if (backgroundImage == image) return;
1075         backgroundImage = image;
1076         updateBackground ();
1077 }
1078
1079 /**
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.
1083  *
1084  * @param color the new color (or null)
1085  *
1086  * @exception IllegalArgumentException <ul>
1087  *    <li>ERROR_INVALID_ARGUMENT - if the argument has been disposed</li>
1088  * </ul>
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>
1092  * </ul>
1093  *
1094  * @since 3.3
1095  */
1096 /*public*/ void setForeground (Color color) {
1097         checkWidget ();
1098         int pixel = -1;
1099         if (color != null) {
1100                 if (color.isDisposed()) error(SWT.ERROR_INVALID_ARGUMENT);
1101                 pixel = color.handle;
1102         }
1103         if (pixel == foreground) return;
1104         foreground = pixel;
1105         updateForeground ();
1106 }
1107
1108 /**
1109  * Sets the default menu item to the argument or removes
1110  * the default emphasis when the argument is <code>null</code>.
1111  *
1112  * @param item the default menu item or null
1113  *
1114  * @exception IllegalArgumentException <ul>
1115  *    <li>ERROR_INVALID_ARGUMENT - if the menu item has been disposed</li>
1116  * </ul>
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>
1120  * </ul>
1121  */
1122 public void setDefaultItem (MenuItem item) {
1123         checkWidget ();
1124         int newID = -1;
1125         if (item != null) {
1126                 if (item.isDisposed()) error(SWT.ERROR_INVALID_ARGUMENT);
1127                 if (item.parent != this) return;
1128                 newID = item.id;
1129         }
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);
1133         redraw ();
1134 }
1135
1136 /**
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.
1141  *
1142  * @param enabled the new enabled state
1143  *
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>
1147  * </ul>
1148  */
1149 public void setEnabled (boolean enabled) {
1150         checkWidget ();
1151         state &= ~DISABLED;
1152         if (!enabled) state |= DISABLED;
1153 }
1154
1155 /**
1156  * Sets the location of the receiver, which must be a popup,
1157  * to the point specified by the arguments which are relative
1158  * to the display.
1159  * <p>
1160  * Note that this is different from most widgets where the
1161  * location of the widget is relative to the parent.
1162  * </p><p>
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
1166  * coordinates.
1167  * </p>
1168  *
1169  * @param x the new x coordinate for the receiver
1170  * @param y the new y coordinate for the receiver
1171  *
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>
1175  * </ul>
1176  */
1177 public void setLocation (int x, int y) {
1178         checkWidget ();
1179         setLocationInPixels(DPIUtil.autoScaleUp(x), DPIUtil.autoScaleUp(y));
1180 }
1181
1182 void setLocationInPixels (int x, int y) {
1183         if ((style & (SWT.BAR | SWT.DROP_DOWN)) != 0) return;
1184         this.x = x;
1185         this.y = y;
1186         hasLocation = true;
1187 }
1188
1189 /**
1190  * Sets the location of the receiver, which must be a popup,
1191  * to the point specified by the argument which is relative
1192  * to the display.
1193  * <p>
1194  * Note that this is different from most widgets where the
1195  * location of the widget is relative to the parent.
1196  * </p><p>
1197  * Note that the platform window manager ultimately has control
1198  * over the location of popup menus.
1199  * </p>
1200  *
1201  * @param location the new location for the receiver
1202  *
1203  * @exception IllegalArgumentException <ul>
1204  *    <li>ERROR_NULL_ARGUMENT - if the point is null</li>
1205  * </ul>
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>
1209  * </ul>
1210  *
1211  * @since 2.1
1212  */
1213 public void setLocation (Point location) {
1214         checkWidget ();
1215         if (location == null) error (SWT.ERROR_NULL_ARGUMENT);
1216         location = DPIUtil.autoScaleUp(location);
1217         setLocationInPixels(location.x, location.y);
1218 }
1219
1220 /**
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>.
1223  * <p>
1224  *
1225  * @param orientation new orientation style
1226  *
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>
1230  * </ul>
1231  *
1232  * @since 3.7
1233  */
1234 public void setOrientation (int orientation) {
1235         checkWidget ();
1236         if ((style & (SWT.BAR | SWT.DROP_DOWN)) != 0) return;
1237         _setOrientation (orientation);
1238 }
1239
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;
1243         style &= ~flags;
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);
1249         }
1250 }
1251
1252 /**
1253  * Marks the receiver as visible if the argument is <code>true</code>,
1254  * and marks it invisible otherwise.
1255  * <p>
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.
1259  * </p>
1260  *
1261  * @param visible the new visibility state
1262  *
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>
1266  * </ul>
1267  */
1268 public void setVisible (boolean visible) {
1269         checkWidget ();
1270         if ((style & (SWT.BAR | SWT.DROP_DOWN)) != 0) return;
1271         if (visible) {
1272                 display.addPopup (this);
1273         } else {
1274                 display.removePopup (this);
1275                 _setVisible (false);
1276         }
1277 }
1278
1279 void update () {
1280         if ((style & SWT.BAR) != 0) {
1281                 if (this == parent.menuBar) OS.DrawMenuBar (parent.handle);
1282                 return;
1283         }
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;
1290                 }
1291                 if ((item.style & (SWT.CHECK | SWT.RADIO)) != 0) {
1292                         if ((hasCheck = true) && hasImage) break;
1293                 }
1294         }
1295
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;
1303         } else {
1304                 lpcmi.dwStyle &= ~OS.MNS_CHECKORBMP;
1305         }
1306         OS.SetMenuInfo (handle, lpcmi);
1307 }
1308
1309 void updateBackground () {
1310         if (hBrush != 0) OS.DeleteObject (hBrush);
1311         hBrush = 0;
1312         if (backgroundImage != null) {
1313                 hBrush = OS.CreatePatternBrush (backgroundImage.handle);
1314         } else {
1315                 if (background != -1) hBrush = OS.CreateSolidBrush (background);
1316         }
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);
1322 }
1323
1324 void updateForeground () {
1325         MENUITEMINFO info = new MENUITEMINFO ();
1326         info.cbSize = MENUITEMINFO.sizeof;
1327         int index = 0;
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);
1332                 index++;
1333         }
1334         redraw ();
1335 }
1336
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);
1348                         } else {
1349                                 /*
1350                                  * Mouse cursor is outside the bounds of the menu item:
1351                                  * Keyboard or mnemonic was used to select menu item
1352                                  */
1353                                 selectedMenuItem.showTooltip ((rect.right + rect.left) / 2, rect.bottom + 5);
1354                         }
1355                 }
1356         }
1357
1358         return null;
1359 }
1360 }