Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com --- dlls/comctl32/tests/listbox.c | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+)
diff --git a/dlls/comctl32/tests/listbox.c b/dlls/comctl32/tests/listbox.c index 43da3ca..d457b13 100644 --- a/dlls/comctl32/tests/listbox.c +++ b/dlls/comctl32/tests/listbox.c @@ -110,6 +110,15 @@ static HWND create_listbox(DWORD add_style, HWND parent) return handle; }
+static UINT lb_resetcontent_count; +static WNDPROC lb_test_subclass_proc_prev; +static LRESULT WINAPI lb_test_subclass_proc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) +{ + if (msg == LB_RESETCONTENT) lb_resetcontent_count++; + + return CallWindowProcW(lb_test_subclass_proc_prev, hwnd, msg, wParam, lParam); +} + struct listbox_prop { DWORD add_style; @@ -227,6 +236,17 @@ static void run_test(DWORD style, const struct listbox_test test) res = SendMessageA(hLB, LB_GETCOUNT, 0, 0); ok(res == 4, "Expected 4 items, got %d\n", res);
+ /* Confirm that emptying the listbox sends a LB_RESETCONTENT to itself */ + lb_test_subclass_proc_prev = (WNDPROC)SetWindowLongPtrW(hLB, GWLP_WNDPROC, (LONG_PTR)lb_test_subclass_proc); + + lb_resetcontent_count = 0; + for (i = 4; i--;) + { + res = SendMessageA(hLB, LB_DELETESTRING, 0, 0); + ok(res == i, "Expected %d items, got %d\n", i, res); + } + ok(lb_resetcontent_count == 1, "Expected 1 LB_RESETCONTENT message, got %u\n", lb_resetcontent_count); + DestroyWindow(hLB); }
@@ -1798,6 +1818,14 @@ static void test_set_count( void ) GetUpdateRect( listbox, &r, TRUE ); ok( !IsRectEmpty( &r ), "got empty rect\n");
+ /* Confirm that emptying the listbox sends a LB_RESETCONTENT to itself */ + lb_test_subclass_proc_prev = (WNDPROC)SetWindowLongPtrW(listbox, GWLP_WNDPROC, (LONG_PTR)lb_test_subclass_proc); + + lb_resetcontent_count = 0; + ret = SendMessageA(listbox, LB_SETCOUNT, 0, 0); + ok(ret == 0, "got %d\n", ret); + ok(lb_resetcontent_count == 1, "Expected 1 LB_RESETCONTENT message, got %u\n", lb_resetcontent_count); + DestroyWindow( listbox );
for (i = 0; i < ARRAY_SIZE(styles); ++i)
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com --- dlls/comctl32/tests/listbox.c | 55 +++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+)
diff --git a/dlls/comctl32/tests/listbox.c b/dlls/comctl32/tests/listbox.c index d457b13..be42e6b 100644 --- a/dlls/comctl32/tests/listbox.c +++ b/dlls/comctl32/tests/listbox.c @@ -735,6 +735,60 @@ static void test_listbox_height(void) DestroyWindow( hList ); }
+static void test_changing_selection_styles(void) +{ + static const DWORD styles[] = + { + 0, + LBS_NODATA | LBS_OWNERDRAWFIXED + }; + static const DWORD selstyles[] = + { + 0, + LBS_MULTIPLESEL, + LBS_EXTENDEDSEL, + LBS_MULTIPLESEL | LBS_EXTENDEDSEL + }; + HWND parent, listbox; + LONG ret, expected; + DWORD style; + UINT i, j; + + parent = create_parent(); + ok(parent != NULL, "Failed to create parent window.\n"); + for (i = 0; i < ARRAY_SIZE(styles); i++) + { + for (j = 0; j < ARRAY_SIZE(selstyles); j++) + { + listbox = CreateWindowA(WC_LISTBOXA, "TestList", styles[i] | selstyles[j] | WS_CHILD | WS_VISIBLE, + 0, 0, 100, 100, parent, (HMENU)ID_LISTBOX, NULL, 0); + ok(listbox != NULL, "%u: Failed to create ListBox window.\n", j); + + style = GetWindowLongA(listbox, GWL_STYLE); + ok((style & (LBS_MULTIPLESEL | LBS_EXTENDEDSEL)) == selstyles[j], "%u: unexpected window styles %#x.\n", j, style); + + if (selstyles[j] & (LBS_MULTIPLESEL | LBS_EXTENDEDSEL)) + { + style &= ~selstyles[j]; + expected = 0; + } + else + { + style |= LBS_MULTIPLESEL | LBS_EXTENDEDSEL; + expected = LB_ERR; + } + SetWindowLongA(listbox, GWL_STYLE, style); + style = GetWindowLongA(listbox, GWL_STYLE); + ok(!(style & selstyles[j]), "%u: unexpected window styles %#x.\n", j, style); + + ret = SendMessageA(listbox, LB_GETSELCOUNT, 0, 0); + ok(ret == expected, "%u: expected %d from LB_GETSELCOUNT, got %d\n", j, expected, ret); + DestroyWindow(listbox); + } + } + DestroyWindow(parent); +} + static void test_itemfrompoint(void) { /* WS_POPUP is required in order to have a more accurate size calculation ( @@ -2390,6 +2444,7 @@ START_TEST(listbox) test_LB_SELITEMRANGE(); test_LB_SETCURSEL(); test_listbox_height(); + test_changing_selection_styles(); test_itemfrompoint(); test_listbox_item_data(); test_listbox_LB_DIR();
Needed for LBS_NODATA.
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com --- dlls/comctl32/listbox.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/dlls/comctl32/listbox.c b/dlls/comctl32/listbox.c index dbc7b4a..a380b59 100644 --- a/dlls/comctl32/listbox.c +++ b/dlls/comctl32/listbox.c @@ -483,7 +483,7 @@ static INT LISTBOX_GetItemFromPoint( const LB_DESCR *descr, INT x, INT y ) * Paint an item. */ static void LISTBOX_PaintItem( LB_DESCR *descr, HDC hdc, const RECT *rect, - INT index, UINT action, BOOL ignoreFocus ) + UINT index, UINT action, BOOL ignoreFocus ) { LB_ITEMDATA *item = NULL; if (index < descr->nb_items) item = &descr->items[index]; @@ -494,7 +494,7 @@ static void LISTBOX_PaintItem( LB_DESCR *descr, HDC hdc, const RECT *rect, RECT r; HRGN hrgn;
- if (!item) + if (index >= descr->nb_items) { if (action == ODA_FOCUS) DrawFocusRect( hdc, rect );
Needed for LBS_NODATA.
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com --- dlls/comctl32/listbox.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/dlls/comctl32/listbox.c b/dlls/comctl32/listbox.c index a380b59..a77de49 100644 --- a/dlls/comctl32/listbox.c +++ b/dlls/comctl32/listbox.c @@ -522,10 +522,10 @@ static void LISTBOX_PaintItem( LB_DESCR *descr, HDC hdc, const RECT *rect, (descr->caret_on) && (descr->in_focus)) dis.itemState |= ODS_FOCUS; if (!IsWindowEnabled(descr->self)) dis.itemState |= ODS_DISABLED; - dis.itemData = item->data; + dis.itemData = item ? item->data : 0; dis.rcItem = *rect; TRACE("[%p]: drawitem %d (%s) action=%02x state=%02x rect=%s\n", - descr->self, index, debugstr_w(item->str), action, + descr->self, index, debugstr_w(item ? item->str : NULL), action, dis.itemState, wine_dbgstr_rect(rect) ); SendMessageW(descr->owner, WM_DRAWITEM, dis.CtlID, (LPARAM)&dis); SelectClipRgn( hdc, hrgn );
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com --- dlls/comctl32/listbox.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-)
diff --git a/dlls/comctl32/listbox.c b/dlls/comctl32/listbox.c index a77de49..0638cdb 100644 --- a/dlls/comctl32/listbox.c +++ b/dlls/comctl32/listbox.c @@ -125,6 +125,11 @@ static TIMER_DIRECTION LISTBOX_Timer = LB_TIMER_NONE;
static LRESULT LISTBOX_GetItemRect( const LB_DESCR *descr, INT index, RECT *rect );
+static BOOL is_item_selected(LB_DESCR *descr, UINT index) +{ + return descr->items[index].selected; +} + /*********************************************************************** * LISTBOX_GetCurrentPageSize * @@ -517,7 +522,7 @@ static void LISTBOX_PaintItem( LB_DESCR *descr, HDC hdc, const RECT *rect, dis.hDC = hdc; dis.itemID = index; dis.itemState = 0; - if (item->selected) dis.itemState |= ODS_SELECTED; + if (is_item_selected(descr, index)) dis.itemState |= ODS_SELECTED; if (!ignoreFocus && (descr->focus_item == index) && (descr->caret_on) && (descr->in_focus)) dis.itemState |= ODS_FOCUS; @@ -2763,7 +2768,7 @@ static LRESULT CALLBACK LISTBOX_WindowProc( HWND hwnd, UINT msg, WPARAM wParam, case LB_GETSEL: if (((INT)wParam < 0) || ((INT)wParam >= descr->nb_items)) return LB_ERR; - return descr->items[wParam].selected; + return is_item_selected(descr, wParam);
case LB_SETSEL: ret = LISTBOX_SetSelection( descr, lParam, wParam, FALSE );
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com --- dlls/comctl32/listbox.c | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-)
diff --git a/dlls/comctl32/listbox.c b/dlls/comctl32/listbox.c index 0638cdb..e05ab6f 100644 --- a/dlls/comctl32/listbox.c +++ b/dlls/comctl32/listbox.c @@ -1526,19 +1526,8 @@ static LRESULT LISTBOX_InsertItem( LB_DESCR *descr, INT index, if (descr->nb_items == max_items) { /* We need to grow the array */ - max_items += LB_ARRAY_GRANULARITY; - if (descr->items) - item = HeapReAlloc( GetProcessHeap(), 0, descr->items, - max_items * sizeof(LB_ITEMDATA) ); - else - item = HeapAlloc( GetProcessHeap(), 0, - max_items * sizeof(LB_ITEMDATA) ); - if (!item) - { - SEND_NOTIFICATION( descr, LBN_ERRSPACE ); + if (LISTBOX_InitStorage(descr, 1) == LB_ERRSPACE) return LB_ERRSPACE; - } - descr->items = item; }
/* Insert the item structure */
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com --- dlls/comctl32/listbox.c | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-)
diff --git a/dlls/comctl32/listbox.c b/dlls/comctl32/listbox.c index e05ab6f..afa6700 100644 --- a/dlls/comctl32/listbox.c +++ b/dlls/comctl32/listbox.c @@ -70,6 +70,7 @@ typedef struct UINT style; /* Window style */ INT width; /* Window width */ INT height; /* Window height */ + UINT array_size; /* Total number of allocated items in the array */ LB_ITEMDATA *items; /* Array of items */ INT nb_items; /* Number of items */ INT top_item; /* Top visible item */ @@ -681,7 +682,7 @@ static LRESULT LISTBOX_InitStorage( LB_DESCR *descr, INT nb_items ) nb_items += LB_ARRAY_GRANULARITY - 1; nb_items -= (nb_items % LB_ARRAY_GRANULARITY); if (descr->items) { - nb_items += HeapSize( GetProcessHeap(), 0, descr->items ) / sizeof(*item); + nb_items += descr->array_size; item = HeapReAlloc( GetProcessHeap(), 0, descr->items, nb_items * sizeof(LB_ITEMDATA)); } @@ -695,6 +696,7 @@ static LRESULT LISTBOX_InitStorage( LB_DESCR *descr, INT nb_items ) SEND_NOTIFICATION( descr, LBN_ERRSPACE ); return LB_ERRSPACE; } + descr->array_size = nb_items; descr->items = item; return LB_OKAY; } @@ -1516,14 +1518,11 @@ static LRESULT LISTBOX_InsertItem( LB_DESCR *descr, INT index, LPWSTR str, ULONG_PTR data ) { LB_ITEMDATA *item; - INT max_items; INT oldfocus = descr->focus_item;
if (index == -1) index = descr->nb_items; else if ((index < 0) || (index > descr->nb_items)) return LB_ERR; - if (!descr->items) max_items = 0; - else max_items = HeapSize( GetProcessHeap(), 0, descr->items ) / sizeof(*item); - if (descr->nb_items == max_items) + if (descr->nb_items == descr->array_size) { /* We need to grow the array */ if (LISTBOX_InitStorage(descr, 1) == LB_ERRSPACE) @@ -1686,13 +1685,17 @@ static LRESULT LISTBOX_RemoveItem( LB_DESCR *descr, INT index )
/* Shrink the item array if possible */
- max_items = HeapSize( GetProcessHeap(), 0, descr->items ) / sizeof(LB_ITEMDATA); + max_items = descr->array_size; if (descr->nb_items < max_items - 2*LB_ARRAY_GRANULARITY) { max_items -= LB_ARRAY_GRANULARITY; item = HeapReAlloc( GetProcessHeap(), 0, descr->items, max_items * sizeof(LB_ITEMDATA) ); - if (item) descr->items = item; + if (item) + { + descr->array_size = max_items; + descr->items = item; + } } /* Repaint the items */
@@ -1738,6 +1741,7 @@ static void LISTBOX_ResetContent( LB_DESCR *descr ) descr->selected_item = -1; descr->focus_item = 0; descr->anchor_item = -1; + descr->array_size = 0; descr->items = NULL; }
@@ -2475,6 +2479,7 @@ static BOOL LISTBOX_Create( HWND hwnd, LPHEADCOMBO lphc ) descr->width = rect.right - rect.left; descr->height = rect.bottom - rect.top; descr->items = NULL; + descr->array_size = 0; descr->nb_items = 0; descr->top_item = 0; descr->selected_item = -1;
Only increase the item array if we actually have to. Previously, sending for example just 1 to nb_items repeatedly would always increase the array by LB_ARRAY_GRANULARITY, even if there was plenty of space available.
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com --- dlls/comctl32/listbox.c | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-)
diff --git a/dlls/comctl32/listbox.c b/dlls/comctl32/listbox.c index afa6700..f7ed2b8 100644 --- a/dlls/comctl32/listbox.c +++ b/dlls/comctl32/listbox.c @@ -36,6 +36,7 @@ #include "wine/unicode.h" #include "wine/exception.h" #include "wine/debug.h" +#include "wine/heap.h"
#include "comctl32.h"
@@ -675,22 +676,19 @@ static void LISTBOX_DrawFocusRect( LB_DESCR *descr, BOOL on ) /*********************************************************************** * LISTBOX_InitStorage */ -static LRESULT LISTBOX_InitStorage( LB_DESCR *descr, INT nb_items ) +static LRESULT LISTBOX_InitStorage( LB_DESCR *descr, UINT nb_items ) { LB_ITEMDATA *item;
- nb_items += LB_ARRAY_GRANULARITY - 1; - nb_items -= (nb_items % LB_ARRAY_GRANULARITY); if (descr->items) { - nb_items += descr->array_size; - item = HeapReAlloc( GetProcessHeap(), 0, descr->items, - nb_items * sizeof(LB_ITEMDATA)); - } - else { - item = HeapAlloc( GetProcessHeap(), 0, - nb_items * sizeof(LB_ITEMDATA)); + nb_items += descr->nb_items; + if (nb_items <= descr->array_size) return LB_OKAY; }
+ nb_items += LB_ARRAY_GRANULARITY - 1; + nb_items -= nb_items % LB_ARRAY_GRANULARITY; + item = heap_realloc(descr->items, nb_items * sizeof(*item)); + if (!item) { SEND_NOTIFICATION( descr, LBN_ERRSPACE );
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com ---
Prepares for the LBS_NODATA patch.
Please note that NODATA_expand and NODATA_shrink use their own allocators, because of 2 main reasons:
(1) they need to zero the (rounded) memory, so using HEAP_ZERO_MEMORY or memset respectively, since SetCount also changes the count.
(2) they *will be changed* later when multi-select LBS_NODATA is implemented. Thus, they will *not* be almost-duplicates of InitStorage anymore, it's only temporary for now to simplify the future patches. Allowing them to be placed here, in this patch, will allow the multi-select LBS_NODATA patch to be vastly smaller. Please consider this.
dlls/comctl32/listbox.c | 88 ++++++++++++++++++++++++++++++++++++----- 1 file changed, 79 insertions(+), 9 deletions(-)
diff --git a/dlls/comctl32/listbox.c b/dlls/comctl32/listbox.c index f7ed2b8..9dbd184 100644 --- a/dlls/comctl32/listbox.c +++ b/dlls/comctl32/listbox.c @@ -127,11 +127,57 @@ static TIMER_DIRECTION LISTBOX_Timer = LB_TIMER_NONE;
static LRESULT LISTBOX_GetItemRect( const LB_DESCR *descr, INT index, RECT *rect );
+/*********************************************************************** + * Helper functions for LBS_NODATA listboxes + */ +static BOOL NODATA_expand(LB_DESCR *descr, UINT num) +{ + LB_ITEMDATA *p = descr->items; + + if (!p || num > descr->array_size) + { + num += LB_ARRAY_GRANULARITY - 1; + num -= num % LB_ARRAY_GRANULARITY; + if (!p) p = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, num * sizeof(*p)); + else p = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, p, num * sizeof(*p)); + if (!p) + { + SEND_NOTIFICATION(descr, LBN_ERRSPACE); + return FALSE; + } + descr->array_size = num; + descr->items = p; + } + return TRUE; +} + +static void NODATA_shrink(LB_DESCR *descr, UINT orig_num) +{ + LB_ITEMDATA *p = descr->items; + UINT sz = descr->nb_items * sizeof(*p); + UINT orig_sz = orig_num * sizeof(*p); + + if (descr->nb_items + LB_ARRAY_GRANULARITY * 2 < descr->array_size) + { + UINT rnd_sz = sz + LB_ARRAY_GRANULARITY * sizeof(*p) - 1; + rnd_sz -= rnd_sz % (LB_ARRAY_GRANULARITY * sizeof(*p)); + if ((p = HeapReAlloc(GetProcessHeap(), 0, p, rnd_sz))) + { + descr->array_size = rnd_sz / sizeof(*p); + descr->items = p; + orig_sz = rnd_sz; + } + } + memset(&p[sz / sizeof(*p)], 0, orig_sz - sz); +} + static BOOL is_item_selected(LB_DESCR *descr, UINT index) { return descr->items[index].selected; }
+ + /*********************************************************************** * LISTBOX_GetCurrentPageSize * @@ -1747,24 +1793,48 @@ static void LISTBOX_ResetContent( LB_DESCR *descr ) /*********************************************************************** * LISTBOX_SetCount */ -static LRESULT LISTBOX_SetCount( LB_DESCR *descr, INT count ) +static LRESULT LISTBOX_SetCount( LB_DESCR *descr, UINT count ) { - LRESULT ret; + INT orig_num;
if (!(descr->style & LBS_NODATA)) return LB_ERR;
- /* FIXME: this is far from optimal... */ if (count > descr->nb_items) { - while (count > descr->nb_items) - if ((ret = LISTBOX_InsertString( descr, -1, 0 )) < 0) - return ret; + if (!NODATA_expand(descr, count)) + return LB_ERRSPACE; + orig_num = descr->nb_items; + descr->nb_items = count; + + LISTBOX_UpdateScroll(descr); + LISTBOX_InvalidateItems(descr, orig_num); + + /* If listbox was empty, set focus to the first item */ + if (orig_num == 0) LISTBOX_SetCaretIndex(descr, 0, FALSE); } else if (count < descr->nb_items) { - while (count < descr->nb_items) - if ((ret = LISTBOX_RemoveItem( descr, (descr->nb_items - 1) )) < 0) - return ret; + LISTBOX_InvalidateItems(descr, count); + orig_num = descr->nb_items; + descr->nb_items = count; + + if (count == 0) SendMessageW(descr->self, LB_RESETCONTENT, 0, 0); + else + { + descr->anchor_item = min(descr->anchor_item, count - 1); + + NODATA_shrink(descr, orig_num); + if (descr->selected_item >= descr->nb_items) + descr->selected_item = -1; + + LISTBOX_UpdateScroll(descr); + + /* If we removed the scrollbar, reset the top of the list */ + if (descr->nb_items <= descr->page_size && orig_num > descr->page_size) + LISTBOX_SetTopItem(descr, 0, TRUE); + + descr->focus_item = min(descr->focus_item, descr->nb_items - 1); + } }
InvalidateRect( descr->self, NULL, TRUE );
Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=32374 Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com ---
IS_MULTISELECT was not used because GetSelItems would blow up otherwise (doesn't check for LBS_NOSEL, so the storage itself has to be based without it).
dlls/comctl32/listbox.c | 166 +++++++++++++++++++++++++--------------- 1 file changed, 106 insertions(+), 60 deletions(-)
diff --git a/dlls/comctl32/listbox.c b/dlls/comctl32/listbox.c index 9dbd184..6953bc9 100644 --- a/dlls/comctl32/listbox.c +++ b/dlls/comctl32/listbox.c @@ -19,7 +19,7 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA * * TODO: - * - LBS_NODATA + * - LBS_NODATA for multi-selection listboxes */
#include <string.h> @@ -129,8 +129,26 @@ static LRESULT LISTBOX_GetItemRect( const LB_DESCR *descr, INT index, RECT *rect
/*********************************************************************** * Helper functions for LBS_NODATA listboxes + * + * Since LBS_NODATA listboxes can be extremely large, we need to store + * them with minimal overhead, both for performance and memory usage. + * + * In all cases, it is important to note that descr->items must never be + * dereferenced directly with LBS_NODATA, outside of these helpers. + * + * For single-selection listboxes, we store literally no data for items, + * and thus descr->items will always be NULL in this case. + * + * FIXME: Multi-selection listboxes are not implemented yet for LBS_NODATA. + * */ -static BOOL NODATA_expand(LB_DESCR *descr, UINT num) +static BOOL is_singlesel_NODATA(const LB_DESCR *descr) +{ + return (descr->style & LBS_NODATA) && + !(descr->style & (LBS_MULTIPLESEL | LBS_EXTENDEDSEL)); +} + +static BOOL NODATA_multisel_expand(LB_DESCR *descr, UINT num) { LB_ITEMDATA *p = descr->items;
@@ -151,7 +169,7 @@ static BOOL NODATA_expand(LB_DESCR *descr, UINT num) return TRUE; }
-static void NODATA_shrink(LB_DESCR *descr, UINT orig_num) +static void NODATA_multisel_shrink(LB_DESCR *descr, UINT orig_num) { LB_ITEMDATA *p = descr->items; UINT sz = descr->nb_items * sizeof(*p); @@ -173,6 +191,8 @@ static void NODATA_shrink(LB_DESCR *descr, UINT orig_num)
static BOOL is_item_selected(LB_DESCR *descr, UINT index) { + if (!(descr->style & (LBS_MULTIPLESEL | LBS_EXTENDEDSEL))) + return index == descr->selected_item; return descr->items[index].selected; }
@@ -570,6 +590,7 @@ static void LISTBOX_PaintItem( LB_DESCR *descr, HDC hdc, const RECT *rect, dis.hDC = hdc; dis.itemID = index; dis.itemState = 0; + if (is_singlesel_NODATA(descr)) item = NULL; if (is_item_selected(descr, index)) dis.itemState |= ODS_SELECTED; if (!ignoreFocus && (descr->focus_item == index) && (descr->caret_on) && @@ -726,6 +747,9 @@ static LRESULT LISTBOX_InitStorage( LB_DESCR *descr, UINT nb_items ) { LB_ITEMDATA *item;
+ if (is_singlesel_NODATA(descr)) + return LB_OKAY; + if (descr->items) { nb_items += descr->nb_items; if (nb_items <= descr->array_size) return LB_OKAY; @@ -1491,10 +1515,13 @@ static LRESULT LISTBOX_SetSelection( LB_DESCR *descr, INT index, { INT oldsel = descr->selected_item; if (index == oldsel) return LB_OKAY; - if (oldsel != -1) descr->items[oldsel].selected = FALSE; - if (index != -1) descr->items[index].selected = TRUE; - if (oldsel != -1) LISTBOX_RepaintItem( descr, oldsel, ODA_SELECT ); + if (!(descr->style & LBS_NODATA)) + { + if (oldsel != -1) descr->items[oldsel].selected = FALSE; + if (index != -1) descr->items[index].selected = TRUE; + } descr->selected_item = index; + if (oldsel != -1) LISTBOX_RepaintItem( descr, oldsel, ODA_SELECT ); if (index != -1) LISTBOX_RepaintItem( descr, index, ODA_SELECT ); if (send_notify && descr->nb_items) SEND_NOTIFICATION( descr, (index != -1) ? LBN_SELCHANGE : LBN_SELCANCEL ); @@ -1566,41 +1593,46 @@ static LRESULT LISTBOX_InsertItem( LB_DESCR *descr, INT index,
if (index == -1) index = descr->nb_items; else if ((index < 0) || (index > descr->nb_items)) return LB_ERR; - if (descr->nb_items == descr->array_size) + if (is_singlesel_NODATA(descr)) { - /* We need to grow the array */ - if (LISTBOX_InitStorage(descr, 1) == LB_ERRSPACE) - return LB_ERRSPACE; + descr->nb_items++; } + else + { + if (descr->nb_items == descr->array_size) + { + /* We need to grow the array */ + if (LISTBOX_InitStorage(descr, 1) == LB_ERRSPACE) + return LB_ERRSPACE; + }
- /* Insert the item structure */ - - item = &descr->items[index]; - if (index < descr->nb_items) - RtlMoveMemory( item + 1, item, - (descr->nb_items - index) * sizeof(LB_ITEMDATA) ); - item->str = str; - item->data = HAS_STRINGS(descr) ? 0 : data; - item->height = 0; - item->selected = FALSE; - descr->nb_items++; - - /* Get item height */ + /* Insert the item structure */ + item = &descr->items[index]; + if (index < descr->nb_items) + RtlMoveMemory( item + 1, item, + (descr->nb_items - index) * sizeof(LB_ITEMDATA) ); + item->str = str; + item->data = HAS_STRINGS(descr) ? 0 : data; + item->height = 0; + item->selected = FALSE; + descr->nb_items++;
- if (descr->style & LBS_OWNERDRAWVARIABLE) - { - MEASUREITEMSTRUCT mis; - UINT id = (UINT)GetWindowLongPtrW( descr->self, GWLP_ID ); + /* Get item height */ + if (descr->style & LBS_OWNERDRAWVARIABLE) + { + MEASUREITEMSTRUCT mis; + UINT id = (UINT)GetWindowLongPtrW( descr->self, GWLP_ID );
- mis.CtlType = ODT_LISTBOX; - mis.CtlID = id; - mis.itemID = index; - mis.itemData = data; - mis.itemHeight = descr->item_height; - SendMessageW( descr->owner, WM_MEASUREITEM, id, (LPARAM)&mis ); - item->height = mis.itemHeight ? mis.itemHeight : 1; - TRACE("[%p]: measure item %d (%s) = %d\n", - descr->self, index, str ? debugstr_w(str) : "", item->height ); + mis.CtlType = ODT_LISTBOX; + mis.CtlID = id; + mis.itemID = index; + mis.itemData = data; + mis.itemHeight = descr->item_height; + SendMessageW( descr->owner, WM_MEASUREITEM, id, (LPARAM)&mis ); + item->height = mis.itemHeight ? mis.itemHeight : 1; + TRACE("[%p]: measure item %d (%s) = %d\n", + descr->self, index, str ? debugstr_w(str) : "", item->height ); + } }
/* Repaint the items */ @@ -1715,32 +1747,42 @@ static LRESULT LISTBOX_RemoveItem( LB_DESCR *descr, INT index ) LISTBOX_InvalidateItems( descr, index );
descr->nb_items--; - LISTBOX_DeleteItem( descr, index ); - - if (!descr->nb_items) return LB_OKAY; - - /* Remove the item */ + if (is_singlesel_NODATA(descr)) + { + if (!descr->nb_items) + { + SendMessageW(descr->self, LB_RESETCONTENT, 0, 0); + return LB_OKAY; + } + } + else + { + LISTBOX_DeleteItem( descr, index );
- item = &descr->items[index]; - if (index < descr->nb_items) - RtlMoveMemory( item, item + 1, - (descr->nb_items - index) * sizeof(LB_ITEMDATA) ); - if (descr->anchor_item == descr->nb_items) descr->anchor_item--; + if (!descr->nb_items) return LB_OKAY;
- /* Shrink the item array if possible */ + /* Remove the item */ + item = &descr->items[index]; + if (index < descr->nb_items) + RtlMoveMemory( item, item + 1, + (descr->nb_items - index) * sizeof(LB_ITEMDATA) );
- max_items = descr->array_size; - if (descr->nb_items < max_items - 2*LB_ARRAY_GRANULARITY) - { - max_items -= LB_ARRAY_GRANULARITY; - item = HeapReAlloc( GetProcessHeap(), 0, descr->items, - max_items * sizeof(LB_ITEMDATA) ); - if (item) + /* Shrink the item array if possible */ + max_items = descr->array_size; + if (descr->nb_items < max_items - 2*LB_ARRAY_GRANULARITY) { - descr->array_size = max_items; - descr->items = item; + max_items -= LB_ARRAY_GRANULARITY; + item = HeapReAlloc( GetProcessHeap(), 0, descr->items, + max_items * sizeof(LB_ITEMDATA) ); + if (item) + { + descr->array_size = max_items; + descr->items = item; + } } } + descr->anchor_item = min(descr->anchor_item, descr->nb_items - 1); + /* Repaint the items */
LISTBOX_UpdateScroll( descr ); @@ -1778,7 +1820,8 @@ static void LISTBOX_ResetContent( LB_DESCR *descr ) { INT i;
- for(i = descr->nb_items - 1; i>=0; i--) LISTBOX_DeleteItem( descr, i); + if (descr->items) + for (i = descr->nb_items - 1; i >= 0; i--) LISTBOX_DeleteItem(descr, i); HeapFree( GetProcessHeap(), 0, descr->items ); descr->nb_items = 0; descr->top_item = 0; @@ -1801,8 +1844,10 @@ static LRESULT LISTBOX_SetCount( LB_DESCR *descr, UINT count )
if (count > descr->nb_items) { - if (!NODATA_expand(descr, count)) + if ((descr->style & (LBS_MULTIPLESEL | LBS_EXTENDEDSEL)) && + !NODATA_multisel_expand(descr, count)) return LB_ERRSPACE; + orig_num = descr->nb_items; descr->nb_items = count;
@@ -1823,8 +1868,9 @@ static LRESULT LISTBOX_SetCount( LB_DESCR *descr, UINT count ) { descr->anchor_item = min(descr->anchor_item, count - 1);
- NODATA_shrink(descr, orig_num); - if (descr->selected_item >= descr->nb_items) + if (descr->style & (LBS_MULTIPLESEL | LBS_EXTENDEDSEL)) + NODATA_multisel_shrink(descr, orig_num); + else if (descr->selected_item >= descr->nb_items) descr->selected_item = -1;
LISTBOX_UpdateScroll(descr);
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com ---
This is placed here *after* LBS_NODATA's implementation because otherwise the test would timeout or run out of memory (since without LBS_NODATA implementation it ends up allocating forever until it runs out of memory).
dlls/comctl32/tests/listbox.c | 5 +++++ 1 file changed, 5 insertions(+)
diff --git a/dlls/comctl32/tests/listbox.c b/dlls/comctl32/tests/listbox.c index be42e6b..030d492 100644 --- a/dlls/comctl32/tests/listbox.c +++ b/dlls/comctl32/tests/listbox.c @@ -1872,6 +1872,11 @@ static void test_set_count( void ) GetUpdateRect( listbox, &r, TRUE ); ok( !IsRectEmpty( &r ), "got empty rect\n");
+ ret = SendMessageA( listbox, LB_SETCOUNT, -5, 0 ); + ok( ret == 0, "got %d\n", ret ); + ret = SendMessageA( listbox, LB_GETCOUNT, 0, 0 ); + ok( ret == -5, "got %d\n", ret ); + /* Confirm that emptying the listbox sends a LB_RESETCONTENT to itself */ lb_test_subclass_proc_prev = (WNDPROC)SetWindowLongPtrW(listbox, GWLP_WNDPROC, (LONG_PTR)lb_test_subclass_proc);