Release 951105
[wine/testsucceed.git] / controls / menu.c
blobb14d1ab2733b8c38ae11c74a89bc790d16ef3840
1 /*
2 * Menu functions
4 * Copyright 1993 Martin Ayotte
5 * Copyright 1994 Alexandre Julliard
6 */
8 /*
9 * Note: the style MF_MOUSESELECT is used to mark popup items that
10 * have been selected, i.e. their popup menu is currently displayed.
11 * This is probably not the meaning this style has in MS-Windows.
14 #include <ctype.h>
15 #include <stdlib.h>
16 #include <stdio.h>
17 #include <string.h>
18 #include "windows.h"
19 #include "syscolor.h"
20 #include "sysmetrics.h"
21 #include "menu.h"
22 #include "user.h"
23 #include "win.h"
24 #include "message.h"
25 #include "graphics.h"
26 #include "stddebug.h"
27 /* #define DEBUG_MENU */
28 /* #define DEBUG_MENUCALC */
29 /* #define DEBUG_MENUSHORTCUT */
30 #include "debug.h"
32 #include "../rc/sysres.h"
34 /* Dimension of the menu bitmaps */
35 static WORD check_bitmap_width = 0, check_bitmap_height = 0;
36 static WORD arrow_bitmap_width = 0, arrow_bitmap_height = 0;
38 /* Flag set by EndMenu() to force an exit from menu tracking */
39 static BOOL fEndMenuCalled = FALSE;
41 /* Space between 2 menu bar items */
42 #define MENU_BAR_ITEMS_SPACE 16
44 /* Minimum width of a tab character */
45 #define MENU_TAB_SPACE 8
47 /* Height of a separator item */
48 #define SEPARATOR_HEIGHT 5
50 /* Values for menu->FocusedItem */
51 /* (other values give the position of the focused item) */
52 #define NO_SELECTED_ITEM 0xffff
53 #define SYSMENU_SELECTED 0xfffe /* Only valid on menu-bars */
55 #define IS_STRING_ITEM(flags) (!((flags) & (MF_BITMAP | MF_OWNERDRAW | \
56 MF_MENUBARBREAK | MF_MENUBREAK | MF_SEPARATOR)))
59 extern void NC_DrawSysButton(HWND hwnd, HDC hdc, BOOL down); /* nonclient.c */
61 static HBITMAP hStdCheck = 0;
62 static HBITMAP hStdMnArrow = 0;
64 HMENU CopySysMenu();
65 WORD * ParseMenuResource(WORD *first_item, int level, HMENU hMenu);
68 /***********************************************************************
69 * MENU_Init
71 * Menus initialisation.
73 BOOL MENU_Init()
75 BITMAP bm;
77 /* Load bitmaps */
79 if (!(hStdCheck = LoadBitmap( 0, MAKEINTRESOURCE(OBM_CHECK) )))
80 return FALSE;
81 GetObject( hStdCheck, sizeof(BITMAP), (LPSTR)&bm );
82 check_bitmap_width = bm.bmWidth;
83 check_bitmap_height = bm.bmHeight;
84 if (!(hStdMnArrow = LoadBitmap( 0, MAKEINTRESOURCE(OBM_MNARROW) )))
85 return FALSE;
86 GetObject( hStdMnArrow, sizeof(BITMAP), (LPSTR)&bm );
87 arrow_bitmap_width = bm.bmWidth;
88 arrow_bitmap_height = bm.bmHeight;
90 return TRUE;
94 /***********************************************************************
95 * MENU_HasSysMenu
97 * Check whether the window owning the menu bar has a system menu.
99 static BOOL MENU_HasSysMenu( POPUPMENU *menu )
101 WND *wndPtr;
103 if (menu->wFlags & MF_POPUP) return FALSE;
104 if (!(wndPtr = WIN_FindWndPtr( menu->hWnd ))) return FALSE;
105 return (wndPtr->dwStyle & WS_SYSMENU) != 0;
109 /***********************************************************************
110 * MENU_IsInSysMenu
112 * Check whether the point (in screen coords) is in the system menu
113 * of the window owning the given menu.
115 static BOOL MENU_IsInSysMenu( POPUPMENU *menu, POINT pt )
117 WND *wndPtr;
119 if (menu->wFlags & MF_POPUP) return FALSE;
120 if (!(wndPtr = WIN_FindWndPtr( menu->hWnd ))) return FALSE;
121 if (!(wndPtr->dwStyle & WS_SYSMENU)) return FALSE;
122 if ((pt.x < wndPtr->rectClient.left) ||
123 (pt.x >= wndPtr->rectClient.left+SYSMETRICS_CXSIZE+SYSMETRICS_CXBORDER))
124 return FALSE;
125 if ((pt.y >= wndPtr->rectClient.top - menu->Height) ||
126 (pt.y < wndPtr->rectClient.top - menu->Height -
127 SYSMETRICS_CYSIZE - SYSMETRICS_CYBORDER)) return FALSE;
128 return TRUE;
132 /***********************************************************************
133 * MENU_FindItem
135 * Find a menu item. Return a pointer on the item, and modifies *hmenu
136 * in case the item was in a sub-menu.
138 static MENUITEM *MENU_FindItem( HMENU *hmenu, UINT *nPos, UINT wFlags )
140 POPUPMENU *menu;
141 MENUITEM *item;
142 int i;
144 if (!(menu = (POPUPMENU *) USER_HEAP_LIN_ADDR(*hmenu))) return NULL;
145 item = (MENUITEM *) USER_HEAP_LIN_ADDR( menu->hItems );
146 if (wFlags & MF_BYPOSITION)
148 if (*nPos >= menu->nItems) return NULL;
149 return &item[*nPos];
151 else
153 for (i = 0; i < menu->nItems; i++, item++)
155 if (item->item_id == *nPos)
157 *nPos = i;
158 return item;
160 else if (item->item_flags & MF_POPUP)
162 HMENU hsubmenu = (HMENU)item->item_id;
163 MENUITEM *subitem = MENU_FindItem( &hsubmenu, nPos, wFlags );
164 if (subitem)
166 *hmenu = hsubmenu;
167 return subitem;
172 return NULL;
176 /***********************************************************************
177 * MENU_FindItemByCoords
179 * Find the item at the specified coordinates (screen coords).
181 static MENUITEM *MENU_FindItemByCoords( POPUPMENU *menu, int x, int y, UINT *pos )
183 MENUITEM *item;
184 WND *wndPtr;
185 int i;
187 if (!(wndPtr = WIN_FindWndPtr( menu->hWnd ))) return NULL;
188 x -= wndPtr->rectWindow.left;
189 y -= wndPtr->rectWindow.top;
190 item = (MENUITEM *) USER_HEAP_LIN_ADDR( menu->hItems );
191 for (i = 0; i < menu->nItems; i++, item++)
193 if ((x >= item->rect.left) && (x < item->rect.right) &&
194 (y >= item->rect.top) && (y < item->rect.bottom))
196 if (pos) *pos = i;
197 return item;
200 return NULL;
204 /***********************************************************************
205 * MENU_FindItemByKey
207 * Find the menu item selected by a key press.
208 * Return item id, -1 if none, -2 if we should close the menu.
210 static UINT MENU_FindItemByKey( HWND hwndOwner, HMENU hmenu, UINT key )
212 POPUPMENU *menu;
213 LPMENUITEM lpitem;
214 int i;
215 LONG menuchar;
217 menu = (POPUPMENU *) USER_HEAP_LIN_ADDR( hmenu );
218 lpitem = (MENUITEM *) USER_HEAP_LIN_ADDR( menu->hItems );
219 key = toupper(key);
220 for (i = 0; i < menu->nItems; i++, lpitem++)
222 if (IS_STRING_ITEM(lpitem->item_flags))
224 char *p = strchr( lpitem->item_text, '&' );
225 if (p && (p[1] != '&') && (toupper(p[1]) == key)) return i;
228 #ifdef WINELIB32
229 menuchar = SendMessage( hwndOwner, WM_MENUCHAR,
230 MAKEWPARAM(key,menu->wFlags), (LPARAM)hmenu );
231 #else
232 menuchar = SendMessage( hwndOwner, WM_MENUCHAR, key,
233 MAKELONG( menu->wFlags, hmenu ) );
234 #endif
235 if (HIWORD(menuchar) == 2) return LOWORD(menuchar);
236 if (HIWORD(menuchar) == 1) return -2;
237 return -1;
241 /***********************************************************************
242 * MENU_CalcItemSize
244 * Calculate the size of the menu item and store it in lpitem->rect.
246 static void MENU_CalcItemSize( HDC hdc, LPMENUITEM lpitem, HWND hwndOwner,
247 int orgX, int orgY, BOOL menuBar )
249 DWORD dwSize;
250 char *p;
252 SetRect( &lpitem->rect, orgX, orgY, orgX, orgY );
253 lpitem->xTab = 0;
254 if (lpitem->item_flags & MF_OWNERDRAW) {
255 static HANDLE mistrh = 0;
256 static SEGPTR mistrsegp = 0;
257 static LPMEASUREITEMSTRUCT mistruct=NULL;
258 if (mistruct == NULL) {
259 mistrh = GlobalAlloc(0,sizeof(MEASUREITEMSTRUCT));
260 mistrsegp = (SEGPTR)WIN16_GlobalLock(mistrh);
261 mistruct = PTR_SEG_TO_LIN(mistrsegp);
263 mistruct->CtlType = ODT_MENU;
264 mistruct->itemID = lpitem->item_id;
265 mistruct->itemData = (long int)lpitem->item_text;
266 mistruct->itemHeight = 16;
267 mistruct->itemWidth = 30;
268 SendMessage(hwndOwner,WM_MEASUREITEM,0,mistrsegp);
269 lpitem->rect.bottom += mistruct->itemHeight;
270 lpitem->rect.right += mistruct->itemWidth;
271 dprintf_menu(stddeb,"DrawMenuItem: MeasureItem %04x %d:%d!\n",
272 lpitem->item_id,mistruct->itemWidth, mistruct->itemHeight);
273 return;
276 if (lpitem->item_flags & MF_SEPARATOR)
278 lpitem->rect.bottom += SEPARATOR_HEIGHT;
279 return;
282 if (!menuBar)
284 lpitem->rect.right += 2 * check_bitmap_width;
285 if (lpitem->item_flags & MF_POPUP)
286 lpitem->rect.right += arrow_bitmap_width;
289 if (lpitem->item_flags & MF_BITMAP)
291 BITMAP bm;
292 GetObject( (HBITMAP)lpitem->hText, sizeof(BITMAP), (LPSTR)&bm );
293 lpitem->rect.right += bm.bmWidth;
294 lpitem->rect.bottom += bm.bmHeight;
295 return;
298 /* If we get here, then it is a text item */
300 dwSize = (lpitem->item_text == NULL) ? 0 : GetTextExtent( hdc, lpitem->item_text, strlen(lpitem->item_text));
301 lpitem->rect.right += LOWORD(dwSize);
302 lpitem->rect.bottom += MAX( HIWORD(dwSize), SYSMETRICS_CYMENU );
304 if (menuBar) lpitem->rect.right += MENU_BAR_ITEMS_SPACE;
305 else if ((p = strchr( lpitem->item_text, '\t' )) != NULL)
307 /* Item contains a tab (only meaningful in popup menus) */
308 lpitem->xTab = check_bitmap_width + MENU_TAB_SPACE +
309 LOWORD( GetTextExtent( hdc, lpitem->item_text,
310 (int)(p - lpitem->item_text) ));
311 lpitem->rect.right += MENU_TAB_SPACE;
313 else
315 if (strchr( lpitem->item_text, '\b' ))
316 lpitem->rect.right += MENU_TAB_SPACE;
317 lpitem->xTab = lpitem->rect.right - check_bitmap_width
318 - arrow_bitmap_width;
323 /***********************************************************************
324 * MENU_PopupMenuCalcSize
326 * Calculate the size of a popup menu.
328 static void MENU_PopupMenuCalcSize( LPPOPUPMENU lppop, HWND hwndOwner )
330 LPMENUITEM items, lpitem;
331 HDC hdc;
332 int start, i;
333 int orgX, orgY, maxX, maxTab, maxTabWidth;
335 lppop->Width = lppop->Height = 0;
336 if (lppop->nItems == 0) return;
337 items = (MENUITEM *)USER_HEAP_LIN_ADDR( lppop->hItems );
338 hdc = GetDC( 0 );
339 maxX = start = 0;
340 while (start < lppop->nItems)
342 lpitem = &items[start];
343 orgX = maxX;
344 orgY = 0;
345 maxTab = maxTabWidth = 0;
347 /* Parse items until column break or end of menu */
348 for (i = start; i < lppop->nItems; i++, lpitem++)
350 if ((i != start) &&
351 (lpitem->item_flags & (MF_MENUBREAK | MF_MENUBARBREAK))) break;
352 MENU_CalcItemSize( hdc, lpitem, hwndOwner, orgX, orgY, FALSE );
353 if (lpitem->item_flags & MF_MENUBARBREAK) orgX++;
354 maxX = MAX( maxX, lpitem->rect.right );
355 orgY = lpitem->rect.bottom;
356 if (lpitem->xTab)
358 maxTab = MAX( maxTab, lpitem->xTab );
359 maxTabWidth = MAX(maxTabWidth,lpitem->rect.right-lpitem->xTab);
363 /* Finish the column (set all items to the largest width found) */
364 maxX = MAX( maxX, maxTab + maxTabWidth );
365 for (lpitem = &items[start]; start < i; start++, lpitem++)
367 lpitem->rect.right = maxX;
368 if (lpitem->xTab) lpitem->xTab = maxTab;
370 lppop->Height = MAX( lppop->Height, orgY );
373 lppop->Width = maxX;
374 ReleaseDC( 0, hdc );
378 /***********************************************************************
379 * MENU_MenuBarCalcSize
381 * Calculate the size of the menu bar.
383 static void MENU_MenuBarCalcSize( HDC hdc, LPRECT lprect, LPPOPUPMENU lppop,
384 HWND hwndOwner )
386 LPMENUITEM lpitem, items;
387 int start, i, orgX, orgY, maxY, helpPos;
389 if ((lprect == NULL) || (lppop == NULL)) return;
390 if (lppop->nItems == 0) return;
391 dprintf_menucalc(stddeb,"MenuBarCalcSize left=%ld top=%ld right=%ld bottom=%ld !\n",
392 (LONG)lprect->left, (LONG)lprect->top, (LONG)lprect->right, (LONG)lprect->bottom);
393 items = (MENUITEM *)USER_HEAP_LIN_ADDR( lppop->hItems );
394 lppop->Width = lprect->right - lprect->left;
395 lppop->Height = 0;
396 maxY = lprect->top;
397 start = 0;
398 helpPos = -1;
399 while (start < lppop->nItems)
401 lpitem = &items[start];
402 orgX = lprect->left;
403 orgY = maxY;
405 /* Parse items until line break or end of menu */
406 for (i = start; i < lppop->nItems; i++, lpitem++)
408 if ((helpPos == -1) && (lpitem->item_flags & MF_HELP)) helpPos = i;
409 if ((i != start) &&
410 (lpitem->item_flags & (MF_MENUBREAK | MF_MENUBARBREAK))) break;
411 MENU_CalcItemSize( hdc, lpitem, hwndOwner, orgX, orgY, TRUE );
412 if (lpitem->rect.right > lprect->right)
414 if (i != start) break;
415 else lpitem->rect.right = lprect->right;
417 maxY = MAX( maxY, lpitem->rect.bottom );
418 orgX = lpitem->rect.right;
421 /* Finish the line (set all items to the largest height found) */
422 while (start < i) items[start++].rect.bottom = maxY;
425 lprect->bottom = maxY;
426 lppop->Height = lprect->bottom - lprect->top;
428 /* Flush right all items between the MF_HELP and the last item */
429 /* (if several lines, only move the last line) */
430 if (helpPos != -1)
432 lpitem = &items[lppop->nItems-1];
433 orgY = lpitem->rect.top;
434 orgX = lprect->right;
435 for (i = lppop->nItems - 1; i >= helpPos; i--, lpitem--)
437 if (lpitem->rect.top != orgY) break; /* Other line */
438 if (lpitem->rect.right >= orgX) break; /* Too far right already */
439 lpitem->rect.left += orgX - lpitem->rect.right;
440 lpitem->rect.right = orgX;
441 orgX = lpitem->rect.left;
447 /***********************************************************************
448 * MENU_DrawMenuItem
450 * Draw a single menu item.
452 static void MENU_DrawMenuItem( HWND hwnd, HDC hdc, LPMENUITEM lpitem,
453 UINT height, BOOL menuBar )
455 RECT rect;
457 if (lpitem->item_flags & MF_OWNERDRAW) {
458 static HANDLE distrh = 0;
459 static SEGPTR distrsegp = 0;
460 static LPDRAWITEMSTRUCT distruct=NULL;
461 if (distruct == NULL) {
462 distrh = GlobalAlloc(0,sizeof(DRAWITEMSTRUCT));
463 distrsegp = (SEGPTR)WIN16_GlobalLock(distrh);
464 distruct = PTR_SEG_TO_LIN(distrsegp);
466 dprintf_menu(stddeb,"DrawMenuItem: Ownerdraw!\n");
467 distruct->CtlType = ODT_MENU;
468 distruct->itemID = lpitem->item_id;
469 distruct->itemData = (long int)lpitem->item_text;
470 distruct->itemState = 0;
471 if (lpitem->item_flags & MF_CHECKED) distruct->itemState |= ODS_CHECKED;
472 if (lpitem->item_flags & MF_GRAYED) distruct->itemState |= ODS_GRAYED;
473 if (lpitem->item_flags & MF_HILITE) distruct->itemState |= ODS_SELECTED;
474 distruct->itemAction = ODA_DRAWENTIRE | ODA_SELECT | ODA_FOCUS;
475 distruct->hwndItem = hwnd;
476 distruct->hDC = hdc;
477 distruct->rcItem = lpitem->rect;
478 SendMessage(hwnd,WM_DRAWITEM,0,distrsegp);
479 return;
481 if (menuBar && (lpitem->item_flags & MF_SEPARATOR)) return;
482 rect = lpitem->rect;
484 /* Draw the background */
486 if (lpitem->item_flags & MF_HILITE)
487 FillRect( hdc, &rect, sysColorObjects.hbrushHighlight );
488 else FillRect( hdc, &rect, sysColorObjects.hbrushMenu );
489 SetBkMode( hdc, TRANSPARENT );
491 /* Draw the separator bar (if any) */
493 if (!menuBar && (lpitem->item_flags & MF_MENUBARBREAK))
495 SelectObject( hdc, sysColorObjects.hpenWindowFrame );
496 MoveTo( hdc, rect.left, 0 );
497 LineTo( hdc, rect.left, height );
499 if (lpitem->item_flags & MF_SEPARATOR)
501 SelectObject( hdc, sysColorObjects.hpenWindowFrame );
502 MoveTo( hdc, rect.left, rect.top + SEPARATOR_HEIGHT/2 );
503 LineTo( hdc, rect.right, rect.top + SEPARATOR_HEIGHT/2 );
504 return;
507 /* Setup colors */
509 if (lpitem->item_flags & MF_HILITE)
511 if (lpitem->item_flags & MF_GRAYED)
512 SetTextColor( hdc, GetSysColor( COLOR_GRAYTEXT ) );
513 else
514 SetTextColor( hdc, GetSysColor( COLOR_HIGHLIGHTTEXT ) );
515 SetBkColor( hdc, GetSysColor( COLOR_HIGHLIGHT ) );
517 else
519 if (lpitem->item_flags & MF_GRAYED)
520 SetTextColor( hdc, GetSysColor( COLOR_GRAYTEXT ) );
521 else
522 SetTextColor( hdc, GetSysColor( COLOR_MENUTEXT ) );
523 SetBkColor( hdc, GetSysColor( COLOR_MENU ) );
526 if (!menuBar)
528 /* Draw the check mark */
530 if (lpitem->item_flags & MF_CHECKED)
532 GRAPH_DrawBitmap(hdc, lpitem->hCheckBit ? lpitem->hCheckBit :
533 hStdCheck, rect.left,
534 (rect.top+rect.bottom-check_bitmap_height) / 2,
535 0, 0, check_bitmap_width, check_bitmap_height );
537 else if (lpitem->hUnCheckBit != 0) /* Not checked */
539 GRAPH_DrawBitmap(hdc, lpitem->hUnCheckBit, rect.left,
540 (rect.top+rect.bottom-check_bitmap_height) / 2,
541 0, 0, check_bitmap_width, check_bitmap_height );
544 /* Draw the popup-menu arrow */
546 if (lpitem->item_flags & MF_POPUP)
548 GRAPH_DrawBitmap( hdc, hStdMnArrow,
549 rect.right-arrow_bitmap_width-1,
550 (rect.top+rect.bottom-arrow_bitmap_height) / 2,
551 0, 0, arrow_bitmap_width, arrow_bitmap_height );
554 rect.left += check_bitmap_width;
555 rect.right -= arrow_bitmap_width;
558 /* Draw the item text or bitmap */
560 if (lpitem->item_flags & MF_BITMAP)
562 GRAPH_DrawBitmap( hdc, (HBITMAP)lpitem->hText, rect.left, rect.top,
563 0, 0, rect.right-rect.left, rect.bottom-rect.top );
564 return;
566 /* No bitmap - process text if present */
567 else if ((lpitem->item_text) != ((char *) NULL))
569 register int i;
571 if (menuBar)
573 rect.left += MENU_BAR_ITEMS_SPACE / 2;
574 rect.right -= MENU_BAR_ITEMS_SPACE / 2;
575 i = strlen( lpitem->item_text );
577 else
579 for (i = 0; lpitem->item_text[i]; i++)
580 if ((lpitem->item_text[i] == '\t') ||
581 (lpitem->item_text[i] == '\b')) break;
584 DrawText( hdc, lpitem->item_text, i, &rect,
585 DT_LEFT | DT_VCENTER | DT_SINGLELINE );
587 if (lpitem->item_text[i]) /* There's a tab or flush-right char */
589 if (lpitem->item_text[i] == '\t')
591 rect.left = lpitem->xTab;
592 DrawText( hdc, lpitem->item_text + i + 1, -1, &rect,
593 DT_LEFT | DT_VCENTER | DT_SINGLELINE );
595 else DrawText( hdc, lpitem->item_text + i + 1, -1, &rect,
596 DT_RIGHT | DT_VCENTER | DT_SINGLELINE );
602 /***********************************************************************
603 * MENU_DrawPopupMenu
605 * Paint a popup menu.
607 static void MENU_DrawPopupMenu( HWND hwnd, HDC hdc, HMENU hmenu )
609 POPUPMENU *menu;
610 MENUITEM *item;
611 RECT rect;
612 int i;
614 GetClientRect( hwnd, &rect );
615 FillRect( hdc, &rect, sysColorObjects.hbrushMenu );
616 menu = (POPUPMENU *) USER_HEAP_LIN_ADDR( hmenu );
617 if (!menu || !menu->nItems) return;
618 item = (MENUITEM *) USER_HEAP_LIN_ADDR( menu->hItems );
619 for (i = menu->nItems; i > 0; i--, item++)
620 MENU_DrawMenuItem( hwnd, hdc, item, menu->Height, FALSE );
624 /***********************************************************************
625 * MENU_DrawMenuBar
627 * Paint a menu bar. Returns the height of the menu bar.
629 UINT MENU_DrawMenuBar(HDC hDC, LPRECT lprect, HWND hwnd, BOOL suppress_draw)
631 LPPOPUPMENU lppop;
632 LPMENUITEM lpitem;
633 int i;
634 WND *wndPtr = WIN_FindWndPtr( hwnd );
636 lppop = (LPPOPUPMENU) USER_HEAP_LIN_ADDR( wndPtr->wIDmenu );
637 if (lppop == NULL || lprect == NULL) return SYSMETRICS_CYMENU;
638 dprintf_menu(stddeb,"MENU_DrawMenuBar("NPFMT", %p, %p); !\n",
639 hDC, lprect, lppop);
640 if (lppop->Height == 0) MENU_MenuBarCalcSize(hDC, lprect, lppop, hwnd);
641 lprect->bottom = lprect->top + lppop->Height;
642 if (suppress_draw) return lppop->Height;
644 FillRect(hDC, lprect, sysColorObjects.hbrushMenu );
645 SelectObject( hDC, sysColorObjects.hpenWindowFrame );
646 MoveTo( hDC, lprect->left, lprect->bottom );
647 LineTo( hDC, lprect->right, lprect->bottom );
649 if (lppop->nItems == 0) return SYSMETRICS_CYMENU;
650 lpitem = (MENUITEM *) USER_HEAP_LIN_ADDR( lppop->hItems );
651 for (i = 0; i < lppop->nItems; i++, lpitem++)
653 MENU_DrawMenuItem( hwnd, hDC, lpitem, lppop->Height, TRUE );
655 return lppop->Height;
659 /***********************************************************************
660 * MENU_ShowPopup
662 * Display a popup menu.
664 static BOOL MENU_ShowPopup(HWND hwndOwner, HMENU hmenu, UINT id, int x, int y)
666 POPUPMENU *menu;
668 if (!(menu = (POPUPMENU *) USER_HEAP_LIN_ADDR( hmenu ))) return FALSE;
669 if (menu->FocusedItem != NO_SELECTED_ITEM)
671 MENUITEM *item = (MENUITEM *) USER_HEAP_LIN_ADDR( menu->hItems );
672 item[menu->FocusedItem].item_flags &= ~(MF_HILITE | MF_MOUSESELECT);
673 menu->FocusedItem = NO_SELECTED_ITEM;
675 SendMessage( hwndOwner, WM_INITMENUPOPUP, (WPARAM)hmenu,
676 MAKELONG( id, (menu->wFlags & MF_SYSMENU) ? 1 : 0 ));
677 MENU_PopupMenuCalcSize( menu, hwndOwner );
678 if (!menu->hWnd)
680 WND *wndPtr = WIN_FindWndPtr( hwndOwner );
681 if (!wndPtr) return FALSE;
682 menu->hWnd = CreateWindow( POPUPMENU_CLASS_ATOM, (SEGPTR)0,
683 WS_POPUP | WS_BORDER, x, y,
684 menu->Width + 2*SYSMETRICS_CXBORDER,
685 menu->Height + 2*SYSMETRICS_CYBORDER,
686 0, 0, wndPtr->hInstance, (SEGPTR)hmenu );
687 if (!menu->hWnd) return FALSE;
689 else SetWindowPos( menu->hWnd, 0, x, y,
690 menu->Width + 2*SYSMETRICS_CXBORDER,
691 menu->Height + 2*SYSMETRICS_CYBORDER,
692 SWP_NOACTIVATE | SWP_NOZORDER );
694 /* Display the window */
696 SetWindowPos( menu->hWnd, HWND_TOP, 0, 0, 0, 0,
697 SWP_SHOWWINDOW | SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE );
698 UpdateWindow( menu->hWnd );
699 return TRUE;
703 /***********************************************************************
704 * MENU_SelectItem
706 static void MENU_SelectItem( HWND hwndOwner, HMENU hmenu, UINT wIndex )
708 MENUITEM *items;
709 LPPOPUPMENU lppop;
710 HDC hdc;
712 lppop = (POPUPMENU *) USER_HEAP_LIN_ADDR( hmenu );
713 if (!lppop->nItems) return;
714 items = (MENUITEM *) USER_HEAP_LIN_ADDR( lppop->hItems );
715 if ((wIndex != NO_SELECTED_ITEM) &&
716 (wIndex != SYSMENU_SELECTED) &&
717 (items[wIndex].item_flags & MF_SEPARATOR))
718 wIndex = NO_SELECTED_ITEM;
719 if (lppop->FocusedItem == wIndex) return;
720 if (lppop->wFlags & MF_POPUP) hdc = GetDC( lppop->hWnd );
721 else hdc = GetDCEx( lppop->hWnd, 0, DCX_CACHE | DCX_WINDOW);
723 /* Clear previous highlighted item */
724 if (lppop->FocusedItem != NO_SELECTED_ITEM)
726 if (lppop->FocusedItem == SYSMENU_SELECTED)
727 NC_DrawSysButton( lppop->hWnd, hdc, FALSE );
728 else
730 items[lppop->FocusedItem].item_flags &=~(MF_HILITE|MF_MOUSESELECT);
731 MENU_DrawMenuItem( lppop->hWnd, hdc, &items[lppop->FocusedItem], lppop->Height,
732 !(lppop->wFlags & MF_POPUP) );
736 /* Highlight new item (if any) */
737 lppop->FocusedItem = wIndex;
738 if (lppop->FocusedItem != NO_SELECTED_ITEM)
740 if (lppop->FocusedItem == SYSMENU_SELECTED)
742 NC_DrawSysButton( lppop->hWnd, hdc, TRUE );
743 #ifdef WINELIB32
744 /* FIX: LostInfo */
745 SendMessage( hwndOwner, WM_MENUSELECT,
746 MAKEWPARAM( (DWORD)GetSystemMenu( lppop->hWnd, FALSE ),
747 lppop->wFlags | MF_MOUSESELECT ),
748 (LPARAM)hmenu );
749 #else
750 SendMessage( hwndOwner, WM_MENUSELECT,
751 GetSystemMenu( lppop->hWnd, FALSE ),
752 MAKELONG( lppop->wFlags | MF_MOUSESELECT, hmenu ) );
753 #endif
755 else
757 items[lppop->FocusedItem].item_flags |= MF_HILITE;
758 MENU_DrawMenuItem( lppop->hWnd, hdc, &items[lppop->FocusedItem], lppop->Height,
759 !(lppop->wFlags & MF_POPUP) );
760 #ifdef WINELIB32
761 SendMessage( hwndOwner, WM_MENUSELECT,
762 MAKEWPARAM( items[lppop->FocusedItem].item_id,
763 items[lppop->FocusedItem].item_flags |
764 MF_MOUSESELECT ),
765 (LPARAM) hmenu );
766 #else
767 SendMessage( hwndOwner, WM_MENUSELECT,
768 items[lppop->FocusedItem].item_id,
769 MAKELONG( items[lppop->FocusedItem].item_flags | MF_MOUSESELECT, hmenu));
770 #endif
773 #ifdef WINELIB32
774 /* FIX: Lost Info */
775 else SendMessage( hwndOwner, WM_MENUSELECT,
776 MAKEWPARAM( (DWORD)hmenu, lppop->wFlags | MF_MOUSESELECT),
777 (LPARAM)hmenu );
778 #else
779 else SendMessage( hwndOwner, WM_MENUSELECT, hmenu,
780 MAKELONG( lppop->wFlags | MF_MOUSESELECT, hmenu ) );
781 #endif
783 ReleaseDC( lppop->hWnd, hdc );
787 /***********************************************************************
788 * MENU_SelectNextItem
790 static void MENU_SelectNextItem( HWND hwndOwner, HMENU hmenu )
792 int i;
793 MENUITEM *items;
794 POPUPMENU *menu;
796 menu = (POPUPMENU *) USER_HEAP_LIN_ADDR( hmenu );
797 if (!menu->nItems) return;
798 items = (MENUITEM *) USER_HEAP_LIN_ADDR( menu->hItems );
799 if ((menu->FocusedItem != NO_SELECTED_ITEM) &&
800 (menu->FocusedItem != SYSMENU_SELECTED))
802 for (i = menu->FocusedItem+1; i < menu->nItems; i++)
804 if (!(items[i].item_flags & MF_SEPARATOR))
806 MENU_SelectItem( hwndOwner, hmenu, i );
807 return;
810 if (MENU_HasSysMenu( menu ))
812 MENU_SelectItem( hwndOwner, hmenu, SYSMENU_SELECTED );
813 return;
816 for (i = 0; i < menu->nItems; i++)
818 if (!(items[i].item_flags & MF_SEPARATOR))
820 MENU_SelectItem( hwndOwner, hmenu, i );
821 return;
824 if (MENU_HasSysMenu( menu ))
825 MENU_SelectItem( hwndOwner, hmenu, SYSMENU_SELECTED );
829 /***********************************************************************
830 * MENU_SelectPrevItem
832 static void MENU_SelectPrevItem( HWND hwndOwner, HMENU hmenu )
834 int i;
835 MENUITEM *items;
836 POPUPMENU *menu;
838 menu = (POPUPMENU *) USER_HEAP_LIN_ADDR( hmenu );
839 if (!menu->nItems) return;
840 items = (MENUITEM *) USER_HEAP_LIN_ADDR( menu->hItems );
841 if ((menu->FocusedItem != NO_SELECTED_ITEM) &&
842 (menu->FocusedItem != SYSMENU_SELECTED))
844 for (i = menu->FocusedItem - 1; i >= 0; i--)
846 if (!(items[i].item_flags & MF_SEPARATOR))
848 MENU_SelectItem( hwndOwner, hmenu, i );
849 return;
852 if (MENU_HasSysMenu( menu ))
854 MENU_SelectItem( hwndOwner, hmenu, SYSMENU_SELECTED );
855 return;
858 for (i = menu->nItems - 1; i > 0; i--)
860 if (!(items[i].item_flags & MF_SEPARATOR))
862 MENU_SelectItem( hwndOwner, hmenu, i );
863 return;
866 if (MENU_HasSysMenu( menu ))
867 MENU_SelectItem( hwndOwner, hmenu, SYSMENU_SELECTED );
871 /***********************************************************************
872 * MENU_GetSubPopup
874 * Return the handle of the selected sub-popup menu (if any).
876 static HMENU MENU_GetSubPopup( HMENU hmenu )
878 POPUPMENU *menu;
879 MENUITEM *item;
881 menu = (POPUPMENU *) USER_HEAP_LIN_ADDR( hmenu );
882 if (menu->FocusedItem == NO_SELECTED_ITEM) return 0;
883 else if (menu->FocusedItem == SYSMENU_SELECTED)
884 return GetSystemMenu( menu->hWnd, FALSE );
886 item = ((MENUITEM *)USER_HEAP_LIN_ADDR(menu->hItems)) + menu->FocusedItem;
887 if (!(item->item_flags & MF_POPUP) || !(item->item_flags & MF_MOUSESELECT))
888 return 0;
889 return (HMENU)item->item_id;
893 /***********************************************************************
894 * MENU_HideSubPopups
896 * Hide the sub-popup menus of this menu.
898 static void MENU_HideSubPopups( HWND hwndOwner, HMENU hmenu )
900 MENUITEM *item;
901 POPUPMENU *menu, *submenu;
902 HMENU hsubmenu;
904 if (!(menu = (POPUPMENU *) USER_HEAP_LIN_ADDR( hmenu ))) return;
905 if (menu->FocusedItem == NO_SELECTED_ITEM) return;
906 if (menu->FocusedItem == SYSMENU_SELECTED)
908 hsubmenu = GetSystemMenu( menu->hWnd, FALSE );
910 else
912 item = ((MENUITEM *)USER_HEAP_LIN_ADDR(menu->hItems)) + menu->FocusedItem;
913 if (!(item->item_flags & MF_POPUP) ||
914 !(item->item_flags & MF_MOUSESELECT)) return;
915 item->item_flags &= ~MF_MOUSESELECT;
916 hsubmenu = (HMENU)item->item_id;
918 submenu = (POPUPMENU *) USER_HEAP_LIN_ADDR( hsubmenu );
919 MENU_HideSubPopups( hwndOwner, hsubmenu );
920 if (submenu->hWnd) ShowWindow( submenu->hWnd, SW_HIDE );
921 MENU_SelectItem( hwndOwner, hsubmenu, NO_SELECTED_ITEM );
925 /***********************************************************************
926 * MENU_ShowSubPopup
928 * Display the sub-menu of the selected item of this menu.
929 * Return the handle of the submenu, or hmenu if no submenu to display.
931 static HMENU MENU_ShowSubPopup( HWND hwndOwner, HMENU hmenu, BOOL selectFirst )
933 POPUPMENU *menu;
934 MENUITEM *item;
935 WND *wndPtr;
937 if (!(menu = (POPUPMENU *) USER_HEAP_LIN_ADDR( hmenu ))) return hmenu;
938 if (!(wndPtr = WIN_FindWndPtr( menu->hWnd ))) return hmenu;
939 if (menu->FocusedItem == NO_SELECTED_ITEM) return hmenu;
940 if (menu->FocusedItem == SYSMENU_SELECTED)
942 MENU_ShowPopup(hwndOwner, wndPtr->hSysMenu, 0, wndPtr->rectClient.left,
943 wndPtr->rectClient.top - menu->Height - 2*SYSMETRICS_CYBORDER);
944 if (selectFirst) MENU_SelectNextItem( hwndOwner, wndPtr->hSysMenu );
945 return wndPtr->hSysMenu;
947 item = ((MENUITEM *)USER_HEAP_LIN_ADDR(menu->hItems)) + menu->FocusedItem;
948 if (!(item->item_flags & MF_POPUP) ||
949 (item->item_flags & (MF_GRAYED | MF_DISABLED))) return hmenu;
950 item->item_flags |= MF_MOUSESELECT;
951 if (menu->wFlags & MF_POPUP)
953 MENU_ShowPopup( hwndOwner, (HMENU)item->item_id, menu->FocusedItem,
954 wndPtr->rectWindow.left + item->rect.right-arrow_bitmap_width,
955 wndPtr->rectWindow.top + item->rect.top );
957 else
959 MENU_ShowPopup( hwndOwner, (HMENU)item->item_id, menu->FocusedItem,
960 wndPtr->rectWindow.left + item->rect.left,
961 wndPtr->rectWindow.top + item->rect.bottom );
963 if (selectFirst) MENU_SelectNextItem( hwndOwner, (HMENU)item->item_id );
964 return (HMENU)item->item_id;
968 /***********************************************************************
969 * MENU_FindMenuByCoords
971 * Find the menu containing a given point (in screen coords).
973 static HMENU MENU_FindMenuByCoords( HMENU hmenu, POINT pt )
975 POPUPMENU *menu;
976 HWND hwnd;
978 if (!(hwnd = WindowFromPoint( pt ))) return 0;
979 while (hmenu)
981 menu = (POPUPMENU *) USER_HEAP_LIN_ADDR( hmenu );
982 if (menu->hWnd == hwnd)
984 if (!(menu->wFlags & MF_POPUP))
986 /* Make sure it's in the menu bar (or in system menu) */
987 WND *wndPtr = WIN_FindWndPtr( menu->hWnd );
988 if ((pt.x < wndPtr->rectClient.left) ||
989 (pt.x >= wndPtr->rectClient.right) ||
990 (pt.y >= wndPtr->rectClient.top)) return 0;
991 if (pt.y < wndPtr->rectClient.top - menu->Height)
993 if (!MENU_IsInSysMenu( menu, pt )) return 0;
995 /* else it's in the menu bar */
997 return hmenu;
999 hmenu = MENU_GetSubPopup( hmenu );
1001 return 0;
1005 /***********************************************************************
1006 * MENU_ExecFocusedItem
1008 * Execute a menu item (for instance when user pressed Enter).
1009 * Return TRUE if we can go on with menu tracking.
1011 static BOOL MENU_ExecFocusedItem( HWND hwndOwner, HMENU hmenu,
1012 HMENU *hmenuCurrent )
1014 MENUITEM *item;
1015 POPUPMENU *menu = (POPUPMENU *) USER_HEAP_LIN_ADDR( hmenu );
1016 if (!menu || !menu->nItems || (menu->FocusedItem == NO_SELECTED_ITEM) ||
1017 (menu->FocusedItem == SYSMENU_SELECTED)) return TRUE;
1018 item = ((MENUITEM *)USER_HEAP_LIN_ADDR(menu->hItems)) + menu->FocusedItem;
1019 if (!(item->item_flags & MF_POPUP))
1021 if (!(item->item_flags & (MF_GRAYED | MF_DISABLED)))
1023 PostMessage( hwndOwner, (menu->wFlags & MF_SYSMENU) ?
1024 WM_SYSCOMMAND : WM_COMMAND, item->item_id, 0 );
1025 return FALSE;
1027 else return TRUE;
1029 else
1031 *hmenuCurrent = MENU_ShowSubPopup( hwndOwner, hmenu, TRUE );
1032 return TRUE;
1037 /***********************************************************************
1038 * MENU_ButtonDown
1040 * Handle a button-down event in a menu. Point is in screen coords.
1041 * hmenuCurrent is the top-most visible popup.
1042 * Return TRUE if we can go on with menu tracking.
1044 static BOOL MENU_ButtonDown( HWND hwndOwner, HMENU hmenu, HMENU *hmenuCurrent,
1045 POINT pt )
1047 POPUPMENU *menu;
1048 MENUITEM *item;
1049 UINT id;
1051 if (!hmenu) return FALSE; /* Outside all menus */
1052 menu = (POPUPMENU *) USER_HEAP_LIN_ADDR( hmenu );
1053 item = MENU_FindItemByCoords( menu, pt.x, pt.y, &id );
1054 if (!item) /* Maybe in system menu */
1056 if (!MENU_IsInSysMenu( menu, pt )) return FALSE;
1057 id = SYSMENU_SELECTED;
1060 if (menu->FocusedItem == id)
1062 if (id == SYSMENU_SELECTED) return FALSE;
1063 if (item->item_flags & MF_POPUP)
1065 if (item->item_flags & MF_MOUSESELECT)
1067 if (menu->wFlags & MF_POPUP)
1069 MENU_HideSubPopups( hwndOwner, hmenu );
1070 *hmenuCurrent = hmenu;
1072 else return FALSE;
1074 else *hmenuCurrent = MENU_ShowSubPopup( hwndOwner, hmenu, FALSE );
1077 else
1079 MENU_HideSubPopups( hwndOwner, hmenu );
1080 MENU_SelectItem( hwndOwner, hmenu, id );
1081 *hmenuCurrent = MENU_ShowSubPopup( hwndOwner, hmenu, FALSE );
1083 return TRUE;
1087 /***********************************************************************
1088 * MENU_ButtonUp
1090 * Handle a button-up event in a menu. Point is in screen coords.
1091 * hmenuCurrent is the top-most visible popup.
1092 * Return TRUE if we can go on with menu tracking.
1094 static BOOL MENU_ButtonUp( HWND hwndOwner, HMENU hmenu, HMENU *hmenuCurrent,
1095 POINT pt )
1097 POPUPMENU *menu;
1098 MENUITEM *item;
1099 HMENU hsubmenu = 0;
1100 UINT id;
1102 if (!hmenu) return FALSE; /* Outside all menus */
1103 menu = (POPUPMENU *) USER_HEAP_LIN_ADDR( hmenu );
1104 item = MENU_FindItemByCoords( menu, pt.x, pt.y, &id );
1105 if (!item) /* Maybe in system menu */
1107 if (!MENU_IsInSysMenu( menu, pt )) return FALSE;
1108 id = SYSMENU_SELECTED;
1109 hsubmenu = GetSystemMenu( menu->hWnd, FALSE );
1112 if (menu->FocusedItem != id) return FALSE;
1114 if (id != SYSMENU_SELECTED)
1116 if (!(item->item_flags & MF_POPUP))
1118 return MENU_ExecFocusedItem( hwndOwner, hmenu, hmenuCurrent );
1120 hsubmenu = (HMENU)item->item_id;
1122 /* Select first item of sub-popup */
1123 MENU_SelectItem( hwndOwner, hsubmenu, NO_SELECTED_ITEM );
1124 MENU_SelectNextItem( hwndOwner, hsubmenu );
1125 return TRUE;
1129 /***********************************************************************
1130 * MENU_MouseMove
1132 * Handle a motion event in a menu. Point is in screen coords.
1133 * hmenuCurrent is the top-most visible popup.
1134 * Return TRUE if we can go on with menu tracking.
1136 static BOOL MENU_MouseMove( HWND hwndOwner, HMENU hmenu, HMENU *hmenuCurrent,
1137 POINT pt )
1139 MENUITEM *item;
1140 POPUPMENU *menu = (POPUPMENU *) USER_HEAP_LIN_ADDR( hmenu );
1141 UINT id = NO_SELECTED_ITEM;
1143 if (hmenu)
1145 item = MENU_FindItemByCoords( menu, pt.x, pt.y, &id );
1146 if (!item) /* Maybe in system menu */
1148 if (!MENU_IsInSysMenu( menu, pt ))
1149 id = NO_SELECTED_ITEM; /* Outside all items */
1150 else id = SYSMENU_SELECTED;
1153 if (id == NO_SELECTED_ITEM)
1155 MENU_SelectItem( hwndOwner, *hmenuCurrent, NO_SELECTED_ITEM );
1157 else if (menu->FocusedItem != id)
1159 MENU_HideSubPopups( hwndOwner, hmenu );
1160 MENU_SelectItem( hwndOwner, hmenu, id );
1161 *hmenuCurrent = MENU_ShowSubPopup( hwndOwner, hmenu, FALSE );
1163 return TRUE;
1167 /***********************************************************************
1168 * MENU_KeyLeft
1170 * Handle a VK_LEFT key event in a menu.
1171 * hmenuCurrent is the top-most visible popup.
1173 static void MENU_KeyLeft( HWND hwndOwner, HMENU hmenu, HMENU *hmenuCurrent )
1175 POPUPMENU *menu;
1176 HMENU hmenutmp, hmenuprev;
1178 menu = (POPUPMENU *) USER_HEAP_LIN_ADDR( hmenu );
1179 hmenuprev = hmenutmp = hmenu;
1180 while (hmenutmp != *hmenuCurrent)
1182 hmenutmp = MENU_GetSubPopup( hmenuprev );
1183 if (hmenutmp != *hmenuCurrent) hmenuprev = hmenutmp;
1185 MENU_HideSubPopups( hwndOwner, hmenuprev );
1187 if ((hmenuprev == hmenu) && !(menu->wFlags & MF_POPUP))
1189 /* Select previous item on the menu bar */
1190 MENU_SelectPrevItem( hwndOwner, hmenu );
1191 if (*hmenuCurrent != hmenu)
1193 /* A popup menu was displayed -> display the next one */
1194 *hmenuCurrent = MENU_ShowSubPopup( hwndOwner, hmenu, TRUE );
1197 else *hmenuCurrent = hmenuprev;
1201 /***********************************************************************
1202 * MENU_KeyRight
1204 * Handle a VK_RIGHT key event in a menu.
1205 * hmenuCurrent is the top-most visible popup.
1207 static void MENU_KeyRight( HWND hwndOwner, HMENU hmenu, HMENU *hmenuCurrent )
1209 POPUPMENU *menu;
1210 HMENU hmenutmp;
1212 menu = (POPUPMENU *) USER_HEAP_LIN_ADDR( hmenu );
1214 if ((menu->wFlags & MF_POPUP) || (*hmenuCurrent != hmenu))
1216 /* If already displaying a popup, try to display sub-popup */
1217 hmenutmp = MENU_ShowSubPopup( hwndOwner, *hmenuCurrent, TRUE );
1218 if (hmenutmp != *hmenuCurrent) /* Sub-popup displayed */
1220 *hmenuCurrent = hmenutmp;
1221 return;
1225 /* If on menu-bar, go to next item */
1226 if (!(menu->wFlags & MF_POPUP))
1228 MENU_HideSubPopups( hwndOwner, hmenu );
1229 MENU_SelectNextItem( hwndOwner, hmenu );
1230 if (*hmenuCurrent != hmenu)
1232 /* A popup menu was displayed -> display the next one */
1233 *hmenuCurrent = MENU_ShowSubPopup( hwndOwner, hmenu, TRUE );
1236 else if (*hmenuCurrent != hmenu) /* Hide last level popup */
1238 HMENU hmenuprev;
1239 hmenuprev = hmenutmp = hmenu;
1240 while (hmenutmp != *hmenuCurrent)
1242 hmenutmp = MENU_GetSubPopup( hmenuprev );
1243 if (hmenutmp != *hmenuCurrent) hmenuprev = hmenutmp;
1245 MENU_HideSubPopups( hwndOwner, hmenuprev );
1246 *hmenuCurrent = hmenuprev;
1251 /***********************************************************************
1252 * MENU_TrackMenu
1254 * Menu tracking code.
1255 * If 'x' and 'y' are not 0, we simulate a button-down event at (x,y)
1256 * before beginning tracking. This is to help menu-bar tracking.
1258 static BOOL MENU_TrackMenu( HMENU hmenu, UINT wFlags, int x, int y,
1259 HWND hwnd, LPRECT lprect )
1261 MSG *msg;
1262 HLOCAL hMsg;
1263 POPUPMENU *menu;
1264 HMENU hmenuCurrent = hmenu;
1265 BOOL fClosed = FALSE, fRemove;
1266 UINT pos;
1268 fEndMenuCalled = FALSE;
1269 if (!(menu = (POPUPMENU *) USER_HEAP_LIN_ADDR( hmenu ))) return FALSE;
1270 if (x && y)
1272 POINT pt = { x, y };
1273 MENU_ButtonDown( hwnd, hmenu, &hmenuCurrent, pt );
1275 SetCapture( hwnd );
1276 hMsg = USER_HEAP_ALLOC( sizeof(MSG) );
1277 msg = (MSG *)USER_HEAP_LIN_ADDR( hMsg );
1278 while (!fClosed)
1280 if (!MSG_InternalGetMessage( (SEGPTR)USER_HEAP_SEG_ADDR(hMsg), 0,
1281 hwnd, MSGF_MENU, 0, TRUE ))
1282 break;
1284 fRemove = FALSE;
1285 if ((msg->message >= WM_MOUSEFIRST) && (msg->message <= WM_MOUSELAST))
1287 /* Find the sub-popup for this mouse event (if any) */
1288 HMENU hsubmenu = MENU_FindMenuByCoords( hmenu, msg->pt );
1290 switch(msg->message)
1292 case WM_RBUTTONDOWN:
1293 case WM_NCRBUTTONDOWN:
1294 if (!(wFlags & TPM_RIGHTBUTTON)) break;
1295 /* fall through */
1296 case WM_LBUTTONDOWN:
1297 case WM_NCLBUTTONDOWN:
1298 fClosed = !MENU_ButtonDown( hwnd, hsubmenu,
1299 &hmenuCurrent, msg->pt );
1300 break;
1302 case WM_RBUTTONUP:
1303 case WM_NCRBUTTONUP:
1304 if (!(wFlags & TPM_RIGHTBUTTON)) break;
1305 /* fall through */
1306 case WM_LBUTTONUP:
1307 case WM_NCLBUTTONUP:
1308 /* If outside all menus but inside lprect, ignore it */
1309 if (!hsubmenu && lprect && PtInRect( lprect, msg->pt )) break;
1310 fClosed = !MENU_ButtonUp( hwnd, hsubmenu,
1311 &hmenuCurrent, msg->pt );
1312 fRemove = TRUE; /* Remove event even if outside menu */
1313 break;
1315 case WM_MOUSEMOVE:
1316 case WM_NCMOUSEMOVE:
1317 if ((msg->wParam & MK_LBUTTON) ||
1318 ((wFlags & TPM_RIGHTBUTTON) && (msg->wParam & MK_RBUTTON)))
1320 fClosed = !MENU_MouseMove( hwnd, hsubmenu,
1321 &hmenuCurrent, msg->pt );
1323 break;
1326 else if ((msg->message >= WM_KEYFIRST) && (msg->message <= WM_KEYLAST))
1328 fRemove = TRUE; /* Keyboard messages are always removed */
1329 switch(msg->message)
1331 case WM_KEYDOWN:
1332 switch(msg->wParam)
1334 case VK_HOME:
1335 MENU_SelectItem( hwnd, hmenuCurrent, NO_SELECTED_ITEM );
1336 MENU_SelectNextItem( hwnd, hmenuCurrent );
1337 break;
1339 case VK_END:
1340 MENU_SelectItem( hwnd, hmenuCurrent, NO_SELECTED_ITEM );
1341 MENU_SelectPrevItem( hwnd, hmenuCurrent );
1342 break;
1344 case VK_UP:
1345 MENU_SelectPrevItem( hwnd, hmenuCurrent );
1346 break;
1348 case VK_DOWN:
1349 /* If on menu bar, pull-down the menu */
1350 if (!(menu->wFlags & MF_POPUP) && (hmenuCurrent == hmenu))
1351 hmenuCurrent = MENU_ShowSubPopup( hwnd, hmenu, TRUE );
1352 else
1353 MENU_SelectNextItem( hwnd, hmenuCurrent );
1354 break;
1356 case VK_LEFT:
1357 MENU_KeyLeft( hwnd, hmenu, &hmenuCurrent );
1358 break;
1360 case VK_RIGHT:
1361 MENU_KeyRight( hwnd, hmenu, &hmenuCurrent );
1362 break;
1364 case VK_SPACE:
1365 case VK_RETURN:
1366 fClosed = !MENU_ExecFocusedItem( hwnd, hmenuCurrent,
1367 &hmenuCurrent );
1368 break;
1370 case VK_ESCAPE:
1371 fClosed = TRUE;
1372 break;
1374 default:
1375 break;
1377 break; /* WM_KEYDOWN */
1379 case WM_SYSKEYDOWN:
1380 switch(msg->wParam)
1382 case VK_MENU:
1383 fClosed = TRUE;
1384 break;
1387 break; /* WM_SYSKEYDOWN */
1389 case WM_CHAR:
1391 /* Hack to avoid control chars. */
1392 /* We will find a better way real soon... */
1393 if ((msg->wParam <= 32) || (msg->wParam >= 127)) break;
1394 pos = MENU_FindItemByKey( hwnd, hmenuCurrent, msg->wParam );
1395 if (pos == (UINT)-2) fClosed = TRUE;
1396 else if (pos == (UINT)-1) MessageBeep(0);
1397 else
1399 MENU_SelectItem( hwnd, hmenuCurrent, pos );
1400 fClosed = !MENU_ExecFocusedItem( hwnd, hmenuCurrent,
1401 &hmenuCurrent );
1405 break; /* WM_CHAR */
1406 } /* switch(msg->message) */
1408 else
1410 DispatchMessage( msg );
1412 if (fEndMenuCalled) fClosed = TRUE;
1413 if (!fClosed) fRemove = TRUE;
1415 if (fRemove) /* Remove the message from the queue */
1416 PeekMessage( msg, 0, msg->message, msg->message, PM_REMOVE );
1418 USER_HEAP_FREE( hMsg );
1419 ReleaseCapture();
1420 MENU_HideSubPopups( hwnd, hmenu );
1421 if (menu->wFlags & MF_POPUP) ShowWindow( menu->hWnd, SW_HIDE );
1422 MENU_SelectItem( hwnd, hmenu, NO_SELECTED_ITEM );
1423 SendMessage( hwnd, WM_MENUSELECT, 0, MAKELONG( 0xffff, 0 ) );
1424 fEndMenuCalled = FALSE;
1425 return TRUE;
1429 /***********************************************************************
1430 * MENU_TrackMouseMenuBar
1432 * Menu-bar tracking upon a mouse event. Called from NC_HandleSysCommand().
1434 void MENU_TrackMouseMenuBar( HWND hwnd, POINT pt )
1436 WND *wndPtr = WIN_FindWndPtr( hwnd );
1437 SendMessage( hwnd, WM_ENTERMENULOOP, 0, 0 );
1438 SendMessage( hwnd, WM_INITMENU, wndPtr->wIDmenu, 0 );
1439 MENU_TrackMenu( (HMENU)wndPtr->wIDmenu, TPM_LEFTALIGN | TPM_LEFTBUTTON,
1440 pt.x, pt.y, hwnd, NULL );
1441 SendMessage( hwnd, WM_EXITMENULOOP, 0, 0 );
1445 /***********************************************************************
1446 * MENU_TrackKbdMenuBar
1448 * Menu-bar tracking upon a keyboard event. Called from NC_HandleSysCommand().
1450 void MENU_TrackKbdMenuBar( HWND hwnd, UINT wParam )
1452 WND *wndPtr = WIN_FindWndPtr( hwnd );
1453 if (!wndPtr->wIDmenu) return;
1454 SendMessage( hwnd, WM_ENTERMENULOOP, 0, 0 );
1455 SendMessage( hwnd, WM_INITMENU, wndPtr->wIDmenu, 0 );
1456 /* Select first selectable item */
1457 MENU_SelectItem( hwnd, wndPtr->wIDmenu, NO_SELECTED_ITEM );
1458 MENU_SelectNextItem( hwnd, (HMENU)wndPtr->wIDmenu );
1459 MENU_TrackMenu( (HMENU)wndPtr->wIDmenu, TPM_LEFTALIGN | TPM_LEFTBUTTON,
1460 0, 0, hwnd, NULL );
1461 SendMessage( hwnd, WM_EXITMENULOOP, 0, 0 );
1465 /**********************************************************************
1466 * TrackPopupMenu (USER.416)
1468 BOOL TrackPopupMenu( HMENU hMenu, UINT wFlags, short x, short y,
1469 short nReserved, HWND hWnd, LPRECT lpRect )
1471 if (!MENU_ShowPopup( hWnd, hMenu, 0, x, y )) return FALSE;
1472 return MENU_TrackMenu( hMenu, wFlags, 0, 0, hWnd, lpRect );
1476 /***********************************************************************
1477 * PopupMenuWndProc
1479 LRESULT PopupMenuWndProc(HWND hwnd,UINT message,WPARAM wParam,LPARAM lParam)
1481 switch(message)
1483 case WM_CREATE:
1485 CREATESTRUCT *createStruct = (CREATESTRUCT*)PTR_SEG_TO_LIN(lParam);
1486 #ifdef WINELIB32
1487 HMENU hmenu = (HMENU) (createStruct->lpCreateParams);
1488 SetWindowLong( hwnd, 0, (LONG)hmenu );
1489 #else
1490 HMENU hmenu = (HMENU) ((int)createStruct->lpCreateParams & 0xffff);
1491 SetWindowWord( hwnd, 0, hmenu );
1492 #endif
1493 return 0;
1496 case WM_MOUSEACTIVATE: /* We don't want to be activated */
1497 return MA_NOACTIVATE;
1499 case WM_PAINT:
1501 PAINTSTRUCT ps;
1502 BeginPaint( hwnd, &ps );
1503 MENU_DrawPopupMenu( hwnd, ps.hdc,
1504 #ifdef WINELIB32
1505 (HMENU)GetWindowLong( hwnd, 0 )
1506 #else
1507 (HMENU)GetWindowWord( hwnd, 0 )
1508 #endif
1510 EndPaint( hwnd, &ps );
1511 return 0;
1514 default:
1515 return DefWindowProc(hwnd, message, wParam, lParam);
1517 return 0;
1521 /***********************************************************************
1522 * MENU_GetMenuBarHeight
1524 * Compute the size of the menu bar height. Used by NC_HandleNCCalcSize().
1526 UINT MENU_GetMenuBarHeight( HWND hwnd, UINT menubarWidth, int orgX, int orgY )
1528 HDC hdc;
1529 RECT rectBar;
1530 WND *wndPtr;
1531 LPPOPUPMENU lppop;
1533 if (!(wndPtr = WIN_FindWndPtr( hwnd ))) return 0;
1534 if (!(lppop = (LPPOPUPMENU)USER_HEAP_LIN_ADDR((HMENU)wndPtr->wIDmenu)))
1535 return 0;
1536 hdc = GetDC( hwnd );
1537 SetRect( &rectBar, orgX, orgY, orgX+menubarWidth, orgY+SYSMETRICS_CYMENU );
1538 MENU_MenuBarCalcSize( hdc, &rectBar, lppop, hwnd );
1539 ReleaseDC( hwnd, hdc );
1540 return lppop->Height;
1544 /**********************************************************************
1545 * ChangeMenu [USER.153]
1547 BOOL ChangeMenu(HMENU hMenu, UINT nPos, LPSTR lpNewItem,
1548 UINT wItemID, UINT wFlags)
1550 dprintf_menu(stddeb,"ChangeMenu: menu="NPFMT" pos=%d ptr=%p item=%04x flags=%04x\n",
1551 hMenu, nPos, lpNewItem, wItemID, wFlags);
1552 if (wFlags & MF_APPEND) {
1553 return AppendMenu(hMenu, wFlags & ~MF_APPEND, wItemID, lpNewItem);
1555 if (wFlags & MF_DELETE) {
1556 return DeleteMenu(hMenu, wFlags & MF_BYPOSITION ? nPos : wItemID,
1557 wFlags & ~MF_DELETE);
1559 if (wFlags & MF_CHANGE) {
1560 return ModifyMenu(hMenu, nPos, wFlags & ~MF_CHANGE, wItemID, lpNewItem);
1562 if (wFlags & MF_REMOVE) {
1563 return RemoveMenu(hMenu, wFlags & MF_BYPOSITION ? nPos : wItemID,
1564 wFlags & ~MF_REMOVE);
1566 /* Default: MF_INSERT */
1567 return InsertMenu(hMenu, nPos, wFlags, wItemID, lpNewItem);
1571 /**********************************************************************
1572 * CheckMenuItem [USER.154]
1574 BOOL CheckMenuItem(HMENU hMenu, UINT wItemID, UINT wFlags)
1576 LPMENUITEM lpitem;
1577 dprintf_menu(stddeb,"CheckMenuItem ("NPFMT", %04X, %04X) !\n",
1578 hMenu, wItemID, wFlags);
1579 if (!(lpitem = MENU_FindItem(&hMenu, &wItemID, wFlags))) return FALSE;
1580 if (wFlags & MF_CHECKED) lpitem->item_flags |= MF_CHECKED;
1581 else lpitem->item_flags &= ~MF_CHECKED;
1582 return TRUE;
1586 /**********************************************************************
1587 * EnableMenuItem [USER.155]
1589 BOOL EnableMenuItem(HMENU hMenu, UINT wItemID, UINT wFlags)
1591 LPMENUITEM lpitem;
1592 dprintf_menu(stddeb,"EnableMenuItem ("NPFMT", %04X, %04X) !\n",
1593 hMenu, wItemID, wFlags);
1594 if (!(lpitem = MENU_FindItem( &hMenu, &wItemID, wFlags ))) return FALSE;
1596 /* We can't have MF_GRAYED and MF_DISABLED together */
1597 if (wFlags & MF_GRAYED)
1599 lpitem->item_flags = (lpitem->item_flags & ~MF_DISABLED) | MF_GRAYED;
1601 else if (wFlags & MF_DISABLED)
1603 lpitem->item_flags = (lpitem->item_flags & ~MF_GRAYED) | MF_DISABLED;
1605 else /* MF_ENABLED */
1607 lpitem->item_flags &= ~(MF_GRAYED | MF_DISABLED);
1609 return TRUE;
1613 /*******************************************************************
1614 * GetMenuString (USER.161)
1616 int GetMenuString( HMENU hMenu, UINT wItemID,
1617 LPSTR str, short nMaxSiz, UINT wFlags )
1619 LPMENUITEM lpitem;
1621 dprintf_menu( stddeb, "GetMenuString: menu="NPFMT" item=%04x ptr=%p len=%d flags=%04x\n",
1622 hMenu, wItemID, str, nMaxSiz, wFlags );
1623 if (!str || !nMaxSiz) return 0;
1624 str[0] = '\0';
1625 if (!(lpitem = MENU_FindItem( &hMenu, &wItemID, wFlags ))) return 0;
1626 if (!lpitem->item_text || !IS_STRING_ITEM(lpitem->item_flags)) return 0;
1627 nMaxSiz = MIN( nMaxSiz-1, strlen(lpitem->item_text) );
1628 strncpy( str, lpitem->item_text, nMaxSiz );
1629 str[nMaxSiz] = '\0';
1630 dprintf_menu( stddeb, "GetMenuString: returning '%s'\n", str );
1631 return nMaxSiz;
1635 /**********************************************************************
1636 * HiliteMenuItem [USER.162]
1638 BOOL HiliteMenuItem(HWND hWnd, HMENU hMenu, UINT wItemID, UINT wHilite)
1640 LPPOPUPMENU menu;
1641 LPMENUITEM lpitem;
1642 dprintf_menu(stddeb,"HiliteMenuItem("NPFMT", "NPFMT", %04X, %04X);\n",
1643 hWnd, hMenu, wItemID, wHilite);
1644 if (!(lpitem = MENU_FindItem( &hMenu, &wItemID, wHilite ))) return FALSE;
1645 if (!(menu = (LPPOPUPMENU) USER_HEAP_LIN_ADDR(hMenu))) return FALSE;
1646 if (menu->FocusedItem == wItemID) return TRUE;
1647 MENU_HideSubPopups( hWnd, hMenu );
1648 MENU_SelectItem( hWnd, hMenu, wItemID );
1649 return TRUE;
1653 /**********************************************************************
1654 * GetMenuState [USER.250]
1656 UINT GetMenuState(HMENU hMenu, UINT wItemID, UINT wFlags)
1658 LPMENUITEM lpitem;
1659 dprintf_menu(stddeb,"GetMenuState("NPFMT", %04X, %04X);\n",
1660 hMenu, wItemID, wFlags);
1661 if (!(lpitem = MENU_FindItem( &hMenu, &wItemID, wFlags ))) return -1;
1662 if (lpitem->item_flags & MF_POPUP)
1664 POPUPMENU *menu = (POPUPMENU *) USER_HEAP_LIN_ADDR( (HMENU)lpitem->item_id );
1665 if (!menu) return -1;
1666 else return (menu->nItems << 8) | (menu->wFlags & 0xff);
1668 else return lpitem->item_flags;
1672 /**********************************************************************
1673 * GetMenuItemCount [USER.263]
1675 WORD GetMenuItemCount(HMENU hMenu)
1677 LPPOPUPMENU menu;
1678 dprintf_menu(stddeb,"GetMenuItemCount("NPFMT");\n", hMenu);
1679 menu = (LPPOPUPMENU) USER_HEAP_LIN_ADDR(hMenu);
1680 if (menu == NULL) return (UINT)-1;
1681 dprintf_menu(stddeb,"GetMenuItemCount("NPFMT") return %d \n",
1682 hMenu, menu->nItems);
1683 return menu->nItems;
1687 /**********************************************************************
1688 * GetMenuItemID [USER.264]
1690 UINT GetMenuItemID(HMENU hMenu, int nPos)
1692 LPPOPUPMENU menu;
1693 MENUITEM *item;
1695 dprintf_menu(stddeb,"GetMenuItemID("NPFMT", %d);\n", hMenu, nPos);
1696 if (!(menu = (LPPOPUPMENU) USER_HEAP_LIN_ADDR(hMenu))) return -1;
1697 if ((nPos < 0) || (nPos >= menu->nItems)) return -1;
1698 item = (MENUITEM *) USER_HEAP_LIN_ADDR( menu->hItems );
1699 if (item[nPos].item_flags & MF_POPUP) return -1;
1700 return item[nPos].item_id;
1704 /**********************************************************************
1705 * InsertMenu [USER.410]
1707 BOOL InsertMenu(HMENU hMenu, UINT nPos, UINT wFlags, UINT wItemID, LPSTR lpNewItem)
1709 HANDLE hNewItems;
1710 MENUITEM *lpitem, *newItems;
1711 LPPOPUPMENU menu;
1713 if (IS_STRING_ITEM(wFlags))
1715 dprintf_menu(stddeb,"InsertMenu ("NPFMT", %04X, %04X, %04X, '%s') !\n",
1716 hMenu, nPos, wFlags, wItemID,
1717 lpNewItem ? lpNewItem : "(null)");
1718 if (!lpNewItem) return FALSE;
1720 else
1721 dprintf_menu(stddeb,"InsertMenu ("NPFMT", %04X, %04X, %04X, %p) !\n",
1722 hMenu, nPos, wFlags, wItemID, lpNewItem);
1724 /* Find where to insert new item */
1726 if ((wFlags & MF_BYPOSITION) &&
1727 ((nPos == (UINT)-1) || (nPos == GetMenuItemCount(hMenu))))
1729 /* Special case: append to menu
1730 Some programs specify the menu length to do that */
1731 if (!(menu = (LPPOPUPMENU) USER_HEAP_LIN_ADDR(hMenu)))
1733 dprintf_menu(stddeb,"InsertMenu: "NPFMT" not a menu handle\n", hMenu);
1734 return FALSE;
1736 nPos = menu->nItems;
1738 else
1740 if (!MENU_FindItem( &hMenu, &nPos, wFlags ))
1742 dprintf_menu(stddeb,"InsertMenu: Item %X not found\n", nPos);
1743 return FALSE;
1745 if (!(menu = (LPPOPUPMENU) USER_HEAP_LIN_ADDR(hMenu)))
1747 dprintf_menu(stddeb,"InsertMenu: "NPFMT" not a menu handle\n", hMenu);
1748 return FALSE;
1752 /* Create new items array */
1754 hNewItems = USER_HEAP_ALLOC( sizeof(MENUITEM) * (menu->nItems+1) );
1755 if (!hNewItems)
1757 dprintf_menu(stddeb,"InsertMenu: allocation failed\n");
1758 return FALSE;
1760 newItems = (MENUITEM *) USER_HEAP_LIN_ADDR( hNewItems );
1761 if (menu->nItems > 0)
1763 /* Copy the old array into the new */
1764 MENUITEM *oldItems = (MENUITEM *) USER_HEAP_LIN_ADDR( menu->hItems );
1765 if (nPos > 0) memcpy( newItems, oldItems, nPos * sizeof(MENUITEM) );
1766 if (nPos < menu->nItems) memcpy( &newItems[nPos+1], &oldItems[nPos],
1767 (menu->nItems-nPos)*sizeof(MENUITEM) );
1769 USER_HEAP_FREE( menu->hItems );
1771 menu->hItems = hNewItems;
1772 menu->nItems++;
1774 /* Store the new item data */
1776 lpitem = &newItems[nPos];
1777 lpitem->item_flags = wFlags & ~(MF_HILITE | MF_MOUSESELECT);
1778 lpitem->item_id = wItemID;
1780 if (IS_STRING_ITEM(wFlags))
1782 /* Item beginning with a backspace is a help item */
1783 if (lpNewItem[0] == '\b')
1785 lpitem->item_flags |= MF_HELP;
1786 lpNewItem++;
1788 lpitem->hText = USER_HEAP_ALLOC( strlen(lpNewItem)+1 );
1789 lpitem->item_text = (char *)USER_HEAP_LIN_ADDR( lpitem->hText );
1790 strcpy( lpitem->item_text, lpNewItem );
1792 #ifdef WINELIB32
1793 else if (wFlags & MF_BITMAP) lpitem->hText = (HANDLE)lpNewItem;
1794 #else
1795 else if (wFlags & MF_BITMAP) lpitem->hText = LOWORD((DWORD)lpNewItem);
1796 #endif
1797 else lpitem->item_text = lpNewItem;
1799 if (wFlags & MF_POPUP) /* Set the MF_POPUP flag on the popup-menu */
1800 ((POPUPMENU *)USER_HEAP_LIN_ADDR((HMENU)wItemID))->wFlags |= MF_POPUP;
1802 SetRectEmpty( &lpitem->rect );
1803 lpitem->hCheckBit = hStdCheck;
1804 lpitem->hUnCheckBit = 0;
1805 return TRUE;
1809 /**********************************************************************
1810 * AppendMenu [USER.411]
1812 BOOL AppendMenu(HMENU hMenu, UINT wFlags, UINT wItemID, LPSTR lpNewItem)
1814 return InsertMenu( hMenu, -1, wFlags | MF_BYPOSITION, wItemID, lpNewItem );
1818 /**********************************************************************
1819 * RemoveMenu [USER.412]
1821 BOOL RemoveMenu(HMENU hMenu, UINT nPos, UINT wFlags)
1823 LPPOPUPMENU menu;
1824 LPMENUITEM lpitem;
1825 dprintf_menu(stddeb,"RemoveMenu ("NPFMT", %04X, %04X) !\n",
1826 hMenu, nPos, wFlags);
1827 if (!(lpitem = MENU_FindItem( &hMenu, &nPos, wFlags ))) return FALSE;
1828 if (!(menu = (LPPOPUPMENU) USER_HEAP_LIN_ADDR(hMenu))) return FALSE;
1830 /* Remove item */
1832 if (IS_STRING_ITEM(lpitem->item_flags)) USER_HEAP_FREE( lpitem->hText );
1833 if (--menu->nItems == 0)
1835 USER_HEAP_FREE( menu->hItems );
1836 menu->hItems = 0;
1838 else
1840 while(nPos < menu->nItems)
1842 *lpitem = *(lpitem+1);
1843 lpitem++;
1844 nPos++;
1846 menu->hItems = USER_HEAP_REALLOC( menu->hItems,
1847 menu->nItems * sizeof(MENUITEM) );
1849 return TRUE;
1853 /**********************************************************************
1854 * DeleteMenu [USER.413]
1856 BOOL DeleteMenu(HMENU hMenu, UINT nPos, UINT wFlags)
1858 MENUITEM *item = MENU_FindItem( &hMenu, &nPos, wFlags );
1859 if (!item) return FALSE;
1860 if (item->item_flags & MF_POPUP) DestroyMenu( item->item_id );
1861 /* nPos is now the position of the item */
1862 RemoveMenu( hMenu, nPos, wFlags | MF_BYPOSITION );
1863 return TRUE;
1867 /**********************************************************************
1868 * ModifyMenu [USER.414]
1870 BOOL ModifyMenu(HMENU hMenu, UINT nPos, UINT wFlags, UINT wItemID, LPSTR lpNewItem)
1872 LPMENUITEM lpitem;
1873 if (IS_STRING_ITEM(wFlags))
1875 dprintf_menu(stddeb,"ModifyMenu ("NPFMT", %04X, %04X, %04X, '%s') !\n",
1876 hMenu, nPos, wFlags, wItemID, lpNewItem ? lpNewItem : "(null)");
1877 if (!lpNewItem) return FALSE;
1879 else
1880 dprintf_menu(stddeb,"ModifyMenu ("NPFMT", %04X, %04X, %04X, %p) !\n",
1881 hMenu, nPos, wFlags, wItemID, lpNewItem);
1882 if (!(lpitem = MENU_FindItem( &hMenu, &nPos, wFlags ))) return FALSE;
1884 if (IS_STRING_ITEM(lpitem->item_flags)) USER_HEAP_FREE( lpitem->hText );
1885 lpitem->item_flags = wFlags & ~(MF_HILITE | MF_MOUSESELECT);
1886 lpitem->item_id = wItemID;
1888 if (IS_STRING_ITEM(wFlags))
1890 lpitem->hText = USER_HEAP_ALLOC( strlen(lpNewItem)+1 );
1891 lpitem->item_text = (char *)USER_HEAP_LIN_ADDR( lpitem->hText );
1892 strcpy( lpitem->item_text, lpNewItem );
1894 #ifdef WINELIB32
1895 else if (wFlags & MF_BITMAP) lpitem->hText = (HANDLE)lpNewItem;
1896 #else
1897 else if (wFlags & MF_BITMAP) lpitem->hText = LOWORD((DWORD)lpNewItem);
1898 #endif
1899 else lpitem->item_text = lpNewItem;
1900 SetRectEmpty( &lpitem->rect );
1901 return TRUE;
1905 /**********************************************************************
1906 * CreatePopupMenu [USER.415]
1908 HMENU CreatePopupMenu()
1910 HMENU hmenu;
1911 POPUPMENU *menu;
1913 if (!(hmenu = CreateMenu())) return 0;
1914 menu = (POPUPMENU *) USER_HEAP_LIN_ADDR( hmenu );
1915 menu->wFlags |= MF_POPUP;
1916 return hmenu;
1920 /**********************************************************************
1921 * GetMenuCheckMarkDimensions [USER.417]
1923 DWORD GetMenuCheckMarkDimensions()
1925 return MAKELONG( check_bitmap_width, check_bitmap_height );
1929 /**********************************************************************
1930 * SetMenuItemBitmaps [USER.418]
1932 BOOL SetMenuItemBitmaps(HMENU hMenu, UINT nPos, UINT wFlags,
1933 HBITMAP hNewCheck, HBITMAP hNewUnCheck)
1935 LPMENUITEM lpitem;
1936 dprintf_menu(stddeb,"SetMenuItemBitmaps ("NPFMT", %04X, %04X, "NPFMT", %08lX) !\n",
1937 hMenu, nPos, wFlags, hNewCheck, (DWORD)hNewUnCheck);
1938 if (!(lpitem = MENU_FindItem( &hMenu, &nPos, wFlags ))) return FALSE;
1940 if (!hNewCheck && !hNewUnCheck)
1942 /* If both are NULL, restore default bitmaps */
1943 lpitem->hCheckBit = hStdCheck;
1944 lpitem->hUnCheckBit = 0;
1945 lpitem->item_flags &= ~MF_USECHECKBITMAPS;
1947 else /* Install new bitmaps */
1949 lpitem->hCheckBit = hNewCheck;
1950 lpitem->hUnCheckBit = hNewUnCheck;
1951 lpitem->item_flags |= MF_USECHECKBITMAPS;
1953 return TRUE;
1957 /**********************************************************************
1958 * CreateMenu [USER.151]
1960 HMENU CreateMenu()
1962 HMENU hMenu;
1963 LPPOPUPMENU menu;
1964 dprintf_menu(stddeb,"CreateMenu !\n");
1965 if (!(hMenu = USER_HEAP_ALLOC( sizeof(POPUPMENU) )))
1966 return 0;
1967 menu = (LPPOPUPMENU) USER_HEAP_LIN_ADDR(hMenu);
1968 menu->hNext = 0;
1969 menu->wFlags = 0;
1970 menu->wMagic = MENU_MAGIC;
1971 menu->hTaskQ = 0;
1972 menu->Width = 0;
1973 menu->Height = 0;
1974 menu->nItems = 0;
1975 menu->hWnd = 0;
1976 menu->hItems = 0;
1977 menu->FocusedItem = NO_SELECTED_ITEM;
1978 dprintf_menu(stddeb,"CreateMenu // return "NPFMT"\n", hMenu);
1979 return hMenu;
1983 /**********************************************************************
1984 * DestroyMenu [USER.152]
1986 BOOL DestroyMenu(HMENU hMenu)
1988 LPPOPUPMENU lppop;
1989 dprintf_menu(stddeb,"DestroyMenu ("NPFMT") !\n", hMenu);
1990 if (hMenu == 0) return FALSE;
1991 lppop = (LPPOPUPMENU) USER_HEAP_LIN_ADDR(hMenu);
1992 if (!lppop || (lppop->wMagic != MENU_MAGIC)) return FALSE;
1993 lppop->wMagic = 0; /* Mark it as destroyed */
1994 if ((lppop->wFlags & MF_POPUP) && lppop->hWnd)
1995 DestroyWindow( lppop->hWnd );
1997 if (lppop->hItems)
1999 int i;
2000 MENUITEM *item = (MENUITEM *) USER_HEAP_LIN_ADDR( lppop->hItems );
2001 for (i = lppop->nItems; i > 0; i--, item++)
2003 if (item->item_flags & MF_POPUP)
2004 DestroyMenu( (HMENU)item->item_id );
2006 USER_HEAP_FREE( lppop->hItems );
2008 USER_HEAP_FREE( hMenu );
2009 dprintf_menu(stddeb,"DestroyMenu ("NPFMT") // End !\n", hMenu);
2010 return TRUE;
2013 /**********************************************************************
2014 * GetSystemMenu [USER.156]
2016 HMENU GetSystemMenu(HWND hWnd, BOOL bRevert)
2018 WND *wndPtr = WIN_FindWndPtr( hWnd );
2019 if (!wndPtr) return 0;
2021 if (!bRevert) return wndPtr->hSysMenu;
2022 DestroyMenu(wndPtr->hSysMenu);
2023 wndPtr->hSysMenu = CopySysMenu();
2024 return wndPtr->hSysMenu;
2027 /**********************************************************************
2028 * SetSystemMenu [USER.280]
2030 BOOL SetSystemMenu(HWND hWnd, HMENU newHmenu)
2032 WND *wndPtr;
2034 if ((wndPtr = WIN_FindWndPtr(hWnd)) != NULL) wndPtr->hSysMenu = newHmenu;
2035 return TRUE;
2039 /**********************************************************************
2040 * GetMenu [USER.157]
2042 HMENU GetMenu(HWND hWnd)
2044 WND * wndPtr = WIN_FindWndPtr(hWnd);
2045 if (wndPtr == NULL) return 0;
2046 return (HMENU)wndPtr->wIDmenu;
2050 /**********************************************************************
2051 * SetMenu [USER.158]
2053 BOOL SetMenu(HWND hWnd, HMENU hMenu)
2055 LPPOPUPMENU lpmenu;
2056 WND * wndPtr = WIN_FindWndPtr(hWnd);
2057 if (wndPtr == NULL) {
2058 fprintf(stderr,"SetMenu("NPFMT", "NPFMT") // Bad window handle !\n",
2059 hWnd, hMenu);
2060 return FALSE;
2062 dprintf_menu(stddeb,"SetMenu("NPFMT", "NPFMT");\n", hWnd, hMenu);
2063 if (GetCapture() == hWnd) ReleaseCapture();
2064 wndPtr->wIDmenu = (UINT)hMenu;
2065 if (hMenu != 0)
2067 lpmenu = (LPPOPUPMENU) USER_HEAP_LIN_ADDR(hMenu);
2068 if (lpmenu == NULL) {
2069 fprintf(stderr,"SetMenu("NPFMT", "NPFMT") // Bad menu handle !\n",
2070 hWnd, hMenu);
2071 return FALSE;
2073 lpmenu->hWnd = hWnd;
2074 lpmenu->wFlags &= ~MF_POPUP; /* Can't be a popup */
2075 lpmenu->Height = 0; /* Make sure we recalculate the size */
2077 SetWindowPos( hWnd, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE |
2078 SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED );
2079 return TRUE;
2084 /**********************************************************************
2085 * GetSubMenu [USER.159]
2087 HMENU GetSubMenu(HMENU hMenu, short nPos)
2089 LPPOPUPMENU lppop;
2090 LPMENUITEM lpitem;
2091 dprintf_menu(stddeb,"GetSubMenu ("NPFMT", %04X) !\n", hMenu, nPos);
2092 if (!(lppop = (LPPOPUPMENU) USER_HEAP_LIN_ADDR(hMenu))) return 0;
2093 if ((UINT)nPos >= lppop->nItems) return 0;
2094 lpitem = (MENUITEM *) USER_HEAP_LIN_ADDR( lppop->hItems );
2095 if (!(lpitem[nPos].item_flags & MF_POPUP)) return 0;
2096 return lpitem[nPos].item_id;
2100 /**********************************************************************
2101 * DrawMenuBar [USER.160]
2103 void DrawMenuBar(HWND hWnd)
2105 WND *wndPtr;
2106 LPPOPUPMENU lppop;
2107 dprintf_menu(stddeb,"DrawMenuBar ("NPFMT")\n", hWnd);
2108 wndPtr = WIN_FindWndPtr(hWnd);
2109 if (wndPtr != NULL && (wndPtr->dwStyle & WS_CHILD) == 0 &&
2110 wndPtr->wIDmenu != 0) {
2111 dprintf_menu(stddeb,"DrawMenuBar wIDmenu=%04X \n",
2112 wndPtr->wIDmenu);
2113 lppop = (LPPOPUPMENU) USER_HEAP_LIN_ADDR((HMENU)wndPtr->wIDmenu);
2114 if (lppop == NULL) return;
2116 lppop->Height = 0; /* Make sure we call MENU_MenuBarCalcSize */
2117 SetWindowPos( hWnd, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE |
2118 SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED );
2123 /***********************************************************************
2124 * EndMenu (USER.187)
2126 void EndMenu(void)
2128 /* Note: this won't work when we have multiple tasks... */
2129 fEndMenuCalled = TRUE;
2133 /***********************************************************************
2134 * LookupMenuHandle (USER.217)
2136 HMENU LookupMenuHandle( HMENU hmenu, INT id )
2138 if (!MENU_FindItem( &hmenu, &id, MF_BYCOMMAND )) return 0;
2139 else return hmenu;
2143 /**********************************************************************
2144 * LoadMenu (USER.150)
2146 HMENU LoadMenu( HINSTANCE instance, SEGPTR name )
2148 HRSRC hRsrc;
2149 HGLOBAL handle;
2150 HMENU hMenu;
2152 if (HIWORD(name))
2154 char *str = (char *)PTR_SEG_TO_LIN( name );
2155 dprintf_menu( stddeb, "LoadMenu("NPFMT",'%s')\n", instance, str );
2156 if (str[0] == '#') name = (SEGPTR)atoi( str + 1 );
2158 else
2159 dprintf_resource(stddeb,"LoadMenu("NPFMT",%04x)\n",instance,LOWORD(name));
2161 if (!name) return 0;
2163 if (!(hRsrc = FindResource( instance, name, RT_MENU ))) return 0;
2164 if (!(handle = LoadResource( instance, hRsrc ))) return 0;
2165 hMenu = LoadMenuIndirect( LockResource(handle) );
2166 FreeResource( handle );
2167 return hMenu;
2171 /**********************************************************************
2172 * LoadMenuIndirect [USER.220]
2174 HMENU LoadMenuIndirect(LPSTR menu_template)
2176 HMENU hMenu;
2177 MENU_HEADER *menu_desc;
2178 dprintf_menu(stddeb,"LoadMenuIndirect: menu_template '%p'\n",
2179 menu_template);
2180 hMenu = CreateMenu();
2181 menu_desc = (MENU_HEADER *)menu_template;
2182 ParseMenuResource((WORD *)(menu_desc + 1), 0, hMenu);
2183 return hMenu;
2187 /**********************************************************************
2188 * CopySysMenu (Internal)
2190 HMENU CopySysMenu()
2192 HMENU hMenu;
2193 LPPOPUPMENU menu;
2195 hMenu = LoadMenuIndirect( sysres_MENU_SYSMENU.bytes );
2196 if(!hMenu)
2198 dprintf_menu(stddeb,"No SYSMENU\n");
2199 return 0;
2201 menu = (POPUPMENU*) USER_HEAP_LIN_ADDR(hMenu);
2202 menu->wFlags |= MF_SYSMENU|MF_POPUP;
2203 dprintf_menu(stddeb,"CopySysMenu hMenu="NPFMT" !\n", hMenu);
2204 return hMenu;
2208 /**********************************************************************
2209 * ParseMenuResource (from Resource or Template)
2211 WORD * ParseMenuResource(WORD *first_item, int level, HMENU hMenu)
2213 WORD *item;
2214 WORD *next_item;
2215 HMENU hSubMenu;
2216 int i;
2218 level++;
2219 next_item = first_item;
2220 i = 0;
2221 do {
2222 i++;
2223 item = next_item;
2224 if (*item & MF_POPUP) {
2225 MENU_POPUPITEM *popup_item = (MENU_POPUPITEM *) item;
2226 next_item = (WORD *) (popup_item->item_text +
2227 strlen(popup_item->item_text) + 1);
2228 hSubMenu = CreatePopupMenu();
2229 next_item = ParseMenuResource(next_item, level, hSubMenu);
2230 AppendMenu(hMenu, popup_item->item_flags,
2231 (UINT)hSubMenu, popup_item->item_text);
2233 else {
2234 MENUITEMTEMPLATE *normal_item = (MENUITEMTEMPLATE *) item;
2235 next_item = (WORD *) (normal_item->item_text +
2236 strlen(normal_item->item_text) + 1);
2237 if (strlen(normal_item->item_text) == 0 && normal_item->item_id == 0)
2238 normal_item->item_flags |= MF_SEPARATOR;
2239 AppendMenu(hMenu, normal_item->item_flags,
2240 normal_item->item_id, normal_item->item_text);
2243 while (!(*item & MF_END));
2244 return next_item;
2248 /**********************************************************************
2249 * IsMenu (USER.358)
2251 BOOL IsMenu( HMENU hmenu )
2253 LPPOPUPMENU menu;
2254 if (!(menu = (LPPOPUPMENU) USER_HEAP_LIN_ADDR( hmenu ))) return FALSE;
2255 return (menu->wMagic == MENU_MAGIC);