[PATCH v2 2/5] win32u: Move NtUserDrawMenuBarTemp implementation from user32.

Jacek Caban wine at gitlab.winehq.org
Tue Jun 14 02:08:27 CDT 2022


From: Jacek Caban <jacek at codeweavers.com>

Signed-off-by: Jacek Caban <jacek at codeweavers.com>
---
 dlls/user32/menu.c           | 140 +--------
 dlls/user32/user32.spec      |   2 +-
 dlls/win32u/dc.c             |  13 +
 dlls/win32u/defwnd.c         |  77 ++++-
 dlls/win32u/gdiobj.c         |   1 +
 dlls/win32u/menu.c           | 556 ++++++++++++++++++++++++++++++++++-
 dlls/win32u/ntgdi_private.h  |   1 +
 dlls/win32u/win32u.spec      |   2 +-
 dlls/win32u/win32u_private.h |   5 +
 dlls/win32u/wrappers.c       |   6 +
 include/ntuser.h             |   1 +
 11 files changed, 659 insertions(+), 145 deletions(-)

diff --git a/dlls/user32/menu.c b/dlls/user32/menu.c
index e30cb4110d2..1372baca6cc 100644
--- a/dlls/user32/menu.c
+++ b/dlls/user32/menu.c
@@ -119,8 +119,6 @@ static HMENU top_popup_hmenu;
   /* Flag set by EndMenu() to force an exit from menu tracking */
 static BOOL fEndMenu = FALSE;
 
-DWORD WINAPI DrawMenuBarTemp(HWND hwnd, HDC hDC, LPRECT lprect, HMENU hMenu, HFONT hFont);
-
 static BOOL is_win_menu_disallowed(HWND hwnd)
 {
     return (GetWindowLongW(hwnd, GWL_STYLE) & (WS_CHILD | WS_POPUP)) == WS_CHILD;
@@ -1198,81 +1196,6 @@ static void MENU_PopupMenuCalcSize( LPPOPUPMENU lppop, UINT max_height )
 }
 
 
-/***********************************************************************
- *           MENU_MenuBarCalcSize
- *
- * FIXME: Word 6 implements its own MDI and its own 'close window' bitmap
- * height is off by 1 pixel which causes lengthy window relocations when
- * active document window is maximized/restored.
- *
- * Calculate the size of the menu bar.
- */
-static void MENU_MenuBarCalcSize( HDC hdc, LPRECT lprect,
-                                  LPPOPUPMENU lppop, HWND hwndOwner )
-{
-    MENUITEM *lpitem;
-    UINT start, i, helpPos;
-    int orgX, orgY;
-
-    if ((lprect == NULL) || (lppop == NULL)) return;
-    if (lppop->nItems == 0) return;
-    TRACE("lprect %p %s\n", lprect, wine_dbgstr_rect( lprect));
-    /* Start with a 1 pixel top border.
-       This corresponds to the difference between SM_CYMENU and SM_CYMENUSIZE. */
-    SetRect(&lppop->items_rect, 0, 0, lprect->right - lprect->left, 1);
-    start = 0;
-    helpPos = ~0U;
-    lppop->textOffset = 0;
-    while (start < lppop->nItems)
-    {
-	lpitem = &lppop->items[start];
-	orgX = lppop->items_rect.left;
-	orgY = lppop->items_rect.bottom;
-
-	  /* Parse items until line break or end of menu */
-	for (i = start; i < lppop->nItems; i++, lpitem++)
-	{
-	    if ((helpPos == ~0U) && (lpitem->fType & MF_RIGHTJUSTIFY)) helpPos = i;
-	    if ((i != start) &&
-		(lpitem->fType & (MF_MENUBREAK | MF_MENUBARBREAK))) break;
-
-	    TRACE("calling MENU_CalcItemSize org=(%d, %d)\n", orgX, orgY );
-	    debug_print_menuitem ("  item: ", lpitem, "");
-	    MENU_CalcItemSize( hdc, lpitem, hwndOwner, orgX, orgY, TRUE, lppop );
-
-	    if (lpitem->rect.right > lppop->items_rect.right)
-	    {
-		if (i != start) break;
-		else lpitem->rect.right = lppop->items_rect.right;
-	    }
-	    lppop->items_rect.bottom = max( lppop->items_rect.bottom, lpitem->rect.bottom );
-	    orgX = lpitem->rect.right;
-	}
-
-	  /* Finish the line (set all items to the largest height found) */
-	while (start < i) lppop->items[start++].rect.bottom = lppop->items_rect.bottom;
-    }
-
-    OffsetRect(&lppop->items_rect, lprect->left, lprect->top);
-    lppop->Width = lppop->items_rect.right - lppop->items_rect.left;
-    lppop->Height = lppop->items_rect.bottom - lppop->items_rect.top;
-    lprect->bottom = lppop->items_rect.bottom;
-
-    /* Flush right all items between the MF_RIGHTJUSTIFY and */
-    /* the last item (if several lines, only move the last line) */
-    if (helpPos == ~0U) return;
-    lpitem = &lppop->items[lppop->nItems-1];
-    orgY = lpitem->rect.top;
-    orgX = lprect->right - lprect->left;
-    for (i = lppop->nItems - 1; i >= helpPos; i--, lpitem--) {
-        if (lpitem->rect.top != orgY) break;	/* Other line */
-        if (lpitem->rect.right >= orgX) break;	/* Too far right already */
-        lpitem->rect.left += orgX - lpitem->rect.right;
-        lpitem->rect.right = orgX;
-        orgX = lpitem->rect.left;
-    }
-}
-
 static void draw_scroll_arrow(HDC hdc, int x, int top, int height, BOOL up, BOOL enabled)
 {
     RECT rect, light_rect;
@@ -1787,7 +1710,7 @@ UINT MENU_DrawMenuBar( HDC hDC, LPRECT lprect, HWND hwnd )
         return GetSystemMetrics(SM_CYMENU);
     }
 
-    return DrawMenuBarTemp(hwnd, hDC, lprect, hMenu, NULL);
+    return NtUserDrawMenuBarTemp( hwnd, hDC, lprect, hMenu, NULL );
 }
 
 
@@ -4028,67 +3951,6 @@ BOOL WINAPI DrawMenuBar( HWND hwnd )
     return NtUserDrawMenuBar( hwnd );
 }
 
