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