source: trunk/src/user32/menu.c@ 21961

Last change on this file since 21961 was 21961, checked in by dmik, 14 years ago

Support MIIM_FTYPE/MIIM_STRING flags properly in InsertMenuItem/SetMenuItemInfo.

The old Wine code doesn't seem to be aware of them. This in particular fixes the
broken context menu of newer Flash (see Flash ticket #42).

File size: 158.8 KB
Line 
1/*
2 * Menu functions
3 *
4 * Copyright 1993 Martin Ayotte
5 * Copyright 1994 Alexandre Julliard
6 * Copyright 1997 Morten Welinder
7 */
8
9/*
10 * Note: the style MF_MOUSESELECT is used to mark popup items that
11 * have been selected, i.e. their popup menu is currently displayed.
12 * This is probably not the meaning this style has in MS-Windows.
13 */
14
15#include "config.h"
16#include "wine/port.h"
17
18#include <assert.h>
19#include <ctype.h>
20#include <stdlib.h>
21#include <string.h>
22
23#include "windef.h"
24#include "winnls.h"
25#include "wingdi.h"
26#include "wine/winbase16.h"
27#include "wine/winuser16.h"
28#include "wine/unicode.h"
29#include "win.h"
30#include "controls.h"
31#include "nonclient.h"
32#include "user.h"
33#ifndef __WIN32OS2__
34#include "message.h"
35#endif
36
37#include "debugtools.h"
38
39#ifdef __WIN32OS2__
40#include <objhandle.h>
41#include <winerror.h>
42#include "pmwindow.h"
43#include "ctrlconf.h"
44#include "oslibmsg.h"
45#include "oslibwin.h"
46#include "heapstring.h"
47
48
49HBRUSH WIN32API GetOS2ColorBrush(int nIndex);
50COLORREF WIN32API GetOS2Color(int nIndex);
51
52static BOOL fDisableOdinSysMenuItems = FALSE;
53
54#endif
55
56DEFAULT_DEBUG_CHANNEL(menu);
57DECLARE_DEBUG_CHANNEL(accel);
58
59#define HACCEL_16(h32) (LOWORD(h32))
60
61/* internal popup menu window messages */
62
63#define MM_SETMENUHANDLE (WM_USER + 0)
64#define MM_GETMENUHANDLE (WM_USER + 1)
65
66/* Menu item structure */
67typedef struct {
68 /* ----------- MENUITEMINFO Stuff ----------- */
69 UINT fType; /* Item type. */
70 UINT fState; /* Item state. */
71 UINT wID; /* Item id. */
72 HMENU hSubMenu; /* Pop-up menu. */
73 HBITMAP hCheckBit; /* Bitmap when checked. */
74 HBITMAP hUnCheckBit; /* Bitmap when unchecked. */
75 LPWSTR text; /* Item text or bitmap handle. */
76 DWORD dwItemData; /* Application defined. */
77 DWORD dwTypeData; /* depends on fMask */
78 HBITMAP hbmpItem; /* bitmap in win98 style menus */
79 /* ----------- Wine stuff ----------- */
80 RECT rect; /* Item area (relative to menu window) */
81 UINT xTab; /* X position of text after Tab */
82} MENUITEM;
83
84/* Popup menu structure */
85typedef struct {
86 WORD wFlags; /* Menu flags (MF_POPUP, MF_SYSMENU) */
87 WORD wMagic; /* Magic number */
88 WORD Width; /* Width of the whole menu */
89 WORD Height; /* Height of the whole menu */
90 UINT nItems; /* Number of items in the menu */
91 HWND hWnd; /* Window containing the menu */
92 MENUITEM *items; /* Array of menu items */
93 UINT FocusedItem; /* Currently focused item */
94 HWND hwndOwner; /* window receiving the messages for ownerdraw */
95 BOOL bTimeToHide; /* Request hiding when receiving a second click in the top-level menu item */
96 /* ------------ MENUINFO members ------ */
97 DWORD dwStyle; /* Extended mennu style */
98 UINT cyMax; /* max hight of the whole menu, 0 is screen hight */
99 HBRUSH hbrBack; /* brush for menu background */
100 DWORD dwContextHelpID;
101 DWORD dwMenuData; /* application defined value */
102 HMENU hSysMenuOwner; /* Handle to the dummy sys menu holder */
103} POPUPMENU, *LPPOPUPMENU;
104
105/* internal flags for menu tracking */
106
107#define TF_ENDMENU 0x0001
108#define TF_SUSPENDPOPUP 0x0002
109#define TF_SKIPREMOVE 0x0004
110
111typedef struct
112{
113 UINT trackFlags;
114 HMENU hCurrentMenu; /* current submenu (can be equal to hTopMenu)*/
115 HMENU hTopMenu; /* initial menu */
116 HWND hOwnerWnd; /* where notifications are sent */
117 POINT pt;
118} MTRACKER;
119
120#define MENU_MAGIC 0x554d /* 'MU' */
121
122#define ITEM_PREV -1
123#define ITEM_NEXT 1
124
125 /* Internal MENU_TrackMenu() flags */
126#define TPM_INTERNAL 0xF0000000
127#define TPM_ENTERIDLEEX 0x80000000 /* set owner window for WM_ENTERIDLE */
128#define TPM_BUTTONDOWN 0x40000000 /* menu was clicked before tracking */
129#define TPM_POPUPMENU 0x20000000 /* menu is a popup menu */
130
131 /* popup menu shade thickness */
132#define POPUP_XSHADE 4
133#define POPUP_YSHADE 4
134
135 /* Space between 2 menu bar items */
136#define MENU_BAR_ITEMS_SPACE 12
137
138 /* Minimum width of a tab character */
139#define MENU_TAB_SPACE 8
140
141 /* Height of a separator item */
142#define SEPARATOR_HEIGHT 5
143
144 /* (other menu->FocusedItem values give the position of the focused item) */
145#define NO_SELECTED_ITEM 0xffff
146
147#define MENU_ITEM_TYPE(flags) \
148 ((flags) & (MF_STRING | MF_BITMAP | MF_OWNERDRAW | MF_SEPARATOR))
149
150#define IS_STRING_ITEM(flags) (MENU_ITEM_TYPE ((flags)) == MF_STRING)
151#define IS_BITMAP_ITEM(flags) (MENU_ITEM_TYPE ((flags)) == MF_BITMAP)
152#define IS_MAGIC_ITEM(text) (LOWORD((int)text)<12)
153
154#define IS_SYSTEM_MENU(menu) \
155 (!((menu)->wFlags & MF_POPUP) && (menu)->wFlags & MF_SYSMENU)
156
157#define IS_SYSTEM_POPUP(menu) \
158 ((menu)->wFlags & MF_POPUP && (menu)->wFlags & MF_SYSMENU)
159
160#define TYPE_MASK (MFT_STRING | MFT_BITMAP | MFT_OWNERDRAW | MFT_SEPARATOR | \
161 MFT_MENUBARBREAK | MFT_MENUBREAK | MFT_RADIOCHECK | \
162 MFT_RIGHTORDER | MFT_RIGHTJUSTIFY | \
163 MF_POPUP | MF_SYSMENU | MF_HELP)
164#define STATE_MASK (~TYPE_MASK)
165
166 /* Dimension of the menu bitmaps */
167static WORD arrow_bitmap_width = 0, arrow_bitmap_height = 0;
168
169static HBITMAP hStdMnArrow = 0;
170static HBITMAP hBmpSysMenu = 0;
171
172static HBRUSH hShadeBrush = 0;
173static HFONT hMenuFont = 0;
174static HFONT hMenuFontBold = 0;
175
176static HMENU MENU_DefSysPopup = 0; /* Default system menu popup */
177
178/* Use global popup window because there's no way 2 menus can
179 * be tracked at the same time. */
180static HWND top_popup;
181
182 /* Flag set by EndMenu() to force an exit from menu tracking */
183static BOOL fEndMenu = FALSE;
184
185static LRESULT WINAPI PopupMenuWndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam );
186
187
188/*********************************************************************
189 * menu class descriptor
190 */
191const struct builtin_class_descr MENU_builtin_class =
192{
193 POPUPMENU_CLASS_ATOM, /* name */
194 CS_GLOBALCLASS | CS_SAVEBITS, /* style */
195 NULL, /* procA (winproc is Unicode only) */
196 PopupMenuWndProc, /* procW */
197 sizeof(HMENU), /* extra */
198 IDC_ARROWA, /* cursor */
199 COLOR_MENU+1 /* brush */
200};
201
202
203/***********************************************************************
204 * debug_print_menuitem
205 *
206 * Print a menuitem in readable form.
207 */
208
209#define debug_print_menuitem(pre, mp, post) \
210 if(!TRACE_ON(menu)) ; else do_debug_print_menuitem(pre, mp, post)
211
212#define MENUOUT(text) \
213 DPRINTF("%s%s", (count++ ? "," : ""), (text))
214
215#define MENUFLAG(bit,text) \
216 do { \
217 if (flags & (bit)) { flags &= ~(bit); MENUOUT ((text)); } \
218 } while (0)
219
220static void do_debug_print_menuitem(const char *prefix, MENUITEM * mp,
221 const char *postfix)
222{
223 TRACE("%s ", prefix);
224 if (mp) {
225 UINT flags = mp->fType;
226 int typ = MENU_ITEM_TYPE(flags);
227 DPRINTF( "{ ID=0x%x", mp->wID);
228 if (flags & MF_POPUP)
229 DPRINTF( ", Sub=0x%x", mp->hSubMenu);
230 if (flags) {
231 int count = 0;
232 DPRINTF( ", Typ=");
233 if (typ == MFT_STRING)
234 /* Nothing */ ;
235 else if (typ == MFT_SEPARATOR)
236 MENUOUT("sep");
237 else if (typ == MFT_OWNERDRAW)
238 MENUOUT("own");
239 else if (typ == MFT_BITMAP)
240 MENUOUT("bit");
241 else
242 MENUOUT("???");
243 flags -= typ;
244
245 MENUFLAG(MF_POPUP, "pop");
246 MENUFLAG(MFT_MENUBARBREAK, "barbrk");
247 MENUFLAG(MFT_MENUBREAK, "brk");
248 MENUFLAG(MFT_RADIOCHECK, "radio");
249 MENUFLAG(MFT_RIGHTORDER, "rorder");
250 MENUFLAG(MF_SYSMENU, "sys");
251 MENUFLAG(MFT_RIGHTJUSTIFY, "right"); /* same as MF_HELP */
252
253 if (flags)
254 DPRINTF( "+0x%x", flags);
255 }
256 flags = mp->fState;
257 if (flags) {
258 int count = 0;
259 DPRINTF( ", State=");
260 MENUFLAG(MFS_GRAYED, "grey");
261 MENUFLAG(MFS_DEFAULT, "default");
262 MENUFLAG(MFS_DISABLED, "dis");
263 MENUFLAG(MFS_CHECKED, "check");
264 MENUFLAG(MFS_HILITE, "hi");
265 MENUFLAG(MF_USECHECKBITMAPS, "usebit");
266 MENUFLAG(MF_MOUSESELECT, "mouse");
267 if (flags)
268 DPRINTF( "+0x%x", flags);
269 }
270 if (mp->hCheckBit)
271 DPRINTF( ", Chk=0x%x", mp->hCheckBit);
272 if (mp->hUnCheckBit)
273 DPRINTF( ", Unc=0x%x", mp->hUnCheckBit);
274
275 if (typ == MFT_STRING) {
276 if (mp->text)
277 DPRINTF( ", Text=%s", debugstr_w(mp->text));
278 else
279 DPRINTF( ", Text=Null");
280 } else if (mp->text == NULL)
281 /* Nothing */ ;
282 else
283 DPRINTF( ", Text=%p", mp->text);
284 if (mp->dwItemData)
285 DPRINTF( ", ItemData=0x%08lx", mp->dwItemData);
286 DPRINTF( " }");
287 } else {
288 DPRINTF( "NULL");
289 }
290
291 DPRINTF(" %s\n", postfix);
292}
293
294#undef MENUOUT
295#undef MENUFLAG
296
297
298/***********************************************************************
299 * MENU_GetMenu
300 *
301 * Validate the given menu handle and returns the menu structure pointer.
302 */
303static POPUPMENU *MENU_GetMenu(HMENU hMenu)
304{
305#ifdef __WIN32OS2__
306 if(ObjQueryHandleType(hMenu) == HNDL_MENU) {
307 POPUPMENU *menu;
308
309 menu = (POPUPMENU *)ObjQueryHandleData(hMenu, HNDL_MENU);
310 return menu;
311 }
312 return NULL;
313#else
314 POPUPMENU *menu = USER_HEAP_LIN_ADDR(hMenu);
315 if (!menu || menu->wMagic != MENU_MAGIC)
316 {
317 WARN("invalid menu handle=%x, ptr=%p, magic=%x\n", hMenu, menu, menu? menu->wMagic:0);
318 menu = NULL;
319 }
320 return menu;
321#endif
322}
323
324#ifndef __WIN32OS2__
325/***********************************************************************
326 * get_win_sys_menu
327 *
328 * Get the system menu of a window
329 */
330static HMENU get_win_sys_menu( HWND hwnd )
331{
332 HMENU ret = 0;
333 WND *win = WIN_FindWndPtr( hwnd );
334 if (win)
335 {
336 ret = win->hSysMenu;
337 WIN_ReleaseWndPtr( win );
338 }
339 return ret;
340}
341#endif
342/***********************************************************************
343 * MENU_CopySysPopup
344 *
345 * Return the default system menu.
346 */
347static HMENU MENU_CopySysPopup(void)
348{
349#ifndef __WIN32OS2__
350 HMENU hMenu = LoadMenuA(GetModuleHandleA("USER32"), "SYSMENU");
351#else
352 HMENU hMenu;
353 hMenu = LoadMenuA(GetModuleHandleA("USER32"), fOS2Look ? "SYSMENUWARP" : "SYSMENU");
354#endif
355
356 if( hMenu ) {
357 POPUPMENU* menu = MENU_GetMenu(hMenu);
358 menu->wFlags |= MF_SYSMENU | MF_POPUP;
359 SetMenuDefaultItem(hMenu, SC_CLOSE, FALSE);
360#ifdef __WIN32OS2__
361 if(!fDisableOdinSysMenuItems) {
362 AppendMenuA(hMenu, MF_SEPARATOR, 0, NULL);
363 AppendMenuA(hMenu,MF_STRING,
364 SC_ABOUTODIN, (LPSTR)"About Odin");
365#ifdef DEBUG
366 AppendMenuA(hMenu, MF_SEPARATOR, 0, NULL);
367 AppendMenuA(hMenu,MF_STRING,
368 SC_PUTMARK, (LPSTR)"Put mark in debug log");
369 AppendMenuA(hMenu,MF_STRING,
370 SC_DEBUGINT3, (LPSTR)"Breakpoint");
371#endif
372 }
373#endif
374 }
375 else
376 ERR("Unable to load default system menu\n" );
377
378 TRACE("returning %x.\n", hMenu );
379
380 return hMenu;
381}
382
383
384/**********************************************************************
385 * MENU_GetSysMenu
386 *
387 * Create a copy of the system menu. System menu in Windows is
388 * a special menu bar with the single entry - system menu popup.
389 * This popup is presented to the outside world as a "system menu".
390 * However, the real system menu handle is sometimes seen in the
391 * WM_MENUSELECT parameters (and Word 6 likes it this way).
392 */
393HMENU MENU_GetSysMenu( HWND hWnd, HMENU hPopupMenu )
394{
395 HMENU hMenu;
396
397 if ((hMenu = CreateMenu()) != 0)
398 {
399 POPUPMENU *menu = MENU_GetMenu(hMenu);
400 menu->wFlags = MF_SYSMENU;
401 menu->hWnd = WIN_GetFullHandle( hWnd );
402
403 if (hPopupMenu == (HMENU)(-1))
404 hPopupMenu = MENU_CopySysPopup();
405 else if( !hPopupMenu ) hPopupMenu = MENU_DefSysPopup;
406
407 if (hPopupMenu)
408 {
409 InsertMenuA( hMenu, -1, MF_SYSMENU | MF_POPUP | MF_BYPOSITION, hPopupMenu, NULL );
410
411 menu->items[0].fType = MF_SYSMENU | MF_POPUP;
412 menu->items[0].fState = 0;
413 if ((menu = MENU_GetMenu(hPopupMenu)) != 0) menu->wFlags |= MF_SYSMENU;
414
415 TRACE("GetSysMenu hMenu=%04x (%04x)\n", hMenu, hPopupMenu );
416 return hMenu;
417 }
418 DestroyMenu( hMenu );
419 }
420 ERR("failed to load system menu!\n");
421 return 0;
422}
423
424
425/***********************************************************************
426 * MENU_Init
427 *
428 * Menus initialisation.
429 */
430BOOL MENU_Init()
431{
432 HBITMAP hBitmap;
433 NONCLIENTMETRICSA ncm;
434
435 static unsigned char shade_bits[16] = { 0x55, 0, 0xAA, 0,
436 0x55, 0, 0xAA, 0,
437 0x55, 0, 0xAA, 0,
438 0x55, 0, 0xAA, 0 };
439
440 /* Load menu bitmaps */
441 hStdMnArrow = LoadBitmapA(0, MAKEINTRESOURCEA(OBM_MNARROW));
442 /* Load system buttons bitmaps */
443 hBmpSysMenu = LoadBitmapA(0, MAKEINTRESOURCEA(OBM_CLOSE));
444
445 if (hStdMnArrow)
446 {
447 BITMAP bm;
448 GetObjectA( hStdMnArrow, sizeof(bm), &bm );
449 arrow_bitmap_width = bm.bmWidth;
450 arrow_bitmap_height = bm.bmHeight;
451 } else
452 return FALSE;
453
454 if (! (hBitmap = CreateBitmap( 8, 8, 1, 1, shade_bits)))
455 return FALSE;
456
457 if(!(hShadeBrush = CreatePatternBrush( hBitmap )))
458 return FALSE;
459
460 DeleteObject( hBitmap );
461 if (!(MENU_DefSysPopup = MENU_CopySysPopup()))
462 return FALSE;
463
464 ncm.cbSize = sizeof (NONCLIENTMETRICSA);
465 if (!(SystemParametersInfoA(SPI_GETNONCLIENTMETRICS, sizeof(NONCLIENTMETRICSA), &ncm, 0)))
466 return FALSE;
467
468 if (!(hMenuFont = CreateFontIndirectA( &ncm.lfMenuFont )))
469 return FALSE;
470
471 ncm.lfMenuFont.lfWeight += 300;
472 if ( ncm.lfMenuFont.lfWeight > 1000)
473 ncm.lfMenuFont.lfWeight = 1000;
474
475 if (!(hMenuFontBold = CreateFontIndirectA( &ncm.lfMenuFont )))
476 return FALSE;
477
478 return TRUE;
479}
480
481/***********************************************************************
482 * MENU_InitSysMenuPopup
483 *
484 * Grey the appropriate items in System menu.
485 */
486static void MENU_InitSysMenuPopup( HMENU hmenu, DWORD style, DWORD clsStyle )
487{
488 BOOL gray;
489
490 gray = !(style & WS_THICKFRAME) || (style & (WS_MAXIMIZE | WS_MINIMIZE));
491 EnableMenuItem( hmenu, SC_SIZE, (gray ? MF_GRAYED : MF_ENABLED) );
492 gray = ((style & WS_MAXIMIZE) != 0);
493 EnableMenuItem( hmenu, SC_MOVE, (gray ? MF_GRAYED : MF_ENABLED) );
494 gray = !(style & WS_MINIMIZEBOX) || (style & WS_MINIMIZE);
495 EnableMenuItem( hmenu, SC_MINIMIZE, (gray ? MF_GRAYED : MF_ENABLED) );
496 gray = !(style & WS_MAXIMIZEBOX) || (style & WS_MAXIMIZE);
497 EnableMenuItem( hmenu, SC_MAXIMIZE, (gray ? MF_GRAYED : MF_ENABLED) );
498 gray = !(style & (WS_MAXIMIZE | WS_MINIMIZE));
499 EnableMenuItem( hmenu, SC_RESTORE, (gray ? MF_GRAYED : MF_ENABLED) );
500 gray = (clsStyle & CS_NOCLOSE) != 0;
501
502 /* The menu item must keep its state if it's disabled */
503 if(gray)
504 EnableMenuItem( hmenu, SC_CLOSE, MF_GRAYED);
505}
506
507
508/******************************************************************************
509 *
510 * UINT MENU_GetStartOfNextColumn(
511 * HMENU hMenu )
512 *
513 *****************************************************************************/
514
515static UINT MENU_GetStartOfNextColumn(
516 HMENU hMenu )
517{
518 POPUPMENU *menu = MENU_GetMenu(hMenu);
519 UINT i;
520
521 if(!menu)
522 return NO_SELECTED_ITEM;
523
524 i = menu->FocusedItem + 1;
525 if( i == NO_SELECTED_ITEM )
526 return i;
527
528 for( ; i < menu->nItems; ++i ) {
529 if (menu->items[i].fType & MF_MENUBARBREAK)
530 return i;
531 }
532
533 return NO_SELECTED_ITEM;
534}
535
536
537/******************************************************************************
538 *
539 * UINT MENU_GetStartOfPrevColumn(
540 * HMENU hMenu )
541 *
542 *****************************************************************************/
543
544static UINT MENU_GetStartOfPrevColumn(
545 HMENU hMenu )
546{
547 POPUPMENU *menu = MENU_GetMenu(hMenu);
548 UINT i;
549
550 if( !menu )
551 return NO_SELECTED_ITEM;
552
553 if( menu->FocusedItem == 0 || menu->FocusedItem == NO_SELECTED_ITEM )
554 return NO_SELECTED_ITEM;
555
556 /* Find the start of the column */
557
558 for(i = menu->FocusedItem; i != 0 &&
559 !(menu->items[i].fType & MF_MENUBARBREAK);
560 --i); /* empty */
561
562 if(i == 0)
563 return NO_SELECTED_ITEM;
564
565 for(--i; i != 0; --i) {
566 if (menu->items[i].fType & MF_MENUBARBREAK)
567 break;
568 }
569
570 TRACE("ret %d.\n", i );
571
572 return i;
573}
574
575
576
577/***********************************************************************
578 * MENU_FindItem
579 *
580 * Find a menu item. Return a pointer on the item, and modifies *hmenu
581 * in case the item was in a sub-menu.
582 */
583static MENUITEM *MENU_FindItem( HMENU *hmenu, UINT *nPos, UINT wFlags )
584{
585 POPUPMENU *menu;
586 UINT i;
587
588 if (((*hmenu)==0xffff) || (!(menu = MENU_GetMenu(*hmenu)))) return NULL;
589 if (wFlags & MF_BYPOSITION)
590 {
591 if (*nPos >= menu->nItems) return NULL;
592 return &menu->items[*nPos];
593 }
594 else
595 {
596 MENUITEM *item = menu->items;
597 for (i = 0; i < menu->nItems; i++, item++)
598 {
599 if (item->wID == *nPos)
600 {
601 *nPos = i;
602 return item;
603 }
604 else if (item->fType & MF_POPUP)
605 {
606 HMENU hsubmenu = item->hSubMenu;
607 MENUITEM *subitem = MENU_FindItem( &hsubmenu, nPos, wFlags );
608#ifdef __WIN32OS2__
609 //YD: 2000-12-01: extra check added for loops in menus
610 if (subitem && subitem!=item)
611#else
612 if (subitem)
613#endif
614 {
615 *hmenu = hsubmenu;
616 return subitem;
617 }
618 }
619 }
620 }
621 return NULL;
622}
623
624/***********************************************************************
625 * MENU_FindSubMenu
626 *
627 * Find a Sub menu. Return the position of the submenu, and modifies
628 * *hmenu in case it is found in another sub-menu.
629 * If the submenu cannot be found, NO_SELECTED_ITEM is returned.
630 */
631UINT MENU_FindSubMenu( HMENU *hmenu, HMENU hSubTarget )
632{
633 POPUPMENU *menu;
634 UINT i;
635 MENUITEM *item;
636 if (((*hmenu)==0xffff) ||
637 (!(menu = MENU_GetMenu(*hmenu))))
638 return NO_SELECTED_ITEM;
639 item = menu->items;
640 for (i = 0; i < menu->nItems; i++, item++) {
641 if(!(item->fType & MF_POPUP)) continue;
642 if (item->hSubMenu == hSubTarget) {
643 return i;
644 }
645 else {
646 HMENU hsubmenu = item->hSubMenu;
647 UINT pos = MENU_FindSubMenu( &hsubmenu, hSubTarget );
648 if (pos != NO_SELECTED_ITEM) {
649 *hmenu = hsubmenu;
650 return pos;
651 }
652 }
653 }
654 return NO_SELECTED_ITEM;
655}
656
657/***********************************************************************
658 * MENU_FreeItemData
659 */
660static void MENU_FreeItemData( MENUITEM* item )
661{
662 /* delete text */
663 if (IS_STRING_ITEM(item->fType) && item->text)
664 HeapFree( GetProcessHeap(), 0, item->text );
665}
666
667/***********************************************************************
668 * MENU_FindItemByCoords
669 *
670 * Find the item at the specified coordinates (screen coords). Does
671 * not work for child windows and therefore should not be called for
672 * an arbitrary system menu.
673 */
674static MENUITEM *MENU_FindItemByCoords( POPUPMENU *menu,
675 POINT pt, UINT *pos )
676{
677 MENUITEM *item;
678 UINT i;
679 RECT wrect;
680
681 if (!GetWindowRect(menu->hWnd,&wrect)) return NULL;
682 pt.x -= wrect.left;pt.y -= wrect.top;
683 item = menu->items;
684 for (i = 0; i < menu->nItems; i++, item++)
685 {
686 if ((pt.x >= item->rect.left) && (pt.x < item->rect.right) &&
687 (pt.y >= item->rect.top) && (pt.y < item->rect.bottom))
688 {
689 if (pos) *pos = i;
690 return item;
691 }
692 }
693 return NULL;
694}
695
696
697/***********************************************************************
698 * MENU_FindItemByKey
699 *
700 * Find the menu item selected by a key press.
701 * Return item id, -1 if none, -2 if we should close the menu.
702 */
703static UINT MENU_FindItemByKey( HWND hwndOwner, HMENU hmenu,
704 UINT key, BOOL forceMenuChar )
705{
706 TRACE("\tlooking for '%c' in [%04x]\n", (char)key, (UINT16)hmenu );
707
708 if (!IsMenu( hmenu )) hmenu = GetSubMenu( get_win_sys_menu(hwndOwner), 0);
709
710 if (hmenu)
711 {
712 POPUPMENU *menu = MENU_GetMenu( hmenu );
713 MENUITEM *item = menu->items;
714 LONG menuchar;
715
716 if( !forceMenuChar )
717 {
718 UINT i;
719
720 key = toupper(key);
721 for (i = 0; i < menu->nItems; i++, item++)
722 {
723 if (item->text && (IS_STRING_ITEM(item->fType)))
724 {
725 WCHAR *p = item->text - 2;
726 do
727 {
728 p = strchrW (p + 2, '&');
729 }
730 while (p != NULL && p [1] == '&');
731 if (p && (toupper(p[1]) == key)) return i;
732 }
733 }
734 }
735 menuchar = SendMessageA( hwndOwner, WM_MENUCHAR,
736 MAKEWPARAM( key, menu->wFlags ), hmenu );
737 if (HIWORD(menuchar) == 2) return LOWORD(menuchar);
738 if (HIWORD(menuchar) == 1) return (UINT)(-2);
739 }
740 return (UINT)(-1);
741}
742
743
744/***********************************************************************
745 * MENU_GetBitmapItemSize
746 *
747 * Get the size of a bitmap item.
748 */
749static void MENU_GetBitmapItemSize( UINT id, DWORD data, SIZE *size )
750{
751 BITMAP bm;
752 HBITMAP bmp = (HBITMAP)id;
753
754 size->cx = size->cy = 0;
755
756 /* check if there is a magic menu item associated with this item */
757 if (id && IS_MAGIC_ITEM( id ))
758 {
759 switch((HBITMAP)LOWORD(id))
760 {
761 case HBMMENU_SYSTEM:
762 if (data)
763 {
764 bmp = (HBITMAP)data;
765 break;
766 }
767 /* fall through */
768 case HBMMENU_MBAR_RESTORE:
769 case HBMMENU_MBAR_MINIMIZE:
770 case HBMMENU_MBAR_MINIMIZE_D:
771 case HBMMENU_MBAR_CLOSE:
772 case HBMMENU_MBAR_CLOSE_D:
773 size->cx = GetSystemMetrics( SM_CXSIZE );
774 size->cy = GetSystemMetrics( SM_CYSIZE );
775 return;
776 case HBMMENU_CALLBACK:
777 case HBMMENU_POPUP_CLOSE:
778 case HBMMENU_POPUP_RESTORE:
779 case HBMMENU_POPUP_MAXIMIZE:
780 case HBMMENU_POPUP_MINIMIZE:
781 default:
782 FIXME("Magic 0x%08x not implemented\n", id);
783 return;
784 }
785 }
786 if (GetObjectA(bmp, sizeof(bm), &bm ))
787 {
788 size->cx = bm.bmWidth;
789 size->cy = bm.bmHeight;
790 }
791}
792
793/***********************************************************************
794 * MENU_DrawBitmapItem
795 *
796 * Draw a bitmap item.
797 */
798static void MENU_DrawBitmapItem( HDC hdc, MENUITEM *lpitem, const RECT *rect, BOOL menuBar )
799{
800 BITMAP bm;
801 DWORD rop;
802 HDC hdcMem;
803 HBITMAP bmp = (HBITMAP)lpitem->text;
804 int w = rect->right - rect->left;
805 int h = rect->bottom - rect->top;
806 int bmp_xoffset = 0;
807 int left, top;
808
809 /* Check if there is a magic menu item associated with this item */
810 if (lpitem->text && IS_MAGIC_ITEM(lpitem->text))
811 {
812 UINT flags = 0;
813 RECT r;
814
815 switch((HBITMAP)LOWORD(lpitem->text))
816 {
817 case HBMMENU_SYSTEM:
818 if (lpitem->dwItemData)
819 {
820 bmp = (HBITMAP)lpitem->dwItemData;
821 if (!GetObjectA( bmp, sizeof(bm), &bm )) return;
822 }
823 else
824 {
825 bmp = hBmpSysMenu;
826 if (!GetObjectA( bmp, sizeof(bm), &bm )) return;
827 /* only use right half of the bitmap */
828 bmp_xoffset = bm.bmWidth / 2;
829 bm.bmWidth -= bmp_xoffset;
830 }
831 goto got_bitmap;
832 case HBMMENU_MBAR_RESTORE:
833 flags = DFCS_CAPTIONRESTORE;
834 break;
835 case HBMMENU_MBAR_MINIMIZE:
836 flags = DFCS_CAPTIONMIN;
837 break;
838 case HBMMENU_MBAR_MINIMIZE_D:
839 flags = DFCS_CAPTIONMIN | DFCS_INACTIVE;
840 break;
841 case HBMMENU_MBAR_CLOSE:
842 flags = DFCS_CAPTIONCLOSE;
843 break;
844 case HBMMENU_MBAR_CLOSE_D:
845 flags = DFCS_CAPTIONCLOSE | DFCS_INACTIVE;
846 break;
847 case HBMMENU_CALLBACK:
848 case HBMMENU_POPUP_CLOSE:
849 case HBMMENU_POPUP_RESTORE:
850 case HBMMENU_POPUP_MAXIMIZE:
851 case HBMMENU_POPUP_MINIMIZE:
852 default:
853 FIXME("Magic 0x%08x not implemented\n", LOWORD(lpitem->text));
854 return;
855 }
856 r = *rect;
857 InflateRect( &r, -1, -1 );
858 if (lpitem->fState & MF_HILITE) flags |= DFCS_PUSHED;
859 DrawFrameControl( hdc, &r, DFC_CAPTION, flags );
860 return;
861 }
862
863 if (!bmp || !GetObjectA( bmp, sizeof(bm), &bm )) return;
864
865 got_bitmap:
866 hdcMem = CreateCompatibleDC( hdc );
867 SelectObject( hdcMem, bmp );
868
869 /* handle fontsize > bitmap_height */
870 top = (h>bm.bmHeight) ? rect->top+(h-bm.bmHeight)/2 : rect->top;
871 left=rect->left;
872 if (TWEAK_WineLook == WIN95_LOOK) {
873 rop=((lpitem->fState & MF_HILITE) && !IS_MAGIC_ITEM(lpitem->text)) ? NOTSRCCOPY : SRCCOPY;
874 if ((lpitem->fState & MF_HILITE) && IS_BITMAP_ITEM(lpitem->fType))
875 SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT));
876 } else {
877 left++;
878 w-=2;
879 rop=((lpitem->fState & MF_HILITE) && !IS_MAGIC_ITEM(lpitem->text) && (!menuBar)) ? MERGEPAINT : SRCCOPY;
880 }
881 BitBlt( hdc, left, top, w, h, hdcMem, bmp_xoffset, 0, rop );
882 DeleteDC( hdcMem );
883}
884
885
886/***********************************************************************
887 * MENU_CalcItemSize
888 *
889 * Calculate the size of the menu item and store it in lpitem->rect.
890 */
891static void MENU_CalcItemSize( HDC hdc, MENUITEM *lpitem, HWND hwndOwner,
892 INT orgX, INT orgY, BOOL menuBar )
893{
894 WCHAR *p;
895 UINT check_bitmap_width = GetSystemMetrics( SM_CXMENUCHECK );
896
897 TRACE("dc=0x%04x owner=0x%04x (%d,%d)\n", hdc, hwndOwner, orgX, orgY);
898 debug_print_menuitem("MENU_CalcItemSize: menuitem:", lpitem,
899 (menuBar ? " (MenuBar)" : ""));
900
901 SetRect( &lpitem->rect, orgX, orgY, orgX, orgY );
902
903 if (lpitem->fType & MF_OWNERDRAW)
904 {
905 /*
906 ** Experimentation under Windows reveals that an owner-drawn
907 ** menu is expected to return the size of the content part of
908 ** the menu item, not including the checkmark nor the submenu
909 ** arrow. Windows adds those values itself and returns the
910 ** enlarged rectangle on subsequent WM_DRAWITEM messages.
911 */
912 MEASUREITEMSTRUCT mis;
913 mis.CtlType = ODT_MENU;
914 mis.CtlID = 0;
915 mis.itemID = lpitem->wID;
916 mis.itemData = (DWORD)lpitem->dwItemData;
917 mis.itemHeight = 0;
918 mis.itemWidth = 0;
919 SendMessageA( hwndOwner, WM_MEASUREITEM, 0, (LPARAM)&mis );
920 lpitem->rect.right += mis.itemWidth;
921
922 if (menuBar)
923 {
924 lpitem->rect.right += MENU_BAR_ITEMS_SPACE;
925
926
927 /* under at least win95 you seem to be given a standard
928 height for the menu and the height value is ignored */
929
930 if (TWEAK_WineLook == WIN31_LOOK)
931 lpitem->rect.bottom += GetSystemMetrics(SM_CYMENU);
932 else
933 lpitem->rect.bottom += GetSystemMetrics(SM_CYMENU)-1;
934 }
935 else
936 lpitem->rect.bottom += mis.itemHeight;
937
938 TRACE("id=%04x size=%dx%d\n",
939 lpitem->wID, mis.itemWidth, mis.itemHeight);
940 /* Fall through to get check/arrow width calculation. */
941 }
942
943 if (lpitem->fType & MF_SEPARATOR)
944 {
945 lpitem->rect.bottom += SEPARATOR_HEIGHT;
946 return;
947 }
948
949 if (!menuBar)
950 {
951 lpitem->rect.right += 2 * check_bitmap_width;
952 if (lpitem->fType & MF_POPUP)
953 lpitem->rect.right += arrow_bitmap_width;
954 }
955
956 if (lpitem->fType & MF_OWNERDRAW)
957 return;
958
959 if (IS_BITMAP_ITEM(lpitem->fType))
960 {
961 SIZE size;
962
963 MENU_GetBitmapItemSize( (int)lpitem->text, lpitem->dwItemData, &size );
964 lpitem->rect.right += size.cx;
965 lpitem->rect.bottom += size.cy;
966 if (TWEAK_WineLook == WIN98_LOOK)
967 {
968 /* Leave space for the sunken border */
969 lpitem->rect.right += 2;
970 lpitem->rect.bottom += 2;
971 }
972 }
973
974
975 /* it must be a text item - unless it's the system menu */
976 if (!(lpitem->fType & MF_SYSMENU) && IS_STRING_ITEM( lpitem->fType ))
977 { SIZE size;
978
979 GetTextExtentPoint32W(hdc, lpitem->text, strlenW(lpitem->text), &size);
980
981 lpitem->rect.right += size.cx;
982 if (TWEAK_WineLook == WIN31_LOOK)
983 lpitem->rect.bottom += max( size.cy, GetSystemMetrics(SM_CYMENU) );
984 else
985 lpitem->rect.bottom += max(size.cy, GetSystemMetrics(SM_CYMENU)-1);
986 lpitem->xTab = 0;
987
988 if (menuBar)
989 {
990 lpitem->rect.right += MENU_BAR_ITEMS_SPACE;
991 }
992 else if ((p = strchrW( lpitem->text, '\t' )) != NULL)
993 {
994 /* Item contains a tab (only meaningful in popup menus) */
995 GetTextExtentPoint32W(hdc, lpitem->text, (int)(p - lpitem->text) , &size);
996 lpitem->xTab = check_bitmap_width + MENU_TAB_SPACE + size.cx;
997 lpitem->rect.right += MENU_TAB_SPACE;
998 }
999 else
1000 {
1001 if (strchrW( lpitem->text, '\b' ))
1002 lpitem->rect.right += MENU_TAB_SPACE;
1003 lpitem->xTab = lpitem->rect.right - check_bitmap_width
1004 - arrow_bitmap_width;
1005 }
1006 }
1007 TRACE("(%d,%d)-(%d,%d)\n", lpitem->rect.left, lpitem->rect.top, lpitem->rect.right, lpitem->rect.bottom);
1008}
1009
1010
1011/***********************************************************************
1012 * MENU_PopupMenuCalcSize
1013 *
1014 * Calculate the size of a popup menu.
1015 */
1016static void MENU_PopupMenuCalcSize( LPPOPUPMENU lppop, HWND hwndOwner )
1017{
1018 MENUITEM *lpitem;
1019 HDC hdc;
1020 int start, i;
1021 int orgX, orgY, maxX, maxTab, maxTabWidth;
1022
1023 lppop->Width = lppop->Height = 0;
1024 if (lppop->nItems == 0) return;
1025#ifdef __WIN32OS2__
1026 hdc = CreateCompatibleDC( 0 );
1027#else
1028 hdc = GetDC( 0 );
1029#endif
1030
1031 SelectObject( hdc, hMenuFont);
1032
1033 start = 0;
1034 maxX = (TWEAK_WineLook == WIN31_LOOK) ? GetSystemMetrics(SM_CXBORDER) : 2+1 ;
1035
1036 dprintf(("*** 1 lppop->nItems %d", lppop->nItems));
1037 while (start < lppop->nItems)
1038 {
1039 lpitem = &lppop->items[start];
1040 dprintf(("*** 1 lpitem=%x %x [%S] %x", lpitem, lpitem->fType, lpitem->text, lpitem->hSubMenu));
1041 orgX = maxX;
1042 orgY = (TWEAK_WineLook == WIN31_LOOK) ? GetSystemMetrics(SM_CYBORDER) : 2;
1043
1044 maxTab = maxTabWidth = 0;
1045
1046 /* Parse items until column break or end of menu */
1047 for (i = start; i < lppop->nItems; i++, lpitem++)
1048 {
1049 if ((i != start) &&
1050 (lpitem->fType & (MF_MENUBREAK | MF_MENUBARBREAK))) break;
1051
1052 dprintf(("*** 2 lpitem=%x %x [%S] %x", lpitem, lpitem->fType, lpitem->text, lpitem->hSubMenu));
1053 MENU_CalcItemSize( hdc, lpitem, hwndOwner, orgX, orgY, FALSE );
1054 dprintf(("*** 2 rect %d %d %d %d", lpitem->rect.left, lpitem->rect.top, lpitem->rect.right, lpitem->rect.bottom));
1055
1056 if (lpitem->fType & MF_MENUBARBREAK) orgX++;
1057 maxX = max( maxX, lpitem->rect.right );
1058 orgY = lpitem->rect.bottom;
1059 if (IS_STRING_ITEM(lpitem->fType) && lpitem->xTab)
1060 {
1061 maxTab = max( maxTab, lpitem->xTab );
1062 maxTabWidth = max(maxTabWidth,lpitem->rect.right-lpitem->xTab);
1063 }
1064 }
1065
1066 /* Finish the column (set all items to the largest width found) */
1067 maxX = max( maxX, maxTab + maxTabWidth );
1068 for (lpitem = &lppop->items[start]; start < i; start++, lpitem++)
1069 {
1070 lpitem->rect.right = maxX;
1071 if (IS_STRING_ITEM(lpitem->fType) && lpitem->xTab)
1072 lpitem->xTab = maxTab;
1073
1074 }
1075 lppop->Height = max( lppop->Height, orgY );
1076 }
1077
1078 lppop->Width = maxX;
1079
1080 /* space for 3d border */
1081 if(TWEAK_WineLook > WIN31_LOOK)
1082 {
1083 lppop->Height += 2;
1084 lppop->Width += 2;
1085 }
1086
1087 dprintf(("*** 1 lppop->Height %d", lppop->Height));
1088 dprintf(("*** 1 lppop->Width %d", lppop->Width));
1089
1090#ifdef __WIN32OS2__
1091 DeleteDC(hdc);
1092#else
1093 ReleaseDC( 0, hdc );
1094#endif
1095}
1096
1097
1098/***********************************************************************
1099 * MENU_MenuBarCalcSize
1100 *
1101 * FIXME: Word 6 implements its own MDI and its own 'close window' bitmap
1102 * height is off by 1 pixel which causes lengthy window relocations when
1103 * active document window is maximized/restored.
1104 *
1105 * Calculate the size of the menu bar.
1106 */
1107static void MENU_MenuBarCalcSize( HDC hdc, LPRECT lprect,
1108 LPPOPUPMENU lppop, HWND hwndOwner )
1109{
1110 MENUITEM *lpitem;
1111 int start, i, orgX, orgY, maxY, helpPos;
1112
1113 if ((lprect == NULL) || (lppop == NULL)) return;
1114 if (lppop->nItems == 0) return;
1115 TRACE("left=%d top=%d right=%d bottom=%d\n",
1116 lprect->left, lprect->top, lprect->right, lprect->bottom);
1117 lppop->Width = lprect->right - lprect->left;
1118 lppop->Height = 0;
1119 maxY = lprect->top+1;
1120 start = 0;
1121 helpPos = -1;
1122 while (start < lppop->nItems)
1123 {
1124 lpitem = &lppop->items[start];
1125 orgX = lprect->left;
1126 orgY = maxY;
1127
1128 /* Parse items until line break or end of menu */
1129 for (i = start; i < lppop->nItems; i++, lpitem++)
1130 {
1131 if ((helpPos == -1) && (lpitem->fType & MF_RIGHTJUSTIFY)) helpPos = i;
1132 if ((i != start) &&
1133 (lpitem->fType & (MF_MENUBREAK | MF_MENUBARBREAK))) break;
1134
1135 TRACE("calling MENU_CalcItemSize org=(%d, %d)\n",
1136 orgX, orgY );
1137 debug_print_menuitem (" item: ", lpitem, "");
1138 MENU_CalcItemSize( hdc, lpitem, hwndOwner, orgX, orgY, TRUE );
1139
1140 if (lpitem->rect.right > lprect->right)
1141 {
1142 if (i != start) break;
1143 else lpitem->rect.right = lprect->right;
1144 }
1145 maxY = max( maxY, lpitem->rect.bottom );
1146 orgX = lpitem->rect.right;
1147 }
1148
1149 /* Finish the line (set all items to the largest height found) */
1150 while (start < i) lppop->items[start++].rect.bottom = maxY;
1151 }
1152
1153 lprect->bottom = maxY;
1154 lppop->Height = lprect->bottom - lprect->top;
1155
1156 /* Flush right all items between the MF_RIGHTJUSTIFY and */
1157 /* the last item (if several lines, only move the last line) */
1158 lpitem = &lppop->items[lppop->nItems-1];
1159 orgY = lpitem->rect.top;
1160 orgX = lprect->right;
1161 for (i = lppop->nItems - 1; i >= helpPos; i--, lpitem--) {
1162 if ( (helpPos==-1) || (helpPos>i) )
1163 break; /* done */
1164 if (lpitem->rect.top != orgY) break; /* Other line */
1165 if (lpitem->rect.right >= orgX) break; /* Too far right already */
1166 lpitem->rect.left += orgX - lpitem->rect.right;
1167 lpitem->rect.right = orgX;
1168 orgX = lpitem->rect.left;
1169 }
1170}
1171
1172/***********************************************************************
1173 * MENU_DrawMenuItem
1174 *
1175 * Draw a single menu item.
1176 */
1177static void MENU_DrawMenuItem( HWND hwnd, HMENU hmenu, HWND hwndOwner, HDC hdc, MENUITEM *lpitem,
1178 UINT height, BOOL menuBar, UINT odaction )
1179{
1180 RECT rect;
1181
1182 debug_print_menuitem("MENU_DrawMenuItem: ", lpitem, "");
1183
1184 if (lpitem->fType & MF_SYSMENU)
1185 {
1186 if( !IsIconic(hwnd) ) {
1187 if (TWEAK_WineLook > WIN31_LOOK)
1188 NC_DrawSysButton95( hwnd, hdc,
1189 lpitem->fState &
1190 (MF_HILITE | MF_MOUSESELECT) );
1191 else
1192 NC_DrawSysButton( hwnd, hdc,
1193 lpitem->fState &
1194 (MF_HILITE | MF_MOUSESELECT) );
1195 }
1196
1197 return;
1198 }
1199
1200 if (lpitem->fType & MF_OWNERDRAW)
1201 {
1202 /*
1203 ** Experimentation under Windows reveals that an owner-drawn
1204 ** menu is given the rectangle which includes the space it requested
1205 ** in its response to WM_MEASUREITEM _plus_ width for a checkmark
1206 ** and a popup-menu arrow. This is the value of lpitem->rect.
1207 ** Windows will leave all drawing to the application except for
1208 ** the popup-menu arrow. Windows always draws that itself, after
1209 ** the menu owner has finished drawing.
1210 */
1211 DRAWITEMSTRUCT dis;
1212
1213 dis.CtlType = ODT_MENU;
1214 dis.CtlID = 0;
1215 dis.itemID = lpitem->wID;
1216 dis.itemData = (DWORD)lpitem->dwItemData;
1217 dis.itemState = 0;
1218 if (lpitem->fState & MF_CHECKED) dis.itemState |= ODS_CHECKED;
1219 if (lpitem->fState & MF_GRAYED) dis.itemState |= ODS_GRAYED|ODS_DISABLED;
1220 if (lpitem->fState & MF_HILITE) dis.itemState |= ODS_SELECTED;
1221 dis.itemAction = odaction; /* ODA_DRAWENTIRE | ODA_SELECT | ODA_FOCUS; */
1222 dis.hwndItem = (HWND)hmenu;
1223 dis.hDC = hdc;
1224 dis.rcItem = lpitem->rect;
1225 TRACE("Ownerdraw: owner=%04x itemID=%d, itemState=%d, itemAction=%d, "
1226 "hwndItem=%04x, hdc=%04x, rcItem={%d,%d,%d,%d}\n", hwndOwner,
1227 dis.itemID, dis.itemState, dis.itemAction, dis.hwndItem,
1228 dis.hDC, dis.rcItem.left, dis.rcItem.top, dis.rcItem.right,
1229 dis.rcItem.bottom);
1230 SendMessageA( hwndOwner, WM_DRAWITEM, 0, (LPARAM)&dis );
1231 /* Fall through to draw popup-menu arrow */
1232 }
1233
1234 TRACE("rect={%d,%d,%d,%d}\n", lpitem->rect.left, lpitem->rect.top,
1235 lpitem->rect.right,lpitem->rect.bottom);
1236
1237 if (menuBar && (lpitem->fType & MF_SEPARATOR)) return;
1238
1239 rect = lpitem->rect;
1240
1241 if (!(lpitem->fType & MF_OWNERDRAW))
1242 {
1243 if (lpitem->fState & MF_HILITE)
1244 {
1245#ifdef __WIN32OS2__
1246 if(!fOS2Look)
1247#else
1248 if(TWEAK_WineLook == WIN98_LOOK)
1249#endif
1250 {
1251 if(menuBar)
1252 DrawEdge(hdc, &rect, BDR_SUNKENOUTER, BF_RECT);
1253 else
1254 FillRect(hdc, &rect, GetSysColorBrush(COLOR_HIGHLIGHT));
1255 }
1256 else /* Not Win98 Look */
1257 {
1258 if(!IS_BITMAP_ITEM(lpitem->fType))
1259#ifdef __WIN32OS2__
1260 FillRect(hdc, &rect, GetOS2ColorBrush(PMSYSCLR_MENUHILITEBGND));
1261#else
1262 FillRect(hdc, &rect, GetSysColorBrush(COLOR_HIGHLIGHT));
1263#endif
1264 }
1265 }
1266 else
1267 FillRect( hdc, &rect, GetSysColorBrush(COLOR_MENU) );
1268 }
1269
1270 SetBkMode( hdc, TRANSPARENT );
1271
1272 if (!(lpitem->fType & MF_OWNERDRAW))
1273 {
1274 /* vertical separator */
1275 if (!menuBar && (lpitem->fType & MF_MENUBARBREAK))
1276 {
1277 if (TWEAK_WineLook > WIN31_LOOK)
1278 {
1279 RECT rc = rect;
1280 rc.top = 3;
1281 rc.bottom = height - 3;
1282 DrawEdge (hdc, &rc, EDGE_ETCHED, BF_LEFT);
1283 }
1284 else
1285 {
1286 SelectObject( hdc, GetSysColorPen(COLOR_WINDOWFRAME) );
1287 MoveToEx( hdc, rect.left, 0, NULL );
1288 LineTo( hdc, rect.left, height );
1289 }
1290 }
1291
1292 /* horizontal separator */
1293 if (lpitem->fType & MF_SEPARATOR)
1294 {
1295 if (TWEAK_WineLook > WIN31_LOOK)
1296 {
1297 RECT rc = rect;
1298 rc.left++;
1299 rc.right--;
1300 rc.top += SEPARATOR_HEIGHT / 2;
1301 DrawEdge (hdc, &rc, EDGE_ETCHED, BF_TOP);
1302 }
1303 else
1304 {
1305 SelectObject( hdc, GetSysColorPen(COLOR_WINDOWFRAME) );
1306 MoveToEx( hdc, rect.left, rect.top + SEPARATOR_HEIGHT/2, NULL );
1307 LineTo( hdc, rect.right, rect.top + SEPARATOR_HEIGHT/2 );
1308 }
1309 return;
1310 }
1311 }
1312
1313 /* Setup colors */
1314
1315 if (lpitem->fState & MF_HILITE)
1316 {
1317#ifdef __WIN32OS2__
1318 if(!fOS2Look)
1319#else
1320 if(TWEAK_WineLook == WIN98_LOOK)
1321#endif
1322 {
1323 if(menuBar) {
1324 SetTextColor(hdc, GetSysColor(COLOR_MENUTEXT));
1325 SetBkColor(hdc, GetSysColor(COLOR_MENU));
1326 } else {
1327 if(lpitem->fState & MF_GRAYED)
1328 SetTextColor(hdc, GetSysColor(COLOR_GRAYTEXT));
1329 else
1330 SetTextColor(hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
1331 SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT));
1332 }
1333 }
1334 else /* Not Win98 Look */
1335 {
1336 SetTextColor(hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
1337 if(!IS_BITMAP_ITEM(lpitem->fType))
1338#ifdef __WIN32OS2__
1339 SetBkColor(hdc, GetOS2Color(PMSYSCLR_MENUHILITEBGND));
1340#else
1341 SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT));
1342#endif
1343 }
1344 }
1345 else
1346 {
1347 if (lpitem->fState & MF_GRAYED)
1348 SetTextColor( hdc, GetSysColor( COLOR_GRAYTEXT ) );
1349 else
1350 SetTextColor( hdc, GetSysColor( COLOR_MENUTEXT ) );
1351 SetBkColor( hdc, GetSysColor( COLOR_MENU ) );
1352 }
1353
1354 /* helper lines for debugging */
1355/* FrameRect(hdc, &rect, GetStockObject(BLACK_BRUSH));
1356 SelectObject( hdc, GetSysColorPen(COLOR_WINDOWFRAME) );
1357 MoveToEx( hdc, rect.left, (rect.top + rect.bottom)/2, NULL );
1358 LineTo( hdc, rect.right, (rect.top + rect.bottom)/2 );
1359*/
1360
1361 if (!menuBar)
1362 {
1363 INT y = rect.top + rect.bottom;
1364 UINT check_bitmap_width = GetSystemMetrics( SM_CXMENUCHECK );
1365 UINT check_bitmap_height = GetSystemMetrics( SM_CYMENUCHECK );
1366
1367 if (!(lpitem->fType & MF_OWNERDRAW))
1368 {
1369 /* Draw the check mark
1370 *
1371 * FIXME:
1372 * Custom checkmark bitmaps are monochrome but not always 1bpp.
1373 */
1374 HBITMAP bm = (lpitem->fState & MF_CHECKED) ? lpitem->hCheckBit : lpitem->hUnCheckBit;
1375 if (bm) /* we have a custom bitmap */
1376 {
1377 HDC hdcMem = CreateCompatibleDC( hdc );
1378 SelectObject( hdcMem, bm );
1379 BitBlt( hdc, rect.left, (y - check_bitmap_height) / 2,
1380 check_bitmap_width, check_bitmap_height,
1381 hdcMem, 0, 0, SRCCOPY );
1382 DeleteDC( hdcMem );
1383 }
1384 else if (lpitem->fState & MF_CHECKED) /* standard bitmaps */
1385 {
1386 RECT r;
1387 HBITMAP bm = CreateBitmap( check_bitmap_width, check_bitmap_height, 1, 1, NULL );
1388 HDC hdcMem = CreateCompatibleDC( hdc );
1389 SelectObject( hdcMem, bm );
1390 SetRect( &r, 0, 0, check_bitmap_width, check_bitmap_height );
1391 DrawFrameControl( hdcMem, &r, DFC_MENU,
1392 (lpitem->fType & MFT_RADIOCHECK) ?
1393 DFCS_MENUBULLET : DFCS_MENUCHECK );
1394 BitBlt( hdc, rect.left, (y - r.bottom) / 2, r.right, r.bottom,
1395 hdcMem, 0, 0, SRCCOPY );
1396 DeleteDC( hdcMem );
1397 DeleteObject( bm );
1398 }
1399 }
1400
1401 /* Draw the popup-menu arrow */
1402 if (lpitem->fType & MF_POPUP)
1403 {
1404 HDC hdcMem = CreateCompatibleDC( hdc );
1405 HBITMAP hOrigBitmap;
1406
1407 hOrigBitmap = SelectObject( hdcMem, hStdMnArrow );
1408 BitBlt( hdc, rect.right - arrow_bitmap_width - 1,
1409 (y - arrow_bitmap_height) / 2,
1410 arrow_bitmap_width, arrow_bitmap_height,
1411 hdcMem, 0, 0, SRCCOPY );
1412 SelectObject( hdcMem, hOrigBitmap );
1413 DeleteDC( hdcMem );
1414 }
1415
1416 rect.left += check_bitmap_width;
1417 rect.right -= arrow_bitmap_width;
1418 }
1419
1420 /* Done for owner-drawn */
1421 if (lpitem->fType & MF_OWNERDRAW)
1422 return;
1423
1424 /* Draw the item text or bitmap */
1425 if (IS_BITMAP_ITEM(lpitem->fType))
1426 {
1427 MENU_DrawBitmapItem( hdc, lpitem, &rect, menuBar );
1428 return;
1429
1430 }
1431 /* No bitmap - process text if present */
1432 else if (IS_STRING_ITEM(lpitem->fType))
1433 {
1434 register int i;
1435 HFONT hfontOld = 0;
1436
1437 UINT uFormat = (menuBar) ?
1438 DT_CENTER | DT_VCENTER | DT_SINGLELINE :
1439 DT_LEFT | DT_VCENTER | DT_SINGLELINE;
1440
1441#ifdef __WIN32OS2__
1442 if ( lpitem->fState & MFS_DEFAULT && !fOS2Look)
1443#else
1444 if ( lpitem->fState & MFS_DEFAULT )
1445#endif
1446 {
1447 hfontOld = SelectObject( hdc, hMenuFontBold);
1448 }
1449
1450 if (menuBar)
1451 {
1452 rect.left += MENU_BAR_ITEMS_SPACE / 2;
1453 rect.right -= MENU_BAR_ITEMS_SPACE / 2;
1454 }
1455
1456 for (i = 0; lpitem->text[i]; i++)
1457 if ((lpitem->text[i] == '\t') || (lpitem->text[i] == '\b'))
1458 break;
1459
1460 if( (TWEAK_WineLook != WIN31_LOOK) && (lpitem->fState & MF_GRAYED))
1461 {
1462 if (!(lpitem->fState & MF_HILITE) )
1463 {
1464 ++rect.left; ++rect.top; ++rect.right; ++rect.bottom;
1465 SetTextColor(hdc, RGB(0xff, 0xff, 0xff));
1466 DrawTextW( hdc, lpitem->text, i, &rect, uFormat );
1467 --rect.left; --rect.top; --rect.right; --rect.bottom;
1468 }
1469 SetTextColor(hdc, RGB(0x80, 0x80, 0x80));
1470 }
1471
1472 DrawTextW( hdc, lpitem->text, i, &rect, uFormat);
1473
1474 /* paint the shortcut text */
1475 if (!menuBar && lpitem->text[i]) /* There's a tab or flush-right char */
1476 {
1477 if (lpitem->text[i] == '\t')
1478 {
1479 rect.left = lpitem->xTab;
1480 uFormat = DT_LEFT | DT_VCENTER | DT_SINGLELINE;
1481 }
1482 else
1483 {
1484 uFormat = DT_RIGHT | DT_VCENTER | DT_SINGLELINE;
1485 }
1486
1487 if( !(TWEAK_WineLook == WIN31_LOOK) && (lpitem->fState & MF_GRAYED))
1488 {
1489 if (!(lpitem->fState & MF_HILITE) )
1490 {
1491 ++rect.left; ++rect.top; ++rect.right; ++rect.bottom;
1492 SetTextColor(hdc, RGB(0xff, 0xff, 0xff));
1493 DrawTextW( hdc, lpitem->text + i + 1, -1, &rect, uFormat );
1494 --rect.left; --rect.top; --rect.right; --rect.bottom;
1495 }
1496 SetTextColor(hdc, RGB(0x80, 0x80, 0x80));
1497 }
1498 DrawTextW( hdc, lpitem->text + i + 1, -1, &rect, uFormat );
1499 }
1500
1501 if (hfontOld)
1502 SelectObject (hdc, hfontOld);
1503 }
1504}
1505
1506
1507/***********************************************************************
1508 * MENU_DrawPopupMenu
1509 *
1510 * Paint a popup menu.
1511 */
1512static void MENU_DrawPopupMenu( HWND hwnd, HDC hdc, HMENU hmenu )
1513{
1514 HBRUSH hPrevBrush = 0;
1515 RECT rect;
1516
1517 TRACE("wnd=0x%04x dc=0x%04x menu=0x%04x\n", hwnd, hdc, hmenu);
1518
1519 GetClientRect( hwnd, &rect );
1520
1521 if(TWEAK_WineLook == WIN31_LOOK)
1522 {
1523 rect.bottom -= POPUP_YSHADE * GetSystemMetrics(SM_CYBORDER);
1524 rect.right -= POPUP_XSHADE * GetSystemMetrics(SM_CXBORDER);
1525 }
1526
1527 if((hPrevBrush = SelectObject( hdc, GetSysColorBrush(COLOR_MENU) ))
1528 && (SelectObject( hdc, hMenuFont)))
1529 {
1530 HPEN hPrevPen;
1531
1532 Rectangle( hdc, rect.left, rect.top, rect.right, rect.bottom );
1533
1534 hPrevPen = SelectObject( hdc, GetStockObject( NULL_PEN ) );
1535 if( hPrevPen )
1536 {
1537 INT ropPrev, i;
1538 POPUPMENU *menu;
1539
1540 /* draw 3-d shade */
1541 if(TWEAK_WineLook == WIN31_LOOK) {
1542 SelectObject( hdc, hShadeBrush );
1543 SetBkMode( hdc, TRANSPARENT );
1544 ropPrev = SetROP2( hdc, R2_MASKPEN );
1545
1546 i = rect.right; /* why SetBrushOrg() doesn't? */
1547 PatBlt( hdc, i & 0xfffffffe,
1548 rect.top + POPUP_YSHADE*GetSystemMetrics(SM_CYBORDER),
1549 i%2 + POPUP_XSHADE*GetSystemMetrics(SM_CXBORDER),
1550 rect.bottom - rect.top, 0x00a000c9 );
1551 i = rect.bottom;
1552 PatBlt( hdc, rect.left + POPUP_XSHADE*GetSystemMetrics(SM_CXBORDER),
1553 i & 0xfffffffe,rect.right - rect.left,
1554 i%2 + POPUP_YSHADE*GetSystemMetrics(SM_CYBORDER), 0x00a000c9 );
1555 SelectObject( hdc, hPrevPen );
1556 SelectObject( hdc, hPrevBrush );
1557 SetROP2( hdc, ropPrev );
1558 }
1559 else
1560 DrawEdge (hdc, &rect, EDGE_RAISED, BF_RECT);
1561
1562 /* draw menu items */
1563
1564 menu = MENU_GetMenu( hmenu );
1565 if (menu && menu->nItems)
1566 {
1567 MENUITEM *item;
1568 UINT u;
1569
1570 for (u = menu->nItems, item = menu->items; u > 0; u--, item++)
1571 MENU_DrawMenuItem( hwnd, hmenu, menu->hwndOwner, hdc, item,
1572 menu->Height, FALSE, ODA_DRAWENTIRE );
1573
1574 }
1575 } else
1576 {
1577 SelectObject( hdc, hPrevBrush );
1578 }
1579 }
1580}
1581
1582/***********************************************************************
1583 * MENU_DrawMenuBar
1584 *
1585 * Paint a menu bar. Returns the height of the menu bar.
1586 * called from [windows/nonclient.c]
1587 */
1588UINT MENU_DrawMenuBar( HDC hDC, LPRECT lprect, HWND hwnd,
1589 BOOL suppress_draw)
1590{
1591 LPPOPUPMENU lppop;
1592 UINT i,retvalue;
1593 HFONT hfontOld = 0;
1594 HMENU hMenu = GetMenu(hwnd);
1595
1596 lppop = MENU_GetMenu( hMenu );
1597 if (lppop == NULL || lprect == NULL)
1598 {
1599 retvalue = GetSystemMetrics(SM_CYMENU);
1600 goto END;
1601 }
1602
1603 TRACE("(%04x, %p, %p)\n", hDC, lprect, lppop);
1604
1605 hfontOld = SelectObject( hDC, hMenuFont);
1606
1607 if (lppop->Height == 0)
1608 MENU_MenuBarCalcSize(hDC, lprect, lppop, hwnd);
1609
1610 lprect->bottom = lprect->top + lppop->Height;
1611
1612 if (suppress_draw)
1613 {
1614 retvalue = lppop->Height;
1615 goto END;
1616 }
1617
1618 FillRect(hDC, lprect, GetSysColorBrush(COLOR_MENU) );
1619
1620 if (TWEAK_WineLook == WIN31_LOOK)
1621 {
1622 SelectObject( hDC, GetSysColorPen(COLOR_WINDOWFRAME) );
1623 MoveToEx( hDC, lprect->left, lprect->bottom, NULL );
1624 LineTo( hDC, lprect->right, lprect->bottom );
1625 }
1626 else
1627 {
1628 SelectObject( hDC, GetSysColorPen(COLOR_3DFACE));
1629 MoveToEx( hDC, lprect->left, lprect->bottom, NULL );
1630 LineTo( hDC, lprect->right, lprect->bottom );
1631 }
1632
1633 if (lppop->nItems == 0)
1634 {
1635 retvalue = GetSystemMetrics(SM_CYMENU);
1636 goto END;
1637 }
1638
1639 for (i = 0; i < lppop->nItems; i++)
1640 {
1641 MENU_DrawMenuItem( hwnd, hMenu, hwnd,
1642 hDC, &lppop->items[i], lppop->Height, TRUE, ODA_DRAWENTIRE );
1643 }
1644 retvalue = lppop->Height;
1645
1646END:
1647 if (hfontOld) SelectObject (hDC, hfontOld);
1648 return retvalue;
1649}
1650
1651
1652/***********************************************************************
1653 * MENU_ShowPopup
1654 *
1655 * Display a popup menu.
1656 */
1657static BOOL MENU_ShowPopup( HWND hwndOwner, HMENU hmenu, UINT id,
1658 INT x, INT y, INT xanchor, INT yanchor )
1659{
1660 POPUPMENU *menu;
1661 UINT width, height;
1662
1663 TRACE("owner=0x%04x hmenu=0x%04x id=0x%04x x=0x%04x y=0x%04x xa=0x%04x ya=0x%04x\n",
1664 hwndOwner, hmenu, id, x, y, xanchor, yanchor);
1665
1666 if (!(menu = MENU_GetMenu( hmenu ))) return FALSE;
1667 if (menu->FocusedItem != NO_SELECTED_ITEM)
1668 {
1669 menu->items[menu->FocusedItem].fState &= ~(MF_HILITE|MF_MOUSESELECT);
1670 menu->FocusedItem = NO_SELECTED_ITEM;
1671 }
1672
1673 /* store the owner for DrawItem */
1674 menu->hwndOwner = hwndOwner;
1675
1676
1677 MENU_PopupMenuCalcSize( menu, hwndOwner );
1678
1679 /* adjust popup menu pos so that it fits within the desktop */
1680
1681 width = menu->Width + GetSystemMetrics(SM_CXBORDER);
1682 height = menu->Height + GetSystemMetrics(SM_CYBORDER);
1683
1684 if( x + width > GetSystemMetrics(SM_CXSCREEN ))
1685 {
1686 if( xanchor )
1687 x -= width - xanchor;
1688 if( x + width > GetSystemMetrics(SM_CXSCREEN))
1689 x = GetSystemMetrics(SM_CXSCREEN) - width;
1690 }
1691 if( x < 0 ) x = 0;
1692
1693 if( y + height > GetSystemMetrics(SM_CYSCREEN ))
1694 {
1695 if( yanchor )
1696 y -= height + yanchor;
1697 if( y + height > GetSystemMetrics(SM_CYSCREEN ))
1698 y = GetSystemMetrics(SM_CYSCREEN) - height;
1699 }
1700 if( y < 0 ) y = 0;
1701
1702 if( TWEAK_WineLook == WIN31_LOOK )
1703 {
1704 width += POPUP_XSHADE * GetSystemMetrics(SM_CXBORDER); /* add space for shading */
1705 height += POPUP_YSHADE * GetSystemMetrics(SM_CYBORDER);
1706 }
1707
1708 /* NOTE: In Windows, top menu popup is not owned. */
1709 menu->hWnd = CreateWindowA( POPUPMENU_CLASS_ATOM, NULL,
1710 WS_POPUP, x, y, width, height,
1711 hwndOwner, 0, GetWindowLongA(hwndOwner,GWL_HINSTANCE),
1712 (LPVOID)hmenu );
1713 if( !menu->hWnd ) return FALSE;
1714 if (!top_popup) top_popup = menu->hWnd;
1715
1716 /* Display the window */
1717
1718 SetWindowPos( menu->hWnd, HWND_TOP, 0, 0, 0, 0,
1719 SWP_SHOWWINDOW | SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE );
1720 UpdateWindow( menu->hWnd );
1721 return TRUE;
1722}
1723
1724
1725/***********************************************************************
1726 * MENU_SelectItem
1727 */
1728static void MENU_SelectItem( HWND hwndOwner, HMENU hmenu, UINT wIndex,
1729 BOOL sendMenuSelect, HMENU topmenu )
1730{
1731 LPPOPUPMENU lppop;
1732 HDC hdc;
1733
1734 TRACE("owner=0x%04x menu=0x%04x index=0x%04x select=0x%04x\n", hwndOwner, hmenu, wIndex, sendMenuSelect);
1735
1736 lppop = MENU_GetMenu( hmenu );
1737 if ((!lppop) || (!lppop->nItems) || (!lppop->hWnd)) return;
1738
1739 if (lppop->FocusedItem == wIndex) return;
1740 if (lppop->wFlags & MF_POPUP) hdc = GetDC( lppop->hWnd );
1741 else hdc = GetDCEx( lppop->hWnd, 0, DCX_CACHE | DCX_WINDOW);
1742
1743 SelectObject( hdc, hMenuFont);
1744
1745 /* Clear previous highlighted item */
1746 if (lppop->FocusedItem != NO_SELECTED_ITEM)
1747 {
1748 lppop->items[lppop->FocusedItem].fState &= ~(MF_HILITE|MF_MOUSESELECT);
1749 MENU_DrawMenuItem(lppop->hWnd, hmenu, hwndOwner, hdc,&lppop->items[lppop->FocusedItem],
1750 lppop->Height, !(lppop->wFlags & MF_POPUP),
1751 ODA_SELECT );
1752 }
1753
1754 /* Highlight new item (if any) */
1755 lppop->FocusedItem = wIndex;
1756 if (lppop->FocusedItem != NO_SELECTED_ITEM)
1757 {
1758 if(!(lppop->items[wIndex].fType & MF_SEPARATOR)) {
1759 lppop->items[wIndex].fState |= MF_HILITE;
1760 MENU_DrawMenuItem( lppop->hWnd, hmenu, hwndOwner, hdc,
1761 &lppop->items[wIndex], lppop->Height,
1762 !(lppop->wFlags & MF_POPUP), ODA_SELECT );
1763 }
1764 if (sendMenuSelect)
1765 {
1766 MENUITEM *ip = &lppop->items[lppop->FocusedItem];
1767 SendMessageA( hwndOwner, WM_MENUSELECT,
1768 MAKELONG(ip->fType & MF_POPUP ? wIndex: ip->wID,
1769 ip->fType | ip->fState | MF_MOUSESELECT |
1770 (lppop->wFlags & MF_SYSMENU)), hmenu);
1771 }
1772 }
1773 else if (sendMenuSelect) {
1774 if(topmenu){
1775 int pos;
1776 if((pos=MENU_FindSubMenu(&topmenu, hmenu))!=NO_SELECTED_ITEM){
1777 POPUPMENU *ptm = MENU_GetMenu( topmenu );
1778 MENUITEM *ip = &ptm->items[pos];
1779 SendMessageA( hwndOwner, WM_MENUSELECT, MAKELONG(pos,
1780 ip->fType | ip->fState | MF_MOUSESELECT |
1781 (ptm->wFlags & MF_SYSMENU)), topmenu);
1782 }
1783 }
1784 }
1785 ReleaseDC( lppop->hWnd, hdc );
1786}
1787
1788
1789/***********************************************************************
1790 * MENU_MoveSelection
1791 *
1792 * Moves currently selected item according to the offset parameter.
1793 * If there is no selection then it should select the last item if
1794 * offset is ITEM_PREV or the first item if offset is ITEM_NEXT.
1795 */
1796static void MENU_MoveSelection( HWND hwndOwner, HMENU hmenu, INT offset )
1797{
1798 INT i;
1799 POPUPMENU *menu;
1800
1801 TRACE("hwnd=0x%04x hmenu=0x%04x off=0x%04x\n", hwndOwner, hmenu, offset);
1802
1803 menu = MENU_GetMenu( hmenu );
1804 if ((!menu) || (!menu->items)) return;
1805
1806 if ( menu->FocusedItem != NO_SELECTED_ITEM )
1807 {
1808 if( menu->nItems == 1 ) return; else
1809 for (i = menu->FocusedItem + offset ; i >= 0 && i < menu->nItems
1810 ; i += offset)
1811 if (!(menu->items[i].fType & MF_SEPARATOR))
1812 {
1813 MENU_SelectItem( hwndOwner, hmenu, i, TRUE, 0 );
1814 return;
1815 }
1816 }
1817
1818 for ( i = (offset > 0) ? 0 : menu->nItems - 1;
1819 i >= 0 && i < menu->nItems ; i += offset)
1820 if (!(menu->items[i].fType & MF_SEPARATOR))
1821 {
1822 MENU_SelectItem( hwndOwner, hmenu, i, TRUE, 0 );
1823 return;
1824 }
1825}
1826
1827
1828/**********************************************************************
1829 * MENU_SetItemData
1830 *
1831 * Set an item flags, id and text ptr. Called by InsertMenu() and
1832 * ModifyMenu().
1833 */
1834static BOOL MENU_SetItemData( MENUITEM *item, UINT flags, UINT id,
1835 LPCWSTR str )
1836{
1837 LPWSTR prevText = IS_STRING_ITEM(item->fType) ? item->text : NULL;
1838
1839 debug_print_menuitem("MENU_SetItemData from: ", item, "");
1840 TRACE("flags=%x str=%p\n", flags, str);
1841
1842 if (IS_STRING_ITEM(flags))
1843 {
1844 if (!str)
1845 {
1846 flags |= MF_SEPARATOR;
1847 item->text = NULL;
1848 }
1849 else
1850 {
1851 LPWSTR text;
1852 /* Item beginning with a backspace is a help item */
1853 if (*str == '\b')
1854 {
1855 flags |= MF_HELP;
1856 str++;
1857 }
1858 if (!(text = HeapAlloc( GetProcessHeap(), 0, (strlenW(str)+1) * sizeof(WCHAR) )))
1859 return FALSE;
1860 strcpyW( text, str );
1861 item->text = text;
1862 }
1863 }
1864 else if (IS_BITMAP_ITEM(flags))
1865 item->text = (LPWSTR)(HBITMAP)LOWORD(str);
1866 else item->text = NULL;
1867
1868 if (flags & MF_OWNERDRAW)
1869 item->dwItemData = (DWORD)str;
1870 else
1871 item->dwItemData = 0;
1872
1873 if ((item->fType & MF_POPUP) && (flags & MF_POPUP) && (item->hSubMenu != id) )
1874 DestroyMenu( item->hSubMenu ); /* ModifyMenu() spec */
1875
1876 if (flags & MF_POPUP)
1877 {
1878 POPUPMENU *menu = MENU_GetMenu((UINT16)id);
1879 if (menu) menu->wFlags |= MF_POPUP;
1880 else
1881 {
1882 item->wID = 0;
1883 item->hSubMenu = 0;
1884 item->fType = 0;
1885 item->fState = 0;
1886 return FALSE;
1887 }
1888 }
1889
1890 item->wID = id;
1891 if (flags & MF_POPUP)
1892 item->hSubMenu = id;
1893
1894 if ((item->fType & MF_POPUP) && !(flags & MF_POPUP) )
1895 flags |= MF_POPUP; /* keep popup */
1896
1897 item->fType = flags & TYPE_MASK;
1898 item->fState = (flags & STATE_MASK) &
1899 ~(MF_HILITE | MF_MOUSESELECT | MF_BYPOSITION);
1900
1901
1902 /* Don't call SetRectEmpty here! */
1903
1904
1905 if (prevText) HeapFree( GetProcessHeap(), 0, prevText );
1906
1907 debug_print_menuitem("MENU_SetItemData to : ", item, "");
1908 return TRUE;
1909}
1910
1911
1912/**********************************************************************
1913 * MENU_InsertItem
1914 *
1915 * Insert a new item into a menu.
1916 */
1917static MENUITEM *MENU_InsertItem( HMENU hMenu, UINT pos, UINT flags )
1918{
1919 MENUITEM *newItems;
1920 POPUPMENU *menu;
1921
1922 if (!(menu = MENU_GetMenu(hMenu)))
1923 return NULL;
1924
1925 /* Find where to insert new item */
1926
1927 if (flags & MF_BYPOSITION) {
1928 if (pos > menu->nItems)
1929 pos = menu->nItems;
1930 } else {
1931 if (!MENU_FindItem( &hMenu, &pos, flags ))
1932 pos = menu->nItems;
1933 else {
1934 if (!(menu = MENU_GetMenu( hMenu )))
1935 return NULL;
1936 }
1937 }
1938
1939 /* Create new items array */
1940 newItems = HeapAlloc( GetProcessHeap(), 0, sizeof(MENUITEM) * (menu->nItems+1) );
1941 if (!newItems)
1942 {
1943 WARN("allocation failed\n" );
1944 return NULL;
1945 }
1946 if (menu->nItems > 0)
1947 {
1948 /* Copy the old array into the new one */
1949 if (pos > 0) memcpy( newItems, menu->items, pos * sizeof(MENUITEM) );
1950 if (pos < menu->nItems) memcpy( &newItems[pos+1], &menu->items[pos],
1951 (menu->nItems-pos)*sizeof(MENUITEM) );
1952 HeapFree( GetProcessHeap(), 0, menu->items );
1953 }
1954 menu->items = newItems;
1955 menu->nItems++;
1956 memset( &newItems[pos], 0, sizeof(*newItems) );
1957 menu->Height = 0; /* force size recalculate */
1958 return &newItems[pos];
1959}
1960
1961
1962/**********************************************************************
1963 * MENU_ParseResource
1964 *
1965 * Parse a standard menu resource and add items to the menu.
1966 * Return a pointer to the end of the resource.
1967 */
1968static LPCSTR MENU_ParseResource( LPCSTR res, HMENU hMenu, BOOL unicode )
1969{
1970 WORD flags, id = 0;
1971 LPCSTR str;
1972
1973 do
1974 {
1975 flags = GET_WORD(res);
1976 res += sizeof(WORD);
1977 if (!(flags & MF_POPUP))
1978 {
1979 id = GET_WORD(res);
1980 res += sizeof(WORD);
1981 }
1982 str = res;
1983 if (!unicode) res += strlen(str) + 1;
1984 else res += (strlenW((LPCWSTR)str) + 1) * sizeof(WCHAR);
1985 if (flags & MF_POPUP)
1986 {
1987 HMENU hSubMenu = CreatePopupMenu();
1988 if (!hSubMenu) return NULL;
1989 if (!(res = MENU_ParseResource( res, hSubMenu, unicode )))
1990 return NULL;
1991 if (!unicode) AppendMenuA( hMenu, flags, (UINT)hSubMenu, str );
1992 else AppendMenuW( hMenu, flags, (UINT)hSubMenu, (LPCWSTR)str );
1993 }
1994 else /* Not a popup */
1995 {
1996 if (!unicode) AppendMenuA( hMenu, flags, id, *str ? str : NULL );
1997 else AppendMenuW( hMenu, flags, id,
1998 *(LPCWSTR)str ? (LPCWSTR)str : NULL );
1999 }
2000 } while (!(flags & MF_END));
2001 return res;
2002}
2003
2004
2005/**********************************************************************
2006 * MENUEX_ParseResource
2007 *
2008 * Parse an extended menu resource and add items to the menu.
2009 * Return a pointer to the end of the resource.
2010 */
2011static LPCSTR MENUEX_ParseResource( LPCSTR res, HMENU hMenu)
2012{
2013 WORD resinfo;
2014 do {
2015 MENUITEMINFOW mii;
2016
2017 mii.cbSize = sizeof(mii);
2018 mii.fMask = MIIM_STATE | MIIM_ID | MIIM_TYPE;
2019 mii.fType = GET_DWORD(res);
2020 res += sizeof(DWORD);
2021 mii.fState = GET_DWORD(res);
2022 res += sizeof(DWORD);
2023 mii.wID = GET_DWORD(res);
2024 res += sizeof(DWORD);
2025 resinfo = GET_WORD(res); /* FIXME: for 16-bit apps this is a byte. */
2026 res += sizeof(WORD);
2027 /* Align the text on a word boundary. */
2028 res += (~((int)res - 1)) & 1;
2029 mii.dwTypeData = (LPWSTR) res;
2030 res += (1 + strlenW(mii.dwTypeData)) * sizeof(WCHAR);
2031 /* Align the following fields on a dword boundary. */
2032 res += (~((int)res - 1)) & 3;
2033
2034 TRACE("Menu item: [%08x,%08x,%04x,%04x,%s]\n",
2035 mii.fType, mii.fState, mii.wID, resinfo, debugstr_w(mii.dwTypeData));
2036
2037 if (resinfo & 1) { /* Pop-up? */
2038 /* DWORD helpid = GET_DWORD(res); FIXME: use this. */
2039 res += sizeof(DWORD);
2040 mii.hSubMenu = CreatePopupMenu();
2041 if (!mii.hSubMenu)
2042 return NULL;
2043 if (!(res = MENUEX_ParseResource(res, mii.hSubMenu))) {
2044 DestroyMenu(mii.hSubMenu);
2045 return NULL;
2046 }
2047 mii.fMask |= MIIM_SUBMENU;
2048 mii.fType |= MF_POPUP;
2049 }
2050 else if(!*mii.dwTypeData && !(mii.fType & MF_SEPARATOR))
2051 {
2052 WARN("Converting NULL menu item %04x, type %04x to SEPARATOR\n",
2053 mii.wID, mii.fType);
2054 mii.fType |= MF_SEPARATOR;
2055 }
2056 InsertMenuItemW(hMenu, -1, MF_BYPOSITION, &mii);
2057 } while (!(resinfo & MF_END));
2058 return res;
2059}
2060
2061
2062/***********************************************************************
2063 * MENU_GetSubPopup
2064 *
2065 * Return the handle of the selected sub-popup menu (if any).
2066 */
2067static HMENU MENU_GetSubPopup( HMENU hmenu )
2068{
2069 POPUPMENU *menu;
2070 MENUITEM *item;
2071
2072 menu = MENU_GetMenu( hmenu );
2073
2074 if ((!menu) || (menu->FocusedItem == NO_SELECTED_ITEM)) return 0;
2075
2076 item = &menu->items[menu->FocusedItem];
2077 if ((item->fType & MF_POPUP) && (item->fState & MF_MOUSESELECT))
2078 return item->hSubMenu;
2079 return 0;
2080}
2081
2082
2083/***********************************************************************
2084 * MENU_HideSubPopups
2085 *
2086 * Hide the sub-popup menus of this menu.
2087 */
2088static void MENU_HideSubPopups( HWND hwndOwner, HMENU hmenu,
2089 BOOL sendMenuSelect )
2090{
2091 POPUPMENU *menu = MENU_GetMenu( hmenu );
2092
2093 TRACE("owner=0x%04x hmenu=0x%04x 0x%04x\n", hwndOwner, hmenu, sendMenuSelect);
2094
2095 if (menu && top_popup)
2096 {
2097 HMENU hsubmenu;
2098 POPUPMENU *submenu;
2099 MENUITEM *item;
2100
2101 if (menu->FocusedItem != NO_SELECTED_ITEM)
2102 {
2103 item = &menu->items[menu->FocusedItem];
2104 if (!(item->fType & MF_POPUP) ||
2105 !(item->fState & MF_MOUSESELECT)) return;
2106 item->fState &= ~MF_MOUSESELECT;
2107 hsubmenu = item->hSubMenu;
2108 } else return;
2109
2110 submenu = MENU_GetMenu( hsubmenu );
2111 MENU_HideSubPopups( hwndOwner, hsubmenu, FALSE );
2112 MENU_SelectItem( hwndOwner, hsubmenu, NO_SELECTED_ITEM, sendMenuSelect, 0 );
2113 DestroyWindow( submenu->hWnd );
2114 submenu->hWnd = 0;
2115 }
2116}
2117
2118
2119/***********************************************************************
2120 * MENU_ShowSubPopup
2121 *
2122 * Display the sub-menu of the selected item of this menu.
2123 * Return the handle of the submenu, or hmenu if no submenu to display.
2124 */
2125static HMENU MENU_ShowSubPopup( HWND hwndOwner, HMENU hmenu,
2126 BOOL selectFirst, UINT wFlags )
2127{
2128 RECT rect;
2129 POPUPMENU *menu;
2130 MENUITEM *item;
2131 HDC hdc;
2132
2133 TRACE("owner=0x%04x hmenu=0x%04x 0x%04x\n", hwndOwner, hmenu, selectFirst);
2134
2135 if (!(menu = MENU_GetMenu( hmenu ))) return hmenu;
2136
2137 if (menu->FocusedItem == NO_SELECTED_ITEM) return hmenu;
2138
2139 item = &menu->items[menu->FocusedItem];
2140 if (!(item->fType & MF_POPUP) || (item->fState & (MF_GRAYED | MF_DISABLED)))
2141 return hmenu;
2142
2143 /* message must be sent before using item,
2144 because nearly everything may be changed by the application ! */
2145
2146 /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
2147 if (!(wFlags & TPM_NONOTIFY))
2148 SendMessageA( hwndOwner, WM_INITMENUPOPUP, item->hSubMenu,
2149 MAKELONG( menu->FocusedItem, IS_SYSTEM_MENU(menu) ));
2150
2151 item = &menu->items[menu->FocusedItem];
2152 rect = item->rect;
2153
2154 /* correct item if modified as a reaction to WM_INITMENUPOPUP message */
2155 if (!(item->fState & MF_HILITE))
2156 {
2157 if (menu->wFlags & MF_POPUP) hdc = GetDC( menu->hWnd );
2158 else hdc = GetDCEx( menu->hWnd, 0, DCX_CACHE | DCX_WINDOW);
2159
2160 SelectObject( hdc, hMenuFont);
2161
2162 item->fState |= MF_HILITE;
2163 MENU_DrawMenuItem( menu->hWnd, hmenu, hwndOwner, hdc, item, menu->Height, !(menu->wFlags & MF_POPUP), ODA_DRAWENTIRE );
2164 ReleaseDC( menu->hWnd, hdc );
2165 }
2166 if (!item->rect.top && !item->rect.left && !item->rect.bottom && !item->rect.right)
2167 item->rect = rect;
2168
2169 item->fState |= MF_MOUSESELECT;
2170
2171 if (IS_SYSTEM_MENU(menu))
2172 {
2173 MENU_InitSysMenuPopup(item->hSubMenu,
2174 GetWindowLongA( menu->hWnd, GWL_STYLE ),
2175 GetClassLongA( menu->hWnd, GCL_STYLE));
2176
2177 NC_GetSysPopupPos( menu->hWnd, &rect );
2178 rect.top = rect.bottom;
2179 rect.right = GetSystemMetrics(SM_CXSIZE);
2180 rect.bottom = GetSystemMetrics(SM_CYSIZE);
2181 }
2182 else
2183 {
2184 GetWindowRect( menu->hWnd, &rect );
2185 if (menu->wFlags & MF_POPUP)
2186 {
2187 rect.left += item->rect.right - GetSystemMetrics(SM_CXBORDER);
2188 rect.top += item->rect.top;
2189 rect.right = item->rect.left - item->rect.right + GetSystemMetrics(SM_CXBORDER);
2190 rect.bottom = item->rect.top - item->rect.bottom;
2191 }
2192 else
2193 {
2194 rect.left += item->rect.left;
2195 rect.top += item->rect.bottom;
2196 rect.right = item->rect.right - item->rect.left;
2197 rect.bottom = item->rect.bottom - item->rect.top;
2198 }
2199 }
2200
2201 MENU_ShowPopup( hwndOwner, item->hSubMenu, menu->FocusedItem,
2202 rect.left, rect.top, rect.right, rect.bottom );
2203 if (selectFirst)
2204 MENU_MoveSelection( hwndOwner, item->hSubMenu, ITEM_NEXT );
2205 return item->hSubMenu;
2206}
2207
2208
2209
2210/**********************************************************************
2211 * MENU_IsMenuActive
2212 */
2213BOOL MENU_IsMenuActive(void)
2214{
2215 return (top_popup != 0);
2216}
2217
2218/***********************************************************************
2219 * MENU_PtMenu
2220 *
2221 * Walks menu chain trying to find a menu pt maps to.
2222 */
2223static HMENU MENU_PtMenu( HMENU hMenu, POINT pt )
2224{
2225 POPUPMENU *menu = MENU_GetMenu( hMenu );
2226 UINT item = menu->FocusedItem;
2227 HMENU ret;
2228
2229 /* try subpopup first (if any) */
2230 ret = (item != NO_SELECTED_ITEM &&
2231 (menu->items[item].fType & MF_POPUP) &&
2232 (menu->items[item].fState & MF_MOUSESELECT))
2233 ? MENU_PtMenu(menu->items[item].hSubMenu, pt) : 0;
2234
2235 if (!ret) /* check the current window (avoiding WM_HITTEST) */
2236 {
2237 INT ht = NC_HandleNCHitTest( menu->hWnd, pt );
2238 if( menu->wFlags & MF_POPUP )
2239 {
2240 if (ht != HTNOWHERE && ht != HTERROR) ret = hMenu;
2241 }
2242 else if (ht == HTSYSMENU)
2243 ret = get_win_sys_menu( menu->hWnd );
2244 else if (ht == HTMENU)
2245 ret = GetMenu( menu->hWnd );
2246 }
2247 return ret;
2248}
2249
2250/***********************************************************************
2251 * MENU_ExecFocusedItem
2252 *
2253 * Execute a menu item (for instance when user pressed Enter).
2254 * Return the wID of the executed item. Otherwise, -1 indicating
2255 * that no menu item was executed;
2256 * Have to receive the flags for the TrackPopupMenu options to avoid
2257 * sending unwanted message.
2258 *
2259 */
2260static INT MENU_ExecFocusedItem( MTRACKER* pmt, HMENU hMenu, UINT wFlags )
2261{
2262 MENUITEM *item;
2263 POPUPMENU *menu = MENU_GetMenu( hMenu );
2264
2265 TRACE("%p hmenu=0x%04x\n", pmt, hMenu);
2266
2267 if (!menu || !menu->nItems ||
2268 (menu->FocusedItem == NO_SELECTED_ITEM)) return -1;
2269
2270 item = &menu->items[menu->FocusedItem];
2271
2272 TRACE("%08x %08x %08x\n",
2273 hMenu, item->wID, item->hSubMenu);
2274
2275 if (!(item->fType & MF_POPUP))
2276 {
2277 if (!(item->fState & (MF_GRAYED | MF_DISABLED)) && !(item->fType & MF_SEPARATOR))
2278 {
2279 /* If TPM_RETURNCMD is set you return the id, but
2280 do not send a message to the owner */
2281 if(!(wFlags & TPM_RETURNCMD))
2282 {
2283 if( menu->wFlags & MF_SYSMENU )
2284 PostMessageA( pmt->hOwnerWnd, WM_SYSCOMMAND, item->wID,
2285 MAKELPARAM((INT16)pmt->pt.x, (INT16)pmt->pt.y) );
2286 else
2287 PostMessageA( pmt->hOwnerWnd, WM_COMMAND, item->wID, 0 );
2288 }
2289 return item->wID;
2290 }
2291 }
2292 else
2293 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd, hMenu, TRUE, wFlags);
2294
2295 return -1;
2296}
2297
2298/***********************************************************************
2299 * MENU_SwitchTracking
2300 *
2301 * Helper function for menu navigation routines.
2302 */
2303static void MENU_SwitchTracking( MTRACKER* pmt, HMENU hPtMenu, UINT id )
2304{
2305 POPUPMENU *ptmenu = MENU_GetMenu( hPtMenu );
2306 POPUPMENU *topmenu = MENU_GetMenu( pmt->hTopMenu );
2307
2308 TRACE("%p hmenu=0x%04x 0x%04x\n", pmt, hPtMenu, id);
2309
2310 if( pmt->hTopMenu != hPtMenu &&
2311 !((ptmenu->wFlags | topmenu->wFlags) & MF_POPUP) )
2312 {
2313 /* both are top level menus (system and menu-bar) */
2314 MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE );
2315 MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, NO_SELECTED_ITEM, FALSE, 0 );
2316 pmt->hTopMenu = hPtMenu;
2317 }
2318 else MENU_HideSubPopups( pmt->hOwnerWnd, hPtMenu, FALSE );
2319 MENU_SelectItem( pmt->hOwnerWnd, hPtMenu, id, TRUE, 0 );
2320}
2321
2322
2323/***********************************************************************
2324 * MENU_ButtonDown
2325 *
2326 * Return TRUE if we can go on with menu tracking.
2327 */
2328static BOOL MENU_ButtonDown( MTRACKER* pmt, HMENU hPtMenu, UINT wFlags )
2329{
2330 TRACE("%p hmenu=0x%04x\n", pmt, hPtMenu);
2331
2332 if (hPtMenu)
2333 {
2334 UINT id = 0;
2335 POPUPMENU *ptmenu = MENU_GetMenu( hPtMenu );
2336 MENUITEM *item;
2337
2338 if( IS_SYSTEM_MENU(ptmenu) )
2339 item = ptmenu->items;
2340 else
2341 item = MENU_FindItemByCoords( ptmenu, pmt->pt, &id );
2342
2343 if( item )
2344 {
2345 if( ptmenu->FocusedItem != id )
2346 MENU_SwitchTracking( pmt, hPtMenu, id );
2347
2348 /* If the popup menu is not already "popped" */
2349 if(!(item->fState & MF_MOUSESELECT ))
2350 {
2351 pmt->hCurrentMenu = MENU_ShowSubPopup( pmt->hOwnerWnd, hPtMenu, FALSE, wFlags );
2352
2353 /* In win31, a newly popped menu always remains opened for the next buttonup */
2354#ifdef __WIN32OS2__
2355 if(fOS2Look)
2356 ptmenu->bTimeToHide = FALSE;
2357#endif
2358 if(TWEAK_WineLook == WIN31_LOOK)
2359 ptmenu->bTimeToHide = FALSE;
2360 }
2361
2362 return TRUE;
2363 }
2364 /* Else the click was on the menu bar, finish the tracking */
2365 }
2366 return FALSE;
2367}
2368
2369/***********************************************************************
2370 * MENU_ButtonUp
2371 *
2372 * Return the value of MENU_ExecFocusedItem if
2373 * the selected item was not a popup. Else open the popup.
2374 * A -1 return value indicates that we go on with menu tracking.
2375 *
2376 */
2377static INT MENU_ButtonUp( MTRACKER* pmt, HMENU hPtMenu, UINT wFlags)
2378{
2379 TRACE("%p hmenu=0x%04x\n", pmt, hPtMenu);
2380
2381 if (hPtMenu)
2382 {
2383 UINT id = 0;
2384 POPUPMENU *ptmenu = MENU_GetMenu( hPtMenu );
2385 MENUITEM *item;
2386
2387 if( IS_SYSTEM_MENU(ptmenu) )
2388 item = ptmenu->items;
2389 else
2390 item = MENU_FindItemByCoords( ptmenu, pmt->pt, &id );
2391
2392 if( item && (ptmenu->FocusedItem == id ))
2393 {
2394 if( !(item->fType & MF_POPUP) )
2395 return MENU_ExecFocusedItem( pmt, hPtMenu, wFlags);
2396
2397 /* If we are dealing with the top-level menu */
2398 /* and this is a click on an already "popped" item: */
2399 /* Stop the menu tracking and close the opened submenus */
2400 if((pmt->hTopMenu == hPtMenu) && (ptmenu->bTimeToHide == TRUE))
2401 return 0;
2402 }
2403 ptmenu->bTimeToHide = TRUE;
2404 }
2405 return -1;
2406}
2407
2408
2409/***********************************************************************
2410 * MENU_MouseMove
2411 *
2412 * Return TRUE if we can go on with menu tracking.
2413 */
2414static BOOL MENU_MouseMove( MTRACKER* pmt, HMENU hPtMenu, UINT wFlags )
2415{
2416 UINT id = NO_SELECTED_ITEM;
2417 POPUPMENU *ptmenu = NULL;
2418
2419 if( hPtMenu )
2420 {
2421 ptmenu = MENU_GetMenu( hPtMenu );
2422 if( IS_SYSTEM_MENU(ptmenu) )
2423 id = 0;
2424 else
2425 MENU_FindItemByCoords( ptmenu, pmt->pt, &id );
2426 }
2427
2428 if( id == NO_SELECTED_ITEM )
2429 {
2430 MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
2431 NO_SELECTED_ITEM, TRUE, pmt->hTopMenu);
2432
2433 }
2434 else if( ptmenu->FocusedItem != id )
2435 {
2436 MENU_SwitchTracking( pmt, hPtMenu, id );
2437 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd, hPtMenu, FALSE, wFlags);
2438 }
2439 return TRUE;
2440}
2441
2442
2443/***********************************************************************
2444 * MENU_DoNextMenu
2445 *
2446 * NOTE: WM_NEXTMENU documented in Win32 is a bit different.
2447 */
2448static LRESULT MENU_DoNextMenu( MTRACKER* pmt, UINT vk )
2449{
2450 POPUPMENU *menu = MENU_GetMenu( pmt->hTopMenu );
2451
2452 if( (vk == VK_LEFT && menu->FocusedItem == 0 ) ||
2453 (vk == VK_RIGHT && menu->FocusedItem == menu->nItems - 1))
2454 {
2455 MDINEXTMENU next_menu;
2456 HMENU hNewMenu;
2457 HWND hNewWnd;
2458 UINT id = 0;
2459
2460 next_menu.hmenuIn = (IS_SYSTEM_MENU(menu)) ? GetSubMenu(pmt->hTopMenu,0) : pmt->hTopMenu;
2461 next_menu.hmenuNext = 0;
2462 next_menu.hwndNext = 0;
2463 SendMessageW( pmt->hOwnerWnd, WM_NEXTMENU, vk, (LPARAM)&next_menu );
2464
2465 TRACE("%04x [%04x] -> %04x [%04x]\n",
2466 pmt->hCurrentMenu, pmt->hOwnerWnd, next_menu.hmenuNext, next_menu.hwndNext );
2467
2468 if (!next_menu.hmenuNext || !next_menu.hwndNext)
2469 {
2470 DWORD style = GetWindowLongA( pmt->hOwnerWnd, GWL_STYLE );
2471 hNewWnd = pmt->hOwnerWnd;
2472 if( IS_SYSTEM_MENU(menu) )
2473 {
2474 /* switch to the menu bar */
2475
2476 if(style & WS_CHILD || !(hNewMenu = GetMenu(hNewWnd))) return FALSE;
2477
2478 if( vk == VK_LEFT )
2479 {
2480 menu = MENU_GetMenu( hNewMenu );
2481 id = menu->nItems - 1;
2482 }
2483 }
2484 else if (style & WS_SYSMENU )
2485 {
2486 /* switch to the system menu */
2487 hNewMenu = get_win_sys_menu( hNewWnd );
2488 }
2489 else return FALSE;
2490 }
2491 else /* application returned a new menu to switch to */
2492 {
2493 hNewMenu = next_menu.hmenuNext;
2494 hNewWnd = WIN_GetFullHandle( next_menu.hwndNext );
2495
2496 if( IsMenu(hNewMenu) && IsWindow(hNewWnd) )
2497 {
2498 DWORD style = GetWindowLongA( hNewWnd, GWL_STYLE );
2499
2500 if (style & WS_SYSMENU &&
2501 GetSubMenu(get_win_sys_menu(hNewWnd), 0) == hNewMenu )
2502 {
2503 /* get the real system menu */
2504 hNewMenu = get_win_sys_menu(hNewWnd);
2505 }
2506 else if (style & WS_CHILD || GetMenu(hNewWnd) != hNewMenu )
2507 {
2508 /* FIXME: Not sure what to do here;
2509 * perhaps try to track hNewMenu as a popup? */
2510
2511 TRACE(" -- got confused.\n");
2512 return FALSE;
2513 }
2514 }
2515 else return FALSE;
2516 }
2517
2518 if( hNewMenu != pmt->hTopMenu )
2519 {
2520 MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, NO_SELECTED_ITEM,
2521 FALSE, 0 );
2522 if( pmt->hCurrentMenu != pmt->hTopMenu )
2523 MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE );
2524 }
2525
2526 if( hNewWnd != pmt->hOwnerWnd )
2527 {
2528 ReleaseCapture();
2529 pmt->hOwnerWnd = hNewWnd;
2530#ifdef __WIN32OS2__
2531 SetCapture(pmt->hOwnerWnd); //SvL: Don't know if this is good enough
2532#else
2533 EVENT_Capture( pmt->hOwnerWnd, HTMENU );
2534#endif
2535 }
2536
2537 pmt->hTopMenu = pmt->hCurrentMenu = hNewMenu; /* all subpopups are hidden */
2538 MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, id, TRUE, 0 );
2539
2540 return TRUE;
2541 }
2542 return FALSE;
2543}
2544
2545/***********************************************************************
2546 * MENU_SuspendPopup
2547 *
2548 * The idea is not to show the popup if the next input message is
2549 * going to hide it anyway.
2550 */
2551static BOOL MENU_SuspendPopup( MTRACKER* pmt, UINT16 uMsg )
2552{
2553 MSG msg;
2554
2555 msg.hwnd = pmt->hOwnerWnd;
2556
2557 PeekMessageA( &msg, 0, 0, 0, PM_NOYIELD | PM_REMOVE);
2558 pmt->trackFlags |= TF_SKIPREMOVE;
2559
2560 switch( uMsg )
2561 {
2562 case WM_KEYDOWN:
2563 PeekMessageA( &msg, 0, 0, 0, PM_NOYIELD | PM_NOREMOVE);
2564 if( msg.message == WM_KEYUP || msg.message == WM_PAINT )
2565 {
2566 PeekMessageA( &msg, 0, 0, 0, PM_NOYIELD | PM_REMOVE);
2567 PeekMessageA( &msg, 0, 0, 0, PM_NOYIELD | PM_NOREMOVE);
2568 if( msg.message == WM_KEYDOWN &&
2569 (msg.wParam == VK_LEFT || msg.wParam == VK_RIGHT))
2570 {
2571 pmt->trackFlags |= TF_SUSPENDPOPUP;
2572 return TRUE;
2573 }
2574 }
2575 break;
2576 }
2577
2578 /* failures go through this */
2579 pmt->trackFlags &= ~TF_SUSPENDPOPUP;
2580 return FALSE;
2581}
2582
2583/***********************************************************************
2584 * MENU_KeyEscape
2585 *
2586 * Handle a VK_ESCAPE key event in a menu.
2587 */
2588static BOOL MENU_KeyEscape(MTRACKER* pmt, UINT wFlags)
2589{
2590 BOOL bEndMenu = TRUE;
2591
2592 if (pmt->hCurrentMenu != pmt->hTopMenu)
2593 {
2594 POPUPMENU *menu = MENU_GetMenu(pmt->hCurrentMenu);
2595
2596 if (menu->wFlags & MF_POPUP)
2597 {
2598 HMENU hmenutmp, hmenuprev;
2599
2600 hmenuprev = hmenutmp = pmt->hTopMenu;
2601
2602 /* close topmost popup */
2603 while (hmenutmp != pmt->hCurrentMenu)
2604 {
2605 hmenuprev = hmenutmp;
2606 hmenutmp = MENU_GetSubPopup( hmenuprev );
2607 }
2608
2609 MENU_HideSubPopups( pmt->hOwnerWnd, hmenuprev, TRUE );
2610 pmt->hCurrentMenu = hmenuprev;
2611 bEndMenu = FALSE;
2612 }
2613 }
2614
2615 return bEndMenu;
2616}
2617
2618/***********************************************************************
2619 * MENU_KeyLeft
2620 *
2621 * Handle a VK_LEFT key event in a menu.
2622 */
2623static void MENU_KeyLeft( MTRACKER* pmt, UINT wFlags )
2624{
2625 POPUPMENU *menu;
2626 HMENU hmenutmp, hmenuprev;
2627 UINT prevcol;
2628
2629 hmenuprev = hmenutmp = pmt->hTopMenu;
2630 menu = MENU_GetMenu( hmenutmp );
2631
2632 /* Try to move 1 column left (if possible) */
2633 if( (prevcol = MENU_GetStartOfPrevColumn( pmt->hCurrentMenu )) !=
2634 NO_SELECTED_ITEM ) {
2635
2636 MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
2637 prevcol, TRUE, 0 );
2638 return;
2639 }
2640
2641 /* close topmost popup */
2642 while (hmenutmp != pmt->hCurrentMenu)
2643 {
2644 hmenuprev = hmenutmp;
2645 hmenutmp = MENU_GetSubPopup( hmenuprev );
2646 }
2647
2648 MENU_HideSubPopups( pmt->hOwnerWnd, hmenuprev, TRUE );
2649 pmt->hCurrentMenu = hmenuprev;
2650
2651 if ( (hmenuprev == pmt->hTopMenu) && !(menu->wFlags & MF_POPUP) )
2652 {
2653 /* move menu bar selection if no more popups are left */
2654
2655 if( !MENU_DoNextMenu( pmt, VK_LEFT) )
2656 MENU_MoveSelection( pmt->hOwnerWnd, pmt->hTopMenu, ITEM_PREV );
2657
2658 if ( hmenuprev != hmenutmp || pmt->trackFlags & TF_SUSPENDPOPUP )
2659 {
2660 /* A sublevel menu was displayed - display the next one
2661 * unless there is another displacement coming up */
2662
2663 if( !MENU_SuspendPopup( pmt, WM_KEYDOWN ) )
2664 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd,
2665 pmt->hTopMenu, TRUE, wFlags);
2666 }
2667 }
2668}
2669
2670
2671/***********************************************************************
2672 * MENU_KeyRight
2673 *
2674 * Handle a VK_RIGHT key event in a menu.
2675 */
2676static void MENU_KeyRight( MTRACKER* pmt, UINT wFlags )
2677{
2678 HMENU hmenutmp;
2679 POPUPMENU *menu = MENU_GetMenu( pmt->hTopMenu );
2680 UINT nextcol;
2681
2682 TRACE("MENU_KeyRight called, cur %x (%s), top %x (%s).\n",
2683 pmt->hCurrentMenu,
2684 debugstr_w((MENU_GetMenu(pmt->hCurrentMenu))->
2685 items[0].text),
2686 pmt->hTopMenu, debugstr_w(menu->items[0].text) );
2687
2688 if ( (menu->wFlags & MF_POPUP) || (pmt->hCurrentMenu != pmt->hTopMenu))
2689 {
2690 /* If already displaying a popup, try to display sub-popup */
2691
2692 hmenutmp = pmt->hCurrentMenu;
2693 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd, hmenutmp, TRUE, wFlags);
2694
2695 /* if subpopup was displayed then we are done */
2696 if (hmenutmp != pmt->hCurrentMenu) return;
2697 }
2698
2699 /* Check to see if there's another column */
2700 if( (nextcol = MENU_GetStartOfNextColumn( pmt->hCurrentMenu )) !=
2701 NO_SELECTED_ITEM ) {
2702 TRACE("Going to %d.\n", nextcol );
2703 MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
2704 nextcol, TRUE, 0 );
2705 return;
2706 }
2707
2708 if (!(menu->wFlags & MF_POPUP)) /* menu bar tracking */
2709 {
2710 if( pmt->hCurrentMenu != pmt->hTopMenu )
2711 {
2712 MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE );
2713 hmenutmp = pmt->hCurrentMenu = pmt->hTopMenu;
2714 } else hmenutmp = 0;
2715
2716 /* try to move to the next item */
2717 if( !MENU_DoNextMenu( pmt, VK_RIGHT) )
2718 MENU_MoveSelection( pmt->hOwnerWnd, pmt->hTopMenu, ITEM_NEXT );
2719
2720 if( hmenutmp || pmt->trackFlags & TF_SUSPENDPOPUP )
2721 if( !MENU_SuspendPopup(pmt, WM_KEYDOWN) )
2722 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd,
2723 pmt->hTopMenu, TRUE, wFlags);
2724 }
2725}
2726
2727/***********************************************************************
2728 * MENU_TrackMenu
2729 *
2730 * Menu tracking code.
2731 */
2732static INT MENU_TrackMenu( HMENU hmenu, UINT wFlags, INT x, INT y,
2733 HWND hwnd, const RECT *lprect )
2734{
2735 MSG msg;
2736 POPUPMENU *menu;
2737 BOOL fRemove;
2738 INT executedMenuId = -1;
2739 MTRACKER mt;
2740 BOOL enterIdleSent = FALSE;
2741#ifdef __WIN32OS2__
2742 BOOL bSysMenu = FALSE, bFirstMouseMove = TRUE;
2743#endif
2744
2745 mt.trackFlags = 0;
2746 mt.hCurrentMenu = hmenu;
2747 mt.hTopMenu = hmenu;
2748 mt.hOwnerWnd = WIN_GetFullHandle( hwnd );
2749 mt.pt.x = x;
2750 mt.pt.y = y;
2751
2752 TRACE("hmenu=0x%04x flags=0x%08x (%d,%d) hwnd=0x%04x (%d,%d)-(%d,%d)\n",
2753 hmenu, wFlags, x, y, hwnd, (lprect) ? lprect->left : 0, (lprect) ? lprect->top : 0,
2754 (lprect) ? lprect->right : 0, (lprect) ? lprect->bottom : 0);
2755
2756 fEndMenu = FALSE;
2757 if (!(menu = MENU_GetMenu( hmenu ))) return FALSE;
2758
2759 if (wFlags & TPM_BUTTONDOWN)
2760 {
2761 /* Get the result in order to start the tracking or not */
2762 fRemove = MENU_ButtonDown( &mt, hmenu, wFlags );
2763 fEndMenu = !fRemove;
2764 }
2765
2766#ifdef __WIN32OS2__
2767 bSysMenu = IS_SYSTEM_MENU(menu);
2768
2769 //SvL: Set keyboard & mouse event capture
2770 SetCapture(mt.hOwnerWnd);
2771 //SetCapture breaks system menu (double click), must generate double
2772 //clicks manually
2773 OSLibSetMenuDoubleClick(TRUE);
2774#else
2775 EVENT_Capture( mt.hOwnerWnd, HTMENU );
2776#endif
2777 while (!fEndMenu)
2778 {
2779 menu = MENU_GetMenu( mt.hCurrentMenu );
2780 if (!menu) /* sometimes happens if I do a window manager close */
2781 break;
2782
2783 /* we have to keep the message in the queue until it's
2784 * clear that menu loop is not over yet. */
2785
2786 for (;;)
2787 {
2788 if (PeekMessageA( &msg, 0, 0, 0, PM_NOREMOVE ))
2789 {
2790#ifdef __WIN32OS2__
2791 //We need to get & remove the message immediately, otherwise
2792 //we'll run into an Odin msg handling limitation (see oslibmsg.cpp todo's)
2793 GetMessageA(&msg, 0, 0, 0);
2794#endif
2795 if (!CallMsgFilterA( &msg, MSGF_MENU )) break;
2796 /* remove the message from the queue */
2797#ifndef __WIN32OS2__
2798 PeekMessageA( &msg, 0, msg.message, msg.message, PM_REMOVE );
2799#endif
2800 }
2801 else
2802 {
2803 if (!enterIdleSent)
2804 {
2805 HWND win = (wFlags & TPM_ENTERIDLEEX && menu->wFlags & MF_POPUP) ? menu->hWnd : 0;
2806 enterIdleSent = TRUE;
2807 SendMessageW( mt.hOwnerWnd, WM_ENTERIDLE, MSGF_MENU, (LPARAM)win );
2808 }
2809 WaitMessage();
2810 }
2811 }
2812
2813 /* check if EndMenu() tried to cancel us, by posting this message */
2814 if(msg.message == WM_CANCELMODE)
2815 {
2816 /* we are now out of the loop */
2817 fEndMenu = TRUE;
2818
2819#ifdef __WIN32OS2__
2820 /* remove the message from the queue */
2821 PeekMessageA( &msg, 0, msg.message, msg.message, PM_REMOVE );
2822#endif
2823 /* break out of internal loop, ala ESCAPE */
2824 break;
2825 }
2826
2827 TranslateMessage( &msg );
2828 mt.pt = msg.pt;
2829
2830 if ( (msg.hwnd==menu->hWnd) || (msg.message!=WM_TIMER) )
2831 enterIdleSent=FALSE;
2832
2833 fRemove = FALSE;
2834 if ((msg.message >= WM_MOUSEFIRST) && (msg.message <= WM_MOUSELAST))
2835 {
2836 /*
2837 * use the mouse coordinates in lParam instead of those in the MSG
2838 * struct to properly handle synthetic messages. lParam coords are
2839 * relative to client area, so they must be converted; since they can
2840 * be negative, we must use SLOWORD/SHIWORD instead of LOWORD/HIWORD.
2841 */
2842 mt.pt.x = SLOWORD(msg.lParam);
2843 mt.pt.y = SHIWORD(msg.lParam);
2844#ifdef __WIN32OS2__
2845 //This MUST be here (in Rewind, but not in Wine)
2846#endif
2847 ClientToScreen(msg.hwnd,&mt.pt);
2848
2849 /* Find a menu for this mouse event */
2850 hmenu = MENU_PtMenu( mt.hTopMenu, mt.pt );
2851
2852 switch(msg.message)
2853 {
2854 /* no WM_NC... messages in captured state */
2855
2856 case WM_RBUTTONDBLCLK:
2857 case WM_RBUTTONDOWN:
2858#ifdef __WIN32OS2__
2859 if (!(wFlags & TPM_RIGHTBUTTON)) {
2860 MENUITEM *item;
2861 UINT id = 0;
2862
2863 if( IS_SYSTEM_MENU(menu) )
2864 item = menu->items;
2865 else
2866 item = MENU_FindItemByCoords( menu, mt.pt, &id );
2867 //@@PF If our pointer is over the menu - do nothing
2868 if (item) break;
2869 //@@PF Time to close menu - win2k&98 checked
2870 fEndMenu = 1;
2871 break;
2872 }
2873 goto buttondown;
2874#else
2875 if (!(wFlags & TPM_RIGHTBUTTON)) break;
2876#endif
2877 /* fall through */
2878 case WM_LBUTTONDBLCLK:
2879#ifdef __WIN32OS2__
2880 if (bSysMenu && (hmenu == mt.hTopMenu))
2881 {
2882 //double click on system menu -> close application
2883 if (!(GetClassLongA(hwnd, GCL_STYLE) & CS_NOCLOSE)) {
2884 PostMessageA(hwnd, WM_SYSCOMMAND, SC_CLOSE, msg.lParam);
2885 fEndMenu = TRUE;
2886 break;
2887 }
2888 }
2889 if(IsIconic(hwnd)) {
2890 //double click on minimized icon -> restore parent window
2891 PostMessageA(hwnd, WM_SYSCOMMAND,SC_RESTORE, msg.lParam);
2892 fEndMenu = TRUE;
2893 break;
2894 }
2895 //fall through
2896buttondown:
2897
2898#endif
2899 case WM_LBUTTONDOWN:
2900 /* If the message belongs to the menu, removes it from the queue */
2901 /* Else, end menu tracking */
2902 fRemove = MENU_ButtonDown( &mt, hmenu, wFlags );
2903 fEndMenu = !fRemove;
2904 break;
2905
2906 case WM_RBUTTONUP:
2907 if (!(wFlags & TPM_RIGHTBUTTON)) break;
2908 /* fall through */
2909 case WM_LBUTTONUP:
2910 /* Check if a menu was selected by the mouse */
2911 if (hmenu)
2912 {
2913 executedMenuId = MENU_ButtonUp( &mt, hmenu, wFlags);
2914
2915 /* End the loop if executedMenuId is an item ID */
2916 /* or if the job was done (executedMenuId = 0). */
2917 fEndMenu = fRemove = (executedMenuId != -1);
2918 }
2919 /* No menu was selected by the mouse */
2920 /* if the function was called by TrackPopupMenu, continue
2921 with the menu tracking. If not, stop it */
2922 else
2923 fEndMenu = ((wFlags & TPM_POPUPMENU) ? FALSE : TRUE);
2924
2925 break;
2926
2927 case WM_MOUSEMOVE:
2928 /* In win95 winelook, the selected menu item must be changed every time the
2929 mouse moves. In Win31 winelook, the mouse button has to be held down */
2930
2931#ifdef __WIN32OS2__
2932 //PF Win32 eats first mousemove to prevent auto-select of item
2933 //on TrackMenuPopup pressed button - verified in Win2k
2934 if (bFirstMouseMove)
2935 {
2936 bFirstMouseMove = FALSE;
2937 break;
2938 }
2939
2940 if (!fOS2Look ||
2941#else
2942 if ( (TWEAK_WineLook > WIN31_LOOK) ||
2943#endif
2944 ( (msg.wParam & MK_LBUTTON) ||
2945 ((wFlags & TPM_RIGHTBUTTON) && (msg.wParam & MK_RBUTTON))) )
2946
2947 fEndMenu |= !MENU_MouseMove( &mt, hmenu, wFlags );
2948
2949 } /* switch(msg.message) - mouse */
2950 }
2951 else if ((msg.message >= WM_KEYFIRST) && (msg.message <= WM_KEYLAST))
2952 {
2953 fRemove = TRUE; /* Keyboard messages are always removed */
2954 switch(msg.message)
2955 {
2956 case WM_KEYDOWN:
2957 switch(msg.wParam)
2958 {
2959 case VK_HOME:
2960 case VK_END:
2961 MENU_SelectItem( mt.hOwnerWnd, mt.hCurrentMenu,
2962 NO_SELECTED_ITEM, FALSE, 0 );
2963 /* fall through */
2964 case VK_UP:
2965 MENU_MoveSelection( mt.hOwnerWnd, mt.hCurrentMenu,
2966 (msg.wParam == VK_HOME)? ITEM_NEXT : ITEM_PREV );
2967 break;
2968
2969 case VK_DOWN: /* If on menu bar, pull-down the menu */
2970
2971 menu = MENU_GetMenu( mt.hCurrentMenu );
2972 if (!(menu->wFlags & MF_POPUP))
2973 mt.hCurrentMenu = MENU_ShowSubPopup(mt.hOwnerWnd, mt.hTopMenu, TRUE, wFlags);
2974 else /* otherwise try to move selection */
2975 MENU_MoveSelection( mt.hOwnerWnd, mt.hCurrentMenu, ITEM_NEXT );
2976 break;
2977
2978 case VK_LEFT:
2979 MENU_KeyLeft( &mt, wFlags );
2980 break;
2981
2982 case VK_RIGHT:
2983 MENU_KeyRight( &mt, wFlags );
2984 break;
2985
2986 case VK_ESCAPE:
2987 fEndMenu = MENU_KeyEscape(&mt, wFlags);
2988 break;
2989
2990 case VK_F1:
2991 {
2992 HELPINFO hi;
2993 hi.cbSize = sizeof(HELPINFO);
2994 hi.iContextType = HELPINFO_MENUITEM;
2995 if (menu->FocusedItem == NO_SELECTED_ITEM)
2996 hi.iCtrlId = 0;
2997 else
2998 hi.iCtrlId = menu->items[menu->FocusedItem].wID;
2999 hi.hItemHandle = hmenu;
3000 hi.dwContextId = menu->dwContextHelpID;
3001 hi.MousePos = msg.pt;
3002 SendMessageA(hwnd, WM_HELP, 0, (LPARAM)&hi);
3003 break;
3004 }
3005
3006 default:
3007 break;
3008 }
3009 break; /* WM_KEYDOWN */
3010
3011 case WM_SYSKEYDOWN:
3012 switch(msg.wParam)
3013 {
3014 case VK_MENU:
3015 fEndMenu = TRUE;
3016 break;
3017
3018 }
3019 break; /* WM_SYSKEYDOWN */
3020
3021 case WM_CHAR:
3022 {
3023 UINT pos;
3024
3025 if (msg.wParam == '\r' || msg.wParam == ' ')
3026 {
3027 executedMenuId = MENU_ExecFocusedItem(&mt,mt.hCurrentMenu, wFlags);
3028 fEndMenu = (executedMenuId != -1);
3029
3030 break;
3031 }
3032
3033 /* Hack to avoid control chars. */
3034 /* We will find a better way real soon... */
3035 if ((msg.wParam <= 32) || (msg.wParam >= 127)) break;
3036
3037 pos = MENU_FindItemByKey( mt.hOwnerWnd, mt.hCurrentMenu,
3038 LOWORD(msg.wParam), FALSE );
3039 if (pos == (UINT)-2) fEndMenu = TRUE;
3040 else if (pos == (UINT)-1) MessageBeep(0);
3041 else
3042 {
3043 MENU_SelectItem( mt.hOwnerWnd, mt.hCurrentMenu, pos,
3044 TRUE, 0 );
3045 executedMenuId = MENU_ExecFocusedItem(&mt,mt.hCurrentMenu, wFlags);
3046 fEndMenu = (executedMenuId != -1);
3047 }
3048 }
3049 break;
3050 } /* switch(msg.message) - kbd */
3051 }
3052 else
3053 {
3054 DispatchMessageA( &msg );
3055 }
3056
3057 if (!fEndMenu) fRemove = TRUE;
3058
3059 /* finally remove message from the queue */
3060
3061 if (fRemove && !(mt.trackFlags & TF_SKIPREMOVE) )
3062#ifdef __WIN32OS2__
3063 ;
3064#else
3065 PeekMessageA( &msg, 0, msg.message, msg.message, PM_REMOVE );
3066#endif
3067 else mt.trackFlags &= ~TF_SKIPREMOVE;
3068 }
3069
3070#ifdef __WIN32OS2__
3071 OSLibSetMenuDoubleClick(FALSE);
3072#endif
3073 ReleaseCapture();
3074
3075 /* If dropdown is still painted and the close box is clicked on
3076 then the menu will be destroyed as part of the DispatchMessage above.
3077 This will then invalidate the menu handle in mt.hTopMenu. We should
3078 check for this first. */
3079 if( IsMenu( mt.hTopMenu ) )
3080 {
3081 menu = MENU_GetMenu( mt.hTopMenu );
3082
3083 if( IsWindow( mt.hOwnerWnd ) )
3084 {
3085 MENU_HideSubPopups( mt.hOwnerWnd, mt.hTopMenu, FALSE );
3086
3087 if (menu && menu->wFlags & MF_POPUP)
3088 {
3089 DestroyWindow( menu->hWnd );
3090 menu->hWnd = 0;
3091 }
3092 MENU_SelectItem( mt.hOwnerWnd, mt.hTopMenu, NO_SELECTED_ITEM, FALSE, 0 );
3093 SendMessageA( mt.hOwnerWnd, WM_MENUSELECT, MAKELONG(0,0xffff), 0 );
3094 }
3095
3096 /* Reset the variable for hiding menu */
3097 if( menu ) menu->bTimeToHide = FALSE;
3098 }
3099
3100 /* The return value is only used by TrackPopupMenu */
3101 return ((executedMenuId != -1) ? executedMenuId : 0);
3102}
3103
3104/***********************************************************************
3105 * MENU_InitTracking
3106 */
3107static BOOL MENU_InitTracking(HWND hWnd, HMENU hMenu, BOOL bPopup, UINT wFlags)
3108{
3109 TRACE("hwnd=0x%04x hmenu=0x%04x\n", hWnd, hMenu);
3110
3111 HideCaret(0);
3112
3113 /* Send WM_ENTERMENULOOP and WM_INITMENU message only if TPM_NONOTIFY flag is not specified */
3114 if (!(wFlags & TPM_NONOTIFY))
3115 SendMessageA( hWnd, WM_ENTERMENULOOP, bPopup, 0 );
3116
3117 SendMessageA( hWnd, WM_SETCURSOR, (WPARAM)hWnd, HTCAPTION );
3118
3119 if (!(wFlags & TPM_NONOTIFY))
3120 {
3121 POPUPMENU *menu;
3122 SendMessageA( hWnd, WM_INITMENU, hMenu, 0 );
3123 if ((menu = MENU_GetMenu( hMenu )) && (!menu->Height))
3124 { /* app changed/recreated menu bar entries in WM_INITMENU
3125 Recalculate menu sizes else clicks will not work */
3126 SetWindowPos( hWnd, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE |
3127 SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED );
3128
3129 }
3130 }
3131 return TRUE;
3132}
3133/***********************************************************************
3134 * MENU_ExitTracking
3135 */
3136static BOOL MENU_ExitTracking(HWND hWnd)
3137{
3138 TRACE("hwnd=0x%04x\n", hWnd);
3139
3140 SendMessageA( hWnd, WM_EXITMENULOOP, 0, 0 );
3141 ShowCaret(0);
3142 return TRUE;
3143}
3144
3145/***********************************************************************
3146 * MENU_TrackMouseMenuBar
3147 *
3148 * Menu-bar tracking upon a mouse event. Called from NC_HandleSysCommand().
3149 */
3150void MENU_TrackMouseMenuBar( HWND hWnd, INT ht, POINT pt )
3151{
3152 HMENU hMenu = (ht == HTSYSMENU) ? get_win_sys_menu( hWnd ) : GetMenu( hWnd );
3153 UINT wFlags = TPM_ENTERIDLEEX | TPM_BUTTONDOWN | TPM_LEFTALIGN | TPM_LEFTBUTTON;
3154
3155 TRACE("wnd=%x ht=0x%04x (%ld,%ld)\n", hWnd, ht, pt.x, pt.y);
3156
3157 if (IsMenu(hMenu))
3158 {
3159 /* map point to parent client coordinates */
3160 HWND parent = GetAncestor( hWnd, GA_PARENT );
3161 if (parent != GetDesktopWindow()) ScreenToClient( parent, &pt );
3162
3163 MENU_InitTracking( hWnd, hMenu, FALSE, wFlags );
3164 MENU_TrackMenu( hMenu, wFlags, pt.x, pt.y, hWnd, NULL );
3165 MENU_ExitTracking(hWnd);
3166 }
3167}
3168
3169
3170/***********************************************************************
3171 * MENU_TrackKbdMenuBar
3172 *
3173 * Menu-bar tracking upon a keyboard event. Called from NC_HandleSysCommand().
3174 */
3175void MENU_TrackKbdMenuBar( HWND hwnd, UINT wParam, INT vkey)
3176{
3177 UINT uItem = NO_SELECTED_ITEM;
3178 HMENU hTrackMenu;
3179 UINT wFlags = TPM_ENTERIDLEEX | TPM_LEFTALIGN | TPM_LEFTBUTTON;
3180
3181 /* find window that has a menu */
3182
3183 while (GetWindowLongA( hwnd, GWL_STYLE ) & WS_CHILD)
3184 if (!(hwnd = GetParent( hwnd ))) return;
3185
3186 /* check if we have to track a system menu */
3187
3188 hTrackMenu = GetMenu( hwnd );
3189 if (!hTrackMenu || IsIconic(hwnd) || vkey == VK_SPACE )
3190 {
3191 if (!(GetWindowLongA( hwnd, GWL_STYLE ) & WS_SYSMENU)) return;
3192#ifdef __WIN32OS2__
3193 if ((GetWindowLongA( hwnd, GWL_EXSTYLE ) & WS_EX_TOOLWINDOW)) return;
3194#endif
3195 hTrackMenu = get_win_sys_menu( hwnd );
3196 uItem = 0;
3197 wParam |= HTSYSMENU; /* prevent item lookup */
3198 }
3199
3200 if (!IsMenu( hTrackMenu )) return;
3201
3202 MENU_InitTracking( hwnd, hTrackMenu, FALSE, wFlags );
3203
3204 if( vkey && vkey != VK_SPACE )
3205 {
3206 uItem = MENU_FindItemByKey( hwnd, hTrackMenu, vkey, (wParam & HTSYSMENU) );
3207 if( uItem >= (UINT)(-2) )
3208 {
3209#ifndef __WIN32OS2__
3210 //Don't beep when unable to find menu item when alt key pressed
3211 if( uItem == (UINT)(-1) ) MessageBeep(0);
3212#endif
3213 hTrackMenu = 0;
3214 }
3215 }
3216
3217 if( hTrackMenu )
3218 {
3219 MENU_SelectItem( hwnd, hTrackMenu, uItem, TRUE, 0 );
3220
3221 if( uItem == NO_SELECTED_ITEM )
3222 MENU_MoveSelection( hwnd, hTrackMenu, ITEM_NEXT );
3223 else if( vkey )
3224 PostMessageA( hwnd, WM_KEYDOWN, VK_DOWN, 0L );
3225
3226 MENU_TrackMenu( hTrackMenu, wFlags, 0, 0, hwnd, NULL );
3227 }
3228 MENU_ExitTracking( hwnd );
3229}
3230
3231
3232/**********************************************************************
3233 * TrackPopupMenu (USER32.@)
3234 *
3235 * Like the win32 API, the function return the command ID only if the
3236 * flag TPM_RETURNCMD is on.
3237 *
3238 */
3239BOOL WINAPI TrackPopupMenu( HMENU hMenu, UINT wFlags, INT x, INT y,
3240 INT nReserved, HWND hWnd, const RECT *lpRect )
3241{
3242 BOOL ret = FALSE;
3243
3244 MENU_InitTracking(hWnd, hMenu, TRUE, wFlags);
3245
3246 /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
3247 if (!(wFlags & TPM_NONOTIFY))
3248 SendMessageA( hWnd, WM_INITMENUPOPUP, hMenu, 0);
3249
3250 if (MENU_ShowPopup( hWnd, hMenu, 0, x, y, 0, 0 ))
3251 ret = MENU_TrackMenu( hMenu, wFlags | TPM_POPUPMENU, 0, 0, hWnd, lpRect );
3252 MENU_ExitTracking(hWnd);
3253
3254 if( (!(wFlags & TPM_RETURNCMD)) && (ret != FALSE) )
3255 ret = 1;
3256
3257 return ret;
3258}
3259
3260/**********************************************************************
3261 * TrackPopupMenuEx (USER32.@)
3262 */
3263BOOL WINAPI TrackPopupMenuEx( HMENU hMenu, UINT wFlags, INT x, INT y,
3264 HWND hWnd, LPTPMPARAMS lpTpm )
3265{
3266 FIXME("not fully implemented\n" );
3267 return TrackPopupMenu( hMenu, wFlags, x, y, 0, hWnd,
3268 lpTpm ? &lpTpm->rcExclude : NULL );
3269}
3270
3271/***********************************************************************
3272 * PopupMenuWndProc
3273 *
3274 * NOTE: Windows has totally different (and undocumented) popup wndproc.
3275 */
3276static LRESULT WINAPI PopupMenuWndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam )
3277{
3278 TRACE("hwnd=0x%04x msg=0x%04x wp=0x%04x lp=0x%08lx\n",
3279 hwnd, message, wParam, lParam);
3280
3281 switch(message)
3282 {
3283 case WM_CREATE:
3284 {
3285 CREATESTRUCTW *cs = (CREATESTRUCTW*)lParam;
3286 SetWindowLongW( hwnd, 0, (LONG)cs->lpCreateParams );
3287 return 0;
3288 }
3289
3290 case WM_MOUSEACTIVATE: /* We don't want to be activated */
3291 return MA_NOACTIVATE;
3292
3293 case WM_PAINT:
3294 {
3295 PAINTSTRUCT ps;
3296 BeginPaint( hwnd, &ps );
3297 MENU_DrawPopupMenu( hwnd, ps.hdc,
3298 (HMENU)GetWindowLongA( hwnd, 0 ) );
3299 EndPaint( hwnd, &ps );
3300 return 0;
3301 }
3302 case WM_ERASEBKGND:
3303 return 1;
3304
3305 case WM_DESTROY:
3306 /* zero out global pointer in case resident popup window was destroyed. */
3307 if (hwnd == top_popup) top_popup = 0;
3308 break;
3309
3310 case WM_SHOWWINDOW:
3311
3312 if( wParam )
3313 {
3314 if (!GetWindowLongW( hwnd, 0 )) ERR("no menu to display\n");
3315 }
3316 else
3317 SetWindowLongW( hwnd, 0, 0 );
3318 break;
3319
3320 case MM_SETMENUHANDLE:
3321 SetWindowLongW( hwnd, 0, wParam );
3322 break;
3323
3324 case MM_GETMENUHANDLE:
3325 return GetWindowLongW( hwnd, 0 );
3326
3327 default:
3328 return DefWindowProcW( hwnd, message, wParam, lParam );
3329 }
3330 return 0;
3331}
3332
3333
3334/***********************************************************************
3335 * MENU_GetMenuBarHeight
3336 *
3337 * Compute the size of the menu bar height. Used by NC_HandleNCCalcSize().
3338 */
3339UINT MENU_GetMenuBarHeight( HWND hwnd, UINT menubarWidth,
3340 INT orgX, INT orgY )
3341{
3342 HDC hdc;
3343 RECT rectBar;
3344 LPPOPUPMENU lppop;
3345
3346 TRACE("HWND 0x%x, width %d, at (%d, %d).\n",
3347 hwnd, menubarWidth, orgX, orgY );
3348
3349 if (!(lppop = MENU_GetMenu( GetMenu(hwnd) ))) return 0;
3350
3351 hdc = GetDCEx( hwnd, 0, DCX_CACHE | DCX_WINDOW );
3352 SelectObject( hdc, hMenuFont);
3353 SetRect(&rectBar, orgX, orgY, orgX+menubarWidth, orgY+GetSystemMetrics(SM_CYMENU));
3354 MENU_MenuBarCalcSize( hdc, &rectBar, lppop, hwnd );
3355 ReleaseDC( hwnd, hdc );
3356 return lppop->Height;
3357}
3358
3359
3360/*******************************************************************
3361 * ChangeMenuA (USER32.@)
3362 */
3363BOOL WINAPI ChangeMenuA( HMENU hMenu, UINT pos, LPCSTR data,
3364 UINT id, UINT flags )
3365{
3366 TRACE("menu=%08x pos=%d data=%08lx id=%08x flags=%08x\n",
3367 hMenu, pos, (DWORD)data, id, flags );
3368 if (flags & MF_APPEND) return AppendMenuA( hMenu, flags & ~MF_APPEND,
3369 id, data );
3370 if (flags & MF_DELETE) return DeleteMenu(hMenu, pos, flags & ~MF_DELETE);
3371 if (flags & MF_CHANGE) return ModifyMenuA(hMenu, pos, flags & ~MF_CHANGE,
3372 id, data );
3373 if (flags & MF_REMOVE) return RemoveMenu( hMenu,
3374 flags & MF_BYPOSITION ? pos : id,
3375 flags & ~MF_REMOVE );
3376 /* Default: MF_INSERT */
3377 return InsertMenuA( hMenu, pos, flags, id, data );
3378}
3379
3380
3381/*******************************************************************
3382 * ChangeMenuW (USER32.@)
3383 */
3384BOOL WINAPI ChangeMenuW( HMENU hMenu, UINT pos, LPCWSTR data,
3385 UINT id, UINT flags )
3386{
3387 TRACE("menu=%08x pos=%d data=%08lx id=%08x flags=%08x\n",
3388 hMenu, pos, (DWORD)data, id, flags );
3389 if (flags & MF_APPEND) return AppendMenuW( hMenu, flags & ~MF_APPEND,
3390 id, data );
3391 if (flags & MF_DELETE) return DeleteMenu(hMenu, pos, flags & ~MF_DELETE);
3392 if (flags & MF_CHANGE) return ModifyMenuW(hMenu, pos, flags & ~MF_CHANGE,
3393 id, data );
3394 if (flags & MF_REMOVE) return RemoveMenu( hMenu,
3395 flags & MF_BYPOSITION ? pos : id,
3396 flags & ~MF_REMOVE );
3397 /* Default: MF_INSERT */
3398 return InsertMenuW( hMenu, pos, flags, id, data );
3399}
3400
3401
3402/*******************************************************************
3403 * CheckMenuItem (USER32.@)
3404 */
3405DWORD WINAPI CheckMenuItem( HMENU hMenu, UINT id, UINT flags )
3406{
3407 MENUITEM *item;
3408 DWORD ret;
3409
3410 TRACE("menu=%04x id=%04x flags=%04x\n", hMenu, id, flags );
3411 if (!(item = MENU_FindItem( &hMenu, &id, flags ))) return -1;
3412 ret = item->fState & MF_CHECKED;
3413 if (flags & MF_CHECKED) item->fState |= MF_CHECKED;
3414 else item->fState &= ~MF_CHECKED;
3415 return ret;
3416}
3417
3418
3419/**********************************************************************
3420 * EnableMenuItem (USER32.@)
3421 */
3422UINT WINAPI EnableMenuItem( HMENU hMenu, UINT wItemID, UINT wFlags )
3423{
3424 UINT oldflags;
3425 MENUITEM *item;
3426 POPUPMENU *menu;
3427
3428 TRACE("(%04x, %04X, %04X) !\n",
3429 hMenu, wItemID, wFlags);
3430
3431 /* Get the Popupmenu to access the owner menu */
3432 if (!(menu = MENU_GetMenu(hMenu)))
3433 return (UINT)-1;
3434
3435 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags )))
3436 return (UINT)-1;
3437
3438 oldflags = item->fState & (MF_GRAYED | MF_DISABLED);
3439 item->fState ^= (oldflags ^ wFlags) & (MF_GRAYED | MF_DISABLED);
3440
3441 /* In win95 if the close item in the system menu change update the close button */
3442 if (TWEAK_WineLook == WIN95_LOOK)
3443 if((item->wID == SC_CLOSE) && (oldflags != wFlags))
3444 {
3445 if (menu->hSysMenuOwner != 0)
3446 {
3447 POPUPMENU* parentMenu;
3448
3449 /* Get the parent menu to access*/
3450 if (!(parentMenu = MENU_GetMenu(menu->hSysMenuOwner)))
3451 return (UINT)-1;
3452
3453 /* Refresh the frame to reflect the change*/
3454 SetWindowPos(parentMenu->hWnd, 0, 0, 0, 0, 0,
3455 SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER);
3456 }
3457 }
3458
3459 return oldflags;
3460}
3461
3462
3463/*******************************************************************
3464 * GetMenuStringA (USER32.@)
3465 */
3466INT WINAPI GetMenuStringA(
3467 HMENU hMenu, /* [in] menuhandle */
3468 UINT wItemID, /* [in] menu item (dep. on wFlags) */
3469 LPSTR str, /* [out] outbuffer. If NULL, func returns entry length*/
3470 INT nMaxSiz, /* [in] length of buffer. if 0, func returns entry len*/
3471 UINT wFlags /* [in] MF_ flags */
3472) {
3473 MENUITEM *item;
3474
3475 TRACE("menu=%04x item=%04x ptr=%p len=%d flags=%04x\n",
3476 hMenu, wItemID, str, nMaxSiz, wFlags );
3477 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) return 0;
3478 if (!IS_STRING_ITEM(item->fType)) return 0;
3479 if (!str || !nMaxSiz) return strlenW(item->text);
3480 str[0] = '\0';
3481 if (!WideCharToMultiByte( CP_ACP, 0, item->text, -1, str, nMaxSiz, NULL, NULL ))
3482#ifdef __WIN32OS2__
3483 lstrtrunc( str, nMaxSiz );
3484#else
3485 str[nMaxSiz-1] = 0;
3486#endif
3487 TRACE("returning '%s'\n", str );
3488 return strlen(str);
3489}
3490
3491
3492/*******************************************************************
3493 * GetMenuStringW (USER32.@)
3494 */
3495INT WINAPI GetMenuStringW( HMENU hMenu, UINT wItemID,
3496 LPWSTR str, INT nMaxSiz, UINT wFlags )
3497{
3498 MENUITEM *item;
3499
3500 TRACE("menu=%04x item=%04x ptr=%p len=%d flags=%04x\n",
3501 hMenu, wItemID, str, nMaxSiz, wFlags );
3502 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) return 0;
3503 if (!IS_STRING_ITEM(item->fType)) return 0;
3504 if (!str || !nMaxSiz) return strlenW(item->text);
3505 str[0] = '\0';
3506 lstrcpynW( str, item->text, nMaxSiz );
3507 return strlenW(str);
3508}
3509
3510
3511/**********************************************************************
3512 * HiliteMenuItem (USER32.@)
3513 */
3514BOOL WINAPI HiliteMenuItem( HWND hWnd, HMENU hMenu, UINT wItemID,
3515 UINT wHilite )
3516{
3517 LPPOPUPMENU menu;
3518 TRACE("(%04x, %04x, %04x, %04x);\n",
3519 hWnd, hMenu, wItemID, wHilite);
3520 if (!MENU_FindItem( &hMenu, &wItemID, wHilite )) return FALSE;
3521 if (!(menu = MENU_GetMenu(hMenu))) return FALSE;
3522 if (menu->FocusedItem == wItemID) return TRUE;
3523 MENU_HideSubPopups( hWnd, hMenu, FALSE );
3524 MENU_SelectItem( hWnd, hMenu, wItemID, TRUE, 0 );
3525 return TRUE;
3526}
3527
3528
3529/**********************************************************************
3530 * GetMenuState (USER32.@)
3531 */
3532UINT WINAPI GetMenuState( HMENU hMenu, UINT wItemID, UINT wFlags )
3533{
3534 MENUITEM *item;
3535 TRACE("(menu=%04x, id=%04x, flags=%04x);\n",
3536 hMenu, wItemID, wFlags);
3537 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) return -1;
3538 debug_print_menuitem (" item: ", item, "");
3539 if (item->fType & MF_POPUP)
3540 {
3541 POPUPMENU *menu = MENU_GetMenu( item->hSubMenu );
3542 if (!menu) return -1;
3543 else return (menu->nItems << 8) | ((item->fState|item->fType) & 0xff);
3544 }
3545 else
3546 {
3547 /* We used to (from way back then) mask the result to 0xff. */
3548 /* I don't know why and it seems wrong as the documented */
3549 /* return flag MF_SEPARATOR is outside that mask. */
3550 return (item->fType | item->fState);
3551 }
3552}
3553
3554
3555/**********************************************************************
3556 * GetMenuItemCount (USER32.@)
3557 */
3558INT WINAPI GetMenuItemCount( HMENU hMenu )
3559{
3560 LPPOPUPMENU menu = MENU_GetMenu(hMenu);
3561 if (!menu) return -1;
3562 TRACE("(%04x) returning %d\n",
3563 hMenu, menu->nItems );
3564 return menu->nItems;
3565}
3566
3567/**********************************************************************
3568 * GetMenuItemID (USER.264)
3569 */
3570UINT16 WINAPI GetMenuItemID16( HMENU16 hMenu, INT16 nPos )
3571{
3572 return (UINT16) GetMenuItemID (hMenu, nPos);
3573}
3574
3575/**********************************************************************
3576 * GetMenuItemID (USER32.@)
3577 */
3578UINT WINAPI GetMenuItemID( HMENU hMenu, INT nPos )
3579{
3580 MENUITEM * lpmi;
3581
3582 if (!(lpmi = MENU_FindItem(&hMenu,(UINT*)&nPos,MF_BYPOSITION))) return -1;
3583 if (lpmi->fType & MF_POPUP) return -1;
3584 return lpmi->wID;
3585
3586}
3587
3588
3589/*******************************************************************
3590 * InsertMenuW (USER32.@)
3591 */
3592BOOL WINAPI InsertMenuW( HMENU hMenu, UINT pos, UINT flags,
3593 UINT id, LPCWSTR str )
3594{
3595 MENUITEM *item;
3596
3597 if (IS_STRING_ITEM(flags) && str)
3598 TRACE("hMenu %04x, pos %d, flags %08x, "
3599 "id %04x, str %s\n",
3600 hMenu, pos, flags, id, debugstr_w(str) );
3601 else TRACE("hMenu %04x, pos %d, flags %08x, "
3602 "id %04x, str %08lx (not a string)\n",
3603 hMenu, pos, flags, id, (DWORD)str );
3604
3605 if (!(item = MENU_InsertItem( hMenu, pos, flags ))) return FALSE;
3606
3607 if (!(MENU_SetItemData( item, flags, id, str )))
3608 {
3609 RemoveMenu( hMenu, pos, flags );
3610 return FALSE;
3611 }
3612
3613 if (flags & MF_POPUP) /* Set the MF_POPUP flag on the popup-menu */
3614 (MENU_GetMenu((HMENU16)id))->wFlags |= MF_POPUP;
3615
3616 item->hCheckBit = item->hUnCheckBit = 0;
3617 return TRUE;
3618}
3619
3620
3621/*******************************************************************
3622 * InsertMenuA (USER32.@)
3623 */
3624BOOL WINAPI InsertMenuA( HMENU hMenu, UINT pos, UINT flags,
3625 UINT id, LPCSTR str )
3626{
3627 BOOL ret = FALSE;
3628
3629 if (IS_STRING_ITEM(flags) && str)
3630 {
3631 INT len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
3632 LPWSTR newstr = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
3633 if (newstr)
3634 {
3635 MultiByteToWideChar( CP_ACP, 0, str, -1, newstr, len );
3636 ret = InsertMenuW( hMenu, pos, flags, id, newstr );
3637 HeapFree( GetProcessHeap(), 0, newstr );
3638 }
3639 return ret;
3640 }
3641 else return InsertMenuW( hMenu, pos, flags, id, (LPCWSTR)str );
3642}
3643
3644
3645/*******************************************************************
3646 * AppendMenuA (USER32.@)
3647 */
3648BOOL WINAPI AppendMenuA( HMENU hMenu, UINT flags,
3649 UINT id, LPCSTR data )
3650{
3651 return InsertMenuA( hMenu, -1, flags | MF_BYPOSITION, id, data );
3652}
3653
3654
3655/*******************************************************************
3656 * AppendMenuW (USER32.@)
3657 */
3658BOOL WINAPI AppendMenuW( HMENU hMenu, UINT flags,
3659 UINT id, LPCWSTR data )
3660{
3661 return InsertMenuW( hMenu, -1, flags | MF_BYPOSITION, id, data );
3662}
3663
3664
3665/**********************************************************************
3666 * RemoveMenu (USER32.@)
3667 */
3668BOOL WINAPI RemoveMenu( HMENU hMenu, UINT nPos, UINT wFlags )
3669{
3670 LPPOPUPMENU menu;
3671 MENUITEM *item;
3672
3673 TRACE("(menu=%04x pos=%04x flags=%04x)\n",hMenu, nPos, wFlags);
3674 if (!(item = MENU_FindItem( &hMenu, &nPos, wFlags ))) return FALSE;
3675 if (!(menu = MENU_GetMenu(hMenu))) return FALSE;
3676
3677 /* Remove item */
3678
3679 MENU_FreeItemData( item );
3680
3681 if (--menu->nItems == 0)
3682 {
3683 HeapFree( GetProcessHeap(), 0, menu->items );
3684 menu->items = NULL;
3685 }
3686 else
3687 {
3688 while(nPos < menu->nItems)
3689 {
3690 *item = *(item+1);
3691 item++;
3692 nPos++;
3693 }
3694 menu->items = HeapReAlloc( GetProcessHeap(), 0, menu->items,
3695 menu->nItems * sizeof(MENUITEM) );
3696 }
3697 return TRUE;
3698}
3699
3700
3701/**********************************************************************
3702 * DeleteMenu (USER32.@)
3703 */
3704BOOL WINAPI DeleteMenu( HMENU hMenu, UINT nPos, UINT wFlags )
3705{
3706 MENUITEM *item = MENU_FindItem( &hMenu, &nPos, wFlags );
3707 if (!item) return FALSE;
3708 if (item->fType & MF_POPUP) DestroyMenu( item->hSubMenu );
3709 /* nPos is now the position of the item */
3710 RemoveMenu( hMenu, nPos, wFlags | MF_BYPOSITION );
3711 return TRUE;
3712}
3713
3714
3715/*******************************************************************
3716 * ModifyMenuW (USER32.@)
3717 */
3718BOOL WINAPI ModifyMenuW( HMENU hMenu, UINT pos, UINT flags,
3719 UINT id, LPCWSTR str )
3720{
3721 MENUITEM *item;
3722
3723 if (IS_STRING_ITEM(flags))
3724 {
3725 TRACE("%04x %d %04x %04x %s\n",
3726 hMenu, pos, flags, id, debugstr_w(str) );
3727 if (!str) return FALSE;
3728 }
3729 else
3730 {
3731 TRACE("%04x %d %04x %04x %08lx\n",
3732 hMenu, pos, flags, id, (DWORD)str );
3733 }
3734
3735 if (!(item = MENU_FindItem( &hMenu, &pos, flags ))) return FALSE;
3736 return MENU_SetItemData( item, flags, id, str );
3737}
3738
3739
3740/*******************************************************************
3741 * ModifyMenuA (USER32.@)
3742 */
3743BOOL WINAPI ModifyMenuA( HMENU hMenu, UINT pos, UINT flags,
3744 UINT id, LPCSTR str )
3745{
3746 BOOL ret = FALSE;
3747
3748 if (IS_STRING_ITEM(flags) && str)
3749 {
3750 INT len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
3751 LPWSTR newstr = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
3752 if (newstr)
3753 {
3754 MultiByteToWideChar( CP_ACP, 0, str, -1, newstr, len );
3755 ret = ModifyMenuW( hMenu, pos, flags, id, newstr );
3756 HeapFree( GetProcessHeap(), 0, newstr );
3757 }
3758 return ret;
3759 }
3760 else return ModifyMenuW( hMenu, pos, flags, id, (LPCWSTR)str );
3761}
3762
3763
3764/**********************************************************************
3765 * CreatePopupMenu (USER32.@)
3766 */
3767HMENU WINAPI CreatePopupMenu(void)
3768{
3769 HMENU hmenu;
3770 POPUPMENU *menu;
3771
3772 if (!(hmenu = CreateMenu())) return 0;
3773 menu = MENU_GetMenu( hmenu );
3774 menu->wFlags |= MF_POPUP;
3775 menu->bTimeToHide = FALSE;
3776 return hmenu;
3777}
3778
3779
3780/**********************************************************************
3781 * GetMenuCheckMarkDimensions (USER.417)
3782 * GetMenuCheckMarkDimensions (USER32.@)
3783 */
3784DWORD WINAPI GetMenuCheckMarkDimensions(void)
3785{
3786 return MAKELONG( GetSystemMetrics(SM_CXMENUCHECK), GetSystemMetrics(SM_CYMENUCHECK) );
3787}
3788
3789
3790/**********************************************************************
3791 * SetMenuItemBitmaps (USER32.@)
3792 */
3793BOOL WINAPI SetMenuItemBitmaps( HMENU hMenu, UINT nPos, UINT wFlags,
3794 HBITMAP hNewUnCheck, HBITMAP hNewCheck)
3795{
3796 MENUITEM *item;
3797 TRACE("(%04x, %04x, %04x, %04x, %04x)\n",
3798 hMenu, nPos, wFlags, hNewCheck, hNewUnCheck);
3799 if (!(item = MENU_FindItem( &hMenu, &nPos, wFlags ))) return FALSE;
3800
3801 if (!hNewCheck && !hNewUnCheck)
3802 {
3803 item->fState &= ~MF_USECHECKBITMAPS;
3804 }
3805 else /* Install new bitmaps */
3806 {
3807 item->hCheckBit = hNewCheck;
3808 item->hUnCheckBit = hNewUnCheck;
3809 item->fState |= MF_USECHECKBITMAPS;
3810 }
3811 return TRUE;
3812}
3813
3814
3815/**********************************************************************
3816 * CreateMenu (USER32.@)
3817 */
3818HMENU WINAPI CreateMenu(void)
3819{
3820 HMENU hMenu;
3821 LPPOPUPMENU menu;
3822
3823#ifdef __WIN32OS2__
3824 if (!(menu = (LPPOPUPMENU)HeapAlloc(GetProcessHeap(),0,sizeof(POPUPMENU)))) return 0;
3825 if(ObjAllocateHandle(&hMenu, (DWORD)menu, HNDL_MENU) == FALSE) return 0;
3826#else
3827 if (!(hMenu = USER_HEAP_ALLOC( sizeof(POPUPMENU) ))) return 0;
3828 menu = (LPPOPUPMENU) USER_HEAP_LIN_ADDR(hMenu);
3829#endif
3830 ZeroMemory(menu, sizeof(POPUPMENU));
3831 menu->wMagic = MENU_MAGIC;
3832 menu->FocusedItem = NO_SELECTED_ITEM;
3833 menu->bTimeToHide = FALSE;
3834
3835 TRACE("return %04x\n", hMenu );
3836
3837 return hMenu;
3838}
3839
3840
3841/**********************************************************************
3842 * DestroyMenu (USER32.@)
3843 */
3844BOOL WINAPI DestroyMenu( HMENU hMenu )
3845{
3846 TRACE("(%04x)\n", hMenu);
3847
3848 /* Silently ignore attempts to destroy default system popup */
3849
3850 if (hMenu && hMenu != MENU_DefSysPopup)
3851 {
3852 LPPOPUPMENU lppop = MENU_GetMenu(hMenu);
3853
3854 if (!lppop) return FALSE;
3855
3856 lppop->wMagic = 0; /* Mark it as destroyed */
3857
3858 if ((lppop->wFlags & MF_POPUP) && lppop->hWnd)
3859 {
3860 DestroyWindow( lppop->hWnd );
3861 lppop->hWnd = 0;
3862 }
3863
3864 if (lppop->items) /* recursively destroy submenus */
3865 {
3866 int i;
3867 MENUITEM *item = lppop->items;
3868 for (i = lppop->nItems; i > 0; i--, item++)
3869 {
3870 if (item->fType & MF_POPUP) DestroyMenu(item->hSubMenu);
3871 MENU_FreeItemData( item );
3872 }
3873 HeapFree( GetProcessHeap(), 0, lppop->items );
3874 }
3875#ifdef __WIN32OS2__
3876 HeapFree(GetProcessHeap(),0,(LPVOID)lppop);
3877 ObjDeleteHandle(hMenu, HNDL_MENU);
3878#else
3879 USER_HEAP_FREE( hMenu );
3880#endif
3881 }
3882 return (hMenu != MENU_DefSysPopup);
3883}
3884
3885
3886/**********************************************************************
3887 * GetSystemMenu (USER32.@)
3888 */
3889HMENU WINAPI GetSystemMenu( HWND hWnd, BOOL bRevert )
3890{
3891#ifdef __WIN32OS2__
3892 HMENU retvalue = 0;
3893
3894 if(IsWindow(hWnd))
3895 {
3896 HMENU hSysMenu = getSysMenu(hWnd);
3897 if(hSysMenu)
3898 {
3899 if( bRevert )
3900 {
3901 DestroyMenu(hSysMenu);
3902 hSysMenu = 0;
3903 setSysMenu(hWnd,hSysMenu);
3904 }
3905 else
3906 {
3907 POPUPMENU *menu = MENU_GetMenu(hSysMenu);
3908 if(menu)
3909 {
3910 if( menu->nItems > 0 && menu->items[0].hSubMenu == MENU_DefSysPopup )
3911 menu->items[0].hSubMenu = MENU_CopySysPopup();
3912 }
3913 else
3914 {
3915 //WARN("Current sys-menu (%04x) of wnd %04x is broken\n",
3916 // wndPtr->hSysMenu, hWnd);
3917 hSysMenu = 0;
3918 setSysMenu(hWnd,hSysMenu);
3919 }
3920 }
3921 }
3922
3923 if(!hSysMenu && (GetWindowLongA(hWnd,GWL_STYLE) & WS_SYSMENU) )
3924 {
3925 hSysMenu = MENU_GetSysMenu( hWnd, (HMENU)(-1) );
3926 setSysMenu(hWnd,hSysMenu);
3927 }
3928
3929 if( hSysMenu )
3930 {
3931 POPUPMENU *menu;
3932 retvalue = GetSubMenu(hSysMenu, 0);
3933
3934 /* Store the dummy sysmenu handle to facilitate the refresh */
3935 /* of the close button if the SC_CLOSE item change */
3936 menu = MENU_GetMenu(retvalue);
3937 if (menu)
3938 menu->hSysMenuOwner = hSysMenu;
3939 }
3940 }
3941 return bRevert ? 0 : retvalue;
3942#else
3943 WND *wndPtr = WIN_FindWndPtr( hWnd );
3944 HMENU retvalue = 0;
3945
3946 if (wndPtr)
3947 {
3948 if( wndPtr->hSysMenu )
3949 {
3950 if( bRevert )
3951 {
3952 DestroyMenu(wndPtr->hSysMenu);
3953 wndPtr->hSysMenu = 0;
3954 }
3955 else
3956 {
3957 POPUPMENU *menu = MENU_GetMenu( wndPtr->hSysMenu );
3958 if( menu )
3959 {
3960 if( menu->nItems > 0 && menu->items[0].hSubMenu == MENU_DefSysPopup )
3961 menu->items[0].hSubMenu = MENU_CopySysPopup();
3962 }
3963 else
3964 {
3965 WARN("Current sys-menu (%04x) of wnd %04x is broken\n",
3966 wndPtr->hSysMenu, hWnd);
3967 wndPtr->hSysMenu = 0;
3968 }
3969 }
3970 }
3971
3972 if(!wndPtr->hSysMenu && (wndPtr->dwStyle & WS_SYSMENU) )
3973 wndPtr->hSysMenu = MENU_GetSysMenu( hWnd, (HMENU)(-1) );
3974
3975 if( wndPtr->hSysMenu )
3976 {
3977 POPUPMENU *menu;
3978 retvalue = GetSubMenu(wndPtr->hSysMenu, 0);
3979
3980 /* Store the dummy sysmenu handle to facilitate the refresh */
3981 /* of the close button if the SC_CLOSE item change */
3982 menu = MENU_GetMenu(retvalue);
3983 if ( menu )
3984 menu->hSysMenuOwner = wndPtr->hSysMenu;
3985 }
3986 WIN_ReleaseWndPtr(wndPtr);
3987 }
3988 return bRevert ? 0 : retvalue;
3989#endif
3990}
3991
3992
3993/*******************************************************************
3994 * SetSystemMenu (USER32.@)
3995 */
3996BOOL WINAPI SetSystemMenu( HWND hwnd, HMENU hMenu )
3997{
3998#ifdef __WIN32OS2__
3999 HMENU hSysMenu;
4000
4001 if(!IsWindow(hwnd)) return FALSE;
4002
4003 hSysMenu = getSysMenu(hwnd);
4004 if(hSysMenu) DestroyMenu(hSysMenu);
4005
4006 setSysMenu(hwnd, MENU_GetSysMenu( hwnd, hMenu ));
4007 return TRUE;
4008#else
4009 WND *wndPtr = WIN_FindWndPtr(hwnd);
4010
4011 if (wndPtr)
4012 {
4013 if (wndPtr->hSysMenu) DestroyMenu( wndPtr->hSysMenu );
4014 wndPtr->hSysMenu = MENU_GetSysMenu( hwnd, hMenu );
4015 WIN_ReleaseWndPtr(wndPtr);
4016 return TRUE;
4017 }
4018 return FALSE;
4019#endif
4020}
4021
4022
4023/**********************************************************************
4024 * GetMenu (USER32.@)
4025 */
4026HMENU WINAPI GetMenu( HWND hWnd )
4027{
4028 HMENU retvalue = (HMENU)GetWindowLongA( hWnd, GWL_ID );
4029#ifdef __WIN32OS2__
4030 if(MENU_GetMenu(retvalue) != NULL) {
4031 return retvalue;
4032 }
4033 dprintf(("WARNING: Invalid menu %x", retvalue));
4034 return 0;
4035#else
4036 TRACE("for %04x returning %04x\n", hWnd, retvalue);
4037 return retvalue;
4038#endif
4039}
4040
4041
4042/**********************************************************************
4043 * SetMenu (USER32.@)
4044 */
4045BOOL WINAPI SetMenu( HWND hWnd, HMENU hMenu )
4046{
4047 TRACE("(%04x, %04x);\n", hWnd, hMenu);
4048
4049 if (hMenu && !IsMenu(hMenu))
4050 {
4051 WARN("hMenu %x is not a menu handle\n", hMenu);
4052 return FALSE;
4053 }
4054 if (GetWindowLongA( hWnd, GWL_STYLE ) & WS_CHILD) return FALSE;
4055
4056 hWnd = WIN_GetFullHandle( hWnd );
4057 if (GetCapture() == hWnd) ReleaseCapture();
4058
4059 if (hMenu != 0)
4060 {
4061 LPPOPUPMENU lpmenu;
4062
4063 if (!(lpmenu = MENU_GetMenu(hMenu))) return FALSE;
4064
4065 lpmenu->hWnd = hWnd;
4066 lpmenu->Height = 0; /* Make sure we recalculate the size */
4067 }
4068 SetWindowLongA( hWnd, GWL_ID, hMenu );
4069
4070 if (IsWindowVisible(hWnd))
4071 SetWindowPos( hWnd, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE |
4072 SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED );
4073 return TRUE;
4074}
4075
4076
4077
4078/**********************************************************************
4079 * GetSubMenu (USER32.@)
4080 */
4081HMENU WINAPI GetSubMenu( HMENU hMenu, INT nPos )
4082{
4083 MENUITEM * lpmi;
4084
4085#ifdef __WIN32OS2__
4086 if (!(lpmi = MENU_FindItem(&hMenu,(UINT*)&nPos,MF_BYPOSITION))) {
4087 SetLastError(ERROR_INVALID_HANDLE);
4088 return 0;
4089 }
4090#else
4091 if (!(lpmi = MENU_FindItem(&hMenu,&nPos,MF_BYPOSITION))) return 0;
4092#endif
4093 if (!(lpmi->fType & MF_POPUP)) return 0;
4094 return lpmi->hSubMenu;
4095}
4096
4097
4098/**********************************************************************
4099 * DrawMenuBar (USER32.@)
4100 */
4101BOOL WINAPI DrawMenuBar( HWND hWnd )
4102{
4103 LPPOPUPMENU lppop;
4104 HMENU hMenu = GetMenu(hWnd);
4105
4106 if (GetWindowLongA( hWnd, GWL_STYLE ) & WS_CHILD) return FALSE;
4107 if (!hMenu || !(lppop = MENU_GetMenu( hMenu ))) return FALSE;
4108
4109 lppop->Height = 0; /* Make sure we call MENU_MenuBarCalcSize */
4110 lppop->hwndOwner = hWnd;
4111 SetWindowPos( hWnd, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE |
4112 SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED );
4113 return TRUE;
4114}
4115
4116/***********************************************************************
4117 * DrawMenuBarTemp (USER32.@)
4118 *
4119 * UNDOCUMENTED !!
4120 *
4121 * called by W98SE desk.cpl Control Panel Applet
4122 *
4123 * Not 100% sure about the param names, but close.
4124 */
4125DWORD WINAPI DrawMenuBarTemp(HWND someHWND, HDC someHDC, LPRECT someRECT, HMENU someHMENU, HFONT someFONT)
4126{
4127 FIXME("(0x%08x, 0x%08x, %p, 0x%08x, 0x%08x): stub\n", someHWND, someHDC, someRECT, someHMENU, someFONT);
4128 return 0;
4129}
4130
4131/***********************************************************************
4132 * EndMenu (USER.187)
4133 * EndMenu (USER32.@)
4134 */
4135void WINAPI EndMenu(void)
4136{
4137 /* if we are in the menu code, and it is active */
4138 if (!fEndMenu && top_popup)
4139 {
4140 /* terminate the menu handling code */
4141 fEndMenu = TRUE;
4142
4143 /* needs to be posted to wakeup the internal menu handler */
4144 /* which will now terminate the menu, in the event that */
4145 /* the main window was minimized, or lost focus, so we */
4146 /* don't end up with an orphaned menu */
4147 PostMessageA( top_popup, WM_CANCELMODE, 0, 0);
4148 }
4149}
4150
4151
4152/***********************************************************************
4153 * LookupMenuHandle (USER.217)
4154 */
4155HMENU16 WINAPI LookupMenuHandle16( HMENU16 hmenu, INT16 id )
4156{
4157 HMENU hmenu32 = hmenu;
4158 UINT id32 = id;
4159 if (!MENU_FindItem( &hmenu32, &id32, MF_BYCOMMAND )) return 0;
4160 else return hmenu32;
4161}
4162
4163
4164#ifndef __WIN32OS2__
4165/**********************************************************************
4166 * LoadMenu (USER.150)
4167 */
4168HMENU16 WINAPI LoadMenu16( HINSTANCE16 instance, LPCSTR name )
4169{
4170 HRSRC16 hRsrc;
4171 HGLOBAL16 handle;
4172 HMENU16 hMenu;
4173
4174 TRACE("(%04x,%s)\n", instance, debugres_a(name) );
4175
4176 if (HIWORD(name))
4177 {
4178 if (name[0] == '#') name = (LPCSTR)atoi( name + 1 );
4179 }
4180
4181 if (!name) return 0;
4182
4183 /* check for Win32 module */
4184 if (HIWORD(instance)) return LoadMenuA( instance, name );
4185 instance = GetExePtr( instance );
4186
4187 if (!(hRsrc = FindResource16( instance, name, RT_MENUA ))) return 0;
4188 if (!(handle = LoadResource16( instance, hRsrc ))) return 0;
4189 hMenu = LoadMenuIndirect16(LockResource16(handle));
4190 FreeResource16( handle );
4191 return hMenu;
4192}
4193#endif
4194
4195/*****************************************************************
4196 * LoadMenuA (USER32.@)
4197 */
4198HMENU WINAPI LoadMenuA( HINSTANCE instance, LPCSTR name )
4199{
4200 HRSRC hrsrc = FindResourceA( instance, name, RT_MENUA );
4201 if (!hrsrc) return 0;
4202 return LoadMenuIndirectA( (LPCVOID)LoadResource( instance, hrsrc ));
4203}
4204
4205
4206/*****************************************************************
4207 * LoadMenuW (USER32.@)
4208 */
4209HMENU WINAPI LoadMenuW( HINSTANCE instance, LPCWSTR name )
4210{
4211 HRSRC hrsrc = FindResourceW( instance, name, RT_MENUW );
4212 if (!hrsrc) return 0;
4213 return LoadMenuIndirectW( (LPCVOID)LoadResource( instance, hrsrc ));
4214}
4215
4216
4217/**********************************************************************
4218 * LoadMenuIndirect (USER.220)
4219 */
4220HMENU16 WINAPI LoadMenuIndirect16( LPCVOID template )
4221{
4222 HMENU16 hMenu;
4223 WORD version, offset;
4224 LPCSTR p = (LPCSTR)template;
4225
4226 TRACE("(%p)\n", template );
4227 version = GET_WORD(p);
4228 p += sizeof(WORD);
4229 if (version)
4230 {
4231 WARN("version must be 0 for Win16\n" );
4232 return 0;
4233 }
4234 offset = GET_WORD(p);
4235 p += sizeof(WORD) + offset;
4236 if (!(hMenu = CreateMenu())) return 0;
4237 if (!MENU_ParseResource( p, hMenu, FALSE ))
4238 {
4239 DestroyMenu( hMenu );
4240 return 0;
4241 }
4242 return hMenu;
4243}
4244
4245
4246/**********************************************************************
4247 * LoadMenuIndirectA (USER32.@)
4248 */
4249HMENU WINAPI LoadMenuIndirectA( LPCVOID template )
4250{
4251 HMENU16 hMenu;
4252 WORD version, offset;
4253 LPCSTR p = (LPCSTR)template;
4254
4255 TRACE("%p\n", template );
4256 version = GET_WORD(p);
4257 p += sizeof(WORD);
4258 switch (version)
4259 {
4260 case 0:
4261 offset = GET_WORD(p);
4262 p += sizeof(WORD) + offset;
4263 if (!(hMenu = CreateMenu())) return 0;
4264 if (!MENU_ParseResource( p, hMenu, TRUE ))
4265 {
4266 DestroyMenu( hMenu );
4267 return 0;
4268 }
4269 return hMenu;
4270 case 1:
4271 offset = GET_WORD(p);
4272 p += sizeof(WORD) + offset;
4273 if (!(hMenu = CreateMenu())) return 0;
4274 if (!MENUEX_ParseResource( p, hMenu))
4275 {
4276 DestroyMenu( hMenu );
4277 return 0;
4278 }
4279 return hMenu;
4280 default:
4281 ERR("version %d not supported.\n", version);
4282 return 0;
4283 }
4284}
4285
4286
4287/**********************************************************************
4288 * LoadMenuIndirectW (USER32.@)
4289 */
4290HMENU WINAPI LoadMenuIndirectW( LPCVOID template )
4291{
4292 /* FIXME: is there anything different between A and W? */
4293 return LoadMenuIndirectA( template );
4294}
4295
4296
4297/**********************************************************************
4298 * IsMenu (USER32.@)
4299 */
4300BOOL WINAPI IsMenu(HMENU hmenu)
4301{
4302 LPPOPUPMENU menu = MENU_GetMenu(hmenu);
4303 return menu != NULL;
4304}
4305
4306/**********************************************************************
4307 * GetMenuItemInfo_common
4308 */
4309
4310static BOOL GetMenuItemInfo_common ( HMENU hmenu, UINT item, BOOL bypos,
4311 LPMENUITEMINFOW lpmii, BOOL unicode)
4312{
4313 MENUITEM *menu = MENU_FindItem (&hmenu, &item, bypos? MF_BYPOSITION : 0);
4314
4315 debug_print_menuitem("GetMenuItemInfo_common: ", menu, "");
4316
4317 if (!menu)
4318 return FALSE;
4319
4320 if (lpmii->fMask & MIIM_TYPE) {
4321 lpmii->fType = menu->fType;
4322 switch (MENU_ITEM_TYPE(menu->fType)) {
4323 case MF_STRING:
4324 break; /* will be done below */
4325 case MF_OWNERDRAW:
4326 case MF_BITMAP:
4327 lpmii->dwTypeData = menu->text;
4328 /* fall through */
4329 default:
4330 lpmii->cch = 0;
4331 }
4332 }
4333
4334 /* copy the text string */
4335 if ((lpmii->fMask & (MIIM_TYPE|MIIM_STRING)) &&
4336 (MENU_ITEM_TYPE(menu->fType) == MF_STRING) && menu->text)
4337 {
4338 int len;
4339 if (unicode)
4340 {
4341 len = strlenW(menu->text);
4342 if(lpmii->dwTypeData && lpmii->cch)
4343 lstrcpynW(lpmii->dwTypeData, menu->text, lpmii->cch);
4344 }
4345 else
4346 {
4347 len = WideCharToMultiByte( CP_ACP, 0, menu->text, -1, NULL, 0, NULL, NULL );
4348 if(lpmii->dwTypeData && lpmii->cch)
4349 if (!WideCharToMultiByte( CP_ACP, 0, menu->text, -1,
4350 (LPSTR)lpmii->dwTypeData, lpmii->cch, NULL, NULL ))
4351#ifdef __WIN32OS2__
4352 lstrtrunc((LPSTR)lpmii->dwTypeData, lpmii->cch );
4353#else
4354 ((LPSTR)lpmii->dwTypeData)[lpmii->cch-1] = 0;
4355#endif
4356 }
4357 /* if we've copied a substring we return its length */
4358 if(lpmii->dwTypeData && lpmii->cch)
4359 {
4360 if (lpmii->cch <= len) lpmii->cch--;
4361 }
4362 else /* return length of string */
4363 lpmii->cch = len;
4364 }
4365
4366 if (lpmii->fMask & MIIM_FTYPE)
4367 lpmii->fType = menu->fType;
4368
4369 if (lpmii->fMask & MIIM_BITMAP)
4370 lpmii->hbmpItem = menu->hbmpItem;
4371
4372 if (lpmii->fMask & MIIM_STATE)
4373 lpmii->fState = menu->fState;
4374
4375 if (lpmii->fMask & MIIM_ID)
4376 lpmii->wID = menu->wID;
4377
4378 if (lpmii->fMask & MIIM_SUBMENU)
4379 lpmii->hSubMenu = menu->hSubMenu;
4380
4381 if (lpmii->fMask & MIIM_CHECKMARKS) {
4382 lpmii->hbmpChecked = menu->hCheckBit;
4383 lpmii->hbmpUnchecked = menu->hUnCheckBit;
4384 }
4385 if (lpmii->fMask & MIIM_DATA)
4386 lpmii->dwItemData = menu->dwItemData;
4387
4388 return TRUE;
4389}
4390
4391/**********************************************************************
4392 * GetMenuItemInfoA (USER32.@)
4393 */
4394BOOL WINAPI GetMenuItemInfoA( HMENU hmenu, UINT item, BOOL bypos,
4395 LPMENUITEMINFOA lpmii)
4396{
4397 return GetMenuItemInfo_common (hmenu, item, bypos,
4398 (LPMENUITEMINFOW)lpmii, FALSE);
4399}
4400
4401/**********************************************************************
4402 * GetMenuItemInfoW (USER32.@)
4403 */
4404BOOL WINAPI GetMenuItemInfoW( HMENU hmenu, UINT item, BOOL bypos,
4405 LPMENUITEMINFOW lpmii)
4406{
4407 return GetMenuItemInfo_common (hmenu, item, bypos,
4408 lpmii, TRUE);
4409}
4410
4411
4412/* set a menu item text from a ASCII or Unicode string */
4413inline static void set_menu_item_text( MENUITEM *menu, LPCWSTR text, BOOL unicode )
4414{
4415 if (!text)
4416 {
4417 menu->text = NULL;
4418 menu->fType |= MF_SEPARATOR;
4419 }
4420 else if (unicode)
4421 {
4422 if ((menu->text = HeapAlloc( GetProcessHeap(), 0, (strlenW(text)+1) * sizeof(WCHAR) )) != NULL)
4423 strcpyW( menu->text, text );
4424 }
4425 else
4426 {
4427 LPCSTR str = (LPCSTR)text;
4428 int len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
4429 if ((menu->text = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) )) != NULL)
4430 MultiByteToWideChar( CP_ACP, 0, str, -1, menu->text, len );
4431 }
4432}
4433
4434
4435/**********************************************************************
4436 * SetMenuItemInfo_common
4437 */
4438
4439static BOOL SetMenuItemInfo_common(MENUITEM * menu,
4440 const MENUITEMINFOW *lpmii,
4441 BOOL unicode)
4442{
4443 if (!menu) return FALSE;
4444
4445 debug_print_menuitem("MENU_SetItemInfo_common from: ", menu, "");
4446
4447 if (lpmii->fMask & MIIM_TYPE ) {
4448 /* Get rid of old string. */
4449 if ( IS_STRING_ITEM(menu->fType) && menu->text) {
4450 HeapFree(GetProcessHeap(), 0, menu->text);
4451 menu->text = NULL;
4452 }
4453
4454 /* make only MENU_ITEM_TYPE bits in menu->fType equal lpmii->fType */
4455 menu->fType &= ~MENU_ITEM_TYPE(menu->fType);
4456 menu->fType |= MENU_ITEM_TYPE(lpmii->fType);
4457
4458 menu->text = lpmii->dwTypeData;
4459
4460 if (IS_STRING_ITEM(menu->fType))
4461 set_menu_item_text( menu, lpmii->dwTypeData, unicode );
4462 }
4463
4464 if (lpmii->fMask & MIIM_FTYPE ) {
4465 /* free the string when the type is changing */
4466 if ( (!IS_STRING_ITEM(lpmii->fType)) && IS_STRING_ITEM(menu->fType) && menu->text) {
4467 HeapFree(GetProcessHeap(), 0, menu->text);
4468 menu->text = NULL;
4469 }
4470 menu->fType &= ~MENU_ITEM_TYPE(menu->fType);
4471 menu->fType |= MENU_ITEM_TYPE(lpmii->fType);
4472 if ( IS_STRING_ITEM(menu->fType) && !menu->text )
4473 menu->fType |= MF_SEPARATOR;
4474 }
4475
4476 if (lpmii->fMask & MIIM_STRING ) {
4477 /* free the string when used */
4478 if ( IS_STRING_ITEM(menu->fType) && menu->text) {
4479 HeapFree(GetProcessHeap(), 0, menu->text);
4480 set_menu_item_text( menu, lpmii->dwTypeData, unicode );
4481 }
4482 }
4483
4484 if (lpmii->fMask & MIIM_STATE)
4485 {
4486 /* FIXME: MFS_DEFAULT do we have to reset the other menu items? */
4487 menu->fState = lpmii->fState;
4488 }
4489
4490 if (lpmii->fMask & MIIM_ID)
4491 menu->wID = lpmii->wID;
4492
4493 if (lpmii->fMask & MIIM_SUBMENU) {
4494 menu->hSubMenu = lpmii->hSubMenu;
4495 if (menu->hSubMenu) {
4496 POPUPMENU *subMenu = MENU_GetMenu((UINT16)menu->hSubMenu);
4497 if (subMenu) {
4498 subMenu->wFlags |= MF_POPUP;
4499 menu->fType |= MF_POPUP;
4500 }
4501 else
4502 /* FIXME: Return an error ? */
4503 menu->fType &= ~MF_POPUP;
4504 }
4505 else
4506 menu->fType &= ~MF_POPUP;
4507 }
4508
4509 if (lpmii->fMask & MIIM_CHECKMARKS)
4510 {
4511 if (lpmii->fType & MFT_RADIOCHECK)
4512 menu->fType |= MFT_RADIOCHECK;
4513
4514 menu->hCheckBit = lpmii->hbmpChecked;
4515 menu->hUnCheckBit = lpmii->hbmpUnchecked;
4516 }
4517 if (lpmii->fMask & MIIM_DATA)
4518 menu->dwItemData = lpmii->dwItemData;
4519
4520 debug_print_menuitem("SetMenuItemInfo_common to : ", menu, "");
4521 return TRUE;
4522}
4523
4524/**********************************************************************
4525 * SetMenuItemInfoA (USER32.@)
4526 */
4527BOOL WINAPI SetMenuItemInfoA(HMENU hmenu, UINT item, BOOL bypos,
4528 const MENUITEMINFOA *lpmii)
4529{
4530 if ((lpmii->fType & (MF_HILITE|MF_POPUP)) || (lpmii->fState)) {
4531 /* QuickTime does pass invalid data into SetMenuItemInfo.
4532 * do some of the checks Windows does.
4533 */
4534 WARN("Bad masks for type (0x%08x) or state (0x%08x)\n",
4535 lpmii->fType,lpmii->fState );
4536 return FALSE;
4537 }
4538
4539 // In the latest Win versions (2000+?) MIIM_TYPE is superceded by separate
4540 // MIIM_BITMAP, MIIM_FTYPE, and MIIM_STRING flags. However, the old Wine
4541 // code seems to be not aware of that. Fix it on our own here.
4542 // NOTE: This should go away once the new Wine is merged in.
4543 MENUITEMINFOA mii;
4544 if (lpmii->fMask & (MIIM_FTYPE | MIIM_STRING) == MIIM_FTYPE | MIIM_STRING)
4545 {
4546 mii = *lpmii;
4547 lpmii = &mii;
4548 mii.fMask |= MIIM_TYPE;
4549 }
4550
4551 return SetMenuItemInfo_common(MENU_FindItem(&hmenu, &item, bypos? MF_BYPOSITION : 0),
4552 (const MENUITEMINFOW *)lpmii, FALSE);
4553}
4554
4555/**********************************************************************
4556 * SetMenuItemInfoW (USER32.@)
4557 */
4558BOOL WINAPI SetMenuItemInfoW(HMENU hmenu, UINT item, BOOL bypos,
4559 const MENUITEMINFOW *lpmii)
4560{
4561 // In the latest Win versions (2000+?) MIIM_TYPE is superceded by separate
4562 // MIIM_BITMAP, MIIM_FTYPE, and MIIM_STRING flags. However, the old Wine
4563 // code seems to be not aware of that. Fix it on our own here.
4564 // NOTE: This should go away once the new Wine is merged in.
4565 MENUITEMINFOW mii;
4566 if (lpmii->fMask & (MIIM_FTYPE | MIIM_STRING) == MIIM_FTYPE | MIIM_STRING)
4567 {
4568 mii = *lpmii;
4569 lpmii = &mii;
4570 mii.fMask |= MIIM_TYPE;
4571 }
4572
4573 return SetMenuItemInfo_common(MENU_FindItem(&hmenu, &item, bypos? MF_BYPOSITION : 0),
4574 lpmii, TRUE);
4575}
4576
4577/**********************************************************************
4578 * SetMenuDefaultItem (USER32.@)
4579 *
4580 */
4581BOOL WINAPI SetMenuDefaultItem(HMENU hmenu, UINT uItem, UINT bypos)
4582{
4583 UINT i;
4584 POPUPMENU *menu;
4585 MENUITEM *item;
4586
4587 TRACE("(0x%x,%d,%d)\n", hmenu, uItem, bypos);
4588
4589 if (!(menu = MENU_GetMenu(hmenu))) return FALSE;
4590
4591 /* reset all default-item flags */
4592 item = menu->items;
4593 for (i = 0; i < menu->nItems; i++, item++)
4594 {
4595 item->fState &= ~MFS_DEFAULT;
4596 }
4597
4598 /* no default item */
4599 if ( -1 == uItem)
4600 {
4601 return TRUE;
4602 }
4603
4604 item = menu->items;
4605 if ( bypos )
4606 {
4607 if ( uItem >= menu->nItems ) return FALSE;
4608 item[uItem].fState |= MFS_DEFAULT;
4609 return TRUE;
4610 }
4611 else
4612 {
4613 for (i = 0; i < menu->nItems; i++, item++)
4614 {
4615 if (item->wID == uItem)
4616 {
4617 item->fState |= MFS_DEFAULT;
4618 return TRUE;
4619 }
4620 }
4621
4622 }
4623 return FALSE;
4624}
4625
4626/**********************************************************************
4627 * GetMenuDefaultItem (USER32.@)
4628 */
4629UINT WINAPI GetMenuDefaultItem(HMENU hmenu, UINT bypos, UINT flags)
4630{
4631 POPUPMENU *menu;
4632 MENUITEM * item;
4633 UINT i = 0;
4634
4635 TRACE("(0x%x,%d,%d)\n", hmenu, bypos, flags);
4636
4637 if (!(menu = MENU_GetMenu(hmenu))) return -1;
4638
4639 /* find default item */
4640 item = menu->items;
4641
4642 /* empty menu */
4643 if (! item) return -1;
4644
4645 while ( !( item->fState & MFS_DEFAULT ) )
4646 {
4647 i++; item++;
4648 if (i >= menu->nItems ) return -1;
4649 }
4650
4651 /* default: don't return disabled items */
4652 if ( (!(GMDI_USEDISABLED & flags)) && (item->fState & MFS_DISABLED )) return -1;
4653
4654 /* search rekursiv when needed */
4655 if ( (item->fType & MF_POPUP) && (flags & GMDI_GOINTOPOPUPS) )
4656 {
4657 UINT ret;
4658 ret = GetMenuDefaultItem( item->hSubMenu, bypos, flags );
4659 if ( -1 != ret ) return ret;
4660
4661 /* when item not found in submenu, return the popup item */
4662 }
4663 return ( bypos ) ? i : item->wID;
4664
4665}
4666
4667
4668/**********************************************************************
4669 * InsertMenuItemA (USER32.@)
4670 */
4671BOOL WINAPI InsertMenuItemA(HMENU hMenu, UINT uItem, BOOL bypos,
4672 const MENUITEMINFOA *lpmii)
4673{
4674 // In the latest Win versions (2000+?) MIIM_TYPE is superceded by separate
4675 // MIIM_BITMAP, MIIM_FTYPE, and MIIM_STRING flags. However, the old Wine
4676 // code seems to be not aware of that. Fix it on our own here.
4677 // NOTE: This should go away once the new Wine is merged in.
4678 MENUITEMINFOA mii;
4679 if (lpmii->fMask & (MIIM_FTYPE | MIIM_STRING) == MIIM_FTYPE | MIIM_STRING)
4680 {
4681 mii = *lpmii;
4682 lpmii = &mii;
4683 mii.fMask |= MIIM_TYPE;
4684 }
4685
4686 MENUITEM *item = MENU_InsertItem(hMenu, uItem, bypos ? MF_BYPOSITION : 0 );
4687 return SetMenuItemInfo_common(item, (const MENUITEMINFOW *)lpmii, FALSE);
4688}
4689
4690
4691/**********************************************************************
4692 * InsertMenuItemW (USER32.@)
4693 */
4694BOOL WINAPI InsertMenuItemW(HMENU hMenu, UINT uItem, BOOL bypos,
4695 const MENUITEMINFOW *lpmii)
4696{
4697 dprintf(("*** InsertMenuItem: %d %x %x [%S]", uItem, lpmii->fMask, lpmii->fType, lpmii->fType == MFT_STRING ? lpmii->dwTypeData : 0));
4698
4699 // In the latest Win versions (2000+?) MIIM_TYPE is superceded by separate
4700 // MIIM_BITMAP, MIIM_FTYPE, and MIIM_STRING flags. However, the old Wine
4701 // code seems to be not aware of that. Fix it on our own here.
4702 // NOTE: This should go away once the new Wine is merged in.
4703 MENUITEMINFOW mii;
4704 if (lpmii->fMask & (MIIM_FTYPE | MIIM_STRING) == MIIM_FTYPE | MIIM_STRING)
4705 {
4706 mii = *lpmii;
4707 lpmii = &mii;
4708 mii.fMask |= MIIM_TYPE;
4709 }
4710
4711 MENUITEM *item = MENU_InsertItem(hMenu, uItem, bypos ? MF_BYPOSITION : 0 );
4712 dprintf(("*** InsertMenuItem 1: lpitem=%x %x [%S] %x", item, item->fType, item->text, item->hSubMenu));
4713
4714 BOOL b = SetMenuItemInfo_common(item, lpmii, TRUE);
4715 dprintf(("*** InsertMenuItem 2: lpitem=%x %x [%S] %x", item, item->fType, item->text, item->hSubMenu));
4716 return b;
4717}
4718
4719/**********************************************************************
4720 * CheckMenuRadioItem (USER32.@)
4721 */
4722
4723BOOL WINAPI CheckMenuRadioItem(HMENU hMenu,
4724 UINT first, UINT last, UINT check,
4725 UINT bypos)
4726{
4727 MENUITEM *mifirst, *milast, *micheck;
4728 HMENU mfirst = hMenu, mlast = hMenu, mcheck = hMenu;
4729
4730 TRACE("ox%x: %d-%d, check %d, bypos=%d\n",
4731 hMenu, first, last, check, bypos);
4732
4733 mifirst = MENU_FindItem (&mfirst, &first, bypos);
4734 milast = MENU_FindItem (&mlast, &last, bypos);
4735 micheck = MENU_FindItem (&mcheck, &check, bypos);
4736
4737 if (mifirst == NULL || milast == NULL || micheck == NULL ||
4738 mifirst > milast || mfirst != mlast || mfirst != mcheck ||
4739 micheck > milast || micheck < mifirst)
4740 return FALSE;
4741
4742 while (mifirst <= milast)
4743 {
4744 if (mifirst == micheck)
4745 {
4746 mifirst->fType |= MFT_RADIOCHECK;
4747 mifirst->fState |= MFS_CHECKED;
4748 } else {
4749 mifirst->fType &= ~MFT_RADIOCHECK;
4750 mifirst->fState &= ~MFS_CHECKED;
4751 }
4752 mifirst++;
4753 }
4754
4755 return TRUE;
4756}
4757
4758/**********************************************************************
4759 * GetMenuItemRect (USER32.@)
4760 *
4761 * ATTENTION: Here, the returned values in rect are the screen
4762 * coordinates of the item just like if the menu was
4763 * always on the upper left side of the application.
4764 *
4765 */
4766BOOL WINAPI GetMenuItemRect (HWND hwnd, HMENU hMenu, UINT uItem,
4767 LPRECT rect)
4768{
4769 POPUPMENU *itemMenu;
4770 MENUITEM *item;
4771 HWND referenceHwnd;
4772
4773 TRACE("(0x%x,0x%x,%d,%p)\n", hwnd, hMenu, uItem, rect);
4774
4775 item = MENU_FindItem (&hMenu, &uItem, MF_BYPOSITION);
4776 referenceHwnd = hwnd;
4777
4778 if(!hwnd)
4779 {
4780 itemMenu = MENU_GetMenu(hMenu);
4781 if (itemMenu == NULL)
4782 return FALSE;
4783
4784 if(itemMenu->hWnd == 0)
4785 return FALSE;
4786 referenceHwnd = itemMenu->hWnd;
4787 }
4788
4789 if ((rect == NULL) || (item == NULL))
4790 return FALSE;
4791
4792 *rect = item->rect;
4793
4794 MapWindowPoints(referenceHwnd, 0, (LPPOINT)rect, 2);
4795
4796 return TRUE;
4797}
4798
4799
4800/**********************************************************************
4801 * SetMenuInfo (USER32.@)
4802 *
4803 * FIXME
4804 * MIM_APPLYTOSUBMENUS
4805 * actually use the items to draw the menu
4806 */
4807BOOL WINAPI SetMenuInfo (HMENU hMenu, LPCMENUINFO lpmi)
4808{
4809 POPUPMENU *menu;
4810
4811 TRACE("(0x%04x %p)\n", hMenu, lpmi);
4812
4813 if (lpmi && (lpmi->cbSize==sizeof(MENUINFO)) && (menu = MENU_GetMenu(hMenu)))
4814 {
4815
4816 if (lpmi->fMask & MIM_BACKGROUND)
4817 menu->hbrBack = lpmi->hbrBack;
4818
4819 if (lpmi->fMask & MIM_HELPID)
4820 menu->dwContextHelpID = lpmi->dwContextHelpID;
4821
4822 if (lpmi->fMask & MIM_MAXHEIGHT)
4823 menu->cyMax = lpmi->cyMax;
4824
4825 if (lpmi->fMask & MIM_MENUDATA)
4826 menu->dwMenuData = lpmi->dwMenuData;
4827
4828 if (lpmi->fMask & MIM_STYLE)
4829 menu->dwStyle = lpmi->dwStyle;
4830
4831 return TRUE;
4832 }
4833 return FALSE;
4834}
4835
4836/**********************************************************************
4837 * GetMenuInfo (USER32.@)
4838 *
4839 * NOTES
4840 * win98/NT5.0
4841 *
4842 */
4843BOOL WINAPI GetMenuInfo (HMENU hMenu, LPMENUINFO lpmi)
4844{ POPUPMENU *menu;
4845
4846 TRACE("(0x%04x %p)\n", hMenu, lpmi);
4847
4848 if (lpmi && (menu = MENU_GetMenu(hMenu)))
4849 {
4850
4851 if (lpmi->fMask & MIM_BACKGROUND)
4852 lpmi->hbrBack = menu->hbrBack;
4853
4854 if (lpmi->fMask & MIM_HELPID)
4855 lpmi->dwContextHelpID = menu->dwContextHelpID;
4856
4857 if (lpmi->fMask & MIM_MAXHEIGHT)
4858 lpmi->cyMax = menu->cyMax;
4859
4860 if (lpmi->fMask & MIM_MENUDATA)
4861 lpmi->dwMenuData = menu->dwMenuData;
4862
4863 if (lpmi->fMask & MIM_STYLE)
4864 lpmi->dwStyle = menu->dwStyle;
4865
4866 return TRUE;
4867 }
4868 return FALSE;
4869}
4870
4871/**********************************************************************
4872 * SetMenuContextHelpId (USER32.@)
4873 */
4874BOOL WINAPI SetMenuContextHelpId( HMENU hMenu, DWORD dwContextHelpID)
4875{
4876 LPPOPUPMENU menu;
4877
4878 TRACE("(0x%04x 0x%08lx)\n", hMenu, dwContextHelpID);
4879
4880 if ((menu = MENU_GetMenu(hMenu)) != 0)
4881 {
4882 menu->dwContextHelpID = dwContextHelpID;
4883 return TRUE;
4884 }
4885 return FALSE;
4886}
4887
4888/**********************************************************************
4889 * GetMenuContextHelpId (USER32.@)
4890 */
4891DWORD WINAPI GetMenuContextHelpId( HMENU hMenu )
4892{
4893 LPPOPUPMENU menu;
4894
4895 TRACE("(0x%04x)\n", hMenu);
4896
4897 if ((menu = MENU_GetMenu(hMenu)) != 0)
4898 {
4899 return menu->dwContextHelpID;
4900 }
4901 return 0;
4902}
4903
4904/**********************************************************************
4905 * MenuItemFromPoint (USER32.@)
4906 */
4907UINT WINAPI MenuItemFromPoint(HWND hWnd, HMENU hMenu, POINT ptScreen)
4908{
4909 POPUPMENU *menu = MENU_GetMenu(hMenu);
4910 UINT pos;
4911 MENUITEM *item;
4912
4913 /*FIXME: Do we have to handle hWnd here? */
4914 item = MENU_FindItemByCoords(menu, ptScreen, &pos);
4915
4916 return pos;
4917}
4918
4919#ifdef __WIN32OS2__
4920//******************************************************************************
4921//******************************************************************************
4922void WIN32API DisableOdinSysMenuItems()
4923{
4924 fDisableOdinSysMenuItems = TRUE;
4925}
4926/**********************************************************************
4927 * KBD_translate_accelerator
4928 *
4929 * FIXME: should send some WM_INITMENU or/and WM_INITMENUPOPUP -messages
4930 */
4931static BOOL KBD_translate_accelerator(HWND hWnd,LPMSG msg,
4932 BYTE fVirt,WORD key,WORD cmd)
4933{
4934 BOOL sendmsg = FALSE;
4935
4936 if(msg->wParam == key)
4937 {
4938 if (msg->message == WM_CHAR) {
4939 if ( !(fVirt & FALT) && !(fVirt & FVIRTKEY) )
4940 {
4941 dprintf(("TranslateAccelerator: found accel for WM_CHAR: ('%c')\n", msg->wParam&0xff));
4942 sendmsg=TRUE;
4943 }
4944 }
4945 else
4946 {
4947 if(fVirt & FVIRTKEY) {
4948 INT mask = 0;
4949 if(GetKeyState(VK_SHIFT) & 0x8000) mask |= FSHIFT;
4950 if(GetKeyState(VK_CONTROL) & 0x8000) mask |= FCONTROL;
4951 if(GetKeyState(VK_MENU) & 0x8000) mask |= FALT;
4952
4953 if(mask == (fVirt & (FSHIFT | FCONTROL | FALT)))
4954 sendmsg=TRUE;
4955 else dprintf(("TranslateAccelerator: but incorrect SHIFT/CTRL/ALT-state %x != %x", mask, fVirt));
4956 }
4957 else
4958 {
4959 if (!(msg->lParam & 0x01000000)) /* no special_key */
4960 {
4961 if ((fVirt & FALT) && (msg->lParam & 0x20000000))
4962 { /* ^^ ALT pressed */
4963 dprintf(("TranslateAccelerator: found accel for Alt-%c\n", msg->wParam&0xff));
4964 sendmsg=TRUE;
4965 }
4966 }
4967 }
4968 }
4969
4970 dprintf(("TranslateAccelerator: not match for %x %x %x", fVirt, key, cmd));
4971 }
4972
4973 else if( key >= 1 && key <= 26 && !( fVirt & FVIRTKEY )) // Ctrl-A to Ctrl-Z
4974 {
4975 int ctrlCh;
4976
4977 if( msg->message == WM_CHAR )
4978 ctrlCh = toupper( msg->wParam ) - 'A' + 1;
4979 else
4980 ctrlCh = msg->wParam - VK_A + 1;
4981
4982 if( ctrlCh == key )
4983 {
4984 INT mask = 0;
4985 if(GetKeyState(VK_SHIFT) & 0x8000) mask |= FSHIFT;
4986 if(GetKeyState(VK_CONTROL) & 0x8000) mask |= FCONTROL;
4987 if(GetKeyState(VK_MENU) & 0x8000) mask |= FALT;
4988
4989 if(mask == FCONTROL)
4990 {
4991 sendmsg=TRUE;
4992 }
4993 }
4994 }
4995
4996 if (sendmsg) /* found an accelerator, but send a message... ? */
4997 {
4998 INT iSysStat = -1,iStat = -1,mesg=0;
4999 HMENU hSysMenu, hMenu, hSubMenu;
5000 UINT nPos;
5001
5002 if (msg->message == WM_KEYUP || msg->message == WM_SYSKEYUP) {
5003 mesg=1;
5004 }
5005 else
5006 if (GetCapture())
5007 mesg=2;
5008 else
5009 if (!IsWindowEnabled(hWnd))
5010 mesg=3;
5011 else
5012 {
5013 hMenu = (GetWindowLongA(hWnd, GWL_STYLE) & WS_CHILD) ? NULL : GetMenu(hWnd);
5014 hSysMenu = getSysMenu(hWnd);
5015
5016 hSubMenu = hSysMenu;
5017 nPos = cmd;
5018 if(MENU_FindItem(&hSubMenu, &nPos, MF_BYCOMMAND))
5019 {
5020 SendMessageA(hWnd, WM_INITMENU, (WPARAM)hSysMenu, 0L);
5021 if(hSubMenu != hSysMenu)
5022 {
5023 nPos = MENU_FindSubMenu(&hSysMenu, hSubMenu);
5024 dprintf(("hSysMenu = %04x, hSubMenu = %04x, nPos = %d\n", hSysMenu, hSubMenu, nPos));
5025 SendMessageA(hWnd, WM_INITMENUPOPUP, (WPARAM)hSubMenu, MAKELPARAM(nPos, TRUE));
5026 }
5027 iSysStat = GetMenuState(GetSubMenu(hSysMenu, 0), cmd, MF_BYCOMMAND);
5028 }
5029 else
5030 {
5031 hSubMenu = hMenu;
5032 nPos = cmd;
5033 if(MENU_FindItem(&hSubMenu, &nPos, MF_BYCOMMAND))
5034 {
5035 SendMessageA(hWnd, WM_INITMENU, (WPARAM)hMenu, 0L);
5036 if(hSubMenu != hMenu)
5037 {
5038 nPos = MENU_FindSubMenu(&hMenu, hSubMenu);
5039 dprintf(("hMenu = %04x, hSubMenu = %04x, nPos = %d\n", hMenu, hSubMenu, nPos));
5040 SendMessageA(hWnd, WM_INITMENUPOPUP, (WPARAM)hSubMenu, MAKELPARAM(nPos, FALSE));
5041 }
5042 iStat = GetMenuState(hMenu, cmd, MF_BYCOMMAND);
5043 }
5044 }
5045
5046 if (iSysStat!=-1)
5047 {
5048 if (iSysStat & (MF_DISABLED|MF_GRAYED))
5049 mesg=4;
5050 else
5051 mesg=WM_SYSCOMMAND;
5052 }
5053 else
5054 {
5055 if (iStat!=-1)
5056 {
5057 if (IsIconic(hWnd)) {
5058 mesg=5;
5059 }
5060 else
5061 {
5062 if (iStat & (MF_DISABLED|MF_GRAYED))
5063 mesg=6;
5064 else
5065 mesg=WM_COMMAND;
5066 }
5067 }
5068 else
5069 mesg=WM_COMMAND;
5070 }
5071 }
5072 if ( mesg==WM_COMMAND )
5073 SendMessageA(hWnd, mesg, MAKEWPARAM( cmd, 1 ), NULL);
5074 else if( mesg==WM_SYSCOMMAND )
5075 SendMessageA( hWnd, mesg, cmd, MAKELPARAM( 0, 1 ));
5076 else
5077 {
5078 /* some reasons for NOT sending the WM_{SYS}COMMAND message:
5079 * #0: unknown (please report!)
5080 * #1: for WM_KEYUP,WM_SYSKEYUP
5081 * #2: mouse is captured
5082 * #3: window is disabled
5083 * #4: it's a disabled system menu option
5084 * #5: it's a menu option, but window is iconic
5085 * #6: it's a menu option, but disabled
5086 */
5087 if(mesg==0)
5088 dprintf(("ERROR: unknown reason - please report!"));
5089 else dprintf(("but won't send WM_{SYS}COMMAND, reason is #%d\n",mesg));
5090
5091 }
5092 return TRUE;
5093 }
5094
5095 return FALSE;
5096}
5097/*****************************************************************************
5098 * Name : int WIN32API TranslateAcceleratorA
5099 * Purpose : Translate WM_*KEYDOWN messages to WM_COMMAND messages
5100 * according to Accelerator table
5101 * Parameters: HWND hwnd, HACCEL haccel, LPMSG lpmsg
5102 * Variables :
5103 * Result : int FALSE (no accelerator found) TRUE (accelerator found)
5104 * Remark : if a accelerator is found it is not neccesarely executed
5105 * depends on window stat
5106 *
5107 *****************************************************************************/
5108INT WINAPI TranslateAcceleratorA(HWND hWnd, HACCEL hAccel, LPMSG msg)
5109{
5110 /* YES, Accel16! */
5111 LPACCEL lpAccelTbl;
5112 int i;
5113
5114 SetLastError(ERROR_SUCCESS);
5115 if (msg == NULL)
5116 {
5117 dprintf(("TranslateAcceleratorAmsg null; should hang here to be win compatible"));
5118 SetLastError(ERROR_INVALID_PARAMETER);
5119 return 0;
5120 }
5121 if (!hAccel || !(lpAccelTbl = (LPACCEL)GlobalLock(hAccel)))
5122 {
5123 dprintf(("TranslateAcceleratorA: invalid accel handle=%x", hAccel));
5124 SetLastError(ERROR_INVALID_PARAMETER);
5125 return 0;
5126 }
5127 if(!IsWindow(hWnd)) {
5128 dprintf(("TranslateAccelerator, window %x not found", hWnd));
5129 SetLastError(ERROR_INVALID_WINDOW_HANDLE);
5130 return 0;
5131 }
5132 if ((msg->message != WM_KEYDOWN &&
5133 msg->message != WM_KEYUP &&
5134 msg->message != WM_SYSKEYDOWN &&
5135 msg->message != WM_SYSKEYUP &&
5136 msg->message != WM_CHAR))
5137 {
5138 return 0;
5139 }
5140
5141/* TRACE_(accel)("TranslateAccelerators hAccel=%04x, hWnd=%04x,"
5142 "msg->hwnd=%04x, msg->message=%04x, wParam=%08x, lParam=%lx\n",
5143 hAccel,hWnd,msg->hwnd,msg->message,msg->wParam,msg->lParam); */
5144
5145 i = 0;
5146 do
5147 {
5148 if (KBD_translate_accelerator(hWnd,msg,lpAccelTbl[i].fVirt,
5149 lpAccelTbl[i].key,lpAccelTbl[i].cmd))
5150 {
5151 return 1;
5152 }
5153 }
5154 while ((lpAccelTbl[i++].fVirt & 0x80) == 0);
5155
5156// WARN_(accel)("couldn't translate accelerator key\n");
5157 return 0;
5158}
5159//******************************************************************************
5160//******************************************************************************
5161#else
5162//winaccel.cpp
5163/**********************************************************************
5164 * translate_accelerator
5165 */
5166static BOOL translate_accelerator( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam,
5167 BYTE fVirt, WORD key, WORD cmd )
5168{
5169 UINT mesg = 0;
5170
5171 if (wParam != key) return FALSE;
5172
5173 if (message == WM_CHAR)
5174 {
5175 if ( !(fVirt & FALT) && !(fVirt & FVIRTKEY) )
5176 {
5177 TRACE_(accel)("found accel for WM_CHAR: ('%c')\n", wParam & 0xff);
5178 goto found;
5179 }
5180 }
5181 else
5182 {
5183 if(fVirt & FVIRTKEY)
5184 {
5185 INT mask = 0;
5186 TRACE_(accel)("found accel for virt_key %04x (scan %04x)\n",
5187 wParam, 0xff & HIWORD(lParam));
5188 if(GetKeyState(VK_SHIFT) & 0x8000) mask |= FSHIFT;
5189 if(GetKeyState(VK_CONTROL) & 0x8000) mask |= FCONTROL;
5190 if(GetKeyState(VK_MENU) & 0x8000) mask |= FALT;
5191 if(mask == (fVirt & (FSHIFT | FCONTROL | FALT))) goto found;
5192 TRACE_(accel)(", but incorrect SHIFT/CTRL/ALT-state\n");
5193 }
5194 else
5195 {
5196 if (!(lParam & 0x01000000)) /* no special_key */
5197 {
5198 if ((fVirt & FALT) && (lParam & 0x20000000))
5199 { /* ^^ ALT pressed */
5200 TRACE_(accel)("found accel for Alt-%c\n", wParam & 0xff);
5201 goto found;
5202 }
5203 }
5204 }
5205 }
5206 return FALSE;
5207
5208 found:
5209 if (message == WM_KEYUP || message == WM_SYSKEYUP)
5210 mesg = 1;
5211 else if (GetCapture())
5212 mesg = 2;
5213 else if (!IsWindowEnabled(hWnd))
5214 mesg = 3;
5215 else
5216 {
5217 HMENU hMenu, hSubMenu, hSysMenu;
5218 UINT uSysStat = (UINT)-1, uStat = (UINT)-1, nPos;
5219
5220 hMenu = (GetWindowLongA( hWnd, GWL_STYLE ) & WS_CHILD) ? 0 : GetMenu(hWnd);
5221 hSysMenu = get_win_sys_menu( hWnd );
5222
5223 /* find menu item and ask application to initialize it */
5224 /* 1. in the system menu */
5225 hSubMenu = hSysMenu;
5226 nPos = cmd;
5227 if(MENU_FindItem(&hSubMenu, &nPos, MF_BYCOMMAND))
5228 {
5229 SendMessageA(hWnd, WM_INITMENU, (WPARAM)hSysMenu, 0L);
5230 if(hSubMenu != hSysMenu)
5231 {
5232 nPos = MENU_FindSubMenu(&hSysMenu, hSubMenu);
5233 TRACE_(accel)("hSysMenu = %04x, hSubMenu = %04x, nPos = %d\n", hSysMenu, hSubMenu, nPos);
5234 SendMessageA(hWnd, WM_INITMENUPOPUP, (WPARAM)hSubMenu, MAKELPARAM(nPos, TRUE));
5235 }
5236 uSysStat = GetMenuState(GetSubMenu(hSysMenu, 0), cmd, MF_BYCOMMAND);
5237 }
5238 else /* 2. in the window's menu */
5239 {
5240 hSubMenu = hMenu;
5241 nPos = cmd;
5242 if(MENU_FindItem(&hSubMenu, &nPos, MF_BYCOMMAND))
5243 {
5244 SendMessageA(hWnd, WM_INITMENU, (WPARAM)hMenu, 0L);
5245 if(hSubMenu != hMenu)
5246 {
5247 nPos = MENU_FindSubMenu(&hMenu, hSubMenu);
5248 TRACE_(accel)("hMenu = %04x, hSubMenu = %04x, nPos = %d\n", hMenu, hSubMenu, nPos);
5249 SendMessageA(hWnd, WM_INITMENUPOPUP, (WPARAM)hSubMenu, MAKELPARAM(nPos, FALSE));
5250 }
5251 uStat = GetMenuState(hMenu, cmd, MF_BYCOMMAND);
5252 }
5253 }
5254
5255 if (uSysStat != (UINT)-1)
5256 {
5257 if (uSysStat & (MF_DISABLED|MF_GRAYED))
5258 mesg=4;
5259 else
5260 mesg=WM_SYSCOMMAND;
5261 }
5262 else
5263 {
5264 if (uStat != (UINT)-1)
5265 {
5266 if (IsIconic(hWnd))
5267 mesg=5;
5268 else
5269 {
5270 if (uStat & (MF_DISABLED|MF_GRAYED))
5271 mesg=6;
5272 else
5273 mesg=WM_COMMAND;
5274 }
5275 }
5276 else
5277 mesg=WM_COMMAND;
5278 }
5279 }
5280
5281 if( mesg==WM_COMMAND )
5282 {
5283 TRACE_(accel)(", sending WM_COMMAND, wParam=%0x\n", 0x10000 | cmd);
5284 SendMessageA(hWnd, mesg, 0x10000 | cmd, 0L);
5285 }
5286 else if( mesg==WM_SYSCOMMAND )
5287 {
5288 TRACE_(accel)(", sending WM_SYSCOMMAND, wParam=%0x\n", cmd);
5289 SendMessageA(hWnd, mesg, cmd, 0x00010000L);
5290 }
5291 else
5292 {
5293 /* some reasons for NOT sending the WM_{SYS}COMMAND message:
5294 * #0: unknown (please report!)
5295 * #1: for WM_KEYUP,WM_SYSKEYUP
5296 * #2: mouse is captured
5297 * #3: window is disabled
5298 * #4: it's a disabled system menu option
5299 * #5: it's a menu option, but window is iconic
5300 * #6: it's a menu option, but disabled
5301 */
5302 TRACE_(accel)(", but won't send WM_{SYS}COMMAND, reason is #%d\n",mesg);
5303 if(mesg==0)
5304 ERR_(accel)(" unknown reason - please report!");
5305 }
5306 return TRUE;
5307}
5308
5309/**********************************************************************
5310 * TranslateAccelerator (USER32.@)
5311 * TranslateAcceleratorA (USER32.@)
5312 * TranslateAcceleratorW (USER32.@)
5313 */
5314INT WINAPI TranslateAccelerator( HWND hWnd, HACCEL hAccel, LPMSG msg )
5315{
5316 /* YES, Accel16! */
5317 LPACCEL16 lpAccelTbl;
5318 int i;
5319
5320 if (msg == NULL)
5321 {
5322 WARN_(accel)("msg null; should hang here to be win compatible\n");
5323 return 0;
5324 }
5325 if (!hAccel || !(lpAccelTbl = (LPACCEL16) LockResource16(HACCEL_16(hAccel))))
5326 {
5327 WARN_(accel)("invalid accel handle=%x\n", hAccel);
5328 return 0;
5329 }
5330 if ((msg->message != WM_KEYDOWN &&
5331 msg->message != WM_KEYUP &&
5332 msg->message != WM_SYSKEYDOWN &&
5333 msg->message != WM_SYSKEYUP &&
5334 msg->message != WM_CHAR)) return 0;
5335
5336 TRACE_(accel)("TranslateAccelerators hAccel=%04x, hWnd=%04x,"
5337 "msg->hwnd=%04x, msg->message=%04x, wParam=%08x, lParam=%lx\n",
5338 hAccel,hWnd,msg->hwnd,msg->message,msg->wParam,msg->lParam);
5339
5340 i = 0;
5341 do
5342 {
5343 if (translate_accelerator( hWnd, msg->message, msg->wParam, msg->lParam,
5344 lpAccelTbl[i].fVirt, lpAccelTbl[i].key, lpAccelTbl[i].cmd))
5345 return 1;
5346 } while ((lpAccelTbl[i++].fVirt & 0x80) == 0);
5347 WARN_(accel)("couldn't translate accelerator key\n");
5348 return 0;
5349}
5350#endif
5351
5352
Note: See TracBrowser for help on using the repository browser.