]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.eclipse.swt.win32.win32.x86_64/src/org/eclipse/swt/widgets/MenuItem.java
Remove invalid SHA-256-Digests
[simantics/platform.git] / bundles / org.eclipse.swt.win32.win32.x86_64 / src / org / eclipse / swt / widgets / MenuItem.java
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  */
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  */
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
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  */
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  */
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  */
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
233 protected void checkSubclass () {
234         if (!isValidSubclass ()) error (SWT.ERROR_INVALID_SUBCLASS);
235 }
236
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
242 void destroyWidget () {
243         parent.destroyItem (this);
244         releaseHandle ();
245 }
246
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
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  */
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  */
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  */
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  */
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
434 public Menu getMenu () {
435         checkWidget ();
436         return menu;
437 }
438
439 @Override
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  */
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  */
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  */
498 public String getToolTipText () {
499         checkWidget();
500         return (itemToolTip == null) ? null : itemToolTip.getMessage();
501 }
502
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  */
523 public boolean isEnabled () {
524         return getEnabled () && parent.isEnabled ();
525 }
526
527 @Override
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
537 void releaseHandle () {
538         super.releaseHandle ();
539         parent = null;
540         id = -1;
541 }
542
543 @Override
544 void releaseParent () {
545         super.releaseParent ();
546         if (menu != null) menu.dispose ();
547         menu = null;
548 }
549
550 @Override
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  */
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  */
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  */
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
635 void reskinChildren (int flags) {
636         if (menu != null) {
637                 menu.reskin (flags);
638         }
639         super.reskinChildren (flags);
640 }
641
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  */
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  */
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  */
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
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.foreground != -1) {
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  */
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
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
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
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  */
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
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         /* Use the character encoding for the default locale */
1018         TCHAR buffer = new TCHAR (0, string, true);
1019         int byteCount = buffer.length () * TCHAR.sizeof;
1020         pszText = OS.HeapAlloc (hHeap, OS.HEAP_ZERO_MEMORY, byteCount);
1021         OS.MoveMemory (pszText, buffer, byteCount);
1022         /*
1023         * Bug in Windows 2000.  For some reason, when MIIM_TYPE is set
1024         * on a menu item that also has MIIM_BITMAP, the MIIM_TYPE clears
1025         * the MIIM_BITMAP style.  The fix is to use MIIM_STRING.
1026         */
1027         info.fMask = OS.MIIM_STRING;
1028         info.dwTypeData = pszText;
1029         boolean success = OS.SetMenuItemInfo (hMenu, id, false, info);
1030         if (pszText != 0) OS.HeapFree (hHeap, 0, pszText);
1031         if (!success) {
1032                 int error = OS.GetLastError();
1033                 SWT.error (SWT.ERROR_CANNOT_SET_TEXT, null, " [GetLastError=0x" + Integer.toHexString(error) + "]");//$NON-NLS-1$ $NON-NLS-2$
1034         }
1035         parent.redraw ();
1036 }
1037
1038 /**
1039  * Sets the receiver's tool tip text to the argument, which
1040  * may be null indicating that the default tool tip for the
1041  * control will be shown. For a menu item that has a default
1042  * tool tip, setting
1043  * the tool tip text to an empty string replaces the default,
1044  * causing no tool tip text to be shown.
1045  * <p>
1046  * The mnemonic indicator (character '&amp;') is not displayed in a tool tip.
1047  * To display a single '&amp;' in the tool tip, the character '&amp;' can be
1048  * escaped by doubling it in the string.
1049  * </p>
1050  * <p>
1051  * NOTE: Tooltips are currently not shown for top-level menu items in the
1052  * {@link Shell#setMenuBar(Menu) shell menubar} on Windows, Mac, and Ubuntu Unity desktop.
1053  * </p>
1054  * <p>
1055  * NOTE: This operation is a hint and behavior is platform specific, on Windows
1056  * for CJK-style mnemonics of the form " (&amp;C)" at the end of the tooltip text
1057  * are not shown in tooltip.
1058  * </p>
1059  * @param toolTip the new tool tip text (or null)
1060  *
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.104
1067  */
1068 public void setToolTipText (String toolTip) {
1069         checkWidget ();
1070
1071         if (toolTip == null && itemToolTip != null) {
1072                 itemToolTip.setVisible (false);
1073                 itemToolTip = null;
1074         }
1075
1076         if (toolTip == null || toolTip.trim().length() == 0
1077                         || (itemToolTip != null && toolTip.equals(itemToolTip.getMessage()))) return;
1078
1079         itemToolTip = new MenuItemToolTip (this.getParent().getShell());
1080         itemToolTip.setMessage (toolTip);
1081         itemToolTip.setVisible (false);
1082 }
1083
1084 void showTooltip (int x, int y) {
1085         if (itemToolTip == null) return;
1086         itemToolTip.setLocationInPixels (x, y);
1087         itemToolTip.setVisible (true);
1088 }
1089
1090 int widgetStyle () {
1091         int bits = 0;
1092         Decorations shell = parent.parent;
1093         if ((shell.style & SWT.MIRRORED) != 0) {
1094                 if ((parent.style & SWT.LEFT_TO_RIGHT) != 0) {
1095                         bits |= OS.MFT_RIGHTJUSTIFY | OS.MFT_RIGHTORDER;
1096                 }
1097         } else {
1098                 if ((parent.style & SWT.RIGHT_TO_LEFT) != 0) {
1099                         bits |= OS.MFT_RIGHTJUSTIFY | OS.MFT_RIGHTORDER;
1100                 }
1101         }
1102         if ((style & SWT.SEPARATOR) != 0) return bits | OS.MFT_SEPARATOR;
1103         if ((style & SWT.RADIO) != 0) return bits | OS.MFT_RADIOCHECK;
1104         return bits | OS.MFT_STRING;
1105 }
1106
1107 LRESULT wmCommandChild (long wParam, long lParam) {
1108         if ((style & SWT.CHECK) != 0) {
1109                 setSelection (!getSelection ());
1110         } else {
1111                 if ((style & SWT.RADIO) != 0) {
1112                         if ((parent.getStyle () & SWT.NO_RADIO_GROUP) != 0) {
1113                                 setSelection (!getSelection ());
1114                         } else {
1115                                 selectRadio ();
1116                         }
1117                 }
1118         }
1119         sendSelectionEvent (SWT.Selection);
1120         return null;
1121 }
1122
1123 LRESULT wmDrawChild (long wParam, long lParam) {
1124         DRAWITEMSTRUCT struct = new DRAWITEMSTRUCT ();
1125         OS.MoveMemory (struct, lParam, DRAWITEMSTRUCT.sizeof);
1126         if (image != null) {
1127                 GCData data = new GCData();
1128                 data.device = display;
1129                 GC gc = GC.win32_new (struct.hDC, data);
1130                 /*
1131                 * Bug in Windows.  When a bitmap is included in the
1132                 * menu bar, the HDC seems to already include the left
1133                 * coordinate.  The fix is to ignore this value when
1134                 * the item is in a menu bar.
1135                 */
1136                 int x = (parent.style & SWT.BAR) != 0 ? MARGIN_WIDTH * 2 : struct.left;
1137                 Image image = getEnabled () ? this.image : new Image (display, this.image, SWT.IMAGE_DISABLE);
1138                 gc.drawImage (image, DPIUtil.autoScaleDown(x), DPIUtil.autoScaleDown(struct.top + MARGIN_HEIGHT));
1139                 if (this.image != image) image.dispose ();
1140                 gc.dispose ();
1141         }
1142         if (parent.foreground != -1) OS.SetTextColor (struct.hDC, parent.foreground);
1143         return null;
1144 }
1145
1146 LRESULT wmMeasureChild (long wParam, long lParam) {
1147         MEASUREITEMSTRUCT struct = new MEASUREITEMSTRUCT ();
1148         OS.MoveMemory (struct, lParam, MEASUREITEMSTRUCT.sizeof);
1149         int width = 0, height = 0;
1150         if (image != null) {
1151                 Rectangle rect = image.getBoundsInPixels ();
1152                 width = rect.width;
1153                 height = rect.height;
1154         } else {
1155                 /*
1156                 * Bug in Windows.  If a menu contains items that have
1157                 * images and can be checked, Windows does not include
1158                 * the width of the image and the width of the check when
1159                 * computing the width of the menu.  When the longest item
1160                 * does not have an image, the label and the accelerator
1161                 * text can overlap.  The fix is to use SetMenuItemInfo()
1162                 * to indicate that all items have a bitmap and then include
1163                 * the width of the widest bitmap in WM_MEASURECHILD.
1164                 */
1165                 MENUINFO lpcmi = new MENUINFO ();
1166                 lpcmi.cbSize = MENUINFO.sizeof;
1167                 lpcmi.fMask = OS.MIM_STYLE;
1168                 long hMenu = parent.handle;
1169                 OS.GetMenuInfo (hMenu, lpcmi);
1170                 if ((lpcmi.dwStyle & OS.MNS_CHECKORBMP) == 0) {
1171                         MenuItem [] items = parent.getItems ();
1172                         for (int i=0; i<items.length; i++) {
1173                                 MenuItem item = items [i];
1174                                 if (item.image != null) {
1175                                         Rectangle rect = item.image.getBoundsInPixels ();
1176                                         width = Math.max (width, rect.width);
1177                                 }
1178                         }
1179                 }
1180         }
1181         if (width != 0 || height != 0) {
1182                 struct.itemWidth = width + MARGIN_WIDTH * 2;
1183                 struct.itemHeight = height + MARGIN_HEIGHT * 2;
1184                 OS.MoveMemory (lParam, struct, MEASUREITEMSTRUCT.sizeof);
1185         }
1186         return null;
1187 }
1188
1189 private static final class MenuItemToolTip extends ToolTip {
1190
1191         public MenuItemToolTip(Shell parent) {
1192                 super(parent, 0);
1193         }
1194
1195         @Override
1196         long hwndToolTip() {
1197                 return parent.menuItemToolTipHandle();
1198         }
1199
1200 }
1201
1202 }