-/***********************************************************************
- *           DrawMenuBarTemp   (USER32.@)
- *
- * UNDOCUMENTED !!
- *
- * called by W98SE desk.cpl Control Panel Applet
- *
- * Not 100% sure about the param names, but close.
- */
-DWORD WINAPI DrawMenuBarTemp(HWND hwnd, HDC hDC, LPRECT lprect, HMENU hMenu, HFONT hFont)
-{
-    LPPOPUPMENU lppop;
-    UINT i,retvalue;
-    HFONT hfontOld = 0;
-    BOOL flat_menu = FALSE;
-
-    SystemParametersInfoW (SPI_GETFLATMENU, 0, &flat_menu, 0);
-
-    if (!hMenu)
-        hMenu = GetMenu(hwnd);
-
-    if (!hFont)
-        hFont = get_menu_font(FALSE);
-
-    lppop = MENU_GetMenu( hMenu );
-    if (lppop == NULL || lprect == NULL)
-    {
-        retvalue = GetSystemMetrics(SM_CYMENU);
-        goto END;
-    }
-
-    TRACE("(%p, %p, %p, %p, %p)\n", hwnd, hDC, lprect, hMenu, hFont);
-
-    hfontOld = SelectObject( hDC, hFont);
-
-    if (lppop->Height == 0)
-        MENU_MenuBarCalcSize(hDC, lprect, lppop, hwnd);
-
-    lprect->bottom = lprect->top + lppop->Height;
-
-    FillRect(hDC, lprect, GetSysColorBrush(flat_menu ? COLOR_MENUBAR : COLOR_MENU) );
-
-    SelectObject( hDC, SYSCOLOR_GetPen(COLOR_3DFACE));
-    MoveToEx( hDC, lprect->left, lprect->bottom, NULL );
-    LineTo( hDC, lprect->right, lprect->bottom );
-
-    if (lppop->nItems == 0)
-    {
-        retvalue = GetSystemMetrics(SM_CYMENU);
-        goto END;
-    }
-
-    for (i = 0; i < lppop->nItems; i++)
-        MENU_DrawMenuItem( hwnd, lppop, hwnd, hDC, &lppop->items[i], TRUE, ODA_DRAWENTIRE );
-
-    retvalue = lppop->Height;
-
-END:
-    if (hfontOld) SelectObject (hDC, hfontOld);
-    return retvalue;
-}
 
 /***********************************************************************
  *           EndMenu   (USER.187)
diff --git a/dlls/user32/user32.spec b/dlls/user32/user32.spec
index b41d661ee2a..1399f488720 100644
--- a/dlls/user32/user32.spec
+++ b/dlls/user32/user32.spec
@@ -194,7 +194,7 @@
 @ stdcall DrawIcon(long long long long)
 @ stdcall DrawIconEx(long long long long long long long long long) NtUserDrawIconEx
 @ stdcall DrawMenuBar(long)
-@ stdcall DrawMenuBarTemp(long long ptr long long)
+@ stdcall DrawMenuBarTemp(long long ptr long long) NtUserDrawMenuBarTemp
 @ stdcall DrawStateA(long long ptr long long long long long long long)
 @ stdcall DrawStateW(long long ptr long long long long long long long)
 @ stdcall DrawTextA(long str long ptr long)
diff --git a/dlls/win32u/dc.c b/dlls/win32u/dc.c
index fc112f0d4b1..d43be1c20be 100644
--- a/dlls/win32u/dc.c
+++ b/dlls/win32u/dc.c
@@ -1103,6 +1103,19 @@ BOOL WINAPI NtGdiSetBrushOrg( HDC hdc, INT x, INT y, POINT *oldorg )
 }
 
 
+BOOL set_viewport_org( HDC hdc, INT x, INT y, POINT *point )
+{
+    DC *dc;
+
+    if (!(dc = get_dc_ptr( hdc ))) return FALSE;
+    if (point) *point = dc->attr->vport_org;
+    dc->attr->vport_org.x = x;
+    dc->attr->vport_org.y = y;
+    release_dc_ptr( dc );
+    return NtGdiComputeXformCoefficients( hdc );
+}
+
+
 /***********************************************************************
  *           NtGdiGetTransform    (win32u.@)
  *
diff --git a/dlls/win32u/defwnd.c b/dlls/win32u/defwnd.c
index 75a7efd2c8d..9a51b9a463a 100644
--- a/dlls/win32u/defwnd.c
+++ b/dlls/win32u/defwnd.c
@@ -138,7 +138,7 @@ static const signed char ltrb_inner_mono[] = {
     -1, COLOR_WINDOW, COLOR_WINDOW, COLOR_WINDOW,
 };
 
-static BOOL draw_rect_edge( HDC hdc, RECT *rc, UINT type, UINT flags, UINT width )
+BOOL draw_rect_edge( HDC hdc, RECT *rc, UINT type, UINT flags, UINT width )
 {
     int lbi_offset = 0, lti_offset = 0, rti_offset = 0, rbi_offset = 0;
     signed char lt_inner, lt_outer, rb_inner, rb_outer;
@@ -1016,7 +1016,7 @@ static void draw_caption_bar( HDC hdc, const RECT *rect, DWORD style, BOOL activ
 }
 
 /* Draw the system icon */
-static BOOL draw_nc_sys_button( HWND hwnd, HDC hdc, BOOL down )
+BOOL draw_nc_sys_button( HWND hwnd, HDC hdc, BOOL down )
 {
     HICON icon = get_nc_icon_for_window( hwnd );
 
@@ -1122,7 +1122,7 @@ static BOOL draw_push_button( HDC dc, RECT *r, UINT flags )
     return TRUE;
 }
 
-static BOOL draw_frame_caption( HDC dc, RECT *r, UINT flags )
+BOOL draw_frame_caption( HDC dc, RECT *r, UINT flags )
 {
     RECT rect;
     int small_diam = make_square_rect( r, &rect ) - 2;
@@ -1181,6 +1181,77 @@ static BOOL draw_frame_caption( HDC dc, RECT *r, UINT flags )
     return TRUE;
 }
 
