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