comctl32/tests: Remove variable hwndheader which is not really used from test_items.
[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     Clear();
392     AddItem('1');
393     r = TreeView_SelectItem(hTree, hRoot);
394     AddItem('2');
395     r = TreeView_SelectItem(hTree, hRoot);
396     AddItem('3');
397     r = TreeView_SelectItem(hTree, NULL);
398     AddItem('4');
399     r = TreeView_SelectItem(hTree, NULL);
400     AddItem('5');
401     r = TreeView_SelectItem(hTree, hRoot);
402     AddItem('.');
403     ok(!strcmp(sequence, "1(nR)nR23(Rn)Rn45(nR)nR."), "root-none select test\n");
404     ok_sequence(MsgSequences, TREEVIEW_SEQ_INDEX, rootnone_select_seq,
405                 "root-none select seq", FALSE);
406
407     /* root-child select tests */
408     flush_sequences(MsgSequences, NUM_MSG_SEQUENCES);
409     r = TreeView_SelectItem(hTree, NULL);
410     Clear();
411     AddItem('1');
412     r = TreeView_SelectItem(hTree, hRoot);
413     AddItem('2');
414     r = TreeView_SelectItem(hTree, hRoot);
415     AddItem('3');
416     r = TreeView_SelectItem(hTree, hChild);
417     AddItem('4');
418     r = TreeView_SelectItem(hTree, hChild);
419     AddItem('5');
420     r = TreeView_SelectItem(hTree, hRoot);
421     AddItem('.');
422     ok(!strcmp(sequence, "1(nR)nR23(RC)RC45(CR)CR."), "root-child select test\n");
423     ok_sequence(MsgSequences, TREEVIEW_SEQ_INDEX, rootchild_select_seq,
424                 "root-child select seq", FALSE);
425
426     DestroyWindow(hTree);
427 }
428
429 static void test_getitemtext(void)
430 {
431     TVINSERTSTRUCTA ins;
432     HTREEITEM hChild;
433     TVITEM tvi;
434     HWND hTree;
435
436     CHAR szBuffer[80] = "Blah";
437     int nBufferSize = sizeof(szBuffer)/sizeof(CHAR);
438
439     hTree = create_treeview_control();
440     fill_tree(hTree);
441
442     flush_sequences(MsgSequences, NUM_MSG_SEQUENCES);
443
444     /* add an item without TVIF_TEXT mask and pszText == NULL */
445     ins.hParent = hRoot;
446     ins.hInsertAfter = TVI_ROOT;
447     U(ins).item.mask = 0;
448     U(ins).item.pszText = NULL;
449     U(ins).item.cchTextMax = 0;
450     hChild = TreeView_InsertItem(hTree, &ins);
451     assert(hChild);
452
453     /* retrieve it with TVIF_TEXT mask */
454     tvi.hItem = hChild;
455     tvi.mask = TVIF_TEXT;
456     tvi.cchTextMax = nBufferSize;
457     tvi.pszText = szBuffer;
458
459     SendMessageA( hTree, TVM_GETITEM, 0, (LPARAM)&tvi );
460     ok(!strcmp(szBuffer, ""), "szBuffer=\"%s\", expected \"\"\n", szBuffer);
461     ok(SendMessageA(hTree, TVM_DELETEITEM, 0, (LPARAM)hChild), "DeleteItem failed\n");
462     ok_sequence(MsgSequences, TREEVIEW_SEQ_INDEX, getitemtext_seq, "get item text seq", FALSE);
463
464     DestroyWindow(hTree);
465 }
466
467 static void test_focus(void)
468 {
469     TVINSERTSTRUCTA ins;
470     static CHAR child1[]  = "Edit",
471                 child2[]  = "A really long string";
472     HTREEITEM hChild1, hChild2;
473     HWND hTree;
474     HWND hEdit;
475
476     hTree = create_treeview_control();
477     fill_tree(hTree);
478
479     flush_sequences(MsgSequences, NUM_MSG_SEQUENCES);
480
481     /* This test verifies that when a label is being edited, scrolling
482      * the treeview does not cause the label to lose focus. To test
483      * this, first some additional entries are added to generate
484      * scrollbars.
485      */
486     ins.hParent = hRoot;
487     ins.hInsertAfter = hChild;
488     U(ins).item.mask = TVIF_TEXT;
489     U(ins).item.pszText = child1;
490     hChild1 = TreeView_InsertItem(hTree, &ins);
491     assert(hChild1);
492     ins.hInsertAfter = hChild1;
493     U(ins).item.mask = TVIF_TEXT;
494     U(ins).item.pszText = child2;
495     hChild2 = TreeView_InsertItem(hTree, &ins);
496     assert(hChild2);
497
498     ShowWindow(hMainWnd,SW_SHOW);
499     SendMessageA(hTree, TVM_SELECTITEM, TVGN_CARET, (LPARAM)hChild);
500     hEdit = TreeView_EditLabel(hTree, hChild);
501     ScrollWindowEx(hTree, -10, 0, NULL, NULL, NULL, NULL, SW_SCROLLCHILDREN);
502     ok(GetFocus() == hEdit, "Edit control should have focus\n");
503     ok_sequence(MsgSequences, TREEVIEW_SEQ_INDEX, focus_seq, "focus test", TRUE);
504
505     DestroyWindow(hTree);
506 }
507
508 static void test_get_set_bkcolor(void)
509 {
510     COLORREF crColor = RGB(0,0,0);
511     HWND hTree;
512
513     hTree = create_treeview_control();
514     fill_tree(hTree);
515
516     flush_sequences(MsgSequences, NUM_MSG_SEQUENCES);
517
518     /* If the value is -1, the control is using the system color for the background color. */
519     crColor = (COLORREF)SendMessage( hTree, TVM_GETBKCOLOR, 0, 0 );
520     ok(crColor == -1, "Default background color reported as 0x%.8x\n", crColor);
521
522     /* Test for black background */
523     SendMessage( hTree, TVM_SETBKCOLOR, 0, RGB(0,0,0) );
524     crColor = (COLORREF)SendMessage( hTree, TVM_GETBKCOLOR, 0, 0 );
525     ok(crColor == RGB(0,0,0), "Black background color reported as 0x%.8x\n", crColor);
526
527     /* Test for white background */
528     SendMessage( hTree, TVM_SETBKCOLOR, 0, RGB(255,255,255) );
529     crColor = (COLORREF)SendMessage( hTree, TVM_GETBKCOLOR, 0, 0 );
530     ok(crColor == RGB(255,255,255), "White background color reported as 0x%.8x\n", crColor);
531
532     /* Reset the default background */
533     SendMessage( hTree, TVM_SETBKCOLOR, 0, -1 );
534
535     ok_sequence(MsgSequences, TREEVIEW_SEQ_INDEX, test_get_set_bkcolor_seq,
536         "test get set bkcolor", FALSE);
537
538     DestroyWindow(hTree);
539 }
540
541 static void test_get_set_imagelist(void)
542 {
543     HIMAGELIST hImageList = NULL;
544     HWND hTree;
545
546     hTree = create_treeview_control();
547     fill_tree(hTree);
548
549     flush_sequences(MsgSequences, NUM_MSG_SEQUENCES);
550
551     /* Test a NULL HIMAGELIST */
552     SendMessage( hTree, TVM_SETIMAGELIST, TVSIL_NORMAL, (LPARAM)hImageList );
553     hImageList = (HIMAGELIST)SendMessage( hTree, TVM_GETIMAGELIST, TVSIL_NORMAL, 0 );
554     ok(hImageList == NULL, "NULL image list, reported as 0x%p, expected 0.\n", hImageList);
555
556     /* TODO: Test an actual image list */
557
558     ok_sequence(MsgSequences, TREEVIEW_SEQ_INDEX, test_get_set_imagelist_seq,
559         "test get imagelist", FALSE);
560
561     DestroyWindow(hTree);
562 }
563
564 static void test_get_set_indent(void)
565 {
566     int ulIndent = -1;
567     int ulMinIndent = -1;
568     int ulMoreThanTwiceMin = -1;
569     HWND hTree;
570
571     hTree = create_treeview_control();
572     fill_tree(hTree);
573
574     flush_sequences(MsgSequences, NUM_MSG_SEQUENCES);
575
576     /* Finding the minimum indent */
577     SendMessage( hTree, TVM_SETINDENT, 0, 0 );
578     ulMinIndent = (int)SendMessage( hTree, TVM_GETINDENT, 0, 0 );
579
580     /* Checking an indent that is more than twice the default indent */
581     ulMoreThanTwiceMin = 2*ulMinIndent+1;
582     SendMessage( hTree, TVM_SETINDENT, ulMoreThanTwiceMin, 0 );
583     ulIndent = (DWORD)SendMessage( hTree, TVM_GETINDENT, 0, 0 );
584     ok(ulIndent == ulMoreThanTwiceMin, "Indent reported as %d, expected %d\n", ulIndent, ulMoreThanTwiceMin);
585
586     ok_sequence(MsgSequences, TREEVIEW_SEQ_INDEX, test_get_set_indent_seq,
587         "test get set indent", FALSE);
588
589     DestroyWindow(hTree);
590 }
591
592 static void test_get_set_insertmark(void)
593 {
594     COLORREF crColor = RGB(0,0,0);
595     HWND hTree;
596
597     hTree = create_treeview_control();
598     fill_tree(hTree);
599
600     flush_sequences(MsgSequences, NUM_MSG_SEQUENCES);
601
602     SendMessage( hTree, TVM_SETINSERTMARKCOLOR, 0, crColor );
603     crColor = (COLORREF)SendMessage( hTree, TVM_GETINSERTMARKCOLOR, 0, 0 );
604     ok(crColor == RGB(0,0,0), "Insert mark color reported as 0x%.8x, expected 0x00000000\n", crColor);
605
606     ok_sequence(MsgSequences, TREEVIEW_SEQ_INDEX, test_get_set_insertmarkcolor_seq,
607         "test get set insertmark color", FALSE);
608
609     DestroyWindow(hTree);
610 }
611
612 static void test_get_set_item(void)
613 {
614     TVITEM tviRoot = {0};
615     int nBufferSize = 80;
616     char szBuffer[80] = {0};
617     HWND hTree;
618
619     hTree = create_treeview_control();
620     fill_tree(hTree);
621
622     flush_sequences(MsgSequences, NUM_MSG_SEQUENCES);
623
624     /* Test the root item */
625     tviRoot.hItem = hRoot;
626     tviRoot.mask = TVIF_TEXT;
627     tviRoot.cchTextMax = nBufferSize;
628     tviRoot.pszText = szBuffer;
629     SendMessage( hTree, TVM_GETITEM, 0, (LPARAM)&tviRoot );
630     ok(!strcmp("Root", szBuffer), "GetItem: szBuffer=\"%s\", expected \"Root\"\n", szBuffer);
631
632     /* Change the root text */
633     strncpy(szBuffer, "Testing123", nBufferSize);
634     SendMessage( hTree, TVM_SETITEM, 0, (LPARAM)&tviRoot );
635     memset(szBuffer, 0, nBufferSize);
636     SendMessage( hTree, TVM_GETITEM, 0, (LPARAM)&tviRoot );
637     ok(!strcmp("Testing123", szBuffer), "GetItem: szBuffer=\"%s\", expected \"Testing123\"\n", szBuffer);
638
639     /* Reset the root text */
640     memset(szBuffer, 0, nBufferSize);
641     strncpy(szBuffer, "Root", nBufferSize);
642     SendMessage( hTree, TVM_SETITEM, 0, (LPARAM)&tviRoot );
643
644     ok_sequence(MsgSequences, TREEVIEW_SEQ_INDEX, test_get_set_item_seq,
645         "test get set item", FALSE);
646
647     DestroyWindow(hTree);
648 }
649
650 static void test_get_set_itemheight(void)
651 {
652     int ulOldHeight = 0;
653     int ulNewHeight = 0;
654     HWND hTree;
655
656     hTree = create_treeview_control();
657     fill_tree(hTree);
658
659     flush_sequences(MsgSequences, NUM_MSG_SEQUENCES);
660
661     /* Assuming default height to begin with */
662     ulOldHeight = (int) SendMessage( hTree, TVM_GETITEMHEIGHT, 0, 0 );
663
664     /* Explicitly setting and getting the default height */
665     SendMessage( hTree, TVM_SETITEMHEIGHT, -1, 0 );
666     ulNewHeight = (int) SendMessage( hTree, TVM_GETITEMHEIGHT, 0, 0 );
667     ok(ulNewHeight == ulOldHeight, "Default height not set properly, reported %d, expected %d\n", ulNewHeight, ulOldHeight);
668
669     /* Explicitly setting and getting the height of twice the normal */
670     SendMessage( hTree, TVM_SETITEMHEIGHT, 2*ulOldHeight, 0 );
671     ulNewHeight = (int) SendMessage( hTree, TVM_GETITEMHEIGHT, 0, 0 );
672     ok(ulNewHeight == 2*ulOldHeight, "New height not set properly, reported %d, expected %d\n", ulNewHeight, 2*ulOldHeight);
673
674     /* Assuming tree doesn't have TVS_NONEVENHEIGHT set, so a set of 9 will round down to 8 */
675     SendMessage( hTree, TVM_SETITEMHEIGHT, 9, 0 );
676     ulNewHeight = (int) SendMessage( hTree, TVM_GETITEMHEIGHT, 0, 0 );
677     ok(ulNewHeight == 8, "Uneven height not set properly, reported %d, expected %d\n", ulNewHeight, 8);
678
679     ok_sequence(MsgSequences, TREEVIEW_SEQ_INDEX, test_get_set_itemheight_seq,
680         "test get set item height", FALSE);
681
682     DestroyWindow(hTree);
683 }
684
685 static void test_get_set_scrolltime(void)
686 {
687     int ulExpectedTime = 20;
688     int ulTime = 0;
689     HWND hTree;
690
691     hTree = create_treeview_control();
692     fill_tree(hTree);
693
694     flush_sequences(MsgSequences, NUM_MSG_SEQUENCES);
695
696     SendMessage( hTree, TVM_SETSCROLLTIME, ulExpectedTime, 0 );
697     ulTime = (int)SendMessage( hTree, TVM_GETSCROLLTIME, 0, 0 );
698     ok(ulTime == ulExpectedTime, "Scroll time reported as %d, expected %d\n", ulTime, ulExpectedTime);
699
700     ok_sequence(MsgSequences, TREEVIEW_SEQ_INDEX, test_get_set_scrolltime_seq,
701         "test get set scroll time", FALSE);
702
703     DestroyWindow(hTree);
704 }
705
706 static void test_get_set_textcolor(void)
707 {
708     /* If the value is -1, the control is using the system color for the text color. */
709     COLORREF crColor = RGB(0,0,0);
710     HWND hTree;
711
712     hTree = create_treeview_control();
713     fill_tree(hTree);
714
715     flush_sequences(MsgSequences, NUM_MSG_SEQUENCES);
716
717     crColor = (COLORREF)SendMessage( hTree, TVM_GETTEXTCOLOR, 0, 0 );
718     ok(crColor == -1, "Default text color reported as 0x%.8x\n", crColor);
719
720     /* Test for black text */
721     SendMessage( hTree, TVM_SETTEXTCOLOR, 0, RGB(0,0,0) );
722     crColor = (COLORREF)SendMessage( hTree, TVM_GETTEXTCOLOR, 0, 0 );
723     ok(crColor == RGB(0,0,0), "Black text color reported as 0x%.8x\n", crColor);
724
725     /* Test for white text */
726     SendMessage( hTree, TVM_SETTEXTCOLOR, 0, RGB(255,255,255) );
727     crColor = (COLORREF)SendMessage( hTree, TVM_GETTEXTCOLOR, 0, 0 );
728     ok(crColor == RGB(255,255,255), "White text color reported as 0x%.8x\n", crColor);
729
730     /* Reset the default text color */
731     SendMessage( hTree, TVM_SETTEXTCOLOR, 0, CLR_NONE );
732
733     ok_sequence(MsgSequences, TREEVIEW_SEQ_INDEX, test_get_set_textcolor_seq,
734         "test get set text color", FALSE);
735
736     DestroyWindow(hTree);
737 }
738
739 static void test_get_set_tooltips(void)
740 {
741     HWND hwndLastToolTip = NULL;
742     HWND hPopupTreeView;
743     HWND hTree;
744
745     hTree = create_treeview_control();
746     fill_tree(hTree);
747
748     flush_sequences(MsgSequences, NUM_MSG_SEQUENCES);
749
750     /* show even WS_POPUP treeview don't send NM_TOOLTIPSCREATED */
751     hPopupTreeView = CreateWindow(WC_TREEVIEW, NULL, WS_POPUP|WS_VISIBLE, 0, 0, 100, 100, hMainWnd, NULL, NULL, NULL);
752     DestroyWindow(hPopupTreeView);
753
754     /* Testing setting a NULL ToolTip */
755     SendMessage( hTree, TVM_SETTOOLTIPS, 0, 0 );
756     hwndLastToolTip = (HWND)SendMessage( hTree, TVM_GETTOOLTIPS, 0, 0 );
757     ok(hwndLastToolTip == NULL, "NULL tool tip, reported as 0x%p, expected 0.\n", hwndLastToolTip);
758
759     ok_sequence(MsgSequences, TREEVIEW_SEQ_INDEX, test_get_set_tooltips_seq,
760         "test get set tooltips", TRUE);
761
762     /* TODO: Add a test of an actual tooltip */
763     DestroyWindow(hTree);
764 }
765
766 static void test_get_set_unicodeformat(void)
767 {
768     BOOL bPreviousSetting = 0;
769     BOOL bNewSetting = 0;
770     HWND hTree;
771
772     hTree = create_treeview_control();
773     fill_tree(hTree);
774
775     flush_sequences(MsgSequences, NUM_MSG_SEQUENCES);
776
777     /* Set to Unicode */
778     bPreviousSetting = (BOOL)SendMessage( hTree, TVM_SETUNICODEFORMAT, 1, 0 );
779     bNewSetting = (BOOL)SendMessage( hTree, TVM_GETUNICODEFORMAT, 0, 0 );
780     ok(bNewSetting == 1, "Unicode setting did not work.\n");
781
782     /* Set to ANSI */
783     SendMessage( hTree, TVM_SETUNICODEFORMAT, 0, 0 );
784     bNewSetting = (BOOL)SendMessage( hTree, TVM_GETUNICODEFORMAT, 0, 0 );
785     ok(bNewSetting == 0, "ANSI setting did not work.\n");
786
787     /* Revert to original setting */
788     SendMessage( hTree, TVM_SETUNICODEFORMAT, bPreviousSetting, 0 );
789
790     ok_sequence(MsgSequences, TREEVIEW_SEQ_INDEX, test_get_set_unicodeformat_seq,
791         "test get set unicode format", FALSE);
792
793     DestroyWindow(hTree);
794 }
795
796 static LRESULT CALLBACK MyWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
797 {
798     switch(msg) {
799     case WM_NOTIFY:
800     {
801         NMHDR *pHdr = (NMHDR *)lParam;
802     
803         ok(pHdr->code != NM_FIRST - 19, "Treeview should not send NM_TOOLTIPSCREATED\n");
804         if (pHdr->idFrom == 100) {
805             NMTREEVIEWA *pTreeView = (LPNMTREEVIEWA) lParam;
806             switch(pHdr->code) {
807             case TVN_SELCHANGINGA:
808                 AddItem('(');
809                 IdentifyItem(pTreeView->itemOld.hItem);
810                 IdentifyItem(pTreeView->itemNew.hItem);
811                 return 0;
812             case TVN_SELCHANGEDA:
813                 AddItem(')');
814                 IdentifyItem(pTreeView->itemOld.hItem);
815                 IdentifyItem(pTreeView->itemNew.hItem);
816                 return 0;
817             case TVN_GETDISPINFOA: {
818                 NMTVDISPINFOA *disp = (NMTVDISPINFOA *)lParam;
819                 if (disp->item.mask & TVIF_TEXT) {
820                     lstrcpyn(disp->item.pszText, TEST_CALLBACK_TEXT, disp->item.cchTextMax);
821                 }
822                 return 0;
823               }
824             case TVN_ENDLABELEDIT: return TRUE;
825             }
826         }
827         return 0;
828     }
829   
830     case WM_DESTROY:
831         PostQuitMessage(0);
832         break;
833   
834     default:
835         return DefWindowProcA(hWnd, msg, wParam, lParam);
836     }
837     return 0L;
838 }
839
840 static void test_expandinvisible(void)
841 {
842     static CHAR nodeText[][5] = {"0", "1", "2", "3", "4"};
843     TVINSERTSTRUCTA ins;
844     HTREEITEM node[5];
845     RECT dummyRect;
846     BOOL nodeVisible;
847     LRESULT ret;
848     HWND hTree;
849
850     hTree = create_treeview_control();
851
852     /* The test builds the following tree and expands then node 1, while node 0 is collapsed.
853      *
854      * 0
855      * |- 1
856      * |  |- 2
857      * |  |- 3
858      * |- 4
859      *
860      */
861
862     ret = TreeView_DeleteAllItems(hTree);
863     ok(ret == TRUE, "ret\n");
864     ins.hParent = TVI_ROOT;
865     ins.hInsertAfter = TVI_ROOT;
866     U(ins).item.mask = TVIF_TEXT;
867     U(ins).item.pszText = nodeText[0];
868     node[0] = TreeView_InsertItem(hTree, &ins);
869     assert(node[0]);
870
871     ins.hInsertAfter = TVI_LAST;
872     U(ins).item.mask = TVIF_TEXT;
873     ins.hParent = node[0];
874
875     U(ins).item.pszText = nodeText[1];
876     node[1] = TreeView_InsertItem(hTree, &ins);
877     assert(node[1]);
878     U(ins).item.pszText = nodeText[4];
879     node[4] = TreeView_InsertItem(hTree, &ins);
880     assert(node[4]);
881
882     ins.hParent = node[1];
883
884     U(ins).item.pszText = nodeText[2];
885     node[2] = TreeView_InsertItem(hTree, &ins);
886     assert(node[2]);
887     U(ins).item.pszText = nodeText[3];
888     node[3] = TreeView_InsertItem(hTree, &ins);
889     assert(node[3]);
890
891
892     nodeVisible = TreeView_GetItemRect(hTree, node[1], &dummyRect, FALSE);
893     ok(!nodeVisible, "Node 1 should not be visible.\n");
894     nodeVisible = TreeView_GetItemRect(hTree, node[2], &dummyRect, FALSE);
895     ok(!nodeVisible, "Node 2 should not be visible.\n");
896     nodeVisible = TreeView_GetItemRect(hTree, node[3], &dummyRect, FALSE);
897     ok(!nodeVisible, "Node 3 should not be visible.\n");
898     nodeVisible = TreeView_GetItemRect(hTree, node[4], &dummyRect, FALSE);
899     ok(!nodeVisible, "Node 4 should not be visible.\n");
900
901     ok(TreeView_Expand(hTree, node[1], TVE_EXPAND), "Expand of node 1 failed.\n");
902
903     nodeVisible = TreeView_GetItemRect(hTree, node[1], &dummyRect, FALSE);
904     ok(!nodeVisible, "Node 1 should not be visible.\n");
905     nodeVisible = TreeView_GetItemRect(hTree, node[2], &dummyRect, FALSE);
906     ok(!nodeVisible, "Node 2 should not be visible.\n");
907     nodeVisible = TreeView_GetItemRect(hTree, node[3], &dummyRect, FALSE);
908     ok(!nodeVisible, "Node 3 should not be visible.\n");
909     nodeVisible = TreeView_GetItemRect(hTree, node[4], &dummyRect, FALSE);
910     ok(!nodeVisible, "Node 4 should not be visible.\n");
911
912     DestroyWindow(hTree);
913 }
914
915 static void test_itemedit(void)
916 {
917     DWORD r;
918     HWND edit;
919     TVITEMA item;
920     CHAR buff[2];
921     HWND hTree;
922
923     hTree = create_treeview_control();
924     fill_tree(hTree);
925
926     /* try with null item */
927     edit = (HWND)SendMessage(hTree, TVM_EDITLABEL, 0, 0);
928     ok(!IsWindow(edit), "Expected valid handle\n");
929
930     /* trigger edit */
931     edit = (HWND)SendMessage(hTree, TVM_EDITLABEL, 0, (LPARAM)hRoot);
932     ok(IsWindow(edit), "Expected valid handle\n");
933     /* item shouldn't be selected automatically after TVM_EDITLABEL */
934     r = SendMessage(hTree, TVM_GETITEMSTATE, (WPARAM)hRoot, TVIS_SELECTED);
935     expect(0, r);
936     /* try to cancel with wrong edit handle */
937     r = SendMessage(hTree, WM_COMMAND, MAKEWPARAM(0, EN_KILLFOCUS), 0);
938     expect(0, r);
939     ok(IsWindow(edit), "Expected edit control to be valid\n");
940     r = SendMessage(hTree, WM_COMMAND, MAKEWPARAM(0, EN_KILLFOCUS), (LPARAM)edit);
941     expect(0, r);
942     ok(!IsWindow(edit), "Expected edit control to be destroyed\n");
943     /* try to cancel without creating edit */
944     r = SendMessage(hTree, WM_COMMAND, MAKEWPARAM(0, EN_KILLFOCUS), 0);
945     expect(0, r);
946
947     /* try to cancel with wrong (not null) handle */
948     edit = (HWND)SendMessage(hTree, TVM_EDITLABEL, 0, (LPARAM)hRoot);
949     ok(IsWindow(edit), "Expected valid handle\n");
950     r = SendMessage(hTree, WM_COMMAND, MAKEWPARAM(0, EN_KILLFOCUS), (LPARAM)hTree);
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
956     /* remove selection after starting edit */
957     r = TreeView_SelectItem(hTree, hRoot);
958     expect(TRUE, r);
959     edit = (HWND)SendMessage(hTree, TVM_EDITLABEL, 0, (LPARAM)hRoot);
960     ok(IsWindow(edit), "Expected valid handle\n");
961     r = TreeView_SelectItem(hTree, NULL);
962     expect(TRUE, r);
963     /* alter text */
964     strncpy(buff, "x", sizeof(buff)/sizeof(CHAR));
965     r = SendMessage(edit, WM_SETTEXT, 0, (LPARAM)buff);
966     expect(TRUE, r);
967     r = SendMessage(hTree, WM_COMMAND, MAKEWPARAM(0, EN_KILLFOCUS), (LPARAM)edit);
968     expect(0, r);
969     ok(!IsWindow(edit), "Expected edit control to be destroyed\n");
970     /* check that text is saved */
971     item.mask = TVIF_TEXT;
972     item.hItem = hRoot;
973     item.pszText = buff;
974     item.cchTextMax = sizeof(buff)/sizeof(CHAR);
975     r = SendMessage(hTree, TVM_GETITEM, 0, (LPARAM)&item);
976     expect(TRUE, r);
977     ok(!strcmp("x", buff), "Expected item text to change\n");
978
979     DestroyWindow(hTree);
980 }
981
982 static void test_treeview_classinfo(void)
983 {
984     WNDCLASSA cls;
985
986     memset(&cls, 0, sizeof(cls));
987     GetClassInfo(GetModuleHandleA("comctl32.dll"), WC_TREEVIEWA, &cls);
988     ok(cls.hbrBackground == NULL, "Expected NULL background brush, got %p\n", cls.hbrBackground);
989     ok(cls.style == (CS_GLOBALCLASS | CS_DBLCLKS), "Expected got %x\n", cls.style);
990     expect(0, cls.cbClsExtra);
991 }
992
993 static void test_get_linecolor(void)
994 {
995     COLORREF clr;
996     HWND hTree;
997
998     hTree = create_treeview_control();
999
1000     /* newly created control has default color */
1001     clr = (COLORREF)SendMessage(hTree, TVM_GETLINECOLOR, 0, 0);
1002     if (clr == 0)
1003         win_skip("TVM_GETLINECOLOR is not supported on comctl32 < 5.80\n");
1004     else
1005         expect(CLR_DEFAULT, clr);
1006
1007     DestroyWindow(hTree);
1008 }
1009
1010 static void test_get_insertmarkcolor(void)
1011 {
1012     COLORREF clr;
1013     HWND hTree;
1014
1015     hTree = create_treeview_control();
1016
1017     /* newly created control has default color */
1018     clr = (COLORREF)SendMessage(hTree, TVM_GETINSERTMARKCOLOR, 0, 0);
1019     if (clr == 0)
1020         win_skip("TVM_GETINSERTMARKCOLOR is not supported on comctl32 < 5.80\n");
1021     else
1022         expect(CLR_DEFAULT, clr);
1023
1024     DestroyWindow(hTree);
1025 }
1026
1027 START_TEST(treeview)
1028 {
1029     HMODULE hComctl32;
1030     BOOL (WINAPI *pInitCommonControlsEx)(const INITCOMMONCONTROLSEX*);
1031     WNDCLASSA wc;
1032     MSG msg;
1033   
1034     hComctl32 = GetModuleHandleA("comctl32.dll");
1035     pInitCommonControlsEx = (void*)GetProcAddress(hComctl32, "InitCommonControlsEx");
1036     if (pInitCommonControlsEx)
1037     {
1038         INITCOMMONCONTROLSEX iccex;
1039         iccex.dwSize = sizeof(iccex);
1040         iccex.dwICC  = ICC_TREEVIEW_CLASSES;
1041         pInitCommonControlsEx(&iccex);
1042     }
1043     else
1044         InitCommonControls();
1045
1046     init_msg_sequences(MsgSequences, NUM_MSG_SEQUENCES);
1047   
1048     wc.style = CS_HREDRAW | CS_VREDRAW;
1049     wc.cbClsExtra = 0;
1050     wc.cbWndExtra = 0;
1051     wc.hInstance = GetModuleHandleA(NULL);
1052     wc.hIcon = NULL;
1053     wc.hCursor = LoadCursorA(NULL, IDC_IBEAM);
1054     wc.hbrBackground = GetSysColorBrush(COLOR_WINDOW);
1055     wc.lpszMenuName = NULL;
1056     wc.lpszClassName = "MyTestWnd";
1057     wc.lpfnWndProc = MyWndProc;
1058     RegisterClassA(&wc);
1059
1060     hMainWnd = CreateWindowExA(0, "MyTestWnd", "Blah", WS_OVERLAPPEDWINDOW,
1061       CW_USEDEFAULT, CW_USEDEFAULT, 130, 105, NULL, NULL, GetModuleHandleA(NULL), 0);
1062
1063     ok(hMainWnd != NULL, "Failed to create parent window. Tests aborted.\n");
1064     if (!hMainWnd) return;
1065
1066     test_fillroot();
1067     test_select();
1068     test_getitemtext();
1069     test_focus();
1070     test_get_set_bkcolor();
1071     test_get_set_imagelist();
1072     test_get_set_indent();
1073     test_get_set_insertmark();
1074     test_get_set_item();
1075     test_get_set_itemheight();
1076     test_get_set_scrolltime();
1077     test_get_set_textcolor();
1078     test_get_linecolor();
1079     test_get_insertmarkcolor();
1080     test_get_set_tooltips();
1081     test_get_set_unicodeformat();
1082     test_callback();
1083     test_expandinvisible();
1084     test_itemedit();
1085     test_treeview_classinfo();
1086
1087     PostMessageA(hMainWnd, WM_CLOSE, 0, 0);
1088     while(GetMessageA(&msg,0,0,0)) {
1089         TranslateMessage(&msg);
1090         DispatchMessageA(&msg);
1091     }
1092 }