Nikolay Sivov : comctl32/listview: Improve LVM_GETSUBITEMRECT implementation for out-of-bounds item indices.

Alexandre Julliard julliard at winehq.org
Thu Jan 3 13:31:15 CST 2013


Module: wine
Branch: master
Commit: 67c57b216c734a7133088bb097f436bc0e0ca743
URL:    http://source.winehq.org/git/wine.git/?a=commit;h=67c57b216c734a7133088bb097f436bc0e0ca743

Author: Nikolay Sivov <nsivov at codeweavers.com>
Date:   Thu Jan  3 15:29:39 2013 +0400

comctl32/listview: Improve LVM_GETSUBITEMRECT implementation for out-of-bounds item indices.

---

 dlls/comctl32/listview.c       |   79 +++++++++++++++++++---------------------
 dlls/comctl32/tests/listview.c |   46 ++++++++++++++++++++++-
 2 files changed, 82 insertions(+), 43 deletions(-)

diff --git a/dlls/comctl32/listview.c b/dlls/comctl32/listview.c
index cf7cc6d..977bb18 100644
--- a/dlls/comctl32/listview.c
+++ b/dlls/comctl32/listview.c
@@ -6,7 +6,7 @@
  * Copyright 2000 Jason Mawdsley
  * Copyright 2001 CodeWeavers Inc.
  * Copyright 2002 Dimitrie O. Paun
- * Copyright 2009-2012 Nikolay Sivov
+ * Copyright 2009-2013 Nikolay Sivov
  * Copyright 2009 Owen Rudge for CodeWeavers
  *
  * This library is free software; you can redistribute it and/or
