[PATCH 2/4] comctl32/listbox: Implement LBS_NODATA for multi-selection listboxes
Gabriel Ivăncescu
gabrielopcode at gmail.com
Mon Feb 25 05:15:53 CST 2019
On 2/25/19 12:37 PM, Huw Davies wrote:
> 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.
>
Yeah, I wanted to avoid too many changes (also because I made these
casts inside the helpers only), it's too bad we can't use anonymous
unions in Wine's C89. I'll see what I can do with the union and helpers.
More information about the wine-devel
mailing list