root/dwt/widgets/MenuItem.d

Revision 263:27244095ce14, 43.1 kB (checked in by Frank Benoit <benoit@tionex.de>, 4 months ago)

Fix struct sizes, based on a comparison to the values seen by C apps.

Line 
1 /*******************************************************************************
2  * Copyright (c) 2000, 2008 IBM Corporation and others.
3  * All rights reserved. This program and the accompanying materials
4  * are made available under the terms of the Eclipse Public License v1.0
5  * which accompanies this distribution, and is available at
6  * http://www.eclipse.org/legal/epl-v10.html
7  *
8  * Contributors:
9  *     IBM Corporation - initial API and implementation
10  * Port to the D programming language:
11  *     Frank Benoit <benoit@tionex.de>
12  *******************************************************************************/
13 module dwt.widgets.MenuItem;
14
15 import dwt.DWT;
16 import dwt.DWTException;
17 import dwt.events.ArmListener;
18 import dwt.events.HelpListener;
19 import dwt.events.SelectionEvent;
20 import dwt.events.SelectionListener;
21 import dwt.graphics.GC;
22 import dwt.graphics.GCData;
23 import dwt.graphics.Image;
24 import dwt.graphics.Rectangle;
25 import dwt.internal.win32.OS;
26
27 import dwt.widgets.Item;
28 import dwt.widgets.Widget;
29 import dwt.widgets.Menu;
30 import dwt.widgets.Decorations;
31 import dwt.widgets.TypedListener;
32 import dwt.widgets.Display;
33 import dwt.widgets.Event;
34
35 import dwt.dwthelper.utils;
36
37 /**
38  * Instances of this class represent a selectable user interface object
39  * that issues notification when pressed and released.
40  * <dl>
41  * <dt><b>Styles:</b></dt>
42  * <dd>CHECK, CASCADE, PUSH, RADIO, SEPARATOR</dd>
43  * <dt><b>Events:</b></dt>
44  * <dd>Arm, Help, Selection</dd>
45  * </dl>
46  * <p>
47  * Note: Only one of the styles CHECK, CASCADE, PUSH, RADIO and SEPARATOR
48  * may be specified.
49  * </p><p>
50  * IMPORTANT: This class is <em>not</em> intended to be subclassed.
51  * </p>
52  *
53  * @see <a href="http://www.eclipse.org/swt/">Sample code and further information</a>
54  */
55
56 public class MenuItem : Item {
57     Menu parent, menu;
58     HBITMAP hBitmap;
59     int id, accelerator;
60     /*
61     * Feature in Windows.  On Windows 98, it is necessary
62     * to add 4 pixels to the width of the image or the image
63     * and text are too close.  On other Windows platforms,
64     * this causes the text of the longest item to touch the
65     * accelerator text.  The fix is to use smaller margins
66     * everywhere but on Windows 98.
67     */
68     private static int MARGIN_WIDTH_;
69     public static int MARGIN_WIDTH(){
70         assert( static_this_completed );
71         return MARGIN_WIDTH_;
72     }
73     private static int MARGIN_HEIGHT_;
74     public static int MARGIN_HEIGHT(){
75         assert( static_this_completed );
76         return MARGIN_HEIGHT_;
77     }
78
79     private static bool static_this_completed = false;
80     private static void static_this() {
81         if( static_this_completed ){
82             return;
83         }
84         synchronized {
85             if( static_this_completed ){
86                 return;
87             }
88             MARGIN_WIDTH_ = OS.IsWin95 ? 2 : 1;
89             MARGIN_HEIGHT_ = OS.IsWin95 ? 2 : 1;
90             static_this_completed = true;
91         }
92     }
93
94 /**
95  * Constructs a new instance of this class given its parent
96  * (which must be a <code>Menu</code>) and a style value
97  * describing its behavior and appearance. The item is added
98  * to the end of the items maintained by its parent.
99  * <p>
100  * The style value is either one of the style constants defined in
101  * class <code>DWT</code> which is applicable to instances of this
102  * class, or must be built by <em>bitwise OR</em>'ing together
103  * (that is, using the <code>int</code> "|" operator) two or more
104  * of those <code>DWT</code> style constants. The class description
105  * lists the style constants that are applicable to the class.
106  * Style bits are also inherited from superclasses.
107  * </p>
108  *
109  * @param parent a menu control which will be the parent of the new instance (cannot be null)
110  * @param style the style of control to construct
111  *
112  * @exception IllegalArgumentException <ul>
113  *    <li>ERROR_NULL_ARGUMENT - if the parent is null</li>
114  * </ul>
115  * @exception DWTException <ul>
116  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li>
117  *    <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li>
118  * </ul>
119  *
120  * @see DWT#CHECK
121  * @see DWT#CASCADE
122  * @see DWT#PUSH
123  * @see DWT#RADIO
124  * @see DWT#SEPARATOR
125  * @see Widget#checkSubclass
126  * @see Widget#getStyle
127  */
128 public this (Menu parent, int style) {
129     static_this();
130     super (parent, checkStyle (style));
131     this.parent = parent;
132     parent.createItem (this, parent.getItemCount ());
133 }
134
135 /**
136  * Constructs a new instance of this class given its parent
137  * (which must be a <code>Menu</code>), a style value
138  * describing its behavior and appearance, and the index
139  * at which to place it in the items maintained by its parent.
140  * <p>
141  * The style value is either one of the style constants defined in
142  * class <code>DWT</code> which is applicable to instances of this
143  * class, or must be built by <em>bitwise OR</em>'ing together
144  * (that is, using the <code>int</code> "|" operator) two or more
145  * of those <code>DWT</code> style constants. The class description
146  * lists the style constants that are applicable to the class.
147  * Style bits are also inherited from superclasses.
148  * </p>
149  *
150  * @param parent a menu control which will be the parent of the new instance (cannot be null)
151  * @param style the style of control to construct
152  * @param index the zero-relative index to store the receiver in its parent
153  *
154  * @exception IllegalArgumentException <ul>
155  *    <li>ERROR_NULL_ARGUMENT - if the parent is null</li>
156  *    <li>ERROR_INVALID_RANGE - if the index is not between 0 and the number of elements in the parent (inclusive)</li>
157  * </ul>
158  * @exception DWTException <ul>
159  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li>
160  *    <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li>
161  * </ul>
162  *
163  * @see DWT#CHECK
164  * @see DWT#CASCADE
165  * @see DWT#PUSH
166  * @see DWT#RADIO
167  * @see DWT#SEPARATOR
168  * @see Widget#checkSubclass
169  * @see Widget#getStyle
170  */
171 public this (Menu parent, int style, int index) {
172     static_this();
173     super (parent, checkStyle (style));
174     this.parent = parent;
175     parent.createItem (this, index);
176 }
177
178 this (Menu parent, Menu menu, int style, int index) {
179     static_this();
180     super (parent, checkStyle (style));
181     this.parent = parent;
182     this.menu = menu;
183     if (menu !is null) menu.cascade = this;
184     display.addMenuItem (this);
185 }
186
187 /**
188  * Adds the listener to the collection of listeners who will
189  * be notified when the arm events are generated for the control, by sending
190  * it one of the messages defined in the <code>ArmListener</code>
191  * interface.
192  *
193  * @param listener the listener which should be notified
194  *
195  * @exception IllegalArgumentException <ul>
196  *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
197  * </ul>
198  * @exception DWTException <ul>
199  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
200  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
201  * </ul>
202  *
203  * @see ArmListener
204  * @see #removeArmListener
205  */
206 public void addArmListener (ArmListener listener) {
207     checkWidget ();
208     if (listener is null) error (DWT.ERROR_NULL_ARGUMENT);
209     TypedListener typedListener = new TypedListener (listener);
210     addListener (DWT.Arm, typedListener);
211 }
212
213 /**
214  * Adds the listener to the collection of listeners who will
215  * be notified when the help events are generated for the control, by sending
216  * it one of the messages defined in the <code>HelpListener</code>
217  * interface.
218  *
219  * @param listener the listener which should be notified
220  *
221  * @exception IllegalArgumentException <ul>
222  *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
223  * </ul>
224  * @exception DWTException <ul>
225  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
226  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
227  * </ul>
228  *
229  * @see HelpListener
230  * @see #removeHelpListener
231  */
232 public void addHelpListener (HelpListener listener) {
233     checkWidget ();
234     if (listener is null) error (DWT.ERROR_NULL_ARGUMENT);
235     TypedListener typedListener = new TypedListener (listener);
236     addListener (DWT.Help, typedListener);
237 }
238
239 /**
240  * Adds the listener to the collection of listeners who will
241  * be notified when the menu item is selected by the user, by sending
242  * it one of the messages defined in the <code>SelectionListener</code>
243  * interface.
244  * <p>
245  * When <code>widgetSelected</code> is called, the stateMask field of the event object is valid.
246  * <code>widgetDefaultSelected</code> is not called.
247  * </p>
248  *
249  * @param listener the listener which should be notified when the menu item is selected by the user
250  *
251  * @exception IllegalArgumentException <ul>
252  *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
253  * </ul>
254  * @exception DWTException <ul>
255  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
256  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
257  * </ul>
258  *
259  * @see SelectionListener
260  * @see #removeSelectionListener
261  * @see SelectionEvent
262  */
263 public void addSelectionListener (SelectionListener listener) {
264     checkWidget ();
265     if (listener is null) error (DWT.ERROR_NULL_ARGUMENT);
266     TypedListener typedListener = new TypedListener(listener);
267     addListener (DWT.Selection,typedListener);
268     addListener (DWT.DefaultSelection,typedListener);
269 }
270
271 override protected void checkSubclass () {
272     if (!isValidSubclass ()) error (DWT.ERROR_INVALID_SUBCLASS);
273 }
274
275 static int checkStyle (int style) {
276     return checkBits (style, DWT.PUSH, DWT.CHECK, DWT.RADIO, DWT.SEPARATOR, DWT.CASCADE, 0);
277 }
278
279 override void destroyWidget () {
280     parent.destroyItem (this);
281     releaseHandle ();
282 }
283
284 bool fillAccel (ACCEL* accel) {
285     accel.cmd = accel.key = accel.fVirt = 0;
286     if (accelerator is 0 || !getEnabled ()) return false;
287     if ((accelerator & DWT.COMMAND) !is 0) return false;
288     int fVirt = OS.FVIRTKEY;
289     int key = accelerator & DWT.KEY_MASK;
290     auto vKey = Display.untranslateKey (key);
291     if (vKey !is 0) {
292         key = vKey;
293     } else {
294         switch (key) {
295             /*
296             * Bug in Windows.  For some reason, VkKeyScan
297             * fails to map ESC to VK_ESCAPE and DEL to
298             * VK_DELETE.  The fix is to map these keys
299             * as a special case.
300             */
301             case 27: key = OS.VK_ESCAPE; break;
302             case 127: key = OS.VK_DELETE; break;
303             default: {
304                 key = Display.wcsToMbcs (cast(char) key);
305                 if (key is 0) return false;
306                 static if (OS.IsWinCE) {
307                     key = cast(int) OS.CharUpper (cast(TCHAR*) key);
308                 } else {
309                     vKey = OS.VkKeyScan (cast(TCHAR) key) & 0xFF;
310                     if (vKey is -1) {
311                         fVirt = 0;
312                     } else {
313                         key = vKey;
314                     }
315                 }
316             }
317         }
318     }
319     accel.key = cast(short) key;
320     accel.cmd = cast(short) id;
321     accel.fVirt = cast(byte) fVirt;
322     if ((accelerator & DWT.ALT) !is 0) accel.fVirt |= OS.FALT;
323     if ((accelerator & DWT.SHIFT) !is 0) accel.fVirt |= OS.FSHIFT;
324     if ((accelerator & DWT.CONTROL) !is 0) accel.fVirt |= OS.FCONTROL;
325     return true;
326 }
327
328 void fixMenus (Decorations newParent) {
329     if (menu !is null) menu.fixMenus (newParent);
330 }
331
332 /**
333  * Returns the widget accelerator.  An accelerator is the bit-wise
334  * OR of zero or more modifier masks and a key. Examples:
335  * <code>DWT.CONTROL | DWT.SHIFT | 'T', DWT.ALT | DWT.F2</code>.
336  * The default value is zero, indicating that the menu item does
337  * not have an accelerator.
338  *
339  * @return the accelerator or 0
340  *
341  * </ul>
342  * @exception DWTException <ul>
343  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
344  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
345  * </ul>
346  */
347 public int getAccelerator () {
348     checkWidget ();
349     return accelerator;
350 }
351
352 /**
353  * Returns a rectangle describing the receiver's size and location
354  * relative to its parent (or its display if its parent is null).
355  *
356  * @return the receiver's bounding rectangle
357  *
358  * @exception DWTException <ul>
359  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
360  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
361  * </ul>
362  *
363  * @since 3.1
364  */
365 /*public*/ Rectangle getBounds () {
366     checkWidget ();
367     static if (OS.IsWinCE) return new Rectangle (0, 0, 0, 0);
368     int index = parent.indexOf (this);
369     if (index is -1) return new Rectangle (0, 0, 0, 0);
370     if ((parent.style & DWT.BAR) !is 0) {
371         Decorations shell = parent.parent;
372         if (shell.menuBar !is parent) {
373             return new Rectangle (0, 0, 0, 0);
374         }
375         auto hwndShell = shell.handle;
376         MENUBARINFO info1;
377         info1.cbSize = MENUBARINFO.sizeof;
378         if (!OS.GetMenuBarInfo (hwndShell, OS.OBJID_MENU, 1, &info1)) {
379             return new Rectangle (0, 0, 0, 0);
380         }
381         MENUBARINFO info2;
382         info2.cbSize = MENUBARINFO.sizeof;
383         if (!OS.GetMenuBarInfo (hwndShell, OS.OBJID_MENU, index + 1, &info2)) {
384             return new Rectangle (0, 0, 0, 0);
385         }
386         int x = info2.rcBar.left - info1.rcBar.left;
387         int y = info2.rcBar.top - info1.rcBar.top;
388         int width = info2.rcBar.right - info2.rcBar.left;
389         int height = info2.rcBar.bottom - info2.rcBar.top;
390         return new Rectangle (x, y, width, height);
391     } else {
392         auto hMenu = parent.handle;
393         RECT rect1;
394         if (!OS.GetMenuItemRect (null, hMenu, 0, &rect1)) {
395             return new Rectangle (0, 0, 0, 0);
396         }
397         RECT rect2;
398         if (!OS.GetMenuItemRect (null, hMenu, index, &rect2)) {
399             return new Rectangle (0, 0, 0, 0);
400         }
401         int x = rect2.left - rect1.left + 2;
402         int y = rect2.top - rect1.top + 2;
403         int width = rect2.right - rect2.left;
404         int height = rect2.bottom - rect2.top;
405         return new Rectangle (x, y, width, height);
406     }
407 }
408
409 /**
410  * Returns <code>true</code> if the receiver is enabled, and
411  * <code>false</code> otherwise. A disabled menu item is typically
412  * not selectable from the user interface and draws with an
413  * inactive or "grayed" look.
414  *
415  * @return the receiver's enabled state
416  *
417  * @exception DWTException <ul>
418  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
419  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
420  * </ul>
421  *
422  * @see #isEnabled
423  */
424 public bool getEnabled () {
425     checkWidget ();
426     if ((OS.IsPPC || OS.IsSP) && parent.hwndCB !is null) {
427         auto hwndCB = parent.hwndCB;
428         TBBUTTONINFO info;
429         info.cbSize = TBBUTTONINFO.sizeof;
430         info.dwMask = OS.TBIF_STATE;
431         OS.SendMessage (hwndCB, OS.TB_GETBUTTONINFO, id, &info);
432         return (info.fsState & OS.TBSTATE_ENABLED) !is 0;
433     }
434     /*
435     * Feature in Windows.  For some reason, when the menu item
436     * is a separator, GetMenuItemInfo() always indicates that
437     * the item is not enabled.  The fix is to track the enabled
438     * state for separators.
439     */
440     if ((style & DWT.SEPARATOR) !is 0) {
441         return (state & DISABLED) is 0;
442     }
443     auto hMenu = parent.handle;
444     MENUITEMINFO info;
445     info.cbSize = OS.MENUITEMINFO_sizeof;
446     info.fMask = OS.MIIM_STATE;
447     bool success;
448     static if (OS.IsWinCE) {
449         int index = parent.indexOf (this);
450         if (index is -1) error (DWT.ERROR_CANNOT_GET_ENABLED);
451         success = cast(bool) OS.GetMenuItemInfo (hMenu, index, true, &info);
452     } else {
453         success = cast(bool) OS.GetMenuItemInfo (hMenu, id, false, &info);
454     }
455     if (!success) error (DWT.ERROR_CANNOT_GET_ENABLED);
456     return (info.fState & (OS.MFS_DISABLED | OS.MFS_GRAYED)) is 0;
457 }
458
459 /**
460  * Returns the receiver's cascade menu if it has one or null
461  * if it does not. Only <code>CASCADE</code> menu items can have
462  * a pull down menu. The sequence of key strokes, button presses
463  * and/or button releases that are used to request a pull down
464  * menu is platform specific.
465  *
466  * @return the receiver's menu
467  *
468  * @exception DWTException <ul>
469  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
470  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
471  * </ul>
472  */
473 override public Menu getMenu () {
474     checkWidget ();
475     return menu;
476 }
477
478 override String getNameText () {
479     if ((style & DWT.SEPARATOR) !is 0) return "|";
480     return super.getNameText ();
481 }
482
483 /**
484  * Returns the receiver's parent, which must be a <code>Menu</code>.
485  *
486  * @return the receiver's parent
487  *
488  * @exception DWTException <ul>
489  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
490  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
491  * </ul>
492  */
493 public Menu getParent () {
494     checkWidget ();
495     return parent;
496 }
497
498 /**
499  * Returns <code>true</code> if the receiver is selected,
500  * and false otherwise.
501  * <p>
502  * When the receiver is of type <code>CHECK</code> or <code>RADIO</code>,
503  * it is selected when it is checked.
504  *
505  * @return the selection state
506  *
507  * @exception DWTException <ul>
508  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
509  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
510  * </ul>
511  */
512 public bool getSelection () {
513     checkWidget ();
514     if ((style & (DWT.CHECK | DWT.RADIO)) is 0) return false;
515     if ((OS.IsPPC || OS.IsSP) && parent.hwndCB !is null) return false;
516     auto hMenu = parent.handle;
517     MENUITEMINFO info;
518     info.cbSize = OS.MENUITEMINFO_sizeof;
519     info.fMask = OS.MIIM_STATE;
520     bool success = cast(bool) OS.GetMenuItemInfo (hMenu, id, false, &info);
521     if (!success) error (DWT.ERROR_CANNOT_GET_SELECTION);
522     return (info.fState & OS.MFS_CHECKED) !is 0;
523 }
524
525 /**
526  * Returns <code>true</code> if the receiver is enabled and all
527  * of the receiver's ancestors are enabled, and <code>false</code>
528  * otherwise. A disabled menu item is typically not selectable from the
529  * user interface and draws with an inactive or "grayed" look.
530  *
531  * @return the receiver's enabled state
532  *
533  * @exception DWTException <ul>
534  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
535  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
536  * </ul>
537  *
538  * @see #getEnabled
539  */
540 public bool isEnabled () {
541     return getEnabled () && parent.isEnabled ();
542 }
543
544 override void releaseChildren (bool destroy) {
545     if (menu !is null) {
546         menu.release (false);
547         menu = null;
548     }
549     super.releaseChildren (destroy);
550 }
551
552 override void releaseHandle () {
553     super.releaseHandle ();
554     parent = null;
555     id = -1;
556 }
557
558 override void releaseParent () {
559     super.releaseParent ();
560     if (menu !is null) menu.dispose ();
561     menu = null;
562 }
563
564 override void releaseWidget () {
565     super.releaseWidget ();
566     if (hBitmap !is null) OS.DeleteObject (hBitmap);
567     hBitmap = null;
568     if (accelerator !is 0) {
569         parent.destroyAccelerators ();
570     }
571     accelerator = 0;
572     display.removeMenuItem (this);
573 }
574
575 /**
576  * Removes the listener from the collection of listeners who will
577  * be notified when the arm events are generated for the control.
578  *
579  * @param listener the listener which should no longer be notified
580  *
581  * @exception IllegalArgumentException <ul>
582  *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
583  * </ul>
584  * @exception DWTException <ul>
585  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
586  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
587  * </ul>
588  *
589  * @see ArmListener
590  * @see #addArmListener
591  */
592 public void removeArmListener (ArmListener listener) {
593     checkWidget ();
594     if (listener is null) error (DWT.ERROR_NULL_ARGUMENT);
595     if (eventTable is null) return;
596     eventTable.unhook (DWT.Arm, listener);
597 }
598 /**
599  * Removes the listener from the collection of listeners who will
600  * be notified when the help events are generated for the control.
601  *
602  * @param listener the listener which should no longer be notified
603  *
604  * @exception IllegalArgumentException <ul>
605  *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
606  * </ul>
607  * @exception DWTException <ul>
608  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
609  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
610  * </ul>
611  *
612  * @see HelpListener
613  * @see #addHelpListener
614  */
615 public void removeHelpListener (HelpListener listener) {
616     checkWidget ();
617     if (listener is null) error (DWT.ERROR_NULL_ARGUMENT);
618     if (eventTable is null) return;
619     eventTable.unhook (DWT.Help, listener);
620 }
621 /**
622  * Removes the listener from the collection of listeners who will
623  * be notified when the control is selected by the user.
624  *
625  * @param listener the listener which should no longer be notified
626  *
627  * @exception IllegalArgumentException <ul>
628  *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
629  * </ul>
630  * @exception DWTException <ul>
631  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
632  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
633  * </ul>
634  *
635  * @see SelectionListener
636  * @see #addSelectionListener
637  */
638 public void removeSelectionListener (SelectionListener listener) {
639     checkWidget ();
640     if (listener is null) error (DWT.ERROR_NULL_ARGUMENT);
641     if (eventTable is null) return;
642     eventTable.unhook (DWT.Selection, listener);
643     eventTable.unhook (DWT.DefaultSelection,listener);
644 }
645
646 void selectRadio () {
647     int index = 0;
648     MenuItem [] items = parent.getItems ();
649     while (index < items.length && items [index] !is this) index++;
650     int i = index - 1;
651     while (i >= 0 && items [i].setRadioSelection (false)) --i;
652     int j = index + 1;
653     while (j < items.length && items [j].setRadioSelection (false)) j++;
654     setSelection (true);
655 }
656
657 /**
658  * Sets the widget accelerator.  An accelerator is the bit-wise
659  * OR of zero or more modifier masks and a key. Examples: