Module: wine Branch: master Commit: dbd997c3c9284749a06d8b645680c78814bb28c1 URL: http://source.winehq.org/git/wine.git/?a=commit;h=dbd997c3c9284749a06d8b6456...
Author: Nikolay Sivov nsivov@codeweavers.com Date: Sat Jan 12 21:29:38 2013 +0400
comctl32/listview: Update focus index when new item data is already there.
---
dlls/comctl32/listview.c | 32 +++++++++++++++----- dlls/comctl32/tests/listview.c | 63 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 87 insertions(+), 8 deletions(-)
diff --git a/dlls/comctl32/listview.c b/dlls/comctl32/listview.c index bb6d1b2..75aa51b 100644 --- a/dlls/comctl32/listview.c +++ b/dlls/comctl32/listview.c @@ -4164,7 +4164,7 @@ static inline BOOL is_assignable_item(const LVITEMW *lpLVItem, LONG lStyle)
/*** * DESCRIPTION: - * Helper for LISTVIEW_SetItemT *only*: sets item attributes. + * Helper for LISTVIEW_SetItemT and LISTVIEW_InsertItemT: sets item attributes. * * PARAMETER(S): * [I] infoPtr : valid pointer to the listview structure @@ -4256,6 +4256,15 @@ static BOOL set_main_item(LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem, BOOL return FALSE; }
+ /* When item is inserted we need to shift existing focus index if new item has lower index. */ + if (isNew && (stateMask & ~infoPtr->uCallbackMask & LVIS_FOCUSED) && + /* this means we won't hit a focus change path later */ + ((uChanged & LVIF_STATE) == 0 || (!(lpLVItem->state & LVIS_FOCUSED) && (infoPtr->nFocusedItem != lpLVItem->iItem)))) + { + if (infoPtr->nFocusedItem != -1 && (lpLVItem->iItem <= infoPtr->nFocusedItem)) + infoPtr->nFocusedItem++; + } + if (!uChanged) return TRUE; *bChanged = TRUE;
@@ -4288,7 +4297,14 @@ static BOOL set_main_item(LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem, BOOL { ranges_delitem(infoPtr->selectionRanges, lpLVItem->iItem); } - /* if we are asked to change focus, and we manage it, do it */ + /* If we are asked to change focus, and we manage it, do it. + It's important to have all new item data stored at this point, + cause changing existing focus could result in redrawing operation, + which in turn could ask for disp data, application should see all data + for inserted item when processing LVN_GETDISPINFO. + + The way this works application will see nested item change notifications - + changed item notifications interrupted by ones from item loosing focus. */ if (stateMask & ~infoPtr->uCallbackMask & LVIS_FOCUSED) { if (lpLVItem->state & LVIS_FOCUSED) @@ -4320,7 +4336,7 @@ static BOOL set_main_item(LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem, BOOL
/* if we're inserting the item, we're done */ if (isNew) return TRUE; - + /* send LVN_ITEMCHANGED notification */ if (lpLVItem->mask & LVIF_PARAM) nmlv.lParam = lpLVItem->lParam; if (infoPtr->bDoChangeNotify) notify_listview(infoPtr, LVN_ITEMCHANGED, &nmlv); @@ -7680,7 +7696,7 @@ static INT LISTVIEW_InsertItemT(LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem, LVITEMW item; HWND hwndSelf = infoPtr->hwndSelf;
- TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW); + TRACE("(item=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW);
if (infoPtr->dwStyle & LVS_OWNERDATA) return infoPtr->nItemCount++;
@@ -7730,14 +7746,13 @@ static INT LISTVIEW_InsertItemT(LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem, else nItem = min(lpLVItem->iItem, infoPtr->nItemCount);
- TRACE(" inserting at %d, sorted=%d, count=%d, iItem=%d\n", nItem, is_sorted, infoPtr->nItemCount, lpLVItem->iItem); + TRACE("inserting at %d, sorted=%d, count=%d, iItem=%d\n", nItem, is_sorted, infoPtr->nItemCount, lpLVItem->iItem); nItem = DPA_InsertPtr( infoPtr->hdpaItems, nItem, hdpaSubItems ); if (nItem == -1) goto fail; infoPtr->nItemCount++;
/* shift indices first so they don't get tangled */ LISTVIEW_ShiftIndices(infoPtr, nItem, 1); - LISTVIEW_ShiftFocus(infoPtr, infoPtr->nFocusedItem, nItem, 1);
/* set the item attributes */ if (lpLVItem->mask & (LVIF_GROUPID|LVIF_COLUMNS)) @@ -7763,6 +7778,7 @@ static INT LISTVIEW_InsertItemT(LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem, item.state &= ~LVIS_STATEIMAGEMASK; item.state |= INDEXTOSTATEIMAGEMASK(1); } + if (!set_main_item(infoPtr, &item, TRUE, isW, &has_changed)) goto undo;
/* make room for the position, if we are in the right mode */ @@ -7776,9 +7792,9 @@ static INT LISTVIEW_InsertItemT(LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem, goto undo; } } - + /* send LVN_INSERTITEM notification */ - ZeroMemory(&nmlv, sizeof(NMLISTVIEW)); + memset(&nmlv, 0, sizeof(NMLISTVIEW)); nmlv.iItem = nItem; nmlv.lParam = lpItem->lParam; notify_listview(infoPtr, LVN_INSERTITEM, &nmlv); diff --git a/dlls/comctl32/tests/listview.c b/dlls/comctl32/tests/listview.c index cff6e92..2fb7cb5 100644 --- a/dlls/comctl32/tests/listview.c +++ b/dlls/comctl32/tests/listview.c @@ -363,6 +363,15 @@ static const struct message listview_header_set_imagelist[] = { { 0 } };
+static const struct message parent_insert_focused_seq[] = { + { WM_NOTIFY, sent|id, 0, 0, LVN_ITEMCHANGING }, + { WM_NOTIFY, sent|id, 0, 0, LVN_ITEMCHANGING }, + { WM_NOTIFY, sent|id, 0, 0, LVN_ITEMCHANGED }, + { WM_NOTIFY, sent|id, 0, 0, LVN_ITEMCHANGED }, + { WM_NOTIFY, sent|id, 0, 0, LVN_INSERTITEM }, + { 0 } +}; + static LRESULT WINAPI parent_wnd_proc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { static LONG defwndproc_counter = 0; @@ -5434,6 +5443,58 @@ static void test_deleteitem(void) DestroyWindow(hwnd); }
+static void test_insertitem(void) +{ + LVITEMA item; + UINT state; + HWND hwnd; + INT ret; + + hwnd = create_listview_control(LVS_REPORT); + + /* insert item 0 focused */ + item.mask = LVIF_STATE; + item.state = LVIS_FOCUSED; + item.stateMask = LVIS_FOCUSED; + item.iItem = 0; + item.iSubItem = 0; + ret = SendMessageA(hwnd, LVM_INSERTITEMA, 0, (LPARAM)&item); + ok(ret == 0, "got %d\n", ret); + + state = SendMessageA(hwnd, LVM_GETITEMSTATE, 0, LVIS_FOCUSED); + ok(state == LVIS_FOCUSED, "got %x\n", state); + + flush_sequences(sequences, NUM_MSG_SEQUENCES); + + /* insert item 1, focus shift */ + item.mask = LVIF_STATE; + item.state = LVIS_FOCUSED; + item.stateMask = LVIS_FOCUSED; + item.iItem = 1; + item.iSubItem = 0; + ret = SendMessageA(hwnd, LVM_INSERTITEMA, 0, (LPARAM)&item); + ok(ret == 1, "got %d\n", ret); + + ok_sequence(sequences, PARENT_SEQ_INDEX, parent_insert_focused_seq, "insert focused", TRUE); + + state = SendMessageA(hwnd, LVM_GETITEMSTATE, 1, LVIS_FOCUSED); + ok(state == LVIS_FOCUSED, "got %x\n", state); + + /* insert item 2, no focus shift */ + item.mask = LVIF_STATE; + item.state = 0; + item.stateMask = LVIS_FOCUSED; + item.iItem = 2; + item.iSubItem = 0; + ret = SendMessageA(hwnd, LVM_INSERTITEMA, 0, (LPARAM)&item); + ok(ret == 2, "got %d\n", ret); + + state = SendMessageA(hwnd, LVM_GETITEMSTATE, 1, LVIS_FOCUSED); + ok(state == LVIS_FOCUSED, "got %x\n", state); + + DestroyWindow(hwnd); +} + START_TEST(listview) { HMODULE hComctl32; @@ -5502,6 +5563,7 @@ START_TEST(listview) test_LVM_SETITEMTEXT(); test_imagelists(); test_deleteitem(); + test_insertitem();
if (!load_v6_module(&ctx_cookie, &hCtx)) { @@ -5533,6 +5595,7 @@ START_TEST(listview) test_LVS_EX_HEADERINALLVIEWS(); test_deleteitem(); test_multiselect(); + test_insertitem();
unload_v6_module(ctx_cookie, hCtx);