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 );