Release 951226
[wine/multimedia.git] / controls / menu.c
blob63f424d4d03b1788f7c3d4e58ba3a66b6b92d9a6
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 HMENU CopySysMenu();
61 WORD * ParseMenuResource(WORD *first_item, int level, HMENU hMenu);
64 /***********************************************************************
65 * MENU_Init
67 * Menus initialisation.
69 BOOL MENU_Init()
71 BITMAP bm;
73 /* Load bitmaps */
75 if (!(hStdCheck = LoadBitmap( 0, MAKEINTRESOURCE(OBM_CHECK) )))
76 return FALSE;
77 GetObject( hStdCheck, sizeof(BITMAP), (LPSTR)&bm );
78 check_bitmap_width = bm.bmWidth;
79 check_bitmap_height = bm.bmHeight;
80 if (!(hStdMnArrow = LoadBitmap( 0, MAKEINTRESOURCE(OBM_MNARROW) )))
81 return FALSE;
82 GetObject( hStdMnArrow, sizeof(BITMAP), (LPSTR)&bm );
83 arrow_bitmap_width = bm.bmWidth;
84 arrow_bitmap_height = bm.bmHeight;
86 return TRUE;
90 /***********************************************************************
91 * MENU_HasSysMenu
93 * Check whether the window owning the menu bar has a system menu.
95 static BOOL MENU_HasSysMenu( POPUPMENU *menu )
97 WND *wndPtr;
99 if (menu->wFlags & MF_POPUP) return FALSE;
100 if (!(wndPtr = WIN_FindWndPtr( menu->hWnd ))) return FALSE;
101 return (wndPtr->dwStyle & WS_SYSMENU) != 0;
105 /***********************************************************************
106 * MENU_IsInSysMenu
108 * Check whether the point (in screen coords) is in the system menu
109 * of the window owning the given menu.
111 static BOOL MENU_IsInSysMenu( POPUPMENU *menu, POINT pt )
113 WND *wndPtr;
115 if (menu->wFlags & MF_POPUP) return FALSE;
116 if (!(wndPtr = WIN_FindWndPtr( menu->hWnd ))) return FALSE;
117 if (!(wndPtr->dwStyle & WS_SYSMENU)) return FALSE;
118 if ((pt.x < wndPtr->rectClient.left) ||
119 (pt.x >= wndPtr->rectClient.left+SYSMETRICS_CXSIZE+SYSMETRICS_CXBORDER))
120 return FALSE;
121 if ((pt.y >= wndPtr->rectClient.top - menu->Height) ||
122 (pt.y < wndPtr->rectClient.top - menu->Height -
123 SYSMETRICS_CYSIZE - SYSMETRICS_CYBORDER)) return FALSE;
124 return TRUE;
128 /***********************************************************************
129 * MENU_FindItem
131 * Find a menu item. Return a pointer on the item, and modifies *hmenu
132 * in case the item was in a sub-menu.
134 static MENUITEM *MENU_FindItem( HMENU *hmenu, UINT *nPos, UINT wFlags )
136 POPUPMENU *menu;
137 MENUITEM *item;
138 int i;
140 if (!(menu = (POPUPMENU *) USER_HEAP_LIN_ADDR(*hmenu))) return NULL;
141 item = (MENUITEM *) USER_HEAP_LIN_ADDR( menu->hItems );
142 if (wFlags & MF_BYPOSITION)
144 if (*nPos >= menu->nItems) return NULL;
145 return &item[*nPos];
147 else
149 for (i = 0; i < menu->nItems; i++, item++)
151 if (item->item_id == *nPos)
153 *nPos = i;
154 return item;
156 else if (item->item_flags & MF_POPUP)
158 HMENU hsubmenu = (HMENU)item->item_id;
159 MENUITEM *subitem = MENU_FindItem( &hsubmenu, nPos, wFlags );
160 if (subitem)
162 *hmenu = hsubmenu;
163 return subitem;
168 return NULL;
172 /***********************************************************************
173 * MENU_FindItemByCoords
175 * Find the item at the specified coordinates (screen coords).
177 static MENUITEM *MENU_FindItemByCoords( POPUPMENU *menu, int x, int y, UINT *pos )
179 MENUITEM *item;
180 WND *wndPtr;
181 int i;
183 if (!(wndPtr = WIN_FindWndPtr( menu->hWnd ))) return NULL;
184 x -= wndPtr->rectWindow.left;
185 y -= wndPtr->rectWindow.top;
186 item = (MENUITEM *) USER_HEAP_LIN_ADDR( menu->hItems );
187 for (i = 0; i < menu->nItems; i++, item++)
189 if ((x >= item->rect.left) && (x < item->rect.right) &&
190 (y >= item->rect.top) && (y < item->rect.bottom))
192 if (pos) *pos = i;
193 return item;
196 return NULL;
200 /***********************************************************************
201 * MENU_FindItemByKey
203 * Find the menu item selected by a key press.
204 * Return item id, -1 if none, -2 if we should close the menu.
206 static UINT MENU_FindItemByKey( HWND hwndOwner, HMENU hmenu, UINT key )
208 POPUPMENU *menu;
209 LPMENUITEM lpitem;
210 int i;
211 LONG menuchar;
213 menu = (POPUPMENU *) USER_HEAP_LIN_ADDR( hmenu );
214 lpitem = (MENUITEM *) USER_HEAP_LIN_ADDR( menu->hItems );
215 key = toupper(key);
216 for (i = 0; i < menu->nItems; i++, lpitem++)
218 if (IS_STRING_ITEM(lpitem->item_flags))
220 char *p = strchr( lpitem->item_text, '&' );
221 if (p && (p[1] != '&') && (toupper(p[1]) == key)) return i;
224 #ifdef WINELIB32
225 menuchar = SendMessage( hwndOwner, WM_MENUCHAR,
226 MAKEWPARAM(key,menu->wFlags), (LPARAM)hmenu );
227 #else
228 menuchar = SendMessage( hwndOwner, WM_MENUCHAR, key,
229 MAKELONG( menu->wFlags, hmenu ) );
230 #endif
231 if (HIWORD(menuchar) == 2) return LOWORD(menuchar);
232 if (HIWORD(menuchar) == 1) return -2;
233 return -1;
237 /***********************************************************************
238 * MENU_CalcItemSize
240 * Calculate the size of the menu item and store it in lpitem->rect.
242 static void MENU_CalcItemSize( HDC hdc, LPMENUITEM lpitem, HWND hwndOwner,
243 int orgX, int orgY, BOOL menuBar )
245 DWORD dwSize;
246 char *p;
248 SetRect( &lpitem->rect, orgX, orgY, orgX, orgY );
249 lpitem->xTab = 0;
250 if (lpitem->item_flags & MF_OWNERDRAW) {
251 static HANDLE mistrh = 0;
252 static SEGPTR mistrsegp = 0;
253 static LPMEASUREITEMSTRUCT mistruct=NULL;
254 if (mistruct == NULL) {
255 mistrh = GlobalAlloc(0,sizeof(MEASUREITEMSTRUCT));
256 mistrsegp = (SEGPTR)WIN16_GlobalLock(mistrh);
257 mistruct = PTR_SEG_TO_LIN(mistrsegp);
259 mistruct->CtlType = ODT_MENU;
260 mistruct->itemID = lpitem->item_id;
261 mistruct->itemData = (long int)lpitem->item_text;
262 mistruct->itemHeight = 16;
263 mistruct->itemWidth = 30;
264 SendMessage(hwndOwner,WM_MEASUREITEM,0,(LPARAM)mistrsegp);
265 lpitem->rect.bottom += mistruct->itemHeight;
266 lpitem->rect.right += mistruct->itemWidth;
267 dprintf_menu(stddeb,"DrawMenuItem: MeasureItem %04x %d:%d!\n",
268 lpitem->item_id,mistruct->itemWidth, mistruct->itemHeight);
269 return;
272 if (lpitem->item_flags & MF_SEPARATOR)
274 lpitem->rect.bottom += SEPARATOR_HEIGHT;
275 return;
278 if (!menuBar)
280 lpitem->rect.right += 2 * check_bitmap_width;
281 if (lpitem->item_flags & MF_POPUP)
282 lpitem->rect.right += arrow_bitmap_width;
285 if (lpitem->item_flags & MF_BITMAP)
287 BITMAP bm;
288 GetObject( (HBITMAP)lpitem->hText, sizeof(BITMAP), (LPSTR)&bm );
289 lpitem->rect.right += bm.bmWidth;
290 lpitem->rect.bottom += bm.bmHeight;
291 return;
294 /* If we get here, then it is a text item */
296 dwSize = (lpitem->item_text == NULL) ? 0 : GetTextExtent( hdc, lpitem->item_text, strlen(lpitem->item_text));
297 lpitem->rect.right += LOWORD(dwSize);
298 lpitem->rect.bottom += MAX( HIWORD(dwSize), SYSMETRICS_CYMENU );
300 if (menuBar) lpitem->rect.right += MENU_BAR_ITEMS_SPACE;
301 else if ( ( lpitem->item_text != NULL ) && (p = strchr( lpitem->item_text, '\t' )) != NULL)
303 /* Item contains a tab (only meaningful in popup menus) */
304 lpitem->xTab = check_bitmap_width + MENU_TAB_SPACE +
305 LOWORD( GetTextExtent( hdc, lpitem->item_text,
306 (int)(p - lpitem->item_text) ));
307 lpitem->rect.right += MENU_TAB_SPACE;
309 else
311 if( ( lpitem->item_text != NULL ) && strchr( lpitem->item_text, '\b' ))
312 lpitem->rect.right += MENU_TAB_SPACE;
313 lpitem->xTab = lpitem->rect.right - check_bitmap_width
314 - arrow_bitmap_width;
319 /***********************************************************************
320 * MENU_PopupMenuCalcSize
322 * Calculate the size of a popup menu.
324 static void MENU_PopupMenuCalcSize( LPPOPUPMENU lppop, HWND hwndOwner )
326 LPMENUITEM items, lpitem;
327 HDC hdc;
328 int start, i;
329 int orgX, orgY, maxX, maxTab, maxTabWidth;
331 lppop->Width = lppop->Height = 0;
332 if (lppop->nItems == 0) return;
333 items = (MENUITEM *)USER_HEAP_LIN_ADDR( lppop->hItems );
334 hdc = GetDC( 0 );
335 maxX = start = 0;
336 while (start < lppop->nItems)
338 lpitem = &items[start];
339 orgX = maxX;
340 orgY = 0;
341 maxTab = maxTabWidth = 0;
343 /* Parse items until column break or end of menu */
344 for (i = start; i < lppop->nItems; i++, lpitem++)
346 if ((i != start) &&
347 (lpitem->item_flags & (MF_MENUBREAK | MF_MENUBARBREAK))) break;
348 MENU_CalcItemSize( hdc, lpitem, hwndOwner, orgX, orgY, FALSE );
349 if (lpitem->item_flags & MF_MENUBARBREAK) orgX++;
350 maxX = MAX( maxX, lpitem->rect.right );
351 orgY = lpitem->rect.bottom;
352 if (lpitem->xTab)
354 maxTab = MAX( maxTab, lpitem->xTab );
355 maxTabWidth = MAX(maxTabWidth,lpitem->rect.right-lpitem->xTab);
359 /* Finish the column (set all items to the largest width found) */
360 maxX = MAX( maxX, maxTab + maxTabWidth );
361 for (lpitem = &items[start]; start < i; start++, lpitem++)
363 lpitem->rect.right = maxX;
364 if (lpitem->xTab) lpitem->xTab = maxTab;
366 lppop->Height = MAX( lppop->Height, orgY );
369 lppop->Width = maxX;
370 ReleaseDC( 0, hdc );
374 /***********************************************************************
375 * MENU_MenuBarCalcSize
377 * Calculate the size of the menu bar.
379 static void MENU_MenuBarCalcSize( HDC hdc, LPRECT lprect, LPPOPUPMENU lppop,
380 HWND hwndOwner )
382 LPMENUITEM lpitem, items;
383 int start, i, orgX, orgY, maxY, helpPos;
385 if ((lprect == NULL) || (lppop == NULL)) return;
386 if (lppop->nItems == 0) return;
387 dprintf_menucalc(stddeb,"MenuBarCalcSize left=%ld top=%ld right=%ld bottom=%ld !\n",
388 (LONG)lprect->left, (LONG)lprect->top, (LONG)lprect->right, (LONG)lprect->bottom);
389 items = (MENUITEM *)USER_HEAP_LIN_ADDR( lppop->hItems );
390 lppop->Width = lprect->right - lprect->left;
391 lppop->Height = 0;
392 maxY = lprect->top;
393 start = 0;
394 helpPos = -1;
395 while (start < lppop->nItems)
397 lpitem = &items[start];
398 orgX = lprect->left;
399 orgY = maxY;
401 /* Parse items until line break or end of menu */
402 for (i = start; i < lppop->nItems; i++, lpitem++)
404 if ((helpPos == -1) && (lpitem->item_flags & MF_HELP)) helpPos = i;
405 if ((i != start) &&
406 (lpitem->item_flags & (MF_MENUBREAK | MF_MENUBARBREAK))) break;
407 MENU_CalcItemSize( hdc, lpitem, hwndOwner, orgX, orgY, TRUE );
408 if (lpitem->rect.right > lprect->right)
410 if (i != start) break;
411 else lpitem->rect.right = lprect->right;
413 maxY = MAX( maxY, lpitem->rect.bottom );
414 orgX = lpitem->rect.right;
417 /* Finish the line (set all items to the largest height found) */
418 while (start < i) items[start++].rect.bottom = maxY;
421 lprect->bottom = maxY;
422 lppop->Height = lprect->bottom - lprect->top;
424 /* Flush right all items between the MF_HELP and the last item */
425 /* (if several lines, only move the last line) */
426 if (helpPos != -1)
428 lpitem = &items[lppop->nItems-1];
429 orgY = lpitem->rect.top;
430 orgX = lprect->right;
431 for (i = lppop->nItems - 1; i >= helpPos; i--, lpitem--)
433 if (lpitem->rect.top != orgY) break; /* Other line */
434 if (lpitem->rect.right >= orgX) break; /* Too far right already */
435 lpitem->rect.left += orgX - lpitem->rect.right;
436 lpitem->rect.right = orgX;
437 orgX = lpitem->rect.left;
443 /***********************************************************************
444 * MENU_DrawMenuItem
446 * Draw a single menu item.
448 static void MENU_DrawMenuItem( HWND hwnd, HDC hdc, LPMENUITEM lpitem,
449 UINT height, BOOL menuBar )
451 RECT rect;
453 if (lpitem->item_flags & MF_OWNERDRAW) {
454 static HANDLE distrh = 0;
455 static SEGPTR distrsegp = 0;
456 static LPDRAWITEMSTRUCT distruct=NULL;
457 if (distruct == NULL) {
458 distrh = GlobalAlloc(0,sizeof(DRAWITEMSTRUCT));
459 distrsegp = (SEGPTR)WIN16_GlobalLock(distrh);
460 distruct = PTR_SEG_TO_LIN(distrsegp);
462 dprintf_menu(stddeb,"DrawMenuItem: Ownerdraw!\n");
463 distruct->CtlType = ODT_MENU;
464 distruct->itemID = lpitem->item_id;
465 distruct->itemData = (long int)lpitem->item_text;
466 distruct->itemState = 0;
467 if (lpitem->item_flags & MF_CHECKED) distruct->itemState |= ODS_CHECKED;
468 if (lpitem->item_flags & MF_GRAYED) distruct->itemState |= ODS_GRAYED;
469 if (lpitem->item_flags & MF_HILITE) distruct->itemState |= ODS_SELECTED;
470 distruct->itemAction = ODA_DRAWENTIRE | ODA_SELECT | ODA_FOCUS;
471 distruct->hwndItem = hwnd;
472 distruct->hDC = hdc;
473 distruct->rcItem = lpitem->rect;
474 SendMessage(hwnd,WM_DRAWITEM,0,(LPARAM)distrsegp);
475 return;
477 if (menuBar && (lpitem->item_flags & MF_SEPARATOR)) return;
478 rect = lpitem->rect;
480 /* Draw the background */
482 if (lpitem->item_flags & MF_HILITE)
483 FillRect( hdc, &rect, sysColorObjects.hbrushHighlight );
484 else FillRect( hdc, &rect, sysColorObjects.hbrushMenu );
485 SetBkMode( hdc, TRANSPARENT );
487 /* Draw the separator bar (if any) */
489 if (!menuBar && (lpitem->item_flags & MF_MENUBARBREAK))
491 SelectObject( hdc, sysColorObjects.hpenWindowFrame );
492 MoveTo( hdc, rect.left, 0 );
493 LineTo( hdc, rect.left, height );
495 if (lpitem->item_flags & MF_SEPARATOR)
497 SelectObject( hdc, sysColorObjects.hpenWindowFrame );
498 MoveTo( hdc, rect.left, rect.top + SEPARATOR_HEIGHT/2 );
499 LineTo( hdc, rect.right, rect.top + SEPARATOR_HEIGHT/2 );
500 return;
503 /* Setup colors */
505 if (lpitem->item_flags & MF_HILITE)
507 if (lpitem->item_flags & MF_GRAYED)
508 SetTextColor( hdc, GetSysColor( COLOR_GRAYTEXT ) );
509 else
510 SetTextColor( hdc, GetSysColor( COLOR_HIGHLIGHTTEXT ) );
511 SetBkColor( hdc, GetSysColor( COLOR_HIGHLIGHT ) );
513 else
515 if (lpitem->item_flags & MF_GRAYED)
516 SetTextColor( hdc, GetSysColor( COLOR_GRAYTEXT ) );
517 else
518 SetTextColor( hdc, GetSysColor( COLOR_MENUTEXT ) );
519 SetBkColor( hdc, GetSysColor( COLOR_MENU ) );
522 if (!menuBar)
524 /* Draw the check mark */
526 if (lpitem->item_flags & MF_CHECKED)
528 GRAPH_DrawBitmap(hdc, lpitem->hCheckBit ? lpitem->hCheckBit :
529 hStdCheck, rect.left,
530 (rect.top+rect.bottom-check_bitmap_height) / 2,
531 0, 0, check_bitmap_width, check_bitmap_height );
533 else if (lpitem->hUnCheckBit != 0) /* Not checked */
535 GRAPH_DrawBitmap(hdc, lpitem->hUnCheckBit, rect.left,
536 (rect.top+rect.bottom-check_bitmap_height) / 2,
537 0, 0, check_bitmap_width, check_bitmap_height );
540 /* Draw the popup-menu arrow */
542 if (lpitem->item_flags & MF_POPUP)
544 GRAPH_DrawBitmap( hdc, hStdMnArrow,
545 rect.right-arrow_bitmap_width-1,
546 (rect.top+rect.bottom-arrow_bitmap_height) / 2,
547 0, 0, arrow_bitmap_width, arrow_bitmap_height );
550 rect.left += check_bitmap_width;
551 rect.right -= arrow_bitmap_width;
554 /* Draw the item text or bitmap */
556 if (lpitem->item_flags & MF_BITMAP)
558 GRAPH_DrawBitmap( hdc, (HBITMAP)lpitem->hText, rect.left, rect.top,
559 0, 0, rect.right-rect.left, rect.bottom-rect.top );
560 return;
562 /* No bitmap - process text if present */
563 else if ((lpitem->item_text) != ((char *) NULL))
565 register int i;
567 if (menuBar)
569 rect.left += MENU_BAR_ITEMS_SPACE / 2;
570 rect.right -= MENU_BAR_ITEMS_SPACE / 2;
571 i = strlen( lpitem->item_text );
573 else
575 for (i = 0; lpitem->item_text[i]; i++)
576 if ((lpitem->item_text[i] == '\t') ||
577 (lpitem->item_text[i] == '\b')) break;
580 DrawText( hdc, lpitem->item_text, i, &rect,
581 DT_LEFT | DT_VCENTER | DT_SINGLELINE );
583 if (lpitem->item_text[i]) /* There's a tab or flush-right char */
585 if (lpitem->item_text[i] == '\t')
587 rect.left = lpitem->xTab;
588 DrawText( hdc, lpitem->item_text + i + 1, -1, &rect,
589 DT_LEFT | DT_VCENTER | DT_SINGLELINE );
591 else DrawText( hdc, lpitem->item_text + i + 1, -1, &rect,
592 DT_RIGHT | DT_VCENTER | DT_SINGLELINE );
598 /***********************************************************************
599 * MENU_DrawPopupMenu
601 * Paint a popup menu.
603 static void MENU_DrawPopupMenu( HWND hwnd, HDC hdc, HMENU hmenu )
605 POPUPMENU *menu;
606 MENUITEM *item;
607 RECT rect;
608 int i;
610 GetClientRect( hwnd, &rect );
611 FillRect( hdc, &rect, sysColorObjects.hbrushMenu );
612 menu = (POPUPMENU *) USER_HEAP_LIN_ADDR( hmenu );
613 if (!menu || !menu->nItems) return;
614 item = (MENUITEM *) USER_HEAP_LIN_ADDR( menu->hItems );
615 for (i = menu->nItems; i > 0; i--, item++)
616 MENU_DrawMenuItem( hwnd, hdc, item, menu->Height, FALSE );
620 /***********************************************************************
621 * MENU_DrawMenuBar
623 * Paint a menu bar. Returns the height of the menu bar.
625 UINT MENU_DrawMenuBar(HDC hDC, LPRECT lprect, HWND hwnd, BOOL suppress_draw)
627 LPPOPUPMENU lppop;
628 LPMENUITEM lpitem;
629 int i;
630 WND *wndPtr = WIN_FindWndPtr( hwnd );
632 lppop = (LPPOPUPMENU) USER_HEAP_LIN_ADDR( (HMENU)wndPtr->wIDmenu );
633 if (lppop == NULL || lprect == NULL) return SYSMETRICS_CYMENU;
634 dprintf_menu(stddeb,"MENU_DrawMenuBar("NPFMT", %p, %p); !\n",
635 hDC, lprect, lppop);
636 if (lppop->Height == 0) MENU_MenuBarCalcSize(hDC, lprect, lppop, hwnd);
637 lprect->bottom = lprect->top + lppop->Height;
638 if (suppress_draw) return lppop->Height;
640 FillRect(hDC, lprect, sysColorObjects.hbrushMenu );
641 SelectObject( hDC, sysColorObjects.hpenWindowFrame );
642 MoveTo( hDC, lprect->left, lprect->bottom );
643 LineTo( hDC, lprect->right, lprect->bottom );
645 if (lppop->nItems == 0) return SYSMETRICS_CYMENU;
646 lpitem = (MENUITEM *) USER_HEAP_LIN_ADDR( lppop->hItems );
647 for (i = 0; i < lppop->nItems; i++, lpitem++)
649 MENU_DrawMenuItem( hwnd, hDC, lpitem, lppop->Height, TRUE );
651 return lppop->Height;
655 /***********************************************************************
656 * MENU_ShowPopup
658 * Display a popup menu.
660 static BOOL MENU_ShowPopup(HWND hwndOwner, HMENU hmenu, UINT id, int x, int y)
662 POPUPMENU *menu;
664 if (!(menu = (POPUPMENU *) USER_HEAP_LIN_ADDR( hmenu ))) return FALSE;
665 if (menu->FocusedItem != NO_SELECTED_ITEM)
667 MENUITEM *item = (MENUITEM *) USER_HEAP_LIN_ADDR( menu->hItems );
668 item[menu->FocusedItem].item_flags &= ~(MF_HILITE | MF_MOUSESELECT);
669 menu->FocusedItem = NO_SELECTED_ITEM;
671 SendMessage( hwndOwner, WM_INITMENUPOPUP, (WPARAM)hmenu,
672 MAKELONG( id, (menu->wFlags & MF_SYSMENU) ? 1 : 0 ));
673 MENU_PopupMenuCalcSize( menu, hwndOwner );
674 if (!menu->hWnd)
676 WND *wndPtr = WIN_FindWndPtr( hwndOwner );
677 if (!wndPtr) return FALSE;
678 menu->hWnd = CreateWindow( POPUPMENU_CLASS_ATOM, (SEGPTR)0,
679 WS_POPUP | WS_BORDER, x, y,
680 menu->Width + 2*SYSMETRICS_CXBORDER,
681 menu->Height + 2*SYSMETRICS_CYBORDER,
682 0, 0, wndPtr->hInstance, (SEGPTR)hmenu );
683 if (!menu->hWnd) return FALSE;
685 else SetWindowPos( menu->hWnd, 0, x, y,
686 menu->Width + 2*SYSMETRICS_CXBORDER,
687 menu->Height + 2*SYSMETRICS_CYBORDER,
688 SWP_NOACTIVATE | SWP_NOZORDER );
690 /* Display the window */
692 SetWindowPos( menu->hWnd, HWND_TOP, 0, 0, 0, 0,
693 SWP_SHOWWINDOW | SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE );
694 UpdateWindow( menu->hWnd );
695 return TRUE;
699 /***********************************************************************
700 * MENU_SelectItem
702 static void MENU_SelectItem( HWND hwndOwner, HMENU hmenu, UINT wIndex )
704 MENUITEM *items;
705 LPPOPUPMENU lppop;
706 HDC hdc;
708 lppop = (POPUPMENU *) USER_HEAP_LIN_ADDR( hmenu );
709 if (!lppop->nItems) return;
710 items = (MENUITEM *) USER_HEAP_LIN_ADDR( lppop->hItems );
711 if ((wIndex != NO_SELECTED_ITEM) &&
712 (wIndex != SYSMENU_SELECTED) &&
713 (items[wIndex].item_flags & MF_SEPARATOR))
714 wIndex = NO_SELECTED_ITEM;
715 if (lppop->FocusedItem == wIndex) return;
716 if (lppop->wFlags & MF_POPUP) hdc = GetDC( lppop->hWnd );
717 else hdc = GetDCEx( lppop->hWnd, 0, DCX_CACHE | DCX_WINDOW);
719 /* Clear previous highlighted item */
720 if (lppop->FocusedItem != NO_SELECTED_ITEM)
722 if (lppop->FocusedItem == SYSMENU_SELECTED)
723 NC_DrawSysButton( lppop->hWnd, hdc, FALSE );
724 else
726 items[lppop->FocusedItem].item_flags &=~(MF_HILITE|MF_MOUSESELECT);
727 MENU_DrawMenuItem( lppop->hWnd, hdc, &items[lppop->FocusedItem], lppop->Height,
728 !(lppop->wFlags & MF_POPUP) );
732 /* Highlight new item (if any) */
733 lppop->FocusedItem = wIndex;
734 if (lppop->FocusedItem != NO_SELECTED_ITEM)
736 if (lppop->FocusedItem == SYSMENU_SELECTED)
738 NC_DrawSysButton( lppop->hWnd, hdc, TRUE );
739 #ifdef WINELIB32
740 /* FIX: LostInfo */
741 SendMessage( hwndOwner, WM_MENUSELECT,
742 MAKEWPARAM( (DWORD)GetSystemMenu( lppop->hWnd, FALSE ),
743 lppop->wFlags | MF_MOUSESELECT ),
744 (LPARAM)hmenu );
745 #else
746 SendMessage( hwndOwner, WM_MENUSELECT,
747 GetSystemMenu( lppop->hWnd, FALSE ),
748 MAKELONG( lppop->wFlags | MF_MOUSESELECT, hmenu ) );
749 #endif
751 else
753 items[lppop->FocusedItem].item_flags |= MF_HILITE;
754 MENU_DrawMenuItem( lppop->hWnd, hdc, &items[lppop->FocusedItem], lppop->Height,
755 !(lppop->wFlags & MF_POPUP) );
756 #ifdef WINELIB32
757 SendMessage( hwndOwner, WM_MENUSELECT,
758 MAKEWPARAM( items[lppop->FocusedItem].item_id,
759 items[lppop->FocusedItem].item_flags |
760 MF_MOUSESELECT ),
761 (LPARAM) hmenu );
762 #else
763 SendMessage( hwndOwner, WM_MENUSELECT,
764 items[lppop->FocusedItem].item_id,
765 MAKELONG( items[lppop->FocusedItem].item_flags | MF_MOUSESELECT, hmenu));
766 #endif
769 #ifdef WINELIB32
770 /* FIX: Lost Info */
771 else SendMessage( hwndOwner, WM_MENUSELECT,
772 MAKEWPARAM( (DWORD)hmenu, lppop->wFlags | MF_MOUSESELECT),
773 (LPARAM)hmenu );
774 #else
775 else SendMessage( hwndOwner, WM_MENUSELECT, hmenu,
776 MAKELONG( lppop->wFlags | MF_MOUSESELECT, hmenu ) );
777 #endif
779 ReleaseDC( lppop->hWnd, hdc );
783 /***********************************************************************
784 * MENU_SelectNextItem
786 static void MENU_SelectNextItem( HWND hwndOwner, HMENU hmenu )
788 int i;
789 MENUITEM *items;
790 POPUPMENU *menu;
792 menu = (POPUPMENU *) USER_HEAP_LIN_ADDR( hmenu );
793 if (!menu->nItems) return;
794 items = (MENUITEM *) USER_HEAP_LIN_ADDR( menu->hItems );
795 if ((menu->FocusedItem != NO_SELECTED_ITEM) &&
796 (menu->FocusedItem != SYSMENU_SELECTED))
798 for (i = menu->FocusedItem+1; i < menu->nItems; i++)
800 if (!(items[i].item_flags & MF_SEPARATOR))
802 MENU_SelectItem( hwndOwner, hmenu, i );
803 return;
806 if (MENU_HasSysMenu( menu ))
808 MENU_SelectItem( hwndOwner, hmenu, SYSMENU_SELECTED );
809 return;
812 for (i = 0; i < menu->nItems; i++)
814 if (!(items[i].item_flags & MF_SEPARATOR))
816 MENU_SelectItem( hwndOwner, hmenu, i );
817 return;
820 if (MENU_HasSysMenu( menu ))
821 MENU_SelectItem( hwndOwner, hmenu, SYSMENU_SELECTED );
825 /***********************************************************************
826 * MENU_SelectPrevItem
828 static void MENU_SelectPrevItem( HWND hwndOwner, HMENU hmenu )
830 int i;
831 MENUITEM *items;
832 POPUPMENU *menu;
834 menu = (POPUPMENU *) USER_HEAP_LIN_ADDR( hmenu );
835 if (!menu->nItems) return;
836 items = (MENUITEM *) USER_HEAP_LIN_ADDR( menu->hItems );
837 if ((menu->FocusedItem != NO_SELECTED_ITEM) &&
838 (menu->FocusedItem != SYSMENU_SELECTED))
840 for (i = menu->FocusedItem - 1; i >= 0; i--)
842 if (!(items[i].item_flags & MF_SEPARATOR))
844 MENU_SelectItem( hwndOwner, hmenu, i );
845 return;
848 if (MENU_HasSysMenu( menu ))
850 MENU_SelectItem( hwndOwner, hmenu, SYSMENU_SELECTED );
851 return;
854 for (i = menu->nItems - 1; i > 0; i--)
856 if (!(items[i].item_flags & MF_SEPARATOR))
858 MENU_SelectItem( hwndOwner, hmenu, i );
859 return;
862 if (MENU_HasSysMenu( menu ))
863 MENU_SelectItem( hwndOwner, hmenu, SYSMENU_SELECTED );
867 /***********************************************************************
868 * MENU_GetSubPopup
870 * Return the handle of the selected sub-popup menu (if any).
872 static HMENU MENU_GetSubPopup( HMENU hmenu )
874 POPUPMENU *menu;
875 MENUITEM *item;
877 menu = (POPUPMENU *) USER_HEAP_LIN_ADDR( hmenu );
878 if (menu->FocusedItem == NO_SELECTED_ITEM) return 0;
879 else if (menu->FocusedItem == SYSMENU_SELECTED)
880 return GetSystemMenu( menu->hWnd, FALSE );
882 item = ((MENUITEM *)USER_HEAP_LIN_ADDR(menu->hItems)) + menu->FocusedItem;
883 if (!(item->item_flags & MF_POPUP) || !(item->item_flags & MF_MOUSESELECT))
884 return 0;
885 return (HMENU)item->item_id;
889 /***********************************************************************
890 * MENU_HideSubPopups
892 * Hide the sub-popup menus of this menu.
894 static void MENU_HideSubPopups( HWND hwndOwner, HMENU hmenu )
896 MENUITEM *item;
897 POPUPMENU *menu, *submenu;
898 HMENU hsubmenu;
900 if (!(menu = (POPUPMENU *) USER_HEAP_LIN_ADDR( hmenu ))) return;
901 if (menu->FocusedItem == NO_SELECTED_ITEM) return;
902 if (menu->FocusedItem == SYSMENU_SELECTED)
904 hsubmenu = GetSystemMenu( menu->hWnd, FALSE );
906 else
908 item = ((MENUITEM *)USER_HEAP_LIN_ADDR(menu->hItems)) + menu->FocusedItem;
909 if (!(item->item_flags & MF_POPUP) ||
910 !(item->item_flags & MF_MOUSESELECT)) return;
911 item->item_flags &= ~MF_MOUSESELECT;
912 hsubmenu = (HMENU)item->item_id;
914 submenu = (POPUPMENU *) USER_HEAP_LIN_ADDR( hsubmenu );
915 MENU_HideSubPopups( hwndOwner, hsubmenu );
916 if (submenu->hWnd) ShowWindow( submenu->hWnd, SW_HIDE );
917 MENU_SelectItem( hwndOwner, hsubmenu, NO_SELECTED_ITEM );
921 /***********************************************************************
922 * MENU_ShowSubPopup
924 * Display the sub-menu of the selected item of this menu.
925 * Return the handle of the submenu, or hmenu if no submenu to display.
927 static HMENU MENU_ShowSubPopup( HWND hwndOwner, HMENU hmenu, BOOL selectFirst )
929 POPUPMENU *menu;
930 MENUITEM *item;
931 WND *wndPtr;
933 if (!(menu = (POPUPMENU *) USER_HEAP_LIN_ADDR( hmenu ))) return hmenu;
934 if (!(wndPtr = WIN_FindWndPtr( menu->hWnd ))) return hmenu;
935 if (menu->FocusedItem == NO_SELECTED_ITEM) return hmenu;
936 if (menu->FocusedItem == SYSMENU_SELECTED)
938 MENU_ShowPopup(hwndOwner, wndPtr->hSysMenu, 0, wndPtr->rectClient.left,
939 wndPtr->rectClient.top - menu->Height - 2*SYSMETRICS_CYBORDER);
940 if (selectFirst) MENU_SelectNextItem( hwndOwner, wndPtr->hSysMenu );
941 return wndPtr->hSysMenu;
943 item = ((MENUITEM *)USER_HEAP_LIN_ADDR(menu->hItems)) + menu->FocusedItem;
944 if (!(item->item_flags & MF_POPUP) ||
945 (item->item_flags & (MF_GRAYED | MF_DISABLED))) return hmenu;
946 item->item_flags |= MF_MOUSESELECT;
947 if (menu->wFlags & MF_POPUP)
949 MENU_ShowPopup( hwndOwner, (HMENU)item->item_id, menu->FocusedItem,
950 wndPtr->rectWindow.left + item->rect.right-arrow_bitmap_width,
951 wndPtr->rectWindow.top + item->rect.top );
953 else
955 MENU_ShowPopup( hwndOwner, (HMENU)item->item_id, menu->FocusedItem,
956 wndPtr->rectWindow.left + item->rect.left,
957 wndPtr->rectWindow.top + item->rect.bottom );
959 if (selectFirst) MENU_SelectNextItem( hwndOwner, (HMENU)item->item_id );
960 return (HMENU)item->item_id;
964 /***********************************************************************
965 * MENU_FindMenuByCoords
967 * Find the menu containing a given point (in screen coords).
969 static HMENU MENU_FindMenuByCoords( HMENU hmenu, POINT pt )
971 POPUPMENU *menu;
972 HWND hwnd;
974 if (!(hwnd = WindowFromPoint( pt ))) return 0;
975 while (hmenu)
977 menu = (POPUPMENU *) USER_HEAP_LIN_ADDR( hmenu );
978 if (menu->hWnd == hwnd)
980 if (!(menu->wFlags & MF_POPUP))
982 /* Make sure it's in the menu bar (or in system menu) */
983 WND *wndPtr = WIN_FindWndPtr( menu->hWnd );
984 if ((pt.x < wndPtr->rectClient.left) ||
985 (pt.x >= wndPtr->rectClient.right) ||
986 (pt.y >= wndPtr->rectClient.top)) return 0;
987 if (pt.y < wndPtr->rectClient.top - menu->Height)
989 if (!MENU_IsInSysMenu( menu, pt )) return 0;
991 /* else it's in the menu bar */
993 return hmenu;
995 hmenu = MENU_GetSubPopup( hmenu );
997 return 0;
1001 /***********************************************************************
1002 * MENU_ExecFocusedItem
1004 * Execute a menu item (for instance when user pressed Enter).
1005 * Return TRUE if we can go on with menu tracking.
1007 static BOOL MENU_ExecFocusedItem( HWND hwndOwner, HMENU hmenu,
1008 HMENU *hmenuCurrent )
1010 MENUITEM *item;
1011 POPUPMENU *menu = (POPUPMENU *) USER_HEAP_LIN_ADDR( hmenu );
1012 if (!menu || !menu->nItems || (menu->FocusedItem == NO_SELECTED_ITEM) ||
1013 (menu->FocusedItem == SYSMENU_SELECTED)) return TRUE;
1014 item = ((MENUITEM *)USER_HEAP_LIN_ADDR(menu->hItems)) + menu->FocusedItem;
1015 if (!(item->item_flags & MF_POPUP))
1017 if (!(item->item_flags & (MF_GRAYED | MF_DISABLED)))
1019 PostMessage( hwndOwner, (menu->wFlags & MF_SYSMENU) ?
1020 WM_SYSCOMMAND : WM_COMMAND, item->item_id, 0 );
1021 return FALSE;
1023 else return TRUE;
1025 else
1027 *hmenuCurrent = MENU_ShowSubPopup( hwndOwner, hmenu, TRUE );
1028 return TRUE;
1033 /***********************************************************************
1034 * MENU_ButtonDown
1036 * Handle a button-down event in a menu. Point is in screen coords.
1037 * hmenuCurrent is the top-most visible popup.
1038 * Return TRUE if we can go on with menu tracking.
1040 static BOOL MENU_ButtonDown( HWND hwndOwner, HMENU hmenu, HMENU *hmenuCurrent,
1041 POINT pt )
1043 POPUPMENU *menu;
1044 MENUITEM *item;
1045 UINT id;
1047 if (!hmenu) return FALSE; /* Outside all menus */
1048 menu = (POPUPMENU *) USER_HEAP_LIN_ADDR( hmenu );
1049 item = MENU_FindItemByCoords( menu, pt.x, pt.y, &id );
1050 if (!item) /* Maybe in system menu */
1052 if (!MENU_IsInSysMenu( menu, pt )) return FALSE;
1053 id = SYSMENU_SELECTED;
1056 if (menu->FocusedItem == id)
1058 if (id == SYSMENU_SELECTED) return FALSE;
1059 if (item->item_flags & MF_POPUP)
1061 if (item->item_flags & MF_MOUSESELECT)
1063 if (menu->wFlags & MF_POPUP)
1065 MENU_HideSubPopups( hwndOwner, hmenu );
1066 *hmenuCurrent = hmenu;
1068 else return FALSE;
1070 else *hmenuCurrent = MENU_ShowSubPopup( hwndOwner, hmenu, FALSE );
1073 else
1075 MENU_HideSubPopups( hwndOwner, hmenu );
1076 MENU_SelectItem( hwndOwner, hmenu, id );
1077 *hmenuCurrent = MENU_ShowSubPopup( hwndOwner, hmenu, FALSE );
1079 return TRUE;
1083 /***********************************************************************
1084 * MENU_ButtonUp
1086 * Handle a button-up event in a menu. Point is in screen coords.
1087 * hmenuCurrent is the top-most visible popup.
1088 * Return TRUE if we can go on with menu tracking.
1090 static BOOL MENU_ButtonUp( HWND hwndOwner, HMENU hmenu, HMENU *hmenuCurrent,
1091 POINT pt )
1093 POPUPMENU *menu;
1094 MENUITEM *item;
1095 HMENU hsubmenu = 0;
1096 UINT id;
1098 if (!hmenu) return FALSE; /* Outside all menus */
1099 menu = (POPUPMENU *) USER_HEAP_LIN_ADDR( hmenu );
1100 item = MENU_FindItemByCoords( menu, pt.x, pt.y, &id );
1101 if (!item) /* Maybe in system menu */
1103 if (!MENU_IsInSysMenu( menu, pt )) return FALSE;
1104 id = SYSMENU_SELECTED;
1105 hsubmenu = GetSystemMenu( menu->hWnd, FALSE );
1108 if (menu->FocusedItem != id) return FALSE;
1110 if (id != SYSMENU_SELECTED)
1112 if (!(item->item_flags & MF_POPUP))
1114 return MENU_ExecFocusedItem( hwndOwner, hmenu, hmenuCurrent );
1116 hsubmenu = (HMENU)item->item_id;
1118 /* Select first item of sub-popup */
1119 MENU_SelectItem( hwndOwner, hsubmenu, NO_SELECTED_ITEM );
1120 MENU_SelectNextItem( hwndOwner, hsubmenu );
1121 return TRUE;
1125 /***********************************************************************
1126 * MENU_MouseMove
1128 * Handle a motion event in a menu. Point is in screen coords.
1129 * hmenuCurrent is the top-most visible popup.
1130 * Return TRUE if we can go on with menu tracking.
1132 static BOOL MENU_MouseMove( HWND hwndOwner, HMENU hmenu, HMENU *hmenuCurrent,
1133 POINT pt )
1135 MENUITEM *item;
1136 POPUPMENU *menu = (POPUPMENU *) USER_HEAP_LIN_ADDR( hmenu );
1137 UINT id = NO_SELECTED_ITEM;
1139 if (hmenu)
1141 item = MENU_FindItemByCoords( menu, pt.x, pt.y, &id );
1142 if (!item) /* Maybe in system menu */
1144 if (!MENU_IsInSysMenu( menu, pt ))
1145 id = NO_SELECTED_ITEM; /* Outside all items */
1146 else id = SYSMENU_SELECTED;
1149 if (id == NO_SELECTED_ITEM)
1151 MENU_SelectItem( hwndOwner, *hmenuCurrent, NO_SELECTED_ITEM );
1153 else if (menu->FocusedItem != id)
1155 MENU_HideSubPopups( hwndOwner, hmenu );
1156 MENU_SelectItem( hwndOwner, hmenu, id );
1157 *hmenuCurrent = MENU_ShowSubPopup( hwndOwner, hmenu, FALSE );
1159 return TRUE;
1163 /***********************************************************************
1164 * MENU_KeyLeft
1166 * Handle a VK_LEFT key event in a menu.
1167 * hmenuCurrent is the top-most visible popup.
1169 static void MENU_KeyLeft( HWND hwndOwner, HMENU hmenu, HMENU *hmenuCurrent )
1171 POPUPMENU *menu;
1172 HMENU hmenutmp, hmenuprev;
1174 menu = (POPUPMENU *) USER_HEAP_LIN_ADDR( hmenu );
1175 hmenuprev = hmenutmp = hmenu;
1176 while (hmenutmp != *hmenuCurrent)
1178 hmenutmp = MENU_GetSubPopup( hmenuprev );
1179 if (hmenutmp != *hmenuCurrent) hmenuprev = hmenutmp;
1181 MENU_HideSubPopups( hwndOwner, hmenuprev );
1183 if ((hmenuprev == hmenu) && !(menu->wFlags & MF_POPUP))
1185 /* Select previous item on the menu bar */
1186 MENU_SelectPrevItem( hwndOwner, hmenu );
1187 if (*hmenuCurrent != hmenu)
1189 /* A popup menu was displayed -> display the next one */
1190 *hmenuCurrent = MENU_ShowSubPopup( hwndOwner, hmenu, TRUE );
1193 else *hmenuCurrent = hmenuprev;
1197 /***********************************************************************
1198 * MENU_KeyRight
1200 * Handle a VK_RIGHT key event in a menu.
1201 * hmenuCurrent is the top-most visible popup.
1203 static void MENU_KeyRight( HWND hwndOwner, HMENU hmenu, HMENU *hmenuCurrent )
1205 POPUPMENU *menu;
1206 HMENU hmenutmp;
1208 menu = (POPUPMENU *) USER_HEAP_LIN_ADDR( hmenu );
1210 if ((menu->wFlags & MF_POPUP) || (*hmenuCurrent != hmenu))
1212 /* If already displaying a popup, try to display sub-popup */
1213 hmenutmp = MENU_ShowSubPopup( hwndOwner, *hmenuCurrent, TRUE );
1214 if (hmenutmp != *hmenuCurrent) /* Sub-popup displayed */
1216 *hmenuCurrent = hmenutmp;
1217 return;
1221 /* If on menu-bar, go to next item */
1222 if (!(menu->wFlags & MF_POPUP))
1224 MENU_HideSubPopups( hwndOwner, hmenu );
1225 MENU_SelectNextItem( hwndOwner, hmenu );
1226 if (*hmenuCurrent != hmenu)
1228 /* A popup menu was displayed -> display the next one */
1229 *hmenuCurrent = MENU_ShowSubPopup( hwndOwner, hmenu, TRUE );
1232 else if (*hmenuCurrent != hmenu) /* Hide last level popup */
1234 HMENU hmenuprev;
1235 hmenuprev = hmenutmp = hmenu;
1236 while (hmenutmp != *hmenuCurrent)
1238 hmenutmp = MENU_GetSubPopup( hmenuprev );
1239 if (hmenutmp != *hmenuCurrent) hmenuprev = hmenutmp;
1241 MENU_HideSubPopups( hwndOwner, hmenuprev );
1242 *hmenuCurrent = hmenuprev;
1247 /***********************************************************************
1248 * MENU_TrackMenu
1250 * Menu tracking code.
1251 * If 'x' and 'y' are not 0, we simulate a button-down event at (x,y)
1252 * before beginning tracking. This is to help menu-bar tracking.
1254 static BOOL MENU_TrackMenu( HMENU hmenu, UINT wFlags, int x, int y,
1255 HWND hwnd, LPRECT lprect )
1257 MSG *msg;
1258 HLOCAL hMsg;
1259 POPUPMENU *menu;
1260 HMENU hmenuCurrent = hmenu;
1261 BOOL fClosed = FALSE, fRemove;
1262 UINT pos;
1264 fEndMenuCalled = FALSE;
1265 if (!(menu = (POPUPMENU *) USER_HEAP_LIN_ADDR( hmenu ))) return FALSE;
1266 if (x && y)
1268 POINT pt = { x, y };
1269 MENU_ButtonDown( hwnd, hmenu, &hmenuCurrent, pt );
1271 SetCapture( hwnd );
1272 hMsg = USER_HEAP_ALLOC( sizeof(MSG) );
1273 msg = (MSG *)USER_HEAP_LIN_ADDR( hMsg );
1274 while (!fClosed)
1276 if (!MSG_InternalGetMessage( (SEGPTR)USER_HEAP_SEG_ADDR(hMsg), 0,
1277 hwnd, MSGF_MENU, 0, TRUE ))
1278 break;
1280 fRemove = FALSE;
1281 if ((msg->message >= WM_MOUSEFIRST) && (msg->message <= WM_MOUSELAST))
1283 /* Find the sub-popup for this mouse event (if any) */
1284 HMENU hsubmenu = MENU_FindMenuByCoords( hmenu, msg->pt );
1286 switch(msg->message)
1288 case WM_RBUTTONDOWN:
1289 case WM_NCRBUTTONDOWN:
1290 if (!(wFlags & TPM_RIGHTBUTTON)) break;
1291 /* fall through */
1292 case WM_LBUTTONDOWN:
1293 case WM_NCLBUTTONDOWN:
1294 fClosed = !MENU_ButtonDown( hwnd, hsubmenu,
1295 &hmenuCurrent, msg->pt );
1296 break;
1298 case WM_RBUTTONUP:
1299 case WM_NCRBUTTONUP:
1300 if (!(wFlags & TPM_RIGHTBUTTON)) break;
1301 /* fall through */
1302 case WM_LBUTTONUP:
1303 case WM_NCLBUTTONUP:
1304 /* If outside all menus but inside lprect, ignore it */
1305 if (!hsubmenu && lprect && PtInRect( lprect, msg->pt )) break;
1306 fClosed = !MENU_ButtonUp( hwnd, hsubmenu,
1307 &hmenuCurrent, msg->pt );
1308 fRemove = TRUE; /* Remove event even if outside menu */
1309 break;
1311 case WM_MOUSEMOVE:
1312 case WM_NCMOUSEMOVE:
1313 if ((msg->wParam & MK_LBUTTON) ||
1314 ((wFlags & TPM_RIGHTBUTTON) && (msg->wParam & MK_RBUTTON)))
1316 fClosed = !MENU_MouseMove( hwnd, hsubmenu,
1317 &hmenuCurrent, msg->pt );
1319 break;
1322 else if ((msg->message >= WM_KEYFIRST) && (msg->message <= WM_KEYLAST))
1324 fRemove = TRUE; /* Keyboard messages are always removed */
1325 switch(msg->message)
1327 case WM_KEYDOWN:
1328 switch(msg->wParam)
1330 case VK_HOME:
1331 MENU_SelectItem( hwnd, hmenuCurrent, NO_SELECTED_ITEM );
1332 MENU_SelectNextItem( hwnd, hmenuCurrent );
1333 break;
1335 case VK_END:
1336 MENU_SelectItem( hwnd, hmenuCurrent, NO_SELECTED_ITEM );
1337 MENU_SelectPrevItem( hwnd, hmenuCurrent );
1338 break;
1340 case VK_UP:
1341 MENU_SelectPrevItem( hwnd, hmenuCurrent );
1342 break;
1344 case VK_DOWN:
1345 /* If on menu bar, pull-down the menu */
1346 if (!(menu->wFlags & MF_POPUP) && (hmenuCurrent == hmenu))
1347 hmenuCurrent = MENU_ShowSubPopup( hwnd, hmenu, TRUE );
1348 else
1349 MENU_SelectNextItem( hwnd, hmenuCurrent );
1350 break;
1352 case VK_LEFT:
1353 MENU_KeyLeft( hwnd, hmenu, &hmenuCurrent );
1354 break;
1356 case VK_RIGHT:
1357 MENU_KeyRight( hwnd, hmenu, &hmenuCurrent );
1358 break;
1360 case VK_SPACE:
1361 case VK_RETURN:
1362 fClosed = !MENU_ExecFocusedItem( hwnd, hmenuCurrent,
1363 &hmenuCurrent );
1364 break;
1366 case VK_ESCAPE:
1367 fClosed = TRUE;
1368 break;
1370 default:
1371 break;
1373 break; /* WM_KEYDOWN */
1375 case WM_SYSKEYDOWN:
1376 switch(msg->wParam)
1378 case VK_MENU:
1379 fClosed = TRUE;
1380 break;
1383 break; /* WM_SYSKEYDOWN */
1385 case WM_CHAR:
1387 /* Hack to avoid control chars. */
1388 /* We will find a better way real soon... */
1389 if ((msg->wParam <= 32) || (msg->wParam >= 127)) break;
1390 pos = MENU_FindItemByKey( hwnd, hmenuCurrent, msg->wParam );
1391 if (pos == (UINT)-2) fClosed = TRUE;
1392 else if (pos == (UINT)-1) MessageBeep(0);
1393 else
1395 MENU_SelectItem( hwnd, hmenuCurrent, pos );
1396 fClosed = !MENU_ExecFocusedItem( hwnd, hmenuCurrent,
1397 &hmenuCurrent );
1401 break; /* WM_CHAR */
1402 } /* switch(msg->message) */
1404 else
1406 DispatchMessage( msg );
1408 if (fEndMenuCalled) fClosed = TRUE;
1409 if (!fClosed) fRemove = TRUE;
1411 if (fRemove) /* Remove the message from the queue */
1412 PeekMessage( msg, 0, msg->message, msg->message, PM_REMOVE );
1414 USER_HEAP_FREE( hMsg );
1415 ReleaseCapture();
1416 MENU_HideSubPopups( hwnd, hmenu );
1417 if (menu->wFlags & MF_POPUP) ShowWindow( menu->hWnd, SW_HIDE );
1418 MENU_SelectItem( hwnd, hmenu, NO_SELECTED_ITEM );
1419 SendMessage( hwnd, WM_MENUSELECT, 0, MAKELONG( 0xffff, 0 ) );
1420 fEndMenuCalled = FALSE;
1421 return TRUE;
1425 /***********************************************************************
1426 * MENU_TrackMouseMenuBar
1428 * Menu-bar tracking upon a mouse event. Called from NC_HandleSysCommand().
1430 void MENU_TrackMouseMenuBar( HWND hwnd, POINT pt )
1432 WND *wndPtr = WIN_FindWndPtr( hwnd );
1433 SendMessage( hwnd, WM_ENTERMENULOOP, 0, 0 );
1434 SendMessage( hwnd, WM_INITMENU, wndPtr->wIDmenu, 0 );
1435 MENU_TrackMenu( (HMENU)wndPtr->wIDmenu, TPM_LEFTALIGN | TPM_LEFTBUTTON,
1436 pt.x, pt.y, hwnd, NULL );
1437 SendMessage( hwnd, WM_EXITMENULOOP, 0, 0 );
1441 /***********************************************************************
1442 * MENU_TrackKbdMenuBar
1444 * Menu-bar tracking upon a keyboard event. Called from NC_HandleSysCommand().
1446 void MENU_TrackKbdMenuBar( HWND hwnd, UINT wParam )
1448 WND *wndPtr = WIN_FindWndPtr( hwnd );
1449 if (!wndPtr->wIDmenu) return;
1450 SendMessage( hwnd, WM_ENTERMENULOOP, 0, 0 );
1451 SendMessage( hwnd, WM_INITMENU, wndPtr->wIDmenu, 0 );
1452 /* Select first selectable item */
1453 MENU_SelectItem( hwnd, (HMENU)wndPtr->wIDmenu, NO_SELECTED_ITEM );
1454 MENU_SelectNextItem( hwnd, (HMENU)wndPtr->wIDmenu );
1455 MENU_TrackMenu( (HMENU)wndPtr->wIDmenu, TPM_LEFTALIGN | TPM_LEFTBUTTON,
1456 0, 0, hwnd, NULL );
1457 SendMessage( hwnd, WM_EXITMENULOOP, 0, 0 );
1461 /**********************************************************************
1462 * TrackPopupMenu (USER.416)
1464 BOOL TrackPopupMenu( HMENU hMenu, UINT wFlags, short x, short y,
1465 short nReserved, HWND hWnd, LPRECT lpRect )
1467 if (!MENU_ShowPopup( hWnd, hMenu, 0, x, y )) return FALSE;
1468 return MENU_TrackMenu( hMenu, wFlags, 0, 0, hWnd, lpRect );
1472 /***********************************************************************
1473 * PopupMenuWndProc
1475 LRESULT PopupMenuWndProc(HWND hwnd,UINT message,WPARAM wParam,LPARAM lParam)
1477 switch(message)
1479 case WM_CREATE:
1481 CREATESTRUCT *createStruct = (CREATESTRUCT*)PTR_SEG_TO_LIN(lParam);
1482 #ifdef WINELIB32
1483 HMENU hmenu = (HMENU) (createStruct->lpCreateParams);
1484 SetWindowLong( hwnd, 0, (LONG)hmenu );
1485 #else
1486 HMENU hmenu = (HMENU) ((int)createStruct->lpCreateParams & 0xffff);
1487 SetWindowWord( hwnd, 0, hmenu );
1488 #endif
1489 return 0;
1492 case WM_MOUSEACTIVATE: /* We don't want to be activated */
1493 return MA_NOACTIVATE;
1495 case WM_PAINT:
1497 PAINTSTRUCT ps;
1498 BeginPaint( hwnd, &ps );
1499 MENU_DrawPopupMenu( hwnd, ps.hdc,
1500 #ifdef WINELIB32
1501 (HMENU)GetWindowLong( hwnd, 0 )
1502 #else
1503 (HMENU)GetWindowWord( hwnd, 0 )
1504 #endif
1506 EndPaint( hwnd, &ps );
1507 return 0;
1510 default:
1511 return DefWindowProc(hwnd, message, wParam, lParam);
1513 return 0;
1517 /***********************************************************************
1518 * MENU_GetMenuBarHeight
1520 * Compute the size of the menu bar height. Used by NC_HandleNCCalcSize().
1522 UINT MENU_GetMenuBarHeight( HWND hwnd, UINT menubarWidth, int orgX, int orgY )
1524 HDC hdc;
1525 RECT rectBar;
1526 WND *wndPtr;
1527 LPPOPUPMENU lppop;
1529 if (!(wndPtr = WIN_FindWndPtr( hwnd ))) return 0;
1530 if (!(lppop = (LPPOPUPMENU)USER_HEAP_LIN_ADDR((HMENU)wndPtr->wIDmenu)))
1531 return 0;
1532 hdc = GetDC( hwnd );
1533 SetRect( &rectBar, orgX, orgY, orgX+menubarWidth, orgY+SYSMETRICS_CYMENU );
1534 MENU_MenuBarCalcSize( hdc, &rectBar, lppop, hwnd );
1535 ReleaseDC( hwnd, hdc );
1536 return lppop->Height;
1540 /**********************************************************************
1541 * ChangeMenu [USER.153]
1543 BOOL ChangeMenu(HMENU hMenu, UINT nPos, LPSTR lpNewItem,
1544 UINT wItemID, UINT wFlags)
1546 dprintf_menu(stddeb,"ChangeMenu: menu="NPFMT" pos=%d ptr=%p item=%04x flags=%04x\n",
1547 hMenu, nPos, lpNewItem, wItemID, wFlags);
1548 if (wFlags & MF_APPEND) {
1549 return AppendMenu(hMenu, wFlags & ~MF_APPEND, wItemID, lpNewItem);
1551 if (wFlags & MF_DELETE) {
1552 return DeleteMenu(hMenu, wFlags & MF_BYPOSITION ? nPos : wItemID,
1553 wFlags & ~MF_DELETE);
1555 if (wFlags & MF_CHANGE) {
1556 return ModifyMenu(hMenu, nPos, wFlags & ~MF_CHANGE, wItemID, lpNewItem);
1558 if (wFlags & MF_REMOVE) {
1559 return RemoveMenu(hMenu, wFlags & MF_BYPOSITION ? nPos : wItemID,
1560 wFlags & ~MF_REMOVE);
1562 /* Default: MF_INSERT */
1563 return InsertMenu(hMenu, nPos, wFlags, wItemID, lpNewItem);
1567 /**********************************************************************
1568 * CheckMenuItem [USER.154]
1570 BOOL CheckMenuItem(HMENU hMenu, UINT wItemID, UINT wFlags)
1572 LPMENUITEM lpitem;
1573 dprintf_menu(stddeb,"CheckMenuItem ("NPFMT", %04X, %04X) !\n",
1574 hMenu, wItemID, wFlags);
1575 if (!(lpitem = MENU_FindItem(&hMenu, &wItemID, wFlags))) return FALSE;
1576 if (wFlags & MF_CHECKED) lpitem->item_flags |= MF_CHECKED;
1577 else lpitem->item_flags &= ~MF_CHECKED;
1578 return TRUE;
1582 /**********************************************************************
1583 * EnableMenuItem [USER.155]
1585 BOOL EnableMenuItem(HMENU hMenu, UINT wItemID, UINT wFlags)
1587 LPMENUITEM lpitem;
1588 dprintf_menu(stddeb,"EnableMenuItem ("NPFMT", %04X, %04X) !\n",
1589 hMenu, wItemID, wFlags);
1590 if (!(lpitem = MENU_FindItem( &hMenu, &wItemID, wFlags ))) return FALSE;
1592 /* We can't have MF_GRAYED and MF_DISABLED together */
1593 if (wFlags & MF_GRAYED)
1595 lpitem->item_flags = (lpitem->item_flags & ~MF_DISABLED) | MF_GRAYED;
1597 else if (wFlags & MF_DISABLED)
1599 lpitem->item_flags = (lpitem->item_flags & ~MF_GRAYED) | MF_DISABLED;
1601 else /* MF_ENABLED */
1603 lpitem->item_flags &= ~(MF_GRAYED | MF_DISABLED);
1605 return TRUE;
1609 /*******************************************************************
1610 * GetMenuString (USER.161)
1612 int GetMenuString( HMENU hMenu, UINT wItemID,
1613 LPSTR str, short nMaxSiz, UINT wFlags )
1615 LPMENUITEM lpitem;
1617 dprintf_menu( stddeb, "GetMenuString: menu="NPFMT" item=%04x ptr=%p len=%d flags=%04x\n",
1618 hMenu, wItemID, str, nMaxSiz, wFlags );
1619 if (!str || !nMaxSiz) return 0;
1620 str[0] = '\0';
1621 if (!(lpitem = MENU_FindItem( &hMenu, &wItemID, wFlags ))) return 0;
1622 if (!lpitem->item_text || !IS_STRING_ITEM(lpitem->item_flags)) return 0;
1623 nMaxSiz = MIN( nMaxSiz-1, strlen(lpitem->item_text) );
1624 strncpy( str, lpitem->item_text, nMaxSiz );
1625 str[nMaxSiz] = '\0';
1626 dprintf_menu( stddeb, "GetMenuString: returning '%s'\n", str );
1627 return nMaxSiz;
1631 /**********************************************************************
1632 * HiliteMenuItem [USER.162]
1634 BOOL HiliteMenuItem(HWND hWnd, HMENU hMenu, UINT wItemID, UINT wHilite)
1636 LPPOPUPMENU menu;
1637 LPMENUITEM lpitem;
1638 dprintf_menu(stddeb,"HiliteMenuItem("NPFMT", "NPFMT", %04X, %04X);\n",
1639 hWnd, hMenu, wItemID, wHilite);
1640 if (!(lpitem = MENU_FindItem( &hMenu, &wItemID, wHilite ))) return FALSE;
1641 if (!(menu = (LPPOPUPMENU) USER_HEAP_LIN_ADDR(hMenu))) return FALSE;
1642 if (menu->FocusedItem == wItemID) return TRUE;
1643 MENU_HideSubPopups( hWnd, hMenu );
1644 MENU_SelectItem( hWnd, hMenu, wItemID );
1645 return TRUE;
1649 /**********************************************************************
1650 * GetMenuState [USER.250]
1652 UINT GetMenuState(HMENU hMenu, UINT wItemID, UINT wFlags)
1654 LPMENUITEM lpitem;
1655 dprintf_menu(stddeb,"GetMenuState("NPFMT", %04X, %04X);\n",
1656 hMenu, wItemID, wFlags);
1657 if (!(lpitem = MENU_FindItem( &hMenu, &wItemID, wFlags ))) return -1;
1658 if (lpitem->item_flags & MF_POPUP)
1660 POPUPMENU *menu = (POPUPMENU *) USER_HEAP_LIN_ADDR( (HMENU)lpitem->item_id );
1661 if (!menu) return -1;
1662 else return (menu->nItems << 8) | (menu->wFlags & 0xff);
1664 else return lpitem->item_flags;
1668 /**********************************************************************
1669 * GetMenuItemCount [USER.263]
1671 INT GetMenuItemCount(HMENU hMenu)
1673 LPPOPUPMENU menu;
1674 dprintf_menu(stddeb,"GetMenuItemCount("NPFMT");\n", hMenu);
1675 menu = (LPPOPUPMENU) USER_HEAP_LIN_ADDR(hMenu);
1676 if (menu == NULL) return (UINT)-1;
1677 dprintf_menu(stddeb,"GetMenuItemCount("NPFMT") return %d \n",
1678 hMenu, menu->nItems);
1679 return menu->nItems;
1683 /**********************************************************************
1684 * GetMenuItemID [USER.264]
1686 UINT GetMenuItemID(HMENU hMenu, int nPos)
1688 LPPOPUPMENU menu;
1689 MENUITEM *item;
1691 dprintf_menu(stddeb,"GetMenuItemID("NPFMT", %d);\n", hMenu, nPos);
1692 if (!(menu = (LPPOPUPMENU) USER_HEAP_LIN_ADDR(hMenu))) return -1;
1693 if ((nPos < 0) || (nPos >= menu->nItems)) return -1;
1694 item = (MENUITEM *) USER_HEAP_LIN_ADDR( menu->hItems );
1695 if (item[nPos].item_flags & MF_POPUP) return -1;
1696 return item[nPos].item_id;
1700 /**********************************************************************
1701 * InsertMenu [USER.410]
1703 BOOL InsertMenu(HMENU hMenu, UINT nPos, UINT wFlags, UINT wItemID, LPSTR lpNewItem)
1705 HANDLE hNewItems;
1706 MENUITEM *lpitem, *newItems;
1707 LPPOPUPMENU menu;
1709 if (IS_STRING_ITEM(wFlags))
1711 dprintf_menu(stddeb,"InsertMenu ("NPFMT", %04X, %04X, %04X, '%s') !\n",
1712 hMenu, nPos, wFlags, wItemID,
1713 lpNewItem ? lpNewItem : "(null)");
1714 if (!lpNewItem) return FALSE;
1716 else
1717 dprintf_menu(stddeb,"InsertMenu ("NPFMT", %04X, %04X, %04X, %p) !\n",
1718 hMenu, nPos, wFlags, wItemID, lpNewItem);
1720 /* Find where to insert new item */
1722 if ((wFlags & MF_BYPOSITION) &&
1723 ((nPos == (UINT)-1) || (nPos == (UINT)GetMenuItemCount(hMenu))))
1725 /* Special case: append to menu
1726 Some programs specify the menu length to do that */
1727 if (!(menu = (LPPOPUPMENU) USER_HEAP_LIN_ADDR(hMenu)))
1729 dprintf_menu(stddeb,"InsertMenu: "NPFMT" not a menu handle\n", hMenu);
1730 return FALSE;
1732 nPos = menu->nItems;
1734 else
1736 if (!MENU_FindItem( &hMenu, &nPos, wFlags ))
1738 dprintf_menu(stddeb,"InsertMenu: Item %X not found\n", nPos);
1739 return FALSE;
1741 if (!(menu = (LPPOPUPMENU) USER_HEAP_LIN_ADDR(hMenu)))
1743 dprintf_menu(stddeb,"InsertMenu: "NPFMT" not a menu handle\n", hMenu);
1744 return FALSE;
1748 /* Create new items array */
1750 hNewItems = USER_HEAP_ALLOC( sizeof(MENUITEM) * (menu->nItems+1) );
1751 if (!hNewItems)
1753 dprintf_menu(stddeb,"InsertMenu: allocation failed\n");
1754 return FALSE;
1756 newItems = (MENUITEM *) USER_HEAP_LIN_ADDR( hNewItems );
1757 if (menu->nItems > 0)
1759 /* Copy the old array into the new */
1760 MENUITEM *oldItems = (MENUITEM *) USER_HEAP_LIN_ADDR( menu->hItems );
1761 if (nPos > 0) memcpy( newItems, oldItems, nPos * sizeof(MENUITEM) );
1762 if (nPos < menu->nItems) memcpy( &newItems[nPos+1], &oldItems[nPos],
1763 (menu->nItems-nPos)*sizeof(MENUITEM) );
1765 USER_HEAP_FREE( menu->hItems );
1767 menu->hItems = hNewItems;
1768 menu->nItems++;
1770 /* Store the new item data */
1772 lpitem = &newItems[nPos];
1773 lpitem->item_flags = wFlags & ~(MF_HILITE | MF_MOUSESELECT);
1774 lpitem->item_id = wItemID;
1776 if (IS_STRING_ITEM(wFlags))
1778 /* Item beginning with a backspace is a help item */
1779 if (lpNewItem[0] == '\b')
1781 lpitem->item_flags |= MF_HELP;
1782 lpNewItem++;
1784 lpitem->hText = USER_HEAP_ALLOC( strlen(lpNewItem)+1 );
1785 lpitem->item_text = (char *)USER_HEAP_LIN_ADDR( lpitem->hText );
1786 strcpy( lpitem->item_text, lpNewItem );
1788 #ifdef WINELIB32
1789 else if (wFlags & MF_BITMAP) lpitem->hText = (HANDLE)lpNewItem;
1790 #else
1791 else if (wFlags & MF_BITMAP) lpitem->hText = LOWORD((DWORD)lpNewItem);
1792 #endif
1793 else lpitem->item_text = lpNewItem;
1795 if (wFlags & MF_POPUP) /* Set the MF_POPUP flag on the popup-menu */
1796 ((POPUPMENU *)USER_HEAP_LIN_ADDR((HMENU)wItemID))->wFlags |= MF_POPUP;
1798 SetRectEmpty( &lpitem->rect );
1799 lpitem->hCheckBit = hStdCheck;
1800 lpitem->hUnCheckBit = 0;
1801 return TRUE;
1805 /**********************************************************************
1806 * AppendMenu [USER.411]
1808 BOOL AppendMenu(HMENU hMenu, UINT wFlags, UINT wItemID, LPSTR lpNewItem)
1810 return InsertMenu( hMenu, -1, wFlags | MF_BYPOSITION, wItemID, lpNewItem );
1814 /**********************************************************************
1815 * RemoveMenu [USER.412]
1817 BOOL RemoveMenu(HMENU hMenu, UINT nPos, UINT wFlags)
1819 LPPOPUPMENU menu;
1820 LPMENUITEM lpitem;
1821 dprintf_menu(stddeb,"RemoveMenu ("NPFMT", %04X, %04X) !\n",
1822 hMenu, nPos, wFlags);
1823 if (!(lpitem = MENU_FindItem( &hMenu, &nPos, wFlags ))) return FALSE;
1824 if (!(menu = (LPPOPUPMENU) USER_HEAP_LIN_ADDR(hMenu))) return FALSE;
1826 /* Remove item */
1828 if (IS_STRING_ITEM(lpitem->item_flags)) USER_HEAP_FREE( lpitem->hText );
1829 if (--menu->nItems == 0)
1831 USER_HEAP_FREE( menu->hItems );
1832 menu->hItems = 0;
1834 else
1836 while(nPos < menu->nItems)
1838 *lpitem = *(lpitem+1);
1839 lpitem++;
1840 nPos++;
1842 menu->hItems = USER_HEAP_REALLOC( menu->hItems,
1843 menu->nItems * sizeof(MENUITEM) );
1845 return TRUE;
1849 /**********************************************************************
1850 * DeleteMenu [USER.413]
1852 BOOL DeleteMenu(HMENU hMenu, UINT nPos, UINT wFlags)
1854 MENUITEM *item = MENU_FindItem( &hMenu, &nPos, wFlags );
1855 if (!item) return FALSE;
1856 if (item->item_flags & MF_POPUP) DestroyMenu( (HMENU)item->item_id );
1857 /* nPos is now the position of the item */
1858 RemoveMenu( hMenu, nPos, wFlags | MF_BYPOSITION );
1859 return TRUE;
1863 /**********************************************************************
1864 * ModifyMenu [USER.414]
1866 BOOL ModifyMenu(HMENU hMenu, UINT nPos, UINT wFlags, UINT wItemID, LPSTR lpNewItem)
1868 LPMENUITEM lpitem;
1869 if (IS_STRING_ITEM(wFlags))
1871 dprintf_menu(stddeb,"ModifyMenu ("NPFMT", %04X, %04X, %04X, '%s') !\n",
1872 hMenu, nPos, wFlags, wItemID, lpNewItem ? lpNewItem : "(null)");
1873 if (!lpNewItem) return FALSE;
1875 else
1876 dprintf_menu(stddeb,"ModifyMenu ("NPFMT", %04X, %04X, %04X, %p) !\n",
1877 hMenu, nPos, wFlags, wItemID, lpNewItem);
1878 if (!(lpitem = MENU_FindItem( &hMenu, &nPos, wFlags ))) return FALSE;
1880 if (IS_STRING_ITEM(lpitem->item_flags)) USER_HEAP_FREE( lpitem->hText );
1881 lpitem->item_flags = wFlags & ~(MF_HILITE | MF_MOUSESELECT);
1882 lpitem->item_id = wItemID;
1884 if (IS_STRING_ITEM(wFlags))
1886 lpitem->hText = USER_HEAP_ALLOC( strlen(lpNewItem)+1 );
1887 lpitem->item_text = (char *)USER_HEAP_LIN_ADDR( lpitem->hText );
1888 strcpy( lpitem->item_text, lpNewItem );
1890 #ifdef WINELIB32
1891 else if (wFlags & MF_BITMAP) lpitem->hText = (HANDLE)lpNewItem;
1892 #else
1893 else if (wFlags & MF_BITMAP) lpitem->hText = LOWORD((DWORD)lpNewItem);
1894 #endif
1895 else lpitem->item_text = lpNewItem;
1896 SetRectEmpty( &lpitem->rect );
1897 return TRUE;
1901 /**********************************************************************
1902 * CreatePopupMenu [USER.415]
1904 HMENU CreatePopupMenu()
1906 HMENU hmenu;
1907 POPUPMENU *menu;
1909 if (!(hmenu = CreateMenu())) return 0;
1910 menu = (POPUPMENU *) USER_HEAP_LIN_ADDR( hmenu );
1911 menu->wFlags |= MF_POPUP;
1912 return hmenu;
1916 /**********************************************************************
1917 * GetMenuCheckMarkDimensions [USER.417]
1919 DWORD GetMenuCheckMarkDimensions()
1921 return MAKELONG( check_bitmap_width, check_bitmap_height );
1925 /**********************************************************************
1926 * SetMenuItemBitmaps [USER.418]
1928 BOOL SetMenuItemBitmaps(HMENU hMenu, UINT nPos, UINT wFlags,
1929 HBITMAP hNewCheck, HBITMAP hNewUnCheck)
1931 LPMENUITEM lpitem;
1932 dprintf_menu(stddeb,"SetMenuItemBitmaps ("NPFMT", %04X, %04X, "NPFMT", %08lX) !\n",
1933 hMenu, nPos, wFlags, hNewCheck, (DWORD)hNewUnCheck);
1934 if (!(lpitem = MENU_FindItem( &hMenu, &nPos, wFlags ))) return FALSE;
1936 if (!hNewCheck && !hNewUnCheck)
1938 /* If both are NULL, restore default bitmaps */
1939 lpitem->hCheckBit = hStdCheck;
1940 lpitem->hUnCheckBit = 0;
1941 lpitem->item_flags &= ~MF_USECHECKBITMAPS;
1943 else /* Install new bitmaps */
1945 lpitem->hCheckBit = hNewCheck;
1946 lpitem->hUnCheckBit = hNewUnCheck;
1947 lpitem->item_flags |= MF_USECHECKBITMAPS;
1949 return TRUE;
1953 /**********************************************************************
1954 * CreateMenu [USER.151]
1956 HMENU CreateMenu()
1958 HMENU hMenu;
1959 LPPOPUPMENU menu;
1960 dprintf_menu(stddeb,"CreateMenu !\n");
1961 if (!(hMenu = USER_HEAP_ALLOC( sizeof(POPUPMENU) )))
1962 return 0;
1963 menu = (LPPOPUPMENU) USER_HEAP_LIN_ADDR(hMenu);
1964 menu->hNext = 0;
1965 menu->wFlags = 0;
1966 menu->wMagic = MENU_MAGIC;
1967 menu->hTaskQ = 0;
1968 menu->Width = 0;
1969 menu->Height = 0;
1970 menu->nItems = 0;
1971 menu->hWnd = 0;
1972 menu->hItems = 0;
1973 menu->FocusedItem = NO_SELECTED_ITEM;
1974 dprintf_menu(stddeb,"CreateMenu // return "NPFMT"\n", hMenu);
1975 return hMenu;
1979 /**********************************************************************
1980 * DestroyMenu [USER.152]
1982 BOOL DestroyMenu(HMENU hMenu)
1984 LPPOPUPMENU lppop;
1985 dprintf_menu(stddeb,"DestroyMenu ("NPFMT") !\n", hMenu);
1986 if (hMenu == 0) return FALSE;
1987 lppop = (LPPOPUPMENU) USER_HEAP_LIN_ADDR(hMenu);
1988 if (!lppop || (lppop->wMagic != MENU_MAGIC)) return FALSE;
1989 lppop->wMagic = 0; /* Mark it as destroyed */
1990 if ((lppop->wFlags & MF_POPUP) && lppop->hWnd)
1991 DestroyWindow( lppop->hWnd );
1993 if (lppop->hItems)
1995 int i;
1996 MENUITEM *item = (MENUITEM *) USER_HEAP_LIN_ADDR( lppop->hItems );
1997 for (i = lppop->nItems; i > 0; i--, item++)
1999 if (item->item_flags & MF_POPUP)
2000 DestroyMenu( (HMENU)item->item_id );
2002 USER_HEAP_FREE( lppop->hItems );
2004 USER_HEAP_FREE( hMenu );
2005 dprintf_menu(stddeb,"DestroyMenu ("NPFMT") // End !\n", hMenu);
2006 return TRUE;
2009 /**********************************************************************
2010 * GetSystemMenu [USER.156]
2012 HMENU GetSystemMenu(HWND hWnd, BOOL bRevert)
2014 WND *wndPtr = WIN_FindWndPtr( hWnd );
2015 if (!wndPtr) return 0;
2017 if (!bRevert) return wndPtr->hSysMenu;
2018 DestroyMenu(wndPtr->hSysMenu);
2019 wndPtr->hSysMenu = CopySysMenu();
2020 return wndPtr->hSysMenu;
2023 /**********************************************************************
2024 * SetSystemMenu [USER.280]
2026 BOOL SetSystemMenu(HWND hWnd, HMENU newHmenu)
2028 WND *wndPtr;
2030 if ((wndPtr = WIN_FindWndPtr(hWnd)) != NULL) wndPtr->hSysMenu = newHmenu;
2031 return TRUE;
2035 /**********************************************************************
2036 * GetMenu [USER.157]
2038 HMENU GetMenu(HWND hWnd)
2040 WND * wndPtr = WIN_FindWndPtr(hWnd);
2041 if (wndPtr == NULL) return 0;
2042 return (HMENU)wndPtr->wIDmenu;
2046 /**********************************************************************
2047 * SetMenu [USER.158]
2049 BOOL SetMenu(HWND hWnd, HMENU hMenu)
2051 LPPOPUPMENU lpmenu;
2052 WND * wndPtr = WIN_FindWndPtr(hWnd);
2053 if (wndPtr == NULL) {
2054 fprintf(stderr,"SetMenu("NPFMT", "NPFMT") // Bad window handle !\n",
2055 hWnd, hMenu);
2056 return FALSE;
2058 dprintf_menu(stddeb,"SetMenu("NPFMT", "NPFMT");\n", hWnd, hMenu);
2059 if (GetCapture() == hWnd) ReleaseCapture();
2060 wndPtr->wIDmenu = (UINT)hMenu;
2061 if (hMenu != 0)
2063 lpmenu = (LPPOPUPMENU) USER_HEAP_LIN_ADDR(hMenu);
2064 if (lpmenu == NULL) {
2065 fprintf(stderr,"SetMenu("NPFMT", "NPFMT") // Bad menu handle !\n",
2066 hWnd, hMenu);
2067 return FALSE;
2069 lpmenu->hWnd = hWnd;
2070 lpmenu->wFlags &= ~MF_POPUP; /* Can't be a popup */
2071 lpmenu->Height = 0; /* Make sure we recalculate the size */
2073 SetWindowPos( hWnd, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE |
2074 SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED );
2075 return TRUE;
2080 /**********************************************************************
2081 * GetSubMenu [USER.159]
2083 HMENU GetSubMenu(HMENU hMenu, short nPos)
2085 LPPOPUPMENU lppop;
2086 LPMENUITEM lpitem;
2087 dprintf_menu(stddeb,"GetSubMenu ("NPFMT", %04X) !\n", hMenu, nPos);
2088 if (!(lppop = (LPPOPUPMENU) USER_HEAP_LIN_ADDR(hMenu))) return 0;
2089 if ((UINT)nPos >= lppop->nItems) return 0;
2090 lpitem = (MENUITEM *) USER_HEAP_LIN_ADDR( lppop->hItems );
2091 if (!(lpitem[nPos].item_flags & MF_POPUP)) return 0;
2092 return (HMENU)lpitem[nPos].item_id;
2096 /**********************************************************************
2097 * DrawMenuBar [USER.160]
2099 void DrawMenuBar(HWND hWnd)
2101 WND *wndPtr;
2102 LPPOPUPMENU lppop;
2103 dprintf_menu(stddeb,"DrawMenuBar ("NPFMT")\n", hWnd);
2104 wndPtr = WIN_FindWndPtr(hWnd);
2105 if (wndPtr != NULL && (wndPtr->dwStyle & WS_CHILD) == 0 &&
2106 wndPtr->wIDmenu != 0) {
2107 dprintf_menu(stddeb,"DrawMenuBar wIDmenu=%04X \n",
2108 wndPtr->wIDmenu);
2109 lppop = (LPPOPUPMENU) USER_HEAP_LIN_ADDR((HMENU)wndPtr->wIDmenu);
2110 if (lppop == NULL) return;
2112 lppop->Height = 0; /* Make sure we call MENU_MenuBarCalcSize */
2113 SetWindowPos( hWnd, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE |
2114 SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED );
2119 /***********************************************************************
2120 * EndMenu (USER.187)
2122 void EndMenu(void)
2124 /* Note: this won't work when we have multiple tasks... */
2125 fEndMenuCalled = TRUE;
2129 /***********************************************************************
2130 * LookupMenuHandle (USER.217)
2132 HMENU LookupMenuHandle( HMENU hmenu, INT id )
2134 if (!MENU_FindItem( &hmenu, &id, MF_BYCOMMAND )) return 0;
2135 else return hmenu;
2139 /**********************************************************************
2140 * LoadMenu (USER.150)
2142 HMENU LoadMenu( HINSTANCE instance, SEGPTR name )
2144 HRSRC hRsrc;
2145 HGLOBAL handle;
2146 HMENU hMenu;
2148 if (HIWORD(name))
2150 char *str = (char *)PTR_SEG_TO_LIN( name );
2151 dprintf_menu( stddeb, "LoadMenu("NPFMT",'%s')\n", instance, str );
2152 if (str[0] == '#') name = (SEGPTR)atoi( str + 1 );
2154 else
2155 dprintf_resource(stddeb,"LoadMenu("NPFMT",%04x)\n",instance,LOWORD(name));
2157 if (!name) return 0;
2159 if (!(hRsrc = FindResource( instance, name, RT_MENU ))) return 0;
2160 if (!(handle = LoadResource( instance, hRsrc ))) return 0;
2161 hMenu = LoadMenuIndirect( LockResource(handle) );
2162 FreeResource( handle );
2163 return hMenu;
2167 /**********************************************************************
2168 * LoadMenuIndirect [USER.220]
2170 HMENU LoadMenuIndirect(LPSTR menu_template)
2172 HMENU hMenu;
2173 MENU_HEADER *menu_desc;
2174 dprintf_menu(stddeb,"LoadMenuIndirect: menu_template '%p'\n",
2175 menu_template);
2176 hMenu = CreateMenu();
2177 menu_desc = (MENU_HEADER *)menu_template;
2178 ParseMenuResource((WORD *)(menu_desc + 1), 0, hMenu);
2179 return hMenu;
2183 /**********************************************************************
2184 * CopySysMenu (Internal)
2186 HMENU CopySysMenu()
2188 HMENU hMenu;
2189 HGLOBAL handle;
2190 LPPOPUPMENU menu;
2192 if (!(handle = SYSRES_LoadResource( SYSRES_MENU_SYSMENU ))) return 0;
2193 hMenu = LoadMenuIndirect( GlobalLock( handle ) );
2194 SYSRES_FreeResource( handle );
2195 if(!hMenu)
2197 dprintf_menu(stddeb,"No SYSMENU\n");
2198 return 0;
2200 menu = (POPUPMENU*) USER_HEAP_LIN_ADDR(hMenu);
2201 menu->wFlags |= MF_SYSMENU|MF_POPUP;
2202 dprintf_menu(stddeb,"CopySysMenu hMenu="NPFMT" !\n", hMenu);
2203 return hMenu;
2207 /**********************************************************************
2208 * ParseMenuResource (from Resource or Template)
2210 WORD * ParseMenuResource(WORD *first_item, int level, HMENU hMenu)
2212 WORD *item;
2213 WORD *next_item;
2214 HMENU hSubMenu;
2215 int i;
2217 level++;
2218 next_item = first_item;
2219 i = 0;
2220 do {
2221 i++;
2222 item = next_item;
2223 if (*item & MF_POPUP) {
2224 MENU_POPUPITEM *popup_item = (MENU_POPUPITEM *) item;
2225 next_item = (WORD *) (popup_item->item_text +
2226 strlen(popup_item->item_text) + 1);
2227 hSubMenu = CreatePopupMenu();
2228 next_item = ParseMenuResource(next_item, level, hSubMenu);
2229 AppendMenu(hMenu, popup_item->item_flags,
2230 (UINT)hSubMenu, popup_item->item_text);
2232 else
2234 MENUITEMTEMPLATE *normal_item = (MENUITEMTEMPLATE *) item;
2235 WORD flags = normal_item->item_flags;
2236 next_item = (WORD *) (normal_item->item_text +
2237 strlen(normal_item->item_text) + 1);
2238 if (!normal_item->item_text[0] && !normal_item->item_id)
2239 flags |= MF_SEPARATOR; /* FIXME: do this in InsertMenu? */
2240 AppendMenu( hMenu, flags, normal_item->item_id,
2241 normal_item->item_text );
2243 } while (!(*item & MF_END));
2244 return next_item;
2248 /**********************************************************************
2249 * IsMenu (USER.358)
2251 BOOL IsMenu( HMENU hmenu )
2253 LPPOPUPMENU menu;
2254 if (!(menu = (LPPOPUPMENU) USER_HEAP_LIN_ADDR( hmenu ))) return FALSE;
2255 return (menu->wMagic == MENU_MAGIC);