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