Release 960314
[wine/multimedia.git] / controls / menu.c
blob00c21290d5d730c8d1968c372e5c99f2e9bafef3
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 "module.h"
23 #include "neexe.h"
24 #include "user.h"
25 #include "win.h"
26 #include "message.h"
27 #include "graphics.h"
28 #include "resource.h"
29 #include "stackframe.h"
30 #include "stddebug.h"
31 #include "debug.h"
33 /* Dimension of the menu bitmaps */
34 static WORD check_bitmap_width = 0, check_bitmap_height = 0;
35 static WORD arrow_bitmap_width = 0, arrow_bitmap_height = 0;
37 /* Flag set by EndMenu() to force an exit from menu tracking */
38 static BOOL fEndMenuCalled = FALSE;
40 /* Space between 2 menu bar items */
41 #define MENU_BAR_ITEMS_SPACE 16
43 /* Minimum width of a tab character */
44 #define MENU_TAB_SPACE 8
46 /* Height of a separator item */
47 #define SEPARATOR_HEIGHT 5
49 /* Values for menu->FocusedItem */
50 /* (other values give the position of the focused item) */
51 #define NO_SELECTED_ITEM 0xffff
52 #define SYSMENU_SELECTED 0xfffe /* Only valid on menu-bars */
54 #define IS_STRING_ITEM(flags) (!((flags) & (MF_BITMAP | MF_OWNERDRAW | \
55 MF_MENUBARBREAK | MF_MENUBREAK | MF_SEPARATOR)))
57 #define SET_OWNERDRAW_DATA(item,data) \
58 ((item)->hText = LOWORD((DWORD)(data)), (item)->xTab = HIWORD((DWORD)(data)))
60 #define GET_OWNERDRAW_DATA(item) \
61 ((DWORD)MAKELONG( (WORD)(item)->hText, (item)->xTab ))
63 extern void NC_DrawSysButton(HWND hwnd, HDC hdc, BOOL down); /* nonclient.c */
65 static HBITMAP hStdCheck = 0;
66 static HBITMAP hStdMnArrow = 0;
69 /***********************************************************************
70 * MENU_Init
72 * Menus initialisation.
74 BOOL MENU_Init()
76 BITMAP bm;
78 /* Load bitmaps */
80 if (!(hStdCheck = LoadBitmap( 0, MAKEINTRESOURCE(OBM_CHECK) )))
81 return FALSE;
82 GetObject( hStdCheck, sizeof(BITMAP), (LPSTR)&bm );
83 check_bitmap_width = bm.bmWidth;
84 check_bitmap_height = bm.bmHeight;
85 if (!(hStdMnArrow = LoadBitmap( 0, MAKEINTRESOURCE(OBM_MNARROW) )))
86 return FALSE;
87 GetObject( hStdMnArrow, sizeof(BITMAP), (LPSTR)&bm );
88 arrow_bitmap_width = bm.bmWidth;
89 arrow_bitmap_height = bm.bmHeight;
91 return TRUE;
95 /***********************************************************************
96 * MENU_HasSysMenu
98 * Check whether the window owning the menu bar has a system menu.
100 static BOOL MENU_HasSysMenu( POPUPMENU *menu )
102 WND *wndPtr;
104 if (menu->wFlags & MF_POPUP) return FALSE;
105 if (!(wndPtr = WIN_FindWndPtr( menu->hWnd ))) return FALSE;
106 return (wndPtr->dwStyle & WS_SYSMENU) != 0;
110 /**********************************************************************
111 * MENU_CopySysMenu
113 static HMENU MENU_CopySysMenu(void)
115 HMENU hMenu;
116 HGLOBAL handle;
117 POPUPMENU *menu;
119 if (!(handle = SYSRES_LoadResource( SYSRES_MENU_SYSMENU ))) return 0;
120 hMenu = LoadMenuIndirect( WIN16_GlobalLock( handle ) );
121 SYSRES_FreeResource( handle );
122 if (!hMenu)
124 dprintf_menu(stddeb,"No SYSMENU\n");
125 return 0;
127 menu = (POPUPMENU*) USER_HEAP_LIN_ADDR(hMenu);
128 menu->wFlags |= MF_SYSMENU | MF_POPUP;
129 dprintf_menu(stddeb,"CopySysMenu hMenu="NPFMT" !\n", hMenu);
130 return hMenu;
134 /***********************************************************************
135 * MENU_IsInSysMenu
137 * Check whether the point (in screen coords) is in the system menu
138 * of the window owning the given menu.
140 static BOOL MENU_IsInSysMenu( POPUPMENU *menu, POINT pt )
142 WND *wndPtr;
144 if (menu->wFlags & MF_POPUP) return FALSE;
145 if (!(wndPtr = WIN_FindWndPtr( menu->hWnd ))) return FALSE;
146 if (!(wndPtr->dwStyle & WS_SYSMENU)) return FALSE;
147 if ((pt.x < wndPtr->rectClient.left) ||
148 (pt.x >= wndPtr->rectClient.left+SYSMETRICS_CXSIZE+SYSMETRICS_CXBORDER))
149 return FALSE;
150 if ((pt.y >= wndPtr->rectClient.top - menu->Height) ||
151 (pt.y < wndPtr->rectClient.top - menu->Height -
152 SYSMETRICS_CYSIZE - SYSMETRICS_CYBORDER)) return FALSE;
153 return TRUE;
157 /***********************************************************************
158 * MENU_FindItem
160 * Find a menu item. Return a pointer on the item, and modifies *hmenu
161 * in case the item was in a sub-menu.
163 static MENUITEM *MENU_FindItem( HMENU *hmenu, UINT *nPos, UINT wFlags )
165 POPUPMENU *menu;
166 MENUITEM *item;
167 int i;
169 if (!(menu = (POPUPMENU *) USER_HEAP_LIN_ADDR(*hmenu))) return NULL;
170 item = (MENUITEM *) USER_HEAP_LIN_ADDR( menu->hItems );
171 if (wFlags & MF_BYPOSITION)
173 if (*nPos >= menu->nItems) return NULL;
174 return &item[*nPos];
176 else
178 for (i = 0; i < menu->nItems; i++, item++)
180 if (item->item_id == *nPos)
182 *nPos = i;
183 return item;
185 else if (item->item_flags & MF_POPUP)
187 HMENU hsubmenu = (HMENU)item->item_id;
188 MENUITEM *subitem = MENU_FindItem( &hsubmenu, nPos, wFlags );
189 if (subitem)
191 *hmenu = hsubmenu;
192 return subitem;
197 return NULL;
201 /***********************************************************************
202 * MENU_FindItemByCoords
204 * Find the item at the specified coordinates (screen coords).
206 static MENUITEM *MENU_FindItemByCoords( POPUPMENU *menu, int x, int y, UINT *pos )
208 MENUITEM *item;
209 WND *wndPtr;
210 int i;
212 if (!(wndPtr = WIN_FindWndPtr( menu->hWnd ))) return NULL;
213 x -= wndPtr->rectWindow.left;
214 y -= wndPtr->rectWindow.top;
215 item = (MENUITEM *) USER_HEAP_LIN_ADDR( menu->hItems );
216 for (i = 0; i < menu->nItems; i++, item++)
218 if ((x >= item->rect.left) && (x < item->rect.right) &&
219 (y >= item->rect.top) && (y < item->rect.bottom))
221 if (pos) *pos = i;
222 return item;
225 return NULL;
229 /***********************************************************************
230 * MENU_FindItemByKey
232 * Find the menu item selected by a key press.
233 * Return item id, -1 if none, -2 if we should close the menu.
235 static UINT MENU_FindItemByKey( HWND hwndOwner, HMENU hmenu, UINT key )
237 POPUPMENU *menu;
238 LPMENUITEM lpitem;
239 int i;
240 LONG menuchar;
242 menu = (POPUPMENU *) USER_HEAP_LIN_ADDR( hmenu );
243 lpitem = (MENUITEM *) USER_HEAP_LIN_ADDR( menu->hItems );
244 key = toupper(key);
245 for (i = 0; i < menu->nItems; i++, lpitem++)
247 if (IS_STRING_ITEM(lpitem->item_flags))
249 char *p = strchr( (char *)USER_HEAP_LIN_ADDR(lpitem->hText), '&' );
250 if (p && (p[1] != '&') && (toupper(p[1]) == key)) return i;
253 #ifdef WINELIB32
254 menuchar = SendMessage( hwndOwner, WM_MENUCHAR,
255 MAKEWPARAM(key,menu->wFlags), (LPARAM)hmenu );
256 #else
257 menuchar = SendMessage( hwndOwner, WM_MENUCHAR, key,
258 MAKELONG( menu->wFlags, hmenu ) );
259 #endif
260 if (HIWORD(menuchar) == 2) return LOWORD(menuchar);
261 if (HIWORD(menuchar) == 1) return -2;
262 return -1;
266 /***********************************************************************
267 * MENU_CalcItemSize
269 * Calculate the size of the menu item and store it in lpitem->rect.
271 static void MENU_CalcItemSize( HDC hdc, LPMENUITEM lpitem, HWND hwndOwner,
272 int orgX, int orgY, BOOL menuBar )
274 DWORD dwSize;
275 char *p;
277 SetRect( &lpitem->rect, orgX, orgY, orgX, orgY );
279 if (lpitem->item_flags & MF_OWNERDRAW)
281 MEASUREITEMSTRUCT mis;
282 mis.CtlType = ODT_MENU;
283 mis.itemID = lpitem->item_id;
284 mis.itemData = GET_OWNERDRAW_DATA(lpitem);
285 mis.itemHeight = 16;
286 mis.itemWidth = 30;
287 SendMessage( hwndOwner, WM_MEASUREITEM, 0, (LPARAM)MAKE_SEGPTR(&mis) );
288 lpitem->rect.bottom += mis.itemHeight;
289 lpitem->rect.right += mis.itemWidth;
290 dprintf_menu( stddeb, "DrawMenuItem: MeasureItem %04x %dx%d!\n",
291 lpitem->item_id, mis.itemWidth, mis.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 if (GetObject( (HBITMAP)lpitem->hText, sizeof(BITMAP), (LPSTR)&bm ))
313 lpitem->rect.right += bm.bmWidth;
314 lpitem->rect.bottom += bm.bmHeight;
316 return;
319 /* If we get here, then it must be a text item */
321 if (IS_STRING_ITEM( lpitem->item_flags ))
323 const char *text = (const char *)USER_HEAP_LIN_ADDR( lpitem->hText );
324 dwSize = GetTextExtent( hdc, text, strlen(text) );
325 lpitem->rect.right += LOWORD(dwSize);
326 lpitem->rect.bottom += MAX( HIWORD(dwSize), SYSMETRICS_CYMENU );
327 lpitem->xTab = 0;
329 if (menuBar) lpitem->rect.right += MENU_BAR_ITEMS_SPACE;
330 else if ((p = strchr( text, '\t' )) != NULL)
332 /* Item contains a tab (only meaningful in popup menus) */
333 lpitem->xTab = check_bitmap_width + MENU_TAB_SPACE +
334 LOWORD( GetTextExtent( hdc, text, (int)(p - text) ));
335 lpitem->rect.right += MENU_TAB_SPACE;
337 else
339 if (strchr( text, '\b' )) lpitem->rect.right += MENU_TAB_SPACE;
340 lpitem->xTab = lpitem->rect.right - check_bitmap_width
341 - arrow_bitmap_width;
347 /***********************************************************************
348 * MENU_PopupMenuCalcSize
350 * Calculate the size of a popup menu.
352 static void MENU_PopupMenuCalcSize( LPPOPUPMENU lppop, HWND hwndOwner )
354 LPMENUITEM items, lpitem;
355 HDC hdc;
356 int start, i;
357 int orgX, orgY, maxX, maxTab, maxTabWidth;
359 lppop->Width = lppop->Height = 0;
360 if (lppop->nItems == 0) return;
361 items = (MENUITEM *)USER_HEAP_LIN_ADDR( lppop->hItems );
362 hdc = GetDC( 0 );
363 maxX = start = 0;
364 while (start < lppop->nItems)
366 lpitem = &items[start];
367 orgX = maxX;
368 orgY = 0;
369 maxTab = maxTabWidth = 0;
371 /* Parse items until column break or end of menu */
372 for (i = start; i < lppop->nItems; i++, lpitem++)
374 if ((i != start) &&
375 (lpitem->item_flags & (MF_MENUBREAK | MF_MENUBARBREAK))) break;
376 MENU_CalcItemSize( hdc, lpitem, hwndOwner, orgX, orgY, FALSE );
377 if (lpitem->item_flags & MF_MENUBARBREAK) orgX++;
378 maxX = MAX( maxX, lpitem->rect.right );
379 orgY = lpitem->rect.bottom;
380 if (IS_STRING_ITEM(lpitem->item_flags) && lpitem->xTab)
382 maxTab = MAX( maxTab, lpitem->xTab );
383 maxTabWidth = MAX(maxTabWidth,lpitem->rect.right-lpitem->xTab);
387 /* Finish the column (set all items to the largest width found) */
388 maxX = MAX( maxX, maxTab + maxTabWidth );
389 for (lpitem = &items[start]; start < i; start++, lpitem++)
391 lpitem->rect.right = maxX;
392 if (IS_STRING_ITEM(lpitem->item_flags) && lpitem->xTab)
393 lpitem->xTab = maxTab;
395 lppop->Height = MAX( lppop->Height, orgY );
398 lppop->Width = maxX;
399 ReleaseDC( 0, hdc );
403 /***********************************************************************
404 * MENU_MenuBarCalcSize
406 * Calculate the size of the menu bar.
408 static void MENU_MenuBarCalcSize( HDC hdc, LPRECT lprect, LPPOPUPMENU lppop,
409 HWND hwndOwner )
411 LPMENUITEM lpitem, items;
412 int start, i, orgX, orgY, maxY, helpPos;
414 if ((lprect == NULL) || (lppop == NULL)) return;
415 if (lppop->nItems == 0) return;
416 dprintf_menu(stddeb,"MENU_MenuBarCalcSize left=%ld top=%ld right=%ld bottom=%ld !\n",
417 (LONG)lprect->left, (LONG)lprect->top, (LONG)lprect->right, (LONG)lprect->bottom);
418 items = (MENUITEM *)USER_HEAP_LIN_ADDR( lppop->hItems );
419 lppop->Width = lprect->right - lprect->left;
420 lppop->Height = 0;
421 maxY = lprect->top;
422 start = 0;
423 helpPos = -1;
424 while (start < lppop->nItems)
426 lpitem = &items[start];
427 orgX = lprect->left;
428 orgY = maxY;
430 /* Parse items until line break or end of menu */
431 for (i = start; i < lppop->nItems; i++, lpitem++)
433 if ((helpPos == -1) && (lpitem->item_flags & MF_HELP)) helpPos = i;
434 if ((i != start) &&
435 (lpitem->item_flags & (MF_MENUBREAK | MF_MENUBARBREAK))) break;
436 MENU_CalcItemSize( hdc, lpitem, hwndOwner, orgX, orgY, TRUE );
437 if (lpitem->rect.right > lprect->right)
439 if (i != start) break;
440 else lpitem->rect.right = lprect->right;
442 maxY = MAX( maxY, lpitem->rect.bottom );
443 orgX = lpitem->rect.right;
446 /* Finish the line (set all items to the largest height found) */
447 while (start < i) items[start++].rect.bottom = maxY;
450 lprect->bottom = maxY;
451 lppop->Height = lprect->bottom - lprect->top;
453 /* Flush right all items between the MF_HELP and the last item */
454 /* (if several lines, only move the last line) */
455 if (helpPos != -1)
457 lpitem = &items[lppop->nItems-1];
458 orgY = lpitem->rect.top;
459 orgX = lprect->right;
460 for (i = lppop->nItems - 1; i >= helpPos; i--, lpitem--)
462 if (lpitem->rect.top != orgY) break; /* Other line */
463 if (lpitem->rect.right >= orgX) break; /* Too far right already */
464 lpitem->rect.left += orgX - lpitem->rect.right;
465 lpitem->rect.right = orgX;
466 orgX = lpitem->rect.left;
472 /***********************************************************************
473 * MENU_DrawMenuItem
475 * Draw a single menu item.
477 static void MENU_DrawMenuItem( HWND hwnd, HDC hdc, LPMENUITEM lpitem,
478 UINT height, BOOL menuBar )
480 RECT rect;
482 if (lpitem->item_flags & MF_OWNERDRAW)
484 DRAWITEMSTRUCT dis;
486 dprintf_menu( stddeb, "DrawMenuItem: Ownerdraw!\n" );
487 dis.CtlType = ODT_MENU;
488 dis.itemID = lpitem->item_id;
489 dis.itemData = GET_OWNERDRAW_DATA(lpitem);
490 dis.itemState = 0;
491 if (lpitem->item_flags & MF_CHECKED) dis.itemState |= ODS_CHECKED;
492 if (lpitem->item_flags & MF_GRAYED) dis.itemState |= ODS_GRAYED;
493 if (lpitem->item_flags & MF_HILITE) dis.itemState |= ODS_SELECTED;
494 dis.itemAction = ODA_DRAWENTIRE | ODA_SELECT | ODA_FOCUS;
495 dis.hwndItem = hwnd;
496 dis.hDC = hdc;
497 dis.rcItem = lpitem->rect;
498 SendMessage( hwnd, WM_DRAWITEM, 0, MAKE_SEGPTR(&dis) );
499 return;
502 if (menuBar && (lpitem->item_flags & MF_SEPARATOR)) return;
503 rect = lpitem->rect;
505 /* Draw the background */
507 if (lpitem->item_flags & MF_HILITE)
508 FillRect( hdc, &rect, sysColorObjects.hbrushHighlight );
509 else FillRect( hdc, &rect, sysColorObjects.hbrushMenu );
510 SetBkMode( hdc, TRANSPARENT );
512 /* Draw the separator bar (if any) */
514 if (!menuBar && (lpitem->item_flags & MF_MENUBARBREAK))
516 SelectObject( hdc, sysColorObjects.hpenWindowFrame );
517 MoveTo( hdc, rect.left, 0 );
518 LineTo( hdc, rect.left, height );
520 if (lpitem->item_flags & MF_SEPARATOR)
522 SelectObject( hdc, sysColorObjects.hpenWindowFrame );
523 MoveTo( hdc, rect.left, rect.top + SEPARATOR_HEIGHT/2 );
524 LineTo( hdc, rect.right, rect.top + SEPARATOR_HEIGHT/2 );
525 return;
528 /* Setup colors */
530 if (lpitem->item_flags & MF_HILITE)
532 if (lpitem->item_flags & MF_GRAYED)
533 SetTextColor( hdc, GetSysColor( COLOR_GRAYTEXT ) );
534 else
535 SetTextColor( hdc, GetSysColor( COLOR_HIGHLIGHTTEXT ) );
536 SetBkColor( hdc, GetSysColor( COLOR_HIGHLIGHT ) );
538 else
540 if (lpitem->item_flags & MF_GRAYED)
541 SetTextColor( hdc, GetSysColor( COLOR_GRAYTEXT ) );
542 else
543 SetTextColor( hdc, GetSysColor( COLOR_MENUTEXT ) );
544 SetBkColor( hdc, GetSysColor( COLOR_MENU ) );
547 if (!menuBar)
549 /* Draw the check mark */
551 if (lpitem->item_flags & MF_CHECKED)
553 GRAPH_DrawBitmap(hdc, lpitem->hCheckBit ? lpitem->hCheckBit :
554 hStdCheck, rect.left,
555 (rect.top+rect.bottom-check_bitmap_height) / 2,
556 0, 0, check_bitmap_width, check_bitmap_height );
558 else if (lpitem->hUnCheckBit != 0) /* Not checked */
560 GRAPH_DrawBitmap(hdc, lpitem->hUnCheckBit, rect.left,
561 (rect.top+rect.bottom-check_bitmap_height) / 2,
562 0, 0, check_bitmap_width, check_bitmap_height );
565 /* Draw the popup-menu arrow */
567 if (lpitem->item_flags & MF_POPUP)
569 GRAPH_DrawBitmap( hdc, hStdMnArrow,
570 rect.right-arrow_bitmap_width-1,
571 (rect.top+rect.bottom-arrow_bitmap_height) / 2,
572 0, 0, arrow_bitmap_width, arrow_bitmap_height );
575 rect.left += check_bitmap_width;
576 rect.right -= arrow_bitmap_width;
579 /* Draw the item text or bitmap */
581 if (lpitem->item_flags & MF_BITMAP)
583 GRAPH_DrawBitmap( hdc, (HBITMAP)lpitem->hText, rect.left, rect.top,
584 0, 0, rect.right-rect.left, rect.bottom-rect.top );
585 return;
587 /* No bitmap - process text if present */
588 else if (IS_STRING_ITEM(lpitem->item_flags))
590 register int i;
591 const char *text = (const char *)USER_HEAP_LIN_ADDR( lpitem->hText );
593 if (menuBar)
595 rect.left += MENU_BAR_ITEMS_SPACE / 2;
596 rect.right -= MENU_BAR_ITEMS_SPACE / 2;
597 i = strlen( text );
599 else
601 for (i = 0; text[i]; i++)
602 if ((text[i] == '\t') || (text[i] == '\b')) break;
605 DrawText( hdc, text, i, &rect, DT_LEFT | DT_VCENTER | DT_SINGLELINE );
607 if (text[i]) /* There's a tab or flush-right char */
609 if (text[i] == '\t')
611 rect.left = lpitem->xTab;
612 DrawText( hdc, text + i + 1, -1, &rect,
613 DT_LEFT | DT_VCENTER | DT_SINGLELINE );
615 else DrawText( hdc, text + i + 1, -1, &rect,
616 DT_RIGHT | DT_VCENTER | DT_SINGLELINE );
622 /***********************************************************************
623 * MENU_DrawPopupMenu
625 * Paint a popup menu.
627 static void MENU_DrawPopupMenu( HWND hwnd, HDC hdc, HMENU hmenu )
629 POPUPMENU *menu;
630 MENUITEM *item;
631 RECT rect;
632 int i;
634 GetClientRect( hwnd, &rect );
635 FillRect( hdc, &rect, sysColorObjects.hbrushMenu );
636 menu = (POPUPMENU *) USER_HEAP_LIN_ADDR( hmenu );
637 if (!menu || !menu->nItems) return;
638 item = (MENUITEM *) USER_HEAP_LIN_ADDR( menu->hItems );
639 for (i = menu->nItems; i > 0; i--, item++)
640 MENU_DrawMenuItem( hwnd, hdc, item, menu->Height, FALSE );
644 /***********************************************************************
645 * MENU_DrawMenuBar
647 * Paint a menu bar. Returns the height of the menu bar.
649 UINT MENU_DrawMenuBar(HDC hDC, LPRECT lprect, HWND hwnd, BOOL suppress_draw)
651 LPPOPUPMENU lppop;
652 LPMENUITEM lpitem;
653 int i;
654 WND *wndPtr = WIN_FindWndPtr( hwnd );
656 lppop = (LPPOPUPMENU) USER_HEAP_LIN_ADDR( (HMENU)wndPtr->wIDmenu );
657 if (lppop == NULL || lprect == NULL) return SYSMETRICS_CYMENU;
658 dprintf_menu(stddeb,"MENU_DrawMenuBar("NPFMT", %p, %p); !\n",
659 hDC, lprect, lppop);
660 if (lppop->Height == 0) MENU_MenuBarCalcSize(hDC, lprect, lppop, hwnd);
661 lprect->bottom = lprect->top + lppop->Height;
662 if (suppress_draw) return lppop->Height;
664 FillRect(hDC, lprect, sysColorObjects.hbrushMenu );
665 SelectObject( hDC, sysColorObjects.hpenWindowFrame );
666 MoveTo( hDC, lprect->left, lprect->bottom );
667 LineTo( hDC, lprect->right, lprect->bottom );
669 if (lppop->nItems == 0) return SYSMETRICS_CYMENU;
670 lpitem = (MENUITEM *) USER_HEAP_LIN_ADDR( lppop->hItems );
671 for (i = 0; i < lppop->nItems; i++, lpitem++)
673 MENU_DrawMenuItem( hwnd, hDC, lpitem, lppop->Height, TRUE );
675 return lppop->Height;
679 /***********************************************************************
680 * MENU_ShowPopup
682 * Display a popup menu.
684 static BOOL MENU_ShowPopup(HWND hwndOwner, HMENU hmenu, UINT id, int x, int y)
686 POPUPMENU *menu;
688 if (!(menu = (POPUPMENU *) USER_HEAP_LIN_ADDR( hmenu ))) return FALSE;
689 if (menu->FocusedItem != NO_SELECTED_ITEM)
691 MENUITEM *item = (MENUITEM *) USER_HEAP_LIN_ADDR( menu->hItems );
692 item[menu->FocusedItem].item_flags &= ~(MF_HILITE | MF_MOUSESELECT);
693 menu->FocusedItem = NO_SELECTED_ITEM;
695 SendMessage( hwndOwner, WM_INITMENUPOPUP, (WPARAM)hmenu,
696 MAKELONG( id, (menu->wFlags & MF_SYSMENU) ? 1 : 0 ));
697 MENU_PopupMenuCalcSize( menu, hwndOwner );
698 if (!menu->hWnd)
700 WND *wndPtr = WIN_FindWndPtr( hwndOwner );
701 if (!wndPtr) return FALSE;
702 menu->hWnd = CreateWindow( POPUPMENU_CLASS_ATOM, (SEGPTR)0,
703 WS_POPUP | WS_BORDER, x, y,
704 menu->Width + 2*SYSMETRICS_CXBORDER,
705 menu->Height + 2*SYSMETRICS_CYBORDER,
706 0, 0, wndPtr->hInstance, (SEGPTR)hmenu );
707 if (!menu->hWnd) return FALSE;
709 else SetWindowPos( menu->hWnd, 0, x, y,
710 menu->Width + 2*SYSMETRICS_CXBORDER,
711 menu->Height + 2*SYSMETRICS_CYBORDER,
712 SWP_NOACTIVATE | SWP_NOZORDER );
714 /* Display the window */
716 SetWindowPos( menu->hWnd, HWND_TOP, 0, 0, 0, 0,
717 SWP_SHOWWINDOW | SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE );
718 UpdateWindow( menu->hWnd );
719 return TRUE;
723 /***********************************************************************
724 * MENU_SelectItem
726 static void MENU_SelectItem( HWND hwndOwner, HMENU hmenu, UINT wIndex )
728 MENUITEM *items;
729 LPPOPUPMENU lppop;
730 HDC hdc;
732 lppop = (POPUPMENU *) USER_HEAP_LIN_ADDR( hmenu );
733 if (!lppop->nItems) return;
734 items = (MENUITEM *) USER_HEAP_LIN_ADDR( lppop->hItems );
735 if ((wIndex != NO_SELECTED_ITEM) &&
736 (wIndex != SYSMENU_SELECTED) &&
737 (items[wIndex].item_flags & MF_SEPARATOR))
738 wIndex = NO_SELECTED_ITEM;
739 if (lppop->FocusedItem == wIndex) return;
740 if (lppop->wFlags & MF_POPUP) hdc = GetDC( lppop->hWnd );
741 else hdc = GetDCEx( lppop->hWnd, 0, DCX_CACHE | DCX_WINDOW);
743 /* Clear previous highlighted item */
744 if (lppop->FocusedItem != NO_SELECTED_ITEM)
746 if (lppop->FocusedItem == SYSMENU_SELECTED)
747 NC_DrawSysButton( lppop->hWnd, hdc, FALSE );
748 else
750 items[lppop->FocusedItem].item_flags &=~(MF_HILITE|MF_MOUSESELECT);
751 MENU_DrawMenuItem( lppop->hWnd, hdc, &items[lppop->FocusedItem], lppop->Height,
752 !(lppop->wFlags & MF_POPUP) );
756 /* Highlight new item (if any) */
757 lppop->FocusedItem = wIndex;
758 if (lppop->FocusedItem != NO_SELECTED_ITEM)
760 if (lppop->FocusedItem == SYSMENU_SELECTED)
762 NC_DrawSysButton( lppop->hWnd, hdc, TRUE );
763 #ifdef WINELIB32
764 /* FIX: LostInfo */
765 SendMessage( hwndOwner, WM_MENUSELECT,
766 MAKEWPARAM( (DWORD)GetSystemMenu( lppop->hWnd, FALSE ),
767 lppop->wFlags | MF_MOUSESELECT ),
768 (LPARAM)hmenu );
769 #else
770 SendMessage( hwndOwner, WM_MENUSELECT,
771 GetSystemMenu( lppop->hWnd, FALSE ),
772 MAKELONG( lppop->wFlags | MF_MOUSESELECT, hmenu ) );
773 #endif
775 else
777 items[lppop->FocusedItem].item_flags |= MF_HILITE;
778 MENU_DrawMenuItem( lppop->hWnd, hdc, &items[lppop->FocusedItem], lppop->Height,
779 !(lppop->wFlags & MF_POPUP) );
780 #ifdef WINELIB32
781 SendMessage( hwndOwner, WM_MENUSELECT,
782 MAKEWPARAM( items[lppop->FocusedItem].item_id,
783 items[lppop->FocusedItem].item_flags |
784 MF_MOUSESELECT ),
785 (LPARAM) hmenu );
786 #else
787 SendMessage( hwndOwner, WM_MENUSELECT,
788 items[lppop->FocusedItem].item_id,
789 MAKELONG( items[lppop->FocusedItem].item_flags | MF_MOUSESELECT, hmenu));
790 #endif
793 #ifdef WINELIB32
794 /* FIX: Lost Info */
795 else SendMessage( hwndOwner, WM_MENUSELECT,
796 MAKEWPARAM( (DWORD)hmenu, lppop->wFlags | MF_MOUSESELECT),
797 (LPARAM)hmenu );
798 #else
799 else SendMessage( hwndOwner, WM_MENUSELECT, hmenu,
800 MAKELONG( lppop->wFlags | MF_MOUSESELECT, hmenu ) );
801 #endif
803 ReleaseDC( lppop->hWnd, hdc );
807 /***********************************************************************
808 * MENU_SelectNextItem
810 static void MENU_SelectNextItem( HWND hwndOwner, HMENU hmenu )
812 int i;
813 MENUITEM *items;
814 POPUPMENU *menu;
816 menu = (POPUPMENU *) USER_HEAP_LIN_ADDR( hmenu );
817 if (!menu->nItems) return;
818 items = (MENUITEM *) USER_HEAP_LIN_ADDR( menu->hItems );
819 if ((menu->FocusedItem != NO_SELECTED_ITEM) &&
820 (menu->FocusedItem != SYSMENU_SELECTED))
822 for (i = menu->FocusedItem+1; i < menu->nItems; i++)
824 if (!(items[i].item_flags & MF_SEPARATOR))
826 MENU_SelectItem( hwndOwner, hmenu, i );
827 return;
830 if (MENU_HasSysMenu( menu ))
832 MENU_SelectItem( hwndOwner, hmenu, SYSMENU_SELECTED );
833 return;
836 for (i = 0; i < menu->nItems; i++)
838 if (!(items[i].item_flags & MF_SEPARATOR))
840 MENU_SelectItem( hwndOwner, hmenu, i );
841 return;
844 if (MENU_HasSysMenu( menu ))
845 MENU_SelectItem( hwndOwner, hmenu, SYSMENU_SELECTED );
849 /***********************************************************************
850 * MENU_SelectPrevItem
852 static void MENU_SelectPrevItem( HWND hwndOwner, HMENU hmenu )
854 int i;
855 MENUITEM *items;
856 POPUPMENU *menu;
858 menu = (POPUPMENU *) USER_HEAP_LIN_ADDR( hmenu );
859 if (!menu->nItems) return;
860 items = (MENUITEM *) USER_HEAP_LIN_ADDR( menu->hItems );
861 if ((menu->FocusedItem != NO_SELECTED_ITEM) &&
862 (menu->FocusedItem != SYSMENU_SELECTED))
864 for (i = menu->FocusedItem - 1; i >= 0; i--)
866 if (!(items[i].item_flags & MF_SEPARATOR))
868 MENU_SelectItem( hwndOwner, hmenu, i );
869 return;
872 if (MENU_HasSysMenu( menu ))
874 MENU_SelectItem( hwndOwner, hmenu, SYSMENU_SELECTED );
875 return;
878 for (i = menu->nItems - 1; i > 0; i--)
880 if (!(items[i].item_flags & MF_SEPARATOR))
882 MENU_SelectItem( hwndOwner, hmenu, i );
883 return;
886 if (MENU_HasSysMenu( menu ))
887 MENU_SelectItem( hwndOwner, hmenu, SYSMENU_SELECTED );
891 /**********************************************************************
892 * MENU_SetItemData
894 * Set an item flags, id and text ptr.
896 static BOOL MENU_SetItemData( MENUITEM *item, UINT flags, UINT id, SEGPTR data)
898 HANDLE hPrevText = IS_STRING_ITEM(item->item_flags) ? item->hText : 0;
900 if (IS_STRING_ITEM(flags))
902 if (!data)
904 flags |= MF_SEPARATOR;
905 item->hText = 0;
907 else
909 char *str = (char *)PTR_SEG_TO_LIN(data);
910 HANDLE hText;
912 /* Item beginning with a backspace is a help item */
913 if (*str == '\b')
915 flags |= MF_HELP;
916 str++;
918 if (!(hText = USER_HEAP_ALLOC( strlen(str)+1 ))) return FALSE;
919 item->hText = hText;
920 strcpy( (char *)USER_HEAP_LIN_ADDR( hText ), str );
923 else if (flags & MF_BITMAP) item->hText = (HANDLE)(DWORD)data;
924 else if (flags & MF_OWNERDRAW) SET_OWNERDRAW_DATA( item, data );
925 else item->hText = 0;
927 item->item_flags = flags & ~(MF_HILITE | MF_MOUSESELECT);
928 item->item_id = id;
929 SetRectEmpty( &item->rect );
930 if (hPrevText) USER_HEAP_FREE( hPrevText );
931 return TRUE;
935 /**********************************************************************
936 * MENU_InsertItem
938 * Insert a new item into a menu.
940 static MENUITEM *MENU_InsertItem( HMENU hMenu, UINT pos, UINT flags )
942 HANDLE hNewItems;
943 MENUITEM *newItems;
944 POPUPMENU *menu;
946 if (!(menu = (POPUPMENU *)USER_HEAP_LIN_ADDR(hMenu)))
948 dprintf_menu( stddeb, "MENU_InsertItem: "NPFMT" not a menu handle\n",
949 hMenu );
950 return NULL;
953 /* Find where to insert new item */
955 if ((flags & MF_BYPOSITION) &&
956 ((pos == (UINT)-1) || (pos == menu->nItems)))
958 /* Special case: append to menu */
959 /* Some programs specify the menu length to do that */
960 pos = menu->nItems;
962 else
964 if (!MENU_FindItem( &hMenu, &pos, flags ))
966 dprintf_menu( stddeb, "MENU_InsertItem: item %x not found\n",
967 pos );
968 return NULL;
970 if (!(menu = (LPPOPUPMENU) USER_HEAP_LIN_ADDR(hMenu)))
972 dprintf_menu(stddeb,"MENU_InsertItem: "NPFMT" not a menu handle\n",
973 hMenu);
974 return NULL;
978 /* Create new items array */
980 hNewItems = USER_HEAP_ALLOC( sizeof(MENUITEM) * (menu->nItems+1) );
981 if (!hNewItems)
983 dprintf_menu( stddeb, "MENU_InsertMenu: allocation failed\n" );
984 return NULL;
986 newItems = (MENUITEM *) USER_HEAP_LIN_ADDR( hNewItems );
987 if (menu->nItems > 0)
989 /* Copy the old array into the new */
990 MENUITEM *oldItems = (MENUITEM *) USER_HEAP_LIN_ADDR( menu->hItems );
991 if (pos > 0) memcpy( newItems, oldItems, pos * sizeof(MENUITEM) );
992 if (pos < menu->nItems) memcpy( &newItems[pos+1], &oldItems[pos],
993 (menu->nItems-pos)*sizeof(MENUITEM) );
995 USER_HEAP_FREE( menu->hItems );
997 menu->hItems = hNewItems;
998 menu->nItems++;
999 memset( &newItems[pos], 0, sizeof(*newItems) );
1000 return &newItems[pos];
1004 /**********************************************************************
1005 * MENU_ParseResource
1007 * Parse a menu resource and add items to the menu.
1008 * Return a pointer to the end of the resource.
1010 static SEGPTR MENU_ParseResource( SEGPTR res, HMENU hMenu )
1012 WORD flags, id = 0;
1013 SEGPTR data;
1017 flags = *(WORD *)PTR_SEG_TO_LIN( res );
1018 res += sizeof(WORD);
1019 if (!(flags & MF_POPUP))
1021 id = *(WORD *)PTR_SEG_TO_LIN( res );
1022 res += sizeof(WORD);
1024 data = res;
1025 res += strlen( (char *)PTR_SEG_TO_LIN(data) ) + 1;
1026 if (!IS_STRING_ITEM(flags))
1027 fprintf( stderr, "MENU_ParseResource: not a string item %04x\n",
1028 flags );
1029 if (flags & MF_POPUP)
1031 HMENU hSubMenu = CreatePopupMenu();
1032 if (!hSubMenu) return (SEGPTR)0;
1033 if (!(res = MENU_ParseResource( res, hSubMenu ))) return (SEGPTR)0;
1034 AppendMenu( hMenu, flags, (UINT)hSubMenu, data );
1036 else
1038 if (!*(char *)PTR_SEG_TO_LIN(data)) data = 0;
1039 AppendMenu( hMenu, flags, id, data );
1041 } while (!(flags & MF_END));
1042 return res;
1046 /***********************************************************************
1047 * MENU_GetSubPopup
1049 * Return the handle of the selected sub-popup menu (if any).
1051 static HMENU MENU_GetSubPopup( HMENU hmenu )
1053 POPUPMENU *menu;
1054 MENUITEM *item;
1056 menu = (POPUPMENU *) USER_HEAP_LIN_ADDR( hmenu );
1057 if (menu->FocusedItem == NO_SELECTED_ITEM) return 0;
1058 else if (menu->FocusedItem == SYSMENU_SELECTED)
1059 return GetSystemMenu( menu->hWnd, FALSE );
1061 item = ((MENUITEM *)USER_HEAP_LIN_ADDR(menu->hItems)) + menu->FocusedItem;
1062 if (!(item->item_flags & MF_POPUP) || !(item->item_flags & MF_MOUSESELECT))
1063 return 0;
1064 return (HMENU)item->item_id;
1068 /***********************************************************************
1069 * MENU_HideSubPopups
1071 * Hide the sub-popup menus of this menu.
1073 static void MENU_HideSubPopups( HWND hwndOwner, HMENU hmenu )
1075 MENUITEM *item;
1076 POPUPMENU *menu, *submenu;
1077 HMENU hsubmenu;
1079 if (!(menu = (POPUPMENU *) USER_HEAP_LIN_ADDR( hmenu ))) return;
1080 if (menu->FocusedItem == NO_SELECTED_ITEM) return;
1081 if (menu->FocusedItem == SYSMENU_SELECTED)
1083 hsubmenu = GetSystemMenu( menu->hWnd, FALSE );
1085 else
1087 item = ((MENUITEM *)USER_HEAP_LIN_ADDR(menu->hItems)) + menu->FocusedItem;
1088 if (!(item->item_flags & MF_POPUP) ||
1089 !(item->item_flags & MF_MOUSESELECT)) return;
1090 item->item_flags &= ~MF_MOUSESELECT;
1091 hsubmenu = (HMENU)item->item_id;
1093 submenu = (POPUPMENU *) USER_HEAP_LIN_ADDR( hsubmenu );
1094 MENU_HideSubPopups( hwndOwner, hsubmenu );
1095 if (submenu->hWnd) ShowWindow( submenu->hWnd, SW_HIDE );
1096 MENU_SelectItem( hwndOwner, hsubmenu, NO_SELECTED_ITEM );
1100 /***********************************************************************
1101 * MENU_ShowSubPopup
1103 * Display the sub-menu of the selected item of this menu.
1104 * Return the handle of the submenu, or hmenu if no submenu to display.
1106 static HMENU MENU_ShowSubPopup( HWND hwndOwner, HMENU hmenu, BOOL selectFirst )
1108 POPUPMENU *menu;
1109 MENUITEM *item;
1110 WND *wndPtr;
1112 if (!(menu = (POPUPMENU *) USER_HEAP_LIN_ADDR( hmenu ))) return hmenu;
1113 if (!(wndPtr = WIN_FindWndPtr( menu->hWnd ))) return hmenu;
1114 if (menu->FocusedItem == NO_SELECTED_ITEM) return hmenu;
1115 if (menu->FocusedItem == SYSMENU_SELECTED)
1117 MENU_ShowPopup(hwndOwner, wndPtr->hSysMenu, 0, wndPtr->rectClient.left,
1118 wndPtr->rectClient.top - menu->Height - 2*SYSMETRICS_CYBORDER);
1119 if (selectFirst) MENU_SelectNextItem( hwndOwner, wndPtr->hSysMenu );
1120 return wndPtr->hSysMenu;
1122 item = ((MENUITEM *)USER_HEAP_LIN_ADDR(menu->hItems)) + menu->FocusedItem;
1123 if (!(item->item_flags & MF_POPUP) ||
1124 (item->item_flags & (MF_GRAYED | MF_DISABLED))) return hmenu;
1125 item->item_flags |= MF_MOUSESELECT;
1126 if (menu->wFlags & MF_POPUP)
1128 MENU_ShowPopup( hwndOwner, (HMENU)item->item_id, menu->FocusedItem,
1129 wndPtr->rectWindow.left + item->rect.right-arrow_bitmap_width,
1130 wndPtr->rectWindow.top + item->rect.top );
1132 else
1134 MENU_ShowPopup( hwndOwner, (HMENU)item->item_id, menu->FocusedItem,
1135 wndPtr->rectWindow.left + item->rect.left,
1136 wndPtr->rectWindow.top + item->rect.bottom );
1138 if (selectFirst) MENU_SelectNextItem( hwndOwner, (HMENU)item->item_id );
1139 return (HMENU)item->item_id;
1143 /***********************************************************************
1144 * MENU_FindMenuByCoords
1146 * Find the menu containing a given point (in screen coords).
1148 static HMENU MENU_FindMenuByCoords( HMENU hmenu, POINT pt )
1150 POPUPMENU *menu;
1151 HWND hwnd;
1153 if (!(hwnd = WindowFromPoint( pt ))) return 0;
1154 while (hmenu)
1156 menu = (POPUPMENU *) USER_HEAP_LIN_ADDR( hmenu );
1157 if (menu->hWnd == hwnd)
1159 if (!(menu->wFlags & MF_POPUP))
1161 /* Make sure it's in the menu bar (or in system menu) */
1162 WND *wndPtr = WIN_FindWndPtr( menu->hWnd );
1163 if ((pt.x < wndPtr->rectClient.left) ||
1164 (pt.x >= wndPtr->rectClient.right) ||
1165 (pt.y >= wndPtr->rectClient.top)) return 0;
1166 if (pt.y < wndPtr->rectClient.top - menu->Height)
1168 if (!MENU_IsInSysMenu( menu, pt )) return 0;
1170 /* else it's in the menu bar */
1172 return hmenu;
1174 hmenu = MENU_GetSubPopup( hmenu );
1176 return 0;
1180 /***********************************************************************
1181 * MENU_ExecFocusedItem
1183 * Execute a menu item (for instance when user pressed Enter).
1184 * Return TRUE if we can go on with menu tracking.
1186 static BOOL MENU_ExecFocusedItem( HWND hwndOwner, HMENU hmenu,
1187 HMENU *hmenuCurrent )
1189 MENUITEM *item;
1190 POPUPMENU *menu = (POPUPMENU *) USER_HEAP_LIN_ADDR( hmenu );
1191 if (!menu || !menu->nItems || (menu->FocusedItem == NO_SELECTED_ITEM) ||
1192 (menu->FocusedItem == SYSMENU_SELECTED)) return TRUE;
1193 item = ((MENUITEM *)USER_HEAP_LIN_ADDR(menu->hItems)) + menu->FocusedItem;
1194 if (!(item->item_flags & MF_POPUP))
1196 if (!(item->item_flags & (MF_GRAYED | MF_DISABLED)))
1198 PostMessage( hwndOwner, (menu->wFlags & MF_SYSMENU) ?
1199 WM_SYSCOMMAND : WM_COMMAND, item->item_id, 0 );
1200 return FALSE;
1202 else return TRUE;
1204 else
1206 *hmenuCurrent = MENU_ShowSubPopup( hwndOwner, hmenu, TRUE );
1207 return TRUE;
1212 /***********************************************************************
1213 * MENU_ButtonDown
1215 * Handle a button-down event in a menu. Point is in screen coords.
1216 * hmenuCurrent is the top-most visible popup.
1217 * Return TRUE if we can go on with menu tracking.
1219 static BOOL MENU_ButtonDown( HWND hwndOwner, HMENU hmenu, HMENU *hmenuCurrent,
1220 POINT pt )
1222 POPUPMENU *menu;
1223 MENUITEM *item;
1224 UINT id;
1226 if (!hmenu) return FALSE; /* Outside all menus */
1227 menu = (POPUPMENU *) USER_HEAP_LIN_ADDR( hmenu );
1228 item = MENU_FindItemByCoords( menu, pt.x, pt.y, &id );
1229 if (!item) /* Maybe in system menu */
1231 if (!MENU_IsInSysMenu( menu, pt )) return FALSE;
1232 id = SYSMENU_SELECTED;
1235 if (menu->FocusedItem == id)
1237 if (id == SYSMENU_SELECTED) return FALSE;
1238 if (item->item_flags & MF_POPUP)
1240 if (item->item_flags & MF_MOUSESELECT)
1242 if (menu->wFlags & MF_POPUP)
1244 MENU_HideSubPopups( hwndOwner, hmenu );
1245 *hmenuCurrent = hmenu;
1247 else return FALSE;
1249 else *hmenuCurrent = MENU_ShowSubPopup( hwndOwner, hmenu, FALSE );
1252 else
1254 MENU_HideSubPopups( hwndOwner, hmenu );
1255 MENU_SelectItem( hwndOwner, hmenu, id );
1256 *hmenuCurrent = MENU_ShowSubPopup( hwndOwner, hmenu, FALSE );
1258 return TRUE;
1262 /***********************************************************************
1263 * MENU_ButtonUp
1265 * Handle a button-up event in a menu. Point is in screen coords.
1266 * hmenuCurrent is the top-most visible popup.
1267 * Return TRUE if we can go on with menu tracking.
1269 static BOOL MENU_ButtonUp( HWND hwndOwner, HMENU hmenu, HMENU *hmenuCurrent,
1270 POINT pt )
1272 POPUPMENU *menu;
1273 MENUITEM *item;
1274 HMENU hsubmenu = 0;
1275 UINT id;
1277 if (!hmenu) return FALSE; /* Outside all menus */
1278 menu = (POPUPMENU *) USER_HEAP_LIN_ADDR( hmenu );
1279 item = MENU_FindItemByCoords( menu, pt.x, pt.y, &id );
1280 if (!item) /* Maybe in system menu */
1282 if (!MENU_IsInSysMenu( menu, pt )) return FALSE;
1283 id = SYSMENU_SELECTED;
1284 hsubmenu = GetSystemMenu( menu->hWnd, FALSE );
1287 if (menu->FocusedItem != id) return FALSE;
1289 if (id != SYSMENU_SELECTED)
1291 if (!(item->item_flags & MF_POPUP))
1293 return MENU_ExecFocusedItem( hwndOwner, hmenu, hmenuCurrent );
1295 hsubmenu = (HMENU)item->item_id;
1297 /* Select first item of sub-popup */
1298 MENU_SelectItem( hwndOwner, hsubmenu, NO_SELECTED_ITEM );
1299 MENU_SelectNextItem( hwndOwner, hsubmenu );
1300 return TRUE;
1304 /***********************************************************************
1305 * MENU_MouseMove
1307 * Handle a motion event in a menu. Point is in screen coords.
1308 * hmenuCurrent is the top-most visible popup.
1309 * Return TRUE if we can go on with menu tracking.
1311 static BOOL MENU_MouseMove( HWND hwndOwner, HMENU hmenu, HMENU *hmenuCurrent,
1312 POINT pt )
1314 MENUITEM *item;
1315 POPUPMENU *menu = (POPUPMENU *) USER_HEAP_LIN_ADDR( hmenu );
1316 UINT id = NO_SELECTED_ITEM;
1318 if (hmenu)
1320 item = MENU_FindItemByCoords( menu, pt.x, pt.y, &id );
1321 if (!item) /* Maybe in system menu */
1323 if (!MENU_IsInSysMenu( menu, pt ))
1324 id = NO_SELECTED_ITEM; /* Outside all items */
1325 else id = SYSMENU_SELECTED;
1328 if (id == NO_SELECTED_ITEM)
1330 MENU_SelectItem( hwndOwner, *hmenuCurrent, NO_SELECTED_ITEM );
1332 else if (menu->FocusedItem != id)
1334 MENU_HideSubPopups( hwndOwner, hmenu );
1335 MENU_SelectItem( hwndOwner, hmenu, id );
1336 *hmenuCurrent = MENU_ShowSubPopup( hwndOwner, hmenu, FALSE );
1338 return TRUE;
1342 /***********************************************************************
1343 * MENU_KeyLeft
1345 * Handle a VK_LEFT key event in a menu.
1346 * hmenuCurrent is the top-most visible popup.
1348 static void MENU_KeyLeft( HWND hwndOwner, HMENU hmenu, HMENU *hmenuCurrent )
1350 POPUPMENU *menu;
1351 HMENU hmenutmp, hmenuprev;
1353 menu = (POPUPMENU *) USER_HEAP_LIN_ADDR( hmenu );
1354 hmenuprev = hmenutmp = hmenu;
1355 while (hmenutmp != *hmenuCurrent)
1357 hmenutmp = MENU_GetSubPopup( hmenuprev );
1358 if (hmenutmp != *hmenuCurrent) hmenuprev = hmenutmp;
1360 MENU_HideSubPopups( hwndOwner, hmenuprev );
1362 if ((hmenuprev == hmenu) && !(menu->wFlags & MF_POPUP))
1364 /* Select previous item on the menu bar */
1365 MENU_SelectPrevItem( hwndOwner, hmenu );
1366 if (*hmenuCurrent != hmenu)
1368 /* A popup menu was displayed -> display the next one */
1369 *hmenuCurrent = MENU_ShowSubPopup( hwndOwner, hmenu, TRUE );
1372 else *hmenuCurrent = hmenuprev;
1376 /***********************************************************************
1377 * MENU_KeyRight
1379 * Handle a VK_RIGHT key event in a menu.
1380 * hmenuCurrent is the top-most visible popup.
1382 static void MENU_KeyRight( HWND hwndOwner, HMENU hmenu, HMENU *hmenuCurrent )
1384 POPUPMENU *menu;
1385 HMENU hmenutmp;
1387 menu = (POPUPMENU *) USER_HEAP_LIN_ADDR( hmenu );
1389 if ((menu->wFlags & MF_POPUP) || (*hmenuCurrent != hmenu))
1391 /* If already displaying a popup, try to display sub-popup */
1392 hmenutmp = MENU_ShowSubPopup( hwndOwner, *hmenuCurrent, TRUE );
1393 if (hmenutmp != *hmenuCurrent) /* Sub-popup displayed */
1395 *hmenuCurrent = hmenutmp;
1396 return;
1400 /* If on menu-bar, go to next item */
1401 if (!(menu->wFlags & MF_POPUP))
1403 MENU_HideSubPopups( hwndOwner, hmenu );
1404 MENU_SelectNextItem( hwndOwner, hmenu );
1405 if (*hmenuCurrent != hmenu)
1407 /* A popup menu was displayed -> display the next one */
1408 *hmenuCurrent = MENU_ShowSubPopup( hwndOwner, hmenu, TRUE );
1411 else if (*hmenuCurrent != hmenu) /* Hide last level popup */
1413 HMENU hmenuprev;
1414 hmenuprev = hmenutmp = hmenu;
1415 while (hmenutmp != *hmenuCurrent)
1417 hmenutmp = MENU_GetSubPopup( hmenuprev );
1418 if (hmenutmp != *hmenuCurrent) hmenuprev = hmenutmp;
1420 MENU_HideSubPopups( hwndOwner, hmenuprev );
1421 *hmenuCurrent = hmenuprev;
1426 /***********************************************************************
1427 * MENU_TrackMenu
1429 * Menu tracking code.
1430 * If 'x' and 'y' are not 0, we simulate a button-down event at (x,y)
1431 * before beginning tracking. This is to help menu-bar tracking.
1433 static BOOL MENU_TrackMenu( HMENU hmenu, UINT wFlags, int x, int y,
1434 HWND hwnd, LPRECT lprect )
1436 MSG *msg;
1437 HLOCAL hMsg;
1438 POPUPMENU *menu;
1439 HMENU hmenuCurrent = hmenu;
1440 BOOL fClosed = FALSE, fRemove;
1441 UINT pos;
1443 fEndMenuCalled = FALSE;
1444 if (!(menu = (POPUPMENU *) USER_HEAP_LIN_ADDR( hmenu ))) return FALSE;
1445 if (x && y)
1447 POINT pt = { x, y };
1448 MENU_ButtonDown( hwnd, hmenu, &hmenuCurrent, pt );
1450 SetCapture( hwnd );
1451 hMsg = USER_HEAP_ALLOC( sizeof(MSG) );
1452 msg = (MSG *)USER_HEAP_LIN_ADDR( hMsg );
1453 while (!fClosed)
1455 if (!MSG_InternalGetMessage( (SEGPTR)USER_HEAP_SEG_ADDR(hMsg), 0,
1456 hwnd, MSGF_MENU, 0, TRUE ))
1457 break;
1459 fRemove = FALSE;
1460 if ((msg->message >= WM_MOUSEFIRST) && (msg->message <= WM_MOUSELAST))
1462 /* Find the sub-popup for this mouse event (if any) */
1463 HMENU hsubmenu = MENU_FindMenuByCoords( hmenu, msg->pt );
1465 switch(msg->message)
1467 case WM_RBUTTONDOWN:
1468 case WM_NCRBUTTONDOWN:
1469 if (!(wFlags & TPM_RIGHTBUTTON)) break;
1470 /* fall through */
1471 case WM_LBUTTONDOWN:
1472 case WM_NCLBUTTONDOWN:
1473 fClosed = !MENU_ButtonDown( hwnd, hsubmenu,
1474 &hmenuCurrent, msg->pt );
1475 break;
1477 case WM_RBUTTONUP:
1478 case WM_NCRBUTTONUP:
1479 if (!(wFlags & TPM_RIGHTBUTTON)) break;
1480 /* fall through */
1481 case WM_LBUTTONUP:
1482 case WM_NCLBUTTONUP:
1483 /* If outside all menus but inside lprect, ignore it */
1484 if (!hsubmenu && lprect && PtInRect( lprect, msg->pt )) break;
1485 fClosed = !MENU_ButtonUp( hwnd, hsubmenu,
1486 &hmenuCurrent, msg->pt );
1487 fRemove = TRUE; /* Remove event even if outside menu */
1488 break;
1490 case WM_MOUSEMOVE:
1491 case WM_NCMOUSEMOVE:
1492 if ((msg->wParam & MK_LBUTTON) ||
1493 ((wFlags & TPM_RIGHTBUTTON) && (msg->wParam & MK_RBUTTON)))
1495 fClosed = !MENU_MouseMove( hwnd, hsubmenu,
1496 &hmenuCurrent, msg->pt );
1498 break;
1501 else if ((msg->message >= WM_KEYFIRST) && (msg->message <= WM_KEYLAST))
1503 fRemove = TRUE; /* Keyboard messages are always removed */
1504 switch(msg->message)
1506 case WM_KEYDOWN:
1507 switch(msg->wParam)
1509 case VK_HOME:
1510 MENU_SelectItem( hwnd, hmenuCurrent, NO_SELECTED_ITEM );
1511 MENU_SelectNextItem( hwnd, hmenuCurrent );
1512 break;
1514 case VK_END:
1515 MENU_SelectItem( hwnd, hmenuCurrent, NO_SELECTED_ITEM );
1516 MENU_SelectPrevItem( hwnd, hmenuCurrent );
1517 break;
1519 case VK_UP:
1520 MENU_SelectPrevItem( hwnd, hmenuCurrent );
1521 break;
1523 case VK_DOWN:
1524 /* If on menu bar, pull-down the menu */
1525 if (!(menu->wFlags & MF_POPUP) && (hmenuCurrent == hmenu))
1526 hmenuCurrent = MENU_ShowSubPopup( hwnd, hmenu, TRUE );
1527 else
1528 MENU_SelectNextItem( hwnd, hmenuCurrent );
1529 break;
1531 case VK_LEFT:
1532 MENU_KeyLeft( hwnd, hmenu, &hmenuCurrent );
1533 break;
1535 case VK_RIGHT:
1536 MENU_KeyRight( hwnd, hmenu, &hmenuCurrent );
1537 break;
1539 case VK_SPACE:
1540 case VK_RETURN:
1541 fClosed = !MENU_ExecFocusedItem( hwnd, hmenuCurrent,
1542 &hmenuCurrent );
1543 break;
1545 case VK_ESCAPE:
1546 fClosed = TRUE;
1547 break;
1549 default:
1550 break;
1552 break; /* WM_KEYDOWN */
1554 case WM_SYSKEYDOWN:
1555 switch(msg->wParam)
1557 case VK_MENU:
1558 fClosed = TRUE;
1559 break;
1562 break; /* WM_SYSKEYDOWN */
1564 case WM_CHAR:
1566 /* Hack to avoid control chars. */
1567 /* We will find a better way real soon... */
1568 if ((msg->wParam <= 32) || (msg->wParam >= 127)) break;
1569 pos = MENU_FindItemByKey( hwnd, hmenuCurrent, msg->wParam );
1570 if (pos == (UINT)-2) fClosed = TRUE;
1571 else if (pos == (UINT)-1) MessageBeep(0);
1572 else
1574 MENU_SelectItem( hwnd, hmenuCurrent, pos );
1575 fClosed = !MENU_ExecFocusedItem( hwnd, hmenuCurrent,
1576 &hmenuCurrent );
1580 break; /* WM_CHAR */
1581 } /* switch(msg->message) */
1583 else
1585 DispatchMessage( msg );
1587 if (fEndMenuCalled) fClosed = TRUE;
1588 if (!fClosed) fRemove = TRUE;
1590 if (fRemove) /* Remove the message from the queue */
1591 PeekMessage( msg, 0, msg->message, msg->message, PM_REMOVE );
1593 USER_HEAP_FREE( hMsg );
1594 ReleaseCapture();
1595 MENU_HideSubPopups( hwnd, hmenu );
1596 if (menu->wFlags & MF_POPUP) ShowWindow( menu->hWnd, SW_HIDE );
1597 MENU_SelectItem( hwnd, hmenu, NO_SELECTED_ITEM );
1598 SendMessage( hwnd, WM_MENUSELECT, 0, MAKELONG( 0xffff, 0 ) );
1599 fEndMenuCalled = FALSE;
1600 return TRUE;
1604 /***********************************************************************
1605 * MENU_TrackMouseMenuBar
1607 * Menu-bar tracking upon a mouse event. Called from NC_HandleSysCommand().
1609 void MENU_TrackMouseMenuBar( HWND hwnd, POINT pt )
1611 WND *wndPtr = WIN_FindWndPtr( hwnd );
1612 HideCaret(0);
1613 SendMessage( hwnd, WM_ENTERMENULOOP, 0, 0 );
1614 SendMessage( hwnd, WM_INITMENU, wndPtr->wIDmenu, 0 );
1615 MENU_TrackMenu( (HMENU)wndPtr->wIDmenu, TPM_LEFTALIGN | TPM_LEFTBUTTON,
1616 pt.x, pt.y, hwnd, NULL );
1617 SendMessage( hwnd, WM_EXITMENULOOP, 0, 0 );
1618 ShowCaret(0);
1622 /***********************************************************************
1623 * MENU_TrackKbdMenuBar
1625 * Menu-bar tracking upon a keyboard event. Called from NC_HandleSysCommand().
1627 void MENU_TrackKbdMenuBar( HWND hwnd, UINT wParam )
1629 WND *wndPtr = WIN_FindWndPtr( hwnd );
1630 if (!wndPtr->wIDmenu) return;
1631 HideCaret(0);
1632 SendMessage( hwnd, WM_ENTERMENULOOP, 0, 0 );
1633 SendMessage( hwnd, WM_INITMENU, wndPtr->wIDmenu, 0 );
1634 /* Select first selectable item */
1635 MENU_SelectItem( hwnd, (HMENU)wndPtr->wIDmenu, NO_SELECTED_ITEM );
1636 MENU_SelectNextItem( hwnd, (HMENU)wndPtr->wIDmenu );
1637 MENU_TrackMenu( (HMENU)wndPtr->wIDmenu, TPM_LEFTALIGN | TPM_LEFTBUTTON,
1638 0, 0, hwnd, NULL );
1639 SendMessage( hwnd, WM_EXITMENULOOP, 0, 0 );
1640 ShowCaret(0);
1644 /**********************************************************************
1645 * TrackPopupMenu (USER.416)
1647 BOOL TrackPopupMenu( HMENU hMenu, UINT wFlags, short x, short y,
1648 short nReserved, HWND hWnd, LPRECT lpRect )
1650 BOOL ret;
1651 HideCaret(0);
1652 if (!MENU_ShowPopup( hWnd, hMenu, 0, x, y ))
1653 ret = FALSE;
1654 else
1655 ret = MENU_TrackMenu( hMenu, wFlags, 0, 0, hWnd, lpRect );
1656 ShowCaret(0);
1657 return ret;
1661 /***********************************************************************
1662 * PopupMenuWndProc
1664 LRESULT PopupMenuWndProc(HWND hwnd,UINT message,WPARAM wParam,LPARAM lParam)
1666 switch(message)
1668 case WM_CREATE:
1670 CREATESTRUCT *createStruct = (CREATESTRUCT*)PTR_SEG_TO_LIN(lParam);
1671 #ifdef WINELIB32
1672 HMENU hmenu = (HMENU) (createStruct->lpCreateParams);
1673 SetWindowLong( hwnd, 0, (LONG)hmenu );
1674 #else
1675 HMENU hmenu = (HMENU) ((int)createStruct->lpCreateParams & 0xffff);
1676 SetWindowWord( hwnd, 0, hmenu );
1677 #endif
1678 return 0;
1681 case WM_MOUSEACTIVATE: /* We don't want to be activated */
1682 return MA_NOACTIVATE;
1684 case WM_PAINT:
1686 PAINTSTRUCT ps;
1687 BeginPaint( hwnd, &ps );
1688 MENU_DrawPopupMenu( hwnd, ps.hdc,
1689 #ifdef WINELIB32
1690 (HMENU)GetWindowLong( hwnd, 0 )
1691 #else
1692 (HMENU)GetWindowWord( hwnd, 0 )
1693 #endif
1695 EndPaint( hwnd, &ps );
1696 return 0;
1699 default:
1700 return DefWindowProc(hwnd, message, wParam, lParam);
1702 return 0;
1706 /***********************************************************************
1707 * MENU_GetMenuBarHeight
1709 * Compute the size of the menu bar height. Used by NC_HandleNCCalcSize().
1711 UINT MENU_GetMenuBarHeight( HWND hwnd, UINT menubarWidth, int orgX, int orgY )
1713 HDC hdc;
1714 RECT rectBar;
1715 WND *wndPtr;
1716 LPPOPUPMENU lppop;
1718 if (!(wndPtr = WIN_FindWndPtr( hwnd ))) return 0;
1719 if (!(lppop = (LPPOPUPMENU)USER_HEAP_LIN_ADDR((HMENU)wndPtr->wIDmenu)))
1720 return 0;
1721 hdc = GetDC( hwnd );
1722 SetRect( &rectBar, orgX, orgY, orgX+menubarWidth, orgY+SYSMETRICS_CYMENU );
1723 MENU_MenuBarCalcSize( hdc, &rectBar, lppop, hwnd );
1724 ReleaseDC( hwnd, hdc );
1725 return lppop->Height;
1729 /*******************************************************************
1730 * ChangeMenu (USER.153)
1732 BOOL ChangeMenu( HMENU hMenu, UINT pos, SEGPTR data, UINT id, UINT flags )
1734 dprintf_menu( stddeb,"ChangeMenu: menu="NPFMT" pos=%d data=%08lx id=%04x flags=%04x\n",
1735 hMenu, pos, data, id, flags );
1736 if (flags & MF_APPEND)
1738 return AppendMenu( hMenu, flags & ~MF_APPEND, id, data );
1740 if (flags & MF_DELETE)
1742 /* FIXME: Word passes the item id in 'pos' and 0 or 0xffff as id */
1743 /* for MF_DELETE. We should check the parameters for all others */
1744 /* MF_* actions also (anybody got a doc on ChangeMenu?). */
1745 return DeleteMenu( hMenu, pos, flags & ~MF_DELETE );
1747 if (flags & MF_CHANGE)
1749 return ModifyMenu( hMenu, pos, flags & ~MF_CHANGE, id, data );
1751 if (flags & MF_REMOVE)
1753 return RemoveMenu( hMenu, flags & MF_BYPOSITION ? pos : id,
1754 flags & ~MF_REMOVE );
1756 /* Default: MF_INSERT */
1757 return InsertMenu( hMenu, pos, flags, id, data );
1761 /*******************************************************************
1762 * CheckMenuItem (USER.154)
1764 INT CheckMenuItem( HMENU hMenu, UINT id, UINT flags )
1766 MENUITEM *item;
1767 INT ret;
1769 dprintf_menu( stddeb,"CheckMenuItem: "NPFMT" %04x %04x\n",
1770 hMenu, id, flags );
1771 if (!(item = MENU_FindItem( &hMenu, &id, flags ))) return -1;
1772 ret = item->item_flags & MF_CHECKED;
1773 if (flags & MF_CHECKED) item->item_flags |= MF_CHECKED;
1774 else item->item_flags &= ~MF_CHECKED;
1775 return ret;
1779 /**********************************************************************
1780 * EnableMenuItem [USER.155]
1782 BOOL EnableMenuItem(HMENU hMenu, UINT wItemID, UINT wFlags)
1784 LPMENUITEM lpitem;
1785 dprintf_menu(stddeb,"EnableMenuItem ("NPFMT", %04X, %04X) !\n",
1786 hMenu, wItemID, wFlags);
1787 if (!(lpitem = MENU_FindItem( &hMenu, &wItemID, wFlags ))) return FALSE;
1789 /* We can't have MF_GRAYED and MF_DISABLED together */
1790 if (wFlags & MF_GRAYED)
1792 lpitem->item_flags = (lpitem->item_flags & ~MF_DISABLED) | MF_GRAYED;
1794 else if (wFlags & MF_DISABLED)
1796 lpitem->item_flags = (lpitem->item_flags & ~MF_GRAYED) | MF_DISABLED;
1798 else /* MF_ENABLED */
1800 lpitem->item_flags &= ~(MF_GRAYED | MF_DISABLED);
1802 return TRUE;
1806 /*******************************************************************
1807 * GetMenuString (USER.161)
1809 int GetMenuString( HMENU hMenu, UINT wItemID,
1810 LPSTR str, short nMaxSiz, UINT wFlags )
1812 LPMENUITEM lpitem;
1814 dprintf_menu( stddeb, "GetMenuString: menu="NPFMT" item=%04x ptr=%p len=%d flags=%04x\n",
1815 hMenu, wItemID, str, nMaxSiz, wFlags );
1816 if (!str || !nMaxSiz) return 0;
1817 str[0] = '\0';
1818 if (!(lpitem = MENU_FindItem( &hMenu, &wItemID, wFlags ))) return 0;
1819 if (!IS_STRING_ITEM(lpitem->item_flags)) return 0;
1820 lstrcpyn( str, (char *)USER_HEAP_LIN_ADDR(lpitem->hText), nMaxSiz );
1821 dprintf_menu( stddeb, "GetMenuString: returning '%s'\n", str );
1822 return strlen(str);
1826 /**********************************************************************
1827 * HiliteMenuItem [USER.162]
1829 BOOL HiliteMenuItem(HWND hWnd, HMENU hMenu, UINT wItemID, UINT wHilite)
1831 LPPOPUPMENU menu;
1832 LPMENUITEM lpitem;
1833 dprintf_menu(stddeb,"HiliteMenuItem("NPFMT", "NPFMT", %04X, %04X);\n",
1834 hWnd, hMenu, wItemID, wHilite);
1835 if (!(lpitem = MENU_FindItem( &hMenu, &wItemID, wHilite ))) return FALSE;
1836 if (!(menu = (LPPOPUPMENU) USER_HEAP_LIN_ADDR(hMenu))) return FALSE;
1837 if (menu->FocusedItem == wItemID) return TRUE;
1838 MENU_HideSubPopups( hWnd, hMenu );
1839 MENU_SelectItem( hWnd, hMenu, wItemID );
1840 return TRUE;
1844 /**********************************************************************
1845 * GetMenuState [USER.250]
1847 UINT GetMenuState(HMENU hMenu, UINT wItemID, UINT wFlags)
1849 LPMENUITEM lpitem;
1850 dprintf_menu(stddeb,"GetMenuState("NPFMT", %04X, %04X);\n",
1851 hMenu, wItemID, wFlags);
1852 if (!(lpitem = MENU_FindItem( &hMenu, &wItemID, wFlags ))) return -1;
1853 if (lpitem->item_flags & MF_POPUP)
1855 POPUPMENU *menu = (POPUPMENU *) USER_HEAP_LIN_ADDR( (HMENU)lpitem->item_id );
1856 if (!menu) return -1;
1857 else return (menu->nItems << 8) | (menu->wFlags & 0xff);
1859 else return lpitem->item_flags;
1863 /**********************************************************************
1864 * GetMenuItemCount [USER.263]
1866 INT GetMenuItemCount(HMENU hMenu)
1868 LPPOPUPMENU menu;
1869 dprintf_menu(stddeb,"GetMenuItemCount("NPFMT");\n", hMenu);
1870 menu = (LPPOPUPMENU) USER_HEAP_LIN_ADDR(hMenu);
1871 if (menu == NULL) return (UINT)-1;
1872 dprintf_menu(stddeb,"GetMenuItemCount("NPFMT") return %d \n",
1873 hMenu, menu->nItems);
1874 return menu->nItems;
1878 /**********************************************************************
1879 * GetMenuItemID [USER.264]
1881 UINT GetMenuItemID(HMENU hMenu, int nPos)
1883 LPPOPUPMENU menu;
1884 MENUITEM *item;
1886 dprintf_menu(stddeb,"GetMenuItemID("NPFMT", %d);\n", hMenu, nPos);
1887 if (!(menu = (LPPOPUPMENU) USER_HEAP_LIN_ADDR(hMenu))) return -1;
1888 if ((nPos < 0) || (nPos >= menu->nItems)) return -1;
1889 item = (MENUITEM *) USER_HEAP_LIN_ADDR( menu->hItems );
1890 if (item[nPos].item_flags & MF_POPUP) return -1;
1891 return item[nPos].item_id;
1895 /*******************************************************************
1896 * InsertMenu (USER.410)
1898 BOOL InsertMenu( HMENU hMenu, UINT pos, UINT flags, UINT id, SEGPTR data )
1900 MENUITEM *item;
1902 if (IS_STRING_ITEM(flags) && data)
1903 dprintf_menu( stddeb, "InsertMenu: "NPFMT" %d %04x %04x '%s'\n",
1904 hMenu, pos, flags, id, (char *)PTR_SEG_TO_LIN(data) );
1905 else dprintf_menu( stddeb, "InsertMenu: "NPFMT" %d %04x %04x %08lx\n",
1906 hMenu, pos, flags, id, (DWORD)data );
1908 if (!(item = MENU_InsertItem( hMenu, pos, flags ))) return FALSE;
1910 if (!(MENU_SetItemData( item, flags, id, data )))
1912 RemoveMenu( hMenu, pos, flags );
1913 return FALSE;
1916 if (flags & MF_POPUP) /* Set the MF_POPUP flag on the popup-menu */
1917 ((POPUPMENU *)USER_HEAP_LIN_ADDR((HMENU)id))->wFlags |= MF_POPUP;
1919 item->hCheckBit = hStdCheck;
1920 item->hUnCheckBit = 0;
1921 return TRUE;
1925 /*******************************************************************
1926 * AppendMenu (USER.411)
1928 BOOL AppendMenu( HMENU hMenu, UINT flags, UINT id, SEGPTR data )
1930 return InsertMenu( hMenu, -1, flags | MF_BYPOSITION, id, data );
1934 /**********************************************************************
1935 * RemoveMenu [USER.412]
1937 BOOL RemoveMenu(HMENU hMenu, UINT nPos, UINT wFlags)
1939 LPPOPUPMENU menu;
1940 LPMENUITEM lpitem;
1941 dprintf_menu(stddeb,"RemoveMenu ("NPFMT", %04X, %04X) !\n",
1942 hMenu, nPos, wFlags);
1943 if (!(lpitem = MENU_FindItem( &hMenu, &nPos, wFlags ))) return FALSE;
1944 if (!(menu = (LPPOPUPMENU) USER_HEAP_LIN_ADDR(hMenu))) return FALSE;
1946 /* Remove item */
1948 if (IS_STRING_ITEM(lpitem->item_flags) && lpitem->hText)
1949 USER_HEAP_FREE(lpitem->hText);
1950 if (--menu->nItems == 0)
1952 USER_HEAP_FREE( menu->hItems );
1953 menu->hItems = 0;
1955 else
1957 while(nPos < menu->nItems)
1959 *lpitem = *(lpitem+1);
1960 lpitem++;
1961 nPos++;
1963 menu->hItems = USER_HEAP_REALLOC( menu->hItems,
1964 menu->nItems * sizeof(MENUITEM) );
1966 return TRUE;
1970 /**********************************************************************
1971 * DeleteMenu [USER.413]
1973 BOOL DeleteMenu(HMENU hMenu, UINT nPos, UINT wFlags)
1975 MENUITEM *item = MENU_FindItem( &hMenu, &nPos, wFlags );
1976 if (!item) return FALSE;
1977 if (item->item_flags & MF_POPUP) DestroyMenu( (HMENU)item->item_id );
1978 /* nPos is now the position of the item */
1979 RemoveMenu( hMenu, nPos, wFlags | MF_BYPOSITION );
1980 return TRUE;
1984 /*******************************************************************
1985 * ModifyMenu (USER.414)
1987 BOOL ModifyMenu( HMENU hMenu, UINT pos, UINT flags, UINT id, SEGPTR data )
1989 MENUITEM *item;
1991 if (IS_STRING_ITEM(flags))
1993 dprintf_menu( stddeb, "ModifyMenu: "NPFMT" %d %04x %04x '%s'\n",
1994 hMenu, pos, flags, id,
1995 data ? (char *)PTR_SEG_TO_LIN(data) : "#NULL#");
1996 if (!data) return FALSE;
1998 else
1999 dprintf_menu( stddeb, "ModifyMenu: "NPFMT" %d %04x %04x %08lx\n",
2000 hMenu, pos, flags, id, (DWORD)data );
2001 if (!(item = MENU_FindItem( &hMenu, &pos, flags ))) return FALSE;
2003 return MENU_SetItemData( item, flags, id, data );
2007 /**********************************************************************
2008 * CreatePopupMenu [USER.415]
2010 HMENU CreatePopupMenu()
2012 HMENU hmenu;
2013 POPUPMENU *menu;
2015 if (!(hmenu = CreateMenu())) return 0;
2016 menu = (POPUPMENU *) USER_HEAP_LIN_ADDR( hmenu );
2017 menu->wFlags |= MF_POPUP;
2018 return hmenu;
2022 /**********************************************************************
2023 * GetMenuCheckMarkDimensions [USER.417]
2025 DWORD GetMenuCheckMarkDimensions()
2027 return MAKELONG( check_bitmap_width, check_bitmap_height );
2031 /**********************************************************************
2032 * SetMenuItemBitmaps [USER.418]
2034 BOOL SetMenuItemBitmaps(HMENU hMenu, UINT nPos, UINT wFlags,
2035 HBITMAP hNewUnCheck, HBITMAP hNewCheck)
2037 LPMENUITEM lpitem;
2038 dprintf_menu(stddeb,"SetMenuItemBitmaps ("NPFMT", %04X, %04X, "NPFMT", %08lX) !\n",
2039 hMenu, nPos, wFlags, hNewCheck, (DWORD)hNewUnCheck);
2040 if (!(lpitem = MENU_FindItem( &hMenu, &nPos, wFlags ))) return FALSE;
2042 if (!hNewCheck && !hNewUnCheck)
2044 /* If both are NULL, restore default bitmaps */
2045 lpitem->hCheckBit = hStdCheck;
2046 lpitem->hUnCheckBit = 0;
2047 lpitem->item_flags &= ~MF_USECHECKBITMAPS;
2049 else /* Install new bitmaps */
2051 lpitem->hCheckBit = hNewCheck;
2052 lpitem->hUnCheckBit = hNewUnCheck;
2053 lpitem->item_flags |= MF_USECHECKBITMAPS;
2055 return TRUE;
2059 /**********************************************************************
2060 * CreateMenu [USER.151]
2062 HMENU CreateMenu()
2064 HMENU hMenu;
2065 LPPOPUPMENU menu;
2066 dprintf_menu(stddeb,"CreateMenu !\n");
2067 if (!(hMenu = USER_HEAP_ALLOC( sizeof(POPUPMENU) )))
2068 return 0;
2069 menu = (LPPOPUPMENU) USER_HEAP_LIN_ADDR(hMenu);
2070 menu->hNext = 0;
2071 menu->wFlags = 0;
2072 menu->wMagic = MENU_MAGIC;
2073 menu->hTaskQ = 0;
2074 menu->Width = 0;
2075 menu->Height = 0;
2076 menu->nItems = 0;
2077 menu->hWnd = 0;
2078 menu->hItems = 0;
2079 menu->FocusedItem = NO_SELECTED_ITEM;
2080 dprintf_menu(stddeb,"CreateMenu // return "NPFMT"\n", hMenu);
2081 return hMenu;
2085 /**********************************************************************
2086 * DestroyMenu [USER.152]
2088 BOOL DestroyMenu(HMENU hMenu)
2090 LPPOPUPMENU lppop;
2091 dprintf_menu(stddeb,"DestroyMenu ("NPFMT") !\n", hMenu);
2092 if (hMenu == 0) return FALSE;
2093 lppop = (LPPOPUPMENU) USER_HEAP_LIN_ADDR(hMenu);
2094 if (!lppop || (lppop->wMagic != MENU_MAGIC)) return FALSE;
2095 lppop->wMagic = 0; /* Mark it as destroyed */
2096 if ((lppop->wFlags & MF_POPUP) && lppop->hWnd)
2097 DestroyWindow( lppop->hWnd );
2099 if (lppop->hItems)
2101 int i;
2102 MENUITEM *item = (MENUITEM *) USER_HEAP_LIN_ADDR( lppop->hItems );
2103 for (i = lppop->nItems; i > 0; i--, item++)
2105 if (item->item_flags & MF_POPUP)
2106 DestroyMenu( (HMENU)item->item_id );
2107 if (IS_STRING_ITEM(item->item_flags) && item->hText)
2108 USER_HEAP_FREE(item->hText);
2110 USER_HEAP_FREE( lppop->hItems );
2112 USER_HEAP_FREE( hMenu );
2113 dprintf_menu(stddeb,"DestroyMenu ("NPFMT") // End !\n", hMenu);
2114 return TRUE;
2117 /**********************************************************************
2118 * GetSystemMenu [USER.156]
2120 HMENU GetSystemMenu(HWND hWnd, BOOL bRevert)
2122 WND *wndPtr = WIN_FindWndPtr( hWnd );
2123 if (!wndPtr) return 0;
2125 if (!bRevert) return wndPtr->hSysMenu;
2126 if (wndPtr->hSysMenu) DestroyMenu(wndPtr->hSysMenu);
2127 wndPtr->hSysMenu = MENU_CopySysMenu();
2128 return wndPtr->hSysMenu;
2132 /*******************************************************************
2133 * SetSystemMenu (USER.280)
2135 BOOL SetSystemMenu( HWND hwnd, HMENU hMenu )
2137 WND *wndPtr;
2139 if (!(wndPtr = WIN_FindWndPtr(hwnd))) return FALSE;
2140 if (wndPtr->hSysMenu) DestroyMenu( wndPtr->hSysMenu );
2141 wndPtr->hSysMenu = hMenu;
2142 return TRUE;
2146 /**********************************************************************
2147 * GetMenu [USER.157]
2149 HMENU GetMenu(HWND hWnd)
2151 WND * wndPtr = WIN_FindWndPtr(hWnd);
2152 if (wndPtr == NULL) return 0;
2153 return (HMENU)wndPtr->wIDmenu;
2157 /**********************************************************************
2158 * SetMenu [USER.158]
2160 BOOL SetMenu(HWND hWnd, HMENU hMenu)
2162 LPPOPUPMENU lpmenu;
2163 WND * wndPtr = WIN_FindWndPtr(hWnd);
2164 if (wndPtr == NULL) {
2165 fprintf(stderr,"SetMenu("NPFMT", "NPFMT") // Bad window handle !\n",
2166 hWnd, hMenu);
2167 return FALSE;
2169 dprintf_menu(stddeb,"SetMenu("NPFMT", "NPFMT");\n", hWnd, hMenu);
2170 if (GetCapture() == hWnd) ReleaseCapture();
2171 wndPtr->wIDmenu = (UINT)hMenu;
2172 if (hMenu != 0)
2174 lpmenu = (LPPOPUPMENU) USER_HEAP_LIN_ADDR(hMenu);
2175 if (lpmenu == NULL) {
2176 fprintf(stderr,"SetMenu("NPFMT", "NPFMT") // Bad menu handle !\n",
2177 hWnd, hMenu);
2178 return FALSE;
2180 lpmenu->hWnd = hWnd;
2181 lpmenu->wFlags &= ~MF_POPUP; /* Can't be a popup */
2182 lpmenu->Height = 0; /* Make sure we recalculate the size */
2184 SetWindowPos( hWnd, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE |
2185 SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED );
2186 return TRUE;
2191 /**********************************************************************
2192 * GetSubMenu [USER.159]
2194 HMENU GetSubMenu(HMENU hMenu, short nPos)
2196 LPPOPUPMENU lppop;
2197 LPMENUITEM lpitem;
2198 dprintf_menu(stddeb,"GetSubMenu ("NPFMT", %04X) !\n", hMenu, nPos);
2199 if (!(lppop = (LPPOPUPMENU) USER_HEAP_LIN_ADDR(hMenu))) return 0;
2200 if ((UINT)nPos >= lppop->nItems) return 0;
2201 lpitem = (MENUITEM *) USER_HEAP_LIN_ADDR( lppop->hItems );
2202 if (!(lpitem[nPos].item_flags & MF_POPUP)) return 0;
2203 return (HMENU)lpitem[nPos].item_id;
2207 /**********************************************************************
2208 * DrawMenuBar [USER.160]
2210 void DrawMenuBar(HWND hWnd)
2212 WND *wndPtr;
2213 LPPOPUPMENU lppop;
2214 dprintf_menu(stddeb,"DrawMenuBar ("NPFMT")\n", hWnd);
2215 wndPtr = WIN_FindWndPtr(hWnd);
2216 if (wndPtr != NULL && (wndPtr->dwStyle & WS_CHILD) == 0 &&
2217 wndPtr->wIDmenu != 0) {
2218 dprintf_menu(stddeb,"DrawMenuBar wIDmenu=%04X \n",
2219 wndPtr->wIDmenu);
2220 lppop = (LPPOPUPMENU) USER_HEAP_LIN_ADDR((HMENU)wndPtr->wIDmenu);
2221 if (lppop == NULL) return;
2223 lppop->Height = 0; /* Make sure we call MENU_MenuBarCalcSize */
2224 SetWindowPos( hWnd, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE |
2225 SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED );
2230 /***********************************************************************
2231 * EndMenu (USER.187)
2233 void EndMenu(void)
2235 /* FIXME: this won't work when we have multiple tasks... */
2236 fEndMenuCalled = TRUE;
2240 /***********************************************************************
2241 * LookupMenuHandle (USER.217)
2243 HMENU LookupMenuHandle( HMENU hmenu, INT id )
2245 if (!MENU_FindItem( &hmenu, &id, MF_BYCOMMAND )) return 0;
2246 else return hmenu;
2250 /**********************************************************************
2251 * LoadMenu (USER.150)
2253 HMENU LoadMenu( HINSTANCE instance, SEGPTR name )
2255 HRSRC hRsrc;
2256 HGLOBAL handle;
2257 HMENU hMenu;
2259 if (HIWORD(name))
2261 char *str = (char *)PTR_SEG_TO_LIN( name );
2262 dprintf_menu( stddeb, "LoadMenu("NPFMT",'%s')\n", instance, str );
2263 if (str[0] == '#') name = (SEGPTR)atoi( str + 1 );
2265 else
2266 dprintf_resource(stddeb,"LoadMenu("NPFMT",%04x)\n",instance,LOWORD(name));
2268 if (!name) return 0;
2270 if (!(hRsrc = FindResource( instance, name, RT_MENU ))) {
2271 /* check for Win32 module */
2272 instance = GetExePtr( instance );
2273 if(((NE_MODULE*)GlobalLock(instance))->magic == PE_SIGNATURE)
2274 return WIN32_LoadMenuA(instance,PTR_SEG_TO_LIN(name));
2275 return 0;
2277 if (!(handle = LoadResource( instance, hRsrc ))) return 0;
2278 hMenu = LoadMenuIndirect( WIN16_LockResource(handle) );
2279 FreeResource( handle );
2280 return hMenu;
2284 /**********************************************************************
2285 * LoadMenuIndirect (USER.220)
2287 HMENU LoadMenuIndirect( SEGPTR template )
2289 HMENU hMenu;
2291 dprintf_menu(stddeb,"LoadMenuIndirect: %08lx\n", (DWORD)template );
2292 if (!(hMenu = CreateMenu())) return (HMENU)0;
2293 template += sizeof(MENU_HEADER);
2294 if (!MENU_ParseResource( template, hMenu ))
2296 DestroyMenu( hMenu );
2297 return (HMENU)0;
2299 return hMenu;
2303 /**********************************************************************
2304 * IsMenu (USER.358)
2306 BOOL IsMenu( HMENU hmenu )
2308 LPPOPUPMENU menu;
2309 if (!(menu = (LPPOPUPMENU) USER_HEAP_LIN_ADDR( hmenu ))) return FALSE;
2310 return (menu->wMagic == MENU_MAGIC);