Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=32374 Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com ---
IS_MULTISELECT was not used because GetSelItems would blow up otherwise (doesn't check for LBS_NOSEL, so the storage itself has to be based without it).
dlls/comctl32/listbox.c | 166 +++++++++++++++++++++++++--------------- 1 file changed, 106 insertions(+), 60 deletions(-)
diff --git a/dlls/comctl32/listbox.c b/dlls/comctl32/listbox.c index 9dbd184..6953bc9 100644 --- a/dlls/comctl32/listbox.c +++ b/dlls/comctl32/listbox.c @@ -19,7 +19,7 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA * * TODO: - * - LBS_NODATA + * - LBS_NODATA for multi-selection listboxes */
#include <string.h> @@ -129,8 +129,26 @@ static LRESULT LISTBOX_GetItemRect( const LB_DESCR *descr, INT index, RECT *rect
/*********************************************************************** * Helper functions for LBS_NODATA listboxes + * + * Since LBS_NODATA listboxes can be extremely large, we need to store + * them with minimal overhead, both for performance and memory usage. + * + * In all cases, it is important to note that descr->items must never be + * dereferenced directly with LBS_NODATA, outside of these helpers. + * + * For single-selection listboxes, we store literally no data for items, + * and thus descr->items will always be NULL in this case. + * + * FIXME: Multi-selection listboxes are not implemented yet for LBS_NODATA. + * */ -static BOOL NODATA_expand(LB_DESCR *descr, UINT num) +static BOOL is_singlesel_NODATA(const LB_DESCR *descr) +{ + return (descr->style & LBS_NODATA) && + !(descr->style & (LBS_MULTIPLESEL | LBS_EXTENDEDSEL)); +} + +static BOOL NODATA_multisel_expand(LB_DESCR *descr, UINT num) { LB_ITEMDATA *p = descr->items;
@@ -151,7 +169,7 @@ static BOOL NODATA_expand(LB_DESCR *descr, UINT num) return TRUE; }
-static void NODATA_shrink(LB_DESCR *descr, UINT orig_num) +static void NODATA_multisel_shrink(LB_DESCR *descr, UINT orig_num) { LB_ITEMDATA *p = descr->items; UINT sz = descr->nb_items * sizeof(*p); @@ -173,6 +191,8 @@ static void NODATA_shrink(LB_DESCR *descr, UINT orig_num)
static BOOL is_item_selected(LB_DESCR *descr, UINT index) { + if (!(descr->style & (LBS_MULTIPLESEL | LBS_EXTENDEDSEL))) + return index == descr->selected_item; return descr->items[index].selected; }
@@ -570,6 +590,7 @@ static void LISTBOX_PaintItem( LB_DESCR *descr, HDC hdc, const RECT *rect, dis.hDC = hdc; dis.itemID = index; dis.itemState = 0; + if (is_singlesel_NODATA(descr)) item = NULL; if (is_item_selected(descr, index)) dis.itemState |= ODS_SELECTED; if (!ignoreFocus && (descr->focus_item == index) && (descr->caret_on) && @@ -726,6 +747,9 @@ static LRESULT LISTBOX_InitStorage( LB_DESCR *descr, UINT nb_items ) { LB_ITEMDATA *item;
+ if (is_singlesel_NODATA(descr)) + return LB_OKAY; + if (descr->items) { nb_items += descr->nb_items; if (nb_items <= descr->array_size) return LB_OKAY; @@ -1491,10 +1515,13 @@ static LRESULT LISTBOX_SetSelection( LB_DESCR *descr, INT index, { INT oldsel = descr->selected_item; if (index == oldsel) return LB_OKAY; - if (oldsel != -1) descr->items[oldsel].selected = FALSE; - if (index != -1) descr->items[index].selected = TRUE; - if (oldsel != -1) LISTBOX_RepaintItem( descr, oldsel, ODA_SELECT ); + if (!(descr->style & LBS_NODATA)) + { + if (oldsel != -1) descr->items[oldsel].selected = FALSE; + if (index != -1) descr->items[index].selected = TRUE; + } descr->selected_item = index; + if (oldsel != -1) LISTBOX_RepaintItem( descr, oldsel, ODA_SELECT ); if (index != -1) LISTBOX_RepaintItem( descr, index, ODA_SELECT ); if (send_notify && descr->nb_items) SEND_NOTIFICATION( descr, (index != -1) ? LBN_SELCHANGE : LBN_SELCANCEL ); @@ -1566,41 +1593,46 @@ static LRESULT LISTBOX_InsertItem( LB_DESCR *descr, INT index,
if (index == -1) index = descr->nb_items; else if ((index < 0) || (index > descr->nb_items)) return LB_ERR; - if (descr->nb_items == descr->array_size) + if (is_singlesel_NODATA(descr)) { - /* We need to grow the array */ - if (LISTBOX_InitStorage(descr, 1) == LB_ERRSPACE) - return LB_ERRSPACE; + descr->nb_items++; } + else + { + if (descr->nb_items == descr->array_size) + { + /* We need to grow the array */ + if (LISTBOX_InitStorage(descr, 1) == LB_ERRSPACE) + return LB_ERRSPACE; + }
- /* Insert the item structure */ - - item = &descr->items[index]; - if (index < descr->nb_items) - RtlMoveMemory( item + 1, item, - (descr->nb_items - index) * sizeof(LB_ITEMDATA) ); - item->str = str; - item->data = HAS_STRINGS(descr) ? 0 : data; - item->height = 0; - item->selected = FALSE; - descr->nb_items++; - - /* Get item height */ + /* Insert the item structure */ + item = &descr->items[index]; + if (index < descr->nb_items) + RtlMoveMemory( item + 1, item, + (descr->nb_items - index) * sizeof(LB_ITEMDATA) ); + item->str = str; + item->data = HAS_STRINGS(descr) ? 0 : data; + item->height = 0; + item->selected = FALSE; + descr->nb_items++;
- if (descr->style & LBS_OWNERDRAWVARIABLE) - { - MEASUREITEMSTRUCT mis; - UINT id = (UINT)GetWindowLongPtrW( descr->self, GWLP_ID ); + /* Get item height */ + if (descr->style & LBS_OWNERDRAWVARIABLE) + { + MEASUREITEMSTRUCT mis; + UINT id = (UINT)GetWindowLongPtrW( descr->self, GWLP_ID );
- mis.CtlType = ODT_LISTBOX; - mis.CtlID = id; - mis.itemID = index; - mis.itemData = data; - mis.itemHeight = descr->item_height; - SendMessageW( descr->owner, WM_MEASUREITEM, id, (LPARAM)&mis ); - item->height = mis.itemHeight ? mis.itemHeight : 1; - TRACE("[%p]: measure item %d (%s) = %d\n", - descr->self, index, str ? debugstr_w(str) : "", item->height ); + mis.CtlType = ODT_LISTBOX; + mis.CtlID = id; + mis.itemID = index; + mis.itemData = data; + mis.itemHeight = descr->item_height; + SendMessageW( descr->owner, WM_MEASUREITEM, id, (LPARAM)&mis ); + item->height = mis.itemHeight ? mis.itemHeight : 1; + TRACE("[%p]: measure item %d (%s) = %d\n", + descr->self, index, str ? debugstr_w(str) : "", item->height ); + } }
/* Repaint the items */ @@ -1715,32 +1747,42 @@ static LRESULT LISTBOX_RemoveItem( LB_DESCR *descr, INT index ) LISTBOX_InvalidateItems( descr, index );
descr->nb_items--; - LISTBOX_DeleteItem( descr, index ); - - if (!descr->nb_items) return LB_OKAY; - - /* Remove the item */ + if (is_singlesel_NODATA(descr)) + { + if (!descr->nb_items) + { + SendMessageW(descr->self, LB_RESETCONTENT, 0, 0); + return LB_OKAY; + } + } + else + { + LISTBOX_DeleteItem( descr, index );
- item = &descr->items[index]; - if (index < descr->nb_items) - RtlMoveMemory( item, item + 1, - (descr->nb_items - index) * sizeof(LB_ITEMDATA) ); - if (descr->anchor_item == descr->nb_items) descr->anchor_item--; + if (!descr->nb_items) return LB_OKAY;
- /* Shrink the item array if possible */ + /* Remove the item */ + item = &descr->items[index]; + if (index < descr->nb_items) + RtlMoveMemory( item, item + 1, + (descr->nb_items - index) * sizeof(LB_ITEMDATA) );
- max_items = descr->array_size; - if (descr->nb_items < max_items - 2*LB_ARRAY_GRANULARITY) - { - max_items -= LB_ARRAY_GRANULARITY; - item = HeapReAlloc( GetProcessHeap(), 0, descr->items, - max_items * sizeof(LB_ITEMDATA) ); - if (item) + /* Shrink the item array if possible */ + max_items = descr->array_size; + if (descr->nb_items < max_items - 2*LB_ARRAY_GRANULARITY) { - descr->array_size = max_items; - descr->items = item; + max_items -= LB_ARRAY_GRANULARITY; + item = HeapReAlloc( GetProcessHeap(), 0, descr->items, + max_items * sizeof(LB_ITEMDATA) ); + if (item) + { + descr->array_size = max_items; + descr->items = item; + } } } + descr->anchor_item = min(descr->anchor_item, descr->nb_items - 1); + /* Repaint the items */
LISTBOX_UpdateScroll( descr ); @@ -1778,7 +1820,8 @@ static void LISTBOX_ResetContent( LB_DESCR *descr ) { INT i;
- for(i = descr->nb_items - 1; i>=0; i--) LISTBOX_DeleteItem( descr, i); + if (descr->items) + for (i = descr->nb_items - 1; i >= 0; i--) LISTBOX_DeleteItem(descr, i); HeapFree( GetProcessHeap(), 0, descr->items ); descr->nb_items = 0; descr->top_item = 0; @@ -1801,8 +1844,10 @@ static LRESULT LISTBOX_SetCount( LB_DESCR *descr, UINT count )
if (count > descr->nb_items) { - if (!NODATA_expand(descr, count)) + if ((descr->style & (LBS_MULTIPLESEL | LBS_EXTENDEDSEL)) && + !NODATA_multisel_expand(descr, count)) return LB_ERRSPACE; + orig_num = descr->nb_items; descr->nb_items = count;
@@ -1823,8 +1868,9 @@ static LRESULT LISTBOX_SetCount( LB_DESCR *descr, UINT count ) { descr->anchor_item = min(descr->anchor_item, count - 1);
- NODATA_shrink(descr, orig_num); - if (descr->selected_item >= descr->nb_items) + if (descr->style & (LBS_MULTIPLESEL | LBS_EXTENDEDSEL)) + NODATA_multisel_shrink(descr, orig_num); + else if (descr->selected_item >= descr->nb_items) descr->selected_item = -1;
LISTBOX_UpdateScroll(descr);