[PATCH v4] comctl32/listview: Add partial support for LVM_SETBKIMAGE.

Zhiyi Zhang zzhang at codeweavers.com
Mon Jun 13 21:51:24 CDT 2022


From: Dmitry Timoshkov <dmitry at baikal.ru>

Signed-off-by: Zhiyi Zhang <zzhang at codeweavers.com>
---
v2: Formatting fixes, add some tests.
v3: Add v6 tests, make v6 behaviour the default one.
v4: Supersede 238455. I was wrong and LVM_SETBKIMAGE should repaint the control. Also the background bitmap
    should be painted in LISTVIEW_EraseBkgnd() when the background color is CLR_NONE.


 dlls/comctl32/listview.c       | 90 ++++++++++++++++++++++++++++++----
 dlls/comctl32/tests/listview.c | 78 +++++++++++++++++++++++++++++
 2 files changed, 158 insertions(+), 10 deletions(-)

diff --git a/dlls/comctl32/listview.c b/dlls/comctl32/listview.c
index 730bf4aaddd..ab328b3e798 100644
--- a/dlls/comctl32/listview.c
+++ b/dlls/comctl32/listview.c
@@ -91,7 +91,7 @@
  *
  * Messages:
  *   -- LVM_ENABLEGROUPVIEW
- *   -- LVM_GETBKIMAGE, LVM_SETBKIMAGE
+ *   -- LVM_GETBKIMAGE
  *   -- LVM_GETGROUPINFO, LVM_SETGROUPINFO
  *   -- LVM_GETGROUPMETRICS, LVM_SETGROUPMETRICS
  *   -- LVM_GETINSERTMARK, LVM_SETINSERTMARK
@@ -297,6 +297,7 @@ typedef struct tagLISTVIEW_INFO
   COLORREF clrBk;
   COLORREF clrText;
   COLORREF clrTextBk;
+  HBITMAP hBkBitmap;
 
   /* font */
   HFONT hDefaultFont;
@@ -4551,6 +4552,21 @@ static INT LISTVIEW_GetTopIndex(const LISTVIEW_INFO *infoPtr)
     return nItem;
 }
 
+static void LISTVIEW_DrawBackgroundBitmap(const LISTVIEW_INFO *infoPtr, HDC hdc, const RECT *lprcBox)
+{
+    HDC mem_hdc;
+
+    if (!infoPtr->hBkBitmap)
+        return;
+
+    TRACE("(hdc=%p, lprcBox=%s, hBkBitmap=%p)\n", hdc, wine_dbgstr_rect(lprcBox), infoPtr->hBkBitmap);
+
+    mem_hdc = CreateCompatibleDC(hdc);
+    SelectObject(mem_hdc, infoPtr->hBkBitmap);
+    BitBlt(hdc, lprcBox->left, lprcBox->top, lprcBox->right - lprcBox->left,
+           lprcBox->bottom - lprcBox->top, mem_hdc, lprcBox->left, lprcBox->top, SRCCOPY);
+    DeleteDC(mem_hdc);
+}
 
 /***
  * DESCRIPTION:
@@ -4565,13 +4581,17 @@ static INT LISTVIEW_GetTopIndex(const LISTVIEW_INFO *infoPtr)
  *   Success: TRUE
  *   Failure: FALSE
  */
-static inline BOOL LISTVIEW_FillBkgnd(const LISTVIEW_INFO *infoPtr, HDC hdc, const RECT *lprcBox)
+static BOOL LISTVIEW_FillBkgnd(const LISTVIEW_INFO *infoPtr, HDC hdc, const RECT *lprcBox)
 {
-    if (!infoPtr->hBkBrush) return FALSE;
+    if (infoPtr->hBkBrush)
+    {
+        TRACE("(hdc=%p, lprcBox=%s, hBkBrush=%p)\n", hdc, wine_dbgstr_rect(lprcBox), infoPtr->hBkBrush);
 
-    TRACE("(hdc=%p, lprcBox=%s, hBkBrush=%p)\n", hdc, wine_dbgstr_rect(lprcBox), infoPtr->hBkBrush);
+        FillRect(hdc, lprcBox, infoPtr->hBkBrush);
+    }
 
-    return FillRect(hdc, lprcBox, infoPtr->hBkBrush);
+    LISTVIEW_DrawBackgroundBitmap(infoPtr, hdc, lprcBox);
+    return TRUE;
 }
 
 /* Draw main item or subitem */
@@ -8028,7 +8048,53 @@ static BOOL LISTVIEW_SetBkColor(LISTVIEW_INFO *infoPtr, COLORREF color)
     return TRUE;
 }
 