+BOOL draw_frame_menu( HDC dc, RECT *r, UINT flags )
+{
+    RECT rect;
+    int dmall_diam = make_square_rect( r, &rect );
+    HBRUSH prev_brush;
+    HPEN prev_pen;
+    POINT points[6];
+    int xe, ye;
+    int xc, yc;
+    BOOL retval = TRUE;
+    ULONG count;
+    int i;
+
+    fill_rect( dc, r, GetStockObject( WHITE_BRUSH ));
+
+    prev_brush = NtGdiSelectBrush( dc, GetStockObject( BLACK_BRUSH ));
+    prev_pen = NtGdiSelectPen( dc, GetStockObject( BLACK_PEN ));
+
+    switch (flags & 0xff)
+    {
+    case DFCS_MENUARROW:
+        i = 187 * dmall_diam / 750;
+        points[2].x = rect.left + 468 * dmall_diam/ 750;
+        points[2].y = rect.top  + 352 * dmall_diam/ 750 + 1;
+        points[0].y = points[2].y - i;
+        points[1].y = points[2].y + i;
+        points[0].x = points[1].x = points[2].x - i;
+        count = 3;
+        NtGdiPolyPolyDraw( dc, points, &count, 1, NtGdiPolyPolygon );
+        break;
+
+    case DFCS_MENUBULLET:
+        xe = rect.left;
+        ye = rect.top  + dmall_diam - dmall_diam / 2;
+        xc = rect.left + dmall_diam - dmall_diam / 2;
+        yc = rect.top  + dmall_diam - dmall_diam / 2;
+        i = 234 * dmall_diam / 750;
+        i = i < 1 ? 1 : i;
+        SetRect( &rect, xc - i + i / 2, yc - i + i / 2, xc + i / 2, yc + i / 2 );
+        NtGdiArcInternal( NtGdiPie, dc, rect.left, rect.top, rect.right, rect.bottom,
+                          xe, ye, xe, ye );
+        break;
+
+    case DFCS_MENUCHECK:
+        points[0].x = rect.left + 253 * dmall_diam / 1000;
+        points[0].y = rect.top  + 445 * dmall_diam / 1000;
+        points[1].x = rect.left + 409 * dmall_diam / 1000;
+        points[1].y = points[0].y + (points[1].x - points[0].x);
+        points[2].x = rect.left + 690 * dmall_diam / 1000;
+        points[2].y = points[1].y - (points[2].x - points[1].x);
+        points[3].x = points[2].x;
+        points[3].y = points[2].y + 3 * dmall_diam / 16;
+        points[4].x = points[1].x;
+        points[4].y = points[1].y + 3 * dmall_diam / 16;
+        points[5].x = points[0].x;
+        points[5].y = points[0].y + 3 * dmall_diam / 16;
+        count = 6;
+        NtGdiPolyPolyDraw( dc, points, &count, 1, NtGdiPolyPolygon );
+        break;
+
+    default:
+        WARN( "Invalid menu; flags=0x%04x\n", flags );
+        retval = FALSE;
+        break;
+    }
+
+    NtGdiSelectPen( dc, prev_pen );
+    NtGdiSelectBrush( dc, prev_brush );
+    return retval;
+}
+
 static void draw_close_button( HWND hwnd, HDC hdc, BOOL down, BOOL grayed )
 {
     RECT rect;
diff --git a/dlls/win32u/gdiobj.c b/dlls/win32u/gdiobj.c
index 72924bb658a..9c6415236c2 100644
--- a/dlls/win32u/gdiobj.c
+++ b/dlls/win32u/gdiobj.c
@@ -1160,6 +1160,7 @@ static struct unix_funcs unix_funcs =
     NtUserDispatchMessage,
     NtUserDragDetect,
     NtUserDrawIconEx,
+    NtUserDrawMenuBarTemp,
     NtUserEmptyClipboard,
     NtUserEnableMenuItem,
     NtUserEndDeferWindowPosEx,
diff --git a/dlls/win32u/menu.c b/dlls/win32u/menu.c
index 2ac469908b9..182dbb6cf58 100644
--- a/dlls/win32u/menu.c
+++ b/dlls/win32u/menu.c
@@ -24,7 +24,7 @@
 #endif
 
 #define OEMRESOURCE
-#include "win32u_private.h"
+#include "ntgdi_private.h"
 #include "ntuser_private.h"
 #include "wine/server.h"
 #include "wine/debug.h"
@@ -48,6 +48,9 @@ struct accelerator
 /* (other menu->FocusedItem values give the position of the focused item) */
 #define NO_SELECTED_ITEM  0xffff
 
+/* Space between 2 columns */
+#define MENU_COL_SPACE 4
+
 /* macro to test that flags do not indicate bitmap, ownerdraw or separator */
 #define IS_STRING_ITEM(flags) (MENU_ITEM_TYPE ((flags)) == MF_STRING)
 #define IS_MAGIC_BITMAP(id)     ((id) && ((INT_PTR)(id) < 12) && ((INT_PTR)(id) >= -1))
@@ -1672,3 +1675,554 @@ UINT get_menu_bar_height( HWND hwnd, UINT width, INT org_x, INT org_y )
     NtUserReleaseDC( hwnd, hdc );
     return menu->Height;
 }
