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