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"
40 #include "wine/winbase16.h"
41 #include "wine/winuser16.h"
43 #include "wine/server.h"
44 #include "wine/unicode.h"
47 #include "nonclient.h"
49 #include "wine/debug.h"
51 WINE_DEFAULT_DEBUG_CHANNEL(menu
);
52 WINE_DECLARE_DEBUG_CHANNEL(accel
);
54 /* internal popup menu window messages */
56 #define MM_SETMENUHANDLE (WM_USER + 0)
57 #define MM_GETMENUHANDLE (WM_USER + 1)
59 /* Menu item structure */
61 /* ----------- MENUITEMINFO Stuff ----------- */
62 UINT fType
; /* Item type. */
63 UINT fState
; /* Item state. */
64 UINT_PTR wID
; /* Item id. */
65 HMENU hSubMenu
; /* Pop-up menu. */
66 HBITMAP hCheckBit
; /* Bitmap when checked. */
67 HBITMAP hUnCheckBit
; /* Bitmap when unchecked. */
68 LPWSTR text
; /* Item text or bitmap handle. */
69 DWORD dwItemData
; /* Application defined. */
70 DWORD dwTypeData
; /* depends on fMask */
71 HBITMAP hbmpItem
; /* bitmap in win98 style menus */
72 /* ----------- Wine stuff ----------- */
73 RECT rect
; /* Item area (relative to menu window) */
74 UINT xTab
; /* X position of text after Tab */
77 /* Popup menu structure */
79 WORD wFlags
; /* Menu flags (MF_POPUP, MF_SYSMENU) */
80 WORD wMagic
; /* Magic number */
81 WORD Width
; /* Width of the whole menu */
82 WORD Height
; /* Height of the whole menu */
83 UINT nItems
; /* Number of items in the menu */
84 HWND hWnd
; /* Window containing the menu */
85 MENUITEM
*items
; /* Array of menu items */
86 UINT FocusedItem
; /* Currently focused item */
87 HWND hwndOwner
; /* window receiving the messages for ownerdraw */
88 BOOL bTimeToHide
; /* Request hiding when receiving a second click in the top-level menu item */
89 /* ------------ MENUINFO members ------ */
90 DWORD dwStyle
; /* Extended mennu style */
91 UINT cyMax
; /* max hight of the whole menu, 0 is screen hight */
92 HBRUSH hbrBack
; /* brush for menu background */
93 DWORD dwContextHelpID
;
94 DWORD dwMenuData
; /* application defined value */
95 HMENU hSysMenuOwner
; /* Handle to the dummy sys menu holder */
96 } POPUPMENU
, *LPPOPUPMENU
;
98 /* internal flags for menu tracking */
100 #define TF_ENDMENU 0x0001
101 #define TF_SUSPENDPOPUP 0x0002
102 #define TF_SKIPREMOVE 0x0004
107 HMENU hCurrentMenu
; /* current submenu (can be equal to hTopMenu)*/
108 HMENU hTopMenu
; /* initial menu */
109 HWND hOwnerWnd
; /* where notifications are sent */
113 #define MENU_MAGIC 0x554d /* 'MU' */
118 /* Internal MENU_TrackMenu() flags */
119 #define TPM_INTERNAL 0xF0000000
120 #define TPM_ENTERIDLEEX 0x80000000 /* set owner window for WM_ENTERIDLE */
121 #define TPM_BUTTONDOWN 0x40000000 /* menu was clicked before tracking */
122 #define TPM_POPUPMENU 0x20000000 /* menu is a popup menu */
124 /* popup menu shade thickness */
125 #define POPUP_XSHADE 4
126 #define POPUP_YSHADE 4
128 /* Space between 2 menu bar items */
129 #define MENU_BAR_ITEMS_SPACE 12
131 /* Minimum width of a tab character */
132 #define MENU_TAB_SPACE 8
134 /* Height of a separator item */
135 #define SEPARATOR_HEIGHT 5
137 /* (other menu->FocusedItem values give the position of the focused item) */
138 #define NO_SELECTED_ITEM 0xffff
140 #define MENU_ITEM_TYPE(flags) \
141 ((flags) & (MF_STRING | MF_BITMAP | MF_OWNERDRAW | MF_SEPARATOR))
143 #define IS_STRING_ITEM(flags) (MENU_ITEM_TYPE ((flags)) == MF_STRING)
144 #define IS_BITMAP_ITEM(flags) (MENU_ITEM_TYPE ((flags)) == MF_BITMAP)
145 #define IS_MAGIC_ITEM(text) (LOWORD((int)text)<12)
147 #define IS_SYSTEM_MENU(menu) \
148 (!((menu)->wFlags & MF_POPUP) && (menu)->wFlags & MF_SYSMENU)
150 #define IS_SYSTEM_POPUP(menu) \
151 ((menu)->wFlags & MF_POPUP && (menu)->wFlags & MF_SYSMENU)
153 #define TYPE_MASK (MFT_STRING | MFT_BITMAP | MFT_OWNERDRAW | MFT_SEPARATOR | \
154 MFT_MENUBARBREAK | MFT_MENUBREAK | MFT_RADIOCHECK | \
155 MFT_RIGHTORDER | MFT_RIGHTJUSTIFY | \
156 MF_POPUP | MF_SYSMENU | MF_HELP)
157 #define STATE_MASK (~TYPE_MASK)
159 /* Dimension of the menu bitmaps */
160 static WORD arrow_bitmap_width
= 0, arrow_bitmap_height
= 0;
162 static HBITMAP hStdMnArrow
= 0;
163 static HBITMAP hBmpSysMenu
= 0;
165 static HBRUSH hShadeBrush
= 0;
166 static HFONT hMenuFont
= 0;
167 static HFONT hMenuFontBold
= 0;
169 static HMENU MENU_DefSysPopup
= 0; /* Default system menu popup */
171 /* Use global popup window because there's no way 2 menus can
172 * be tracked at the same time. */
173 static HWND top_popup
;
175 /* Flag set by EndMenu() to force an exit from menu tracking */
176 static BOOL fEndMenu
= FALSE
;
178 static LRESULT WINAPI
PopupMenuWndProc( HWND hwnd
, UINT message
, WPARAM wParam
, LPARAM lParam
);
181 /*********************************************************************
182 * menu class descriptor
184 const struct builtin_class_descr MENU_builtin_class
=
186 POPUPMENU_CLASS_ATOM
, /* name */
187 CS_GLOBALCLASS
| CS_SAVEBITS
, /* style */
188 NULL
, /* procA (winproc is Unicode only) */
189 PopupMenuWndProc
, /* procW */
190 sizeof(HMENU
), /* extra */
191 IDC_ARROWA
, /* 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 HMENU hMenu
= LoadMenuA(GetModuleHandleA("USER32"), "SYSMENU");
334 POPUPMENU
* menu
= MENU_GetMenu(hMenu
);
335 menu
->wFlags
|= MF_SYSMENU
| MF_POPUP
;
336 SetMenuDefaultItem(hMenu
, SC_CLOSE
, FALSE
);
339 ERR("Unable to load default system menu\n" );
341 TRACE("returning %p.\n", hMenu
);
347 /**********************************************************************
350 * Create a copy of the system menu. System menu in Windows is
351 * a special menu bar with the single entry - system menu popup.
352 * This popup is presented to the outside world as a "system menu".
353 * However, the real system menu handle is sometimes seen in the
354 * WM_MENUSELECT parameters (and Word 6 likes it this way).
356 HMENU
MENU_GetSysMenu( HWND hWnd
, HMENU hPopupMenu
)
360 TRACE("loading system menu, hWnd %p, hPopupMenu %p\n", hWnd
, hPopupMenu
);
361 if ((hMenu
= CreateMenu()))
363 POPUPMENU
*menu
= MENU_GetMenu(hMenu
);
364 menu
->wFlags
= MF_SYSMENU
;
365 menu
->hWnd
= WIN_GetFullHandle( hWnd
);
366 TRACE("hWnd %p (hMenu %p)\n", menu
->hWnd
, hMenu
);
368 if (hPopupMenu
== (HMENU
)(-1))
369 hPopupMenu
= MENU_CopySysPopup();
370 else if( !hPopupMenu
) hPopupMenu
= MENU_DefSysPopup
;
374 InsertMenuA( hMenu
, -1, MF_SYSMENU
| MF_POPUP
| MF_BYPOSITION
,
375 (UINT_PTR
)hPopupMenu
, NULL
);
377 menu
->items
[0].fType
= MF_SYSMENU
| MF_POPUP
;
378 menu
->items
[0].fState
= 0;
379 if ((menu
= MENU_GetMenu(hPopupMenu
))) menu
->wFlags
|= MF_SYSMENU
;
381 TRACE("hMenu=%p (hPopup %p)\n", hMenu
, hPopupMenu
);
384 DestroyMenu( hMenu
);
386 ERR("failed to load system menu!\n");
391 /***********************************************************************
394 * Menus initialisation.
399 NONCLIENTMETRICSA ncm
;
401 static unsigned char shade_bits
[16] = { 0x55, 0, 0xAA, 0,
406 /* Load menu bitmaps */
407 hStdMnArrow
= LoadBitmapA(0, MAKEINTRESOURCEA(OBM_MNARROW
));
408 /* Load system buttons bitmaps */
409 hBmpSysMenu
= LoadBitmapA(0, MAKEINTRESOURCEA(OBM_CLOSE
));
414 GetObjectA( hStdMnArrow
, sizeof(bm
), &bm
);
415 arrow_bitmap_width
= bm
.bmWidth
;
416 arrow_bitmap_height
= bm
.bmHeight
;
420 if (! (hBitmap
= CreateBitmap( 8, 8, 1, 1, shade_bits
)))
423 if(!(hShadeBrush
= CreatePatternBrush( hBitmap
)))
426 DeleteObject( hBitmap
);
427 if (!(MENU_DefSysPopup
= MENU_CopySysPopup()))
430 ncm
.cbSize
= sizeof (NONCLIENTMETRICSA
);
431 if (!(SystemParametersInfoA(SPI_GETNONCLIENTMETRICS
, sizeof(NONCLIENTMETRICSA
), &ncm
, 0)))
434 if (!(hMenuFont
= CreateFontIndirectA( &ncm
.lfMenuFont
)))
437 ncm
.lfMenuFont
.lfWeight
+= 300;
438 if ( ncm
.lfMenuFont
.lfWeight
> 1000)
439 ncm
.lfMenuFont
.lfWeight
= 1000;
441 if (!(hMenuFontBold
= CreateFontIndirectA( &ncm
.lfMenuFont
)))
447 /***********************************************************************
448 * MENU_InitSysMenuPopup
450 * Grey the appropriate items in System menu.
452 static void MENU_InitSysMenuPopup( HMENU hmenu
, DWORD style
, DWORD clsStyle
)
456 gray
= !(style
& WS_THICKFRAME
) || (style
& (WS_MAXIMIZE
| WS_MINIMIZE
));
457 EnableMenuItem( hmenu
, SC_SIZE
, (gray
? MF_GRAYED
: MF_ENABLED
) );
458 gray
= ((style
& WS_MAXIMIZE
) != 0);
459 EnableMenuItem( hmenu
, SC_MOVE
, (gray
? MF_GRAYED
: MF_ENABLED
) );
460 gray
= !(style
& WS_MINIMIZEBOX
) || (style
& WS_MINIMIZE
);
461 EnableMenuItem( hmenu
, SC_MINIMIZE
, (gray
? MF_GRAYED
: MF_ENABLED
) );
462 gray
= !(style
& WS_MAXIMIZEBOX
) || (style
& WS_MAXIMIZE
);
463 EnableMenuItem( hmenu
, SC_MAXIMIZE
, (gray
? MF_GRAYED
: MF_ENABLED
) );
464 gray
= !(style
& (WS_MAXIMIZE
| WS_MINIMIZE
));
465 EnableMenuItem( hmenu
, SC_RESTORE
, (gray
? MF_GRAYED
: MF_ENABLED
) );
466 gray
= (clsStyle
& CS_NOCLOSE
) != 0;
468 /* The menu item must keep its state if it's disabled */
470 EnableMenuItem( hmenu
, SC_CLOSE
, MF_GRAYED
);
474 /******************************************************************************
476 * UINT MENU_GetStartOfNextColumn(
479 *****************************************************************************/
481 static UINT
MENU_GetStartOfNextColumn(
484 POPUPMENU
*menu
= MENU_GetMenu(hMenu
);
488 return NO_SELECTED_ITEM
;
490 i
= menu
->FocusedItem
+ 1;
491 if( i
== NO_SELECTED_ITEM
)
494 for( ; i
< menu
->nItems
; ++i
) {
495 if (menu
->items
[i
].fType
& MF_MENUBARBREAK
)
499 return NO_SELECTED_ITEM
;
503 /******************************************************************************
505 * UINT MENU_GetStartOfPrevColumn(
508 *****************************************************************************/
510 static UINT
MENU_GetStartOfPrevColumn(
513 POPUPMENU
*menu
= MENU_GetMenu(hMenu
);
517 return NO_SELECTED_ITEM
;
519 if( menu
->FocusedItem
== 0 || menu
->FocusedItem
== NO_SELECTED_ITEM
)
520 return NO_SELECTED_ITEM
;
522 /* Find the start of the column */
524 for(i
= menu
->FocusedItem
; i
!= 0 &&
525 !(menu
->items
[i
].fType
& MF_MENUBARBREAK
);
529 return NO_SELECTED_ITEM
;
531 for(--i
; i
!= 0; --i
) {
532 if (menu
->items
[i
].fType
& MF_MENUBARBREAK
)
536 TRACE("ret %d.\n", i
);
543 /***********************************************************************
546 * Find a menu item. Return a pointer on the item, and modifies *hmenu
547 * in case the item was in a sub-menu.
549 static MENUITEM
*MENU_FindItem( HMENU
*hmenu
, UINT
*nPos
, UINT wFlags
)
554 if ((*hmenu
== (HMENU
)0xffff) || (!(menu
= MENU_GetMenu(*hmenu
)))) return NULL
;
555 if (wFlags
& MF_BYPOSITION
)
557 if (*nPos
>= menu
->nItems
) return NULL
;
558 return &menu
->items
[*nPos
];
562 MENUITEM
*item
= menu
->items
;
563 for (i
= 0; i
< menu
->nItems
; i
++, item
++)
565 if (item
->wID
== *nPos
)
570 else if (item
->fType
& MF_POPUP
)
572 HMENU hsubmenu
= item
->hSubMenu
;
573 MENUITEM
*subitem
= MENU_FindItem( &hsubmenu
, nPos
, wFlags
);
585 /***********************************************************************
588 * Find a Sub menu. Return the position of the submenu, and modifies
589 * *hmenu in case it is found in another sub-menu.
590 * If the submenu cannot be found, NO_SELECTED_ITEM is returned.
592 UINT
MENU_FindSubMenu( HMENU
*hmenu
, HMENU hSubTarget
)
597 if (((*hmenu
)==(HMENU
)0xffff) ||
598 (!(menu
= MENU_GetMenu(*hmenu
))))
599 return NO_SELECTED_ITEM
;
601 for (i
= 0; i
< menu
->nItems
; i
++, item
++) {
602 if(!(item
->fType
& MF_POPUP
)) continue;
603 if (item
->hSubMenu
== hSubTarget
) {
607 HMENU hsubmenu
= item
->hSubMenu
;
608 UINT pos
= MENU_FindSubMenu( &hsubmenu
, hSubTarget
);
609 if (pos
!= NO_SELECTED_ITEM
) {
615 return NO_SELECTED_ITEM
;
618 /***********************************************************************
621 static void MENU_FreeItemData( MENUITEM
* item
)
624 if (IS_STRING_ITEM(item
->fType
) && item
->text
)
625 HeapFree( GetProcessHeap(), 0, item
->text
);
628 /***********************************************************************
629 * MENU_FindItemByCoords
631 * Find the item at the specified coordinates (screen coords). Does
632 * not work for child windows and therefore should not be called for
633 * an arbitrary system menu.
635 static MENUITEM
*MENU_FindItemByCoords( POPUPMENU
*menu
,
636 POINT pt
, UINT
*pos
)
642 if (!GetWindowRect(menu
->hWnd
,&wrect
)) return NULL
;
643 pt
.x
-= wrect
.left
;pt
.y
-= wrect
.top
;
645 for (i
= 0; i
< menu
->nItems
; i
++, item
++)
647 if ((pt
.x
>= item
->rect
.left
) && (pt
.x
< item
->rect
.right
) &&
648 (pt
.y
>= item
->rect
.top
) && (pt
.y
< item
->rect
.bottom
))
658 /***********************************************************************
661 * Find the menu item selected by a key press.
662 * Return item id, -1 if none, -2 if we should close the menu.
664 static UINT
MENU_FindItemByKey( HWND hwndOwner
, HMENU hmenu
,
665 UINT key
, BOOL forceMenuChar
)
667 TRACE("\tlooking for '%c' in [%p]\n", (char)key
, hmenu
);
669 if (!IsMenu( hmenu
)) hmenu
= GetSubMenu( get_win_sys_menu(hwndOwner
), 0);
673 POPUPMENU
*menu
= MENU_GetMenu( hmenu
);
674 MENUITEM
*item
= menu
->items
;
682 for (i
= 0; i
< menu
->nItems
; i
++, item
++)
684 if (IS_STRING_ITEM(item
->fType
) && item
->text
)
686 WCHAR
*p
= item
->text
- 2;
689 p
= strchrW (p
+ 2, '&');
691 while (p
!= NULL
&& p
[1] == '&');
692 if (p
&& (toupper(p
[1]) == key
)) return i
;
696 menuchar
= SendMessageA( hwndOwner
, WM_MENUCHAR
,
697 MAKEWPARAM( key
, menu
->wFlags
), (LPARAM
)hmenu
);
698 if (HIWORD(menuchar
) == 2) return LOWORD(menuchar
);
699 if (HIWORD(menuchar
) == 1) return (UINT
)(-2);
705 /***********************************************************************
706 * MENU_GetBitmapItemSize
708 * Get the size of a bitmap item.
710 static void MENU_GetBitmapItemSize( UINT id
, DWORD data
, SIZE
*size
)
713 HBITMAP bmp
= (HBITMAP
)id
;
715 size
->cx
= size
->cy
= 0;
717 /* check if there is a magic menu item associated with this item */
718 if (id
&& IS_MAGIC_ITEM( id
))
722 case (INT_PTR
)HBMMENU_SYSTEM
:
729 case (INT_PTR
)HBMMENU_MBAR_RESTORE
:
730 case (INT_PTR
)HBMMENU_MBAR_MINIMIZE
:
731 case (INT_PTR
)HBMMENU_MBAR_MINIMIZE_D
:
732 case (INT_PTR
)HBMMENU_MBAR_CLOSE
:
733 case (INT_PTR
)HBMMENU_MBAR_CLOSE_D
:
734 size
->cx
= GetSystemMetrics( SM_CXSIZE
);
735 size
->cy
= GetSystemMetrics( SM_CYSIZE
);
737 case (INT_PTR
)HBMMENU_CALLBACK
:
738 case (INT_PTR
)HBMMENU_POPUP_CLOSE
:
739 case (INT_PTR
)HBMMENU_POPUP_RESTORE
:
740 case (INT_PTR
)HBMMENU_POPUP_MAXIMIZE
:
741 case (INT_PTR
)HBMMENU_POPUP_MINIMIZE
:
743 FIXME("Magic 0x%08x not implemented\n", id
);
747 if (GetObjectA(bmp
, sizeof(bm
), &bm
))
749 size
->cx
= bm
.bmWidth
;
750 size
->cy
= bm
.bmHeight
;
754 /***********************************************************************
755 * MENU_DrawBitmapItem
757 * Draw a bitmap item.
759 static void MENU_DrawBitmapItem( HDC hdc
, MENUITEM
*lpitem
, const RECT
*rect
, BOOL menuBar
)
764 HBITMAP bmp
= (HBITMAP
)lpitem
->text
;
765 int w
= rect
->right
- rect
->left
;
766 int h
= rect
->bottom
- rect
->top
;
770 /* Check if there is a magic menu item associated with this item */
771 if (lpitem
->text
&& IS_MAGIC_ITEM(lpitem
->text
))
776 switch(LOWORD(lpitem
->text
))
778 case (INT_PTR
)HBMMENU_SYSTEM
:
779 if (lpitem
->dwItemData
)
781 bmp
= (HBITMAP
)lpitem
->dwItemData
;
782 if (!GetObjectA( bmp
, sizeof(bm
), &bm
)) return;
787 if (!GetObjectA( bmp
, sizeof(bm
), &bm
)) return;
788 /* only use right half of the bitmap */
789 bmp_xoffset
= bm
.bmWidth
/ 2;
790 bm
.bmWidth
-= bmp_xoffset
;
793 case (INT_PTR
)HBMMENU_MBAR_RESTORE
:
794 flags
= DFCS_CAPTIONRESTORE
;
796 case (INT_PTR
)HBMMENU_MBAR_MINIMIZE
:
797 flags
= DFCS_CAPTIONMIN
;
799 case (INT_PTR
)HBMMENU_MBAR_MINIMIZE_D
:
800 flags
= DFCS_CAPTIONMIN
| DFCS_INACTIVE
;
802 case (INT_PTR
)HBMMENU_MBAR_CLOSE
:
803 flags
= DFCS_CAPTIONCLOSE
;
805 case (INT_PTR
)HBMMENU_MBAR_CLOSE_D
:
806 flags
= DFCS_CAPTIONCLOSE
| DFCS_INACTIVE
;
808 case (INT_PTR
)HBMMENU_CALLBACK
:
809 case (INT_PTR
)HBMMENU_POPUP_CLOSE
:
810 case (INT_PTR
)HBMMENU_POPUP_RESTORE
:
811 case (INT_PTR
)HBMMENU_POPUP_MAXIMIZE
:
812 case (INT_PTR
)HBMMENU_POPUP_MINIMIZE
:
814 FIXME("Magic 0x%08x not implemented\n", LOWORD(lpitem
->text
));
818 InflateRect( &r
, -1, -1 );
819 if (lpitem
->fState
& MF_HILITE
) flags
|= DFCS_PUSHED
;
820 DrawFrameControl( hdc
, &r
, DFC_CAPTION
, flags
);
824 if (!bmp
|| !GetObjectA( bmp
, sizeof(bm
), &bm
)) return;
827 hdcMem
= CreateCompatibleDC( hdc
);
828 SelectObject( hdcMem
, bmp
);
830 /* handle fontsize > bitmap_height */
831 top
= (h
>bm
.bmHeight
) ? rect
->top
+(h
-bm
.bmHeight
)/2 : rect
->top
;
833 if (TWEAK_WineLook
== WIN95_LOOK
) {
834 rop
=((lpitem
->fState
& MF_HILITE
) && !IS_MAGIC_ITEM(lpitem
->text
)) ? NOTSRCCOPY
: SRCCOPY
;
835 if ((lpitem
->fState
& MF_HILITE
) && IS_BITMAP_ITEM(lpitem
->fType
))
836 SetBkColor(hdc
, GetSysColor(COLOR_HIGHLIGHT
));
840 rop
=((lpitem
->fState
& MF_HILITE
) && !IS_MAGIC_ITEM(lpitem
->text
) && (!menuBar
)) ? MERGEPAINT
: SRCCOPY
;
842 BitBlt( hdc
, left
, top
, w
, h
, hdcMem
, bmp_xoffset
, 0, rop
);
847 /***********************************************************************
850 * Calculate the size of the menu item and store it in lpitem->rect.
852 static void MENU_CalcItemSize( HDC hdc
, MENUITEM
*lpitem
, HWND hwndOwner
,
853 INT orgX
, INT orgY
, BOOL menuBar
)
856 UINT check_bitmap_width
= GetSystemMetrics( SM_CXMENUCHECK
);
858 TRACE("dc=%p owner=%p (%d,%d)\n", hdc
, hwndOwner
, orgX
, orgY
);
859 debug_print_menuitem("MENU_CalcItemSize: menuitem:", lpitem
,
860 (menuBar
? " (MenuBar)" : ""));
862 SetRect( &lpitem
->rect
, orgX
, orgY
, orgX
, orgY
);
864 if (lpitem
->fType
& MF_OWNERDRAW
)
867 ** Experimentation under Windows reveals that an owner-drawn
868 ** menu is expected to return the size of the content part of
869 ** the menu item, not including the checkmark nor the submenu
870 ** arrow. Windows adds those values itself and returns the
871 ** enlarged rectangle on subsequent WM_DRAWITEM messages.
873 MEASUREITEMSTRUCT mis
;
874 mis
.CtlType
= ODT_MENU
;
876 mis
.itemID
= lpitem
->wID
;
877 mis
.itemData
= (DWORD
)lpitem
->dwItemData
;
880 SendMessageA( hwndOwner
, WM_MEASUREITEM
, 0, (LPARAM
)&mis
);
881 lpitem
->rect
.right
+= mis
.itemWidth
;
885 lpitem
->rect
.right
+= MENU_BAR_ITEMS_SPACE
;
888 /* under at least win95 you seem to be given a standard
889 height for the menu and the height value is ignored */
891 if (TWEAK_WineLook
== WIN31_LOOK
)
892 lpitem
->rect
.bottom
+= GetSystemMetrics(SM_CYMENU
);
894 lpitem
->rect
.bottom
+= GetSystemMetrics(SM_CYMENU
)-1;
897 lpitem
->rect
.bottom
+= mis
.itemHeight
;
899 TRACE("id=%04x size=%dx%d\n",
900 lpitem
->wID
, mis
.itemWidth
, mis
.itemHeight
);
901 /* Fall through to get check/arrow width calculation. */
904 if (lpitem
->fType
& MF_SEPARATOR
)
906 lpitem
->rect
.bottom
+= SEPARATOR_HEIGHT
;
912 lpitem
->rect
.right
+= 2 * check_bitmap_width
;
913 if (lpitem
->fType
& MF_POPUP
)
914 lpitem
->rect
.right
+= arrow_bitmap_width
;
917 if (lpitem
->fType
& MF_OWNERDRAW
)
920 if (IS_BITMAP_ITEM(lpitem
->fType
))
924 MENU_GetBitmapItemSize( (int)lpitem
->text
, lpitem
->dwItemData
, &size
);
925 lpitem
->rect
.right
+= size
.cx
;
926 lpitem
->rect
.bottom
+= size
.cy
;
927 if (TWEAK_WineLook
== WIN98_LOOK
)
929 /* Leave space for the sunken border */
930 lpitem
->rect
.right
+= 2;
931 lpitem
->rect
.bottom
+= 2;
936 /* it must be a text item - unless it's the system menu */
937 if (!(lpitem
->fType
& MF_SYSMENU
) && IS_STRING_ITEM( lpitem
->fType
))
940 GetTextExtentPoint32W(hdc
, lpitem
->text
, strlenW(lpitem
->text
), &size
);
942 lpitem
->rect
.right
+= size
.cx
;
943 if (TWEAK_WineLook
== WIN31_LOOK
)
944 lpitem
->rect
.bottom
+= max( size
.cy
, GetSystemMetrics(SM_CYMENU
) );
946 lpitem
->rect
.bottom
+= max(size
.cy
, GetSystemMetrics(SM_CYMENU
)-1);
951 lpitem
->rect
.right
+= MENU_BAR_ITEMS_SPACE
;
953 else if ((p
= strchrW( lpitem
->text
, '\t' )) != NULL
)
955 /* Item contains a tab (only meaningful in popup menus) */
956 GetTextExtentPoint32W(hdc
, lpitem
->text
, (int)(p
- lpitem
->text
) , &size
);
957 lpitem
->xTab
= check_bitmap_width
+ MENU_TAB_SPACE
+ size
.cx
;
958 lpitem
->rect
.right
+= MENU_TAB_SPACE
;
962 if (strchrW( lpitem
->text
, '\b' ))
963 lpitem
->rect
.right
+= MENU_TAB_SPACE
;
964 lpitem
->xTab
= lpitem
->rect
.right
- check_bitmap_width
965 - arrow_bitmap_width
;
968 TRACE("(%ld,%ld)-(%ld,%ld)\n", lpitem
->rect
.left
, lpitem
->rect
.top
, lpitem
->rect
.right
, lpitem
->rect
.bottom
);
972 /***********************************************************************
973 * MENU_PopupMenuCalcSize
975 * Calculate the size of a popup menu.
977 static void MENU_PopupMenuCalcSize( LPPOPUPMENU lppop
, HWND hwndOwner
)
982 int orgX
, orgY
, maxX
, maxTab
, maxTabWidth
;
984 lppop
->Width
= lppop
->Height
= 0;
985 if (lppop
->nItems
== 0) return;
988 SelectObject( hdc
, hMenuFont
);
991 maxX
= (TWEAK_WineLook
== WIN31_LOOK
) ? GetSystemMetrics(SM_CXBORDER
) : 2+1 ;
993 while (start
< lppop
->nItems
)
995 lpitem
= &lppop
->items
[start
];
997 orgY
= (TWEAK_WineLook
== WIN31_LOOK
) ? GetSystemMetrics(SM_CYBORDER
) : 2;
999 maxTab
= maxTabWidth
= 0;
1001 /* Parse items until column break or end of menu */
1002 for (i
= start
; i
< lppop
->nItems
; i
++, lpitem
++)
1005 (lpitem
->fType
& (MF_MENUBREAK
| MF_MENUBARBREAK
))) break;
1007 MENU_CalcItemSize( hdc
, lpitem
, hwndOwner
, orgX
, orgY
, FALSE
);
1009 if (lpitem
->fType
& MF_MENUBARBREAK
) orgX
++;
1010 maxX
= max( maxX
, lpitem
->rect
.right
);
1011 orgY
= lpitem
->rect
.bottom
;
1012 if (IS_STRING_ITEM(lpitem
->fType
) && lpitem
->xTab
)
1014 maxTab
= max( maxTab
, lpitem
->xTab
);
1015 maxTabWidth
= max(maxTabWidth
,lpitem
->rect
.right
-lpitem
->xTab
);
1019 /* Finish the column (set all items to the largest width found) */
1020 maxX
= max( maxX
, maxTab
+ maxTabWidth
);
1021 for (lpitem
= &lppop
->items
[start
]; start
< i
; start
++, lpitem
++)
1023 lpitem
->rect
.right
= maxX
;
1024 if (IS_STRING_ITEM(lpitem
->fType
) && lpitem
->xTab
)
1025 lpitem
->xTab
= maxTab
;
1028 lppop
->Height
= max( lppop
->Height
, orgY
);
1031 lppop
->Width
= maxX
;
1033 /* space for 3d border */
1034 if(TWEAK_WineLook
> WIN31_LOOK
)
1040 ReleaseDC( 0, hdc
);
1044 /***********************************************************************
1045 * MENU_MenuBarCalcSize
1047 * FIXME: Word 6 implements its own MDI and its own 'close window' bitmap
1048 * height is off by 1 pixel which causes lengthy window relocations when
1049 * active document window is maximized/restored.
1051 * Calculate the size of the menu bar.
1053 static void MENU_MenuBarCalcSize( HDC hdc
, LPRECT lprect
,
1054 LPPOPUPMENU lppop
, HWND hwndOwner
)
1057 int start
, i
, orgX
, orgY
, maxY
, helpPos
;
1059 if ((lprect
== NULL
) || (lppop
== NULL
)) return;
1060 if (lppop
->nItems
== 0) return;
1061 TRACE("left=%ld top=%ld right=%ld bottom=%ld\n",
1062 lprect
->left
, lprect
->top
, lprect
->right
, lprect
->bottom
);
1063 lppop
->Width
= lprect
->right
- lprect
->left
;
1065 maxY
= lprect
->top
+1;
1068 while (start
< lppop
->nItems
)
1070 lpitem
= &lppop
->items
[start
];
1071 orgX
= lprect
->left
;
1074 /* Parse items until line break or end of menu */
1075 for (i
= start
; i
< lppop
->nItems
; i
++, lpitem
++)
1077 if ((helpPos
== -1) && (lpitem
->fType
& MF_RIGHTJUSTIFY
)) helpPos
= i
;
1079 (lpitem
->fType
& (MF_MENUBREAK
| MF_MENUBARBREAK
))) break;
1081 TRACE("calling MENU_CalcItemSize org=(%d, %d)\n",
1083 debug_print_menuitem (" item: ", lpitem
, "");
1084 MENU_CalcItemSize( hdc
, lpitem
, hwndOwner
, orgX
, orgY
, TRUE
);
1086 if (lpitem
->rect
.right
> lprect
->right
)
1088 if (i
!= start
) break;
1089 else lpitem
->rect
.right
= lprect
->right
;
1091 maxY
= max( maxY
, lpitem
->rect
.bottom
);
1092 orgX
= lpitem
->rect
.right
;
1095 /* Finish the line (set all items to the largest height found) */
1096 while (start
< i
) lppop
->items
[start
++].rect
.bottom
= maxY
;
1099 lprect
->bottom
= maxY
;
1100 lppop
->Height
= lprect
->bottom
- lprect
->top
;
1102 /* Flush right all items between the MF_RIGHTJUSTIFY and */
1103 /* the last item (if several lines, only move the last line) */
1104 lpitem
= &lppop
->items
[lppop
->nItems
-1];
1105 orgY
= lpitem
->rect
.top
;
1106 orgX
= lprect
->right
;
1107 for (i
= lppop
->nItems
- 1; i
>= helpPos
; i
--, lpitem
--) {
1108 if ( (helpPos
==-1) || (helpPos
>i
) )
1110 if (lpitem
->rect
.top
!= orgY
) break; /* Other line */
1111 if (lpitem
->rect
.right
>= orgX
) break; /* Too far right already */
1112 lpitem
->rect
.left
+= orgX
- lpitem
->rect
.right
;
1113 lpitem
->rect
.right
= orgX
;
1114 orgX
= lpitem
->rect
.left
;
1118 /***********************************************************************
1121 * Draw a single menu item.
1123 static void MENU_DrawMenuItem( HWND hwnd
, HMENU hmenu
, HWND hwndOwner
, HDC hdc
, MENUITEM
*lpitem
,
1124 UINT height
, BOOL menuBar
, UINT odaction
)
1128 debug_print_menuitem("MENU_DrawMenuItem: ", lpitem
, "");
1130 if (lpitem
->fType
& MF_SYSMENU
)
1132 if( !IsIconic(hwnd
) ) {
1133 if (TWEAK_WineLook
> WIN31_LOOK
)
1134 NC_DrawSysButton95( hwnd
, hdc
,
1136 (MF_HILITE
| MF_MOUSESELECT
) );
1138 NC_DrawSysButton( hwnd
, hdc
,
1140 (MF_HILITE
| MF_MOUSESELECT
) );
1146 if (lpitem
->fType
& MF_OWNERDRAW
)
1149 ** Experimentation under Windows reveals that an owner-drawn
1150 ** menu is given the rectangle which includes the space it requested
1151 ** in its response to WM_MEASUREITEM _plus_ width for a checkmark
1152 ** and a popup-menu arrow. This is the value of lpitem->rect.
1153 ** Windows will leave all drawing to the application except for
1154 ** the popup-menu arrow. Windows always draws that itself, after
1155 ** the menu owner has finished drawing.
1159 dis
.CtlType
= ODT_MENU
;
1161 dis
.itemID
= lpitem
->wID
;
1162 dis
.itemData
= (DWORD
)lpitem
->dwItemData
;
1164 if (lpitem
->fState
& MF_CHECKED
) dis
.itemState
|= ODS_CHECKED
;
1165 if (lpitem
->fState
& MF_GRAYED
) dis
.itemState
|= ODS_GRAYED
|ODS_DISABLED
;
1166 if (lpitem
->fState
& MF_HILITE
) dis
.itemState
|= ODS_SELECTED
;
1167 dis
.itemAction
= odaction
; /* ODA_DRAWENTIRE | ODA_SELECT | ODA_FOCUS; */
1168 dis
.hwndItem
= (HWND
)hmenu
;
1170 dis
.rcItem
= lpitem
->rect
;
1171 TRACE("Ownerdraw: owner=%p itemID=%d, itemState=%d, itemAction=%d, "
1172 "hwndItem=%p, hdc=%p, rcItem={%ld,%ld,%ld,%ld}\n", hwndOwner
,
1173 dis
.itemID
, dis
.itemState
, dis
.itemAction
, dis
.hwndItem
,
1174 dis
.hDC
, dis
.rcItem
.left
, dis
.rcItem
.top
, dis
.rcItem
.right
,
1176 SendMessageA( hwndOwner
, WM_DRAWITEM
, 0, (LPARAM
)&dis
);
1177 /* Fall through to draw popup-menu arrow */
1180 TRACE("rect={%ld,%ld,%ld,%ld}\n", lpitem
->rect
.left
, lpitem
->rect
.top
,
1181 lpitem
->rect
.right
,lpitem
->rect
.bottom
);
1183 if (menuBar
&& (lpitem
->fType
& MF_SEPARATOR
)) return;
1185 rect
= lpitem
->rect
;
1187 if (!(lpitem
->fType
& MF_OWNERDRAW
))
1189 if (lpitem
->fState
& MF_HILITE
)
1191 if(TWEAK_WineLook
== WIN98_LOOK
)
1194 DrawEdge(hdc
, &rect
, BDR_SUNKENOUTER
, BF_RECT
);
1196 FillRect(hdc
, &rect
, GetSysColorBrush(COLOR_HIGHLIGHT
));
1198 else /* Not Win98 Look */
1200 if(!IS_BITMAP_ITEM(lpitem
->fType
))
1201 FillRect(hdc
, &rect
, GetSysColorBrush(COLOR_HIGHLIGHT
));
1205 FillRect( hdc
, &rect
, GetSysColorBrush(COLOR_MENU
) );
1208 SetBkMode( hdc
, TRANSPARENT
);
1210 if (!(lpitem
->fType
& MF_OWNERDRAW
))
1212 /* vertical separator */
1213 if (!menuBar
&& (lpitem
->fType
& MF_MENUBARBREAK
))
1215 if (TWEAK_WineLook
> WIN31_LOOK
)
1219 rc
.bottom
= height
- 3;
1220 DrawEdge (hdc
, &rc
, EDGE_ETCHED
, BF_LEFT
);
1224 SelectObject( hdc
, SYSCOLOR_GetPen(COLOR_WINDOWFRAME
) );
1225 MoveToEx( hdc
, rect
.left
, 0, NULL
);
1226 LineTo( hdc
, rect
.left
, height
);
1230 /* horizontal separator */
1231 if (lpitem
->fType
& MF_SEPARATOR
)
1233 if (TWEAK_WineLook
> WIN31_LOOK
)
1238 rc
.top
+= SEPARATOR_HEIGHT
/ 2;
1239 DrawEdge (hdc
, &rc
, EDGE_ETCHED
, BF_TOP
);
1243 SelectObject( hdc
, SYSCOLOR_GetPen(COLOR_WINDOWFRAME
) );
1244 MoveToEx( hdc
, rect
.left
, rect
.top
+ SEPARATOR_HEIGHT
/2, NULL
);
1245 LineTo( hdc
, rect
.right
, rect
.top
+ SEPARATOR_HEIGHT
/2 );
1253 if (lpitem
->fState
& MF_HILITE
)
1255 if(TWEAK_WineLook
== WIN98_LOOK
)
1258 SetTextColor(hdc
, GetSysColor(COLOR_MENUTEXT
));
1259 SetBkColor(hdc
, GetSysColor(COLOR_MENU
));
1261 if(lpitem
->fState
& MF_GRAYED
)
1262 SetTextColor(hdc
, GetSysColor(COLOR_GRAYTEXT
));
1264 SetTextColor(hdc
, GetSysColor(COLOR_HIGHLIGHTTEXT
));
1265 SetBkColor(hdc
, GetSysColor(COLOR_HIGHLIGHT
));
1268 else /* Not Win98 Look */
1270 SetTextColor(hdc
, GetSysColor(COLOR_HIGHLIGHTTEXT
));
1271 if(!IS_BITMAP_ITEM(lpitem
->fType
))
1272 SetBkColor(hdc
, GetSysColor(COLOR_HIGHLIGHT
));
1277 if (lpitem
->fState
& MF_GRAYED
)
1278 SetTextColor( hdc
, GetSysColor( COLOR_GRAYTEXT
) );
1280 SetTextColor( hdc
, GetSysColor( COLOR_MENUTEXT
) );
1281 SetBkColor( hdc
, GetSysColor( COLOR_MENU
) );
1284 /* helper lines for debugging */
1285 /* FrameRect(hdc, &rect, GetStockObject(BLACK_BRUSH));
1286 SelectObject( hdc, SYSCOLOR_GetPen(COLOR_WINDOWFRAME) );
1287 MoveToEx( hdc, rect.left, (rect.top + rect.bottom)/2, NULL );
1288 LineTo( hdc, rect.right, (rect.top + rect.bottom)/2 );
1293 INT y
= rect
.top
+ rect
.bottom
;
1294 UINT check_bitmap_width
= GetSystemMetrics( SM_CXMENUCHECK
);
1295 UINT check_bitmap_height
= GetSystemMetrics( SM_CYMENUCHECK
);
1297 if (!(lpitem
->fType
& MF_OWNERDRAW
))
1299 /* Draw the check mark
1302 * Custom checkmark bitmaps are monochrome but not always 1bpp.
1304 HBITMAP bm
= (lpitem
->fState
& MF_CHECKED
) ? lpitem
->hCheckBit
: lpitem
->hUnCheckBit
;
1305 if (bm
) /* we have a custom bitmap */
1307 HDC hdcMem
= CreateCompatibleDC( hdc
);
1308 SelectObject( hdcMem
, bm
);
1309 BitBlt( hdc
, rect
.left
, (y
- check_bitmap_height
) / 2,
1310 check_bitmap_width
, check_bitmap_height
,
1311 hdcMem
, 0, 0, SRCCOPY
);
1314 else if (lpitem
->fState
& MF_CHECKED
) /* standard bitmaps */
1317 HBITMAP bm
= CreateBitmap( check_bitmap_width
, check_bitmap_height
, 1, 1, NULL
);
1318 HDC hdcMem
= CreateCompatibleDC( hdc
);
1319 SelectObject( hdcMem
, bm
);
1320 SetRect( &r
, 0, 0, check_bitmap_width
, check_bitmap_height
);
1321 DrawFrameControl( hdcMem
, &r
, DFC_MENU
,
1322 (lpitem
->fType
& MFT_RADIOCHECK
) ?
1323 DFCS_MENUBULLET
: DFCS_MENUCHECK
);
1324 BitBlt( hdc
, rect
.left
, (y
- r
.bottom
) / 2, r
.right
, r
.bottom
,
1325 hdcMem
, 0, 0, SRCCOPY
);
1331 /* Draw the popup-menu arrow */
1332 if (lpitem
->fType
& MF_POPUP
)
1334 HDC hdcMem
= CreateCompatibleDC( hdc
);
1335 HBITMAP hOrigBitmap
;
1337 hOrigBitmap
= SelectObject( hdcMem
, hStdMnArrow
);
1338 BitBlt( hdc
, rect
.right
- arrow_bitmap_width
- 1,
1339 (y
- arrow_bitmap_height
) / 2,
1340 arrow_bitmap_width
, arrow_bitmap_height
,
1341 hdcMem
, 0, 0, SRCCOPY
);
1342 SelectObject( hdcMem
, hOrigBitmap
);
1346 rect
.left
+= check_bitmap_width
;
1347 rect
.right
-= arrow_bitmap_width
;
1350 /* Done for owner-drawn */
1351 if (lpitem
->fType
& MF_OWNERDRAW
)
1354 /* Draw the item text or bitmap */
1355 if (IS_BITMAP_ITEM(lpitem
->fType
))
1357 MENU_DrawBitmapItem( hdc
, lpitem
, &rect
, menuBar
);
1361 /* No bitmap - process text if present */
1362 else if (IS_STRING_ITEM(lpitem
->fType
))
1367 UINT uFormat
= (menuBar
) ?
1368 DT_CENTER
| DT_VCENTER
| DT_SINGLELINE
:
1369 DT_LEFT
| DT_VCENTER
| DT_SINGLELINE
;
1371 if ( lpitem
->fState
& MFS_DEFAULT
)
1373 hfontOld
= SelectObject( hdc
, hMenuFontBold
);
1378 rect
.left
+= MENU_BAR_ITEMS_SPACE
/ 2;
1379 rect
.right
-= MENU_BAR_ITEMS_SPACE
/ 2;
1382 for (i
= 0; lpitem
->text
[i
]; i
++)
1383 if ((lpitem
->text
[i
] == '\t') || (lpitem
->text
[i
] == '\b'))
1386 if( (TWEAK_WineLook
!= WIN31_LOOK
) && (lpitem
->fState
& MF_GRAYED
))
1388 if (!(lpitem
->fState
& MF_HILITE
) )
1390 ++rect
.left
; ++rect
.top
; ++rect
.right
; ++rect
.bottom
;
1391 SetTextColor(hdc
, RGB(0xff, 0xff, 0xff));
1392 DrawTextW( hdc
, lpitem
->text
, i
, &rect
, uFormat
);
1393 --rect
.left
; --rect
.top
; --rect
.right
; --rect
.bottom
;
1395 SetTextColor(hdc
, RGB(0x80, 0x80, 0x80));
1398 DrawTextW( hdc
, lpitem
->text
, i
, &rect
, uFormat
);
1400 /* paint the shortcut text */
1401 if (!menuBar
&& lpitem
->text
[i
]) /* There's a tab or flush-right char */
1403 if (lpitem
->text
[i
] == '\t')
1405 rect
.left
= lpitem
->xTab
;
1406 uFormat
= DT_LEFT
| DT_VCENTER
| DT_SINGLELINE
;
1410 uFormat
= DT_RIGHT
| DT_VCENTER
| DT_SINGLELINE
;
1413 if( (TWEAK_WineLook
!= WIN31_LOOK
) && (lpitem
->fState
& MF_GRAYED
))
1415 if (!(lpitem
->fState
& MF_HILITE
) )
1417 ++rect
.left
; ++rect
.top
; ++rect
.right
; ++rect
.bottom
;
1418 SetTextColor(hdc
, RGB(0xff, 0xff, 0xff));
1419 DrawTextW( hdc
, lpitem
->text
+ i
+ 1, -1, &rect
, uFormat
);
1420 --rect
.left
; --rect
.top
; --rect
.right
; --rect
.bottom
;
1422 SetTextColor(hdc
, RGB(0x80, 0x80, 0x80));
1424 DrawTextW( hdc
, lpitem
->text
+ i
+ 1, -1, &rect
, uFormat
);
1428 SelectObject (hdc
, hfontOld
);
1433 /***********************************************************************
1434 * MENU_DrawPopupMenu
1436 * Paint a popup menu.
1438 static void MENU_DrawPopupMenu( HWND hwnd
, HDC hdc
, HMENU hmenu
)
1440 HBRUSH hPrevBrush
= 0;
1443 TRACE("wnd=%p dc=%p menu=%p\n", hwnd
, hdc
, hmenu
);
1445 GetClientRect( hwnd
, &rect
);
1447 if(TWEAK_WineLook
== WIN31_LOOK
)
1449 rect
.bottom
-= POPUP_YSHADE
* GetSystemMetrics(SM_CYBORDER
);
1450 rect
.right
-= POPUP_XSHADE
* GetSystemMetrics(SM_CXBORDER
);
1453 if((hPrevBrush
= SelectObject( hdc
, GetSysColorBrush(COLOR_MENU
) ))
1454 && (SelectObject( hdc
, hMenuFont
)))
1458 Rectangle( hdc
, rect
.left
, rect
.top
, rect
.right
, rect
.bottom
);
1460 hPrevPen
= SelectObject( hdc
, GetStockObject( NULL_PEN
) );
1466 /* draw 3-d shade */
1467 if(TWEAK_WineLook
== WIN31_LOOK
) {
1468 SelectObject( hdc
, hShadeBrush
);
1469 SetBkMode( hdc
, TRANSPARENT
);
1470 ropPrev
= SetROP2( hdc
, R2_MASKPEN
);
1472 i
= rect
.right
; /* why SetBrushOrg() doesn't? */
1473 PatBlt( hdc
, i
& 0xfffffffe,
1474 rect
.top
+ POPUP_YSHADE
*GetSystemMetrics(SM_CYBORDER
),
1475 i
%2 + POPUP_XSHADE
*GetSystemMetrics(SM_CXBORDER
),
1476 rect
.bottom
- rect
.top
, 0x00a000c9 );
1478 PatBlt( hdc
, rect
.left
+ POPUP_XSHADE
*GetSystemMetrics(SM_CXBORDER
),
1479 i
& 0xfffffffe,rect
.right
- rect
.left
,
1480 i
%2 + POPUP_YSHADE
*GetSystemMetrics(SM_CYBORDER
), 0x00a000c9 );
1481 SelectObject( hdc
, hPrevPen
);
1482 SelectObject( hdc
, hPrevBrush
);
1483 SetROP2( hdc
, ropPrev
);
1486 DrawEdge (hdc
, &rect
, EDGE_RAISED
, BF_RECT
);
1488 /* draw menu items */
1490 menu
= MENU_GetMenu( hmenu
);
1491 if (menu
&& menu
->nItems
)
1496 for (u
= menu
->nItems
, item
= menu
->items
; u
> 0; u
--, item
++)
1497 MENU_DrawMenuItem( hwnd
, hmenu
, menu
->hwndOwner
, hdc
, item
,
1498 menu
->Height
, FALSE
, ODA_DRAWENTIRE
);
1503 SelectObject( hdc
, hPrevBrush
);
1508 /***********************************************************************
1511 * Paint a menu bar. Returns the height of the menu bar.
1512 * called from [windows/nonclient.c]
1514 UINT
MENU_DrawMenuBar( HDC hDC
, LPRECT lprect
, HWND hwnd
,
1520 HMENU hMenu
= GetMenu(hwnd
);
1522 lppop
= MENU_GetMenu( hMenu
);
1523 if (lppop
== NULL
|| lprect
== NULL
)
1525 retvalue
= GetSystemMetrics(SM_CYMENU
);
1529 TRACE("(%p, %p, %p)\n", hDC
, lprect
, lppop
);
1531 hfontOld
= SelectObject( hDC
, hMenuFont
);
1533 if (lppop
->Height
== 0)
1534 MENU_MenuBarCalcSize(hDC
, lprect
, lppop
, hwnd
);
1536 lprect
->bottom
= lprect
->top
+ lppop
->Height
;
1540 retvalue
= lppop
->Height
;
1544 FillRect(hDC
, lprect
, GetSysColorBrush(COLOR_MENU
) );
1546 if (TWEAK_WineLook
== WIN31_LOOK
)
1548 SelectObject( hDC
, SYSCOLOR_GetPen(COLOR_WINDOWFRAME
) );
1549 MoveToEx( hDC
, lprect
->left
, lprect
->bottom
, NULL
);
1550 LineTo( hDC
, lprect
->right
, lprect
->bottom
);
1554 SelectObject( hDC
, SYSCOLOR_GetPen(COLOR_3DFACE
));
1555 MoveToEx( hDC
, lprect
->left
, lprect
->bottom
, NULL
);
1556 LineTo( hDC
, lprect
->right
, lprect
->bottom
);
1559 if (lppop
->nItems
== 0)
1561 retvalue
= GetSystemMetrics(SM_CYMENU
);
1565 for (i
= 0; i
< lppop
->nItems
; i
++)
1567 MENU_DrawMenuItem( hwnd
, hMenu
, hwnd
,
1568 hDC
, &lppop
->items
[i
], lppop
->Height
, TRUE
, ODA_DRAWENTIRE
);
1570 retvalue
= lppop
->Height
;
1573 if (hfontOld
) SelectObject (hDC
, hfontOld
);
1578 /***********************************************************************
1581 * Display a popup menu.
1583 static BOOL
MENU_ShowPopup( HWND hwndOwner
, HMENU hmenu
, UINT id
,
1584 INT x
, INT y
, INT xanchor
, INT yanchor
)
1589 TRACE("owner=%p hmenu=%p id=0x%04x x=0x%04x y=0x%04x xa=0x%04x ya=0x%04x\n",
1590 hwndOwner
, hmenu
, id
, x
, y
, xanchor
, yanchor
);
1592 if (!(menu
= MENU_GetMenu( hmenu
))) return FALSE
;
1593 if (menu
->FocusedItem
!= NO_SELECTED_ITEM
)
1595 menu
->items
[menu
->FocusedItem
].fState
&= ~(MF_HILITE
|MF_MOUSESELECT
);
1596 menu
->FocusedItem
= NO_SELECTED_ITEM
;
1599 /* store the owner for DrawItem */
1600 menu
->hwndOwner
= hwndOwner
;
1603 MENU_PopupMenuCalcSize( menu
, hwndOwner
);
1605 /* adjust popup menu pos so that it fits within the desktop */
1607 width
= menu
->Width
+ GetSystemMetrics(SM_CXBORDER
);
1608 height
= menu
->Height
+ GetSystemMetrics(SM_CYBORDER
);
1610 if( x
+ width
> GetSystemMetrics(SM_CXSCREEN
))
1613 x
-= width
- xanchor
;
1614 if( x
+ width
> GetSystemMetrics(SM_CXSCREEN
))
1615 x
= GetSystemMetrics(SM_CXSCREEN
) - width
;
1619 if( y
+ height
> GetSystemMetrics(SM_CYSCREEN
))
1622 y
-= height
+ yanchor
;
1623 if( y
+ height
> GetSystemMetrics(SM_CYSCREEN
))
1624 y
= GetSystemMetrics(SM_CYSCREEN
) - height
;
1628 if( TWEAK_WineLook
== WIN31_LOOK
)
1630 width
+= POPUP_XSHADE
* GetSystemMetrics(SM_CXBORDER
); /* add space for shading */
1631 height
+= POPUP_YSHADE
* GetSystemMetrics(SM_CYBORDER
);
1634 /* NOTE: In Windows, top menu popup is not owned. */
1635 menu
->hWnd
= CreateWindowA( POPUPMENU_CLASS_ATOM
, NULL
,
1636 WS_POPUP
, x
, y
, width
, height
,
1637 hwndOwner
, 0, (HINSTANCE
)GetWindowLongA(hwndOwner
,GWL_HINSTANCE
),
1639 if( !menu
->hWnd
) return FALSE
;
1640 if (!top_popup
) top_popup
= menu
->hWnd
;
1642 /* Display the window */
1644 SetWindowPos( menu
->hWnd
, HWND_TOP
, 0, 0, 0, 0,
1645 SWP_SHOWWINDOW
| SWP_NOSIZE
| SWP_NOMOVE
| SWP_NOACTIVATE
);
1646 UpdateWindow( menu
->hWnd
);
1651 /***********************************************************************
1654 static void MENU_SelectItem( HWND hwndOwner
, HMENU hmenu
, UINT wIndex
,
1655 BOOL sendMenuSelect
, HMENU topmenu
)
1660 TRACE("owner=%p menu=%p index=0x%04x select=0x%04x\n", hwndOwner
, hmenu
, wIndex
, sendMenuSelect
);
1662 lppop
= MENU_GetMenu( hmenu
);
1663 if ((!lppop
) || (!lppop
->nItems
) || (!lppop
->hWnd
)) return;
1665 if (lppop
->FocusedItem
== wIndex
) return;
1666 if (lppop
->wFlags
& MF_POPUP
) hdc
= GetDC( lppop
->hWnd
);
1667 else hdc
= GetDCEx( lppop
->hWnd
, 0, DCX_CACHE
| DCX_WINDOW
);
1668 if (!top_popup
) top_popup
= lppop
->hWnd
;
1670 SelectObject( hdc
, hMenuFont
);
1672 /* Clear previous highlighted item */
1673 if (lppop
->FocusedItem
!= NO_SELECTED_ITEM
)
1675 lppop
->items
[lppop
->FocusedItem
].fState
&= ~(MF_HILITE
|MF_MOUSESELECT
);
1676 MENU_DrawMenuItem(lppop
->hWnd
, hmenu
, hwndOwner
, hdc
,&lppop
->items
[lppop
->FocusedItem
],
1677 lppop
->Height
, !(lppop
->wFlags
& MF_POPUP
),
1681 /* Highlight new item (if any) */
1682 lppop
->FocusedItem
= wIndex
;
1683 if (lppop
->FocusedItem
!= NO_SELECTED_ITEM
)
1685 if(!(lppop
->items
[wIndex
].fType
& MF_SEPARATOR
)) {
1686 lppop
->items
[wIndex
].fState
|= MF_HILITE
;
1687 MENU_DrawMenuItem( lppop
->hWnd
, hmenu
, hwndOwner
, hdc
,
1688 &lppop
->items
[wIndex
], lppop
->Height
,
1689 !(lppop
->wFlags
& MF_POPUP
), ODA_SELECT
);
1693 MENUITEM
*ip
= &lppop
->items
[lppop
->FocusedItem
];
1694 SendMessageA( hwndOwner
, WM_MENUSELECT
,
1695 MAKELONG(ip
->fType
& MF_POPUP
? wIndex
: ip
->wID
,
1696 ip
->fType
| ip
->fState
| MF_MOUSESELECT
|
1697 (lppop
->wFlags
& MF_SYSMENU
)), (LPARAM
)hmenu
);
1700 else if (sendMenuSelect
) {
1703 if((pos
=MENU_FindSubMenu(&topmenu
, hmenu
))!=NO_SELECTED_ITEM
){
1704 POPUPMENU
*ptm
= MENU_GetMenu( topmenu
);
1705 MENUITEM
*ip
= &ptm
->items
[pos
];
1706 SendMessageA( hwndOwner
, WM_MENUSELECT
, MAKELONG(pos
,
1707 ip
->fType
| ip
->fState
| MF_MOUSESELECT
|
1708 (ptm
->wFlags
& MF_SYSMENU
)), (LPARAM
)topmenu
);
1712 ReleaseDC( lppop
->hWnd
, hdc
);
1716 /***********************************************************************
1717 * MENU_MoveSelection
1719 * Moves currently selected item according to the offset parameter.
1720 * If there is no selection then it should select the last item if
1721 * offset is ITEM_PREV or the first item if offset is ITEM_NEXT.
1723 static void MENU_MoveSelection( HWND hwndOwner
, HMENU hmenu
, INT offset
)
1728 TRACE("hwnd=%p hmenu=%p off=0x%04x\n", hwndOwner
, hmenu
, offset
);
1730 menu
= MENU_GetMenu( hmenu
);
1731 if ((!menu
) || (!menu
->items
)) return;
1733 if ( menu
->FocusedItem
!= NO_SELECTED_ITEM
)
1735 if( menu
->nItems
== 1 ) return; else
1736 for (i
= menu
->FocusedItem
+ offset
; i
>= 0 && i
< menu
->nItems
1738 if (!(menu
->items
[i
].fType
& MF_SEPARATOR
))
1740 MENU_SelectItem( hwndOwner
, hmenu
, i
, TRUE
, 0 );
1745 for ( i
= (offset
> 0) ? 0 : menu
->nItems
- 1;
1746 i
>= 0 && i
< menu
->nItems
; i
+= offset
)
1747 if (!(menu
->items
[i
].fType
& MF_SEPARATOR
))
1749 MENU_SelectItem( hwndOwner
, hmenu
, i
, TRUE
, 0 );
1755 /**********************************************************************
1758 * Set an item's flags, id and text ptr. Called by InsertMenu() and
1761 static BOOL
MENU_SetItemData( MENUITEM
*item
, UINT flags
, UINT_PTR id
,
1764 LPWSTR prevText
= IS_STRING_ITEM(item
->fType
) ? item
->text
: NULL
;
1766 debug_print_menuitem("MENU_SetItemData from: ", item
, "");
1767 TRACE("flags=%x str=%p\n", flags
, str
);
1769 if (IS_STRING_ITEM(flags
))
1773 flags
|= MF_SEPARATOR
;
1779 /* Item beginning with a backspace is a help item */
1785 if (!(text
= HeapAlloc( GetProcessHeap(), 0, (strlenW(str
)+1) * sizeof(WCHAR
) )))
1787 strcpyW( text
, str
);
1791 else if (IS_BITMAP_ITEM(flags
))
1792 item
->text
= (LPWSTR
)HBITMAP_32(LOWORD(str
));
1793 else item
->text
= NULL
;
1795 if (flags
& MF_OWNERDRAW
)
1796 item
->dwItemData
= (DWORD
)str
;
1798 item
->dwItemData
= 0;
1800 if ((item
->fType
& MF_POPUP
) && (flags
& MF_POPUP
) && (item
->hSubMenu
!= (HMENU
)id
) )
1801 DestroyMenu( item
->hSubMenu
); /* ModifyMenu() spec */
1803 if (flags
& MF_POPUP
)
1805 POPUPMENU
*menu
= MENU_GetMenu((HMENU
)id
);
1806 if (menu
) menu
->wFlags
|= MF_POPUP
;
1818 if (flags
& MF_POPUP
) item
->hSubMenu
= (HMENU
)id
;
1820 if ((item
->fType
& MF_POPUP
) && !(flags
& MF_POPUP
) )
1821 flags
|= MF_POPUP
; /* keep popup */
1823 item
->fType
= flags
& TYPE_MASK
;
1824 item
->fState
= (flags
& STATE_MASK
) &
1825 ~(MF_HILITE
| MF_MOUSESELECT
| MF_BYPOSITION
);
1828 /* Don't call SetRectEmpty here! */
1831 if (prevText
) HeapFree( GetProcessHeap(), 0, prevText
);
1833 debug_print_menuitem("MENU_SetItemData to : ", item
, "");
1838 /**********************************************************************
1841 * Insert (allocate) a new item into a menu.
1843 static MENUITEM
*MENU_InsertItem( HMENU hMenu
, UINT pos
, UINT flags
)
1848 if (!(menu
= MENU_GetMenu(hMenu
)))
1851 /* Find where to insert new item */
1853 if (flags
& MF_BYPOSITION
) {
1854 if (pos
> menu
->nItems
)
1857 if (!MENU_FindItem( &hMenu
, &pos
, flags
))
1860 if (!(menu
= MENU_GetMenu( hMenu
)))
1865 /* Create new items array */
1867 newItems
= HeapAlloc( GetProcessHeap(), 0, sizeof(MENUITEM
) * (menu
->nItems
+1) );
1870 WARN("allocation failed\n" );
1873 if (menu
->nItems
> 0)
1875 /* Copy the old array into the new one */
1876 if (pos
> 0) memcpy( newItems
, menu
->items
, pos
* sizeof(MENUITEM
) );
1877 if (pos
< menu
->nItems
) memcpy( &newItems
[pos
+1], &menu
->items
[pos
],
1878 (menu
->nItems
-pos
)*sizeof(MENUITEM
) );
1879 HeapFree( GetProcessHeap(), 0, menu
->items
);
1881 menu
->items
= newItems
;
1883 memset( &newItems
[pos
], 0, sizeof(*newItems
) );
1884 menu
->Height
= 0; /* force size recalculate */
1885 return &newItems
[pos
];
1889 /**********************************************************************
1890 * MENU_ParseResource
1892 * Parse a standard menu resource and add items to the menu.
1893 * Return a pointer to the end of the resource.
1895 * NOTE: flags is equivalent to the mtOption field
1897 static LPCSTR
MENU_ParseResource( LPCSTR res
, HMENU hMenu
, BOOL unicode
)
1904 flags
= GET_WORD(res
);
1905 res
+= sizeof(WORD
);
1906 if (!(flags
& MF_POPUP
))
1909 res
+= sizeof(WORD
);
1912 if (!unicode
) res
+= strlen(str
) + 1;
1913 else res
+= (strlenW((LPCWSTR
)str
) + 1) * sizeof(WCHAR
);
1914 if (flags
& MF_POPUP
)
1916 HMENU hSubMenu
= CreatePopupMenu();
1917 if (!hSubMenu
) return NULL
;
1918 if (!(res
= MENU_ParseResource( res
, hSubMenu
, unicode
)))
1920 if (!unicode
) AppendMenuA( hMenu
, flags
, (UINT
)hSubMenu
, str
);
1921 else AppendMenuW( hMenu
, flags
, (UINT
)hSubMenu
, (LPCWSTR
)str
);
1923 else /* Not a popup */
1925 if (!unicode
) AppendMenuA( hMenu
, flags
, id
, *str
? str
: NULL
);
1926 else AppendMenuW( hMenu
, flags
, id
,
1927 *(LPCWSTR
)str
? (LPCWSTR
)str
: NULL
);
1929 } while (!(flags
& MF_END
));
1934 /**********************************************************************
1935 * MENUEX_ParseResource
1937 * Parse an extended menu resource and add items to the menu.
1938 * Return a pointer to the end of the resource.
1940 static LPCSTR
MENUEX_ParseResource( LPCSTR res
, HMENU hMenu
)
1946 mii
.cbSize
= sizeof(mii
);
1947 mii
.fMask
= MIIM_STATE
| MIIM_ID
| MIIM_TYPE
;
1948 mii
.fType
= GET_DWORD(res
);
1949 res
+= sizeof(DWORD
);
1950 mii
.fState
= GET_DWORD(res
);
1951 res
+= sizeof(DWORD
);
1952 mii
.wID
= GET_DWORD(res
);
1953 res
+= sizeof(DWORD
);
1954 resinfo
= GET_WORD(res
); /* FIXME: for 16-bit apps this is a byte. */
1955 res
+= sizeof(WORD
);
1956 /* Align the text on a word boundary. */
1957 res
+= (~((int)res
- 1)) & 1;
1958 mii
.dwTypeData
= (LPWSTR
) res
;
1959 res
+= (1 + strlenW(mii
.dwTypeData
)) * sizeof(WCHAR
);
1960 /* Align the following fields on a dword boundary. */
1961 res
+= (~((int)res
- 1)) & 3;
1963 TRACE("Menu item: [%08x,%08x,%04x,%04x,%s]\n",
1964 mii
.fType
, mii
.fState
, mii
.wID
, resinfo
, debugstr_w(mii
.dwTypeData
));
1966 if (resinfo
& 1) { /* Pop-up? */
1967 /* DWORD helpid = GET_DWORD(res); FIXME: use this. */
1968 res
+= sizeof(DWORD
);
1969 mii
.hSubMenu
= CreatePopupMenu();
1972 if (!(res
= MENUEX_ParseResource(res
, mii
.hSubMenu
))) {
1973 DestroyMenu(mii
.hSubMenu
);
1976 mii
.fMask
|= MIIM_SUBMENU
;
1977 mii
.fType
|= MF_POPUP
;
1979 else if(!*mii
.dwTypeData
&& !(mii
.fType
& MF_SEPARATOR
))
1981 WARN("Converting NULL menu item %04x, type %04x to SEPARATOR\n",
1982 mii
.wID
, mii
.fType
);
1983 mii
.fType
|= MF_SEPARATOR
;
1985 InsertMenuItemW(hMenu
, -1, MF_BYPOSITION
, &mii
);
1986 } while (!(resinfo
& MF_END
));
1991 /***********************************************************************
1994 * Return the handle of the selected sub-popup menu (if any).
1996 static HMENU
MENU_GetSubPopup( HMENU hmenu
)
2001 menu
= MENU_GetMenu( hmenu
);
2003 if ((!menu
) || (menu
->FocusedItem
== NO_SELECTED_ITEM
)) return 0;
2005 item
= &menu
->items
[menu
->FocusedItem
];
2006 if ((item
->fType
& MF_POPUP
) && (item
->fState
& MF_MOUSESELECT
))
2007 return item
->hSubMenu
;
2012 /***********************************************************************
2013 * MENU_HideSubPopups
2015 * Hide the sub-popup menus of this menu.
2017 static void MENU_HideSubPopups( HWND hwndOwner
, HMENU hmenu
,
2018 BOOL sendMenuSelect
)
2020 POPUPMENU
*menu
= MENU_GetMenu( hmenu
);
2022 TRACE("owner=%p hmenu=%p 0x%04x\n", hwndOwner
, hmenu
, sendMenuSelect
);
2024 if (menu
&& top_popup
)
2030 if (menu
->FocusedItem
!= NO_SELECTED_ITEM
)
2032 item
= &menu
->items
[menu
->FocusedItem
];
2033 if (!(item
->fType
& MF_POPUP
) ||
2034 !(item
->fState
& MF_MOUSESELECT
)) return;
2035 item
->fState
&= ~MF_MOUSESELECT
;
2036 hsubmenu
= item
->hSubMenu
;
2039 submenu
= MENU_GetMenu( hsubmenu
);
2040 MENU_HideSubPopups( hwndOwner
, hsubmenu
, FALSE
);
2041 MENU_SelectItem( hwndOwner
, hsubmenu
, NO_SELECTED_ITEM
, sendMenuSelect
, 0 );
2042 DestroyWindow( submenu
->hWnd
);
2048 /***********************************************************************
2051 * Display the sub-menu of the selected item of this menu.
2052 * Return the handle of the submenu, or hmenu if no submenu to display.
2054 static HMENU
MENU_ShowSubPopup( HWND hwndOwner
, HMENU hmenu
,
2055 BOOL selectFirst
, UINT wFlags
)
2062 TRACE("owner=%p hmenu=%p 0x%04x\n", hwndOwner
, hmenu
, selectFirst
);
2064 if (!(menu
= MENU_GetMenu( hmenu
))) return hmenu
;
2066 if (menu
->FocusedItem
== NO_SELECTED_ITEM
) return hmenu
;
2068 item
= &menu
->items
[menu
->FocusedItem
];
2069 if (!(item
->fType
& MF_POPUP
) || (item
->fState
& (MF_GRAYED
| MF_DISABLED
)))
2072 /* message must be sent before using item,
2073 because nearly everything may be changed by the application ! */
2075 /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
2076 if (!(wFlags
& TPM_NONOTIFY
))
2077 SendMessageA( hwndOwner
, WM_INITMENUPOPUP
, (WPARAM
)item
->hSubMenu
,
2078 MAKELONG( menu
->FocusedItem
, IS_SYSTEM_MENU(menu
) ));
2080 item
= &menu
->items
[menu
->FocusedItem
];
2083 /* correct item if modified as a reaction to WM_INITMENUPOPUP message */
2084 if (!(item
->fState
& MF_HILITE
))
2086 if (menu
->wFlags
& MF_POPUP
) hdc
= GetDC( menu
->hWnd
);
2087 else hdc
= GetDCEx( menu
->hWnd
, 0, DCX_CACHE
| DCX_WINDOW
);
2089 SelectObject( hdc
, hMenuFont
);
2091 item
->fState
|= MF_HILITE
;
2092 MENU_DrawMenuItem( menu
->hWnd
, hmenu
, hwndOwner
, hdc
, item
, menu
->Height
, !(menu
->wFlags
& MF_POPUP
), ODA_DRAWENTIRE
);
2093 ReleaseDC( menu
->hWnd
, hdc
);
2095 if (!item
->rect
.top
&& !item
->rect
.left
&& !item
->rect
.bottom
&& !item
->rect
.right
)
2098 item
->fState
|= MF_MOUSESELECT
;
2100 if (IS_SYSTEM_MENU(menu
))
2102 MENU_InitSysMenuPopup(item
->hSubMenu
,
2103 GetWindowLongA( menu
->hWnd
, GWL_STYLE
),
2104 GetClassLongA( menu
->hWnd
, GCL_STYLE
));
2106 NC_GetSysPopupPos( menu
->hWnd
, &rect
);
2107 rect
.top
= rect
.bottom
;
2108 rect
.right
= GetSystemMetrics(SM_CXSIZE
);
2109 rect
.bottom
= GetSystemMetrics(SM_CYSIZE
);
2113 GetWindowRect( menu
->hWnd
, &rect
);
2114 if (menu
->wFlags
& MF_POPUP
)
2116 rect
.left
+= item
->rect
.right
- GetSystemMetrics(SM_CXBORDER
);
2117 rect
.top
+= item
->rect
.top
;
2118 rect
.right
= item
->rect
.left
- item
->rect
.right
+ GetSystemMetrics(SM_CXBORDER
);
2119 rect
.bottom
= item
->rect
.top
- item
->rect
.bottom
;
2123 rect
.left
+= item
->rect
.left
;
2124 rect
.top
+= item
->rect
.bottom
;
2125 rect
.right
= item
->rect
.right
- item
->rect
.left
;
2126 rect
.bottom
= item
->rect
.bottom
- item
->rect
.top
;
2130 MENU_ShowPopup( hwndOwner
, item
->hSubMenu
, menu
->FocusedItem
,
2131 rect
.left
, rect
.top
, rect
.right
, rect
.bottom
);
2133 MENU_MoveSelection( hwndOwner
, item
->hSubMenu
, ITEM_NEXT
);
2134 return item
->hSubMenu
;
2139 /**********************************************************************
2142 BOOL
MENU_IsMenuActive(void)
2144 return (top_popup
!= 0);
2147 /***********************************************************************
2150 * Walks menu chain trying to find a menu pt maps to.
2152 static HMENU
MENU_PtMenu( HMENU hMenu
, POINT pt
)
2154 POPUPMENU
*menu
= MENU_GetMenu( hMenu
);
2155 UINT item
= menu
->FocusedItem
;
2158 /* try subpopup first (if any) */
2159 ret
= (item
!= NO_SELECTED_ITEM
&&
2160 (menu
->items
[item
].fType
& MF_POPUP
) &&
2161 (menu
->items
[item
].fState
& MF_MOUSESELECT
))
2162 ? MENU_PtMenu(menu
->items
[item
].hSubMenu
, pt
) : 0;
2164 if (!ret
) /* check the current window (avoiding WM_HITTEST) */
2166 INT ht
= NC_HandleNCHitTest( menu
->hWnd
, pt
);
2167 if( menu
->wFlags
& MF_POPUP
)
2169 if (ht
!= HTNOWHERE
&& ht
!= HTERROR
) ret
= hMenu
;
2171 else if (ht
== HTSYSMENU
)
2172 ret
= get_win_sys_menu( menu
->hWnd
);
2173 else if (ht
== HTMENU
)
2174 ret
= GetMenu( menu
->hWnd
);
2179 /***********************************************************************
2180 * MENU_ExecFocusedItem
2182 * Execute a menu item (for instance when user pressed Enter).
2183 * Return the wID of the executed item. Otherwise, -1 indicating
2184 * that no menu item was executed;
2185 * Have to receive the flags for the TrackPopupMenu options to avoid
2186 * sending unwanted message.
2189 static INT
MENU_ExecFocusedItem( MTRACKER
* pmt
, HMENU hMenu
, UINT wFlags
)
2192 POPUPMENU
*menu
= MENU_GetMenu( hMenu
);
2194 TRACE("%p hmenu=%p\n", pmt
, hMenu
);
2196 if (!menu
|| !menu
->nItems
||
2197 (menu
->FocusedItem
== NO_SELECTED_ITEM
)) return -1;
2199 item
= &menu
->items
[menu
->FocusedItem
];
2201 TRACE("%p %08x %p\n", hMenu
, item
->wID
, item
->hSubMenu
);
2203 if (!(item
->fType
& MF_POPUP
))
2205 if (!(item
->fState
& (MF_GRAYED
| MF_DISABLED
)) && !(item
->fType
& MF_SEPARATOR
))
2207 /* If TPM_RETURNCMD is set you return the id, but
2208 do not send a message to the owner */
2209 if(!(wFlags
& TPM_RETURNCMD
))
2211 if( menu
->wFlags
& MF_SYSMENU
)
2212 PostMessageA( pmt
->hOwnerWnd
, WM_SYSCOMMAND
, item
->wID
,
2213 MAKELPARAM((INT16
)pmt
->pt
.x
, (INT16
)pmt
->pt
.y
) );
2215 PostMessageA( pmt
->hOwnerWnd
, WM_COMMAND
, item
->wID
, 0 );
2221 pmt
->hCurrentMenu
= MENU_ShowSubPopup(pmt
->hOwnerWnd
, hMenu
, TRUE
, wFlags
);
2226 /***********************************************************************
2227 * MENU_SwitchTracking
2229 * Helper function for menu navigation routines.
2231 static void MENU_SwitchTracking( MTRACKER
* pmt
, HMENU hPtMenu
, UINT id
)
2233 POPUPMENU
*ptmenu
= MENU_GetMenu( hPtMenu
);
2234 POPUPMENU
*topmenu
= MENU_GetMenu( pmt
->hTopMenu
);
2236 TRACE("%p hmenu=%p 0x%04x\n", pmt
, hPtMenu
, id
);
2238 if( pmt
->hTopMenu
!= hPtMenu
&&
2239 !((ptmenu
->wFlags
| topmenu
->wFlags
) & MF_POPUP
) )
2241 /* both are top level menus (system and menu-bar) */
2242 MENU_HideSubPopups( pmt
->hOwnerWnd
, pmt
->hTopMenu
, FALSE
);
2243 MENU_SelectItem( pmt
->hOwnerWnd
, pmt
->hTopMenu
, NO_SELECTED_ITEM
, FALSE
, 0 );
2244 pmt
->hTopMenu
= hPtMenu
;
2246 else MENU_HideSubPopups( pmt
->hOwnerWnd
, hPtMenu
, FALSE
);
2247 MENU_SelectItem( pmt
->hOwnerWnd
, hPtMenu
, id
, TRUE
, 0 );
2251 /***********************************************************************
2254 * Return TRUE if we can go on with menu tracking.
2256 static BOOL
MENU_ButtonDown( MTRACKER
* pmt
, HMENU hPtMenu
, UINT wFlags
)
2258 TRACE("%p hPtMenu=%p\n", pmt
, hPtMenu
);
2263 POPUPMENU
*ptmenu
= MENU_GetMenu( hPtMenu
);
2266 if( IS_SYSTEM_MENU(ptmenu
) )
2267 item
= ptmenu
->items
;
2269 item
= MENU_FindItemByCoords( ptmenu
, pmt
->pt
, &id
);
2273 if( ptmenu
->FocusedItem
!= id
)
2274 MENU_SwitchTracking( pmt
, hPtMenu
, id
);
2276 /* If the popup menu is not already "popped" */
2277 if(!(item
->fState
& MF_MOUSESELECT
))
2279 pmt
->hCurrentMenu
= MENU_ShowSubPopup( pmt
->hOwnerWnd
, hPtMenu
, FALSE
, wFlags
);
2281 /* In win31, a newly popped menu always remains opened for the next buttonup */
2282 if(TWEAK_WineLook
== WIN31_LOOK
)
2283 ptmenu
->bTimeToHide
= FALSE
;
2288 /* Else the click was on the menu bar, finish the tracking */
2293 /***********************************************************************
2296 * Return the value of MENU_ExecFocusedItem if
2297 * the selected item was not a popup. Else open the popup.
2298 * A -1 return value indicates that we go on with menu tracking.
2301 static INT
MENU_ButtonUp( MTRACKER
* pmt
, HMENU hPtMenu
, UINT wFlags
)
2303 TRACE("%p hmenu=%p\n", pmt
, hPtMenu
);
2308 POPUPMENU
*ptmenu
= MENU_GetMenu( hPtMenu
);
2311 if( IS_SYSTEM_MENU(ptmenu
) )
2312 item
= ptmenu
->items
;
2314 item
= MENU_FindItemByCoords( ptmenu
, pmt
->pt
, &id
);
2316 if( item
&& (ptmenu
->FocusedItem
== id
))
2318 if( !(item
->fType
& MF_POPUP
) )
2319 return MENU_ExecFocusedItem( pmt
, hPtMenu
, wFlags
);
2321 /* If we are dealing with the top-level menu */
2322 /* and this is a click on an already "popped" item: */
2323 /* Stop the menu tracking and close the opened submenus */
2324 if((pmt
->hTopMenu
== hPtMenu
) && (ptmenu
->bTimeToHide
== TRUE
))
2327 ptmenu
->bTimeToHide
= TRUE
;
2333 /***********************************************************************
2336 * Return TRUE if we can go on with menu tracking.
2338 static BOOL
MENU_MouseMove( MTRACKER
* pmt
, HMENU hPtMenu
, UINT wFlags
)
2340 UINT id
= NO_SELECTED_ITEM
;
2341 POPUPMENU
*ptmenu
= NULL
;
2345 ptmenu
= MENU_GetMenu( hPtMenu
);
2346 if( IS_SYSTEM_MENU(ptmenu
) )
2349 MENU_FindItemByCoords( ptmenu
, pmt
->pt
, &id
);
2352 if( id
== NO_SELECTED_ITEM
)
2354 MENU_SelectItem( pmt
->hOwnerWnd
, pmt
->hCurrentMenu
,
2355 NO_SELECTED_ITEM
, TRUE
, pmt
->hTopMenu
);
2358 else if( ptmenu
->FocusedItem
!= id
)
2360 MENU_SwitchTracking( pmt
, hPtMenu
, id
);
2361 pmt
->hCurrentMenu
= MENU_ShowSubPopup(pmt
->hOwnerWnd
, hPtMenu
, FALSE
, wFlags
);
2367 /***********************************************************************
2370 static void MENU_SetCapture( HWND hwnd
)
2374 SERVER_START_REQ( set_capture_window
)
2377 req
->flags
= CAPTURE_MENU
;
2378 if (!wine_server_call_err( req
))
2380 previous
= reply
->previous
;
2381 hwnd
= reply
->full_handle
;
2386 if (previous
&& previous
!= hwnd
)
2387 SendMessageW( previous
, WM_CAPTURECHANGED
, 0, (LPARAM
)hwnd
);
2391 /***********************************************************************
2394 * NOTE: WM_NEXTMENU documented in Win32 is a bit different.
2396 static LRESULT
MENU_DoNextMenu( MTRACKER
* pmt
, UINT vk
)
2398 POPUPMENU
*menu
= MENU_GetMenu( pmt
->hTopMenu
);
2400 if( (vk
== VK_LEFT
&& menu
->FocusedItem
== 0 ) ||
2401 (vk
== VK_RIGHT
&& menu
->FocusedItem
== menu
->nItems
- 1))
2403 MDINEXTMENU next_menu
;
2408 next_menu
.hmenuIn
= (IS_SYSTEM_MENU(menu
)) ? GetSubMenu(pmt
->hTopMenu
,0) : pmt
->hTopMenu
;
2409 next_menu
.hmenuNext
= 0;
2410 next_menu
.hwndNext
= 0;
2411 SendMessageW( pmt
->hOwnerWnd
, WM_NEXTMENU
, vk
, (LPARAM
)&next_menu
);
2413 TRACE("%p [%p] -> %p [%p]\n",
2414 pmt
->hCurrentMenu
, pmt
->hOwnerWnd
, next_menu
.hmenuNext
, next_menu
.hwndNext
);
2416 if (!next_menu
.hmenuNext
|| !next_menu
.hwndNext
)
2418 DWORD style
= GetWindowLongA( pmt
->hOwnerWnd
, GWL_STYLE
);
2419 hNewWnd
= pmt
->hOwnerWnd
;
2420 if( IS_SYSTEM_MENU(menu
) )
2422 /* switch to the menu bar */
2424 if(style
& WS_CHILD
|| !(hNewMenu
= GetMenu(hNewWnd
))) return FALSE
;
2428 menu
= MENU_GetMenu( hNewMenu
);
2429 id
= menu
->nItems
- 1;
2432 else if (style
& WS_SYSMENU
)
2434 /* switch to the system menu */
2435 hNewMenu
= get_win_sys_menu( hNewWnd
);
2439 else /* application returned a new menu to switch to */
2441 hNewMenu
= next_menu
.hmenuNext
;
2442 hNewWnd
= WIN_GetFullHandle( next_menu
.hwndNext
);
2444 if( IsMenu(hNewMenu
) && IsWindow(hNewWnd
) )
2446 DWORD style
= GetWindowLongA( hNewWnd
, GWL_STYLE
);
2448 if (style
& WS_SYSMENU
&&
2449 GetSubMenu(get_win_sys_menu(hNewWnd
), 0) == hNewMenu
)
2451 /* get the real system menu */
2452 hNewMenu
= get_win_sys_menu(hNewWnd
);
2454 else if (style
& WS_CHILD
|| GetMenu(hNewWnd
) != hNewMenu
)
2456 /* FIXME: Not sure what to do here;
2457 * perhaps try to track hNewMenu as a popup? */
2459 TRACE(" -- got confused.\n");
2466 if( hNewMenu
!= pmt
->hTopMenu
)
2468 MENU_SelectItem( pmt
->hOwnerWnd
, pmt
->hTopMenu
, NO_SELECTED_ITEM
,
2470 if( pmt
->hCurrentMenu
!= pmt
->hTopMenu
)
2471 MENU_HideSubPopups( pmt
->hOwnerWnd
, pmt
->hTopMenu
, FALSE
);
2474 if( hNewWnd
!= pmt
->hOwnerWnd
)
2476 pmt
->hOwnerWnd
= hNewWnd
;
2477 MENU_SetCapture( pmt
->hOwnerWnd
);
2480 pmt
->hTopMenu
= pmt
->hCurrentMenu
= hNewMenu
; /* all subpopups are hidden */
2481 MENU_SelectItem( pmt
->hOwnerWnd
, pmt
->hTopMenu
, id
, TRUE
, 0 );
2488 /***********************************************************************
2491 * The idea is not to show the popup if the next input message is
2492 * going to hide it anyway.
2494 static BOOL
MENU_SuspendPopup( MTRACKER
* pmt
, UINT16 uMsg
)
2498 msg
.hwnd
= pmt
->hOwnerWnd
;
2500 PeekMessageA( &msg
, 0, 0, 0, PM_NOYIELD
| PM_REMOVE
);
2501 pmt
->trackFlags
|= TF_SKIPREMOVE
;
2506 PeekMessageA( &msg
, 0, 0, 0, PM_NOYIELD
| PM_NOREMOVE
);
2507 if( msg
.message
== WM_KEYUP
|| msg
.message
== WM_PAINT
)
2509 PeekMessageA( &msg
, 0, 0, 0, PM_NOYIELD
| PM_REMOVE
);
2510 PeekMessageA( &msg
, 0, 0, 0, PM_NOYIELD
| PM_NOREMOVE
);
2511 if( msg
.message
== WM_KEYDOWN
&&
2512 (msg
.wParam
== VK_LEFT
|| msg
.wParam
== VK_RIGHT
))
2514 pmt
->trackFlags
|= TF_SUSPENDPOPUP
;
2521 /* failures go through this */
2522 pmt
->trackFlags
&= ~TF_SUSPENDPOPUP
;
2526 /***********************************************************************
2529 * Handle a VK_ESCAPE key event in a menu.
2531 static BOOL
MENU_KeyEscape(MTRACKER
* pmt
, UINT wFlags
)
2533 BOOL bEndMenu
= TRUE
;
2535 if (pmt
->hCurrentMenu
!= pmt
->hTopMenu
)
2537 POPUPMENU
*menu
= MENU_GetMenu(pmt
->hCurrentMenu
);
2539 if (menu
->wFlags
& MF_POPUP
)
2541 HMENU hmenutmp
, hmenuprev
;
2543 hmenuprev
= hmenutmp
= pmt
->hTopMenu
;
2545 /* close topmost popup */
2546 while (hmenutmp
!= pmt
->hCurrentMenu
)
2548 hmenuprev
= hmenutmp
;
2549 hmenutmp
= MENU_GetSubPopup( hmenuprev
);
2552 MENU_HideSubPopups( pmt
->hOwnerWnd
, hmenuprev
, TRUE
);
2553 pmt
->hCurrentMenu
= hmenuprev
;
2561 /***********************************************************************
2564 * Handle a VK_LEFT key event in a menu.
2566 static void MENU_KeyLeft( MTRACKER
* pmt
, UINT wFlags
)
2569 HMENU hmenutmp
, hmenuprev
;
2572 hmenuprev
= hmenutmp
= pmt
->hTopMenu
;
2573 menu
= MENU_GetMenu( hmenutmp
);
2575 /* Try to move 1 column left (if possible) */
2576 if( (prevcol
= MENU_GetStartOfPrevColumn( pmt
->hCurrentMenu
)) !=
2577 NO_SELECTED_ITEM
) {
2579 MENU_SelectItem( pmt
->hOwnerWnd
, pmt
->hCurrentMenu
,
2584 /* close topmost popup */
2585 while (hmenutmp
!= pmt
->hCurrentMenu
)
2587 hmenuprev
= hmenutmp
;
2588 hmenutmp
= MENU_GetSubPopup( hmenuprev
);
2591 MENU_HideSubPopups( pmt
->hOwnerWnd
, hmenuprev
, TRUE
);
2592 pmt
->hCurrentMenu
= hmenuprev
;
2594 if ( (hmenuprev
== pmt
->hTopMenu
) && !(menu
->wFlags
& MF_POPUP
) )
2596 /* move menu bar selection if no more popups are left */
2598 if( !MENU_DoNextMenu( pmt
, VK_LEFT
) )
2599 MENU_MoveSelection( pmt
->hOwnerWnd
, pmt
->hTopMenu
, ITEM_PREV
);
2601 if ( hmenuprev
!= hmenutmp
|| pmt
->trackFlags
& TF_SUSPENDPOPUP
)
2603 /* A sublevel menu was displayed - display the next one
2604 * unless there is another displacement coming up */
2606 if( !MENU_SuspendPopup( pmt
, WM_KEYDOWN
) )
2607 pmt
->hCurrentMenu
= MENU_ShowSubPopup(pmt
->hOwnerWnd
,
2608 pmt
->hTopMenu
, TRUE
, wFlags
);
2614 /***********************************************************************
2617 * Handle a VK_RIGHT key event in a menu.
2619 static void MENU_KeyRight( MTRACKER
* pmt
, UINT wFlags
)
2622 POPUPMENU
*menu
= MENU_GetMenu( pmt
->hTopMenu
);
2625 TRACE("MENU_KeyRight called, cur %p (%s), top %p (%s).\n",
2627 debugstr_w((MENU_GetMenu(pmt
->hCurrentMenu
))->items
[0].text
),
2628 pmt
->hTopMenu
, debugstr_w(menu
->items
[0].text
) );
2630 if ( (menu
->wFlags
& MF_POPUP
) || (pmt
->hCurrentMenu
!= pmt
->hTopMenu
))
2632 /* If already displaying a popup, try to display sub-popup */
2634 hmenutmp
= pmt
->hCurrentMenu
;
2635 pmt
->hCurrentMenu
= MENU_ShowSubPopup(pmt
->hOwnerWnd
, hmenutmp
, TRUE
, wFlags
);
2637 /* if subpopup was displayed then we are done */
2638 if (hmenutmp
!= pmt
->hCurrentMenu
) return;
2641 /* Check to see if there's another column */
2642 if( (nextcol
= MENU_GetStartOfNextColumn( pmt
->hCurrentMenu
)) !=
2643 NO_SELECTED_ITEM
) {
2644 TRACE("Going to %d.\n", nextcol
);
2645 MENU_SelectItem( pmt
->hOwnerWnd
, pmt
->hCurrentMenu
,
2650 if (!(menu
->wFlags
& MF_POPUP
)) /* menu bar tracking */
2652 if( pmt
->hCurrentMenu
!= pmt
->hTopMenu
)
2654 MENU_HideSubPopups( pmt
->hOwnerWnd
, pmt
->hTopMenu
, FALSE
);
2655 hmenutmp
= pmt
->hCurrentMenu
= pmt
->hTopMenu
;
2656 } else hmenutmp
= 0;
2658 /* try to move to the next item */
2659 if( !MENU_DoNextMenu( pmt
, VK_RIGHT
) )
2660 MENU_MoveSelection( pmt
->hOwnerWnd
, pmt
->hTopMenu
, ITEM_NEXT
);
2662 if( hmenutmp
|| pmt
->trackFlags
& TF_SUSPENDPOPUP
)
2663 if( !MENU_SuspendPopup(pmt
, WM_KEYDOWN
) )
2664 pmt
->hCurrentMenu
= MENU_ShowSubPopup(pmt
->hOwnerWnd
,
2665 pmt
->hTopMenu
, TRUE
, wFlags
);
2669 /***********************************************************************
2672 * Menu tracking code.
2674 static INT
MENU_TrackMenu( HMENU hmenu
, UINT wFlags
, INT x
, INT y
,
2675 HWND hwnd
, const RECT
*lprect
)
2680 INT executedMenuId
= -1;
2682 BOOL enterIdleSent
= FALSE
;
2685 mt
.hCurrentMenu
= hmenu
;
2686 mt
.hTopMenu
= hmenu
;
2687 mt
.hOwnerWnd
= WIN_GetFullHandle( hwnd
);
2691 TRACE("hmenu=%p flags=0x%08x (%d,%d) hwnd=%p (%ld,%ld)-(%ld,%ld)\n",
2692 hmenu
, wFlags
, x
, y
, hwnd
, (lprect
) ? lprect
->left
: 0, (lprect
) ? lprect
->top
: 0,
2693 (lprect
) ? lprect
->right
: 0, (lprect
) ? lprect
->bottom
: 0);
2696 if (!(menu
= MENU_GetMenu( hmenu
))) return FALSE
;
2698 if (wFlags
& TPM_BUTTONDOWN
)
2700 /* Get the result in order to start the tracking or not */
2701 fRemove
= MENU_ButtonDown( &mt
, hmenu
, wFlags
);
2702 fEndMenu
= !fRemove
;
2705 MENU_SetCapture( mt
.hOwnerWnd
);
2709 menu
= MENU_GetMenu( mt
.hCurrentMenu
);
2710 if (!menu
) /* sometimes happens if I do a window manager close */
2713 /* we have to keep the message in the queue until it's
2714 * clear that menu loop is not over yet. */
2718 if (PeekMessageA( &msg
, 0, 0, 0, PM_NOREMOVE
))
2720 if (!CallMsgFilterA( &msg
, MSGF_MENU
)) break;
2721 /* remove the message from the queue */
2722 PeekMessageA( &msg
, 0, msg
.message
, msg
.message
, PM_REMOVE
);
2728 HWND win
= (wFlags
& TPM_ENTERIDLEEX
&& menu
->wFlags
& MF_POPUP
) ? menu
->hWnd
: 0;
2729 enterIdleSent
= TRUE
;
2730 SendMessageW( mt
.hOwnerWnd
, WM_ENTERIDLE
, MSGF_MENU
, (LPARAM
)win
);
2736 /* check if EndMenu() tried to cancel us, by posting this message */
2737 if(msg
.message
== WM_CANCELMODE
)
2739 /* we are now out of the loop */
2742 /* remove the message from the queue */
2743 PeekMessageA( &msg
, 0, msg
.message
, msg
.message
, PM_REMOVE
);
2745 /* break out of internal loop, ala ESCAPE */
2749 TranslateMessage( &msg
);
2752 if ( (msg
.hwnd
==menu
->hWnd
) || (msg
.message
!=WM_TIMER
) )
2753 enterIdleSent
=FALSE
;
2756 if ((msg
.message
>= WM_MOUSEFIRST
) && (msg
.message
<= WM_MOUSELAST
))
2759 * Use the mouse coordinates in lParam instead of those in the MSG
2760 * struct to properly handle synthetic messages. They are already
2761 * in screen coordinates.
2763 mt
.pt
.x
= SLOWORD(msg
.lParam
);
2764 mt
.pt
.y
= SHIWORD(msg
.lParam
);
2766 /* Find a menu for this mouse event */
2767 hmenu
= MENU_PtMenu( mt
.hTopMenu
, mt
.pt
);
2771 /* no WM_NC... messages in captured state */
2773 case WM_RBUTTONDBLCLK
:
2774 case WM_RBUTTONDOWN
:
2775 if (!(wFlags
& TPM_RIGHTBUTTON
)) break;
2777 case WM_LBUTTONDBLCLK
:
2778 case WM_LBUTTONDOWN
:
2779 /* If the message belongs to the menu, removes it from the queue */
2780 /* Else, end menu tracking */
2781 fRemove
= MENU_ButtonDown( &mt
, hmenu
, wFlags
);
2782 fEndMenu
= !fRemove
;
2786 if (!(wFlags
& TPM_RIGHTBUTTON
)) break;
2789 /* Check if a menu was selected by the mouse */
2792 executedMenuId
= MENU_ButtonUp( &mt
, hmenu
, wFlags
);
2794 /* End the loop if executedMenuId is an item ID */
2795 /* or if the job was done (executedMenuId = 0). */
2796 fEndMenu
= fRemove
= (executedMenuId
!= -1);
2798 /* No menu was selected by the mouse */
2799 /* if the function was called by TrackPopupMenu, continue
2800 with the menu tracking. If not, stop it */
2802 fEndMenu
= ((wFlags
& TPM_POPUPMENU
) ? FALSE
: TRUE
);
2807 /* In win95 winelook, the selected menu item must be changed every time the
2808 mouse moves. In Win31 winelook, the mouse button has to be held down */
2810 if ( hmenu
&& ((TWEAK_WineLook
> WIN31_LOOK
) ||
2811 ( (msg
.wParam
& MK_LBUTTON
) ||
2812 ((wFlags
& TPM_RIGHTBUTTON
) && (msg
.wParam
& MK_RBUTTON
)))) )
2814 fEndMenu
|= !MENU_MouseMove( &mt
, hmenu
, wFlags
);
2816 } /* switch(msg.message) - mouse */
2818 else if ((msg
.message
>= WM_KEYFIRST
) && (msg
.message
<= WM_KEYLAST
))
2820 fRemove
= TRUE
; /* Keyboard messages are always removed */
2828 MENU_SelectItem( mt
.hOwnerWnd
, mt
.hCurrentMenu
,
2829 NO_SELECTED_ITEM
, FALSE
, 0 );
2832 MENU_MoveSelection( mt
.hOwnerWnd
, mt
.hCurrentMenu
,
2833 (msg
.wParam
== VK_HOME
)? ITEM_NEXT
: ITEM_PREV
);
2836 case VK_DOWN
: /* If on menu bar, pull-down the menu */
2838 menu
= MENU_GetMenu( mt
.hCurrentMenu
);
2839 if (!(menu
->wFlags
& MF_POPUP
))
2840 mt
.hCurrentMenu
= MENU_ShowSubPopup(mt
.hOwnerWnd
, mt
.hTopMenu
, TRUE
, wFlags
);
2841 else /* otherwise try to move selection */
2842 MENU_MoveSelection( mt
.hOwnerWnd
, mt
.hCurrentMenu
, ITEM_NEXT
);
2846 MENU_KeyLeft( &mt
, wFlags
);
2850 MENU_KeyRight( &mt
, wFlags
);
2854 fEndMenu
= MENU_KeyEscape(&mt
, wFlags
);
2860 hi
.cbSize
= sizeof(HELPINFO
);
2861 hi
.iContextType
= HELPINFO_MENUITEM
;
2862 if (menu
->FocusedItem
== NO_SELECTED_ITEM
)
2865 hi
.iCtrlId
= menu
->items
[menu
->FocusedItem
].wID
;
2866 hi
.hItemHandle
= hmenu
;
2867 hi
.dwContextId
= menu
->dwContextHelpID
;
2868 hi
.MousePos
= msg
.pt
;
2869 SendMessageA(hwnd
, WM_HELP
, 0, (LPARAM
)&hi
);
2876 break; /* WM_KEYDOWN */
2886 break; /* WM_SYSKEYDOWN */
2892 if (msg
.wParam
== '\r' || msg
.wParam
== ' ')
2894 executedMenuId
= MENU_ExecFocusedItem(&mt
,mt
.hCurrentMenu
, wFlags
);
2895 fEndMenu
= (executedMenuId
!= -1);
2900 /* Hack to avoid control chars. */
2901 /* We will find a better way real soon... */
2902 if ((msg
.wParam
<= 32) || (msg
.wParam
>= 127)) break;
2904 pos
= MENU_FindItemByKey( mt
.hOwnerWnd
, mt
.hCurrentMenu
,
2905 LOWORD(msg
.wParam
), FALSE
);
2906 if (pos
== (UINT
)-2) fEndMenu
= TRUE
;
2907 else if (pos
== (UINT
)-1) MessageBeep(0);
2910 MENU_SelectItem( mt
.hOwnerWnd
, mt
.hCurrentMenu
, pos
,
2912 executedMenuId
= MENU_ExecFocusedItem(&mt
,mt
.hCurrentMenu
, wFlags
);
2913 fEndMenu
= (executedMenuId
!= -1);
2917 } /* switch(msg.message) - kbd */
2921 DispatchMessageA( &msg
);
2924 if (!fEndMenu
) fRemove
= TRUE
;
2926 /* finally remove message from the queue */
2928 if (fRemove
&& !(mt
.trackFlags
& TF_SKIPREMOVE
) )
2929 PeekMessageA( &msg
, 0, msg
.message
, msg
.message
, PM_REMOVE
);
2930 else mt
.trackFlags
&= ~TF_SKIPREMOVE
;
2933 MENU_SetCapture(0); /* release the capture */
2935 /* If dropdown is still painted and the close box is clicked on
2936 then the menu will be destroyed as part of the DispatchMessage above.
2937 This will then invalidate the menu handle in mt.hTopMenu. We should
2938 check for this first. */
2939 if( IsMenu( mt
.hTopMenu
) )
2941 menu
= MENU_GetMenu( mt
.hTopMenu
);
2943 if( IsWindow( mt
.hOwnerWnd
) )
2945 MENU_HideSubPopups( mt
.hOwnerWnd
, mt
.hTopMenu
, FALSE
);
2947 if (menu
&& menu
->wFlags
& MF_POPUP
)
2949 DestroyWindow( menu
->hWnd
);
2952 MENU_SelectItem( mt
.hOwnerWnd
, mt
.hTopMenu
, NO_SELECTED_ITEM
, FALSE
, 0 );
2953 SendMessageA( mt
.hOwnerWnd
, WM_MENUSELECT
, MAKELONG(0,0xffff), 0 );
2956 /* Reset the variable for hiding menu */
2957 if( menu
) menu
->bTimeToHide
= FALSE
;
2960 /* The return value is only used by TrackPopupMenu */
2961 return ((executedMenuId
!= -1) ? executedMenuId
: 0);
2964 /***********************************************************************
2967 static BOOL
MENU_InitTracking(HWND hWnd
, HMENU hMenu
, BOOL bPopup
, UINT wFlags
)
2969 TRACE("hwnd=%p hmenu=%p\n", hWnd
, hMenu
);
2973 /* Send WM_ENTERMENULOOP and WM_INITMENU message only if TPM_NONOTIFY flag is not specified */
2974 if (!(wFlags
& TPM_NONOTIFY
))
2975 SendMessageA( hWnd
, WM_ENTERMENULOOP
, bPopup
, 0 );
2977 SendMessageA( hWnd
, WM_SETCURSOR
, (WPARAM
)hWnd
, HTCAPTION
);
2979 if (!(wFlags
& TPM_NONOTIFY
))
2982 SendMessageA( hWnd
, WM_INITMENU
, (WPARAM
)hMenu
, 0 );
2983 if ((menu
= MENU_GetMenu( hMenu
)) && (!menu
->Height
))
2984 { /* app changed/recreated menu bar entries in WM_INITMENU
2985 Recalculate menu sizes else clicks will not work */
2986 SetWindowPos( hWnd
, 0, 0, 0, 0, 0, SWP_NOSIZE
| SWP_NOMOVE
|
2987 SWP_NOACTIVATE
| SWP_NOZORDER
| SWP_FRAMECHANGED
);
2993 /***********************************************************************
2996 static BOOL
MENU_ExitTracking(HWND hWnd
)
2998 TRACE("hwnd=%p\n", hWnd
);
3000 SendMessageA( hWnd
, WM_EXITMENULOOP
, 0, 0 );
3005 /***********************************************************************
3006 * MENU_TrackMouseMenuBar
3008 * Menu-bar tracking upon a mouse event. Called from NC_HandleSysCommand().
3010 void MENU_TrackMouseMenuBar( HWND hWnd
, INT ht
, POINT pt
)
3012 HMENU hMenu
= (ht
== HTSYSMENU
) ? get_win_sys_menu( hWnd
) : GetMenu( hWnd
);
3013 UINT wFlags
= TPM_ENTERIDLEEX
| TPM_BUTTONDOWN
| TPM_LEFTALIGN
| TPM_LEFTBUTTON
;
3015 TRACE("wnd=%p ht=0x%04x (%ld,%ld)\n", hWnd
, ht
, pt
.x
, pt
.y
);
3019 /* map point to parent client coordinates */
3020 HWND parent
= GetAncestor( hWnd
, GA_PARENT
);
3021 if (parent
!= GetDesktopWindow()) ScreenToClient( parent
, &pt
);
3023 MENU_InitTracking( hWnd
, hMenu
, FALSE
, wFlags
);
3024 MENU_TrackMenu( hMenu
, wFlags
, pt
.x
, pt
.y
, hWnd
, NULL
);
3025 MENU_ExitTracking(hWnd
);
3030 /***********************************************************************
3031 * MENU_TrackKbdMenuBar
3033 * Menu-bar tracking upon a keyboard event. Called from NC_HandleSysCommand().
3035 void MENU_TrackKbdMenuBar( HWND hwnd
, UINT wParam
, INT vkey
)
3037 UINT uItem
= NO_SELECTED_ITEM
;
3039 UINT wFlags
= TPM_ENTERIDLEEX
| TPM_LEFTALIGN
| TPM_LEFTBUTTON
;
3041 /* find window that has a menu */
3043 while (GetWindowLongA( hwnd
, GWL_STYLE
) & WS_CHILD
)
3044 if (!(hwnd
= GetParent( hwnd
))) return;
3046 /* check if we have to track a system menu */
3048 hTrackMenu
= GetMenu( hwnd
);
3049 if (!hTrackMenu
|| IsIconic(hwnd
) || vkey
== VK_SPACE
)
3051 if (!(GetWindowLongA( hwnd
, GWL_STYLE
) & WS_SYSMENU
)) return;
3052 hTrackMenu
= get_win_sys_menu( hwnd
);
3054 wParam
|= HTSYSMENU
; /* prevent item lookup */
3057 if (!IsMenu( hTrackMenu
)) return;
3059 MENU_InitTracking( hwnd
, hTrackMenu
, FALSE
, wFlags
);
3061 if( vkey
&& vkey
!= VK_SPACE
)
3063 uItem
= MENU_FindItemByKey( hwnd
, hTrackMenu
, vkey
, (wParam
& HTSYSMENU
) );
3064 if( uItem
>= (UINT
)(-2) )
3066 if( uItem
== (UINT
)(-1) ) MessageBeep(0);
3073 MENU_SelectItem( hwnd
, hTrackMenu
, uItem
, TRUE
, 0 );
3075 if( uItem
== NO_SELECTED_ITEM
)
3076 MENU_MoveSelection( hwnd
, hTrackMenu
, ITEM_NEXT
);
3078 PostMessageA( hwnd
, WM_KEYDOWN
, VK_DOWN
, 0L );
3080 MENU_TrackMenu( hTrackMenu
, wFlags
, 0, 0, hwnd
, NULL
);
3082 MENU_ExitTracking( hwnd
);
3086 /**********************************************************************
3087 * TrackPopupMenu (USER32.@)
3089 * Like the win32 API, the function return the command ID only if the
3090 * flag TPM_RETURNCMD is on.
3093 BOOL WINAPI
TrackPopupMenu( HMENU hMenu
, UINT wFlags
, INT x
, INT y
,
3094 INT nReserved
, HWND hWnd
, const RECT
*lpRect
)
3098 MENU_InitTracking(hWnd
, hMenu
, TRUE
, wFlags
);
3100 /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
3101 if (!(wFlags
& TPM_NONOTIFY
))
3102 SendMessageA( hWnd
, WM_INITMENUPOPUP
, (WPARAM
)hMenu
, 0);
3104 if (MENU_ShowPopup( hWnd
, hMenu
, 0, x
, y
, 0, 0 ))
3105 ret
= MENU_TrackMenu( hMenu
, wFlags
| TPM_POPUPMENU
, 0, 0, hWnd
, lpRect
);
3106 MENU_ExitTracking(hWnd
);
3108 if( (!(wFlags
& TPM_RETURNCMD
)) && (ret
!= FALSE
) )
3114 /**********************************************************************
3115 * TrackPopupMenuEx (USER32.@)
3117 BOOL WINAPI
TrackPopupMenuEx( HMENU hMenu
, UINT wFlags
, INT x
, INT y
,
3118 HWND hWnd
, LPTPMPARAMS lpTpm
)
3120 FIXME("not fully implemented\n" );
3121 return TrackPopupMenu( hMenu
, wFlags
, x
, y
, 0, hWnd
,
3122 lpTpm
? &lpTpm
->rcExclude
: NULL
);
3125 /***********************************************************************
3128 * NOTE: Windows has totally different (and undocumented) popup wndproc.
3130 static LRESULT WINAPI
PopupMenuWndProc( HWND hwnd
, UINT message
, WPARAM wParam
, LPARAM lParam
)
3132 TRACE("hwnd=%p msg=0x%04x wp=0x%04x lp=0x%08lx\n", hwnd
, message
, wParam
, lParam
);
3138 CREATESTRUCTW
*cs
= (CREATESTRUCTW
*)lParam
;
3139 SetWindowLongW( hwnd
, 0, (LONG
)cs
->lpCreateParams
);
3143 case WM_MOUSEACTIVATE
: /* We don't want to be activated */
3144 return MA_NOACTIVATE
;
3149 BeginPaint( hwnd
, &ps
);
3150 MENU_DrawPopupMenu( hwnd
, ps
.hdc
,
3151 (HMENU
)GetWindowLongA( hwnd
, 0 ) );
3152 EndPaint( hwnd
, &ps
);
3159 /* zero out global pointer in case resident popup window was destroyed. */
3160 if (hwnd
== top_popup
) top_popup
= 0;
3167 if (!GetWindowLongW( hwnd
, 0 )) ERR("no menu to display\n");
3170 SetWindowLongW( hwnd
, 0, 0 );
3173 case MM_SETMENUHANDLE
:
3174 SetWindowLongW( hwnd
, 0, wParam
);
3177 case MM_GETMENUHANDLE
:
3178 return GetWindowLongW( hwnd
, 0 );
3181 return DefWindowProcW( hwnd
, message
, wParam
, lParam
);
3187 /***********************************************************************
3188 * MENU_GetMenuBarHeight
3190 * Compute the size of the menu bar height. Used by NC_HandleNCCalcSize().
3192 UINT
MENU_GetMenuBarHeight( HWND hwnd
, UINT menubarWidth
,
3193 INT orgX
, INT orgY
)
3199 TRACE("HWND %p, width %d, at (%d, %d).\n", hwnd
, menubarWidth
, orgX
, orgY
);
3201 if (!(lppop
= MENU_GetMenu( GetMenu(hwnd
) ))) return 0;
3203 hdc
= GetDCEx( hwnd
, 0, DCX_CACHE
| DCX_WINDOW
);
3204 SelectObject( hdc
, hMenuFont
);
3205 SetRect(&rectBar
, orgX
, orgY
, orgX
+menubarWidth
, orgY
+GetSystemMetrics(SM_CYMENU
));
3206 MENU_MenuBarCalcSize( hdc
, &rectBar
, lppop
, hwnd
);
3207 ReleaseDC( hwnd
, hdc
);
3208 return lppop
->Height
;
3212 /*******************************************************************
3213 * ChangeMenuA (USER32.@)
3215 BOOL WINAPI
ChangeMenuA( HMENU hMenu
, UINT pos
, LPCSTR data
,
3216 UINT id
, UINT flags
)
3218 TRACE("menu=%p pos=%d data=%p id=%08x flags=%08x\n", hMenu
, pos
, data
, id
, flags
);
3219 if (flags
& MF_APPEND
) return AppendMenuA( hMenu
, flags
& ~MF_APPEND
,
3221 if (flags
& MF_DELETE
) return DeleteMenu(hMenu
, pos
, flags
& ~MF_DELETE
);
3222 if (flags
& MF_CHANGE
) return ModifyMenuA(hMenu
, pos
, flags
& ~MF_CHANGE
,
3224 if (flags
& MF_REMOVE
) return RemoveMenu( hMenu
,
3225 flags
& MF_BYPOSITION
? pos
: id
,
3226 flags
& ~MF_REMOVE
);
3227 /* Default: MF_INSERT */
3228 return InsertMenuA( hMenu
, pos
, flags
, id
, data
);
3232 /*******************************************************************
3233 * ChangeMenuW (USER32.@)
3235 BOOL WINAPI
ChangeMenuW( HMENU hMenu
, UINT pos
, LPCWSTR data
,
3236 UINT id
, UINT flags
)
3238 TRACE("menu=%p pos=%d data=%p id=%08x flags=%08x\n", hMenu
, pos
, data
, id
, flags
);
3239 if (flags
& MF_APPEND
) return AppendMenuW( hMenu
, flags
& ~MF_APPEND
,
3241 if (flags
& MF_DELETE
) return DeleteMenu(hMenu
, pos
, flags
& ~MF_DELETE
);
3242 if (flags
& MF_CHANGE
) return ModifyMenuW(hMenu
, pos
, flags
& ~MF_CHANGE
,
3244 if (flags
& MF_REMOVE
) return RemoveMenu( hMenu
,
3245 flags
& MF_BYPOSITION
? pos
: id
,
3246 flags
& ~MF_REMOVE
);
3247 /* Default: MF_INSERT */
3248 return InsertMenuW( hMenu
, pos
, flags
, id
, data
);
3252 /*******************************************************************
3253 * CheckMenuItem (USER32.@)
3255 DWORD WINAPI
CheckMenuItem( HMENU hMenu
, UINT id
, UINT flags
)
3260 TRACE("menu=%p id=%04x flags=%04x\n", hMenu
, id
, flags
);
3261 if (!(item
= MENU_FindItem( &hMenu
, &id
, flags
))) return -1;
3262 ret
= item
->fState
& MF_CHECKED
;
3263 if (flags
& MF_CHECKED
) item
->fState
|= MF_CHECKED
;
3264 else item
->fState
&= ~MF_CHECKED
;
3269 /**********************************************************************
3270 * EnableMenuItem (USER32.@)
3272 UINT WINAPI
EnableMenuItem( HMENU hMenu
, UINT wItemID
, UINT wFlags
)
3278 TRACE("(%p, %04x, %04x) !\n", hMenu
, wItemID
, wFlags
);
3280 /* Get the Popupmenu to access the owner menu */
3281 if (!(menu
= MENU_GetMenu(hMenu
)))
3284 if (!(item
= MENU_FindItem( &hMenu
, &wItemID
, wFlags
)))
3287 oldflags
= item
->fState
& (MF_GRAYED
| MF_DISABLED
);
3288 item
->fState
^= (oldflags
^ wFlags
) & (MF_GRAYED
| MF_DISABLED
);
3290 /* In win95 if the close item in the system menu change update the close button */
3291 if (TWEAK_WineLook
== WIN95_LOOK
)
3292 if((item
->wID
== SC_CLOSE
) && (oldflags
!= wFlags
))
3294 if (menu
->hSysMenuOwner
!= 0)
3296 POPUPMENU
* parentMenu
;
3298 /* Get the parent menu to access*/
3299 if (!(parentMenu
= MENU_GetMenu(menu
->hSysMenuOwner
)))
3302 /* Refresh the frame to reflect the change*/
3303 SetWindowPos(parentMenu
->hWnd
, 0, 0, 0, 0, 0,
3304 SWP_NOACTIVATE
| SWP_NOMOVE
| SWP_NOSIZE
| SWP_NOZORDER
);
3312 /*******************************************************************
3313 * GetMenuStringA (USER32.@)
3315 INT WINAPI
GetMenuStringA(
3316 HMENU hMenu
, /* [in] menuhandle */
3317 UINT wItemID
, /* [in] menu item (dep. on wFlags) */
3318 LPSTR str
, /* [out] outbuffer. If NULL, func returns entry length*/
3319 INT nMaxSiz
, /* [in] length of buffer. if 0, func returns entry len*/
3320 UINT wFlags
/* [in] MF_ flags */
3324 TRACE("menu=%p item=%04x ptr=%p len=%d flags=%04x\n", hMenu
, wItemID
, str
, nMaxSiz
, wFlags
);
3325 if (!(item
= MENU_FindItem( &hMenu
, &wItemID
, wFlags
))) return 0;
3326 if (!IS_STRING_ITEM(item
->fType
)) return 0;
3327 if (!str
|| !nMaxSiz
) return strlenW(item
->text
);
3329 if (!WideCharToMultiByte( CP_ACP
, 0, item
->text
, -1, str
, nMaxSiz
, NULL
, NULL
))
3331 TRACE("returning '%s'\n", str
);
3336 /*******************************************************************
3337 * GetMenuStringW (USER32.@)
3339 INT WINAPI
GetMenuStringW( HMENU hMenu
, UINT wItemID
,
3340 LPWSTR str
, INT nMaxSiz
, UINT wFlags
)
3344 TRACE("menu=%p item=%04x ptr=%p len=%d flags=%04x\n", hMenu
, wItemID
, str
, nMaxSiz
, wFlags
);
3345 if (!(item
= MENU_FindItem( &hMenu
, &wItemID
, wFlags
))) return 0;
3346 if (!IS_STRING_ITEM(item
->fType
)) return 0;
3347 if (!str
|| !nMaxSiz
) return strlenW(item
->text
);
3349 lstrcpynW( str
, item
->text
, nMaxSiz
);
3350 return strlenW(str
);
3354 /**********************************************************************
3355 * HiliteMenuItem (USER32.@)
3357 BOOL WINAPI
HiliteMenuItem( HWND hWnd
, HMENU hMenu
, UINT wItemID
,
3361 TRACE("(%p, %p, %04x, %04x);\n", hWnd
, hMenu
, wItemID
, wHilite
);
3362 if (!MENU_FindItem( &hMenu
, &wItemID
, wHilite
)) return FALSE
;
3363 if (!(menu
= MENU_GetMenu(hMenu
))) return FALSE
;
3364 if (menu
->FocusedItem
== wItemID
) return TRUE
;
3365 MENU_HideSubPopups( hWnd
, hMenu
, FALSE
);
3366 MENU_SelectItem( hWnd
, hMenu
, wItemID
, TRUE
, 0 );
3371 /**********************************************************************
3372 * GetMenuState (USER32.@)
3374 UINT WINAPI
GetMenuState( HMENU hMenu
, UINT wItemID
, UINT wFlags
)
3377 TRACE("(menu=%p, id=%04x, flags=%04x);\n", hMenu
, wItemID
, wFlags
);
3378 if (!(item
= MENU_FindItem( &hMenu
, &wItemID
, wFlags
))) return -1;
3379 debug_print_menuitem (" item: ", item
, "");
3380 if (item
->fType
& MF_POPUP
)
3382 POPUPMENU
*menu
= MENU_GetMenu( item
->hSubMenu
);
3383 if (!menu
) return -1;
3384 else return (menu
->nItems
<< 8) | ((item
->fState
|item
->fType
) & 0xff);
3388 /* We used to (from way back then) mask the result to 0xff. */
3389 /* I don't know why and it seems wrong as the documented */
3390 /* return flag MF_SEPARATOR is outside that mask. */
3391 return (item
->fType
| item
->fState
);
3396 /**********************************************************************
3397 * GetMenuItemCount (USER32.@)
3399 INT WINAPI
GetMenuItemCount( HMENU hMenu
)
3401 LPPOPUPMENU menu
= MENU_GetMenu(hMenu
);
3402 if (!menu
) return -1;
3403 TRACE("(%p) returning %d\n", hMenu
, menu
->nItems
);
3404 return menu
->nItems
;
3408 /**********************************************************************
3409 * GetMenuItemID (USER32.@)
3411 UINT WINAPI
GetMenuItemID( HMENU hMenu
, INT nPos
)
3415 if (!(lpmi
= MENU_FindItem(&hMenu
,&nPos
,MF_BYPOSITION
))) return -1;
3416 if (lpmi
->fType
& MF_POPUP
) return -1;
3422 /*******************************************************************
3423 * InsertMenuW (USER32.@)
3425 BOOL WINAPI
InsertMenuW( HMENU hMenu
, UINT pos
, UINT flags
,
3426 UINT_PTR id
, LPCWSTR str
)
3430 if (IS_STRING_ITEM(flags
) && str
)
3431 TRACE("hMenu %p, pos %d, flags %08x, id %04x, str %s\n",
3432 hMenu
, pos
, flags
, id
, debugstr_w(str
) );
3433 else TRACE("hMenu %p, pos %d, flags %08x, id %04x, str %08lx (not a string)\n",
3434 hMenu
, pos
, flags
, id
, (DWORD
)str
);
3436 if (!(item
= MENU_InsertItem( hMenu
, pos
, flags
))) return FALSE
;
3438 if (!(MENU_SetItemData( item
, flags
, id
, str
)))
3440 RemoveMenu( hMenu
, pos
, flags
);
3444 if (flags
& MF_POPUP
) /* Set the MF_POPUP flag on the popup-menu */
3445 (MENU_GetMenu((HMENU
)id
))->wFlags
|= MF_POPUP
;
3447 item
->hCheckBit
= item
->hUnCheckBit
= 0;
3452 /*******************************************************************
3453 * InsertMenuA (USER32.@)
3455 BOOL WINAPI
InsertMenuA( HMENU hMenu
, UINT pos
, UINT flags
,
3456 UINT_PTR id
, LPCSTR str
)
3460 if (IS_STRING_ITEM(flags
) && str
)
3462 INT len
= MultiByteToWideChar( CP_ACP
, 0, str
, -1, NULL
, 0 );
3463 LPWSTR newstr
= HeapAlloc( GetProcessHeap(), 0, len
* sizeof(WCHAR
) );
3466 MultiByteToWideChar( CP_ACP
, 0, str
, -1, newstr
, len
);
3467 ret
= InsertMenuW( hMenu
, pos
, flags
, id
, newstr
);
3468 HeapFree( GetProcessHeap(), 0, newstr
);
3472 else return InsertMenuW( hMenu
, pos
, flags
, id
, (LPCWSTR
)str
);
3476 /*******************************************************************
3477 * AppendMenuA (USER32.@)
3479 BOOL WINAPI
AppendMenuA( HMENU hMenu
, UINT flags
,
3480 UINT_PTR id
, LPCSTR data
)
3482 return InsertMenuA( hMenu
, -1, flags
| MF_BYPOSITION
, id
, data
);
3486 /*******************************************************************
3487 * AppendMenuW (USER32.@)
3489 BOOL WINAPI
AppendMenuW( HMENU hMenu
, UINT flags
,
3490 UINT_PTR id
, LPCWSTR data
)
3492 return InsertMenuW( hMenu
, -1, flags
| MF_BYPOSITION
, id
, data
);
3496 /**********************************************************************
3497 * RemoveMenu (USER32.@)
3499 BOOL WINAPI
RemoveMenu( HMENU hMenu
, UINT nPos
, UINT wFlags
)
3504 TRACE("(menu=%p pos=%04x flags=%04x)\n",hMenu
, nPos
, wFlags
);
3505 if (!(item
= MENU_FindItem( &hMenu
, &nPos
, wFlags
))) return FALSE
;
3506 if (!(menu
= MENU_GetMenu(hMenu
))) return FALSE
;
3510 MENU_FreeItemData( item
);
3512 if (--menu
->nItems
== 0)
3514 HeapFree( GetProcessHeap(), 0, menu
->items
);
3519 while(nPos
< menu
->nItems
)
3525 menu
->items
= HeapReAlloc( GetProcessHeap(), 0, menu
->items
,
3526 menu
->nItems
* sizeof(MENUITEM
) );
3532 /**********************************************************************
3533 * DeleteMenu (USER32.@)
3535 BOOL WINAPI
DeleteMenu( HMENU hMenu
, UINT nPos
, UINT wFlags
)
3537 MENUITEM
*item
= MENU_FindItem( &hMenu
, &nPos
, wFlags
);
3538 if (!item
) return FALSE
;
3539 if (item
->fType
& MF_POPUP
) DestroyMenu( item
->hSubMenu
);
3540 /* nPos is now the position of the item */
3541 RemoveMenu( hMenu
, nPos
, wFlags
| MF_BYPOSITION
);
3546 /*******************************************************************
3547 * ModifyMenuW (USER32.@)
3549 BOOL WINAPI
ModifyMenuW( HMENU hMenu
, UINT pos
, UINT flags
,
3550 UINT_PTR id
, LPCWSTR str
)
3554 if (IS_STRING_ITEM(flags
))
3556 TRACE("%p %d %04x %04x %s\n", hMenu
, pos
, flags
, id
, debugstr_w(str
) );
3557 if (!str
) return FALSE
;
3561 TRACE("%p %d %04x %04x %08lx\n", hMenu
, pos
, flags
, id
, (DWORD
)str
);
3564 if (!(item
= MENU_FindItem( &hMenu
, &pos
, flags
))) return FALSE
;
3565 return MENU_SetItemData( item
, flags
, id
, str
);
3569 /*******************************************************************
3570 * ModifyMenuA (USER32.@)
3572 BOOL WINAPI
ModifyMenuA( HMENU hMenu
, UINT pos
, UINT flags
,
3573 UINT_PTR id
, LPCSTR str
)
3577 if (IS_STRING_ITEM(flags
) && str
)
3579 INT len
= MultiByteToWideChar( CP_ACP
, 0, str
, -1, NULL
, 0 );
3580 LPWSTR newstr
= HeapAlloc( GetProcessHeap(), 0, len
* sizeof(WCHAR
) );
3583 MultiByteToWideChar( CP_ACP
, 0, str
, -1, newstr
, len
);
3584 ret
= ModifyMenuW( hMenu
, pos
, flags
, id
, newstr
);
3585 HeapFree( GetProcessHeap(), 0, newstr
);
3589 else return ModifyMenuW( hMenu
, pos
, flags
, id
, (LPCWSTR
)str
);
3593 /**********************************************************************
3594 * CreatePopupMenu (USER32.@)
3596 HMENU WINAPI
CreatePopupMenu(void)
3601 if (!(hmenu
= CreateMenu())) return 0;
3602 menu
= MENU_GetMenu( hmenu
);
3603 menu
->wFlags
|= MF_POPUP
;
3604 menu
->bTimeToHide
= FALSE
;
3609 /**********************************************************************
3610 * GetMenuCheckMarkDimensions (USER.417)
3611 * GetMenuCheckMarkDimensions (USER32.@)
3613 DWORD WINAPI
GetMenuCheckMarkDimensions(void)
3615 return MAKELONG( GetSystemMetrics(SM_CXMENUCHECK
), GetSystemMetrics(SM_CYMENUCHECK
) );
3619 /**********************************************************************
3620 * SetMenuItemBitmaps (USER32.@)
3622 BOOL WINAPI
SetMenuItemBitmaps( HMENU hMenu
, UINT nPos
, UINT wFlags
,
3623 HBITMAP hNewUnCheck
, HBITMAP hNewCheck
)
3626 TRACE("(%p, %04x, %04x, %p, %p)\n",
3627 hMenu
, nPos
, wFlags
, hNewCheck
, hNewUnCheck
);
3628 if (!(item
= MENU_FindItem( &hMenu
, &nPos
, wFlags
))) return FALSE
;
3630 if (!hNewCheck
&& !hNewUnCheck
)
3632 item
->fState
&= ~MF_USECHECKBITMAPS
;
3634 else /* Install new bitmaps */
3636 item
->hCheckBit
= hNewCheck
;
3637 item
->hUnCheckBit
= hNewUnCheck
;
3638 item
->fState
|= MF_USECHECKBITMAPS
;
3644 /**********************************************************************
3645 * CreateMenu (USER32.@)
3647 HMENU WINAPI
CreateMenu(void)
3651 if (!(hMenu
= USER_HEAP_ALLOC( sizeof(POPUPMENU
) ))) return 0;
3652 menu
= (LPPOPUPMENU
) USER_HEAP_LIN_ADDR(hMenu
);
3654 ZeroMemory(menu
, sizeof(POPUPMENU
));
3655 menu
->wMagic
= MENU_MAGIC
;
3656 menu
->FocusedItem
= NO_SELECTED_ITEM
;
3657 menu
->bTimeToHide
= FALSE
;
3659 TRACE("return %p\n", hMenu
);
3665 /**********************************************************************
3666 * DestroyMenu (USER32.@)
3668 BOOL WINAPI
DestroyMenu( HMENU hMenu
)
3670 TRACE("(%p)\n", hMenu
);
3672 /* Silently ignore attempts to destroy default system popup */
3674 if (hMenu
&& hMenu
!= MENU_DefSysPopup
)
3676 LPPOPUPMENU lppop
= MENU_GetMenu(hMenu
);
3678 if (!lppop
) return FALSE
;
3680 lppop
->wMagic
= 0; /* Mark it as destroyed */
3682 if ((lppop
->wFlags
& MF_POPUP
) && lppop
->hWnd
)
3684 DestroyWindow( lppop
->hWnd
);
3688 if (lppop
->items
) /* recursively destroy submenus */
3691 MENUITEM
*item
= lppop
->items
;
3692 for (i
= lppop
->nItems
; i
> 0; i
--, item
++)
3694 if (item
->fType
& MF_POPUP
) DestroyMenu(item
->hSubMenu
);
3695 MENU_FreeItemData( item
);
3697 HeapFree( GetProcessHeap(), 0, lppop
->items
);
3699 USER_HEAP_FREE( hMenu
);
3701 return (hMenu
!= MENU_DefSysPopup
);
3705 /**********************************************************************
3706 * GetSystemMenu (USER32.@)
3708 HMENU WINAPI
GetSystemMenu( HWND hWnd
, BOOL bRevert
)
3710 WND
*wndPtr
= WIN_FindWndPtr( hWnd
);
3715 if( wndPtr
->hSysMenu
)
3719 DestroyMenu(wndPtr
->hSysMenu
);
3720 wndPtr
->hSysMenu
= 0;
3724 POPUPMENU
*menu
= MENU_GetMenu( wndPtr
->hSysMenu
);
3727 if( menu
->nItems
> 0 && menu
->items
[0].hSubMenu
== MENU_DefSysPopup
)
3728 menu
->items
[0].hSubMenu
= MENU_CopySysPopup();
3732 WARN("Current sys-menu (%p) of wnd %p is broken\n",
3733 wndPtr
->hSysMenu
, hWnd
);
3734 wndPtr
->hSysMenu
= 0;
3739 if(!wndPtr
->hSysMenu
&& (wndPtr
->dwStyle
& WS_SYSMENU
) )
3740 wndPtr
->hSysMenu
= MENU_GetSysMenu( hWnd
, (HMENU
)(-1) );
3742 if( wndPtr
->hSysMenu
)
3745 retvalue
= GetSubMenu(wndPtr
->hSysMenu
, 0);
3747 /* Store the dummy sysmenu handle to facilitate the refresh */
3748 /* of the close button if the SC_CLOSE item change */
3749 menu
= MENU_GetMenu(retvalue
);
3751 menu
->hSysMenuOwner
= wndPtr
->hSysMenu
;
3753 WIN_ReleaseWndPtr(wndPtr
);
3755 return bRevert
? 0 : retvalue
;
3759 /*******************************************************************
3760 * SetSystemMenu (USER32.@)
3762 BOOL WINAPI
SetSystemMenu( HWND hwnd
, HMENU hMenu
)
3764 WND
*wndPtr
= WIN_FindWndPtr(hwnd
);
3768 if (wndPtr
->hSysMenu
) DestroyMenu( wndPtr
->hSysMenu
);
3769 wndPtr
->hSysMenu
= MENU_GetSysMenu( hwnd
, hMenu
);
3770 WIN_ReleaseWndPtr(wndPtr
);
3777 /**********************************************************************
3778 * GetMenu (USER32.@)
3780 HMENU WINAPI
GetMenu( HWND hWnd
)
3782 HMENU retvalue
= (HMENU
)GetWindowLongA( hWnd
, GWL_ID
);
3783 TRACE("for %p returning %p\n", hWnd
, retvalue
);
3788 /**********************************************************************
3789 * SetMenu (USER32.@)
3791 BOOL WINAPI
SetMenu( HWND hWnd
, HMENU hMenu
)
3793 TRACE("(%p, %p);\n", hWnd
, hMenu
);
3795 if (hMenu
&& !IsMenu(hMenu
))
3797 WARN("hMenu %p is not a menu handle\n", hMenu
);
3800 if (GetWindowLongA( hWnd
, GWL_STYLE
) & WS_CHILD
) return FALSE
;
3802 hWnd
= WIN_GetFullHandle( hWnd
);
3803 if (GetCapture() == hWnd
) MENU_SetCapture(0); /* release the capture */
3809 if (!(lpmenu
= MENU_GetMenu(hMenu
))) return FALSE
;
3811 lpmenu
->hWnd
= hWnd
;
3812 lpmenu
->Height
= 0; /* Make sure we recalculate the size */
3814 SetWindowLongA( hWnd
, GWL_ID
, (LONG_PTR
)hMenu
);
3816 if (IsWindowVisible(hWnd
))
3817 SetWindowPos( hWnd
, 0, 0, 0, 0, 0, SWP_NOSIZE
| SWP_NOMOVE
|
3818 SWP_NOACTIVATE
| SWP_NOZORDER
| SWP_FRAMECHANGED
);
3824 /**********************************************************************
3825 * GetSubMenu (USER32.@)
3827 HMENU WINAPI
GetSubMenu( HMENU hMenu
, INT nPos
)
3831 if (!(lpmi
= MENU_FindItem(&hMenu
,&nPos
,MF_BYPOSITION
))) return 0;
3832 if (!(lpmi
->fType
& MF_POPUP
)) return 0;
3833 return lpmi
->hSubMenu
;
3837 /**********************************************************************
3838 * DrawMenuBar (USER32.@)
3840 BOOL WINAPI
DrawMenuBar( HWND hWnd
)
3843 HMENU hMenu
= GetMenu(hWnd
);
3845 if (GetWindowLongA( hWnd
, GWL_STYLE
) & WS_CHILD
) return FALSE
;
3846 if (!hMenu
|| !(lppop
= MENU_GetMenu( hMenu
))) return FALSE
;
3848 lppop
->Height
= 0; /* Make sure we call MENU_MenuBarCalcSize */
3849 lppop
->hwndOwner
= hWnd
;
3850 SetWindowPos( hWnd
, 0, 0, 0, 0, 0, SWP_NOSIZE
| SWP_NOMOVE
|
3851 SWP_NOACTIVATE
| SWP_NOZORDER
| SWP_FRAMECHANGED
);
3855 /***********************************************************************
3856 * DrawMenuBarTemp (USER32.@)
3860 * called by W98SE desk.cpl Control Panel Applet
3862 * Not 100% sure about the param names, but close.
3864 DWORD WINAPI
DrawMenuBarTemp(HWND someHWND
, HDC someHDC
, LPRECT someRECT
, HMENU someHMENU
, HFONT someFONT
)
3866 FIXME("(%p, %p, %p, %p, %p): stub\n", someHWND
, someHDC
, someRECT
, someHMENU
, someFONT
);
3870 /***********************************************************************
3871 * EndMenu (USER.187)
3872 * EndMenu (USER32.@)
3874 void WINAPI
EndMenu(void)
3876 /* if we are in the menu code, and it is active */
3877 if (!fEndMenu
&& top_popup
)
3879 /* terminate the menu handling code */
3882 /* needs to be posted to wakeup the internal menu handler */
3883 /* which will now terminate the menu, in the event that */
3884 /* the main window was minimized, or lost focus, so we */
3885 /* don't end up with an orphaned menu */
3886 PostMessageA( top_popup
, WM_CANCELMODE
, 0, 0);
3891 /***********************************************************************
3892 * LookupMenuHandle (USER.217)
3894 HMENU16 WINAPI
LookupMenuHandle16( HMENU16 hmenu
, INT16 id
)
3896 HMENU hmenu32
= HMENU_32(hmenu
);
3898 if (!MENU_FindItem( &hmenu32
, &id32
, MF_BYCOMMAND
)) return 0;
3899 else return HMENU_16(hmenu32
);
3903 /**********************************************************************
3904 * LoadMenu (USER.150)
3906 HMENU16 WINAPI
LoadMenu16( HINSTANCE16 instance
, LPCSTR name
)
3912 if (HIWORD(name
) && name
[0] == '#') name
= (LPCSTR
)atoi( name
+ 1 );
3913 if (!name
) return 0;
3915 instance
= GetExePtr( instance
);
3916 if (!(hRsrc
= FindResource16( instance
, name
, RT_MENUA
))) return 0;
3917 if (!(handle
= LoadResource16( instance
, hRsrc
))) return 0;
3918 hMenu
= LoadMenuIndirect16(LockResource16(handle
));
3919 FreeResource16( handle
);
3924 /*****************************************************************
3925 * LoadMenuA (USER32.@)
3927 HMENU WINAPI
LoadMenuA( HINSTANCE instance
, LPCSTR name
)
3929 HRSRC hrsrc
= FindResourceA( instance
, name
, RT_MENUA
);
3930 if (!hrsrc
) return 0;
3931 return LoadMenuIndirectA( (LPCVOID
)LoadResource( instance
, hrsrc
));
3935 /*****************************************************************
3936 * LoadMenuW (USER32.@)
3938 HMENU WINAPI
LoadMenuW( HINSTANCE instance
, LPCWSTR name
)
3940 HRSRC hrsrc
= FindResourceW( instance
, name
, RT_MENUW
);
3941 if (!hrsrc
) return 0;
3942 return LoadMenuIndirectW( (LPCVOID
)LoadResource( instance
, hrsrc
));
3946 /**********************************************************************
3947 * LoadMenuIndirect (USER.220)
3949 HMENU16 WINAPI
LoadMenuIndirect16( LPCVOID
template )
3952 WORD version
, offset
;
3953 LPCSTR p
= (LPCSTR
)template;
3955 TRACE("(%p)\n", template );
3956 version
= GET_WORD(p
);
3960 WARN("version must be 0 for Win16\n" );
3963 offset
= GET_WORD(p
);
3964 p
+= sizeof(WORD
) + offset
;
3965 if (!(hMenu
= CreateMenu())) return 0;
3966 if (!MENU_ParseResource( p
, hMenu
, FALSE
))
3968 DestroyMenu( hMenu
);
3971 return HMENU_16(hMenu
);
3975 /**********************************************************************
3976 * LoadMenuIndirectA (USER32.@)
3978 HMENU WINAPI
LoadMenuIndirectA( LPCVOID
template )
3981 WORD version
, offset
;
3982 LPCSTR p
= (LPCSTR
)template;
3984 version
= GET_WORD(p
);
3986 TRACE("%p, ver %d\n", template, version
);
3989 case 0: /* standard format is version of 0 */
3990 offset
= GET_WORD(p
);
3991 p
+= sizeof(WORD
) + offset
;
3992 if (!(hMenu
= CreateMenu())) return 0;
3993 if (!MENU_ParseResource( p
, hMenu
, TRUE
))
3995 DestroyMenu( hMenu
);
3999 case 1: /* extended format is version of 1 */
4000 offset
= GET_WORD(p
);
4001 p
+= sizeof(WORD
) + offset
;
4002 if (!(hMenu
= CreateMenu())) return 0;
4003 if (!MENUEX_ParseResource( p
, hMenu
))
4005 DestroyMenu( hMenu
);
4010 ERR("version %d not supported.\n", version
);
4016 /**********************************************************************
4017 * LoadMenuIndirectW (USER32.@)
4019 HMENU WINAPI
LoadMenuIndirectW( LPCVOID
template )
4021 /* FIXME: is there anything different between A and W? */
4022 return LoadMenuIndirectA( template );
4026 /**********************************************************************
4029 BOOL WINAPI
IsMenu(HMENU hmenu
)
4031 LPPOPUPMENU menu
= MENU_GetMenu(hmenu
);
4032 return menu
!= NULL
;
4035 /**********************************************************************
4036 * GetMenuItemInfo_common
4039 static BOOL
GetMenuItemInfo_common ( HMENU hmenu
, UINT item
, BOOL bypos
,
4040 LPMENUITEMINFOW lpmii
, BOOL unicode
)
4042 MENUITEM
*menu
= MENU_FindItem (&hmenu
, &item
, bypos
? MF_BYPOSITION
: 0);
4044 debug_print_menuitem("GetMenuItemInfo_common: ", menu
, "");
4049 if (lpmii
->fMask
& MIIM_TYPE
) {
4050 lpmii
->fType
= menu
->fType
;
4051 switch (MENU_ITEM_TYPE(menu
->fType
)) {
4053 break; /* will be done below */
4056 lpmii
->dwTypeData
= menu
->text
;
4063 /* copy the text string */
4064 if ((lpmii
->fMask
& (MIIM_TYPE
|MIIM_STRING
)) &&
4065 (MENU_ITEM_TYPE(menu
->fType
) == MF_STRING
) && menu
->text
)
4070 len
= strlenW(menu
->text
);
4071 if(lpmii
->dwTypeData
&& lpmii
->cch
)
4072 lstrcpynW(lpmii
->dwTypeData
, menu
->text
, lpmii
->cch
);
4076 len
= WideCharToMultiByte( CP_ACP
, 0, menu
->text
, -1, NULL
, 0, NULL
, NULL
);
4077 if(lpmii
->dwTypeData
&& lpmii
->cch
)
4078 if (!WideCharToMultiByte( CP_ACP
, 0, menu
->text
, -1,
4079 (LPSTR
)lpmii
->dwTypeData
, lpmii
->cch
, NULL
, NULL
))
4080 ((LPSTR
)lpmii
->dwTypeData
)[lpmii
->cch
-1] = 0;
4082 /* if we've copied a substring we return its length */
4083 if(lpmii
->dwTypeData
&& lpmii
->cch
)
4085 if (lpmii
->cch
<= len
) lpmii
->cch
--;
4087 else /* return length of string */
4091 if (lpmii
->fMask
& MIIM_FTYPE
)
4092 lpmii
->fType
= menu
->fType
;
4094 if (lpmii
->fMask
& MIIM_BITMAP
)
4095 lpmii
->hbmpItem
= menu
->hbmpItem
;
4097 if (lpmii
->fMask
& MIIM_STATE
)
4098 lpmii
->fState
= menu
->fState
;
4100 if (lpmii
->fMask
& MIIM_ID
)
4101 lpmii
->wID
= menu
->wID
;
4103 if (lpmii
->fMask
& MIIM_SUBMENU
)
4104 lpmii
->hSubMenu
= menu
->hSubMenu
;
4106 if (lpmii
->fMask
& MIIM_CHECKMARKS
) {
4107 lpmii
->hbmpChecked
= menu
->hCheckBit
;
4108 lpmii
->hbmpUnchecked
= menu
->hUnCheckBit
;
4110 if (lpmii
->fMask
& MIIM_DATA
)
4111 lpmii
->dwItemData
= menu
->dwItemData
;
4116 /**********************************************************************
4117 * GetMenuItemInfoA (USER32.@)
4119 BOOL WINAPI
GetMenuItemInfoA( HMENU hmenu
, UINT item
, BOOL bypos
,
4120 LPMENUITEMINFOA lpmii
)
4122 return GetMenuItemInfo_common (hmenu
, item
, bypos
,
4123 (LPMENUITEMINFOW
)lpmii
, FALSE
);
4126 /**********************************************************************
4127 * GetMenuItemInfoW (USER32.@)
4129 BOOL WINAPI
GetMenuItemInfoW( HMENU hmenu
, UINT item
, BOOL bypos
,
4130 LPMENUITEMINFOW lpmii
)
4132 return GetMenuItemInfo_common (hmenu
, item
, bypos
,
4137 /* set a menu item text from a ASCII or Unicode string */
4138 inline static void set_menu_item_text( MENUITEM
*menu
, LPCWSTR text
, BOOL unicode
)
4143 menu
->fType
|= MF_SEPARATOR
;
4147 if ((menu
->text
= HeapAlloc( GetProcessHeap(), 0, (strlenW(text
)+1) * sizeof(WCHAR
) )))
4148 strcpyW( menu
->text
, text
);
4152 LPCSTR str
= (LPCSTR
)text
;
4153 int len
= MultiByteToWideChar( CP_ACP
, 0, str
, -1, NULL
, 0 );
4154 if ((menu
->text
= HeapAlloc( GetProcessHeap(), 0, len
* sizeof(WCHAR
) )))
4155 MultiByteToWideChar( CP_ACP
, 0, str
, -1, menu
->text
, len
);
4160 /**********************************************************************
4161 * SetMenuItemInfo_common
4164 static BOOL
SetMenuItemInfo_common(MENUITEM
* menu
,
4165 const MENUITEMINFOW
*lpmii
,
4168 if (!menu
) return FALSE
;
4170 debug_print_menuitem("MENU_SetItemInfo_common from: ", menu
, "");
4172 if (lpmii
->fMask
& MIIM_TYPE
) {
4173 /* Get rid of old string. */
4174 if (IS_STRING_ITEM(menu
->fType
) && menu
->text
) {
4175 HeapFree(GetProcessHeap(), 0, menu
->text
);
4179 /* make only MENU_ITEM_TYPE bits in menu->fType equal lpmii->fType */
4180 menu
->fType
&= ~MENU_ITEM_TYPE(menu
->fType
);
4181 menu
->fType
|= MENU_ITEM_TYPE(lpmii
->fType
);
4183 menu
->text
= lpmii
->dwTypeData
;
4185 if (IS_STRING_ITEM(menu
->fType
))
4186 set_menu_item_text( menu
, lpmii
->dwTypeData
, unicode
);
4189 if (lpmii
->fMask
& MIIM_FTYPE
) {
4190 /* free the string when the type is changing */
4191 if ( (!IS_STRING_ITEM(lpmii
->fType
)) && IS_STRING_ITEM(menu
->fType
) && menu
->text
) {
4192 HeapFree(GetProcessHeap(), 0, menu
->text
);
4195 menu
->fType
&= ~MENU_ITEM_TYPE(menu
->fType
);
4196 menu
->fType
|= MENU_ITEM_TYPE(lpmii
->fType
);
4197 if ( IS_STRING_ITEM(menu
->fType
) && !menu
->text
)
4198 menu
->fType
|= MF_SEPARATOR
;
4201 if (lpmii
->fMask
& MIIM_STRING
) {
4202 /* free the string when used */
4203 if (IS_STRING_ITEM(menu
->fType
) && menu
->text
) {
4204 HeapFree(GetProcessHeap(), 0, menu
->text
);
4205 set_menu_item_text( menu
, lpmii
->dwTypeData
, unicode
);
4209 if (lpmii
->fMask
& MIIM_STATE
)
4211 /* FIXME: MFS_DEFAULT do we have to reset the other menu items? */
4212 menu
->fState
= lpmii
->fState
;
4215 if (lpmii
->fMask
& MIIM_ID
)
4216 menu
->wID
= lpmii
->wID
;
4218 if (lpmii
->fMask
& MIIM_SUBMENU
) {
4219 menu
->hSubMenu
= lpmii
->hSubMenu
;
4220 if (menu
->hSubMenu
) {
4221 POPUPMENU
*subMenu
= MENU_GetMenu(menu
->hSubMenu
);
4223 subMenu
->wFlags
|= MF_POPUP
;
4224 menu
->fType
|= MF_POPUP
;
4227 /* FIXME: Return an error ? */
4228 menu
->fType
&= ~MF_POPUP
;
4231 menu
->fType
&= ~MF_POPUP
;
4234 if (lpmii
->fMask
& MIIM_CHECKMARKS
)
4236 if (lpmii
->fType
& MFT_RADIOCHECK
)
4237 menu
->fType
|= MFT_RADIOCHECK
;
4239 menu
->hCheckBit
= lpmii
->hbmpChecked
;
4240 menu
->hUnCheckBit
= lpmii
->hbmpUnchecked
;
4242 if (lpmii
->fMask
& MIIM_DATA
)
4243 menu
->dwItemData
= lpmii
->dwItemData
;
4245 debug_print_menuitem("SetMenuItemInfo_common to : ", menu
, "");
4249 /**********************************************************************
4250 * SetMenuItemInfoA (USER32.@)
4252 BOOL WINAPI
SetMenuItemInfoA(HMENU hmenu
, UINT item
, BOOL bypos
,
4253 const MENUITEMINFOA
*lpmii
)
4255 if ((lpmii
->fType
& (MF_HILITE
|MF_POPUP
)) || (lpmii
->fState
)) {
4256 /* QuickTime does pass invalid data into SetMenuItemInfo.
4257 * do some of the checks Windows does.
4259 WARN("Bad masks for type (0x%08x) or state (0x%08x)\n",
4260 lpmii
->fType
,lpmii
->fState
);
4264 return SetMenuItemInfo_common(MENU_FindItem(&hmenu
, &item
, bypos
? MF_BYPOSITION
: 0),
4265 (const MENUITEMINFOW
*)lpmii
, FALSE
);
4268 /**********************************************************************
4269 * SetMenuItemInfoW (USER32.@)
4271 BOOL WINAPI
SetMenuItemInfoW(HMENU hmenu
, UINT item
, BOOL bypos
,
4272 const MENUITEMINFOW
*lpmii
)
4274 return SetMenuItemInfo_common(MENU_FindItem(&hmenu
, &item
, bypos
? MF_BYPOSITION
: 0),
4278 /**********************************************************************
4279 * SetMenuDefaultItem (USER32.@)
4282 BOOL WINAPI
SetMenuDefaultItem(HMENU hmenu
, UINT uItem
, UINT bypos
)
4288 TRACE("(%p,%d,%d)\n", hmenu
, uItem
, bypos
);
4290 if (!(menu
= MENU_GetMenu(hmenu
))) return FALSE
;
4292 /* reset all default-item flags */
4294 for (i
= 0; i
< menu
->nItems
; i
++, item
++)
4296 item
->fState
&= ~MFS_DEFAULT
;
4299 /* no default item */
4308 if ( uItem
>= menu
->nItems
) return FALSE
;
4309 item
[uItem
].fState
|= MFS_DEFAULT
;
4314 for (i
= 0; i
< menu
->nItems
; i
++, item
++)
4316 if (item
->wID
== uItem
)
4318 item
->fState
|= MFS_DEFAULT
;
4327 /**********************************************************************
4328 * GetMenuDefaultItem (USER32.@)
4330 UINT WINAPI
GetMenuDefaultItem(HMENU hmenu
, UINT bypos
, UINT flags
)
4336 TRACE("(%p,%d,%d)\n", hmenu
, bypos
, flags
);
4338 if (!(menu
= MENU_GetMenu(hmenu
))) return -1;
4340 /* find default item */
4344 if (! item
) return -1;
4346 while ( !( item
->fState
& MFS_DEFAULT
) )
4349 if (i
>= menu
->nItems
) return -1;
4352 /* default: don't return disabled items */
4353 if ( (!(GMDI_USEDISABLED
& flags
)) && (item
->fState
& MFS_DISABLED
)) return -1;
4355 /* search rekursiv when needed */
4356 if ( (item
->fType
& MF_POPUP
) && (flags
& GMDI_GOINTOPOPUPS
) )
4359 ret
= GetMenuDefaultItem( item
->hSubMenu
, bypos
, flags
);
4360 if ( -1 != ret
) return ret
;
4362 /* when item not found in submenu, return the popup item */
4364 return ( bypos
) ? i
: item
->wID
;
4369 /**********************************************************************
4370 * InsertMenuItemA (USER32.@)
4372 BOOL WINAPI
InsertMenuItemA(HMENU hMenu
, UINT uItem
, BOOL bypos
,
4373 const MENUITEMINFOA
*lpmii
)
4375 MENUITEM
*item
= MENU_InsertItem(hMenu
, uItem
, bypos
? MF_BYPOSITION
: 0 );
4376 return SetMenuItemInfo_common(item
, (const MENUITEMINFOW
*)lpmii
, FALSE
);
4380 /**********************************************************************
4381 * InsertMenuItemW (USER32.@)
4383 BOOL WINAPI
InsertMenuItemW(HMENU hMenu
, UINT uItem
, BOOL bypos
,
4384 const MENUITEMINFOW
*lpmii
)
4386 MENUITEM
*item
= MENU_InsertItem(hMenu
, uItem
, bypos
? MF_BYPOSITION
: 0 );
4387 return SetMenuItemInfo_common(item
, lpmii
, TRUE
);
4390 /**********************************************************************
4391 * CheckMenuRadioItem (USER32.@)
4394 BOOL WINAPI
CheckMenuRadioItem(HMENU hMenu
,
4395 UINT first
, UINT last
, UINT check
,
4398 MENUITEM
*mifirst
, *milast
, *micheck
;
4399 HMENU mfirst
= hMenu
, mlast
= hMenu
, mcheck
= hMenu
;
4401 TRACE("%p: %d-%d, check %d, bypos=%d\n", hMenu
, first
, last
, check
, bypos
);
4403 mifirst
= MENU_FindItem (&mfirst
, &first
, bypos
);
4404 milast
= MENU_FindItem (&mlast
, &last
, bypos
);
4405 micheck
= MENU_FindItem (&mcheck
, &check
, bypos
);
4407 if (mifirst
== NULL
|| milast
== NULL
|| micheck
== NULL
||
4408 mifirst
> milast
|| mfirst
!= mlast
|| mfirst
!= mcheck
||
4409 micheck
> milast
|| micheck
< mifirst
)
4412 while (mifirst
<= milast
)
4414 if (mifirst
== micheck
)
4416 mifirst
->fType
|= MFT_RADIOCHECK
;
4417 mifirst
->fState
|= MFS_CHECKED
;
4419 mifirst
->fType
&= ~MFT_RADIOCHECK
;
4420 mifirst
->fState
&= ~MFS_CHECKED
;
4429 /**********************************************************************
4430 * GetMenuItemRect (USER32.@)
4432 * ATTENTION: Here, the returned values in rect are the screen
4433 * coordinates of the item just like if the menu was
4434 * always on the upper left side of the application.
4437 BOOL WINAPI
GetMenuItemRect (HWND hwnd
, HMENU hMenu
, UINT uItem
,
4440 POPUPMENU
*itemMenu
;
4444 TRACE("(%p,%p,%d,%p)\n", hwnd
, hMenu
, uItem
, rect
);
4446 item
= MENU_FindItem (&hMenu
, &uItem
, MF_BYPOSITION
);
4447 referenceHwnd
= hwnd
;
4451 itemMenu
= MENU_GetMenu(hMenu
);
4452 if (itemMenu
== NULL
)
4455 if(itemMenu
->hWnd
== 0)
4457 referenceHwnd
= itemMenu
->hWnd
;
4460 if ((rect
== NULL
) || (item
== NULL
))
4465 MapWindowPoints(referenceHwnd
, 0, (LPPOINT
)rect
, 2);
4471 /**********************************************************************
4472 * SetMenuInfo (USER32.@)
4475 * MIM_APPLYTOSUBMENUS
4476 * actually use the items to draw the menu
4478 BOOL WINAPI
SetMenuInfo (HMENU hMenu
, LPCMENUINFO lpmi
)
4482 TRACE("(%p %p)\n", hMenu
, lpmi
);
4484 if (lpmi
&& (lpmi
->cbSize
==sizeof(MENUINFO
)) && (menu
= MENU_GetMenu(hMenu
)))
4487 if (lpmi
->fMask
& MIM_BACKGROUND
)
4488 menu
->hbrBack
= lpmi
->hbrBack
;
4490 if (lpmi
->fMask
& MIM_HELPID
)
4491 menu
->dwContextHelpID
= lpmi
->dwContextHelpID
;
4493 if (lpmi
->fMask
& MIM_MAXHEIGHT
)
4494 menu
->cyMax
= lpmi
->cyMax
;
4496 if (lpmi
->fMask
& MIM_MENUDATA
)
4497 menu
->dwMenuData
= lpmi
->dwMenuData
;
4499 if (lpmi
->fMask
& MIM_STYLE
)
4500 menu
->dwStyle
= lpmi
->dwStyle
;
4507 /**********************************************************************
4508 * GetMenuInfo (USER32.@)
4514 BOOL WINAPI
GetMenuInfo (HMENU hMenu
, LPMENUINFO lpmi
)
4517 TRACE("(%p %p)\n", hMenu
, lpmi
);
4519 if (lpmi
&& (menu
= MENU_GetMenu(hMenu
)))
4522 if (lpmi
->fMask
& MIM_BACKGROUND
)
4523 lpmi
->hbrBack
= menu
->hbrBack
;
4525 if (lpmi
->fMask
& MIM_HELPID
)
4526 lpmi
->dwContextHelpID
= menu
->dwContextHelpID
;
4528 if (lpmi
->fMask
& MIM_MAXHEIGHT
)
4529 lpmi
->cyMax
= menu
->cyMax
;
4531 if (lpmi
->fMask
& MIM_MENUDATA
)
4532 lpmi
->dwMenuData
= menu
->dwMenuData
;
4534 if (lpmi
->fMask
& MIM_STYLE
)
4535 lpmi
->dwStyle
= menu
->dwStyle
;
4543 /**********************************************************************
4544 * SetMenuContextHelpId (USER32.@)
4546 BOOL WINAPI
SetMenuContextHelpId( HMENU hMenu
, DWORD dwContextHelpID
)
4550 TRACE("(%p 0x%08lx)\n", hMenu
, dwContextHelpID
);
4552 if ((menu
= MENU_GetMenu(hMenu
)))
4554 menu
->dwContextHelpID
= dwContextHelpID
;
4561 /**********************************************************************
4562 * GetMenuContextHelpId (USER32.@)
4564 DWORD WINAPI
GetMenuContextHelpId( HMENU hMenu
)
4568 TRACE("(%p)\n", hMenu
);
4570 if ((menu
= MENU_GetMenu(hMenu
)))
4572 return menu
->dwContextHelpID
;
4577 /**********************************************************************
4578 * MenuItemFromPoint (USER32.@)
4580 UINT WINAPI
MenuItemFromPoint(HWND hWnd
, HMENU hMenu
, POINT ptScreen
)
4582 POPUPMENU
*menu
= MENU_GetMenu(hMenu
);
4586 /*FIXME: Do we have to handle hWnd here? */
4587 item
= MENU_FindItemByCoords(menu
, ptScreen
, &pos
);
4593 /**********************************************************************
4594 * translate_accelerator
4596 static BOOL
translate_accelerator( HWND hWnd
, UINT message
, WPARAM wParam
, LPARAM lParam
,
4597 BYTE fVirt
, WORD key
, WORD cmd
)
4601 if (wParam
!= key
) return FALSE
;
4603 if (message
== WM_CHAR
)
4605 if ( !(fVirt
& FALT
) && !(fVirt
& FVIRTKEY
) )
4607 TRACE_(accel
)("found accel for WM_CHAR: ('%c')\n", wParam
& 0xff);
4613 if(fVirt
& FVIRTKEY
)
4616 TRACE_(accel
)("found accel for virt_key %04x (scan %04x)\n",
4617 wParam
, 0xff & HIWORD(lParam
));
4618 if(GetKeyState(VK_SHIFT
) & 0x8000) mask
|= FSHIFT
;
4619 if(GetKeyState(VK_CONTROL
) & 0x8000) mask
|= FCONTROL
;
4620 if(GetKeyState(VK_MENU
) & 0x8000) mask
|= FALT
;
4621 if(mask
== (fVirt
& (FSHIFT
| FCONTROL
| FALT
))) goto found
;
4622 TRACE_(accel
)(", but incorrect SHIFT/CTRL/ALT-state\n");
4626 if (!(lParam
& 0x01000000)) /* no special_key */
4628 if ((fVirt
& FALT
) && (lParam
& 0x20000000))
4629 { /* ^^ ALT pressed */
4630 TRACE_(accel
)("found accel for Alt-%c\n", wParam
& 0xff);
4639 if (message
== WM_KEYUP
|| message
== WM_SYSKEYUP
)
4641 else if (GetCapture())
4643 else if (!IsWindowEnabled(hWnd
))
4647 HMENU hMenu
, hSubMenu
, hSysMenu
;
4648 UINT uSysStat
= (UINT
)-1, uStat
= (UINT
)-1, nPos
;
4650 hMenu
= (GetWindowLongA( hWnd
, GWL_STYLE
) & WS_CHILD
) ? 0 : GetMenu(hWnd
);
4651 hSysMenu
= get_win_sys_menu( hWnd
);
4653 /* find menu item and ask application to initialize it */
4654 /* 1. in the system menu */
4655 hSubMenu
= hSysMenu
;
4657 if(MENU_FindItem(&hSubMenu
, &nPos
, MF_BYCOMMAND
))
4659 SendMessageA(hWnd
, WM_INITMENU
, (WPARAM
)hSysMenu
, 0L);
4660 if(hSubMenu
!= hSysMenu
)
4662 nPos
= MENU_FindSubMenu(&hSysMenu
, hSubMenu
);
4663 TRACE_(accel
)("hSysMenu = %p, hSubMenu = %p, nPos = %d\n", hSysMenu
, hSubMenu
, nPos
);
4664 SendMessageA(hWnd
, WM_INITMENUPOPUP
, (WPARAM
)hSubMenu
, MAKELPARAM(nPos
, TRUE
));
4666 uSysStat
= GetMenuState(GetSubMenu(hSysMenu
, 0), cmd
, MF_BYCOMMAND
);
4668 else /* 2. in the window's menu */
4672 if(MENU_FindItem(&hSubMenu
, &nPos
, MF_BYCOMMAND
))
4674 SendMessageA(hWnd
, WM_INITMENU
, (WPARAM
)hMenu
, 0L);
4675 if(hSubMenu
!= hMenu
)
4677 nPos
= MENU_FindSubMenu(&hMenu
, hSubMenu
);
4678 TRACE_(accel
)("hMenu = %p, hSubMenu = %p, nPos = %d\n", hMenu
, hSubMenu
, nPos
);
4679 SendMessageA(hWnd
, WM_INITMENUPOPUP
, (WPARAM
)hSubMenu
, MAKELPARAM(nPos
, FALSE
));
4681 uStat
= GetMenuState(hMenu
, cmd
, MF_BYCOMMAND
);
4685 if (uSysStat
!= (UINT
)-1)
4687 if (uSysStat
& (MF_DISABLED
|MF_GRAYED
))
4694 if (uStat
!= (UINT
)-1)
4700 if (uStat
& (MF_DISABLED
|MF_GRAYED
))
4711 if( mesg
==WM_COMMAND
)
4713 TRACE_(accel
)(", sending WM_COMMAND, wParam=%0x\n", 0x10000 | cmd
);
4714 SendMessageA(hWnd
, mesg
, 0x10000 | cmd
, 0L);
4716 else if( mesg
==WM_SYSCOMMAND
)
4718 TRACE_(accel
)(", sending WM_SYSCOMMAND, wParam=%0x\n", cmd
);
4719 SendMessageA(hWnd
, mesg
, cmd
, 0x00010000L
);
4723 /* some reasons for NOT sending the WM_{SYS}COMMAND message:
4724 * #0: unknown (please report!)
4725 * #1: for WM_KEYUP,WM_SYSKEYUP
4726 * #2: mouse is captured
4727 * #3: window is disabled
4728 * #4: it's a disabled system menu option
4729 * #5: it's a menu option, but window is iconic
4730 * #6: it's a menu option, but disabled
4732 TRACE_(accel
)(", but won't send WM_{SYS}COMMAND, reason is #%d\n",mesg
);
4734 ERR_(accel
)(" unknown reason - please report!");
4739 /**********************************************************************
4740 * TranslateAccelerator (USER32.@)
4741 * TranslateAcceleratorA (USER32.@)
4742 * TranslateAcceleratorW (USER32.@)
4744 INT WINAPI
TranslateAccelerator( HWND hWnd
, HACCEL hAccel
, LPMSG msg
)
4747 LPACCEL16 lpAccelTbl
;
4752 WARN_(accel
)("msg null; should hang here to be win compatible\n");
4755 if (!hAccel
|| !(lpAccelTbl
= (LPACCEL16
) LockResource16(HACCEL_16(hAccel
))))
4757 WARN_(accel
)("invalid accel handle=%p\n", hAccel
);
4760 if ((msg
->message
!= WM_KEYDOWN
&&
4761 msg
->message
!= WM_KEYUP
&&
4762 msg
->message
!= WM_SYSKEYDOWN
&&
4763 msg
->message
!= WM_SYSKEYUP
&&
4764 msg
->message
!= WM_CHAR
)) return 0;
4766 TRACE_(accel
)("TranslateAccelerators hAccel=%p, hWnd=%p,"
4767 "msg->hwnd=%p, msg->message=%04x, wParam=%08x, lParam=%lx\n",
4768 hAccel
,hWnd
,msg
->hwnd
,msg
->message
,msg
->wParam
,msg
->lParam
);
4773 if (translate_accelerator( hWnd
, msg
->message
, msg
->wParam
, msg
->lParam
,
4774 lpAccelTbl
[i
].fVirt
, lpAccelTbl
[i
].key
, lpAccelTbl
[i
].cmd
))
4776 } while ((lpAccelTbl
[i
++].fVirt
& 0x80) == 0);
4777 WARN_(accel
)("couldn't translate accelerator key\n");