+
+static void draw_popup_arrow( HDC hdc, RECT rect, UINT arrow_width, UINT arrow_height )
+{
+    HDC mem_hdc = NtGdiCreateCompatibleDC( hdc );
+    HBITMAP prev_bitmap;
+
+    prev_bitmap = NtGdiSelectBitmap( mem_hdc, get_arrow_bitmap() );
+    NtGdiBitBlt( hdc, rect.right - arrow_width - 1,
+                 (rect.top + rect.bottom - arrow_height) / 2,
+                 arrow_width, arrow_height, mem_hdc, 0, 0, SRCCOPY, 0, 0 );
+    NtGdiSelectBitmap( mem_hdc, prev_bitmap );
+    NtGdiDeleteObjectApp( mem_hdc );
+}
+
+static void draw_bitmap_item( HDC hdc, MENUITEM *item, const RECT *rect,
+                              POPUPMENU *menu, HWND owner, UINT odaction )
+{
+    int w = rect->right - rect->left;
+    int h = rect->bottom - rect->top;
+    int bmp_xoffset = 0, left, top;
+    HBITMAP bmp_to_draw = item->hbmpItem;
+    HBITMAP bmp = bmp_to_draw;
+    BITMAP bm;
+    DWORD rop;
+    HDC mem_hdc;
+
+    /* Check if there is a magic menu item associated with this item */
+    if (IS_MAGIC_BITMAP( bmp_to_draw ))
+    {
+        UINT flags = 0;
+        WCHAR bmchr = 0;
+        RECT r;
+
+        switch ((INT_PTR)bmp_to_draw)
+        {
+        case (INT_PTR)HBMMENU_SYSTEM:
+            if (item->dwItemData)
+            {
+                bmp = (HBITMAP)item->dwItemData;
+                if (!NtGdiExtGetObjectW( bmp, sizeof(bm), &bm )) return;
+            }
+            else
+            {
+                static HBITMAP sys_menu_bmp;
+
+                if (!sys_menu_bmp)
+                    sys_menu_bmp = LoadImageW( 0, MAKEINTRESOURCEW(OBM_CLOSE), IMAGE_BITMAP, 0, 0, 0 );
+                bmp = sys_menu_bmp;
+                if (!NtGdiExtGetObjectW( bmp, sizeof(bm), &bm )) return;
+                /* only use right half of the bitmap */
+                bmp_xoffset = bm.bmWidth / 2;
+                bm.bmWidth -= bmp_xoffset;
+            }
+            goto got_bitmap;
+        case (INT_PTR)HBMMENU_MBAR_RESTORE:
+            flags = DFCS_CAPTIONRESTORE;
+            break;
+        case (INT_PTR)HBMMENU_MBAR_MINIMIZE:
+            flags = DFCS_CAPTIONMIN;
+            break;
+        case (INT_PTR)HBMMENU_MBAR_MINIMIZE_D:
+            flags = DFCS_CAPTIONMIN | DFCS_INACTIVE;
+            break;
+        case (INT_PTR)HBMMENU_MBAR_CLOSE:
+            flags = DFCS_CAPTIONCLOSE;
+            break;
+        case (INT_PTR)HBMMENU_MBAR_CLOSE_D:
+            flags = DFCS_CAPTIONCLOSE | DFCS_INACTIVE;
+            break;
+        case (INT_PTR)HBMMENU_CALLBACK:
+            {
+                DRAWITEMSTRUCT drawItem;
+                drawItem.CtlType = ODT_MENU;
+                drawItem.CtlID = 0;
+                drawItem.itemID = item->wID;
+                drawItem.itemAction = odaction;
+                drawItem.itemState = 0;
+                if (item->fState & MF_CHECKED)  drawItem.itemState |= ODS_CHECKED;
+                if (item->fState & MF_DEFAULT)  drawItem.itemState |= ODS_DEFAULT;
+                if (item->fState & MF_DISABLED) drawItem.itemState |= ODS_DISABLED;
+                if (item->fState & MF_GRAYED)   drawItem.itemState |= ODS_GRAYED|ODS_DISABLED;
+                if (item->fState & MF_HILITE)   drawItem.itemState |= ODS_SELECTED;
+                drawItem.hwndItem = (HWND)menu->obj.handle;
+                drawItem.hDC = hdc;
+                drawItem.itemData = item->dwItemData;
+                drawItem.rcItem = *rect;
+                send_message( owner, WM_DRAWITEM, 0, (LPARAM)&drawItem );
+                return;
+            }
+            break;
+        case (INT_PTR)HBMMENU_POPUP_CLOSE:
+            bmchr = 0x72;
+            break;
+        case (INT_PTR)HBMMENU_POPUP_RESTORE:
+            bmchr = 0x32;
+            break;
+        case (INT_PTR)HBMMENU_POPUP_MAXIMIZE:
+            bmchr = 0x31;
+            break;
+        case (INT_PTR)HBMMENU_POPUP_MINIMIZE:
+            bmchr = 0x30;
+            break;
+        default:
+            FIXME( "Magic %p not implemented\n", bmp_to_draw );
+            return;
+        }
+
+        if (bmchr)
+        {
+            /* draw the magic bitmaps using marlett font characters */
+            /* FIXME: fontsize and the position (x,y) could probably be better */
+            HFONT hfont, prev_font;
+            LOGFONTW logfont = { 0, 0, 0, 0, FW_NORMAL, 0, 0, 0, SYMBOL_CHARSET, 0, 0, 0, 0,
+                                 {'M','a','r','l','e','t','t'}};
+            logfont.lfHeight =  min( h, w) - 5 ;
+            TRACE( " height %d rect %s\n", logfont.lfHeight, wine_dbgstr_rect( rect ));
+            hfont = NtGdiHfontCreate( &logfont, sizeof(logfont), 0, 0, NULL );
+            prev_font = NtGdiSelectFont( hdc, hfont );
+            NtGdiExtTextOutW( hdc, rect->left, rect->top + 2, 0, NULL, &bmchr, 1, NULL, 0 );
+            NtGdiSelectFont( hdc, prev_font );
+            NtGdiDeleteObjectApp( hfont );
+        }
+        else
+        {
+            r = *rect;
+            InflateRect( &r, -1, -1 );
+            if (item->fState & MF_HILITE) flags |= DFCS_PUSHED;
+            draw_frame_caption( hdc, &r, flags );
+        }
+        return;
+    }
+
+    if (!bmp || !NtGdiExtGetObjectW( bmp, sizeof(bm), &bm )) return;
+
+got_bitmap:
+    mem_hdc = NtGdiCreateCompatibleDC( hdc );
+    NtGdiSelectBitmap( mem_hdc, bmp );
+
+    /* handle fontsize > bitmap_height */
+    top = (h>bm.bmHeight) ? rect->top + (h - bm.bmHeight) / 2 : rect->top;
+    left=rect->left;
+    rop= ((item->fState & MF_HILITE) && !IS_MAGIC_BITMAP(bmp_to_draw)) ? NOTSRCCOPY : SRCCOPY;
+    if ((item->fState & MF_HILITE) && item->hbmpItem)
+        NtGdiGetAndSetDCDword( hdc, NtGdiSetBkColor, get_sys_color( COLOR_HIGHLIGHT ), NULL );
+    NtGdiBitBlt( hdc, left, top, w, h, mem_hdc, bmp_xoffset, 0, rop, 0, 0 );
+    NtGdiDeleteObjectApp( mem_hdc );
+}
+
+/* Adjust menu item rectangle according to scrolling state */
+static void adjust_menu_item_rect( const POPUPMENU *menu, RECT *rect )
+{
+    INT scroll_offset = menu->bScrolling ? menu->nScrollPos : 0;
+    OffsetRect( rect, menu->items_rect.left, menu->items_rect.top - scroll_offset );
+}
+
+/* Draw a single menu item */
+static void draw_menu_item( HWND hwnd, POPUPMENU *menu, HWND owner, HDC hdc,
+                            MENUITEM *item, BOOL menu_bar, UINT odaction )
+{
+    UINT arrow_width = 0, arrow_height = 0;
+    HRGN old_clip = NULL, clip;
+    BOOL flat_menu = FALSE;
+    RECT rect, bmprc;
+    int bkgnd;
+
+    TRACE( "%s\n", debugstr_menuitem( item ));
+
+    if (!menu_bar)
+    {
+        BITMAP bmp;
+        NtGdiExtGetObjectW( get_arrow_bitmap(), sizeof(bmp), &bmp );
+        arrow_width = bmp.bmWidth;
+        arrow_height = bmp.bmHeight;
+    }
+
+    if (item->fType & MF_SYSMENU)
+    {
+        if (!is_iconic( hwnd ))
+            draw_nc_sys_button( hwnd, hdc, item->fState & (MF_HILITE | MF_MOUSESELECT) );
+        return;
+    }
+
+    TRACE( "rect=%s\n", wine_dbgstr_rect( &item->rect ));
+    rect = item->rect;
+    adjust_menu_item_rect( menu, &rect );
+    if (!intersect_rect( &bmprc, &rect, &menu->items_rect )) /* bmprc is used as a dummy */
+        return;
+
+    NtUserSystemParametersInfo( SPI_GETFLATMENU, 0, &flat_menu, 0 );
+    bkgnd = (menu_bar && flat_menu) ? COLOR_MENUBAR : COLOR_MENU;
+
+    /* Setup colors */
+    if (item->fState & MF_HILITE)
+    {
+        if (menu_bar && !flat_menu)
+        {
+            NtGdiGetAndSetDCDword( hdc, NtGdiSetTextColor, get_sys_color(COLOR_MENUTEXT), NULL );
+            NtGdiGetAndSetDCDword( hdc, NtGdiSetBkColor, get_sys_color(COLOR_MENU), NULL );
+        }
+        else
+        {
+            if (item->fState & MF_GRAYED)
+                NtGdiGetAndSetDCDword( hdc, NtGdiSetTextColor, get_sys_color( COLOR_GRAYTEXT ), NULL );
+            else
+                NtGdiGetAndSetDCDword( hdc, NtGdiSetTextColor, get_sys_color( COLOR_HIGHLIGHTTEXT ), NULL );
+            NtGdiGetAndSetDCDword( hdc, NtGdiSetBkColor, get_sys_color( COLOR_HIGHLIGHT ), NULL );
+        }
+    }
+    else
+    {
+        if (item->fState & MF_GRAYED)
+            NtGdiGetAndSetDCDword( hdc, NtGdiSetTextColor, get_sys_color( COLOR_GRAYTEXT ), NULL );
+        else
+            NtGdiGetAndSetDCDword( hdc, NtGdiSetTextColor, get_sys_color( COLOR_MENUTEXT ), NULL );
+        NtGdiGetAndSetDCDword( hdc, NtGdiSetBkColor, get_sys_color( bkgnd ), NULL );
+    }
+
+    old_clip = NtGdiCreateRectRgn( 0, 0, 0, 0 );
+    if (NtGdiGetRandomRgn( hdc, old_clip, NTGDI_RGN_MIRROR_RTL | 1 ) <= 0)
+    {
+        NtGdiDeleteObjectApp( old_clip );
+        old_clip = NULL;
+    }
+    clip = NtGdiCreateRectRgn( menu->items_rect.left, menu->items_rect.top,
+                               menu->items_rect.right, menu->items_rect.bottom );
+    NtGdiExtSelectClipRgn( hdc, clip, RGN_AND );
+    NtGdiDeleteObjectApp( clip );
+
+    if (item->fType & MF_OWNERDRAW)
+    {
+        /*
+         * Experimentation under Windows reveals that an owner-drawn
+         * menu is given the rectangle which includes the space it requested
+         * in its response to WM_MEASUREITEM _plus_ width for a checkmark
+         * and a popup-menu arrow.  This is the value of item->rect.
+         * Windows will leave all drawing to the application except for
+         * the popup-menu arrow.  Windows always draws that itself, after
+         * the menu owner has finished drawing.
+         */
+        DRAWITEMSTRUCT dis;
+        DWORD old_bk, old_text;
+
+        dis.CtlType   = ODT_MENU;
+        dis.CtlID     = 0;
+        dis.itemID    = item->wID;
+        dis.itemData  = item->dwItemData;
+        dis.itemState = 0;
+        if (item->fState & MF_CHECKED) dis.itemState |= ODS_CHECKED;
+        if (item->fState & MF_GRAYED)  dis.itemState |= ODS_GRAYED|ODS_DISABLED;
+        if (item->fState & MF_HILITE)  dis.itemState |= ODS_SELECTED;
+        dis.itemAction = odaction; /* ODA_DRAWENTIRE | ODA_SELECT | ODA_FOCUS; */
+        dis.hwndItem   = (HWND)menu->obj.handle;
+        dis.hDC        = hdc;
+        dis.rcItem     = rect;
+        TRACE( "Ownerdraw: owner=%p itemID=%d, itemState=%d, itemAction=%d, "
+               "hwndItem=%p, hdc=%p, rcItem=%s\n", owner,
+               dis.itemID, dis.itemState, dis.itemAction, dis.hwndItem,
+               dis.hDC, wine_dbgstr_rect( &dis.rcItem ));
+        NtGdiGetDCDword( hdc, NtGdiGetBkColor, &old_bk );
+        NtGdiGetDCDword( hdc, NtGdiGetTextColor, &old_text );
+        send_message( owner, WM_DRAWITEM, 0, (LPARAM)&dis );
+        /* Draw the popup-menu arrow */
+        NtGdiGetAndSetDCDword( hdc, NtGdiGetBkColor, old_bk, NULL );
+        NtGdiGetAndSetDCDword( hdc, NtGdiGetTextColor, old_text, NULL );
+        if (item->fType & MF_POPUP)
+            draw_popup_arrow( hdc, rect, arrow_width, arrow_height );
+        goto done;
+    }
+
+    if (menu_bar && (item->fType & MF_SEPARATOR)) goto done;
+
+    if (item->fState & MF_HILITE)
+    {
+        if (flat_menu)
+        {
+            InflateRect (&rect, -1, -1);
+            fill_rect( hdc, &rect, get_sys_color_brush( COLOR_MENUHILIGHT ));
+            InflateRect (&rect, 1, 1);
+            fill_rect( hdc, &rect, get_sys_color_brush( COLOR_HIGHLIGHT ));
+        }
+        else
+        {
+            if (menu_bar)
+                draw_rect_edge( hdc, &rect, BDR_SUNKENOUTER, BF_RECT, 1 );
+            else
+                fill_rect( hdc, &rect, get_sys_color_brush( COLOR_HIGHLIGHT ));
+        }
+    }
+    else
+        fill_rect( hdc, &rect, get_sys_color_brush(bkgnd) );
+
+    NtGdiGetAndSetDCDword( hdc, NtGdiSetBkMode, TRANSPARENT, NULL );
+
+    /* vertical separator */
+    if (!menu_bar && (item->fType & MF_MENUBARBREAK))
+    {
+        HPEN oldPen;
+        RECT rc = rect;
+
+        rc.left -= MENU_COL_SPACE / 2 + 1;
+        rc.top = 3;
+        rc.bottom = menu->Height - 3;
+        if (flat_menu)
+        {
+            oldPen = NtGdiSelectPen( hdc, get_sys_color_pen( COLOR_BTNSHADOW ));
+            NtGdiMoveTo( hdc, rc.left, rc.top, NULL );
+            NtGdiLineTo( hdc, rc.left, rc.bottom );
+            NtGdiSelectPen( hdc, oldPen );
+        }
+        else
+            draw_rect_edge( hdc, &rc, EDGE_ETCHED, BF_LEFT, 1 );
+    }
+
+    /* horizontal separator */
+    if (item->fType & MF_SEPARATOR)
+    {
+        HPEN oldPen;
+        RECT rc = rect;
+
+        InflateRect( &rc, -1, 0 );
+        rc.top = ( rc.top + rc.bottom) / 2;
+        if (flat_menu)
+        {
+            oldPen = NtGdiSelectPen( hdc, get_sys_color_pen( COLOR_BTNSHADOW ));
+            NtGdiMoveTo( hdc, rc.left, rc.top, NULL );
+            NtGdiLineTo( hdc, rc.right, rc.top );
+            NtGdiSelectPen( hdc, oldPen );
+        }
+        else
+            draw_rect_edge( hdc, &rc, EDGE_ETCHED, BF_TOP, 1 );
+        goto done;
+    }
+
+    if (item->hbmpItem)
+    {
+        /* calculate the bitmap rectangle in coordinates relative
+         * to the item rectangle */
+        if (menu_bar)
+        {
+            if (item->hbmpItem == HBMMENU_CALLBACK)
+                bmprc.left = 3;
+            else
+                bmprc.left = item->text ? menucharsize.cx : 0;
+        }
+        else if (menu->dwStyle & MNS_NOCHECK)
+            bmprc.left = 4;
+        else if (menu->dwStyle & MNS_CHECKORBMP)
+            bmprc.left = 2;
+        else
+            bmprc.left = 4 + get_system_metrics( SM_CXMENUCHECK );
+        bmprc.right =  bmprc.left + item->bmpsize.cx;
+        if (menu_bar && !(item->hbmpItem == HBMMENU_CALLBACK))
+            bmprc.top = 0;
+        else
+            bmprc.top = (rect.bottom - rect.top - item->bmpsize.cy) / 2;
+        bmprc.bottom = bmprc.top + item->bmpsize.cy;
+    }
+
+    if (!menu_bar)
+    {
+        HBITMAP bm;
+        INT y = rect.top + rect.bottom;
+        BOOL checked = FALSE;
+        UINT check_bitmap_width = get_system_metrics( SM_CXMENUCHECK );
+        UINT check_bitmap_height = get_system_metrics( SM_CYMENUCHECK );
+
+        /* Draw the check mark */
+        if (!(menu->dwStyle & MNS_NOCHECK))
+        {
+            bm = (item->fState & MF_CHECKED) ? item->hCheckBit :
+                item->hUnCheckBit;
+            if (bm)  /* we have a custom bitmap */
+            {
+                HDC mem_hdc = NtGdiCreateCompatibleDC( hdc );
+
+                NtGdiSelectBitmap( mem_hdc, bm );
+                NtGdiBitBlt( hdc, rect.left, (y - check_bitmap_height) / 2,
+                             check_bitmap_width, check_bitmap_height,
+                             mem_hdc, 0, 0, SRCCOPY, 0, 0 );
+                NtGdiDeleteObjectApp( mem_hdc );
+                checked = TRUE;
+            }
+            else if (item->fState & MF_CHECKED) /* standard bitmaps */
+            {
+                RECT r;
+                HBITMAP bm = NtGdiCreateBitmap( check_bitmap_width,
+                        check_bitmap_height, 1, 1, NULL );
+                HDC mem_hdc = NtGdiCreateCompatibleDC( hdc );
+
+                NtGdiSelectBitmap( mem_hdc, bm );
+                SetRect( &r, 0, 0, check_bitmap_width, check_bitmap_height);
+                draw_frame_menu( mem_hdc, &r,
+                                 (item->fType & MFT_RADIOCHECK) ? DFCS_MENUBULLET : DFCS_MENUCHECK );
+                NtGdiBitBlt( hdc, rect.left, (y - r.bottom) / 2, r.right, r.bottom,
+                             mem_hdc, 0, 0, SRCCOPY, 0, 0 );
+                NtGdiDeleteObjectApp( mem_hdc );
+                NtGdiDeleteObjectApp( bm );
+                checked = TRUE;
+            }
+        }
+        if (item->hbmpItem && !(checked && (menu->dwStyle & MNS_CHECKORBMP)))
+        {
+            POINT origorg;
+            /* some applications make this assumption on the DC's origin */
+            set_viewport_org( hdc, rect.left, rect.top, &origorg );
+            draw_bitmap_item( hdc, item, &bmprc, menu, owner, odaction );
+            set_viewport_org( hdc, origorg.x, origorg.y, NULL );
+        }
+        /* Draw the popup-menu arrow */
+        if (item->fType & MF_POPUP)
+            draw_popup_arrow( hdc, rect, arrow_width, arrow_height);
+        rect.left += 4;
+        if (!(menu->dwStyle & MNS_NOCHECK))
+            rect.left += check_bitmap_width;
+        rect.right -= arrow_width;
+    }
+    else if (item->hbmpItem)
+    {   /* Draw the bitmap */
+        POINT origorg;
+
+        set_viewport_org( hdc, rect.left, rect.top, &origorg);
+        draw_bitmap_item( hdc, item, &bmprc, menu, owner, odaction );
+        set_viewport_org( hdc, origorg.x, origorg.y, NULL);
+    }
+    /* process text if present */
+    if (item->text)
+    {
+        int i;
+        HFONT prev_font = 0;
+        UINT format = menu_bar ?
+            DT_CENTER | DT_VCENTER | DT_SINGLELINE :
+            DT_LEFT | DT_VCENTER | DT_SINGLELINE;
+
+        if (!(menu->dwStyle & MNS_CHECKORBMP))
+            rect.left += menu->textOffset;
+
+        if (item->fState & MFS_DEFAULT)
+        {
+             prev_font = NtGdiSelectFont(hdc, get_menu_font( TRUE ));
+        }
+
+        if (menu_bar)
+        {
+            if (item->hbmpItem)
+                rect.left += item->bmpsize.cx;
+            if (item->hbmpItem != HBMMENU_CALLBACK)
+                rect.left += menucharsize.cx;
+            rect.right -= menucharsize.cx;
+        }
+
+        for (i = 0; item->text[i]; i++)
+            if ((item->text[i] == '\t') || (item->text[i] == '\b'))
+                break;
+
+        if (item->fState & MF_GRAYED)
+        {
+            if (!(item->fState & MF_HILITE) )
+            {
+                ++rect.left; ++rect.top; ++rect.right; ++rect.bottom;
+                NtGdiGetAndSetDCDword( hdc, NtGdiSetTextColor, RGB(0xff, 0xff, 0xff), NULL );
+                DrawTextW( hdc, item->text, i, &rect, format );
+                --rect.left; --rect.top; --rect.right; --rect.bottom;
+            }
+            NtGdiGetAndSetDCDword( hdc, NtGdiSetTextColor, RGB(0x80, 0x80, 0x80), NULL );
+        }
+
+        DrawTextW( hdc, item->text, i, &rect, format );
+
+        /* paint the shortcut text */
+        if (!menu_bar && item->text[i])  /* There's a tab or flush-right char */
+        {
+            if (item->text[i] == '\t')
+            {
+                rect.left = item->xTab;
+                format = DT_LEFT | DT_VCENTER | DT_SINGLELINE;
+            }
+            else
+            {
+                rect.right = item->xTab;
+                format = DT_RIGHT | DT_VCENTER | DT_SINGLELINE;
+            }
+
+            if (item->fState & MF_GRAYED)
+            {
+                if (!(item->fState & MF_HILITE) )
+                {
+                    ++rect.left; ++rect.top; ++rect.right; ++rect.bottom;
+                    NtGdiGetAndSetDCDword( hdc, NtGdiSetTextColor, RGB(0xff, 0xff, 0xff), NULL );
+                    DrawTextW( hdc, item->text + i + 1, -1, &rect, format );
+                    --rect.left; --rect.top; --rect.right; --rect.bottom;
+                }
+                NtGdiGetAndSetDCDword( hdc, NtGdiSetTextColor, RGB(0x80, 0x80, 0x80), NULL );
+            }
+            DrawTextW( hdc, item->text + i + 1, -1, &rect, format );
+        }
+
+        if (prev_font) NtGdiSelectFont( hdc, prev_font );
+    }
+
+done:
+    NtGdiExtSelectClipRgn( hdc, old_clip, RGN_COPY );
+    if (old_clip) NtGdiDeleteObjectApp( old_clip );
+}
+
+/***********************************************************************
+ *           NtUserDrawMenuBarTemp   (win32u.@)
+ */
+DWORD WINAPI NtUserDrawMenuBarTemp( HWND hwnd, HDC hdc, RECT *rect, HMENU handle, HFONT font )
+{
+    BOOL flat_menu = FALSE;
+    HFONT prev_font = 0;
+    POPUPMENU *menu;
+    UINT i, retvalue;
+
+    NtUserSystemParametersInfo( SPI_GETFLATMENU, 0, &flat_menu, 0 );
+
+    if (!handle) handle = get_menu( hwnd );
+    if (!font) font = get_menu_font(FALSE);
+
+    menu = unsafe_menu_ptr( handle );
+    if (!menu || !rect) return get_system_metrics( SM_CYMENU );
+
+    TRACE( "(%p, %p, %p, %p, %p)\n", hwnd, hdc, rect, handle, font );
+
+    prev_font = NtGdiSelectFont( hdc, font );
+
+    if (!menu->Height) calc_menu_bar_size( hdc, rect, menu, hwnd );
+
+    rect->bottom = rect->top + menu->Height;
+
+    fill_rect( hdc, rect, get_sys_color_brush( flat_menu ? COLOR_MENUBAR : COLOR_MENU ));
+
+    NtGdiSelectPen( hdc, get_sys_color_pen( COLOR_3DFACE ));
+    NtGdiMoveTo( hdc, rect->left, rect->bottom, NULL );
+    NtGdiLineTo( hdc, rect->right, rect->bottom );
+
+    if (menu->nItems)
+    {
+        for (i = 0; i < menu->nItems; i++)
+            draw_menu_item( hwnd, menu, hwnd, hdc, &menu->items[i], TRUE, ODA_DRAWENTIRE );
+
+        retvalue = menu->Height;
+    }
+    else
+    {
+        retvalue = get_system_metrics( SM_CYMENU );
+    }
+
+    if (prev_font) NtGdiSelectFont( hdc, prev_font );
+    return retvalue;
+}
diff --git a/dlls/win32u/ntgdi_private.h b/dlls/win32u/ntgdi_private.h
index b04f7a87e34..ac552897769 100644
--- a/dlls/win32u/ntgdi_private.h
+++ b/dlls/win32u/ntgdi_private.h
@@ -181,6 +181,7 @@ extern struct dce *get_dc_dce( HDC hdc ) DECLSPEC_HIDDEN;
 extern void set_dc_dce( HDC hdc, struct dce *dce ) DECLSPEC_HIDDEN;
 extern WORD set_dce_flags( HDC hdc, WORD flags ) DECLSPEC_HIDDEN;
 extern DWORD set_stretch_blt_mode( HDC hdc, DWORD mode ) DECLSPEC_HIDDEN;
