4 * Copyright 1993 Martin Ayotte
5 * Copyright 1994 Alexandre Julliard
6 * Copyright 1997 Morten Welinder
10 * Note: the style MF_MOUSESELECT is used to mark popup items that
11 * have been selected, i.e. their popup menu is currently displayed.
12 * This is probably not the meaning this style has in MS-Windows.
16 #include "wine/port.h"
26 #include "wine/winbase16.h"
27 #include "wine/winuser16.h"
28 #include "wine/unicode.h"
31 #include "nonclient.h"
35 #include "debugtools.h"
37 DEFAULT_DEBUG_CHANNEL(menu);
38 DECLARE_DEBUG_CHANNEL(accel);
40 /* internal popup menu window messages */
42 #define MM_SETMENUHANDLE (WM_USER + 0)
43 #define MM_GETMENUHANDLE (WM_USER + 1)
45 /* Menu item structure */
47 /* ----------- MENUITEMINFO Stuff ----------- */
48 UINT fType; /* Item type. */
49 UINT fState; /* Item state. */
50 UINT wID; /* Item id. */
51 HMENU hSubMenu; /* Pop-up menu. */
52 HBITMAP hCheckBit; /* Bitmap when checked. */
53 HBITMAP hUnCheckBit; /* Bitmap when unchecked. */
54 LPWSTR text; /* Item text or bitmap handle. */
55 DWORD dwItemData; /* Application defined. */
56 DWORD dwTypeData; /* depends on fMask */
57 HBITMAP hbmpItem; /* bitmap in win98 style menus */
58 /* ----------- Wine stuff ----------- */
59 RECT rect; /* Item area (relative to menu window) */
60 UINT xTab; /* X position of text after Tab */
63 /* Popup menu structure */
65 WORD wFlags; /* Menu flags (MF_POPUP, MF_SYSMENU) */
66 WORD wMagic; /* Magic number */
67 WORD Width; /* Width of the whole menu */
68 WORD Height; /* Height of the whole menu */
69 UINT nItems; /* Number of items in the menu */
70 HWND hWnd; /* Window containing the menu */
71 MENUITEM *items; /* Array of menu items */
72 UINT FocusedItem; /* Currently focused item */
73 HWND hwndOwner; /* window receiving the messages for ownerdraw */
74 BOOL bTimeToHide; /* Request hiding when receiving a second click in the top-level menu item */
75 /* ------------ MENUINFO members ------ */
76 DWORD dwStyle; /* Extended mennu style */
77 UINT cyMax; /* max hight of the whole menu, 0 is screen hight */
78 HBRUSH hbrBack; /* brush for menu background */
79 DWORD dwContextHelpID;
80 DWORD dwMenuData; /* application defined value */
81 HMENU hSysMenuOwner; /* Handle to the dummy sys menu holder */
82 } POPUPMENU, *LPPOPUPMENU;
84 /* internal flags for menu tracking */
86 #define TF_ENDMENU 0x0001
87 #define TF_SUSPENDPOPUP 0x0002
88 #define TF_SKIPREMOVE 0x0004
93 HMENU hCurrentMenu; /* current submenu (can be equal to hTopMenu)*/
94 HMENU hTopMenu; /* initial menu */
95 HWND hOwnerWnd; /* where notifications are sent */
99 #define MENU_MAGIC 0x554d /* 'MU' */
104 /* Internal MENU_TrackMenu() flags */
105 #define TPM_INTERNAL 0xF0000000
106 #define TPM_ENTERIDLEEX 0x80000000 /* set owner window for WM_ENTERIDLE */
107 #define TPM_BUTTONDOWN 0x40000000 /* menu was clicked before tracking */
108 #define TPM_POPUPMENU 0x20000000 /* menu is a popup menu */
110 /* popup menu shade thickness */
111 #define POPUP_XSHADE 4
112 #define POPUP_YSHADE 4
114 /* Space between 2 menu bar items */
115 #define MENU_BAR_ITEMS_SPACE 12
117 /* Minimum width of a tab character */
118 #define MENU_TAB_SPACE 8
120 /* Height of a separator item */
121 #define SEPARATOR_HEIGHT 5
123 /* (other menu->FocusedItem values give the position of the focused item) */
124 #define NO_SELECTED_ITEM 0xffff
126 #define MENU_ITEM_TYPE(flags) \
127 ((flags) & (MF_STRING | MF_BITMAP | MF_OWNERDRAW | MF_SEPARATOR))
129 #define IS_STRING_ITEM(flags) (MENU_ITEM_TYPE ((flags)) == MF_STRING)
130 #define IS_BITMAP_ITEM(flags) (MENU_ITEM_TYPE ((flags)) == MF_BITMAP)
131 #define IS_MAGIC_ITEM(text) (LOWORD((int)text)<12)
133 #define IS_SYSTEM_MENU(menu) \
134 (!((menu)->wFlags & MF_POPUP) && (menu)->wFlags & MF_SYSMENU)
136 #define IS_SYSTEM_POPUP(menu) \
137 ((menu)->wFlags & MF_POPUP && (menu)->wFlags & MF_SYSMENU)
139 #define TYPE_MASK (MFT_STRING | MFT_BITMAP | MFT_OWNERDRAW | MFT_SEPARATOR | \
140 MFT_MENUBARBREAK | MFT_MENUBREAK | MFT_RADIOCHECK | \
141 MFT_RIGHTORDER | MFT_RIGHTJUSTIFY | \
142 MF_POPUP | MF_SYSMENU | MF_HELP)
143 #define STATE_MASK (~TYPE_MASK)
145 /* Dimension of the menu bitmaps */
146 static WORD arrow_bitmap_width = 0, arrow_bitmap_height = 0;
148 static HBITMAP hStdMnArrow = 0;
149 static HBITMAP hBmpSysMenu = 0;
151 static HBRUSH hShadeBrush = 0;
152 static HFONT hMenuFont = 0;
153 static HFONT hMenuFontBold = 0;
155 static HMENU MENU_DefSysPopup = 0; /* Default system menu popup */
157 /* Use global popup window because there's no way 2 menus can
158 * be tracked at the same time. */
159 static HWND top_popup;
161 /* Flag set by EndMenu() to force an exit from menu tracking */
162 static BOOL fEndMenu = FALSE;
164 static LRESULT WINAPI PopupMenuWndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam );
167 /*********************************************************************
168 * menu class descriptor
170 const struct builtin_class_descr MENU_builtin_class =
172 POPUPMENU_CLASS_ATOM, /* name */
173 CS_GLOBALCLASS | CS_SAVEBITS, /* style */
174 NULL, /* procA (winproc is Unicode only) */
175 PopupMenuWndProc, /* procW */
176 sizeof(HMENU), /* extra */
177 IDC_ARROWA, /* cursor */
178 COLOR_MENU+1 /* brush */
182 /***********************************************************************
183 * debug_print_menuitem
185 * Print a menuitem in readable form.
188 #define debug_print_menuitem(pre, mp, post) \
189 if(!TRACE_ON(menu)) ; else do_debug_print_menuitem(pre, mp, post)
191 #define MENUOUT(text) \
192 DPRINTF("%s%s", (count++ ? "," : ""), (text))
194 #define MENUFLAG(bit,text) \
196 if (flags & (bit)) { flags &= ~(bit); MENUOUT ((text)); } \
199 static void do_debug_print_menuitem(const char *prefix, MENUITEM * mp,
202 TRACE("%s ", prefix);
204 UINT flags = mp->fType;
205 int typ = MENU_ITEM_TYPE(flags);
206 DPRINTF( "{ ID=0x%x", mp->wID);
207 if (flags & MF_POPUP)
208 DPRINTF( ", Sub=0x%x", mp->hSubMenu);
212 if (typ == MFT_STRING)
214 else if (typ == MFT_SEPARATOR)
216 else if (typ == MFT_OWNERDRAW)
218 else if (typ == MFT_BITMAP)
224 MENUFLAG(MF_POPUP, "pop");
225 MENUFLAG(MFT_MENUBARBREAK, "barbrk");
226 MENUFLAG(MFT_MENUBREAK, "brk");
227 MENUFLAG(MFT_RADIOCHECK, "radio");
228 MENUFLAG(MFT_RIGHTORDER, "rorder");
229 MENUFLAG(MF_SYSMENU, "sys");
230 MENUFLAG(MFT_RIGHTJUSTIFY, "right"); /* same as MF_HELP */
233 DPRINTF( "+0x%x", flags);
238 DPRINTF( ", State=");
239 MENUFLAG(MFS_GRAYED, "grey");
240 MENUFLAG(MFS_DEFAULT, "default");
241 MENUFLAG(MFS_DISABLED, "dis");
242 MENUFLAG(MFS_CHECKED, "check");
243 MENUFLAG(MFS_HILITE, "hi");
244 MENUFLAG(MF_USECHECKBITMAPS, "usebit");
245 MENUFLAG(MF_MOUSESELECT, "mouse");
247 DPRINTF( "+0x%x", flags);
250 DPRINTF( ", Chk=0x%x", mp->hCheckBit);
252 DPRINTF( ", Unc=0x%x", mp->hUnCheckBit);
254 if (typ == MFT_STRING) {
256 DPRINTF( ", Text=%s", debugstr_w(mp->text));
258 DPRINTF( ", Text=Null");
259 } else if (mp->text == NULL)
262 DPRINTF( ", Text=%p", mp->text);
264 DPRINTF( ", ItemData=0x%08lx", mp->dwItemData);
270 DPRINTF(" %s\n", postfix);
277 /***********************************************************************
280 * Validate the given menu handle and returns the menu structure pointer.
282 static POPUPMENU *MENU_GetMenu(HMENU hMenu)
284 POPUPMENU *menu = USER_HEAP_LIN_ADDR(hMenu);
285 if (!menu || menu->wMagic != MENU_MAGIC)
287 WARN("invalid menu handle=%x, ptr=%p, magic=%x\n", hMenu, menu, menu? menu->wMagic:0);
293 /***********************************************************************
296 * Get the system menu of a window
298 static HMENU get_win_sys_menu( HWND hwnd )
301 WND *win = WIN_FindWndPtr( hwnd );
305 WIN_ReleaseWndPtr( win );
310 /***********************************************************************
313 * Return the default system menu.
315 static HMENU MENU_CopySysPopup(void)
317 HMENU hMenu = LoadMenuA(GetModuleHandleA("USER32"), "SYSMENU");
320 POPUPMENU* menu = MENU_GetMenu(hMenu);
321 menu->wFlags |= MF_SYSMENU | MF_POPUP;
322 SetMenuDefaultItem(hMenu, SC_CLOSE, FALSE);
325 ERR("Unable to load default system menu\n" );
327 TRACE("returning %x.\n", hMenu );
333 /**********************************************************************
336 * Create a copy of the system menu. System menu in Windows is
337 * a special menu bar with the single entry - system menu popup.
338 * This popup is presented to the outside world as a "system menu".
339 * However, the real system menu handle is sometimes seen in the
340 * WM_MENUSELECT parameters (and Word 6 likes it this way).
342 HMENU MENU_GetSysMenu( HWND hWnd, HMENU hPopupMenu )
346 if ((hMenu = CreateMenu()))
348 POPUPMENU *menu = MENU_GetMenu(hMenu);
349 menu->wFlags = MF_SYSMENU;
350 menu->hWnd = WIN_GetFullHandle( hWnd );
352 if (hPopupMenu == (HMENU)(-1))
353 hPopupMenu = MENU_CopySysPopup();
354 else if( !hPopupMenu ) hPopupMenu = MENU_DefSysPopup;
358 InsertMenuA( hMenu, -1, MF_SYSMENU | MF_POPUP | MF_BYPOSITION, hPopupMenu, NULL );
360 menu->items[0].fType = MF_SYSMENU | MF_POPUP;
361 menu->items[0].fState = 0;
362 if ((menu = MENU_GetMenu(hPopupMenu))) menu->wFlags |= MF_SYSMENU;
364 TRACE("GetSysMenu hMenu=%04x (%04x)\n", hMenu, hPopupMenu );
367 DestroyMenu( hMenu );
369 ERR("failed to load system menu!\n");
374 /***********************************************************************
377 * Menus initialisation.
382 NONCLIENTMETRICSA ncm;
384 static unsigned char shade_bits[16] = { 0x55, 0, 0xAA, 0,
389 /* Load menu bitmaps */
390 hStdMnArrow = LoadBitmapA(0, MAKEINTRESOURCEA(OBM_MNARROW));
391 /* Load system buttons bitmaps */
392 hBmpSysMenu = LoadBitmapA(0, MAKEINTRESOURCEA(OBM_CLOSE));
397 GetObjectA( hStdMnArrow, sizeof(bm), &bm );
398 arrow_bitmap_width = bm.bmWidth;
399 arrow_bitmap_height = bm.bmHeight;
403 if (! (hBitmap = CreateBitmap( 8, 8, 1, 1, shade_bits)))
406 if(!(hShadeBrush = CreatePatternBrush( hBitmap )))
409 DeleteObject( hBitmap );
410 if (!(MENU_DefSysPopup = MENU_CopySysPopup()))
413 ncm.cbSize = sizeof (NONCLIENTMETRICSA);
414 if (!(SystemParametersInfoA(SPI_GETNONCLIENTMETRICS, sizeof(NONCLIENTMETRICSA), &ncm, 0)))
417 if (!(hMenuFont = CreateFontIndirectA( &ncm.lfMenuFont )))
420 ncm.lfMenuFont.lfWeight += 300;
421 if ( ncm.lfMenuFont.lfWeight > 1000)
422 ncm.lfMenuFont.lfWeight = 1000;
424 if (!(hMenuFontBold = CreateFontIndirectA( &ncm.lfMenuFont )))
430 /***********************************************************************
431 * MENU_InitSysMenuPopup
433 * Grey the appropriate items in System menu.
435 static void MENU_InitSysMenuPopup( HMENU hmenu, DWORD style, DWORD clsStyle )
439 gray = !(style & WS_THICKFRAME) || (style & (WS_MAXIMIZE | WS_MINIMIZE));
440 EnableMenuItem( hmenu, SC_SIZE, (gray ? MF_GRAYED : MF_ENABLED) );
441 gray = ((style & WS_MAXIMIZE) != 0);
442 EnableMenuItem( hmenu, SC_MOVE, (gray ? MF_GRAYED : MF_ENABLED) );
443 gray = !(style & WS_MINIMIZEBOX) || (style & WS_MINIMIZE);
444 EnableMenuItem( hmenu, SC_MINIMIZE, (gray ? MF_GRAYED : MF_ENABLED) );
445 gray = !(style & WS_MAXIMIZEBOX) || (style & WS_MAXIMIZE);
446 EnableMenuItem( hmenu, SC_MAXIMIZE, (gray ? MF_GRAYED : MF_ENABLED) );
447 gray = !(style & (WS_MAXIMIZE | WS_MINIMIZE));
448 EnableMenuItem( hmenu, SC_RESTORE, (gray ? MF_GRAYED : MF_ENABLED) );
449 gray = (clsStyle & CS_NOCLOSE) != 0;
451 /* The menu item must keep its state if it's disabled */
453 EnableMenuItem( hmenu, SC_CLOSE, MF_GRAYED);
457 /******************************************************************************
459 * UINT MENU_GetStartOfNextColumn(
462 *****************************************************************************/
464 static UINT MENU_GetStartOfNextColumn(
467 POPUPMENU *menu = MENU_GetMenu(hMenu);
471 return NO_SELECTED_ITEM;
473 i = menu->FocusedItem + 1;
474 if( i == NO_SELECTED_ITEM )
477 for( ; i < menu->nItems; ++i ) {
478 if (menu->items[i].fType & MF_MENUBARBREAK)
482 return NO_SELECTED_ITEM;
486 /******************************************************************************
488 * UINT MENU_GetStartOfPrevColumn(
491 *****************************************************************************/
493 static UINT MENU_GetStartOfPrevColumn(
496 POPUPMENU *menu = MENU_GetMenu(hMenu);
500 return NO_SELECTED_ITEM;
502 if( menu->FocusedItem == 0 || menu->FocusedItem == NO_SELECTED_ITEM )
503 return NO_SELECTED_ITEM;
505 /* Find the start of the column */
507 for(i = menu->FocusedItem; i != 0 &&
508 !(menu->items[i].fType & MF_MENUBARBREAK);
512 return NO_SELECTED_ITEM;
514 for(--i; i != 0; --i) {
515 if (menu->items[i].fType & MF_MENUBARBREAK)
519 TRACE("ret %d.\n", i );
526 /***********************************************************************
529 * Find a menu item. Return a pointer on the item, and modifies *hmenu
530 * in case the item was in a sub-menu.
532 static MENUITEM *MENU_FindItem( HMENU *hmenu, UINT *nPos, UINT wFlags )
537 if (((*hmenu)==0xffff) || (!(menu = MENU_GetMenu(*hmenu)))) return NULL;
538 if (wFlags & MF_BYPOSITION)
540 if (*nPos >= menu->nItems) return NULL;
541 return &menu->items[*nPos];
545 MENUITEM *item = menu->items;
546 for (i = 0; i < menu->nItems; i++, item++)
548 if (item->wID == *nPos)
553 else if (item->fType & MF_POPUP)
555 HMENU hsubmenu = item->hSubMenu;
556 MENUITEM *subitem = MENU_FindItem( &hsubmenu, nPos, wFlags );
568 /***********************************************************************
571 * Find a Sub menu. Return the position of the submenu, and modifies
572 * *hmenu in case it is found in another sub-menu.
573 * If the submenu cannot be found, NO_SELECTED_ITEM is returned.
575 UINT MENU_FindSubMenu( HMENU *hmenu, HMENU hSubTarget )
580 if (((*hmenu)==0xffff) ||
581 (!(menu = MENU_GetMenu(*hmenu))))
582 return NO_SELECTED_ITEM;
584 for (i = 0; i < menu->nItems; i++, item++) {
585 if(!(item->fType & MF_POPUP)) continue;
586 if (item->hSubMenu == hSubTarget) {
590 HMENU hsubmenu = item->hSubMenu;
591 UINT pos = MENU_FindSubMenu( &hsubmenu, hSubTarget );
592 if (pos != NO_SELECTED_ITEM) {
598 return NO_SELECTED_ITEM;
601 /***********************************************************************
604 static void MENU_FreeItemData( MENUITEM* item )
607 if (IS_STRING_ITEM(item->fType) && item->text)
608 HeapFree( GetProcessHeap(), 0, item->text );
611 /***********************************************************************
612 * MENU_FindItemByCoords
614 * Find the item at the specified coordinates (screen coords). Does
615 * not work for child windows and therefore should not be called for
616 * an arbitrary system menu.
618 static MENUITEM *MENU_FindItemByCoords( POPUPMENU *menu,
619 POINT pt, UINT *pos )
625 if (!GetWindowRect(menu->hWnd,&wrect)) return NULL;
626 pt.x -= wrect.left;pt.y -= wrect.top;
628 for (i = 0; i < menu->nItems; i++, item++)
630 if ((pt.x >= item->rect.left) && (pt.x < item->rect.right) &&
631 (pt.y >= item->rect.top) && (pt.y < item->rect.bottom))
641 /***********************************************************************
644 * Find the menu item selected by a key press.
645 * Return item id, -1 if none, -2 if we should close the menu.
647 static UINT MENU_FindItemByKey( HWND hwndOwner, HMENU hmenu,
648 UINT key, BOOL forceMenuChar )
650 TRACE("\tlooking for '%c' in [%04x]\n", (char)key, (UINT16)hmenu );
652 if (!IsMenu( hmenu )) hmenu = GetSubMenu( get_win_sys_menu(hwndOwner), 0);
656 POPUPMENU *menu = MENU_GetMenu( hmenu );
657 MENUITEM *item = menu->items;
665 for (i = 0; i < menu->nItems; i++, item++)
667 if (item->text && (IS_STRING_ITEM(item->fType)))
669 WCHAR *p = item->text - 2;
672 p = strchrW (p + 2, '&');
674 while (p != NULL && p [1] == '&');
675 if (p && (toupper(p[1]) == key)) return i;
679 menuchar = SendMessageA( hwndOwner, WM_MENUCHAR,
680 MAKEWPARAM( key, menu->wFlags ), hmenu );
681 if (HIWORD(menuchar) == 2) return LOWORD(menuchar);
682 if (HIWORD(menuchar) == 1) return (UINT)(-2);
688 /***********************************************************************
689 * MENU_GetBitmapItemSize
691 * Get the size of a bitmap item.
693 static void MENU_GetBitmapItemSize( UINT id, DWORD data, SIZE *size )
696 HBITMAP bmp = (HBITMAP)id;
698 size->cx = size->cy = 0;
700 /* check if there is a magic menu item associated with this item */
701 if (id && IS_MAGIC_ITEM( id ))
712 case HBMMENU_MBAR_RESTORE:
713 case HBMMENU_MBAR_MINIMIZE:
714 case HBMMENU_MBAR_MINIMIZE_D:
715 case HBMMENU_MBAR_CLOSE:
716 case HBMMENU_MBAR_CLOSE_D:
717 size->cx = GetSystemMetrics( SM_CXSIZE );
718 size->cy = GetSystemMetrics( SM_CYSIZE );
720 case HBMMENU_CALLBACK:
721 case HBMMENU_POPUP_CLOSE:
722 case HBMMENU_POPUP_RESTORE:
723 case HBMMENU_POPUP_MAXIMIZE:
724 case HBMMENU_POPUP_MINIMIZE:
726 FIXME("Magic 0x%08x not implemented\n", id);
730 if (GetObjectA(bmp, sizeof(bm), &bm ))
732 size->cx = bm.bmWidth;
733 size->cy = bm.bmHeight;
737 /***********************************************************************
738 * MENU_DrawBitmapItem
740 * Draw a bitmap item.
742 static void MENU_DrawBitmapItem( HDC hdc, MENUITEM *lpitem, const RECT *rect, BOOL menuBar )
747 HBITMAP bmp = (HBITMAP)lpitem->text;
748 int w = rect->right - rect->left;
749 int h = rect->bottom - rect->top;
753 /* Check if there is a magic menu item associated with this item */
754 if (lpitem->text && IS_MAGIC_ITEM(lpitem->text))
759 switch(LOWORD(lpitem->text))
762 if (lpitem->dwItemData)
764 bmp = (HBITMAP)lpitem->dwItemData;
765 if (!GetObjectA( bmp, sizeof(bm), &bm )) return;
770 if (!GetObjectA( bmp, sizeof(bm), &bm )) return;
771 /* only use right half of the bitmap */
772 bmp_xoffset = bm.bmWidth / 2;
773 bm.bmWidth -= bmp_xoffset;
776 case HBMMENU_MBAR_RESTORE:
777 flags = DFCS_CAPTIONRESTORE;
779 case HBMMENU_MBAR_MINIMIZE:
780 flags = DFCS_CAPTIONMIN;
782 case HBMMENU_MBAR_MINIMIZE_D:
783 flags = DFCS_CAPTIONMIN | DFCS_INACTIVE;
785 case HBMMENU_MBAR_CLOSE:
786 flags = DFCS_CAPTIONCLOSE;
788 case HBMMENU_MBAR_CLOSE_D:
789 flags = DFCS_CAPTIONCLOSE | DFCS_INACTIVE;
791 case HBMMENU_CALLBACK:
792 case HBMMENU_POPUP_CLOSE:
793 case HBMMENU_POPUP_RESTORE:
794 case HBMMENU_POPUP_MAXIMIZE:
795 case HBMMENU_POPUP_MINIMIZE:
797 FIXME("Magic 0x%08x not implemented\n", LOWORD(lpitem->text));
801 InflateRect( &r, -1, -1 );
802 if (lpitem->fState & MF_HILITE) flags |= DFCS_PUSHED;
803 DrawFrameControl( hdc, &r, DFC_CAPTION, flags );
807 if (!bmp || !GetObjectA( bmp, sizeof(bm), &bm )) return;
810 hdcMem = CreateCompatibleDC( hdc );
811 SelectObject( hdcMem, bmp );
813 /* handle fontsize > bitmap_height */
814 top = (h>bm.bmHeight) ? rect->top+(h-bm.bmHeight)/2 : rect->top;
816 if (TWEAK_WineLook == WIN95_LOOK) {
817 rop=((lpitem->fState & MF_HILITE) && !IS_MAGIC_ITEM(lpitem->text)) ? NOTSRCCOPY : SRCCOPY;
818 if ((lpitem->fState & MF_HILITE) && IS_BITMAP_ITEM(lpitem->fType))
819 SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT));
823 rop=((lpitem->fState & MF_HILITE) && !IS_MAGIC_ITEM(lpitem->text) && (!menuBar)) ? MERGEPAINT : SRCCOPY;
825 BitBlt( hdc, left, top, w, h, hdcMem, bmp_xoffset, 0, rop );
830 /***********************************************************************
833 * Calculate the size of the menu item and store it in lpitem->rect.
835 static void MENU_CalcItemSize( HDC hdc, MENUITEM *lpitem, HWND hwndOwner,
836 INT orgX, INT orgY, BOOL menuBar )
839 UINT check_bitmap_width = GetSystemMetrics( SM_CXMENUCHECK );
841 TRACE("dc=0x%04x owner=0x%04x (%d,%d)\n", hdc, hwndOwner, orgX, orgY);
842 debug_print_menuitem("MENU_CalcItemSize: menuitem:", lpitem,
843 (menuBar ? " (MenuBar)" : ""));
845 SetRect( &lpitem->rect, orgX, orgY, orgX, orgY );
847 if (lpitem->fType & MF_OWNERDRAW)
850 ** Experimentation under Windows reveals that an owner-drawn
851 ** menu is expected to return the size of the content part of
852 ** the menu item, not including the checkmark nor the submenu
853 ** arrow. Windows adds those values itself and returns the
854 ** enlarged rectangle on subsequent WM_DRAWITEM messages.
856 MEASUREITEMSTRUCT mis;
857 mis.CtlType = ODT_MENU;
859 mis.itemID = lpitem->wID;
860 mis.itemData = (DWORD)lpitem->dwItemData;
863 SendMessageA( hwndOwner, WM_MEASUREITEM, 0, (LPARAM)&mis );
864 lpitem->rect.right += mis.itemWidth;
868 lpitem->rect.right += MENU_BAR_ITEMS_SPACE;
871 /* under at least win95 you seem to be given a standard
872 height for the menu and the height value is ignored */
874 if (TWEAK_WineLook == WIN31_LOOK)
875 lpitem->rect.bottom += GetSystemMetrics(SM_CYMENU);
877 lpitem->rect.bottom += GetSystemMetrics(SM_CYMENU)-1;
880 lpitem->rect.bottom += mis.itemHeight;
882 TRACE("id=%04x size=%dx%d\n",
883 lpitem->wID, mis.itemWidth, mis.itemHeight);
884 /* Fall through to get check/arrow width calculation. */
887 if (lpitem->fType & MF_SEPARATOR)
889 lpitem->rect.bottom += SEPARATOR_HEIGHT;
895 lpitem->rect.right += 2 * check_bitmap_width;
896 if (lpitem->fType & MF_POPUP)
897 lpitem->rect.right += arrow_bitmap_width;
900 if (lpitem->fType & MF_OWNERDRAW)
903 if (IS_BITMAP_ITEM(lpitem->fType))
907 MENU_GetBitmapItemSize( (int)lpitem->text, lpitem->dwItemData, &size );
908 lpitem->rect.right += size.cx;
909 lpitem->rect.bottom += size.cy;
910 if (TWEAK_WineLook == WIN98_LOOK)
912 /* Leave space for the sunken border */
913 lpitem->rect.right += 2;
914 lpitem->rect.bottom += 2;
919 /* it must be a text item - unless it's the system menu */
920 if (!(lpitem->fType & MF_SYSMENU) && IS_STRING_ITEM( lpitem->fType ))
923 GetTextExtentPoint32W(hdc, lpitem->text, strlenW(lpitem->text), &size);
925 lpitem->rect.right += size.cx;
926 if (TWEAK_WineLook == WIN31_LOOK)
927 lpitem->rect.bottom += max( size.cy, GetSystemMetrics(SM_CYMENU) );
929 lpitem->rect.bottom += max(size.cy, GetSystemMetrics(SM_CYMENU)-1);
934 lpitem->rect.right += MENU_BAR_ITEMS_SPACE;
936 else if ((p = strchrW( lpitem->text, '\t' )) != NULL)
938 /* Item contains a tab (only meaningful in popup menus) */
939 GetTextExtentPoint32W(hdc, lpitem->text, (int)(p - lpitem->text) , &size);
940 lpitem->xTab = check_bitmap_width + MENU_TAB_SPACE + size.cx;
941 lpitem->rect.right += MENU_TAB_SPACE;
945 if (strchrW( lpitem->text, '\b' ))
946 lpitem->rect.right += MENU_TAB_SPACE;
947 lpitem->xTab = lpitem->rect.right - check_bitmap_width
948 - arrow_bitmap_width;
951 TRACE("(%d,%d)-(%d,%d)\n", lpitem->rect.left, lpitem->rect.top, lpitem->rect.right, lpitem->rect.bottom);
955 /***********************************************************************
956 * MENU_PopupMenuCalcSize
958 * Calculate the size of a popup menu.
960 static void MENU_PopupMenuCalcSize( LPPOPUPMENU lppop, HWND hwndOwner )
965 int orgX, orgY, maxX, maxTab, maxTabWidth;
967 lppop->Width = lppop->Height = 0;
968 if (lppop->nItems == 0) return;
971 SelectObject( hdc, hMenuFont);
974 maxX = (TWEAK_WineLook == WIN31_LOOK) ? GetSystemMetrics(SM_CXBORDER) : 2+1 ;
976 while (start < lppop->nItems)
978 lpitem = &lppop->items[start];
980 orgY = (TWEAK_WineLook == WIN31_LOOK) ? GetSystemMetrics(SM_CYBORDER) : 2;
982 maxTab = maxTabWidth = 0;
984 /* Parse items until column break or end of menu */
985 for (i = start; i < lppop->nItems; i++, lpitem++)
988 (lpitem->fType & (MF_MENUBREAK | MF_MENUBARBREAK))) break;
990 MENU_CalcItemSize( hdc, lpitem, hwndOwner, orgX, orgY, FALSE );
992 if (lpitem->fType & MF_MENUBARBREAK) orgX++;
993 maxX = max( maxX, lpitem->rect.right );
994 orgY = lpitem->rect.bottom;
995 if (IS_STRING_ITEM(lpitem->fType) && lpitem->xTab)
997 maxTab = max( maxTab, lpitem->xTab );
998 maxTabWidth = max(maxTabWidth,lpitem->rect.right-lpitem->xTab);
1002 /* Finish the column (set all items to the largest width found) */
1003 maxX = max( maxX, maxTab + maxTabWidth );
1004 for (lpitem = &lppop->items[start]; start < i; start++, lpitem++)
1006 lpitem->rect.right = maxX;
1007 if (IS_STRING_ITEM(lpitem->fType) && lpitem->xTab)
1008 lpitem->xTab = maxTab;
1011 lppop->Height = max( lppop->Height, orgY );
1014 lppop->Width = maxX;
1016 /* space for 3d border */
1017 if(TWEAK_WineLook > WIN31_LOOK)
1023 ReleaseDC( 0, hdc );
1027 /***********************************************************************
1028 * MENU_MenuBarCalcSize
1030 * FIXME: Word 6 implements its own MDI and its own 'close window' bitmap
1031 * height is off by 1 pixel which causes lengthy window relocations when
1032 * active document window is maximized/restored.
1034 * Calculate the size of the menu bar.
1036 static void MENU_MenuBarCalcSize( HDC hdc, LPRECT lprect,
1037 LPPOPUPMENU lppop, HWND hwndOwner )
1040 int start, i, orgX, orgY, maxY, helpPos;
1042 if ((lprect == NULL) || (lppop == NULL)) return;
1043 if (lppop->nItems == 0) return;
1044 TRACE("left=%d top=%d right=%d bottom=%d\n",
1045 lprect->left, lprect->top, lprect->right, lprect->bottom);
1046 lppop->Width = lprect->right - lprect->left;
1048 maxY = lprect->top+1;
1051 while (start < lppop->nItems)
1053 lpitem = &lppop->items[start];
1054 orgX = lprect->left;
1057 /* Parse items until line break or end of menu */
1058 for (i = start; i < lppop->nItems; i++, lpitem++)
1060 if ((helpPos == -1) && (lpitem->fType & MF_RIGHTJUSTIFY)) helpPos = i;
1062 (lpitem->fType & (MF_MENUBREAK | MF_MENUBARBREAK))) break;
1064 TRACE("calling MENU_CalcItemSize org=(%d, %d)\n",
1066 debug_print_menuitem (" item: ", lpitem, "");
1067 MENU_CalcItemSize( hdc, lpitem, hwndOwner, orgX, orgY, TRUE );
1069 if (lpitem->rect.right > lprect->right)
1071 if (i != start) break;
1072 else lpitem->rect.right = lprect->right;
1074 maxY = max( maxY, lpitem->rect.bottom );
1075 orgX = lpitem->rect.right;
1078 /* Finish the line (set all items to the largest height found) */
1079 while (start < i) lppop->items[start++].rect.bottom = maxY;
1082 lprect->bottom = maxY;
1083 lppop->Height = lprect->bottom - lprect->top;
1085 /* Flush right all items between the MF_RIGHTJUSTIFY and */
1086 /* the last item (if several lines, only move the last line) */
1087 lpitem = &lppop->items[lppop->nItems-1];
1088 orgY = lpitem->rect.top;
1089 orgX = lprect->right;
1090 for (i = lppop->nItems - 1; i >= helpPos; i--, lpitem--) {
1091 if ( (helpPos==-1) || (helpPos>i) )
1093 if (lpitem->rect.top != orgY) break; /* Other line */
1094 if (lpitem->rect.right >= orgX) break; /* Too far right already */
1095 lpitem->rect.left += orgX - lpitem->rect.right;
1096 lpitem->rect.right = orgX;
1097 orgX = lpitem->rect.left;
1101 /***********************************************************************
1104 * Draw a single menu item.
1106 static void MENU_DrawMenuItem( HWND hwnd, HMENU hmenu, HWND hwndOwner, HDC hdc, MENUITEM *lpitem,
1107 UINT height, BOOL menuBar, UINT odaction )
1111 debug_print_menuitem("MENU_DrawMenuItem: ", lpitem, "");
1113 if (lpitem->fType & MF_SYSMENU)
1115 if( !IsIconic(hwnd) ) {
1116 if (TWEAK_WineLook > WIN31_LOOK)
1117 NC_DrawSysButton95( hwnd, hdc,
1119 (MF_HILITE | MF_MOUSESELECT) );
1121 NC_DrawSysButton( hwnd, hdc,
1123 (MF_HILITE | MF_MOUSESELECT) );
1129 if (lpitem->fType & MF_OWNERDRAW)
1132 ** Experimentation under Windows reveals that an owner-drawn
1133 ** menu is given the rectangle which includes the space it requested
1134 ** in its response to WM_MEASUREITEM _plus_ width for a checkmark
1135 ** and a popup-menu arrow. This is the value of lpitem->rect.
1136 ** Windows will leave all drawing to the application except for
1137 ** the popup-menu arrow. Windows always draws that itself, after
1138 ** the menu owner has finished drawing.
1142 dis.CtlType = ODT_MENU;
1144 dis.itemID = lpitem->wID;
1145 dis.itemData = (DWORD)lpitem->dwItemData;
1147 if (lpitem->fState & MF_CHECKED) dis.itemState |= ODS_CHECKED;
1148 if (lpitem->fState & MF_GRAYED) dis.itemState |= ODS_GRAYED;
1149 if (lpitem->fState & MF_HILITE) dis.itemState |= ODS_SELECTED;
1150 dis.itemAction = odaction; /* ODA_DRAWENTIRE | ODA_SELECT | ODA_FOCUS; */
1151 dis.hwndItem = (HWND)hmenu;
1153 dis.rcItem = lpitem->rect;
1154 TRACE("Ownerdraw: owner=%04x itemID=%d, itemState=%d, itemAction=%d, "
1155 "hwndItem=%04x, hdc=%04x, rcItem={%d,%d,%d,%d}\n", hwndOwner,
1156 dis.itemID, dis.itemState, dis.itemAction, dis.hwndItem,
1157 dis.hDC, dis.rcItem.left, dis.rcItem.top, dis.rcItem.right,
1159 SendMessageA( hwndOwner, WM_DRAWITEM, 0, (LPARAM)&dis );
1160 /* Fall through to draw popup-menu arrow */
1163 TRACE("rect={%d,%d,%d,%d}\n", lpitem->rect.left, lpitem->rect.top,
1164 lpitem->rect.right,lpitem->rect.bottom);
1166 if (menuBar && (lpitem->fType & MF_SEPARATOR)) return;
1168 rect = lpitem->rect;
1170 if (!(lpitem->fType & MF_OWNERDRAW))
1172 if (lpitem->fState & MF_HILITE)
1174 if(TWEAK_WineLook == WIN98_LOOK)
1177 DrawEdge(hdc, &rect, BDR_SUNKENOUTER, BF_RECT);
1179 FillRect(hdc, &rect, GetSysColorBrush(COLOR_HIGHLIGHT));
1181 else /* Not Win98 Look */
1183 if(!IS_BITMAP_ITEM(lpitem->fType))
1184 FillRect(hdc, &rect, GetSysColorBrush(COLOR_HIGHLIGHT));
1188 FillRect( hdc, &rect, GetSysColorBrush(COLOR_MENU) );
1191 SetBkMode( hdc, TRANSPARENT );
1193 if (!(lpitem->fType & MF_OWNERDRAW))
1195 /* vertical separator */
1196 if (!menuBar && (lpitem->fType & MF_MENUBARBREAK))
1198 if (TWEAK_WineLook > WIN31_LOOK)
1202 rc.bottom = height - 3;
1203 DrawEdge (hdc, &rc, EDGE_ETCHED, BF_LEFT);
1207 SelectObject( hdc, GetSysColorPen(COLOR_WINDOWFRAME) );
1208 MoveToEx( hdc, rect.left, 0, NULL );
1209 LineTo( hdc, rect.left, height );
1213 /* horizontal separator */
1214 if (lpitem->fType & MF_SEPARATOR)
1216 if (TWEAK_WineLook > WIN31_LOOK)
1221 rc.top += SEPARATOR_HEIGHT / 2;
1222 DrawEdge (hdc, &rc, EDGE_ETCHED, BF_TOP);
1226 SelectObject( hdc, GetSysColorPen(COLOR_WINDOWFRAME) );
1227 MoveToEx( hdc, rect.left, rect.top + SEPARATOR_HEIGHT/2, NULL );
1228 LineTo( hdc, rect.right, rect.top + SEPARATOR_HEIGHT/2 );
1236 if (lpitem->fState & MF_HILITE)
1238 if(TWEAK_WineLook == WIN98_LOOK)
1241 SetTextColor(hdc, GetSysColor(COLOR_MENUTEXT));
1242 SetBkColor(hdc, GetSysColor(COLOR_MENU));
1244 if(lpitem->fState & MF_GRAYED)
1245 SetTextColor(hdc, GetSysColor(COLOR_GRAYTEXT));
1247 SetTextColor(hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
1248 SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT));
1251 else /* Not Win98 Look */
1253 SetTextColor(hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
1254 if(!IS_BITMAP_ITEM(lpitem->fType))
1255 SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT));
1260 if (lpitem->fState & MF_GRAYED)
1261 SetTextColor( hdc, GetSysColor( COLOR_GRAYTEXT ) );
1263 SetTextColor( hdc, GetSysColor( COLOR_MENUTEXT ) );
1264 SetBkColor( hdc, GetSysColor( COLOR_MENU ) );
1267 /* helper lines for debugging */
1268 /* FrameRect(hdc, &rect, GetStockObject(BLACK_BRUSH));
1269 SelectObject( hdc, GetSysColorPen(COLOR_WINDOWFRAME) );
1270 MoveToEx( hdc, rect.left, (rect.top + rect.bottom)/2, NULL );
1271 LineTo( hdc, rect.right, (rect.top + rect.bottom)/2 );
1276 INT y = rect.top + rect.bottom;
1277 UINT check_bitmap_width = GetSystemMetrics( SM_CXMENUCHECK );
1278 UINT check_bitmap_height = GetSystemMetrics( SM_CYMENUCHECK );
1280 if (!(lpitem->fType & MF_OWNERDRAW))
1282 /* Draw the check mark
1285 * Custom checkmark bitmaps are monochrome but not always 1bpp.
1287 HBITMAP bm = (lpitem->fState & MF_CHECKED) ? lpitem->hCheckBit : lpitem->hUnCheckBit;
1288 if (bm) /* we have a custom bitmap */
1290 HDC hdcMem = CreateCompatibleDC( hdc );
1291 SelectObject( hdcMem, bm );
1292 BitBlt( hdc, rect.left, (y - check_bitmap_height) / 2,
1293 check_bitmap_width, check_bitmap_height,
1294 hdcMem, 0, 0, SRCCOPY );
1297 else if (lpitem->fState & MF_CHECKED) /* standard bitmaps */
1300 HBITMAP bm = CreateBitmap( check_bitmap_width, check_bitmap_height, 1, 1, NULL );
1301 HDC hdcMem = CreateCompatibleDC( hdc );
1302 SelectObject( hdcMem, bm );
1303 SetRect( &r, 0, 0, check_bitmap_width, check_bitmap_height );
1304 DrawFrameControl( hdcMem, &r, DFC_MENU,
1305 (lpitem->fType & MFT_RADIOCHECK) ?
1306 DFCS_MENUBULLET : DFCS_MENUCHECK );
1307 BitBlt( hdc, rect.left, (y - r.bottom) / 2, r.right, r.bottom,
1308 hdcMem, 0, 0, SRCCOPY );
1314 /* Draw the popup-menu arrow */
1315 if (lpitem->fType & MF_POPUP)
1317 HDC hdcMem = CreateCompatibleDC( hdc );
1318 HBITMAP hOrigBitmap;
1320 hOrigBitmap = SelectObject( hdcMem, hStdMnArrow );
1321 BitBlt( hdc, rect.right - arrow_bitmap_width - 1,
1322 (y - arrow_bitmap_height) / 2,
1323 arrow_bitmap_width, arrow_bitmap_height,
1324 hdcMem, 0, 0, SRCCOPY );
1325 SelectObject( hdcMem, hOrigBitmap );
1329 rect.left += check_bitmap_width;
1330 rect.right -= arrow_bitmap_width;
1333 /* Done for owner-drawn */
1334 if (lpitem->fType & MF_OWNERDRAW)
1337 /* Draw the item text or bitmap */
1338 if (IS_BITMAP_ITEM(lpitem->fType))
1340 MENU_DrawBitmapItem( hdc, lpitem, &rect, menuBar );
1344 /* No bitmap - process text if present */
1345 else if (IS_STRING_ITEM(lpitem->fType))
1350 UINT uFormat = (menuBar) ?
1351 DT_CENTER | DT_VCENTER | DT_SINGLELINE :
1352 DT_LEFT | DT_VCENTER | DT_SINGLELINE;
1354 if ( lpitem->fState & MFS_DEFAULT )
1356 hfontOld = SelectObject( hdc, hMenuFontBold);
1361 rect.left += MENU_BAR_ITEMS_SPACE / 2;
1362 rect.right -= MENU_BAR_ITEMS_SPACE / 2;
1363 i = strlenW( lpitem->text );
1367 for (i = 0; lpitem->text[i]; i++)
1368 if ((lpitem->text[i] == '\t') || (lpitem->text[i] == '\b'))
1372 if( !(TWEAK_WineLook == WIN31_LOOK) && (lpitem->fState & MF_GRAYED))
1374 if (!(lpitem->fState & MF_HILITE) )
1376 ++rect.left; ++rect.top; ++rect.right; ++rect.bottom;
1377 SetTextColor(hdc, RGB(0xff, 0xff, 0xff));
1378 DrawTextW( hdc, lpitem->text, i, &rect, uFormat );
1379 --rect.left; --rect.top; --rect.right; --rect.bottom;
1381 SetTextColor(hdc, RGB(0x80, 0x80, 0x80));
1384 DrawTextW( hdc, lpitem->text, i, &rect, uFormat);
1386 /* paint the shortcut text */
1387 if (lpitem->text[i]) /* There's a tab or flush-right char */
1389 if (lpitem->text[i] == '\t')
1391 rect.left = lpitem->xTab;
1392 uFormat = DT_LEFT | DT_VCENTER | DT_SINGLELINE;
1396 uFormat = DT_RIGHT | DT_VCENTER | DT_SINGLELINE;
1399 if( !(TWEAK_WineLook == WIN31_LOOK) && (lpitem->fState & MF_GRAYED))
1401 if (!(lpitem->fState & MF_HILITE) )
1403 ++rect.left; ++rect.top; ++rect.right; ++rect.bottom;
1404 SetTextColor(hdc, RGB(0xff, 0xff, 0xff));
1405 DrawTextW( hdc, lpitem->text + i + 1, -1, &rect, uFormat );
1406 --rect.left; --rect.top; --rect.right; --rect.bottom;
1408 SetTextColor(hdc, RGB(0x80, 0x80, 0x80));
1410 DrawTextW( hdc, lpitem->text + i + 1, -1, &rect, uFormat );
1414 SelectObject (hdc, hfontOld);
1419 /***********************************************************************
1420 * MENU_DrawPopupMenu
1422 * Paint a popup menu.
1424 static void MENU_DrawPopupMenu( HWND hwnd, HDC hdc, HMENU hmenu )
1426 HBRUSH hPrevBrush = 0;
1429 TRACE("wnd=0x%04x dc=0x%04x menu=0x%04x\n", hwnd, hdc, hmenu);
1431 GetClientRect( hwnd, &rect );
1433 if(TWEAK_WineLook == WIN31_LOOK)
1435 rect.bottom -= POPUP_YSHADE * GetSystemMetrics(SM_CYBORDER);
1436 rect.right -= POPUP_XSHADE * GetSystemMetrics(SM_CXBORDER);
1439 if((hPrevBrush = SelectObject( hdc, GetSysColorBrush(COLOR_MENU) ))
1440 && (SelectObject( hdc, hMenuFont)))
1444 Rectangle( hdc, rect.left, rect.top, rect.right, rect.bottom );
1446 hPrevPen = SelectObject( hdc, GetStockObject( NULL_PEN ) );
1452 /* draw 3-d shade */
1453 if(TWEAK_WineLook == WIN31_LOOK) {
1454 SelectObject( hdc, hShadeBrush );
1455 SetBkMode( hdc, TRANSPARENT );
1456 ropPrev = SetROP2( hdc, R2_MASKPEN );
1458 i = rect.right; /* why SetBrushOrg() doesn't? */
1459 PatBlt( hdc, i & 0xfffffffe,
1460 rect.top + POPUP_YSHADE*GetSystemMetrics(SM_CYBORDER),
1461 i%2 + POPUP_XSHADE*GetSystemMetrics(SM_CXBORDER),
1462 rect.bottom - rect.top, 0x00a000c9 );
1464 PatBlt( hdc, rect.left + POPUP_XSHADE*GetSystemMetrics(SM_CXBORDER),
1465 i & 0xfffffffe,rect.right - rect.left,
1466 i%2 + POPUP_YSHADE*GetSystemMetrics(SM_CYBORDER), 0x00a000c9 );
1467 SelectObject( hdc, hPrevPen );
1468 SelectObject( hdc, hPrevBrush );
1469 SetROP2( hdc, ropPrev );
1472 DrawEdge (hdc, &rect, EDGE_RAISED, BF_RECT);
1474 /* draw menu items */
1476 menu = MENU_GetMenu( hmenu );
1477 if (menu && menu->nItems)
1482 for (u = menu->nItems, item = menu->items; u > 0; u--, item++)
1483 MENU_DrawMenuItem( hwnd, hmenu, menu->hwndOwner, hdc, item,
1484 menu->Height, FALSE, ODA_DRAWENTIRE );
1489 SelectObject( hdc, hPrevBrush );
1494 /***********************************************************************
1497 * Paint a menu bar. Returns the height of the menu bar.
1498 * called from [windows/nonclient.c]
1500 UINT MENU_DrawMenuBar( HDC hDC, LPRECT lprect, HWND hwnd,
1506 HMENU hMenu = GetMenu(hwnd);
1508 lppop = MENU_GetMenu( hMenu );
1509 if (lppop == NULL || lprect == NULL)
1511 retvalue = GetSystemMetrics(SM_CYMENU);
1515 TRACE("(%04x, %p, %p)\n", hDC, lprect, lppop);
1517 hfontOld = SelectObject( hDC, hMenuFont);
1519 if (lppop->Height == 0)
1520 MENU_MenuBarCalcSize(hDC, lprect, lppop, hwnd);
1522 lprect->bottom = lprect->top + lppop->Height;
1526 retvalue = lppop->Height;
1530 FillRect(hDC, lprect, GetSysColorBrush(COLOR_MENU) );
1532 if (TWEAK_WineLook == WIN31_LOOK)
1534 SelectObject( hDC, GetSysColorPen(COLOR_WINDOWFRAME) );
1535 MoveToEx( hDC, lprect->left, lprect->bottom, NULL );
1536 LineTo( hDC, lprect->right, lprect->bottom );
1540 SelectObject( hDC, GetSysColorPen(COLOR_3DFACE));
1541 MoveToEx( hDC, lprect->left, lprect->bottom, NULL );
1542 LineTo( hDC, lprect->right, lprect->bottom );
1545 if (lppop->nItems == 0)
1547 retvalue = GetSystemMetrics(SM_CYMENU);
1551 for (i = 0; i < lppop->nItems; i++)
1553 MENU_DrawMenuItem( hwnd, hMenu, hwnd,
1554 hDC, &lppop->items[i], lppop->Height, TRUE, ODA_DRAWENTIRE );
1556 retvalue = lppop->Height;
1559 if (hfontOld) SelectObject (hDC, hfontOld);
1564 /***********************************************************************
1567 * Display a popup menu.
1569 static BOOL MENU_ShowPopup( HWND hwndOwner, HMENU hmenu, UINT id,
1570 INT x, INT y, INT xanchor, INT yanchor )
1575 TRACE("owner=0x%04x hmenu=0x%04x id=0x%04x x=0x%04x y=0x%04x xa=0x%04x ya=0x%04x\n",
1576 hwndOwner, hmenu, id, x, y, xanchor, yanchor);
1578 if (!(menu = MENU_GetMenu( hmenu ))) return FALSE;
1579 if (menu->FocusedItem != NO_SELECTED_ITEM)
1581 menu->items[menu->FocusedItem].fState &= ~(MF_HILITE|MF_MOUSESELECT);
1582 menu->FocusedItem = NO_SELECTED_ITEM;
1585 /* store the owner for DrawItem */
1586 menu->hwndOwner = hwndOwner;
1589 MENU_PopupMenuCalcSize( menu, hwndOwner );
1591 /* adjust popup menu pos so that it fits within the desktop */
1593 width = menu->Width + GetSystemMetrics(SM_CXBORDER);
1594 height = menu->Height + GetSystemMetrics(SM_CYBORDER);
1596 if( x + width > GetSystemMetrics(SM_CXSCREEN ))
1599 x -= width - xanchor;
1600 if( x + width > GetSystemMetrics(SM_CXSCREEN))
1601 x = GetSystemMetrics(SM_CXSCREEN) - width;
1605 if( y + height > GetSystemMetrics(SM_CYSCREEN ))
1608 y -= height + yanchor;
1609 if( y + height > GetSystemMetrics(SM_CYSCREEN ))
1610 y = GetSystemMetrics(SM_CYSCREEN) - height;
1614 if( TWEAK_WineLook == WIN31_LOOK )
1616 width += POPUP_XSHADE * GetSystemMetrics(SM_CXBORDER); /* add space for shading */
1617 height += POPUP_YSHADE * GetSystemMetrics(SM_CYBORDER);
1620 /* NOTE: In Windows, top menu popup is not owned. */
1621 menu->hWnd = CreateWindowA( POPUPMENU_CLASS_ATOM, NULL,
1622 WS_POPUP, x, y, width, height,
1623 hwndOwner, 0, GetWindowLongA(hwndOwner,GWL_HINSTANCE),
1625 if( !menu->hWnd ) return FALSE;
1626 if (!top_popup) top_popup = menu->hWnd;
1628 /* Display the window */
1630 SetWindowPos( menu->hWnd, HWND_TOP, 0, 0, 0, 0,
1631 SWP_SHOWWINDOW | SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE );
1632 UpdateWindow( menu->hWnd );
1637 /***********************************************************************
1640 static void MENU_SelectItem( HWND hwndOwner, HMENU hmenu, UINT wIndex,
1641 BOOL sendMenuSelect, HMENU topmenu )
1646 TRACE("owner=0x%04x menu=0x%04x index=0x%04x select=0x%04x\n", hwndOwner, hmenu, wIndex, sendMenuSelect);
1648 lppop = MENU_GetMenu( hmenu );
1649 if ((!lppop) || (!lppop->nItems) || (!lppop->hWnd)) return;
1651 if (lppop->FocusedItem == wIndex) return;
1652 if (lppop->wFlags & MF_POPUP) hdc = GetDC( lppop->hWnd );
1653 else hdc = GetDCEx( lppop->hWnd, 0, DCX_CACHE | DCX_WINDOW);
1655 SelectObject( hdc, hMenuFont);
1657 /* Clear previous highlighted item */
1658 if (lppop->FocusedItem != NO_SELECTED_ITEM)
1660 lppop->items[lppop->FocusedItem].fState &= ~(MF_HILITE|MF_MOUSESELECT);
1661 MENU_DrawMenuItem(lppop->hWnd, hmenu, hwndOwner, hdc,&lppop->items[lppop->FocusedItem],
1662 lppop->Height, !(lppop->wFlags & MF_POPUP),
1666 /* Highlight new item (if any) */
1667 lppop->FocusedItem = wIndex;
1668 if (lppop->FocusedItem != NO_SELECTED_ITEM)
1670 if(!(lppop->items[wIndex].fType & MF_SEPARATOR)) {
1671 lppop->items[wIndex].fState |= MF_HILITE;
1672 MENU_DrawMenuItem( lppop->hWnd, hmenu, hwndOwner, hdc,
1673 &lppop->items[wIndex], lppop->Height,
1674 !(lppop->wFlags & MF_POPUP), ODA_SELECT );
1678 MENUITEM *ip = &lppop->items[lppop->FocusedItem];
1679 SendMessageA( hwndOwner, WM_MENUSELECT,
1680 MAKELONG(ip->fType & MF_POPUP ? wIndex: ip->wID,
1681 ip->fType | ip->fState | MF_MOUSESELECT |
1682 (lppop->wFlags & MF_SYSMENU)), hmenu);
1685 else if (sendMenuSelect) {
1688 if((pos=MENU_FindSubMenu(&topmenu, hmenu))!=NO_SELECTED_ITEM){
1689 POPUPMENU *ptm = MENU_GetMenu( topmenu );
1690 MENUITEM *ip = &ptm->items[pos];
1691 SendMessageA( hwndOwner, WM_MENUSELECT, MAKELONG(pos,
1692 ip->fType | ip->fState | MF_MOUSESELECT |
1693 (ptm->wFlags & MF_SYSMENU)), topmenu);
1697 ReleaseDC( lppop->hWnd, hdc );
1701 /***********************************************************************
1702 * MENU_MoveSelection
1704 * Moves currently selected item according to the offset parameter.
1705 * If there is no selection then it should select the last item if
1706 * offset is ITEM_PREV or the first item if offset is ITEM_NEXT.
1708 static void MENU_MoveSelection( HWND hwndOwner, HMENU hmenu, INT offset )
1713 TRACE("hwnd=0x%04x hmenu=0x%04x off=0x%04x\n", hwndOwner, hmenu, offset);
1715 menu = MENU_GetMenu( hmenu );
1716 if ((!menu) || (!menu->items)) return;
1718 if ( menu->FocusedItem != NO_SELECTED_ITEM )
1720 if( menu->nItems == 1 ) return; else
1721 for (i = menu->FocusedItem + offset ; i >= 0 && i < menu->nItems
1723 if (!(menu->items[i].fType & MF_SEPARATOR))
1725 MENU_SelectItem( hwndOwner, hmenu, i, TRUE, 0 );
1730 for ( i = (offset > 0) ? 0 : menu->nItems - 1;
1731 i >= 0 && i < menu->nItems ; i += offset)
1732 if (!(menu->items[i].fType & MF_SEPARATOR))
1734 MENU_SelectItem( hwndOwner, hmenu, i, TRUE, 0 );
1740 /**********************************************************************
1743 * Set an item flags, id and text ptr. Called by InsertMenu() and
1746 static BOOL MENU_SetItemData( MENUITEM *item, UINT flags, UINT id,
1749 LPWSTR prevText = IS_STRING_ITEM(item->fType) ? item->text : NULL;
1751 debug_print_menuitem("MENU_SetItemData from: ", item, "");
1752 TRACE("flags=%x str=%p\n", flags, str);
1754 if (IS_STRING_ITEM(flags))
1758 flags |= MF_SEPARATOR;
1764 /* Item beginning with a backspace is a help item */
1770 if (!(text = HeapAlloc( GetProcessHeap(), 0, (strlenW(str)+1) * sizeof(WCHAR) )))
1772 strcpyW( text, str );
1776 else if (IS_BITMAP_ITEM(flags))
1777 item->text = (LPWSTR)(HBITMAP)LOWORD(str);
1778 else item->text = NULL;
1780 if (flags & MF_OWNERDRAW)
1781 item->dwItemData = (DWORD)str;
1783 item->dwItemData = 0;
1785 if ((item->fType & MF_POPUP) && (flags & MF_POPUP) && (item->hSubMenu != id) )
1786 DestroyMenu( item->hSubMenu ); /* ModifyMenu() spec */
1788 if (flags & MF_POPUP)
1790 POPUPMENU *menu = MENU_GetMenu((UINT16)id);
1791 if (menu) menu->wFlags |= MF_POPUP;
1803 if (flags & MF_POPUP)
1804 item->hSubMenu = id;
1806 if ((item->fType & MF_POPUP) && !(flags & MF_POPUP) )
1807 flags |= MF_POPUP; /* keep popup */
1809 item->fType = flags & TYPE_MASK;
1810 item->fState = (flags & STATE_MASK) &
1811 ~(MF_HILITE | MF_MOUSESELECT | MF_BYPOSITION);
1814 /* Don't call SetRectEmpty here! */
1817 if (prevText) HeapFree( GetProcessHeap(), 0, prevText );
1819 debug_print_menuitem("MENU_SetItemData to : ", item, "");
1824 /**********************************************************************
1827 * Insert a new item into a menu.
1829 static MENUITEM *MENU_InsertItem( HMENU hMenu, UINT pos, UINT flags )
1834 if (!(menu = MENU_GetMenu(hMenu)))
1837 /* Find where to insert new item */
1839 if (flags & MF_BYPOSITION) {
1840 if (pos > menu->nItems)
1843 if (!MENU_FindItem( &hMenu, &pos, flags ))
1846 if (!(menu = MENU_GetMenu( hMenu )))
1851 /* Create new items array */
1853 newItems = HeapAlloc( GetProcessHeap(), 0, sizeof(MENUITEM) * (menu->nItems+1) );
1856 WARN("allocation failed\n" );
1859 if (menu->nItems > 0)
1861 /* Copy the old array into the new one */
1862 if (pos > 0) memcpy( newItems, menu->items, pos * sizeof(MENUITEM) );
1863 if (pos < menu->nItems) memcpy( &newItems[pos+1], &menu->items[pos],
1864 (menu->nItems-pos)*sizeof(MENUITEM) );
1865 HeapFree( GetProcessHeap(), 0, menu->items );
1867 menu->items = newItems;
1869 memset( &newItems[pos], 0, sizeof(*newItems) );
1870 menu->Height = 0; /* force size recalculate */
1871 return &newItems[pos];
1875 /**********************************************************************
1876 * MENU_ParseResource
1878 * Parse a standard menu resource and add items to the menu.
1879 * Return a pointer to the end of the resource.
1881 static LPCSTR MENU_ParseResource( LPCSTR res, HMENU hMenu, BOOL unicode )
1888 flags = GET_WORD(res);
1889 res += sizeof(WORD);
1890 if (!(flags & MF_POPUP))
1893 res += sizeof(WORD);
1895 if (!IS_STRING_ITEM(flags))
1896 ERR("not a string item %04x\n", flags );
1898 if (!unicode) res += strlen(str) + 1;
1899 else res += (strlenW((LPCWSTR)str) + 1) * sizeof(WCHAR);
1900 if (flags & MF_POPUP)
1902 HMENU hSubMenu = CreatePopupMenu();
1903 if (!hSubMenu) return NULL;
1904 if (!(res = MENU_ParseResource( res, hSubMenu, unicode )))
1906 if (!unicode) AppendMenuA( hMenu, flags, (UINT)hSubMenu, str );
1907 else AppendMenuW( hMenu, flags, (UINT)hSubMenu, (LPCWSTR)str );
1909 else /* Not a popup */
1911 if (!unicode) AppendMenuA( hMenu, flags, id, *str ? str : NULL );
1912 else AppendMenuW( hMenu, flags, id,
1913 *(LPCWSTR)str ? (LPCWSTR)str : NULL );
1915 } while (!(flags & MF_END));
1920 /**********************************************************************
1921 * MENUEX_ParseResource
1923 * Parse an extended menu resource and add items to the menu.
1924 * Return a pointer to the end of the resource.
1926 static LPCSTR MENUEX_ParseResource( LPCSTR res, HMENU hMenu)
1932 mii.cbSize = sizeof(mii);
1933 mii.fMask = MIIM_STATE | MIIM_ID | MIIM_TYPE;
1934 mii.fType = GET_DWORD(res);
1935 res += sizeof(DWORD);
1936 mii.fState = GET_DWORD(res);
1937 res += sizeof(DWORD);
1938 mii.wID = GET_DWORD(res);
1939 res += sizeof(DWORD);
1940 resinfo = GET_WORD(res); /* FIXME: for 16-bit apps this is a byte. */
1941 res += sizeof(WORD);
1942 /* Align the text on a word boundary. */
1943 res += (~((int)res - 1)) & 1;
1944 mii.dwTypeData = (LPWSTR) res;
1945 res += (1 + strlenW(mii.dwTypeData)) * sizeof(WCHAR);
1946 /* Align the following fields on a dword boundary. */
1947 res += (~((int)res - 1)) & 3;
1949 TRACE("Menu item: [%08x,%08x,%04x,%04x,%s]\n",
1950 mii.fType, mii.fState, mii.wID, resinfo, debugstr_w(mii.dwTypeData));
1952 if (resinfo & 1) { /* Pop-up? */
1953 /* DWORD helpid = GET_DWORD(res); FIXME: use this. */
1954 res += sizeof(DWORD);
1955 mii.hSubMenu = CreatePopupMenu();
1958 if (!(res = MENUEX_ParseResource(res, mii.hSubMenu))) {
1959 DestroyMenu(mii.hSubMenu);
1962 mii.fMask |= MIIM_SUBMENU;
1963 mii.fType |= MF_POPUP;
1965 InsertMenuItemW(hMenu, -1, MF_BYPOSITION, &mii);
1966 } while (!(resinfo & MF_END));
1971 /***********************************************************************
1974 * Return the handle of the selected sub-popup menu (if any).
1976 static HMENU MENU_GetSubPopup( HMENU hmenu )
1981 menu = MENU_GetMenu( hmenu );
1983 if ((!menu) || (menu->FocusedItem == NO_SELECTED_ITEM)) return 0;
1985 item = &menu->items[menu->FocusedItem];
1986 if ((item->fType & MF_POPUP) && (item->fState & MF_MOUSESELECT))
1987 return item->hSubMenu;
1992 /***********************************************************************
1993 * MENU_HideSubPopups
1995 * Hide the sub-popup menus of this menu.
1997 static void MENU_HideSubPopups( HWND hwndOwner, HMENU hmenu,
1998 BOOL sendMenuSelect )
2000 POPUPMENU *menu = MENU_GetMenu( hmenu );
2002 TRACE("owner=0x%04x hmenu=0x%04x 0x%04x\n", hwndOwner, hmenu, sendMenuSelect);
2004 if (menu && top_popup)
2010 if (menu->FocusedItem != NO_SELECTED_ITEM)
2012 item = &menu->items[menu->FocusedItem];
2013 if (!(item->fType & MF_POPUP) ||
2014 !(item->fState & MF_MOUSESELECT)) return;
2015 item->fState &= ~MF_MOUSESELECT;
2016 hsubmenu = item->hSubMenu;
2019 submenu = MENU_GetMenu( hsubmenu );
2020 MENU_HideSubPopups( hwndOwner, hsubmenu, FALSE );
2021 MENU_SelectItem( hwndOwner, hsubmenu, NO_SELECTED_ITEM, sendMenuSelect, 0 );
2022 DestroyWindow( submenu->hWnd );
2028 /***********************************************************************
2031 * Display the sub-menu of the selected item of this menu.
2032 * Return the handle of the submenu, or hmenu if no submenu to display.
2034 static HMENU MENU_ShowSubPopup( HWND hwndOwner, HMENU hmenu,
2035 BOOL selectFirst, UINT wFlags )
2042 TRACE("owner=0x%04x hmenu=0x%04x 0x%04x\n", hwndOwner, hmenu, selectFirst);
2044 if (!(menu = MENU_GetMenu( hmenu ))) return hmenu;
2046 if (menu->FocusedItem == NO_SELECTED_ITEM) return hmenu;
2048 item = &menu->items[menu->FocusedItem];
2049 if (!(item->fType & MF_POPUP) || (item->fState & (MF_GRAYED | MF_DISABLED)))
2052 /* message must be sent before using item,
2053 because nearly everything may be changed by the application ! */
2055 /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
2056 if (!(wFlags & TPM_NONOTIFY))
2057 SendMessageA( hwndOwner, WM_INITMENUPOPUP, item->hSubMenu,
2058 MAKELONG( menu->FocusedItem, IS_SYSTEM_MENU(menu) ));
2060 item = &menu->items[menu->FocusedItem];
2063 /* correct item if modified as a reaction to WM_INITMENUPOPUP message */
2064 if (!(item->fState & MF_HILITE))
2066 if (menu->wFlags & MF_POPUP) hdc = GetDC( menu->hWnd );
2067 else hdc = GetDCEx( menu->hWnd, 0, DCX_CACHE | DCX_WINDOW);
2069 SelectObject( hdc, hMenuFont);
2071 item->fState |= MF_HILITE;
2072 MENU_DrawMenuItem( menu->hWnd, hmenu, hwndOwner, hdc, item, menu->Height, !(menu->wFlags & MF_POPUP), ODA_DRAWENTIRE );
2073 ReleaseDC( menu->hWnd, hdc );
2075 if (!item->rect.top && !item->rect.left && !item->rect.bottom && !item->rect.right)
2078 item->fState |= MF_MOUSESELECT;
2080 if (IS_SYSTEM_MENU(menu))
2082 MENU_InitSysMenuPopup(item->hSubMenu,
2083 GetWindowLongA( menu->hWnd, GWL_STYLE ),
2084 GetClassLongA( menu->hWnd, GCL_STYLE));
2086 NC_GetSysPopupPos( menu->hWnd, &rect );
2087 rect.top = rect.bottom;
2088 rect.right = GetSystemMetrics(SM_CXSIZE);
2089 rect.bottom = GetSystemMetrics(SM_CYSIZE);
2093 GetWindowRect( menu->hWnd, &rect );
2094 if (menu->wFlags & MF_POPUP)
2096 rect.left += item->rect.right - GetSystemMetrics(SM_CXBORDER);
2097 rect.top += item->rect.top;
2098 rect.right = item->rect.left - item->rect.right + GetSystemMetrics(SM_CXBORDER);
2099 rect.bottom = item->rect.top - item->rect.bottom;
2103 rect.left += item->rect.left;
2104 rect.top += item->rect.bottom;
2105 rect.right = item->rect.right - item->rect.left;
2106 rect.bottom = item->rect.bottom - item->rect.top;
2110 MENU_ShowPopup( hwndOwner, item->hSubMenu, menu->FocusedItem,
2111 rect.left, rect.top, rect.right, rect.bottom );
2113 MENU_MoveSelection( hwndOwner, item->hSubMenu, ITEM_NEXT );
2114 return item->hSubMenu;
2119 /**********************************************************************
2122 BOOL MENU_IsMenuActive(void)
2124 return (top_popup != 0);
2127 /***********************************************************************
2130 * Walks menu chain trying to find a menu pt maps to.
2132 static HMENU MENU_PtMenu( HMENU hMenu, POINT pt )
2134 POPUPMENU *menu = MENU_GetMenu( hMenu );
2135 UINT item = menu->FocusedItem;
2138 /* try subpopup first (if any) */
2139 ret = (item != NO_SELECTED_ITEM &&
2140 (menu->items[item].fType & MF_POPUP) &&
2141 (menu->items[item].fState & MF_MOUSESELECT))
2142 ? MENU_PtMenu(menu->items[item].hSubMenu, pt) : 0;
2144 if (!ret) /* check the current window (avoiding WM_HITTEST) */
2146 INT ht = NC_HandleNCHitTest( menu->hWnd, pt );
2147 if( menu->wFlags & MF_POPUP )
2149 if (ht != HTNOWHERE && ht != HTERROR) ret = hMenu;
2151 else if (ht == HTSYSMENU)
2152 ret = get_win_sys_menu( menu->hWnd );
2153 else if (ht == HTMENU)
2154 ret = GetMenu( menu->hWnd );
2159 /***********************************************************************
2160 * MENU_ExecFocusedItem
2162 * Execute a menu item (for instance when user pressed Enter).
2163 * Return the wID of the executed item. Otherwise, -1 indicating
2164 * that no menu item was executed;
2165 * Have to receive the flags for the TrackPopupMenu options to avoid
2166 * sending unwanted message.
2169 static INT MENU_ExecFocusedItem( MTRACKER* pmt, HMENU hMenu, UINT wFlags )
2172 POPUPMENU *menu = MENU_GetMenu( hMenu );
2174 TRACE("%p hmenu=0x%04x\n", pmt, hMenu);
2176 if (!menu || !menu->nItems ||
2177 (menu->FocusedItem == NO_SELECTED_ITEM)) return -1;
2179 item = &menu->items[menu->FocusedItem];
2181 TRACE("%08x %08x %08x\n",
2182 hMenu, item->wID, item->hSubMenu);
2184 if (!(item->fType & MF_POPUP))
2186 if (!(item->fState & (MF_GRAYED | MF_DISABLED)) && !(item->fType & MF_SEPARATOR))
2188 /* If TPM_RETURNCMD is set you return the id, but
2189 do not send a message to the owner */
2190 if(!(wFlags & TPM_RETURNCMD))
2192 if( menu->wFlags & MF_SYSMENU )
2193 PostMessageA( pmt->hOwnerWnd, WM_SYSCOMMAND, item->wID,
2194 MAKELPARAM((INT16)pmt->pt.x, (INT16)pmt->pt.y) );
2196 PostMessageA( pmt->hOwnerWnd, WM_COMMAND, item->wID, 0 );
2202 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd, hMenu, TRUE, wFlags);
2207 /***********************************************************************
2208 * MENU_SwitchTracking
2210 * Helper function for menu navigation routines.
2212 static void MENU_SwitchTracking( MTRACKER* pmt, HMENU hPtMenu, UINT id )
2214 POPUPMENU *ptmenu = MENU_GetMenu( hPtMenu );
2215 POPUPMENU *topmenu = MENU_GetMenu( pmt->hTopMenu );
2217 TRACE("%p hmenu=0x%04x 0x%04x\n", pmt, hPtMenu, id);
2219 if( pmt->hTopMenu != hPtMenu &&
2220 !((ptmenu->wFlags | topmenu->wFlags) & MF_POPUP) )
2222 /* both are top level menus (system and menu-bar) */
2223 MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE );
2224 MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, NO_SELECTED_ITEM, FALSE, 0 );
2225 pmt->hTopMenu = hPtMenu;
2227 else MENU_HideSubPopups( pmt->hOwnerWnd, hPtMenu, FALSE );
2228 MENU_SelectItem( pmt->hOwnerWnd, hPtMenu, id, TRUE, 0 );
2232 /***********************************************************************
2235 * Return TRUE if we can go on with menu tracking.
2237 static BOOL MENU_ButtonDown( MTRACKER* pmt, HMENU hPtMenu, UINT wFlags )
2239 TRACE("%p hmenu=0x%04x\n", pmt, hPtMenu);
2244 POPUPMENU *ptmenu = MENU_GetMenu( hPtMenu );
2247 if( IS_SYSTEM_MENU(ptmenu) )
2248 item = ptmenu->items;
2250 item = MENU_FindItemByCoords( ptmenu, pmt->pt, &id );
2254 if( ptmenu->FocusedItem != id )
2255 MENU_SwitchTracking( pmt, hPtMenu, id );
2257 /* If the popup menu is not already "popped" */
2258 if(!(item->fState & MF_MOUSESELECT ))
2260 pmt->hCurrentMenu = MENU_ShowSubPopup( pmt->hOwnerWnd, hPtMenu, FALSE, wFlags );
2262 /* In win31, a newly popped menu always remains opened for the next buttonup */
2263 if(TWEAK_WineLook == WIN31_LOOK)
2264 ptmenu->bTimeToHide = FALSE;
2269 /* Else the click was on the menu bar, finish the tracking */
2274 /***********************************************************************
2277 * Return the value of MENU_ExecFocusedItem if
2278 * the selected item was not a popup. Else open the popup.
2279 * A -1 return value indicates that we go on with menu tracking.
2282 static INT MENU_ButtonUp( MTRACKER* pmt, HMENU hPtMenu, UINT wFlags)
2284 TRACE("%p hmenu=0x%04x\n", pmt, hPtMenu);
2289 POPUPMENU *ptmenu = MENU_GetMenu( hPtMenu );
2292 if( IS_SYSTEM_MENU(ptmenu) )
2293 item = ptmenu->items;
2295 item = MENU_FindItemByCoords( ptmenu, pmt->pt, &id );
2297 if( item && (ptmenu->FocusedItem == id ))
2299 if( !(item->fType & MF_POPUP) )
2300 return MENU_ExecFocusedItem( pmt, hPtMenu, wFlags);
2302 /* If we are dealing with the top-level menu */
2303 /* and this is a click on an already "popped" item: */
2304 /* Stop the menu tracking and close the opened submenus */
2305 if((pmt->hTopMenu == hPtMenu) && (ptmenu->bTimeToHide == TRUE))
2308 ptmenu->bTimeToHide = TRUE;
2314 /***********************************************************************
2317 * Return TRUE if we can go on with menu tracking.
2319 static BOOL MENU_MouseMove( MTRACKER* pmt, HMENU hPtMenu, UINT wFlags )
2321 UINT id = NO_SELECTED_ITEM;
2322 POPUPMENU *ptmenu = NULL;
2326 ptmenu = MENU_GetMenu( hPtMenu );
2327 if( IS_SYSTEM_MENU(ptmenu) )
2330 MENU_FindItemByCoords( ptmenu, pmt->pt, &id );
2333 if( id == NO_SELECTED_ITEM )
2335 MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
2336 NO_SELECTED_ITEM, TRUE, pmt->hTopMenu);
2339 else if( ptmenu->FocusedItem != id )
2341 MENU_SwitchTracking( pmt, hPtMenu, id );
2342 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd, hPtMenu, FALSE, wFlags);
2348 /***********************************************************************
2351 * NOTE: WM_NEXTMENU documented in Win32 is a bit different.
2353 static LRESULT MENU_DoNextMenu( MTRACKER* pmt, UINT vk )
2355 POPUPMENU *menu = MENU_GetMenu( pmt->hTopMenu );
2357 if( (vk == VK_LEFT && menu->FocusedItem == 0 ) ||
2358 (vk == VK_RIGHT && menu->FocusedItem == menu->nItems - 1))
2360 MDINEXTMENU next_menu;
2365 next_menu.hmenuIn = (IS_SYSTEM_MENU(menu)) ? GetSubMenu(pmt->hTopMenu,0) : pmt->hTopMenu;
2366 next_menu.hmenuNext = 0;
2367 next_menu.hwndNext = 0;
2368 SendMessageW( pmt->hOwnerWnd, WM_NEXTMENU, vk, (LPARAM)&next_menu );
2370 TRACE("%04x [%04x] -> %04x [%04x]\n",
2371 pmt->hCurrentMenu, pmt->hOwnerWnd, next_menu.hmenuNext, next_menu.hwndNext );
2373 if (!next_menu.hmenuNext || !next_menu.hwndNext)
2375 DWORD style = GetWindowLongA( pmt->hOwnerWnd, GWL_STYLE );
2376 hNewWnd = pmt->hOwnerWnd;
2377 if( IS_SYSTEM_MENU(menu) )
2379 /* switch to the menu bar */
2381 if(style & WS_CHILD || !(hNewMenu = GetMenu(hNewWnd))) return FALSE;
2385 menu = MENU_GetMenu( hNewMenu );
2386 id = menu->nItems - 1;
2389 else if (style & WS_SYSMENU )
2391 /* switch to the system menu */
2392 hNewMenu = get_win_sys_menu( hNewWnd );
2396 else /* application returned a new menu to switch to */
2398 hNewMenu = next_menu.hmenuNext;
2399 hNewWnd = WIN_GetFullHandle( next_menu.hwndNext );
2401 if( IsMenu(hNewMenu) && IsWindow(hNewWnd) )
2403 DWORD style = GetWindowLongA( hNewWnd, GWL_STYLE );
2405 if (style & WS_SYSMENU &&
2406 GetSubMenu(get_win_sys_menu(hNewWnd), 0) == hNewMenu )
2408 /* get the real system menu */
2409 hNewMenu = get_win_sys_menu(hNewWnd);
2411 else if (style & WS_CHILD || GetMenu(hNewWnd) != hNewMenu )
2413 /* FIXME: Not sure what to do here;
2414 * perhaps try to track hNewMenu as a popup? */
2416 TRACE(" -- got confused.\n");
2423 if( hNewMenu != pmt->hTopMenu )
2425 MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, NO_SELECTED_ITEM,
2427 if( pmt->hCurrentMenu != pmt->hTopMenu )
2428 MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE );
2431 if( hNewWnd != pmt->hOwnerWnd )
2434 pmt->hOwnerWnd = hNewWnd;
2435 EVENT_Capture( pmt->hOwnerWnd, HTMENU );
2438 pmt->hTopMenu = pmt->hCurrentMenu = hNewMenu; /* all subpopups are hidden */
2439 MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, id, TRUE, 0 );
2446 /***********************************************************************
2449 * The idea is not to show the popup if the next input message is
2450 * going to hide it anyway.
2452 static BOOL MENU_SuspendPopup( MTRACKER* pmt, UINT16 uMsg )
2456 msg.hwnd = pmt->hOwnerWnd;
2458 PeekMessageA( &msg, 0, 0, 0, PM_NOYIELD | PM_REMOVE);
2459 pmt->trackFlags |= TF_SKIPREMOVE;
2464 PeekMessageA( &msg, 0, 0, 0, PM_NOYIELD | PM_NOREMOVE);
2465 if( msg.message == WM_KEYUP || msg.message == WM_PAINT )
2467 PeekMessageA( &msg, 0, 0, 0, PM_NOYIELD | PM_REMOVE);
2468 PeekMessageA( &msg, 0, 0, 0, PM_NOYIELD | PM_NOREMOVE);
2469 if( msg.message == WM_KEYDOWN &&
2470 (msg.wParam == VK_LEFT || msg.wParam == VK_RIGHT))
2472 pmt->trackFlags |= TF_SUSPENDPOPUP;
2479 /* failures go through this */
2480 pmt->trackFlags &= ~TF_SUSPENDPOPUP;
2484 /***********************************************************************
2487 * Handle a VK_ESCAPE key event in a menu.
2489 static BOOL MENU_KeyEscape(MTRACKER* pmt, UINT wFlags)
2491 BOOL bEndMenu = TRUE;
2493 if (pmt->hCurrentMenu != pmt->hTopMenu)
2495 POPUPMENU *menu = MENU_GetMenu(pmt->hCurrentMenu);
2497 if (menu->wFlags & MF_POPUP)
2499 HMENU hmenutmp, hmenuprev;
2501 hmenuprev = hmenutmp = pmt->hTopMenu;
2503 /* close topmost popup */
2504 while (hmenutmp != pmt->hCurrentMenu)
2506 hmenuprev = hmenutmp;
2507 hmenutmp = MENU_GetSubPopup( hmenuprev );
2510 MENU_HideSubPopups( pmt->hOwnerWnd, hmenuprev, TRUE );
2511 pmt->hCurrentMenu = hmenuprev;
2519 /***********************************************************************
2522 * Handle a VK_LEFT key event in a menu.
2524 static void MENU_KeyLeft( MTRACKER* pmt, UINT wFlags )
2527 HMENU hmenutmp, hmenuprev;
2530 hmenuprev = hmenutmp = pmt->hTopMenu;
2531 menu = MENU_GetMenu( hmenutmp );
2533 /* Try to move 1 column left (if possible) */
2534 if( (prevcol = MENU_GetStartOfPrevColumn( pmt->hCurrentMenu )) !=
2535 NO_SELECTED_ITEM ) {
2537 MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
2542 /* close topmost popup */
2543 while (hmenutmp != pmt->hCurrentMenu)
2545 hmenuprev = hmenutmp;
2546 hmenutmp = MENU_GetSubPopup( hmenuprev );
2549 MENU_HideSubPopups( pmt->hOwnerWnd, hmenuprev, TRUE );
2550 pmt->hCurrentMenu = hmenuprev;
2552 if ( (hmenuprev == pmt->hTopMenu) && !(menu->wFlags & MF_POPUP) )
2554 /* move menu bar selection if no more popups are left */
2556 if( !MENU_DoNextMenu( pmt, VK_LEFT) )
2557 MENU_MoveSelection( pmt->hOwnerWnd, pmt->hTopMenu, ITEM_PREV );
2559 if ( hmenuprev != hmenutmp || pmt->trackFlags & TF_SUSPENDPOPUP )
2561 /* A sublevel menu was displayed - display the next one
2562 * unless there is another displacement coming up */
2564 if( !MENU_SuspendPopup( pmt, WM_KEYDOWN ) )
2565 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd,
2566 pmt->hTopMenu, TRUE, wFlags);
2572 /***********************************************************************
2575 * Handle a VK_RIGHT key event in a menu.
2577 static void MENU_KeyRight( MTRACKER* pmt, UINT wFlags )
2580 POPUPMENU *menu = MENU_GetMenu( pmt->hTopMenu );
2583 TRACE("MENU_KeyRight called, cur %x (%s), top %x (%s).\n",
2585 debugstr_w((MENU_GetMenu(pmt->hCurrentMenu))->
2587 pmt->hTopMenu, debugstr_w(menu->items[0].text) );
2589 if ( (menu->wFlags & MF_POPUP) || (pmt->hCurrentMenu != pmt->hTopMenu))
2591 /* If already displaying a popup, try to display sub-popup */
2593 hmenutmp = pmt->hCurrentMenu;
2594 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd, hmenutmp, TRUE, wFlags);
2596 /* if subpopup was displayed then we are done */
2597 if (hmenutmp != pmt->hCurrentMenu) return;
2600 /* Check to see if there's another column */
2601 if( (nextcol = MENU_GetStartOfNextColumn( pmt->hCurrentMenu )) !=
2602 NO_SELECTED_ITEM ) {
2603 TRACE("Going to %d.\n", nextcol );
2604 MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
2609 if (!(menu->wFlags & MF_POPUP)) /* menu bar tracking */
2611 if( pmt->hCurrentMenu != pmt->hTopMenu )
2613 MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE );
2614 hmenutmp = pmt->hCurrentMenu = pmt->hTopMenu;
2615 } else hmenutmp = 0;
2617 /* try to move to the next item */
2618 if( !MENU_DoNextMenu( pmt, VK_RIGHT) )
2619 MENU_MoveSelection( pmt->hOwnerWnd, pmt->hTopMenu, ITEM_NEXT );
2621 if( hmenutmp || pmt->trackFlags & TF_SUSPENDPOPUP )
2622 if( !MENU_SuspendPopup(pmt, WM_KEYDOWN) )
2623 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd,
2624 pmt->hTopMenu, TRUE, wFlags);
2628 /***********************************************************************
2631 * Menu tracking code.
2633 static INT MENU_TrackMenu( HMENU hmenu, UINT wFlags, INT x, INT y,
2634 HWND hwnd, const RECT *lprect )
2639 INT executedMenuId = -1;
2641 BOOL enterIdleSent = FALSE;
2644 mt.hCurrentMenu = hmenu;
2645 mt.hTopMenu = hmenu;
2646 mt.hOwnerWnd = WIN_GetFullHandle( hwnd );
2650 TRACE("hmenu=0x%04x flags=0x%08x (%d,%d) hwnd=0x%04x (%d,%d)-(%d,%d)\n",
2651 hmenu, wFlags, x, y, hwnd, (lprect) ? lprect->left : 0, (lprect) ? lprect->top : 0,
2652 (lprect) ? lprect->right : 0, (lprect) ? lprect->bottom : 0);
2655 if (!(menu = MENU_GetMenu( hmenu ))) return FALSE;
2657 if (wFlags & TPM_BUTTONDOWN)
2659 /* Get the result in order to start the tracking or not */
2660 fRemove = MENU_ButtonDown( &mt, hmenu, wFlags );
2661 fEndMenu = !fRemove;
2664 EVENT_Capture( mt.hOwnerWnd, HTMENU );
2668 menu = MENU_GetMenu( mt.hCurrentMenu );
2669 if (!menu) /* sometimes happens if I do a window manager close */
2672 /* we have to keep the message in the queue until it's
2673 * clear that menu loop is not over yet. */
2677 if (PeekMessageA( &msg, 0, 0, 0, PM_NOREMOVE ))
2679 if (!CallMsgFilterA( &msg, MSGF_MENU )) break;
2680 /* remove the message from the queue */
2681 PeekMessageA( &msg, 0, msg.message, msg.message, PM_REMOVE );
2687 HWND win = (wFlags & TPM_ENTERIDLEEX && menu->wFlags & MF_POPUP) ? menu->hWnd : 0;
2688 enterIdleSent = TRUE;
2689 SendMessageW( mt.hOwnerWnd, WM_ENTERIDLE, MSGF_MENU, (LPARAM)win );
2695 /* check if EndMenu() tried to cancel us, by posting this message */
2696 if(msg.message == WM_CANCELMODE)
2698 /* we are now out of the loop */
2701 /* remove the message from the queue */
2702 PeekMessageA( &msg, 0, msg.message, msg.message, PM_REMOVE );
2704 /* break out of internal loop, ala ESCAPE */
2708 TranslateMessage( &msg );
2711 if ( (msg.hwnd==menu->hWnd) || (msg.message!=WM_TIMER) )
2712 enterIdleSent=FALSE;
2715 if ((msg.message >= WM_MOUSEFIRST) && (msg.message <= WM_MOUSELAST))
2718 * use the mouse coordinates in lParam instead of those in the MSG
2719 * struct to properly handle synthetic messages. lParam coords are
2720 * relative to client area, so they must be converted; since they can
2721 * be negative, we must use SLOWORD/SHIWORD instead of LOWORD/HIWORD.
2723 mt.pt.x = SLOWORD(msg.lParam);
2724 mt.pt.y = SHIWORD(msg.lParam);
2725 ClientToScreen(msg.hwnd,&mt.pt);
2727 /* Find a menu for this mouse event */
2728 hmenu = MENU_PtMenu( mt.hTopMenu, mt.pt );
2732 /* no WM_NC... messages in captured state */
2734 case WM_RBUTTONDBLCLK:
2735 case WM_RBUTTONDOWN:
2736 if (!(wFlags & TPM_RIGHTBUTTON)) break;
2738 case WM_LBUTTONDBLCLK:
2739 case WM_LBUTTONDOWN:
2740 /* If the message belongs to the menu, removes it from the queue */
2741 /* Else, end menu tracking */
2742 fRemove = MENU_ButtonDown( &mt, hmenu, wFlags );
2743 fEndMenu = !fRemove;
2747 if (!(wFlags & TPM_RIGHTBUTTON)) break;
2750 /* Check if a menu was selected by the mouse */
2753 executedMenuId = MENU_ButtonUp( &mt, hmenu, wFlags);
2755 /* End the loop if executedMenuId is an item ID */
2756 /* or if the job was done (executedMenuId = 0). */
2757 fEndMenu = fRemove = (executedMenuId != -1);
2759 /* No menu was selected by the mouse */
2760 /* if the function was called by TrackPopupMenu, continue
2761 with the menu tracking. If not, stop it */
2763 fEndMenu = ((wFlags & TPM_POPUPMENU) ? FALSE : TRUE);
2768 /* In win95 winelook, the selected menu item must be changed every time the
2769 mouse moves. In Win31 winelook, the mouse button has to be held down */
2771 if ( (TWEAK_WineLook > WIN31_LOOK) ||
2772 ( (msg.wParam & MK_LBUTTON) ||
2773 ((wFlags & TPM_RIGHTBUTTON) && (msg.wParam & MK_RBUTTON))) )
2775 fEndMenu |= !MENU_MouseMove( &mt, hmenu, wFlags );
2777 } /* switch(msg.message) - mouse */
2779 else if ((msg.message >= WM_KEYFIRST) && (msg.message <= WM_KEYLAST))
2781 fRemove = TRUE; /* Keyboard messages are always removed */
2789 MENU_SelectItem( mt.hOwnerWnd, mt.hCurrentMenu,
2790 NO_SELECTED_ITEM, FALSE, 0 );
2793 MENU_MoveSelection( mt.hOwnerWnd, mt.hCurrentMenu,
2794 (msg.wParam == VK_HOME)? ITEM_NEXT : ITEM_PREV );
2797 case VK_DOWN: /* If on menu bar, pull-down the menu */
2799 menu = MENU_GetMenu( mt.hCurrentMenu );
2800 if (!(menu->wFlags & MF_POPUP))
2801 mt.hCurrentMenu = MENU_ShowSubPopup(mt.hOwnerWnd, mt.hTopMenu, TRUE, wFlags);
2802 else /* otherwise try to move selection */
2803 MENU_MoveSelection( mt.hOwnerWnd, mt.hCurrentMenu, ITEM_NEXT );
2807 MENU_KeyLeft( &mt, wFlags );
2811 MENU_KeyRight( &mt, wFlags );
2815 fEndMenu = MENU_KeyEscape(&mt, wFlags);
2821 hi.cbSize = sizeof(HELPINFO);
2822 hi.iContextType = HELPINFO_MENUITEM;
2823 if (menu->FocusedItem == NO_SELECTED_ITEM)
2826 hi.iCtrlId = menu->items[menu->FocusedItem].wID;
2827 hi.hItemHandle = hmenu;
2828 hi.dwContextId = menu->dwContextHelpID;
2829 hi.MousePos = msg.pt;
2830 SendMessageA(hwnd, WM_HELP, 0, (LPARAM)&hi);
2837 break; /* WM_KEYDOWN */
2847 break; /* WM_SYSKEYDOWN */
2853 if (msg.wParam == '\r' || msg.wParam == ' ')
2855 executedMenuId = MENU_ExecFocusedItem(&mt,mt.hCurrentMenu, wFlags);
2856 fEndMenu = (executedMenuId != -1);
2861 /* Hack to avoid control chars. */
2862 /* We will find a better way real soon... */
2863 if ((msg.wParam <= 32) || (msg.wParam >= 127)) break;
2865 pos = MENU_FindItemByKey( mt.hOwnerWnd, mt.hCurrentMenu,
2866 LOWORD(msg.wParam), FALSE );
2867 if (pos == (UINT)-2) fEndMenu = TRUE;
2868 else if (pos == (UINT)-1) MessageBeep(0);
2871 MENU_SelectItem( mt.hOwnerWnd, mt.hCurrentMenu, pos,
2873 executedMenuId = MENU_ExecFocusedItem(&mt,mt.hCurrentMenu, wFlags);
2874 fEndMenu = (executedMenuId != -1);
2878 } /* switch(msg.message) - kbd */
2882 DispatchMessageA( &msg );
2885 if (!fEndMenu) fRemove = TRUE;
2887 /* finally remove message from the queue */
2889 if (fRemove && !(mt.trackFlags & TF_SKIPREMOVE) )
2890 PeekMessageA( &msg, 0, msg.message, msg.message, PM_REMOVE );
2891 else mt.trackFlags &= ~TF_SKIPREMOVE;
2896 /* If dropdown is still painted and the close box is clicked on
2897 then the menu will be destroyed as part of the DispatchMessage above.
2898 This will then invalidate the menu handle in mt.hTopMenu. We should
2899 check for this first. */
2900 if( IsMenu( mt.hTopMenu ) )
2902 menu = MENU_GetMenu( mt.hTopMenu );
2904 if( IsWindow( mt.hOwnerWnd ) )
2906 MENU_HideSubPopups( mt.hOwnerWnd, mt.hTopMenu, FALSE );
2908 if (menu && menu->wFlags & MF_POPUP)
2910 DestroyWindow( menu->hWnd );
2913 MENU_SelectItem( mt.hOwnerWnd, mt.hTopMenu, NO_SELECTED_ITEM, FALSE, 0 );
2914 SendMessageA( mt.hOwnerWnd, WM_MENUSELECT, MAKELONG(0,0xffff), 0 );
2917 /* Reset the variable for hiding menu */
2918 if( menu ) menu->bTimeToHide = FALSE;
2921 /* The return value is only used by TrackPopupMenu */
2922 return ((executedMenuId != -1) ? executedMenuId : 0);
2925 /***********************************************************************
2928 static BOOL MENU_InitTracking(HWND hWnd, HMENU hMenu, BOOL bPopup, UINT wFlags)
2930 TRACE("hwnd=0x%04x hmenu=0x%04x\n", hWnd, hMenu);
2934 /* Send WM_ENTERMENULOOP and WM_INITMENU message only if TPM_NONOTIFY flag is not specified */
2935 if (!(wFlags & TPM_NONOTIFY))
2936 SendMessageA( hWnd, WM_ENTERMENULOOP, bPopup, 0 );
2938 SendMessageA( hWnd, WM_SETCURSOR, (WPARAM)hWnd, HTCAPTION );
2940 if (!(wFlags & TPM_NONOTIFY))
2943 SendMessageA( hWnd, WM_INITMENU, hMenu, 0 );
2944 if ((menu = MENU_GetMenu( hMenu )) && (!menu->Height))
2945 { /* app changed/recreated menu bar entries in WM_INITMENU
2946 Recalculate menu sizes else clicks will not work */
2947 SetWindowPos( hWnd, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE |
2948 SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED );
2954 /***********************************************************************
2957 static BOOL MENU_ExitTracking(HWND hWnd)
2959 TRACE("hwnd=0x%04x\n", hWnd);
2961 SendMessageA( hWnd, WM_EXITMENULOOP, 0, 0 );
2966 /***********************************************************************
2967 * MENU_TrackMouseMenuBar
2969 * Menu-bar tracking upon a mouse event. Called from NC_HandleSysCommand().
2971 void MENU_TrackMouseMenuBar( HWND hWnd, INT ht, POINT pt )
2973 HMENU hMenu = (ht == HTSYSMENU) ? get_win_sys_menu( hWnd ) : GetMenu( hWnd );
2974 UINT wFlags = TPM_ENTERIDLEEX | TPM_BUTTONDOWN | TPM_LEFTALIGN | TPM_LEFTBUTTON;
2976 TRACE("wnd=%x ht=0x%04x (%ld,%ld)\n", hWnd, ht, pt.x, pt.y);
2980 /* map point to parent client coordinates */
2981 HWND parent = GetAncestor( hWnd, GA_PARENT );
2982 if (parent != GetDesktopWindow()) ScreenToClient( parent, &pt );
2984 MENU_InitTracking( hWnd, hMenu, FALSE, wFlags );
2985 MENU_TrackMenu( hMenu, wFlags, pt.x, pt.y, hWnd, NULL );
2986 MENU_ExitTracking(hWnd);
2991 /***********************************************************************
2992 * MENU_TrackKbdMenuBar
2994 * Menu-bar tracking upon a keyboard event. Called from NC_HandleSysCommand().
2996 void MENU_TrackKbdMenuBar( HWND hwnd, UINT wParam, INT vkey)
2998 UINT uItem = NO_SELECTED_ITEM;
3000 UINT wFlags = TPM_ENTERIDLEEX | TPM_LEFTALIGN | TPM_LEFTBUTTON;
3002 /* find window that has a menu */
3004 while (GetWindowLongA( hwnd, GWL_STYLE ) & WS_CHILD)
3005 if (!(hwnd = GetParent( hwnd ))) return;
3007 /* check if we have to track a system menu */
3009 hTrackMenu = GetMenu( hwnd );
3010 if (!hTrackMenu || IsIconic(hwnd) || vkey == VK_SPACE )
3012 if (!(GetWindowLongA( hwnd, GWL_STYLE ) & WS_SYSMENU)) return;
3013 hTrackMenu = get_win_sys_menu( hwnd );
3015 wParam |= HTSYSMENU; /* prevent item lookup */
3018 if (!IsMenu( hTrackMenu )) return;
3020 MENU_InitTracking( hwnd, hTrackMenu, FALSE, wFlags );
3022 if( vkey && vkey != VK_SPACE )
3024 uItem = MENU_FindItemByKey( hwnd, hTrackMenu, vkey, (wParam & HTSYSMENU) );
3025 if( uItem >= (UINT)(-2) )
3027 if( uItem == (UINT)(-1) ) MessageBeep(0);
3034 MENU_SelectItem( hwnd, hTrackMenu, uItem, TRUE, 0 );
3036 if( uItem == NO_SELECTED_ITEM )
3037 MENU_MoveSelection( hwnd, hTrackMenu, ITEM_NEXT );
3039 PostMessageA( hwnd, WM_KEYDOWN, VK_DOWN, 0L );
3041 MENU_TrackMenu( hTrackMenu, wFlags, 0, 0, hwnd, NULL );
3043 MENU_ExitTracking( hwnd );
3047 /**********************************************************************
3048 * TrackPopupMenu (USER32.@)
3050 * Like the win32 API, the function return the command ID only if the
3051 * flag TPM_RETURNCMD is on.
3054 BOOL WINAPI TrackPopupMenu( HMENU hMenu, UINT wFlags, INT x, INT y,
3055 INT nReserved, HWND hWnd, const RECT *lpRect )
3059 MENU_InitTracking(hWnd, hMenu, TRUE, wFlags);
3061 /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
3062 if (!(wFlags & TPM_NONOTIFY))
3063 SendMessageA( hWnd, WM_INITMENUPOPUP, hMenu, 0);
3065 if (MENU_ShowPopup( hWnd, hMenu, 0, x, y, 0, 0 ))
3066 ret = MENU_TrackMenu( hMenu, wFlags | TPM_POPUPMENU, 0, 0, hWnd, lpRect );
3067 MENU_ExitTracking(hWnd);
3069 if( (!(wFlags & TPM_RETURNCMD)) && (ret != FALSE) )
3075 /**********************************************************************
3076 * TrackPopupMenuEx (USER32.@)
3078 BOOL WINAPI TrackPopupMenuEx( HMENU hMenu, UINT wFlags, INT x, INT y,
3079 HWND hWnd, LPTPMPARAMS lpTpm )
3081 FIXME("not fully implemented\n" );
3082 return TrackPopupMenu( hMenu, wFlags, x, y, 0, hWnd,
3083 lpTpm ? &lpTpm->rcExclude : NULL );
3086 /***********************************************************************
3089 * NOTE: Windows has totally different (and undocumented) popup wndproc.
3091 static LRESULT WINAPI PopupMenuWndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam )
3093 TRACE("hwnd=0x%04x msg=0x%04x wp=0x%04x lp=0x%08lx\n",
3094 hwnd, message, wParam, lParam);
3100 CREATESTRUCTW *cs = (CREATESTRUCTW*)lParam;
3101 SetWindowLongW( hwnd, 0, (LONG)cs->lpCreateParams );
3105 case WM_MOUSEACTIVATE: /* We don't want to be activated */
3106 return MA_NOACTIVATE;
3111 BeginPaint( hwnd, &ps );
3112 MENU_DrawPopupMenu( hwnd, ps.hdc,
3113 (HMENU)GetWindowLongA( hwnd, 0 ) );
3114 EndPaint( hwnd, &ps );
3121 /* zero out global pointer in case resident popup window was destroyed. */
3122 if (hwnd == top_popup) top_popup = 0;
3129 if (!GetWindowLongW( hwnd, 0 )) ERR("no menu to display\n");
3132 SetWindowLongW( hwnd, 0, 0 );
3135 case MM_SETMENUHANDLE:
3136 SetWindowLongW( hwnd, 0, wParam );
3139 case MM_GETMENUHANDLE:
3140 return GetWindowLongW( hwnd, 0 );
3143 return DefWindowProcW( hwnd, message, wParam, lParam );
3149 /***********************************************************************
3150 * MENU_GetMenuBarHeight
3152 * Compute the size of the menu bar height. Used by NC_HandleNCCalcSize().
3154 UINT MENU_GetMenuBarHeight( HWND hwnd, UINT menubarWidth,
3155 INT orgX, INT orgY )
3161 TRACE("HWND 0x%x, width %d, at (%d, %d).\n",
3162 hwnd, menubarWidth, orgX, orgY );
3164 if (!(lppop = MENU_GetMenu( GetMenu(hwnd) ))) return 0;
3166 hdc = GetDCEx( hwnd, 0, DCX_CACHE | DCX_WINDOW );
3167 SelectObject( hdc, hMenuFont);
3168 SetRect(&rectBar, orgX, orgY, orgX+menubarWidth, orgY+GetSystemMetrics(SM_CYMENU));
3169 MENU_MenuBarCalcSize( hdc, &rectBar, lppop, hwnd );
3170 ReleaseDC( hwnd, hdc );
3171 return lppop->Height;
3175 /*******************************************************************
3176 * ChangeMenu (USER.153)
3178 BOOL16 WINAPI ChangeMenu16( HMENU16 hMenu, UINT16 pos, SEGPTR data,
3179 UINT16 id, UINT16 flags )
3181 TRACE("menu=%04x pos=%d data=%08lx id=%04x flags=%04x\n",
3182 hMenu, pos, (DWORD)data, id, flags );
3183 if (flags & MF_APPEND) return AppendMenu16( hMenu, flags & ~MF_APPEND,
3186 /* FIXME: Word passes the item id in 'pos' and 0 or 0xffff as id */
3187 /* for MF_DELETE. We should check the parameters for all others */
3188 /* MF_* actions also (anybody got a doc on ChangeMenu?). */
3190 if (flags & MF_DELETE) return DeleteMenu16(hMenu, pos, flags & ~MF_DELETE);
3191 if (flags & MF_CHANGE) return ModifyMenu16(hMenu, pos, flags & ~MF_CHANGE,
3193 if (flags & MF_REMOVE) return RemoveMenu16(hMenu,
3194 flags & MF_BYPOSITION ? pos : id,
3195 flags & ~MF_REMOVE );
3196 /* Default: MF_INSERT */
3197 return InsertMenu16( hMenu, pos, flags, id, data );
3201 /*******************************************************************
3202 * ChangeMenuA (USER32.@)
3204 BOOL WINAPI ChangeMenuA( HMENU hMenu, UINT pos, LPCSTR data,
3205 UINT id, UINT flags )
3207 TRACE("menu=%08x pos=%d data=%08lx id=%08x flags=%08x\n",
3208 hMenu, pos, (DWORD)data, id, flags );
3209 if (flags & MF_APPEND) return AppendMenuA( hMenu, flags & ~MF_APPEND,
3211 if (flags & MF_DELETE) return DeleteMenu(hMenu, pos, flags & ~MF_DELETE);
3212 if (flags & MF_CHANGE) return ModifyMenuA(hMenu, pos, flags & ~MF_CHANGE,
3214 if (flags & MF_REMOVE) return RemoveMenu( hMenu,
3215 flags & MF_BYPOSITION ? pos : id,
3216 flags & ~MF_REMOVE );
3217 /* Default: MF_INSERT */
3218 return InsertMenuA( hMenu, pos, flags, id, data );
3222 /*******************************************************************
3223 * ChangeMenuW (USER32.@)
3225 BOOL WINAPI ChangeMenuW( HMENU hMenu, UINT pos, LPCWSTR data,
3226 UINT id, UINT flags )
3228 TRACE("menu=%08x pos=%d data=%08lx id=%08x flags=%08x\n",
3229 hMenu, pos, (DWORD)data, id, flags );
3230 if (flags & MF_APPEND) return AppendMenuW( hMenu, flags & ~MF_APPEND,
3232 if (flags & MF_DELETE) return DeleteMenu(hMenu, pos, flags & ~MF_DELETE);
3233 if (flags & MF_CHANGE) return ModifyMenuW(hMenu, pos, flags & ~MF_CHANGE,
3235 if (flags & MF_REMOVE) return RemoveMenu( hMenu,
3236 flags & MF_BYPOSITION ? pos : id,
3237 flags & ~MF_REMOVE );
3238 /* Default: MF_INSERT */
3239 return InsertMenuW( hMenu, pos, flags, id, data );
3243 /*******************************************************************
3244 * CheckMenuItem (USER.154)
3246 BOOL16 WINAPI CheckMenuItem16( HMENU16 hMenu, UINT16 id, UINT16 flags )
3248 return (BOOL16)CheckMenuItem( hMenu, id, flags );
3252 /*******************************************************************
3253 * CheckMenuItem (USER32.@)
3255 DWORD WINAPI CheckMenuItem( HMENU hMenu, UINT id, UINT flags )
3260 TRACE("menu=%04x id=%04x flags=%04x\n", hMenu, id, flags );
3261 if (!(item = MENU_FindItem( &hMenu, &id, flags ))) return -1;
3262 ret = item->fState & MF_CHECKED;
3263 if (flags & MF_CHECKED) item->fState |= MF_CHECKED;
3264 else item->fState &= ~MF_CHECKED;
3269 /**********************************************************************
3270 * EnableMenuItem (USER.155)
3272 UINT16 WINAPI EnableMenuItem16( HMENU16 hMenu, UINT16 wItemID, UINT16 wFlags )
3274 return EnableMenuItem( hMenu, wItemID, wFlags );
3278 /**********************************************************************
3279 * EnableMenuItem (USER32.@)
3281 UINT WINAPI EnableMenuItem( HMENU hMenu, UINT wItemID, UINT wFlags )
3287 TRACE("(%04x, %04X, %04X) !\n",
3288 hMenu, wItemID, wFlags);
3290 /* Get the Popupmenu to access the owner menu */
3291 if (!(menu = MENU_GetMenu(hMenu)))
3294 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags )))
3297 oldflags = item->fState & (MF_GRAYED | MF_DISABLED);
3298 item->fState ^= (oldflags ^ wFlags) & (MF_GRAYED | MF_DISABLED);
3300 /* In win95 if the close item in the system menu change update the close button */
3301 if (TWEAK_WineLook == WIN95_LOOK)
3302 if((item->wID == SC_CLOSE) && (oldflags != wFlags))
3304 if (menu->hSysMenuOwner != 0)
3306 POPUPMENU* parentMenu;
3308 /* Get the parent menu to access*/
3309 if (!(parentMenu = MENU_GetMenu(menu->hSysMenuOwner)))
3312 /* Refresh the frame to reflect the change*/
3313 SetWindowPos(parentMenu->hWnd, 0, 0, 0, 0, 0,
3314 SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER);
3322 /*******************************************************************
3323 * GetMenuString (USER.161)
3325 INT16 WINAPI GetMenuString16( HMENU16 hMenu, UINT16 wItemID,
3326 LPSTR str, INT16 nMaxSiz, UINT16 wFlags )
3328 return GetMenuStringA( hMenu, wItemID, str, nMaxSiz, wFlags );
3332 /*******************************************************************
3333 * GetMenuStringA (USER32.@)
3335 INT WINAPI GetMenuStringA(
3336 HMENU hMenu, /* [in] menuhandle */
3337 UINT wItemID, /* [in] menu item (dep. on wFlags) */
3338 LPSTR str, /* [out] outbuffer. If NULL, func returns entry length*/
3339 INT nMaxSiz, /* [in] length of buffer. if 0, func returns entry len*/
3340 UINT wFlags /* [in] MF_ flags */
3344 TRACE("menu=%04x item=%04x ptr=%p len=%d flags=%04x\n",
3345 hMenu, wItemID, str, nMaxSiz, wFlags );
3346 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) return 0;
3347 if (!IS_STRING_ITEM(item->fType)) return 0;
3348 if (!str || !nMaxSiz) return strlenW(item->text);
3350 if (!WideCharToMultiByte( CP_ACP, 0, item->text, -1, str, nMaxSiz, NULL, NULL ))
3352 TRACE("returning '%s'\n", str );
3357 /*******************************************************************
3358 * GetMenuStringW (USER32.@)
3360 INT WINAPI GetMenuStringW( HMENU hMenu, UINT wItemID,
3361 LPWSTR str, INT nMaxSiz, UINT wFlags )
3365 TRACE("menu=%04x item=%04x ptr=%p len=%d flags=%04x\n",
3366 hMenu, wItemID, str, nMaxSiz, wFlags );
3367 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) return 0;
3368 if (!IS_STRING_ITEM(item->fType)) return 0;
3369 if (!str || !nMaxSiz) return strlenW(item->text);
3371 lstrcpynW( str, item->text, nMaxSiz );
3372 return strlenW(str);
3376 /**********************************************************************
3377 * HiliteMenuItem (USER32.@)
3379 BOOL WINAPI HiliteMenuItem( HWND hWnd, HMENU hMenu, UINT wItemID,
3383 TRACE("(%04x, %04x, %04x, %04x);\n",
3384 hWnd, hMenu, wItemID, wHilite);
3385 if (!MENU_FindItem( &hMenu, &wItemID, wHilite )) return FALSE;
3386 if (!(menu = MENU_GetMenu(hMenu))) return FALSE;
3387 if (menu->FocusedItem == wItemID) return TRUE;
3388 MENU_HideSubPopups( hWnd, hMenu, FALSE );
3389 MENU_SelectItem( hWnd, hMenu, wItemID, TRUE, 0 );
3394 /**********************************************************************
3395 * GetMenuState (USER.250)
3397 UINT16 WINAPI GetMenuState16( HMENU16 hMenu, UINT16 wItemID, UINT16 wFlags )
3399 return GetMenuState( hMenu, wItemID, wFlags );
3403 /**********************************************************************
3404 * GetMenuState (USER32.@)
3406 UINT WINAPI GetMenuState( HMENU hMenu, UINT wItemID, UINT wFlags )
3409 TRACE("(menu=%04x, id=%04x, flags=%04x);\n",
3410 hMenu, wItemID, wFlags);
3411 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) return -1;
3412 debug_print_menuitem (" item: ", item, "");
3413 if (item->fType & MF_POPUP)
3415 POPUPMENU *menu = MENU_GetMenu( item->hSubMenu );
3416 if (!menu) return -1;
3417 else return (menu->nItems << 8) | ((item->fState|item->fType) & 0xff);
3421 /* We used to (from way back then) mask the result to 0xff. */
3422 /* I don't know why and it seems wrong as the documented */
3423 /* return flag MF_SEPARATOR is outside that mask. */
3424 return (item->fType | item->fState);
3429 /**********************************************************************
3430 * GetMenuItemCount (USER.263)
3432 INT16 WINAPI GetMenuItemCount16( HMENU16 hMenu )
3434 LPPOPUPMENU menu = MENU_GetMenu(hMenu);
3435 if (!menu) return -1;
3436 TRACE("(%04x) returning %d\n",
3437 hMenu, menu->nItems );
3438 return menu->nItems;
3442 /**********************************************************************
3443 * GetMenuItemCount (USER32.@)
3445 INT WINAPI GetMenuItemCount( HMENU hMenu )
3447 LPPOPUPMENU menu = MENU_GetMenu(hMenu);
3448 if (!menu) return -1;
3449 TRACE("(%04x) returning %d\n",
3450 hMenu, menu->nItems );
3451 return menu->nItems;
3454 /**********************************************************************
3455 * GetMenuItemID (USER.264)
3457 UINT16 WINAPI GetMenuItemID16( HMENU16 hMenu, INT16 nPos )
3459 return (UINT16) GetMenuItemID (hMenu, nPos);
3462 /**********************************************************************
3463 * GetMenuItemID (USER32.@)
3465 UINT WINAPI GetMenuItemID( HMENU hMenu, INT nPos )
3469 if (!(lpmi = MENU_FindItem(&hMenu,&nPos,MF_BYPOSITION))) return -1;
3470 if (lpmi->fType & MF_POPUP) return -1;
3475 /*******************************************************************
3476 * InsertMenu (USER.410)
3478 BOOL16 WINAPI InsertMenu16( HMENU16 hMenu, UINT16 pos, UINT16 flags,
3479 UINT16 id, SEGPTR data )
3481 UINT pos32 = (UINT)pos;
3482 if ((pos == (UINT16)-1) && (flags & MF_BYPOSITION)) pos32 = (UINT)-1;
3483 if (IS_STRING_ITEM(flags) && data)
3484 return InsertMenuA( hMenu, pos32, flags, id, MapSL(data) );
3485 return InsertMenuA( hMenu, pos32, flags, id, (LPSTR)data );
3489 /*******************************************************************
3490 * InsertMenuW (USER32.@)
3492 BOOL WINAPI InsertMenuW( HMENU hMenu, UINT pos, UINT flags,
3493 UINT id, LPCWSTR str )
3497 if (IS_STRING_ITEM(flags) && str)
3498 TRACE("hMenu %04x, pos %d, flags %08x, "
3499 "id %04x, str %s\n",
3500 hMenu, pos, flags, id, debugstr_w(str) );
3501 else TRACE("hMenu %04x, pos %d, flags %08x, "
3502 "id %04x, str %08lx (not a string)\n",
3503 hMenu, pos, flags, id, (DWORD)str );
3505 if (!(item = MENU_InsertItem( hMenu, pos, flags ))) return FALSE;
3507 if (!(MENU_SetItemData( item, flags, id, str )))
3509 RemoveMenu( hMenu, pos, flags );
3513 if (flags & MF_POPUP) /* Set the MF_POPUP flag on the popup-menu */
3514 (MENU_GetMenu((HMENU16)id))->wFlags |= MF_POPUP;
3516 item->hCheckBit = item->hUnCheckBit = 0;
3521 /*******************************************************************
3522 * InsertMenuA (USER32.@)
3524 BOOL WINAPI InsertMenuA( HMENU hMenu, UINT pos, UINT flags,
3525 UINT id, LPCSTR str )
3529 if (IS_STRING_ITEM(flags) && str)
3531 INT len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
3532 LPWSTR newstr = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
3535 MultiByteToWideChar( CP_ACP, 0, str, -1, newstr, len );
3536 ret = InsertMenuW( hMenu, pos, flags, id, newstr );
3537 HeapFree( GetProcessHeap(), 0, newstr );
3541 else return InsertMenuW( hMenu, pos, flags, id, (LPCWSTR)str );
3545 /*******************************************************************
3546 * AppendMenu (USER.411)
3548 BOOL16 WINAPI AppendMenu16(HMENU16 hMenu, UINT16 flags, UINT16 id, SEGPTR data)
3550 return InsertMenu16( hMenu, -1, flags | MF_BYPOSITION, id, data );
3554 /*******************************************************************
3555 * AppendMenuA (USER32.@)
3557 BOOL WINAPI AppendMenuA( HMENU hMenu, UINT flags,
3558 UINT id, LPCSTR data )
3560 return InsertMenuA( hMenu, -1, flags | MF_BYPOSITION, id, data );
3564 /*******************************************************************
3565 * AppendMenuW (USER32.@)
3567 BOOL WINAPI AppendMenuW( HMENU hMenu, UINT flags,
3568 UINT id, LPCWSTR data )
3570 return InsertMenuW( hMenu, -1, flags | MF_BYPOSITION, id, data );
3574 /**********************************************************************
3575 * RemoveMenu (USER.412)
3577 BOOL16 WINAPI RemoveMenu16( HMENU16 hMenu, UINT16 nPos, UINT16 wFlags )
3579 return RemoveMenu( hMenu, nPos, wFlags );
3583 /**********************************************************************
3584 * RemoveMenu (USER32.@)
3586 BOOL WINAPI RemoveMenu( HMENU hMenu, UINT nPos, UINT wFlags )
3591 TRACE("(menu=%04x pos=%04x flags=%04x)\n",hMenu, nPos, wFlags);
3592 if (!(item = MENU_FindItem( &hMenu, &nPos, wFlags ))) return FALSE;
3593 if (!(menu = MENU_GetMenu(hMenu))) return FALSE;
3597 MENU_FreeItemData( item );
3599 if (--menu->nItems == 0)
3601 HeapFree( GetProcessHeap(), 0, menu->items );
3606 while(nPos < menu->nItems)
3612 menu->items = HeapReAlloc( GetProcessHeap(), 0, menu->items,
3613 menu->nItems * sizeof(MENUITEM) );
3619 /**********************************************************************
3620 * DeleteMenu (USER.413)
3622 BOOL16 WINAPI DeleteMenu16( HMENU16 hMenu, UINT16 nPos, UINT16 wFlags )
3624 return DeleteMenu( hMenu, nPos, wFlags );
3628 /**********************************************************************
3629 * DeleteMenu (USER32.@)
3631 BOOL WINAPI DeleteMenu( HMENU hMenu, UINT nPos, UINT wFlags )
3633 MENUITEM *item = MENU_FindItem( &hMenu, &nPos, wFlags );
3634 if (!item) return FALSE;
3635 if (item->fType & MF_POPUP) DestroyMenu( item->hSubMenu );
3636 /* nPos is now the position of the item */
3637 RemoveMenu( hMenu, nPos, wFlags | MF_BYPOSITION );
3642 /*******************************************************************
3643 * ModifyMenu (USER.414)
3645 BOOL16 WINAPI ModifyMenu16( HMENU16 hMenu, UINT16 pos, UINT16 flags,
3646 UINT16 id, SEGPTR data )
3648 if (IS_STRING_ITEM(flags))
3649 return ModifyMenuA( hMenu, pos, flags, id, MapSL(data) );
3650 return ModifyMenuA( hMenu, pos, flags, id, (LPSTR)data );
3654 /*******************************************************************
3655 * ModifyMenuW (USER32.@)
3657 BOOL WINAPI ModifyMenuW( HMENU hMenu, UINT pos, UINT flags,
3658 UINT id, LPCWSTR str )
3662 if (IS_STRING_ITEM(flags))
3664 TRACE("%04x %d %04x %04x %s\n",
3665 hMenu, pos, flags, id, debugstr_w(str) );
3666 if (!str) return FALSE;
3670 TRACE("%04x %d %04x %04x %08lx\n",
3671 hMenu, pos, flags, id, (DWORD)str );
3674 if (!(item = MENU_FindItem( &hMenu, &pos, flags ))) return FALSE;
3675 return MENU_SetItemData( item, flags, id, str );
3679 /*******************************************************************
3680 * ModifyMenuA (USER32.@)
3682 BOOL WINAPI ModifyMenuA( HMENU hMenu, UINT pos, UINT flags,
3683 UINT id, LPCSTR str )
3687 if (IS_STRING_ITEM(flags) && str)
3689 INT len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
3690 LPWSTR newstr = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
3693 MultiByteToWideChar( CP_ACP, 0, str, -1, newstr, len );
3694 ret = ModifyMenuW( hMenu, pos, flags, id, newstr );
3695 HeapFree( GetProcessHeap(), 0, newstr );
3699 else return ModifyMenuW( hMenu, pos, flags, id, (LPCWSTR)str );
3703 /**********************************************************************
3704 * CreatePopupMenu (USER.415)
3706 HMENU16 WINAPI CreatePopupMenu16(void)
3708 return CreatePopupMenu();
3712 /**********************************************************************
3713 * CreatePopupMenu (USER32.@)
3715 HMENU WINAPI CreatePopupMenu(void)
3720 if (!(hmenu = CreateMenu())) return 0;
3721 menu = MENU_GetMenu( hmenu );
3722 menu->wFlags |= MF_POPUP;
3723 menu->bTimeToHide = FALSE;
3728 /**********************************************************************
3729 * GetMenuCheckMarkDimensions (USER.417)
3730 * GetMenuCheckMarkDimensions (USER32.@)
3732 DWORD WINAPI GetMenuCheckMarkDimensions(void)
3734 return MAKELONG( GetSystemMetrics(SM_CXMENUCHECK), GetSystemMetrics(SM_CYMENUCHECK) );
3738 /**********************************************************************
3739 * SetMenuItemBitmaps (USER.418)
3741 BOOL16 WINAPI SetMenuItemBitmaps16( HMENU16 hMenu, UINT16 nPos, UINT16 wFlags,
3742 HBITMAP16 hNewUnCheck, HBITMAP16 hNewCheck)
3744 return SetMenuItemBitmaps( hMenu, nPos, wFlags, hNewUnCheck, hNewCheck );
3748 /**********************************************************************
3749 * SetMenuItemBitmaps (USER32.@)
3751 BOOL WINAPI SetMenuItemBitmaps( HMENU hMenu, UINT nPos, UINT wFlags,
3752 HBITMAP hNewUnCheck, HBITMAP hNewCheck)
3755 TRACE("(%04x, %04x, %04x, %04x, %04x)\n",
3756 hMenu, nPos, wFlags, hNewCheck, hNewUnCheck);
3757 if (!(item = MENU_FindItem( &hMenu, &nPos, wFlags ))) return FALSE;
3759 if (!hNewCheck && !hNewUnCheck)
3761 item->fState &= ~MF_USECHECKBITMAPS;
3763 else /* Install new bitmaps */
3765 item->hCheckBit = hNewCheck;
3766 item->hUnCheckBit = hNewUnCheck;
3767 item->fState |= MF_USECHECKBITMAPS;
3773 /**********************************************************************
3774 * CreateMenu (USER.151)
3776 HMENU16 WINAPI CreateMenu16(void)
3778 return CreateMenu();
3782 /**********************************************************************
3783 * CreateMenu (USER32.@)
3785 HMENU WINAPI CreateMenu(void)
3789 if (!(hMenu = USER_HEAP_ALLOC( sizeof(POPUPMENU) ))) return 0;
3790 menu = (LPPOPUPMENU) USER_HEAP_LIN_ADDR(hMenu);
3792 ZeroMemory(menu, sizeof(POPUPMENU));
3793 menu->wMagic = MENU_MAGIC;
3794 menu->FocusedItem = NO_SELECTED_ITEM;
3795 menu->bTimeToHide = FALSE;
3797 TRACE("return %04x\n", hMenu );
3803 /**********************************************************************
3804 * DestroyMenu (USER.152)
3806 BOOL16 WINAPI DestroyMenu16( HMENU16 hMenu )
3808 return DestroyMenu( hMenu );
3812 /**********************************************************************
3813 * DestroyMenu (USER32.@)
3815 BOOL WINAPI DestroyMenu( HMENU hMenu )
3817 TRACE("(%04x)\n", hMenu);
3819 /* Silently ignore attempts to destroy default system popup */
3821 if (hMenu && hMenu != MENU_DefSysPopup)
3823 LPPOPUPMENU lppop = MENU_GetMenu(hMenu);
3825 if (!lppop) return FALSE;
3827 lppop->wMagic = 0; /* Mark it as destroyed */
3829 if ((lppop->wFlags & MF_POPUP) && lppop->hWnd)
3831 DestroyWindow( lppop->hWnd );
3835 if (lppop->items) /* recursively destroy submenus */
3838 MENUITEM *item = lppop->items;
3839 for (i = lppop->nItems; i > 0; i--, item++)
3841 if (item->fType & MF_POPUP) DestroyMenu(item->hSubMenu);
3842 MENU_FreeItemData( item );
3844 HeapFree( GetProcessHeap(), 0, lppop->items );
3846 USER_HEAP_FREE( hMenu );
3848 return (hMenu != MENU_DefSysPopup);
3852 /**********************************************************************
3853 * GetSystemMenu (USER32.@)
3855 HMENU WINAPI GetSystemMenu( HWND hWnd, BOOL bRevert )
3857 WND *wndPtr = WIN_FindWndPtr( hWnd );
3862 if( wndPtr->hSysMenu )
3866 DestroyMenu(wndPtr->hSysMenu);
3867 wndPtr->hSysMenu = 0;
3871 POPUPMENU *menu = MENU_GetMenu( wndPtr->hSysMenu );
3874 if( menu->nItems > 0 && menu->items[0].hSubMenu == MENU_DefSysPopup )
3875 menu->items[0].hSubMenu = MENU_CopySysPopup();
3879 WARN("Current sys-menu (%04x) of wnd %04x is broken\n",
3880 wndPtr->hSysMenu, hWnd);
3881 wndPtr->hSysMenu = 0;
3886 if(!wndPtr->hSysMenu && (wndPtr->dwStyle & WS_SYSMENU) )
3887 wndPtr->hSysMenu = MENU_GetSysMenu( hWnd, (HMENU)(-1) );
3889 if( wndPtr->hSysMenu )
3892 retvalue = GetSubMenu(wndPtr->hSysMenu, 0);
3894 /* Store the dummy sysmenu handle to facilitate the refresh */
3895 /* of the close button if the SC_CLOSE item change */
3896 menu = MENU_GetMenu(retvalue);
3898 menu->hSysMenuOwner = wndPtr->hSysMenu;
3900 WIN_ReleaseWndPtr(wndPtr);
3902 return bRevert ? 0 : retvalue;
3906 /*******************************************************************
3907 * SetSystemMenu (USER32.@)
3909 BOOL WINAPI SetSystemMenu( HWND hwnd, HMENU hMenu )
3911 WND *wndPtr = WIN_FindWndPtr(hwnd);
3915 if (wndPtr->hSysMenu) DestroyMenu( wndPtr->hSysMenu );
3916 wndPtr->hSysMenu = MENU_GetSysMenu( hwnd, hMenu );
3917 WIN_ReleaseWndPtr(wndPtr);
3924 /**********************************************************************
3925 * GetMenu (USER32.@)
3927 HMENU WINAPI GetMenu( HWND hWnd )
3929 HMENU retvalue = (HMENU)GetWindowLongA( hWnd, GWL_ID );
3930 TRACE("for %04x returning %04x\n", hWnd, retvalue);
3935 /**********************************************************************
3936 * SetMenu (USER32.@)
3938 BOOL WINAPI SetMenu( HWND hWnd, HMENU hMenu )
3940 TRACE("(%04x, %04x);\n", hWnd, hMenu);
3942 if (hMenu && !IsMenu(hMenu))
3944 WARN("hMenu %x is not a menu handle\n", hMenu);
3947 if (GetWindowLongA( hWnd, GWL_STYLE ) & WS_CHILD) return FALSE;
3949 hWnd = WIN_GetFullHandle( hWnd );
3950 if (GetCapture() == hWnd) ReleaseCapture();
3956 if (!(lpmenu = MENU_GetMenu(hMenu))) return FALSE;
3958 lpmenu->hWnd = hWnd;
3959 lpmenu->Height = 0; /* Make sure we recalculate the size */
3961 SetWindowLongA( hWnd, GWL_ID, hMenu );
3963 if (IsWindowVisible(hWnd))
3964 SetWindowPos( hWnd, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE |
3965 SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED );
3971 /**********************************************************************
3972 * GetSubMenu (USER.159)
3974 HMENU16 WINAPI GetSubMenu16( HMENU16 hMenu, INT16 nPos )
3976 return GetSubMenu( hMenu, nPos );
3980 /**********************************************************************
3981 * GetSubMenu (USER32.@)
3983 HMENU WINAPI GetSubMenu( HMENU hMenu, INT nPos )
3987 if (!(lpmi = MENU_FindItem(&hMenu,&nPos,MF_BYPOSITION))) return 0;
3988 if (!(lpmi->fType & MF_POPUP)) return 0;
3989 return lpmi->hSubMenu;
3993 /**********************************************************************
3994 * DrawMenuBar (USER32.@)
3996 BOOL WINAPI DrawMenuBar( HWND hWnd )
3999 HMENU hMenu = GetMenu(hWnd);
4001 if (GetWindowLongA( hWnd, GWL_STYLE ) & WS_CHILD) return FALSE;
4002 if (!hMenu || !(lppop = MENU_GetMenu( hMenu ))) return FALSE;
4004 lppop->Height = 0; /* Make sure we call MENU_MenuBarCalcSize */
4005 lppop->hwndOwner = hWnd;
4006 SetWindowPos( hWnd, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE |
4007 SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED );
4011 /***********************************************************************
4012 * DrawMenuBarTemp (USER32.@)
4016 * called by W98SE desk.cpl Control Panel Applet
4018 * Not 100% sure about the param names, but close.
4020 DWORD WINAPI DrawMenuBarTemp(HWND someHWND, HDC someHDC, LPRECT someRECT, HMENU someHMENU, HFONT someFONT)
4022 FIXME("(0x%08x, 0x%08x, %p, 0x%08x, 0x%08x): stub\n", someHWND, someHDC, someRECT, someHMENU, someFONT);
4026 /***********************************************************************
4027 * EndMenu (USER.187)
4028 * EndMenu (USER32.@)
4030 void WINAPI EndMenu(void)
4032 /* if we are in the menu code, and it is active */
4033 if (!fEndMenu && top_popup)
4035 /* terminate the menu handling code */
4038 /* needs to be posted to wakeup the internal menu handler */
4039 /* which will now terminate the menu, in the event that */
4040 /* the main window was minimized, or lost focus, so we */
4041 /* don't end up with an orphaned menu */
4042 PostMessageA( top_popup, WM_CANCELMODE, 0, 0);
4047 /***********************************************************************
4048 * LookupMenuHandle (USER.217)
4050 HMENU16 WINAPI LookupMenuHandle16( HMENU16 hmenu, INT16 id )
4052 HMENU hmenu32 = hmenu;
4054 if (!MENU_FindItem( &hmenu32, &id32, MF_BYCOMMAND )) return 0;
4055 else return hmenu32;
4059 /**********************************************************************
4060 * LoadMenu (USER.150)
4062 HMENU16 WINAPI LoadMenu16( HINSTANCE16 instance, LPCSTR name )
4068 TRACE("(%04x,%s)\n", instance, debugres_a(name) );
4072 if (name[0] == '#') name = (LPCSTR)atoi( name + 1 );
4075 if (!name) return 0;
4077 /* check for Win32 module */
4078 if (HIWORD(instance)) return LoadMenuA( instance, name );
4079 instance = GetExePtr( instance );
4081 if (!(hRsrc = FindResource16( instance, name, RT_MENUA ))) return 0;
4082 if (!(handle = LoadResource16( instance, hRsrc ))) return 0;
4083 hMenu = LoadMenuIndirect16(LockResource16(handle));
4084 FreeResource16( handle );
4089 /*****************************************************************
4090 * LoadMenuA (USER32.@)
4092 HMENU WINAPI LoadMenuA( HINSTANCE instance, LPCSTR name )
4094 HRSRC hrsrc = FindResourceA( instance, name, RT_MENUA );
4095 if (!hrsrc) return 0;
4096 return LoadMenuIndirectA( (LPCVOID)LoadResource( instance, hrsrc ));
4100 /*****************************************************************
4101 * LoadMenuW (USER32.@)
4103 HMENU WINAPI LoadMenuW( HINSTANCE instance, LPCWSTR name )
4105 HRSRC hrsrc = FindResourceW( instance, name, RT_MENUW );
4106 if (!hrsrc) return 0;
4107 return LoadMenuIndirectW( (LPCVOID)LoadResource( instance, hrsrc ));
4111 /**********************************************************************
4112 * LoadMenuIndirect (USER.220)
4114 HMENU16 WINAPI LoadMenuIndirect16( LPCVOID template )
4117 WORD version, offset;
4118 LPCSTR p = (LPCSTR)template;
4120 TRACE("(%p)\n", template );
4121 version = GET_WORD(p);
4125 WARN("version must be 0 for Win16\n" );
4128 offset = GET_WORD(p);
4129 p += sizeof(WORD) + offset;
4130 if (!(hMenu = CreateMenu())) return 0;
4131 if (!MENU_ParseResource( p, hMenu, FALSE ))
4133 DestroyMenu( hMenu );
4140 /**********************************************************************
4141 * LoadMenuIndirectA (USER32.@)
4143 HMENU WINAPI LoadMenuIndirectA( LPCVOID template )
4146 WORD version, offset;
4147 LPCSTR p = (LPCSTR)template;
4149 TRACE("%p\n", template );
4150 version = GET_WORD(p);
4155 offset = GET_WORD(p);
4156 p += sizeof(WORD) + offset;
4157 if (!(hMenu = CreateMenu())) return 0;
4158 if (!MENU_ParseResource( p, hMenu, TRUE ))
4160 DestroyMenu( hMenu );
4165 offset = GET_WORD(p);
4166 p += sizeof(WORD) + offset;
4167 if (!(hMenu = CreateMenu())) return 0;
4168 if (!MENUEX_ParseResource( p, hMenu))
4170 DestroyMenu( hMenu );
4175 ERR("version %d not supported.\n", version);
4181 /**********************************************************************
4182 * LoadMenuIndirectW (USER32.@)
4184 HMENU WINAPI LoadMenuIndirectW( LPCVOID template )
4186 /* FIXME: is there anything different between A and W? */
4187 return LoadMenuIndirectA( template );
4191 /**********************************************************************
4194 BOOL16 WINAPI IsMenu16( HMENU16 hmenu )
4196 return IsMenu( hmenu );
4200 /**********************************************************************
4203 BOOL WINAPI IsMenu(HMENU hmenu)
4205 LPPOPUPMENU menu = MENU_GetMenu(hmenu);
4206 return menu != NULL;
4209 /**********************************************************************
4210 * GetMenuItemInfo_common
4213 static BOOL GetMenuItemInfo_common ( HMENU hmenu, UINT item, BOOL bypos,
4214 LPMENUITEMINFOW lpmii, BOOL unicode)
4216 MENUITEM *menu = MENU_FindItem (&hmenu, &item, bypos? MF_BYPOSITION : 0);
4218 debug_print_menuitem("GetMenuItemInfo_common: ", menu, "");
4223 if (lpmii->fMask & MIIM_TYPE) {
4224 lpmii->fType = menu->fType;
4225 switch (MENU_ITEM_TYPE(menu->fType)) {
4227 break; /* will be done below */
4230 lpmii->dwTypeData = menu->text;
4237 /* copy the text string */
4238 if ((lpmii->fMask & (MIIM_TYPE|MIIM_STRING)) &&
4239 (MENU_ITEM_TYPE(menu->fType) == MF_STRING) && menu->text)
4244 len = strlenW(menu->text);
4245 if(lpmii->dwTypeData && lpmii->cch)
4246 lstrcpynW(lpmii->dwTypeData, menu->text, lpmii->cch);
4250 len = WideCharToMultiByte( CP_ACP, 0, menu->text, -1, NULL, 0, NULL, NULL );
4251 if(lpmii->dwTypeData && lpmii->cch)
4252 if (!WideCharToMultiByte( CP_ACP, 0, menu->text, -1,
4253 (LPSTR)lpmii->dwTypeData, lpmii->cch, NULL, NULL ))
4254 ((LPSTR)lpmii->dwTypeData)[lpmii->cch-1] = 0;
4256 /* if we've copied a substring we return its length */
4257 if(lpmii->dwTypeData && lpmii->cch)
4259 if (lpmii->cch <= len) lpmii->cch--;
4261 else /* return length of string */
4265 if (lpmii->fMask & MIIM_FTYPE)
4266 lpmii->fType = menu->fType;
4268 if (lpmii->fMask & MIIM_BITMAP)
4269 lpmii->hbmpItem = menu->hbmpItem;
4271 if (lpmii->fMask & MIIM_STATE)
4272 lpmii->fState = menu->fState;
4274 if (lpmii->fMask & MIIM_ID)
4275 lpmii->wID = menu->wID;
4277 if (lpmii->fMask & MIIM_SUBMENU)
4278 lpmii->hSubMenu = menu->hSubMenu;
4280 if (lpmii->fMask & MIIM_CHECKMARKS) {
4281 lpmii->hbmpChecked = menu->hCheckBit;
4282 lpmii->hbmpUnchecked = menu->hUnCheckBit;
4284 if (lpmii->fMask & MIIM_DATA)
4285 lpmii->dwItemData = menu->dwItemData;
4290 /**********************************************************************
4291 * GetMenuItemInfoA (USER32.@)
4293 BOOL WINAPI GetMenuItemInfoA( HMENU hmenu, UINT item, BOOL bypos,
4294 LPMENUITEMINFOA lpmii)
4296 return GetMenuItemInfo_common (hmenu, item, bypos,
4297 (LPMENUITEMINFOW)lpmii, FALSE);
4300 /**********************************************************************
4301 * GetMenuItemInfoW (USER32.@)
4303 BOOL WINAPI GetMenuItemInfoW( HMENU hmenu, UINT item, BOOL bypos,
4304 LPMENUITEMINFOW lpmii)
4306 return GetMenuItemInfo_common (hmenu, item, bypos,
4311 /* set a menu item text from a ASCII or Unicode string */
4312 inline static void set_menu_item_text( MENUITEM *menu, LPCWSTR text, BOOL unicode )
4317 menu->fType |= MF_SEPARATOR;
4321 if ((menu->text = HeapAlloc( GetProcessHeap(), 0, (strlenW(text)+1) * sizeof(WCHAR) )))
4322 strcpyW( menu->text, text );
4326 LPCSTR str = (LPCSTR)text;
4327 int len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
4328 if ((menu->text = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) )))
4329 MultiByteToWideChar( CP_ACP, 0, str, -1, menu->text, len );
4334 /**********************************************************************
4335 * SetMenuItemInfo_common
4338 static BOOL SetMenuItemInfo_common(MENUITEM * menu,
4339 const MENUITEMINFOW *lpmii,
4342 if (!menu) return FALSE;
4344 debug_print_menuitem("MENU_SetItemInfo_common from: ", menu, "");
4346 if (lpmii->fMask & MIIM_TYPE ) {
4347 /* Get rid of old string. */
4348 if ( IS_STRING_ITEM(menu->fType) && menu->text) {
4349 HeapFree(GetProcessHeap(), 0, menu->text);
4353 /* make only MENU_ITEM_TYPE bits in menu->fType equal lpmii->fType */
4354 menu->fType &= ~MENU_ITEM_TYPE(menu->fType);
4355 menu->fType |= MENU_ITEM_TYPE(lpmii->fType);
4357 menu->text = lpmii->dwTypeData;
4359 if (IS_STRING_ITEM(menu->fType))
4360 set_menu_item_text( menu, lpmii->dwTypeData, unicode );
4363 if (lpmii->fMask & MIIM_FTYPE ) {
4364 /* free the string when the type is changing */
4365 if ( (!IS_STRING_ITEM(lpmii->fType)) && IS_STRING_ITEM(menu->fType) && menu->text) {
4366 HeapFree(GetProcessHeap(), 0, menu->text);
4369 menu->fType &= ~MENU_ITEM_TYPE(menu->fType);
4370 menu->fType |= MENU_ITEM_TYPE(lpmii->fType);
4371 if ( IS_STRING_ITEM(menu->fType) && !menu->text )
4372 menu->fType |= MF_SEPARATOR;
4375 if (lpmii->fMask & MIIM_STRING ) {
4376 /* free the string when used */
4377 if ( IS_STRING_ITEM(menu->fType) && menu->text) {
4378 HeapFree(GetProcessHeap(), 0, menu->text);
4379 set_menu_item_text( menu, lpmii->dwTypeData, unicode );
4383 if (lpmii->fMask & MIIM_STATE)
4385 /* fixme: MFS_DEFAULT do we have to reset the other menu items? */
4386 menu->fState = lpmii->fState;
4389 if (lpmii->fMask & MIIM_ID)
4390 menu->wID = lpmii->wID;
4392 if (lpmii->fMask & MIIM_SUBMENU) {
4393 menu->hSubMenu = lpmii->hSubMenu;
4394 if (menu->hSubMenu) {
4395 POPUPMENU *subMenu = MENU_GetMenu((UINT16)menu->hSubMenu);
4397 subMenu->wFlags |= MF_POPUP;
4398 menu->fType |= MF_POPUP;
4401 /* FIXME: Return an error ? */
4402 menu->fType &= ~MF_POPUP;
4405 menu->fType &= ~MF_POPUP;
4408 if (lpmii->fMask & MIIM_CHECKMARKS)
4410 if (lpmii->fType & MFT_RADIOCHECK)
4411 menu->fType |= MFT_RADIOCHECK;
4413 menu->hCheckBit = lpmii->hbmpChecked;
4414 menu->hUnCheckBit = lpmii->hbmpUnchecked;
4416 if (lpmii->fMask & MIIM_DATA)
4417 menu->dwItemData = lpmii->dwItemData;
4419 debug_print_menuitem("SetMenuItemInfo_common to : ", menu, "");
4423 /**********************************************************************
4424 * SetMenuItemInfoA (USER32.@)
4426 BOOL WINAPI SetMenuItemInfoA(HMENU hmenu, UINT item, BOOL bypos,
4427 const MENUITEMINFOA *lpmii)
4429 if ((lpmii->fType & (MF_HILITE|MF_POPUP)) || (lpmii->fState)) {
4430 /* QuickTime does pass invalid data into SetMenuItemInfo.
4431 * do some of the checks Windows does.
4433 WARN("Bad masks for type (0x%08x) or state (0x%08x)\n",
4434 lpmii->fType,lpmii->fState );
4438 return SetMenuItemInfo_common(MENU_FindItem(&hmenu, &item, bypos? MF_BYPOSITION : 0),
4439 (const MENUITEMINFOW *)lpmii, FALSE);
4442 /**********************************************************************
4443 * SetMenuItemInfoW (USER32.@)
4445 BOOL WINAPI SetMenuItemInfoW(HMENU hmenu, UINT item, BOOL bypos,
4446 const MENUITEMINFOW *lpmii)
4448 return SetMenuItemInfo_common(MENU_FindItem(&hmenu, &item, bypos? MF_BYPOSITION : 0),
4452 /**********************************************************************
4453 * SetMenuDefaultItem (USER32.@)
4456 BOOL WINAPI SetMenuDefaultItem(HMENU hmenu, UINT uItem, UINT bypos)
4462 TRACE("(0x%x,%d,%d)\n", hmenu, uItem, bypos);
4464 if (!(menu = MENU_GetMenu(hmenu))) return FALSE;
4466 /* reset all default-item flags */
4468 for (i = 0; i < menu->nItems; i++, item++)
4470 item->fState &= ~MFS_DEFAULT;
4473 /* no default item */
4482 if ( uItem >= menu->nItems ) return FALSE;
4483 item[uItem].fState |= MFS_DEFAULT;
4488 for (i = 0; i < menu->nItems; i++, item++)
4490 if (item->wID == uItem)
4492 item->fState |= MFS_DEFAULT;
4501 /**********************************************************************
4502 * GetMenuDefaultItem (USER32.@)
4504 UINT WINAPI GetMenuDefaultItem(HMENU hmenu, UINT bypos, UINT flags)
4510 TRACE("(0x%x,%d,%d)\n", hmenu, bypos, flags);
4512 if (!(menu = MENU_GetMenu(hmenu))) return -1;
4514 /* find default item */
4518 if (! item) return -1;
4520 while ( !( item->fState & MFS_DEFAULT ) )
4523 if (i >= menu->nItems ) return -1;
4526 /* default: don't return disabled items */
4527 if ( (!(GMDI_USEDISABLED & flags)) && (item->fState & MFS_DISABLED )) return -1;
4529 /* search rekursiv when needed */
4530 if ( (item->fType & MF_POPUP) && (flags & GMDI_GOINTOPOPUPS) )
4533 ret = GetMenuDefaultItem( item->hSubMenu, bypos, flags );
4534 if ( -1 != ret ) return ret;
4536 /* when item not found in submenu, return the popup item */
4538 return ( bypos ) ? i : item->wID;
4542 /*******************************************************************
4543 * InsertMenuItem (USER.441)
4547 BOOL16 WINAPI InsertMenuItem16( HMENU16 hmenu, UINT16 pos, BOOL16 byposition,
4548 const MENUITEMINFO16 *mii )
4552 miia.cbSize = sizeof(miia);
4553 miia.fMask = mii->fMask;
4554 miia.dwTypeData = (LPSTR)mii->dwTypeData;
4555 miia.fType = mii->fType;
4556 miia.fState = mii->fState;
4557 miia.wID = mii->wID;
4558 miia.hSubMenu = mii->hSubMenu;
4559 miia.hbmpChecked = mii->hbmpChecked;
4560 miia.hbmpUnchecked = mii->hbmpUnchecked;
4561 miia.dwItemData = mii->dwItemData;
4562 miia.cch = mii->cch;
4563 if (IS_STRING_ITEM(miia.fType))
4564 miia.dwTypeData = MapSL(mii->dwTypeData);
4565 return InsertMenuItemA( hmenu, pos, byposition, &miia );
4569 /**********************************************************************
4570 * InsertMenuItemA (USER32.@)
4572 BOOL WINAPI InsertMenuItemA(HMENU hMenu, UINT uItem, BOOL bypos,
4573 const MENUITEMINFOA *lpmii)
4575 MENUITEM *item = MENU_InsertItem(hMenu, uItem, bypos ? MF_BYPOSITION : 0 );
4576 return SetMenuItemInfo_common(item, (const MENUITEMINFOW *)lpmii, FALSE);
4580 /**********************************************************************
4581 * InsertMenuItemW (USER32.@)
4583 BOOL WINAPI InsertMenuItemW(HMENU hMenu, UINT uItem, BOOL bypos,
4584 const MENUITEMINFOW *lpmii)
4586 MENUITEM *item = MENU_InsertItem(hMenu, uItem, bypos ? MF_BYPOSITION : 0 );
4587 return SetMenuItemInfo_common(item, lpmii, TRUE);
4590 /**********************************************************************
4591 * CheckMenuRadioItem (USER32.@)
4594 BOOL WINAPI CheckMenuRadioItem(HMENU hMenu,
4595 UINT first, UINT last, UINT check,
4598 MENUITEM *mifirst, *milast, *micheck;
4599 HMENU mfirst = hMenu, mlast = hMenu, mcheck = hMenu;
4601 TRACE("ox%x: %d-%d, check %d, bypos=%d\n",
4602 hMenu, first, last, check, bypos);
4604 mifirst = MENU_FindItem (&mfirst, &first, bypos);
4605 milast = MENU_FindItem (&mlast, &last, bypos);
4606 micheck = MENU_FindItem (&mcheck, &check, bypos);
4608 if (mifirst == NULL || milast == NULL || micheck == NULL ||
4609 mifirst > milast || mfirst != mlast || mfirst != mcheck ||
4610 micheck > milast || micheck < mifirst)
4613 while (mifirst <= milast)
4615 if (mifirst == micheck)
4617 mifirst->fType |= MFT_RADIOCHECK;
4618 mifirst->fState |= MFS_CHECKED;
4620 mifirst->fType &= ~MFT_RADIOCHECK;
4621 mifirst->fState &= ~MFS_CHECKED;
4629 /**********************************************************************
4630 * CheckMenuRadioItem (USER.666)
4632 BOOL16 WINAPI CheckMenuRadioItem16(HMENU16 hMenu,
4633 UINT16 first, UINT16 last, UINT16 check,
4636 return CheckMenuRadioItem (hMenu, first, last, check, bypos);
4639 /**********************************************************************
4640 * GetMenuItemRect (USER32.@)
4642 * ATTENTION: Here, the returned values in rect are the screen
4643 * coordinates of the item just like if the menu was
4644 * always on the upper left side of the application.
4647 BOOL WINAPI GetMenuItemRect (HWND hwnd, HMENU hMenu, UINT uItem,
4650 POPUPMENU *itemMenu;
4654 TRACE("(0x%x,0x%x,%d,%p)\n", hwnd, hMenu, uItem, rect);
4656 item = MENU_FindItem (&hMenu, &uItem, MF_BYPOSITION);
4657 referenceHwnd = hwnd;
4661 itemMenu = MENU_GetMenu(hMenu);
4662 if (itemMenu == NULL)
4665 if(itemMenu->hWnd == 0)
4667 referenceHwnd = itemMenu->hWnd;
4670 if ((rect == NULL) || (item == NULL))
4675 MapWindowPoints(referenceHwnd, 0, (LPPOINT)rect, 2);
4681 /**********************************************************************
4682 * SetMenuInfo (USER32.@)
4685 * MIM_APPLYTOSUBMENUS
4686 * actually use the items to draw the menu
4688 BOOL WINAPI SetMenuInfo (HMENU hMenu, LPCMENUINFO lpmi)
4692 TRACE("(0x%04x %p)\n", hMenu, lpmi);
4694 if (lpmi && (lpmi->cbSize==sizeof(MENUINFO)) && (menu = MENU_GetMenu(hMenu)))
4697 if (lpmi->fMask & MIM_BACKGROUND)
4698 menu->hbrBack = lpmi->hbrBack;
4700 if (lpmi->fMask & MIM_HELPID)
4701 menu->dwContextHelpID = lpmi->dwContextHelpID;
4703 if (lpmi->fMask & MIM_MAXHEIGHT)
4704 menu->cyMax = lpmi->cyMax;
4706 if (lpmi->fMask & MIM_MENUDATA)
4707 menu->dwMenuData = lpmi->dwMenuData;
4709 if (lpmi->fMask & MIM_STYLE)
4710 menu->dwStyle = lpmi->dwStyle;
4717 /**********************************************************************
4718 * GetMenuInfo (USER32.@)
4724 BOOL WINAPI GetMenuInfo (HMENU hMenu, LPMENUINFO lpmi)
4727 TRACE("(0x%04x %p)\n", hMenu, lpmi);
4729 if (lpmi && (menu = MENU_GetMenu(hMenu)))
4732 if (lpmi->fMask & MIM_BACKGROUND)
4733 lpmi->hbrBack = menu->hbrBack;
4735 if (lpmi->fMask & MIM_HELPID)
4736 lpmi->dwContextHelpID = menu->dwContextHelpID;
4738 if (lpmi->fMask & MIM_MAXHEIGHT)
4739 lpmi->cyMax = menu->cyMax;
4741 if (lpmi->fMask & MIM_MENUDATA)
4742 lpmi->dwMenuData = menu->dwMenuData;
4744 if (lpmi->fMask & MIM_STYLE)
4745 lpmi->dwStyle = menu->dwStyle;
4752 /**********************************************************************
4753 * SetMenuContextHelpId (USER.384)
4755 BOOL16 WINAPI SetMenuContextHelpId16( HMENU16 hMenu, DWORD dwContextHelpID)
4757 return SetMenuContextHelpId( hMenu, dwContextHelpID );
4761 /**********************************************************************
4762 * SetMenuContextHelpId (USER32.@)
4764 BOOL WINAPI SetMenuContextHelpId( HMENU hMenu, DWORD dwContextHelpID)
4768 TRACE("(0x%04x 0x%08lx)\n", hMenu, dwContextHelpID);
4770 if ((menu = MENU_GetMenu(hMenu)))
4772 menu->dwContextHelpID = dwContextHelpID;
4778 /**********************************************************************
4779 * GetMenuContextHelpId (USER.385)
4781 DWORD WINAPI GetMenuContextHelpId16( HMENU16 hMenu )
4783 return GetMenuContextHelpId( hMenu );
4786 /**********************************************************************
4787 * GetMenuContextHelpId (USER32.@)
4789 DWORD WINAPI GetMenuContextHelpId( HMENU hMenu )
4793 TRACE("(0x%04x)\n", hMenu);
4795 if ((menu = MENU_GetMenu(hMenu)))
4797 return menu->dwContextHelpID;
4802 /**********************************************************************
4803 * MenuItemFromPoint (USER32.@)
4805 UINT WINAPI MenuItemFromPoint(HWND hWnd, HMENU hMenu, POINT ptScreen)
4807 FIXME("(0x%04x,0x%04x,(%ld,%ld)):stub\n",
4808 hWnd, hMenu, ptScreen.x, ptScreen.y);
4813 /**********************************************************************
4814 * translate_accelerator
4816 static BOOL translate_accelerator( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam,
4817 BYTE fVirt, WORD key, WORD cmd )
4821 if (wParam != key) return FALSE;
4823 if (message == WM_CHAR)
4825 if ( !(fVirt & FALT) && !(fVirt & FVIRTKEY) )
4827 TRACE_(accel)("found accel for WM_CHAR: ('%c')\n", wParam & 0xff);
4833 if(fVirt & FVIRTKEY)
4836 TRACE_(accel)("found accel for virt_key %04x (scan %04x)\n",
4837 wParam, 0xff & HIWORD(lParam));
4838 if(GetKeyState(VK_SHIFT) & 0x8000) mask |= FSHIFT;
4839 if(GetKeyState(VK_CONTROL) & 0x8000) mask |= FCONTROL;
4840 if(GetKeyState(VK_MENU) & 0x8000) mask |= FALT;
4841 if(mask == (fVirt & (FSHIFT | FCONTROL | FALT))) goto found;
4842 TRACE_(accel)(", but incorrect SHIFT/CTRL/ALT-state\n");
4846 if (!(lParam & 0x01000000)) /* no special_key */
4848 if ((fVirt & FALT) && (lParam & 0x20000000))
4849 { /* ^^ ALT pressed */
4850 TRACE_(accel)("found accel for Alt-%c\n", wParam & 0xff);
4859 if (message == WM_KEYUP || message == WM_SYSKEYUP)
4861 else if (GetCapture())
4863 else if (!IsWindowEnabled(hWnd))
4867 HMENU hMenu, hSubMenu, hSysMenu;
4868 UINT uSysStat = (UINT)-1, uStat = (UINT)-1, nPos;
4870 hMenu = (GetWindowLongA( hWnd, GWL_STYLE ) & WS_CHILD) ? 0 : GetMenu(hWnd);
4871 hSysMenu = get_win_sys_menu( hWnd );
4873 /* find menu item and ask application to initialize it */
4874 /* 1. in the system menu */
4875 hSubMenu = hSysMenu;
4877 if(MENU_FindItem(&hSubMenu, &nPos, MF_BYCOMMAND))
4879 SendMessageA(hWnd, WM_INITMENU, (WPARAM)hSysMenu, 0L);
4880 if(hSubMenu != hSysMenu)
4882 nPos = MENU_FindSubMenu(&hSysMenu, hSubMenu);
4883 TRACE_(accel)("hSysMenu = %04x, hSubMenu = %04x, nPos = %d\n", hSysMenu, hSubMenu, nPos);
4884 SendMessageA(hWnd, WM_INITMENUPOPUP, (WPARAM)hSubMenu, MAKELPARAM(nPos, TRUE));
4886 uSysStat = GetMenuState(GetSubMenu(hSysMenu, 0), cmd, MF_BYCOMMAND);
4888 else /* 2. in the window's menu */
4892 if(MENU_FindItem(&hSubMenu, &nPos, MF_BYCOMMAND))
4894 SendMessageA(hWnd, WM_INITMENU, (WPARAM)hMenu, 0L);
4895 if(hSubMenu != hMenu)
4897 nPos = MENU_FindSubMenu(&hMenu, hSubMenu);
4898 TRACE_(accel)("hMenu = %04x, hSubMenu = %04x, nPos = %d\n", hMenu, hSubMenu, nPos);
4899 SendMessageA(hWnd, WM_INITMENUPOPUP, (WPARAM)hSubMenu, MAKELPARAM(nPos, FALSE));
4901 uStat = GetMenuState(hMenu, cmd, MF_BYCOMMAND);
4905 if (uSysStat != (UINT)-1)
4907 if (uSysStat & (MF_DISABLED|MF_GRAYED))
4914 if (uStat != (UINT)-1)
4920 if (uStat & (MF_DISABLED|MF_GRAYED))
4931 if( mesg==WM_COMMAND )
4933 TRACE_(accel)(", sending WM_COMMAND, wParam=%0x\n", 0x10000 | cmd);
4934 SendMessageA(hWnd, mesg, 0x10000 | cmd, 0L);
4936 else if( mesg==WM_SYSCOMMAND )
4938 TRACE_(accel)(", sending WM_SYSCOMMAND, wParam=%0x\n", cmd);
4939 SendMessageA(hWnd, mesg, cmd, 0x00010000L);
4943 /* some reasons for NOT sending the WM_{SYS}COMMAND message:
4944 * #0: unknown (please report!)
4945 * #1: for WM_KEYUP,WM_SYSKEYUP
4946 * #2: mouse is captured
4947 * #3: window is disabled
4948 * #4: it's a disabled system menu option
4949 * #5: it's a menu option, but window is iconic
4950 * #6: it's a menu option, but disabled
4952 TRACE_(accel)(", but won't send WM_{SYS}COMMAND, reason is #%d\n",mesg);
4954 ERR_(accel)(" unknown reason - please report!");
4959 /**********************************************************************
4960 * TranslateAccelerator (USER32.@)
4961 * TranslateAcceleratorA (USER32.@)
4962 * TranslateAcceleratorW (USER32.@)
4964 INT WINAPI TranslateAccelerator( HWND hWnd, HACCEL hAccel, LPMSG msg )
4967 LPACCEL16 lpAccelTbl;
4972 WARN_(accel)("msg null; should hang here to be win compatible\n");
4975 if (!hAccel || !(lpAccelTbl = (LPACCEL16) LockResource16(hAccel)))
4977 WARN_(accel)("invalid accel handle=%x\n", hAccel);
4980 if ((msg->message != WM_KEYDOWN &&
4981 msg->message != WM_KEYUP &&
4982 msg->message != WM_SYSKEYDOWN &&
4983 msg->message != WM_SYSKEYUP &&
4984 msg->message != WM_CHAR)) return 0;
4986 TRACE_(accel)("TranslateAccelerators hAccel=%04x, hWnd=%04x,"
4987 "msg->hwnd=%04x, msg->message=%04x, wParam=%08x, lParam=%lx\n",
4988 hAccel,hWnd,msg->hwnd,msg->message,msg->wParam,msg->lParam);
4993 if (translate_accelerator( hWnd, msg->message, msg->wParam, msg->lParam,
4994 lpAccelTbl[i].fVirt, lpAccelTbl[i].key, lpAccelTbl[i].cmd))
4996 } while ((lpAccelTbl[i++].fVirt & 0x80) == 0);
4997 WARN_(accel)("couldn't translate accelerator key\n");