Don't allow comctl32 controls to access their infoPtr before it has
[wine.git] / controls / menu.c
blob70269547849dce0a93c53d8c34b3f29e207631e3
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( SystemHeap, 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 /* If we get here, then it must be a text item */
869 if (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 = (TDB*)GlobalLock16( 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, "");
1856 if (IS_STRING_ITEM(flags))
1858 if (!str || !*str)
1860 flags |= MF_SEPARATOR;
1861 item->text = NULL;
1863 else
1865 LPWSTR text;
1866 /* Item beginning with a backspace is a help item */
1867 if (*str == '\b')
1869 flags |= MF_HELP;
1870 str++;
1872 if (!(text = HEAP_strdupW( SystemHeap, 0, str ))) return FALSE;
1873 item->text = text;
1876 else if (IS_BITMAP_ITEM(flags))
1877 item->text = (LPWSTR)(HBITMAP)LOWORD(str);
1878 else item->text = NULL;
1880 if (flags & MF_OWNERDRAW)
1881 item->dwItemData = (DWORD)str;
1882 else
1883 item->dwItemData = 0;
1885 if ((item->fType & MF_POPUP) && (flags & MF_POPUP) && (item->hSubMenu != id) )
1886 DestroyMenu( item->hSubMenu ); /* ModifyMenu() spec */
1888 if (flags & MF_POPUP)
1890 POPUPMENU *menu = MENU_GetMenu((UINT16)id);
1891 if (menu) menu->wFlags |= MF_POPUP;
1892 else
1894 item->wID = 0;
1895 item->hSubMenu = 0;
1896 item->fType = 0;
1897 item->fState = 0;
1898 return FALSE;
1902 item->wID = id;
1903 if (flags & MF_POPUP)
1904 item->hSubMenu = id;
1906 if ((item->fType & MF_POPUP) && !(flags & MF_POPUP) )
1907 flags |= MF_POPUP; /* keep popup */
1909 item->fType = flags & TYPE_MASK;
1910 item->fState = (flags & STATE_MASK) &
1911 ~(MF_HILITE | MF_MOUSESELECT | MF_BYPOSITION);
1914 /* Don't call SetRectEmpty here! */
1917 if (prevText) HeapFree( SystemHeap, 0, prevText );
1919 debug_print_menuitem("MENU_SetItemData to : ", item, "");
1920 return TRUE;
1924 /**********************************************************************
1925 * MENU_InsertItem
1927 * Insert a new item into a menu.
1929 static MENUITEM *MENU_InsertItem( HMENU hMenu, UINT pos, UINT flags )
1931 MENUITEM *newItems;
1932 POPUPMENU *menu;
1934 if (!(menu = MENU_GetMenu(hMenu)))
1935 return NULL;
1937 /* Find where to insert new item */
1939 if (flags & MF_BYPOSITION) {
1940 if (pos > menu->nItems)
1941 pos = menu->nItems;
1942 } else {
1943 if (!MENU_FindItem( &hMenu, &pos, flags ))
1944 pos = menu->nItems;
1945 else {
1946 if (!(menu = MENU_GetMenu( hMenu )))
1947 return NULL;
1951 /* Create new items array */
1953 newItems = HeapAlloc( SystemHeap, 0, sizeof(MENUITEM) * (menu->nItems+1) );
1954 if (!newItems)
1956 WARN("allocation failed\n" );
1957 return NULL;
1959 if (menu->nItems > 0)
1961 /* Copy the old array into the new one */
1962 if (pos > 0) memcpy( newItems, menu->items, pos * sizeof(MENUITEM) );
1963 if (pos < menu->nItems) memcpy( &newItems[pos+1], &menu->items[pos],
1964 (menu->nItems-pos)*sizeof(MENUITEM) );
1965 HeapFree( SystemHeap, 0, menu->items );
1967 menu->items = newItems;
1968 menu->nItems++;
1969 memset( &newItems[pos], 0, sizeof(*newItems) );
1970 menu->Height = 0; /* force size recalculate */
1971 return &newItems[pos];
1975 /**********************************************************************
1976 * MENU_ParseResource
1978 * Parse a standard menu resource and add items to the menu.
1979 * Return a pointer to the end of the resource.
1981 static LPCSTR MENU_ParseResource( LPCSTR res, HMENU hMenu, BOOL unicode )
1983 WORD flags, id = 0;
1984 LPCSTR str;
1988 flags = GET_WORD(res);
1989 res += sizeof(WORD);
1990 if (!(flags & MF_POPUP))
1992 id = GET_WORD(res);
1993 res += sizeof(WORD);
1995 if (!IS_STRING_ITEM(flags))
1996 ERR("not a string item %04x\n", flags );
1997 str = res;
1998 if (!unicode) res += strlen(str) + 1;
1999 else res += (strlenW((LPCWSTR)str) + 1) * sizeof(WCHAR);
2000 if (flags & MF_POPUP)
2002 HMENU hSubMenu = CreatePopupMenu();
2003 if (!hSubMenu) return NULL;
2004 if (!(res = MENU_ParseResource( res, hSubMenu, unicode )))
2005 return NULL;
2006 if (!unicode) AppendMenuA( hMenu, flags, (UINT)hSubMenu, str );
2007 else AppendMenuW( hMenu, flags, (UINT)hSubMenu, (LPCWSTR)str );
2009 else /* Not a popup */
2011 if (!unicode) AppendMenuA( hMenu, flags, id, *str ? str : NULL );
2012 else AppendMenuW( hMenu, flags, id,
2013 *(LPCWSTR)str ? (LPCWSTR)str : NULL );
2015 } while (!(flags & MF_END));
2016 return res;
2020 /**********************************************************************
2021 * MENUEX_ParseResource
2023 * Parse an extended menu resource and add items to the menu.
2024 * Return a pointer to the end of the resource.
2026 static LPCSTR MENUEX_ParseResource( LPCSTR res, HMENU hMenu)
2028 WORD resinfo;
2029 do {
2030 MENUITEMINFOW mii;
2032 mii.cbSize = sizeof(mii);
2033 mii.fMask = MIIM_STATE | MIIM_ID | MIIM_TYPE;
2034 mii.fType = GET_DWORD(res);
2035 res += sizeof(DWORD);
2036 mii.fState = GET_DWORD(res);
2037 res += sizeof(DWORD);
2038 mii.wID = GET_DWORD(res);
2039 res += sizeof(DWORD);
2040 resinfo = GET_WORD(res); /* FIXME: for 16-bit apps this is a byte. */
2041 res += sizeof(WORD);
2042 /* Align the text on a word boundary. */
2043 res += (~((int)res - 1)) & 1;
2044 mii.dwTypeData = (LPWSTR) res;
2045 res += (1 + strlenW(mii.dwTypeData)) * sizeof(WCHAR);
2046 /* Align the following fields on a dword boundary. */
2047 res += (~((int)res - 1)) & 3;
2049 /* FIXME: This is inefficient and cannot be optimised away by gcc. */
2051 LPSTR newstr = HEAP_strdupWtoA(GetProcessHeap(),
2052 0, mii.dwTypeData);
2053 TRACE("Menu item: [%08x,%08x,%04x,%04x,%s]\n",
2054 mii.fType, mii.fState, mii.wID, resinfo, newstr);
2055 HeapFree( GetProcessHeap(), 0, newstr );
2058 if (resinfo & 1) { /* Pop-up? */
2059 /* DWORD helpid = GET_DWORD(res); FIXME: use this. */
2060 res += sizeof(DWORD);
2061 mii.hSubMenu = CreatePopupMenu();
2062 if (!mii.hSubMenu)
2063 return NULL;
2064 if (!(res = MENUEX_ParseResource(res, mii.hSubMenu))) {
2065 DestroyMenu(mii.hSubMenu);
2066 return NULL;
2068 mii.fMask |= MIIM_SUBMENU;
2069 mii.fType |= MF_POPUP;
2071 InsertMenuItemW(hMenu, -1, MF_BYPOSITION, &mii);
2072 } while (!(resinfo & MF_END));
2073 return res;
2077 /***********************************************************************
2078 * MENU_GetSubPopup
2080 * Return the handle of the selected sub-popup menu (if any).
2082 static HMENU MENU_GetSubPopup( HMENU hmenu )
2084 POPUPMENU *menu;
2085 MENUITEM *item;
2087 menu = MENU_GetMenu( hmenu );
2089 if ((!menu) || (menu->FocusedItem == NO_SELECTED_ITEM)) return 0;
2091 item = &menu->items[menu->FocusedItem];
2092 if ((item->fType & MF_POPUP) && (item->fState & MF_MOUSESELECT))
2093 return item->hSubMenu;
2094 return 0;
2098 /***********************************************************************
2099 * MENU_HideSubPopups
2101 * Hide the sub-popup menus of this menu.
2103 static void MENU_HideSubPopups( HWND hwndOwner, HMENU hmenu,
2104 BOOL sendMenuSelect )
2106 POPUPMENU *menu = MENU_GetMenu( hmenu );
2108 TRACE("owner=0x%04x hmenu=0x%04x 0x%04x\n", hwndOwner, hmenu, sendMenuSelect);
2110 if (menu && uSubPWndLevel)
2112 HMENU hsubmenu;
2113 POPUPMENU *submenu;
2114 MENUITEM *item;
2116 if (menu->FocusedItem != NO_SELECTED_ITEM)
2118 item = &menu->items[menu->FocusedItem];
2119 if (!(item->fType & MF_POPUP) ||
2120 !(item->fState & MF_MOUSESELECT)) return;
2121 item->fState &= ~MF_MOUSESELECT;
2122 hsubmenu = item->hSubMenu;
2123 } else return;
2125 submenu = MENU_GetMenu( hsubmenu );
2126 MENU_HideSubPopups( hwndOwner, hsubmenu, FALSE );
2127 MENU_SelectItem( hwndOwner, hsubmenu, NO_SELECTED_ITEM, sendMenuSelect, 0 );
2129 if (submenu->hWnd == MENU_GetTopPopupWnd()->hwndSelf )
2131 ShowWindow( submenu->hWnd, SW_HIDE );
2132 uSubPWndLevel = 0;
2134 else
2136 DestroyWindow( submenu->hWnd );
2137 submenu->hWnd = 0;
2139 MENU_ReleaseTopPopupWnd();
2144 /***********************************************************************
2145 * MENU_ShowSubPopup
2147 * Display the sub-menu of the selected item of this menu.
2148 * Return the handle of the submenu, or hmenu if no submenu to display.
2150 static HMENU MENU_ShowSubPopup( HWND hwndOwner, HMENU hmenu,
2151 BOOL selectFirst, UINT wFlags )
2153 RECT rect;
2154 POPUPMENU *menu;
2155 MENUITEM *item;
2156 WND *wndPtr;
2157 HDC hdc;
2159 TRACE("owner=0x%04x hmenu=0x%04x 0x%04x\n", hwndOwner, hmenu, selectFirst);
2161 if (!(menu = MENU_GetMenu( hmenu ))) return hmenu;
2163 if (!(wndPtr = WIN_FindWndPtr( menu->hWnd )) ||
2164 (menu->FocusedItem == NO_SELECTED_ITEM))
2166 WIN_ReleaseWndPtr(wndPtr);
2167 return hmenu;
2170 item = &menu->items[menu->FocusedItem];
2171 if (!(item->fType & MF_POPUP) ||
2172 (item->fState & (MF_GRAYED | MF_DISABLED)))
2174 WIN_ReleaseWndPtr(wndPtr);
2175 return hmenu;
2178 /* message must be sent before using item,
2179 because nearly everything may be changed by the application ! */
2181 /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
2182 if (!(wFlags & TPM_NONOTIFY))
2183 SendMessageA( hwndOwner, WM_INITMENUPOPUP, item->hSubMenu,
2184 MAKELONG( menu->FocusedItem, IS_SYSTEM_MENU(menu) ));
2186 item = &menu->items[menu->FocusedItem];
2187 rect = item->rect;
2189 /* correct item if modified as a reaction to WM_INITMENUPOPUP message */
2190 if (!(item->fState & MF_HILITE))
2192 if (menu->wFlags & MF_POPUP) hdc = GetDC( menu->hWnd );
2193 else hdc = GetDCEx( menu->hWnd, 0, DCX_CACHE | DCX_WINDOW);
2195 SelectObject( hdc, hMenuFont);
2197 item->fState |= MF_HILITE;
2198 MENU_DrawMenuItem( menu->hWnd, hmenu, hwndOwner, hdc, item, menu->Height, !(menu->wFlags & MF_POPUP), ODA_DRAWENTIRE );
2199 ReleaseDC( menu->hWnd, hdc );
2201 if (!item->rect.top && !item->rect.left && !item->rect.bottom && !item->rect.right)
2202 item->rect = rect;
2204 item->fState |= MF_MOUSESELECT;
2206 if (IS_SYSTEM_MENU(menu))
2208 MENU_InitSysMenuPopup(item->hSubMenu, wndPtr->dwStyle, GetClassLongA(wndPtr->hwndSelf, GCL_STYLE));
2210 NC_GetSysPopupPos( wndPtr, &rect );
2211 rect.top = rect.bottom;
2212 rect.right = GetSystemMetrics(SM_CXSIZE);
2213 rect.bottom = GetSystemMetrics(SM_CYSIZE);
2215 else
2217 if (menu->wFlags & MF_POPUP)
2219 rect.left = wndPtr->rectWindow.left + item->rect.right - GetSystemMetrics(SM_CXBORDER);
2220 rect.top = wndPtr->rectWindow.top + item->rect.top;
2221 rect.right = item->rect.left - item->rect.right + GetSystemMetrics(SM_CXBORDER);
2222 rect.bottom = item->rect.top - item->rect.bottom;
2224 else
2226 rect.left = wndPtr->rectWindow.left + item->rect.left;
2227 rect.top = wndPtr->rectWindow.top + item->rect.bottom;
2228 rect.right = item->rect.right - item->rect.left;
2229 rect.bottom = item->rect.bottom - item->rect.top;
2233 MENU_ShowPopup( hwndOwner, item->hSubMenu, menu->FocusedItem,
2234 rect.left, rect.top, rect.right, rect.bottom );
2235 if (selectFirst)
2236 MENU_MoveSelection( hwndOwner, item->hSubMenu, ITEM_NEXT );
2237 WIN_ReleaseWndPtr(wndPtr);
2238 return item->hSubMenu;
2243 /**********************************************************************
2244 * MENU_IsMenuActive
2246 BOOL MENU_IsMenuActive(void)
2248 return pTopPopupWnd && (pTopPopupWnd->dwStyle & WS_VISIBLE);
2251 /***********************************************************************
2252 * MENU_PtMenu
2254 * Walks menu chain trying to find a menu pt maps to.
2256 static HMENU MENU_PtMenu( HMENU hMenu, POINT pt )
2258 POPUPMENU *menu = MENU_GetMenu( hMenu );
2259 register UINT ht = menu->FocusedItem;
2261 /* try subpopup first (if any) */
2262 ht = (ht != NO_SELECTED_ITEM &&
2263 (menu->items[ht].fType & MF_POPUP) &&
2264 (menu->items[ht].fState & MF_MOUSESELECT))
2265 ? (UINT) MENU_PtMenu(menu->items[ht].hSubMenu, pt) : 0;
2267 if( !ht ) /* check the current window (avoiding WM_HITTEST) */
2269 ht = (UINT)NC_HandleNCHitTest( menu->hWnd, pt );
2270 if( menu->wFlags & MF_POPUP )
2271 ht = (ht != (UINT)HTNOWHERE &&
2272 ht != (UINT)HTERROR) ? (UINT)hMenu : 0;
2273 else
2275 WND* wndPtr = WIN_FindWndPtr(menu->hWnd);
2277 ht = ( ht == HTSYSMENU ) ? (UINT)(wndPtr->hSysMenu)
2278 : ( ht == HTMENU ) ? (UINT)(wndPtr->wIDmenu) : 0;
2279 WIN_ReleaseWndPtr(wndPtr);
2282 return (HMENU)ht;
2285 /***********************************************************************
2286 * MENU_ExecFocusedItem
2288 * Execute a menu item (for instance when user pressed Enter).
2289 * Return the wID of the executed item. Otherwise, -1 indicating
2290 * that no menu item was executed;
2291 * Have to receive the flags for the TrackPopupMenu options to avoid
2292 * sending unwanted message.
2295 static INT MENU_ExecFocusedItem( MTRACKER* pmt, HMENU hMenu, UINT wFlags )
2297 MENUITEM *item;
2298 POPUPMENU *menu = MENU_GetMenu( hMenu );
2300 TRACE("%p hmenu=0x%04x\n", pmt, hMenu);
2302 if (!menu || !menu->nItems ||
2303 (menu->FocusedItem == NO_SELECTED_ITEM)) return -1;
2305 item = &menu->items[menu->FocusedItem];
2307 TRACE("%08x %08x %08x\n",
2308 hMenu, item->wID, item->hSubMenu);
2310 if (!(item->fType & MF_POPUP))
2312 if (!(item->fState & (MF_GRAYED | MF_DISABLED)) && !(item->fType & MF_SEPARATOR))
2314 /* If TPM_RETURNCMD is set you return the id, but
2315 do not send a message to the owner */
2316 if(!(wFlags & TPM_RETURNCMD))
2318 if( menu->wFlags & MF_SYSMENU )
2319 PostMessageA( pmt->hOwnerWnd, WM_SYSCOMMAND, item->wID,
2320 MAKELPARAM((INT16)pmt->pt.x, (INT16)pmt->pt.y) );
2321 else
2322 PostMessageA( pmt->hOwnerWnd, WM_COMMAND, item->wID, 0 );
2324 return item->wID;
2327 else
2328 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd, hMenu, TRUE, wFlags);
2330 return -1;
2333 /***********************************************************************
2334 * MENU_SwitchTracking
2336 * Helper function for menu navigation routines.
2338 static void MENU_SwitchTracking( MTRACKER* pmt, HMENU hPtMenu, UINT id )
2340 POPUPMENU *ptmenu = MENU_GetMenu( hPtMenu );
2341 POPUPMENU *topmenu = MENU_GetMenu( pmt->hTopMenu );
2343 TRACE("%p hmenu=0x%04x 0x%04x\n", pmt, hPtMenu, id);
2345 if( pmt->hTopMenu != hPtMenu &&
2346 !((ptmenu->wFlags | topmenu->wFlags) & MF_POPUP) )
2348 /* both are top level menus (system and menu-bar) */
2349 MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE );
2350 MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, NO_SELECTED_ITEM, FALSE, 0 );
2351 pmt->hTopMenu = hPtMenu;
2353 else MENU_HideSubPopups( pmt->hOwnerWnd, hPtMenu, FALSE );
2354 MENU_SelectItem( pmt->hOwnerWnd, hPtMenu, id, TRUE, 0 );
2358 /***********************************************************************
2359 * MENU_ButtonDown
2361 * Return TRUE if we can go on with menu tracking.
2363 static BOOL MENU_ButtonDown( MTRACKER* pmt, HMENU hPtMenu, UINT wFlags )
2365 TRACE("%p hmenu=0x%04x\n", pmt, hPtMenu);
2367 if (hPtMenu)
2369 UINT id = 0;
2370 POPUPMENU *ptmenu = MENU_GetMenu( hPtMenu );
2371 MENUITEM *item;
2373 if( IS_SYSTEM_MENU(ptmenu) )
2374 item = ptmenu->items;
2375 else
2376 item = MENU_FindItemByCoords( ptmenu, pmt->pt, &id );
2378 if( item )
2380 if( ptmenu->FocusedItem != id )
2381 MENU_SwitchTracking( pmt, hPtMenu, id );
2383 /* If the popup menu is not already "popped" */
2384 if(!(item->fState & MF_MOUSESELECT ))
2386 pmt->hCurrentMenu = MENU_ShowSubPopup( pmt->hOwnerWnd, hPtMenu, FALSE, wFlags );
2388 /* In win31, a newly popped menu always remains opened for the next buttonup */
2389 if(TWEAK_WineLook == WIN31_LOOK)
2390 ptmenu->bTimeToHide = FALSE;
2393 return TRUE;
2395 /* Else the click was on the menu bar, finish the tracking */
2397 return FALSE;
2400 /***********************************************************************
2401 * MENU_ButtonUp
2403 * Return the value of MENU_ExecFocusedItem if
2404 * the selected item was not a popup. Else open the popup.
2405 * A -1 return value indicates that we go on with menu tracking.
2408 static INT MENU_ButtonUp( MTRACKER* pmt, HMENU hPtMenu, UINT wFlags)
2410 TRACE("%p hmenu=0x%04x\n", pmt, hPtMenu);
2412 if (hPtMenu)
2414 UINT id = 0;
2415 POPUPMENU *ptmenu = MENU_GetMenu( hPtMenu );
2416 MENUITEM *item;
2418 if( IS_SYSTEM_MENU(ptmenu) )
2419 item = ptmenu->items;
2420 else
2421 item = MENU_FindItemByCoords( ptmenu, pmt->pt, &id );
2423 if( item && (ptmenu->FocusedItem == id ))
2425 if( !(item->fType & MF_POPUP) )
2426 return MENU_ExecFocusedItem( pmt, hPtMenu, wFlags);
2428 /* If we are dealing with the top-level menu */
2429 /* and this is a click on an already "popped" item: */
2430 /* Stop the menu tracking and close the opened submenus */
2431 if((pmt->hTopMenu == hPtMenu) && (ptmenu->bTimeToHide == TRUE))
2432 return 0;
2434 ptmenu->bTimeToHide = TRUE;
2436 return -1;
2440 /***********************************************************************
2441 * MENU_MouseMove
2443 * Return TRUE if we can go on with menu tracking.
2445 static BOOL MENU_MouseMove( MTRACKER* pmt, HMENU hPtMenu, UINT wFlags )
2447 UINT id = NO_SELECTED_ITEM;
2448 POPUPMENU *ptmenu = NULL;
2450 if( hPtMenu )
2452 ptmenu = MENU_GetMenu( hPtMenu );
2453 if( IS_SYSTEM_MENU(ptmenu) )
2454 id = 0;
2455 else
2456 MENU_FindItemByCoords( ptmenu, pmt->pt, &id );
2459 if( id == NO_SELECTED_ITEM )
2461 MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
2462 NO_SELECTED_ITEM, TRUE, pmt->hTopMenu);
2465 else if( ptmenu->FocusedItem != id )
2467 MENU_SwitchTracking( pmt, hPtMenu, id );
2468 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd, hPtMenu, FALSE, wFlags);
2470 return TRUE;
2474 /***********************************************************************
2475 * MENU_DoNextMenu
2477 * NOTE: WM_NEXTMENU documented in Win32 is a bit different.
2479 static LRESULT MENU_DoNextMenu( MTRACKER* pmt, UINT vk )
2481 POPUPMENU *menu = MENU_GetMenu( pmt->hTopMenu );
2483 if( (vk == VK_LEFT && menu->FocusedItem == 0 ) ||
2484 (vk == VK_RIGHT && menu->FocusedItem == menu->nItems - 1))
2486 WND* wndPtr;
2487 HMENU hNewMenu;
2488 HWND hNewWnd;
2489 UINT id = 0;
2490 LRESULT l = SendMessageA( pmt->hOwnerWnd, WM_NEXTMENU, vk,
2491 (IS_SYSTEM_MENU(menu)) ? GetSubMenu16(pmt->hTopMenu,0) : pmt->hTopMenu );
2493 TRACE("%04x [%04x] -> %04x [%04x]\n",
2494 (UINT16)pmt->hCurrentMenu, (UINT16)pmt->hOwnerWnd, LOWORD(l), HIWORD(l) );
2496 if( l == 0 )
2498 wndPtr = WIN_FindWndPtr(pmt->hOwnerWnd);
2500 hNewWnd = pmt->hOwnerWnd;
2501 if( IS_SYSTEM_MENU(menu) )
2503 /* switch to the menu bar */
2505 if( wndPtr->dwStyle & WS_CHILD || !wndPtr->wIDmenu )
2507 WIN_ReleaseWndPtr(wndPtr);
2508 return FALSE;
2511 hNewMenu = wndPtr->wIDmenu;
2512 if( vk == VK_LEFT )
2514 menu = MENU_GetMenu( hNewMenu );
2515 id = menu->nItems - 1;
2518 else if( wndPtr->dwStyle & WS_SYSMENU )
2520 /* switch to the system menu */
2521 hNewMenu = wndPtr->hSysMenu;
2523 else
2525 WIN_ReleaseWndPtr(wndPtr);
2526 return FALSE;
2528 WIN_ReleaseWndPtr(wndPtr);
2530 else /* application returned a new menu to switch to */
2532 hNewMenu = LOWORD(l); hNewWnd = HIWORD(l);
2534 if( IsMenu(hNewMenu) && IsWindow(hNewWnd) )
2536 wndPtr = WIN_FindWndPtr(hNewWnd);
2538 if( wndPtr->dwStyle & WS_SYSMENU &&
2539 GetSubMenu16(wndPtr->hSysMenu, 0) == hNewMenu )
2541 /* get the real system menu */
2542 hNewMenu = wndPtr->hSysMenu;
2544 else if( wndPtr->dwStyle & WS_CHILD || wndPtr->wIDmenu != hNewMenu )
2546 /* FIXME: Not sure what to do here;
2547 * perhaps try to track hNewMenu as a popup? */
2549 TRACE(" -- got confused.\n");
2550 WIN_ReleaseWndPtr(wndPtr);
2551 return FALSE;
2553 WIN_ReleaseWndPtr(wndPtr);
2555 else return FALSE;
2558 if( hNewMenu != pmt->hTopMenu )
2560 MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, NO_SELECTED_ITEM,
2561 FALSE, 0 );
2562 if( pmt->hCurrentMenu != pmt->hTopMenu )
2563 MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE );
2566 if( hNewWnd != pmt->hOwnerWnd )
2568 ReleaseCapture();
2569 pmt->hOwnerWnd = hNewWnd;
2570 EVENT_Capture( pmt->hOwnerWnd, HTMENU );
2573 pmt->hTopMenu = pmt->hCurrentMenu = hNewMenu; /* all subpopups are hidden */
2574 MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, id, TRUE, 0 );
2576 return TRUE;
2578 return FALSE;
2581 /***********************************************************************
2582 * MENU_SuspendPopup
2584 * The idea is not to show the popup if the next input message is
2585 * going to hide it anyway.
2587 static BOOL MENU_SuspendPopup( MTRACKER* pmt, UINT16 uMsg )
2589 MSG msg;
2591 msg.hwnd = pmt->hOwnerWnd;
2593 PeekMessageA( &msg, 0, 0, 0, PM_NOYIELD | PM_REMOVE);
2594 pmt->trackFlags |= TF_SKIPREMOVE;
2596 switch( uMsg )
2598 case WM_KEYDOWN:
2599 PeekMessageA( &msg, 0, 0, 0, PM_NOYIELD | PM_NOREMOVE);
2600 if( msg.message == WM_KEYUP || msg.message == WM_PAINT )
2602 PeekMessageA( &msg, 0, 0, 0, PM_NOYIELD | PM_REMOVE);
2603 PeekMessageA( &msg, 0, 0, 0, PM_NOYIELD | PM_NOREMOVE);
2604 if( msg.message == WM_KEYDOWN &&
2605 (msg.wParam == VK_LEFT || msg.wParam == VK_RIGHT))
2607 pmt->trackFlags |= TF_SUSPENDPOPUP;
2608 return TRUE;
2611 break;
2614 /* failures go through this */
2615 pmt->trackFlags &= ~TF_SUSPENDPOPUP;
2616 return FALSE;
2619 /***********************************************************************
2620 * MENU_KeyLeft
2622 * Handle a VK_LEFT key event in a menu.
2624 static void MENU_KeyLeft( MTRACKER* pmt, UINT wFlags )
2626 POPUPMENU *menu;
2627 HMENU hmenutmp, hmenuprev;
2628 UINT prevcol;
2630 hmenuprev = hmenutmp = pmt->hTopMenu;
2631 menu = MENU_GetMenu( hmenutmp );
2633 /* Try to move 1 column left (if possible) */
2634 if( (prevcol = MENU_GetStartOfPrevColumn( pmt->hCurrentMenu )) !=
2635 NO_SELECTED_ITEM ) {
2637 MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
2638 prevcol, TRUE, 0 );
2639 return;
2642 /* close topmost popup */
2643 while (hmenutmp != pmt->hCurrentMenu)
2645 hmenuprev = hmenutmp;
2646 hmenutmp = MENU_GetSubPopup( hmenuprev );
2649 MENU_HideSubPopups( pmt->hOwnerWnd, hmenuprev, TRUE );
2650 pmt->hCurrentMenu = hmenuprev;
2652 if ( (hmenuprev == pmt->hTopMenu) && !(menu->wFlags & MF_POPUP) )
2654 /* move menu bar selection if no more popups are left */
2656 if( !MENU_DoNextMenu( pmt, VK_LEFT) )
2657 MENU_MoveSelection( pmt->hOwnerWnd, pmt->hTopMenu, ITEM_PREV );
2659 if ( hmenuprev != hmenutmp || pmt->trackFlags & TF_SUSPENDPOPUP )
2661 /* A sublevel menu was displayed - display the next one
2662 * unless there is another displacement coming up */
2664 if( !MENU_SuspendPopup( pmt, WM_KEYDOWN ) )
2665 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd,
2666 pmt->hTopMenu, TRUE, wFlags);
2672 /***********************************************************************
2673 * MENU_KeyRight
2675 * Handle a VK_RIGHT key event in a menu.
2677 static void MENU_KeyRight( MTRACKER* pmt, UINT wFlags )
2679 HMENU hmenutmp;
2680 POPUPMENU *menu = MENU_GetMenu( pmt->hTopMenu );
2681 UINT nextcol;
2683 TRACE("MENU_KeyRight called, cur %x (%s), top %x (%s).\n",
2684 pmt->hCurrentMenu,
2685 debugstr_w((MENU_GetMenu(pmt->hCurrentMenu))->
2686 items[0].text),
2687 pmt->hTopMenu, debugstr_w(menu->items[0].text) );
2689 if ( (menu->wFlags & MF_POPUP) || (pmt->hCurrentMenu != pmt->hTopMenu))
2691 /* If already displaying a popup, try to display sub-popup */
2693 hmenutmp = pmt->hCurrentMenu;
2694 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd, hmenutmp, TRUE, wFlags);
2696 /* if subpopup was displayed then we are done */
2697 if (hmenutmp != pmt->hCurrentMenu) return;
2700 /* Check to see if there's another column */
2701 if( (nextcol = MENU_GetStartOfNextColumn( pmt->hCurrentMenu )) !=
2702 NO_SELECTED_ITEM ) {
2703 TRACE("Going to %d.\n", nextcol );
2704 MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
2705 nextcol, TRUE, 0 );
2706 return;
2709 if (!(menu->wFlags & MF_POPUP)) /* menu bar tracking */
2711 if( pmt->hCurrentMenu != pmt->hTopMenu )
2713 MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE );
2714 hmenutmp = pmt->hCurrentMenu = pmt->hTopMenu;
2715 } else hmenutmp = 0;
2717 /* try to move to the next item */
2718 if( !MENU_DoNextMenu( pmt, VK_RIGHT) )
2719 MENU_MoveSelection( pmt->hOwnerWnd, pmt->hTopMenu, ITEM_NEXT );
2721 if( hmenutmp || pmt->trackFlags & TF_SUSPENDPOPUP )
2722 if( !MENU_SuspendPopup(pmt, WM_KEYDOWN) )
2723 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd,
2724 pmt->hTopMenu, TRUE, wFlags);
2728 /***********************************************************************
2729 * MENU_TrackMenu
2731 * Menu tracking code.
2733 static INT MENU_TrackMenu( HMENU hmenu, UINT wFlags, INT x, INT y,
2734 HWND hwnd, const RECT *lprect )
2736 MSG msg;
2737 POPUPMENU *menu;
2738 BOOL fRemove;
2739 INT executedMenuId = -1;
2740 MTRACKER mt;
2741 BOOL enterIdleSent = FALSE;
2743 mt.trackFlags = 0;
2744 mt.hCurrentMenu = hmenu;
2745 mt.hTopMenu = hmenu;
2746 mt.hOwnerWnd = hwnd;
2747 mt.pt.x = x;
2748 mt.pt.y = y;
2750 TRACE("hmenu=0x%04x flags=0x%08x (%d,%d) hwnd=0x%04x (%d,%d)-(%d,%d)\n",
2751 hmenu, wFlags, x, y, hwnd, (lprect) ? lprect->left : 0, (lprect) ? lprect->top : 0,
2752 (lprect) ? lprect->right : 0, (lprect) ? lprect->bottom : 0);
2754 fEndMenu = FALSE;
2755 if (!(menu = MENU_GetMenu( hmenu ))) return FALSE;
2757 if (wFlags & TPM_BUTTONDOWN)
2759 /* Get the result in order to start the tracking or not */
2760 fRemove = MENU_ButtonDown( &mt, hmenu, wFlags );
2761 fEndMenu = !fRemove;
2764 EVENT_Capture( mt.hOwnerWnd, HTMENU );
2766 while (!fEndMenu)
2768 menu = MENU_GetMenu( mt.hCurrentMenu );
2769 msg.hwnd = (wFlags & TPM_ENTERIDLEEX && menu->wFlags & MF_POPUP) ? menu->hWnd : 0;
2771 /* we have to keep the message in the queue until it's
2772 * clear that menu loop is not over yet. */
2774 if (!MSG_InternalGetMessage( QMSG_WIN32A, &msg, msg.hwnd, mt.hOwnerWnd,
2775 MSGF_MENU, PM_NOREMOVE, !enterIdleSent, &enterIdleSent )) break;
2777 /* check if EndMenu() tried to cancel us, by posting this message */
2778 if(msg.message == WM_CANCELMODE)
2780 /* we are now out of the loop */
2781 fEndMenu = TRUE;
2783 /* remove the message from the queue */
2784 PeekMessageA( &msg, 0, msg.message, msg.message, PM_REMOVE );
2786 /* break out of internal loop, ala ESCAPE */
2787 break;
2790 TranslateMessage( &msg );
2791 mt.pt = msg.pt;
2793 if ( (msg.hwnd==menu->hWnd) || (msg.message!=WM_TIMER) )
2794 enterIdleSent=FALSE;
2796 fRemove = FALSE;
2797 if ((msg.message >= WM_MOUSEFIRST) && (msg.message <= WM_MOUSELAST))
2800 * use the mouse coordinates in lParam instead of those in the MSG
2801 * struct to properly handle synthetic messages. lParam coords are
2802 * relative to client area, so they must be converted; since they can
2803 * be negative, we must use SLOWORD/SHIWORD instead of LOWORD/HIWORD.
2805 mt.pt.x = SLOWORD(msg.lParam);
2806 mt.pt.y = SHIWORD(msg.lParam);
2807 ClientToScreen(msg.hwnd,&mt.pt);
2809 /* Find a menu for this mouse event */
2810 hmenu = MENU_PtMenu( mt.hTopMenu, mt.pt );
2812 switch(msg.message)
2814 /* no WM_NC... messages in captured state */
2816 case WM_RBUTTONDBLCLK:
2817 case WM_RBUTTONDOWN:
2818 if (!(wFlags & TPM_RIGHTBUTTON)) break;
2819 /* fall through */
2820 case WM_LBUTTONDBLCLK:
2821 case WM_LBUTTONDOWN:
2822 /* If the message belongs to the menu, removes it from the queue */
2823 /* Else, end menu tracking */
2824 fRemove = MENU_ButtonDown( &mt, hmenu, wFlags );
2825 fEndMenu = !fRemove;
2826 break;
2828 case WM_RBUTTONUP:
2829 if (!(wFlags & TPM_RIGHTBUTTON)) break;
2830 /* fall through */
2831 case WM_LBUTTONUP:
2832 /* Check if a menu was selected by the mouse */
2833 if (hmenu)
2835 executedMenuId = MENU_ButtonUp( &mt, hmenu, wFlags);
2837 /* End the loop if executedMenuId is an item ID */
2838 /* or if the job was done (executedMenuId = 0). */
2839 fEndMenu = fRemove = (executedMenuId != -1);
2841 /* No menu was selected by the mouse */
2842 /* if the function was called by TrackPopupMenu, continue
2843 with the menu tracking. If not, stop it */
2844 else
2845 fEndMenu = ((wFlags & TPM_POPUPMENU) ? FALSE : TRUE);
2847 break;
2849 case WM_MOUSEMOVE:
2850 /* In win95 winelook, the selected menu item must be changed every time the
2851 mouse moves. In Win31 winelook, the mouse button has to be held down */
2853 if ( (TWEAK_WineLook > WIN31_LOOK) ||
2854 ( (msg.wParam & MK_LBUTTON) ||
2855 ((wFlags & TPM_RIGHTBUTTON) && (msg.wParam & MK_RBUTTON))) )
2857 fEndMenu |= !MENU_MouseMove( &mt, hmenu, wFlags );
2859 } /* switch(msg.message) - mouse */
2861 else if ((msg.message >= WM_KEYFIRST) && (msg.message <= WM_KEYLAST))
2863 fRemove = TRUE; /* Keyboard messages are always removed */
2864 switch(msg.message)
2866 case WM_KEYDOWN:
2867 switch(msg.wParam)
2869 case VK_HOME:
2870 case VK_END:
2871 MENU_SelectItem( mt.hOwnerWnd, mt.hCurrentMenu,
2872 NO_SELECTED_ITEM, FALSE, 0 );
2873 /* fall through */
2874 case VK_UP:
2875 MENU_MoveSelection( mt.hOwnerWnd, mt.hCurrentMenu,
2876 (msg.wParam == VK_HOME)? ITEM_NEXT : ITEM_PREV );
2877 break;
2879 case VK_DOWN: /* If on menu bar, pull-down the menu */
2881 menu = MENU_GetMenu( mt.hCurrentMenu );
2882 if (!(menu->wFlags & MF_POPUP))
2883 mt.hCurrentMenu = MENU_ShowSubPopup(mt.hOwnerWnd, mt.hTopMenu, TRUE, wFlags);
2884 else /* otherwise try to move selection */
2885 MENU_MoveSelection( mt.hOwnerWnd, mt.hCurrentMenu, ITEM_NEXT );
2886 break;
2888 case VK_LEFT:
2889 MENU_KeyLeft( &mt, wFlags );
2890 break;
2892 case VK_RIGHT:
2893 MENU_KeyRight( &mt, wFlags );
2894 break;
2896 case VK_ESCAPE:
2897 fEndMenu = TRUE;
2898 break;
2900 case VK_F1:
2902 HELPINFO hi;
2903 hi.cbSize = sizeof(HELPINFO);
2904 hi.iContextType = HELPINFO_MENUITEM;
2905 if (menu->FocusedItem == NO_SELECTED_ITEM)
2906 hi.iCtrlId = 0;
2907 else
2908 hi.iCtrlId = menu->items[menu->FocusedItem].wID;
2909 hi.hItemHandle = hmenu;
2910 hi.dwContextId = menu->dwContextHelpID;
2911 hi.MousePos = msg.pt;
2912 SendMessageA(hwnd, WM_HELP, 0, (LPARAM)&hi);
2913 break;
2916 default:
2917 break;
2919 break; /* WM_KEYDOWN */
2921 case WM_SYSKEYDOWN:
2922 switch(msg.wParam)
2924 case VK_MENU:
2925 fEndMenu = TRUE;
2926 break;
2929 break; /* WM_SYSKEYDOWN */
2931 case WM_CHAR:
2933 UINT pos;
2935 if (msg.wParam == '\r' || msg.wParam == ' ')
2937 executedMenuId = MENU_ExecFocusedItem(&mt,mt.hCurrentMenu, wFlags);
2938 fEndMenu = (executedMenuId != -1);
2940 break;
2943 /* Hack to avoid control chars. */
2944 /* We will find a better way real soon... */
2945 if ((msg.wParam <= 32) || (msg.wParam >= 127)) break;
2947 pos = MENU_FindItemByKey( mt.hOwnerWnd, mt.hCurrentMenu,
2948 LOWORD(msg.wParam), FALSE );
2949 if (pos == (UINT)-2) fEndMenu = TRUE;
2950 else if (pos == (UINT)-1) MessageBeep(0);
2951 else
2953 MENU_SelectItem( mt.hOwnerWnd, mt.hCurrentMenu, pos,
2954 TRUE, 0 );
2955 executedMenuId = MENU_ExecFocusedItem(&mt,mt.hCurrentMenu, wFlags);
2956 fEndMenu = (executedMenuId != -1);
2959 break;
2960 } /* switch(msg.message) - kbd */
2962 else
2964 DispatchMessageA( &msg );
2967 if (!fEndMenu) fRemove = TRUE;
2969 /* finally remove message from the queue */
2971 if (fRemove && !(mt.trackFlags & TF_SKIPREMOVE) )
2972 PeekMessageA( &msg, 0, msg.message, msg.message, PM_REMOVE );
2973 else mt.trackFlags &= ~TF_SKIPREMOVE;
2976 ReleaseCapture();
2978 /* If dropdown is still painted and the close box is clicked on
2979 then the menu will be destroyed as part of the DispatchMessage above.
2980 This will then invalidate the menu handle in mt.hTopMenu. We should
2981 check for this first. */
2982 if( IsMenu( mt.hTopMenu ) )
2984 menu = MENU_GetMenu( mt.hTopMenu );
2986 if( IsWindow( mt.hOwnerWnd ) )
2988 MENU_HideSubPopups( mt.hOwnerWnd, mt.hTopMenu, FALSE );
2990 if (menu && menu->wFlags & MF_POPUP)
2992 ShowWindow( menu->hWnd, SW_HIDE );
2993 uSubPWndLevel = 0;
2995 MENU_SelectItem( mt.hOwnerWnd, mt.hTopMenu, NO_SELECTED_ITEM, FALSE, 0 );
2996 SendMessageA( mt.hOwnerWnd, WM_MENUSELECT, MAKELONG(0,0xffff), 0 );
2999 /* Reset the variable for hiding menu */
3000 if( menu ) menu->bTimeToHide = FALSE;
3003 /* The return value is only used by TrackPopupMenu */
3004 return ((executedMenuId != -1) ? executedMenuId : 0);
3007 /***********************************************************************
3008 * MENU_InitTracking
3010 static BOOL MENU_InitTracking(HWND hWnd, HMENU hMenu, BOOL bPopup, UINT wFlags)
3012 TRACE("hwnd=0x%04x hmenu=0x%04x\n", hWnd, hMenu);
3014 HideCaret(0);
3016 /* Send WM_ENTERMENULOOP and WM_INITMENU message only if TPM_NONOTIFY flag is not specified */
3017 if (!(wFlags & TPM_NONOTIFY))
3018 SendMessageA( hWnd, WM_ENTERMENULOOP, bPopup, 0 );
3020 SendMessageA( hWnd, WM_SETCURSOR, hWnd, HTCAPTION );
3022 if (!(wFlags & TPM_NONOTIFY))
3023 SendMessageA( hWnd, WM_INITMENU, hMenu, 0 );
3025 return TRUE;
3027 /***********************************************************************
3028 * MENU_ExitTracking
3030 static BOOL MENU_ExitTracking(HWND hWnd)
3032 TRACE("hwnd=0x%04x\n", hWnd);
3034 SendMessageA( hWnd, WM_EXITMENULOOP, 0, 0 );
3035 ShowCaret(0);
3036 return TRUE;
3039 /***********************************************************************
3040 * MENU_TrackMouseMenuBar
3042 * Menu-bar tracking upon a mouse event. Called from NC_HandleSysCommand().
3044 void MENU_TrackMouseMenuBar( WND* wndPtr, INT ht, POINT pt )
3046 HWND hWnd = wndPtr->hwndSelf;
3047 HMENU hMenu = (ht == HTSYSMENU) ? wndPtr->hSysMenu : wndPtr->wIDmenu;
3048 UINT wFlags = TPM_ENTERIDLEEX | TPM_BUTTONDOWN | TPM_LEFTALIGN | TPM_LEFTBUTTON;
3050 TRACE("pwnd=%p ht=0x%04x (%ld,%ld)\n", wndPtr, ht, pt.x, pt.y);
3052 if (IsMenu(hMenu))
3054 MENU_InitTracking( hWnd, hMenu, FALSE, wFlags );
3055 MENU_TrackMenu( hMenu, wFlags, pt.x, pt.y, hWnd, NULL );
3056 MENU_ExitTracking(hWnd);
3061 /***********************************************************************
3062 * MENU_TrackKbdMenuBar
3064 * Menu-bar tracking upon a keyboard event. Called from NC_HandleSysCommand().
3066 void MENU_TrackKbdMenuBar( WND* wndPtr, UINT wParam, INT vkey)
3068 UINT uItem = NO_SELECTED_ITEM;
3069 HMENU hTrackMenu;
3070 UINT wFlags = TPM_ENTERIDLEEX | TPM_LEFTALIGN | TPM_LEFTBUTTON;
3072 /* find window that has a menu */
3074 while( wndPtr->dwStyle & WS_CHILD)
3075 if( !(wndPtr = wndPtr->parent) ) return;
3077 /* check if we have to track a system menu */
3079 if( (wndPtr->dwStyle & (WS_CHILD | WS_MINIMIZE)) ||
3080 !wndPtr->wIDmenu || vkey == VK_SPACE )
3082 if( !(wndPtr->dwStyle & WS_SYSMENU) ) return;
3083 hTrackMenu = wndPtr->hSysMenu;
3084 uItem = 0;
3085 wParam |= HTSYSMENU; /* prevent item lookup */
3087 else
3088 hTrackMenu = wndPtr->wIDmenu;
3090 if (IsMenu( hTrackMenu ))
3092 MENU_InitTracking( wndPtr->hwndSelf, hTrackMenu, FALSE, wFlags );
3094 if( vkey && vkey != VK_SPACE )
3096 uItem = MENU_FindItemByKey( wndPtr->hwndSelf, hTrackMenu,
3097 vkey, (wParam & HTSYSMENU) );
3098 if( uItem >= (UINT)(-2) )
3100 if( uItem == (UINT)(-1) ) MessageBeep(0);
3101 hTrackMenu = 0;
3105 if( hTrackMenu )
3107 MENU_SelectItem( wndPtr->hwndSelf, hTrackMenu, uItem, TRUE, 0 );
3109 if( uItem == NO_SELECTED_ITEM )
3110 MENU_MoveSelection( wndPtr->hwndSelf, hTrackMenu, ITEM_NEXT );
3111 else if( vkey )
3112 PostMessageA( wndPtr->hwndSelf, WM_KEYDOWN, VK_DOWN, 0L );
3114 MENU_TrackMenu( hTrackMenu, wFlags, 0, 0, wndPtr->hwndSelf, NULL );
3117 MENU_ExitTracking (wndPtr->hwndSelf);
3122 /**********************************************************************
3123 * TrackPopupMenu16 (USER.416)
3125 BOOL16 WINAPI TrackPopupMenu16( HMENU16 hMenu, UINT16 wFlags, INT16 x, INT16 y,
3126 INT16 nReserved, HWND16 hWnd, const RECT16 *lpRect )
3128 RECT r;
3129 if (lpRect)
3130 CONV_RECT16TO32( lpRect, &r );
3131 return TrackPopupMenu( hMenu, wFlags, x, y, nReserved, hWnd,
3132 lpRect ? &r : NULL );
3136 /**********************************************************************
3137 * TrackPopupMenu (USER32.@)
3139 * Like the win32 API, the function return the command ID only if the
3140 * flag TPM_RETURNCMD is on.
3143 BOOL WINAPI TrackPopupMenu( HMENU hMenu, UINT wFlags, INT x, INT y,
3144 INT nReserved, HWND hWnd, const RECT *lpRect )
3146 BOOL ret = FALSE;
3148 MENU_InitTracking(hWnd, hMenu, TRUE, wFlags);
3150 /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
3151 if (!(wFlags & TPM_NONOTIFY))
3152 SendMessageA( hWnd, WM_INITMENUPOPUP, hMenu, 0);
3154 if (MENU_ShowPopup( hWnd, hMenu, 0, x, y, 0, 0 ))
3155 ret = MENU_TrackMenu( hMenu, wFlags | TPM_POPUPMENU, 0, 0, hWnd, lpRect );
3156 MENU_ExitTracking(hWnd);
3158 if( (!(wFlags & TPM_RETURNCMD)) && (ret != FALSE) )
3159 ret = 1;
3161 return ret;
3164 /**********************************************************************
3165 * TrackPopupMenuEx (USER32.@)
3167 BOOL WINAPI TrackPopupMenuEx( HMENU hMenu, UINT wFlags, INT x, INT y,
3168 HWND hWnd, LPTPMPARAMS lpTpm )
3170 FIXME("not fully implemented\n" );
3171 return TrackPopupMenu( hMenu, wFlags, x, y, 0, hWnd,
3172 lpTpm ? &lpTpm->rcExclude : NULL );
3175 /***********************************************************************
3176 * PopupMenuWndProc
3178 * NOTE: Windows has totally different (and undocumented) popup wndproc.
3180 static LRESULT WINAPI PopupMenuWndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam )
3182 TRACE("hwnd=0x%04x msg=0x%04x wp=0x%04x lp=0x%08lx\n",
3183 hwnd, message, wParam, lParam);
3185 switch(message)
3187 case WM_CREATE:
3189 CREATESTRUCTW *cs = (CREATESTRUCTW*)lParam;
3190 SetWindowLongW( hwnd, 0, (LONG)cs->lpCreateParams );
3191 return 0;
3194 case WM_MOUSEACTIVATE: /* We don't want to be activated */
3195 return MA_NOACTIVATE;
3197 case WM_PAINT:
3199 PAINTSTRUCT ps;
3200 BeginPaint( hwnd, &ps );
3201 MENU_DrawPopupMenu( hwnd, ps.hdc,
3202 (HMENU)GetWindowLongA( hwnd, 0 ) );
3203 EndPaint( hwnd, &ps );
3204 return 0;
3206 case WM_ERASEBKGND:
3207 return 1;
3209 case WM_DESTROY:
3211 /* zero out global pointer in case resident popup window
3212 * was somehow destroyed. */
3214 if(MENU_GetTopPopupWnd() )
3216 if( hwnd == pTopPopupWnd->hwndSelf )
3218 ERR("resident popup destroyed!\n");
3220 MENU_DestroyTopPopupWnd();
3221 uSubPWndLevel = 0;
3223 else
3224 uSubPWndLevel--;
3225 MENU_ReleaseTopPopupWnd();
3227 break;
3229 case WM_SHOWWINDOW:
3231 if( wParam )
3233 if (!GetWindowLongW( hwnd, 0 )) ERR("no menu to display\n");
3235 else
3236 SetWindowLongW( hwnd, 0, 0 );
3237 break;
3239 case MM_SETMENUHANDLE:
3240 SetWindowLongW( hwnd, 0, wParam );
3241 break;
3243 case MM_GETMENUHANDLE:
3244 return GetWindowLongW( hwnd, 0 );
3246 default:
3247 return DefWindowProcW( hwnd, message, wParam, lParam );
3249 return 0;
3253 /***********************************************************************
3254 * MENU_GetMenuBarHeight
3256 * Compute the size of the menu bar height. Used by NC_HandleNCCalcSize().
3258 UINT MENU_GetMenuBarHeight( HWND hwnd, UINT menubarWidth,
3259 INT orgX, INT orgY )
3261 HDC hdc;
3262 RECT rectBar;
3263 WND *wndPtr;
3264 LPPOPUPMENU lppop;
3265 UINT retvalue;
3267 TRACE("HWND 0x%x, width %d, at (%d, %d).\n",
3268 hwnd, menubarWidth, orgX, orgY );
3270 if (!(wndPtr = WIN_FindWndPtr( hwnd )))
3271 return 0;
3273 if (!(lppop = MENU_GetMenu((HMENU16)wndPtr->wIDmenu)))
3275 WIN_ReleaseWndPtr(wndPtr);
3276 return 0;
3279 hdc = GetDCEx( hwnd, 0, DCX_CACHE | DCX_WINDOW );
3280 SelectObject( hdc, hMenuFont);
3281 SetRect(&rectBar, orgX, orgY, orgX+menubarWidth, orgY+GetSystemMetrics(SM_CYMENU));
3282 MENU_MenuBarCalcSize( hdc, &rectBar, lppop, hwnd );
3283 ReleaseDC( hwnd, hdc );
3284 retvalue = lppop->Height;
3285 WIN_ReleaseWndPtr(wndPtr);
3286 return retvalue;
3290 /*******************************************************************
3291 * ChangeMenu16 (USER.153)
3293 BOOL16 WINAPI ChangeMenu16( HMENU16 hMenu, UINT16 pos, SEGPTR data,
3294 UINT16 id, UINT16 flags )
3296 TRACE("menu=%04x pos=%d data=%08lx id=%04x flags=%04x\n",
3297 hMenu, pos, (DWORD)data, id, flags );
3298 if (flags & MF_APPEND) return AppendMenu16( hMenu, flags & ~MF_APPEND,
3299 id, data );
3301 /* FIXME: Word passes the item id in 'pos' and 0 or 0xffff as id */
3302 /* for MF_DELETE. We should check the parameters for all others */
3303 /* MF_* actions also (anybody got a doc on ChangeMenu?). */
3305 if (flags & MF_DELETE) return DeleteMenu16(hMenu, pos, flags & ~MF_DELETE);
3306 if (flags & MF_CHANGE) return ModifyMenu16(hMenu, pos, flags & ~MF_CHANGE,
3307 id, data );
3308 if (flags & MF_REMOVE) return RemoveMenu16(hMenu,
3309 flags & MF_BYPOSITION ? pos : id,
3310 flags & ~MF_REMOVE );
3311 /* Default: MF_INSERT */
3312 return InsertMenu16( hMenu, pos, flags, id, data );
3316 /*******************************************************************
3317 * ChangeMenuA (USER32.@)
3319 BOOL WINAPI ChangeMenuA( HMENU hMenu, UINT pos, LPCSTR data,
3320 UINT id, UINT flags )
3322 TRACE("menu=%08x pos=%d data=%08lx id=%08x flags=%08x\n",
3323 hMenu, pos, (DWORD)data, id, flags );
3324 if (flags & MF_APPEND) return AppendMenuA( hMenu, flags & ~MF_APPEND,
3325 id, data );
3326 if (flags & MF_DELETE) return DeleteMenu(hMenu, pos, flags & ~MF_DELETE);
3327 if (flags & MF_CHANGE) return ModifyMenuA(hMenu, pos, flags & ~MF_CHANGE,
3328 id, data );
3329 if (flags & MF_REMOVE) return RemoveMenu( hMenu,
3330 flags & MF_BYPOSITION ? pos : id,
3331 flags & ~MF_REMOVE );
3332 /* Default: MF_INSERT */
3333 return InsertMenuA( hMenu, pos, flags, id, data );
3337 /*******************************************************************
3338 * ChangeMenuW (USER32.@)
3340 BOOL WINAPI ChangeMenuW( HMENU hMenu, UINT pos, LPCWSTR data,
3341 UINT id, UINT flags )
3343 TRACE("menu=%08x pos=%d data=%08lx id=%08x flags=%08x\n",
3344 hMenu, pos, (DWORD)data, id, flags );
3345 if (flags & MF_APPEND) return AppendMenuW( hMenu, flags & ~MF_APPEND,
3346 id, data );
3347 if (flags & MF_DELETE) return DeleteMenu(hMenu, pos, flags & ~MF_DELETE);
3348 if (flags & MF_CHANGE) return ModifyMenuW(hMenu, pos, flags & ~MF_CHANGE,
3349 id, data );
3350 if (flags & MF_REMOVE) return RemoveMenu( hMenu,
3351 flags & MF_BYPOSITION ? pos : id,
3352 flags & ~MF_REMOVE );
3353 /* Default: MF_INSERT */
3354 return InsertMenuW( hMenu, pos, flags, id, data );
3358 /*******************************************************************
3359 * CheckMenuItem16 (USER.154)
3361 BOOL16 WINAPI CheckMenuItem16( HMENU16 hMenu, UINT16 id, UINT16 flags )
3363 return (BOOL16)CheckMenuItem( hMenu, id, flags );
3367 /*******************************************************************
3368 * CheckMenuItem (USER32.@)
3370 DWORD WINAPI CheckMenuItem( HMENU hMenu, UINT id, UINT flags )
3372 MENUITEM *item;
3373 DWORD ret;
3375 TRACE("menu=%04x id=%04x flags=%04x\n", hMenu, id, flags );
3376 if (!(item = MENU_FindItem( &hMenu, &id, flags ))) return -1;
3377 ret = item->fState & MF_CHECKED;
3378 if (flags & MF_CHECKED) item->fState |= MF_CHECKED;
3379 else item->fState &= ~MF_CHECKED;
3380 return ret;
3384 /**********************************************************************
3385 * EnableMenuItem16 (USER.155)
3387 UINT16 WINAPI EnableMenuItem16( HMENU16 hMenu, UINT16 wItemID, UINT16 wFlags )
3389 return EnableMenuItem( hMenu, wItemID, wFlags );
3393 /**********************************************************************
3394 * EnableMenuItem (USER32.@)
3396 UINT WINAPI EnableMenuItem( HMENU hMenu, UINT wItemID, UINT wFlags )
3398 UINT oldflags;
3399 MENUITEM *item;
3400 POPUPMENU *menu;
3402 TRACE("(%04x, %04X, %04X) !\n",
3403 hMenu, wItemID, wFlags);
3405 /* Get the Popupmenu to access the owner menu */
3406 if (!(menu = MENU_GetMenu(hMenu)))
3407 return (UINT)-1;
3409 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags )))
3410 return (UINT)-1;
3412 oldflags = item->fState & (MF_GRAYED | MF_DISABLED);
3413 item->fState ^= (oldflags ^ wFlags) & (MF_GRAYED | MF_DISABLED);
3415 /* In win95 if the close item in the system menu change update the close button */
3416 if (TWEAK_WineLook == WIN95_LOOK)
3417 if((item->wID == SC_CLOSE) && (oldflags != wFlags))
3419 if (menu->hSysMenuOwner != 0)
3421 POPUPMENU* parentMenu;
3423 /* Get the parent menu to access*/
3424 if (!(parentMenu = MENU_GetMenu(menu->hSysMenuOwner)))
3425 return (UINT)-1;
3427 /* Refresh the frame to reflect the change*/
3428 SetWindowPos(parentMenu->hWnd, 0, 0, 0, 0, 0,
3429 SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER);
3433 return oldflags;
3437 /*******************************************************************
3438 * GetMenuString16 (USER.161)
3440 INT16 WINAPI GetMenuString16( HMENU16 hMenu, UINT16 wItemID,
3441 LPSTR str, INT16 nMaxSiz, UINT16 wFlags )
3443 return GetMenuStringA( hMenu, wItemID, str, nMaxSiz, wFlags );
3447 /*******************************************************************
3448 * GetMenuStringA (USER32.@)
3450 INT WINAPI GetMenuStringA(
3451 HMENU hMenu, /* [in] menuhandle */
3452 UINT wItemID, /* [in] menu item (dep. on wFlags) */
3453 LPSTR str, /* [out] outbuffer. If NULL, func returns entry length*/
3454 INT nMaxSiz, /* [in] length of buffer. if 0, func returns entry len*/
3455 UINT wFlags /* [in] MF_ flags */
3457 MENUITEM *item;
3459 TRACE("menu=%04x item=%04x ptr=%p len=%d flags=%04x\n",
3460 hMenu, wItemID, str, nMaxSiz, wFlags );
3461 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) return 0;
3462 if (!IS_STRING_ITEM(item->fType)) return 0;
3463 if (!str || !nMaxSiz) return strlenW(item->text);
3464 str[0] = '\0';
3465 if (!WideCharToMultiByte( CP_ACP, 0, item->text, -1, str, nMaxSiz, NULL, NULL ))
3466 str[nMaxSiz-1] = 0;
3467 TRACE("returning '%s'\n", str );
3468 return strlen(str);
3472 /*******************************************************************
3473 * GetMenuStringW (USER32.@)
3475 INT WINAPI GetMenuStringW( HMENU hMenu, UINT wItemID,
3476 LPWSTR str, INT nMaxSiz, UINT wFlags )
3478 MENUITEM *item;
3480 TRACE("menu=%04x item=%04x ptr=%p len=%d flags=%04x\n",
3481 hMenu, wItemID, str, nMaxSiz, wFlags );
3482 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) return 0;
3483 if (!IS_STRING_ITEM(item->fType)) return 0;
3484 if (!str || !nMaxSiz) return strlenW(item->text);
3485 str[0] = '\0';
3486 lstrcpynW( str, item->text, nMaxSiz );
3487 return strlenW(str);
3491 /**********************************************************************
3492 * HiliteMenuItem16 (USER.162)
3494 BOOL16 WINAPI HiliteMenuItem16( HWND16 hWnd, HMENU16 hMenu, UINT16 wItemID,
3495 UINT16 wHilite )
3497 return HiliteMenuItem( hWnd, hMenu, wItemID, wHilite );
3501 /**********************************************************************
3502 * HiliteMenuItem (USER32.@)
3504 BOOL WINAPI HiliteMenuItem( HWND hWnd, HMENU hMenu, UINT wItemID,
3505 UINT wHilite )
3507 LPPOPUPMENU menu;
3508 TRACE("(%04x, %04x, %04x, %04x);\n",
3509 hWnd, hMenu, wItemID, wHilite);
3510 if (!MENU_FindItem( &hMenu, &wItemID, wHilite )) return FALSE;
3511 if (!(menu = MENU_GetMenu(hMenu))) return FALSE;
3512 if (menu->FocusedItem == wItemID) return TRUE;
3513 MENU_HideSubPopups( hWnd, hMenu, FALSE );
3514 MENU_SelectItem( hWnd, hMenu, wItemID, TRUE, 0 );
3515 return TRUE;
3519 /**********************************************************************
3520 * GetMenuState16 (USER.250)
3522 UINT16 WINAPI GetMenuState16( HMENU16 hMenu, UINT16 wItemID, UINT16 wFlags )
3524 return GetMenuState( hMenu, wItemID, wFlags );
3528 /**********************************************************************
3529 * GetMenuState (USER32.@)
3531 UINT WINAPI GetMenuState( HMENU hMenu, UINT wItemID, UINT wFlags )
3533 MENUITEM *item;
3534 TRACE("(menu=%04x, id=%04x, flags=%04x);\n",
3535 hMenu, wItemID, wFlags);
3536 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) return -1;
3537 debug_print_menuitem (" item: ", item, "");
3538 if (item->fType & MF_POPUP)
3540 POPUPMENU *menu = MENU_GetMenu( item->hSubMenu );
3541 if (!menu) return -1;
3542 else return (menu->nItems << 8) | ((item->fState|item->fType) & 0xff);
3544 else
3546 /* We used to (from way back then) mask the result to 0xff. */
3547 /* I don't know why and it seems wrong as the documented */
3548 /* return flag MF_SEPARATOR is outside that mask. */
3549 return (item->fType | item->fState);
3554 /**********************************************************************
3555 * GetMenuItemCount16 (USER.263)
3557 INT16 WINAPI GetMenuItemCount16( HMENU16 hMenu )
3559 LPPOPUPMENU menu = MENU_GetMenu(hMenu);
3560 if (!menu) return -1;
3561 TRACE("(%04x) returning %d\n",
3562 hMenu, menu->nItems );
3563 return menu->nItems;
3567 /**********************************************************************
3568 * GetMenuItemCount (USER32.@)
3570 INT WINAPI GetMenuItemCount( HMENU hMenu )
3572 LPPOPUPMENU menu = MENU_GetMenu(hMenu);
3573 if (!menu) return -1;
3574 TRACE("(%04x) returning %d\n",
3575 hMenu, menu->nItems );
3576 return menu->nItems;
3579 /**********************************************************************
3580 * GetMenuItemID16 (USER.264)
3582 UINT16 WINAPI GetMenuItemID16( HMENU16 hMenu, INT16 nPos )
3584 return (UINT16) GetMenuItemID (hMenu, nPos);
3587 /**********************************************************************
3588 * GetMenuItemID (USER32.@)
3590 UINT WINAPI GetMenuItemID( HMENU hMenu, INT nPos )
3592 MENUITEM * lpmi;
3594 if (!(lpmi = MENU_FindItem(&hMenu,&nPos,MF_BYPOSITION))) return 0;
3595 if (lpmi->fType & MF_POPUP) return -1;
3596 return lpmi->wID;
3600 /*******************************************************************
3601 * InsertMenu16 (USER.410)
3603 BOOL16 WINAPI InsertMenu16( HMENU16 hMenu, UINT16 pos, UINT16 flags,
3604 UINT16 id, SEGPTR data )
3606 UINT pos32 = (UINT)pos;
3607 if ((pos == (UINT16)-1) && (flags & MF_BYPOSITION)) pos32 = (UINT)-1;
3608 if (IS_STRING_ITEM(flags) && data)
3609 return InsertMenuA( hMenu, pos32, flags, id, MapSL(data) );
3610 return InsertMenuA( hMenu, pos32, flags, id, (LPSTR)data );
3614 /*******************************************************************
3615 * InsertMenuW (USER32.@)
3617 BOOL WINAPI InsertMenuW( HMENU hMenu, UINT pos, UINT flags,
3618 UINT id, LPCWSTR str )
3620 MENUITEM *item;
3622 if (IS_STRING_ITEM(flags) && str)
3623 TRACE("hMenu %04x, pos %d, flags %08x, "
3624 "id %04x, str '%s'\n",
3625 hMenu, pos, flags, id, debugstr_w(str) );
3626 else TRACE("hMenu %04x, pos %d, flags %08x, "
3627 "id %04x, str %08lx (not a string)\n",
3628 hMenu, pos, flags, id, (DWORD)str );
3630 if (!(item = MENU_InsertItem( hMenu, pos, flags ))) return FALSE;
3632 if (!(MENU_SetItemData( item, flags, id, str )))
3634 RemoveMenu( hMenu, pos, flags );
3635 return FALSE;
3638 if (flags & MF_POPUP) /* Set the MF_POPUP flag on the popup-menu */
3639 (MENU_GetMenu((HMENU16)id))->wFlags |= MF_POPUP;
3641 item->hCheckBit = item->hUnCheckBit = 0;
3642 return TRUE;
3646 /*******************************************************************
3647 * InsertMenuA (USER32.@)
3649 BOOL WINAPI InsertMenuA( HMENU hMenu, UINT pos, UINT flags,
3650 UINT id, LPCSTR str )
3652 BOOL ret;
3654 if (IS_STRING_ITEM(flags) && str)
3656 LPWSTR newstr = HEAP_strdupAtoW( GetProcessHeap(), 0, str );
3657 ret = InsertMenuW( hMenu, pos, flags, id, newstr );
3658 HeapFree( GetProcessHeap(), 0, newstr );
3659 return ret;
3661 else return InsertMenuW( hMenu, pos, flags, id, (LPCWSTR)str );
3665 /*******************************************************************
3666 * AppendMenu16 (USER.411)
3668 BOOL16 WINAPI AppendMenu16(HMENU16 hMenu, UINT16 flags, UINT16 id, SEGPTR data)
3670 return InsertMenu16( hMenu, -1, flags | MF_BYPOSITION, id, data );
3674 /*******************************************************************
3675 * AppendMenuA (USER32.@)
3677 BOOL WINAPI AppendMenuA( HMENU hMenu, UINT flags,
3678 UINT id, LPCSTR data )
3680 return InsertMenuA( hMenu, -1, flags | MF_BYPOSITION, id, data );
3684 /*******************************************************************
3685 * AppendMenuW (USER32.@)
3687 BOOL WINAPI AppendMenuW( HMENU hMenu, UINT flags,
3688 UINT id, LPCWSTR data )
3690 return InsertMenuW( hMenu, -1, flags | MF_BYPOSITION, id, data );
3694 /**********************************************************************
3695 * RemoveMenu16 (USER.412)
3697 BOOL16 WINAPI RemoveMenu16( HMENU16 hMenu, UINT16 nPos, UINT16 wFlags )
3699 return RemoveMenu( hMenu, nPos, wFlags );
3703 /**********************************************************************
3704 * RemoveMenu (USER32.@)
3706 BOOL WINAPI RemoveMenu( HMENU hMenu, UINT nPos, UINT wFlags )
3708 LPPOPUPMENU menu;
3709 MENUITEM *item;
3711 TRACE("(menu=%04x pos=%04x flags=%04x)\n",hMenu, nPos, wFlags);
3712 if (!(item = MENU_FindItem( &hMenu, &nPos, wFlags ))) return FALSE;
3713 if (!(menu = MENU_GetMenu(hMenu))) return FALSE;
3715 /* Remove item */
3717 MENU_FreeItemData( item );
3719 if (--menu->nItems == 0)
3721 HeapFree( SystemHeap, 0, menu->items );
3722 menu->items = NULL;
3724 else
3726 while(nPos < menu->nItems)
3728 *item = *(item+1);
3729 item++;
3730 nPos++;
3732 menu->items = HeapReAlloc( SystemHeap, 0, menu->items,
3733 menu->nItems * sizeof(MENUITEM) );
3735 return TRUE;
3739 /**********************************************************************
3740 * DeleteMenu16 (USER.413)
3742 BOOL16 WINAPI DeleteMenu16( HMENU16 hMenu, UINT16 nPos, UINT16 wFlags )
3744 return DeleteMenu( hMenu, nPos, wFlags );
3748 /**********************************************************************
3749 * DeleteMenu (USER32.@)
3751 BOOL WINAPI DeleteMenu( HMENU hMenu, UINT nPos, UINT wFlags )
3753 MENUITEM *item = MENU_FindItem( &hMenu, &nPos, wFlags );
3754 if (!item) return FALSE;
3755 if (item->fType & MF_POPUP) DestroyMenu( item->hSubMenu );
3756 /* nPos is now the position of the item */
3757 RemoveMenu( hMenu, nPos, wFlags | MF_BYPOSITION );
3758 return TRUE;
3762 /*******************************************************************
3763 * ModifyMenu16 (USER.414)
3765 BOOL16 WINAPI ModifyMenu16( HMENU16 hMenu, UINT16 pos, UINT16 flags,
3766 UINT16 id, SEGPTR data )
3768 if (IS_STRING_ITEM(flags))
3769 return ModifyMenuA( hMenu, pos, flags, id, MapSL(data) );
3770 return ModifyMenuA( hMenu, pos, flags, id, (LPSTR)data );
3774 /*******************************************************************
3775 * ModifyMenuW (USER32.@)
3777 BOOL WINAPI ModifyMenuW( HMENU hMenu, UINT pos, UINT flags,
3778 UINT id, LPCWSTR str )
3780 MENUITEM *item;
3782 if (IS_STRING_ITEM(flags))
3784 TRACE("%04x %d %04x %04x '%s'\n",
3785 hMenu, pos, flags, id, str ? debugstr_w(str) : "#NULL#" );
3786 if (!str) return FALSE;
3788 else
3790 TRACE("%04x %d %04x %04x %08lx\n",
3791 hMenu, pos, flags, id, (DWORD)str );
3794 if (!(item = MENU_FindItem( &hMenu, &pos, flags ))) return FALSE;
3795 return MENU_SetItemData( item, flags, id, str );
3799 /*******************************************************************
3800 * ModifyMenuA (USER32.@)
3802 BOOL WINAPI ModifyMenuA( HMENU hMenu, UINT pos, UINT flags,
3803 UINT id, LPCSTR str )
3805 BOOL ret;
3807 if (IS_STRING_ITEM(flags) && str)
3809 LPWSTR newstr = HEAP_strdupAtoW( GetProcessHeap(), 0, str );
3810 ret = ModifyMenuW( hMenu, pos, flags, id, newstr );
3811 HeapFree( GetProcessHeap(), 0, newstr );
3812 return ret;
3814 else return ModifyMenuW( hMenu, pos, flags, id, (LPCWSTR)str );
3818 /**********************************************************************
3819 * CreatePopupMenu16 (USER.415)
3821 HMENU16 WINAPI CreatePopupMenu16(void)
3823 return CreatePopupMenu();
3827 /**********************************************************************
3828 * CreatePopupMenu (USER32.@)
3830 HMENU WINAPI CreatePopupMenu(void)
3832 HMENU hmenu;
3833 POPUPMENU *menu;
3835 if (!(hmenu = CreateMenu())) return 0;
3836 menu = (POPUPMENU *) USER_HEAP_LIN_ADDR( hmenu );
3837 menu->wFlags |= MF_POPUP;
3838 menu->bTimeToHide = FALSE;
3839 return hmenu;
3843 /**********************************************************************
3844 * GetMenuCheckMarkDimensions (USER.417) (USER32.@)
3846 DWORD WINAPI GetMenuCheckMarkDimensions(void)
3848 return MAKELONG( GetSystemMetrics(SM_CXMENUCHECK), GetSystemMetrics(SM_CYMENUCHECK) );
3852 /**********************************************************************
3853 * SetMenuItemBitmaps16 (USER.418)
3855 BOOL16 WINAPI SetMenuItemBitmaps16( HMENU16 hMenu, UINT16 nPos, UINT16 wFlags,
3856 HBITMAP16 hNewUnCheck, HBITMAP16 hNewCheck)
3858 return SetMenuItemBitmaps( hMenu, nPos, wFlags, hNewUnCheck, hNewCheck );
3862 /**********************************************************************
3863 * SetMenuItemBitmaps (USER32.@)
3865 BOOL WINAPI SetMenuItemBitmaps( HMENU hMenu, UINT nPos, UINT wFlags,
3866 HBITMAP hNewUnCheck, HBITMAP hNewCheck)
3868 MENUITEM *item;
3869 TRACE("(%04x, %04x, %04x, %04x, %04x)\n",
3870 hMenu, nPos, wFlags, hNewCheck, hNewUnCheck);
3871 if (!(item = MENU_FindItem( &hMenu, &nPos, wFlags ))) return FALSE;
3873 if (!hNewCheck && !hNewUnCheck)
3875 item->fState &= ~MF_USECHECKBITMAPS;
3877 else /* Install new bitmaps */
3879 item->hCheckBit = hNewCheck;
3880 item->hUnCheckBit = hNewUnCheck;
3881 item->fState |= MF_USECHECKBITMAPS;
3883 return TRUE;
3887 /**********************************************************************
3888 * CreateMenu16 (USER.151)
3890 HMENU16 WINAPI CreateMenu16(void)
3892 return CreateMenu();
3896 /**********************************************************************
3897 * CreateMenu (USER32.@)
3899 HMENU WINAPI CreateMenu(void)
3901 HMENU hMenu;
3902 LPPOPUPMENU menu;
3903 if (!(hMenu = USER_HEAP_ALLOC( sizeof(POPUPMENU) ))) return 0;
3904 menu = (LPPOPUPMENU) USER_HEAP_LIN_ADDR(hMenu);
3906 ZeroMemory(menu, sizeof(POPUPMENU));
3907 menu->wMagic = MENU_MAGIC;
3908 menu->FocusedItem = NO_SELECTED_ITEM;
3909 menu->bTimeToHide = FALSE;
3911 TRACE("return %04x\n", hMenu );
3913 return hMenu;
3917 /**********************************************************************
3918 * DestroyMenu16 (USER.152)
3920 BOOL16 WINAPI DestroyMenu16( HMENU16 hMenu )
3922 return DestroyMenu( hMenu );
3926 /**********************************************************************
3927 * DestroyMenu (USER32.@)
3929 BOOL WINAPI DestroyMenu( HMENU hMenu )
3931 TRACE("(%04x)\n", hMenu);
3933 /* Silently ignore attempts to destroy default system popup */
3935 if (hMenu && hMenu != MENU_DefSysPopup)
3937 LPPOPUPMENU lppop = (LPPOPUPMENU) USER_HEAP_LIN_ADDR(hMenu);
3938 WND *pTPWnd = MENU_GetTopPopupWnd();
3940 if( pTPWnd && (hMenu == *(HMENU*)pTPWnd->wExtra) )
3941 *(UINT*)pTPWnd->wExtra = 0;
3943 if (!IS_A_MENU(lppop)) lppop = NULL;
3944 if ( lppop )
3946 lppop->wMagic = 0; /* Mark it as destroyed */
3948 if ((lppop->wFlags & MF_POPUP) && lppop->hWnd &&
3949 (!pTPWnd || (lppop->hWnd != pTPWnd->hwndSelf)))
3950 DestroyWindow( lppop->hWnd );
3952 if (lppop->items) /* recursively destroy submenus */
3954 int i;
3955 MENUITEM *item = lppop->items;
3956 for (i = lppop->nItems; i > 0; i--, item++)
3958 if (item->fType & MF_POPUP) DestroyMenu(item->hSubMenu);
3959 MENU_FreeItemData( item );
3961 HeapFree( SystemHeap, 0, lppop->items );
3963 USER_HEAP_FREE( hMenu );
3964 MENU_ReleaseTopPopupWnd();
3966 else
3968 MENU_ReleaseTopPopupWnd();
3969 return FALSE;
3972 return (hMenu != MENU_DefSysPopup);
3976 /**********************************************************************
3977 * GetSystemMenu16 (USER.156)
3979 HMENU16 WINAPI GetSystemMenu16( HWND16 hWnd, BOOL16 bRevert )
3981 return GetSystemMenu( hWnd, bRevert );
3985 /**********************************************************************
3986 * GetSystemMenu (USER32.@)
3988 HMENU WINAPI GetSystemMenu( HWND hWnd, BOOL bRevert )
3990 WND *wndPtr = WIN_FindWndPtr( hWnd );
3991 HMENU retvalue = 0;
3993 if (wndPtr)
3995 if( wndPtr->hSysMenu )
3997 if( bRevert )
3999 DestroyMenu(wndPtr->hSysMenu);
4000 wndPtr->hSysMenu = 0;
4002 else
4004 POPUPMENU *menu = MENU_GetMenu( wndPtr->hSysMenu );
4005 if( menu )
4007 if( menu->nItems > 0 && menu->items[0].hSubMenu == MENU_DefSysPopup )
4008 menu->items[0].hSubMenu = MENU_CopySysPopup();
4010 else
4012 WARN("Current sys-menu (%04x) of wnd %04x is broken\n",
4013 wndPtr->hSysMenu, hWnd);
4014 wndPtr->hSysMenu = 0;
4019 if(!wndPtr->hSysMenu && (wndPtr->dwStyle & WS_SYSMENU) )
4020 wndPtr->hSysMenu = MENU_GetSysMenu( hWnd, (HMENU)(-1) );
4022 if( wndPtr->hSysMenu )
4024 POPUPMENU *menu;
4025 retvalue = GetSubMenu16(wndPtr->hSysMenu, 0);
4027 /* Store the dummy sysmenu handle to facilitate the refresh */
4028 /* of the close button if the SC_CLOSE item change */
4029 menu = MENU_GetMenu(retvalue);
4030 if ( menu )
4031 menu->hSysMenuOwner = wndPtr->hSysMenu;
4033 WIN_ReleaseWndPtr(wndPtr);
4035 return bRevert ? 0 : retvalue;
4039 /*******************************************************************
4040 * SetSystemMenu16 (USER.280)
4042 BOOL16 WINAPI SetSystemMenu16( HWND16 hwnd, HMENU16 hMenu )
4044 return SetSystemMenu( hwnd, hMenu );
4048 /*******************************************************************
4049 * SetSystemMenu (USER32.@)
4051 BOOL WINAPI SetSystemMenu( HWND hwnd, HMENU hMenu )
4053 WND *wndPtr = WIN_FindWndPtr(hwnd);
4055 if (wndPtr)
4057 if (wndPtr->hSysMenu) DestroyMenu( wndPtr->hSysMenu );
4058 wndPtr->hSysMenu = MENU_GetSysMenu( hwnd, hMenu );
4059 WIN_ReleaseWndPtr(wndPtr);
4060 return TRUE;
4062 return FALSE;
4066 /**********************************************************************
4067 * GetMenu16 (USER.157)
4069 HMENU16 WINAPI GetMenu16( HWND16 hWnd )
4071 return (HMENU16)GetMenu(hWnd);
4075 /**********************************************************************
4076 * GetMenu (USER32.@)
4078 HMENU WINAPI GetMenu( HWND hWnd )
4080 HMENU retvalue;
4081 WND * wndPtr = WIN_FindWndPtr(hWnd);
4083 if (!wndPtr) return 0;
4085 retvalue = (HMENU)wndPtr->wIDmenu;
4086 TRACE("for %swindow %04x returning %04x\n",
4087 (wndPtr->dwStyle & WS_CHILD) ? "child " : "", hWnd, retvalue);
4088 WIN_ReleaseWndPtr(wndPtr);
4089 return retvalue;
4093 /**********************************************************************
4094 * SetMenu16 (USER.158)
4096 BOOL16 WINAPI SetMenu16( HWND16 hWnd, HMENU16 hMenu )
4098 return SetMenu( hWnd, hMenu );
4102 /**********************************************************************
4103 * SetMenu (USER32.@)
4105 BOOL WINAPI SetMenu( HWND hWnd, HMENU hMenu )
4107 WND * wndPtr = WIN_FindWndPtr(hWnd);
4108 BOOL res = FALSE;
4110 TRACE("(%04x, %04x);\n", hWnd, hMenu);
4112 if (hMenu && !IsMenu(hMenu))
4114 WARN("hMenu is not a menu handle\n");
4115 goto exit;
4118 if (wndPtr && !(wndPtr->dwStyle & WS_CHILD))
4120 if (GetCapture() == hWnd) ReleaseCapture();
4122 wndPtr->wIDmenu = (UINT)hMenu;
4123 if (hMenu != 0)
4125 LPPOPUPMENU lpmenu;
4127 if (!(lpmenu = MENU_GetMenu(hMenu)))
4128 goto exit;
4130 lpmenu->hWnd = hWnd;
4131 lpmenu->Height = 0; /* Make sure we recalculate the size */
4133 if (IsWindowVisible(hWnd))
4134 SetWindowPos( hWnd, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE |
4135 SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED );
4136 res = TRUE;
4138 exit:
4139 WIN_ReleaseWndPtr(wndPtr);
4140 return res;
4145 /**********************************************************************
4146 * GetSubMenu16 (USER.159)
4148 HMENU16 WINAPI GetSubMenu16( HMENU16 hMenu, INT16 nPos )
4150 return GetSubMenu( hMenu, nPos );
4154 /**********************************************************************
4155 * GetSubMenu (USER32.@)
4157 HMENU WINAPI GetSubMenu( HMENU hMenu, INT nPos )
4159 MENUITEM * lpmi;
4161 if (!(lpmi = MENU_FindItem(&hMenu,&nPos,MF_BYPOSITION))) return 0;
4162 if (!(lpmi->fType & MF_POPUP)) return 0;
4163 return lpmi->hSubMenu;
4167 /**********************************************************************
4168 * DrawMenuBar16 (USER.160)
4170 void WINAPI DrawMenuBar16( HWND16 hWnd )
4172 DrawMenuBar( hWnd );
4176 /**********************************************************************
4177 * DrawMenuBar (USER32.@)
4179 BOOL WINAPI DrawMenuBar( HWND hWnd )
4181 LPPOPUPMENU lppop;
4182 WND *wndPtr = WIN_FindWndPtr(hWnd);
4183 if (wndPtr && !(wndPtr->dwStyle & WS_CHILD) && wndPtr->wIDmenu)
4185 lppop = MENU_GetMenu((HMENU16)wndPtr->wIDmenu);
4186 if (lppop == NULL)
4188 WIN_ReleaseWndPtr(wndPtr);
4189 return FALSE;
4192 lppop->Height = 0; /* Make sure we call MENU_MenuBarCalcSize */
4193 lppop->hwndOwner = hWnd;
4194 SetWindowPos( hWnd, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE |
4195 SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED );
4196 WIN_ReleaseWndPtr(wndPtr);
4197 return TRUE;
4199 WIN_ReleaseWndPtr(wndPtr);
4200 return FALSE;
4204 /***********************************************************************
4205 * EndMenu (USER.187) (USER32.@)
4207 void WINAPI EndMenu(void)
4209 /* if we are in the menu code, and it is active */
4210 if (fEndMenu == FALSE && MENU_IsMenuActive())
4212 /* terminate the menu handling code */
4213 fEndMenu = TRUE;
4215 /* needs to be posted to wakeup the internal menu handler */
4216 /* which will now terminate the menu, in the event that */
4217 /* the main window was minimized, or lost focus, so we */
4218 /* don't end up with an orphaned menu */
4219 PostMessageA( pTopPopupWnd->hwndSelf, WM_CANCELMODE, 0, 0);
4224 /***********************************************************************
4225 * LookupMenuHandle (USER.217)
4227 HMENU16 WINAPI LookupMenuHandle16( HMENU16 hmenu, INT16 id )
4229 HMENU hmenu32 = hmenu;
4230 UINT id32 = id;
4231 if (!MENU_FindItem( &hmenu32, &id32, MF_BYCOMMAND )) return 0;
4232 else return hmenu32;
4236 /**********************************************************************
4237 * LoadMenu16 (USER.150)
4239 HMENU16 WINAPI LoadMenu16( HINSTANCE16 instance, LPCSTR name )
4241 HRSRC16 hRsrc;
4242 HGLOBAL16 handle;
4243 HMENU16 hMenu;
4245 TRACE("(%04x,%s)\n", instance, debugres_a(name) );
4247 if (HIWORD(name))
4249 if (name[0] == '#') name = (LPCSTR)atoi( name + 1 );
4252 if (!name) return 0;
4254 /* check for Win32 module */
4255 if (HIWORD(instance)) return LoadMenuA( instance, name );
4256 instance = GetExePtr( instance );
4258 if (!(hRsrc = FindResource16( instance, name, RT_MENUA ))) return 0;
4259 if (!(handle = LoadResource16( instance, hRsrc ))) return 0;
4260 hMenu = LoadMenuIndirect16(LockResource16(handle));
4261 FreeResource16( handle );
4262 return hMenu;
4266 /*****************************************************************
4267 * LoadMenuA (USER32.@)
4269 HMENU WINAPI LoadMenuA( HINSTANCE instance, LPCSTR name )
4271 HRSRC hrsrc = FindResourceA( instance, name, RT_MENUA );
4272 if (!hrsrc) return 0;
4273 return LoadMenuIndirectA( (LPCVOID)LoadResource( instance, hrsrc ));
4277 /*****************************************************************
4278 * LoadMenuW (USER32.@)
4280 HMENU WINAPI LoadMenuW( HINSTANCE instance, LPCWSTR name )
4282 HRSRC hrsrc = FindResourceW( instance, name, RT_MENUW );
4283 if (!hrsrc) return 0;
4284 return LoadMenuIndirectW( (LPCVOID)LoadResource( instance, hrsrc ));
4288 /**********************************************************************
4289 * LoadMenuIndirect16 (USER.220)
4291 HMENU16 WINAPI LoadMenuIndirect16( LPCVOID template )
4293 HMENU16 hMenu;
4294 WORD version, offset;
4295 LPCSTR p = (LPCSTR)template;
4297 TRACE("(%p)\n", template );
4298 version = GET_WORD(p);
4299 p += sizeof(WORD);
4300 if (version)
4302 WARN("version must be 0 for Win16\n" );
4303 return 0;
4305 offset = GET_WORD(p);
4306 p += sizeof(WORD) + offset;
4307 if (!(hMenu = CreateMenu())) return 0;
4308 if (!MENU_ParseResource( p, hMenu, FALSE ))
4310 DestroyMenu( hMenu );
4311 return 0;
4313 return hMenu;
4317 /**********************************************************************
4318 * LoadMenuIndirectA (USER32.@)
4320 HMENU WINAPI LoadMenuIndirectA( LPCVOID template )
4322 HMENU16 hMenu;
4323 WORD version, offset;
4324 LPCSTR p = (LPCSTR)template;
4326 TRACE("%p\n", template );
4327 version = GET_WORD(p);
4328 p += sizeof(WORD);
4329 switch (version)
4331 case 0:
4332 offset = GET_WORD(p);
4333 p += sizeof(WORD) + offset;
4334 if (!(hMenu = CreateMenu())) return 0;
4335 if (!MENU_ParseResource( p, hMenu, TRUE ))
4337 DestroyMenu( hMenu );
4338 return 0;
4340 return hMenu;
4341 case 1:
4342 offset = GET_WORD(p);
4343 p += sizeof(WORD) + offset;
4344 if (!(hMenu = CreateMenu())) return 0;
4345 if (!MENUEX_ParseResource( p, hMenu))
4347 DestroyMenu( hMenu );
4348 return 0;
4350 return hMenu;
4351 default:
4352 ERR("version %d not supported.\n", version);
4353 return 0;
4358 /**********************************************************************
4359 * LoadMenuIndirectW (USER32.@)
4361 HMENU WINAPI LoadMenuIndirectW( LPCVOID template )
4363 /* FIXME: is there anything different between A and W? */
4364 return LoadMenuIndirectA( template );
4368 /**********************************************************************
4369 * IsMenu16 (USER.358)
4371 BOOL16 WINAPI IsMenu16( HMENU16 hmenu )
4373 LPPOPUPMENU menu = (LPPOPUPMENU) USER_HEAP_LIN_ADDR(hmenu);
4374 return IS_A_MENU(menu);
4378 /**********************************************************************
4379 * IsMenu (USER32.@)
4381 BOOL WINAPI IsMenu(HMENU hmenu)
4383 LPPOPUPMENU menu = (LPPOPUPMENU) USER_HEAP_LIN_ADDR(hmenu);
4384 return IS_A_MENU(menu);
4387 /**********************************************************************
4388 * GetMenuItemInfo_common
4391 static BOOL GetMenuItemInfo_common ( HMENU hmenu, UINT item, BOOL bypos,
4392 LPMENUITEMINFOW lpmii, BOOL unicode)
4394 MENUITEM *menu = MENU_FindItem (&hmenu, &item, bypos? MF_BYPOSITION : 0);
4396 debug_print_menuitem("GetMenuItemInfo_common: ", menu, "");
4398 if (!menu)
4399 return FALSE;
4401 if (lpmii->fMask & MIIM_TYPE) {
4402 lpmii->fType = menu->fType;
4403 switch (MENU_ITEM_TYPE(menu->fType)) {
4404 case MF_STRING:
4405 break; /* will be done below */
4406 case MF_OWNERDRAW:
4407 case MF_BITMAP:
4408 lpmii->dwTypeData = menu->text;
4409 /* fall through */
4410 default:
4411 lpmii->cch = 0;
4415 /* copy the text string */
4416 if ((lpmii->fMask & (MIIM_TYPE|MIIM_STRING)) &&
4417 (MENU_ITEM_TYPE(menu->fType) == MF_STRING) && menu->text)
4419 int len;
4420 if (unicode)
4422 len = strlenW(menu->text);
4423 if(lpmii->dwTypeData && lpmii->cch)
4424 lstrcpynW(lpmii->dwTypeData, menu->text, lpmii->cch);
4426 else
4428 len = WideCharToMultiByte( CP_ACP, 0, menu->text, -1, NULL, 0, NULL, NULL );
4429 if(lpmii->dwTypeData && lpmii->cch)
4430 if (!WideCharToMultiByte( CP_ACP, 0, menu->text, -1,
4431 (LPSTR)lpmii->dwTypeData, lpmii->cch, NULL, NULL ))
4432 ((LPSTR)lpmii->dwTypeData)[lpmii->cch-1] = 0;
4434 /* if we've copied a substring we return its length */
4435 if(lpmii->dwTypeData && lpmii->cch)
4437 if (lpmii->cch <= len) lpmii->cch--;
4439 else /* return length of string */
4440 lpmii->cch = len;
4443 if (lpmii->fMask & MIIM_FTYPE)
4444 lpmii->fType = menu->fType;
4446 if (lpmii->fMask & MIIM_BITMAP)
4447 lpmii->hbmpItem = menu->hbmpItem;
4449 if (lpmii->fMask & MIIM_STATE)
4450 lpmii->fState = menu->fState;
4452 if (lpmii->fMask & MIIM_ID)
4453 lpmii->wID = menu->wID;
4455 if (lpmii->fMask & MIIM_SUBMENU)
4456 lpmii->hSubMenu = menu->hSubMenu;
4458 if (lpmii->fMask & MIIM_CHECKMARKS) {
4459 lpmii->hbmpChecked = menu->hCheckBit;
4460 lpmii->hbmpUnchecked = menu->hUnCheckBit;
4462 if (lpmii->fMask & MIIM_DATA)
4463 lpmii->dwItemData = menu->dwItemData;
4465 return TRUE;
4468 /**********************************************************************
4469 * GetMenuItemInfoA (USER32.@)
4471 BOOL WINAPI GetMenuItemInfoA( HMENU hmenu, UINT item, BOOL bypos,
4472 LPMENUITEMINFOA lpmii)
4474 return GetMenuItemInfo_common (hmenu, item, bypos,
4475 (LPMENUITEMINFOW)lpmii, FALSE);
4478 /**********************************************************************
4479 * GetMenuItemInfoW (USER32.@)
4481 BOOL WINAPI GetMenuItemInfoW( HMENU hmenu, UINT item, BOOL bypos,
4482 LPMENUITEMINFOW lpmii)
4484 return GetMenuItemInfo_common (hmenu, item, bypos,
4485 lpmii, TRUE);
4488 /**********************************************************************
4489 * SetMenuItemInfo_common
4492 static BOOL SetMenuItemInfo_common(MENUITEM * menu,
4493 const MENUITEMINFOW *lpmii,
4494 BOOL unicode)
4496 if (!menu) return FALSE;
4498 if (lpmii->fMask & MIIM_TYPE ) {
4499 /* Get rid of old string. */
4500 if ( IS_STRING_ITEM(menu->fType) && menu->text) {
4501 HeapFree(SystemHeap, 0, menu->text);
4502 menu->text = NULL;
4505 /* make only MENU_ITEM_TYPE bits in menu->fType equal lpmii->fType */
4506 menu->fType &= ~MENU_ITEM_TYPE(menu->fType);
4507 menu->fType |= MENU_ITEM_TYPE(lpmii->fType);
4509 menu->text = lpmii->dwTypeData;
4511 if (IS_STRING_ITEM(menu->fType) && menu->text) {
4512 if (unicode)
4513 menu->text = HEAP_strdupW(SystemHeap, 0, lpmii->dwTypeData);
4514 else
4515 menu->text = HEAP_strdupAtoW(SystemHeap, 0, (LPSTR)lpmii->dwTypeData);
4519 if (lpmii->fMask & MIIM_FTYPE ) {
4520 /* free the string when the type is changing */
4521 if ( (!IS_STRING_ITEM(lpmii->fType)) && IS_STRING_ITEM(menu->fType) && menu->text) {
4522 HeapFree(SystemHeap, 0, menu->text);
4523 menu->text = NULL;
4525 menu->fType &= ~MENU_ITEM_TYPE(menu->fType);
4526 menu->fType |= MENU_ITEM_TYPE(lpmii->fType);
4529 if (lpmii->fMask & MIIM_STRING ) {
4530 /* free the string when used */
4531 if ( IS_STRING_ITEM(menu->fType) && menu->text) {
4532 HeapFree(SystemHeap, 0, menu->text);
4533 if (unicode)
4534 menu->text = HEAP_strdupW(SystemHeap, 0, lpmii->dwTypeData);
4535 else
4536 menu->text = HEAP_strdupAtoW(SystemHeap, 0, (LPSTR) lpmii->dwTypeData);
4540 if (lpmii->fMask & MIIM_STATE)
4542 /* fixme: MFS_DEFAULT do we have to reset the other menu items? */
4543 menu->fState = lpmii->fState;
4546 if (lpmii->fMask & MIIM_ID)
4547 menu->wID = lpmii->wID;
4549 if (lpmii->fMask & MIIM_SUBMENU) {
4550 menu->hSubMenu = lpmii->hSubMenu;
4551 if (menu->hSubMenu) {
4552 POPUPMENU *subMenu = MENU_GetMenu((UINT16)menu->hSubMenu);
4553 if (subMenu) {
4554 subMenu->wFlags |= MF_POPUP;
4555 menu->fType |= MF_POPUP;
4557 else
4558 /* FIXME: Return an error ? */
4559 menu->fType &= ~MF_POPUP;
4561 else
4562 menu->fType &= ~MF_POPUP;
4565 if (lpmii->fMask & MIIM_CHECKMARKS)
4567 if (lpmii->fType & MFT_RADIOCHECK)
4568 menu->fType |= MFT_RADIOCHECK;
4570 menu->hCheckBit = lpmii->hbmpChecked;
4571 menu->hUnCheckBit = lpmii->hbmpUnchecked;
4573 if (lpmii->fMask & MIIM_DATA)
4574 menu->dwItemData = lpmii->dwItemData;
4576 debug_print_menuitem("SetMenuItemInfo_common: ", menu, "");
4577 return TRUE;
4580 /**********************************************************************
4581 * SetMenuItemInfoA (USER32.@)
4583 BOOL WINAPI SetMenuItemInfoA(HMENU hmenu, UINT item, BOOL bypos,
4584 const MENUITEMINFOA *lpmii)
4586 return SetMenuItemInfo_common(MENU_FindItem(&hmenu, &item, bypos? MF_BYPOSITION : 0),
4587 (const MENUITEMINFOW *)lpmii, FALSE);
4590 /**********************************************************************
4591 * SetMenuItemInfoW (USER32.@)
4593 BOOL WINAPI SetMenuItemInfoW(HMENU hmenu, UINT item, BOOL bypos,
4594 const MENUITEMINFOW *lpmii)
4596 return SetMenuItemInfo_common(MENU_FindItem(&hmenu, &item, bypos? MF_BYPOSITION : 0),
4597 lpmii, TRUE);
4600 /**********************************************************************
4601 * SetMenuDefaultItem (USER32.@)
4604 BOOL WINAPI SetMenuDefaultItem(HMENU hmenu, UINT uItem, UINT bypos)
4606 UINT i;
4607 POPUPMENU *menu;
4608 MENUITEM *item;
4610 TRACE("(0x%x,%d,%d)\n", hmenu, uItem, bypos);
4612 if (!(menu = MENU_GetMenu(hmenu))) return FALSE;
4614 /* reset all default-item flags */
4615 item = menu->items;
4616 for (i = 0; i < menu->nItems; i++, item++)
4618 item->fState &= ~MFS_DEFAULT;
4621 /* no default item */
4622 if ( -1 == uItem)
4624 return TRUE;
4627 item = menu->items;
4628 if ( bypos )
4630 if ( uItem >= menu->nItems ) return FALSE;
4631 item[uItem].fState |= MFS_DEFAULT;
4632 return TRUE;
4634 else
4636 for (i = 0; i < menu->nItems; i++, item++)
4638 if (item->wID == uItem)
4640 item->fState |= MFS_DEFAULT;
4641 return TRUE;
4646 return FALSE;
4649 /**********************************************************************
4650 * GetMenuDefaultItem (USER32.@)
4652 UINT WINAPI GetMenuDefaultItem(HMENU hmenu, UINT bypos, UINT flags)
4654 POPUPMENU *menu;
4655 MENUITEM * item;
4656 UINT i = 0;
4658 TRACE("(0x%x,%d,%d)\n", hmenu, bypos, flags);
4660 if (!(menu = MENU_GetMenu(hmenu))) return -1;
4662 /* find default item */
4663 item = menu->items;
4665 /* empty menu */
4666 if (! item) return -1;
4668 while ( !( item->fState & MFS_DEFAULT ) )
4670 i++; item++;
4671 if (i >= menu->nItems ) return -1;
4674 /* default: don't return disabled items */
4675 if ( (!(GMDI_USEDISABLED & flags)) && (item->fState & MFS_DISABLED )) return -1;
4677 /* search rekursiv when needed */
4678 if ( (item->fType & MF_POPUP) && (flags & GMDI_GOINTOPOPUPS) )
4680 UINT ret;
4681 ret = GetMenuDefaultItem( item->hSubMenu, bypos, flags );
4682 if ( -1 != ret ) return ret;
4684 /* when item not found in submenu, return the popup item */
4686 return ( bypos ) ? i : item->wID;
4690 /*******************************************************************
4691 * InsertMenuItem16 (USER.441)
4693 * FIXME: untested
4695 BOOL16 WINAPI InsertMenuItem16( HMENU16 hmenu, UINT16 pos, BOOL16 byposition,
4696 const MENUITEMINFO16 *mii )
4698 MENUITEMINFOA miia;
4700 miia.cbSize = sizeof(miia);
4701 miia.fMask = mii->fMask;
4702 miia.dwTypeData = (LPSTR)mii->dwTypeData;
4703 miia.fType = mii->fType;
4704 miia.fState = mii->fState;
4705 miia.wID = mii->wID;
4706 miia.hSubMenu = mii->hSubMenu;
4707 miia.hbmpChecked = mii->hbmpChecked;
4708 miia.hbmpUnchecked = mii->hbmpUnchecked;
4709 miia.dwItemData = mii->dwItemData;
4710 miia.cch = mii->cch;
4711 if (IS_STRING_ITEM(miia.fType))
4712 miia.dwTypeData = MapSL(mii->dwTypeData);
4713 return InsertMenuItemA( hmenu, pos, byposition, &miia );
4717 /**********************************************************************
4718 * InsertMenuItemA (USER32.@)
4720 BOOL WINAPI InsertMenuItemA(HMENU hMenu, UINT uItem, BOOL bypos,
4721 const MENUITEMINFOA *lpmii)
4723 MENUITEM *item = MENU_InsertItem(hMenu, uItem, bypos ? MF_BYPOSITION : 0 );
4724 return SetMenuItemInfo_common(item, (const MENUITEMINFOW *)lpmii, FALSE);
4728 /**********************************************************************
4729 * InsertMenuItemW (USER32.@)
4731 BOOL WINAPI InsertMenuItemW(HMENU hMenu, UINT uItem, BOOL bypos,
4732 const MENUITEMINFOW *lpmii)
4734 MENUITEM *item = MENU_InsertItem(hMenu, uItem, bypos ? MF_BYPOSITION : 0 );
4735 return SetMenuItemInfo_common(item, lpmii, TRUE);
4738 /**********************************************************************
4739 * CheckMenuRadioItem (USER32.@)
4742 BOOL WINAPI CheckMenuRadioItem(HMENU hMenu,
4743 UINT first, UINT last, UINT check,
4744 UINT bypos)
4746 MENUITEM *mifirst, *milast, *micheck;
4747 HMENU mfirst = hMenu, mlast = hMenu, mcheck = hMenu;
4749 TRACE("ox%x: %d-%d, check %d, bypos=%d\n",
4750 hMenu, first, last, check, bypos);
4752 mifirst = MENU_FindItem (&mfirst, &first, bypos);
4753 milast = MENU_FindItem (&mlast, &last, bypos);
4754 micheck = MENU_FindItem (&mcheck, &check, bypos);
4756 if (mifirst == NULL || milast == NULL || micheck == NULL ||
4757 mifirst > milast || mfirst != mlast || mfirst != mcheck ||
4758 micheck > milast || micheck < mifirst)
4759 return FALSE;
4761 while (mifirst <= milast)
4763 if (mifirst == micheck)
4765 mifirst->fType |= MFT_RADIOCHECK;
4766 mifirst->fState |= MFS_CHECKED;
4767 } else {
4768 mifirst->fType &= ~MFT_RADIOCHECK;
4769 mifirst->fState &= ~MFS_CHECKED;
4771 mifirst++;
4774 return TRUE;
4777 /**********************************************************************
4778 * CheckMenuRadioItem16 (not a Windows API)
4781 BOOL16 WINAPI CheckMenuRadioItem16(HMENU16 hMenu,
4782 UINT16 first, UINT16 last, UINT16 check,
4783 BOOL16 bypos)
4785 return CheckMenuRadioItem (hMenu, first, last, check, bypos);
4788 /**********************************************************************
4789 * GetMenuItemRect (USER32.@)
4791 * ATTENTION: Here, the returned values in rect are the screen
4792 * coordinates of the item just like if the menu was
4793 * always on the upper left side of the application.
4796 BOOL WINAPI GetMenuItemRect (HWND hwnd, HMENU hMenu, UINT uItem,
4797 LPRECT rect)
4799 POPUPMENU *itemMenu;
4800 MENUITEM *item;
4801 HWND referenceHwnd;
4803 TRACE("(0x%x,0x%x,%d,%p)\n", hwnd, hMenu, uItem, rect);
4805 item = MENU_FindItem (&hMenu, &uItem, MF_BYPOSITION);
4806 referenceHwnd = hwnd;
4808 if(!hwnd)
4810 itemMenu = MENU_GetMenu(hMenu);
4811 if (itemMenu == NULL)
4812 return FALSE;
4814 if(itemMenu->hWnd == 0)
4815 return FALSE;
4816 referenceHwnd = itemMenu->hWnd;
4819 if ((rect == NULL) || (item == NULL))
4820 return FALSE;
4822 *rect = item->rect;
4824 MapWindowPoints(referenceHwnd, 0, (LPPOINT)rect, 2);
4826 return TRUE;
4829 /**********************************************************************
4830 * GetMenuItemRect16 (USER.665)
4833 BOOL16 WINAPI GetMenuItemRect16 (HWND16 hwnd, HMENU16 hMenu, UINT16 uItem,
4834 LPRECT16 rect)
4836 RECT r32;
4837 BOOL res;
4839 if (!rect) return FALSE;
4840 res = GetMenuItemRect (hwnd, hMenu, uItem, &r32);
4841 CONV_RECT32TO16 (&r32, rect);
4842 return res;
4845 /**********************************************************************
4846 * SetMenuInfo (USER32.@)
4848 * FIXME
4849 * MIM_APPLYTOSUBMENUS
4850 * actually use the items to draw the menu
4852 BOOL WINAPI SetMenuInfo (HMENU hMenu, LPCMENUINFO lpmi)
4854 POPUPMENU *menu;
4856 TRACE("(0x%04x %p)\n", hMenu, lpmi);
4858 if (lpmi && (lpmi->cbSize==sizeof(MENUINFO)) && (menu = MENU_GetMenu(hMenu)))
4861 if (lpmi->fMask & MIM_BACKGROUND)
4862 menu->hbrBack = lpmi->hbrBack;
4864 if (lpmi->fMask & MIM_HELPID)
4865 menu->dwContextHelpID = lpmi->dwContextHelpID;
4867 if (lpmi->fMask & MIM_MAXHEIGHT)
4868 menu->cyMax = lpmi->cyMax;
4870 if (lpmi->fMask & MIM_MENUDATA)
4871 menu->dwMenuData = lpmi->dwMenuData;
4873 if (lpmi->fMask & MIM_STYLE)
4874 menu->dwStyle = lpmi->dwStyle;
4876 return TRUE;
4878 return FALSE;
4881 /**********************************************************************
4882 * GetMenuInfo (USER32.@)
4884 * NOTES
4885 * win98/NT5.0
4888 BOOL WINAPI GetMenuInfo (HMENU hMenu, LPMENUINFO lpmi)
4889 { POPUPMENU *menu;
4891 TRACE("(0x%04x %p)\n", hMenu, lpmi);
4893 if (lpmi && (menu = MENU_GetMenu(hMenu)))
4896 if (lpmi->fMask & MIM_BACKGROUND)
4897 lpmi->hbrBack = menu->hbrBack;
4899 if (lpmi->fMask & MIM_HELPID)
4900 lpmi->dwContextHelpID = menu->dwContextHelpID;
4902 if (lpmi->fMask & MIM_MAXHEIGHT)
4903 lpmi->cyMax = menu->cyMax;
4905 if (lpmi->fMask & MIM_MENUDATA)
4906 lpmi->dwMenuData = menu->dwMenuData;
4908 if (lpmi->fMask & MIM_STYLE)
4909 lpmi->dwStyle = menu->dwStyle;
4911 return TRUE;
4913 return FALSE;
4916 /**********************************************************************
4917 * SetMenuContextHelpId16 (USER.384)
4919 BOOL16 WINAPI SetMenuContextHelpId16( HMENU16 hMenu, DWORD dwContextHelpID)
4921 return SetMenuContextHelpId( hMenu, dwContextHelpID );
4925 /**********************************************************************
4926 * SetMenuContextHelpId (USER32.@)
4928 BOOL WINAPI SetMenuContextHelpId( HMENU hMenu, DWORD dwContextHelpID)
4930 LPPOPUPMENU menu;
4932 TRACE("(0x%04x 0x%08lx)\n", hMenu, dwContextHelpID);
4934 if ((menu = MENU_GetMenu(hMenu)))
4936 menu->dwContextHelpID = dwContextHelpID;
4937 return TRUE;
4939 return FALSE;
4942 /**********************************************************************
4943 * GetMenuContextHelpId16 (USER.385)
4945 DWORD WINAPI GetMenuContextHelpId16( HMENU16 hMenu )
4947 return GetMenuContextHelpId( hMenu );
4950 /**********************************************************************
4951 * GetMenuContextHelpId (USER32.@)
4953 DWORD WINAPI GetMenuContextHelpId( HMENU hMenu )
4955 LPPOPUPMENU menu;
4957 TRACE("(0x%04x)\n", hMenu);
4959 if ((menu = MENU_GetMenu(hMenu)))
4961 return menu->dwContextHelpID;
4963 return 0;
4966 /**********************************************************************
4967 * MenuItemFromPoint (USER32.@)
4969 UINT WINAPI MenuItemFromPoint(HWND hWnd, HMENU hMenu, POINT ptScreen)
4971 FIXME("(0x%04x,0x%04x,(%ld,%ld)):stub\n",
4972 hWnd, hMenu, ptScreen.x, ptScreen.y);
4973 return 0;
4977 /**********************************************************************
4978 * translate_accelerator
4980 static BOOL translate_accelerator( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam,
4981 BYTE fVirt, WORD key, WORD cmd )
4983 UINT mesg = 0;
4985 if (wParam != key) return FALSE;
4987 if (message == WM_CHAR)
4989 if ( !(fVirt & FALT) && !(fVirt & FVIRTKEY) )
4991 TRACE_(accel)("found accel for WM_CHAR: ('%c')\n", wParam & 0xff);
4992 goto found;
4995 else
4997 if(fVirt & FVIRTKEY)
4999 INT mask = 0;
5000 TRACE_(accel)("found accel for virt_key %04x (scan %04x)\n",
5001 wParam, 0xff & HIWORD(lParam));
5002 if(GetKeyState(VK_SHIFT) & 0x8000) mask |= FSHIFT;
5003 if(GetKeyState(VK_CONTROL) & 0x8000) mask |= FCONTROL;
5004 if(GetKeyState(VK_MENU) & 0x8000) mask |= FALT;
5005 if(mask == (fVirt & (FSHIFT | FCONTROL | FALT))) goto found;
5006 TRACE_(accel)(", but incorrect SHIFT/CTRL/ALT-state\n");
5008 else
5010 if (!(lParam & 0x01000000)) /* no special_key */
5012 if ((fVirt & FALT) && (lParam & 0x20000000))
5013 { /* ^^ ALT pressed */
5014 TRACE_(accel)("found accel for Alt-%c\n", wParam & 0xff);
5015 goto found;
5020 return FALSE;
5022 found:
5023 if (message == WM_KEYUP || message == WM_SYSKEYUP)
5024 mesg = 1;
5025 else if (GetCapture())
5026 mesg = 2;
5027 else if (!IsWindowEnabled(hWnd))
5028 mesg = 3;
5029 else
5031 HMENU hMenu, hSubMenu, hSysMenu;
5032 UINT uSysStat = (UINT)-1, uStat = (UINT)-1, nPos;
5033 WND* wndPtr = WIN_FindWndPtr(hWnd);
5035 hMenu = (wndPtr->dwStyle & WS_CHILD) ? 0 : (HMENU)wndPtr->wIDmenu;
5036 hSysMenu = wndPtr->hSysMenu;
5037 WIN_ReleaseWndPtr(wndPtr);
5039 /* find menu item and ask application to initialize it */
5040 /* 1. in the system menu */
5041 hSubMenu = hSysMenu;
5042 nPos = cmd;
5043 if(MENU_FindItem(&hSubMenu, &nPos, MF_BYCOMMAND))
5045 SendMessageA(hWnd, WM_INITMENU, (WPARAM)hSysMenu, 0L);
5046 if(hSubMenu != hSysMenu)
5048 nPos = MENU_FindSubMenu(&hSysMenu, hSubMenu);
5049 TRACE_(accel)("hSysMenu = %04x, hSubMenu = %04x, nPos = %d\n", hSysMenu, hSubMenu, nPos);
5050 SendMessageA(hWnd, WM_INITMENUPOPUP, (WPARAM)hSubMenu, MAKELPARAM(nPos, TRUE));
5052 uSysStat = GetMenuState(GetSubMenu(hSysMenu, 0), cmd, MF_BYCOMMAND);
5054 else /* 2. in the window's menu */
5056 hSubMenu = hMenu;
5057 nPos = cmd;
5058 if(MENU_FindItem(&hSubMenu, &nPos, MF_BYCOMMAND))
5060 SendMessageA(hWnd, WM_INITMENU, (WPARAM)hMenu, 0L);
5061 if(hSubMenu != hMenu)
5063 nPos = MENU_FindSubMenu(&hMenu, hSubMenu);
5064 TRACE_(accel)("hMenu = %04x, hSubMenu = %04x, nPos = %d\n", hMenu, hSubMenu, nPos);
5065 SendMessageA(hWnd, WM_INITMENUPOPUP, (WPARAM)hSubMenu, MAKELPARAM(nPos, FALSE));
5067 uStat = GetMenuState(hMenu, cmd, MF_BYCOMMAND);
5071 if (uSysStat != (UINT)-1)
5073 if (uSysStat & (MF_DISABLED|MF_GRAYED))
5074 mesg=4;
5075 else
5076 mesg=WM_SYSCOMMAND;
5078 else
5080 if (uStat != (UINT)-1)
5082 if (IsIconic(hWnd))
5083 mesg=5;
5084 else
5086 if (uStat & (MF_DISABLED|MF_GRAYED))
5087 mesg=6;
5088 else
5089 mesg=WM_COMMAND;
5092 else
5093 mesg=WM_COMMAND;
5097 if( mesg==WM_COMMAND )
5099 TRACE_(accel)(", sending WM_COMMAND, wParam=%0x\n", 0x10000 | cmd);
5100 SendMessageA(hWnd, mesg, 0x10000 | cmd, 0L);
5102 else if( mesg==WM_SYSCOMMAND )
5104 TRACE_(accel)(", sending WM_SYSCOMMAND, wParam=%0x\n", cmd);
5105 SendMessageA(hWnd, mesg, cmd, 0x00010000L);
5107 else
5109 /* some reasons for NOT sending the WM_{SYS}COMMAND message:
5110 * #0: unknown (please report!)
5111 * #1: for WM_KEYUP,WM_SYSKEYUP
5112 * #2: mouse is captured
5113 * #3: window is disabled
5114 * #4: it's a disabled system menu option
5115 * #5: it's a menu option, but window is iconic
5116 * #6: it's a menu option, but disabled
5118 TRACE_(accel)(", but won't send WM_{SYS}COMMAND, reason is #%d\n",mesg);
5119 if(mesg==0)
5120 ERR_(accel)(" unknown reason - please report!");
5122 return TRUE;
5125 /**********************************************************************
5126 * TranslateAccelerator (USER32.@)
5128 INT WINAPI TranslateAccelerator( HWND hWnd, HACCEL hAccel, LPMSG msg )
5130 /* YES, Accel16! */
5131 LPACCEL16 lpAccelTbl;
5132 int i;
5134 if (msg == NULL)
5136 WARN_(accel)("msg null; should hang here to be win compatible\n");
5137 return 0;
5139 if (!hAccel || !(lpAccelTbl = (LPACCEL16) LockResource16(hAccel)))
5141 WARN_(accel)("invalid accel handle=%x\n", hAccel);
5142 return 0;
5144 if ((msg->message != WM_KEYDOWN &&
5145 msg->message != WM_KEYUP &&
5146 msg->message != WM_SYSKEYDOWN &&
5147 msg->message != WM_SYSKEYUP &&
5148 msg->message != WM_CHAR)) return 0;
5150 TRACE_(accel)("TranslateAccelerators hAccel=%04x, hWnd=%04x,"
5151 "msg->hwnd=%04x, msg->message=%04x, wParam=%08x, lParam=%lx\n",
5152 hAccel,hWnd,msg->hwnd,msg->message,msg->wParam,msg->lParam);
5154 i = 0;
5157 if (translate_accelerator( hWnd, msg->message, msg->wParam, msg->lParam,
5158 lpAccelTbl[i].fVirt, lpAccelTbl[i].key, lpAccelTbl[i].cmd))
5159 return 1;
5160 } while ((lpAccelTbl[i++].fVirt & 0x80) == 0);
5161 WARN_(accel)("couldn't translate accelerator key\n");
5162 return 0;
5166 /**********************************************************************
5167 * TranslateAccelerator16 (USER.178)
5169 INT16 WINAPI TranslateAccelerator16( HWND16 hWnd, HACCEL16 hAccel, LPMSG16 msg )
5171 LPACCEL16 lpAccelTbl;
5172 int i;
5174 if (msg == NULL)
5176 WARN_(accel)("msg null; should hang here to be win compatible\n");
5177 return 0;
5179 if (!hAccel || !(lpAccelTbl = (LPACCEL16) LockResource16(hAccel)))
5181 WARN_(accel)("invalid accel handle=%x\n", hAccel);
5182 return 0;
5184 if ((msg->message != WM_KEYDOWN &&
5185 msg->message != WM_KEYUP &&
5186 msg->message != WM_SYSKEYDOWN &&
5187 msg->message != WM_SYSKEYUP &&
5188 msg->message != WM_CHAR)) return 0;
5190 TRACE_(accel)("TranslateAccelerators hAccel=%04x, hWnd=%04x,"
5191 "msg->hwnd=%04x, msg->message=%04x, wParam=%04x, lParam=%lx\n",
5192 hAccel,hWnd,msg->hwnd,msg->message,msg->wParam,msg->lParam);
5194 i = 0;
5197 if (translate_accelerator( hWnd, msg->message, msg->wParam, msg->lParam,
5198 lpAccelTbl[i].fVirt, lpAccelTbl[i].key, lpAccelTbl[i].cmd ))
5199 return 1;
5200 } while ((lpAccelTbl[i++].fVirt & 0x80) == 0);
5201 WARN_(accel)("couldn't translate accelerator key\n");
5202 return 0;