Added first/last message filters to MSG_InternalGetMessage to avoid
[wine/multimedia.git] / controls / menu.c
blob3a33c9ca13c2730f41701053f609287e4d134a47
1 /*
2 * Menu functions
4 * Copyright 1993 Martin Ayotte
5 * Copyright 1994 Alexandre Julliard
6 * Copyright 1997 Morten Welinder
7 */
9 /*
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.
15 #include <assert.h>
16 #include <ctype.h>
17 #include <stdlib.h>
18 #include <string.h>
20 #include "windef.h"
21 #include "winnls.h"
22 #include "wingdi.h"
23 #include "wine/winbase16.h"
24 #include "wine/winuser16.h"
25 #include "wine/unicode.h"
26 #include "wine/port.h"
27 #include "win.h"
28 #include "task.h"
29 #include "heap.h"
30 #include "controls.h"
31 #include "nonclient.h"
32 #include "user.h"
33 #include "message.h"
34 #include "queue.h"
36 #include "debugtools.h"
38 DEFAULT_DEBUG_CHANNEL(menu);
39 DECLARE_DEBUG_CHANNEL(accel);
41 /* internal popup menu window messages */
43 #define MM_SETMENUHANDLE (WM_USER + 0)
44 #define MM_GETMENUHANDLE (WM_USER + 1)
46 /* Menu item structure */
47 typedef struct {
48 /* ----------- MENUITEMINFO Stuff ----------- */
49 UINT fType; /* Item type. */
50 UINT fState; /* Item state. */
51 UINT wID; /* Item id. */
52 HMENU hSubMenu; /* Pop-up menu. */
53 HBITMAP hCheckBit; /* Bitmap when checked. */
54 HBITMAP hUnCheckBit; /* Bitmap when unchecked. */
55 LPWSTR text; /* Item text or bitmap handle. */
56 DWORD dwItemData; /* Application defined. */
57 DWORD dwTypeData; /* depends on fMask */
58 HBITMAP hbmpItem; /* bitmap in win98 style menus */
59 /* ----------- Wine stuff ----------- */
60 RECT rect; /* Item area (relative to menu window) */
61 UINT xTab; /* X position of text after Tab */
62 } MENUITEM;
64 /* Popup menu structure */
65 typedef struct {
66 WORD wFlags; /* Menu flags (MF_POPUP, MF_SYSMENU) */
67 WORD wMagic; /* Magic number */
68 HQUEUE16 hTaskQ; /* Task queue for this menu */
69 WORD Width; /* Width of the whole menu */
70 WORD Height; /* Height of the whole menu */
71 WORD nItems; /* Number of items in the menu */
72 HWND hWnd; /* Window containing the menu */
73 MENUITEM *items; /* Array of menu items */
74 UINT FocusedItem; /* Currently focused item */
75 HWND hwndOwner; /* window receiving the messages for ownerdraw */
76 BOOL bTimeToHide; /* Request hiding when receiving a second click in the top-level menu item */
77 /* ------------ MENUINFO members ------ */
78 DWORD dwStyle; /* Extended mennu style */
79 UINT cyMax; /* max hight of the whole menu, 0 is screen hight */
80 HBRUSH hbrBack; /* brush for menu background */
81 DWORD dwContextHelpID;
82 DWORD dwMenuData; /* application defined value */
83 HMENU hSysMenuOwner; /* Handle to the dummy sys menu holder */
84 } POPUPMENU, *LPPOPUPMENU;
86 /* internal flags for menu tracking */
88 #define TF_ENDMENU 0x0001
89 #define TF_SUSPENDPOPUP 0x0002
90 #define TF_SKIPREMOVE 0x0004
92 typedef struct
94 UINT trackFlags;
95 HMENU hCurrentMenu; /* current submenu (can be equal to hTopMenu)*/
96 HMENU hTopMenu; /* initial menu */
97 HWND hOwnerWnd; /* where notifications are sent */
98 POINT pt;
99 } MTRACKER;
101 #define MENU_MAGIC 0x554d /* 'MU' */
102 #define IS_A_MENU(pmenu) ((pmenu) && (pmenu)->wMagic == MENU_MAGIC)
104 #define ITEM_PREV -1
105 #define ITEM_NEXT 1
107 /* Internal MENU_TrackMenu() flags */
108 #define TPM_INTERNAL 0xF0000000
109 #define TPM_ENTERIDLEEX 0x80000000 /* set owner window for WM_ENTERIDLE */
110 #define TPM_BUTTONDOWN 0x40000000 /* menu was clicked before tracking */
111 #define TPM_POPUPMENU 0x20000000 /* menu is a popup menu */
113 /* popup menu shade thickness */
114 #define POPUP_XSHADE 4
115 #define POPUP_YSHADE 4
117 /* Space between 2 menu bar items */
118 #define MENU_BAR_ITEMS_SPACE 12
120 /* Minimum width of a tab character */
121 #define MENU_TAB_SPACE 8
123 /* Height of a separator item */
124 #define SEPARATOR_HEIGHT 5
126 /* (other menu->FocusedItem values give the position of the focused item) */
127 #define NO_SELECTED_ITEM 0xffff
129 #define MENU_ITEM_TYPE(flags) \
130 ((flags) & (MF_STRING | MF_BITMAP | MF_OWNERDRAW | MF_SEPARATOR))
132 #define IS_STRING_ITEM(flags) (MENU_ITEM_TYPE ((flags)) == MF_STRING)
133 #define IS_BITMAP_ITEM(flags) (MENU_ITEM_TYPE ((flags)) == MF_BITMAP)
134 #define IS_MAGIC_ITEM(text) (LOWORD((int)text)<12)
136 #define IS_SYSTEM_MENU(menu) \
137 (!((menu)->wFlags & MF_POPUP) && (menu)->wFlags & MF_SYSMENU)
139 #define IS_SYSTEM_POPUP(menu) \
140 ((menu)->wFlags & MF_POPUP && (menu)->wFlags & MF_SYSMENU)
142 #define TYPE_MASK (MFT_STRING | MFT_BITMAP | MFT_OWNERDRAW | MFT_SEPARATOR | \
143 MFT_MENUBARBREAK | MFT_MENUBREAK | MFT_RADIOCHECK | \
144 MFT_RIGHTORDER | MFT_RIGHTJUSTIFY | \
145 MF_POPUP | MF_SYSMENU | MF_HELP)
146 #define STATE_MASK (~TYPE_MASK)
148 /* Dimension of the menu bitmaps */
149 static WORD arrow_bitmap_width = 0, arrow_bitmap_height = 0;
151 static HBITMAP hStdMnArrow = 0;
153 /* Minimze/restore/close buttons to be inserted in menubar */
154 static HBITMAP hBmpMinimize = 0;
155 static HBITMAP hBmpMinimizeD = 0;
156 static HBITMAP hBmpMaximize = 0;
157 static HBITMAP hBmpMaximizeD = 0;
158 static HBITMAP hBmpClose = 0;
159 static HBITMAP hBmpCloseD = 0;
162 static HBRUSH hShadeBrush = 0;
163 static HFONT hMenuFont = 0;
164 static HFONT hMenuFontBold = 0;
166 static HMENU MENU_DefSysPopup = 0; /* Default system menu popup */
168 /* Use global popup window because there's no way 2 menus can
169 * be tracked at the same time. */
171 static WND* pTopPopupWnd = 0;
172 static UINT uSubPWndLevel = 0;
174 /* Flag set by EndMenu() to force an exit from menu tracking */
175 static BOOL fEndMenu = FALSE;
177 static LRESULT WINAPI PopupMenuWndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam );
180 /*********************************************************************
181 * menu class descriptor
183 const struct builtin_class_descr MENU_builtin_class =
185 POPUPMENU_CLASS_ATOM, /* name */
186 CS_GLOBALCLASS | CS_SAVEBITS, /* style */
187 NULL, /* procA (winproc is Unicode only) */
188 PopupMenuWndProc, /* procW */
189 sizeof(HMENU), /* extra */
190 IDC_ARROWA, /* cursor */
191 COLOR_MENU+1 /* brush */
195 /***********************************************************************
196 * debug_print_menuitem
198 * Print a menuitem in readable form.
201 #define debug_print_menuitem(pre, mp, post) \
202 if(!TRACE_ON(menu)) ; else do_debug_print_menuitem(pre, mp, post)
204 #define MENUOUT(text) \
205 DPRINTF("%s%s", (count++ ? "," : ""), (text))
207 #define MENUFLAG(bit,text) \
208 do { \
209 if (flags & (bit)) { flags &= ~(bit); MENUOUT ((text)); } \
210 } while (0)
212 static void do_debug_print_menuitem(const char *prefix, MENUITEM * mp,
213 const char *postfix)
215 TRACE("%s ", prefix);
216 if (mp) {
217 UINT flags = mp->fType;
218 int typ = MENU_ITEM_TYPE(flags);
219 DPRINTF( "{ ID=0x%x", mp->wID);
220 if (flags & MF_POPUP)
221 DPRINTF( ", Sub=0x%x", mp->hSubMenu);
222 if (flags) {
223 int count = 0;
224 DPRINTF( ", Typ=");
225 if (typ == MFT_STRING)
226 /* Nothing */ ;
227 else if (typ == MFT_SEPARATOR)
228 MENUOUT("sep");
229 else if (typ == MFT_OWNERDRAW)
230 MENUOUT("own");
231 else if (typ == MFT_BITMAP)
232 MENUOUT("bit");
233 else
234 MENUOUT("???");
235 flags -= typ;
237 MENUFLAG(MF_POPUP, "pop");
238 MENUFLAG(MFT_MENUBARBREAK, "barbrk");
239 MENUFLAG(MFT_MENUBREAK, "brk");
240 MENUFLAG(MFT_RADIOCHECK, "radio");
241 MENUFLAG(MFT_RIGHTORDER, "rorder");
242 MENUFLAG(MF_SYSMENU, "sys");
243 MENUFLAG(MFT_RIGHTJUSTIFY, "right"); /* same as MF_HELP */
245 if (flags)
246 DPRINTF( "+0x%x", flags);
248 flags = mp->fState;
249 if (flags) {
250 int count = 0;
251 DPRINTF( ", State=");
252 MENUFLAG(MFS_GRAYED, "grey");
253 MENUFLAG(MFS_DEFAULT, "default");
254 MENUFLAG(MFS_DISABLED, "dis");
255 MENUFLAG(MFS_CHECKED, "check");
256 MENUFLAG(MFS_HILITE, "hi");
257 MENUFLAG(MF_USECHECKBITMAPS, "usebit");
258 MENUFLAG(MF_MOUSESELECT, "mouse");
259 if (flags)
260 DPRINTF( "+0x%x", flags);
262 if (mp->hCheckBit)
263 DPRINTF( ", Chk=0x%x", mp->hCheckBit);
264 if (mp->hUnCheckBit)
265 DPRINTF( ", Unc=0x%x", mp->hUnCheckBit);
267 if (typ == MFT_STRING) {
268 if (mp->text)
269 DPRINTF( ", Text=%s", debugstr_w(mp->text));
270 else
271 DPRINTF( ", Text=Null");
272 } else if (mp->text == NULL)
273 /* Nothing */ ;
274 else
275 DPRINTF( ", Text=%p", mp->text);
276 if (mp->dwItemData)
277 DPRINTF( ", ItemData=0x%08lx", mp->dwItemData);
278 DPRINTF( " }");
279 } else {
280 DPRINTF( "NULL");
283 DPRINTF(" %s\n", postfix);
286 #undef MENUOUT
287 #undef MENUFLAG
290 /***********************************************************************
291 * MENU_GetMenu
293 * Validate the given menu handle and returns the menu structure pointer.
295 POPUPMENU *MENU_GetMenu(HMENU hMenu)
297 POPUPMENU *menu;
298 menu = (POPUPMENU *) USER_HEAP_LIN_ADDR(hMenu);
299 if (!IS_A_MENU(menu))
301 WARN("invalid menu handle=%x, ptr=%p, magic=%x\n", hMenu, menu, menu? menu->wMagic:0);
302 menu = NULL;
304 return menu;
307 /***********************************************************************
308 * MENU_CopySysPopup
310 * Return the default system menu.
312 static HMENU MENU_CopySysPopup(void)
314 HMENU hMenu = LoadMenuA(GetModuleHandleA("USER32"), "SYSMENU");
316 if( hMenu ) {
317 POPUPMENU* menu = (POPUPMENU *) USER_HEAP_LIN_ADDR(hMenu);
318 menu->wFlags |= MF_SYSMENU | MF_POPUP;
319 SetMenuDefaultItem(hMenu, SC_CLOSE, FALSE);
321 else {
322 hMenu = 0;
323 ERR("Unable to load default system menu\n" );
326 TRACE("returning %x.\n", hMenu );
328 return hMenu;
331 /***********************************************************************
332 * MENU_GetTopPopupWnd()
334 * Return the locked pointer pTopPopupWnd.
336 static WND *MENU_GetTopPopupWnd()
338 return WIN_LockWndPtr(pTopPopupWnd);
340 /***********************************************************************
341 * MENU_ReleaseTopPopupWnd()
343 * Release the locked pointer pTopPopupWnd.
345 static void MENU_ReleaseTopPopupWnd()
347 WIN_ReleaseWndPtr(pTopPopupWnd);
349 /***********************************************************************
350 * MENU_DestroyTopPopupWnd()
352 * Destroy the locked pointer pTopPopupWnd.
354 static void MENU_DestroyTopPopupWnd()
356 WND *tmpWnd = pTopPopupWnd;
357 pTopPopupWnd = NULL;
358 WIN_ReleaseWndPtr(tmpWnd);
363 /**********************************************************************
364 * MENU_GetSysMenu
366 * Create a copy of the system menu. System menu in Windows is
367 * a special menu bar with the single entry - system menu popup.
368 * This popup is presented to the outside world as a "system menu".
369 * However, the real system menu handle is sometimes seen in the
370 * WM_MENUSELECT parameters (and Word 6 likes it this way).
372 HMENU MENU_GetSysMenu( HWND hWnd, HMENU hPopupMenu )
374 HMENU hMenu;
376 if ((hMenu = CreateMenu()))
378 POPUPMENU *menu = (POPUPMENU*) USER_HEAP_LIN_ADDR(hMenu);
379 menu->wFlags = MF_SYSMENU;
380 menu->hWnd = hWnd;
382 if (hPopupMenu == (HMENU)(-1))
383 hPopupMenu = MENU_CopySysPopup();
384 else if( !hPopupMenu ) hPopupMenu = MENU_DefSysPopup;
386 if (hPopupMenu)
388 InsertMenuA( hMenu, -1, MF_SYSMENU | MF_POPUP | MF_BYPOSITION, hPopupMenu, NULL );
390 menu->items[0].fType = MF_SYSMENU | MF_POPUP;
391 menu->items[0].fState = 0;
392 menu = (POPUPMENU*) USER_HEAP_LIN_ADDR(hPopupMenu);
393 menu->wFlags |= MF_SYSMENU;
395 TRACE("GetSysMenu hMenu=%04x (%04x)\n", hMenu, hPopupMenu );
396 return hMenu;
398 DestroyMenu( hMenu );
400 ERR("failed to load system menu!\n");
401 return 0;
405 /***********************************************************************
406 * MENU_Init
408 * Menus initialisation.
410 BOOL MENU_Init()
412 HBITMAP hBitmap;
413 NONCLIENTMETRICSA ncm;
415 static unsigned char shade_bits[16] = { 0x55, 0, 0xAA, 0,
416 0x55, 0, 0xAA, 0,
417 0x55, 0, 0xAA, 0,
418 0x55, 0, 0xAA, 0 };
420 /* Load menu bitmaps */
421 hStdMnArrow = LoadBitmapA(0, MAKEINTRESOURCEA(OBM_MNARROW));
422 /* Load system buttons bitmaps */
423 hBmpMinimize = LoadBitmapA(0,MAKEINTRESOURCEA(OBM_REDUCE));
424 hBmpMinimizeD = LoadBitmapA(0,MAKEINTRESOURCEA(OBM_REDUCED));
425 hBmpMaximize = LoadBitmapA(0,MAKEINTRESOURCEA(OBM_RESTORE));
426 hBmpMaximizeD = LoadBitmapA(0,MAKEINTRESOURCEA(OBM_RESTORED));
427 hBmpClose = LoadBitmapA(0,MAKEINTRESOURCEA(OBM_CLOSE));
428 hBmpCloseD = LoadBitmapA(0,MAKEINTRESOURCEA(OBM_CLOSED));
430 if (hStdMnArrow)
432 BITMAP bm;
433 GetObjectA( hStdMnArrow, sizeof(bm), &bm );
434 arrow_bitmap_width = bm.bmWidth;
435 arrow_bitmap_height = bm.bmHeight;
436 } else
437 return FALSE;
439 if (! (hBitmap = CreateBitmap( 8, 8, 1, 1, shade_bits)))
440 return FALSE;
442 if(!(hShadeBrush = CreatePatternBrush( hBitmap )))
443 return FALSE;
445 DeleteObject( hBitmap );
446 if (!(MENU_DefSysPopup = MENU_CopySysPopup()))
447 return FALSE;
449 ncm.cbSize = sizeof (NONCLIENTMETRICSA);
450 if (!(SystemParametersInfoA(SPI_GETNONCLIENTMETRICS, sizeof(NONCLIENTMETRICSA), &ncm, 0)))
451 return FALSE;
453 if (!(hMenuFont = CreateFontIndirectA( &ncm.lfMenuFont )))
454 return FALSE;
456 ncm.lfMenuFont.lfWeight += 300;
457 if ( ncm.lfMenuFont.lfWeight > 1000)
458 ncm.lfMenuFont.lfWeight = 1000;
460 if (!(hMenuFontBold = CreateFontIndirectA( &ncm.lfMenuFont )))
461 return FALSE;
463 return TRUE;
466 /***********************************************************************
467 * MENU_InitSysMenuPopup
469 * Grey the appropriate items in System menu.
471 static void MENU_InitSysMenuPopup( HMENU hmenu, DWORD style, DWORD clsStyle )
473 BOOL gray;
475 gray = !(style & WS_THICKFRAME) || (style & (WS_MAXIMIZE | WS_MINIMIZE));
476 EnableMenuItem( hmenu, SC_SIZE, (gray ? MF_GRAYED : MF_ENABLED) );
477 gray = ((style & WS_MAXIMIZE) != 0);
478 EnableMenuItem( hmenu, SC_MOVE, (gray ? MF_GRAYED : MF_ENABLED) );
479 gray = !(style & WS_MINIMIZEBOX) || (style & WS_MINIMIZE);
480 EnableMenuItem( hmenu, SC_MINIMIZE, (gray ? MF_GRAYED : MF_ENABLED) );
481 gray = !(style & WS_MAXIMIZEBOX) || (style & WS_MAXIMIZE);
482 EnableMenuItem( hmenu, SC_MAXIMIZE, (gray ? MF_GRAYED : MF_ENABLED) );
483 gray = !(style & (WS_MAXIMIZE | WS_MINIMIZE));
484 EnableMenuItem( hmenu, SC_RESTORE, (gray ? MF_GRAYED : MF_ENABLED) );
485 gray = (clsStyle & CS_NOCLOSE) != 0;
487 /* The menu item must keep its state if it's disabled */
488 if(gray)
489 EnableMenuItem( hmenu, SC_CLOSE, MF_GRAYED);
493 /******************************************************************************
495 * UINT MENU_GetStartOfNextColumn(
496 * HMENU hMenu )
498 *****************************************************************************/
500 static UINT MENU_GetStartOfNextColumn(
501 HMENU hMenu )
503 POPUPMENU *menu = (POPUPMENU *)USER_HEAP_LIN_ADDR(hMenu);
504 UINT i = menu->FocusedItem + 1;
506 if(!menu)
507 return NO_SELECTED_ITEM;
509 if( i == NO_SELECTED_ITEM )
510 return i;
512 for( ; i < menu->nItems; ++i ) {
513 if (menu->items[i].fType & MF_MENUBARBREAK)
514 return i;
517 return NO_SELECTED_ITEM;
521 /******************************************************************************
523 * UINT MENU_GetStartOfPrevColumn(
524 * HMENU hMenu )
526 *****************************************************************************/
528 static UINT MENU_GetStartOfPrevColumn(
529 HMENU hMenu )
531 POPUPMENU const *menu = (POPUPMENU *)USER_HEAP_LIN_ADDR(hMenu);
532 UINT i;
534 if( !menu )
535 return NO_SELECTED_ITEM;
537 if( menu->FocusedItem == 0 || menu->FocusedItem == NO_SELECTED_ITEM )
538 return NO_SELECTED_ITEM;
540 /* Find the start of the column */
542 for(i = menu->FocusedItem; i != 0 &&
543 !(menu->items[i].fType & MF_MENUBARBREAK);
544 --i); /* empty */
546 if(i == 0)
547 return NO_SELECTED_ITEM;
549 for(--i; i != 0; --i) {
550 if (menu->items[i].fType & MF_MENUBARBREAK)
551 break;
554 TRACE("ret %d.\n", i );
556 return i;
561 /***********************************************************************
562 * MENU_FindItem
564 * Find a menu item. Return a pointer on the item, and modifies *hmenu
565 * in case the item was in a sub-menu.
567 static MENUITEM *MENU_FindItem( HMENU *hmenu, UINT *nPos, UINT wFlags )
569 POPUPMENU *menu;
570 UINT i;
572 if (((*hmenu)==0xffff) || (!(menu = MENU_GetMenu(*hmenu)))) return NULL;
573 if (wFlags & MF_BYPOSITION)
575 if (*nPos >= menu->nItems) return NULL;
576 return &menu->items[*nPos];
578 else
580 MENUITEM *item = menu->items;
581 for (i = 0; i < menu->nItems; i++, item++)
583 if (item->wID == *nPos)
585 *nPos = i;
586 return item;
588 else if (item->fType & MF_POPUP)
590 HMENU hsubmenu = item->hSubMenu;
591 MENUITEM *subitem = MENU_FindItem( &hsubmenu, nPos, wFlags );
592 if (subitem)
594 *hmenu = hsubmenu;
595 return subitem;
600 return NULL;
603 /***********************************************************************
604 * MENU_FindSubMenu
606 * Find a Sub menu. Return the position of the submenu, and modifies
607 * *hmenu in case it is found in another sub-menu.
608 * If the submenu cannot be found, NO_SELECTED_ITEM is returned.
610 UINT MENU_FindSubMenu( HMENU *hmenu, HMENU hSubTarget )
612 POPUPMENU *menu;
613 UINT i;
614 MENUITEM *item;
615 if (((*hmenu)==0xffff) ||
616 (!(menu = MENU_GetMenu(*hmenu))))
617 return NO_SELECTED_ITEM;
618 item = menu->items;
619 for (i = 0; i < menu->nItems; i++, item++) {
620 if(!(item->fType & MF_POPUP)) continue;
621 if (item->hSubMenu == hSubTarget) {
622 return i;
624 else {
625 HMENU hsubmenu = item->hSubMenu;
626 UINT pos = MENU_FindSubMenu( &hsubmenu, hSubTarget );
627 if (pos != NO_SELECTED_ITEM) {
628 *hmenu = hsubmenu;
629 return pos;
633 return NO_SELECTED_ITEM;
636 /***********************************************************************
637 * MENU_FreeItemData
639 static void MENU_FreeItemData( MENUITEM* item )
641 /* delete text */
642 if (IS_STRING_ITEM(item->fType) && item->text)
643 HeapFree( GetProcessHeap(), 0, item->text );
646 /***********************************************************************
647 * MENU_FindItemByCoords
649 * Find the item at the specified coordinates (screen coords). Does
650 * not work for child windows and therefore should not be called for
651 * an arbitrary system menu.
653 static MENUITEM *MENU_FindItemByCoords( POPUPMENU *menu,
654 POINT pt, UINT *pos )
656 MENUITEM *item;
657 UINT i;
658 RECT wrect;
660 if (!GetWindowRect(menu->hWnd,&wrect)) return NULL;
661 pt.x -= wrect.left;pt.y -= wrect.top;
662 item = menu->items;
663 for (i = 0; i < menu->nItems; i++, item++)
665 if ((pt.x >= item->rect.left) && (pt.x < item->rect.right) &&
666 (pt.y >= item->rect.top) && (pt.y < item->rect.bottom))
668 if (pos) *pos = i;
669 return item;
672 return NULL;
676 /***********************************************************************
677 * MENU_FindItemByKey
679 * Find the menu item selected by a key press.
680 * Return item id, -1 if none, -2 if we should close the menu.
682 static UINT MENU_FindItemByKey( HWND hwndOwner, HMENU hmenu,
683 UINT key, BOOL forceMenuChar )
685 TRACE("\tlooking for '%c' in [%04x]\n", (char)key, (UINT16)hmenu );
687 if (!IsMenu( hmenu ))
689 WND* w = WIN_FindWndPtr(hwndOwner);
690 hmenu = GetSubMenu(w->hSysMenu, 0);
691 WIN_ReleaseWndPtr(w);
694 if (hmenu)
696 POPUPMENU *menu = MENU_GetMenu( hmenu );
697 MENUITEM *item = menu->items;
698 LONG menuchar;
700 if( !forceMenuChar )
702 UINT i;
704 key = toupper(key);
705 for (i = 0; i < menu->nItems; i++, item++)
707 if (item->text && (IS_STRING_ITEM(item->fType)))
709 WCHAR *p = item->text - 2;
712 p = strchrW (p + 2, '&');
714 while (p != NULL && p [1] == '&');
715 if (p && (toupper(p[1]) == key)) return i;
719 menuchar = SendMessageA( hwndOwner, WM_MENUCHAR,
720 MAKEWPARAM( key, menu->wFlags ), hmenu );
721 if (HIWORD(menuchar) == 2) return LOWORD(menuchar);
722 if (HIWORD(menuchar) == 1) return (UINT)(-2);
724 return (UINT)(-1);
726 /***********************************************************************
727 * MENU_LoadMagicItem
729 * Load the bitmap associated with the magic menu item and its style
732 static HBITMAP MENU_LoadMagicItem(UINT id, BOOL hilite, DWORD dwItemData)
735 * Magic menu item id's section
736 * These magic id's are used by windows to insert "standard" mdi
737 * buttons (minimize,restore,close) on menu. Under windows,
738 * these magic id's make sure the right things appear when those
739 * bitmap buttons are pressed/selected/released.
742 switch(id & 0xffff)
743 { case HBMMENU_SYSTEM:
744 return (dwItemData) ?
745 (HBITMAP)dwItemData :
746 (hilite ? hBmpMinimizeD : hBmpMinimize);
747 case HBMMENU_MBAR_RESTORE:
748 return (hilite ? hBmpMaximizeD: hBmpMaximize);
749 case HBMMENU_MBAR_MINIMIZE:
750 return (hilite ? hBmpMinimizeD : hBmpMinimize);
751 case HBMMENU_MBAR_CLOSE:
752 return (hilite ? hBmpCloseD : hBmpClose);
753 case HBMMENU_CALLBACK:
754 case HBMMENU_MBAR_CLOSE_D:
755 case HBMMENU_MBAR_MINIMIZE_D:
756 case HBMMENU_POPUP_CLOSE:
757 case HBMMENU_POPUP_RESTORE:
758 case HBMMENU_POPUP_MAXIMIZE:
759 case HBMMENU_POPUP_MINIMIZE:
760 default:
761 FIXME("Magic 0x%08x not implemented\n", id);
762 return 0;
767 /***********************************************************************
768 * MENU_CalcItemSize
770 * Calculate the size of the menu item and store it in lpitem->rect.
772 static void MENU_CalcItemSize( HDC hdc, MENUITEM *lpitem, HWND hwndOwner,
773 INT orgX, INT orgY, BOOL menuBar )
775 WCHAR *p;
776 UINT check_bitmap_width = GetSystemMetrics( SM_CXMENUCHECK );
778 TRACE("dc=0x%04x owner=0x%04x (%d,%d)\n", hdc, hwndOwner, orgX, orgY);
779 debug_print_menuitem("MENU_CalcItemSize: menuitem:", lpitem,
780 (menuBar ? " (MenuBar)" : ""));
782 SetRect( &lpitem->rect, orgX, orgY, orgX, orgY );
784 if (lpitem->fType & MF_OWNERDRAW)
787 ** Experimentation under Windows reveals that an owner-drawn
788 ** menu is expected to return the size of the content part of
789 ** the menu item, not including the checkmark nor the submenu
790 ** arrow. Windows adds those values itself and returns the
791 ** enlarged rectangle on subsequent WM_DRAWITEM messages.
793 MEASUREITEMSTRUCT mis;
794 mis.CtlType = ODT_MENU;
795 mis.CtlID = 0;
796 mis.itemID = lpitem->wID;
797 mis.itemData = (DWORD)lpitem->dwItemData;
798 mis.itemHeight = 0;
799 mis.itemWidth = 0;
800 SendMessageA( hwndOwner, WM_MEASUREITEM, 0, (LPARAM)&mis );
801 lpitem->rect.right += mis.itemWidth;
803 if (menuBar)
805 lpitem->rect.right += MENU_BAR_ITEMS_SPACE;
808 /* under at least win95 you seem to be given a standard
809 height for the menu and the height value is ignored */
811 if (TWEAK_WineLook == WIN31_LOOK)
812 lpitem->rect.bottom += GetSystemMetrics(SM_CYMENU);
813 else
814 lpitem->rect.bottom += GetSystemMetrics(SM_CYMENU)-1;
816 else
817 lpitem->rect.bottom += mis.itemHeight;
819 TRACE("id=%04x size=%dx%d\n",
820 lpitem->wID, mis.itemWidth, mis.itemHeight);
821 /* Fall through to get check/arrow width calculation. */
824 if (lpitem->fType & MF_SEPARATOR)
826 lpitem->rect.bottom += SEPARATOR_HEIGHT;
827 return;
830 if (!menuBar)
832 lpitem->rect.right += 2 * check_bitmap_width;
833 if (lpitem->fType & MF_POPUP)
834 lpitem->rect.right += arrow_bitmap_width;
837 if (lpitem->fType & MF_OWNERDRAW)
838 return;
840 if (IS_BITMAP_ITEM(lpitem->fType))
842 BITMAP bm;
843 HBITMAP resBmp = 0;
845 /* Check if there is a magic menu item associated with this item */
846 if (IS_MAGIC_ITEM(lpitem->text))
848 resBmp = MENU_LoadMagicItem((int)lpitem->text, (lpitem->fType & MF_HILITE),
849 lpitem->dwItemData);
851 else
852 resBmp = (HBITMAP)lpitem->text;
854 if (GetObjectA(resBmp, sizeof(bm), &bm ))
856 lpitem->rect.right += bm.bmWidth;
857 lpitem->rect.bottom += bm.bmHeight;
858 if (TWEAK_WineLook == WIN98_LOOK) {
859 /* Leave space for the sunken border */
860 lpitem->rect.right += 2;
861 lpitem->rect.bottom += 2;
868 /* it must be a text item - unless it's the system menu */
869 if (!(lpitem->fType & MF_SYSMENU) && IS_STRING_ITEM( lpitem->fType ))
870 { SIZE size;
872 GetTextExtentPoint32W(hdc, lpitem->text, strlenW(lpitem->text), &size);
874 lpitem->rect.right += size.cx;
875 if (TWEAK_WineLook == WIN31_LOOK)
876 lpitem->rect.bottom += max( size.cy, GetSystemMetrics(SM_CYMENU) );
877 else
878 lpitem->rect.bottom += max(size.cy, GetSystemMetrics(SM_CYMENU)-1);
879 lpitem->xTab = 0;
881 if (menuBar)
883 lpitem->rect.right += MENU_BAR_ITEMS_SPACE;
885 else if ((p = strchrW( lpitem->text, '\t' )) != NULL)
887 /* Item contains a tab (only meaningful in popup menus) */
888 GetTextExtentPoint32W(hdc, lpitem->text, (int)(p - lpitem->text) , &size);
889 lpitem->xTab = check_bitmap_width + MENU_TAB_SPACE + size.cx;
890 lpitem->rect.right += MENU_TAB_SPACE;
892 else
894 if (strchrW( lpitem->text, '\b' ))
895 lpitem->rect.right += MENU_TAB_SPACE;
896 lpitem->xTab = lpitem->rect.right - check_bitmap_width
897 - arrow_bitmap_width;
900 TRACE("(%d,%d)-(%d,%d)\n", lpitem->rect.left, lpitem->rect.top, lpitem->rect.right, lpitem->rect.bottom);
904 /***********************************************************************
905 * MENU_PopupMenuCalcSize
907 * Calculate the size of a popup menu.
909 static void MENU_PopupMenuCalcSize( LPPOPUPMENU lppop, HWND hwndOwner )
911 MENUITEM *lpitem;
912 HDC hdc;
913 int start, i;
914 int orgX, orgY, maxX, maxTab, maxTabWidth;
916 lppop->Width = lppop->Height = 0;
917 if (lppop->nItems == 0) return;
918 hdc = GetDC( 0 );
920 SelectObject( hdc, hMenuFont);
922 start = 0;
923 maxX = (TWEAK_WineLook == WIN31_LOOK) ? GetSystemMetrics(SM_CXBORDER) : 2+1 ;
925 while (start < lppop->nItems)
927 lpitem = &lppop->items[start];
928 orgX = maxX;
929 orgY = (TWEAK_WineLook == WIN31_LOOK) ? GetSystemMetrics(SM_CYBORDER) : 2;
931 maxTab = maxTabWidth = 0;
933 /* Parse items until column break or end of menu */
934 for (i = start; i < lppop->nItems; i++, lpitem++)
936 if ((i != start) &&
937 (lpitem->fType & (MF_MENUBREAK | MF_MENUBARBREAK))) break;
939 MENU_CalcItemSize( hdc, lpitem, hwndOwner, orgX, orgY, FALSE );
941 if (lpitem->fType & MF_MENUBARBREAK) orgX++;
942 maxX = max( maxX, lpitem->rect.right );
943 orgY = lpitem->rect.bottom;
944 if (IS_STRING_ITEM(lpitem->fType) && lpitem->xTab)
946 maxTab = max( maxTab, lpitem->xTab );
947 maxTabWidth = max(maxTabWidth,lpitem->rect.right-lpitem->xTab);
951 /* Finish the column (set all items to the largest width found) */
952 maxX = max( maxX, maxTab + maxTabWidth );
953 for (lpitem = &lppop->items[start]; start < i; start++, lpitem++)
955 lpitem->rect.right = maxX;
956 if (IS_STRING_ITEM(lpitem->fType) && lpitem->xTab)
957 lpitem->xTab = maxTab;
960 lppop->Height = max( lppop->Height, orgY );
963 lppop->Width = maxX;
965 /* space for 3d border */
966 if(TWEAK_WineLook > WIN31_LOOK)
968 lppop->Height += 2;
969 lppop->Width += 2;
972 ReleaseDC( 0, hdc );
976 /***********************************************************************
977 * MENU_MenuBarCalcSize
979 * FIXME: Word 6 implements its own MDI and its own 'close window' bitmap
980 * height is off by 1 pixel which causes lengthy window relocations when
981 * active document window is maximized/restored.
983 * Calculate the size of the menu bar.
985 static void MENU_MenuBarCalcSize( HDC hdc, LPRECT lprect,
986 LPPOPUPMENU lppop, HWND hwndOwner )
988 MENUITEM *lpitem;
989 int start, i, orgX, orgY, maxY, helpPos;
991 if ((lprect == NULL) || (lppop == NULL)) return;
992 if (lppop->nItems == 0) return;
993 TRACE("left=%d top=%d right=%d bottom=%d\n",
994 lprect->left, lprect->top, lprect->right, lprect->bottom);
995 lppop->Width = lprect->right - lprect->left;
996 lppop->Height = 0;
997 maxY = lprect->top+1;
998 start = 0;
999 helpPos = -1;
1000 while (start < lppop->nItems)
1002 lpitem = &lppop->items[start];
1003 orgX = lprect->left;
1004 orgY = maxY;
1006 /* Parse items until line break or end of menu */
1007 for (i = start; i < lppop->nItems; i++, lpitem++)
1009 if ((helpPos == -1) && (lpitem->fType & MF_RIGHTJUSTIFY)) helpPos = i;
1010 if ((i != start) &&
1011 (lpitem->fType & (MF_MENUBREAK | MF_MENUBARBREAK))) break;
1013 TRACE("calling MENU_CalcItemSize org=(%d, %d)\n",
1014 orgX, orgY );
1015 debug_print_menuitem (" item: ", lpitem, "");
1016 MENU_CalcItemSize( hdc, lpitem, hwndOwner, orgX, orgY, TRUE );
1018 if (lpitem->rect.right > lprect->right)
1020 if (i != start) break;
1021 else lpitem->rect.right = lprect->right;
1023 maxY = max( maxY, lpitem->rect.bottom );
1024 orgX = lpitem->rect.right;
1027 /* Finish the line (set all items to the largest height found) */
1028 while (start < i) lppop->items[start++].rect.bottom = maxY;
1031 lprect->bottom = maxY;
1032 lppop->Height = lprect->bottom - lprect->top;
1034 /* Flush right all items between the MF_RIGHTJUSTIFY and */
1035 /* the last item (if several lines, only move the last line) */
1036 lpitem = &lppop->items[lppop->nItems-1];
1037 orgY = lpitem->rect.top;
1038 orgX = lprect->right;
1039 for (i = lppop->nItems - 1; i >= helpPos; i--, lpitem--) {
1040 if ( (helpPos==-1) || (helpPos>i) )
1041 break; /* done */
1042 if (lpitem->rect.top != orgY) break; /* Other line */
1043 if (lpitem->rect.right >= orgX) break; /* Too far right already */
1044 lpitem->rect.left += orgX - lpitem->rect.right;
1045 lpitem->rect.right = orgX;
1046 orgX = lpitem->rect.left;
1050 /***********************************************************************
1051 * MENU_DrawMenuItem
1053 * Draw a single menu item.
1055 static void MENU_DrawMenuItem( HWND hwnd, HMENU hmenu, HWND hwndOwner, HDC hdc, MENUITEM *lpitem,
1056 UINT height, BOOL menuBar, UINT odaction )
1058 RECT rect;
1060 debug_print_menuitem("MENU_DrawMenuItem: ", lpitem, "");
1062 if (lpitem->fType & MF_SYSMENU)
1064 if( !IsIconic(hwnd) ) {
1065 if (TWEAK_WineLook > WIN31_LOOK)
1066 NC_DrawSysButton95( hwnd, hdc,
1067 lpitem->fState &
1068 (MF_HILITE | MF_MOUSESELECT) );
1069 else
1070 NC_DrawSysButton( hwnd, hdc,
1071 lpitem->fState &
1072 (MF_HILITE | MF_MOUSESELECT) );
1075 return;
1078 if (lpitem->fType & MF_OWNERDRAW)
1081 ** Experimentation under Windows reveals that an owner-drawn
1082 ** menu is given the rectangle which includes the space it requested
1083 ** in its response to WM_MEASUREITEM _plus_ width for a checkmark
1084 ** and a popup-menu arrow. This is the value of lpitem->rect.
1085 ** Windows will leave all drawing to the application except for
1086 ** the popup-menu arrow. Windows always draws that itself, after
1087 ** the menu owner has finished drawing.
1089 DRAWITEMSTRUCT dis;
1091 dis.CtlType = ODT_MENU;
1092 dis.CtlID = 0;
1093 dis.itemID = lpitem->wID;
1094 dis.itemData = (DWORD)lpitem->dwItemData;
1095 dis.itemState = 0;
1096 if (lpitem->fState & MF_CHECKED) dis.itemState |= ODS_CHECKED;
1097 if (lpitem->fState & MF_GRAYED) dis.itemState |= ODS_GRAYED;
1098 if (lpitem->fState & MF_HILITE) dis.itemState |= ODS_SELECTED;
1099 dis.itemAction = odaction; /* ODA_DRAWENTIRE | ODA_SELECT | ODA_FOCUS; */
1100 dis.hwndItem = hmenu;
1101 dis.hDC = hdc;
1102 dis.rcItem = lpitem->rect;
1103 TRACE("Ownerdraw: owner=%04x itemID=%d, itemState=%d, itemAction=%d, "
1104 "hwndItem=%04x, hdc=%04x, rcItem={%d,%d,%d,%d}\n", hwndOwner,
1105 dis.itemID, dis.itemState, dis.itemAction, dis.hwndItem,
1106 dis.hDC, dis.rcItem.left, dis.rcItem.top, dis.rcItem.right,
1107 dis.rcItem.bottom);
1108 SendMessageA( hwndOwner, WM_DRAWITEM, 0, (LPARAM)&dis );
1109 /* Fall through to draw popup-menu arrow */
1112 TRACE("rect={%d,%d,%d,%d}\n", lpitem->rect.left, lpitem->rect.top,
1113 lpitem->rect.right,lpitem->rect.bottom);
1115 if (menuBar && (lpitem->fType & MF_SEPARATOR)) return;
1117 rect = lpitem->rect;
1119 if (!(lpitem->fType & MF_OWNERDRAW))
1121 if (lpitem->fState & MF_HILITE)
1123 if(TWEAK_WineLook == WIN98_LOOK)
1125 if(menuBar)
1126 DrawEdge(hdc, &rect, BDR_SUNKENOUTER, BF_RECT);
1127 else
1128 FillRect(hdc, &rect, GetSysColorBrush(COLOR_HIGHLIGHT));
1130 else /* Not Win98 Look */
1132 if(!IS_BITMAP_ITEM(lpitem->fType))
1133 FillRect(hdc, &rect, GetSysColorBrush(COLOR_HIGHLIGHT));
1136 else
1137 FillRect( hdc, &rect, GetSysColorBrush(COLOR_MENU) );
1140 SetBkMode( hdc, TRANSPARENT );
1142 if (!(lpitem->fType & MF_OWNERDRAW))
1144 /* vertical separator */
1145 if (!menuBar && (lpitem->fType & MF_MENUBARBREAK))
1147 if (TWEAK_WineLook > WIN31_LOOK)
1149 RECT rc = rect;
1150 rc.top = 3;
1151 rc.bottom = height - 3;
1152 DrawEdge (hdc, &rc, EDGE_ETCHED, BF_LEFT);
1154 else
1156 SelectObject( hdc, GetSysColorPen(COLOR_WINDOWFRAME) );
1157 MoveToEx( hdc, rect.left, 0, NULL );
1158 LineTo( hdc, rect.left, height );
1162 /* horizontal separator */
1163 if (lpitem->fType & MF_SEPARATOR)
1165 if (TWEAK_WineLook > WIN31_LOOK)
1167 RECT rc = rect;
1168 rc.left++;
1169 rc.right--;
1170 rc.top += SEPARATOR_HEIGHT / 2;
1171 DrawEdge (hdc, &rc, EDGE_ETCHED, BF_TOP);
1173 else
1175 SelectObject( hdc, GetSysColorPen(COLOR_WINDOWFRAME) );
1176 MoveToEx( hdc, rect.left, rect.top + SEPARATOR_HEIGHT/2, NULL );
1177 LineTo( hdc, rect.right, rect.top + SEPARATOR_HEIGHT/2 );
1179 return;
1183 /* Setup colors */
1185 if (lpitem->fState & MF_HILITE)
1187 if(TWEAK_WineLook == WIN98_LOOK)
1189 if(menuBar) {
1190 SetTextColor(hdc, GetSysColor(COLOR_MENUTEXT));
1191 SetBkColor(hdc, GetSysColor(COLOR_MENU));
1192 } else {
1193 if(lpitem->fState & MF_GRAYED)
1194 SetTextColor(hdc, GetSysColor(COLOR_GRAYTEXT));
1195 else
1196 SetTextColor(hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
1197 SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT));
1200 else /* Not Win98 Look */
1202 SetTextColor(hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
1203 if(!IS_BITMAP_ITEM(lpitem->fType))
1204 SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT));
1207 else
1209 if (lpitem->fState & MF_GRAYED)
1210 SetTextColor( hdc, GetSysColor( COLOR_GRAYTEXT ) );
1211 else
1212 SetTextColor( hdc, GetSysColor( COLOR_MENUTEXT ) );
1213 SetBkColor( hdc, GetSysColor( COLOR_MENU ) );
1216 /* helper lines for debugging */
1217 /* FrameRect(hdc, &rect, GetStockObject(BLACK_BRUSH));
1218 SelectObject( hdc, GetSysColorPen(COLOR_WINDOWFRAME) );
1219 MoveToEx( hdc, rect.left, (rect.top + rect.bottom)/2, NULL );
1220 LineTo( hdc, rect.right, (rect.top + rect.bottom)/2 );
1223 if (!menuBar)
1225 INT y = rect.top + rect.bottom;
1226 UINT check_bitmap_width = GetSystemMetrics( SM_CXMENUCHECK );
1227 UINT check_bitmap_height = GetSystemMetrics( SM_CYMENUCHECK );
1229 if (!(lpitem->fType & MF_OWNERDRAW))
1231 /* Draw the check mark
1233 * FIXME:
1234 * Custom checkmark bitmaps are monochrome but not always 1bpp.
1236 HBITMAP bm = (lpitem->fState & MF_CHECKED) ? lpitem->hCheckBit : lpitem->hUnCheckBit;
1237 if (bm) /* we have a custom bitmap */
1239 HDC hdcMem = CreateCompatibleDC( hdc );
1240 SelectObject( hdcMem, bm );
1241 BitBlt( hdc, rect.left, (y - check_bitmap_height) / 2,
1242 check_bitmap_width, check_bitmap_height,
1243 hdcMem, 0, 0, SRCCOPY );
1244 DeleteDC( hdcMem );
1246 else if (lpitem->fState & MF_CHECKED) /* standard bitmaps */
1248 RECT r;
1249 HBITMAP bm = CreateBitmap( check_bitmap_width, check_bitmap_height, 1, 1, NULL );
1250 HDC hdcMem = CreateCompatibleDC( hdc );
1251 SelectObject( hdcMem, bm );
1252 SetRect( &r, 0, 0, check_bitmap_width, check_bitmap_height );
1253 DrawFrameControl( hdcMem, &r, DFC_MENU,
1254 (lpitem->fType & MFT_RADIOCHECK) ?
1255 DFCS_MENUBULLET : DFCS_MENUCHECK );
1256 BitBlt( hdc, rect.left, (y - r.bottom) / 2, r.right, r.bottom,
1257 hdcMem, 0, 0, SRCCOPY );
1258 DeleteDC( hdcMem );
1259 DeleteObject( bm );
1263 /* Draw the popup-menu arrow */
1264 if (lpitem->fType & MF_POPUP)
1266 HDC hdcMem = CreateCompatibleDC( hdc );
1267 HBITMAP hOrigBitmap;
1269 hOrigBitmap = SelectObject( hdcMem, hStdMnArrow );
1270 BitBlt( hdc, rect.right - arrow_bitmap_width - 1,
1271 (y - arrow_bitmap_height) / 2,
1272 arrow_bitmap_width, arrow_bitmap_height,
1273 hdcMem, 0, 0, SRCCOPY );
1274 SelectObject( hdcMem, hOrigBitmap );
1275 DeleteDC( hdcMem );
1278 rect.left += check_bitmap_width;
1279 rect.right -= arrow_bitmap_width;
1282 /* Done for owner-drawn */
1283 if (lpitem->fType & MF_OWNERDRAW)
1284 return;
1286 /* Draw the item text or bitmap */
1287 if (IS_BITMAP_ITEM(lpitem->fType))
1289 int left,top,w,h;
1290 DWORD rop;
1292 HBITMAP resBmp = 0;
1294 HDC hdcMem = CreateCompatibleDC( hdc );
1297 * Check if there is a magic menu item associated with this item
1298 * and load the appropriate bitmap
1300 if (IS_MAGIC_ITEM(lpitem->text))
1302 resBmp = MENU_LoadMagicItem((int)lpitem->text, (lpitem->fState & MF_HILITE),
1303 lpitem->dwItemData);
1305 else
1306 resBmp = (HBITMAP)lpitem->text;
1308 if (resBmp)
1310 BITMAP bm;
1311 GetObjectA( resBmp, sizeof(bm), &bm );
1313 SelectObject(hdcMem,resBmp );
1315 /* handle fontsize > bitmap_height */
1316 h=rect.bottom - rect.top;
1317 top = (h>bm.bmHeight) ?
1318 rect.top+(h-bm.bmHeight)/2 : rect.top;
1319 w=rect.right - rect.left;
1320 left=rect.left;
1321 if (TWEAK_WineLook == WIN95_LOOK) {
1322 rop=((lpitem->fState & MF_HILITE) && !IS_MAGIC_ITEM(lpitem->text)) ? NOTSRCCOPY : SRCCOPY;
1323 if ((lpitem->fState & MF_HILITE) && IS_BITMAP_ITEM(lpitem->fType))
1324 SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT));
1325 } else {
1326 left++;
1327 w-=2;
1328 rop=((lpitem->fState & MF_HILITE) && !IS_MAGIC_ITEM(lpitem->text) && (!menuBar)) ? MERGEPAINT : SRCCOPY;
1330 BitBlt( hdc, left, top, w,
1331 h, hdcMem, 0, 0,
1332 rop);
1334 DeleteDC( hdcMem );
1336 return;
1339 /* No bitmap - process text if present */
1340 else if (IS_STRING_ITEM(lpitem->fType))
1342 register int i;
1343 HFONT hfontOld = 0;
1345 UINT uFormat = (menuBar) ?
1346 DT_CENTER | DT_VCENTER | DT_SINGLELINE :
1347 DT_LEFT | DT_VCENTER | DT_SINGLELINE;
1349 if ( lpitem->fState & MFS_DEFAULT )
1351 hfontOld = SelectObject( hdc, hMenuFontBold);
1354 if (menuBar)
1356 rect.left += MENU_BAR_ITEMS_SPACE / 2;
1357 rect.right -= MENU_BAR_ITEMS_SPACE / 2;
1358 i = strlenW( lpitem->text );
1360 else
1362 for (i = 0; lpitem->text[i]; i++)
1363 if ((lpitem->text[i] == '\t') || (lpitem->text[i] == '\b'))
1364 break;
1367 if( !(TWEAK_WineLook == WIN31_LOOK) && (lpitem->fState & MF_GRAYED))
1369 if (!(lpitem->fState & MF_HILITE) )
1371 ++rect.left; ++rect.top; ++rect.right; ++rect.bottom;
1372 SetTextColor(hdc, RGB(0xff, 0xff, 0xff));
1373 DrawTextW( hdc, lpitem->text, i, &rect, uFormat );
1374 --rect.left; --rect.top; --rect.right; --rect.bottom;
1376 SetTextColor(hdc, RGB(0x80, 0x80, 0x80));
1379 DrawTextW( hdc, lpitem->text, i, &rect, uFormat);
1381 /* paint the shortcut text */
1382 if (lpitem->text[i]) /* There's a tab or flush-right char */
1384 if (lpitem->text[i] == '\t')
1386 rect.left = lpitem->xTab;
1387 uFormat = DT_LEFT | DT_VCENTER | DT_SINGLELINE;
1389 else
1391 uFormat = DT_RIGHT | DT_VCENTER | DT_SINGLELINE;
1394 if( !(TWEAK_WineLook == WIN31_LOOK) && (lpitem->fState & MF_GRAYED))
1396 if (!(lpitem->fState & MF_HILITE) )
1398 ++rect.left; ++rect.top; ++rect.right; ++rect.bottom;
1399 SetTextColor(hdc, RGB(0xff, 0xff, 0xff));
1400 DrawTextW( hdc, lpitem->text + i + 1, -1, &rect, uFormat );
1401 --rect.left; --rect.top; --rect.right; --rect.bottom;
1403 SetTextColor(hdc, RGB(0x80, 0x80, 0x80));
1405 DrawTextW( hdc, lpitem->text + i + 1, -1, &rect, uFormat );
1408 if (hfontOld)
1409 SelectObject (hdc, hfontOld);
1414 /***********************************************************************
1415 * MENU_DrawPopupMenu
1417 * Paint a popup menu.
1419 static void MENU_DrawPopupMenu( HWND hwnd, HDC hdc, HMENU hmenu )
1421 HBRUSH hPrevBrush = 0;
1422 RECT rect;
1424 TRACE("wnd=0x%04x dc=0x%04x menu=0x%04x\n", hwnd, hdc, hmenu);
1426 GetClientRect( hwnd, &rect );
1428 if(TWEAK_WineLook == WIN31_LOOK)
1430 rect.bottom -= POPUP_YSHADE * GetSystemMetrics(SM_CYBORDER);
1431 rect.right -= POPUP_XSHADE * GetSystemMetrics(SM_CXBORDER);
1434 if((hPrevBrush = SelectObject( hdc, GetSysColorBrush(COLOR_MENU) ))
1435 && (SelectObject( hdc, hMenuFont)))
1437 HPEN hPrevPen;
1439 Rectangle( hdc, rect.left, rect.top, rect.right, rect.bottom );
1441 hPrevPen = SelectObject( hdc, GetStockObject( NULL_PEN ) );
1442 if( hPrevPen )
1444 INT ropPrev, i;
1445 POPUPMENU *menu;
1447 /* draw 3-d shade */
1448 if(TWEAK_WineLook == WIN31_LOOK) {
1449 SelectObject( hdc, hShadeBrush );
1450 SetBkMode( hdc, TRANSPARENT );
1451 ropPrev = SetROP2( hdc, R2_MASKPEN );
1453 i = rect.right; /* why SetBrushOrg() doesn't? */
1454 PatBlt( hdc, i & 0xfffffffe,
1455 rect.top + POPUP_YSHADE*GetSystemMetrics(SM_CYBORDER),
1456 i%2 + POPUP_XSHADE*GetSystemMetrics(SM_CXBORDER),
1457 rect.bottom - rect.top, 0x00a000c9 );
1458 i = rect.bottom;
1459 PatBlt( hdc, rect.left + POPUP_XSHADE*GetSystemMetrics(SM_CXBORDER),
1460 i & 0xfffffffe,rect.right - rect.left,
1461 i%2 + POPUP_YSHADE*GetSystemMetrics(SM_CYBORDER), 0x00a000c9 );
1462 SelectObject( hdc, hPrevPen );
1463 SelectObject( hdc, hPrevBrush );
1464 SetROP2( hdc, ropPrev );
1466 else
1467 DrawEdge (hdc, &rect, EDGE_RAISED, BF_RECT);
1469 /* draw menu items */
1471 menu = MENU_GetMenu( hmenu );
1472 if (menu && menu->nItems)
1474 MENUITEM *item;
1475 UINT u;
1477 for (u = menu->nItems, item = menu->items; u > 0; u--, item++)
1478 MENU_DrawMenuItem( hwnd, hmenu, menu->hwndOwner, hdc, item,
1479 menu->Height, FALSE, ODA_DRAWENTIRE );
1482 } else
1484 SelectObject( hdc, hPrevBrush );
1489 /***********************************************************************
1490 * MENU_DrawMenuBar
1492 * Paint a menu bar. Returns the height of the menu bar.
1493 * called from [windows/nonclient.c]
1495 UINT MENU_DrawMenuBar( HDC hDC, LPRECT lprect, HWND hwnd,
1496 BOOL suppress_draw)
1498 LPPOPUPMENU lppop;
1499 UINT i,retvalue;
1500 HFONT hfontOld = 0;
1502 WND *wndPtr = WIN_FindWndPtr( hwnd );
1504 lppop = MENU_GetMenu ((HMENU)wndPtr->wIDmenu );
1505 if (lppop == NULL || lprect == NULL)
1507 retvalue = GetSystemMetrics(SM_CYMENU);
1508 goto END;
1511 TRACE("(%04x, %p, %p)\n", hDC, lprect, lppop);
1513 hfontOld = SelectObject( hDC, hMenuFont);
1515 if (lppop->Height == 0)
1516 MENU_MenuBarCalcSize(hDC, lprect, lppop, hwnd);
1518 lprect->bottom = lprect->top + lppop->Height;
1520 if (suppress_draw)
1522 retvalue = lppop->Height;
1523 goto END;
1526 FillRect(hDC, lprect, GetSysColorBrush(COLOR_MENU) );
1528 if (TWEAK_WineLook == WIN31_LOOK)
1530 SelectObject( hDC, GetSysColorPen(COLOR_WINDOWFRAME) );
1531 MoveToEx( hDC, lprect->left, lprect->bottom, NULL );
1532 LineTo( hDC, lprect->right, lprect->bottom );
1534 else
1536 SelectObject( hDC, GetSysColorPen(COLOR_3DFACE));
1537 MoveToEx( hDC, lprect->left, lprect->bottom, NULL );
1538 LineTo( hDC, lprect->right, lprect->bottom );
1541 if (lppop->nItems == 0)
1543 retvalue = GetSystemMetrics(SM_CYMENU);
1544 goto END;
1547 for (i = 0; i < lppop->nItems; i++)
1549 MENU_DrawMenuItem( hwnd, (HMENU)wndPtr->wIDmenu, hwnd,
1550 hDC, &lppop->items[i], lppop->Height, TRUE, ODA_DRAWENTIRE );
1552 retvalue = lppop->Height;
1554 END:
1555 if (hfontOld)
1556 SelectObject (hDC, hfontOld);
1558 WIN_ReleaseWndPtr(wndPtr);
1559 return retvalue;
1562 /***********************************************************************
1563 * MENU_PatchResidentPopup
1565 BOOL MENU_PatchResidentPopup( HQUEUE16 checkQueue, WND* checkWnd )
1567 WND *pTPWnd = MENU_GetTopPopupWnd();
1569 if( pTPWnd )
1571 HTASK16 hTask = 0;
1573 TRACE("patching resident popup: %04x %04x [%04x %04x]\n",
1574 checkQueue, checkWnd ? checkWnd->hwndSelf : 0, pTPWnd->hmemTaskQ,
1575 pTPWnd->owner ? pTPWnd->owner->hwndSelf : 0);
1577 switch( checkQueue )
1579 case 0: /* checkWnd is the new popup owner */
1580 if( checkWnd )
1582 pTPWnd->owner = checkWnd;
1583 if( pTPWnd->hmemTaskQ != checkWnd->hmemTaskQ )
1584 hTask = QUEUE_GetQueueTask( checkWnd->hmemTaskQ );
1586 break;
1588 case 0xFFFF: /* checkWnd is destroyed */
1589 if( pTPWnd->owner == checkWnd )
1590 pTPWnd->owner = NULL;
1591 MENU_ReleaseTopPopupWnd();
1592 return TRUE;
1594 default: /* checkQueue is exiting */
1595 if( pTPWnd->hmemTaskQ == checkQueue )
1597 hTask = QUEUE_GetQueueTask( pTPWnd->hmemTaskQ );
1598 hTask = TASK_GetNextTask( hTask );
1600 break;
1603 if( hTask )
1605 TDB* task = TASK_GetPtr( hTask );
1606 if( task )
1608 pTPWnd->hInstance = task->hInstance;
1609 pTPWnd->hmemTaskQ = task->hQueue;
1610 MENU_ReleaseTopPopupWnd();
1611 return TRUE;
1613 else WARN("failed to patch resident popup.\n");
1616 MENU_ReleaseTopPopupWnd();
1617 return FALSE;
1620 /***********************************************************************
1621 * MENU_ShowPopup
1623 * Display a popup menu.
1625 static BOOL MENU_ShowPopup( HWND hwndOwner, HMENU hmenu, UINT id,
1626 INT x, INT y, INT xanchor, INT yanchor )
1628 POPUPMENU *menu;
1629 WND *wndOwner = NULL;
1631 TRACE("owner=0x%04x hmenu=0x%04x id=0x%04x x=0x%04x y=0x%04x xa=0x%04x ya=0x%04x\n",
1632 hwndOwner, hmenu, id, x, y, xanchor, yanchor);
1634 if (!(menu = MENU_GetMenu( hmenu ))) return FALSE;
1635 if (menu->FocusedItem != NO_SELECTED_ITEM)
1637 menu->items[menu->FocusedItem].fState &= ~(MF_HILITE|MF_MOUSESELECT);
1638 menu->FocusedItem = NO_SELECTED_ITEM;
1641 /* store the owner for DrawItem */
1642 menu->hwndOwner = hwndOwner;
1644 if( (wndOwner = WIN_FindWndPtr( hwndOwner )) )
1646 UINT width, height;
1648 MENU_PopupMenuCalcSize( menu, hwndOwner );
1650 /* adjust popup menu pos so that it fits within the desktop */
1652 width = menu->Width + GetSystemMetrics(SM_CXBORDER);
1653 height = menu->Height + GetSystemMetrics(SM_CYBORDER);
1655 if( x + width > GetSystemMetrics(SM_CXSCREEN ))
1657 if( xanchor )
1658 x -= width - xanchor;
1659 if( x + width > GetSystemMetrics(SM_CXSCREEN))
1660 x = GetSystemMetrics(SM_CXSCREEN) - width;
1662 if( x < 0 ) x = 0;
1664 if( y + height > GetSystemMetrics(SM_CYSCREEN ))
1666 if( yanchor )
1667 y -= height + yanchor;
1668 if( y + height > GetSystemMetrics(SM_CYSCREEN ))
1669 y = GetSystemMetrics(SM_CYSCREEN) - height;
1671 if( y < 0 ) y = 0;
1673 if( TWEAK_WineLook == WIN31_LOOK )
1675 width += POPUP_XSHADE * GetSystemMetrics(SM_CXBORDER); /* add space for shading */
1676 height += POPUP_YSHADE * GetSystemMetrics(SM_CYBORDER);
1679 /* NOTE: In Windows, top menu popup is not owned. */
1680 if (!pTopPopupWnd) /* create top level popup menu window */
1682 assert( uSubPWndLevel == 0 );
1684 pTopPopupWnd = WIN_FindWndPtr(CreateWindowA( POPUPMENU_CLASS_ATOM, NULL,
1685 WS_POPUP, x, y, width, height,
1686 hwndOwner, 0, wndOwner->hInstance,
1687 (LPVOID)hmenu ));
1688 if (!pTopPopupWnd)
1690 WIN_ReleaseWndPtr(wndOwner);
1691 return FALSE;
1693 menu->hWnd = pTopPopupWnd->hwndSelf;
1694 MENU_ReleaseTopPopupWnd();
1696 else
1697 if( uSubPWndLevel )
1699 /* create a new window for the submenu */
1701 menu->hWnd = CreateWindowA( POPUPMENU_CLASS_ATOM, NULL,
1702 WS_POPUP, x, y, width, height,
1703 hwndOwner, 0, wndOwner->hInstance,
1704 (LPVOID)hmenu );
1705 if( !menu->hWnd )
1707 WIN_ReleaseWndPtr(wndOwner);
1708 return FALSE;
1711 else /* top level popup menu window already exists */
1713 WND *pTPWnd = MENU_GetTopPopupWnd();
1714 menu->hWnd = pTPWnd->hwndSelf;
1716 MENU_PatchResidentPopup( 0, wndOwner );
1717 SendMessageA( pTPWnd->hwndSelf, MM_SETMENUHANDLE, (WPARAM16)hmenu, 0L);
1719 /* adjust its size */
1721 SetWindowPos( menu->hWnd, 0, x, y, width, height,
1722 SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOREDRAW);
1723 MENU_ReleaseTopPopupWnd();
1726 uSubPWndLevel++; /* menu level counter */
1728 /* Display the window */
1730 SetWindowPos( menu->hWnd, HWND_TOP, 0, 0, 0, 0,
1731 SWP_SHOWWINDOW | SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE );
1732 UpdateWindow( menu->hWnd );
1733 WIN_ReleaseWndPtr(wndOwner);
1734 return TRUE;
1736 return FALSE;
1740 /***********************************************************************
1741 * MENU_SelectItem
1743 static void MENU_SelectItem( HWND hwndOwner, HMENU hmenu, UINT wIndex,
1744 BOOL sendMenuSelect, HMENU topmenu )
1746 LPPOPUPMENU lppop;
1747 HDC hdc;
1749 TRACE("owner=0x%04x menu=0x%04x index=0x%04x select=0x%04x\n", hwndOwner, hmenu, wIndex, sendMenuSelect);
1751 lppop = MENU_GetMenu( hmenu );
1752 if ((!lppop) || (!lppop->nItems)) return;
1754 if (lppop->FocusedItem == wIndex) return;
1755 if (lppop->wFlags & MF_POPUP) hdc = GetDC( lppop->hWnd );
1756 else hdc = GetDCEx( lppop->hWnd, 0, DCX_CACHE | DCX_WINDOW);
1758 SelectObject( hdc, hMenuFont);
1760 /* Clear previous highlighted item */
1761 if (lppop->FocusedItem != NO_SELECTED_ITEM)
1763 lppop->items[lppop->FocusedItem].fState &= ~(MF_HILITE|MF_MOUSESELECT);
1764 MENU_DrawMenuItem(lppop->hWnd, hmenu, hwndOwner, hdc,&lppop->items[lppop->FocusedItem],
1765 lppop->Height, !(lppop->wFlags & MF_POPUP),
1766 ODA_SELECT );
1769 /* Highlight new item (if any) */
1770 lppop->FocusedItem = wIndex;
1771 if (lppop->FocusedItem != NO_SELECTED_ITEM)
1773 if(!(lppop->items[wIndex].fType & MF_SEPARATOR)) {
1774 lppop->items[wIndex].fState |= MF_HILITE;
1775 MENU_DrawMenuItem( lppop->hWnd, hmenu, hwndOwner, hdc,
1776 &lppop->items[wIndex], lppop->Height,
1777 !(lppop->wFlags & MF_POPUP), ODA_SELECT );
1779 if (sendMenuSelect)
1781 MENUITEM *ip = &lppop->items[lppop->FocusedItem];
1782 SendMessageA( hwndOwner, WM_MENUSELECT,
1783 MAKELONG(ip->fType & MF_POPUP ? wIndex: ip->wID,
1784 ip->fType | ip->fState | MF_MOUSESELECT |
1785 (lppop->wFlags & MF_SYSMENU)), hmenu);
1788 else if (sendMenuSelect) {
1789 if(topmenu){
1790 int pos;
1791 if((pos=MENU_FindSubMenu(&topmenu, hmenu))!=NO_SELECTED_ITEM){
1792 POPUPMENU *ptm = (POPUPMENU *) USER_HEAP_LIN_ADDR( topmenu );
1793 MENUITEM *ip = &ptm->items[pos];
1794 SendMessageA( hwndOwner, WM_MENUSELECT, MAKELONG(pos,
1795 ip->fType | ip->fState | MF_MOUSESELECT |
1796 (ptm->wFlags & MF_SYSMENU)), topmenu);
1800 ReleaseDC( lppop->hWnd, hdc );
1804 /***********************************************************************
1805 * MENU_MoveSelection
1807 * Moves currently selected item according to the offset parameter.
1808 * If there is no selection then it should select the last item if
1809 * offset is ITEM_PREV or the first item if offset is ITEM_NEXT.
1811 static void MENU_MoveSelection( HWND hwndOwner, HMENU hmenu, INT offset )
1813 INT i;
1814 POPUPMENU *menu;
1816 TRACE("hwnd=0x%04x hmenu=0x%04x off=0x%04x\n", hwndOwner, hmenu, offset);
1818 menu = MENU_GetMenu( hmenu );
1819 if ((!menu) || (!menu->items)) return;
1821 if ( menu->FocusedItem != NO_SELECTED_ITEM )
1823 if( menu->nItems == 1 ) return; else
1824 for (i = menu->FocusedItem + offset ; i >= 0 && i < menu->nItems
1825 ; i += offset)
1826 if (!(menu->items[i].fType & MF_SEPARATOR))
1828 MENU_SelectItem( hwndOwner, hmenu, i, TRUE, 0 );
1829 return;
1833 for ( i = (offset > 0) ? 0 : menu->nItems - 1;
1834 i >= 0 && i < menu->nItems ; i += offset)
1835 if (!(menu->items[i].fType & MF_SEPARATOR))
1837 MENU_SelectItem( hwndOwner, hmenu, i, TRUE, 0 );
1838 return;
1843 /**********************************************************************
1844 * MENU_SetItemData
1846 * Set an item flags, id and text ptr. Called by InsertMenu() and
1847 * ModifyMenu().
1849 static BOOL MENU_SetItemData( MENUITEM *item, UINT flags, UINT id,
1850 LPCWSTR str )
1852 LPWSTR prevText = IS_STRING_ITEM(item->fType) ? item->text : NULL;
1854 debug_print_menuitem("MENU_SetItemData from: ", item, "");
1855 TRACE("flags=%x str=%p\n", flags, str);
1857 if (IS_STRING_ITEM(flags))
1859 if (!str)
1861 flags |= MF_SEPARATOR;
1862 item->text = NULL;
1864 else
1866 LPWSTR text;
1867 /* Item beginning with a backspace is a help item */
1868 if (*str == '\b')
1870 flags |= MF_HELP;
1871 str++;
1873 if (!(text = HEAP_strdupW( GetProcessHeap(), 0, str ))) return FALSE;
1874 item->text = text;
1877 else if (IS_BITMAP_ITEM(flags))
1878 item->text = (LPWSTR)(HBITMAP)LOWORD(str);
1879 else item->text = NULL;
1881 if (flags & MF_OWNERDRAW)
1882 item->dwItemData = (DWORD)str;
1883 else
1884 item->dwItemData = 0;
1886 if ((item->fType & MF_POPUP) && (flags & MF_POPUP) && (item->hSubMenu != id) )
1887 DestroyMenu( item->hSubMenu ); /* ModifyMenu() spec */
1889 if (flags & MF_POPUP)
1891 POPUPMENU *menu = MENU_GetMenu((UINT16)id);
1892 if (menu) menu->wFlags |= MF_POPUP;
1893 else
1895 item->wID = 0;
1896 item->hSubMenu = 0;
1897 item->fType = 0;
1898 item->fState = 0;
1899 return FALSE;
1903 item->wID = id;
1904 if (flags & MF_POPUP)
1905 item->hSubMenu = id;
1907 if ((item->fType & MF_POPUP) && !(flags & MF_POPUP) )
1908 flags |= MF_POPUP; /* keep popup */
1910 item->fType = flags & TYPE_MASK;
1911 item->fState = (flags & STATE_MASK) &
1912 ~(MF_HILITE | MF_MOUSESELECT | MF_BYPOSITION);
1915 /* Don't call SetRectEmpty here! */
1918 if (prevText) HeapFree( GetProcessHeap(), 0, prevText );
1920 debug_print_menuitem("MENU_SetItemData to : ", item, "");
1921 return TRUE;
1925 /**********************************************************************
1926 * MENU_InsertItem
1928 * Insert a new item into a menu.
1930 static MENUITEM *MENU_InsertItem( HMENU hMenu, UINT pos, UINT flags )
1932 MENUITEM *newItems;
1933 POPUPMENU *menu;
1935 if (!(menu = MENU_GetMenu(hMenu)))
1936 return NULL;
1938 /* Find where to insert new item */
1940 if (flags & MF_BYPOSITION) {
1941 if (pos > menu->nItems)
1942 pos = menu->nItems;
1943 } else {
1944 if (!MENU_FindItem( &hMenu, &pos, flags ))
1945 pos = menu->nItems;
1946 else {
1947 if (!(menu = MENU_GetMenu( hMenu )))
1948 return NULL;
1952 /* Create new items array */
1954 newItems = HeapAlloc( GetProcessHeap(), 0, sizeof(MENUITEM) * (menu->nItems+1) );
1955 if (!newItems)
1957 WARN("allocation failed\n" );
1958 return NULL;
1960 if (menu->nItems > 0)
1962 /* Copy the old array into the new one */
1963 if (pos > 0) memcpy( newItems, menu->items, pos * sizeof(MENUITEM) );
1964 if (pos < menu->nItems) memcpy( &newItems[pos+1], &menu->items[pos],
1965 (menu->nItems-pos)*sizeof(MENUITEM) );
1966 HeapFree( GetProcessHeap(), 0, menu->items );
1968 menu->items = newItems;
1969 menu->nItems++;
1970 memset( &newItems[pos], 0, sizeof(*newItems) );
1971 menu->Height = 0; /* force size recalculate */
1972 return &newItems[pos];
1976 /**********************************************************************
1977 * MENU_ParseResource
1979 * Parse a standard menu resource and add items to the menu.
1980 * Return a pointer to the end of the resource.
1982 static LPCSTR MENU_ParseResource( LPCSTR res, HMENU hMenu, BOOL unicode )
1984 WORD flags, id = 0;
1985 LPCSTR str;
1989 flags = GET_WORD(res);
1990 res += sizeof(WORD);
1991 if (!(flags & MF_POPUP))
1993 id = GET_WORD(res);
1994 res += sizeof(WORD);
1996 if (!IS_STRING_ITEM(flags))
1997 ERR("not a string item %04x\n", flags );
1998 str = res;
1999 if (!unicode) res += strlen(str) + 1;
2000 else res += (strlenW((LPCWSTR)str) + 1) * sizeof(WCHAR);
2001 if (flags & MF_POPUP)
2003 HMENU hSubMenu = CreatePopupMenu();
2004 if (!hSubMenu) return NULL;
2005 if (!(res = MENU_ParseResource( res, hSubMenu, unicode )))
2006 return NULL;
2007 if (!unicode) AppendMenuA( hMenu, flags, (UINT)hSubMenu, str );
2008 else AppendMenuW( hMenu, flags, (UINT)hSubMenu, (LPCWSTR)str );
2010 else /* Not a popup */
2012 if (!unicode) AppendMenuA( hMenu, flags, id, *str ? str : NULL );
2013 else AppendMenuW( hMenu, flags, id,
2014 *(LPCWSTR)str ? (LPCWSTR)str : NULL );
2016 } while (!(flags & MF_END));
2017 return res;
2021 /**********************************************************************
2022 * MENUEX_ParseResource
2024 * Parse an extended menu resource and add items to the menu.
2025 * Return a pointer to the end of the resource.
2027 static LPCSTR MENUEX_ParseResource( LPCSTR res, HMENU hMenu)
2029 WORD resinfo;
2030 do {
2031 MENUITEMINFOW mii;
2033 mii.cbSize = sizeof(mii);
2034 mii.fMask = MIIM_STATE | MIIM_ID | MIIM_TYPE;
2035 mii.fType = GET_DWORD(res);
2036 res += sizeof(DWORD);
2037 mii.fState = GET_DWORD(res);
2038 res += sizeof(DWORD);
2039 mii.wID = GET_DWORD(res);
2040 res += sizeof(DWORD);
2041 resinfo = GET_WORD(res); /* FIXME: for 16-bit apps this is a byte. */
2042 res += sizeof(WORD);
2043 /* Align the text on a word boundary. */
2044 res += (~((int)res - 1)) & 1;
2045 mii.dwTypeData = (LPWSTR) res;
2046 res += (1 + strlenW(mii.dwTypeData)) * sizeof(WCHAR);
2047 /* Align the following fields on a dword boundary. */
2048 res += (~((int)res - 1)) & 3;
2050 TRACE("Menu item: [%08x,%08x,%04x,%04x,%s]\n",
2051 mii.fType, mii.fState, mii.wID, resinfo, debugstr_w(mii.dwTypeData));
2053 if (resinfo & 1) { /* Pop-up? */
2054 /* DWORD helpid = GET_DWORD(res); FIXME: use this. */
2055 res += sizeof(DWORD);
2056 mii.hSubMenu = CreatePopupMenu();
2057 if (!mii.hSubMenu)
2058 return NULL;
2059 if (!(res = MENUEX_ParseResource(res, mii.hSubMenu))) {
2060 DestroyMenu(mii.hSubMenu);
2061 return NULL;
2063 mii.fMask |= MIIM_SUBMENU;
2064 mii.fType |= MF_POPUP;
2066 InsertMenuItemW(hMenu, -1, MF_BYPOSITION, &mii);
2067 } while (!(resinfo & MF_END));
2068 return res;
2072 /***********************************************************************
2073 * MENU_GetSubPopup
2075 * Return the handle of the selected sub-popup menu (if any).
2077 static HMENU MENU_GetSubPopup( HMENU hmenu )
2079 POPUPMENU *menu;
2080 MENUITEM *item;
2082 menu = MENU_GetMenu( hmenu );
2084 if ((!menu) || (menu->FocusedItem == NO_SELECTED_ITEM)) return 0;
2086 item = &menu->items[menu->FocusedItem];
2087 if ((item->fType & MF_POPUP) && (item->fState & MF_MOUSESELECT))
2088 return item->hSubMenu;
2089 return 0;
2093 /***********************************************************************
2094 * MENU_HideSubPopups
2096 * Hide the sub-popup menus of this menu.
2098 static void MENU_HideSubPopups( HWND hwndOwner, HMENU hmenu,
2099 BOOL sendMenuSelect )
2101 POPUPMENU *menu = MENU_GetMenu( hmenu );
2103 TRACE("owner=0x%04x hmenu=0x%04x 0x%04x\n", hwndOwner, hmenu, sendMenuSelect);
2105 if (menu && uSubPWndLevel)
2107 HMENU hsubmenu;
2108 POPUPMENU *submenu;
2109 MENUITEM *item;
2111 if (menu->FocusedItem != NO_SELECTED_ITEM)
2113 item = &menu->items[menu->FocusedItem];
2114 if (!(item->fType & MF_POPUP) ||
2115 !(item->fState & MF_MOUSESELECT)) return;
2116 item->fState &= ~MF_MOUSESELECT;
2117 hsubmenu = item->hSubMenu;
2118 } else return;
2120 submenu = MENU_GetMenu( hsubmenu );
2121 MENU_HideSubPopups( hwndOwner, hsubmenu, FALSE );
2122 MENU_SelectItem( hwndOwner, hsubmenu, NO_SELECTED_ITEM, sendMenuSelect, 0 );
2124 if (submenu->hWnd == MENU_GetTopPopupWnd()->hwndSelf )
2126 ShowWindow( submenu->hWnd, SW_HIDE );
2127 uSubPWndLevel = 0;
2129 else
2131 DestroyWindow( submenu->hWnd );
2132 submenu->hWnd = 0;
2134 MENU_ReleaseTopPopupWnd();
2139 /***********************************************************************
2140 * MENU_ShowSubPopup
2142 * Display the sub-menu of the selected item of this menu.
2143 * Return the handle of the submenu, or hmenu if no submenu to display.
2145 static HMENU MENU_ShowSubPopup( HWND hwndOwner, HMENU hmenu,
2146 BOOL selectFirst, UINT wFlags )
2148 RECT rect;
2149 POPUPMENU *menu;
2150 MENUITEM *item;
2151 WND *wndPtr;
2152 HDC hdc;
2154 TRACE("owner=0x%04x hmenu=0x%04x 0x%04x\n", hwndOwner, hmenu, selectFirst);
2156 if (!(menu = MENU_GetMenu( hmenu ))) return hmenu;
2158 if (!(wndPtr = WIN_FindWndPtr( menu->hWnd )) ||
2159 (menu->FocusedItem == NO_SELECTED_ITEM))
2161 WIN_ReleaseWndPtr(wndPtr);
2162 return hmenu;
2165 item = &menu->items[menu->FocusedItem];
2166 if (!(item->fType & MF_POPUP) ||
2167 (item->fState & (MF_GRAYED | MF_DISABLED)))
2169 WIN_ReleaseWndPtr(wndPtr);
2170 return hmenu;
2173 /* message must be sent before using item,
2174 because nearly everything may be changed by the application ! */
2176 /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
2177 if (!(wFlags & TPM_NONOTIFY))
2178 SendMessageA( hwndOwner, WM_INITMENUPOPUP, item->hSubMenu,
2179 MAKELONG( menu->FocusedItem, IS_SYSTEM_MENU(menu) ));
2181 item = &menu->items[menu->FocusedItem];
2182 rect = item->rect;
2184 /* correct item if modified as a reaction to WM_INITMENUPOPUP message */
2185 if (!(item->fState & MF_HILITE))
2187 if (menu->wFlags & MF_POPUP) hdc = GetDC( menu->hWnd );
2188 else hdc = GetDCEx( menu->hWnd, 0, DCX_CACHE | DCX_WINDOW);
2190 SelectObject( hdc, hMenuFont);
2192 item->fState |= MF_HILITE;
2193 MENU_DrawMenuItem( menu->hWnd, hmenu, hwndOwner, hdc, item, menu->Height, !(menu->wFlags & MF_POPUP), ODA_DRAWENTIRE );
2194 ReleaseDC( menu->hWnd, hdc );
2196 if (!item->rect.top && !item->rect.left && !item->rect.bottom && !item->rect.right)
2197 item->rect = rect;
2199 item->fState |= MF_MOUSESELECT;
2201 if (IS_SYSTEM_MENU(menu))
2203 MENU_InitSysMenuPopup(item->hSubMenu, wndPtr->dwStyle, GetClassLongA(wndPtr->hwndSelf, GCL_STYLE));
2205 NC_GetSysPopupPos( wndPtr, &rect );
2206 rect.top = rect.bottom;
2207 rect.right = GetSystemMetrics(SM_CXSIZE);
2208 rect.bottom = GetSystemMetrics(SM_CYSIZE);
2210 else
2212 if (menu->wFlags & MF_POPUP)
2214 rect.left = wndPtr->rectWindow.left + item->rect.right - GetSystemMetrics(SM_CXBORDER);
2215 rect.top = wndPtr->rectWindow.top + item->rect.top;
2216 rect.right = item->rect.left - item->rect.right + GetSystemMetrics(SM_CXBORDER);
2217 rect.bottom = item->rect.top - item->rect.bottom;
2219 else
2221 rect.left = wndPtr->rectWindow.left + item->rect.left;
2222 rect.top = wndPtr->rectWindow.top + item->rect.bottom;
2223 rect.right = item->rect.right - item->rect.left;
2224 rect.bottom = item->rect.bottom - item->rect.top;
2228 MENU_ShowPopup( hwndOwner, item->hSubMenu, menu->FocusedItem,
2229 rect.left, rect.top, rect.right, rect.bottom );
2230 if (selectFirst)
2231 MENU_MoveSelection( hwndOwner, item->hSubMenu, ITEM_NEXT );
2232 WIN_ReleaseWndPtr(wndPtr);
2233 return item->hSubMenu;
2238 /**********************************************************************
2239 * MENU_IsMenuActive
2241 BOOL MENU_IsMenuActive(void)
2243 return pTopPopupWnd && (pTopPopupWnd->dwStyle & WS_VISIBLE);
2246 /***********************************************************************
2247 * MENU_PtMenu
2249 * Walks menu chain trying to find a menu pt maps to.
2251 static HMENU MENU_PtMenu( HMENU hMenu, POINT pt )
2253 POPUPMENU *menu = MENU_GetMenu( hMenu );
2254 register UINT ht = menu->FocusedItem;
2256 /* try subpopup first (if any) */
2257 ht = (ht != NO_SELECTED_ITEM &&
2258 (menu->items[ht].fType & MF_POPUP) &&
2259 (menu->items[ht].fState & MF_MOUSESELECT))
2260 ? (UINT) MENU_PtMenu(menu->items[ht].hSubMenu, pt) : 0;
2262 if( !ht ) /* check the current window (avoiding WM_HITTEST) */
2264 ht = (UINT)NC_HandleNCHitTest( menu->hWnd, pt );
2265 if( menu->wFlags & MF_POPUP )
2266 ht = (ht != (UINT)HTNOWHERE &&
2267 ht != (UINT)HTERROR) ? (UINT)hMenu : 0;
2268 else
2270 WND* wndPtr = WIN_FindWndPtr(menu->hWnd);
2272 ht = ( ht == HTSYSMENU ) ? (UINT)(wndPtr->hSysMenu)
2273 : ( ht == HTMENU ) ? (UINT)(wndPtr->wIDmenu) : 0;
2274 WIN_ReleaseWndPtr(wndPtr);
2277 return (HMENU)ht;
2280 /***********************************************************************
2281 * MENU_ExecFocusedItem
2283 * Execute a menu item (for instance when user pressed Enter).
2284 * Return the wID of the executed item. Otherwise, -1 indicating
2285 * that no menu item was executed;
2286 * Have to receive the flags for the TrackPopupMenu options to avoid
2287 * sending unwanted message.
2290 static INT MENU_ExecFocusedItem( MTRACKER* pmt, HMENU hMenu, UINT wFlags )
2292 MENUITEM *item;
2293 POPUPMENU *menu = MENU_GetMenu( hMenu );
2295 TRACE("%p hmenu=0x%04x\n", pmt, hMenu);
2297 if (!menu || !menu->nItems ||
2298 (menu->FocusedItem == NO_SELECTED_ITEM)) return -1;
2300 item = &menu->items[menu->FocusedItem];
2302 TRACE("%08x %08x %08x\n",
2303 hMenu, item->wID, item->hSubMenu);
2305 if (!(item->fType & MF_POPUP))
2307 if (!(item->fState & (MF_GRAYED | MF_DISABLED)) && !(item->fType & MF_SEPARATOR))
2309 /* If TPM_RETURNCMD is set you return the id, but
2310 do not send a message to the owner */
2311 if(!(wFlags & TPM_RETURNCMD))
2313 if( menu->wFlags & MF_SYSMENU )
2314 PostMessageA( pmt->hOwnerWnd, WM_SYSCOMMAND, item->wID,
2315 MAKELPARAM((INT16)pmt->pt.x, (INT16)pmt->pt.y) );
2316 else
2317 PostMessageA( pmt->hOwnerWnd, WM_COMMAND, item->wID, 0 );
2319 return item->wID;
2322 else
2323 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd, hMenu, TRUE, wFlags);
2325 return -1;
2328 /***********************************************************************
2329 * MENU_SwitchTracking
2331 * Helper function for menu navigation routines.
2333 static void MENU_SwitchTracking( MTRACKER* pmt, HMENU hPtMenu, UINT id )
2335 POPUPMENU *ptmenu = MENU_GetMenu( hPtMenu );
2336 POPUPMENU *topmenu = MENU_GetMenu( pmt->hTopMenu );
2338 TRACE("%p hmenu=0x%04x 0x%04x\n", pmt, hPtMenu, id);
2340 if( pmt->hTopMenu != hPtMenu &&
2341 !((ptmenu->wFlags | topmenu->wFlags) & MF_POPUP) )
2343 /* both are top level menus (system and menu-bar) */
2344 MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE );
2345 MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, NO_SELECTED_ITEM, FALSE, 0 );
2346 pmt->hTopMenu = hPtMenu;
2348 else MENU_HideSubPopups( pmt->hOwnerWnd, hPtMenu, FALSE );
2349 MENU_SelectItem( pmt->hOwnerWnd, hPtMenu, id, TRUE, 0 );
2353 /***********************************************************************
2354 * MENU_ButtonDown
2356 * Return TRUE if we can go on with menu tracking.
2358 static BOOL MENU_ButtonDown( MTRACKER* pmt, HMENU hPtMenu, UINT wFlags )
2360 TRACE("%p hmenu=0x%04x\n", pmt, hPtMenu);
2362 if (hPtMenu)
2364 UINT id = 0;
2365 POPUPMENU *ptmenu = MENU_GetMenu( hPtMenu );
2366 MENUITEM *item;
2368 if( IS_SYSTEM_MENU(ptmenu) )
2369 item = ptmenu->items;
2370 else
2371 item = MENU_FindItemByCoords( ptmenu, pmt->pt, &id );
2373 if( item )
2375 if( ptmenu->FocusedItem != id )
2376 MENU_SwitchTracking( pmt, hPtMenu, id );
2378 /* If the popup menu is not already "popped" */
2379 if(!(item->fState & MF_MOUSESELECT ))
2381 pmt->hCurrentMenu = MENU_ShowSubPopup( pmt->hOwnerWnd, hPtMenu, FALSE, wFlags );
2383 /* In win31, a newly popped menu always remains opened for the next buttonup */
2384 if(TWEAK_WineLook == WIN31_LOOK)
2385 ptmenu->bTimeToHide = FALSE;
2388 return TRUE;
2390 /* Else the click was on the menu bar, finish the tracking */
2392 return FALSE;
2395 /***********************************************************************
2396 * MENU_ButtonUp
2398 * Return the value of MENU_ExecFocusedItem if
2399 * the selected item was not a popup. Else open the popup.
2400 * A -1 return value indicates that we go on with menu tracking.
2403 static INT MENU_ButtonUp( MTRACKER* pmt, HMENU hPtMenu, UINT wFlags)
2405 TRACE("%p hmenu=0x%04x\n", pmt, hPtMenu);
2407 if (hPtMenu)
2409 UINT id = 0;
2410 POPUPMENU *ptmenu = MENU_GetMenu( hPtMenu );
2411 MENUITEM *item;
2413 if( IS_SYSTEM_MENU(ptmenu) )
2414 item = ptmenu->items;
2415 else
2416 item = MENU_FindItemByCoords( ptmenu, pmt->pt, &id );
2418 if( item && (ptmenu->FocusedItem == id ))
2420 if( !(item->fType & MF_POPUP) )
2421 return MENU_ExecFocusedItem( pmt, hPtMenu, wFlags);
2423 /* If we are dealing with the top-level menu */
2424 /* and this is a click on an already "popped" item: */
2425 /* Stop the menu tracking and close the opened submenus */
2426 if((pmt->hTopMenu == hPtMenu) && (ptmenu->bTimeToHide == TRUE))
2427 return 0;
2429 ptmenu->bTimeToHide = TRUE;
2431 return -1;
2435 /***********************************************************************
2436 * MENU_MouseMove
2438 * Return TRUE if we can go on with menu tracking.
2440 static BOOL MENU_MouseMove( MTRACKER* pmt, HMENU hPtMenu, UINT wFlags )
2442 UINT id = NO_SELECTED_ITEM;
2443 POPUPMENU *ptmenu = NULL;
2445 if( hPtMenu )
2447 ptmenu = MENU_GetMenu( hPtMenu );
2448 if( IS_SYSTEM_MENU(ptmenu) )
2449 id = 0;
2450 else
2451 MENU_FindItemByCoords( ptmenu, pmt->pt, &id );
2454 if( id == NO_SELECTED_ITEM )
2456 MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
2457 NO_SELECTED_ITEM, TRUE, pmt->hTopMenu);
2460 else if( ptmenu->FocusedItem != id )
2462 MENU_SwitchTracking( pmt, hPtMenu, id );
2463 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd, hPtMenu, FALSE, wFlags);
2465 return TRUE;
2469 /***********************************************************************
2470 * MENU_DoNextMenu
2472 * NOTE: WM_NEXTMENU documented in Win32 is a bit different.
2474 static LRESULT MENU_DoNextMenu( MTRACKER* pmt, UINT vk )
2476 POPUPMENU *menu = MENU_GetMenu( pmt->hTopMenu );
2478 if( (vk == VK_LEFT && menu->FocusedItem == 0 ) ||
2479 (vk == VK_RIGHT && menu->FocusedItem == menu->nItems - 1))
2481 WND* wndPtr;
2482 HMENU hNewMenu;
2483 HWND hNewWnd;
2484 UINT id = 0;
2485 LRESULT l = SendMessageA( pmt->hOwnerWnd, WM_NEXTMENU, vk,
2486 (IS_SYSTEM_MENU(menu)) ? GetSubMenu16(pmt->hTopMenu,0) : pmt->hTopMenu );
2488 TRACE("%04x [%04x] -> %04x [%04x]\n",
2489 (UINT16)pmt->hCurrentMenu, (UINT16)pmt->hOwnerWnd, LOWORD(l), HIWORD(l) );
2491 if( l == 0 )
2493 wndPtr = WIN_FindWndPtr(pmt->hOwnerWnd);
2495 hNewWnd = pmt->hOwnerWnd;
2496 if( IS_SYSTEM_MENU(menu) )
2498 /* switch to the menu bar */
2500 if( wndPtr->dwStyle & WS_CHILD || !wndPtr->wIDmenu )
2502 WIN_ReleaseWndPtr(wndPtr);
2503 return FALSE;
2506 hNewMenu = wndPtr->wIDmenu;
2507 if( vk == VK_LEFT )
2509 menu = MENU_GetMenu( hNewMenu );
2510 id = menu->nItems - 1;
2513 else if( wndPtr->dwStyle & WS_SYSMENU )
2515 /* switch to the system menu */
2516 hNewMenu = wndPtr->hSysMenu;
2518 else
2520 WIN_ReleaseWndPtr(wndPtr);
2521 return FALSE;
2523 WIN_ReleaseWndPtr(wndPtr);
2525 else /* application returned a new menu to switch to */
2527 hNewMenu = LOWORD(l); hNewWnd = HIWORD(l);
2529 if( IsMenu(hNewMenu) && IsWindow(hNewWnd) )
2531 wndPtr = WIN_FindWndPtr(hNewWnd);
2533 if( wndPtr->dwStyle & WS_SYSMENU &&
2534 GetSubMenu16(wndPtr->hSysMenu, 0) == hNewMenu )
2536 /* get the real system menu */
2537 hNewMenu = wndPtr->hSysMenu;
2539 else if( wndPtr->dwStyle & WS_CHILD || wndPtr->wIDmenu != hNewMenu )
2541 /* FIXME: Not sure what to do here;
2542 * perhaps try to track hNewMenu as a popup? */
2544 TRACE(" -- got confused.\n");
2545 WIN_ReleaseWndPtr(wndPtr);
2546 return FALSE;
2548 WIN_ReleaseWndPtr(wndPtr);
2550 else return FALSE;
2553 if( hNewMenu != pmt->hTopMenu )
2555 MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, NO_SELECTED_ITEM,
2556 FALSE, 0 );
2557 if( pmt->hCurrentMenu != pmt->hTopMenu )
2558 MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE );
2561 if( hNewWnd != pmt->hOwnerWnd )
2563 ReleaseCapture();
2564 pmt->hOwnerWnd = hNewWnd;
2565 EVENT_Capture( pmt->hOwnerWnd, HTMENU );
2568 pmt->hTopMenu = pmt->hCurrentMenu = hNewMenu; /* all subpopups are hidden */
2569 MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, id, TRUE, 0 );
2571 return TRUE;
2573 return FALSE;
2576 /***********************************************************************
2577 * MENU_SuspendPopup
2579 * The idea is not to show the popup if the next input message is
2580 * going to hide it anyway.
2582 static BOOL MENU_SuspendPopup( MTRACKER* pmt, UINT16 uMsg )
2584 MSG msg;
2586 msg.hwnd = pmt->hOwnerWnd;
2588 PeekMessageA( &msg, 0, 0, 0, PM_NOYIELD | PM_REMOVE);
2589 pmt->trackFlags |= TF_SKIPREMOVE;
2591 switch( uMsg )
2593 case WM_KEYDOWN:
2594 PeekMessageA( &msg, 0, 0, 0, PM_NOYIELD | PM_NOREMOVE);
2595 if( msg.message == WM_KEYUP || msg.message == WM_PAINT )
2597 PeekMessageA( &msg, 0, 0, 0, PM_NOYIELD | PM_REMOVE);
2598 PeekMessageA( &msg, 0, 0, 0, PM_NOYIELD | PM_NOREMOVE);
2599 if( msg.message == WM_KEYDOWN &&
2600 (msg.wParam == VK_LEFT || msg.wParam == VK_RIGHT))
2602 pmt->trackFlags |= TF_SUSPENDPOPUP;
2603 return TRUE;
2606 break;
2609 /* failures go through this */
2610 pmt->trackFlags &= ~TF_SUSPENDPOPUP;
2611 return FALSE;
2614 /***********************************************************************
2615 * MENU_KeyLeft
2617 * Handle a VK_LEFT key event in a menu.
2619 static void MENU_KeyLeft( MTRACKER* pmt, UINT wFlags )
2621 POPUPMENU *menu;
2622 HMENU hmenutmp, hmenuprev;
2623 UINT prevcol;
2625 hmenuprev = hmenutmp = pmt->hTopMenu;
2626 menu = MENU_GetMenu( hmenutmp );
2628 /* Try to move 1 column left (if possible) */
2629 if( (prevcol = MENU_GetStartOfPrevColumn( pmt->hCurrentMenu )) !=
2630 NO_SELECTED_ITEM ) {
2632 MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
2633 prevcol, TRUE, 0 );
2634 return;
2637 /* close topmost popup */
2638 while (hmenutmp != pmt->hCurrentMenu)
2640 hmenuprev = hmenutmp;
2641 hmenutmp = MENU_GetSubPopup( hmenuprev );
2644 MENU_HideSubPopups( pmt->hOwnerWnd, hmenuprev, TRUE );
2645 pmt->hCurrentMenu = hmenuprev;
2647 if ( (hmenuprev == pmt->hTopMenu) && !(menu->wFlags & MF_POPUP) )
2649 /* move menu bar selection if no more popups are left */
2651 if( !MENU_DoNextMenu( pmt, VK_LEFT) )
2652 MENU_MoveSelection( pmt->hOwnerWnd, pmt->hTopMenu, ITEM_PREV );
2654 if ( hmenuprev != hmenutmp || pmt->trackFlags & TF_SUSPENDPOPUP )
2656 /* A sublevel menu was displayed - display the next one
2657 * unless there is another displacement coming up */
2659 if( !MENU_SuspendPopup( pmt, WM_KEYDOWN ) )
2660 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd,
2661 pmt->hTopMenu, TRUE, wFlags);
2667 /***********************************************************************
2668 * MENU_KeyRight
2670 * Handle a VK_RIGHT key event in a menu.
2672 static void MENU_KeyRight( MTRACKER* pmt, UINT wFlags )
2674 HMENU hmenutmp;
2675 POPUPMENU *menu = MENU_GetMenu( pmt->hTopMenu );
2676 UINT nextcol;
2678 TRACE("MENU_KeyRight called, cur %x (%s), top %x (%s).\n",
2679 pmt->hCurrentMenu,
2680 debugstr_w((MENU_GetMenu(pmt->hCurrentMenu))->
2681 items[0].text),
2682 pmt->hTopMenu, debugstr_w(menu->items[0].text) );
2684 if ( (menu->wFlags & MF_POPUP) || (pmt->hCurrentMenu != pmt->hTopMenu))
2686 /* If already displaying a popup, try to display sub-popup */
2688 hmenutmp = pmt->hCurrentMenu;
2689 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd, hmenutmp, TRUE, wFlags);
2691 /* if subpopup was displayed then we are done */
2692 if (hmenutmp != pmt->hCurrentMenu) return;
2695 /* Check to see if there's another column */
2696 if( (nextcol = MENU_GetStartOfNextColumn( pmt->hCurrentMenu )) !=
2697 NO_SELECTED_ITEM ) {
2698 TRACE("Going to %d.\n", nextcol );
2699 MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
2700 nextcol, TRUE, 0 );
2701 return;
2704 if (!(menu->wFlags & MF_POPUP)) /* menu bar tracking */
2706 if( pmt->hCurrentMenu != pmt->hTopMenu )
2708 MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE );
2709 hmenutmp = pmt->hCurrentMenu = pmt->hTopMenu;
2710 } else hmenutmp = 0;
2712 /* try to move to the next item */
2713 if( !MENU_DoNextMenu( pmt, VK_RIGHT) )
2714 MENU_MoveSelection( pmt->hOwnerWnd, pmt->hTopMenu, ITEM_NEXT );
2716 if( hmenutmp || pmt->trackFlags & TF_SUSPENDPOPUP )
2717 if( !MENU_SuspendPopup(pmt, WM_KEYDOWN) )
2718 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd,
2719 pmt->hTopMenu, TRUE, wFlags);
2723 /***********************************************************************
2724 * MENU_TrackMenu
2726 * Menu tracking code.
2728 static INT MENU_TrackMenu( HMENU hmenu, UINT wFlags, INT x, INT y,
2729 HWND hwnd, const RECT *lprect )
2731 MSG msg;
2732 POPUPMENU *menu;
2733 BOOL fRemove;
2734 INT executedMenuId = -1;
2735 MTRACKER mt;
2736 BOOL enterIdleSent = FALSE;
2738 mt.trackFlags = 0;
2739 mt.hCurrentMenu = hmenu;
2740 mt.hTopMenu = hmenu;
2741 mt.hOwnerWnd = hwnd;
2742 mt.pt.x = x;
2743 mt.pt.y = y;
2745 TRACE("hmenu=0x%04x flags=0x%08x (%d,%d) hwnd=0x%04x (%d,%d)-(%d,%d)\n",
2746 hmenu, wFlags, x, y, hwnd, (lprect) ? lprect->left : 0, (lprect) ? lprect->top : 0,
2747 (lprect) ? lprect->right : 0, (lprect) ? lprect->bottom : 0);
2749 fEndMenu = FALSE;
2750 if (!(menu = MENU_GetMenu( hmenu ))) return FALSE;
2752 if (wFlags & TPM_BUTTONDOWN)
2754 /* Get the result in order to start the tracking or not */
2755 fRemove = MENU_ButtonDown( &mt, hmenu, wFlags );
2756 fEndMenu = !fRemove;
2759 EVENT_Capture( mt.hOwnerWnd, HTMENU );
2761 while (!fEndMenu)
2763 menu = MENU_GetMenu( mt.hCurrentMenu );
2764 if (!menu) /* sometimes happens if I do a window manager close */
2765 break;
2766 msg.hwnd = (wFlags & TPM_ENTERIDLEEX && menu->wFlags & MF_POPUP) ? menu->hWnd : 0;
2768 /* we have to keep the message in the queue until it's
2769 * clear that menu loop is not over yet. */
2771 if (!MSG_InternalGetMessage( &msg, msg.hwnd, mt.hOwnerWnd, 0, 0,
2772 MSGF_MENU, PM_NOREMOVE, !enterIdleSent, &enterIdleSent )) break;
2774 /* check if EndMenu() tried to cancel us, by posting this message */
2775 if(msg.message == WM_CANCELMODE)
2777 /* we are now out of the loop */
2778 fEndMenu = TRUE;
2780 /* remove the message from the queue */
2781 PeekMessageA( &msg, 0, msg.message, msg.message, PM_REMOVE );
2783 /* break out of internal loop, ala ESCAPE */
2784 break;
2787 TranslateMessage( &msg );
2788 mt.pt = msg.pt;
2790 if ( (msg.hwnd==menu->hWnd) || (msg.message!=WM_TIMER) )
2791 enterIdleSent=FALSE;
2793 fRemove = FALSE;
2794 if ((msg.message >= WM_MOUSEFIRST) && (msg.message <= WM_MOUSELAST))
2797 * use the mouse coordinates in lParam instead of those in the MSG
2798 * struct to properly handle synthetic messages. lParam coords are
2799 * relative to client area, so they must be converted; since they can
2800 * be negative, we must use SLOWORD/SHIWORD instead of LOWORD/HIWORD.
2802 mt.pt.x = SLOWORD(msg.lParam);
2803 mt.pt.y = SHIWORD(msg.lParam);
2804 ClientToScreen(msg.hwnd,&mt.pt);
2806 /* Find a menu for this mouse event */
2807 hmenu = MENU_PtMenu( mt.hTopMenu, mt.pt );
2809 switch(msg.message)
2811 /* no WM_NC... messages in captured state */
2813 case WM_RBUTTONDBLCLK:
2814 case WM_RBUTTONDOWN:
2815 if (!(wFlags & TPM_RIGHTBUTTON)) break;
2816 /* fall through */
2817 case WM_LBUTTONDBLCLK:
2818 case WM_LBUTTONDOWN:
2819 /* If the message belongs to the menu, removes it from the queue */
2820 /* Else, end menu tracking */
2821 fRemove = MENU_ButtonDown( &mt, hmenu, wFlags );
2822 fEndMenu = !fRemove;
2823 break;
2825 case WM_RBUTTONUP:
2826 if (!(wFlags & TPM_RIGHTBUTTON)) break;
2827 /* fall through */
2828 case WM_LBUTTONUP:
2829 /* Check if a menu was selected by the mouse */
2830 if (hmenu)
2832 executedMenuId = MENU_ButtonUp( &mt, hmenu, wFlags);
2834 /* End the loop if executedMenuId is an item ID */
2835 /* or if the job was done (executedMenuId = 0). */
2836 fEndMenu = fRemove = (executedMenuId != -1);
2838 /* No menu was selected by the mouse */
2839 /* if the function was called by TrackPopupMenu, continue
2840 with the menu tracking. If not, stop it */
2841 else
2842 fEndMenu = ((wFlags & TPM_POPUPMENU) ? FALSE : TRUE);
2844 break;
2846 case WM_MOUSEMOVE:
2847 /* In win95 winelook, the selected menu item must be changed every time the
2848 mouse moves. In Win31 winelook, the mouse button has to be held down */
2850 if ( (TWEAK_WineLook > WIN31_LOOK) ||
2851 ( (msg.wParam & MK_LBUTTON) ||
2852 ((wFlags & TPM_RIGHTBUTTON) && (msg.wParam & MK_RBUTTON))) )
2854 fEndMenu |= !MENU_MouseMove( &mt, hmenu, wFlags );
2856 } /* switch(msg.message) - mouse */
2858 else if ((msg.message >= WM_KEYFIRST) && (msg.message <= WM_KEYLAST))
2860 fRemove = TRUE; /* Keyboard messages are always removed */
2861 switch(msg.message)
2863 case WM_KEYDOWN:
2864 switch(msg.wParam)
2866 case VK_HOME:
2867 case VK_END:
2868 MENU_SelectItem( mt.hOwnerWnd, mt.hCurrentMenu,
2869 NO_SELECTED_ITEM, FALSE, 0 );
2870 /* fall through */
2871 case VK_UP:
2872 MENU_MoveSelection( mt.hOwnerWnd, mt.hCurrentMenu,
2873 (msg.wParam == VK_HOME)? ITEM_NEXT : ITEM_PREV );
2874 break;
2876 case VK_DOWN: /* If on menu bar, pull-down the menu */
2878 menu = MENU_GetMenu( mt.hCurrentMenu );
2879 if (!(menu->wFlags & MF_POPUP))
2880 mt.hCurrentMenu = MENU_ShowSubPopup(mt.hOwnerWnd, mt.hTopMenu, TRUE, wFlags);
2881 else /* otherwise try to move selection */
2882 MENU_MoveSelection( mt.hOwnerWnd, mt.hCurrentMenu, ITEM_NEXT );
2883 break;
2885 case VK_LEFT:
2886 MENU_KeyLeft( &mt, wFlags );
2887 break;
2889 case VK_RIGHT:
2890 MENU_KeyRight( &mt, wFlags );
2891 break;
2893 case VK_ESCAPE:
2894 fEndMenu = TRUE;
2895 break;
2897 case VK_F1:
2899 HELPINFO hi;
2900 hi.cbSize = sizeof(HELPINFO);
2901 hi.iContextType = HELPINFO_MENUITEM;
2902 if (menu->FocusedItem == NO_SELECTED_ITEM)
2903 hi.iCtrlId = 0;
2904 else
2905 hi.iCtrlId = menu->items[menu->FocusedItem].wID;
2906 hi.hItemHandle = hmenu;
2907 hi.dwContextId = menu->dwContextHelpID;
2908 hi.MousePos = msg.pt;
2909 SendMessageA(hwnd, WM_HELP, 0, (LPARAM)&hi);
2910 break;
2913 default:
2914 break;
2916 break; /* WM_KEYDOWN */
2918 case WM_SYSKEYDOWN:
2919 switch(msg.wParam)
2921 case VK_MENU:
2922 fEndMenu = TRUE;
2923 break;
2926 break; /* WM_SYSKEYDOWN */
2928 case WM_CHAR:
2930 UINT pos;
2932 if (msg.wParam == '\r' || msg.wParam == ' ')
2934 executedMenuId = MENU_ExecFocusedItem(&mt,mt.hCurrentMenu, wFlags);
2935 fEndMenu = (executedMenuId != -1);
2937 break;
2940 /* Hack to avoid control chars. */
2941 /* We will find a better way real soon... */
2942 if ((msg.wParam <= 32) || (msg.wParam >= 127)) break;
2944 pos = MENU_FindItemByKey( mt.hOwnerWnd, mt.hCurrentMenu,
2945 LOWORD(msg.wParam), FALSE );
2946 if (pos == (UINT)-2) fEndMenu = TRUE;
2947 else if (pos == (UINT)-1) MessageBeep(0);
2948 else
2950 MENU_SelectItem( mt.hOwnerWnd, mt.hCurrentMenu, pos,
2951 TRUE, 0 );
2952 executedMenuId = MENU_ExecFocusedItem(&mt,mt.hCurrentMenu, wFlags);
2953 fEndMenu = (executedMenuId != -1);
2956 break;
2957 } /* switch(msg.message) - kbd */
2959 else
2961 DispatchMessageA( &msg );
2964 if (!fEndMenu) fRemove = TRUE;
2966 /* finally remove message from the queue */
2968 if (fRemove && !(mt.trackFlags & TF_SKIPREMOVE) )
2969 PeekMessageA( &msg, 0, msg.message, msg.message, PM_REMOVE );
2970 else mt.trackFlags &= ~TF_SKIPREMOVE;
2973 ReleaseCapture();
2975 /* If dropdown is still painted and the close box is clicked on
2976 then the menu will be destroyed as part of the DispatchMessage above.
2977 This will then invalidate the menu handle in mt.hTopMenu. We should
2978 check for this first. */
2979 if( IsMenu( mt.hTopMenu ) )
2981 menu = MENU_GetMenu( mt.hTopMenu );
2983 if( IsWindow( mt.hOwnerWnd ) )
2985 MENU_HideSubPopups( mt.hOwnerWnd, mt.hTopMenu, FALSE );
2987 if (menu && menu->wFlags & MF_POPUP)
2989 ShowWindow( menu->hWnd, SW_HIDE );
2990 uSubPWndLevel = 0;
2992 MENU_SelectItem( mt.hOwnerWnd, mt.hTopMenu, NO_SELECTED_ITEM, FALSE, 0 );
2993 SendMessageA( mt.hOwnerWnd, WM_MENUSELECT, MAKELONG(0,0xffff), 0 );
2996 /* Reset the variable for hiding menu */
2997 if( menu ) menu->bTimeToHide = FALSE;
3000 /* The return value is only used by TrackPopupMenu */
3001 return ((executedMenuId != -1) ? executedMenuId : 0);
3004 /***********************************************************************
3005 * MENU_InitTracking
3007 static BOOL MENU_InitTracking(HWND hWnd, HMENU hMenu, BOOL bPopup, UINT wFlags)
3009 TRACE("hwnd=0x%04x hmenu=0x%04x\n", hWnd, hMenu);
3011 HideCaret(0);
3013 /* Send WM_ENTERMENULOOP and WM_INITMENU message only if TPM_NONOTIFY flag is not specified */
3014 if (!(wFlags & TPM_NONOTIFY))
3015 SendMessageA( hWnd, WM_ENTERMENULOOP, bPopup, 0 );
3017 SendMessageA( hWnd, WM_SETCURSOR, hWnd, HTCAPTION );
3019 if (!(wFlags & TPM_NONOTIFY))
3021 POPUPMENU *menu;
3022 SendMessageA( hWnd, WM_INITMENU, hMenu, 0 );
3023 if ((menu = MENU_GetMenu( hMenu )) && (!menu->Height))
3024 { /* app changed/recreated menu bar entries in WM_INITMENU
3025 Recalculate menu sizes else clicks will not work */
3026 RECT r;
3027 HDC hdc = GetDCEx( hWnd, 0, DCX_CACHE | DCX_WINDOW );
3028 SelectObject( hdc, hMenuFont);
3029 GetClientRect(hWnd, &r); /* probably too simple */
3030 MENU_MenuBarCalcSize( hdc, &r, menu, hWnd );
3031 ReleaseDC(hWnd, hdc);
3034 return TRUE;
3036 /***********************************************************************
3037 * MENU_ExitTracking
3039 static BOOL MENU_ExitTracking(HWND hWnd)
3041 TRACE("hwnd=0x%04x\n", hWnd);
3043 SendMessageA( hWnd, WM_EXITMENULOOP, 0, 0 );
3044 ShowCaret(0);
3045 return TRUE;
3048 /***********************************************************************
3049 * MENU_TrackMouseMenuBar
3051 * Menu-bar tracking upon a mouse event. Called from NC_HandleSysCommand().
3053 void MENU_TrackMouseMenuBar( WND* wndPtr, INT ht, POINT pt )
3055 HWND hWnd = wndPtr->hwndSelf;
3056 HMENU hMenu = (ht == HTSYSMENU) ? wndPtr->hSysMenu : wndPtr->wIDmenu;
3057 UINT wFlags = TPM_ENTERIDLEEX | TPM_BUTTONDOWN | TPM_LEFTALIGN | TPM_LEFTBUTTON;
3059 TRACE("pwnd=%p ht=0x%04x (%ld,%ld)\n", wndPtr, ht, pt.x, pt.y);
3061 if (IsMenu(hMenu))
3063 MENU_InitTracking( hWnd, hMenu, FALSE, wFlags );
3064 MENU_TrackMenu( hMenu, wFlags, pt.x, pt.y, hWnd, NULL );
3065 MENU_ExitTracking(hWnd);
3070 /***********************************************************************
3071 * MENU_TrackKbdMenuBar
3073 * Menu-bar tracking upon a keyboard event. Called from NC_HandleSysCommand().
3075 void MENU_TrackKbdMenuBar( WND* wndPtr, UINT wParam, INT vkey)
3077 UINT uItem = NO_SELECTED_ITEM;
3078 HMENU hTrackMenu;
3079 UINT wFlags = TPM_ENTERIDLEEX | TPM_LEFTALIGN | TPM_LEFTBUTTON;
3081 /* find window that has a menu */
3083 while( wndPtr->dwStyle & WS_CHILD)
3084 if( !(wndPtr = wndPtr->parent) ) return;
3086 /* check if we have to track a system menu */
3088 if( (wndPtr->dwStyle & (WS_CHILD | WS_MINIMIZE)) ||
3089 !wndPtr->wIDmenu || vkey == VK_SPACE )
3091 if( !(wndPtr->dwStyle & WS_SYSMENU) ) return;
3092 hTrackMenu = wndPtr->hSysMenu;
3093 uItem = 0;
3094 wParam |= HTSYSMENU; /* prevent item lookup */
3096 else
3097 hTrackMenu = wndPtr->wIDmenu;
3099 if (IsMenu( hTrackMenu ))
3101 MENU_InitTracking( wndPtr->hwndSelf, hTrackMenu, FALSE, wFlags );
3103 if( vkey && vkey != VK_SPACE )
3105 uItem = MENU_FindItemByKey( wndPtr->hwndSelf, hTrackMenu,
3106 vkey, (wParam & HTSYSMENU) );
3107 if( uItem >= (UINT)(-2) )
3109 if( uItem == (UINT)(-1) ) MessageBeep(0);
3110 hTrackMenu = 0;
3114 if( hTrackMenu )
3116 MENU_SelectItem( wndPtr->hwndSelf, hTrackMenu, uItem, TRUE, 0 );
3118 if( uItem == NO_SELECTED_ITEM )
3119 MENU_MoveSelection( wndPtr->hwndSelf, hTrackMenu, ITEM_NEXT );
3120 else if( vkey )
3121 PostMessageA( wndPtr->hwndSelf, WM_KEYDOWN, VK_DOWN, 0L );
3123 MENU_TrackMenu( hTrackMenu, wFlags, 0, 0, wndPtr->hwndSelf, NULL );
3126 MENU_ExitTracking (wndPtr->hwndSelf);
3131 /**********************************************************************
3132 * TrackPopupMenu16 (USER.416)
3134 BOOL16 WINAPI TrackPopupMenu16( HMENU16 hMenu, UINT16 wFlags, INT16 x, INT16 y,
3135 INT16 nReserved, HWND16 hWnd, const RECT16 *lpRect )
3137 RECT r;
3138 if (lpRect)
3139 CONV_RECT16TO32( lpRect, &r );
3140 return TrackPopupMenu( hMenu, wFlags, x, y, nReserved, hWnd,
3141 lpRect ? &r : NULL );
3145 /**********************************************************************
3146 * TrackPopupMenu (USER32.@)
3148 * Like the win32 API, the function return the command ID only if the
3149 * flag TPM_RETURNCMD is on.
3152 BOOL WINAPI TrackPopupMenu( HMENU hMenu, UINT wFlags, INT x, INT y,
3153 INT nReserved, HWND hWnd, const RECT *lpRect )
3155 BOOL ret = FALSE;
3157 MENU_InitTracking(hWnd, hMenu, TRUE, wFlags);
3159 /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
3160 if (!(wFlags & TPM_NONOTIFY))
3161 SendMessageA( hWnd, WM_INITMENUPOPUP, hMenu, 0);
3163 if (MENU_ShowPopup( hWnd, hMenu, 0, x, y, 0, 0 ))
3164 ret = MENU_TrackMenu( hMenu, wFlags | TPM_POPUPMENU, 0, 0, hWnd, lpRect );
3165 MENU_ExitTracking(hWnd);
3167 if( (!(wFlags & TPM_RETURNCMD)) && (ret != FALSE) )
3168 ret = 1;
3170 return ret;
3173 /**********************************************************************
3174 * TrackPopupMenuEx (USER32.@)
3176 BOOL WINAPI TrackPopupMenuEx( HMENU hMenu, UINT wFlags, INT x, INT y,
3177 HWND hWnd, LPTPMPARAMS lpTpm )
3179 FIXME("not fully implemented\n" );
3180 return TrackPopupMenu( hMenu, wFlags, x, y, 0, hWnd,
3181 lpTpm ? &lpTpm->rcExclude : NULL );
3184 /***********************************************************************
3185 * PopupMenuWndProc
3187 * NOTE: Windows has totally different (and undocumented) popup wndproc.
3189 static LRESULT WINAPI PopupMenuWndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam )
3191 TRACE("hwnd=0x%04x msg=0x%04x wp=0x%04x lp=0x%08lx\n",
3192 hwnd, message, wParam, lParam);
3194 switch(message)
3196 case WM_CREATE:
3198 CREATESTRUCTW *cs = (CREATESTRUCTW*)lParam;
3199 SetWindowLongW( hwnd, 0, (LONG)cs->lpCreateParams );
3200 return 0;
3203 case WM_MOUSEACTIVATE: /* We don't want to be activated */
3204 return MA_NOACTIVATE;
3206 case WM_PAINT:
3208 PAINTSTRUCT ps;
3209 BeginPaint( hwnd, &ps );
3210 MENU_DrawPopupMenu( hwnd, ps.hdc,
3211 (HMENU)GetWindowLongA( hwnd, 0 ) );
3212 EndPaint( hwnd, &ps );
3213 return 0;
3215 case WM_ERASEBKGND:
3216 return 1;
3218 case WM_DESTROY:
3220 /* zero out global pointer in case resident popup window
3221 * was somehow destroyed. */
3223 if(MENU_GetTopPopupWnd() )
3225 if( hwnd == pTopPopupWnd->hwndSelf )
3227 ERR("resident popup destroyed!\n");
3229 MENU_DestroyTopPopupWnd();
3230 uSubPWndLevel = 0;
3232 else
3233 uSubPWndLevel--;
3234 MENU_ReleaseTopPopupWnd();
3236 break;
3238 case WM_SHOWWINDOW:
3240 if( wParam )
3242 if (!GetWindowLongW( hwnd, 0 )) ERR("no menu to display\n");
3244 else
3245 SetWindowLongW( hwnd, 0, 0 );
3246 break;
3248 case MM_SETMENUHANDLE:
3249 SetWindowLongW( hwnd, 0, wParam );
3250 break;
3252 case MM_GETMENUHANDLE:
3253 return GetWindowLongW( hwnd, 0 );
3255 default:
3256 return DefWindowProcW( hwnd, message, wParam, lParam );
3258 return 0;
3262 /***********************************************************************
3263 * MENU_GetMenuBarHeight
3265 * Compute the size of the menu bar height. Used by NC_HandleNCCalcSize().
3267 UINT MENU_GetMenuBarHeight( HWND hwnd, UINT menubarWidth,
3268 INT orgX, INT orgY )
3270 HDC hdc;
3271 RECT rectBar;
3272 WND *wndPtr;
3273 LPPOPUPMENU lppop;
3274 UINT retvalue;
3276 TRACE("HWND 0x%x, width %d, at (%d, %d).\n",
3277 hwnd, menubarWidth, orgX, orgY );
3279 if (!(wndPtr = WIN_FindWndPtr( hwnd )))
3280 return 0;
3282 if (!(lppop = MENU_GetMenu((HMENU16)wndPtr->wIDmenu)))
3284 WIN_ReleaseWndPtr(wndPtr);
3285 return 0;
3288 hdc = GetDCEx( hwnd, 0, DCX_CACHE | DCX_WINDOW );
3289 SelectObject( hdc, hMenuFont);
3290 SetRect(&rectBar, orgX, orgY, orgX+menubarWidth, orgY+GetSystemMetrics(SM_CYMENU));
3291 MENU_MenuBarCalcSize( hdc, &rectBar, lppop, hwnd );
3292 ReleaseDC( hwnd, hdc );
3293 retvalue = lppop->Height;
3294 WIN_ReleaseWndPtr(wndPtr);
3295 return retvalue;
3299 /*******************************************************************
3300 * ChangeMenu16 (USER.153)
3302 BOOL16 WINAPI ChangeMenu16( HMENU16 hMenu, UINT16 pos, SEGPTR data,
3303 UINT16 id, UINT16 flags )
3305 TRACE("menu=%04x pos=%d data=%08lx id=%04x flags=%04x\n",
3306 hMenu, pos, (DWORD)data, id, flags );
3307 if (flags & MF_APPEND) return AppendMenu16( hMenu, flags & ~MF_APPEND,
3308 id, data );
3310 /* FIXME: Word passes the item id in 'pos' and 0 or 0xffff as id */
3311 /* for MF_DELETE. We should check the parameters for all others */
3312 /* MF_* actions also (anybody got a doc on ChangeMenu?). */
3314 if (flags & MF_DELETE) return DeleteMenu16(hMenu, pos, flags & ~MF_DELETE);
3315 if (flags & MF_CHANGE) return ModifyMenu16(hMenu, pos, flags & ~MF_CHANGE,
3316 id, data );
3317 if (flags & MF_REMOVE) return RemoveMenu16(hMenu,
3318 flags & MF_BYPOSITION ? pos : id,
3319 flags & ~MF_REMOVE );
3320 /* Default: MF_INSERT */
3321 return InsertMenu16( hMenu, pos, flags, id, data );
3325 /*******************************************************************
3326 * ChangeMenuA (USER32.@)
3328 BOOL WINAPI ChangeMenuA( HMENU hMenu, UINT pos, LPCSTR data,
3329 UINT id, UINT flags )
3331 TRACE("menu=%08x pos=%d data=%08lx id=%08x flags=%08x\n",
3332 hMenu, pos, (DWORD)data, id, flags );
3333 if (flags & MF_APPEND) return AppendMenuA( hMenu, flags & ~MF_APPEND,
3334 id, data );
3335 if (flags & MF_DELETE) return DeleteMenu(hMenu, pos, flags & ~MF_DELETE);
3336 if (flags & MF_CHANGE) return ModifyMenuA(hMenu, pos, flags & ~MF_CHANGE,
3337 id, data );
3338 if (flags & MF_REMOVE) return RemoveMenu( hMenu,
3339 flags & MF_BYPOSITION ? pos : id,
3340 flags & ~MF_REMOVE );
3341 /* Default: MF_INSERT */
3342 return InsertMenuA( hMenu, pos, flags, id, data );
3346 /*******************************************************************
3347 * ChangeMenuW (USER32.@)
3349 BOOL WINAPI ChangeMenuW( HMENU hMenu, UINT pos, LPCWSTR data,
3350 UINT id, UINT flags )
3352 TRACE("menu=%08x pos=%d data=%08lx id=%08x flags=%08x\n",
3353 hMenu, pos, (DWORD)data, id, flags );
3354 if (flags & MF_APPEND) return AppendMenuW( hMenu, flags & ~MF_APPEND,
3355 id, data );
3356 if (flags & MF_DELETE) return DeleteMenu(hMenu, pos, flags & ~MF_DELETE);
3357 if (flags & MF_CHANGE) return ModifyMenuW(hMenu, pos, flags & ~MF_CHANGE,
3358 id, data );
3359 if (flags & MF_REMOVE) return RemoveMenu( hMenu,
3360 flags & MF_BYPOSITION ? pos : id,
3361 flags & ~MF_REMOVE );
3362 /* Default: MF_INSERT */
3363 return InsertMenuW( hMenu, pos, flags, id, data );
3367 /*******************************************************************
3368 * CheckMenuItem16 (USER.154)
3370 BOOL16 WINAPI CheckMenuItem16( HMENU16 hMenu, UINT16 id, UINT16 flags )
3372 return (BOOL16)CheckMenuItem( hMenu, id, flags );
3376 /*******************************************************************
3377 * CheckMenuItem (USER32.@)
3379 DWORD WINAPI CheckMenuItem( HMENU hMenu, UINT id, UINT flags )
3381 MENUITEM *item;
3382 DWORD ret;
3384 TRACE("menu=%04x id=%04x flags=%04x\n", hMenu, id, flags );
3385 if (!(item = MENU_FindItem( &hMenu, &id, flags ))) return -1;
3386 ret = item->fState & MF_CHECKED;
3387 if (flags & MF_CHECKED) item->fState |= MF_CHECKED;
3388 else item->fState &= ~MF_CHECKED;
3389 return ret;
3393 /**********************************************************************
3394 * EnableMenuItem16 (USER.155)
3396 UINT16 WINAPI EnableMenuItem16( HMENU16 hMenu, UINT16 wItemID, UINT16 wFlags )
3398 return EnableMenuItem( hMenu, wItemID, wFlags );
3402 /**********************************************************************
3403 * EnableMenuItem (USER32.@)
3405 UINT WINAPI EnableMenuItem( HMENU hMenu, UINT wItemID, UINT wFlags )
3407 UINT oldflags;
3408 MENUITEM *item;
3409 POPUPMENU *menu;
3411 TRACE("(%04x, %04X, %04X) !\n",
3412 hMenu, wItemID, wFlags);
3414 /* Get the Popupmenu to access the owner menu */
3415 if (!(menu = MENU_GetMenu(hMenu)))
3416 return (UINT)-1;
3418 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags )))
3419 return (UINT)-1;
3421 oldflags = item->fState & (MF_GRAYED | MF_DISABLED);
3422 item->fState ^= (oldflags ^ wFlags) & (MF_GRAYED | MF_DISABLED);
3424 /* In win95 if the close item in the system menu change update the close button */
3425 if (TWEAK_WineLook == WIN95_LOOK)
3426 if((item->wID == SC_CLOSE) && (oldflags != wFlags))
3428 if (menu->hSysMenuOwner != 0)
3430 POPUPMENU* parentMenu;
3432 /* Get the parent menu to access*/
3433 if (!(parentMenu = MENU_GetMenu(menu->hSysMenuOwner)))
3434 return (UINT)-1;
3436 /* Refresh the frame to reflect the change*/
3437 SetWindowPos(parentMenu->hWnd, 0, 0, 0, 0, 0,
3438 SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER);
3442 return oldflags;
3446 /*******************************************************************
3447 * GetMenuString16 (USER.161)
3449 INT16 WINAPI GetMenuString16( HMENU16 hMenu, UINT16 wItemID,
3450 LPSTR str, INT16 nMaxSiz, UINT16 wFlags )
3452 return GetMenuStringA( hMenu, wItemID, str, nMaxSiz, wFlags );
3456 /*******************************************************************
3457 * GetMenuStringA (USER32.@)
3459 INT WINAPI GetMenuStringA(
3460 HMENU hMenu, /* [in] menuhandle */
3461 UINT wItemID, /* [in] menu item (dep. on wFlags) */
3462 LPSTR str, /* [out] outbuffer. If NULL, func returns entry length*/
3463 INT nMaxSiz, /* [in] length of buffer. if 0, func returns entry len*/
3464 UINT wFlags /* [in] MF_ flags */
3466 MENUITEM *item;
3468 TRACE("menu=%04x item=%04x ptr=%p len=%d flags=%04x\n",
3469 hMenu, wItemID, str, nMaxSiz, wFlags );
3470 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) return 0;
3471 if (!IS_STRING_ITEM(item->fType)) return 0;
3472 if (!str || !nMaxSiz) return strlenW(item->text);
3473 str[0] = '\0';
3474 if (!WideCharToMultiByte( CP_ACP, 0, item->text, -1, str, nMaxSiz, NULL, NULL ))
3475 str[nMaxSiz-1] = 0;
3476 TRACE("returning '%s'\n", str );
3477 return strlen(str);
3481 /*******************************************************************
3482 * GetMenuStringW (USER32.@)
3484 INT WINAPI GetMenuStringW( HMENU hMenu, UINT wItemID,
3485 LPWSTR str, INT nMaxSiz, UINT wFlags )
3487 MENUITEM *item;
3489 TRACE("menu=%04x item=%04x ptr=%p len=%d flags=%04x\n",
3490 hMenu, wItemID, str, nMaxSiz, wFlags );
3491 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) return 0;
3492 if (!IS_STRING_ITEM(item->fType)) return 0;
3493 if (!str || !nMaxSiz) return strlenW(item->text);
3494 str[0] = '\0';
3495 lstrcpynW( str, item->text, nMaxSiz );
3496 return strlenW(str);
3500 /**********************************************************************
3501 * HiliteMenuItem16 (USER.162)
3503 BOOL16 WINAPI HiliteMenuItem16( HWND16 hWnd, HMENU16 hMenu, UINT16 wItemID,
3504 UINT16 wHilite )
3506 return HiliteMenuItem( hWnd, hMenu, wItemID, wHilite );
3510 /**********************************************************************
3511 * HiliteMenuItem (USER32.@)
3513 BOOL WINAPI HiliteMenuItem( HWND hWnd, HMENU hMenu, UINT wItemID,
3514 UINT wHilite )
3516 LPPOPUPMENU menu;
3517 TRACE("(%04x, %04x, %04x, %04x);\n",
3518 hWnd, hMenu, wItemID, wHilite);
3519 if (!MENU_FindItem( &hMenu, &wItemID, wHilite )) return FALSE;
3520 if (!(menu = MENU_GetMenu(hMenu))) return FALSE;
3521 if (menu->FocusedItem == wItemID) return TRUE;
3522 MENU_HideSubPopups( hWnd, hMenu, FALSE );
3523 MENU_SelectItem( hWnd, hMenu, wItemID, TRUE, 0 );
3524 return TRUE;
3528 /**********************************************************************
3529 * GetMenuState16 (USER.250)
3531 UINT16 WINAPI GetMenuState16( HMENU16 hMenu, UINT16 wItemID, UINT16 wFlags )
3533 return GetMenuState( hMenu, wItemID, wFlags );
3537 /**********************************************************************
3538 * GetMenuState (USER32.@)
3540 UINT WINAPI GetMenuState( HMENU hMenu, UINT wItemID, UINT wFlags )
3542 MENUITEM *item;
3543 TRACE("(menu=%04x, id=%04x, flags=%04x);\n",
3544 hMenu, wItemID, wFlags);
3545 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) return -1;
3546 debug_print_menuitem (" item: ", item, "");
3547 if (item->fType & MF_POPUP)
3549 POPUPMENU *menu = MENU_GetMenu( item->hSubMenu );
3550 if (!menu) return -1;
3551 else return (menu->nItems << 8) | ((item->fState|item->fType) & 0xff);
3553 else
3555 /* We used to (from way back then) mask the result to 0xff. */
3556 /* I don't know why and it seems wrong as the documented */
3557 /* return flag MF_SEPARATOR is outside that mask. */
3558 return (item->fType | item->fState);
3563 /**********************************************************************
3564 * GetMenuItemCount16 (USER.263)
3566 INT16 WINAPI GetMenuItemCount16( HMENU16 hMenu )
3568 LPPOPUPMENU menu = MENU_GetMenu(hMenu);
3569 if (!menu) return -1;
3570 TRACE("(%04x) returning %d\n",
3571 hMenu, menu->nItems );
3572 return menu->nItems;
3576 /**********************************************************************
3577 * GetMenuItemCount (USER32.@)
3579 INT WINAPI GetMenuItemCount( HMENU hMenu )
3581 LPPOPUPMENU menu = MENU_GetMenu(hMenu);
3582 if (!menu) return -1;
3583 TRACE("(%04x) returning %d\n",
3584 hMenu, menu->nItems );
3585 return menu->nItems;
3588 /**********************************************************************
3589 * GetMenuItemID16 (USER.264)
3591 UINT16 WINAPI GetMenuItemID16( HMENU16 hMenu, INT16 nPos )
3593 return (UINT16) GetMenuItemID (hMenu, nPos);
3596 /**********************************************************************
3597 * GetMenuItemID (USER32.@)
3599 UINT WINAPI GetMenuItemID( HMENU hMenu, INT nPos )
3601 MENUITEM * lpmi;
3603 if (!(lpmi = MENU_FindItem(&hMenu,&nPos,MF_BYPOSITION))) return 0;
3604 if (lpmi->fType & MF_POPUP) return -1;
3605 return lpmi->wID;
3609 /*******************************************************************
3610 * InsertMenu16 (USER.410)
3612 BOOL16 WINAPI InsertMenu16( HMENU16 hMenu, UINT16 pos, UINT16 flags,
3613 UINT16 id, SEGPTR data )
3615 UINT pos32 = (UINT)pos;
3616 if ((pos == (UINT16)-1) && (flags & MF_BYPOSITION)) pos32 = (UINT)-1;
3617 if (IS_STRING_ITEM(flags) && data)
3618 return InsertMenuA( hMenu, pos32, flags, id, MapSL(data) );
3619 return InsertMenuA( hMenu, pos32, flags, id, (LPSTR)data );
3623 /*******************************************************************
3624 * InsertMenuW (USER32.@)
3626 BOOL WINAPI InsertMenuW( HMENU hMenu, UINT pos, UINT flags,
3627 UINT id, LPCWSTR str )
3629 MENUITEM *item;
3631 if (IS_STRING_ITEM(flags) && str)
3632 TRACE("hMenu %04x, pos %d, flags %08x, "
3633 "id %04x, str '%s'\n",
3634 hMenu, pos, flags, id, debugstr_w(str) );
3635 else TRACE("hMenu %04x, pos %d, flags %08x, "
3636 "id %04x, str %08lx (not a string)\n",
3637 hMenu, pos, flags, id, (DWORD)str );
3639 if (!(item = MENU_InsertItem( hMenu, pos, flags ))) return FALSE;
3641 if (!(MENU_SetItemData( item, flags, id, str )))
3643 RemoveMenu( hMenu, pos, flags );
3644 return FALSE;
3647 if (flags & MF_POPUP) /* Set the MF_POPUP flag on the popup-menu */
3648 (MENU_GetMenu((HMENU16)id))->wFlags |= MF_POPUP;
3650 item->hCheckBit = item->hUnCheckBit = 0;
3651 return TRUE;
3655 /*******************************************************************
3656 * InsertMenuA (USER32.@)
3658 BOOL WINAPI InsertMenuA( HMENU hMenu, UINT pos, UINT flags,
3659 UINT id, LPCSTR str )
3661 BOOL ret;
3663 if (IS_STRING_ITEM(flags) && str)
3665 LPWSTR newstr = HEAP_strdupAtoW( GetProcessHeap(), 0, str );
3666 ret = InsertMenuW( hMenu, pos, flags, id, newstr );
3667 HeapFree( GetProcessHeap(), 0, newstr );
3668 return ret;
3670 else return InsertMenuW( hMenu, pos, flags, id, (LPCWSTR)str );
3674 /*******************************************************************
3675 * AppendMenu16 (USER.411)
3677 BOOL16 WINAPI AppendMenu16(HMENU16 hMenu, UINT16 flags, UINT16 id, SEGPTR data)
3679 return InsertMenu16( hMenu, -1, flags | MF_BYPOSITION, id, data );
3683 /*******************************************************************
3684 * AppendMenuA (USER32.@)
3686 BOOL WINAPI AppendMenuA( HMENU hMenu, UINT flags,
3687 UINT id, LPCSTR data )
3689 return InsertMenuA( hMenu, -1, flags | MF_BYPOSITION, id, data );
3693 /*******************************************************************
3694 * AppendMenuW (USER32.@)
3696 BOOL WINAPI AppendMenuW( HMENU hMenu, UINT flags,
3697 UINT id, LPCWSTR data )
3699 return InsertMenuW( hMenu, -1, flags | MF_BYPOSITION, id, data );
3703 /**********************************************************************
3704 * RemoveMenu16 (USER.412)
3706 BOOL16 WINAPI RemoveMenu16( HMENU16 hMenu, UINT16 nPos, UINT16 wFlags )
3708 return RemoveMenu( hMenu, nPos, wFlags );
3712 /**********************************************************************
3713 * RemoveMenu (USER32.@)
3715 BOOL WINAPI RemoveMenu( HMENU hMenu, UINT nPos, UINT wFlags )
3717 LPPOPUPMENU menu;
3718 MENUITEM *item;
3720 TRACE("(menu=%04x pos=%04x flags=%04x)\n",hMenu, nPos, wFlags);
3721 if (!(item = MENU_FindItem( &hMenu, &nPos, wFlags ))) return FALSE;
3722 if (!(menu = MENU_GetMenu(hMenu))) return FALSE;
3724 /* Remove item */
3726 MENU_FreeItemData( item );
3728 if (--menu->nItems == 0)
3730 HeapFree( GetProcessHeap(), 0, menu->items );
3731 menu->items = NULL;
3733 else
3735 while(nPos < menu->nItems)
3737 *item = *(item+1);
3738 item++;
3739 nPos++;
3741 menu->items = HeapReAlloc( GetProcessHeap(), 0, menu->items,
3742 menu->nItems * sizeof(MENUITEM) );
3744 return TRUE;
3748 /**********************************************************************
3749 * DeleteMenu16 (USER.413)
3751 BOOL16 WINAPI DeleteMenu16( HMENU16 hMenu, UINT16 nPos, UINT16 wFlags )
3753 return DeleteMenu( hMenu, nPos, wFlags );
3757 /**********************************************************************
3758 * DeleteMenu (USER32.@)
3760 BOOL WINAPI DeleteMenu( HMENU hMenu, UINT nPos, UINT wFlags )
3762 MENUITEM *item = MENU_FindItem( &hMenu, &nPos, wFlags );
3763 if (!item) return FALSE;
3764 if (item->fType & MF_POPUP) DestroyMenu( item->hSubMenu );
3765 /* nPos is now the position of the item */
3766 RemoveMenu( hMenu, nPos, wFlags | MF_BYPOSITION );
3767 return TRUE;
3771 /*******************************************************************
3772 * ModifyMenu16 (USER.414)
3774 BOOL16 WINAPI ModifyMenu16( HMENU16 hMenu, UINT16 pos, UINT16 flags,
3775 UINT16 id, SEGPTR data )
3777 if (IS_STRING_ITEM(flags))
3778 return ModifyMenuA( hMenu, pos, flags, id, MapSL(data) );
3779 return ModifyMenuA( hMenu, pos, flags, id, (LPSTR)data );
3783 /*******************************************************************
3784 * ModifyMenuW (USER32.@)
3786 BOOL WINAPI ModifyMenuW( HMENU hMenu, UINT pos, UINT flags,
3787 UINT id, LPCWSTR str )
3789 MENUITEM *item;
3791 if (IS_STRING_ITEM(flags))
3793 TRACE("%04x %d %04x %04x '%s'\n",
3794 hMenu, pos, flags, id, str ? debugstr_w(str) : "#NULL#" );
3795 if (!str) return FALSE;
3797 else
3799 TRACE("%04x %d %04x %04x %08lx\n",
3800 hMenu, pos, flags, id, (DWORD)str );
3803 if (!(item = MENU_FindItem( &hMenu, &pos, flags ))) return FALSE;
3804 return MENU_SetItemData( item, flags, id, str );
3808 /*******************************************************************
3809 * ModifyMenuA (USER32.@)
3811 BOOL WINAPI ModifyMenuA( HMENU hMenu, UINT pos, UINT flags,
3812 UINT id, LPCSTR str )
3814 BOOL ret;
3816 if (IS_STRING_ITEM(flags) && str)
3818 LPWSTR newstr = HEAP_strdupAtoW( GetProcessHeap(), 0, str );
3819 ret = ModifyMenuW( hMenu, pos, flags, id, newstr );
3820 HeapFree( GetProcessHeap(), 0, newstr );
3821 return ret;
3823 else return ModifyMenuW( hMenu, pos, flags, id, (LPCWSTR)str );
3827 /**********************************************************************
3828 * CreatePopupMenu16 (USER.415)
3830 HMENU16 WINAPI CreatePopupMenu16(void)
3832 return CreatePopupMenu();
3836 /**********************************************************************
3837 * CreatePopupMenu (USER32.@)
3839 HMENU WINAPI CreatePopupMenu(void)
3841 HMENU hmenu;
3842 POPUPMENU *menu;
3844 if (!(hmenu = CreateMenu())) return 0;
3845 menu = (POPUPMENU *) USER_HEAP_LIN_ADDR( hmenu );
3846 menu->wFlags |= MF_POPUP;
3847 menu->bTimeToHide = FALSE;
3848 return hmenu;
3852 /**********************************************************************
3853 * GetMenuCheckMarkDimensions (USER.417) (USER32.@)
3855 DWORD WINAPI GetMenuCheckMarkDimensions(void)
3857 return MAKELONG( GetSystemMetrics(SM_CXMENUCHECK), GetSystemMetrics(SM_CYMENUCHECK) );
3861 /**********************************************************************
3862 * SetMenuItemBitmaps16 (USER.418)
3864 BOOL16 WINAPI SetMenuItemBitmaps16( HMENU16 hMenu, UINT16 nPos, UINT16 wFlags,
3865 HBITMAP16 hNewUnCheck, HBITMAP16 hNewCheck)
3867 return SetMenuItemBitmaps( hMenu, nPos, wFlags, hNewUnCheck, hNewCheck );
3871 /**********************************************************************
3872 * SetMenuItemBitmaps (USER32.@)
3874 BOOL WINAPI SetMenuItemBitmaps( HMENU hMenu, UINT nPos, UINT wFlags,
3875 HBITMAP hNewUnCheck, HBITMAP hNewCheck)
3877 MENUITEM *item;
3878 TRACE("(%04x, %04x, %04x, %04x, %04x)\n",
3879 hMenu, nPos, wFlags, hNewCheck, hNewUnCheck);
3880 if (!(item = MENU_FindItem( &hMenu, &nPos, wFlags ))) return FALSE;
3882 if (!hNewCheck && !hNewUnCheck)
3884 item->fState &= ~MF_USECHECKBITMAPS;
3886 else /* Install new bitmaps */
3888 item->hCheckBit = hNewCheck;
3889 item->hUnCheckBit = hNewUnCheck;
3890 item->fState |= MF_USECHECKBITMAPS;
3892 return TRUE;
3896 /**********************************************************************
3897 * CreateMenu16 (USER.151)
3899 HMENU16 WINAPI CreateMenu16(void)
3901 return CreateMenu();
3905 /**********************************************************************
3906 * CreateMenu (USER32.@)
3908 HMENU WINAPI CreateMenu(void)
3910 HMENU hMenu;
3911 LPPOPUPMENU menu;
3912 if (!(hMenu = USER_HEAP_ALLOC( sizeof(POPUPMENU) ))) return 0;
3913 menu = (LPPOPUPMENU) USER_HEAP_LIN_ADDR(hMenu);
3915 ZeroMemory(menu, sizeof(POPUPMENU));
3916 menu->wMagic = MENU_MAGIC;
3917 menu->FocusedItem = NO_SELECTED_ITEM;
3918 menu->bTimeToHide = FALSE;
3920 TRACE("return %04x\n", hMenu );
3922 return hMenu;
3926 /**********************************************************************
3927 * DestroyMenu16 (USER.152)
3929 BOOL16 WINAPI DestroyMenu16( HMENU16 hMenu )
3931 return DestroyMenu( hMenu );
3935 /**********************************************************************
3936 * DestroyMenu (USER32.@)
3938 BOOL WINAPI DestroyMenu( HMENU hMenu )
3940 TRACE("(%04x)\n", hMenu);
3942 /* Silently ignore attempts to destroy default system popup */
3944 if (hMenu && hMenu != MENU_DefSysPopup)
3946 LPPOPUPMENU lppop = (LPPOPUPMENU) USER_HEAP_LIN_ADDR(hMenu);
3947 WND *pTPWnd = MENU_GetTopPopupWnd();
3949 if( pTPWnd && (hMenu == *(HMENU*)pTPWnd->wExtra) )
3950 *(UINT*)pTPWnd->wExtra = 0;
3952 if (!IS_A_MENU(lppop)) lppop = NULL;
3953 if ( lppop )
3955 lppop->wMagic = 0; /* Mark it as destroyed */
3957 if ((lppop->wFlags & MF_POPUP) && lppop->hWnd &&
3958 (!pTPWnd || (lppop->hWnd != pTPWnd->hwndSelf)))
3959 DestroyWindow( lppop->hWnd );
3961 if (lppop->items) /* recursively destroy submenus */
3963 int i;
3964 MENUITEM *item = lppop->items;
3965 for (i = lppop->nItems; i > 0; i--, item++)
3967 if (item->fType & MF_POPUP) DestroyMenu(item->hSubMenu);
3968 MENU_FreeItemData( item );
3970 HeapFree( GetProcessHeap(), 0, lppop->items );
3972 USER_HEAP_FREE( hMenu );
3973 MENU_ReleaseTopPopupWnd();
3975 else
3977 MENU_ReleaseTopPopupWnd();
3978 return FALSE;
3981 return (hMenu != MENU_DefSysPopup);
3985 /**********************************************************************
3986 * GetSystemMenu16 (USER.156)
3988 HMENU16 WINAPI GetSystemMenu16( HWND16 hWnd, BOOL16 bRevert )
3990 return GetSystemMenu( hWnd, bRevert );
3994 /**********************************************************************
3995 * GetSystemMenu (USER32.@)
3997 HMENU WINAPI GetSystemMenu( HWND hWnd, BOOL bRevert )
3999 WND *wndPtr = WIN_FindWndPtr( hWnd );
4000 HMENU retvalue = 0;
4002 if (wndPtr)
4004 if( wndPtr->hSysMenu )
4006 if( bRevert )
4008 DestroyMenu(wndPtr->hSysMenu);
4009 wndPtr->hSysMenu = 0;
4011 else
4013 POPUPMENU *menu = MENU_GetMenu( wndPtr->hSysMenu );
4014 if( menu )
4016 if( menu->nItems > 0 && menu->items[0].hSubMenu == MENU_DefSysPopup )
4017 menu->items[0].hSubMenu = MENU_CopySysPopup();
4019 else
4021 WARN("Current sys-menu (%04x) of wnd %04x is broken\n",
4022 wndPtr->hSysMenu, hWnd);
4023 wndPtr->hSysMenu = 0;
4028 if(!wndPtr->hSysMenu && (wndPtr->dwStyle & WS_SYSMENU) )
4029 wndPtr->hSysMenu = MENU_GetSysMenu( hWnd, (HMENU)(-1) );
4031 if( wndPtr->hSysMenu )
4033 POPUPMENU *menu;
4034 retvalue = GetSubMenu16(wndPtr->hSysMenu, 0);
4036 /* Store the dummy sysmenu handle to facilitate the refresh */
4037 /* of the close button if the SC_CLOSE item change */
4038 menu = MENU_GetMenu(retvalue);
4039 if ( menu )
4040 menu->hSysMenuOwner = wndPtr->hSysMenu;
4042 WIN_ReleaseWndPtr(wndPtr);
4044 return bRevert ? 0 : retvalue;
4048 /*******************************************************************
4049 * SetSystemMenu16 (USER.280)
4051 BOOL16 WINAPI SetSystemMenu16( HWND16 hwnd, HMENU16 hMenu )
4053 return SetSystemMenu( hwnd, hMenu );
4057 /*******************************************************************
4058 * SetSystemMenu (USER32.@)
4060 BOOL WINAPI SetSystemMenu( HWND hwnd, HMENU hMenu )
4062 WND *wndPtr = WIN_FindWndPtr(hwnd);
4064 if (wndPtr)
4066 if (wndPtr->hSysMenu) DestroyMenu( wndPtr->hSysMenu );
4067 wndPtr->hSysMenu = MENU_GetSysMenu( hwnd, hMenu );
4068 WIN_ReleaseWndPtr(wndPtr);
4069 return TRUE;
4071 return FALSE;
4075 /**********************************************************************
4076 * GetMenu16 (USER.157)
4078 HMENU16 WINAPI GetMenu16( HWND16 hWnd )
4080 return (HMENU16)GetMenu(hWnd);
4084 /**********************************************************************
4085 * GetMenu (USER32.@)
4087 HMENU WINAPI GetMenu( HWND hWnd )
4089 HMENU retvalue;
4090 WND * wndPtr = WIN_FindWndPtr(hWnd);
4092 if (!wndPtr) return 0;
4094 retvalue = (HMENU)wndPtr->wIDmenu;
4095 TRACE("for %swindow %04x returning %04x\n",
4096 (wndPtr->dwStyle & WS_CHILD) ? "child " : "", hWnd, retvalue);
4097 WIN_ReleaseWndPtr(wndPtr);
4098 return retvalue;
4102 /**********************************************************************
4103 * SetMenu16 (USER.158)
4105 BOOL16 WINAPI SetMenu16( HWND16 hWnd, HMENU16 hMenu )
4107 return SetMenu( hWnd, hMenu );
4111 /**********************************************************************
4112 * SetMenu (USER32.@)
4114 BOOL WINAPI SetMenu( HWND hWnd, HMENU hMenu )
4116 WND * wndPtr = WIN_FindWndPtr(hWnd);
4117 BOOL res = FALSE;
4119 TRACE("(%04x, %04x);\n", hWnd, hMenu);
4121 if (hMenu && !IsMenu(hMenu))
4123 WARN("hMenu is not a menu handle\n");
4124 goto exit;
4127 if (wndPtr && !(wndPtr->dwStyle & WS_CHILD))
4129 if (GetCapture() == hWnd) ReleaseCapture();
4131 wndPtr->wIDmenu = (UINT)hMenu;
4132 if (hMenu != 0)
4134 LPPOPUPMENU lpmenu;
4136 if (!(lpmenu = MENU_GetMenu(hMenu)))
4137 goto exit;
4139 lpmenu->hWnd = hWnd;
4140 lpmenu->Height = 0; /* Make sure we recalculate the size */
4142 if (IsWindowVisible(hWnd))
4143 SetWindowPos( hWnd, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE |
4144 SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED );
4145 res = TRUE;
4147 exit:
4148 WIN_ReleaseWndPtr(wndPtr);
4149 return res;
4154 /**********************************************************************
4155 * GetSubMenu16 (USER.159)
4157 HMENU16 WINAPI GetSubMenu16( HMENU16 hMenu, INT16 nPos )
4159 return GetSubMenu( hMenu, nPos );
4163 /**********************************************************************
4164 * GetSubMenu (USER32.@)
4166 HMENU WINAPI GetSubMenu( HMENU hMenu, INT nPos )
4168 MENUITEM * lpmi;
4170 if (!(lpmi = MENU_FindItem(&hMenu,&nPos,MF_BYPOSITION))) return 0;
4171 if (!(lpmi->fType & MF_POPUP)) return 0;
4172 return lpmi->hSubMenu;
4176 /**********************************************************************
4177 * DrawMenuBar16 (USER.160)
4179 void WINAPI DrawMenuBar16( HWND16 hWnd )
4181 DrawMenuBar( hWnd );
4185 /**********************************************************************
4186 * DrawMenuBar (USER32.@)
4188 BOOL WINAPI DrawMenuBar( HWND hWnd )
4190 LPPOPUPMENU lppop;
4191 WND *wndPtr = WIN_FindWndPtr(hWnd);
4192 if (wndPtr && !(wndPtr->dwStyle & WS_CHILD) && wndPtr->wIDmenu)
4194 lppop = MENU_GetMenu((HMENU16)wndPtr->wIDmenu);
4195 if (lppop == NULL)
4197 WIN_ReleaseWndPtr(wndPtr);
4198 return FALSE;
4201 lppop->Height = 0; /* Make sure we call MENU_MenuBarCalcSize */
4202 lppop->hwndOwner = hWnd;
4203 SetWindowPos( hWnd, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE |
4204 SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED );
4205 WIN_ReleaseWndPtr(wndPtr);
4206 return TRUE;
4208 WIN_ReleaseWndPtr(wndPtr);
4209 return FALSE;
4212 /***********************************************************************
4213 * DrawMenuBarTemp (USER32.@)
4215 DWORD WINAPI DrawMenuBarTemp(DWORD p1, DWORD p2)
4217 FIXME("(%08lx %08lx): stub\n", p1, p2);
4218 return 0;
4221 /***********************************************************************
4222 * EndMenu (USER.187) (USER32.@)
4224 void WINAPI EndMenu(void)
4226 /* if we are in the menu code, and it is active */
4227 if (fEndMenu == FALSE && MENU_IsMenuActive())
4229 /* terminate the menu handling code */
4230 fEndMenu = TRUE;
4232 /* needs to be posted to wakeup the internal menu handler */
4233 /* which will now terminate the menu, in the event that */
4234 /* the main window was minimized, or lost focus, so we */
4235 /* don't end up with an orphaned menu */
4236 PostMessageA( pTopPopupWnd->hwndSelf, WM_CANCELMODE, 0, 0);
4241 /***********************************************************************
4242 * LookupMenuHandle (USER.217)
4244 HMENU16 WINAPI LookupMenuHandle16( HMENU16 hmenu, INT16 id )
4246 HMENU hmenu32 = hmenu;
4247 UINT id32 = id;
4248 if (!MENU_FindItem( &hmenu32, &id32, MF_BYCOMMAND )) return 0;
4249 else return hmenu32;
4253 /**********************************************************************
4254 * LoadMenu16 (USER.150)
4256 HMENU16 WINAPI LoadMenu16( HINSTANCE16 instance, LPCSTR name )
4258 HRSRC16 hRsrc;
4259 HGLOBAL16 handle;
4260 HMENU16 hMenu;
4262 TRACE("(%04x,%s)\n", instance, debugres_a(name) );
4264 if (HIWORD(name))
4266 if (name[0] == '#') name = (LPCSTR)atoi( name + 1 );
4269 if (!name) return 0;
4271 /* check for Win32 module */
4272 if (HIWORD(instance)) return LoadMenuA( instance, name );
4273 instance = GetExePtr( instance );
4275 if (!(hRsrc = FindResource16( instance, name, RT_MENUA ))) return 0;
4276 if (!(handle = LoadResource16( instance, hRsrc ))) return 0;
4277 hMenu = LoadMenuIndirect16(LockResource16(handle));
4278 FreeResource16( handle );
4279 return hMenu;
4283 /*****************************************************************
4284 * LoadMenuA (USER32.@)
4286 HMENU WINAPI LoadMenuA( HINSTANCE instance, LPCSTR name )
4288 HRSRC hrsrc = FindResourceA( instance, name, RT_MENUA );
4289 if (!hrsrc) return 0;
4290 return LoadMenuIndirectA( (LPCVOID)LoadResource( instance, hrsrc ));
4294 /*****************************************************************
4295 * LoadMenuW (USER32.@)
4297 HMENU WINAPI LoadMenuW( HINSTANCE instance, LPCWSTR name )
4299 HRSRC hrsrc = FindResourceW( instance, name, RT_MENUW );
4300 if (!hrsrc) return 0;
4301 return LoadMenuIndirectW( (LPCVOID)LoadResource( instance, hrsrc ));
4305 /**********************************************************************
4306 * LoadMenuIndirect16 (USER.220)
4308 HMENU16 WINAPI LoadMenuIndirect16( LPCVOID template )
4310 HMENU16 hMenu;
4311 WORD version, offset;
4312 LPCSTR p = (LPCSTR)template;
4314 TRACE("(%p)\n", template );
4315 version = GET_WORD(p);
4316 p += sizeof(WORD);
4317 if (version)
4319 WARN("version must be 0 for Win16\n" );
4320 return 0;
4322 offset = GET_WORD(p);
4323 p += sizeof(WORD) + offset;
4324 if (!(hMenu = CreateMenu())) return 0;
4325 if (!MENU_ParseResource( p, hMenu, FALSE ))
4327 DestroyMenu( hMenu );
4328 return 0;
4330 return hMenu;
4334 /**********************************************************************
4335 * LoadMenuIndirectA (USER32.@)
4337 HMENU WINAPI LoadMenuIndirectA( LPCVOID template )
4339 HMENU16 hMenu;
4340 WORD version, offset;
4341 LPCSTR p = (LPCSTR)template;
4343 TRACE("%p\n", template );
4344 version = GET_WORD(p);
4345 p += sizeof(WORD);
4346 switch (version)
4348 case 0:
4349 offset = GET_WORD(p);
4350 p += sizeof(WORD) + offset;
4351 if (!(hMenu = CreateMenu())) return 0;
4352 if (!MENU_ParseResource( p, hMenu, TRUE ))
4354 DestroyMenu( hMenu );
4355 return 0;
4357 return hMenu;
4358 case 1:
4359 offset = GET_WORD(p);
4360 p += sizeof(WORD) + offset;
4361 if (!(hMenu = CreateMenu())) return 0;
4362 if (!MENUEX_ParseResource( p, hMenu))
4364 DestroyMenu( hMenu );
4365 return 0;
4367 return hMenu;
4368 default:
4369 ERR("version %d not supported.\n", version);
4370 return 0;
4375 /**********************************************************************
4376 * LoadMenuIndirectW (USER32.@)
4378 HMENU WINAPI LoadMenuIndirectW( LPCVOID template )
4380 /* FIXME: is there anything different between A and W? */
4381 return LoadMenuIndirectA( template );
4385 /**********************************************************************
4386 * IsMenu16 (USER.358)
4388 BOOL16 WINAPI IsMenu16( HMENU16 hmenu )
4390 LPPOPUPMENU menu = (LPPOPUPMENU) USER_HEAP_LIN_ADDR(hmenu);
4391 return IS_A_MENU(menu);
4395 /**********************************************************************
4396 * IsMenu (USER32.@)
4398 BOOL WINAPI IsMenu(HMENU hmenu)
4400 LPPOPUPMENU menu = (LPPOPUPMENU) USER_HEAP_LIN_ADDR(hmenu);
4401 return IS_A_MENU(menu);
4404 /**********************************************************************
4405 * GetMenuItemInfo_common
4408 static BOOL GetMenuItemInfo_common ( HMENU hmenu, UINT item, BOOL bypos,
4409 LPMENUITEMINFOW lpmii, BOOL unicode)
4411 MENUITEM *menu = MENU_FindItem (&hmenu, &item, bypos? MF_BYPOSITION : 0);
4413 debug_print_menuitem("GetMenuItemInfo_common: ", menu, "");
4415 if (!menu)
4416 return FALSE;
4418 if (lpmii->fMask & MIIM_TYPE) {
4419 lpmii->fType = menu->fType;
4420 switch (MENU_ITEM_TYPE(menu->fType)) {
4421 case MF_STRING:
4422 break; /* will be done below */
4423 case MF_OWNERDRAW:
4424 case MF_BITMAP:
4425 lpmii->dwTypeData = menu->text;
4426 /* fall through */
4427 default:
4428 lpmii->cch = 0;
4432 /* copy the text string */
4433 if ((lpmii->fMask & (MIIM_TYPE|MIIM_STRING)) &&
4434 (MENU_ITEM_TYPE(menu->fType) == MF_STRING) && menu->text)
4436 int len;
4437 if (unicode)
4439 len = strlenW(menu->text);
4440 if(lpmii->dwTypeData && lpmii->cch)
4441 lstrcpynW(lpmii->dwTypeData, menu->text, lpmii->cch);
4443 else
4445 len = WideCharToMultiByte( CP_ACP, 0, menu->text, -1, NULL, 0, NULL, NULL );
4446 if(lpmii->dwTypeData && lpmii->cch)
4447 if (!WideCharToMultiByte( CP_ACP, 0, menu->text, -1,
4448 (LPSTR)lpmii->dwTypeData, lpmii->cch, NULL, NULL ))
4449 ((LPSTR)lpmii->dwTypeData)[lpmii->cch-1] = 0;
4451 /* if we've copied a substring we return its length */
4452 if(lpmii->dwTypeData && lpmii->cch)
4454 if (lpmii->cch <= len) lpmii->cch--;
4456 else /* return length of string */
4457 lpmii->cch = len;
4460 if (lpmii->fMask & MIIM_FTYPE)
4461 lpmii->fType = menu->fType;
4463 if (lpmii->fMask & MIIM_BITMAP)
4464 lpmii->hbmpItem = menu->hbmpItem;
4466 if (lpmii->fMask & MIIM_STATE)
4467 lpmii->fState = menu->fState;
4469 if (lpmii->fMask & MIIM_ID)
4470 lpmii->wID = menu->wID;
4472 if (lpmii->fMask & MIIM_SUBMENU)
4473 lpmii->hSubMenu = menu->hSubMenu;
4475 if (lpmii->fMask & MIIM_CHECKMARKS) {
4476 lpmii->hbmpChecked = menu->hCheckBit;
4477 lpmii->hbmpUnchecked = menu->hUnCheckBit;
4479 if (lpmii->fMask & MIIM_DATA)
4480 lpmii->dwItemData = menu->dwItemData;
4482 return TRUE;
4485 /**********************************************************************
4486 * GetMenuItemInfoA (USER32.@)
4488 BOOL WINAPI GetMenuItemInfoA( HMENU hmenu, UINT item, BOOL bypos,
4489 LPMENUITEMINFOA lpmii)
4491 return GetMenuItemInfo_common (hmenu, item, bypos,
4492 (LPMENUITEMINFOW)lpmii, FALSE);
4495 /**********************************************************************
4496 * GetMenuItemInfoW (USER32.@)
4498 BOOL WINAPI GetMenuItemInfoW( HMENU hmenu, UINT item, BOOL bypos,
4499 LPMENUITEMINFOW lpmii)
4501 return GetMenuItemInfo_common (hmenu, item, bypos,
4502 lpmii, TRUE);
4505 /**********************************************************************
4506 * SetMenuItemInfo_common
4509 static BOOL SetMenuItemInfo_common(MENUITEM * menu,
4510 const MENUITEMINFOW *lpmii,
4511 BOOL unicode)
4513 if (!menu) return FALSE;
4515 debug_print_menuitem("MENU_SetItemInfo_common from: ", menu, "");
4517 if (lpmii->fMask & MIIM_TYPE ) {
4518 /* Get rid of old string. */
4519 if ( IS_STRING_ITEM(menu->fType) && menu->text) {
4520 HeapFree(GetProcessHeap(), 0, menu->text);
4521 menu->text = NULL;
4524 /* make only MENU_ITEM_TYPE bits in menu->fType equal lpmii->fType */
4525 menu->fType &= ~MENU_ITEM_TYPE(menu->fType);
4526 menu->fType |= MENU_ITEM_TYPE(lpmii->fType);
4528 menu->text = lpmii->dwTypeData;
4530 if (IS_STRING_ITEM(menu->fType)) {
4531 if (menu->text) {
4532 if (unicode)
4533 menu->text = HEAP_strdupW(GetProcessHeap(), 0, lpmii->dwTypeData);
4534 else
4535 menu->text = HEAP_strdupAtoW(GetProcessHeap(), 0, (LPSTR)lpmii->dwTypeData);
4537 else
4538 menu->fType |= MF_SEPARATOR;
4542 if (lpmii->fMask & MIIM_FTYPE ) {
4543 /* free the string when the type is changing */
4544 if ( (!IS_STRING_ITEM(lpmii->fType)) && IS_STRING_ITEM(menu->fType) && menu->text) {
4545 HeapFree(GetProcessHeap(), 0, menu->text);
4546 menu->text = NULL;
4548 menu->fType &= ~MENU_ITEM_TYPE(menu->fType);
4549 menu->fType |= MENU_ITEM_TYPE(lpmii->fType);
4550 if ( IS_STRING_ITEM(menu->fType) && !menu->text )
4551 menu->fType |= MF_SEPARATOR;
4554 if (lpmii->fMask & MIIM_STRING ) {
4555 /* free the string when used */
4556 if ( IS_STRING_ITEM(menu->fType) && menu->text) {
4557 HeapFree(GetProcessHeap(), 0, menu->text);
4558 if (lpmii->dwTypeData) {
4559 if (unicode)
4560 menu->text = HEAP_strdupW(GetProcessHeap(), 0, lpmii->dwTypeData);
4561 else
4562 menu->text = HEAP_strdupAtoW(GetProcessHeap(), 0, (LPSTR) lpmii->dwTypeData);
4564 else
4565 menu->fType |= MF_SEPARATOR;
4569 if (lpmii->fMask & MIIM_STATE)
4571 /* fixme: MFS_DEFAULT do we have to reset the other menu items? */
4572 menu->fState = lpmii->fState;
4575 if (lpmii->fMask & MIIM_ID)
4576 menu->wID = lpmii->wID;
4578 if (lpmii->fMask & MIIM_SUBMENU) {
4579 menu->hSubMenu = lpmii->hSubMenu;
4580 if (menu->hSubMenu) {
4581 POPUPMENU *subMenu = MENU_GetMenu((UINT16)menu->hSubMenu);
4582 if (subMenu) {
4583 subMenu->wFlags |= MF_POPUP;
4584 menu->fType |= MF_POPUP;
4586 else
4587 /* FIXME: Return an error ? */
4588 menu->fType &= ~MF_POPUP;
4590 else
4591 menu->fType &= ~MF_POPUP;
4594 if (lpmii->fMask & MIIM_CHECKMARKS)
4596 if (lpmii->fType & MFT_RADIOCHECK)
4597 menu->fType |= MFT_RADIOCHECK;
4599 menu->hCheckBit = lpmii->hbmpChecked;
4600 menu->hUnCheckBit = lpmii->hbmpUnchecked;
4602 if (lpmii->fMask & MIIM_DATA)
4603 menu->dwItemData = lpmii->dwItemData;
4605 debug_print_menuitem("SetMenuItemInfo_common to : ", menu, "");
4606 return TRUE;
4609 /**********************************************************************
4610 * SetMenuItemInfoA (USER32.@)
4612 BOOL WINAPI SetMenuItemInfoA(HMENU hmenu, UINT item, BOOL bypos,
4613 const MENUITEMINFOA *lpmii)
4615 return SetMenuItemInfo_common(MENU_FindItem(&hmenu, &item, bypos? MF_BYPOSITION : 0),
4616 (const MENUITEMINFOW *)lpmii, FALSE);
4619 /**********************************************************************
4620 * SetMenuItemInfoW (USER32.@)
4622 BOOL WINAPI SetMenuItemInfoW(HMENU hmenu, UINT item, BOOL bypos,
4623 const MENUITEMINFOW *lpmii)
4625 return SetMenuItemInfo_common(MENU_FindItem(&hmenu, &item, bypos? MF_BYPOSITION : 0),
4626 lpmii, TRUE);
4629 /**********************************************************************
4630 * SetMenuDefaultItem (USER32.@)
4633 BOOL WINAPI SetMenuDefaultItem(HMENU hmenu, UINT uItem, UINT bypos)
4635 UINT i;
4636 POPUPMENU *menu;
4637 MENUITEM *item;
4639 TRACE("(0x%x,%d,%d)\n", hmenu, uItem, bypos);
4641 if (!(menu = MENU_GetMenu(hmenu))) return FALSE;
4643 /* reset all default-item flags */
4644 item = menu->items;
4645 for (i = 0; i < menu->nItems; i++, item++)
4647 item->fState &= ~MFS_DEFAULT;
4650 /* no default item */
4651 if ( -1 == uItem)
4653 return TRUE;
4656 item = menu->items;
4657 if ( bypos )
4659 if ( uItem >= menu->nItems ) return FALSE;
4660 item[uItem].fState |= MFS_DEFAULT;
4661 return TRUE;
4663 else
4665 for (i = 0; i < menu->nItems; i++, item++)
4667 if (item->wID == uItem)
4669 item->fState |= MFS_DEFAULT;
4670 return TRUE;
4675 return FALSE;
4678 /**********************************************************************
4679 * GetMenuDefaultItem (USER32.@)
4681 UINT WINAPI GetMenuDefaultItem(HMENU hmenu, UINT bypos, UINT flags)
4683 POPUPMENU *menu;
4684 MENUITEM * item;
4685 UINT i = 0;
4687 TRACE("(0x%x,%d,%d)\n", hmenu, bypos, flags);
4689 if (!(menu = MENU_GetMenu(hmenu))) return -1;
4691 /* find default item */
4692 item = menu->items;
4694 /* empty menu */
4695 if (! item) return -1;
4697 while ( !( item->fState & MFS_DEFAULT ) )
4699 i++; item++;
4700 if (i >= menu->nItems ) return -1;
4703 /* default: don't return disabled items */
4704 if ( (!(GMDI_USEDISABLED & flags)) && (item->fState & MFS_DISABLED )) return -1;
4706 /* search rekursiv when needed */
4707 if ( (item->fType & MF_POPUP) && (flags & GMDI_GOINTOPOPUPS) )
4709 UINT ret;
4710 ret = GetMenuDefaultItem( item->hSubMenu, bypos, flags );
4711 if ( -1 != ret ) return ret;
4713 /* when item not found in submenu, return the popup item */
4715 return ( bypos ) ? i : item->wID;
4719 /*******************************************************************
4720 * InsertMenuItem16 (USER.441)
4722 * FIXME: untested
4724 BOOL16 WINAPI InsertMenuItem16( HMENU16 hmenu, UINT16 pos, BOOL16 byposition,
4725 const MENUITEMINFO16 *mii )
4727 MENUITEMINFOA miia;
4729 miia.cbSize = sizeof(miia);
4730 miia.fMask = mii->fMask;
4731 miia.dwTypeData = (LPSTR)mii->dwTypeData;
4732 miia.fType = mii->fType;
4733 miia.fState = mii->fState;
4734 miia.wID = mii->wID;
4735 miia.hSubMenu = mii->hSubMenu;
4736 miia.hbmpChecked = mii->hbmpChecked;
4737 miia.hbmpUnchecked = mii->hbmpUnchecked;
4738 miia.dwItemData = mii->dwItemData;
4739 miia.cch = mii->cch;
4740 if (IS_STRING_ITEM(miia.fType))
4741 miia.dwTypeData = MapSL(mii->dwTypeData);
4742 return InsertMenuItemA( hmenu, pos, byposition, &miia );
4746 /**********************************************************************
4747 * InsertMenuItemA (USER32.@)
4749 BOOL WINAPI InsertMenuItemA(HMENU hMenu, UINT uItem, BOOL bypos,
4750 const MENUITEMINFOA *lpmii)
4752 MENUITEM *item = MENU_InsertItem(hMenu, uItem, bypos ? MF_BYPOSITION : 0 );
4753 return SetMenuItemInfo_common(item, (const MENUITEMINFOW *)lpmii, FALSE);
4757 /**********************************************************************
4758 * InsertMenuItemW (USER32.@)
4760 BOOL WINAPI InsertMenuItemW(HMENU hMenu, UINT uItem, BOOL bypos,
4761 const MENUITEMINFOW *lpmii)
4763 MENUITEM *item = MENU_InsertItem(hMenu, uItem, bypos ? MF_BYPOSITION : 0 );
4764 return SetMenuItemInfo_common(item, lpmii, TRUE);
4767 /**********************************************************************
4768 * CheckMenuRadioItem (USER32.@)
4771 BOOL WINAPI CheckMenuRadioItem(HMENU hMenu,
4772 UINT first, UINT last, UINT check,
4773 UINT bypos)
4775 MENUITEM *mifirst, *milast, *micheck;
4776 HMENU mfirst = hMenu, mlast = hMenu, mcheck = hMenu;
4778 TRACE("ox%x: %d-%d, check %d, bypos=%d\n",
4779 hMenu, first, last, check, bypos);
4781 mifirst = MENU_FindItem (&mfirst, &first, bypos);
4782 milast = MENU_FindItem (&mlast, &last, bypos);
4783 micheck = MENU_FindItem (&mcheck, &check, bypos);
4785 if (mifirst == NULL || milast == NULL || micheck == NULL ||
4786 mifirst > milast || mfirst != mlast || mfirst != mcheck ||
4787 micheck > milast || micheck < mifirst)
4788 return FALSE;
4790 while (mifirst <= milast)
4792 if (mifirst == micheck)
4794 mifirst->fType |= MFT_RADIOCHECK;
4795 mifirst->fState |= MFS_CHECKED;
4796 } else {
4797 mifirst->fType &= ~MFT_RADIOCHECK;
4798 mifirst->fState &= ~MFS_CHECKED;
4800 mifirst++;
4803 return TRUE;
4806 /**********************************************************************
4807 * CheckMenuRadioItem16 (not a Windows API)
4810 BOOL16 WINAPI CheckMenuRadioItem16(HMENU16 hMenu,
4811 UINT16 first, UINT16 last, UINT16 check,
4812 BOOL16 bypos)
4814 return CheckMenuRadioItem (hMenu, first, last, check, bypos);
4817 /**********************************************************************
4818 * GetMenuItemRect (USER32.@)
4820 * ATTENTION: Here, the returned values in rect are the screen
4821 * coordinates of the item just like if the menu was
4822 * always on the upper left side of the application.
4825 BOOL WINAPI GetMenuItemRect (HWND hwnd, HMENU hMenu, UINT uItem,
4826 LPRECT rect)
4828 POPUPMENU *itemMenu;
4829 MENUITEM *item;
4830 HWND referenceHwnd;
4832 TRACE("(0x%x,0x%x,%d,%p)\n", hwnd, hMenu, uItem, rect);
4834 item = MENU_FindItem (&hMenu, &uItem, MF_BYPOSITION);
4835 referenceHwnd = hwnd;
4837 if(!hwnd)
4839 itemMenu = MENU_GetMenu(hMenu);
4840 if (itemMenu == NULL)
4841 return FALSE;
4843 if(itemMenu->hWnd == 0)
4844 return FALSE;
4845 referenceHwnd = itemMenu->hWnd;
4848 if ((rect == NULL) || (item == NULL))
4849 return FALSE;
4851 *rect = item->rect;
4853 MapWindowPoints(referenceHwnd, 0, (LPPOINT)rect, 2);
4855 return TRUE;
4858 /**********************************************************************
4859 * GetMenuItemRect16 (USER.665)
4862 BOOL16 WINAPI GetMenuItemRect16 (HWND16 hwnd, HMENU16 hMenu, UINT16 uItem,
4863 LPRECT16 rect)
4865 RECT r32;
4866 BOOL res;
4868 if (!rect) return FALSE;
4869 res = GetMenuItemRect (hwnd, hMenu, uItem, &r32);
4870 CONV_RECT32TO16 (&r32, rect);
4871 return res;
4874 /**********************************************************************
4875 * SetMenuInfo (USER32.@)
4877 * FIXME
4878 * MIM_APPLYTOSUBMENUS
4879 * actually use the items to draw the menu
4881 BOOL WINAPI SetMenuInfo (HMENU hMenu, LPCMENUINFO lpmi)
4883 POPUPMENU *menu;
4885 TRACE("(0x%04x %p)\n", hMenu, lpmi);
4887 if (lpmi && (lpmi->cbSize==sizeof(MENUINFO)) && (menu = MENU_GetMenu(hMenu)))
4890 if (lpmi->fMask & MIM_BACKGROUND)
4891 menu->hbrBack = lpmi->hbrBack;
4893 if (lpmi->fMask & MIM_HELPID)
4894 menu->dwContextHelpID = lpmi->dwContextHelpID;
4896 if (lpmi->fMask & MIM_MAXHEIGHT)
4897 menu->cyMax = lpmi->cyMax;
4899 if (lpmi->fMask & MIM_MENUDATA)
4900 menu->dwMenuData = lpmi->dwMenuData;
4902 if (lpmi->fMask & MIM_STYLE)
4903 menu->dwStyle = lpmi->dwStyle;
4905 return TRUE;
4907 return FALSE;
4910 /**********************************************************************
4911 * GetMenuInfo (USER32.@)
4913 * NOTES
4914 * win98/NT5.0
4917 BOOL WINAPI GetMenuInfo (HMENU hMenu, LPMENUINFO lpmi)
4918 { POPUPMENU *menu;
4920 TRACE("(0x%04x %p)\n", hMenu, lpmi);
4922 if (lpmi && (menu = MENU_GetMenu(hMenu)))
4925 if (lpmi->fMask & MIM_BACKGROUND)
4926 lpmi->hbrBack = menu->hbrBack;
4928 if (lpmi->fMask & MIM_HELPID)
4929 lpmi->dwContextHelpID = menu->dwContextHelpID;
4931 if (lpmi->fMask & MIM_MAXHEIGHT)
4932 lpmi->cyMax = menu->cyMax;
4934 if (lpmi->fMask & MIM_MENUDATA)
4935 lpmi->dwMenuData = menu->dwMenuData;
4937 if (lpmi->fMask & MIM_STYLE)
4938 lpmi->dwStyle = menu->dwStyle;
4940 return TRUE;
4942 return FALSE;
4945 /**********************************************************************
4946 * SetMenuContextHelpId16 (USER.384)
4948 BOOL16 WINAPI SetMenuContextHelpId16( HMENU16 hMenu, DWORD dwContextHelpID)
4950 return SetMenuContextHelpId( hMenu, dwContextHelpID );
4954 /**********************************************************************
4955 * SetMenuContextHelpId (USER32.@)
4957 BOOL WINAPI SetMenuContextHelpId( HMENU hMenu, DWORD dwContextHelpID)
4959 LPPOPUPMENU menu;
4961 TRACE("(0x%04x 0x%08lx)\n", hMenu, dwContextHelpID);
4963 if ((menu = MENU_GetMenu(hMenu)))
4965 menu->dwContextHelpID = dwContextHelpID;
4966 return TRUE;
4968 return FALSE;
4971 /**********************************************************************
4972 * GetMenuContextHelpId16 (USER.385)
4974 DWORD WINAPI GetMenuContextHelpId16( HMENU16 hMenu )
4976 return GetMenuContextHelpId( hMenu );
4979 /**********************************************************************
4980 * GetMenuContextHelpId (USER32.@)
4982 DWORD WINAPI GetMenuContextHelpId( HMENU hMenu )
4984 LPPOPUPMENU menu;
4986 TRACE("(0x%04x)\n", hMenu);
4988 if ((menu = MENU_GetMenu(hMenu)))
4990 return menu->dwContextHelpID;
4992 return 0;
4995 /**********************************************************************
4996 * MenuItemFromPoint (USER32.@)
4998 UINT WINAPI MenuItemFromPoint(HWND hWnd, HMENU hMenu, POINT ptScreen)
5000 FIXME("(0x%04x,0x%04x,(%ld,%ld)):stub\n",
5001 hWnd, hMenu, ptScreen.x, ptScreen.y);
5002 return 0;
5006 /**********************************************************************
5007 * translate_accelerator
5009 static BOOL translate_accelerator( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam,
5010 BYTE fVirt, WORD key, WORD cmd )
5012 UINT mesg = 0;
5014 if (wParam != key) return FALSE;
5016 if (message == WM_CHAR)
5018 if ( !(fVirt & FALT) && !(fVirt & FVIRTKEY) )
5020 TRACE_(accel)("found accel for WM_CHAR: ('%c')\n", wParam & 0xff);
5021 goto found;
5024 else
5026 if(fVirt & FVIRTKEY)
5028 INT mask = 0;
5029 TRACE_(accel)("found accel for virt_key %04x (scan %04x)\n",
5030 wParam, 0xff & HIWORD(lParam));
5031 if(GetKeyState(VK_SHIFT) & 0x8000) mask |= FSHIFT;
5032 if(GetKeyState(VK_CONTROL) & 0x8000) mask |= FCONTROL;
5033 if(GetKeyState(VK_MENU) & 0x8000) mask |= FALT;
5034 if(mask == (fVirt & (FSHIFT | FCONTROL | FALT))) goto found;
5035 TRACE_(accel)(", but incorrect SHIFT/CTRL/ALT-state\n");
5037 else
5039 if (!(lParam & 0x01000000)) /* no special_key */
5041 if ((fVirt & FALT) && (lParam & 0x20000000))
5042 { /* ^^ ALT pressed */
5043 TRACE_(accel)("found accel for Alt-%c\n", wParam & 0xff);
5044 goto found;
5049 return FALSE;
5051 found:
5052 if (message == WM_KEYUP || message == WM_SYSKEYUP)
5053 mesg = 1;
5054 else if (GetCapture())
5055 mesg = 2;
5056 else if (!IsWindowEnabled(hWnd))
5057 mesg = 3;
5058 else
5060 HMENU hMenu, hSubMenu, hSysMenu;
5061 UINT uSysStat = (UINT)-1, uStat = (UINT)-1, nPos;
5062 WND* wndPtr = WIN_FindWndPtr(hWnd);
5064 hMenu = (wndPtr->dwStyle & WS_CHILD) ? 0 : (HMENU)wndPtr->wIDmenu;
5065 hSysMenu = wndPtr->hSysMenu;
5066 WIN_ReleaseWndPtr(wndPtr);
5068 /* find menu item and ask application to initialize it */
5069 /* 1. in the system menu */
5070 hSubMenu = hSysMenu;
5071 nPos = cmd;
5072 if(MENU_FindItem(&hSubMenu, &nPos, MF_BYCOMMAND))
5074 SendMessageA(hWnd, WM_INITMENU, (WPARAM)hSysMenu, 0L);
5075 if(hSubMenu != hSysMenu)
5077 nPos = MENU_FindSubMenu(&hSysMenu, hSubMenu);
5078 TRACE_(accel)("hSysMenu = %04x, hSubMenu = %04x, nPos = %d\n", hSysMenu, hSubMenu, nPos);
5079 SendMessageA(hWnd, WM_INITMENUPOPUP, (WPARAM)hSubMenu, MAKELPARAM(nPos, TRUE));
5081 uSysStat = GetMenuState(GetSubMenu(hSysMenu, 0), cmd, MF_BYCOMMAND);
5083 else /* 2. in the window's menu */
5085 hSubMenu = hMenu;
5086 nPos = cmd;
5087 if(MENU_FindItem(&hSubMenu, &nPos, MF_BYCOMMAND))
5089 SendMessageA(hWnd, WM_INITMENU, (WPARAM)hMenu, 0L);
5090 if(hSubMenu != hMenu)
5092 nPos = MENU_FindSubMenu(&hMenu, hSubMenu);
5093 TRACE_(accel)("hMenu = %04x, hSubMenu = %04x, nPos = %d\n", hMenu, hSubMenu, nPos);
5094 SendMessageA(hWnd, WM_INITMENUPOPUP, (WPARAM)hSubMenu, MAKELPARAM(nPos, FALSE));
5096 uStat = GetMenuState(hMenu, cmd, MF_BYCOMMAND);
5100 if (uSysStat != (UINT)-1)
5102 if (uSysStat & (MF_DISABLED|MF_GRAYED))
5103 mesg=4;
5104 else
5105 mesg=WM_SYSCOMMAND;
5107 else
5109 if (uStat != (UINT)-1)
5111 if (IsIconic(hWnd))
5112 mesg=5;
5113 else
5115 if (uStat & (MF_DISABLED|MF_GRAYED))
5116 mesg=6;
5117 else
5118 mesg=WM_COMMAND;
5121 else
5122 mesg=WM_COMMAND;
5126 if( mesg==WM_COMMAND )
5128 TRACE_(accel)(", sending WM_COMMAND, wParam=%0x\n", 0x10000 | cmd);
5129 SendMessageA(hWnd, mesg, 0x10000 | cmd, 0L);
5131 else if( mesg==WM_SYSCOMMAND )
5133 TRACE_(accel)(", sending WM_SYSCOMMAND, wParam=%0x\n", cmd);
5134 SendMessageA(hWnd, mesg, cmd, 0x00010000L);
5136 else
5138 /* some reasons for NOT sending the WM_{SYS}COMMAND message:
5139 * #0: unknown (please report!)
5140 * #1: for WM_KEYUP,WM_SYSKEYUP
5141 * #2: mouse is captured
5142 * #3: window is disabled
5143 * #4: it's a disabled system menu option
5144 * #5: it's a menu option, but window is iconic
5145 * #6: it's a menu option, but disabled
5147 TRACE_(accel)(", but won't send WM_{SYS}COMMAND, reason is #%d\n",mesg);
5148 if(mesg==0)
5149 ERR_(accel)(" unknown reason - please report!");
5151 return TRUE;
5154 /**********************************************************************
5155 * TranslateAccelerator (USER32.@)
5157 INT WINAPI TranslateAccelerator( HWND hWnd, HACCEL hAccel, LPMSG msg )
5159 /* YES, Accel16! */
5160 LPACCEL16 lpAccelTbl;
5161 int i;
5163 if (msg == NULL)
5165 WARN_(accel)("msg null; should hang here to be win compatible\n");
5166 return 0;
5168 if (!hAccel || !(lpAccelTbl = (LPACCEL16) LockResource16(hAccel)))
5170 WARN_(accel)("invalid accel handle=%x\n", hAccel);
5171 return 0;
5173 if ((msg->message != WM_KEYDOWN &&
5174 msg->message != WM_KEYUP &&
5175 msg->message != WM_SYSKEYDOWN &&
5176 msg->message != WM_SYSKEYUP &&
5177 msg->message != WM_CHAR)) return 0;
5179 TRACE_(accel)("TranslateAccelerators hAccel=%04x, hWnd=%04x,"
5180 "msg->hwnd=%04x, msg->message=%04x, wParam=%08x, lParam=%lx\n",
5181 hAccel,hWnd,msg->hwnd,msg->message,msg->wParam,msg->lParam);
5183 i = 0;
5186 if (translate_accelerator( hWnd, msg->message, msg->wParam, msg->lParam,
5187 lpAccelTbl[i].fVirt, lpAccelTbl[i].key, lpAccelTbl[i].cmd))
5188 return 1;
5189 } while ((lpAccelTbl[i++].fVirt & 0x80) == 0);
5190 WARN_(accel)("couldn't translate accelerator key\n");
5191 return 0;
5195 /**********************************************************************
5196 * TranslateAccelerator16 (USER.178)
5198 INT16 WINAPI TranslateAccelerator16( HWND16 hWnd, HACCEL16 hAccel, LPMSG16 msg )
5200 LPACCEL16 lpAccelTbl;
5201 int i;
5203 if (msg == NULL)
5205 WARN_(accel)("msg null; should hang here to be win compatible\n");
5206 return 0;
5208 if (!hAccel || !(lpAccelTbl = (LPACCEL16) LockResource16(hAccel)))
5210 WARN_(accel)("invalid accel handle=%x\n", hAccel);
5211 return 0;
5213 if ((msg->message != WM_KEYDOWN &&
5214 msg->message != WM_KEYUP &&
5215 msg->message != WM_SYSKEYDOWN &&
5216 msg->message != WM_SYSKEYUP &&
5217 msg->message != WM_CHAR)) return 0;
5219 TRACE_(accel)("TranslateAccelerators hAccel=%04x, hWnd=%04x,"
5220 "msg->hwnd=%04x, msg->message=%04x, wParam=%04x, lParam=%lx\n",
5221 hAccel,hWnd,msg->hwnd,msg->message,msg->wParam,msg->lParam);
5223 i = 0;
5226 if (translate_accelerator( hWnd, msg->message, msg->wParam, msg->lParam,
5227 lpAccelTbl[i].fVirt, lpAccelTbl[i].key, lpAccelTbl[i].cmd ))
5228 return 1;
5229 } while ((lpAccelTbl[i++].fVirt & 0x80) == 0);
5230 WARN_(accel)("couldn't translate accelerator key\n");
5231 return 0;