[PATCH v4 09/11] comctl32/listbox: (De)allocate everything at once in SetCount
Gabriel Ivăncescu
gabrielopcode at gmail.com
Wed Nov 21 13:15:12 CST 2018
Signed-off-by: Gabriel Ivăncescu <gabrielopcode at 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 );
--
2.19.1
More information about the wine-devel
mailing list