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.
51 #include "wine/server.h"
52 #include "wine/exception.h"
55 #include "user_private.h"
56 #include "wine/debug.h"
58 WINE_DEFAULT_DEBUG_CHANNEL(menu
);
59 WINE_DECLARE_DEBUG_CHANNEL(accel
);
61 /* Menu item structure */
63 /* ----------- MENUITEMINFO Stuff ----------- */
64 UINT fType
; /* Item type. */
65 UINT fState
; /* Item state. */
66 UINT_PTR wID
; /* Item id. */
67 HMENU hSubMenu
; /* Pop-up menu. */
68 HBITMAP hCheckBit
; /* Bitmap when checked. */
69 HBITMAP hUnCheckBit
; /* Bitmap when unchecked. */
70 LPWSTR text
; /* Item text. */
71 ULONG_PTR dwItemData
; /* Application defined. */
72 LPWSTR dwTypeData
; /* depends on fMask */
73 HBITMAP hbmpItem
; /* bitmap */
74 /* ----------- Wine stuff ----------- */
75 RECT rect
; /* Item area (relative to the items_rect).
76 * See MENU_AdjustMenuItemRect(). */
77 UINT xTab
; /* X position of text after Tab */
78 SIZE bmpsize
; /* size needed for the HBMMENU_CALLBACK
82 /* Popup menu structure */
84 struct user_object obj
;
85 WORD wFlags
; /* Menu flags (MF_POPUP, MF_SYSMENU) */
86 WORD Width
; /* Width of the whole menu */
87 WORD Height
; /* Height of the whole menu */
88 UINT nItems
; /* Number of items in the menu */
89 HWND hWnd
; /* Window containing the menu */
90 MENUITEM
*items
; /* Array of menu items */
91 UINT FocusedItem
; /* Currently focused item */
92 HWND hwndOwner
; /* window receiving the messages for ownerdraw */
93 BOOL bScrolling
; /* Scroll arrows are active */
94 UINT nScrollPos
; /* Current scroll position */
95 UINT nTotalHeight
; /* Total height of menu items inside menu */
96 RECT items_rect
; /* Rectangle within which the items lie. Excludes margins and scroll arrows */
98 /* ------------ MENUINFO members ------ */
99 DWORD dwStyle
; /* Extended menu style */
100 UINT cyMax
; /* max height of the whole menu, 0 is screen height */
101 HBRUSH hbrBack
; /* brush for menu background */
102 DWORD dwContextHelpID
;
103 ULONG_PTR dwMenuData
; /* application defined value */
104 HMENU hSysMenuOwner
; /* Handle to the dummy sys menu holder */
105 WORD textOffset
; /* Offset of text when items have both bitmaps and text */
106 } POPUPMENU
, *LPPOPUPMENU
;
108 /* internal flags for menu tracking */
110 #define TF_ENDMENU 0x10000
111 #define TF_SUSPENDPOPUP 0x20000
112 #define TF_SKIPREMOVE 0x40000
113 #define TF_RCVD_BTN_UP 0x80000
118 HMENU hCurrentMenu
; /* current submenu (can be equal to hTopMenu)*/
119 HMENU hTopMenu
; /* initial menu */
120 HWND hOwnerWnd
; /* where notifications are sent */
127 /* Internal MENU_TrackMenu() flags */
128 #define TPM_INTERNAL 0xF0000000
129 #define TPM_BUTTONDOWN 0x40000000 /* menu was clicked before tracking */
130 #define TPM_POPUPMENU 0x20000000 /* menu is a popup menu */
132 /* Space between 2 columns */
133 #define MENU_COL_SPACE 4
135 /* Margins for popup menus */
136 #define MENU_MARGIN 3
138 /* maximum allowed depth of any branch in the menu tree.
139 * This value is slightly larger than in windows (25) to
140 * stay on the safe side. */
141 #define MAXMENUDEPTH 30
143 /* (other menu->FocusedItem values give the position of the focused item) */
144 #define NO_SELECTED_ITEM 0xffff
146 #define MENU_ITEM_TYPE(flags) \
147 ((flags) & (MF_STRING | MF_BITMAP | MF_OWNERDRAW | MF_SEPARATOR))
149 /* macro to test that flags do not indicate bitmap, ownerdraw or separator */
150 #define IS_STRING_ITEM(flags) (MENU_ITEM_TYPE ((flags)) == MF_STRING)
151 #define IS_MAGIC_BITMAP(id) ((id) && ((INT_PTR)(id) < 12) && ((INT_PTR)(id) >= -1))
153 #define IS_SYSTEM_MENU(menu) \
154 (!((menu)->wFlags & MF_POPUP) && ((menu)->wFlags & MF_SYSMENU))
156 #define MENUITEMINFO_TYPE_MASK \
157 (MFT_STRING | MFT_BITMAP | MFT_OWNERDRAW | MFT_SEPARATOR | \
158 MFT_MENUBARBREAK | MFT_MENUBREAK | MFT_RADIOCHECK | \
159 MFT_RIGHTORDER | MFT_RIGHTJUSTIFY /* same as MF_HELP */ )
160 #define TYPE_MASK (MENUITEMINFO_TYPE_MASK | MF_POPUP | MF_SYSMENU)
161 #define STATE_MASK (~TYPE_MASK)
162 #define MENUITEMINFO_STATE_MASK (STATE_MASK & ~(MF_BYPOSITION | MF_MOUSESELECT))
164 static SIZE menucharsize
;
165 static UINT ODitemheight
; /* default owner drawn item height */
167 /* Use global popup window because there's no way 2 menus can
168 * be tracked at the same time. */
169 static HWND top_popup
;
170 static HMENU top_popup_hmenu
;
172 /* Flag set by EndMenu() to force an exit from menu tracking */
173 static BOOL fEndMenu
= FALSE
;
175 DWORD WINAPI
DrawMenuBarTemp(HWND hwnd
, HDC hDC
, LPRECT lprect
, HMENU hMenu
, HFONT hFont
);
177 static BOOL
SetMenuItemInfo_common( MENUITEM
*, const MENUITEMINFOW
*, BOOL
);
179 static BOOL
is_win_menu_disallowed(HWND hwnd
)
181 return (GetWindowLongW(hwnd
, GWL_STYLE
) & (WS_CHILD
| WS_POPUP
)) == WS_CHILD
;
184 /*********************************************************************
185 * menu class descriptor
187 const struct builtin_class_descr MENU_builtin_class
=
189 (LPCWSTR
)POPUPMENU_CLASS_ATOM
, /* name */
190 CS_DROPSHADOW
| CS_SAVEBITS
| CS_DBLCLKS
, /* style */
191 WINPROC_MENU
, /* proc */
192 sizeof(HMENU
), /* extra */
193 IDC_ARROW
, /* cursor */
194 (HBRUSH
)(COLOR_MENU
+1) /* brush */
198 /***********************************************************************
199 * debug_print_menuitem
201 * Print a menuitem in readable form.
204 #define debug_print_menuitem(pre, mp, post) \
205 do { if (TRACE_ON(menu)) do_debug_print_menuitem(pre, mp, post); } while (0)
207 #define MENUOUT(text) \
208 TRACE("%s%s", (count++ ? "," : ""), (text))
210 #define MENUFLAG(bit,text) \
212 if (flags & (bit)) { flags &= ~(bit); MENUOUT ((text)); } \
215 static void do_debug_print_menuitem(const char *prefix
, const MENUITEM
*mp
,
218 static const char * const hbmmenus
[] = { "HBMMENU_CALLBACK", "", "HBMMENU_SYSTEM",
219 "HBMMENU_MBAR_RESTORE", "HBMMENU_MBAR_MINIMIZE", "UNKNOWN BITMAP", "HBMMENU_MBAR_CLOSE",
220 "HBMMENU_MBAR_CLOSE_D", "HBMMENU_MBAR_MINIMIZE_D", "HBMMENU_POPUP_CLOSE",
221 "HBMMENU_POPUP_RESTORE", "HBMMENU_POPUP_MAXIMIZE", "HBMMENU_POPUP_MINIMIZE"};
222 TRACE("%s ", prefix
);
224 UINT flags
= mp
->fType
;
225 TRACE( "{ ID=0x%lx", mp
->wID
);
227 TRACE( ", Sub=%p", mp
->hSubMenu
);
231 MENUFLAG( MFT_SEPARATOR
, "sep");
232 MENUFLAG( MFT_OWNERDRAW
, "own");
233 MENUFLAG( MFT_BITMAP
, "bit");
234 MENUFLAG(MF_POPUP
, "pop");
235 MENUFLAG(MFT_MENUBARBREAK
, "barbrk");
236 MENUFLAG(MFT_MENUBREAK
, "brk");
237 MENUFLAG(MFT_RADIOCHECK
, "radio");
238 MENUFLAG(MFT_RIGHTORDER
, "rorder");
239 MENUFLAG(MF_SYSMENU
, "sys");
240 MENUFLAG(MFT_RIGHTJUSTIFY
, "right"); /* same as MF_HELP */
242 TRACE( "+0x%x", flags
);
248 MENUFLAG(MFS_GRAYED
, "grey");
249 MENUFLAG(MFS_DEFAULT
, "default");
250 MENUFLAG(MFS_DISABLED
, "dis");
251 MENUFLAG(MFS_CHECKED
, "check");
252 MENUFLAG(MFS_HILITE
, "hi");
253 MENUFLAG(MF_USECHECKBITMAPS
, "usebit");
254 MENUFLAG(MF_MOUSESELECT
, "mouse");
256 TRACE( "+0x%x", flags
);
259 TRACE( ", Chk=%p", mp
->hCheckBit
);
261 TRACE( ", Unc=%p", mp
->hUnCheckBit
);
263 TRACE( ", Text=%s", debugstr_w(mp
->text
));
265 TRACE( ", ItemData=0x%08lx", mp
->dwItemData
);
268 if( IS_MAGIC_BITMAP(mp
->hbmpItem
))
269 TRACE( ", hbitmap=%s", hbmmenus
[ (INT_PTR
)mp
->hbmpItem
+ 1]);
271 TRACE( ", hbitmap=%p", mp
->hbmpItem
);
276 TRACE(" %s\n", postfix
);
283 /***********************************************************************
286 * Validate the given menu handle and returns the menu structure pointer.
288 static POPUPMENU
*MENU_GetMenu(HMENU hMenu
)
290 POPUPMENU
*menu
= get_user_handle_ptr( hMenu
, USER_MENU
);
292 if (menu
== OBJ_OTHER_PROCESS
)
294 WARN( "other process menu %p?\n", hMenu
);
297 if (menu
) release_user_handle_ptr( menu
); /* FIXME! */
298 else WARN("invalid menu handle=%p\n", hMenu
);
302 static POPUPMENU
*grab_menu_ptr(HMENU hMenu
)
304 POPUPMENU
*menu
= get_user_handle_ptr( hMenu
, USER_MENU
);
306 if (menu
== OBJ_OTHER_PROCESS
)
308 WARN("other process menu %p?\n", hMenu
);
315 WARN("invalid menu handle=%p\n", hMenu
);
319 static void release_menu_ptr(POPUPMENU
*menu
)
324 release_user_handle_ptr(menu
);
328 /***********************************************************************
331 * Get the system menu of a window
333 static HMENU
get_win_sys_menu( HWND hwnd
)
336 WND
*win
= WIN_GetPtr( hwnd
);
337 if (win
&& win
!= WND_OTHER_PROCESS
&& win
!= WND_DESKTOP
)
340 WIN_ReleasePtr( win
);
345 /***********************************************************************
348 static HFONT
get_menu_font( BOOL bold
)
350 static HFONT hMenuFont
, hMenuFontBold
;
352 HFONT ret
= bold
? hMenuFontBold
: hMenuFont
;
356 NONCLIENTMETRICSW ncm
;
359 ncm
.cbSize
= sizeof(NONCLIENTMETRICSW
);
360 SystemParametersInfoW(SPI_GETNONCLIENTMETRICS
, sizeof(NONCLIENTMETRICSW
), &ncm
, 0);
364 ncm
.lfMenuFont
.lfWeight
+= 300;
365 if (ncm
.lfMenuFont
.lfWeight
> 1000) ncm
.lfMenuFont
.lfWeight
= 1000;
367 if (!(ret
= CreateFontIndirectW( &ncm
.lfMenuFont
))) return 0;
368 prev
= InterlockedCompareExchangePointer( (void **)(bold
? &hMenuFontBold
: &hMenuFont
),
372 /* another thread beat us to it */
380 /***********************************************************************
383 static HBITMAP
get_arrow_bitmap(void)
385 static HBITMAP arrow_bitmap
;
387 if (!arrow_bitmap
) arrow_bitmap
= LoadBitmapW(0, MAKEINTRESOURCEW(OBM_MNARROW
));
391 static inline UINT
get_scroll_arrow_height(const POPUPMENU
*menu
)
393 return menucharsize
.cy
+ 4;
396 /***********************************************************************
399 * Return the default system menu.
401 static HMENU
MENU_CopySysPopup(BOOL mdi
)
403 HMENU hMenu
= LoadMenuW(user32_module
, mdi
? L
"SYSMENUMDI" : L
"SYSMENU");
407 MENUITEMINFOW miteminfo
;
408 POPUPMENU
* menu
= MENU_GetMenu(hMenu
);
409 menu
->wFlags
|= MF_SYSMENU
| MF_POPUP
;
410 /* decorate the menu with bitmaps */
411 minfo
.cbSize
= sizeof( MENUINFO
);
412 minfo
.dwStyle
= MNS_CHECKORBMP
;
413 minfo
.fMask
= MIM_STYLE
;
414 SetMenuInfo( hMenu
, &minfo
);
415 miteminfo
.cbSize
= sizeof( MENUITEMINFOW
);
416 miteminfo
.fMask
= MIIM_BITMAP
;
417 miteminfo
.hbmpItem
= HBMMENU_POPUP_CLOSE
;
418 SetMenuItemInfoW( hMenu
, SC_CLOSE
, FALSE
, &miteminfo
);
419 miteminfo
.hbmpItem
= HBMMENU_POPUP_RESTORE
;
420 SetMenuItemInfoW( hMenu
, SC_RESTORE
, FALSE
, &miteminfo
);
421 miteminfo
.hbmpItem
= HBMMENU_POPUP_MAXIMIZE
;
422 SetMenuItemInfoW( hMenu
, SC_MAXIMIZE
, FALSE
, &miteminfo
);
423 miteminfo
.hbmpItem
= HBMMENU_POPUP_MINIMIZE
;
424 SetMenuItemInfoW( hMenu
, SC_MINIMIZE
, FALSE
, &miteminfo
);
425 SetMenuDefaultItem(hMenu
, SC_CLOSE
, FALSE
);
428 ERR("Unable to load default system menu\n" );
430 TRACE("returning %p (mdi=%d).\n", hMenu
, mdi
);
436 /**********************************************************************
439 * Create a copy of the system menu. System menu in Windows is
440 * a special menu bar with the single entry - system menu popup.
441 * This popup is presented to the outside world as a "system menu".
442 * However, the real system menu handle is sometimes seen in the
443 * WM_MENUSELECT parameters (and Word 6 likes it this way).
445 static HMENU
MENU_GetSysMenu( HWND hWnd
, HMENU hPopupMenu
)
449 TRACE("loading system menu, hWnd %p, hPopupMenu %p\n", hWnd
, hPopupMenu
);
450 if ((hMenu
= CreateMenu()))
452 POPUPMENU
*menu
= MENU_GetMenu(hMenu
);
453 menu
->wFlags
= MF_SYSMENU
;
454 menu
->hWnd
= WIN_GetFullHandle( hWnd
);
455 TRACE("hWnd %p (hMenu %p)\n", menu
->hWnd
, hMenu
);
459 if (GetWindowLongW(hWnd
, GWL_EXSTYLE
) & WS_EX_MDICHILD
)
460 hPopupMenu
= MENU_CopySysPopup(TRUE
);
462 hPopupMenu
= MENU_CopySysPopup(FALSE
);
467 if (GetClassLongW(hWnd
, GCL_STYLE
) & CS_NOCLOSE
)
468 DeleteMenu(hPopupMenu
, SC_CLOSE
, MF_BYCOMMAND
);
470 InsertMenuW( hMenu
, -1, MF_SYSMENU
| MF_POPUP
| MF_BYPOSITION
,
471 (UINT_PTR
)hPopupMenu
, NULL
);
473 menu
->items
[0].fType
= MF_SYSMENU
| MF_POPUP
;
474 menu
->items
[0].fState
= 0;
475 if ((menu
= MENU_GetMenu(hPopupMenu
))) menu
->wFlags
|= MF_SYSMENU
;
477 TRACE("hMenu=%p (hPopup %p)\n", hMenu
, hPopupMenu
);
480 DestroyMenu( hMenu
);
482 ERR("failed to load system menu!\n");
487 /***********************************************************************
488 * MENU_InitSysMenuPopup
490 * Grey the appropriate items in System menu.
492 static void MENU_InitSysMenuPopup( HMENU hmenu
, DWORD style
, DWORD clsStyle
)
496 gray
= !(style
& WS_THICKFRAME
) || (style
& (WS_MAXIMIZE
| WS_MINIMIZE
));
497 EnableMenuItem( hmenu
, SC_SIZE
, (gray
? MF_GRAYED
: MF_ENABLED
) );
498 gray
= ((style
& WS_MAXIMIZE
) != 0);
499 EnableMenuItem( hmenu
, SC_MOVE
, (gray
? MF_GRAYED
: MF_ENABLED
) );
500 gray
= !(style
& WS_MINIMIZEBOX
) || (style
& WS_MINIMIZE
);
501 EnableMenuItem( hmenu
, SC_MINIMIZE
, (gray
? MF_GRAYED
: MF_ENABLED
) );
502 gray
= !(style
& WS_MAXIMIZEBOX
) || (style
& WS_MAXIMIZE
);
503 EnableMenuItem( hmenu
, SC_MAXIMIZE
, (gray
? MF_GRAYED
: MF_ENABLED
) );
504 gray
= !(style
& (WS_MAXIMIZE
| WS_MINIMIZE
));
505 EnableMenuItem( hmenu
, SC_RESTORE
, (gray
? MF_GRAYED
: MF_ENABLED
) );
506 gray
= (clsStyle
& CS_NOCLOSE
) != 0;
508 /* The menu item must keep its state if it's disabled */
510 EnableMenuItem( hmenu
, SC_CLOSE
, MF_GRAYED
);
514 /******************************************************************************
516 * UINT MENU_GetStartOfNextColumn(
519 *****************************************************************************/
521 static UINT
MENU_GetStartOfNextColumn(
524 POPUPMENU
*menu
= MENU_GetMenu(hMenu
);
528 return NO_SELECTED_ITEM
;
530 i
= menu
->FocusedItem
+ 1;
531 if( i
== NO_SELECTED_ITEM
)
534 for( ; i
< menu
->nItems
; ++i
) {
535 if (menu
->items
[i
].fType
& (MF_MENUBREAK
| MF_MENUBARBREAK
))
539 return NO_SELECTED_ITEM
;
543 /******************************************************************************
545 * UINT MENU_GetStartOfPrevColumn(
548 *****************************************************************************/
550 static UINT
MENU_GetStartOfPrevColumn(
553 POPUPMENU
*menu
= MENU_GetMenu(hMenu
);
557 return NO_SELECTED_ITEM
;
559 if( menu
->FocusedItem
== 0 || menu
->FocusedItem
== NO_SELECTED_ITEM
)
560 return NO_SELECTED_ITEM
;
562 /* Find the start of the column */
564 for(i
= menu
->FocusedItem
; i
!= 0 &&
565 !(menu
->items
[i
].fType
& (MF_MENUBREAK
| MF_MENUBARBREAK
));
569 return NO_SELECTED_ITEM
;
571 for(--i
; i
!= 0; --i
) {
572 if (menu
->items
[i
].fType
& (MF_MENUBREAK
| MF_MENUBARBREAK
))
576 TRACE("ret %d.\n", i
);
581 static POPUPMENU
*find_menu_item(HMENU hmenu
, UINT id
, UINT flags
, UINT
*pos
)
583 UINT fallback_pos
= ~0u, i
;
586 menu
= grab_menu_ptr(hmenu
);
590 if (flags
& MF_BYPOSITION
)
592 if (id
>= menu
->nItems
)
594 release_menu_ptr(menu
);
603 MENUITEM
*item
= menu
->items
;
604 for (i
= 0; i
< menu
->nItems
; i
++, item
++)
606 if (item
->fType
& MF_POPUP
)
608 POPUPMENU
*submenu
= find_menu_item(item
->hSubMenu
, id
, flags
, pos
);
612 release_menu_ptr(menu
);
615 else if (item
->wID
== id
)
617 /* fallback to this item if nothing else found */
621 else if (item
->wID
== id
)
629 if (fallback_pos
!= ~0u)
633 release_menu_ptr(menu
);
640 /***********************************************************************
643 * Find a Sub menu. Return the position of the submenu, and modifies
644 * *hmenu in case it is found in another sub-menu.
645 * If the submenu cannot be found, NO_SELECTED_ITEM is returned.
647 static UINT
MENU_FindSubMenu( HMENU
*hmenu
, HMENU hSubTarget
)
652 if (((*hmenu
)==(HMENU
)0xffff) ||
653 (!(menu
= MENU_GetMenu(*hmenu
))))
654 return NO_SELECTED_ITEM
;
656 for (i
= 0; i
< menu
->nItems
; i
++, item
++) {
657 if(!(item
->fType
& MF_POPUP
)) continue;
658 if (item
->hSubMenu
== hSubTarget
) {
662 HMENU hsubmenu
= item
->hSubMenu
;
663 UINT pos
= MENU_FindSubMenu( &hsubmenu
, hSubTarget
);
664 if (pos
!= NO_SELECTED_ITEM
) {
670 return NO_SELECTED_ITEM
;
673 /***********************************************************************
676 static void MENU_FreeItemData( MENUITEM
* item
)
679 HeapFree( GetProcessHeap(), 0, item
->text
);
682 /***********************************************************************
683 * MENU_AdjustMenuItemRect
685 * Adjust menu item rectangle according to scrolling state.
688 MENU_AdjustMenuItemRect(const POPUPMENU
*menu
, LPRECT rect
)
690 INT scroll_offset
= menu
->bScrolling
? menu
->nScrollPos
: 0;
692 OffsetRect( rect
, menu
->items_rect
.left
, menu
->items_rect
.top
- scroll_offset
);
697 ht_nowhere
, /* outside the menu */
698 ht_border
, /* anywhere that's not an item or a scroll arrow */
699 ht_item
, /* a menu item */
700 ht_scroll_up
, /* scroll up arrow */
701 ht_scroll_down
/* scroll down arrow */
704 /***********************************************************************
705 * MENU_FindItemByCoords
707 * Find the item at the specified coordinates (screen coords). Does
708 * not work for child windows and therefore should not be called for
709 * an arbitrary system menu.
711 * Returns a hittest code. *pos will contain the position of the
712 * item or NO_SELECTED_ITEM. If the hittest code is ht_scroll_up
713 * or ht_scroll_down then *pos will contain the position of the
714 * item that's just outside the items_rect - ie, the one that would
715 * be scrolled completely into view.
717 static enum hittest
MENU_FindItemByCoords( const POPUPMENU
*menu
, POINT pt
, UINT
*pos
)
722 enum hittest ht
= ht_border
;
724 *pos
= NO_SELECTED_ITEM
;
726 if (!GetWindowRect(menu
->hWnd
, &rect
) || !PtInRect(&rect
, pt
))
729 if (GetWindowLongW( menu
->hWnd
, GWL_EXSTYLE
) & WS_EX_LAYOUTRTL
) pt
.x
= rect
.right
- 1 - pt
.x
;
730 else pt
.x
-= rect
.left
;
733 if (!PtInRect(&menu
->items_rect
, pt
))
735 if (!menu
->bScrolling
|| pt
.x
< menu
->items_rect
.left
|| pt
.x
>= menu
->items_rect
.right
)
738 /* On a scroll arrow. Update pt so that it points to the item just outside items_rect */
739 if (pt
.y
< menu
->items_rect
.top
)
742 pt
.y
= menu
->items_rect
.top
- 1;
747 pt
.y
= menu
->items_rect
.bottom
;
752 for (i
= 0; i
< menu
->nItems
; i
++, item
++)
755 MENU_AdjustMenuItemRect(menu
, &rect
);
756 if (PtInRect(&rect
, pt
))
759 if (ht
!= ht_scroll_up
&& ht
!= ht_scroll_down
) ht
= ht_item
;
768 /***********************************************************************
771 * Find the menu item selected by a key press.
772 * Return item id, -1 if none, -2 if we should close the menu.
774 static UINT
MENU_FindItemByKey( HWND hwndOwner
, HMENU hmenu
,
775 WCHAR key
, BOOL forceMenuChar
)
777 TRACE("\tlooking for '%c' (0x%02x) in [%p]\n", (char)key
, key
, hmenu
);
779 if (!IsMenu( hmenu
)) hmenu
= GetSubMenu( get_win_sys_menu(hwndOwner
), 0);
783 POPUPMENU
*menu
= MENU_GetMenu( hmenu
);
784 MENUITEM
*item
= menu
->items
;
790 BOOL cjk
= GetSystemMetrics( SM_DBCSENABLED
);
792 for (i
= 0; i
< menu
->nItems
; i
++, item
++)
796 const WCHAR
*p
= item
->text
- 2;
799 const WCHAR
*q
= p
+ 2;
801 if (!p
&& cjk
) p
= wcschr (q
, '\036'); /* Japanese Win16 */
803 while (p
!= NULL
&& p
[1] == '&');
804 if (p
&& (towupper(p
[1]) == towupper(key
))) return i
;
808 menuchar
= SendMessageW( hwndOwner
, WM_MENUCHAR
,
809 MAKEWPARAM( key
, menu
->wFlags
), (LPARAM
)hmenu
);
810 if (HIWORD(menuchar
) == MNC_EXECUTE
) return LOWORD(menuchar
);
811 if (HIWORD(menuchar
) == MNC_CLOSE
) return (UINT
)(-2);
817 /***********************************************************************
818 * MENU_GetBitmapItemSize
820 * Get the size of a bitmap item.
822 static void MENU_GetBitmapItemSize( MENUITEM
*lpitem
, SIZE
*size
,
826 HBITMAP bmp
= lpitem
->hbmpItem
;
828 size
->cx
= size
->cy
= 0;
830 /* check if there is a magic menu item associated with this item */
831 switch( (INT_PTR
) bmp
)
833 case (INT_PTR
)HBMMENU_CALLBACK
:
835 MEASUREITEMSTRUCT measItem
;
836 measItem
.CtlType
= ODT_MENU
;
838 measItem
.itemID
= lpitem
->wID
;
839 measItem
.itemWidth
= lpitem
->rect
.right
- lpitem
->rect
.left
;
840 measItem
.itemHeight
= lpitem
->rect
.bottom
- lpitem
->rect
.top
;
841 measItem
.itemData
= lpitem
->dwItemData
;
842 SendMessageW( hwndOwner
, WM_MEASUREITEM
, 0, (LPARAM
)&measItem
);
843 size
->cx
= measItem
.itemWidth
;
844 size
->cy
= measItem
.itemHeight
;
848 case (INT_PTR
)HBMMENU_SYSTEM
:
849 if (lpitem
->dwItemData
)
851 bmp
= (HBITMAP
)lpitem
->dwItemData
;
855 case (INT_PTR
)HBMMENU_MBAR_RESTORE
:
856 case (INT_PTR
)HBMMENU_MBAR_MINIMIZE
:
857 case (INT_PTR
)HBMMENU_MBAR_MINIMIZE_D
:
858 case (INT_PTR
)HBMMENU_MBAR_CLOSE
:
859 case (INT_PTR
)HBMMENU_MBAR_CLOSE_D
:
860 size
->cx
= GetSystemMetrics( SM_CYMENU
) - 4;
863 case (INT_PTR
)HBMMENU_POPUP_CLOSE
:
864 case (INT_PTR
)HBMMENU_POPUP_RESTORE
:
865 case (INT_PTR
)HBMMENU_POPUP_MAXIMIZE
:
866 case (INT_PTR
)HBMMENU_POPUP_MINIMIZE
:
867 size
->cx
= GetSystemMetrics( SM_CXMENUSIZE
);
868 size
->cy
= GetSystemMetrics( SM_CYMENUSIZE
);
871 if (GetObjectW(bmp
, sizeof(bm
), &bm
))
873 size
->cx
= bm
.bmWidth
;
874 size
->cy
= bm
.bmHeight
;
878 /***********************************************************************
879 * MENU_DrawBitmapItem
881 * Draw a bitmap item.
883 static void MENU_DrawBitmapItem( HDC hdc
, MENUITEM
*lpitem
, const RECT
*rect
,
884 POPUPMENU
*menu
, HWND hwndOwner
, UINT odaction
)
890 int w
= rect
->right
- rect
->left
;
891 int h
= rect
->bottom
- rect
->top
;
894 HBITMAP hbmToDraw
= lpitem
->hbmpItem
;
897 /* Check if there is a magic menu item associated with this item */
898 if (IS_MAGIC_BITMAP(hbmToDraw
))
904 switch((INT_PTR
)hbmToDraw
)
906 case (INT_PTR
)HBMMENU_SYSTEM
:
907 if (lpitem
->dwItemData
)
909 bmp
= (HBITMAP
)lpitem
->dwItemData
;
910 if (!GetObjectW( bmp
, sizeof(bm
), &bm
)) return;
914 static HBITMAP hBmpSysMenu
;
916 if (!hBmpSysMenu
) hBmpSysMenu
= LoadBitmapW(0, MAKEINTRESOURCEW(OBM_CLOSE
));
918 if (!GetObjectW( bmp
, sizeof(bm
), &bm
)) return;
919 /* only use right half of the bitmap */
920 bmp_xoffset
= bm
.bmWidth
/ 2;
921 bm
.bmWidth
-= bmp_xoffset
;
924 case (INT_PTR
)HBMMENU_MBAR_RESTORE
:
925 flags
= DFCS_CAPTIONRESTORE
;
927 case (INT_PTR
)HBMMENU_MBAR_MINIMIZE
:
928 flags
= DFCS_CAPTIONMIN
;
930 case (INT_PTR
)HBMMENU_MBAR_MINIMIZE_D
:
931 flags
= DFCS_CAPTIONMIN
| DFCS_INACTIVE
;
933 case (INT_PTR
)HBMMENU_MBAR_CLOSE
:
934 flags
= DFCS_CAPTIONCLOSE
;
936 case (INT_PTR
)HBMMENU_MBAR_CLOSE_D
:
937 flags
= DFCS_CAPTIONCLOSE
| DFCS_INACTIVE
;
939 case (INT_PTR
)HBMMENU_CALLBACK
:
941 DRAWITEMSTRUCT drawItem
;
942 drawItem
.CtlType
= ODT_MENU
;
944 drawItem
.itemID
= lpitem
->wID
;
945 drawItem
.itemAction
= odaction
;
946 drawItem
.itemState
= (lpitem
->fState
& MF_CHECKED
)?ODS_CHECKED
:0;
947 drawItem
.itemState
|= (lpitem
->fState
& MF_DEFAULT
)?ODS_DEFAULT
:0;
948 drawItem
.itemState
|= (lpitem
->fState
& MF_DISABLED
)?ODS_DISABLED
:0;
949 drawItem
.itemState
|= (lpitem
->fState
& MF_GRAYED
)?ODS_GRAYED
|ODS_DISABLED
:0;
950 drawItem
.itemState
|= (lpitem
->fState
& MF_HILITE
)?ODS_SELECTED
:0;
951 drawItem
.hwndItem
= (HWND
)menu
->obj
.handle
;
953 drawItem
.itemData
= lpitem
->dwItemData
;
954 drawItem
.rcItem
= *rect
;
955 SendMessageW( hwndOwner
, WM_DRAWITEM
, 0, (LPARAM
)&drawItem
);
959 case (INT_PTR
)HBMMENU_POPUP_CLOSE
:
962 case (INT_PTR
)HBMMENU_POPUP_RESTORE
:
965 case (INT_PTR
)HBMMENU_POPUP_MAXIMIZE
:
968 case (INT_PTR
)HBMMENU_POPUP_MINIMIZE
:
972 FIXME("Magic %p not implemented\n", hbmToDraw
);
977 /* draw the magic bitmaps using marlett font characters */
978 /* FIXME: fontsize and the position (x,y) could probably be better */
979 HFONT hfont
, hfontsav
;
980 LOGFONTW logfont
= { 0, 0, 0, 0, FW_NORMAL
, 0, 0, 0, SYMBOL_CHARSET
, 0, 0, 0, 0, L
"Marlett" };
981 logfont
.lfHeight
= min( h
, w
) - 5 ;
982 TRACE(" height %d rect %s\n", logfont
.lfHeight
, wine_dbgstr_rect( rect
));
983 hfont
= CreateFontIndirectW( &logfont
);
984 hfontsav
= SelectObject(hdc
, hfont
);
985 TextOutW( hdc
, rect
->left
, rect
->top
+ 2, &bmchr
, 1);
986 SelectObject(hdc
, hfontsav
);
987 DeleteObject( hfont
);
992 InflateRect( &r
, -1, -1 );
993 if (lpitem
->fState
& MF_HILITE
) flags
|= DFCS_PUSHED
;
994 DrawFrameControl( hdc
, &r
, DFC_CAPTION
, flags
);
999 if (!bmp
|| !GetObjectW( bmp
, sizeof(bm
), &bm
)) return;
1002 hdcMem
= CreateCompatibleDC( hdc
);
1003 SelectObject( hdcMem
, bmp
);
1005 /* handle fontsize > bitmap_height */
1006 top
= (h
>bm
.bmHeight
) ? rect
->top
+(h
-bm
.bmHeight
)/2 : rect
->top
;
1008 rop
=((lpitem
->fState
& MF_HILITE
) && !IS_MAGIC_BITMAP(hbmToDraw
)) ? NOTSRCCOPY
: SRCCOPY
;
1009 if ((lpitem
->fState
& MF_HILITE
) && lpitem
->hbmpItem
)
1010 SetBkColor(hdc
, GetSysColor(COLOR_HIGHLIGHT
));
1011 BitBlt( hdc
, left
, top
, w
, h
, hdcMem
, bmp_xoffset
, 0, rop
);
1016 /***********************************************************************
1019 * Calculate the size of the menu item and store it in lpitem->rect.
1021 static void MENU_CalcItemSize( HDC hdc
, MENUITEM
*lpitem
, HWND hwndOwner
,
1022 INT orgX
, INT orgY
, BOOL menuBar
, POPUPMENU
* lppop
)
1025 UINT check_bitmap_width
= GetSystemMetrics( SM_CXMENUCHECK
);
1026 UINT arrow_bitmap_width
;
1030 TRACE("dc=%p owner=%p (%d,%d)\n", hdc
, hwndOwner
, orgX
, orgY
);
1031 debug_print_menuitem("MENU_CalcItemSize: menuitem:", lpitem
,
1032 (menuBar
? " (MenuBar)" : ""));
1034 GetObjectW( get_arrow_bitmap(), sizeof(bm
), &bm
);
1035 arrow_bitmap_width
= bm
.bmWidth
;
1037 /* not done in Menu_Init: GetDialogBaseUnits() breaks there */
1038 if( !menucharsize
.cx
) {
1039 menucharsize
.cx
= GdiGetCharDimensions( hdc
, NULL
, &menucharsize
.cy
);
1040 /* Win95/98/ME will use menucharsize.cy here. Testing is possible
1041 * but it is unlikely an application will depend on that */
1042 ODitemheight
= HIWORD( GetDialogBaseUnits());
1045 SetRect( &lpitem
->rect
, orgX
, orgY
, orgX
, orgY
);
1047 if (lpitem
->fType
& MF_OWNERDRAW
)
1049 MEASUREITEMSTRUCT mis
;
1050 mis
.CtlType
= ODT_MENU
;
1052 mis
.itemID
= lpitem
->wID
;
1053 mis
.itemData
= lpitem
->dwItemData
;
1054 mis
.itemHeight
= ODitemheight
;
1056 SendMessageW( hwndOwner
, WM_MEASUREITEM
, 0, (LPARAM
)&mis
);
1057 /* Tests reveal that Windows ( Win95 through WinXP) adds twice the average
1058 * width of a menufont character to the width of an owner-drawn menu.
1060 lpitem
->rect
.right
+= mis
.itemWidth
+ 2 * menucharsize
.cx
;
1062 /* under at least win95 you seem to be given a standard
1063 height for the menu and the height value is ignored */
1064 lpitem
->rect
.bottom
+= GetSystemMetrics(SM_CYMENUSIZE
);
1066 lpitem
->rect
.bottom
+= mis
.itemHeight
;
1068 TRACE("id=%04lx size=%dx%d\n",
1069 lpitem
->wID
, lpitem
->rect
.right
-lpitem
->rect
.left
,
1070 lpitem
->rect
.bottom
-lpitem
->rect
.top
);
1074 if (lpitem
->fType
& MF_SEPARATOR
)
1076 lpitem
->rect
.bottom
+= GetSystemMetrics( SM_CYMENUSIZE
)/2;
1078 lpitem
->rect
.right
+= arrow_bitmap_width
+ menucharsize
.cx
;
1086 if (lpitem
->hbmpItem
) {
1089 MENU_GetBitmapItemSize(lpitem
, &size
, hwndOwner
);
1090 /* Keep the size of the bitmap in callback mode to be able
1091 * to draw it correctly */
1092 lpitem
->bmpsize
= size
;
1093 lppop
->textOffset
= max( lppop
->textOffset
, size
.cx
);
1094 lpitem
->rect
.right
+= size
.cx
+ 2;
1095 itemheight
= size
.cy
+ 2;
1097 if( !(lppop
->dwStyle
& MNS_NOCHECK
))
1098 lpitem
->rect
.right
+= check_bitmap_width
;
1099 lpitem
->rect
.right
+= 4 + menucharsize
.cx
;
1100 lpitem
->xTab
= lpitem
->rect
.right
;
1101 lpitem
->rect
.right
+= arrow_bitmap_width
;
1102 } else if (lpitem
->hbmpItem
) { /* menuBar */
1105 MENU_GetBitmapItemSize( lpitem
, &size
, hwndOwner
);
1106 lpitem
->bmpsize
= size
;
1107 lpitem
->rect
.right
+= size
.cx
;
1108 if( lpitem
->text
) lpitem
->rect
.right
+= 2;
1109 itemheight
= size
.cy
;
1112 /* it must be a text item - unless it's the system menu */
1113 if (!(lpitem
->fType
& MF_SYSMENU
) && lpitem
->text
) {
1114 HFONT hfontOld
= NULL
;
1115 RECT rc
= lpitem
->rect
;
1116 LONG txtheight
, txtwidth
;
1118 if ( lpitem
->fState
& MFS_DEFAULT
) {
1119 hfontOld
= SelectObject( hdc
, get_menu_font(TRUE
) );
1122 txtheight
= DrawTextW( hdc
, lpitem
->text
, -1, &rc
,
1123 DT_SINGLELINE
|DT_CALCRECT
);
1124 lpitem
->rect
.right
+= rc
.right
- rc
.left
;
1125 itemheight
= max( max( itemheight
, txtheight
),
1126 GetSystemMetrics( SM_CYMENU
) - 1);
1127 lpitem
->rect
.right
+= 2 * menucharsize
.cx
;
1129 if ((p
= wcschr( lpitem
->text
, '\t' )) != NULL
) {
1132 int n
= (int)( p
- lpitem
->text
);
1133 /* Item contains a tab (only meaningful in popup menus) */
1134 /* get text size before the tab */
1135 txtheight
= DrawTextW( hdc
, lpitem
->text
, n
, &rc
,
1136 DT_SINGLELINE
|DT_CALCRECT
);
1137 txtwidth
= rc
.right
- rc
.left
;
1138 p
+= 1; /* advance past the Tab */
1139 /* get text size after the tab */
1140 tmpheight
= DrawTextW( hdc
, p
, -1, &tmprc
,
1141 DT_SINGLELINE
|DT_CALCRECT
);
1142 lpitem
->xTab
+= txtwidth
;
1143 txtheight
= max( txtheight
, tmpheight
);
1144 txtwidth
+= menucharsize
.cx
+ /* space for the tab */
1145 tmprc
.right
- tmprc
.left
; /* space for the short cut */
1147 txtheight
= DrawTextW( hdc
, lpitem
->text
, -1, &rc
,
1148 DT_SINGLELINE
|DT_CALCRECT
);
1149 txtwidth
= rc
.right
- rc
.left
;
1150 lpitem
->xTab
+= txtwidth
;
1152 lpitem
->rect
.right
+= 2 + txtwidth
;
1153 itemheight
= max( itemheight
,
1154 max( txtheight
+ 2, menucharsize
.cy
+ 4));
1156 if (hfontOld
) SelectObject (hdc
, hfontOld
);
1157 } else if( menuBar
) {
1158 itemheight
= max( itemheight
, GetSystemMetrics(SM_CYMENU
)-1);
1160 lpitem
->rect
.bottom
+= itemheight
;
1161 TRACE("%s\n", wine_dbgstr_rect( &lpitem
->rect
));
1164 /***********************************************************************
1165 * MENU_PopupMenuCalcSize
1167 * Calculate the size of a popup menu.
1169 static void MENU_PopupMenuCalcSize( LPPOPUPMENU lppop
, UINT max_height
)
1174 BOOL textandbmp
= FALSE
, multi_col
= FALSE
;
1175 int orgX
, orgY
, maxTab
, maxTabWidth
;
1177 lppop
->Width
= lppop
->Height
= 0;
1178 SetRectEmpty(&lppop
->items_rect
);
1180 if (lppop
->nItems
== 0) return;
1183 SelectObject( hdc
, get_menu_font(FALSE
));
1187 lppop
->textOffset
= 0;
1189 while (start
< lppop
->nItems
)
1191 lpitem
= &lppop
->items
[start
];
1192 orgX
= lppop
->items_rect
.right
;
1193 if( lpitem
->fType
& (MF_MENUBREAK
| MF_MENUBARBREAK
))
1194 orgX
+= MENU_COL_SPACE
;
1195 orgY
= lppop
->items_rect
.top
;
1197 maxTab
= maxTabWidth
= 0;
1198 /* Parse items until column break or end of menu */
1199 for (i
= start
; i
< lppop
->nItems
; i
++, lpitem
++)
1201 if (lpitem
->fType
& (MF_MENUBREAK
| MF_MENUBARBREAK
))
1204 if (i
!= start
) break;
1207 MENU_CalcItemSize( hdc
, lpitem
, lppop
->hwndOwner
, orgX
, orgY
, FALSE
, lppop
);
1208 lppop
->items_rect
.right
= max( lppop
->items_rect
.right
, lpitem
->rect
.right
);
1209 orgY
= lpitem
->rect
.bottom
;
1210 if (IS_STRING_ITEM(lpitem
->fType
) && lpitem
->xTab
)
1212 maxTab
= max( maxTab
, lpitem
->xTab
);
1213 maxTabWidth
= max(maxTabWidth
,lpitem
->rect
.right
-lpitem
->xTab
);
1215 if( lpitem
->text
&& lpitem
->hbmpItem
) textandbmp
= TRUE
;
1218 /* Finish the column (set all items to the largest width found) */
1219 lppop
->items_rect
.right
= max( lppop
->items_rect
.right
, maxTab
+ maxTabWidth
);
1220 for (lpitem
= &lppop
->items
[start
]; start
< i
; start
++, lpitem
++)
1222 lpitem
->rect
.right
= lppop
->items_rect
.right
;
1223 if (IS_STRING_ITEM(lpitem
->fType
) && lpitem
->xTab
)
1224 lpitem
->xTab
= maxTab
;
1227 lppop
->items_rect
.bottom
= max( lppop
->items_rect
.bottom
, orgY
);
1230 /* if none of the items have both text and bitmap then
1231 * the text and bitmaps are all aligned on the left. If there is at
1232 * least one item with both text and bitmap then bitmaps are
1233 * on the left and texts left aligned with the right hand side
1235 if( !textandbmp
) lppop
->textOffset
= 0;
1237 lppop
->nTotalHeight
= lppop
->items_rect
.bottom
;
1239 /* space for the border */
1240 OffsetRect(&lppop
->items_rect
, MENU_MARGIN
, MENU_MARGIN
);
1241 lppop
->Height
= lppop
->items_rect
.bottom
+ MENU_MARGIN
;
1242 lppop
->Width
= lppop
->items_rect
.right
+ MENU_MARGIN
;
1244 /* Adjust popup height if it exceeds maximum */
1245 if (lppop
->Height
>= max_height
)
1247 lppop
->Height
= max_height
;
1248 lppop
->bScrolling
= !multi_col
;
1249 /* When the scroll arrows are present, don't add the top/bottom margin as well */
1250 if (lppop
->bScrolling
)
1252 lppop
->items_rect
.top
= get_scroll_arrow_height(lppop
);
1253 lppop
->items_rect
.bottom
= lppop
->Height
- get_scroll_arrow_height(lppop
);
1258 lppop
->bScrolling
= FALSE
;
1261 ReleaseDC( 0, hdc
);
1265 /***********************************************************************
1266 * MENU_MenuBarCalcSize
1268 * FIXME: Word 6 implements its own MDI and its own 'close window' bitmap
1269 * height is off by 1 pixel which causes lengthy window relocations when
1270 * active document window is maximized/restored.
1272 * Calculate the size of the menu bar.
1274 static void MENU_MenuBarCalcSize( HDC hdc
, LPRECT lprect
,
1275 LPPOPUPMENU lppop
, HWND hwndOwner
)
1278 UINT start
, i
, helpPos
;
1281 if ((lprect
== NULL
) || (lppop
== NULL
)) return;
1282 if (lppop
->nItems
== 0) return;
1283 TRACE("lprect %p %s\n", lprect
, wine_dbgstr_rect( lprect
));
1284 /* Start with a 1 pixel top border.
1285 This corresponds to the difference between SM_CYMENU and SM_CYMENUSIZE. */
1286 SetRect(&lppop
->items_rect
, 0, 0, lprect
->right
- lprect
->left
, 1);
1289 lppop
->textOffset
= 0;
1290 while (start
< lppop
->nItems
)
1292 lpitem
= &lppop
->items
[start
];
1293 orgX
= lppop
->items_rect
.left
;
1294 orgY
= lppop
->items_rect
.bottom
;
1296 /* Parse items until line break or end of menu */
1297 for (i
= start
; i
< lppop
->nItems
; i
++, lpitem
++)
1299 if ((helpPos
== ~0U) && (lpitem
->fType
& MF_RIGHTJUSTIFY
)) helpPos
= i
;
1301 (lpitem
->fType
& (MF_MENUBREAK
| MF_MENUBARBREAK
))) break;
1303 TRACE("calling MENU_CalcItemSize org=(%d, %d)\n", orgX
, orgY
);
1304 debug_print_menuitem (" item: ", lpitem
, "");
1305 MENU_CalcItemSize( hdc
, lpitem
, hwndOwner
, orgX
, orgY
, TRUE
, lppop
);
1307 if (lpitem
->rect
.right
> lppop
->items_rect
.right
)
1309 if (i
!= start
) break;
1310 else lpitem
->rect
.right
= lppop
->items_rect
.right
;
1312 lppop
->items_rect
.bottom
= max( lppop
->items_rect
.bottom
, lpitem
->rect
.bottom
);
1313 orgX
= lpitem
->rect
.right
;
1316 /* Finish the line (set all items to the largest height found) */
1317 while (start
< i
) lppop
->items
[start
++].rect
.bottom
= lppop
->items_rect
.bottom
;
1320 OffsetRect(&lppop
->items_rect
, lprect
->left
, lprect
->top
);
1321 lppop
->Width
= lppop
->items_rect
.right
- lppop
->items_rect
.left
;
1322 lppop
->Height
= lppop
->items_rect
.bottom
- lppop
->items_rect
.top
;
1323 lprect
->bottom
= lppop
->items_rect
.bottom
;
1325 /* Flush right all items between the MF_RIGHTJUSTIFY and */
1326 /* the last item (if several lines, only move the last line) */
1327 if (helpPos
== ~0U) return;
1328 lpitem
= &lppop
->items
[lppop
->nItems
-1];
1329 orgY
= lpitem
->rect
.top
;
1330 orgX
= lprect
->right
- lprect
->left
;
1331 for (i
= lppop
->nItems
- 1; i
>= helpPos
; i
--, lpitem
--) {
1332 if (lpitem
->rect
.top
!= orgY
) break; /* Other line */
1333 if (lpitem
->rect
.right
>= orgX
) break; /* Too far right already */
1334 lpitem
->rect
.left
+= orgX
- lpitem
->rect
.right
;
1335 lpitem
->rect
.right
= orgX
;
1336 orgX
= lpitem
->rect
.left
;
1340 static void draw_scroll_arrow(HDC hdc
, int x
, int top
, int height
, BOOL up
, BOOL enabled
)
1342 RECT rect
, light_rect
;
1343 HBRUSH brush
= GetSysColorBrush( enabled
? COLOR_BTNTEXT
: COLOR_BTNSHADOW
);
1344 HBRUSH light
= GetSysColorBrush( COLOR_3DLIGHT
);
1351 SetRect( &rect
, x
+ 1, top
, x
+ 2, top
+ 1);
1352 FillRect( hdc
, &rect
, light
);
1357 SetRect( &rect
, x
, top
, x
+ 1, top
+ 1);
1360 FillRect( hdc
, &rect
, brush
);
1361 if (!enabled
&& !up
&& height
)
1363 SetRect( &light_rect
, rect
.right
, rect
.top
, rect
.right
+ 2, rect
.bottom
);
1364 FillRect( hdc
, &light_rect
, light
);
1366 InflateRect( &rect
, 1, 0 );
1367 OffsetRect( &rect
, 0, up
? 1 : -1 );
1373 FillRect( hdc
, &rect
, light
);
1377 /***********************************************************************
1378 * MENU_DrawScrollArrows
1380 * Draw scroll arrows.
1383 MENU_DrawScrollArrows(const POPUPMENU
*menu
, HDC hdc
)
1385 UINT full_height
= get_scroll_arrow_height( menu
);
1386 UINT arrow_height
= full_height
/ 3;
1387 BOOL at_end
= menu
->nScrollPos
+ menu
->items_rect
.bottom
- menu
->items_rect
.top
== menu
->nTotalHeight
;
1389 draw_scroll_arrow( hdc
, menu
->Width
/ 3, arrow_height
, arrow_height
,
1390 TRUE
, menu
->nScrollPos
!= 0);
1391 draw_scroll_arrow( hdc
, menu
->Width
/ 3, menu
->Height
- 2 * arrow_height
, arrow_height
,
1396 /***********************************************************************
1399 * Draws the popup-menu arrow.
1401 static void draw_popup_arrow( HDC hdc
, RECT rect
, UINT arrow_bitmap_width
,
1402 UINT arrow_bitmap_height
)
1404 HDC hdcMem
= CreateCompatibleDC( hdc
);
1405 HBITMAP hOrigBitmap
;
1407 hOrigBitmap
= SelectObject( hdcMem
, get_arrow_bitmap() );
1408 BitBlt( hdc
, rect
.right
- arrow_bitmap_width
- 1,
1409 (rect
.top
+ rect
.bottom
- arrow_bitmap_height
) / 2,
1410 arrow_bitmap_width
, arrow_bitmap_height
,
1411 hdcMem
, 0, 0, SRCCOPY
);
1412 SelectObject( hdcMem
, hOrigBitmap
);
1415 /***********************************************************************
1418 * Draw a single menu item.
1420 static void MENU_DrawMenuItem( HWND hwnd
, POPUPMENU
*menu
, HWND hwndOwner
, HDC hdc
,
1421 MENUITEM
*lpitem
, BOOL menuBar
, UINT odaction
)
1424 BOOL flat_menu
= FALSE
;
1426 UINT arrow_bitmap_width
= 0, arrow_bitmap_height
= 0;
1427 HRGN old_clip
= NULL
, clip
;
1429 debug_print_menuitem("MENU_DrawMenuItem: ", lpitem
, "");
1433 GetObjectW( get_arrow_bitmap(), sizeof(bmp
), &bmp
);
1434 arrow_bitmap_width
= bmp
.bmWidth
;
1435 arrow_bitmap_height
= bmp
.bmHeight
;
1438 if (lpitem
->fType
& MF_SYSMENU
)
1440 if( !IsIconic(hwnd
) )
1441 NC_DrawSysButton( hwnd
, hdc
, lpitem
->fState
& (MF_HILITE
| MF_MOUSESELECT
) );
1445 TRACE( "rect=%s\n", wine_dbgstr_rect( &lpitem
->rect
) );
1446 rect
= lpitem
->rect
;
1447 MENU_AdjustMenuItemRect( menu
, &rect
);
1448 if (!IntersectRect( &bmprc
, &rect
, &menu
->items_rect
)) /* bmprc is used as a dummy */
1451 SystemParametersInfoW (SPI_GETFLATMENU
, 0, &flat_menu
, 0);
1452 bkgnd
= (menuBar
&& flat_menu
) ? COLOR_MENUBAR
: COLOR_MENU
;
1456 if (lpitem
->fState
& MF_HILITE
)
1458 if(menuBar
&& !flat_menu
) {
1459 SetTextColor(hdc
, GetSysColor(COLOR_MENUTEXT
));
1460 SetBkColor(hdc
, GetSysColor(COLOR_MENU
));
1462 if(lpitem
->fState
& MF_GRAYED
)
1463 SetTextColor(hdc
, GetSysColor(COLOR_GRAYTEXT
));
1465 SetTextColor(hdc
, GetSysColor(COLOR_HIGHLIGHTTEXT
));
1466 SetBkColor(hdc
, GetSysColor(COLOR_HIGHLIGHT
));
1471 if (lpitem
->fState
& MF_GRAYED
)
1472 SetTextColor( hdc
, GetSysColor( COLOR_GRAYTEXT
) );
1474 SetTextColor( hdc
, GetSysColor( COLOR_MENUTEXT
) );
1475 SetBkColor( hdc
, GetSysColor( bkgnd
) );
1478 old_clip
= CreateRectRgn( 0, 0, 0, 0 );
1479 if (GetClipRgn( hdc
, old_clip
) <= 0)
1481 DeleteObject( old_clip
);
1484 clip
= CreateRectRgnIndirect( &menu
->items_rect
);
1485 ExtSelectClipRgn( hdc
, clip
, RGN_AND
);
1486 DeleteObject( clip
);
1488 if (lpitem
->fType
& MF_OWNERDRAW
)
1491 ** Experimentation under Windows reveals that an owner-drawn
1492 ** menu is given the rectangle which includes the space it requested
1493 ** in its response to WM_MEASUREITEM _plus_ width for a checkmark
1494 ** and a popup-menu arrow. This is the value of lpitem->rect.
1495 ** Windows will leave all drawing to the application except for
1496 ** the popup-menu arrow. Windows always draws that itself, after
1497 ** the menu owner has finished drawing.
1500 COLORREF old_bk
, old_text
;
1502 dis
.CtlType
= ODT_MENU
;
1504 dis
.itemID
= lpitem
->wID
;
1505 dis
.itemData
= lpitem
->dwItemData
;
1507 if (lpitem
->fState
& MF_CHECKED
) dis
.itemState
|= ODS_CHECKED
;
1508 if (lpitem
->fState
& MF_GRAYED
) dis
.itemState
|= ODS_GRAYED
|ODS_DISABLED
;
1509 if (lpitem
->fState
& MF_HILITE
) dis
.itemState
|= ODS_SELECTED
;
1510 dis
.itemAction
= odaction
; /* ODA_DRAWENTIRE | ODA_SELECT | ODA_FOCUS; */
1511 dis
.hwndItem
= (HWND
)menu
->obj
.handle
;
1514 TRACE("Ownerdraw: owner=%p itemID=%d, itemState=%d, itemAction=%d, "
1515 "hwndItem=%p, hdc=%p, rcItem=%s\n", hwndOwner
,
1516 dis
.itemID
, dis
.itemState
, dis
.itemAction
, dis
.hwndItem
,
1517 dis
.hDC
, wine_dbgstr_rect( &dis
.rcItem
));
1518 old_bk
= GetBkColor( hdc
);
1519 old_text
= GetTextColor( hdc
);
1520 SendMessageW( hwndOwner
, WM_DRAWITEM
, 0, (LPARAM
)&dis
);
1521 /* Draw the popup-menu arrow */
1522 SetBkColor( hdc
, old_bk
);
1523 SetTextColor( hdc
, old_text
);
1524 if (lpitem
->fType
& MF_POPUP
)
1525 draw_popup_arrow( hdc
, rect
, arrow_bitmap_width
,
1526 arrow_bitmap_height
);
1530 if (menuBar
&& (lpitem
->fType
& MF_SEPARATOR
)) goto done
;
1532 if (lpitem
->fState
& MF_HILITE
)
1536 InflateRect (&rect
, -1, -1);
1537 FillRect(hdc
, &rect
, GetSysColorBrush(COLOR_MENUHILIGHT
));
1538 InflateRect (&rect
, 1, 1);
1539 FrameRect(hdc
, &rect
, GetSysColorBrush(COLOR_HIGHLIGHT
));
1544 DrawEdge(hdc
, &rect
, BDR_SUNKENOUTER
, BF_RECT
);
1546 FillRect(hdc
, &rect
, GetSysColorBrush(COLOR_HIGHLIGHT
));
1550 FillRect( hdc
, &rect
, GetSysColorBrush(bkgnd
) );
1552 SetBkMode( hdc
, TRANSPARENT
);
1554 /* vertical separator */
1555 if (!menuBar
&& (lpitem
->fType
& MF_MENUBARBREAK
))
1560 rc
.left
-= MENU_COL_SPACE
/ 2 + 1;
1562 rc
.bottom
= menu
->Height
- 3;
1565 oldPen
= SelectObject( hdc
, SYSCOLOR_GetPen(COLOR_BTNSHADOW
) );
1566 MoveToEx( hdc
, rc
.left
, rc
.top
, NULL
);
1567 LineTo( hdc
, rc
.left
, rc
.bottom
);
1568 SelectObject( hdc
, oldPen
);
1571 DrawEdge (hdc
, &rc
, EDGE_ETCHED
, BF_LEFT
);
1574 /* horizontal separator */
1575 if (lpitem
->fType
& MF_SEPARATOR
)
1580 InflateRect( &rc
, -1, 0 );
1581 rc
.top
= ( rc
.top
+ rc
.bottom
) / 2;
1584 oldPen
= SelectObject( hdc
, SYSCOLOR_GetPen(COLOR_BTNSHADOW
) );
1585 MoveToEx( hdc
, rc
.left
, rc
.top
, NULL
);
1586 LineTo( hdc
, rc
.right
, rc
.top
);
1587 SelectObject( hdc
, oldPen
);
1590 DrawEdge (hdc
, &rc
, EDGE_ETCHED
, BF_TOP
);
1594 /* helper lines for debugging */
1595 /* FrameRect(hdc, &rect, GetStockObject(BLACK_BRUSH));
1596 SelectObject( hdc, SYSCOLOR_GetPen(COLOR_WINDOWFRAME) );
1597 MoveToEx( hdc, rect.left, (rect.top + rect.bottom)/2, NULL );
1598 LineTo( hdc, rect.right, (rect.top + rect.bottom)/2 );
1601 if (lpitem
->hbmpItem
) {
1602 /* calculate the bitmap rectangle in coordinates relative
1603 * to the item rectangle */
1605 if( lpitem
->hbmpItem
== HBMMENU_CALLBACK
)
1608 bmprc
.left
= lpitem
->text
? menucharsize
.cx
: 0;
1610 else if (menu
->dwStyle
& MNS_NOCHECK
)
1612 else if (menu
->dwStyle
& MNS_CHECKORBMP
)
1615 bmprc
.left
= 4 + GetSystemMetrics(SM_CXMENUCHECK
);
1616 bmprc
.right
= bmprc
.left
+ lpitem
->bmpsize
.cx
;
1617 if( menuBar
&& !(lpitem
->hbmpItem
== HBMMENU_CALLBACK
))
1620 bmprc
.top
= (rect
.bottom
- rect
.top
-
1621 lpitem
->bmpsize
.cy
) / 2;
1622 bmprc
.bottom
= bmprc
.top
+ lpitem
->bmpsize
.cy
;
1628 INT y
= rect
.top
+ rect
.bottom
;
1629 BOOL checked
= FALSE
;
1630 UINT check_bitmap_width
= GetSystemMetrics( SM_CXMENUCHECK
);
1631 UINT check_bitmap_height
= GetSystemMetrics( SM_CYMENUCHECK
);
1632 /* Draw the check mark
1635 * Custom checkmark bitmaps are monochrome but not always 1bpp.
1637 if (!(menu
->dwStyle
& MNS_NOCHECK
))
1639 bm
= (lpitem
->fState
& MF_CHECKED
) ? lpitem
->hCheckBit
:
1640 lpitem
->hUnCheckBit
;
1641 if (bm
) /* we have a custom bitmap */
1643 HDC hdcMem
= CreateCompatibleDC( hdc
);
1645 SelectObject( hdcMem
, bm
);
1646 BitBlt( hdc
, rect
.left
, (y
- check_bitmap_height
) / 2,
1647 check_bitmap_width
, check_bitmap_height
,
1648 hdcMem
, 0, 0, SRCCOPY
);
1652 else if (lpitem
->fState
& MF_CHECKED
) /* standard bitmaps */
1655 HBITMAP bm
= CreateBitmap( check_bitmap_width
,
1656 check_bitmap_height
, 1, 1, NULL
);
1657 HDC hdcMem
= CreateCompatibleDC( hdc
);
1659 SelectObject( hdcMem
, bm
);
1660 SetRect( &r
, 0, 0, check_bitmap_width
, check_bitmap_height
);
1661 DrawFrameControl( hdcMem
, &r
, DFC_MENU
,
1662 (lpitem
->fType
& MFT_RADIOCHECK
) ?
1663 DFCS_MENUBULLET
: DFCS_MENUCHECK
);
1664 BitBlt( hdc
, rect
.left
, (y
- r
.bottom
) / 2, r
.right
, r
.bottom
,
1665 hdcMem
, 0, 0, SRCCOPY
);
1671 if (lpitem
->hbmpItem
&& !(checked
&& (menu
->dwStyle
& MNS_CHECKORBMP
)))
1674 /* some applications make this assumption on the DC's origin */
1675 SetViewportOrgEx( hdc
, rect
.left
, rect
.top
, &origorg
);
1676 MENU_DrawBitmapItem( hdc
, lpitem
, &bmprc
, menu
, hwndOwner
, odaction
);
1677 SetViewportOrgEx( hdc
, origorg
.x
, origorg
.y
, NULL
);
1679 /* Draw the popup-menu arrow */
1680 if (lpitem
->fType
& MF_POPUP
)
1681 draw_popup_arrow( hdc
, rect
, arrow_bitmap_width
,
1682 arrow_bitmap_height
);
1684 if( !(menu
->dwStyle
& MNS_NOCHECK
))
1685 rect
.left
+= check_bitmap_width
;
1686 rect
.right
-= arrow_bitmap_width
;
1688 else if (lpitem
->hbmpItem
)
1689 { /* Draw the bitmap */
1692 SetViewportOrgEx( hdc
, rect
.left
, rect
.top
, &origorg
);
1693 MENU_DrawBitmapItem( hdc
, lpitem
, &bmprc
, menu
, hwndOwner
, odaction
);
1694 SetViewportOrgEx( hdc
, origorg
.x
, origorg
.y
, NULL
);
1696 /* process text if present */
1702 UINT uFormat
= (menuBar
) ?
1703 DT_CENTER
| DT_VCENTER
| DT_SINGLELINE
:
1704 DT_LEFT
| DT_VCENTER
| DT_SINGLELINE
;
1706 if( !(menu
->dwStyle
& MNS_CHECKORBMP
))
1707 rect
.left
+= menu
->textOffset
;
1709 if ( lpitem
->fState
& MFS_DEFAULT
)
1711 hfontOld
= SelectObject( hdc
, get_menu_font(TRUE
) );
1715 if( lpitem
->hbmpItem
)
1716 rect
.left
+= lpitem
->bmpsize
.cx
;
1717 if( !(lpitem
->hbmpItem
== HBMMENU_CALLBACK
))
1718 rect
.left
+= menucharsize
.cx
;
1719 rect
.right
-= menucharsize
.cx
;
1722 for (i
= 0; lpitem
->text
[i
]; i
++)
1723 if ((lpitem
->text
[i
] == '\t') || (lpitem
->text
[i
] == '\b'))
1726 if(lpitem
->fState
& MF_GRAYED
)
1728 if (!(lpitem
->fState
& MF_HILITE
) )
1730 ++rect
.left
; ++rect
.top
; ++rect
.right
; ++rect
.bottom
;
1731 SetTextColor(hdc
, RGB(0xff, 0xff, 0xff));
1732 DrawTextW( hdc
, lpitem
->text
, i
, &rect
, uFormat
);
1733 --rect
.left
; --rect
.top
; --rect
.right
; --rect
.bottom
;
1735 SetTextColor(hdc
, RGB(0x80, 0x80, 0x80));
1738 DrawTextW( hdc
, lpitem
->text
, i
, &rect
, uFormat
);
1740 /* paint the shortcut text */
1741 if (!menuBar
&& lpitem
->text
[i
]) /* There's a tab or flush-right char */
1743 if (lpitem
->text
[i
] == '\t')
1745 rect
.left
= lpitem
->xTab
;
1746 uFormat
= DT_LEFT
| DT_VCENTER
| DT_SINGLELINE
;
1750 rect
.right
= lpitem
->xTab
;
1751 uFormat
= DT_RIGHT
| DT_VCENTER
| DT_SINGLELINE
;
1754 if(lpitem
->fState
& MF_GRAYED
)
1756 if (!(lpitem
->fState
& MF_HILITE
) )
1758 ++rect
.left
; ++rect
.top
; ++rect
.right
; ++rect
.bottom
;
1759 SetTextColor(hdc
, RGB(0xff, 0xff, 0xff));
1760 DrawTextW( hdc
, lpitem
->text
+ i
+ 1, -1, &rect
, uFormat
);
1761 --rect
.left
; --rect
.top
; --rect
.right
; --rect
.bottom
;
1763 SetTextColor(hdc
, RGB(0x80, 0x80, 0x80));
1765 DrawTextW( hdc
, lpitem
->text
+ i
+ 1, -1, &rect
, uFormat
);
1769 SelectObject (hdc
, hfontOld
);
1773 ExtSelectClipRgn( hdc
, old_clip
, RGN_COPY
);
1774 if (old_clip
) DeleteObject( old_clip
);
1778 /***********************************************************************
1779 * MENU_DrawPopupMenu
1781 * Paint a popup menu.
1783 static void MENU_DrawPopupMenu( HWND hwnd
, HDC hdc
, HMENU hmenu
)
1785 HBRUSH hPrevBrush
, brush
= GetSysColorBrush( COLOR_MENU
);
1787 POPUPMENU
*menu
= MENU_GetMenu( hmenu
);
1789 TRACE("wnd=%p dc=%p menu=%p\n", hwnd
, hdc
, hmenu
);
1791 GetClientRect( hwnd
, &rect
);
1793 if (menu
&& menu
->hbrBack
) brush
= menu
->hbrBack
;
1794 if ((hPrevBrush
= SelectObject( hdc
, brush
))
1795 && SelectObject( hdc
, get_menu_font(FALSE
) ))
1799 Rectangle( hdc
, rect
.left
, rect
.top
, rect
.right
, rect
.bottom
);
1801 hPrevPen
= SelectObject( hdc
, GetStockObject( NULL_PEN
) );
1804 BOOL flat_menu
= FALSE
;
1806 SystemParametersInfoW (SPI_GETFLATMENU
, 0, &flat_menu
, 0);
1808 FrameRect(hdc
, &rect
, GetSysColorBrush(COLOR_BTNSHADOW
));
1810 DrawEdge (hdc
, &rect
, EDGE_RAISED
, BF_RECT
);
1814 TRACE("hmenu %p Style %08x\n", hmenu
, menu
->dwStyle
);
1815 /* draw menu items */
1822 for (u
= menu
->nItems
; u
> 0; u
--, item
++)
1823 MENU_DrawMenuItem( hwnd
, menu
, menu
->hwndOwner
, hdc
,
1824 item
, FALSE
, ODA_DRAWENTIRE
);
1826 /* draw scroll arrows */
1827 if (menu
->bScrolling
)
1828 MENU_DrawScrollArrows(menu
, hdc
);
1832 SelectObject( hdc
, hPrevBrush
);
1837 /***********************************************************************
1840 * Paint a menu bar. Returns the height of the menu bar.
1841 * called from [windows/nonclient.c]
1843 UINT
MENU_DrawMenuBar( HDC hDC
, LPRECT lprect
, HWND hwnd
)
1846 HMENU hMenu
= GetMenu(hwnd
);
1848 lppop
= MENU_GetMenu( hMenu
);
1849 if (lppop
== NULL
|| lprect
== NULL
)
1851 return GetSystemMetrics(SM_CYMENU
);
1854 return DrawMenuBarTemp(hwnd
, hDC
, lprect
, hMenu
, NULL
);
1858 /***********************************************************************
1861 * Popup menu initialization before WM_ENTERMENULOOP.
1863 static BOOL
MENU_InitPopup( HWND hwndOwner
, HMENU hmenu
, UINT flags
)
1868 TRACE("owner=%p hmenu=%p\n", hwndOwner
, hmenu
);
1870 if (!(menu
= MENU_GetMenu( hmenu
))) return FALSE
;
1872 /* store the owner for DrawItem */
1873 if (!IsWindow( hwndOwner
))
1875 SetLastError( ERROR_INVALID_WINDOW_HANDLE
);
1878 menu
->hwndOwner
= hwndOwner
;
1880 if (flags
& TPM_LAYOUTRTL
)
1881 ex_style
= WS_EX_LAYOUTRTL
;
1883 /* NOTE: In Windows, top menu popup is not owned. */
1884 menu
->hWnd
= CreateWindowExW( ex_style
, (LPCWSTR
)POPUPMENU_CLASS_ATOM
, NULL
,
1885 WS_POPUP
, 0, 0, 0, 0,
1886 hwndOwner
, 0, (HINSTANCE
)GetWindowLongPtrW(hwndOwner
, GWLP_HINSTANCE
),
1888 if( !menu
->hWnd
) return FALSE
;
1893 /***********************************************************************
1896 * Display a popup menu.
1898 static BOOL
MENU_ShowPopup( HWND hwndOwner
, HMENU hmenu
, UINT id
, UINT flags
,
1899 INT x
, INT y
, INT xanchor
, INT yanchor
)
1907 TRACE("owner=%p hmenu=%p id=0x%04x x=0x%04x y=0x%04x xa=0x%04x ya=0x%04x\n",
1908 hwndOwner
, hmenu
, id
, x
, y
, xanchor
, yanchor
);
1910 if (!(menu
= MENU_GetMenu( hmenu
))) return FALSE
;
1911 if (menu
->FocusedItem
!= NO_SELECTED_ITEM
)
1913 menu
->items
[menu
->FocusedItem
].fState
&= ~(MF_HILITE
|MF_MOUSESELECT
);
1914 menu
->FocusedItem
= NO_SELECTED_ITEM
;
1917 menu
->nScrollPos
= 0;
1919 /* FIXME: should use item rect */
1922 monitor
= MonitorFromPoint( pt
, MONITOR_DEFAULTTONEAREST
);
1923 info
.cbSize
= sizeof(info
);
1924 GetMonitorInfoW( monitor
, &info
);
1926 max_height
= info
.rcWork
.bottom
- info
.rcWork
.top
;
1928 max_height
= min( max_height
, menu
->cyMax
);
1930 MENU_PopupMenuCalcSize( menu
, max_height
);
1932 /* adjust popup menu pos so that it fits within the desktop */
1934 if (flags
& TPM_LAYOUTRTL
)
1935 flags
^= TPM_RIGHTALIGN
;
1937 if( flags
& TPM_RIGHTALIGN
) x
-= menu
->Width
;
1938 if( flags
& TPM_CENTERALIGN
) x
-= menu
->Width
/ 2;
1940 if( flags
& TPM_BOTTOMALIGN
) y
-= menu
->Height
;
1941 if( flags
& TPM_VCENTERALIGN
) y
-= menu
->Height
/ 2;
1943 if( x
+ menu
->Width
> info
.rcWork
.right
)
1945 if( xanchor
&& x
>= menu
->Width
- xanchor
)
1946 x
-= menu
->Width
- xanchor
;
1948 if( x
+ menu
->Width
> info
.rcWork
.right
)
1949 x
= info
.rcWork
.right
- menu
->Width
;
1951 if( x
< info
.rcWork
.left
) x
= info
.rcWork
.left
;
1953 if( y
+ menu
->Height
> info
.rcWork
.bottom
)
1955 if( yanchor
&& y
>= menu
->Height
+ yanchor
)
1956 y
-= menu
->Height
+ yanchor
;
1958 if( y
+ menu
->Height
> info
.rcWork
.bottom
)
1959 y
= info
.rcWork
.bottom
- menu
->Height
;
1961 if( y
< info
.rcWork
.top
) y
= info
.rcWork
.top
;
1964 top_popup
= menu
->hWnd
;
1965 top_popup_hmenu
= hmenu
;
1967 /* Display the window */
1969 SetWindowPos( menu
->hWnd
, HWND_TOPMOST
, x
, y
, menu
->Width
, menu
->Height
,
1970 SWP_SHOWWINDOW
| SWP_NOACTIVATE
);
1971 UpdateWindow( menu
->hWnd
);
1976 /***********************************************************************
1977 * MENU_EnsureMenuItemVisible
1980 MENU_EnsureMenuItemVisible(LPPOPUPMENU lppop
, UINT wIndex
, HDC hdc
)
1982 if (lppop
->bScrolling
)
1984 MENUITEM
*item
= &lppop
->items
[wIndex
];
1985 UINT nOldPos
= lppop
->nScrollPos
;
1986 const RECT
*rc
= &lppop
->items_rect
;
1987 UINT scroll_height
= rc
->bottom
- rc
->top
;
1989 if (item
->rect
.bottom
> lppop
->nScrollPos
+ scroll_height
)
1991 lppop
->nScrollPos
= item
->rect
.bottom
- scroll_height
;
1992 ScrollWindow(lppop
->hWnd
, 0, nOldPos
- lppop
->nScrollPos
, rc
, rc
);
1994 else if (item
->rect
.top
< lppop
->nScrollPos
)
1996 lppop
->nScrollPos
= item
->rect
.top
;
1997 ScrollWindow(lppop
->hWnd
, 0, nOldPos
- lppop
->nScrollPos
, rc
, rc
);
2000 /* Invalidate the scroll arrows if necessary */
2001 if (nOldPos
!= lppop
->nScrollPos
)
2003 RECT arrow_rect
= lppop
->items_rect
;
2004 if (nOldPos
== 0 || lppop
->nScrollPos
== 0)
2007 arrow_rect
.bottom
= lppop
->items_rect
.top
;
2008 InvalidateRect(lppop
->hWnd
, &arrow_rect
, FALSE
);
2010 if (nOldPos
+ scroll_height
== lppop
->nTotalHeight
||
2011 lppop
->nScrollPos
+ scroll_height
== lppop
->nTotalHeight
)
2013 arrow_rect
.top
= lppop
->items_rect
.bottom
;
2014 arrow_rect
.bottom
= lppop
->Height
;
2015 InvalidateRect(lppop
->hWnd
, &arrow_rect
, FALSE
);
2022 /***********************************************************************
2025 static void MENU_SelectItem( HWND hwndOwner
, HMENU hmenu
, UINT wIndex
,
2026 BOOL sendMenuSelect
, HMENU topmenu
)
2031 TRACE("owner=%p menu=%p index=0x%04x select=0x%04x\n", hwndOwner
, hmenu
, wIndex
, sendMenuSelect
);
2033 lppop
= MENU_GetMenu( hmenu
);
2034 if ((!lppop
) || (!lppop
->nItems
) || (!lppop
->hWnd
)) return;
2036 if (lppop
->FocusedItem
== wIndex
) return;
2037 if (lppop
->wFlags
& MF_POPUP
) hdc
= GetDC( lppop
->hWnd
);
2038 else hdc
= GetDCEx( lppop
->hWnd
, 0, DCX_CACHE
| DCX_WINDOW
);
2040 top_popup
= lppop
->hWnd
;
2041 top_popup_hmenu
= hmenu
;
2044 SelectObject( hdc
, get_menu_font(FALSE
));
2046 /* Clear previous highlighted item */
2047 if (lppop
->FocusedItem
!= NO_SELECTED_ITEM
)
2049 lppop
->items
[lppop
->FocusedItem
].fState
&= ~(MF_HILITE
|MF_MOUSESELECT
);
2050 MENU_DrawMenuItem( lppop
->hWnd
, lppop
, hwndOwner
, hdc
, &lppop
->items
[lppop
->FocusedItem
],
2051 !(lppop
->wFlags
& MF_POPUP
), ODA_SELECT
);
2054 /* Highlight new item (if any) */
2055 lppop
->FocusedItem
= wIndex
;
2056 if (lppop
->FocusedItem
!= NO_SELECTED_ITEM
)
2058 if(!(lppop
->items
[wIndex
].fType
& MF_SEPARATOR
)) {
2059 lppop
->items
[wIndex
].fState
|= MF_HILITE
;
2060 MENU_EnsureMenuItemVisible(lppop
, wIndex
, hdc
);
2061 MENU_DrawMenuItem( lppop
->hWnd
, lppop
, hwndOwner
, hdc
, &lppop
->items
[wIndex
],
2062 !(lppop
->wFlags
& MF_POPUP
), ODA_SELECT
);
2066 MENUITEM
*ip
= &lppop
->items
[lppop
->FocusedItem
];
2067 SendMessageW( hwndOwner
, WM_MENUSELECT
,
2068 MAKEWPARAM(ip
->fType
& MF_POPUP
? wIndex
: ip
->wID
,
2069 ip
->fType
| ip
->fState
|
2070 (lppop
->wFlags
& MF_SYSMENU
)), (LPARAM
)hmenu
);
2073 else if (sendMenuSelect
) {
2076 if((pos
=MENU_FindSubMenu(&topmenu
, hmenu
))!=NO_SELECTED_ITEM
){
2077 POPUPMENU
*ptm
= MENU_GetMenu( topmenu
);
2078 MENUITEM
*ip
= &ptm
->items
[pos
];
2079 SendMessageW( hwndOwner
, WM_MENUSELECT
, MAKEWPARAM(pos
,
2080 ip
->fType
| ip
->fState
|
2081 (ptm
->wFlags
& MF_SYSMENU
)), (LPARAM
)topmenu
);
2085 ReleaseDC( lppop
->hWnd
, hdc
);
2089 /***********************************************************************
2090 * MENU_MoveSelection
2092 * Moves currently selected item according to the offset parameter.
2093 * If there is no selection then it should select the last item if
2094 * offset is ITEM_PREV or the first item if offset is ITEM_NEXT.
2096 static void MENU_MoveSelection( HWND hwndOwner
, HMENU hmenu
, INT offset
)
2101 TRACE("hwnd=%p hmenu=%p off=0x%04x\n", hwndOwner
, hmenu
, offset
);
2103 menu
= MENU_GetMenu( hmenu
);
2104 if ((!menu
) || (!menu
->items
)) return;
2106 if ( menu
->FocusedItem
!= NO_SELECTED_ITEM
)
2108 if( menu
->nItems
== 1 ) return; else
2109 for (i
= menu
->FocusedItem
+ offset
; i
>= 0 && i
< menu
->nItems
2111 if (!(menu
->items
[i
].fType
& MF_SEPARATOR
))
2113 MENU_SelectItem( hwndOwner
, hmenu
, i
, TRUE
, 0 );
2118 for ( i
= (offset
> 0) ? 0 : menu
->nItems
- 1;
2119 i
>= 0 && i
< menu
->nItems
; i
+= offset
)
2120 if (!(menu
->items
[i
].fType
& MF_SEPARATOR
))
2122 MENU_SelectItem( hwndOwner
, hmenu
, i
, TRUE
, 0 );
2128 /**********************************************************************
2131 * Insert (allocate) a new item into a menu.
2133 static POPUPMENU
*insert_menu_item(HMENU hMenu
, UINT id
, UINT flags
, UINT
*ret_pos
)
2139 /* Find where to insert new item */
2140 if (!(menu
= find_menu_item(hMenu
, id
, flags
, &pos
)))
2142 if (!(menu
= grab_menu_ptr(hMenu
)))
2147 /* Make sure that MDI system buttons stay on the right side.
2148 * Note: XP treats only bitmap handles 1 - 6 as "magic" ones
2149 * regardless of their id.
2151 while (pos
> 0 && (INT_PTR
)menu
->items
[pos
- 1].hbmpItem
>= (INT_PTR
)HBMMENU_SYSTEM
&&
2152 (INT_PTR
)menu
->items
[pos
- 1].hbmpItem
<= (INT_PTR
)HBMMENU_MBAR_CLOSE_D
)
2155 TRACE("inserting at %u flags %x\n", pos
, flags
);
2157 /* Create new items array */
2159 newItems
= HeapAlloc( GetProcessHeap(), 0, sizeof(MENUITEM
) * (menu
->nItems
+1) );
2162 release_menu_ptr(menu
);
2163 WARN("allocation failed\n" );
2166 if (menu
->nItems
> 0)
2168 /* Copy the old array into the new one */
2169 if (pos
> 0) memcpy( newItems
, menu
->items
, pos
* sizeof(MENUITEM
) );
2170 if (pos
< menu
->nItems
) memcpy( &newItems
[pos
+1], &menu
->items
[pos
],
2171 (menu
->nItems
-pos
)*sizeof(MENUITEM
) );
2172 HeapFree( GetProcessHeap(), 0, menu
->items
);
2174 menu
->items
= newItems
;
2176 memset( &newItems
[pos
], 0, sizeof(*newItems
) );
2177 menu
->Height
= 0; /* force size recalculate */
2184 /**********************************************************************
2185 * MENU_ParseResource
2187 * Parse a standard menu resource and add items to the menu.
2188 * Return a pointer to the end of the resource.
2190 * NOTE: flags is equivalent to the mtOption field
2192 static LPCSTR
MENU_ParseResource( LPCSTR res
, HMENU hMenu
)
2200 flags
= GET_WORD(res
);
2201 end_flag
= flags
& MF_END
;
2202 /* Remove MF_END because it has the same value as MF_HILITE */
2204 res
+= sizeof(WORD
);
2205 if (!(flags
& MF_POPUP
))
2208 res
+= sizeof(WORD
);
2211 res
+= (lstrlenW(str
) + 1) * sizeof(WCHAR
);
2212 if (flags
& MF_POPUP
)
2214 HMENU hSubMenu
= CreatePopupMenu();
2215 if (!hSubMenu
) return NULL
;
2216 if (!(res
= MENU_ParseResource( res
, hSubMenu
))) return NULL
;
2217 AppendMenuW( hMenu
, flags
, (UINT_PTR
)hSubMenu
, str
);
2219 else /* Not a popup */
2221 AppendMenuW( hMenu
, flags
, id
, *str
? str
: NULL
);
2223 } while (!end_flag
);
2228 /**********************************************************************
2229 * MENUEX_ParseResource
2231 * Parse an extended menu resource and add items to the menu.
2232 * Return a pointer to the end of the resource.
2234 static LPCSTR
MENUEX_ParseResource( LPCSTR res
, HMENU hMenu
)
2240 mii
.cbSize
= sizeof(mii
);
2241 mii
.fMask
= MIIM_STATE
| MIIM_ID
| MIIM_TYPE
;
2242 mii
.fType
= GET_DWORD(res
);
2243 res
+= sizeof(DWORD
);
2244 mii
.fState
= GET_DWORD(res
);
2245 res
+= sizeof(DWORD
);
2246 mii
.wID
= GET_DWORD(res
);
2247 res
+= sizeof(DWORD
);
2248 resinfo
= GET_WORD(res
); /* FIXME: for 16-bit apps this is a byte. */
2249 res
+= sizeof(WORD
);
2250 /* Align the text on a word boundary. */
2251 res
+= (~((UINT_PTR
)res
- 1)) & 1;
2252 mii
.dwTypeData
= (LPWSTR
) res
;
2253 res
+= (1 + lstrlenW(mii
.dwTypeData
)) * sizeof(WCHAR
);
2254 /* Align the following fields on a dword boundary. */
2255 res
+= (~((UINT_PTR
)res
- 1)) & 3;
2257 TRACE("Menu item: [%08x,%08x,%04x,%04x,%s]\n",
2258 mii
.fType
, mii
.fState
, mii
.wID
, resinfo
, debugstr_w(mii
.dwTypeData
));
2260 if (resinfo
& 1) { /* Pop-up? */
2261 /* DWORD helpid = GET_DWORD(res); FIXME: use this. */
2262 res
+= sizeof(DWORD
);
2263 mii
.hSubMenu
= CreatePopupMenu();
2266 if (!(res
= MENUEX_ParseResource(res
, mii
.hSubMenu
))) {
2267 DestroyMenu(mii
.hSubMenu
);
2270 mii
.fMask
|= MIIM_SUBMENU
;
2271 mii
.fType
|= MF_POPUP
;
2273 else if(!*mii
.dwTypeData
&& !(mii
.fType
& MF_SEPARATOR
))
2275 WARN("Converting NULL menu item %04x, type %04x to SEPARATOR\n",
2276 mii
.wID
, mii
.fType
);
2277 mii
.fType
|= MF_SEPARATOR
;
2279 InsertMenuItemW(hMenu
, -1, MF_BYPOSITION
, &mii
);
2280 } while (!(resinfo
& MF_END
));
2285 /***********************************************************************
2288 * Return the handle of the selected sub-popup menu (if any).
2290 static HMENU
MENU_GetSubPopup( HMENU hmenu
)
2295 menu
= MENU_GetMenu( hmenu
);
2297 if ((!menu
) || (menu
->FocusedItem
== NO_SELECTED_ITEM
)) return 0;
2299 item
= &menu
->items
[menu
->FocusedItem
];
2300 if ((item
->fType
& MF_POPUP
) && (item
->fState
& MF_MOUSESELECT
))
2301 return item
->hSubMenu
;
2306 /***********************************************************************
2307 * MENU_HideSubPopups
2309 * Hide the sub-popup menus of this menu.
2311 static void MENU_HideSubPopups( HWND hwndOwner
, HMENU hmenu
,
2312 BOOL sendMenuSelect
, UINT wFlags
)
2314 POPUPMENU
*menu
= MENU_GetMenu( hmenu
);
2316 TRACE("owner=%p hmenu=%p 0x%04x\n", hwndOwner
, hmenu
, sendMenuSelect
);
2318 if (menu
&& top_popup
)
2324 if (menu
->FocusedItem
!= NO_SELECTED_ITEM
)
2326 item
= &menu
->items
[menu
->FocusedItem
];
2327 if (!(item
->fType
& MF_POPUP
) ||
2328 !(item
->fState
& MF_MOUSESELECT
)) return;
2329 item
->fState
&= ~MF_MOUSESELECT
;
2330 hsubmenu
= item
->hSubMenu
;
2333 if (!(submenu
= MENU_GetMenu( hsubmenu
))) return;
2334 MENU_HideSubPopups( hwndOwner
, hsubmenu
, FALSE
, wFlags
);
2335 MENU_SelectItem( hwndOwner
, hsubmenu
, NO_SELECTED_ITEM
, sendMenuSelect
, 0 );
2336 DestroyWindow( submenu
->hWnd
);
2339 if (!(wFlags
& TPM_NONOTIFY
))
2340 SendMessageW( hwndOwner
, WM_UNINITMENUPOPUP
, (WPARAM
)hsubmenu
,
2341 MAKELPARAM(0, IS_SYSTEM_MENU(submenu
)) );
2346 /***********************************************************************
2349 * Display the sub-menu of the selected item of this menu.
2350 * Return the handle of the submenu, or hmenu if no submenu to display.
2352 static HMENU
MENU_ShowSubPopup( HWND hwndOwner
, HMENU hmenu
,
2353 BOOL selectFirst
, UINT wFlags
)
2360 TRACE("owner=%p hmenu=%p 0x%04x\n", hwndOwner
, hmenu
, selectFirst
);
2362 if (!(menu
= MENU_GetMenu( hmenu
))) return hmenu
;
2364 if (menu
->FocusedItem
== NO_SELECTED_ITEM
) return hmenu
;
2366 item
= &menu
->items
[menu
->FocusedItem
];
2367 if (!(item
->fType
& MF_POPUP
) || (item
->fState
& (MF_GRAYED
| MF_DISABLED
)))
2370 /* message must be sent before using item,
2371 because nearly everything may be changed by the application ! */
2373 /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
2374 if (!(wFlags
& TPM_NONOTIFY
))
2375 SendMessageW( hwndOwner
, WM_INITMENUPOPUP
, (WPARAM
)item
->hSubMenu
,
2376 MAKELPARAM( menu
->FocusedItem
, IS_SYSTEM_MENU(menu
) ));
2378 item
= &menu
->items
[menu
->FocusedItem
];
2381 /* correct item if modified as a reaction to WM_INITMENUPOPUP message */
2382 if (!(item
->fState
& MF_HILITE
))
2384 if (menu
->wFlags
& MF_POPUP
) hdc
= GetDC( menu
->hWnd
);
2385 else hdc
= GetDCEx( menu
->hWnd
, 0, DCX_CACHE
| DCX_WINDOW
);
2387 SelectObject( hdc
, get_menu_font(FALSE
));
2389 item
->fState
|= MF_HILITE
;
2390 MENU_DrawMenuItem( menu
->hWnd
, menu
, hwndOwner
, hdc
, item
, !(menu
->wFlags
& MF_POPUP
), ODA_DRAWENTIRE
);
2391 ReleaseDC( menu
->hWnd
, hdc
);
2393 if (!item
->rect
.top
&& !item
->rect
.left
&& !item
->rect
.bottom
&& !item
->rect
.right
)
2396 item
->fState
|= MF_MOUSESELECT
;
2398 if (IS_SYSTEM_MENU(menu
))
2400 MENU_InitSysMenuPopup(item
->hSubMenu
,
2401 GetWindowLongW( menu
->hWnd
, GWL_STYLE
),
2402 GetClassLongW( menu
->hWnd
, GCL_STYLE
));
2404 NC_GetSysPopupPos( menu
->hWnd
, &rect
);
2405 if (wFlags
& TPM_LAYOUTRTL
) rect
.left
= rect
.right
;
2406 rect
.top
= rect
.bottom
;
2407 rect
.right
= GetSystemMetrics(SM_CXSIZE
);
2408 rect
.bottom
= GetSystemMetrics(SM_CYSIZE
);
2412 RECT item_rect
= item
->rect
;
2414 MENU_AdjustMenuItemRect(menu
, &item_rect
);
2415 GetWindowRect( menu
->hWnd
, &rect
);
2417 if (menu
->wFlags
& MF_POPUP
)
2419 /* The first item in the popup menu has to be at the
2420 same y position as the focused menu item */
2421 if (wFlags
& TPM_LAYOUTRTL
)
2422 rect
.left
+= GetSystemMetrics(SM_CXBORDER
);
2424 rect
.left
+= item_rect
.right
- GetSystemMetrics(SM_CXBORDER
);
2425 rect
.top
+= item_rect
.top
- MENU_MARGIN
;
2426 rect
.right
= item_rect
.left
- item_rect
.right
+ GetSystemMetrics(SM_CXBORDER
);
2427 rect
.bottom
= item_rect
.top
- item_rect
.bottom
- 2 * MENU_MARGIN
;
2431 if (wFlags
& TPM_LAYOUTRTL
)
2432 rect
.left
= rect
.right
- item_rect
.left
;
2434 rect
.left
+= item_rect
.left
;
2435 rect
.top
+= item_rect
.bottom
;
2436 rect
.right
= item_rect
.right
- item_rect
.left
;
2437 rect
.bottom
= item_rect
.bottom
- item_rect
.top
;
2441 /* use default alignment for submenus */
2442 wFlags
&= ~(TPM_CENTERALIGN
| TPM_RIGHTALIGN
| TPM_VCENTERALIGN
| TPM_BOTTOMALIGN
);
2444 MENU_InitPopup( hwndOwner
, item
->hSubMenu
, wFlags
);
2446 MENU_ShowPopup( hwndOwner
, item
->hSubMenu
, menu
->FocusedItem
, wFlags
,
2447 rect
.left
, rect
.top
, rect
.right
, rect
.bottom
);
2449 MENU_MoveSelection( hwndOwner
, item
->hSubMenu
, ITEM_NEXT
);
2450 return item
->hSubMenu
;
2455 /**********************************************************************
2458 HWND
MENU_IsMenuActive(void)
2463 /**********************************************************************
2466 * Calls EndMenu() if the hwnd parameter belongs to the menu owner
2468 * Does the (menu stuff) of the default window handling of WM_CANCELMODE
2470 void MENU_EndMenu( HWND hwnd
)
2473 menu
= top_popup_hmenu
? MENU_GetMenu( top_popup_hmenu
) : NULL
;
2474 if (menu
&& (hwnd
== menu
->hWnd
|| hwnd
== menu
->hwndOwner
)) EndMenu();
2477 /***********************************************************************
2480 * Walks menu chain trying to find a menu pt maps to.
2482 static HMENU
MENU_PtMenu( HMENU hMenu
, POINT pt
)
2484 POPUPMENU
*menu
= MENU_GetMenu( hMenu
);
2485 UINT item
= menu
->FocusedItem
;
2488 /* try subpopup first (if any) */
2489 ret
= (item
!= NO_SELECTED_ITEM
&&
2490 (menu
->items
[item
].fType
& MF_POPUP
) &&
2491 (menu
->items
[item
].fState
& MF_MOUSESELECT
))
2492 ? MENU_PtMenu(menu
->items
[item
].hSubMenu
, pt
) : 0;
2494 if (!ret
) /* check the current window (avoiding WM_HITTEST) */
2496 INT ht
= NC_HandleNCHitTest( menu
->hWnd
, pt
);
2497 if( menu
->wFlags
& MF_POPUP
)
2499 if (ht
!= HTNOWHERE
&& ht
!= HTERROR
) ret
= hMenu
;
2501 else if (ht
== HTSYSMENU
)
2502 ret
= get_win_sys_menu( menu
->hWnd
);
2503 else if (ht
== HTMENU
)
2504 ret
= GetMenu( menu
->hWnd
);
2509 /***********************************************************************
2510 * MENU_ExecFocusedItem
2512 * Execute a menu item (for instance when user pressed Enter).
2513 * Return the wID of the executed item. Otherwise, -1 indicating
2514 * that no menu item was executed, -2 if a popup is shown;
2515 * Have to receive the flags for the TrackPopupMenu options to avoid
2516 * sending unwanted message.
2519 static INT
MENU_ExecFocusedItem( MTRACKER
* pmt
, HMENU hMenu
, UINT wFlags
)
2522 POPUPMENU
*menu
= MENU_GetMenu( hMenu
);
2524 TRACE("%p hmenu=%p\n", pmt
, hMenu
);
2526 if (!menu
|| !menu
->nItems
||
2527 (menu
->FocusedItem
== NO_SELECTED_ITEM
)) return -1;
2529 item
= &menu
->items
[menu
->FocusedItem
];
2531 TRACE("hMenu %p wID %08lx hSubMenu %p fType %04x\n", hMenu
, item
->wID
, item
->hSubMenu
, item
->fType
);
2533 if (!(item
->fType
& MF_POPUP
))
2535 if (!(item
->fState
& (MF_GRAYED
| MF_DISABLED
)) && !(item
->fType
& MF_SEPARATOR
))
2537 /* If TPM_RETURNCMD is set you return the id, but
2538 do not send a message to the owner */
2539 if(!(wFlags
& TPM_RETURNCMD
))
2541 if( menu
->wFlags
& MF_SYSMENU
)
2542 PostMessageW( pmt
->hOwnerWnd
, WM_SYSCOMMAND
, item
->wID
,
2543 MAKELPARAM((INT16
)pmt
->pt
.x
, (INT16
)pmt
->pt
.y
) );
2546 POPUPMENU
*topmenu
= MENU_GetMenu( pmt
->hTopMenu
);
2547 DWORD dwStyle
= menu
->dwStyle
| (topmenu
? topmenu
->dwStyle
: 0);
2549 if (dwStyle
& MNS_NOTIFYBYPOS
)
2550 PostMessageW( pmt
->hOwnerWnd
, WM_MENUCOMMAND
, menu
->FocusedItem
,
2553 PostMessageW( pmt
->hOwnerWnd
, WM_COMMAND
, item
->wID
, 0 );
2561 pmt
->hCurrentMenu
= MENU_ShowSubPopup(pmt
->hOwnerWnd
, hMenu
, TRUE
, wFlags
);
2568 /***********************************************************************
2569 * MENU_SwitchTracking
2571 * Helper function for menu navigation routines.
2573 static void MENU_SwitchTracking( MTRACKER
* pmt
, HMENU hPtMenu
, UINT id
, UINT wFlags
)
2575 POPUPMENU
*ptmenu
= MENU_GetMenu( hPtMenu
);
2576 POPUPMENU
*topmenu
= MENU_GetMenu( pmt
->hTopMenu
);
2578 TRACE("%p hmenu=%p 0x%04x\n", pmt
, hPtMenu
, id
);
2580 if( pmt
->hTopMenu
!= hPtMenu
&&
2581 !((ptmenu
->wFlags
| topmenu
->wFlags
) & MF_POPUP
) )
2583 /* both are top level menus (system and menu-bar) */
2584 MENU_HideSubPopups( pmt
->hOwnerWnd
, pmt
->hTopMenu
, FALSE
, wFlags
);
2585 MENU_SelectItem( pmt
->hOwnerWnd
, pmt
->hTopMenu
, NO_SELECTED_ITEM
, FALSE
, 0 );
2586 pmt
->hTopMenu
= hPtMenu
;
2588 else MENU_HideSubPopups( pmt
->hOwnerWnd
, hPtMenu
, FALSE
, wFlags
);
2589 MENU_SelectItem( pmt
->hOwnerWnd
, hPtMenu
, id
, TRUE
, 0 );
2593 /***********************************************************************
2596 * Return TRUE if we can go on with menu tracking.
2598 static BOOL
MENU_ButtonDown( MTRACKER
* pmt
, UINT message
, HMENU hPtMenu
, UINT wFlags
)
2600 TRACE("%p hPtMenu=%p\n", pmt
, hPtMenu
);
2605 POPUPMENU
*ptmenu
= MENU_GetMenu( hPtMenu
);
2606 enum hittest ht
= ht_item
;
2608 if( IS_SYSTEM_MENU(ptmenu
) )
2610 if (message
== WM_LBUTTONDBLCLK
) return FALSE
;
2614 ht
= MENU_FindItemByCoords( ptmenu
, pmt
->pt
, &pos
);
2616 if (pos
!= NO_SELECTED_ITEM
)
2618 if (ptmenu
->FocusedItem
!= pos
)
2619 MENU_SwitchTracking( pmt
, hPtMenu
, pos
, wFlags
);
2621 /* If the popup menu is not already "popped" */
2622 if (!(ptmenu
->items
[pos
].fState
& MF_MOUSESELECT
))
2623 pmt
->hCurrentMenu
= MENU_ShowSubPopup( pmt
->hOwnerWnd
, hPtMenu
, FALSE
, wFlags
);
2626 /* A click on an item or anywhere on a popup keeps tracking going */
2627 if (ht
== ht_item
|| ((ptmenu
->wFlags
& MF_POPUP
) && ht
!= ht_nowhere
))
2633 /***********************************************************************
2636 * Return the value of MENU_ExecFocusedItem if
2637 * the selected item was not a popup. Else open the popup.
2638 * A -1 return value indicates that we go on with menu tracking.
2641 static INT
MENU_ButtonUp( MTRACKER
* pmt
, HMENU hPtMenu
, UINT wFlags
)
2643 TRACE("%p hmenu=%p\n", pmt
, hPtMenu
);
2648 POPUPMENU
*ptmenu
= MENU_GetMenu( hPtMenu
);
2650 if( IS_SYSTEM_MENU(ptmenu
) )
2652 else if (MENU_FindItemByCoords( ptmenu
, pmt
->pt
, &pos
) != ht_item
)
2653 pos
= NO_SELECTED_ITEM
;
2655 if (pos
!= NO_SELECTED_ITEM
&& (ptmenu
->FocusedItem
== pos
))
2657 debug_print_menuitem ("FocusedItem: ", &ptmenu
->items
[pos
], "");
2659 if (!(ptmenu
->items
[pos
].fType
& MF_POPUP
))
2661 INT executedMenuId
= MENU_ExecFocusedItem( pmt
, hPtMenu
, wFlags
);
2662 if (executedMenuId
== -1 || executedMenuId
== -2) return -1;
2663 return executedMenuId
;
2666 /* If we are dealing with the menu bar */
2667 /* and this is a click on an already "popped" item: */
2668 /* Stop the menu tracking and close the opened submenus */
2669 if(((pmt
->hTopMenu
== hPtMenu
) || IS_SYSTEM_MENU(ptmenu
)) && (pmt
->trackFlags
& TF_RCVD_BTN_UP
))
2672 if( GetMenu(ptmenu
->hWnd
) == hPtMenu
|| IS_SYSTEM_MENU(ptmenu
) )
2674 if (pos
== NO_SELECTED_ITEM
) return 0;
2675 pmt
->trackFlags
|= TF_RCVD_BTN_UP
;
2682 /***********************************************************************
2685 * Return TRUE if we can go on with menu tracking.
2687 static BOOL
MENU_MouseMove( MTRACKER
* pmt
, HMENU hPtMenu
, UINT wFlags
)
2689 UINT id
= NO_SELECTED_ITEM
;
2690 POPUPMENU
*ptmenu
= NULL
;
2694 ptmenu
= MENU_GetMenu( hPtMenu
);
2695 if( IS_SYSTEM_MENU(ptmenu
) )
2697 else if (MENU_FindItemByCoords( ptmenu
, pmt
->pt
, &id
) != ht_item
)
2698 id
= NO_SELECTED_ITEM
;
2701 if( id
== NO_SELECTED_ITEM
)
2703 MENU_SelectItem( pmt
->hOwnerWnd
, pmt
->hCurrentMenu
,
2704 NO_SELECTED_ITEM
, TRUE
, pmt
->hTopMenu
);
2707 else if( ptmenu
->FocusedItem
!= id
)
2709 MENU_SwitchTracking( pmt
, hPtMenu
, id
, wFlags
);
2710 pmt
->hCurrentMenu
= MENU_ShowSubPopup(pmt
->hOwnerWnd
, hPtMenu
, FALSE
, wFlags
);
2716 /***********************************************************************
2719 * NOTE: WM_NEXTMENU documented in Win32 is a bit different.
2721 static LRESULT
MENU_DoNextMenu( MTRACKER
* pmt
, UINT vk
, UINT wFlags
)
2723 POPUPMENU
*menu
= MENU_GetMenu( pmt
->hTopMenu
);
2726 /* When skipping left, we need to do something special after the
2728 if (vk
== VK_LEFT
&& menu
->FocusedItem
== 0)
2732 /* When skipping right, for the non-system menu, we need to
2733 handle the last non-special menu item (ie skip any window
2734 icons such as MDI maximize, restore or close) */
2735 else if ((vk
== VK_RIGHT
) && !IS_SYSTEM_MENU(menu
))
2737 UINT i
= menu
->FocusedItem
+ 1;
2738 while (i
< menu
->nItems
) {
2739 if ((menu
->items
[i
].wID
>= SC_SIZE
&&
2740 menu
->items
[i
].wID
<= SC_RESTORE
)) {
2744 if (i
== menu
->nItems
) {
2748 /* When skipping right, we need to cater for the system menu */
2749 else if ((vk
== VK_RIGHT
) && IS_SYSTEM_MENU(menu
))
2751 if (menu
->FocusedItem
== (menu
->nItems
- 1)) {
2758 MDINEXTMENU next_menu
;
2763 next_menu
.hmenuIn
= (IS_SYSTEM_MENU(menu
)) ? GetSubMenu(pmt
->hTopMenu
,0) : pmt
->hTopMenu
;
2764 next_menu
.hmenuNext
= 0;
2765 next_menu
.hwndNext
= 0;
2766 SendMessageW( pmt
->hOwnerWnd
, WM_NEXTMENU
, vk
, (LPARAM
)&next_menu
);
2768 TRACE("%p [%p] -> %p [%p]\n",
2769 pmt
->hCurrentMenu
, pmt
->hOwnerWnd
, next_menu
.hmenuNext
, next_menu
.hwndNext
);
2771 if (!next_menu
.hmenuNext
|| !next_menu
.hwndNext
)
2773 DWORD style
= GetWindowLongW( pmt
->hOwnerWnd
, GWL_STYLE
);
2774 hNewWnd
= pmt
->hOwnerWnd
;
2775 if( IS_SYSTEM_MENU(menu
) )
2777 /* switch to the menu bar */
2779 if(style
& WS_CHILD
|| !(hNewMenu
= GetMenu(hNewWnd
))) return FALSE
;
2783 menu
= MENU_GetMenu( hNewMenu
);
2784 id
= menu
->nItems
- 1;
2786 /* Skip backwards over any system predefined icons,
2787 eg. MDI close, restore etc icons */
2789 (menu
->items
[id
].wID
>= SC_SIZE
&&
2790 menu
->items
[id
].wID
<= SC_RESTORE
)) id
--;
2793 else if (style
& WS_SYSMENU
)
2795 /* switch to the system menu */
2796 hNewMenu
= get_win_sys_menu( hNewWnd
);
2800 else /* application returned a new menu to switch to */
2802 hNewMenu
= next_menu
.hmenuNext
;
2803 hNewWnd
= WIN_GetFullHandle( next_menu
.hwndNext
);
2805 if( IsMenu(hNewMenu
) && IsWindow(hNewWnd
) )
2807 DWORD style
= GetWindowLongW( hNewWnd
, GWL_STYLE
);
2809 if (style
& WS_SYSMENU
&&
2810 GetSubMenu(get_win_sys_menu(hNewWnd
), 0) == hNewMenu
)
2812 /* get the real system menu */
2813 hNewMenu
= get_win_sys_menu(hNewWnd
);
2815 else if (style
& WS_CHILD
|| GetMenu(hNewWnd
) != hNewMenu
)
2817 /* FIXME: Not sure what to do here;
2818 * perhaps try to track hNewMenu as a popup? */
2820 TRACE(" -- got confused.\n");
2827 if( hNewMenu
!= pmt
->hTopMenu
)
2829 MENU_SelectItem( pmt
->hOwnerWnd
, pmt
->hTopMenu
, NO_SELECTED_ITEM
,
2831 if( pmt
->hCurrentMenu
!= pmt
->hTopMenu
)
2832 MENU_HideSubPopups( pmt
->hOwnerWnd
, pmt
->hTopMenu
, FALSE
, wFlags
);
2835 if( hNewWnd
!= pmt
->hOwnerWnd
)
2837 pmt
->hOwnerWnd
= hNewWnd
;
2838 set_capture_window( pmt
->hOwnerWnd
, GUI_INMENUMODE
, NULL
);
2841 pmt
->hTopMenu
= pmt
->hCurrentMenu
= hNewMenu
; /* all subpopups are hidden */
2842 MENU_SelectItem( pmt
->hOwnerWnd
, pmt
->hTopMenu
, id
, TRUE
, 0 );
2849 /***********************************************************************
2852 * The idea is not to show the popup if the next input message is
2853 * going to hide it anyway.
2855 static BOOL
MENU_SuspendPopup( MTRACKER
* pmt
, UINT uMsg
)
2859 msg
.hwnd
= pmt
->hOwnerWnd
;
2861 PeekMessageW( &msg
, 0, uMsg
, uMsg
, PM_NOYIELD
| PM_REMOVE
);
2862 pmt
->trackFlags
|= TF_SKIPREMOVE
;
2867 PeekMessageW( &msg
, 0, 0, 0, PM_NOYIELD
| PM_NOREMOVE
);
2868 if( msg
.message
== WM_KEYUP
|| msg
.message
== WM_PAINT
)
2870 PeekMessageW( &msg
, 0, 0, 0, PM_NOYIELD
| PM_REMOVE
);
2871 PeekMessageW( &msg
, 0, 0, 0, PM_NOYIELD
| PM_NOREMOVE
);
2872 if( msg
.message
== WM_KEYDOWN
&&
2873 (msg
.wParam
== VK_LEFT
|| msg
.wParam
== VK_RIGHT
))
2875 pmt
->trackFlags
|= TF_SUSPENDPOPUP
;
2882 /* failures go through this */
2883 pmt
->trackFlags
&= ~TF_SUSPENDPOPUP
;
2887 /***********************************************************************
2890 * Handle a VK_ESCAPE key event in a menu.
2892 static BOOL
MENU_KeyEscape(MTRACKER
* pmt
, UINT wFlags
)
2894 BOOL bEndMenu
= TRUE
;
2896 if (pmt
->hCurrentMenu
!= pmt
->hTopMenu
)
2898 POPUPMENU
*menu
= MENU_GetMenu(pmt
->hCurrentMenu
);
2900 if (menu
->wFlags
& MF_POPUP
)
2902 HMENU hmenutmp
, hmenuprev
;
2904 hmenuprev
= hmenutmp
= pmt
->hTopMenu
;
2906 /* close topmost popup */
2907 while (hmenutmp
!= pmt
->hCurrentMenu
)
2909 hmenuprev
= hmenutmp
;
2910 hmenutmp
= MENU_GetSubPopup( hmenuprev
);
2913 MENU_HideSubPopups( pmt
->hOwnerWnd
, hmenuprev
, TRUE
, wFlags
);
2914 pmt
->hCurrentMenu
= hmenuprev
;
2922 /***********************************************************************
2925 * Handle a VK_LEFT key event in a menu.
2927 static void MENU_KeyLeft( MTRACKER
* pmt
, UINT wFlags
, UINT msg
)
2930 HMENU hmenutmp
, hmenuprev
;
2933 hmenuprev
= hmenutmp
= pmt
->hTopMenu
;
2934 menu
= MENU_GetMenu( hmenutmp
);
2936 /* Try to move 1 column left (if possible) */
2937 if( (prevcol
= MENU_GetStartOfPrevColumn( pmt
->hCurrentMenu
)) !=
2938 NO_SELECTED_ITEM
) {
2940 MENU_SelectItem( pmt
->hOwnerWnd
, pmt
->hCurrentMenu
,
2945 /* close topmost popup */
2946 while (hmenutmp
!= pmt
->hCurrentMenu
)
2948 hmenuprev
= hmenutmp
;
2949 hmenutmp
= MENU_GetSubPopup( hmenuprev
);
2952 MENU_HideSubPopups( pmt
->hOwnerWnd
, hmenuprev
, TRUE
, wFlags
);
2953 pmt
->hCurrentMenu
= hmenuprev
;
2955 if ( (hmenuprev
== pmt
->hTopMenu
) && !(menu
->wFlags
& MF_POPUP
) )
2957 /* move menu bar selection if no more popups are left */
2959 if( !MENU_DoNextMenu( pmt
, VK_LEFT
, wFlags
) )
2960 MENU_MoveSelection( pmt
->hOwnerWnd
, pmt
->hTopMenu
, ITEM_PREV
);
2962 if ( hmenuprev
!= hmenutmp
|| pmt
->trackFlags
& TF_SUSPENDPOPUP
)
2964 /* A sublevel menu was displayed - display the next one
2965 * unless there is another displacement coming up */
2967 if( !MENU_SuspendPopup( pmt
, msg
) )
2968 pmt
->hCurrentMenu
= MENU_ShowSubPopup(pmt
->hOwnerWnd
,
2969 pmt
->hTopMenu
, TRUE
, wFlags
);
2975 /***********************************************************************
2978 * Handle a VK_RIGHT key event in a menu.
2980 static void MENU_KeyRight( MTRACKER
* pmt
, UINT wFlags
, UINT msg
)
2983 POPUPMENU
*menu
= MENU_GetMenu( pmt
->hTopMenu
);
2986 TRACE("MENU_KeyRight called, cur %p (%s), top %p (%s).\n",
2988 debugstr_w((MENU_GetMenu(pmt
->hCurrentMenu
))->items
[0].text
),
2989 pmt
->hTopMenu
, debugstr_w(menu
->items
[0].text
) );
2991 if ( (menu
->wFlags
& MF_POPUP
) || (pmt
->hCurrentMenu
!= pmt
->hTopMenu
))
2993 /* If already displaying a popup, try to display sub-popup */
2995 hmenutmp
= pmt
->hCurrentMenu
;
2996 pmt
->hCurrentMenu
= MENU_ShowSubPopup(pmt
->hOwnerWnd
, hmenutmp
, TRUE
, wFlags
);
2998 /* if subpopup was displayed then we are done */
2999 if (hmenutmp
!= pmt
->hCurrentMenu
) return;
3002 /* Check to see if there's another column */
3003 if( (nextcol
= MENU_GetStartOfNextColumn( pmt
->hCurrentMenu
)) !=
3004 NO_SELECTED_ITEM
) {
3005 TRACE("Going to %d.\n", nextcol
);
3006 MENU_SelectItem( pmt
->hOwnerWnd
, pmt
->hCurrentMenu
,
3011 if (!(menu
->wFlags
& MF_POPUP
)) /* menu bar tracking */
3013 if( pmt
->hCurrentMenu
!= pmt
->hTopMenu
)
3015 MENU_HideSubPopups( pmt
->hOwnerWnd
, pmt
->hTopMenu
, FALSE
, wFlags
);
3016 hmenutmp
= pmt
->hCurrentMenu
= pmt
->hTopMenu
;
3017 } else hmenutmp
= 0;
3019 /* try to move to the next item */
3020 if( !MENU_DoNextMenu( pmt
, VK_RIGHT
, wFlags
) )
3021 MENU_MoveSelection( pmt
->hOwnerWnd
, pmt
->hTopMenu
, ITEM_NEXT
);
3023 if( hmenutmp
|| pmt
->trackFlags
& TF_SUSPENDPOPUP
)
3024 if( !MENU_SuspendPopup( pmt
, msg
) )
3025 pmt
->hCurrentMenu
= MENU_ShowSubPopup(pmt
->hOwnerWnd
,
3026 pmt
->hTopMenu
, TRUE
, wFlags
);
3030 static void CALLBACK
release_capture( BOOL __normal
)
3032 set_capture_window( 0, GUI_INMENUMODE
, NULL
);
3035 /***********************************************************************
3038 * Menu tracking code.
3040 static BOOL
MENU_TrackMenu( HMENU hmenu
, UINT wFlags
, INT x
, INT y
,
3041 HWND hwnd
, const RECT
*lprect
)
3046 INT executedMenuId
= -1;
3048 BOOL enterIdleSent
= FALSE
;
3052 mt
.hCurrentMenu
= hmenu
;
3053 mt
.hTopMenu
= hmenu
;
3054 mt
.hOwnerWnd
= WIN_GetFullHandle( hwnd
);
3058 TRACE("hmenu=%p flags=0x%08x (%d,%d) hwnd=%p %s\n",
3059 hmenu
, wFlags
, x
, y
, hwnd
, wine_dbgstr_rect( lprect
));
3061 if (!(menu
= MENU_GetMenu( hmenu
)))
3063 WARN("Invalid menu handle %p\n", hmenu
);
3064 SetLastError(ERROR_INVALID_MENU_HANDLE
);
3068 if (wFlags
& TPM_BUTTONDOWN
)
3070 /* Get the result in order to start the tracking or not */
3071 fRemove
= MENU_ButtonDown( &mt
, WM_LBUTTONDOWN
, hmenu
, wFlags
);
3072 fEndMenu
= !fRemove
;
3075 if (wFlags
& TF_ENDMENU
) fEndMenu
= TRUE
;
3077 /* owner may not be visible when tracking a popup, so use the menu itself */
3078 capture_win
= (wFlags
& TPM_POPUPMENU
) ? menu
->hWnd
: mt
.hOwnerWnd
;
3079 set_capture_window( capture_win
, GUI_INMENUMODE
, NULL
);
3081 if ((wFlags
& TPM_POPUPMENU
) && menu
->nItems
== 0)
3084 __TRY
while (!fEndMenu
)
3086 menu
= MENU_GetMenu( mt
.hCurrentMenu
);
3087 if (!menu
) /* sometimes happens if I do a window manager close */
3090 /* we have to keep the message in the queue until it's
3091 * clear that menu loop is not over yet. */
3095 if (PeekMessageW( &msg
, 0, 0, 0, PM_NOREMOVE
))
3097 if (!CallMsgFilterW( &msg
, MSGF_MENU
)) break;
3098 /* remove the message from the queue */
3099 PeekMessageW( &msg
, 0, msg
.message
, msg
.message
, PM_REMOVE
);
3105 HWND win
= menu
->wFlags
& MF_POPUP
? menu
->hWnd
: 0;
3106 enterIdleSent
= TRUE
;
3107 SendMessageW( mt
.hOwnerWnd
, WM_ENTERIDLE
, MSGF_MENU
, (LPARAM
)win
);
3113 /* check if EndMenu() tried to cancel us, by posting this message */
3114 if(msg
.message
== WM_CANCELMODE
)
3116 /* we are now out of the loop */
3119 /* remove the message from the queue */
3120 PeekMessageW( &msg
, 0, msg
.message
, msg
.message
, PM_REMOVE
);
3122 /* break out of internal loop, ala ESCAPE */
3128 if ( (msg
.hwnd
==menu
->hWnd
) || (msg
.message
!=WM_TIMER
) )
3129 enterIdleSent
=FALSE
;
3132 if ((msg
.message
>= WM_MOUSEFIRST
) && (msg
.message
<= WM_MOUSELAST
))
3135 * Use the mouse coordinates in lParam instead of those in the MSG
3136 * struct to properly handle synthetic messages. They are already
3137 * in screen coordinates.
3139 mt
.pt
.x
= (short)LOWORD(msg
.lParam
);
3140 mt
.pt
.y
= (short)HIWORD(msg
.lParam
);
3142 /* Find a menu for this mouse event */
3143 hmenu
= MENU_PtMenu( mt
.hTopMenu
, mt
.pt
);
3147 /* no WM_NC... messages in captured state */
3149 case WM_RBUTTONDBLCLK
:
3150 case WM_RBUTTONDOWN
:
3151 if (!(wFlags
& TPM_RIGHTBUTTON
)) break;
3153 case WM_LBUTTONDBLCLK
:
3154 case WM_LBUTTONDOWN
:
3155 /* If the message belongs to the menu, removes it from the queue */
3156 /* Else, end menu tracking */
3157 fRemove
= MENU_ButtonDown( &mt
, msg
.message
, hmenu
, wFlags
);
3158 fEndMenu
= !fRemove
;
3162 if (!(wFlags
& TPM_RIGHTBUTTON
)) break;
3165 /* Check if a menu was selected by the mouse */
3168 executedMenuId
= MENU_ButtonUp( &mt
, hmenu
, wFlags
);
3169 TRACE("executedMenuId %d\n", executedMenuId
);
3171 /* End the loop if executedMenuId is an item ID */
3172 /* or if the job was done (executedMenuId = 0). */
3173 fEndMenu
= fRemove
= (executedMenuId
!= -1);
3175 /* No menu was selected by the mouse */
3176 /* if the function was called by TrackPopupMenu, continue
3177 with the menu tracking. If not, stop it */
3179 fEndMenu
= !(wFlags
& TPM_POPUPMENU
);
3184 /* the selected menu item must be changed every time */
3185 /* the mouse moves. */
3188 fEndMenu
|= !MENU_MouseMove( &mt
, hmenu
, wFlags
);
3190 } /* switch(msg.message) - mouse */
3192 else if ((msg
.message
>= WM_KEYFIRST
) && (msg
.message
<= WM_KEYLAST
))
3194 fRemove
= TRUE
; /* Keyboard messages are always removed */
3208 MENU_SelectItem( mt
.hOwnerWnd
, mt
.hCurrentMenu
,
3209 NO_SELECTED_ITEM
, FALSE
, 0 );
3210 MENU_MoveSelection( mt
.hOwnerWnd
, mt
.hCurrentMenu
,
3211 (msg
.wParam
== VK_HOME
)? ITEM_NEXT
: ITEM_PREV
);
3215 case VK_DOWN
: /* If on menu bar, pull-down the menu */
3217 menu
= MENU_GetMenu( mt
.hCurrentMenu
);
3218 if (!(menu
->wFlags
& MF_POPUP
))
3219 mt
.hCurrentMenu
= MENU_ShowSubPopup(mt
.hOwnerWnd
, mt
.hTopMenu
, TRUE
, wFlags
);
3220 else /* otherwise try to move selection */
3221 MENU_MoveSelection( mt
.hOwnerWnd
, mt
.hCurrentMenu
,
3222 (msg
.wParam
== VK_UP
)? ITEM_PREV
: ITEM_NEXT
);
3226 MENU_KeyLeft( &mt
, wFlags
, msg
.message
);
3230 MENU_KeyRight( &mt
, wFlags
, msg
.message
);
3234 fEndMenu
= MENU_KeyEscape(&mt
, wFlags
);
3240 hi
.cbSize
= sizeof(HELPINFO
);
3241 hi
.iContextType
= HELPINFO_MENUITEM
;
3242 if (menu
->FocusedItem
== NO_SELECTED_ITEM
)
3245 hi
.iCtrlId
= menu
->items
[menu
->FocusedItem
].wID
;
3246 hi
.hItemHandle
= hmenu
;
3247 hi
.dwContextId
= menu
->dwContextHelpID
;
3248 hi
.MousePos
= msg
.pt
;
3249 SendMessageW(hwnd
, WM_HELP
, 0, (LPARAM
)&hi
);
3254 TranslateMessage( &msg
);
3257 break; /* WM_KEYDOWN */
3264 if (msg
.wParam
== '\r' || msg
.wParam
== ' ')
3266 executedMenuId
= MENU_ExecFocusedItem(&mt
,mt
.hCurrentMenu
, wFlags
);
3267 fEndMenu
= (executedMenuId
!= -2);
3272 /* Hack to avoid control chars. */
3273 /* We will find a better way real soon... */
3274 if (msg
.wParam
< 32) break;
3276 pos
= MENU_FindItemByKey( mt
.hOwnerWnd
, mt
.hCurrentMenu
,
3277 LOWORD(msg
.wParam
), FALSE
);
3278 if (pos
== (UINT
)-2) fEndMenu
= TRUE
;
3279 else if (pos
== (UINT
)-1) MessageBeep(0);
3282 MENU_SelectItem( mt
.hOwnerWnd
, mt
.hCurrentMenu
, pos
,
3284 executedMenuId
= MENU_ExecFocusedItem(&mt
,mt
.hCurrentMenu
, wFlags
);
3285 fEndMenu
= (executedMenuId
!= -2);
3289 } /* switch(msg.message) - kbd */
3293 PeekMessageW( &msg
, 0, msg
.message
, msg
.message
, PM_REMOVE
);
3294 DispatchMessageW( &msg
);
3298 if (!fEndMenu
) fRemove
= TRUE
;
3300 /* finally remove message from the queue */
3302 if (fRemove
&& !(mt
.trackFlags
& TF_SKIPREMOVE
) )
3303 PeekMessageW( &msg
, 0, msg
.message
, msg
.message
, PM_REMOVE
);
3304 else mt
.trackFlags
&= ~TF_SKIPREMOVE
;
3306 __FINALLY( release_capture
)
3308 /* If dropdown is still painted and the close box is clicked on
3309 then the menu will be destroyed as part of the DispatchMessage above.
3310 This will then invalidate the menu handle in mt.hTopMenu. We should
3311 check for this first. */
3312 if( IsMenu( mt
.hTopMenu
) )
3314 menu
= MENU_GetMenu( mt
.hTopMenu
);
3316 if( IsWindow( mt
.hOwnerWnd
) )
3318 MENU_HideSubPopups( mt
.hOwnerWnd
, mt
.hTopMenu
, FALSE
, wFlags
);
3320 if (menu
&& (menu
->wFlags
& MF_POPUP
))
3322 DestroyWindow( menu
->hWnd
);
3325 if (!(wFlags
& TPM_NONOTIFY
))
3326 SendMessageW( mt
.hOwnerWnd
, WM_UNINITMENUPOPUP
, (WPARAM
)mt
.hTopMenu
,
3327 MAKELPARAM(0, IS_SYSTEM_MENU(menu
)) );
3329 MENU_SelectItem( mt
.hOwnerWnd
, mt
.hTopMenu
, NO_SELECTED_ITEM
, FALSE
, 0 );
3330 SendMessageW( mt
.hOwnerWnd
, WM_MENUSELECT
, MAKEWPARAM(0,0xffff), 0 );
3334 SetLastError( ERROR_SUCCESS
);
3335 /* The return value is only used by TrackPopupMenu */
3336 if (!(wFlags
& TPM_RETURNCMD
)) return TRUE
;
3337 if (executedMenuId
== -1) executedMenuId
= 0;
3338 return executedMenuId
;
3341 /***********************************************************************
3344 static BOOL
MENU_InitTracking(HWND hWnd
, HMENU hMenu
, BOOL bPopup
, UINT wFlags
)
3348 TRACE("hwnd=%p hmenu=%p\n", hWnd
, hMenu
);
3352 if (!(menu
= MENU_GetMenu( hMenu
))) return FALSE
;
3354 /* This makes the menus of applications built with Delphi work.
3355 * It also enables menus to be displayed in more than one window,
3356 * but there are some bugs left that need to be fixed in this case.
3358 if (!bPopup
) menu
->hWnd
= hWnd
;
3361 top_popup
= menu
->hWnd
;
3362 top_popup_hmenu
= hMenu
;
3367 /* Send WM_ENTERMENULOOP and WM_INITMENU message only if TPM_NONOTIFY flag is not specified */
3368 if (!(wFlags
& TPM_NONOTIFY
))
3369 SendMessageW( hWnd
, WM_ENTERMENULOOP
, bPopup
, 0 );
3371 SendMessageW( hWnd
, WM_SETCURSOR
, (WPARAM
)hWnd
, HTCAPTION
);
3373 if (!(wFlags
& TPM_NONOTIFY
))
3375 SendMessageW( hWnd
, WM_INITMENU
, (WPARAM
)hMenu
, 0 );
3376 /* If an app changed/recreated menu bar entries in WM_INITMENU
3377 * menu sizes will be recalculated once the menu created/shown.
3384 /***********************************************************************
3387 static BOOL
MENU_ExitTracking(HWND hWnd
, BOOL bPopup
)
3389 TRACE("hwnd=%p\n", hWnd
);
3391 SendMessageW( hWnd
, WM_EXITMENULOOP
, bPopup
, 0 );
3394 top_popup_hmenu
= NULL
;
3398 /***********************************************************************
3399 * MENU_TrackMouseMenuBar
3401 * Menu-bar tracking upon a mouse event. Called from NC_HandleSysCommand().
3403 void MENU_TrackMouseMenuBar( HWND hWnd
, INT ht
, POINT pt
)
3405 HMENU hMenu
= (ht
== HTSYSMENU
) ? get_win_sys_menu( hWnd
) : GetMenu( hWnd
);
3406 UINT wFlags
= TPM_BUTTONDOWN
| TPM_LEFTALIGN
| TPM_LEFTBUTTON
;
3408 TRACE("wnd=%p ht=0x%04x %s\n", hWnd
, ht
, wine_dbgstr_point( &pt
));
3410 if (GetWindowLongW( hWnd
, GWL_EXSTYLE
) & WS_EX_LAYOUTRTL
) wFlags
|= TPM_LAYOUTRTL
;
3413 MENU_InitTracking( hWnd
, hMenu
, FALSE
, wFlags
);
3415 /* fetch the window menu again, it may have changed */
3416 hMenu
= (ht
== HTSYSMENU
) ? get_win_sys_menu( hWnd
) : GetMenu( hWnd
);
3417 MENU_TrackMenu( hMenu
, wFlags
, pt
.x
, pt
.y
, hWnd
, NULL
);
3418 MENU_ExitTracking(hWnd
, FALSE
);
3423 /***********************************************************************
3424 * MENU_TrackKbdMenuBar
3426 * Menu-bar tracking upon a keyboard event. Called from NC_HandleSysCommand().
3428 void MENU_TrackKbdMenuBar( HWND hwnd
, UINT wParam
, WCHAR wChar
)
3430 UINT uItem
= NO_SELECTED_ITEM
;
3432 UINT wFlags
= TPM_LEFTALIGN
| TPM_LEFTBUTTON
;
3434 TRACE("hwnd %p wParam 0x%04x wChar 0x%04x\n", hwnd
, wParam
, wChar
);
3436 /* find window that has a menu */
3438 while (is_win_menu_disallowed(hwnd
))
3439 if (!(hwnd
= GetAncestor( hwnd
, GA_PARENT
))) return;
3441 /* check if we have to track a system menu */
3443 hTrackMenu
= GetMenu( hwnd
);
3444 if (!hTrackMenu
|| IsIconic(hwnd
) || wChar
== ' ' )
3446 if (!(GetWindowLongW( hwnd
, GWL_STYLE
) & WS_SYSMENU
)) return;
3447 hTrackMenu
= get_win_sys_menu( hwnd
);
3449 wParam
|= HTSYSMENU
; /* prevent item lookup */
3451 if (GetWindowLongW( hwnd
, GWL_EXSTYLE
) & WS_EX_LAYOUTRTL
) wFlags
|= TPM_LAYOUTRTL
;
3453 if (!IsMenu( hTrackMenu
)) return;
3455 MENU_InitTracking( hwnd
, hTrackMenu
, FALSE
, wFlags
);
3457 /* fetch the window menu again, it may have changed */
3458 hTrackMenu
= (wParam
& HTSYSMENU
) ? get_win_sys_menu( hwnd
) : GetMenu( hwnd
);
3460 if( wChar
&& wChar
!= ' ' )
3462 uItem
= MENU_FindItemByKey( hwnd
, hTrackMenu
, wChar
, (wParam
& HTSYSMENU
) );
3463 if ( uItem
>= (UINT
)(-2) )
3465 if( uItem
== (UINT
)(-1) ) MessageBeep(0);
3466 /* schedule end of menu tracking */
3467 wFlags
|= TF_ENDMENU
;
3472 MENU_SelectItem( hwnd
, hTrackMenu
, uItem
, TRUE
, 0 );
3474 if (!(wParam
& HTSYSMENU
) || wChar
== ' ')
3476 if( uItem
== NO_SELECTED_ITEM
)
3477 MENU_MoveSelection( hwnd
, hTrackMenu
, ITEM_NEXT
);
3479 PostMessageW( hwnd
, WM_KEYDOWN
, VK_RETURN
, 0 );
3483 MENU_TrackMenu( hTrackMenu
, wFlags
, 0, 0, hwnd
, NULL
);
3484 MENU_ExitTracking( hwnd
, FALSE
);
3487 /**********************************************************************
3488 * TrackPopupMenuEx (USER32.@)
3490 BOOL WINAPI
TrackPopupMenuEx( HMENU hMenu
, UINT wFlags
, INT x
, INT y
,
3491 HWND hWnd
, LPTPMPARAMS lpTpm
)
3496 TRACE("hmenu %p flags %04x (%d,%d) hwnd %p lpTpm %p rect %s\n",
3497 hMenu
, wFlags
, x
, y
, hWnd
, lpTpm
,
3498 lpTpm
? wine_dbgstr_rect( &lpTpm
->rcExclude
) : "-" );
3500 /* Parameter check */
3501 /* FIXME: this check is performed several times, here and in the called
3502 functions. That could be optimized */
3503 if (!(menu
= MENU_GetMenu( hMenu
)))
3505 SetLastError( ERROR_INVALID_MENU_HANDLE
);
3509 if (IsWindow(menu
->hWnd
))
3511 SetLastError( ERROR_POPUP_ALREADY_ACTIVE
);
3515 if (MENU_InitPopup( hWnd
, hMenu
, wFlags
))
3517 MENU_InitTracking(hWnd
, hMenu
, TRUE
, wFlags
);
3519 /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
3520 if (!(wFlags
& TPM_NONOTIFY
))
3521 SendMessageW( hWnd
, WM_INITMENUPOPUP
, (WPARAM
)hMenu
, 0);
3523 if (menu
->wFlags
& MF_SYSMENU
)
3524 MENU_InitSysMenuPopup( hMenu
, GetWindowLongW( hWnd
, GWL_STYLE
),
3525 GetClassLongW( hWnd
, GCL_STYLE
));
3527 if (MENU_ShowPopup( hWnd
, hMenu
, 0, wFlags
, x
, y
, 0, 0 ))
3528 ret
= MENU_TrackMenu( hMenu
, wFlags
| TPM_POPUPMENU
, 0, 0, hWnd
,
3529 lpTpm
? &lpTpm
->rcExclude
: NULL
);
3530 MENU_ExitTracking(hWnd
, TRUE
);
3534 DestroyWindow( menu
->hWnd
);
3537 if (!(wFlags
& TPM_NONOTIFY
))
3538 SendMessageW( hWnd
, WM_UNINITMENUPOPUP
, (WPARAM
)hMenu
,
3539 MAKELPARAM(0, IS_SYSTEM_MENU(menu
)) );
3547 /**********************************************************************
3548 * TrackPopupMenu (USER32.@)
3550 * Like the win32 API, the function return the command ID only if the
3551 * flag TPM_RETURNCMD is on.
3554 BOOL WINAPI
TrackPopupMenu( HMENU hMenu
, UINT wFlags
, INT x
, INT y
,
3555 INT nReserved
, HWND hWnd
, const RECT
*lpRect
)
3557 return TrackPopupMenuEx( hMenu
, wFlags
, x
, y
, hWnd
, NULL
);
3560 /***********************************************************************
3563 * NOTE: Windows has totally different (and undocumented) popup wndproc.
3565 LRESULT WINAPI
PopupMenuWndProc( HWND hwnd
, UINT message
, WPARAM wParam
, LPARAM lParam
)
3567 TRACE("hwnd=%p msg=0x%04x wp=0x%04lx lp=0x%08lx\n", hwnd
, message
, wParam
, lParam
);
3573 CREATESTRUCTW
*cs
= (CREATESTRUCTW
*)lParam
;
3574 SetWindowLongPtrW( hwnd
, 0, (LONG_PTR
)cs
->lpCreateParams
);
3578 case WM_MOUSEACTIVATE
: /* We don't want to be activated */
3579 return MA_NOACTIVATE
;
3584 BeginPaint( hwnd
, &ps
);
3585 MENU_DrawPopupMenu( hwnd
, ps
.hdc
,
3586 (HMENU
)GetWindowLongPtrW( hwnd
, 0 ) );
3587 EndPaint( hwnd
, &ps
);
3591 case WM_PRINTCLIENT
:
3593 MENU_DrawPopupMenu( hwnd
, (HDC
)wParam
,
3594 (HMENU
)GetWindowLongPtrW( hwnd
, 0 ) );
3602 /* zero out global pointer in case resident popup window was destroyed. */
3603 if (hwnd
== top_popup
) {
3605 top_popup_hmenu
= NULL
;
3613 if (!GetWindowLongPtrW( hwnd
, 0 )) ERR("no menu to display\n");
3616 SetWindowLongPtrW( hwnd
, 0, 0 );
3620 return GetWindowLongPtrW( hwnd
, 0 );
3623 return DefWindowProcW( hwnd
, message
, wParam
, lParam
);
3629 /***********************************************************************
3630 * MENU_GetMenuBarHeight
3632 * Compute the size of the menu bar height. Used by NC_HandleNCCalcSize().
3634 UINT
MENU_GetMenuBarHeight( HWND hwnd
, UINT menubarWidth
,
3635 INT orgX
, INT orgY
)
3641 TRACE("HWND %p, width %d, at (%d, %d).\n", hwnd
, menubarWidth
, orgX
, orgY
);
3643 if (!(lppop
= MENU_GetMenu( GetMenu(hwnd
) ))) return 0;
3645 hdc
= GetDCEx( hwnd
, 0, DCX_CACHE
| DCX_WINDOW
);
3646 SelectObject( hdc
, get_menu_font(FALSE
));
3647 SetRect(&rectBar
, orgX
, orgY
, orgX
+menubarWidth
, orgY
+GetSystemMetrics(SM_CYMENU
));
3648 MENU_MenuBarCalcSize( hdc
, &rectBar
, lppop
, hwnd
);
3649 ReleaseDC( hwnd
, hdc
);
3650 return lppop
->Height
;
3654 /*******************************************************************
3655 * ChangeMenuA (USER32.@)
3657 BOOL WINAPI
ChangeMenuA( HMENU hMenu
, UINT pos
, LPCSTR data
,
3658 UINT id
, UINT flags
)
3660 TRACE("menu=%p pos=%d data=%p id=%08x flags=%08x\n", hMenu
, pos
, data
, id
, flags
);
3661 if (flags
& MF_APPEND
) return AppendMenuA( hMenu
, flags
& ~MF_APPEND
,
3663 if (flags
& MF_DELETE
) return DeleteMenu(hMenu
, pos
, flags
& ~MF_DELETE
);
3664 if (flags
& MF_CHANGE
) return ModifyMenuA(hMenu
, pos
, flags
& ~MF_CHANGE
,
3666 if (flags
& MF_REMOVE
) return RemoveMenu( hMenu
,
3667 flags
& MF_BYPOSITION
? pos
: id
,
3668 flags
& ~MF_REMOVE
);
3669 /* Default: MF_INSERT */
3670 return InsertMenuA( hMenu
, pos
, flags
, id
, data
);
3674 /*******************************************************************
3675 * ChangeMenuW (USER32.@)
3677 BOOL WINAPI
ChangeMenuW( HMENU hMenu
, UINT pos
, LPCWSTR data
,
3678 UINT id
, UINT flags
)
3680 TRACE("menu=%p pos=%d data=%p id=%08x flags=%08x\n", hMenu
, pos
, data
, id
, flags
);
3681 if (flags
& MF_APPEND
) return AppendMenuW( hMenu
, flags
& ~MF_APPEND
,
3683 if (flags
& MF_DELETE
) return DeleteMenu(hMenu
, pos
, flags
& ~MF_DELETE
);
3684 if (flags
& MF_CHANGE
) return ModifyMenuW(hMenu
, pos
, flags
& ~MF_CHANGE
,
3686 if (flags
& MF_REMOVE
) return RemoveMenu( hMenu
,
3687 flags
& MF_BYPOSITION
? pos
: id
,
3688 flags
& ~MF_REMOVE
);
3689 /* Default: MF_INSERT */
3690 return InsertMenuW( hMenu
, pos
, flags
, id
, data
);
3694 /*******************************************************************
3695 * CheckMenuItem (USER32.@)
3697 DWORD WINAPI
CheckMenuItem( HMENU hMenu
, UINT id
, UINT flags
)
3704 if (!(menu
= find_menu_item(hMenu
, id
, flags
, &pos
)))
3706 item
= &menu
->items
[pos
];
3708 ret
= item
->fState
& MF_CHECKED
;
3709 if (flags
& MF_CHECKED
) item
->fState
|= MF_CHECKED
;
3710 else item
->fState
&= ~MF_CHECKED
;
3711 release_menu_ptr(menu
);
3716 /**********************************************************************
3717 * EnableMenuItem (USER32.@)
3719 BOOL WINAPI
EnableMenuItem( HMENU hMenu
, UINT id
, UINT wFlags
)
3725 TRACE("(%p, %04x, %04x)\n", hMenu
, id
, wFlags
);
3727 /* Get the Popupmenu to access the owner menu */
3728 if (!(menu
= find_menu_item(hMenu
, id
, wFlags
, &pos
)))
3731 item
= &menu
->items
[pos
];
3732 oldflags
= item
->fState
& (MF_GRAYED
| MF_DISABLED
);
3733 item
->fState
^= (oldflags
^ wFlags
) & (MF_GRAYED
| MF_DISABLED
);
3735 /* If the close item in the system menu change update the close button */
3736 if ((item
->wID
== SC_CLOSE
) && (oldflags
!= wFlags
) && menu
->hSysMenuOwner
)
3739 POPUPMENU
* parentMenu
;
3742 /* Get the parent menu to access */
3743 parentMenu
= grab_menu_ptr(menu
->hSysMenuOwner
);
3744 release_menu_ptr(menu
);
3748 hwnd
= parentMenu
->hWnd
;
3749 release_menu_ptr(parentMenu
);
3751 /* Refresh the frame to reflect the change */
3752 WIN_GetRectangles(hwnd
, COORDS_CLIENT
, &rc
, NULL
);
3754 RedrawWindow(hwnd
, &rc
, 0, RDW_FRAME
| RDW_INVALIDATE
| RDW_NOCHILDREN
);
3757 release_menu_ptr(menu
);
3763 /*******************************************************************
3764 * GetMenuStringA (USER32.@)
3766 INT WINAPI
GetMenuStringA(
3767 HMENU hMenu
, /* [in] menuhandle */
3768 UINT wItemID
, /* [in] menu item (dep. on wFlags) */
3769 LPSTR str
, /* [out] outbuffer. If NULL, func returns entry length*/
3770 INT nMaxSiz
, /* [in] length of buffer. if 0, func returns entry len*/
3771 UINT wFlags
/* [in] MF_ flags */
3779 TRACE("menu=%p item=%04x ptr=%p len=%d flags=%04x\n", hMenu
, wItemID
, str
, nMaxSiz
, wFlags
);
3780 if (str
&& nMaxSiz
) str
[0] = '\0';
3782 if (!(menu
= find_menu_item(hMenu
, wItemID
, wFlags
, &pos
)))
3784 SetLastError( ERROR_MENU_ITEM_NOT_FOUND
);
3787 item
= &menu
->items
[pos
];
3791 else if (!str
|| !nMaxSiz
)
3792 ret
= WideCharToMultiByte( CP_ACP
, 0, item
->text
, -1, NULL
, 0, NULL
, NULL
);
3795 if (!WideCharToMultiByte( CP_ACP
, 0, item
->text
, -1, str
, nMaxSiz
, NULL
, NULL
))
3799 release_menu_ptr(menu
);
3801 TRACE("returning %s\n", debugstr_a(str
));
3806 /*******************************************************************
3807 * GetMenuStringW (USER32.@)
3809 INT WINAPI
GetMenuStringW( HMENU hMenu
, UINT wItemID
,
3810 LPWSTR str
, INT nMaxSiz
, UINT wFlags
)
3817 TRACE("menu=%p item=%04x ptr=%p len=%d flags=%04x\n", hMenu
, wItemID
, str
, nMaxSiz
, wFlags
);
3818 if (str
&& nMaxSiz
) str
[0] = '\0';
3820 if (!(menu
= find_menu_item(hMenu
, wItemID
, wFlags
, &pos
)))
3822 SetLastError( ERROR_MENU_ITEM_NOT_FOUND
);
3825 item
= &menu
->items
[pos
];
3827 if (!str
|| !nMaxSiz
)
3828 ret
= item
->text
? lstrlenW(item
->text
) : 0;
3829 else if (!item
->text
)
3836 lstrcpynW( str
, item
->text
, nMaxSiz
);
3837 ret
= lstrlenW(str
);
3839 release_menu_ptr(menu
);
3841 TRACE("returning %s\n", debugstr_w(str
));
3846 /**********************************************************************
3847 * HiliteMenuItem (USER32.@)
3849 BOOL WINAPI
HiliteMenuItem( HWND hWnd
, HMENU hMenu
, UINT wItemID
,
3857 TRACE("(%p, %p, %04x, %04x);\n", hWnd
, hMenu
, wItemID
, wHilite
);
3859 if (!(menu
= find_menu_item(hMenu
, wItemID
, wHilite
, &pos
))) return FALSE
;
3861 handle_menu
= menu
->obj
.handle
;
3862 focused_item
= menu
->FocusedItem
;
3863 release_menu_ptr(menu
);
3865 if (focused_item
!= pos
)
3867 MENU_HideSubPopups( hWnd
, handle_menu
, FALSE
, 0 );
3868 MENU_SelectItem( hWnd
, handle_menu
, pos
, TRUE
, 0 );
3875 /**********************************************************************
3876 * GetMenuState (USER32.@)
3878 UINT WINAPI
GetMenuState( HMENU hMenu
, UINT wItemID
, UINT wFlags
)
3884 TRACE("(menu=%p, id=%04x, flags=%04x);\n", hMenu
, wItemID
, wFlags
);
3886 if (!(menu
= find_menu_item(hMenu
, wItemID
, wFlags
, &pos
)))
3889 item
= &menu
->items
[pos
];
3890 debug_print_menuitem (" item: ", item
, "");
3891 if (item
->fType
& MF_POPUP
)
3893 POPUPMENU
*submenu
= grab_menu_ptr(item
->hSubMenu
);
3895 state
= (submenu
->nItems
<< 8) | ((item
->fState
| item
->fType
) & 0xff);
3898 release_menu_ptr(submenu
);
3902 /* We used to (from way back then) mask the result to 0xff. */
3903 /* I don't know why and it seems wrong as the documented */
3904 /* return flag MF_SEPARATOR is outside that mask. */
3905 state
= (item
->fType
| item
->fState
);
3907 release_menu_ptr(menu
);
3912 /**********************************************************************
3913 * GetMenuItemCount (USER32.@)
3915 INT WINAPI
GetMenuItemCount( HMENU hMenu
)
3917 POPUPMENU
*menu
= grab_menu_ptr(hMenu
);
3920 if (!menu
) return -1;
3921 count
= menu
->nItems
;
3922 release_menu_ptr(menu
);
3924 TRACE("(%p) returning %d\n", hMenu
, count
);
3929 /**********************************************************************
3930 * GetMenuItemID (USER32.@)
3932 UINT WINAPI
GetMenuItemID( HMENU hMenu
, INT nPos
)
3937 if (!(menu
= find_menu_item(hMenu
, nPos
, MF_BYPOSITION
, &pos
)))
3940 id
= menu
->items
[pos
].fType
& MF_POPUP
? -1 : menu
->items
[pos
].wID
;
3941 release_menu_ptr(menu
);
3946 /**********************************************************************
3949 * Uses flags, id and text ptr, passed by InsertMenu() and
3950 * ModifyMenu() to setup a MenuItemInfo structure.
3952 static void MENU_mnu2mnuii( UINT flags
, UINT_PTR id
, LPCWSTR str
,
3953 LPMENUITEMINFOW pmii
)
3955 ZeroMemory( pmii
, sizeof( MENUITEMINFOW
));
3956 pmii
->cbSize
= sizeof( MENUITEMINFOW
);
3957 pmii
->fMask
= MIIM_STATE
| MIIM_ID
| MIIM_FTYPE
;
3958 /* setting bitmap clears text and vice versa */
3959 if( IS_STRING_ITEM(flags
)) {
3960 pmii
->fMask
|= MIIM_STRING
| MIIM_BITMAP
;
3962 flags
|= MF_SEPARATOR
;
3963 /* Item beginning with a backspace is a help item */
3964 /* FIXME: wrong place, this is only true in win16 */
3965 else if( *str
== '\b') {
3969 pmii
->dwTypeData
= (LPWSTR
)str
;
3970 } else if( flags
& MFT_BITMAP
){
3971 pmii
->fMask
|= MIIM_BITMAP
| MIIM_STRING
;
3972 pmii
->hbmpItem
= (HBITMAP
)str
;
3974 if( flags
& MF_OWNERDRAW
){
3975 pmii
->fMask
|= MIIM_DATA
;
3976 pmii
->dwItemData
= (ULONG_PTR
) str
;
3978 if( flags
& MF_POPUP
&& MENU_GetMenu((HMENU
)id
)) {
3979 pmii
->fMask
|= MIIM_SUBMENU
;
3980 pmii
->hSubMenu
= (HMENU
)id
;
3982 if( flags
& MF_SEPARATOR
) flags
|= MF_GRAYED
| MF_DISABLED
;
3983 pmii
->fState
= flags
& MENUITEMINFO_STATE_MASK
& ~MFS_DEFAULT
;
3984 pmii
->fType
= flags
& MENUITEMINFO_TYPE_MASK
;
3985 pmii
->wID
= (UINT
)id
;
3989 /*******************************************************************
3990 * InsertMenuW (USER32.@)
3992 BOOL WINAPI
InsertMenuW( HMENU hMenu
, UINT pos
, UINT flags
,
3993 UINT_PTR id
, LPCWSTR str
)
4001 if (IS_STRING_ITEM(flags
) && str
)
4002 TRACE("hMenu %p, pos %d, flags %08x, id %04lx, str %s\n",
4003 hMenu
, pos
, flags
, id
, debugstr_w(str
) );
4004 else TRACE("hMenu %p, pos %d, flags %08x, id %04lx, str %p (not a string)\n",
4005 hMenu
, pos
, flags
, id
, str
);
4007 if (!(menu
= insert_menu_item(hMenu
, pos
, flags
, &newpos
)))
4010 MENU_mnu2mnuii( flags
, id
, str
, &mii
);
4012 item
= &menu
->items
[newpos
];
4013 ret
= SetMenuItemInfo_common( item
, &mii
, TRUE
);
4015 item
->hCheckBit
= item
->hUnCheckBit
= 0;
4017 RemoveMenu( hMenu
, pos
, flags
);
4018 release_menu_ptr(menu
);
4024 /*******************************************************************
4025 * InsertMenuA (USER32.@)
4027 BOOL WINAPI
InsertMenuA( HMENU hMenu
, UINT pos
, UINT flags
,
4028 UINT_PTR id
, LPCSTR str
)
4032 if (IS_STRING_ITEM(flags
) && str
)
4034 INT len
= MultiByteToWideChar( CP_ACP
, 0, str
, -1, NULL
, 0 );
4035 LPWSTR newstr
= HeapAlloc( GetProcessHeap(), 0, len
* sizeof(WCHAR
) );
4038 MultiByteToWideChar( CP_ACP
, 0, str
, -1, newstr
, len
);
4039 ret
= InsertMenuW( hMenu
, pos
, flags
, id
, newstr
);
4040 HeapFree( GetProcessHeap(), 0, newstr
);
4044 else return InsertMenuW( hMenu
, pos
, flags
, id
, (LPCWSTR
)str
);
4048 /*******************************************************************
4049 * AppendMenuA (USER32.@)
4051 BOOL WINAPI
AppendMenuA( HMENU hMenu
, UINT flags
,
4052 UINT_PTR id
, LPCSTR data
)
4054 return InsertMenuA( hMenu
, -1, flags
| MF_BYPOSITION
, id
, data
);
4058 /*******************************************************************
4059 * AppendMenuW (USER32.@)
4061 BOOL WINAPI
AppendMenuW( HMENU hMenu
, UINT flags
,
4062 UINT_PTR id
, LPCWSTR data
)
4064 return InsertMenuW( hMenu
, -1, flags
| MF_BYPOSITION
, id
, data
);
4068 /**********************************************************************
4069 * RemoveMenu (USER32.@)
4071 BOOL WINAPI
RemoveMenu( HMENU hMenu
, UINT id
, UINT flags
)
4076 TRACE("(menu=%p id=%#x flags=%04x)\n", hMenu
, id
, flags
);
4078 if (!(menu
= find_menu_item(hMenu
, id
, flags
, &pos
)))
4082 MENU_FreeItemData( &menu
->items
[pos
] );
4084 if (--menu
->nItems
== 0)
4086 HeapFree( GetProcessHeap(), 0, menu
->items
);
4091 MENUITEM
*new_items
, *item
= &menu
->items
[pos
];
4093 while (pos
< menu
->nItems
)
4099 new_items
= HeapReAlloc( GetProcessHeap(), 0, menu
->items
, menu
->nItems
* sizeof(MENUITEM
) );
4100 if (new_items
) menu
->items
= new_items
;
4102 release_menu_ptr(menu
);
4108 /**********************************************************************
4109 * DeleteMenu (USER32.@)
4111 BOOL WINAPI
DeleteMenu( HMENU hMenu
, UINT id
, UINT flags
)
4116 if (!(menu
= find_menu_item(hMenu
, id
, flags
, &pos
)))
4119 if (menu
->items
[pos
].fType
& MF_POPUP
)
4120 DestroyMenu(menu
->items
[pos
].hSubMenu
);
4122 RemoveMenu(menu
->obj
.handle
, pos
, flags
| MF_BYPOSITION
);
4123 release_menu_ptr(menu
);
4128 /*******************************************************************
4129 * ModifyMenuW (USER32.@)
4131 BOOL WINAPI
ModifyMenuW( HMENU hMenu
, UINT pos
, UINT flags
,
4132 UINT_PTR id
, LPCWSTR str
)
4139 if (IS_STRING_ITEM(flags
))
4140 TRACE("%p %d %04x %04lx %s\n", hMenu
, pos
, flags
, id
, debugstr_w(str
) );
4142 TRACE("%p %d %04x %04lx %p\n", hMenu
, pos
, flags
, id
, str
);
4144 if (!(menu
= find_menu_item(hMenu
, pos
, flags
, &item_pos
)))
4146 /* workaround for Word 95: pretend that SC_TASKLIST item exists */
4147 if (pos
== SC_TASKLIST
&& !(flags
& MF_BYPOSITION
)) return TRUE
;
4150 menu
->Height
= 0; /* force size recalculate */
4151 MENU_mnu2mnuii( flags
, id
, str
, &mii
);
4152 ret
= SetMenuItemInfo_common(&menu
->items
[item_pos
], &mii
, TRUE
);
4153 release_menu_ptr(menu
);
4158 /*******************************************************************
4159 * ModifyMenuA (USER32.@)
4161 BOOL WINAPI
ModifyMenuA( HMENU hMenu
, UINT pos
, UINT flags
,
4162 UINT_PTR id
, LPCSTR str
)
4166 if (IS_STRING_ITEM(flags
) && str
)
4168 INT len
= MultiByteToWideChar( CP_ACP
, 0, str
, -1, NULL
, 0 );
4169 LPWSTR newstr
= HeapAlloc( GetProcessHeap(), 0, len
* sizeof(WCHAR
) );
4172 MultiByteToWideChar( CP_ACP
, 0, str
, -1, newstr
, len
);
4173 ret
= ModifyMenuW( hMenu
, pos
, flags
, id
, newstr
);
4174 HeapFree( GetProcessHeap(), 0, newstr
);
4178 else return ModifyMenuW( hMenu
, pos
, flags
, id
, (LPCWSTR
)str
);
4182 /**********************************************************************
4183 * CreatePopupMenu (USER32.@)
4185 HMENU WINAPI
CreatePopupMenu(void)
4190 if (!(hmenu
= CreateMenu())) return 0;
4191 menu
= MENU_GetMenu( hmenu
);
4192 menu
->wFlags
|= MF_POPUP
;
4197 /**********************************************************************
4198 * GetMenuCheckMarkDimensions (USER.417)
4199 * GetMenuCheckMarkDimensions (USER32.@)
4201 DWORD WINAPI
GetMenuCheckMarkDimensions(void)
4203 return MAKELONG( GetSystemMetrics(SM_CXMENUCHECK
), GetSystemMetrics(SM_CYMENUCHECK
) );
4207 /**********************************************************************
4208 * SetMenuItemBitmaps (USER32.@)
4210 BOOL WINAPI
SetMenuItemBitmaps( HMENU hMenu
, UINT nPos
, UINT wFlags
,
4211 HBITMAP hNewUnCheck
, HBITMAP hNewCheck
)
4217 if (!(menu
= find_menu_item(hMenu
, nPos
, wFlags
, &pos
)))
4220 item
= &menu
->items
[pos
];
4221 if (!hNewCheck
&& !hNewUnCheck
)
4223 item
->fState
&= ~MF_USECHECKBITMAPS
;
4225 else /* Install new bitmaps */
4227 item
->hCheckBit
= hNewCheck
;
4228 item
->hUnCheckBit
= hNewUnCheck
;
4229 item
->fState
|= MF_USECHECKBITMAPS
;
4231 release_menu_ptr(menu
);
4237 /**********************************************************************
4238 * CreateMenu (USER32.@)
4240 HMENU WINAPI
CreateMenu(void)
4245 if (!(menu
= HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(*menu
) ))) return 0;
4246 menu
->FocusedItem
= NO_SELECTED_ITEM
;
4249 if (!(hMenu
= alloc_user_handle( &menu
->obj
, USER_MENU
))) HeapFree( GetProcessHeap(), 0, menu
);
4251 TRACE("return %p\n", hMenu
);
4257 /**********************************************************************
4258 * DestroyMenu (USER32.@)
4260 BOOL WINAPI
DestroyMenu( HMENU hMenu
)
4264 TRACE("(%p)\n", hMenu
);
4266 if (!(lppop
= free_user_handle( hMenu
, USER_MENU
))) return FALSE
;
4267 if (lppop
== OBJ_OTHER_PROCESS
) return FALSE
;
4269 /* DestroyMenu should not destroy system menu popup owner */
4270 if ((lppop
->wFlags
& (MF_POPUP
| MF_SYSMENU
)) == MF_POPUP
&& lppop
->hWnd
)
4272 DestroyWindow( lppop
->hWnd
);
4276 if (lppop
->items
) /* recursively destroy submenus */
4279 MENUITEM
*item
= lppop
->items
;
4280 for (i
= lppop
->nItems
; i
> 0; i
--, item
++)
4282 if (item
->fType
& MF_POPUP
) DestroyMenu(item
->hSubMenu
);
4283 MENU_FreeItemData( item
);
4285 HeapFree( GetProcessHeap(), 0, lppop
->items
);
4287 HeapFree( GetProcessHeap(), 0, lppop
);
4292 /**********************************************************************
4293 * GetSystemMenu (USER32.@)
4295 HMENU WINAPI
GetSystemMenu( HWND hWnd
, BOOL bRevert
)
4297 WND
*wndPtr
= WIN_GetPtr( hWnd
);
4300 if (wndPtr
== WND_DESKTOP
) return 0;
4301 if (wndPtr
== WND_OTHER_PROCESS
)
4303 if (IsWindow( hWnd
)) FIXME( "not supported on other process window %p\n", hWnd
);
4307 if (wndPtr
->hSysMenu
&& bRevert
)
4309 DestroyMenu(wndPtr
->hSysMenu
);
4310 wndPtr
->hSysMenu
= 0;
4313 if(!wndPtr
->hSysMenu
&& (wndPtr
->dwStyle
& WS_SYSMENU
) )
4314 wndPtr
->hSysMenu
= MENU_GetSysMenu( hWnd
, 0 );
4316 if( wndPtr
->hSysMenu
)
4319 retvalue
= GetSubMenu(wndPtr
->hSysMenu
, 0);
4321 /* Store the dummy sysmenu handle to facilitate the refresh */
4322 /* of the close button if the SC_CLOSE item change */
4323 menu
= MENU_GetMenu(retvalue
);
4325 menu
->hSysMenuOwner
= wndPtr
->hSysMenu
;
4327 WIN_ReleasePtr( wndPtr
);
4329 return bRevert
? 0 : retvalue
;
4333 /*******************************************************************
4334 * SetSystemMenu (USER32.@)
4336 BOOL WINAPI
SetSystemMenu( HWND hwnd
, HMENU hMenu
)
4338 WND
*wndPtr
= WIN_GetPtr( hwnd
);
4340 if (wndPtr
&& wndPtr
!= WND_OTHER_PROCESS
&& wndPtr
!= WND_DESKTOP
)
4342 if (wndPtr
->hSysMenu
) DestroyMenu( wndPtr
->hSysMenu
);
4343 wndPtr
->hSysMenu
= MENU_GetSysMenu( hwnd
, hMenu
);
4344 WIN_ReleasePtr( wndPtr
);
4351 /**********************************************************************
4352 * GetMenu (USER32.@)
4354 HMENU WINAPI
GetMenu( HWND hWnd
)
4356 HMENU retvalue
= (HMENU
)GetWindowLongPtrW( hWnd
, GWLP_ID
);
4357 TRACE("for %p returning %p\n", hWnd
, retvalue
);
4361 /**********************************************************************
4362 * GetMenuBarInfo (USER32.@)
4364 BOOL WINAPI
GetMenuBarInfo( HWND hwnd
, LONG idObject
, LONG idItem
, PMENUBARINFO pmbi
)
4370 TRACE( "(%p,0x%08x,0x%08x,%p)\n", hwnd
, idObject
, idItem
, pmbi
);
4375 class_atom
= GetClassLongW(hwnd
, GCW_ATOM
);
4378 if (class_atom
!= POPUPMENU_CLASS_ATOM
)
4380 WARN("called on invalid window: %d\n", class_atom
);
4381 SetLastError(ERROR_INVALID_MENU_HANDLE
);
4385 hmenu
= (HMENU
)GetWindowLongPtrW(hwnd
, 0);
4388 hmenu
= GetMenu(hwnd
);
4391 hmenu
= GetSystemMenu(hwnd
, FALSE
);
4400 if (pmbi
->cbSize
!= sizeof(MENUBARINFO
))
4402 SetLastError(ERROR_INVALID_PARAMETER
);
4406 menu
= MENU_GetMenu(hmenu
);
4409 if (idItem
< 0 || idItem
> menu
->nItems
)
4414 SetRectEmpty(&pmbi
->rcBar
);
4416 else if (idItem
== 0)
4418 GetMenuItemRect(hwnd
, hmenu
, 0, &pmbi
->rcBar
);
4419 pmbi
->rcBar
.right
= pmbi
->rcBar
.left
+ menu
->Width
;
4420 pmbi
->rcBar
.bottom
= pmbi
->rcBar
.top
+ menu
->Height
;
4424 GetMenuItemRect(hwnd
, hmenu
, idItem
- 1, &pmbi
->rcBar
);
4427 pmbi
->hMenu
= hmenu
;
4428 pmbi
->hwndMenu
= NULL
;
4429 pmbi
->fBarFocused
= top_popup_hmenu
== hmenu
;
4432 pmbi
->fFocused
= menu
->FocusedItem
== idItem
- 1;
4433 if (pmbi
->fFocused
&& (menu
->items
[idItem
- 1].fType
& MF_POPUP
))
4435 menu
= MENU_GetMenu(menu
->items
[idItem
- 1].hSubMenu
);
4437 pmbi
->hwndMenu
= menu
->hWnd
;
4442 pmbi
->fFocused
= pmbi
->fBarFocused
;
4448 /**********************************************************************
4451 * Helper for SetMenu. Also called by WIN_CreateWindowEx to avoid the
4452 * SetWindowPos call that would result if SetMenu were called directly.
4454 BOOL
MENU_SetMenu( HWND hWnd
, HMENU hMenu
)
4456 TRACE("(%p, %p);\n", hWnd
, hMenu
);
4458 if (hMenu
&& !IsMenu(hMenu
))
4460 WARN("hMenu %p is not a menu handle\n", hMenu
);
4463 if (is_win_menu_disallowed(hWnd
))
4466 hWnd
= WIN_GetFullHandle( hWnd
);
4467 if (GetCapture() == hWnd
)
4468 set_capture_window( 0, GUI_INMENUMODE
, NULL
); /* release the capture */
4474 if (!(menu
= grab_menu_ptr(hMenu
))) return FALSE
;
4476 menu
->Height
= 0; /* Make sure we recalculate the size */
4477 release_menu_ptr(menu
);
4479 SetWindowLongPtrW( hWnd
, GWLP_ID
, (LONG_PTR
)hMenu
);
4484 /**********************************************************************
4485 * SetMenu (USER32.@)
4487 BOOL WINAPI
SetMenu( HWND hWnd
, HMENU hMenu
)
4489 if(!MENU_SetMenu(hWnd
, hMenu
))
4492 SetWindowPos( hWnd
, 0, 0, 0, 0, 0, SWP_NOSIZE
| SWP_NOMOVE
|
4493 SWP_NOACTIVATE
| SWP_NOZORDER
| SWP_FRAMECHANGED
);
4498 /**********************************************************************
4499 * GetSubMenu (USER32.@)
4501 HMENU WINAPI
GetSubMenu( HMENU hMenu
, INT nPos
)
4507 if (!(menu
= find_menu_item(hMenu
, nPos
, MF_BYPOSITION
, &pos
)))
4510 if (menu
->items
[pos
].fType
& MF_POPUP
)
4511 submenu
= menu
->items
[pos
].hSubMenu
;
4515 release_menu_ptr(menu
);
4520 /**********************************************************************
4521 * DrawMenuBar (USER32.@)
4523 BOOL WINAPI
DrawMenuBar( HWND hWnd
)
4527 if (!IsWindow( hWnd
))
4529 if (is_win_menu_disallowed(hWnd
))
4532 if ((hMenu
= GetMenu( hWnd
)))
4534 POPUPMENU
*menu
= grab_menu_ptr(hMenu
);
4537 menu
->Height
= 0; /* Make sure we call MENU_MenuBarCalcSize */
4538 menu
->hwndOwner
= hWnd
;
4539 release_menu_ptr(menu
);
4543 return SetWindowPos( hWnd
, 0, 0, 0, 0, 0, SWP_NOSIZE
| SWP_NOMOVE
|
4544 SWP_NOACTIVATE
| SWP_NOZORDER
| SWP_FRAMECHANGED
);
4547 /***********************************************************************
4548 * DrawMenuBarTemp (USER32.@)
4552 * called by W98SE desk.cpl Control Panel Applet
4554 * Not 100% sure about the param names, but close.
4556 DWORD WINAPI
DrawMenuBarTemp(HWND hwnd
, HDC hDC
, LPRECT lprect
, HMENU hMenu
, HFONT hFont
)
4561 BOOL flat_menu
= FALSE
;
4563 SystemParametersInfoW (SPI_GETFLATMENU
, 0, &flat_menu
, 0);
4566 hMenu
= GetMenu(hwnd
);
4569 hFont
= get_menu_font(FALSE
);
4571 lppop
= MENU_GetMenu( hMenu
);
4572 if (lppop
== NULL
|| lprect
== NULL
)
4574 retvalue
= GetSystemMetrics(SM_CYMENU
);
4578 TRACE("(%p, %p, %p, %p, %p)\n", hwnd
, hDC
, lprect
, hMenu
, hFont
);
4580 hfontOld
= SelectObject( hDC
, hFont
);
4582 if (lppop
->Height
== 0)
4583 MENU_MenuBarCalcSize(hDC
, lprect
, lppop
, hwnd
);
4585 lprect
->bottom
= lprect
->top
+ lppop
->Height
;
4587 FillRect(hDC
, lprect
, GetSysColorBrush(flat_menu
? COLOR_MENUBAR
: COLOR_MENU
) );
4589 SelectObject( hDC
, SYSCOLOR_GetPen(COLOR_3DFACE
));
4590 MoveToEx( hDC
, lprect
->left
, lprect
->bottom
, NULL
);
4591 LineTo( hDC
, lprect
->right
, lprect
->bottom
);
4593 if (lppop
->nItems
== 0)
4595 retvalue
= GetSystemMetrics(SM_CYMENU
);
4599 for (i
= 0; i
< lppop
->nItems
; i
++)
4600 MENU_DrawMenuItem( hwnd
, lppop
, hwnd
, hDC
, &lppop
->items
[i
], TRUE
, ODA_DRAWENTIRE
);
4602 retvalue
= lppop
->Height
;
4605 if (hfontOld
) SelectObject (hDC
, hfontOld
);
4609 /***********************************************************************
4610 * EndMenu (USER.187)
4611 * EndMenu (USER32.@)
4613 BOOL WINAPI
EndMenu(void)
4615 /* if we are in the menu code, and it is active */
4616 if (!fEndMenu
&& top_popup
)
4618 /* terminate the menu handling code */
4621 /* needs to be posted to wakeup the internal menu handler */
4622 /* which will now terminate the menu, in the event that */
4623 /* the main window was minimized, or lost focus, so we */
4624 /* don't end up with an orphaned menu */
4625 PostMessageW( top_popup
, WM_CANCELMODE
, 0, 0);
4631 /*****************************************************************
4632 * LoadMenuA (USER32.@)
4634 HMENU WINAPI
LoadMenuA( HINSTANCE instance
, LPCSTR name
)
4636 HRSRC hrsrc
= FindResourceA( instance
, name
, (LPSTR
)RT_MENU
);
4637 if (!hrsrc
) return 0;
4638 return LoadMenuIndirectA( LoadResource( instance
, hrsrc
));
4642 /*****************************************************************
4643 * LoadMenuW (USER32.@)
4645 HMENU WINAPI
LoadMenuW( HINSTANCE instance
, LPCWSTR name
)
4647 HRSRC hrsrc
= FindResourceW( instance
, name
, (LPWSTR
)RT_MENU
);
4648 if (!hrsrc
) return 0;
4649 return LoadMenuIndirectW( LoadResource( instance
, hrsrc
));
4653 /**********************************************************************
4654 * LoadMenuIndirectW (USER32.@)
4656 HMENU WINAPI
LoadMenuIndirectW( LPCVOID
template )
4659 WORD version
, offset
;
4660 LPCSTR p
= template;
4662 version
= GET_WORD(p
);
4664 TRACE("%p, ver %d\n", template, version
);
4667 case 0: /* standard format is version of 0 */
4668 offset
= GET_WORD(p
);
4669 p
+= sizeof(WORD
) + offset
;
4670 if (!(hMenu
= CreateMenu())) return 0;
4671 if (!MENU_ParseResource( p
, hMenu
))
4673 DestroyMenu( hMenu
);
4677 case 1: /* extended format is version of 1 */
4678 offset
= GET_WORD(p
);
4679 p
+= sizeof(WORD
) + offset
;
4680 if (!(hMenu
= CreateMenu())) return 0;
4681 if (!MENUEX_ParseResource( p
, hMenu
))
4683 DestroyMenu( hMenu
);
4688 ERR("version %d not supported.\n", version
);
4694 /**********************************************************************
4695 * LoadMenuIndirectA (USER32.@)
4697 HMENU WINAPI
LoadMenuIndirectA( LPCVOID
template )
4699 return LoadMenuIndirectW( template );
4703 /**********************************************************************
4706 BOOL WINAPI
IsMenu(HMENU hmenu
)
4711 menu
= grab_menu_ptr(hmenu
);
4712 is_menu
= menu
!= NULL
;
4713 release_menu_ptr(menu
);
4716 SetLastError(ERROR_INVALID_MENU_HANDLE
);
4721 /**********************************************************************
4722 * GetMenuItemInfo_common
4725 static BOOL
GetMenuItemInfo_common ( HMENU hmenu
, UINT id
, BOOL bypos
,
4726 LPMENUITEMINFOW lpmii
, BOOL unicode
)
4732 menu
= find_menu_item(hmenu
, id
, bypos
? MF_BYPOSITION
: 0, &pos
);
4734 item
= menu
? &menu
->items
[pos
] : NULL
;
4736 debug_print_menuitem("GetMenuItemInfo_common: ", item
, "");
4740 SetLastError( ERROR_MENU_ITEM_NOT_FOUND
);
4744 if( lpmii
->fMask
& MIIM_TYPE
) {
4745 if( lpmii
->fMask
& ( MIIM_STRING
| MIIM_FTYPE
| MIIM_BITMAP
)) {
4746 release_menu_ptr(menu
);
4747 WARN("invalid combination of fMask bits used\n");
4748 /* this does not happen on Win9x/ME */
4749 SetLastError( ERROR_INVALID_PARAMETER
);
4752 lpmii
->fType
= item
->fType
& MENUITEMINFO_TYPE_MASK
;
4753 if (item
->hbmpItem
&& !IS_MAGIC_BITMAP(item
->hbmpItem
))
4754 lpmii
->fType
|= MFT_BITMAP
;
4755 lpmii
->hbmpItem
= item
->hbmpItem
; /* not on Win9x/ME */
4756 if( lpmii
->fType
& MFT_BITMAP
) {
4757 lpmii
->dwTypeData
= (LPWSTR
) item
->hbmpItem
;
4759 } else if( lpmii
->fType
& (MFT_OWNERDRAW
| MFT_SEPARATOR
)) {
4760 /* this does not happen on Win9x/ME */
4761 lpmii
->dwTypeData
= 0;
4766 /* copy the text string */
4767 if ((lpmii
->fMask
& (MIIM_TYPE
|MIIM_STRING
))) {
4769 if(lpmii
->dwTypeData
&& lpmii
->cch
) {
4771 *((WCHAR
*)lpmii
->dwTypeData
) = 0;
4773 *((CHAR
*)lpmii
->dwTypeData
) = 0;
4780 len
= lstrlenW(item
->text
);
4781 if(lpmii
->dwTypeData
&& lpmii
->cch
)
4782 lstrcpynW(lpmii
->dwTypeData
, item
->text
, lpmii
->cch
);
4786 len
= WideCharToMultiByte( CP_ACP
, 0, item
->text
, -1, NULL
,
4787 0, NULL
, NULL
) - 1;
4788 if(lpmii
->dwTypeData
&& lpmii
->cch
)
4789 if (!WideCharToMultiByte( CP_ACP
, 0, item
->text
, -1,
4790 (LPSTR
)lpmii
->dwTypeData
, lpmii
->cch
, NULL
, NULL
))
4791 ((LPSTR
)lpmii
->dwTypeData
)[lpmii
->cch
- 1] = 0;
4793 /* if we've copied a substring we return its length */
4794 if(lpmii
->dwTypeData
&& lpmii
->cch
)
4795 if (lpmii
->cch
<= len
+ 1)
4800 /* return length of string */
4801 /* not on Win9x/ME if fType & MFT_BITMAP */
4807 if (lpmii
->fMask
& MIIM_FTYPE
)
4808 lpmii
->fType
= item
->fType
& MENUITEMINFO_TYPE_MASK
;
4810 if (lpmii
->fMask
& MIIM_BITMAP
)
4811 lpmii
->hbmpItem
= item
->hbmpItem
;
4813 if (lpmii
->fMask
& MIIM_STATE
)
4814 lpmii
->fState
= item
->fState
& MENUITEMINFO_STATE_MASK
;
4816 if (lpmii
->fMask
& MIIM_ID
)
4817 lpmii
->wID
= item
->wID
;
4819 if (lpmii
->fMask
& MIIM_SUBMENU
)
4820 lpmii
->hSubMenu
= item
->hSubMenu
;
4822 /* hSubMenu is always cleared
4823 * (not on Win9x/ME ) */
4824 lpmii
->hSubMenu
= 0;
4827 if (lpmii
->fMask
& MIIM_CHECKMARKS
) {
4828 lpmii
->hbmpChecked
= item
->hCheckBit
;
4829 lpmii
->hbmpUnchecked
= item
->hUnCheckBit
;
4831 if (lpmii
->fMask
& MIIM_DATA
)
4832 lpmii
->dwItemData
= item
->dwItemData
;
4834 release_menu_ptr(menu
);
4838 /**********************************************************************
4839 * GetMenuItemInfoA (USER32.@)
4841 BOOL WINAPI
GetMenuItemInfoA( HMENU hmenu
, UINT item
, BOOL bypos
,
4842 LPMENUITEMINFOA lpmii
)
4846 if( lpmii
->cbSize
!= sizeof( mii
) &&
4847 lpmii
->cbSize
!= sizeof( mii
) - sizeof ( mii
.hbmpItem
)) {
4848 SetLastError( ERROR_INVALID_PARAMETER
);
4851 memcpy( &mii
, lpmii
, lpmii
->cbSize
);
4852 mii
.cbSize
= sizeof( mii
);
4853 ret
= GetMenuItemInfo_common (hmenu
, item
, bypos
,
4854 (LPMENUITEMINFOW
)&mii
, FALSE
);
4855 mii
.cbSize
= lpmii
->cbSize
;
4856 memcpy( lpmii
, &mii
, mii
.cbSize
);
4860 /**********************************************************************
4861 * GetMenuItemInfoW (USER32.@)
4863 BOOL WINAPI
GetMenuItemInfoW( HMENU hmenu
, UINT item
, BOOL bypos
,
4864 LPMENUITEMINFOW lpmii
)
4868 if( lpmii
->cbSize
!= sizeof( mii
) &&
4869 lpmii
->cbSize
!= sizeof( mii
) - sizeof ( mii
.hbmpItem
)) {
4870 SetLastError( ERROR_INVALID_PARAMETER
);
4873 memcpy( &mii
, lpmii
, lpmii
->cbSize
);
4874 mii
.cbSize
= sizeof( mii
);
4875 ret
= GetMenuItemInfo_common (hmenu
, item
, bypos
, &mii
, TRUE
);
4876 mii
.cbSize
= lpmii
->cbSize
;
4877 memcpy( lpmii
, &mii
, mii
.cbSize
);
4882 /* set a menu item text from a ASCII or Unicode string */
4883 static inline void set_menu_item_text( MENUITEM
*menu
, LPCWSTR text
, BOOL unicode
)
4889 if ((menu
->text
= HeapAlloc( GetProcessHeap(), 0, (lstrlenW(text
)+1) * sizeof(WCHAR
) )))
4890 lstrcpyW( menu
->text
, text
);
4894 LPCSTR str
= (LPCSTR
)text
;
4895 int len
= MultiByteToWideChar( CP_ACP
, 0, str
, -1, NULL
, 0 );
4896 if ((menu
->text
= HeapAlloc( GetProcessHeap(), 0, len
* sizeof(WCHAR
) )))
4897 MultiByteToWideChar( CP_ACP
, 0, str
, -1, menu
->text
, len
);
4902 /**********************************************************************
4905 * detect if there are loops in the menu tree (or the depth is too large)
4907 static int MENU_depth( POPUPMENU
*pmenu
, int depth
)
4914 if( depth
> MAXMENUDEPTH
) return depth
;
4915 item
= pmenu
->items
;
4917 for( i
= 0; i
< pmenu
->nItems
&& subdepth
<= MAXMENUDEPTH
; i
++, item
++){
4918 POPUPMENU
*psubmenu
= item
->hSubMenu
? MENU_GetMenu( item
->hSubMenu
) : NULL
;
4920 int bdepth
= MENU_depth( psubmenu
, depth
);
4921 if( bdepth
> subdepth
) subdepth
= bdepth
;
4923 if( subdepth
> MAXMENUDEPTH
)
4924 TRACE("<- hmenu %p\n", item
->hSubMenu
);
4930 /**********************************************************************
4931 * SetMenuItemInfo_common
4933 * Note: does not support the MIIM_TYPE flag. Use the MIIM_FTYPE,
4934 * MIIM_BITMAP and MIIM_STRING flags instead.
4937 static BOOL
SetMenuItemInfo_common(MENUITEM
* menu
,
4938 const MENUITEMINFOW
*lpmii
,
4941 if (!menu
) return FALSE
;
4943 debug_print_menuitem("SetMenuItemInfo_common from: ", menu
, "");
4945 if (lpmii
->fMask
& MIIM_FTYPE
) {
4946 menu
->fType
&= ~MENUITEMINFO_TYPE_MASK
;
4947 menu
->fType
|= lpmii
->fType
& MENUITEMINFO_TYPE_MASK
;
4949 if (lpmii
->fMask
& MIIM_STRING
) {
4950 /* free the string when used */
4951 HeapFree(GetProcessHeap(), 0, menu
->text
);
4952 set_menu_item_text( menu
, lpmii
->dwTypeData
, unicode
);
4955 if (lpmii
->fMask
& MIIM_STATE
)
4956 /* Other menu items having MFS_DEFAULT are not converted
4958 menu
->fState
= lpmii
->fState
& MENUITEMINFO_STATE_MASK
;
4960 if (lpmii
->fMask
& MIIM_ID
)
4961 menu
->wID
= lpmii
->wID
;
4963 if (lpmii
->fMask
& MIIM_SUBMENU
) {
4964 menu
->hSubMenu
= lpmii
->hSubMenu
;
4965 if (menu
->hSubMenu
) {
4966 POPUPMENU
*subMenu
= MENU_GetMenu(menu
->hSubMenu
);
4968 if( MENU_depth( subMenu
, 0) > MAXMENUDEPTH
) {
4969 ERR( "Loop detected in menu hierarchy or maximum menu depth exceeded!\n");
4973 subMenu
->wFlags
|= MF_POPUP
;
4974 menu
->fType
|= MF_POPUP
;
4976 SetLastError( ERROR_INVALID_PARAMETER
);
4981 menu
->fType
&= ~MF_POPUP
;
4984 if (lpmii
->fMask
& MIIM_CHECKMARKS
)
4986 menu
->hCheckBit
= lpmii
->hbmpChecked
;
4987 menu
->hUnCheckBit
= lpmii
->hbmpUnchecked
;
4989 if (lpmii
->fMask
& MIIM_DATA
)
4990 menu
->dwItemData
= lpmii
->dwItemData
;
4992 if (lpmii
->fMask
& MIIM_BITMAP
)
4993 menu
->hbmpItem
= lpmii
->hbmpItem
;
4995 if( !menu
->text
&& !(menu
->fType
& MFT_OWNERDRAW
) && !menu
->hbmpItem
)
4996 menu
->fType
|= MFT_SEPARATOR
;
4998 debug_print_menuitem("SetMenuItemInfo_common to : ", menu
, "");
5002 /**********************************************************************
5003 * MENU_NormalizeMenuItemInfoStruct
5005 * Helper for SetMenuItemInfo and InsertMenuItemInfo:
5006 * check, copy and extend the MENUITEMINFO struct from the version that the application
5007 * supplied to the version used by wine source. */
5008 static BOOL
MENU_NormalizeMenuItemInfoStruct( const MENUITEMINFOW
*pmii_in
,
5009 MENUITEMINFOW
*pmii_out
)
5011 /* do we recognize the size? */
5012 if( !pmii_in
|| (pmii_in
->cbSize
!= sizeof( MENUITEMINFOW
) &&
5013 pmii_in
->cbSize
!= sizeof( MENUITEMINFOW
) - sizeof( pmii_in
->hbmpItem
)) ) {
5014 SetLastError( ERROR_INVALID_PARAMETER
);
5017 /* copy the fields that we have */
5018 memcpy( pmii_out
, pmii_in
, pmii_in
->cbSize
);
5019 /* if the hbmpItem member is missing then extend */
5020 if( pmii_in
->cbSize
!= sizeof( MENUITEMINFOW
)) {
5021 pmii_out
->cbSize
= sizeof( MENUITEMINFOW
);
5022 pmii_out
->hbmpItem
= NULL
;
5024 /* test for invalid bit combinations */
5025 if( (pmii_out
->fMask
& MIIM_TYPE
&&
5026 pmii_out
->fMask
& (MIIM_STRING
| MIIM_FTYPE
| MIIM_BITMAP
)) ||
5027 (pmii_out
->fMask
& MIIM_FTYPE
&& pmii_out
->fType
& MFT_BITMAP
)) {
5028 WARN("invalid combination of fMask bits used\n");
5029 /* this does not happen on Win9x/ME */
5030 SetLastError( ERROR_INVALID_PARAMETER
);
5033 /* convert old style (MIIM_TYPE) to the new */
5034 if( pmii_out
->fMask
& MIIM_TYPE
){
5035 pmii_out
->fMask
|= MIIM_FTYPE
;
5036 if( IS_STRING_ITEM(pmii_out
->fType
)){
5037 pmii_out
->fMask
|= MIIM_STRING
;
5038 } else if( (pmii_out
->fType
) & MFT_BITMAP
){
5039 pmii_out
->fMask
|= MIIM_BITMAP
;
5040 pmii_out
->hbmpItem
= UlongToHandle(LOWORD(pmii_out
->dwTypeData
));
5046 /**********************************************************************
5047 * SetMenuItemInfoA (USER32.@)
5049 BOOL WINAPI
SetMenuItemInfoA(HMENU hmenu
, UINT item
, BOOL bypos
,
5050 const MENUITEMINFOA
*lpmii
)
5057 TRACE("hmenu %p, item %u, by pos %d, info %p\n", hmenu
, item
, bypos
, lpmii
);
5059 if (!MENU_NormalizeMenuItemInfoStruct( (const MENUITEMINFOW
*)lpmii
, &mii
)) return FALSE
;
5061 if (!(menu
= find_menu_item(hmenu
, item
, bypos
? MF_BYPOSITION
: 0, &pos
)))
5063 /* workaround for Word 95: pretend that SC_TASKLIST item exists */
5064 if (item
== SC_TASKLIST
&& !bypos
) return TRUE
;
5067 ret
= SetMenuItemInfo_common(&menu
->items
[pos
], &mii
, FALSE
);
5068 release_menu_ptr(menu
);
5072 /**********************************************************************
5073 * SetMenuItemInfoW (USER32.@)
5075 BOOL WINAPI
SetMenuItemInfoW(HMENU hmenu
, UINT item
, BOOL bypos
,
5076 const MENUITEMINFOW
*lpmii
)
5083 TRACE("hmenu %p, item %u, by pos %d, info %p\n", hmenu
, item
, bypos
, lpmii
);
5085 if (!MENU_NormalizeMenuItemInfoStruct( lpmii
, &mii
)) return FALSE
;
5087 if (!(menu
= find_menu_item(hmenu
, item
, bypos
? MF_BYPOSITION
: 0, &pos
)))
5089 /* workaround for Word 95: pretend that SC_TASKLIST item exists */
5090 if (item
== SC_TASKLIST
&& !bypos
) return TRUE
;
5094 ret
= SetMenuItemInfo_common(&menu
->items
[pos
], &mii
, TRUE
);
5095 release_menu_ptr(menu
);
5099 static BOOL
set_menu_default_item(POPUPMENU
*menu
, UINT uItem
, UINT bypos
)
5104 /* reset all default-item flags */
5106 for (i
= 0; i
< menu
->nItems
; i
++, item
++)
5108 item
->fState
&= ~MFS_DEFAULT
;
5111 /* no default item */
5118 if ( uItem
>= menu
->nItems
) return FALSE
;
5119 item
[uItem
].fState
|= MFS_DEFAULT
;
5124 for (i
= 0; i
< menu
->nItems
; i
++, item
++)
5126 if (item
->wID
== uItem
)
5128 item
->fState
|= MFS_DEFAULT
;
5137 /**********************************************************************
5138 * SetMenuDefaultItem (USER32.@)
5141 BOOL WINAPI
SetMenuDefaultItem(HMENU hmenu
, UINT uItem
, UINT bypos
)
5146 TRACE("(%p,%d,%d)\n", hmenu
, uItem
, bypos
);
5148 if (!(menu
= grab_menu_ptr(hmenu
))) return FALSE
;
5149 ret
= set_menu_default_item(menu
, uItem
, bypos
);
5150 release_menu_ptr(menu
);
5155 /**********************************************************************
5156 * GetMenuDefaultItem (USER32.@)
5158 UINT WINAPI
GetMenuDefaultItem(HMENU hmenu
, UINT bypos
, UINT flags
)
5164 TRACE("(%p,%d,%d)\n", hmenu
, bypos
, flags
);
5166 if (!(menu
= MENU_GetMenu(hmenu
))) return -1;
5168 /* find default item */
5172 if (! item
) return -1;
5174 while ( !( item
->fState
& MFS_DEFAULT
) )
5177 if (i
>= menu
->nItems
) return -1;
5180 /* default: don't return disabled items */
5181 if ( (!(GMDI_USEDISABLED
& flags
)) && (item
->fState
& MFS_DISABLED
)) return -1;
5183 /* search rekursiv when needed */
5184 if ( (item
->fType
& MF_POPUP
) && (flags
& GMDI_GOINTOPOPUPS
) )
5187 ret
= GetMenuDefaultItem( item
->hSubMenu
, bypos
, flags
);
5188 if ( -1 != ret
) return ret
;
5190 /* when item not found in submenu, return the popup item */
5192 return ( bypos
) ? i
: item
->wID
;
5197 /**********************************************************************
5198 * InsertMenuItemA (USER32.@)
5200 BOOL WINAPI
InsertMenuItemA(HMENU hMenu
, UINT uItem
, BOOL bypos
,
5201 const MENUITEMINFOA
*lpmii
)
5208 TRACE("hmenu %p, item %04x, by pos %d, info %p\n", hMenu
, uItem
, bypos
, lpmii
);
5210 if (!MENU_NormalizeMenuItemInfoStruct( (const MENUITEMINFOW
*)lpmii
, &mii
)) return FALSE
;
5212 if (!(menu
= insert_menu_item(hMenu
, uItem
, bypos
? MF_BYPOSITION
: 0, &pos
)))
5215 ret
= SetMenuItemInfo_common(&menu
->items
[pos
], &mii
, FALSE
);
5216 release_menu_ptr(menu
);
5221 /**********************************************************************
5222 * InsertMenuItemW (USER32.@)
5224 BOOL WINAPI
InsertMenuItemW(HMENU hMenu
, UINT uItem
, BOOL bypos
,
5225 const MENUITEMINFOW
*lpmii
)
5232 TRACE("hmenu %p, item %04x, by pos %d, info %p\n", hMenu
, uItem
, bypos
, lpmii
);
5234 if (!MENU_NormalizeMenuItemInfoStruct( lpmii
, &mii
)) return FALSE
;
5236 if (!(menu
= insert_menu_item(hMenu
, uItem
, bypos
? MF_BYPOSITION
: 0, &pos
)))
5239 ret
= SetMenuItemInfo_common(&menu
->items
[pos
], &mii
, TRUE
);
5240 release_menu_ptr(menu
);
5244 /**********************************************************************
5245 * CheckMenuRadioItem (USER32.@)
5248 BOOL WINAPI
CheckMenuRadioItem(HMENU hMenu
, UINT first
, UINT last
,
5249 UINT check
, UINT flags
)
5251 POPUPMENU
*first_menu
= NULL
, *check_menu
;
5255 for (i
= first
; i
<= last
; i
++)
5259 if (!(check_menu
= find_menu_item(hMenu
, i
, flags
, &check_pos
)))
5263 first_menu
= grab_menu_ptr(check_menu
->obj
.handle
);
5265 if (first_menu
!= check_menu
)
5267 release_menu_ptr(check_menu
);
5271 item
= &check_menu
->items
[check_pos
];
5272 if (item
->fType
!= MFT_SEPARATOR
)
5276 item
->fType
|= MFT_RADIOCHECK
;
5277 item
->fState
|= MFS_CHECKED
;
5282 /* MSDN is wrong, Windows does not remove MFT_RADIOCHECK */
5283 item
->fState
&= ~MFS_CHECKED
;
5287 release_menu_ptr(check_menu
);
5289 release_menu_ptr(first_menu
);
5295 /**********************************************************************
5296 * GetMenuItemRect (USER32.@)
5298 * ATTENTION: Here, the returned values in rect are the screen
5299 * coordinates of the item just like if the menu was
5300 * always on the upper left side of the application.
5303 BOOL WINAPI
GetMenuItemRect(HWND hwnd
, HMENU hMenu
, UINT uItem
, RECT
*rect
)
5309 TRACE("(%p,%p,%d,%p)\n", hwnd
, hMenu
, uItem
, rect
);
5314 if (!(menu
= find_menu_item(hMenu
, uItem
, MF_BYPOSITION
, &pos
)))
5317 if (!hwnd
) hwnd
= menu
->hWnd
;
5320 release_menu_ptr(menu
);
5324 *rect
= menu
->items
[pos
].rect
;
5325 OffsetRect(rect
, menu
->items_rect
.left
, menu
->items_rect
.top
);
5327 /* Popup menu item draws in the client area */
5328 if (menu
->wFlags
& MF_POPUP
) MapWindowPoints(hwnd
, 0, (POINT
*)rect
, 2);
5331 /* Sysmenu draws in the non-client area */
5332 GetWindowRect(hwnd
, &window_rect
);
5333 OffsetRect(rect
, window_rect
.left
, window_rect
.top
);
5336 release_menu_ptr(menu
);
5340 /**********************************************************************
5341 * SetMenuInfo (USER32.@)
5344 * actually use the items to draw the menu
5345 * (recalculate and/or redraw)
5347 static BOOL
menu_SetMenuInfo( HMENU hMenu
, LPCMENUINFO lpmi
)
5350 if( !(menu
= MENU_GetMenu(hMenu
))) return FALSE
;
5352 if (lpmi
->fMask
& MIM_BACKGROUND
)
5353 menu
->hbrBack
= lpmi
->hbrBack
;
5355 if (lpmi
->fMask
& MIM_HELPID
)
5356 menu
->dwContextHelpID
= lpmi
->dwContextHelpID
;
5358 if (lpmi
->fMask
& MIM_MAXHEIGHT
)
5359 menu
->cyMax
= lpmi
->cyMax
;
5361 if (lpmi
->fMask
& MIM_MENUDATA
)
5362 menu
->dwMenuData
= lpmi
->dwMenuData
;
5364 if (lpmi
->fMask
& MIM_STYLE
)
5365 menu
->dwStyle
= lpmi
->dwStyle
;
5367 if( lpmi
->fMask
& MIM_APPLYTOSUBMENUS
) {
5369 MENUITEM
*item
= menu
->items
;
5370 for( i
= menu
->nItems
; i
; i
--, item
++)
5371 if( item
->fType
& MF_POPUP
)
5372 menu_SetMenuInfo( item
->hSubMenu
, lpmi
);
5377 BOOL WINAPI
SetMenuInfo (HMENU hMenu
, LPCMENUINFO lpmi
)
5379 TRACE("(%p %p)\n", hMenu
, lpmi
);
5380 if( lpmi
&& (lpmi
->cbSize
== sizeof( MENUINFO
)) && (menu_SetMenuInfo( hMenu
, lpmi
))) {
5381 if( lpmi
->fMask
& MIM_STYLE
) {
5382 if (lpmi
->dwStyle
& MNS_AUTODISMISS
) FIXME("MNS_AUTODISMISS unimplemented\n");
5383 if (lpmi
->dwStyle
& MNS_DRAGDROP
) FIXME("MNS_DRAGDROP unimplemented\n");
5384 if (lpmi
->dwStyle
& MNS_MODELESS
) FIXME("MNS_MODELESS unimplemented\n");
5388 SetLastError( ERROR_INVALID_PARAMETER
);
5392 /**********************************************************************
5393 * GetMenuInfo (USER32.@)
5399 BOOL WINAPI
GetMenuInfo (HMENU hMenu
, LPMENUINFO lpmi
)
5403 TRACE("(%p %p)\n", hMenu
, lpmi
);
5405 if (lpmi
&& (lpmi
->cbSize
== sizeof(MENUINFO
)) && (menu
= grab_menu_ptr(hMenu
)))
5407 if (lpmi
->fMask
& MIM_BACKGROUND
)
5408 lpmi
->hbrBack
= menu
->hbrBack
;
5410 if (lpmi
->fMask
& MIM_HELPID
)
5411 lpmi
->dwContextHelpID
= menu
->dwContextHelpID
;
5413 if (lpmi
->fMask
& MIM_MAXHEIGHT
)
5414 lpmi
->cyMax
= menu
->cyMax
;
5416 if (lpmi
->fMask
& MIM_MENUDATA
)
5417 lpmi
->dwMenuData
= menu
->dwMenuData
;
5419 if (lpmi
->fMask
& MIM_STYLE
)
5420 lpmi
->dwStyle
= menu
->dwStyle
;
5422 release_menu_ptr(menu
);
5425 SetLastError( ERROR_INVALID_PARAMETER
);
5430 /**********************************************************************
5431 * SetMenuContextHelpId (USER32.@)
5433 BOOL WINAPI
SetMenuContextHelpId( HMENU hMenu
, DWORD dwContextHelpID
)
5437 TRACE("(%p 0x%08x)\n", hMenu
, dwContextHelpID
);
5439 if ((menu
= grab_menu_ptr(hMenu
)))
5441 menu
->dwContextHelpID
= dwContextHelpID
;
5442 release_menu_ptr(menu
);
5449 /**********************************************************************
5450 * GetMenuContextHelpId (USER32.@)
5452 DWORD WINAPI
GetMenuContextHelpId( HMENU hMenu
)
5457 TRACE("(%p)\n", hMenu
);
5459 if ((menu
= grab_menu_ptr(hMenu
)))
5461 help_id
= menu
->dwContextHelpID
;
5462 release_menu_ptr(menu
);
5468 /**********************************************************************
5469 * MenuItemFromPoint (USER32.@)
5471 INT WINAPI
MenuItemFromPoint(HWND hWnd
, HMENU hMenu
, POINT ptScreen
)
5473 POPUPMENU
*menu
= grab_menu_ptr(hMenu
);
5476 /*FIXME: Do we have to handle hWnd here? */
5477 if (!menu
) return -1;
5479 if (MENU_FindItemByCoords( menu
, ptScreen
, &pos
) != ht_item
)
5482 release_menu_ptr(menu
);
5487 /**********************************************************************
5488 * CalcMenuBar (USER32.@)
5490 DWORD WINAPI
CalcMenuBar(HWND hwnd
, DWORD left
, DWORD right
, DWORD top
, RECT
*rect
)
5492 FIXME("(%p, %d, %d, %d, %p): stub\n", hwnd
, left
, right
, top
, rect
);
5497 /**********************************************************************
5498 * translate_accelerator
5500 static BOOL
translate_accelerator( HWND hWnd
, UINT message
, WPARAM wParam
, LPARAM lParam
,
5501 BYTE fVirt
, WORD key
, WORD cmd
)
5506 if (wParam
!= key
) return FALSE
;
5508 if (GetKeyState(VK_CONTROL
) & 0x8000) mask
|= FCONTROL
;
5509 if (GetKeyState(VK_MENU
) & 0x8000) mask
|= FALT
;
5510 if (GetKeyState(VK_SHIFT
) & 0x8000) mask
|= FSHIFT
;
5512 if (message
== WM_CHAR
|| message
== WM_SYSCHAR
)
5514 if ( !(fVirt
& FVIRTKEY
) && (mask
& FALT
) == (fVirt
& FALT
) )
5516 TRACE_(accel
)("found accel for WM_CHAR: ('%c')\n", LOWORD(wParam
) & 0xff);
5522 if(fVirt
& FVIRTKEY
)
5524 TRACE_(accel
)("found accel for virt_key %04lx (scan %04x)\n",
5525 wParam
, 0xff & HIWORD(lParam
));
5527 if(mask
== (fVirt
& (FSHIFT
| FCONTROL
| FALT
))) goto found
;
5528 TRACE_(accel
)(", but incorrect SHIFT/CTRL/ALT-state\n");
5532 if (!(lParam
& 0x01000000)) /* no special_key */
5534 if ((fVirt
& FALT
) && (lParam
& 0x20000000))
5535 { /* ^^ ALT pressed */
5536 TRACE_(accel
)("found accel for Alt-%c\n", LOWORD(wParam
) & 0xff);
5545 if (message
== WM_KEYUP
|| message
== WM_SYSKEYUP
)
5549 HMENU hMenu
, hSubMenu
, hSysMenu
;
5550 UINT uSysStat
= (UINT
)-1, uStat
= (UINT
)-1, nPos
;
5553 hMenu
= (GetWindowLongW( hWnd
, GWL_STYLE
) & WS_CHILD
) ? 0 : GetMenu(hWnd
);
5554 hSysMenu
= get_win_sys_menu( hWnd
);
5556 /* find menu item and ask application to initialize it */
5557 /* 1. in the system menu */
5558 if ((menu
= find_menu_item(hSysMenu
, cmd
, MF_BYCOMMAND
, NULL
)))
5560 hSubMenu
= menu
->obj
.handle
;
5561 release_menu_ptr(menu
);
5565 if (!IsWindowEnabled(hWnd
))
5569 SendMessageW(hWnd
, WM_INITMENU
, (WPARAM
)hSysMenu
, 0L);
5570 if(hSubMenu
!= hSysMenu
)
5572 nPos
= MENU_FindSubMenu(&hSysMenu
, hSubMenu
);
5573 TRACE_(accel
)("hSysMenu = %p, hSubMenu = %p, nPos = %d\n", hSysMenu
, hSubMenu
, nPos
);
5574 SendMessageW(hWnd
, WM_INITMENUPOPUP
, (WPARAM
)hSubMenu
, MAKELPARAM(nPos
, TRUE
));
5576 uSysStat
= GetMenuState(GetSubMenu(hSysMenu
, 0), cmd
, MF_BYCOMMAND
);
5579 else /* 2. in the window's menu */
5581 if ((menu
= find_menu_item(hMenu
, cmd
, MF_BYCOMMAND
, NULL
)))
5583 hSubMenu
= menu
->obj
.handle
;
5584 release_menu_ptr(menu
);
5588 if (!IsWindowEnabled(hWnd
))
5592 SendMessageW(hWnd
, WM_INITMENU
, (WPARAM
)hMenu
, 0L);
5593 if(hSubMenu
!= hMenu
)
5595 nPos
= MENU_FindSubMenu(&hMenu
, hSubMenu
);
5596 TRACE_(accel
)("hMenu = %p, hSubMenu = %p, nPos = %d\n", hMenu
, hSubMenu
, nPos
);
5597 SendMessageW(hWnd
, WM_INITMENUPOPUP
, (WPARAM
)hSubMenu
, MAKELPARAM(nPos
, FALSE
));
5599 uStat
= GetMenuState(hMenu
, cmd
, MF_BYCOMMAND
);
5606 if (uSysStat
!= (UINT
)-1)
5608 if (uSysStat
& (MF_DISABLED
|MF_GRAYED
))
5615 if (uStat
!= (UINT
)-1)
5621 if (uStat
& (MF_DISABLED
|MF_GRAYED
))
5633 if( mesg
==WM_COMMAND
)
5635 TRACE_(accel
)(", sending WM_COMMAND, wParam=%0x\n", 0x10000 | cmd
);
5636 SendMessageW(hWnd
, mesg
, 0x10000 | cmd
, 0L);
5638 else if( mesg
==WM_SYSCOMMAND
)
5640 TRACE_(accel
)(", sending WM_SYSCOMMAND, wParam=%0x\n", cmd
);
5641 SendMessageW(hWnd
, mesg
, cmd
, 0x00010000L
);
5645 /* some reasons for NOT sending the WM_{SYS}COMMAND message:
5646 * #0: unknown (please report!)
5647 * #1: for WM_KEYUP,WM_SYSKEYUP
5648 * #2: mouse is captured
5649 * #3: window is disabled
5650 * #4: it's a disabled system menu option
5651 * #5: it's a menu option, but window is iconic
5652 * #6: it's a menu option, but disabled
5654 TRACE_(accel
)(", but won't send WM_{SYS}COMMAND, reason is #%d\n",mesg
);
5656 ERR_(accel
)(" unknown reason - please report!\n");
5661 /**********************************************************************
5662 * TranslateAcceleratorA (USER32.@)
5663 * TranslateAccelerator (USER32.@)
5665 INT WINAPI
TranslateAcceleratorA( HWND hWnd
, HACCEL hAccel
, LPMSG msg
)
5667 switch (msg
->message
)
5671 return TranslateAcceleratorW( hWnd
, hAccel
, msg
);
5677 char ch
= LOWORD(msg
->wParam
);
5679 MultiByteToWideChar(CP_ACP
, 0, &ch
, 1, &wch
, 1);
5680 msgW
.wParam
= MAKEWPARAM(wch
, HIWORD(msg
->wParam
));
5681 return TranslateAcceleratorW( hWnd
, hAccel
, &msgW
);
5689 /**********************************************************************
5690 * TranslateAcceleratorW (USER32.@)
5692 INT WINAPI
TranslateAcceleratorW( HWND hWnd
, HACCEL hAccel
, LPMSG msg
)
5694 ACCEL data
[32], *ptr
= data
;
5697 if (!hWnd
) return 0;
5699 if (msg
->message
!= WM_KEYDOWN
&&
5700 msg
->message
!= WM_SYSKEYDOWN
&&
5701 msg
->message
!= WM_CHAR
&&
5702 msg
->message
!= WM_SYSCHAR
)
5705 TRACE_(accel
)("hAccel %p, hWnd %p, msg->hwnd %p, msg->message %04x, wParam %08lx, lParam %08lx\n",
5706 hAccel
,hWnd
,msg
->hwnd
,msg
->message
,msg
->wParam
,msg
->lParam
);
5708 if (!(count
= CopyAcceleratorTableW( hAccel
, NULL
, 0 ))) return 0;
5709 if (count
> ARRAY_SIZE( data
))
5711 if (!(ptr
= HeapAlloc( GetProcessHeap(), 0, count
* sizeof(*ptr
) ))) return 0;
5713 count
= CopyAcceleratorTableW( hAccel
, ptr
, count
);
5714 for (i
= 0; i
< count
; i
++)
5716 if (translate_accelerator( hWnd
, msg
->message
, msg
->wParam
, msg
->lParam
,
5717 ptr
[i
].fVirt
, ptr
[i
].key
, ptr
[i
].cmd
))
5720 if (ptr
!= data
) HeapFree( GetProcessHeap(), 0, ptr
);