4 * Copyright 1993 Martin Ayotte
5 * Copyright 1994 Alexandre Julliard
6 * Copyright 1997 Morten Welinder
7 * Copyright 2005 Maxime Bellengé
8 * Copyright 2006 Phil Krylov
10 * This library is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU Lesser General Public
12 * License as published by the Free Software Foundation; either
13 * version 2.1 of the License, or (at your option) any later version.
15 * This library is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * Lesser General Public License for more details.
20 * You should have received a copy of the GNU Lesser General Public
21 * License along with this library; if not, write to the Free Software
22 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
26 * Note: the style MF_MOUSESELECT is used to mark popup items that
27 * have been selected, i.e. their popup menu is currently displayed.
28 * This is probably not the meaning this style has in MS-Windows.
30 * Note 2: where there is a difference, these menu API's are according
31 * the behavior of Windows 2k and Windows XP. Known differences with
32 * Windows 9x/ME are documented in the comments, in case an application
33 * is found to depend on the old behavior.
43 #include "wine/port.h"
54 #include "wine/server.h"
55 #include "wine/unicode.h"
56 #include "wine/exception.h"
59 #include "user_private.h"
60 #include "wine/debug.h"
62 WINE_DEFAULT_DEBUG_CHANNEL(menu
);
63 WINE_DECLARE_DEBUG_CHANNEL(accel
);
65 /* internal popup menu window messages */
67 #define MM_SETMENUHANDLE (WM_USER + 0)
68 #define MM_GETMENUHANDLE (WM_USER + 1)
70 /* Menu item structure */
72 /* ----------- MENUITEMINFO Stuff ----------- */
73 UINT fType
; /* Item type. */
74 UINT fState
; /* Item state. */
75 UINT_PTR wID
; /* Item id. */
76 HMENU hSubMenu
; /* Pop-up menu. */
77 HBITMAP hCheckBit
; /* Bitmap when checked. */
78 HBITMAP hUnCheckBit
; /* Bitmap when unchecked. */
79 LPWSTR text
; /* Item text. */
80 ULONG_PTR dwItemData
; /* Application defined. */
81 LPWSTR dwTypeData
; /* depends on fMask */
82 HBITMAP hbmpItem
; /* bitmap */
83 /* ----------- Wine stuff ----------- */
84 RECT rect
; /* Item area (relative to menu window) */
85 UINT xTab
; /* X position of text after Tab */
86 SIZE bmpsize
; /* size needed for the HBMMENU_CALLBACK
90 /* Popup menu structure */
92 struct user_object obj
;
93 WORD wFlags
; /* Menu flags (MF_POPUP, MF_SYSMENU) */
94 WORD Width
; /* Width of the whole menu */
95 WORD Height
; /* Height of the whole menu */
96 UINT nItems
; /* Number of items in the menu */
97 HWND hWnd
; /* Window containing the menu */
98 MENUITEM
*items
; /* Array of menu items */
99 UINT FocusedItem
; /* Currently focused item */
100 HWND hwndOwner
; /* window receiving the messages for ownerdraw */
101 BOOL bTimeToHide
; /* Request hiding when receiving a second click in the top-level menu item */
102 BOOL bScrolling
; /* Scroll arrows are active */
103 UINT nScrollPos
; /* Current scroll position */
104 UINT nTotalHeight
; /* Total height of menu items inside menu */
105 /* ------------ MENUINFO members ------ */
106 DWORD dwStyle
; /* Extended menu style */
107 UINT cyMax
; /* max height of the whole menu, 0 is screen height */
108 HBRUSH hbrBack
; /* brush for menu background */
109 DWORD dwContextHelpID
;
110 DWORD dwMenuData
; /* application defined value */
111 HMENU hSysMenuOwner
; /* Handle to the dummy sys menu holder */
112 WORD textOffset
; /* Offset of text when items have both bitmaps and text */
113 } POPUPMENU
, *LPPOPUPMENU
;
115 /* internal flags for menu tracking */
117 #define TF_ENDMENU 0x10000
118 #define TF_SUSPENDPOPUP 0x20000
119 #define TF_SKIPREMOVE 0x40000
124 HMENU hCurrentMenu
; /* current submenu (can be equal to hTopMenu)*/
125 HMENU hTopMenu
; /* initial menu */
126 HWND hOwnerWnd
; /* where notifications are sent */
130 #define MENU_MAGIC 0x554d /* 'MU' */
135 /* Internal MENU_TrackMenu() flags */
136 #define TPM_INTERNAL 0xF0000000
137 #define TPM_BUTTONDOWN 0x40000000 /* menu was clicked before tracking */
138 #define TPM_POPUPMENU 0x20000000 /* menu is a popup menu */
140 /* Space between 2 columns */
141 #define MENU_COL_SPACE 4
143 /* top and bottom margins for popup menus */
144 #define MENU_TOP_MARGIN 3
145 #define MENU_BOTTOM_MARGIN 2
147 /* maximum allowed depth of any branch in the menu tree.
148 * This value is slightly larger than in windows (25) to
149 * stay on the safe side. */
150 #define MAXMENUDEPTH 30
152 /* (other menu->FocusedItem values give the position of the focused item) */
153 #define NO_SELECTED_ITEM 0xffff
155 #define MENU_ITEM_TYPE(flags) \
156 ((flags) & (MF_STRING | MF_BITMAP | MF_OWNERDRAW | MF_SEPARATOR))
158 /* macro to test that flags do not indicate bitmap, ownerdraw or separator */
159 #define IS_STRING_ITEM(flags) (MENU_ITEM_TYPE ((flags)) == MF_STRING)
160 #define IS_MAGIC_BITMAP(id) ((id) && ((INT_PTR)(id) < 12) && ((INT_PTR)(id) >= -1))
162 #define IS_SYSTEM_MENU(menu) \
163 (!((menu)->wFlags & MF_POPUP) && ((menu)->wFlags & MF_SYSMENU))
165 #define MENUITEMINFO_TYPE_MASK \
166 (MFT_STRING | MFT_BITMAP | MFT_OWNERDRAW | MFT_SEPARATOR | \
167 MFT_MENUBARBREAK | MFT_MENUBREAK | MFT_RADIOCHECK | \
168 MFT_RIGHTORDER | MFT_RIGHTJUSTIFY /* same as MF_HELP */ )
169 #define TYPE_MASK (MENUITEMINFO_TYPE_MASK | MF_POPUP | MF_SYSMENU)
170 #define STATE_MASK (~TYPE_MASK)
171 #define MENUITEMINFO_STATE_MASK (STATE_MASK & ~(MF_BYPOSITION | MF_MOUSESELECT))
173 #define WIN_ALLOWED_MENU(style) ((style & (WS_CHILD | WS_POPUP)) != WS_CHILD)
175 static SIZE menucharsize
;
176 static UINT ODitemheight
; /* default owner drawn item height */
178 /* Use global popup window because there's no way 2 menus can
179 * be tracked at the same time. */
180 static HWND top_popup
;
181 static HMENU top_popup_hmenu
;
183 /* Flag set by EndMenu() to force an exit from menu tracking */
184 static BOOL fEndMenu
= FALSE
;
186 DWORD WINAPI
DrawMenuBarTemp(HWND hwnd
, HDC hDC
, LPRECT lprect
, HMENU hMenu
, HFONT hFont
);
188 static BOOL
SetMenuItemInfo_common( MENUITEM
*, const MENUITEMINFOW
*, BOOL
);
190 /*********************************************************************
191 * menu class descriptor
193 const struct builtin_class_descr MENU_builtin_class
=
195 (LPCWSTR
)POPUPMENU_CLASS_ATOM
, /* name */
196 CS_DROPSHADOW
| CS_SAVEBITS
| CS_DBLCLKS
, /* style */
197 WINPROC_MENU
, /* proc */
198 sizeof(HMENU
), /* extra */
199 IDC_ARROW
, /* cursor */
200 (HBRUSH
)(COLOR_MENU
+1) /* brush */
204 /***********************************************************************
205 * debug_print_menuitem
207 * Print a menuitem in readable form.
210 #define debug_print_menuitem(pre, mp, post) \
211 do { if (TRACE_ON(menu)) do_debug_print_menuitem(pre, mp, post); } while (0)
213 #define MENUOUT(text) \
214 TRACE("%s%s", (count++ ? "," : ""), (text))
216 #define MENUFLAG(bit,text) \
218 if (flags & (bit)) { flags &= ~(bit); MENUOUT ((text)); } \
221 static void do_debug_print_menuitem(const char *prefix
, const MENUITEM
*mp
,
224 static const char * const hbmmenus
[] = { "HBMMENU_CALLBACK", "", "HBMMENU_SYSTEM",
225 "HBMMENU_MBAR_RESTORE", "HBMMENU_MBAR_MINIMIZE", "UNKNOWN BITMAP", "HBMMENU_MBAR_CLOSE",
226 "HBMMENU_MBAR_CLOSE_D", "HBMMENU_MBAR_MINIMIZE_D", "HBMMENU_POPUP_CLOSE",
227 "HBMMENU_POPUP_RESTORE", "HBMMENU_POPUP_MAXIMIZE", "HBMMENU_POPUP_MINIMIZE"};
228 TRACE("%s ", prefix
);
230 UINT flags
= mp
->fType
;
231 TRACE( "{ ID=0x%lx", mp
->wID
);
233 TRACE( ", Sub=%p", mp
->hSubMenu
);
237 MENUFLAG( MFT_SEPARATOR
, "sep");
238 MENUFLAG( MFT_OWNERDRAW
, "own");
239 MENUFLAG( MFT_BITMAP
, "bit");
240 MENUFLAG(MF_POPUP
, "pop");
241 MENUFLAG(MFT_MENUBARBREAK
, "barbrk");
242 MENUFLAG(MFT_MENUBREAK
, "brk");
243 MENUFLAG(MFT_RADIOCHECK
, "radio");
244 MENUFLAG(MFT_RIGHTORDER
, "rorder");
245 MENUFLAG(MF_SYSMENU
, "sys");
246 MENUFLAG(MFT_RIGHTJUSTIFY
, "right"); /* same as MF_HELP */
248 TRACE( "+0x%x", flags
);
254 MENUFLAG(MFS_GRAYED
, "grey");
255 MENUFLAG(MFS_DEFAULT
, "default");
256 MENUFLAG(MFS_DISABLED
, "dis");
257 MENUFLAG(MFS_CHECKED
, "check");
258 MENUFLAG(MFS_HILITE
, "hi");
259 MENUFLAG(MF_USECHECKBITMAPS
, "usebit");
260 MENUFLAG(MF_MOUSESELECT
, "mouse");
262 TRACE( "+0x%x", flags
);
265 TRACE( ", Chk=%p", mp
->hCheckBit
);
267 TRACE( ", Unc=%p", mp
->hUnCheckBit
);
269 TRACE( ", Text=%s", debugstr_w(mp
->text
));
271 TRACE( ", ItemData=0x%08lx", mp
->dwItemData
);
274 if( IS_MAGIC_BITMAP(mp
->hbmpItem
))
275 TRACE( ", hbitmap=%s", hbmmenus
[ (INT_PTR
)mp
->hbmpItem
+ 1]);
277 TRACE( ", hbitmap=%p", mp
->hbmpItem
);
282 TRACE(" %s\n", postfix
);
289 /***********************************************************************
292 * Validate the given menu handle and returns the menu structure pointer.
294 static POPUPMENU
*MENU_GetMenu(HMENU hMenu
)
296 POPUPMENU
*menu
= get_user_handle_ptr( hMenu
, USER_MENU
);
298 if (menu
== OBJ_OTHER_PROCESS
)
300 WARN( "other process menu %p?\n", hMenu
);
303 if (menu
) release_user_handle_ptr( menu
); /* FIXME! */
304 else WARN("invalid menu handle=%p\n", hMenu
);
308 /***********************************************************************
311 * Get the system menu of a window
313 static HMENU
get_win_sys_menu( HWND hwnd
)
316 WND
*win
= WIN_GetPtr( hwnd
);
317 if (win
&& win
!= WND_OTHER_PROCESS
&& win
!= WND_DESKTOP
)
320 WIN_ReleasePtr( win
);
325 /***********************************************************************
328 static HFONT
get_menu_font( BOOL bold
)
330 static HFONT hMenuFont
, hMenuFontBold
;
332 HFONT ret
= bold
? hMenuFontBold
: hMenuFont
;
336 NONCLIENTMETRICSW ncm
;
339 ncm
.cbSize
= sizeof(NONCLIENTMETRICSW
);
340 SystemParametersInfoW(SPI_GETNONCLIENTMETRICS
, sizeof(NONCLIENTMETRICSW
), &ncm
, 0);
344 ncm
.lfMenuFont
.lfWeight
+= 300;
345 if (ncm
.lfMenuFont
.lfWeight
> 1000) ncm
.lfMenuFont
.lfWeight
= 1000;
347 if (!(ret
= CreateFontIndirectW( &ncm
.lfMenuFont
))) return 0;
348 prev
= InterlockedCompareExchangePointer( (void **)(bold
? &hMenuFontBold
: &hMenuFont
),
352 /* another thread beat us to it */
360 /***********************************************************************
363 static HBITMAP
get_arrow_bitmap(void)
365 static HBITMAP arrow_bitmap
;
367 if (!arrow_bitmap
) arrow_bitmap
= LoadBitmapW(0, MAKEINTRESOURCEW(OBM_MNARROW
));
371 /***********************************************************************
372 * get_down_arrow_bitmap
374 static HBITMAP
get_down_arrow_bitmap(void)
376 static HBITMAP arrow_bitmap
;
378 if (!arrow_bitmap
) arrow_bitmap
= LoadBitmapW(0, MAKEINTRESOURCEW(OBM_DNARROW
));
382 /***********************************************************************
383 * get_down_arrow_inactive_bitmap
385 static HBITMAP
get_down_arrow_inactive_bitmap(void)
387 static HBITMAP arrow_bitmap
;
389 if (!arrow_bitmap
) arrow_bitmap
= LoadBitmapW(0, MAKEINTRESOURCEW(OBM_DNARROWI
));
393 /***********************************************************************
394 * get_up_arrow_bitmap
396 static HBITMAP
get_up_arrow_bitmap(void)
398 static HBITMAP arrow_bitmap
;
400 if (!arrow_bitmap
) arrow_bitmap
= LoadBitmapW(0, MAKEINTRESOURCEW(OBM_UPARROW
));
404 /***********************************************************************
405 * get_up_arrow_inactive_bitmap
407 static HBITMAP
get_up_arrow_inactive_bitmap(void)
409 static HBITMAP arrow_bitmap
;
411 if (!arrow_bitmap
) arrow_bitmap
= LoadBitmapW(0, MAKEINTRESOURCEW(OBM_UPARROWI
));
415 /***********************************************************************
418 * Return the default system menu.
420 static HMENU
MENU_CopySysPopup(BOOL mdi
)
422 static const WCHAR sysmenuW
[] = {'S','Y','S','M','E','N','U',0};
423 static const WCHAR sysmenumdiW
[] = {'S','Y','S','M','E','N','U','M','D','I',0};
424 HMENU hMenu
= LoadMenuW(user32_module
, (mdi
? sysmenumdiW
: sysmenuW
));
428 MENUITEMINFOW miteminfo
;
429 POPUPMENU
* menu
= MENU_GetMenu(hMenu
);
430 menu
->wFlags
|= MF_SYSMENU
| MF_POPUP
;
431 /* decorate the menu with bitmaps */
432 minfo
.cbSize
= sizeof( MENUINFO
);
433 minfo
.dwStyle
= MNS_CHECKORBMP
;
434 minfo
.fMask
= MIM_STYLE
;
435 SetMenuInfo( hMenu
, &minfo
);
436 miteminfo
.cbSize
= sizeof( MENUITEMINFOW
);
437 miteminfo
.fMask
= MIIM_BITMAP
;
438 miteminfo
.hbmpItem
= HBMMENU_POPUP_CLOSE
;
439 SetMenuItemInfoW( hMenu
, SC_CLOSE
, FALSE
, &miteminfo
);
440 miteminfo
.hbmpItem
= HBMMENU_POPUP_RESTORE
;
441 SetMenuItemInfoW( hMenu
, SC_RESTORE
, FALSE
, &miteminfo
);
442 miteminfo
.hbmpItem
= HBMMENU_POPUP_MAXIMIZE
;
443 SetMenuItemInfoW( hMenu
, SC_MAXIMIZE
, FALSE
, &miteminfo
);
444 miteminfo
.hbmpItem
= HBMMENU_POPUP_MINIMIZE
;
445 SetMenuItemInfoW( hMenu
, SC_MINIMIZE
, FALSE
, &miteminfo
);
446 SetMenuDefaultItem(hMenu
, SC_CLOSE
, FALSE
);
449 ERR("Unable to load default system menu\n" );
451 TRACE("returning %p (mdi=%d).\n", hMenu
, mdi
);
457 /**********************************************************************
460 * Create a copy of the system menu. System menu in Windows is
461 * a special menu bar with the single entry - system menu popup.
462 * This popup is presented to the outside world as a "system menu".
463 * However, the real system menu handle is sometimes seen in the
464 * WM_MENUSELECT parameters (and Word 6 likes it this way).
466 static HMENU
MENU_GetSysMenu( HWND hWnd
, HMENU hPopupMenu
)
470 TRACE("loading system menu, hWnd %p, hPopupMenu %p\n", hWnd
, hPopupMenu
);
471 if ((hMenu
= CreateMenu()))
473 POPUPMENU
*menu
= MENU_GetMenu(hMenu
);
474 menu
->wFlags
= MF_SYSMENU
;
475 menu
->hWnd
= WIN_GetFullHandle( hWnd
);
476 TRACE("hWnd %p (hMenu %p)\n", menu
->hWnd
, hMenu
);
480 if (GetWindowLongW(hWnd
, GWL_EXSTYLE
) & WS_EX_MDICHILD
)
481 hPopupMenu
= MENU_CopySysPopup(TRUE
);
483 hPopupMenu
= MENU_CopySysPopup(FALSE
);
488 if (GetClassLongW(hWnd
, GCL_STYLE
) & CS_NOCLOSE
)
489 DeleteMenu(hPopupMenu
, SC_CLOSE
, MF_BYCOMMAND
);
491 InsertMenuW( hMenu
, -1, MF_SYSMENU
| MF_POPUP
| MF_BYPOSITION
,
492 (UINT_PTR
)hPopupMenu
, NULL
);
494 menu
->items
[0].fType
= MF_SYSMENU
| MF_POPUP
;
495 menu
->items
[0].fState
= 0;
496 if ((menu
= MENU_GetMenu(hPopupMenu
))) menu
->wFlags
|= MF_SYSMENU
;
498 TRACE("hMenu=%p (hPopup %p)\n", hMenu
, hPopupMenu
);
501 DestroyMenu( hMenu
);
503 ERR("failed to load system menu!\n");
508 /***********************************************************************
509 * MENU_InitSysMenuPopup
511 * Grey the appropriate items in System menu.
513 static void MENU_InitSysMenuPopup( HMENU hmenu
, DWORD style
, DWORD clsStyle
)
517 gray
= !(style
& WS_THICKFRAME
) || (style
& (WS_MAXIMIZE
| WS_MINIMIZE
));
518 EnableMenuItem( hmenu
, SC_SIZE
, (gray
? MF_GRAYED
: MF_ENABLED
) );
519 gray
= ((style
& WS_MAXIMIZE
) != 0);
520 EnableMenuItem( hmenu
, SC_MOVE
, (gray
? MF_GRAYED
: MF_ENABLED
) );
521 gray
= !(style
& WS_MINIMIZEBOX
) || (style
& WS_MINIMIZE
);
522 EnableMenuItem( hmenu
, SC_MINIMIZE
, (gray
? MF_GRAYED
: MF_ENABLED
) );
523 gray
= !(style
& WS_MAXIMIZEBOX
) || (style
& WS_MAXIMIZE
);
524 EnableMenuItem( hmenu
, SC_MAXIMIZE
, (gray
? MF_GRAYED
: MF_ENABLED
) );
525 gray
= !(style
& (WS_MAXIMIZE
| WS_MINIMIZE
));
526 EnableMenuItem( hmenu
, SC_RESTORE
, (gray
? MF_GRAYED
: MF_ENABLED
) );
527 gray
= (clsStyle
& CS_NOCLOSE
) != 0;
529 /* The menu item must keep its state if it's disabled */
531 EnableMenuItem( hmenu
, SC_CLOSE
, MF_GRAYED
);
535 /******************************************************************************
537 * UINT MENU_GetStartOfNextColumn(
540 *****************************************************************************/
542 static UINT
MENU_GetStartOfNextColumn(
545 POPUPMENU
*menu
= MENU_GetMenu(hMenu
);
549 return NO_SELECTED_ITEM
;
551 i
= menu
->FocusedItem
+ 1;
552 if( i
== NO_SELECTED_ITEM
)
555 for( ; i
< menu
->nItems
; ++i
) {
556 if (menu
->items
[i
].fType
& (MF_MENUBREAK
| MF_MENUBARBREAK
))
560 return NO_SELECTED_ITEM
;
564 /******************************************************************************
566 * UINT MENU_GetStartOfPrevColumn(
569 *****************************************************************************/
571 static UINT
MENU_GetStartOfPrevColumn(
574 POPUPMENU
*menu
= MENU_GetMenu(hMenu
);
578 return NO_SELECTED_ITEM
;
580 if( menu
->FocusedItem
== 0 || menu
->FocusedItem
== NO_SELECTED_ITEM
)
581 return NO_SELECTED_ITEM
;
583 /* Find the start of the column */
585 for(i
= menu
->FocusedItem
; i
!= 0 &&
586 !(menu
->items
[i
].fType
& (MF_MENUBREAK
| MF_MENUBARBREAK
));
590 return NO_SELECTED_ITEM
;
592 for(--i
; i
!= 0; --i
) {
593 if (menu
->items
[i
].fType
& (MF_MENUBREAK
| MF_MENUBARBREAK
))
597 TRACE("ret %d.\n", i
);
604 /***********************************************************************
607 * Find a menu item. Return a pointer on the item, and modifies *hmenu
608 * in case the item was in a sub-menu.
610 static MENUITEM
*MENU_FindItem( HMENU
*hmenu
, UINT
*nPos
, UINT wFlags
)
613 MENUITEM
*fallback
= NULL
;
614 UINT fallback_pos
= 0;
617 if ((*hmenu
== (HMENU
)0xffff) || (!(menu
= MENU_GetMenu(*hmenu
)))) return NULL
;
618 if (wFlags
& MF_BYPOSITION
)
620 if (*nPos
>= menu
->nItems
) return NULL
;
621 return &menu
->items
[*nPos
];
625 MENUITEM
*item
= menu
->items
;
626 for (i
= 0; i
< menu
->nItems
; i
++, item
++)
628 if (item
->fType
& MF_POPUP
)
630 HMENU hsubmenu
= item
->hSubMenu
;
631 MENUITEM
*subitem
= MENU_FindItem( &hsubmenu
, nPos
, wFlags
);
637 else if (item
->wID
== *nPos
)
639 /* fallback to this item if nothing else found */
644 else if (item
->wID
== *nPos
)
653 *nPos
= fallback_pos
;
658 /***********************************************************************
661 * Find a Sub menu. Return the position of the submenu, and modifies
662 * *hmenu in case it is found in another sub-menu.
663 * If the submenu cannot be found, NO_SELECTED_ITEM is returned.
665 static UINT
MENU_FindSubMenu( HMENU
*hmenu
, HMENU hSubTarget
)
670 if (((*hmenu
)==(HMENU
)0xffff) ||
671 (!(menu
= MENU_GetMenu(*hmenu
))))
672 return NO_SELECTED_ITEM
;
674 for (i
= 0; i
< menu
->nItems
; i
++, item
++) {
675 if(!(item
->fType
& MF_POPUP
)) continue;
676 if (item
->hSubMenu
== hSubTarget
) {
680 HMENU hsubmenu
= item
->hSubMenu
;
681 UINT pos
= MENU_FindSubMenu( &hsubmenu
, hSubTarget
);
682 if (pos
!= NO_SELECTED_ITEM
) {
688 return NO_SELECTED_ITEM
;
691 /***********************************************************************
694 static void MENU_FreeItemData( MENUITEM
* item
)
697 HeapFree( GetProcessHeap(), 0, item
->text
);
700 /***********************************************************************
701 * MENU_AdjustMenuItemRect
703 * Adjust menu item rectangle according to scrolling state.
706 MENU_AdjustMenuItemRect(const POPUPMENU
*menu
, LPRECT rect
)
708 if (menu
->bScrolling
)
710 UINT arrow_bitmap_height
;
713 GetObjectW(get_up_arrow_bitmap(), sizeof(bmp
), &bmp
);
714 arrow_bitmap_height
= bmp
.bmHeight
;
715 rect
->top
+= arrow_bitmap_height
- menu
->nScrollPos
;
716 rect
->bottom
+= arrow_bitmap_height
- menu
->nScrollPos
;
721 /***********************************************************************
722 * MENU_FindItemByCoords
724 * Find the item at the specified coordinates (screen coords). Does
725 * not work for child windows and therefore should not be called for
726 * an arbitrary system menu.
728 static MENUITEM
*MENU_FindItemByCoords( const POPUPMENU
*menu
,
729 POINT pt
, UINT
*pos
)
735 if (!GetWindowRect(menu
->hWnd
, &rect
)) return NULL
;
736 if (GetWindowLongW( menu
->hWnd
, GWL_EXSTYLE
) & WS_EX_LAYOUTRTL
) pt
.x
= rect
.right
- 1 - pt
.x
;
737 else pt
.x
-= rect
.left
;
740 for (i
= 0; i
< menu
->nItems
; i
++, item
++)
743 MENU_AdjustMenuItemRect(menu
, &rect
);
744 if (PtInRect(&rect
, pt
))
754 /***********************************************************************
757 * Find the menu item selected by a key press.
758 * Return item id, -1 if none, -2 if we should close the menu.
760 static UINT
MENU_FindItemByKey( HWND hwndOwner
, HMENU hmenu
,
761 WCHAR key
, BOOL forceMenuChar
)
763 TRACE("\tlooking for '%c' (0x%02x) in [%p]\n", (char)key
, key
, hmenu
);
765 if (!IsMenu( hmenu
)) hmenu
= GetSubMenu( get_win_sys_menu(hwndOwner
), 0);
769 POPUPMENU
*menu
= MENU_GetMenu( hmenu
);
770 MENUITEM
*item
= menu
->items
;
776 BOOL cjk
= GetSystemMetrics( SM_DBCSENABLED
);
778 for (i
= 0; i
< menu
->nItems
; i
++, item
++)
782 const WCHAR
*p
= item
->text
- 2;
785 const WCHAR
*q
= p
+ 2;
786 p
= strchrW (q
, '&');
787 if (!p
&& cjk
) p
= strchrW (q
, '\036'); /* Japanese Win16 */
789 while (p
!= NULL
&& p
[1] == '&');
790 if (p
&& (toupperW(p
[1]) == toupperW(key
))) return i
;
794 menuchar
= SendMessageW( hwndOwner
, WM_MENUCHAR
,
795 MAKEWPARAM( key
, menu
->wFlags
), (LPARAM
)hmenu
);
796 if (HIWORD(menuchar
) == MNC_EXECUTE
) return LOWORD(menuchar
);
797 if (HIWORD(menuchar
) == MNC_CLOSE
) return (UINT
)(-2);
803 /***********************************************************************
804 * MENU_GetBitmapItemSize
806 * Get the size of a bitmap item.
808 static void MENU_GetBitmapItemSize( MENUITEM
*lpitem
, SIZE
*size
,
812 HBITMAP bmp
= lpitem
->hbmpItem
;
814 size
->cx
= size
->cy
= 0;
816 /* check if there is a magic menu item associated with this item */
817 switch( (INT_PTR
) bmp
)
819 case (INT_PTR
)HBMMENU_CALLBACK
:
821 MEASUREITEMSTRUCT measItem
;
822 measItem
.CtlType
= ODT_MENU
;
824 measItem
.itemID
= lpitem
->wID
;
825 measItem
.itemWidth
= lpitem
->rect
.right
- lpitem
->rect
.left
;
826 measItem
.itemHeight
= lpitem
->rect
.bottom
- lpitem
->rect
.top
;
827 measItem
.itemData
= lpitem
->dwItemData
;
828 SendMessageW( hwndOwner
, WM_MEASUREITEM
, 0, (LPARAM
)&measItem
);
829 size
->cx
= measItem
.itemWidth
;
830 size
->cy
= measItem
.itemHeight
;
834 case (INT_PTR
)HBMMENU_SYSTEM
:
835 if (lpitem
->dwItemData
)
837 bmp
= (HBITMAP
)lpitem
->dwItemData
;
841 case (INT_PTR
)HBMMENU_MBAR_RESTORE
:
842 case (INT_PTR
)HBMMENU_MBAR_MINIMIZE
:
843 case (INT_PTR
)HBMMENU_MBAR_MINIMIZE_D
:
844 case (INT_PTR
)HBMMENU_MBAR_CLOSE
:
845 case (INT_PTR
)HBMMENU_MBAR_CLOSE_D
:
846 size
->cx
= GetSystemMetrics( SM_CYMENU
) - 4;
849 case (INT_PTR
)HBMMENU_POPUP_CLOSE
:
850 case (INT_PTR
)HBMMENU_POPUP_RESTORE
:
851 case (INT_PTR
)HBMMENU_POPUP_MAXIMIZE
:
852 case (INT_PTR
)HBMMENU_POPUP_MINIMIZE
:
853 size
->cx
= GetSystemMetrics( SM_CXMENUSIZE
);
854 size
->cy
= GetSystemMetrics( SM_CYMENUSIZE
);
857 if (GetObjectW(bmp
, sizeof(bm
), &bm
))
859 size
->cx
= bm
.bmWidth
;
860 size
->cy
= bm
.bmHeight
;
864 /***********************************************************************
865 * MENU_DrawBitmapItem
867 * Draw a bitmap item.
869 static void MENU_DrawBitmapItem( HDC hdc
, MENUITEM
*lpitem
, const RECT
*rect
,
870 HMENU hmenu
, HWND hwndOwner
, UINT odaction
, BOOL menuBar
)
876 int w
= rect
->right
- rect
->left
;
877 int h
= rect
->bottom
- rect
->top
;
880 HBITMAP hbmToDraw
= lpitem
->hbmpItem
;
883 /* Check if there is a magic menu item associated with this item */
884 if (IS_MAGIC_BITMAP(hbmToDraw
))
890 switch((INT_PTR
)hbmToDraw
)
892 case (INT_PTR
)HBMMENU_SYSTEM
:
893 if (lpitem
->dwItemData
)
895 bmp
= (HBITMAP
)lpitem
->dwItemData
;
896 if (!GetObjectW( bmp
, sizeof(bm
), &bm
)) return;
900 static HBITMAP hBmpSysMenu
;
902 if (!hBmpSysMenu
) hBmpSysMenu
= LoadBitmapW(0, MAKEINTRESOURCEW(OBM_CLOSE
));
904 if (!GetObjectW( bmp
, sizeof(bm
), &bm
)) return;
905 /* only use right half of the bitmap */
906 bmp_xoffset
= bm
.bmWidth
/ 2;
907 bm
.bmWidth
-= bmp_xoffset
;
910 case (INT_PTR
)HBMMENU_MBAR_RESTORE
:
911 flags
= DFCS_CAPTIONRESTORE
;
913 case (INT_PTR
)HBMMENU_MBAR_MINIMIZE
:
914 flags
= DFCS_CAPTIONMIN
;
916 case (INT_PTR
)HBMMENU_MBAR_MINIMIZE_D
:
917 flags
= DFCS_CAPTIONMIN
| DFCS_INACTIVE
;
919 case (INT_PTR
)HBMMENU_MBAR_CLOSE
:
920 flags
= DFCS_CAPTIONCLOSE
;
922 case (INT_PTR
)HBMMENU_MBAR_CLOSE_D
:
923 flags
= DFCS_CAPTIONCLOSE
| DFCS_INACTIVE
;
925 case (INT_PTR
)HBMMENU_CALLBACK
:
927 DRAWITEMSTRUCT drawItem
;
928 drawItem
.CtlType
= ODT_MENU
;
930 drawItem
.itemID
= lpitem
->wID
;
931 drawItem
.itemAction
= odaction
;
932 drawItem
.itemState
= (lpitem
->fState
& MF_CHECKED
)?ODS_CHECKED
:0;
933 drawItem
.itemState
|= (lpitem
->fState
& MF_DEFAULT
)?ODS_DEFAULT
:0;
934 drawItem
.itemState
|= (lpitem
->fState
& MF_DISABLED
)?ODS_DISABLED
:0;
935 drawItem
.itemState
|= (lpitem
->fState
& MF_GRAYED
)?ODS_GRAYED
|ODS_DISABLED
:0;
936 drawItem
.itemState
|= (lpitem
->fState
& MF_HILITE
)?ODS_SELECTED
:0;
937 drawItem
.hwndItem
= (HWND
)hmenu
;
939 drawItem
.itemData
= lpitem
->dwItemData
;
940 drawItem
.rcItem
= *rect
;
941 SendMessageW( hwndOwner
, WM_DRAWITEM
, 0, (LPARAM
)&drawItem
);
945 case (INT_PTR
)HBMMENU_POPUP_CLOSE
:
948 case (INT_PTR
)HBMMENU_POPUP_RESTORE
:
951 case (INT_PTR
)HBMMENU_POPUP_MAXIMIZE
:
954 case (INT_PTR
)HBMMENU_POPUP_MINIMIZE
:
958 FIXME("Magic %p not implemented\n", hbmToDraw
);
963 /* draw the magic bitmaps using marlett font characters */
964 /* FIXME: fontsize and the position (x,y) could probably be better */
965 HFONT hfont
, hfontsav
;
966 LOGFONTW logfont
= { 0, 0, 0, 0, FW_NORMAL
,
967 0, 0, 0, SYMBOL_CHARSET
, 0, 0, 0, 0,
968 { 'M','a','r','l','e','t','t',0 } };
969 logfont
.lfHeight
= min( h
, w
) - 5 ;
970 TRACE(" height %d rect %s\n", logfont
.lfHeight
, wine_dbgstr_rect( rect
));
971 hfont
= CreateFontIndirectW( &logfont
);
972 hfontsav
= SelectObject(hdc
, hfont
);
973 TextOutW( hdc
, rect
->left
, rect
->top
+ 2, &bmchr
, 1);
974 SelectObject(hdc
, hfontsav
);
975 DeleteObject( hfont
);
980 InflateRect( &r
, -1, -1 );
981 if (lpitem
->fState
& MF_HILITE
) flags
|= DFCS_PUSHED
;
982 DrawFrameControl( hdc
, &r
, DFC_CAPTION
, flags
);
987 if (!bmp
|| !GetObjectW( bmp
, sizeof(bm
), &bm
)) return;
990 hdcMem
= CreateCompatibleDC( hdc
);
991 SelectObject( hdcMem
, bmp
);
993 /* handle fontsize > bitmap_height */
994 top
= (h
>bm
.bmHeight
) ? rect
->top
+(h
-bm
.bmHeight
)/2 : rect
->top
;
996 rop
=((lpitem
->fState
& MF_HILITE
) && !IS_MAGIC_BITMAP(hbmToDraw
)) ? NOTSRCCOPY
: SRCCOPY
;
997 if ((lpitem
->fState
& MF_HILITE
) && lpitem
->hbmpItem
)
998 SetBkColor(hdc
, GetSysColor(COLOR_HIGHLIGHT
));
999 BitBlt( hdc
, left
, top
, w
, h
, hdcMem
, bmp_xoffset
, 0, rop
);
1004 /***********************************************************************
1007 * Calculate the size of the menu item and store it in lpitem->rect.
1009 static void MENU_CalcItemSize( HDC hdc
, MENUITEM
*lpitem
, HWND hwndOwner
,
1010 INT orgX
, INT orgY
, BOOL menuBar
, POPUPMENU
* lppop
)
1013 UINT check_bitmap_width
= GetSystemMetrics( SM_CXMENUCHECK
);
1014 UINT arrow_bitmap_width
;
1018 TRACE("dc=%p owner=%p (%d,%d)\n", hdc
, hwndOwner
, orgX
, orgY
);
1019 debug_print_menuitem("MENU_CalcItemSize: menuitem:", lpitem
,
1020 (menuBar
? " (MenuBar)" : ""));
1022 GetObjectW( get_arrow_bitmap(), sizeof(bm
), &bm
);
1023 arrow_bitmap_width
= bm
.bmWidth
;
1025 /* not done in Menu_Init: GetDialogBaseUnits() breaks there */
1026 if( !menucharsize
.cx
) {
1027 menucharsize
.cx
= GdiGetCharDimensions( hdc
, NULL
, &menucharsize
.cy
);
1028 /* Win95/98/ME will use menucharsize.cy here. Testing is possible
1029 * but it is unlikely an application will depend on that */
1030 ODitemheight
= HIWORD( GetDialogBaseUnits());
1033 SetRect( &lpitem
->rect
, orgX
, orgY
, orgX
, orgY
);
1035 if (lpitem
->fType
& MF_OWNERDRAW
)
1037 MEASUREITEMSTRUCT mis
;
1038 mis
.CtlType
= ODT_MENU
;
1040 mis
.itemID
= lpitem
->wID
;
1041 mis
.itemData
= lpitem
->dwItemData
;
1042 mis
.itemHeight
= ODitemheight
;
1044 SendMessageW( hwndOwner
, WM_MEASUREITEM
, 0, (LPARAM
)&mis
);
1045 /* Tests reveal that Windows ( Win95 through WinXP) adds twice the average
1046 * width of a menufont character to the width of an owner-drawn menu.
1048 lpitem
->rect
.right
+= mis
.itemWidth
+ 2 * menucharsize
.cx
;
1050 /* under at least win95 you seem to be given a standard
1051 height for the menu and the height value is ignored */
1052 lpitem
->rect
.bottom
+= GetSystemMetrics(SM_CYMENUSIZE
);
1054 lpitem
->rect
.bottom
+= mis
.itemHeight
;
1056 TRACE("id=%04lx size=%dx%d\n",
1057 lpitem
->wID
, lpitem
->rect
.right
-lpitem
->rect
.left
,
1058 lpitem
->rect
.bottom
-lpitem
->rect
.top
);
1062 if (lpitem
->fType
& MF_SEPARATOR
)
1064 lpitem
->rect
.bottom
+= GetSystemMetrics( SM_CYMENUSIZE
)/2;
1066 lpitem
->rect
.right
+= arrow_bitmap_width
+ menucharsize
.cx
;
1074 if (lpitem
->hbmpItem
) {
1077 MENU_GetBitmapItemSize(lpitem
, &size
, hwndOwner
);
1078 /* Keep the size of the bitmap in callback mode to be able
1079 * to draw it correctly */
1080 lpitem
->bmpsize
= size
;
1081 lppop
->textOffset
= max( lppop
->textOffset
, size
.cx
);
1082 lpitem
->rect
.right
+= size
.cx
+ 2;
1083 itemheight
= size
.cy
+ 2;
1085 if( !(lppop
->dwStyle
& MNS_NOCHECK
))
1086 lpitem
->rect
.right
+= check_bitmap_width
;
1087 lpitem
->rect
.right
+= 4 + menucharsize
.cx
;
1088 lpitem
->xTab
= lpitem
->rect
.right
;
1089 lpitem
->rect
.right
+= arrow_bitmap_width
;
1090 } else if (lpitem
->hbmpItem
) { /* menuBar */
1093 MENU_GetBitmapItemSize( lpitem
, &size
, hwndOwner
);
1094 lpitem
->bmpsize
= size
;
1095 lpitem
->rect
.right
+= size
.cx
;
1096 if( lpitem
->text
) lpitem
->rect
.right
+= 2;
1097 itemheight
= size
.cy
;
1100 /* it must be a text item - unless it's the system menu */
1101 if (!(lpitem
->fType
& MF_SYSMENU
) && lpitem
->text
) {
1102 HFONT hfontOld
= NULL
;
1103 RECT rc
= lpitem
->rect
;
1104 LONG txtheight
, txtwidth
;
1106 if ( lpitem
->fState
& MFS_DEFAULT
) {
1107 hfontOld
= SelectObject( hdc
, get_menu_font(TRUE
) );
1110 txtheight
= DrawTextW( hdc
, lpitem
->text
, -1, &rc
,
1111 DT_SINGLELINE
|DT_CALCRECT
);
1112 lpitem
->rect
.right
+= rc
.right
- rc
.left
;
1113 itemheight
= max( max( itemheight
, txtheight
),
1114 GetSystemMetrics( SM_CYMENU
) - 1);
1115 lpitem
->rect
.right
+= 2 * menucharsize
.cx
;
1117 if ((p
= strchrW( lpitem
->text
, '\t' )) != NULL
) {
1120 int n
= (int)( p
- lpitem
->text
);
1121 /* Item contains a tab (only meaningful in popup menus) */
1122 /* get text size before the tab */
1123 txtheight
= DrawTextW( hdc
, lpitem
->text
, n
, &rc
,
1124 DT_SINGLELINE
|DT_CALCRECT
);
1125 txtwidth
= rc
.right
- rc
.left
;
1126 p
+= 1; /* advance past the Tab */
1127 /* get text size after the tab */
1128 tmpheight
= DrawTextW( hdc
, p
, -1, &tmprc
,
1129 DT_SINGLELINE
|DT_CALCRECT
);
1130 lpitem
->xTab
+= txtwidth
;
1131 txtheight
= max( txtheight
, tmpheight
);
1132 txtwidth
+= menucharsize
.cx
+ /* space for the tab */
1133 tmprc
.right
- tmprc
.left
; /* space for the short cut */
1135 txtheight
= DrawTextW( hdc
, lpitem
->text
, -1, &rc
,
1136 DT_SINGLELINE
|DT_CALCRECT
);
1137 txtwidth
= rc
.right
- rc
.left
;
1138 lpitem
->xTab
+= txtwidth
;
1140 lpitem
->rect
.right
+= 2 + txtwidth
;
1141 itemheight
= max( itemheight
,
1142 max( txtheight
+ 2, menucharsize
.cy
+ 4));
1144 if (hfontOld
) SelectObject (hdc
, hfontOld
);
1145 } else if( menuBar
) {
1146 itemheight
= max( itemheight
, GetSystemMetrics(SM_CYMENU
)-1);
1148 lpitem
->rect
.bottom
+= itemheight
;
1149 TRACE("%s\n", wine_dbgstr_rect( &lpitem
->rect
));
1153 /***********************************************************************
1154 * MENU_GetMaxPopupHeight
1157 MENU_GetMaxPopupHeight(const POPUPMENU
*lppop
)
1160 return lppop
->cyMax
;
1161 return GetSystemMetrics(SM_CYSCREEN
) - GetSystemMetrics(SM_CYBORDER
);
1165 /***********************************************************************
1166 * MENU_PopupMenuCalcSize
1168 * Calculate the size of a popup menu.
1170 static void MENU_PopupMenuCalcSize( LPPOPUPMENU lppop
)
1175 BOOL textandbmp
= FALSE
;
1176 int orgX
, orgY
, maxX
, maxTab
, maxTabWidth
, maxHeight
;
1178 lppop
->Width
= lppop
->Height
= 0;
1179 if (lppop
->nItems
== 0) return;
1182 SelectObject( hdc
, get_menu_font(FALSE
));
1187 lppop
->textOffset
= 0;
1189 while (start
< lppop
->nItems
)
1191 lpitem
= &lppop
->items
[start
];
1193 if( lpitem
->fType
& (MF_MENUBREAK
| MF_MENUBARBREAK
))
1194 orgX
+= MENU_COL_SPACE
;
1195 orgY
= MENU_TOP_MARGIN
;
1197 maxTab
= maxTabWidth
= 0;
1198 /* Parse items until column break or end of menu */
1199 for (i
= start
; i
< lppop
->nItems
; i
++, lpitem
++)
1202 (lpitem
->fType
& (MF_MENUBREAK
| MF_MENUBARBREAK
))) break;
1204 MENU_CalcItemSize( hdc
, lpitem
, lppop
->hwndOwner
, orgX
, orgY
, FALSE
, lppop
);
1205 maxX
= max( maxX
, lpitem
->rect
.right
);
1206 orgY
= lpitem
->rect
.bottom
;
1207 if (IS_STRING_ITEM(lpitem
->fType
) && lpitem
->xTab
)
1209 maxTab
= max( maxTab
, lpitem
->xTab
);
1210 maxTabWidth
= max(maxTabWidth
,lpitem
->rect
.right
-lpitem
->xTab
);
1212 if( lpitem
->text
&& lpitem
->hbmpItem
) textandbmp
= TRUE
;
1215 /* Finish the column (set all items to the largest width found) */
1216 maxX
= max( maxX
, maxTab
+ maxTabWidth
);
1217 for (lpitem
= &lppop
->items
[start
]; start
< i
; start
++, lpitem
++)
1219 lpitem
->rect
.right
= maxX
;
1220 if (IS_STRING_ITEM(lpitem
->fType
) && lpitem
->xTab
)
1221 lpitem
->xTab
= maxTab
;
1224 lppop
->Height
= max( lppop
->Height
, orgY
);
1227 lppop
->Width
= maxX
;
1228 /* if none of the items have both text and bitmap then
1229 * the text and bitmaps are all aligned on the left. If there is at
1230 * least one item with both text and bitmap then bitmaps are
1231 * on the left and texts left aligned with the right hand side
1233 if( !textandbmp
) lppop
->textOffset
= 0;
1235 /* space for 3d border */
1236 lppop
->Height
+= MENU_BOTTOM_MARGIN
;
1239 /* Adjust popup height if it exceeds maximum */
1240 maxHeight
= MENU_GetMaxPopupHeight(lppop
);
1241 lppop
->nTotalHeight
= lppop
->Height
- MENU_TOP_MARGIN
;
1242 if (lppop
->Height
>= maxHeight
)
1244 lppop
->Height
= maxHeight
;
1245 lppop
->bScrolling
= TRUE
;
1249 lppop
->bScrolling
= FALSE
;
1252 ReleaseDC( 0, hdc
);
1256 /***********************************************************************
1257 * MENU_MenuBarCalcSize
1259 * FIXME: Word 6 implements its own MDI and its own 'close window' bitmap
1260 * height is off by 1 pixel which causes lengthy window relocations when
1261 * active document window is maximized/restored.
1263 * Calculate the size of the menu bar.
1265 static void MENU_MenuBarCalcSize( HDC hdc
, LPRECT lprect
,
1266 LPPOPUPMENU lppop
, HWND hwndOwner
)
1269 UINT start
, i
, helpPos
;
1270 int orgX
, orgY
, maxY
;
1272 if ((lprect
== NULL
) || (lppop
== NULL
)) return;
1273 if (lppop
->nItems
== 0) return;
1274 TRACE("lprect %p %s\n", lprect
, wine_dbgstr_rect( lprect
));
1275 lppop
->Width
= lprect
->right
- lprect
->left
;
1277 maxY
= lprect
->top
+1;
1280 lppop
->textOffset
= 0;
1281 while (start
< lppop
->nItems
)
1283 lpitem
= &lppop
->items
[start
];
1284 orgX
= lprect
->left
;
1287 /* Parse items until line break or end of menu */
1288 for (i
= start
; i
< lppop
->nItems
; i
++, lpitem
++)
1290 if ((helpPos
== ~0U) && (lpitem
->fType
& MF_RIGHTJUSTIFY
)) helpPos
= i
;
1292 (lpitem
->fType
& (MF_MENUBREAK
| MF_MENUBARBREAK
))) break;
1294 TRACE("calling MENU_CalcItemSize org=(%d, %d)\n", orgX
, orgY
);
1295 debug_print_menuitem (" item: ", lpitem
, "");
1296 MENU_CalcItemSize( hdc
, lpitem
, hwndOwner
, orgX
, orgY
, TRUE
, lppop
);
1298 if (lpitem
->rect
.right
> lprect
->right
)
1300 if (i
!= start
) break;
1301 else lpitem
->rect
.right
= lprect
->right
;
1303 maxY
= max( maxY
, lpitem
->rect
.bottom
);
1304 orgX
= lpitem
->rect
.right
;
1307 /* Finish the line (set all items to the largest height found) */
1308 while (start
< i
) lppop
->items
[start
++].rect
.bottom
= maxY
;
1311 lprect
->bottom
= maxY
;
1312 lppop
->Height
= lprect
->bottom
- lprect
->top
;
1314 /* Flush right all items between the MF_RIGHTJUSTIFY and */
1315 /* the last item (if several lines, only move the last line) */
1316 if (helpPos
== ~0U) return;
1317 lpitem
= &lppop
->items
[lppop
->nItems
-1];
1318 orgY
= lpitem
->rect
.top
;
1319 orgX
= lprect
->right
;
1320 for (i
= lppop
->nItems
- 1; i
>= helpPos
; i
--, lpitem
--) {
1321 if (lpitem
->rect
.top
!= orgY
) break; /* Other line */
1322 if (lpitem
->rect
.right
>= orgX
) break; /* Too far right already */
1323 lpitem
->rect
.left
+= orgX
- lpitem
->rect
.right
;
1324 lpitem
->rect
.right
= orgX
;
1325 orgX
= lpitem
->rect
.left
;
1330 /***********************************************************************
1331 * MENU_DrawScrollArrows
1333 * Draw scroll arrows.
1336 MENU_DrawScrollArrows(const POPUPMENU
*lppop
, HDC hdc
)
1338 HDC hdcMem
= CreateCompatibleDC(hdc
);
1339 HBITMAP hOrigBitmap
;
1340 UINT arrow_bitmap_width
, arrow_bitmap_height
;
1344 GetObjectW(get_down_arrow_bitmap(), sizeof(bmp
), &bmp
);
1345 arrow_bitmap_width
= bmp
.bmWidth
;
1346 arrow_bitmap_height
= bmp
.bmHeight
;
1349 if (lppop
->nScrollPos
)
1350 hOrigBitmap
= SelectObject(hdcMem
, get_up_arrow_bitmap());
1352 hOrigBitmap
= SelectObject(hdcMem
, get_up_arrow_inactive_bitmap());
1355 rect
.right
= lppop
->Width
;
1356 rect
.bottom
= arrow_bitmap_height
;
1357 FillRect(hdc
, &rect
, GetSysColorBrush(COLOR_MENU
));
1358 BitBlt(hdc
, (lppop
->Width
- arrow_bitmap_width
) / 2, 0,
1359 arrow_bitmap_width
, arrow_bitmap_height
, hdcMem
, 0, 0, SRCCOPY
);
1360 rect
.top
= lppop
->Height
- arrow_bitmap_height
;
1361 rect
.bottom
= lppop
->Height
;
1362 FillRect(hdc
, &rect
, GetSysColorBrush(COLOR_MENU
));
1363 if (lppop
->nScrollPos
< lppop
->nTotalHeight
- (MENU_GetMaxPopupHeight(lppop
) - 2 * arrow_bitmap_height
))
1364 SelectObject(hdcMem
, get_down_arrow_bitmap());
1366 SelectObject(hdcMem
, get_down_arrow_inactive_bitmap());
1367 BitBlt(hdc
, (lppop
->Width
- arrow_bitmap_width
) / 2,
1368 lppop
->Height
- arrow_bitmap_height
,
1369 arrow_bitmap_width
, arrow_bitmap_height
, hdcMem
, 0, 0, SRCCOPY
);
1370 SelectObject(hdcMem
, hOrigBitmap
);
1375 /***********************************************************************
1378 * Draws the popup-menu arrow.
1380 static void draw_popup_arrow( HDC hdc
, RECT rect
, UINT arrow_bitmap_width
,
1381 UINT arrow_bitmap_height
)
1383 HDC hdcMem
= CreateCompatibleDC( hdc
);
1384 HBITMAP hOrigBitmap
;
1386 hOrigBitmap
= SelectObject( hdcMem
, get_arrow_bitmap() );
1387 BitBlt( hdc
, rect
.right
- arrow_bitmap_width
- 1,
1388 (rect
.top
+ rect
.bottom
- arrow_bitmap_height
) / 2,
1389 arrow_bitmap_width
, arrow_bitmap_height
,
1390 hdcMem
, 0, 0, SRCCOPY
);
1391 SelectObject( hdcMem
, hOrigBitmap
);
1394 /***********************************************************************
1397 * Draw a single menu item.
1399 static void MENU_DrawMenuItem( HWND hwnd
, HMENU hmenu
, HWND hwndOwner
, HDC hdc
, MENUITEM
*lpitem
,
1400 UINT height
, BOOL menuBar
, UINT odaction
)
1403 BOOL flat_menu
= FALSE
;
1405 UINT arrow_bitmap_width
= 0, arrow_bitmap_height
= 0;
1406 POPUPMENU
*menu
= MENU_GetMenu(hmenu
);
1409 debug_print_menuitem("MENU_DrawMenuItem: ", lpitem
, "");
1413 GetObjectW( get_arrow_bitmap(), sizeof(bmp
), &bmp
);
1414 arrow_bitmap_width
= bmp
.bmWidth
;
1415 arrow_bitmap_height
= bmp
.bmHeight
;
1418 if (lpitem
->fType
& MF_SYSMENU
)
1420 if( !IsIconic(hwnd
) )
1421 NC_DrawSysButton( hwnd
, hdc
, lpitem
->fState
& (MF_HILITE
| MF_MOUSESELECT
) );
1425 SystemParametersInfoW (SPI_GETFLATMENU
, 0, &flat_menu
, 0);
1426 bkgnd
= (menuBar
&& flat_menu
) ? COLOR_MENUBAR
: COLOR_MENU
;
1430 if (lpitem
->fState
& MF_HILITE
)
1432 if(menuBar
&& !flat_menu
) {
1433 SetTextColor(hdc
, GetSysColor(COLOR_MENUTEXT
));
1434 SetBkColor(hdc
, GetSysColor(COLOR_MENU
));
1436 if(lpitem
->fState
& MF_GRAYED
)
1437 SetTextColor(hdc
, GetSysColor(COLOR_GRAYTEXT
));
1439 SetTextColor(hdc
, GetSysColor(COLOR_HIGHLIGHTTEXT
));
1440 SetBkColor(hdc
, GetSysColor(COLOR_HIGHLIGHT
));
1445 if (lpitem
->fState
& MF_GRAYED
)
1446 SetTextColor( hdc
, GetSysColor( COLOR_GRAYTEXT
) );
1448 SetTextColor( hdc
, GetSysColor( COLOR_MENUTEXT
) );
1449 SetBkColor( hdc
, GetSysColor( bkgnd
) );
1452 TRACE("rect=%s\n", wine_dbgstr_rect( &lpitem
->rect
));
1453 rect
= lpitem
->rect
;
1454 MENU_AdjustMenuItemRect(MENU_GetMenu(hmenu
), &rect
);
1456 if (lpitem
->fType
& MF_OWNERDRAW
)
1459 ** Experimentation under Windows reveals that an owner-drawn
1460 ** menu is given the rectangle which includes the space it requested
1461 ** in its response to WM_MEASUREITEM _plus_ width for a checkmark
1462 ** and a popup-menu arrow. This is the value of lpitem->rect.
1463 ** Windows will leave all drawing to the application except for
1464 ** the popup-menu arrow. Windows always draws that itself, after
1465 ** the menu owner has finished drawing.
1469 dis
.CtlType
= ODT_MENU
;
1471 dis
.itemID
= lpitem
->wID
;
1472 dis
.itemData
= lpitem
->dwItemData
;
1474 if (lpitem
->fState
& MF_CHECKED
) dis
.itemState
|= ODS_CHECKED
;
1475 if (lpitem
->fState
& MF_GRAYED
) dis
.itemState
|= ODS_GRAYED
|ODS_DISABLED
;
1476 if (lpitem
->fState
& MF_HILITE
) dis
.itemState
|= ODS_SELECTED
;
1477 dis
.itemAction
= odaction
; /* ODA_DRAWENTIRE | ODA_SELECT | ODA_FOCUS; */
1478 dis
.hwndItem
= (HWND
)hmenu
;
1481 TRACE("Ownerdraw: owner=%p itemID=%d, itemState=%d, itemAction=%d, "
1482 "hwndItem=%p, hdc=%p, rcItem=%s\n", hwndOwner
,
1483 dis
.itemID
, dis
.itemState
, dis
.itemAction
, dis
.hwndItem
,
1484 dis
.hDC
, wine_dbgstr_rect( &dis
.rcItem
));
1485 SendMessageW( hwndOwner
, WM_DRAWITEM
, 0, (LPARAM
)&dis
);
1486 /* Draw the popup-menu arrow */
1487 if (lpitem
->fType
& MF_POPUP
)
1488 draw_popup_arrow( hdc
, rect
, arrow_bitmap_width
,
1489 arrow_bitmap_height
);
1493 if (menuBar
&& (lpitem
->fType
& MF_SEPARATOR
)) return;
1495 if (lpitem
->fState
& MF_HILITE
)
1499 InflateRect (&rect
, -1, -1);
1500 FillRect(hdc
, &rect
, GetSysColorBrush(COLOR_MENUHILIGHT
));
1501 InflateRect (&rect
, 1, 1);
1502 FrameRect(hdc
, &rect
, GetSysColorBrush(COLOR_HIGHLIGHT
));
1507 DrawEdge(hdc
, &rect
, BDR_SUNKENOUTER
, BF_RECT
);
1509 FillRect(hdc
, &rect
, GetSysColorBrush(COLOR_HIGHLIGHT
));
1513 FillRect( hdc
, &rect
, GetSysColorBrush(bkgnd
) );
1515 SetBkMode( hdc
, TRANSPARENT
);
1517 /* vertical separator */
1518 if (!menuBar
&& (lpitem
->fType
& MF_MENUBARBREAK
))
1523 rc
.left
-= MENU_COL_SPACE
/ 2 + 1;
1525 rc
.bottom
= height
- 3;
1528 oldPen
= SelectObject( hdc
, SYSCOLOR_GetPen(COLOR_BTNSHADOW
) );
1529 MoveToEx( hdc
, rc
.left
, rc
.top
, NULL
);
1530 LineTo( hdc
, rc
.left
, rc
.bottom
);
1531 SelectObject( hdc
, oldPen
);
1534 DrawEdge (hdc
, &rc
, EDGE_ETCHED
, BF_LEFT
);
1537 /* horizontal separator */
1538 if (lpitem
->fType
& MF_SEPARATOR
)
1545 rc
.top
= ( rc
.top
+ rc
.bottom
) / 2;
1548 oldPen
= SelectObject( hdc
, SYSCOLOR_GetPen(COLOR_BTNSHADOW
) );
1549 MoveToEx( hdc
, rc
.left
, rc
.top
, NULL
);
1550 LineTo( hdc
, rc
.right
, rc
.top
);
1551 SelectObject( hdc
, oldPen
);
1554 DrawEdge (hdc
, &rc
, EDGE_ETCHED
, BF_TOP
);
1558 /* helper lines for debugging */
1559 /* FrameRect(hdc, &rect, GetStockObject(BLACK_BRUSH));
1560 SelectObject( hdc, SYSCOLOR_GetPen(COLOR_WINDOWFRAME) );
1561 MoveToEx( hdc, rect.left, (rect.top + rect.bottom)/2, NULL );
1562 LineTo( hdc, rect.right, (rect.top + rect.bottom)/2 );
1565 if (lpitem
->hbmpItem
) {
1566 /* calculate the bitmap rectangle in coordinates relative
1567 * to the item rectangle */
1569 if( lpitem
->hbmpItem
== HBMMENU_CALLBACK
)
1572 bmprc
.left
= lpitem
->text
? menucharsize
.cx
: 0;
1574 else if (menu
->dwStyle
& MNS_NOCHECK
)
1576 else if (menu
->dwStyle
& MNS_CHECKORBMP
)
1579 bmprc
.left
= 4 + GetSystemMetrics(SM_CXMENUCHECK
);
1580 bmprc
.right
= bmprc
.left
+ lpitem
->bmpsize
.cx
;
1581 if( menuBar
&& !(lpitem
->hbmpItem
== HBMMENU_CALLBACK
))
1584 bmprc
.top
= (rect
.bottom
- rect
.top
-
1585 lpitem
->bmpsize
.cy
) / 2;
1586 bmprc
.bottom
= bmprc
.top
+ lpitem
->bmpsize
.cy
;
1592 INT y
= rect
.top
+ rect
.bottom
;
1594 BOOL checked
= FALSE
;
1595 UINT check_bitmap_width
= GetSystemMetrics( SM_CXMENUCHECK
);
1596 UINT check_bitmap_height
= GetSystemMetrics( SM_CYMENUCHECK
);
1597 /* Draw the check mark
1600 * Custom checkmark bitmaps are monochrome but not always 1bpp.
1602 if( !(menu
->dwStyle
& MNS_NOCHECK
)) {
1603 bm
= (lpitem
->fState
& MF_CHECKED
) ? lpitem
->hCheckBit
:
1604 lpitem
->hUnCheckBit
;
1605 if (bm
) /* we have a custom bitmap */
1607 HDC hdcMem
= CreateCompatibleDC( hdc
);
1609 SelectObject( hdcMem
, bm
);
1610 BitBlt( hdc
, rc
.left
, (y
- check_bitmap_height
) / 2,
1611 check_bitmap_width
, check_bitmap_height
,
1612 hdcMem
, 0, 0, SRCCOPY
);
1616 else if (lpitem
->fState
& MF_CHECKED
) /* standard bitmaps */
1619 HBITMAP bm
= CreateBitmap( check_bitmap_width
,
1620 check_bitmap_height
, 1, 1, NULL
);
1621 HDC hdcMem
= CreateCompatibleDC( hdc
);
1623 SelectObject( hdcMem
, bm
);
1624 SetRect( &r
, 0, 0, check_bitmap_width
, check_bitmap_height
);
1625 DrawFrameControl( hdcMem
, &r
, DFC_MENU
,
1626 (lpitem
->fType
& MFT_RADIOCHECK
) ?
1627 DFCS_MENUBULLET
: DFCS_MENUCHECK
);
1628 BitBlt( hdc
, rc
.left
, (y
- r
.bottom
) / 2, r
.right
, r
.bottom
,
1629 hdcMem
, 0, 0, SRCCOPY
);
1635 if( lpitem
->hbmpItem
&&
1636 !( checked
&& (menu
->dwStyle
& MNS_CHECKORBMP
))) {
1638 /* some applications make this assumption on the DC's origin */
1639 SetViewportOrgEx( hdc
, rect
.left
, rect
.top
, &origorg
);
1640 MENU_DrawBitmapItem(hdc
, lpitem
, &bmprc
, hmenu
, hwndOwner
,
1642 SetViewportOrgEx( hdc
, origorg
.x
, origorg
.y
, NULL
);
1644 /* Draw the popup-menu arrow */
1645 if (lpitem
->fType
& MF_POPUP
)
1646 draw_popup_arrow( hdc
, rect
, arrow_bitmap_width
,
1647 arrow_bitmap_height
);
1649 if( !(menu
->dwStyle
& MNS_NOCHECK
))
1650 rect
.left
+= check_bitmap_width
;
1651 rect
.right
-= arrow_bitmap_width
;
1653 else if( lpitem
->hbmpItem
)
1654 { /* Draw the bitmap */
1657 SetViewportOrgEx( hdc
, rect
.left
, rect
.top
, &origorg
);
1658 MENU_DrawBitmapItem( hdc
, lpitem
, &bmprc
, hmenu
, hwndOwner
,
1660 SetViewportOrgEx( hdc
, origorg
.x
, origorg
.y
, NULL
);
1662 /* process text if present */
1668 UINT uFormat
= (menuBar
) ?
1669 DT_CENTER
| DT_VCENTER
| DT_SINGLELINE
:
1670 DT_LEFT
| DT_VCENTER
| DT_SINGLELINE
;
1672 if( !(menu
->dwStyle
& MNS_CHECKORBMP
))
1673 rect
.left
+= menu
->textOffset
;
1675 if ( lpitem
->fState
& MFS_DEFAULT
)
1677 hfontOld
= SelectObject( hdc
, get_menu_font(TRUE
) );
1681 if( lpitem
->hbmpItem
)
1682 rect
.left
+= lpitem
->bmpsize
.cx
;
1683 if( !(lpitem
->hbmpItem
== HBMMENU_CALLBACK
))
1684 rect
.left
+= menucharsize
.cx
;
1685 rect
.right
-= menucharsize
.cx
;
1688 for (i
= 0; lpitem
->text
[i
]; i
++)
1689 if ((lpitem
->text
[i
] == '\t') || (lpitem
->text
[i
] == '\b'))
1692 if(lpitem
->fState
& MF_GRAYED
)
1694 if (!(lpitem
->fState
& MF_HILITE
) )
1696 ++rect
.left
; ++rect
.top
; ++rect
.right
; ++rect
.bottom
;
1697 SetTextColor(hdc
, RGB(0xff, 0xff, 0xff));
1698 DrawTextW( hdc
, lpitem
->text
, i
, &rect
, uFormat
);
1699 --rect
.left
; --rect
.top
; --rect
.right
; --rect
.bottom
;
1701 SetTextColor(hdc
, RGB(0x80, 0x80, 0x80));
1704 DrawTextW( hdc
, lpitem
->text
, i
, &rect
, uFormat
);
1706 /* paint the shortcut text */
1707 if (!menuBar
&& lpitem
->text
[i
]) /* There's a tab or flush-right char */
1709 if (lpitem
->text
[i
] == '\t')
1711 rect
.left
= lpitem
->xTab
;
1712 uFormat
= DT_LEFT
| DT_VCENTER
| DT_SINGLELINE
;
1716 rect
.right
= lpitem
->xTab
;
1717 uFormat
= DT_RIGHT
| DT_VCENTER
| DT_SINGLELINE
;
1720 if(lpitem
->fState
& MF_GRAYED
)
1722 if (!(lpitem
->fState
& MF_HILITE
) )
1724 ++rect
.left
; ++rect
.top
; ++rect
.right
; ++rect
.bottom
;
1725 SetTextColor(hdc
, RGB(0xff, 0xff, 0xff));
1726 DrawTextW( hdc
, lpitem
->text
+ i
+ 1, -1, &rect
, uFormat
);
1727 --rect
.left
; --rect
.top
; --rect
.right
; --rect
.bottom
;
1729 SetTextColor(hdc
, RGB(0x80, 0x80, 0x80));
1731 DrawTextW( hdc
, lpitem
->text
+ i
+ 1, -1, &rect
, uFormat
);
1735 SelectObject (hdc
, hfontOld
);
1740 /***********************************************************************
1741 * MENU_DrawPopupMenu
1743 * Paint a popup menu.
1745 static void MENU_DrawPopupMenu( HWND hwnd
, HDC hdc
, HMENU hmenu
)
1747 HBRUSH hPrevBrush
= 0;
1750 TRACE("wnd=%p dc=%p menu=%p\n", hwnd
, hdc
, hmenu
);
1752 GetClientRect( hwnd
, &rect
);
1754 if((hPrevBrush
= SelectObject( hdc
, GetSysColorBrush(COLOR_MENU
) ))
1755 && (SelectObject( hdc
, get_menu_font(FALSE
))))
1759 Rectangle( hdc
, rect
.left
, rect
.top
, rect
.right
, rect
.bottom
);
1761 hPrevPen
= SelectObject( hdc
, GetStockObject( NULL_PEN
) );
1765 BOOL flat_menu
= FALSE
;
1767 SystemParametersInfoW (SPI_GETFLATMENU
, 0, &flat_menu
, 0);
1769 FrameRect(hdc
, &rect
, GetSysColorBrush(COLOR_BTNSHADOW
));
1771 DrawEdge (hdc
, &rect
, EDGE_RAISED
, BF_RECT
);
1773 if( (menu
= MENU_GetMenu( hmenu
)))
1775 TRACE("hmenu %p Style %08x\n", hmenu
, menu
->dwStyle
);
1776 /* draw menu items */
1783 for( u
= menu
->nItems
; u
> 0; u
--, item
++)
1784 MENU_DrawMenuItem( hwnd
, hmenu
, menu
->hwndOwner
, hdc
,
1785 item
, menu
->Height
, FALSE
, ODA_DRAWENTIRE
);
1787 /* draw scroll arrows */
1788 if (menu
->bScrolling
)
1789 MENU_DrawScrollArrows(menu
, hdc
);
1793 SelectObject( hdc
, hPrevBrush
);
1798 /***********************************************************************
1801 * Paint a menu bar. Returns the height of the menu bar.
1802 * called from [windows/nonclient.c]
1804 UINT
MENU_DrawMenuBar( HDC hDC
, LPRECT lprect
, HWND hwnd
)
1807 HMENU hMenu
= GetMenu(hwnd
);
1809 lppop
= MENU_GetMenu( hMenu
);
1810 if (lppop
== NULL
|| lprect
== NULL
)
1812 return GetSystemMetrics(SM_CYMENU
);
1815 return DrawMenuBarTemp(hwnd
, hDC
, lprect
, hMenu
, NULL
);
1819 /***********************************************************************
1822 * Popup menu initialization before WM_ENTERMENULOOP.
1824 static BOOL
MENU_InitPopup( HWND hwndOwner
, HMENU hmenu
, UINT flags
)
1829 TRACE("owner=%p hmenu=%p\n", hwndOwner
, hmenu
);
1831 if (!(menu
= MENU_GetMenu( hmenu
))) return FALSE
;
1833 /* store the owner for DrawItem */
1834 if (!IsWindow( hwndOwner
))
1836 SetLastError( ERROR_INVALID_WINDOW_HANDLE
);
1839 menu
->hwndOwner
= hwndOwner
;
1841 if (flags
& TPM_LAYOUTRTL
)
1842 ex_style
= WS_EX_LAYOUTRTL
;
1844 /* NOTE: In Windows, top menu popup is not owned. */
1845 menu
->hWnd
= CreateWindowExW( ex_style
, (LPCWSTR
)POPUPMENU_CLASS_ATOM
, NULL
,
1846 WS_POPUP
, 0, 0, 0, 0,
1847 hwndOwner
, 0, (HINSTANCE
)GetWindowLongPtrW(hwndOwner
, GWLP_HINSTANCE
),
1849 if( !menu
->hWnd
) return FALSE
;
1854 /***********************************************************************
1857 * Display a popup menu.
1859 static BOOL
MENU_ShowPopup( HWND hwndOwner
, HMENU hmenu
, UINT id
, UINT flags
,
1860 INT x
, INT y
, INT xanchor
, INT yanchor
)
1868 TRACE("owner=%p hmenu=%p id=0x%04x x=0x%04x y=0x%04x xa=0x%04x ya=0x%04x\n",
1869 hwndOwner
, hmenu
, id
, x
, y
, xanchor
, yanchor
);
1871 if (!(menu
= MENU_GetMenu( hmenu
))) return FALSE
;
1872 if (menu
->FocusedItem
!= NO_SELECTED_ITEM
)
1874 menu
->items
[menu
->FocusedItem
].fState
&= ~(MF_HILITE
|MF_MOUSESELECT
);
1875 menu
->FocusedItem
= NO_SELECTED_ITEM
;
1878 menu
->nScrollPos
= 0;
1879 MENU_PopupMenuCalcSize( menu
);
1881 /* adjust popup menu pos so that it fits within the desktop */
1883 width
= menu
->Width
+ GetSystemMetrics(SM_CXBORDER
);
1884 height
= menu
->Height
+ GetSystemMetrics(SM_CYBORDER
);
1886 /* FIXME: should use item rect */
1889 monitor
= MonitorFromPoint( pt
, MONITOR_DEFAULTTONEAREST
);
1890 info
.cbSize
= sizeof(info
);
1891 GetMonitorInfoW( monitor
, &info
);
1893 if (flags
& TPM_LAYOUTRTL
)
1894 flags
^= TPM_RIGHTALIGN
;
1896 if( flags
& TPM_RIGHTALIGN
) x
-= width
;
1897 if( flags
& TPM_CENTERALIGN
) x
-= width
/ 2;
1899 if( flags
& TPM_BOTTOMALIGN
) y
-= height
;
1900 if( flags
& TPM_VCENTERALIGN
) y
-= height
/ 2;
1902 if( x
+ width
> info
.rcWork
.right
)
1904 if( xanchor
&& x
>= width
- xanchor
)
1905 x
-= width
- xanchor
;
1907 if( x
+ width
> info
.rcWork
.right
)
1908 x
= info
.rcWork
.right
- width
;
1910 if( x
< info
.rcWork
.left
) x
= info
.rcWork
.left
;
1912 if( y
+ height
> info
.rcWork
.bottom
)
1914 if( yanchor
&& y
>= height
+ yanchor
)
1915 y
-= height
+ yanchor
;
1917 if( y
+ height
> info
.rcWork
.bottom
)
1918 y
= info
.rcWork
.bottom
- height
;
1920 if( y
< info
.rcWork
.top
) y
= info
.rcWork
.top
;
1923 top_popup
= menu
->hWnd
;
1924 top_popup_hmenu
= hmenu
;
1926 /* Display the window */
1928 SetWindowPos( menu
->hWnd
, HWND_TOPMOST
, x
, y
, width
, height
,
1929 SWP_SHOWWINDOW
| SWP_NOACTIVATE
);
1930 UpdateWindow( menu
->hWnd
);
1935 /***********************************************************************
1936 * MENU_EnsureMenuItemVisible
1939 MENU_EnsureMenuItemVisible(LPPOPUPMENU lppop
, UINT wIndex
, HDC hdc
)
1941 if (lppop
->bScrolling
)
1943 MENUITEM
*item
= &lppop
->items
[wIndex
];
1944 UINT nMaxHeight
= MENU_GetMaxPopupHeight(lppop
);
1945 UINT nOldPos
= lppop
->nScrollPos
;
1947 UINT arrow_bitmap_height
;
1950 GetClientRect(lppop
->hWnd
, &rc
);
1952 GetObjectW(get_down_arrow_bitmap(), sizeof(bmp
), &bmp
);
1953 arrow_bitmap_height
= bmp
.bmHeight
;
1955 rc
.top
+= arrow_bitmap_height
;
1956 rc
.bottom
-= arrow_bitmap_height
+ MENU_BOTTOM_MARGIN
;
1958 nMaxHeight
-= GetSystemMetrics(SM_CYBORDER
) + 2 * arrow_bitmap_height
;
1959 if (item
->rect
.bottom
> lppop
->nScrollPos
+ nMaxHeight
)
1962 lppop
->nScrollPos
= item
->rect
.bottom
- nMaxHeight
;
1963 ScrollWindow(lppop
->hWnd
, 0, nOldPos
- lppop
->nScrollPos
, &rc
, &rc
);
1964 MENU_DrawScrollArrows(lppop
, hdc
);
1966 else if (item
->rect
.top
- MENU_TOP_MARGIN
< lppop
->nScrollPos
)
1968 lppop
->nScrollPos
= item
->rect
.top
- MENU_TOP_MARGIN
;
1969 ScrollWindow(lppop
->hWnd
, 0, nOldPos
- lppop
->nScrollPos
, &rc
, &rc
);
1970 MENU_DrawScrollArrows(lppop
, hdc
);
1976 /***********************************************************************
1979 static void MENU_SelectItem( HWND hwndOwner
, HMENU hmenu
, UINT wIndex
,
1980 BOOL sendMenuSelect
, HMENU topmenu
)
1985 TRACE("owner=%p menu=%p index=0x%04x select=0x%04x\n", hwndOwner
, hmenu
, wIndex
, sendMenuSelect
);
1987 lppop
= MENU_GetMenu( hmenu
);
1988 if ((!lppop
) || (!lppop
->nItems
) || (!lppop
->hWnd
)) return;
1990 if (lppop
->FocusedItem
== wIndex
) return;
1991 if (lppop
->wFlags
& MF_POPUP
) hdc
= GetDC( lppop
->hWnd
);
1992 else hdc
= GetDCEx( lppop
->hWnd
, 0, DCX_CACHE
| DCX_WINDOW
);
1994 top_popup
= lppop
->hWnd
;
1995 top_popup_hmenu
= hmenu
;
1998 SelectObject( hdc
, get_menu_font(FALSE
));
2000 /* Clear previous highlighted item */
2001 if (lppop
->FocusedItem
!= NO_SELECTED_ITEM
)
2003 lppop
->items
[lppop
->FocusedItem
].fState
&= ~(MF_HILITE
|MF_MOUSESELECT
);
2004 MENU_DrawMenuItem(lppop
->hWnd
, hmenu
, hwndOwner
, hdc
,&lppop
->items
[lppop
->FocusedItem
],
2005 lppop
->Height
, !(lppop
->wFlags
& MF_POPUP
),
2009 /* Highlight new item (if any) */
2010 lppop
->FocusedItem
= wIndex
;
2011 if (lppop
->FocusedItem
!= NO_SELECTED_ITEM
)
2013 if(!(lppop
->items
[wIndex
].fType
& MF_SEPARATOR
)) {
2014 lppop
->items
[wIndex
].fState
|= MF_HILITE
;
2015 MENU_EnsureMenuItemVisible(lppop
, wIndex
, hdc
);
2016 MENU_DrawMenuItem( lppop
->hWnd
, hmenu
, hwndOwner
, hdc
,
2017 &lppop
->items
[wIndex
], lppop
->Height
,
2018 !(lppop
->wFlags
& MF_POPUP
), ODA_SELECT
);
2022 MENUITEM
*ip
= &lppop
->items
[lppop
->FocusedItem
];
2023 SendMessageW( hwndOwner
, WM_MENUSELECT
,
2024 MAKEWPARAM(ip
->fType
& MF_POPUP
? wIndex
: ip
->wID
,
2025 ip
->fType
| ip
->fState
|
2026 (lppop
->wFlags
& MF_SYSMENU
)), (LPARAM
)hmenu
);
2029 else if (sendMenuSelect
) {
2032 if((pos
=MENU_FindSubMenu(&topmenu
, hmenu
))!=NO_SELECTED_ITEM
){
2033 POPUPMENU
*ptm
= MENU_GetMenu( topmenu
);
2034 MENUITEM
*ip
= &ptm
->items
[pos
];
2035 SendMessageW( hwndOwner
, WM_MENUSELECT
, MAKEWPARAM(pos
,
2036 ip
->fType
| ip
->fState
|
2037 (ptm
->wFlags
& MF_SYSMENU
)), (LPARAM
)topmenu
);
2041 ReleaseDC( lppop
->hWnd
, hdc
);
2045 /***********************************************************************
2046 * MENU_MoveSelection
2048 * Moves currently selected item according to the offset parameter.
2049 * If there is no selection then it should select the last item if
2050 * offset is ITEM_PREV or the first item if offset is ITEM_NEXT.
2052 static void MENU_MoveSelection( HWND hwndOwner
, HMENU hmenu
, INT offset
)
2057 TRACE("hwnd=%p hmenu=%p off=0x%04x\n", hwndOwner
, hmenu
, offset
);
2059 menu
= MENU_GetMenu( hmenu
);
2060 if ((!menu
) || (!menu
->items
)) return;
2062 if ( menu
->FocusedItem
!= NO_SELECTED_ITEM
)
2064 if( menu
->nItems
== 1 ) return; else
2065 for (i
= menu
->FocusedItem
+ offset
; i
>= 0 && i
< menu
->nItems
2067 if (!(menu
->items
[i
].fType
& MF_SEPARATOR
))
2069 MENU_SelectItem( hwndOwner
, hmenu
, i
, TRUE
, 0 );
2074 for ( i
= (offset
> 0) ? 0 : menu
->nItems
- 1;
2075 i
>= 0 && i
< menu
->nItems
; i
+= offset
)
2076 if (!(menu
->items
[i
].fType
& MF_SEPARATOR
))
2078 MENU_SelectItem( hwndOwner
, hmenu
, i
, TRUE
, 0 );
2084 /**********************************************************************
2087 * Insert (allocate) a new item into a menu.
2089 static MENUITEM
*MENU_InsertItem( HMENU hMenu
, UINT pos
, UINT flags
)
2094 if (!(menu
= MENU_GetMenu(hMenu
)))
2097 /* Find where to insert new item */
2099 if (flags
& MF_BYPOSITION
) {
2100 if (pos
> menu
->nItems
)
2103 if (!MENU_FindItem( &hMenu
, &pos
, flags
))
2106 if (!(menu
= MENU_GetMenu( hMenu
)))
2111 /* Make sure that MDI system buttons stay on the right side.
2112 * Note: XP treats only bitmap handles 1 - 6 as "magic" ones
2113 * regardless of their id.
2115 while (pos
> 0 && (INT_PTR
)menu
->items
[pos
- 1].hbmpItem
>= (INT_PTR
)HBMMENU_SYSTEM
&&
2116 (INT_PTR
)menu
->items
[pos
- 1].hbmpItem
<= (INT_PTR
)HBMMENU_MBAR_CLOSE_D
)
2119 TRACE("inserting at %u flags %x\n", pos
, flags
);
2121 /* Create new items array */
2123 newItems
= HeapAlloc( GetProcessHeap(), 0, sizeof(MENUITEM
) * (menu
->nItems
+1) );
2126 WARN("allocation failed\n" );
2129 if (menu
->nItems
> 0)
2131 /* Copy the old array into the new one */
2132 if (pos
> 0) memcpy( newItems
, menu
->items
, pos
* sizeof(MENUITEM
) );
2133 if (pos
< menu
->nItems
) memcpy( &newItems
[pos
+1], &menu
->items
[pos
],
2134 (menu
->nItems
-pos
)*sizeof(MENUITEM
) );
2135 HeapFree( GetProcessHeap(), 0, menu
->items
);
2137 menu
->items
= newItems
;
2139 memset( &newItems
[pos
], 0, sizeof(*newItems
) );
2140 menu
->Height
= 0; /* force size recalculate */
2141 return &newItems
[pos
];
2145 /**********************************************************************
2146 * MENU_ParseResource
2148 * Parse a standard menu resource and add items to the menu.
2149 * Return a pointer to the end of the resource.
2151 * NOTE: flags is equivalent to the mtOption field
2153 static LPCSTR
MENU_ParseResource( LPCSTR res
, HMENU hMenu
)
2161 flags
= GET_WORD(res
);
2162 end_flag
= flags
& MF_END
;
2163 /* Remove MF_END because it has the same value as MF_HILITE */
2165 res
+= sizeof(WORD
);
2166 if (!(flags
& MF_POPUP
))
2169 res
+= sizeof(WORD
);
2172 res
+= (strlenW(str
) + 1) * sizeof(WCHAR
);
2173 if (flags
& MF_POPUP
)
2175 HMENU hSubMenu
= CreatePopupMenu();
2176 if (!hSubMenu
) return NULL
;
2177 if (!(res
= MENU_ParseResource( res
, hSubMenu
))) return NULL
;
2178 AppendMenuW( hMenu
, flags
, (UINT_PTR
)hSubMenu
, str
);
2180 else /* Not a popup */
2182 AppendMenuW( hMenu
, flags
, id
, *str
? str
: NULL
);
2184 } while (!end_flag
);
2189 /**********************************************************************
2190 * MENUEX_ParseResource
2192 * Parse an extended menu resource and add items to the menu.
2193 * Return a pointer to the end of the resource.
2195 static LPCSTR
MENUEX_ParseResource( LPCSTR res
, HMENU hMenu
)
2201 mii
.cbSize
= sizeof(mii
);
2202 mii
.fMask
= MIIM_STATE
| MIIM_ID
| MIIM_TYPE
;
2203 mii
.fType
= GET_DWORD(res
);
2204 res
+= sizeof(DWORD
);
2205 mii
.fState
= GET_DWORD(res
);
2206 res
+= sizeof(DWORD
);
2207 mii
.wID
= GET_DWORD(res
);
2208 res
+= sizeof(DWORD
);
2209 resinfo
= GET_WORD(res
); /* FIXME: for 16-bit apps this is a byte. */
2210 res
+= sizeof(WORD
);
2211 /* Align the text on a word boundary. */
2212 res
+= (~((UINT_PTR
)res
- 1)) & 1;
2213 mii
.dwTypeData
= (LPWSTR
) res
;
2214 res
+= (1 + strlenW(mii
.dwTypeData
)) * sizeof(WCHAR
);
2215 /* Align the following fields on a dword boundary. */
2216 res
+= (~((UINT_PTR
)res
- 1)) & 3;
2218 TRACE("Menu item: [%08x,%08x,%04x,%04x,%s]\n",
2219 mii
.fType
, mii
.fState
, mii
.wID
, resinfo
, debugstr_w(mii
.dwTypeData
));
2221 if (resinfo
& 1) { /* Pop-up? */
2222 /* DWORD helpid = GET_DWORD(res); FIXME: use this. */
2223 res
+= sizeof(DWORD
);
2224 mii
.hSubMenu
= CreatePopupMenu();
2227 if (!(res
= MENUEX_ParseResource(res
, mii
.hSubMenu
))) {
2228 DestroyMenu(mii
.hSubMenu
);
2231 mii
.fMask
|= MIIM_SUBMENU
;
2232 mii
.fType
|= MF_POPUP
;
2234 else if(!*mii
.dwTypeData
&& !(mii
.fType
& MF_SEPARATOR
))
2236 WARN("Converting NULL menu item %04x, type %04x to SEPARATOR\n",
2237 mii
.wID
, mii
.fType
);
2238 mii
.fType
|= MF_SEPARATOR
;
2240 InsertMenuItemW(hMenu
, -1, MF_BYPOSITION
, &mii
);
2241 } while (!(resinfo
& MF_END
));
2246 /***********************************************************************
2249 * Return the handle of the selected sub-popup menu (if any).
2251 static HMENU
MENU_GetSubPopup( HMENU hmenu
)
2256 menu
= MENU_GetMenu( hmenu
);
2258 if ((!menu
) || (menu
->FocusedItem
== NO_SELECTED_ITEM
)) return 0;
2260 item
= &menu
->items
[menu
->FocusedItem
];
2261 if ((item
->fType
& MF_POPUP
) && (item
->fState
& MF_MOUSESELECT
))
2262 return item
->hSubMenu
;
2267 /***********************************************************************
2268 * MENU_HideSubPopups
2270 * Hide the sub-popup menus of this menu.
2272 static void MENU_HideSubPopups( HWND hwndOwner
, HMENU hmenu
,
2273 BOOL sendMenuSelect
, UINT wFlags
)
2275 POPUPMENU
*menu
= MENU_GetMenu( hmenu
);
2277 TRACE("owner=%p hmenu=%p 0x%04x\n", hwndOwner
, hmenu
, sendMenuSelect
);
2279 if (menu
&& top_popup
)
2285 if (menu
->FocusedItem
!= NO_SELECTED_ITEM
)
2287 item
= &menu
->items
[menu
->FocusedItem
];
2288 if (!(item
->fType
& MF_POPUP
) ||
2289 !(item
->fState
& MF_MOUSESELECT
)) return;
2290 item
->fState
&= ~MF_MOUSESELECT
;
2291 hsubmenu
= item
->hSubMenu
;
2294 if (!(submenu
= MENU_GetMenu( hsubmenu
))) return;
2295 MENU_HideSubPopups( hwndOwner
, hsubmenu
, FALSE
, wFlags
);
2296 MENU_SelectItem( hwndOwner
, hsubmenu
, NO_SELECTED_ITEM
, sendMenuSelect
, 0 );
2297 DestroyWindow( submenu
->hWnd
);
2300 if (!(wFlags
& TPM_NONOTIFY
))
2301 SendMessageW( hwndOwner
, WM_UNINITMENUPOPUP
, (WPARAM
)hsubmenu
,
2302 MAKELPARAM(0, IS_SYSTEM_MENU(submenu
)) );
2307 /***********************************************************************
2310 * Display the sub-menu of the selected item of this menu.
2311 * Return the handle of the submenu, or hmenu if no submenu to display.
2313 static HMENU
MENU_ShowSubPopup( HWND hwndOwner
, HMENU hmenu
,
2314 BOOL selectFirst
, UINT wFlags
)
2321 TRACE("owner=%p hmenu=%p 0x%04x\n", hwndOwner
, hmenu
, selectFirst
);
2323 if (!(menu
= MENU_GetMenu( hmenu
))) return hmenu
;
2325 if (menu
->FocusedItem
== NO_SELECTED_ITEM
) return hmenu
;
2327 item
= &menu
->items
[menu
->FocusedItem
];
2328 if (!(item
->fType
& MF_POPUP
) || (item
->fState
& (MF_GRAYED
| MF_DISABLED
)))
2331 /* message must be sent before using item,
2332 because nearly everything may be changed by the application ! */
2334 /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
2335 if (!(wFlags
& TPM_NONOTIFY
))
2336 SendMessageW( hwndOwner
, WM_INITMENUPOPUP
, (WPARAM
)item
->hSubMenu
,
2337 MAKELPARAM( menu
->FocusedItem
, IS_SYSTEM_MENU(menu
) ));
2339 item
= &menu
->items
[menu
->FocusedItem
];
2342 /* correct item if modified as a reaction to WM_INITMENUPOPUP message */
2343 if (!(item
->fState
& MF_HILITE
))
2345 if (menu
->wFlags
& MF_POPUP
) hdc
= GetDC( menu
->hWnd
);
2346 else hdc
= GetDCEx( menu
->hWnd
, 0, DCX_CACHE
| DCX_WINDOW
);
2348 SelectObject( hdc
, get_menu_font(FALSE
));
2350 item
->fState
|= MF_HILITE
;
2351 MENU_DrawMenuItem( menu
->hWnd
, hmenu
, hwndOwner
, hdc
, item
, menu
->Height
, !(menu
->wFlags
& MF_POPUP
), ODA_DRAWENTIRE
);
2352 ReleaseDC( menu
->hWnd
, hdc
);
2354 if (!item
->rect
.top
&& !item
->rect
.left
&& !item
->rect
.bottom
&& !item
->rect
.right
)
2357 item
->fState
|= MF_MOUSESELECT
;
2359 if (IS_SYSTEM_MENU(menu
))
2361 MENU_InitSysMenuPopup(item
->hSubMenu
,
2362 GetWindowLongW( menu
->hWnd
, GWL_STYLE
),
2363 GetClassLongW( menu
->hWnd
, GCL_STYLE
));
2365 NC_GetSysPopupPos( menu
->hWnd
, &rect
);
2366 if (wFlags
& TPM_LAYOUTRTL
) rect
.left
= rect
.right
;
2367 rect
.top
= rect
.bottom
;
2368 rect
.right
= GetSystemMetrics(SM_CXSIZE
);
2369 rect
.bottom
= GetSystemMetrics(SM_CYSIZE
);
2373 GetWindowRect( menu
->hWnd
, &rect
);
2374 if (menu
->wFlags
& MF_POPUP
)
2376 RECT rc
= item
->rect
;
2378 MENU_AdjustMenuItemRect(menu
, &rc
);
2380 /* The first item in the popup menu has to be at the
2381 same y position as the focused menu item */
2382 if (wFlags
& TPM_LAYOUTRTL
)
2383 rect
.left
+= GetSystemMetrics(SM_CXBORDER
);
2385 rect
.left
+= rc
.right
- GetSystemMetrics(SM_CXBORDER
);
2386 rect
.top
+= rc
.top
- MENU_TOP_MARGIN
;
2387 rect
.right
= rc
.left
- rc
.right
+ GetSystemMetrics(SM_CXBORDER
);
2388 rect
.bottom
= rc
.top
- rc
.bottom
- MENU_TOP_MARGIN
2389 - MENU_BOTTOM_MARGIN
- GetSystemMetrics(SM_CYBORDER
);
2393 if (wFlags
& TPM_LAYOUTRTL
)
2394 rect
.left
= rect
.right
- item
->rect
.left
;
2396 rect
.left
+= item
->rect
.left
;
2397 rect
.top
+= item
->rect
.bottom
;
2398 rect
.right
= item
->rect
.right
- item
->rect
.left
;
2399 rect
.bottom
= item
->rect
.bottom
- item
->rect
.top
;
2403 /* use default alignment for submenus */
2404 wFlags
&= ~(TPM_CENTERALIGN
| TPM_RIGHTALIGN
| TPM_VCENTERALIGN
| TPM_BOTTOMALIGN
);
2406 MENU_InitPopup( hwndOwner
, item
->hSubMenu
, wFlags
);
2408 MENU_ShowPopup( hwndOwner
, item
->hSubMenu
, menu
->FocusedItem
, wFlags
,
2409 rect
.left
, rect
.top
, rect
.right
, rect
.bottom
);
2411 MENU_MoveSelection( hwndOwner
, item
->hSubMenu
, ITEM_NEXT
);
2412 return item
->hSubMenu
;
2417 /**********************************************************************
2420 HWND
MENU_IsMenuActive(void)
2425 /**********************************************************************
2428 * Calls EndMenu() if the hwnd parameter belongs to the menu owner
2430 * Does the (menu stuff) of the default window handling of WM_CANCELMODE
2432 void MENU_EndMenu( HWND hwnd
)
2435 menu
= top_popup_hmenu
? MENU_GetMenu( top_popup_hmenu
) : NULL
;
2436 if (menu
&& (hwnd
== menu
->hWnd
|| hwnd
== menu
->hwndOwner
)) EndMenu();
2439 /***********************************************************************
2442 * Walks menu chain trying to find a menu pt maps to.
2444 static HMENU
MENU_PtMenu( HMENU hMenu
, POINT pt
)
2446 POPUPMENU
*menu
= MENU_GetMenu( hMenu
);
2447 UINT item
= menu
->FocusedItem
;
2450 /* try subpopup first (if any) */
2451 ret
= (item
!= NO_SELECTED_ITEM
&&
2452 (menu
->items
[item
].fType
& MF_POPUP
) &&
2453 (menu
->items
[item
].fState
& MF_MOUSESELECT
))
2454 ? MENU_PtMenu(menu
->items
[item
].hSubMenu
, pt
) : 0;
2456 if (!ret
) /* check the current window (avoiding WM_HITTEST) */
2458 INT ht
= NC_HandleNCHitTest( menu
->hWnd
, pt
);
2459 if( menu
->wFlags
& MF_POPUP
)
2461 if (ht
!= HTNOWHERE
&& ht
!= HTERROR
) ret
= hMenu
;
2463 else if (ht
== HTSYSMENU
)
2464 ret
= get_win_sys_menu( menu
->hWnd
);
2465 else if (ht
== HTMENU
)
2466 ret
= GetMenu( menu
->hWnd
);
2471 /***********************************************************************
2472 * MENU_ExecFocusedItem
2474 * Execute a menu item (for instance when user pressed Enter).
2475 * Return the wID of the executed item. Otherwise, -1 indicating
2476 * that no menu item was executed, -2 if a popup is shown;
2477 * Have to receive the flags for the TrackPopupMenu options to avoid
2478 * sending unwanted message.
2481 static INT
MENU_ExecFocusedItem( MTRACKER
* pmt
, HMENU hMenu
, UINT wFlags
)
2484 POPUPMENU
*menu
= MENU_GetMenu( hMenu
);
2486 TRACE("%p hmenu=%p\n", pmt
, hMenu
);
2488 if (!menu
|| !menu
->nItems
||
2489 (menu
->FocusedItem
== NO_SELECTED_ITEM
)) return -1;
2491 item
= &menu
->items
[menu
->FocusedItem
];
2493 TRACE("hMenu %p wID %08lx hSubMenu %p fType %04x\n", hMenu
, item
->wID
, item
->hSubMenu
, item
->fType
);
2495 if (!(item
->fType
& MF_POPUP
))
2497 if (!(item
->fState
& (MF_GRAYED
| MF_DISABLED
)) && !(item
->fType
& MF_SEPARATOR
))
2499 /* If TPM_RETURNCMD is set you return the id, but
2500 do not send a message to the owner */
2501 if(!(wFlags
& TPM_RETURNCMD
))
2503 if( menu
->wFlags
& MF_SYSMENU
)
2504 PostMessageW( pmt
->hOwnerWnd
, WM_SYSCOMMAND
, item
->wID
,
2505 MAKELPARAM((INT16
)pmt
->pt
.x
, (INT16
)pmt
->pt
.y
) );
2508 POPUPMENU
*topmenu
= MENU_GetMenu( pmt
->hTopMenu
);
2509 DWORD dwStyle
= menu
->dwStyle
| (topmenu
? topmenu
->dwStyle
: 0);
2511 if (dwStyle
& MNS_NOTIFYBYPOS
)
2512 PostMessageW( pmt
->hOwnerWnd
, WM_MENUCOMMAND
, menu
->FocusedItem
,
2515 PostMessageW( pmt
->hOwnerWnd
, WM_COMMAND
, item
->wID
, 0 );
2523 pmt
->hCurrentMenu
= MENU_ShowSubPopup(pmt
->hOwnerWnd
, hMenu
, TRUE
, wFlags
);
2530 /***********************************************************************
2531 * MENU_SwitchTracking
2533 * Helper function for menu navigation routines.
2535 static void MENU_SwitchTracking( MTRACKER
* pmt
, HMENU hPtMenu
, UINT id
, UINT wFlags
)
2537 POPUPMENU
*ptmenu
= MENU_GetMenu( hPtMenu
);
2538 POPUPMENU
*topmenu
= MENU_GetMenu( pmt
->hTopMenu
);
2540 TRACE("%p hmenu=%p 0x%04x\n", pmt
, hPtMenu
, id
);
2542 if( pmt
->hTopMenu
!= hPtMenu
&&
2543 !((ptmenu
->wFlags
| topmenu
->wFlags
) & MF_POPUP
) )
2545 /* both are top level menus (system and menu-bar) */
2546 MENU_HideSubPopups( pmt
->hOwnerWnd
, pmt
->hTopMenu
, FALSE
, wFlags
);
2547 MENU_SelectItem( pmt
->hOwnerWnd
, pmt
->hTopMenu
, NO_SELECTED_ITEM
, FALSE
, 0 );
2548 pmt
->hTopMenu
= hPtMenu
;
2550 else MENU_HideSubPopups( pmt
->hOwnerWnd
, hPtMenu
, FALSE
, wFlags
);
2551 MENU_SelectItem( pmt
->hOwnerWnd
, hPtMenu
, id
, TRUE
, 0 );
2555 /***********************************************************************
2558 * Return TRUE if we can go on with menu tracking.
2560 static BOOL
MENU_ButtonDown( MTRACKER
* pmt
, HMENU hPtMenu
, UINT wFlags
)
2562 TRACE("%p hPtMenu=%p\n", pmt
, hPtMenu
);
2567 POPUPMENU
*ptmenu
= MENU_GetMenu( hPtMenu
);
2570 if( IS_SYSTEM_MENU(ptmenu
) )
2571 item
= ptmenu
->items
;
2573 item
= MENU_FindItemByCoords( ptmenu
, pmt
->pt
, &id
);
2577 if( ptmenu
->FocusedItem
!= id
)
2578 MENU_SwitchTracking( pmt
, hPtMenu
, id
, wFlags
);
2580 /* If the popup menu is not already "popped" */
2581 if(!(item
->fState
& MF_MOUSESELECT
))
2583 pmt
->hCurrentMenu
= MENU_ShowSubPopup( pmt
->hOwnerWnd
, hPtMenu
, FALSE
, wFlags
);
2588 /* Else the click was on the menu bar, finish the tracking */
2593 /***********************************************************************
2596 * Return the value of MENU_ExecFocusedItem if
2597 * the selected item was not a popup. Else open the popup.
2598 * A -1 return value indicates that we go on with menu tracking.
2601 static INT
MENU_ButtonUp( MTRACKER
* pmt
, HMENU hPtMenu
, UINT wFlags
)
2603 TRACE("%p hmenu=%p\n", pmt
, hPtMenu
);
2608 POPUPMENU
*ptmenu
= MENU_GetMenu( hPtMenu
);
2611 if( IS_SYSTEM_MENU(ptmenu
) )
2612 item
= ptmenu
->items
;
2614 item
= MENU_FindItemByCoords( ptmenu
, pmt
->pt
, &id
);
2616 if( item
&& (ptmenu
->FocusedItem
== id
))
2618 debug_print_menuitem ("FocusedItem: ", item
, "");
2620 if( !(item
->fType
& MF_POPUP
) )
2622 INT executedMenuId
= MENU_ExecFocusedItem( pmt
, hPtMenu
, wFlags
);
2623 if (executedMenuId
== -1 || executedMenuId
== -2) return -1;
2624 return executedMenuId
;
2627 /* If we are dealing with the top-level menu */
2628 /* and this is a click on an already "popped" item: */
2629 /* Stop the menu tracking and close the opened submenus */
2630 if((pmt
->hTopMenu
== hPtMenu
) && ptmenu
->bTimeToHide
)
2633 ptmenu
->bTimeToHide
= TRUE
;
2639 /***********************************************************************
2642 * Return TRUE if we can go on with menu tracking.
2644 static BOOL
MENU_MouseMove( MTRACKER
* pmt
, HMENU hPtMenu
, UINT wFlags
)
2646 UINT id
= NO_SELECTED_ITEM
;
2647 POPUPMENU
*ptmenu
= NULL
;
2651 ptmenu
= MENU_GetMenu( hPtMenu
);
2652 if( IS_SYSTEM_MENU(ptmenu
) )
2655 MENU_FindItemByCoords( ptmenu
, pmt
->pt
, &id
);
2658 if( id
== NO_SELECTED_ITEM
)
2660 MENU_SelectItem( pmt
->hOwnerWnd
, pmt
->hCurrentMenu
,
2661 NO_SELECTED_ITEM
, TRUE
, pmt
->hTopMenu
);
2664 else if( ptmenu
->FocusedItem
!= id
)
2666 MENU_SwitchTracking( pmt
, hPtMenu
, id
, wFlags
);
2667 pmt
->hCurrentMenu
= MENU_ShowSubPopup(pmt
->hOwnerWnd
, hPtMenu
, FALSE
, wFlags
);
2673 /***********************************************************************
2676 * NOTE: WM_NEXTMENU documented in Win32 is a bit different.
2678 static LRESULT
MENU_DoNextMenu( MTRACKER
* pmt
, UINT vk
, UINT wFlags
)
2680 POPUPMENU
*menu
= MENU_GetMenu( pmt
->hTopMenu
);
2683 /* When skipping left, we need to do something special after the
2685 if (vk
== VK_LEFT
&& menu
->FocusedItem
== 0)
2689 /* When skipping right, for the non-system menu, we need to
2690 handle the last non-special menu item (ie skip any window
2691 icons such as MDI maximize, restore or close) */
2692 else if ((vk
== VK_RIGHT
) && !IS_SYSTEM_MENU(menu
))
2694 UINT i
= menu
->FocusedItem
+ 1;
2695 while (i
< menu
->nItems
) {
2696 if ((menu
->items
[i
].wID
>= SC_SIZE
&&
2697 menu
->items
[i
].wID
<= SC_RESTORE
)) {
2701 if (i
== menu
->nItems
) {
2705 /* When skipping right, we need to cater for the system menu */
2706 else if ((vk
== VK_RIGHT
) && IS_SYSTEM_MENU(menu
))
2708 if (menu
->FocusedItem
== (menu
->nItems
- 1)) {
2715 MDINEXTMENU next_menu
;
2720 next_menu
.hmenuIn
= (IS_SYSTEM_MENU(menu
)) ? GetSubMenu(pmt
->hTopMenu
,0) : pmt
->hTopMenu
;
2721 next_menu
.hmenuNext
= 0;
2722 next_menu
.hwndNext
= 0;
2723 SendMessageW( pmt
->hOwnerWnd
, WM_NEXTMENU
, vk
, (LPARAM
)&next_menu
);
2725 TRACE("%p [%p] -> %p [%p]\n",
2726 pmt
->hCurrentMenu
, pmt
->hOwnerWnd
, next_menu
.hmenuNext
, next_menu
.hwndNext
);
2728 if (!next_menu
.hmenuNext
|| !next_menu
.hwndNext
)
2730 DWORD style
= GetWindowLongW( pmt
->hOwnerWnd
, GWL_STYLE
);
2731 hNewWnd
= pmt
->hOwnerWnd
;
2732 if( IS_SYSTEM_MENU(menu
) )
2734 /* switch to the menu bar */
2736 if(style
& WS_CHILD
|| !(hNewMenu
= GetMenu(hNewWnd
))) return FALSE
;
2740 menu
= MENU_GetMenu( hNewMenu
);
2741 id
= menu
->nItems
- 1;
2743 /* Skip backwards over any system predefined icons,
2744 eg. MDI close, restore etc icons */
2746 (menu
->items
[id
].wID
>= SC_SIZE
&&
2747 menu
->items
[id
].wID
<= SC_RESTORE
)) id
--;
2750 else if (style
& WS_SYSMENU
)
2752 /* switch to the system menu */
2753 hNewMenu
= get_win_sys_menu( hNewWnd
);
2757 else /* application returned a new menu to switch to */
2759 hNewMenu
= next_menu
.hmenuNext
;
2760 hNewWnd
= WIN_GetFullHandle( next_menu
.hwndNext
);
2762 if( IsMenu(hNewMenu
) && IsWindow(hNewWnd
) )
2764 DWORD style
= GetWindowLongW( hNewWnd
, GWL_STYLE
);
2766 if (style
& WS_SYSMENU
&&
2767 GetSubMenu(get_win_sys_menu(hNewWnd
), 0) == hNewMenu
)
2769 /* get the real system menu */
2770 hNewMenu
= get_win_sys_menu(hNewWnd
);
2772 else if (style
& WS_CHILD
|| GetMenu(hNewWnd
) != hNewMenu
)
2774 /* FIXME: Not sure what to do here;
2775 * perhaps try to track hNewMenu as a popup? */
2777 TRACE(" -- got confused.\n");
2784 if( hNewMenu
!= pmt
->hTopMenu
)
2786 MENU_SelectItem( pmt
->hOwnerWnd
, pmt
->hTopMenu
, NO_SELECTED_ITEM
,
2788 if( pmt
->hCurrentMenu
!= pmt
->hTopMenu
)
2789 MENU_HideSubPopups( pmt
->hOwnerWnd
, pmt
->hTopMenu
, FALSE
, wFlags
);
2792 if( hNewWnd
!= pmt
->hOwnerWnd
)
2794 pmt
->hOwnerWnd
= hNewWnd
;
2795 set_capture_window( pmt
->hOwnerWnd
, GUI_INMENUMODE
, NULL
);
2798 pmt
->hTopMenu
= pmt
->hCurrentMenu
= hNewMenu
; /* all subpopups are hidden */
2799 MENU_SelectItem( pmt
->hOwnerWnd
, pmt
->hTopMenu
, id
, TRUE
, 0 );
2806 /***********************************************************************
2809 * The idea is not to show the popup if the next input message is
2810 * going to hide it anyway.
2812 static BOOL
MENU_SuspendPopup( MTRACKER
* pmt
, UINT16 uMsg
)
2816 msg
.hwnd
= pmt
->hOwnerWnd
;
2818 PeekMessageW( &msg
, 0, uMsg
, uMsg
, PM_NOYIELD
| PM_REMOVE
);
2819 pmt
->trackFlags
|= TF_SKIPREMOVE
;
2824 PeekMessageW( &msg
, 0, 0, 0, PM_NOYIELD
| PM_NOREMOVE
);
2825 if( msg
.message
== WM_KEYUP
|| msg
.message
== WM_PAINT
)
2827 PeekMessageW( &msg
, 0, 0, 0, PM_NOYIELD
| PM_REMOVE
);
2828 PeekMessageW( &msg
, 0, 0, 0, PM_NOYIELD
| PM_NOREMOVE
);
2829 if( msg
.message
== WM_KEYDOWN
&&
2830 (msg
.wParam
== VK_LEFT
|| msg
.wParam
== VK_RIGHT
))
2832 pmt
->trackFlags
|= TF_SUSPENDPOPUP
;
2839 /* failures go through this */
2840 pmt
->trackFlags
&= ~TF_SUSPENDPOPUP
;
2844 /***********************************************************************
2847 * Handle a VK_ESCAPE key event in a menu.
2849 static BOOL
MENU_KeyEscape(MTRACKER
* pmt
, UINT wFlags
)
2851 BOOL bEndMenu
= TRUE
;
2853 if (pmt
->hCurrentMenu
!= pmt
->hTopMenu
)
2855 POPUPMENU
*menu
= MENU_GetMenu(pmt
->hCurrentMenu
);
2857 if (menu
->wFlags
& MF_POPUP
)
2859 HMENU hmenutmp
, hmenuprev
;
2861 hmenuprev
= hmenutmp
= pmt
->hTopMenu
;
2863 /* close topmost popup */
2864 while (hmenutmp
!= pmt
->hCurrentMenu
)
2866 hmenuprev
= hmenutmp
;
2867 hmenutmp
= MENU_GetSubPopup( hmenuprev
);
2870 MENU_HideSubPopups( pmt
->hOwnerWnd
, hmenuprev
, TRUE
, wFlags
);
2871 pmt
->hCurrentMenu
= hmenuprev
;
2879 /***********************************************************************
2882 * Handle a VK_LEFT key event in a menu.
2884 static void MENU_KeyLeft( MTRACKER
* pmt
, UINT wFlags
)
2887 HMENU hmenutmp
, hmenuprev
;
2890 hmenuprev
= hmenutmp
= pmt
->hTopMenu
;
2891 menu
= MENU_GetMenu( hmenutmp
);
2893 /* Try to move 1 column left (if possible) */
2894 if( (prevcol
= MENU_GetStartOfPrevColumn( pmt
->hCurrentMenu
)) !=
2895 NO_SELECTED_ITEM
) {
2897 MENU_SelectItem( pmt
->hOwnerWnd
, pmt
->hCurrentMenu
,
2902 /* close topmost popup */
2903 while (hmenutmp
!= pmt
->hCurrentMenu
)
2905 hmenuprev
= hmenutmp
;
2906 hmenutmp
= MENU_GetSubPopup( hmenuprev
);
2909 MENU_HideSubPopups( pmt
->hOwnerWnd
, hmenuprev
, TRUE
, wFlags
);
2910 pmt
->hCurrentMenu
= hmenuprev
;
2912 if ( (hmenuprev
== pmt
->hTopMenu
) && !(menu
->wFlags
& MF_POPUP
) )
2914 /* move menu bar selection if no more popups are left */
2916 if( !MENU_DoNextMenu( pmt
, VK_LEFT
, wFlags
) )
2917 MENU_MoveSelection( pmt
->hOwnerWnd
, pmt
->hTopMenu
, ITEM_PREV
);
2919 if ( hmenuprev
!= hmenutmp
|| pmt
->trackFlags
& TF_SUSPENDPOPUP
)
2921 /* A sublevel menu was displayed - display the next one
2922 * unless there is another displacement coming up */
2924 if( !MENU_SuspendPopup( pmt
, WM_KEYDOWN
) )
2925 pmt
->hCurrentMenu
= MENU_ShowSubPopup(pmt
->hOwnerWnd
,
2926 pmt
->hTopMenu
, TRUE
, wFlags
);
2932 /***********************************************************************
2935 * Handle a VK_RIGHT key event in a menu.
2937 static void MENU_KeyRight( MTRACKER
* pmt
, UINT wFlags
)
2940 POPUPMENU
*menu
= MENU_GetMenu( pmt
->hTopMenu
);
2943 TRACE("MENU_KeyRight called, cur %p (%s), top %p (%s).\n",
2945 debugstr_w((MENU_GetMenu(pmt
->hCurrentMenu
))->items
[0].text
),
2946 pmt
->hTopMenu
, debugstr_w(menu
->items
[0].text
) );
2948 if ( (menu
->wFlags
& MF_POPUP
) || (pmt
->hCurrentMenu
!= pmt
->hTopMenu
))
2950 /* If already displaying a popup, try to display sub-popup */
2952 hmenutmp
= pmt
->hCurrentMenu
;
2953 pmt
->hCurrentMenu
= MENU_ShowSubPopup(pmt
->hOwnerWnd
, hmenutmp
, TRUE
, wFlags
);
2955 /* if subpopup was displayed then we are done */
2956 if (hmenutmp
!= pmt
->hCurrentMenu
) return;
2959 /* Check to see if there's another column */
2960 if( (nextcol
= MENU_GetStartOfNextColumn( pmt
->hCurrentMenu
)) !=
2961 NO_SELECTED_ITEM
) {
2962 TRACE("Going to %d.\n", nextcol
);
2963 MENU_SelectItem( pmt
->hOwnerWnd
, pmt
->hCurrentMenu
,
2968 if (!(menu
->wFlags
& MF_POPUP
)) /* menu bar tracking */
2970 if( pmt
->hCurrentMenu
!= pmt
->hTopMenu
)
2972 MENU_HideSubPopups( pmt
->hOwnerWnd
, pmt
->hTopMenu
, FALSE
, wFlags
);
2973 hmenutmp
= pmt
->hCurrentMenu
= pmt
->hTopMenu
;
2974 } else hmenutmp
= 0;
2976 /* try to move to the next item */
2977 if( !MENU_DoNextMenu( pmt
, VK_RIGHT
, wFlags
) )
2978 MENU_MoveSelection( pmt
->hOwnerWnd
, pmt
->hTopMenu
, ITEM_NEXT
);
2980 if( hmenutmp
|| pmt
->trackFlags
& TF_SUSPENDPOPUP
)
2981 if( !MENU_SuspendPopup(pmt
, WM_KEYDOWN
) )
2982 pmt
->hCurrentMenu
= MENU_ShowSubPopup(pmt
->hOwnerWnd
,
2983 pmt
->hTopMenu
, TRUE
, wFlags
);
2987 static void CALLBACK
release_capture( BOOL __normal
)
2989 set_capture_window( 0, GUI_INMENUMODE
, NULL
);
2992 /***********************************************************************
2995 * Menu tracking code.
2997 static BOOL
MENU_TrackMenu( HMENU hmenu
, UINT wFlags
, INT x
, INT y
,
2998 HWND hwnd
, const RECT
*lprect
)
3003 INT executedMenuId
= -1;
3005 BOOL enterIdleSent
= FALSE
;
3009 mt
.hCurrentMenu
= hmenu
;
3010 mt
.hTopMenu
= hmenu
;
3011 mt
.hOwnerWnd
= WIN_GetFullHandle( hwnd
);
3015 TRACE("hmenu=%p flags=0x%08x (%d,%d) hwnd=%p %s\n",
3016 hmenu
, wFlags
, x
, y
, hwnd
, wine_dbgstr_rect( lprect
));
3019 if (!(menu
= MENU_GetMenu( hmenu
)))
3021 WARN("Invalid menu handle %p\n", hmenu
);
3022 SetLastError(ERROR_INVALID_MENU_HANDLE
);
3026 if (wFlags
& TPM_BUTTONDOWN
)
3028 /* Get the result in order to start the tracking or not */
3029 fRemove
= MENU_ButtonDown( &mt
, hmenu
, wFlags
);
3030 fEndMenu
= !fRemove
;
3033 if (wFlags
& TF_ENDMENU
) fEndMenu
= TRUE
;
3035 /* owner may not be visible when tracking a popup, so use the menu itself */
3036 capture_win
= (wFlags
& TPM_POPUPMENU
) ? menu
->hWnd
: mt
.hOwnerWnd
;
3037 set_capture_window( capture_win
, GUI_INMENUMODE
, NULL
);
3039 __TRY
while (!fEndMenu
)
3041 menu
= MENU_GetMenu( mt
.hCurrentMenu
);
3042 if (!menu
) /* sometimes happens if I do a window manager close */
3045 /* we have to keep the message in the queue until it's
3046 * clear that menu loop is not over yet. */
3050 if (PeekMessageW( &msg
, 0, 0, 0, PM_NOREMOVE
))
3052 if (!CallMsgFilterW( &msg
, MSGF_MENU
)) break;
3053 /* remove the message from the queue */
3054 PeekMessageW( &msg
, 0, msg
.message
, msg
.message
, PM_REMOVE
);
3060 HWND win
= menu
->wFlags
& MF_POPUP
? menu
->hWnd
: 0;
3061 enterIdleSent
= TRUE
;
3062 SendMessageW( mt
.hOwnerWnd
, WM_ENTERIDLE
, MSGF_MENU
, (LPARAM
)win
);
3068 /* check if EndMenu() tried to cancel us, by posting this message */
3069 if(msg
.message
== WM_CANCELMODE
)
3071 /* we are now out of the loop */
3074 /* remove the message from the queue */
3075 PeekMessageW( &msg
, 0, msg
.message
, msg
.message
, PM_REMOVE
);
3077 /* break out of internal loop, ala ESCAPE */
3081 TranslateMessage( &msg
);
3084 if ( (msg
.hwnd
==menu
->hWnd
) || (msg
.message
!=WM_TIMER
) )
3085 enterIdleSent
=FALSE
;
3088 if ((msg
.message
>= WM_MOUSEFIRST
) && (msg
.message
<= WM_MOUSELAST
))
3091 * Use the mouse coordinates in lParam instead of those in the MSG
3092 * struct to properly handle synthetic messages. They are already
3093 * in screen coordinates.
3095 mt
.pt
.x
= (short)LOWORD(msg
.lParam
);
3096 mt
.pt
.y
= (short)HIWORD(msg
.lParam
);
3098 /* Find a menu for this mouse event */
3099 hmenu
= MENU_PtMenu( mt
.hTopMenu
, mt
.pt
);
3103 /* no WM_NC... messages in captured state */
3105 case WM_RBUTTONDBLCLK
:
3106 case WM_RBUTTONDOWN
:
3107 if (!(wFlags
& TPM_RIGHTBUTTON
)) break;
3109 case WM_LBUTTONDBLCLK
:
3110 case WM_LBUTTONDOWN
:
3111 /* If the message belongs to the menu, removes it from the queue */
3112 /* Else, end menu tracking */
3113 fRemove
= MENU_ButtonDown( &mt
, hmenu
, wFlags
);
3114 fEndMenu
= !fRemove
;
3118 if (!(wFlags
& TPM_RIGHTBUTTON
)) break;
3121 /* Check if a menu was selected by the mouse */
3124 executedMenuId
= MENU_ButtonUp( &mt
, hmenu
, wFlags
);
3125 TRACE("executedMenuId %d\n", executedMenuId
);
3127 /* End the loop if executedMenuId is an item ID */
3128 /* or if the job was done (executedMenuId = 0). */
3129 fEndMenu
= fRemove
= (executedMenuId
!= -1);
3131 /* No menu was selected by the mouse */
3132 /* if the function was called by TrackPopupMenu, continue
3133 with the menu tracking. If not, stop it */
3135 fEndMenu
= !(wFlags
& TPM_POPUPMENU
);
3140 /* the selected menu item must be changed every time */
3141 /* the mouse moves. */
3144 fEndMenu
|= !MENU_MouseMove( &mt
, hmenu
, wFlags
);
3146 } /* switch(msg.message) - mouse */
3148 else if ((msg
.message
>= WM_KEYFIRST
) && (msg
.message
<= WM_KEYLAST
))
3150 fRemove
= TRUE
; /* Keyboard messages are always removed */
3164 MENU_SelectItem( mt
.hOwnerWnd
, mt
.hCurrentMenu
,
3165 NO_SELECTED_ITEM
, FALSE
, 0 );
3166 MENU_MoveSelection( mt
.hOwnerWnd
, mt
.hCurrentMenu
,
3167 (msg
.wParam
== VK_HOME
)? ITEM_NEXT
: ITEM_PREV
);
3171 case VK_DOWN
: /* If on menu bar, pull-down the menu */
3173 menu
= MENU_GetMenu( mt
.hCurrentMenu
);
3174 if (!(menu
->wFlags
& MF_POPUP
))
3175 mt
.hCurrentMenu
= MENU_ShowSubPopup(mt
.hOwnerWnd
, mt
.hTopMenu
, TRUE
, wFlags
);
3176 else /* otherwise try to move selection */
3177 MENU_MoveSelection( mt
.hOwnerWnd
, mt
.hCurrentMenu
,
3178 (msg
.wParam
== VK_UP
)? ITEM_PREV
: ITEM_NEXT
);
3182 MENU_KeyLeft( &mt
, wFlags
);
3186 MENU_KeyRight( &mt
, wFlags
);
3190 fEndMenu
= MENU_KeyEscape(&mt
, wFlags
);
3196 hi
.cbSize
= sizeof(HELPINFO
);
3197 hi
.iContextType
= HELPINFO_MENUITEM
;
3198 if (menu
->FocusedItem
== NO_SELECTED_ITEM
)
3201 hi
.iCtrlId
= menu
->items
[menu
->FocusedItem
].wID
;
3202 hi
.hItemHandle
= hmenu
;
3203 hi
.dwContextId
= menu
->dwContextHelpID
;
3204 hi
.MousePos
= msg
.pt
;
3205 SendMessageW(hwnd
, WM_HELP
, 0, (LPARAM
)&hi
);
3212 break; /* WM_KEYDOWN */
3219 if (msg
.wParam
== '\r' || msg
.wParam
== ' ')
3221 executedMenuId
= MENU_ExecFocusedItem(&mt
,mt
.hCurrentMenu
, wFlags
);
3222 fEndMenu
= (executedMenuId
!= -2);
3227 /* Hack to avoid control chars. */
3228 /* We will find a better way real soon... */
3229 if (msg
.wParam
< 32) break;
3231 pos
= MENU_FindItemByKey( mt
.hOwnerWnd
, mt
.hCurrentMenu
,
3232 LOWORD(msg
.wParam
), FALSE
);
3233 if (pos
== (UINT
)-2) fEndMenu
= TRUE
;
3234 else if (pos
== (UINT
)-1) MessageBeep(0);
3237 MENU_SelectItem( mt
.hOwnerWnd
, mt
.hCurrentMenu
, pos
,
3239 executedMenuId
= MENU_ExecFocusedItem(&mt
,mt
.hCurrentMenu
, wFlags
);
3240 fEndMenu
= (executedMenuId
!= -2);
3244 } /* switch(msg.message) - kbd */
3248 PeekMessageW( &msg
, 0, msg
.message
, msg
.message
, PM_REMOVE
);
3249 DispatchMessageW( &msg
);
3253 if (!fEndMenu
) fRemove
= TRUE
;
3255 /* finally remove message from the queue */
3257 if (fRemove
&& !(mt
.trackFlags
& TF_SKIPREMOVE
) )
3258 PeekMessageW( &msg
, 0, msg
.message
, msg
.message
, PM_REMOVE
);
3259 else mt
.trackFlags
&= ~TF_SKIPREMOVE
;
3261 __FINALLY( release_capture
)
3263 /* If dropdown is still painted and the close box is clicked on
3264 then the menu will be destroyed as part of the DispatchMessage above.
3265 This will then invalidate the menu handle in mt.hTopMenu. We should
3266 check for this first. */
3267 if( IsMenu( mt
.hTopMenu
) )
3269 menu
= MENU_GetMenu( mt
.hTopMenu
);
3271 if( IsWindow( mt
.hOwnerWnd
) )
3273 MENU_HideSubPopups( mt
.hOwnerWnd
, mt
.hTopMenu
, FALSE
, wFlags
);
3275 if (menu
&& (menu
->wFlags
& MF_POPUP
))
3277 DestroyWindow( menu
->hWnd
);
3280 if (!(wFlags
& TPM_NONOTIFY
))
3281 SendMessageW( mt
.hOwnerWnd
, WM_UNINITMENUPOPUP
, (WPARAM
)mt
.hTopMenu
,
3282 MAKELPARAM(0, IS_SYSTEM_MENU(menu
)) );
3284 MENU_SelectItem( mt
.hOwnerWnd
, mt
.hTopMenu
, NO_SELECTED_ITEM
, FALSE
, 0 );
3285 SendMessageW( mt
.hOwnerWnd
, WM_MENUSELECT
, MAKEWPARAM(0,0xffff), 0 );
3288 /* Reset the variable for hiding menu */
3289 if( menu
) menu
->bTimeToHide
= FALSE
;
3292 /* The return value is only used by TrackPopupMenu */
3293 if (!(wFlags
& TPM_RETURNCMD
)) return TRUE
;
3294 if (executedMenuId
== -1) executedMenuId
= 0;
3295 return executedMenuId
;
3298 /***********************************************************************
3301 static BOOL
MENU_InitTracking(HWND hWnd
, HMENU hMenu
, BOOL bPopup
, UINT wFlags
)
3305 TRACE("hwnd=%p hmenu=%p\n", hWnd
, hMenu
);
3309 /* This makes the menus of applications built with Delphi work.
3310 * It also enables menus to be displayed in more than one window,
3311 * but there are some bugs left that need to be fixed in this case.
3313 if (!bPopup
&& (menu
= MENU_GetMenu( hMenu
))) menu
->hWnd
= hWnd
;
3314 if (!top_popup
) top_popup_hmenu
= hMenu
;
3316 /* Send WM_ENTERMENULOOP and WM_INITMENU message only if TPM_NONOTIFY flag is not specified */
3317 if (!(wFlags
& TPM_NONOTIFY
))
3318 SendMessageW( hWnd
, WM_ENTERMENULOOP
, bPopup
, 0 );
3320 SendMessageW( hWnd
, WM_SETCURSOR
, (WPARAM
)hWnd
, HTCAPTION
);
3322 if (!(wFlags
& TPM_NONOTIFY
))
3324 SendMessageW( hWnd
, WM_INITMENU
, (WPARAM
)hMenu
, 0 );
3325 /* If an app changed/recreated menu bar entries in WM_INITMENU
3326 * menu sizes will be recalculated once the menu created/shown.
3333 /***********************************************************************
3336 static BOOL
MENU_ExitTracking(HWND hWnd
, BOOL bPopup
)
3338 TRACE("hwnd=%p\n", hWnd
);
3340 SendMessageW( hWnd
, WM_EXITMENULOOP
, bPopup
, 0 );
3343 top_popup_hmenu
= NULL
;
3347 /***********************************************************************
3348 * MENU_TrackMouseMenuBar
3350 * Menu-bar tracking upon a mouse event. Called from NC_HandleSysCommand().
3352 void MENU_TrackMouseMenuBar( HWND hWnd
, INT ht
, POINT pt
)
3354 HMENU hMenu
= (ht
== HTSYSMENU
) ? get_win_sys_menu( hWnd
) : GetMenu( hWnd
);
3355 UINT wFlags
= TPM_BUTTONDOWN
| TPM_LEFTALIGN
| TPM_LEFTBUTTON
;
3357 TRACE("wnd=%p ht=0x%04x %s\n", hWnd
, ht
, wine_dbgstr_point( &pt
));
3359 if (GetWindowLongW( hWnd
, GWL_EXSTYLE
) & WS_EX_LAYOUTRTL
) wFlags
|= TPM_LAYOUTRTL
;
3362 MENU_InitTracking( hWnd
, hMenu
, FALSE
, wFlags
);
3364 /* fetch the window menu again, it may have changed */
3365 hMenu
= (ht
== HTSYSMENU
) ? get_win_sys_menu( hWnd
) : GetMenu( hWnd
);
3366 MENU_TrackMenu( hMenu
, wFlags
, pt
.x
, pt
.y
, hWnd
, NULL
);
3367 MENU_ExitTracking(hWnd
, FALSE
);
3372 /***********************************************************************
3373 * MENU_TrackKbdMenuBar
3375 * Menu-bar tracking upon a keyboard event. Called from NC_HandleSysCommand().
3377 void MENU_TrackKbdMenuBar( HWND hwnd
, UINT wParam
, WCHAR wChar
)
3379 UINT uItem
= NO_SELECTED_ITEM
;
3381 UINT wFlags
= TPM_LEFTALIGN
| TPM_LEFTBUTTON
;
3383 TRACE("hwnd %p wParam 0x%04x wChar 0x%04x\n", hwnd
, wParam
, wChar
);
3385 /* find window that has a menu */
3387 while (!WIN_ALLOWED_MENU(GetWindowLongW( hwnd
, GWL_STYLE
)))
3388 if (!(hwnd
= GetAncestor( hwnd
, GA_PARENT
))) return;
3390 /* check if we have to track a system menu */
3392 hTrackMenu
= GetMenu( hwnd
);
3393 if (!hTrackMenu
|| IsIconic(hwnd
) || wChar
== ' ' )
3395 if (!(GetWindowLongW( hwnd
, GWL_STYLE
) & WS_SYSMENU
)) return;
3396 hTrackMenu
= get_win_sys_menu( hwnd
);
3398 wParam
|= HTSYSMENU
; /* prevent item lookup */
3400 if (GetWindowLongW( hwnd
, GWL_EXSTYLE
) & WS_EX_LAYOUTRTL
) wFlags
|= TPM_LAYOUTRTL
;
3402 if (!IsMenu( hTrackMenu
)) return;
3404 MENU_InitTracking( hwnd
, hTrackMenu
, FALSE
, wFlags
);
3406 /* fetch the window menu again, it may have changed */
3407 hTrackMenu
= (wParam
& HTSYSMENU
) ? get_win_sys_menu( hwnd
) : GetMenu( hwnd
);
3409 if( wChar
&& wChar
!= ' ' )
3411 uItem
= MENU_FindItemByKey( hwnd
, hTrackMenu
, wChar
, (wParam
& HTSYSMENU
) );
3412 if ( uItem
>= (UINT
)(-2) )
3414 if( uItem
== (UINT
)(-1) ) MessageBeep(0);
3415 /* schedule end of menu tracking */
3416 wFlags
|= TF_ENDMENU
;
3421 MENU_SelectItem( hwnd
, hTrackMenu
, uItem
, TRUE
, 0 );
3423 if (!(wParam
& HTSYSMENU
) || wChar
== ' ')
3425 if( uItem
== NO_SELECTED_ITEM
)
3426 MENU_MoveSelection( hwnd
, hTrackMenu
, ITEM_NEXT
);
3428 PostMessageW( hwnd
, WM_KEYDOWN
, VK_RETURN
, 0 );
3432 MENU_TrackMenu( hTrackMenu
, wFlags
, 0, 0, hwnd
, NULL
);
3433 MENU_ExitTracking( hwnd
, FALSE
);
3436 /**********************************************************************
3437 * TrackPopupMenuEx (USER32.@)
3439 BOOL WINAPI
TrackPopupMenuEx( HMENU hMenu
, UINT wFlags
, INT x
, INT y
,
3440 HWND hWnd
, LPTPMPARAMS lpTpm
)
3445 TRACE("hmenu %p flags %04x (%d,%d) hwnd %p lpTpm %p rect %s\n",
3446 hMenu
, wFlags
, x
, y
, hWnd
, lpTpm
,
3447 lpTpm
? wine_dbgstr_rect( &lpTpm
->rcExclude
) : "-" );
3449 /* Parameter check */
3450 /* FIXME: this check is performed several times, here and in the called
3451 functions. That could be optimized */
3452 if (!(menu
= MENU_GetMenu( hMenu
)))
3454 SetLastError( ERROR_INVALID_MENU_HANDLE
);
3458 if (IsWindow(menu
->hWnd
))
3460 SetLastError( ERROR_POPUP_ALREADY_ACTIVE
);
3464 if (MENU_InitPopup( hWnd
, hMenu
, wFlags
))
3466 MENU_InitTracking(hWnd
, hMenu
, TRUE
, wFlags
);
3468 /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
3469 if (!(wFlags
& TPM_NONOTIFY
))
3470 SendMessageW( hWnd
, WM_INITMENUPOPUP
, (WPARAM
)hMenu
, 0);
3472 if (MENU_ShowPopup( hWnd
, hMenu
, 0, wFlags
, x
, y
, 0, 0 ))
3473 ret
= MENU_TrackMenu( hMenu
, wFlags
| TPM_POPUPMENU
, 0, 0, hWnd
,
3474 lpTpm
? &lpTpm
->rcExclude
: NULL
);
3475 MENU_ExitTracking(hWnd
, TRUE
);
3479 DestroyWindow( menu
->hWnd
);
3482 if (!(wFlags
& TPM_NONOTIFY
))
3483 SendMessageW( hWnd
, WM_UNINITMENUPOPUP
, (WPARAM
)hMenu
,
3484 MAKELPARAM(0, IS_SYSTEM_MENU(menu
)) );
3491 /**********************************************************************
3492 * TrackPopupMenu (USER32.@)
3494 * Like the win32 API, the function return the command ID only if the
3495 * flag TPM_RETURNCMD is on.
3498 BOOL WINAPI
TrackPopupMenu( HMENU hMenu
, UINT wFlags
, INT x
, INT y
,
3499 INT nReserved
, HWND hWnd
, const RECT
*lpRect
)
3501 return TrackPopupMenuEx( hMenu
, wFlags
, x
, y
, hWnd
, NULL
);
3504 /***********************************************************************
3507 * NOTE: Windows has totally different (and undocumented) popup wndproc.
3509 LRESULT WINAPI
PopupMenuWndProc( HWND hwnd
, UINT message
, WPARAM wParam
, LPARAM lParam
)
3511 TRACE("hwnd=%p msg=0x%04x wp=0x%04lx lp=0x%08lx\n", hwnd
, message
, wParam
, lParam
);
3517 CREATESTRUCTW
*cs
= (CREATESTRUCTW
*)lParam
;
3518 SetWindowLongPtrW( hwnd
, 0, (LONG_PTR
)cs
->lpCreateParams
);
3522 case WM_MOUSEACTIVATE
: /* We don't want to be activated */
3523 return MA_NOACTIVATE
;
3528 BeginPaint( hwnd
, &ps
);
3529 MENU_DrawPopupMenu( hwnd
, ps
.hdc
,
3530 (HMENU
)GetWindowLongPtrW( hwnd
, 0 ) );
3531 EndPaint( hwnd
, &ps
);
3535 case WM_PRINTCLIENT
:
3537 MENU_DrawPopupMenu( hwnd
, (HDC
)wParam
,
3538 (HMENU
)GetWindowLongPtrW( hwnd
, 0 ) );
3546 /* zero out global pointer in case resident popup window was destroyed. */
3547 if (hwnd
== top_popup
) {
3549 top_popup_hmenu
= NULL
;
3557 if (!GetWindowLongPtrW( hwnd
, 0 )) ERR("no menu to display\n");
3560 SetWindowLongPtrW( hwnd
, 0, 0 );
3563 case MM_SETMENUHANDLE
:
3564 SetWindowLongPtrW( hwnd
, 0, wParam
);
3567 case MM_GETMENUHANDLE
:
3569 return GetWindowLongPtrW( hwnd
, 0 );
3572 return DefWindowProcW( hwnd
, message
, wParam
, lParam
);
3578 /***********************************************************************
3579 * MENU_GetMenuBarHeight
3581 * Compute the size of the menu bar height. Used by NC_HandleNCCalcSize().
3583 UINT
MENU_GetMenuBarHeight( HWND hwnd
, UINT menubarWidth
,
3584 INT orgX
, INT orgY
)
3590 TRACE("HWND %p, width %d, at (%d, %d).\n", hwnd
, menubarWidth
, orgX
, orgY
);
3592 if (!(lppop
= MENU_GetMenu( GetMenu(hwnd
) ))) return 0;
3594 hdc
= GetDCEx( hwnd
, 0, DCX_CACHE
| DCX_WINDOW
);
3595 SelectObject( hdc
, get_menu_font(FALSE
));
3596 SetRect(&rectBar
, orgX
, orgY
, orgX
+menubarWidth
, orgY
+GetSystemMetrics(SM_CYMENU
));
3597 MENU_MenuBarCalcSize( hdc
, &rectBar
, lppop
, hwnd
);
3598 ReleaseDC( hwnd
, hdc
);
3599 return lppop
->Height
;
3603 /*******************************************************************
3604 * ChangeMenuA (USER32.@)
3606 BOOL WINAPI
ChangeMenuA( HMENU hMenu
, UINT pos
, LPCSTR data
,
3607 UINT id
, UINT flags
)
3609 TRACE("menu=%p pos=%d data=%p id=%08x flags=%08x\n", hMenu
, pos
, data
, id
, flags
);
3610 if (flags
& MF_APPEND
) return AppendMenuA( hMenu
, flags
& ~MF_APPEND
,
3612 if (flags
& MF_DELETE
) return DeleteMenu(hMenu
, pos
, flags
& ~MF_DELETE
);
3613 if (flags
& MF_CHANGE
) return ModifyMenuA(hMenu
, pos
, flags
& ~MF_CHANGE
,
3615 if (flags
& MF_REMOVE
) return RemoveMenu( hMenu
,
3616 flags
& MF_BYPOSITION
? pos
: id
,
3617 flags
& ~MF_REMOVE
);
3618 /* Default: MF_INSERT */
3619 return InsertMenuA( hMenu
, pos
, flags
, id
, data
);
3623 /*******************************************************************
3624 * ChangeMenuW (USER32.@)
3626 BOOL WINAPI
ChangeMenuW( HMENU hMenu
, UINT pos
, LPCWSTR data
,
3627 UINT id
, UINT flags
)
3629 TRACE("menu=%p pos=%d data=%p id=%08x flags=%08x\n", hMenu
, pos
, data
, id
, flags
);
3630 if (flags
& MF_APPEND
) return AppendMenuW( hMenu
, flags
& ~MF_APPEND
,
3632 if (flags
& MF_DELETE
) return DeleteMenu(hMenu
, pos
, flags
& ~MF_DELETE
);
3633 if (flags
& MF_CHANGE
) return ModifyMenuW(hMenu
, pos
, flags
& ~MF_CHANGE
,
3635 if (flags
& MF_REMOVE
) return RemoveMenu( hMenu
,
3636 flags
& MF_BYPOSITION
? pos
: id
,
3637 flags
& ~MF_REMOVE
);
3638 /* Default: MF_INSERT */
3639 return InsertMenuW( hMenu
, pos
, flags
, id
, data
);
3643 /*******************************************************************
3644 * CheckMenuItem (USER32.@)
3646 DWORD WINAPI
CheckMenuItem( HMENU hMenu
, UINT id
, UINT flags
)
3651 if (!(item
= MENU_FindItem( &hMenu
, &id
, flags
))) return -1;
3652 ret
= item
->fState
& MF_CHECKED
;
3653 if (flags
& MF_CHECKED
) item
->fState
|= MF_CHECKED
;
3654 else item
->fState
&= ~MF_CHECKED
;
3659 /**********************************************************************
3660 * EnableMenuItem (USER32.@)
3662 BOOL WINAPI
EnableMenuItem( HMENU hMenu
, UINT wItemID
, UINT wFlags
)
3668 TRACE("(%p, %04x, %04x) !\n", hMenu
, wItemID
, wFlags
);
3670 /* Get the Popupmenu to access the owner menu */
3671 if (!(menu
= MENU_GetMenu(hMenu
)))
3674 if (!(item
= MENU_FindItem( &hMenu
, &wItemID
, wFlags
)))
3677 oldflags
= item
->fState
& (MF_GRAYED
| MF_DISABLED
);
3678 item
->fState
^= (oldflags
^ wFlags
) & (MF_GRAYED
| MF_DISABLED
);
3680 /* If the close item in the system menu change update the close button */
3681 if((item
->wID
== SC_CLOSE
) && (oldflags
!= wFlags
))
3683 if (menu
->hSysMenuOwner
!= 0)
3686 POPUPMENU
* parentMenu
;
3688 /* Get the parent menu to access*/
3689 if (!(parentMenu
= MENU_GetMenu(menu
->hSysMenuOwner
)))
3692 /* Refresh the frame to reflect the change */
3693 WIN_GetRectangles( parentMenu
->hWnd
, COORDS_CLIENT
, &rc
, NULL
);
3695 RedrawWindow(parentMenu
->hWnd
, &rc
, 0, RDW_FRAME
| RDW_INVALIDATE
| RDW_NOCHILDREN
);
3703 /*******************************************************************
3704 * GetMenuStringA (USER32.@)
3706 INT WINAPI
GetMenuStringA(
3707 HMENU hMenu
, /* [in] menuhandle */
3708 UINT wItemID
, /* [in] menu item (dep. on wFlags) */
3709 LPSTR str
, /* [out] outbuffer. If NULL, func returns entry length*/
3710 INT nMaxSiz
, /* [in] length of buffer. if 0, func returns entry len*/
3711 UINT wFlags
/* [in] MF_ flags */
3715 TRACE("menu=%p item=%04x ptr=%p len=%d flags=%04x\n", hMenu
, wItemID
, str
, nMaxSiz
, wFlags
);
3716 if (str
&& nMaxSiz
) str
[0] = '\0';
3717 if (!(item
= MENU_FindItem( &hMenu
, &wItemID
, wFlags
))) {
3718 SetLastError( ERROR_MENU_ITEM_NOT_FOUND
);
3721 if (!item
->text
) return 0;
3722 if (!str
|| !nMaxSiz
) return WideCharToMultiByte( CP_ACP
, 0, item
->text
, -1, NULL
, 0, NULL
, NULL
);
3723 if (!WideCharToMultiByte( CP_ACP
, 0, item
->text
, -1, str
, nMaxSiz
, NULL
, NULL
))
3725 TRACE("returning %s\n", debugstr_a(str
));
3730 /*******************************************************************
3731 * GetMenuStringW (USER32.@)
3733 INT WINAPI
GetMenuStringW( HMENU hMenu
, UINT wItemID
,
3734 LPWSTR str
, INT nMaxSiz
, UINT wFlags
)
3738 TRACE("menu=%p item=%04x ptr=%p len=%d flags=%04x\n", hMenu
, wItemID
, str
, nMaxSiz
, wFlags
);
3739 if (str
&& nMaxSiz
) str
[0] = '\0';
3740 if (!(item
= MENU_FindItem( &hMenu
, &wItemID
, wFlags
))) {
3741 SetLastError( ERROR_MENU_ITEM_NOT_FOUND
);
3744 if (!str
|| !nMaxSiz
) return item
->text
? strlenW(item
->text
) : 0;
3745 if( !(item
->text
)) {
3749 lstrcpynW( str
, item
->text
, nMaxSiz
);
3750 TRACE("returning %s\n", debugstr_w(str
));
3751 return strlenW(str
);
3755 /**********************************************************************
3756 * HiliteMenuItem (USER32.@)
3758 BOOL WINAPI
HiliteMenuItem( HWND hWnd
, HMENU hMenu
, UINT wItemID
,
3762 TRACE("(%p, %p, %04x, %04x);\n", hWnd
, hMenu
, wItemID
, wHilite
);
3763 if (!MENU_FindItem( &hMenu
, &wItemID
, wHilite
)) return FALSE
;
3764 if (!(menu
= MENU_GetMenu(hMenu
))) return FALSE
;
3765 if (menu
->FocusedItem
== wItemID
) return TRUE
;
3766 MENU_HideSubPopups( hWnd
, hMenu
, FALSE
, 0 );
3767 MENU_SelectItem( hWnd
, hMenu
, wItemID
, TRUE
, 0 );
3772 /**********************************************************************
3773 * GetMenuState (USER32.@)
3775 UINT WINAPI
GetMenuState( HMENU hMenu
, UINT wItemID
, UINT wFlags
)
3778 TRACE("(menu=%p, id=%04x, flags=%04x);\n", hMenu
, wItemID
, wFlags
);
3779 if (!(item
= MENU_FindItem( &hMenu
, &wItemID
, wFlags
))) return -1;
3780 debug_print_menuitem (" item: ", item
, "");
3781 if (item
->fType
& MF_POPUP
)
3783 POPUPMENU
*menu
= MENU_GetMenu( item
->hSubMenu
);
3784 if (!menu
) return -1;
3785 else return (menu
->nItems
<< 8) | ((item
->fState
|item
->fType
) & 0xff);
3789 /* We used to (from way back then) mask the result to 0xff. */
3790 /* I don't know why and it seems wrong as the documented */
3791 /* return flag MF_SEPARATOR is outside that mask. */
3792 return (item
->fType
| item
->fState
);
3797 /**********************************************************************
3798 * GetMenuItemCount (USER32.@)
3800 INT WINAPI
GetMenuItemCount( HMENU hMenu
)
3802 LPPOPUPMENU menu
= MENU_GetMenu(hMenu
);
3803 if (!menu
) return -1;
3804 TRACE("(%p) returning %d\n", hMenu
, menu
->nItems
);
3805 return menu
->nItems
;
3809 /**********************************************************************
3810 * GetMenuItemID (USER32.@)
3812 UINT WINAPI
GetMenuItemID( HMENU hMenu
, INT nPos
)
3816 if (!(lpmi
= MENU_FindItem(&hMenu
,(UINT
*)&nPos
,MF_BYPOSITION
))) return -1;
3817 if (lpmi
->fType
& MF_POPUP
) return -1;
3823 /**********************************************************************
3826 * Uses flags, id and text ptr, passed by InsertMenu() and
3827 * ModifyMenu() to setup a MenuItemInfo structure.
3829 static void MENU_mnu2mnuii( UINT flags
, UINT_PTR id
, LPCWSTR str
,
3830 LPMENUITEMINFOW pmii
)
3832 ZeroMemory( pmii
, sizeof( MENUITEMINFOW
));
3833 pmii
->cbSize
= sizeof( MENUITEMINFOW
);
3834 pmii
->fMask
= MIIM_STATE
| MIIM_ID
| MIIM_FTYPE
;
3835 /* setting bitmap clears text and vice versa */
3836 if( IS_STRING_ITEM(flags
)) {
3837 pmii
->fMask
|= MIIM_STRING
| MIIM_BITMAP
;
3839 flags
|= MF_SEPARATOR
;
3840 /* Item beginning with a backspace is a help item */
3841 /* FIXME: wrong place, this is only true in win16 */
3842 else if( *str
== '\b') {
3846 pmii
->dwTypeData
= (LPWSTR
)str
;
3847 } else if( flags
& MFT_BITMAP
){
3848 pmii
->fMask
|= MIIM_BITMAP
| MIIM_STRING
;
3849 pmii
->hbmpItem
= (HBITMAP
)str
;
3851 if( flags
& MF_OWNERDRAW
){
3852 pmii
->fMask
|= MIIM_DATA
;
3853 pmii
->dwItemData
= (ULONG_PTR
) str
;
3855 if( flags
& MF_POPUP
) {
3856 pmii
->fMask
|= MIIM_SUBMENU
;
3857 pmii
->hSubMenu
= (HMENU
)id
;
3859 if( flags
& MF_SEPARATOR
) flags
|= MF_GRAYED
| MF_DISABLED
;
3860 pmii
->fState
= flags
& MENUITEMINFO_STATE_MASK
& ~MFS_DEFAULT
;
3861 pmii
->fType
= flags
& MENUITEMINFO_TYPE_MASK
;
3862 pmii
->wID
= (UINT
)id
;
3866 /*******************************************************************
3867 * InsertMenuW (USER32.@)
3869 BOOL WINAPI
InsertMenuW( HMENU hMenu
, UINT pos
, UINT flags
,
3870 UINT_PTR id
, LPCWSTR str
)
3875 if (IS_STRING_ITEM(flags
) && str
)
3876 TRACE("hMenu %p, pos %d, flags %08x, id %04lx, str %s\n",
3877 hMenu
, pos
, flags
, id
, debugstr_w(str
) );
3878 else TRACE("hMenu %p, pos %d, flags %08x, id %04lx, str %p (not a string)\n",
3879 hMenu
, pos
, flags
, id
, str
);
3881 if (!(item
= MENU_InsertItem( hMenu
, pos
, flags
))) return FALSE
;
3882 MENU_mnu2mnuii( flags
, id
, str
, &mii
);
3883 if (!(SetMenuItemInfo_common( item
, &mii
, TRUE
)))
3885 RemoveMenu( hMenu
, pos
, flags
);
3889 item
->hCheckBit
= item
->hUnCheckBit
= 0;
3894 /*******************************************************************
3895 * InsertMenuA (USER32.@)
3897 BOOL WINAPI
InsertMenuA( HMENU hMenu
, UINT pos
, UINT flags
,
3898 UINT_PTR id
, LPCSTR str
)
3902 if (IS_STRING_ITEM(flags
) && str
)
3904 INT len
= MultiByteToWideChar( CP_ACP
, 0, str
, -1, NULL
, 0 );
3905 LPWSTR newstr
= HeapAlloc( GetProcessHeap(), 0, len
* sizeof(WCHAR
) );
3908 MultiByteToWideChar( CP_ACP
, 0, str
, -1, newstr
, len
);
3909 ret
= InsertMenuW( hMenu
, pos
, flags
, id
, newstr
);
3910 HeapFree( GetProcessHeap(), 0, newstr
);
3914 else return InsertMenuW( hMenu
, pos
, flags
, id
, (LPCWSTR
)str
);
3918 /*******************************************************************
3919 * AppendMenuA (USER32.@)
3921 BOOL WINAPI
AppendMenuA( HMENU hMenu
, UINT flags
,
3922 UINT_PTR id
, LPCSTR data
)
3924 return InsertMenuA( hMenu
, -1, flags
| MF_BYPOSITION
, id
, data
);
3928 /*******************************************************************
3929 * AppendMenuW (USER32.@)
3931 BOOL WINAPI
AppendMenuW( HMENU hMenu
, UINT flags
,
3932 UINT_PTR id
, LPCWSTR data
)
3934 return InsertMenuW( hMenu
, -1, flags
| MF_BYPOSITION
, id
, data
);
3938 /**********************************************************************
3939 * RemoveMenu (USER32.@)
3941 BOOL WINAPI
RemoveMenu( HMENU hMenu
, UINT nPos
, UINT wFlags
)
3946 TRACE("(menu=%p pos=%04x flags=%04x)\n",hMenu
, nPos
, wFlags
);
3947 if (!(item
= MENU_FindItem( &hMenu
, &nPos
, wFlags
))) return FALSE
;
3948 if (!(menu
= MENU_GetMenu(hMenu
))) return FALSE
;
3952 MENU_FreeItemData( item
);
3954 if (--menu
->nItems
== 0)
3956 HeapFree( GetProcessHeap(), 0, menu
->items
);
3961 while(nPos
< menu
->nItems
)
3967 menu
->items
= HeapReAlloc( GetProcessHeap(), 0, menu
->items
,
3968 menu
->nItems
* sizeof(MENUITEM
) );
3974 /**********************************************************************
3975 * DeleteMenu (USER32.@)
3977 BOOL WINAPI
DeleteMenu( HMENU hMenu
, UINT nPos
, UINT wFlags
)
3979 MENUITEM
*item
= MENU_FindItem( &hMenu
, &nPos
, wFlags
);
3980 if (!item
) return FALSE
;
3981 if (item
->fType
& MF_POPUP
) DestroyMenu( item
->hSubMenu
);
3982 /* nPos is now the position of the item */
3983 RemoveMenu( hMenu
, nPos
, wFlags
| MF_BYPOSITION
);
3988 /*******************************************************************
3989 * ModifyMenuW (USER32.@)
3991 BOOL WINAPI
ModifyMenuW( HMENU hMenu
, UINT pos
, UINT flags
,
3992 UINT_PTR id
, LPCWSTR str
)
3997 if (IS_STRING_ITEM(flags
))
3998 TRACE("%p %d %04x %04lx %s\n", hMenu
, pos
, flags
, id
, debugstr_w(str
) );
4000 TRACE("%p %d %04x %04lx %p\n", hMenu
, pos
, flags
, id
, str
);
4002 if (!(item
= MENU_FindItem( &hMenu
, &pos
, flags
))) return FALSE
;
4003 MENU_GetMenu(hMenu
)->Height
= 0; /* force size recalculate */
4004 MENU_mnu2mnuii( flags
, id
, str
, &mii
);
4005 return SetMenuItemInfo_common( item
, &mii
, TRUE
);
4009 /*******************************************************************
4010 * ModifyMenuA (USER32.@)
4012 BOOL WINAPI
ModifyMenuA( HMENU hMenu
, UINT pos
, UINT flags
,
4013 UINT_PTR id
, LPCSTR str
)
4017 if (IS_STRING_ITEM(flags
) && str
)
4019 INT len
= MultiByteToWideChar( CP_ACP
, 0, str
, -1, NULL
, 0 );
4020 LPWSTR newstr
= HeapAlloc( GetProcessHeap(), 0, len
* sizeof(WCHAR
) );
4023 MultiByteToWideChar( CP_ACP
, 0, str
, -1, newstr
, len
);
4024 ret
= ModifyMenuW( hMenu
, pos
, flags
, id
, newstr
);
4025 HeapFree( GetProcessHeap(), 0, newstr
);
4029 else return ModifyMenuW( hMenu
, pos
, flags
, id
, (LPCWSTR
)str
);
4033 /**********************************************************************
4034 * CreatePopupMenu (USER32.@)
4036 HMENU WINAPI
CreatePopupMenu(void)
4041 if (!(hmenu
= CreateMenu())) return 0;
4042 menu
= MENU_GetMenu( hmenu
);
4043 menu
->wFlags
|= MF_POPUP
;
4044 menu
->bTimeToHide
= FALSE
;
4049 /**********************************************************************
4050 * GetMenuCheckMarkDimensions (USER.417)
4051 * GetMenuCheckMarkDimensions (USER32.@)
4053 DWORD WINAPI
GetMenuCheckMarkDimensions(void)
4055 return MAKELONG( GetSystemMetrics(SM_CXMENUCHECK
), GetSystemMetrics(SM_CYMENUCHECK
) );
4059 /**********************************************************************
4060 * SetMenuItemBitmaps (USER32.@)
4062 BOOL WINAPI
SetMenuItemBitmaps( HMENU hMenu
, UINT nPos
, UINT wFlags
,
4063 HBITMAP hNewUnCheck
, HBITMAP hNewCheck
)
4067 if (!(item
= MENU_FindItem( &hMenu
, &nPos
, wFlags
))) return FALSE
;
4069 if (!hNewCheck
&& !hNewUnCheck
)
4071 item
->fState
&= ~MF_USECHECKBITMAPS
;
4073 else /* Install new bitmaps */
4075 item
->hCheckBit
= hNewCheck
;
4076 item
->hUnCheckBit
= hNewUnCheck
;
4077 item
->fState
|= MF_USECHECKBITMAPS
;
4083 /**********************************************************************
4084 * CreateMenu (USER32.@)
4086 HMENU WINAPI
CreateMenu(void)
4091 if (!(menu
= HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(*menu
) ))) return 0;
4092 menu
->FocusedItem
= NO_SELECTED_ITEM
;
4093 menu
->bTimeToHide
= FALSE
;
4095 if (!(hMenu
= alloc_user_handle( &menu
->obj
, USER_MENU
))) HeapFree( GetProcessHeap(), 0, menu
);
4097 TRACE("return %p\n", hMenu
);
4103 /**********************************************************************
4104 * DestroyMenu (USER32.@)
4106 BOOL WINAPI
DestroyMenu( HMENU hMenu
)
4110 TRACE("(%p)\n", hMenu
);
4112 if (!(lppop
= free_user_handle( hMenu
, USER_MENU
))) return FALSE
;
4113 if (lppop
== OBJ_OTHER_PROCESS
) return FALSE
;
4115 /* DestroyMenu should not destroy system menu popup owner */
4116 if ((lppop
->wFlags
& (MF_POPUP
| MF_SYSMENU
)) == MF_POPUP
&& lppop
->hWnd
)
4118 DestroyWindow( lppop
->hWnd
);
4122 if (lppop
->items
) /* recursively destroy submenus */
4125 MENUITEM
*item
= lppop
->items
;
4126 for (i
= lppop
->nItems
; i
> 0; i
--, item
++)
4128 if (item
->fType
& MF_POPUP
) DestroyMenu(item
->hSubMenu
);
4129 MENU_FreeItemData( item
);
4131 HeapFree( GetProcessHeap(), 0, lppop
->items
);
4133 HeapFree( GetProcessHeap(), 0, lppop
);
4138 /**********************************************************************
4139 * GetSystemMenu (USER32.@)
4141 HMENU WINAPI
GetSystemMenu( HWND hWnd
, BOOL bRevert
)
4143 WND
*wndPtr
= WIN_GetPtr( hWnd
);
4146 if (wndPtr
== WND_DESKTOP
) return 0;
4147 if (wndPtr
== WND_OTHER_PROCESS
)
4149 if (IsWindow( hWnd
)) FIXME( "not supported on other process window %p\n", hWnd
);
4153 if (wndPtr
->hSysMenu
&& bRevert
)
4155 DestroyMenu(wndPtr
->hSysMenu
);
4156 wndPtr
->hSysMenu
= 0;
4159 if(!wndPtr
->hSysMenu
&& (wndPtr
->dwStyle
& WS_SYSMENU
) )
4160 wndPtr
->hSysMenu
= MENU_GetSysMenu( hWnd
, 0 );
4162 if( wndPtr
->hSysMenu
)
4165 retvalue
= GetSubMenu(wndPtr
->hSysMenu
, 0);
4167 /* Store the dummy sysmenu handle to facilitate the refresh */
4168 /* of the close button if the SC_CLOSE item change */
4169 menu
= MENU_GetMenu(retvalue
);
4171 menu
->hSysMenuOwner
= wndPtr
->hSysMenu
;
4173 WIN_ReleasePtr( wndPtr
);
4175 return bRevert
? 0 : retvalue
;
4179 /*******************************************************************
4180 * SetSystemMenu (USER32.@)
4182 BOOL WINAPI
SetSystemMenu( HWND hwnd
, HMENU hMenu
)
4184 WND
*wndPtr
= WIN_GetPtr( hwnd
);
4186 if (wndPtr
&& wndPtr
!= WND_OTHER_PROCESS
&& wndPtr
!= WND_DESKTOP
)
4188 if (wndPtr
->hSysMenu
) DestroyMenu( wndPtr
->hSysMenu
);
4189 wndPtr
->hSysMenu
= MENU_GetSysMenu( hwnd
, hMenu
);
4190 WIN_ReleasePtr( wndPtr
);
4197 /**********************************************************************
4198 * GetMenu (USER32.@)
4200 HMENU WINAPI
GetMenu( HWND hWnd
)
4202 HMENU retvalue
= (HMENU
)GetWindowLongPtrW( hWnd
, GWLP_ID
);
4203 TRACE("for %p returning %p\n", hWnd
, retvalue
);
4207 /**********************************************************************
4208 * GetMenuBarInfo (USER32.@)
4210 BOOL WINAPI
GetMenuBarInfo( HWND hwnd
, LONG idObject
, LONG idItem
, PMENUBARINFO pmbi
)
4216 TRACE( "(%p,0x%08x,0x%08x,%p)\n", hwnd
, idObject
, idItem
, pmbi
);
4221 class_atom
= GetClassLongW(hwnd
, GCW_ATOM
);
4224 if (class_atom
!= POPUPMENU_CLASS_ATOM
)
4226 WARN("called on invalid window: %d\n", class_atom
);
4227 SetLastError(ERROR_INVALID_MENU_HANDLE
);
4231 hmenu
= (HMENU
)GetWindowLongPtrW(hwnd
, 0);
4234 hmenu
= GetMenu(hwnd
);
4237 hmenu
= GetSystemMenu(hwnd
, FALSE
);
4246 if (pmbi
->cbSize
!= sizeof(MENUBARINFO
))
4248 SetLastError(ERROR_INVALID_PARAMETER
);
4252 menu
= MENU_GetMenu(hmenu
);
4255 if (idItem
< 0 || idItem
> menu
->nItems
)
4260 SetRectEmpty(&pmbi
->rcBar
);
4262 else if (idItem
== 0)
4264 GetMenuItemRect(hwnd
, hmenu
, 0, &pmbi
->rcBar
);
4265 pmbi
->rcBar
.right
= pmbi
->rcBar
.left
+ menu
->Width
;
4266 pmbi
->rcBar
.bottom
= pmbi
->rcBar
.top
+ menu
->Height
;
4270 GetMenuItemRect(hwnd
, hmenu
, idItem
- 1, &pmbi
->rcBar
);
4273 pmbi
->hMenu
= hmenu
;
4274 pmbi
->hwndMenu
= NULL
;
4275 pmbi
->fBarFocused
= top_popup_hmenu
== hmenu
;
4278 pmbi
->fFocused
= menu
->FocusedItem
== idItem
- 1;
4279 if (pmbi
->fFocused
&& (menu
->items
[idItem
- 1].fType
& MF_POPUP
))
4281 menu
= MENU_GetMenu(menu
->items
[idItem
- 1].hSubMenu
);
4283 pmbi
->hwndMenu
= menu
->hWnd
;
4288 pmbi
->fFocused
= pmbi
->fBarFocused
;
4294 /**********************************************************************
4297 * Helper for SetMenu. Also called by WIN_CreateWindowEx to avoid the
4298 * SetWindowPos call that would result if SetMenu were called directly.
4300 BOOL
MENU_SetMenu( HWND hWnd
, HMENU hMenu
)
4302 TRACE("(%p, %p);\n", hWnd
, hMenu
);
4304 if (hMenu
&& !IsMenu(hMenu
))
4306 WARN("hMenu %p is not a menu handle\n", hMenu
);
4309 if (!WIN_ALLOWED_MENU(GetWindowLongW( hWnd
, GWL_STYLE
)))
4312 hWnd
= WIN_GetFullHandle( hWnd
);
4313 if (GetCapture() == hWnd
)
4314 set_capture_window( 0, GUI_INMENUMODE
, NULL
); /* release the capture */
4320 if (!(lpmenu
= MENU_GetMenu(hMenu
))) return FALSE
;
4322 lpmenu
->hWnd
= hWnd
;
4323 lpmenu
->Height
= 0; /* Make sure we recalculate the size */
4325 SetWindowLongPtrW( hWnd
, GWLP_ID
, (LONG_PTR
)hMenu
);
4330 /**********************************************************************
4331 * SetMenu (USER32.@)
4333 BOOL WINAPI
SetMenu( HWND hWnd
, HMENU hMenu
)
4335 if(!MENU_SetMenu(hWnd
, hMenu
))
4338 SetWindowPos( hWnd
, 0, 0, 0, 0, 0, SWP_NOSIZE
| SWP_NOMOVE
|
4339 SWP_NOACTIVATE
| SWP_NOZORDER
| SWP_FRAMECHANGED
);
4344 /**********************************************************************
4345 * GetSubMenu (USER32.@)
4347 HMENU WINAPI
GetSubMenu( HMENU hMenu
, INT nPos
)
4351 if (!(lpmi
= MENU_FindItem(&hMenu
,(UINT
*)&nPos
,MF_BYPOSITION
))) return 0;
4352 if (!(lpmi
->fType
& MF_POPUP
)) return 0;
4353 return lpmi
->hSubMenu
;
4357 /**********************************************************************
4358 * DrawMenuBar (USER32.@)
4360 BOOL WINAPI
DrawMenuBar( HWND hWnd
)
4363 HMENU hMenu
= GetMenu(hWnd
);
4365 if (!WIN_ALLOWED_MENU(GetWindowLongW( hWnd
, GWL_STYLE
)))
4367 if (!hMenu
|| !(lppop
= MENU_GetMenu( hMenu
))) return FALSE
;
4369 lppop
->Height
= 0; /* Make sure we call MENU_MenuBarCalcSize */
4370 lppop
->hwndOwner
= hWnd
;
4371 SetWindowPos( hWnd
, 0, 0, 0, 0, 0, SWP_NOSIZE
| SWP_NOMOVE
|
4372 SWP_NOACTIVATE
| SWP_NOZORDER
| SWP_FRAMECHANGED
);
4376 /***********************************************************************
4377 * DrawMenuBarTemp (USER32.@)
4381 * called by W98SE desk.cpl Control Panel Applet
4383 * Not 100% sure about the param names, but close.
4385 DWORD WINAPI
DrawMenuBarTemp(HWND hwnd
, HDC hDC
, LPRECT lprect
, HMENU hMenu
, HFONT hFont
)
4390 BOOL flat_menu
= FALSE
;
4392 SystemParametersInfoW (SPI_GETFLATMENU
, 0, &flat_menu
, 0);
4395 hMenu
= GetMenu(hwnd
);
4398 hFont
= get_menu_font(FALSE
);
4400 lppop
= MENU_GetMenu( hMenu
);
4401 if (lppop
== NULL
|| lprect
== NULL
)
4403 retvalue
= GetSystemMetrics(SM_CYMENU
);
4407 TRACE("(%p, %p, %p, %p, %p)\n", hwnd
, hDC
, lprect
, hMenu
, hFont
);
4409 hfontOld
= SelectObject( hDC
, hFont
);
4411 if (lppop
->Height
== 0)
4412 MENU_MenuBarCalcSize(hDC
, lprect
, lppop
, hwnd
);
4414 lprect
->bottom
= lprect
->top
+ lppop
->Height
;
4416 FillRect(hDC
, lprect
, GetSysColorBrush(flat_menu
? COLOR_MENUBAR
: COLOR_MENU
) );
4418 SelectObject( hDC
, SYSCOLOR_GetPen(COLOR_3DFACE
));
4419 MoveToEx( hDC
, lprect
->left
, lprect
->bottom
, NULL
);
4420 LineTo( hDC
, lprect
->right
, lprect
->bottom
);
4422 if (lppop
->nItems
== 0)
4424 retvalue
= GetSystemMetrics(SM_CYMENU
);
4428 for (i
= 0; i
< lppop
->nItems
; i
++)
4430 MENU_DrawMenuItem( hwnd
, hMenu
, hwnd
,
4431 hDC
, &lppop
->items
[i
], lppop
->Height
, TRUE
, ODA_DRAWENTIRE
);
4433 retvalue
= lppop
->Height
;
4436 if (hfontOld
) SelectObject (hDC
, hfontOld
);
4440 /***********************************************************************
4441 * EndMenu (USER.187)
4442 * EndMenu (USER32.@)
4444 BOOL WINAPI
EndMenu(void)
4446 /* if we are in the menu code, and it is active */
4447 if (!fEndMenu
&& top_popup
)
4449 /* terminate the menu handling code */
4452 /* needs to be posted to wakeup the internal menu handler */
4453 /* which will now terminate the menu, in the event that */
4454 /* the main window was minimized, or lost focus, so we */
4455 /* don't end up with an orphaned menu */
4456 PostMessageW( top_popup
, WM_CANCELMODE
, 0, 0);
4462 /*****************************************************************
4463 * LoadMenuA (USER32.@)
4465 HMENU WINAPI
LoadMenuA( HINSTANCE instance
, LPCSTR name
)
4467 HRSRC hrsrc
= FindResourceA( instance
, name
, (LPSTR
)RT_MENU
);
4468 if (!hrsrc
) return 0;
4469 return LoadMenuIndirectA( LoadResource( instance
, hrsrc
));
4473 /*****************************************************************
4474 * LoadMenuW (USER32.@)
4476 HMENU WINAPI
LoadMenuW( HINSTANCE instance
, LPCWSTR name
)
4478 HRSRC hrsrc
= FindResourceW( instance
, name
, (LPWSTR
)RT_MENU
);
4479 if (!hrsrc
) return 0;
4480 return LoadMenuIndirectW( LoadResource( instance
, hrsrc
));
4484 /**********************************************************************
4485 * LoadMenuIndirectW (USER32.@)
4487 HMENU WINAPI
LoadMenuIndirectW( LPCVOID
template )
4490 WORD version
, offset
;
4491 LPCSTR p
= template;
4493 version
= GET_WORD(p
);
4495 TRACE("%p, ver %d\n", template, version
);
4498 case 0: /* standard format is version of 0 */
4499 offset
= GET_WORD(p
);
4500 p
+= sizeof(WORD
) + offset
;
4501 if (!(hMenu
= CreateMenu())) return 0;
4502 if (!MENU_ParseResource( p
, hMenu
))
4504 DestroyMenu( hMenu
);
4508 case 1: /* extended format is version of 1 */
4509 offset
= GET_WORD(p
);
4510 p
+= sizeof(WORD
) + offset
;
4511 if (!(hMenu
= CreateMenu())) return 0;
4512 if (!MENUEX_ParseResource( p
, hMenu
))
4514 DestroyMenu( hMenu
);
4519 ERR("version %d not supported.\n", version
);
4525 /**********************************************************************
4526 * LoadMenuIndirectA (USER32.@)
4528 HMENU WINAPI
LoadMenuIndirectA( LPCVOID
template )
4530 return LoadMenuIndirectW( template );
4534 /**********************************************************************
4537 BOOL WINAPI
IsMenu(HMENU hmenu
)
4539 LPPOPUPMENU menu
= MENU_GetMenu(hmenu
);
4543 SetLastError(ERROR_INVALID_MENU_HANDLE
);
4549 /**********************************************************************
4550 * GetMenuItemInfo_common
4553 static BOOL
GetMenuItemInfo_common ( HMENU hmenu
, UINT item
, BOOL bypos
,
4554 LPMENUITEMINFOW lpmii
, BOOL unicode
)
4556 MENUITEM
*menu
= MENU_FindItem (&hmenu
, &item
, bypos
? MF_BYPOSITION
: 0);
4558 debug_print_menuitem("GetMenuItemInfo_common: ", menu
, "");
4561 SetLastError( ERROR_MENU_ITEM_NOT_FOUND
);
4565 if( lpmii
->fMask
& MIIM_TYPE
) {
4566 if( lpmii
->fMask
& ( MIIM_STRING
| MIIM_FTYPE
| MIIM_BITMAP
)) {
4567 WARN("invalid combination of fMask bits used\n");
4568 /* this does not happen on Win9x/ME */
4569 SetLastError( ERROR_INVALID_PARAMETER
);
4572 lpmii
->fType
= menu
->fType
& MENUITEMINFO_TYPE_MASK
;
4573 if( menu
->hbmpItem
) lpmii
->fType
|= MFT_BITMAP
;
4574 lpmii
->hbmpItem
= menu
->hbmpItem
; /* not on Win9x/ME */
4575 if( lpmii
->fType
& MFT_BITMAP
) {
4576 lpmii
->dwTypeData
= (LPWSTR
) menu
->hbmpItem
;
4578 } else if( lpmii
->fType
& (MFT_OWNERDRAW
| MFT_SEPARATOR
)) {
4579 /* this does not happen on Win9x/ME */
4580 lpmii
->dwTypeData
= 0;
4585 /* copy the text string */
4586 if ((lpmii
->fMask
& (MIIM_TYPE
|MIIM_STRING
))) {
4588 if(lpmii
->dwTypeData
&& lpmii
->cch
) {
4591 *((WCHAR
*)lpmii
->dwTypeData
) = 0;
4593 *((CHAR
*)lpmii
->dwTypeData
) = 0;
4599 len
= strlenW(menu
->text
);
4600 if(lpmii
->dwTypeData
&& lpmii
->cch
)
4601 lstrcpynW(lpmii
->dwTypeData
, menu
->text
, lpmii
->cch
);
4605 len
= WideCharToMultiByte( CP_ACP
, 0, menu
->text
, -1, NULL
,
4606 0, NULL
, NULL
) - 1;
4607 if(lpmii
->dwTypeData
&& lpmii
->cch
)
4608 if (!WideCharToMultiByte( CP_ACP
, 0, menu
->text
, -1,
4609 (LPSTR
)lpmii
->dwTypeData
, lpmii
->cch
, NULL
, NULL
))
4610 ((LPSTR
)lpmii
->dwTypeData
)[lpmii
->cch
- 1] = 0;
4612 /* if we've copied a substring we return its length */
4613 if(lpmii
->dwTypeData
&& lpmii
->cch
)
4614 if (lpmii
->cch
<= len
+ 1)
4619 /* return length of string */
4620 /* not on Win9x/ME if fType & MFT_BITMAP */
4626 if (lpmii
->fMask
& MIIM_FTYPE
)
4627 lpmii
->fType
= menu
->fType
& MENUITEMINFO_TYPE_MASK
;
4629 if (lpmii
->fMask
& MIIM_BITMAP
)
4630 lpmii
->hbmpItem
= menu
->hbmpItem
;
4632 if (lpmii
->fMask
& MIIM_STATE
)
4633 lpmii
->fState
= menu
->fState
& MENUITEMINFO_STATE_MASK
;
4635 if (lpmii
->fMask
& MIIM_ID
)
4636 lpmii
->wID
= menu
->wID
;
4638 if (lpmii
->fMask
& MIIM_SUBMENU
)
4639 lpmii
->hSubMenu
= menu
->hSubMenu
;
4641 /* hSubMenu is always cleared
4642 * (not on Win9x/ME ) */
4643 lpmii
->hSubMenu
= 0;
4646 if (lpmii
->fMask
& MIIM_CHECKMARKS
) {
4647 lpmii
->hbmpChecked
= menu
->hCheckBit
;
4648 lpmii
->hbmpUnchecked
= menu
->hUnCheckBit
;
4650 if (lpmii
->fMask
& MIIM_DATA
)
4651 lpmii
->dwItemData
= menu
->dwItemData
;
4656 /**********************************************************************
4657 * GetMenuItemInfoA (USER32.@)
4659 BOOL WINAPI
GetMenuItemInfoA( HMENU hmenu
, UINT item
, BOOL bypos
,
4660 LPMENUITEMINFOA lpmii
)
4664 if( lpmii
->cbSize
!= sizeof( mii
) &&
4665 lpmii
->cbSize
!= sizeof( mii
) - sizeof ( mii
.hbmpItem
)) {
4666 SetLastError( ERROR_INVALID_PARAMETER
);
4669 memcpy( &mii
, lpmii
, lpmii
->cbSize
);
4670 mii
.cbSize
= sizeof( mii
);
4671 ret
= GetMenuItemInfo_common (hmenu
, item
, bypos
,
4672 (LPMENUITEMINFOW
)&mii
, FALSE
);
4673 mii
.cbSize
= lpmii
->cbSize
;
4674 memcpy( lpmii
, &mii
, mii
.cbSize
);
4678 /**********************************************************************
4679 * GetMenuItemInfoW (USER32.@)
4681 BOOL WINAPI
GetMenuItemInfoW( HMENU hmenu
, UINT item
, BOOL bypos
,
4682 LPMENUITEMINFOW lpmii
)
4686 if( lpmii
->cbSize
!= sizeof( mii
) &&
4687 lpmii
->cbSize
!= sizeof( mii
) - sizeof ( mii
.hbmpItem
)) {
4688 SetLastError( ERROR_INVALID_PARAMETER
);
4691 memcpy( &mii
, lpmii
, lpmii
->cbSize
);
4692 mii
.cbSize
= sizeof( mii
);
4693 ret
= GetMenuItemInfo_common (hmenu
, item
, bypos
, &mii
, TRUE
);
4694 mii
.cbSize
= lpmii
->cbSize
;
4695 memcpy( lpmii
, &mii
, mii
.cbSize
);
4700 /* set a menu item text from a ASCII or Unicode string */
4701 static inline void set_menu_item_text( MENUITEM
*menu
, LPCWSTR text
, BOOL unicode
)
4707 if ((menu
->text
= HeapAlloc( GetProcessHeap(), 0, (strlenW(text
)+1) * sizeof(WCHAR
) )))
4708 strcpyW( menu
->text
, text
);
4712 LPCSTR str
= (LPCSTR
)text
;
4713 int len
= MultiByteToWideChar( CP_ACP
, 0, str
, -1, NULL
, 0 );
4714 if ((menu
->text
= HeapAlloc( GetProcessHeap(), 0, len
* sizeof(WCHAR
) )))
4715 MultiByteToWideChar( CP_ACP
, 0, str
, -1, menu
->text
, len
);
4720 /**********************************************************************
4723 * detect if there are loops in the menu tree (or the depth is too large)
4725 static int MENU_depth( POPUPMENU
*pmenu
, int depth
)
4732 if( depth
> MAXMENUDEPTH
) return depth
;
4733 item
= pmenu
->items
;
4735 for( i
= 0; i
< pmenu
->nItems
&& subdepth
<= MAXMENUDEPTH
; i
++, item
++){
4736 POPUPMENU
*psubmenu
= item
->hSubMenu
? MENU_GetMenu( item
->hSubMenu
) : NULL
;
4738 int bdepth
= MENU_depth( psubmenu
, depth
);
4739 if( bdepth
> subdepth
) subdepth
= bdepth
;
4741 if( subdepth
> MAXMENUDEPTH
)
4742 TRACE("<- hmenu %p\n", item
->hSubMenu
);
4748 /**********************************************************************
4749 * SetMenuItemInfo_common
4751 * Note: does not support the MIIM_TYPE flag. Use the MIIM_FTYPE,
4752 * MIIM_BITMAP and MIIM_STRING flags instead.
4755 static BOOL
SetMenuItemInfo_common(MENUITEM
* menu
,
4756 const MENUITEMINFOW
*lpmii
,
4759 if (!menu
) return FALSE
;
4761 debug_print_menuitem("SetMenuItemInfo_common from: ", menu
, "");
4763 if (lpmii
->fMask
& MIIM_FTYPE
) {
4764 menu
->fType
&= ~MENUITEMINFO_TYPE_MASK
;
4765 menu
->fType
|= lpmii
->fType
& MENUITEMINFO_TYPE_MASK
;
4767 if (lpmii
->fMask
& MIIM_STRING
) {
4768 /* free the string when used */
4769 HeapFree(GetProcessHeap(), 0, menu
->text
);
4770 set_menu_item_text( menu
, lpmii
->dwTypeData
, unicode
);
4773 if (lpmii
->fMask
& MIIM_STATE
)
4774 /* Other menu items having MFS_DEFAULT are not converted
4776 menu
->fState
= lpmii
->fState
& MENUITEMINFO_STATE_MASK
;
4778 if (lpmii
->fMask
& MIIM_ID
)
4779 menu
->wID
= lpmii
->wID
;
4781 if (lpmii
->fMask
& MIIM_SUBMENU
) {
4782 menu
->hSubMenu
= lpmii
->hSubMenu
;
4783 if (menu
->hSubMenu
) {
4784 POPUPMENU
*subMenu
= MENU_GetMenu(menu
->hSubMenu
);
4786 if( MENU_depth( subMenu
, 0) > MAXMENUDEPTH
) {
4787 ERR( "Loop detected in menu hierarchy or maximum menu depth exceeded!\n");
4791 subMenu
->wFlags
|= MF_POPUP
;
4792 menu
->fType
|= MF_POPUP
;
4794 SetLastError( ERROR_INVALID_PARAMETER
);
4799 menu
->fType
&= ~MF_POPUP
;
4802 if (lpmii
->fMask
& MIIM_CHECKMARKS
)
4804 menu
->hCheckBit
= lpmii
->hbmpChecked
;
4805 menu
->hUnCheckBit
= lpmii
->hbmpUnchecked
;
4807 if (lpmii
->fMask
& MIIM_DATA
)
4808 menu
->dwItemData
= lpmii
->dwItemData
;
4810 if (lpmii
->fMask
& MIIM_BITMAP
)
4811 menu
->hbmpItem
= lpmii
->hbmpItem
;
4813 if( !menu
->text
&& !(menu
->fType
& MFT_OWNERDRAW
) && !menu
->hbmpItem
)
4814 menu
->fType
|= MFT_SEPARATOR
;
4816 debug_print_menuitem("SetMenuItemInfo_common to : ", menu
, "");
4820 /**********************************************************************
4821 * MENU_NormalizeMenuItemInfoStruct
4823 * Helper for SetMenuItemInfo and InsertMenuItemInfo:
4824 * check, copy and extend the MENUITEMINFO struct from the version that the application
4825 * supplied to the version used by wine source. */
4826 static BOOL
MENU_NormalizeMenuItemInfoStruct( const MENUITEMINFOW
*pmii_in
,
4827 MENUITEMINFOW
*pmii_out
)
4829 /* do we recognize the size? */
4830 if( !pmii_in
|| (pmii_in
->cbSize
!= sizeof( MENUITEMINFOW
) &&
4831 pmii_in
->cbSize
!= sizeof( MENUITEMINFOW
) - sizeof( pmii_in
->hbmpItem
)) ) {
4832 SetLastError( ERROR_INVALID_PARAMETER
);
4835 /* copy the fields that we have */
4836 memcpy( pmii_out
, pmii_in
, pmii_in
->cbSize
);
4837 /* if the hbmpItem member is missing then extend */
4838 if( pmii_in
->cbSize
!= sizeof( MENUITEMINFOW
)) {
4839 pmii_out
->cbSize
= sizeof( MENUITEMINFOW
);
4840 pmii_out
->hbmpItem
= NULL
;
4842 /* test for invalid bit combinations */
4843 if( (pmii_out
->fMask
& MIIM_TYPE
&&
4844 pmii_out
->fMask
& (MIIM_STRING
| MIIM_FTYPE
| MIIM_BITMAP
)) ||
4845 (pmii_out
->fMask
& MIIM_FTYPE
&& pmii_out
->fType
& MFT_BITMAP
)) {
4846 WARN("invalid combination of fMask bits used\n");
4847 /* this does not happen on Win9x/ME */
4848 SetLastError( ERROR_INVALID_PARAMETER
);
4851 /* convert old style (MIIM_TYPE) to the new */
4852 if( pmii_out
->fMask
& MIIM_TYPE
){
4853 pmii_out
->fMask
|= MIIM_FTYPE
;
4854 if( IS_STRING_ITEM(pmii_out
->fType
)){
4855 pmii_out
->fMask
|= MIIM_STRING
;
4856 } else if( (pmii_out
->fType
) & MFT_BITMAP
){
4857 pmii_out
->fMask
|= MIIM_BITMAP
;
4858 pmii_out
->hbmpItem
= UlongToHandle(LOWORD(pmii_out
->dwTypeData
));
4864 /**********************************************************************
4865 * SetMenuItemInfoA (USER32.@)
4867 BOOL WINAPI
SetMenuItemInfoA(HMENU hmenu
, UINT item
, BOOL bypos
,
4868 const MENUITEMINFOA
*lpmii
)
4872 TRACE("hmenu %p, item %u, by pos %d, info %p\n", hmenu
, item
, bypos
, lpmii
);
4874 if (!MENU_NormalizeMenuItemInfoStruct( (const MENUITEMINFOW
*)lpmii
, &mii
)) return FALSE
;
4876 return SetMenuItemInfo_common(MENU_FindItem(&hmenu
, &item
, bypos
? MF_BYPOSITION
: 0),
4880 /**********************************************************************
4881 * SetMenuItemInfoW (USER32.@)
4883 BOOL WINAPI
SetMenuItemInfoW(HMENU hmenu
, UINT item
, BOOL bypos
,
4884 const MENUITEMINFOW
*lpmii
)
4888 TRACE("hmenu %p, item %u, by pos %d, info %p\n", hmenu
, item
, bypos
, lpmii
);
4890 if (!MENU_NormalizeMenuItemInfoStruct( lpmii
, &mii
)) return FALSE
;
4891 return SetMenuItemInfo_common(MENU_FindItem(&hmenu
,
4892 &item
, bypos
? MF_BYPOSITION
: 0), &mii
, TRUE
);
4895 /**********************************************************************
4896 * SetMenuDefaultItem (USER32.@)
4899 BOOL WINAPI
SetMenuDefaultItem(HMENU hmenu
, UINT uItem
, UINT bypos
)
4905 TRACE("(%p,%d,%d)\n", hmenu
, uItem
, bypos
);
4907 if (!(menu
= MENU_GetMenu(hmenu
))) return FALSE
;
4909 /* reset all default-item flags */
4911 for (i
= 0; i
< menu
->nItems
; i
++, item
++)
4913 item
->fState
&= ~MFS_DEFAULT
;
4916 /* no default item */
4925 if ( uItem
>= menu
->nItems
) return FALSE
;
4926 item
[uItem
].fState
|= MFS_DEFAULT
;
4931 for (i
= 0; i
< menu
->nItems
; i
++, item
++)
4933 if (item
->wID
== uItem
)
4935 item
->fState
|= MFS_DEFAULT
;
4944 /**********************************************************************
4945 * GetMenuDefaultItem (USER32.@)
4947 UINT WINAPI
GetMenuDefaultItem(HMENU hmenu
, UINT bypos
, UINT flags
)
4953 TRACE("(%p,%d,%d)\n", hmenu
, bypos
, flags
);
4955 if (!(menu
= MENU_GetMenu(hmenu
))) return -1;
4957 /* find default item */
4961 if (! item
) return -1;
4963 while ( !( item
->fState
& MFS_DEFAULT
) )
4966 if (i
>= menu
->nItems
) return -1;
4969 /* default: don't return disabled items */
4970 if ( (!(GMDI_USEDISABLED
& flags
)) && (item
->fState
& MFS_DISABLED
)) return -1;
4972 /* search rekursiv when needed */
4973 if ( (item
->fType
& MF_POPUP
) && (flags
& GMDI_GOINTOPOPUPS
) )
4976 ret
= GetMenuDefaultItem( item
->hSubMenu
, bypos
, flags
);
4977 if ( -1 != ret
) return ret
;
4979 /* when item not found in submenu, return the popup item */
4981 return ( bypos
) ? i
: item
->wID
;
4986 /**********************************************************************
4987 * InsertMenuItemA (USER32.@)
4989 BOOL WINAPI
InsertMenuItemA(HMENU hMenu
, UINT uItem
, BOOL bypos
,
4990 const MENUITEMINFOA
*lpmii
)
4995 TRACE("hmenu %p, item %04x, by pos %d, info %p\n", hMenu
, uItem
, bypos
, lpmii
);
4997 if (!MENU_NormalizeMenuItemInfoStruct( (const MENUITEMINFOW
*)lpmii
, &mii
)) return FALSE
;
4999 item
= MENU_InsertItem(hMenu
, uItem
, bypos
? MF_BYPOSITION
: 0 );
5000 return SetMenuItemInfo_common(item
, &mii
, FALSE
);
5004 /**********************************************************************
5005 * InsertMenuItemW (USER32.@)
5007 BOOL WINAPI
InsertMenuItemW(HMENU hMenu
, UINT uItem
, BOOL bypos
,
5008 const MENUITEMINFOW
*lpmii
)
5013 TRACE("hmenu %p, item %04x, by pos %d, info %p\n", hMenu
, uItem
, bypos
, lpmii
);
5015 if (!MENU_NormalizeMenuItemInfoStruct( lpmii
, &mii
)) return FALSE
;
5017 item
= MENU_InsertItem(hMenu
, uItem
, bypos
? MF_BYPOSITION
: 0 );
5018 return SetMenuItemInfo_common(item
, &mii
, TRUE
);
5021 /**********************************************************************
5022 * CheckMenuRadioItem (USER32.@)
5025 BOOL WINAPI
CheckMenuRadioItem(HMENU hMenu
,
5026 UINT first
, UINT last
, UINT check
,
5031 MENUITEM
*mi_first
= NULL
, *mi_check
;
5032 HMENU m_first
, m_check
;
5034 for (i
= first
; i
<= last
; i
++)
5041 mi_first
= MENU_FindItem(&m_first
, &pos
, bypos
);
5042 if (!mi_first
) continue;
5043 mi_check
= mi_first
;
5049 mi_check
= MENU_FindItem(&m_check
, &pos
, bypos
);
5050 if (!mi_check
) continue;
5053 if (m_first
!= m_check
) continue;
5054 if (mi_check
->fType
== MFT_SEPARATOR
) continue;
5058 mi_check
->fType
|= MFT_RADIOCHECK
;
5059 mi_check
->fState
|= MFS_CHECKED
;
5064 /* MSDN is wrong, Windows does not remove MFT_RADIOCHECK */
5065 mi_check
->fState
&= ~MFS_CHECKED
;
5073 /**********************************************************************
5074 * GetMenuItemRect (USER32.@)
5076 * ATTENTION: Here, the returned values in rect are the screen
5077 * coordinates of the item just like if the menu was
5078 * always on the upper left side of the application.
5081 BOOL WINAPI
GetMenuItemRect (HWND hwnd
, HMENU hMenu
, UINT uItem
,
5084 POPUPMENU
*itemMenu
;
5088 TRACE("(%p,%p,%d,%p)\n", hwnd
, hMenu
, uItem
, rect
);
5090 item
= MENU_FindItem (&hMenu
, &uItem
, MF_BYPOSITION
);
5091 referenceHwnd
= hwnd
;
5095 itemMenu
= MENU_GetMenu(hMenu
);
5096 if (itemMenu
== NULL
)
5099 if(itemMenu
->hWnd
== 0)
5101 referenceHwnd
= itemMenu
->hWnd
;
5104 if ((rect
== NULL
) || (item
== NULL
))
5109 MapWindowPoints(referenceHwnd
, 0, (LPPOINT
)rect
, 2);
5114 /**********************************************************************
5115 * SetMenuInfo (USER32.@)
5118 * actually use the items to draw the menu
5119 * (recalculate and/or redraw)
5121 static BOOL
menu_SetMenuInfo( HMENU hMenu
, LPCMENUINFO lpmi
)
5124 if( !(menu
= MENU_GetMenu(hMenu
))) return FALSE
;
5126 if (lpmi
->fMask
& MIM_BACKGROUND
)
5127 menu
->hbrBack
= lpmi
->hbrBack
;
5129 if (lpmi
->fMask
& MIM_HELPID
)
5130 menu
->dwContextHelpID
= lpmi
->dwContextHelpID
;
5132 if (lpmi
->fMask
& MIM_MAXHEIGHT
)
5133 menu
->cyMax
= lpmi
->cyMax
;
5135 if (lpmi
->fMask
& MIM_MENUDATA
)
5136 menu
->dwMenuData
= lpmi
->dwMenuData
;
5138 if (lpmi
->fMask
& MIM_STYLE
)
5139 menu
->dwStyle
= lpmi
->dwStyle
;
5141 if( lpmi
->fMask
& MIM_APPLYTOSUBMENUS
) {
5143 MENUITEM
*item
= menu
->items
;
5144 for( i
= menu
->nItems
; i
; i
--, item
++)
5145 if( item
->fType
& MF_POPUP
)
5146 menu_SetMenuInfo( item
->hSubMenu
, lpmi
);
5151 BOOL WINAPI
SetMenuInfo (HMENU hMenu
, LPCMENUINFO lpmi
)
5153 TRACE("(%p %p)\n", hMenu
, lpmi
);
5154 if( lpmi
&& (lpmi
->cbSize
== sizeof( MENUINFO
)) && (menu_SetMenuInfo( hMenu
, lpmi
))) {
5155 if( lpmi
->fMask
& MIM_STYLE
) {
5156 if (lpmi
->dwStyle
& MNS_AUTODISMISS
) FIXME("MNS_AUTODISMISS unimplemented\n");
5157 if (lpmi
->dwStyle
& MNS_DRAGDROP
) FIXME("MNS_DRAGDROP unimplemented\n");
5158 if (lpmi
->dwStyle
& MNS_MODELESS
) FIXME("MNS_MODELESS unimplemented\n");
5162 SetLastError( ERROR_INVALID_PARAMETER
);
5166 /**********************************************************************
5167 * GetMenuInfo (USER32.@)
5173 BOOL WINAPI
GetMenuInfo (HMENU hMenu
, LPMENUINFO lpmi
)
5176 TRACE("(%p %p)\n", hMenu
, lpmi
);
5178 if (lpmi
&& (lpmi
->cbSize
== sizeof( MENUINFO
)) && (menu
= MENU_GetMenu(hMenu
)))
5181 if (lpmi
->fMask
& MIM_BACKGROUND
)
5182 lpmi
->hbrBack
= menu
->hbrBack
;
5184 if (lpmi
->fMask
& MIM_HELPID
)
5185 lpmi
->dwContextHelpID
= menu
->dwContextHelpID
;
5187 if (lpmi
->fMask
& MIM_MAXHEIGHT
)
5188 lpmi
->cyMax
= menu
->cyMax
;
5190 if (lpmi
->fMask
& MIM_MENUDATA
)
5191 lpmi
->dwMenuData
= menu
->dwMenuData
;
5193 if (lpmi
->fMask
& MIM_STYLE
)
5194 lpmi
->dwStyle
= menu
->dwStyle
;
5198 SetLastError( ERROR_INVALID_PARAMETER
);
5203 /**********************************************************************
5204 * SetMenuContextHelpId (USER32.@)
5206 BOOL WINAPI
SetMenuContextHelpId( HMENU hMenu
, DWORD dwContextHelpID
)
5210 TRACE("(%p 0x%08x)\n", hMenu
, dwContextHelpID
);
5212 if ((menu
= MENU_GetMenu(hMenu
)))
5214 menu
->dwContextHelpID
= dwContextHelpID
;
5221 /**********************************************************************
5222 * GetMenuContextHelpId (USER32.@)
5224 DWORD WINAPI
GetMenuContextHelpId( HMENU hMenu
)
5228 TRACE("(%p)\n", hMenu
);
5230 if ((menu
= MENU_GetMenu(hMenu
)))
5232 return menu
->dwContextHelpID
;
5237 /**********************************************************************
5238 * MenuItemFromPoint (USER32.@)
5240 INT WINAPI
MenuItemFromPoint(HWND hWnd
, HMENU hMenu
, POINT ptScreen
)
5242 POPUPMENU
*menu
= MENU_GetMenu(hMenu
);
5245 /*FIXME: Do we have to handle hWnd here? */
5246 if (!menu
) return -1;
5247 if (!MENU_FindItemByCoords(menu
, ptScreen
, &pos
)) return -1;
5252 /**********************************************************************
5253 * translate_accelerator
5255 static BOOL
translate_accelerator( HWND hWnd
, UINT message
, WPARAM wParam
, LPARAM lParam
,
5256 BYTE fVirt
, WORD key
, WORD cmd
)
5261 if (wParam
!= key
) return FALSE
;
5263 if (GetKeyState(VK_CONTROL
) & 0x8000) mask
|= FCONTROL
;
5264 if (GetKeyState(VK_MENU
) & 0x8000) mask
|= FALT
;
5265 if (GetKeyState(VK_SHIFT
) & 0x8000) mask
|= FSHIFT
;
5267 if (message
== WM_CHAR
|| message
== WM_SYSCHAR
)
5269 if ( !(fVirt
& FVIRTKEY
) && (mask
& FALT
) == (fVirt
& FALT
) )
5271 TRACE_(accel
)("found accel for WM_CHAR: ('%c')\n", LOWORD(wParam
) & 0xff);
5277 if(fVirt
& FVIRTKEY
)
5279 TRACE_(accel
)("found accel for virt_key %04lx (scan %04x)\n",
5280 wParam
, 0xff & HIWORD(lParam
));
5282 if(mask
== (fVirt
& (FSHIFT
| FCONTROL
| FALT
))) goto found
;
5283 TRACE_(accel
)(", but incorrect SHIFT/CTRL/ALT-state\n");
5287 if (!(lParam
& 0x01000000)) /* no special_key */
5289 if ((fVirt
& FALT
) && (lParam
& 0x20000000))
5290 { /* ^^ ALT pressed */
5291 TRACE_(accel
)("found accel for Alt-%c\n", LOWORD(wParam
) & 0xff);
5300 if (message
== WM_KEYUP
|| message
== WM_SYSKEYUP
)
5304 HMENU hMenu
, hSubMenu
, hSysMenu
;
5305 UINT uSysStat
= (UINT
)-1, uStat
= (UINT
)-1, nPos
;
5307 hMenu
= (GetWindowLongW( hWnd
, GWL_STYLE
) & WS_CHILD
) ? 0 : GetMenu(hWnd
);
5308 hSysMenu
= get_win_sys_menu( hWnd
);
5310 /* find menu item and ask application to initialize it */
5311 /* 1. in the system menu */
5312 hSubMenu
= hSysMenu
;
5314 if(MENU_FindItem(&hSubMenu
, &nPos
, MF_BYCOMMAND
))
5318 if (!IsWindowEnabled(hWnd
))
5322 SendMessageW(hWnd
, WM_INITMENU
, (WPARAM
)hSysMenu
, 0L);
5323 if(hSubMenu
!= hSysMenu
)
5325 nPos
= MENU_FindSubMenu(&hSysMenu
, hSubMenu
);
5326 TRACE_(accel
)("hSysMenu = %p, hSubMenu = %p, nPos = %d\n", hSysMenu
, hSubMenu
, nPos
);
5327 SendMessageW(hWnd
, WM_INITMENUPOPUP
, (WPARAM
)hSubMenu
, MAKELPARAM(nPos
, TRUE
));
5329 uSysStat
= GetMenuState(GetSubMenu(hSysMenu
, 0), cmd
, MF_BYCOMMAND
);
5332 else /* 2. in the window's menu */
5336 if(MENU_FindItem(&hSubMenu
, &nPos
, MF_BYCOMMAND
))
5340 if (!IsWindowEnabled(hWnd
))
5344 SendMessageW(hWnd
, WM_INITMENU
, (WPARAM
)hMenu
, 0L);
5345 if(hSubMenu
!= hMenu
)
5347 nPos
= MENU_FindSubMenu(&hMenu
, hSubMenu
);
5348 TRACE_(accel
)("hMenu = %p, hSubMenu = %p, nPos = %d\n", hMenu
, hSubMenu
, nPos
);
5349 SendMessageW(hWnd
, WM_INITMENUPOPUP
, (WPARAM
)hSubMenu
, MAKELPARAM(nPos
, FALSE
));
5351 uStat
= GetMenuState(hMenu
, cmd
, MF_BYCOMMAND
);
5358 if (uSysStat
!= (UINT
)-1)
5360 if (uSysStat
& (MF_DISABLED
|MF_GRAYED
))
5367 if (uStat
!= (UINT
)-1)
5373 if (uStat
& (MF_DISABLED
|MF_GRAYED
))
5385 if( mesg
==WM_COMMAND
)
5387 TRACE_(accel
)(", sending WM_COMMAND, wParam=%0x\n", 0x10000 | cmd
);
5388 SendMessageW(hWnd
, mesg
, 0x10000 | cmd
, 0L);
5390 else if( mesg
==WM_SYSCOMMAND
)
5392 TRACE_(accel
)(", sending WM_SYSCOMMAND, wParam=%0x\n", cmd
);
5393 SendMessageW(hWnd
, mesg
, cmd
, 0x00010000L
);
5397 /* some reasons for NOT sending the WM_{SYS}COMMAND message:
5398 * #0: unknown (please report!)
5399 * #1: for WM_KEYUP,WM_SYSKEYUP
5400 * #2: mouse is captured
5401 * #3: window is disabled
5402 * #4: it's a disabled system menu option
5403 * #5: it's a menu option, but window is iconic
5404 * #6: it's a menu option, but disabled
5406 TRACE_(accel
)(", but won't send WM_{SYS}COMMAND, reason is #%d\n",mesg
);
5408 ERR_(accel
)(" unknown reason - please report!\n");
5413 /**********************************************************************
5414 * TranslateAcceleratorA (USER32.@)
5415 * TranslateAccelerator (USER32.@)
5417 INT WINAPI
TranslateAcceleratorA( HWND hWnd
, HACCEL hAccel
, LPMSG msg
)
5419 switch (msg
->message
)
5423 return TranslateAcceleratorW( hWnd
, hAccel
, msg
);
5429 char ch
= LOWORD(msg
->wParam
);
5431 MultiByteToWideChar(CP_ACP
, 0, &ch
, 1, &wch
, 1);
5432 msgW
.wParam
= MAKEWPARAM(wch
, HIWORD(msg
->wParam
));
5433 return TranslateAcceleratorW( hWnd
, hAccel
, &msgW
);
5441 /**********************************************************************
5442 * TranslateAcceleratorW (USER32.@)
5444 INT WINAPI
TranslateAcceleratorW( HWND hWnd
, HACCEL hAccel
, LPMSG msg
)
5446 ACCEL data
[32], *ptr
= data
;
5449 if (!hWnd
) return 0;
5451 if (msg
->message
!= WM_KEYDOWN
&&
5452 msg
->message
!= WM_SYSKEYDOWN
&&
5453 msg
->message
!= WM_CHAR
&&
5454 msg
->message
!= WM_SYSCHAR
)
5457 TRACE_(accel
)("hAccel %p, hWnd %p, msg->hwnd %p, msg->message %04x, wParam %08lx, lParam %08lx\n",
5458 hAccel
,hWnd
,msg
->hwnd
,msg
->message
,msg
->wParam
,msg
->lParam
);
5460 if (!(count
= CopyAcceleratorTableW( hAccel
, NULL
, 0 ))) return 0;
5461 if (count
> sizeof(data
)/sizeof(data
[0]))
5463 if (!(ptr
= HeapAlloc( GetProcessHeap(), 0, count
* sizeof(*ptr
) ))) return 0;
5465 count
= CopyAcceleratorTableW( hAccel
, ptr
, count
);
5466 for (i
= 0; i
< count
; i
++)
5468 if (translate_accelerator( hWnd
, msg
->message
, msg
->wParam
, msg
->lParam
,
5469 ptr
[i
].fVirt
, ptr
[i
].key
, ptr
[i
].cmd
))
5472 if (ptr
!= data
) HeapFree( GetProcessHeap(), 0, ptr
);