samlib: Add stubbed samlib.dll.
[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 "msg.h"
34
35 const char *TEST_CALLBACK_TEXT = "callback_text";
36
37 #define NUM_MSG_SEQUENCES   1
38 #define TREEVIEW_SEQ_INDEX  0
39
40 #define expect(expected, got) ok(got == expected, "Expected %d, got %d\n", expected, got)
41
42 static struct msg_sequence *MsgSequences[NUM_MSG_SEQUENCES];
43
44 static const struct message FillRootSeq[] = {
45     { TVM_INSERTITEM, sent },
46     { TVM_INSERTITEM, sent },
47     { 0 }
48 };
49
50 static const struct message rootnone_select_seq[] = {
51     { TVM_SELECTITEM, sent|wparam, 9 },
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     { 0 }
58 };
59
60 static const struct message rootchild_select_seq[] = {
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     { TVM_SELECTITEM, sent|wparam, 9 },
66     { TVM_SELECTITEM, sent|wparam, 9 },
67     { 0 }
68 };
69
70 static const struct message getitemtext_seq[] = {
71     { TVM_INSERTITEM, sent },
72     { TVM_GETITEM, sent },
73     { TVM_DELETEITEM, sent },
74     { 0 }
75 };
76
77 static const struct message focus_seq[] = {
78     { TVM_INSERTITEM, sent },
79     { TVM_INSERTITEM, sent },
80     { TVM_SELECTITEM, sent|wparam, 9 },
81     /* The following end up out of order in wine */
82     { WM_WINDOWPOSCHANGING, sent|defwinproc },
83     { WM_NCCALCSIZE, sent|wparam|defwinproc, TRUE },
84     { WM_WINDOWPOSCHANGED, sent|defwinproc },
85     { WM_SIZE, sent|defwinproc },
86     { WM_PAINT, sent|defwinproc },
87     { WM_NCPAINT, sent|wparam|defwinproc, 1 },
88     { WM_ERASEBKGND, sent|defwinproc },
89     { TVM_EDITLABEL, sent },
90     { WM_COMMAND, sent|wparam|defwinproc, MAKEWPARAM(0, EN_UPDATE) },
91     { WM_COMMAND, sent|wparam|defwinproc, MAKEWPARAM(0, EN_CHANGE) },
92     { WM_PARENTNOTIFY, sent|wparam|defwinproc, MAKEWPARAM(WM_CREATE, 0) },
93     { WM_KILLFOCUS, sent|defwinproc },
94     { WM_PAINT, sent|defwinproc },
95     { WM_IME_SETCONTEXT, sent|defwinproc|optional },
96     { WM_COMMAND, sent|wparam|defwinproc, MAKEWPARAM(0, EN_SETFOCUS) },
97     { WM_ERASEBKGND, sent|defwinproc|optional },
98     { WM_CTLCOLOREDIT, sent|defwinproc|optional },
99     { WM_CTLCOLOREDIT, sent|defwinproc|optional },
100     { 0 }
101 };
102
103 static const struct message test_get_set_bkcolor_seq[] = {
104     { TVM_GETBKCOLOR, sent|wparam|lparam, 0, 0 },
105     { TVM_SETBKCOLOR, sent|wparam|lparam, 0, 0 },
106     { TVM_GETBKCOLOR, sent|wparam|lparam, 0, 0 },
107     { TVM_SETBKCOLOR, sent|wparam|lparam, 0, 0x00ffffff },
108     { TVM_GETBKCOLOR, sent|wparam|lparam, 0, 0 },
109     { TVM_SETBKCOLOR, sent|wparam|lparam, 0, -1 },
110     { 0 }
111 };
112
113 static const struct message test_get_set_imagelist_seq[] = {
114     { TVM_SETIMAGELIST, sent|wparam|lparam, 0, 0 },
115     { TVM_GETIMAGELIST, sent|wparam|lparam, 0, 0 },
116     { 0 }
117 };
118
119 static const struct message test_get_set_indent_seq[] = {
120     { TVM_SETINDENT, sent|wparam|lparam, 0, 0 },
121     { TVM_GETINDENT, sent|wparam|lparam, 0, 0 },
122     /* The actual amount to indent is dependent on the system for this message */
123     { TVM_SETINDENT, sent },
124     { TVM_GETINDENT, sent|wparam|lparam, 0, 0 },
125     { 0 }
126 };
127
128 static const struct message test_get_set_insertmarkcolor_seq[] = {
129     { TVM_SETINSERTMARKCOLOR, sent|wparam|lparam, 0, 0 },
130     { TVM_GETINSERTMARKCOLOR, sent|wparam|lparam, 0, 0 },
131     { 0 }
132 };
133
134 static const struct message test_get_set_item_seq[] = {
135     { TVM_GETITEM, sent },
136     { TVM_SETITEM, sent },
137     { TVM_GETITEM, sent },
138     { TVM_SETITEM, sent },
139     { 0 }
140 };
141
142 static const struct message test_get_set_itemheight_seq[] = {
143     { TVM_GETITEMHEIGHT, sent|wparam|lparam, 0, 0 },
144     { TVM_SETITEMHEIGHT, sent|wparam|lparam, -1, 0 },
145     { TVM_GETITEMHEIGHT, sent|wparam|lparam, 0, 0 },
146     { TVM_SETITEMHEIGHT, sent|lparam, 0xcccccccc, 0 },
147     { TVM_GETITEMHEIGHT, sent|wparam|lparam|optional, 0, 0 },
148     { TVM_SETITEMHEIGHT, sent|wparam|lparam|optional, 9, 0 },
149     { TVM_GETITEMHEIGHT, sent|wparam|lparam, 0, 0 },
150     { 0 }
151 };
152
153 static const struct message test_get_set_scrolltime_seq[] = {
154     { TVM_SETSCROLLTIME, sent|wparam|lparam, 20, 0 },
155     { TVM_GETSCROLLTIME, sent|wparam|lparam, 0, 0 },
156     { 0 }
157 };
158
159 static const struct message test_get_set_textcolor_seq[] = {
160     { TVM_GETTEXTCOLOR, sent|wparam|lparam, 0, 0 },
161     { TVM_SETTEXTCOLOR, sent|wparam|lparam, 0, 0 },
162     { TVM_GETTEXTCOLOR, sent|wparam|lparam, 0, 0 },
163     { TVM_SETTEXTCOLOR, sent|wparam|lparam, 0, RGB(255, 255, 255) },
164     { TVM_GETTEXTCOLOR, sent|wparam|lparam, 0, 0 },
165     { TVM_SETTEXTCOLOR, sent|wparam|lparam, 0, CLR_NONE },
166     { 0 }
167 };
168
169 static const struct message test_get_set_tooltips_seq[] = {
170     { WM_KILLFOCUS,    sent },
171     { WM_IME_SETCONTEXT, sent|optional },
172     { WM_IME_NOTIFY, sent|optional },
173     { TVM_SETTOOLTIPS, sent|wparam|lparam, 0, 0 },
174     { TVM_GETTOOLTIPS, sent|wparam|lparam, 0, 0 },
175     { 0 }
176 };
177
178 static const struct message test_get_set_unicodeformat_seq[] = {
179     { TVM_SETUNICODEFORMAT, sent|wparam|lparam, TRUE, 0 },
180     { TVM_GETUNICODEFORMAT, sent|wparam|lparam, 0, 0 },
181     { TVM_SETUNICODEFORMAT, sent|wparam|lparam, 0, 0 },
182     { TVM_GETUNICODEFORMAT, sent|wparam|lparam, 0, 0 },
183     { TVM_SETUNICODEFORMAT, sent|wparam|lparam, 0, 0 },
184     { 0 }
185 };
186
187 static HWND hMainWnd;
188
189 static HTREEITEM hRoot, hChild;
190
191 static int pos = 0;
192 static char sequence[256];
193
194 static void Clear(void)
195 {
196     pos = 0;
197     sequence[0] = '\0';
198 }
199
200 static void AddItem(char ch)
201 {
202     sequence[pos++] = ch;
203     sequence[pos] = '\0';
204 }
205
206 static void IdentifyItem(HTREEITEM hItem)
207 {
208     if (hItem == hRoot) {
209         AddItem('R');
210         return;
211     }
212     if (hItem == hChild) {
213         AddItem('C');
214         return;
215     }
216     if (hItem == NULL) {
217         AddItem('n');
218         return;
219     }
220     AddItem('?');
221 }
222
223 /* This function hooks in and records all messages to the treeview control */
224 static LRESULT WINAPI TreeviewWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
225 {
226     static LONG defwndproc_counter = 0;
227     LRESULT ret;
228     struct message msg;
229     WNDPROC lpOldProc = (WNDPROC)GetWindowLongPtrA(hwnd, GWLP_USERDATA);
230
231     msg.message = message;
232     msg.flags = sent|wparam|lparam;
233     if (defwndproc_counter) msg.flags |= defwinproc;
234     msg.wParam = wParam;
235     msg.lParam = lParam;
236     add_message(MsgSequences, TREEVIEW_SEQ_INDEX, &msg);
237
238     defwndproc_counter++;
239     ret = CallWindowProcA(lpOldProc, hwnd, message, wParam, lParam);
240     defwndproc_counter--;
241
242     return ret;
243 }
244
245 static HWND create_treeview_control(void)
246 {
247     WNDPROC pOldWndProc;
248     HWND hTree;
249
250     hTree = CreateWindowExA(WS_EX_CLIENTEDGE, WC_TREEVIEWA, NULL, WS_CHILD|WS_VISIBLE|
251             TVS_LINESATROOT|TVS_HASLINES|TVS_HASBUTTONS|TVS_EDITLABELS,
252             0, 0, 120, 100, hMainWnd, (HMENU)100, GetModuleHandleA(0), 0);
253
254     SetFocus(hTree);
255
256     /* Record the old WNDPROC so we can call it after recording the messages */
257     pOldWndProc = (WNDPROC)SetWindowLongPtrA(hTree, GWLP_WNDPROC, (LONG_PTR)TreeviewWndProc);
258     SetWindowLongPtrA(hTree, GWLP_USERDATA, (LONG_PTR)pOldWndProc);
259
260     return hTree;
261 }
262
263 static void fill_tree(HWND hTree)
264 {
265     TVINSERTSTRUCTA ins;
266     static CHAR root[]  = "Root",
267                 child[] = "Child";
268
269     ins.hParent = TVI_ROOT;
270     ins.hInsertAfter = TVI_ROOT;
271     U(ins).item.mask = TVIF_TEXT;
272     U(ins).item.pszText = root;
273     hRoot = TreeView_InsertItem(hTree, &ins);
274
275     ins.hParent = hRoot;
276     ins.hInsertAfter = TVI_FIRST;
277     U(ins).item.mask = TVIF_TEXT;
278     U(ins).item.pszText = child;
279     hChild = TreeView_InsertItem(hTree, &ins);
280 }
281
282 static void test_fillroot(void)
283 {
284     TVITEM tvi;
285     HWND hTree;
286
287     hTree = create_treeview_control();
288
289     flush_sequences(MsgSequences, NUM_MSG_SEQUENCES);
290
291     fill_tree(hTree);
292
293     Clear();
294     AddItem('A');
295     assert(hRoot);
296     AddItem('B');
297     assert(hChild);
298     AddItem('.');
299     ok_sequence(MsgSequences, TREEVIEW_SEQ_INDEX, FillRootSeq, "FillRoot", FALSE);
300     ok(!strcmp(sequence, "AB."), "Item creation\n");
301
302     /* UMLPad 1.15 depends on this being not -1 (I_IMAGECALLBACK) */
303     tvi.hItem = hRoot;
304     tvi.mask = TVIF_IMAGE | TVIF_SELECTEDIMAGE;
305     SendMessage( hTree, TVM_GETITEM, 0, (LPARAM)&tvi );
306     ok(tvi.iImage == 0, "tvi.iImage=%d\n", tvi.iImage);
307     ok(tvi.iSelectedImage == 0, "tvi.iSelectedImage=%d\n", tvi.iSelectedImage);
308
309     DestroyWindow(hTree);
310 }
311
312 static void test_callback(void)
313 {
314     HTREEITEM hRoot;
315     HTREEITEM hItem1, hItem2;
316     TVINSERTSTRUCTA ins;
317     TVITEM tvi;
318     CHAR test_string[] = "Test_string";
319     CHAR buf[128];
320     LRESULT ret;
321     HWND hTree;
322
323     hTree = create_treeview_control();
324     fill_tree(hTree);
325
326     ret = TreeView_DeleteAllItems(hTree);
327     ok(ret == TRUE, "ret\n");
328     ins.hParent = TVI_ROOT;
329     ins.hInsertAfter = TVI_ROOT;
330     U(ins).item.mask = TVIF_TEXT;
331     U(ins).item.pszText = LPSTR_TEXTCALLBACK;
332     hRoot = TreeView_InsertItem(hTree, &ins);
333     assert(hRoot);
334
335     tvi.hItem = hRoot;
336     tvi.mask = TVIF_TEXT;
337     tvi.pszText = buf;
338     tvi.cchTextMax = sizeof(buf)/sizeof(buf[0]);
339     ret = TreeView_GetItem(hTree, &tvi);
340     ok(ret == 1, "ret\n");
341     ok(strcmp(tvi.pszText, TEST_CALLBACK_TEXT) == 0, "Callback item text mismatch %s vs %s\n",
342         tvi.pszText, TEST_CALLBACK_TEXT);
343
344     ins.hParent = hRoot;
345     ins.hInsertAfter = TVI_FIRST;
346     U(ins).item.mask = TVIF_TEXT;
347     U(ins).item.pszText = test_string;
348     hItem1 = TreeView_InsertItem(hTree, &ins);
349     assert(hItem1);
350
351     tvi.hItem = hItem1;
352     ret = TreeView_GetItem(hTree, &tvi);
353     ok(ret == TRUE, "ret\n");
354     ok(strcmp(tvi.pszText, test_string) == 0, "Item text mismatch %s vs %s\n",
355         tvi.pszText, test_string);
356
357     /* undocumented: pszText of NULL also means LPSTR_CALLBACK: */
358     tvi.pszText = NULL;
359     ret = TreeView_SetItem(hTree, &tvi);
360     ok(ret == 1, "Expected SetItem return 1, got %ld\n", ret);
361     tvi.pszText = buf;
362     ret = TreeView_GetItem(hTree, &tvi);
363     ok(ret == TRUE, "Expected GetItem return TRUE, got %ld\n", ret);
364     ok(strcmp(tvi.pszText, TEST_CALLBACK_TEXT) == 0, "Item text mismatch %s vs %s\n",
365         tvi.pszText, TEST_CALLBACK_TEXT);
366
367     U(ins).item.pszText = NULL;
368     hItem2 = TreeView_InsertItem(hTree, &ins);
369     assert(hItem2);
370     tvi.hItem = hItem2;
371     memset(buf, 0, sizeof(buf));
372     ret = TreeView_GetItem(hTree, &tvi);
373     ok(ret == TRUE, "Expected GetItem return TRUE, got %ld\n", ret);
374     ok(strcmp(tvi.pszText, TEST_CALLBACK_TEXT) == 0, "Item text mismatch %s vs %s\n",
375         tvi.pszText, TEST_CALLBACK_TEXT);
376
377     DestroyWindow(hTree);
378 }
379
380 static void test_select(void)
381 {
382     BOOL r;
383     HWND hTree;
384
385     hTree = create_treeview_control();
386     fill_tree(hTree);
387
388     /* root-none select tests */
389     flush_sequences(MsgSequences, NUM_MSG_SEQUENCES);
390     r = TreeView_SelectItem(hTree, NULL);
391     expect(TRUE, r);
392     Clear();
393     AddItem('1');
394     r = TreeView_SelectItem(hTree, hRoot);
395     expect(TRUE, r);
396     AddItem('2');
397     r = TreeView_SelectItem(hTree, hRoot);
398     expect(TRUE, r);
399     AddItem('3');
400     r = TreeView_SelectItem(hTree, NULL);
401     expect(TRUE, r);
402     AddItem('4');
403     r = TreeView_SelectItem(hTree, NULL);
404     expect(TRUE, r);
405     AddItem('5');
406     r = TreeView_SelectItem(hTree, hRoot);
407     expect(TRUE, r);
408     AddItem('.');
409     ok(!strcmp(sequence, "1(nR)nR23(Rn)Rn45(nR)nR."), "root-none select test\n");
410     ok_sequence(MsgSequences, TREEVIEW_SEQ_INDEX, rootnone_select_seq,
411                 "root-none select seq", FALSE);
412
413     /* root-child select tests */
414     flush_sequences(MsgSequences, NUM_MSG_SEQUENCES);
415     r = TreeView_SelectItem(hTree, NULL);
416     expect(TRUE, r);
417
418     Clear();
419     AddItem('1');
420     r = TreeView_SelectItem(hTree, hRoot);
421     expect(TRUE, r);
422     AddItem('2');
423     r = TreeView_SelectItem(hTree, hRoot);
424     expect(TRUE, r);
425     AddItem('3');
426     r = TreeView_SelectItem(hTree, hChild);
427     expect(TRUE, r);
428     AddItem('4');
429     r = TreeView_SelectItem(hTree, hChild);
430     expect(TRUE, r);
431     AddItem('5');
432     r = TreeView_SelectItem(hTree, hRoot);
433     expect(TRUE, r);
434     AddItem('.');
435     ok(!strcmp(sequence, "1(nR)nR23(RC)RC45(CR)CR."), "root-child select test\n");
436     ok_sequence(MsgSequences, TREEVIEW_SEQ_INDEX, rootchild_select_seq,
437                 "root-child select seq", FALSE);
438
439     DestroyWindow(hTree);
440 }
441
442 static void test_getitemtext(void)
443 {
444     TVINSERTSTRUCTA ins;
445     HTREEITEM hChild;
446     TVITEM tvi;
447     HWND hTree;
448
449     CHAR szBuffer[80] = "Blah";
450     int nBufferSize = sizeof(szBuffer)/sizeof(CHAR);
451
452     hTree = create_treeview_control();
453     fill_tree(hTree);
454
455     flush_sequences(MsgSequences, NUM_MSG_SEQUENCES);
456
457     /* add an item without TVIF_TEXT mask and pszText == NULL */
458     ins.hParent = hRoot;
459     ins.hInsertAfter = TVI_ROOT;
460     U(ins).item.mask = 0;
461     U(ins).item.pszText = NULL;
462     U(ins).item.cchTextMax = 0;
463     hChild = TreeView_InsertItem(hTree, &ins);
464     assert(hChild);
465
466     /* retrieve it with TVIF_TEXT mask */
467     tvi.hItem = hChild;
468     tvi.mask = TVIF_TEXT;
469     tvi.cchTextMax = nBufferSize;
470     tvi.pszText = szBuffer;
471
472     SendMessageA( hTree, TVM_GETITEM, 0, (LPARAM)&tvi );
473     ok(!strcmp(szBuffer, ""), "szBuffer=\"%s\", expected \"\"\n", szBuffer);
474     ok(SendMessageA(hTree, TVM_DELETEITEM, 0, (LPARAM)hChild), "DeleteItem failed\n");
475     ok_sequence(MsgSequences, TREEVIEW_SEQ_INDEX, getitemtext_seq, "get item text seq", FALSE);
476
477     DestroyWindow(hTree);
478 }
479
480 static void test_focus(void)
481 {
482     TVINSERTSTRUCTA ins;
483     static CHAR child1[]  = "Edit",
484                 child2[]  = "A really long string";
485     HTREEITEM hChild1, hChild2;
486     HWND hTree;
487     HWND hEdit;
488
489     hTree = create_treeview_control();
490     fill_tree(hTree);
491
492     flush_sequences(MsgSequences, NUM_MSG_SEQUENCES);
493
494     /* This test verifies that when a label is being edited, scrolling
495      * the treeview does not cause the label to lose focus. To test
496      * this, first some additional entries are added to generate
497      * scrollbars.
498      */
499     ins.hParent = hRoot;
500     ins.hInsertAfter = hChild;
501     U(ins).item.mask = TVIF_TEXT;
502     U(ins).item.pszText = child1;
503     hChild1 = TreeView_InsertItem(hTree, &ins);
504     assert(hChild1);
505     ins.hInsertAfter = hChild1;
506     U(ins).item.mask = TVIF_TEXT;
507     U(ins).item.pszText = child2;
508     hChild2 = TreeView_InsertItem(hTree, &ins);
509     assert(hChild2);
510
511     ShowWindow(hMainWnd,SW_SHOW);
512     SendMessageA(hTree, TVM_SELECTITEM, TVGN_CARET, (LPARAM)hChild);
513     hEdit = TreeView_EditLabel(hTree, hChild);
514     ScrollWindowEx(hTree, -10, 0, NULL, NULL, NULL, NULL, SW_SCROLLCHILDREN);
515     ok(GetFocus() == hEdit, "Edit control should have focus\n");
516     ok_sequence(MsgSequences, TREEVIEW_SEQ_INDEX, focus_seq, "focus test", TRUE);
517
518     DestroyWindow(hTree);
519 }
520
521 static void test_get_set_bkcolor(void)
522 {
523     COLORREF crColor = RGB(0,0,0);
524     HWND hTree;
525
526     hTree = create_treeview_control();
527     fill_tree(hTree);
528
529     flush_sequences(MsgSequences, NUM_MSG_SEQUENCES);
530
531     /* If the value is -1, the control is using the system color for the background color. */
532     crColor = (COLORREF)SendMessage( hTree, TVM_GETBKCOLOR, 0, 0 );
533     ok(crColor == -1, "Default background color reported as 0x%.8x\n", crColor);
534
535     /* Test for black background */
536     SendMessage( hTree, TVM_SETBKCOLOR, 0, RGB(0,0,0) );
537     crColor = (COLORREF)SendMessage( hTree, TVM_GETBKCOLOR, 0, 0 );
538     ok(crColor == RGB(0,0,0), "Black background color reported as 0x%.8x\n", crColor);
539
540     /* Test for white background */
541     SendMessage( hTree, TVM_SETBKCOLOR, 0, RGB(255,255,255) );
542     crColor = (COLORREF)SendMessage( hTree, TVM_GETBKCOLOR, 0, 0 );
543     ok(crColor == RGB(255,255,255), "White background color reported as 0x%.8x\n", crColor);
544
545     /* Reset the default background */
546     SendMessage( hTree, TVM_SETBKCOLOR, 0, -1 );
547
548     ok_sequence(MsgSequences, TREEVIEW_SEQ_INDEX, test_get_set_bkcolor_seq,
549         "test get set bkcolor", FALSE);
550
551     DestroyWindow(hTree);
552 }
553
554 static void test_get_set_imagelist(void)
555 {
556     HIMAGELIST hImageList = NULL;
557     HWND hTree;
558
559     hTree = create_treeview_control();
560     fill_tree(hTree);
561
562     flush_sequences(MsgSequences, NUM_MSG_SEQUENCES);
563
564     /* Test a NULL HIMAGELIST */
565     SendMessage( hTree, TVM_SETIMAGELIST, TVSIL_NORMAL, (LPARAM)hImageList );
566     hImageList = (HIMAGELIST)SendMessage( hTree, TVM_GETIMAGELIST, TVSIL_NORMAL, 0 );
567     ok(hImageList == NULL, "NULL image list, reported as 0x%p, expected 0.\n", hImageList);
568
569     /* TODO: Test an actual image list */
570
571     ok_sequence(MsgSequences, TREEVIEW_SEQ_INDEX, test_get_set_imagelist_seq,
572         "test get imagelist", FALSE);
573
574     DestroyWindow(hTree);
575 }
576
577 static void test_get_set_indent(void)
578 {
579     int ulIndent = -1;
580     int ulMinIndent = -1;
581     int ulMoreThanTwiceMin = -1;
582     HWND hTree;
583
584     hTree = create_treeview_control();
585     fill_tree(hTree);
586
587     flush_sequences(MsgSequences, NUM_MSG_SEQUENCES);
588
589     /* Finding the minimum indent */
590     SendMessage( hTree, TVM_SETINDENT, 0, 0 );
591     ulMinIndent = (int)SendMessage( hTree, TVM_GETINDENT, 0, 0 );
592
593     /* Checking an indent that is more than twice the default indent */
594     ulMoreThanTwiceMin = 2*ulMinIndent+1;
595     SendMessage( hTree, TVM_SETINDENT, ulMoreThanTwiceMin, 0 );
596     ulIndent = (DWORD)SendMessage( hTree, TVM_GETINDENT, 0, 0 );
597     ok(ulIndent == ulMoreThanTwiceMin, "Indent reported as %d, expected %d\n", ulIndent, ulMoreThanTwiceMin);
598
599     ok_sequence(MsgSequences, TREEVIEW_SEQ_INDEX, test_get_set_indent_seq,
600         "test get set indent", FALSE);
601
602     DestroyWindow(hTree);
603 }
604
605 static void test_get_set_insertmark(void)
606 {
607     COLORREF crColor = RGB(0,0,0);
608     HWND hTree;
609
610     hTree = create_treeview_control();
611     fill_tree(hTree);
612
613     flush_sequences(MsgSequences, NUM_MSG_SEQUENCES);
614
615     SendMessage( hTree, TVM_SETINSERTMARKCOLOR, 0, crColor );
616     crColor = (COLORREF)SendMessage( hTree, TVM_GETINSERTMARKCOLOR, 0, 0 );
617     ok(crColor == RGB(0,0,0), "Insert mark color reported as 0x%.8x, expected 0x00000000\n", crColor);
618
619     ok_sequence(MsgSequences, TREEVIEW_SEQ_INDEX, test_get_set_insertmarkcolor_seq,
620         "test get set insertmark color", FALSE);
621
622     DestroyWindow(hTree);
623 }
624
625 static void test_get_set_item(void)
626 {
627     TVITEM tviRoot = {0};
628     int nBufferSize = 80;
629     char szBuffer[80] = {0};
630     HWND hTree;
631
632     hTree = create_treeview_control();
633     fill_tree(hTree);
634
635     flush_sequences(MsgSequences, NUM_MSG_SEQUENCES);
636
637     /* Test the root item */
638     tviRoot.hItem = hRoot;
639     tviRoot.mask = TVIF_TEXT;
640     tviRoot.cchTextMax = nBufferSize;
641     tviRoot.pszText = szBuffer;
642     SendMessage( hTree, TVM_GETITEM, 0, (LPARAM)&tviRoot );
643     ok(!strcmp("Root", szBuffer), "GetItem: szBuffer=\"%s\", expected \"Root\"\n", szBuffer);
644
645     /* Change the root text */
646     strncpy(szBuffer, "Testing123", nBufferSize);
647     SendMessage( hTree, TVM_SETITEM, 0, (LPARAM)&tviRoot );
648     memset(szBuffer, 0, nBufferSize);
649     SendMessage( hTree, TVM_GETITEM, 0, (LPARAM)&tviRoot );
650     ok(!strcmp("Testing123", szBuffer), "GetItem: szBuffer=\"%s\", expected \"Testing123\"\n", szBuffer);
651
652     /* Reset the root text */
653     memset(szBuffer, 0, nBufferSize);
654     strncpy(szBuffer, "Root", nBufferSize);
655     SendMessage( hTree, TVM_SETITEM, 0, (LPARAM)&tviRoot );
656
657     ok_sequence(MsgSequences, TREEVIEW_SEQ_INDEX, test_get_set_item_seq,
658         "test get set item", FALSE);
659
660     DestroyWindow(hTree);
661 }
662
663 static void test_get_set_itemheight(void)
664 {
665     int ulOldHeight = 0;
666     int ulNewHeight = 0;
667     HWND hTree;
668
669     hTree = create_treeview_control();
670     fill_tree(hTree);
671
672     flush_sequences(MsgSequences, NUM_MSG_SEQUENCES);
673
674     /* Assuming default height to begin with */
675     ulOldHeight = (int) SendMessage( hTree, TVM_GETITEMHEIGHT, 0, 0 );
676
677     /* Explicitly setting and getting the default height */
678     SendMessage( hTree, TVM_SETITEMHEIGHT, -1, 0 );
679     ulNewHeight = (int) SendMessage( hTree, TVM_GETITEMHEIGHT, 0, 0 );
680     ok(ulNewHeight == ulOldHeight, "Default height not set properly, reported %d, expected %d\n", ulNewHeight, ulOldHeight);
681
682     /* Explicitly setting and getting the height of twice the normal */
683     SendMessage( hTree, TVM_SETITEMHEIGHT, 2*ulOldHeight, 0 );
684     ulNewHeight = (int) SendMessage( hTree, TVM_GETITEMHEIGHT, 0, 0 );
685     ok(ulNewHeight == 2*ulOldHeight, "New height not set properly, reported %d, expected %d\n", ulNewHeight, 2*ulOldHeight);
686
687     /* Assuming tree doesn't have TVS_NONEVENHEIGHT set, so a set of 9 will round down to 8 */
688     SendMessage( hTree, TVM_SETITEMHEIGHT, 9, 0 );
689     ulNewHeight = (int) SendMessage( hTree, TVM_GETITEMHEIGHT, 0, 0 );
690     ok(ulNewHeight == 8, "Uneven height not set properly, reported %d, expected %d\n", ulNewHeight, 8);
691
692     ok_sequence(MsgSequences, TREEVIEW_SEQ_INDEX, test_get_set_itemheight_seq,
693         "test get set item height", FALSE);
694
695     DestroyWindow(hTree);
696 }
697
698 static void test_get_set_scrolltime(void)
699 {
700     int ulExpectedTime = 20;
701     int ulTime = 0;
702     HWND hTree;
703
704     hTree = create_treeview_control();
705     fill_tree(hTree);
706
707     flush_sequences(MsgSequences, NUM_MSG_SEQUENCES);
708
709     SendMessage( hTree, TVM_SETSCROLLTIME, ulExpectedTime, 0 );
710     ulTime = (int)SendMessage( hTree, TVM_GETSCROLLTIME, 0, 0 );
711     ok(ulTime == ulExpectedTime, "Scroll time reported as %d, expected %d\n", ulTime, ulExpectedTime);
712
713     ok_sequence(MsgSequences, TREEVIEW_SEQ_INDEX, test_get_set_scrolltime_seq,
714         "test get set scroll time", FALSE);
715
716     DestroyWindow(hTree);
717 }
718
719 static void test_get_set_textcolor(void)
720 {
721     /* If the value is -1, the control is using the system color for the text color. */
722     COLORREF crColor = RGB(0,0,0);
723     HWND hTree;
724
725     hTree = create_treeview_control();
726     fill_tree(hTree);
727
728     flush_sequences(MsgSequences, NUM_MSG_SEQUENCES);
729
730     crColor = (COLORREF)SendMessage( hTree, TVM_GETTEXTCOLOR, 0, 0 );
731     ok(crColor == -1, "Default text color reported as 0x%.8x\n", crColor);
732
733     /* Test for black text */
734     SendMessage( hTree, TVM_SETTEXTCOLOR, 0, RGB(0,0,0) );
735     crColor = (COLORREF)SendMessage( hTree, TVM_GETTEXTCOLOR, 0, 0 );
736     ok(crColor == RGB(0,0,0), "Black text color reported as 0x%.8x\n", crColor);
737
738     /* Test for white text */
739     SendMessage( hTree, TVM_SETTEXTCOLOR, 0, RGB(255,255,255) );
740     crColor = (COLORREF)SendMessage( hTree, TVM_GETTEXTCOLOR, 0, 0 );
741     ok(crColor == RGB(255,255,255), "White text color reported as 0x%.8x\n", crColor);
742
743     /* Reset the default text color */
744     SendMessage( hTree, TVM_SETTEXTCOLOR, 0, CLR_NONE );
745
746     ok_sequence(MsgSequences, TREEVIEW_SEQ_INDEX, test_get_set_textcolor_seq,
747         "test get set text color", FALSE);
748
749     DestroyWindow(hTree);
750 }
751
752 static void test_get_set_tooltips(void)
753 {
754     HWND hwndLastToolTip = NULL;
755     HWND hPopupTreeView;
756     HWND hTree;
757
758     hTree = create_treeview_control();
759     fill_tree(hTree);
760
761     flush_sequences(MsgSequences, NUM_MSG_SEQUENCES);
762
763     /* show even WS_POPUP treeview don't send NM_TOOLTIPSCREATED */
764     hPopupTreeView = CreateWindow(WC_TREEVIEW, NULL, WS_POPUP|WS_VISIBLE, 0, 0, 100, 100, hMainWnd, NULL, NULL, NULL);
765     DestroyWindow(hPopupTreeView);
766
767     /* Testing setting a NULL ToolTip */
768     SendMessage( hTree, TVM_SETTOOLTIPS, 0, 0 );
769     hwndLastToolTip = (HWND)SendMessage( hTree, TVM_GETTOOLTIPS, 0, 0 );
770     ok(hwndLastToolTip == NULL, "NULL tool tip, reported as 0x%p, expected 0.\n", hwndLastToolTip);
771
772     ok_sequence(MsgSequences, TREEVIEW_SEQ_INDEX, test_get_set_tooltips_seq,
773         "test get set tooltips", TRUE);
774
775     /* TODO: Add a test of an actual tooltip */
776     DestroyWindow(hTree);
777 }
778
779 static void test_get_set_unicodeformat(void)
780 {
781     BOOL bPreviousSetting = 0;
782     BOOL bNewSetting = 0;
783     HWND hTree;
784
785     hTree = create_treeview_control();
786     fill_tree(hTree);
787
788     flush_sequences(MsgSequences, NUM_MSG_SEQUENCES);
789
790     /* Set to Unicode */
791     bPreviousSetting = (BOOL)SendMessage( hTree, TVM_SETUNICODEFORMAT, 1, 0 );
792     bNewSetting = (BOOL)SendMessage( hTree, TVM_GETUNICODEFORMAT, 0, 0 );
793     ok(bNewSetting == 1, "Unicode setting did not work.\n");
794
795     /* Set to ANSI */
796     SendMessage( hTree, TVM_SETUNICODEFORMAT, 0, 0 );
797     bNewSetting = (BOOL)SendMessage( hTree, TVM_GETUNICODEFORMAT, 0, 0 );
798     ok(bNewSetting == 0, "ANSI setting did not work.\n");
799
800     /* Revert to original setting */
801     SendMessage( hTree, TVM_SETUNICODEFORMAT, bPreviousSetting, 0 );
802
803     ok_sequence(MsgSequences, TREEVIEW_SEQ_INDEX, test_get_set_unicodeformat_seq,
804         "test get set unicode format", FALSE);
805
806     DestroyWindow(hTree);
807 }
808
809 static LRESULT CALLBACK MyWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
810 {
811     switch(msg) {
812     case WM_NOTIFY:
813     {
814         NMHDR *pHdr = (NMHDR *)lParam;
815     
816         ok(pHdr->code != NM_FIRST - 19, "Treeview should not send NM_TOOLTIPSCREATED\n");
817         if (pHdr->idFrom == 100) {
818             NMTREEVIEWA *pTreeView = (LPNMTREEVIEWA) lParam;
819             switch(pHdr->code) {
820             case TVN_SELCHANGINGA:
821                 AddItem('(');
822                 IdentifyItem(pTreeView->itemOld.hItem);
823                 IdentifyItem(pTreeView->itemNew.hItem);
824                 return 0;
825             case TVN_SELCHANGEDA:
826                 AddItem(')');
827                 IdentifyItem(pTreeView->itemOld.hItem);
828                 IdentifyItem(pTreeView->itemNew.hItem);
829                 return 0;
830             case TVN_GETDISPINFOA: {
831                 NMTVDISPINFOA *disp = (NMTVDISPINFOA *)lParam;
832                 if (disp->item.mask & TVIF_TEXT) {
833                     lstrcpyn(disp->item.pszText, TEST_CALLBACK_TEXT, disp->item.cchTextMax);
834                 }
835                 return 0;
836               }
837             case TVN_ENDLABELEDIT: return TRUE;
838             }
839         }
840         return 0;
841     }
842   
843     case WM_DESTROY:
844         PostQuitMessage(0);
845         break;
846   
847     default:
848         return DefWindowProcA(hWnd, msg, wParam, lParam);
849     }
850     return 0L;
851 }
852
853 static void test_expandinvisible(void)
854 {
855     static CHAR nodeText[][5] = {"0", "1", "2", "3", "4"};
856     TVINSERTSTRUCTA ins;
857     HTREEITEM node[5];
858     RECT dummyRect;
859     BOOL nodeVisible;
860     LRESULT ret;
861     HWND hTree;
862
863     hTree = create_treeview_control();
864
865     /* The test builds the following tree and expands then node 1, while node 0 is collapsed.
866      *
867      * 0
868      * |- 1
869      * |  |- 2
870      * |  |- 3
871      * |- 4
872      *
873      */
874
875     ret = TreeView_DeleteAllItems(hTree);
876     ok(ret == TRUE, "ret\n");
877     ins.hParent = TVI_ROOT;
878     ins.hInsertAfter = TVI_ROOT;
879     U(ins).item.mask = TVIF_TEXT;
880     U(ins).item.pszText = nodeText[0];
881     node[0] = TreeView_InsertItem(hTree, &ins);
882     assert(node[0]);
883
884     ins.hInsertAfter = TVI_LAST;
885     U(ins).item.mask = TVIF_TEXT;
886     ins.hParent = node[0];
887
888     U(ins).item.pszText = nodeText[1];
889     node[1] = TreeView_InsertItem(hTree, &ins);
890     assert(node[1]);
891     U(ins).item.pszText = nodeText[4];
892     node[4] = TreeView_InsertItem(hTree, &ins);
893     assert(node[4]);
894
895     ins.hParent = node[1];
896
897     U(ins).item.pszText = nodeText[2];
898     node[2] = TreeView_InsertItem(hTree, &ins);
899     assert(node[2]);
900     U(ins).item.pszText = nodeText[3];
901     node[3] = TreeView_InsertItem(hTree, &ins);
902     assert(node[3]);
903
904
905     nodeVisible = TreeView_GetItemRect(hTree, node[1], &dummyRect, FALSE);
906     ok(!nodeVisible, "Node 1 should not be visible.\n");
907     nodeVisible = TreeView_GetItemRect(hTree, node[2], &dummyRect, FALSE);
908     ok(!nodeVisible, "Node 2 should not be visible.\n");
909     nodeVisible = TreeView_GetItemRect(hTree, node[3], &dummyRect, FALSE);
910     ok(!nodeVisible, "Node 3 should not be visible.\n");
911     nodeVisible = TreeView_GetItemRect(hTree, node[4], &dummyRect, FALSE);
912     ok(!nodeVisible, "Node 4 should not be visible.\n");
913
914     ok(TreeView_Expand(hTree, node[1], TVE_EXPAND), "Expand of node 1 failed.\n");
915
916     nodeVisible = TreeView_GetItemRect(hTree, node[1], &dummyRect, FALSE);
917     ok(!nodeVisible, "Node 1 should not be visible.\n");
918     nodeVisible = TreeView_GetItemRect(hTree, node[2], &dummyRect, FALSE);
919     ok(!nodeVisible, "Node 2 should not be visible.\n");
920     nodeVisible = TreeView_GetItemRect(hTree, node[3], &dummyRect, FALSE);
921     ok(!nodeVisible, "Node 3 should not be visible.\n");
922     nodeVisible = TreeView_GetItemRect(hTree, node[4], &dummyRect, FALSE);
923     ok(!nodeVisible, "Node 4 should not be visible.\n");
924
925     DestroyWindow(hTree);
926 }
927
928 static void test_itemedit(void)
929 {
930     DWORD r;
931     HWND edit;
932     TVITEMA item;
933     CHAR buff[2];
934     HWND hTree;
935
936     hTree = create_treeview_control();
937     fill_tree(hTree);
938
939     /* try with null item */
940     edit = (HWND)SendMessage(hTree, TVM_EDITLABEL, 0, 0);
941     ok(!IsWindow(edit), "Expected valid handle\n");
942
943     /* trigger edit */
944     edit = (HWND)SendMessage(hTree, TVM_EDITLABEL, 0, (LPARAM)hRoot);
945     ok(IsWindow(edit), "Expected valid handle\n");
946     /* item shouldn't be selected automatically after TVM_EDITLABEL */
947     r = SendMessage(hTree, TVM_GETITEMSTATE, (WPARAM)hRoot, TVIS_SELECTED);
948     expect(0, r);
949     /* try to cancel with wrong edit handle */
950     r = SendMessage(hTree, WM_COMMAND, MAKEWPARAM(0, EN_KILLFOCUS), 0);
951     expect(0, r);
952     ok(IsWindow(edit), "Expected edit control to be valid\n");
953     r = SendMessage(hTree, WM_COMMAND, MAKEWPARAM(0, EN_KILLFOCUS), (LPARAM)edit);
954     expect(0, r);
955     ok(!IsWindow(edit), "Expected edit control to be destroyed\n");
956     /* try to cancel without creating edit */
957     r = SendMessage(hTree, WM_COMMAND, MAKEWPARAM(0, EN_KILLFOCUS), 0);
958     expect(0, r);
959
960     /* try to cancel with wrong (not null) handle */
961     edit = (HWND)SendMessage(hTree, TVM_EDITLABEL, 0, (LPARAM)hRoot);
962     ok(IsWindow(edit), "Expected valid handle\n");
963     r = SendMessage(hTree, WM_COMMAND, MAKEWPARAM(0, EN_KILLFOCUS), (LPARAM)hTree);
964     expect(0, r);
965     ok(IsWindow(edit), "Expected edit control to be valid\n");
966     r = SendMessage(hTree, WM_COMMAND, MAKEWPARAM(0, EN_KILLFOCUS), (LPARAM)edit);
967     expect(0, r);
968
969     /* remove selection after starting edit */
970     r = TreeView_SelectItem(hTree, hRoot);
971     expect(TRUE, r);
972     edit = (HWND)SendMessage(hTree, TVM_EDITLABEL, 0, (LPARAM)hRoot);
973     ok(IsWindow(edit), "Expected valid handle\n");
974     r = TreeView_SelectItem(hTree, NULL);
975     expect(TRUE, r);
976     /* alter text */
977     strncpy(buff, "x", sizeof(buff)/sizeof(CHAR));
978     r = SendMessage(edit, WM_SETTEXT, 0, (LPARAM)buff);
979     expect(TRUE, r);
980     r = SendMessage(hTree, WM_COMMAND, MAKEWPARAM(0, EN_KILLFOCUS), (LPARAM)edit);
981     expect(0, r);
982     ok(!IsWindow(edit), "Expected edit control to be destroyed\n");
983     /* check that text is saved */
984     item.mask = TVIF_TEXT;
985     item.hItem = hRoot;
986     item.pszText = buff;
987     item.cchTextMax = sizeof(buff)/sizeof(CHAR);
988     r = SendMessage(hTree, TVM_GETITEM, 0, (LPARAM)&item);
989     expect(TRUE, r);
990     ok(!strcmp("x", buff), "Expected item text to change\n");
991
992     DestroyWindow(hTree);
993 }
994
995 static void test_treeview_classinfo(void)
996 {
997     WNDCLASSA cls;
998
999     memset(&cls, 0, sizeof(cls));
1000     GetClassInfo(GetModuleHandleA("comctl32.dll"), WC_TREEVIEWA, &cls);
1001     ok(cls.hbrBackground == NULL, "Expected NULL background brush, got %p\n", cls.hbrBackground);
1002     ok(cls.style == (CS_GLOBALCLASS | CS_DBLCLKS), "Expected got %x\n", cls.style);
1003     expect(0, cls.cbClsExtra);
1004 }
1005
1006 static void test_get_linecolor(void)
1007 {
1008     COLORREF clr;
1009     HWND hTree;
1010
1011     hTree = create_treeview_control();
1012
1013     /* newly created control has default color */
1014     clr = (COLORREF)SendMessage(hTree, TVM_GETLINECOLOR, 0, 0);
1015     if (clr == 0)
1016         win_skip("TVM_GETLINECOLOR is not supported on comctl32 < 5.80\n");
1017     else
1018         expect(CLR_DEFAULT, clr);
1019
1020     DestroyWindow(hTree);
1021 }
1022
1023 static void test_get_insertmarkcolor(void)
1024 {
1025     COLORREF clr;
1026     HWND hTree;
1027
1028     hTree = create_treeview_control();
1029
1030     /* newly created control has default color */
1031     clr = (COLORREF)SendMessage(hTree, TVM_GETINSERTMARKCOLOR, 0, 0);
1032     if (clr == 0)
1033         win_skip("TVM_GETINSERTMARKCOLOR is not supported on comctl32 < 5.80\n");
1034     else
1035         expect(CLR_DEFAULT, clr);
1036
1037     DestroyWindow(hTree);
1038 }
1039
1040 START_TEST(treeview)
1041 {
1042     HMODULE hComctl32;
1043     BOOL (WINAPI *pInitCommonControlsEx)(const INITCOMMONCONTROLSEX*);
1044     WNDCLASSA wc;
1045     MSG msg;
1046   
1047     hComctl32 = GetModuleHandleA("comctl32.dll");
1048     pInitCommonControlsEx = (void*)GetProcAddress(hComctl32, "InitCommonControlsEx");
1049     if (pInitCommonControlsEx)
1050     {
1051         INITCOMMONCONTROLSEX iccex;
1052         iccex.dwSize = sizeof(iccex);
1053         iccex.dwICC  = ICC_TREEVIEW_CLASSES;
1054         pInitCommonControlsEx(&iccex);
1055     }
1056     else
1057         InitCommonControls();
1058
1059     init_msg_sequences(MsgSequences, NUM_MSG_SEQUENCES);
1060   
1061     wc.style = CS_HREDRAW | CS_VREDRAW;
1062     wc.cbClsExtra = 0;
1063     wc.cbWndExtra = 0;
1064     wc.hInstance = GetModuleHandleA(NULL);
1065     wc.hIcon = NULL;
1066     wc.hCursor = LoadCursorA(NULL, IDC_IBEAM);
1067     wc.hbrBackground = GetSysColorBrush(COLOR_WINDOW);
1068     wc.lpszMenuName = NULL;
1069     wc.lpszClassName = "MyTestWnd";
1070     wc.lpfnWndProc = MyWndProc;
1071     RegisterClassA(&wc);
1072
1073     hMainWnd = CreateWindowExA(0, "MyTestWnd", "Blah", WS_OVERLAPPEDWINDOW,
1074       CW_USEDEFAULT, CW_USEDEFAULT, 130, 105, NULL, NULL, GetModuleHandleA(NULL), 0);
1075
1076     ok(hMainWnd != NULL, "Failed to create parent window. Tests aborted.\n");
1077     if (!hMainWnd) return;
1078
1079     test_fillroot();
1080     test_select();
1081     test_getitemtext();
1082     test_focus();
1083     test_get_set_bkcolor();
1084     test_get_set_imagelist();
1085     test_get_set_indent();
1086     test_get_set_insertmark();
1087     test_get_set_item();
1088     test_get_set_itemheight();
1089     test_get_set_scrolltime();
1090     test_get_set_textcolor();
1091     test_get_linecolor();
1092     test_get_insertmarkcolor();
1093     test_get_set_tooltips();
1094     test_get_set_unicodeformat();
1095     test_callback();
1096     test_expandinvisible();
1097     test_itemedit();
1098     test_treeview_classinfo();
1099
1100     PostMessageA(hMainWnd, WM_CLOSE, 0, 0);
1101     while(GetMessageA(&msg,0,0,0)) {
1102         TranslateMessage(&msg);
1103         DispatchMessageA(&msg);
1104     }
1105 }