include/msvcrt: Define more CPU control word flags.
[wine] / dlls / comctl32 / tests / treeview.c
1 /* Unit tests for treeview.
2  *
3  * Copyright 2005 Krzysztof Foltman
4  * Copyright 2007 Christopher James Peterson
5  *
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.
10  *
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.
15  *
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
19  */
20
21 #include <assert.h>
22 #include <stdarg.h>
23
24 #include "windef.h"
25 #include "winbase.h"
26 #include "wingdi.h"
27 #include "winuser.h"
28 #include "winnls.h"
29 #include "winreg.h"
30 #include "commctrl.h" 
31
32 #include "wine/test.h"
33 #include "v6util.h"
34 #include "msg.h"
35
36 static const char *TEST_CALLBACK_TEXT = "callback_text";
37
38 static TVITEMA g_item_expanding, g_item_expanded;
39 static BOOL g_get_from_expand;
40 static BOOL g_get_rect_in_expand;
41 static BOOL g_disp_A_to_W;
42
43 #define NUM_MSG_SEQUENCES   2
44 #define TREEVIEW_SEQ_INDEX  0
45 #define PARENT_SEQ_INDEX    1
46
47 #define expect(expected, got) ok(got == expected, "Expected %d, got %d\n", expected, got)
48
49 static struct msg_sequence *sequences[NUM_MSG_SEQUENCES];
50 static struct msg_sequence *item_sequence[1];
51
52 static const struct message FillRootSeq[] = {
53     { TVM_INSERTITEM, sent },
54     { TVM_INSERTITEM, sent },
55     { 0 }
56 };
57
58 static const struct message rootnone_select_seq[] = {
59     { TVM_SELECTITEM, sent|wparam, 9 },
60     { TVM_SELECTITEM, sent|wparam, 9 },
61     { TVM_SELECTITEM, sent|wparam, 9 },
62     { TVM_SELECTITEM, sent|wparam, 9 },
63     { TVM_SELECTITEM, sent|wparam, 9 },
64     { TVM_SELECTITEM, sent|wparam, 9 },
65     { 0 }
66 };
67
68 static const struct message rootchild_select_seq[] = {
69     { TVM_SELECTITEM, sent|wparam, 9 },
70     { TVM_SELECTITEM, sent|wparam, 9 },
71     { TVM_SELECTITEM, sent|wparam, 9 },
72     { TVM_SELECTITEM, sent|wparam, 9 },
73     { TVM_SELECTITEM, sent|wparam, 9 },
74     { TVM_SELECTITEM, sent|wparam, 9 },
75     { 0 }
76 };
77
78 static const struct message getitemtext_seq[] = {
79     { TVM_INSERTITEMA, sent },
80     { TVM_GETITEMA, sent },
81     { TVM_DELETEITEM, sent },
82     { 0 }
83 };
84
85 static const struct message focus_seq[] = {
86     { TVM_INSERTITEM, sent },
87     { TVM_INSERTITEM, sent },
88     { TVM_SELECTITEM, sent|wparam, 9 },
89     /* The following end up out of order in wine */
90     { WM_WINDOWPOSCHANGING, sent|defwinproc },
91     { WM_NCCALCSIZE, sent|wparam|defwinproc, TRUE },
92     { WM_WINDOWPOSCHANGED, sent|defwinproc },
93     { WM_SIZE, sent|defwinproc },
94     { WM_PAINT, sent|defwinproc },
95     { WM_NCPAINT, sent|wparam|defwinproc, 1 },
96     { WM_ERASEBKGND, sent|defwinproc },
97     { TVM_EDITLABEL, sent },
98     { WM_COMMAND, sent|wparam|defwinproc, MAKEWPARAM(0, EN_UPDATE) },
99     { WM_COMMAND, sent|wparam|defwinproc, MAKEWPARAM(0, EN_CHANGE) },
100     { WM_PARENTNOTIFY, sent|wparam|defwinproc, MAKEWPARAM(WM_CREATE, 0) },
101     { WM_KILLFOCUS, sent|defwinproc },
102     { WM_PAINT, sent|defwinproc },
103     { WM_IME_SETCONTEXT, sent|defwinproc|optional },
104     { WM_COMMAND, sent|wparam|defwinproc, MAKEWPARAM(0, EN_SETFOCUS) },
105     { WM_ERASEBKGND, sent|defwinproc|optional },
106     { WM_CTLCOLOREDIT, sent|defwinproc|optional },
107     { WM_CTLCOLOREDIT, sent|defwinproc|optional },
108     { 0 }
109 };
110
111 static const struct message test_get_set_bkcolor_seq[] = {
112     { TVM_GETBKCOLOR, sent|wparam|lparam, 0, 0 },
113     { TVM_SETBKCOLOR, sent|wparam|lparam, 0, 0 },
114     { TVM_GETBKCOLOR, sent|wparam|lparam, 0, 0 },
115     { TVM_SETBKCOLOR, sent|wparam|lparam, 0, 0x00ffffff },
116     { TVM_GETBKCOLOR, sent|wparam|lparam, 0, 0 },
117     { TVM_SETBKCOLOR, sent|wparam|lparam, 0, -1 },
118     { 0 }
119 };
120
121 static const struct message test_get_set_imagelist_seq[] = {
122     { TVM_SETIMAGELIST, sent|wparam|lparam, 0, 0 },
123     { TVM_GETIMAGELIST, sent|wparam|lparam, 0, 0 },
124     { 0 }
125 };
126
127 static const struct message test_get_set_indent_seq[] = {
128     { TVM_SETINDENT, sent|wparam|lparam, 0, 0 },
129     { TVM_GETINDENT, sent|wparam|lparam, 0, 0 },
130     /* The actual amount to indent is dependent on the system for this message */
131     { TVM_SETINDENT, sent },
132     { TVM_GETINDENT, sent|wparam|lparam, 0, 0 },
133     { 0 }
134 };
135
136 static const struct message test_get_set_insertmarkcolor_seq[] = {
137     { TVM_SETINSERTMARKCOLOR, sent|wparam|lparam, 0, 0 },
138     { TVM_GETINSERTMARKCOLOR, sent|wparam|lparam, 0, 0 },
139     { 0 }
140 };
141
142 static const struct message test_get_set_item_seq[] = {
143     { TVM_GETITEMA, sent },
144     { TVM_SETITEMA, sent },
145     { TVM_GETITEMA, sent },
146     { TVM_SETITEMA, sent },
147     { 0 }
148 };
149
150 static const struct message test_get_set_itemheight_seq[] = {
151     { TVM_GETITEMHEIGHT, sent|wparam|lparam, 0, 0 },
152     { TVM_SETITEMHEIGHT, sent|wparam|lparam, -1, 0 },
153     { TVM_GETITEMHEIGHT, sent|wparam|lparam, 0, 0 },
154     { TVM_SETITEMHEIGHT, sent|lparam, 0xcccccccc, 0 },
155     { TVM_GETITEMHEIGHT, sent|wparam|lparam|optional, 0, 0 },
156     { TVM_SETITEMHEIGHT, sent|wparam|lparam|optional, 9, 0 },
157     { TVM_GETITEMHEIGHT, sent|wparam|lparam, 0, 0 },
158     { 0 }
159 };
160
161 static const struct message test_get_set_scrolltime_seq[] = {
162     { TVM_SETSCROLLTIME, sent|wparam|lparam, 20, 0 },
163     { TVM_GETSCROLLTIME, sent|wparam|lparam, 0, 0 },
164     { 0 }
165 };
166
167 static const struct message test_get_set_textcolor_seq[] = {
168     { TVM_GETTEXTCOLOR, sent|wparam|lparam, 0, 0 },
169     { TVM_SETTEXTCOLOR, sent|wparam|lparam, 0, 0 },
170     { TVM_GETTEXTCOLOR, sent|wparam|lparam, 0, 0 },
171     { TVM_SETTEXTCOLOR, sent|wparam|lparam, 0, RGB(255, 255, 255) },
172     { TVM_GETTEXTCOLOR, sent|wparam|lparam, 0, 0 },
173     { TVM_SETTEXTCOLOR, sent|wparam|lparam, 0, CLR_NONE },
174     { 0 }
175 };
176
177 static const struct message test_get_set_tooltips_seq[] = {
178     { WM_KILLFOCUS,    sent },
179     { WM_IME_SETCONTEXT, sent|optional },
180     { WM_IME_NOTIFY, sent|optional },
181     { TVM_SETTOOLTIPS, sent|wparam|lparam, 0, 0 },
182     { TVM_GETTOOLTIPS, sent|wparam|lparam, 0, 0 },
183     { 0 }
184 };
185
186 static const struct message test_get_set_unicodeformat_seq[] = {
187     { TVM_SETUNICODEFORMAT, sent|wparam|lparam, TRUE, 0 },
188     { TVM_GETUNICODEFORMAT, sent|wparam|lparam, 0, 0 },
189     { TVM_SETUNICODEFORMAT, sent|wparam|lparam, 0, 0 },
190     { TVM_GETUNICODEFORMAT, sent|wparam|lparam, 0, 0 },
191     { TVM_SETUNICODEFORMAT, sent|wparam|lparam, 0, 0 },
192     { 0 }
193 };
194
195 static const struct message parent_expand_seq[] = {
196     { WM_NOTIFY, sent|id, 0, 0, TVN_ITEMEXPANDINGA },
197     { WM_NOTIFY, sent|id, 0, 0, TVN_ITEMEXPANDEDA },
198     { 0 }
199 };
200
201 static const struct message parent_singleexpand_seq[] = {
202     { WM_NOTIFY, sent|id, 0, 0, TVN_SELCHANGINGA },
203     { WM_NOTIFY, sent|id, 0, 0, TVN_SELCHANGEDA },
204     { WM_NOTIFY, sent|id, 0, 0, TVN_SINGLEEXPAND },
205     { WM_NOTIFY, sent|id, 0, 0, TVN_ITEMEXPANDINGA },
206     { WM_NOTIFY, sent|id, 0, 0, TVN_ITEMEXPANDEDA },
207     { 0 }
208 };
209
210 static const struct message empty_seq[] = {
211     { 0 }
212 };
213
214 static HWND hMainWnd;
215
216 static HTREEITEM hRoot, hChild;
217
218 static int pos = 0;
219 static char sequence[256];
220
221 static void Clear(void)
222 {
223     pos = 0;
224     sequence[0] = '\0';
225 }
226
227 static void AddItem(char ch)
228 {
229     sequence[pos++] = ch;
230     sequence[pos] = '\0';
231 }
232
233 static void IdentifyItem(HTREEITEM hItem)
234 {
235     if (hItem == hRoot) {
236         AddItem('R');
237         return;
238     }
239     if (hItem == hChild) {
240         AddItem('C');
241         return;
242     }
243     if (hItem == NULL) {
244         AddItem('n');
245         return;
246     }
247     AddItem('?');
248 }
249
250 /* This function hooks in and records all messages to the treeview control */
251 static LRESULT WINAPI TreeviewWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
252 {
253     static LONG defwndproc_counter = 0;
254     LRESULT ret;
255     struct message msg;
256     WNDPROC lpOldProc = (WNDPROC)GetWindowLongPtrA(hwnd, GWLP_USERDATA);
257
258     msg.message = message;
259     msg.flags = sent|wparam|lparam;
260     if (defwndproc_counter) msg.flags |= defwinproc;
261     msg.wParam = wParam;
262     msg.lParam = lParam;
263     add_message(sequences, TREEVIEW_SEQ_INDEX, &msg);
264
265     defwndproc_counter++;
266     ret = CallWindowProcA(lpOldProc, hwnd, message, wParam, lParam);
267     defwndproc_counter--;
268
269     return ret;
270 }
271
272 static HWND create_treeview_control(void)
273 {
274     WNDPROC pOldWndProc;
275     HWND hTree;
276
277     hTree = CreateWindowExA(WS_EX_CLIENTEDGE, WC_TREEVIEWA, NULL, WS_CHILD|WS_VISIBLE|
278             TVS_LINESATROOT|TVS_HASLINES|TVS_HASBUTTONS|TVS_EDITLABELS,
279             0, 0, 120, 100, hMainWnd, (HMENU)100, GetModuleHandleA(0), 0);
280
281     SetFocus(hTree);
282
283     /* Record the old WNDPROC so we can call it after recording the messages */
284     pOldWndProc = (WNDPROC)SetWindowLongPtrA(hTree, GWLP_WNDPROC, (LONG_PTR)TreeviewWndProc);
285     SetWindowLongPtrA(hTree, GWLP_USERDATA, (LONG_PTR)pOldWndProc);
286
287     return hTree;
288 }
289
290 static void fill_tree(HWND hTree)
291 {
292     TVINSERTSTRUCTA ins;
293     static CHAR root[]  = "Root",
294                 child[] = "Child";
295
296     ins.hParent = TVI_ROOT;
297     ins.hInsertAfter = TVI_ROOT;
298     U(ins).item.mask = TVIF_TEXT;
299     U(ins).item.pszText = root;
300     hRoot = TreeView_InsertItem(hTree, &ins);
301
302     ins.hParent = hRoot;
303     ins.hInsertAfter = TVI_FIRST;
304     U(ins).item.mask = TVIF_TEXT;
305     U(ins).item.pszText = child;
306     hChild = TreeView_InsertItem(hTree, &ins);
307 }
308
309 static void test_fillroot(void)
310 {
311     TVITEMA tvi;
312     HWND hTree;
313
314     hTree = create_treeview_control();
315
316     flush_sequences(sequences, NUM_MSG_SEQUENCES);
317
318     fill_tree(hTree);
319
320     Clear();
321     AddItem('A');
322     assert(hRoot);
323     AddItem('B');
324     assert(hChild);
325     AddItem('.');
326     ok_sequence(sequences, TREEVIEW_SEQ_INDEX, FillRootSeq, "FillRoot", FALSE);
327     ok(!strcmp(sequence, "AB."), "Item creation\n");
328
329     /* UMLPad 1.15 depends on this being not -1 (I_IMAGECALLBACK) */
330     tvi.hItem = hRoot;
331     tvi.mask = TVIF_IMAGE | TVIF_SELECTEDIMAGE;
332     SendMessage( hTree, TVM_GETITEMA, 0, (LPARAM)&tvi );
333     ok(tvi.iImage == 0, "tvi.iImage=%d\n", tvi.iImage);
334     ok(tvi.iSelectedImage == 0, "tvi.iSelectedImage=%d\n", tvi.iSelectedImage);
335
336     DestroyWindow(hTree);
337 }
338
339 static void test_callback(void)
340 {
341     HTREEITEM hRoot;
342     HTREEITEM hItem1, hItem2;
343     TVINSERTSTRUCTA ins;
344     TVITEMA tvi;
345     CHAR test_string[] = "Test_string";
346     static const CHAR test2A[] = "TEST2";
347     CHAR buf[128];
348     LRESULT ret;
349     HWND hTree;
350
351     hTree = create_treeview_control();
352
353     ret = TreeView_DeleteAllItems(hTree);
354     ok(ret == TRUE, "ret\n");
355     ins.hParent = TVI_ROOT;
356     ins.hInsertAfter = TVI_ROOT;
357     U(ins).item.mask = TVIF_TEXT;
358     U(ins).item.pszText = LPSTR_TEXTCALLBACK;
359     hRoot = TreeView_InsertItem(hTree, &ins);
360     assert(hRoot);
361
362     tvi.hItem = hRoot;
363     tvi.mask = TVIF_TEXT;
364     tvi.pszText = buf;
365     tvi.cchTextMax = sizeof(buf)/sizeof(buf[0]);
366     ret = TreeView_GetItem(hTree, &tvi);
367     ok(ret == 1, "ret\n");
368     ok(strcmp(tvi.pszText, TEST_CALLBACK_TEXT) == 0, "Callback item text mismatch %s vs %s\n",
369         tvi.pszText, TEST_CALLBACK_TEXT);
370
371     ins.hParent = hRoot;
372     ins.hInsertAfter = TVI_FIRST;
373     U(ins).item.mask = TVIF_TEXT;
374     U(ins).item.pszText = test_string;
375     hItem1 = TreeView_InsertItem(hTree, &ins);
376     assert(hItem1);
377
378     tvi.hItem = hItem1;
379     ret = TreeView_GetItem(hTree, &tvi);
380     ok(ret == TRUE, "ret\n");
381     ok(strcmp(tvi.pszText, test_string) == 0, "Item text mismatch %s vs %s\n",
382         tvi.pszText, test_string);
383
384     /* undocumented: pszText of NULL also means LPSTR_CALLBACK: */
385     tvi.pszText = NULL;
386     ret = TreeView_SetItem(hTree, &tvi);
387     ok(ret == 1, "Expected SetItem return 1, got %ld\n", ret);
388     tvi.pszText = buf;
389     ret = TreeView_GetItem(hTree, &tvi);
390     ok(ret == TRUE, "Expected GetItem return TRUE, got %ld\n", ret);
391     ok(strcmp(tvi.pszText, TEST_CALLBACK_TEXT) == 0, "Item text mismatch %s vs %s\n",
392         tvi.pszText, TEST_CALLBACK_TEXT);
393
394     U(ins).item.pszText = NULL;
395     hItem2 = TreeView_InsertItem(hTree, &ins);
396     assert(hItem2);
397     tvi.hItem = hItem2;
398     memset(buf, 0, sizeof(buf));
399     ret = TreeView_GetItem(hTree, &tvi);
400     ok(ret == TRUE, "Expected GetItem return TRUE, got %ld\n", ret);
401     ok(strcmp(tvi.pszText, TEST_CALLBACK_TEXT) == 0, "Item text mismatch %s vs %s\n",
402         tvi.pszText, TEST_CALLBACK_TEXT);
403
404     /* notification handler changed A->W */
405     g_disp_A_to_W = TRUE;
406     tvi.hItem = hItem2;
407     memset(buf, 0, sizeof(buf));
408     ret = TreeView_GetItem(hTree, &tvi);
409     ok(ret == TRUE, "got %ld\n", ret);
410     ok(strcmp(tvi.pszText, test2A) == 0, "got %s, expected %s\n",
411         tvi.pszText, test2A);
412     g_disp_A_to_W = FALSE;
413
414     DestroyWindow(hTree);
415 }
416
417 static void test_select(void)
418 {
419     BOOL r;
420     HWND hTree;
421
422     hTree = create_treeview_control();
423     fill_tree(hTree);
424
425     /* root-none select tests */
426     flush_sequences(sequences, NUM_MSG_SEQUENCES);
427     r = TreeView_SelectItem(hTree, NULL);
428     expect(TRUE, r);
429     Clear();
430     AddItem('1');
431     r = TreeView_SelectItem(hTree, hRoot);
432     expect(TRUE, r);
433     AddItem('2');
434     r = TreeView_SelectItem(hTree, hRoot);
435     expect(TRUE, r);
436     AddItem('3');
437     r = TreeView_SelectItem(hTree, NULL);
438     expect(TRUE, r);
439     AddItem('4');
440     r = TreeView_SelectItem(hTree, NULL);
441     expect(TRUE, r);
442     AddItem('5');
443     r = TreeView_SelectItem(hTree, hRoot);
444     expect(TRUE, r);
445     AddItem('.');
446     ok(!strcmp(sequence, "1(nR)nR23(Rn)Rn45(nR)nR."), "root-none select test\n");
447     ok_sequence(sequences, TREEVIEW_SEQ_INDEX, rootnone_select_seq,
448                 "root-none select seq", FALSE);
449
450     /* root-child select tests */
451     flush_sequences(sequences, NUM_MSG_SEQUENCES);
452     r = TreeView_SelectItem(hTree, NULL);
453     expect(TRUE, r);
454
455     Clear();
456     AddItem('1');
457     r = TreeView_SelectItem(hTree, hRoot);
458     expect(TRUE, r);
459     AddItem('2');
460     r = TreeView_SelectItem(hTree, hRoot);
461     expect(TRUE, r);
462     AddItem('3');
463     r = TreeView_SelectItem(hTree, hChild);
464     expect(TRUE, r);
465     AddItem('4');
466     r = TreeView_SelectItem(hTree, hChild);
467     expect(TRUE, r);
468     AddItem('5');
469     r = TreeView_SelectItem(hTree, hRoot);
470     expect(TRUE, r);
471     AddItem('.');
472     ok(!strcmp(sequence, "1(nR)nR23(RC)RC45(CR)CR."), "root-child select test\n");
473     ok_sequence(sequences, TREEVIEW_SEQ_INDEX, rootchild_select_seq,
474                 "root-child select seq", FALSE);
475
476     DestroyWindow(hTree);
477 }
478
479 static void test_getitemtext(void)
480 {
481     TVINSERTSTRUCTA ins;
482     HTREEITEM hChild;
483     TVITEMA tvi;
484     HWND hTree;
485
486     CHAR szBuffer[80] = "Blah";
487     int nBufferSize = sizeof(szBuffer)/sizeof(CHAR);
488
489     hTree = create_treeview_control();
490     fill_tree(hTree);
491
492     flush_sequences(sequences, NUM_MSG_SEQUENCES);
493
494     /* add an item without TVIF_TEXT mask and pszText == NULL */
495     ins.hParent = hRoot;
496     ins.hInsertAfter = TVI_ROOT;
497     U(ins).item.mask = 0;
498     U(ins).item.pszText = NULL;
499     U(ins).item.cchTextMax = 0;
500     hChild = TreeView_InsertItem(hTree, &ins);
501     assert(hChild);
502
503     /* retrieve it with TVIF_TEXT mask */
504     tvi.hItem = hChild;
505     tvi.mask = TVIF_TEXT;
506     tvi.cchTextMax = nBufferSize;
507     tvi.pszText = szBuffer;
508
509     SendMessageA( hTree, TVM_GETITEMA, 0, (LPARAM)&tvi );
510     ok(!strcmp(szBuffer, ""), "szBuffer=\"%s\", expected \"\"\n", szBuffer);
511     ok(SendMessageA(hTree, TVM_DELETEITEM, 0, (LPARAM)hChild), "DeleteItem failed\n");
512     ok_sequence(sequences, TREEVIEW_SEQ_INDEX, getitemtext_seq, "get item text seq", FALSE);
513
514     DestroyWindow(hTree);
515 }
516
517 static void test_focus(void)
518 {
519     TVINSERTSTRUCTA ins;
520     static CHAR child1[]  = "Edit",
521                 child2[]  = "A really long string";
522     HTREEITEM hChild1, hChild2;
523     HWND hTree;
524     HWND hEdit;
525
526     hTree = create_treeview_control();
527     fill_tree(hTree);
528
529     flush_sequences(sequences, NUM_MSG_SEQUENCES);
530
531     /* This test verifies that when a label is being edited, scrolling
532      * the treeview does not cause the label to lose focus. To test
533      * this, first some additional entries are added to generate
534      * scrollbars.
535      */
536     ins.hParent = hRoot;
537     ins.hInsertAfter = hChild;
538     U(ins).item.mask = TVIF_TEXT;
539     U(ins).item.pszText = child1;
540     hChild1 = TreeView_InsertItem(hTree, &ins);
541     assert(hChild1);
542     ins.hInsertAfter = hChild1;
543     U(ins).item.mask = TVIF_TEXT;
544     U(ins).item.pszText = child2;
545     hChild2 = TreeView_InsertItem(hTree, &ins);
546     assert(hChild2);
547
548     ShowWindow(hMainWnd,SW_SHOW);
549     SendMessageA(hTree, TVM_SELECTITEM, TVGN_CARET, (LPARAM)hChild);
550     hEdit = TreeView_EditLabel(hTree, hChild);
551     ScrollWindowEx(hTree, -10, 0, NULL, NULL, NULL, NULL, SW_SCROLLCHILDREN);
552     ok(GetFocus() == hEdit, "Edit control should have focus\n");
553     ok_sequence(sequences, TREEVIEW_SEQ_INDEX, focus_seq, "focus test", TRUE);
554
555     DestroyWindow(hTree);
556 }
557
558 static void test_get_set_bkcolor(void)
559 {
560     COLORREF crColor = RGB(0,0,0);
561     HWND hTree;
562
563     hTree = create_treeview_control();
564     fill_tree(hTree);
565
566     flush_sequences(sequences, NUM_MSG_SEQUENCES);
567
568     /* If the value is -1, the control is using the system color for the background color. */
569     crColor = (COLORREF)SendMessage( hTree, TVM_GETBKCOLOR, 0, 0 );
570     ok(crColor == -1, "Default background color reported as 0x%.8x\n", crColor);
571
572     /* Test for black background */
573     SendMessage( hTree, TVM_SETBKCOLOR, 0, RGB(0,0,0) );
574     crColor = (COLORREF)SendMessage( hTree, TVM_GETBKCOLOR, 0, 0 );
575     ok(crColor == RGB(0,0,0), "Black background color reported as 0x%.8x\n", crColor);
576
577     /* Test for white background */
578     SendMessage( hTree, TVM_SETBKCOLOR, 0, RGB(255,255,255) );
579     crColor = (COLORREF)SendMessage( hTree, TVM_GETBKCOLOR, 0, 0 );
580     ok(crColor == RGB(255,255,255), "White background color reported as 0x%.8x\n", crColor);
581
582     /* Reset the default background */
583     SendMessage( hTree, TVM_SETBKCOLOR, 0, -1 );
584
585     ok_sequence(sequences, TREEVIEW_SEQ_INDEX, test_get_set_bkcolor_seq,
586         "test get set bkcolor", FALSE);
587
588     DestroyWindow(hTree);
589 }
590
591 static void test_get_set_imagelist(void)
592 {
593     HIMAGELIST hImageList = NULL;
594     HWND hTree;
595
596     hTree = create_treeview_control();
597     fill_tree(hTree);
598
599     flush_sequences(sequences, NUM_MSG_SEQUENCES);
600
601     /* Test a NULL HIMAGELIST */
602     SendMessage( hTree, TVM_SETIMAGELIST, TVSIL_NORMAL, (LPARAM)hImageList );
603     hImageList = (HIMAGELIST)SendMessage( hTree, TVM_GETIMAGELIST, TVSIL_NORMAL, 0 );
604     ok(hImageList == NULL, "NULL image list, reported as 0x%p, expected 0.\n", hImageList);
605
606     /* TODO: Test an actual image list */
607
608     ok_sequence(sequences, TREEVIEW_SEQ_INDEX, test_get_set_imagelist_seq,
609         "test get imagelist", FALSE);
610
611     DestroyWindow(hTree);
612 }
613
614 static void test_get_set_indent(void)
615 {
616     int ulIndent = -1;
617     int ulMinIndent = -1;
618     int ulMoreThanTwiceMin = -1;
619     HWND hTree;
620
621     hTree = create_treeview_control();
622     fill_tree(hTree);
623
624     flush_sequences(sequences, NUM_MSG_SEQUENCES);
625
626     /* Finding the minimum indent */
627     SendMessage( hTree, TVM_SETINDENT, 0, 0 );
628     ulMinIndent = (int)SendMessage( hTree, TVM_GETINDENT, 0, 0 );
629
630     /* Checking an indent that is more than twice the default indent */
631     ulMoreThanTwiceMin = 2*ulMinIndent+1;
632     SendMessage( hTree, TVM_SETINDENT, ulMoreThanTwiceMin, 0 );
633     ulIndent = (DWORD)SendMessage( hTree, TVM_GETINDENT, 0, 0 );
634     ok(ulIndent == ulMoreThanTwiceMin, "Indent reported as %d, expected %d\n", ulIndent, ulMoreThanTwiceMin);
635
636     ok_sequence(sequences, TREEVIEW_SEQ_INDEX, test_get_set_indent_seq,
637         "test get set indent", FALSE);
638
639     DestroyWindow(hTree);
640 }
641
642 static void test_get_set_insertmark(void)
643 {
644     COLORREF crColor = RGB(0,0,0);
645     HWND hTree;
646
647     hTree = create_treeview_control();
648     fill_tree(hTree);
649
650     flush_sequences(sequences, NUM_MSG_SEQUENCES);
651
652     SendMessage( hTree, TVM_SETINSERTMARKCOLOR, 0, crColor );
653     crColor = (COLORREF)SendMessage( hTree, TVM_GETINSERTMARKCOLOR, 0, 0 );
654     ok(crColor == RGB(0,0,0), "Insert mark color reported as 0x%.8x, expected 0x00000000\n", crColor);
655
656     ok_sequence(sequences, TREEVIEW_SEQ_INDEX, test_get_set_insertmarkcolor_seq,
657         "test get set insertmark color", FALSE);
658
659     DestroyWindow(hTree);
660 }
661
662 static void test_get_set_item(void)
663 {
664     TVITEMA tviRoot = {0};
665     int nBufferSize = 80;
666     char szBuffer[80] = {0};
667     HWND hTree;
668
669     hTree = create_treeview_control();
670     fill_tree(hTree);
671
672     flush_sequences(sequences, NUM_MSG_SEQUENCES);
673
674     /* Test the root item */
675     tviRoot.hItem = hRoot;
676     tviRoot.mask = TVIF_TEXT;
677     tviRoot.cchTextMax = nBufferSize;
678     tviRoot.pszText = szBuffer;
679     SendMessage( hTree, TVM_GETITEMA, 0, (LPARAM)&tviRoot );
680     ok(!strcmp("Root", szBuffer), "GetItem: szBuffer=\"%s\", expected \"Root\"\n", szBuffer);
681
682     /* Change the root text */
683     strncpy(szBuffer, "Testing123", nBufferSize);
684     SendMessage( hTree, TVM_SETITEMA, 0, (LPARAM)&tviRoot );
685     memset(szBuffer, 0, nBufferSize);
686     SendMessage( hTree, TVM_GETITEMA, 0, (LPARAM)&tviRoot );
687     ok(!strcmp("Testing123", szBuffer), "GetItem: szBuffer=\"%s\", expected \"Testing123\"\n", szBuffer);
688
689     /* Reset the root text */
690     memset(szBuffer, 0, nBufferSize);
691     strncpy(szBuffer, "Root", nBufferSize);
692     SendMessage( hTree, TVM_SETITEMA, 0, (LPARAM)&tviRoot );
693
694     ok_sequence(sequences, TREEVIEW_SEQ_INDEX, test_get_set_item_seq,
695         "test get set item", FALSE);
696
697     DestroyWindow(hTree);
698 }
699
700 static void test_get_set_itemheight(void)
701 {
702     int ulOldHeight = 0;
703     int ulNewHeight = 0;
704     HWND hTree;
705
706     hTree = create_treeview_control();
707     fill_tree(hTree);
708
709     flush_sequences(sequences, NUM_MSG_SEQUENCES);
710
711     /* Assuming default height to begin with */
712     ulOldHeight = (int) SendMessage( hTree, TVM_GETITEMHEIGHT, 0, 0 );
713
714     /* Explicitly setting and getting the default height */
715     SendMessage( hTree, TVM_SETITEMHEIGHT, -1, 0 );
716     ulNewHeight = (int) SendMessage( hTree, TVM_GETITEMHEIGHT, 0, 0 );
717     ok(ulNewHeight == ulOldHeight, "Default height not set properly, reported %d, expected %d\n", ulNewHeight, ulOldHeight);
718
719     /* Explicitly setting and getting the height of twice the normal */
720     SendMessage( hTree, TVM_SETITEMHEIGHT, 2*ulOldHeight, 0 );
721     ulNewHeight = (int) SendMessage( hTree, TVM_GETITEMHEIGHT, 0, 0 );
722     ok(ulNewHeight == 2*ulOldHeight, "New height not set properly, reported %d, expected %d\n", ulNewHeight, 2*ulOldHeight);
723
724     /* Assuming tree doesn't have TVS_NONEVENHEIGHT set, so a set of 9 will round down to 8 */
725     SendMessage( hTree, TVM_SETITEMHEIGHT, 9, 0 );
726     ulNewHeight = (int) SendMessage( hTree, TVM_GETITEMHEIGHT, 0, 0 );
727     ok(ulNewHeight == 8, "Uneven height not set properly, reported %d, expected %d\n", ulNewHeight, 8);
728
729     ok_sequence(sequences, TREEVIEW_SEQ_INDEX, test_get_set_itemheight_seq,
730         "test get set item height", FALSE);
731
732     /* without TVS_NONEVENHEIGHT */
733     SetWindowLong(hTree, GWL_STYLE, GetWindowLong(hTree, GWL_STYLE) & ~TVS_NONEVENHEIGHT);
734     /* odd value */
735     ulOldHeight = SendMessage( hTree, TVM_SETITEMHEIGHT, 3, 0);
736     ok(ulOldHeight == 8, "got %d, expected %d\n", ulOldHeight, 8);
737     ulNewHeight = (int) SendMessage( hTree, TVM_GETITEMHEIGHT, 0, 0 );
738     ok(ulNewHeight == 2, "got %d, expected %d\n", ulNewHeight, 2);
739
740     ulOldHeight = SendMessage( hTree, TVM_SETITEMHEIGHT, 4, 0);
741     ok(ulOldHeight == 2, "got %d, expected %d\n", ulOldHeight, 2);
742     ulNewHeight = (int) SendMessage( hTree, TVM_GETITEMHEIGHT, 0, 0 );
743     ok(ulNewHeight == 4, "got %d, expected %d\n", ulNewHeight, 4);
744
745     /* with TVS_NONEVENHEIGHT */
746     SetWindowLong(hTree, GWL_STYLE, GetWindowLong(hTree, GWL_STYLE) | TVS_NONEVENHEIGHT);
747     /* odd value */
748     ulOldHeight = SendMessage( hTree, TVM_SETITEMHEIGHT, 3, 0);
749     ok(ulOldHeight == 4, "got %d, expected %d\n", ulOldHeight, 4);
750     ulNewHeight = (int) SendMessage( hTree, TVM_GETITEMHEIGHT, 0, 0 );
751     ok(ulNewHeight == 3, "got %d, expected %d\n", ulNewHeight, 3);
752     /* even value */
753     ulOldHeight = SendMessage( hTree, TVM_SETITEMHEIGHT, 10, 0);
754     ok(ulOldHeight == 3, "got %d, expected %d\n", ulOldHeight, 3);
755     ulNewHeight = (int) SendMessage( hTree, TVM_GETITEMHEIGHT, 0, 0 );
756     ok(ulNewHeight == 10, "got %d, expected %d\n", ulNewHeight, 10);
757
758     DestroyWindow(hTree);
759 }
760
761 static void test_get_set_scrolltime(void)
762 {
763     int ulExpectedTime = 20;
764     int ulTime = 0;
765     HWND hTree;
766
767     hTree = create_treeview_control();
768     fill_tree(hTree);
769
770     flush_sequences(sequences, NUM_MSG_SEQUENCES);
771
772     SendMessage( hTree, TVM_SETSCROLLTIME, ulExpectedTime, 0 );
773     ulTime = (int)SendMessage( hTree, TVM_GETSCROLLTIME, 0, 0 );
774     ok(ulTime == ulExpectedTime, "Scroll time reported as %d, expected %d\n", ulTime, ulExpectedTime);
775
776     ok_sequence(sequences, TREEVIEW_SEQ_INDEX, test_get_set_scrolltime_seq,
777         "test get set scroll time", FALSE);
778
779     DestroyWindow(hTree);
780 }
781
782 static void test_get_set_textcolor(void)
783 {
784     /* If the value is -1, the control is using the system color for the text color. */
785     COLORREF crColor = RGB(0,0,0);
786     HWND hTree;
787
788     hTree = create_treeview_control();
789     fill_tree(hTree);
790
791     flush_sequences(sequences, NUM_MSG_SEQUENCES);
792
793     crColor = (COLORREF)SendMessage( hTree, TVM_GETTEXTCOLOR, 0, 0 );
794     ok(crColor == -1, "Default text color reported as 0x%.8x\n", crColor);
795
796     /* Test for black text */
797     SendMessage( hTree, TVM_SETTEXTCOLOR, 0, RGB(0,0,0) );
798     crColor = (COLORREF)SendMessage( hTree, TVM_GETTEXTCOLOR, 0, 0 );
799     ok(crColor == RGB(0,0,0), "Black text color reported as 0x%.8x\n", crColor);
800
801     /* Test for white text */
802     SendMessage( hTree, TVM_SETTEXTCOLOR, 0, RGB(255,255,255) );
803     crColor = (COLORREF)SendMessage( hTree, TVM_GETTEXTCOLOR, 0, 0 );
804     ok(crColor == RGB(255,255,255), "White text color reported as 0x%.8x\n", crColor);
805
806     /* Reset the default text color */
807     SendMessage( hTree, TVM_SETTEXTCOLOR, 0, CLR_NONE );
808
809     ok_sequence(sequences, TREEVIEW_SEQ_INDEX, test_get_set_textcolor_seq,
810         "test get set text color", FALSE);
811
812     DestroyWindow(hTree);
813 }
814
815 static void test_get_set_tooltips(void)
816 {
817     HWND hwndLastToolTip = NULL;
818     HWND hPopupTreeView;
819     HWND hTree;
820
821     hTree = create_treeview_control();
822     fill_tree(hTree);
823
824     flush_sequences(sequences, NUM_MSG_SEQUENCES);
825
826     /* show even WS_POPUP treeview don't send NM_TOOLTIPSCREATED */
827     hPopupTreeView = CreateWindow(WC_TREEVIEW, NULL, WS_POPUP|WS_VISIBLE, 0, 0, 100, 100, hMainWnd, NULL, NULL, NULL);
828     DestroyWindow(hPopupTreeView);
829
830     /* Testing setting a NULL ToolTip */
831     SendMessage( hTree, TVM_SETTOOLTIPS, 0, 0 );
832     hwndLastToolTip = (HWND)SendMessage( hTree, TVM_GETTOOLTIPS, 0, 0 );
833     ok(hwndLastToolTip == NULL, "NULL tool tip, reported as 0x%p, expected 0.\n", hwndLastToolTip);
834
835     ok_sequence(sequences, TREEVIEW_SEQ_INDEX, test_get_set_tooltips_seq,
836         "test get set tooltips", TRUE);
837
838     /* TODO: Add a test of an actual tooltip */
839     DestroyWindow(hTree);
840 }
841
842 static void test_get_set_unicodeformat(void)
843 {
844     BOOL bPreviousSetting = 0;
845     BOOL bNewSetting = 0;
846     HWND hTree;
847
848     hTree = create_treeview_control();
849     fill_tree(hTree);
850
851     flush_sequences(sequences, NUM_MSG_SEQUENCES);
852
853     /* Set to Unicode */
854     bPreviousSetting = (BOOL)SendMessage( hTree, TVM_SETUNICODEFORMAT, 1, 0 );
855     bNewSetting = (BOOL)SendMessage( hTree, TVM_GETUNICODEFORMAT, 0, 0 );
856     ok(bNewSetting == 1, "Unicode setting did not work.\n");
857
858     /* Set to ANSI */
859     SendMessage( hTree, TVM_SETUNICODEFORMAT, 0, 0 );
860     bNewSetting = (BOOL)SendMessage( hTree, TVM_GETUNICODEFORMAT, 0, 0 );
861     ok(bNewSetting == 0, "ANSI setting did not work.\n");
862
863     /* Revert to original setting */
864     SendMessage( hTree, TVM_SETUNICODEFORMAT, bPreviousSetting, 0 );
865
866     ok_sequence(sequences, TREEVIEW_SEQ_INDEX, test_get_set_unicodeformat_seq,
867         "test get set unicode format", FALSE);
868
869     DestroyWindow(hTree);
870 }
871
872 static LRESULT CALLBACK parent_wnd_proc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
873 {
874     static LONG defwndproc_counter = 0;
875     struct message msg;
876     LRESULT ret;
877     RECT rect;
878     HTREEITEM visibleItem;
879
880     msg.message = message;
881     msg.flags = sent|wparam|lparam;
882     if (defwndproc_counter) msg.flags |= defwinproc;
883     msg.wParam = wParam;
884     msg.lParam = lParam;
885     if (message == WM_NOTIFY && lParam) msg.id = ((NMHDR*)lParam)->code;
886
887     /* log system messages, except for painting */
888     if (message < WM_USER &&
889         message != WM_PAINT &&
890         message != WM_ERASEBKGND &&
891         message != WM_NCPAINT &&
892         message != WM_NCHITTEST &&
893         message != WM_GETTEXT &&
894         message != WM_GETICON &&
895         message != WM_DEVICECHANGE)
896     {
897         trace("parent: %p, %04x, %08lx, %08lx\n", hWnd, message, wParam, lParam);
898         add_message(sequences, PARENT_SEQ_INDEX, &msg);
899     }
900
901     switch(message) {
902     case WM_NOTIFY:
903     {
904         NMHDR *pHdr = (NMHDR *)lParam;
905     
906         ok(pHdr->code != NM_TOOLTIPSCREATED, "Treeview should not send NM_TOOLTIPSCREATED\n");
907         if (pHdr->idFrom == 100)
908         {
909             NMTREEVIEWA *pTreeView = (LPNMTREEVIEWA) lParam;
910             switch(pHdr->code)
911             {
912             case TVN_SELCHANGINGA:
913                 AddItem('(');
914                 IdentifyItem(pTreeView->itemOld.hItem);
915                 IdentifyItem(pTreeView->itemNew.hItem);
916                 break;
917             case TVN_SELCHANGEDA:
918                 AddItem(')');
919                 IdentifyItem(pTreeView->itemOld.hItem);
920                 IdentifyItem(pTreeView->itemNew.hItem);
921                 break;
922             case TVN_GETDISPINFOA: {
923                 NMTVDISPINFOA *disp = (NMTVDISPINFOA *)lParam;
924                 if (disp->item.mask & TVIF_TEXT) {
925                     lstrcpyn(disp->item.pszText, TEST_CALLBACK_TEXT, disp->item.cchTextMax);
926                 }
927
928                 if (g_disp_A_to_W && (disp->item.mask & TVIF_TEXT)) {
929                     static const WCHAR testW[] = {'T','E','S','T','2',0};
930
931                     disp->hdr.code = TVN_GETDISPINFOW;
932                     memcpy(disp->item.pszText, testW, sizeof(testW));
933                 }
934
935                 break;
936               }
937             case TVN_ENDLABELEDIT: return TRUE;
938             case TVN_ITEMEXPANDINGA:
939                 ok(pTreeView->itemNew.mask ==
940                    (TVIF_HANDLE | TVIF_SELECTEDIMAGE | TVIF_IMAGE | TVIF_PARAM | TVIF_STATE),
941                    "got wrong mask %x\n", pTreeView->itemNew.mask);
942                 ok((pTreeView->itemNew.state & TVIS_EXPANDED) == 0,
943                    "got wrong state %x\n", pTreeView->itemNew.state);
944                 ok(pTreeView->itemOld.mask == 0,
945                    "got wrong mask %x\n", pTreeView->itemOld.mask);
946
947                 if (g_get_from_expand)
948                 {
949                   g_item_expanding.mask = TVIF_STATE;
950                   g_item_expanding.hItem = hRoot;
951                   ret = SendMessageA(pHdr->hwndFrom, TVM_GETITEMA, 0, (LPARAM)&g_item_expanding);
952                   ok(ret == TRUE, "got %lu\n", ret);
953                 }
954                 break;
955             case TVN_ITEMEXPANDEDA:
956                 ok(pTreeView->itemNew.mask & TVIF_STATE, "got wrong mask %x\n", pTreeView->itemNew.mask);
957                 ok(pTreeView->itemNew.state & (TVIS_EXPANDED|TVIS_EXPANDEDONCE),
958                    "got wrong mask %x\n", pTreeView->itemNew.mask);
959                 ok(pTreeView->itemOld.mask == 0,
960                    "got wrong mask %x\n", pTreeView->itemOld.mask);
961
962                 if (g_get_from_expand)
963                 {
964                   g_item_expanded.mask = TVIF_STATE;
965                   g_item_expanded.hItem = hRoot;
966                   ret = SendMessageA(pHdr->hwndFrom, TVM_GETITEMA, 0, (LPARAM)&g_item_expanded);
967                   ok(ret == TRUE, "got %lu\n", ret);
968                 }
969                 if (g_get_rect_in_expand)
970                 {
971                   visibleItem = TreeView_GetNextItem(pHdr->hwndFrom, NULL, TVGN_FIRSTVISIBLE);
972                   ok(pTreeView->itemNew.hItem == visibleItem, "expanded item == first visible item\n");
973                   *(HTREEITEM*)&rect = visibleItem;
974                   ok(SendMessage(pHdr->hwndFrom, TVM_GETITEMRECT, TRUE, (LPARAM)&rect), "Failed to get rect for first visible item.\n");
975                   visibleItem = TreeView_GetNextItem(pHdr->hwndFrom, visibleItem, TVGN_NEXTVISIBLE);
976                   *(HTREEITEM*)&rect = visibleItem;
977                   ok(visibleItem != NULL, "There must be a visible item after the first visisble item.\n");
978                   ok(SendMessage(pHdr->hwndFrom, TVM_GETITEMRECT, TRUE, (LPARAM)&rect), "Failed to get rect for second visible item.\n");
979                 }
980                 break;
981             case TVN_DELETEITEMA:
982             {
983                 struct message item;
984
985                 ok(pTreeView->itemNew.mask == 0, "got wrong mask 0x%x\n", pTreeView->itemNew.mask);
986
987                 ok(pTreeView->itemOld.mask == (TVIF_HANDLE | TVIF_PARAM), "got wrong mask 0x%x\n", pTreeView->itemOld.mask);
988                 ok(pTreeView->itemOld.hItem != NULL, "got %p\n", pTreeView->itemOld.hItem);
989
990                 memset(&item, 0, sizeof(item));
991                 item.lParam = (LPARAM)pTreeView->itemOld.hItem;
992                 add_message(item_sequence, 0, &item);
993
994                 break;
995             }
996             }
997         }
998     }
999
1000     case WM_DESTROY:
1001         PostQuitMessage(0);
1002         break;
1003     }
1004
1005     defwndproc_counter++;
1006     ret = DefWindowProcA(hWnd, message, wParam, lParam);
1007     defwndproc_counter--;
1008
1009     return ret;
1010 }
1011
1012 static void test_expandinvisible(void)
1013 {
1014     static CHAR nodeText[][5] = {"0", "1", "2", "3", "4"};
1015     TVINSERTSTRUCTA ins;
1016     HTREEITEM node[5];
1017     RECT dummyRect;
1018     BOOL nodeVisible;
1019     LRESULT ret;
1020     HWND hTree;
1021
1022     hTree = create_treeview_control();
1023
1024     /* The test builds the following tree and expands then node 1, while node 0 is collapsed.
1025      *
1026      * 0
1027      * |- 1
1028      * |  |- 2
1029      * |  |- 3
1030      * |- 4
1031      *
1032      */
1033
1034     ret = TreeView_DeleteAllItems(hTree);
1035     ok(ret == TRUE, "ret\n");
1036     ins.hParent = TVI_ROOT;
1037     ins.hInsertAfter = TVI_ROOT;
1038     U(ins).item.mask = TVIF_TEXT;
1039     U(ins).item.pszText = nodeText[0];
1040     node[0] = TreeView_InsertItem(hTree, &ins);
1041     assert(node[0]);
1042
1043     ins.hInsertAfter = TVI_LAST;
1044     U(ins).item.mask = TVIF_TEXT;
1045     ins.hParent = node[0];
1046
1047     U(ins).item.pszText = nodeText[1];
1048     node[1] = TreeView_InsertItem(hTree, &ins);
1049     assert(node[1]);
1050     U(ins).item.pszText = nodeText[4];
1051     node[4] = TreeView_InsertItem(hTree, &ins);
1052     assert(node[4]);
1053
1054     ins.hParent = node[1];
1055
1056     U(ins).item.pszText = nodeText[2];
1057     node[2] = TreeView_InsertItem(hTree, &ins);
1058     assert(node[2]);
1059     U(ins).item.pszText = nodeText[3];
1060     node[3] = TreeView_InsertItem(hTree, &ins);
1061     assert(node[3]);
1062
1063
1064     nodeVisible = TreeView_GetItemRect(hTree, node[1], &dummyRect, FALSE);
1065     ok(!nodeVisible, "Node 1 should not be visible.\n");
1066     nodeVisible = TreeView_GetItemRect(hTree, node[2], &dummyRect, FALSE);
1067     ok(!nodeVisible, "Node 2 should not be visible.\n");
1068     nodeVisible = TreeView_GetItemRect(hTree, node[3], &dummyRect, FALSE);
1069     ok(!nodeVisible, "Node 3 should not be visible.\n");
1070     nodeVisible = TreeView_GetItemRect(hTree, node[4], &dummyRect, FALSE);
1071     ok(!nodeVisible, "Node 4 should not be visible.\n");
1072
1073     ok(TreeView_Expand(hTree, node[1], TVE_EXPAND), "Expand of node 1 failed.\n");
1074
1075     nodeVisible = TreeView_GetItemRect(hTree, node[1], &dummyRect, FALSE);
1076     ok(!nodeVisible, "Node 1 should not be visible.\n");
1077     nodeVisible = TreeView_GetItemRect(hTree, node[2], &dummyRect, FALSE);
1078     ok(!nodeVisible, "Node 2 should not be visible.\n");
1079     nodeVisible = TreeView_GetItemRect(hTree, node[3], &dummyRect, FALSE);
1080     ok(!nodeVisible, "Node 3 should not be visible.\n");
1081     nodeVisible = TreeView_GetItemRect(hTree, node[4], &dummyRect, FALSE);
1082     ok(!nodeVisible, "Node 4 should not be visible.\n");
1083
1084     DestroyWindow(hTree);
1085 }
1086
1087 static void test_itemedit(void)
1088 {
1089     DWORD r;
1090     HWND edit;
1091     TVITEMA item;
1092     CHAR buff[2];
1093     HWND hTree;
1094
1095     hTree = create_treeview_control();
1096     fill_tree(hTree);
1097
1098     /* try with null item */
1099     edit = (HWND)SendMessage(hTree, TVM_EDITLABEL, 0, 0);
1100     ok(!IsWindow(edit), "Expected valid handle\n");
1101
1102     /* trigger edit */
1103     edit = (HWND)SendMessage(hTree, TVM_EDITLABEL, 0, (LPARAM)hRoot);
1104     ok(IsWindow(edit), "Expected valid handle\n");
1105     /* item shouldn't be selected automatically after TVM_EDITLABEL */
1106     r = SendMessage(hTree, TVM_GETITEMSTATE, (WPARAM)hRoot, TVIS_SELECTED);
1107     expect(0, r);
1108     /* try to cancel with wrong edit handle */
1109     r = SendMessage(hTree, WM_COMMAND, MAKEWPARAM(0, EN_KILLFOCUS), 0);
1110     expect(0, r);
1111     ok(IsWindow(edit), "Expected edit control to be valid\n");
1112     r = SendMessage(hTree, WM_COMMAND, MAKEWPARAM(0, EN_KILLFOCUS), (LPARAM)edit);
1113     expect(0, r);
1114     ok(!IsWindow(edit), "Expected edit control to be destroyed\n");
1115     /* try to cancel without creating edit */
1116     r = SendMessage(hTree, WM_COMMAND, MAKEWPARAM(0, EN_KILLFOCUS), 0);
1117     expect(0, r);
1118
1119     /* try to cancel with wrong (not null) handle */
1120     edit = (HWND)SendMessage(hTree, TVM_EDITLABEL, 0, (LPARAM)hRoot);
1121     ok(IsWindow(edit), "Expected valid handle\n");
1122     r = SendMessage(hTree, WM_COMMAND, MAKEWPARAM(0, EN_KILLFOCUS), (LPARAM)hTree);
1123     expect(0, r);
1124     ok(IsWindow(edit), "Expected edit control to be valid\n");
1125     r = SendMessage(hTree, WM_COMMAND, MAKEWPARAM(0, EN_KILLFOCUS), (LPARAM)edit);
1126     expect(0, r);
1127
1128     /* remove selection after starting edit */
1129     r = TreeView_SelectItem(hTree, hRoot);
1130     expect(TRUE, r);
1131     edit = (HWND)SendMessage(hTree, TVM_EDITLABEL, 0, (LPARAM)hRoot);
1132     ok(IsWindow(edit), "Expected valid handle\n");
1133     r = TreeView_SelectItem(hTree, NULL);
1134     expect(TRUE, r);
1135     /* alter text */
1136     strncpy(buff, "x", sizeof(buff)/sizeof(CHAR));
1137     r = SendMessage(edit, WM_SETTEXT, 0, (LPARAM)buff);
1138     expect(TRUE, r);
1139     r = SendMessage(hTree, WM_COMMAND, MAKEWPARAM(0, EN_KILLFOCUS), (LPARAM)edit);
1140     expect(0, r);
1141     ok(!IsWindow(edit), "Expected edit control to be destroyed\n");
1142     /* check that text is saved */
1143     item.mask = TVIF_TEXT;
1144     item.hItem = hRoot;
1145     item.pszText = buff;
1146     item.cchTextMax = sizeof(buff)/sizeof(CHAR);
1147     r = SendMessage(hTree, TVM_GETITEMA, 0, (LPARAM)&item);
1148     expect(TRUE, r);
1149     ok(!strcmp("x", buff), "Expected item text to change\n");
1150
1151     DestroyWindow(hTree);
1152 }
1153
1154 static void test_treeview_classinfo(void)
1155 {
1156     WNDCLASSA cls;
1157
1158     memset(&cls, 0, sizeof(cls));
1159     GetClassInfo(GetModuleHandleA("comctl32.dll"), WC_TREEVIEWA, &cls);
1160     ok(cls.hbrBackground == NULL, "Expected NULL background brush, got %p\n", cls.hbrBackground);
1161     ok(cls.style == (CS_GLOBALCLASS | CS_DBLCLKS), "Expected got %x\n", cls.style);
1162     expect(0, cls.cbClsExtra);
1163 }
1164
1165 static void test_get_linecolor(void)
1166 {
1167     COLORREF clr;
1168     HWND hTree;
1169
1170     hTree = create_treeview_control();
1171
1172     /* newly created control has default color */
1173     clr = (COLORREF)SendMessage(hTree, TVM_GETLINECOLOR, 0, 0);
1174     if (clr == 0)
1175         win_skip("TVM_GETLINECOLOR is not supported on comctl32 < 5.80\n");
1176     else
1177         expect(CLR_DEFAULT, clr);
1178
1179     DestroyWindow(hTree);
1180 }
1181
1182 static void test_get_insertmarkcolor(void)
1183 {
1184     COLORREF clr;
1185     HWND hTree;
1186
1187     hTree = create_treeview_control();
1188
1189     /* newly created control has default color */
1190     clr = (COLORREF)SendMessage(hTree, TVM_GETINSERTMARKCOLOR, 0, 0);
1191     if (clr == 0)
1192         win_skip("TVM_GETINSERTMARKCOLOR is not supported on comctl32 < 5.80\n");
1193     else
1194         expect(CLR_DEFAULT, clr);
1195
1196     DestroyWindow(hTree);
1197 }
1198
1199 static void test_expandnotify(void)
1200 {
1201     HWND hTree;
1202     BOOL ret;
1203     TVITEMA item;
1204
1205     hTree = create_treeview_control();
1206     fill_tree(hTree);
1207
1208     item.hItem = hRoot;
1209     item.mask = TVIF_STATE;
1210
1211     item.state = TVIS_EXPANDED;
1212     ret = SendMessageA(hTree, TVM_GETITEMA, 0, (LPARAM)&item);
1213     ok(ret == TRUE, "got %d\n", ret);
1214     ok((item.state & TVIS_EXPANDED) == 0, "expected collapsed\n");
1215
1216     /* preselect root node here */
1217     ret = SendMessageA(hTree, TVM_SELECTITEM, TVGN_CARET, (LPARAM)hRoot);
1218     ok(ret == TRUE, "got %d\n", ret);
1219
1220     g_get_from_expand = TRUE;
1221     /* expand */
1222     flush_sequences(sequences, NUM_MSG_SEQUENCES);
1223     g_item_expanding.state = 0xdeadbeef;
1224     g_item_expanded.state = 0xdeadbeef;
1225     ret = SendMessageA(hTree, TVM_EXPAND, TVE_EXPAND, (LPARAM)hRoot);
1226     ok(ret == TRUE, "got %d\n", ret);
1227     ok(g_item_expanding.state == TVIS_SELECTED, "got state on TVN_ITEMEXPANDING 0x%08x\n",
1228        g_item_expanding.state);
1229     ok(g_item_expanded.state == (TVIS_SELECTED|TVIS_EXPANDED), "got state on TVN_ITEMEXPANDED 0x%08x\n",
1230        g_item_expanded.state);
1231     ok_sequence(sequences, PARENT_SEQ_INDEX, parent_expand_seq, "expand notifications", FALSE);
1232     g_get_from_expand = FALSE;
1233
1234     /* check that it's expanded */
1235     item.state = TVIS_EXPANDED;
1236     ret = SendMessageA(hTree, TVM_GETITEMA, 0, (LPARAM)&item);
1237     ok(ret == TRUE, "got %d\n", ret);
1238     ok((item.state & TVIS_EXPANDED) == TVIS_EXPANDED, "expected expanded\n");
1239
1240     /* collapse */
1241     flush_sequences(sequences, NUM_MSG_SEQUENCES);
1242     ret = SendMessageA(hTree, TVM_EXPAND, TVE_COLLAPSE, (LPARAM)hRoot);
1243     ok(ret == TRUE, "got %d\n", ret);
1244     item.state = TVIS_EXPANDED;
1245     ret = SendMessageA(hTree, TVM_GETITEMA, 0, (LPARAM)&item);
1246     ok(ret == TRUE, "got %d\n", ret);
1247     ok((item.state & TVIS_EXPANDED) == 0, "expected collapsed\n");
1248     /* all next collapse/expand attempts won't produce any notifications,
1249        the only way is to reset with all children removed */
1250     ok_sequence(sequences, PARENT_SEQ_INDEX, empty_seq, "collapse after expand notifications", FALSE);
1251
1252     DestroyWindow(hTree);
1253
1254     /* test TVM_GETITEMRECT inside TVN_ITEMEXPANDED notification */
1255     hTree = create_treeview_control();
1256     fill_tree(hTree);
1257     g_get_rect_in_expand = TRUE;
1258     ret = TreeView_Select(hTree, hChild, TVGN_CARET);
1259     g_get_rect_in_expand = FALSE;
1260     ok(ret, "got %d\n", ret);
1261     DestroyWindow(hTree);
1262 }
1263
1264 static void test_expandedimage(void)
1265 {
1266     TVITEMEXA item;
1267     HWND hTree;
1268     BOOL ret;
1269
1270     hTree = create_treeview_control();
1271     fill_tree(hTree);
1272
1273     item.mask = TVIF_EXPANDEDIMAGE;
1274     item.iExpandedImage = 1;
1275     item.hItem = hRoot;
1276     ret = SendMessageA(hTree, TVM_SETITEMA, 0, (LPARAM)&item);
1277     ok(ret, "got %d\n", ret);
1278
1279     item.mask = TVIF_EXPANDEDIMAGE;
1280     item.iExpandedImage = -1;
1281     item.hItem = hRoot;
1282     ret = SendMessageA(hTree, TVM_GETITEMA, 0, (LPARAM)&item);
1283     ok(ret, "got %d\n", ret);
1284
1285     if (item.iExpandedImage != 1)
1286     {
1287         win_skip("TVIF_EXPANDEDIMAGE not supported\n");
1288         DestroyWindow(hTree);
1289         return;
1290     }
1291
1292     /* test for default iExpandedImage value */
1293     item.mask = TVIF_EXPANDEDIMAGE;
1294     item.iExpandedImage = -1;
1295     item.hItem = hChild;
1296     ret = SendMessageA(hTree, TVM_GETITEMA, 0, (LPARAM)&item);
1297     ok(ret, "got %d\n", ret);
1298     ok(item.iExpandedImage == (WORD)I_IMAGENONE, "got %d\n", item.iExpandedImage);
1299
1300     DestroyWindow(hTree);
1301 }
1302
1303 static void test_TVS_SINGLEEXPAND(void)
1304 {
1305     HWND hTree;
1306     BOOL ret;
1307
1308     hTree = create_treeview_control();
1309     SetWindowLongA(hTree, GWL_STYLE, GetWindowLong(hTree, GWL_STYLE) | TVS_SINGLEEXPAND);
1310     /* to avoid paiting related notifications */
1311     ShowWindow(hTree, SW_HIDE);
1312     fill_tree(hTree);
1313
1314     flush_sequences(sequences, NUM_MSG_SEQUENCES);
1315     ret = SendMessageA(hTree, TVM_SELECTITEM, TVGN_CARET, (LPARAM)hRoot);
1316     ok(ret, "got %d\n", ret);
1317     ok_sequence(sequences, PARENT_SEQ_INDEX, parent_singleexpand_seq, "singleexpand notifications", FALSE);
1318
1319     /* a workaround for NT4 that sends expanding notification when nothing is about to expand */
1320     ret = SendMessageA(hTree, TVM_DELETEITEM, 0, (LPARAM)hRoot);
1321     ok(ret, "got %d\n", ret);
1322     fill_tree(hTree);
1323     ret = SendMessageA(hTree, TVM_SELECTITEM, TVGN_CARET, 0);
1324     ok(ret, "got %d\n", ret);
1325
1326     DestroyWindow(hTree);
1327 }
1328
1329 static void test_WM_PAINT(void)
1330 {
1331     HWND hTree;
1332     COLORREF clr;
1333     LONG ret;
1334     RECT rc;
1335     HDC hdc;
1336
1337     hTree = create_treeview_control();
1338
1339     clr = SendMessageA(hTree, TVM_SETBKCOLOR, 0, RGB(255, 0, 0));
1340     ok(clr == -1, "got %d, expected -1\n", clr);
1341
1342     hdc = GetDC(hMainWnd);
1343
1344     GetClientRect(hMainWnd, &rc);
1345     FillRect(hdc, &rc, GetStockObject(BLACK_BRUSH));
1346
1347     clr = GetPixel(hdc, 1, 1);
1348     ok(clr == RGB(0, 0, 0), "got 0x%x\n", clr);
1349
1350     ret = SendMessageA(hTree, WM_PAINT, (WPARAM)hdc, 0);
1351     ok(ret == 0, "got %d\n", ret);
1352
1353     clr = GetPixel(hdc, 1, 1);
1354     ok(clr == RGB(255, 0, 0) || broken(clr == RGB(0, 0, 0)) /* win98 */,
1355         "got 0x%x\n", clr);
1356
1357     ReleaseDC(hMainWnd, hdc);
1358
1359     DestroyWindow(hTree);
1360 }
1361
1362 static void test_delete_items(void)
1363 {
1364     const struct message *msg;
1365     HWND hTree;
1366     INT ret;
1367
1368     hTree = create_treeview_control();
1369     fill_tree(hTree);
1370
1371     /* check delete order */
1372     flush_sequences(item_sequence, 1);
1373     ret = SendMessage(hTree, TVM_DELETEITEM, 0, 0);
1374     ok(ret == TRUE, "got %d\n", ret);
1375
1376     msg = item_sequence[0]->sequence;
1377     ok(item_sequence[0]->count == 2, "expected 2 items, got %d\n", item_sequence[0]->count);
1378
1379     if (item_sequence[0]->count == 2)
1380     {
1381       ok(msg[0].lParam == (LPARAM)hChild, "expected %p, got 0x%lx\n", hChild, msg[0].lParam);
1382       ok(msg[1].lParam == (LPARAM)hRoot, "expected %p, got 0x%lx\n", hRoot, msg[1].lParam);
1383     }
1384
1385     ret = SendMessageA(hTree, TVM_GETCOUNT, 0, 0);
1386     ok(ret == 0, "got %d\n", ret);
1387
1388     DestroyWindow(hTree);
1389 }
1390
1391 struct _ITEM_DATA
1392 {
1393     HTREEITEM  parent; /* for root value of parent field is unidetified */
1394     HTREEITEM  nextsibling;
1395     HTREEITEM  firstchild;
1396 };
1397
1398 static void _check_item(HTREEITEM item, HTREEITEM parent, HTREEITEM nextsibling, HTREEITEM firstchild, int line)
1399 {
1400     struct _ITEM_DATA *data = (struct _ITEM_DATA*)item;
1401
1402     ok_(__FILE__, line)(data->parent == parent, "parent %p, got %p\n", parent, data->parent);
1403     ok_(__FILE__, line)(data->nextsibling == nextsibling, "sibling %p, got %p\n", nextsibling, data->nextsibling);
1404     ok_(__FILE__, line)(data->firstchild == firstchild, "firstchild %p, got %p\n", firstchild, data->firstchild);
1405 }
1406
1407 #define check_item(a, b, c, d) _check_item(a, b, c, d, __LINE__)
1408
1409 static void test_htreeitem_layout(void)
1410 {
1411     TVINSERTSTRUCTA ins;
1412     HTREEITEM item1, item2;
1413     HWND hTree;
1414
1415     hTree = create_treeview_control();
1416     fill_tree(hTree);
1417
1418     /* root has some special pointer in parent field */
1419     check_item(hRoot, ((struct _ITEM_DATA*)hRoot)->parent, 0, hChild);
1420     check_item(hChild, hRoot, 0, 0);
1421
1422     ins.hParent = hChild;
1423     ins.hInsertAfter = TVI_FIRST;
1424     item1 = TreeView_InsertItem(hTree, &ins);
1425
1426     check_item(item1, hChild, 0, 0);
1427
1428     ins.hParent = hRoot;
1429     ins.hInsertAfter = TVI_FIRST;
1430     item2 = TreeView_InsertItem(hTree, &ins);
1431
1432     check_item(item2, hRoot, hChild, 0);
1433
1434     SendMessage(hTree, TVM_DELETEITEM, 0, (LPARAM)hChild);
1435
1436     /* without children now */
1437     check_item(hRoot, ((struct _ITEM_DATA*)hRoot)->parent, 0, item2);
1438
1439     DestroyWindow(hTree);
1440 }
1441
1442 START_TEST(treeview)
1443 {
1444     HMODULE hComctl32;
1445     BOOL (WINAPI *pInitCommonControlsEx)(const INITCOMMONCONTROLSEX*);
1446     WNDCLASSA wc;
1447     MSG msg;
1448
1449     ULONG_PTR ctx_cookie;
1450     HANDLE hCtx;
1451     HWND hwnd;
1452   
1453     hComctl32 = GetModuleHandleA("comctl32.dll");
1454     pInitCommonControlsEx = (void*)GetProcAddress(hComctl32, "InitCommonControlsEx");
1455     if (pInitCommonControlsEx)
1456     {
1457         INITCOMMONCONTROLSEX iccex;
1458         iccex.dwSize = sizeof(iccex);
1459         iccex.dwICC  = ICC_TREEVIEW_CLASSES;
1460         pInitCommonControlsEx(&iccex);
1461     }
1462     else
1463         InitCommonControls();
1464
1465     init_msg_sequences(sequences, NUM_MSG_SEQUENCES);
1466     init_msg_sequences(item_sequence, 1);
1467   
1468     wc.style = CS_HREDRAW | CS_VREDRAW;
1469     wc.cbClsExtra = 0;
1470     wc.cbWndExtra = 0;
1471     wc.hInstance = GetModuleHandleA(NULL);
1472     wc.hIcon = NULL;
1473     wc.hCursor = LoadCursorA(NULL, IDC_IBEAM);
1474     wc.hbrBackground = GetSysColorBrush(COLOR_WINDOW);
1475     wc.lpszMenuName = NULL;
1476     wc.lpszClassName = "MyTestWnd";
1477     wc.lpfnWndProc = parent_wnd_proc;
1478     RegisterClassA(&wc);
1479
1480     hMainWnd = CreateWindowExA(0, "MyTestWnd", "Blah", WS_OVERLAPPEDWINDOW,
1481       CW_USEDEFAULT, CW_USEDEFAULT, 130, 105, NULL, NULL, GetModuleHandleA(NULL), 0);
1482
1483     ok(hMainWnd != NULL, "Failed to create parent window. Tests aborted.\n");
1484     if (!hMainWnd) return;
1485
1486     test_fillroot();
1487     test_select();
1488     test_getitemtext();
1489     test_focus();
1490     test_get_set_bkcolor();
1491     test_get_set_imagelist();
1492     test_get_set_indent();
1493     test_get_set_insertmark();
1494     test_get_set_item();
1495     test_get_set_itemheight();
1496     test_get_set_scrolltime();
1497     test_get_set_textcolor();
1498     test_get_linecolor();
1499     test_get_insertmarkcolor();
1500     test_get_set_tooltips();
1501     test_get_set_unicodeformat();
1502     test_callback();
1503     test_expandinvisible();
1504     test_itemedit();
1505     test_treeview_classinfo();
1506     test_expandnotify();
1507     test_TVS_SINGLEEXPAND();
1508     test_WM_PAINT();
1509     test_delete_items();
1510     test_htreeitem_layout();
1511
1512     if (!load_v6_module(&ctx_cookie, &hCtx))
1513     {
1514         DestroyWindow(hMainWnd);
1515         return;
1516     }
1517
1518     /* this is a XP SP3 failure workaround */
1519     hwnd = CreateWindowExA(0, WC_TREEVIEW, "foo",
1520                            WS_CHILD | WS_BORDER | WS_VISIBLE,
1521                            0, 0, 100, 100,
1522                            hMainWnd, NULL, GetModuleHandleA(NULL), NULL);
1523     if (!IsWindow(hwnd))
1524     {
1525         win_skip("FIXME: failed to create TreeView window.\n");
1526         unload_v6_module(ctx_cookie, hCtx);
1527         DestroyWindow(hMainWnd);
1528         return;
1529     }
1530     else
1531         DestroyWindow(hwnd);
1532
1533     /* comctl32 version 6 tests start here */
1534     test_expandedimage();
1535     test_htreeitem_layout();
1536
1537     unload_v6_module(ctx_cookie, hCtx);
1538
1539     PostMessageA(hMainWnd, WM_CLOSE, 0, 0);
1540     while(GetMessageA(&msg, 0, 0, 0))
1541     {
1542         TranslateMessage(&msg);
1543         DispatchMessageA(&msg);
1544     }
1545 }