4 * Copyright 1993 Martin Ayotte
5 * Copyright 1994 Alexandre Julliard
6 * Copyright 1997 Morten Welinder
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2.1 of the License, or (at your option) any later version.
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
24 * Note: the style MF_MOUSESELECT is used to mark popup items that
25 * have been selected, i.e. their popup menu is currently displayed.
26 * This is probably not the meaning this style has in MS-Windows.
30 #include "wine/port.h"
39 #include "wine/winbase16.h"
40 #include "wine/winuser16.h"
42 #include "wine/server.h"
43 #include "wine/unicode.h"
46 #include "nonclient.h"
48 #include "wine/debug.h"
50 WINE_DEFAULT_DEBUG_CHANNEL(menu
);
51 WINE_DECLARE_DEBUG_CHANNEL(accel
);
53 /* internal popup menu window messages */
55 #define MM_SETMENUHANDLE (WM_USER + 0)
56 #define MM_GETMENUHANDLE (WM_USER + 1)
58 /* Menu item structure */
60 /* ----------- MENUITEMINFO Stuff ----------- */
61 UINT fType
; /* Item type. */
62 UINT fState
; /* Item state. */
63 UINT_PTR wID
; /* Item id. */
64 HMENU hSubMenu
; /* Pop-up menu. */
65 HBITMAP hCheckBit
; /* Bitmap when checked. */
66 HBITMAP hUnCheckBit
; /* Bitmap when unchecked. */
67 LPWSTR text
; /* Item text or bitmap handle. */
68 DWORD dwItemData
; /* Application defined. */
69 DWORD dwTypeData
; /* depends on fMask */
70 HBITMAP hbmpItem
; /* bitmap in win98 style menus */
71 /* ----------- Wine stuff ----------- */
72 RECT rect
; /* Item area (relative to menu window) */
73 UINT xTab
; /* X position of text after Tab */
76 /* Popup menu structure */
78 WORD wFlags
; /* Menu flags (MF_POPUP, MF_SYSMENU) */
79 WORD wMagic
; /* Magic number */
80 WORD Width
; /* Width of the whole menu */
81 WORD Height
; /* Height of the whole menu */
82 UINT nItems
; /* Number of items in the menu */
83 HWND hWnd
; /* Window containing the menu */
84 MENUITEM
*items
; /* Array of menu items */
85 UINT FocusedItem
; /* Currently focused item */
86 HWND hwndOwner
; /* window receiving the messages for ownerdraw */
87 BOOL bTimeToHide
; /* Request hiding when receiving a second click in the top-level menu item */
88 /* ------------ MENUINFO members ------ */
89 DWORD dwStyle
; /* Extended mennu style */
90 UINT cyMax
; /* max hight of the whole menu, 0 is screen hight */
91 HBRUSH hbrBack
; /* brush for menu background */
92 DWORD dwContextHelpID
;
93 DWORD dwMenuData
; /* application defined value */
94 HMENU hSysMenuOwner
; /* Handle to the dummy sys menu holder */
95 } POPUPMENU
, *LPPOPUPMENU
;
97 /* internal flags for menu tracking */
99 #define TF_ENDMENU 0x0001
100 #define TF_SUSPENDPOPUP 0x0002
101 #define TF_SKIPREMOVE 0x0004
106 HMENU hCurrentMenu
; /* current submenu (can be equal to hTopMenu)*/
107 HMENU hTopMenu
; /* initial menu */
108 HWND hOwnerWnd
; /* where notifications are sent */
112 #define MENU_MAGIC 0x554d /* 'MU' */
117 /* Internal MENU_TrackMenu() flags */
118 #define TPM_INTERNAL 0xF0000000
119 #define TPM_ENTERIDLEEX 0x80000000 /* set owner window for WM_ENTERIDLE */
120 #define TPM_BUTTONDOWN 0x40000000 /* menu was clicked before tracking */
121 #define TPM_POPUPMENU 0x20000000 /* menu is a popup menu */
123 /* popup menu shade thickness */
124 #define POPUP_XSHADE 4
125 #define POPUP_YSHADE 4
127 /* Space between 2 menu bar items */
128 #define MENU_BAR_ITEMS_SPACE 12
130 /* Minimum width of a tab character */
131 #define MENU_TAB_SPACE 8
133 /* Height of a separator item */
134 #define SEPARATOR_HEIGHT 5
136 /* (other menu->FocusedItem values give the position of the focused item) */
137 #define NO_SELECTED_ITEM 0xffff
139 #define MENU_ITEM_TYPE(flags) \
140 ((flags) & (MF_STRING | MF_BITMAP | MF_OWNERDRAW | MF_SEPARATOR))
142 #define IS_STRING_ITEM(flags) (MENU_ITEM_TYPE ((flags)) == MF_STRING)
143 #define IS_BITMAP_ITEM(flags) (MENU_ITEM_TYPE ((flags)) == MF_BITMAP)
144 #define IS_MAGIC_ITEM(text) (LOWORD((int)text)<12)
146 #define IS_SYSTEM_MENU(menu) \
147 (!((menu)->wFlags & MF_POPUP) && (menu)->wFlags & MF_SYSMENU)
149 #define IS_SYSTEM_POPUP(menu) \
150 ((menu)->wFlags & MF_POPUP && (menu)->wFlags & MF_SYSMENU)
152 #define TYPE_MASK (MFT_STRING | MFT_BITMAP | MFT_OWNERDRAW | MFT_SEPARATOR | \
153 MFT_MENUBARBREAK | MFT_MENUBREAK | MFT_RADIOCHECK | \
154 MFT_RIGHTORDER | MFT_RIGHTJUSTIFY | \
155 MF_POPUP | MF_SYSMENU | MF_HELP)
156 #define STATE_MASK (~TYPE_MASK)
158 /* Dimension of the menu bitmaps */
159 static WORD arrow_bitmap_width
= 0, arrow_bitmap_height
= 0;
161 static HBITMAP hStdMnArrow
= 0;
162 static HBITMAP hBmpSysMenu
= 0;
164 static HBRUSH hShadeBrush
= 0;
165 static HFONT hMenuFont
= 0;
166 static HFONT hMenuFontBold
= 0;
168 static HMENU MENU_DefSysPopup
= 0; /* Default system menu popup */
170 /* Use global popup window because there's no way 2 menus can
171 * be tracked at the same time. */
172 static HWND top_popup
;
174 /* Flag set by EndMenu() to force an exit from menu tracking */
175 static BOOL fEndMenu
= FALSE
;
177 static LRESULT WINAPI
PopupMenuWndProc( HWND hwnd
, UINT message
, WPARAM wParam
, LPARAM lParam
);
179 DWORD WINAPI
DrawMenuBarTemp(HWND hwnd
, HDC hDC
, LPRECT lprect
, HMENU hMenu
, HFONT hFont
);
181 /*********************************************************************
182 * menu class descriptor
184 const struct builtin_class_descr MENU_builtin_class
=
186 POPUPMENU_CLASS_ATOMA
, /* name */
187 CS_DROPSHADOW
| CS_SAVEBITS
| CS_DBLCLKS
, /* style */
188 NULL
, /* procA (winproc is Unicode only) */
189 PopupMenuWndProc
, /* procW */
190 sizeof(HMENU
), /* extra */
191 IDC_ARROW
, /* cursor */
192 (HBRUSH
)(COLOR_MENU
+1) /* brush */
196 /***********************************************************************
197 * debug_print_menuitem
199 * Print a menuitem in readable form.
202 #define debug_print_menuitem(pre, mp, post) \
203 if(!TRACE_ON(menu)) ; else do_debug_print_menuitem(pre, mp, post)
205 #define MENUOUT(text) \
206 DPRINTF("%s%s", (count++ ? "," : ""), (text))
208 #define MENUFLAG(bit,text) \
210 if (flags & (bit)) { flags &= ~(bit); MENUOUT ((text)); } \
213 static void do_debug_print_menuitem(const char *prefix
, MENUITEM
* mp
,
216 TRACE("%s ", prefix
);
218 UINT flags
= mp
->fType
;
219 int type
= MENU_ITEM_TYPE(flags
);
220 DPRINTF( "{ ID=0x%x", mp
->wID
);
221 if (flags
& MF_POPUP
)
222 DPRINTF( ", Sub=%p", mp
->hSubMenu
);
226 if (type
== MFT_STRING
)
228 else if (type
== MFT_SEPARATOR
)
230 else if (type
== MFT_OWNERDRAW
)
232 else if (type
== MFT_BITMAP
)
238 MENUFLAG(MF_POPUP
, "pop");
239 MENUFLAG(MFT_MENUBARBREAK
, "barbrk");
240 MENUFLAG(MFT_MENUBREAK
, "brk");
241 MENUFLAG(MFT_RADIOCHECK
, "radio");
242 MENUFLAG(MFT_RIGHTORDER
, "rorder");
243 MENUFLAG(MF_SYSMENU
, "sys");
244 MENUFLAG(MFT_RIGHTJUSTIFY
, "right"); /* same as MF_HELP */
247 DPRINTF( "+0x%x", flags
);
252 DPRINTF( ", State=");
253 MENUFLAG(MFS_GRAYED
, "grey");
254 MENUFLAG(MFS_DEFAULT
, "default");
255 MENUFLAG(MFS_DISABLED
, "dis");
256 MENUFLAG(MFS_CHECKED
, "check");
257 MENUFLAG(MFS_HILITE
, "hi");
258 MENUFLAG(MF_USECHECKBITMAPS
, "usebit");
259 MENUFLAG(MF_MOUSESELECT
, "mouse");
261 DPRINTF( "+0x%x", flags
);
264 DPRINTF( ", Chk=%p", mp
->hCheckBit
);
266 DPRINTF( ", Unc=%p", mp
->hUnCheckBit
);
268 if (type
== MFT_STRING
) {
270 DPRINTF( ", Text=%s", debugstr_w(mp
->text
));
272 DPRINTF( ", Text=Null");
273 } else if (mp
->text
== NULL
)
276 DPRINTF( ", Text=%p", mp
->text
);
278 DPRINTF( ", ItemData=0x%08lx", mp
->dwItemData
);
284 DPRINTF(" %s\n", postfix
);
291 /***********************************************************************
294 * Validate the given menu handle and returns the menu structure pointer.
296 static POPUPMENU
*MENU_GetMenu(HMENU hMenu
)
298 POPUPMENU
*menu
= USER_HEAP_LIN_ADDR(hMenu
);
299 if (!menu
|| menu
->wMagic
!= MENU_MAGIC
)
301 WARN("invalid menu handle=%p, ptr=%p, magic=%x\n", hMenu
, menu
, menu
? menu
->wMagic
:0);
307 /***********************************************************************
310 * Get the system menu of a window
312 static HMENU
get_win_sys_menu( HWND hwnd
)
315 WND
*win
= WIN_FindWndPtr( hwnd
);
319 WIN_ReleaseWndPtr( win
);
324 /***********************************************************************
327 * Return the default system menu.
329 static HMENU
MENU_CopySysPopup(void)
331 static const WCHAR user32W
[] = {'U','S','E','R','3','2',0};
332 static const WCHAR sysmenuW
[] = {'S','Y','S','M','E','N','U',0};
333 HMENU hMenu
= LoadMenuW(GetModuleHandleW(user32W
), sysmenuW
);
336 POPUPMENU
* menu
= MENU_GetMenu(hMenu
);
337 menu
->wFlags
|= MF_SYSMENU
| MF_POPUP
;
338 SetMenuDefaultItem(hMenu
, SC_CLOSE
, FALSE
);
341 ERR("Unable to load default system menu\n" );
343 TRACE("returning %p.\n", hMenu
);
349 /**********************************************************************
352 * Create a copy of the system menu. System menu in Windows is
353 * a special menu bar with the single entry - system menu popup.
354 * This popup is presented to the outside world as a "system menu".
355 * However, the real system menu handle is sometimes seen in the
356 * WM_MENUSELECT parameters (and Word 6 likes it this way).
358 HMENU
MENU_GetSysMenu( HWND hWnd
, HMENU hPopupMenu
)
362 TRACE("loading system menu, hWnd %p, hPopupMenu %p\n", hWnd
, hPopupMenu
);
363 if ((hMenu
= CreateMenu()))
365 POPUPMENU
*menu
= MENU_GetMenu(hMenu
);
366 menu
->wFlags
= MF_SYSMENU
;
367 menu
->hWnd
= WIN_GetFullHandle( hWnd
);
368 TRACE("hWnd %p (hMenu %p)\n", menu
->hWnd
, hMenu
);
370 if (hPopupMenu
== (HMENU
)(-1))
371 hPopupMenu
= MENU_CopySysPopup();
372 else if( !hPopupMenu
) hPopupMenu
= MENU_DefSysPopup
;
376 InsertMenuW( hMenu
, -1, MF_SYSMENU
| MF_POPUP
| MF_BYPOSITION
,
377 (UINT_PTR
)hPopupMenu
, NULL
);
379 menu
->items
[0].fType
= MF_SYSMENU
| MF_POPUP
;
380 menu
->items
[0].fState
= 0;
381 if ((menu
= MENU_GetMenu(hPopupMenu
))) menu
->wFlags
|= MF_SYSMENU
;
383 TRACE("hMenu=%p (hPopup %p)\n", hMenu
, hPopupMenu
);
386 DestroyMenu( hMenu
);
388 ERR("failed to load system menu!\n");
393 /***********************************************************************
396 * Menus initialisation.
401 NONCLIENTMETRICSW ncm
;
403 static unsigned char shade_bits
[16] = { 0x55, 0, 0xAA, 0,
408 /* Load menu bitmaps */
409 hStdMnArrow
= LoadBitmapW(0, MAKEINTRESOURCEW(OBM_MNARROW
));
410 /* Load system buttons bitmaps */
411 hBmpSysMenu
= LoadBitmapW(0, MAKEINTRESOURCEW(OBM_CLOSE
));
416 GetObjectW( hStdMnArrow
, sizeof(bm
), &bm
);
417 arrow_bitmap_width
= bm
.bmWidth
;
418 arrow_bitmap_height
= bm
.bmHeight
;
422 if (! (hBitmap
= CreateBitmap( 8, 8, 1, 1, shade_bits
)))
425 if(!(hShadeBrush
= CreatePatternBrush( hBitmap
)))
428 DeleteObject( hBitmap
);
429 if (!(MENU_DefSysPopup
= MENU_CopySysPopup()))
432 ncm
.cbSize
= sizeof(NONCLIENTMETRICSW
);
433 if (!(SystemParametersInfoW(SPI_GETNONCLIENTMETRICS
, sizeof(NONCLIENTMETRICSW
), &ncm
, 0)))
436 if (!(hMenuFont
= CreateFontIndirectW( &ncm
.lfMenuFont
)))
439 ncm
.lfMenuFont
.lfWeight
+= 300;
440 if ( ncm
.lfMenuFont
.lfWeight
> 1000)
441 ncm
.lfMenuFont
.lfWeight
= 1000;
443 if (!(hMenuFontBold
= CreateFontIndirectW( &ncm
.lfMenuFont
)))
449 /***********************************************************************
450 * MENU_InitSysMenuPopup
452 * Grey the appropriate items in System menu.
454 static void MENU_InitSysMenuPopup( HMENU hmenu
, DWORD style
, DWORD clsStyle
)
458 gray
= !(style
& WS_THICKFRAME
) || (style
& (WS_MAXIMIZE
| WS_MINIMIZE
));
459 EnableMenuItem( hmenu
, SC_SIZE
, (gray
? MF_GRAYED
: MF_ENABLED
) );
460 gray
= ((style
& WS_MAXIMIZE
) != 0);
461 EnableMenuItem( hmenu
, SC_MOVE
, (gray
? MF_GRAYED
: MF_ENABLED
) );
462 gray
= !(style
& WS_MINIMIZEBOX
) || (style
& WS_MINIMIZE
);
463 EnableMenuItem( hmenu
, SC_MINIMIZE
, (gray
? MF_GRAYED
: MF_ENABLED
) );
464 gray
= !(style
& WS_MAXIMIZEBOX
) || (style
& WS_MAXIMIZE
);
465 EnableMenuItem( hmenu
, SC_MAXIMIZE
, (gray
? MF_GRAYED
: MF_ENABLED
) );
466 gray
= !(style
& (WS_MAXIMIZE
| WS_MINIMIZE
));
467 EnableMenuItem( hmenu
, SC_RESTORE
, (gray
? MF_GRAYED
: MF_ENABLED
) );
468 gray
= (clsStyle
& CS_NOCLOSE
) != 0;
470 /* The menu item must keep its state if it's disabled */
472 EnableMenuItem( hmenu
, SC_CLOSE
, MF_GRAYED
);
476 /******************************************************************************
478 * UINT MENU_GetStartOfNextColumn(
481 *****************************************************************************/
483 static UINT
MENU_GetStartOfNextColumn(
486 POPUPMENU
*menu
= MENU_GetMenu(hMenu
);
490 return NO_SELECTED_ITEM
;
492 i
= menu
->FocusedItem
+ 1;
493 if( i
== NO_SELECTED_ITEM
)
496 for( ; i
< menu
->nItems
; ++i
) {
497 if (menu
->items
[i
].fType
& MF_MENUBARBREAK
)
501 return NO_SELECTED_ITEM
;
505 /******************************************************************************
507 * UINT MENU_GetStartOfPrevColumn(
510 *****************************************************************************/
512 static UINT
MENU_GetStartOfPrevColumn(
515 POPUPMENU
*menu
= MENU_GetMenu(hMenu
);
519 return NO_SELECTED_ITEM
;
521 if( menu
->FocusedItem
== 0 || menu
->FocusedItem
== NO_SELECTED_ITEM
)
522 return NO_SELECTED_ITEM
;
524 /* Find the start of the column */
526 for(i
= menu
->FocusedItem
; i
!= 0 &&
527 !(menu
->items
[i
].fType
& MF_MENUBARBREAK
);
531 return NO_SELECTED_ITEM
;
533 for(--i
; i
!= 0; --i
) {
534 if (menu
->items
[i
].fType
& MF_MENUBARBREAK
)
538 TRACE("ret %d.\n", i
);
545 /***********************************************************************
548 * Find a menu item. Return a pointer on the item, and modifies *hmenu
549 * in case the item was in a sub-menu.
551 static MENUITEM
*MENU_FindItem( HMENU
*hmenu
, UINT
*nPos
, UINT wFlags
)
556 if ((*hmenu
== (HMENU
)0xffff) || (!(menu
= MENU_GetMenu(*hmenu
)))) return NULL
;
557 if (wFlags
& MF_BYPOSITION
)
559 if (*nPos
>= menu
->nItems
) return NULL
;
560 return &menu
->items
[*nPos
];
564 MENUITEM
*item
= menu
->items
;
565 for (i
= 0; i
< menu
->nItems
; i
++, item
++)
567 if (item
->wID
== *nPos
)
572 else if (item
->fType
& MF_POPUP
)
574 HMENU hsubmenu
= item
->hSubMenu
;
575 MENUITEM
*subitem
= MENU_FindItem( &hsubmenu
, nPos
, wFlags
);
587 /***********************************************************************
590 * Find a Sub menu. Return the position of the submenu, and modifies
591 * *hmenu in case it is found in another sub-menu.
592 * If the submenu cannot be found, NO_SELECTED_ITEM is returned.
594 UINT
MENU_FindSubMenu( HMENU
*hmenu
, HMENU hSubTarget
)
599 if (((*hmenu
)==(HMENU
)0xffff) ||
600 (!(menu
= MENU_GetMenu(*hmenu
))))
601 return NO_SELECTED_ITEM
;
603 for (i
= 0; i
< menu
->nItems
; i
++, item
++) {
604 if(!(item
->fType
& MF_POPUP
)) continue;
605 if (item
->hSubMenu
== hSubTarget
) {
609 HMENU hsubmenu
= item
->hSubMenu
;
610 UINT pos
= MENU_FindSubMenu( &hsubmenu
, hSubTarget
);
611 if (pos
!= NO_SELECTED_ITEM
) {
617 return NO_SELECTED_ITEM
;
620 /***********************************************************************
623 static void MENU_FreeItemData( MENUITEM
* item
)
626 if (IS_STRING_ITEM(item
->fType
) && item
->text
)
627 HeapFree( GetProcessHeap(), 0, item
->text
);
630 /***********************************************************************
631 * MENU_FindItemByCoords
633 * Find the item at the specified coordinates (screen coords). Does
634 * not work for child windows and therefore should not be called for
635 * an arbitrary system menu.
637 static MENUITEM
*MENU_FindItemByCoords( POPUPMENU
*menu
,
638 POINT pt
, UINT
*pos
)
644 if (!GetWindowRect(menu
->hWnd
,&wrect
)) return NULL
;
645 pt
.x
-= wrect
.left
;pt
.y
-= wrect
.top
;
647 for (i
= 0; i
< menu
->nItems
; i
++, item
++)
649 if ((pt
.x
>= item
->rect
.left
) && (pt
.x
< item
->rect
.right
) &&
650 (pt
.y
>= item
->rect
.top
) && (pt
.y
< item
->rect
.bottom
))
660 /***********************************************************************
663 * Find the menu item selected by a key press.
664 * Return item id, -1 if none, -2 if we should close the menu.
666 static UINT
MENU_FindItemByKey( HWND hwndOwner
, HMENU hmenu
,
667 WCHAR key
, BOOL forceMenuChar
)
669 TRACE("\tlooking for '%c' (0x%02x) in [%p]\n", (char)key
, key
, hmenu
);
671 if (!IsMenu( hmenu
)) hmenu
= GetSubMenu( get_win_sys_menu(hwndOwner
), 0);
675 POPUPMENU
*menu
= MENU_GetMenu( hmenu
);
676 MENUITEM
*item
= menu
->items
;
684 for (i
= 0; i
< menu
->nItems
; i
++, item
++)
686 if (IS_STRING_ITEM(item
->fType
) && item
->text
)
688 WCHAR
*p
= item
->text
- 2;
691 p
= strchrW (p
+ 2, '&');
693 while (p
!= NULL
&& p
[1] == '&');
694 if (p
&& (toupperW(p
[1]) == key
)) return i
;
698 menuchar
= SendMessageW( hwndOwner
, WM_MENUCHAR
,
699 MAKEWPARAM( key
, menu
->wFlags
), (LPARAM
)hmenu
);
700 if (HIWORD(menuchar
) == 2) return LOWORD(menuchar
);
701 if (HIWORD(menuchar
) == 1) return (UINT
)(-2);
707 /***********************************************************************
708 * MENU_GetBitmapItemSize
710 * Get the size of a bitmap item.
712 static void MENU_GetBitmapItemSize( UINT id
, DWORD data
, SIZE
*size
)
715 HBITMAP bmp
= (HBITMAP
)id
;
717 size
->cx
= size
->cy
= 0;
719 /* check if there is a magic menu item associated with this item */
720 if (id
&& IS_MAGIC_ITEM( id
))
724 case (INT_PTR
)HBMMENU_SYSTEM
:
731 case (INT_PTR
)HBMMENU_MBAR_RESTORE
:
732 case (INT_PTR
)HBMMENU_MBAR_MINIMIZE
:
733 case (INT_PTR
)HBMMENU_MBAR_MINIMIZE_D
:
734 case (INT_PTR
)HBMMENU_MBAR_CLOSE
:
735 case (INT_PTR
)HBMMENU_MBAR_CLOSE_D
:
736 size
->cx
= GetSystemMetrics( SM_CXSIZE
);
737 size
->cy
= GetSystemMetrics( SM_CYSIZE
);
739 case (INT_PTR
)HBMMENU_CALLBACK
:
740 case (INT_PTR
)HBMMENU_POPUP_CLOSE
:
741 case (INT_PTR
)HBMMENU_POPUP_RESTORE
:
742 case (INT_PTR
)HBMMENU_POPUP_MAXIMIZE
:
743 case (INT_PTR
)HBMMENU_POPUP_MINIMIZE
:
745 FIXME("Magic 0x%08x not implemented\n", id
);
749 if (GetObjectW(bmp
, sizeof(bm
), &bm
))
751 size
->cx
= bm
.bmWidth
;
752 size
->cy
= bm
.bmHeight
;
756 /***********************************************************************
757 * MENU_DrawBitmapItem
759 * Draw a bitmap item.
761 static void MENU_DrawBitmapItem( HDC hdc
, MENUITEM
*lpitem
, const RECT
*rect
, BOOL menuBar
)
766 HBITMAP bmp
= (HBITMAP
)lpitem
->text
;
767 int w
= rect
->right
- rect
->left
;
768 int h
= rect
->bottom
- rect
->top
;
772 /* Check if there is a magic menu item associated with this item */
773 if (lpitem
->text
&& IS_MAGIC_ITEM(lpitem
->text
))
778 switch(LOWORD(lpitem
->text
))
780 case (INT_PTR
)HBMMENU_SYSTEM
:
781 if (lpitem
->dwItemData
)
783 bmp
= (HBITMAP
)lpitem
->dwItemData
;
784 if (!GetObjectW( bmp
, sizeof(bm
), &bm
)) return;
789 if (!GetObjectW( bmp
, sizeof(bm
), &bm
)) return;
790 /* only use right half of the bitmap */
791 bmp_xoffset
= bm
.bmWidth
/ 2;
792 bm
.bmWidth
-= bmp_xoffset
;
795 case (INT_PTR
)HBMMENU_MBAR_RESTORE
:
796 flags
= DFCS_CAPTIONRESTORE
;
798 case (INT_PTR
)HBMMENU_MBAR_MINIMIZE
:
799 flags
= DFCS_CAPTIONMIN
;
801 case (INT_PTR
)HBMMENU_MBAR_MINIMIZE_D
:
802 flags
= DFCS_CAPTIONMIN
| DFCS_INACTIVE
;
804 case (INT_PTR
)HBMMENU_MBAR_CLOSE
:
805 flags
= DFCS_CAPTIONCLOSE
;
807 case (INT_PTR
)HBMMENU_MBAR_CLOSE_D
:
808 flags
= DFCS_CAPTIONCLOSE
| DFCS_INACTIVE
;
810 case (INT_PTR
)HBMMENU_CALLBACK
:
811 case (INT_PTR
)HBMMENU_POPUP_CLOSE
:
812 case (INT_PTR
)HBMMENU_POPUP_RESTORE
:
813 case (INT_PTR
)HBMMENU_POPUP_MAXIMIZE
:
814 case (INT_PTR
)HBMMENU_POPUP_MINIMIZE
:
816 FIXME("Magic 0x%08x not implemented\n", LOWORD(lpitem
->text
));
820 InflateRect( &r
, -1, -1 );
821 if (lpitem
->fState
& MF_HILITE
) flags
|= DFCS_PUSHED
;
822 DrawFrameControl( hdc
, &r
, DFC_CAPTION
, flags
);
826 if (!bmp
|| !GetObjectW( bmp
, sizeof(bm
), &bm
)) return;
829 hdcMem
= CreateCompatibleDC( hdc
);
830 SelectObject( hdcMem
, bmp
);
832 /* handle fontsize > bitmap_height */
833 top
= (h
>bm
.bmHeight
) ? rect
->top
+(h
-bm
.bmHeight
)/2 : rect
->top
;
835 if (TWEAK_WineLook
== WIN95_LOOK
) {
836 rop
=((lpitem
->fState
& MF_HILITE
) && !IS_MAGIC_ITEM(lpitem
->text
)) ? NOTSRCCOPY
: SRCCOPY
;
837 if ((lpitem
->fState
& MF_HILITE
) && IS_BITMAP_ITEM(lpitem
->fType
))
838 SetBkColor(hdc
, GetSysColor(COLOR_HIGHLIGHT
));
842 rop
=((lpitem
->fState
& MF_HILITE
) && !IS_MAGIC_ITEM(lpitem
->text
) && (!menuBar
)) ? MERGEPAINT
: SRCCOPY
;
844 BitBlt( hdc
, left
, top
, w
, h
, hdcMem
, bmp_xoffset
, 0, rop
);
849 /***********************************************************************
852 * Calculate the size of the menu item and store it in lpitem->rect.
854 static void MENU_CalcItemSize( HDC hdc
, MENUITEM
*lpitem
, HWND hwndOwner
,
855 INT orgX
, INT orgY
, BOOL menuBar
)
858 UINT check_bitmap_width
= GetSystemMetrics( SM_CXMENUCHECK
);
860 TRACE("dc=%p owner=%p (%d,%d)\n", hdc
, hwndOwner
, orgX
, orgY
);
861 debug_print_menuitem("MENU_CalcItemSize: menuitem:", lpitem
,
862 (menuBar
? " (MenuBar)" : ""));
864 SetRect( &lpitem
->rect
, orgX
, orgY
, orgX
, orgY
);
866 if (lpitem
->fType
& MF_OWNERDRAW
)
869 ** Experimentation under Windows reveals that an owner-drawn
870 ** menu is expected to return the size of the content part of
871 ** the menu item, not including the checkmark nor the submenu
872 ** arrow. Windows adds those values itself and returns the
873 ** enlarged rectangle on subsequent WM_DRAWITEM messages.
875 MEASUREITEMSTRUCT mis
;
876 mis
.CtlType
= ODT_MENU
;
878 mis
.itemID
= lpitem
->wID
;
879 mis
.itemData
= (DWORD
)lpitem
->dwItemData
;
882 SendMessageW( hwndOwner
, WM_MEASUREITEM
, 0, (LPARAM
)&mis
);
883 lpitem
->rect
.right
+= mis
.itemWidth
;
887 lpitem
->rect
.right
+= MENU_BAR_ITEMS_SPACE
;
890 /* under at least win95 you seem to be given a standard
891 height for the menu and the height value is ignored */
893 if (TWEAK_WineLook
== WIN31_LOOK
)
894 lpitem
->rect
.bottom
+= GetSystemMetrics(SM_CYMENU
);
896 lpitem
->rect
.bottom
+= GetSystemMetrics(SM_CYMENU
)-1;
899 lpitem
->rect
.bottom
+= mis
.itemHeight
;
901 TRACE("id=%04x size=%dx%d\n",
902 lpitem
->wID
, mis
.itemWidth
, mis
.itemHeight
);
903 /* Fall through to get check/arrow width calculation. */
906 if (lpitem
->fType
& MF_SEPARATOR
)
908 lpitem
->rect
.bottom
+= SEPARATOR_HEIGHT
;
914 lpitem
->rect
.right
+= 2 * check_bitmap_width
;
915 if (lpitem
->fType
& MF_POPUP
)
916 lpitem
->rect
.right
+= arrow_bitmap_width
;
919 if (lpitem
->fType
& MF_OWNERDRAW
)
922 if (IS_BITMAP_ITEM(lpitem
->fType
))
926 MENU_GetBitmapItemSize( (int)lpitem
->text
, lpitem
->dwItemData
, &size
);
927 lpitem
->rect
.right
+= size
.cx
;
928 lpitem
->rect
.bottom
+= size
.cy
;
929 if (TWEAK_WineLook
== WIN98_LOOK
)
931 /* Leave space for the sunken border */
932 lpitem
->rect
.right
+= 2;
933 lpitem
->rect
.bottom
+= 2;
938 /* it must be a text item - unless it's the system menu */
939 if (!(lpitem
->fType
& MF_SYSMENU
) && IS_STRING_ITEM( lpitem
->fType
))
942 GetTextExtentPoint32W(hdc
, lpitem
->text
, strlenW(lpitem
->text
), &size
);
944 lpitem
->rect
.right
+= size
.cx
;
945 if (TWEAK_WineLook
== WIN31_LOOK
)
946 lpitem
->rect
.bottom
+= max( size
.cy
, GetSystemMetrics(SM_CYMENU
) );
948 lpitem
->rect
.bottom
+= max(size
.cy
, GetSystemMetrics(SM_CYMENU
)-1);
953 lpitem
->rect
.right
+= MENU_BAR_ITEMS_SPACE
;
955 else if ((p
= strchrW( lpitem
->text
, '\t' )) != NULL
)
957 /* Item contains a tab (only meaningful in popup menus) */
958 GetTextExtentPoint32W(hdc
, lpitem
->text
, (int)(p
- lpitem
->text
) , &size
);
959 lpitem
->xTab
= check_bitmap_width
+ MENU_TAB_SPACE
+ size
.cx
;
960 lpitem
->rect
.right
+= MENU_TAB_SPACE
;
964 if (strchrW( lpitem
->text
, '\b' ))
965 lpitem
->rect
.right
+= MENU_TAB_SPACE
;
966 lpitem
->xTab
= lpitem
->rect
.right
- check_bitmap_width
967 - arrow_bitmap_width
;
970 TRACE("(%ld,%ld)-(%ld,%ld)\n", lpitem
->rect
.left
, lpitem
->rect
.top
, lpitem
->rect
.right
, lpitem
->rect
.bottom
);
974 /***********************************************************************
975 * MENU_PopupMenuCalcSize
977 * Calculate the size of a popup menu.
979 static void MENU_PopupMenuCalcSize( LPPOPUPMENU lppop
, HWND hwndOwner
)
984 int orgX
, orgY
, maxX
, maxTab
, maxTabWidth
;
986 lppop
->Width
= lppop
->Height
= 0;
987 if (lppop
->nItems
== 0) return;
990 SelectObject( hdc
, hMenuFont
);
993 maxX
= (TWEAK_WineLook
== WIN31_LOOK
) ? GetSystemMetrics(SM_CXBORDER
) : 2+1 ;
995 while (start
< lppop
->nItems
)
997 lpitem
= &lppop
->items
[start
];
999 orgY
= (TWEAK_WineLook
== WIN31_LOOK
) ? GetSystemMetrics(SM_CYBORDER
) : 2;
1001 maxTab
= maxTabWidth
= 0;
1003 /* Parse items until column break or end of menu */
1004 for (i
= start
; i
< lppop
->nItems
; i
++, lpitem
++)
1007 (lpitem
->fType
& (MF_MENUBREAK
| MF_MENUBARBREAK
))) break;
1009 MENU_CalcItemSize( hdc
, lpitem
, hwndOwner
, orgX
, orgY
, FALSE
);
1011 if (lpitem
->fType
& MF_MENUBARBREAK
) orgX
++;
1012 maxX
= max( maxX
, lpitem
->rect
.right
);
1013 orgY
= lpitem
->rect
.bottom
;
1014 if (IS_STRING_ITEM(lpitem
->fType
) && lpitem
->xTab
)
1016 maxTab
= max( maxTab
, lpitem
->xTab
);
1017 maxTabWidth
= max(maxTabWidth
,lpitem
->rect
.right
-lpitem
->xTab
);
1021 /* Finish the column (set all items to the largest width found) */
1022 maxX
= max( maxX
, maxTab
+ maxTabWidth
);
1023 for (lpitem
= &lppop
->items
[start
]; start
< i
; start
++, lpitem
++)
1025 lpitem
->rect
.right
= maxX
;
1026 if (IS_STRING_ITEM(lpitem
->fType
) && lpitem
->xTab
)
1027 lpitem
->xTab
= maxTab
;
1030 lppop
->Height
= max( lppop
->Height
, orgY
);
1033 lppop
->Width
= maxX
;
1035 /* space for 3d border */
1036 if(TWEAK_WineLook
> WIN31_LOOK
)
1042 ReleaseDC( 0, hdc
);
1046 /***********************************************************************
1047 * MENU_MenuBarCalcSize
1049 * FIXME: Word 6 implements its own MDI and its own 'close window' bitmap
1050 * height is off by 1 pixel which causes lengthy window relocations when
1051 * active document window is maximized/restored.
1053 * Calculate the size of the menu bar.
1055 static void MENU_MenuBarCalcSize( HDC hdc
, LPRECT lprect
,
1056 LPPOPUPMENU lppop
, HWND hwndOwner
)
1059 int start
, i
, orgX
, orgY
, maxY
, helpPos
;
1061 if ((lprect
== NULL
) || (lppop
== NULL
)) return;
1062 if (lppop
->nItems
== 0) return;
1063 TRACE("left=%ld top=%ld right=%ld bottom=%ld\n",
1064 lprect
->left
, lprect
->top
, lprect
->right
, lprect
->bottom
);
1065 lppop
->Width
= lprect
->right
- lprect
->left
;
1067 maxY
= lprect
->top
+1;
1070 while (start
< lppop
->nItems
)
1072 lpitem
= &lppop
->items
[start
];
1073 orgX
= lprect
->left
;
1076 /* Parse items until line break or end of menu */
1077 for (i
= start
; i
< lppop
->nItems
; i
++, lpitem
++)
1079 if ((helpPos
== -1) && (lpitem
->fType
& MF_RIGHTJUSTIFY
)) helpPos
= i
;
1081 (lpitem
->fType
& (MF_MENUBREAK
| MF_MENUBARBREAK
))) break;
1083 TRACE("calling MENU_CalcItemSize org=(%d, %d)\n",
1085 debug_print_menuitem (" item: ", lpitem
, "");
1086 MENU_CalcItemSize( hdc
, lpitem
, hwndOwner
, orgX
, orgY
, TRUE
);
1088 if (lpitem
->rect
.right
> lprect
->right
)
1090 if (i
!= start
) break;
1091 else lpitem
->rect
.right
= lprect
->right
;
1093 maxY
= max( maxY
, lpitem
->rect
.bottom
);
1094 orgX
= lpitem
->rect
.right
;
1097 /* Finish the line (set all items to the largest height found) */
1098 while (start
< i
) lppop
->items
[start
++].rect
.bottom
= maxY
;
1101 lprect
->bottom
= maxY
;
1102 lppop
->Height
= lprect
->bottom
- lprect
->top
;
1104 /* Flush right all items between the MF_RIGHTJUSTIFY and */
1105 /* the last item (if several lines, only move the last line) */
1106 lpitem
= &lppop
->items
[lppop
->nItems
-1];
1107 orgY
= lpitem
->rect
.top
;
1108 orgX
= lprect
->right
;
1109 for (i
= lppop
->nItems
- 1; i
>= helpPos
; i
--, lpitem
--) {
1110 if ( (helpPos
==-1) || (helpPos
>i
) )
1112 if (lpitem
->rect
.top
!= orgY
) break; /* Other line */
1113 if (lpitem
->rect
.right
>= orgX
) break; /* Too far right already */
1114 lpitem
->rect
.left
+= orgX
- lpitem
->rect
.right
;
1115 lpitem
->rect
.right
= orgX
;
1116 orgX
= lpitem
->rect
.left
;
1120 /***********************************************************************
1123 * Draw a single menu item.
1125 static void MENU_DrawMenuItem( HWND hwnd
, HMENU hmenu
, HWND hwndOwner
, HDC hdc
, MENUITEM
*lpitem
,
1126 UINT height
, BOOL menuBar
, UINT odaction
)
1130 debug_print_menuitem("MENU_DrawMenuItem: ", lpitem
, "");
1132 if (lpitem
->fType
& MF_SYSMENU
)
1134 if( !IsIconic(hwnd
) ) {
1135 if (TWEAK_WineLook
> WIN31_LOOK
)
1136 NC_DrawSysButton95( hwnd
, hdc
,
1138 (MF_HILITE
| MF_MOUSESELECT
) );
1140 NC_DrawSysButton( hwnd
, hdc
,
1142 (MF_HILITE
| MF_MOUSESELECT
) );
1148 if (lpitem
->fType
& MF_OWNERDRAW
)
1151 ** Experimentation under Windows reveals that an owner-drawn
1152 ** menu is given the rectangle which includes the space it requested
1153 ** in its response to WM_MEASUREITEM _plus_ width for a checkmark
1154 ** and a popup-menu arrow. This is the value of lpitem->rect.
1155 ** Windows will leave all drawing to the application except for
1156 ** the popup-menu arrow. Windows always draws that itself, after
1157 ** the menu owner has finished drawing.
1161 dis
.CtlType
= ODT_MENU
;
1163 dis
.itemID
= lpitem
->wID
;
1164 dis
.itemData
= (DWORD
)lpitem
->dwItemData
;
1166 if (lpitem
->fState
& MF_CHECKED
) dis
.itemState
|= ODS_CHECKED
;
1167 if (lpitem
->fState
& MF_GRAYED
) dis
.itemState
|= ODS_GRAYED
|ODS_DISABLED
;
1168 if (lpitem
->fState
& MF_HILITE
) dis
.itemState
|= ODS_SELECTED
;
1169 dis
.itemAction
= odaction
; /* ODA_DRAWENTIRE | ODA_SELECT | ODA_FOCUS; */
1170 dis
.hwndItem
= (HWND
)hmenu
;
1172 dis
.rcItem
= lpitem
->rect
;
1173 TRACE("Ownerdraw: owner=%p itemID=%d, itemState=%d, itemAction=%d, "
1174 "hwndItem=%p, hdc=%p, rcItem={%ld,%ld,%ld,%ld}\n", hwndOwner
,
1175 dis
.itemID
, dis
.itemState
, dis
.itemAction
, dis
.hwndItem
,
1176 dis
.hDC
, dis
.rcItem
.left
, dis
.rcItem
.top
, dis
.rcItem
.right
,
1178 SendMessageW( hwndOwner
, WM_DRAWITEM
, 0, (LPARAM
)&dis
);
1179 /* Fall through to draw popup-menu arrow */
1182 TRACE("rect={%ld,%ld,%ld,%ld}\n", lpitem
->rect
.left
, lpitem
->rect
.top
,
1183 lpitem
->rect
.right
,lpitem
->rect
.bottom
);
1185 if (menuBar
&& (lpitem
->fType
& MF_SEPARATOR
)) return;
1187 rect
= lpitem
->rect
;
1189 if (!(lpitem
->fType
& MF_OWNERDRAW
))
1191 if (lpitem
->fState
& MF_HILITE
)
1193 if(TWEAK_WineLook
== WIN98_LOOK
)
1196 DrawEdge(hdc
, &rect
, BDR_SUNKENOUTER
, BF_RECT
);
1198 FillRect(hdc
, &rect
, GetSysColorBrush(COLOR_HIGHLIGHT
));
1200 else /* Not Win98 Look */
1202 if(!IS_BITMAP_ITEM(lpitem
->fType
))
1203 FillRect(hdc
, &rect
, GetSysColorBrush(COLOR_HIGHLIGHT
));
1207 FillRect( hdc
, &rect
, GetSysColorBrush(COLOR_MENU
) );
1210 SetBkMode( hdc
, TRANSPARENT
);
1212 if (!(lpitem
->fType
& MF_OWNERDRAW
))
1214 /* vertical separator */
1215 if (!menuBar
&& (lpitem
->fType
& MF_MENUBARBREAK
))
1217 if (TWEAK_WineLook
> WIN31_LOOK
)
1221 rc
.bottom
= height
- 3;
1222 DrawEdge (hdc
, &rc
, EDGE_ETCHED
, BF_LEFT
);
1226 SelectObject( hdc
, SYSCOLOR_GetPen(COLOR_WINDOWFRAME
) );
1227 MoveToEx( hdc
, rect
.left
, 0, NULL
);
1228 LineTo( hdc
, rect
.left
, height
);
1232 /* horizontal separator */
1233 if (lpitem
->fType
& MF_SEPARATOR
)
1235 if (TWEAK_WineLook
> WIN31_LOOK
)
1240 rc
.top
+= SEPARATOR_HEIGHT
/ 2;
1241 DrawEdge (hdc
, &rc
, EDGE_ETCHED
, BF_TOP
);
1245 SelectObject( hdc
, SYSCOLOR_GetPen(COLOR_WINDOWFRAME
) );
1246 MoveToEx( hdc
, rect
.left
, rect
.top
+ SEPARATOR_HEIGHT
/2, NULL
);
1247 LineTo( hdc
, rect
.right
, rect
.top
+ SEPARATOR_HEIGHT
/2 );
1255 if (lpitem
->fState
& MF_HILITE
)
1257 if(TWEAK_WineLook
== WIN98_LOOK
)
1260 SetTextColor(hdc
, GetSysColor(COLOR_MENUTEXT
));
1261 SetBkColor(hdc
, GetSysColor(COLOR_MENU
));
1263 if(lpitem
->fState
& MF_GRAYED
)
1264 SetTextColor(hdc
, GetSysColor(COLOR_GRAYTEXT
));
1266 SetTextColor(hdc
, GetSysColor(COLOR_HIGHLIGHTTEXT
));
1267 SetBkColor(hdc
, GetSysColor(COLOR_HIGHLIGHT
));
1270 else /* Not Win98 Look */
1272 SetTextColor(hdc
, GetSysColor(COLOR_HIGHLIGHTTEXT
));
1273 if(!IS_BITMAP_ITEM(lpitem
->fType
))
1274 SetBkColor(hdc
, GetSysColor(COLOR_HIGHLIGHT
));
1279 if (lpitem
->fState
& MF_GRAYED
)
1280 SetTextColor( hdc
, GetSysColor( COLOR_GRAYTEXT
) );
1282 SetTextColor( hdc
, GetSysColor( COLOR_MENUTEXT
) );
1283 SetBkColor( hdc
, GetSysColor( COLOR_MENU
) );
1286 /* helper lines for debugging */
1287 /* FrameRect(hdc, &rect, GetStockObject(BLACK_BRUSH));
1288 SelectObject( hdc, SYSCOLOR_GetPen(COLOR_WINDOWFRAME) );
1289 MoveToEx( hdc, rect.left, (rect.top + rect.bottom)/2, NULL );
1290 LineTo( hdc, rect.right, (rect.top + rect.bottom)/2 );
1295 INT y
= rect
.top
+ rect
.bottom
;
1296 UINT check_bitmap_width
= GetSystemMetrics( SM_CXMENUCHECK
);
1297 UINT check_bitmap_height
= GetSystemMetrics( SM_CYMENUCHECK
);
1299 if (!(lpitem
->fType
& MF_OWNERDRAW
))
1301 /* Draw the check mark
1304 * Custom checkmark bitmaps are monochrome but not always 1bpp.
1306 HBITMAP bm
= (lpitem
->fState
& MF_CHECKED
) ? lpitem
->hCheckBit
: lpitem
->hUnCheckBit
;
1307 if (bm
) /* we have a custom bitmap */
1309 HDC hdcMem
= CreateCompatibleDC( hdc
);
1310 SelectObject( hdcMem
, bm
);
1311 BitBlt( hdc
, rect
.left
, (y
- check_bitmap_height
) / 2,
1312 check_bitmap_width
, check_bitmap_height
,
1313 hdcMem
, 0, 0, SRCCOPY
);
1316 else if (lpitem
->fState
& MF_CHECKED
) /* standard bitmaps */
1319 HBITMAP bm
= CreateBitmap( check_bitmap_width
, check_bitmap_height
, 1, 1, NULL
);
1320 HDC hdcMem
= CreateCompatibleDC( hdc
);
1321 SelectObject( hdcMem
, bm
);
1322 SetRect( &r
, 0, 0, check_bitmap_width
, check_bitmap_height
);
1323 DrawFrameControl( hdcMem
, &r
, DFC_MENU
,
1324 (lpitem
->fType
& MFT_RADIOCHECK
) ?
1325 DFCS_MENUBULLET
: DFCS_MENUCHECK
);
1326 BitBlt( hdc
, rect
.left
, (y
- r
.bottom
) / 2, r
.right
, r
.bottom
,
1327 hdcMem
, 0, 0, SRCCOPY
);
1333 /* Draw the popup-menu arrow */
1334 if (lpitem
->fType
& MF_POPUP
)
1336 HDC hdcMem
= CreateCompatibleDC( hdc
);
1337 HBITMAP hOrigBitmap
;
1339 hOrigBitmap
= SelectObject( hdcMem
, hStdMnArrow
);
1340 BitBlt( hdc
, rect
.right
- arrow_bitmap_width
- 1,
1341 (y
- arrow_bitmap_height
) / 2,
1342 arrow_bitmap_width
, arrow_bitmap_height
,
1343 hdcMem
, 0, 0, SRCCOPY
);
1344 SelectObject( hdcMem
, hOrigBitmap
);
1348 rect
.left
+= check_bitmap_width
;
1349 rect
.right
-= arrow_bitmap_width
;
1352 /* Done for owner-drawn */
1353 if (lpitem
->fType
& MF_OWNERDRAW
)
1356 /* Draw the item text or bitmap */
1357 if (IS_BITMAP_ITEM(lpitem
->fType
))
1359 MENU_DrawBitmapItem( hdc
, lpitem
, &rect
, menuBar
);
1363 /* No bitmap - process text if present */
1364 else if (IS_STRING_ITEM(lpitem
->fType
))
1369 UINT uFormat
= (menuBar
) ?
1370 DT_CENTER
| DT_VCENTER
| DT_SINGLELINE
:
1371 DT_LEFT
| DT_VCENTER
| DT_SINGLELINE
;
1373 if ( lpitem
->fState
& MFS_DEFAULT
)
1375 hfontOld
= SelectObject( hdc
, hMenuFontBold
);
1380 rect
.left
+= MENU_BAR_ITEMS_SPACE
/ 2;
1381 rect
.right
-= MENU_BAR_ITEMS_SPACE
/ 2;
1384 for (i
= 0; lpitem
->text
[i
]; i
++)
1385 if ((lpitem
->text
[i
] == '\t') || (lpitem
->text
[i
] == '\b'))
1388 if( (TWEAK_WineLook
!= WIN31_LOOK
) && (lpitem
->fState
& MF_GRAYED
))
1390 if (!(lpitem
->fState
& MF_HILITE
) )
1392 ++rect
.left
; ++rect
.top
; ++rect
.right
; ++rect
.bottom
;
1393 SetTextColor(hdc
, RGB(0xff, 0xff, 0xff));
1394 DrawTextW( hdc
, lpitem
->text
, i
, &rect
, uFormat
);
1395 --rect
.left
; --rect
.top
; --rect
.right
; --rect
.bottom
;
1397 SetTextColor(hdc
, RGB(0x80, 0x80, 0x80));
1400 DrawTextW( hdc
, lpitem
->text
, i
, &rect
, uFormat
);
1402 /* paint the shortcut text */
1403 if (!menuBar
&& lpitem
->text
[i
]) /* There's a tab or flush-right char */
1405 if (lpitem
->text
[i
] == '\t')
1407 rect
.left
= lpitem
->xTab
;
1408 uFormat
= DT_LEFT
| DT_VCENTER
| DT_SINGLELINE
;
1412 uFormat
= DT_RIGHT
| DT_VCENTER
| DT_SINGLELINE
;
1415 if( (TWEAK_WineLook
!= WIN31_LOOK
) && (lpitem
->fState
& MF_GRAYED
))
1417 if (!(lpitem
->fState
& MF_HILITE
) )
1419 ++rect
.left
; ++rect
.top
; ++rect
.right
; ++rect
.bottom
;
1420 SetTextColor(hdc
, RGB(0xff, 0xff, 0xff));
1421 DrawTextW( hdc
, lpitem
->text
+ i
+ 1, -1, &rect
, uFormat
);
1422 --rect
.left
; --rect
.top
; --rect
.right
; --rect
.bottom
;
1424 SetTextColor(hdc
, RGB(0x80, 0x80, 0x80));
1426 DrawTextW( hdc
, lpitem
->text
+ i
+ 1, -1, &rect
, uFormat
);
1430 SelectObject (hdc
, hfontOld
);
1435 /***********************************************************************
1436 * MENU_DrawPopupMenu
1438 * Paint a popup menu.
1440 static void MENU_DrawPopupMenu( HWND hwnd
, HDC hdc
, HMENU hmenu
)
1442 HBRUSH hPrevBrush
= 0;
1445 TRACE("wnd=%p dc=%p menu=%p\n", hwnd
, hdc
, hmenu
);
1447 GetClientRect( hwnd
, &rect
);
1449 if(TWEAK_WineLook
== WIN31_LOOK
)
1451 rect
.bottom
-= POPUP_YSHADE
* GetSystemMetrics(SM_CYBORDER
);
1452 rect
.right
-= POPUP_XSHADE
* GetSystemMetrics(SM_CXBORDER
);
1455 if((hPrevBrush
= SelectObject( hdc
, GetSysColorBrush(COLOR_MENU
) ))
1456 && (SelectObject( hdc
, hMenuFont
)))
1460 Rectangle( hdc
, rect
.left
, rect
.top
, rect
.right
, rect
.bottom
);
1462 hPrevPen
= SelectObject( hdc
, GetStockObject( NULL_PEN
) );
1468 /* draw 3-d shade */
1469 if(TWEAK_WineLook
== WIN31_LOOK
) {
1470 SelectObject( hdc
, hShadeBrush
);
1471 SetBkMode( hdc
, TRANSPARENT
);
1472 ropPrev
= SetROP2( hdc
, R2_MASKPEN
);
1474 i
= rect
.right
; /* why SetBrushOrg() doesn't? */
1475 PatBlt( hdc
, i
& 0xfffffffe,
1476 rect
.top
+ POPUP_YSHADE
*GetSystemMetrics(SM_CYBORDER
),
1477 i
%2 + POPUP_XSHADE
*GetSystemMetrics(SM_CXBORDER
),
1478 rect
.bottom
- rect
.top
, 0x00a000c9 );
1480 PatBlt( hdc
, rect
.left
+ POPUP_XSHADE
*GetSystemMetrics(SM_CXBORDER
),
1481 i
& 0xfffffffe,rect
.right
- rect
.left
,
1482 i
%2 + POPUP_YSHADE
*GetSystemMetrics(SM_CYBORDER
), 0x00a000c9 );
1483 SelectObject( hdc
, hPrevPen
);
1484 SelectObject( hdc
, hPrevBrush
);
1485 SetROP2( hdc
, ropPrev
);
1488 DrawEdge (hdc
, &rect
, EDGE_RAISED
, BF_RECT
);
1490 /* draw menu items */
1492 menu
= MENU_GetMenu( hmenu
);
1493 if (menu
&& menu
->nItems
)
1498 for (u
= menu
->nItems
, item
= menu
->items
; u
> 0; u
--, item
++)
1499 MENU_DrawMenuItem( hwnd
, hmenu
, menu
->hwndOwner
, hdc
, item
,
1500 menu
->Height
, FALSE
, ODA_DRAWENTIRE
);
1505 SelectObject( hdc
, hPrevBrush
);
1510 /***********************************************************************
1513 * Paint a menu bar. Returns the height of the menu bar.
1514 * called from [windows/nonclient.c]
1516 UINT
MENU_DrawMenuBar( HDC hDC
, LPRECT lprect
, HWND hwnd
,
1521 HMENU hMenu
= GetMenu(hwnd
);
1523 lppop
= MENU_GetMenu( hMenu
);
1524 if (lppop
== NULL
|| lprect
== NULL
)
1526 return GetSystemMetrics(SM_CYMENU
);
1531 hfontOld
= SelectObject( hDC
, hMenuFont
);
1533 if (lppop
->Height
== 0)
1534 MENU_MenuBarCalcSize(hDC
, lprect
, lppop
, hwnd
);
1536 lprect
->bottom
= lprect
->top
+ lppop
->Height
;
1538 if (hfontOld
) SelectObject( hDC
, hfontOld
);
1539 return lppop
->Height
;
1542 return DrawMenuBarTemp(hwnd
, hDC
, lprect
, hMenu
, NULL
);
1546 /***********************************************************************
1549 * Display a popup menu.
1551 static BOOL
MENU_ShowPopup( HWND hwndOwner
, HMENU hmenu
, UINT id
,
1552 INT x
, INT y
, INT xanchor
, INT yanchor
)
1557 TRACE("owner=%p hmenu=%p id=0x%04x x=0x%04x y=0x%04x xa=0x%04x ya=0x%04x\n",
1558 hwndOwner
, hmenu
, id
, x
, y
, xanchor
, yanchor
);
1560 if (!(menu
= MENU_GetMenu( hmenu
))) return FALSE
;
1561 if (menu
->FocusedItem
!= NO_SELECTED_ITEM
)
1563 menu
->items
[menu
->FocusedItem
].fState
&= ~(MF_HILITE
|MF_MOUSESELECT
);
1564 menu
->FocusedItem
= NO_SELECTED_ITEM
;
1567 /* store the owner for DrawItem */
1568 menu
->hwndOwner
= hwndOwner
;
1571 MENU_PopupMenuCalcSize( menu
, hwndOwner
);
1573 /* adjust popup menu pos so that it fits within the desktop */
1575 width
= menu
->Width
+ GetSystemMetrics(SM_CXBORDER
);
1576 height
= menu
->Height
+ GetSystemMetrics(SM_CYBORDER
);
1578 if( x
+ width
> GetSystemMetrics(SM_CXSCREEN
))
1581 x
-= width
- xanchor
;
1582 if( x
+ width
> GetSystemMetrics(SM_CXSCREEN
))
1583 x
= GetSystemMetrics(SM_CXSCREEN
) - width
;
1587 if( y
+ height
> GetSystemMetrics(SM_CYSCREEN
))
1590 y
-= height
+ yanchor
;
1591 if( y
+ height
> GetSystemMetrics(SM_CYSCREEN
))
1592 y
= GetSystemMetrics(SM_CYSCREEN
) - height
;
1596 if( TWEAK_WineLook
== WIN31_LOOK
)
1598 width
+= POPUP_XSHADE
* GetSystemMetrics(SM_CXBORDER
); /* add space for shading */
1599 height
+= POPUP_YSHADE
* GetSystemMetrics(SM_CYBORDER
);
1602 /* NOTE: In Windows, top menu popup is not owned. */
1603 menu
->hWnd
= CreateWindowExW( 0, POPUPMENU_CLASS_ATOMW
, NULL
,
1604 WS_POPUP
, x
, y
, width
, height
,
1605 hwndOwner
, 0, (HINSTANCE
)GetWindowLongW(hwndOwner
, GWL_HINSTANCE
),
1607 if( !menu
->hWnd
) return FALSE
;
1608 if (!top_popup
) top_popup
= menu
->hWnd
;
1610 /* Display the window */
1612 SetWindowPos( menu
->hWnd
, HWND_TOP
, 0, 0, 0, 0,
1613 SWP_SHOWWINDOW
| SWP_NOSIZE
| SWP_NOMOVE
| SWP_NOACTIVATE
);
1614 UpdateWindow( menu
->hWnd
);
1619 /***********************************************************************
1622 static void MENU_SelectItem( HWND hwndOwner
, HMENU hmenu
, UINT wIndex
,
1623 BOOL sendMenuSelect
, HMENU topmenu
)
1628 TRACE("owner=%p menu=%p index=0x%04x select=0x%04x\n", hwndOwner
, hmenu
, wIndex
, sendMenuSelect
);
1630 lppop
= MENU_GetMenu( hmenu
);
1631 if ((!lppop
) || (!lppop
->nItems
) || (!lppop
->hWnd
)) return;
1633 if (lppop
->FocusedItem
== wIndex
) return;
1634 if (lppop
->wFlags
& MF_POPUP
) hdc
= GetDC( lppop
->hWnd
);
1635 else hdc
= GetDCEx( lppop
->hWnd
, 0, DCX_CACHE
| DCX_WINDOW
);
1636 if (!top_popup
) top_popup
= lppop
->hWnd
;
1638 SelectObject( hdc
, hMenuFont
);
1640 /* Clear previous highlighted item */
1641 if (lppop
->FocusedItem
!= NO_SELECTED_ITEM
)
1643 lppop
->items
[lppop
->FocusedItem
].fState
&= ~(MF_HILITE
|MF_MOUSESELECT
);
1644 MENU_DrawMenuItem(lppop
->hWnd
, hmenu
, hwndOwner
, hdc
,&lppop
->items
[lppop
->FocusedItem
],
1645 lppop
->Height
, !(lppop
->wFlags
& MF_POPUP
),
1649 /* Highlight new item (if any) */
1650 lppop
->FocusedItem
= wIndex
;
1651 if (lppop
->FocusedItem
!= NO_SELECTED_ITEM
)
1653 if(!(lppop
->items
[wIndex
].fType
& MF_SEPARATOR
)) {
1654 lppop
->items
[wIndex
].fState
|= MF_HILITE
;
1655 MENU_DrawMenuItem( lppop
->hWnd
, hmenu
, hwndOwner
, hdc
,
1656 &lppop
->items
[wIndex
], lppop
->Height
,
1657 !(lppop
->wFlags
& MF_POPUP
), ODA_SELECT
);
1661 MENUITEM
*ip
= &lppop
->items
[lppop
->FocusedItem
];
1662 SendMessageW( hwndOwner
, WM_MENUSELECT
,
1663 MAKELONG(ip
->fType
& MF_POPUP
? wIndex
: ip
->wID
,
1664 ip
->fType
| ip
->fState
| MF_MOUSESELECT
|
1665 (lppop
->wFlags
& MF_SYSMENU
)), (LPARAM
)hmenu
);
1668 else if (sendMenuSelect
) {
1671 if((pos
=MENU_FindSubMenu(&topmenu
, hmenu
))!=NO_SELECTED_ITEM
){
1672 POPUPMENU
*ptm
= MENU_GetMenu( topmenu
);
1673 MENUITEM
*ip
= &ptm
->items
[pos
];
1674 SendMessageW( hwndOwner
, WM_MENUSELECT
, MAKELONG(pos
,
1675 ip
->fType
| ip
->fState
| MF_MOUSESELECT
|
1676 (ptm
->wFlags
& MF_SYSMENU
)), (LPARAM
)topmenu
);
1680 ReleaseDC( lppop
->hWnd
, hdc
);
1684 /***********************************************************************
1685 * MENU_MoveSelection
1687 * Moves currently selected item according to the offset parameter.
1688 * If there is no selection then it should select the last item if
1689 * offset is ITEM_PREV or the first item if offset is ITEM_NEXT.
1691 static void MENU_MoveSelection( HWND hwndOwner
, HMENU hmenu
, INT offset
)
1696 TRACE("hwnd=%p hmenu=%p off=0x%04x\n", hwndOwner
, hmenu
, offset
);
1698 menu
= MENU_GetMenu( hmenu
);
1699 if ((!menu
) || (!menu
->items
)) return;
1701 if ( menu
->FocusedItem
!= NO_SELECTED_ITEM
)
1703 if( menu
->nItems
== 1 ) return; else
1704 for (i
= menu
->FocusedItem
+ offset
; i
>= 0 && i
< menu
->nItems
1706 if (!(menu
->items
[i
].fType
& MF_SEPARATOR
))
1708 MENU_SelectItem( hwndOwner
, hmenu
, i
, TRUE
, 0 );
1713 for ( i
= (offset
> 0) ? 0 : menu
->nItems
- 1;
1714 i
>= 0 && i
< menu
->nItems
; i
+= offset
)
1715 if (!(menu
->items
[i
].fType
& MF_SEPARATOR
))
1717 MENU_SelectItem( hwndOwner
, hmenu
, i
, TRUE
, 0 );
1723 /**********************************************************************
1726 * Set an item's flags, id and text ptr. Called by InsertMenu() and
1729 static BOOL
MENU_SetItemData( MENUITEM
*item
, UINT flags
, UINT_PTR id
,
1732 LPWSTR prevText
= IS_STRING_ITEM(item
->fType
) ? item
->text
: NULL
;
1734 debug_print_menuitem("MENU_SetItemData from: ", item
, "");
1735 TRACE("flags=%x str=%p\n", flags
, str
);
1737 if (IS_STRING_ITEM(flags
))
1741 flags
|= MF_SEPARATOR
;
1747 /* Item beginning with a backspace is a help item */
1753 if (!(text
= HeapAlloc( GetProcessHeap(), 0, (strlenW(str
)+1) * sizeof(WCHAR
) )))
1755 strcpyW( text
, str
);
1759 else if (IS_BITMAP_ITEM(flags
))
1760 item
->text
= (LPWSTR
)HBITMAP_32(LOWORD(str
));
1761 else item
->text
= NULL
;
1763 if (flags
& MF_OWNERDRAW
)
1764 item
->dwItemData
= (DWORD
)str
;
1766 item
->dwItemData
= 0;
1768 if ((item
->fType
& MF_POPUP
) && (flags
& MF_POPUP
) && (item
->hSubMenu
!= (HMENU
)id
) )
1769 DestroyMenu( item
->hSubMenu
); /* ModifyMenu() spec */
1771 if (flags
& MF_POPUP
)
1773 POPUPMENU
*menu
= MENU_GetMenu((HMENU
)id
);
1774 if (menu
) menu
->wFlags
|= MF_POPUP
;
1786 if (flags
& MF_POPUP
) item
->hSubMenu
= (HMENU
)id
;
1788 if ((item
->fType
& MF_POPUP
) && !(flags
& MF_POPUP
) )
1789 flags
|= MF_POPUP
; /* keep popup */
1791 item
->fType
= flags
& TYPE_MASK
;
1792 item
->fState
= (flags
& STATE_MASK
) &
1793 ~(MF_HILITE
| MF_MOUSESELECT
| MF_BYPOSITION
);
1796 /* Don't call SetRectEmpty here! */
1799 if (prevText
) HeapFree( GetProcessHeap(), 0, prevText
);
1801 debug_print_menuitem("MENU_SetItemData to : ", item
, "");
1806 /**********************************************************************
1809 * Insert (allocate) a new item into a menu.
1811 static MENUITEM
*MENU_InsertItem( HMENU hMenu
, UINT pos
, UINT flags
)
1816 if (!(menu
= MENU_GetMenu(hMenu
)))
1819 /* Find where to insert new item */
1821 if (flags
& MF_BYPOSITION
) {
1822 if (pos
> menu
->nItems
)
1825 if (!MENU_FindItem( &hMenu
, &pos
, flags
))
1828 if (!(menu
= MENU_GetMenu( hMenu
)))
1833 /* Create new items array */
1835 newItems
= HeapAlloc( GetProcessHeap(), 0, sizeof(MENUITEM
) * (menu
->nItems
+1) );
1838 WARN("allocation failed\n" );
1841 if (menu
->nItems
> 0)
1843 /* Copy the old array into the new one */
1844 if (pos
> 0) memcpy( newItems
, menu
->items
, pos
* sizeof(MENUITEM
) );
1845 if (pos
< menu
->nItems
) memcpy( &newItems
[pos
+1], &menu
->items
[pos
],
1846 (menu
->nItems
-pos
)*sizeof(MENUITEM
) );
1847 HeapFree( GetProcessHeap(), 0, menu
->items
);
1849 menu
->items
= newItems
;
1851 memset( &newItems
[pos
], 0, sizeof(*newItems
) );
1852 menu
->Height
= 0; /* force size recalculate */
1853 return &newItems
[pos
];
1857 /**********************************************************************
1858 * MENU_ParseResource
1860 * Parse a standard menu resource and add items to the menu.
1861 * Return a pointer to the end of the resource.
1863 * NOTE: flags is equivalent to the mtOption field
1865 static LPCSTR
MENU_ParseResource( LPCSTR res
, HMENU hMenu
, BOOL unicode
)
1872 flags
= GET_WORD(res
);
1873 res
+= sizeof(WORD
);
1874 if (!(flags
& MF_POPUP
))
1877 res
+= sizeof(WORD
);
1880 if (!unicode
) res
+= strlen(str
) + 1;
1881 else res
+= (strlenW((LPCWSTR
)str
) + 1) * sizeof(WCHAR
);
1882 if (flags
& MF_POPUP
)
1884 HMENU hSubMenu
= CreatePopupMenu();
1885 if (!hSubMenu
) return NULL
;
1886 if (!(res
= MENU_ParseResource( res
, hSubMenu
, unicode
)))
1888 if (!unicode
) AppendMenuA( hMenu
, flags
, (UINT
)hSubMenu
, str
);
1889 else AppendMenuW( hMenu
, flags
, (UINT
)hSubMenu
, (LPCWSTR
)str
);
1891 else /* Not a popup */
1893 if (!unicode
) AppendMenuA( hMenu
, flags
, id
, *str
? str
: NULL
);
1894 else AppendMenuW( hMenu
, flags
, id
,
1895 *(LPCWSTR
)str
? (LPCWSTR
)str
: NULL
);
1897 } while (!(flags
& MF_END
));
1902 /**********************************************************************
1903 * MENUEX_ParseResource
1905 * Parse an extended menu resource and add items to the menu.
1906 * Return a pointer to the end of the resource.
1908 static LPCSTR
MENUEX_ParseResource( LPCSTR res
, HMENU hMenu
)
1914 mii
.cbSize
= sizeof(mii
);
1915 mii
.fMask
= MIIM_STATE
| MIIM_ID
| MIIM_TYPE
;
1916 mii
.fType
= GET_DWORD(res
);
1917 res
+= sizeof(DWORD
);
1918 mii
.fState
= GET_DWORD(res
);
1919 res
+= sizeof(DWORD
);
1920 mii
.wID
= GET_DWORD(res
);
1921 res
+= sizeof(DWORD
);
1922 resinfo
= GET_WORD(res
); /* FIXME: for 16-bit apps this is a byte. */
1923 res
+= sizeof(WORD
);
1924 /* Align the text on a word boundary. */
1925 res
+= (~((int)res
- 1)) & 1;
1926 mii
.dwTypeData
= (LPWSTR
) res
;
1927 res
+= (1 + strlenW(mii
.dwTypeData
)) * sizeof(WCHAR
);
1928 /* Align the following fields on a dword boundary. */
1929 res
+= (~((int)res
- 1)) & 3;
1931 TRACE("Menu item: [%08x,%08x,%04x,%04x,%s]\n",
1932 mii
.fType
, mii
.fState
, mii
.wID
, resinfo
, debugstr_w(mii
.dwTypeData
));
1934 if (resinfo
& 1) { /* Pop-up? */
1935 /* DWORD helpid = GET_DWORD(res); FIXME: use this. */
1936 res
+= sizeof(DWORD
);
1937 mii
.hSubMenu
= CreatePopupMenu();
1940 if (!(res
= MENUEX_ParseResource(res
, mii
.hSubMenu
))) {
1941 DestroyMenu(mii
.hSubMenu
);
1944 mii
.fMask
|= MIIM_SUBMENU
;
1945 mii
.fType
|= MF_POPUP
;
1947 else if(!*mii
.dwTypeData
&& !(mii
.fType
& MF_SEPARATOR
))
1949 WARN("Converting NULL menu item %04x, type %04x to SEPARATOR\n",
1950 mii
.wID
, mii
.fType
);
1951 mii
.fType
|= MF_SEPARATOR
;
1953 InsertMenuItemW(hMenu
, -1, MF_BYPOSITION
, &mii
);
1954 } while (!(resinfo
& MF_END
));
1959 /***********************************************************************
1962 * Return the handle of the selected sub-popup menu (if any).
1964 static HMENU
MENU_GetSubPopup( HMENU hmenu
)
1969 menu
= MENU_GetMenu( hmenu
);
1971 if ((!menu
) || (menu
->FocusedItem
== NO_SELECTED_ITEM
)) return 0;
1973 item
= &menu
->items
[menu
->FocusedItem
];
1974 if ((item
->fType
& MF_POPUP
) && (item
->fState
& MF_MOUSESELECT
))
1975 return item
->hSubMenu
;
1980 /***********************************************************************
1981 * MENU_HideSubPopups
1983 * Hide the sub-popup menus of this menu.
1985 static void MENU_HideSubPopups( HWND hwndOwner
, HMENU hmenu
,
1986 BOOL sendMenuSelect
)
1988 POPUPMENU
*menu
= MENU_GetMenu( hmenu
);
1990 TRACE("owner=%p hmenu=%p 0x%04x\n", hwndOwner
, hmenu
, sendMenuSelect
);
1992 if (menu
&& top_popup
)
1998 if (menu
->FocusedItem
!= NO_SELECTED_ITEM
)
2000 item
= &menu
->items
[menu
->FocusedItem
];
2001 if (!(item
->fType
& MF_POPUP
) ||
2002 !(item
->fState
& MF_MOUSESELECT
)) return;
2003 item
->fState
&= ~MF_MOUSESELECT
;
2004 hsubmenu
= item
->hSubMenu
;
2007 submenu
= MENU_GetMenu( hsubmenu
);
2008 MENU_HideSubPopups( hwndOwner
, hsubmenu
, FALSE
);
2009 MENU_SelectItem( hwndOwner
, hsubmenu
, NO_SELECTED_ITEM
, sendMenuSelect
, 0 );
2010 DestroyWindow( submenu
->hWnd
);
2016 /***********************************************************************
2019 * Display the sub-menu of the selected item of this menu.
2020 * Return the handle of the submenu, or hmenu if no submenu to display.
2022 static HMENU
MENU_ShowSubPopup( HWND hwndOwner
, HMENU hmenu
,
2023 BOOL selectFirst
, UINT wFlags
)
2030 TRACE("owner=%p hmenu=%p 0x%04x\n", hwndOwner
, hmenu
, selectFirst
);
2032 if (!(menu
= MENU_GetMenu( hmenu
))) return hmenu
;
2034 if (menu
->FocusedItem
== NO_SELECTED_ITEM
) return hmenu
;
2036 item
= &menu
->items
[menu
->FocusedItem
];
2037 if (!(item
->fType
& MF_POPUP
) || (item
->fState
& (MF_GRAYED
| MF_DISABLED
)))
2040 /* message must be sent before using item,
2041 because nearly everything may be changed by the application ! */
2043 /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
2044 if (!(wFlags
& TPM_NONOTIFY
))
2045 SendMessageW( hwndOwner
, WM_INITMENUPOPUP
, (WPARAM
)item
->hSubMenu
,
2046 MAKELONG( menu
->FocusedItem
, IS_SYSTEM_MENU(menu
) ));
2048 item
= &menu
->items
[menu
->FocusedItem
];
2051 /* correct item if modified as a reaction to WM_INITMENUPOPUP message */
2052 if (!(item
->fState
& MF_HILITE
))
2054 if (menu
->wFlags
& MF_POPUP
) hdc
= GetDC( menu
->hWnd
);
2055 else hdc
= GetDCEx( menu
->hWnd
, 0, DCX_CACHE
| DCX_WINDOW
);
2057 SelectObject( hdc
, hMenuFont
);
2059 item
->fState
|= MF_HILITE
;
2060 MENU_DrawMenuItem( menu
->hWnd
, hmenu
, hwndOwner
, hdc
, item
, menu
->Height
, !(menu
->wFlags
& MF_POPUP
), ODA_DRAWENTIRE
);
2061 ReleaseDC( menu
->hWnd
, hdc
);
2063 if (!item
->rect
.top
&& !item
->rect
.left
&& !item
->rect
.bottom
&& !item
->rect
.right
)
2066 item
->fState
|= MF_MOUSESELECT
;
2068 if (IS_SYSTEM_MENU(menu
))
2070 MENU_InitSysMenuPopup(item
->hSubMenu
,
2071 GetWindowLongW( menu
->hWnd
, GWL_STYLE
),
2072 GetClassLongW( menu
->hWnd
, GCL_STYLE
));
2074 NC_GetSysPopupPos( menu
->hWnd
, &rect
);
2075 rect
.top
= rect
.bottom
;
2076 rect
.right
= GetSystemMetrics(SM_CXSIZE
);
2077 rect
.bottom
= GetSystemMetrics(SM_CYSIZE
);
2081 GetWindowRect( menu
->hWnd
, &rect
);
2082 if (menu
->wFlags
& MF_POPUP
)
2084 rect
.left
+= item
->rect
.right
- GetSystemMetrics(SM_CXBORDER
);
2085 rect
.top
+= item
->rect
.top
;
2086 rect
.right
= item
->rect
.left
- item
->rect
.right
+ GetSystemMetrics(SM_CXBORDER
);
2087 rect
.bottom
= item
->rect
.top
- item
->rect
.bottom
;
2091 rect
.left
+= item
->rect
.left
;
2092 rect
.top
+= item
->rect
.bottom
;
2093 rect
.right
= item
->rect
.right
- item
->rect
.left
;
2094 rect
.bottom
= item
->rect
.bottom
- item
->rect
.top
;
2098 MENU_ShowPopup( hwndOwner
, item
->hSubMenu
, menu
->FocusedItem
,
2099 rect
.left
, rect
.top
, rect
.right
, rect
.bottom
);
2101 MENU_MoveSelection( hwndOwner
, item
->hSubMenu
, ITEM_NEXT
);
2102 return item
->hSubMenu
;
2107 /**********************************************************************
2110 BOOL
MENU_IsMenuActive(void)
2112 return (top_popup
!= 0);
2115 /***********************************************************************
2118 * Walks menu chain trying to find a menu pt maps to.
2120 static HMENU
MENU_PtMenu( HMENU hMenu
, POINT pt
)
2122 POPUPMENU
*menu
= MENU_GetMenu( hMenu
);
2123 UINT item
= menu
->FocusedItem
;
2126 /* try subpopup first (if any) */
2127 ret
= (item
!= NO_SELECTED_ITEM
&&
2128 (menu
->items
[item
].fType
& MF_POPUP
) &&
2129 (menu
->items
[item
].fState
& MF_MOUSESELECT
))
2130 ? MENU_PtMenu(menu
->items
[item
].hSubMenu
, pt
) : 0;
2132 if (!ret
) /* check the current window (avoiding WM_HITTEST) */
2134 INT ht
= NC_HandleNCHitTest( menu
->hWnd
, pt
);
2135 if( menu
->wFlags
& MF_POPUP
)
2137 if (ht
!= HTNOWHERE
&& ht
!= HTERROR
) ret
= hMenu
;
2139 else if (ht
== HTSYSMENU
)
2140 ret
= get_win_sys_menu( menu
->hWnd
);
2141 else if (ht
== HTMENU
)
2142 ret
= GetMenu( menu
->hWnd
);
2147 /***********************************************************************
2148 * MENU_ExecFocusedItem
2150 * Execute a menu item (for instance when user pressed Enter).
2151 * Return the wID of the executed item. Otherwise, -1 indicating
2152 * that no menu item was executed;
2153 * Have to receive the flags for the TrackPopupMenu options to avoid
2154 * sending unwanted message.
2157 static INT
MENU_ExecFocusedItem( MTRACKER
* pmt
, HMENU hMenu
, UINT wFlags
)
2160 POPUPMENU
*menu
= MENU_GetMenu( hMenu
);
2162 TRACE("%p hmenu=%p\n", pmt
, hMenu
);
2164 if (!menu
|| !menu
->nItems
||
2165 (menu
->FocusedItem
== NO_SELECTED_ITEM
)) return -1;
2167 item
= &menu
->items
[menu
->FocusedItem
];
2169 TRACE("%p %08x %p\n", hMenu
, item
->wID
, item
->hSubMenu
);
2171 if (!(item
->fType
& MF_POPUP
))
2173 if (!(item
->fState
& (MF_GRAYED
| MF_DISABLED
)) && !(item
->fType
& MF_SEPARATOR
))
2175 /* If TPM_RETURNCMD is set you return the id, but
2176 do not send a message to the owner */
2177 if(!(wFlags
& TPM_RETURNCMD
))
2179 if( menu
->wFlags
& MF_SYSMENU
)
2180 PostMessageW( pmt
->hOwnerWnd
, WM_SYSCOMMAND
, item
->wID
,
2181 MAKELPARAM((INT16
)pmt
->pt
.x
, (INT16
)pmt
->pt
.y
) );
2183 PostMessageW( pmt
->hOwnerWnd
, WM_COMMAND
, item
->wID
, 0 );
2189 pmt
->hCurrentMenu
= MENU_ShowSubPopup(pmt
->hOwnerWnd
, hMenu
, TRUE
, wFlags
);
2194 /***********************************************************************
2195 * MENU_SwitchTracking
2197 * Helper function for menu navigation routines.
2199 static void MENU_SwitchTracking( MTRACKER
* pmt
, HMENU hPtMenu
, UINT id
)
2201 POPUPMENU
*ptmenu
= MENU_GetMenu( hPtMenu
);
2202 POPUPMENU
*topmenu
= MENU_GetMenu( pmt
->hTopMenu
);
2204 TRACE("%p hmenu=%p 0x%04x\n", pmt
, hPtMenu
, id
);
2206 if( pmt
->hTopMenu
!= hPtMenu
&&
2207 !((ptmenu
->wFlags
| topmenu
->wFlags
) & MF_POPUP
) )
2209 /* both are top level menus (system and menu-bar) */
2210 MENU_HideSubPopups( pmt
->hOwnerWnd
, pmt
->hTopMenu
, FALSE
);
2211 MENU_SelectItem( pmt
->hOwnerWnd
, pmt
->hTopMenu
, NO_SELECTED_ITEM
, FALSE
, 0 );
2212 pmt
->hTopMenu
= hPtMenu
;
2214 else MENU_HideSubPopups( pmt
->hOwnerWnd
, hPtMenu
, FALSE
);
2215 MENU_SelectItem( pmt
->hOwnerWnd
, hPtMenu
, id
, TRUE
, 0 );
2219 /***********************************************************************
2222 * Return TRUE if we can go on with menu tracking.
2224 static BOOL
MENU_ButtonDown( MTRACKER
* pmt
, HMENU hPtMenu
, UINT wFlags
)
2226 TRACE("%p hPtMenu=%p\n", pmt
, hPtMenu
);
2231 POPUPMENU
*ptmenu
= MENU_GetMenu( hPtMenu
);
2234 if( IS_SYSTEM_MENU(ptmenu
) )
2235 item
= ptmenu
->items
;
2237 item
= MENU_FindItemByCoords( ptmenu
, pmt
->pt
, &id
);
2241 if( ptmenu
->FocusedItem
!= id
)
2242 MENU_SwitchTracking( pmt
, hPtMenu
, id
);
2244 /* If the popup menu is not already "popped" */
2245 if(!(item
->fState
& MF_MOUSESELECT
))
2247 pmt
->hCurrentMenu
= MENU_ShowSubPopup( pmt
->hOwnerWnd
, hPtMenu
, FALSE
, wFlags
);
2249 /* In win31, a newly popped menu always remains opened for the next buttonup */
2250 if(TWEAK_WineLook
== WIN31_LOOK
)
2251 ptmenu
->bTimeToHide
= FALSE
;
2256 /* Else the click was on the menu bar, finish the tracking */
2261 /***********************************************************************
2264 * Return the value of MENU_ExecFocusedItem if
2265 * the selected item was not a popup. Else open the popup.
2266 * A -1 return value indicates that we go on with menu tracking.
2269 static INT
MENU_ButtonUp( MTRACKER
* pmt
, HMENU hPtMenu
, UINT wFlags
)
2271 TRACE("%p hmenu=%p\n", pmt
, hPtMenu
);
2276 POPUPMENU
*ptmenu
= MENU_GetMenu( hPtMenu
);
2279 if( IS_SYSTEM_MENU(ptmenu
) )
2280 item
= ptmenu
->items
;
2282 item
= MENU_FindItemByCoords( ptmenu
, pmt
->pt
, &id
);
2284 if( item
&& (ptmenu
->FocusedItem
== id
))
2286 if( !(item
->fType
& MF_POPUP
) )
2287 return MENU_ExecFocusedItem( pmt
, hPtMenu
, wFlags
);
2289 /* If we are dealing with the top-level menu */
2290 /* and this is a click on an already "popped" item: */
2291 /* Stop the menu tracking and close the opened submenus */
2292 if((pmt
->hTopMenu
== hPtMenu
) && (ptmenu
->bTimeToHide
== TRUE
))
2295 ptmenu
->bTimeToHide
= TRUE
;
2301 /***********************************************************************
2304 * Return TRUE if we can go on with menu tracking.
2306 static BOOL
MENU_MouseMove( MTRACKER
* pmt
, HMENU hPtMenu
, UINT wFlags
)
2308 UINT id
= NO_SELECTED_ITEM
;
2309 POPUPMENU
*ptmenu
= NULL
;
2313 ptmenu
= MENU_GetMenu( hPtMenu
);
2314 if( IS_SYSTEM_MENU(ptmenu
) )
2317 MENU_FindItemByCoords( ptmenu
, pmt
->pt
, &id
);
2320 if( id
== NO_SELECTED_ITEM
)
2322 MENU_SelectItem( pmt
->hOwnerWnd
, pmt
->hCurrentMenu
,
2323 NO_SELECTED_ITEM
, TRUE
, pmt
->hTopMenu
);
2326 else if( ptmenu
->FocusedItem
!= id
)
2328 MENU_SwitchTracking( pmt
, hPtMenu
, id
);
2329 pmt
->hCurrentMenu
= MENU_ShowSubPopup(pmt
->hOwnerWnd
, hPtMenu
, FALSE
, wFlags
);
2335 /***********************************************************************
2338 static void MENU_SetCapture( HWND hwnd
)
2342 SERVER_START_REQ( set_capture_window
)
2345 req
->flags
= CAPTURE_MENU
;
2346 if (!wine_server_call_err( req
))
2348 previous
= reply
->previous
;
2349 hwnd
= reply
->full_handle
;
2354 if (previous
&& previous
!= hwnd
)
2355 SendMessageW( previous
, WM_CAPTURECHANGED
, 0, (LPARAM
)hwnd
);
2359 /***********************************************************************
2362 * NOTE: WM_NEXTMENU documented in Win32 is a bit different.
2364 static LRESULT
MENU_DoNextMenu( MTRACKER
* pmt
, UINT vk
)
2366 POPUPMENU
*menu
= MENU_GetMenu( pmt
->hTopMenu
);
2368 if( (vk
== VK_LEFT
&& menu
->FocusedItem
== 0 ) ||
2369 (vk
== VK_RIGHT
&& menu
->FocusedItem
== menu
->nItems
- 1))
2371 MDINEXTMENU next_menu
;
2376 next_menu
.hmenuIn
= (IS_SYSTEM_MENU(menu
)) ? GetSubMenu(pmt
->hTopMenu
,0) : pmt
->hTopMenu
;
2377 next_menu
.hmenuNext
= 0;
2378 next_menu
.hwndNext
= 0;
2379 SendMessageW( pmt
->hOwnerWnd
, WM_NEXTMENU
, vk
, (LPARAM
)&next_menu
);
2381 TRACE("%p [%p] -> %p [%p]\n",
2382 pmt
->hCurrentMenu
, pmt
->hOwnerWnd
, next_menu
.hmenuNext
, next_menu
.hwndNext
);
2384 if (!next_menu
.hmenuNext
|| !next_menu
.hwndNext
)
2386 DWORD style
= GetWindowLongW( pmt
->hOwnerWnd
, GWL_STYLE
);
2387 hNewWnd
= pmt
->hOwnerWnd
;
2388 if( IS_SYSTEM_MENU(menu
) )
2390 /* switch to the menu bar */
2392 if(style
& WS_CHILD
|| !(hNewMenu
= GetMenu(hNewWnd
))) return FALSE
;
2396 menu
= MENU_GetMenu( hNewMenu
);
2397 id
= menu
->nItems
- 1;
2400 else if (style
& WS_SYSMENU
)
2402 /* switch to the system menu */
2403 hNewMenu
= get_win_sys_menu( hNewWnd
);
2407 else /* application returned a new menu to switch to */
2409 hNewMenu
= next_menu
.hmenuNext
;
2410 hNewWnd
= WIN_GetFullHandle( next_menu
.hwndNext
);
2412 if( IsMenu(hNewMenu
) && IsWindow(hNewWnd
) )
2414 DWORD style
= GetWindowLongW( hNewWnd
, GWL_STYLE
);
2416 if (style
& WS_SYSMENU
&&
2417 GetSubMenu(get_win_sys_menu(hNewWnd
), 0) == hNewMenu
)
2419 /* get the real system menu */
2420 hNewMenu
= get_win_sys_menu(hNewWnd
);
2422 else if (style
& WS_CHILD
|| GetMenu(hNewWnd
) != hNewMenu
)
2424 /* FIXME: Not sure what to do here;
2425 * perhaps try to track hNewMenu as a popup? */
2427 TRACE(" -- got confused.\n");
2434 if( hNewMenu
!= pmt
->hTopMenu
)
2436 MENU_SelectItem( pmt
->hOwnerWnd
, pmt
->hTopMenu
, NO_SELECTED_ITEM
,
2438 if( pmt
->hCurrentMenu
!= pmt
->hTopMenu
)
2439 MENU_HideSubPopups( pmt
->hOwnerWnd
, pmt
->hTopMenu
, FALSE
);
2442 if( hNewWnd
!= pmt
->hOwnerWnd
)
2444 pmt
->hOwnerWnd
= hNewWnd
;
2445 MENU_SetCapture( pmt
->hOwnerWnd
);
2448 pmt
->hTopMenu
= pmt
->hCurrentMenu
= hNewMenu
; /* all subpopups are hidden */
2449 MENU_SelectItem( pmt
->hOwnerWnd
, pmt
->hTopMenu
, id
, TRUE
, 0 );
2456 /***********************************************************************
2459 * The idea is not to show the popup if the next input message is
2460 * going to hide it anyway.
2462 static BOOL
MENU_SuspendPopup( MTRACKER
* pmt
, UINT16 uMsg
)
2466 msg
.hwnd
= pmt
->hOwnerWnd
;
2468 PeekMessageW( &msg
, 0, 0, 0, PM_NOYIELD
| PM_REMOVE
);
2469 pmt
->trackFlags
|= TF_SKIPREMOVE
;
2474 PeekMessageW( &msg
, 0, 0, 0, PM_NOYIELD
| PM_NOREMOVE
);
2475 if( msg
.message
== WM_KEYUP
|| msg
.message
== WM_PAINT
)
2477 PeekMessageW( &msg
, 0, 0, 0, PM_NOYIELD
| PM_REMOVE
);
2478 PeekMessageW( &msg
, 0, 0, 0, PM_NOYIELD
| PM_NOREMOVE
);
2479 if( msg
.message
== WM_KEYDOWN
&&
2480 (msg
.wParam
== VK_LEFT
|| msg
.wParam
== VK_RIGHT
))
2482 pmt
->trackFlags
|= TF_SUSPENDPOPUP
;
2489 /* failures go through this */
2490 pmt
->trackFlags
&= ~TF_SUSPENDPOPUP
;
2494 /***********************************************************************
2497 * Handle a VK_ESCAPE key event in a menu.
2499 static BOOL
MENU_KeyEscape(MTRACKER
* pmt
, UINT wFlags
)
2501 BOOL bEndMenu
= TRUE
;
2503 if (pmt
->hCurrentMenu
!= pmt
->hTopMenu
)
2505 POPUPMENU
*menu
= MENU_GetMenu(pmt
->hCurrentMenu
);
2507 if (menu
->wFlags
& MF_POPUP
)
2509 HMENU hmenutmp
, hmenuprev
;
2511 hmenuprev
= hmenutmp
= pmt
->hTopMenu
;
2513 /* close topmost popup */
2514 while (hmenutmp
!= pmt
->hCurrentMenu
)
2516 hmenuprev
= hmenutmp
;
2517 hmenutmp
= MENU_GetSubPopup( hmenuprev
);
2520 MENU_HideSubPopups( pmt
->hOwnerWnd
, hmenuprev
, TRUE
);
2521 pmt
->hCurrentMenu
= hmenuprev
;
2529 /***********************************************************************
2532 * Handle a VK_LEFT key event in a menu.
2534 static void MENU_KeyLeft( MTRACKER
* pmt
, UINT wFlags
)
2537 HMENU hmenutmp
, hmenuprev
;
2540 hmenuprev
= hmenutmp
= pmt
->hTopMenu
;
2541 menu
= MENU_GetMenu( hmenutmp
);
2543 /* Try to move 1 column left (if possible) */
2544 if( (prevcol
= MENU_GetStartOfPrevColumn( pmt
->hCurrentMenu
)) !=
2545 NO_SELECTED_ITEM
) {
2547 MENU_SelectItem( pmt
->hOwnerWnd
, pmt
->hCurrentMenu
,
2552 /* close topmost popup */
2553 while (hmenutmp
!= pmt
->hCurrentMenu
)
2555 hmenuprev
= hmenutmp
;
2556 hmenutmp
= MENU_GetSubPopup( hmenuprev
);
2559 MENU_HideSubPopups( pmt
->hOwnerWnd
, hmenuprev
, TRUE
);
2560 pmt
->hCurrentMenu
= hmenuprev
;
2562 if ( (hmenuprev
== pmt
->hTopMenu
) && !(menu
->wFlags
& MF_POPUP
) )
2564 /* move menu bar selection if no more popups are left */
2566 if( !MENU_DoNextMenu( pmt
, VK_LEFT
) )
2567 MENU_MoveSelection( pmt
->hOwnerWnd
, pmt
->hTopMenu
, ITEM_PREV
);
2569 if ( hmenuprev
!= hmenutmp
|| pmt
->trackFlags
& TF_SUSPENDPOPUP
)
2571 /* A sublevel menu was displayed - display the next one
2572 * unless there is another displacement coming up */
2574 if( !MENU_SuspendPopup( pmt
, WM_KEYDOWN
) )
2575 pmt
->hCurrentMenu
= MENU_ShowSubPopup(pmt
->hOwnerWnd
,
2576 pmt
->hTopMenu
, TRUE
, wFlags
);
2582 /***********************************************************************
2585 * Handle a VK_RIGHT key event in a menu.
2587 static void MENU_KeyRight( MTRACKER
* pmt
, UINT wFlags
)
2590 POPUPMENU
*menu
= MENU_GetMenu( pmt
->hTopMenu
);
2593 TRACE("MENU_KeyRight called, cur %p (%s), top %p (%s).\n",
2595 debugstr_w((MENU_GetMenu(pmt
->hCurrentMenu
))->items
[0].text
),
2596 pmt
->hTopMenu
, debugstr_w(menu
->items
[0].text
) );
2598 if ( (menu
->wFlags
& MF_POPUP
) || (pmt
->hCurrentMenu
!= pmt
->hTopMenu
))
2600 /* If already displaying a popup, try to display sub-popup */
2602 hmenutmp
= pmt
->hCurrentMenu
;
2603 pmt
->hCurrentMenu
= MENU_ShowSubPopup(pmt
->hOwnerWnd
, hmenutmp
, TRUE
, wFlags
);
2605 /* if subpopup was displayed then we are done */
2606 if (hmenutmp
!= pmt
->hCurrentMenu
) return;
2609 /* Check to see if there's another column */
2610 if( (nextcol
= MENU_GetStartOfNextColumn( pmt
->hCurrentMenu
)) !=
2611 NO_SELECTED_ITEM
) {
2612 TRACE("Going to %d.\n", nextcol
);
2613 MENU_SelectItem( pmt
->hOwnerWnd
, pmt
->hCurrentMenu
,
2618 if (!(menu
->wFlags
& MF_POPUP
)) /* menu bar tracking */
2620 if( pmt
->hCurrentMenu
!= pmt
->hTopMenu
)
2622 MENU_HideSubPopups( pmt
->hOwnerWnd
, pmt
->hTopMenu
, FALSE
);
2623 hmenutmp
= pmt
->hCurrentMenu
= pmt
->hTopMenu
;
2624 } else hmenutmp
= 0;
2626 /* try to move to the next item */
2627 if( !MENU_DoNextMenu( pmt
, VK_RIGHT
) )
2628 MENU_MoveSelection( pmt
->hOwnerWnd
, pmt
->hTopMenu
, ITEM_NEXT
);
2630 if( hmenutmp
|| pmt
->trackFlags
& TF_SUSPENDPOPUP
)
2631 if( !MENU_SuspendPopup(pmt
, WM_KEYDOWN
) )
2632 pmt
->hCurrentMenu
= MENU_ShowSubPopup(pmt
->hOwnerWnd
,
2633 pmt
->hTopMenu
, TRUE
, wFlags
);
2637 /***********************************************************************
2640 * Menu tracking code.
2642 static INT
MENU_TrackMenu( HMENU hmenu
, UINT wFlags
, INT x
, INT y
,
2643 HWND hwnd
, const RECT
*lprect
)
2648 INT executedMenuId
= -1;
2650 BOOL enterIdleSent
= FALSE
;
2653 mt
.hCurrentMenu
= hmenu
;
2654 mt
.hTopMenu
= hmenu
;
2655 mt
.hOwnerWnd
= WIN_GetFullHandle( hwnd
);
2659 TRACE("hmenu=%p flags=0x%08x (%d,%d) hwnd=%p (%ld,%ld)-(%ld,%ld)\n",
2660 hmenu
, wFlags
, x
, y
, hwnd
, (lprect
) ? lprect
->left
: 0, (lprect
) ? lprect
->top
: 0,
2661 (lprect
) ? lprect
->right
: 0, (lprect
) ? lprect
->bottom
: 0);
2664 if (!(menu
= MENU_GetMenu( hmenu
))) return FALSE
;
2666 if (wFlags
& TPM_BUTTONDOWN
)
2668 /* Get the result in order to start the tracking or not */
2669 fRemove
= MENU_ButtonDown( &mt
, hmenu
, wFlags
);
2670 fEndMenu
= !fRemove
;
2673 MENU_SetCapture( mt
.hOwnerWnd
);
2677 menu
= MENU_GetMenu( mt
.hCurrentMenu
);
2678 if (!menu
) /* sometimes happens if I do a window manager close */
2681 /* we have to keep the message in the queue until it's
2682 * clear that menu loop is not over yet. */
2686 if (PeekMessageW( &msg
, 0, 0, 0, PM_NOREMOVE
))
2688 if (!CallMsgFilterW( &msg
, MSGF_MENU
)) break;
2689 /* remove the message from the queue */
2690 PeekMessageW( &msg
, 0, msg
.message
, msg
.message
, PM_REMOVE
);
2696 HWND win
= (wFlags
& TPM_ENTERIDLEEX
&& menu
->wFlags
& MF_POPUP
) ? menu
->hWnd
: 0;
2697 enterIdleSent
= TRUE
;
2698 SendMessageW( mt
.hOwnerWnd
, WM_ENTERIDLE
, MSGF_MENU
, (LPARAM
)win
);
2704 /* check if EndMenu() tried to cancel us, by posting this message */
2705 if(msg
.message
== WM_CANCELMODE
)
2707 /* we are now out of the loop */
2710 /* remove the message from the queue */
2711 PeekMessageW( &msg
, 0, msg
.message
, msg
.message
, PM_REMOVE
);
2713 /* break out of internal loop, ala ESCAPE */
2717 TranslateMessage( &msg
);
2720 if ( (msg
.hwnd
==menu
->hWnd
) || (msg
.message
!=WM_TIMER
) )
2721 enterIdleSent
=FALSE
;
2724 if ((msg
.message
>= WM_MOUSEFIRST
) && (msg
.message
<= WM_MOUSELAST
))
2727 * Use the mouse coordinates in lParam instead of those in the MSG
2728 * struct to properly handle synthetic messages. They are already
2729 * in screen coordinates.
2731 mt
.pt
.x
= (short)LOWORD(msg
.lParam
);
2732 mt
.pt
.y
= (short)HIWORD(msg
.lParam
);
2734 /* Find a menu for this mouse event */
2735 hmenu
= MENU_PtMenu( mt
.hTopMenu
, mt
.pt
);
2739 /* no WM_NC... messages in captured state */
2741 case WM_RBUTTONDBLCLK
:
2742 case WM_RBUTTONDOWN
:
2743 if (!(wFlags
& TPM_RIGHTBUTTON
)) break;
2745 case WM_LBUTTONDBLCLK
:
2746 case WM_LBUTTONDOWN
:
2747 /* If the message belongs to the menu, removes it from the queue */
2748 /* Else, end menu tracking */
2749 fRemove
= MENU_ButtonDown( &mt
, hmenu
, wFlags
);
2750 fEndMenu
= !fRemove
;
2754 if (!(wFlags
& TPM_RIGHTBUTTON
)) break;
2757 /* Check if a menu was selected by the mouse */
2760 executedMenuId
= MENU_ButtonUp( &mt
, hmenu
, wFlags
);
2762 /* End the loop if executedMenuId is an item ID */
2763 /* or if the job was done (executedMenuId = 0). */
2764 fEndMenu
= fRemove
= (executedMenuId
!= -1);
2766 /* No menu was selected by the mouse */
2767 /* if the function was called by TrackPopupMenu, continue
2768 with the menu tracking. If not, stop it */
2770 fEndMenu
= ((wFlags
& TPM_POPUPMENU
) ? FALSE
: TRUE
);
2775 /* In win95 winelook, the selected menu item must be changed every time the
2776 mouse moves. In Win31 winelook, the mouse button has to be held down */
2778 if ( hmenu
&& ((TWEAK_WineLook
> WIN31_LOOK
) ||
2779 ( (msg
.wParam
& MK_LBUTTON
) ||
2780 ((wFlags
& TPM_RIGHTBUTTON
) && (msg
.wParam
& MK_RBUTTON
)))) )
2782 fEndMenu
|= !MENU_MouseMove( &mt
, hmenu
, wFlags
);
2784 } /* switch(msg.message) - mouse */
2786 else if ((msg
.message
>= WM_KEYFIRST
) && (msg
.message
<= WM_KEYLAST
))
2788 fRemove
= TRUE
; /* Keyboard messages are always removed */
2796 MENU_SelectItem( mt
.hOwnerWnd
, mt
.hCurrentMenu
,
2797 NO_SELECTED_ITEM
, FALSE
, 0 );
2800 MENU_MoveSelection( mt
.hOwnerWnd
, mt
.hCurrentMenu
,
2801 (msg
.wParam
== VK_HOME
)? ITEM_NEXT
: ITEM_PREV
);
2804 case VK_DOWN
: /* If on menu bar, pull-down the menu */
2806 menu
= MENU_GetMenu( mt
.hCurrentMenu
);
2807 if (!(menu
->wFlags
& MF_POPUP
))
2808 mt
.hCurrentMenu
= MENU_ShowSubPopup(mt
.hOwnerWnd
, mt
.hTopMenu
, TRUE
, wFlags
);
2809 else /* otherwise try to move selection */
2810 MENU_MoveSelection( mt
.hOwnerWnd
, mt
.hCurrentMenu
, ITEM_NEXT
);
2814 MENU_KeyLeft( &mt
, wFlags
);
2818 MENU_KeyRight( &mt
, wFlags
);
2822 fEndMenu
= MENU_KeyEscape(&mt
, wFlags
);
2828 hi
.cbSize
= sizeof(HELPINFO
);
2829 hi
.iContextType
= HELPINFO_MENUITEM
;
2830 if (menu
->FocusedItem
== NO_SELECTED_ITEM
)
2833 hi
.iCtrlId
= menu
->items
[menu
->FocusedItem
].wID
;
2834 hi
.hItemHandle
= hmenu
;
2835 hi
.dwContextId
= menu
->dwContextHelpID
;
2836 hi
.MousePos
= msg
.pt
;
2837 SendMessageW(hwnd
, WM_HELP
, 0, (LPARAM
)&hi
);
2844 break; /* WM_KEYDOWN */
2854 break; /* WM_SYSKEYDOWN */
2860 if (msg
.wParam
== '\r' || msg
.wParam
== ' ')
2862 executedMenuId
= MENU_ExecFocusedItem(&mt
,mt
.hCurrentMenu
, wFlags
);
2863 fEndMenu
= (executedMenuId
!= -1);
2868 /* Hack to avoid control chars. */
2869 /* We will find a better way real soon... */
2870 if (msg
.wParam
< 32) break;
2872 pos
= MENU_FindItemByKey( mt
.hOwnerWnd
, mt
.hCurrentMenu
,
2873 LOWORD(msg
.wParam
), FALSE
);
2874 if (pos
== (UINT
)-2) fEndMenu
= TRUE
;
2875 else if (pos
== (UINT
)-1) MessageBeep(0);
2878 MENU_SelectItem( mt
.hOwnerWnd
, mt
.hCurrentMenu
, pos
,
2880 executedMenuId
= MENU_ExecFocusedItem(&mt
,mt
.hCurrentMenu
, wFlags
);
2881 fEndMenu
= (executedMenuId
!= -1);
2885 } /* switch(msg.message) - kbd */
2889 DispatchMessageW( &msg
);
2892 if (!fEndMenu
) fRemove
= TRUE
;
2894 /* finally remove message from the queue */
2896 if (fRemove
&& !(mt
.trackFlags
& TF_SKIPREMOVE
) )
2897 PeekMessageW( &msg
, 0, msg
.message
, msg
.message
, PM_REMOVE
);
2898 else mt
.trackFlags
&= ~TF_SKIPREMOVE
;
2901 MENU_SetCapture(0); /* release the capture */
2903 /* If dropdown is still painted and the close box is clicked on
2904 then the menu will be destroyed as part of the DispatchMessage above.
2905 This will then invalidate the menu handle in mt.hTopMenu. We should
2906 check for this first. */
2907 if( IsMenu( mt
.hTopMenu
) )
2909 menu
= MENU_GetMenu( mt
.hTopMenu
);
2911 if( IsWindow( mt
.hOwnerWnd
) )
2913 MENU_HideSubPopups( mt
.hOwnerWnd
, mt
.hTopMenu
, FALSE
);
2915 if (menu
&& menu
->wFlags
& MF_POPUP
)
2917 DestroyWindow( menu
->hWnd
);
2920 MENU_SelectItem( mt
.hOwnerWnd
, mt
.hTopMenu
, NO_SELECTED_ITEM
, FALSE
, 0 );
2921 SendMessageW( mt
.hOwnerWnd
, WM_MENUSELECT
, MAKELONG(0,0xffff), 0 );
2924 /* Reset the variable for hiding menu */
2925 if( menu
) menu
->bTimeToHide
= FALSE
;
2928 /* The return value is only used by TrackPopupMenu */
2929 return ((executedMenuId
!= -1) ? executedMenuId
: 0);
2932 /***********************************************************************
2935 static BOOL
MENU_InitTracking(HWND hWnd
, HMENU hMenu
, BOOL bPopup
, UINT wFlags
)
2937 TRACE("hwnd=%p hmenu=%p\n", hWnd
, hMenu
);
2941 /* Send WM_ENTERMENULOOP and WM_INITMENU message only if TPM_NONOTIFY flag is not specified */
2942 if (!(wFlags
& TPM_NONOTIFY
))
2943 SendMessageW( hWnd
, WM_ENTERMENULOOP
, bPopup
, 0 );
2945 SendMessageW( hWnd
, WM_SETCURSOR
, (WPARAM
)hWnd
, HTCAPTION
);
2947 if (!(wFlags
& TPM_NONOTIFY
))
2950 SendMessageW( hWnd
, WM_INITMENU
, (WPARAM
)hMenu
, 0 );
2951 if ((menu
= MENU_GetMenu( hMenu
)) && (!menu
->Height
))
2952 { /* app changed/recreated menu bar entries in WM_INITMENU
2953 Recalculate menu sizes else clicks will not work */
2954 SetWindowPos( hWnd
, 0, 0, 0, 0, 0, SWP_NOSIZE
| SWP_NOMOVE
|
2955 SWP_NOACTIVATE
| SWP_NOZORDER
| SWP_FRAMECHANGED
);
2961 /***********************************************************************
2964 static BOOL
MENU_ExitTracking(HWND hWnd
)
2966 TRACE("hwnd=%p\n", hWnd
);
2968 SendMessageW( hWnd
, WM_EXITMENULOOP
, 0, 0 );
2973 /***********************************************************************
2974 * MENU_TrackMouseMenuBar
2976 * Menu-bar tracking upon a mouse event. Called from NC_HandleSysCommand().
2978 void MENU_TrackMouseMenuBar( HWND hWnd
, INT ht
, POINT pt
)
2980 HMENU hMenu
= (ht
== HTSYSMENU
) ? get_win_sys_menu( hWnd
) : GetMenu( hWnd
);
2981 UINT wFlags
= TPM_ENTERIDLEEX
| TPM_BUTTONDOWN
| TPM_LEFTALIGN
| TPM_LEFTBUTTON
;
2983 TRACE("wnd=%p ht=0x%04x (%ld,%ld)\n", hWnd
, ht
, pt
.x
, pt
.y
);
2987 /* map point to parent client coordinates */
2988 HWND parent
= GetAncestor( hWnd
, GA_PARENT
);
2989 if (parent
!= GetDesktopWindow()) ScreenToClient( parent
, &pt
);
2991 MENU_InitTracking( hWnd
, hMenu
, FALSE
, wFlags
);
2992 MENU_TrackMenu( hMenu
, wFlags
, pt
.x
, pt
.y
, hWnd
, NULL
);
2993 MENU_ExitTracking(hWnd
);
2998 /***********************************************************************
2999 * MENU_TrackKbdMenuBar
3001 * Menu-bar tracking upon a keyboard event. Called from NC_HandleSysCommand().
3003 void MENU_TrackKbdMenuBar( HWND hwnd
, UINT wParam
, WCHAR wChar
)
3005 UINT uItem
= NO_SELECTED_ITEM
;
3007 UINT wFlags
= TPM_ENTERIDLEEX
| TPM_LEFTALIGN
| TPM_LEFTBUTTON
;
3009 TRACE("hwnd %p wParam 0x%04x wChar 0x%04x\n", hwnd
, wParam
, wChar
);
3011 /* find window that has a menu */
3013 while (GetWindowLongW( hwnd
, GWL_STYLE
) & WS_CHILD
)
3014 if (!(hwnd
= GetParent( hwnd
))) return;
3016 /* check if we have to track a system menu */
3018 hTrackMenu
= GetMenu( hwnd
);
3019 if (!hTrackMenu
|| IsIconic(hwnd
) || wChar
== ' ' )
3021 if (!(GetWindowLongW( hwnd
, GWL_STYLE
) & WS_SYSMENU
)) return;
3022 if (GetWindowLongW( hwnd
, GWL_EXSTYLE
) & WS_EX_MANAGED
) return;
3023 hTrackMenu
= get_win_sys_menu( hwnd
);
3025 wParam
|= HTSYSMENU
; /* prevent item lookup */
3028 if (!IsMenu( hTrackMenu
)) return;
3030 MENU_InitTracking( hwnd
, hTrackMenu
, FALSE
, wFlags
);
3032 if( wChar
&& wChar
!= ' ' )
3034 uItem
= MENU_FindItemByKey( hwnd
, hTrackMenu
, wChar
, (wParam
& HTSYSMENU
) );
3035 if( uItem
>= (UINT
)(-2) )
3037 if( uItem
== (UINT
)(-1) ) MessageBeep(0);
3044 MENU_SelectItem( hwnd
, hTrackMenu
, uItem
, TRUE
, 0 );
3046 if( uItem
== NO_SELECTED_ITEM
)
3047 MENU_MoveSelection( hwnd
, hTrackMenu
, ITEM_NEXT
);
3049 PostMessageW( hwnd
, WM_KEYDOWN
, VK_DOWN
, 0L );
3051 MENU_TrackMenu( hTrackMenu
, wFlags
, 0, 0, hwnd
, NULL
);
3053 MENU_ExitTracking( hwnd
);
3057 /**********************************************************************
3058 * TrackPopupMenu (USER32.@)
3060 * Like the win32 API, the function return the command ID only if the
3061 * flag TPM_RETURNCMD is on.
3064 BOOL WINAPI
TrackPopupMenu( HMENU hMenu
, UINT wFlags
, INT x
, INT y
,
3065 INT nReserved
, HWND hWnd
, const RECT
*lpRect
)
3069 MENU_InitTracking(hWnd
, hMenu
, TRUE
, wFlags
);
3071 /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
3072 if (!(wFlags
& TPM_NONOTIFY
))
3073 SendMessageW( hWnd
, WM_INITMENUPOPUP
, (WPARAM
)hMenu
, 0);
3075 if (MENU_ShowPopup( hWnd
, hMenu
, 0, x
, y
, 0, 0 ))
3076 ret
= MENU_TrackMenu( hMenu
, wFlags
| TPM_POPUPMENU
, 0, 0, hWnd
, lpRect
);
3077 MENU_ExitTracking(hWnd
);
3079 if( (!(wFlags
& TPM_RETURNCMD
)) && (ret
!= FALSE
) )
3085 /**********************************************************************
3086 * TrackPopupMenuEx (USER32.@)
3088 BOOL WINAPI
TrackPopupMenuEx( HMENU hMenu
, UINT wFlags
, INT x
, INT y
,
3089 HWND hWnd
, LPTPMPARAMS lpTpm
)
3091 FIXME("not fully implemented\n" );
3092 return TrackPopupMenu( hMenu
, wFlags
, x
, y
, 0, hWnd
,
3093 lpTpm
? &lpTpm
->rcExclude
: NULL
);
3096 /***********************************************************************
3099 * NOTE: Windows has totally different (and undocumented) popup wndproc.
3101 static LRESULT WINAPI
PopupMenuWndProc( HWND hwnd
, UINT message
, WPARAM wParam
, LPARAM lParam
)
3103 TRACE("hwnd=%p msg=0x%04x wp=0x%04x lp=0x%08lx\n", hwnd
, message
, wParam
, lParam
);
3109 CREATESTRUCTW
*cs
= (CREATESTRUCTW
*)lParam
;
3110 SetWindowLongW( hwnd
, 0, (LONG
)cs
->lpCreateParams
);
3114 case WM_MOUSEACTIVATE
: /* We don't want to be activated */
3115 return MA_NOACTIVATE
;
3120 BeginPaint( hwnd
, &ps
);
3121 MENU_DrawPopupMenu( hwnd
, ps
.hdc
,
3122 (HMENU
)GetWindowLongW( hwnd
, 0 ) );
3123 EndPaint( hwnd
, &ps
);
3130 /* zero out global pointer in case resident popup window was destroyed. */
3131 if (hwnd
== top_popup
) top_popup
= 0;
3138 if (!GetWindowLongW( hwnd
, 0 )) ERR("no menu to display\n");
3141 SetWindowLongW( hwnd
, 0, 0 );
3144 case MM_SETMENUHANDLE
:
3145 SetWindowLongW( hwnd
, 0, wParam
);
3148 case MM_GETMENUHANDLE
:
3149 return GetWindowLongW( hwnd
, 0 );
3152 return DefWindowProcW( hwnd
, message
, wParam
, lParam
);
3158 /***********************************************************************
3159 * MENU_GetMenuBarHeight
3161 * Compute the size of the menu bar height. Used by NC_HandleNCCalcSize().
3163 UINT
MENU_GetMenuBarHeight( HWND hwnd
, UINT menubarWidth
,
3164 INT orgX
, INT orgY
)
3170 TRACE("HWND %p, width %d, at (%d, %d).\n", hwnd
, menubarWidth
, orgX
, orgY
);
3172 if (!(lppop
= MENU_GetMenu( GetMenu(hwnd
) ))) return 0;
3174 hdc
= GetDCEx( hwnd
, 0, DCX_CACHE
| DCX_WINDOW
);
3175 SelectObject( hdc
, hMenuFont
);
3176 SetRect(&rectBar
, orgX
, orgY
, orgX
+menubarWidth
, orgY
+GetSystemMetrics(SM_CYMENU
));
3177 MENU_MenuBarCalcSize( hdc
, &rectBar
, lppop
, hwnd
);
3178 ReleaseDC( hwnd
, hdc
);
3179 return lppop
->Height
;
3183 /*******************************************************************
3184 * ChangeMenuA (USER32.@)
3186 BOOL WINAPI
ChangeMenuA( HMENU hMenu
, UINT pos
, LPCSTR data
,
3187 UINT id
, UINT flags
)
3189 TRACE("menu=%p pos=%d data=%p id=%08x flags=%08x\n", hMenu
, pos
, data
, id
, flags
);
3190 if (flags
& MF_APPEND
) return AppendMenuA( hMenu
, flags
& ~MF_APPEND
,
3192 if (flags
& MF_DELETE
) return DeleteMenu(hMenu
, pos
, flags
& ~MF_DELETE
);
3193 if (flags
& MF_CHANGE
) return ModifyMenuA(hMenu
, pos
, flags
& ~MF_CHANGE
,
3195 if (flags
& MF_REMOVE
) return RemoveMenu( hMenu
,
3196 flags
& MF_BYPOSITION
? pos
: id
,
3197 flags
& ~MF_REMOVE
);
3198 /* Default: MF_INSERT */
3199 return InsertMenuA( hMenu
, pos
, flags
, id
, data
);
3203 /*******************************************************************
3204 * ChangeMenuW (USER32.@)
3206 BOOL WINAPI
ChangeMenuW( HMENU hMenu
, UINT pos
, LPCWSTR data
,
3207 UINT id
, UINT flags
)
3209 TRACE("menu=%p pos=%d data=%p id=%08x flags=%08x\n", hMenu
, pos
, data
, id
, flags
);
3210 if (flags
& MF_APPEND
) return AppendMenuW( hMenu
, flags
& ~MF_APPEND
,
3212 if (flags
& MF_DELETE
) return DeleteMenu(hMenu
, pos
, flags
& ~MF_DELETE
);
3213 if (flags
& MF_CHANGE
) return ModifyMenuW(hMenu
, pos
, flags
& ~MF_CHANGE
,
3215 if (flags
& MF_REMOVE
) return RemoveMenu( hMenu
,
3216 flags
& MF_BYPOSITION
? pos
: id
,
3217 flags
& ~MF_REMOVE
);
3218 /* Default: MF_INSERT */
3219 return InsertMenuW( hMenu
, pos
, flags
, id
, data
);
3223 /*******************************************************************
3224 * CheckMenuItem (USER32.@)
3226 DWORD WINAPI
CheckMenuItem( HMENU hMenu
, UINT id
, UINT flags
)
3231 TRACE("menu=%p id=%04x flags=%04x\n", hMenu
, id
, flags
);
3232 if (!(item
= MENU_FindItem( &hMenu
, &id
, flags
))) return -1;
3233 ret
= item
->fState
& MF_CHECKED
;
3234 if (flags
& MF_CHECKED
) item
->fState
|= MF_CHECKED
;
3235 else item
->fState
&= ~MF_CHECKED
;
3240 /**********************************************************************
3241 * EnableMenuItem (USER32.@)
3243 UINT WINAPI
EnableMenuItem( HMENU hMenu
, UINT wItemID
, UINT wFlags
)
3249 TRACE("(%p, %04x, %04x) !\n", hMenu
, wItemID
, wFlags
);
3251 /* Get the Popupmenu to access the owner menu */
3252 if (!(menu
= MENU_GetMenu(hMenu
)))
3255 if (!(item
= MENU_FindItem( &hMenu
, &wItemID
, wFlags
)))
3258 oldflags
= item
->fState
& (MF_GRAYED
| MF_DISABLED
);
3259 item
->fState
^= (oldflags
^ wFlags
) & (MF_GRAYED
| MF_DISABLED
);
3261 /* In win95 if the close item in the system menu change update the close button */
3262 if (TWEAK_WineLook
== WIN95_LOOK
)
3263 if((item
->wID
== SC_CLOSE
) && (oldflags
!= wFlags
))
3265 if (menu
->hSysMenuOwner
!= 0)
3267 POPUPMENU
* parentMenu
;
3269 /* Get the parent menu to access*/
3270 if (!(parentMenu
= MENU_GetMenu(menu
->hSysMenuOwner
)))
3273 /* Refresh the frame to reflect the change*/
3274 SetWindowPos(parentMenu
->hWnd
, 0, 0, 0, 0, 0,
3275 SWP_NOACTIVATE
| SWP_NOMOVE
| SWP_NOSIZE
| SWP_NOZORDER
);
3283 /*******************************************************************
3284 * GetMenuStringA (USER32.@)
3286 INT WINAPI
GetMenuStringA(
3287 HMENU hMenu
, /* [in] menuhandle */
3288 UINT wItemID
, /* [in] menu item (dep. on wFlags) */
3289 LPSTR str
, /* [out] outbuffer. If NULL, func returns entry length*/
3290 INT nMaxSiz
, /* [in] length of buffer. if 0, func returns entry len*/
3291 UINT wFlags
/* [in] MF_ flags */
3295 TRACE("menu=%p item=%04x ptr=%p len=%d flags=%04x\n", hMenu
, wItemID
, str
, nMaxSiz
, wFlags
);
3296 if (!(item
= MENU_FindItem( &hMenu
, &wItemID
, wFlags
))) return 0;
3297 if (!IS_STRING_ITEM(item
->fType
)) return 0;
3298 if (!str
|| !nMaxSiz
) return strlenW(item
->text
);
3300 if (!WideCharToMultiByte( CP_ACP
, 0, item
->text
, -1, str
, nMaxSiz
, NULL
, NULL
))
3302 TRACE("returning '%s'\n", str
);
3307 /*******************************************************************
3308 * GetMenuStringW (USER32.@)
3310 INT WINAPI
GetMenuStringW( HMENU hMenu
, UINT wItemID
,
3311 LPWSTR str
, INT nMaxSiz
, UINT wFlags
)
3315 TRACE("menu=%p item=%04x ptr=%p len=%d flags=%04x\n", hMenu
, wItemID
, str
, nMaxSiz
, wFlags
);
3316 if (!(item
= MENU_FindItem( &hMenu
, &wItemID
, wFlags
))) return 0;
3317 if (!IS_STRING_ITEM(item
->fType
)) return 0;
3318 if (!str
|| !nMaxSiz
) return strlenW(item
->text
);
3320 lstrcpynW( str
, item
->text
, nMaxSiz
);
3321 return strlenW(str
);
3325 /**********************************************************************
3326 * HiliteMenuItem (USER32.@)
3328 BOOL WINAPI
HiliteMenuItem( HWND hWnd
, HMENU hMenu
, UINT wItemID
,
3332 TRACE("(%p, %p, %04x, %04x);\n", hWnd
, hMenu
, wItemID
, wHilite
);
3333 if (!MENU_FindItem( &hMenu
, &wItemID
, wHilite
)) return FALSE
;
3334 if (!(menu
= MENU_GetMenu(hMenu
))) return FALSE
;
3335 if (menu
->FocusedItem
== wItemID
) return TRUE
;
3336 MENU_HideSubPopups( hWnd
, hMenu
, FALSE
);
3337 MENU_SelectItem( hWnd
, hMenu
, wItemID
, TRUE
, 0 );
3342 /**********************************************************************
3343 * GetMenuState (USER32.@)
3345 UINT WINAPI
GetMenuState( HMENU hMenu
, UINT wItemID
, UINT wFlags
)
3348 TRACE("(menu=%p, id=%04x, flags=%04x);\n", hMenu
, wItemID
, wFlags
);
3349 if (!(item
= MENU_FindItem( &hMenu
, &wItemID
, wFlags
))) return -1;
3350 debug_print_menuitem (" item: ", item
, "");
3351 if (item
->fType
& MF_POPUP
)
3353 POPUPMENU
*menu
= MENU_GetMenu( item
->hSubMenu
);
3354 if (!menu
) return -1;
3355 else return (menu
->nItems
<< 8) | ((item
->fState
|item
->fType
) & 0xff);
3359 /* We used to (from way back then) mask the result to 0xff. */
3360 /* I don't know why and it seems wrong as the documented */
3361 /* return flag MF_SEPARATOR is outside that mask. */
3362 return (item
->fType
| item
->fState
);
3367 /**********************************************************************
3368 * GetMenuItemCount (USER32.@)
3370 INT WINAPI
GetMenuItemCount( HMENU hMenu
)
3372 LPPOPUPMENU menu
= MENU_GetMenu(hMenu
);
3373 if (!menu
) return -1;
3374 TRACE("(%p) returning %d\n", hMenu
, menu
->nItems
);
3375 return menu
->nItems
;
3379 /**********************************************************************
3380 * GetMenuItemID (USER32.@)
3382 UINT WINAPI
GetMenuItemID( HMENU hMenu
, INT nPos
)
3386 if (!(lpmi
= MENU_FindItem(&hMenu
,&nPos
,MF_BYPOSITION
))) return -1;
3387 if (lpmi
->fType
& MF_POPUP
) return -1;
3393 /*******************************************************************
3394 * InsertMenuW (USER32.@)
3396 BOOL WINAPI
InsertMenuW( HMENU hMenu
, UINT pos
, UINT flags
,
3397 UINT_PTR id
, LPCWSTR str
)
3401 if (IS_STRING_ITEM(flags
) && str
)
3402 TRACE("hMenu %p, pos %d, flags %08x, id %04x, str %s\n",
3403 hMenu
, pos
, flags
, id
, debugstr_w(str
) );
3404 else TRACE("hMenu %p, pos %d, flags %08x, id %04x, str %08lx (not a string)\n",
3405 hMenu
, pos
, flags
, id
, (DWORD
)str
);
3407 if (!(item
= MENU_InsertItem( hMenu
, pos
, flags
))) return FALSE
;
3409 if (!(MENU_SetItemData( item
, flags
, id
, str
)))
3411 RemoveMenu( hMenu
, pos
, flags
);
3415 if (flags
& MF_POPUP
) /* Set the MF_POPUP flag on the popup-menu */
3416 (MENU_GetMenu((HMENU
)id
))->wFlags
|= MF_POPUP
;
3418 item
->hCheckBit
= item
->hUnCheckBit
= 0;
3423 /*******************************************************************
3424 * InsertMenuA (USER32.@)
3426 BOOL WINAPI
InsertMenuA( HMENU hMenu
, UINT pos
, UINT flags
,
3427 UINT_PTR id
, LPCSTR str
)
3431 if (IS_STRING_ITEM(flags
) && str
)
3433 INT len
= MultiByteToWideChar( CP_ACP
, 0, str
, -1, NULL
, 0 );
3434 LPWSTR newstr
= HeapAlloc( GetProcessHeap(), 0, len
* sizeof(WCHAR
) );
3437 MultiByteToWideChar( CP_ACP
, 0, str
, -1, newstr
, len
);
3438 ret
= InsertMenuW( hMenu
, pos
, flags
, id
, newstr
);
3439 HeapFree( GetProcessHeap(), 0, newstr
);
3443 else return InsertMenuW( hMenu
, pos
, flags
, id
, (LPCWSTR
)str
);
3447 /*******************************************************************
3448 * AppendMenuA (USER32.@)
3450 BOOL WINAPI
AppendMenuA( HMENU hMenu
, UINT flags
,
3451 UINT_PTR id
, LPCSTR data
)
3453 return InsertMenuA( hMenu
, -1, flags
| MF_BYPOSITION
, id
, data
);
3457 /*******************************************************************
3458 * AppendMenuW (USER32.@)
3460 BOOL WINAPI
AppendMenuW( HMENU hMenu
, UINT flags
,
3461 UINT_PTR id
, LPCWSTR data
)
3463 return InsertMenuW( hMenu
, -1, flags
| MF_BYPOSITION
, id
, data
);
3467 /**********************************************************************
3468 * RemoveMenu (USER32.@)
3470 BOOL WINAPI
RemoveMenu( HMENU hMenu
, UINT nPos
, UINT wFlags
)
3475 TRACE("(menu=%p pos=%04x flags=%04x)\n",hMenu
, nPos
, wFlags
);
3476 if (!(item
= MENU_FindItem( &hMenu
, &nPos
, wFlags
))) return FALSE
;
3477 if (!(menu
= MENU_GetMenu(hMenu
))) return FALSE
;
3481 MENU_FreeItemData( item
);
3483 if (--menu
->nItems
== 0)
3485 HeapFree( GetProcessHeap(), 0, menu
->items
);
3490 while(nPos
< menu
->nItems
)
3496 menu
->items
= HeapReAlloc( GetProcessHeap(), 0, menu
->items
,
3497 menu
->nItems
* sizeof(MENUITEM
) );
3503 /**********************************************************************
3504 * DeleteMenu (USER32.@)
3506 BOOL WINAPI
DeleteMenu( HMENU hMenu
, UINT nPos
, UINT wFlags
)
3508 MENUITEM
*item
= MENU_FindItem( &hMenu
, &nPos
, wFlags
);
3509 if (!item
) return FALSE
;
3510 if (item
->fType
& MF_POPUP
) DestroyMenu( item
->hSubMenu
);
3511 /* nPos is now the position of the item */
3512 RemoveMenu( hMenu
, nPos
, wFlags
| MF_BYPOSITION
);
3517 /*******************************************************************
3518 * ModifyMenuW (USER32.@)
3520 BOOL WINAPI
ModifyMenuW( HMENU hMenu
, UINT pos
, UINT flags
,
3521 UINT_PTR id
, LPCWSTR str
)
3525 if (IS_STRING_ITEM(flags
))
3527 TRACE("%p %d %04x %04x %s\n", hMenu
, pos
, flags
, id
, debugstr_w(str
) );
3531 TRACE("%p %d %04x %04x %08lx\n", hMenu
, pos
, flags
, id
, (DWORD
)str
);
3534 if (!(item
= MENU_FindItem( &hMenu
, &pos
, flags
))) return FALSE
;
3535 return MENU_SetItemData( item
, flags
, id
, str
);
3539 /*******************************************************************
3540 * ModifyMenuA (USER32.@)
3542 BOOL WINAPI
ModifyMenuA( HMENU hMenu
, UINT pos
, UINT flags
,
3543 UINT_PTR id
, LPCSTR str
)
3547 if (IS_STRING_ITEM(flags
) && str
)
3549 INT len
= MultiByteToWideChar( CP_ACP
, 0, str
, -1, NULL
, 0 );
3550 LPWSTR newstr
= HeapAlloc( GetProcessHeap(), 0, len
* sizeof(WCHAR
) );
3553 MultiByteToWideChar( CP_ACP
, 0, str
, -1, newstr
, len
);
3554 ret
= ModifyMenuW( hMenu
, pos
, flags
, id
, newstr
);
3555 HeapFree( GetProcessHeap(), 0, newstr
);
3559 else return ModifyMenuW( hMenu
, pos
, flags
, id
, (LPCWSTR
)str
);
3563 /**********************************************************************
3564 * CreatePopupMenu (USER32.@)
3566 HMENU WINAPI
CreatePopupMenu(void)
3571 if (!(hmenu
= CreateMenu())) return 0;
3572 menu
= MENU_GetMenu( hmenu
);
3573 menu
->wFlags
|= MF_POPUP
;
3574 menu
->bTimeToHide
= FALSE
;
3579 /**********************************************************************
3580 * GetMenuCheckMarkDimensions (USER.417)
3581 * GetMenuCheckMarkDimensions (USER32.@)
3583 DWORD WINAPI
GetMenuCheckMarkDimensions(void)
3585 return MAKELONG( GetSystemMetrics(SM_CXMENUCHECK
), GetSystemMetrics(SM_CYMENUCHECK
) );
3589 /**********************************************************************
3590 * SetMenuItemBitmaps (USER32.@)
3592 BOOL WINAPI
SetMenuItemBitmaps( HMENU hMenu
, UINT nPos
, UINT wFlags
,
3593 HBITMAP hNewUnCheck
, HBITMAP hNewCheck
)
3596 TRACE("(%p, %04x, %04x, %p, %p)\n",
3597 hMenu
, nPos
, wFlags
, hNewCheck
, hNewUnCheck
);
3598 if (!(item
= MENU_FindItem( &hMenu
, &nPos
, wFlags
))) return FALSE
;
3600 if (!hNewCheck
&& !hNewUnCheck
)
3602 item
->fState
&= ~MF_USECHECKBITMAPS
;
3604 else /* Install new bitmaps */
3606 item
->hCheckBit
= hNewCheck
;
3607 item
->hUnCheckBit
= hNewUnCheck
;
3608 item
->fState
|= MF_USECHECKBITMAPS
;
3614 /**********************************************************************
3615 * CreateMenu (USER32.@)
3617 HMENU WINAPI
CreateMenu(void)
3621 if (!(hMenu
= USER_HEAP_ALLOC( sizeof(POPUPMENU
) ))) return 0;
3622 menu
= (LPPOPUPMENU
) USER_HEAP_LIN_ADDR(hMenu
);
3624 ZeroMemory(menu
, sizeof(POPUPMENU
));
3625 menu
->wMagic
= MENU_MAGIC
;
3626 menu
->FocusedItem
= NO_SELECTED_ITEM
;
3627 menu
->bTimeToHide
= FALSE
;
3629 TRACE("return %p\n", hMenu
);
3635 /**********************************************************************
3636 * DestroyMenu (USER32.@)
3638 BOOL WINAPI
DestroyMenu( HMENU hMenu
)
3640 TRACE("(%p)\n", hMenu
);
3642 /* Silently ignore attempts to destroy default system popup */
3644 if (hMenu
&& hMenu
!= MENU_DefSysPopup
)
3646 LPPOPUPMENU lppop
= MENU_GetMenu(hMenu
);
3648 if (!lppop
) return FALSE
;
3650 lppop
->wMagic
= 0; /* Mark it as destroyed */
3652 if ((lppop
->wFlags
& MF_POPUP
) && lppop
->hWnd
)
3654 DestroyWindow( lppop
->hWnd
);
3658 if (lppop
->items
) /* recursively destroy submenus */
3661 MENUITEM
*item
= lppop
->items
;
3662 for (i
= lppop
->nItems
; i
> 0; i
--, item
++)
3664 if (item
->fType
& MF_POPUP
) DestroyMenu(item
->hSubMenu
);
3665 MENU_FreeItemData( item
);
3667 HeapFree( GetProcessHeap(), 0, lppop
->items
);
3669 USER_HEAP_FREE( hMenu
);
3671 return (hMenu
!= MENU_DefSysPopup
);
3675 /**********************************************************************
3676 * GetSystemMenu (USER32.@)
3678 HMENU WINAPI
GetSystemMenu( HWND hWnd
, BOOL bRevert
)
3680 WND
*wndPtr
= WIN_FindWndPtr( hWnd
);
3685 if( wndPtr
->hSysMenu
)
3689 DestroyMenu(wndPtr
->hSysMenu
);
3690 wndPtr
->hSysMenu
= 0;
3694 POPUPMENU
*menu
= MENU_GetMenu( wndPtr
->hSysMenu
);
3697 if( menu
->nItems
> 0 && menu
->items
[0].hSubMenu
== MENU_DefSysPopup
)
3698 menu
->items
[0].hSubMenu
= MENU_CopySysPopup();
3702 WARN("Current sys-menu (%p) of wnd %p is broken\n",
3703 wndPtr
->hSysMenu
, hWnd
);
3704 wndPtr
->hSysMenu
= 0;
3709 if(!wndPtr
->hSysMenu
&& (wndPtr
->dwStyle
& WS_SYSMENU
) )
3710 wndPtr
->hSysMenu
= MENU_GetSysMenu( hWnd
, (HMENU
)(-1) );
3712 if( wndPtr
->hSysMenu
)
3715 retvalue
= GetSubMenu(wndPtr
->hSysMenu
, 0);
3717 /* Store the dummy sysmenu handle to facilitate the refresh */
3718 /* of the close button if the SC_CLOSE item change */
3719 menu
= MENU_GetMenu(retvalue
);
3721 menu
->hSysMenuOwner
= wndPtr
->hSysMenu
;
3723 WIN_ReleaseWndPtr(wndPtr
);
3725 return bRevert
? 0 : retvalue
;
3729 /*******************************************************************
3730 * SetSystemMenu (USER32.@)
3732 BOOL WINAPI
SetSystemMenu( HWND hwnd
, HMENU hMenu
)
3734 WND
*wndPtr
= WIN_FindWndPtr(hwnd
);
3738 if (wndPtr
->hSysMenu
) DestroyMenu( wndPtr
->hSysMenu
);
3739 wndPtr
->hSysMenu
= MENU_GetSysMenu( hwnd
, hMenu
);
3740 WIN_ReleaseWndPtr(wndPtr
);
3747 /**********************************************************************
3748 * GetMenu (USER32.@)
3750 HMENU WINAPI
GetMenu( HWND hWnd
)
3752 HMENU retvalue
= (HMENU
)GetWindowLongW( hWnd
, GWL_ID
);
3753 TRACE("for %p returning %p\n", hWnd
, retvalue
);
3758 /**********************************************************************
3759 * SetMenu (USER32.@)
3761 BOOL WINAPI
SetMenu( HWND hWnd
, HMENU hMenu
)
3763 TRACE("(%p, %p);\n", hWnd
, hMenu
);
3765 if (hMenu
&& !IsMenu(hMenu
))
3767 WARN("hMenu %p is not a menu handle\n", hMenu
);
3770 if (GetWindowLongW( hWnd
, GWL_STYLE
) & WS_CHILD
) return FALSE
;
3772 hWnd
= WIN_GetFullHandle( hWnd
);
3773 if (GetCapture() == hWnd
) MENU_SetCapture(0); /* release the capture */
3779 if (!(lpmenu
= MENU_GetMenu(hMenu
))) return FALSE
;
3781 lpmenu
->hWnd
= hWnd
;
3782 lpmenu
->Height
= 0; /* Make sure we recalculate the size */
3784 SetWindowLongW( hWnd
, GWL_ID
, (LONG_PTR
)hMenu
);
3786 if (IsWindowVisible(hWnd
))
3787 SetWindowPos( hWnd
, 0, 0, 0, 0, 0, SWP_NOSIZE
| SWP_NOMOVE
|
3788 SWP_NOACTIVATE
| SWP_NOZORDER
| SWP_FRAMECHANGED
);
3794 /**********************************************************************
3795 * GetSubMenu (USER32.@)
3797 HMENU WINAPI
GetSubMenu( HMENU hMenu
, INT nPos
)
3801 if (!(lpmi
= MENU_FindItem(&hMenu
,&nPos
,MF_BYPOSITION
))) return 0;
3802 if (!(lpmi
->fType
& MF_POPUP
)) return 0;
3803 return lpmi
->hSubMenu
;
3807 /**********************************************************************
3808 * DrawMenuBar (USER32.@)
3810 BOOL WINAPI
DrawMenuBar( HWND hWnd
)
3813 HMENU hMenu
= GetMenu(hWnd
);
3815 if (GetWindowLongW( hWnd
, GWL_STYLE
) & WS_CHILD
) return FALSE
;
3816 if (!hMenu
|| !(lppop
= MENU_GetMenu( hMenu
))) return FALSE
;
3818 lppop
->Height
= 0; /* Make sure we call MENU_MenuBarCalcSize */
3819 lppop
->hwndOwner
= hWnd
;
3820 SetWindowPos( hWnd
, 0, 0, 0, 0, 0, SWP_NOSIZE
| SWP_NOMOVE
|
3821 SWP_NOACTIVATE
| SWP_NOZORDER
| SWP_FRAMECHANGED
);
3825 /***********************************************************************
3826 * DrawMenuBarTemp (USER32.@)
3830 * called by W98SE desk.cpl Control Panel Applet
3832 * Not 100% sure about the param names, but close.
3834 DWORD WINAPI
DrawMenuBarTemp(HWND hwnd
, HDC hDC
, LPRECT lprect
, HMENU hMenu
, HFONT hFont
)
3841 hMenu
= GetMenu(hwnd
);
3846 lppop
= MENU_GetMenu( hMenu
);
3847 if (lppop
== NULL
|| lprect
== NULL
)
3849 retvalue
= GetSystemMetrics(SM_CYMENU
);
3853 TRACE("(%p, %p, %p, %p, %p)\n", hwnd
, hDC
, lprect
, hMenu
, hFont
);
3855 hfontOld
= SelectObject( hDC
, hFont
);
3857 if (lppop
->Height
== 0)
3858 MENU_MenuBarCalcSize(hDC
, lprect
, lppop
, hwnd
);
3860 lprect
->bottom
= lprect
->top
+ lppop
->Height
;
3862 FillRect(hDC
, lprect
, GetSysColorBrush(COLOR_MENU
) );
3864 if (TWEAK_WineLook
== WIN31_LOOK
)
3866 SelectObject( hDC
, SYSCOLOR_GetPen(COLOR_WINDOWFRAME
) );
3867 MoveToEx( hDC
, lprect
->left
, lprect
->bottom
, NULL
);
3868 LineTo( hDC
, lprect
->right
, lprect
->bottom
);
3872 SelectObject( hDC
, SYSCOLOR_GetPen(COLOR_3DFACE
));
3873 MoveToEx( hDC
, lprect
->left
, lprect
->bottom
, NULL
);
3874 LineTo( hDC
, lprect
->right
, lprect
->bottom
);
3877 if (lppop
->nItems
== 0)
3879 retvalue
= GetSystemMetrics(SM_CYMENU
);
3883 for (i
= 0; i
< lppop
->nItems
; i
++)
3885 MENU_DrawMenuItem( hwnd
, hMenu
, hwnd
,
3886 hDC
, &lppop
->items
[i
], lppop
->Height
, TRUE
, ODA_DRAWENTIRE
);
3888 retvalue
= lppop
->Height
;
3891 if (hfontOld
) SelectObject (hDC
, hfontOld
);
3895 /***********************************************************************
3896 * EndMenu (USER.187)
3897 * EndMenu (USER32.@)
3899 void WINAPI
EndMenu(void)
3901 /* if we are in the menu code, and it is active */
3902 if (!fEndMenu
&& top_popup
)
3904 /* terminate the menu handling code */
3907 /* needs to be posted to wakeup the internal menu handler */
3908 /* which will now terminate the menu, in the event that */
3909 /* the main window was minimized, or lost focus, so we */
3910 /* don't end up with an orphaned menu */
3911 PostMessageW( top_popup
, WM_CANCELMODE
, 0, 0);
3916 /***********************************************************************
3917 * LookupMenuHandle (USER.217)
3919 HMENU16 WINAPI
LookupMenuHandle16( HMENU16 hmenu
, INT16 id
)
3921 HMENU hmenu32
= HMENU_32(hmenu
);
3923 if (!MENU_FindItem( &hmenu32
, &id32
, MF_BYCOMMAND
)) return 0;
3924 else return HMENU_16(hmenu32
);
3928 /**********************************************************************
3929 * LoadMenu (USER.150)
3931 HMENU16 WINAPI
LoadMenu16( HINSTANCE16 instance
, LPCSTR name
)
3937 if (HIWORD(name
) && name
[0] == '#') name
= (LPCSTR
)atoi( name
+ 1 );
3938 if (!name
) return 0;
3940 instance
= GetExePtr( instance
);
3941 if (!(hRsrc
= FindResource16( instance
, name
, (LPSTR
)RT_MENU
))) return 0;
3942 if (!(handle
= LoadResource16( instance
, hRsrc
))) return 0;
3943 hMenu
= LoadMenuIndirect16(LockResource16(handle
));
3944 FreeResource16( handle
);
3949 /*****************************************************************
3950 * LoadMenuA (USER32.@)
3952 HMENU WINAPI
LoadMenuA( HINSTANCE instance
, LPCSTR name
)
3954 HRSRC hrsrc
= FindResourceA( instance
, name
, (LPSTR
)RT_MENU
);
3955 if (!hrsrc
) return 0;
3956 return LoadMenuIndirectA( (LPCVOID
)LoadResource( instance
, hrsrc
));
3960 /*****************************************************************
3961 * LoadMenuW (USER32.@)
3963 HMENU WINAPI
LoadMenuW( HINSTANCE instance
, LPCWSTR name
)
3965 HRSRC hrsrc
= FindResourceW( instance
, name
, (LPWSTR
)RT_MENU
);
3966 if (!hrsrc
) return 0;
3967 return LoadMenuIndirectW( (LPCVOID
)LoadResource( instance
, hrsrc
));
3971 /**********************************************************************
3972 * LoadMenuIndirect (USER.220)
3974 HMENU16 WINAPI
LoadMenuIndirect16( LPCVOID
template )
3977 WORD version
, offset
;
3978 LPCSTR p
= (LPCSTR
)template;
3980 TRACE("(%p)\n", template );
3981 version
= GET_WORD(p
);
3985 WARN("version must be 0 for Win16\n" );
3988 offset
= GET_WORD(p
);
3989 p
+= sizeof(WORD
) + offset
;
3990 if (!(hMenu
= CreateMenu())) return 0;
3991 if (!MENU_ParseResource( p
, hMenu
, FALSE
))
3993 DestroyMenu( hMenu
);
3996 return HMENU_16(hMenu
);
4000 /**********************************************************************
4001 * LoadMenuIndirectW (USER32.@)
4003 HMENU WINAPI
LoadMenuIndirectW( LPCVOID
template )
4006 WORD version
, offset
;
4007 LPCSTR p
= (LPCSTR
)template;
4009 version
= GET_WORD(p
);
4011 TRACE("%p, ver %d\n", template, version
);
4014 case 0: /* standard format is version of 0 */
4015 offset
= GET_WORD(p
);
4016 p
+= sizeof(WORD
) + offset
;
4017 if (!(hMenu
= CreateMenu())) return 0;
4018 if (!MENU_ParseResource( p
, hMenu
, TRUE
))
4020 DestroyMenu( hMenu
);
4024 case 1: /* extended format is version of 1 */
4025 offset
= GET_WORD(p
);
4026 p
+= sizeof(WORD
) + offset
;
4027 if (!(hMenu
= CreateMenu())) return 0;
4028 if (!MENUEX_ParseResource( p
, hMenu
))
4030 DestroyMenu( hMenu
);
4035 ERR("version %d not supported.\n", version
);
4041 /**********************************************************************
4042 * LoadMenuIndirectA (USER32.@)
4044 HMENU WINAPI
LoadMenuIndirectA( LPCVOID
template )
4046 return LoadMenuIndirectW( template );
4050 /**********************************************************************
4053 BOOL WINAPI
IsMenu(HMENU hmenu
)
4055 LPPOPUPMENU menu
= MENU_GetMenu(hmenu
);
4056 return menu
!= NULL
;
4059 /**********************************************************************
4060 * GetMenuItemInfo_common
4063 static BOOL
GetMenuItemInfo_common ( HMENU hmenu
, UINT item
, BOOL bypos
,
4064 LPMENUITEMINFOW lpmii
, BOOL unicode
)
4066 MENUITEM
*menu
= MENU_FindItem (&hmenu
, &item
, bypos
? MF_BYPOSITION
: 0);
4068 debug_print_menuitem("GetMenuItemInfo_common: ", menu
, "");
4073 if (lpmii
->fMask
& MIIM_TYPE
) {
4074 lpmii
->fType
= menu
->fType
;
4075 switch (MENU_ITEM_TYPE(menu
->fType
)) {
4077 break; /* will be done below */
4080 lpmii
->dwTypeData
= menu
->text
;
4087 /* copy the text string */
4088 if ((lpmii
->fMask
& (MIIM_TYPE
|MIIM_STRING
)) &&
4089 (MENU_ITEM_TYPE(menu
->fType
) == MF_STRING
) && menu
->text
)
4094 len
= strlenW(menu
->text
);
4095 if(lpmii
->dwTypeData
&& lpmii
->cch
)
4096 lstrcpynW(lpmii
->dwTypeData
, menu
->text
, lpmii
->cch
);
4100 len
= WideCharToMultiByte( CP_ACP
, 0, menu
->text
, -1, NULL
, 0, NULL
, NULL
);
4101 if(lpmii
->dwTypeData
&& lpmii
->cch
)
4102 if (!WideCharToMultiByte( CP_ACP
, 0, menu
->text
, -1,
4103 (LPSTR
)lpmii
->dwTypeData
, lpmii
->cch
, NULL
, NULL
))
4104 ((LPSTR
)lpmii
->dwTypeData
)[lpmii
->cch
-1] = 0;
4106 /* if we've copied a substring we return its length */
4107 if(lpmii
->dwTypeData
&& lpmii
->cch
)
4109 if (lpmii
->cch
<= len
) lpmii
->cch
--;
4111 else /* return length of string */
4115 if (lpmii
->fMask
& MIIM_FTYPE
)
4116 lpmii
->fType
= menu
->fType
;
4118 if (lpmii
->fMask
& MIIM_BITMAP
)
4119 lpmii
->hbmpItem
= menu
->hbmpItem
;
4121 if (lpmii
->fMask
& MIIM_STATE
)
4122 lpmii
->fState
= menu
->fState
;
4124 if (lpmii
->fMask
& MIIM_ID
)
4125 lpmii
->wID
= menu
->wID
;
4127 if (lpmii
->fMask
& MIIM_SUBMENU
)
4128 lpmii
->hSubMenu
= menu
->hSubMenu
;
4130 if (lpmii
->fMask
& MIIM_CHECKMARKS
) {
4131 lpmii
->hbmpChecked
= menu
->hCheckBit
;
4132 lpmii
->hbmpUnchecked
= menu
->hUnCheckBit
;
4134 if (lpmii
->fMask
& MIIM_DATA
)
4135 lpmii
->dwItemData
= menu
->dwItemData
;
4140 /**********************************************************************
4141 * GetMenuItemInfoA (USER32.@)
4143 BOOL WINAPI
GetMenuItemInfoA( HMENU hmenu
, UINT item
, BOOL bypos
,
4144 LPMENUITEMINFOA lpmii
)
4146 return GetMenuItemInfo_common (hmenu
, item
, bypos
,
4147 (LPMENUITEMINFOW
)lpmii
, FALSE
);
4150 /**********************************************************************
4151 * GetMenuItemInfoW (USER32.@)
4153 BOOL WINAPI
GetMenuItemInfoW( HMENU hmenu
, UINT item
, BOOL bypos
,
4154 LPMENUITEMINFOW lpmii
)
4156 return GetMenuItemInfo_common (hmenu
, item
, bypos
,
4161 /* set a menu item text from a ASCII or Unicode string */
4162 inline static void set_menu_item_text( MENUITEM
*menu
, LPCWSTR text
, BOOL unicode
)
4167 menu
->fType
|= MF_SEPARATOR
;
4171 if ((menu
->text
= HeapAlloc( GetProcessHeap(), 0, (strlenW(text
)+1) * sizeof(WCHAR
) )))
4172 strcpyW( menu
->text
, text
);
4176 LPCSTR str
= (LPCSTR
)text
;
4177 int len
= MultiByteToWideChar( CP_ACP
, 0, str
, -1, NULL
, 0 );
4178 if ((menu
->text
= HeapAlloc( GetProcessHeap(), 0, len
* sizeof(WCHAR
) )))
4179 MultiByteToWideChar( CP_ACP
, 0, str
, -1, menu
->text
, len
);
4184 /**********************************************************************
4185 * SetMenuItemInfo_common
4188 static BOOL
SetMenuItemInfo_common(MENUITEM
* menu
,
4189 const MENUITEMINFOW
*lpmii
,
4192 if (!menu
) return FALSE
;
4194 debug_print_menuitem("MENU_SetItemInfo_common from: ", menu
, "");
4196 if (lpmii
->fMask
& MIIM_TYPE
) {
4197 /* Get rid of old string. */
4198 if (IS_STRING_ITEM(menu
->fType
) && menu
->text
) {
4199 HeapFree(GetProcessHeap(), 0, menu
->text
);
4203 /* make only MENU_ITEM_TYPE bits in menu->fType equal lpmii->fType */
4204 menu
->fType
&= ~MENU_ITEM_TYPE(menu
->fType
);
4205 menu
->fType
|= MENU_ITEM_TYPE(lpmii
->fType
);
4207 menu
->text
= lpmii
->dwTypeData
;
4209 if (IS_STRING_ITEM(menu
->fType
))
4210 set_menu_item_text( menu
, lpmii
->dwTypeData
, unicode
);
4213 if (lpmii
->fMask
& MIIM_FTYPE
) {
4214 /* free the string when the type is changing */
4215 if ( (!IS_STRING_ITEM(lpmii
->fType
)) && IS_STRING_ITEM(menu
->fType
) && menu
->text
) {
4216 HeapFree(GetProcessHeap(), 0, menu
->text
);
4219 menu
->fType
&= ~MENU_ITEM_TYPE(menu
->fType
);
4220 menu
->fType
|= MENU_ITEM_TYPE(lpmii
->fType
);
4221 if ( IS_STRING_ITEM(menu
->fType
) && !menu
->text
)
4222 menu
->fType
|= MF_SEPARATOR
;
4225 if (lpmii
->fMask
& MIIM_STRING
) {
4226 /* free the string when used */
4227 if (IS_STRING_ITEM(menu
->fType
) && menu
->text
) {
4228 HeapFree(GetProcessHeap(), 0, menu
->text
);
4229 set_menu_item_text( menu
, lpmii
->dwTypeData
, unicode
);
4233 if (lpmii
->fMask
& MIIM_STATE
)
4235 /* FIXME: MFS_DEFAULT do we have to reset the other menu items? */
4236 menu
->fState
= lpmii
->fState
;
4239 if (lpmii
->fMask
& MIIM_ID
)
4240 menu
->wID
= lpmii
->wID
;
4242 if (lpmii
->fMask
& MIIM_SUBMENU
) {
4243 menu
->hSubMenu
= lpmii
->hSubMenu
;
4244 if (menu
->hSubMenu
) {
4245 POPUPMENU
*subMenu
= MENU_GetMenu(menu
->hSubMenu
);
4247 subMenu
->wFlags
|= MF_POPUP
;
4248 menu
->fType
|= MF_POPUP
;
4251 /* FIXME: Return an error ? */
4252 menu
->fType
&= ~MF_POPUP
;
4255 menu
->fType
&= ~MF_POPUP
;
4258 if (lpmii
->fMask
& MIIM_CHECKMARKS
)
4260 if (lpmii
->fType
& MFT_RADIOCHECK
)
4261 menu
->fType
|= MFT_RADIOCHECK
;
4263 menu
->hCheckBit
= lpmii
->hbmpChecked
;
4264 menu
->hUnCheckBit
= lpmii
->hbmpUnchecked
;
4266 if (lpmii
->fMask
& MIIM_DATA
)
4267 menu
->dwItemData
= lpmii
->dwItemData
;
4269 debug_print_menuitem("SetMenuItemInfo_common to : ", menu
, "");
4273 /**********************************************************************
4274 * SetMenuItemInfoA (USER32.@)
4276 BOOL WINAPI
SetMenuItemInfoA(HMENU hmenu
, UINT item
, BOOL bypos
,
4277 const MENUITEMINFOA
*lpmii
)
4279 return SetMenuItemInfo_common(MENU_FindItem(&hmenu
, &item
, bypos
? MF_BYPOSITION
: 0),
4280 (const MENUITEMINFOW
*)lpmii
, FALSE
);
4283 /**********************************************************************
4284 * SetMenuItemInfoW (USER32.@)
4286 BOOL WINAPI
SetMenuItemInfoW(HMENU hmenu
, UINT item
, BOOL bypos
,
4287 const MENUITEMINFOW
*lpmii
)
4289 return SetMenuItemInfo_common(MENU_FindItem(&hmenu
, &item
, bypos
? MF_BYPOSITION
: 0),
4293 /**********************************************************************
4294 * SetMenuDefaultItem (USER32.@)
4297 BOOL WINAPI
SetMenuDefaultItem(HMENU hmenu
, UINT uItem
, UINT bypos
)
4303 TRACE("(%p,%d,%d)\n", hmenu
, uItem
, bypos
);
4305 if (!(menu
= MENU_GetMenu(hmenu
))) return FALSE
;
4307 /* reset all default-item flags */
4309 for (i
= 0; i
< menu
->nItems
; i
++, item
++)
4311 item
->fState
&= ~MFS_DEFAULT
;
4314 /* no default item */
4323 if ( uItem
>= menu
->nItems
) return FALSE
;
4324 item
[uItem
].fState
|= MFS_DEFAULT
;
4329 for (i
= 0; i
< menu
->nItems
; i
++, item
++)
4331 if (item
->wID
== uItem
)
4333 item
->fState
|= MFS_DEFAULT
;
4342 /**********************************************************************
4343 * GetMenuDefaultItem (USER32.@)
4345 UINT WINAPI
GetMenuDefaultItem(HMENU hmenu
, UINT bypos
, UINT flags
)
4351 TRACE("(%p,%d,%d)\n", hmenu
, bypos
, flags
);
4353 if (!(menu
= MENU_GetMenu(hmenu
))) return -1;
4355 /* find default item */
4359 if (! item
) return -1;
4361 while ( !( item
->fState
& MFS_DEFAULT
) )
4364 if (i
>= menu
->nItems
) return -1;
4367 /* default: don't return disabled items */
4368 if ( (!(GMDI_USEDISABLED
& flags
)) && (item
->fState
& MFS_DISABLED
)) return -1;
4370 /* search rekursiv when needed */
4371 if ( (item
->fType
& MF_POPUP
) && (flags
& GMDI_GOINTOPOPUPS
) )
4374 ret
= GetMenuDefaultItem( item
->hSubMenu
, bypos
, flags
);
4375 if ( -1 != ret
) return ret
;
4377 /* when item not found in submenu, return the popup item */
4379 return ( bypos
) ? i
: item
->wID
;
4384 /**********************************************************************
4385 * InsertMenuItemA (USER32.@)
4387 BOOL WINAPI
InsertMenuItemA(HMENU hMenu
, UINT uItem
, BOOL bypos
,
4388 const MENUITEMINFOA
*lpmii
)
4390 MENUITEM
*item
= MENU_InsertItem(hMenu
, uItem
, bypos
? MF_BYPOSITION
: 0 );
4391 return SetMenuItemInfo_common(item
, (const MENUITEMINFOW
*)lpmii
, FALSE
);
4395 /**********************************************************************
4396 * InsertMenuItemW (USER32.@)
4398 BOOL WINAPI
InsertMenuItemW(HMENU hMenu
, UINT uItem
, BOOL bypos
,
4399 const MENUITEMINFOW
*lpmii
)
4401 MENUITEM
*item
= MENU_InsertItem(hMenu
, uItem
, bypos
? MF_BYPOSITION
: 0 );
4402 return SetMenuItemInfo_common(item
, lpmii
, TRUE
);
4405 /**********************************************************************
4406 * CheckMenuRadioItem (USER32.@)
4409 BOOL WINAPI
CheckMenuRadioItem(HMENU hMenu
,
4410 UINT first
, UINT last
, UINT check
,
4413 MENUITEM
*mifirst
, *milast
, *micheck
;
4414 HMENU mfirst
= hMenu
, mlast
= hMenu
, mcheck
= hMenu
;
4416 TRACE("%p: %d-%d, check %d, bypos=%d\n", hMenu
, first
, last
, check
, bypos
);
4418 mifirst
= MENU_FindItem (&mfirst
, &first
, bypos
);
4419 milast
= MENU_FindItem (&mlast
, &last
, bypos
);
4420 micheck
= MENU_FindItem (&mcheck
, &check
, bypos
);
4422 if (mifirst
== NULL
|| milast
== NULL
|| micheck
== NULL
||
4423 mifirst
> milast
|| mfirst
!= mlast
|| mfirst
!= mcheck
||
4424 micheck
> milast
|| micheck
< mifirst
)
4427 while (mifirst
<= milast
)
4429 if (mifirst
== micheck
)
4431 mifirst
->fType
|= MFT_RADIOCHECK
;
4432 mifirst
->fState
|= MFS_CHECKED
;
4434 mifirst
->fType
&= ~MFT_RADIOCHECK
;
4435 mifirst
->fState
&= ~MFS_CHECKED
;
4444 /**********************************************************************
4445 * GetMenuItemRect (USER32.@)
4447 * ATTENTION: Here, the returned values in rect are the screen
4448 * coordinates of the item just like if the menu was
4449 * always on the upper left side of the application.
4452 BOOL WINAPI
GetMenuItemRect (HWND hwnd
, HMENU hMenu
, UINT uItem
,
4455 POPUPMENU
*itemMenu
;
4459 TRACE("(%p,%p,%d,%p)\n", hwnd
, hMenu
, uItem
, rect
);
4461 item
= MENU_FindItem (&hMenu
, &uItem
, MF_BYPOSITION
);
4462 referenceHwnd
= hwnd
;
4466 itemMenu
= MENU_GetMenu(hMenu
);
4467 if (itemMenu
== NULL
)
4470 if(itemMenu
->hWnd
== 0)
4472 referenceHwnd
= itemMenu
->hWnd
;
4475 if ((rect
== NULL
) || (item
== NULL
))
4480 MapWindowPoints(referenceHwnd
, 0, (LPPOINT
)rect
, 2);
4486 /**********************************************************************
4487 * SetMenuInfo (USER32.@)
4490 * MIM_APPLYTOSUBMENUS
4491 * actually use the items to draw the menu
4493 BOOL WINAPI
SetMenuInfo (HMENU hMenu
, LPCMENUINFO lpmi
)
4497 TRACE("(%p %p)\n", hMenu
, lpmi
);
4499 if (lpmi
&& (lpmi
->cbSize
==sizeof(MENUINFO
)) && (menu
= MENU_GetMenu(hMenu
)))
4502 if (lpmi
->fMask
& MIM_BACKGROUND
)
4503 menu
->hbrBack
= lpmi
->hbrBack
;
4505 if (lpmi
->fMask
& MIM_HELPID
)
4506 menu
->dwContextHelpID
= lpmi
->dwContextHelpID
;
4508 if (lpmi
->fMask
& MIM_MAXHEIGHT
)
4509 menu
->cyMax
= lpmi
->cyMax
;
4511 if (lpmi
->fMask
& MIM_MENUDATA
)
4512 menu
->dwMenuData
= lpmi
->dwMenuData
;
4514 if (lpmi
->fMask
& MIM_STYLE
)
4515 menu
->dwStyle
= lpmi
->dwStyle
;
4522 /**********************************************************************
4523 * GetMenuInfo (USER32.@)
4529 BOOL WINAPI
GetMenuInfo (HMENU hMenu
, LPMENUINFO lpmi
)
4532 TRACE("(%p %p)\n", hMenu
, lpmi
);
4534 if (lpmi
&& (menu
= MENU_GetMenu(hMenu
)))
4537 if (lpmi
->fMask
& MIM_BACKGROUND
)
4538 lpmi
->hbrBack
= menu
->hbrBack
;
4540 if (lpmi
->fMask
& MIM_HELPID
)
4541 lpmi
->dwContextHelpID
= menu
->dwContextHelpID
;
4543 if (lpmi
->fMask
& MIM_MAXHEIGHT
)
4544 lpmi
->cyMax
= menu
->cyMax
;
4546 if (lpmi
->fMask
& MIM_MENUDATA
)
4547 lpmi
->dwMenuData
= menu
->dwMenuData
;
4549 if (lpmi
->fMask
& MIM_STYLE
)
4550 lpmi
->dwStyle
= menu
->dwStyle
;
4558 /**********************************************************************
4559 * SetMenuContextHelpId (USER32.@)
4561 BOOL WINAPI
SetMenuContextHelpId( HMENU hMenu
, DWORD dwContextHelpID
)
4565 TRACE("(%p 0x%08lx)\n", hMenu
, dwContextHelpID
);
4567 if ((menu
= MENU_GetMenu(hMenu
)))
4569 menu
->dwContextHelpID
= dwContextHelpID
;
4576 /**********************************************************************
4577 * GetMenuContextHelpId (USER32.@)
4579 DWORD WINAPI
GetMenuContextHelpId( HMENU hMenu
)
4583 TRACE("(%p)\n", hMenu
);
4585 if ((menu
= MENU_GetMenu(hMenu
)))
4587 return menu
->dwContextHelpID
;
4592 /**********************************************************************
4593 * MenuItemFromPoint (USER32.@)
4595 UINT WINAPI
MenuItemFromPoint(HWND hWnd
, HMENU hMenu
, POINT ptScreen
)
4597 POPUPMENU
*menu
= MENU_GetMenu(hMenu
);
4601 /*FIXME: Do we have to handle hWnd here? */
4602 item
= MENU_FindItemByCoords(menu
, ptScreen
, &pos
);
4608 /**********************************************************************
4609 * translate_accelerator
4611 static BOOL
translate_accelerator( HWND hWnd
, UINT message
, WPARAM wParam
, LPARAM lParam
,
4612 BYTE fVirt
, WORD key
, WORD cmd
)
4616 if (wParam
!= key
) return FALSE
;
4618 if (message
== WM_CHAR
)
4620 if ( !(fVirt
& FALT
) && !(fVirt
& FVIRTKEY
) )
4622 TRACE_(accel
)("found accel for WM_CHAR: ('%c')\n", wParam
& 0xff);
4628 if(fVirt
& FVIRTKEY
)
4631 TRACE_(accel
)("found accel for virt_key %04x (scan %04x)\n",
4632 wParam
, 0xff & HIWORD(lParam
));
4633 if(GetKeyState(VK_SHIFT
) & 0x8000) mask
|= FSHIFT
;
4634 if(GetKeyState(VK_CONTROL
) & 0x8000) mask
|= FCONTROL
;
4635 if(GetKeyState(VK_MENU
) & 0x8000) mask
|= FALT
;
4636 if(mask
== (fVirt
& (FSHIFT
| FCONTROL
| FALT
))) goto found
;
4637 TRACE_(accel
)(", but incorrect SHIFT/CTRL/ALT-state\n");
4641 if (!(lParam
& 0x01000000)) /* no special_key */
4643 if ((fVirt
& FALT
) && (lParam
& 0x20000000))
4644 { /* ^^ ALT pressed */
4645 TRACE_(accel
)("found accel for Alt-%c\n", wParam
& 0xff);
4654 if (message
== WM_KEYUP
|| message
== WM_SYSKEYUP
)
4656 else if (GetCapture())
4658 else if (!IsWindowEnabled(hWnd
))
4662 HMENU hMenu
, hSubMenu
, hSysMenu
;
4663 UINT uSysStat
= (UINT
)-1, uStat
= (UINT
)-1, nPos
;
4665 hMenu
= (GetWindowLongW( hWnd
, GWL_STYLE
) & WS_CHILD
) ? 0 : GetMenu(hWnd
);
4666 hSysMenu
= get_win_sys_menu( hWnd
);
4668 /* find menu item and ask application to initialize it */
4669 /* 1. in the system menu */
4670 hSubMenu
= hSysMenu
;
4672 if(MENU_FindItem(&hSubMenu
, &nPos
, MF_BYCOMMAND
))
4674 SendMessageW(hWnd
, WM_INITMENU
, (WPARAM
)hSysMenu
, 0L);
4675 if(hSubMenu
!= hSysMenu
)
4677 nPos
= MENU_FindSubMenu(&hSysMenu
, hSubMenu
);
4678 TRACE_(accel
)("hSysMenu = %p, hSubMenu = %p, nPos = %d\n", hSysMenu
, hSubMenu
, nPos
);
4679 SendMessageW(hWnd
, WM_INITMENUPOPUP
, (WPARAM
)hSubMenu
, MAKELPARAM(nPos
, TRUE
));
4681 uSysStat
= GetMenuState(GetSubMenu(hSysMenu
, 0), cmd
, MF_BYCOMMAND
);
4683 else /* 2. in the window's menu */
4687 if(MENU_FindItem(&hSubMenu
, &nPos
, MF_BYCOMMAND
))
4689 SendMessageW(hWnd
, WM_INITMENU
, (WPARAM
)hMenu
, 0L);
4690 if(hSubMenu
!= hMenu
)
4692 nPos
= MENU_FindSubMenu(&hMenu
, hSubMenu
);
4693 TRACE_(accel
)("hMenu = %p, hSubMenu = %p, nPos = %d\n", hMenu
, hSubMenu
, nPos
);
4694 SendMessageW(hWnd
, WM_INITMENUPOPUP
, (WPARAM
)hSubMenu
, MAKELPARAM(nPos
, FALSE
));
4696 uStat
= GetMenuState(hMenu
, cmd
, MF_BYCOMMAND
);
4700 if (uSysStat
!= (UINT
)-1)
4702 if (uSysStat
& (MF_DISABLED
|MF_GRAYED
))
4709 if (uStat
!= (UINT
)-1)
4715 if (uStat
& (MF_DISABLED
|MF_GRAYED
))
4726 if( mesg
==WM_COMMAND
)
4728 TRACE_(accel
)(", sending WM_COMMAND, wParam=%0x\n", 0x10000 | cmd
);
4729 SendMessageW(hWnd
, mesg
, 0x10000 | cmd
, 0L);
4731 else if( mesg
==WM_SYSCOMMAND
)
4733 TRACE_(accel
)(", sending WM_SYSCOMMAND, wParam=%0x\n", cmd
);
4734 SendMessageW(hWnd
, mesg
, cmd
, 0x00010000L
);
4738 /* some reasons for NOT sending the WM_{SYS}COMMAND message:
4739 * #0: unknown (please report!)
4740 * #1: for WM_KEYUP,WM_SYSKEYUP
4741 * #2: mouse is captured
4742 * #3: window is disabled
4743 * #4: it's a disabled system menu option
4744 * #5: it's a menu option, but window is iconic
4745 * #6: it's a menu option, but disabled
4747 TRACE_(accel
)(", but won't send WM_{SYS}COMMAND, reason is #%d\n",mesg
);
4749 ERR_(accel
)(" unknown reason - please report!\n");
4754 /**********************************************************************
4755 * TranslateAccelerator (USER32.@)
4756 * TranslateAcceleratorA (USER32.@)
4757 * TranslateAcceleratorW (USER32.@)
4759 INT WINAPI
TranslateAcceleratorW( HWND hWnd
, HACCEL hAccel
, LPMSG msg
)
4762 LPACCEL16 lpAccelTbl
;
4767 WARN_(accel
)("msg null; should hang here to be win compatible\n");
4770 if (!hAccel
|| !(lpAccelTbl
= (LPACCEL16
) LockResource16(HACCEL_16(hAccel
))))
4772 WARN_(accel
)("invalid accel handle=%p\n", hAccel
);
4775 if ((msg
->message
!= WM_KEYDOWN
&&
4776 msg
->message
!= WM_KEYUP
&&
4777 msg
->message
!= WM_SYSKEYDOWN
&&
4778 msg
->message
!= WM_SYSKEYUP
&&
4779 msg
->message
!= WM_CHAR
)) return 0;
4781 TRACE_(accel
)("TranslateAccelerators hAccel=%p, hWnd=%p,"
4782 "msg->hwnd=%p, msg->message=%04x, wParam=%08x, lParam=%lx\n",
4783 hAccel
,hWnd
,msg
->hwnd
,msg
->message
,msg
->wParam
,msg
->lParam
);
4788 if (translate_accelerator( hWnd
, msg
->message
, msg
->wParam
, msg
->lParam
,
4789 lpAccelTbl
[i
].fVirt
, lpAccelTbl
[i
].key
, lpAccelTbl
[i
].cmd
))
4791 } while ((lpAccelTbl
[i
++].fVirt
& 0x80) == 0);
4792 WARN_(accel
)("couldn't translate accelerator key\n");