root/dwt/widgets/Menu.d

Revision 263:27244095ce14, 52.5 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.Menu;
14
15 import dwt.DWT;
16 import dwt.DWTException;
17 import dwt.events.HelpListener;
18 import dwt.events.MenuListener;
19 import dwt.graphics.Color;
20 import dwt.graphics.Image;
21 import dwt.graphics.Point;
22 import dwt.graphics.Rectangle;
23 import dwt.internal.ImageList;
24 import dwt.internal.win32.OS;
25
26 import dwt.widgets.Widget;
27 import dwt.widgets.Decorations;
28 import dwt.widgets.MenuItem;
29 import dwt.widgets.Control;
30 import dwt.widgets.Shell;
31 import dwt.widgets.TypedListener;
32
33 import dwt.dwthelper.utils;
34
35 /**
36  * Instances of this class are user interface objects that contain
37  * menu items.
38  * <dl>
39  * <dt><b>Styles:</b></dt>
40  * <dd>BAR, DROP_DOWN, POP_UP, NO_RADIO_GROUP</dd>
41  * <dd>LEFT_TO_RIGHT, RIGHT_TO_LEFT</dd>
42  * <dt><b>Events:</b></dt>
43  * <dd>Help, Hide, Show </dd>
44  * </dl>
45  * <p>
46  * Note: Only one of BAR, DROP_DOWN and POP_UP may be specified.
47  * Only one of LEFT_TO_RIGHT or RIGHT_TO_LEFT may be specified.
48  * </p><p>
49  * IMPORTANT: This class is <em>not</em> intended to be subclassed.
50  * </p>
51  *
52  * @see <a href="http://www.eclipse.org/swt/snippets/#menu">Menu snippets</a>
53  * @see <a href="http://www.eclipse.org/swt/examples.php">DWT Example: ControlExample</a>
54  * @see <a href="http://www.eclipse.org/swt/">Sample code and further information</a>
55  */
56
57 public class Menu : Widget {
58     /**
59      * the handle to the OS resource
60      * (Warning: This field is platform dependent)
61      * <p>
62      * <b>IMPORTANT:</b> This field is <em>not</em> part of the DWT
63      * public API. It is marked public only so that it can be shared
64      * within the packages provided by DWT. It is not available on all
65      * platforms and should never be accessed from application code.
66      * </p>
67      */
68     public HMENU handle;
69
70     int x, y;
71     HBRUSH hBrush;
72     HWND hwndCB;
73     int id0, id1;
74     int foreground = -1, background = -1;
75     Image backgroundImage;
76     bool hasLocation;
77     MenuItem cascade;
78     Decorations parent;
79     ImageList imageList;
80
81     /* Resource ID for SHMENUBARINFO */
82     static const int ID_PPC = 100;
83
84     /* SmartPhone SoftKeyBar resource ids */
85     static const int ID_SPMM = 102;
86     static const int ID_SPBM = 103;
87     static const int ID_SPMB = 104;
88     static const int ID_SPBB = 105;
89     static const int ID_SPSOFTKEY0 = 106;
90     static const int ID_SPSOFTKEY1 = 107;
91
92 /**
93  * Constructs a new instance of this class given its parent,
94  * and sets the style for the instance so that the instance
95  * will be a popup menu on the given parent's shell.
96  * <p>
97  * After constructing a menu, it can be set into its parent
98  * using <code>parent.setMenu(menu)</code>.  In this case, the parent may
99  * be any control in the same widget tree as the parent.
100  * </p>
101  *
102  * @param parent a control which will be the parent of the new instance (cannot be null)
103  *
104  * @exception IllegalArgumentException <ul>
105  *    <li>ERROR_NULL_ARGUMENT - if the parent is null</li>
106  * </ul>
107  * @exception DWTException <ul>
108  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li>
109  *    <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li>
110  * </ul>
111  *
112  * @see DWT#POP_UP
113  * @see Widget#checkSubclass
114  * @see Widget#getStyle
115  */
116 public this (Control parent) {
117     this (checkNull (parent).menuShell (), DWT.POP_UP);
118 }
119
120 /**
121  * Constructs a new instance of this class given its parent
122  * (which must be a <code>Decorations</code>) and a style value
123  * describing its behavior and appearance.
124  * <p>
125  * The style value is either one of the style constants defined in
126  * class <code>DWT</code> which is applicable to instances of this
127  * class, or must be built by <em>bitwise OR</em>'ing together
128  * (that is, using the <code>int</code> "|" operator) two or more
129  * of those <code>DWT</code> style constants. The class description
130  * lists the style constants that are applicable to the class.
131  * Style bits are also inherited from superclasses.
132  * </p><p>
133  * After constructing a menu or menuBar, it can be set into its parent
134  * using <code>parent.setMenu(menu)</code> or <code>parent.setMenuBar(menuBar)</code>.
135  * </p>
136  *
137  * @param parent a decorations control which will be the parent of the new instance (cannot be null)
138  * @param style the style of menu to construct
139  *
140  * @exception IllegalArgumentException <ul>
141  *    <li>ERROR_NULL_ARGUMENT - if the parent is null</li>
142  * </ul>
143  * @exception DWTException <ul>
144  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li>
145  *    <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li>
146  * </ul>
147  *
148  * @see DWT#BAR
149  * @see DWT#DROP_DOWN
150  * @see DWT#POP_UP
151  * @see Widget#checkSubclass
152  * @see Widget#getStyle
153  */
154 public this (Decorations parent, int style) {
155     this (parent, checkStyle (style), null);
156 }
157
158 /**
159  * Constructs a new instance of this class given its parent
160  * (which must be a <code>Menu</code>) and sets the style
161  * for the instance so that the instance will be a drop-down
162  * menu on the given parent's parent.
163  * <p>
164  * After constructing a drop-down menu, it can be set into its parentMenu
165  * using <code>parentMenu.setMenu(menu)</code>.
166  * </p>
167  *
168  * @param parentMenu a menu which will be the parent of the new instance (cannot be null)
169  *
170  * @exception IllegalArgumentException <ul>
171  *    <li>ERROR_NULL_ARGUMENT - if the parent is null</li>
172  * </ul>
173  * @exception DWTException <ul>
174  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li>
175  *    <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li>
176  * </ul>
177  *
178  * @see DWT#DROP_DOWN
179  * @see Widget#checkSubclass
180  * @see Widget#getStyle
181  */
182 public this (Menu parentMenu) {
183     this (checkNull (parentMenu).parent, DWT.DROP_DOWN);
184 }
185
186 /**
187  * Constructs a new instance of this class given its parent
188  * (which must be a <code>MenuItem</code>) and sets the style
189  * for the instance so that the instance will be a drop-down
190  * menu on the given parent's parent menu.
191  * <p>
192  * After constructing a drop-down menu, it can be set into its parentItem
193  * using <code>parentItem.setMenu(menu)</code>.
194  * </p>
195  *
196  * @param parentItem a menu item which will be the parent of the new instance (cannot be null)
197  *
198  * @exception IllegalArgumentException <ul>
199  *    <li>ERROR_NULL_ARGUMENT - if the parent is null</li>
200  * </ul>
201  * @exception DWTException <ul>
202  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li>
203  *    <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li>
204  * </ul>
205  *
206  * @see DWT#DROP_DOWN
207  * @see Widget#checkSubclass
208  * @see Widget#getStyle
209  */
210 public this (MenuItem parentItem) {
211     this (checkNull (parentItem).parent);
212 }
213
214 this (Decorations parent, int style, HWND handle) {
215     super (parent, checkStyle (style));
216     this.parent = parent;
217     this.handle = handle;
218     /*
219     * Bug in IBM JVM 1.3.1.  For some reason, when the checkOrientation() is
220     * called from createWidget(), the JVM issues this error:
221     *
222     * JVM Exception 0x2 (subcode 0x0) occurred in thread "main" (TID:0x9F19D8)
223     *
224     * In addition, on Windows XP, a dialog appears with following error message,
225     * indicating that the problem may be in the JIT:
226     *
227     * AppName: java.exe  AppVer: 0.0.0.0     ModName: jitc.dll
228     * ModVer: 0.0.0.0    Offset: 000b6912
229     *
230     * The fix is to call checkOrientation() from here.
231     */
232     checkOrientation (parent);
233     createWidget ();
234 }
235
236 void _setVisible (bool visible) {
237     if ((style & (DWT.BAR | DWT.DROP_DOWN)) !is 0) return;
238     auto hwndParent = parent.handle;
239     if (visible) {
240         int flags = OS.TPM_LEFTBUTTON;
241         if (OS.GetKeyState (OS.VK_LBUTTON) >= 0) flags |= OS.TPM_RIGHTBUTTON;
242         if ((style & DWT.RIGHT_TO_LEFT) !is 0) flags |= OS.TPM_RIGHTALIGN;
243         if ((parent.style & DWT.MIRRORED) !is 0) {
244             flags &= ~OS.TPM_RIGHTALIGN;
245             if ((style & DWT.LEFT_TO_RIGHT) !is 0) flags |= OS.TPM_RIGHTALIGN;
246         }
247         int nX = x, nY = y;
248         if (!hasLocation) {
249             int pos = OS.GetMessagePos ();
250             nX = OS.GET_X_LPARAM (pos);
251             nY = OS.GET_Y_LPARAM (pos);
252         }
253         /*
254         * Feature in Windows.  It is legal use TrackPopupMenu()
255         * to display an empty menu as long as menu items are added
256         * inside of WM_INITPOPUPMENU.  If no items are added, then
257         * TrackPopupMenu() fails and does not send an indication
258         * that the menu has been closed.  This is not strictly a
259         * bug but leads to unwanted behavior when application code
260         * assumes that every WM_INITPOPUPMENU will eventually result
261         * in a WM_MENUSELECT, wParam=MAKEWPARAM (0, 0xFFFF), lParam=0 to
262         * indicate that the menu has been closed.  The fix is to detect
263         * the case when TrackPopupMenu() fails and the number of items in
264         * the menu is zero and issue a fake WM_MENUSELECT.
265         */
266         bool success = cast(bool) OS.TrackPopupMenu (handle, flags, nX, nY, 0, hwndParent, null);
267         if (!success && GetMenuItemCount (handle) is 0) {
268             OS.SendMessage (hwndParent, OS.WM_MENUSELECT, OS.MAKEWPARAM (0, 0xFFFF), 0);
269         }
270     } else {
271         OS.SendMessage (hwndParent, OS.WM_CANCELMODE, 0, 0);
272     }
273 }
274
275 /**
276  * Adds the listener to the collection of listeners who will
277  * be notified when help events are generated for the control,
278  * by sending it one of the messages defined in the
279  * <code>HelpListener</code> interface.
280  *
281  * @param listener the listener which should be notified
282  *
283  * @exception IllegalArgumentException <ul>
284  *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
285  * </ul>
286  * @exception DWTException <ul>
287  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
288  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
289  * </ul>
290  *
291  * @see HelpListener
292  * @see #removeHelpListener
293  */
294 public void addHelpListener (HelpListener listener) {
295     checkWidget ();
296     if (listener is null) error (DWT.ERROR_NULL_ARGUMENT);
297     TypedListener typedListener = new TypedListener (listener);
298     addListener (DWT.Help, typedListener);
299 }
300
301 /**
302  * Adds the listener to the collection of listeners who will
303  * be notified when menus are hidden or shown, by sending it
304  * one of the messages defined in the <code>MenuListener</code>
305  * interface.
306  *
307  * @param listener the listener which should be notified
308  *
309  * @exception IllegalArgumentException <ul>
310  *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
311  * </ul>
312  * @exception DWTException <ul>
313  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
314  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
315  * </ul>
316  *
317  * @see MenuListener
318  * @see #removeMenuListener
319  */
320 public void addMenuListener (MenuListener listener) {
321     checkWidget ();
322     if (listener is null) error (DWT.ERROR_NULL_ARGUMENT);
323     TypedListener typedListener = new TypedListener (listener);
324     addListener (DWT.Hide,typedListener);
325     addListener (DWT.Show,typedListener);
326 }
327
328 static Control checkNull (Control control) {
329     if (control is null) DWT.error (DWT.ERROR_NULL_ARGUMENT);
330     return control;
331 }
332
333 static Menu checkNull (Menu menu) {
334     if (menu is null) DWT.error (DWT.ERROR_NULL_ARGUMENT);
335     return menu;
336 }
337
338 static MenuItem checkNull (MenuItem item) {
339     if (item is null) DWT.error (DWT.ERROR_NULL_ARGUMENT);
340     return item;
341 }
342
343 static int checkStyle (int style) {
344     return checkBits (style, DWT.POP_UP, DWT.BAR, DWT.DROP_DOWN, 0, 0, 0);
345 }
346
347 void createHandle () {
348     if (handle !is null) return;
349     if ((style & DWT.BAR) !is 0) {
350         static if( OS.IsWinCE ) if (OS.IsPPC) {
351             auto hwndShell = parent.handle;
352             SHMENUBARINFO mbi;
353             mbi.cbSize = SHMENUBARINFO.sizeof;
354             mbi.hwndParent = hwndShell;
355             mbi.dwFlags = OS.SHCMBF_HIDDEN;
356             mbi.nToolBarId = ID_PPC;
357             mbi.hInstRes = OS.GetLibraryHandle ();
358             bool success = cast(bool) OS.SHCreateMenuBar (&mbi);
359             hwndCB = mbi.hwndMB;
360             if (!success) error (DWT.ERROR_NO_HANDLES);
361             /* Remove the item from the resource file */
362             OS.SendMessage (hwndCB, OS.TB_DELETEBUTTON, 0, 0);
363             return;
364         }
365         /*
366         * Note in WinCE SmartPhone.  The SoftBar contains only 2 items.
367         * An item can either be a menu or a button.
368         * DWT.BAR: creates a SoftBar with 2 menus
369         * DWT.BAR | DWT.BUTTON1: creates a SoftBar with 1 button
370         *    for button1, and a menu for button2
371         * DWT.BAR | DWT.BUTTON1 | DWT.BUTTON2: creates a SoftBar with
372         *    2 buttons
373         */
374         static if (OS.IsSP) {
375             /* Determine type of menubar */
376             int nToolBarId;
377             if ((style & DWT.BUTTON1) !is 0) {
378                 nToolBarId = ((style & DWT.BUTTON2) !is 0) ? ID_SPBB : ID_SPBM;
379             } else {
380                 nToolBarId = ((style & DWT.BUTTON2) !is 0) ? ID_SPMB : ID_SPMM;
381             }
382
383             /* Create SHMENUBAR */
384             SHMENUBARINFO mbi;
385             mbi.cbSize = SHMENUBARINFO.sizeof;
386             mbi.hwndParent = parent.handle;
387             mbi.dwFlags = OS.SHCMBF_HIDDEN;
388             mbi.nToolBarId = nToolBarId; /* as defined in .rc file */
389             mbi.hInstRes = OS.GetLibraryHandle ();
390             if (!OS.SHCreateMenuBar (&mbi)) error (DWT.ERROR_NO_HANDLES);
391             hwndCB = mbi.hwndMB;
392
393             /*
394             * Feature on WinCE SmartPhone.  The SHCMBF_HIDDEN flag causes the
395             * SHMENUBAR to not be drawn. However the keyboard events still go
396             * through it.  The workaround is to also hide the SHMENUBAR with
397             * ShowWindow ().
398             */
399             OS.ShowWindow (hwndCB, OS.SW_HIDE);
400
401             TBBUTTONINFO info = new TBBUTTONINFO ();
402             info.cbSize = TBBUTTONINFO.sizeof;
403             info.dwMask = OS.TBIF_COMMAND;
404             MenuItem item;
405
406             /* Set first item */
407             if (nToolBarId is ID_SPMM || nToolBarId is ID_SPMB) {
408                 int /*long*/ hMenu = OS.SendMessage (hwndCB, OS.SHCMBM_GETSUBMENU, 0, ID_SPSOFTKEY0);
409                 /* Remove the item from the resource file */
410                 OS.RemoveMenu (hMenu, 0, OS.MF_BYPOSITION);
411                 Menu menu = new Menu (parent, DWT.DROP_DOWN, hMenu);
412                 item = new MenuItem (this, menu, DWT.CASCADE, 0);
413             } else {
414                 item = new MenuItem (this, null, DWT.PUSH, 0);
415             }
416             info.idCommand = id0 = item.id;
417             OS.SendMessage (hwndCB, OS.TB_SETBUTTONINFO, ID_SPSOFTKEY0, info);
418
419             /* Set second item */
420             if (nToolBarId is ID_SPMM || nToolBarId is ID_SPBM) {
421                 int /*long*/ hMenu = OS.SendMessage (hwndCB, OS.SHCMBM_GETSUBMENU, 0, ID_SPSOFTKEY1);
422                 OS.RemoveMenu (hMenu, 0, OS.MF_BYPOSITION);
423                 Menu menu = new Menu (parent, DWT.DROP_DOWN, hMenu);
424                 item = new MenuItem (this, menu, DWT.CASCADE, 1);
425             } else {
426                 item = new MenuItem (this, null, DWT.PUSH, 1);
427             }
428             info.idCommand = id1 = item.id;
429             OS.SendMessage (hwndCB, OS.TB_SETBUTTONINFO, ID_SPSOFTKEY1, info);
430
431             /*
432             * Override the Back key.  For some reason, the owner of the menubar
433             * must be a Dialog or it won't receive the WM_HOTKEY message.  As
434             * a result, Shell on WinCE SP must use the class Dialog.
435             */
436             int dwMask = OS.SHMBOF_NODEFAULT | OS.SHMBOF_NOTIFY;
437             int /*long*/ lParam = OS.MAKELPARAM (dwMask, dwMask);
438             OS.SendMessage (hwndCB, OS.SHCMBM_OVERRIDEKEY, OS.VK_ESCAPE, lParam);
439             return;
440         }
441         handle = OS.CreateMenu ();
442         if (handle is null) error (DWT.ERROR_NO_HANDLES);
443         static if (OS.IsHPC) {
444             auto hwndShell = parent.handle;
445             hwndCB = OS.CommandBar_Create (OS.GetModuleHandle (null), hwndShell, 1);
446             if (hwndCB is null) error (DWT.ERROR_NO_HANDLES);
447             OS.CommandBar_Show (hwndCB, false);
448             OS.CommandBar_InsertMenubarEx (hwndCB, 0, handle, 0);
449             /*
450             * The command bar hosts the 'close' button when the window does not
451             * have a caption.
452             */
453             if ((parent.style & DWT.CLOSE) !is 0 && (parent.style & DWT.TITLE) is 0) {
454                 OS.CommandBar_AddAdornments (hwndCB, 0, 0);
455             }
456         }
457     } else {
458         handle = OS.CreatePopupMenu ();
459         if (handle is null) error (DWT.ERROR_NO_HANDLES);
460     }
461 }
462
463 void createItem (MenuItem item, int index) {
464     int count = GetMenuItemCount (handle);
465     if (!(0 <= index && index <= count)) error (DWT.ERROR_INVALID_RANGE);
466     display.addMenuItem (item);
467     bool success = false;
468     if ((OS.IsPPC || OS.IsSP) && hwndCB !is null) {
469         if (OS.IsSP) return;
470         TBBUTTON lpButton;
471         lpButton.idCommand = item.id;
472         lpButton.fsStyle = cast(byte) OS.TBSTYLE_AUTOSIZE;
473         if ((item.style & DWT.CASCADE) !is 0) lpButton.fsStyle |= OS.TBSTYLE_DROPDOWN | 0x80;
474         if ((item.style & DWT.SEPARATOR) !is 0) lpButton.fsStyle = cast(byte) OS.BTNS_SEP;
475         lpButton.fsState = cast(byte) OS.TBSTATE_ENABLED;
476         lpButton.iBitmap = OS.I_IMAGENONE;
477         success = OS.SendMessage (hwndCB, OS.TB_INSERTBUTTON, index, &lpButton) !is 0;
478     } else {
479         static if (OS.IsWinCE) {
480             int uFlags = OS.MF_BYPOSITION;
481             TCHAR lpNewItem = null;
482             if ((item.style & DWT.SEPARATOR) !is 0) {
483                 uFlags |= OS.MF_SEPARATOR;
484             } else {
485                 lpNewItem = new TCHAR (0, " ", true);
486             }
487             success = OS.InsertMenu (handle, index, uFlags, item.id, lpNewItem);
488             if (success) {
489                 MENUITEMINFO info = new MENUITEMINFO ();
490                 info.cbSize = OS.MENUITEMINFO_sizeof;
491                 info.fMask = OS.MIIM_DATA;
492                 info.dwItemData = item.id;
493                 success = OS.SetMenuItemInfo (handle, index, true, info);
494             }
495         } else {
496             /*
497             * Bug in Windows.  For some reason, when InsertMenuItem()
498             * is used to insert an item without text, it is not possible
499             * to use SetMenuItemInfo() to set the text at a later time.
500             * The fix is to insert the item with some text.
501             *
502             * Feature in Windows.  When an empty string is used instead
503             * of a space and InsertMenuItem() is used to set a submenu
504             * before setting text to a non-empty string, the menu item
505             * becomes unexpectedly disabled.  The fix is to insert a
506             * space.
507             */
508             auto hHeap = OS.GetProcessHeap ();
509             TCHAR[] buffer = StrToTCHARs (0, " \0");
510             int byteCount = (buffer.length-1) * TCHAR.sizeof;
511             auto pszText = cast(TCHAR*) OS.HeapAlloc (hHeap, OS.HEAP_ZERO_MEMORY, byteCount);
512             OS.MoveMemory (pszText, buffer.ptr, byteCount);
513             MENUITEMINFO info;
514             info.cbSize = OS.MENUITEMINFO_sizeof;
515             info.fMask = OS.MIIM_ID | OS.MIIM_TYPE | OS.MIIM_DATA;
516             info.wID = item.id;
517             info.dwItemData = item.id;
518             info.fType = item.widgetStyle ();
519             info.dwTypeData = pszText;
520             success = cast(bool) OS.InsertMenuItem (handle, index, true, &info);
521             if (pszText !is null) OS.HeapFree (hHeap, 0, pszText);
522         }
523     }
524     if (!success) {
525         display.removeMenuItem (item);
526         error (DWT.ERROR_ITEM_NOT_ADDED);
527     }
528     redraw ();
529 }
530
531 void createWidget () {
532     /*
533     * Bug in IBM JVM 1.3.1.  For some reason, when the following code is called
534     * from this method, the JVM issues this error:
535     *
536     * JVM Exception 0x2 (subcode 0x0) occurred in thread "main" (TID:0x9F19D8)
537     *
538     * In addition, on Windows XP, a dialog appears with following error message,
539     * indicating that the problem may be in the JIT:
540     *
541     * AppName: java.exe  AppVer: 0.0.0.0     ModName: jitc.dll
542     * ModVer: 0.0.0.0    Offset: 000b6912
543     *
544     * The fix is to move the code to the caller of this method.
545     */
546 //  checkOrientation (parent);
547     createHandle ();
548     parent.addMenu (this);
549 }
550
551 int defaultBackground () {
552     return OS.GetSysColor (OS.COLOR_MENU);
553 }
554
555 int defaultForeground () {
556     return OS.GetSysColor (OS.COLOR_MENUTEXT);
557 }
558
559 void destroyAccelerators () {
560     parent.destroyAccelerators ();
561 }
562
563 void destroyItem (MenuItem item) {
564     static if (OS.IsWinCE) {
565         if ((OS.IsPPC || OS.IsSP) && hwndCB !is null) {
566             if (OS.IsSP) {
567                 redraw();
568                 return;
569             }
570             int index = OS.SendMessage (hwndCB, OS.TB_COMMANDTOINDEX, item.id, 0);
571             if (OS.SendMessage (hwndCB, OS.TB_DELETEBUTTON, index, 0) is 0) {
572                 error (DWT.ERROR_ITEM_NOT_REMOVED);
573             }
574             int count = OS.SendMessage (hwndCB, OS.TB_BUTTONCOUNT, 0, 0);
575             if (count is 0) {
576                 if (imageList !is null) {
577                     OS.SendMessage (handle, OS.TB_SETIMAGELIST, 0, 0);
578                     display.releaseImageList (imageList);
579                     imageList = null;
580                 }
581             }
582         } else {
583             int index = 0;
584             MENUITEMINFO info;
585             info.cbSize = OS.MENUITEMINFO_sizeof;
586             info.fMask = OS.MIIM_DATA;
587             while (OS.GetMenuItemInfo (handle, index, true, &info)) {
588                 if (info.dw