Sync up to trunk head before merging.
[reactos.git] / win32ss / user / user32 / windows / menu.c
1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS user32.dll
4 * FILE: user32/windows/menu.c
5 * PURPOSE: Menus
6 *
7 * PROGRAMMERS: Casper S. Hornstrup
8 * James Tabor
9 */
10
11 /* INCLUDES ******************************************************************/
12
13 #include <user32.h>
14 #include <wine/debug.h>
15
16 LRESULT DefWndNCPaint(HWND hWnd, HRGN hRgn, BOOL Active);
17 BOOL WINAPI GdiValidateHandle(HGDIOBJ hobj);
18
19 WINE_DEFAULT_DEBUG_CHANNEL(menu);
20
21 /* internal popup menu window messages */
22
23 #define MM_SETMENUHANDLE (WM_USER + 0)
24 #define MM_GETMENUHANDLE (WM_USER + 1)
25
26 /* internal flags for menu tracking */
27
28 #define TF_ENDMENU 0x10000
29 #define TF_SUSPENDPOPUP 0x20000
30 #define TF_SKIPREMOVE 0x40000
31
32 #define ITEM_PREV -1
33 #define ITEM_NEXT 1
34
35 /* Internal MenuTrackMenu() flags */
36 #define TPM_INTERNAL 0xF0000000
37 #define TPM_BUTTONDOWN 0x40000000 /* menu was clicked before tracking */
38 #define TPM_POPUPMENU 0x20000000 /* menu is a popup menu */
39
40 /* top and bottom margins for popup menus */
41 #define MENU_TOP_MARGIN 3
42 #define MENU_BOTTOM_MARGIN 2
43
44 #define MENU_TYPE_MASK (MF_STRING | MF_BITMAP | MF_OWNERDRAW | MF_SEPARATOR)
45
46 #define MENU_ITEM_TYPE(flags) ((flags) & MENU_TYPE_MASK)
47
48 #define MNS_STYLE_MASK (MNS_NOCHECK|MNS_MODELESS|MNS_DRAGDROP|MNS_AUTODISMISS|MNS_NOTIFYBYPOS|MNS_CHECKORBMP)
49
50 #define MENUITEMINFO_TYPE_MASK \
51 (MFT_STRING | MFT_BITMAP | MFT_OWNERDRAW | MFT_SEPARATOR | \
52 MFT_MENUBARBREAK | MFT_MENUBREAK | MFT_RADIOCHECK | \
53 MFT_RIGHTORDER | MFT_RIGHTJUSTIFY /* same as MF_HELP */ )
54
55 #define TYPE_MASK (MENUITEMINFO_TYPE_MASK | MF_POPUP | MF_SYSMENU)
56
57 #define STATE_MASK (~TYPE_MASK)
58
59 #define MENUITEMINFO_STATE_MASK (STATE_MASK & ~(MF_BYPOSITION | MF_MOUSESELECT))
60
61 #define MII_STATE_MASK (MFS_GRAYED|MFS_CHECKED|MFS_HILITE|MFS_DEFAULT)
62
63 /* macro to test that flags do not indicate bitmap, ownerdraw or separator */
64 #define IS_STRING_ITEM(flags) (MF_STRING == MENU_ITEM_TYPE(flags))
65 #define IS_MAGIC_BITMAP(id) ((id) && ((INT_PTR)(id) < 12) && ((INT_PTR)(id) >= -1))
66
67 #define IS_SYSTEM_MENU(MenuInfo) \
68 (0 == ((MenuInfo)->fFlags & MNF_POPUP) && 0 != ((MenuInfo)->fFlags & MNF_SYSDESKMN))
69
70 #define IS_SYSTEM_POPUP(MenuInfo) \
71 (0 != ((MenuInfo)->fFlags & MNF_POPUP) && 0 != ((MenuInfo)->fFlags & MNF_SYSDESKMN))
72
73 #define IS_BITMAP_ITEM(flags) (MF_BITMAP == MENU_ITEM_TYPE(flags))
74
75 /* Use global popup window because there's no way 2 menus can
76 * be tracked at the same time. */
77 static HWND top_popup;
78 static HMENU top_popup_hmenu;
79
80 /* Flag set by EndMenu() to force an exit from menu tracking */
81 static BOOL fEndMenu = FALSE;
82
83 #define MENU_ITEM_HBMP_SPACE (5)
84 #define MENU_BAR_ITEMS_SPACE (12)
85 #define SEPARATOR_HEIGHT (5)
86 #define MENU_TAB_SPACE (8)
87
88 typedef struct
89 {
90 UINT TrackFlags;
91 HMENU CurrentMenu; /* current submenu (can be equal to hTopMenu)*/
92 HMENU TopMenu; /* initial menu */
93 HWND OwnerWnd; /* where notifications are sent */
94 POINT Pt;
95 } MTRACKER;
96
97
98 /*********************************************************************
99 * PopupMenu class descriptor
100 */
101 const struct builtin_class_descr POPUPMENU_builtin_class =
102 {
103 WC_MENU, /* name */
104 CS_SAVEBITS | CS_DBLCLKS, /* style */
105 (WNDPROC) NULL, /* FIXME - procA */
106 (WNDPROC) PopupMenuWndProcW, /* FIXME - procW */
107 sizeof(MENUINFO *), /* extra */
108 (LPCWSTR) IDC_ARROW, /* cursor */
109 (HBRUSH)(COLOR_MENU + 1) /* brush */
110 };
111
112 #ifndef GET_WORD
113 #define GET_WORD(ptr) (*(WORD *)(ptr))
114 #endif
115 #ifndef GET_DWORD
116 #define GET_DWORD(ptr) (*(DWORD *)(ptr))
117 #endif
118
119 HFONT hMenuFont = NULL;
120 HFONT hMenuFontBold = NULL;
121
122 /* Dimension of the menu bitmaps */
123 static HBITMAP BmpSysMenu = NULL;
124
125 static SIZE MenuCharSize;
126
127
128 /***********************************************************************
129 * MENU_GetMenu
130 *
131 * Validate the given menu handle and returns the menu structure pointer.
132 */
133 FORCEINLINE PMENU MENU_GetMenu(HMENU hMenu)
134 {
135 return ValidateHandle(hMenu, TYPE_MENU);
136 }
137
138 /***********************************************************************
139 * MENU_FindItem
140 *
141 * Find a menu item. Return a pointer on the item, and modifies *hmenu
142 * in case the item was in a sub-menu.
143 */
144 ITEM *MENU_FindItem( HMENU *hmenu, UINT *nPos, UINT wFlags )
145 {
146 MENU *menu;
147 ITEM *fallback = NULL;
148 UINT fallback_pos = 0;
149 UINT i;
150 PITEM pItem;
151
152 if ((*hmenu == (HMENU)0xffff) || (!(menu = MENU_GetMenu(*hmenu)))) return NULL;
153 if (wFlags & MF_BYPOSITION)
154 {
155 if (*nPos >= menu->cItems) return NULL;
156 pItem = menu->rgItems ? DesktopPtrToUser(menu->rgItems) : NULL;
157 //pItem = &menu->rgItems[*nPos];
158 i = 0;
159 while(pItem) // Do this for now.
160 {
161 if (i < (INT)menu->cItems)
162 {
163 if ( *nPos == i ) return pItem;
164 }
165 pItem = pItem->Next ? DesktopPtrToUser(pItem->Next) : NULL;
166 i++;
167 }
168 }
169 else
170 {
171 PITEM item = menu->rgItems ? DesktopPtrToUser(menu->rgItems) : NULL;
172 for (i = 0; item ,i < menu->cItems; i++, item = item->Next ? DesktopPtrToUser(item->Next) : NULL)//, item++)
173 {
174 if (item->spSubMenu)
175 {
176 PMENU pSubMenu = DesktopPtrToUser(item->spSubMenu);
177 HMENU hsubmenu = UserHMGetHandle(pSubMenu);
178 ITEM *subitem = MENU_FindItem( &hsubmenu, nPos, wFlags );
179 if (subitem)
180 {
181 *hmenu = hsubmenu;
182 return subitem;
183 }
184 else if (item->wID == *nPos)
185 {
186 /* fallback to this item if nothing else found */
187 fallback_pos = i;
188 fallback = item;
189 }
190 }
191 else if (item->wID == *nPos)
192 {
193 *nPos = i;
194 return item;
195 }
196 }
197 }
198
199 if (fallback)
200 *nPos = fallback_pos;
201
202 return fallback;
203 }
204
205 #define MAX_GOINTOSUBMENU (0x10)
206 UINT FASTCALL
207 IntGetMenuDefaultItem(PMENU Menu, BOOL fByPos, UINT gmdiFlags, DWORD *gismc)
208 {
209 UINT x = 0;
210 UINT res = -1;
211 UINT sres;
212 PITEM Item = Menu->rgItems ? DesktopPtrToUser(Menu->rgItems) : NULL;
213
214 while(Item)
215 {
216 if (Item->fState & MFS_DEFAULT)
217 {
218 if (!(gmdiFlags & GMDI_USEDISABLED) &&
219 (Item->fState & MFS_DISABLED) )
220 break;
221
222 res = fByPos ? x : Item->wID;
223
224 if ((*gismc < MAX_GOINTOSUBMENU) &&
225 (gmdiFlags & GMDI_GOINTOPOPUPS) &&
226 Item->spSubMenu)
227 {
228 if (DesktopPtrToUser(Item->spSubMenu) == Menu)
229 break;
230
231 (*gismc)++;
232 sres = IntGetMenuDefaultItem( DesktopPtrToUser(Item->spSubMenu), fByPos, gmdiFlags, gismc);
233 (*gismc)--;
234
235 if(sres > (UINT)-1)
236 res = sres;
237 }
238 break;
239 }
240 Item = Item->Next ? DesktopPtrToUser(Item->Next) : NULL;
241 x++;
242 }
243 return res;
244 }
245
246 static BOOL GetMenuItemInfo_common ( HMENU hmenu,
247 UINT item,
248 BOOL bypos,
249 LPMENUITEMINFOW lpmii,
250 BOOL unicode)
251 {
252 ITEM *pItem = MENU_FindItem (&hmenu, &item, bypos ? MF_BYPOSITION : 0);
253
254 //debug_print_menuitem("GetMenuItemInfo_common: ", pItem, "");
255
256 if (!pItem)
257 {
258 SetLastError( ERROR_MENU_ITEM_NOT_FOUND);
259 //SetLastError(ERROR_INVALID_PARAMETER);
260 return FALSE;
261 }
262
263 if( lpmii->fMask & MIIM_TYPE)
264 {
265 if( lpmii->fMask & ( MIIM_STRING | MIIM_FTYPE | MIIM_BITMAP))
266 {
267 ERR("invalid combination of fMask bits used\n");
268 /* this does not happen on Win9x/ME */
269 SetLastError( ERROR_INVALID_PARAMETER);
270 return FALSE;
271 }
272 lpmii->fType = pItem->fType & MENUITEMINFO_TYPE_MASK;
273 if( pItem->hbmp) lpmii->fType |= MFT_BITMAP;
274 lpmii->hbmpItem = pItem->hbmp; /* not on Win9x/ME */
275 if( lpmii->fType & MFT_BITMAP)
276 {
277 lpmii->dwTypeData = (LPWSTR) pItem->hbmp;
278 lpmii->cch = 0;
279 }
280 else if( lpmii->fType & (MFT_OWNERDRAW | MFT_SEPARATOR))
281 {
282 /* this does not happen on Win9x/ME */
283 lpmii->dwTypeData = 0;
284 lpmii->cch = 0;
285 }
286 }
287
288 /* copy the text string */
289 if ((lpmii->fMask & (MIIM_TYPE|MIIM_STRING)))
290 {
291 if( !pItem->Xlpstr )
292 { // Very strange this fixes a wine test with a crash.
293 if(lpmii->dwTypeData && lpmii->cch && !(GdiValidateHandle((HGDIOBJ)lpmii->dwTypeData)) )
294 {
295 lpmii->cch = 0;
296 if( unicode)
297 *((WCHAR *)lpmii->dwTypeData) = 0;
298 else
299 *((CHAR *)lpmii->dwTypeData) = 0;
300 }
301 }
302 else
303 {
304 int len;
305 LPWSTR text = DesktopPtrToUser(pItem->Xlpstr);
306 if (unicode)
307 {
308 len = strlenW(text);
309 if(lpmii->dwTypeData && lpmii->cch)
310 lstrcpynW(lpmii->dwTypeData, text, lpmii->cch);
311 }
312 else
313 {
314 len = WideCharToMultiByte( CP_ACP, 0, text, -1, NULL, 0, NULL, NULL ) - 1;
315 if(lpmii->dwTypeData && lpmii->cch)
316 if (!WideCharToMultiByte( CP_ACP, 0, text, -1,
317 (LPSTR)lpmii->dwTypeData, lpmii->cch, NULL, NULL ))
318 ((LPSTR)lpmii->dwTypeData)[lpmii->cch - 1] = 0;
319 }
320 /* if we've copied a substring we return its length */
321 if(lpmii->dwTypeData && lpmii->cch)
322 if (lpmii->cch <= len + 1)
323 lpmii->cch--;
324 else
325 lpmii->cch = len;
326 else
327 {
328 /* return length of string */
329 /* not on Win9x/ME if fType & MFT_BITMAP */
330 lpmii->cch = len;
331 }
332 }
333 }
334
335 if (lpmii->fMask & MIIM_FTYPE)
336 lpmii->fType = pItem->fType & MENUITEMINFO_TYPE_MASK;
337
338 if (lpmii->fMask & MIIM_BITMAP)
339 lpmii->hbmpItem = pItem->hbmp;
340
341 if (lpmii->fMask & MIIM_STATE)
342 lpmii->fState = pItem->fState & MII_STATE_MASK; //MENUITEMINFO_STATE_MASK;
343
344 if (lpmii->fMask & MIIM_ID)
345 lpmii->wID = pItem->wID;
346
347 if (lpmii->fMask & MIIM_SUBMENU && pItem->spSubMenu )
348 {
349 PMENU pSubMenu = DesktopPtrToUser(pItem->spSubMenu);
350 HMENU hSubMenu = UserHMGetHandle(pSubMenu);
351 lpmii->hSubMenu = hSubMenu;
352 }
353 else
354 {
355 /* hSubMenu is always cleared
356 * (not on Win9x/ME ) */
357 lpmii->hSubMenu = 0;
358 }
359
360 if (lpmii->fMask & MIIM_CHECKMARKS)
361 {
362 lpmii->hbmpChecked = pItem->hbmpChecked;
363 lpmii->hbmpUnchecked = pItem->hbmpUnchecked;
364 }
365 if (lpmii->fMask & MIIM_DATA)
366 lpmii->dwItemData = pItem->dwItemData;
367
368 return TRUE;
369 }
370
371 /***********************************************************************
372 * MenuGetRosMenuInfo
373 *
374 * Get full information about menu
375 */
376 static BOOL FASTCALL
377 MenuGetRosMenuInfo(PROSMENUINFO MenuInfo, HMENU Menu)
378 {
379 PMENU pMenu;
380 if (!(pMenu = ValidateHandle(Menu, TYPE_MENU))) return FALSE;
381
382 MenuInfo->hbrBack = pMenu->hbrBack;
383 MenuInfo->dwContextHelpID = pMenu->dwContextHelpId;
384 MenuInfo->cyMax = pMenu->cyMax;
385 MenuInfo->dwMenuData = pMenu->dwMenuData;
386 MenuInfo->dwStyle = pMenu->fFlags & MNS_STYLE_MASK;
387
388 MenuInfo->cItems = pMenu->cItems;
389
390 MenuInfo->iItem = pMenu->iItem;
391 MenuInfo->cxMenu = pMenu->cxMenu;
392 MenuInfo->cyMenu = pMenu->cyMenu;
393 MenuInfo->spwndNotify = pMenu->spwndNotify;
394 MenuInfo->cxTextAlign = pMenu->cxTextAlign;
395 MenuInfo->iTop = pMenu->iMaxTop;
396 MenuInfo->iMaxTop = pMenu->iMaxTop;
397 MenuInfo->dwArrowsOn = pMenu->dwArrowsOn;
398
399 MenuInfo->fFlags = pMenu->fFlags;
400 MenuInfo->Self = pMenu->head.h;
401 MenuInfo->TimeToHide = pMenu->TimeToHide;
402 MenuInfo->Wnd = pMenu->hWnd;
403 return TRUE;
404 }
405
406 /***********************************************************************
407 * MenuSetRosMenuInfo
408 *
409 * Set full information about menu
410 */
411 static BOOL FASTCALL
412 MenuSetRosMenuInfo(PROSMENUINFO MenuInfo)
413 {
414 MenuInfo->cbSize = sizeof(ROSMENUINFO);
415 MenuInfo->fMask = MIM_BACKGROUND | MIM_HELPID | MIM_MAXHEIGHT | MIM_MENUDATA | MIM_STYLE;
416
417 return NtUserThunkedMenuInfo(MenuInfo->Self, (LPCMENUINFO)MenuInfo);
418 }
419
420 /***********************************************************************
421 * MenuInitRosMenuItemInfo
422 *
423 * Initialize a buffer for use with MenuGet/SetRosMenuItemInfo
424 */
425 static VOID FASTCALL
426 MenuInitRosMenuItemInfo(PROSMENUITEMINFO ItemInfo)
427 {
428 ZeroMemory(ItemInfo, sizeof(ROSMENUITEMINFO));
429 ItemInfo->cbSize = sizeof(ROSMENUITEMINFO);
430 }
431
432 /***********************************************************************
433 * MenuGetRosMenuItemInfo
434 *
435 * Get full information about a menu item
436 */
437 static BOOL FASTCALL
438 MenuGetRosMenuItemInfo(HMENU Menu, UINT Index, PROSMENUITEMINFO ItemInfo)
439 {
440 PITEM pItem;
441 UINT Save_Mask = ItemInfo->fMask; /* Save the org mask bits. */
442
443 if (ItemInfo->dwTypeData != NULL)
444 {
445 HeapFree(GetProcessHeap(), 0, ItemInfo->dwTypeData);
446 }
447
448 ItemInfo->dwTypeData = NULL;
449
450 if (!(pItem = MENU_FindItem(&Menu, &Index, MF_BYPOSITION)))
451 {
452 ItemInfo->fType = 0;
453 return FALSE;
454 }
455
456 ItemInfo->fType = pItem->fType;
457 ItemInfo->hbmpItem = pItem->hbmp;
458 ItemInfo->hbmpChecked = pItem->hbmpChecked;
459 ItemInfo->hbmpUnchecked = pItem->hbmpUnchecked;
460 ItemInfo->dwItemData = pItem->dwItemData;
461 ItemInfo->wID = pItem->wID;
462 ItemInfo->fState = pItem->fState;
463
464 if (pItem->spSubMenu)
465 {
466 PMENU pSubMenu = DesktopPtrToUser(pItem->spSubMenu);
467 HMENU hSubMenu = UserHMGetHandle(pSubMenu);
468 ItemInfo->hSubMenu = hSubMenu;
469 }
470 else
471 ItemInfo->hSubMenu = NULL;
472
473 if (MENU_ITEM_TYPE(ItemInfo->fType) == MF_STRING)
474 {
475 LPWSTR lpstr = pItem->lpstr.Buffer ? DesktopPtrToUser(pItem->lpstr.Buffer) : NULL;
476 if (lpstr)
477 {
478 ItemInfo->cch = pItem->lpstr.Length / sizeof(WCHAR);
479 ItemInfo->cch++;
480 ItemInfo->dwTypeData = HeapAlloc(GetProcessHeap(), 0, ItemInfo->cch * sizeof(WCHAR));
481 if (ItemInfo->dwTypeData == NULL)
482 {
483 return FALSE;
484 }
485 RtlCopyMemory(ItemInfo->dwTypeData, lpstr, min(ItemInfo->cch * sizeof(WCHAR), pItem->lpstr.MaximumLength));
486 }
487 else
488 {
489 ItemInfo->cch = 0;
490 }
491 }
492
493 ItemInfo->Rect.left = pItem->xItem;
494 ItemInfo->Rect.top = pItem->yItem;
495 ItemInfo->Rect.right = pItem->cxItem; // Do this for now......
496 ItemInfo->Rect.bottom = pItem->cyItem;
497 ItemInfo->dxTab = pItem->dxTab;
498 ItemInfo->lpstr = pItem->lpstr.Buffer;
499 ItemInfo->maxBmpSize.cx = pItem->cxBmp;
500 ItemInfo->maxBmpSize.cy = pItem->cyBmp;
501
502 ItemInfo->fMask = Save_Mask;
503 return TRUE;
504 }
505
506 /***********************************************************************
507 * MenuSetRosMenuItemInfo
508 *
509 * Set selected information about a menu item, need to set the mask bits.
510 */
511 static BOOL FASTCALL
512 MenuSetRosMenuItemInfo(HMENU Menu, UINT Index, PROSMENUITEMINFO ItemInfo)
513 {
514 BOOL Ret;
515
516 if (MENU_ITEM_TYPE(ItemInfo->fType) == MF_STRING &&
517 ItemInfo->dwTypeData != NULL)
518 {
519 ItemInfo->cch = strlenW(ItemInfo->dwTypeData);
520 }
521 Ret = NtUserThunkedMenuItemInfo(Menu, Index, TRUE, FALSE, (LPMENUITEMINFOW)ItemInfo, NULL);
522 return Ret;
523 }
524
525 /***********************************************************************
526 * MenuCleanupRosMenuItemInfo
527 *
528 * Cleanup after use of MenuGet/SetRosMenuItemInfo
529 */
530 static VOID FASTCALL
531 MenuCleanupRosMenuItemInfo(PROSMENUITEMINFO ItemInfo)
532 {
533 if (ItemInfo->dwTypeData != NULL)
534 {
535 HeapFree(GetProcessHeap(), 0, ItemInfo->dwTypeData);
536 ItemInfo->dwTypeData = NULL;
537 }
538 }
539
540 DWORD FASTCALL
541 IntBuildMenuItemList(PMENU MenuObject, PVOID Buffer, ULONG nMax)
542 {
543 DWORD res = 0;
544 ROSMENUITEMINFO mii;
545 PVOID Buf;
546 PITEM CurItem = MenuObject->rgItems ? DesktopPtrToUser(MenuObject->rgItems) : NULL;
547 PWCHAR StrOut;
548 WCHAR NulByte;
549
550 if (0 != nMax)
551 {
552 if (nMax < MenuObject->cItems * sizeof(ROSMENUITEMINFO))
553 {
554 return 0;
555 }
556 StrOut = (PWCHAR)((char *) Buffer + MenuObject->cItems * sizeof(ROSMENUITEMINFO));
557 nMax -= MenuObject->cItems * sizeof(ROSMENUITEMINFO);
558 Buf = Buffer;
559 mii.cbSize = sizeof(ROSMENUITEMINFO);
560 mii.fMask = 0;
561 NulByte = L'\0';
562
563 while (NULL != CurItem)
564 {
565 mii.cch = CurItem->lpstr.Length / sizeof(WCHAR);
566 mii.dwItemData = CurItem->dwItemData;
567 if (0 != CurItem->lpstr.Length)
568 {
569 mii.dwTypeData = StrOut;
570 }
571 else
572 {
573 mii.dwTypeData = NULL;
574 }
575 mii.fState = CurItem->fState;
576 mii.fType = CurItem->fType;
577 mii.wID = CurItem->wID;
578 mii.hbmpChecked = CurItem->hbmpChecked;
579 mii.hbmpItem = CurItem->hbmp;
580 mii.hbmpUnchecked = CurItem->hbmpUnchecked;
581 if (CurItem->spSubMenu)
582 {
583 PMENU pSubMenu = DesktopPtrToUser(CurItem->spSubMenu);
584 HMENU hSubMenu = UserHMGetHandle(pSubMenu);
585 mii.hSubMenu = hSubMenu;
586 }
587 else
588 mii.hSubMenu = NULL;
589 mii.Rect.left = CurItem->xItem;
590 mii.Rect.top = CurItem->yItem;
591 mii.Rect.right = CurItem->cxItem; // Do this for now......
592 mii.Rect.bottom = CurItem->cyItem;
593 mii.dxTab = CurItem->dxTab;
594 mii.lpstr = CurItem->lpstr.Buffer; // Can be read from user side!
595 //mii.maxBmpSize.cx = CurItem->cxBmp;
596 //mii.maxBmpSize.cy = CurItem->cyBmp;
597
598 RtlCopyMemory(Buf, &mii, sizeof(ROSMENUITEMINFO));
599 Buf = (PVOID)((ULONG_PTR)Buf + sizeof(ROSMENUITEMINFO));
600
601 if (0 != CurItem->lpstr.Length && (nMax >= CurItem->lpstr.Length + sizeof(WCHAR)))
602 {
603 LPWSTR lpstr = CurItem->lpstr.Buffer ? DesktopPtrToUser(CurItem->lpstr.Buffer) : NULL;
604 if (lpstr)
605 {
606 /* Copy string */
607 RtlCopyMemory(StrOut, lpstr, CurItem->lpstr.Length);
608
609 StrOut += CurItem->lpstr.Length / sizeof(WCHAR);
610 RtlCopyMemory(StrOut, &NulByte, sizeof(WCHAR));
611 StrOut++;
612 nMax -= CurItem->lpstr.Length + sizeof(WCHAR);
613 }
614 }
615 else if (0 != CurItem->lpstr.Length)
616 {
617 break;
618 }
619
620 CurItem = CurItem->Next ? DesktopPtrToUser(CurItem->Next) : NULL;
621 res++;
622 }
623 }
624 else
625 {
626 while (NULL != CurItem)
627 {
628 res += sizeof(ROSMENUITEMINFO) + CurItem->lpstr.Length + sizeof(WCHAR);
629 CurItem = CurItem->Next ? DesktopPtrToUser(CurItem->Next) : NULL;
630 }
631 }
632 return res;
633 }
634
635 /***********************************************************************
636 * MenuGetAllRosMenuItemInfo
637 *
638 * Get full information about all menu items
639 */
640 static INT FASTCALL
641 MenuGetAllRosMenuItemInfo(HMENU Menu, PROSMENUITEMINFO *ItemInfo)
642 {
643 DWORD BufSize;
644 PMENU pMenu;
645
646 if (!(pMenu = ValidateHandle(Menu, TYPE_MENU))) return -1;
647
648 BufSize = IntBuildMenuItemList(pMenu, (PVOID)1, 0);
649 if (BufSize == (DWORD) -1 || BufSize == 0)
650 {
651 return -1;
652 }
653 *ItemInfo = HeapAlloc(GetProcessHeap(), 0, BufSize);
654 if (NULL == *ItemInfo)
655 {
656 return -1;
657 }
658
659 return IntBuildMenuItemList(pMenu, (PVOID)*ItemInfo, BufSize);
660 }
661
662 /***********************************************************************
663 * MenuCleanupAllRosMenuItemInfo
664 *
665 * Cleanup after use of MenuGetAllRosMenuItemInfo
666 */
667 static VOID FASTCALL
668 MenuCleanupAllRosMenuItemInfo(PROSMENUITEMINFO ItemInfo)
669 {
670 HeapFree(GetProcessHeap(), 0, ItemInfo);
671 }
672
673 /***********************************************************************
674 * MenuInitSysMenuPopup
675 *
676 * Grey the appropriate items in System menu.
677 */
678 void FASTCALL MenuInitSysMenuPopup(HMENU hmenu, DWORD style, DWORD clsStyle, LONG HitTest )
679 {
680 BOOL gray;
681 UINT DefItem;
682 #if 0
683 MENUITEMINFOW mii;
684 #endif
685
686 gray = !(style & WS_THICKFRAME) || (style & (WS_MAXIMIZE | WS_MINIMIZE));
687 EnableMenuItem( hmenu, SC_SIZE, (gray ? MF_GRAYED : MF_ENABLED) );
688 gray = ((style & WS_MAXIMIZE) != 0);
689 EnableMenuItem( hmenu, SC_MOVE, (gray ? MF_GRAYED : MF_ENABLED) );
690 gray = !(style & WS_MINIMIZEBOX) || (style & WS_MINIMIZE);
691 EnableMenuItem( hmenu, SC_MINIMIZE, (gray ? MF_GRAYED : MF_ENABLED) );
692 gray = !(style & WS_MAXIMIZEBOX) || (style & WS_MAXIMIZE);
693 EnableMenuItem( hmenu, SC_MAXIMIZE, (gray ? MF_GRAYED : MF_ENABLED) );
694 gray = !(style & (WS_MAXIMIZE | WS_MINIMIZE));
695 EnableMenuItem( hmenu, SC_RESTORE, (gray ? MF_GRAYED : MF_ENABLED) );
696 gray = (clsStyle & CS_NOCLOSE) != 0;
697
698 /* The menu item must keep its state if it's disabled */
699 if(gray)
700 EnableMenuItem( hmenu, SC_CLOSE, MF_GRAYED);
701
702 /* Set default menu item */
703 if(style & WS_MINIMIZE) DefItem = SC_RESTORE;
704 else if(HitTest == HTCAPTION) DefItem = ((style & (WS_MAXIMIZE | WS_MINIMIZE)) ? SC_RESTORE : SC_MAXIMIZE);
705 else DefItem = SC_CLOSE;
706 #if 0
707 mii.cbSize = sizeof(MENUITEMINFOW);
708 mii.fMask |= MIIM_STATE;
709 if((DefItem != SC_CLOSE) && GetMenuItemInfoW(hmenu, DefItem, FALSE, &mii) &&
710 (mii.fState & (MFS_GRAYED | MFS_DISABLED))) DefItem = SC_CLOSE;
711 #endif
712 SetMenuDefaultItem(hmenu, DefItem, MF_BYCOMMAND);
713 }
714
715 /******************************************************************************
716 *
717 * UINT MenuGetStartOfNextColumn(
718 * PROSMENUINFO MenuInfo)
719 *
720 *****************************************************************************/
721 static UINT MenuGetStartOfNextColumn(
722 PROSMENUINFO MenuInfo)
723 {
724 PROSMENUITEMINFO MenuItems;
725 UINT i;
726
727 i = MenuInfo->iItem;
728 if ( i == NO_SELECTED_ITEM )
729 return i;
730
731 if (MenuGetAllRosMenuItemInfo(MenuInfo->Self, &MenuItems) <= 0)
732 return NO_SELECTED_ITEM;
733
734 for (i++ ; i < MenuInfo->cItems; i++)
735 if (0 != (MenuItems[i].fType & (MF_MENUBREAK | MF_MENUBARBREAK)))
736 return i;
737
738 return NO_SELECTED_ITEM;
739 }
740
741 /******************************************************************************
742 *
743 * UINT MenuGetStartOfPrevColumn(
744 * PROSMENUINFO MenuInfo)
745 *
746 *****************************************************************************/
747
748 static UINT FASTCALL MenuGetStartOfPrevColumn(
749 PROSMENUINFO MenuInfo)
750 {
751 PROSMENUITEMINFO MenuItems;
752 UINT i;
753
754 if (!MenuInfo->iItem || MenuInfo->iItem == NO_SELECTED_ITEM)
755 return NO_SELECTED_ITEM;
756
757 if (MenuGetAllRosMenuItemInfo(MenuInfo->Self, &MenuItems) <= 0)
758 return NO_SELECTED_ITEM;
759
760 /* Find the start of the column */
761 for (i = MenuInfo->iItem;
762 0 != i && 0 == (MenuItems[i].fType & (MF_MENUBREAK | MF_MENUBARBREAK));
763 --i)
764 {
765 ; /* empty */
766 }
767
768 if (i == 0)
769 {
770 MenuCleanupAllRosMenuItemInfo(MenuItems);
771 return NO_SELECTED_ITEM;
772 }
773
774 for (--i; 0 != i; --i)
775 if (MenuItems[i].fType & (MF_MENUBREAK | MF_MENUBARBREAK))
776 break;
777
778 MenuCleanupAllRosMenuItemInfo(MenuItems);
779 TRACE("ret %d.\n", i );
780
781 return i;
782 }
783
784 /***********************************************************************
785 * MenuLoadBitmaps
786 *
787 * Load the arrow bitmap. We can't do this from MenuInit since user32
788 * can also be used (and thus initialized) from text-mode.
789 */
790 static void FASTCALL
791 MenuLoadBitmaps(VOID)
792 {
793 /* Load system buttons bitmaps */
794 if (NULL == BmpSysMenu)
795 {
796 BmpSysMenu = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_CLOSE));
797 }
798 }
799 /////////// Make gpsi OBMI via callback //////////////
800 /***********************************************************************
801 * get_arrow_bitmap
802 */
803 HBITMAP get_arrow_bitmap(void)
804 {
805 static HBITMAP arrow_bitmap;
806
807 if (!arrow_bitmap) arrow_bitmap = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_MNARROW));
808 return arrow_bitmap;
809 }
810
811 /***********************************************************************
812 * get_down_arrow_bitmap DFCS_MENUARROWDOWN
813 */
814 HBITMAP get_down_arrow_bitmap(void)
815 {
816 static HBITMAP arrow_bitmap;
817
818 if (!arrow_bitmap) arrow_bitmap = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_DNARROW));
819 return arrow_bitmap;
820 }
821
822 /***********************************************************************
823 * get_down_arrow_inactive_bitmap DFCS_MENUARROWDOWN | DFCS_INACTIVE
824 */
825 HBITMAP get_down_arrow_inactive_bitmap(void)
826 {
827 static HBITMAP arrow_bitmap;
828
829 if (!arrow_bitmap) arrow_bitmap = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_DNARROWI));
830 return arrow_bitmap;
831 }
832
833 /***********************************************************************
834 * get_up_arrow_bitmap DFCS_MENUARROWUP
835 */
836 HBITMAP get_up_arrow_bitmap(void)
837 {
838 static HBITMAP arrow_bitmap;
839
840 if (!arrow_bitmap) arrow_bitmap = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_UPARROW));
841 return arrow_bitmap;
842 }
843
844 /***********************************************************************
845 * get_up_arrow_inactive_bitmap DFCS_MENUARROWUP | DFCS_INACTIVE
846 */
847 static HBITMAP get_up_arrow_inactive_bitmap(void)
848 {
849 static HBITMAP arrow_bitmap;
850
851 if (!arrow_bitmap) arrow_bitmap = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_UPARROWI));
852 return arrow_bitmap;
853 }
854 ////////////////
855 /***********************************************************************
856 * MenuFindSubMenu
857 *
858 * Find a Sub menu. Return the position of the submenu, and modifies
859 * *hmenu in case it is found in another sub-menu.
860 * If the submenu cannot be found, NO_SELECTED_ITEM is returned.
861 */
862 static UINT FASTCALL MenuFindSubMenu(HMENU *hmenu, HMENU hSubTarget )
863 {
864 PMENU menu, pSubMenu;
865 HMENU hSubMenu;
866 UINT i;
867 PITEM item;
868
869 if (((*hmenu)==(HMENU)0xffff) ||(!(menu = MENU_GetMenu(*hmenu))))
870 return NO_SELECTED_ITEM;
871
872 item = menu->rgItems ? DesktopPtrToUser(menu->rgItems) : NULL;
873 for (i = 0; i < menu->cItems; i++, item = item->Next ? DesktopPtrToUser(item->Next) : NULL)//item++)
874 {
875 if (!item->spSubMenu)
876 continue;
877 else
878 {
879 pSubMenu = DesktopPtrToUser(item->spSubMenu);
880 hSubMenu = UserHMGetHandle(pSubMenu);
881 if (hSubMenu == hSubTarget)
882 {
883 return i;
884 }
885 else
886 {
887 HMENU hsubmenu = hSubMenu;
888 UINT pos = MenuFindSubMenu( &hsubmenu, hSubTarget );
889 if (pos != NO_SELECTED_ITEM)
890 {
891 *hmenu = hsubmenu;
892 return pos;
893 }
894 }
895 }
896 }
897 return NO_SELECTED_ITEM;
898 }
899
900 /***********************************************************************
901 * MENU_AdjustMenuItemRect
902 *
903 * Adjust menu item rectangle according to scrolling state.
904 */
905 static void
906 MENU_AdjustMenuItemRect(PROSMENUINFO menu, LPRECT rect)
907 {
908 if (menu->dwArrowsOn)
909 {
910 UINT arrow_bitmap_height;
911 BITMAP bmp;
912
913 GetObjectW(get_up_arrow_bitmap(), sizeof(bmp), &bmp);
914 arrow_bitmap_height = bmp.bmHeight;
915 rect->top += arrow_bitmap_height - menu->iTop;
916 rect->bottom += arrow_bitmap_height - menu->iTop;
917 }
918 }
919
920 /***********************************************************************
921 * MenuDrawPopupGlyph
922 *
923 * Draws popup magic glyphs (can be found in system menu).
924 */
925 static void FASTCALL
926 MenuDrawPopupGlyph(HDC dc, LPRECT r, INT_PTR popupMagic, BOOL inactive, BOOL hilite)
927 {
928 LOGFONTW lf;
929 HFONT hFont, hOldFont;
930 COLORREF clrsave;
931 INT bkmode;
932 TCHAR symbol;
933 switch (popupMagic)
934 {
935 case (INT_PTR) HBMMENU_POPUP_RESTORE:
936 symbol = '2';
937 break;
938 case (INT_PTR) HBMMENU_POPUP_MINIMIZE:
939 symbol = '0';
940 break;
941 case (INT_PTR) HBMMENU_POPUP_MAXIMIZE:
942 symbol = '1';
943 break;
944 case (INT_PTR) HBMMENU_POPUP_CLOSE:
945 symbol = 'r';
946 break;
947 default:
948 ERR("Invalid popup magic bitmap %d\n", (int)popupMagic);
949 return;
950 }
951 ZeroMemory(&lf, sizeof(LOGFONTW));
952 InflateRect(r, -2, -2);
953 lf.lfHeight = r->bottom - r->top;
954 lf.lfWidth = 0;
955 lf.lfWeight = FW_NORMAL;
956 lf.lfCharSet = DEFAULT_CHARSET;
957 lstrcpy(lf.lfFaceName, TEXT("Marlett"));
958 hFont = CreateFontIndirect(&lf);
959 /* save font and text color */
960 hOldFont = SelectObject(dc, hFont);
961 clrsave = GetTextColor(dc);
962 bkmode = GetBkMode(dc);
963 /* set color and drawing mode */
964 SetBkMode(dc, TRANSPARENT);
965 if (inactive)
966 {
967 /* draw shadow */
968 if (!hilite)
969 {
970 SetTextColor(dc, GetSysColor(COLOR_HIGHLIGHTTEXT));
971 TextOut(dc, r->left + 1, r->top + 1, &symbol, 1);
972 }
973 }
974 SetTextColor(dc, GetSysColor(inactive ? COLOR_GRAYTEXT : (hilite ? COLOR_HIGHLIGHTTEXT : COLOR_MENUTEXT)));
975 /* draw selected symbol */
976 TextOut(dc, r->left, r->top, &symbol, 1);
977 /* restore previous settings */
978 SetTextColor(dc, clrsave);
979 SelectObject(dc, hOldFont);
980 SetBkMode(dc, bkmode);
981 DeleteObject(hFont);
982 }
983
984 /***********************************************************************
985 * MenuFindItemByKey
986 *
987 * Find the menu item selected by a key press.
988 * Return item id, -1 if none, -2 if we should close the menu.
989 */
990 static UINT FASTCALL MenuFindItemByKey(HWND WndOwner, PROSMENUINFO MenuInfo,
991 WCHAR Key, BOOL ForceMenuChar)
992 {
993 ROSMENUINFO SysMenuInfo;
994 PROSMENUITEMINFO Items, ItemInfo;
995 LRESULT MenuChar;
996 UINT i;
997 WORD Flags = 0;
998
999 TRACE("\tlooking for '%c' (0x%02x) in [%p]\n", (char) Key, Key, MenuInfo);
1000
1001 if (NULL == MenuInfo || ! IsMenu(MenuInfo->Self))
1002 {
1003 if (MenuGetRosMenuInfo(&SysMenuInfo, GetSystemMenu(WndOwner, FALSE)))
1004 {
1005 MenuInfo = &SysMenuInfo;
1006 }
1007 else
1008 {
1009 MenuInfo = NULL;
1010 }
1011 }
1012
1013 if (NULL != MenuInfo)
1014 {
1015 if (MenuGetAllRosMenuItemInfo(MenuInfo->Self, &Items) <= 0)
1016 {
1017 return -1;
1018 }
1019 if ( !ForceMenuChar )
1020 {
1021 ItemInfo = Items;
1022 for (i = 0; i < MenuInfo->cItems; i++, ItemInfo++)
1023 {
1024 if ((ItemInfo->lpstr) && NULL != ItemInfo->dwTypeData)
1025 {
1026 WCHAR *p = (WCHAR *) ItemInfo->dwTypeData - 2;
1027 do
1028 {
1029 p = strchrW (p + 2, '&');
1030 }
1031 while (p != NULL && p [1] == '&');
1032 if (p && (toupperW(p[1]) == toupperW(Key))) return i;
1033 }
1034 }
1035 }
1036
1037 Flags |= MenuInfo->fFlags & MNF_POPUP ? MF_POPUP : 0;
1038 Flags |= MenuInfo->fFlags & MNF_SYSDESKMN ? MF_SYSMENU : 0;
1039
1040 MenuChar = SendMessageW(WndOwner, WM_MENUCHAR,
1041 MAKEWPARAM(Key, Flags), (LPARAM) MenuInfo->Self);
1042 if (HIWORD(MenuChar) == MNC_EXECUTE) return LOWORD(MenuChar);
1043 if (HIWORD(MenuChar) == MNC_CLOSE) return (UINT)(-2);
1044 }
1045 return (UINT)(-1);
1046 }
1047
1048 /***********************************************************************
1049 * MenuGetBitmapItemSize
1050 *
1051 * Get the size of a bitmap item.
1052 */
1053 static void FASTCALL MenuGetBitmapItemSize(PROSMENUITEMINFO lpitem, SIZE *size, HWND WndOwner)
1054 {
1055 BITMAP bm;
1056 HBITMAP bmp = lpitem->hbmpItem;
1057
1058 size->cx = size->cy = 0;
1059
1060 /* check if there is a magic menu item associated with this item */
1061 if (IS_MAGIC_BITMAP(bmp))
1062 {
1063 switch((INT_PTR) bmp)
1064 {
1065 case (INT_PTR)HBMMENU_CALLBACK:
1066 {
1067 MEASUREITEMSTRUCT measItem;
1068 measItem.CtlType = ODT_MENU;
1069 measItem.CtlID = 0;
1070 measItem.itemID = lpitem->wID;
1071 measItem.itemWidth = lpitem->Rect.right - lpitem->Rect.left;
1072 measItem.itemHeight = lpitem->Rect.bottom - lpitem->Rect.top;
1073 measItem.itemData = lpitem->dwItemData;
1074 SendMessageW( WndOwner, WM_MEASUREITEM, lpitem->wID, (LPARAM)&measItem);
1075 size->cx = measItem.itemWidth;
1076 size->cy = measItem.itemHeight;
1077 return;
1078 }
1079 break;
1080
1081 case (INT_PTR) HBMMENU_SYSTEM:
1082 if (0 != lpitem->dwItemData)
1083 {
1084 bmp = (HBITMAP) lpitem->dwItemData;
1085 break;
1086 }
1087 /* fall through */
1088 case (INT_PTR) HBMMENU_MBAR_RESTORE:
1089 case (INT_PTR) HBMMENU_MBAR_MINIMIZE:
1090 case (INT_PTR) HBMMENU_MBAR_CLOSE:
1091 case (INT_PTR) HBMMENU_MBAR_MINIMIZE_D:
1092 case (INT_PTR) HBMMENU_MBAR_CLOSE_D:
1093 case (INT_PTR) HBMMENU_POPUP_CLOSE:
1094 case (INT_PTR) HBMMENU_POPUP_RESTORE:
1095 case (INT_PTR) HBMMENU_POPUP_MAXIMIZE:
1096 case (INT_PTR) HBMMENU_POPUP_MINIMIZE:
1097 /* FIXME: Why we need to subtract these magic values? */
1098 /* to make them smaller than the menu bar? */
1099 size->cx = GetSystemMetrics(SM_CXSIZE) - 2;
1100 size->cy = GetSystemMetrics(SM_CYSIZE) - 4;
1101 return;
1102 }
1103 }
1104
1105 if (GetObjectW(bmp, sizeof(BITMAP), &bm))
1106 {
1107 size->cx = bm.bmWidth;
1108 size->cy = bm.bmHeight;
1109 }
1110 }
1111
1112 /***********************************************************************
1113 * MenuDrawBitmapItem
1114 *
1115 * Draw a bitmap item.
1116 */
1117 static void FASTCALL MenuDrawBitmapItem(HDC hdc, PROSMENUITEMINFO lpitem, const RECT *rect,
1118 PROSMENUINFO MenuInfo, HWND WndOwner, UINT odaction, BOOL MenuBar)
1119 {
1120 BITMAP bm;
1121 DWORD rop;
1122 HDC hdcMem;
1123 HBITMAP bmp;
1124 int w = rect->right - rect->left;
1125 int h = rect->bottom - rect->top;
1126 int bmp_xoffset = 0;
1127 int left, top;
1128 HBITMAP hbmToDraw = lpitem->hbmpItem;
1129 bmp = hbmToDraw;
1130
1131 /* Check if there is a magic menu item associated with this item */
1132 if (IS_MAGIC_BITMAP(hbmToDraw))
1133 {
1134 UINT flags = 0;
1135 RECT r;
1136
1137 r = *rect;
1138 switch ((INT_PTR)hbmToDraw)
1139 {
1140 case (INT_PTR)HBMMENU_SYSTEM:
1141 if (lpitem->dwTypeData)
1142 {
1143 bmp = (HBITMAP)lpitem->dwTypeData;
1144 if (!GetObjectW( bmp, sizeof(bm), &bm )) return;
1145 }
1146 else
1147 {
1148 if (!BmpSysMenu) BmpSysMenu = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_CLOSE));
1149 bmp = BmpSysMenu;
1150 if (! GetObjectW(bmp, sizeof(bm), &bm)) return;
1151 /* only use right half of the bitmap */
1152 bmp_xoffset = bm.bmWidth / 2;
1153 bm.bmWidth -= bmp_xoffset;
1154 }
1155 goto got_bitmap;
1156 case (INT_PTR)HBMMENU_MBAR_RESTORE:
1157 flags = DFCS_CAPTIONRESTORE;
1158 break;
1159 case (INT_PTR)HBMMENU_MBAR_MINIMIZE:
1160 r.right += 1;
1161 flags = DFCS_CAPTIONMIN;
1162 break;
1163 case (INT_PTR)HBMMENU_MBAR_MINIMIZE_D:
1164 r.right += 1;
1165 flags = DFCS_CAPTIONMIN | DFCS_INACTIVE;
1166 break;
1167 case (INT_PTR)HBMMENU_MBAR_CLOSE:
1168 flags = DFCS_CAPTIONCLOSE;
1169 break;
1170 case (INT_PTR)HBMMENU_MBAR_CLOSE_D:
1171 flags = DFCS_CAPTIONCLOSE | DFCS_INACTIVE;
1172 break;
1173 case (INT_PTR)HBMMENU_CALLBACK:
1174 {
1175 DRAWITEMSTRUCT drawItem;
1176 POINT origorg;
1177 drawItem.CtlType = ODT_MENU;
1178 drawItem.CtlID = 0;
1179 drawItem.itemID = lpitem->wID;
1180 drawItem.itemAction = odaction;
1181 drawItem.itemState = (lpitem->fState & MF_CHECKED)?ODS_CHECKED:0;
1182 drawItem.itemState |= (lpitem->fState & MF_DEFAULT)?ODS_DEFAULT:0;
1183 drawItem.itemState |= (lpitem->fState & MF_DISABLED)?ODS_DISABLED:0;
1184 drawItem.itemState |= (lpitem->fState & MF_GRAYED)?ODS_GRAYED|ODS_DISABLED:0;
1185 drawItem.itemState |= (lpitem->fState & MF_HILITE)?ODS_SELECTED:0;
1186 //drawItem.itemState |= (!(MenuInfo->fFlags & MNF_UNDERLINE))?ODS_NOACCEL:0;
1187 //drawItem.itemState |= (MenuInfo->fFlags & MNF_INACTIVE)?ODS_INACTIVE:0;
1188 drawItem.hwndItem = (HWND)MenuInfo->Self;
1189 drawItem.hDC = hdc;
1190 drawItem.rcItem = *rect;
1191 drawItem.itemData = lpitem->dwItemData;
1192 /* some applications make this assumption on the DC's origin */
1193 SetViewportOrgEx( hdc, lpitem->Rect.left, lpitem->Rect.top, &origorg);
1194 OffsetRect( &drawItem.rcItem, - lpitem->Rect.left, - lpitem->Rect.top);
1195 SendMessageW( WndOwner, WM_DRAWITEM, 0, (LPARAM)&drawItem);
1196 SetViewportOrgEx( hdc, origorg.x, origorg.y, NULL);
1197 return;
1198 }
1199 break;
1200
1201 case (INT_PTR) HBMMENU_POPUP_CLOSE:
1202 case (INT_PTR) HBMMENU_POPUP_RESTORE:
1203 case (INT_PTR) HBMMENU_POPUP_MAXIMIZE:
1204 case (INT_PTR) HBMMENU_POPUP_MINIMIZE:
1205 MenuDrawPopupGlyph(hdc, &r, (INT_PTR)hbmToDraw, lpitem->fState & MF_GRAYED, lpitem->fState & MF_HILITE);
1206 return;
1207 }
1208 InflateRect(&r, -1, -1);
1209 if (0 != (lpitem->fState & MF_HILITE))
1210 {
1211 flags |= DFCS_PUSHED;
1212 }
1213 DrawFrameControl(hdc, &r, DFC_CAPTION, flags);
1214 return;
1215 }
1216
1217 if (!bmp || !GetObjectW( bmp, sizeof(bm), &bm )) return;
1218
1219 got_bitmap:
1220 hdcMem = CreateCompatibleDC( hdc );
1221 SelectObject( hdcMem, bmp );
1222
1223 /* handle fontsize > bitmap_height */
1224 top = (h>bm.bmHeight) ? rect->top+(h-bm.bmHeight)/2 : rect->top;
1225 left=rect->left;
1226 rop=((lpitem->fState & MF_HILITE) && !IS_MAGIC_BITMAP(hbmToDraw)) ? NOTSRCCOPY : SRCCOPY;
1227 if ((lpitem->fState & MF_HILITE) && lpitem->hbmpItem)
1228 SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT));
1229 BitBlt( hdc, left, top, w, h, hdcMem, bmp_xoffset, 0, rop );
1230 DeleteDC( hdcMem );
1231 }
1232
1233 /***********************************************************************
1234 * MenuCalcItemSize
1235 *
1236 * Calculate the size of the menu item and store it in lpitem->rect.
1237 */
1238 static void FASTCALL MenuCalcItemSize( HDC hdc, PROSMENUITEMINFO lpitem, PROSMENUINFO MenuInfo, HWND hwndOwner,
1239 INT orgX, INT orgY, BOOL menuBar, BOOL textandbmp)
1240 {
1241 WCHAR *p;
1242 UINT check_bitmap_width = GetSystemMetrics( SM_CXMENUCHECK );
1243 UINT arrow_bitmap_width;
1244 BITMAP bm;
1245 INT itemheight = 0;
1246
1247 TRACE("dc=%x owner=%x (%d,%d)\n", hdc, hwndOwner, orgX, orgY);
1248
1249 GetObjectW( get_arrow_bitmap(), sizeof(bm), &bm );
1250 arrow_bitmap_width = bm.bmWidth;
1251
1252 MenuCharSize.cx = GdiGetCharDimensions( hdc, NULL, &MenuCharSize.cy );
1253
1254 SetRect( &lpitem->Rect, orgX, orgY, orgX, orgY );
1255
1256 if (lpitem->fType & MF_OWNERDRAW)
1257 {
1258 MEASUREITEMSTRUCT mis;
1259 mis.CtlType = ODT_MENU;
1260 mis.CtlID = 0;
1261 mis.itemID = lpitem->wID;
1262 mis.itemData = lpitem->dwItemData;
1263 mis.itemHeight = HIWORD( GetDialogBaseUnits());
1264 mis.itemWidth = 0;
1265 SendMessageW( hwndOwner, WM_MEASUREITEM, 0, (LPARAM)&mis );
1266 /* Tests reveal that Windows ( Win95 thru WinXP) adds twice the average
1267 * width of a menufont character to the width of an owner-drawn menu.
1268 */
1269 lpitem->Rect.right += mis.itemWidth + 2 * MenuCharSize.cx;
1270 if (menuBar) {
1271 /* under at least win95 you seem to be given a standard
1272 height for the menu and the height value is ignored */
1273 lpitem->Rect.bottom += GetSystemMetrics(SM_CYMENUSIZE);
1274 } else
1275 lpitem->Rect.bottom += mis.itemHeight;
1276 // Or this,
1277 //Item->cxBmp = mis.itemWidth;
1278 //Item->cyBmp = mis.itemHeight;
1279 TRACE("id=%04lx size=%dx%d\n",
1280 lpitem->wID, lpitem->Rect.right-lpitem->Rect.left,
1281 lpitem->Rect.bottom-lpitem->Rect.top);
1282 return;
1283 }
1284
1285 if (lpitem->fType & MF_SEPARATOR)
1286 {
1287 lpitem->Rect.bottom += GetSystemMetrics( SM_CYMENUSIZE)/2;//SEPARATOR_HEIGHT;
1288 if( !menuBar)
1289 lpitem->Rect.right += arrow_bitmap_width/*check_bitmap_width*/ + MenuCharSize.cx;
1290 return;
1291 }
1292
1293 lpitem->dxTab = 0;
1294
1295 if (lpitem->hbmpItem)
1296 {
1297 SIZE size;
1298
1299 if (!menuBar) {
1300 MenuGetBitmapItemSize(lpitem, &size, hwndOwner );
1301 /* Keep the size of the bitmap in callback mode to be able
1302 * to draw it correctly */
1303 lpitem->maxBmpSize = size;
1304 MenuInfo->cxTextAlign = max(MenuInfo->cxTextAlign, size.cx);
1305 MenuSetRosMenuInfo(MenuInfo);
1306 lpitem->Rect.right += size.cx + 2;
1307 itemheight = size.cy + 2;
1308
1309 if( !(MenuInfo->dwStyle & MNS_NOCHECK))
1310 lpitem->Rect.right += 2 * check_bitmap_width;
1311 lpitem->Rect.right += 4 + MenuCharSize.cx;
1312 lpitem->dxTab = lpitem->Rect.right;
1313 lpitem->Rect.right += arrow_bitmap_width;//check_bitmap_width;
1314 } else /* hbmpItem & MenuBar */ {
1315 MenuGetBitmapItemSize(lpitem, &size, hwndOwner );
1316 lpitem->Rect.right += size.cx;
1317 if( lpitem->lpstr) lpitem->Rect.right += 2;
1318 itemheight = size.cy;
1319
1320 /* Special case: Minimize button doesn't have a space behind it. */
1321 if (lpitem->hbmpItem == (HBITMAP)HBMMENU_MBAR_MINIMIZE ||
1322 lpitem->hbmpItem == (HBITMAP)HBMMENU_MBAR_MINIMIZE_D)
1323 lpitem->Rect.right -= 1;
1324 }
1325 }
1326 else if (!menuBar) {
1327 if( !(MenuInfo->dwStyle & MNS_NOCHECK))
1328 lpitem->Rect.right += check_bitmap_width;
1329 lpitem->Rect.right += 4 + MenuCharSize.cx;
1330 lpitem->dxTab = lpitem->Rect.right;
1331 lpitem->Rect.right += check_bitmap_width;
1332 }
1333
1334 /* it must be a text item - unless it's the system menu */
1335 if (!(lpitem->fType & MF_SYSMENU) && lpitem->lpstr) {
1336 HFONT hfontOld = NULL;
1337 RECT rc = lpitem->Rect;
1338 LONG txtheight, txtwidth;
1339
1340 if ( lpitem->fState & MFS_DEFAULT ) {
1341 hfontOld = SelectObject( hdc, hMenuFontBold );
1342 }
1343 if (menuBar) {
1344 txtheight = DrawTextW( hdc, lpitem->dwTypeData, -1, &rc,
1345 DT_SINGLELINE|DT_CALCRECT);
1346 lpitem->Rect.right += rc.right - rc.left;
1347 itemheight = max( max( itemheight, txtheight),
1348 GetSystemMetrics( SM_CYMENU) - 1);
1349 lpitem->Rect.right += 2 * MenuCharSize.cx;
1350 } else {
1351 if ((p = strchrW( lpitem->dwTypeData, '\t' )) != NULL) {
1352 RECT tmprc = rc;
1353 LONG tmpheight;
1354 int n = (int)( p - lpitem->dwTypeData);
1355 /* Item contains a tab (only meaningful in popup menus) */
1356 /* get text size before the tab */
1357 txtheight = DrawTextW( hdc, lpitem->dwTypeData, n, &rc,
1358 DT_SINGLELINE|DT_CALCRECT);
1359 txtwidth = rc.right - rc.left;
1360 p += 1; /* advance past the Tab */
1361 /* get text size after the tab */
1362 tmpheight = DrawTextW( hdc, p, -1, &tmprc,
1363 DT_SINGLELINE|DT_CALCRECT);
1364 lpitem->dxTab += txtwidth;
1365 txtheight = max( txtheight, tmpheight);
1366 txtwidth += MenuCharSize.cx + /* space for the tab */
1367 tmprc.right - tmprc.left; /* space for the short cut */
1368 } else {
1369 txtheight = DrawTextW( hdc, lpitem->dwTypeData, -1, &rc,
1370 DT_SINGLELINE|DT_CALCRECT);
1371 txtwidth = rc.right - rc.left;
1372 lpitem->dxTab += txtwidth;
1373 }
1374 lpitem->Rect.right += 2 + txtwidth;
1375 itemheight = max( itemheight,
1376 max( txtheight + 2, MenuCharSize.cy + 4));
1377 }
1378 if (hfontOld) SelectObject (hdc, hfontOld);
1379 } else if( menuBar) {
1380 itemheight = max( itemheight, GetSystemMetrics(SM_CYMENU)-1);
1381 }
1382 lpitem->Rect.bottom += itemheight;
1383 TRACE("(%ld,%ld)-(%ld,%ld)\n", lpitem->Rect.left, lpitem->Rect.top, lpitem->Rect.right, lpitem->Rect.bottom);
1384 }
1385
1386 /***********************************************************************
1387 * MENU_GetMaxPopupHeight
1388 */
1389 static UINT
1390 MENU_GetMaxPopupHeight(PROSMENUINFO lppop)
1391 {
1392 if (lppop->cyMax)
1393 return lppop->cyMax;
1394 return GetSystemMetrics(SM_CYSCREEN) - GetSystemMetrics(SM_CYBORDER);
1395 }
1396
1397 /***********************************************************************
1398 * MenuPopupMenuCalcSize
1399 *
1400 * Calculate the size of a popup menu.
1401 */
1402 static void FASTCALL MenuPopupMenuCalcSize(PROSMENUINFO MenuInfo, HWND WndOwner)
1403 {
1404 ROSMENUITEMINFO lpitem;
1405 HDC hdc;
1406 int start, i;
1407 int orgX, orgY, maxX, maxTab, maxTabWidth, maxHeight;
1408 BOOL textandbmp = FALSE;
1409
1410 MenuInfo->cxMenu = MenuInfo->cyMenu = 0;
1411 if (MenuInfo->cItems == 0)
1412 {
1413 MenuSetRosMenuInfo(MenuInfo);
1414 return;
1415 }
1416
1417 hdc = GetDC(NULL);
1418 SelectObject( hdc, hMenuFont );
1419
1420 start = 0;
1421 maxX = 2 + 1;
1422
1423 MenuInfo->cxTextAlign = 0;
1424
1425 MenuInitRosMenuItemInfo(&lpitem);
1426 while (start < MenuInfo->cItems)
1427 {
1428 orgX = maxX;
1429 orgY = 2;
1430
1431 maxTab = maxTabWidth = 0;
1432
1433 /* Parse items until column break or end of menu */
1434 for (i = start; i < MenuInfo->cItems; i++)
1435 {
1436 if (! MenuGetRosMenuItemInfo(MenuInfo->Self, i, &lpitem))
1437 {
1438 MenuCleanupRosMenuItemInfo(&lpitem);
1439 MenuSetRosMenuInfo(MenuInfo);
1440 return;
1441 }
1442 if (i != start &&
1443 (lpitem.fType & (MF_MENUBREAK | MF_MENUBARBREAK))) break;
1444
1445 if( lpitem.lpstr && lpitem.hbmpItem) textandbmp = TRUE;
1446
1447 MenuCalcItemSize(hdc, &lpitem, MenuInfo, WndOwner, orgX, orgY, FALSE, textandbmp);
1448 if (! MenuSetRosMenuItemInfo(MenuInfo->Self, i, &lpitem))
1449 {
1450 MenuCleanupRosMenuItemInfo(&lpitem);
1451 MenuSetRosMenuInfo(MenuInfo);
1452 return;
1453 }
1454 // Not sure here,, The patch from wine removes this.
1455 // if ((lpitem.fType & MF_MENUBARBREAK) != 0)
1456 // {
1457 // OrgX++;
1458 // }
1459 maxX = max(maxX, lpitem.Rect.right);
1460 orgY = lpitem.Rect.bottom;
1461 if ((lpitem.lpstr) && lpitem.dxTab )
1462 {
1463 maxTab = max( maxTab, lpitem.dxTab );
1464 maxTabWidth = max(maxTabWidth, lpitem.Rect.right - lpitem.dxTab);
1465 }
1466 }
1467
1468 /* Finish the column (set all items to the largest width found) */
1469 maxX = max( maxX, maxTab + maxTabWidth );
1470 while (start < i)
1471 {
1472 if (MenuGetRosMenuItemInfo(MenuInfo->Self, start, &lpitem))
1473 {
1474 lpitem.Rect.right = maxX;
1475 if ((lpitem.lpstr) && 0 != lpitem.dxTab)
1476 {
1477 lpitem.dxTab = maxTab;
1478 }
1479 MenuSetRosMenuItemInfo(MenuInfo->Self, start, &lpitem);
1480 }
1481 start++;
1482 }
1483 MenuInfo->cyMenu = max(MenuInfo->cyMenu, orgY);
1484 }
1485
1486 MenuInfo->cxMenu = maxX;
1487 /* if none of the items have both text and bitmap then
1488 * the text and bitmaps are all aligned on the left. If there is at
1489 * least one item with both text and bitmap then bitmaps are
1490 * on the left and texts left aligned with the right hand side
1491 * of the bitmaps */
1492 if( !textandbmp) MenuInfo->cxTextAlign = 0;
1493
1494 /* space for 3d border */
1495 MenuInfo->cyMenu += MENU_BOTTOM_MARGIN;
1496 MenuInfo->cxMenu += 2;
1497
1498 /* Adjust popup height if it exceeds maximum */
1499 maxHeight = MENU_GetMaxPopupHeight(MenuInfo);
1500 MenuInfo->iMaxTop = MenuInfo->cyMenu - MENU_TOP_MARGIN;
1501 if (MenuInfo->cyMenu >= maxHeight)
1502 {
1503 MenuInfo->cyMenu = maxHeight;
1504 MenuInfo->dwArrowsOn = 1;
1505 }
1506 else
1507 {
1508 MenuInfo->dwArrowsOn = 0;
1509 }
1510
1511 MenuCleanupRosMenuItemInfo(&lpitem);
1512 MenuSetRosMenuInfo(MenuInfo);
1513 ReleaseDC( 0, hdc );
1514 }
1515
1516 /***********************************************************************
1517 * MenuMenuBarCalcSize
1518 *
1519 * FIXME: Word 6 implements its own MDI and its own 'close window' bitmap
1520 * height is off by 1 pixel which causes lengthy window relocations when
1521 * active document window is maximized/restored.
1522 *
1523 * Calculate the size of the menu bar.
1524 */
1525 static void FASTCALL MenuMenuBarCalcSize( HDC hdc, LPRECT lprect,
1526 PROSMENUINFO MenuInfo, HWND hwndOwner )
1527 {
1528 ROSMENUITEMINFO ItemInfo;
1529 int start, i, orgX, orgY, maxY, helpPos;
1530
1531 if ((lprect == NULL) || (MenuInfo == NULL)) return;
1532 if (MenuInfo->cItems == 0) return;
1533 TRACE("left=%ld top=%ld right=%ld bottom=%ld\n", lprect->left, lprect->top, lprect->right, lprect->bottom);
1534 MenuInfo->cxMenu = lprect->right - lprect->left;
1535 MenuInfo->cyMenu = 0;
1536 maxY = lprect->top + 1;
1537 start = 0;
1538 helpPos = -1;
1539
1540 MenuInfo->cxTextAlign = 0;
1541
1542 MenuInitRosMenuItemInfo(&ItemInfo);
1543 while (start < MenuInfo->cItems)
1544 {
1545 if (! MenuGetRosMenuItemInfo(MenuInfo->Self, start, &ItemInfo))
1546 {
1547 MenuCleanupRosMenuItemInfo(&ItemInfo);
1548 return;
1549 }
1550 orgX = lprect->left;
1551 orgY = maxY;
1552
1553 /* Parse items until line break or end of menu */
1554 for (i = start; i < MenuInfo->cItems; i++)
1555 {
1556 if ((helpPos == -1) && (ItemInfo.fType & MF_RIGHTJUSTIFY)) helpPos = i;
1557 if ((i != start) &&
1558 (ItemInfo.fType & (MF_MENUBREAK | MF_MENUBARBREAK))) break;
1559
1560 TRACE("calling MENU_CalcItemSize org=(%d, %d)\n", orgX, orgY);
1561 MenuCalcItemSize(hdc, &ItemInfo, MenuInfo, hwndOwner, orgX, orgY, TRUE, FALSE);
1562 if (! MenuSetRosMenuItemInfo(MenuInfo->Self, i, &ItemInfo))
1563 {
1564 MenuCleanupRosMenuItemInfo(&ItemInfo);
1565 return;
1566 }
1567
1568 if (ItemInfo.Rect.right > lprect->right)
1569 {
1570 if (i != start) break;
1571 else ItemInfo.Rect.right = lprect->right;
1572 }
1573 maxY = max( maxY, ItemInfo.Rect.bottom );
1574 orgX = ItemInfo.Rect.right;
1575 if (i + 1 < MenuInfo->cItems)
1576 {
1577 if (! MenuGetRosMenuItemInfo(MenuInfo->Self, i + 1, &ItemInfo))
1578 {
1579 MenuCleanupRosMenuItemInfo(&ItemInfo);
1580 return;
1581 }
1582 }
1583 }
1584
1585 /* FIXME: Is this really needed? */ /*NO! it is not needed, why make the
1586 HBMMENU_MBAR_CLOSE, MINIMIZE & RESTORE, look the same size as the menu bar! */
1587 #if 0
1588 /* Finish the line (set all items to the largest height found) */
1589 while (start < i)
1590 {
1591 if (MenuGetRosMenuItemInfo(MenuInfo->Self, start, &ItemInfo))
1592 {
1593 ItemInfo.Rect.bottom = maxY;
1594 MenuSetRosMenuItemInfo(MenuInfo->Self, start, &ItemInfo);
1595 }
1596 start++;
1597 }
1598 #else
1599 start = i; /* This works! */
1600 #endif
1601 }
1602
1603 lprect->bottom = maxY;
1604 MenuInfo->cyMenu = lprect->bottom - lprect->top;
1605 MenuSetRosMenuInfo(MenuInfo);
1606
1607 if (helpPos != -1)
1608 {
1609 /* Flush right all items between the MF_RIGHTJUSTIFY and */
1610 /* the last item (if several lines, only move the last line) */
1611 if (! MenuGetRosMenuItemInfo(MenuInfo->Self, MenuInfo->cItems - 1, &ItemInfo))
1612 {
1613 MenuCleanupRosMenuItemInfo(&ItemInfo);
1614 return;
1615 }
1616 orgY = ItemInfo.Rect.top;
1617 orgX = lprect->right;
1618 for (i = MenuInfo->cItems - 1; helpPos <= i; i--)
1619 {
1620 if (i < helpPos)
1621 {
1622 break; /* done */
1623 }
1624 if (ItemInfo.Rect.top != orgY)
1625 {
1626 break; /* Other line */
1627 }
1628 if (orgX <= ItemInfo.Rect.right)
1629 {
1630 break; /* Too far right already */
1631 }
1632 ItemInfo.Rect.left += orgX - ItemInfo.Rect.right;
1633 ItemInfo.Rect.right = orgX;
1634 orgX = ItemInfo.Rect.left;
1635 MenuSetRosMenuItemInfo(MenuInfo->Self, i, &ItemInfo);
1636 if (helpPos + 1 <= i &&
1637 ! MenuGetRosMenuItemInfo(MenuInfo->Self, i - 1, &ItemInfo))
1638 {
1639 MenuCleanupRosMenuItemInfo(&ItemInfo);
1640 return;
1641 }
1642 }
1643 }
1644
1645 MenuCleanupRosMenuItemInfo(&ItemInfo);
1646 }
1647
1648 /***********************************************************************
1649 * MENU_DrawScrollArrows
1650 *
1651 * Draw scroll arrows.
1652 */
1653 static void
1654 MENU_DrawScrollArrows(PROSMENUINFO lppop, HDC hdc)
1655 {
1656 HDC hdcMem = CreateCompatibleDC(hdc);
1657 HBITMAP hOrigBitmap;
1658 UINT arrow_bitmap_width, arrow_bitmap_height;
1659 BITMAP bmp;
1660 RECT rect;
1661
1662 GetObjectW(get_down_arrow_bitmap(), sizeof(bmp), &bmp);
1663 arrow_bitmap_width = bmp.bmWidth;
1664 arrow_bitmap_height = bmp.bmHeight;
1665
1666
1667 if (lppop->iTop)
1668 hOrigBitmap = SelectObject(hdcMem, get_up_arrow_bitmap());
1669 else
1670 hOrigBitmap = SelectObject(hdcMem, get_up_arrow_inactive_bitmap());
1671 rect.left = 0;
1672 rect.top = 0;
1673 rect.right = lppop->cxMenu;
1674 rect.bottom = arrow_bitmap_height;
1675 FillRect(hdc, &rect, GetSysColorBrush(COLOR_MENU));
1676 BitBlt(hdc, (lppop->cxMenu - arrow_bitmap_width) / 2, 0,
1677 arrow_bitmap_width, arrow_bitmap_height, hdcMem, 0, 0, SRCCOPY);
1678 rect.top = lppop->cyMenu - arrow_bitmap_height;
1679 rect.bottom = lppop->cyMenu;
1680 FillRect(hdc, &rect, GetSysColorBrush(COLOR_MENU));
1681 if (lppop->iTop < lppop->iMaxTop - (MENU_GetMaxPopupHeight(lppop) - 2 * arrow_bitmap_height))
1682 SelectObject(hdcMem, get_down_arrow_bitmap());
1683 else
1684 SelectObject(hdcMem, get_down_arrow_inactive_bitmap());
1685 BitBlt(hdc, (lppop->cxMenu - arrow_bitmap_width) / 2,
1686 lppop->cyMenu - arrow_bitmap_height,
1687 arrow_bitmap_width, arrow_bitmap_height, hdcMem, 0, 0, SRCCOPY);
1688 SelectObject(hdcMem, hOrigBitmap);
1689 DeleteDC(hdcMem);
1690 }
1691
1692
1693 /***********************************************************************
1694 * MenuDrawMenuItem
1695 *
1696 * Draw a single menu item.
1697 */
1698 static void FASTCALL MenuDrawMenuItem(HWND hWnd, PROSMENUINFO MenuInfo, HWND WndOwner, HDC hdc,
1699 PROSMENUITEMINFO lpitem, UINT Height, BOOL menuBar, UINT odaction)
1700 {
1701 RECT rect;
1702 PWCHAR Text;
1703 BOOL flat_menu = FALSE;
1704 int bkgnd;
1705 PWND Wnd = ValidateHwnd(hWnd);
1706
1707 if (!Wnd)
1708 return;
1709
1710 if (lpitem->fType & MF_SYSMENU)
1711 {
1712 if ( (Wnd->style & WS_MINIMIZE))
1713 {
1714 UserGetInsideRectNC(Wnd, &rect);
1715 UserDrawSysMenuButton(hWnd, hdc, &rect, lpitem->fState & (MF_HILITE | MF_MOUSESELECT));
1716 }
1717 return;
1718 }
1719
1720 SystemParametersInfoW (SPI_GETFLATMENU, 0, &flat_menu, 0);
1721 bkgnd = (menuBar && flat_menu) ? COLOR_MENUBAR : COLOR_MENU;
1722
1723 /* Setup colors */
1724
1725 if (lpitem->fState & MF_HILITE)
1726 {
1727 if(menuBar && !flat_menu) {
1728 SetTextColor(hdc, GetSysColor(COLOR_MENUTEXT));
1729 SetBkColor(hdc, GetSysColor(COLOR_MENU));
1730 } else {
1731 if (lpitem->fState & MF_GRAYED)
1732 SetTextColor(hdc, GetSysColor(COLOR_GRAYTEXT));
1733 else
1734 SetTextColor(hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
1735 SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT));
1736 }
1737 }
1738 else
1739 {
1740 if (lpitem->fState & MF_GRAYED)
1741 SetTextColor( hdc, GetSysColor( COLOR_GRAYTEXT ) );
1742 else
1743 SetTextColor( hdc, GetSysColor( COLOR_MENUTEXT ) );
1744 SetBkColor( hdc, GetSysColor( bkgnd ) );
1745 }
1746
1747 rect = lpitem->Rect;
1748 MENU_AdjustMenuItemRect(MenuInfo, &rect);
1749
1750 if (lpitem->fType & MF_OWNERDRAW)
1751 {
1752 /*
1753 ** Experimentation under Windows reveals that an owner-drawn
1754 ** menu is given the rectangle which includes the space it requested
1755 ** in its response to WM_MEASUREITEM _plus_ width for a checkmark
1756 ** and a popup-menu arrow. This is the value of lpitem->rect.
1757 ** Windows will leave all drawing to the application except for
1758 ** the popup-menu arrow. Windows always draws that itself, after
1759 ** the menu owner has finished drawing.
1760 */
1761 DRAWITEMSTRUCT dis;
1762
1763 dis.CtlType = ODT_MENU;
1764 dis.CtlID = 0;
1765 dis.itemID = lpitem->wID;
1766 dis.itemData = (DWORD)lpitem->dwItemData;
1767 dis.itemState = 0;
1768 if (lpitem->fState & MF_CHECKED) dis.itemState |= ODS_CHECKED;
1769 if (lpitem->fState & MF_DEFAULT) dis.itemState |= ODS_DEFAULT;
1770 if (lpitem->fState & MF_DISABLED) dis.itemState |= ODS_DISABLED;
1771 if (lpitem->fState & MF_GRAYED) dis.itemState |= ODS_GRAYED | ODS_DISABLED;
1772 if (lpitem->fState & MF_HILITE) dis.itemState |= ODS_SELECTED;
1773 //if (!(MenuInfo->fFlags & MNF_UNDERLINE)) dis.itemState |= ODS_NOACCEL;
1774 //if (MenuInfo->fFlags & MNF_INACTIVE) dis.itemState |= ODS_INACTIVE;
1775 dis.itemAction = odaction; /* ODA_DRAWENTIRE | ODA_SELECT | ODA_FOCUS; */
1776 dis.hwndItem = (HWND) MenuInfo->Self;
1777 dis.hDC = hdc;
1778 dis.rcItem = rect;
1779 TRACE("Ownerdraw: owner=%p itemID=%d, itemState=%d, itemAction=%d, "
1780 "hwndItem=%p, hdc=%p, rcItem={%ld,%ld,%ld,%ld}\n", hWnd,
1781 dis.itemID, dis.itemState, dis.itemAction, dis.hwndItem,
1782 dis.hDC, dis.rcItem.left, dis.rcItem.top, dis.rcItem.right,
1783 dis.rcItem.bottom);
1784 SendMessageW(WndOwner, WM_DRAWITEM, 0, (LPARAM) &dis);
1785 /* Draw the popup-menu arrow */
1786 if (lpitem->hSubMenu)
1787 {
1788 RECT rectTemp;
1789 CopyRect(&rectTemp, &rect);
1790 rectTemp.left = rectTemp.right - GetSystemMetrics(SM_CXMENUCHECK);
1791 DrawFrameControl(hdc, &rectTemp, DFC_MENU, DFCS_MENUARROW);
1792 }
1793 return;
1794 }
1795
1796 if (menuBar && (lpitem->fType & MF_SEPARATOR)) return;
1797
1798 if (lpitem->fState & MF_HILITE)
1799 {
1800 if (flat_menu)
1801 {
1802 InflateRect (&rect, -1, -1);
1803 FillRect(hdc, &rect, GetSysColorBrush(COLOR_MENUHILIGHT));
1804 InflateRect (&rect, 1, 1);
1805 FrameRect(hdc, &rect, GetSysColorBrush(COLOR_HIGHLIGHT));
1806 }
1807 else
1808 {
1809 if(menuBar)
1810 DrawEdge(hdc, &rect, BDR_SUNKENOUTER, BF_RECT);
1811 else
1812 FillRect(hdc, &rect, GetSysColorBrush(COLOR_HIGHLIGHT));
1813 }
1814 }
1815 else
1816 FillRect( hdc, &rect, GetSysColorBrush(bkgnd) );
1817
1818 SetBkMode( hdc, TRANSPARENT );
1819
1820 /* vertical separator */
1821 if (!menuBar && (lpitem->fType & MF_MENUBARBREAK))
1822 {
1823 HPEN oldPen;
1824 RECT rc = rect;
1825
1826 rc.left -= 3;
1827 rc.top = 3;
1828 rc.bottom = Height - 3;
1829 if (flat_menu)
1830 {
1831 oldPen = SelectObject( hdc, GetStockObject(DC_PEN) );
1832 SetDCPenColor(hdc, GetSysColor(COLOR_BTNSHADOW));
1833 MoveToEx( hdc, rc.left, rc.top, NULL );
1834 LineTo( hdc, rc.left, rc.bottom );
1835 SelectObject( hdc, oldPen );
1836 }
1837 else
1838 DrawEdge (hdc, &rc, EDGE_ETCHED, BF_LEFT);
1839 }
1840
1841 /* horizontal separator */
1842 if (lpitem->fType & MF_SEPARATOR)
1843 {
1844 HPEN oldPen;
1845 RECT rc = rect;
1846
1847 rc.left++;
1848 rc.right--;
1849 rc.top += SEPARATOR_HEIGHT / 2;
1850 if (flat_menu)
1851 {
1852 oldPen = SelectObject( hdc, GetStockObject(DC_PEN) );
1853 SetDCPenColor( hdc, GetSysColor(COLOR_BTNSHADOW));
1854 MoveToEx( hdc, rc.left, rc.top, NULL );
1855 LineTo( hdc, rc.right, rc.top );
1856 SelectObject( hdc, oldPen );
1857 }
1858 else
1859 DrawEdge (hdc, &rc, EDGE_ETCHED, BF_TOP);
1860 return;
1861 }
1862
1863 #if 0
1864 /* helper lines for debugging */
1865 /* This is a very good test tool when hacking menus! (JT) 07/16/2006 */
1866 FrameRect(hdc, &rect, GetStockObject(BLACK_BRUSH));
1867 SelectObject(hdc, GetStockObject(DC_PEN));
1868 SetDCPenColor(hdc, GetSysColor(COLOR_WINDOWFRAME));
1869 MoveToEx(hdc, rect.left, (rect.top + rect.bottom) / 2, NULL);
1870 LineTo(hdc, rect.right, (rect.top + rect.bottom) / 2);
1871 #endif
1872
1873 if (!menuBar)
1874 {
1875 HBITMAP bm;
1876 INT y = rect.top + rect.bottom;
1877 RECT rc = rect;
1878 BOOL checked = FALSE;
1879 UINT check_bitmap_width = GetSystemMetrics( SM_CXMENUCHECK );
1880 UINT check_bitmap_height = GetSystemMetrics( SM_CYMENUCHECK );
1881 /* Draw the check mark
1882 *
1883 * FIXME:
1884 * Custom checkmark bitmaps are monochrome but not always 1bpp.
1885 */
1886 if( !(MenuInfo->dwStyle & MNS_NOCHECK)) {
1887 bm = (lpitem->fState & MF_CHECKED) ? lpitem->hbmpChecked :
1888 lpitem->hbmpUnchecked;
1889 if (bm) /* we have a custom bitmap */
1890 {
1891 HDC hdcMem = CreateCompatibleDC( hdc );
1892
1893 SelectObject( hdcMem, bm );
1894 BitBlt( hdc, rc.left, (y - check_bitmap_height) / 2,
1895 check_bitmap_width, check_bitmap_height,
1896 hdcMem, 0, 0, SRCCOPY );
1897 DeleteDC( hdcMem );
1898 checked = TRUE;
1899 }
1900 else if (lpitem->fState & MF_CHECKED) /* standard bitmaps */
1901 {
1902 RECT r;
1903 CopyRect(&r, &rect);
1904 r.right = r.left + GetSystemMetrics(SM_CXMENUCHECK);
1905 DrawFrameControl( hdc, &r, DFC_MENU,
1906 (lpitem->fType & MFT_RADIOCHECK) ?
1907 DFCS_MENUBULLET : DFCS_MENUCHECK);
1908 checked = TRUE;
1909 }
1910 }
1911 if ( lpitem->hbmpItem )
1912 {
1913 RECT bmpRect;
1914 CopyRect(&bmpRect, &rect);
1915 if (!(MenuInfo->dwStyle & MNS_CHECKORBMP) && !(MenuInfo->dwStyle & MNS_NOCHECK))
1916 bmpRect.left += check_bitmap_width + 2;
1917 if (!(checked && (MenuInfo->dwStyle & MNS_CHECKORBMP)))
1918 {
1919 bmpRect.right = bmpRect.left + lpitem->maxBmpSize.cx;
1920 MenuDrawBitmapItem(hdc, lpitem, &bmpRect, MenuInfo, WndOwner, odaction, menuBar);
1921 }
1922 }
1923 /* Draw the popup-menu arrow */
1924 if (lpitem->hSubMenu)
1925 {
1926 RECT rectTemp;
1927 CopyRect(&rectTemp, &rect);
1928 rectTemp.left = rectTemp.right - GetSystemMetrics(SM_CXMENUCHECK);
1929 DrawFrameControl(hdc, &rectTemp, DFC_MENU, DFCS_MENUARROW);
1930 }
1931 rect.left += 4;
1932 if( !(MenuInfo->dwStyle & MNS_NOCHECK))
1933 rect.left += check_bitmap_width;
1934 rect.right -= check_bitmap_width;
1935 }
1936 else if( lpitem->hbmpItem)
1937 { /* Draw the bitmap */
1938 MenuDrawBitmapItem(hdc, lpitem, &rect, MenuInfo, WndOwner, odaction, menuBar);
1939 }
1940
1941 /* process text if present */
1942 if (lpitem->lpstr)
1943 {
1944 register int i = 0;
1945 HFONT hfontOld = 0;
1946
1947 UINT uFormat = menuBar ?
1948 DT_CENTER | DT_VCENTER | DT_SINGLELINE :
1949 DT_LEFT | DT_VCENTER | DT_SINGLELINE;
1950
1951 if((MenuInfo->dwStyle & MNS_CHECKORBMP))
1952 rect.left += max(0, MenuInfo->cxTextAlign - GetSystemMetrics(SM_CXMENUCHECK));
1953 else
1954 rect.left += MenuInfo->cxTextAlign;
1955
1956 if ( lpitem->fState & MFS_DEFAULT )
1957 {
1958 hfontOld = SelectObject(hdc, hMenuFontBold);
1959 }
1960
1961 if (menuBar) {
1962 rect.left += MENU_BAR_ITEMS_SPACE / 2;
1963 rect.right -= MENU_BAR_ITEMS_SPACE / 2;
1964 }
1965
1966 Text = (PWCHAR) lpitem->dwTypeData;
1967 if(Text)
1968 {
1969 for (i = 0; L'\0' != Text[i]; i++)
1970 if (Text[i] == L'\t' || Text[i] == L'\b')
1971 break;
1972 }
1973
1974 if(lpitem->fState & MF_GRAYED)
1975 {
1976 if (!(lpitem->fState & MF_HILITE) )
1977 {
1978 ++rect.left; ++rect.top; ++rect.right; ++rect.bottom;
1979 SetTextColor(hdc, RGB(0xff, 0xff, 0xff));
1980 DrawTextW( hdc, Text, i, &rect, uFormat );
1981 --rect.left; --rect.top; --rect.right; --rect.bottom;
1982 }
1983 SetTextColor(hdc, RGB(0x80, 0x80, 0x80));
1984 }
1985
1986 DrawTextW( hdc, Text, i, &rect, uFormat);
1987
1988 /* paint the shortcut text */
1989 if (!menuBar && L'\0' != Text[i]) /* There's a tab or flush-right char */
1990 {
1991 if (L'\t' == Text[i])
1992 {
1993 rect.left = lpitem->dxTab;
1994 uFormat = DT_LEFT | DT_VCENTER | DT_SINGLELINE;
1995 }
1996 else
1997 {
1998 rect.right = lpitem->dxTab;
1999 uFormat = DT_RIGHT | DT_VCENTER | DT_SINGLELINE;
2000 }
2001
2002 if (lpitem->fState & MF_GRAYED)
2003 {
2004 if (!(lpitem->fState & MF_HILITE) )
2005 {
2006 ++rect.left; ++rect.top; ++rect.right; ++rect.bottom;
2007 SetTextColor(hdc, RGB(0xff, 0xff, 0xff));
2008 DrawTextW( hdc, Text + i + 1, -1, &rect, uFormat);
2009 --rect.left; --rect.top; --rect.right; --rect.bottom;
2010 }
2011 SetTextColor(hdc, RGB(0x80, 0x80, 0x80));
2012 }
2013 DrawTextW( hdc, Text + i + 1, -1, &rect, uFormat );
2014 }
2015
2016 if (hfontOld)
2017 SelectObject (hdc, hfontOld);
2018 }
2019 }
2020
2021 /***********************************************************************
2022 * MenuDrawPopupMenu
2023 *
2024 * Paint a popup menu.
2025 */
2026 static void FASTCALL MenuDrawPopupMenu(HWND hwnd, HDC hdc, HMENU hmenu )
2027 {
2028 HBRUSH hPrevBrush = 0;
2029 RECT rect;
2030
2031 TRACE("wnd=%p dc=%p menu=%p\n", hwnd, hdc, hmenu);
2032
2033 GetClientRect( hwnd, &rect );
2034
2035 if((hPrevBrush = SelectObject( hdc, GetSysColorBrush(COLOR_MENU) ))
2036 && (SelectObject( hdc, hMenuFont)))
2037 {
2038 HPEN hPrevPen;
2039
2040 Rectangle( hdc, rect.left, rect.top, rect.right, rect.bottom );
2041
2042 hPrevPen = SelectObject( hdc, GetStockObject( NULL_PEN ) );
2043 if ( hPrevPen )
2044 {
2045 BOOL flat_menu = FALSE;
2046 ROSMENUINFO MenuInfo;
2047 ROSMENUITEMINFO ItemInfo;
2048
2049 SystemParametersInfoW (SPI_GETFLATMENU, 0, &flat_menu, 0);
2050 if (flat_menu)
2051 FrameRect(hdc, &rect, GetSysColorBrush(COLOR_BTNSHADOW));
2052 else
2053 DrawEdge (hdc, &rect, EDGE_RAISED, BF_RECT);
2054
2055 /* draw menu items */
2056 if (MenuGetRosMenuInfo(&MenuInfo, hmenu) && MenuInfo.cItems)
2057 {
2058 UINT u;
2059 MenuInitRosMenuItemInfo(&ItemInfo);
2060
2061 for (u = 0; u < MenuInfo.cItems; u++)
2062 {
2063 if (MenuGetRosMenuItemInfo(MenuInfo.Self, u, &ItemInfo))
2064 {
2065 HWND WndOwner = MenuInfo.spwndNotify ? MenuInfo.spwndNotify->head.h : NULL;
2066 MenuDrawMenuItem(hwnd, &MenuInfo, WndOwner, hdc, &ItemInfo,
2067 MenuInfo.cyMenu, FALSE, ODA_DRAWENTIRE);
2068 }
2069 }
2070
2071 /* draw scroll arrows */
2072 if (MenuInfo.dwArrowsOn)
2073 MENU_DrawScrollArrows(&MenuInfo, hdc);
2074
2075 MenuSetRosMenuInfo(&MenuInfo);
2076 MenuCleanupRosMenuItemInfo(&ItemInfo);
2077 }
2078 } else
2079 {
2080 SelectObject( hdc, hPrevBrush );
2081 }
2082 }
2083 }
2084
2085 /***********************************************************************
2086 * MenuDrawMenuBar
2087 *
2088 * Paint a menu bar. Returns the height of the menu bar.
2089 * called from [windows/nonclient.c]
2090 */
2091 UINT MenuDrawMenuBar( HDC hDC, LPRECT lprect, HWND hwnd,
2092 BOOL suppress_draw)
2093 {
2094 ROSMENUINFO lppop;
2095 HFONT hfontOld = 0;
2096 HMENU hMenu = GetMenu(hwnd);
2097
2098 if (! MenuGetRosMenuInfo(&lppop, hMenu) || lprect == NULL)
2099 {
2100 return GetSystemMetrics(SM_CYMENU);
2101 }
2102
2103 if (suppress_draw)
2104 {
2105 hfontOld = SelectObject(hDC, hMenuFont);
2106
2107 MenuMenuBarCalcSize(hDC, lprect, &lppop, hwnd);
2108
2109 lprect->bottom = lprect->top + lppop.cyMenu;
2110
2111 if (hfontOld) SelectObject( hDC, hfontOld);
2112 return lppop.cyMenu;
2113 }
2114 else
2115 return DrawMenuBarTemp(hwnd, hDC, lprect, hMenu, NULL);
2116 }
2117
2118
2119 /***********************************************************************
2120 * MenuShowPopup
2121 *
2122 * Display a popup menu.
2123 */
2124 static BOOL FASTCALL MenuShowPopup(HWND hwndOwner, HMENU hmenu, UINT id, UINT flags,
2125 INT x, INT y, INT xanchor, INT yanchor )
2126 {
2127 ROSMENUINFO MenuInfo;
2128 ROSMENUITEMINFO ItemInfo;
2129 UINT width, height;
2130 POINT pt;
2131 HMONITOR monitor;
2132 MONITORINFO info;
2133 DWORD ex_style = 0;
2134
2135 TRACE("owner=%p hmenu=%p id=0x%04x x=0x%04x y=0x%04x xa=0x%04x ya=0x%04x\n",
2136 hwndOwner, hmenu, id, x, y, xanchor, yanchor);
2137
2138 if (! MenuGetRosMenuInfo(&MenuInfo, hmenu)) return FALSE;
2139 if (MenuInfo.iItem != NO_SELECTED_ITEM)
2140 {
2141 MenuInitRosMenuItemInfo(&ItemInfo);
2142 if (MenuGetRosMenuItemInfo(MenuInfo.Self, MenuInfo.iItem, &ItemInfo))
2143 {
2144 ItemInfo.fMask |= MIIM_STATE;
2145 ItemInfo.fState &= ~(MF_HILITE|MF_MOUSESELECT);
2146 MenuSetRosMenuItemInfo(MenuInfo.Self, MenuInfo.iItem, &ItemInfo);
2147 }
2148 MenuCleanupRosMenuItemInfo(&ItemInfo);
2149 MenuInfo.iItem = NO_SELECTED_ITEM;
2150 }
2151
2152 /* store the owner for DrawItem */
2153 if (!IsWindow(hwndOwner))
2154 {
2155 SetLastError( ERROR_INVALID_WINDOW_HANDLE );
2156 return FALSE;
2157 }
2158 MenuInfo.spwndNotify = ValidateHwndNoErr(hwndOwner);
2159 MenuSetRosMenuInfo(&MenuInfo);
2160
2161 MenuPopupMenuCalcSize(&MenuInfo, hwndOwner);
2162
2163 /* adjust popup menu pos so that it fits within the desktop */
2164
2165 width = MenuInfo.cxMenu + GetSystemMetrics(SM_CXBORDER);
2166 height = MenuInfo.cyMenu + GetSystemMetrics(SM_CYBORDER);
2167
2168 /* FIXME: should use item rect */
2169 pt.x = x;
2170 pt.y = y;
2171 monitor = MonitorFromPoint( pt, MONITOR_DEFAULTTONEAREST );
2172 info.cbSize = sizeof(info);
2173 GetMonitorInfoW( monitor, &info );
2174
2175 if (flags & TPM_LAYOUTRTL)
2176 {
2177 ex_style = WS_EX_LAYOUTRTL;
2178 flags ^= TPM_RIGHTALIGN;
2179 }
2180 if( flags & TPM_RIGHTALIGN ) x -= width;
2181 if( flags & TPM_CENTERALIGN ) x -= width / 2;
2182
2183 if( flags & TPM_BOTTOMALIGN ) y -= height;
2184 if( flags & TPM_VCENTERALIGN ) y -= height / 2;
2185
2186 if( x + width > info.rcMonitor.right)
2187 {
2188 if( xanchor && x >= width - xanchor )
2189 x -= width - xanchor;
2190
2191 if( x + width > info.rcMonitor.right)
2192 x = info.rcMonitor.right - width;
2193 }
2194 if( x < info.rcMonitor.left ) x = info.rcMonitor.left;
2195
2196 if( y + height > info.rcMonitor.bottom)
2197 {
2198 if( yanchor && y >= height + yanchor )
2199 y -= height + yanchor;
2200
2201 if( y + height > info.rcMonitor.bottom)
2202 y = info.rcMonitor.bottom - height;
2203 }
2204 if( y < info.rcMonitor.top ) y = info.rcMonitor.top;
2205
2206 /* NOTE: In Windows, top menu popup is not owned. */
2207 MenuInfo.Wnd = CreateWindowExW( ex_style, WC_MENU, NULL,
2208 WS_POPUP, x, y, width, height,
2209 hwndOwner, 0, (HINSTANCE) GetWindowLongPtrW(hwndOwner, GWLP_HINSTANCE),
2210 (LPVOID) MenuInfo.Self);
2211 if ( !MenuInfo.Wnd || ! MenuSetRosMenuInfo(&MenuInfo)) return FALSE;
2212 if (!top_popup) {
2213 top_popup = MenuInfo.Wnd;
2214 top_popup_hmenu = hmenu;
2215 }
2216
2217 IntNotifyWinEvent(EVENT_SYSTEM_MENUPOPUPSTART, MenuInfo.Wnd, OBJID_CLIENT, CHILDID_SELF, 0);
2218
2219 /* Display the window */
2220
2221 SetWindowPos( MenuInfo.Wnd, HWND_TOPMOST, 0, 0, 0, 0,
2222 SWP_SHOWWINDOW | SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE);
2223 UpdateWindow( MenuInfo.Wnd );
2224 return TRUE;
2225 }
2226
2227 /***********************************************************************
2228 * MENU_EnsureMenuItemVisible
2229 */
2230 void
2231 MENU_EnsureMenuItemVisible(PROSMENUINFO lppop, PROSMENUITEMINFO item, HDC hdc)
2232 {
2233 if (lppop->dwArrowsOn)
2234 {
2235 //ITEM *item = &lppop->items[wIndex];
2236 UINT nMaxHeight = MENU_GetMaxPopupHeight(lppop);
2237 UINT nOldPos = lppop->iTop;
2238 RECT rc;
2239 UINT arrow_bitmap_height;
2240 BITMAP bmp;
2241
2242 GetClientRect(lppop->Wnd, &rc);
2243
2244 GetObjectW(get_down_arrow_bitmap(), sizeof(bmp), &bmp);
2245 arrow_bitmap_height = bmp.bmHeight;
2246
2247 rc.top += arrow_bitmap_height;
2248 rc.bottom -= arrow_bitmap_height + MENU_BOTTOM_MARGIN;
2249
2250 nMaxHeight -= GetSystemMetrics(SM_CYBORDER) + 2 * arrow_bitmap_height;
2251 if (item->Rect.bottom > lppop->iTop + nMaxHeight)
2252 {
2253 lppop->iTop = item->Rect.bottom - nMaxHeight;
2254 ScrollWindow(lppop->Wnd, 0, nOldPos - lppop->iTop, &rc, &rc);
2255 MENU_DrawScrollArrows(lppop, hdc);
2256 }
2257 else if (item->Rect.top - MENU_TOP_MARGIN < lppop->iTop)
2258 {
2259 lppop->iTop = item->Rect.top - MENU_TOP_MARGIN;
2260 ScrollWindow(lppop->Wnd, 0, nOldPos - lppop->iTop, &rc, &rc);
2261 MENU_DrawScrollArrows(lppop, hdc);
2262 }
2263 }
2264 }
2265
2266 /***********************************************************************
2267 * MenuSelectItem
2268 */
2269 static void FASTCALL MenuSelectItem(HWND hwndOwner, PROSMENUINFO hmenu, UINT wIndex,
2270 BOOL sendMenuSelect, HMENU topmenu)
2271 {
2272 ROSMENUITEMINFO ItemInfo;
2273 ROSMENUINFO TopMenuInfo;
2274 HDC hdc;
2275
2276 TRACE("owner=%p menu=%p index=0x%04x select=0x%04x\n", hwndOwner, hmenu, wIndex, sendMenuSelect);
2277
2278 if (!hmenu || !hmenu->cItems || !hmenu->Wnd) return;
2279 if (hmenu->iItem == wIndex) return;
2280 if (hmenu->fFlags & MNF_POPUP) hdc = GetDC(hmenu->Wnd);
2281 else hdc = GetDCEx(hmenu->Wnd, 0, DCX_CACHE | DCX_WINDOW);
2282 if (!top_popup) {
2283 top_popup = hmenu->Wnd;
2284 top_popup_hmenu = hmenu->Self;
2285 }
2286
2287 SelectObject( hdc, hMenuFont );
2288
2289 MenuInitRosMenuItemInfo(&ItemInfo);
2290
2291 /* Clear previous highlighted item */
2292 if (hmenu->iItem != NO_SELECTED_ITEM)
2293 {
2294 if (MenuGetRosMenuItemInfo(hmenu->Self, hmenu->iItem, &ItemInfo))
2295 {
2296 ItemInfo.fMask |= MIIM_STATE;
2297 ItemInfo.fState &= ~(MF_HILITE|MF_MOUSESELECT);
2298 MenuSetRosMenuItemInfo(hmenu->Self, hmenu->iItem, &ItemInfo);
2299 }
2300 //MENU_EnsureMenuItemVisible(hmenu, &ItemInfo, hdc);
2301 MenuDrawMenuItem(hmenu->Wnd, hmenu, hwndOwner, hdc, &ItemInfo,
2302 hmenu->cyMenu, !(hmenu->fFlags & MNF_POPUP),
2303 ODA_SELECT);
2304 }
2305
2306 /* Highlight new item (if any) */
2307 hmenu->iItem = wIndex;
2308 MenuSetRosMenuInfo(hmenu);
2309 if (hmenu->iItem != NO_SELECTED_ITEM)
2310 {
2311 if (MenuGetRosMenuItemInfo(hmenu->Self, hmenu->iItem, &ItemInfo))
2312 {
2313 if (!(ItemInfo.fType & MF_SEPARATOR))
2314 {
2315 ItemInfo.fMask |= MIIM_STATE;
2316 ItemInfo.fState |= MF_HILITE;
2317 MenuSetRosMenuItemInfo(hmenu->Self, hmenu->iItem, &ItemInfo);
2318 MenuDrawMenuItem(hmenu->Wnd, hmenu, hwndOwner, hdc,
2319 &ItemInfo, hmenu->cyMenu, !(hmenu->fFlags & MNF_POPUP),
2320 ODA_SELECT);
2321 }
2322 if (sendMenuSelect)
2323 {
2324 WPARAM wParam = MAKEWPARAM( ItemInfo.hSubMenu ? wIndex : ItemInfo.wID,
2325 ItemInfo.fType | ItemInfo.fState |
2326 (ItemInfo.hSubMenu ? MF_POPUP : 0) |
2327 (hmenu->fFlags & MNF_SYSDESKMN ? MF_SYSMENU : 0 ) );
2328
2329 SendMessageW(hwndOwner, WM_MENUSELECT, wParam, (LPARAM) hmenu->Self);
2330 }
2331 }
2332 }
2333 else if (sendMenuSelect)
2334 {
2335 if(topmenu)
2336 {
2337 int pos;
2338 pos = MenuFindSubMenu(&topmenu, hmenu->Self);
2339 if (pos != NO_SELECTED_ITEM)
2340 {
2341 if (MenuGetRosMenuInfo(&TopMenuInfo, topmenu)
2342 && MenuGetRosMenuItemInfo(topmenu, pos, &ItemInfo))
2343 {
2344 WPARAM wParam = MAKEWPARAM( Pos, ItemInfo.fType | ItemInfo.fState |
2345 (ItemInfo.hSubMenu ? MF_POPUP : 0) |
2346 (TopMenuInfo.fFlags & MNF_SYSDESKMN ? MF_SYSMENU : 0 ) );
2347
2348 SendMessageW(hwndOwner, WM_MENUSELECT, wParam, (LPARAM) topmenu);
2349 }
2350 }
2351 }
2352 }
2353 MenuCleanupRosMenuItemInfo(&ItemInfo);
2354 ReleaseDC(hmenu->Wnd, hdc);
2355 }
2356
2357 /***********************************************************************
2358 * MenuMoveSelection
2359 *
2360 * Moves currently selected item according to the Offset parameter.
2361 * If there is no selection then it should select the last item if
2362 * Offset is ITEM_PREV or the first item if Offset is ITEM_NEXT.
2363 */
2364 static void FASTCALL
2365 MenuMoveSelection(HWND WndOwner, PROSMENUINFO MenuInfo, INT Offset)
2366 {
2367 INT i;
2368 ROSMENUITEMINFO ItemInfo;
2369 INT OrigPos;
2370
2371 TRACE("hwnd=%x menu=%x off=0x%04x\n", WndOwner, MenuInfo, Offset);
2372
2373 /* Prevent looping */
2374 if (0 == MenuInfo->cItems || 0 == Offset)
2375 return;
2376 else if (Offset < -1)
2377 Offset = -1;
2378 else if (Offset > 1)
2379 Offset = 1;
2380
2381 MenuInitRosMenuItemInfo(&ItemInfo);
2382
2383 OrigPos = MenuInfo->iItem;
2384 if (OrigPos == NO_SELECTED_ITEM) /* NO_SELECTED_ITEM is not -1 ! */
2385 {
2386 OrigPos = 0;
2387 i = -1;
2388 }
2389 else
2390 {
2391 i = MenuInfo->iItem;
2392 }
2393
2394 do
2395 {
2396 /* Step */
2397 i += Offset;
2398 /* Clip and wrap around */
2399 if (i < 0)
2400 {
2401 i = MenuInfo->cItems - 1;
2402 }
2403 else if (i >= MenuInfo->cItems)
2404 {
2405 i = 0;
2406 }
2407 /* If this is a good candidate; */
2408 if (MenuGetRosMenuItemInfo(MenuInfo->Self, i, &ItemInfo) &&
2409 0 == (ItemInfo.fType & MF_SEPARATOR))
2410 {
2411 MenuSelectItem(WndOwner, MenuInfo, i, TRUE, NULL);
2412 MenuCleanupRosMenuItemInfo(&ItemInfo);
2413 return;
2414 }
2415 } while (i != OrigPos);
2416
2417 /* Not found */
2418 MenuCleanupRosMenuItemInfo(&ItemInfo);
2419 }
2420
2421 #if 0
2422 LRESULT WINAPI
2423 PopupMenuWndProcW(HWND Wnd, UINT Message, WPARAM wParam, LPARAM lParam)
2424 {
2425 #ifdef __REACTOS__ // Do this now, remove after Server side is fixed.
2426 PWND pWnd;
2427 PPOPUPMENU pPopupMenu;
2428
2429 pWnd = ValidateHwnd(Wnd);
2430 if (pWnd)
2431 {
2432 if (!pWnd->fnid)
2433 {
2434 if (Message != WM_NCCREATE)
2435 {
2436 return DefWindowProcW(Wnd, Message, wParam, lParam);
2437 }
2438 NtUserSetWindowFNID(Wnd, FNID_MENU);
2439 pPopupMenu = HeapAlloc( GetProcessHeap(), 0, sizeof(POPUPMENU) );
2440 pPopupMenu->spwndPopupMenu = pWnd;
2441 SetWindowLongPtrW(Wnd, 0, (LONG_PTR)pPopupMenu);
2442 }
2443 else
2444 {
2445 if (pWnd->fnid != FNID_MENU)
2446 {
2447 ERR("Wrong window class for Menu!\n");
2448 return 0;
2449 }
2450 pPopupMenu = ((PMENUWND)pWnd)->ppopupmenu;
2451 }
2452 }
2453 #endif
2454
2455 TRACE("hwnd=%x msg=0x%04x wp=0x%04lx lp=0x%08lx\n", Wnd, Message, wParam, lParam);
2456
2457 switch(Message)
2458 {
2459 case WM_CREATE:
2460 {
2461 CREATESTRUCTW *cs = (CREATESTRUCTW *) lParam;
2462 pPopupMenu->spmenu = ValidateHandle(cs->lpCreateParams, TYPE_MENU);
2463 return 0;
2464 }
2465
2466 case WM_MOUSEACTIVATE: /* We don't want to be activated */
2467 return MA_NOACTIVATE;
2468
2469 case WM_PAINT:
2470 {
2471 PAINTSTRUCT ps;
2472 BeginPaint(Wnd, &ps);
2473 MenuDrawPopupMenu(Wnd, ps.hdc, pPopupMenu->spmenu->head.h);
2474 EndPaint(Wnd, &ps);
2475 return 0;
2476 }
2477
2478 case WM_PRINTCLIENT:
2479 {
2480 MenuDrawPopupMenu( Wnd, (HDC)wParam, pPopupMenu->spmenu->head.h);
2481 return 0;
2482 }
2483
2484 case WM_ERASEBKGND:
2485 return 1;
2486
2487 case WM_DESTROY:
2488 /* zero out global pointer in case resident popup window was destroyed. */
2489 if (Wnd == top_popup)
2490 {
2491 top_popup = NULL;
2492 top_popup_hmenu = NULL;
2493 }
2494 break;
2495
2496 case WM_NCDESTROY:
2497 {
2498 HeapFree( GetProcessHeap(), 0, pPopupMenu );
2499 SetWindowLongPtrW(Wnd, 0, 0);
2500 NtUserSetWindowFNID(Wnd, FNID_DESTROY);
2501 break;
2502 }
2503
2504 case WM_SHOWWINDOW:
2505 if (0 != wParam)
2506 {
2507 if (!pPopupMenu || !pPopupMenu->spmenu)
2508 {
2509 OutputDebugStringA("no menu to display\n");
2510 }
2511 }
2512 /*else
2513 {
2514 pPopupMenu->spmenu = NULL; ///// WTF?
2515 }*/
2516 break;
2517
2518 case MM_SETMENUHANDLE:
2519 {
2520 PMENU pmenu = ValidateHandle((HMENU)wParam, TYPE_MENU);
2521 if (!pmenu)
2522 {
2523 ERR("Bad Menu Handle\n");
2524 break;
2525 }
2526 pPopupMenu->spmenu = pmenu;
2527 break;
2528 }
2529
2530 case MM_GETMENUHANDLE:
2531 case MN_GETHMENU:
2532 return (LRESULT)(pPopupMenu ? (pPopupMenu->spmenu ? pPopupMenu->spmenu->head.h : NULL) : NULL);
2533
2534 default:
2535 return DefWindowProcW(Wnd, Message, wParam, lParam);
2536 }
2537
2538 return 0;
2539 }
2540 #endif
2541
2542 LRESULT WINAPI
2543 PopupMenuWndProcW(HWND Wnd, UINT Message, WPARAM wParam, LPARAM lParam)
2544 {
2545 #ifdef __REACTOS__ // Do this now, remove after Server side is fixed.
2546 PWND pWnd;
2547
2548 pWnd = ValidateHwnd(Wnd);
2549 if (pWnd)
2550 {
2551 if (!pWnd->fnid)
2552 {
2553 if (Message != WM_NCCREATE)
2554 {
2555 return DefWindowProcW(Wnd, Message, wParam, lParam);
2556 }
2557 NtUserSetWindowFNID(Wnd, FNID_MENU);
2558 }
2559 else
2560 {
2561 if (pWnd->fnid != FNID_MENU)
2562 {
2563 ERR("Wrong window class for Menu!\n");
2564 return 0;
2565 }
2566 }
2567 }
2568 #endif
2569
2570 TRACE("hwnd=%x msg=0x%04x wp=0x%04lx lp=0x%08lx\n", Wnd, Message, wParam, lParam);
2571
2572 switch(Message)
2573 {
2574 case WM_CREATE:
2575 {
2576 CREATESTRUCTW *cs = (CREATESTRUCTW *) lParam;
2577 SetWindowLongPtrW(Wnd, 0, (LONG_PTR)cs->lpCreateParams);
2578 return 0;
2579 }
2580
2581 case WM_MOUSEACTIVATE: /* We don't want to be activated */
2582 return MA_NOACTIVATE;
2583
2584 case WM_PAINT:
2585 {
2586 PAINTSTRUCT ps;
2587 BeginPaint(Wnd, &ps);
2588 MenuDrawPopupMenu(Wnd, ps.hdc, (HMENU)GetWindowLongPtrW(Wnd, 0));
2589 EndPaint(Wnd, &ps);
2590 return 0;
2591 }
2592
2593 case WM_PRINTCLIENT:
2594 {
2595 MenuDrawPopupMenu( Wnd, (HDC)wParam,
2596 (HMENU)GetWindowLongPtrW( Wnd, 0 ) );
2597 return 0;
2598 }
2599
2600 case WM_ERASEBKGND:
2601 return 1;
2602
2603 case WM_DESTROY:
2604 /* zero out global pointer in case resident popup window was destroyed. */
2605 if (Wnd == top_popup)
2606 {
2607 top_popup = NULL;
2608 top_popup_hmenu = NULL;
2609 }
2610 break;
2611
2612 case WM_SHOWWINDOW:
2613 if (0 != wParam)
2614 {
2615 if (0 == GetWindowLongPtrW(Wnd, 0))
2616 {
2617 OutputDebugStringA("no menu to display\n");
2618 }
2619 }
2620 else
2621 {
2622 SetWindowLongPtrW(Wnd, 0, 0);
2623 }
2624 break;
2625
2626 case MM_SETMENUHANDLE:
2627 SetWindowLongPtrW(Wnd, 0, wParam);
2628 break;
2629
2630 case MM_GETMENUHANDLE:
2631 case MN_GETHMENU:
2632 return GetWindowLongPtrW(Wnd, 0);
2633
2634 default:
2635 return DefWindowProcW(Wnd, Message, wParam, lParam);
2636 }
2637
2638 return 0;
2639 }
2640
2641 //
2642 // This breaks some test results. Should handle A2U if called!
2643 //
2644 LRESULT WINAPI PopupMenuWndProcA(HWND Wnd, UINT Message, WPARAM wParam, LPARAM lParam)
2645 {
2646 PWND pWnd;
2647
2648 pWnd = ValidateHwnd(Wnd);
2649 if (pWnd && !pWnd->fnid && Message != WM_NCCREATE)
2650 {
2651 return DefWindowProcA(Wnd, Message, wParam, lParam);
2652 }
2653 TRACE("YES! hwnd=%x msg=0x%04x wp=0x%04lx lp=0x%08lx\n", Wnd, Message, wParam, lParam);
2654
2655 switch(Message)
2656 {
2657 case WM_NCCREATE:
2658 case WM_CREATE:
2659 case WM_MOUSEACTIVATE:
2660 case WM_PAINT:
2661 case WM_PRINTCLIENT:
2662 case WM_ERASEBKGND:
2663 case WM_DESTROY:
2664 case WM_NCDESTROY:
2665 case WM_SHOWWINDOW:
2666 case MM_SETMENUHANDLE:
2667 case MM_GETMENUHANDLE:
2668 case MN_GETHMENU:
2669 return PopupMenuWndProcW(Wnd, Message, wParam, lParam);
2670
2671 default:
2672 return DefWindowProcA(Wnd, Message, wParam, lParam);
2673 }
2674 return 0;
2675 }
2676
2677 /**********************************************************************
2678 * MENU_ParseResource
2679 *
2680 * Parse a standard menu resource and add items to the menu.
2681 * Return a pointer to the end of the resource.
2682 *
2683 * NOTE: flags is equivalent to the mtOption field
2684 */
2685 static LPCSTR MENU_ParseResource( LPCSTR res, HMENU hMenu)
2686 {
2687 WORD flags, id = 0;
2688 HMENU hSubMenu;
2689 LPCWSTR str;
2690 BOOL end = FALSE;
2691
2692 do
2693 {
2694 flags = GET_WORD(res);
2695
2696 /* remove MF_END flag before passing it to AppendMenu()! */
2697 end = (flags & MF_END);
2698 if(end) flags ^= MF_END;
2699
2700 res += sizeof(WORD);
2701 if(!(flags & MF_POPUP))
2702 {
2703 id = GET_WORD(res);
2704 res += sizeof(WORD);
2705 }
2706 str = (LPCWSTR)res;
2707 res += (strlenW(str) + 1) * sizeof(WCHAR);
2708
2709 if (flags & MF_POPUP)
2710 {
2711 hSubMenu = CreatePopupMenu();
2712 if(!hSubMenu) return NULL;
2713 if(!(res = MENU_ParseResource(res, hSubMenu))) return NULL;
2714 AppendMenuW(hMenu, flags, (UINT_PTR)hSubMenu, (LPCWSTR)str);
2715 }
2716 else /* Not a popup */
2717 {
2718 AppendMenuW(hMenu, flags, id, *(LPCWSTR)str ? (LPCWSTR)str : NULL);
2719 }
2720 } while(!end);
2721 return res;
2722 }
2723
2724
2725 /**********************************************************************
2726 * MENUEX_ParseResource
2727 *
2728 * Parse an extended menu resource and add items to the menu.
2729 * Return a pointer to the end of the resource.
2730 */
2731 static LPCSTR MENUEX_ParseResource(LPCSTR res, HMENU hMenu)
2732 {
2733 WORD resinfo;
2734 do
2735 {
2736 MENUITEMINFOW mii;
2737
2738 mii.cbSize = sizeof(mii);
2739 mii.fMask = MIIM_STATE | MIIM_ID | MIIM_TYPE;
2740 mii.fType = GET_DWORD(res);
2741 res += sizeof(DWORD);
2742 mii.fState = GET_DWORD(res);
2743 res += sizeof(DWORD);
2744 mii.wID = GET_DWORD(res);
2745 res += sizeof(DWORD);
2746 resinfo = GET_WORD(res);
2747 res += sizeof(WORD);
2748 /* Align the text on a word boundary. */
2749 res += (~((UINT_PTR)res - 1)) & 1;
2750 mii.dwTypeData = (LPWSTR)res;
2751 mii.cch = strlenW(mii.dwTypeData);
2752 res += (1 + strlenW(mii.dwTypeData)) * sizeof(WCHAR);
2753 /* Align the following fields on a dword boundary. */
2754 res += (~((UINT_PTR)res - 1)) & 3;
2755
2756 TRACE("Menu item: [%08x,%08x,%04x,%04x,%S]\n",
2757 mii.fType, mii.fState, mii.wID, resinfo, mii.dwTypeData);
2758
2759 if (resinfo & 1) /* Pop-up? */
2760 {
2761 /* DWORD helpid = GET_DWORD(res); FIXME: use this. */
2762 res += sizeof(DWORD);
2763 mii.hSubMenu = CreatePopupMenu();
2764 if (!mii.hSubMenu)
2765 {
2766 ERR("CreatePopupMenu failed\n");
2767 return NULL;
2768 }
2769
2770 if (!(res = MENUEX_ParseResource(res, mii.hSubMenu)))
2771 {
2772 ERR("MENUEX_ParseResource failed\n");
2773 DestroyMenu(mii.hSubMenu);
2774 return NULL;
2775 }
2776 mii.fMask |= MIIM_SUBMENU;
2777 }
2778 else if (!mii.dwTypeData[0] && !(mii.fType & MF_SEPARATOR))
2779 {
2780 mii.fType |= MF_SEPARATOR;
2781 }
2782 InsertMenuItemW(hMenu, -1, MF_BYPOSITION, &mii);
2783 } while (!(resinfo & MF_END));
2784 return res;
2785 }
2786
2787 NTSTATUS WINAPI
2788 User32LoadSysMenuTemplateForKernel(PVOID Arguments, ULONG ArgumentLength)
2789 {
2790 HMENU hmenu = LoadMenuW(User32Instance, L"SYSMENU");
2791 LRESULT Result = (LRESULT)hmenu;
2792 MENUINFO menuinfo = {0};
2793 MENUITEMINFOW info = {0};
2794
2795 // removing space for checkboxes from menu
2796 menuinfo.cbSize = sizeof(menuinfo);
2797 menuinfo.fMask = MIM_STYLE;
2798 GetMenuInfo(hmenu, &menuinfo);
2799 menuinfo.dwStyle |= MNS_CHECKORBMP; // test_menu_bmp_and_string MNS_CHECKORBMP
2800 SetMenuInfo(hmenu, &menuinfo);
2801
2802 // adding bitmaps to menu items
2803 info.cbSize = sizeof(info);
2804 info.fMask |= MIIM_BITMAP;
2805 info.hbmpItem = HBMMENU_POPUP_MINIMIZE;
2806 SetMenuItemInfoW(hmenu, SC_MINIMIZE, FALSE, &info);
2807 info.hbmpItem = HBMMENU_POPUP_RESTORE;
2808 SetMenuItemInfoW(hmenu, SC_RESTORE, FALSE, &info);
2809 info.hbmpItem = HBMMENU_POPUP_MAXIMIZE;
2810 SetMenuItemInfoW(hmenu, SC_MAXIMIZE, FALSE, &info);
2811 info.hbmpItem = HBMMENU_POPUP_CLOSE;
2812 SetMenuItemInfoW(hmenu, SC_CLOSE, FALSE, &info);
2813
2814 return(ZwCallbackReturn(&Result, sizeof(LRESULT), STATUS_SUCCESS));
2815 }
2816
2817
2818 BOOL
2819 MenuInit(VOID)
2820 {
2821 NONCLIENTMETRICSW ncm;
2822
2823 /* get the menu font */
2824 if(!hMenuFont || !hMenuFontBold)
2825 {
2826 ncm.cbSize = sizeof(ncm);
2827 if(!SystemParametersInfoW(SPI_GETNONCLIENTMETRICS, sizeof(ncm), &ncm, 0))
2828 {
2829 ERR("MenuInit(): SystemParametersInfoW(SPI_GETNONCLIENTMETRICS) failed!\n");
2830 return FALSE;
2831 }
2832
2833 hMenuFont = CreateFontIndirectW(&ncm.lfMenuFont);
2834 if(hMenuFont == NULL)
2835 {
2836 ERR("MenuInit(): CreateFontIndirectW(hMenuFont) failed!\n");
2837 return FALSE;
2838 }
2839
2840 ncm.lfMenuFont.lfWeight = max(ncm.lfMenuFont.lfWeight + 300, 1000);
2841 hMenuFontBold = CreateFontIndirectW(&ncm.lfMenuFont);
2842 if(hMenuFontBold == NULL)
2843 {
2844 ERR("MenuInit(): CreateFontIndirectW(hMenuFontBold) failed!\n");
2845 DeleteObject(hMenuFont);
2846 hMenuFont = NULL;
2847 return FALSE;
2848 }
2849 }
2850
2851 return TRUE;
2852 }
2853
2854 VOID
2855 MenuCleanup(VOID)
2856 {
2857 if (hMenuFont)
2858 {
2859 DeleteObject(hMenuFont);
2860 hMenuFont = NULL;
2861 }
2862
2863 if (hMenuFontBold)
2864 {
2865 DeleteObject(hMenuFontBold);
2866 hMenuFontBold = NULL;
2867 }
2868 }
2869
2870 /***********************************************************************
2871 * DrawMenuBarTemp (USER32.@)
2872 *
2873 * UNDOCUMENTED !!
2874 *
2875 * called by W98SE desk.cpl Control Panel Applet
2876 *
2877 * Not 100% sure about the param names, but close.
2878 *
2879 * @implemented
2880 */
2881 DWORD WINAPI
2882 DrawMenuBarTemp(HWND Wnd, HDC DC, LPRECT Rect, HMENU Menu, HFONT Font)
2883 {
2884 ROSMENUINFO MenuInfo;
2885 ROSMENUITEMINFO ItemInfo;
2886 UINT i;
2887 HFONT FontOld = NULL;
2888 BOOL flat_menu = FALSE;
2889
2890 SystemParametersInfoW (SPI_GETFLATMENU, 0, &flat_menu, 0);
2891
2892 if (NULL == Menu)
2893 {
2894 Menu = GetMenu(Wnd);
2895 }
2896
2897 if (NULL == Font)
2898 {
2899 Font = hMenuFont;
2900 }
2901
2902 if (NULL == Rect || ! MenuGetRosMenuInfo(&MenuInfo, Menu))
2903 {
2904 return GetSystemMetrics(SM_CYMENU);
2905 }
2906
2907 TRACE("(%x, %x, %p, %x, %x)\n", Wnd, DC, Rect, Menu, Font);
2908
2909 FontOld = SelectObject(DC, Font);
2910
2911 if (0 == MenuInfo.cyMenu)
2912 {
2913 MenuMenuBarCalcSize(DC, Rect, &MenuInfo, Wnd);
2914 }
2915
2916 Rect->bottom = Rect->top + MenuInfo.cyMenu;
2917
2918 FillRect(DC, Rect, GetSysColorBrush(flat_menu ? COLOR_MENUBAR : COLOR_MENU));
2919
2920 SelectObject(DC, GetStockObject(DC_PEN));
2921 SetDCPenColor(DC, GetSysColor(COLOR_3DFACE));
2922 MoveToEx(DC, Rect->left, Rect->bottom - 1, NULL);
2923 LineTo(DC, Rect->right, Rect->bottom - 1);
2924
2925 if (0 == MenuInfo.cItems)
2926 {
2927 SelectObject(DC, FontOld);
2928 return GetSystemMetrics(SM_CYMENU);
2929 }
2930
2931 MenuInitRosMenuItemInfo(&ItemInfo);
2932 for (i = 0; i < MenuInfo.cItems; i++)
2933 {
2934 if (MenuGetRosMenuItemInfo(MenuInfo.Self, i, &ItemInfo))
2935 {
2936 MenuDrawMenuItem(Wnd, &MenuInfo, Wnd, DC, &ItemInfo,
2937 MenuInfo.cyMenu, TRUE, ODA_DRAWENTIRE);
2938 }
2939 }
2940 MenuCleanupRosMenuItemInfo(&ItemInfo);
2941
2942 SelectObject(DC, FontOld);
2943
2944 return MenuInfo.cyMenu;
2945 }
2946 #if 0
2947 static BOOL MENU_InitPopup( HWND hwndOwner, HMENU hmenu, UINT flags )
2948 {
2949 POPUPMENU *menu;
2950 DWORD ex_style = 0;
2951
2952 TRACE("owner=%p hmenu=%p\n", hwndOwner, hmenu);
2953
2954 if (!(menu = MENU_GetMenu( hmenu ))) return FALSE;
2955
2956 /* store the owner for DrawItem */
2957 if (!IsWindow( hwndOwner ))
2958 {
2959 SetLastError( ERROR_INVALID_WINDOW_HANDLE );
2960 return FALSE;
2961 }
2962 menu->hwndOwner = hwndOwner;
2963
2964 if (flags & TPM_LAYOUTRTL)
2965 ex_style = WS_EX_LAYOUTRTL;
2966
2967 /* NOTE: In Windows, top menu popup is not owned. */
2968 menu->hWnd = CreateWindowExW( ex_style, (LPCWSTR)POPUPMENU_CLASS_ATOM, NULL,
2969 WS_POPUP, 0, 0, 0, 0,
2970 hwndOwner, 0, (HINSTANCE)GetWindowLongPtrW(hwndOwner, GWLP_HINSTANCE),
2971 (LPVOID)hmenu );
2972 if( !menu->hWnd ) return FALSE;
2973 return TRUE;
2974 }
2975 #endif
2976 /***********************************************************************
2977 * MenuShowSubPopup
2978 *
2979 * Display the sub-menu of the selected item of this menu.
2980 * Return the handle of the submenu, or menu if no submenu to display.
2981 */
2982 static HMENU FASTCALL
2983 MenuShowSubPopup(HWND WndOwner, PROSMENUINFO MenuInfo, BOOL SelectFirst, UINT Flags)
2984 {
2985 extern void FASTCALL NcGetSysPopupPos(HWND Wnd, RECT *Rect);
2986 RECT Rect;
2987 ROSMENUITEMINFO ItemInfo;
2988 ROSMENUINFO SubMenuInfo;
2989 HDC Dc;
2990 HMENU Ret;
2991
2992 TRACE("owner=%x menu=%p 0x%04x\n", WndOwner, MenuInfo, SelectFirst);
2993
2994 if (NO_SELECTED_ITEM == MenuInfo->iItem)
2995 {
2996 return MenuInfo->Self;
2997 }
2998
2999 MenuInitRosMenuItemInfo(&ItemInfo);
3000 if (! MenuGetRosMenuItemInfo(MenuInfo->Self, MenuInfo->iItem, &ItemInfo))
3001 {
3002 MenuCleanupRosMenuItemInfo(&ItemInfo);
3003 return MenuInfo->Self;
3004 }
3005 if (0 == (ItemInfo.hSubMenu) || 0 != (ItemInfo.fState & (MF_GRAYED | MF_DISABLED)))
3006 {
3007 MenuCleanupRosMenuItemInfo(&ItemInfo);
3008 return MenuInfo->Self;
3009 }
3010
3011 /* message must be sent before using item,
3012 because nearly everything may be changed by the application ! */
3013
3014 /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
3015 if (0 == (Flags & TPM_NONOTIFY))
3016 {
3017 SendMessageW(WndOwner, WM_INITMENUPOPUP, (WPARAM) ItemInfo.hSubMenu,
3018 MAKELPARAM(MenuInfo->iItem, IS_SYSTEM_MENU(MenuInfo)));
3019 }
3020
3021 if (! MenuGetRosMenuItemInfo(MenuInfo->Self, MenuInfo->iItem, &ItemInfo))
3022 {
3023 MenuCleanupRosMenuItemInfo(&ItemInfo);
3024 return MenuInfo->Self;
3025 }
3026 Rect = ItemInfo.Rect;
3027
3028 /* correct item if modified as a reaction to WM_INITMENUPOPUP message */
3029 if (0 == (ItemInfo.fState & MF_HILITE))
3030 {
3031 if (0 != (MenuInfo->fFlags & MNF_POPUP))
3032 {
3033 Dc = GetDC(MenuInfo->Wnd);
3034 }
3035 else
3036 {
3037 Dc = GetDCEx(MenuInfo->Wnd, 0, DCX_CACHE | DCX_WINDOW);
3038 }
3039
3040 SelectObject(Dc, hMenuFont);
3041 ItemInfo.fMask |= MIIM_STATE;
3042 ItemInfo.fState |= MF_HILITE;
3043 MenuSetRosMenuItemInfo(MenuInfo->Self, MenuInfo->iItem, &ItemInfo);
3044 MenuDrawMenuItem(MenuInfo->Wnd, MenuInfo, WndOwner, Dc, &ItemInfo, MenuInfo->cyMenu,
3045 !(MenuInfo->fFlags & MNF_POPUP), ODA_DRAWENTIRE);
3046 ReleaseDC(MenuInfo->Wnd, Dc);
3047 }
3048
3049 if (0 == ItemInfo.Rect.top && 0 == ItemInfo.Rect.left
3050 && 0 == ItemInfo.Rect.bottom && 0 == ItemInfo.Rect.right)
3051 {
3052 ItemInfo.Rect = Rect;
3053 }
3054
3055 ItemInfo.fMask |= MIIM_STATE;
3056 ItemInfo.fState |= MF_MOUSESELECT;
3057 MenuSetRosMenuItemInfo(MenuInfo->Self, MenuInfo->iItem, &ItemInfo);
3058
3059 if (IS_SYSTEM_MENU(MenuInfo))
3060 {
3061 ERR("Right click on window bar and Draw system menu!\n");
3062 MenuInitSysMenuPopup(ItemInfo.hSubMenu, GetWindowLongPtrW(MenuInfo->Wnd, GWL_STYLE),
3063 GetClassLongPtrW(MenuInfo->Wnd, GCL_STYLE), HTSYSMENU);
3064 if (Flags & TPM_LAYOUTRTL) Rect.left;
3065 NcGetSysPopupPos(MenuInfo->Wnd, &Rect);
3066 Rect.top = Rect.bottom;
3067 Rect.right = GetSystemMetrics(SM_CXSIZE);
3068 Rect.bottom = GetSystemMetrics(SM_CYSIZE);
3069 }
3070 else
3071 {
3072 GetWindowRect(MenuInfo->Wnd, &Rect);
3073 if (0 != (MenuInfo->fFlags & MNF_POPUP))
3074 {
3075 RECT rc = ItemInfo.Rect;
3076
3077 MENU_AdjustMenuItemRect(MenuInfo, &rc);
3078
3079 if(Flags & TPM_LAYOUTRTL)
3080 Rect.left += GetSystemMetrics(SM_CXBORDER);
3081 else
3082 Rect.left += ItemInfo.Rect.right- GetSystemMetrics(SM_CXBORDER);
3083 Rect.top += rc.top - MENU_TOP_MARGIN;//3;
3084 Rect.right = rc.left - rc.right + GetSystemMetrics(SM_CXBORDER);
3085 Rect.bottom = rc.top - rc.bottom - MENU_TOP_MARGIN - MENU_BOTTOM_MARGIN/*2*/
3086 - GetSystemMetrics(SM_CYBORDER);
3087 }
3088 else
3089 {
3090 if(Flags & TPM_LAYOUTRTL)
3091 Rect.left += Rect.right - ItemInfo.Rect.left;
3092 else
3093 Rect.left += ItemInfo.Rect.left;
3094 Rect.top += ItemInfo.Rect.bottom;
3095 Rect.right = ItemInfo.Rect.right - ItemInfo.Rect.left;
3096 Rect.bottom = ItemInfo.Rect.bottom - ItemInfo.Rect.top;
3097 }
3098 }
3099
3100 /* use default alignment for submenus */
3101 Flags &= ~(TPM_CENTERALIGN | TPM_RIGHTALIGN | TPM_VCENTERALIGN | TPM_BOTTOMALIGN);
3102
3103 //MENU_InitPopup( WndOwner, ItemInfo.hSubMenu, Flags );
3104
3105 MenuShowPopup(WndOwner, ItemInfo.hSubMenu, MenuInfo->iItem, Flags,
3106 Rect.left, Rect.top, Rect.right, Rect.bottom );
3107 if (SelectFirst && MenuGetRosMenuInfo(&SubMenuInfo, ItemInfo.hSubMenu))
3108 {
3109 MenuMoveSelection(WndOwner, &SubMenuInfo, ITEM_NEXT);
3110 }
3111
3112 Ret = ItemInfo.hSubMenu;
3113 MenuCleanupRosMenuItemInfo(&ItemInfo);
3114
3115 return Ret;
3116 }
3117
3118 /**********************************************************************
3119 * MENU_EndMenu
3120 *
3121 * Calls EndMenu() if the hwnd parameter belongs to the menu owner
3122 *
3123 * Does the (menu stuff) of the default window handling of WM_CANCELMODE
3124 */
3125 void MENU_EndMenu( HWND hwnd )
3126 {
3127 ROSMENUINFO MenuInfo;
3128 BOOL Ret = FALSE;
3129 if (top_popup_hmenu)
3130 Ret = MenuGetRosMenuInfo(&MenuInfo, top_popup_hmenu);
3131 if (Ret && hwnd == (MenuInfo.spwndNotify ? MenuInfo.spwndNotify->head.h : NULL)) EndMenu();
3132 }
3133
3134 /***********************************************************************
3135 * MenuHideSubPopups
3136 *
3137 * Hide the sub-popup menus of this menu.
3138 */
3139 static void FASTCALL
3140 MenuHideSubPopups(HWND WndOwner, PROSMENUINFO MenuInfo,
3141 BOOL SendMenuSelect, UINT wFlags)
3142 {
3143 ROSMENUINFO SubMenuInfo;
3144 ROSMENUITEMINFO ItemInfo;
3145
3146 TRACE("owner=%x menu=%x 0x%04x\n", WndOwner, MenuInfo, SendMenuSelect);
3147
3148 if (NULL != MenuInfo && NULL != top_popup && NO_SELECTED_ITEM != MenuInfo->iItem)
3149 {
3150 MenuInitRosMenuItemInfo(&ItemInfo);
3151 ItemInfo.fMask |= MIIM_FTYPE | MIIM_STATE;
3152 if (! MenuGetRosMenuItemInfo(MenuInfo->Self, MenuInfo->iItem, &ItemInfo)
3153 || 0 == (ItemInfo.hSubMenu)
3154 || 0 == (ItemInfo.fState & MF_MOUSESELECT))
3155 {
3156 MenuCleanupRosMenuItemInfo(&ItemInfo);
3157 return;
3158 }
3159 ItemInfo.fState &= ~MF_MOUSESELECT;
3160 ItemInfo.fMask |= MIIM_STATE;
3161 MenuSetRosMenuItemInfo(MenuInfo->Self, MenuInfo->iItem, &ItemInfo);
3162 if (MenuGetRosMenuInfo(&SubMenuInfo, ItemInfo.hSubMenu))
3163 {
3164 MenuHideSubPopups(WndOwner, &SubMenuInfo, FALSE, wFlags);
3165 MenuSelectItem(WndOwner, &SubMenuInfo, NO_SELECTED_ITEM, SendMenuSelect, NULL);
3166 DestroyWindow(SubMenuInfo.Wnd);
3167 SubMenuInfo.Wnd = NULL;
3168 MenuSetRosMenuInfo(&SubMenuInfo);
3169
3170 if (!(wFlags & TPM_NONOTIFY))
3171 SendMessageW( WndOwner, WM_UNINITMENUPOPUP, (WPARAM)ItemInfo.hSubMenu,
3172 MAKELPARAM(0, IS_SYSTEM_MENU(&SubMenuInfo)) );
3173 }
3174 }
3175 }
3176
3177 /***********************************************************************
3178 * MenuSwitchTracking
3179 *
3180 * Helper function for menu navigation routines.
3181 */
3182 static void FASTCALL
3183 MenuSwitchTracking(MTRACKER* Mt, PROSMENUINFO PtMenuInfo, UINT Index, UINT wFlags)
3184 {
3185 ROSMENUINFO TopMenuInfo;
3186
3187 TRACE("%x menu=%x 0x%04x\n", Mt, PtMenuInfo->Self, Index);
3188
3189 if (MenuGetRosMenuInfo(&TopMenuInfo, Mt->TopMenu) &&
3190 Mt->TopMenu != PtMenuInfo->Self &&
3191 0 == ((PtMenuInfo->fFlags | TopMenuInfo.fFlags) & MNF_POPUP))
3192 {
3193 /* both are top level menus (system and menu-bar) */
3194 MenuHideSubPopups(Mt->OwnerWnd, &TopMenuInfo, FALSE, wFlags);
3195 MenuSelectItem(Mt->OwnerWnd, &TopMenuInfo, NO_SELECTED_ITEM, FALSE, NULL);
3196 Mt->TopMenu = PtMenuInfo->Self;
3197 }
3198 else
3199 {
3200 MenuHideSubPopups(Mt->OwnerWnd, PtMenuInfo, FALSE, wFlags);
3201 }
3202
3203 MenuSelectItem(Mt->OwnerWnd, PtMenuInfo, Index, TRUE, NULL);
3204 }
3205
3206 /***********************************************************************
3207 * MenuExecFocusedItem
3208 *
3209 * Execute a menu item (for instance when user pressed Enter).
3210 * Return the wID of the executed item. Otherwise, -1 indicating
3211 * that no menu item was executed, -2 if a popup is shown;
3212 * Have to receive the flags for the TrackPopupMenu options to avoid
3213 * sending unwanted message.
3214 *
3215 */
3216 static INT FASTCALL
3217 MenuExecFocusedItem(MTRACKER *Mt, PROSMENUINFO MenuInfo, UINT Flags)
3218 {
3219 ROSMENUITEMINFO ItemInfo;
3220 UINT wID;
3221
3222 TRACE("%p menu=%p\n", Mt, MenuInfo);
3223
3224 if (0 == MenuInfo->cItems || NO_SELECTED_ITEM == MenuInfo->iItem)
3225 {
3226 return -1;
3227 }
3228
3229 MenuInitRosMenuItemInfo(&ItemInfo);
3230 if (! MenuGetRosMenuItemInfo(MenuInfo->Self, MenuInfo->iItem, &ItemInfo))
3231 {
3232 MenuCleanupRosMenuItemInfo(&ItemInfo);
3233 return -1;
3234 }
3235
3236 TRACE("%p %08x %p\n", MenuInfo, ItemInfo.wID, ItemInfo.hSubMenu);
3237
3238 if (0 == (ItemInfo.hSubMenu))
3239 {
3240 if (0 == (ItemInfo.fState & (MF_GRAYED | MF_DISABLED))
3241 && 0 == (ItemInfo.fType & MF_SEPARATOR))
3242 {
3243 /* If TPM_RETURNCMD is set you return the id, but
3244 do not send a message to the owner */
3245 if (0 == (Flags & TPM_RETURNCMD))
3246 {
3247 if (0 != (MenuInfo->fFlags & MNF_SYSDESKMN))
3248 {
3249 PostMessageW(Mt->OwnerWnd, WM_SYSCOMMAND, ItemInfo.wID,
3250 MAKELPARAM((SHORT) Mt->Pt.x, (SHORT) Mt->Pt.y));
3251 }
3252 else
3253 {
3254 ROSMENUINFO topmenuI;
3255 BOOL ret = MenuGetRosMenuInfo(&topmenuI, Mt->TopMenu);
3256 DWORD dwStyle = MenuInfo->dwStyle | (ret ? topmenuI.dwStyle : 0);
3257
3258 if (dwStyle & MNS_NOTIFYBYPOS)
3259 PostMessageW(Mt->OwnerWnd, WM_MENUCOMMAND, MenuInfo->iItem, (LPARAM)MenuInfo->Self);
3260 else
3261 PostMessageW(Mt->OwnerWnd, WM_COMMAND, ItemInfo.wID, 0);
3262 }
3263 }
3264 wID = ItemInfo.wID;
3265 MenuCleanupRosMenuItemInfo(&ItemInfo);
3266 return wID;
3267 }
3268 }
3269 else
3270 {
3271 Mt->CurrentMenu = MenuShowSubPopup(Mt->OwnerWnd, MenuInfo, TRUE, Flags);
3272 return -2;
3273 }
3274
3275 return -1;
3276 }
3277
3278 /***********************************************************************
3279 * MenuButtonDown
3280 *
3281 * Return TRUE if we can go on with menu tracking.
3282 */
3283 static BOOL FASTCALL
3284 MenuButtonDown(MTRACKER* Mt, HMENU PtMenu, UINT Flags)
3285 {
3286 int Index;
3287 ROSMENUINFO MenuInfo;
3288 ROSMENUITEMINFO Item;
3289
3290 TRACE("%x PtMenu=%p\n", Mt, PtMenu);
3291
3292 if (NULL != PtMenu)
3293 {
3294 if (! MenuGetRosMenuInfo(&MenuInfo, PtMenu))
3295 {
3296 return FALSE;
3297 }
3298 if (IS_SYSTEM_MENU(&MenuInfo))
3299 {
3300 Index = 0;
3301 }
3302 else
3303 {
3304 Index = NtUserMenuItemFromPoint(Mt->OwnerWnd, PtMenu, Mt->Pt.x, Mt->Pt.y);
3305 }
3306 MenuInitRosMenuItemInfo(&Item);
3307 if (NO_SELECTED_ITEM == Index || ! MenuGetRosMenuItemInfo(PtMenu, Index, &Item))
3308 {
3309 MenuCleanupRosMenuItemInfo(&Item);
3310 return FALSE;
3311 }
3312
3313 if (!(Item.fType & MF_SEPARATOR) &&
3314 !(Item.fState & (MFS_DISABLED | MFS_GRAYED)) )
3315 {
3316 if (MenuInfo.iItem != Index)
3317 {
3318 MenuSwitchTracking(Mt, &MenuInfo, Index, Flags);
3319 }
3320
3321 /* If the popup menu is not already "popped" */
3322 if (0 == (Item.fState & MF_MOUSESELECT))
3323 {
3324 Mt->CurrentMenu = MenuShowSubPopup(Mt->OwnerWnd, &MenuInfo, FALSE, Flags);
3325 }
3326 }
3327
3328 MenuCleanupRosMenuItemInfo(&Item);
3329
3330 return TRUE;
3331 }
3332
3333 /* else the click was on the menu bar, finish the tracking */
3334
3335 return FALSE;
3336 }
3337
3338 /***********************************************************************
3339 * MenuButtonUp
3340 *
3341 * Return the value of MenuExecFocusedItem if
3342 * the selected item was not a popup. Else open the popup.
3343 * A -1 return value indicates that we go on with menu tracking.
3344 *
3345 */
3346 static INT FASTCALL
3347 MenuButtonUp(MTRACKER *Mt, HMENU PtMenu, UINT Flags)
3348 {
3349 INT Id;
3350 ROSMENUINFO MenuInfo;
3351 ROSMENUITEMINFO ItemInfo;
3352
3353 TRACE("%p hmenu=%x\n", Mt, PtMenu);
3354
3355 if (NULL != PtMenu)
3356 {
3357 Id = 0;
3358 if (! MenuGetRosMenuInfo(&MenuInfo, PtMenu))
3359 {
3360 return -1;
3361 }
3362
3363 if (! IS_SYSTEM_MENU(&MenuInfo))
3364 {
3365 Id = NtUserMenuItemFromPoint(Mt->OwnerWnd, MenuInfo.Self, Mt->Pt.x, Mt->Pt.y);
3366 }
3367 MenuInitRosMenuItemInfo(&ItemInfo);
3368 if (0 <= Id && MenuGetRosMenuItemInfo(MenuInfo.Self, Id, &ItemInfo) &&
3369 MenuInfo.iItem == Id)
3370 {
3371 if (0 == (ItemInfo.hSubMenu))
3372 {
3373 INT ExecutedMenuId = MenuExecFocusedItem(Mt, &MenuInfo, Flags);
3374 MenuCleanupRosMenuItemInfo(&ItemInfo);
3375 return (ExecutedMenuId < 0) ? -1 : ExecutedMenuId;
3376 }
3377 MenuCleanupRosMenuItemInfo(&ItemInfo);
3378
3379 /* If we are dealing with the top-level menu */
3380 /* and this is a click on an already "popped" item: */
3381 /* Stop the menu tracking and close the opened submenus */
3382 if (Mt->TopMenu == MenuInfo.Self && MenuInfo.TimeToHide)
3383 {
3384 MenuCleanupRosMenuItemInfo(&ItemInfo);
3385 return 0;
3386 }
3387 }
3388 MenuCleanupRosMenuItemInfo(&ItemInfo);
3389 MenuInfo.TimeToHide = TRUE;
3390 MenuSetRosMenuInfo(&MenuInfo);
3391 }
3392
3393 return -1;
3394 }
3395
3396 /***********************************************************************
3397 * MenuPtMenu
3398 *
3399 * Walks menu chain trying to find a menu pt maps to.
3400 */
3401 static HMENU FASTCALL
3402 MenuPtMenu(HMENU Menu, POINT Pt)
3403 {
3404 extern LRESULT DefWndNCHitTest(HWND hWnd, POINT Point);
3405 ROSMENUINFO MenuInfo;
3406 ROSMENUITEMINFO ItemInfo;
3407 HMENU Ret = NULL;
3408 INT Ht;
3409
3410 if (! MenuGetRosMenuInfo(&MenuInfo, Menu))
3411 {
3412 return NULL;
3413 }
3414
3415 /* try subpopup first (if any) */
3416 if (NO_SELECTED_ITEM != MenuInfo.iItem)
3417 {
3418 MenuInitRosMenuItemInfo(&ItemInfo);
3419 if (MenuGetRosMenuItemInfo(MenuInfo.Self, MenuInfo.iItem, &ItemInfo) &&
3420 0 != (ItemInfo.hSubMenu) &&
3421 0 != (ItemInfo.fState & MF_MOUSESELECT))
3422 {
3423 Ret = MenuPtMenu(ItemInfo.hSubMenu, Pt);
3424 if (NULL != Ret)
3425 {
3426 MenuCleanupRosMenuItemInfo(&ItemInfo);
3427 return Ret;
3428 }
3429 }
3430 MenuCleanupRosMenuItemInfo(&ItemInfo);
3431 }
3432
3433 /* check the current window (avoiding WM_HITTEST) */
3434 Ht = DefWndNCHitTest(MenuInfo.Wnd, Pt);
3435 if (0 != (MenuInfo.fFlags & MNF_POPUP))
3436 {
3437 if (HTNOWHERE != Ht && HTERROR != Ht)
3438 {
3439 Ret = Menu;
3440 }
3441 }
3442 else if (HTSYSMENU == Ht)
3443 {
3444 Ret = NtUserGetSystemMenu(MenuInfo.Wnd, FALSE);
3445 }
3446 else if (HTMENU == Ht)
3447 {
3448 Ret = GetMenu(MenuInfo.Wnd);
3449 }
3450
3451 return Ret;
3452 }
3453
3454 /***********************************************************************
3455 * MenuMouseMove
3456 *
3457 * Return TRUE if we can go on with menu tracking.
3458 */
3459 static BOOL FASTCALL
3460 MenuMouseMove(MTRACKER *Mt, HMENU PtMenu, UINT Flags)
3461 {
3462 INT Index;
3463 ROSMENUINFO MenuInfo;
3464 ROSMENUITEMINFO ItemInfo;
3465
3466 if (NULL != PtMenu)
3467 {
3468 if (! MenuGetRosMenuInfo(&MenuInfo, PtMenu))
3469 {
3470 return TRUE;
3471 }
3472 if (IS_SYSTEM_MENU(&MenuInfo))
3473 {
3474 Index = 0;
3475 }
3476 else
3477 {
3478 Index = NtUserMenuItemFromPoint(Mt->OwnerWnd, PtMenu, Mt->Pt.x, Mt->Pt.y);
3479 }
3480 }
3481 else
3482 {
3483 Index = NO_SELECTED_ITEM;
3484 }
3485
3486 if (NO_SELECTED_ITEM == Index)
3487 {
3488 if (Mt->CurrentMenu == MenuInfo.Self ||
3489 MenuGetRosMenuInfo(&MenuInfo, Mt->CurrentMenu))
3490 {
3491 MenuSelectItem(Mt->OwnerWnd, &MenuInfo, NO_SELECTED_ITEM,
3492 TRUE, Mt->TopMenu);
3493 }
3494 }
3495 else if (MenuInfo.iItem != Index)
3496 {
3497 MenuInitRosMenuItemInfo(&ItemInfo);
3498 if (MenuGetRosMenuItemInfo(MenuInfo.Self, Index, &ItemInfo) &&
3499 !(ItemInfo.fType & MF_SEPARATOR))
3500 {
3501 MenuSwitchTracking(Mt, &MenuInfo, Index, Flags);
3502 if (!(ItemInfo.fState & (MFS_DISABLED | MFS_GRAYED)))
3503 Mt->CurrentMenu = MenuShowSubPopup(Mt->OwnerWnd, &MenuInfo, FALSE, Flags);
3504 }
3505 MenuCleanupRosMenuItemInfo(&ItemInfo);
3506 }
3507
3508 return TRUE;
3509 }
3510
3511 /***********************************************************************
3512 * MenuGetSubPopup
3513 *
3514 * Return the handle of the selected sub-popup menu (if any).
3515 */
3516 static HMENU FASTCALL
3517 MenuGetSubPopup(HMENU Menu)
3518 {
3519 ROSMENUINFO MenuInfo;
3520 ROSMENUITEMINFO ItemInfo;
3521
3522 if (! MenuGetRosMenuInfo(&MenuInfo, Menu)
3523 || NO_SELECTED_ITEM == MenuInfo.iItem)
3524 {
3525 return NULL;
3526 }
3527
3528 MenuInitRosMenuItemInfo(&ItemInfo);
3529 if (! MenuGetRosMenuItemInfo(MenuInfo.Self, MenuInfo.iItem, &ItemInfo))
3530 {
3531 MenuCleanupRosMenuItemInfo(&ItemInfo);
3532 return NULL;
3533 }
3534 if (0 != (ItemInfo.hSubMenu) && 0 != (ItemInfo.fState & MF_MOUSESELECT))
3535 {
3536 MenuCleanupRosMenuItemInfo(&ItemInfo);
3537 return ItemInfo.hSubMenu;
3538 }
3539
3540 MenuCleanupRosMenuItemInfo(&ItemInfo);
3541 return NULL;
3542 }
3543
3544 /***********************************************************************
3545 * MenuDoNextMenu
3546 *
3547 * NOTE: WM_NEXTMENU documented in Win32 is a bit different.
3548 */
3549 static LRESULT FASTCALL
3550 MenuDoNextMenu(MTRACKER* Mt, UINT Vk, UINT wFlags)
3551 {
3552 ROSMENUINFO TopMenuInfo;
3553 ROSMENUINFO MenuInfo;
3554
3555 if (! MenuGetRosMenuInfo(&TopMenuInfo, Mt->TopMenu))
3556 {
3557 return (LRESULT) FALSE;
3558 }
3559
3560 if ((VK_LEFT == Vk && 0 == TopMenuInfo.iItem)
3561 || (VK_RIGHT == Vk && TopMenuInfo.iItem == TopMenuInfo.cItems - 1))
3562 {
3563 MDINEXTMENU NextMenu;
3564 HMENU NewMenu;
3565 HWND NewWnd;
3566 UINT Id = 0;
3567
3568 NextMenu.hmenuIn = (IS_SYSTEM_MENU(&TopMenuInfo)) ? GetSubMenu(Mt->TopMenu, 0) : Mt->TopMenu;
3569 NextMenu.hmenuNext = NULL;
3570 NextMenu.hwndNext = NULL;
3571 SendMessageW(Mt->OwnerWnd, WM_NEXTMENU, Vk, (LPARAM) &NextMenu);
3572
3573 TRACE("%p [%p] -> %p [%p]\n",
3574 Mt->CurrentMenu, Mt->OwnerWnd, NextMenu.hmenuNext, NextMenu.hwndNext );
3575
3576 if (NULL == NextMenu.hmenuNext || NULL == NextMenu.hwndNext)
3577 {
3578 DWORD Style = GetWindowLongPtrW(Mt->OwnerWnd, GWL_STYLE);
3579 NewWnd = Mt->OwnerWnd;
3580 if (IS_SYSTEM_MENU(&TopMenuInfo))
3581 {
3582 /* switch to the menu bar */
3583
3584 if (0 != (Style & WS_CHILD)
3585 || NULL == (NewMenu = GetMenu(NewWnd)))
3586 {
3587 return FALSE;
3588 }
3589
3590 if (VK_LEFT == Vk)
3591 {
3592 if (! MenuGetRosMenuInfo(&MenuInfo, NewMenu))
3593 {
3594 return FALSE;
3595 }
3596 Id = MenuInfo.cItems - 1;
3597 }
3598 }
3599 else if (0 != (Style & WS_SYSMENU))
3600 {
3601 /* switch to the system menu */
3602 NewMenu = NtUserGetSystemMenu(NewWnd, FALSE);
3603 }
3604 else
3605 {
3606 return FALSE;
3607 }
3608 }
3609 else /* application returned a new menu to switch to */
3610 {
3611 NewMenu = NextMenu.hmenuNext;
3612 NewWnd = NextMenu.hwndNext;
3613
3614 if (IsMenu(NewMenu) && IsWindow(NewWnd))
3615 {
3616 DWORD Style = GetWindowLongPtrW(NewWnd, GWL_STYLE);
3617
3618 if (0 != (Style & WS_SYSMENU)
3619 && GetSystemMenu(NewWnd, FALSE) == NewMenu)
3620 {
3621 /* get the real system menu */
3622 NewMenu = NtUserGetSystemMenu(NewWnd, FALSE);
3623 }
3624 else if (0 != (Style & WS_CHILD) || GetMenu(NewWnd) != NewMenu)
3625 {
3626 /* FIXME: Not sure what to do here;
3627 * perhaps try to track NewMenu as a popup? */
3628
3629 WARN(" -- got confused.\n");
3630 return FALSE;
3631 }
3632 }
3633 else
3634 {
3635 return FALSE;
3636 }
3637 }
3638
3639 if (NewMenu != Mt->TopMenu)
3640 {
3641 MenuSelectItem(Mt->OwnerWnd, &TopMenuInfo, NO_SELECTED_ITEM,
3642 FALSE, 0 );
3643 if (Mt->CurrentMenu != Mt->TopMenu)
3644 {
3645 MenuHideSubPopups(Mt->OwnerWnd, &TopMenuInfo, FALSE, wFlags);
3646 }
3647 }
3648
3649 if (NewWnd != Mt->OwnerWnd)
3650 {
3651 Mt->OwnerWnd = NewWnd;
3652 NtUserxSetGUIThreadHandle(MSQ_STATE_MENUOWNER, Mt->OwnerWnd); // 1
3653 SetCapture(Mt->OwnerWnd); // 2
3654 }
3655
3656 Mt->TopMenu = Mt->CurrentMenu = NewMenu; /* all subpopups are hidden */
3657 if (MenuGetRosMenuInfo(&TopMenuInfo, Mt->TopMenu))
3658 {
3659 MenuSelectItem(Mt->OwnerWnd, &TopMenuInfo, Id, TRUE, 0);
3660 }
3661
3662 return TRUE;
3663 }
3664
3665 return FALSE;
3666 }
3667
3668 /***********************************************************************
3669 * MenuSuspendPopup
3670 *
3671 * The idea is not to show the popup if the next input message is
3672 * going to hide it anyway.
3673 */
3674 static BOOL FASTCALL
3675 MenuSuspendPopup(MTRACKER* Mt, UINT uMsg)
3676 {
3677 MSG msg;
3678
3679 msg.hwnd = Mt->OwnerWnd;
3680
3681 PeekMessageW( &msg, 0, uMsg, uMsg, PM_NOYIELD | PM_REMOVE); // ported incorrectly since 8317 GvG
3682 // Mt->TrackFlags |= TF_SKIPREMOVE; // This sends TrackMenu into a loop with arrow keys!!!!
3683
3684 switch( uMsg )
3685 {
3686 case WM_KEYDOWN:
3687 PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_NOREMOVE);
3688 if( msg.message == WM_KEYUP || msg.message == WM_PAINT )
3689 {
3690 PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_REMOVE);
3691 PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_NOREMOVE);
3692 if( msg.message == WM_KEYDOWN &&
3693 (msg.wParam == VK_LEFT || msg.wParam == VK_RIGHT))
3694 {
3695 Mt->TrackFlags |= TF_SUSPENDPOPUP;
3696 return TRUE;
3697 }
3698 }
3699 break;
3700 }
3701 /* failures go through this */
3702 Mt->TrackFlags &= ~TF_SUSPENDPOPUP;
3703 return FALSE;
3704 }
3705
3706 /***********************************************************************
3707 * MenuKeyEscape
3708 *
3709 * Handle a VK_ESCAPE key event in a menu.
3710 */
3711 static BOOL FASTCALL
3712 MenuKeyEscape(MTRACKER *Mt, UINT Flags)
3713 {
3714 BOOL EndMenu = TRUE;
3715 ROSMENUINFO MenuInfo;
3716 HMENU MenuTmp, MenuPrev;
3717
3718 if (Mt->CurrentMenu != Mt->TopMenu)
3719 {
3720 if (MenuGetRosMenuInfo(&MenuInfo, Mt->CurrentMenu)
3721 && 0 != (MenuInfo.fFlags & MNF_POPUP))
3722 {
3723 MenuPrev = MenuTmp = Mt->TopMenu;
3724
3725 /* close topmost popup */
3726 while (MenuTmp != Mt->CurrentMenu)
3727 {
3728 MenuPrev = MenuTmp;
3729 MenuTmp = MenuGetSubPopup(MenuPrev);
3730 }
3731
3732 if (MenuGetRosMenuInfo(&MenuInfo, MenuPrev))
3733 {
3734 MenuHideSubPopups(Mt->OwnerWnd, &MenuInfo, TRUE, Flags);
3735 }
3736 Mt->CurrentMenu = MenuPrev;
3737 EndMenu = FALSE;
3738 }
3739 }
3740
3741 return EndMenu;
3742 }
3743
3744 /***********************************************************************
3745 * MenuKeyLeft
3746 *
3747 * Handle a VK_LEFT key event in a menu.
3748 */
3749 static void FASTCALL
3750 MenuKeyLeft(MTRACKER* Mt, UINT Flags)
3751 {
3752 ROSMENUINFO MenuInfo;
3753 ROSMENUINFO TopMenuInfo;
3754 ROSMENUINFO PrevMenuInfo;
3755 HMENU MenuTmp, MenuPrev;
3756 UINT PrevCol;
3757
3758 MenuPrev = MenuTmp = Mt->TopMenu;
3759
3760 if (! MenuGetRosMenuInfo(&MenuInfo, Mt->CurrentMenu))
3761 {
3762 return;
3763 }
3764
3765 /* Try to move 1 column left (if possible) */
3766 if ( (PrevCol = MenuGetStartOfPrevColumn(&MenuInfo)) != NO_SELECTED_ITEM)
3767 {
3768 if (MenuGetRosMenuInfo(&MenuInfo, Mt->CurrentMenu))
3769 {
3770 MenuSelectItem(Mt->OwnerWnd, &MenuInfo, PrevCol, TRUE, 0);
3771 }
3772 return;
3773 }
3774
3775 /* close topmost popup */
3776 while (MenuTmp != Mt->CurrentMenu)
3777 {
3778 MenuPrev = MenuTmp;
3779 MenuTmp = MenuGetSubPopup(MenuPrev);
3780 }
3781
3782 if (! MenuGetRosMenuInfo(&PrevMenuInfo, MenuPrev))
3783 {
3784 return;
3785 }
3786 MenuHideSubPopups(Mt->OwnerWnd, &PrevMenuInfo, TRUE, Flags);
3787 Mt->CurrentMenu = MenuPrev;
3788
3789 if (! MenuGetRosMenuInfo(&TopMenuInfo, Mt->TopMenu))
3790 {
3791 return;
3792 }
3793 if ((MenuPrev == Mt->TopMenu) && !(TopMenuInfo.fFlags & MNF_POPUP))
3794 {
3795 /* move menu bar selection if no more popups are left */
3796
3797 if (!MenuDoNextMenu(Mt, VK_LEFT, Flags))
3798 {
3799 MenuMoveSelection(Mt->OwnerWnd, &TopMenuInfo, ITEM_PREV);
3800 }
3801
3802 if (MenuPrev != MenuTmp || Mt->TrackFlags & TF_SUSPENDPOPUP)
3803 {
3804 /* A sublevel menu was displayed - display the next one
3805 * unless there is another displacement coming up */
3806
3807 if (! MenuSuspendPopup(Mt, WM_KEYDOWN)
3808 && MenuGetRosMenuInfo(&TopMenuInfo, Mt->TopMenu))
3809 {
3810 Mt->CurrentMenu = MenuShowSubPopup(Mt->OwnerWnd, &TopMenuInfo,
3811 TRUE, Flags);
3812 }
3813 }
3814 }
3815 }
3816
3817 /***********************************************************************
3818 * MenuKeyRight
3819 *
3820 * Handle a VK_RIGHT key event in a menu.
3821 */
3822 static void FASTCALL MenuKeyRight(MTRACKER *Mt, UINT Flags)
3823 {
3824 HMENU hmenutmp;
3825 ROSMENUINFO MenuInfo;
3826 ROSMENUINFO CurrentMenuInfo;
3827 UINT NextCol;
3828
3829 TRACE("MenuKeyRight called, cur %p, top %p.\n",
3830 Mt->CurrentMenu, Mt->TopMenu);
3831
3832 if (! MenuGetRosMenuInfo(&MenuInfo, Mt->TopMenu)) return;
3833 if ((MenuInfo.fFlags & MNF_POPUP) || (Mt->CurrentMenu != Mt->TopMenu))
3834 {
3835 /* If already displaying a popup, try to display sub-popup */
3836
3837 hmenutmp = Mt->CurrentMenu;
3838 if (MenuGetRosMenuInfo(&CurrentMenuInfo, Mt->CurrentMenu))
3839 {
3840 Mt->CurrentMenu = MenuShowSubPopup(Mt->OwnerWnd, &CurrentMenuInfo, TRUE, Flags);
3841 }
3842
3843 /* if subpopup was displayed then we are done */
3844 if (hmenutmp != Mt->CurrentMenu) return;
3845 }
3846
3847 if (! MenuGetRosMenuInfo(&CurrentMenuInfo, Mt->CurrentMenu))
3848 {
3849 return;
3850 }
3851
3852 /* Check to see if there's another column */
3853 if ( (NextCol = MenuGetStartOfNextColumn(&CurrentMenuInfo)) != NO_SELECTED_ITEM)
3854 {
3855 TRACE("Going to %d.\n", NextCol);
3856 if (MenuGetRosMenuInfo(&MenuInfo, Mt->CurrentMenu))
3857 {
3858 MenuSelectItem(Mt->OwnerWnd, &MenuInfo, NextCol, TRUE, 0);
3859 }
3860 return;
3861 }
3862
3863 if (!(MenuInfo.fFlags & MNF_POPUP)) /* menu bar tracking */
3864 {
3865 if (Mt->CurrentMenu != Mt->TopMenu)
3866 {
3867 MenuHideSubPopups(Mt->OwnerWnd, &MenuInfo, FALSE, Flags);
3868 hmenutmp = Mt->CurrentMenu = Mt->TopMenu;
3869 }
3870 else
3871 {
3872 hmenutmp = NULL;
3873 }
3874
3875 /* try to move to the next item */
3876 if ( !MenuDoNextMenu(Mt, VK_RIGHT, Flags))
3877 MenuMoveSelection(Mt->OwnerWnd, &MenuInfo, ITEM_NEXT);
3878
3879 if ( hmenutmp || Mt->TrackFlags & TF_SUSPENDPOPUP )
3880 {
3881 if (! MenuSuspendPopup(Mt, WM_KEYDOWN)
3882 && MenuGetRosMenuInfo(&MenuInfo, Mt->TopMenu))
3883 {
3884 Mt->CurrentMenu = MenuShowSubPopup(Mt->OwnerWnd, &MenuInfo,
3885 TRUE, Flags);
3886 }
3887 }
3888 }
3889 }
3890
3891 /***********************************************************************
3892 * MenuTrackMenu
3893 *
3894 * Menu tracking code.
3895 */
3896 static INT FASTCALL MenuTrackMenu(HMENU hmenu, UINT wFlags, INT x, INT y,
3897 HWND hwnd, const RECT *lprect )
3898 {
3899 MSG msg;
3900 ROSMENUINFO MenuInfo;
3901 ROSMENUITEMINFO ItemInfo;
3902 BOOL fRemove;
3903 INT executedMenuId = -1;
3904 MTRACKER mt;
3905 HWND capture_win;
3906 BOOL enterIdleSent = FALSE;
3907
3908 mt.TrackFlags = 0;
3909 mt.CurrentMenu = hmenu;
3910 mt.TopMenu = hmenu;
3911 mt.OwnerWnd = hwnd;
3912 mt.Pt.x = x;
3913 mt.Pt.y = y;
3914
3915 TRACE("hmenu=%p flags=0x%08x (%d,%d) hwnd=%x (%ld,%ld)-(%ld,%ld)\n",
3916 hmenu, wFlags, x, y, hwnd, lprect ? lprect->left : 0, lprect ? lprect->top : 0,
3917 lprect ? lprect->right : 0, lprect ? lprect->bottom : 0);
3918
3919 if (!IsMenu(hmenu))
3920 {
3921 WARN("Invalid menu handle %p\n", hmenu);
3922 SetLastError( ERROR_INVALID_MENU_HANDLE );
3923 return FALSE;
3924 }
3925
3926 fEndMenu = FALSE;
3927 if (! MenuGetRosMenuInfo(&MenuInfo, hmenu))
3928 {
3929 return FALSE;
3930 }
3931
3932 if (wFlags & TPM_BUTTONDOWN)
3933 {
3934 /* Get the result in order to start the tracking or not */
3935 fRemove = MenuButtonDown( &mt, hmenu, wFlags );
3936 fEndMenu = !fRemove;
3937 }
3938
3939 if (wFlags & TF_ENDMENU) fEndMenu = TRUE;
3940
3941 /* owner may not be visible when tracking a popup, so use the menu itself */
3942 capture_win = (wFlags & TPM_POPUPMENU) ? MenuInfo.Wnd : mt.OwnerWnd;
3943 NtUserxSetGUIThreadHandle(MSQ_STATE_MENUOWNER, capture_win); // 1
3944 SetCapture(capture_win); // 2
3945
3946 while (! fEndMenu)
3947 {
3948 BOOL ErrorExit = FALSE;
3949 PMENU menu = ValidateHandle(mt.CurrentMenu, TYPE_MENU);
3950 if (!menu) /* sometimes happens if I do a window manager close */
3951 break;
3952
3953 /* we have to keep the message in the queue until it's
3954 * clear that menu loop is not over yet. */
3955
3956 for (;;)
3957 {
3958 if (PeekMessageW( &msg, 0, 0, 0, PM_NOREMOVE ))
3959 {
3960 if (!CallMsgFilterW( &msg, MSGF_MENU )) break;
3961 /* remove the message from the queue */
3962 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
3963 }
3964 else
3965 {
3966 /* ReactOS Check */
3967 if (!ValidateHwnd(mt.OwnerWnd) || !ValidateHwnd(MenuInfo.Wnd))
3968 {
3969 ErrorExit = TRUE; // Do not wait on dead windows, now test_capture_4 works.
3970 break;
3971 }
3972 if (!enterIdleSent)
3973 {
3974 HWND win = MenuInfo.fFlags & MNF_POPUP ? MenuInfo.Wnd : NULL;
3975 enterIdleSent = TRUE;
3976 SendMessageW( mt.OwnerWnd, WM_ENTERIDLE, MSGF_MENU, (LPARAM) win);
3977 }
3978 WaitMessage();
3979 }
3980 }
3981
3982 if (ErrorExit) break; // Gracefully dropout.
3983
3984 /* check if EndMenu() tried to cancel us, by posting this message */
3985 if (msg.message == WM_CANCELMODE)
3986 {
3987 /* we are now out of the loop */
3988 fEndMenu = TRUE;
3989
3990 /* remove the message from the queue */
3991 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
3992
3993 /* break out of internal loop, ala ESCAPE */
3994 break;
3995 }
3996
3997 TranslateMessage( &msg );
3998 mt.Pt = msg.pt;
3999
4000 if ( (msg.hwnd == MenuInfo.Wnd) || (msg.message!=WM_TIMER) )
4001 enterIdleSent=FALSE;
4002
4003 fRemove = FALSE;
4004 if ((msg.message >= WM_MOUSEFIRST) && (msg.message <= WM_MOUSELAST))
4005 {
4006 /*
4007 * Use the mouse coordinates in lParam instead of those in the MSG
4008 * struct to properly handle synthetic messages. They are already
4009 * in screen coordinates.
4010 */
4011 mt.Pt.x = (short)LOWORD(msg.lParam);
4012 mt.Pt.y = (short)HIWORD(msg.lParam);
4013
4014 /* Find a menu for this mouse event */
4015 hmenu = MenuPtMenu(mt.TopMenu, mt.Pt);
4016
4017 switch(msg.message)
4018 {
4019 /* no WM_NC... messages in captured state */
4020
4021 case WM_RBUTTONDBLCLK:
4022 ERR("WM_RBUTTONDBLCLK\n");
4023 case WM_RBUTTONDOWN:
4024 if (!(wFlags & TPM_RIGHTBUTTON)) break;
4025 /* fall through */
4026 case WM_LBUTTONDBLCLK:
4027 case WM_LBUTTONDOWN:
4028 /* If the message belongs to the menu, removes it from the queue */
4029 /* Else, end menu tracking */
4030 fRemove = MenuButtonDown(&mt, hmenu, wFlags);
4031 fEndMenu = !fRemove;
4032 break;
4033
4034 case WM_RBUTTONUP:
4035 if (!(wFlags & TPM_RIGHTBUTTON)) break;
4036 /* fall through */
4037 case WM_LBUTTONUP:
4038 /* Check if a menu was selected by the mouse */
4039 if (hmenu)
4040 {
4041 executedMenuId = MenuButtonUp( &mt, hmenu, wFlags);
4042 TRACE("executedMenuId %d\n", executedMenuId);
4043
4044 /* End the loop if executedMenuId is an item ID */
4045 /* or if the job was done (executedMenuId = 0). */
4046 fEndMenu = fRemove = (executedMenuId != -1);
4047 }
4048 /* No menu was selected by the mouse */
4049 /* if the function was called by TrackPopupMenu, continue
4050 with the menu tracking. If not, stop it */
4051 else
4052 fEndMenu = ((wFlags & TPM_POPUPMENU) ? FALSE : TRUE);
4053
4054 break;
4055
4056 case WM_MOUSEMOVE:
4057 /* the selected menu item must be changed every time */
4058 /* the mouse moves. */
4059
4060 if (hmenu)
4061 fEndMenu |= !MenuMouseMove( &mt, hmenu, wFlags );
4062
4063 } /* switch(msg.message) - mouse */
4064 }
4065 else if ((msg.message >= WM_KEYFIRST) && (msg.message <= WM_KEYLAST))
4066 {
4067 fRemove = TRUE; /* Keyboard messages are always removed */
4068 switch(msg.message)
4069 {
4070 case WM_KEYDOWN:
4071 case WM_SYSKEYDOWN:
4072 switch(msg.wParam)
4073 {
4074 case VK_MENU:
4075 case VK_F10:
4076 fEndMenu = TRUE;
4077 break;
4078
4079 case VK_HOME:
4080 case VK_END:
4081 if (MenuGetRosMenuInfo(&MenuInfo, mt.CurrentMenu))
4082 {
4083 MenuSelectItem(mt.OwnerWnd, &MenuInfo,
4084 NO_SELECTED_ITEM, FALSE, 0 );
4085 MenuMoveSelection(mt.OwnerWnd, &MenuInfo,
4086 VK_HOME == msg.wParam ? ITEM_NEXT : ITEM_PREV);
4087 }
4088 break;
4089
4090 case VK_UP:
4091 case VK_DOWN: /* If on menu bar, pull-down the menu */
4092 if (MenuGetRosMenuInfo(&MenuInfo, mt.CurrentMenu))
4093 {
4094 if (!(MenuInfo.fFlags & MNF_POPUP))
4095 {
4096 if (MenuGetRosMenuInfo(&MenuInfo, mt.TopMenu))
4097 mt.CurrentMenu = MenuShowSubPopup(mt.OwnerWnd, &MenuInfo, TRUE, wFlags);
4098 }
4099 else /* otherwise try to move selection */
4100 MenuMoveSelection(mt.OwnerWnd, &MenuInfo,
4101 (msg.wParam == VK_UP)? ITEM_PREV : ITEM_NEXT );
4102 }
4103 break;
4104
4105 case VK_LEFT:
4106 MenuKeyLeft( &mt, wFlags );
4107 break;
4108
4109 case VK_RIGHT:
4110 MenuKeyRight( &mt, wFlags );
4111 break;
4112
4113 case VK_ESCAPE:
4114 fEndMenu = MenuKeyEscape(&mt, wFlags);
4115 break;
4116
4117 case VK_F1:
4118 {
4119 HELPINFO hi;
4120 hi.cbSize = sizeof(HELPINFO);
4121 hi.iContextType = HELPINFO_MENUITEM;
4122 if (MenuGetRosMenuInfo(&MenuInfo, mt.CurrentMenu))
4123 {
4124 if (MenuInfo.iItem == NO_SELECTED_ITEM)
4125 hi.iCtrlId = 0;
4126 else
4127 {
4128 MenuInitRosMenuItemInfo(&ItemInfo);
4129 if (MenuGetRosMenuItemInfo(MenuInfo.Self,
4130 MenuInfo.iItem,
4131 &ItemInfo))
4132 {
4133 hi.iCtrlId = ItemInfo.wID;
4134 }
4135 else
4136 {
4137 hi.iCtrlId = 0;
4138 }
4139 MenuCleanupRosMenuItemInfo(&ItemInfo);
4140 }
4141 }
4142 hi.hItemHandle = hmenu;
4143 hi.dwContextId = MenuInfo.dwContextHelpID;
4144 hi.MousePos = msg.pt;
4145 SendMessageW(hwnd, WM_HELP, 0, (LPARAM)&hi);
4146 break;
4147 }
4148
4149 default:
4150 break;
4151 }
4152 break; /* WM_KEYDOWN */
4153
4154 case WM_CHAR:
4155 case WM_SYSCHAR:
4156 {
4157 UINT pos;
4158
4159 if (! MenuGetRosMenuInfo(&MenuInfo, mt.CurrentMenu)) break;
4160 if (msg.wParam == L'\r' || msg.wParam == L' ')
4161 {
4162 executedMenuId = MenuExecFocusedItem(&mt, &MenuInfo, wFlags);
4163 fEndMenu = (executedMenuId != -2);
4164 break;
4165 }
4166
4167 /* Hack to avoid control chars. */
4168 /* We will find a better way real soon... */
4169 if (msg.wParam < 32) break;
4170
4171 pos = MenuFindItemByKey(mt.OwnerWnd, &MenuInfo,
4172 LOWORD(msg.wParam), FALSE);
4173 if (pos == (UINT)-2) fEndMenu = TRUE;
4174 else if (pos == (UINT)-1) MessageBeep(0);
4175 else
4176 {
4177 MenuSelectItem(mt.OwnerWnd, &MenuInfo, pos,
4178 TRUE, 0);
4179 executedMenuId = MenuExecFocusedItem(&mt, &MenuInfo, wFlags);
4180 fEndMenu = (executedMenuId != -2);
4181 }
4182 }
4183 break;
4184 } /* switch(msg.message) - kbd */
4185 }
4186 else
4187 {
4188 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
4189 DispatchMessageW( &msg );
4190 continue;
4191 }
4192
4193 if (!fEndMenu) fRemove = TRUE;
4194
4195 /* finally remove message from the queue */
4196
4197 if (fRemove && !(mt.TrackFlags & TF_SKIPREMOVE) )
4198 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
4199 else mt.TrackFlags &= ~TF_SKIPREMOVE;
4200 }
4201
4202 NtUserxSetGUIThreadHandle(MSQ_STATE_MENUOWNER, NULL);
4203 SetCapture(NULL); /* release the capture */
4204
4205 /* If dropdown is still painted and the close box is clicked on
4206 then the menu will be destroyed as part of the DispatchMessage above.
4207 This will then invalidate the menu handle in mt.hTopMenu. We should
4208 check for this first. */
4209 if( IsMenu( mt.TopMenu ) )
4210 {
4211 if (IsWindow(mt.OwnerWnd))
4212 {
4213 if (MenuGetRosMenuInfo(&MenuInfo, mt.TopMenu))
4214 {
4215 MenuHideSubPopups(mt.OwnerWnd, &MenuInfo, FALSE, wFlags);
4216
4217 if (MenuInfo.fFlags & MNF_POPUP)
4218 {
4219 IntNotifyWinEvent(EVENT_SYSTEM_MENUPOPUPEND, MenuInfo.Wnd, OBJID_CLIENT, CHILDID_SELF, 0);
4220 DestroyWindow(MenuInfo.Wnd);
4221 MenuInfo.Wnd = NULL;
4222
4223 if (!(wFlags & TPM_NONOTIFY))
4224 SendMessageW( mt.OwnerWnd, WM_UNINITMENUPOPUP, (WPARAM)mt.TopMenu,
4225 MAKELPARAM(0, IS_SYSTEM_MENU(&MenuInfo)) );
4226 }
4227 MenuSelectItem( mt.OwnerWnd, &MenuInfo, NO_SELECTED_ITEM, FALSE, 0 );
4228 }
4229
4230 SendMessageW( mt.OwnerWnd, WM_MENUSELECT, MAKEWPARAM(0, 0xffff), 0 );
4231 }
4232
4233 /* Reset the variable for hiding menu */
4234 if (MenuGetRosMenuInfo(&MenuInfo, mt.TopMenu))
4235 {
4236 MenuInfo.TimeToHide = FALSE;
4237 MenuSetRosMenuInfo(&MenuInfo);
4238 }
4239 }
4240
4241 /* The return value is only used by TrackPopupMenu */
4242 if (!(wFlags & TPM_RETURNCMD)) return TRUE;
4243 if (executedMenuId == -1) executedMenuId = 0;
4244 return executedMenuId;
4245 }
4246
4247 /***********************************************************************
4248 * MenuInitTracking
4249 */
4250 static BOOL FASTCALL MenuInitTracking(HWND hWnd, HMENU hMenu, BOOL bPopup, UINT wFlags)
4251 {
4252 ROSMENUINFO MenuInfo;
4253
4254 TRACE("hwnd=%p hmenu=%p\n", hWnd, hMenu);
4255
4256 HideCaret(0);
4257
4258 /* This makes the menus of applications built with Delphi work.
4259 * It also enables menus to be displayed in more than one window,
4260 * but there are some bugs left that need to be fixed in this case.
4261 */
4262 if (MenuGetRosMenuInfo(&MenuInfo, hMenu))
4263 {
4264 MenuInfo.Wnd = hWnd;
4265 MenuSetRosMenuInfo(&MenuInfo);
4266 }
4267
4268 /* Send WM_ENTERMENULOOP and WM_INITMENU message only if TPM_NONOTIFY flag is not specified */
4269 if (!(wFlags & TPM_NONOTIFY))
4270 SendMessageW( hWnd, WM_ENTERMENULOOP, bPopup, 0 );
4271
4272 SendMessageW( hWnd, WM_SETCURSOR, (WPARAM)hWnd, HTCAPTION );
4273
4274 if (!(wFlags & TPM_NONOTIFY))
4275 {
4276 SendMessageW( hWnd, WM_INITMENU, (WPARAM)hMenu, 0 );
4277 /* If an app changed/recreated menu bar entries in WM_INITMENU
4278 * menu sizes will be recalculated once the menu created/shown.
4279 */
4280
4281 if (!MenuInfo.cyMenu)
4282 {
4283 /* app changed/recreated menu bar entries in WM_INITMENU
4284 Recalculate menu sizes else clicks will not work */
4285 SetWindowPos(hWnd, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE |
4286 SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED );
4287
4288 }
4289 }
4290
4291 IntNotifyWinEvent( EVENT_SYSTEM_MENUSTART,
4292 hWnd,
4293 MenuInfo.fFlags & MNF_SYSDESKMN ? OBJID_SYSMENU : OBJID_MENU,
4294 CHILDID_SELF, 0);
4295 return TRUE;
4296 }
4297 /***********************************************************************
4298 * MenuExitTracking
4299 */
4300 static BOOL FASTCALL MenuExitTracking(HWND hWnd, BOOL bPopup)
4301 {
4302 TRACE("hwnd=%p\n", hWnd);
4303
4304 IntNotifyWinEvent( EVENT_SYSTEM_MENUEND, hWnd, OBJID_WINDOW, CHILDID_SELF, 0);
4305 SendMessageW( hWnd, WM_EXITMENULOOP, bPopup, 0 );
4306 ShowCaret(0);
4307 top_popup = 0;
4308 top_popup_hmenu = NULL;
4309 return TRUE;
4310 }
4311
4312 /***********************************************************************
4313 * MenuTrackMouseMenuBar
4314 *
4315 * Menu-bar tracking upon a mouse event. Called from NC_HandleSysCommand().
4316 */
4317 VOID MenuTrackMouseMenuBar( HWND hWnd, ULONG ht, POINT pt)
4318 {
4319 HMENU hMenu = (ht == HTSYSMENU) ? NtUserGetSystemMenu( hWnd, FALSE) : GetMenu(hWnd);
4320 UINT wFlags = TPM_BUTTONDOWN | TPM_LEFTALIGN | TPM_LEFTBUTTON;
4321
4322 TRACE("wnd=%p ht=0x%04x (%ld,%ld)\n", hWnd, ht, pt.x, pt.y);
4323
4324 if (GetWindowLongW( hWnd, GWL_EXSTYLE ) & WS_EX_LAYOUTRTL) wFlags |= TPM_LAYOUTRTL;
4325 if (IsMenu(hMenu))
4326 {
4327 /* map point to parent client coordinates */
4328 HWND Parent = GetAncestor(hWnd, GA_PARENT );
4329 if (Parent != GetDesktopWindow())
4330 {
4331 ScreenToClient(Parent, &pt);
4332 }
4333
4334 MenuInitTracking(hWnd, hMenu, FALSE, wFlags);
4335 MenuTrackMenu(hMenu, wFlags, pt.x, pt.y, hWnd, NULL);
4336 MenuExitTracking(hWnd, FALSE);
4337 }
4338 }
4339
4340
4341 /***********************************************************************
4342 * MenuTrackKbdMenuBar
4343 *
4344 * Menu-bar tracking upon a keyboard event. Called from NC_HandleSysCommand().
4345 */
4346 VOID MenuTrackKbdMenuBar(HWND hwnd, UINT wParam, WCHAR wChar)
4347 {
4348 UINT uItem = NO_SELECTED_ITEM;
4349 HMENU hTrackMenu;
4350 ROSMENUINFO MenuInfo;
4351 UINT wFlags = TPM_LEFTALIGN | TPM_LEFTBUTTON;
4352
4353 TRACE("hwnd %p wParam 0x%04x wChar 0x%04x\n", hwnd, wParam, wChar);
4354
4355 /* find window that has a menu */
4356
4357 while (!((GetWindowLongPtrW( hwnd, GWL_STYLE ) &
4358 (WS_CHILD | WS_POPUP)) != WS_CHILD))
4359 if (!(hwnd = GetAncestor( hwnd, GA_PARENT ))) return;
4360
4361 /* check if we have to track a system menu */
4362
4363 hTrackMenu = GetMenu( hwnd );
4364 if (!hTrackMenu || IsIconic(hwnd) || wChar == ' ' )
4365 {
4366 if (!(GetWindowLongPtrW( hwnd, GWL_STYLE ) & WS_SYSMENU)) return;
4367 hTrackMenu = NtUserGetSystemMenu(hwnd, FALSE);
4368 uItem = 0;
4369 wParam |= HTSYSMENU; /* prevent item lookup */
4370 }
4371
4372 if (!IsMenu( hTrackMenu )) return;
4373
4374 MenuInitTracking( hwnd, hTrackMenu, FALSE, wFlags );
4375
4376 if (! MenuGetRosMenuInfo(&MenuInfo, hTrackMenu))
4377 {
4378 goto track_menu;
4379 }
4380
4381 if( wChar && wChar != ' ' )
4382 {
4383 uItem = MenuFindItemByKey( hwnd, &MenuInfo, wChar, (wParam & HTSYSMENU) );
4384 if ( uItem >= (UINT)(-2) )
4385 {
4386 if( uItem == (UINT)(-1) ) MessageBeep(0);
4387 /* schedule end of menu tracking */
4388 wFlags |= TF_ENDMENU;
4389 goto track_menu;
4390 }
4391 }
4392
4393 MenuSelectItem( hwnd, &MenuInfo, uItem, TRUE, 0 );
4394
4395 if (!(wParam & HTSYSMENU) || wChar == ' ')
4396 {
4397 if( uItem == NO_SELECTED_ITEM )
4398 MenuMoveSelection( hwnd, &MenuInfo, ITEM_NEXT );
4399 else
4400 PostMessageW( hwnd, WM_KEYDOWN, VK_RETURN, 0 );
4401 }
4402
4403 track_menu:
4404 MenuTrackMenu( hTrackMenu, wFlags, 0, 0, hwnd, NULL );
4405 MenuExitTracking( hwnd, FALSE );
4406 }
4407
4408 /**********************************************************************
4409 * TrackPopupMenuEx (USER32.@)
4410 */
4411 BOOL WINAPI TrackPopupMenuEx( HMENU Menu, UINT Flags, int x, int y,
4412 HWND Wnd, LPTPMPARAMS Tpm)
4413 {
4414 BOOL ret = FALSE;
4415 ROSMENUINFO MenuInfo;
4416
4417 if (!IsMenu(Menu))
4418 {
4419 SetLastError( ERROR_INVALID_MENU_HANDLE );
4420 return FALSE;
4421 }
4422
4423 /* ReactOS Check */
4424 if (!ValidateHwnd(Wnd))
4425 {
4426 /* invalid window see wine menu.c test_menu_trackpopupmenu line 3146 */
4427 return FALSE;
4428 }
4429
4430 MenuGetRosMenuInfo(&MenuInfo, Menu);
4431 if (IsWindow(MenuInfo.Wnd))
4432 {
4433 SetLastError( ERROR_POPUP_ALREADY_ACTIVE );
4434 return FALSE;
4435 }
4436
4437 MenuInitTracking(Wnd, Menu, TRUE, Flags);
4438
4439 /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
4440 if (!(Flags & TPM_NONOTIFY))
4441 SendMessageW(Wnd, WM_INITMENUPOPUP, (WPARAM) Menu, 0);
4442
4443 if (MenuShowPopup(Wnd, Menu, 0, Flags, x, y, 0, 0 ))
4444 ret = MenuTrackMenu(Menu, Flags | TPM_POPUPMENU, 0, 0, Wnd,
4445 Tpm ? &Tpm->rcExclude : NULL);
4446 MenuExitTracking(Wnd, TRUE);
4447 return ret;
4448 }
4449
4450 /**********************************************************************
4451 * TrackPopupMenu (USER32.@)
4452 */
4453 BOOL WINAPI TrackPopupMenu( HMENU Menu, UINT Flags, int x, int y,
4454 int Reserved, HWND Wnd, CONST RECT *Rect)
4455 {
4456 return TrackPopupMenuEx( Menu, Flags, x, y, Wnd, NULL);
4457 }
4458
4459 /*
4460 * From MSDN:
4461 * The MFT_BITMAP, MFT_SEPARATOR, and MFT_STRING values cannot be combined
4462 * with one another. Also MFT_OWNERDRAW. Set fMask to MIIM_TYPE to use fType.
4463 *
4464 * Windows 2K/XP: fType is used only if fMask has a value of MIIM_FTYPE.
4465 *
4466 * MIIM_TYPE: Retrieves or sets the fType and dwTypeData members. Windows
4467 * 2K/XP: MIIM_TYPE is replaced by MIIM_BITMAP, MIIM_FTYPE, and MIIM_STRING.
4468 * MFT_STRING is replaced by MIIM_STRING.
4469 * (So, I guess we should use MIIM_STRING only for strings?)
4470 *
4471 * MIIM_FTYPE: Windows 2K/Windows XP: Retrieves or sets the fType member.
4472 *
4473 * Based on wine, SetMenuItemInfo_common:
4474 * 1) set MIIM_STRING | MIIM_FTYPE | MIIM_BITMAP any one with MIIM_TYPE,
4475 * it will result in a error.
4476 * 2) set menu mask to MIIM_FTYPE and MFT_BITMAP ftype it will result in a error.
4477 * These conditions are addressed in Win32k IntSetMenuItemInfo.
4478 *
4479 */
4480 static
4481 BOOL
4482 FASTCALL
4483 MenuSetItemData(
4484 LPMENUITEMINFOW mii,
4485 UINT Flags,
4486 UINT_PTR IDNewItem,
4487 LPCWSTR NewItem,
4488 BOOL Unicode)
4489 {
4490 /*
4491 * Let us assume MIIM_FTYPE is set and building a new menu item structure.
4492 */
4493 if(Flags & MF_BITMAP)
4494 {
4495 mii->fMask |= MIIM_BITMAP; /* Use the new way of seting hbmpItem.*/
4496 mii->hbmpItem = (HBITMAP) NewItem;
4497
4498 if (Flags & MF_HELP)
4499 {
4500 /* increase ident */
4501 mii->fType |= MF_HELP;
4502 }
4503 }
4504 else if(Flags & MF_OWNERDRAW)
4505 {
4506 mii->fType |= MFT_OWNERDRAW;
4507 mii->fMask |= MIIM_DATA;
4508 mii->dwItemData = (DWORD_PTR) NewItem;
4509 }
4510 else if (Flags & MF_SEPARATOR)
4511 {
4512 mii->fType |= MFT_SEPARATOR;
4513 if (!(Flags & (MF_GRAYED|MF_DISABLED)))
4514 Flags |= MF_GRAYED|MF_DISABLED;
4515 }
4516 else /* Default action MF_STRING. */
4517 {
4518 /* Item beginning with a backspace is a help item */
4519 if (NewItem != NULL)
4520 {
4521 if (Unicode)
4522 {
4523 if (*NewItem == '\b')
4524 {
4525 mii->fType |= MF_HELP;
4526 NewItem++;
4527 }
4528 }
4529 else
4530 {
4531 LPCSTR NewItemA = (LPCSTR) NewItem;
4532 if (*NewItemA == '\b')
4533 {
4534 mii->fType |= MF_HELP;
4535 NewItemA++;
4536 NewItem = (LPCWSTR) NewItemA;
4537 }
4538 }
4539
4540 if (Flags & MF_HELP)
4541 mii->fType |= MF_HELP;
4542 mii->fMask |= MIIM_STRING;
4543 mii->fType |= MFT_STRING; /* Zero */
4544 mii->dwTypeData = (LPWSTR)NewItem;
4545 if (Unicode)
4546 mii->cch = (NULL == NewItem ? 0 : strlenW(NewItem));
4547 else
4548 mii->cch = (NULL == NewItem ? 0 : strlen((LPCSTR)NewItem));
4549 }
4550 else
4551 {
4552 mii->fType |= MFT_SEPARATOR;
4553 if (!(Flags & (MF_GRAYED|MF_DISABLED)))
4554 Flags |= MF_GRAYED|MF_DISABLED;
4555 }
4556 }
4557
4558 if(Flags & MF_RIGHTJUSTIFY) /* Same as MF_HELP */
4559 {
4560 mii->fType |= MFT_RIGHTJUSTIFY;
4561 }
4562
4563 if(Flags & MF_MENUBREAK)
4564 {
4565 mii->fType |= MFT_MENUBREAK;
4566 }
4567 else if(Flags & MF_MENUBARBREAK)
4568 {
4569 mii->fType |= MFT_MENUBARBREAK;
4570 }
4571
4572 if(Flags & MF_GRAYED || Flags & MF_DISABLED)
4573 {
4574 if (Flags & MF_GRAYED)
4575 mii->fState |= MF_GRAYED;
4576
4577 if (Flags & MF_DISABLED)
4578 mii->fState |= MF_DISABLED;
4579
4580 mii->fMask |= MIIM_STATE;
4581 }
4582 else if (Flags & MF_HILITE)
4583 {
4584 mii->fState |= MF_HILITE;
4585 mii->fMask |= MIIM_STATE;
4586 }
4587 else /* default state */
4588 {
4589 mii->fState |= MFS_ENABLED;
4590 mii->fMask |= MIIM_STATE;
4591 }
4592
4593 if(Flags & MF_POPUP && IsMenu((HMENU)IDNewItem))
4594 {
4595 mii->fMask |= MIIM_SUBMENU;
4596 mii->hSubMenu = (HMENU)IDNewItem;
4597 }
4598 mii->fMask |= MIIM_ID;
4599 mii->wID = (UINT)IDNewItem;
4600 return TRUE;
4601 }
4602
4603 NTSTATUS WINAPI
4604 User32CallLoadMenuFromKernel(PVOID Arguments, ULONG ArgumentLength)
4605 {
4606 PLOADMENU_CALLBACK_ARGUMENTS Common;
4607 LRESULT Result;
4608
4609 Common = (PLOADMENU_CALLBACK_ARGUMENTS) Arguments;
4610
4611 Result = (LRESULT)LoadMenuW( Common->hModule,
4612 IS_INTRESOURCE(Common->MenuName[0]) ?
4613 MAKEINTRESOURCE(Common->MenuName[0]) :
4614 (LPCWSTR)&Common->MenuName);
4615
4616 return ZwCallbackReturn(&Result, sizeof(LRESULT), STATUS_SUCCESS);
4617 }
4618
4619 /**********************************************************************
4620 * MENU_NormalizeMenuItemInfoStruct
4621 *
4622 * Helper for SetMenuItemInfo and InsertMenuItemInfo:
4623 * check, copy and extend the MENUITEMINFO struct from the version that the application
4624 * supplied to the version used by wine source. */
4625 static BOOL MENU_NormalizeMenuItemInfoStruct( const MENUITEMINFOW *pmii_in,
4626 MENUITEMINFOW *pmii_out )
4627 {
4628 /* do we recognize the size? */
4629 if( !pmii_in || (pmii_in->cbSize != sizeof( MENUITEMINFOW) &&
4630 pmii_in->cbSize != sizeof( MENUITEMINFOW) - sizeof( pmii_in->hbmpItem)) ) {
4631 SetLastError( ERROR_INVALID_PARAMETER);
4632 return FALSE;
4633 }
4634 /* copy the fields that we have */
4635 memcpy( pmii_out, pmii_in, pmii_in->cbSize);
4636 /* if the hbmpItem member is missing then extend */
4637 if( pmii_in->cbSize != sizeof( MENUITEMINFOW)) {
4638 pmii_out->cbSize = sizeof( MENUITEMINFOW);
4639 pmii_out->hbmpItem = NULL;
4640 }
4641 /* test for invalid bit combinations */
4642 if( (pmii_out->fMask & MIIM_TYPE &&
4643 pmii_out->fMask & (MIIM_STRING | MIIM_FTYPE | MIIM_BITMAP)) ||
4644 (pmii_out->fMask & MIIM_FTYPE && pmii_out->fType & MFT_BITMAP)) {
4645 ERR("invalid combination of fMask bits used\n");
4646 /* this does not happen on Win9x/ME */
4647 SetLastError( ERROR_INVALID_PARAMETER);
4648 return FALSE;
4649 }
4650 /* convert old style (MIIM_TYPE) to the new and keep the old one too */
4651 if( pmii_out->fMask & MIIM_TYPE){
4652 pmii_out->fMask |= MIIM_FTYPE;
4653 if( IS_STRING_ITEM(pmii_out->fType)){
4654 pmii_out->fMask |= MIIM_STRING;
4655 } else if( (pmii_out->fType) & MFT_BITMAP){
4656 pmii_out->fMask |= MIIM_BITMAP;
4657 pmii_out->hbmpItem = UlongToHandle(LOWORD(pmii_out->dwTypeData));
4658 }
4659 }
4660 if (pmii_out->fMask & MIIM_FTYPE )
4661 {
4662 pmii_out->fType &= ~MENUITEMINFO_TYPE_MASK;
4663 pmii_out->fType |= pmii_in->fType & MENUITEMINFO_TYPE_MASK;
4664 }
4665 if (pmii_out->fMask & MIIM_STATE)
4666 /* Other menu items having MFS_DEFAULT are not converted
4667 to normal items */
4668 pmii_out->fState = pmii_in->fState & MENUITEMINFO_STATE_MASK;
4669
4670 return TRUE;
4671 }
4672
4673
4674 /* FUNCTIONS *****************************************************************/
4675
4676 /*
4677 * @implemented
4678 */
4679 BOOL WINAPI
4680 AppendMenuA(HMENU hMenu,
4681 UINT uFlags,
4682 UINT_PTR uIDNewItem,
4683 LPCSTR lpNewItem)
4684 {
4685 return(InsertMenuA(hMenu, -1, uFlags | MF_BYPOSITION, uIDNewItem, lpNewItem));
4686 }
4687
4688 /*
4689 * @implemented
4690 */
4691 BOOL WINAPI
4692 AppendMenuW(HMENU hMenu,
4693 UINT uFlags,
4694 UINT_PTR uIDNewItem,
4695 LPCWSTR lpNewItem)
4696 {
4697 return(InsertMenuW(hMenu, -1, uFlags | MF_BYPOSITION, uIDNewItem, lpNewItem));
4698 }
4699
4700 /*
4701 * @implemented
4702 */
4703 DWORD WINAPI
4704 CheckMenuItem(HMENU hmenu,
4705 UINT uIDCheckItem,
4706 UINT uCheck)
4707 {
4708 PMENU pMenu;
4709 PITEM item;
4710 DWORD Ret;
4711
4712 if (!(pMenu = ValidateHandle(hmenu, TYPE_MENU)))
4713 return -1;
4714
4715 if (!(item = MENU_FindItem( &hmenu, &uIDCheckItem, uCheck ))) return -1;
4716
4717 Ret = item->fState & MFS_CHECKED;
4718 if ( Ret == (uCheck & MFS_CHECKED)) return Ret; // Already Checked...
4719
4720 return NtUserCheckMenuItem(hmenu, uIDCheckItem, uCheck);
4721 }
4722
4723
4724 /*
4725 * @implemented
4726 */
4727 BOOL WINAPI
4728 CheckMenuRadioItem(HMENU hMenu,
4729 UINT first,
4730 UINT last,
4731 UINT check,
4732 UINT bypos)
4733 {
4734 BOOL done = FALSE;
4735 UINT i;
4736 PITEM mi_first = NULL, mi_check;
4737 HMENU m_first, m_check;
4738 MENUITEMINFOW mii;
4739 mii.cbSize = sizeof( mii);
4740
4741 for (i = first; i <= last; i++)
4742 {
4743 UINT pos = i;
4744
4745 if (!mi_first)
4746 {
4747 m_first = hMenu;
4748 mi_first = MENU_FindItem(&m_first, &pos, bypos);
4749 if (!mi_first) continue;
4750 mi_check = mi_first;
4751 m_check = m_first;
4752 }
4753 else
4754 {
4755 m_check = hMenu;
4756 mi_check = MENU_FindItem(&m_check, &pos, bypos);
4757 if (!mi_check) continue;
4758 }
4759
4760 if (m_first != m_check) continue;
4761 if (mi_check->fType == MFT_SEPARATOR) continue;
4762
4763 if (i == check)
4764 {
4765 if (!(mi_check->fType & MFT_RADIOCHECK) || !(mi_check->fState & MFS_CHECKED))
4766 {
4767 mii.fMask = MIIM_FTYPE | MIIM_STATE;
4768 mii.fType = (mi_check->fType & MENUITEMINFO_TYPE_MASK) | MFT_RADIOCHECK;
4769 mii.fState = (mi_check->fState & MII_STATE_MASK) | MFS_CHECKED;
4770 NtUserThunkedMenuItemInfo(m_check, i, bypos, FALSE, &mii, NULL);
4771 }
4772 done = TRUE;
4773 }
4774 else
4775 {
4776 /* MSDN is wrong, Windows does not remove MFT_RADIOCHECK */
4777 if (mi_check->fState & MFS_CHECKED)
4778 {
4779 mii.fMask = MIIM_STATE;
4780 mii.fState = (mi_check->fState & MII_STATE_MASK) & ~MFS_CHECKED;
4781 NtUserThunkedMenuItemInfo(m_check, i, bypos, FALSE, &mii, NULL);
4782 }
4783 }
4784 }
4785 return done;
4786 }
4787
4788 /*
4789 * @implemented
4790 */
4791 HMENU WINAPI
4792 CreateMenu(VOID)
4793 {
4794 MenuLoadBitmaps();
4795 return NtUserxCreateMenu();
4796 }
4797
4798 /*
4799 * @implemented
4800 */
4801 HMENU WINAPI
4802 CreatePopupMenu(VOID)
4803 {
4804 MenuLoadBitmaps();
4805 return NtUserxCreatePopupMenu();
4806 }
4807
4808 /*
4809 * @implemented
4810 */
4811 BOOL WINAPI
4812 DrawMenuBar(HWND hWnd)
4813 {
4814 return NtUserxDrawMenuBar(hWnd);
4815 }
4816
4817 /*
4818 * @implemented
4819 */
4820 BOOL WINAPI
4821 EnableMenuItem(HMENU hMenu,
4822 UINT uIDEnableItem,
4823 UINT uEnable)
4824 {
4825 return NtUserEnableMenuItem(hMenu, uIDEnableItem, uEnable);
4826 }
4827
4828 /*
4829 * @implemented
4830 */
4831 BOOL WINAPI
4832 EndMenu(VOID)
4833 {
4834 GUITHREADINFO guii;
4835 guii.cbSize = sizeof(GUITHREADINFO);
4836 if(GetGUIThreadInfo(GetCurrentThreadId(), &guii) && guii.hwndMenuOwner)
4837 {
4838 if (!fEndMenu &&
4839 top_popup &&
4840 guii.hwndMenuOwner != top_popup )
4841 {
4842 ERR("Capture GUI pti hWnd does not match top_popup!\n");
4843 }
4844 }
4845
4846 /* if we are in the menu code, and it is active */
4847 if (!fEndMenu && top_popup)
4848 {
4849 /* terminate the menu handling code */
4850 fEndMenu = TRUE;
4851
4852 /* needs to be posted to wakeup the internal menu handler */
4853 /* which will now terminate the menu, in the event that */
4854 /* the main window was minimized, or lost focus, so we */
4855 /* don't end up with an orphaned menu */
4856 PostMessageW( top_popup, WM_CANCELMODE, 0, 0);
4857 }
4858 return TRUE;
4859 }
4860
4861 BOOL WINAPI HiliteMenuItem( HWND hWnd, HMENU hMenu, UINT wItemID,
4862 UINT wHilite )
4863 {
4864 ROSMENUINFO MenuInfo;
4865 TRACE("(%p, %p, %04x, %04x);\n", hWnd, hMenu, wItemID, wHilite);
4866 // Force bits to be set call server side....
4867 // This alone works and passes all the menu test_menu_hilitemenuitem tests.
4868 if (!NtUserHiliteMenuItem(hWnd, hMenu, wItemID, wHilite)) return FALSE;
4869 // Without the above call we fail 3 out of the wine failed todo tests, see CORE-7967
4870 // Now redraw menu.
4871 if (MenuGetRosMenuInfo(&MenuInfo, hMenu))
4872 {
4873 if (MenuInfo.iItem == wItemID) return TRUE;
4874 MenuHideSubPopups( hWnd, &MenuInfo, FALSE, 0 );
4875 MenuSelectItem( hWnd, &MenuInfo, wItemID, TRUE, 0 );
4876 }
4877 return TRUE; // Always returns TRUE!
4878 }
4879
4880 /*
4881 * @implemented
4882 */
4883 HMENU WINAPI
4884 GetMenu(HWND hWnd)
4885 {
4886 PWND Wnd = ValidateHwnd(hWnd);
4887
4888 if (!Wnd)
4889 return NULL;
4890
4891 return UlongToHandle(Wnd->IDMenu);
4892 }
4893
4894
4895 /*
4896 * @implemented
4897 */
4898 BOOL WINAPI GetMenuBarInfo( HWND hwnd, LONG idObject, LONG idItem, PMENUBARINFO pmbi )
4899 {
4900 BOOL Ret;
4901 Ret = NtUserGetMenuBarInfo( hwnd, idObject, idItem, pmbi);
4902 // Reason to move to server side!!!!!
4903 if (!Ret) return Ret;
4904 // EL HAXZO!!!
4905 pmbi->fBarFocused = top_popup_hmenu == pmbi->hMenu;
4906 if (!idItem)
4907 {
4908 pmbi->fFocused = pmbi->fBarFocused;
4909 }
4910
4911 return TRUE;
4912 }
4913
4914 /*
4915 * @implemented
4916 */
4917 LONG WINAPI
4918 GetMenuCheckMarkDimensions(VOID)
4919 {
4920 return(MAKELONG(GetSystemMetrics(SM_CXMENUCHECK),
4921 GetSystemMetrics(SM_CYMENUCHECK)));
4922 }
4923
4924 /*
4925 * @implemented
4926 */
4927 DWORD
4928 WINAPI
4929 GetMenuContextHelpId(HMENU hmenu)
4930 {
4931 PMENU pMenu;
4932 if ((pMenu = ValidateHandle(hmenu, TYPE_MENU)))
4933 return pMenu->dwContextHelpId;
4934 return 0;
4935 }
4936
4937 /*
4938 * @implemented
4939 */
4940 UINT WINAPI
4941 GetMenuDefaultItem(HMENU hMenu,
4942 UINT fByPos,
4943 UINT gmdiFlags)
4944 {
4945 PMENU pMenu;
4946 DWORD gismc = 0;
4947 if (!(pMenu = ValidateHandle(hMenu, TYPE_MENU)))
4948 return (UINT)-1;
4949
4950 return IntGetMenuDefaultItem( pMenu, (BOOL)fByPos, gmdiFlags, &gismc);
4951 }
4952
4953 /*
4954 * @implemented
4955 */
4956 BOOL WINAPI
4957 GetMenuInfo(HMENU hmenu,
4958 LPMENUINFO lpcmi)
4959 {
4960 PMENU pMenu;
4961
4962 if (!lpcmi || (lpcmi->cbSize != sizeof(MENUINFO)))
4963 {
4964 SetLastError(ERROR_INVALID_PARAMETER);
4965 return FALSE;
4966 }
4967
4968 if (!(pMenu = ValidateHandle(hmenu, TYPE_MENU)))
4969 return FALSE;
4970
4971 if (lpcmi->fMask & MIM_BACKGROUND)
4972 lpcmi->hbrBack = pMenu->hbrBack;
4973
4974 if (lpcmi->fMask & MIM_HELPID)
4975 lpcmi->dwContextHelpID = pMenu->dwContextHelpId;
4976
4977 if (lpcmi->fMask & MIM_MAXHEIGHT)
4978 lpcmi->cyMax = pMenu->cyMax;
4979
4980 if (lpcmi->fMask & MIM_MENUDATA)
4981 lpcmi->dwMenuData = pMenu->dwMenuData;
4982
4983 if (lpcmi->fMask & MIM_STYLE)
4984 lpcmi->dwStyle = pMenu->fFlags & MNS_STYLE_MASK;
4985
4986 return TRUE;
4987 }
4988
4989 /*
4990 * @implemented
4991 */
4992 int WINAPI
4993 GetMenuItemCount(HMENU hmenu)
4994 {
4995 PMENU pMenu;
4996 if ((pMenu = ValidateHandle(hmenu, TYPE_MENU)))
4997 return pMenu->cItems;
4998 return -1;
4999 }
5000
5001 /*
5002 * @implemented
5003 */
5004 UINT WINAPI
5005 GetMenuItemID(HMENU hMenu,
5006 int nPos)
5007 {
5008 PMENU pMenu;
5009 PITEM pItem;
5010 INT i = 0;
5011
5012 if (!(pMenu = ValidateHandle(hMenu, TYPE_MENU)))
5013 return -1;
5014
5015 pItem = pMenu->rgItems ? DesktopPtrToUser(pMenu->rgItems) : NULL;
5016 if ( nPos >= 0 )
5017 {
5018 //pItem = &menu->rgItems[nPos]; or pItem[nPos]; after dptu.
5019 while(pItem) // Do this for now.
5020 {
5021 if (i < (INT)pMenu->cItems)
5022 {
5023 if ( nPos == i && !pItem->spSubMenu) return pItem->wID;
5024 }
5025 pItem = pItem->Next ? DesktopPtrToUser(pItem->Next) : NULL;
5026 i++;
5027 }
5028 }
5029 return -1;
5030 }
5031
5032 /*
5033 * @implemented
5034 */
5035 BOOL WINAPI
5036 GetMenuItemInfoA(
5037 HMENU hmenu,
5038 UINT item,
5039 BOOL bypos,
5040 LPMENUITEMINFOA lpmii)
5041 {
5042 BOOL ret;
5043 MENUITEMINFOA mii;
5044
5045 if( lpmii->cbSize != sizeof( mii) &&
5046 lpmii->cbSize != sizeof( mii) - sizeof ( mii.hbmpItem))
5047 {
5048 SetLastError( ERROR_INVALID_PARAMETER);
5049 return FALSE;
5050 }
5051 memcpy( &mii, lpmii, lpmii->cbSize);
5052 mii.cbSize = sizeof( mii);
5053 ret = GetMenuItemInfo_common (hmenu,
5054 item,
5055 bypos,
5056 (LPMENUITEMINFOW)&mii,
5057 FALSE);
5058 mii.cbSize = lpmii->cbSize;
5059 memcpy( lpmii, &mii, mii.cbSize);
5060 return ret;
5061 }
5062
5063 /*
5064 * @implemented
5065 */
5066 BOOL WINAPI
5067 GetMenuItemInfoW(
5068 HMENU hMenu,
5069 UINT Item,
5070 BOOL bypos,
5071 LPMENUITEMINFOW lpmii)
5072 {
5073 BOOL ret;
5074 MENUITEMINFOW mii;
5075 if( lpmii->cbSize != sizeof( mii) && lpmii->cbSize != sizeof( mii) - sizeof ( mii.hbmpItem))
5076 {
5077 SetLastError( ERROR_INVALID_PARAMETER);
5078 return FALSE;
5079 }
5080 memcpy( &mii, lpmii, lpmii->cbSize);
5081 mii.cbSize = sizeof( mii);
5082 ret = GetMenuItemInfo_common (hMenu, Item, bypos, &mii, TRUE);
5083 mii.cbSize = lpmii->cbSize;
5084 memcpy( lpmii, &mii, mii.cbSize);
5085 return ret;
5086 }
5087
5088 /*
5089 * @implemented
5090 */
5091 UINT
5092 WINAPI
5093 GetMenuState(
5094 HMENU hMenu,
5095 UINT uId,
5096 UINT uFlags)
5097 {
5098 PITEM pItem;
5099 TRACE("(menu=%p, id=%04x, flags=%04x);\n", hMenu, uId, uFlags);
5100 if (!(pItem = MENU_FindItem( &hMenu, &uId, uFlags ))) return -1;
5101
5102 if (pItem->spSubMenu)
5103 {
5104 PMENU pSubMenu = DesktopPtrToUser(pItem->spSubMenu);
5105 HMENU hsubmenu = UserHMGetHandle(pSubMenu);
5106 if (!IsMenu(hsubmenu)) return (UINT)-1;
5107 else return (pSubMenu->cItems << 8) | ((pItem->fState|pItem->fType) & 0xff);
5108 }
5109 else
5110 return (pItem->fType | pItem->fState);
5111 }
5112
5113 /*
5114 * @implemented
5115 */
5116 int
5117 WINAPI
5118 GetMenuStringA(
5119 HMENU hMenu,
5120 UINT uIDItem,
5121 LPSTR lpString,
5122 int nMaxCount,
5123 UINT uFlag)
5124 {
5125 /* MENUITEMINFOA mii;
5126 memset( &mii, 0, sizeof(mii) );
5127 mii.dwTypeData = lpString;
5128 mii.fMask = MIIM_STRING;
5129 mii.cbSize = sizeof(MENUITEMINFOA);
5130 mii.cch = nMaxCount;
5131
5132 if(!(GetMenuItemInfoA( hMenu, uIDItem, (BOOL)(MF_BYPOSITION & uFlag),&mii)))
5133 return 0;
5134 else
5135 return mii.cch;
5136 */
5137 ITEM *item;
5138 LPWSTR text;
5139 ////// wine Code, seems to be faster.
5140 TRACE("menu=%p item=%04x ptr=%p len=%d flags=%04x\n", hMenu, uIDItem, lpString, nMaxCount, uFlag );
5141
5142 if (lpString && nMaxCount) lpString[0] = '\0';
5143
5144 if (!(item = MENU_FindItem( &hMenu, &uIDItem, uFlag )))
5145 {
5146 SetLastError( ERROR_MENU_ITEM_NOT_FOUND);
5147 return 0;
5148 }
5149
5150 text = item->Xlpstr ? DesktopPtrToUser(item->Xlpstr) : NULL;
5151
5152 if (!text) return 0;
5153 if (!lpString || !nMaxCount) return WideCharToMultiByte( CP_ACP, 0, text, -1, NULL, 0, NULL, NULL );
5154 if (!WideCharToMultiByte( CP_ACP, 0, text, -1, lpString, nMaxCount, NULL, NULL ))
5155 lpString[nMaxCount-1] = 0;
5156 ERR("returning %s\n", lpString);
5157 return strlen(lpString);
5158 }
5159
5160 /*
5161 * @implemented
5162 */
5163 int
5164 WINAPI
5165 GetMenuStringW(
5166 HMENU hMenu,
5167 UINT uIDItem,
5168 LPWSTR lpString,
5169 int nMaxCount,
5170 UINT uFlag)
5171 {
5172 /* MENUITEMINFOW miiW;
5173 memset( &miiW, 0, sizeof(miiW) );
5174 miiW.dwTypeData = lpString;
5175 miiW.fMask = MIIM_STRING | MIIM_FTYPE;
5176 miiW.fType = MFT_STRING;
5177 miiW.cbSize = sizeof(MENUITEMINFOW);
5178 miiW.cch = nMaxCount;
5179
5180 if(!(GetMenuItemInfoW( hMenu, uIDItem, (BOOL)(MF_BYPOSITION & uFlag),&miiW)))
5181 return 0;
5182 else
5183 return miiW.cch;
5184 */
5185 ITEM *item;
5186 LPWSTR text;
5187
5188 TRACE("menu=%p item=%04x ptr=%p len=%d flags=%04x\n", hMenu, uIDItem, lpString, nMaxCount, uFlag );
5189
5190 if (lpString && nMaxCount) lpString[0] = '\0';
5191
5192 if (!(item = MENU_FindItem( &hMenu, &uIDItem, uFlag )))
5193 {
5194 SetLastError( ERROR_MENU_ITEM_NOT_FOUND);
5195 return 0;
5196 }
5197
5198 text = item->Xlpstr ? DesktopPtrToUser(item->Xlpstr) : NULL;
5199
5200 if (!lpString || !nMaxCount) return text ? strlenW(text) : 0;
5201 if( !(text))
5202 {
5203 lpString[0] = 0;
5204 return 0;
5205 }
5206 lstrcpynW( lpString, text, nMaxCount );
5207 ERR("returning %S\n", lpString);
5208 return strlenW(lpString);
5209 }
5210
5211 /*
5212 * @implemented
5213 */
5214 HMENU
5215 WINAPI
5216 GetSubMenu(
5217 HMENU hMenu,
5218 int nPos)
5219 {
5220 PITEM pItem;
5221 if (!(pItem = MENU_FindItem( &hMenu, (UINT*)&nPos, MF_BYPOSITION ))) return NULL;
5222
5223 if (pItem->spSubMenu)
5224 {
5225 PMENU pSubMenu = DesktopPtrToUser(pItem->spSubMenu);
5226 HMENU hsubmenu = UserHMGetHandle(pSubMenu);
5227 if (IsMenu(hsubmenu)) return hsubmenu;
5228 }
5229 return NULL;
5230 }
5231
5232 /*
5233 * @implemented
5234 */
5235 HMENU
5236 WINAPI
5237 GetSystemMenu(
5238 HWND hWnd,
5239 BOOL bRevert)
5240 {
5241 HMENU TopMenu;
5242
5243 TopMenu = NtUserGetSystemMenu(hWnd, bRevert);
5244
5245 return NULL == TopMenu ? NULL : GetSubMenu(TopMenu, 0);
5246 }
5247
5248 /*
5249 * @implemented
5250 */
5251 BOOL
5252 WINAPI
5253 InsertMenuA(
5254 HMENU hMenu,
5255 UINT uPosition,
5256 UINT uFlags,
5257 UINT_PTR uIDNewItem,
5258 LPCSTR lpNewItem)
5259 {
5260 MENUITEMINFOA mii;
5261 memset( &mii, 0, sizeof(mii) );
5262 mii.cbSize = sizeof(MENUITEMINFOA);
5263 mii.fMask = MIIM_FTYPE;
5264
5265 MenuSetItemData((LPMENUITEMINFOW) &mii,
5266 uFlags,
5267 uIDNewItem,
5268 (LPCWSTR) lpNewItem,
5269 FALSE);
5270
5271 return InsertMenuItemA(hMenu, uPosition, (BOOL)((MF_BYPOSITION & uFlags) > 0), &mii);
5272 }
5273
5274 /*
5275 * @implemented
5276 */
5277 BOOL
5278 WINAPI
5279 InsertMenuItemA(
5280 HMENU hMenu,
5281 UINT uItem,
5282 BOOL fByPosition,
5283 LPCMENUITEMINFOA lpmii)
5284 {
5285 MENUITEMINFOW mii;
5286 UNICODE_STRING UnicodeString;
5287 BOOL res;
5288
5289 TRACE("hmenu %p, item %04x, by pos %d, info %p\n", hMenu, uItem, fByPosition, lpmii);
5290
5291 RtlInitUnicodeString(&UnicodeString, 0);
5292
5293 if (!MENU_NormalizeMenuItemInfoStruct( (const MENUITEMINFOW *)lpmii, &mii )) return FALSE;
5294
5295 /* copy the text string */
5296 if (((mii.fMask & MIIM_STRING) ||
5297 ((mii.fMask & MIIM_TYPE) && (MENU_ITEM_TYPE(mii.fType) == MF_STRING)))
5298 && mii.dwTypeData && !(GdiValidateHandle((HGDIOBJ)mii.dwTypeData)) )
5299 {
5300 if (!RtlCreateUnicodeStringFromAsciiz(&UnicodeString, (LPSTR)mii.dwTypeData))
5301 {
5302 SetLastError (ERROR_NOT_ENOUGH_MEMORY);
5303 return FALSE;
5304 }
5305 mii.dwTypeData = UnicodeString.Buffer;
5306 mii.cch = UnicodeString.Length / sizeof(WCHAR);
5307 }
5308 else
5309 {
5310 UnicodeString.Buffer = NULL;
5311 }
5312 res = NtUserThunkedMenuItemInfo(hMenu, uItem, fByPosition, TRUE, &mii, &UnicodeString);
5313 if ( UnicodeString.Buffer ) RtlFreeUnicodeString ( &UnicodeString );
5314 return res;
5315 }
5316
5317 /*
5318 * @implemented
5319 */
5320 BOOL
5321 WINAPI
5322 InsertMenuItemW(
5323 HMENU hMenu,
5324 UINT uItem,
5325 BOOL fByPosition,
5326 LPCMENUITEMINFOW lpmii)
5327 {
5328 MENUITEMINFOW mii;
5329 UNICODE_STRING MenuText;
5330 BOOL res = FALSE;
5331
5332 /* while we could just pass 'lpmii' to win32k, we make a copy so that
5333 if a bad user passes bad data, we crash his process instead of the
5334 entire kernel */
5335
5336 TRACE("hmenu %p, item %04x, by pos %d, info %p\n", hMenu, uItem, fByPosition, lpmii);
5337
5338 RtlInitUnicodeString(&MenuText, 0);
5339
5340 if (!MENU_NormalizeMenuItemInfoStruct( (const MENUITEMINFOW *)lpmii, &mii )) return FALSE;
5341
5342 /* copy the text string */
5343 if (((mii.fMask & MIIM_STRING) ||
5344 ((mii.fMask & MIIM_TYPE) && (MENU_ITEM_TYPE(mii.fType) == MF_STRING)))
5345 && mii.dwTypeData && !(GdiValidateHandle((HGDIOBJ)mii.dwTypeData)) )
5346 {
5347 RtlInitUnicodeString(&MenuText, (PWSTR)lpmii->dwTypeData);
5348 mii.dwTypeData = MenuText.Buffer;
5349 mii.cch = MenuText.Length / sizeof(WCHAR);
5350 }
5351 res = NtUserThunkedMenuItemInfo(hMenu, uItem, fByPosition, TRUE, &mii, &MenuText);
5352 return res;
5353 }
5354
5355 /*
5356 * @implemented
5357 */
5358 BOOL
5359 WINAPI
5360 InsertMenuW(
5361 HMENU hMenu,
5362 UINT uPosition,
5363 UINT uFlags,
5364 UINT_PTR uIDNewItem,
5365 LPCWSTR lpNewItem)
5366 {
5367 MENUITEMINFOW mii;
5368 memset( &mii, 0, sizeof(mii) );
5369 mii.cbSize = sizeof(MENUITEMINFOW);
5370 mii.fMask = MIIM_FTYPE;
5371
5372 MenuSetItemData( &mii,
5373 uFlags,
5374 uIDNewItem,
5375 lpNewItem,
5376 TRUE);
5377
5378 return InsertMenuItemW(hMenu, uPosition, (BOOL)((MF_BYPOSITION & uFlags) > 0), &mii);
5379 }
5380
5381 /*
5382 * @implemented
5383 */
5384 BOOL
5385 WINAPI
5386 IsMenu(
5387 HMENU Menu)
5388 {
5389 if (ValidateHandle(Menu, TYPE_MENU)) return TRUE;
5390 return FALSE;
5391 }
5392
5393 /*
5394 * @implemented
5395 */
5396 HMENU WINAPI
5397 LoadMenuA(HINSTANCE hInstance,
5398 LPCSTR lpMenuName)
5399 {
5400 HANDLE Resource = FindResourceA(hInstance, lpMenuName, MAKEINTRESOURCEA(4));
5401 if (Resource == NULL)
5402 {
5403 return(NULL);
5404 }
5405 return(LoadMenuIndirectA((PVOID)LoadResource(hInstance, Resource)));
5406 }
5407
5408 /*
5409 * @implemented
5410 */
5411 HMENU WINAPI
5412 LoadMenuIndirectA(CONST MENUTEMPLATE *lpMenuTemplate)
5413 {
5414 return(LoadMenuIndirectW(lpMenuTemplate));
5415 }
5416
5417 /*
5418 * @implemented
5419 */
5420 HMENU WINAPI
5421 LoadMenuIndirectW(CONST MENUTEMPLATE *lpMenuTemplate)
5422 {
5423 HMENU hMenu;
5424 WORD version, offset;
5425 LPCSTR p = (LPCSTR)lpMenuTemplate;
5426
5427 version = GET_WORD(p);
5428 p += sizeof(WORD);
5429
5430 switch (version)
5431 {
5432 case 0: /* standard format is version of 0 */
5433 offset = GET_WORD(p);
5434 p += sizeof(WORD) + offset;
5435 if (!(hMenu = CreateMenu())) return 0;
5436 if (!MENU_ParseResource(p, hMenu))
5437 {
5438 DestroyMenu(hMenu);
5439 return 0;
5440 }
5441 return hMenu;
5442 case 1: /* extended format is version of 1 */
5443 offset = GET_WORD(p);
5444 p += sizeof(WORD) + offset;
5445 if (!(hMenu = CreateMenu())) return 0;
5446 if (!MENUEX_ParseResource(p, hMenu))
5447 {
5448 DestroyMenu( hMenu );
5449 return 0;
5450 }
5451 return hMenu;
5452 default:
5453 ERR("Menu template version %d not supported.\n", version);
5454 return 0;
5455 }
5456 }
5457
5458 /*
5459 * @implemented
5460 */
5461 HMENU WINAPI
5462 LoadMenuW(HINSTANCE hInstance,
5463 LPCWSTR lpMenuName)
5464 {
5465 HANDLE Resource = FindResourceW(hInstance, lpMenuName, RT_MENU);
5466 if (Resource == NULL)
5467 {
5468 return(NULL);
5469 }
5470 return(LoadMenuIndirectW((PVOID)LoadResource(hInstance, Resource)));
5471 }
5472
5473 /*
5474 * @implemented
5475 */
5476 int
5477 WINAPI
5478 MenuItemFromPoint(
5479 HWND hWnd,
5480 HMENU hMenu,
5481 POINT ptScreen)
5482 {
5483 return NtUserMenuItemFromPoint(hWnd, hMenu, ptScreen.x, ptScreen.y);
5484 }
5485
5486 /*
5487 * @implemented
5488 */
5489 BOOL
5490 WINAPI
5491 ModifyMenuA(
5492 HMENU hMnu,
5493 UINT uPosition,
5494 UINT uFlags,
5495 UINT_PTR uIDNewItem,
5496 LPCSTR lpNewItem)
5497 {
5498 MENUITEMINFOA mii;
5499 memset( &mii, 0, sizeof(mii) );
5500 mii.cbSize = sizeof(MENUITEMINFOA);
5501 mii.fMask = MIIM_FTYPE;
5502
5503 MenuSetItemData((LPMENUITEMINFOW) &mii,
5504 uFlags,
5505 uIDNewItem,
5506 (LPCWSTR) lpNewItem,
5507 FALSE);
5508
5509 //if (mii.hSubMenu && (uFlags & MF_POPUP) && (mii.hSubMenu != (HMENU)uIDNewItem))
5510 // NtUserDestroyMenu( mii.hSubMenu ); /* ModifyMenu() spec */
5511
5512 return SetMenuItemInfoA( hMnu,
5513 uPosition,
5514 (BOOL)(MF_BYPOSITION & uFlags),
5515 &mii);
5516 }
5517
5518 /*
5519 * @implemented
5520 */
5521 BOOL
5522 WINAPI
5523 ModifyMenuW(
5524 HMENU hMnu,
5525 UINT uPosition,
5526 UINT uFlags,
5527 UINT_PTR uIDNewItem,
5528 LPCWSTR lpNewItem)
5529 {
5530 MENUITEMINFOW mii;
5531 memset ( &mii, 0, sizeof(mii) );
5532 mii.cbSize = sizeof(MENUITEMINFOW);
5533 mii.fMask = MIIM_FTYPE;
5534
5535 /* Init new data for this menu item */
5536 MenuSetItemData( &mii,
5537 uFlags,
5538 uIDNewItem,
5539 lpNewItem,
5540 TRUE);
5541
5542 //if (mii.hSubMenu && (uFlags & MF_POPUP) && (mii.hSubMenu != (HMENU)uIDNewItem))
5543 // NtUserDestroyMenu( mii.hSubMenu ); /* ModifyMenu() spec */
5544
5545 return SetMenuItemInfoW( hMnu,
5546 uPosition,
5547 (BOOL)(MF_BYPOSITION & uFlags),
5548 &mii);
5549 }
5550
5551 /*
5552 * @implemented
5553 */
5554 BOOL WINAPI
5555 SetMenu(HWND hWnd,
5556 HMENU hMenu)
5557 {
5558 return NtUserSetMenu(hWnd, hMenu, TRUE);
5559 }
5560
5561 /*
5562 * @implemented
5563 */
5564 BOOL
5565 WINAPI
5566 SetMenuInfo(
5567 HMENU hmenu,
5568 LPCMENUINFO lpcmi)
5569 {
5570 ROSMENUINFO mi;
5571 BOOL res = FALSE;
5572
5573 if (!lpcmi || (lpcmi->cbSize != sizeof(MENUINFO)))
5574 {
5575 SetLastError(ERROR_INVALID_PARAMETER);
5576 return res;
5577 }
5578
5579 memcpy(&mi, lpcmi, sizeof(MENUINFO));
5580 return NtUserThunkedMenuInfo(hmenu, (LPCMENUINFO)&mi);
5581 }
5582
5583 /*
5584 * @implemented
5585 */
5586 BOOL
5587 WINAPI
5588 SetMenuItemBitmaps(
5589 HMENU hMenu,
5590 UINT uPosition,
5591 UINT uFlags,
5592 HBITMAP hBitmapUnchecked,
5593 HBITMAP hBitmapChecked)
5594 {
5595 MENUITEMINFOW uItem;
5596 memset ( &uItem, 0, sizeof(uItem) );
5597 uItem.cbSize = sizeof(MENUITEMINFOW);
5598 uItem.fMask = MIIM_CHECKMARKS;
5599 uItem.hbmpUnchecked = hBitmapUnchecked;
5600 uItem.hbmpChecked = hBitmapChecked;
5601 return SetMenuItemInfoW(hMenu, uPosition, (BOOL)(uFlags & MF_BYPOSITION), &uItem);
5602 }
5603
5604 /*
5605 * @implemented
5606 */
5607 BOOL
5608 WINAPI
5609 SetMenuItemInfoA(
5610 HMENU hmenu,
5611 UINT item,
5612 BOOL bypos,
5613 LPCMENUITEMINFOA lpmii)
5614 {
5615 MENUITEMINFOW mii;
5616 UNICODE_STRING UnicodeString;
5617 BOOL Ret;
5618
5619 TRACE("hmenu %p, item %u, by pos %d, info %p\n", hmenu, item, bypos, lpmii);
5620
5621 RtlInitUnicodeString(&UnicodeString, 0);
5622
5623 if (!MENU_NormalizeMenuItemInfoStruct( (const MENUITEMINFOW *)lpmii, &mii )) return FALSE;
5624 /*
5625 * MIIM_STRING == good
5626 * MIIM_TYPE & MFT_STRING == good
5627 * MIIM_STRING & MFT_STRING == good
5628 * MIIM_STRING & MFT_OWNERDRAW == good
5629 */
5630 if (((mii.fMask & MIIM_STRING) ||
5631 ((mii.fMask & MIIM_TYPE) && (MENU_ITEM_TYPE(mii.fType) == MF_STRING)))
5632 && mii.dwTypeData && !(GdiValidateHandle((HGDIOBJ)mii.dwTypeData)) )
5633 {
5634 /* cch is ignored when the content of a menu item is set by calling SetMenuItemInfo. */
5635 if (!RtlCreateUnicodeStringFromAsciiz(&UnicodeString, (LPSTR)mii.dwTypeData))
5636 {
5637 SetLastError (ERROR_NOT_ENOUGH_MEMORY);
5638 return FALSE;
5639 }
5640 mii.dwTypeData = UnicodeString.Buffer;
5641 mii.cch = UnicodeString.Length / sizeof(WCHAR);
5642 }
5643 else
5644 {
5645 UnicodeString.Buffer = NULL;
5646 }
5647 Ret = NtUserThunkedMenuItemInfo(hmenu, item, bypos, FALSE, &mii, &UnicodeString);
5648 if (UnicodeString.Buffer != NULL) RtlFreeUnicodeString(&UnicodeString);
5649 return Ret;
5650 }
5651
5652 /*
5653 * @implemented
5654 */
5655 BOOL
5656 WINAPI
5657 SetMenuItemInfoW(
5658 HMENU hMenu,
5659 UINT uItem,
5660 BOOL fByPosition,
5661 LPCMENUITEMINFOW lpmii)
5662 {
5663 MENUITEMINFOW MenuItemInfoW;
5664 UNICODE_STRING UnicodeString;
5665 BOOL Ret;
5666
5667 TRACE("hmenu %p, item %u, by pos %d, info %p\n", hMenu, uItem, fByPosition, lpmii);
5668
5669 RtlInitUnicodeString(&UnicodeString, 0);
5670
5671 if (!MENU_NormalizeMenuItemInfoStruct( (const MENUITEMINFOW *)lpmii, &MenuItemInfoW )) return FALSE;
5672
5673 if (((MenuItemInfoW.fMask & MIIM_STRING) ||
5674 ((MenuItemInfoW.fMask & MIIM_TYPE) &&
5675 (MENU_ITEM_TYPE(MenuItemInfoW.fType) == MF_STRING)))
5676 && MenuItemInfoW.dwTypeData && !(GdiValidateHandle((HGDIOBJ)MenuItemInfoW.dwTypeData)) )
5677 {
5678 RtlInitUnicodeString(&UnicodeString, (PCWSTR)MenuItemInfoW.dwTypeData);
5679 MenuItemInfoW.cch = strlenW(MenuItemInfoW.dwTypeData);
5680 }
5681 Ret = NtUserThunkedMenuItemInfo(hMenu, uItem, fByPosition, FALSE, &MenuItemInfoW, &UnicodeString);
5682
5683 return Ret;
5684 }
5685
5686 /*
5687 * @implemented
5688 */
5689 BOOL
5690 WINAPI
5691 SetSystemMenu (
5692 HWND hwnd,
5693 HMENU hMenu)
5694 {
5695 if(!hwnd)
5696 {
5697 SetLastError(ERROR_INVALID_WINDOW_HANDLE);
5698 return FALSE;
5699 }
5700 if(!hMenu)
5701 {
5702 SetLastError(ERROR_INVALID_MENU_HANDLE);
5703 return FALSE;
5704 }
5705 return NtUserSetSystemMenu(hwnd, hMenu);
5706 }
5707
5708 //
5709 // Example for the Win32/User32 rewrite.
5710 // Def = TrackPopupMenuEx@24=NtUserTrackPopupMenuEx@24
5711 //
5712 //
5713 BOOL
5714 WINAPI
5715 NEWTrackPopupMenu(
5716 HMENU Menu,
5717 UINT Flags,
5718 int x,
5719 int y,
5720 int Reserved,
5721 HWND Wnd,
5722 CONST RECT *Rect)
5723 {
5724 return NtUserTrackPopupMenuEx( Menu,
5725 Flags,
5726 x,
5727 y,
5728 Wnd,
5729 NULL); // LPTPMPARAMS is null
5730 }
5731
5732
5733
5734 /*
5735 * @unimplemented
5736 */
5737 BOOL
5738 WINAPI
5739 MenuWindowProcA(
5740 HWND hWnd,
5741 ULONG_PTR Result,
5742 UINT Msg,
5743 WPARAM wParam,
5744 LPARAM lParam
5745 )
5746 {
5747 if ( Msg < WM_USER)
5748 {
5749 LRESULT lResult;
5750 lResult = PopupMenuWndProcA(hWnd, Msg, wParam, lParam );
5751 if (Result)
5752 {
5753 Result = (ULONG_PTR)lResult;
5754 return TRUE;
5755 }
5756 return FALSE;
5757 }
5758 return NtUserMessageCall(hWnd, Msg, wParam, lParam, Result, FNID_MENU, TRUE);
5759
5760 }
5761
5762 /*
5763 * @unimplemented
5764 */
5765 BOOL
5766 WINAPI
5767 MenuWindowProcW(
5768 HWND hWnd,
5769 ULONG_PTR Result,
5770 UINT Msg,
5771 WPARAM wParam,
5772 LPARAM lParam
5773 )
5774 {
5775 if ( Msg < WM_USER)
5776 {
5777 LRESULT lResult;
5778 lResult = PopupMenuWndProcW(hWnd, Msg, wParam, lParam );
5779 if (Result)
5780 {
5781 Result = (ULONG_PTR)lResult;
5782 return TRUE;
5783 }
5784 return FALSE;
5785 }
5786 return NtUserMessageCall(hWnd, Msg, wParam, lParam, Result, FNID_MENU, FALSE);
5787 }
5788
5789 /*
5790 * @implemented
5791 */
5792 BOOL
5793 WINAPI
5794 ChangeMenuW(
5795 HMENU hMenu,
5796 UINT cmd,
5797 LPCWSTR lpszNewItem,
5798 UINT cmdInsert,
5799 UINT flags)
5800 {
5801 /*
5802 FIXME: Word passes the item id in 'cmd' and 0 or 0xffff as cmdInsert
5803 for MF_DELETE. We should check the parameters for all others
5804 MF_* actions also (anybody got a doc on ChangeMenu?).
5805 */
5806
5807 switch(flags & (MF_APPEND | MF_DELETE | MF_CHANGE | MF_REMOVE | MF_INSERT))
5808 {
5809 case MF_APPEND :
5810 return AppendMenuW(hMenu, flags &~ MF_APPEND, cmdInsert, lpszNewItem);
5811
5812 case MF_DELETE :
5813 return DeleteMenu(hMenu, cmd, flags &~ MF_DELETE);
5814
5815 case MF_CHANGE :
5816 return ModifyMenuW(hMenu, cmd, flags &~ MF_CHANGE, cmdInsert, lpszNewItem);
5817
5818 case MF_REMOVE :
5819 return RemoveMenu(hMenu, flags & MF_BYPOSITION ? cmd : cmdInsert,
5820 flags &~ MF_REMOVE);
5821
5822 default : /* MF_INSERT */
5823 return InsertMenuW(hMenu, cmd, flags, cmdInsert, lpszNewItem);
5824 };
5825 }
5826
5827 /*
5828 * @implemented
5829 */
5830 BOOL
5831 WINAPI
5832 ChangeMenuA(
5833 HMENU hMenu,
5834 UINT cmd,
5835 LPCSTR lpszNewItem,
5836 UINT cmdInsert,
5837 UINT flags)
5838 {
5839 /*
5840 FIXME: Word passes the item id in 'cmd' and 0 or 0xffff as cmdInsert
5841 for MF_DELETE. We should check the parameters for all others
5842 MF_* actions also (anybody got a doc on ChangeMenu?).
5843 */
5844
5845 switch(flags & (MF_APPEND | MF_DELETE | MF_CHANGE | MF_REMOVE | MF_INSERT))
5846 {
5847 case MF_APPEND :
5848 return AppendMenuA(hMenu, flags &~ MF_APPEND, cmdInsert, lpszNewItem);
5849
5850 case MF_DELETE :
5851 return DeleteMenu(hMenu, cmd, flags &~ MF_DELETE);
5852
5853 case MF_CHANGE :
5854 return ModifyMenuA(hMenu, cmd, flags &~ MF_CHANGE, cmdInsert, lpszNewItem);
5855
5856 case MF_REMOVE :
5857 return RemoveMenu(hMenu, flags & MF_BYPOSITION ? cmd : cmdInsert,
5858 flags &~ MF_REMOVE);
5859
5860 default : /* MF_INSERT */
5861 return InsertMenuA(hMenu, cmd, flags, cmdInsert, lpszNewItem);
5862 };
5863 }
5864