1 /* Unit tests for treeview.
3 * Copyright 2005 Krzysztof Foltman
4 * Copyright 2007 Christopher James Peterson
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
32 #include "wine/test.h"
35 const char *TEST_CALLBACK_TEXT = "callback_text";
37 #define NUM_MSG_SEQUENCES 2
38 #define TREEVIEW_SEQ_INDEX 0
39 #define PARENT_SEQ_INDEX 1
41 #define expect(expected, got) ok(got == expected, "Expected %d, got %d\n", expected, got)
43 static struct msg_sequence *sequences[NUM_MSG_SEQUENCES];
45 static const struct message FillRootSeq[] = {
46 { TVM_INSERTITEM, sent },
47 { TVM_INSERTITEM, sent },
51 static const struct message rootnone_select_seq[] = {
52 { TVM_SELECTITEM, sent|wparam, 9 },
53 { TVM_SELECTITEM, sent|wparam, 9 },
54 { TVM_SELECTITEM, sent|wparam, 9 },
55 { TVM_SELECTITEM, sent|wparam, 9 },
56 { TVM_SELECTITEM, sent|wparam, 9 },
57 { TVM_SELECTITEM, sent|wparam, 9 },
61 static const struct message rootchild_select_seq[] = {
62 { TVM_SELECTITEM, sent|wparam, 9 },
63 { TVM_SELECTITEM, sent|wparam, 9 },
64 { TVM_SELECTITEM, sent|wparam, 9 },
65 { TVM_SELECTITEM, sent|wparam, 9 },
66 { TVM_SELECTITEM, sent|wparam, 9 },
67 { TVM_SELECTITEM, sent|wparam, 9 },
71 static const struct message getitemtext_seq[] = {
72 { TVM_INSERTITEM, sent },
73 { TVM_GETITEM, sent },
74 { TVM_DELETEITEM, sent },
78 static const struct message focus_seq[] = {
79 { TVM_INSERTITEM, sent },
80 { TVM_INSERTITEM, sent },
81 { TVM_SELECTITEM, sent|wparam, 9 },
82 /* The following end up out of order in wine */
83 { WM_WINDOWPOSCHANGING, sent|defwinproc },
84 { WM_NCCALCSIZE, sent|wparam|defwinproc, TRUE },
85 { WM_WINDOWPOSCHANGED, sent|defwinproc },
86 { WM_SIZE, sent|defwinproc },
87 { WM_PAINT, sent|defwinproc },
88 { WM_NCPAINT, sent|wparam|defwinproc, 1 },
89 { WM_ERASEBKGND, sent|defwinproc },
90 { TVM_EDITLABEL, sent },
91 { WM_COMMAND, sent|wparam|defwinproc, MAKEWPARAM(0, EN_UPDATE) },
92 { WM_COMMAND, sent|wparam|defwinproc, MAKEWPARAM(0, EN_CHANGE) },
93 { WM_PARENTNOTIFY, sent|wparam|defwinproc, MAKEWPARAM(WM_CREATE, 0) },
94 { WM_KILLFOCUS, sent|defwinproc },
95 { WM_PAINT, sent|defwinproc },
96 { WM_IME_SETCONTEXT, sent|defwinproc|optional },
97 { WM_COMMAND, sent|wparam|defwinproc, MAKEWPARAM(0, EN_SETFOCUS) },
98 { WM_ERASEBKGND, sent|defwinproc|optional },
99 { WM_CTLCOLOREDIT, sent|defwinproc|optional },
100 { WM_CTLCOLOREDIT, sent|defwinproc|optional },
104 static const struct message test_get_set_bkcolor_seq[] = {
105 { TVM_GETBKCOLOR, sent|wparam|lparam, 0, 0 },
106 { TVM_SETBKCOLOR, sent|wparam|lparam, 0, 0 },
107 { TVM_GETBKCOLOR, sent|wparam|lparam, 0, 0 },
108 { TVM_SETBKCOLOR, sent|wparam|lparam, 0, 0x00ffffff },
109 { TVM_GETBKCOLOR, sent|wparam|lparam, 0, 0 },
110 { TVM_SETBKCOLOR, sent|wparam|lparam, 0, -1 },
114 static const struct message test_get_set_imagelist_seq[] = {
115 { TVM_SETIMAGELIST, sent|wparam|lparam, 0, 0 },
116 { TVM_GETIMAGELIST, sent|wparam|lparam, 0, 0 },
120 static const struct message test_get_set_indent_seq[] = {
121 { TVM_SETINDENT, sent|wparam|lparam, 0, 0 },
122 { TVM_GETINDENT, sent|wparam|lparam, 0, 0 },
123 /* The actual amount to indent is dependent on the system for this message */
124 { TVM_SETINDENT, sent },
125 { TVM_GETINDENT, sent|wparam|lparam, 0, 0 },
129 static const struct message test_get_set_insertmarkcolor_seq[] = {
130 { TVM_SETINSERTMARKCOLOR, sent|wparam|lparam, 0, 0 },
131 { TVM_GETINSERTMARKCOLOR, sent|wparam|lparam, 0, 0 },
135 static const struct message test_get_set_item_seq[] = {
136 { TVM_GETITEM, sent },
137 { TVM_SETITEM, sent },
138 { TVM_GETITEM, sent },
139 { TVM_SETITEM, sent },
143 static const struct message test_get_set_itemheight_seq[] = {
144 { TVM_GETITEMHEIGHT, sent|wparam|lparam, 0, 0 },
145 { TVM_SETITEMHEIGHT, sent|wparam|lparam, -1, 0 },
146 { TVM_GETITEMHEIGHT, sent|wparam|lparam, 0, 0 },
147 { TVM_SETITEMHEIGHT, sent|lparam, 0xcccccccc, 0 },
148 { TVM_GETITEMHEIGHT, sent|wparam|lparam|optional, 0, 0 },
149 { TVM_SETITEMHEIGHT, sent|wparam|lparam|optional, 9, 0 },
150 { TVM_GETITEMHEIGHT, sent|wparam|lparam, 0, 0 },
154 static const struct message test_get_set_scrolltime_seq[] = {
155 { TVM_SETSCROLLTIME, sent|wparam|lparam, 20, 0 },
156 { TVM_GETSCROLLTIME, sent|wparam|lparam, 0, 0 },
160 static const struct message test_get_set_textcolor_seq[] = {
161 { TVM_GETTEXTCOLOR, sent|wparam|lparam, 0, 0 },
162 { TVM_SETTEXTCOLOR, sent|wparam|lparam, 0, 0 },
163 { TVM_GETTEXTCOLOR, sent|wparam|lparam, 0, 0 },
164 { TVM_SETTEXTCOLOR, sent|wparam|lparam, 0, RGB(255, 255, 255) },
165 { TVM_GETTEXTCOLOR, sent|wparam|lparam, 0, 0 },
166 { TVM_SETTEXTCOLOR, sent|wparam|lparam, 0, CLR_NONE },
170 static const struct message test_get_set_tooltips_seq[] = {
171 { WM_KILLFOCUS, sent },
172 { WM_IME_SETCONTEXT, sent|optional },
173 { WM_IME_NOTIFY, sent|optional },
174 { TVM_SETTOOLTIPS, sent|wparam|lparam, 0, 0 },
175 { TVM_GETTOOLTIPS, sent|wparam|lparam, 0, 0 },
179 static const struct message test_get_set_unicodeformat_seq[] = {
180 { TVM_SETUNICODEFORMAT, sent|wparam|lparam, TRUE, 0 },
181 { TVM_GETUNICODEFORMAT, sent|wparam|lparam, 0, 0 },
182 { TVM_SETUNICODEFORMAT, sent|wparam|lparam, 0, 0 },
183 { TVM_GETUNICODEFORMAT, sent|wparam|lparam, 0, 0 },
184 { TVM_SETUNICODEFORMAT, sent|wparam|lparam, 0, 0 },
188 static const struct message parent_expand_seq[] = {
189 { WM_NOTIFY, sent|id, 0, 0, TVN_ITEMEXPANDING },
190 { WM_NOTIFY, sent|id, 0, 0, TVN_ITEMEXPANDED },
194 static const struct message empty_seq[] = {
198 static HWND hMainWnd;
200 static HTREEITEM hRoot, hChild;
203 static char sequence[256];
205 static void Clear(void)
211 static void AddItem(char ch)
213 sequence[pos++] = ch;
214 sequence[pos] = '\0';
217 static void IdentifyItem(HTREEITEM hItem)
219 if (hItem == hRoot) {
223 if (hItem == hChild) {
234 /* This function hooks in and records all messages to the treeview control */
235 static LRESULT WINAPI TreeviewWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
237 static LONG defwndproc_counter = 0;
240 WNDPROC lpOldProc = (WNDPROC)GetWindowLongPtrA(hwnd, GWLP_USERDATA);
242 msg.message = message;
243 msg.flags = sent|wparam|lparam;
244 if (defwndproc_counter) msg.flags |= defwinproc;
247 add_message(sequences, TREEVIEW_SEQ_INDEX, &msg);
249 defwndproc_counter++;
250 ret = CallWindowProcA(lpOldProc, hwnd, message, wParam, lParam);
251 defwndproc_counter--;
256 static HWND create_treeview_control(void)
261 hTree = CreateWindowExA(WS_EX_CLIENTEDGE, WC_TREEVIEWA, NULL, WS_CHILD|WS_VISIBLE|
262 TVS_LINESATROOT|TVS_HASLINES|TVS_HASBUTTONS|TVS_EDITLABELS,
263 0, 0, 120, 100, hMainWnd, (HMENU)100, GetModuleHandleA(0), 0);
267 /* Record the old WNDPROC so we can call it after recording the messages */
268 pOldWndProc = (WNDPROC)SetWindowLongPtrA(hTree, GWLP_WNDPROC, (LONG_PTR)TreeviewWndProc);
269 SetWindowLongPtrA(hTree, GWLP_USERDATA, (LONG_PTR)pOldWndProc);
274 static void fill_tree(HWND hTree)
277 static CHAR root[] = "Root",
280 ins.hParent = TVI_ROOT;
281 ins.hInsertAfter = TVI_ROOT;
282 U(ins).item.mask = TVIF_TEXT;
283 U(ins).item.pszText = root;
284 hRoot = TreeView_InsertItem(hTree, &ins);
287 ins.hInsertAfter = TVI_FIRST;
288 U(ins).item.mask = TVIF_TEXT;
289 U(ins).item.pszText = child;
290 hChild = TreeView_InsertItem(hTree, &ins);
293 static void test_fillroot(void)
298 hTree = create_treeview_control();
300 flush_sequences(sequences, NUM_MSG_SEQUENCES);
310 ok_sequence(sequences, TREEVIEW_SEQ_INDEX, FillRootSeq, "FillRoot", FALSE);
311 ok(!strcmp(sequence, "AB."), "Item creation\n");
313 /* UMLPad 1.15 depends on this being not -1 (I_IMAGECALLBACK) */
315 tvi.mask = TVIF_IMAGE | TVIF_SELECTEDIMAGE;
316 SendMessage( hTree, TVM_GETITEM, 0, (LPARAM)&tvi );
317 ok(tvi.iImage == 0, "tvi.iImage=%d\n", tvi.iImage);
318 ok(tvi.iSelectedImage == 0, "tvi.iSelectedImage=%d\n", tvi.iSelectedImage);
320 DestroyWindow(hTree);
323 static void test_callback(void)
326 HTREEITEM hItem1, hItem2;
329 CHAR test_string[] = "Test_string";
334 hTree = create_treeview_control();
337 ret = TreeView_DeleteAllItems(hTree);
338 ok(ret == TRUE, "ret\n");
339 ins.hParent = TVI_ROOT;
340 ins.hInsertAfter = TVI_ROOT;
341 U(ins).item.mask = TVIF_TEXT;
342 U(ins).item.pszText = LPSTR_TEXTCALLBACK;
343 hRoot = TreeView_InsertItem(hTree, &ins);
347 tvi.mask = TVIF_TEXT;
349 tvi.cchTextMax = sizeof(buf)/sizeof(buf[0]);
350 ret = TreeView_GetItem(hTree, &tvi);
351 ok(ret == 1, "ret\n");
352 ok(strcmp(tvi.pszText, TEST_CALLBACK_TEXT) == 0, "Callback item text mismatch %s vs %s\n",
353 tvi.pszText, TEST_CALLBACK_TEXT);
356 ins.hInsertAfter = TVI_FIRST;
357 U(ins).item.mask = TVIF_TEXT;
358 U(ins).item.pszText = test_string;
359 hItem1 = TreeView_InsertItem(hTree, &ins);
363 ret = TreeView_GetItem(hTree, &tvi);
364 ok(ret == TRUE, "ret\n");
365 ok(strcmp(tvi.pszText, test_string) == 0, "Item text mismatch %s vs %s\n",
366 tvi.pszText, test_string);
368 /* undocumented: pszText of NULL also means LPSTR_CALLBACK: */
370 ret = TreeView_SetItem(hTree, &tvi);
371 ok(ret == 1, "Expected SetItem return 1, got %ld\n", ret);
373 ret = TreeView_GetItem(hTree, &tvi);
374 ok(ret == TRUE, "Expected GetItem return TRUE, got %ld\n", ret);
375 ok(strcmp(tvi.pszText, TEST_CALLBACK_TEXT) == 0, "Item text mismatch %s vs %s\n",
376 tvi.pszText, TEST_CALLBACK_TEXT);
378 U(ins).item.pszText = NULL;
379 hItem2 = TreeView_InsertItem(hTree, &ins);
382 memset(buf, 0, sizeof(buf));
383 ret = TreeView_GetItem(hTree, &tvi);
384 ok(ret == TRUE, "Expected GetItem return TRUE, got %ld\n", ret);
385 ok(strcmp(tvi.pszText, TEST_CALLBACK_TEXT) == 0, "Item text mismatch %s vs %s\n",
386 tvi.pszText, TEST_CALLBACK_TEXT);
388 DestroyWindow(hTree);
391 static void test_select(void)
396 hTree = create_treeview_control();
399 /* root-none select tests */
400 flush_sequences(sequences, NUM_MSG_SEQUENCES);
401 r = TreeView_SelectItem(hTree, NULL);
405 r = TreeView_SelectItem(hTree, hRoot);
408 r = TreeView_SelectItem(hTree, hRoot);
411 r = TreeView_SelectItem(hTree, NULL);
414 r = TreeView_SelectItem(hTree, NULL);
417 r = TreeView_SelectItem(hTree, hRoot);
420 ok(!strcmp(sequence, "1(nR)nR23(Rn)Rn45(nR)nR."), "root-none select test\n");
421 ok_sequence(sequences, TREEVIEW_SEQ_INDEX, rootnone_select_seq,
422 "root-none select seq", FALSE);
424 /* root-child select tests */
425 flush_sequences(sequences, NUM_MSG_SEQUENCES);
426 r = TreeView_SelectItem(hTree, NULL);
431 r = TreeView_SelectItem(hTree, hRoot);
434 r = TreeView_SelectItem(hTree, hRoot);
437 r = TreeView_SelectItem(hTree, hChild);
440 r = TreeView_SelectItem(hTree, hChild);
443 r = TreeView_SelectItem(hTree, hRoot);
446 ok(!strcmp(sequence, "1(nR)nR23(RC)RC45(CR)CR."), "root-child select test\n");
447 ok_sequence(sequences, TREEVIEW_SEQ_INDEX, rootchild_select_seq,
448 "root-child select seq", FALSE);
450 DestroyWindow(hTree);
453 static void test_getitemtext(void)
460 CHAR szBuffer[80] = "Blah";
461 int nBufferSize = sizeof(szBuffer)/sizeof(CHAR);
463 hTree = create_treeview_control();
466 flush_sequences(sequences, NUM_MSG_SEQUENCES);
468 /* add an item without TVIF_TEXT mask and pszText == NULL */
470 ins.hInsertAfter = TVI_ROOT;
471 U(ins).item.mask = 0;
472 U(ins).item.pszText = NULL;
473 U(ins).item.cchTextMax = 0;
474 hChild = TreeView_InsertItem(hTree, &ins);
477 /* retrieve it with TVIF_TEXT mask */
479 tvi.mask = TVIF_TEXT;
480 tvi.cchTextMax = nBufferSize;
481 tvi.pszText = szBuffer;
483 SendMessageA( hTree, TVM_GETITEM, 0, (LPARAM)&tvi );
484 ok(!strcmp(szBuffer, ""), "szBuffer=\"%s\", expected \"\"\n", szBuffer);
485 ok(SendMessageA(hTree, TVM_DELETEITEM, 0, (LPARAM)hChild), "DeleteItem failed\n");
486 ok_sequence(sequences, TREEVIEW_SEQ_INDEX, getitemtext_seq, "get item text seq", FALSE);
488 DestroyWindow(hTree);
491 static void test_focus(void)
494 static CHAR child1[] = "Edit",
495 child2[] = "A really long string";
496 HTREEITEM hChild1, hChild2;
500 hTree = create_treeview_control();
503 flush_sequences(sequences, NUM_MSG_SEQUENCES);
505 /* This test verifies that when a label is being edited, scrolling
506 * the treeview does not cause the label to lose focus. To test
507 * this, first some additional entries are added to generate
511 ins.hInsertAfter = hChild;
512 U(ins).item.mask = TVIF_TEXT;
513 U(ins).item.pszText = child1;
514 hChild1 = TreeView_InsertItem(hTree, &ins);
516 ins.hInsertAfter = hChild1;
517 U(ins).item.mask = TVIF_TEXT;
518 U(ins).item.pszText = child2;
519 hChild2 = TreeView_InsertItem(hTree, &ins);
522 ShowWindow(hMainWnd,SW_SHOW);
523 SendMessageA(hTree, TVM_SELECTITEM, TVGN_CARET, (LPARAM)hChild);
524 hEdit = TreeView_EditLabel(hTree, hChild);
525 ScrollWindowEx(hTree, -10, 0, NULL, NULL, NULL, NULL, SW_SCROLLCHILDREN);
526 ok(GetFocus() == hEdit, "Edit control should have focus\n");
527 ok_sequence(sequences, TREEVIEW_SEQ_INDEX, focus_seq, "focus test", TRUE);
529 DestroyWindow(hTree);
532 static void test_get_set_bkcolor(void)
534 COLORREF crColor = RGB(0,0,0);
537 hTree = create_treeview_control();
540 flush_sequences(sequences, NUM_MSG_SEQUENCES);
542 /* If the value is -1, the control is using the system color for the background color. */
543 crColor = (COLORREF)SendMessage( hTree, TVM_GETBKCOLOR, 0, 0 );
544 ok(crColor == -1, "Default background color reported as 0x%.8x\n", crColor);
546 /* Test for black background */
547 SendMessage( hTree, TVM_SETBKCOLOR, 0, RGB(0,0,0) );
548 crColor = (COLORREF)SendMessage( hTree, TVM_GETBKCOLOR, 0, 0 );
549 ok(crColor == RGB(0,0,0), "Black background color reported as 0x%.8x\n", crColor);
551 /* Test for white background */
552 SendMessage( hTree, TVM_SETBKCOLOR, 0, RGB(255,255,255) );
553 crColor = (COLORREF)SendMessage( hTree, TVM_GETBKCOLOR, 0, 0 );
554 ok(crColor == RGB(255,255,255), "White background color reported as 0x%.8x\n", crColor);
556 /* Reset the default background */
557 SendMessage( hTree, TVM_SETBKCOLOR, 0, -1 );
559 ok_sequence(sequences, TREEVIEW_SEQ_INDEX, test_get_set_bkcolor_seq,
560 "test get set bkcolor", FALSE);
562 DestroyWindow(hTree);
565 static void test_get_set_imagelist(void)
567 HIMAGELIST hImageList = NULL;
570 hTree = create_treeview_control();
573 flush_sequences(sequences, NUM_MSG_SEQUENCES);
575 /* Test a NULL HIMAGELIST */
576 SendMessage( hTree, TVM_SETIMAGELIST, TVSIL_NORMAL, (LPARAM)hImageList );
577 hImageList = (HIMAGELIST)SendMessage( hTree, TVM_GETIMAGELIST, TVSIL_NORMAL, 0 );
578 ok(hImageList == NULL, "NULL image list, reported as 0x%p, expected 0.\n", hImageList);
580 /* TODO: Test an actual image list */
582 ok_sequence(sequences, TREEVIEW_SEQ_INDEX, test_get_set_imagelist_seq,
583 "test get imagelist", FALSE);
585 DestroyWindow(hTree);
588 static void test_get_set_indent(void)
591 int ulMinIndent = -1;
592 int ulMoreThanTwiceMin = -1;
595 hTree = create_treeview_control();
598 flush_sequences(sequences, NUM_MSG_SEQUENCES);
600 /* Finding the minimum indent */
601 SendMessage( hTree, TVM_SETINDENT, 0, 0 );
602 ulMinIndent = (int)SendMessage( hTree, TVM_GETINDENT, 0, 0 );
604 /* Checking an indent that is more than twice the default indent */
605 ulMoreThanTwiceMin = 2*ulMinIndent+1;
606 SendMessage( hTree, TVM_SETINDENT, ulMoreThanTwiceMin, 0 );
607 ulIndent = (DWORD)SendMessage( hTree, TVM_GETINDENT, 0, 0 );
608 ok(ulIndent == ulMoreThanTwiceMin, "Indent reported as %d, expected %d\n", ulIndent, ulMoreThanTwiceMin);
610 ok_sequence(sequences, TREEVIEW_SEQ_INDEX, test_get_set_indent_seq,
611 "test get set indent", FALSE);
613 DestroyWindow(hTree);
616 static void test_get_set_insertmark(void)
618 COLORREF crColor = RGB(0,0,0);
621 hTree = create_treeview_control();
624 flush_sequences(sequences, NUM_MSG_SEQUENCES);
626 SendMessage( hTree, TVM_SETINSERTMARKCOLOR, 0, crColor );
627 crColor = (COLORREF)SendMessage( hTree, TVM_GETINSERTMARKCOLOR, 0, 0 );
628 ok(crColor == RGB(0,0,0), "Insert mark color reported as 0x%.8x, expected 0x00000000\n", crColor);
630 ok_sequence(sequences, TREEVIEW_SEQ_INDEX, test_get_set_insertmarkcolor_seq,
631 "test get set insertmark color", FALSE);
633 DestroyWindow(hTree);
636 static void test_get_set_item(void)
638 TVITEM tviRoot = {0};
639 int nBufferSize = 80;
640 char szBuffer[80] = {0};
643 hTree = create_treeview_control();
646 flush_sequences(sequences, NUM_MSG_SEQUENCES);
648 /* Test the root item */
649 tviRoot.hItem = hRoot;
650 tviRoot.mask = TVIF_TEXT;
651 tviRoot.cchTextMax = nBufferSize;
652 tviRoot.pszText = szBuffer;
653 SendMessage( hTree, TVM_GETITEM, 0, (LPARAM)&tviRoot );
654 ok(!strcmp("Root", szBuffer), "GetItem: szBuffer=\"%s\", expected \"Root\"\n", szBuffer);
656 /* Change the root text */
657 strncpy(szBuffer, "Testing123", nBufferSize);
658 SendMessage( hTree, TVM_SETITEM, 0, (LPARAM)&tviRoot );
659 memset(szBuffer, 0, nBufferSize);
660 SendMessage( hTree, TVM_GETITEM, 0, (LPARAM)&tviRoot );
661 ok(!strcmp("Testing123", szBuffer), "GetItem: szBuffer=\"%s\", expected \"Testing123\"\n", szBuffer);
663 /* Reset the root text */
664 memset(szBuffer, 0, nBufferSize);
665 strncpy(szBuffer, "Root", nBufferSize);
666 SendMessage( hTree, TVM_SETITEM, 0, (LPARAM)&tviRoot );
668 ok_sequence(sequences, TREEVIEW_SEQ_INDEX, test_get_set_item_seq,
669 "test get set item", FALSE);
671 DestroyWindow(hTree);
674 static void test_get_set_itemheight(void)
680 hTree = create_treeview_control();
683 flush_sequences(sequences, NUM_MSG_SEQUENCES);
685 /* Assuming default height to begin with */
686 ulOldHeight = (int) SendMessage( hTree, TVM_GETITEMHEIGHT, 0, 0 );
688 /* Explicitly setting and getting the default height */
689 SendMessage( hTree, TVM_SETITEMHEIGHT, -1, 0 );
690 ulNewHeight = (int) SendMessage( hTree, TVM_GETITEMHEIGHT, 0, 0 );
691 ok(ulNewHeight == ulOldHeight, "Default height not set properly, reported %d, expected %d\n", ulNewHeight, ulOldHeight);
693 /* Explicitly setting and getting the height of twice the normal */
694 SendMessage( hTree, TVM_SETITEMHEIGHT, 2*ulOldHeight, 0 );
695 ulNewHeight = (int) SendMessage( hTree, TVM_GETITEMHEIGHT, 0, 0 );
696 ok(ulNewHeight == 2*ulOldHeight, "New height not set properly, reported %d, expected %d\n", ulNewHeight, 2*ulOldHeight);
698 /* Assuming tree doesn't have TVS_NONEVENHEIGHT set, so a set of 9 will round down to 8 */
699 SendMessage( hTree, TVM_SETITEMHEIGHT, 9, 0 );
700 ulNewHeight = (int) SendMessage( hTree, TVM_GETITEMHEIGHT, 0, 0 );
701 ok(ulNewHeight == 8, "Uneven height not set properly, reported %d, expected %d\n", ulNewHeight, 8);
703 ok_sequence(sequences, TREEVIEW_SEQ_INDEX, test_get_set_itemheight_seq,
704 "test get set item height", FALSE);
706 DestroyWindow(hTree);
709 static void test_get_set_scrolltime(void)
711 int ulExpectedTime = 20;
715 hTree = create_treeview_control();
718 flush_sequences(sequences, NUM_MSG_SEQUENCES);
720 SendMessage( hTree, TVM_SETSCROLLTIME, ulExpectedTime, 0 );
721 ulTime = (int)SendMessage( hTree, TVM_GETSCROLLTIME, 0, 0 );
722 ok(ulTime == ulExpectedTime, "Scroll time reported as %d, expected %d\n", ulTime, ulExpectedTime);
724 ok_sequence(sequences, TREEVIEW_SEQ_INDEX, test_get_set_scrolltime_seq,
725 "test get set scroll time", FALSE);
727 DestroyWindow(hTree);
730 static void test_get_set_textcolor(void)
732 /* If the value is -1, the control is using the system color for the text color. */
733 COLORREF crColor = RGB(0,0,0);
736 hTree = create_treeview_control();
739 flush_sequences(sequences, NUM_MSG_SEQUENCES);
741 crColor = (COLORREF)SendMessage( hTree, TVM_GETTEXTCOLOR, 0, 0 );
742 ok(crColor == -1, "Default text color reported as 0x%.8x\n", crColor);
744 /* Test for black text */
745 SendMessage( hTree, TVM_SETTEXTCOLOR, 0, RGB(0,0,0) );
746 crColor = (COLORREF)SendMessage( hTree, TVM_GETTEXTCOLOR, 0, 0 );
747 ok(crColor == RGB(0,0,0), "Black text color reported as 0x%.8x\n", crColor);
749 /* Test for white text */
750 SendMessage( hTree, TVM_SETTEXTCOLOR, 0, RGB(255,255,255) );
751 crColor = (COLORREF)SendMessage( hTree, TVM_GETTEXTCOLOR, 0, 0 );
752 ok(crColor == RGB(255,255,255), "White text color reported as 0x%.8x\n", crColor);
754 /* Reset the default text color */
755 SendMessage( hTree, TVM_SETTEXTCOLOR, 0, CLR_NONE );
757 ok_sequence(sequences, TREEVIEW_SEQ_INDEX, test_get_set_textcolor_seq,
758 "test get set text color", FALSE);
760 DestroyWindow(hTree);
763 static void test_get_set_tooltips(void)
765 HWND hwndLastToolTip = NULL;
769 hTree = create_treeview_control();
772 flush_sequences(sequences, NUM_MSG_SEQUENCES);
774 /* show even WS_POPUP treeview don't send NM_TOOLTIPSCREATED */
775 hPopupTreeView = CreateWindow(WC_TREEVIEW, NULL, WS_POPUP|WS_VISIBLE, 0, 0, 100, 100, hMainWnd, NULL, NULL, NULL);
776 DestroyWindow(hPopupTreeView);
778 /* Testing setting a NULL ToolTip */
779 SendMessage( hTree, TVM_SETTOOLTIPS, 0, 0 );
780 hwndLastToolTip = (HWND)SendMessage( hTree, TVM_GETTOOLTIPS, 0, 0 );
781 ok(hwndLastToolTip == NULL, "NULL tool tip, reported as 0x%p, expected 0.\n", hwndLastToolTip);
783 ok_sequence(sequences, TREEVIEW_SEQ_INDEX, test_get_set_tooltips_seq,
784 "test get set tooltips", TRUE);
786 /* TODO: Add a test of an actual tooltip */
787 DestroyWindow(hTree);
790 static void test_get_set_unicodeformat(void)
792 BOOL bPreviousSetting = 0;
793 BOOL bNewSetting = 0;
796 hTree = create_treeview_control();
799 flush_sequences(sequences, NUM_MSG_SEQUENCES);
802 bPreviousSetting = (BOOL)SendMessage( hTree, TVM_SETUNICODEFORMAT, 1, 0 );
803 bNewSetting = (BOOL)SendMessage( hTree, TVM_GETUNICODEFORMAT, 0, 0 );
804 ok(bNewSetting == 1, "Unicode setting did not work.\n");
807 SendMessage( hTree, TVM_SETUNICODEFORMAT, 0, 0 );
808 bNewSetting = (BOOL)SendMessage( hTree, TVM_GETUNICODEFORMAT, 0, 0 );
809 ok(bNewSetting == 0, "ANSI setting did not work.\n");
811 /* Revert to original setting */
812 SendMessage( hTree, TVM_SETUNICODEFORMAT, bPreviousSetting, 0 );
814 ok_sequence(sequences, TREEVIEW_SEQ_INDEX, test_get_set_unicodeformat_seq,
815 "test get set unicode format", FALSE);
817 DestroyWindow(hTree);
820 static TVITEMA g_item_expanding, g_item_expanded;
821 static BOOL g_get_from_expand;
822 static BOOL g_get_rect_in_expand;
824 static LRESULT CALLBACK parent_wnd_proc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
826 static LONG defwndproc_counter = 0;
830 HTREEITEM visibleItem;
832 msg.message = message;
833 msg.flags = sent|wparam|lparam;
834 if (defwndproc_counter) msg.flags |= defwinproc;
837 if (message == WM_NOTIFY && lParam) msg.id = ((NMHDR*)lParam)->code;
839 /* log system messages, except for painting */
840 if (message < WM_USER &&
841 message != WM_PAINT &&
842 message != WM_ERASEBKGND &&
843 message != WM_NCPAINT &&
844 message != WM_NCHITTEST &&
845 message != WM_GETTEXT &&
846 message != WM_GETICON &&
847 message != WM_DEVICECHANGE)
849 trace("parent: %p, %04x, %08lx, %08lx\n", hWnd, message, wParam, lParam);
850 add_message(sequences, PARENT_SEQ_INDEX, &msg);
856 NMHDR *pHdr = (NMHDR *)lParam;
858 ok(pHdr->code != NM_TOOLTIPSCREATED, "Treeview should not send NM_TOOLTIPSCREATED\n");
859 if (pHdr->idFrom == 100)
861 NMTREEVIEWA *pTreeView = (LPNMTREEVIEWA) lParam;
864 case TVN_SELCHANGINGA:
866 IdentifyItem(pTreeView->itemOld.hItem);
867 IdentifyItem(pTreeView->itemNew.hItem);
869 case TVN_SELCHANGEDA:
871 IdentifyItem(pTreeView->itemOld.hItem);
872 IdentifyItem(pTreeView->itemNew.hItem);
874 case TVN_GETDISPINFOA: {
875 NMTVDISPINFOA *disp = (NMTVDISPINFOA *)lParam;
876 if (disp->item.mask & TVIF_TEXT) {
877 lstrcpyn(disp->item.pszText, TEST_CALLBACK_TEXT, disp->item.cchTextMax);
881 case TVN_ENDLABELEDIT: return TRUE;
882 case TVN_ITEMEXPANDING:
883 ok(pTreeView->itemNew.mask ==
884 (TVIF_HANDLE | TVIF_SELECTEDIMAGE | TVIF_IMAGE | TVIF_PARAM | TVIF_STATE),
885 "got wrong mask %x\n", pTreeView->itemNew.mask);
886 ok((pTreeView->itemNew.state & TVIS_EXPANDED) == 0,
887 "got wrong state %x\n", pTreeView->itemNew.state);
888 ok(pTreeView->itemOld.mask == 0,
889 "got wrong mask %x\n", pTreeView->itemOld.mask);
891 if (g_get_from_expand)
893 g_item_expanding.mask = TVIF_STATE;
894 g_item_expanding.hItem = hRoot;
895 ret = SendMessageA(pHdr->hwndFrom, TVM_GETITEMA, 0, (LPARAM)&g_item_expanding);
896 ok(ret == TRUE, "got %lu\n", ret);
899 case TVN_ITEMEXPANDED:
900 ok(pTreeView->itemNew.mask & TVIF_STATE, "got wrong mask %x\n", pTreeView->itemNew.mask);
901 ok(pTreeView->itemNew.state & (TVIS_EXPANDED|TVIS_EXPANDEDONCE),
902 "got wrong mask %x\n", pTreeView->itemNew.mask);
903 ok(pTreeView->itemOld.mask == 0,
904 "got wrong mask %x\n", pTreeView->itemOld.mask);
906 if (g_get_from_expand)
908 g_item_expanded.mask = TVIF_STATE;
909 g_item_expanded.hItem = hRoot;
910 ret = SendMessageA(pHdr->hwndFrom, TVM_GETITEMA, 0, (LPARAM)&g_item_expanded);
911 ok(ret == TRUE, "got %lu\n", ret);
913 if (g_get_rect_in_expand) {
914 visibleItem = TreeView_GetNextItem(pHdr->hwndFrom, NULL, TVGN_FIRSTVISIBLE);
915 ok(pTreeView->itemNew.hItem == visibleItem, "expanded item == first visible item\n");
916 *(HTREEITEM*)&rect = visibleItem;
917 ok(SendMessage(pHdr->hwndFrom, TVM_GETITEMRECT, TRUE, (LPARAM)&rect), "Failed to get rect for first visible item.\n");
918 visibleItem = TreeView_GetNextItem(pHdr->hwndFrom, visibleItem, TVGN_NEXTVISIBLE);
919 *(HTREEITEM*)&rect = visibleItem;
920 ok(visibleItem != NULL, "There must be a visible item after the first visisble item.\n");
921 ok(SendMessage(pHdr->hwndFrom, TVM_GETITEMRECT, TRUE, (LPARAM)&rect), "Failed to get rect for second visible item.\n");
933 defwndproc_counter++;
934 ret = DefWindowProcA(hWnd, message, wParam, lParam);
935 defwndproc_counter--;
940 static void test_expandinvisible(void)
942 static CHAR nodeText[][5] = {"0", "1", "2", "3", "4"};
950 hTree = create_treeview_control();
952 /* The test builds the following tree and expands then node 1, while node 0 is collapsed.
962 ret = TreeView_DeleteAllItems(hTree);
963 ok(ret == TRUE, "ret\n");
964 ins.hParent = TVI_ROOT;
965 ins.hInsertAfter = TVI_ROOT;
966 U(ins).item.mask = TVIF_TEXT;
967 U(ins).item.pszText = nodeText[0];
968 node[0] = TreeView_InsertItem(hTree, &ins);
971 ins.hInsertAfter = TVI_LAST;
972 U(ins).item.mask = TVIF_TEXT;
973 ins.hParent = node[0];
975 U(ins).item.pszText = nodeText[1];
976 node[1] = TreeView_InsertItem(hTree, &ins);
978 U(ins).item.pszText = nodeText[4];
979 node[4] = TreeView_InsertItem(hTree, &ins);
982 ins.hParent = node[1];
984 U(ins).item.pszText = nodeText[2];
985 node[2] = TreeView_InsertItem(hTree, &ins);
987 U(ins).item.pszText = nodeText[3];
988 node[3] = TreeView_InsertItem(hTree, &ins);
992 nodeVisible = TreeView_GetItemRect(hTree, node[1], &dummyRect, FALSE);
993 ok(!nodeVisible, "Node 1 should not be visible.\n");
994 nodeVisible = TreeView_GetItemRect(hTree, node[2], &dummyRect, FALSE);
995 ok(!nodeVisible, "Node 2 should not be visible.\n");
996 nodeVisible = TreeView_GetItemRect(hTree, node[3], &dummyRect, FALSE);
997 ok(!nodeVisible, "Node 3 should not be visible.\n");
998 nodeVisible = TreeView_GetItemRect(hTree, node[4], &dummyRect, FALSE);
999 ok(!nodeVisible, "Node 4 should not be visible.\n");
1001 ok(TreeView_Expand(hTree, node[1], TVE_EXPAND), "Expand of node 1 failed.\n");
1003 nodeVisible = TreeView_GetItemRect(hTree, node[1], &dummyRect, FALSE);
1004 ok(!nodeVisible, "Node 1 should not be visible.\n");
1005 nodeVisible = TreeView_GetItemRect(hTree, node[2], &dummyRect, FALSE);
1006 ok(!nodeVisible, "Node 2 should not be visible.\n");
1007 nodeVisible = TreeView_GetItemRect(hTree, node[3], &dummyRect, FALSE);
1008 ok(!nodeVisible, "Node 3 should not be visible.\n");
1009 nodeVisible = TreeView_GetItemRect(hTree, node[4], &dummyRect, FALSE);
1010 ok(!nodeVisible, "Node 4 should not be visible.\n");
1012 DestroyWindow(hTree);
1015 static void test_itemedit(void)
1023 hTree = create_treeview_control();
1026 /* try with null item */
1027 edit = (HWND)SendMessage(hTree, TVM_EDITLABEL, 0, 0);
1028 ok(!IsWindow(edit), "Expected valid handle\n");
1031 edit = (HWND)SendMessage(hTree, TVM_EDITLABEL, 0, (LPARAM)hRoot);
1032 ok(IsWindow(edit), "Expected valid handle\n");
1033 /* item shouldn't be selected automatically after TVM_EDITLABEL */
1034 r = SendMessage(hTree, TVM_GETITEMSTATE, (WPARAM)hRoot, TVIS_SELECTED);
1036 /* try to cancel with wrong edit handle */
1037 r = SendMessage(hTree, WM_COMMAND, MAKEWPARAM(0, EN_KILLFOCUS), 0);
1039 ok(IsWindow(edit), "Expected edit control to be valid\n");
1040 r = SendMessage(hTree, WM_COMMAND, MAKEWPARAM(0, EN_KILLFOCUS), (LPARAM)edit);
1042 ok(!IsWindow(edit), "Expected edit control to be destroyed\n");
1043 /* try to cancel without creating edit */
1044 r = SendMessage(hTree, WM_COMMAND, MAKEWPARAM(0, EN_KILLFOCUS), 0);
1047 /* try to cancel with wrong (not null) handle */
1048 edit = (HWND)SendMessage(hTree, TVM_EDITLABEL, 0, (LPARAM)hRoot);
1049 ok(IsWindow(edit), "Expected valid handle\n");
1050 r = SendMessage(hTree, WM_COMMAND, MAKEWPARAM(0, EN_KILLFOCUS), (LPARAM)hTree);
1052 ok(IsWindow(edit), "Expected edit control to be valid\n");
1053 r = SendMessage(hTree, WM_COMMAND, MAKEWPARAM(0, EN_KILLFOCUS), (LPARAM)edit);
1056 /* remove selection after starting edit */
1057 r = TreeView_SelectItem(hTree, hRoot);
1059 edit = (HWND)SendMessage(hTree, TVM_EDITLABEL, 0, (LPARAM)hRoot);
1060 ok(IsWindow(edit), "Expected valid handle\n");
1061 r = TreeView_SelectItem(hTree, NULL);
1064 strncpy(buff, "x", sizeof(buff)/sizeof(CHAR));
1065 r = SendMessage(edit, WM_SETTEXT, 0, (LPARAM)buff);
1067 r = SendMessage(hTree, WM_COMMAND, MAKEWPARAM(0, EN_KILLFOCUS), (LPARAM)edit);
1069 ok(!IsWindow(edit), "Expected edit control to be destroyed\n");
1070 /* check that text is saved */
1071 item.mask = TVIF_TEXT;
1073 item.pszText = buff;
1074 item.cchTextMax = sizeof(buff)/sizeof(CHAR);
1075 r = SendMessage(hTree, TVM_GETITEM, 0, (LPARAM)&item);
1077 ok(!strcmp("x", buff), "Expected item text to change\n");
1079 DestroyWindow(hTree);
1082 static void test_treeview_classinfo(void)
1086 memset(&cls, 0, sizeof(cls));
1087 GetClassInfo(GetModuleHandleA("comctl32.dll"), WC_TREEVIEWA, &cls);
1088 ok(cls.hbrBackground == NULL, "Expected NULL background brush, got %p\n", cls.hbrBackground);
1089 ok(cls.style == (CS_GLOBALCLASS | CS_DBLCLKS), "Expected got %x\n", cls.style);
1090 expect(0, cls.cbClsExtra);
1093 static void test_get_linecolor(void)
1098 hTree = create_treeview_control();
1100 /* newly created control has default color */
1101 clr = (COLORREF)SendMessage(hTree, TVM_GETLINECOLOR, 0, 0);
1103 win_skip("TVM_GETLINECOLOR is not supported on comctl32 < 5.80\n");
1105 expect(CLR_DEFAULT, clr);
1107 DestroyWindow(hTree);
1110 static void test_get_insertmarkcolor(void)
1115 hTree = create_treeview_control();
1117 /* newly created control has default color */
1118 clr = (COLORREF)SendMessage(hTree, TVM_GETINSERTMARKCOLOR, 0, 0);
1120 win_skip("TVM_GETINSERTMARKCOLOR is not supported on comctl32 < 5.80\n");
1122 expect(CLR_DEFAULT, clr);
1124 DestroyWindow(hTree);
1127 static void test_expandnotify(void)
1133 hTree = create_treeview_control();
1137 item.mask = TVIF_STATE;
1139 item.state = TVIS_EXPANDED;
1140 ret = SendMessageA(hTree, TVM_GETITEMA, 0, (LPARAM)&item);
1141 ok(ret == TRUE, "got %d\n", ret);
1142 ok((item.state & TVIS_EXPANDED) == 0, "expected collapsed\n");
1144 /* preselect root node here */
1145 ret = SendMessageA(hTree, TVM_SELECTITEM, TVGN_CARET, (LPARAM)hRoot);
1146 ok(ret == TRUE, "got %d\n", ret);
1148 g_get_from_expand = TRUE;
1150 flush_sequences(sequences, NUM_MSG_SEQUENCES);
1151 g_item_expanding.state = 0xdeadbeef;
1152 g_item_expanded.state = 0xdeadbeef;
1153 ret = SendMessageA(hTree, TVM_EXPAND, TVE_EXPAND, (LPARAM)hRoot);
1154 ok(ret == TRUE, "got %d\n", ret);
1155 ok(g_item_expanding.state == TVIS_SELECTED, "got state on TVN_ITEMEXPANDING 0x%08x\n",
1156 g_item_expanding.state);
1157 ok(g_item_expanded.state == (TVIS_SELECTED|TVIS_EXPANDED), "got state on TVN_ITEMEXPANDED 0x%08x\n",
1158 g_item_expanded.state);
1159 ok_sequence(sequences, PARENT_SEQ_INDEX, parent_expand_seq, "expand notifications", FALSE);
1160 g_get_from_expand = FALSE;
1162 /* check that it's expanded */
1163 item.state = TVIS_EXPANDED;
1164 ret = SendMessageA(hTree, TVM_GETITEMA, 0, (LPARAM)&item);
1165 ok(ret == TRUE, "got %d\n", ret);
1166 ok((item.state & TVIS_EXPANDED) == TVIS_EXPANDED, "expected expanded\n");
1169 flush_sequences(sequences, NUM_MSG_SEQUENCES);
1170 ret = SendMessageA(hTree, TVM_EXPAND, TVE_COLLAPSE, (LPARAM)hRoot);
1171 ok(ret == TRUE, "got %d\n", ret);
1172 item.state = TVIS_EXPANDED;
1173 ret = SendMessageA(hTree, TVM_GETITEMA, 0, (LPARAM)&item);
1174 ok(ret == TRUE, "got %d\n", ret);
1175 ok((item.state & TVIS_EXPANDED) == 0, "expected collapsed\n");
1176 /* all next collapse/expand attempts won't produce any notifications,
1177 the only way is to reset with all children removed */
1178 ok_sequence(sequences, PARENT_SEQ_INDEX, empty_seq, "collapse after expand notifications", FALSE);
1180 DestroyWindow(hTree);
1183 static void test_rect_retrieval_after_expand_with_select(void) {
1186 hTree = create_treeview_control();
1188 g_get_rect_in_expand = TRUE;
1189 ret = TreeView_Select(hTree, hChild, TVGN_CARET);
1190 g_get_rect_in_expand = FALSE;
1191 ok(ret,"TreeView_Select should return true\n");
1194 START_TEST(treeview)
1197 BOOL (WINAPI *pInitCommonControlsEx)(const INITCOMMONCONTROLSEX*);
1201 hComctl32 = GetModuleHandleA("comctl32.dll");
1202 pInitCommonControlsEx = (void*)GetProcAddress(hComctl32, "InitCommonControlsEx");
1203 if (pInitCommonControlsEx)
1205 INITCOMMONCONTROLSEX iccex;
1206 iccex.dwSize = sizeof(iccex);
1207 iccex.dwICC = ICC_TREEVIEW_CLASSES;
1208 pInitCommonControlsEx(&iccex);
1211 InitCommonControls();
1213 init_msg_sequences(sequences, NUM_MSG_SEQUENCES);
1215 wc.style = CS_HREDRAW | CS_VREDRAW;
1218 wc.hInstance = GetModuleHandleA(NULL);
1220 wc.hCursor = LoadCursorA(NULL, IDC_IBEAM);
1221 wc.hbrBackground = GetSysColorBrush(COLOR_WINDOW);
1222 wc.lpszMenuName = NULL;
1223 wc.lpszClassName = "MyTestWnd";
1224 wc.lpfnWndProc = parent_wnd_proc;
1225 RegisterClassA(&wc);
1227 hMainWnd = CreateWindowExA(0, "MyTestWnd", "Blah", WS_OVERLAPPEDWINDOW,
1228 CW_USEDEFAULT, CW_USEDEFAULT, 130, 105, NULL, NULL, GetModuleHandleA(NULL), 0);
1230 ok(hMainWnd != NULL, "Failed to create parent window. Tests aborted.\n");
1231 if (!hMainWnd) return;
1237 test_get_set_bkcolor();
1238 test_get_set_imagelist();
1239 test_get_set_indent();
1240 test_get_set_insertmark();
1241 test_get_set_item();
1242 test_get_set_itemheight();
1243 test_get_set_scrolltime();
1244 test_get_set_textcolor();
1245 test_get_linecolor();
1246 test_get_insertmarkcolor();
1247 test_get_set_tooltips();
1248 test_get_set_unicodeformat();
1250 test_expandinvisible();
1252 test_treeview_classinfo();
1253 test_expandnotify();
1254 test_rect_retrieval_after_expand_with_select();
1256 PostMessageA(hMainWnd, WM_CLOSE, 0, 0);
1257 while(GetMessageA(&msg,0,0,0)) {
1258 TranslateMessage(&msg);
1259 DispatchMessageA(&msg);