Release 950109
[wine] / controls / menu.c
1 /*
2  * Menu functions
3  *
4  * Copyright 1993 Martin Ayotte
5  * Copyright 1994 Alexandre Julliard
6  */
7
8 /*
9  * Note: the style MF_MOUSESELECT is used to mark popup items that
10  * have been selected, i.e. their popup menu is currently displayed.
11  * This is probably not the meaning this style has in MS-Windows.
12  */
13
14 #include <ctype.h>
15 #include <stdlib.h>
16 #include <stdio.h>
17 #include <string.h>
18 #include "windows.h"
19 #include "syscolor.h"
20 #include "sysmetrics.h"
21 #include "menu.h"
22 #include "user.h"
23 #include "win.h"
24 #include "library.h"
25 #include "message.h"
26 #include "graphics.h"
27 #include "stddebug.h"
28 /* #define DEBUG_MENU */
29 /* #define DEBUG_MENUCALC */
30 /* #define DEBUG_MENUSHORTCUT */
31 #include "debug.h"
32
33
34   /* Dimension of the menu bitmaps */
35 static WORD check_bitmap_width = 0, check_bitmap_height = 0;
36 static WORD arrow_bitmap_width = 0, arrow_bitmap_height = 0;
37
38   /* Flag set by EndMenu() to force an exit from menu tracking */
39 static BOOL fEndMenuCalled = FALSE;
40
41   /* Space between 2 menu bar items */
42 #define MENU_BAR_ITEMS_SPACE  16
43
44   /* Minimum width of a tab character */
45 #define MENU_TAB_SPACE        8
46
47   /* Height of a separator item */
48 #define SEPARATOR_HEIGHT      5
49
50   /* Values for menu->FocusedItem */
51   /* (other values give the position of the focused item) */
52 #define NO_SELECTED_ITEM  0xffff
53 #define SYSMENU_SELECTED  0xfffe  /* Only valid on menu-bars */
54
55 #define IS_STRING_ITEM(flags) (!((flags) & (MF_BITMAP | MF_OWNERDRAW | \
56                              MF_MENUBARBREAK | MF_MENUBREAK | MF_SEPARATOR)))
57
58
59 extern void NC_DrawSysButton(HWND hwnd, HDC hdc, BOOL down);  /* nonclient.c */
60
61 static HBITMAP hStdCheck = 0;
62 static HBITMAP hStdMnArrow = 0;
63
64 HMENU CopySysMenu();
65 WORD * ParseMenuResource(WORD *first_item, int level, HMENU hMenu);
66
67
68 /***********************************************************************
69  *           MENU_Init
70  *
71  * Menus initialisation.
72  */
73 BOOL MENU_Init()
74 {
75     BITMAP bm;
76
77       /* Load bitmaps */
78
79     if (!(hStdCheck = LoadBitmap( 0, MAKEINTRESOURCE(OBM_CHECK) )))
80         return FALSE;
81     GetObject( hStdCheck, sizeof(BITMAP), (LPSTR)&bm );
82     check_bitmap_width = bm.bmWidth;
83     check_bitmap_height = bm.bmHeight;
84     if (!(hStdMnArrow = LoadBitmap( 0, MAKEINTRESOURCE(OBM_MNARROW) )))
85         return FALSE;
86     GetObject( hStdMnArrow, sizeof(BITMAP), (LPSTR)&bm );
87     arrow_bitmap_width = bm.bmWidth;
88     arrow_bitmap_height = bm.bmHeight;
89
90     return TRUE;
91 }
92
93
94 /***********************************************************************
95  *           MENU_HasSysMenu
96  *
97  * Check whether the window owning the menu bar has a system menu.
98  */
99 static BOOL MENU_HasSysMenu( POPUPMENU *menu )
100 {
101     WND *wndPtr;
102
103     if (menu->wFlags & MF_POPUP) return FALSE;
104     if (!(wndPtr = WIN_FindWndPtr( menu->hWnd ))) return FALSE;
105     return (wndPtr->dwStyle & WS_SYSMENU) != 0;
106 }
107
108
109 /***********************************************************************
110  *           MENU_IsInSysMenu
111  *
112  * Check whether the point (in screen coords) is in the system menu
113  * of the window owning the given menu.
114  */
115 static BOOL MENU_IsInSysMenu( POPUPMENU *menu, POINT pt )
116 {
117     WND *wndPtr;
118
119     if (menu->wFlags & MF_POPUP) return FALSE;
120     if (!(wndPtr = WIN_FindWndPtr( menu->hWnd ))) return FALSE;
121     if (!(wndPtr->dwStyle & WS_SYSMENU)) return FALSE;
122     if ((pt.x < wndPtr->rectClient.left) ||
123         (pt.x >= wndPtr->rectClient.left+SYSMETRICS_CXSIZE+SYSMETRICS_CXBORDER))
124         return FALSE;
125     if ((pt.y >= wndPtr->rectClient.top - menu->Height) ||
126         (pt.y < wndPtr->rectClient.top - menu->Height -
127                       SYSMETRICS_CYSIZE - SYSMETRICS_CYBORDER)) return FALSE;
128     return TRUE;
129 }
130
131
132 /***********************************************************************
133  *           MENU_FindItem
134  *
135  * Find a menu item. Return a pointer on the item, and modifies *hmenu
136  * in case the item was in a sub-menu.
137  */
138 static MENUITEM *MENU_FindItem( HMENU *hmenu, WORD *nPos, WORD wFlags )
139 {
140     POPUPMENU *menu;
141     MENUITEM *item;
142     int i;
143
144     if (!(menu = (POPUPMENU *) USER_HEAP_ADDR(*hmenu))) return NULL;
145     item = (MENUITEM *) USER_HEAP_ADDR( menu->hItems );
146     if (wFlags & MF_BYPOSITION)
147     {
148         if (*nPos >= menu->nItems) return NULL;
149         return &item[*nPos];
150     }
151     else
152     {
153         for (i = 0; i < menu->nItems; i++, item++)
154         {
155             if (item->item_id == *nPos)
156             {
157                 *nPos = i;
158                 return item;
159             }
160             else if (item->item_flags & MF_POPUP)
161             {
162                 HMENU hsubmenu = (HMENU)item->item_id;
163                 MENUITEM *subitem = MENU_FindItem( &hsubmenu, nPos, wFlags );
164                 if (subitem)
165                 {
166                     *hmenu = hsubmenu;
167                     return subitem;
168                 }
169             }
170         }
171     }
172     return NULL;
173 }
174
175
176 /***********************************************************************
177  *           MENU_FindItemByCoords
178  *
179  * Find the item at the specified coordinates (screen coords).
180  */
181 static MENUITEM *MENU_FindItemByCoords( POPUPMENU *menu, int x, int y, WORD *pos )
182 {
183     MENUITEM *item;
184     WND *wndPtr;
185     int i;
186
187     if (!(wndPtr = WIN_FindWndPtr( menu->hWnd ))) return NULL;
188     x -= wndPtr->rectWindow.left;
189     y -= wndPtr->rectWindow.top;
190     item = (MENUITEM *) USER_HEAP_ADDR( menu->hItems );
191     for (i = 0; i < menu->nItems; i++, item++)
192     {
193         if ((x >= item->rect.left) && (x < item->rect.right) &&
194             (y >= item->rect.top) && (y < item->rect.bottom))
195         {
196             if (pos) *pos = i;
197             return item;
198         }
199     }
200     return NULL;
201 }
202
203
204 /***********************************************************************
205  *           MENU_FindItemByKey
206  *
207  * Find the menu item selected by a key press.
208  * Return item id, -1 if none, -2 if we should close the menu.
209  */
210 static WORD MENU_FindItemByKey( HWND hwndOwner, HMENU hmenu, WORD key )
211 {
212     POPUPMENU *menu;
213     LPMENUITEM lpitem;
214     int i;
215     LONG menuchar;
216
217     menu = (POPUPMENU *) USER_HEAP_ADDR( hmenu );
218     lpitem = (MENUITEM *) USER_HEAP_ADDR( menu->hItems );
219     key = toupper(key);
220     for (i = 0; i < menu->nItems; i++, lpitem++)
221     {
222         if (IS_STRING_ITEM(lpitem->item_flags))
223         {
224             char *p = strchr( lpitem->item_text, '&' );
225             if (p && (p[1] != '&') && (toupper(p[1]) == key)) return i;
226         }
227     }
228     menuchar = SendMessage( hwndOwner, WM_MENUCHAR, key,
229                             MAKELONG( menu->wFlags, hmenu ) );
230     if (HIWORD(menuchar) == 2) return LOWORD(menuchar);
231     if (HIWORD(menuchar) == 1) return -2;
232     return -1;
233 }
234
235
236 /***********************************************************************
237  *           MENU_CalcItemSize
238  *
239  * Calculate the size of the menu item and store it in lpitem->rect.
240  */
241 static void MENU_CalcItemSize( HDC hdc, LPMENUITEM lpitem, HWND hwndOwner,
242                                int orgX, int orgY, BOOL menuBar )
243 {
244     DWORD dwSize;
245     char *p;
246
247     SetRect( &lpitem->rect, orgX, orgY, orgX, orgY );
248     lpitem->xTab = 0;
249
250     if (lpitem->item_flags & MF_SEPARATOR)
251     {
252         lpitem->rect.bottom += SEPARATOR_HEIGHT;
253         return;
254     }
255
256     if (!menuBar)
257     {
258         lpitem->rect.right += 2 * check_bitmap_width;
259         if (lpitem->item_flags & MF_POPUP)
260             lpitem->rect.right += arrow_bitmap_width;
261     }
262
263     if (lpitem->item_flags & MF_BITMAP)
264     {
265         BITMAP bm;
266         GetObject( (HBITMAP)lpitem->hText, sizeof(BITMAP), (LPSTR)&bm );
267         lpitem->rect.right  += bm.bmWidth;
268         lpitem->rect.bottom += bm.bmHeight;
269         return;
270     }
271     
272       /* If we get here, then it is a text item */
273
274     dwSize = (lpitem->item_text == NULL) ? 0 : GetTextExtent( hdc, lpitem->item_text, strlen(lpitem->item_text));
275     lpitem->rect.right  += LOWORD(dwSize);
276     lpitem->rect.bottom += max( HIWORD(dwSize), SYSMETRICS_CYMENU );
277
278     if (menuBar) lpitem->rect.right += MENU_BAR_ITEMS_SPACE;
279     else if ((p = strchr( lpitem->item_text, '\t' )) != NULL)
280     {
281           /* Item contains a tab (only meaningful in popup menus) */
282         lpitem->xTab = check_bitmap_width + MENU_TAB_SPACE + 
283                          LOWORD( GetTextExtent( hdc, lpitem->item_text,
284                                                (int)(p - lpitem->item_text) ));
285         lpitem->rect.right += MENU_TAB_SPACE;
286     }
287     else
288     {
289         if (strchr( lpitem->item_text, '\b' ))
290             lpitem->rect.right += MENU_TAB_SPACE;
291         lpitem->xTab = lpitem->rect.right - check_bitmap_width 
292                         - arrow_bitmap_width;
293     }
294 }
295
296
297 /***********************************************************************
298  *           MENU_PopupMenuCalcSize
299  *
300  * Calculate the size of a popup menu.
301  */
302 static void MENU_PopupMenuCalcSize( LPPOPUPMENU lppop, HWND hwndOwner )
303 {
304     LPMENUITEM  items, lpitem;
305     HDC hdc;
306     int start, i;
307     int orgX, orgY, maxX, maxTab, maxTabWidth;
308
309     lppop->Width = lppop->Height = 0;
310     if (lppop->nItems == 0) return;
311     items = (MENUITEM *)USER_HEAP_ADDR( lppop->hItems );
312     hdc = GetDC( 0 );
313     maxX = start = 0;
314     while (start < lppop->nItems)
315     {
316         lpitem = &items[start];
317         orgX = maxX;
318         orgY = 0;
319         maxTab = maxTabWidth = 0;
320
321           /* Parse items until column break or end of menu */
322         for (i = start; i < lppop->nItems; i++, lpitem++)
323         {
324             if ((i != start) &&
325                 (lpitem->item_flags & (MF_MENUBREAK | MF_MENUBARBREAK))) break;
326             MENU_CalcItemSize( hdc, lpitem, hwndOwner, orgX, orgY, FALSE );
327             maxX = max( maxX, lpitem->rect.right );
328             orgY = lpitem->rect.bottom;
329             if (lpitem->xTab)
330             {
331                 maxTab = max( maxTab, lpitem->xTab );
332                 maxTabWidth = max(maxTabWidth,lpitem->rect.right-lpitem->xTab);
333             }
334         }
335
336           /* Finish the column (set all items to the largest width found) */
337         maxX = max( maxX, maxTab + maxTabWidth );
338         for (lpitem = &items[start]; start < i; start++, lpitem++)
339         {
340             lpitem->rect.right = maxX;
341             if (lpitem->xTab) lpitem->xTab = maxTab;
342         }
343         lppop->Height = max( lppop->Height, orgY );
344     }
345
346     lppop->Width  = maxX;
347     ReleaseDC( 0, hdc );
348 }
349
350
351 /***********************************************************************
352  *           MENU_MenuBarCalcSize
353  *
354  * Calculate the size of the menu bar.
355  */
356 static void MENU_MenuBarCalcSize( HDC hdc, LPRECT lprect, LPPOPUPMENU lppop,
357                                   HWND hwndOwner )
358 {
359     LPMENUITEM lpitem, items;
360     int start, i, orgX, orgY, maxY, helpPos;
361
362     if ((lprect == NULL) || (lppop == NULL)) return;
363     if (lppop->nItems == 0) return;
364         dprintf_menucalc(stddeb,"MenuBarCalcSize left=%d top=%d right=%d bottom=%d !\n", 
365                 lprect->left, lprect->top, lprect->right, lprect->bottom);
366     items = (MENUITEM *)USER_HEAP_ADDR( lppop->hItems );
367     lppop->Width  = lprect->right - lprect->left;
368     lppop->Height = 0;
369     maxY = lprect->top;
370     start = 0;
371     helpPos = -1;
372     while (start < lppop->nItems)
373     {
374         lpitem = &items[start];
375         orgX = lprect->left;
376         orgY = maxY;
377
378           /* Parse items until line break or end of menu */
379         for (i = start; i < lppop->nItems; i++, lpitem++)
380         {
381             if ((helpPos == -1) && (lpitem->item_flags & MF_HELP)) helpPos = i;
382             if ((i != start) &&
383                 (lpitem->item_flags & (MF_MENUBREAK | MF_MENUBARBREAK))) break;
384             MENU_CalcItemSize( hdc, lpitem, hwndOwner, orgX, orgY, TRUE );
385             if (lpitem->rect.right > lprect->right)
386             {
387                 if (i != start) break;
388                 else lpitem->rect.right = lprect->right;
389             }
390             maxY = max( maxY, lpitem->rect.bottom );
391             orgX = lpitem->rect.right;
392         }
393
394           /* Finish the line (set all items to the largest height found) */
395         while (start < i) items[start++].rect.bottom = maxY;
396     }
397
398     lprect->bottom = maxY;
399     lppop->Height = lprect->bottom - lprect->top;
400
401       /* Flush right all items between the MF_HELP and the last item */
402       /* (if several lines, only move the last line) */
403     if (helpPos != -1)
404     {
405         lpitem = &items[lppop->nItems-1];
406         orgY = lpitem->rect.top;
407         orgX = lprect->right;
408         for (i = lppop->nItems - 1; i >= helpPos; i--, lpitem--)
409         {
410             if (lpitem->rect.top != orgY) break;    /* Other line */
411             if (lpitem->rect.right >= orgX) break;  /* Too far right already */
412             lpitem->rect.left += orgX - lpitem->rect.right;
413             lpitem->rect.right = orgX;
414             orgX = lpitem->rect.left;
415         }
416     }
417 }
418
419
420 /***********************************************************************
421  *           MENU_DrawMenuItem
422  *
423  * Draw a single menu item.
424  */
425 static void MENU_DrawMenuItem( HDC hdc, LPMENUITEM lpitem,
426                                WORD height, BOOL menuBar )
427 {
428     RECT rect;
429
430     if (menuBar && (lpitem->item_flags & MF_SEPARATOR)) return;
431     rect = lpitem->rect;
432
433       /* Draw the background */
434
435     if (lpitem->item_flags & MF_HILITE)
436         FillRect( hdc, &rect, sysColorObjects.hbrushHighlight );
437     else FillRect( hdc, &rect, sysColorObjects.hbrushMenu );
438     SetBkMode( hdc, TRANSPARENT );
439
440       /* Draw the separator bar (if any) */
441
442     if (!menuBar && (lpitem->item_flags & MF_MENUBARBREAK))
443     {
444         SelectObject( hdc, sysColorObjects.hpenWindowFrame );
445         MoveTo( hdc, rect.left, 0 );
446         LineTo( hdc, rect.left, height );
447     }
448     if (lpitem->item_flags & MF_SEPARATOR)
449     {
450         SelectObject( hdc, sysColorObjects.hpenWindowFrame );
451         MoveTo( hdc, rect.left, rect.top + SEPARATOR_HEIGHT/2 );
452         LineTo( hdc, rect.right, rect.top + SEPARATOR_HEIGHT/2 );
453         return;
454     }
455
456       /* Setup colors */
457
458     if (lpitem->item_flags & MF_HILITE)
459     {
460         if (lpitem->item_flags & MF_GRAYED)
461             SetTextColor( hdc, GetSysColor( COLOR_GRAYTEXT ) );
462         else
463             SetTextColor( hdc, GetSysColor( COLOR_HIGHLIGHTTEXT ) );
464         SetBkColor( hdc, GetSysColor( COLOR_HIGHLIGHT ) );
465     }
466     else
467     {
468         if (lpitem->item_flags & MF_GRAYED)
469             SetTextColor( hdc, GetSysColor( COLOR_GRAYTEXT ) );
470         else
471             SetTextColor( hdc, GetSysColor( COLOR_MENUTEXT ) );
472         SetBkColor( hdc, GetSysColor( COLOR_MENU ) );
473     }
474
475     if (!menuBar)
476     {
477           /* Draw the check mark */
478
479         if (lpitem->item_flags & MF_CHECKED)
480         {
481             GRAPH_DrawBitmap(hdc, lpitem->hCheckBit ? lpitem->hCheckBit :
482                              hStdCheck, rect.left,
483                              (rect.top+rect.bottom-check_bitmap_height) / 2,
484                              0, 0, check_bitmap_width, check_bitmap_height );
485         }
486         else if (lpitem->hUnCheckBit != 0)  /* Not checked */
487         {
488             GRAPH_DrawBitmap(hdc, lpitem->hUnCheckBit, rect.left,
489                              (rect.top+rect.bottom-check_bitmap_height) / 2,
490                              0, 0, check_bitmap_width, check_bitmap_height );
491         }
492
493           /* Draw the popup-menu arrow */
494
495         if (lpitem->item_flags & MF_POPUP)
496         {
497             GRAPH_DrawBitmap( hdc, hStdMnArrow,
498                               rect.right-arrow_bitmap_width-1,
499                               (rect.top+rect.bottom-arrow_bitmap_height) / 2,
500                               0, 0, arrow_bitmap_width, arrow_bitmap_height );
501         }
502
503         rect.left += check_bitmap_width;
504         rect.right -= arrow_bitmap_width;
505     }
506
507       /* Draw the item text or bitmap */
508
509     if (lpitem->item_flags & MF_BITMAP)
510     {
511         GRAPH_DrawBitmap( hdc, (HBITMAP)lpitem->hText, rect.left, rect.top,
512                           0, 0, rect.right-rect.left, rect.bottom-rect.top );
513         return;
514     }
515     /* No bitmap - process text if present */
516     else if ((lpitem->item_text) != ((char *) NULL)) 
517     {
518         register int i;
519
520         if (menuBar)
521         {
522             rect.left += MENU_BAR_ITEMS_SPACE / 2;
523             rect.right -= MENU_BAR_ITEMS_SPACE / 2;
524             i = strlen( lpitem->item_text );
525         }
526         else
527         {
528             for (i = 0; lpitem->item_text[i]; i++)
529                 if ((lpitem->item_text[i] == '\t') || 
530                     (lpitem->item_text[i] == '\b')) break;
531         }
532         
533         DrawText( hdc, lpitem->item_text, i, &rect,
534                  DT_LEFT | DT_VCENTER | DT_SINGLELINE );
535
536         if (lpitem->item_text[i])  /* There's a tab or flush-right char */
537         {
538             if (lpitem->item_text[i] == '\t')
539             {
540                 rect.left = lpitem->xTab;
541                 DrawText( hdc, lpitem->item_text + i + 1, -1, &rect,
542                           DT_LEFT | DT_VCENTER | DT_SINGLELINE );
543             }
544             else DrawText( hdc, lpitem->item_text + i + 1, -1, &rect,
545                            DT_RIGHT | DT_VCENTER | DT_SINGLELINE );
546         }
547     }
548 }
549
550
551 /***********************************************************************
552  *           MENU_DrawPopupMenu
553  *
554  * Paint a popup menu.
555  */
556 static void MENU_DrawPopupMenu( HWND hwnd, HDC hdc, HMENU hmenu )
557 {
558     POPUPMENU *menu;
559     MENUITEM *item;
560     RECT rect;
561     int i;
562
563     GetClientRect( hwnd, &rect );
564     FillRect( hdc, &rect, sysColorObjects.hbrushMenu );
565     menu = (POPUPMENU *) USER_HEAP_ADDR( hmenu );
566     if (!menu || !menu->nItems) return;
567     item = (MENUITEM *) USER_HEAP_ADDR( menu->hItems );
568     for (i = menu->nItems; i > 0; i--, item++)
569         MENU_DrawMenuItem( hdc, item, menu->Height, FALSE );
570 }
571
572
573 /***********************************************************************
574  *           MENU_DrawMenuBar
575  *
576  * Paint a menu bar. Returns the height of the menu bar.
577  */
578 WORD MENU_DrawMenuBar(HDC hDC, LPRECT lprect, HWND hwnd, BOOL suppress_draw)
579 {
580     LPPOPUPMENU lppop;
581     LPMENUITEM lpitem;
582     int i;
583     WND *wndPtr = WIN_FindWndPtr( hwnd );
584     
585     lppop = (LPPOPUPMENU) USER_HEAP_ADDR( wndPtr->wIDmenu );
586     if (lppop == NULL || lprect == NULL) return SYSMETRICS_CYMENU;
587     dprintf_menu(stddeb,"MENU_DrawMenuBar(%04X, %p, %p); !\n", 
588                  hDC, lprect, lppop);
589     if (lppop->Height == 0) MENU_MenuBarCalcSize(hDC, lprect, lppop, hwnd);
590     lprect->bottom = lprect->top + lppop->Height;
591     if (suppress_draw) return lppop->Height;
592     
593     FillRect(hDC, lprect, sysColorObjects.hbrushMenu );
594     SelectObject( hDC, sysColorObjects.hpenWindowFrame );
595     MoveTo( hDC, lprect->left, lprect->bottom );
596     LineTo( hDC, lprect->right, lprect->bottom );
597
598     if (lppop->nItems == 0) return SYSMETRICS_CYMENU;
599     lpitem = (MENUITEM *) USER_HEAP_ADDR( lppop->hItems );
600     for (i = 0; i < lppop->nItems; i++, lpitem++)
601     {
602         MENU_DrawMenuItem( hDC, lpitem, lppop->Height, TRUE );
603     }
604     return lppop->Height;
605
606
607
608 /***********************************************************************
609  *           MENU_ShowPopup
610  *
611  * Display a popup menu.
612  */
613 static BOOL MENU_ShowPopup(HWND hwndOwner, HMENU hmenu, WORD id, int x, int y)
614 {
615     POPUPMENU *menu;
616
617     if (!(menu = (POPUPMENU *) USER_HEAP_ADDR( hmenu ))) return FALSE;
618     if (menu->FocusedItem != NO_SELECTED_ITEM)
619     {
620         MENUITEM *item = (MENUITEM *) USER_HEAP_ADDR( menu->hItems );
621         item[menu->FocusedItem].item_flags &= ~(MF_HILITE | MF_MOUSESELECT);
622         menu->FocusedItem = NO_SELECTED_ITEM;
623     }
624     SendMessage( hwndOwner, WM_INITMENUPOPUP, hmenu,
625                  MAKELONG( id, (menu->wFlags & MF_POPUP) ? 1 : 0 ));
626     MENU_PopupMenuCalcSize( menu, hwndOwner );
627     if (!menu->hWnd)
628     {
629         WND *wndPtr = WIN_FindWndPtr( hwndOwner );
630         if (!wndPtr) return FALSE;
631         menu->hWnd = CreateWindow( POPUPMENU_CLASS_NAME, "",
632                                    WS_POPUP | WS_BORDER, x, y, 
633                                    menu->Width + 2*SYSMETRICS_CXBORDER,
634                                    menu->Height + 2*SYSMETRICS_CYBORDER,
635                                    0, 0, wndPtr->hInstance,
636                                    (LPSTR)(DWORD)hmenu );
637         if (!menu->hWnd) return FALSE;
638     }
639     else SetWindowPos( menu->hWnd, 0, x, y,
640                        menu->Width + 2*SYSMETRICS_CXBORDER,
641                        menu->Height + 2*SYSMETRICS_CYBORDER,
642                        SWP_NOACTIVATE | SWP_NOZORDER );
643
644       /* Display the window */
645
646     SetWindowPos( menu->hWnd, HWND_TOP, 0, 0, 0, 0,
647                   SWP_SHOWWINDOW | SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE );
648     UpdateWindow( menu->hWnd );
649     return TRUE;
650 }
651
652
653 /***********************************************************************
654  *           MENU_SelectItem
655  */
656 static void MENU_SelectItem( HMENU hmenu, WORD wIndex )
657 {
658     MENUITEM *items;
659     LPPOPUPMENU lppop;
660     HDC hdc;
661
662     lppop = (POPUPMENU *) USER_HEAP_ADDR( hmenu );
663     if (!lppop->nItems) return;
664     items = (MENUITEM *) USER_HEAP_ADDR( lppop->hItems );
665     if ((wIndex != NO_SELECTED_ITEM) && 
666         (wIndex != SYSMENU_SELECTED) &&
667         (items[wIndex].item_flags & MF_SEPARATOR))
668         wIndex = NO_SELECTED_ITEM;
669     if (lppop->FocusedItem == wIndex) return;
670     if (lppop->wFlags & MF_POPUP) hdc = GetDC( lppop->hWnd );
671     else hdc = GetDCEx( lppop->hWnd, 0, DCX_CACHE | DCX_WINDOW);
672
673       /* Clear previous highlighted item */
674     if (lppop->FocusedItem != NO_SELECTED_ITEM) 
675     {
676         if (lppop->FocusedItem == SYSMENU_SELECTED)
677             NC_DrawSysButton( lppop->hWnd, hdc, FALSE );
678         else
679         {
680             items[lppop->FocusedItem].item_flags &=~(MF_HILITE|MF_MOUSESELECT);
681             MENU_DrawMenuItem( hdc, &items[lppop->FocusedItem], lppop->Height,
682                                !(lppop->wFlags & MF_POPUP) );
683         }
684     }
685
686       /* Highlight new item (if any) */
687     lppop->FocusedItem = wIndex;
688     if (lppop->FocusedItem != NO_SELECTED_ITEM) 
689     {
690         if (lppop->FocusedItem == SYSMENU_SELECTED)
691             NC_DrawSysButton( lppop->hWnd, hdc, TRUE );
692         else
693         {
694             items[lppop->FocusedItem].item_flags |= MF_HILITE;
695             MENU_DrawMenuItem( hdc, &items[lppop->FocusedItem], lppop->Height,
696                                !(lppop->wFlags & MF_POPUP) );
697             SendMessage(lppop->hWnd, WM_MENUSELECT,
698                         items[lppop->FocusedItem].item_id, 
699                        MAKELONG( hmenu, items[lppop->FocusedItem].item_flags));
700         }
701     }
702     ReleaseDC( lppop->hWnd, hdc );
703 }
704
705
706 /***********************************************************************
707  *           MENU_SelectNextItem
708  */
709 static void MENU_SelectNextItem( HMENU hmenu )
710 {
711     int i;
712     MENUITEM *items;
713     POPUPMENU *menu;
714
715     menu = (POPUPMENU *) USER_HEAP_ADDR( hmenu );
716     if (!menu->nItems) return;
717     items = (MENUITEM *) USER_HEAP_ADDR( menu->hItems );
718     if ((menu->FocusedItem != NO_SELECTED_ITEM) &&
719         (menu->FocusedItem != SYSMENU_SELECTED))
720     {
721         for (i = menu->FocusedItem+1; i < menu->nItems; i++)
722         {
723             if (!(items[i].item_flags & MF_SEPARATOR))
724             {
725                 MENU_SelectItem( hmenu, i );
726                 return;
727             }
728         }
729         if (MENU_HasSysMenu( menu ))
730         {
731             MENU_SelectItem( hmenu, SYSMENU_SELECTED );
732             return;
733         }
734     }
735     for (i = 0; i < menu->nItems; i++)
736     {
737         if (!(items[i].item_flags & MF_SEPARATOR))
738         {
739             MENU_SelectItem( hmenu, i );
740             return;
741         }
742     }
743     if (MENU_HasSysMenu( menu )) MENU_SelectItem( hmenu, SYSMENU_SELECTED );
744 }
745
746
747 /***********************************************************************
748  *           MENU_SelectPrevItem
749  */
750 static void MENU_SelectPrevItem( HMENU hmenu )
751 {
752     int i;
753     MENUITEM *items;
754     POPUPMENU *menu;
755
756     menu = (POPUPMENU *) USER_HEAP_ADDR( hmenu );
757     if (!menu->nItems) return;
758     items = (MENUITEM *) USER_HEAP_ADDR( menu->hItems );
759     if ((menu->FocusedItem != NO_SELECTED_ITEM) &&
760         (menu->FocusedItem != SYSMENU_SELECTED))
761     {
762         for (i = menu->FocusedItem - 1; i >= 0; i--)
763         {
764             if (!(items[i].item_flags & MF_SEPARATOR))
765             {
766                 MENU_SelectItem( hmenu, i );
767                 return;
768             }
769         }
770         if (MENU_HasSysMenu( menu ))
771         {
772             MENU_SelectItem( hmenu, SYSMENU_SELECTED );
773             return;
774         }
775     }
776     for (i = menu->nItems - 1; i > 0; i--)
777     {
778         if (!(items[i].item_flags & MF_SEPARATOR))
779         {
780             MENU_SelectItem( hmenu, i );
781             return;
782         }
783     }
784     if (MENU_HasSysMenu( menu )) MENU_SelectItem( hmenu, SYSMENU_SELECTED );
785 }
786
787
788 /***********************************************************************
789  *           MENU_GetSubPopup
790  *
791  * Return the handle of the selected sub-popup menu (if any).
792  */
793 static HMENU MENU_GetSubPopup( HMENU hmenu )
794 {
795     POPUPMENU *menu;
796     MENUITEM *item;
797
798     menu = (POPUPMENU *) USER_HEAP_ADDR( hmenu );
799     if (menu->FocusedItem == NO_SELECTED_ITEM) return 0;
800     else if (menu->FocusedItem == SYSMENU_SELECTED)
801         return GetSystemMenu( menu->hWnd, FALSE );
802
803     item = ((MENUITEM *)USER_HEAP_ADDR( menu->hItems )) + menu->FocusedItem;
804     if (!(item->item_flags & MF_POPUP) || !(item->item_flags & MF_MOUSESELECT))
805         return 0;
806     return item->item_id;
807 }
808
809
810 /***********************************************************************
811  *           MENU_HideSubPopups
812  *
813  * Hide the sub-popup menus of this menu.
814  */
815 static void MENU_HideSubPopups( HMENU hmenu )
816 {
817     MENUITEM *item;
818     POPUPMENU *menu, *submenu;
819     HMENU hsubmenu;
820
821     if (!(menu = (POPUPMENU *) USER_HEAP_ADDR( hmenu ))) return;
822     if (menu->FocusedItem == NO_SELECTED_ITEM) return;
823     if (menu->FocusedItem == SYSMENU_SELECTED)
824     {
825         hsubmenu = GetSystemMenu( menu->hWnd, FALSE );
826     }
827     else
828     {
829         item = ((MENUITEM *)USER_HEAP_ADDR(menu->hItems)) + menu->FocusedItem;
830         if (!(item->item_flags & MF_POPUP) ||
831             !(item->item_flags & MF_MOUSESELECT)) return;
832         item->item_flags &= ~MF_MOUSESELECT;
833         hsubmenu = item->item_id;
834     }
835     submenu = (POPUPMENU *) USER_HEAP_ADDR( hsubmenu );
836     MENU_HideSubPopups( hsubmenu );
837     if (submenu->hWnd) ShowWindow( submenu->hWnd, SW_HIDE );
838     MENU_SelectItem( hsubmenu, NO_SELECTED_ITEM );
839 }
840
841
842 /***********************************************************************
843  *           MENU_ShowSubPopup
844  *
845  * Display the sub-menu of the selected item of this menu.
846  * Return the handle of the submenu, or hmenu if no submenu to display.
847  */
848 static HMENU MENU_ShowSubPopup( HWND hwndOwner, HMENU hmenu, BOOL selectFirst )
849 {
850     POPUPMENU *menu;
851     MENUITEM *item;
852     WND *wndPtr;
853
854     if (!(menu = (POPUPMENU *) USER_HEAP_ADDR( hmenu ))) return hmenu;
855     if (!(wndPtr = WIN_FindWndPtr( menu->hWnd ))) return hmenu;
856     if (menu->FocusedItem == NO_SELECTED_ITEM) return hmenu;
857     if (menu->FocusedItem == SYSMENU_SELECTED)
858     {
859         MENU_ShowPopup(hwndOwner, wndPtr->hSysMenu, 0, wndPtr->rectClient.left,
860                 wndPtr->rectClient.top - menu->Height - 2*SYSMETRICS_CYBORDER);
861         if (selectFirst) MENU_SelectNextItem( wndPtr->hSysMenu );
862         return wndPtr->hSysMenu;
863     }
864     item = ((MENUITEM *)USER_HEAP_ADDR( menu->hItems )) + menu->FocusedItem;
865     if (!(item->item_flags & MF_POPUP) ||
866         (item->item_flags & (MF_GRAYED | MF_DISABLED))) return hmenu;
867     item->item_flags |= MF_MOUSESELECT;
868     if (menu->wFlags & MF_POPUP)
869     {
870         MENU_ShowPopup( hwndOwner, (HMENU)item->item_id, menu->FocusedItem,
871                  wndPtr->rectWindow.left + item->rect.right-arrow_bitmap_width,
872                  wndPtr->rectWindow.top + item->rect.top );
873     }
874     else
875     {
876         MENU_ShowPopup( hwndOwner, (HMENU)item->item_id, menu->FocusedItem,
877                         wndPtr->rectWindow.left + item->rect.left,
878                         wndPtr->rectWindow.top + item->rect.bottom );
879     }
880     if (selectFirst) MENU_SelectNextItem( (HMENU)item->item_id );
881     return (HMENU)item->item_id;
882 }
883
884
885 /***********************************************************************
886  *           MENU_FindMenuByCoords
887  *
888  * Find the menu containing a given point (in screen coords).
889  */
890 static HMENU MENU_FindMenuByCoords( HMENU hmenu, POINT pt )
891 {
892     POPUPMENU *menu;
893     HWND hwnd;
894
895     if (!(hwnd = WindowFromPoint( pt ))) return 0;
896     while (hmenu)
897     {
898         menu = (POPUPMENU *) USER_HEAP_ADDR( hmenu );
899         if (menu->hWnd == hwnd)
900         {
901             if (!(menu->wFlags & MF_POPUP))
902             {
903                   /* Make sure it's in the menu bar (or in system menu) */
904                 WND *wndPtr = WIN_FindWndPtr( menu->hWnd );
905                 if ((pt.x < wndPtr->rectClient.left) ||
906                     (pt.x >= wndPtr->rectClient.right) ||
907                     (pt.y >= wndPtr->rectClient.top)) return 0;
908                 if (pt.y < wndPtr->rectClient.top - menu->Height)
909                 {
910                     if (!MENU_IsInSysMenu( menu, pt )) return 0;
911                 }
912                 /* else it's in the menu bar */
913             }
914             return hmenu;
915         }
916         hmenu = MENU_GetSubPopup( hmenu );
917     }
918     return 0;
919 }
920
921
922 /***********************************************************************
923  *           MENU_ExecFocusedItem
924  *
925  * Execute a menu item (for instance when user pressed Enter).
926  * Return TRUE if we can go on with menu tracking.
927  */
928 static BOOL MENU_ExecFocusedItem( HWND hwndOwner, HMENU hmenu,
929                                   HMENU *hmenuCurrent )
930 {
931     MENUITEM *item;
932     POPUPMENU *menu = (POPUPMENU *) USER_HEAP_ADDR( hmenu );
933     if (!menu || !menu->nItems || (menu->FocusedItem == NO_SELECTED_ITEM) ||
934         (menu->FocusedItem == SYSMENU_SELECTED)) return TRUE;
935     item = ((MENUITEM *)USER_HEAP_ADDR( menu->hItems )) + menu->FocusedItem;
936     if (!(item->item_flags & MF_POPUP))
937     {
938         if (!(item->item_flags & (MF_GRAYED | MF_DISABLED)))
939         {
940             PostMessage( hwndOwner, (menu->wFlags & MF_SYSMENU) ? 
941                         WM_SYSCOMMAND : WM_COMMAND, item->item_id, 0 );
942             return FALSE;
943         }
944         else return TRUE;
945     }
946     else
947     {
948         *hmenuCurrent = MENU_ShowSubPopup( hwndOwner, hmenu, TRUE );
949         return TRUE;
950     }
951 }
952
953
954 /***********************************************************************
955  *           MENU_ButtonDown
956  *
957  * Handle a button-down event in a menu. Point is in screen coords.
958  * hmenuCurrent is the top-most visible popup.
959  * Return TRUE if we can go on with menu tracking.
960  */
961 static BOOL MENU_ButtonDown( HWND hwndOwner, HMENU hmenu, HMENU *hmenuCurrent,
962                              POINT pt )
963 {
964     POPUPMENU *menu;
965     MENUITEM *item;
966     WORD id;
967
968     if (!hmenu) return FALSE;  /* Outside all menus */
969     menu = (POPUPMENU *) USER_HEAP_ADDR( hmenu );
970     item = MENU_FindItemByCoords( menu, pt.x, pt.y, &id );
971     if (!item)  /* Maybe in system menu */
972     {
973         if (!MENU_IsInSysMenu( menu, pt )) return FALSE;
974         id = SYSMENU_SELECTED;
975     }   
976
977     if (menu->FocusedItem == id)
978     {
979         if (id == SYSMENU_SELECTED) return FALSE;
980         if (item->item_flags & MF_POPUP)
981         {
982             if (item->item_flags & MF_MOUSESELECT)
983             {
984                 if (menu->wFlags & MF_POPUP)
985                 {
986                     MENU_HideSubPopups( hmenu );
987                     *hmenuCurrent = hmenu;
988                 }
989                 else return FALSE;
990             }
991             else *hmenuCurrent = MENU_ShowSubPopup( hwndOwner, hmenu, FALSE );
992         }
993     }
994     else
995     {
996         MENU_HideSubPopups( hmenu );
997         MENU_SelectItem( hmenu, id );
998         *hmenuCurrent = MENU_ShowSubPopup( hwndOwner, hmenu, FALSE );
999     }
1000     return TRUE;
1001 }
1002
1003
1004 /***********************************************************************
1005  *           MENU_ButtonUp
1006  *
1007  * Handle a button-up event in a menu. Point is in screen coords.
1008  * hmenuCurrent is the top-most visible popup.
1009  * Return TRUE if we can go on with menu tracking.
1010  */
1011 static BOOL MENU_ButtonUp( HWND hwndOwner, HMENU hmenu, HMENU *hmenuCurrent,
1012                            POINT pt )
1013 {
1014     POPUPMENU *menu;
1015     MENUITEM *item;
1016     HMENU hsubmenu = 0;
1017     WORD id;
1018
1019     if (!hmenu) return FALSE;  /* Outside all menus */
1020     menu = (POPUPMENU *) USER_HEAP_ADDR( hmenu );
1021     item = MENU_FindItemByCoords( menu, pt.x, pt.y, &id );
1022     if (!item)  /* Maybe in system menu */
1023     {
1024         if (!MENU_IsInSysMenu( menu, pt )) return FALSE;
1025         id = SYSMENU_SELECTED;
1026         hsubmenu = GetSystemMenu( menu->hWnd, FALSE );
1027     }   
1028
1029     if (menu->FocusedItem != id) return FALSE;
1030
1031     if (id != SYSMENU_SELECTED)
1032     {
1033         if (!(item->item_flags & MF_POPUP))
1034         {
1035             return MENU_ExecFocusedItem( hwndOwner, hmenu, hmenuCurrent );
1036         }
1037         hsubmenu = item->item_id;
1038     }
1039       /* Select first item of sub-popup */
1040     MENU_SelectItem( hsubmenu, NO_SELECTED_ITEM );
1041     MENU_SelectNextItem( hsubmenu );
1042     return TRUE;
1043 }
1044
1045
1046 /***********************************************************************
1047  *           MENU_MouseMove
1048  *
1049  * Handle a motion event in a menu. Point is in screen coords.
1050  * hmenuCurrent is the top-most visible popup.
1051  * Return TRUE if we can go on with menu tracking.
1052  */
1053 static BOOL MENU_MouseMove( HWND hwndOwner, HMENU hmenu, HMENU *hmenuCurrent,
1054                             POINT pt )
1055 {
1056     MENUITEM *item;
1057     POPUPMENU *menu = (POPUPMENU *) USER_HEAP_ADDR( hmenu );
1058     WORD id = NO_SELECTED_ITEM;
1059
1060     if (hmenu)
1061     {
1062         item = MENU_FindItemByCoords( menu, pt.x, pt.y, &id );
1063         if (!item)  /* Maybe in system menu */
1064         {
1065             if (!MENU_IsInSysMenu( menu, pt ))
1066                 id = NO_SELECTED_ITEM;  /* Outside all items */
1067             else id = SYSMENU_SELECTED;
1068         }
1069     }   
1070     if (id == NO_SELECTED_ITEM)
1071     {
1072         MENU_SelectItem( *hmenuCurrent, NO_SELECTED_ITEM );
1073     }
1074     else if (menu->FocusedItem != id)
1075     {
1076         MENU_HideSubPopups( hmenu );
1077         MENU_SelectItem( hmenu, id );
1078         *hmenuCurrent = MENU_ShowSubPopup( hwndOwner, hmenu, FALSE );
1079     }
1080     return TRUE;
1081 }
1082
1083
1084 /***********************************************************************
1085  *           MENU_KeyLeft
1086  *
1087  * Handle a VK_LEFT key event in a menu.
1088  * hmenuCurrent is the top-most visible popup.
1089  */
1090 static void MENU_KeyLeft( HWND hwndOwner, HMENU hmenu, HMENU *hmenuCurrent )
1091 {
1092     POPUPMENU *menu;
1093     HMENU hmenutmp, hmenuprev;
1094
1095     menu = (POPUPMENU *) USER_HEAP_ADDR( hmenu );
1096     hmenuprev = hmenutmp = hmenu;
1097     while (hmenutmp != *hmenuCurrent)
1098     {
1099         hmenutmp = MENU_GetSubPopup( hmenuprev );
1100         if (hmenutmp != *hmenuCurrent) hmenuprev = hmenutmp;
1101     }
1102     MENU_HideSubPopups( hmenuprev );
1103
1104     if ((hmenuprev == hmenu) && !(menu->wFlags & MF_POPUP))
1105     {
1106           /* Select previous item on the menu bar */
1107         MENU_SelectPrevItem( hmenu );
1108         if (*hmenuCurrent != hmenu)
1109         {
1110               /* A popup menu was displayed -> display the next one */
1111             *hmenuCurrent = MENU_ShowSubPopup( hwndOwner, hmenu, TRUE );
1112         }
1113     }
1114     else *hmenuCurrent = hmenuprev;
1115 }
1116
1117
1118 /***********************************************************************
1119  *           MENU_KeyRight
1120  *
1121  * Handle a VK_RIGHT key event in a menu.
1122  * hmenuCurrent is the top-most visible popup.
1123  */
1124 static void MENU_KeyRight( HWND hwndOwner, HMENU hmenu, HMENU *hmenuCurrent )
1125 {
1126     POPUPMENU *menu;
1127     HMENU hmenutmp;
1128
1129     menu = (POPUPMENU *) USER_HEAP_ADDR( hmenu );
1130
1131     if ((menu->wFlags & MF_POPUP) || (*hmenuCurrent != hmenu))
1132     {
1133           /* If already displaying a popup, try to display sub-popup */
1134         hmenutmp = MENU_ShowSubPopup( hwndOwner, *hmenuCurrent, TRUE );
1135         if (hmenutmp != *hmenuCurrent)  /* Sub-popup displayed */
1136         {
1137             *hmenuCurrent = hmenutmp;
1138             return;
1139         }
1140     }
1141
1142       /* If on menu-bar, go to next item */
1143     if (!(menu->wFlags & MF_POPUP))
1144     {
1145         MENU_HideSubPopups( hmenu );
1146         MENU_SelectNextItem( hmenu );
1147         if (*hmenuCurrent != hmenu)
1148         {
1149               /* A popup menu was displayed -> display the next one */
1150             *hmenuCurrent = MENU_ShowSubPopup( hwndOwner, hmenu, TRUE );
1151         }
1152     }
1153     else if (*hmenuCurrent != hmenu)  /* Hide last level popup */
1154     {
1155         HMENU hmenuprev;
1156         hmenuprev = hmenutmp = hmenu;
1157         while (hmenutmp != *hmenuCurrent)
1158         {
1159             hmenutmp = MENU_GetSubPopup( hmenuprev );
1160             if (hmenutmp != *hmenuCurrent) hmenuprev = hmenutmp;
1161         }
1162         MENU_HideSubPopups( hmenuprev );
1163         *hmenuCurrent = hmenuprev;
1164     }
1165 }
1166
1167
1168 /***********************************************************************
1169  *           MENU_TrackMenu
1170  *
1171  * Menu tracking code.
1172  * If 'x' and 'y' are not 0, we simulate a button-down event at (x,y)
1173  * before beginning tracking. This is to help menu-bar tracking.
1174  */
1175 static BOOL MENU_TrackMenu( HMENU hmenu, WORD wFlags, int x, int y,
1176                             HWND hwnd, LPRECT lprect )
1177 {
1178     MSG msg;
1179     POPUPMENU *menu;
1180     HMENU hmenuCurrent = hmenu;
1181     BOOL fClosed = FALSE;
1182     WORD pos;
1183
1184     fEndMenuCalled = FALSE;
1185     if (!(menu = (POPUPMENU *) USER_HEAP_ADDR( hmenu ))) return FALSE;
1186     if (x && y)
1187     {
1188         POINT pt = { x, y };
1189         MENU_ButtonDown( hwnd, hmenu, &hmenuCurrent, pt );
1190     }
1191     SetCapture( hwnd );
1192
1193     while (!fClosed)
1194     {
1195         if (!MSG_InternalGetMessage( &msg, 0, hwnd, MSGF_MENU, 0, TRUE ))
1196             break;
1197
1198         if ((msg.message >= WM_MOUSEFIRST) && (msg.message <= WM_MOUSELAST))
1199         {
1200               /* Find the sub-popup for this mouse event (if any) */
1201             HMENU hsubmenu = MENU_FindMenuByCoords( hmenu, msg.pt );
1202
1203             switch(msg.message)
1204             {
1205             case WM_RBUTTONDOWN:
1206             case WM_NCRBUTTONDOWN:
1207                 if (!(wFlags & TPM_RIGHTBUTTON)) break;
1208                 /* fall through */
1209             case WM_LBUTTONDOWN:
1210             case WM_NCLBUTTONDOWN:
1211                 fClosed = !MENU_ButtonDown( hwnd, hsubmenu,
1212                                             &hmenuCurrent, msg.pt );
1213                 break;
1214                 
1215             case WM_RBUTTONUP:
1216             case WM_NCRBUTTONUP:
1217                 if (!(wFlags & TPM_RIGHTBUTTON)) break;
1218                 /* fall through */
1219             case WM_LBUTTONUP:
1220             case WM_NCLBUTTONUP:
1221                   /* If outside all menus but inside lprect, ignore it */
1222                 if (!hsubmenu && lprect && PtInRect( lprect, msg.pt )) break;
1223                 fClosed = !MENU_ButtonUp( hwnd, hsubmenu,
1224                                           &hmenuCurrent, msg.pt );
1225                 break;
1226                 
1227             case WM_MOUSEMOVE:
1228             case WM_NCMOUSEMOVE:
1229                 if ((msg.wParam & MK_LBUTTON) ||
1230                     ((wFlags & TPM_RIGHTBUTTON) && (msg.wParam & MK_RBUTTON)))
1231                 {
1232                     fClosed = !MENU_MouseMove( hwnd, hsubmenu,
1233                                                &hmenuCurrent, msg.pt );
1234                 }
1235                 break;
1236             }
1237         }
1238         else if ((msg.message >= WM_KEYFIRST) && (msg.message <= WM_KEYLAST))
1239         {
1240             switch(msg.message)
1241             {
1242             case WM_KEYDOWN:
1243                 switch(msg.wParam)
1244                 {
1245                 case VK_HOME:
1246                     MENU_SelectItem( hmenuCurrent, NO_SELECTED_ITEM );
1247                     MENU_SelectNextItem( hmenuCurrent );
1248                     break;
1249
1250                 case VK_END:
1251                     MENU_SelectItem( hmenuCurrent, NO_SELECTED_ITEM );
1252                     MENU_SelectPrevItem( hmenuCurrent );
1253                     break;
1254
1255                 case VK_UP:
1256                     MENU_SelectPrevItem( hmenuCurrent );
1257                     break;
1258
1259                 case VK_DOWN:
1260                       /* If on menu bar, pull-down the menu */
1261                     if (!(menu->wFlags & MF_POPUP) && (hmenuCurrent == hmenu))
1262                         hmenuCurrent = MENU_ShowSubPopup( hwnd, hmenu, TRUE );
1263                     else
1264                         MENU_SelectNextItem( hmenuCurrent );
1265                     break;
1266
1267                 case VK_LEFT:
1268                     MENU_KeyLeft( hwnd, hmenu, &hmenuCurrent );
1269                     break;
1270                     
1271                 case VK_RIGHT:
1272                     MENU_KeyRight( hwnd, hmenu, &hmenuCurrent );
1273                     break;
1274                     
1275                 case VK_SPACE:
1276                 case VK_RETURN:
1277                     fClosed = !MENU_ExecFocusedItem( hwnd, hmenuCurrent,
1278                                                      &hmenuCurrent );
1279                     break;
1280
1281                 case VK_ESCAPE:
1282                     fClosed = TRUE;
1283                     break;
1284
1285                 default:
1286                     break;
1287                 }
1288                 break;  /* WM_KEYDOWN */
1289
1290             case WM_SYSKEYDOWN:
1291                 switch(msg.wParam)
1292                 {
1293                 case VK_MENU:
1294                     fClosed = TRUE;
1295                     break;
1296                     
1297                 }
1298                 break;  /* WM_SYSKEYDOWN */
1299
1300             case WM_CHAR:
1301                 {
1302                       /* Hack to avoid control chars. */
1303                       /* We will find a better way real soon... */
1304                     if ((msg.wParam <= 32) || (msg.wParam >= 127)) break;
1305                     pos = MENU_FindItemByKey( hwnd, hmenuCurrent, msg.wParam );
1306                     if (pos == (WORD)-2) fClosed = TRUE;
1307                     else if (pos == (WORD)-1) MessageBeep(0);
1308                     else
1309                     {
1310                         MENU_SelectItem( hmenuCurrent, pos );
1311                         fClosed = !MENU_ExecFocusedItem( hwnd, hmenuCurrent,
1312                                                          &hmenuCurrent );
1313                         
1314                     }
1315                 }                   
1316                 break;  /* WM_CHAR */
1317             }  /* switch(msg.message) */
1318         }
1319         else
1320         {
1321             DispatchMessage( &msg );
1322         }
1323         if (fEndMenuCalled) fClosed = TRUE;
1324
1325         if (!fClosed)  /* Remove the message from the queue */
1326             PeekMessage( &msg, 0, 0, 0, PM_REMOVE );
1327     }
1328     ReleaseCapture();
1329     MENU_HideSubPopups( hmenu );
1330     if (menu->wFlags & MF_POPUP) ShowWindow( menu->hWnd, SW_HIDE );
1331     MENU_SelectItem( hmenu, NO_SELECTED_ITEM );
1332     fEndMenuCalled = FALSE;
1333     return TRUE;
1334 }
1335
1336
1337 /***********************************************************************
1338  *           MENU_TrackMouseMenuBar
1339  *
1340  * Menu-bar tracking upon a mouse event. Called from NC_HandleSysCommand().
1341  */
1342 void MENU_TrackMouseMenuBar( HWND hwnd, POINT pt )
1343 {
1344     WND *wndPtr = WIN_FindWndPtr( hwnd );
1345     SendMessage( hwnd, WM_ENTERMENULOOP, 0, 0 );
1346     MENU_TrackMenu( (HMENU)wndPtr->wIDmenu, TPM_LEFTALIGN | TPM_LEFTBUTTON,
1347                     pt.x, pt.y, hwnd, NULL );
1348     SendMessage( hwnd, WM_EXITMENULOOP, 0, 0 );
1349 }
1350
1351
1352 /***********************************************************************
1353  *           MENU_TrackKbdMenuBar
1354  *
1355  * Menu-bar tracking upon a keyboard event. Called from NC_HandleSysCommand().
1356  */
1357 void MENU_TrackKbdMenuBar( HWND hwnd, WORD wParam )
1358 {
1359     WND *wndPtr = WIN_FindWndPtr( hwnd );
1360     SendMessage( hwnd, WM_ENTERMENULOOP, 0, 0 );
1361       /* Select first selectable item */
1362     MENU_SelectItem( wndPtr->wIDmenu, NO_SELECTED_ITEM );
1363     MENU_SelectNextItem( (HMENU)wndPtr->wIDmenu );
1364     MENU_TrackMenu( (HMENU)wndPtr->wIDmenu, TPM_LEFTALIGN | TPM_LEFTBUTTON,
1365                     0, 0, hwnd, NULL );
1366     SendMessage( hwnd, WM_EXITMENULOOP, 0, 0 );
1367 }
1368
1369
1370 /**********************************************************************
1371  *                      TrackPopupMenu          [USER.416]
1372  */
1373 BOOL TrackPopupMenu( HMENU hMenu, WORD wFlags, short x, short y,
1374                      short nReserved, HWND hWnd, LPRECT lpRect )
1375 {
1376     if (!MENU_ShowPopup( hWnd, hMenu, 0, x, y )) return FALSE;
1377     return MENU_TrackMenu( hMenu, wFlags, 0, 0, hWnd, lpRect );
1378 }
1379
1380
1381 /***********************************************************************
1382  *           PopupMenuWndProc
1383  */
1384 LONG PopupMenuWndProc( HWND hwnd, WORD message, WORD wParam, LONG lParam )
1385 {    
1386     switch(message)
1387     {
1388     case WM_CREATE:
1389         {
1390             CREATESTRUCT *createStruct = (CREATESTRUCT *)lParam;
1391             HMENU hmenu = (HMENU) ((int)createStruct->lpCreateParams & 0xffff);
1392             SetWindowWord( hwnd, 0, hmenu );
1393             return 0;
1394         }
1395
1396     case WM_MOUSEACTIVATE:  /* We don't want to be activated */
1397         return MA_NOACTIVATE;
1398
1399     case WM_PAINT:
1400         {
1401             PAINTSTRUCT ps;
1402             BeginPaint( hwnd, &ps );
1403             MENU_DrawPopupMenu( hwnd, ps.hdc,
1404                                 (HMENU)GetWindowWord( hwnd, 0 ) );
1405             EndPaint( hwnd, &ps );
1406             return 0;
1407         }
1408
1409     default:
1410         return DefWindowProc(hwnd, message, wParam, lParam);
1411     }
1412     return 0;
1413 }
1414
1415
1416 /***********************************************************************
1417  *           MENU_GetMenuBarHeight
1418  *
1419  * Compute the size of the menu bar height. Used by NC_HandleNCCalcSize().
1420  */
1421 WORD MENU_GetMenuBarHeight( HWND hwnd, WORD menubarWidth, int orgX, int orgY )
1422 {
1423     HDC hdc;
1424     RECT rectBar;
1425     WND *wndPtr;
1426     LPPOPUPMENU lppop;
1427
1428     if (!(wndPtr = WIN_FindWndPtr( hwnd ))) return 0;
1429     if (!(lppop = (LPPOPUPMENU)USER_HEAP_ADDR( wndPtr->wIDmenu ))) return 0;
1430     hdc = GetDC( hwnd );
1431     SetRect( &rectBar, orgX, orgY, orgX+menubarWidth, orgY+SYSMETRICS_CYMENU );
1432     MENU_MenuBarCalcSize( hdc, &rectBar, lppop, hwnd );
1433     ReleaseDC( hwnd, hdc );
1434     return lppop->Height;
1435 }
1436
1437
1438 /**********************************************************************
1439  *                      ChangeMenu              [USER.153]
1440  */
1441 BOOL ChangeMenu(HMENU hMenu, WORD nPos, LPSTR lpNewItem, 
1442                         WORD wItemID, WORD wFlags)
1443 {
1444         if (wFlags & MF_APPEND)
1445                 return AppendMenu(hMenu, wFlags, wItemID, lpNewItem);
1446         if (wFlags & MF_DELETE)
1447                 return DeleteMenu(hMenu, wItemID, wFlags);
1448         if (wFlags & MF_INSERT) 
1449                 return InsertMenu(hMenu, nPos, wFlags, wItemID, lpNewItem);
1450         if (wFlags & MF_CHANGE) 
1451                 return ModifyMenu(hMenu, nPos, wFlags, wItemID, lpNewItem);
1452         if (wFlags & MF_REMOVE) 
1453                 return RemoveMenu(hMenu, wItemID, wFlags);
1454         return FALSE;
1455 }
1456
1457
1458 /**********************************************************************
1459  *                      CheckMenuItem           [USER.154]
1460  */
1461 BOOL CheckMenuItem(HMENU hMenu, WORD wItemID, WORD wFlags)
1462 {
1463         LPMENUITEM      lpitem;
1464         dprintf_menu(stddeb,"CheckMenuItem (%04X, %04X, %04X) !\n", 
1465                      hMenu, wItemID, wFlags);
1466         if (!(lpitem = MENU_FindItem(&hMenu, &wItemID, wFlags))) return FALSE;
1467         if (wFlags & MF_CHECKED) lpitem->item_flags |= MF_CHECKED;
1468         else lpitem->item_flags &= ~MF_CHECKED;
1469         return TRUE;
1470 }
1471
1472
1473 /**********************************************************************
1474  *                      EnableMenuItem          [USER.155]
1475  */
1476 BOOL EnableMenuItem(HMENU hMenu, WORD wItemID, WORD wFlags)
1477 {
1478     LPMENUITEM  lpitem;
1479     dprintf_menu(stddeb,"EnableMenuItem (%04X, %04X, %04X) !\n", 
1480                  hMenu, wItemID, wFlags);
1481     if (!(lpitem = MENU_FindItem( &hMenu, &wItemID, wFlags ))) return FALSE;
1482
1483       /* We can't have MF_GRAYED and MF_DISABLED together */
1484     if (wFlags & MF_GRAYED)
1485     {
1486         lpitem->item_flags = (lpitem->item_flags & ~MF_DISABLED) | MF_GRAYED;
1487     }
1488     else if (wFlags & MF_DISABLED)
1489     {
1490         lpitem->item_flags = (lpitem->item_flags & ~MF_GRAYED) | MF_DISABLED;
1491     }
1492     else   /* MF_ENABLED */
1493     {
1494         lpitem->item_flags &= ~(MF_GRAYED | MF_DISABLED);
1495     }
1496     return TRUE;
1497 }
1498
1499
1500 /**********************************************************************
1501  *                      GetMenuString           [USER.161]
1502  */
1503 int GetMenuString(HMENU hMenu, WORD wItemID, 
1504                   LPSTR str, short nMaxSiz, WORD wFlags)
1505 {
1506         LPMENUITEM      lpitem;
1507         int             maxsiz;
1508         dprintf_menu(stddeb,"GetMenuString(%04X, %04X, %p, %d, %04X);\n",
1509                                         hMenu, wItemID, str, nMaxSiz, wFlags);
1510         if (str == NULL) return FALSE;
1511         lpitem = MENU_FindItem( &hMenu, &wItemID, wFlags );
1512         if (lpitem != NULL) {
1513                 if (lpitem->item_text != NULL) {
1514                         maxsiz = min(nMaxSiz - 1, strlen(lpitem->item_text));
1515                         strncpy(str, lpitem->item_text, maxsiz + 1);
1516                         }
1517                 else
1518                         maxsiz = 0;
1519                 dprintf_menu(stddeb,"GetMenuString // Found !\n");
1520                 return maxsiz;
1521                 }
1522         return 0;
1523 }
1524
1525
1526 /**********************************************************************
1527  *                      HiliteMenuItem          [USER.162]
1528  */
1529 BOOL HiliteMenuItem(HWND hWnd, HMENU hMenu, WORD wItemID, WORD wHilite)
1530 {
1531     LPPOPUPMENU menu;
1532     LPMENUITEM  lpitem;
1533     dprintf_menu(stddeb,"HiliteMenuItem(%04X, %04X, %04X, %04X);\n", 
1534                                                 hWnd, hMenu, wItemID, wHilite);
1535     if (!(lpitem = MENU_FindItem( &hMenu, &wItemID, wHilite ))) return FALSE;
1536     if (!(menu = (LPPOPUPMENU) USER_HEAP_ADDR(hMenu))) return FALSE;
1537     if (menu->FocusedItem == wItemID) return TRUE;
1538     MENU_HideSubPopups( hMenu );
1539     MENU_SelectItem( hMenu, wItemID );
1540     return TRUE;
1541 }
1542
1543
1544 /**********************************************************************
1545  *                      GetMenuState            [USER.250]
1546  */
1547 WORD GetMenuState(HMENU hMenu, WORD wItemID, WORD wFlags)
1548 {
1549     LPMENUITEM lpitem;
1550     dprintf_menu(stddeb,"GetMenuState(%04X, %04X, %04X);\n", 
1551                  hMenu, wItemID, wFlags);
1552     if (!(lpitem = MENU_FindItem( &hMenu, &wItemID, wFlags ))) return -1;
1553     if (lpitem->item_flags & MF_POPUP)
1554     {
1555         POPUPMENU *menu = (POPUPMENU *) USER_HEAP_ADDR( lpitem->item_id );
1556         if (!menu) return -1;
1557         else return (menu->nItems << 8) | (menu->wFlags & 0xff);
1558     }
1559     else return lpitem->item_flags;
1560 }
1561
1562
1563 /**********************************************************************
1564  *                      GetMenuItemCount                [USER.263]
1565  */
1566 WORD GetMenuItemCount(HMENU hMenu)
1567 {
1568         LPPOPUPMENU     menu;
1569         dprintf_menu(stddeb,"GetMenuItemCount(%04X);\n", hMenu);
1570         menu = (LPPOPUPMENU) USER_HEAP_ADDR(hMenu);
1571         if (menu == NULL) return (WORD)-1;
1572         dprintf_menu(stddeb,"GetMenuItemCount(%04X) return %d \n", 
1573                      hMenu, menu->nItems);
1574         return menu->nItems;
1575 }
1576
1577
1578 /**********************************************************************
1579  *                      GetMenuItemID                   [USER.264]
1580  */
1581 WORD GetMenuItemID(HMENU hMenu, int nPos)
1582 {
1583     LPPOPUPMENU menu;
1584     MENUITEM *item;
1585
1586     dprintf_menu(stddeb,"GetMenuItemID(%04X, %d);\n", hMenu, nPos);
1587     if (!(menu = (LPPOPUPMENU) USER_HEAP_ADDR(hMenu))) return -1;
1588     if ((nPos < 0) || (nPos >= menu->nItems)) return -1;
1589     item = (MENUITEM *) USER_HEAP_ADDR( menu->hItems );
1590     if (item[nPos].item_flags & MF_POPUP) return -1;
1591     return item[nPos].item_id;
1592 }
1593
1594
1595 /**********************************************************************
1596  *                      InsertMenu              [USER.410]
1597  */
1598 BOOL InsertMenu(HMENU hMenu, WORD nPos, WORD wFlags, WORD wItemID, LPSTR lpNewItem)
1599 {
1600     HANDLE hNewItems;
1601     MENUITEM *lpitem, *newItems;
1602     LPPOPUPMENU menu;
1603     
1604     if (IS_STRING_ITEM(wFlags))
1605         {
1606            dprintf_menu(stddeb,"InsertMenu (%04X, %04X, %04X, %04X, '%s') !\n",
1607                                  hMenu, nPos, wFlags, wItemID, lpNewItem);
1608         }
1609     else
1610            dprintf_menu(stddeb,"InsertMenu (%04X, %04X, %04X, %04X, %p) !\n",
1611                                   hMenu, nPos, wFlags, wItemID, lpNewItem);
1612
1613       /* Find where to insert new item */
1614
1615     if ((wFlags & MF_BYPOSITION) && (nPos == (WORD)-1))
1616     {
1617           /* Special case: append to menu */
1618         if (!(menu = (LPPOPUPMENU) USER_HEAP_ADDR(hMenu))) return FALSE;
1619         nPos = menu->nItems;
1620     }
1621     else
1622     {
1623         if (!MENU_FindItem( &hMenu, &nPos, wFlags )) return FALSE;
1624         if (!(menu = (LPPOPUPMENU) USER_HEAP_ADDR(hMenu))) return FALSE;
1625     }
1626
1627       /* Create new items array */
1628
1629     hNewItems = USER_HEAP_ALLOC( GMEM_MOVEABLE,
1630                                  sizeof(MENUITEM) * (menu->nItems+1) );
1631     if (!hNewItems) return FALSE;
1632     newItems = (MENUITEM *) USER_HEAP_ADDR( hNewItems );
1633     if (menu->nItems > 0)
1634     {
1635           /* Copy the old array into the new */
1636         MENUITEM *oldItems = (MENUITEM *) USER_HEAP_ADDR( menu->hItems );
1637         if (nPos > 0) memcpy( newItems, oldItems, nPos * sizeof(MENUITEM) );
1638         if (nPos < menu->nItems) memcpy( &newItems[nPos+1], &oldItems[nPos],
1639                                         (menu->nItems-nPos)*sizeof(MENUITEM) );
1640
1641         USER_HEAP_FREE( menu->hItems );
1642     }
1643     menu->hItems = hNewItems;
1644     menu->nItems++;
1645
1646       /* Store the new item data */
1647
1648     lpitem = &newItems[nPos];
1649     lpitem->item_flags = wFlags & ~(MF_HILITE | MF_MOUSESELECT);
1650     lpitem->item_id    = wItemID;
1651
1652     if (IS_STRING_ITEM(wFlags))
1653     {
1654           /* Item beginning with a backspace is a help item */
1655         if (lpNewItem[0] == '\b')
1656         {
1657             lpitem->item_flags |= MF_HELP;
1658             lpNewItem++;
1659         }
1660         lpitem->hText = USER_HEAP_ALLOC( GMEM_MOVEABLE, strlen(lpNewItem)+1 );
1661         lpitem->item_text = (char *)USER_HEAP_ADDR( lpitem->hText );
1662         strcpy( lpitem->item_text, lpNewItem );
1663     }
1664     else if (wFlags & MF_BITMAP) lpitem->hText = LOWORD((DWORD)lpNewItem);
1665     else lpitem->item_text = lpNewItem;
1666
1667     if (wFlags & MF_POPUP)  /* Set the MF_POPUP flag on the popup-menu */
1668         ((POPUPMENU *)USER_HEAP_ADDR(wItemID))->wFlags |= MF_POPUP;
1669
1670     SetRectEmpty( &lpitem->rect );
1671     lpitem->hCheckBit   = hStdCheck;
1672     lpitem->hUnCheckBit = 0;
1673     return TRUE;
1674 }
1675
1676
1677 /**********************************************************************
1678  *                      AppendMenu              [USER.411]
1679  */
1680 BOOL AppendMenu(HMENU hMenu, WORD wFlags, WORD wItemID, LPSTR lpNewItem)
1681 {
1682     return InsertMenu( hMenu, -1, wFlags | MF_BYPOSITION, wItemID, lpNewItem );
1683 }
1684
1685
1686 /**********************************************************************
1687  *                      RemoveMenu              [USER.412]
1688  */
1689 BOOL RemoveMenu(HMENU hMenu, WORD nPos, WORD wFlags)
1690 {
1691     LPPOPUPMENU menu;
1692     LPMENUITEM  lpitem;
1693         dprintf_menu(stddeb,"RemoveMenu (%04X, %04X, %04X) !\n", 
1694                      hMenu, nPos, wFlags);
1695     if (!(lpitem = MENU_FindItem( &hMenu, &nPos, wFlags ))) return FALSE;
1696     if (!(menu = (LPPOPUPMENU) USER_HEAP_ADDR(hMenu))) return FALSE;
1697     
1698       /* Remove item */
1699
1700     if (IS_STRING_ITEM(lpitem->item_flags)) USER_HEAP_FREE( lpitem->hText );
1701     if (--menu->nItems == 0)
1702     {
1703         USER_HEAP_FREE( menu->hItems );
1704         menu->hItems = 0;
1705     }
1706     else
1707     {
1708         while(nPos < menu->nItems)
1709         {
1710             *lpitem = *(lpitem+1);
1711             lpitem++;
1712             nPos++;
1713         }
1714         menu->hItems = USER_HEAP_REALLOC( menu->hItems,
1715                                           menu->nItems * sizeof(MENUITEM),
1716                                           GMEM_MOVEABLE );
1717     }
1718     return TRUE;
1719 }
1720
1721
1722 /**********************************************************************
1723  *                      DeleteMenu              [USER.413]
1724  */
1725 BOOL DeleteMenu(HMENU hMenu, WORD nPos, WORD wFlags)
1726 {
1727     MENUITEM *item = MENU_FindItem( &hMenu, &nPos, wFlags );
1728     if (!item) return FALSE;
1729     if (item->item_flags & MF_POPUP) DestroyMenu( item->item_id );
1730       /* nPos is now the position of the item */
1731     RemoveMenu( hMenu, nPos, wFlags | MF_BYPOSITION );
1732     return TRUE;
1733 }
1734
1735
1736 /**********************************************************************
1737  *                      ModifyMenu              [USER.414]
1738  */
1739 BOOL ModifyMenu(HMENU hMenu, WORD nPos, WORD wFlags, WORD wItemID, LPSTR lpNewItem)
1740 {
1741     LPMENUITEM  lpitem;
1742     if (IS_STRING_ITEM(wFlags))
1743         dprintf_menu(stddeb,"ModifyMenu (%04X, %04X, %04X, %04X, '%s') !\n",
1744                hMenu, nPos, wFlags, wItemID, lpNewItem);
1745     else
1746         dprintf_menu(stddeb,"ModifyMenu (%04X, %04X, %04X, %04X, %p) !\n",
1747                hMenu, nPos, wFlags, wItemID, lpNewItem);
1748     if (!(lpitem = MENU_FindItem( &hMenu, &nPos, wFlags ))) return FALSE;
1749     
1750     if (IS_STRING_ITEM(lpitem->item_flags)) USER_HEAP_FREE( lpitem->hText );
1751     lpitem->item_flags = wFlags & ~(MF_HILITE | MF_MOUSESELECT);
1752     lpitem->item_id    = wItemID;
1753
1754     if (IS_STRING_ITEM(wFlags))
1755     {
1756         lpitem->hText = USER_HEAP_ALLOC( GMEM_MOVEABLE, strlen(lpNewItem)+1 );
1757         lpitem->item_text = (char *)USER_HEAP_ADDR( lpitem->hText );
1758         strcpy( lpitem->item_text, lpNewItem );
1759     }
1760     else if (wFlags & MF_BITMAP) lpitem->hText = LOWORD((DWORD)lpNewItem);
1761     else lpitem->item_text = lpNewItem;
1762     SetRectEmpty( &lpitem->rect );
1763     return TRUE;
1764 }
1765
1766
1767 /**********************************************************************
1768  *                      CreatePopupMenu         [USER.415]
1769  */
1770 HMENU CreatePopupMenu()
1771 {
1772     HMENU hmenu;
1773     POPUPMENU *menu;
1774
1775     if (!(hmenu = CreateMenu())) return 0;
1776     menu = (POPUPMENU *) USER_HEAP_ADDR( hmenu );
1777     menu->wFlags |= MF_POPUP;
1778     return hmenu;
1779 }
1780
1781
1782 /**********************************************************************
1783  *                      GetMenuCheckMarkDimensions      [USER.417]
1784  */
1785 DWORD GetMenuCheckMarkDimensions()
1786 {
1787     return MAKELONG( check_bitmap_width, check_bitmap_height );
1788 }
1789
1790
1791 /**********************************************************************
1792  *                      SetMenuItemBitmaps      [USER.418]
1793  */
1794 BOOL SetMenuItemBitmaps(HMENU hMenu, WORD nPos, WORD wFlags,
1795                 HBITMAP hNewCheck, HBITMAP hNewUnCheck)
1796 {
1797     LPMENUITEM lpitem;
1798    dprintf_menu(stddeb,"SetMenuItemBitmaps (%04X, %04X, %04X, %04X, %08X) !\n",
1799             hMenu, nPos, wFlags, hNewCheck, hNewUnCheck);
1800     if (!(lpitem = MENU_FindItem( &hMenu, &nPos, wFlags ))) return FALSE;
1801
1802     if (!hNewCheck && !hNewUnCheck)
1803     {
1804           /* If both are NULL, restore default bitmaps */
1805         lpitem->hCheckBit   = hStdCheck;
1806         lpitem->hUnCheckBit = 0;
1807         lpitem->item_flags &= ~MF_USECHECKBITMAPS;
1808     }
1809     else  /* Install new bitmaps */
1810     {
1811         lpitem->hCheckBit   = hNewCheck;
1812         lpitem->hUnCheckBit = hNewUnCheck;
1813         lpitem->item_flags |= MF_USECHECKBITMAPS;
1814     }
1815     return TRUE;
1816 }
1817
1818
1819 /**********************************************************************
1820  *                      CreateMenu              [USER.151]
1821  */
1822 HMENU CreateMenu()
1823 {
1824     HMENU hMenu;
1825     LPPOPUPMENU menu;
1826     dprintf_menu(stddeb,"CreateMenu !\n");
1827     if (!(hMenu = USER_HEAP_ALLOC( GMEM_MOVEABLE, sizeof(POPUPMENU) )))
1828         return 0;
1829     menu = (LPPOPUPMENU) USER_HEAP_ADDR(hMenu);
1830     menu->hNext  = 0;
1831     menu->wFlags = 0;
1832     menu->wMagic = MENU_MAGIC;
1833     menu->hTaskQ = 0;
1834     menu->Width  = 0;
1835     menu->Height = 0;
1836     menu->nItems = 0;
1837     menu->hWnd   = 0;
1838     menu->hItems = 0;
1839     menu->FocusedItem = NO_SELECTED_ITEM;
1840     dprintf_menu(stddeb,"CreateMenu // return %04X\n", hMenu);
1841     return hMenu;
1842 }
1843
1844
1845 /**********************************************************************
1846  *                      DestroyMenu             [USER.152]
1847  */
1848 BOOL DestroyMenu(HMENU hMenu)
1849 {
1850         LPPOPUPMENU lppop;
1851         dprintf_menu(stddeb,"DestroyMenu (%04X) !\n", hMenu);
1852         if (hMenu == 0) return FALSE;
1853         lppop = (LPPOPUPMENU) USER_HEAP_ADDR(hMenu);
1854         if (lppop == NULL) return FALSE;
1855         if ((lppop->wFlags & MF_POPUP) && lppop->hWnd)
1856             DestroyWindow( lppop->hWnd );
1857
1858         if (lppop->hItems)
1859         {
1860             int i;
1861             MENUITEM *item = (MENUITEM *) USER_HEAP_ADDR( lppop->hItems );
1862             for (i = lppop->nItems; i > 0; i--, item++)
1863             {
1864                 if (item->item_flags & MF_POPUP)
1865                     DestroyMenu( item->item_id );
1866             }
1867             USER_HEAP_FREE( lppop->hItems );
1868         }
1869         USER_HEAP_FREE( hMenu );
1870         dprintf_menu(stddeb,"DestroyMenu (%04X) // End !\n", hMenu);
1871         return TRUE;
1872 }
1873
1874 /**********************************************************************
1875  *                      GetSystemMenu           [USER.156]
1876  */
1877 HMENU GetSystemMenu(HWND hWnd, BOOL bRevert)
1878 {
1879         WND             *wndPtr;
1880         wndPtr = WIN_FindWndPtr(hWnd);
1881         if (!bRevert) {
1882                 return wndPtr->hSysMenu;
1883                 }
1884         else {
1885                 DestroyMenu(wndPtr->hSysMenu);
1886                 wndPtr->hSysMenu = CopySysMenu();
1887                 }
1888         return wndPtr->hSysMenu;
1889 }
1890
1891 /**********************************************************************
1892  *                      SetSystemMenu           [USER.280]
1893  */
1894 BOOL SetSystemMenu(HWND hWnd, HMENU newHmenu)
1895 {
1896     WND *wndPtr;
1897
1898     if ((wndPtr = WIN_FindWndPtr(hWnd)) != NULL) wndPtr->hSysMenu = newHmenu;
1899     return TRUE;
1900 }
1901
1902
1903 /**********************************************************************
1904  *                      GetMenu         [USER.157]
1905  */
1906 HMENU GetMenu(HWND hWnd) 
1907
1908         WND * wndPtr = WIN_FindWndPtr(hWnd);
1909         if (wndPtr == NULL) return 0;
1910         return wndPtr->wIDmenu;
1911 }
1912
1913
1914 /**********************************************************************
1915  *                      SetMenu         [USER.158]
1916  */
1917 BOOL SetMenu(HWND hWnd, HMENU hMenu)
1918 {
1919         LPPOPUPMENU lpmenu;
1920         WND * wndPtr = WIN_FindWndPtr(hWnd);
1921         if (wndPtr == NULL) {
1922                 fprintf(stderr,"SetMenu(%04X, %04X) // Bad window handle !\n",
1923                         hWnd, hMenu);
1924                 return FALSE;
1925                 }
1926         dprintf_menu(stddeb,"SetMenu(%04X, %04X);\n", hWnd, hMenu);
1927         if (GetCapture() == hWnd) ReleaseCapture();
1928         wndPtr->wIDmenu = hMenu;
1929         if (hMenu != 0)
1930         {
1931             lpmenu = (LPPOPUPMENU) USER_HEAP_ADDR(hMenu);
1932             if (lpmenu == NULL) {
1933                 fprintf(stderr,"SetMenu(%04X, %04X) // Bad menu handle !\n", 
1934                         hWnd, hMenu);
1935                 return FALSE;
1936                 }
1937             lpmenu->hWnd = hWnd;
1938             lpmenu->wFlags &= ~MF_POPUP;  /* Can't be a popup */
1939             lpmenu->Height = 0;  /* Make sure we recalculate the size */
1940         }
1941         SetWindowPos( hWnd, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE |
1942                       SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED );
1943         return TRUE;
1944 }
1945
1946
1947
1948 /**********************************************************************
1949  *                      GetSubMenu              [USER.159]
1950  */
1951 HMENU GetSubMenu(HMENU hMenu, short nPos)
1952 {
1953     LPPOPUPMENU lppop;
1954     LPMENUITEM  lpitem;
1955     dprintf_menu(stddeb,"GetSubMenu (%04X, %04X) !\n", hMenu, nPos);
1956     if (!(lppop = (LPPOPUPMENU) USER_HEAP_ADDR(hMenu))) return 0;
1957     if ((WORD)nPos >= lppop->nItems) return 0;
1958     lpitem = (MENUITEM *) USER_HEAP_ADDR( lppop->hItems );
1959     if (!(lpitem[nPos].item_flags & MF_POPUP)) return 0;
1960     return lpitem[nPos].item_id;
1961 }
1962
1963
1964 /**********************************************************************
1965  *                      DrawMenuBar             [USER.160]
1966  */
1967 void DrawMenuBar(HWND hWnd)
1968 {
1969         WND             *wndPtr;
1970         LPPOPUPMENU lppop;
1971         dprintf_menu(stddeb,"DrawMenuBar (%04X)\n", hWnd);
1972         wndPtr = WIN_FindWndPtr(hWnd);
1973         if (wndPtr != NULL && (wndPtr->dwStyle & WS_CHILD) == 0 && 
1974                 wndPtr->wIDmenu != 0) {
1975                 dprintf_menu(stddeb,"DrawMenuBar wIDmenu=%04X \n", 
1976                              wndPtr->wIDmenu);
1977                 lppop = (LPPOPUPMENU) USER_HEAP_ADDR(wndPtr->wIDmenu);
1978                 if (lppop == NULL) return;
1979
1980                 lppop->Height = 0; /* Make sure we call MENU_MenuBarCalcSize */
1981                 SetWindowPos( hWnd, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE |
1982                             SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED );
1983             }
1984 }
1985
1986
1987 /***********************************************************************
1988  *           EndMenu   (USER.187)
1989  */
1990 void EndMenu(void)
1991 {
1992       /* Note: this won't work when we have multiple tasks... */
1993     fEndMenuCalled = TRUE;
1994 }
1995
1996
1997 /***********************************************************************
1998  *           LookupMenuHandle   (USER.217)
1999  */
2000 HMENU LookupMenuHandle( HMENU hmenu, INT id )
2001 {
2002     if (!MENU_FindItem( &hmenu, &id, MF_BYCOMMAND )) return 0;
2003     else return hmenu;
2004 }
2005
2006
2007 /**********************************************************************
2008  *                      LoadMenuIndirect        [USER.220]
2009  */
2010 HMENU LoadMenuIndirect(LPSTR menu_template)
2011 {
2012         HMENU                   hMenu;
2013         MENU_HEADER     *menu_desc;
2014         dprintf_menu(stddeb,"LoadMenuIndirect: menu_template '%p'\n", 
2015                      menu_template);
2016         hMenu = CreateMenu();
2017         menu_desc = (MENU_HEADER *)menu_template;
2018         ParseMenuResource((WORD *)(menu_desc + 1), 0, hMenu); 
2019         return hMenu;
2020 }
2021
2022
2023 /**********************************************************************
2024  *                      CopySysMenu (Internal)
2025  */
2026 HMENU CopySysMenu()
2027 {
2028     HMENU hMenu;
2029     LPPOPUPMENU menu;
2030     extern unsigned char sysres_MENU_SYSMENU[];
2031
2032     hMenu=LoadMenuIndirect(sysres_MENU_SYSMENU);
2033     if(!hMenu){
2034         dprintf_menu(stddeb,"No SYSMENU\n");
2035         return 0;
2036     }
2037     menu = (POPUPMENU*) USER_HEAP_ADDR(hMenu);
2038     menu->wFlags |= MF_SYSMENU|MF_POPUP;
2039     dprintf_menu(stddeb,"CopySysMenu hMenu=%04X !\n", hMenu);
2040     return hMenu;
2041 }
2042
2043
2044 /**********************************************************************
2045  *                      ParseMenuResource (from Resource or Template)
2046  */
2047 WORD * ParseMenuResource(WORD *first_item, int level, HMENU hMenu)
2048 {
2049     WORD        *item;
2050     WORD        *next_item;
2051     HMENU       hSubMenu;
2052     int         i;
2053
2054     level++;
2055     next_item = first_item;
2056     i = 0;
2057     do {
2058         i++;
2059         item = next_item;
2060         if (*item & MF_POPUP) {
2061             MENU_POPUPITEM *popup_item = (MENU_POPUPITEM *) item;
2062             next_item = (WORD *) (popup_item->item_text + 
2063                                   strlen(popup_item->item_text) + 1);
2064             hSubMenu = CreatePopupMenu();
2065             next_item = ParseMenuResource(next_item, level, hSubMenu);
2066             AppendMenu(hMenu, popup_item->item_flags, 
2067                 hSubMenu, popup_item->item_text);
2068             }
2069         else {
2070                 MENUITEMTEMPLATE *normal_item = (MENUITEMTEMPLATE *) item;
2071                 next_item = (WORD *) (normal_item->item_text + 
2072                 strlen(normal_item->item_text) + 1);
2073                 if (strlen(normal_item->item_text) == 0 && normal_item->item_id == 0) 
2074                         normal_item->item_flags |= MF_SEPARATOR;
2075                 AppendMenu(hMenu, normal_item->item_flags, 
2076                         normal_item->item_id, normal_item->item_text);
2077             }
2078         }
2079     while (!(*item & MF_END));
2080     return next_item;
2081 }
2082
2083
2084 /**********************************************************************
2085  *              IsMenu    (USER.358)
2086  */
2087 BOOL IsMenu( HMENU hmenu )
2088 {
2089     LPPOPUPMENU menu;
2090     if (!(menu = (LPPOPUPMENU) USER_HEAP_ADDR( hmenu ))) return FALSE;
2091     return (menu->wMagic == MENU_MAGIC);
2092 }