1 /*******************************************************************************
2  * Copyright (c) 2000, 2017 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 represent a selectable user interface object
25  * that issues notification when pressed and released.
26  * <dl>
27  * <dt><b>Styles:</b></dt>
28  * <dd>CHECK, CASCADE, PUSH, RADIO, SEPARATOR</dd>
29  * <dt><b>Events:</b></dt>
30  * <dd>Arm, Help, Selection</dd>
31  * </dl>
32  * <p>
33  * Note: Only one of the styles CHECK, CASCADE, PUSH, RADIO and SEPARATOR
34  * may be specified.
35  * </p><p>
36  * IMPORTANT: This class is <em>not</em> intended to be subclassed.
37  * </p>
38  *
39  * @see <a href="http://www.eclipse.org/swt/">Sample code and further information</a>
40  * @noextend This class is not intended to be subclassed by clients.
41  */
42 public class MenuItem extends Item {
43 	Menu parent, menu;
44 	long hBitmap;
45 	int id, accelerator, userId, index;
46 	ToolTip itemToolTip;
47 	/* Image margin. */
48 	final static int MARGIN_WIDTH = 1;
49 	final static int MARGIN_HEIGHT = 1;
50 
51 /**
52  * Constructs a new instance of this class given its parent
53  * (which must be a <code>Menu</code>) and a style value
54  * describing its behavior and appearance. The item is added
55  * to the end of the items maintained by its parent.
56  * <p>
57  * The style value is either one of the style constants defined in
58  * class <code>SWT</code> which is applicable to instances of this
59  * class, or must be built by <em>bitwise OR</em>'ing together
60  * (that is, using the <code>int</code> "|" operator) two or more
61  * of those <code>SWT</code> style constants. The class description
62  * lists the style constants that are applicable to the class.
63  * Style bits are also inherited from superclasses.
64  * </p>
65  *
66  * @param parent a menu control which will be the parent of the new instance (cannot be null)
67  * @param style the style of control to construct
68  *
69  * @exception IllegalArgumentException <ul>
70  *    <li>ERROR_NULL_ARGUMENT - if the parent is null</li>
71  * </ul>
72  * @exception SWTException <ul>
73  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li>
74  *    <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li>
75  * </ul>
76  *
77  * @see SWT#CHECK
78  * @see SWT#CASCADE
79  * @see SWT#PUSH
80  * @see SWT#RADIO
81  * @see SWT#SEPARATOR
82  * @see Widget#checkSubclass
83  * @see Widget#getStyle
84  */
MenuItem(Menu parent, int style)85 public MenuItem (Menu parent, int style) {
86 	super (parent, checkStyle (style));
87 	this.parent = parent;
88 	parent.createItem (this, (index = parent.getItemCount ()));
89 }
90 
91 /**
92  * Constructs a new instance of this class given its parent
93  * (which must be a <code>Menu</code>), a style value
94  * describing its behavior and appearance, and the index
95  * at which to place it in the items maintained by its parent.
96  * <p>
97  * The style value is either one of the style constants defined in
98  * class <code>SWT</code> which is applicable to instances of this
99  * class, or must be built by <em>bitwise OR</em>'ing together
100  * (that is, using the <code>int</code> "|" operator) two or more
101  * of those <code>SWT</code> style constants. The class description
102  * lists the style constants that are applicable to the class.
103  * Style bits are also inherited from superclasses.
104  * </p>
105  *
106  * @param parent a menu control which will be the parent of the new instance (cannot be null)
107  * @param style the style of control to construct
108  * @param index the zero-relative index to store the receiver in its parent
109  *
110  * @exception IllegalArgumentException <ul>
111  *    <li>ERROR_NULL_ARGUMENT - if the parent is null</li>
112  *    <li>ERROR_INVALID_RANGE - if the index is not between 0 and the number of elements in the parent (inclusive)</li>
113  * </ul>
114  * @exception SWTException <ul>
115  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li>
116  *    <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li>
117  * </ul>
118  *
119  * @see SWT#CHECK
120  * @see SWT#CASCADE
121  * @see SWT#PUSH
122  * @see SWT#RADIO
123  * @see SWT#SEPARATOR
124  * @see Widget#checkSubclass
125  * @see Widget#getStyle
126  */
MenuItem(Menu parent, int style, int index)127 public MenuItem (Menu parent, int style, int index) {
128 	super (parent, checkStyle (style));
129 	this.parent = parent;
130 	parent.createItem (this, (this.index = index));
131 }
132 
MenuItem(Menu parent, Menu menu, int style, int index)133 MenuItem (Menu parent, Menu menu, int style, int index) {
134 	super (parent, checkStyle (style));
135 	this.parent = parent;
136 	this.menu = menu;
137 	this.index = index;
138 	if (menu != null) menu.cascade = this;
139 	display.addMenuItem (this);
140 }
141 
142 /**
143  * Adds the listener to the collection of listeners who will
144  * be notified when the arm events are generated for the control, by sending
145  * it one of the messages defined in the <code>ArmListener</code>
146  * interface.
147  *
148  * @param listener the listener which should be notified
149  *
150  * @exception IllegalArgumentException <ul>
151  *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
152  * </ul>
153  * @exception SWTException <ul>
154  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
155  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
156  * </ul>
157  *
158  * @see ArmListener
159  * @see #removeArmListener
160  */
addArmListener(ArmListener listener)161 public void addArmListener (ArmListener listener) {
162 	checkWidget ();
163 	if (listener == null) error (SWT.ERROR_NULL_ARGUMENT);
164 	TypedListener typedListener = new TypedListener (listener);
165 	addListener (SWT.Arm, typedListener);
166 }
167 
168 /**
169  * Adds the listener to the collection of listeners who will
170  * be notified when the help events are generated for the control, by sending
171  * it one of the messages defined in the <code>HelpListener</code>
172  * interface.
173  *
174  * @param listener the listener which should be notified
175  *
176  * @exception IllegalArgumentException <ul>
177  *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
178  * </ul>
179  * @exception SWTException <ul>
180  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
181  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
182  * </ul>
183  *
184  * @see HelpListener
185  * @see #removeHelpListener
186  */
addHelpListener(HelpListener listener)187 public void addHelpListener (HelpListener listener) {
188 	checkWidget ();
189 	if (listener == null) error (SWT.ERROR_NULL_ARGUMENT);
190 	TypedListener typedListener = new TypedListener (listener);
191 	addListener (SWT.Help, typedListener);
192 }
193 
194 /**
195  * Adds the listener to the collection of listeners who will
196  * be notified when the menu item is selected by the user, by sending
197  * it one of the messages defined in the <code>SelectionListener</code>
198  * interface.
199  * <p>
200  * When <code>widgetSelected</code> is called, the stateMask field of the event object is valid.
201  * <code>widgetDefaultSelected</code> is not called.
202  * </p>
203  * <p>
204  * When the <code>SWT.RADIO</code> style bit is set, the <code>widgetSelected</code> method is
205  * also called when the receiver loses selection because another item in the same radio group
206  * was selected by the user. During <code>widgetSelected</code> the application can use
207  * <code>getSelection()</code> to determine the current selected state of the receiver.
208  * </p>
209  *
210  * @param listener the listener which should be notified when the menu item is selected by the user
211  *
212  * @exception IllegalArgumentException <ul>
213  *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
214  * </ul>
215  * @exception SWTException <ul>
216  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
217  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
218  * </ul>
219  *
220  * @see SelectionListener
221  * @see #removeSelectionListener
222  * @see SelectionEvent
223  */
addSelectionListener(SelectionListener listener)224 public void addSelectionListener (SelectionListener listener) {
225 	checkWidget ();
226 	if (listener == null) error (SWT.ERROR_NULL_ARGUMENT);
227 	TypedListener typedListener = new TypedListener(listener);
228 	addListener (SWT.Selection,typedListener);
229 	addListener (SWT.DefaultSelection,typedListener);
230 }
231 
232 @Override
checkSubclass()233 protected void checkSubclass () {
234 	if (!isValidSubclass ()) error (SWT.ERROR_INVALID_SUBCLASS);
235 }
236 
checkStyle(int style)237 static int checkStyle (int style) {
238 	return checkBits (style, SWT.PUSH, SWT.CHECK, SWT.RADIO, SWT.SEPARATOR, SWT.CASCADE, 0);
239 }
240 
241 @Override
destroyWidget()242 void destroyWidget () {
243 	parent.destroyItem (this);
244 	releaseHandle ();
245 }
246 
fillAccel(ACCEL accel)247 boolean fillAccel (ACCEL accel) {
248 	accel.cmd = accel.key = accel.fVirt = 0;
249 	if (accelerator == 0 || !getEnabled ()) return false;
250 	if ((accelerator & SWT.COMMAND) != 0) return false;
251 	int fVirt = OS.FVIRTKEY;
252 	int key = accelerator & SWT.KEY_MASK;
253 	int vKey = Display.untranslateKey (key);
254 	if (vKey != 0) {
255 		key = vKey;
256 	} else {
257 		switch (key) {
258 			/*
259 			* Bug in Windows.  For some reason, VkKeyScan
260 			* fails to map ESC to VK_ESCAPE and DEL to
261 			* VK_DELETE.  The fix is to map these keys
262 			* as a special case.
263 			*/
264 			case 27: key = OS.VK_ESCAPE; break;
265 			case 127: key = OS.VK_DELETE; break;
266 			default: {
267 				if (key == 0) return false;
268 				vKey = OS.VkKeyScan ((short) key);
269 				if (vKey == -1) {
270 					if (key != (int)OS.CharUpper ((short) key)) {
271 						fVirt = 0;
272 					}
273 				} else {
274 					key = vKey & 0xFF;
275 				}
276 			}
277 		}
278 	}
279 	accel.key = (short) key;
280 	accel.cmd = (short) id;
281 	accel.fVirt = (byte) fVirt;
282 	if ((accelerator & SWT.ALT) != 0) accel.fVirt |= OS.FALT;
283 	if ((accelerator & SWT.SHIFT) != 0) accel.fVirt |= OS.FSHIFT;
284 	if ((accelerator & SWT.CONTROL) != 0) accel.fVirt |= OS.FCONTROL;
285 	return true;
286 }
287 
fixMenus(Decorations newParent)288 void fixMenus (Decorations newParent) {
289 	if (menu != null && !menu.isDisposed() && !newParent.isDisposed()) menu.fixMenus (newParent);
290 }
291 
292 /**
293  * Returns the widget accelerator.  An accelerator is the bit-wise
294  * OR of zero or more modifier masks and a key. Examples:
295  * <code>SWT.CONTROL | SWT.SHIFT | 'T', SWT.ALT | SWT.F2</code>.
296  * The default value is zero, indicating that the menu item does
297  * not have an accelerator.
298  *
299  * @return the accelerator or 0
300  *
301  * @exception SWTException <ul>
302  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
303  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
304  * </ul>
305  */
getAccelerator()306 public int getAccelerator () {
307 	checkWidget ();
308 	return accelerator;
309 }
310 
311 /**
312  * Returns a rectangle describing the receiver's size and location
313  * relative to its parent (or its display if its parent is null).
314  *
315  * @return the receiver's bounding rectangle
316  *
317  * @exception SWTException <ul>
318  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
319  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
320  * </ul>
321  *
322  * @since 3.1
323  */
getBounds()324 /*public*/ Rectangle getBounds () {
325 	checkWidget ();
326 	int index = parent.indexOf (this);
327 	if (index == -1) return new Rectangle (0, 0, 0, 0);
328 	if ((parent.style & SWT.BAR) != 0) {
329 		Decorations shell = parent.parent;
330 		if (shell.menuBar != parent) {
331 			return new Rectangle (0, 0, 0, 0);
332 		}
333 		long hwndShell = shell.handle;
334 		MENUBARINFO info1 = new MENUBARINFO ();
335 		info1.cbSize = MENUBARINFO.sizeof;
336 		if (!OS.GetMenuBarInfo (hwndShell, OS.OBJID_MENU, 1, info1)) {
337 			return new Rectangle (0, 0, 0, 0);
338 		}
339 		MENUBARINFO info2 = new MENUBARINFO ();
340 		info2.cbSize = MENUBARINFO.sizeof;
341 		if (!OS.GetMenuBarInfo (hwndShell, OS.OBJID_MENU, index + 1, info2)) {
342 			return new Rectangle (0, 0, 0, 0);
343 		}
344 		int x = info2.left - info1.left;
345 		int y = info2.top - info1.top;
346 		int width = info2.right - info2.left;
347 		int height = info2.bottom - info2.top;
348 		return new Rectangle (x, y, width, height);
349 	} else {
350 		long hMenu = parent.handle;
351 		RECT rect1 = new RECT ();
352 		if (!OS.GetMenuItemRect (0, hMenu, 0, rect1)) {
353 			return new Rectangle (0, 0, 0, 0);
354 		}
355 		RECT rect2 = new RECT ();
356 		if (!OS.GetMenuItemRect (0, hMenu, index, rect2)) {
357 			return new Rectangle (0, 0, 0, 0);
358 		}
359 		int x = rect2.left - rect1.left + 2;
360 		int y = rect2.top - rect1.top + 2;
361 		int width = rect2.right - rect2.left;
362 		int height = rect2.bottom - rect2.top;
363 		return new Rectangle (x, y, width, height);
364 	}
365 }
366 
367 /**
368  * Returns <code>true</code> if the receiver is enabled, and
369  * <code>false</code> otherwise. A disabled menu item is typically
370  * not selectable from the user interface and draws with an
371  * inactive or "grayed" look.
372  *
373  * @return the receiver's enabled state
374  *
375  * @exception SWTException <ul>
376  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
377  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
378  * </ul>
379  *
380  * @see #isEnabled
381  */
getEnabled()382 public boolean getEnabled () {
383 	checkWidget ();
384 	/*
385 	* Feature in Windows.  For some reason, when the menu item
386 	* is a separator, GetMenuItemInfo() always indicates that
387 	* the item is not enabled.  The fix is to track the enabled
388 	* state for separators.
389 	*/
390 	if ((style & SWT.SEPARATOR) != 0) {
391 		return (state & DISABLED) == 0;
392 	}
393 	long hMenu = parent.handle;
394 	MENUITEMINFO info = new MENUITEMINFO ();
395 	info.cbSize = MENUITEMINFO.sizeof;
396 	info.fMask = OS.MIIM_STATE;
397 	boolean success = OS.GetMenuItemInfo (hMenu, id, false, info);
398 	if (!success) error (SWT.ERROR_CANNOT_GET_ENABLED);
399 	return (info.fState & (OS.MFS_DISABLED | OS.MFS_GRAYED)) == 0;
400 }
401 
402 /**
403  * Gets the identifier associated with the receiver.
404  *
405  * @return the receiver's identifier
406  *
407  * @exception SWTException <ul>
408  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
409  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
410  * </ul>
411  *
412  * @since 3.7
413  */
getID()414 public int getID () {
415 	checkWidget();
416 	return userId;
417 }
418 
419 /**
420  * Returns the receiver's cascade menu if it has one or null
421  * if it does not. Only <code>CASCADE</code> menu items can have
422  * a pull down menu. The sequence of key strokes, button presses
423  * and/or button releases that are used to request a pull down
424  * menu is platform specific.
425  *
426  * @return the receiver's menu
427  *
428  * @exception SWTException <ul>
429  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
430  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
431  * </ul>
432  */
433 @Override
getMenu()434 public Menu getMenu () {
435 	checkWidget ();
436 	return menu;
437 }
438 
439 @Override
getNameText()440 String getNameText () {
441 	if ((style & SWT.SEPARATOR) != 0) return "|";
442 	return super.getNameText ();
443 }
444 
445 /**
446  * Returns the receiver's parent, which must be a <code>Menu</code>.
447  *
448  * @return the receiver's parent
449  *
450  * @exception SWTException <ul>
451  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
452  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
453  * </ul>
454  */
getParent()455 public Menu getParent () {
456 	checkWidget ();
457 	return parent;
458 }
459 
460 /**
461  * Returns <code>true</code> if the receiver is selected,
462  * and false otherwise.
463  * <p>
464  * When the receiver is of type <code>CHECK</code> or <code>RADIO</code>,
465  * it is selected when it is checked.
466  *
467  * @return the selection state
468  *
469  * @exception SWTException <ul>
470  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
471  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
472  * </ul>
473  */
getSelection()474 public boolean getSelection () {
475 	checkWidget ();
476 	if ((style & (SWT.CHECK | SWT.RADIO)) == 0) return false;
477 	long hMenu = parent.handle;
478 	MENUITEMINFO info = new MENUITEMINFO ();
479 	info.cbSize = MENUITEMINFO.sizeof;
480 	info.fMask = OS.MIIM_STATE;
481 	boolean success = OS.GetMenuItemInfo (hMenu, id, false, info);
482 	if (!success) error (SWT.ERROR_CANNOT_GET_SELECTION);
483 	return (info.fState & OS.MFS_CHECKED) !=0;
484 }
485 
486 /**
487  * Returns the receiver's tool tip text, or null if it has not been set.
488  *
489  * @return the receiver's tool tip text
490  *
491  * @exception SWTException <ul>
492  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
493  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
494  * </ul>
495  *
496  * @since 3.104
497  */
getToolTipText()498 public String getToolTipText () {
499 	checkWidget();
500 	return (itemToolTip == null) ? null : itemToolTip.getMessage();
501 }
502 
hideToolTip()503 void hideToolTip () {
504 	if (itemToolTip == null) return;
505 	itemToolTip.setVisible (false);
506 }
507 
508 /**
509  * Returns <code>true</code> if the receiver is enabled and all
510  * of the receiver's ancestors are enabled, and <code>false</code>
511  * otherwise. A disabled menu item is typically not selectable from the
512  * user interface and draws with an inactive or "grayed" look.
513  *
514  * @return the receiver's enabled state
515  *
516  * @exception SWTException <ul>
517  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
518  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
519  * </ul>
520  *
521  * @see #getEnabled
522  */
isEnabled()523 public boolean isEnabled () {
524 	return getEnabled () && parent.isEnabled ();
525 }
526 
527 @Override
releaseChildren(boolean destroy)528 void releaseChildren (boolean destroy) {
529 	if (menu != null) {
530 		menu.release (false);
531 		menu = null;
532 	}
533 	super.releaseChildren (destroy);
534 }
535 
536 @Override
releaseHandle()537 void releaseHandle () {
538 	super.releaseHandle ();
539 	parent = null;
540 	id = -1;
541 }
542 
543 @Override
releaseParent()544 void releaseParent () {
545 	super.releaseParent ();
546 	if (menu != null) menu.dispose ();
547 	menu = null;
548 }
549 
550 @Override
releaseWidget()551 void releaseWidget () {
552 	super.releaseWidget ();
553 	if (hBitmap != 0) OS.DeleteObject (hBitmap);
554 	hBitmap = 0;
555 	if (accelerator != 0) {
556 		parent.destroyAccelerators ();
557 	}
558 	accelerator = 0;
559 	display.removeMenuItem (this);
560 }
561 
562 /**
563  * Removes the listener from the collection of listeners who will
564  * be notified when the arm events are generated for the control.
565  *
566  * @param listener the listener which should no longer be notified
567  *
568  * @exception IllegalArgumentException <ul>
569  *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
570  * </ul>
571  * @exception SWTException <ul>
572  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
573  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
574  * </ul>
575  *
576  * @see ArmListener
577  * @see #addArmListener
578  */
removeArmListener(ArmListener listener)579 public void removeArmListener (ArmListener listener) {
580 	checkWidget ();
581 	if (listener == null) error (SWT.ERROR_NULL_ARGUMENT);
582 	if (eventTable == null) return;
583 	eventTable.unhook (SWT.Arm, listener);
584 }
585 /**
586  * Removes the listener from the collection of listeners who will
587  * be notified when the help events are generated for the control.
588  *
589  * @param listener the listener which should no longer be notified
590  *
591  * @exception IllegalArgumentException <ul>
592  *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
593  * </ul>
594  * @exception SWTException <ul>
595  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
596  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
597  * </ul>
598  *
599  * @see HelpListener
600  * @see #addHelpListener
601  */
removeHelpListener(HelpListener listener)602 public void removeHelpListener (HelpListener listener) {
603 	checkWidget ();
604 	if (listener == null) error (SWT.ERROR_NULL_ARGUMENT);
605 	if (eventTable == null) return;
606 	eventTable.unhook (SWT.Help, listener);
607 }
608 /**
609  * Removes the listener from the collection of listeners who will
610  * be notified when the control is selected by the user.
611  *
612  * @param listener the listener which should no longer be notified
613  *
614  * @exception IllegalArgumentException <ul>
615  *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
616  * </ul>
617  * @exception SWTException <ul>
618  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
619  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
620  * </ul>
621  *
622  * @see SelectionListener
623  * @see #addSelectionListener
624  */
removeSelectionListener(SelectionListener listener)625 public void removeSelectionListener (SelectionListener listener) {
626 	checkWidget ();
627 	if (listener == null) error (SWT.ERROR_NULL_ARGUMENT);
628 	if (eventTable == null) return;
629 	eventTable.unhook (SWT.Selection, listener);
630 	eventTable.unhook (SWT.DefaultSelection,listener);
631 }
632 
633 
634 @Override
reskinChildren(int flags)635 void reskinChildren (int flags) {
636 	if (menu != null) {
637 		menu.reskin (flags);
638 	}
639 	super.reskinChildren (flags);
640 }
641 
selectRadio()642 void selectRadio () {
643 	int index = 0;
644 	MenuItem [] items = parent.getItems ();
645 	while (index < items.length && items [index] != this) index++;
646 	int i = index - 1;
647 	while (i >= 0 && items [i].setRadioSelection (false)) --i;
648 	int j = index + 1;
649 	while (j < items.length && items [j].setRadioSelection (false)) j++;
650 	setSelection (true);
651 }
652 
653 /**
654  * Sets the widget accelerator.  An accelerator is the bit-wise
655  * OR of zero or more modifier masks and a key. Examples:
656  * <code>SWT.MOD1 | SWT.MOD2 | 'T', SWT.MOD3 | SWT.F2</code>.
657  * <code>SWT.CONTROL | SWT.SHIFT | 'T', SWT.ALT | SWT.F2</code>.
658  * The default value is zero, indicating that the menu item does
659  * not have an accelerator.
660  *
661  * @param accelerator an integer that is the bit-wise OR of masks and a key
662  *
663  * @exception SWTException <ul>
664  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
665  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
666  * </ul>
667  */
setAccelerator(int accelerator)668 public void setAccelerator (int accelerator) {
669 	checkWidget ();
670 	if (this.accelerator == accelerator) return;
671 	this.accelerator = accelerator;
672 	parent.destroyAccelerators ();
673 }
674 
675 /**
676  * Enables the receiver if the argument is <code>true</code>,
677  * and disables it otherwise. A disabled menu item is typically
678  * not selectable from the user interface and draws with an
679  * inactive or "grayed" look.
680  *
681  * @param enabled the new enabled state
682  *
683  * @exception SWTException <ul>
684  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
685  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
686  * </ul>
687  */
setEnabled(boolean enabled)688 public void setEnabled (boolean enabled) {
689 	checkWidget ();
690 	/*
691 	* Feature in Windows.  For some reason, when the menu item
692 	* is a separator, GetMenuItemInfo() always indicates that
693 	* the item is not enabled.  The fix is to track the enabled
694 	* state for separators.
695 	*/
696 	if ((style & SWT.SEPARATOR) != 0) {
697 		if (enabled) {
698 			state &= ~DISABLED;
699 		} else {
700 			state |= DISABLED;
701 		}
702 	}
703 	long hMenu = parent.handle;
704 	MENUITEMINFO info = new MENUITEMINFO ();
705 	info.cbSize = MENUITEMINFO.sizeof;
706 	info.fMask = OS.MIIM_STATE;
707 	boolean success = OS.GetMenuItemInfo (hMenu, id, false, info);
708 	if (!success) {
709 		int error = OS.GetLastError();
710 		SWT.error (SWT.ERROR_CANNOT_SET_ENABLED, null, " [GetLastError=0x" + Integer.toHexString(error) + "]");//$NON-NLS-1$ $NON-NLS-2$
711 	}
712 	int bits = OS.MFS_DISABLED | OS.MFS_GRAYED;
713 	if (enabled) {
714 		if ((info.fState & bits) == 0) return;
715 		info.fState &= ~bits;
716 	} else {
717 		if ((info.fState & bits) == bits) return;
718 		info.fState |= bits;
719 	}
720 	success = OS.SetMenuItemInfo (hMenu, id, false, info);
721 	if (!success) {
722 		/*
723 		* Bug in Windows.  For some reason SetMenuItemInfo(),
724 		* returns a fail code when setting the enabled or
725 		* selected state of a default item, but sets the
726 		* state anyway.  The fix is to ignore the error.
727 		*
728 		* NOTE:  This only happens on Vista.
729 		*/
730 		success = id == OS.GetMenuDefaultItem (hMenu, OS.MF_BYCOMMAND, OS.GMDI_USEDISABLED);
731 		if (!success) {
732 			int error = OS.GetLastError();
733 			SWT.error (SWT.ERROR_CANNOT_SET_ENABLED, null, " [GetLastError=0x" + Integer.toHexString(error) + "]");//$NON-NLS-1$ $NON-NLS-2$
734 		}
735 	}
736 	parent.destroyAccelerators ();
737 	parent.redraw ();
738 }
739 
740 /**
741  * Sets the identifier associated with the receiver to the argument.
742  *
743  * @param id the new identifier. This must be a non-negative value. System-defined identifiers are negative values.
744  *
745  * @exception SWTException <ul>
746  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
747  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
748  *    <li>ERROR_INVALID_ARGUMENT - if called with an negative-valued argument.</li>
749  * </ul>
750  *
751  * @since 3.7
752  */
setID(int id)753 public void setID (int id) {
754 	checkWidget();
755 	if (id < 0) error(SWT.ERROR_INVALID_ARGUMENT);
756 	userId = id;
757 }
758 
759 /**
760  * Sets the receiver's image to the argument, which may be
761  * null indicating that no image should be displayed.
762  * <p>
763  * Note: This operation is a <em>HINT</em> and is not supported on
764  * platforms that do not have this concept (for example, Windows NT).
765  * Furthermore, some platforms (such as GTK2), cannot display both
766  * a check box and an image at the same time.  Instead, they hide
767  * the image and display the check box. Some platforms (such as GTK3)
768  * support images alongside check boxes.
769  * </p>
770  *
771  * @param image the image to display on the receiver (may be null)
772  *
773  * @exception SWTException <ul>
774  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
775  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
776  * </ul>
777  */
778 @Override
setImage(Image image)779 public void setImage (Image image) {
780 	checkWidget ();
781 	if ((style & SWT.SEPARATOR) != 0) return;
782 	super.setImage (image);
783 	MENUITEMINFO info = new MENUITEMINFO ();
784 	info.cbSize = MENUITEMINFO.sizeof;
785 	info.fMask = OS.MIIM_BITMAP;
786 	if (parent.needsMenuCallback()) {
787 		info.hbmpItem = OS.HBMMENU_CALLBACK;
788 	} else {
789 		if (OS.IsAppThemed ()) {
790 			if (hBitmap != 0) OS.DeleteObject (hBitmap);
791 			info.hbmpItem = hBitmap = image != null ? Display.create32bitDIB (image) : 0;
792 		} else {
793 			info.hbmpItem = image != null ? OS.HBMMENU_CALLBACK : 0;
794 		}
795 	}
796 	long hMenu = parent.handle;
797 	OS.SetMenuItemInfo (hMenu, id, false, info);
798 	parent.redraw ();
799 }
800 
801 /**
802  * Sets the receiver's pull down menu to the argument.
803  * Only <code>CASCADE</code> menu items can have a
804  * pull down menu. The sequence of key strokes, button presses
805  * and/or button releases that are used to request a pull down
806  * menu is platform specific.
807  * <p>
808  * Note: Disposing of a menu item that has a pull down menu
809  * will dispose of the menu.  To avoid this behavior, set the
810  * menu to null before the menu item is disposed.
811  * </p>
812  *
813  * @param menu the new pull down menu
814  *
815  * @exception IllegalArgumentException <ul>
816  *    <li>ERROR_MENU_NOT_DROP_DOWN - if the menu is not a drop down menu</li>
817  *    <li>ERROR_MENUITEM_NOT_CASCADE - if the menu item is not a <code>CASCADE</code></li>
818  *    <li>ERROR_INVALID_ARGUMENT - if the menu has been disposed</li>
819  *    <li>ERROR_INVALID_PARENT - if the menu is not in the same widget tree</li>
820  * </ul>
821  * @exception SWTException <ul>
822  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
823  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
824  * </ul>
825  */
setMenu(Menu menu)826 public void setMenu (Menu menu) {
827 	checkWidget ();
828 
829 	/* Check to make sure the new menu is valid */
830 	if ((style & SWT.CASCADE) == 0) {
831 		error (SWT.ERROR_MENUITEM_NOT_CASCADE);
832 	}
833 	if (menu != null) {
834 		if (menu.isDisposed()) error(SWT.ERROR_INVALID_ARGUMENT);
835 		if ((menu.style & SWT.DROP_DOWN) == 0) {
836 			error (SWT.ERROR_MENU_NOT_DROP_DOWN);
837 		}
838 		if (menu.parent != parent.parent) {
839 			error (SWT.ERROR_INVALID_PARENT);
840 		}
841 	}
842 	setMenu (menu, false);
843 }
844 
setMenu(Menu menu, boolean dispose)845 void setMenu (Menu menu, boolean dispose) {
846 
847 	/* Assign the new menu */
848 	Menu oldMenu = this.menu;
849 	if (oldMenu == menu) return;
850 	if (oldMenu != null) oldMenu.cascade = null;
851 	this.menu = menu;
852 
853 	long hMenu = parent.handle;
854 	MENUITEMINFO info = new MENUITEMINFO ();
855 	info.cbSize = MENUITEMINFO.sizeof;
856 	info.fMask = OS.MIIM_DATA;
857 	int index = 0;
858 	while (OS.GetMenuItemInfo (hMenu, index, true, info)) {
859 		if (info.dwItemData == id) break;
860 		index++;
861 	}
862 	if (info.dwItemData != id) return;
863 	int cch = 128;
864 	long hHeap = OS.GetProcessHeap ();
865 	int byteCount = cch * 2;
866 	long pszText = OS.HeapAlloc (hHeap, OS.HEAP_ZERO_MEMORY, byteCount);
867 	info.fMask = OS.MIIM_STATE | OS.MIIM_ID | OS.MIIM_DATA;
868 	/*
869 	* Bug in Windows.  When GetMenuItemInfo() is used to get the text,
870 	* for an item that has a bitmap set using MIIM_BITMAP, the text is
871 	* not returned.  This means that when SetMenuItemInfo() is used to
872 	* set the submenu and the current menu state, the text is lost.
873 	* The fix is use MIIM_BITMAP and MIIM_STRING.
874 	*/
875 	info.fMask |= OS.MIIM_BITMAP | OS.MIIM_STRING;
876 	info.dwTypeData = pszText;
877 	info.cch = cch;
878 	boolean success = OS.GetMenuItemInfo (hMenu, index, true, info);
879 	if (menu != null) {
880 		menu.cascade = this;
881 		info.fMask |= OS.MIIM_SUBMENU;
882 		info.hSubMenu = menu.handle;
883 	}
884 	if (dispose || oldMenu == null) {
885 		success = OS.SetMenuItemInfo (hMenu, index, true, info);
886 	} else {
887 		/*
888 		* Feature in Windows.  When SetMenuItemInfo () is used to
889 		* set a submenu and the menu item already has a submenu,
890 		* Windows destroys the previous menu.  This is undocumented
891 		* and unexpected but not necessarily wrong.  The fix is to
892 		* remove the item with RemoveMenu () which does not destroy
893 		* the submenu and then insert the item with InsertMenuItem ().
894 		*/
895 		OS.RemoveMenu (hMenu, index, OS.MF_BYPOSITION);
896 		success = OS.InsertMenuItem (hMenu, index, true, info);
897 	}
898 	if (pszText != 0) OS.HeapFree (hHeap, 0, pszText);
899 	if (!success) {
900 		int error = OS.GetLastError();
901 		SWT.error (SWT.ERROR_CANNOT_SET_MENU, null, " [GetLastError=0x" + Integer.toHexString(error) + "]");//$NON-NLS-1$ $NON-NLS-2$
902 	}
903 	parent.destroyAccelerators ();
904 }
905 
setRadioSelection(boolean value)906 boolean setRadioSelection (boolean value) {
907 	if ((style & SWT.RADIO) == 0) return false;
908 	if (getSelection () != value) {
909 		setSelection (value);
910 		sendSelectionEvent (SWT.Selection);
911 	}
912 	return true;
913 }
914 
setOrientation(int orientation)915 void setOrientation (int orientation) {
916 	long hMenu = parent.handle;
917 	MENUITEMINFO info = new MENUITEMINFO ();
918 	info.cbSize = MENUITEMINFO.sizeof;
919 	info.fMask = OS.MIIM_FTYPE;
920 	info.fType = widgetStyle ();
921 	OS.SetMenuItemInfo (hMenu, id, false, info);
922 	if (menu != null) menu._setOrientation (orientation);
923 }
924 
925 /**
926  * Sets the selection state of the receiver.
927  * <p>
928  * When the receiver is of type <code>CHECK</code> or <code>RADIO</code>,
929  * it is selected when it is checked.
930  *
931  * @param selected the new selection state
932  *
933  * @exception SWTException <ul>
934  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
935  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
936  * </ul>
937  */
setSelection(boolean selected)938 public void setSelection (boolean selected) {
939 	checkWidget ();
940 	if ((style & (SWT.CHECK | SWT.RADIO)) == 0) return;
941 	long hMenu = parent.handle;
942 	MENUITEMINFO info = new MENUITEMINFO ();
943 	info.cbSize = MENUITEMINFO.sizeof;
944 	info.fMask = OS.MIIM_STATE;
945 	boolean success = OS.GetMenuItemInfo (hMenu, id, false, info);
946 	if (!success) error (SWT.ERROR_CANNOT_SET_SELECTION);
947 	info.fState &= ~OS.MFS_CHECKED;
948 	if (selected) info.fState |= OS.MFS_CHECKED;
949 	success = OS.SetMenuItemInfo (hMenu, id, false, info);
950 	if (!success) {
951 		/*
952 		* Bug in Windows.  For some reason SetMenuItemInfo(),
953 		* returns a fail code when setting the enabled or
954 		* selected state of a default item, but sets the
955 		* state anyway.  The fix is to ignore the error.
956 		*
957 		* NOTE:  This only happens on Vista.
958 		*/
959 		success = id == OS.GetMenuDefaultItem (hMenu, OS.MF_BYCOMMAND, OS.GMDI_USEDISABLED);
960 		if (!success) {
961 			int error = OS.GetLastError();
962 			SWT.error (SWT.ERROR_CANNOT_SET_SELECTION, null, " [GetLastError=0x" + Integer.toHexString(error) + "]");//$NON-NLS-1$ $NON-NLS-2$
963 		}
964 	}
965 	parent.redraw ();
966 }
967 /**
968  * Sets the receiver's text. The string may include
969  * the mnemonic character and accelerator text.
970  * <p>
971  * Mnemonics are indicated by an '&amp;' that causes the next
972  * character to be the mnemonic.  When the user presses a
973  * key sequence that matches the mnemonic, a selection
974  * event occurs. On most platforms, the mnemonic appears
975  * underlined but may be emphasised in a platform specific
976  * manner.  The mnemonic indicator character '&amp;' can be
977  * escaped by doubling it in the string, causing a single
978  * '&amp;' to be displayed.
979  * </p>
980  * <p>
981  * Accelerator text is indicated by the '\t' character.
982  * On platforms that support accelerator text, the text
983  * that follows the '\t' character is displayed to the user,
984  * typically indicating the key stroke that will cause
985  * the item to become selected.  On most platforms, the
986  * accelerator text appears right aligned in the menu.
987  * Setting the accelerator text does not install the
988  * accelerator key sequence. The accelerator key sequence
989  * is installed using #setAccelerator.
990  * </p>
991  *
992  * @param string the new text
993  *
994  * @exception IllegalArgumentException <ul>
995  *    <li>ERROR_NULL_ARGUMENT - if the text is null</li>
996  * </ul>
997  * @exception SWTException <ul>
998  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
999  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1000  * </ul>
1001  *
1002  * @see #setAccelerator
1003  */
1004 @Override
setText(String string)1005 public void setText (String string) {
1006 	checkWidget ();
1007 	if (string == null) error (SWT.ERROR_NULL_ARGUMENT);
1008 	if ((style & SWT.SEPARATOR) != 0) return;
1009 	if (text.equals (string)) return;
1010 	super.setText (string);
1011 	long hHeap = OS.GetProcessHeap ();
1012 	long pszText = 0;
1013 	MENUITEMINFO info = new MENUITEMINFO ();
1014 	info.cbSize = MENUITEMINFO.sizeof;
1015 	long hMenu = parent.handle;
1016 
1017 	TCHAR buffer = new TCHAR (0, string, true);
1018 	int byteCount = buffer.length () * TCHAR.sizeof;
1019 	pszText = OS.HeapAlloc (hHeap, OS.HEAP_ZERO_MEMORY, byteCount);
1020 	OS.MoveMemory (pszText, buffer, byteCount);
1021 	/*
1022 	* Bug in Windows 2000.  For some reason, when MIIM_TYPE is set
1023 	* on a menu item that also has MIIM_BITMAP, the MIIM_TYPE clears
1024 	* the MIIM_BITMAP style.  The fix is to use MIIM_STRING.
1025 	*/
1026 	info.fMask = OS.MIIM_STRING;
1027 	info.dwTypeData = pszText;
1028 	boolean success = OS.SetMenuItemInfo (hMenu, id, false, info);
1029 	if (pszText != 0) OS.HeapFree (hHeap, 0, pszText);
1030 	if (!success) {
1031 		int error = OS.GetLastError();
1032 		SWT.error (SWT.ERROR_CANNOT_SET_TEXT, null, " [GetLastError=0x" + Integer.toHexString(error) + "]");//$NON-NLS-1$ $NON-NLS-2$
1033 	}
1034 	parent.redraw ();
1035 }
1036 
1037 /**
1038  * Sets the receiver's tool tip text to the argument, which
1039  * may be null indicating that the default tool tip for the
1040  * control will be shown. For a menu item that has a default
1041  * tool tip, setting
1042  * the tool tip text to an empty string replaces the default,
1043  * causing no tool tip text to be shown.
1044  * <p>
1045  * The mnemonic indicator (character '&amp;') is not displayed in a tool tip.
1046  * To display a single '&amp;' in the tool tip, the character '&amp;' can be
1047  * escaped by doubling it in the string.
1048  * </p>
1049  * <p>
1050  * NOTE: Tooltips are currently not shown for top-level menu items in the
1051  * {@link Shell#setMenuBar(Menu) shell menubar} on Windows, Mac, and Ubuntu Unity desktop.
1052  * </p>
1053  * <p>
1054  * NOTE: This operation is a hint and behavior is platform specific, on Windows
1055  * for CJK-style mnemonics of the form " (&amp;C)" at the end of the tooltip text
1056  * are not shown in tooltip.
1057  * </p>
1058  * @param toolTip the new tool tip text (or null)
1059  *
1060  * @exception SWTException <ul>
1061  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1062  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1063  * </ul>
1064  *
1065  * @since 3.104
1066  */
setToolTipText(String toolTip)1067 public void setToolTipText (String toolTip) {
1068 	checkWidget ();
1069 
1070 	if (toolTip == null && itemToolTip != null) {
1071 		itemToolTip.setVisible (false);
1072 		itemToolTip = null;
1073 	}
1074 
1075 	if (toolTip == null || toolTip.trim().length() == 0
1076 			|| (itemToolTip != null && toolTip.equals(itemToolTip.getMessage()))) return;
1077 
1078 	itemToolTip = new MenuItemToolTip (this.getParent().getShell());
1079 	itemToolTip.setMessage (toolTip);
1080 	itemToolTip.setVisible (false);
1081 }
1082 
showTooltip(int x, int y)1083 void showTooltip (int x, int y) {
1084 	if (itemToolTip == null) return;
1085 	itemToolTip.setLocationInPixels (x, y);
1086 	itemToolTip.setVisible (true);
1087 }
1088 
widgetStyle()1089 int widgetStyle () {
1090 	int bits = 0;
1091 	Decorations shell = parent.parent;
1092 	if ((shell.style & SWT.MIRRORED) != 0) {
1093 		if ((parent.style & SWT.LEFT_TO_RIGHT) != 0) {
1094 			bits |= OS.MFT_RIGHTJUSTIFY | OS.MFT_RIGHTORDER;
1095 		}
1096 	} else {
1097 		if ((parent.style & SWT.RIGHT_TO_LEFT) != 0) {
1098 			bits |= OS.MFT_RIGHTJUSTIFY | OS.MFT_RIGHTORDER;
1099 		}
1100 	}
1101 	if ((style & SWT.SEPARATOR) != 0) return bits | OS.MFT_SEPARATOR;
1102 	if ((style & SWT.RADIO) != 0) return bits | OS.MFT_RADIOCHECK;
1103 	return bits | OS.MFT_STRING;
1104 }
1105 
wmCommandChild(long wParam, long lParam)1106 LRESULT wmCommandChild (long wParam, long lParam) {
1107 	if ((style & SWT.CHECK) != 0) {
1108 		setSelection (!getSelection ());
1109 	} else {
1110 		if ((style & SWT.RADIO) != 0) {
1111 			if ((parent.getStyle () & SWT.NO_RADIO_GROUP) != 0) {
1112 				setSelection (!getSelection ());
1113 			} else {
1114 				selectRadio ();
1115 			}
1116 		}
1117 	}
1118 	sendSelectionEvent (SWT.Selection);
1119 	return null;
1120 }
1121 
wmDrawChild(long wParam, long lParam)1122 LRESULT wmDrawChild (long wParam, long lParam) {
1123 	DRAWITEMSTRUCT struct = new DRAWITEMSTRUCT ();
1124 	OS.MoveMemory (struct, lParam, DRAWITEMSTRUCT.sizeof);
1125 	if (image != null) {
1126 		GCData data = new GCData();
1127 		data.device = display;
1128 		GC gc = GC.win32_new (struct.hDC, data);
1129 		/*
1130 		* Bug in Windows.  When a bitmap is included in the
1131 		* menu bar, the HDC seems to already include the left
1132 		* coordinate.  The fix is to ignore this value when
1133 		* the item is in a menu bar.
1134 		*/
1135 		int x = (parent.style & SWT.BAR) != 0 ? MARGIN_WIDTH * 2 : struct.left;
1136 		Image image = getEnabled () ? this.image : new Image (display, this.image, SWT.IMAGE_DISABLE);
1137 		gc.drawImage (image, DPIUtil.autoScaleDown(x), DPIUtil.autoScaleDown(struct.top + MARGIN_HEIGHT));
1138 		if (this.image != image) image.dispose ();
1139 		gc.dispose ();
1140 	}
1141 	if (parent.foreground != -1) OS.SetTextColor (struct.hDC, parent.foreground);
1142 	return null;
1143 }
1144 
wmMeasureChild(long wParam, long lParam)1145 LRESULT wmMeasureChild (long wParam, long lParam) {
1146 	MEASUREITEMSTRUCT struct = new MEASUREITEMSTRUCT ();
1147 	OS.MoveMemory (struct, lParam, MEASUREITEMSTRUCT.sizeof);
1148 
1149 	if ((parent.style & SWT.BAR) != 0) {
1150 		if (parent.needsMenuCallback()) {
1151 			/*
1152 			 * Weirdness in Windows. Setting `HBMMENU_CALLBACK` causes
1153 			 * item sizes to mean something else. It seems that it is
1154 			 * the size of left margin before the text. At the same time,
1155 			 * if menu item has a mnemonic, it's always drawn at a fixed
1156 			 * position. I have tested on Win7, Win8.1, Win10 and found
1157 			 * that value of 5 works well in matching text to mnemonic.
1158 			 * NOTE: autoScaleUpUsingNativeDPI() is used to avoid problems
1159 			 * with applications that disable automatic scaling.
1160 			 */
1161 			struct.itemWidth = DPIUtil.autoScaleUpUsingNativeDPI(5);
1162 			OS.MoveMemory (lParam, struct, MEASUREITEMSTRUCT.sizeof);
1163 			return null;
1164 		}
1165 	}
1166 
1167 	int width = 0, height = 0;
1168 	if (image != null) {
1169 		Rectangle rect = image.getBoundsInPixels ();
1170 		width = rect.width;
1171 		height = rect.height;
1172 	} else {
1173 		/*
1174 		* Bug in Windows.  If a menu contains items that have
1175 		* images and can be checked, Windows does not include
1176 		* the width of the image and the width of the check when
1177 		* computing the width of the menu.  When the longest item
1178 		* does not have an image, the label and the accelerator
1179 		* text can overlap.  The fix is to use SetMenuItemInfo()
1180 		* to indicate that all items have a bitmap and then include
1181 		* the width of the widest bitmap in WM_MEASURECHILD.
1182 		*/
1183 		MENUINFO lpcmi = new MENUINFO ();
1184 		lpcmi.cbSize = MENUINFO.sizeof;
1185 		lpcmi.fMask = OS.MIM_STYLE;
1186 		long hMenu = parent.handle;
1187 		OS.GetMenuInfo (hMenu, lpcmi);
1188 		if ((lpcmi.dwStyle & OS.MNS_CHECKORBMP) == 0) {
1189 			for (MenuItem item : parent.getItems ()) {
1190 				if (item.image != null) {
1191 					Rectangle rect = item.image.getBoundsInPixels ();
1192 					width = Math.max (width, rect.width);
1193 				}
1194 			}
1195 		}
1196 	}
1197 	if (width != 0 || height != 0) {
1198 		struct.itemWidth = width + MARGIN_WIDTH * 2;
1199 		struct.itemHeight = height + MARGIN_HEIGHT * 2;
1200 		OS.MoveMemory (lParam, struct, MEASUREITEMSTRUCT.sizeof);
1201 	}
1202 	return null;
1203 }
1204 
1205 private static final class MenuItemToolTip extends ToolTip {
1206 
MenuItemToolTip(Shell parent)1207 	public MenuItemToolTip(Shell parent) {
1208 		super(parent, 0);
1209 	}
1210 
1211 	@Override
hwndToolTip()1212 	long hwndToolTip() {
1213 		return parent.menuItemToolTipHandle();
1214 	}
1215 
1216 }
1217 
1218 }
1219