wine/dlls/uxtheme/dialog.c
Zhiyi Zhang d461cca19a uxtheme: Avoid calling application dialog procedures repeatedly.
Application dialog procedures are called in UXTHEME_DefDlgProc(), then may be called again in
USER_DefDlgProcA/W(). Lotus Approach doesn't expect this and run into infinite recursion.

Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=52586
Signed-off-by: Zhiyi Zhang <zzhang@codeweavers.com>
2022-06-29 09:04:55 +02:00

193 lines
5.6 KiB
C

/*
* Dialog theming support
*
* Copyright 2022 Zhiyi Zhang for CodeWeavers
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include <stdarg.h>
#include "windef.h"
#include "winbase.h"
#include "wingdi.h"
#include "winuser.h"
#include "uxtheme.h"
#include "uxthemedll.h"
#include "vssym32.h"
extern ATOM atDialogThemeEnabled;
static const WCHAR wine_dialog_brush[] = L"wine_dialog_brush";
static HBRUSH get_dialog_background_brush(HWND hwnd, BOOL create)
{
HBITMAP bitmap, old_bitmap;
HDC hdc, hdc_screen;
HBRUSH brush;
HTHEME theme;
DWORD flag;
HRESULT hr;
RECT rect;
SIZE size;
if (!IsThemeActive())
return NULL;
flag = HandleToUlong(GetPropW(hwnd, (LPCWSTR)MAKEINTATOM(atDialogThemeEnabled)));
if (flag != ETDT_ENABLETAB && flag != ETDT_ENABLEAEROWIZARDTAB)
return NULL;
brush = GetPropW(hwnd, wine_dialog_brush);
if (brush)
return brush;
if (!create)
return NULL;
theme = OpenThemeData(NULL, L"Tab");
if (!theme)
return NULL;
hr = GetThemePartSize(theme, NULL, TABP_BODY, 0, NULL, TS_TRUE, &size);
if (FAILED(hr))
{
size.cx = 10;
size.cy = 600;
}
hdc_screen = GetDC(NULL);
hdc = CreateCompatibleDC(hdc_screen);
bitmap = CreateCompatibleBitmap(hdc_screen, size.cx, size.cy);
old_bitmap = SelectObject(hdc, bitmap);
SetRect(&rect, 0, 0, size.cx, size.cy);
/* FIXME: XP draws the tab body bitmap directly without transparency even if there is */
FillRect(hdc, &rect, GetSysColorBrush(COLOR_3DFACE));
hr = DrawThemeBackground(theme, hdc, TABP_BODY, 0, &rect, NULL);
if (SUCCEEDED(hr))
{
brush = CreatePatternBrush(bitmap);
SetPropW(hwnd, wine_dialog_brush, brush);
}
SelectObject(hdc, old_bitmap);
DeleteDC(hdc);
ReleaseDC(NULL, hdc_screen);
CloseThemeData(theme);
return brush;
}
static void destroy_dialog_brush(HWND hwnd)
{
LOGBRUSH logbrush;
HBRUSH brush;
brush = GetPropW(hwnd, wine_dialog_brush);
if (brush)
{
RemovePropW(hwnd, wine_dialog_brush);
if (GetObjectW(brush, sizeof(logbrush), &logbrush) == sizeof(logbrush))
DeleteObject((HBITMAP)logbrush.lbHatch);
DeleteObject(brush);
}
}
LRESULT WINAPI UXTHEME_DefDlgProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp, BOOL unicode)
{
POINT org, old_org;
WNDPROC dlgproc;
HBRUSH brush;
LRESULT lr;
RECT rect;
HDC hdc;
switch (msg)
{
case WM_NCDESTROY:
{
destroy_dialog_brush(hwnd);
break;
}
case WM_THEMECHANGED:
{
destroy_dialog_brush(hwnd);
InvalidateRect(hwnd, NULL, TRUE);
break;
}
case WM_ERASEBKGND:
{
dlgproc = (WNDPROC)GetWindowLongPtrW(hwnd, DWLP_DLGPROC);
SetWindowLongPtrW(hwnd, DWLP_MSGRESULT, 0);
lr = LOWORD(CallWindowProcW(dlgproc, hwnd, msg, wp, lp));
if (lr || !IsWindow(hwnd))
return GetWindowLongPtrW(hwnd, DWLP_MSGRESULT);
brush = get_dialog_background_brush(hwnd, TRUE);
if (!brush)
{
/* Copied from DEFDLG_Proc() */
brush = (HBRUSH)SendMessageW(hwnd, WM_CTLCOLORDLG, wp, (LPARAM)hwnd);
if (!brush)
brush = (HBRUSH)DefWindowProcW(hwnd, WM_CTLCOLORDLG, wp, (LPARAM)hwnd);
if (brush)
{
hdc = (HDC)wp;
GetClientRect(hwnd, &rect);
DPtoLP(hdc, (LPPOINT)&rect, 2);
FillRect(hdc, &rect, brush);
}
return TRUE;
}
/* Using FillRect() to draw background could introduce a tiling effect if the destination
* rectangle is larger than the pattern brush size, which is usually 10x600. This bug is
* visible on property sheet pages if system DPI is set to 192. However, the same bug also
* exists on XP and explains why vista+ don't use gradient tab body background anymore */
hdc = (HDC)wp;
GetViewportOrgEx(hdc, &org);
SetBrushOrgEx(hdc, org.x, org.y, &old_org);
GetClientRect(hwnd, &rect);
FillRect(hdc, &rect, brush);
SetBrushOrgEx(hdc, old_org.x, old_org.y, NULL);
return TRUE;
}
case WM_CTLCOLORMSGBOX:
case WM_CTLCOLORBTN:
case WM_CTLCOLORDLG:
case WM_CTLCOLORSTATIC:
{
dlgproc = (WNDPROC)GetWindowLongPtrW(hwnd, DWLP_DLGPROC);
lr = CallWindowProcW(dlgproc, hwnd, msg, wp, lp);
if (lr || !IsWindow(hwnd))
return lr;
brush = get_dialog_background_brush(hwnd, FALSE);
if (!brush)
return DefWindowProcW(hwnd, msg, wp, lp);
hdc = (HDC)wp;
SetBkColor(hdc, GetSysColor(COLOR_BTNFACE));
SetBkMode(hdc, TRANSPARENT);
org.x = 0;
org.y = 0;
MapWindowPoints((HWND)lp, hwnd, &org, 1);
SetBrushOrgEx(hdc, -org.x, -org.y, NULL);
return (LRESULT)brush;
}
}
return user_api.pDefDlgProc(hwnd, msg, wp, lp, unicode);
}