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