Release 950403
[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_LIN_ADDR(*hmenu))) return NULL;
145     item = (MENUITEM *) USER_HEAP_LIN_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_LIN_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_LIN_ADDR( hmenu );
218     lpitem = (MENUITEM *) USER_HEAP_LIN_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     if (lpitem->item_flags & MF_OWNERDRAW)  {
250       static HANDLE mistrh = 0;
251       static SEGPTR mistrsegp = 0;
252       static LPMEASUREITEMSTRUCT mistruct=NULL;
253       if (mistruct == NULL)  {
254         mistrh = GlobalAlloc(0,sizeof(MEASUREITEMSTRUCT));
255         mistrsegp = WIN16_GlobalLock(mistrh);
256         mistruct = PTR_SEG_TO_LIN(mistrsegp);
257       }
258       mistruct->CtlType = ODT_MENU;
259       mistruct->itemID = lpitem->item_id;
260       mistruct->itemData = (long int)lpitem->item_text;
261       mistruct->itemHeight = 16;
262       mistruct->itemWidth = 30;
263       SendMessage(hwndOwner,WM_MEASUREITEM,0,mistrsegp);
264       lpitem->rect.bottom += mistruct->itemHeight;
265       lpitem->rect.right += mistruct->itemWidth;
266       dprintf_menu(stddeb,"DrawMenuItem: MeasureItem %04x %d:%d!\n",
267                    lpitem->item_id,mistruct->itemWidth, mistruct->itemHeight);
268       return;
269     } 
270
271     if (lpitem->item_flags & MF_SEPARATOR)
272     {
273         lpitem->rect.bottom += SEPARATOR_HEIGHT;
274         return;
275     }
276
277     if (!menuBar)
278     {
279         lpitem->rect.right += 2 * check_bitmap_width;
280         if (lpitem->item_flags & MF_POPUP)
281             lpitem->rect.right += arrow_bitmap_width;
282     }
283
284     if (lpitem->item_flags & MF_BITMAP)
285     {
286         BITMAP bm;
287         GetObject( (HBITMAP)lpitem->hText, sizeof(BITMAP), (LPSTR)&bm );
288         lpitem->rect.right  += bm.bmWidth;
289         lpitem->rect.bottom += bm.bmHeight;
290         return;
291     }
292     
293       /* If we get here, then it is a text item */
294
295     dwSize = (lpitem->item_text == NULL) ? 0 : GetTextExtent( hdc, lpitem->item_text, strlen(lpitem->item_text));
296     lpitem->rect.right  += LOWORD(dwSize);
297     lpitem->rect.bottom += max( HIWORD(dwSize), SYSMETRICS_CYMENU );
298
299     if (menuBar) lpitem->rect.right += MENU_BAR_ITEMS_SPACE;
300     else if ((p = strchr( lpitem->item_text, '\t' )) != NULL)
301     {
302           /* Item contains a tab (only meaningful in popup menus) */
303         lpitem->xTab = check_bitmap_width + MENU_TAB_SPACE + 
304                          LOWORD( GetTextExtent( hdc, lpitem->item_text,
305                                                (int)(p - lpitem->item_text) ));
306         lpitem->rect.right += MENU_TAB_SPACE;
307     }
308     else
309     {
310         if (strchr( lpitem->item_text, '\b' ))
311             lpitem->rect.right += MENU_TAB_SPACE;
312         lpitem->xTab = lpitem->rect.right - check_bitmap_width 
313                         - arrow_bitmap_width;
314     }
315 }
316
317
318 /***********************************************************************
319  *           MENU_PopupMenuCalcSize
320  *
321  * Calculate the size of a popup menu.
322  */
323 static void MENU_PopupMenuCalcSize( LPPOPUPMENU lppop, HWND hwndOwner )
324 {
325     LPMENUITEM  items, lpitem;
326     HDC hdc;
327     int start, i;
328     int orgX, orgY, maxX, maxTab, maxTabWidth;
329
330     lppop->Width = lppop->Height = 0;
331     if (lppop->nItems == 0) return;
332     items = (MENUITEM *)USER_HEAP_LIN_ADDR( lppop->hItems );
333     hdc = GetDC( 0 );
334     maxX = start = 0;
335     while (start < lppop->nItems)
336     {
337         lpitem = &items[start];
338         orgX = maxX;
339         orgY = 0;
340         maxTab = maxTabWidth = 0;
341
342           /* Parse items until column break or end of menu */
343         for (i = start; i < lppop->nItems; i++, lpitem++)
344         {
345             if ((i != start) &&
346                 (lpitem->item_flags & (MF_MENUBREAK | MF_MENUBARBREAK))) break;
347             MENU_CalcItemSize( hdc, lpitem, hwndOwner, orgX, orgY, FALSE );
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_POPUP) ? 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( POPUPMENU_CLASS_NAME, "",
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( 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             SendMessage(lppop->hWnd, WM_MENUSELECT,
742                         items[lppop->FocusedItem].item_id, 
743                        MAKELONG( hmenu, items[lppop->FocusedItem].item_flags));
744         }
745     }
746     ReleaseDC( lppop->hWnd, hdc );
747 }
748
749
750 /***********************************************************************
751  *           MENU_SelectNextItem
752  */
753 static void MENU_SelectNextItem( HMENU hmenu )
754 {
755     int i;
756     MENUITEM *items;
757     POPUPMENU *menu;
758
759     menu = (POPUPMENU *) USER_HEAP_LIN_ADDR( hmenu );
760     if (!menu->nItems) return;
761     items = (MENUITEM *) USER_HEAP_LIN_ADDR( menu->hItems );
762     if ((menu->FocusedItem != NO_SELECTED_ITEM) &&
763         (menu->FocusedItem != SYSMENU_SELECTED))
764     {
765         for (i = menu->FocusedItem+1; i < menu->nItems; i++)
766         {
767             if (!(items[i].item_flags & MF_SEPARATOR))
768             {
769                 MENU_SelectItem( hmenu, i );
770                 return;
771             }
772         }
773         if (MENU_HasSysMenu( menu ))
774         {
775             MENU_SelectItem( hmenu, SYSMENU_SELECTED );
776             return;
777         }
778     }
779     for (i = 0; i < menu->nItems; i++)
780     {
781         if (!(items[i].item_flags & MF_SEPARATOR))
782         {
783             MENU_SelectItem( hmenu, i );
784             return;
785         }
786     }
787     if (MENU_HasSysMenu( menu )) MENU_SelectItem( hmenu, SYSMENU_SELECTED );
788 }
789
790
791 /***********************************************************************
792  *           MENU_SelectPrevItem
793  */
794 static void MENU_SelectPrevItem( HMENU hmenu )
795 {
796     int i;
797     MENUITEM *items;
798     POPUPMENU *menu;
799
800     menu = (POPUPMENU *) USER_HEAP_LIN_ADDR( hmenu );
801     if (!menu->nItems) return;
802     items = (MENUITEM *) USER_HEAP_LIN_ADDR( menu->hItems );
803     if ((menu->FocusedItem != NO_SELECTED_ITEM) &&
804         (menu->FocusedItem != SYSMENU_SELECTED))
805     {
806         for (i = menu->FocusedItem - 1; i >= 0; i--)
807         {
808             if (!(items[i].item_flags & MF_SEPARATOR))
809             {
810                 MENU_SelectItem( hmenu, i );
811                 return;
812             }
813         }
814         if (MENU_HasSysMenu( menu ))
815         {
816             MENU_SelectItem( hmenu, SYSMENU_SELECTED );
817             return;
818         }
819     }
820     for (i = menu->nItems - 1; i > 0; i--)
821     {
822         if (!(items[i].item_flags & MF_SEPARATOR))
823         {
824             MENU_SelectItem( hmenu, i );
825             return;
826         }
827     }
828     if (MENU_HasSysMenu( menu )) MENU_SelectItem( hmenu, SYSMENU_SELECTED );
829 }
830
831
832 /***********************************************************************
833  *           MENU_GetSubPopup
834  *
835  * Return the handle of the selected sub-popup menu (if any).
836  */
837 static HMENU MENU_GetSubPopup( HMENU hmenu )
838 {
839     POPUPMENU *menu;
840     MENUITEM *item;
841
842     menu = (POPUPMENU *) USER_HEAP_LIN_ADDR( hmenu );
843     if (menu->FocusedItem == NO_SELECTED_ITEM) return 0;
844     else if (menu->FocusedItem == SYSMENU_SELECTED)
845         return GetSystemMenu( menu->hWnd, FALSE );
846
847     item = ((MENUITEM *)USER_HEAP_LIN_ADDR(menu->hItems)) + menu->FocusedItem;
848     if (!(item->item_flags & MF_POPUP) || !(item->item_flags & MF_MOUSESELECT))
849         return 0;
850     return item->item_id;
851 }
852
853
854 /***********************************************************************
855  *           MENU_HideSubPopups
856  *
857  * Hide the sub-popup menus of this menu.
858  */
859 static void MENU_HideSubPopups( HMENU hmenu )
860 {
861     MENUITEM *item;
862     POPUPMENU *menu, *submenu;
863     HMENU hsubmenu;
864
865     if (!(menu = (POPUPMENU *) USER_HEAP_LIN_ADDR( hmenu ))) return;
866     if (menu->FocusedItem == NO_SELECTED_ITEM) return;
867     if (menu->FocusedItem == SYSMENU_SELECTED)
868     {
869         hsubmenu = GetSystemMenu( menu->hWnd, FALSE );
870     }
871     else
872     {
873         item = ((MENUITEM *)USER_HEAP_LIN_ADDR(menu->hItems)) + menu->FocusedItem;
874         if (!(item->item_flags & MF_POPUP) ||
875             !(item->item_flags & MF_MOUSESELECT)) return;
876         item->item_flags &= ~MF_MOUSESELECT;
877         hsubmenu = item->item_id;
878     }
879     submenu = (POPUPMENU *) USER_HEAP_LIN_ADDR( hsubmenu );
880     MENU_HideSubPopups( hsubmenu );
881     if (submenu->hWnd) ShowWindow( submenu->hWnd, SW_HIDE );
882     MENU_SelectItem( hsubmenu, NO_SELECTED_ITEM );
883 }
884
885
886 /***********************************************************************
887  *           MENU_ShowSubPopup
888  *
889  * Display the sub-menu of the selected item of this menu.
890  * Return the handle of the submenu, or hmenu if no submenu to display.
891  */
892 static HMENU MENU_ShowSubPopup( HWND hwndOwner, HMENU hmenu, BOOL selectFirst )
893 {
894     POPUPMENU *menu;
895     MENUITEM *item;
896     WND *wndPtr;
897
898     if (!(menu = (POPUPMENU *) USER_HEAP_LIN_ADDR( hmenu ))) return hmenu;
899     if (!(wndPtr = WIN_FindWndPtr( menu->hWnd ))) return hmenu;
900     if (menu->FocusedItem == NO_SELECTED_ITEM) return hmenu;
901     if (menu->FocusedItem == SYSMENU_SELECTED)
902     {
903         MENU_ShowPopup(hwndOwner, wndPtr->hSysMenu, 0, wndPtr->rectClient.left,
904                 wndPtr->rectClient.top - menu->Height - 2*SYSMETRICS_CYBORDER);
905         if (selectFirst) MENU_SelectNextItem( wndPtr->hSysMenu );
906         return wndPtr->hSysMenu;
907     }
908     item = ((MENUITEM *)USER_HEAP_LIN_ADDR(menu->hItems)) + menu->FocusedItem;
909     if (!(item->item_flags & MF_POPUP) ||
910         (item->item_flags & (MF_GRAYED | MF_DISABLED))) return hmenu;
911     item->item_flags |= MF_MOUSESELECT;
912     if (menu->wFlags & MF_POPUP)
913     {
914         MENU_ShowPopup( hwndOwner, (HMENU)item->item_id, menu->FocusedItem,
915                  wndPtr->rectWindow.left + item->rect.right-arrow_bitmap_width,
916                  wndPtr->rectWindow.top + item->rect.top );
917     }
918     else
919     {
920         MENU_ShowPopup( hwndOwner, (HMENU)item->item_id, menu->FocusedItem,
921                         wndPtr->rectWindow.left + item->rect.left,
922                         wndPtr->rectWindow.top + item->rect.bottom );
923     }
924     if (selectFirst) MENU_SelectNextItem( (HMENU)item->item_id );
925     return (HMENU)item->item_id;
926 }
927
928
929 /***********************************************************************
930  *           MENU_FindMenuByCoords
931  *
932  * Find the menu containing a given point (in screen coords).
933  */
934 static HMENU MENU_FindMenuByCoords( HMENU hmenu, POINT pt )
935 {
936     POPUPMENU *menu;
937     HWND hwnd;
938
939     if (!(hwnd = WindowFromPoint( pt ))) return 0;
940     while (hmenu)
941     {
942         menu = (POPUPMENU *) USER_HEAP_LIN_ADDR( hmenu );
943         if (menu->hWnd == hwnd)
944         {
945             if (!(menu->wFlags & MF_POPUP))
946             {
947                   /* Make sure it's in the menu bar (or in system menu) */
948                 WND *wndPtr = WIN_FindWndPtr( menu->hWnd );
949                 if ((pt.x < wndPtr->rectClient.left) ||
950                     (pt.x >= wndPtr->rectClient.right) ||
951                     (pt.y >= wndPtr->rectClient.top)) return 0;
952                 if (pt.y < wndPtr->rectClient.top - menu->Height)
953                 {
954                     if (!MENU_IsInSysMenu( menu, pt )) return 0;
955                 }
956                 /* else it's in the menu bar */
957             }
958             return hmenu;
959         }
960         hmenu = MENU_GetSubPopup( hmenu );
961     }
962     return 0;
963 }
964
965
966 /***********************************************************************
967  *           MENU_ExecFocusedItem
968  *
969  * Execute a menu item (for instance when user pressed Enter).
970  * Return TRUE if we can go on with menu tracking.
971  */
972 static BOOL MENU_ExecFocusedItem( HWND hwndOwner, HMENU hmenu,
973                                   HMENU *hmenuCurrent )
974 {
975     MENUITEM *item;
976     POPUPMENU *menu = (POPUPMENU *) USER_HEAP_LIN_ADDR( hmenu );
977     if (!menu || !menu->nItems || (menu->FocusedItem == NO_SELECTED_ITEM) ||
978         (menu->FocusedItem == SYSMENU_SELECTED)) return TRUE;
979     item = ((MENUITEM *)USER_HEAP_LIN_ADDR(menu->hItems)) + menu->FocusedItem;
980     if (!(item->item_flags & MF_POPUP))
981     {
982         if (!(item->item_flags & (MF_GRAYED | MF_DISABLED)))
983         {
984             PostMessage( hwndOwner, (menu->wFlags & MF_SYSMENU) ? 
985                         WM_SYSCOMMAND : WM_COMMAND, item->item_id, 0 );
986             return FALSE;
987         }
988         else return TRUE;
989     }
990     else
991     {
992         *hmenuCurrent = MENU_ShowSubPopup( hwndOwner, hmenu, TRUE );
993         return TRUE;
994     }
995 }
996
997
998 /***********************************************************************
999  *           MENU_ButtonDown
1000  *
1001  * Handle a button-down event in a menu. Point is in screen coords.
1002  * hmenuCurrent is the top-most visible popup.
1003  * Return TRUE if we can go on with menu tracking.
1004  */
1005 static BOOL MENU_ButtonDown( HWND hwndOwner, HMENU hmenu, HMENU *hmenuCurrent,
1006                              POINT pt )
1007 {
1008     POPUPMENU *menu;
1009     MENUITEM *item;
1010     WORD id;
1011
1012     if (!hmenu) return FALSE;  /* Outside all menus */
1013     menu = (POPUPMENU *) USER_HEAP_LIN_ADDR( hmenu );
1014     item = MENU_FindItemByCoords( menu, pt.x, pt.y, &id );
1015     if (!item)  /* Maybe in system menu */
1016     {
1017         if (!MENU_IsInSysMenu( menu, pt )) return FALSE;
1018         id = SYSMENU_SELECTED;
1019     }   
1020
1021     if (menu->FocusedItem == id)
1022     {
1023         if (id == SYSMENU_SELECTED) return FALSE;
1024         if (item->item_flags & MF_POPUP)
1025         {
1026             if (item->item_flags & MF_MOUSESELECT)
1027             {
1028                 if (menu->wFlags & MF_POPUP)
1029                 {
1030                     MENU_HideSubPopups( hmenu );
1031                     *hmenuCurrent = hmenu;
1032                 }
1033                 else return FALSE;
1034             }
1035             else *hmenuCurrent = MENU_ShowSubPopup( hwndOwner, hmenu, FALSE );
1036         }
1037     }
1038     else
1039     {
1040         MENU_HideSubPopups( hmenu );
1041         MENU_SelectItem( hmenu, id );
1042         *hmenuCurrent = MENU_ShowSubPopup( hwndOwner, hmenu, FALSE );
1043     }
1044     return TRUE;
1045 }
1046
1047
1048 /***********************************************************************
1049  *           MENU_ButtonUp
1050  *
1051  * Handle a button-up event in a menu. Point is in screen coords.
1052  * hmenuCurrent is the top-most visible popup.
1053  * Return TRUE if we can go on with menu tracking.
1054  */
1055 static BOOL MENU_ButtonUp( HWND hwndOwner, HMENU hmenu, HMENU *hmenuCurrent,
1056                            POINT pt )
1057 {
1058     POPUPMENU *menu;
1059     MENUITEM *item;
1060     HMENU hsubmenu = 0;
1061     WORD id;
1062
1063     if (!hmenu) return FALSE;  /* Outside all menus */
1064     menu = (POPUPMENU *) USER_HEAP_LIN_ADDR( hmenu );
1065     item = MENU_FindItemByCoords( menu, pt.x, pt.y, &id );
1066     if (!item)  /* Maybe in system menu */
1067     {
1068         if (!MENU_IsInSysMenu( menu, pt )) return FALSE;
1069         id = SYSMENU_SELECTED;
1070         hsubmenu = GetSystemMenu( menu->hWnd, FALSE );
1071     }   
1072
1073     if (menu->FocusedItem != id) return FALSE;
1074
1075     if (id != SYSMENU_SELECTED)
1076     {
1077         if (!(item->item_flags & MF_POPUP))
1078         {
1079             return MENU_ExecFocusedItem( hwndOwner, hmenu, hmenuCurrent );
1080         }
1081         hsubmenu = item->item_id;
1082     }
1083       /* Select first item of sub-popup */
1084     MENU_SelectItem( hsubmenu, NO_SELECTED_ITEM );
1085     MENU_SelectNextItem( hsubmenu );
1086     return TRUE;
1087 }
1088
1089
1090 /***********************************************************************
1091  *           MENU_MouseMove
1092  *
1093  * Handle a motion event in a menu. Point is in screen coords.
1094  * hmenuCurrent is the top-most visible popup.
1095  * Return TRUE if we can go on with menu tracking.
1096  */
1097 static BOOL MENU_MouseMove( HWND hwndOwner, HMENU hmenu, HMENU *hmenuCurrent,
1098                             POINT pt )
1099 {
1100     MENUITEM *item;
1101     POPUPMENU *menu = (POPUPMENU *) USER_HEAP_LIN_ADDR( hmenu );
1102     WORD id = NO_SELECTED_ITEM;
1103
1104     if (hmenu)
1105     {
1106         item = MENU_FindItemByCoords( menu, pt.x, pt.y, &id );
1107         if (!item)  /* Maybe in system menu */
1108         {
1109             if (!MENU_IsInSysMenu( menu, pt ))
1110                 id = NO_SELECTED_ITEM;  /* Outside all items */
1111             else id = SYSMENU_SELECTED;
1112         }
1113     }   
1114     if (id == NO_SELECTED_ITEM)
1115     {
1116         MENU_SelectItem( *hmenuCurrent, NO_SELECTED_ITEM );
1117     }
1118     else if (menu->FocusedItem != id)
1119     {
1120         MENU_HideSubPopups( hmenu );
1121         MENU_SelectItem( hmenu, id );
1122         *hmenuCurrent = MENU_ShowSubPopup( hwndOwner, hmenu, FALSE );
1123     }
1124     return TRUE;
1125 }
1126
1127
1128 /***********************************************************************
1129  *           MENU_KeyLeft
1130  *
1131  * Handle a VK_LEFT key event in a menu.
1132  * hmenuCurrent is the top-most visible popup.
1133  */
1134 static void MENU_KeyLeft( HWND hwndOwner, HMENU hmenu, HMENU *hmenuCurrent )
1135 {
1136     POPUPMENU *menu;
1137     HMENU hmenutmp, hmenuprev;
1138
1139     menu = (POPUPMENU *) USER_HEAP_LIN_ADDR( hmenu );
1140     hmenuprev = hmenutmp = hmenu;
1141     while (hmenutmp != *hmenuCurrent)
1142     {
1143         hmenutmp = MENU_GetSubPopup( hmenuprev );
1144         if (hmenutmp != *hmenuCurrent) hmenuprev = hmenutmp;
1145     }
1146     MENU_HideSubPopups( hmenuprev );
1147
1148     if ((hmenuprev == hmenu) && !(menu->wFlags & MF_POPUP))
1149     {
1150           /* Select previous item on the menu bar */
1151         MENU_SelectPrevItem( hmenu );
1152         if (*hmenuCurrent != hmenu)
1153         {
1154               /* A popup menu was displayed -> display the next one */
1155             *hmenuCurrent = MENU_ShowSubPopup( hwndOwner, hmenu, TRUE );
1156         }
1157     }
1158     else *hmenuCurrent = hmenuprev;
1159 }
1160
1161
1162 /***********************************************************************
1163  *           MENU_KeyRight
1164  *
1165  * Handle a VK_RIGHT key event in a menu.
1166  * hmenuCurrent is the top-most visible popup.
1167  */
1168 static void MENU_KeyRight( HWND hwndOwner, HMENU hmenu, HMENU *hmenuCurrent )
1169 {
1170     POPUPMENU *menu;
1171     HMENU hmenutmp;
1172
1173     menu = (POPUPMENU *) USER_HEAP_LIN_ADDR( hmenu );
1174
1175     if ((menu->wFlags & MF_POPUP) || (*hmenuCurrent != hmenu))
1176     {
1177           /* If already displaying a popup, try to display sub-popup */
1178         hmenutmp = MENU_ShowSubPopup( hwndOwner, *hmenuCurrent, TRUE );
1179         if (hmenutmp != *hmenuCurrent)  /* Sub-popup displayed */
1180         {
1181             *hmenuCurrent = hmenutmp;
1182             return;
1183         }
1184     }
1185
1186       /* If on menu-bar, go to next item */
1187     if (!(menu->wFlags & MF_POPUP))
1188     {
1189         MENU_HideSubPopups( hmenu );
1190         MENU_SelectNextItem( hmenu );
1191         if (*hmenuCurrent != hmenu)
1192         {
1193               /* A popup menu was displayed -> display the next one */
1194             *hmenuCurrent = MENU_ShowSubPopup( hwndOwner, hmenu, TRUE );
1195         }
1196     }
1197     else if (*hmenuCurrent != hmenu)  /* Hide last level popup */
1198     {
1199         HMENU hmenuprev;
1200         hmenuprev = hmenutmp = hmenu;
1201         while (hmenutmp != *hmenuCurrent)
1202         {
1203             hmenutmp = MENU_GetSubPopup( hmenuprev );
1204             if (hmenutmp != *hmenuCurrent) hmenuprev = hmenutmp;
1205         }
1206         MENU_HideSubPopups( hmenuprev );
1207         *hmenuCurrent = hmenuprev;
1208     }
1209 }
1210
1211
1212 /***********************************************************************
1213  *           MENU_TrackMenu
1214  *
1215  * Menu tracking code.
1216  * If 'x' and 'y' are not 0, we simulate a button-down event at (x,y)
1217  * before beginning tracking. This is to help menu-bar tracking.
1218  */
1219 static BOOL MENU_TrackMenu( HMENU hmenu, WORD wFlags, int x, int y,
1220                             HWND hwnd, LPRECT lprect )
1221 {
1222     MSG *msg;
1223     HLOCAL hMsg;
1224     POPUPMENU *menu;
1225     HMENU hmenuCurrent = hmenu;
1226     BOOL fClosed = FALSE, fRemove;
1227     WORD pos;
1228
1229     fEndMenuCalled = FALSE;
1230     if (!(menu = (POPUPMENU *) USER_HEAP_LIN_ADDR( hmenu ))) return FALSE;
1231     if (x && y)
1232     {
1233         POINT pt = { x, y };
1234         MENU_ButtonDown( hwnd, hmenu, &hmenuCurrent, pt );
1235     }
1236     SetCapture( hwnd );
1237     hMsg = USER_HEAP_ALLOC( sizeof(MSG) );
1238     msg = (MSG *)USER_HEAP_LIN_ADDR( hMsg );
1239     while (!fClosed)
1240     {
1241         if (!MSG_InternalGetMessage( USER_HEAP_SEG_ADDR(hMsg), 0,
1242                                      hwnd, MSGF_MENU, 0, TRUE ))
1243             break;
1244
1245         fRemove = FALSE;
1246         if ((msg->message >= WM_MOUSEFIRST) && (msg->message <= WM_MOUSELAST))
1247         {
1248               /* Find the sub-popup for this mouse event (if any) */
1249             HMENU hsubmenu = MENU_FindMenuByCoords( hmenu, msg->pt );
1250
1251             switch(msg->message)
1252             {
1253             case WM_RBUTTONDOWN:
1254             case WM_NCRBUTTONDOWN:
1255                 if (!(wFlags & TPM_RIGHTBUTTON)) break;
1256                 /* fall through */
1257             case WM_LBUTTONDOWN:
1258             case WM_NCLBUTTONDOWN:
1259                 fClosed = !MENU_ButtonDown( hwnd, hsubmenu,
1260                                             &hmenuCurrent, msg->pt );
1261                 break;
1262                 
1263             case WM_RBUTTONUP:
1264             case WM_NCRBUTTONUP:
1265                 if (!(wFlags & TPM_RIGHTBUTTON)) break;
1266                 /* fall through */
1267             case WM_LBUTTONUP:
1268             case WM_NCLBUTTONUP:
1269                   /* If outside all menus but inside lprect, ignore it */
1270                 if (!hsubmenu && lprect && PtInRect( lprect, msg->pt )) break;
1271                 fClosed = !MENU_ButtonUp( hwnd, hsubmenu,
1272                                           &hmenuCurrent, msg->pt );
1273                 fRemove = TRUE;  /* Remove event even if outside menu */
1274                 break;
1275                 
1276             case WM_MOUSEMOVE:
1277             case WM_NCMOUSEMOVE:
1278                 if ((msg->wParam & MK_LBUTTON) ||
1279                     ((wFlags & TPM_RIGHTBUTTON) && (msg->wParam & MK_RBUTTON)))
1280                 {
1281                     fClosed = !MENU_MouseMove( hwnd, hsubmenu,
1282                                                &hmenuCurrent, msg->pt );
1283                 }
1284                 break;
1285             }
1286         }
1287         else if ((msg->message >= WM_KEYFIRST) && (msg->message <= WM_KEYLAST))
1288         {
1289             fRemove = TRUE;  /* Keyboard messages are always removed */
1290             switch(msg->message)
1291             {
1292             case WM_KEYDOWN:
1293                 switch(msg->wParam)
1294                 {
1295                 case VK_HOME:
1296                     MENU_SelectItem( hmenuCurrent, NO_SELECTED_ITEM );
1297                     MENU_SelectNextItem( hmenuCurrent );
1298                     break;
1299
1300                 case VK_END:
1301                     MENU_SelectItem( hmenuCurrent, NO_SELECTED_ITEM );
1302                     MENU_SelectPrevItem( hmenuCurrent );
1303                     break;
1304
1305                 case VK_UP:
1306                     MENU_SelectPrevItem( hmenuCurrent );
1307                     break;
1308
1309                 case VK_DOWN:
1310                       /* If on menu bar, pull-down the menu */
1311                     if (!(menu->wFlags & MF_POPUP) && (hmenuCurrent == hmenu))
1312                         hmenuCurrent = MENU_ShowSubPopup( hwnd, hmenu, TRUE );
1313                     else
1314                         MENU_SelectNextItem( hmenuCurrent );
1315                     break;
1316
1317                 case VK_LEFT:
1318                     MENU_KeyLeft( hwnd, hmenu, &hmenuCurrent );
1319                     break;
1320                     
1321                 case VK_RIGHT:
1322                     MENU_KeyRight( hwnd, hmenu, &hmenuCurrent );
1323                     break;
1324                     
1325                 case VK_SPACE:
1326                 case VK_RETURN:
1327                     fClosed = !MENU_ExecFocusedItem( hwnd, hmenuCurrent,
1328                                                      &hmenuCurrent );
1329                     break;
1330
1331                 case VK_ESCAPE:
1332                     fClosed = TRUE;
1333                     break;
1334
1335                 default:
1336                     break;
1337                 }
1338                 break;  /* WM_KEYDOWN */
1339
1340             case WM_SYSKEYDOWN:
1341                 switch(msg->wParam)
1342                 {
1343                 case VK_MENU:
1344                     fClosed = TRUE;
1345                     break;
1346                     
1347                 }
1348                 break;  /* WM_SYSKEYDOWN */
1349
1350             case WM_CHAR:
1351                 {
1352                       /* Hack to avoid control chars. */
1353                       /* We will find a better way real soon... */
1354                     if ((msg->wParam <= 32) || (msg->wParam >= 127)) break;
1355                     pos = MENU_FindItemByKey( hwnd, hmenuCurrent, msg->wParam );
1356                     if (pos == (WORD)-2) fClosed = TRUE;
1357                     else if (pos == (WORD)-1) MessageBeep(0);
1358                     else
1359                     {
1360                         MENU_SelectItem( hmenuCurrent, pos );
1361                         fClosed = !MENU_ExecFocusedItem( hwnd, hmenuCurrent,
1362                                                          &hmenuCurrent );
1363                         
1364                     }
1365                 }                   
1366                 break;  /* WM_CHAR */
1367             }  /* switch(msg->message) */
1368         }
1369         else
1370         {
1371             DispatchMessage( msg );
1372         }
1373         if (fEndMenuCalled) fClosed = TRUE;
1374         if (!fClosed) fRemove = TRUE;
1375
1376         if (fRemove)  /* Remove the message from the queue */
1377             PeekMessage( msg, 0, msg->message, msg->message, PM_REMOVE );
1378     }
1379     USER_HEAP_FREE( hMsg );
1380     ReleaseCapture();
1381     MENU_HideSubPopups( hmenu );
1382     if (menu->wFlags & MF_POPUP) ShowWindow( menu->hWnd, SW_HIDE );
1383     MENU_SelectItem( hmenu, NO_SELECTED_ITEM );
1384     fEndMenuCalled = FALSE;
1385     return TRUE;
1386 }
1387
1388
1389 /***********************************************************************
1390  *           MENU_TrackMouseMenuBar
1391  *
1392  * Menu-bar tracking upon a mouse event. Called from NC_HandleSysCommand().
1393  */
1394 void MENU_TrackMouseMenuBar( HWND hwnd, POINT pt )
1395 {
1396     WND *wndPtr = WIN_FindWndPtr( hwnd );
1397     SendMessage( hwnd, WM_ENTERMENULOOP, 0, 0 );
1398     MENU_TrackMenu( (HMENU)wndPtr->wIDmenu, TPM_LEFTALIGN | TPM_LEFTBUTTON,
1399                     pt.x, pt.y, hwnd, NULL );
1400     SendMessage( hwnd, WM_EXITMENULOOP, 0, 0 );
1401 }
1402
1403
1404 /***********************************************************************
1405  *           MENU_TrackKbdMenuBar
1406  *
1407  * Menu-bar tracking upon a keyboard event. Called from NC_HandleSysCommand().
1408  */
1409 void MENU_TrackKbdMenuBar( HWND hwnd, WORD wParam )
1410 {
1411     WND *wndPtr = WIN_FindWndPtr( hwnd );
1412     if (!wndPtr->wIDmenu) return;
1413     SendMessage( hwnd, WM_ENTERMENULOOP, 0, 0 );
1414       /* Select first selectable item */
1415     MENU_SelectItem( wndPtr->wIDmenu, NO_SELECTED_ITEM );
1416     MENU_SelectNextItem( (HMENU)wndPtr->wIDmenu );
1417     MENU_TrackMenu( (HMENU)wndPtr->wIDmenu, TPM_LEFTALIGN | TPM_LEFTBUTTON,
1418                     0, 0, hwnd, NULL );
1419     SendMessage( hwnd, WM_EXITMENULOOP, 0, 0 );
1420 }
1421
1422
1423 /**********************************************************************
1424  *                      TrackPopupMenu          [USER.416]
1425  */
1426 BOOL TrackPopupMenu( HMENU hMenu, WORD wFlags, short x, short y,
1427                      short nReserved, HWND hWnd, LPRECT lpRect )
1428 {
1429     if (!MENU_ShowPopup( hWnd, hMenu, 0, x, y )) return FALSE;
1430     return MENU_TrackMenu( hMenu, wFlags, 0, 0, hWnd, lpRect );
1431 }
1432
1433
1434 /***********************************************************************
1435  *           PopupMenuWndProc
1436  */
1437 LONG PopupMenuWndProc( HWND hwnd, WORD message, WORD wParam, LONG lParam )
1438 {    
1439     switch(message)
1440     {
1441     case WM_CREATE:
1442         {
1443             CREATESTRUCT *createStruct = (CREATESTRUCT*)PTR_SEG_TO_LIN(lParam);
1444             HMENU hmenu = (HMENU) ((int)createStruct->lpCreateParams & 0xffff);
1445             SetWindowWord( hwnd, 0, hmenu );
1446             return 0;
1447         }
1448
1449     case WM_MOUSEACTIVATE:  /* We don't want to be activated */
1450         return MA_NOACTIVATE;
1451
1452     case WM_PAINT:
1453         {
1454             PAINTSTRUCT ps;
1455             BeginPaint( hwnd, &ps );
1456             MENU_DrawPopupMenu( hwnd, ps.hdc,
1457                                 (HMENU)GetWindowWord( hwnd, 0 ) );
1458             EndPaint( hwnd, &ps );
1459             return 0;
1460         }
1461
1462     default:
1463         return DefWindowProc(hwnd, message, wParam, lParam);
1464     }
1465     return 0;
1466 }
1467
1468
1469 /***********************************************************************
1470  *           MENU_GetMenuBarHeight
1471  *
1472  * Compute the size of the menu bar height. Used by NC_HandleNCCalcSize().
1473  */
1474 WORD MENU_GetMenuBarHeight( HWND hwnd, WORD menubarWidth, int orgX, int orgY )
1475 {
1476     HDC hdc;
1477     RECT rectBar;
1478     WND *wndPtr;
1479     LPPOPUPMENU lppop;
1480
1481     if (!(wndPtr = WIN_FindWndPtr( hwnd ))) return 0;
1482     if (!(lppop = (LPPOPUPMENU)USER_HEAP_LIN_ADDR(wndPtr->wIDmenu))) return 0;
1483     hdc = GetDC( hwnd );
1484     SetRect( &rectBar, orgX, orgY, orgX+menubarWidth, orgY+SYSMETRICS_CYMENU );
1485     MENU_MenuBarCalcSize( hdc, &rectBar, lppop, hwnd );
1486     ReleaseDC( hwnd, hdc );
1487     return lppop->Height;
1488 }
1489
1490
1491 /**********************************************************************
1492  *                      ChangeMenu              [USER.153]
1493  */
1494 BOOL ChangeMenu(HMENU hMenu, WORD nPos, LPSTR lpNewItem, 
1495                         WORD wItemID, WORD wFlags)
1496 {
1497         dprintf_menu(stddeb,"ChangeMenu(%04X, %X, '%s', %X, %X)\n",
1498                 hMenu, nPos, lpNewItem, wItemID, wFlags);
1499         if (wFlags & MF_APPEND)
1500                 return AppendMenu(hMenu, wFlags, wItemID, lpNewItem);
1501         if (wFlags & MF_DELETE)
1502                 return DeleteMenu(hMenu, wItemID, wFlags);
1503         if (wFlags & MF_CHANGE) 
1504                 return ModifyMenu(hMenu, nPos, wFlags, wItemID, lpNewItem);
1505         if (wFlags & MF_REMOVE) 
1506                 return RemoveMenu(hMenu, wItemID, wFlags);
1507         /* Default: MF_INSERT */
1508         return InsertMenu(hMenu, nPos, wFlags, wItemID, lpNewItem);
1509 }
1510
1511
1512 /**********************************************************************
1513  *                      CheckMenuItem           [USER.154]
1514  */
1515 BOOL CheckMenuItem(HMENU hMenu, WORD wItemID, WORD wFlags)
1516 {
1517         LPMENUITEM      lpitem;
1518         dprintf_menu(stddeb,"CheckMenuItem (%04X, %04X, %04X) !\n", 
1519                      hMenu, wItemID, wFlags);
1520         if (!(lpitem = MENU_FindItem(&hMenu, &wItemID, wFlags))) return FALSE;
1521         if (wFlags & MF_CHECKED) lpitem->item_flags |= MF_CHECKED;
1522         else lpitem->item_flags &= ~MF_CHECKED;
1523         return TRUE;
1524 }
1525
1526
1527 /**********************************************************************
1528  *                      EnableMenuItem          [USER.155]
1529  */
1530 BOOL EnableMenuItem(HMENU hMenu, WORD wItemID, WORD wFlags)
1531 {
1532     LPMENUITEM  lpitem;
1533     dprintf_menu(stddeb,"EnableMenuItem (%04X, %04X, %04X) !\n", 
1534                  hMenu, wItemID, wFlags);
1535     if (!(lpitem = MENU_FindItem( &hMenu, &wItemID, wFlags ))) return FALSE;
1536
1537       /* We can't have MF_GRAYED and MF_DISABLED together */
1538     if (wFlags & MF_GRAYED)
1539     {
1540         lpitem->item_flags = (lpitem->item_flags & ~MF_DISABLED) | MF_GRAYED;
1541     }
1542     else if (wFlags & MF_DISABLED)
1543     {
1544         lpitem->item_flags = (lpitem->item_flags & ~MF_GRAYED) | MF_DISABLED;
1545     }
1546     else   /* MF_ENABLED */
1547     {
1548         lpitem->item_flags &= ~(MF_GRAYED | MF_DISABLED);
1549     }
1550     return TRUE;
1551 }
1552
1553
1554 /**********************************************************************
1555  *                      GetMenuString           [USER.161]
1556  */
1557 int GetMenuString(HMENU hMenu, WORD wItemID, 
1558                   LPSTR str, short nMaxSiz, WORD wFlags)
1559 {
1560         LPMENUITEM      lpitem;
1561         int             maxsiz;
1562         dprintf_menu(stddeb,"GetMenuString(%04X, %04X, %p, %d, %04X);\n",
1563                                         hMenu, wItemID, str, nMaxSiz, wFlags);
1564         if (str == NULL) return FALSE;
1565         lpitem = MENU_FindItem( &hMenu, &wItemID, wFlags );
1566         if (lpitem != NULL) {
1567                 if (lpitem->item_text != NULL) {
1568                         maxsiz = min(nMaxSiz - 1, strlen(lpitem->item_text));
1569                         strncpy(str, lpitem->item_text, maxsiz + 1);
1570                         }
1571                 else
1572                         maxsiz = 0;
1573                 dprintf_menu(stddeb,"GetMenuString // Found !\n");
1574                 return maxsiz;
1575                 }
1576         return 0;
1577 }
1578
1579
1580 /**********************************************************************
1581  *                      HiliteMenuItem          [USER.162]
1582  */
1583 BOOL HiliteMenuItem(HWND hWnd, HMENU hMenu, WORD wItemID, WORD wHilite)
1584 {
1585     LPPOPUPMENU menu;
1586     LPMENUITEM  lpitem;
1587     dprintf_menu(stddeb,"HiliteMenuItem(%04X, %04X, %04X, %04X);\n", 
1588                                                 hWnd, hMenu, wItemID, wHilite);
1589     if (!(lpitem = MENU_FindItem( &hMenu, &wItemID, wHilite ))) return FALSE;
1590     if (!(menu = (LPPOPUPMENU) USER_HEAP_LIN_ADDR(hMenu))) return FALSE;
1591     if (menu->FocusedItem == wItemID) return TRUE;
1592     MENU_HideSubPopups( hMenu );
1593     MENU_SelectItem( hMenu, wItemID );
1594     return TRUE;
1595 }
1596
1597
1598 /**********************************************************************
1599  *                      GetMenuState            [USER.250]
1600  */
1601 WORD GetMenuState(HMENU hMenu, WORD wItemID, WORD wFlags)
1602 {
1603     LPMENUITEM lpitem;
1604     dprintf_menu(stddeb,"GetMenuState(%04X, %04X, %04X);\n", 
1605                  hMenu, wItemID, wFlags);
1606     if (!(lpitem = MENU_FindItem( &hMenu, &wItemID, wFlags ))) return -1;
1607     if (lpitem->item_flags & MF_POPUP)
1608     {
1609         POPUPMENU *menu = (POPUPMENU *) USER_HEAP_LIN_ADDR( lpitem->item_id );
1610         if (!menu) return -1;
1611         else return (menu->nItems << 8) | (menu->wFlags & 0xff);
1612     }
1613     else return lpitem->item_flags;
1614 }
1615
1616
1617 /**********************************************************************
1618  *                      GetMenuItemCount                [USER.263]
1619  */
1620 WORD GetMenuItemCount(HMENU hMenu)
1621 {
1622         LPPOPUPMENU     menu;
1623         dprintf_menu(stddeb,"GetMenuItemCount(%04X);\n", hMenu);
1624         menu = (LPPOPUPMENU) USER_HEAP_LIN_ADDR(hMenu);
1625         if (menu == NULL) return (WORD)-1;
1626         dprintf_menu(stddeb,"GetMenuItemCount(%04X) return %d \n", 
1627                      hMenu, menu->nItems);
1628         return menu->nItems;
1629 }
1630
1631
1632 /**********************************************************************
1633  *                      GetMenuItemID                   [USER.264]
1634  */
1635 WORD GetMenuItemID(HMENU hMenu, int nPos)
1636 {
1637     LPPOPUPMENU menu;
1638     MENUITEM *item;
1639
1640     dprintf_menu(stddeb,"GetMenuItemID(%04X, %d);\n", hMenu, nPos);
1641     if (!(menu = (LPPOPUPMENU) USER_HEAP_LIN_ADDR(hMenu))) return -1;
1642     if ((nPos < 0) || (nPos >= menu->nItems)) return -1;
1643     item = (MENUITEM *) USER_HEAP_LIN_ADDR( menu->hItems );
1644     if (item[nPos].item_flags & MF_POPUP) return -1;
1645     return item[nPos].item_id;
1646 }
1647
1648
1649 /**********************************************************************
1650  *                      InsertMenu              [USER.410]
1651  */
1652 BOOL InsertMenu(HMENU hMenu, WORD nPos, WORD wFlags, WORD wItemID, LPSTR lpNewItem)
1653 {
1654     HANDLE hNewItems;
1655     MENUITEM *lpitem, *newItems;
1656     LPPOPUPMENU menu;
1657     
1658     if (IS_STRING_ITEM(wFlags))
1659         {
1660            dprintf_menu(stddeb,"InsertMenu (%04X, %04X, %04X, %04X, '%s') !\n",
1661                                  hMenu, nPos, wFlags, wItemID, lpNewItem);
1662         }
1663     else
1664            dprintf_menu(stddeb,"InsertMenu (%04X, %04X, %04X, %04X, %p) !\n",
1665                                   hMenu, nPos, wFlags, wItemID, lpNewItem);
1666
1667       /* Find where to insert new item */
1668
1669     if ((wFlags & MF_BYPOSITION) && 
1670         ((nPos == (WORD)-1) || (nPos == GetMenuItemCount(hMenu))))
1671     {
1672           /* Special case: append to menu 
1673              Some programs specify the menu length to do that */
1674         if (!(menu = (LPPOPUPMENU) USER_HEAP_LIN_ADDR(hMenu))) 
1675         {
1676             dprintf_menu(stddeb,"InsertMenu: %04X not a menu handle\n", hMenu);
1677             return FALSE;
1678         }
1679         nPos = menu->nItems;
1680     }
1681     else
1682     {
1683         if (!MENU_FindItem( &hMenu, &nPos, wFlags )) 
1684         {
1685             dprintf_menu(stddeb,"InsertMenu: Item %X not found\n", nPos);
1686             return FALSE;
1687         }
1688         if (!(menu = (LPPOPUPMENU) USER_HEAP_LIN_ADDR(hMenu)))
1689         {
1690             dprintf_menu(stddeb,"InsertMenu: %04X not a menu handle\n", hMenu);
1691             return FALSE;
1692         }
1693     }
1694
1695       /* Create new items array */
1696
1697     hNewItems = USER_HEAP_ALLOC( sizeof(MENUITEM) * (menu->nItems+1) );
1698     if (!hNewItems)
1699     {
1700         dprintf_menu(stddeb,"InsertMenu: allocation failed\n");
1701         return FALSE;
1702     }
1703     newItems = (MENUITEM *) USER_HEAP_LIN_ADDR( hNewItems );
1704     if (menu->nItems > 0)
1705     {
1706           /* Copy the old array into the new */
1707         MENUITEM *oldItems = (MENUITEM *) USER_HEAP_LIN_ADDR( menu->hItems );
1708         if (nPos > 0) memcpy( newItems, oldItems, nPos * sizeof(MENUITEM) );
1709         if (nPos < menu->nItems) memcpy( &newItems[nPos+1], &oldItems[nPos],
1710                                         (menu->nItems-nPos)*sizeof(MENUITEM) );
1711
1712         USER_HEAP_FREE( menu->hItems );
1713     }
1714     menu->hItems = hNewItems;
1715     menu->nItems++;
1716
1717       /* Store the new item data */
1718
1719     lpitem = &newItems[nPos];
1720     lpitem->item_flags = wFlags & ~(MF_HILITE | MF_MOUSESELECT);
1721     lpitem->item_id    = wItemID;
1722
1723     if (IS_STRING_ITEM(wFlags))
1724     {
1725           /* Item beginning with a backspace is a help item */
1726         if (lpNewItem[0] == '\b')
1727         {
1728             lpitem->item_flags |= MF_HELP;
1729             lpNewItem++;
1730         }
1731         lpitem->hText = USER_HEAP_ALLOC( strlen(lpNewItem)+1 );
1732         lpitem->item_text = (char *)USER_HEAP_LIN_ADDR( lpitem->hText );
1733         strcpy( lpitem->item_text, lpNewItem );
1734     }
1735     else if (wFlags & MF_BITMAP) lpitem->hText = LOWORD((DWORD)lpNewItem);
1736     else lpitem->item_text = lpNewItem;
1737
1738     if (wFlags & MF_POPUP)  /* Set the MF_POPUP flag on the popup-menu */
1739         ((POPUPMENU *)USER_HEAP_LIN_ADDR(wItemID))->wFlags |= MF_POPUP;
1740
1741     SetRectEmpty( &lpitem->rect );
1742     lpitem->hCheckBit   = hStdCheck;
1743     lpitem->hUnCheckBit = 0;
1744     return TRUE;
1745 }
1746
1747
1748 /**********************************************************************
1749  *                      AppendMenu              [USER.411]
1750  */
1751 BOOL AppendMenu(HMENU hMenu, WORD wFlags, WORD wItemID, LPSTR lpNewItem)
1752 {
1753     return InsertMenu( hMenu, -1, wFlags | MF_BYPOSITION, wItemID, lpNewItem );
1754 }
1755
1756
1757 /**********************************************************************
1758  *                      RemoveMenu              [USER.412]
1759  */
1760 BOOL RemoveMenu(HMENU hMenu, WORD nPos, WORD wFlags)
1761 {
1762     LPPOPUPMENU menu;
1763     LPMENUITEM  lpitem;
1764         dprintf_menu(stddeb,"RemoveMenu (%04X, %04X, %04X) !\n", 
1765                      hMenu, nPos, wFlags);
1766     if (!(lpitem = MENU_FindItem( &hMenu, &nPos, wFlags ))) return FALSE;
1767     if (!(menu = (LPPOPUPMENU) USER_HEAP_LIN_ADDR(hMenu))) return FALSE;
1768     
1769       /* Remove item */
1770
1771     if (IS_STRING_ITEM(lpitem->item_flags)) USER_HEAP_FREE( lpitem->hText );
1772     if (--menu->nItems == 0)
1773     {
1774         USER_HEAP_FREE( menu->hItems );
1775         menu->hItems = 0;
1776     }
1777     else
1778     {
1779         while(nPos < menu->nItems)
1780         {
1781             *lpitem = *(lpitem+1);
1782             lpitem++;
1783             nPos++;
1784         }
1785         menu->hItems = USER_HEAP_REALLOC( menu->hItems,
1786                                           menu->nItems * sizeof(MENUITEM) );
1787     }
1788     return TRUE;
1789 }
1790
1791
1792 /**********************************************************************
1793  *                      DeleteMenu              [USER.413]
1794  */
1795 BOOL DeleteMenu(HMENU hMenu, WORD nPos, WORD wFlags)
1796 {
1797     MENUITEM *item = MENU_FindItem( &hMenu, &nPos, wFlags );
1798     if (!item) return FALSE;
1799     if (item->item_flags & MF_POPUP) DestroyMenu( item->item_id );
1800       /* nPos is now the position of the item */
1801     RemoveMenu( hMenu, nPos, wFlags | MF_BYPOSITION );
1802     return TRUE;
1803 }
1804
1805
1806 /**********************************************************************
1807  *                      ModifyMenu              [USER.414]
1808  */
1809 BOOL ModifyMenu(HMENU hMenu, WORD nPos, WORD wFlags, WORD wItemID, LPSTR lpNewItem)
1810 {
1811     LPMENUITEM  lpitem;
1812     if (IS_STRING_ITEM(wFlags))
1813         dprintf_menu(stddeb,"ModifyMenu (%04X, %04X, %04X, %04X, '%s') !\n",
1814                hMenu, nPos, wFlags, wItemID, lpNewItem);
1815     else
1816         dprintf_menu(stddeb,"ModifyMenu (%04X, %04X, %04X, %04X, %p) !\n",
1817                hMenu, nPos, wFlags, wItemID, lpNewItem);
1818     if (!(lpitem = MENU_FindItem( &hMenu, &nPos, wFlags ))) return FALSE;
1819     
1820     if (IS_STRING_ITEM(lpitem->item_flags)) USER_HEAP_FREE( lpitem->hText );
1821     lpitem->item_flags = wFlags & ~(MF_HILITE | MF_MOUSESELECT);
1822     lpitem->item_id    = wItemID;
1823
1824     if (IS_STRING_ITEM(wFlags))
1825     {
1826         lpitem->hText = USER_HEAP_ALLOC( strlen(lpNewItem)+1 );
1827         lpitem->item_text = (char *)USER_HEAP_LIN_ADDR( lpitem->hText );
1828         strcpy( lpitem->item_text, lpNewItem );
1829     }
1830     else if (wFlags & MF_BITMAP) lpitem->hText = LOWORD((DWORD)lpNewItem);
1831     else lpitem->item_text = lpNewItem;
1832     SetRectEmpty( &lpitem->rect );
1833     return TRUE;
1834 }
1835
1836
1837 /**********************************************************************
1838  *                      CreatePopupMenu         [USER.415]
1839  */
1840 HMENU CreatePopupMenu()
1841 {
1842     HMENU hmenu;
1843     POPUPMENU *menu;
1844
1845     if (!(hmenu = CreateMenu())) return 0;
1846     menu = (POPUPMENU *) USER_HEAP_LIN_ADDR( hmenu );
1847     menu->wFlags |= MF_POPUP;
1848     return hmenu;
1849 }
1850
1851
1852 /**********************************************************************
1853  *                      GetMenuCheckMarkDimensions      [USER.417]
1854  */
1855 DWORD GetMenuCheckMarkDimensions()
1856 {
1857     return MAKELONG( check_bitmap_width, check_bitmap_height );
1858 }
1859
1860
1861 /**********************************************************************
1862  *                      SetMenuItemBitmaps      [USER.418]
1863  */
1864 BOOL SetMenuItemBitmaps(HMENU hMenu, WORD nPos, WORD wFlags,
1865                 HBITMAP hNewCheck, HBITMAP hNewUnCheck)
1866 {
1867     LPMENUITEM lpitem;
1868    dprintf_menu(stddeb,"SetMenuItemBitmaps (%04X, %04X, %04X, %04X, %08X) !\n",
1869             hMenu, nPos, wFlags, hNewCheck, hNewUnCheck);
1870     if (!(lpitem = MENU_FindItem( &hMenu, &nPos, wFlags ))) return FALSE;
1871
1872     if (!hNewCheck && !hNewUnCheck)
1873     {
1874           /* If both are NULL, restore default bitmaps */
1875         lpitem->hCheckBit   = hStdCheck;
1876         lpitem->hUnCheckBit = 0;
1877         lpitem->item_flags &= ~MF_USECHECKBITMAPS;
1878     }
1879     else  /* Install new bitmaps */
1880     {
1881         lpitem->hCheckBit   = hNewCheck;
1882         lpitem->hUnCheckBit = hNewUnCheck;
1883         lpitem->item_flags |= MF_USECHECKBITMAPS;
1884     }
1885     return TRUE;
1886 }
1887
1888
1889 /**********************************************************************
1890  *                      CreateMenu              [USER.151]
1891  */
1892 HMENU CreateMenu()
1893 {
1894     HMENU hMenu;
1895     LPPOPUPMENU menu;
1896     dprintf_menu(stddeb,"CreateMenu !\n");
1897     if (!(hMenu = USER_HEAP_ALLOC( sizeof(POPUPMENU) )))
1898         return 0;
1899     menu = (LPPOPUPMENU) USER_HEAP_LIN_ADDR(hMenu);
1900     menu->hNext  = 0;
1901     menu->wFlags = 0;
1902     menu->wMagic = MENU_MAGIC;
1903     menu->hTaskQ = 0;
1904     menu->Width  = 0;
1905     menu->Height = 0;
1906     menu->nItems = 0;
1907     menu->hWnd   = 0;
1908     menu->hItems = 0;
1909     menu->FocusedItem = NO_SELECTED_ITEM;
1910     dprintf_menu(stddeb,"CreateMenu // return %04X\n", hMenu);
1911     return hMenu;
1912 }
1913
1914
1915 /**********************************************************************
1916  *                      DestroyMenu             [USER.152]
1917  */
1918 BOOL DestroyMenu(HMENU hMenu)
1919 {
1920         LPPOPUPMENU lppop;
1921         dprintf_menu(stddeb,"DestroyMenu (%04X) !\n", hMenu);
1922         if (hMenu == 0) return FALSE;
1923         lppop = (LPPOPUPMENU) USER_HEAP_LIN_ADDR(hMenu);
1924         if (lppop == NULL) return FALSE;
1925         if ((lppop->wFlags & MF_POPUP) && lppop->hWnd)
1926             DestroyWindow( lppop->hWnd );
1927
1928         if (lppop->hItems)
1929         {
1930             int i;
1931             MENUITEM *item = (MENUITEM *) USER_HEAP_LIN_ADDR( lppop->hItems );
1932             for (i = lppop->nItems; i > 0; i--, item++)
1933             {
1934                 if (item->item_flags & MF_POPUP)
1935                     DestroyMenu( item->item_id );
1936             }
1937             USER_HEAP_FREE( lppop->hItems );
1938         }
1939         USER_HEAP_FREE( hMenu );
1940         dprintf_menu(stddeb,"DestroyMenu (%04X) // End !\n", hMenu);
1941         return TRUE;
1942 }
1943
1944 /**********************************************************************
1945  *                      GetSystemMenu           [USER.156]
1946  */
1947 HMENU GetSystemMenu(HWND hWnd, BOOL bRevert)
1948 {
1949         WND             *wndPtr;
1950         wndPtr = WIN_FindWndPtr(hWnd);
1951         if (!bRevert) {
1952                 return wndPtr->hSysMenu;
1953                 }
1954         else {
1955                 DestroyMenu(wndPtr->hSysMenu);
1956                 wndPtr->hSysMenu = CopySysMenu();
1957                 }
1958         return wndPtr->hSysMenu;
1959 }
1960
1961 /**********************************************************************
1962  *                      SetSystemMenu           [USER.280]
1963  */
1964 BOOL SetSystemMenu(HWND hWnd, HMENU newHmenu)
1965 {
1966     WND *wndPtr;
1967
1968     if ((wndPtr = WIN_FindWndPtr(hWnd)) != NULL) wndPtr->hSysMenu = newHmenu;
1969     return TRUE;
1970 }
1971
1972
1973 /**********************************************************************
1974  *                      GetMenu         [USER.157]
1975  */
1976 HMENU GetMenu(HWND hWnd) 
1977
1978         WND * wndPtr = WIN_FindWndPtr(hWnd);
1979         if (wndPtr == NULL) return 0;
1980         return wndPtr->wIDmenu;
1981 }
1982
1983
1984 /**********************************************************************
1985  *                      SetMenu         [USER.158]
1986  */
1987 BOOL SetMenu(HWND hWnd, HMENU hMenu)
1988 {
1989         LPPOPUPMENU lpmenu;
1990         WND * wndPtr = WIN_FindWndPtr(hWnd);
1991         if (wndPtr == NULL) {
1992                 fprintf(stderr,"SetMenu(%04X, %04X) // Bad window handle !\n",
1993                         hWnd, hMenu);
1994                 return FALSE;
1995                 }
1996         dprintf_menu(stddeb,"SetMenu(%04X, %04X);\n", hWnd, hMenu);
1997         if (GetCapture() == hWnd) ReleaseCapture();
1998         wndPtr->wIDmenu = hMenu;
1999         if (hMenu != 0)
2000         {
2001             lpmenu = (LPPOPUPMENU) USER_HEAP_LIN_ADDR(hMenu);
2002             if (lpmenu == NULL) {
2003                 fprintf(stderr,"SetMenu(%04X, %04X) // Bad menu handle !\n", 
2004                         hWnd, hMenu);
2005                 return FALSE;
2006                 }
2007             lpmenu->hWnd = hWnd;
2008             lpmenu->wFlags &= ~MF_POPUP;  /* Can't be a popup */
2009             lpmenu->Height = 0;  /* Make sure we recalculate the size */
2010         }
2011         SetWindowPos( hWnd, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE |
2012                       SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED );
2013         return TRUE;
2014 }
2015
2016
2017
2018 /**********************************************************************
2019  *                      GetSubMenu              [USER.159]
2020  */
2021 HMENU GetSubMenu(HMENU hMenu, short nPos)
2022 {
2023     LPPOPUPMENU lppop;
2024     LPMENUITEM  lpitem;
2025     dprintf_menu(stddeb,"GetSubMenu (%04X, %04X) !\n", hMenu, nPos);
2026     if (!(lppop = (LPPOPUPMENU) USER_HEAP_LIN_ADDR(hMenu))) return 0;
2027     if ((WORD)nPos >= lppop->nItems) return 0;
2028     lpitem = (MENUITEM *) USER_HEAP_LIN_ADDR( lppop->hItems );
2029     if (!(lpitem[nPos].item_flags & MF_POPUP)) return 0;
2030     return lpitem[nPos].item_id;
2031 }
2032
2033
2034 /**********************************************************************
2035  *                      DrawMenuBar             [USER.160]
2036  */
2037 void DrawMenuBar(HWND hWnd)
2038 {
2039         WND             *wndPtr;
2040         LPPOPUPMENU lppop;
2041         dprintf_menu(stddeb,"DrawMenuBar (%04X)\n", hWnd);
2042         wndPtr = WIN_FindWndPtr(hWnd);
2043         if (wndPtr != NULL && (wndPtr->dwStyle & WS_CHILD) == 0 && 
2044                 wndPtr->wIDmenu != 0) {
2045                 dprintf_menu(stddeb,"DrawMenuBar wIDmenu=%04X \n", 
2046                              wndPtr->wIDmenu);
2047                 lppop = (LPPOPUPMENU) USER_HEAP_LIN_ADDR(wndPtr->wIDmenu);
2048                 if (lppop == NULL) return;
2049
2050                 lppop->Height = 0; /* Make sure we call MENU_MenuBarCalcSize */
2051                 SetWindowPos( hWnd, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE |
2052                             SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED );
2053             }
2054 }
2055
2056
2057 /***********************************************************************
2058  *           EndMenu   (USER.187)
2059  */
2060 void EndMenu(void)
2061 {
2062       /* Note: this won't work when we have multiple tasks... */
2063     fEndMenuCalled = TRUE;
2064 }
2065
2066
2067 /***********************************************************************
2068  *           LookupMenuHandle   (USER.217)
2069  */
2070 HMENU LookupMenuHandle( HMENU hmenu, INT id )
2071 {
2072     if (!MENU_FindItem( &hmenu, &id, MF_BYCOMMAND )) return 0;
2073     else return hmenu;
2074 }
2075
2076
2077 /**********************************************************************
2078  *                      LoadMenuIndirect        [USER.220]
2079  */
2080 HMENU LoadMenuIndirect(LPSTR menu_template)
2081 {
2082         HMENU                   hMenu;
2083         MENU_HEADER     *menu_desc;
2084         dprintf_menu(stddeb,"LoadMenuIndirect: menu_template '%p'\n", 
2085                      menu_template);
2086         hMenu = CreateMenu();
2087         menu_desc = (MENU_HEADER *)menu_template;
2088         ParseMenuResource((WORD *)(menu_desc + 1), 0, hMenu); 
2089         return hMenu;
2090 }
2091
2092
2093 /**********************************************************************
2094  *                      CopySysMenu (Internal)
2095  */
2096 HMENU CopySysMenu()
2097 {
2098     HMENU hMenu;
2099     LPPOPUPMENU menu;
2100     extern unsigned char sysres_MENU_SYSMENU[];
2101
2102     hMenu=LoadMenuIndirect(sysres_MENU_SYSMENU);
2103     if(!hMenu){
2104         dprintf_menu(stddeb,"No SYSMENU\n");
2105         return 0;
2106     }
2107     menu = (POPUPMENU*) USER_HEAP_LIN_ADDR(hMenu);
2108     menu->wFlags |= MF_SYSMENU|MF_POPUP;
2109     dprintf_menu(stddeb,"CopySysMenu hMenu=%04X !\n", hMenu);
2110     return hMenu;
2111 }
2112
2113
2114 /**********************************************************************
2115  *                      ParseMenuResource (from Resource or Template)
2116  */
2117 WORD * ParseMenuResource(WORD *first_item, int level, HMENU hMenu)
2118 {
2119     WORD        *item;
2120     WORD        *next_item;
2121     HMENU       hSubMenu;
2122     int         i;
2123
2124     level++;
2125     next_item = first_item;
2126     i = 0;
2127     do {
2128         i++;
2129         item = next_item;
2130         if (*item & MF_POPUP) {
2131             MENU_POPUPITEM *popup_item = (MENU_POPUPITEM *) item;
2132             next_item = (WORD *) (popup_item->item_text + 
2133                                   strlen(popup_item->item_text) + 1);
2134             hSubMenu = CreatePopupMenu();
2135             next_item = ParseMenuResource(next_item, level, hSubMenu);
2136             AppendMenu(hMenu, popup_item->item_flags, 
2137                 hSubMenu, popup_item->item_text);
2138             }
2139         else {
2140                 MENUITEMTEMPLATE *normal_item = (MENUITEMTEMPLATE *) item;
2141                 next_item = (WORD *) (normal_item->item_text + 
2142                 strlen(normal_item->item_text) + 1);
2143                 if (strlen(normal_item->item_text) == 0 && normal_item->item_id == 0) 
2144                         normal_item->item_flags |= MF_SEPARATOR;
2145                 AppendMenu(hMenu, normal_item->item_flags, 
2146                         normal_item->item_id, normal_item->item_text);
2147             }
2148         }
2149     while (!(*item & MF_END));
2150     return next_item;
2151 }
2152
2153
2154 /**********************************************************************
2155  *              IsMenu    (USER.358)
2156  */
2157 BOOL IsMenu( HMENU hmenu )
2158 {
2159     LPPOPUPMENU menu;
2160     if (!(menu = (LPPOPUPMENU) USER_HEAP_LIN_ADDR( hmenu ))) return FALSE;
2161     return (menu->wMagic == MENU_MAGIC);
2162 }