+extern BOOL set_viewport_org( HDC hdc, INT x, INT y, POINT *point ) DECLSPEC_HIDDEN;
 extern void DC_InitDC( DC * dc ) DECLSPEC_HIDDEN;
 extern void DC_UpdateXforms( DC * dc ) DECLSPEC_HIDDEN;
 
diff --git a/dlls/win32u/win32u.spec b/dlls/win32u/win32u.spec
index de500da143b..4dfebaa9d2d 100644
--- a/dlls/win32u/win32u.spec
+++ b/dlls/win32u/win32u.spec
@@ -851,7 +851,7 @@
 @ stub NtUserDrawCaption
 @ stub NtUserDrawCaptionTemp
 @ stdcall NtUserDrawIconEx(long long long long long long long long long)
-@ stub NtUserDrawMenuBarTemp
+@ stdcall NtUserDrawMenuBarTemp(long long ptr long long)
 @ stub NtUserDwmGetRemoteSessionOcclusionEvent
 @ stub NtUserDwmGetRemoteSessionOcclusionState
 @ stub NtUserDwmKernelShutdown
diff --git a/dlls/win32u/win32u_private.h b/dlls/win32u/win32u_private.h
index b8a2beeb15f..5f5563ec9f9 100644
--- a/dlls/win32u/win32u_private.h
+++ b/dlls/win32u/win32u_private.h
@@ -217,6 +217,7 @@ struct unix_funcs
     BOOL     (WINAPI *pNtUserDragDetect)( HWND hwnd, int x, int y );
     BOOL     (WINAPI *pNtUserDrawIconEx)( HDC hdc, INT x0, INT y0, HICON icon, INT width,
                                           INT height, UINT istep, HBRUSH hbr, UINT flags );
