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