comctl32/listview: Block redrawing entirely after WM_SETREDRAW wParam=FALSE.
[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 TestGetSetBkColorSeq[] = {
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 TestGetSetImageListSeq[] = {
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 TestGetSetIndentSeq[] = {
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 TestGetSetInsertMarkColorSeq[] = {
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 TestGetSetItemSeq[] = {
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 TestGetSetItemHeightSeq[] = {
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 TestGetSetScrollTimeSeq[] = {
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 TestGetSetTextColorSeq[] = {
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, 0x00ffffff },
164     { TVM_GETTEXTCOLOR, sent|wparam|lparam, 0, 0 },
165     { TVM_SETTEXTCOLOR, sent|wparam|lparam, 0, -1 },
166     { 0 }
167 };
168
169 static const struct message TestGetSetToolTipsSeq[] = {
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 TestGetSetUnicodeFormatSeq[] = {
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 HWND hTree, hEdit;
190 static HTREEITEM hRoot, hChild;
191
192 static int pos = 0;
193 static char sequence[256];
194
195 static void Clear(void)
196 {
197     pos = 0;
198     sequence[0] = '\0';
199 }
200
201 static void AddItem(char ch)
202 {
203     sequence[pos++] = ch;
204     sequence[pos] = '\0';
205 }
206
207 static void IdentifyItem(HTREEITEM hItem)
208 {
209     if (hItem == hRoot) {
210         AddItem('R');
211         return;
212     }
213     if (hItem == hChild) {
214         AddItem('C');
215         return;
216     }
217     if (hItem == NULL) {
218         AddItem('n');
219         return;
220     }
221     AddItem('?');
222 }
223
224 /* This function hooks in and records all messages to the treeview control */
225 static LRESULT WINAPI TreeviewWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
226 {
227     static LONG defwndproc_counter = 0;
228     LRESULT ret;
229     struct message msg;
230     WNDPROC lpOldProc = (WNDPROC)GetWindowLongPtrA(hwnd, GWLP_USERDATA);
231
232     msg.message = message;
233     msg.flags = sent|wparam|lparam;
234     if (defwndproc_counter) msg.flags |= defwinproc;
235     msg.wParam = wParam;
236     msg.lParam = lParam;
237     add_message(MsgSequences, TREEVIEW_SEQ_INDEX, &msg);
238
239     defwndproc_counter++;
240     ret = CallWindowProcA(lpOldProc, hwnd, message, wParam, lParam);
241     defwndproc_counter--;
242
243     return ret;
244 }
245
246 static HWND create_treeview_control(void)
247 {
248     WNDPROC pOldWndProc;
249     HWND hTree;
250
251     hTree = CreateWindowExA(WS_EX_CLIENTEDGE, WC_TREEVIEWA, NULL, WS_CHILD|WS_VISIBLE|
252             TVS_LINESATROOT|TVS_HASLINES|TVS_HASBUTTONS|TVS_EDITLABELS,
253             0, 0, 120, 100, hMainWnd, (HMENU)100, GetModuleHandleA(0), 0);
254
255     SetFocus(hTree);
256
257     /* Record the old WNDPROC so we can call it after recording the messages */
258     pOldWndProc = (WNDPROC)SetWindowLongPtrA(hTree, GWLP_WNDPROC, (LONG_PTR)TreeviewWndProc);
259     SetWindowLongPtrA(hTree, GWLP_USERDATA, (LONG_PTR)pOldWndProc);
260
261     return hTree;
262 }
263
264 static void fill_tree(HWND hTree)
265 {
266     TVINSERTSTRUCTA ins;
267     static CHAR root[]  = "Root",
268                 child[] = "Child";
269
270     ins.hParent = TVI_ROOT;
271     ins.hInsertAfter = TVI_ROOT;
272     U(ins).item.mask = TVIF_TEXT;
273     U(ins).item.pszText = root;
274     hRoot = TreeView_InsertItem(hTree, &ins);
275
276     ins.hParent = hRoot;
277     ins.hInsertAfter = TVI_FIRST;
278     U(ins).item.mask = TVIF_TEXT;
279     U(ins).item.pszText = child;
280     hChild = TreeView_InsertItem(hTree, &ins);
281 }
282
283 static void test_fillroot(void)
284 {
285     TVITEM tvi;
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
322     hTree = create_treeview_control();
323     fill_tree(hTree);
324
325     ret = TreeView_DeleteAllItems(hTree);
326     ok(ret == TRUE, "ret\n");
327     ins.hParent = TVI_ROOT;
328     ins.hInsertAfter = TVI_ROOT;
329     U(ins).item.mask = TVIF_TEXT;
330     U(ins).item.pszText = LPSTR_TEXTCALLBACK;
331     hRoot = TreeView_InsertItem(hTree, &ins);
332     assert(hRoot);
333
334     tvi.hItem = hRoot;
335     tvi.mask = TVIF_TEXT;
336     tvi.pszText = buf;
337     tvi.cchTextMax = sizeof(buf)/sizeof(buf[0]);
338     ret = TreeView_GetItem(hTree, &tvi);
339     ok(ret == 1, "ret\n");
340     ok(strcmp(tvi.pszText, TEST_CALLBACK_TEXT) == 0, "Callback item text mismatch %s vs %s\n",
341         tvi.pszText, TEST_CALLBACK_TEXT);
342
343     ins.hParent = hRoot;
344     ins.hInsertAfter = TVI_FIRST;
345     U(ins).item.mask = TVIF_TEXT;
346     U(ins).item.pszText = test_string;
347     hItem1 = TreeView_InsertItem(hTree, &ins);
348     assert(hItem1);
349
350     tvi.hItem = hItem1;
351     ret = TreeView_GetItem(hTree, &tvi);
352     ok(ret == TRUE, "ret\n");
353     ok(strcmp(tvi.pszText, test_string) == 0, "Item text mismatch %s vs %s\n",
354         tvi.pszText, test_string);
355
356     /* undocumented: pszText of NULL also means LPSTR_CALLBACK: */
357     tvi.pszText = NULL;
358     ret = TreeView_SetItem(hTree, &tvi);
359     ok(ret == 1, "Expected SetItem return 1, got %ld\n", ret);
360     tvi.pszText = buf;
361     ret = TreeView_GetItem(hTree, &tvi);
362     ok(ret == TRUE, "Expected GetItem return TRUE, got %ld\n", ret);
363     ok(strcmp(tvi.pszText, TEST_CALLBACK_TEXT) == 0, "Item text mismatch %s vs %s\n",
364         tvi.pszText, TEST_CALLBACK_TEXT);
365
366     U(ins).item.pszText = NULL;
367     hItem2 = TreeView_InsertItem(hTree, &ins);
368     assert(hItem2);
369     tvi.hItem = hItem2;
370     memset(buf, 0, sizeof(buf));
371     ret = TreeView_GetItem(hTree, &tvi);
372     ok(ret == TRUE, "Expected GetItem return TRUE, got %ld\n", ret);
373     ok(strcmp(tvi.pszText, TEST_CALLBACK_TEXT) == 0, "Item text mismatch %s vs %s\n",
374         tvi.pszText, TEST_CALLBACK_TEXT);
375
376     DestroyWindow(hTree);
377 }
378
379 static void test_select(void)
380 {
381     BOOL r;
382
383     hTree = create_treeview_control();
384     fill_tree(hTree);
385
386     /* root-none select tests */
387     flush_sequences(MsgSequences, NUM_MSG_SEQUENCES);
388     r = TreeView_SelectItem(hTree, NULL);
389     Clear();
390     AddItem('1');
391     r = TreeView_SelectItem(hTree, hRoot);
392     AddItem('2');
393     r = TreeView_SelectItem(hTree, hRoot);
394     AddItem('3');
395     r = TreeView_SelectItem(hTree, NULL);
396     AddItem('4');
397     r = TreeView_SelectItem(hTree, NULL);
398     AddItem('5');
399     r = TreeView_SelectItem(hTree, hRoot);
400     AddItem('.');
401     ok(!strcmp(sequence, "1(nR)nR23(Rn)Rn45(nR)nR."), "root-none select test\n");
402     ok_sequence(MsgSequences, TREEVIEW_SEQ_INDEX, rootnone_select_seq,
403                 "root-none select seq", FALSE);
404
405     /* root-child select tests */
406     flush_sequences(MsgSequences, NUM_MSG_SEQUENCES);
407     r = TreeView_SelectItem(hTree, NULL);
408     Clear();
409     AddItem('1');
410     r = TreeView_SelectItem(hTree, hRoot);
411     AddItem('2');
412     r = TreeView_SelectItem(hTree, hRoot);
413     AddItem('3');
414     r = TreeView_SelectItem(hTree, hChild);
415     AddItem('4');
416     r = TreeView_SelectItem(hTree, hChild);
417     AddItem('5');
418     r = TreeView_SelectItem(hTree, hRoot);
419     AddItem('.');
420     ok(!strcmp(sequence, "1(nR)nR23(RC)RC45(CR)CR."), "root-child select test\n");
421     ok_sequence(MsgSequences, TREEVIEW_SEQ_INDEX, rootchild_select_seq,
422                 "root-child select seq", FALSE);
423
424     DestroyWindow(hTree);
425 }
426
427 static void test_getitemtext(void)
428 {
429     TVINSERTSTRUCTA ins;
430     HTREEITEM hChild;
431     TVITEM tvi;
432
433     CHAR szBuffer[80] = "Blah";
434     int nBufferSize = sizeof(szBuffer)/sizeof(CHAR);
435
436     hTree = create_treeview_control();
437     fill_tree(hTree);
438
439     flush_sequences(MsgSequences, NUM_MSG_SEQUENCES);
440
441     /* add an item without TVIF_TEXT mask and pszText == NULL */
442     ins.hParent = hRoot;
443     ins.hInsertAfter = TVI_ROOT;
444     U(ins).item.mask = 0;
445     U(ins).item.pszText = NULL;
446     U(ins).item.cchTextMax = 0;
447     hChild = TreeView_InsertItem(hTree, &ins);
448     assert(hChild);
449
450     /* retrieve it with TVIF_TEXT mask */
451     tvi.hItem = hChild;
452     tvi.mask = TVIF_TEXT;
453     tvi.cchTextMax = nBufferSize;
454     tvi.pszText = szBuffer;
455
456     SendMessageA( hTree, TVM_GETITEM, 0, (LPARAM)&tvi );
457     ok(!strcmp(szBuffer, ""), "szBuffer=\"%s\", expected \"\"\n", szBuffer);
458     ok(SendMessageA(hTree, TVM_DELETEITEM, 0, (LPARAM)hChild), "DeleteItem failed\n");
459     ok_sequence(MsgSequences, TREEVIEW_SEQ_INDEX, getitemtext_seq, "get item text seq", FALSE);
460
461     DestroyWindow(hTree);
462 }
463
464 static void test_focus(void)
465 {
466     TVINSERTSTRUCTA ins;
467     static CHAR child1[]  = "Edit",
468                 child2[]  = "A really long string";
469     HTREEITEM hChild1, hChild2;
470
471     hTree = create_treeview_control();
472     fill_tree(hTree);
473
474     flush_sequences(MsgSequences, NUM_MSG_SEQUENCES);
475
476     /* This test verifies that when a label is being edited, scrolling
477      * the treeview does not cause the label to lose focus. To test
478      * this, first some additional entries are added to generate
479      * scrollbars.
480      */
481     ins.hParent = hRoot;
482     ins.hInsertAfter = hChild;
483     U(ins).item.mask = TVIF_TEXT;
484     U(ins).item.pszText = child1;
485     hChild1 = TreeView_InsertItem(hTree, &ins);
486     assert(hChild1);
487     ins.hInsertAfter = hChild1;
488     U(ins).item.mask = TVIF_TEXT;
489     U(ins).item.pszText = child2;
490     hChild2 = TreeView_InsertItem(hTree, &ins);
491     assert(hChild2);
492
493     ShowWindow(hMainWnd,SW_SHOW);
494     SendMessageA(hTree, TVM_SELECTITEM, TVGN_CARET, (LPARAM)hChild);
495     hEdit = TreeView_EditLabel(hTree, hChild);
496     ScrollWindowEx(hTree, -10, 0, NULL, NULL, NULL, NULL, SW_SCROLLCHILDREN);
497     ok(GetFocus() == hEdit, "Edit control should have focus\n");
498     ok_sequence(MsgSequences, TREEVIEW_SEQ_INDEX, focus_seq, "focus test", TRUE);
499
500     DestroyWindow(hTree);
501 }
502
503 static void TestGetSetBkColor(void)
504 {
505     COLORREF crColor = RGB(0,0,0);
506
507     /* If the value is -1, the control is using the system color for the background color. */
508     crColor = (COLORREF)SendMessage( hTree, TVM_GETBKCOLOR, 0, 0 );
509     ok(crColor == -1, "Default background color reported as 0x%.8x\n", crColor);
510
511     /* Test for black background */
512     SendMessage( hTree, TVM_SETBKCOLOR, 0, (LPARAM)RGB(0,0,0) );
513     crColor = (COLORREF)SendMessage( hTree, TVM_GETBKCOLOR, 0, 0 );
514     ok(crColor == RGB(0,0,0), "Black background color reported as 0x%.8x\n", crColor);
515
516     /* Test for white background */
517     SendMessage( hTree, TVM_SETBKCOLOR, 0, (LPARAM)RGB(255,255,255) );
518     crColor = (COLORREF)SendMessage( hTree, TVM_GETBKCOLOR, 0, 0 );
519     ok(crColor == RGB(255,255,255), "White background color reported as 0x%.8x\n", crColor);
520
521     /* Reset the default background */
522     SendMessage( hTree, TVM_SETBKCOLOR, 0, -1 );
523 }
524
525 static void TestGetSetImageList(void)
526 {
527     HIMAGELIST hImageList = NULL;
528
529     /* Test a NULL HIMAGELIST */
530     SendMessage( hTree, TVM_SETIMAGELIST, TVSIL_NORMAL, (LPARAM)hImageList );
531     hImageList = (HIMAGELIST)SendMessage( hTree, TVM_GETIMAGELIST, TVSIL_NORMAL, 0 );
532     ok(hImageList == NULL, "NULL image list, reported as 0x%p, expected 0.\n", hImageList);
533
534     /* TODO: Test an actual image list */
535 }
536
537 static void TestGetSetIndent(void)
538 {
539     int ulIndent = -1;
540     int ulMinIndent = -1;
541     int ulMoreThanTwiceMin = -1;
542
543     /* Finding the minimum indent */
544     SendMessage( hTree, TVM_SETINDENT, 0, 0 );
545     ulMinIndent = (int)SendMessage( hTree, TVM_GETINDENT, 0, 0 );
546
547     /* Checking an indent that is more than twice the default indent */
548     ulMoreThanTwiceMin = 2*ulMinIndent+1;
549     SendMessage( hTree, TVM_SETINDENT, ulMoreThanTwiceMin, 0 );
550     ulIndent = (DWORD)SendMessage( hTree, TVM_GETINDENT, 0, 0 );
551     ok(ulIndent == ulMoreThanTwiceMin, "Indent reported as %d, expected %d\n", ulIndent, ulMoreThanTwiceMin);
552 }
553
554 static void TestGetSetInsertMarkColor(void)
555 {
556     COLORREF crColor = RGB(0,0,0);
557     SendMessage( hTree, TVM_SETINSERTMARKCOLOR, 0, crColor );
558     crColor = (COLORREF)SendMessage( hTree, TVM_GETINSERTMARKCOLOR, 0, 0 );
559     ok(crColor == RGB(0,0,0), "Insert mark color reported as 0x%.8x, expected 0x00000000\n", crColor);
560 }
561
562 static void TestGetSetItem(void)
563 {
564     TVITEM tviRoot = {0};
565     int nBufferSize = 80;
566     char szBuffer[80] = {0};
567
568     /* Test the root item */
569     tviRoot.hItem = hRoot;
570     tviRoot.mask = TVIF_TEXT;
571     tviRoot.cchTextMax = nBufferSize;
572     tviRoot.pszText = szBuffer;
573     SendMessage( hTree, TVM_GETITEM, 0, (LPARAM)&tviRoot );
574     ok(!strcmp("Root", szBuffer), "GetItem: szBuffer=\"%s\", expected \"Root\"\n", szBuffer);
575
576     /* Change the root text */
577     strncpy(szBuffer, "Testing123", nBufferSize);
578     SendMessage( hTree, TVM_SETITEM, 0, (LPARAM)&tviRoot );
579     memset(szBuffer, 0, nBufferSize);
580     SendMessage( hTree, TVM_GETITEM, 0, (LPARAM)&tviRoot );
581     ok(!strcmp("Testing123", szBuffer), "GetItem: szBuffer=\"%s\", expected \"Testing123\"\n", szBuffer);
582
583     /* Reset the root text */
584     memset(szBuffer, 0, nBufferSize);
585     strncpy(szBuffer, "Root", nBufferSize);
586     SendMessage( hTree, TVM_SETITEM, 0, (LPARAM)&tviRoot );
587 }
588
589 static void TestGetSetItemHeight(void)
590 {
591     int ulOldHeight = 0;
592     int ulNewHeight = 0;
593
594     /* Assuming default height to begin with */
595     ulOldHeight = (int) SendMessage( hTree, TVM_GETITEMHEIGHT, 0, 0 );
596
597     /* Explicitly setting and getting the default height */
598     SendMessage( hTree, TVM_SETITEMHEIGHT, -1, 0 );
599     ulNewHeight = (int) SendMessage( hTree, TVM_GETITEMHEIGHT, 0, 0 );
600     ok(ulNewHeight == ulOldHeight, "Default height not set properly, reported %d, expected %d\n", ulNewHeight, ulOldHeight);
601
602     /* Explicitly setting and getting the height of twice the normal */
603     SendMessage( hTree, TVM_SETITEMHEIGHT, 2*ulOldHeight, 0 );
604     ulNewHeight = (int) SendMessage( hTree, TVM_GETITEMHEIGHT, 0, 0 );
605     ok(ulNewHeight == 2*ulOldHeight, "New height not set properly, reported %d, expected %d\n", ulNewHeight, 2*ulOldHeight);
606
607     /* Assuming tree doesn't have TVS_NONEVENHEIGHT set, so a set of 9 will round down to 8 */
608     SendMessage( hTree, TVM_SETITEMHEIGHT, 9, 0 );
609     ulNewHeight = (int) SendMessage( hTree, TVM_GETITEMHEIGHT, 0, 0 );
610     ok(ulNewHeight == 8, "Uneven height not set properly, reported %d, expected %d\n", ulNewHeight, 8);
611 }
612
613 static void TestGetSetScrollTime(void)
614 {
615     int ulExpectedTime = 20;
616     int ulTime = 0;
617     SendMessage( hTree, TVM_SETSCROLLTIME, ulExpectedTime, 0 );
618     ulTime = (int)SendMessage( hTree, TVM_GETSCROLLTIME, 0, 0 );
619     ok(ulTime == ulExpectedTime, "Scroll time reported as %d, expected %d\n", ulTime, ulExpectedTime);
620 }
621
622 static void TestGetSetTextColor(void)
623 {
624     /* If the value is -1, the control is using the system color for the text color. */
625     COLORREF crColor = RGB(0,0,0);
626     crColor = (COLORREF)SendMessage( hTree, TVM_GETTEXTCOLOR, 0, 0 );
627     ok(crColor == -1, "Default text color reported as 0x%.8x\n", crColor);
628
629     /* Test for black text */
630     SendMessage( hTree, TVM_SETTEXTCOLOR, 0, (LPARAM)RGB(0,0,0) );
631     crColor = (COLORREF)SendMessage( hTree, TVM_GETTEXTCOLOR, 0, 0 );
632     ok(crColor == RGB(0,0,0), "Black text color reported as 0x%.8x\n", crColor);
633
634     /* Test for white text */
635     SendMessage( hTree, TVM_SETTEXTCOLOR, 0, (LPARAM)RGB(255,255,255) );
636     crColor = (COLORREF)SendMessage( hTree, TVM_GETTEXTCOLOR, 0, 0 );
637     ok(crColor == RGB(255,255,255), "White text color reported as 0x%.8x\n", crColor);
638
639     /* Reset the default text color */
640     SendMessage( hTree, TVM_SETTEXTCOLOR, 0, -1 );
641 }
642
643 static void TestGetSetToolTips(void)
644 {
645     HWND hwndLastToolTip = NULL;
646     HWND hPopupTreeView;
647
648     /* show even WS_POPUP treeview don't send NM_TOOLTIPSCREATED */
649     hPopupTreeView = CreateWindow(WC_TREEVIEW, NULL, WS_POPUP|WS_VISIBLE, 0, 0, 100, 100, hMainWnd, NULL, NULL, NULL);
650     DestroyWindow(hPopupTreeView);
651
652     /* Testing setting a NULL ToolTip */
653     SendMessage( hTree, TVM_SETTOOLTIPS, 0, 0 );
654     hwndLastToolTip = (HWND)SendMessage( hTree, TVM_GETTOOLTIPS, 0, 0 );
655     ok(hwndLastToolTip == NULL, "NULL tool tip, reported as 0x%p, expected 0.\n", hwndLastToolTip);
656
657     /* TODO: Add a test of an actual tooltip */
658 }
659
660 static void TestGetSetUnicodeFormat(void)
661 {
662     BOOL bPreviousSetting = 0;
663     BOOL bNewSetting = 0;
664
665     /* Set to Unicode */
666     bPreviousSetting = (BOOL)SendMessage( hTree, TVM_SETUNICODEFORMAT, 1, 0 );
667     bNewSetting = (BOOL)SendMessage( hTree, TVM_GETUNICODEFORMAT, 0, 0 );
668     ok(bNewSetting == 1, "Unicode setting did not work.\n");
669
670     /* Set to ANSI */
671     SendMessage( hTree, TVM_SETUNICODEFORMAT, 0, 0 );
672     bNewSetting = (BOOL)SendMessage( hTree, TVM_GETUNICODEFORMAT, 0, 0 );
673     ok(bNewSetting == 0, "ANSI setting did not work.\n");
674
675     /* Revert to original setting */
676     SendMessage( hTree, TVM_SETUNICODEFORMAT, (LPARAM)bPreviousSetting, 0 );
677 }
678
679 static void test_getset(void)
680 {
681     hTree = create_treeview_control();
682     fill_tree(hTree);
683
684     /* TVM_GETBKCOLOR and TVM_SETBKCOLOR */
685     flush_sequences(MsgSequences, NUM_MSG_SEQUENCES);
686     TestGetSetBkColor();
687     ok_sequence(MsgSequences, TREEVIEW_SEQ_INDEX, TestGetSetBkColorSeq,
688         "TestGetSetBkColor", FALSE);
689
690     /* TVM_GETIMAGELIST and TVM_SETIMAGELIST */
691     flush_sequences(MsgSequences, NUM_MSG_SEQUENCES);
692     TestGetSetImageList();
693     ok_sequence(MsgSequences, TREEVIEW_SEQ_INDEX, TestGetSetImageListSeq,
694         "TestGetImageList", FALSE);
695
696     /* TVM_SETINDENT and TVM_GETINDENT */
697     flush_sequences(MsgSequences, NUM_MSG_SEQUENCES);
698     TestGetSetIndent();
699     ok_sequence(MsgSequences, TREEVIEW_SEQ_INDEX, TestGetSetIndentSeq,
700         "TestGetSetIndent", FALSE);
701
702     /* TVM_GETINSERTMARKCOLOR and TVM_GETINSERTMARKCOLOR */
703     flush_sequences(MsgSequences, NUM_MSG_SEQUENCES);
704     TestGetSetInsertMarkColor();
705     ok_sequence(MsgSequences, TREEVIEW_SEQ_INDEX, TestGetSetInsertMarkColorSeq,
706         "TestGetSetInsertMarkColor", FALSE);
707
708     /* TVM_GETITEM and TVM_SETITEM */
709     flush_sequences(MsgSequences, NUM_MSG_SEQUENCES);
710     TestGetSetItem();
711     ok_sequence(MsgSequences, TREEVIEW_SEQ_INDEX, TestGetSetItemSeq,
712         "TestGetSetItem", FALSE);
713
714     /* TVM_GETITEMHEIGHT and TVM_SETITEMHEIGHT */
715     flush_sequences(MsgSequences, NUM_MSG_SEQUENCES);
716     TestGetSetItemHeight();
717     ok_sequence(MsgSequences, TREEVIEW_SEQ_INDEX, TestGetSetItemHeightSeq,
718         "TestGetSetItemHeight", FALSE);
719
720     /* TVM_GETSCROLLTIME and TVM_SETSCROLLTIME */
721     flush_sequences(MsgSequences, NUM_MSG_SEQUENCES);
722     TestGetSetScrollTime();
723     ok_sequence(MsgSequences, TREEVIEW_SEQ_INDEX, TestGetSetScrollTimeSeq,
724         "TestGetSetScrollTime", FALSE);
725
726     /* TVM_GETTEXTCOLOR and TVM_SETTEXTCOLOR */
727     flush_sequences(MsgSequences, NUM_MSG_SEQUENCES);
728     TestGetSetTextColor();
729     ok_sequence(MsgSequences, TREEVIEW_SEQ_INDEX, TestGetSetTextColorSeq,
730         "TestGetSetTextColor", FALSE);
731
732     /* TVM_GETTOOLTIPS and TVM_SETTOOLTIPS */
733     flush_sequences(MsgSequences, NUM_MSG_SEQUENCES);
734     TestGetSetToolTips();
735     ok_sequence(MsgSequences, TREEVIEW_SEQ_INDEX, TestGetSetToolTipsSeq,
736         "TestGetSetToolTips", TRUE);
737
738     /* TVM_GETUNICODEFORMAT and TVM_SETUNICODEFORMAT */
739     flush_sequences(MsgSequences, NUM_MSG_SEQUENCES);
740     TestGetSetUnicodeFormat();
741     ok_sequence(MsgSequences, TREEVIEW_SEQ_INDEX, TestGetSetUnicodeFormatSeq,
742         "TestGetSetUnicodeFormat", FALSE);
743
744     DestroyWindow(hTree);
745 }
746
747 static LRESULT CALLBACK MyWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
748 {
749     switch(msg) {
750     case WM_NOTIFY:
751     {
752         NMHDR *pHdr = (NMHDR *)lParam;
753     
754         ok(pHdr->code != NM_FIRST - 19, "Treeview should not send NM_TOOLTIPSCREATED\n");
755         if (pHdr->idFrom == 100) {
756             NMTREEVIEWA *pTreeView = (LPNMTREEVIEWA) lParam;
757             switch(pHdr->code) {
758             case TVN_SELCHANGINGA:
759                 AddItem('(');
760                 IdentifyItem(pTreeView->itemOld.hItem);
761                 IdentifyItem(pTreeView->itemNew.hItem);
762                 return 0;
763             case TVN_SELCHANGEDA:
764                 AddItem(')');
765                 IdentifyItem(pTreeView->itemOld.hItem);
766                 IdentifyItem(pTreeView->itemNew.hItem);
767                 return 0;
768             case TVN_GETDISPINFOA: {
769                 NMTVDISPINFOA *disp = (NMTVDISPINFOA *)lParam;
770                 if (disp->item.mask & TVIF_TEXT) {
771                     lstrcpyn(disp->item.pszText, TEST_CALLBACK_TEXT, disp->item.cchTextMax);
772                 }
773                 return 0;
774               }
775             case TVN_ENDLABELEDIT: return TRUE;
776             }
777         }
778         return 0;
779     }
780   
781     case WM_DESTROY:
782         PostQuitMessage(0);
783         break;
784   
785     default:
786         return DefWindowProcA(hWnd, msg, wParam, lParam);
787     }
788     return 0L;
789 }
790
791 static void test_expandinvisible(void)
792 {
793     static CHAR nodeText[][5] = {"0", "1", "2", "3", "4"};
794     TVINSERTSTRUCTA ins;
795     HTREEITEM node[5];
796     RECT dummyRect;
797     BOOL nodeVisible;
798     LRESULT ret;
799
800     hTree = create_treeview_control();
801
802     /* The test builds the following tree and expands then node 1, while node 0 is collapsed.
803      *
804      * 0
805      * |- 1
806      * |  |- 2
807      * |  |- 3
808      * |- 4
809      *
810      */
811
812     ret = TreeView_DeleteAllItems(hTree);
813     ok(ret == TRUE, "ret\n");
814     ins.hParent = TVI_ROOT;
815     ins.hInsertAfter = TVI_ROOT;
816     U(ins).item.mask = TVIF_TEXT;
817     U(ins).item.pszText = nodeText[0];
818     node[0] = TreeView_InsertItem(hTree, &ins);
819     assert(node[0]);
820
821     ins.hInsertAfter = TVI_LAST;
822     U(ins).item.mask = TVIF_TEXT;
823     ins.hParent = node[0];
824
825     U(ins).item.pszText = nodeText[1];
826     node[1] = TreeView_InsertItem(hTree, &ins);
827     assert(node[1]);
828     U(ins).item.pszText = nodeText[4];
829     node[4] = TreeView_InsertItem(hTree, &ins);
830     assert(node[4]);
831
832     ins.hParent = node[1];
833
834     U(ins).item.pszText = nodeText[2];
835     node[2] = TreeView_InsertItem(hTree, &ins);
836     assert(node[2]);
837     U(ins).item.pszText = nodeText[3];
838     node[3] = TreeView_InsertItem(hTree, &ins);
839     assert(node[3]);
840
841
842     nodeVisible = TreeView_GetItemRect(hTree, node[1], &dummyRect, FALSE);
843     ok(!nodeVisible, "Node 1 should not be visible.\n");
844     nodeVisible = TreeView_GetItemRect(hTree, node[2], &dummyRect, FALSE);
845     ok(!nodeVisible, "Node 2 should not be visible.\n");
846     nodeVisible = TreeView_GetItemRect(hTree, node[3], &dummyRect, FALSE);
847     ok(!nodeVisible, "Node 3 should not be visible.\n");
848     nodeVisible = TreeView_GetItemRect(hTree, node[4], &dummyRect, FALSE);
849     ok(!nodeVisible, "Node 4 should not be visible.\n");
850
851     ok(TreeView_Expand(hTree, node[1], TVE_EXPAND), "Expand of node 1 failed.\n");
852
853     nodeVisible = TreeView_GetItemRect(hTree, node[1], &dummyRect, FALSE);
854     ok(!nodeVisible, "Node 1 should not be visible.\n");
855     nodeVisible = TreeView_GetItemRect(hTree, node[2], &dummyRect, FALSE);
856     ok(!nodeVisible, "Node 2 should not be visible.\n");
857     nodeVisible = TreeView_GetItemRect(hTree, node[3], &dummyRect, FALSE);
858     ok(!nodeVisible, "Node 3 should not be visible.\n");
859     nodeVisible = TreeView_GetItemRect(hTree, node[4], &dummyRect, FALSE);
860     ok(!nodeVisible, "Node 4 should not be visible.\n");
861
862     DestroyWindow(hTree);
863 }
864
865 static void test_itemedit(void)
866 {
867     DWORD r;
868     HWND edit;
869     TVITEMA item;
870     CHAR buff[2];
871
872     hTree = create_treeview_control();
873     fill_tree(hTree);
874
875     /* try with null item */
876     edit = (HWND)SendMessage(hTree, TVM_EDITLABEL, 0, (LPARAM)NULL);
877     ok(!IsWindow(edit), "Expected valid handle\n");
878
879     /* trigger edit */
880     edit = (HWND)SendMessage(hTree, TVM_EDITLABEL, 0, (LPARAM)hRoot);
881     ok(IsWindow(edit), "Expected valid handle\n");
882     /* item shouldn't be selected automatically after TVM_EDITLABEL */
883     r = SendMessage(hTree, TVM_GETITEMSTATE, (WPARAM)hRoot, TVIS_SELECTED);
884     expect(0, r);
885     /* try to cancel with wrong edit handle */
886     r = SendMessage(hTree, WM_COMMAND, MAKEWPARAM(0, EN_KILLFOCUS), (LPARAM)NULL);
887     expect(0, r);
888     ok(IsWindow(edit), "Expected edit control to be valid\n");
889     r = SendMessage(hTree, WM_COMMAND, MAKEWPARAM(0, EN_KILLFOCUS), (LPARAM)edit);
890     expect(0, r);
891     ok(!IsWindow(edit), "Expected edit control to be destroyed\n");
892     /* try to cancel without creating edit */
893     r = SendMessage(hTree, WM_COMMAND, MAKEWPARAM(0, EN_KILLFOCUS), (LPARAM)NULL);
894     expect(0, r);
895
896     /* try to cancel with wrong (not null) handle */
897     edit = (HWND)SendMessage(hTree, TVM_EDITLABEL, 0, (LPARAM)hRoot);
898     ok(IsWindow(edit), "Expected valid handle\n");
899     r = SendMessage(hTree, WM_COMMAND, MAKEWPARAM(0, EN_KILLFOCUS), (LPARAM)hTree);
900     expect(0, r);
901     ok(IsWindow(edit), "Expected edit control to be valid\n");
902     r = SendMessage(hTree, WM_COMMAND, MAKEWPARAM(0, EN_KILLFOCUS), (LPARAM)edit);
903     expect(0, r);
904
905     /* remove selection after starting edit */
906     r = TreeView_SelectItem(hTree, hRoot);
907     expect(TRUE, r);
908     edit = (HWND)SendMessage(hTree, TVM_EDITLABEL, 0, (LPARAM)hRoot);
909     ok(IsWindow(edit), "Expected valid handle\n");
910     r = TreeView_SelectItem(hTree, NULL);
911     expect(TRUE, r);
912     /* alter text */
913     strncpy(buff, "x", sizeof(buff)/sizeof(CHAR));
914     r = SendMessage(edit, WM_SETTEXT, 0, (LPARAM)buff);
915     expect(TRUE, r);
916     r = SendMessage(hTree, WM_COMMAND, MAKEWPARAM(0, EN_KILLFOCUS), (LPARAM)edit);
917     expect(0, r);
918     ok(!IsWindow(edit), "Expected edit control to be destroyed\n");
919     /* check that text is saved */
920     item.mask = TVIF_TEXT;
921     item.hItem = hRoot;
922     item.pszText = buff;
923     item.cchTextMax = sizeof(buff)/sizeof(CHAR);
924     r = SendMessage(hTree, TVM_GETITEM, 0, (LPARAM)&item);
925     expect(TRUE, r);
926     ok(!strcmp("x", buff), "Expected item text to change\n");
927
928     DestroyWindow(hTree);
929 }
930
931 START_TEST(treeview)
932 {
933     HMODULE hComctl32;
934     BOOL (WINAPI *pInitCommonControlsEx)(const INITCOMMONCONTROLSEX*);
935     WNDCLASSA wc;
936     MSG msg;
937   
938     hComctl32 = GetModuleHandleA("comctl32.dll");
939     pInitCommonControlsEx = (void*)GetProcAddress(hComctl32, "InitCommonControlsEx");
940     if (pInitCommonControlsEx)
941     {
942         INITCOMMONCONTROLSEX iccex;
943         iccex.dwSize = sizeof(iccex);
944         iccex.dwICC  = ICC_TREEVIEW_CLASSES;
945         pInitCommonControlsEx(&iccex);
946     }
947     else
948         InitCommonControls();
949
950     init_msg_sequences(MsgSequences, NUM_MSG_SEQUENCES);
951   
952     wc.style = CS_HREDRAW | CS_VREDRAW;
953     wc.cbClsExtra = 0;
954     wc.cbWndExtra = 0;
955     wc.hInstance = GetModuleHandleA(NULL);
956     wc.hIcon = NULL;
957     wc.hCursor = LoadCursorA(NULL, IDC_IBEAM);
958     wc.hbrBackground = GetSysColorBrush(COLOR_WINDOW);
959     wc.lpszMenuName = NULL;
960     wc.lpszClassName = "MyTestWnd";
961     wc.lpfnWndProc = MyWndProc;
962     RegisterClassA(&wc);
963
964
965     hMainWnd = CreateWindowExA(0, "MyTestWnd", "Blah", WS_OVERLAPPEDWINDOW,
966       CW_USEDEFAULT, CW_USEDEFAULT, 130, 105, NULL, NULL, GetModuleHandleA(NULL), 0);
967
968     if ( !ok(hMainWnd != NULL, "Failed to create parent window. Tests aborted.\n") )
969         return;
970
971     test_fillroot();
972     test_select();
973     test_getitemtext();
974     test_focus();
975     test_getset();
976     test_callback();
977     test_expandinvisible();
978     test_itemedit();
979
980     PostMessageA(hMainWnd, WM_CLOSE, 0, 0);
981     while(GetMessageA(&msg,0,0,0)) {
982         TranslateMessage(&msg);
983         DispatchMessageA(&msg);
984     }
985 }