-/* LISTVIEW_SetBkImage */
+static BOOL LISTVIEW_SetBkImage(LISTVIEW_INFO *infoPtr, const LVBKIMAGEW *image, BOOL isW)
+{
+    TRACE("%08lx, %p, %p, %u, %d, %d\n", image->ulFlags, image->hbm, image->pszImage,
+          image->cchImageMax, image->xOffsetPercent, image->yOffsetPercent);
+
+    if (image->ulFlags & ~LVBKIF_SOURCE_MASK)
+        FIXME("unsupported flags %08lx\n", image->ulFlags & ~LVBKIF_SOURCE_MASK);
+
+    if (image->xOffsetPercent || image->yOffsetPercent)
+        FIXME("unsupported offset %d,%d\n", image->xOffsetPercent, image->yOffsetPercent);
+
+    switch (image->ulFlags & LVBKIF_SOURCE_MASK)
+    {
+    case LVBKIF_SOURCE_NONE:
+        if (infoPtr->hBkBitmap)
+        {
+            DeleteObject(infoPtr->hBkBitmap);
+            infoPtr->hBkBitmap = NULL;
+        }
+        InvalidateRect(infoPtr->hwndSelf, NULL, TRUE);
+        break;
+
+    case LVBKIF_SOURCE_HBITMAP:
+    {
+        BITMAP bm;
+
+        if (infoPtr->hBkBitmap)
+        {
+            DeleteObject(infoPtr->hBkBitmap);
+            infoPtr->hBkBitmap = NULL;
+        }
+        InvalidateRect(infoPtr->hwndSelf, NULL, TRUE);
+        if (GetObjectW(image->hbm, sizeof(bm), &bm) == sizeof(bm))
+        {
+            infoPtr->hBkBitmap = image->hbm;
+            return TRUE;
+        }
+        break;
+    }
+
+    case LVBKIF_SOURCE_URL:
+        FIXME("LVBKIF_SOURCE_URL: %s\n", isW ? debugstr_w(image->pszImage) : debugstr_a((LPCSTR)image->pszImage));
+        break;
+    }
+
+    return FALSE;
+}
 
 /*** Helper for {Insert,Set}ColumnT *only* */
 static void column_fill_hditem(const LISTVIEW_INFO *infoPtr, HDITEMW *lphdi, INT nColumn,
@@ -9641,10 +9707,11 @@ static inline BOOL LISTVIEW_EraseBkgnd(const LISTVIEW_INFO *infoPtr, HDC hdc)
     if (infoPtr->clrBk == CLR_NONE)
     {
         if (infoPtr->dwLvExStyle & LVS_EX_TRANSPARENTBKGND)
-            return SendMessageW(infoPtr->hwndNotify, WM_PRINTCLIENT,
-                                (WPARAM)hdc, PRF_ERASEBKGND);
+            SendMessageW(infoPtr->hwndNotify, WM_PRINTCLIENT, (WPARAM)hdc, PRF_ERASEBKGND);
         else
-            return SendMessageW(infoPtr->hwndNotify, WM_ERASEBKGND, (WPARAM)hdc, 0);
+            SendMessageW(infoPtr->hwndNotify, WM_ERASEBKGND, (WPARAM)hdc, 0);
+        LISTVIEW_DrawBackgroundBitmap(infoPtr, hdc, &rc);
+        return TRUE;
     }
 
     /* for double buffered controls we need to do this during refresh */
@@ -10406,6 +10473,7 @@ static LRESULT LISTVIEW_NCDestroy(LISTVIEW_INFO *infoPtr)
   infoPtr->hFont = 0;
   if (infoPtr->hDefaultFont) DeleteObject(infoPtr->hDefaultFont);
   if (infoPtr->clrBk != CLR_NONE) DeleteObject(infoPtr->hBkBrush);
+  if (infoPtr->hBkBitmap) DeleteObject(infoPtr->hBkBitmap);
 
   SetWindowLongPtrW(infoPtr->hwndSelf, 0, 0);
 
@@ -11554,7 +11622,9 @@ LISTVIEW_WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
   case LVM_SETBKCOLOR:
     return LISTVIEW_SetBkColor(infoPtr, (COLORREF)lParam);
 
-  /* case LVM_SETBKIMAGE: */
+  case LVM_SETBKIMAGEA:
+  case LVM_SETBKIMAGEW:
+    return LISTVIEW_SetBkImage(infoPtr, (LVBKIMAGEW *)lParam, uMsg == LVM_SETBKIMAGEW);
 
   case LVM_SETCALLBACKMASK:
     infoPtr->uCallbackMask = (UINT)wParam;
diff --git a/dlls/comctl32/tests/listview.c b/dlls/comctl32/tests/listview.c
index 6ac7f53137d..ed5222a5ee8 100644
--- a/dlls/comctl32/tests/listview.c
+++ b/dlls/comctl32/tests/listview.c
@@ -23,6 +23,7 @@
 #include <stdio.h>
 #include <windows.h>
 #include <commctrl.h>
+#include <objbase.h>
 
 #include "wine/test.h"
 #include "v6util.h"
@@ -6924,6 +6925,81 @@ static void test_LVM_GETNEXTITEMINDEX(void)
     DestroyWindow(hwnd);
 }
 
