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 '&' 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 '&' can be
977 * escaped by doubling it in the string, causing a single
978 * '&' 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 '&') is not displayed in a tool tip.
1046 * To display a single '&' in the tool tip, the character '&' 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 " (&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