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

Last change on this file since 21990 was 21990, checked in by dmik, 13 years ago

Make About Odin system menu item localizable.

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