@@ -6536,7 +6536,7 @@ static BOOL LISTVIEW_GetItemT(const LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem,
     HDPA hdpaSubItems;
     INT isubitem;
 
-    TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW);
+    TRACE("(item=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW);
 
     if (!lpLVItem || lpLVItem->iItem < 0 || lpLVItem->iItem >= infoPtr->nItemCount)
 	return FALSE;
@@ -7027,65 +7027,61 @@ static BOOL LISTVIEW_GetItemRect(const LISTVIEW_INFO *infoPtr, INT nItem, LPRECT
  * 
  * NOTE: for subItem = 0, we should return the bounds of the _entire_ item,
  *       not only those of the first column.
- *       Fortunately, LISTVIEW_GetItemMetrics does the right thing.
  * 
  * RETURN:
  *     TRUE: success
  *     FALSE: failure
  */
-static BOOL LISTVIEW_GetSubItemRect(const LISTVIEW_INFO *infoPtr, INT nItem, LPRECT lprc)
+static BOOL LISTVIEW_GetSubItemRect(const LISTVIEW_INFO *infoPtr, INT item, LPRECT lprc)
 {
-    POINT Position, Origin;
-    LVITEMW lvItem;
-    INT nColumn;
+    RECT rect = { 0, 0, 0, 0 };
+    POINT origin;
+    INT y;
     
     if (!lprc) return FALSE;
 
-    nColumn = lprc->top;
-
-    TRACE("(nItem=%d, nSubItem=%d, type=%d)\n", nItem, lprc->top, lprc->left);
-    /* On WinNT, a subitem of '0' calls LISTVIEW_GetItemRect */
+    TRACE("(item=%d, subitem=%d, type=%d)\n", item, lprc->top, lprc->left);
+    /* Subitem of '0' means item itself, and this works for all control view modes */
     if (lprc->top == 0)
-        return LISTVIEW_GetItemRect(infoPtr, nItem, lprc);
+        return LISTVIEW_GetItemRect(infoPtr, item, lprc);
 
     if (infoPtr->uView != LV_VIEW_DETAILS) return FALSE;
 
-    /* special case for header items */
-    if (nItem == -1)
-    {
-        if (lprc->left != LVIR_BOUNDS)
-        {
-            FIXME("Only LVIR_BOUNDS is implemented for header, got %d\n", lprc->left);
-            return FALSE;
-        }
+    LISTVIEW_GetOrigin(infoPtr, &origin);
+    /* this works for any item index, no matter if it exists or not */
+    y = item * infoPtr->nItemHeight + origin.y;
 
-        if (infoPtr->hwndHeader)
-            return SendMessageW(infoPtr->hwndHeader, HDM_GETITEMRECT, lprc->top, (LPARAM)lprc);
-        else
-        {
-            memset(lprc, 0, sizeof(RECT));
-            return TRUE;
-        }
+    if (infoPtr->hwndHeader && SendMessageW(infoPtr->hwndHeader, HDM_GETITEMRECT, lprc->top, (LPARAM)&rect))
+    {
+        rect.top = 0;
+        rect.bottom = infoPtr->nItemHeight;
+    }
+    else
+    {
+        /* Native implementation is broken for this case and garbage is left for left and right fields,
+           we zero them to get predictable output */
+        lprc->left = lprc->right = lprc->top = 0;
+        lprc->bottom = infoPtr->nItemHeight;
+        OffsetRect(lprc, origin.x, y);
+        TRACE("return rect %s\n", wine_dbgstr_rect(lprc));
+        return TRUE;
     }
 
-    if (!LISTVIEW_GetItemPosition(infoPtr, nItem, &Position)) return FALSE;
-    LISTVIEW_GetOrigin(infoPtr, &Origin);
-
-    if (nColumn < 0 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
-
-    lvItem.mask = 0;
-    lvItem.iItem = nItem;
-    lvItem.iSubItem = nColumn;
-    
-    switch(lprc->left)
+    switch (lprc->left)
     {
     case LVIR_ICON:
-	LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, NULL, lprc, NULL, NULL);
-        break;
+    {
+        /* it doesn't matter if main item actually has an icon, if imagelist is set icon width is returned */
+        if (infoPtr->himlSmall)
+            rect.right = rect.left + infoPtr->iconSize.cx;
+        else
+            rect.right = rect.left;
 
+        rect.bottom = rect.top + infoPtr->iconSize.cy;
+        break;
+    }
     case LVIR_LABEL:
     case LVIR_BOUNDS:
-	LISTVIEW_GetItemMetrics(infoPtr, &lvItem, lprc, NULL, NULL, NULL, NULL);
         break;
 
     default:
@@ -7093,7 +7089,8 @@ static BOOL LISTVIEW_GetSubItemRect(const LISTVIEW_INFO *infoPtr, INT nItem, LPR
 	return FALSE;
     }
 
-    OffsetRect(lprc, Origin.x, Position.y);
+    OffsetRect(&rect, origin.x, y);
+    *lprc = rect;
     TRACE("return rect %s\n", wine_dbgstr_rect(lprc));
 
     return TRUE;
diff --git a/dlls/comctl32/tests/listview.c b/dlls/comctl32/tests/listview.c
index 0dfddef..0620afe 100644
--- a/dlls/comctl32/tests/listview.c
+++ b/dlls/comctl32/tests/listview.c
@@ -247,6 +247,18 @@ static const struct message getitemposition_seq2[] = {
     { 0 }
 };
 
+static const struct message getsubitemrect_seq[] = {
+    { LVM_GETSUBITEMRECT, sent|id|wparam, -1, 0, LISTVIEW_ID },
+    { HDM_GETITEMRECT, sent|id, 0, 0, HEADER_ID },
+    { LVM_GETSUBITEMRECT, sent|id|wparam, 0, 0, LISTVIEW_ID },
+    { HDM_GETITEMRECT, sent|id, 0, 0, HEADER_ID },
+    { LVM_GETSUBITEMRECT, sent|id|wparam, -10, 0, LISTVIEW_ID },
+    { HDM_GETITEMRECT, sent|id, 0, 0, HEADER_ID },
+    { LVM_GETSUBITEMRECT, sent|id|wparam, 20, 0, LISTVIEW_ID },
+    { HDM_GETITEMRECT, sent|id, 0, 0, HEADER_ID },
+    { 0 }
+};
+
 static const struct message editbox_create_pos[] = {
     /* sequence sent after LVN_BEGINLABELEDIT */
     /* next two are 4.7x specific */
@@ -1602,6 +1614,8 @@ static void test_create(void)
     rect.top  = 1;
     rect.right = rect.bottom = -10;
     r = SendMessage(hList, LVM_GETSUBITEMRECT, -1, (LPARAM)&rect);
+    /* right value contains garbage, probably because header columns are not set up */
+    expect(0, rect.bottom);
     expect(1, r);
 
     hHeader = (HWND)SendMessage(hList, LVM_GETHEADER, 0, 0);
@@ -2473,6 +2487,36 @@ todo_wine
 
     SendMessage(hwnd, LVM_SCROLL, -10, 0);
 
+    /* test header interaction */
+    subclass_header(hwnd);
+    flush_sequences(sequences, NUM_MSG_SEQUENCES);
+
+    rect.left = LVIR_BOUNDS;
+    rect.top  = 1;
+    rect.right = rect.bottom = 0;
+    r = SendMessage(hwnd, LVM_GETSUBITEMRECT, -1, (LPARAM)&rect);
+    expect(1, r);
+
+    rect.left = LVIR_BOUNDS;
+    rect.top  = 1;
+    rect.right = rect.bottom = 0;
+    r = SendMessage(hwnd, LVM_GETSUBITEMRECT, 0, (LPARAM)&rect);
+    expect(1, r);
+
+    rect.left = LVIR_BOUNDS;
+    rect.top  = 1;
+    rect.right = rect.bottom = 0;
+    r = SendMessage(hwnd, LVM_GETSUBITEMRECT, -10, (LPARAM)&rect);
+    expect(1, r);
+
+    rect.left = LVIR_BOUNDS;
+    rect.top  = 1;
+    rect.right = rect.bottom = 0;
+    r = SendMessage(hwnd, LVM_GETSUBITEMRECT, 20, (LPARAM)&rect);
+    expect(1, r);
+
+    ok_sequence(sequences, LISTVIEW_SEQ_INDEX, getsubitemrect_seq, "LVM_GETSUBITEMRECT negative index", FALSE);
+
     DestroyWindow(hwnd);
 
     /* test subitem rects after re-arranging columns */
@@ -2514,13 +2558,11 @@ todo_wine
     rect2.top  = 1;
     rect2.right = rect2.bottom = -1;
     r = SendMessage(hwnd, LVM_GETSUBITEMRECT, 2, (LPARAM)&rect2);
-todo_wine {
     expect(TRUE, r);
     expect(rect.right, rect2.right);
     expect(rect.left, rect2.left);
     expect(rect.bottom, rect2.top);
     ok(rect2.bottom > rect2.top, "expected not zero height\n");
-}
 
     arr[0] = 1; arr[1] = 0; arr[2] = 2;
     r = SendMessage(hwnd, LVM_SETCOLUMNORDERARRAY, 3, (LPARAM)arr);




More information about the wine-cvs mailing list