+static void test_LVM_SETBKIMAGE(BOOL is_v6)
+{
+    LVBKIMAGEA image;
+    HBITMAP hbmp;
+    BITMAP bm;
+    HWND hwnd;
+    int ret;
+
+    CoInitialize(NULL);
+
+    hbmp = CreateBitmap(32, 32, 1, 1, NULL);
+    hwnd = create_listview_control(LVS_REPORT);
+
+    image.ulFlags = LVBKIF_SOURCE_NONE;
+    image.hbm = 0;
+    image.pszImage = NULL;
+    image.cchImageMax = 0;
+    image.xOffsetPercent = 0;
+    image.yOffsetPercent = 0;
+    ret = SendMessageA(hwnd, LVM_SETBKIMAGEA, 0, (LPARAM)&image);
+    ok(!ret, "got %d\n", ret);
+
+    ret = GetObjectA(hbmp, sizeof(bm), &bm);
+    ok(ret == sizeof(bm), "got %d\n", ret);
+
+    image.ulFlags = LVBKIF_SOURCE_HBITMAP;
+    image.hbm = hbmp;
+    ret = SendMessageA(hwnd, LVM_SETBKIMAGEA, 0, (LPARAM)&image);
+    if (is_v6)
+        ok(ret, "got %d\n", ret);
+    else
+        todo_wine ok(!ret, "got %d\n", ret);
+
+    ret = GetObjectA(hbmp, sizeof(bm), &bm);
+    ok(ret == sizeof(bm), "got %d\n", ret);
+
+    image.ulFlags = LVBKIF_SOURCE_NONE;
+    image.hbm = 0;
+    ret = SendMessageA(hwnd, LVM_SETBKIMAGEA, 0, (LPARAM)&image);
+    ok(!ret, "got %d\n", ret);
+
+    ret = GetObjectA(hbmp, sizeof(bm), &bm);
+    ok(!ret, "got %d\n", ret);
+
+    hbmp = CreateBitmap(32, 32, 1, 1, NULL);
+
+    image.ulFlags = LVBKIF_SOURCE_HBITMAP;
+    image.hbm = hbmp;
+    ret = SendMessageA(hwnd, LVM_SETBKIMAGEA, 0, (LPARAM)&image);
+    if (is_v6)
+        ok(ret, "got %d\n", ret);
+    else
+        todo_wine ok(!ret, "got %d\n", ret);
+
+    ret = GetObjectA(hbmp, sizeof(bm), &bm);
+    ok(ret == sizeof(bm), "got %d\n", ret);
+
+    image.ulFlags = LVBKIF_SOURCE_HBITMAP;
+    image.hbm = hbmp;
+    ret = SendMessageA(hwnd, LVM_SETBKIMAGEA, 0, (LPARAM)&image);
+    ok(!ret, "got %d\n", ret);
+
+    ret = GetObjectA(hbmp, sizeof(bm), &bm);
+    ok(!ret, "got %d\n", ret);
+
+    image.ulFlags = LVBKIF_SOURCE_NONE;
+    image.hbm = 0;
+    ret = SendMessageA(hwnd, LVM_SETBKIMAGEA, 0, (LPARAM)&image);
+    ok(!ret, "got %d\n", ret);
+
+    DestroyWindow(hwnd);
+
+    CoUninitialize();
+}
+
 START_TEST(listview)
 {
     ULONG_PTR ctx_cookie;
@@ -6987,6 +7063,7 @@ START_TEST(listview)
     test_LVN_ENDLABELEDIT();
     test_LVM_GETCOUNTPERPAGE();
     test_item_state_change();
+    test_LVM_SETBKIMAGE(FALSE);
 
     if (!load_v6_module(&ctx_cookie, &hCtx))
     {
@@ -7034,6 +7111,7 @@ START_TEST(listview)
     test_item_state_change();
     test_selected_column();
     test_LVM_GETNEXTITEMINDEX();
+    test_LVM_SETBKIMAGE(TRUE);
 
     unload_v6_module(ctx_cookie, hCtx);
 
-- 
2.34.1



More information about the wine-devel mailing list