+    DWORD    (WINAPI *pNtUserDrawMenuBarTemp)( HWND hwnd, HDC hdc, RECT *rect, HMENU handle, HFONT font );
     BOOL     (WINAPI *pNtUserEmptyClipboard)(void);
     BOOL     (WINAPI *pNtUserEnableMenuItem)( HMENU handle, UINT id, UINT flags );
     BOOL     (WINAPI *pNtUserEndDeferWindowPosEx)( HDWP hdwp, BOOL async );
@@ -365,6 +366,10 @@ extern void register_window_surface( struct window_surface *old,
 extern LRESULT default_window_proc( HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam,
                                     BOOL ansi ) DECLSPEC_HIDDEN;
 extern LRESULT desktop_window_proc( HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam ) DECLSPEC_HIDDEN;
+extern BOOL draw_frame_caption( HDC dc, LPRECT r, UINT uFlags ) DECLSPEC_HIDDEN;
+extern BOOL draw_frame_menu( HDC dc, RECT *r, UINT flags ) DECLSPEC_HIDDEN;
+extern BOOL draw_nc_sys_button( HWND hwnd, HDC hdc, BOOL down ) DECLSPEC_HIDDEN;
+extern BOOL draw_rect_edge( HDC hdc, RECT *rc, UINT uType, UINT uFlags, UINT width ) DECLSPEC_HIDDEN;
 extern void fill_rect( HDC dc, const RECT *rect, HBRUSH hbrush ) DECLSPEC_HIDDEN;
 
 /* hook.c */
diff --git a/dlls/win32u/wrappers.c b/dlls/win32u/wrappers.c
index 99e0b9f3d88..6620597b7b1 100644
--- a/dlls/win32u/wrappers.c
+++ b/dlls/win32u/wrappers.c
@@ -868,6 +868,12 @@ BOOL WINAPI NtUserDrawIconEx( HDC hdc, INT x0, INT y0, HICON icon, INT width,
     return unix_funcs->pNtUserDrawIconEx( hdc, x0, y0, icon, width, height, istep, hbr, flags );
 }
 
+DWORD WINAPI NtUserDrawMenuBarTemp( HWND hwnd, HDC hdc, RECT *rect, HMENU handle, HFONT font )
+{
+    if (!unix_funcs) return 0;
+    return unix_funcs->pNtUserDrawMenuBarTemp( hwnd, hdc, rect, handle, font );
+}
+
 BOOL WINAPI NtUserEnableMenuItem( HMENU handle, UINT id, UINT flags )
 {
     if (!unix_funcs) return FALSE;
diff --git a/include/ntuser.h b/include/ntuser.h
index f2fdab6c0f9..9cf26534e10 100644
--- a/include/ntuser.h
+++ b/include/ntuser.h
@@ -539,6 +539,7 @@ LRESULT WINAPI NtUserDispatchMessage( const MSG *msg );
 BOOL    WINAPI NtUserDragDetect( HWND hwnd, int x, int y );
 BOOL    WINAPI NtUserDrawIconEx( HDC hdc, INT x0, INT y0, HICON icon, INT width,
                                  INT height, UINT istep, HBRUSH hbr, UINT flags );
+DWORD   WINAPI NtUserDrawMenuBarTemp( HWND hwnd, HDC hdc, RECT *rect, HMENU handle, HFONT font );
 BOOL    WINAPI NtUserEmptyClipboard(void);
 BOOL    WINAPI NtUserEnableMenuItem( HMENU handle, UINT id, UINT flags );
 BOOL    WINAPI NtUserEndDeferWindowPosEx( HDWP hdwp, BOOL async );
-- 
GitLab


https://gitlab.winehq.org/wine/wine/-/merge_requests/240



More information about the wine-devel mailing list