[PATCH 2/4] comctl32/listbox: Implement LBS_NODATA for multi-selection listboxes

Huw Davies huw at codeweavers.com
Mon Feb 25 04:37:51 CST 2019


On Fri, Feb 22, 2019 at 02:00:57PM +0200, Gabriel Iv─âncescu wrote:
> Use a byte array to store selection state of items, since we don't need any
> other data for LBS_NODATA multi-selection listboxes. This improves memory
> usage dramatically for large lists, and performance boosts are nice too.
> 
> Signed-off-by: Gabriel Iv─âncescu <gabrielopcode at gmail.com>
> ---
> 
> For now, this should be good enough (byte array). Unfortunately, using
> other storage representations in the future (like selection ranges, or bit
> arrays) would probably need the avoided NODATA helpers to split the functions
> (in order to be easier maintainable), so for now this will suffice.
> 
>  dlls/comctl32/listbox.c | 48 +++++++++++++++++++++++++++++++++++------
>  1 file changed, 41 insertions(+), 7 deletions(-)
> 
> diff --git a/dlls/comctl32/listbox.c b/dlls/comctl32/listbox.c
> index b6024c3..5afdf5c 100644
> --- a/dlls/comctl32/listbox.c
> +++ b/dlls/comctl32/listbox.c
> @@ -18,8 +18,6 @@
>   * License along with this library; if not, write to the Free Software
>   * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
>   *
> - * TODO:
> - *    - LBS_NODATA for multi-selection listboxes
>   */
>  
>  #include <string.h>
> @@ -127,6 +125,15 @@ static TIMER_DIRECTION LISTBOX_Timer = LB_TIMER_NONE;
>  
>  static LRESULT LISTBOX_GetItemRect( const LB_DESCR *descr, INT index, RECT *rect );
>  
> +/*
> +   Resize the item storage array if needed.
> +
> +   For single-selection LBS_NODATA listboxes, no storage is allocated,
> +   and thus descr->items will always be NULL.
> +
> +   For multi-selection LBS_NODATA listboxes, one byte per item is stored
> +   for the item's selection state, instead of the usual LB_ITEMDATA.
> +*/
>  static BOOL resize_storage(LB_DESCR *descr, UINT items_size)
>  {
>      LB_ITEMDATA *items;
> @@ -137,7 +144,10 @@ static BOOL resize_storage(LB_DESCR *descr, UINT items_size)
>          items_size = (items_size + LB_ARRAY_GRANULARITY - 1) & ~(LB_ARRAY_GRANULARITY - 1);
>          if ((descr->style & (LBS_NODATA | LBS_MULTIPLESEL | LBS_EXTENDEDSEL)) != LBS_NODATA)
>          {
> -            items = heap_realloc(descr->items, items_size * sizeof(LB_ITEMDATA));
> +            size_t size = items_size;
> +            if (!(descr->style & LBS_NODATA)) size *= sizeof(LB_ITEMDATA);
> +
> +            items = heap_realloc(descr->items, size);
>              if (!items)
>              {
>                  SEND_NOTIFICATION(descr, LBN_ERRSPACE);
> @@ -150,8 +160,7 @@ static BOOL resize_storage(LB_DESCR *descr, UINT items_size)
>  
>      if ((descr->style & LBS_NODATA) && descr->items && items_size > descr->nb_items)
>      {
> -        memset(&descr->items[descr->nb_items], 0,
> -               (items_size - descr->nb_items) * sizeof(LB_ITEMDATA));
> +        memset((BYTE*)descr->items + descr->nb_items, 0, items_size - descr->nb_items);
>      }
>      return TRUE;
>  }
> @@ -180,13 +189,21 @@ static BOOL is_item_selected( const LB_DESCR *descr, UINT index )
>  {
>      if (!(descr->style & (LBS_MULTIPLESEL | LBS_EXTENDEDSEL)))
>          return index == descr->selected_item;
> -    return descr->items[index].selected;
> +    if (descr->style & LBS_NODATA)
> +        return ((BYTE*)descr->items)[index];
> +    else
> +        return descr->items[index].selected;

Make descr->items a union of the two types, that will get rid of these
horrible casts.  If you find you need to change a lot of code to make
this work, then you need to add more calls to item getter/setter helpers.

>  }
>  
>  static void set_item_selected_state(LB_DESCR *descr, UINT index, BOOL state)
>  {
>      if (descr->style & (LBS_MULTIPLESEL | LBS_EXTENDEDSEL))
> -        descr->items[index].selected = state;
> +    {
> +        if (descr->style & LBS_NODATA)
> +            ((BYTE*)descr->items)[index] = state;
> +        else
> +            descr->items[index].selected = state;
> +    }
>  }
>  
>  static void insert_item_data(LB_DESCR *descr, UINT index, WCHAR *str, ULONG_PTR data)
> @@ -194,6 +211,15 @@ static void insert_item_data(LB_DESCR *descr, UINT index, WCHAR *str, ULONG_PTR
>      LB_ITEMDATA *item;
>  
>      if (!descr->items) return;
> +    if (descr->style & LBS_NODATA)
> +    {
> +        BYTE *item = (BYTE*)descr->items + index;
> +
> +        if (index < descr->nb_items)
> +            memmove(item + 1, item, descr->nb_items - index);
> +        *item = FALSE;
> +        return;
> +    }

This is essentially the same code as what follows for the !LBS_NODATA
case.  If you added a helper to return the size of an item then these
could be merged.  This helper could also be used in resize_storage().

Huw.



More information about the wine-devel mailing list