From: Piotr Pawłowski <p@perkele.cc> --- dlls/comctl32/tests/treeview.c | 251 +++++++++++++++++++++++++++++++-- 1 file changed, 238 insertions(+), 13 deletions(-) diff --git a/dlls/comctl32/tests/treeview.c b/dlls/comctl32/tests/treeview.c index 997efa01a9d..6470e4b824e 100644 --- a/dlls/comctl32/tests/treeview.c +++ b/dlls/comctl32/tests/treeview.c @@ -58,6 +58,7 @@ static HFONT g_customdraw_font; static BOOL g_v6; static int g_reject_tvn_itemexpanding = 0; static int g_click_delete_test = 0; +static BOOL g_skip_paint_messages = FALSE; #define NUM_MSG_SEQUENCES 3 #define TREEVIEW_SEQ_INDEX 0 @@ -104,6 +105,98 @@ static const struct message rootnone_select_seq[] = { { 0 } }; +/* + * TVN_ITEMCHANGINGW and TVN_ITEMCHANGEDW intended! + * Verified against Windows versions from 7 up. + */ +static const struct message parent_rootnone_select_seq_v6[] = { + { WM_NOTIFY, sent|id, 0, 0, TVN_SELCHANGINGA }, + { WM_NOTIFY, sent|id, 0, 0, TVN_ITEMCHANGINGW }, + { WM_NOTIFY, sent|id, 0, 0, TVN_ITEMCHANGEDW }, + { WM_NOTIFY, sent|id, 0, 0, TVN_SELCHANGEDA }, + { WM_NOTIFY, sent|id, 0, 0, TVN_SELCHANGINGA }, + { WM_NOTIFY, sent|id, 0, 0, TVN_ITEMCHANGINGW }, + { WM_NOTIFY, sent|id, 0, 0, TVN_ITEMCHANGEDW }, + { WM_NOTIFY, sent|id, 0, 0, TVN_SELCHANGEDA }, + { WM_NOTIFY, sent|id, 0, 0, TVN_SELCHANGINGA }, + { WM_NOTIFY, sent|id, 0, 0, TVN_ITEMCHANGINGW }, + { WM_NOTIFY, sent|id, 0, 0, TVN_ITEMCHANGEDW }, + { WM_NOTIFY, sent|id, 0, 0, TVN_SELCHANGEDA }, + { 0 } +}; + +static const struct message parent_rootnone_select_seq[] = { + { WM_NOTIFY, sent|id, 0, 0, TVN_SELCHANGINGA }, + { WM_NOTIFY, sent|id, 0, 0, TVN_SELCHANGEDA }, + { WM_NOTIFY, sent|id, 0, 0, TVN_SELCHANGINGA }, + { WM_NOTIFY, sent|id, 0, 0, TVN_SELCHANGEDA }, + { WM_NOTIFY, sent|id, 0, 0, TVN_SELCHANGINGA }, + { WM_NOTIFY, sent|id, 0, 0, TVN_SELCHANGEDA }, + { 0 } +}; + +static const struct message parent_rootchild_select_seq_v6[] = { + { WM_NOTIFY, sent|id, 0, 0, TVN_SELCHANGINGA }, + { WM_NOTIFY, sent|id, 0, 0, TVN_ITEMCHANGINGW }, + { WM_NOTIFY, sent|id, 0, 0, TVN_ITEMCHANGEDW }, + { WM_NOTIFY, sent|id, 0, 0, TVN_SELCHANGEDA }, + { WM_NOTIFY, sent|id, 0, 0, TVN_SELCHANGINGA }, + { WM_NOTIFY, sent|id, 0, 0, TVN_ITEMCHANGINGW }, + { WM_NOTIFY, sent|id, 0, 0, TVN_ITEMCHANGEDW }, + { WM_NOTIFY, sent|id, 0, 0, TVN_SELCHANGEDA }, + { WM_NOTIFY, sent|id, 0, 0, TVN_SELCHANGINGA }, + { WM_NOTIFY, sent|id, 0, 0, TVN_ITEMCHANGINGW }, + { WM_NOTIFY, sent|id, 0, 0, TVN_ITEMCHANGEDW }, + { WM_NOTIFY, sent|id, 0, 0, TVN_ITEMCHANGINGW }, + { WM_NOTIFY, sent|id, 0, 0, TVN_ITEMCHANGEDW }, + { WM_NOTIFY, sent|id, 0, 0, TVN_ITEMEXPANDINGA }, + { WM_NOTIFY, sent|id, 0, 0, TVN_ITEMEXPANDEDA }, + { WM_NOTIFY, sent|id, 0, 0, TVN_SELCHANGEDA }, + { WM_NOTIFY, sent|id, 0, 0, TVN_SELCHANGINGA }, + { WM_NOTIFY, sent|id, 0, 0, TVN_ITEMCHANGINGW }, + { WM_NOTIFY, sent|id, 0, 0, TVN_ITEMCHANGEDW }, + { WM_NOTIFY, sent|id, 0, 0, TVN_ITEMCHANGINGW }, + { WM_NOTIFY, sent|id, 0, 0, TVN_ITEMCHANGEDW }, + { WM_NOTIFY, sent|id, 0, 0, TVN_SELCHANGEDA }, + { 0 } +}; + +static const struct message parent_rootchild_select_seq[] = { + { WM_NOTIFY, sent|id, 0, 0, TVN_SELCHANGINGA }, + { WM_NOTIFY, sent|id, 0, 0, TVN_SELCHANGEDA }, + { WM_NOTIFY, sent|id, 0, 0, TVN_SELCHANGINGA }, + { WM_NOTIFY, sent|id, 0, 0, TVN_SELCHANGEDA }, + { WM_NOTIFY, sent|id, 0, 0, TVN_SELCHANGINGA }, + { WM_NOTIFY, sent|id, 0, 0, TVN_ITEMEXPANDINGA }, + { WM_NOTIFY, sent|id, 0, 0, TVN_ITEMEXPANDEDA }, + { WM_NOTIFY, sent|id, 0, 0, TVN_SELCHANGEDA }, + { WM_NOTIFY, sent|id, 0, 0, TVN_SELCHANGINGA }, + { WM_NOTIFY, sent|id, 0, 0, TVN_SELCHANGEDA }, + { 0 } +}; + +static const struct message select_previous_item_v6[] = { + { WM_NOTIFY, sent|id, 0, 0, TVN_SELCHANGINGA }, + { WM_NOTIFY, sent|id, 0, 0, TVN_ITEMCHANGINGW }, + { WM_NOTIFY, sent|id, 0, 0, TVN_ITEMCHANGEDW }, + { WM_NOTIFY, sent|id, 0, 0, TVN_SELCHANGEDA }, + { WM_NOTIFY, sent|id, 0, 0, TVN_SELCHANGINGA }, + { WM_NOTIFY, sent|id, 0, 0, TVN_ITEMCHANGINGW }, + { WM_NOTIFY, sent|id, 0, 0, TVN_ITEMCHANGEDW }, + { WM_NOTIFY, sent|id, 0, 0, TVN_ITEMCHANGINGW }, + { WM_NOTIFY, sent|id, 0, 0, TVN_ITEMCHANGEDW }, + { WM_NOTIFY, sent|id, 0, 0, TVN_SELCHANGEDA }, + { 0 }, +}; + +static const struct message select_previous_item[] = { + { WM_NOTIFY, sent|id, 0, 0, TVN_SELCHANGINGA }, + { WM_NOTIFY, sent|id, 0, 0, TVN_SELCHANGEDA }, + { WM_NOTIFY, sent|id, 0, 0, TVN_SELCHANGINGA }, + { WM_NOTIFY, sent|id, 0, 0, TVN_SELCHANGEDA }, + { 0 }, +}; + static const struct message rootchild_select_seq[] = { { TVM_SELECTITEM, sent|wparam, 9 }, { TVM_SELECTITEM, sent|wparam, 9 }, @@ -187,6 +280,16 @@ static const struct message test_get_set_item_seq[] = { { TVM_SETITEMA, sent }, { TVM_GETITEMA, sent }, { TVM_SETITEMA, sent }, + { TVM_SETITEMA, sent }, + { TVM_SETITEMA, sent }, + { 0 } +}; + +static const struct message test_get_set_item_seq_parent_v6[] = { + { WM_NOTIFY, sent|id, 0, 0, TVN_ITEMCHANGINGW }, + { WM_NOTIFY, sent|id, 0, 0, TVN_ITEMCHANGEDW }, + { WM_NOTIFY, sent|id, 0, 0, TVN_ITEMCHANGINGW }, + { WM_NOTIFY, sent|id, 0, 0, TVN_ITEMCHANGEDW }, { 0 } }; @@ -425,7 +528,8 @@ static const struct message parent_right_click_seq[] = { static HWND hMainWnd; -static HTREEITEM hRoot, hChild; +static HTREEITEM hRoot, hChild, hBlockChange; +static BOOL bSelectPreviousItem = FALSE; static int pos = 0; static char sequence[256]; @@ -459,20 +563,29 @@ static void IdentifyItem(HTREEITEM hItem) AddItem('?'); } +static BOOL IsPaintMessage(UINT message) +{ + return message == WM_PAINT || message == WM_NCPAINT || message == WM_ERASEBKGND; +} + /* This function hooks in and records all messages to the treeview control */ static LRESULT WINAPI TreeviewWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { static LONG defwndproc_counter = 0; LRESULT ret; WNDPROC lpOldProc = (WNDPROC)GetWindowLongPtrA(hwnd, GWLP_USERDATA); - struct message msg = { 0 }; - msg.message = message; - msg.flags = sent|wparam|lparam; - if (defwndproc_counter) msg.flags |= defwinproc; - msg.wParam = wParam; - msg.lParam = lParam; - add_message(sequences, TREEVIEW_SEQ_INDEX, &msg); + if (!(g_skip_paint_messages && IsPaintMessage(message))) + { + struct message msg = { 0 }; + + msg.message = message; + msg.flags = sent|wparam|lparam; + if (defwndproc_counter) msg.flags |= defwinproc; + msg.wParam = wParam; + msg.lParam = lParam; + add_message(sequences, TREEVIEW_SEQ_INDEX, &msg); + } defwndproc_counter++; ret = CallWindowProcA(lpOldProc, hwnd, message, wParam, lParam); @@ -688,6 +801,9 @@ static void test_select(void) BOOL r; HWND hTree; + /* suppress logging of painting related messages in this test */ + g_skip_paint_messages = TRUE; + hTree = create_treeview_control(0); fill_tree(hTree); @@ -713,8 +829,11 @@ static void test_select(void) expect(TRUE, r); AddItem('.'); ok(!strcmp(sequence, "1(nR)nR23(Rn)Rn45(nR)nR."), "root-none select test\n"); - ok_sequence(sequences, TREEVIEW_SEQ_INDEX, rootnone_select_seq, - "root-none select seq", FALSE); + ok_sequence(sequences, TREEVIEW_SEQ_INDEX, rootnone_select_seq, "root-none select seq", FALSE); + if (g_v6) + ok_sequence(sequences, PARENT_SEQ_INDEX, parent_rootnone_select_seq_v6, "root-none parent select seq v6", TRUE); + else + ok_sequence(sequences, PARENT_SEQ_INDEX, parent_rootnone_select_seq, "root-none parent select seq", FALSE); /* root-child select tests */ flush_sequences(sequences, NUM_MSG_SEQUENCES); @@ -739,10 +858,55 @@ static void test_select(void) expect(TRUE, r); AddItem('.'); ok(!strcmp(sequence, "1(nR)nR23(RC)RC45(CR)CR."), "root-child select test\n"); - ok_sequence(sequences, TREEVIEW_SEQ_INDEX, rootchild_select_seq, - "root-child select seq", FALSE); + ok_sequence(sequences, TREEVIEW_SEQ_INDEX, rootchild_select_seq, "root-child select seq", FALSE); + + if (g_v6) + ok_sequence(sequences, PARENT_SEQ_INDEX, parent_rootchild_select_seq_v6, "root-child parent select seq v6", TRUE); + else + ok_sequence(sequences, PARENT_SEQ_INDEX, parent_rootchild_select_seq, "root-child parent select seq", FALSE); + + /* Test if we can prevent item being selected; TVN_ITEMCHANGING logic only in v6 */ + r = SendMessageA(hTree, TVM_SELECTITEM, TVGN_CARET, (LPARAM)hRoot); + expect(TRUE, r); + r = SendMessageA(hTree, TVM_GETITEMSTATE, (WPARAM)hRoot, TVIS_SELECTED) & TVIS_SELECTED; + expect(TVIS_SELECTED, r); + hBlockChange = hChild; + r = SendMessageA(hTree, TVM_SELECTITEM, TVGN_CARET, (LPARAM)hChild); + expect(TRUE, r); + r = SendMessageA(hTree, TVM_GETITEMSTATE, (WPARAM)hChild, TVIS_SELECTED) & TVIS_SELECTED; + if (g_v6) + todo_wine expect(0, r); + else + expect(TVIS_SELECTED, r); + + r = SendMessageA(hTree, TVM_GETITEMSTATE, (WPARAM)hRoot, TVIS_SELECTED) & TVIS_SELECTED; + expect(0, r); + hBlockChange = NULL; + r = SendMessageA(hTree, TVM_SELECTITEM, TVGN_CARET, (LPARAM)hChild); + expect(TRUE, r); + r = SendMessageA(hTree, TVM_GETITEMSTATE, (WPARAM)hChild, TVIS_SELECTED) & TVIS_SELECTED; + if (g_v6) + todo_wine expect(0, r); + else + expect(TVIS_SELECTED, r); + r = SendMessageA(hTree, TVM_GETITEMSTATE, (WPARAM)hRoot, TVIS_SELECTED) & TVIS_SELECTED; + expect(0, r); + + /* Test altering selection from TVN_ITEMCHANGING */ + flush_sequences(sequences, NUM_MSG_SEQUENCES); + SendMessageA(hTree, TVM_SELECTITEM, TVGN_CARET, (WPARAM)hRoot); + bSelectPreviousItem = TRUE; + SendMessageA(hTree, TVM_SELECTITEM, TVGN_CARET, (WPARAM)hChild); + bSelectPreviousItem = FALSE; + + if (g_v6) + ok_sequence(sequences, PARENT_SEQ_INDEX, select_previous_item_v6, "select previous item seq", TRUE); + else + ok_sequence(sequences, PARENT_SEQ_INDEX, select_previous_item, "select previous item seq", FALSE); DestroyWindow(hTree); + + g_skip_paint_messages = FALSE; } static void test_getitemtext(void) @@ -936,6 +1100,8 @@ static void test_get_set_item(void) HWND hTree, hTree2; DWORD ret; + g_skip_paint_messages = TRUE; + hTree = create_treeview_control(0); fill_tree(hTree); @@ -975,8 +1141,31 @@ static void test_get_set_item(void) ret = SendMessageA(hTree, TVM_SETITEMA, 0, (LPARAM)&tviRoot); expect(TRUE, ret); + tviRoot.mask = TVIF_STATE; + tviRoot.stateMask = TVIS_SELECTED; + tviRoot.state = TVIS_SELECTED; + tviRoot.cchTextMax = 0; + tviRoot.pszText = NULL; + ret = SendMessageA(hTree, TVM_SETITEMA, 0, (LPARAM)&tviRoot); + expect(TRUE, ret); + tviRoot.state = 0; + ret = SendMessageA(hTree, TVM_SETITEMA, 0, (LPARAM)&tviRoot); + expect(TRUE, ret); + + ok_sequence(sequences, TREEVIEW_SEQ_INDEX, test_get_set_item_seq, "test get set item", FALSE); + if (g_v6) + { + ok_sequence(sequences, PARENT_SEQ_INDEX, test_get_set_item_seq_parent_v6, + "test get set item notifications v6", TRUE); + } + else + { + ok_sequence(sequences, PARENT_SEQ_INDEX, empty_seq, + "test get set item notifications", FALSE); + } + /* get item from a different tree */ hTree2 = create_treeview_control(0); @@ -997,6 +1186,8 @@ static void test_get_set_item(void) DestroyWindow(hTree); DestroyWindow(hTree2); + + g_skip_paint_messages = FALSE; } static void test_get_set_itemheight(void) @@ -1271,6 +1462,25 @@ static void test_get_set_unicodeformat(void) DestroyWindow(hTree); } +static BOOL IsParentPaintMessage(UINT message, WPARAM wParam, LPARAM lParam) +{ + if (message >= WM_CTLCOLORMSGBOX && message <= WM_CTLCOLORSTATIC) return TRUE; + else if (message == WM_NOTIFY) + { + return ((LPNMHDR)lParam)->code == NM_CUSTOMDRAW; + } + else return FALSE; +} + +static void HandleSelectPreviousItem(NMTREEVIEWA *nmtv) +{ + TVITEMA item = {0}; + item.mask = TVIF_STATE; + item.hItem = nmtv->itemOld.hItem; + item.stateMask = TVIS_SELECTED; + SendMessageA(nmtv->hdr.hwndFrom, TVM_SETITEMA, 0, (LPARAM)&item); +} + static LRESULT CALLBACK parent_wnd_proc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { static LONG defwndproc_counter = 0; @@ -1295,7 +1505,9 @@ static LRESULT CALLBACK parent_wnd_proc(HWND hWnd, UINT message, WPARAM wParam, message != WM_NCHITTEST && message != WM_GETTEXT && message != WM_GETICON && - message != WM_DEVICECHANGE) + message != WM_DEVICECHANGE && + !(g_skip_paint_messages && IsParentPaintMessage(message, wParam, lParam)) + ) { add_message(sequences, PARENT_SEQ_INDEX, &msg); } @@ -1318,7 +1530,19 @@ static LRESULT CALLBACK parent_wnd_proc(HWND hWnd, UINT message, WPARAM wParam, NMTREEVIEWA *pTreeView = (LPNMTREEVIEWA) lParam; switch(pHdr->code) { + /* + * TVN_ITEMCHANGINGW (not TVN_ITEMCHANGINGA) intended. + * MS implementation appears to send TVN_ITEMCHANGINGW only. + * Available only in comctl32 v6. + */ + case TVN_ITEMCHANGINGW: + { + NMTVITEMCHANGE * pChange = (NMTVITEMCHANGE*) lParam; + if (pChange->hItem == hBlockChange) return TRUE; + } + break; case TVN_SELCHANGINGA: + if (bSelectPreviousItem) HandleSelectPreviousItem(pTreeView); AddItem('('); IdentifyItem(pTreeView->itemOld.hItem); IdentifyItem(pTreeView->itemNew.hItem); @@ -3422,6 +3646,7 @@ START_TEST(treeview) g_v6 = TRUE; test_fillroot(); + test_select(); test_getitemtext(); test_get_set_insertmark(); test_get_set_item(); -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10451