ebd1898150da4a7873cb011b278bfe310ab6d4c5
[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 2 //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 NULL, /* FIXME - procA */
111 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 if( unicode)
299 *((WCHAR *)lpmii->dwTypeData) = 0;
300 else
301 *((CHAR *)lpmii->dwTypeData) = 0;
302 }
303 lpmii->cch = 0;
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->iTop;
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 (BmpSysMenu == NULL)
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 = 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 = WS_EX_TOOLWINDOW;
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 //ERR("Scroll Down iTop %d iMaxTop %d nMaxHeight %d\n",lppop->iTop,lppop->iMaxTop,nMaxHeight);
2195 }
2196 else if (item->Rect.top - MENU_TOP_MARGIN < lppop->iTop)
2197 {
2198 lppop->iTop = item->Rect.top - MENU_TOP_MARGIN;
2199 ScrollWindow(lppop->Wnd, 0, nOldPos - lppop->iTop, &rc, &rc);
2200 MENU_DrawScrollArrows(lppop, hdc);
2201 //ERR("Scroll Up iTop %d iMaxTop %d nMaxHeight %d\n",lppop->iTop,lppop->iMaxTop,nMaxHeight);
2202 }
2203 MenuSetRosMenuInfo(lppop);
2204 }
2205 }
2206
2207 /***********************************************************************
2208 * MenuSelectItem
2209 */
2210 static void FASTCALL MenuSelectItem(HWND hwndOwner, PROSMENUINFO hmenu, UINT wIndex,
2211 BOOL sendMenuSelect, HMENU topmenu)
2212 {
2213 ROSMENUITEMINFO ItemInfo;
2214 ROSMENUINFO TopMenuInfo;
2215 HDC hdc;
2216
2217 TRACE("owner=%p menu=%p index=0x%04x select=0x%04x\n", hwndOwner, hmenu, wIndex, sendMenuSelect);
2218
2219 if (!hmenu || !hmenu->cItems || !hmenu->Wnd) return;
2220 if (hmenu->iItem == wIndex) return;
2221 if (hmenu->fFlags & MNF_POPUP) hdc = GetDC(hmenu->Wnd);
2222 else hdc = GetDCEx(hmenu->Wnd, 0, DCX_CACHE | DCX_WINDOW);
2223 if (!top_popup) {
2224 top_popup = hmenu->Wnd;
2225 top_popup_hmenu = hmenu->Self;
2226 }
2227
2228 SelectObject( hdc, hMenuFont );
2229
2230 MenuInitRosMenuItemInfo(&ItemInfo);
2231
2232 /* Clear previous highlighted item */
2233 if (hmenu->iItem != NO_SELECTED_ITEM)
2234 {
2235 if (MenuGetRosMenuItemInfo(hmenu->Self, hmenu->iItem, &ItemInfo))
2236 {
2237 ItemInfo.fMask |= MIIM_STATE;
2238 ItemInfo.fState &= ~(MF_HILITE|MF_MOUSESELECT);
2239 MenuSetRosMenuItemInfo(hmenu->Self, hmenu->iItem, &ItemInfo);
2240 }
2241 MenuDrawMenuItem(hmenu->Wnd, hmenu, hwndOwner, hdc, &ItemInfo,
2242 hmenu->cyMenu, !(hmenu->fFlags & MNF_POPUP),
2243 ODA_SELECT);
2244 }
2245
2246 /* Highlight new item (if any) */
2247 hmenu->iItem = wIndex;
2248 MenuSetRosMenuInfo(hmenu);
2249 if (hmenu->iItem != NO_SELECTED_ITEM)
2250 {
2251 if (MenuGetRosMenuItemInfo(hmenu->Self, hmenu->iItem, &ItemInfo))
2252 {
2253 if (!(ItemInfo.fType & MF_SEPARATOR))
2254 {
2255 ItemInfo.fMask |= MIIM_STATE;
2256 ItemInfo.fState |= MF_HILITE;
2257 MenuSetRosMenuItemInfo(hmenu->Self, hmenu->iItem, &ItemInfo);
2258 MENU_EnsureMenuItemVisible(hmenu, &ItemInfo, hdc);
2259 MenuDrawMenuItem(hmenu->Wnd, hmenu, hwndOwner, hdc,
2260 &ItemInfo, hmenu->cyMenu, !(hmenu->fFlags & MNF_POPUP), ODA_SELECT);
2261 }
2262 if (sendMenuSelect)
2263 {
2264 WPARAM wParam = MAKEWPARAM( ItemInfo.hSubMenu ? wIndex : ItemInfo.wID,
2265 ItemInfo.fType | ItemInfo.fState |
2266 (ItemInfo.hSubMenu ? MF_POPUP : 0) |
2267 (hmenu->fFlags & MNF_SYSMENU ? MF_SYSMENU : 0 ) );
2268
2269 SendMessageW(hwndOwner, WM_MENUSELECT, wParam, (LPARAM) hmenu->Self);
2270 }
2271 }
2272 }
2273 else if (sendMenuSelect)
2274 {
2275 if(topmenu)
2276 {
2277 int pos;
2278 pos = MenuFindSubMenu(&topmenu, hmenu->Self);
2279 if (pos != NO_SELECTED_ITEM)
2280 {
2281 if (MenuGetRosMenuInfo(&TopMenuInfo, topmenu)
2282 && MenuGetRosMenuItemInfo(topmenu, pos, &ItemInfo))
2283 {
2284 WPARAM wParam = MAKEWPARAM( Pos, ItemInfo.fType | ItemInfo.fState |
2285 (ItemInfo.hSubMenu ? MF_POPUP : 0) |
2286 (TopMenuInfo.fFlags & MNF_SYSMENU ? MF_SYSMENU : 0 ) );
2287
2288 SendMessageW(hwndOwner, WM_MENUSELECT, wParam, (LPARAM) topmenu);
2289 }
2290 }
2291 }
2292 }
2293 MenuCleanupRosMenuItemInfo(&ItemInfo);
2294 ReleaseDC(hmenu->Wnd, hdc);
2295 }
2296
2297 /***********************************************************************
2298 * MenuMoveSelection
2299 *
2300 * Moves currently selected item according to the Offset parameter.
2301 * If there is no selection then it should select the last item if
2302 * Offset is ITEM_PREV or the first item if Offset is ITEM_NEXT.
2303 */
2304 static void FASTCALL
2305 MenuMoveSelection(HWND WndOwner, PROSMENUINFO MenuInfo, INT Offset)
2306 {
2307 INT i;
2308 ROSMENUITEMINFO ItemInfo;
2309 INT OrigPos;
2310
2311 TRACE("hwnd=%x menu=%x off=0x%04x\n", WndOwner, MenuInfo, Offset);
2312
2313 /* Prevent looping */
2314 if (0 == MenuInfo->cItems || 0 == Offset)
2315 return;
2316 else if (Offset < -1)
2317 Offset = -1;
2318 else if (Offset > 1)
2319 Offset = 1;
2320
2321 MenuInitRosMenuItemInfo(&ItemInfo);
2322
2323 OrigPos = MenuInfo->iItem;
2324 if (OrigPos == NO_SELECTED_ITEM) /* NO_SELECTED_ITEM is not -1 ! */
2325 {
2326 OrigPos = 0;
2327 i = -1;
2328 }
2329 else
2330 {
2331 i = MenuInfo->iItem;
2332 }
2333
2334 do
2335 {
2336 /* Step */
2337 i += Offset;
2338 /* Clip and wrap around */
2339 if (i < 0)
2340 {
2341 i = MenuInfo->cItems - 1;
2342 }
2343 else if (i >= MenuInfo->cItems)
2344 {
2345 i = 0;
2346 }
2347 /* If this is a good candidate; */
2348 if (MenuGetRosMenuItemInfo(MenuInfo->Self, i, &ItemInfo) &&
2349 0 == (ItemInfo.fType & MF_SEPARATOR))
2350 {
2351 MenuSelectItem(WndOwner, MenuInfo, i, TRUE, NULL);
2352 MenuCleanupRosMenuItemInfo(&ItemInfo);
2353 return;
2354 }
2355 } while (i != OrigPos);
2356
2357 /* Not found */
2358 MenuCleanupRosMenuItemInfo(&ItemInfo);
2359 }
2360
2361 #if 0
2362 LRESULT WINAPI
2363 PopupMenuWndProcW(HWND Wnd, UINT Message, WPARAM wParam, LPARAM lParam)
2364 {
2365 #ifdef __REACTOS__ // Do this now, remove after Server side is fixed.
2366 PWND pWnd;
2367 PPOPUPMENU pPopupMenu;
2368
2369 pWnd = ValidateHwndNoErr(Wnd);
2370 if (pWnd)
2371 {
2372 if (!pWnd->fnid)
2373 {
2374 if (Message != WM_NCCREATE)
2375 {
2376 return DefWindowProcW(Wnd, Message, wParam, lParam);
2377 }
2378 NtUserSetWindowFNID(Wnd, FNID_MENU);
2379 pPopupMenu = HeapAlloc( GetProcessHeap(), 0, sizeof(POPUPMENU) );
2380 pPopupMenu->spwndPopupMenu = pWnd;
2381 SetWindowLongPtrW(Wnd, 0, (LONG_PTR)pPopupMenu);
2382 }
2383 else
2384 {
2385 if (pWnd->fnid != FNID_MENU)
2386 {
2387 ERR("Wrong window class for Menu!\n");
2388 return 0;
2389 }
2390 pPopupMenu = ((PMENUWND)pWnd)->ppopupmenu;
2391 }
2392 }
2393 #endif
2394
2395 TRACE("hwnd=%x msg=0x%04x wp=0x%04lx lp=0x%08lx\n", Wnd, Message, wParam, lParam);
2396
2397 switch(Message)
2398 {
2399 case WM_CREATE:
2400 {
2401 CREATESTRUCTW *cs = (CREATESTRUCTW *) lParam;
2402 pPopupMenu->spmenu = ValidateHandle(cs->lpCreateParams, TYPE_MENU);
2403 return 0;
2404 }
2405
2406 case WM_MOUSEACTIVATE: /* We don't want to be activated */
2407 return MA_NOACTIVATE;
2408
2409 case WM_PAINT:
2410 {
2411 PAINTSTRUCT ps;
2412 BeginPaint(Wnd, &ps);
2413 MenuDrawPopupMenu(Wnd, ps.hdc, pPopupMenu->spmenu->head.h);
2414 EndPaint(Wnd, &ps);
2415 return 0;
2416 }
2417
2418 case WM_PRINTCLIENT:
2419 {
2420 MenuDrawPopupMenu( Wnd, (HDC)wParam, pPopupMenu->spmenu->head.h);
2421 return 0;
2422 }
2423
2424 case WM_ERASEBKGND:
2425 return 1;
2426
2427 case WM_DESTROY:
2428 /* zero out global pointer in case resident popup window was destroyed. */
2429 if (Wnd == top_popup)
2430 {
2431 top_popup = NULL;
2432 top_popup_hmenu = NULL;
2433 }
2434 break;
2435
2436 case WM_NCDESTROY:
2437 {
2438 HeapFree( GetProcessHeap(), 0, pPopupMenu );
2439 SetWindowLongPtrW(Wnd, 0, 0);
2440 NtUserSetWindowFNID(Wnd, FNID_DESTROY);
2441 break;
2442 }
2443
2444 case WM_SHOWWINDOW:
2445 if (0 != wParam)
2446 {
2447 if (!pPopupMenu || !pPopupMenu->spmenu)
2448 {
2449 OutputDebugStringA("no menu to display\n");
2450 }
2451 }
2452 /*else
2453 {
2454 pPopupMenu->spmenu = NULL; ///// WTF?
2455 }*/
2456 break;
2457
2458 case MM_SETMENUHANDLE:
2459 {
2460 PMENU pmenu = ValidateHandle((HMENU)wParam, TYPE_MENU);
2461 if (!pmenu)
2462 {
2463 ERR("Bad Menu Handle\n");
2464 break;
2465 }
2466 pPopupMenu->spmenu = pmenu;
2467 break;
2468 }
2469
2470 case MM_GETMENUHANDLE:
2471 case MN_GETHMENU:
2472 return (LRESULT)(pPopupMenu ? (pPopupMenu->spmenu ? pPopupMenu->spmenu->head.h : NULL) : NULL);
2473
2474 default:
2475 return DefWindowProcW(Wnd, Message, wParam, lParam);
2476 }
2477
2478 return 0;
2479 }
2480 #endif
2481
2482 LRESULT WINAPI
2483 PopupMenuWndProcW(HWND Wnd, UINT Message, WPARAM wParam, LPARAM lParam)
2484 {
2485 #ifdef __REACTOS__ // Do this now, remove after Server side is fixed.
2486 PWND pWnd;
2487
2488 pWnd = ValidateHwnd(Wnd);
2489 if (pWnd)
2490 {
2491 if (!pWnd->fnid)
2492 {
2493 if (Message != WM_NCCREATE)
2494 {
2495 return DefWindowProcW(Wnd, Message, wParam, lParam);
2496 }
2497 NtUserSetWindowFNID(Wnd, FNID_MENU);
2498 }
2499 else
2500 {
2501 if (pWnd->fnid != FNID_MENU)
2502 {
2503 ERR("Wrong window class for Menu!\n");
2504 return 0;
2505 }
2506 }
2507 }
2508 #endif
2509
2510 TRACE("hwnd=%x msg=0x%04x wp=0x%04lx lp=0x%08lx\n", Wnd, Message, wParam, lParam);
2511
2512 switch(Message)
2513 {
2514 case WM_CREATE:
2515 {
2516 CREATESTRUCTW *cs = (CREATESTRUCTW *) lParam;
2517 SetWindowLongPtrW(Wnd, 0, (LONG_PTR)cs->lpCreateParams);
2518 return 0;
2519 }
2520
2521 case WM_MOUSEACTIVATE: /* We don't want to be activated */
2522 return MA_NOACTIVATE;
2523
2524 case WM_PAINT:
2525 {
2526 PAINTSTRUCT ps;
2527 BeginPaint(Wnd, &ps);
2528 MenuDrawPopupMenu(Wnd, ps.hdc, (HMENU)GetWindowLongPtrW(Wnd, 0));
2529 EndPaint(Wnd, &ps);
2530 return 0;
2531 }
2532
2533 case WM_PRINTCLIENT:
2534 {
2535 MenuDrawPopupMenu( Wnd, (HDC)wParam,
2536 (HMENU)GetWindowLongPtrW( Wnd, 0 ) );
2537 return 0;
2538 }
2539
2540 case WM_ERASEBKGND:
2541 return 1;
2542
2543 case WM_DESTROY:
2544 /* zero out global pointer in case resident popup window was destroyed. */
2545 if (Wnd == top_popup)
2546 {
2547 top_popup = NULL;
2548 top_popup_hmenu = NULL;
2549 }
2550 break;
2551
2552 #ifdef __REACTOS__
2553 case WM_NCDESTROY:
2554 NtUserSetWindowFNID(Wnd, FNID_DESTROY);
2555 break;
2556 #endif
2557
2558 case WM_SHOWWINDOW:
2559 if (0 != wParam)
2560 {
2561 if (0 == GetWindowLongPtrW(Wnd, 0))
2562 {
2563 OutputDebugStringA("no menu to display\n");
2564 }
2565 }
2566 else
2567 {
2568 SetWindowLongPtrW(Wnd, 0, 0);
2569 }
2570 break;
2571
2572 case MM_SETMENUHANDLE:
2573 SetWindowLongPtrW(Wnd, 0, wParam);
2574 break;
2575
2576 case MM_GETMENUHANDLE:
2577 case MN_GETHMENU:
2578 return GetWindowLongPtrW(Wnd, 0);
2579
2580 default:
2581 return DefWindowProcW(Wnd, Message, wParam, lParam);
2582 }
2583
2584 return 0;
2585 }
2586
2587 //
2588 // This breaks some test results. Should handle A2U if called!
2589 //
2590 LRESULT WINAPI PopupMenuWndProcA(HWND Wnd, UINT Message, WPARAM wParam, LPARAM lParam)
2591 {
2592 PWND pWnd;
2593
2594 pWnd = ValidateHwnd(Wnd);
2595 if (pWnd && !pWnd->fnid && Message != WM_NCCREATE)
2596 {
2597 return DefWindowProcA(Wnd, Message, wParam, lParam);
2598 }
2599 TRACE("YES! hwnd=%x msg=0x%04x wp=0x%04lx lp=0x%08lx\n", Wnd, Message, wParam, lParam);
2600
2601 switch(Message)
2602 {
2603 case WM_NCCREATE:
2604 case WM_CREATE:
2605 case WM_MOUSEACTIVATE:
2606 case WM_PAINT:
2607 case WM_PRINTCLIENT:
2608 case WM_ERASEBKGND:
2609 case WM_DESTROY:
2610 case WM_NCDESTROY:
2611 case WM_SHOWWINDOW:
2612 case MM_SETMENUHANDLE:
2613 case MM_GETMENUHANDLE:
2614 case MN_GETHMENU:
2615 return PopupMenuWndProcW(Wnd, Message, wParam, lParam);
2616
2617 default:
2618 return DefWindowProcA(Wnd, Message, wParam, lParam);
2619 }
2620 return 0;
2621 }
2622
2623 /**********************************************************************
2624 * MENU_ParseResource
2625 *
2626 * Parse a standard menu resource and add items to the menu.
2627 * Return a pointer to the end of the resource.
2628 *
2629 * NOTE: flags is equivalent to the mtOption field
2630 */
2631 static LPCSTR MENU_ParseResource( LPCSTR res, HMENU hMenu)
2632 {
2633 WORD flags, id = 0;
2634 HMENU hSubMenu;
2635 LPCWSTR str;
2636 BOOL end = FALSE;
2637
2638 do
2639 {
2640 flags = GET_WORD(res);
2641
2642 /* remove MF_END flag before passing it to AppendMenu()! */
2643 end = (flags & MF_END);
2644 if(end) flags ^= MF_END;
2645
2646 res += sizeof(WORD);
2647 if(!(flags & MF_POPUP))
2648 {
2649 id = GET_WORD(res);
2650 res += sizeof(WORD);
2651 }
2652 str = (LPCWSTR)res;
2653 res += (strlenW(str) + 1) * sizeof(WCHAR);
2654
2655 if (flags & MF_POPUP)
2656 {
2657 hSubMenu = CreatePopupMenu();
2658 if(!hSubMenu) return NULL;
2659 if(!(res = MENU_ParseResource(res, hSubMenu))) return NULL;
2660 AppendMenuW(hMenu, flags, (UINT_PTR)hSubMenu, (LPCWSTR)str);
2661 }
2662 else /* Not a popup */
2663 {
2664 AppendMenuW(hMenu, flags, id, *(LPCWSTR)str ? (LPCWSTR)str : NULL);
2665 }
2666 } while(!end);
2667 return res;
2668 }
2669
2670 /**********************************************************************
2671 * MENUEX_ParseResource
2672 *
2673 * Parse an extended menu resource and add items to the menu.
2674 * Return a pointer to the end of the resource.
2675 */
2676 static LPCSTR MENUEX_ParseResource(LPCSTR res, HMENU hMenu)
2677 {
2678 WORD resinfo;
2679 do
2680 {
2681 MENUITEMINFOW mii;
2682
2683 mii.cbSize = sizeof(mii);
2684 mii.fMask = MIIM_STATE | MIIM_ID | MIIM_TYPE;
2685 mii.fType = GET_DWORD(res);
2686 res += sizeof(DWORD);
2687 mii.fState = GET_DWORD(res);
2688 res += sizeof(DWORD);
2689 mii.wID = GET_DWORD(res);
2690 res += sizeof(DWORD);
2691 resinfo = GET_WORD(res);
2692 res += sizeof(WORD);
2693 /* Align the text on a word boundary. */
2694 res += (~((UINT_PTR)res - 1)) & 1;
2695 mii.dwTypeData = (LPWSTR)res;
2696 mii.cch = strlenW(mii.dwTypeData);
2697 res += (1 + strlenW(mii.dwTypeData)) * sizeof(WCHAR);
2698 /* Align the following fields on a dword boundary. */
2699 res += (~((UINT_PTR)res - 1)) & 3;
2700
2701 TRACE("Menu item: [%08x,%08x,%04x,%04x,%S]\n",
2702 mii.fType, mii.fState, mii.wID, resinfo, mii.dwTypeData);
2703
2704 if (resinfo & 1) /* Pop-up? */
2705 {
2706 /* DWORD helpid = GET_DWORD(res); FIXME: use this. */
2707 res += sizeof(DWORD);
2708 mii.hSubMenu = CreatePopupMenu();
2709 if (!mii.hSubMenu)
2710 {
2711 ERR("CreatePopupMenu failed\n");
2712 return NULL;
2713 }
2714
2715 if (!(res = MENUEX_ParseResource(res, mii.hSubMenu)))
2716 {
2717 ERR("MENUEX_ParseResource failed\n");
2718 DestroyMenu(mii.hSubMenu);
2719 return NULL;
2720 }
2721 mii.fMask |= MIIM_SUBMENU;
2722 }
2723 else if (!mii.dwTypeData[0] && !(mii.fType & MF_SEPARATOR))
2724 {
2725 WARN("Converting NULL menu item %04x, type %04x to SEPARATOR\n",
2726 mii.wID, mii.fType);
2727 mii.fType |= MF_SEPARATOR;
2728 }
2729 InsertMenuItemW(hMenu, -1, MF_BYPOSITION, &mii);
2730 } while (!(resinfo & MF_END));
2731 return res;
2732 }
2733
2734
2735 /***********************************************************************
2736 * DrawMenuBarTemp (USER32.@)
2737 *
2738 * UNDOCUMENTED !!
2739 *
2740 * called by W98SE desk.cpl Control Panel Applet
2741 *
2742 * Not 100% sure about the param names, but close.
2743 *
2744 * @implemented
2745 */
2746 DWORD WINAPI
2747 DrawMenuBarTemp(HWND Wnd, HDC DC, LPRECT Rect, HMENU Menu, HFONT Font)
2748 {
2749 ROSMENUINFO MenuInfo;
2750 ROSMENUITEMINFO ItemInfo;
2751 UINT i;
2752 HFONT FontOld = NULL;
2753 BOOL flat_menu = FALSE;
2754
2755 SystemParametersInfoW (SPI_GETFLATMENU, 0, &flat_menu, 0);
2756
2757 if (NULL == Menu)
2758 {
2759 Menu = GetMenu(Wnd);
2760 }
2761
2762 if (NULL == Font)
2763 {
2764 Font = hMenuFont;
2765 }
2766
2767 if (NULL == Rect || ! MenuGetRosMenuInfo(&MenuInfo, Menu))
2768 {
2769 return GetSystemMetrics(SM_CYMENU);
2770 }
2771
2772 TRACE("(%x, %x, %p, %x, %x)\n", Wnd, DC, Rect, Menu, Font);
2773
2774 FontOld = SelectObject(DC, Font);
2775
2776 if (0 == MenuInfo.cyMenu)
2777 {
2778 MenuMenuBarCalcSize(DC, Rect, &MenuInfo, Wnd);
2779 }
2780
2781 Rect->bottom = Rect->top + MenuInfo.cyMenu;
2782
2783 FillRect(DC, Rect, GetSysColorBrush(flat_menu ? COLOR_MENUBAR : COLOR_MENU));
2784
2785 SelectObject(DC, GetStockObject(DC_PEN));
2786 SetDCPenColor(DC, GetSysColor(COLOR_3DFACE));
2787 MoveToEx(DC, Rect->left, Rect->bottom - 1, NULL);
2788 LineTo(DC, Rect->right, Rect->bottom - 1);
2789
2790 if (0 == MenuInfo.cItems)
2791 {
2792 SelectObject(DC, FontOld);
2793 return GetSystemMetrics(SM_CYMENU);
2794 }
2795
2796 MenuInitRosMenuItemInfo(&ItemInfo);
2797 for (i = 0; i < MenuInfo.cItems; i++)
2798 {
2799 if (MenuGetRosMenuItemInfo(MenuInfo.Self, i, &ItemInfo))
2800 {
2801 MenuDrawMenuItem(Wnd, &MenuInfo, Wnd, DC, &ItemInfo,
2802 MenuInfo.cyMenu, TRUE, ODA_DRAWENTIRE);
2803 }
2804 }
2805 MenuCleanupRosMenuItemInfo(&ItemInfo);
2806
2807 SelectObject(DC, FontOld);
2808
2809 return MenuInfo.cyMenu;
2810 }
2811
2812
2813 /***********************************************************************
2814 * MenuShowSubPopup
2815 *
2816 * Display the sub-menu of the selected item of this menu.
2817 * Return the handle of the submenu, or menu if no submenu to display.
2818 */
2819 static HMENU FASTCALL
2820 MenuShowSubPopup(HWND WndOwner, PROSMENUINFO MenuInfo, BOOL SelectFirst, UINT Flags)
2821 {
2822 RECT Rect;
2823 ROSMENUITEMINFO ItemInfo;
2824 ROSMENUINFO SubMenuInfo;
2825 HDC Dc;
2826 HMENU Ret;
2827
2828 TRACE("owner=%x menu=%p 0x%04x\n", WndOwner, MenuInfo, SelectFirst);
2829
2830 if (MenuInfo->iItem == NO_SELECTED_ITEM) return MenuInfo->Self;
2831
2832 MenuInitRosMenuItemInfo(&ItemInfo);
2833 if (! MenuGetRosMenuItemInfo(MenuInfo->Self, MenuInfo->iItem, &ItemInfo))
2834 {
2835 MenuCleanupRosMenuItemInfo(&ItemInfo);
2836 return MenuInfo->Self;
2837 }
2838
2839 //item = &menu->rgItems[menu->iItem];
2840 if (!(ItemInfo.hSubMenu) || (ItemInfo.fState & (MF_GRAYED | MF_DISABLED)))
2841 {
2842 MenuCleanupRosMenuItemInfo(&ItemInfo);
2843 return MenuInfo->Self;
2844 }
2845
2846 /* message must be sent before using item,
2847 because nearly everything may be changed by the application ! */
2848
2849 /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
2850 if (!(Flags & TPM_NONOTIFY))
2851 {
2852 SendMessageW(WndOwner, WM_INITMENUPOPUP, (WPARAM) ItemInfo.hSubMenu,
2853 MAKELPARAM(MenuInfo->iItem, IS_SYSTEM_MENU(MenuInfo)));
2854 }
2855
2856 if (! MenuGetRosMenuItemInfo(MenuInfo->Self, MenuInfo->iItem, &ItemInfo))
2857 {
2858 MenuCleanupRosMenuItemInfo(&ItemInfo);
2859 return MenuInfo->Self;
2860 }
2861
2862 //item = &menu->rgItems[menu->iItem];
2863 Rect = ItemInfo.Rect;
2864
2865 /* correct item if modified as a reaction to WM_INITMENUPOPUP message */
2866 if (!(ItemInfo.fState & MF_HILITE))
2867 {
2868 if (MenuInfo->fFlags & MNF_POPUP) Dc = GetDC(MenuInfo->Wnd);
2869 else Dc = GetDCEx(MenuInfo->Wnd, 0, DCX_CACHE | DCX_WINDOW);
2870
2871 SelectObject(Dc, hMenuFont);
2872 ItemInfo.fMask |= MIIM_STATE;
2873 ItemInfo.fState |= MF_HILITE;
2874 MenuSetRosMenuItemInfo(MenuInfo->Self, MenuInfo->iItem, &ItemInfo);
2875 MenuDrawMenuItem(MenuInfo->Wnd, MenuInfo, WndOwner, Dc, &ItemInfo, MenuInfo->cyMenu,
2876 !(MenuInfo->fFlags & MNF_POPUP), ODA_DRAWENTIRE);
2877 ReleaseDC(MenuInfo->Wnd, Dc);
2878 }
2879
2880 if (!ItemInfo.Rect.top && !ItemInfo.Rect.left && !ItemInfo.Rect.bottom && !ItemInfo.Rect.right)
2881 ItemInfo.Rect = Rect;
2882
2883 ItemInfo.fMask |= MIIM_STATE;
2884 ItemInfo.fState |= MF_MOUSESELECT;
2885 MenuSetRosMenuItemInfo(MenuInfo->Self, MenuInfo->iItem, &ItemInfo);
2886
2887 if (IS_SYSTEM_MENU(MenuInfo))
2888 {
2889 MenuInitSysMenuPopup(ItemInfo.hSubMenu,
2890 GetWindowLongPtrW(MenuInfo->Wnd, GWL_STYLE),
2891 GetClassLongPtrW(MenuInfo->Wnd, GCL_STYLE), HTSYSMENU);
2892
2893 NcGetSysPopupPos(MenuInfo->Wnd, &Rect);
2894 if (Flags & TPM_LAYOUTRTL) Rect.left = Rect.right;
2895 Rect.top = Rect.bottom;
2896 Rect.right = GetSystemMetrics(SM_CXSIZE);
2897 Rect.bottom = GetSystemMetrics(SM_CYSIZE);
2898 }
2899 else
2900 {
2901 GetWindowRect(MenuInfo->Wnd, &Rect);
2902 if (MenuInfo->fFlags & MNF_POPUP)
2903 {
2904 RECT rc = ItemInfo.Rect;
2905
2906 MENU_AdjustMenuItemRect(MenuInfo, &rc);
2907
2908 /* The first item in the popup menu has to be at the
2909 same y position as the focused menu item */
2910 if(Flags & TPM_LAYOUTRTL)
2911 Rect.left += GetSystemMetrics(SM_CXBORDER);
2912 else
2913 Rect.left += rc.right /*ItemInfo.Rect.right*/ - GetSystemMetrics(SM_CXBORDER);
2914 Rect.top += rc.top - MENU_TOP_MARGIN;//3;
2915 Rect.right = rc.left - rc.right + GetSystemMetrics(SM_CXBORDER);
2916 Rect.bottom = rc.top - rc.bottom - MENU_TOP_MARGIN - MENU_BOTTOM_MARGIN/*2*/
2917 - GetSystemMetrics(SM_CYBORDER);
2918 }
2919 else
2920 {
2921 if(Flags & TPM_LAYOUTRTL)
2922 Rect.left += Rect.right - ItemInfo.Rect.left;
2923 else
2924 Rect.left += ItemInfo.Rect.left;
2925 Rect.top += ItemInfo.Rect.bottom;
2926 Rect.right = ItemInfo.Rect.right - ItemInfo.Rect.left;
2927 Rect.bottom = ItemInfo.Rect.bottom - ItemInfo.Rect.top;
2928 }
2929 }
2930
2931 /* use default alignment for submenus */
2932 Flags &= ~(TPM_CENTERALIGN | TPM_RIGHTALIGN | TPM_VCENTERALIGN | TPM_BOTTOMALIGN);
2933
2934 MENU_InitPopup( WndOwner, ItemInfo.hSubMenu, Flags );
2935
2936 MenuShowPopup(WndOwner, ItemInfo.hSubMenu, MenuInfo->iItem, Flags,
2937 Rect.left, Rect.top, Rect.right, Rect.bottom );
2938 if (SelectFirst && MenuGetRosMenuInfo(&SubMenuInfo, ItemInfo.hSubMenu))
2939 {
2940 MenuMoveSelection(WndOwner, &SubMenuInfo, ITEM_NEXT);
2941 }
2942
2943 Ret = ItemInfo.hSubMenu;
2944 MenuCleanupRosMenuItemInfo(&ItemInfo);
2945
2946 return Ret;
2947 }
2948
2949 /**********************************************************************
2950 * MENU_EndMenu
2951 *
2952 * Calls EndMenu() if the hwnd parameter belongs to the menu owner
2953 *
2954 * Does the (menu stuff) of the default window handling of WM_CANCELMODE
2955 */
2956 void MENU_EndMenu( HWND hwnd )
2957 {
2958 MENU *menu;
2959 menu = top_popup_hmenu ? MENU_GetMenu( top_popup_hmenu ) : NULL;
2960 if (menu && ( hwnd == menu->hWnd || hwnd == (menu->spwndNotify ? menu->spwndNotify->head.h : NULL)) )
2961 EndMenu();
2962 }
2963
2964 /***********************************************************************
2965 * MenuHideSubPopups
2966 *
2967 * Hide the sub-popup menus of this menu.
2968 */
2969 static void FASTCALL
2970 MenuHideSubPopups(HWND WndOwner, PROSMENUINFO MenuInfo,
2971 BOOL SendMenuSelect, UINT wFlags)
2972 {
2973 ROSMENUINFO SubMenuInfo;
2974 ROSMENUITEMINFO ItemInfo;
2975
2976 TRACE("owner=%x menu=%x 0x%04x\n", WndOwner, MenuInfo, SendMenuSelect);
2977
2978 if (MenuInfo && top_popup && NO_SELECTED_ITEM != MenuInfo->iItem)
2979 {
2980 //item = &menu->rgItems[menu->iItem];
2981 MenuInitRosMenuItemInfo(&ItemInfo);
2982 ItemInfo.fMask |= MIIM_FTYPE | MIIM_STATE;
2983 if (! MenuGetRosMenuItemInfo(MenuInfo->Self, MenuInfo->iItem, &ItemInfo)
2984 || !(ItemInfo.hSubMenu)
2985 || !(ItemInfo.fState & MF_MOUSESELECT))
2986 {
2987 MenuCleanupRosMenuItemInfo(&ItemInfo);
2988 return;
2989 }
2990 ItemInfo.fState &= ~MF_MOUSESELECT;
2991 ItemInfo.fMask |= MIIM_STATE;
2992 MenuSetRosMenuItemInfo(MenuInfo->Self, MenuInfo->iItem, &ItemInfo);
2993 if (MenuGetRosMenuInfo(&SubMenuInfo, ItemInfo.hSubMenu))
2994 {
2995 MenuHideSubPopups(WndOwner, &SubMenuInfo, FALSE, wFlags);
2996 MenuSelectItem(WndOwner, &SubMenuInfo, NO_SELECTED_ITEM, SendMenuSelect, NULL);
2997
2998 DestroyWindow(SubMenuInfo.Wnd);
2999 /* Native returns handle to destroyed window */
3000 if (!(wFlags & TPM_NONOTIFY))
3001 SendMessageW( WndOwner, WM_UNINITMENUPOPUP, (WPARAM)ItemInfo.hSubMenu,
3002 MAKELPARAM(0, IS_SYSTEM_MENU(&SubMenuInfo)) );
3003 ////
3004 // Call WM_UNINITMENUPOPUP FIRST before destroy!!
3005 // Fixes todo_wine User32 test menu.c line 2233 GetMenuBarInfo callback....
3006 //
3007 SubMenuInfo.Wnd = NULL;
3008 MenuSetRosMenuInfo(&SubMenuInfo);
3009 ////
3010 }
3011 }
3012 }
3013
3014 /***********************************************************************
3015 * MenuSwitchTracking
3016 *
3017 * Helper function for menu navigation routines.
3018 */
3019 static void FASTCALL
3020 MenuSwitchTracking(MTRACKER* pmt, PROSMENUINFO PtMenuInfo, UINT Index, UINT wFlags)
3021 {
3022 ROSMENUINFO TopMenuInfo;
3023
3024 TRACE("%x menu=%x 0x%04x\n", pmt, PtMenuInfo->Self, Index);
3025
3026 if ( MenuGetRosMenuInfo(&TopMenuInfo, pmt->TopMenu) &&
3027 pmt->TopMenu != PtMenuInfo->Self &&
3028 !((PtMenuInfo->fFlags | TopMenuInfo.fFlags) & MNF_POPUP) )
3029 {
3030 /* both are top level menus (system and menu-bar) */
3031 MenuHideSubPopups(pmt->OwnerWnd, &TopMenuInfo, FALSE, wFlags);
3032 MenuSelectItem(pmt->OwnerWnd, &TopMenuInfo, NO_SELECTED_ITEM, FALSE, NULL);
3033 pmt->TopMenu = PtMenuInfo->Self;
3034 }
3035 else
3036 {
3037 MenuHideSubPopups(pmt->OwnerWnd, PtMenuInfo, FALSE, wFlags);
3038 }
3039
3040 MenuSelectItem(pmt->OwnerWnd, PtMenuInfo, Index, TRUE, NULL);
3041 }
3042
3043 /***********************************************************************
3044 * MenuExecFocusedItem
3045 *
3046 * Execute a menu item (for instance when user pressed Enter).
3047 * Return the wID of the executed item. Otherwise, -1 indicating
3048 * that no menu item was executed, -2 if a popup is shown;
3049 * Have to receive the flags for the TrackPopupMenu options to avoid
3050 * sending unwanted message.
3051 *
3052 */
3053 static INT FASTCALL
3054 MenuExecFocusedItem(MTRACKER *pmt, PROSMENUINFO MenuInfo, UINT Flags)
3055 {
3056 ROSMENUITEMINFO ItemInfo;
3057 UINT wID;
3058
3059 TRACE("%p menu=%p\n", pmt, MenuInfo);
3060
3061 if (0 == MenuInfo->cItems || NO_SELECTED_ITEM == MenuInfo->iItem)
3062 {
3063 return -1;
3064 }
3065
3066 MenuInitRosMenuItemInfo(&ItemInfo);
3067 if (! MenuGetRosMenuItemInfo(MenuInfo->Self, MenuInfo->iItem, &ItemInfo))
3068 {
3069 MenuCleanupRosMenuItemInfo(&ItemInfo);
3070 return -1;
3071 }
3072
3073 TRACE("%p %08x %p\n", MenuInfo, ItemInfo.wID, ItemInfo.hSubMenu);
3074
3075 if (0 == (ItemInfo.hSubMenu))
3076 {
3077 if (0 == (ItemInfo.fState & (MF_GRAYED | MF_DISABLED))
3078 && 0 == (ItemInfo.fType & MF_SEPARATOR))
3079 {
3080 /* If TPM_RETURNCMD is set you return the id, but
3081 do not send a message to the owner */
3082 if (0 == (Flags & TPM_RETURNCMD))
3083 {
3084 if (0 != (MenuInfo->fFlags & MNF_SYSMENU))
3085 {
3086 PostMessageW(pmt->OwnerWnd, WM_SYSCOMMAND, ItemInfo.wID,
3087 MAKELPARAM((SHORT) pmt->Pt.x, (SHORT) pmt->Pt.y));
3088 }
3089 else
3090 {
3091 ROSMENUINFO topmenuI;
3092 BOOL ret = MenuGetRosMenuInfo(&topmenuI, pmt->TopMenu);
3093 DWORD dwStyle = MenuInfo->dwStyle | (ret ? topmenuI.dwStyle : 0);
3094
3095 if (dwStyle & MNS_NOTIFYBYPOS)
3096 PostMessageW(pmt->OwnerWnd, WM_MENUCOMMAND, MenuInfo->iItem, (LPARAM)MenuInfo->Self);
3097 else
3098 PostMessageW(pmt->OwnerWnd, WM_COMMAND, ItemInfo.wID, 0);
3099 }
3100 }
3101 wID = ItemInfo.wID;
3102 MenuCleanupRosMenuItemInfo(&ItemInfo);
3103 return wID;
3104 }
3105 }
3106 else
3107 {
3108 pmt->CurrentMenu = MenuShowSubPopup(pmt->OwnerWnd, MenuInfo, TRUE, Flags);
3109 return -2;
3110 }
3111
3112 return -1;
3113 }
3114
3115 /***********************************************************************
3116 * MenuButtonDown
3117 *
3118 * Return TRUE if we can go on with menu tracking.
3119 */
3120 static BOOL FASTCALL
3121 MENU_ButtonDown(MTRACKER* pmt, HMENU hPtMenu, UINT Flags)
3122 {
3123 int Index;
3124 ROSMENUINFO MenuInfo;
3125 ROSMENUITEMINFO Item;
3126
3127 TRACE("%x PtMenu=%p\n", pmt, hPtMenu);
3128
3129 if (NULL != hPtMenu)
3130 {
3131 if (! MenuGetRosMenuInfo(&MenuInfo, hPtMenu))
3132 {
3133 return FALSE;
3134 }
3135 if (IS_SYSTEM_MENU(&MenuInfo))
3136 {
3137 Index = 0;
3138 }
3139 else
3140 {
3141 Index = NtUserMenuItemFromPoint(pmt->OwnerWnd, hPtMenu, pmt->Pt.x, pmt->Pt.y);
3142 }
3143 MenuInitRosMenuItemInfo(&Item);
3144 if (NO_SELECTED_ITEM == Index || ! MenuGetRosMenuItemInfo(hPtMenu, Index, &Item))
3145 {
3146 MenuCleanupRosMenuItemInfo(&Item);
3147 return FALSE;
3148 }
3149
3150 if (!(Item.fType & MF_SEPARATOR) &&
3151 !(Item.fState & (MFS_DISABLED | MFS_GRAYED)) )
3152 {
3153 if (MenuInfo.iItem != Index)
3154 {
3155 MenuSwitchTracking(pmt, &MenuInfo, Index, Flags);
3156 }
3157
3158 /* If the popup menu is not already "popped" */
3159 if (!(Item.fState & MF_MOUSESELECT))
3160 {
3161 pmt->CurrentMenu = MenuShowSubPopup(pmt->OwnerWnd, &MenuInfo, FALSE, Flags);
3162 }
3163
3164 MenuCleanupRosMenuItemInfo(&Item);
3165
3166 return TRUE;
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 *pmt, HMENU hPtMenu, UINT Flags)
3183 {
3184 INT Id;
3185 ROSMENUINFO MenuInfo;
3186 ROSMENUITEMINFO ItemInfo;
3187
3188 TRACE("%p hmenu=%x\n", pmt, hPtMenu);
3189
3190 if (hPtMenu)
3191 {
3192 Id = 0;
3193 if (! MenuGetRosMenuInfo(&MenuInfo, hPtMenu))
3194 {
3195 return -1;
3196 }
3197
3198 if (! IS_SYSTEM_MENU(&MenuInfo))
3199 {
3200 Id = NtUserMenuItemFromPoint(pmt->OwnerWnd, MenuInfo.Self, pmt->Pt.x, pmt->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( pmt, &MenuInfo, Flags);
3209 MenuCleanupRosMenuItemInfo(&ItemInfo);
3210 if (ExecutedMenuId == -1 || ExecutedMenuId == -2) return -1;
3211 return ExecutedMenuId;
3212 }
3213 MenuCleanupRosMenuItemInfo(&ItemInfo);
3214
3215 /* If we are dealing with the menu bar */
3216 /* and this is a click on an already "popped" item: */
3217 /* Stop the menu tracking and close the opened submenus */
3218 if (pmt->TopMenu == MenuInfo.Self && MenuInfo.TimeToHide)
3219 {
3220 MenuCleanupRosMenuItemInfo(&ItemInfo);
3221 return 0;
3222 }
3223 }
3224 MenuCleanupRosMenuItemInfo(&ItemInfo);
3225 if( GetMenu(MenuInfo.Wnd) == hPtMenu )
3226 {
3227 MenuInfo.TimeToHide = TRUE;
3228 MenuSetRosMenuInfo(&MenuInfo);
3229 }
3230 }
3231 return -1;
3232 }
3233
3234 /***********************************************************************
3235 * MenuPtMenu
3236 *
3237 * Walks menu chain trying to find a menu pt maps to.
3238 */
3239 static HMENU FASTCALL
3240 MENU_PtMenu(HMENU hMenu, POINT pt)
3241 {
3242 MENU *menu;
3243 PITEM pItem;
3244 HMENU ret = NULL;
3245
3246 menu = MENU_GetMenu( hMenu );
3247 if (!menu) return NULL;
3248
3249 /* try subpopup first (if any) */
3250 if (menu->iItem != NO_SELECTED_ITEM)
3251 {
3252 pItem = menu->rgItems ? DesktopPtrToUser(menu->rgItems) : NULL;
3253 if ( pItem ) pItem = &pItem[menu->iItem];
3254 if ( pItem && pItem->spSubMenu && pItem->fState & MF_MOUSESELECT)
3255 {
3256 PMENU pSubMenu = DesktopPtrToUser(pItem->spSubMenu);
3257 ret = MENU_PtMenu( UserHMGetHandle(pSubMenu), pt);
3258 }
3259 }
3260
3261 /* check the current window (avoiding WM_HITTEST) */
3262 if (!ret)
3263 {
3264 INT ht = DefWndNCHitTest(menu->hWnd, pt);
3265 if ( menu->fFlags & MNF_POPUP )
3266 {
3267 if (ht != HTNOWHERE && ht != HTERROR) ret = hMenu;
3268 }
3269 else if (ht == HTSYSMENU)
3270 ret = get_win_sys_menu(menu->hWnd);
3271 else if (ht == HTMENU)
3272 ret = GetMenu( menu->hWnd );
3273 }
3274 return ret;
3275 }
3276
3277 /***********************************************************************
3278 * MenuMouseMove
3279 *
3280 * Return TRUE if we can go on with menu tracking.
3281 */
3282 static BOOL FASTCALL
3283 MenuMouseMove(MTRACKER *pmt, HMENU PtMenu, UINT Flags)
3284 {
3285 INT Index;
3286 ROSMENUINFO MenuInfo;
3287 ROSMENUITEMINFO ItemInfo;
3288
3289 if (NULL != PtMenu)
3290 {
3291 if (! MenuGetRosMenuInfo(&MenuInfo, PtMenu))
3292 {
3293 return TRUE;
3294 }
3295 if (IS_SYSTEM_MENU(&MenuInfo))
3296 {
3297 Index = 0;
3298 }
3299 else
3300 {
3301 Index = NtUserMenuItemFromPoint(pmt->OwnerWnd, PtMenu, pmt->Pt.x, pmt->Pt.y);
3302 }
3303 }
3304 else
3305 {
3306 Index = NO_SELECTED_ITEM;
3307 }
3308
3309 if (NO_SELECTED_ITEM == Index)
3310 {
3311 if (pmt->CurrentMenu == MenuInfo.Self ||
3312 MenuGetRosMenuInfo(&MenuInfo, pmt->CurrentMenu))
3313 {
3314 MenuSelectItem(pmt->OwnerWnd, &MenuInfo, NO_SELECTED_ITEM,
3315 TRUE, pmt->TopMenu);
3316 }
3317 }
3318 else if (MenuInfo.iItem != Index)
3319 {
3320 MenuInitRosMenuItemInfo(&ItemInfo);
3321 if (MenuGetRosMenuItemInfo(MenuInfo.Self, Index, &ItemInfo) &&
3322 !(ItemInfo.fType & MF_SEPARATOR))
3323 {
3324 MenuSwitchTracking(pmt, &MenuInfo, Index, Flags);
3325 if (!(ItemInfo.fState & (MFS_DISABLED | MFS_GRAYED)))
3326 pmt->CurrentMenu = MenuShowSubPopup(pmt->OwnerWnd, &MenuInfo, FALSE, Flags);
3327 }
3328 MenuCleanupRosMenuItemInfo(&ItemInfo);
3329 }
3330
3331 return TRUE;
3332 }
3333
3334 /***********************************************************************
3335 * MenuGetSubPopup
3336 *
3337 * Return the handle of the selected sub-popup menu (if any).
3338 */
3339 static
3340 HMENU MENU_GetSubPopup( HMENU hmenu )
3341 {
3342 MENU *menu;
3343 ITEM *item;
3344
3345 menu = MENU_GetMenu( hmenu );
3346
3347 if ((!menu) || (menu->iItem == NO_SELECTED_ITEM)) return 0;
3348
3349 //item = &menu->rgItems[menu->iItem];
3350 item = menu->rgItems ? DesktopPtrToUser(menu->rgItems) : NULL;
3351 if (item) item = &item[menu->iItem];
3352 if (item && (item->spSubMenu) && (item->fState & MF_MOUSESELECT))
3353 {
3354 PMENU pSubMenu = DesktopPtrToUser(item->spSubMenu);
3355 return UserHMGetHandle(pSubMenu);
3356 }
3357 return 0;
3358 }
3359
3360 /***********************************************************************
3361 * MenuDoNextMenu
3362 *
3363 * NOTE: WM_NEXTMENU documented in Win32 is a bit different.
3364 */
3365 static LRESULT FASTCALL
3366 MenuDoNextMenu(MTRACKER* pmt, UINT Vk, UINT wFlags)
3367 {
3368 ROSMENUINFO TopMenuInfo;
3369 ROSMENUINFO MenuInfo;
3370
3371 if (! MenuGetRosMenuInfo(&TopMenuInfo, pmt->TopMenu))
3372 {
3373 return (LRESULT) FALSE;
3374 }
3375
3376 if ((VK_LEFT == Vk && 0 == TopMenuInfo.iItem)
3377 || (VK_RIGHT == Vk && TopMenuInfo.iItem == TopMenuInfo.cItems - 1))
3378 {
3379 MDINEXTMENU NextMenu;
3380 HMENU NewMenu;
3381 HWND NewWnd;
3382 UINT Id = 0;
3383
3384 NextMenu.hmenuIn = (IS_SYSTEM_MENU(&TopMenuInfo)) ? GetSubMenu(pmt->TopMenu, 0) : pmt->TopMenu;
3385 NextMenu.hmenuNext = NULL;
3386 NextMenu.hwndNext = NULL;
3387 SendMessageW(pmt->OwnerWnd, WM_NEXTMENU, Vk, (LPARAM) &NextMenu);
3388
3389 TRACE("%p [%p] -> %p [%p]\n",
3390 pmt->CurrentMenu, pmt->OwnerWnd, NextMenu.hmenuNext, NextMenu.hwndNext );
3391
3392 if (NULL == NextMenu.hmenuNext || NULL == NextMenu.hwndNext)
3393 {
3394 DWORD Style = GetWindowLongPtrW(pmt->OwnerWnd, GWL_STYLE);
3395 NewWnd = pmt->OwnerWnd;
3396 if (IS_SYSTEM_MENU(&TopMenuInfo))
3397 {
3398 /* switch to the menu bar */
3399
3400 if (0 != (Style & WS_CHILD)
3401 || NULL == (NewMenu = GetMenu(NewWnd)))
3402 {
3403 return FALSE;
3404 }
3405
3406 if (VK_LEFT == Vk)
3407 {
3408 if (! MenuGetRosMenuInfo(&MenuInfo, NewMenu))
3409 {
3410 return FALSE;
3411 }
3412 Id = MenuInfo.cItems - 1;
3413 }
3414 }
3415 else if (0 != (Style & WS_SYSMENU))
3416 {
3417 /* switch to the system menu */
3418 NewMenu = get_win_sys_menu(NewWnd);
3419 }
3420 else
3421 {
3422 return FALSE;
3423 }
3424 }
3425 else /* application returned a new menu to switch to */
3426 {
3427 NewMenu = NextMenu.hmenuNext;
3428 NewWnd = NextMenu.hwndNext;
3429
3430 if (IsMenu(NewMenu) && IsWindow(NewWnd))
3431 {
3432 DWORD Style = GetWindowLongPtrW(NewWnd, GWL_STYLE);
3433
3434 if (0 != (Style & WS_SYSMENU)
3435 && get_win_sys_menu(NewWnd) == NewMenu)
3436 {
3437 /* get the real system menu */
3438 NewMenu = get_win_sys_menu(NewWnd);
3439 }
3440 else if (0 != (Style & WS_CHILD) || GetMenu(NewWnd) != NewMenu)
3441 {
3442 /* FIXME: Not sure what to do here;
3443 * perhaps try to track NewMenu as a popup? */
3444
3445 WARN(" -- got confused.\n");
3446 return FALSE;
3447 }
3448 }
3449 else
3450 {
3451 return FALSE;
3452 }
3453 }
3454
3455 if (NewMenu != pmt->TopMenu)
3456 {
3457 MenuSelectItem(pmt->OwnerWnd, &TopMenuInfo, NO_SELECTED_ITEM,
3458 FALSE, 0 );
3459 if (pmt->CurrentMenu != pmt->TopMenu)
3460 {
3461 MenuHideSubPopups(pmt->OwnerWnd, &TopMenuInfo, FALSE, wFlags);
3462 }
3463 }
3464
3465 if (NewWnd != pmt->OwnerWnd)
3466 {
3467 pmt->OwnerWnd = NewWnd;
3468 NtUserxSetGUIThreadHandle(MSQ_STATE_MENUOWNER, pmt->OwnerWnd); // 1
3469 SetCapture(pmt->OwnerWnd); // 2
3470 }
3471
3472 pmt->TopMenu = pmt->CurrentMenu = NewMenu; /* all subpopups are hidden */
3473 if (MenuGetRosMenuInfo(&TopMenuInfo, pmt->TopMenu))
3474 {
3475 MenuSelectItem(pmt->OwnerWnd, &TopMenuInfo, Id, TRUE, 0);
3476 }
3477
3478 return TRUE;
3479 }
3480
3481 return FALSE;
3482 }
3483
3484 /***********************************************************************
3485 * MenuSuspendPopup
3486 *
3487 * The idea is not to show the popup if the next input message is
3488 * going to hide it anyway.
3489 */
3490 static BOOL FASTCALL
3491 MenuSuspendPopup(MTRACKER* pmt, UINT uMsg)
3492 {
3493 MSG msg;
3494
3495 msg.hwnd = pmt->OwnerWnd;
3496
3497 PeekMessageW( &msg, 0, uMsg, uMsg, PM_NOYIELD | PM_REMOVE); // ported incorrectly since 8317 GvG
3498 //pmt->TrackFlags |= TF_SKIPREMOVE; // This sends TrackMenu into a loop with arrow keys!!!!
3499
3500 switch( uMsg )
3501 {
3502 case WM_KEYDOWN:
3503 PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_NOREMOVE);
3504 if( msg.message == WM_KEYUP || msg.message == WM_PAINT )
3505 {
3506 PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_REMOVE);
3507 PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_NOREMOVE);
3508 if( msg.message == WM_KEYDOWN &&
3509 (msg.wParam == VK_LEFT || msg.wParam == VK_RIGHT))
3510 {
3511 pmt->TrackFlags |= TF_SUSPENDPOPUP;
3512 return TRUE;
3513 }
3514 }
3515 break;
3516 }
3517 /* failures go through this */
3518 pmt->TrackFlags &= ~TF_SUSPENDPOPUP;
3519 return FALSE;
3520 }
3521
3522 /***********************************************************************
3523 * MenuKeyEscape
3524 *
3525 * Handle a VK_ESCAPE key event in a menu.
3526 */
3527 static BOOL FASTCALL
3528 MenuKeyEscape(MTRACKER *pmt, UINT Flags)
3529 {
3530 BOOL EndMenu = TRUE;
3531 ROSMENUINFO MenuInfo;
3532 HMENU MenuTmp, MenuPrev;
3533
3534 if (pmt->CurrentMenu != pmt->TopMenu)
3535 {
3536 if (MenuGetRosMenuInfo(&MenuInfo, pmt->CurrentMenu)
3537 && 0 != (MenuInfo.fFlags & MNF_POPUP))
3538 {
3539 MenuPrev = MenuTmp = pmt->TopMenu;
3540
3541 /* close topmost popup */
3542 while (MenuTmp != pmt->CurrentMenu)
3543 {
3544 MenuPrev = MenuTmp;
3545 MenuTmp = MENU_GetSubPopup(MenuPrev);
3546 }
3547
3548 if (MenuGetRosMenuInfo(&MenuInfo, MenuPrev))
3549 {
3550 MenuHideSubPopups(pmt->OwnerWnd, &MenuInfo, TRUE, Flags);
3551 }
3552 pmt->CurrentMenu = MenuPrev;
3553 EndMenu = FALSE;
3554 }
3555 }
3556
3557 return EndMenu;
3558 }
3559
3560 /***********************************************************************
3561 * MenuKeyLeft
3562 *
3563 * Handle a VK_LEFT key event in a menu.
3564 */
3565 static void FASTCALL
3566 MenuKeyLeft(MTRACKER* pmt, UINT Flags)
3567 {
3568 ROSMENUINFO MenuInfo;
3569 ROSMENUINFO TopMenuInfo;
3570 ROSMENUINFO PrevMenuInfo;
3571 HMENU MenuTmp, MenuPrev;
3572 UINT PrevCol;
3573
3574 MenuPrev = MenuTmp = pmt->TopMenu;
3575
3576 /* Try to move 1 column left (if possible) */
3577 if ( (PrevCol = MENU_GetStartOfPrevColumn(pmt->CurrentMenu)) != NO_SELECTED_ITEM)
3578 {
3579 if (MenuGetRosMenuInfo(&MenuInfo, pmt->CurrentMenu))
3580 {
3581 MenuSelectItem(pmt->OwnerWnd, &MenuInfo, PrevCol, TRUE, 0);
3582 }
3583 return;
3584 }
3585
3586 /* close topmost popup */
3587 while (MenuTmp != pmt->CurrentMenu)
3588 {
3589 MenuPrev = MenuTmp;
3590 MenuTmp = MENU_GetSubPopup(MenuPrev);
3591 }
3592
3593 if (! MenuGetRosMenuInfo(&PrevMenuInfo, MenuPrev))
3594 {
3595 return;
3596 }
3597 MenuHideSubPopups(pmt->OwnerWnd, &PrevMenuInfo, TRUE, Flags);
3598 pmt->CurrentMenu = MenuPrev;
3599
3600 if (! MenuGetRosMenuInfo(&TopMenuInfo, pmt->TopMenu))
3601 {
3602 return;
3603 }
3604 if ((MenuPrev == pmt->TopMenu) && !(TopMenuInfo.fFlags & MNF_POPUP))
3605 {
3606 /* move menu bar selection if no more popups are left */
3607
3608 if (!MenuDoNextMenu(pmt, VK_LEFT, Flags))
3609 {
3610 MenuMoveSelection(pmt->OwnerWnd, &TopMenuInfo, ITEM_PREV);
3611 }
3612
3613 if (MenuPrev != MenuTmp || pmt->TrackFlags & TF_SUSPENDPOPUP)
3614 {
3615 /* A sublevel menu was displayed - display the next one
3616 * unless there is another displacement coming up */
3617
3618 if (! MenuSuspendPopup(pmt, WM_KEYDOWN)
3619 && MenuGetRosMenuInfo(&TopMenuInfo, pmt->TopMenu))
3620 {
3621 pmt->CurrentMenu = MenuShowSubPopup(pmt->OwnerWnd, &TopMenuInfo,
3622 TRUE, Flags);
3623 }
3624 }
3625 }
3626 }
3627
3628 /***********************************************************************
3629 * MenuKeyRight
3630 *
3631 * Handle a VK_RIGHT key event in a menu.
3632 */
3633 static void FASTCALL MenuKeyRight(MTRACKER *pmt, UINT Flags)
3634 {
3635 HMENU hmenutmp;
3636 ROSMENUINFO MenuInfo;
3637 ROSMENUINFO CurrentMenuInfo;
3638 UINT NextCol;
3639
3640 TRACE("MenuKeyRight called, cur %p, top %p.\n",
3641 pmt->CurrentMenu, pmt->TopMenu);
3642
3643 if (! MenuGetRosMenuInfo(&MenuInfo, pmt->TopMenu)) return;
3644 if ((MenuInfo.fFlags & MNF_POPUP) || (pmt->CurrentMenu != pmt->TopMenu))
3645 {
3646 /* If already displaying a popup, try to display sub-popup */
3647
3648 hmenutmp = pmt->CurrentMenu;
3649 if (MenuGetRosMenuInfo(&CurrentMenuInfo, pmt->CurrentMenu))
3650 {
3651 pmt->CurrentMenu = MenuShowSubPopup(pmt->OwnerWnd, &CurrentMenuInfo, TRUE, Flags);
3652 }
3653
3654 /* if subpopup was displayed then we are done */
3655 if (hmenutmp != pmt->CurrentMenu) return;
3656 }
3657
3658 /* Check to see if there's another column */
3659 if ( (NextCol = MENU_GetStartOfNextColumn(pmt->CurrentMenu)) != NO_SELECTED_ITEM)
3660 {
3661 TRACE("Going to %d.\n", NextCol);
3662 if (MenuGetRosMenuInfo(&MenuInfo, pmt->CurrentMenu))
3663 {
3664 MenuSelectItem(pmt->OwnerWnd, &MenuInfo, NextCol, TRUE, 0);
3665 }
3666 return;
3667 }
3668
3669 if (!(MenuInfo.fFlags & MNF_POPUP)) /* menu bar tracking */
3670 {
3671 if (pmt->CurrentMenu != pmt->TopMenu)
3672 {
3673 MenuHideSubPopups(pmt->OwnerWnd, &MenuInfo, FALSE, Flags);
3674 hmenutmp = pmt->CurrentMenu = pmt->TopMenu;
3675 }
3676 else
3677 {
3678 hmenutmp = NULL;
3679 }
3680
3681 /* try to move to the next item */
3682 if ( !MenuDoNextMenu(pmt, VK_RIGHT, Flags))
3683 MenuMoveSelection(pmt->OwnerWnd, &MenuInfo, ITEM_NEXT);
3684
3685 if ( hmenutmp || pmt->TrackFlags & TF_SUSPENDPOPUP )
3686 {
3687 if (! MenuSuspendPopup(pmt, WM_KEYDOWN)
3688 && MenuGetRosMenuInfo(&MenuInfo, pmt->TopMenu))
3689 {
3690 pmt->CurrentMenu = MenuShowSubPopup(pmt->OwnerWnd, &MenuInfo,
3691 TRUE, Flags);
3692 }
3693 }
3694 }
3695 }
3696
3697 /***********************************************************************
3698 * MenuTrackMenu
3699 *
3700 * Menu tracking code.
3701 */
3702 static INT FASTCALL MenuTrackMenu(HMENU hmenu, UINT wFlags, INT x, INT y,
3703 HWND hwnd, const RECT *lprect )
3704 {
3705 MSG msg;
3706 ROSMENUINFO MenuInfo;
3707 ROSMENUITEMINFO ItemInfo;
3708 PMENU menu;
3709 BOOL fRemove;
3710 INT executedMenuId = -1;
3711 MTRACKER mt;
3712 HWND capture_win;
3713 BOOL enterIdleSent = FALSE;
3714
3715 mt.TrackFlags = 0;
3716 mt.CurrentMenu = hmenu;
3717 mt.TopMenu = hmenu;
3718 mt.OwnerWnd = hwnd;
3719 mt.Pt.x = x;
3720 mt.Pt.y = y;
3721
3722 TRACE("hmenu=%p flags=0x%08x (%d,%d) hwnd=%x (%ld,%ld)-(%ld,%ld)\n",
3723 hmenu, wFlags, x, y, hwnd, lprect ? lprect->left : 0, lprect ? lprect->top : 0,
3724 lprect ? lprect->right : 0, lprect ? lprect->bottom : 0);
3725
3726 if (!IsMenu(hmenu))
3727 {
3728 WARN("Invalid menu handle %p\n", hmenu); // Error already set in IsMenu.
3729 return FALSE;
3730 }
3731
3732 if (! MenuGetRosMenuInfo(&MenuInfo, hmenu))
3733 {
3734 return FALSE;
3735 }
3736
3737 if (wFlags & TPM_BUTTONDOWN)
3738 {
3739 /* Get the result in order to start the tracking or not */
3740 fRemove = MENU_ButtonDown( &mt, hmenu, wFlags );
3741 fEndMenu = !fRemove;
3742 }
3743
3744 if (wFlags & TF_ENDMENU) fEndMenu = TRUE;
3745
3746 /* owner may not be visible when tracking a popup, so use the menu itself */
3747 capture_win = (wFlags & TPM_POPUPMENU) ? MenuInfo.Wnd : mt.OwnerWnd;
3748 NtUserxSetGUIThreadHandle(MSQ_STATE_MENUOWNER, capture_win); // 1
3749 SetCapture(capture_win); // 2
3750
3751 while (!fEndMenu)
3752 {
3753 BOOL ErrorExit = FALSE;
3754 menu = MENU_GetMenu( mt.CurrentMenu );
3755 if (!menu) /* sometimes happens if I do a window manager close */
3756 break;
3757
3758 /* we have to keep the message in the queue until it's
3759 * clear that menu loop is not over yet. */
3760
3761 for (;;)
3762 {
3763 if (PeekMessageW( &msg, 0, 0, 0, PM_NOREMOVE ))
3764 {
3765 if (!CallMsgFilterW( &msg, MSGF_MENU )) break;
3766 /* remove the message from the queue */
3767 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
3768 }
3769 else
3770 {
3771 /* ReactOS Check */
3772 if (!ValidateHwndNoErr(mt.OwnerWnd) || !ValidateHwndNoErr(MenuInfo.Wnd))
3773 {
3774 ErrorExit = TRUE; // Do not wait on dead windows, now test_capture_4 works.
3775 break;
3776 }
3777 if (!enterIdleSent)
3778 {
3779 HWND win = MenuInfo.fFlags & MNF_POPUP ? MenuInfo.Wnd : NULL;
3780 enterIdleSent = TRUE;
3781 SendMessageW( mt.OwnerWnd, WM_ENTERIDLE, MSGF_MENU, (LPARAM) win);
3782 }
3783 WaitMessage();
3784 }
3785 }
3786
3787 if (ErrorExit) break; // Gracefully dropout.
3788
3789 /* check if EndMenu() tried to cancel us, by posting this message */
3790 if (msg.message == WM_CANCELMODE)
3791 {
3792 /* we are now out of the loop */
3793 fEndMenu = TRUE;
3794
3795 /* remove the message from the queue */
3796 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
3797
3798 /* break out of internal loop, ala ESCAPE */
3799 break;
3800 }
3801
3802 TranslateMessage( &msg );
3803 mt.Pt = msg.pt;
3804
3805 if ( (msg.hwnd == MenuInfo.Wnd) || (msg.message!=WM_TIMER) )
3806 enterIdleSent=FALSE;
3807
3808 fRemove = FALSE;
3809 if ((msg.message >= WM_MOUSEFIRST) && (msg.message <= WM_MOUSELAST))
3810 {
3811 /*
3812 * Use the mouse coordinates in lParam instead of those in the MSG
3813 * struct to properly handle synthetic messages. They are already
3814 * in screen coordinates.
3815 */
3816 mt.Pt.x = (short)LOWORD(msg.lParam);
3817 mt.Pt.y = (short)HIWORD(msg.lParam);
3818
3819 /* Find a menu for this mouse event */
3820 hmenu = MENU_PtMenu(mt.TopMenu, mt.Pt);
3821
3822 switch(msg.message)
3823 {
3824 /* no WM_NC... messages in captured state */
3825
3826 case WM_RBUTTONDBLCLK:
3827 case WM_RBUTTONDOWN:
3828 if (!(wFlags & TPM_RIGHTBUTTON)) break;
3829 /* fall through */
3830 case WM_LBUTTONDBLCLK:
3831 case WM_LBUTTONDOWN:
3832 /* If the message belongs to the menu, removes it from the queue */
3833 /* Else, end menu tracking */
3834 fRemove = MENU_ButtonDown(&mt, hmenu, wFlags);
3835 fEndMenu = !fRemove;
3836 if (msg.message == WM_LBUTTONDBLCLK) fEndMenu = TRUE; // Must exit or loop forever!
3837 break;
3838
3839 case WM_RBUTTONUP:
3840 if (!(wFlags & TPM_RIGHTBUTTON)) break;
3841 /* fall through */
3842 case WM_LBUTTONUP:
3843 /* Check if a menu was selected by the mouse */
3844 if (hmenu)
3845 {
3846 executedMenuId = MENU_ButtonUp( &mt, hmenu, wFlags);
3847 TRACE("executedMenuId %d\n", executedMenuId);
3848
3849 /* End the loop if executedMenuId is an item ID */
3850 /* or if the job was done (executedMenuId = 0). */
3851 fEndMenu = fRemove = (executedMenuId != -1);
3852 }
3853 /* No menu was selected by the mouse */
3854 /* if the function was called by TrackPopupMenu, continue
3855 with the menu tracking. If not, stop it */
3856 else
3857 fEndMenu = ((wFlags & TPM_POPUPMENU) ? FALSE : TRUE);
3858
3859 break;
3860
3861 case WM_MOUSEMOVE:
3862 /* the selected menu item must be changed every time */
3863 /* the mouse moves. */
3864
3865 if (hmenu)
3866 fEndMenu |= !MenuMouseMove( &mt, hmenu, wFlags );
3867
3868 } /* switch(msg.message) - mouse */
3869 }
3870 else if ((msg.message >= WM_KEYFIRST) && (msg.message <= WM_KEYLAST))
3871 {
3872 fRemove = TRUE; /* Keyboard messages are always removed */
3873 switch(msg.message)
3874 {
3875 case WM_KEYDOWN:
3876 case WM_SYSKEYDOWN:
3877 switch(msg.wParam)
3878 {
3879 case VK_MENU:
3880 case VK_F10:
3881 fEndMenu = TRUE;
3882 break;
3883
3884 case VK_HOME:
3885 case VK_END:
3886 if (MenuGetRosMenuInfo(&MenuInfo, mt.CurrentMenu))
3887 {
3888 MenuSelectItem(mt.OwnerWnd, &MenuInfo, NO_SELECTED_ITEM, FALSE, 0 );
3889 MenuMoveSelection(mt.OwnerWnd, &MenuInfo, VK_HOME == msg.wParam ? ITEM_NEXT : ITEM_PREV);
3890 }
3891 break;
3892
3893 case VK_UP:
3894 case VK_DOWN: /* If on menu bar, pull-down the menu */
3895 if (MenuGetRosMenuInfo(&MenuInfo, mt.CurrentMenu))
3896 {
3897 if (!(MenuInfo.fFlags & MNF_POPUP))
3898 {
3899 if (MenuGetRosMenuInfo(&MenuInfo, mt.TopMenu))
3900 mt.CurrentMenu = MenuShowSubPopup(mt.OwnerWnd, &MenuInfo, TRUE, wFlags);
3901 }
3902 else /* otherwise try to move selection */
3903 MenuMoveSelection(mt.OwnerWnd, &MenuInfo, (msg.wParam == VK_UP)? ITEM_PREV : ITEM_NEXT );
3904 }
3905 break;
3906
3907 case VK_LEFT:
3908 MenuKeyLeft( &mt, wFlags );
3909 break;
3910
3911 case VK_RIGHT:
3912 MenuKeyRight( &mt, wFlags );
3913 break;
3914
3915 case VK_ESCAPE:
3916 fEndMenu = MenuKeyEscape(&mt, wFlags);
3917 break;
3918
3919 case VK_F1:
3920 {
3921 HELPINFO hi;
3922 hi.cbSize = sizeof(HELPINFO);
3923 hi.iContextType = HELPINFO_MENUITEM;
3924 if (MenuGetRosMenuInfo(&MenuInfo, mt.CurrentMenu))
3925 {
3926 if (MenuInfo.iItem == NO_SELECTED_ITEM)
3927 hi.iCtrlId = 0;
3928 else
3929 {
3930 MenuInitRosMenuItemInfo(&ItemInfo);
3931 if (MenuGetRosMenuItemInfo(MenuInfo.Self,
3932 MenuInfo.iItem,
3933 &ItemInfo))
3934 {
3935 hi.iCtrlId = ItemInfo.wID;
3936 }
3937 else
3938 {
3939 hi.iCtrlId = 0;
3940 }
3941 MenuCleanupRosMenuItemInfo(&ItemInfo);
3942 }
3943 }
3944 hi.hItemHandle = hmenu;
3945 hi.dwContextId = MenuInfo.dwContextHelpID;
3946 hi.MousePos = msg.pt;
3947 SendMessageW(hwnd, WM_HELP, 0, (LPARAM)&hi);
3948 break;
3949 }
3950
3951 default:
3952 break;
3953 }
3954 break; /* WM_KEYDOWN */
3955
3956 case WM_CHAR:
3957 case WM_SYSCHAR:
3958 {
3959 UINT pos;
3960
3961 if (! MenuGetRosMenuInfo(&MenuInfo, mt.CurrentMenu)) break;
3962 if (msg.wParam == L'\r' || msg.wParam == L' ')
3963 {
3964 executedMenuId = MenuExecFocusedItem(&mt, &MenuInfo, wFlags);
3965 fEndMenu = (executedMenuId != -2);
3966 break;
3967 }
3968
3969 /* Hack to avoid control chars. */
3970 /* We will find a better way real soon... */
3971 if (msg.wParam < 32) break;
3972
3973 pos = MENU_FindItemByKey(mt.OwnerWnd, mt.CurrentMenu, LOWORD(msg.wParam), FALSE);
3974 if (pos == (UINT)-2) fEndMenu = TRUE;
3975 else if (pos == (UINT)-1) MessageBeep(0);
3976 else
3977 {
3978 MenuSelectItem(mt.OwnerWnd, &MenuInfo, pos, TRUE, 0);
3979 executedMenuId = MenuExecFocusedItem(&mt, &MenuInfo, wFlags);
3980 fEndMenu = (executedMenuId != -2);
3981 }
3982 }
3983 break;
3984 } /* switch(msg.message) - kbd */
3985 }
3986 else
3987 {
3988 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
3989 DispatchMessageW( &msg );
3990 continue;
3991 }
3992
3993 if (!fEndMenu) fRemove = TRUE;
3994
3995 /* finally remove message from the queue */
3996
3997 if (fRemove && !(mt.TrackFlags & TF_SKIPREMOVE) )
3998 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
3999 else mt.TrackFlags &= ~TF_SKIPREMOVE;
4000 }
4001
4002 NtUserxSetGUIThreadHandle(MSQ_STATE_MENUOWNER, NULL);
4003 SetCapture(NULL); /* release the capture */
4004
4005 /* If dropdown is still painted and the close box is clicked on
4006 then the menu will be destroyed as part of the DispatchMessage above.
4007 This will then invalidate the menu handle in mt.hTopMenu. We should
4008 check for this first. */
4009 if( IsMenu( mt.TopMenu ) )
4010 {
4011 if (IsWindow(mt.OwnerWnd))
4012 {
4013 if (MenuGetRosMenuInfo(&MenuInfo, mt.TopMenu))
4014 {
4015 MenuHideSubPopups(mt.OwnerWnd, &MenuInfo, FALSE, wFlags);
4016
4017 if (MenuInfo.fFlags & MNF_POPUP)
4018 {
4019 IntNotifyWinEvent(EVENT_SYSTEM_MENUPOPUPEND, MenuInfo.Wnd, OBJID_CLIENT, CHILDID_SELF, 0);
4020 DestroyWindow(MenuInfo.Wnd);
4021 MenuInfo.Wnd = NULL;
4022 MenuSetRosMenuInfo(&MenuInfo);
4023
4024 if (!(wFlags & TPM_NONOTIFY))
4025 SendMessageW( mt.OwnerWnd, WM_UNINITMENUPOPUP, (WPARAM)mt.TopMenu,
4026 MAKELPARAM(0, IS_SYSTEM_MENU(&MenuInfo)) );
4027 }
4028 MenuSelectItem( mt.OwnerWnd, &MenuInfo, NO_SELECTED_ITEM, FALSE, 0 );
4029 }
4030
4031 SendMessageW( mt.OwnerWnd, WM_MENUSELECT, MAKEWPARAM(0, 0xffff), 0 );
4032 }
4033
4034 /* Reset the variable for hiding menu */
4035 if (MenuGetRosMenuInfo(&MenuInfo, mt.TopMenu))
4036 {
4037 MenuInfo.TimeToHide = FALSE;
4038 MenuSetRosMenuInfo(&MenuInfo);
4039 }
4040 }
4041
4042 /* The return value is only used by TrackPopupMenu */
4043 if (!(wFlags & TPM_RETURNCMD)) return TRUE;
4044 if (executedMenuId == -1) executedMenuId = 0;
4045 return executedMenuId;
4046 }
4047
4048 /***********************************************************************
4049 * MenuInitTracking
4050 */
4051 static BOOL FASTCALL MenuInitTracking(HWND hWnd, HMENU hMenu, BOOL bPopup, UINT wFlags)
4052 {
4053 ROSMENUINFO MenuInfo = {0};
4054
4055 TRACE("hwnd=%p hmenu=%p\n", hWnd, hMenu);
4056
4057 if (!MenuGetRosMenuInfo(&MenuInfo, hMenu)) return FALSE;
4058
4059 HideCaret(0);
4060
4061 /* This makes the menus of applications built with Delphi work.
4062 * It also enables menus to be displayed in more than one window,
4063 * but there are some bugs left that need to be fixed in this case.
4064 */
4065 if (!bPopup)
4066 {
4067 MenuInfo.Wnd = hWnd;
4068 MenuSetRosMenuInfo(&MenuInfo);
4069 }
4070 //if (!bPopup) menu->hWnd = hWnd;
4071 if (!top_popup)
4072 {
4073 top_popup = MenuInfo.Wnd;//menu->hWnd;
4074 top_popup_hmenu = hMenu;
4075 }
4076
4077 fEndMenu = FALSE;
4078
4079 /* Send WM_ENTERMENULOOP and WM_INITMENU message only if TPM_NONOTIFY flag is not specified */
4080 if (!(wFlags & TPM_NONOTIFY))
4081 SendMessageW( hWnd, WM_ENTERMENULOOP, bPopup, 0 );
4082
4083 SendMessageW( hWnd, WM_SETCURSOR, (WPARAM)hWnd, HTCAPTION );
4084
4085 if (!(wFlags & TPM_NONOTIFY))
4086 {
4087 SendMessageW( hWnd, WM_INITMENU, (WPARAM)hMenu, 0 );
4088 /* If an app changed/recreated menu bar entries in WM_INITMENU
4089 * menu sizes will be recalculated once the menu created/shown.
4090 */
4091
4092 if (!MenuInfo.cyMenu)
4093 {
4094 /* app changed/recreated menu bar entries in WM_INITMENU
4095 Recalculate menu sizes else clicks will not work */
4096 SetWindowPos(hWnd, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE |
4097 SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED );
4098
4099 }
4100 }
4101
4102 IntNotifyWinEvent( EVENT_SYSTEM_MENUSTART,
4103 hWnd,
4104 MenuInfo.fFlags & MNF_SYSMENU ? OBJID_SYSMENU : OBJID_MENU,
4105 CHILDID_SELF, 0);
4106 return TRUE;
4107 }
4108
4109 /***********************************************************************
4110 * MenuExitTracking
4111 */
4112 static BOOL FASTCALL MenuExitTracking(HWND hWnd, BOOL bPopup)
4113 {
4114 TRACE("hwnd=%p\n", hWnd);
4115
4116 IntNotifyWinEvent( EVENT_SYSTEM_MENUEND, hWnd, OBJID_WINDOW, CHILDID_SELF, 0);
4117 SendMessageW( hWnd, WM_EXITMENULOOP, bPopup, 0 );
4118 ShowCaret(0);
4119 top_popup = 0;
4120 top_popup_hmenu = NULL;
4121 return TRUE;
4122 }
4123
4124 /***********************************************************************
4125 * MenuTrackMouseMenuBar
4126 *
4127 * Menu-bar tracking upon a mouse event. Called from NC_HandleSysCommand().
4128 */
4129 VOID MenuTrackMouseMenuBar( HWND hWnd, ULONG ht, POINT pt)
4130 {
4131 HMENU hMenu = (ht == HTSYSMENU) ? get_win_sys_menu( hWnd ) : GetMenu(hWnd);
4132 UINT wFlags = TPM_BUTTONDOWN | TPM_LEFTALIGN | TPM_LEFTBUTTON;
4133
4134 TRACE("wnd=%p ht=0x%04x (%ld,%ld)\n", hWnd, ht, pt.x, pt.y);
4135
4136 if (GetWindowLongW( hWnd, GWL_EXSTYLE ) & WS_EX_LAYOUTRTL) wFlags |= TPM_LAYOUTRTL;
4137 if (IsMenu(hMenu))
4138 {
4139 /* map point to parent client coordinates */
4140 HWND Parent = GetAncestor(hWnd, GA_PARENT );
4141 if (Parent != GetDesktopWindow())
4142 {
4143 ScreenToClient(Parent, &pt);
4144 }
4145
4146 MenuInitTracking(hWnd, hMenu, FALSE, wFlags);
4147 /* fetch the window menu again, it may have changed */
4148 hMenu = (ht == HTSYSMENU) ? get_win_sys_menu( hWnd ) : GetMenu( hWnd );
4149 MenuTrackMenu(hMenu, wFlags, pt.x, pt.y, hWnd, NULL);
4150 MenuExitTracking(hWnd, FALSE);
4151 }
4152 }
4153
4154 /***********************************************************************
4155 * MenuTrackKbdMenuBar
4156 *
4157 * Menu-bar tracking upon a keyboard event. Called from NC_HandleSysCommand().
4158 */
4159 VOID MenuTrackKbdMenuBar(HWND hwnd, UINT wParam, WCHAR wChar)
4160 {
4161 UINT uItem = NO_SELECTED_ITEM;
4162 HMENU hTrackMenu;
4163 ROSMENUINFO MenuInfo;
4164 UINT wFlags = TPM_LEFTALIGN | TPM_LEFTBUTTON;
4165
4166 TRACE("hwnd %p wParam 0x%04x wChar 0x%04x\n", hwnd, wParam, wChar);
4167
4168 /* find window that has a menu */
4169
4170 while (!((GetWindowLongPtrW( hwnd, GWL_STYLE ) &
4171 (WS_CHILD | WS_POPUP)) != WS_CHILD))
4172 if (!(hwnd = GetAncestor( hwnd, GA_PARENT ))) return;
4173
4174 /* check if we have to track a system menu */
4175
4176 hTrackMenu = GetMenu( hwnd );
4177 if (!hTrackMenu || IsIconic(hwnd) || wChar == ' ' )
4178 {
4179 if (!(GetWindowLongPtrW( hwnd, GWL_STYLE ) & WS_SYSMENU)) return;
4180 hTrackMenu = get_win_sys_menu(hwnd);
4181 uItem = 0;
4182 wParam |= HTSYSMENU; /* prevent item lookup */
4183 }
4184
4185 if (!IsMenu( hTrackMenu )) return;
4186
4187 MenuInitTracking( hwnd, hTrackMenu, FALSE, wFlags );
4188
4189 /* fetch the window menu again, it may have changed */
4190 hTrackMenu = (wParam & HTSYSMENU) ? get_win_sys_menu( hwnd ) : GetMenu( hwnd );
4191
4192 if (! MenuGetRosMenuInfo(&MenuInfo, hTrackMenu))
4193 {
4194 goto track_menu;
4195 }
4196
4197 if( wChar && wChar != ' ' )
4198 {
4199 uItem = MENU_FindItemByKey( hwnd, hTrackMenu, wChar, (wParam & HTSYSMENU) );
4200 if ( uItem >= (UINT)(-2) )
4201 {
4202 if( uItem == (UINT)(-1) ) MessageBeep(0);
4203 /* schedule end of menu tracking */
4204 wFlags |= TF_ENDMENU;
4205 goto track_menu;
4206 }
4207 }
4208
4209 MenuSelectItem( hwnd, &MenuInfo, uItem, TRUE, 0 );
4210
4211 if (!(wParam & HTSYSMENU) || wChar == ' ')
4212 {
4213 if( uItem == NO_SELECTED_ITEM )
4214 MenuMoveSelection( hwnd, &MenuInfo, ITEM_NEXT );
4215 else
4216 PostMessageW( hwnd, WM_KEYDOWN, VK_RETURN, 0 );
4217 }
4218
4219 track_menu:
4220 MenuTrackMenu( hTrackMenu, wFlags, 0, 0, hwnd, NULL );
4221 MenuExitTracking( hwnd, FALSE );
4222 }
4223
4224 /**********************************************************************
4225 * TrackPopupMenuEx (USER32.@)
4226 */
4227 BOOL WINAPI TrackPopupMenuEx( HMENU hMenu, UINT wFlags, int x, int y,
4228 HWND hWnd, LPTPMPARAMS lpTpm)
4229 {
4230 BOOL ret = FALSE;
4231 MENU *menu;
4232
4233 TRACE("hmenu %p flags %04x (%d,%d) hwnd %p lpTpm %p rect %s\n",
4234 hMenu, wFlags, x, y, hWnd, lpTpm,
4235 lpTpm ? wine_dbgstr_rect( &lpTpm->rcExclude) : "-" );
4236
4237 /* Parameter check */
4238 /* FIXME: this check is performed several times, here and in the called
4239 functions. That could be optimized */
4240 if (!(menu = MENU_GetMenu( hMenu )))
4241 {
4242 SetLastError( ERROR_INVALID_MENU_HANDLE );
4243 return FALSE;
4244 }
4245
4246 if (IsWindow(menu->hWnd))
4247 {
4248 SetLastError( ERROR_POPUP_ALREADY_ACTIVE );
4249 return FALSE;
4250 }
4251
4252 if (MENU_InitPopup( hWnd, hMenu, wFlags ))
4253 {
4254 MenuInitTracking(hWnd, hMenu, TRUE, wFlags);
4255
4256 /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
4257 if (!(wFlags & TPM_NONOTIFY))
4258 SendMessageW(hWnd, WM_INITMENUPOPUP, (WPARAM) hMenu, 0);
4259
4260 if (MenuShowPopup(hWnd, hMenu, 0, wFlags, x, y, 0, 0 ))
4261 ret = MenuTrackMenu(hMenu, wFlags | TPM_POPUPMENU, 0, 0, hWnd,
4262 lpTpm ? &lpTpm->rcExclude : NULL);
4263 MenuExitTracking(hWnd, TRUE);
4264
4265 if (menu->hWnd)
4266 {
4267 ROSMENUINFO MenuInfo;
4268 if (IsWindow( menu->hWnd )) // wine hack around this with their destroy function.
4269 DestroyWindow( menu->hWnd ); // Fix wrong error return.
4270 //menu->hWnd = 0;
4271 MenuGetRosMenuInfo(&MenuInfo, menu->head.h);
4272 MenuInfo.Wnd = 0;
4273 MenuSetRosMenuInfo(&MenuInfo);
4274
4275 if (!(wFlags & TPM_NONOTIFY))
4276 SendMessageW( hWnd, WM_UNINITMENUPOPUP, (WPARAM)hMenu,
4277 MAKELPARAM(0, IS_SYSTEM_MENU(menu)) );
4278 }
4279 }
4280 return ret;
4281 }
4282
4283 /**********************************************************************
4284 * TrackPopupMenu (USER32.@)
4285 */
4286 BOOL WINAPI TrackPopupMenu( HMENU Menu, UINT Flags, int x, int y,
4287 int Reserved, HWND Wnd, CONST RECT *Rect)
4288 {
4289 return TrackPopupMenuEx( Menu, Flags, x, y, Wnd, NULL);
4290 }
4291
4292 /**********************************************************************
4293 * MENU_mnu2mnuii
4294 *
4295 * Uses flags, id and text ptr, passed by InsertMenu() and
4296 * ModifyMenu() to setup a MenuItemInfo structure.
4297 */
4298 static void MENU_mnu2mnuii( UINT flags, UINT_PTR id, LPCWSTR str, LPMENUITEMINFOW pmii, BOOL Unicode)
4299 {
4300 RtlZeroMemory( pmii, sizeof( MENUITEMINFOW));
4301 pmii->cbSize = sizeof( MENUITEMINFOW);
4302 pmii->fMask = MIIM_STATE | MIIM_ID | MIIM_FTYPE;
4303 /* setting bitmap clears text and vice versa */
4304 if( IS_STRING_ITEM(flags)) {
4305 pmii->fMask |= MIIM_STRING | MIIM_BITMAP;
4306 if( !str)
4307 flags |= MF_SEPARATOR;
4308 /* Item beginning with a backspace is a help item */
4309 /* FIXME: wrong place, this is only true in win16 */
4310 else
4311 {
4312 if (Unicode)
4313 {
4314 if (*str == '\b')
4315 {
4316 flags |= MF_HELP;
4317 str++;
4318 }
4319 }
4320 else
4321 {
4322 LPCSTR NewItemA = (LPCSTR) str;
4323 if (*NewItemA == '\b')
4324 {
4325 flags |= MF_HELP;
4326 NewItemA++;
4327 str = (LPCWSTR) NewItemA;
4328 }
4329 TRACE("A cch %d\n",strlen(NewItemA));
4330 }
4331 }
4332 pmii->dwTypeData = (LPWSTR)str;
4333 } else if( flags & MFT_BITMAP){
4334 pmii->fMask |= MIIM_BITMAP | MIIM_STRING;
4335 pmii->hbmpItem = (HBITMAP)str;
4336 }
4337 if( flags & MF_OWNERDRAW){
4338 pmii->fMask |= MIIM_DATA;
4339 pmii->dwItemData = (ULONG_PTR) str;
4340 }
4341 if( flags & MF_POPUP && MENU_GetMenu((HMENU)id)) {
4342 pmii->fMask |= MIIM_SUBMENU;
4343 pmii->hSubMenu = (HMENU)id;
4344 }
4345 if( flags & MF_SEPARATOR) flags |= MF_GRAYED | MF_DISABLED;
4346 pmii->fState = flags & MENUITEMINFO_STATE_MASK & ~MFS_DEFAULT;
4347 pmii->fType = flags & MENUITEMINFO_TYPE_MASK;
4348 pmii->wID = (UINT)id;
4349 }
4350
4351 /**********************************************************************
4352 * MENU_NormalizeMenuItemInfoStruct
4353 *
4354 * Helper for SetMenuItemInfo and InsertMenuItemInfo:
4355 * check, copy and extend the MENUITEMINFO struct from the version that the application
4356 * supplied to the version used by wine source. */
4357 static BOOL MENU_NormalizeMenuItemInfoStruct( const MENUITEMINFOW *pmii_in,
4358 MENUITEMINFOW *pmii_out )
4359 {
4360 /* do we recognize the size? */
4361 if( !pmii_in || (pmii_in->cbSize != sizeof( MENUITEMINFOW) &&
4362 pmii_in->cbSize != sizeof( MENUITEMINFOW) - sizeof( pmii_in->hbmpItem)) ) {
4363 SetLastError( ERROR_INVALID_PARAMETER);
4364 return FALSE;
4365 }
4366 /* copy the fields that we have */
4367 memcpy( pmii_out, pmii_in, pmii_in->cbSize);
4368 /* if the hbmpItem member is missing then extend */
4369 if( pmii_in->cbSize != sizeof( MENUITEMINFOW)) {
4370 pmii_out->cbSize = sizeof( MENUITEMINFOW);
4371 pmii_out->hbmpItem = NULL;
4372 }
4373 /* test for invalid bit combinations */
4374 if( (pmii_out->fMask & MIIM_TYPE &&
4375 pmii_out->fMask & (MIIM_STRING | MIIM_FTYPE | MIIM_BITMAP)) ||
4376 (pmii_out->fMask & MIIM_FTYPE && pmii_out->fType & MFT_BITMAP)) {
4377 ERR("invalid combination of fMask bits used\n");
4378 /* this does not happen on Win9x/ME */
4379 SetLastError( ERROR_INVALID_PARAMETER);
4380 return FALSE;
4381 }
4382 /* convert old style (MIIM_TYPE) to the new and keep the old one too */
4383 if( pmii_out->fMask & MIIM_TYPE){
4384 pmii_out->fMask |= MIIM_FTYPE;
4385 if( IS_STRING_ITEM(pmii_out->fType)){
4386 pmii_out->fMask |= MIIM_STRING;
4387 } else if( (pmii_out->fType) & MFT_BITMAP){
4388 pmii_out->fMask |= MIIM_BITMAP;
4389 pmii_out->hbmpItem = UlongToHandle(LOWORD(pmii_out->dwTypeData));
4390 }
4391 }
4392 if (pmii_out->fMask & MIIM_FTYPE )
4393 {
4394 pmii_out->fType &= ~MENUITEMINFO_TYPE_MASK;
4395 pmii_out->fType |= pmii_in->fType & MENUITEMINFO_TYPE_MASK;
4396 }
4397 if (pmii_out->fMask & MIIM_STATE)
4398 /* Other menu items having MFS_DEFAULT are not converted
4399 to normal items */
4400 pmii_out->fState = pmii_in->fState & MENUITEMINFO_STATE_MASK;
4401
4402 if (pmii_out->fMask & MIIM_SUBMENU)
4403 {
4404 if ((pmii_out->hSubMenu != NULL) && !IsMenu(pmii_out->hSubMenu))
4405 return FALSE;
4406 }
4407
4408 return TRUE;
4409 }
4410
4411 BOOL
4412 MenuInit(VOID)
4413 {
4414 NONCLIENTMETRICSW ncm;
4415
4416 /* get the menu font */
4417 if(!hMenuFont || !hMenuFontBold)
4418 {
4419 ncm.cbSize = sizeof(ncm);
4420 if(!SystemParametersInfoW(SPI_GETNONCLIENTMETRICS, sizeof(ncm), &ncm, 0))
4421 {
4422 ERR("MenuInit(): SystemParametersInfoW(SPI_GETNONCLIENTMETRICS) failed!\n");
4423 return FALSE;
4424 }
4425
4426 hMenuFont = CreateFontIndirectW(&ncm.lfMenuFont);
4427 if(hMenuFont == NULL)
4428 {
4429 ERR("MenuInit(): CreateFontIndirectW(hMenuFont) failed!\n");
4430 return FALSE;
4431 }
4432
4433 ncm.lfMenuFont.lfWeight = max(ncm.lfMenuFont.lfWeight + 300, 1000);
4434 hMenuFontBold = CreateFontIndirectW(&ncm.lfMenuFont);
4435 if(hMenuFontBold == NULL)
4436 {
4437 ERR("MenuInit(): CreateFontIndirectW(hMenuFontBold) failed!\n");
4438 DeleteObject(hMenuFont);
4439 hMenuFont = NULL;
4440 return FALSE;
4441 }
4442 }
4443
4444 return TRUE;
4445 }
4446
4447 VOID
4448 MenuCleanup(VOID)
4449 {
4450 if (hMenuFont)
4451 {
4452 DeleteObject(hMenuFont);
4453 hMenuFont = NULL;
4454 }
4455
4456 if (hMenuFontBold)
4457 {
4458 DeleteObject(hMenuFontBold);
4459 hMenuFontBold = NULL;
4460 }
4461 }
4462
4463 HMENU FASTCALL MENU_LoadSystemMenu(BOOL mdi)
4464 {
4465 HMENU hmenu = LoadMenuW(User32Instance, L"SYSMENU");
4466
4467 if (hmenu)
4468 {
4469 MENUINFO menuinfo = {0};
4470 MENUITEMINFOW info = {0};
4471 //WCHAR buf[128];
4472
4473 // removing space for checkboxes from menu
4474 menuinfo.cbSize = sizeof(menuinfo);
4475 menuinfo.fMask = MIM_STYLE;
4476 GetMenuInfo(hmenu, &menuinfo);
4477 menuinfo.dwStyle |= MNS_CHECKORBMP;
4478 SetMenuInfo(hmenu, &menuinfo);
4479
4480 // adding bitmaps to menu items
4481 info.cbSize = sizeof(info);
4482 info.fMask |= MIIM_BITMAP;
4483 info.hbmpItem = HBMMENU_POPUP_MINIMIZE;
4484 SetMenuItemInfoW(hmenu, SC_MINIMIZE, FALSE, &info);
4485 info.hbmpItem = HBMMENU_POPUP_RESTORE;
4486 SetMenuItemInfoW(hmenu, SC_RESTORE, FALSE, &info);
4487 info.hbmpItem = HBMMENU_POPUP_MAXIMIZE;
4488 SetMenuItemInfoW(hmenu, SC_MAXIMIZE, FALSE, &info);
4489 info.hbmpItem = HBMMENU_POPUP_CLOSE;
4490 SetMenuItemInfoW(hmenu, SC_CLOSE, FALSE, &info);
4491 if (mdi)
4492 {
4493 AppendMenuW(hmenu, MF_SEPARATOR, 0, NULL);
4494 //LoadStringW(User32Instance, IDS_MDI_NEXT, buf, sizeof(buf)/sizeof(WCHAR));
4495 //AppendMenuW(hmenu, MF_STRING, SC_NEXTWINDOW, buf);
4496 }
4497 }
4498 return hmenu;
4499 }
4500
4501 NTSTATUS WINAPI
4502 User32LoadSysMenuTemplateForKernel(PVOID Arguments, ULONG ArgumentLength)
4503 {
4504 LRESULT Result = 0;
4505
4506 // Will be converted to load bitmaps for OBMI!
4507
4508 return(ZwCallbackReturn(&Result, sizeof(LRESULT), STATUS_SUCCESS));
4509 }
4510
4511 NTSTATUS WINAPI
4512 User32CallLoadMenuFromKernel(PVOID Arguments, ULONG ArgumentLength)
4513 {
4514 PLOADMENU_CALLBACK_ARGUMENTS Common;
4515 LRESULT Result;
4516
4517 Common = (PLOADMENU_CALLBACK_ARGUMENTS) Arguments;
4518
4519 Result = (LRESULT)LoadMenuW( Common->hModule, Common->InterSource ? MAKEINTRESOURCE(Common->InterSource) : (LPCWSTR)&Common->MenuName);
4520
4521 return ZwCallbackReturn(&Result, sizeof(LRESULT), STATUS_SUCCESS);
4522 }
4523
4524
4525 /* FUNCTIONS *****************************************************************/
4526
4527 /*
4528 * @implemented
4529 */
4530 BOOL WINAPI
4531 AppendMenuA(HMENU hMenu,
4532 UINT uFlags,
4533 UINT_PTR uIDNewItem,
4534 LPCSTR lpNewItem)
4535 {
4536 MENUITEMINFOW mii;
4537 UNICODE_STRING UnicodeString;
4538 BOOL res;
4539
4540 RtlInitUnicodeString(&UnicodeString, 0);
4541
4542 MENU_mnu2mnuii( uFlags, uIDNewItem, (LPCWSTR)lpNewItem, &mii, FALSE);
4543
4544 /* copy the text string, it will be one or the other */
4545 if (lpNewItem && mii.fMask & MIIM_STRING && !mii.hbmpItem && mii.dwTypeData)
4546 {
4547 if (!RtlCreateUnicodeStringFromAsciiz(&UnicodeString, (LPSTR)mii.dwTypeData))
4548 {
4549 SetLastError (ERROR_NOT_ENOUGH_MEMORY);
4550 return FALSE;
4551 }
4552 mii.dwTypeData = UnicodeString.Buffer;
4553 mii.cch = UnicodeString.Length / sizeof(WCHAR);
4554 }
4555 else
4556 {
4557 TRACE("AMA Handle bitmaps\n");
4558 }
4559 ////// Answer a question, why a -1? To hunt for the end of the item list. Get it, to Append?
4560 res = NtUserThunkedMenuItemInfo(hMenu, -1, TRUE, TRUE, &mii, &UnicodeString);
4561 if ( UnicodeString.Buffer ) RtlFreeUnicodeString ( &UnicodeString );
4562 return res;
4563 }
4564
4565 /*
4566 * @implemented
4567 */
4568 BOOL WINAPI
4569 AppendMenuW(HMENU hMenu,
4570 UINT uFlags,
4571 UINT_PTR uIDNewItem,
4572 LPCWSTR lpNewItem)
4573 {
4574 MENUITEMINFOW mii;
4575 UNICODE_STRING MenuText;
4576 BOOL res;
4577
4578 RtlInitUnicodeString(&MenuText, 0);
4579
4580 MENU_mnu2mnuii( uFlags, uIDNewItem, lpNewItem, &mii, TRUE);
4581
4582 /* copy the text string, it will be one or the other */
4583 if (lpNewItem && mii.fMask & MIIM_STRING && !mii.hbmpItem && mii.dwTypeData)
4584 {
4585 RtlInitUnicodeString(&MenuText, (PWSTR)mii.dwTypeData);
4586 mii.dwTypeData = MenuText.Buffer;
4587 mii.cch = MenuText.Length / sizeof(WCHAR);
4588 }
4589 res = NtUserThunkedMenuItemInfo(hMenu, -1, TRUE, TRUE, &mii, &MenuText);
4590 return res;
4591 }
4592
4593 /*
4594 * @implemented
4595 */
4596 DWORD WINAPI
4597 CheckMenuItem(HMENU hmenu,
4598 UINT uIDCheckItem,
4599 UINT uCheck)
4600 {
4601 PITEM item;
4602 DWORD Ret;
4603 UINT uID = uIDCheckItem;
4604
4605 if (!ValidateHandle(hmenu, TYPE_MENU))
4606 return -1;
4607
4608 if (!(item = MENU_FindItem( &hmenu, &uID, uCheck ))) return -1;
4609
4610 Ret = item->fState & MFS_CHECKED;
4611 if ( Ret == (uCheck & MFS_CHECKED)) return Ret; // Already Checked...
4612
4613 return NtUserCheckMenuItem(hmenu, uIDCheckItem, uCheck);
4614 }
4615
4616 /*
4617 * @implemented
4618 */
4619 BOOL WINAPI
4620 CheckMenuRadioItem(HMENU hMenu,
4621 UINT first,
4622 UINT last,
4623 UINT check,
4624 UINT bypos)
4625 {
4626 BOOL done = FALSE;
4627 UINT i;
4628 PITEM mi_first = NULL, mi_check;
4629 HMENU m_first, m_check;
4630 MENUITEMINFOW mii;
4631 mii.cbSize = sizeof( mii);
4632
4633 for (i = first; i <= last; i++)
4634 {
4635 UINT pos = i;
4636
4637 if (!mi_first)
4638 {
4639 m_first = hMenu;
4640 mi_first = MENU_FindItem(&m_first, &pos, bypos);
4641 if (!mi_first) continue;
4642 mi_check = mi_first;
4643 m_check = m_first;
4644 }
4645 else
4646 {
4647 m_check = hMenu;
4648 mi_check = MENU_FindItem(&m_check, &pos, bypos);
4649 if (!mi_check) continue;
4650 }
4651
4652 if (m_first != m_check) continue;
4653 if (mi_check->fType == MFT_SEPARATOR) continue;
4654
4655 if (i == check)
4656 {
4657 if (!(mi_check->fType & MFT_RADIOCHECK) || !(mi_check->fState & MFS_CHECKED))
4658 {
4659 mii.fMask = MIIM_FTYPE | MIIM_STATE;
4660 mii.fType = (mi_check->fType & MENUITEMINFO_TYPE_MASK) | MFT_RADIOCHECK;
4661 mii.fState = (mi_check->fState & MII_STATE_MASK) | MFS_CHECKED;
4662 NtUserThunkedMenuItemInfo(m_check, i, bypos, FALSE, &mii, NULL);
4663 }
4664 done = TRUE;
4665 }
4666 else
4667 {
4668 /* MSDN is wrong, Windows does not remove MFT_RADIOCHECK */
4669 if (mi_check->fState & MFS_CHECKED)
4670 {
4671 mii.fMask = MIIM_STATE;
4672 mii.fState = (mi_check->fState & MII_STATE_MASK) & ~MFS_CHECKED;
4673 NtUserThunkedMenuItemInfo(m_check, i, bypos, FALSE, &mii, NULL);
4674 }
4675 }
4676 }
4677 return done;
4678 }
4679
4680 /*
4681 * @implemented
4682 */
4683 HMENU WINAPI
4684 CreateMenu(VOID)
4685 {
4686 MenuLoadBitmaps();
4687 return NtUserxCreateMenu();
4688 }
4689
4690 /*
4691 * @implemented
4692 */
4693 HMENU WINAPI
4694 CreatePopupMenu(VOID)
4695 {
4696 MenuLoadBitmaps();
4697 return NtUserxCreatePopupMenu();
4698 }
4699
4700 /*
4701 * @implemented
4702 */
4703 BOOL WINAPI
4704 DrawMenuBar(HWND hWnd)
4705 {
4706 return NtUserxDrawMenuBar(hWnd);
4707 }
4708
4709 /*
4710 * @implemented
4711 */
4712 BOOL WINAPI
4713 EnableMenuItem(HMENU hMenu,
4714 UINT uIDEnableItem,
4715 UINT uEnable)
4716 {
4717 return NtUserEnableMenuItem(hMenu, uIDEnableItem, uEnable);
4718 }
4719
4720 /*
4721 * @implemented
4722 */
4723 BOOL WINAPI
4724 EndMenu(VOID)
4725 {
4726 GUITHREADINFO guii;
4727 guii.cbSize = sizeof(GUITHREADINFO);
4728 if(GetGUIThreadInfo(GetCurrentThreadId(), &guii) && guii.hwndMenuOwner)
4729 {
4730 if (!fEndMenu &&
4731 top_popup &&
4732 guii.hwndMenuOwner != top_popup )
4733 {
4734 ERR("Capture GUI pti hWnd does not match top_popup!\n");
4735 }
4736 }
4737
4738 /* if we are in the menu code, and it is active */
4739 if (!fEndMenu && top_popup)
4740 {
4741 /* terminate the menu handling code */
4742 fEndMenu = TRUE;
4743
4744 /* needs to be posted to wakeup the internal menu handler */
4745 /* which will now terminate the menu, in the event that */
4746 /* the main window was minimized, or lost focus, so we */
4747 /* don't end up with an orphaned menu */
4748 PostMessageW( top_popup, WM_CANCELMODE, 0, 0);
4749 }
4750 return fEndMenu;
4751 }
4752
4753 BOOL WINAPI HiliteMenuItem( HWND hWnd, HMENU hMenu, UINT wItemID,
4754 UINT wHilite )
4755 {
4756 ROSMENUINFO MenuInfo;
4757 TRACE("(%p, %p, %04x, %04x);\n", hWnd, hMenu, wItemID, wHilite);
4758 // Force bits to be set call server side....
4759 // This alone works and passes all the menu test_menu_hilitemenuitem tests.
4760 if (!NtUserHiliteMenuItem(hWnd, hMenu, wItemID, wHilite)) return FALSE;
4761 // Without the above call we fail 3 out of the wine failed todo tests, see CORE-7967
4762 // Now redraw menu.
4763 if (MenuGetRosMenuInfo(&MenuInfo, hMenu))
4764 {
4765 if (MenuInfo.iItem == wItemID) return TRUE;
4766 MenuHideSubPopups( hWnd, &MenuInfo, FALSE, 0 );
4767 MenuSelectItem( hWnd, &MenuInfo, wItemID, TRUE, 0 );
4768 }
4769 return TRUE; // Always returns TRUE!
4770 }
4771
4772 /*
4773 * @implemented
4774 */
4775 HMENU WINAPI
4776 GetMenu(HWND hWnd)
4777 {
4778 PWND Wnd = ValidateHwnd(hWnd);
4779
4780 if (!Wnd)
4781 return NULL;
4782
4783 return UlongToHandle(Wnd->IDMenu);
4784 }
4785
4786 /*
4787 * @implemented
4788 */
4789 BOOL WINAPI GetMenuBarInfo( HWND hwnd, LONG idObject, LONG idItem, PMENUBARINFO pmbi )
4790 {
4791 BOOL Ret;
4792 Ret = NtUserGetMenuBarInfo( hwnd, idObject, idItem, pmbi);
4793 // Reason to move to server side!!!!!
4794 if (!Ret) return Ret;
4795 // EL HAXZO!!!
4796 pmbi->fBarFocused = top_popup_hmenu == pmbi->hMenu;
4797 if (!idItem)
4798 {
4799 pmbi->fFocused = pmbi->fBarFocused;
4800 }
4801
4802 return TRUE;
4803 }
4804
4805 /*
4806 * @implemented
4807 */
4808 LONG WINAPI
4809 GetMenuCheckMarkDimensions(VOID)
4810 {
4811 return(MAKELONG(GetSystemMetrics(SM_CXMENUCHECK),
4812 GetSystemMetrics(SM_CYMENUCHECK)));
4813 }
4814
4815 /*
4816 * @implemented
4817 */
4818 DWORD
4819 WINAPI
4820 GetMenuContextHelpId(HMENU hmenu)
4821 {
4822 PMENU pMenu;
4823 if ((pMenu = ValidateHandle(hmenu, TYPE_MENU)))
4824 return pMenu->dwContextHelpId;
4825 return 0;
4826 }
4827
4828 /*
4829 * @implemented
4830 */
4831 UINT WINAPI
4832 GetMenuDefaultItem(HMENU hMenu,
4833 UINT fByPos,
4834 UINT gmdiFlags)
4835 {
4836 PMENU pMenu;
4837 DWORD gismc = 0;
4838 if (!(pMenu = ValidateHandle(hMenu, TYPE_MENU)))
4839 return (UINT)-1;
4840
4841 return IntGetMenuDefaultItem( pMenu, (BOOL)fByPos, gmdiFlags, &gismc);
4842 }
4843
4844 /*
4845 * @implemented
4846 */
4847 BOOL WINAPI
4848 GetMenuInfo(HMENU hmenu,
4849 LPMENUINFO lpcmi)
4850 {
4851 PMENU pMenu;
4852
4853 if (!lpcmi || (lpcmi->cbSize != sizeof(MENUINFO)))
4854 {
4855 SetLastError(ERROR_INVALID_PARAMETER);
4856 return FALSE;
4857 }
4858
4859 if (!(pMenu = ValidateHandle(hmenu, TYPE_MENU)))
4860 return FALSE;
4861
4862 if (lpcmi->fMask & MIM_BACKGROUND)
4863 lpcmi->hbrBack = pMenu->hbrBack;
4864
4865 if (lpcmi->fMask & MIM_HELPID)
4866 lpcmi->dwContextHelpID = pMenu->dwContextHelpId;
4867
4868 if (lpcmi->fMask & MIM_MAXHEIGHT)
4869 lpcmi->cyMax = pMenu->cyMax;
4870
4871 if (lpcmi->fMask & MIM_MENUDATA)
4872 lpcmi->dwMenuData = pMenu->dwMenuData;
4873
4874 if (lpcmi->fMask & MIM_STYLE)
4875 lpcmi->dwStyle = pMenu->fFlags & MNS_STYLE_MASK;
4876
4877 return TRUE;
4878 }
4879
4880 /*
4881 * @implemented
4882 */
4883 int WINAPI
4884 GetMenuItemCount(HMENU hmenu)
4885 {
4886 PMENU pMenu;
4887 if ((pMenu = ValidateHandle(hmenu, TYPE_MENU)))
4888 return pMenu->cItems;
4889 return -1;
4890 }
4891
4892 /*
4893 * @implemented
4894 */
4895 UINT WINAPI
4896 GetMenuItemID(HMENU hMenu,
4897 int nPos)
4898 {
4899 ITEM * lpmi;
4900 if (!(lpmi = MENU_FindItem(&hMenu,(UINT*)&nPos,MF_BYPOSITION))) return -1;
4901 if (lpmi->spSubMenu) return -1;
4902 return lpmi->wID;
4903 }
4904
4905 /*
4906 * @implemented
4907 */
4908 BOOL WINAPI
4909 GetMenuItemInfoA(
4910 HMENU hmenu,
4911 UINT item,
4912 BOOL bypos,
4913 LPMENUITEMINFOA lpmii)
4914 {
4915 BOOL ret;
4916 MENUITEMINFOA mii;
4917
4918 if( lpmii->cbSize != sizeof( mii) &&
4919 lpmii->cbSize != sizeof( mii) - sizeof ( mii.hbmpItem))
4920 {
4921 SetLastError( ERROR_INVALID_PARAMETER);
4922 return FALSE;
4923 }
4924 memcpy( &mii, lpmii, lpmii->cbSize);
4925 mii.cbSize = sizeof( mii);
4926 ret = GetMenuItemInfo_common (hmenu,
4927 item,
4928 bypos,
4929 (LPMENUITEMINFOW)&mii,
4930 FALSE);
4931 mii.cbSize = lpmii->cbSize;
4932 memcpy( lpmii, &mii, mii.cbSize);
4933 return ret;
4934 }
4935
4936 /*
4937 * @implemented
4938 */
4939 BOOL WINAPI
4940 GetMenuItemInfoW(
4941 HMENU hMenu,
4942 UINT Item,
4943 BOOL bypos,
4944 LPMENUITEMINFOW lpmii)
4945 {
4946 BOOL ret;
4947 MENUITEMINFOW mii;
4948 if( lpmii->cbSize != sizeof( mii) && lpmii->cbSize != sizeof( mii) - sizeof ( mii.hbmpItem))
4949 {
4950 SetLastError( ERROR_INVALID_PARAMETER);
4951 return FALSE;
4952 }
4953 memcpy( &mii, lpmii, lpmii->cbSize);
4954 mii.cbSize = sizeof( mii);
4955 ret = GetMenuItemInfo_common (hMenu, Item, bypos, &mii, TRUE);
4956 mii.cbSize = lpmii->cbSize;
4957 memcpy( lpmii, &mii, mii.cbSize);
4958 return ret;
4959 }
4960
4961 /*
4962 * @implemented
4963 */
4964 UINT
4965 WINAPI
4966 GetMenuState(
4967 HMENU hMenu,
4968 UINT uId,
4969 UINT uFlags)
4970 {
4971 PITEM pItem;
4972 UINT Type = 0;
4973 TRACE("(menu=%p, id=%04x, flags=%04x);\n", hMenu, uId, uFlags);
4974 if (!(pItem = MENU_FindItem( &hMenu, &uId, uFlags ))) return -1;
4975
4976 if (!pItem->Xlpstr && pItem->hbmp) Type = MFT_BITMAP;
4977
4978 if (pItem->spSubMenu)
4979 {
4980 PMENU pSubMenu = DesktopPtrToUser(pItem->spSubMenu);
4981 HMENU hsubmenu = UserHMGetHandle(pSubMenu);
4982 if (!IsMenu(hsubmenu)) return (UINT)-1;
4983 else return (pSubMenu->cItems << 8) | ((pItem->fState|pItem->fType|Type) & 0xff);
4984 }
4985 else
4986 return (pItem->fType | pItem->fState | Type);
4987 }
4988
4989 /*
4990 * @implemented
4991 */
4992 int
4993 WINAPI
4994 GetMenuStringA(
4995 HMENU hMenu,
4996 UINT uIDItem,
4997 LPSTR lpString,
4998 int nMaxCount,
4999 UINT uFlag)
5000 {
5001 ITEM *item;
5002 LPWSTR text;
5003 ////// wine Code, seems to be faster.
5004 TRACE("menu=%p item=%04x ptr=%p len=%d flags=%04x\n", hMenu, uIDItem, lpString, nMaxCount, uFlag );
5005
5006 if (lpString && nMaxCount) lpString[0] = '\0';
5007
5008 if (!(item = MENU_FindItem( &hMenu, &uIDItem, uFlag )))
5009 {
5010 SetLastError( ERROR_MENU_ITEM_NOT_FOUND);
5011 return 0;
5012 }
5013
5014 text = item->Xlpstr ? DesktopPtrToUser(item->Xlpstr) : NULL;
5015
5016 if (!text) return 0;
5017 if (!lpString || !nMaxCount) return WideCharToMultiByte( CP_ACP, 0, text, -1, NULL, 0, NULL, NULL );
5018 if (!WideCharToMultiByte( CP_ACP, 0, text, -1, lpString, nMaxCount, NULL, NULL ))
5019 lpString[nMaxCount-1] = 0;
5020 TRACE("A returning %s\n", lpString);
5021 return strlen(lpString);
5022 }
5023
5024 /*
5025 * @implemented
5026 */
5027 int
5028 WINAPI
5029 GetMenuStringW(
5030 HMENU hMenu,
5031 UINT uIDItem,
5032 LPWSTR lpString,
5033 int nMaxCount,
5034 UINT uFlag)
5035 {
5036 ITEM *item;
5037 LPWSTR text;
5038
5039 TRACE("menu=%p item=%04x ptr=%p len=%d flags=%04x\n", hMenu, uIDItem, lpString, nMaxCount, uFlag );
5040
5041 if (lpString && nMaxCount) lpString[0] = '\0';
5042
5043 if (!(item = MENU_FindItem( &hMenu, &uIDItem, uFlag )))
5044 {
5045 SetLastError( ERROR_MENU_ITEM_NOT_FOUND);
5046 return 0;
5047 }
5048
5049 text = item->Xlpstr ? DesktopPtrToUser(item->Xlpstr) : NULL;
5050
5051 if (!lpString || !nMaxCount) return text ? strlenW(text) : 0;
5052 if( !(text))
5053 {
5054 lpString[0] = 0;
5055 return 0;
5056 }
5057 lstrcpynW( lpString, text, nMaxCount );
5058 TRACE("W returning %S\n", lpString);
5059 return strlenW(lpString);
5060 }
5061
5062 /*
5063 * @implemented
5064 */
5065 HMENU
5066 WINAPI
5067 GetSubMenu(
5068 HMENU hMenu,
5069 int nPos)
5070 {
5071 PITEM pItem;
5072 if (!(pItem = MENU_FindItem( &hMenu, (UINT*)&nPos, MF_BYPOSITION ))) return NULL;
5073
5074 if (pItem->spSubMenu)
5075 {
5076 PMENU pSubMenu = DesktopPtrToUser(pItem->spSubMenu);
5077 HMENU hsubmenu = UserHMGetHandle(pSubMenu);
5078 if (IsMenu(hsubmenu)) return hsubmenu;
5079 }
5080 return NULL;
5081 }
5082
5083 /*
5084 * @implemented
5085 */
5086 HMENU
5087 WINAPI
5088 GetSystemMenu(
5089 HWND hWnd,
5090 BOOL bRevert)
5091 {
5092 return NtUserGetSystemMenu(hWnd, bRevert);
5093 }
5094
5095 /*
5096 * @implemented
5097 */
5098 BOOL
5099 WINAPI
5100 InsertMenuA(
5101 HMENU hMenu,
5102 UINT uPosition,
5103 UINT uFlags,
5104 UINT_PTR uIDNewItem,
5105 LPCSTR lpNewItem)
5106 {
5107 MENUITEMINFOW mii;
5108 UNICODE_STRING UnicodeString;
5109 BOOL res;
5110
5111 RtlInitUnicodeString(&UnicodeString, 0);
5112
5113 MENU_mnu2mnuii( uFlags, uIDNewItem, (LPCWSTR)lpNewItem, &mii, FALSE);
5114
5115 /* copy the text string, it will be one or the other */
5116 if (lpNewItem && mii.fMask & MIIM_STRING && !mii.hbmpItem && mii.dwTypeData)
5117 {
5118 if (!RtlCreateUnicodeStringFromAsciiz(&UnicodeString, (LPSTR)mii.dwTypeData))
5119 {
5120 SetLastError (ERROR_NOT_ENOUGH_MEMORY);
5121 return FALSE;
5122 }
5123 mii.dwTypeData = UnicodeString.Buffer;
5124 mii.cch = UnicodeString.Length / sizeof(WCHAR);
5125 }
5126 else
5127 {
5128 TRACE("Handle bitmaps\n");
5129 }
5130 res = NtUserThunkedMenuItemInfo(hMenu, uPosition, (BOOL)(uFlags & MF_BYPOSITION), TRUE, &mii, &UnicodeString);
5131 if ( UnicodeString.Buffer ) RtlFreeUnicodeString ( &UnicodeString );
5132 return res;
5133 }
5134
5135 /*
5136 * @implemented
5137 */
5138 BOOL
5139 WINAPI
5140 InsertMenuItemA(
5141 HMENU hMenu,
5142 UINT uItem,
5143 BOOL fByPosition,
5144 LPCMENUITEMINFOA lpmii)
5145 {
5146 MENUITEMINFOW mii;
5147 UNICODE_STRING UnicodeString;
5148 BOOL res;
5149
5150 TRACE("hmenu %p, item %04x, by pos %d, info %p\n", hMenu, uItem, fByPosition, lpmii);
5151
5152 RtlInitUnicodeString(&UnicodeString, 0);
5153
5154 if (!MENU_NormalizeMenuItemInfoStruct( (const MENUITEMINFOW *)lpmii, &mii )) return FALSE;
5155
5156 /* copy the text string */
5157 if (((mii.fMask & MIIM_STRING) ||
5158 ((mii.fMask & MIIM_TYPE) && (MENU_ITEM_TYPE(mii.fType) == MF_STRING)))
5159 && mii.dwTypeData && !(GdiValidateHandle((HGDIOBJ)mii.dwTypeData)) )
5160 {
5161 if (!RtlCreateUnicodeStringFromAsciiz(&UnicodeString, (LPSTR)mii.dwTypeData))
5162 {
5163 SetLastError (ERROR_NOT_ENOUGH_MEMORY);
5164 return FALSE;
5165 }
5166 mii.dwTypeData = UnicodeString.Buffer;
5167 mii.cch = UnicodeString.Length / sizeof(WCHAR);
5168 }
5169 else
5170 {
5171 TRACE("Handle bitmaps\n");
5172 }
5173 res = NtUserThunkedMenuItemInfo(hMenu, uItem, fByPosition, TRUE, &mii, &UnicodeString);
5174 if ( UnicodeString.Buffer ) RtlFreeUnicodeString ( &UnicodeString );
5175 return res;
5176 }
5177
5178 /*
5179 * @implemented
5180 */
5181 BOOL
5182 WINAPI
5183 InsertMenuItemW(
5184 HMENU hMenu,
5185 UINT uItem,
5186 BOOL fByPosition,
5187 LPCMENUITEMINFOW lpmii)
5188 {
5189 MENUITEMINFOW mii;
5190 UNICODE_STRING MenuText;
5191 BOOL res = FALSE;
5192
5193 /* while we could just pass 'lpmii' to win32k, we make a copy so that
5194 if a bad user passes bad data, we crash his process instead of the
5195 entire kernel */
5196
5197 TRACE("hmenu %p, item %04x, by pos %d, info %p\n", hMenu, uItem, fByPosition, lpmii);
5198
5199 RtlInitUnicodeString(&MenuText, 0);
5200
5201 if (!MENU_NormalizeMenuItemInfoStruct( (const MENUITEMINFOW *)lpmii, &mii )) return FALSE;
5202
5203 /* copy the text string */
5204 if (((mii.fMask & MIIM_STRING) ||
5205 ((mii.fMask & MIIM_TYPE) && (MENU_ITEM_TYPE(mii.fType) == MF_STRING)))
5206 && mii.dwTypeData && !(GdiValidateHandle((HGDIOBJ)mii.dwTypeData)) )
5207 {
5208 RtlInitUnicodeString(&MenuText, (PWSTR)lpmii->dwTypeData);
5209 mii.dwTypeData = MenuText.Buffer;
5210 mii.cch = MenuText.Length / sizeof(WCHAR);
5211 }
5212 res = NtUserThunkedMenuItemInfo(hMenu, uItem, fByPosition, TRUE, &mii, &MenuText);
5213 return res;
5214 }
5215
5216 /*
5217 * @implemented
5218 */
5219 BOOL
5220 WINAPI
5221 InsertMenuW(
5222 HMENU hMenu,
5223 UINT uPosition,
5224 UINT uFlags,
5225 UINT_PTR uIDNewItem,
5226 LPCWSTR lpNewItem)
5227 {
5228 MENUITEMINFOW mii;
5229 UNICODE_STRING MenuText;
5230 BOOL res;
5231
5232 RtlInitUnicodeString(&MenuText, 0);
5233
5234 MENU_mnu2mnuii( uFlags, uIDNewItem, lpNewItem, &mii, TRUE);
5235
5236 /* copy the text string, it will be one or the other */
5237 if (lpNewItem && mii.fMask & MIIM_STRING && !mii.hbmpItem && mii.dwTypeData)
5238 {
5239 RtlInitUnicodeString(&MenuText, (PWSTR)mii.dwTypeData);
5240 mii.dwTypeData = MenuText.Buffer;
5241 mii.cch = MenuText.Length / sizeof(WCHAR);
5242 }
5243 res = NtUserThunkedMenuItemInfo(hMenu, uPosition, (BOOL)(uFlags & MF_BYPOSITION), TRUE, &mii, &MenuText);
5244 return res;
5245 }
5246
5247 /*
5248 * @implemented
5249 */
5250 BOOL
5251 WINAPI
5252 IsMenu(
5253 HMENU Menu)
5254 {
5255 if (ValidateHandle(Menu, TYPE_MENU)) return TRUE;
5256 return FALSE;
5257 }
5258
5259 /*
5260 * @implemented
5261 */
5262 HMENU WINAPI
5263 LoadMenuA(HINSTANCE hInstance,
5264 LPCSTR lpMenuName)
5265 {
5266 HANDLE Resource = FindResourceA(hInstance, lpMenuName, MAKEINTRESOURCEA(4));
5267 if (Resource == NULL)
5268 {
5269 return(NULL);
5270 }
5271 return(LoadMenuIndirectA((PVOID)LoadResource(hInstance, Resource)));
5272 }
5273
5274 /*
5275 * @implemented
5276 */
5277 HMENU WINAPI
5278 LoadMenuIndirectA(CONST MENUTEMPLATE *lpMenuTemplate)
5279 {
5280 return(LoadMenuIndirectW(lpMenuTemplate));
5281 }
5282
5283 /*
5284 * @implemented
5285 */
5286 HMENU WINAPI
5287 LoadMenuIndirectW(CONST MENUTEMPLATE *lpMenuTemplate)
5288 {
5289 HMENU hMenu;
5290 WORD version, offset;
5291 LPCSTR p = (LPCSTR)lpMenuTemplate;
5292
5293 version = GET_WORD(p);
5294 p += sizeof(WORD);
5295
5296 switch (version)
5297 {
5298 case 0: /* standard format is version of 0 */
5299 offset = GET_WORD(p);
5300 p += sizeof(WORD) + offset;
5301 if (!(hMenu = CreateMenu())) return 0;
5302 if (!MENU_ParseResource(p, hMenu))
5303 {
5304 DestroyMenu(hMenu);
5305 return 0;
5306 }
5307 return hMenu;
5308 case 1: /* extended format is version of 1 */
5309 offset = GET_WORD(p);
5310 p += sizeof(WORD) + offset;
5311 if (!(hMenu = CreateMenu())) return 0;
5312 if (!MENUEX_ParseResource(p, hMenu))
5313 {
5314 DestroyMenu( hMenu );
5315 return 0;
5316 }
5317 return hMenu;
5318 default:
5319 ERR("Menu template version %d not supported.\n", version);
5320 return 0;
5321 }
5322 }
5323
5324 /*
5325 * @implemented
5326 */
5327 HMENU WINAPI
5328 LoadMenuW(HINSTANCE hInstance,
5329 LPCWSTR lpMenuName)
5330 {
5331 HANDLE Resource = FindResourceW(hInstance, lpMenuName, RT_MENU);
5332 if (Resource == NULL)
5333 {
5334 return(NULL);
5335 }
5336 return(LoadMenuIndirectW((PVOID)LoadResource(hInstance, Resource)));
5337 }
5338
5339 /*
5340 * @implemented
5341 */
5342 int
5343 WINAPI
5344 MenuItemFromPoint(
5345 HWND hWnd,
5346 HMENU hMenu,
5347 POINT ptScreen)
5348 {
5349 return NtUserMenuItemFromPoint(hWnd, hMenu, ptScreen.x, ptScreen.y);
5350 }
5351
5352 /*
5353 * @implemented
5354 */
5355 BOOL
5356 WINAPI
5357 ModifyMenuA(
5358 HMENU hMenu,
5359 UINT uPosition,
5360 UINT uFlags,
5361 UINT_PTR uIDNewItem,
5362 LPCSTR lpNewItem)
5363 {
5364 MENUITEMINFOW mii;
5365 UNICODE_STRING UnicodeString;
5366 BOOL res;
5367
5368 RtlInitUnicodeString(&UnicodeString, 0);
5369
5370 MENU_mnu2mnuii( uFlags, uIDNewItem, (LPCWSTR)lpNewItem, &mii, FALSE);
5371
5372 /* copy the text string, it will be one or the other */
5373 if (lpNewItem && mii.fMask & MIIM_STRING && !mii.hbmpItem && mii.dwTypeData)
5374 {
5375 if (!RtlCreateUnicodeStringFromAsciiz(&UnicodeString, (LPSTR)mii.dwTypeData))
5376 {
5377 SetLastError (ERROR_NOT_ENOUGH_MEMORY);
5378 return FALSE;
5379 }
5380 mii.dwTypeData = UnicodeString.Buffer;
5381 mii.cch = UnicodeString.Length / sizeof(WCHAR);
5382 }
5383 else
5384 {
5385 TRACE("Handle bitmaps\n");
5386 }
5387 res = NtUserThunkedMenuItemInfo(hMenu, uPosition, (BOOL)(uFlags & MF_BYPOSITION), FALSE, &mii, &UnicodeString);
5388 if ( UnicodeString.Buffer ) RtlFreeUnicodeString ( &UnicodeString );
5389 return res;
5390 }
5391
5392 /*
5393 * @implemented
5394 */
5395 BOOL
5396 WINAPI
5397 ModifyMenuW(
5398 HMENU hMenu,
5399 UINT uPosition,
5400 UINT uFlags,
5401 UINT_PTR uIDNewItem,
5402 LPCWSTR lpNewItem)
5403 {
5404 MENUITEMINFOW mii;
5405 UNICODE_STRING MenuText;
5406 BOOL res;
5407
5408 RtlInitUnicodeString(&MenuText, 0);
5409
5410 MENU_mnu2mnuii( uFlags, uIDNewItem, lpNewItem, &mii, TRUE);
5411
5412 /* copy the text string, it will be one or the other */
5413 if (lpNewItem && mii.fMask & MIIM_STRING && !mii.hbmpItem && mii.dwTypeData)
5414 {
5415 RtlInitUnicodeString(&MenuText, (PWSTR)mii.dwTypeData);
5416 mii.dwTypeData = MenuText.Buffer;
5417 mii.cch = MenuText.Length / sizeof(WCHAR);
5418 }
5419 else
5420 {
5421 TRACE("Handle bitmaps\n");
5422 }
5423 res = NtUserThunkedMenuItemInfo(hMenu, uPosition, (BOOL)(uFlags & MF_BYPOSITION), FALSE, &mii, &MenuText);
5424 return res;
5425 }
5426
5427 /*
5428 * @implemented
5429 */
5430 BOOL WINAPI
5431 SetMenu(HWND hWnd,
5432 HMENU hMenu)
5433 {
5434 return NtUserSetMenu(hWnd, hMenu, TRUE);
5435 }
5436
5437 /*
5438 * @implemented
5439 */
5440 BOOL
5441 WINAPI
5442 SetMenuInfo(
5443 HMENU hmenu,
5444 LPCMENUINFO lpcmi)
5445 {
5446 ROSMENUINFO mi;
5447 BOOL res = FALSE;
5448
5449 if (!lpcmi || (lpcmi->cbSize != sizeof(MENUINFO)))
5450 {
5451 SetLastError(ERROR_INVALID_PARAMETER);
5452 return res;
5453 }
5454
5455 memcpy(&mi, lpcmi, sizeof(MENUINFO));
5456 return NtUserThunkedMenuInfo(hmenu, (LPCMENUINFO)&mi);
5457 }
5458
5459 /*
5460 * @implemented
5461 */
5462 BOOL
5463 WINAPI
5464 SetMenuItemBitmaps(
5465 HMENU hMenu,
5466 UINT uPosition,
5467 UINT uFlags,
5468 HBITMAP hBitmapUnchecked,
5469 HBITMAP hBitmapChecked)
5470 {
5471 MENUITEMINFOW uItem;
5472 memset ( &uItem, 0, sizeof(uItem) );
5473 uItem.cbSize = sizeof(MENUITEMINFOW);
5474 uItem.fMask = MIIM_CHECKMARKS;
5475 uItem.hbmpUnchecked = hBitmapUnchecked;
5476 uItem.hbmpChecked = hBitmapChecked;
5477 return SetMenuItemInfoW(hMenu, uPosition, (BOOL)(uFlags & MF_BYPOSITION), &uItem);
5478 }
5479
5480 /*
5481 * @implemented
5482 */
5483 BOOL
5484 WINAPI
5485 SetMenuItemInfoA(
5486 HMENU hmenu,
5487 UINT item,
5488 BOOL bypos,
5489 LPCMENUITEMINFOA lpmii)
5490 {
5491 MENUITEMINFOW mii;
5492 UNICODE_STRING UnicodeString;
5493 BOOL Ret;
5494
5495 TRACE("hmenu %p, item %u, by pos %d, info %p\n", hmenu, item, bypos, lpmii);
5496
5497 RtlInitUnicodeString(&UnicodeString, 0);
5498
5499 if (!MENU_NormalizeMenuItemInfoStruct( (const MENUITEMINFOW *)lpmii, &mii )) return FALSE;
5500 /*
5501 * MIIM_STRING == good
5502 * MIIM_TYPE & MFT_STRING == good
5503 * MIIM_STRING & MFT_STRING == good
5504 * MIIM_STRING & MFT_OWNERDRAW == good
5505 */
5506 if (((mii.fMask & MIIM_STRING) ||
5507 ((mii.fMask & MIIM_TYPE) && (MENU_ITEM_TYPE(mii.fType) == MF_STRING)))
5508 && mii.dwTypeData && !(GdiValidateHandle((HGDIOBJ)mii.dwTypeData)) )
5509 {
5510 /* cch is ignored when the content of a menu item is set by calling SetMenuItemInfo. */
5511 if (!RtlCreateUnicodeStringFromAsciiz(&UnicodeString, (LPSTR)mii.dwTypeData))
5512 {
5513 SetLastError (ERROR_NOT_ENOUGH_MEMORY);
5514 return FALSE;
5515 }
5516 mii.dwTypeData = UnicodeString.Buffer;
5517 mii.cch = UnicodeString.Length / sizeof(WCHAR);
5518 }
5519 else
5520 {
5521 UnicodeString.Buffer = NULL;
5522 }
5523 Ret = NtUserThunkedMenuItemInfo(hmenu, item, bypos, FALSE, &mii, &UnicodeString);
5524 if (UnicodeString.Buffer != NULL) RtlFreeUnicodeString(&UnicodeString);
5525 return Ret;
5526 }
5527
5528 /*
5529 * @implemented
5530 */
5531 BOOL
5532 WINAPI
5533 SetMenuItemInfoW(
5534 HMENU hMenu,
5535 UINT uItem,
5536 BOOL fByPosition,
5537 LPCMENUITEMINFOW lpmii)
5538 {
5539 MENUITEMINFOW MenuItemInfoW;
5540 UNICODE_STRING UnicodeString;
5541 BOOL Ret;
5542
5543 TRACE("hmenu %p, item %u, by pos %d, info %p\n", hMenu, uItem, fByPosition, lpmii);
5544
5545 RtlInitUnicodeString(&UnicodeString, 0);
5546
5547 if (!MENU_NormalizeMenuItemInfoStruct( (const MENUITEMINFOW *)lpmii, &MenuItemInfoW )) return FALSE;
5548
5549 if (((MenuItemInfoW.fMask & MIIM_STRING) ||
5550 ((MenuItemInfoW.fMask & MIIM_TYPE) &&
5551 (MENU_ITEM_TYPE(MenuItemInfoW.fType) == MF_STRING)))
5552 && MenuItemInfoW.dwTypeData && !(GdiValidateHandle((HGDIOBJ)MenuItemInfoW.dwTypeData)) )
5553 {
5554 RtlInitUnicodeString(&UnicodeString, (PCWSTR)MenuItemInfoW.dwTypeData);
5555 MenuItemInfoW.cch = strlenW(MenuItemInfoW.dwTypeData);
5556 }
5557 Ret = NtUserThunkedMenuItemInfo(hMenu, uItem, fByPosition, FALSE, &MenuItemInfoW, &UnicodeString);
5558
5559 return Ret;
5560 }
5561
5562 /*
5563 * @implemented
5564 */
5565 BOOL
5566 WINAPI
5567 SetSystemMenu (
5568 HWND hwnd,
5569 HMENU hMenu)
5570 {
5571 if(!hwnd)
5572 {
5573 SetLastError(ERROR_INVALID_WINDOW_HANDLE);
5574 return FALSE;
5575 }
5576 if(!hMenu)
5577 {
5578 SetLastError(ERROR_INVALID_MENU_HANDLE);
5579 return FALSE;
5580 }
5581 return NtUserSetSystemMenu(hwnd, hMenu);
5582 }
5583
5584 //
5585 // Example for the Win32/User32 rewrite.
5586 // Def = TrackPopupMenuEx@24=NtUserTrackPopupMenuEx@24
5587 //
5588 //
5589 BOOL
5590 WINAPI
5591 NEWTrackPopupMenu(
5592 HMENU Menu,
5593 UINT Flags,
5594 int x,
5595 int y,
5596 int Reserved,
5597 HWND Wnd,
5598 CONST RECT *Rect)
5599 {
5600 return NtUserTrackPopupMenuEx( Menu,
5601 Flags,
5602 x,
5603 y,
5604 Wnd,
5605 NULL); // LPTPMPARAMS is null
5606 }
5607
5608 /*
5609 * @unimplemented
5610 */
5611 BOOL
5612 WINAPI
5613 MenuWindowProcA(
5614 HWND hWnd,
5615 ULONG_PTR Result,
5616 UINT Msg,
5617 WPARAM wParam,
5618 LPARAM lParam
5619 )
5620 {
5621 if ( Msg < WM_USER)
5622 {
5623 LRESULT lResult;
5624 lResult = PopupMenuWndProcA(hWnd, Msg, wParam, lParam );
5625 if (Result)
5626 {
5627 Result = (ULONG_PTR)lResult;
5628 return TRUE;
5629 }
5630 return FALSE;
5631 }
5632 return NtUserMessageCall(hWnd, Msg, wParam, lParam, Result, FNID_MENU, TRUE);
5633 }
5634
5635 /*
5636 * @unimplemented
5637 */
5638 BOOL
5639 WINAPI
5640 MenuWindowProcW(
5641 HWND hWnd,
5642 ULONG_PTR Result,
5643 UINT Msg,
5644 WPARAM wParam,
5645 LPARAM lParam
5646 )
5647 {
5648 if ( Msg < WM_USER)
5649 {
5650 LRESULT lResult;
5651 lResult = PopupMenuWndProcW(hWnd, Msg, wParam, lParam );
5652 if (Result)
5653 {
5654 Result = (ULONG_PTR)lResult;
5655 return TRUE;
5656 }
5657 return FALSE;
5658 }
5659 return NtUserMessageCall(hWnd, Msg, wParam, lParam, Result, FNID_MENU, FALSE);
5660 }
5661
5662 /*
5663 * @implemented
5664 */
5665 BOOL
5666 WINAPI
5667 ChangeMenuW(
5668 HMENU hMenu,
5669 UINT cmd,
5670 LPCWSTR lpszNewItem,
5671 UINT cmdInsert,
5672 UINT flags)
5673 {
5674 /*
5675 FIXME: Word passes the item id in 'cmd' and 0 or 0xffff as cmdInsert
5676 for MF_DELETE. We should check the parameters for all others
5677 MF_* actions also (anybody got a doc on ChangeMenu?).
5678 */
5679
5680 switch(flags & (MF_APPEND | MF_DELETE | MF_CHANGE | MF_REMOVE | MF_INSERT))
5681 {
5682 case MF_APPEND :
5683 return AppendMenuW(hMenu, flags &~ MF_APPEND, cmdInsert, lpszNewItem);
5684
5685 case MF_DELETE :
5686 return DeleteMenu(hMenu, cmd, flags &~ MF_DELETE);
5687
5688 case MF_CHANGE :
5689 return ModifyMenuW(hMenu, cmd, flags &~ MF_CHANGE, cmdInsert, lpszNewItem);
5690
5691 case MF_REMOVE :
5692 return RemoveMenu(hMenu, flags & MF_BYPOSITION ? cmd : cmdInsert,
5693 flags &~ MF_REMOVE);
5694
5695 default : /* MF_INSERT */
5696 return InsertMenuW(hMenu, cmd, flags, cmdInsert, lpszNewItem);
5697 };
5698 }
5699
5700 /*
5701 * @implemented
5702 */
5703 BOOL
5704 WINAPI
5705 ChangeMenuA(
5706 HMENU hMenu,
5707 UINT cmd,
5708 LPCSTR lpszNewItem,
5709 UINT cmdInsert,
5710 UINT flags)
5711 {
5712 /*
5713 FIXME: Word passes the item id in 'cmd' and 0 or 0xffff as cmdInsert
5714 for MF_DELETE. We should check the parameters for all others
5715 MF_* actions also (anybody got a doc on ChangeMenu?).
5716 */
5717
5718 switch(flags & (MF_APPEND | MF_DELETE | MF_CHANGE | MF_REMOVE | MF_INSERT))
5719 {
5720 case MF_APPEND :
5721 return AppendMenuA(hMenu, flags &~ MF_APPEND, cmdInsert, lpszNewItem);
5722
5723 case MF_DELETE :
5724 return DeleteMenu(hMenu, cmd, flags &~ MF_DELETE);
5725
5726 case MF_CHANGE :
5727 return ModifyMenuA(hMenu, cmd, flags &~ MF_CHANGE, cmdInsert, lpszNewItem);
5728
5729 case MF_REMOVE :
5730 return RemoveMenu(hMenu, flags & MF_BYPOSITION ? cmd : cmdInsert,
5731 flags &~ MF_REMOVE);
5732
5733 default : /* MF_INSERT */
5734 return InsertMenuA(hMenu, cmd, flags, cmdInsert, lpszNewItem);
5735 };
5736 }
5737