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