Release 960114
[wine.git] / controls / menu.c
blobe820d181913d9122b5d77d4dbc4b740f8835a2f5
1 /*
2 * Menu functions
4 * Copyright 1993 Martin Ayotte
5 * Copyright 1994 Alexandre Julliard
6 */
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.
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"
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;
34 /* Flag set by EndMenu() to force an exit from menu tracking */
35 static BOOL fEndMenuCalled = FALSE;
37 /* Space between 2 menu bar items */
38 #define MENU_BAR_ITEMS_SPACE 16
40 /* Minimum width of a tab character */
41 #define MENU_TAB_SPACE 8
43 /* Height of a separator item */
44 #define SEPARATOR_HEIGHT 5
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 */
51 #define IS_STRING_ITEM(flags) (!((flags) & (MF_BITMAP | MF_OWNERDRAW | \
52 MF_MENUBARBREAK | MF_MENUBREAK | MF_SEPARATOR)))
55 extern void NC_DrawSysButton(HWND hwnd, HDC hdc, BOOL down); /* nonclient.c */
57 static HBITMAP hStdCheck = 0;
58 static HBITMAP hStdMnArrow = 0;
60 WORD * ParseMenuResource(WORD *first_item, int level, HMENU hMenu);
63 /***********************************************************************
64 * MENU_Init
66 * Menus initialisation.
68 BOOL MENU_Init()
70 BITMAP bm;
72 /* Load bitmaps */
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;
85 return TRUE;
89 /***********************************************************************
90 * MENU_HasSysMenu
92 * Check whether the window owning the menu bar has a system menu.
94 static BOOL MENU_HasSysMenu( POPUPMENU *menu )
96 WND *wndPtr;
98 if (menu->wFlags & MF_POPUP) return FALSE;
99 if (!(wndPtr = WIN_FindWndPtr( menu->hWnd ))) return FALSE;
100 return (wndPtr->dwStyle & WS_SYSMENU) != 0;
104 /**********************************************************************
105 * MENU_CopySysMenu
107 static HMENU MENU_CopySysMenu(void)
109 HMENU hMenu;
110 HGLOBAL handle;
111 POPUPMENU *menu;
113 if (!(handle = SYSRES_LoadResource( SYSRES_MENU_SYSMENU ))) return 0;
114 hMenu = LoadMenuIndirect( GlobalLock( handle ) );
115 SYSRES_FreeResource( handle );
116 if (!hMenu)
118 dprintf_menu(stddeb,"No SYSMENU\n");
119 return 0;
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;
128 /***********************************************************************
129 * MENU_IsInSysMenu
131 * Check whether the point (in screen coords) is in the system menu
132 * of the window owning the given menu.
134 static BOOL MENU_IsInSysMenu( POPUPMENU *menu, POINT pt )
136 WND *wndPtr;
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;
151 /***********************************************************************
152 * MENU_FindItem
154 * Find a menu item. Return a pointer on the item, and modifies *hmenu
155 * in case the item was in a sub-menu.
157 static MENUITEM *MENU_FindItem( HMENU *hmenu, UINT *nPos, UINT wFlags )
159 POPUPMENU *menu;
160 MENUITEM *item;
161 int i;
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)
167 if (*nPos >= menu->nItems) return NULL;
168 return &item[*nPos];
170 else
172 for (i = 0; i < menu->nItems; i++, item++)
174 if (item->item_id == *nPos)
176 *nPos = i;
177 return item;
179 else if (item->item_flags & MF_POPUP)
181 HMENU hsubmenu = (HMENU)item->item_id;
182 MENUITEM *subitem = MENU_FindItem( &hsubmenu, nPos, wFlags );
183 if (subitem)
185 *hmenu = hsubmenu;
186 return subitem;
191 return NULL;
195 /***********************************************************************
196 * MENU_FindItemByCoords
198 * Find the item at the specified coordinates (screen coords).
200 static MENUITEM *MENU_FindItemByCoords( POPUPMENU *menu, int x, int y, UINT *pos )
202 MENUITEM *item;
203 WND *wndPtr;
204 int i;
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++)
212 if ((x >= item->rect.left) && (x < item->rect.right) &&
213 (y >= item->rect.top) && (y < item->rect.bottom))
215 if (pos) *pos = i;
216 return item;
219 return NULL;
223 /***********************************************************************
224 * MENU_FindItemByKey
226 * Find the menu item selected by a key press.
227 * Return item id, -1 if none, -2 if we should close the menu.
229 static UINT MENU_FindItemByKey( HWND hwndOwner, HMENU hmenu, UINT key )
231 POPUPMENU *menu;
232 LPMENUITEM lpitem;
233 int i;
234 LONG menuchar;
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++)
241 if (IS_STRING_ITEM(lpitem->item_flags))
243 char *p = strchr( lpitem->item_text, '&' );
244 if (p && (p[1] != '&') && (toupper(p[1]) == key)) return i;
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;
260 /***********************************************************************
261 * MENU_CalcItemSize
263 * Calculate the size of the menu item and store it in lpitem->rect.
265 static void MENU_CalcItemSize( HDC hdc, LPMENUITEM lpitem, HWND hwndOwner,
266 int orgX, int orgY, BOOL menuBar )
268 DWORD dwSize;
269 char *p;
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);
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;
295 if (lpitem->item_flags & MF_SEPARATOR)
297 lpitem->rect.bottom += SEPARATOR_HEIGHT;
298 return;
301 if (!menuBar)
303 lpitem->rect.right += 2 * check_bitmap_width;
304 if (lpitem->item_flags & MF_POPUP)
305 lpitem->rect.right += arrow_bitmap_width;
308 if (lpitem->item_flags & MF_BITMAP)
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;
317 /* If we get here, then it is a text item */
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 );
323 if (menuBar) lpitem->rect.right += MENU_BAR_ITEMS_SPACE;
324 else if ( ( lpitem->item_text != NULL ) && (p = strchr( lpitem->item_text, '\t' )) != NULL)
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;
332 else
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;
342 /***********************************************************************
343 * MENU_PopupMenuCalcSize
345 * Calculate the size of a popup menu.
347 static void MENU_PopupMenuCalcSize( LPPOPUPMENU lppop, HWND hwndOwner )
349 LPMENUITEM items, lpitem;
350 HDC hdc;
351 int start, i;
352 int orgX, orgY, maxX, maxTab, maxTabWidth;
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)
361 lpitem = &items[start];
362 orgX = maxX;
363 orgY = 0;
364 maxTab = maxTabWidth = 0;
366 /* Parse items until column break or end of menu */
367 for (i = start; i < lppop->nItems; i++, lpitem++)
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)
377 maxTab = MAX( maxTab, lpitem->xTab );
378 maxTabWidth = MAX(maxTabWidth,lpitem->rect.right-lpitem->xTab);
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++)
386 lpitem->rect.right = maxX;
387 if (lpitem->xTab) lpitem->xTab = maxTab;
389 lppop->Height = MAX( lppop->Height, orgY );
392 lppop->Width = maxX;
393 ReleaseDC( 0, hdc );
397 /***********************************************************************
398 * MENU_MenuBarCalcSize
400 * Calculate the size of the menu bar.
402 static void MENU_MenuBarCalcSize( HDC hdc, LPRECT lprect, LPPOPUPMENU lppop,
403 HWND hwndOwner )
405 LPMENUITEM lpitem, items;
406 int start, i, orgX, orgY, maxY, helpPos;
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)
420 lpitem = &items[start];
421 orgX = lprect->left;
422 orgY = maxY;
424 /* Parse items until line break or end of menu */
425 for (i = start; i < lppop->nItems; i++, lpitem++)
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)
433 if (i != start) break;
434 else lpitem->rect.right = lprect->right;
436 maxY = MAX( maxY, lpitem->rect.bottom );
437 orgX = lpitem->rect.right;
440 /* Finish the line (set all items to the largest height found) */
441 while (start < i) items[start++].rect.bottom = maxY;
444 lprect->bottom = maxY;
445 lppop->Height = lprect->bottom - lprect->top;
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)
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--)
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;
466 /***********************************************************************
467 * MENU_DrawMenuItem
469 * Draw a single menu item.
471 static void MENU_DrawMenuItem( HWND hwnd, HDC hdc, LPMENUITEM lpitem,
472 UINT height, BOOL menuBar )
474 RECT rect;
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);
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;
500 if (menuBar && (lpitem->item_flags & MF_SEPARATOR)) return;
501 rect = lpitem->rect;
503 /* Draw the background */
505 if (lpitem->item_flags & MF_HILITE)
506 FillRect( hdc, &rect, sysColorObjects.hbrushHighlight );
507 else FillRect( hdc, &rect, sysColorObjects.hbrushMenu );
508 SetBkMode( hdc, TRANSPARENT );
510 /* Draw the separator bar (if any) */
512 if (!menuBar && (lpitem->item_flags & MF_MENUBARBREAK))
514 SelectObject( hdc, sysColorObjects.hpenWindowFrame );
515 MoveTo( hdc, rect.left, 0 );
516 LineTo( hdc, rect.left, height );
518 if (lpitem->item_flags & MF_SEPARATOR)
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;
526 /* Setup colors */
528 if (lpitem->item_flags & MF_HILITE)
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 ) );
536 else
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 ) );
545 if (!menuBar)
547 /* Draw the check mark */
549 if (lpitem->item_flags & MF_CHECKED)
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 );
556 else if (lpitem->hUnCheckBit != 0) /* Not checked */
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 );
563 /* Draw the popup-menu arrow */
565 if (lpitem->item_flags & MF_POPUP)
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 );
573 rect.left += check_bitmap_width;
574 rect.right -= arrow_bitmap_width;
577 /* Draw the item text or bitmap */
579 if (lpitem->item_flags & MF_BITMAP)
581 GRAPH_DrawBitmap( hdc, (HBITMAP)lpitem->hText, rect.left, rect.top,
582 0, 0, rect.right-rect.left, rect.bottom-rect.top );
583 return;
585 /* No bitmap - process text if present */
586 else if ((lpitem->item_text) != ((char *) NULL))
588 register int i;
590 if (menuBar)
592 rect.left += MENU_BAR_ITEMS_SPACE / 2;
593 rect.right -= MENU_BAR_ITEMS_SPACE / 2;
594 i = strlen( lpitem->item_text );
596 else
598 for (i = 0; lpitem->item_text[i]; i++)
599 if ((lpitem->item_text[i] == '\t') ||
600 (lpitem->item_text[i] == '\b')) break;
603 DrawText( hdc, lpitem->item_text, i, &rect,
604 DT_LEFT | DT_VCENTER | DT_SINGLELINE );
606 if (lpitem->item_text[i]) /* There's a tab or flush-right char */
608 if (lpitem->item_text[i] == '\t')
610 rect.left = lpitem->xTab;
611 DrawText( hdc, lpitem->item_text + i + 1, -1, &rect,
612 DT_LEFT | DT_VCENTER | DT_SINGLELINE );
614 else DrawText( hdc, lpitem->item_text + i + 1, -1, &rect,
615 DT_RIGHT | DT_VCENTER | DT_SINGLELINE );
621 /***********************************************************************
622 * MENU_DrawPopupMenu
624 * Paint a popup menu.
626 static void MENU_DrawPopupMenu( HWND hwnd, HDC hdc, HMENU hmenu )
628 POPUPMENU *menu;
629 MENUITEM *item;
630 RECT rect;
631 int i;
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 );
643 /***********************************************************************
644 * MENU_DrawMenuBar
646 * Paint a menu bar. Returns the height of the menu bar.
648 UINT MENU_DrawMenuBar(HDC hDC, LPRECT lprect, HWND hwnd, BOOL suppress_draw)
650 LPPOPUPMENU lppop;
651 LPMENUITEM lpitem;
652 int i;
653 WND *wndPtr = WIN_FindWndPtr( hwnd );
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;
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 );
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++)
672 MENU_DrawMenuItem( hwnd, hDC, lpitem, lppop->Height, TRUE );
674 return lppop->Height;
678 /***********************************************************************
679 * MENU_ShowPopup
681 * Display a popup menu.
683 static BOOL MENU_ShowPopup(HWND hwndOwner, HMENU hmenu, UINT id, int x, int y)
685 POPUPMENU *menu;
687 if (!(menu = (POPUPMENU *) USER_HEAP_LIN_ADDR( hmenu ))) return FALSE;
688 if (menu->FocusedItem != NO_SELECTED_ITEM)
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;
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)
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;
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 );
713 /* Display the window */
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;
722 /***********************************************************************
723 * MENU_SelectItem
725 static void MENU_SelectItem( HWND hwndOwner, HMENU hmenu, UINT wIndex )
727 MENUITEM *items;
728 LPPOPUPMENU lppop;
729 HDC hdc;
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);
742 /* Clear previous highlighted item */
743 if (lppop->FocusedItem != NO_SELECTED_ITEM)
745 if (lppop->FocusedItem == SYSMENU_SELECTED)
746 NC_DrawSysButton( lppop->hWnd, hdc, FALSE );
747 else
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) );
755 /* Highlight new item (if any) */
756 lppop->FocusedItem = wIndex;
757 if (lppop->FocusedItem != NO_SELECTED_ITEM)
759 if (lppop->FocusedItem == SYSMENU_SELECTED)
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
774 else
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
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
802 ReleaseDC( lppop->hWnd, hdc );
806 /***********************************************************************
807 * MENU_SelectNextItem
809 static void MENU_SelectNextItem( HWND hwndOwner, HMENU hmenu )
811 int i;
812 MENUITEM *items;
813 POPUPMENU *menu;
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))
821 for (i = menu->FocusedItem+1; i < menu->nItems; i++)
823 if (!(items[i].item_flags & MF_SEPARATOR))
825 MENU_SelectItem( hwndOwner, hmenu, i );
826 return;
829 if (MENU_HasSysMenu( menu ))
831 MENU_SelectItem( hwndOwner, hmenu, SYSMENU_SELECTED );
832 return;
835 for (i = 0; i < menu->nItems; i++)
837 if (!(items[i].item_flags & MF_SEPARATOR))
839 MENU_SelectItem( hwndOwner, hmenu, i );
840 return;
843 if (MENU_HasSysMenu( menu ))
844 MENU_SelectItem( hwndOwner, hmenu, SYSMENU_SELECTED );
848 /***********************************************************************
849 * MENU_SelectPrevItem
851 static void MENU_SelectPrevItem( HWND hwndOwner, HMENU hmenu )
853 int i;
854 MENUITEM *items;
855 POPUPMENU *menu;
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))
863 for (i = menu->FocusedItem - 1; i >= 0; i--)
865 if (!(items[i].item_flags & MF_SEPARATOR))
867 MENU_SelectItem( hwndOwner, hmenu, i );
868 return;
871 if (MENU_HasSysMenu( menu ))
873 MENU_SelectItem( hwndOwner, hmenu, SYSMENU_SELECTED );
874 return;
877 for (i = menu->nItems - 1; i > 0; i--)
879 if (!(items[i].item_flags & MF_SEPARATOR))
881 MENU_SelectItem( hwndOwner, hmenu, i );
882 return;
885 if (MENU_HasSysMenu( menu ))
886 MENU_SelectItem( hwndOwner, hmenu, SYSMENU_SELECTED );
890 /***********************************************************************
891 * MENU_GetSubPopup
893 * Return the handle of the selected sub-popup menu (if any).
895 static HMENU MENU_GetSubPopup( HMENU hmenu )
897 POPUPMENU *menu;
898 MENUITEM *item;
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 );
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;
912 /***********************************************************************
913 * MENU_HideSubPopups
915 * Hide the sub-popup menus of this menu.
917 static void MENU_HideSubPopups( HWND hwndOwner, HMENU hmenu )
919 MENUITEM *item;
920 POPUPMENU *menu, *submenu;
921 HMENU hsubmenu;
923 if (!(menu = (POPUPMENU *) USER_HEAP_LIN_ADDR( hmenu ))) return;
924 if (menu->FocusedItem == NO_SELECTED_ITEM) return;
925 if (menu->FocusedItem == SYSMENU_SELECTED)
927 hsubmenu = GetSystemMenu( menu->hWnd, FALSE );
929 else
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;
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 );
944 /***********************************************************************
945 * MENU_ShowSubPopup
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.
950 static HMENU MENU_ShowSubPopup( HWND hwndOwner, HMENU hmenu, BOOL selectFirst )
952 POPUPMENU *menu;
953 MENUITEM *item;
954 WND *wndPtr;
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)
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;
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)
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 );
976 else
978 MENU_ShowPopup( hwndOwner, (HMENU)item->item_id, menu->FocusedItem,
979 wndPtr->rectWindow.left + item->rect.left,
980 wndPtr->rectWindow.top + item->rect.bottom );
982 if (selectFirst) MENU_SelectNextItem( hwndOwner, (HMENU)item->item_id );
983 return (HMENU)item->item_id;
987 /***********************************************************************
988 * MENU_FindMenuByCoords
990 * Find the menu containing a given point (in screen coords).
992 static HMENU MENU_FindMenuByCoords( HMENU hmenu, POINT pt )
994 POPUPMENU *menu;
995 HWND hwnd;
997 if (!(hwnd = WindowFromPoint( pt ))) return 0;
998 while (hmenu)
1000 menu = (POPUPMENU *) USER_HEAP_LIN_ADDR( hmenu );
1001 if (menu->hWnd == hwnd)
1003 if (!(menu->wFlags & MF_POPUP))
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)
1012 if (!MENU_IsInSysMenu( menu, pt )) return 0;
1014 /* else it's in the menu bar */
1016 return hmenu;
1018 hmenu = MENU_GetSubPopup( hmenu );
1020 return 0;
1024 /***********************************************************************
1025 * MENU_ExecFocusedItem
1027 * Execute a menu item (for instance when user pressed Enter).
1028 * Return TRUE if we can go on with menu tracking.
1030 static BOOL MENU_ExecFocusedItem( HWND hwndOwner, HMENU hmenu,
1031 HMENU *hmenuCurrent )
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))
1040 if (!(item->item_flags & (MF_GRAYED | MF_DISABLED)))
1042 PostMessage( hwndOwner, (menu->wFlags & MF_SYSMENU) ?
1043 WM_SYSCOMMAND : WM_COMMAND, item->item_id, 0 );
1044 return FALSE;
1046 else return TRUE;
1048 else
1050 *hmenuCurrent = MENU_ShowSubPopup( hwndOwner, hmenu, TRUE );
1051 return TRUE;
1056 /***********************************************************************
1057 * MENU_ButtonDown
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.
1063 static BOOL MENU_ButtonDown( HWND hwndOwner, HMENU hmenu, HMENU *hmenuCurrent,
1064 POINT pt )
1066 POPUPMENU *menu;
1067 MENUITEM *item;
1068 UINT id;
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 */
1075 if (!MENU_IsInSysMenu( menu, pt )) return FALSE;
1076 id = SYSMENU_SELECTED;
1079 if (menu->FocusedItem == id)
1081 if (id == SYSMENU_SELECTED) return FALSE;
1082 if (item->item_flags & MF_POPUP)
1084 if (item->item_flags & MF_MOUSESELECT)
1086 if (menu->wFlags & MF_POPUP)
1088 MENU_HideSubPopups( hwndOwner, hmenu );
1089 *hmenuCurrent = hmenu;
1091 else return FALSE;
1093 else *hmenuCurrent = MENU_ShowSubPopup( hwndOwner, hmenu, FALSE );
1096 else
1098 MENU_HideSubPopups( hwndOwner, hmenu );
1099 MENU_SelectItem( hwndOwner, hmenu, id );
1100 *hmenuCurrent = MENU_ShowSubPopup( hwndOwner, hmenu, FALSE );
1102 return TRUE;
1106 /***********************************************************************
1107 * MENU_ButtonUp
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.
1113 static BOOL MENU_ButtonUp( HWND hwndOwner, HMENU hmenu, HMENU *hmenuCurrent,
1114 POINT pt )
1116 POPUPMENU *menu;
1117 MENUITEM *item;
1118 HMENU hsubmenu = 0;
1119 UINT id;
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 */
1126 if (!MENU_IsInSysMenu( menu, pt )) return FALSE;
1127 id = SYSMENU_SELECTED;
1128 hsubmenu = GetSystemMenu( menu->hWnd, FALSE );
1131 if (menu->FocusedItem != id) return FALSE;
1133 if (id != SYSMENU_SELECTED)
1135 if (!(item->item_flags & MF_POPUP))
1137 return MENU_ExecFocusedItem( hwndOwner, hmenu, hmenuCurrent );
1139 hsubmenu = (HMENU)item->item_id;
1141 /* Select first item of sub-popup */
1142 MENU_SelectItem( hwndOwner, hsubmenu, NO_SELECTED_ITEM );
1143 MENU_SelectNextItem( hwndOwner, hsubmenu );
1144 return TRUE;
1148 /***********************************************************************
1149 * MENU_MouseMove
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.
1155 static BOOL MENU_MouseMove( HWND hwndOwner, HMENU hmenu, HMENU *hmenuCurrent,
1156 POINT pt )
1158 MENUITEM *item;
1159 POPUPMENU *menu = (POPUPMENU *) USER_HEAP_LIN_ADDR( hmenu );
1160 UINT id = NO_SELECTED_ITEM;
1162 if (hmenu)
1164 item = MENU_FindItemByCoords( menu, pt.x, pt.y, &id );
1165 if (!item) /* Maybe in system menu */
1167 if (!MENU_IsInSysMenu( menu, pt ))
1168 id = NO_SELECTED_ITEM; /* Outside all items */
1169 else id = SYSMENU_SELECTED;
1172 if (id == NO_SELECTED_ITEM)
1174 MENU_SelectItem( hwndOwner, *hmenuCurrent, NO_SELECTED_ITEM );
1176 else if (menu->FocusedItem != id)
1178 MENU_HideSubPopups( hwndOwner, hmenu );
1179 MENU_SelectItem( hwndOwner, hmenu, id );
1180 *hmenuCurrent = MENU_ShowSubPopup( hwndOwner, hmenu, FALSE );
1182 return TRUE;
1186 /***********************************************************************
1187 * MENU_KeyLeft
1189 * Handle a VK_LEFT key event in a menu.
1190 * hmenuCurrent is the top-most visible popup.
1192 static void MENU_KeyLeft( HWND hwndOwner, HMENU hmenu, HMENU *hmenuCurrent )
1194 POPUPMENU *menu;
1195 HMENU hmenutmp, hmenuprev;
1197 menu = (POPUPMENU *) USER_HEAP_LIN_ADDR( hmenu );
1198 hmenuprev = hmenutmp = hmenu;
1199 while (hmenutmp != *hmenuCurrent)
1201 hmenutmp = MENU_GetSubPopup( hmenuprev );
1202 if (hmenutmp != *hmenuCurrent) hmenuprev = hmenutmp;
1204 MENU_HideSubPopups( hwndOwner, hmenuprev );
1206 if ((hmenuprev == hmenu) && !(menu->wFlags & MF_POPUP))
1208 /* Select previous item on the menu bar */
1209 MENU_SelectPrevItem( hwndOwner, hmenu );
1210 if (*hmenuCurrent != hmenu)
1212 /* A popup menu was displayed -> display the next one */
1213 *hmenuCurrent = MENU_ShowSubPopup( hwndOwner, hmenu, TRUE );
1216 else *hmenuCurrent = hmenuprev;
1220 /***********************************************************************
1221 * MENU_KeyRight
1223 * Handle a VK_RIGHT key event in a menu.
1224 * hmenuCurrent is the top-most visible popup.
1226 static void MENU_KeyRight( HWND hwndOwner, HMENU hmenu, HMENU *hmenuCurrent )
1228 POPUPMENU *menu;
1229 HMENU hmenutmp;
1231 menu = (POPUPMENU *) USER_HEAP_LIN_ADDR( hmenu );
1233 if ((menu->wFlags & MF_POPUP) || (*hmenuCurrent != hmenu))
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 */
1239 *hmenuCurrent = hmenutmp;
1240 return;
1244 /* If on menu-bar, go to next item */
1245 if (!(menu->wFlags & MF_POPUP))
1247 MENU_HideSubPopups( hwndOwner, hmenu );
1248 MENU_SelectNextItem( hwndOwner, hmenu );
1249 if (*hmenuCurrent != hmenu)
1251 /* A popup menu was displayed -> display the next one */
1252 *hmenuCurrent = MENU_ShowSubPopup( hwndOwner, hmenu, TRUE );
1255 else if (*hmenuCurrent != hmenu) /* Hide last level popup */
1257 HMENU hmenuprev;
1258 hmenuprev = hmenutmp = hmenu;
1259 while (hmenutmp != *hmenuCurrent)
1261 hmenutmp = MENU_GetSubPopup( hmenuprev );
1262 if (hmenutmp != *hmenuCurrent) hmenuprev = hmenutmp;
1264 MENU_HideSubPopups( hwndOwner, hmenuprev );
1265 *hmenuCurrent = hmenuprev;
1270 /***********************************************************************
1271 * MENU_TrackMenu
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.
1277 static BOOL MENU_TrackMenu( HMENU hmenu, UINT wFlags, int x, int y,
1278 HWND hwnd, LPRECT lprect )
1280 MSG *msg;
1281 HLOCAL hMsg;
1282 POPUPMENU *menu;
1283 HMENU hmenuCurrent = hmenu;
1284 BOOL fClosed = FALSE, fRemove;
1285 UINT pos;
1287 fEndMenuCalled = FALSE;
1288 if (!(menu = (POPUPMENU *) USER_HEAP_LIN_ADDR( hmenu ))) return FALSE;
1289 if (x && y)
1291 POINT pt = { x, y };
1292 MENU_ButtonDown( hwnd, hmenu, &hmenuCurrent, pt );
1294 SetCapture( hwnd );
1295 hMsg = USER_HEAP_ALLOC( sizeof(MSG) );
1296 msg = (MSG *)USER_HEAP_LIN_ADDR( hMsg );
1297 while (!fClosed)
1299 if (!MSG_InternalGetMessage( (SEGPTR)USER_HEAP_SEG_ADDR(hMsg), 0,
1300 hwnd, MSGF_MENU, 0, TRUE ))
1301 break;
1303 fRemove = FALSE;
1304 if ((msg->message >= WM_MOUSEFIRST) && (msg->message <= WM_MOUSELAST))
1306 /* Find the sub-popup for this mouse event (if any) */
1307 HMENU hsubmenu = MENU_FindMenuByCoords( hmenu, msg->pt );
1309 switch(msg->message)
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;
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;
1334 case WM_MOUSEMOVE:
1335 case WM_NCMOUSEMOVE:
1336 if ((msg->wParam & MK_LBUTTON) ||
1337 ((wFlags & TPM_RIGHTBUTTON) && (msg->wParam & MK_RBUTTON)))
1339 fClosed = !MENU_MouseMove( hwnd, hsubmenu,
1340 &hmenuCurrent, msg->pt );
1342 break;
1345 else if ((msg->message >= WM_KEYFIRST) && (msg->message <= WM_KEYLAST))
1347 fRemove = TRUE; /* Keyboard messages are always removed */
1348 switch(msg->message)
1350 case WM_KEYDOWN:
1351 switch(msg->wParam)
1353 case VK_HOME:
1354 MENU_SelectItem( hwnd, hmenuCurrent, NO_SELECTED_ITEM );
1355 MENU_SelectNextItem( hwnd, hmenuCurrent );
1356 break;
1358 case VK_END:
1359 MENU_SelectItem( hwnd, hmenuCurrent, NO_SELECTED_ITEM );
1360 MENU_SelectPrevItem( hwnd, hmenuCurrent );
1361 break;
1363 case VK_UP:
1364 MENU_SelectPrevItem( hwnd, hmenuCurrent );
1365 break;
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;
1375 case VK_LEFT:
1376 MENU_KeyLeft( hwnd, hmenu, &hmenuCurrent );
1377 break;
1379 case VK_RIGHT:
1380 MENU_KeyRight( hwnd, hmenu, &hmenuCurrent );
1381 break;
1383 case VK_SPACE:
1384 case VK_RETURN:
1385 fClosed = !MENU_ExecFocusedItem( hwnd, hmenuCurrent,
1386 &hmenuCurrent );
1387 break;
1389 case VK_ESCAPE:
1390 fClosed = TRUE;
1391 break;
1393 default:
1394 break;
1396 break; /* WM_KEYDOWN */
1398 case WM_SYSKEYDOWN:
1399 switch(msg->wParam)
1401 case VK_MENU:
1402 fClosed = TRUE;
1403 break;
1406 break; /* WM_SYSKEYDOWN */
1408 case WM_CHAR:
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
1418 MENU_SelectItem( hwnd, hmenuCurrent, pos );
1419 fClosed = !MENU_ExecFocusedItem( hwnd, hmenuCurrent,
1420 &hmenuCurrent );
1424 break; /* WM_CHAR */
1425 } /* switch(msg->message) */
1427 else
1429 DispatchMessage( msg );
1431 if (fEndMenuCalled) fClosed = TRUE;
1432 if (!fClosed) fRemove = TRUE;
1434 if (fRemove) /* Remove the message from the queue */
1435 PeekMessage( msg, 0, msg->message, msg->message, PM_REMOVE );
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;
1448 /***********************************************************************
1449 * MENU_TrackMouseMenuBar
1451 * Menu-bar tracking upon a mouse event. Called from NC_HandleSysCommand().
1453 void MENU_TrackMouseMenuBar( HWND hwnd, POINT pt )
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 );
1464 /***********************************************************************
1465 * MENU_TrackKbdMenuBar
1467 * Menu-bar tracking upon a keyboard event. Called from NC_HandleSysCommand().
1469 void MENU_TrackKbdMenuBar( HWND hwnd, UINT wParam )
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 );
1484 /**********************************************************************
1485 * TrackPopupMenu (USER.416)
1487 BOOL TrackPopupMenu( HMENU hMenu, UINT wFlags, short x, short y,
1488 short nReserved, HWND hWnd, LPRECT lpRect )
1490 if (!MENU_ShowPopup( hWnd, hMenu, 0, x, y )) return FALSE;
1491 return MENU_TrackMenu( hMenu, wFlags, 0, 0, hWnd, lpRect );
1495 /***********************************************************************
1496 * PopupMenuWndProc
1498 LRESULT PopupMenuWndProc(HWND hwnd,UINT message,WPARAM wParam,LPARAM lParam)
1500 switch(message)
1502 case WM_CREATE:
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;
1515 case WM_MOUSEACTIVATE: /* We don't want to be activated */
1516 return MA_NOACTIVATE;
1518 case WM_PAINT:
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
1529 EndPaint( hwnd, &ps );
1530 return 0;
1533 default:
1534 return DefWindowProc(hwnd, message, wParam, lParam);
1536 return 0;
1540 /***********************************************************************
1541 * MENU_GetMenuBarHeight
1543 * Compute the size of the menu bar height. Used by NC_HandleNCCalcSize().
1545 UINT MENU_GetMenuBarHeight( HWND hwnd, UINT menubarWidth, int orgX, int orgY )
1547 HDC hdc;
1548 RECT rectBar;
1549 WND *wndPtr;
1550 LPPOPUPMENU lppop;
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;
1563 /**********************************************************************
1564 * ChangeMenu [USER.153]
1566 BOOL ChangeMenu(HMENU hMenu, UINT nPos, LPSTR lpNewItem,
1567 UINT wItemID, UINT wFlags)
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);
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);
1580 if (wFlags & MF_CHANGE) {
1581 return ModifyMenu(hMenu, nPos, wFlags & ~MF_CHANGE, wItemID, lpNewItem);
1583 if (wFlags & MF_REMOVE) {
1584 return RemoveMenu(hMenu, wFlags & MF_BYPOSITION ? nPos : wItemID,
1585 wFlags & ~MF_REMOVE);
1587 /* Default: MF_INSERT */
1588 return InsertMenu(hMenu, nPos, wFlags, wItemID, lpNewItem);
1592 /**********************************************************************
1593 * CheckMenuItem [USER.154]
1595 BOOL CheckMenuItem(HMENU hMenu, UINT wItemID, UINT wFlags)
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;
1607 /**********************************************************************
1608 * EnableMenuItem [USER.155]
1610 BOOL EnableMenuItem(HMENU hMenu, UINT wItemID, UINT wFlags)
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;
1617 /* We can't have MF_GRAYED and MF_DISABLED together */
1618 if (wFlags & MF_GRAYED)
1620 lpitem->item_flags = (lpitem->item_flags & ~MF_DISABLED) | MF_GRAYED;
1622 else if (wFlags & MF_DISABLED)
1624 lpitem->item_flags = (lpitem->item_flags & ~MF_GRAYED) | MF_DISABLED;
1626 else /* MF_ENABLED */
1628 lpitem->item_flags &= ~(MF_GRAYED | MF_DISABLED);
1630 return TRUE;
1634 /*******************************************************************
1635 * GetMenuString (USER.161)
1637 int GetMenuString( HMENU hMenu, UINT wItemID,
1638 LPSTR str, short nMaxSiz, UINT wFlags )
1640 LPMENUITEM lpitem;
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;
1656 /**********************************************************************
1657 * HiliteMenuItem [USER.162]
1659 BOOL HiliteMenuItem(HWND hWnd, HMENU hMenu, UINT wItemID, UINT wHilite)
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;
1674 /**********************************************************************
1675 * GetMenuState [USER.250]
1677 UINT GetMenuState(HMENU hMenu, UINT wItemID, UINT wFlags)
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)
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);
1689 else return lpitem->item_flags;
1693 /**********************************************************************
1694 * GetMenuItemCount [USER.263]
1696 INT GetMenuItemCount(HMENU hMenu)
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;
1708 /**********************************************************************
1709 * GetMenuItemID [USER.264]
1711 UINT GetMenuItemID(HMENU hMenu, int nPos)
1713 LPPOPUPMENU menu;
1714 MENUITEM *item;
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;
1725 /**********************************************************************
1726 * InsertMenu [USER.410]
1728 BOOL InsertMenu(HMENU hMenu, UINT nPos, UINT wFlags, UINT wItemID, LPSTR lpNewItem)
1730 HANDLE hNewItems;
1731 MENUITEM *lpitem, *newItems;
1732 LPPOPUPMENU menu;
1734 if (IS_STRING_ITEM(wFlags))
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;
1741 else
1742 dprintf_menu(stddeb,"InsertMenu ("NPFMT", %04X, %04X, %04X, %p) !\n",
1743 hMenu, nPos, wFlags, wItemID, lpNewItem);
1745 /* Find where to insert new item */
1747 if ((wFlags & MF_BYPOSITION) &&
1748 ((nPos == (UINT)-1) || (nPos == (UINT)GetMenuItemCount(hMenu))))
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)))
1754 dprintf_menu(stddeb,"InsertMenu: "NPFMT" not a menu handle\n", hMenu);
1755 return FALSE;
1757 nPos = menu->nItems;
1759 else
1761 if (!MENU_FindItem( &hMenu, &nPos, wFlags ))
1763 dprintf_menu(stddeb,"InsertMenu: Item %X not found\n", nPos);
1764 return FALSE;
1766 if (!(menu = (LPPOPUPMENU) USER_HEAP_LIN_ADDR(hMenu)))
1768 dprintf_menu(stddeb,"InsertMenu: "NPFMT" not a menu handle\n", hMenu);
1769 return FALSE;
1773 /* Create new items array */
1775 hNewItems = USER_HEAP_ALLOC( sizeof(MENUITEM) * (menu->nItems+1) );
1776 if (!hNewItems)
1778 dprintf_menu(stddeb,"InsertMenu: allocation failed\n");
1779 return FALSE;
1781 newItems = (MENUITEM *) USER_HEAP_LIN_ADDR( hNewItems );
1782 if (menu->nItems > 0)
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) );
1790 USER_HEAP_FREE( menu->hItems );
1792 menu->hItems = hNewItems;
1793 menu->nItems++;
1795 /* Store the new item data */
1797 lpitem = &newItems[nPos];
1798 lpitem->item_flags = wFlags & ~(MF_HILITE | MF_MOUSESELECT);
1799 lpitem->item_id = wItemID;
1801 if (IS_STRING_ITEM(wFlags))
1803 /* Item beginning with a backspace is a help item */
1804 if (lpNewItem[0] == '\b')
1806 lpitem->item_flags |= MF_HELP;
1807 lpNewItem++;
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 );
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;
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;
1823 SetRectEmpty( &lpitem->rect );
1824 lpitem->hCheckBit = hStdCheck;
1825 lpitem->hUnCheckBit = 0;
1826 return TRUE;
1830 /**********************************************************************
1831 * AppendMenu [USER.411]
1833 BOOL AppendMenu(HMENU hMenu, UINT wFlags, UINT wItemID, LPSTR lpNewItem)
1835 return InsertMenu( hMenu, -1, wFlags | MF_BYPOSITION, wItemID, lpNewItem );
1839 /**********************************************************************
1840 * RemoveMenu [USER.412]
1842 BOOL RemoveMenu(HMENU hMenu, UINT nPos, UINT wFlags)
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;
1851 /* Remove item */
1853 if (IS_STRING_ITEM(lpitem->item_flags)) USER_HEAP_FREE( lpitem->hText );
1854 if (--menu->nItems == 0)
1856 USER_HEAP_FREE( menu->hItems );
1857 menu->hItems = 0;
1859 else
1861 while(nPos < menu->nItems)
1863 *lpitem = *(lpitem+1);
1864 lpitem++;
1865 nPos++;
1867 menu->hItems = USER_HEAP_REALLOC( menu->hItems,
1868 menu->nItems * sizeof(MENUITEM) );
1870 return TRUE;
1874 /**********************************************************************
1875 * DeleteMenu [USER.413]
1877 BOOL DeleteMenu(HMENU hMenu, UINT nPos, UINT wFlags)
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;
1888 /**********************************************************************
1889 * ModifyMenu [USER.414]
1891 BOOL ModifyMenu(HMENU hMenu, UINT nPos, UINT wFlags, UINT wItemID, LPSTR lpNewItem)
1893 LPMENUITEM lpitem;
1894 if (IS_STRING_ITEM(wFlags))
1896 dprintf_menu(stddeb,"ModifyMenu ("NPFMT", %04X, %04X, %04X, '%s') !\n",
1897 hMenu, nPos, wFlags, wItemID, lpNewItem ? lpNewItem : "(null)");
1898 if (!lpNewItem) return FALSE;
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;
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;
1909 if (IS_STRING_ITEM(wFlags))
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 );
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;
1926 /**********************************************************************
1927 * CreatePopupMenu [USER.415]
1929 HMENU CreatePopupMenu()
1931 HMENU hmenu;
1932 POPUPMENU *menu;
1934 if (!(hmenu = CreateMenu())) return 0;
1935 menu = (POPUPMENU *) USER_HEAP_LIN_ADDR( hmenu );
1936 menu->wFlags |= MF_POPUP;
1937 return hmenu;
1941 /**********************************************************************
1942 * GetMenuCheckMarkDimensions [USER.417]
1944 DWORD GetMenuCheckMarkDimensions()
1946 return MAKELONG( check_bitmap_width, check_bitmap_height );
1950 /**********************************************************************
1951 * SetMenuItemBitmaps [USER.418]
1953 BOOL SetMenuItemBitmaps(HMENU hMenu, UINT nPos, UINT wFlags,
1954 HBITMAP hNewCheck, HBITMAP hNewUnCheck)
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;
1961 if (!hNewCheck && !hNewUnCheck)
1963 /* If both are NULL, restore default bitmaps */
1964 lpitem->hCheckBit = hStdCheck;
1965 lpitem->hUnCheckBit = 0;
1966 lpitem->item_flags &= ~MF_USECHECKBITMAPS;
1968 else /* Install new bitmaps */
1970 lpitem->hCheckBit = hNewCheck;
1971 lpitem->hUnCheckBit = hNewUnCheck;
1972 lpitem->item_flags |= MF_USECHECKBITMAPS;
1974 return TRUE;
1978 /**********************************************************************
1979 * CreateMenu [USER.151]
1981 HMENU CreateMenu()
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;
2004 /**********************************************************************
2005 * DestroyMenu [USER.152]
2007 BOOL DestroyMenu(HMENU hMenu)
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 );
2018 if (lppop->hItems)
2020 int i;
2021 MENUITEM *item = (MENUITEM *) USER_HEAP_LIN_ADDR( lppop->hItems );
2022 for (i = lppop->nItems; i > 0; i--, item++)
2024 if (item->item_flags & MF_POPUP)
2025 DestroyMenu( (HMENU)item->item_id );
2027 USER_HEAP_FREE( lppop->hItems );
2029 USER_HEAP_FREE( hMenu );
2030 dprintf_menu(stddeb,"DestroyMenu ("NPFMT") // End !\n", hMenu);
2031 return TRUE;
2034 /**********************************************************************
2035 * GetSystemMenu [USER.156]
2037 HMENU GetSystemMenu(HWND hWnd, BOOL bRevert)
2039 WND *wndPtr = WIN_FindWndPtr( hWnd );
2040 if (!wndPtr) return 0;
2042 if (!bRevert) return wndPtr->hSysMenu;
2043 if (wndPtr->hSysMenu) DestroyMenu(wndPtr->hSysMenu);
2044 wndPtr->hSysMenu = MENU_CopySysMenu();
2045 return wndPtr->hSysMenu;
2048 /**********************************************************************
2049 * SetSystemMenu [USER.280]
2051 BOOL SetSystemMenu(HWND hWnd, HMENU newHmenu)
2053 WND *wndPtr;
2055 if ((wndPtr = WIN_FindWndPtr(hWnd)) != NULL) wndPtr->hSysMenu = newHmenu;
2056 return TRUE;
2060 /**********************************************************************
2061 * GetMenu [USER.157]
2063 HMENU GetMenu(HWND hWnd)
2065 WND * wndPtr = WIN_FindWndPtr(hWnd);
2066 if (wndPtr == NULL) return 0;
2067 return (HMENU)wndPtr->wIDmenu;
2071 /**********************************************************************
2072 * SetMenu [USER.158]
2074 BOOL SetMenu(HWND hWnd, HMENU hMenu)
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;
2083 dprintf_menu(stddeb,"SetMenu("NPFMT", "NPFMT");\n", hWnd, hMenu);
2084 if (GetCapture() == hWnd) ReleaseCapture();
2085 wndPtr->wIDmenu = (UINT)hMenu;
2086 if (hMenu != 0)
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;
2094 lpmenu->hWnd = hWnd;
2095 lpmenu->wFlags &= ~MF_POPUP; /* Can't be a popup */
2096 lpmenu->Height = 0; /* Make sure we recalculate the size */
2098 SetWindowPos( hWnd, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE |
2099 SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED );
2100 return TRUE;
2105 /**********************************************************************
2106 * GetSubMenu [USER.159]
2108 HMENU GetSubMenu(HMENU hMenu, short nPos)
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;
2121 /**********************************************************************
2122 * DrawMenuBar [USER.160]
2124 void DrawMenuBar(HWND hWnd)
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;
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 );
2144 /***********************************************************************
2145 * EndMenu (USER.187)
2147 void EndMenu(void)
2149 /* Note: this won't work when we have multiple tasks... */
2150 fEndMenuCalled = TRUE;
2154 /***********************************************************************
2155 * LookupMenuHandle (USER.217)
2157 HMENU LookupMenuHandle( HMENU hmenu, INT id )
2159 if (!MENU_FindItem( &hmenu, &id, MF_BYCOMMAND )) return 0;
2160 else return hmenu;
2164 /**********************************************************************
2165 * LoadMenu (USER.150)
2167 HMENU LoadMenu( HINSTANCE instance, SEGPTR name )
2169 HRSRC hRsrc;
2170 HGLOBAL handle;
2171 HMENU hMenu;
2173 if (HIWORD(name))
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 );
2179 else
2180 dprintf_resource(stddeb,"LoadMenu("NPFMT",%04x)\n",instance,LOWORD(name));
2182 if (!name) return 0;
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;
2192 /**********************************************************************
2193 * LoadMenuIndirect [USER.220]
2195 HMENU LoadMenuIndirect(LPSTR menu_template)
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;
2208 /**********************************************************************
2209 * ParseMenuResource (from Resource or Template)
2211 WORD * ParseMenuResource(WORD *first_item, int level, HMENU hMenu)
2213 WORD *item;
2214 WORD *next_item;
2215 HMENU hSubMenu;
2216 int i;
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);
2233 else
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 );
2244 } while (!(*item & MF_END));
2245 return next_item;
2249 /**********************************************************************
2250 * IsMenu (USER.358)
2252 BOOL IsMenu( HMENU hmenu )
2254 LPPOPUPMENU menu;
2255 if (!(menu = (LPPOPUPMENU) USER_HEAP_LIN_ADDR( hmenu ))) return FALSE;
2256 return (menu->wMagic == MENU_MAGIC);