Release 960805
[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 "task.h"
22 #include "win.h"
23 #include "heap.h"
24 #include "menu.h"
25 #include "module.h"
26 #include "neexe.h"
27 #include "user.h"
28 #include "message.h"
29 #include "graphics.h"
30 #include "resource.h"
31 #include "string32.h"
32 #include "stddebug.h"
33 #include "debug.h"
34
35 /* Menu item structure */
36 typedef struct
37 {
38     WORD        item_flags;    /* Item flags */
39     UINT        item_id;       /* Item or popup id */
40     RECT16      rect;          /* Item area (relative to menu window) */
41     WORD        xTab;          /* X position of text after Tab */
42     HBITMAP     hCheckBit;     /* Bitmap for checked item */
43     HBITMAP     hUnCheckBit;   /* Bitmap for unchecked item */
44     LPSTR       text;          /* Item text or bitmap handle */
45 } MENUITEM;
46
47 /* Popup menu structure */
48 typedef struct
49 {
50     WORD        wFlags;       /* Menu flags (MF_POPUP, MF_SYSMENU) */
51     WORD        wMagic;       /* Magic number */
52     HANDLE      hTaskQ;       /* Task queue for this menu */
53     WORD        Width;        /* Width of the whole menu */
54     WORD        Height;       /* Height of the whole menu */
55     WORD        nItems;       /* Number of items in the menu */
56     HWND        hWnd;         /* Window containing the menu */
57     MENUITEM   *items;        /* Array of menu items */
58     UINT        FocusedItem;  /* Currently focused item */
59 } POPUPMENU, *LPPOPUPMENU;
60
61 #define MENU_MAGIC   0x554d  /* 'MU' */
62
63   /* Dimension of the menu bitmaps */
64 static WORD check_bitmap_width = 0, check_bitmap_height = 0;
65 static WORD arrow_bitmap_width = 0, arrow_bitmap_height = 0;
66
67   /* Flag set by EndMenu() to force an exit from menu tracking */
68 static BOOL fEndMenuCalled = FALSE;
69
70   /* Space between 2 menu bar items */
71 #define MENU_BAR_ITEMS_SPACE  16
72
73   /* Minimum width of a tab character */
74 #define MENU_TAB_SPACE        8
75
76   /* Height of a separator item */
77 #define SEPARATOR_HEIGHT      5
78
79   /* Values for menu->FocusedItem */
80   /* (other values give the position of the focused item) */
81 #define NO_SELECTED_ITEM  0xffff
82 #define SYSMENU_SELECTED  0xfffe  /* Only valid on menu-bars */
83
84 #define IS_STRING_ITEM(flags) (!((flags) & (MF_BITMAP | MF_OWNERDRAW | \
85                              MF_MENUBARBREAK | MF_MENUBREAK | MF_SEPARATOR)))
86
87 extern void NC_DrawSysButton(HWND hwnd, HDC hdc, BOOL down);  /* nonclient.c */
88
89 static HBITMAP hStdCheck = 0;
90 static HBITMAP hStdMnArrow = 0;
91 static HMENU MENU_DefSysMenu = 0;  /* Default system menu */
92
93
94 /* we _can_ use global popup window because there's no way 2 menues can
95  * be tracked at the same time.
96  */ 
97
98 static WND* pTopPWnd   = 0;
99 static UINT uSubPWndLevel = 0;
100
101
102 /**********************************************************************
103  *           MENU_CopySysMenu
104  *
105  * Load a copy of the system menu.
106  */
107 static HMENU MENU_CopySysMenu(void)
108 {
109     HMENU hMenu;
110     HGLOBAL handle;
111     POPUPMENU *menu;
112
113     if (!(handle = SYSRES_LoadResource( SYSRES_MENU_SYSMENU ))) return 0;
114     hMenu = LoadMenuIndirect16( GlobalLock16( handle ) );
115     SYSRES_FreeResource( handle );
116     if (!hMenu)
117     {
118         dprintf_menu(stddeb,"No SYSMENU\n");
119         return 0;
120     }
121     menu = (POPUPMENU*) USER_HEAP_LIN_ADDR(hMenu);
122     menu->wFlags |= MF_SYSMENU | MF_POPUP;
123     dprintf_menu(stddeb,"CopySysMenu hMenu=%04x !\n", hMenu);
124     return hMenu;
125 }
126
127
128 /***********************************************************************
129  *           MENU_Init
130  *
131  * Menus initialisation.
132  */
133 BOOL MENU_Init()
134 {
135     BITMAP16 bm;
136
137       /* Load bitmaps */
138
139     if (!(hStdCheck = LoadBitmap16( 0, MAKEINTRESOURCE(OBM_CHECK) )))
140         return FALSE;
141     GetObject16( hStdCheck, sizeof(bm), &bm );
142     check_bitmap_width = bm.bmWidth;
143     check_bitmap_height = bm.bmHeight;
144     if (!(hStdMnArrow = LoadBitmap16( 0, MAKEINTRESOURCE(OBM_MNARROW) )))
145         return FALSE;
146     GetObject16( hStdMnArrow, sizeof(bm), &bm );
147     arrow_bitmap_width = bm.bmWidth;
148     arrow_bitmap_height = bm.bmHeight;
149
150     if (!(MENU_DefSysMenu = MENU_CopySysMenu()))
151     {
152         fprintf( stderr, "Unable to create default system menu\n" );
153         return FALSE;
154     }
155     return TRUE;
156 }
157
158
159 /***********************************************************************
160  *           MENU_GetDefSysMenu
161  *
162  * Return the default system menu.
163  */
164 HMENU MENU_GetDefSysMenu(void)
165 {
166     return MENU_DefSysMenu;
167 }
168
169
170 /***********************************************************************
171  *           MENU_HasSysMenu
172  *
173  * Check whether the window owning the menu bar has a system menu.
174  */
175 static BOOL MENU_HasSysMenu( POPUPMENU *menu )
176 {
177     WND *wndPtr;
178
179     if (menu->wFlags & MF_POPUP) return FALSE;
180     if (!(wndPtr = WIN_FindWndPtr( menu->hWnd ))) return FALSE;
181     return (wndPtr->dwStyle & WS_SYSMENU) != 0;
182 }
183
184
185 /***********************************************************************
186  *           MENU_IsInSysMenu
187  *
188  * Check whether the point (in screen coords) is in the system menu
189  * of the window owning the given menu.
190  */
191 static BOOL MENU_IsInSysMenu( POPUPMENU *menu, POINT16 pt )
192 {
193     WND *wndPtr;
194
195     if (menu->wFlags & MF_POPUP) return FALSE;
196     if (!(wndPtr = WIN_FindWndPtr( menu->hWnd ))) return FALSE;
197     if (!(wndPtr->dwStyle & WS_SYSMENU)) return FALSE;
198     if ((pt.x < wndPtr->rectClient.left) ||
199         (pt.x >= wndPtr->rectClient.left+SYSMETRICS_CXSIZE+SYSMETRICS_CXBORDER))
200         return FALSE;
201     if ((pt.y >= wndPtr->rectClient.top - menu->Height) ||
202         (pt.y < wndPtr->rectClient.top - menu->Height -
203                       SYSMETRICS_CYSIZE - SYSMETRICS_CYBORDER)) return FALSE;
204     return TRUE;
205 }
206
207
208 /***********************************************************************
209  *           MENU_FindItem
210  *
211  * Find a menu item. Return a pointer on the item, and modifies *hmenu
212  * in case the item was in a sub-menu.
213  */
214 static MENUITEM *MENU_FindItem( HMENU *hmenu, UINT *nPos, UINT wFlags )
215 {
216     POPUPMENU *menu;
217     int i;
218
219     if (!(menu = (POPUPMENU *) USER_HEAP_LIN_ADDR(*hmenu))) return NULL;
220     if (wFlags & MF_BYPOSITION)
221     {
222         if (*nPos >= menu->nItems) return NULL;
223         return &menu->items[*nPos];
224     }
225     else
226     {
227         MENUITEM *item = menu->items;
228         for (i = 0; i < menu->nItems; i++, item++)
229         {
230             if (item->item_id == *nPos)
231             {
232                 *nPos = i;
233                 return item;
234             }
235             else if (item->item_flags & MF_POPUP)
236             {
237                 HMENU hsubmenu = (HMENU)item->item_id;
238                 MENUITEM *subitem = MENU_FindItem( &hsubmenu, nPos, wFlags );
239                 if (subitem)
240                 {
241                     *hmenu = hsubmenu;
242                     return subitem;
243                 }
244             }
245         }
246     }
247     return NULL;
248 }
249
250
251 /***********************************************************************
252  *           MENU_FindItemByCoords
253  *
254  * Find the item at the specified coordinates (screen coords).
255  */
256 static MENUITEM *MENU_FindItemByCoords( POPUPMENU *menu, int x, int y, UINT *pos )
257 {
258     MENUITEM *item;
259     WND *wndPtr;
260     int i;
261
262     if (!(wndPtr = WIN_FindWndPtr( menu->hWnd ))) return NULL;
263     x -= wndPtr->rectWindow.left;
264     y -= wndPtr->rectWindow.top;
265     item = menu->items;
266     for (i = 0; i < menu->nItems; i++, item++)
267     {
268         if ((x >= item->rect.left) && (x < item->rect.right) &&
269             (y >= item->rect.top) && (y < item->rect.bottom))
270         {
271             if (pos) *pos = i;
272             return item;
273         }
274     }
275     return NULL;
276 }
277
278
279 /***********************************************************************
280  *           MENU_FindItemByKey
281  *
282  * Find the menu item selected by a key press.
283  * Return item id, -1 if none, -2 if we should close the menu.
284  */
285 static UINT MENU_FindItemByKey( HWND hwndOwner, HMENU hmenu, UINT key )
286 {
287     POPUPMENU *menu;
288     MENUITEM *item;
289     int i;
290     LONG menuchar;
291
292     if (!IsMenu( hmenu )) hmenu = WIN_FindWndPtr(hwndOwner)->hSysMenu;
293     if (!hmenu) return -1;
294
295     menu = (POPUPMENU *) USER_HEAP_LIN_ADDR( hmenu );
296     item = menu->items;
297     key = toupper(key);
298     for (i = 0; i < menu->nItems; i++, item++)
299     {
300         if (IS_STRING_ITEM(item->item_flags))
301         {
302             char *p = strchr( item->text, '&' );
303             if (p && (p[1] != '&') && (toupper(p[1]) == key)) return i;
304         }
305     }
306     menuchar = SendMessage32A( hwndOwner, WM_MENUCHAR, 
307                                MAKEWPARAM( key, menu->wFlags ), hmenu );
308     if (HIWORD(menuchar) == 2) return LOWORD(menuchar);
309     if (HIWORD(menuchar) == 1) return -2;
310     return -1;
311 }
312
313
314 /***********************************************************************
315  *           MENU_CalcItemSize
316  *
317  * Calculate the size of the menu item and store it in lpitem->rect.
318  */
319 static void MENU_CalcItemSize( HDC hdc, MENUITEM *lpitem, HWND hwndOwner,
320                                int orgX, int orgY, BOOL menuBar )
321 {
322     DWORD dwSize;
323     char *p;
324
325     SetRect16( &lpitem->rect, orgX, orgY, orgX, orgY );
326
327     if (lpitem->item_flags & MF_OWNERDRAW)
328     {
329         MEASUREITEMSTRUCT16 *mis;
330         if (!(mis = SEGPTR_NEW(MEASUREITEMSTRUCT16))) return;
331         mis->CtlType    = ODT_MENU;
332         mis->itemID     = lpitem->item_id;
333         mis->itemData   = (DWORD)lpitem->text;
334         mis->itemHeight = 16;
335         mis->itemWidth  = 30;
336         SendMessage16( hwndOwner, WM_MEASUREITEM, 0, (LPARAM)SEGPTR_GET(mis) );
337         lpitem->rect.bottom += mis->itemHeight;
338         lpitem->rect.right  += mis->itemWidth;
339         dprintf_menu( stddeb, "DrawMenuItem: MeasureItem %04x %dx%d!\n",
340                       lpitem->item_id, mis->itemWidth, mis->itemHeight );
341         SEGPTR_FREE(mis);
342         return;
343     } 
344
345     if (lpitem->item_flags & MF_SEPARATOR)
346     {
347         lpitem->rect.bottom += SEPARATOR_HEIGHT;
348         return;
349     }
350
351     if (!menuBar)
352     {
353         lpitem->rect.right += 2 * check_bitmap_width;
354         if (lpitem->item_flags & MF_POPUP)
355             lpitem->rect.right += arrow_bitmap_width;
356     }
357
358     if (lpitem->item_flags & MF_BITMAP)
359     {
360         BITMAP16 bm;
361         if (GetObject16( (HBITMAP16)(UINT32)lpitem->text, sizeof(bm), &bm ))
362         {
363             lpitem->rect.right  += bm.bmWidth;
364             lpitem->rect.bottom += bm.bmHeight;
365         }
366         return;
367     }
368     
369     /* If we get here, then it must be a text item */
370
371     if (IS_STRING_ITEM( lpitem->item_flags ))
372     {
373         dwSize = GetTextExtent( hdc, lpitem->text, strlen(lpitem->text) );
374         lpitem->rect.right  += LOWORD(dwSize);
375         lpitem->rect.bottom += MAX( HIWORD(dwSize), SYSMETRICS_CYMENU );
376         lpitem->xTab = 0;
377
378         if (menuBar) lpitem->rect.right += MENU_BAR_ITEMS_SPACE;
379         else if ((p = strchr( lpitem->text, '\t' )) != NULL)
380         {
381             /* Item contains a tab (only meaningful in popup menus) */
382             lpitem->xTab = check_bitmap_width + MENU_TAB_SPACE + 
383                 LOWORD( GetTextExtent( hdc, lpitem->text,
384                                        (int)(p - lpitem->text) ));
385             lpitem->rect.right += MENU_TAB_SPACE;
386         }
387         else
388         {
389             if (strchr( lpitem->text, '\b' ))
390                 lpitem->rect.right += MENU_TAB_SPACE;
391             lpitem->xTab = lpitem->rect.right - check_bitmap_width 
392                            - arrow_bitmap_width;
393         }
394     }
395 }
396
397
398 /***********************************************************************
399  *           MENU_PopupMenuCalcSize
400  *
401  * Calculate the size of a popup menu.
402  */
403 static void MENU_PopupMenuCalcSize( LPPOPUPMENU lppop, HWND hwndOwner )
404 {
405     MENUITEM *lpitem;
406     HDC hdc;
407     int start, i;
408     int orgX, orgY, maxX, maxTab, maxTabWidth;
409
410     lppop->Width = lppop->Height = 0;
411     if (lppop->nItems == 0) return;
412     hdc = GetDC( 0 );
413     maxX = start = 0;
414     while (start < lppop->nItems)
415     {
416         lpitem = &lppop->items[start];
417         orgX = maxX;
418         orgY = 0;
419         maxTab = maxTabWidth = 0;
420
421           /* Parse items until column break or end of menu */
422         for (i = start; i < lppop->nItems; i++, lpitem++)
423         {
424             if ((i != start) &&
425                 (lpitem->item_flags & (MF_MENUBREAK | MF_MENUBARBREAK))) break;
426             MENU_CalcItemSize( hdc, lpitem, hwndOwner, orgX, orgY, FALSE );
427             if (lpitem->item_flags & MF_MENUBARBREAK) orgX++;
428             maxX = MAX( maxX, lpitem->rect.right );
429             orgY = lpitem->rect.bottom;
430             if (IS_STRING_ITEM(lpitem->item_flags) && lpitem->xTab)
431             {
432                 maxTab = MAX( maxTab, lpitem->xTab );
433                 maxTabWidth = MAX(maxTabWidth,lpitem->rect.right-lpitem->xTab);
434             }
435         }
436
437           /* Finish the column (set all items to the largest width found) */
438         maxX = MAX( maxX, maxTab + maxTabWidth );
439         for (lpitem = &lppop->items[start]; start < i; start++, lpitem++)
440         {
441             lpitem->rect.right = maxX;
442             if (IS_STRING_ITEM(lpitem->item_flags) && lpitem->xTab)
443                 lpitem->xTab = maxTab;
444         }
445         lppop->Height = MAX( lppop->Height, orgY );
446     }
447
448     lppop->Width  = maxX;
449     ReleaseDC( 0, hdc );
450 }
451
452
453 /***********************************************************************
454  *           MENU_MenuBarCalcSize
455  *
456  * Calculate the size of the menu bar.
457  */
458 static void MENU_MenuBarCalcSize( HDC hdc, LPRECT16 lprect, LPPOPUPMENU lppop,
459                                   HWND hwndOwner )
460 {
461     MENUITEM *lpitem;
462     int start, i, orgX, orgY, maxY, helpPos;
463
464     if ((lprect == NULL) || (lppop == NULL)) return;
465     if (lppop->nItems == 0) return;
466     dprintf_menu(stddeb,"MENU_MenuBarCalcSize left=%d top=%d right=%d bottom=%d\n", 
467                  lprect->left, lprect->top, lprect->right, lprect->bottom);
468     lppop->Width  = lprect->right - lprect->left;
469     lppop->Height = 0;
470     maxY = lprect->top;
471     start = 0;
472     helpPos = -1;
473     while (start < lppop->nItems)
474     {
475         lpitem = &lppop->items[start];
476         orgX = lprect->left;
477         orgY = maxY;
478
479           /* Parse items until line break or end of menu */
480         for (i = start; i < lppop->nItems; i++, lpitem++)
481         {
482             if ((helpPos == -1) && (lpitem->item_flags & MF_HELP)) helpPos = i;
483             if ((i != start) &&
484                 (lpitem->item_flags & (MF_MENUBREAK | MF_MENUBARBREAK))) break;
485             MENU_CalcItemSize( hdc, lpitem, hwndOwner, orgX, orgY, TRUE );
486             if (lpitem->rect.right > lprect->right)
487             {
488                 if (i != start) break;
489                 else lpitem->rect.right = lprect->right;
490             }
491             maxY = MAX( maxY, lpitem->rect.bottom );
492             orgX = lpitem->rect.right;
493         }
494
495           /* Finish the line (set all items to the largest height found) */
496         while (start < i) lppop->items[start++].rect.bottom = maxY;
497     }
498
499     lprect->bottom = maxY;
500     lppop->Height = lprect->bottom - lprect->top;
501
502       /* Flush right all items between the MF_HELP and the last item */
503       /* (if several lines, only move the last line) */
504     if (helpPos != -1)
505     {
506         lpitem = &lppop->items[lppop->nItems-1];
507         orgY = lpitem->rect.top;
508         orgX = lprect->right;
509         for (i = lppop->nItems - 1; i >= helpPos; i--, lpitem--)
510         {
511             if (lpitem->rect.top != orgY) break;    /* Other line */
512             if (lpitem->rect.right >= orgX) break;  /* Too far right already */
513             lpitem->rect.left += orgX - lpitem->rect.right;
514             lpitem->rect.right = orgX;
515             orgX = lpitem->rect.left;
516         }
517     }
518 }
519
520
521 /***********************************************************************
522  *           MENU_DrawMenuItem
523  *
524  * Draw a single menu item.
525  */
526 static void MENU_DrawMenuItem( HWND hwnd, HDC hdc, MENUITEM *lpitem,
527                                UINT height, BOOL menuBar )
528 {
529     RECT16 rect;
530
531     if (lpitem->item_flags & MF_OWNERDRAW)
532     {
533         DRAWITEMSTRUCT32 dis;
534
535         dprintf_menu( stddeb, "DrawMenuItem: Ownerdraw!\n" );
536         dis.CtlType   = ODT_MENU;
537         dis.itemID    = lpitem->item_id;
538         dis.itemData  = (DWORD)lpitem->text;
539         dis.itemState = 0;
540         if (lpitem->item_flags & MF_CHECKED) dis.itemState |= ODS_CHECKED;
541         if (lpitem->item_flags & MF_GRAYED)  dis.itemState |= ODS_GRAYED;
542         if (lpitem->item_flags & MF_HILITE)  dis.itemState |= ODS_SELECTED;
543         dis.itemAction = ODA_DRAWENTIRE | ODA_SELECT | ODA_FOCUS;
544         dis.hwndItem   = hwnd;
545         dis.hDC        = hdc;
546         CONV_RECT16TO32( &lpitem->rect, &dis.rcItem );
547         SendMessage32A( hwnd, WM_DRAWITEM, 0, (LPARAM)&dis );
548         return;
549     }
550
551     if (menuBar && (lpitem->item_flags & MF_SEPARATOR)) return;
552     rect = lpitem->rect;
553
554       /* Draw the background */
555
556     if (lpitem->item_flags & MF_HILITE)
557         FillRect16( hdc, &rect, sysColorObjects.hbrushHighlight );
558     else FillRect16( hdc, &rect, sysColorObjects.hbrushMenu );
559     SetBkMode( hdc, TRANSPARENT );
560
561       /* Draw the separator bar (if any) */
562
563     if (!menuBar && (lpitem->item_flags & MF_MENUBARBREAK))
564     {
565         SelectObject( hdc, sysColorObjects.hpenWindowFrame );
566         MoveTo( hdc, rect.left, 0 );
567         LineTo( hdc, rect.left, height );
568     }
569     if (lpitem->item_flags & MF_SEPARATOR)
570     {
571         SelectObject( hdc, sysColorObjects.hpenWindowFrame );
572         MoveTo( hdc, rect.left, rect.top + SEPARATOR_HEIGHT/2 );
573         LineTo( hdc, rect.right, rect.top + SEPARATOR_HEIGHT/2 );
574         return;
575     }
576
577       /* Setup colors */
578
579     if (lpitem->item_flags & MF_HILITE)
580     {
581         if (lpitem->item_flags & MF_GRAYED)
582             SetTextColor( hdc, GetSysColor( COLOR_GRAYTEXT ) );
583         else
584             SetTextColor( hdc, GetSysColor( COLOR_HIGHLIGHTTEXT ) );
585         SetBkColor( hdc, GetSysColor( COLOR_HIGHLIGHT ) );
586     }
587     else
588     {
589         if (lpitem->item_flags & MF_GRAYED)
590             SetTextColor( hdc, GetSysColor( COLOR_GRAYTEXT ) );
591         else
592             SetTextColor( hdc, GetSysColor( COLOR_MENUTEXT ) );
593         SetBkColor( hdc, GetSysColor( COLOR_MENU ) );
594     }
595
596     if (!menuBar)
597     {
598           /* Draw the check mark */
599
600         if (lpitem->item_flags & MF_CHECKED)
601         {
602             GRAPH_DrawBitmap(hdc, lpitem->hCheckBit ? lpitem->hCheckBit :
603                              hStdCheck, rect.left,
604                              (rect.top+rect.bottom-check_bitmap_height) / 2,
605                              0, 0, check_bitmap_width, check_bitmap_height );
606         }
607         else if (lpitem->hUnCheckBit != 0)  /* Not checked */
608         {
609             GRAPH_DrawBitmap(hdc, lpitem->hUnCheckBit, rect.left,
610                              (rect.top+rect.bottom-check_bitmap_height) / 2,
611                              0, 0, check_bitmap_width, check_bitmap_height );
612         }
613
614           /* Draw the popup-menu arrow */
615
616         if (lpitem->item_flags & MF_POPUP)
617         {
618             GRAPH_DrawBitmap( hdc, hStdMnArrow,
619                               rect.right-arrow_bitmap_width-1,
620                               (rect.top+rect.bottom-arrow_bitmap_height) / 2,
621                               0, 0, arrow_bitmap_width, arrow_bitmap_height );
622         }
623
624         rect.left += check_bitmap_width;
625         rect.right -= arrow_bitmap_width;
626     }
627
628       /* Draw the item text or bitmap */
629
630     if (lpitem->item_flags & MF_BITMAP)
631     {
632         GRAPH_DrawBitmap( hdc, (HBITMAP16)(UINT32)lpitem->text,
633                           rect.left, rect.top, 0, 0,
634                           rect.right-rect.left, rect.bottom-rect.top );
635         return;
636     }
637     /* No bitmap - process text if present */
638     else if (IS_STRING_ITEM(lpitem->item_flags))
639     {
640         register int i;
641
642         if (menuBar)
643         {
644             rect.left += MENU_BAR_ITEMS_SPACE / 2;
645             rect.right -= MENU_BAR_ITEMS_SPACE / 2;
646             i = strlen( lpitem->text );
647         }
648         else
649         {
650             for (i = 0; lpitem->text[i]; i++)
651                 if ((lpitem->text[i] == '\t') || (lpitem->text[i] == '\b'))
652                     break;
653         }
654         
655         DrawText16( hdc, lpitem->text, i, &rect,
656                     DT_LEFT | DT_VCENTER | DT_SINGLELINE );
657
658         if (lpitem->text[i])  /* There's a tab or flush-right char */
659         {
660             if (lpitem->text[i] == '\t')
661             {
662                 rect.left = lpitem->xTab;
663                 DrawText16( hdc, lpitem->text + i + 1, -1, &rect,
664                             DT_LEFT | DT_VCENTER | DT_SINGLELINE );
665             }
666             else DrawText16( hdc, lpitem->text + i + 1, -1, &rect,
667                              DT_RIGHT | DT_VCENTER | DT_SINGLELINE );
668         }
669     }
670 }
671
672
673 /***********************************************************************
674  *           MENU_DrawPopupMenu
675  *
676  * Paint a popup menu.
677  */
678 static void MENU_DrawPopupMenu( HWND hwnd, HDC hdc, HMENU hmenu )
679 {
680     POPUPMENU *menu;
681     MENUITEM *item;
682     RECT16 rect;
683     int i;
684
685     GetClientRect16( hwnd, &rect );
686     FillRect16( hdc, &rect, sysColorObjects.hbrushMenu );
687     menu = (POPUPMENU *) USER_HEAP_LIN_ADDR( hmenu );
688     if (!menu || !menu->nItems) return;
689     for (i = menu->nItems, item = menu->items; i > 0; i--, item++)
690         MENU_DrawMenuItem( hwnd, hdc, item, menu->Height, FALSE );
691 }
692
693
694 /***********************************************************************
695  *           MENU_DrawMenuBar
696  *
697  * Paint a menu bar. Returns the height of the menu bar.
698  */
699 UINT MENU_DrawMenuBar(HDC hDC, LPRECT16 lprect, HWND hwnd, BOOL suppress_draw)
700 {
701     LPPOPUPMENU lppop;
702     int i;
703     WND *wndPtr = WIN_FindWndPtr( hwnd );
704     
705     lppop = (LPPOPUPMENU) USER_HEAP_LIN_ADDR( (HMENU)wndPtr->wIDmenu );
706     if (lppop == NULL || lprect == NULL) return SYSMETRICS_CYMENU;
707     dprintf_menu(stddeb,"MENU_DrawMenuBar(%04x, %p, %p); !\n", 
708                  hDC, lprect, lppop);
709     if (lppop->Height == 0) MENU_MenuBarCalcSize(hDC, lprect, lppop, hwnd);
710     lprect->bottom = lprect->top + lppop->Height;
711     if (suppress_draw) return lppop->Height;
712     
713     FillRect16(hDC, lprect, sysColorObjects.hbrushMenu );
714     SelectObject( hDC, sysColorObjects.hpenWindowFrame );
715     MoveTo( hDC, lprect->left, lprect->bottom );
716     LineTo( hDC, lprect->right, lprect->bottom );
717
718     if (lppop->nItems == 0) return SYSMETRICS_CYMENU;
719     for (i = 0; i < lppop->nItems; i++)
720     {
721         MENU_DrawMenuItem( hwnd, hDC, &lppop->items[i], lppop->Height, TRUE );
722     }
723     return lppop->Height;
724
725
726
727 /***********************************************************************
728  *           MENU_SwitchTPWndTo
729  */
730 static BOOL MENU_SwitchTPWndTo( HTASK hTask)
731 {
732   /* This is supposed to be called when popup is hidden */
733
734   TDB* task = (TDB*)GlobalLock16(hTask);
735
736   if( !task ) return 0;
737
738   /* if this task got as far as menu tracking it must have a queue */
739
740   pTopPWnd->hInstance = task->hInstance;
741   pTopPWnd->hmemTaskQ = task->hQueue;
742   return 1;
743 }
744
745 /***********************************************************************
746  *           MENU_ShowPopup
747  *
748  * Display a popup menu.
749  */
750 static BOOL MENU_ShowPopup(HWND hwndOwner, HMENU hmenu, UINT id, int x, int y)
751 {
752     POPUPMENU   *menu;
753     WND         *wndPtr = NULL;
754     BOOL         skip_init = 0;
755
756     if (!(menu = (POPUPMENU *) USER_HEAP_LIN_ADDR( hmenu ))) return FALSE;
757     if (menu->FocusedItem != NO_SELECTED_ITEM)
758     {
759         menu->items[menu->FocusedItem].item_flags &= ~(MF_HILITE|MF_MOUSESELECT);
760         menu->FocusedItem = NO_SELECTED_ITEM;
761     }
762     SendMessage16( hwndOwner, WM_INITMENUPOPUP, (WPARAM)hmenu,
763                  MAKELONG( id, (menu->wFlags & MF_SYSMENU) ? 1 : 0 ));
764     MENU_PopupMenuCalcSize( menu, hwndOwner );
765
766     wndPtr = WIN_FindWndPtr( hwndOwner );
767     if (!wndPtr) return FALSE;
768
769     if (!pTopPWnd)
770     {
771         pTopPWnd = WIN_FindWndPtr(CreateWindow16( POPUPMENU_CLASS_ATOM, NULL,
772                                           WS_POPUP | WS_BORDER, x, y,
773                                           menu->Width + 2*SYSMETRICS_CXBORDER,
774                                           menu->Height + 2*SYSMETRICS_CYBORDER,
775                                           0, 0, wndPtr->hInstance,
776                                           (LPVOID)(HMENU32)hmenu ));
777         if (!pTopPWnd) return FALSE;
778         skip_init = TRUE;
779     }
780
781     if( uSubPWndLevel )
782     {
783         /* create new window for the submenu */
784         HWND  hWnd = CreateWindow16( POPUPMENU_CLASS_ATOM, NULL,
785                                    WS_POPUP | WS_BORDER, x, y,
786                                    menu->Width + 2*SYSMETRICS_CXBORDER,
787                                    menu->Height + 2*SYSMETRICS_CYBORDER,
788                                    menu->hWnd, 0, wndPtr->hInstance,
789                                    (LPVOID)(HMENU32)hmenu );
790         if( !hWnd ) return FALSE;
791         menu->hWnd = hWnd;
792     }
793     else 
794     {
795         if( !skip_init )
796           {
797             MENU_SwitchTPWndTo(GetCurrentTask());
798             SendMessage16( pTopPWnd->hwndSelf, WM_USER, (WPARAM)hmenu, 0L);
799           }
800         menu->hWnd = pTopPWnd->hwndSelf;
801     }
802
803     uSubPWndLevel++;
804
805     SetWindowPos(menu->hWnd, 0, x, y, menu->Width + 2*SYSMETRICS_CXBORDER, 
806                                       menu->Height + 2*SYSMETRICS_CYBORDER,
807                                       SWP_NOACTIVATE | SWP_NOZORDER );
808
809       /* Display the window */
810
811     SetWindowPos( menu->hWnd, HWND_TOP, 0, 0, 0, 0,
812                   SWP_SHOWWINDOW | SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE );
813     UpdateWindow( menu->hWnd );
814     return TRUE;
815 }
816
817
818 /***********************************************************************
819  *           MENU_SelectItem
820  */
821 static void MENU_SelectItem( HWND hwndOwner, HMENU hmenu, UINT wIndex,
822                              BOOL sendMenuSelect )
823 {
824     LPPOPUPMENU lppop;
825     HDC hdc;
826
827     lppop = (POPUPMENU *) USER_HEAP_LIN_ADDR( hmenu );
828     if (!lppop->nItems) return;
829     if ((wIndex != NO_SELECTED_ITEM) && 
830         (wIndex != SYSMENU_SELECTED) &&
831         (lppop->items[wIndex].item_flags & MF_SEPARATOR))
832         wIndex = NO_SELECTED_ITEM;
833     if (lppop->FocusedItem == wIndex) return;
834     if (lppop->wFlags & MF_POPUP) hdc = GetDC( lppop->hWnd );
835     else hdc = GetDCEx( lppop->hWnd, 0, DCX_CACHE | DCX_WINDOW);
836
837       /* Clear previous highlighted item */
838     if (lppop->FocusedItem != NO_SELECTED_ITEM) 
839     {
840         if (lppop->FocusedItem == SYSMENU_SELECTED)
841             NC_DrawSysButton( lppop->hWnd, hdc, FALSE );
842         else
843         {
844             lppop->items[lppop->FocusedItem].item_flags &=~(MF_HILITE|MF_MOUSESELECT);
845             MENU_DrawMenuItem(lppop->hWnd,hdc,&lppop->items[lppop->FocusedItem],
846                               lppop->Height, !(lppop->wFlags & MF_POPUP) );
847         }
848     }
849
850       /* Highlight new item (if any) */
851     lppop->FocusedItem = wIndex;
852     if (lppop->FocusedItem != NO_SELECTED_ITEM) 
853     {
854         if (lppop->FocusedItem == SYSMENU_SELECTED)
855         {
856             NC_DrawSysButton( lppop->hWnd, hdc, TRUE );
857             if (sendMenuSelect)
858                 SendMessage16( hwndOwner, WM_MENUSELECT,
859                              WIN_FindWndPtr(lppop->hWnd)->hSysMenu,
860                              MAKELONG(lppop->wFlags | MF_MOUSESELECT, hmenu));
861         }
862         else
863         {
864             lppop->items[lppop->FocusedItem].item_flags |= MF_HILITE;
865             MENU_DrawMenuItem( lppop->hWnd, hdc, &lppop->items[lppop->FocusedItem],
866                                lppop->Height, !(lppop->wFlags & MF_POPUP) );
867             if (sendMenuSelect)
868                 SendMessage16( hwndOwner, WM_MENUSELECT,
869                              lppop->items[lppop->FocusedItem].item_id,
870                              MAKELONG( lppop->items[lppop->FocusedItem].item_flags | MF_MOUSESELECT, hmenu));
871         }
872     }
873     else if (sendMenuSelect)
874         SendMessage16( hwndOwner, WM_MENUSELECT, hmenu,
875                        MAKELONG( lppop->wFlags | MF_MOUSESELECT, hmenu ) );
876
877     ReleaseDC( lppop->hWnd, hdc );
878 }
879
880
881 /***********************************************************************
882  *           MENU_SelectNextItem
883  */
884 static void MENU_SelectNextItem( HWND hwndOwner, HMENU hmenu )
885 {
886     int i;
887     POPUPMENU *menu;
888
889     menu = (POPUPMENU *) USER_HEAP_LIN_ADDR( hmenu );
890     if (!menu->items) return;
891     if ((menu->FocusedItem != NO_SELECTED_ITEM) &&
892         (menu->FocusedItem != SYSMENU_SELECTED))
893     {
894         for (i = menu->FocusedItem+1; i < menu->nItems; i++)
895         {
896             if (!(menu->items[i].item_flags & MF_SEPARATOR))
897             {
898                 MENU_SelectItem( hwndOwner, hmenu, i, TRUE );
899                 return;
900             }
901         }
902         if (MENU_HasSysMenu( menu ))
903         {
904             MENU_SelectItem( hwndOwner, hmenu, SYSMENU_SELECTED, TRUE );
905             return;
906         }
907     }
908     for (i = 0; i < menu->nItems; i++)
909     {
910         if (!(menu->items[i].item_flags & MF_SEPARATOR))
911         {
912             MENU_SelectItem( hwndOwner, hmenu, i, TRUE );
913             return;
914         }
915     }
916     if (MENU_HasSysMenu( menu ))
917         MENU_SelectItem( hwndOwner, hmenu, SYSMENU_SELECTED, TRUE );
918 }
919
920
921 /***********************************************************************
922  *           MENU_SelectPrevItem
923  */
924 static void MENU_SelectPrevItem( HWND hwndOwner, HMENU hmenu )
925 {
926     int i;
927     POPUPMENU *menu;
928
929     menu = (POPUPMENU *) USER_HEAP_LIN_ADDR( hmenu );
930     if (!menu->items) return;
931     if ((menu->FocusedItem != NO_SELECTED_ITEM) &&
932         (menu->FocusedItem != SYSMENU_SELECTED))
933     {
934         for (i = menu->FocusedItem - 1; i >= 0; i--)
935         {
936             if (!(menu->items[i].item_flags & MF_SEPARATOR))
937             {
938                 MENU_SelectItem( hwndOwner, hmenu, i, TRUE );
939                 return;
940             }
941         }
942         if (MENU_HasSysMenu( menu ))
943         {
944             MENU_SelectItem( hwndOwner, hmenu, SYSMENU_SELECTED, TRUE );
945             return;
946         }
947     }
948     for (i = menu->nItems - 1; i > 0; i--)
949     {
950         if (!(menu->items[i].item_flags & MF_SEPARATOR))
951         {
952             MENU_SelectItem( hwndOwner, hmenu, i, TRUE );
953             return;
954         }
955     }
956     if (MENU_HasSysMenu( menu ))
957         MENU_SelectItem( hwndOwner, hmenu, SYSMENU_SELECTED, TRUE );
958 }
959
960
961 /**********************************************************************
962  *         MENU_SetItemData
963  *
964  * Set an item flags, id and text ptr.
965  */
966 static BOOL MENU_SetItemData( MENUITEM *item, UINT flags, UINT id, LPCSTR str )
967 {
968     LPSTR prevText = IS_STRING_ITEM(item->item_flags) ? item->text : NULL;
969
970     if (IS_STRING_ITEM(flags))
971     {
972         if (!str)
973         {
974             flags |= MF_SEPARATOR;
975             item->text = NULL;
976         }
977         else
978         {
979             LPSTR text;
980             /* Item beginning with a backspace is a help item */
981             if (*str == '\b')
982             {
983                 flags |= MF_HELP;
984                 str++;
985             }
986             if (!(text = HEAP_strdupA( SystemHeap, 0, str ))) return FALSE;
987             item->text = text;
988         }
989     }
990     else if ((flags & MF_BITMAP) || (flags & MF_OWNERDRAW))
991         item->text = (LPSTR)str;
992     else item->text = NULL;
993
994     item->item_flags = flags & ~(MF_HILITE | MF_MOUSESELECT);
995     item->item_id    = id;
996     SetRectEmpty16( &item->rect );
997     if (prevText) HeapFree( SystemHeap, 0, prevText );
998     return TRUE;
999 }
1000
1001
1002 /**********************************************************************
1003  *         MENU_InsertItem
1004  *
1005  * Insert a new item into a menu.
1006  */
1007 static MENUITEM *MENU_InsertItem( HMENU hMenu, UINT pos, UINT flags )
1008 {
1009     MENUITEM *newItems;
1010     POPUPMENU *menu;
1011
1012     if (!(menu = (POPUPMENU *)USER_HEAP_LIN_ADDR(hMenu))) 
1013     {
1014         dprintf_menu( stddeb, "MENU_InsertItem: %04x not a menu handle\n",
1015                       hMenu );
1016         return NULL;
1017     }
1018
1019     /* Find where to insert new item */
1020
1021     if ((flags & MF_BYPOSITION) &&
1022         ((pos == (UINT)-1) || (pos == menu->nItems)))
1023     {
1024         /* Special case: append to menu */
1025         /* Some programs specify the menu length to do that */
1026         pos = menu->nItems;
1027     }
1028     else
1029     {
1030         if (!MENU_FindItem( &hMenu, &pos, flags )) 
1031         {
1032             dprintf_menu( stddeb, "MENU_InsertItem: item %x not found\n",
1033                           pos );
1034             return NULL;
1035         }
1036         if (!(menu = (LPPOPUPMENU) USER_HEAP_LIN_ADDR(hMenu)))
1037         {
1038             dprintf_menu(stddeb,"MENU_InsertItem: %04x not a menu handle\n",
1039                          hMenu);
1040             return NULL;
1041         }
1042     }
1043
1044     /* Create new items array */
1045
1046     newItems = HeapAlloc( SystemHeap, 0, sizeof(MENUITEM) * (menu->nItems+1) );
1047     if (!newItems)
1048     {
1049         dprintf_menu( stddeb, "MENU_InsertItem: allocation failed\n" );
1050         return NULL;
1051     }
1052     if (menu->nItems > 0)
1053     {
1054           /* Copy the old array into the new */
1055         if (pos > 0) memcpy( newItems, menu->items, pos * sizeof(MENUITEM) );
1056         if (pos < menu->nItems) memcpy( &newItems[pos+1], &menu->items[pos],
1057                                         (menu->nItems-pos)*sizeof(MENUITEM) );
1058         HeapFree( SystemHeap, 0, menu->items );
1059     }
1060     menu->items = newItems;
1061     menu->nItems++;
1062     memset( &newItems[pos], 0, sizeof(*newItems) );
1063     return &newItems[pos];
1064 }
1065
1066
1067 /**********************************************************************
1068  *         MENU_ParseResource
1069  *
1070  * Parse a standard menu resource and add items to the menu.
1071  * Return a pointer to the end of the resource.
1072  */
1073 static LPCSTR MENU_ParseResource( LPCSTR res, HMENU hMenu, BOOL unicode )
1074 {
1075     WORD flags, id = 0;
1076     LPCSTR str;
1077
1078     do
1079     {
1080         flags = GET_WORD(res);
1081         res += sizeof(WORD);
1082         if (!(flags & MF_POPUP))
1083         {
1084             id = GET_WORD(res);
1085             res += sizeof(WORD);
1086         }
1087         if (!IS_STRING_ITEM(flags))
1088             fprintf( stderr, "MENU_ParseResource: not a string item %04x\n",
1089                      flags );
1090         str = res;
1091         if (!unicode) res += strlen(str) + 1;
1092         else res += (lstrlen32W((LPCWSTR)str) + 1) * sizeof(WCHAR);
1093         if (flags & MF_POPUP)
1094         {
1095             HMENU hSubMenu = CreatePopupMenu();
1096             if (!hSubMenu) return NULL;
1097             if (!(res = MENU_ParseResource( res, hSubMenu, unicode )))
1098                 return NULL;
1099             if (!unicode) AppendMenu32A( hMenu, flags, (UINT)hSubMenu, str );
1100             else AppendMenu32W( hMenu, flags, (UINT)hSubMenu, (LPCWSTR)str );
1101         }
1102         else  /* Not a popup */
1103         {
1104             if (!unicode) AppendMenu32A( hMenu, flags, id, *str ? str : NULL );
1105             else AppendMenu32W( hMenu, flags, id,
1106                                 *(LPCWSTR)str ? (LPCWSTR)str : NULL );
1107         }
1108     } while (!(flags & MF_END));
1109     return res;
1110 }
1111
1112
1113 /***********************************************************************
1114  *           MENU_GetSubPopup
1115  *
1116  * Return the handle of the selected sub-popup menu (if any).
1117  */
1118 static HMENU MENU_GetSubPopup( HMENU hmenu )
1119 {
1120     POPUPMENU *menu;
1121     MENUITEM *item;
1122
1123     menu = (POPUPMENU *) USER_HEAP_LIN_ADDR( hmenu );
1124     if (menu->FocusedItem == NO_SELECTED_ITEM) return 0;
1125     else if (menu->FocusedItem == SYSMENU_SELECTED)
1126         return WIN_FindWndPtr(menu->hWnd)->hSysMenu;
1127
1128     item = &menu->items[menu->FocusedItem];
1129     if (!(item->item_flags & MF_POPUP) || !(item->item_flags & MF_MOUSESELECT))
1130         return 0;
1131     return (HMENU)item->item_id;
1132 }
1133
1134
1135 /***********************************************************************
1136  *           MENU_HideSubPopups
1137  *
1138  * Hide the sub-popup menus of this menu.
1139  */
1140 static void MENU_HideSubPopups( HWND hwndOwner, HMENU hmenu,
1141                                 BOOL sendMenuSelect )
1142 {
1143     MENUITEM *item;
1144     POPUPMENU *menu, *submenu;
1145     HMENU hsubmenu;
1146
1147     if (!(menu = (POPUPMENU *) USER_HEAP_LIN_ADDR( hmenu ))) return;
1148     if (menu->FocusedItem == NO_SELECTED_ITEM) return;
1149     if (menu->FocusedItem == SYSMENU_SELECTED)
1150     {
1151         hsubmenu = WIN_FindWndPtr(menu->hWnd)->hSysMenu;
1152     }
1153     else
1154     {
1155         item = &menu->items[menu->FocusedItem];
1156         if (!(item->item_flags & MF_POPUP) ||
1157             !(item->item_flags & MF_MOUSESELECT)) return;
1158         item->item_flags &= ~MF_MOUSESELECT;
1159         hsubmenu = (HMENU)item->item_id;
1160     }
1161     submenu = (POPUPMENU *) USER_HEAP_LIN_ADDR( hsubmenu );
1162     MENU_HideSubPopups( hwndOwner, hsubmenu, FALSE );
1163     MENU_SelectItem( hwndOwner, hsubmenu, NO_SELECTED_ITEM, sendMenuSelect );
1164     if (submenu->hWnd == pTopPWnd->hwndSelf ) 
1165     {
1166         ShowWindow( submenu->hWnd, SW_HIDE );
1167         uSubPWndLevel = 0;
1168     }
1169     else
1170     {
1171         DestroyWindow( submenu->hWnd );
1172         submenu->hWnd = 0;
1173     }
1174 }
1175
1176
1177 /***********************************************************************
1178  *           MENU_ShowSubPopup
1179  *
1180  * Display the sub-menu of the selected item of this menu.
1181  * Return the handle of the submenu, or hmenu if no submenu to display.
1182  */
1183 static HMENU MENU_ShowSubPopup( HWND hwndOwner, HMENU hmenu, BOOL selectFirst )
1184 {
1185     POPUPMENU *menu;
1186     MENUITEM *item;
1187     WND *wndPtr;
1188
1189     if (!(menu = (POPUPMENU *) USER_HEAP_LIN_ADDR( hmenu ))) return hmenu;
1190     if (!(wndPtr = WIN_FindWndPtr( menu->hWnd ))) return hmenu;
1191     if (menu->FocusedItem == NO_SELECTED_ITEM) return hmenu;
1192     if (menu->FocusedItem == SYSMENU_SELECTED)
1193     {
1194         MENU_ShowPopup(hwndOwner, wndPtr->hSysMenu, 0, wndPtr->rectClient.left,
1195                 wndPtr->rectClient.top - menu->Height - 2*SYSMETRICS_CYBORDER);
1196         if (selectFirst) MENU_SelectNextItem( hwndOwner, wndPtr->hSysMenu );
1197         return wndPtr->hSysMenu;
1198     }
1199     item = &menu->items[menu->FocusedItem];
1200     if (!(item->item_flags & MF_POPUP) ||
1201         (item->item_flags & (MF_GRAYED | MF_DISABLED))) return hmenu;
1202     item->item_flags |= MF_MOUSESELECT;
1203     if (menu->wFlags & MF_POPUP)
1204     {
1205         MENU_ShowPopup( hwndOwner, (HMENU)item->item_id, menu->FocusedItem,
1206                  wndPtr->rectWindow.left + item->rect.right-arrow_bitmap_width,
1207                  wndPtr->rectWindow.top + item->rect.top );
1208     }
1209     else
1210     {
1211         MENU_ShowPopup( hwndOwner, (HMENU)item->item_id, menu->FocusedItem,
1212                         wndPtr->rectWindow.left + item->rect.left,
1213                         wndPtr->rectWindow.top + item->rect.bottom );
1214     }
1215     if (selectFirst) MENU_SelectNextItem( hwndOwner, (HMENU)item->item_id );
1216     return (HMENU)item->item_id;
1217 }
1218
1219
1220 /***********************************************************************
1221  *           MENU_FindMenuByCoords
1222  *
1223  * Find the menu containing a given point (in screen coords).
1224  */
1225 static HMENU MENU_FindMenuByCoords( HMENU hmenu, POINT16 pt )
1226 {
1227     POPUPMENU *menu;
1228     HWND hwnd;
1229
1230     if (!(hwnd = WindowFromPoint16( pt ))) return 0;
1231     while (hmenu)
1232     {
1233         menu = (POPUPMENU *) USER_HEAP_LIN_ADDR( hmenu );
1234         if (menu->hWnd == hwnd)
1235         {
1236             if (!(menu->wFlags & MF_POPUP))
1237             {
1238                   /* Make sure it's in the menu bar (or in system menu) */
1239                 WND *wndPtr = WIN_FindWndPtr( menu->hWnd );
1240                 if ((pt.x < wndPtr->rectClient.left) ||
1241                     (pt.x >= wndPtr->rectClient.right) ||
1242                     (pt.y >= wndPtr->rectClient.top)) return 0;
1243                 if (pt.y < wndPtr->rectClient.top - menu->Height)
1244                 {
1245                     if (!MENU_IsInSysMenu( menu, pt )) return 0;
1246                 }
1247                 /* else it's in the menu bar */
1248             }
1249             return hmenu;
1250         }
1251         hmenu = MENU_GetSubPopup( hmenu );
1252     }
1253     return 0;
1254 }
1255
1256
1257 /***********************************************************************
1258  *           MENU_ExecFocusedItem
1259  *
1260  * Execute a menu item (for instance when user pressed Enter).
1261  * Return TRUE if we can go on with menu tracking.
1262  */
1263 static BOOL MENU_ExecFocusedItem( HWND hwndOwner, HMENU hmenu,
1264                                   HMENU *hmenuCurrent )
1265 {
1266     MENUITEM *item;
1267     POPUPMENU *menu = (POPUPMENU *) USER_HEAP_LIN_ADDR( hmenu );
1268     if (!menu || !menu->nItems || (menu->FocusedItem == NO_SELECTED_ITEM) ||
1269         (menu->FocusedItem == SYSMENU_SELECTED)) return TRUE;
1270     item = &menu->items[menu->FocusedItem];
1271     if (!(item->item_flags & MF_POPUP))
1272     {
1273         if (!(item->item_flags & (MF_GRAYED | MF_DISABLED)))
1274         {
1275             PostMessage( hwndOwner, (menu->wFlags & MF_SYSMENU) ? 
1276                         WM_SYSCOMMAND : WM_COMMAND, item->item_id, 0 );
1277             return FALSE;
1278         }
1279         else return TRUE;
1280     }
1281     else
1282     {
1283         *hmenuCurrent = MENU_ShowSubPopup( hwndOwner, hmenu, TRUE );
1284         return TRUE;
1285     }
1286 }
1287
1288
1289 /***********************************************************************
1290  *           MENU_ButtonDown
1291  *
1292  * Handle a button-down event in a menu. Point is in screen coords.
1293  * hmenuCurrent is the top-most visible popup.
1294  * Return TRUE if we can go on with menu tracking.
1295  */
1296 static BOOL MENU_ButtonDown( HWND hwndOwner, HMENU hmenu, HMENU *hmenuCurrent,
1297                              POINT16 pt )
1298 {
1299     POPUPMENU *menu;
1300     MENUITEM *item;
1301     UINT id;
1302
1303     if (!hmenu) return FALSE;  /* Outside all menus */
1304     menu = (POPUPMENU *) USER_HEAP_LIN_ADDR( hmenu );
1305     item = MENU_FindItemByCoords( menu, pt.x, pt.y, &id );
1306     if (!item)  /* Maybe in system menu */
1307     {
1308         if (!MENU_IsInSysMenu( menu, pt )) return FALSE;
1309         id = SYSMENU_SELECTED;
1310     }   
1311
1312     if (menu->FocusedItem == id)
1313     {
1314         if (id == SYSMENU_SELECTED) return FALSE;
1315         if (item->item_flags & MF_POPUP)
1316         {
1317             if (item->item_flags & MF_MOUSESELECT)
1318             {
1319                 if (menu->wFlags & MF_POPUP)
1320                 {
1321                     MENU_HideSubPopups( hwndOwner, hmenu, TRUE );
1322                     *hmenuCurrent = hmenu;
1323                 }
1324                 else return FALSE;
1325             }
1326             else *hmenuCurrent = MENU_ShowSubPopup( hwndOwner, hmenu, FALSE );
1327         }
1328     }
1329     else
1330     {
1331         MENU_HideSubPopups( hwndOwner, hmenu, FALSE );
1332         MENU_SelectItem( hwndOwner, hmenu, id, TRUE );
1333         *hmenuCurrent = MENU_ShowSubPopup( hwndOwner, hmenu, FALSE );
1334     }
1335     return TRUE;
1336 }
1337
1338
1339 /***********************************************************************
1340  *           MENU_ButtonUp
1341  *
1342  * Handle a button-up event in a menu. Point is in screen coords.
1343  * hmenuCurrent is the top-most visible popup.
1344  * Return TRUE if we can go on with menu tracking.
1345  */
1346 static BOOL MENU_ButtonUp( HWND hwndOwner, HMENU hmenu, HMENU *hmenuCurrent,
1347                            POINT16 pt )
1348 {
1349     POPUPMENU *menu;
1350     MENUITEM *item;
1351     HMENU hsubmenu = 0;
1352     UINT id;
1353
1354     if (!hmenu) return FALSE;  /* Outside all menus */
1355     menu = (POPUPMENU *) USER_HEAP_LIN_ADDR( hmenu );
1356     item = MENU_FindItemByCoords( menu, pt.x, pt.y, &id );
1357     if (!item)  /* Maybe in system menu */
1358     {
1359         if (!MENU_IsInSysMenu( menu, pt )) return FALSE;
1360         id = SYSMENU_SELECTED;
1361         hsubmenu = WIN_FindWndPtr(menu->hWnd)->hSysMenu;
1362     }   
1363
1364     if (menu->FocusedItem != id) return FALSE;
1365
1366     if (id != SYSMENU_SELECTED)
1367     {
1368         if (!(item->item_flags & MF_POPUP))
1369         {
1370             return MENU_ExecFocusedItem( hwndOwner, hmenu, hmenuCurrent );
1371         }
1372         hsubmenu = (HMENU)item->item_id;
1373     }
1374       /* Select first item of sub-popup */
1375     MENU_SelectItem( hwndOwner, hsubmenu, NO_SELECTED_ITEM, FALSE );
1376     MENU_SelectNextItem( hwndOwner, hsubmenu );
1377     return TRUE;
1378 }
1379
1380
1381 /***********************************************************************
1382  *           MENU_MouseMove
1383  *
1384  * Handle a motion event in a menu. Point is in screen coords.
1385  * hmenuCurrent is the top-most visible popup.
1386  * Return TRUE if we can go on with menu tracking.
1387  */
1388 static BOOL MENU_MouseMove( HWND hwndOwner, HMENU hmenu, HMENU *hmenuCurrent,
1389                             POINT16 pt )
1390 {
1391     MENUITEM *item;
1392     POPUPMENU *menu = (POPUPMENU *) USER_HEAP_LIN_ADDR( hmenu );
1393     UINT id = NO_SELECTED_ITEM;
1394
1395     if (hmenu)
1396     {
1397         item = MENU_FindItemByCoords( menu, pt.x, pt.y, &id );
1398         if (!item)  /* Maybe in system menu */
1399         {
1400             if (!MENU_IsInSysMenu( menu, pt ))
1401                 id = NO_SELECTED_ITEM;  /* Outside all items */
1402             else id = SYSMENU_SELECTED;
1403         }
1404     }   
1405     if (id == NO_SELECTED_ITEM)
1406     {
1407         MENU_SelectItem( hwndOwner, *hmenuCurrent, NO_SELECTED_ITEM, TRUE );
1408     }
1409     else if (menu->FocusedItem != id)
1410     {
1411         MENU_HideSubPopups( hwndOwner, hmenu, FALSE );
1412         MENU_SelectItem( hwndOwner, hmenu, id, TRUE );
1413         *hmenuCurrent = MENU_ShowSubPopup( hwndOwner, hmenu, FALSE );
1414     }
1415     return TRUE;
1416 }
1417
1418
1419 /***********************************************************************
1420  *           MENU_KeyLeft
1421  *
1422  * Handle a VK_LEFT key event in a menu.
1423  * hmenuCurrent is the top-most visible popup.
1424  */
1425 static void MENU_KeyLeft( HWND hwndOwner, HMENU hmenu, HMENU *hmenuCurrent )
1426 {
1427     POPUPMENU *menu;
1428     HMENU hmenutmp, hmenuprev;
1429
1430     menu = (POPUPMENU *) USER_HEAP_LIN_ADDR( hmenu );
1431     hmenuprev = hmenutmp = hmenu;
1432     while (hmenutmp != *hmenuCurrent)
1433     {
1434         hmenutmp = MENU_GetSubPopup( hmenuprev );
1435         if (hmenutmp != *hmenuCurrent) hmenuprev = hmenutmp;
1436     }
1437     MENU_HideSubPopups( hwndOwner, hmenuprev, TRUE );
1438
1439     if ((hmenuprev == hmenu) && !(menu->wFlags & MF_POPUP))
1440     {
1441           /* Select previous item on the menu bar */
1442         MENU_SelectPrevItem( hwndOwner, hmenu );
1443         if (*hmenuCurrent != hmenu)
1444         {
1445               /* A popup menu was displayed -> display the next one */
1446             *hmenuCurrent = MENU_ShowSubPopup( hwndOwner, hmenu, TRUE );
1447         }
1448     }
1449     else *hmenuCurrent = hmenuprev;
1450 }
1451
1452
1453 /***********************************************************************
1454  *           MENU_KeyRight
1455  *
1456  * Handle a VK_RIGHT key event in a menu.
1457  * hmenuCurrent is the top-most visible popup.
1458  */
1459 static void MENU_KeyRight( HWND hwndOwner, HMENU hmenu, HMENU *hmenuCurrent )
1460 {
1461     POPUPMENU *menu;
1462     HMENU hmenutmp;
1463
1464     menu = (POPUPMENU *) USER_HEAP_LIN_ADDR( hmenu );
1465
1466     if ((menu->wFlags & MF_POPUP) || (*hmenuCurrent != hmenu))
1467     {
1468           /* If already displaying a popup, try to display sub-popup */
1469         hmenutmp = MENU_ShowSubPopup( hwndOwner, *hmenuCurrent, TRUE );
1470         if (hmenutmp != *hmenuCurrent)  /* Sub-popup displayed */
1471         {
1472             *hmenuCurrent = hmenutmp;
1473             return;
1474         }
1475     }
1476
1477       /* If on menu-bar, go to next item */
1478     if (!(menu->wFlags & MF_POPUP))
1479     {
1480         MENU_HideSubPopups( hwndOwner, hmenu, FALSE );
1481         MENU_SelectNextItem( hwndOwner, hmenu );
1482         if (*hmenuCurrent != hmenu)
1483         {
1484               /* A popup menu was displayed -> display the next one */
1485             *hmenuCurrent = MENU_ShowSubPopup( hwndOwner, hmenu, TRUE );
1486         }
1487     }
1488     else if (*hmenuCurrent != hmenu)  /* Hide last level popup */
1489     {
1490         HMENU hmenuprev;
1491         hmenuprev = hmenutmp = hmenu;
1492         while (hmenutmp != *hmenuCurrent)
1493         {
1494             hmenutmp = MENU_GetSubPopup( hmenuprev );
1495             if (hmenutmp != *hmenuCurrent) hmenuprev = hmenutmp;
1496         }
1497         MENU_HideSubPopups( hwndOwner, hmenuprev, TRUE );
1498         *hmenuCurrent = hmenuprev;
1499     }
1500 }
1501
1502
1503 /***********************************************************************
1504  *           MENU_TrackMenu
1505  *
1506  * Menu tracking code.
1507  * If 'x' and 'y' are not 0, we simulate a button-down event at (x,y)
1508  * before beginning tracking. This is to help menu-bar tracking.
1509  */
1510 static BOOL MENU_TrackMenu( HMENU hmenu, UINT wFlags, int x, int y,
1511                             HWND hwnd, const RECT16 *lprect )
1512 {
1513     MSG16 *msg;
1514     HLOCAL16 hMsg;
1515     POPUPMENU *menu;
1516     HMENU hmenuCurrent = hmenu;
1517     BOOL fClosed = FALSE, fRemove;
1518     UINT pos;
1519
1520     fEndMenuCalled = FALSE;
1521     if (!(menu = (POPUPMENU *) USER_HEAP_LIN_ADDR( hmenu ))) return FALSE;
1522     if (x && y)
1523     {
1524         POINT16 pt = { x, y };
1525         MENU_ButtonDown( hwnd, hmenu, &hmenuCurrent, pt );
1526     }
1527     SetCapture( hwnd );
1528     hMsg = USER_HEAP_ALLOC( sizeof(MSG16) );
1529     msg = (MSG16 *)USER_HEAP_LIN_ADDR( hMsg );
1530     while (!fClosed)
1531     {
1532         if (!MSG_InternalGetMessage( (SEGPTR)USER_HEAP_SEG_ADDR(hMsg), 0,
1533                                      hwnd, MSGF_MENU, 0, TRUE ))
1534             break;
1535
1536         TranslateMessage( msg );
1537
1538         fRemove = FALSE;
1539         if ((msg->message >= WM_MOUSEFIRST) && (msg->message <= WM_MOUSELAST))
1540         {
1541               /* Find the sub-popup for this mouse event (if any) */
1542             HMENU hsubmenu = MENU_FindMenuByCoords( hmenu, msg->pt );
1543
1544             switch(msg->message)
1545             {
1546             case WM_RBUTTONDOWN:
1547             case WM_NCRBUTTONDOWN:
1548                 if (!(wFlags & TPM_RIGHTBUTTON)) break;
1549                 /* fall through */
1550             case WM_LBUTTONDOWN:
1551             case WM_NCLBUTTONDOWN:
1552                 fClosed = !MENU_ButtonDown( hwnd, hsubmenu,
1553                                             &hmenuCurrent, msg->pt );
1554                 break;
1555                 
1556             case WM_RBUTTONUP:
1557             case WM_NCRBUTTONUP:
1558                 if (!(wFlags & TPM_RIGHTBUTTON)) break;
1559                 /* fall through */
1560             case WM_LBUTTONUP:
1561             case WM_NCLBUTTONUP:
1562                   /* If outside all menus but inside lprect, ignore it */
1563                 if (!hsubmenu && lprect && PtInRect16(lprect, msg->pt)) break;
1564                 fClosed = !MENU_ButtonUp( hwnd, hsubmenu,
1565                                           &hmenuCurrent, msg->pt );
1566                 fRemove = TRUE;  /* Remove event even if outside menu */
1567                 break;
1568                 
1569             case WM_MOUSEMOVE:
1570             case WM_NCMOUSEMOVE:
1571                 if ((msg->wParam & MK_LBUTTON) ||
1572                     ((wFlags & TPM_RIGHTBUTTON) && (msg->wParam & MK_RBUTTON)))
1573                 {
1574                     fClosed = !MENU_MouseMove( hwnd, hsubmenu,
1575                                                &hmenuCurrent, msg->pt );
1576                 }
1577                 break;
1578             }
1579         }
1580         else if ((msg->message >= WM_KEYFIRST) && (msg->message <= WM_KEYLAST))
1581         {
1582             fRemove = TRUE;  /* Keyboard messages are always removed */
1583             switch(msg->message)
1584             {
1585             case WM_KEYDOWN:
1586                 switch(msg->wParam)
1587                 {
1588                 case VK_HOME:
1589                     MENU_SelectItem( hwnd, hmenuCurrent, NO_SELECTED_ITEM, FALSE );
1590                     MENU_SelectNextItem( hwnd, hmenuCurrent );
1591                     break;
1592
1593                 case VK_END:
1594                     MENU_SelectItem( hwnd, hmenuCurrent, NO_SELECTED_ITEM, FALSE );
1595                     MENU_SelectPrevItem( hwnd, hmenuCurrent );
1596                     break;
1597
1598                 case VK_UP:
1599                     MENU_SelectPrevItem( hwnd, hmenuCurrent );
1600                     break;
1601
1602                 case VK_DOWN:
1603                       /* If on menu bar, pull-down the menu */
1604                     if (!(menu->wFlags & MF_POPUP) && (hmenuCurrent == hmenu))
1605                         hmenuCurrent = MENU_ShowSubPopup( hwnd, hmenu, TRUE );
1606                     else
1607                         MENU_SelectNextItem( hwnd, hmenuCurrent );
1608                     break;
1609
1610                 case VK_LEFT:
1611                     MENU_KeyLeft( hwnd, hmenu, &hmenuCurrent );
1612                     break;
1613                     
1614                 case VK_RIGHT:
1615                     MENU_KeyRight( hwnd, hmenu, &hmenuCurrent );
1616                     break;
1617                     
1618                 case VK_SPACE:
1619                 case VK_RETURN:
1620                     fClosed = !MENU_ExecFocusedItem( hwnd, hmenuCurrent,
1621                                                      &hmenuCurrent );
1622                     break;
1623
1624                 case VK_ESCAPE:
1625                     fClosed = TRUE;
1626                     break;
1627
1628                 default:
1629                     break;
1630                 }
1631                 break;  /* WM_KEYDOWN */
1632
1633             case WM_SYSKEYDOWN:
1634                 switch(msg->wParam)
1635                 {
1636                 case VK_MENU:
1637                     fClosed = TRUE;
1638                     break;
1639                     
1640                 }
1641                 break;  /* WM_SYSKEYDOWN */
1642
1643             case WM_CHAR:
1644                 {
1645                       /* Hack to avoid control chars. */
1646                       /* We will find a better way real soon... */
1647                     if ((msg->wParam <= 32) || (msg->wParam >= 127)) break;
1648                     pos = MENU_FindItemByKey( hwnd, hmenuCurrent, msg->wParam );
1649                     if (pos == (UINT)-2) fClosed = TRUE;
1650                     else if (pos == (UINT)-1) MessageBeep(0);
1651                     else
1652                     {
1653                         MENU_SelectItem( hwnd, hmenuCurrent, pos, TRUE );
1654                         fClosed = !MENU_ExecFocusedItem( hwnd, hmenuCurrent,
1655                                                          &hmenuCurrent );
1656                         
1657                     }
1658                 }                   
1659                 break;  /* WM_CHAR */
1660             }  /* switch(msg->message) */
1661         }
1662         else
1663         {
1664             DispatchMessage( msg );
1665         }
1666         if (fEndMenuCalled) fClosed = TRUE;
1667         if (!fClosed) fRemove = TRUE;
1668
1669         if (fRemove)  /* Remove the message from the queue */
1670             PeekMessage16( msg, 0, msg->message, msg->message, PM_REMOVE );
1671     }
1672     USER_HEAP_FREE( hMsg );
1673     ReleaseCapture();
1674     MENU_HideSubPopups( hwnd, hmenu, FALSE );
1675     if (menu->wFlags & MF_POPUP) 
1676     {
1677          ShowWindow( menu->hWnd, SW_HIDE );
1678          uSubPWndLevel = 0;
1679     }
1680     MENU_SelectItem( hwnd, hmenu, NO_SELECTED_ITEM, FALSE );
1681     SendMessage16( hwnd, WM_MENUSELECT, 0, MAKELONG( 0xffff, 0 ) );
1682     fEndMenuCalled = FALSE;
1683     return TRUE;
1684 }
1685
1686
1687 /***********************************************************************
1688  *           MENU_TrackMouseMenuBar
1689  *
1690  * Menu-bar tracking upon a mouse event. Called from NC_HandleSysCommand().
1691  */
1692 void MENU_TrackMouseMenuBar( HWND hwnd, POINT16 pt )
1693 {
1694     WND *wndPtr = WIN_FindWndPtr( hwnd );
1695     HideCaret(0);
1696     SendMessage16( hwnd, WM_ENTERMENULOOP, 0, 0 );
1697     SendMessage16( hwnd, WM_INITMENU, wndPtr->wIDmenu, 0 );
1698     MENU_TrackMenu( (HMENU)wndPtr->wIDmenu, TPM_LEFTALIGN | TPM_LEFTBUTTON,
1699                     pt.x, pt.y, hwnd, NULL );
1700     SendMessage16( hwnd, WM_EXITMENULOOP, 0, 0 );
1701     ShowCaret(0);
1702 }
1703
1704
1705 /***********************************************************************
1706  *           MENU_TrackKbdMenuBar
1707  *
1708  * Menu-bar tracking upon a keyboard event. Called from NC_HandleSysCommand().
1709  */
1710 void MENU_TrackKbdMenuBar( WND* wndPtr, UINT wParam, INT vkey)
1711 {
1712     UINT uItem = NO_SELECTED_ITEM;
1713    HMENU hTrackMenu; 
1714
1715     /* find window that has a menu 
1716      */
1717  
1718     if( !(wndPtr->dwStyle & WS_CHILD) )
1719       {
1720           wndPtr = WIN_FindWndPtr( GetActiveWindow() );
1721           if( !wndPtr ) return;
1722       }
1723     else
1724       while( wndPtr->dwStyle & WS_CHILD && 
1725            !(wndPtr->dwStyle & WS_SYSMENU) )
1726            if( !(wndPtr = wndPtr->parent) ) return;
1727           
1728     if( wndPtr->dwStyle & WS_CHILD || !wndPtr->wIDmenu )
1729       if( !(wndPtr->dwStyle & WS_SYSMENU) )
1730         return;
1731
1732     hTrackMenu = ( IsMenu( wndPtr->wIDmenu ) )? wndPtr->wIDmenu:
1733                                                 wndPtr->hSysMenu;
1734
1735     HideCaret(0);
1736     SendMessage16( wndPtr->hwndSelf, WM_ENTERMENULOOP, 0, 0 );
1737     SendMessage16( wndPtr->hwndSelf, WM_INITMENU, wndPtr->wIDmenu, 0 );
1738
1739     /* find suitable menu entry 
1740      */
1741
1742     if( vkey == VK_SPACE )
1743         uItem = SYSMENU_SELECTED;
1744     else if( vkey )
1745       {
1746         uItem = MENU_FindItemByKey( wndPtr->hwndSelf, wndPtr->wIDmenu, vkey );
1747         if( uItem >= 0xFFFE )
1748           {
1749             if( uItem == 0xFFFF ) 
1750                 MessageBeep(0);
1751             SendMessage16( wndPtr->hwndSelf, WM_EXITMENULOOP, 0, 0 );
1752             ShowCaret(0);
1753             return;
1754           }
1755       }
1756
1757     MENU_SelectItem( wndPtr->hwndSelf, hTrackMenu, uItem, TRUE );
1758     if( uItem == NO_SELECTED_ITEM )
1759       MENU_SelectNextItem( wndPtr->hwndSelf, hTrackMenu );
1760     else
1761       PostMessage( wndPtr->hwndSelf, WM_KEYDOWN, VK_DOWN, 0L );
1762
1763     MENU_TrackMenu( hTrackMenu, TPM_LEFTALIGN | TPM_LEFTBUTTON,
1764                     0, 0, wndPtr->hwndSelf, NULL );
1765
1766     SendMessage16( wndPtr->hwndSelf, WM_EXITMENULOOP, 0, 0 );
1767     ShowCaret(0);
1768 }
1769
1770
1771 /**********************************************************************
1772  *           TrackPopupMenu16   (USER.416)
1773  */
1774 BOOL16 TrackPopupMenu16( HMENU16 hMenu, UINT16 wFlags, INT16 x, INT16 y,
1775                          INT16 nReserved, HWND16 hWnd, const RECT16 *lpRect )
1776 {
1777     BOOL ret = FALSE;
1778
1779     HideCaret(0);
1780     if (MENU_ShowPopup( hWnd, hMenu, 0, x, y )) 
1781         ret = MENU_TrackMenu( hMenu, wFlags, 0, 0, hWnd, lpRect );
1782     ShowCaret(0);
1783     return ret;
1784 }
1785
1786
1787 /**********************************************************************
1788  *           TrackPopupMenu32   (USER32.548)
1789  */
1790 BOOL32 TrackPopupMenu32( HMENU32 hMenu, UINT32 wFlags, INT32 x, INT32 y,
1791                          INT32 nReserved, HWND32 hWnd, const RECT32 *lpRect )
1792 {
1793     RECT16 r;
1794     CONV_RECT32TO16( lpRect, &r );
1795     return TrackPopupMenu16( hMenu, wFlags, x, y, nReserved, hWnd, &r );
1796 }
1797
1798
1799 /***********************************************************************
1800  *           PopupMenuWndProc
1801  */
1802 LRESULT PopupMenuWndProc(HWND hwnd,UINT message,WPARAM wParam,LPARAM lParam)
1803 {    
1804     switch(message)
1805     {
1806     case WM_CREATE:
1807         {
1808             CREATESTRUCT16 *cs = (CREATESTRUCT16*)PTR_SEG_TO_LIN(lParam);
1809             SetWindowLong32A( hwnd, 0, (LONG)cs->lpCreateParams );
1810             return 0;
1811         }
1812
1813     case WM_MOUSEACTIVATE:  /* We don't want to be activated */
1814         return MA_NOACTIVATE;
1815
1816     case WM_PAINT:
1817         {
1818             PAINTSTRUCT16 ps;
1819             BeginPaint16( hwnd, &ps );
1820             MENU_DrawPopupMenu( hwnd, ps.hdc,
1821                                 (HMENU)GetWindowLong32A( hwnd, 0 ) );
1822             EndPaint16( hwnd, &ps );
1823             return 0;
1824         }
1825
1826     case WM_DESTROY:
1827             /* zero out global pointer in case system popup
1828              * was destroyed by AppExit 
1829              */
1830
1831             if( hwnd == pTopPWnd->hwndSelf )
1832                 pTopPWnd = 0;
1833             else
1834                 uSubPWndLevel--;
1835             break;
1836
1837     case WM_USER:
1838         if (wParam) SetWindowLong32A( hwnd, 0, (HMENU)wParam );
1839         break;
1840     default:
1841         return DefWindowProc16(hwnd, message, wParam, lParam);
1842     }
1843     return 0;
1844 }
1845
1846
1847 /***********************************************************************
1848  *           MENU_GetMenuBarHeight
1849  *
1850  * Compute the size of the menu bar height. Used by NC_HandleNCCalcSize().
1851  */
1852 UINT MENU_GetMenuBarHeight( HWND hwnd, UINT menubarWidth, int orgX, int orgY )
1853 {
1854     HDC hdc;
1855     RECT16 rectBar;
1856     WND *wndPtr;
1857     LPPOPUPMENU lppop;
1858
1859     if (!(wndPtr = WIN_FindWndPtr( hwnd ))) return 0;
1860     if (!(lppop = (LPPOPUPMENU)USER_HEAP_LIN_ADDR((HMENU)wndPtr->wIDmenu)))
1861       return 0;
1862     hdc = GetDCEx( hwnd, 0, DCX_CACHE | DCX_WINDOW );
1863     SetRect16(&rectBar, orgX, orgY, orgX+menubarWidth, orgY+SYSMETRICS_CYMENU);
1864     MENU_MenuBarCalcSize( hdc, &rectBar, lppop, hwnd );
1865     ReleaseDC( hwnd, hdc );
1866     return lppop->Height;
1867 }
1868
1869
1870 /*******************************************************************
1871  *         ChangeMenu16    (USER.153)
1872  */
1873 BOOL16 ChangeMenu16( HMENU16 hMenu, UINT16 pos, SEGPTR data,
1874                      UINT16 id, UINT16 flags )
1875 {
1876     dprintf_menu( stddeb,"ChangeMenu16: menu=%04x pos=%d data=%08lx id=%04x flags=%04x\n",
1877                   hMenu, pos, (DWORD)data, id, flags );
1878     if (flags & MF_APPEND) return AppendMenu16( hMenu, flags & ~MF_APPEND,
1879                                                 id, data );
1880     /* FIXME: Word passes the item id in 'pos' and 0 or 0xffff as id */
1881     /* for MF_DELETE. We should check the parameters for all others */
1882     /* MF_* actions also (anybody got a doc on ChangeMenu?). */
1883     if (flags & MF_DELETE) return DeleteMenu( hMenu, pos, flags & ~MF_DELETE );
1884     if (flags & MF_CHANGE) return ModifyMenu16( hMenu, pos, flags & ~MF_CHANGE,
1885                                                 id, data );
1886     if (flags & MF_REMOVE) return RemoveMenu( hMenu,
1887                                               flags & MF_BYPOSITION ? pos : id,
1888                                               flags & ~MF_REMOVE );
1889     /* Default: MF_INSERT */
1890     return InsertMenu16( hMenu, pos, flags, id, data );
1891 }
1892
1893
1894 /*******************************************************************
1895  *         ChangeMenu32A    (USER32.22)
1896  */
1897 BOOL32 ChangeMenu32A( HMENU32 hMenu, UINT32 pos, LPCSTR data,
1898                       UINT32 id, UINT32 flags )
1899 {
1900     dprintf_menu( stddeb,"ChangeMenu32A: menu=%08x pos=%d data=%08lx id=%08x flags=%08x\n",
1901                   hMenu, pos, (DWORD)data, id, flags );
1902     if (flags & MF_APPEND) return AppendMenu32A( hMenu, flags & ~MF_APPEND,
1903                                                  id, data );
1904     if (flags & MF_DELETE) return DeleteMenu( hMenu, pos, flags & ~MF_DELETE );
1905     if (flags & MF_CHANGE) return ModifyMenu32A(hMenu, pos, flags & ~MF_CHANGE,
1906                                                 id, data );
1907     if (flags & MF_REMOVE) return RemoveMenu( hMenu,
1908                                               flags & MF_BYPOSITION ? pos : id,
1909                                               flags & ~MF_REMOVE );
1910     /* Default: MF_INSERT */
1911     return InsertMenu32A( hMenu, pos, flags, id, data );
1912 }
1913
1914
1915 /*******************************************************************
1916  *         ChangeMenu32W    (USER32.23)
1917  */
1918 BOOL32 ChangeMenu32W( HMENU32 hMenu, UINT32 pos, LPCWSTR data,
1919                       UINT32 id, UINT32 flags )
1920 {
1921     dprintf_menu( stddeb,"ChangeMenu32W: menu=%08x pos=%d data=%08lx id=%08x flags=%08x\n",
1922                   hMenu, pos, (DWORD)data, id, flags );
1923     if (flags & MF_APPEND) return AppendMenu32W( hMenu, flags & ~MF_APPEND,
1924                                                  id, data );
1925     if (flags & MF_DELETE) return DeleteMenu( hMenu, pos, flags & ~MF_DELETE );
1926     if (flags & MF_CHANGE) return ModifyMenu32W(hMenu, pos, flags & ~MF_CHANGE,
1927                                                 id, data );
1928     if (flags & MF_REMOVE) return RemoveMenu( hMenu,
1929                                               flags & MF_BYPOSITION ? pos : id,
1930                                               flags & ~MF_REMOVE );
1931     /* Default: MF_INSERT */
1932     return InsertMenu32W( hMenu, pos, flags, id, data );
1933 }
1934
1935
1936 /*******************************************************************
1937  *         CheckMenuItem    (USER.154)
1938  */
1939 INT CheckMenuItem( HMENU hMenu, UINT id, UINT flags )
1940 {
1941     MENUITEM *item;
1942     INT ret;
1943
1944     dprintf_menu( stddeb,"CheckMenuItem: %04x %04x %04x\n", hMenu, id, flags );
1945     if (!(item = MENU_FindItem( &hMenu, &id, flags ))) return -1;
1946     ret = item->item_flags & MF_CHECKED;
1947     if (flags & MF_CHECKED) item->item_flags |= MF_CHECKED;
1948     else item->item_flags &= ~MF_CHECKED;
1949     return ret;
1950 }
1951
1952
1953 /**********************************************************************
1954  *                      EnableMenuItem          [USER.155]
1955  */
1956 BOOL EnableMenuItem(HMENU hMenu, UINT wItemID, UINT wFlags)
1957 {
1958     MENUITEM *item;
1959     dprintf_menu(stddeb,"EnableMenuItem (%04x, %04X, %04X) !\n", 
1960                  hMenu, wItemID, wFlags);
1961     if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) return FALSE;
1962
1963       /* We can't have MF_GRAYED and MF_DISABLED together */
1964     if (wFlags & MF_GRAYED)
1965     {
1966         item->item_flags = (item->item_flags & ~MF_DISABLED) | MF_GRAYED;
1967     }
1968     else if (wFlags & MF_DISABLED)
1969     {
1970         item->item_flags = (item->item_flags & ~MF_GRAYED) | MF_DISABLED;
1971     }
1972     else   /* MF_ENABLED */
1973     {
1974         item->item_flags &= ~(MF_GRAYED | MF_DISABLED);
1975     }
1976     return TRUE;
1977 }
1978
1979
1980 /*******************************************************************
1981  *         GetMenuString    (USER.161)
1982  */
1983 int GetMenuString( HMENU hMenu, UINT wItemID,
1984                    LPSTR str, short nMaxSiz, UINT wFlags )
1985 {
1986     MENUITEM *item;
1987
1988     dprintf_menu( stddeb, "GetMenuString: menu=%04x item=%04x ptr=%p len=%d flags=%04x\n",
1989                  hMenu, wItemID, str, nMaxSiz, wFlags );
1990     if (!str || !nMaxSiz) return 0;
1991     str[0] = '\0';
1992     if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) return 0;
1993     if (!IS_STRING_ITEM(item->item_flags)) return 0;
1994     lstrcpyn32A( str, item->text, nMaxSiz );
1995     dprintf_menu( stddeb, "GetMenuString: returning '%s'\n", str );
1996     return strlen(str);
1997 }
1998
1999
2000 /**********************************************************************
2001  *                      HiliteMenuItem          [USER.162]
2002  */
2003 BOOL HiliteMenuItem(HWND hWnd, HMENU hMenu, UINT wItemID, UINT wHilite)
2004 {
2005     LPPOPUPMENU menu;
2006     dprintf_menu(stddeb,"HiliteMenuItem(%04x, %04x, %04x, %04x);\n", 
2007                  hWnd, hMenu, wItemID, wHilite);
2008     if (!MENU_FindItem( &hMenu, &wItemID, wHilite )) return FALSE;
2009     if (!(menu = (LPPOPUPMENU) USER_HEAP_LIN_ADDR(hMenu))) return FALSE;
2010     if (menu->FocusedItem == wItemID) return TRUE;
2011     MENU_HideSubPopups( hWnd, hMenu, FALSE );
2012     MENU_SelectItem( hWnd, hMenu, wItemID, TRUE );
2013     return TRUE;
2014 }
2015
2016
2017 /**********************************************************************
2018  *                      GetMenuState            [USER.250]
2019  */
2020 UINT GetMenuState(HMENU hMenu, UINT wItemID, UINT wFlags)
2021 {
2022     MENUITEM *item;
2023     dprintf_menu(stddeb,"GetMenuState(%04x, %04x, %04x);\n", 
2024                  hMenu, wItemID, wFlags);
2025     if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) return -1;
2026     if (item->item_flags & MF_POPUP)
2027     {
2028         POPUPMENU *menu = (POPUPMENU *) USER_HEAP_LIN_ADDR( (HMENU)item->item_id );
2029         if (!menu) return -1;
2030         else return (menu->nItems << 8) | (menu->wFlags & 0xff);
2031     }
2032     else return item->item_flags;
2033 }
2034
2035
2036 /**********************************************************************
2037  *                      GetMenuItemCount                [USER.263]
2038  */
2039 INT GetMenuItemCount(HMENU hMenu)
2040 {
2041         LPPOPUPMENU     menu;
2042         dprintf_menu(stddeb,"GetMenuItemCount(%04x);\n", hMenu);
2043         menu = (LPPOPUPMENU) USER_HEAP_LIN_ADDR(hMenu);
2044         if (menu == NULL) return (UINT)-1;
2045         dprintf_menu(stddeb,"GetMenuItemCount(%04x) return %d \n", 
2046                      hMenu, menu->nItems);
2047         return menu->nItems;
2048 }
2049
2050
2051 /**********************************************************************
2052  *                      GetMenuItemID                   [USER.264]
2053  */
2054 UINT GetMenuItemID(HMENU hMenu, int nPos)
2055 {
2056     LPPOPUPMENU menu;
2057
2058     dprintf_menu(stddeb,"GetMenuItemID(%04x, %d);\n", hMenu, nPos);
2059     if (!(menu = (LPPOPUPMENU) USER_HEAP_LIN_ADDR(hMenu))) return -1;
2060     if ((nPos < 0) || (nPos >= menu->nItems)) return -1;
2061     if (menu->items[nPos].item_flags & MF_POPUP) return -1;
2062     return menu->items[nPos].item_id;
2063 }
2064
2065
2066 /*******************************************************************
2067  *         InsertMenu16    (USER.410)
2068  */
2069 BOOL16 InsertMenu16( HMENU16 hMenu, UINT16 pos, UINT16 flags,
2070                      UINT16 id, SEGPTR data )
2071 {
2072     if (IS_STRING_ITEM(flags) && data)
2073         return InsertMenu32A( hMenu, (INT32)(INT16)pos, flags, id,
2074                               (LPSTR)PTR_SEG_TO_LIN(data) );
2075     return InsertMenu32A( hMenu, (INT32)(INT16)pos, flags, id, (LPSTR)data );
2076 }
2077
2078
2079 /*******************************************************************
2080  *         InsertMenu32A    (USER32.321)
2081  */
2082 BOOL32 InsertMenu32A( HMENU32 hMenu, UINT32 pos, UINT32 flags,
2083                       UINT32 id, LPCSTR str )
2084 {
2085     MENUITEM *item;
2086
2087     if (IS_STRING_ITEM(flags) && str)
2088         dprintf_menu( stddeb, "InsertMenu: %04x %d %04x %04x '%s'\n",
2089                       hMenu, pos, flags, id, str );
2090     else dprintf_menu( stddeb, "InsertMenu: %04x %d %04x %04x %08lx\n",
2091                        hMenu, pos, flags, id, (DWORD)str );
2092
2093     if (!(item = MENU_InsertItem( hMenu, pos, flags ))) return FALSE;
2094
2095     if (!(MENU_SetItemData( item, flags, id, str )))
2096     {
2097         RemoveMenu( hMenu, pos, flags );
2098         return FALSE;
2099     }
2100
2101     if (flags & MF_POPUP)  /* Set the MF_POPUP flag on the popup-menu */
2102         ((POPUPMENU *)USER_HEAP_LIN_ADDR((HMENU)id))->wFlags |= MF_POPUP;
2103
2104     item->hCheckBit   = hStdCheck;
2105     item->hUnCheckBit = 0;
2106     return TRUE;
2107 }
2108
2109
2110 /*******************************************************************
2111  *         InsertMenu32W    (USER32.324)
2112  */
2113 BOOL32 InsertMenu32W( HMENU32 hMenu, UINT32 pos, UINT32 flags,
2114                       UINT32 id, LPCWSTR str )
2115 {
2116     BOOL32 ret;
2117
2118     if (IS_STRING_ITEM(flags) && str)
2119     {
2120         LPSTR newstr = STRING32_DupUniToAnsi( str );
2121         ret = InsertMenu32A( hMenu, pos, flags, id, newstr );
2122         free( newstr );
2123         return ret;
2124     }
2125     else return InsertMenu32A( hMenu, pos, flags, id, (LPCSTR)str );
2126 }
2127
2128
2129 /*******************************************************************
2130  *         AppendMenu16    (USER.411)
2131  */
2132 BOOL16 AppendMenu16( HMENU16 hMenu, UINT16 flags, UINT16 id, SEGPTR data )
2133 {
2134     return InsertMenu16( hMenu, -1, flags | MF_BYPOSITION, id, data );
2135 }
2136
2137
2138 /*******************************************************************
2139  *         AppendMenu32A    (USER32.4)
2140  */
2141 BOOL32 AppendMenu32A( HMENU32 hMenu, UINT32 flags, UINT32 id, LPCSTR data )
2142 {
2143     return InsertMenu32A( hMenu, -1, flags | MF_BYPOSITION, id, data );
2144 }
2145
2146
2147 /*******************************************************************
2148  *         AppendMenu32W    (USER32.5)
2149  */
2150 BOOL32 AppendMenu32W( HMENU32 hMenu, UINT32 flags, UINT32 id, LPCWSTR data )
2151 {
2152     return InsertMenu32W( hMenu, -1, flags | MF_BYPOSITION, id, data );
2153 }
2154
2155
2156 /**********************************************************************
2157  *                      RemoveMenu              [USER.412]
2158  */
2159 BOOL RemoveMenu(HMENU hMenu, UINT nPos, UINT wFlags)
2160 {
2161     LPPOPUPMENU menu;
2162     MENUITEM *item;
2163
2164     dprintf_menu(stddeb,"RemoveMenu (%04x, %04x, %04x)\n",hMenu, nPos, wFlags);
2165     if (!(item = MENU_FindItem( &hMenu, &nPos, wFlags ))) return FALSE;
2166     if (!(menu = (LPPOPUPMENU) USER_HEAP_LIN_ADDR(hMenu))) return FALSE;
2167     
2168       /* Remove item */
2169
2170     if (IS_STRING_ITEM(item->item_flags) && item->text)
2171         HeapFree( SystemHeap, 0, item->text );
2172     if (--menu->nItems == 0)
2173     {
2174         HeapFree( SystemHeap, 0, menu->items );
2175         menu->items = NULL;
2176     }
2177     else
2178     {
2179         while(nPos < menu->nItems)
2180         {
2181             *item = *(item+1);
2182             item++;
2183             nPos++;
2184         }
2185         menu->items = HeapReAlloc( SystemHeap, 0, menu->items,
2186                                    menu->nItems * sizeof(MENUITEM) );
2187     }
2188     return TRUE;
2189 }
2190
2191
2192 /**********************************************************************
2193  *                      DeleteMenu              [USER.413]
2194  */
2195 BOOL DeleteMenu(HMENU hMenu, UINT nPos, UINT wFlags)
2196 {
2197     MENUITEM *item = MENU_FindItem( &hMenu, &nPos, wFlags );
2198     if (!item) return FALSE;
2199     if (item->item_flags & MF_POPUP) DestroyMenu( (HMENU)item->item_id );
2200       /* nPos is now the position of the item */
2201     RemoveMenu( hMenu, nPos, wFlags | MF_BYPOSITION );
2202     return TRUE;
2203 }
2204
2205
2206 /*******************************************************************
2207  *         ModifyMenu16    (USER.414)
2208  */
2209 BOOL16 ModifyMenu16( HMENU16 hMenu, UINT16 pos, UINT16 flags,
2210                      UINT16 id, SEGPTR data )
2211 {
2212     if (IS_STRING_ITEM(flags))
2213         return ModifyMenu32A( hMenu, (INT32)(INT16)pos, flags, id,
2214                               (LPSTR)PTR_SEG_TO_LIN(data) );
2215     return ModifyMenu32A( hMenu, (INT32)(INT16)pos, flags, id, (LPSTR)data );
2216 }
2217
2218
2219 /*******************************************************************
2220  *         ModifyMenu32A    (USER32.396)
2221  */
2222 BOOL32 ModifyMenu32A( HMENU32 hMenu, UINT32 pos, UINT32 flags,
2223                       UINT32 id, LPCSTR str )
2224 {
2225     MENUITEM *item;
2226     HMENU hMenu16 = hMenu;
2227     UINT16 pos16 = pos;
2228
2229     if (IS_STRING_ITEM(flags))
2230     {
2231         dprintf_menu( stddeb, "ModifyMenu: %04x %d %04x %04x '%s'\n",
2232                       hMenu, pos, flags, id, str ? str : "#NULL#" );
2233         if (!str) return FALSE;
2234     }
2235     else
2236     {
2237         dprintf_menu( stddeb, "ModifyMenu: %04x %d %04x %04x %08lx\n",
2238                       hMenu, pos, flags, id, (DWORD)str );
2239     }
2240
2241     if (!(item = MENU_FindItem( &hMenu16, &pos16, flags ))) return FALSE;
2242     return MENU_SetItemData( item, flags, id, str );
2243 }
2244
2245
2246 /*******************************************************************
2247  *         ModifyMenu32W    (USER32.397)
2248  */
2249 BOOL32 ModifyMenu32W( HMENU32 hMenu, UINT32 pos, UINT32 flags,
2250                       UINT32 id, LPCWSTR str )
2251 {
2252     BOOL32 ret;
2253
2254     if (IS_STRING_ITEM(flags) && str)
2255     {
2256         LPSTR newstr = STRING32_DupUniToAnsi( str );
2257         ret = ModifyMenu32A( hMenu, pos, flags, id, newstr );
2258         free( newstr );
2259         return ret;
2260     }
2261     else return ModifyMenu32A( hMenu, pos, flags, id, (LPCSTR)str );
2262 }
2263
2264
2265 /**********************************************************************
2266  *                      CreatePopupMenu         [USER.415]
2267  */
2268 HMENU CreatePopupMenu()
2269 {
2270     HMENU hmenu;
2271     POPUPMENU *menu;
2272
2273     if (!(hmenu = CreateMenu())) return 0;
2274     menu = (POPUPMENU *) USER_HEAP_LIN_ADDR( hmenu );
2275     menu->wFlags |= MF_POPUP;
2276     return hmenu;
2277 }
2278
2279
2280 /**********************************************************************
2281  *                      GetMenuCheckMarkDimensions      [USER.417]
2282  */
2283 DWORD GetMenuCheckMarkDimensions()
2284 {
2285     return MAKELONG( check_bitmap_width, check_bitmap_height );
2286 }
2287
2288
2289 /**********************************************************************
2290  *                      SetMenuItemBitmaps      [USER.418]
2291  */
2292 BOOL SetMenuItemBitmaps(HMENU hMenu, UINT nPos, UINT wFlags,
2293                         HBITMAP hNewUnCheck, HBITMAP hNewCheck)
2294 {
2295     MENUITEM *item;
2296     dprintf_menu(stddeb,"SetMenuItemBitmaps(%04x, %04x, %04x, %04x, %04x)\n",
2297                  hMenu, nPos, wFlags, hNewCheck, hNewUnCheck);
2298     if (!(item = MENU_FindItem( &hMenu, &nPos, wFlags ))) return FALSE;
2299
2300     if (!hNewCheck && !hNewUnCheck)
2301     {
2302           /* If both are NULL, restore default bitmaps */
2303         item->hCheckBit   = hStdCheck;
2304         item->hUnCheckBit = 0;
2305         item->item_flags &= ~MF_USECHECKBITMAPS;
2306     }
2307     else  /* Install new bitmaps */
2308     {
2309         item->hCheckBit   = hNewCheck;
2310         item->hUnCheckBit = hNewUnCheck;
2311         item->item_flags |= MF_USECHECKBITMAPS;
2312     }
2313     return TRUE;
2314 }
2315
2316
2317 /**********************************************************************
2318  *                      CreateMenu              [USER.151]
2319  */
2320 HMENU CreateMenu()
2321 {
2322     HMENU hMenu;
2323     LPPOPUPMENU menu;
2324     dprintf_menu(stddeb,"CreateMenu !\n");
2325     if (!(hMenu = USER_HEAP_ALLOC( sizeof(POPUPMENU) )))
2326         return 0;
2327     menu = (LPPOPUPMENU) USER_HEAP_LIN_ADDR(hMenu);
2328     menu->wFlags = 0;
2329     menu->wMagic = MENU_MAGIC;
2330     menu->hTaskQ = 0;
2331     menu->Width  = 0;
2332     menu->Height = 0;
2333     menu->nItems = 0;
2334     menu->hWnd   = 0;
2335     menu->items  = NULL;
2336     menu->FocusedItem = NO_SELECTED_ITEM;
2337     dprintf_menu(stddeb,"CreateMenu // return %04x\n", hMenu);
2338     return hMenu;
2339 }
2340
2341
2342 /**********************************************************************
2343  *                      DestroyMenu             [USER.152]
2344  */
2345 BOOL DestroyMenu(HMENU hMenu)
2346 {
2347     LPPOPUPMENU lppop;
2348     dprintf_menu(stddeb,"DestroyMenu (%04x) !\n", hMenu);
2349
2350     if (hMenu == 0) return FALSE;
2351     /* Silently ignore attempts to destroy default system menu */
2352     if (hMenu == MENU_DefSysMenu) return TRUE;
2353     lppop = (LPPOPUPMENU) USER_HEAP_LIN_ADDR(hMenu);
2354     if (!lppop || (lppop->wMagic != MENU_MAGIC)) return FALSE;
2355     lppop->wMagic = 0;  /* Mark it as destroyed */
2356     if ((lppop->wFlags & MF_POPUP) && lppop->hWnd && (!pTopPWnd || (lppop->hWnd != pTopPWnd->hwndSelf)))
2357         DestroyWindow( lppop->hWnd );
2358
2359     if (lppop->items)
2360     {
2361         int i;
2362         MENUITEM *item = lppop->items;
2363         for (i = lppop->nItems; i > 0; i--, item++)
2364         {
2365             if (item->item_flags & MF_POPUP)
2366                 DestroyMenu( (HMENU)item->item_id );
2367             if (IS_STRING_ITEM(item->item_flags) && item->text)
2368                 HeapFree( SystemHeap, 0, item->text );
2369         }
2370         HeapFree( SystemHeap, 0, lppop->items );
2371     }
2372     USER_HEAP_FREE( hMenu );
2373     dprintf_menu(stddeb,"DestroyMenu (%04x) // End !\n", hMenu);
2374     return TRUE;
2375 }
2376
2377 /**********************************************************************
2378  *                      GetSystemMenu           [USER.156]
2379  */
2380 HMENU GetSystemMenu(HWND hWnd, BOOL bRevert)
2381 {
2382     WND *wndPtr = WIN_FindWndPtr( hWnd );
2383     if (!wndPtr) return 0;
2384
2385     if (!wndPtr->hSysMenu || (wndPtr->hSysMenu == MENU_DefSysMenu))
2386     {
2387         wndPtr->hSysMenu = MENU_CopySysMenu();
2388         return wndPtr->hSysMenu;
2389     }
2390     if (!bRevert) return wndPtr->hSysMenu;
2391     if (wndPtr->hSysMenu) DestroyMenu(wndPtr->hSysMenu);
2392     wndPtr->hSysMenu = MENU_CopySysMenu();
2393     return wndPtr->hSysMenu;
2394 }
2395
2396
2397 /*******************************************************************
2398  *         SetSystemMenu    (USER.280)
2399  */
2400 BOOL SetSystemMenu( HWND hwnd, HMENU hMenu )
2401 {
2402     WND *wndPtr;
2403
2404     if (!(wndPtr = WIN_FindWndPtr(hwnd))) return FALSE;
2405     if (wndPtr->hSysMenu && (wndPtr->hSysMenu != MENU_DefSysMenu))
2406         DestroyMenu( wndPtr->hSysMenu );
2407     wndPtr->hSysMenu = hMenu;
2408     return TRUE;
2409 }
2410
2411
2412 /**********************************************************************
2413  *                      GetMenu         [USER.157]
2414  */
2415 HMENU GetMenu(HWND hWnd) 
2416
2417         WND * wndPtr = WIN_FindWndPtr(hWnd);
2418         if (wndPtr == NULL) return 0;
2419         return (HMENU)wndPtr->wIDmenu;
2420 }
2421
2422
2423 /**********************************************************************
2424  *                      SetMenu         [USER.158]
2425  */
2426 BOOL SetMenu(HWND hWnd, HMENU hMenu)
2427 {
2428         LPPOPUPMENU lpmenu;
2429         WND * wndPtr = WIN_FindWndPtr(hWnd);
2430         if (wndPtr == NULL) {
2431                 fprintf(stderr,"SetMenu(%04x, %04x) // Bad window handle !\n",
2432                         hWnd, hMenu);
2433                 return FALSE;
2434                 }
2435         dprintf_menu(stddeb,"SetMenu(%04x, %04x);\n", hWnd, hMenu);
2436         if (GetCapture() == hWnd) ReleaseCapture();
2437         wndPtr->wIDmenu = (UINT)hMenu;
2438         if (hMenu != 0)
2439         {
2440             lpmenu = (LPPOPUPMENU) USER_HEAP_LIN_ADDR(hMenu);
2441             if (lpmenu == NULL) {
2442                 fprintf(stderr,"SetMenu(%04x, %04x) // Bad menu handle !\n", 
2443                         hWnd, hMenu);
2444                 return FALSE;
2445                 }
2446             lpmenu->hWnd = hWnd;
2447             lpmenu->wFlags &= ~MF_POPUP;  /* Can't be a popup */
2448             lpmenu->Height = 0;  /* Make sure we recalculate the size */
2449         }
2450         if (IsWindowVisible(hWnd))
2451             SetWindowPos( hWnd, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE |
2452                           SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED );
2453         return TRUE;
2454 }
2455
2456
2457
2458 /**********************************************************************
2459  *                      GetSubMenu              [USER.159]
2460  */
2461 HMENU GetSubMenu(HMENU hMenu, short nPos)
2462 {
2463     LPPOPUPMENU lppop;
2464
2465     dprintf_menu(stddeb,"GetSubMenu (%04x, %04X) !\n", hMenu, nPos);
2466     if (!(lppop = (LPPOPUPMENU) USER_HEAP_LIN_ADDR(hMenu))) return 0;
2467     if ((UINT)nPos >= lppop->nItems) return 0;
2468     if (!(lppop->items[nPos].item_flags & MF_POPUP)) return 0;
2469     return (HMENU)lppop->items[nPos].item_id;
2470 }
2471
2472
2473 /**********************************************************************
2474  *                      DrawMenuBar             [USER.160]
2475  */
2476 void DrawMenuBar(HWND hWnd)
2477 {
2478         WND             *wndPtr;
2479         LPPOPUPMENU lppop;
2480         dprintf_menu(stddeb,"DrawMenuBar (%04x)\n", hWnd);
2481         wndPtr = WIN_FindWndPtr(hWnd);
2482         if (wndPtr != NULL && (wndPtr->dwStyle & WS_CHILD) == 0 && 
2483                 wndPtr->wIDmenu != 0) {
2484                 dprintf_menu(stddeb,"DrawMenuBar wIDmenu=%04X \n", 
2485                              wndPtr->wIDmenu);
2486                 lppop = (LPPOPUPMENU) USER_HEAP_LIN_ADDR((HMENU)wndPtr->wIDmenu);
2487                 if (lppop == NULL) return;
2488
2489                 lppop->Height = 0; /* Make sure we call MENU_MenuBarCalcSize */
2490                 SetWindowPos( hWnd, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE |
2491                             SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED );
2492             }
2493 }
2494
2495
2496 /***********************************************************************
2497  *           EndMenu   (USER.187)
2498  */
2499 void EndMenu(void)
2500 {
2501     /* FIXME: this won't work when we have multiple tasks... */
2502     fEndMenuCalled = TRUE;
2503 }
2504
2505
2506 /***********************************************************************
2507  *           LookupMenuHandle   (USER.217)
2508  */
2509 HMENU LookupMenuHandle( HMENU hmenu, INT id )
2510 {
2511     if (!MENU_FindItem( &hmenu, &id, MF_BYCOMMAND )) return 0;
2512     else return hmenu;
2513 }
2514
2515
2516 /**********************************************************************
2517  *          LoadMenu    (USER.150)
2518  */
2519 HMENU16 LoadMenu16( HINSTANCE16 instance, SEGPTR name )
2520 {
2521     HRSRC16 hRsrc;
2522     HGLOBAL16 handle;
2523     HMENU16 hMenu;
2524
2525     if (HIWORD(name))
2526     {
2527         char *str = (char *)PTR_SEG_TO_LIN( name );
2528         dprintf_menu( stddeb, "LoadMenu(%04x,'%s')\n", instance, str );
2529         if (str[0] == '#') name = (SEGPTR)atoi( str + 1 );
2530     }
2531     else
2532         dprintf_resource(stddeb,"LoadMenu(%04x,%04x)\n",instance,LOWORD(name));
2533
2534     if (!name) return 0;
2535     
2536     /* check for Win32 module */
2537     instance = GetExePtr( instance );
2538     if (MODULE_GetPtr(instance)->flags & NE_FFLAGS_WIN32)
2539         return LoadMenu32A(instance,PTR_SEG_TO_LIN(name));
2540
2541     if (!(hRsrc = FindResource16( instance, name, RT_MENU ))) return 0;
2542     if (!(handle = LoadResource16( instance, hRsrc ))) return 0;
2543     hMenu = LoadMenuIndirect16(LockResource16(handle));
2544     FreeResource16( handle );
2545     return hMenu;
2546 }
2547
2548
2549 /*****************************************************************
2550  *        LoadMenu32A   (USER32.370)
2551  */
2552 HMENU32 LoadMenu32A( HINSTANCE32 instance, LPCSTR name )
2553 {
2554     HRSRC32 hrsrc = FindResource32A( instance, name, (LPSTR)RT_MENU );
2555     if (!hrsrc) return 0;
2556     return LoadMenuIndirect32A( (LPCVOID)LoadResource32( instance, hrsrc ));
2557 }
2558
2559
2560 /*****************************************************************
2561  *        LoadMenu32W   (USER32.372)
2562  */
2563 HMENU32 LoadMenu32W( HINSTANCE32 instance, LPCWSTR name )
2564 {
2565     HRSRC32 hrsrc = FindResource32W( instance, name, (LPWSTR)RT_MENU );
2566     if (!hrsrc) return 0;
2567     return LoadMenuIndirect32W( (LPCVOID)LoadResource32( instance, hrsrc ));
2568 }
2569
2570
2571 /**********************************************************************
2572  *          LoadMenuIndirect16    (USER.220)
2573  */
2574 HMENU16 LoadMenuIndirect16( LPCVOID template )
2575 {
2576     HMENU hMenu;
2577     WORD version, offset;
2578     LPCSTR p = (LPCSTR)template;
2579
2580     dprintf_menu(stddeb,"LoadMenuIndirect32A: %p\n", template );
2581     version = GET_WORD(p);
2582     p += sizeof(WORD);
2583     if (version)
2584     {
2585         fprintf( stderr, "LoadMenuIndirect16: version must be 0 for Win16\n" );
2586         return 0;
2587     }
2588     offset = GET_WORD(p);
2589     p += sizeof(WORD) + offset;
2590     if (!(hMenu = CreateMenu())) return 0;
2591     if (!MENU_ParseResource( p, hMenu, FALSE ))
2592     {
2593         DestroyMenu( hMenu );
2594         return 0;
2595     }
2596     return hMenu;
2597 }
2598
2599
2600 /**********************************************************************
2601  *          LoadMenuIndirect32A    (USER32.370)
2602  */
2603 HMENU32 LoadMenuIndirect32A( LPCVOID template )
2604 {
2605     HMENU hMenu;
2606     WORD version, offset;
2607     LPCSTR p = (LPCSTR)template;
2608
2609     dprintf_menu(stddeb,"LoadMenuIndirect32A: %p\n", template );
2610     version = GET_WORD(p);
2611     p += sizeof(WORD);
2612     if (version)
2613     {
2614         fprintf( stderr, "LoadMenuIndirect32A: version %d not supported.\n",
2615                  version );
2616         return 0;
2617     }
2618     offset = GET_WORD(p);
2619     p += sizeof(WORD) + offset;
2620     if (!(hMenu = CreateMenu())) return 0;
2621     if (!MENU_ParseResource( p, hMenu, TRUE ))
2622     {
2623         DestroyMenu( hMenu );
2624         return 0;
2625     }
2626     return hMenu;
2627 }
2628
2629
2630 /**********************************************************************
2631  *          LoadMenuIndirect32W    (USER32.371)
2632  */
2633 HMENU32 LoadMenuIndirect32W( LPCVOID template )
2634 {
2635     /* FIXME: is there anything different between A and W? */
2636     return LoadMenuIndirect32A( template );
2637 }
2638
2639
2640 /**********************************************************************
2641  *              IsMenu    (USER.358)
2642  */
2643 BOOL IsMenu( HMENU hmenu )
2644 {
2645     LPPOPUPMENU menu;
2646     if (!(menu = (LPPOPUPMENU) USER_HEAP_LIN_ADDR( hmenu ))) return FALSE;
2647     return (menu->wMagic == MENU_MAGIC);
2648 }