Wine-Devel
By thread
wine-devel@list.winehq.org
By month
Messages by month
- ----- 2026 -----
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2025 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2024 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2023 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2022 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2021 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2020 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2019 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2018 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2017 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2016 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2015 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2014 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2013 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2012 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2011 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2010 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2009 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2008 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2007 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2006 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2005 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2004 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2003 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2002 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2001 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
June 2022
- 68 participants
- 3274 messages
[PATCH 2/5] win32u: Move NtUserDrawMenuBarTemp implementation from user32.
by Jacek Caban
From: Jacek Caban <jacek(a)codeweavers.com>
Signed-off-by: Jacek Caban <jacek(a)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
June 14, 2022
[PATCH 1/5] win32u: Move WM_NCCALCSIZE implementation from user32.
by Jacek Caban
From: Jacek Caban <jacek(a)codeweavers.com>
Signed-off-by: Jacek Caban <jacek(a)codeweavers.com>
---
dlls/user32/controls.h | 3 -
dlls/user32/defwnd.c | 4 -
dlls/user32/menu.c | 25 ---
dlls/user32/nonclient.c | 70 -------
dlls/win32u/defwnd.c | 64 +++++++
dlls/win32u/menu.c | 345 +++++++++++++++++++++++++++++++++++
dlls/win32u/sysparams.c | 4 +-
dlls/win32u/win32u_private.h | 3 +
8 files changed, 414 insertions(+), 104 deletions(-)
diff --git a/dlls/user32/controls.h b/dlls/user32/controls.h
index 83d07e4ad76..2b6c4912709 100644
--- a/dlls/user32/controls.h
+++ b/dlls/user32/controls.h
@@ -114,8 +114,6 @@ extern BOOL update_wallpaper( const WCHAR *wallpaper, const WCHAR *pattern ) DEC
/* menu controls */
extern HWND MENU_IsMenuActive(void) DECLSPEC_HIDDEN;
-extern UINT MENU_GetMenuBarHeight( HWND hwnd, UINT menubarWidth,
- INT orgX, INT orgY ) DECLSPEC_HIDDEN;
extern void MENU_TrackMouseMenuBar( HWND hwnd, INT ht, POINT pt ) DECLSPEC_HIDDEN;
extern void MENU_TrackKbdMenuBar( HWND hwnd, UINT wParam, WCHAR wChar ) DECLSPEC_HIDDEN;
extern UINT MENU_DrawMenuBar( HDC hDC, LPRECT lprect, HWND hwnd ) DECLSPEC_HIDDEN;
@@ -123,7 +121,6 @@ extern void MENU_EndMenu(HWND) DECLSPEC_HIDDEN;
extern HMENU MENU_GetSysMenu( HWND hWnd, HMENU hPopupMenu ) DECLSPEC_HIDDEN;
/* nonclient area */
-extern void NC_HandleNCCalcSize( HWND hwnd, WPARAM wParam, RECT *winRect ) DECLSPEC_HIDDEN;
extern LRESULT NC_HandleNCHitTest( HWND hwnd, POINT pt ) DECLSPEC_HIDDEN;
extern LRESULT NC_HandleNCLButtonDown( HWND hwnd, WPARAM wParam, LPARAM lParam ) DECLSPEC_HIDDEN;
extern LRESULT NC_HandleNCMouseMove( HWND hwnd, WPARAM wParam, LPARAM lParam ) DECLSPEC_HIDDEN;
diff --git a/dlls/user32/defwnd.c b/dlls/user32/defwnd.c
index 9b23d1e6315..df5aa9353f4 100644
--- a/dlls/user32/defwnd.c
+++ b/dlls/user32/defwnd.c
@@ -173,10 +173,6 @@ static LRESULT DEFWND_DefWinProc( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lPa
return NC_HandleNCHitTest( hwnd, pt );
}
- case WM_NCCALCSIZE:
- NC_HandleNCCalcSize( hwnd, wParam, (RECT *)lParam );
- break;
-
case WM_WINDOWPOSCHANGED:
DEFWND_HandleWindowPosChanged( hwnd, (const WINDOWPOS *)lParam );
break;
diff --git a/dlls/user32/menu.c b/dlls/user32/menu.c
index 1ae722b7662..e30cb4110d2 100644
--- a/dlls/user32/menu.c
+++ b/dlls/user32/menu.c
@@ -3506,31 +3506,6 @@ LRESULT WINAPI PopupMenuWndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM
}
-/***********************************************************************
- * MENU_GetMenuBarHeight
- *
- * Compute the size of the menu bar height. Used by NC_HandleNCCalcSize().
- */
-UINT MENU_GetMenuBarHeight( HWND hwnd, UINT menubarWidth,
- INT orgX, INT orgY )
-{
- HDC hdc;
- RECT rectBar;
- LPPOPUPMENU lppop;
-
- TRACE("HWND %p, width %d, at (%d, %d).\n", hwnd, menubarWidth, orgX, orgY );
-
- if (!(lppop = MENU_GetMenu( GetMenu(hwnd) ))) return 0;
-
- hdc = NtUserGetDCEx( hwnd, 0, DCX_CACHE | DCX_WINDOW );
- SelectObject( hdc, get_menu_font(FALSE));
- SetRect(&rectBar, orgX, orgY, orgX+menubarWidth, orgY+GetSystemMetrics(SM_CYMENU));
- MENU_MenuBarCalcSize( hdc, &rectBar, lppop, hwnd );
- NtUserReleaseDC( hwnd, hdc );
- return lppop->Height;
-}
-
-
/*******************************************************************
* ChangeMenuA (USER32.@)
*/
diff --git a/dlls/user32/nonclient.c b/dlls/user32/nonclient.c
index dab6b73ee1c..96518ed4648 100644
--- a/dlls/user32/nonclient.c
+++ b/dlls/user32/nonclient.c
@@ -341,76 +341,6 @@ BOOL WINAPI DECLSPEC_HOTPATCH AdjustWindowRectExForDpi( LPRECT rect, DWORD style
}
-/***********************************************************************
- * NC_HandleNCCalcSize
- *
- * Handle a WM_NCCALCSIZE message. Called from DefWindowProc().
- */
-void NC_HandleNCCalcSize( HWND hwnd, WPARAM wparam, RECT *winRect )
-{
- RECT tmpRect = { 0, 0, 0, 0 };
- LONG style = GetWindowLongW( hwnd, GWL_STYLE );
- LONG exStyle = GetWindowLongW( hwnd, GWL_EXSTYLE );
-
- if (winRect == NULL)
- return;
-
- if (!(style & WS_MINIMIZE))
- {
- AdjustWindowRectEx( &tmpRect, style, FALSE, exStyle & ~WS_EX_CLIENTEDGE);
-
- winRect->left -= tmpRect.left;
- winRect->top -= tmpRect.top;
- winRect->right -= tmpRect.right;
- winRect->bottom -= tmpRect.bottom;
-
- if (((style & (WS_CHILD | WS_POPUP)) != WS_CHILD) && GetMenu(hwnd))
- {
- TRACE("Calling GetMenuBarHeight with hwnd %p, width %ld, at (%ld, %ld).\n",
- hwnd, winRect->right - winRect->left, -tmpRect.left, -tmpRect.top );
-
- winRect->top +=
- MENU_GetMenuBarHeight( hwnd,
- winRect->right - winRect->left,
- -tmpRect.left, -tmpRect.top );
- }
-
- if( exStyle & WS_EX_CLIENTEDGE)
- if( winRect->right - winRect->left > 2 * GetSystemMetrics(SM_CXEDGE) &&
- winRect->bottom - winRect->top > 2 * GetSystemMetrics(SM_CYEDGE))
- InflateRect( winRect, - GetSystemMetrics(SM_CXEDGE),
- - GetSystemMetrics(SM_CYEDGE));
-
- if (style & WS_VSCROLL)
- if (winRect->right - winRect->left >= GetSystemMetrics(SM_CXVSCROLL))
- {
- /* rectangle is in screen coords when wparam is false */
- if (!wparam && (exStyle & WS_EX_LAYOUTRTL)) exStyle ^= WS_EX_LEFTSCROLLBAR;
-
- if((exStyle & WS_EX_LEFTSCROLLBAR) != 0)
- winRect->left += GetSystemMetrics(SM_CXVSCROLL);
- else
- winRect->right -= GetSystemMetrics(SM_CXVSCROLL);
- }
-
- if (style & WS_HSCROLL)
- if( winRect->bottom - winRect->top > GetSystemMetrics(SM_CYHSCROLL))
- winRect->bottom -= GetSystemMetrics(SM_CYHSCROLL);
-
- if (winRect->top > winRect->bottom)
- winRect->bottom = winRect->top;
-
- if (winRect->left > winRect->right)
- winRect->right = winRect->left;
- }
- else
- {
- winRect->right = winRect->left;
- winRect->bottom = winRect->top;
- }
-}
-
-
/***********************************************************************
* NC_GetInsideRect
*
diff --git a/dlls/win32u/defwnd.c b/dlls/win32u/defwnd.c
index db8187a3ea1..75a7efd2c8d 100644
--- a/dlls/win32u/defwnd.c
+++ b/dlls/win32u/defwnd.c
@@ -1487,6 +1487,66 @@ static LRESULT handle_nc_activate( HWND hwnd, WPARAM wparam, LPARAM lparam )
return TRUE;
}
+static void handle_nc_calc_size( HWND hwnd, WPARAM wparam, RECT *win_rect )
+{
+ RECT rect = { 0, 0, 0, 0 };
+ LONG style = get_window_long( hwnd, GWL_STYLE );
+ LONG ex_style = get_window_long( hwnd, GWL_EXSTYLE );
+
+ if (!win_rect) return;
+
+ if (!(style & WS_MINIMIZE))
+ {
+ AdjustWindowRectEx( &rect, style, FALSE, ex_style & ~WS_EX_CLIENTEDGE );
+
+ win_rect->left -= rect.left;
+ win_rect->top -= rect.top;
+ win_rect->right -= rect.right;
+ win_rect->bottom -= rect.bottom;
+
+ if (((style & (WS_CHILD | WS_POPUP)) != WS_CHILD) && get_menu( hwnd ))
+ {
+ TRACE( "getting menu bar height with hwnd %p, width %d, at (%d, %d)\n",
+ hwnd, win_rect->right - win_rect->left, -rect.left, -rect.top );
+
+ win_rect->top += get_menu_bar_height( hwnd, win_rect->right - win_rect->left,
+ -rect.left, -rect.top );
+ }
+
+ if (ex_style & WS_EX_CLIENTEDGE)
+ if (win_rect->right - win_rect->left > 2 * get_system_metrics( SM_CXEDGE ) &&
+ win_rect->bottom - win_rect->top > 2 * get_system_metrics( SM_CYEDGE ))
+ InflateRect( win_rect, -get_system_metrics( SM_CXEDGE ),
+ -get_system_metrics( SM_CYEDGE ));
+
+ if ((style & WS_VSCROLL) &&
+ win_rect->right - win_rect->left >= get_system_metrics( SM_CXVSCROLL ))
+ {
+ /* rectangle is in screen coords when wparam is false */
+ if (!wparam && (ex_style & WS_EX_LAYOUTRTL)) ex_style ^= WS_EX_LEFTSCROLLBAR;
+
+ if (ex_style & WS_EX_LEFTSCROLLBAR)
+ win_rect->left += get_system_metrics( SM_CXVSCROLL );
+ else
+ win_rect->right -= get_system_metrics( SM_CXVSCROLL );
+ }
+
+ if ((style & WS_HSCROLL) &&
+ win_rect->bottom - win_rect->top > get_system_metrics( SM_CYHSCROLL ))
+ {
+ win_rect->bottom -= get_system_metrics( SM_CYHSCROLL );
+ }
+
+ if (win_rect->top > win_rect->bottom) win_rect->bottom = win_rect->top;
+ if (win_rect->left > win_rect->right) win_rect->right = win_rect->left;
+ }
+ else
+ {
+ win_rect->right = win_rect->left;
+ win_rect->bottom = win_rect->top;
+ }
+}
+
LRESULT default_window_proc( HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam, BOOL ansi )
{
LRESULT result = 0;
@@ -1514,6 +1574,10 @@ LRESULT default_window_proc( HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam,
break;
}
+ case WM_NCCALCSIZE:
+ handle_nc_calc_size( hwnd, wparam, (RECT *)lparam );
+ break;
+
case WM_NCPAINT:
return handle_nc_paint( hwnd, (HRGN)wparam );
diff --git a/dlls/win32u/menu.c b/dlls/win32u/menu.c
index 4622105ddf1..2ac469908b9 100644
--- a/dlls/win32u/menu.c
+++ b/dlls/win32u/menu.c
@@ -23,6 +23,7 @@
#pragma makedep unix
#endif
+#define OEMRESOURCE
#include "win32u_private.h"
#include "ntuser_private.h"
#include "wine/server.h"
@@ -59,6 +60,10 @@ struct accelerator
#define STATE_MASK (~TYPE_MASK)
#define MENUITEMINFO_STATE_MASK (STATE_MASK & ~(MF_BYPOSITION | MF_MOUSESELECT))
+
+static SIZE menucharsize;
+static UINT od_item_hight; /* default owner drawn item height */
+
/**********************************************************************
* NtUserCopyAcceleratorTable (win32u.@)
*/
@@ -221,6 +226,17 @@ static void release_menu_ptr( POPUPMENU *menu )
}
}
+/*
+ * Validate the given menu handle and returns the menu structure pointer.
+ * FIXME: this is unsafe, we should use a better mechanism instead.
+ */
+static POPUPMENU *unsafe_menu_ptr( HMENU handle )
+{
+ POPUPMENU *menu = grab_menu_ptr( handle );
+ if (menu) release_menu_ptr( menu );
+ return menu;
+}
+
/* see IsMenu */
static BOOL is_menu( HMENU handle )
{
@@ -1327,3 +1343,332 @@ INT WINAPI NtUserTranslateAccelerator( HWND hwnd, HACCEL accel, MSG *msg )
if (ptr != data) free( ptr );
return (i < count);
}
+
+static HFONT get_menu_font( BOOL bold )
+{
+ static HFONT menu_font, menu_font_bold;
+
+ HFONT ret = bold ? menu_font_bold : menu_font;
+
+ if (!ret)
+ {
+ NONCLIENTMETRICSW ncm;
+ HFONT prev;
+
+ ncm.cbSize = sizeof(NONCLIENTMETRICSW);
+ NtUserSystemParametersInfo( SPI_GETNONCLIENTMETRICS, sizeof(NONCLIENTMETRICSW), &ncm, 0 );
+
+ if (bold)
+ {
+ ncm.lfMenuFont.lfWeight += 300;
+ if (ncm.lfMenuFont.lfWeight > 1000) ncm.lfMenuFont.lfWeight = 1000;
+ }
+ if (!(ret = NtGdiHfontCreate( &ncm.lfMenuFont, sizeof(ncm.lfMenuFont), 0, 0, NULL )))
+ return 0;
+ prev = InterlockedCompareExchangePointer( (void **)(bold ? &menu_font_bold : &menu_font),
+ ret, NULL );
+ if (prev)
+ {
+ /* another thread beat us to it */
+ NtGdiDeleteObjectApp( ret );
+ ret = prev;
+ }
+ }
+ return ret;
+}
+
+static HBITMAP get_arrow_bitmap(void)
+{
+ static HBITMAP arrow_bitmap;
+
+ if (!arrow_bitmap)
+ arrow_bitmap = LoadImageW( 0, MAKEINTRESOURCEW(OBM_MNARROW), IMAGE_BITMAP, 0, 0, 0 );
+ return arrow_bitmap;
+}
+
+/* Get the size of a bitmap item */
+static void get_bitmap_item_size( MENUITEM *item, SIZE *size, HWND owner )
+{
+ HBITMAP bmp = item->hbmpItem;
+ BITMAP bm;
+
+ size->cx = size->cy = 0;
+
+ /* check if there is a magic menu item associated with this item */
+ switch ((INT_PTR)bmp)
+ {
+ case (INT_PTR)HBMMENU_CALLBACK:
+ {
+ MEASUREITEMSTRUCT meas_item;
+ meas_item.CtlType = ODT_MENU;
+ meas_item.CtlID = 0;
+ meas_item.itemID = item->wID;
+ meas_item.itemWidth = item->rect.right - item->rect.left;
+ meas_item.itemHeight = item->rect.bottom - item->rect.top;
+ meas_item.itemData = item->dwItemData;
+ send_message( owner, WM_MEASUREITEM, 0, (LPARAM)&meas_item );
+ size->cx = meas_item.itemWidth;
+ size->cy = meas_item.itemHeight;
+ return;
+ }
+ break;
+ case (INT_PTR)HBMMENU_SYSTEM:
+ if (item->dwItemData)
+ {
+ bmp = (HBITMAP)item->dwItemData;
+ break;
+ }
+ /* fall through */
+ case (INT_PTR)HBMMENU_MBAR_RESTORE:
+ case (INT_PTR)HBMMENU_MBAR_MINIMIZE:
+ case (INT_PTR)HBMMENU_MBAR_MINIMIZE_D:
+ case (INT_PTR)HBMMENU_MBAR_CLOSE:
+ case (INT_PTR)HBMMENU_MBAR_CLOSE_D:
+ size->cx = get_system_metrics( SM_CYMENU ) - 4;
+ size->cy = size->cx;
+ return;
+ case (INT_PTR)HBMMENU_POPUP_CLOSE:
+ case (INT_PTR)HBMMENU_POPUP_RESTORE:
+ case (INT_PTR)HBMMENU_POPUP_MAXIMIZE:
+ case (INT_PTR)HBMMENU_POPUP_MINIMIZE:
+ size->cx = get_system_metrics( SM_CXMENUSIZE );
+ size->cy = get_system_metrics( SM_CYMENUSIZE );
+ return;
+ }
+ if (NtGdiExtGetObjectW( bmp, sizeof(bm), &bm ))
+ {
+ size->cx = bm.bmWidth;
+ size->cy = bm.bmHeight;
+ }
+}
+
+/* Calculate the size of the menu item and store it in item->rect */
+static void calc_menu_item_size( HDC hdc, MENUITEM *item, HWND owner, INT org_x, INT org_y,
+ BOOL menu_bar, POPUPMENU *menu )
+{
+ UINT check_bitmap_width = get_system_metrics( SM_CXMENUCHECK );
+ UINT arrow_bitmap_width;
+ INT item_height;
+ BITMAP bm;
+ WCHAR *p;
+
+ TRACE( "dc=%p owner=%p (%d,%d) item %s\n", hdc, owner, org_x, org_y, debugstr_menuitem( item ));
+
+ NtGdiExtGetObjectW( get_arrow_bitmap(), sizeof(bm), &bm );
+ arrow_bitmap_width = bm.bmWidth;
+
+ if (!menucharsize.cx)
+ {
+ menucharsize.cx = get_char_dimensions( hdc, NULL, &menucharsize.cy );
+ /* Win95/98/ME will use menucharsize.cy here. Testing is possible
+ * but it is unlikely an application will depend on that */
+ od_item_hight = HIWORD( get_dialog_base_units() );
+ }
+
+ SetRect( &item->rect, org_x, org_y, org_x, org_y );
+
+ if (item->fType & MF_OWNERDRAW)
+ {
+ MEASUREITEMSTRUCT mis;
+ mis.CtlType = ODT_MENU;
+ mis.CtlID = 0;
+ mis.itemID = item->wID;
+ mis.itemData = item->dwItemData;
+ mis.itemHeight = od_item_hight;
+ mis.itemWidth = 0;
+ send_message( owner, WM_MEASUREITEM, 0, (LPARAM)&mis );
+ /* Tests reveal that Windows ( Win95 through WinXP) adds twice the average
+ * width of a menufont character to the width of an owner-drawn menu. */
+ item->rect.right += mis.itemWidth + 2 * menucharsize.cx;
+ if (menu_bar)
+ {
+ /* Under at least win95 you seem to be given a standard
+ * height for the menu and the height value is ignored. */
+ item->rect.bottom += get_system_metrics( SM_CYMENUSIZE );
+ }
+ else
+ item->rect.bottom += mis.itemHeight;
+
+ TRACE( "id=%04lx size=%dx%d\n", item->wID, item->rect.right-item->rect.left,
+ item->rect.bottom-item->rect.top );
+ return;
+ }
+
+ if (item->fType & MF_SEPARATOR)
+ {
+ item->rect.bottom += get_system_metrics( SM_CYMENUSIZE ) / 2;
+ if (!menu_bar) item->rect.right += arrow_bitmap_width + menucharsize.cx;
+ return;
+ }
+
+ item_height = 0;
+ item->xTab = 0;
+
+ if (!menu_bar)
+ {
+ if (item->hbmpItem)
+ {
+ SIZE size;
+
+ get_bitmap_item_size( item, &size, owner );
+ /* Keep the size of the bitmap in callback mode to be able
+ * to draw it correctly */
+ item->bmpsize = size;
+ menu->textOffset = max( menu->textOffset, size.cx );
+ item->rect.right += size.cx + 2;
+ item_height = size.cy + 2;
+ }
+ if (!(menu->dwStyle & MNS_NOCHECK)) item->rect.right += check_bitmap_width;
+ item->rect.right += 4 + menucharsize.cx;
+ item->xTab = item->rect.right;
+ item->rect.right += arrow_bitmap_width;
+ }
+ else if (item->hbmpItem) /* menu_bar */
+ {
+ SIZE size;
+
+ get_bitmap_item_size( item, &size, owner );
+ item->bmpsize = size;
+ item->rect.right += size.cx;
+ if (item->text) item->rect.right += 2;
+ item_height = size.cy;
+ }
+
+ /* it must be a text item - unless it's the system menu */
+ if (!(item->fType & MF_SYSMENU) && item->text)
+ {
+ LONG txt_height, txt_width;
+ HFONT prev_font = NULL;
+ RECT rc = item->rect;
+
+ if (item->fState & MFS_DEFAULT)
+ prev_font = NtGdiSelectFont( hdc, get_menu_font(TRUE) );
+
+ if (menu_bar)
+ {
+ txt_height = DrawTextW( hdc, item->text, -1, &rc, DT_SINGLELINE | DT_CALCRECT );
+ item->rect.right += rc.right - rc.left;
+ item_height = max( max( item_height, txt_height ),
+ get_system_metrics( SM_CYMENU ) - 1 );
+ item->rect.right += 2 * menucharsize.cx;
+ }
+ else
+ {
+ if ((p = wcschr( item->text, '\t' )))
+ {
+ RECT r = rc;
+ int h, n = (int)(p - item->text);
+
+ /* Item contains a tab (only meaningful in popup menus) */
+ /* get text size before the tab */
+ txt_height = DrawTextW( hdc, item->text, n, &rc, DT_SINGLELINE | DT_CALCRECT );
+ txt_width = rc.right - rc.left;
+ p += 1; /* advance past the Tab */
+ /* get text size after the tab */
+ h = DrawTextW( hdc, p, -1, &r, DT_SINGLELINE | DT_CALCRECT );
+ item->xTab += txt_width;
+ txt_height = max( txt_height, h );
+ /* space for the tab and the short cut */
+ txt_width += menucharsize.cx + r.right - r.left;
+ }
+ else
+ {
+ txt_height = DrawTextW( hdc, item->text, -1, &rc, DT_SINGLELINE | DT_CALCRECT );
+ txt_width = rc.right - rc.left;
+ item->xTab += txt_width;
+ }
+ item->rect.right += 2 + txt_width;
+ item_height = max( item_height, max( txt_height + 2, menucharsize.cy + 4 ));
+ }
+ if (prev_font) NtGdiSelectFont( hdc, prev_font );
+ }
+ else if (menu_bar)
+ {
+ item_height = max( item_height, get_system_metrics( SM_CYMENU ) - 1 );
+ }
+ item->rect.bottom += item_height;
+ TRACE( "%s\n", wine_dbgstr_rect( &item->rect ));
+}
+
+/* Calculate the size of the menu bar */
+static void calc_menu_bar_size( HDC hdc, RECT *rect, POPUPMENU *menu, HWND owner )
+{
+ UINT start, i, help_pos;
+ int org_x, org_y;
+ MENUITEM *item;
+
+ if (!rect || !menu || !menu->nItems) return;
+
+ TRACE( "rect %p %s\n", rect, wine_dbgstr_rect( rect ));
+ /* Start with a 1 pixel top border.
+ This corresponds to the difference between SM_CYMENU and SM_CYMENUSIZE. */
+ SetRect( &menu->items_rect, 0, 0, rect->right - rect->left, 1 );
+ start = 0;
+ help_pos = ~0u;
+ menu->textOffset = 0;
+ while (start < menu->nItems)
+ {
+ item = &menu->items[start];
+ org_x = menu->items_rect.left;
+ org_y = menu->items_rect.bottom;
+
+ /* Parse items until line break or end of menu */
+ for (i = start; i < menu->nItems; i++, item++)
+ {
+ if (help_pos == ~0u && (item->fType & MF_RIGHTJUSTIFY)) help_pos = i;
+ if (i != start && (item->fType & (MF_MENUBREAK | MF_MENUBARBREAK))) break;
+
+ TRACE("item org=(%d, %d) %s\n", org_x, org_y, debugstr_menuitem( item ));
+ calc_menu_item_size( hdc, item, owner, org_x, org_y, TRUE, menu );
+
+ if (item->rect.right > menu->items_rect.right)
+ {
+ if (i != start) break;
+ else item->rect.right = menu->items_rect.right;
+ }
+ menu->items_rect.bottom = max( menu->items_rect.bottom, item->rect.bottom );
+ org_x = item->rect.right;
+ }
+
+ /* Finish the line (set all items to the largest height found) */
+ while (start < i) menu->items[start++].rect.bottom = menu->items_rect.bottom;
+ }
+
+ OffsetRect( &menu->items_rect, rect->left, rect->top );
+ menu->Width = menu->items_rect.right - menu->items_rect.left;
+ menu->Height = menu->items_rect.bottom - menu->items_rect.top;
+ rect->bottom = menu->items_rect.bottom;
+
+ /* Flush right all items between the MF_RIGHTJUSTIFY and */
+ /* the last item (if several lines, only move the last line) */
+ if (help_pos == ~0u) return;
+ item = &menu->items[menu->nItems-1];
+ org_y = item->rect.top;
+ org_x = rect->right - rect->left;
+ for (i = menu->nItems - 1; i >= help_pos; i--, item--)
+ {
+ if (item->rect.top != org_y) break; /* other line */
+ if (item->rect.right >= org_x) break; /* too far right already */
+ item->rect.left += org_x - item->rect.right;
+ item->rect.right = org_x;
+ org_x = item->rect.left;
+ }
+}
+
+UINT get_menu_bar_height( HWND hwnd, UINT width, INT org_x, INT org_y )
+{
+ POPUPMENU *menu;
+ RECT rect_bar;
+ HDC hdc;
+
+ TRACE( "hwnd %p, width %d, at (%d, %d).\n", hwnd, width, org_x, org_y );
+
+ if (!(menu = unsafe_menu_ptr( get_menu( hwnd )))) return 0;
+
+ hdc = NtUserGetDCEx( hwnd, 0, DCX_CACHE | DCX_WINDOW );
+ NtGdiSelectFont( hdc, get_menu_font(FALSE));
+ SetRect( &rect_bar, org_x, org_y, org_x + width, org_y + get_system_metrics( SM_CYMENU ));
+ calc_menu_bar_size( hdc, &rect_bar, menu, hwnd );
+ NtUserReleaseDC( hwnd, hdc );
+ return menu->Height;
+}
diff --git a/dlls/win32u/sysparams.c b/dlls/win32u/sysparams.c
index 0b6053a5ab9..1c1d9ddc621 100644
--- a/dlls/win32u/sysparams.c
+++ b/dlls/win32u/sysparams.c
@@ -2724,7 +2724,7 @@ static void get_real_fontname( LOGFONTW *lf, WCHAR fullname[LF_FACESIZE] )
lstrcpyW( fullname, lf->lfFaceName );
}
-static LONG get_char_dimensions( HDC hdc, TEXTMETRICW *metric, LONG *height )
+LONG get_char_dimensions( HDC hdc, TEXTMETRICW *metric, LONG *height )
{
SIZE sz;
static const WCHAR abcdW[] =
@@ -2763,7 +2763,7 @@ static void get_text_metr_size( HDC hdc, LOGFONTW *lf, TEXTMETRICW *metric, UINT
NtGdiDeleteObjectApp( hfont );
}
-static DWORD get_dialog_base_units(void)
+DWORD get_dialog_base_units(void)
{
static LONG cx, cy;
diff --git a/dlls/win32u/win32u_private.h b/dlls/win32u/win32u_private.h
index be15dac62e7..b8a2beeb15f 100644
--- a/dlls/win32u/win32u_private.h
+++ b/dlls/win32u/win32u_private.h
@@ -392,6 +392,7 @@ extern void update_mouse_tracking_info( HWND hwnd ) DECLSPEC_HIDDEN;
extern HMENU create_menu( BOOL is_popup ) DECLSPEC_HIDDEN;
extern BOOL draw_menu_bar( HWND hwnd ) DECLSPEC_HIDDEN;
extern HMENU get_menu( HWND hwnd ) DECLSPEC_HIDDEN;
+extern UINT get_menu_bar_height( HWND hwnd, UINT width, INT org_x, INT org_y ) DECLSPEC_HIDDEN;
extern BOOL get_menu_info( HMENU handle, MENUINFO *info ) DECLSPEC_HIDDEN;
extern INT get_menu_item_count( HMENU handle ) DECLSPEC_HIDDEN;
extern UINT get_menu_state( HMENU handle, UINT item_id, UINT flags ) DECLSPEC_HIDDEN;
@@ -413,6 +414,8 @@ extern LRESULT send_message_timeout( HWND hwnd, UINT msg, WPARAM wparam, LPARAM
/* sysparams.c */
extern BOOL enable_thunk_lock DECLSPEC_HIDDEN;
extern HBRUSH get_55aa_brush(void) DECLSPEC_HIDDEN;
+extern DWORD get_dialog_base_units(void) DECLSPEC_HIDDEN;
+extern LONG get_char_dimensions( HDC hdc, TEXTMETRICW *metric, LONG *height ) DECLSPEC_HIDDEN;
extern RECT get_display_rect( const WCHAR *display ) DECLSPEC_HIDDEN;
extern UINT get_monitor_dpi( HMONITOR monitor ) DECLSPEC_HIDDEN;
extern BOOL get_monitor_info( HMONITOR handle, MONITORINFO *info ) DECLSPEC_HIDDEN;
--
GitLab
https://gitlab.winehq.org/wine/wine/-/merge_requests/240
June 14, 2022
[PATCH 0/5] MR240: win32u: menu conversion
by Jacek Caban (@jacek)
--
https://gitlab.winehq.org/wine/wine/-/merge_requests/240
June 14, 2022
Re: [PATCH 0/3] MR239: uiautomationcore: Test fix and MSAA provider navigation patches. - closed
by Connor McAdams (@cmcadams)
This merge request was closed by Connor McAdams.
--
https://gitlab.winehq.org/wine/wine/-/merge_requests/239
June 13, 2022
Re: [PATCH 3/3] uiautomationcore: Implement NavigateDirection_{Previous/Next}Sibling for MSAA providers.
by Marvin
Hi,
While running your changed tests, I think I found new failures.
Being a bot and all I'm not very good at pattern recognition, so I might be
wrong, but could you please double-check?
Full results can be found at:
https://testbot.winehq.org/JobDetails.pl?Key=116890
Your paranoid android.
=== build (build log) ===
error: patch failed: dlls/uiautomationcore/tests/uiautomation.c:1134
error: patch failed: dlls/uiautomationcore/uia_provider.c:220
error: patch failed: dlls/uiautomationcore/tests/uiautomation.c:1112
error: patch failed: dlls/uiautomationcore/uia_provider.c:223
Task: Patch failed to apply
=== debian11 (build log) ===
error: patch failed: dlls/uiautomationcore/tests/uiautomation.c:1134
error: patch failed: dlls/uiautomationcore/uia_provider.c:220
error: patch failed: dlls/uiautomationcore/tests/uiautomation.c:1112
error: patch failed: dlls/uiautomationcore/uia_provider.c:223
Task: Patch failed to apply
=== debian11 (build log) ===
error: patch failed: dlls/uiautomationcore/tests/uiautomation.c:1134
error: patch failed: dlls/uiautomationcore/uia_provider.c:220
error: patch failed: dlls/uiautomationcore/tests/uiautomation.c:1112
error: patch failed: dlls/uiautomationcore/uia_provider.c:223
Task: Patch failed to apply
June 13, 2022
Re: [PATCH 2/3] uiautomationcore: Implement NavigateDirection_{First/Last}Child for MSAA Providers.
by Marvin
Hi,
While running your changed tests, I think I found new failures.
Being a bot and all I'm not very good at pattern recognition, so I might be
wrong, but could you please double-check?
Full results can be found at:
https://testbot.winehq.org/JobDetails.pl?Key=116889
Your paranoid android.
=== build (build log) ===
error: patch failed: dlls/uiautomationcore/tests/uiautomation.c:1134
error: patch failed: dlls/uiautomationcore/uia_provider.c:220
Task: Patch failed to apply
=== debian11 (build log) ===
error: patch failed: dlls/uiautomationcore/tests/uiautomation.c:1134
error: patch failed: dlls/uiautomationcore/uia_provider.c:220
Task: Patch failed to apply
=== debian11 (build log) ===
error: patch failed: dlls/uiautomationcore/tests/uiautomation.c:1134
error: patch failed: dlls/uiautomationcore/uia_provider.c:220
Task: Patch failed to apply
June 13, 2022
[PATCH 3/3] uiautomationcore: Implement NavigateDirection_{Previous/Next}Sibling for MSAA providers.
by Connor McAdams
From: Connor McAdams <cmcadams(a)codeweavers.com>
Signed-off-by: Connor McAdams <cmcadams(a)codeweavers.com>
---
dlls/uiautomationcore/tests/uiautomation.c | 132 +++++++++----------
dlls/uiautomationcore/uia_provider.c | 140 +++++++++++++++++++--
2 files changed, 195 insertions(+), 77 deletions(-)
diff --git a/dlls/uiautomationcore/tests/uiautomation.c b/dlls/uiautomationcore/tests/uiautomation.c
index da19d3668fa..15d7b4e784b 100644
--- a/dlls/uiautomationcore/tests/uiautomation.c
+++ b/dlls/uiautomationcore/tests/uiautomation.c
@@ -1112,13 +1112,13 @@ static void test_uia_prov_from_acc_navigation(void)
elfrag2 = (void *)0xdeadbeef;
hr = IRawElementProviderFragment_Navigate(elfrag, NavigateDirection_NextSibling, &elfrag2);
ok(Accessible.ref == 2, "Unexpected refcnt %ld\n", Accessible.ref);
- todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
+ ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
ok(!elfrag2, "elfrag2 != NULL\n");
elfrag2 = (void *)0xdeadbeef;
hr = IRawElementProviderFragment_Navigate(elfrag, NavigateDirection_PreviousSibling, &elfrag2);
ok(Accessible.ref == 2, "Unexpected refcnt %ld\n", Accessible.ref);
- todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
+ ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
ok(!elfrag2, "elfrag2 != NULL\n");
/*
@@ -1149,18 +1149,16 @@ static void test_uia_prov_from_acc_navigation(void)
SET_EXPECT(Accessible_get_accChild);
SET_EXPECT(Accessible_get_accState);
hr = IRawElementProviderFragment_Navigate(elfrag2, NavigateDirection_NextSibling, &elfrag3);
- todo_wine ok(Accessible.ref == 5, "Unexpected refcnt %ld\n", Accessible.ref);
- todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
- todo_wine ok(!!elfrag3, "elfrag2 == NULL\n");
- todo_wine CHECK_CALLED(Accessible_get_accChildCount);
- todo_wine CHECK_CALLED(Accessible_get_accChild);
- todo_wine CHECK_CALLED(Accessible_get_accState);
- if (elfrag3)
- {
- check_fragment_acc(elfrag3, &Accessible.IAccessible_iface, 3);
- IRawElementProviderFragment_Release(elfrag3);
- ok(Accessible.ref == 3, "Unexpected refcnt %ld\n", Accessible.ref);
- }
+ ok(Accessible.ref == 5, "Unexpected refcnt %ld\n", Accessible.ref);
+ ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
+ ok(!!elfrag3, "elfrag2 == NULL\n");
+ CHECK_CALLED(Accessible_get_accChildCount);
+ CHECK_CALLED(Accessible_get_accChild);
+ CHECK_CALLED(Accessible_get_accState);
+ check_fragment_acc(elfrag3, &Accessible.IAccessible_iface, 3);
+
+ IRawElementProviderFragment_Release(elfrag3);
+ ok(Accessible.ref == 3, "Unexpected refcnt %ld\n", Accessible.ref);
IRawElementProviderFragment_Release(elfrag2);
ok(Accessible_child.ref == 1, "Unexpected refcnt %ld\n", Accessible_child.ref);
ok(Accessible.ref == 2, "Unexpected refcnt %ld\n", Accessible.ref);
@@ -1206,19 +1204,16 @@ static void test_uia_prov_from_acc_navigation(void)
SET_EXPECT(Accessible_get_accChild);
SET_EXPECT(Accessible_get_accState);
hr = IRawElementProviderFragment_Navigate(elfrag2, NavigateDirection_PreviousSibling, &elfrag3);
- todo_wine ok(Accessible.ref == 5, "Unexpected refcnt %ld\n", Accessible.ref);
- todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
- todo_wine ok(!!elfrag3, "elfrag2 == NULL\n");
- todo_wine CHECK_CALLED(Accessible_get_accChildCount);
- todo_wine CHECK_CALLED(Accessible_get_accChild);
- todo_wine CHECK_CALLED(Accessible_get_accState);
- if (elfrag3)
- {
- check_fragment_acc(elfrag3, &Accessible.IAccessible_iface, 3);
- IRawElementProviderFragment_Release(elfrag3);
- ok(Accessible.ref == 3, "Unexpected refcnt %ld\n", Accessible.ref);
- }
+ ok(Accessible.ref == 5, "Unexpected refcnt %ld\n", Accessible.ref);
+ ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
+ ok(!!elfrag3, "elfrag2 == NULL\n");
+ CHECK_CALLED(Accessible_get_accChildCount);
+ CHECK_CALLED(Accessible_get_accChild);
+ CHECK_CALLED(Accessible_get_accState);
+ check_fragment_acc(elfrag3, &Accessible.IAccessible_iface, 3);
+ IRawElementProviderFragment_Release(elfrag3);
+ ok(Accessible.ref == 3, "Unexpected refcnt %ld\n", Accessible.ref);
IRawElementProviderFragment_Release(elfrag2);
ok(Accessible_child2.ref == 1, "Unexpected refcnt %ld\n", Accessible_child2.ref);
ok(Accessible.ref == 2, "Unexpected refcnt %ld\n", Accessible.ref);
@@ -1338,21 +1333,21 @@ static void test_uia_prov_from_acc_navigation(void)
SET_EXPECT(Accessible_child2_accLocation);
SET_EXPECT(Accessible_child2_get_accName);
hr = IRawElementProviderFragment_Navigate(elfrag, NavigateDirection_NextSibling, &elfrag2);
- todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
+ ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
ok(!elfrag2, "elfrag2 != NULL\n");
- todo_wine CHECK_CALLED_MULTI(Accessible_get_accChildCount, 5);
- todo_wine CHECK_CALLED_MULTI(Accessible_get_accChild, 4);
- todo_wine CHECK_CALLED(Accessible_child_get_accParent);
- todo_wine CHECK_CALLED(Accessible_child_get_accRole);
- todo_wine CHECK_CALLED(Accessible_child_get_accState);
- todo_wine CHECK_CALLED(Accessible_child_get_accChildCount);
- todo_wine CHECK_CALLED(Accessible_child_accLocation);
- todo_wine CHECK_CALLED(Accessible_child_get_accName);
- todo_wine CHECK_CALLED(Accessible_child2_get_accRole);
- todo_wine CHECK_CALLED(Accessible_child2_get_accState);
- todo_wine CHECK_CALLED(Accessible_child2_get_accChildCount);
- todo_wine CHECK_CALLED(Accessible_child2_accLocation);
- todo_wine CHECK_CALLED(Accessible_child2_get_accName);
+ CHECK_CALLED_MULTI(Accessible_get_accChildCount, 5);
+ CHECK_CALLED_MULTI(Accessible_get_accChild, 4);
+ CHECK_CALLED(Accessible_child_get_accParent);
+ CHECK_CALLED(Accessible_child_get_accRole);
+ CHECK_CALLED(Accessible_child_get_accState);
+ CHECK_CALLED(Accessible_child_get_accChildCount);
+ CHECK_CALLED(Accessible_child_accLocation);
+ CHECK_CALLED(Accessible_child_get_accName);
+ CHECK_CALLED(Accessible_child2_get_accRole);
+ CHECK_CALLED(Accessible_child2_get_accState);
+ CHECK_CALLED(Accessible_child2_get_accChildCount);
+ CHECK_CALLED(Accessible_child2_accLocation);
+ CHECK_CALLED(Accessible_child2_get_accName);
/* Now they have a role mismatch, we can determine our position. */
set_accessible_props(&Accessible_child2, ROLE_SYSTEM_DOCUMENT, STATE_SYSTEM_FOCUSABLE, 1,
@@ -1369,27 +1364,27 @@ static void test_uia_prov_from_acc_navigation(void)
* Even though we didn't get a new fragment, now that we know our
* position, a reference is added to the parent IAccessible.
*/
- todo_wine ok(Accessible.ref == 2, "Unexpected refcnt %ld\n", Accessible.ref);
- todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
+ ok(Accessible.ref == 2, "Unexpected refcnt %ld\n", Accessible.ref);
+ ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
ok(!elfrag2, "elfrag2 != NULL\n");
- todo_wine CHECK_CALLED_MULTI(Accessible_get_accChildCount, 6);
- todo_wine CHECK_CALLED_MULTI(Accessible_get_accChild, 5);
- todo_wine CHECK_CALLED(Accessible_get_accState);
- todo_wine CHECK_CALLED(Accessible_child_get_accParent);
- todo_wine CHECK_CALLED(Accessible_child_get_accRole);
- todo_wine CHECK_CALLED(Accessible_child2_get_accRole);
+ CHECK_CALLED_MULTI(Accessible_get_accChildCount, 6);
+ CHECK_CALLED_MULTI(Accessible_get_accChild, 5);
+ CHECK_CALLED(Accessible_get_accState);
+ CHECK_CALLED(Accessible_child_get_accParent);
+ CHECK_CALLED(Accessible_child_get_accRole);
+ CHECK_CALLED(Accessible_child2_get_accRole);
/* Now that we know our position, no extra nav work. */
SET_EXPECT(Accessible_get_accChildCount);
SET_EXPECT(Accessible_get_accChild);
SET_EXPECT(Accessible_get_accState);
hr = IRawElementProviderFragment_Navigate(elfrag, NavigateDirection_NextSibling, &elfrag2);
- todo_wine ok(Accessible.ref == 4, "Unexpected refcnt %ld\n", Accessible.ref);
- todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
- todo_wine ok(!!elfrag2, "elfrag2 == NULL\n");
- todo_wine CHECK_CALLED(Accessible_get_accChildCount);
- todo_wine CHECK_CALLED(Accessible_get_accChild);
- todo_wine CHECK_CALLED(Accessible_get_accState);
+ ok(Accessible.ref == 4, "Unexpected refcnt %ld\n", Accessible.ref);
+ ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
+ ok(!!elfrag2, "elfrag2 == NULL\n");
+ CHECK_CALLED(Accessible_get_accChildCount);
+ CHECK_CALLED(Accessible_get_accChild);
+ CHECK_CALLED(Accessible_get_accState);
if (elfrag2)
{
check_fragment_acc(elfrag2, &Accessible.IAccessible_iface, 3);
@@ -1449,23 +1444,20 @@ static void test_uia_prov_from_acc_navigation(void)
SET_EXPECT(Accessible_child_accNavigate);
SET_EXPECT(Accessible_child_get_accParent);
hr = IRawElementProviderFragment_Navigate(elfrag, NavigateDirection_NextSibling, &elfrag2);
- todo_wine ok(Accessible_child.ref == 2, "Unexpected refcnt %ld\n", Accessible_child.ref);
- todo_wine ok(Accessible.ref == 4, "Unexpected refcnt %ld\n", Accessible.ref);
- todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
- todo_wine ok(!!elfrag2, "elfrag2 == NULL\n");
- todo_wine CHECK_CALLED(Accessible_get_accChildCount);
- todo_wine CHECK_CALLED(Accessible_get_accChild);
- todo_wine CHECK_CALLED(Accessible_child_get_accState);
- todo_wine CHECK_CALLED(Accessible_child_accNavigate);
- todo_wine CHECK_CALLED(Accessible_child_get_accParent);
- if (elfrag2)
- {
- check_fragment_acc(elfrag2, &Accessible_child.IAccessible_iface, CHILDID_SELF);
- IRawElementProviderFragment_Release(elfrag2);
- ok(Accessible_child.ref == 1, "Unexpected refcnt %ld\n", Accessible_child.ref);
- ok(Accessible.ref == 3, "Unexpected refcnt %ld\n", Accessible.ref);
- }
+ ok(Accessible_child.ref == 2, "Unexpected refcnt %ld\n", Accessible_child.ref);
+ ok(Accessible.ref == 4, "Unexpected refcnt %ld\n", Accessible.ref);
+ ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
+ ok(!!elfrag2, "elfrag2 == NULL\n");
+ CHECK_CALLED(Accessible_get_accChildCount);
+ CHECK_CALLED(Accessible_get_accChild);
+ CHECK_CALLED(Accessible_child_get_accState);
+ CHECK_CALLED(Accessible_child_accNavigate);
+ CHECK_CALLED(Accessible_child_get_accParent);
+ check_fragment_acc(elfrag2, &Accessible_child.IAccessible_iface, CHILDID_SELF);
+ IRawElementProviderFragment_Release(elfrag2);
+ ok(Accessible_child.ref == 1, "Unexpected refcnt %ld\n", Accessible_child.ref);
+ ok(Accessible.ref == 3, "Unexpected refcnt %ld\n", Accessible.ref);
IRawElementProviderFragment_Release(elfrag);
IRawElementProviderSimple_Release(elprov);
ok(Accessible.ref == 1, "Unexpected refcnt %ld\n", Accessible.ref);
diff --git a/dlls/uiautomationcore/uia_provider.c b/dlls/uiautomationcore/uia_provider.c
index 891cb486161..f72628885bd 100644
--- a/dlls/uiautomationcore/uia_provider.c
+++ b/dlls/uiautomationcore/uia_provider.c
@@ -223,7 +223,7 @@ static HRESULT msaa_acc_get_parent(IAccessible *acc, IAccessible **parent)
#define DIR_FORWARD 0
#define DIR_REVERSE 1
static HRESULT msaa_acc_get_next_child(IAccessible *acc, LONG start_pos, LONG direction,
- IAccessible **child, LONG *child_id, LONG *end_pos)
+ IAccessible **child, LONG *child_id, LONG *end_pos, BOOL check_visible)
{
LONG child_count, cur_pos;
IDispatch *disp;
@@ -246,7 +246,7 @@ static HRESULT msaa_acc_get_next_child(IAccessible *acc, LONG start_pos, LONG di
if (hr == S_FALSE)
{
- if (!msaa_check_acc_state(acc, cid, STATE_SYSTEM_INVISIBLE))
+ if (!check_visible || !msaa_check_acc_state(acc, cid, STATE_SYSTEM_INVISIBLE))
{
*child = acc;
*child_id = *end_pos = cur_pos;
@@ -263,7 +263,7 @@ static HRESULT msaa_acc_get_next_child(IAccessible *acc, LONG start_pos, LONG di
break;
variant_init_i4(&cid, CHILDID_SELF);
- if (!msaa_check_acc_state(acc_child, cid, STATE_SYSTEM_INVISIBLE))
+ if (!check_visible || !msaa_check_acc_state(acc_child, cid, STATE_SYSTEM_INVISIBLE))
{
*child = acc_child;
*child_id = CHILDID_SELF;
@@ -286,6 +286,83 @@ static HRESULT msaa_acc_get_next_child(IAccessible *acc, LONG start_pos, LONG di
return hr;
}
+static HRESULT msaa_acc_get_child_pos(IAccessible *acc, IAccessible **out_parent, LONG *out_pos)
+{
+ IAccessible *child, *parent, *match, **children;
+ LONG child_count, child_id, end_pos, match_pos;
+ HRESULT hr;
+ int i;
+
+ *out_parent = NULL;
+ *out_pos = 0;
+ hr = msaa_acc_get_parent(acc, &parent);
+ if (FAILED(hr) || !parent)
+ return hr;
+
+ hr = IAccessible_get_accChildCount(parent, &child_count);
+ if (FAILED(hr) || !child_count)
+ {
+ IAccessible_Release(parent);
+ return hr;
+ }
+
+ children = heap_alloc_zero(sizeof(*children) * child_count);
+ if (!children)
+ return E_OUTOFMEMORY;
+
+ match = NULL;
+ for (i = 0; i < child_count; i++)
+ {
+ hr = msaa_acc_get_next_child(parent, i + 1, DIR_FORWARD, &child, &child_id, &end_pos, FALSE);
+ if (FAILED(hr) || !child)
+ goto exit;
+
+ if (child != parent)
+ children[i] = child;
+ }
+
+ for (i = 0; i < child_count; i++)
+ {
+ if (!children[i])
+ continue;
+
+ if (msaa_acc_compare(acc, children[i]))
+ {
+ if (!match)
+ {
+ match = children[i];
+ match_pos = i + 1;
+ }
+ /* Can't have more than one IAccessible match. */
+ else
+ {
+ match = NULL;
+ match_pos = 0;
+ break;
+ }
+ }
+ }
+
+exit:
+ if (match)
+ {
+ *out_parent = parent;
+ *out_pos = match_pos;
+ }
+ else
+ IAccessible_Release(parent);
+
+ for (i = 0; i < child_count; i++)
+ {
+ if (children[i])
+ IAccessible_Release(children[i]);
+ }
+
+ heap_free(children);
+
+ return hr;
+}
+
static LONG msaa_role_to_uia_control_type(LONG role)
{
switch (role)
@@ -647,9 +724,11 @@ static HRESULT WINAPI msaa_fragment_Navigate(IRawElementProviderFragment *iface,
break;
if (direction == NavigateDirection_FirstChild)
- hr = msaa_acc_get_next_child(msaa_prov->acc, 1, DIR_FORWARD, &acc, &child_id, &end_pos);
+ hr = msaa_acc_get_next_child(msaa_prov->acc, 1, DIR_FORWARD, &acc, &child_id,
+ &end_pos, TRUE);
else
- hr = msaa_acc_get_next_child(msaa_prov->acc, child_count, DIR_REVERSE, &acc, &child_id, &end_pos);
+ hr = msaa_acc_get_next_child(msaa_prov->acc, child_count, DIR_REVERSE, &acc, &child_id,
+ &end_pos, TRUE);
if (FAILED(hr) || !acc)
break;
@@ -675,8 +754,55 @@ static HRESULT WINAPI msaa_fragment_Navigate(IRawElementProviderFragment *iface,
case NavigateDirection_NextSibling:
case NavigateDirection_PreviousSibling:
- FIXME("Unimplemented NavigateDirection %d\n", direction);
- return E_NOTIMPL;
+ if (msaa_check_root_acc(msaa_prov))
+ break;
+
+ if (!msaa_prov->parent)
+ {
+ if (V_I4(&msaa_prov->cid) != CHILDID_SELF)
+ {
+ msaa_prov->parent = msaa_prov->acc;
+ IAccessible_AddRef(msaa_prov->acc);
+ msaa_prov->child_pos = V_I4(&msaa_prov->cid);
+ }
+ else
+ {
+ hr = msaa_acc_get_child_pos(msaa_prov->acc, &acc, &child_id);
+ if (FAILED(hr) || !acc)
+ break;
+ msaa_prov->parent = acc;
+ msaa_prov->child_pos = child_id;
+ }
+ }
+
+ if (direction == NavigateDirection_NextSibling)
+ hr = msaa_acc_get_next_child(msaa_prov->parent, msaa_prov->child_pos + 1, DIR_FORWARD,
+ &acc, &child_id, &end_pos, TRUE);
+ else
+ hr = msaa_acc_get_next_child(msaa_prov->parent, msaa_prov->child_pos - 1, DIR_REVERSE,
+ &acc, &child_id, &end_pos, TRUE);
+
+ if (FAILED(hr) || !acc)
+ break;
+
+ hr = UiaProviderFromIAccessible(acc, child_id, 0, &elprov);
+ if (SUCCEEDED(hr))
+ {
+ struct msaa_provider *prov = impl_from_msaa_provider(elprov);
+
+ *ret_val = &prov->IRawElementProviderFragment_iface;
+ prov->parent = msaa_prov->parent;
+ IAccessible_AddRef(msaa_prov->parent);
+ if (acc != msaa_prov->acc)
+ prov->child_pos = end_pos;
+ else
+ prov->child_pos = child_id;
+ }
+
+ if (acc != msaa_prov->parent)
+ IAccessible_Release(acc);
+
+ break;
default:
FIXME("Invalid NavigateDirection %d\n", direction);
--
GitLab
https://gitlab.winehq.org/wine/wine/-/merge_requests/239
June 13, 2022
[PATCH 2/3] uiautomationcore: Implement NavigateDirection_{First/Last}Child for MSAA Providers.
by Connor McAdams
From: Connor McAdams <cmcadams(a)codeweavers.com>
Signed-off-by: Connor McAdams <cmcadams(a)codeweavers.com>
---
dlls/uiautomationcore/tests/uiautomation.c | 155 ++++++++++-----------
dlls/uiautomationcore/uia_provider.c | 108 +++++++++++++-
2 files changed, 181 insertions(+), 82 deletions(-)
diff --git a/dlls/uiautomationcore/tests/uiautomation.c b/dlls/uiautomationcore/tests/uiautomation.c
index 10d91d04d81..da19d3668fa 100644
--- a/dlls/uiautomationcore/tests/uiautomation.c
+++ b/dlls/uiautomationcore/tests/uiautomation.c
@@ -1134,37 +1134,36 @@ static void test_uia_prov_from_acc_navigation(void)
SET_EXPECT(Accessible_child_accNavigate);
SET_EXPECT(Accessible_child_get_accParent);
hr = IRawElementProviderFragment_Navigate(elfrag, NavigateDirection_FirstChild, &elfrag2);
- todo_wine ok(Accessible_child.ref == 2, "Unexpected refcnt %ld\n", Accessible_child.ref);
- todo_wine ok(Accessible.ref == 3, "Unexpected refcnt %ld\n", Accessible.ref);
+ ok(Accessible_child.ref == 2, "Unexpected refcnt %ld\n", Accessible_child.ref);
+ ok(Accessible.ref == 3, "Unexpected refcnt %ld\n", Accessible.ref);
+ ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
+ ok(!!elfrag2, "elfrag2 == NULL\n");
+ CHECK_CALLED_MULTI(Accessible_get_accChildCount, 3);
+ CHECK_CALLED_MULTI(Accessible_get_accChild, 2);
+ CHECK_CALLED(Accessible_child_get_accState);
+ CHECK_CALLED(Accessible_child_accNavigate);
+ CHECK_CALLED(Accessible_child_get_accParent);
+
+ check_fragment_acc(elfrag2, &Accessible_child.IAccessible_iface, CHILDID_SELF);
+ SET_EXPECT(Accessible_get_accChildCount);
+ SET_EXPECT(Accessible_get_accChild);
+ SET_EXPECT(Accessible_get_accState);
+ hr = IRawElementProviderFragment_Navigate(elfrag2, NavigateDirection_NextSibling, &elfrag3);
+ todo_wine ok(Accessible.ref == 5, "Unexpected refcnt %ld\n", Accessible.ref);
todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
- todo_wine ok(!!elfrag2, "elfrag2 == NULL\n");
- todo_wine CHECK_CALLED_MULTI(Accessible_get_accChildCount, 3);
- todo_wine CHECK_CALLED_MULTI(Accessible_get_accChild, 2);
- todo_wine CHECK_CALLED(Accessible_child_get_accState);
- todo_wine CHECK_CALLED(Accessible_child_accNavigate);
- todo_wine CHECK_CALLED(Accessible_child_get_accParent);
- if (elfrag2)
+ todo_wine ok(!!elfrag3, "elfrag2 == NULL\n");
+ todo_wine CHECK_CALLED(Accessible_get_accChildCount);
+ todo_wine CHECK_CALLED(Accessible_get_accChild);
+ todo_wine CHECK_CALLED(Accessible_get_accState);
+ if (elfrag3)
{
- check_fragment_acc(elfrag2, &Accessible_child.IAccessible_iface, CHILDID_SELF);
-
- SET_EXPECT(Accessible_get_accChildCount);
- SET_EXPECT(Accessible_get_accChild);
- SET_EXPECT(Accessible_get_accState);
- hr = IRawElementProviderFragment_Navigate(elfrag2, NavigateDirection_NextSibling, &elfrag3);
- ok(Accessible.ref == 5, "Unexpected refcnt %ld\n", Accessible.ref);
- ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
- ok(!!elfrag3, "elfrag2 == NULL\n");
- CHECK_CALLED(Accessible_get_accChildCount);
- CHECK_CALLED(Accessible_get_accChild);
- CHECK_CALLED(Accessible_get_accState);
check_fragment_acc(elfrag3, &Accessible.IAccessible_iface, 3);
-
IRawElementProviderFragment_Release(elfrag3);
ok(Accessible.ref == 3, "Unexpected refcnt %ld\n", Accessible.ref);
- IRawElementProviderFragment_Release(elfrag2);
- ok(Accessible_child.ref == 1, "Unexpected refcnt %ld\n", Accessible_child.ref);
- ok(Accessible.ref == 2, "Unexpected refcnt %ld\n", Accessible.ref);
}
+ IRawElementProviderFragment_Release(elfrag2);
+ ok(Accessible_child.ref == 1, "Unexpected refcnt %ld\n", Accessible_child.ref);
+ ok(Accessible.ref == 2, "Unexpected refcnt %ld\n", Accessible.ref);
/* Retrieve childid 3 as first child now that Accessible_child is invisible. */
set_accessible_props(&Accessible_child, 0, STATE_SYSTEM_INVISIBLE, 0, NULL, 0, 0, 0, 0);
@@ -1173,19 +1172,16 @@ static void test_uia_prov_from_acc_navigation(void)
SET_EXPECT_MULTI(Accessible_get_accState, 2);
SET_EXPECT(Accessible_child_get_accState);
hr = IRawElementProviderFragment_Navigate(elfrag, NavigateDirection_FirstChild, &elfrag2);
- todo_wine ok(Accessible.ref == 4, "Unexpected refcnt %ld\n", Accessible.ref);
- todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
- todo_wine ok(!!elfrag2, "elfrag2 == NULL\n");
- todo_wine CHECK_CALLED_MULTI(Accessible_get_accChildCount, 4);
- todo_wine CHECK_CALLED_MULTI(Accessible_get_accChild, 3);
- todo_wine CHECK_CALLED_MULTI(Accessible_get_accState, 2);
- todo_wine CHECK_CALLED(Accessible_child_get_accState);
- if (elfrag2)
- {
- check_fragment_acc(elfrag2, &Accessible.IAccessible_iface, 3);
- IRawElementProviderFragment_Release(elfrag2);
- ok(Accessible.ref == 2, "Unexpected refcnt %ld\n", Accessible.ref);
- }
+ ok(Accessible.ref == 4, "Unexpected refcnt %ld\n", Accessible.ref);
+ ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
+ ok(!!elfrag2, "elfrag2 == NULL\n");
+ CHECK_CALLED_MULTI(Accessible_get_accChildCount, 4);
+ CHECK_CALLED_MULTI(Accessible_get_accChild, 3);
+ CHECK_CALLED_MULTI(Accessible_get_accState, 2);
+ CHECK_CALLED(Accessible_child_get_accState);
+ check_fragment_acc(elfrag2, &Accessible.IAccessible_iface, 3);
+ IRawElementProviderFragment_Release(elfrag2);
+ ok(Accessible.ref == 2, "Unexpected refcnt %ld\n", Accessible.ref);
/* Retrieve childid 4 (Accessible_child2) as last child. */
set_accessible_props(&Accessible_child2, 0, STATE_SYSTEM_FOCUSABLE, 0, NULL, 0, 0, 0, 0);
@@ -1195,38 +1191,38 @@ static void test_uia_prov_from_acc_navigation(void)
SET_EXPECT(Accessible_child2_accNavigate);
SET_EXPECT(Accessible_child2_get_accParent);
hr = IRawElementProviderFragment_Navigate(elfrag, NavigateDirection_LastChild, &elfrag2);
- todo_wine ok(Accessible_child2.ref == 2, "Unexpected refcnt %ld\n", Accessible_child2.ref);
- todo_wine ok(Accessible.ref == 3, "Unexpected refcnt %ld\n", Accessible.ref);
+ ok(Accessible_child2.ref == 2, "Unexpected refcnt %ld\n", Accessible_child2.ref);
+ ok(Accessible.ref == 3, "Unexpected refcnt %ld\n", Accessible.ref);
+ ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
+ ok(!!elfrag2, "elfrag2 == NULL\n");
+ CHECK_CALLED_MULTI(Accessible_get_accChildCount, 2);
+ CHECK_CALLED(Accessible_get_accChild);
+ CHECK_CALLED(Accessible_child2_get_accState);
+ CHECK_CALLED(Accessible_child2_accNavigate);
+ CHECK_CALLED(Accessible_child2_get_accParent);
+
+ check_fragment_acc(elfrag2, &Accessible_child2.IAccessible_iface, CHILDID_SELF);
+ SET_EXPECT(Accessible_get_accChildCount);
+ SET_EXPECT(Accessible_get_accChild);
+ SET_EXPECT(Accessible_get_accState);
+ hr = IRawElementProviderFragment_Navigate(elfrag2, NavigateDirection_PreviousSibling, &elfrag3);
+ todo_wine ok(Accessible.ref == 5, "Unexpected refcnt %ld\n", Accessible.ref);
todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
- todo_wine ok(!!elfrag2, "elfrag2 == NULL\n");
- todo_wine CHECK_CALLED_MULTI(Accessible_get_accChildCount, 2);
+ todo_wine ok(!!elfrag3, "elfrag2 == NULL\n");
+ todo_wine CHECK_CALLED(Accessible_get_accChildCount);
todo_wine CHECK_CALLED(Accessible_get_accChild);
- todo_wine CHECK_CALLED(Accessible_child2_get_accState);
- todo_wine CHECK_CALLED(Accessible_child2_accNavigate);
- todo_wine CHECK_CALLED(Accessible_child2_get_accParent);
- if (elfrag2)
+ todo_wine CHECK_CALLED(Accessible_get_accState);
+ if (elfrag3)
{
- check_fragment_acc(elfrag2, &Accessible_child2.IAccessible_iface, CHILDID_SELF);
-
- SET_EXPECT(Accessible_get_accChildCount);
- SET_EXPECT(Accessible_get_accChild);
- SET_EXPECT(Accessible_get_accState);
- hr = IRawElementProviderFragment_Navigate(elfrag2, NavigateDirection_PreviousSibling, &elfrag3);
- ok(Accessible.ref == 5, "Unexpected refcnt %ld\n", Accessible.ref);
- ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
- ok(!!elfrag3, "elfrag2 == NULL\n");
- CHECK_CALLED(Accessible_get_accChildCount);
- CHECK_CALLED(Accessible_get_accChild);
- CHECK_CALLED(Accessible_get_accState);
check_fragment_acc(elfrag3, &Accessible.IAccessible_iface, 3);
-
IRawElementProviderFragment_Release(elfrag3);
ok(Accessible.ref == 3, "Unexpected refcnt %ld\n", Accessible.ref);
- IRawElementProviderFragment_Release(elfrag2);
- ok(Accessible_child2.ref == 1, "Unexpected refcnt %ld\n", Accessible_child2.ref);
- ok(Accessible.ref == 2, "Unexpected refcnt %ld\n", Accessible.ref);
}
+ IRawElementProviderFragment_Release(elfrag2);
+ ok(Accessible_child2.ref == 1, "Unexpected refcnt %ld\n", Accessible_child2.ref);
+ ok(Accessible.ref == 2, "Unexpected refcnt %ld\n", Accessible.ref);
+
/* Retrieve childid 3 as last child, now that Accessible_child2 is STATE_SYSTEM_INVISIBLE. */
set_accessible_props(&Accessible_child2, 0, STATE_SYSTEM_INVISIBLE, 0, NULL, 0, 0, 0, 0);
SET_EXPECT_MULTI(Accessible_get_accChildCount, 3);
@@ -1234,19 +1230,16 @@ static void test_uia_prov_from_acc_navigation(void)
SET_EXPECT(Accessible_get_accState);
SET_EXPECT(Accessible_child2_get_accState);
hr = IRawElementProviderFragment_Navigate(elfrag, NavigateDirection_LastChild, &elfrag2);
- todo_wine ok(Accessible.ref == 4, "Unexpected refcnt %ld\n", Accessible.ref);
- todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
- todo_wine ok(!!elfrag2, "elfrag2 == NULL\n");
- todo_wine CHECK_CALLED_MULTI(Accessible_get_accChildCount, 3);
- todo_wine CHECK_CALLED_MULTI(Accessible_get_accChild, 2);
- todo_wine CHECK_CALLED(Accessible_get_accState);
- todo_wine CHECK_CALLED(Accessible_child2_get_accState);
- if (elfrag2)
- {
- check_fragment_acc(elfrag2, &Accessible.IAccessible_iface, 3);
- IRawElementProviderFragment_Release(elfrag2);
- ok(Accessible.ref == 2, "Unexpected refcnt %ld\n", Accessible.ref);
- }
+ ok(Accessible.ref == 4, "Unexpected refcnt %ld\n", Accessible.ref);
+ ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
+ ok(!!elfrag2, "elfrag2 == NULL\n");
+ CHECK_CALLED_MULTI(Accessible_get_accChildCount, 3);
+ CHECK_CALLED_MULTI(Accessible_get_accChild, 2);
+ CHECK_CALLED(Accessible_get_accState);
+ CHECK_CALLED(Accessible_child2_get_accState);
+ check_fragment_acc(elfrag2, &Accessible.IAccessible_iface, 3);
+ IRawElementProviderFragment_Release(elfrag2);
+ ok(Accessible.ref == 2, "Unexpected refcnt %ld\n", Accessible.ref);
IRawElementProviderFragment_Release(elfrag);
IRawElementProviderSimple_Release(elprov);
@@ -1309,15 +1302,15 @@ static void test_uia_prov_from_acc_navigation(void)
/* ChildCount of 0, do nothing for First/Last child.*/
SET_EXPECT(Accessible_child_get_accChildCount);
hr = IRawElementProviderFragment_Navigate(elfrag, NavigateDirection_FirstChild, &elfrag2);
- todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
+ ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
ok(!elfrag2, "elfrag2 != NULL\n");
- todo_wine CHECK_CALLED(Accessible_child_get_accChildCount);
+ CHECK_CALLED(Accessible_child_get_accChildCount);
SET_EXPECT(Accessible_child_get_accChildCount);
hr = IRawElementProviderFragment_Navigate(elfrag, NavigateDirection_LastChild, &elfrag2);
- todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
+ ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
ok(!elfrag2, "elfrag2 != NULL\n");
- todo_wine CHECK_CALLED(Accessible_child_get_accChildCount);
+ CHECK_CALLED(Accessible_child_get_accChildCount);
/*
* In the case of sibling navigation on an IAccessible that wasn't
@@ -1438,12 +1431,12 @@ static void test_uia_prov_from_acc_navigation(void)
*/
hr = IRawElementProviderFragment_Navigate(elfrag, NavigateDirection_FirstChild, &elfrag2);
ok(Accessible.ref == 2, "Unexpected refcnt %ld\n", Accessible.ref);
- todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
+ ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
ok(!elfrag2, "elfrag2 != NULL\n");
hr = IRawElementProviderFragment_Navigate(elfrag, NavigateDirection_LastChild, &elfrag2);
ok(Accessible.ref == 2, "Unexpected refcnt %ld\n", Accessible.ref);
- todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
+ ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
ok(!elfrag2, "elfrag2 != NULL\n");
/*
diff --git a/dlls/uiautomationcore/uia_provider.c b/dlls/uiautomationcore/uia_provider.c
index 1dfe447331e..891cb486161 100644
--- a/dlls/uiautomationcore/uia_provider.c
+++ b/dlls/uiautomationcore/uia_provider.c
@@ -220,6 +220,72 @@ static HRESULT msaa_acc_get_parent(IAccessible *acc, IAccessible **parent)
return hr;
}
+#define DIR_FORWARD 0
+#define DIR_REVERSE 1
+static HRESULT msaa_acc_get_next_child(IAccessible *acc, LONG start_pos, LONG direction,
+ IAccessible **child, LONG *child_id, LONG *end_pos)
+{
+ LONG child_count, cur_pos;
+ IDispatch *disp;
+ VARIANT cid;
+ HRESULT hr;
+
+ *child = NULL;
+ *child_id = 0;
+ cur_pos = start_pos;
+ while (1)
+ {
+ hr = IAccessible_get_accChildCount(acc, &child_count);
+ if (FAILED(hr) || (cur_pos > child_count))
+ break;
+
+ variant_init_i4(&cid, cur_pos);
+ hr = IAccessible_get_accChild(acc, cid, &disp);
+ if (FAILED(hr))
+ break;
+
+ if (hr == S_FALSE)
+ {
+ if (!msaa_check_acc_state(acc, cid, STATE_SYSTEM_INVISIBLE))
+ {
+ *child = acc;
+ *child_id = *end_pos = cur_pos;
+ return S_OK;
+ }
+ }
+ else
+ {
+ IAccessible *acc_child = NULL;
+
+ hr = IDispatch_QueryInterface(disp, &IID_IAccessible, (void **)&acc_child);
+ IDispatch_Release(disp);
+ if (FAILED(hr))
+ break;
+
+ variant_init_i4(&cid, CHILDID_SELF);
+ if (!msaa_check_acc_state(acc_child, cid, STATE_SYSTEM_INVISIBLE))
+ {
+ *child = acc_child;
+ *child_id = CHILDID_SELF;
+ *end_pos = cur_pos;
+ return S_OK;
+ }
+
+ IAccessible_Release(acc_child);
+ }
+
+ if (direction == DIR_FORWARD)
+ cur_pos++;
+ else
+ cur_pos--;
+
+ if ((cur_pos > child_count) || (cur_pos <= 0))
+ break;
+ }
+
+ return hr;
+}
+
static LONG msaa_role_to_uia_control_type(LONG role)
{
switch (role)
@@ -315,6 +381,9 @@ struct msaa_provider {
BOOL root_acc_check_ran;
BOOL is_root_acc;
+
+ IAccessible *parent;
+ INT child_pos;
};
static BOOL msaa_check_root_acc(struct msaa_provider *msaa_prov)
@@ -326,7 +395,7 @@ static BOOL msaa_check_root_acc(struct msaa_provider *msaa_prov)
return msaa_prov->is_root_acc;
msaa_prov->root_acc_check_ran = TRUE;
- if (V_I4(&msaa_prov->cid) != CHILDID_SELF)
+ if (V_I4(&msaa_prov->cid) != CHILDID_SELF || msaa_prov->parent)
return FALSE;
hr = AccessibleObjectFromWindow(msaa_prov->hwnd, OBJID_CLIENT, &IID_IAccessible, (void **)&acc);
@@ -383,6 +452,8 @@ ULONG WINAPI msaa_provider_Release(IRawElementProviderSimple *iface)
if (!refcount)
{
IAccessible_Release(msaa_prov->acc);
+ if (msaa_prov->parent)
+ IAccessible_Release(msaa_prov->parent);
heap_free(msaa_prov);
}
@@ -531,6 +602,7 @@ static HRESULT WINAPI msaa_fragment_Navigate(IRawElementProviderFragment *iface,
enum NavigateDirection direction, IRawElementProviderFragment **ret_val)
{
struct msaa_provider *msaa_prov = impl_from_msaa_fragment(iface);
+ LONG child_count, child_id, end_pos;
IRawElementProviderSimple *elprov;
IAccessible *acc;
HRESULT hr;
@@ -567,6 +639,40 @@ static HRESULT WINAPI msaa_fragment_Navigate(IRawElementProviderFragment *iface,
case NavigateDirection_FirstChild:
case NavigateDirection_LastChild:
+ if (V_I4(&msaa_prov->cid) != CHILDID_SELF)
+ break;
+
+ hr = IAccessible_get_accChildCount(msaa_prov->acc, &child_count);
+ if (FAILED(hr) || !child_count)
+ break;
+
+ if (direction == NavigateDirection_FirstChild)
+ hr = msaa_acc_get_next_child(msaa_prov->acc, 1, DIR_FORWARD, &acc, &child_id, &end_pos);
+ else
+ hr = msaa_acc_get_next_child(msaa_prov->acc, child_count, DIR_REVERSE, &acc, &child_id, &end_pos);
+
+ if (FAILED(hr) || !acc)
+ break;
+
+ hr = UiaProviderFromIAccessible(acc, child_id, 0, &elprov);
+ if (SUCCEEDED(hr))
+ {
+ struct msaa_provider *prov = impl_from_msaa_provider(elprov);
+
+ *ret_val = &prov->IRawElementProviderFragment_iface;
+ prov->parent = msaa_prov->acc;
+ IAccessible_AddRef(msaa_prov->acc);
+ if (acc != msaa_prov->acc)
+ prov->child_pos = end_pos;
+ else
+ prov->child_pos = child_id;
+ }
+
+ if (acc != msaa_prov->acc)
+ IAccessible_Release(acc);
+
+ break;
+
case NavigateDirection_NextSibling:
case NavigateDirection_PreviousSibling:
FIXME("Unimplemented NavigateDirection %d\n", direction);
--
GitLab
https://gitlab.winehq.org/wine/wine/-/merge_requests/239
June 13, 2022
[PATCH 1/3] uiautomationcore/tests: Release interfaces returned from get_HostRawElementProvider.
by Connor McAdams
From: Connor McAdams <cmcadams(a)codeweavers.com>
Signed-off-by: Connor McAdams <cmcadams(a)codeweavers.com>
---
dlls/uiautomationcore/tests/uiautomation.c | 2 ++
1 file changed, 2 insertions(+)
diff --git a/dlls/uiautomationcore/tests/uiautomation.c b/dlls/uiautomationcore/tests/uiautomation.c
index 885e520bfd3..10d91d04d81 100644
--- a/dlls/uiautomationcore/tests/uiautomation.c
+++ b/dlls/uiautomationcore/tests/uiautomation.c
@@ -2092,12 +2092,14 @@ static void test_UiaProviderFromIAccessible(void)
CHECK_CALLED(Accessible2_get_accName);
todo_wine CHECK_CALLED(Accessible2_QI_IAccIdentity);
todo_wine CHECK_CALLED(Accessible2_get_accParent);
+ IRawElementProviderSimple_Release(elprov2);
elprov2 = (void *)0xdeadbeef;
acc_client = NULL;
hr = IRawElementProviderSimple_get_HostRawElementProvider(elprov, &elprov2);
ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
ok(!!elprov2, "elprov == NULL, elprov %p\n", elprov2);
+ IRawElementProviderSimple_Release(elprov2);
IRawElementProviderSimple_Release(elprov);
ok(Accessible.ref == 1, "Unexpected refcnt %ld\n", Accessible.ref);
--
GitLab
https://gitlab.winehq.org/wine/wine/-/merge_requests/239
June 13, 2022
[PATCH 0/3] MR239: uiautomationcore: Test fix and MSAA provider navigation patches.
by Connor McAdams (@cmcadams)
--
https://gitlab.winehq.org/wine/wine/-/merge_requests/239
June 13, 2022