[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