[COMCTL32_WINETEST] Sync with Wine Staging 2.2. CORE-12823
[reactos.git] / rostests / winetests / comctl32 / 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 <stdarg.h>
22 #include <stdio.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 "wine/commctrl.h"
31
32 #include "wine/test.h"
33 #include "v6util.h"
34 #include "msg.h"
35
36 static const char *TEST_CALLBACK_TEXT = "callback_text";
37
38 static TVITEMA g_item_expanding, g_item_expanded;
39 static BOOL g_get_from_expand;
40 static BOOL g_get_rect_in_expand;
41 static BOOL g_disp_A_to_W;
42 static BOOL g_disp_set_stateimage;
43 static BOOL g_beginedit_alter_text;
44 static HFONT g_customdraw_font;
45 static BOOL g_v6;
46
47 #define NUM_MSG_SEQUENCES 3
48 #define TREEVIEW_SEQ_INDEX 0
49 #define PARENT_SEQ_INDEX 1
50 #define PARENT_CD_SEQ_INDEX 2
51
52 #define expect(expected, got) ok(got == expected, "Expected %d, got %d\n", expected, got)
53
54 static struct msg_sequence *sequences[NUM_MSG_SEQUENCES];
55 static struct msg_sequence *item_sequence[1];
56
57 static const struct message FillRootSeq[] = {
58 { TVM_INSERTITEMA, sent },
59 { TVM_INSERTITEMA, sent },
60 { 0 }
61 };
62
63 static const struct message rootnone_select_seq[] = {
64 { TVM_SELECTITEM, sent|wparam, 9 },
65 { TVM_SELECTITEM, sent|wparam, 9 },
66 { TVM_SELECTITEM, sent|wparam, 9 },
67 { TVM_SELECTITEM, sent|wparam, 9 },
68 { TVM_SELECTITEM, sent|wparam, 9 },
69 { TVM_SELECTITEM, sent|wparam, 9 },
70 { 0 }
71 };
72
73 static const struct message rootchild_select_seq[] = {
74 { TVM_SELECTITEM, sent|wparam, 9 },
75 { TVM_SELECTITEM, sent|wparam, 9 },
76 { TVM_SELECTITEM, sent|wparam, 9 },
77 { TVM_SELECTITEM, sent|wparam, 9 },
78 { TVM_SELECTITEM, sent|wparam, 9 },
79 { TVM_SELECTITEM, sent|wparam, 9 },
80 { 0 }
81 };
82
83 static const struct message getitemtext_seq[] = {
84 { TVM_INSERTITEMA, sent },
85 { TVM_GETITEMA, sent },
86 { TVM_DELETEITEM, sent },
87 { 0 }
88 };
89
90 static const struct message focus_seq[] = {
91 { TVM_INSERTITEMA, sent },
92 { TVM_INSERTITEMA, sent },
93 { TVM_SELECTITEM, sent|wparam, 9 },
94 /* The following end up out of order in wine */
95 { WM_WINDOWPOSCHANGING, sent|defwinproc },
96 { WM_NCCALCSIZE, sent|wparam|defwinproc, TRUE },
97 { WM_WINDOWPOSCHANGED, sent|defwinproc },
98 { WM_SIZE, sent|defwinproc },
99 { WM_WINDOWPOSCHANGING, sent|defwinproc|optional },
100 { WM_NCCALCSIZE, sent|wparam|defwinproc|optional, TRUE },
101 { WM_WINDOWPOSCHANGED, sent|defwinproc|optional },
102 { WM_SIZE, sent|defwinproc|optional },
103 { WM_PAINT, sent|defwinproc },
104 { WM_NCPAINT, sent|wparam|defwinproc, 1 },
105 { WM_ERASEBKGND, sent|defwinproc },
106 { TVM_EDITLABELA, sent },
107 { WM_COMMAND, sent|wparam|defwinproc, MAKEWPARAM(0, EN_UPDATE) },
108 { WM_COMMAND, sent|wparam|defwinproc, MAKEWPARAM(0, EN_CHANGE) },
109 { WM_PARENTNOTIFY, sent|wparam|defwinproc, MAKEWPARAM(WM_CREATE, 0) },
110 { WM_KILLFOCUS, sent|defwinproc },
111 { WM_PAINT, sent|defwinproc },
112 { WM_IME_SETCONTEXT, sent|defwinproc|optional },
113 { WM_COMMAND, sent|wparam|defwinproc, MAKEWPARAM(0, EN_SETFOCUS) },
114 { WM_ERASEBKGND, sent|defwinproc|optional },
115 { WM_CTLCOLOREDIT, sent|defwinproc|optional },
116 { WM_CTLCOLOREDIT, sent|defwinproc|optional },
117 { 0 }
118 };
119
120 static const struct message test_get_set_bkcolor_seq[] = {
121 { TVM_GETBKCOLOR, sent|wparam|lparam, 0, 0 },
122 { TVM_SETBKCOLOR, sent|wparam|lparam, 0, 0 },
123 { TVM_GETBKCOLOR, sent|wparam|lparam, 0, 0 },
124 { TVM_SETBKCOLOR, sent|wparam|lparam, 0, 0x00ffffff },
125 { TVM_GETBKCOLOR, sent|wparam|lparam, 0, 0 },
126 { TVM_SETBKCOLOR, sent|wparam|lparam, 0, -1 },
127 { 0 }
128 };
129
130 static const struct message test_get_set_imagelist_seq[] = {
131 { TVM_SETIMAGELIST, sent|wparam|lparam, 0, 0 },
132 { TVM_GETIMAGELIST, sent|wparam|lparam, 0, 0 },
133 { 0 }
134 };
135
136 static const struct message test_get_set_indent_seq[] = {
137 { TVM_SETINDENT, sent|wparam|lparam, 0, 0 },
138 { TVM_GETINDENT, sent|wparam|lparam, 0, 0 },
139 /* The actual amount to indent is dependent on the system for this message */
140 { TVM_SETINDENT, sent },
141 { TVM_GETINDENT, sent|wparam|lparam, 0, 0 },
142 { 0 }
143 };
144
145 static const struct message test_get_set_insertmarkcolor_seq[] = {
146 { TVM_SETINSERTMARKCOLOR, sent|wparam|lparam, 0, 0 },
147 { TVM_GETINSERTMARKCOLOR, sent|wparam|lparam, 0, 0 },
148 { 0 }
149 };
150
151 static const struct message test_get_set_item_seq[] = {
152 { TVM_GETITEMA, sent },
153 { TVM_SETITEMA, sent },
154 { TVM_GETITEMA, sent },
155 { TVM_SETITEMA, sent },
156 { 0 }
157 };
158
159 static const struct message test_get_set_itemheight_seq[] = {
160 { TVM_GETITEMHEIGHT, sent|wparam|lparam, 0, 0 },
161 { TVM_SETITEMHEIGHT, sent|wparam|lparam, -1, 0 },
162 { TVM_GETITEMHEIGHT, sent|wparam|lparam, 0, 0 },
163 { TVM_SETITEMHEIGHT, sent|lparam, 0xcccccccc, 0 },
164 { TVM_GETITEMHEIGHT, sent|wparam|lparam|optional, 0, 0 },
165 { TVM_SETITEMHEIGHT, sent|wparam|lparam|optional, 9, 0 },
166 { TVM_GETITEMHEIGHT, sent|wparam|lparam, 0, 0 },
167 { 0 }
168 };
169
170 static const struct message test_get_set_scrolltime_seq[] = {
171 { TVM_SETSCROLLTIME, sent|wparam|lparam, 20, 0 },
172 { TVM_GETSCROLLTIME, sent|wparam|lparam, 0, 0 },
173 { 0 }
174 };
175
176 static const struct message test_get_set_textcolor_seq[] = {
177 { TVM_GETTEXTCOLOR, sent|wparam|lparam, 0, 0 },
178 { TVM_SETTEXTCOLOR, sent|wparam|lparam, 0, 0 },
179 { TVM_GETTEXTCOLOR, sent|wparam|lparam, 0, 0 },
180 { TVM_SETTEXTCOLOR, sent|wparam|lparam, 0, RGB(255, 255, 255) },
181 { TVM_GETTEXTCOLOR, sent|wparam|lparam, 0, 0 },
182 { TVM_SETTEXTCOLOR, sent|wparam|lparam, 0, CLR_NONE },
183 { 0 }
184 };
185
186 static const struct message test_get_set_tooltips_seq[] = {
187 { WM_KILLFOCUS, sent },
188 { WM_IME_SETCONTEXT, sent|optional },
189 { WM_IME_NOTIFY, sent|optional },
190 { TVM_SETTOOLTIPS, sent|wparam|lparam, 0, 0 },
191 { TVM_GETTOOLTIPS, sent|wparam|lparam, 0, 0 },
192 { 0 }
193 };
194
195 static const struct message test_get_set_unicodeformat_seq[] = {
196 { TVM_SETUNICODEFORMAT, sent|wparam|lparam, TRUE, 0 },
197 { TVM_GETUNICODEFORMAT, sent|wparam|lparam, 0, 0 },
198 { TVM_SETUNICODEFORMAT, sent|wparam|lparam, 0, 0 },
199 { TVM_GETUNICODEFORMAT, sent|wparam|lparam, 0, 0 },
200 { TVM_SETUNICODEFORMAT, sent|wparam|lparam, 0, 0 },
201 { 0 }
202 };
203
204 static const struct message parent_expand_seq[] = {
205 { WM_NOTIFY, sent|id, 0, 0, TVN_ITEMEXPANDINGA },
206 { WM_NOTIFY, sent|id, 0, 0, TVN_ITEMEXPANDEDA },
207 { 0 }
208 };
209
210 static const struct message parent_expand_kb_seq[] = {
211 { WM_NOTIFY, sent|id, 0, 0, TVN_KEYDOWN },
212 { WM_NOTIFY, sent|id, 0, 0, TVN_ITEMEXPANDINGA },
213 { WM_NOTIFY, sent|id, 0, 0, TVN_ITEMEXPANDEDA },
214 { WM_CHANGEUISTATE, sent|optional },
215 { 0 }
216 };
217
218 static const struct message parent_collapse_2nd_kb_seq[] = {
219 { WM_NOTIFY, sent|id|optional, 0, 0, TVN_KEYDOWN },
220 { WM_NOTIFY, sent|id, 0, 0, TVN_ITEMEXPANDINGA },
221 { WM_CHANGEUISTATE, sent|optional },
222 { 0 }
223 };
224
225 static const struct message parent_expand_empty_kb_seq[] = {
226 { WM_NOTIFY, sent|id, 0, 0, TVN_KEYDOWN },
227 { WM_CHANGEUISTATE, sent|optional },
228 { 0 }
229 };
230
231 static const struct message parent_singleexpand_seq0[] = {
232 /* alpha expands */
233 { WM_NOTIFY, sent|id, 0, 0, TVN_SELCHANGINGA },
234 { WM_NOTIFY, sent|id, 0, 0, TVN_SELCHANGEDA },
235 { WM_NOTIFY, sent|id, 0, 0, TVN_SINGLEEXPAND },
236 { WM_NOTIFY, sent|id, 0, 0, TVN_ITEMEXPANDINGA },
237 { WM_NOTIFY, sent|id, 0, 0, TVN_ITEMEXPANDEDA },
238 { 0 }
239 };
240
241 static const struct message parent_singleexpand_seq1[] = {
242 /* bravo expands */
243 { WM_NOTIFY, sent|id, 0, 0, TVN_SELCHANGINGA },
244 { WM_NOTIFY, sent|id, 0, 0, TVN_SELCHANGEDA },
245 { WM_NOTIFY, sent|id, 0, 0, TVN_SINGLEEXPAND },
246 { WM_NOTIFY, sent|id, 0, 0, TVN_ITEMEXPANDINGA },
247 { WM_NOTIFY, sent|id, 0, 0, TVN_ITEMEXPANDEDA },
248 { 0 }
249 };
250
251 static const struct message parent_singleexpand_seq2[] = {
252 /* delta expands, bravo collapses */
253 { WM_NOTIFY, sent|id, 0, 0, TVN_SELCHANGINGA },
254 { WM_NOTIFY, sent|id, 0, 0, TVN_SELCHANGEDA },
255 { WM_NOTIFY, sent|id, 0, 0, TVN_SINGLEEXPAND },
256 { WM_NOTIFY, sent|id, 0, 0, TVN_ITEMEXPANDINGA },
257 { WM_NOTIFY, sent|id, 0, 0, TVN_ITEMEXPANDEDA },
258 { WM_NOTIFY, sent|id, 0, 0, TVN_ITEMEXPANDINGA },
259 { WM_NOTIFY, sent|id, 0, 0, TVN_ITEMEXPANDEDA },
260 { 0 }
261 };
262
263 static const struct message parent_singleexpand_seq3[] = {
264 /* foxtrot expands, alpha and delta collapse */
265 { WM_NOTIFY, sent|id, 0, 0, TVN_SELCHANGINGA },
266 { WM_NOTIFY, sent|id, 0, 0, TVN_SELCHANGEDA },
267 { WM_NOTIFY, sent|id, 0, 0, TVN_SINGLEEXPAND },
268 { WM_NOTIFY, sent|id, 0, 0, TVN_ITEMEXPANDINGA },
269 { WM_NOTIFY, sent|id, 0, 0, TVN_ITEMEXPANDEDA },
270 { WM_NOTIFY, sent|id, 0, 0, TVN_ITEMEXPANDINGA },
271 { WM_NOTIFY, sent|id, 0, 0, TVN_ITEMEXPANDEDA },
272 { WM_NOTIFY, sent|id, 0, 0, TVN_ITEMEXPANDINGA },
273 { WM_NOTIFY, sent|id, 0, 0, TVN_ITEMEXPANDEDA },
274 { 0 }
275 };
276
277 static const struct message parent_singleexpand_seq4[] = {
278 /* alpha expands, foxtrot collapses */
279 { WM_NOTIFY, sent|id, 0, 0, TVN_SELCHANGINGA },
280 { WM_NOTIFY, sent|id, 0, 0, TVN_SELCHANGEDA },
281 { WM_NOTIFY, sent|id, 0, 0, TVN_SINGLEEXPAND },
282 { WM_NOTIFY, sent|id, 0, 0, TVN_ITEMEXPANDINGA },
283 { WM_NOTIFY, sent|id, 0, 0, TVN_ITEMEXPANDEDA },
284 { WM_NOTIFY, sent|id, 0, 0, TVN_ITEMEXPANDINGA },
285 { WM_NOTIFY, sent|id, 0, 0, TVN_ITEMEXPANDEDA },
286 { 0 }
287 };
288
289 static const struct message parent_singleexpand_seq5[] = {
290 /* foxtrot expands while golf is selected, then golf expands and alpha collapses */
291 { WM_NOTIFY, sent|id, 0, 0, TVN_SELCHANGINGA },
292 { WM_NOTIFY, sent|id, 0, 0, TVN_ITEMEXPANDINGA },
293 { WM_NOTIFY, sent|id, 0, 0, TVN_ITEMEXPANDEDA },
294 { WM_NOTIFY, sent|id, 0, 0, TVN_SELCHANGEDA },
295 { WM_NOTIFY, sent|id, 0, 0, TVN_SINGLEEXPAND },
296 { WM_NOTIFY, sent|id, 0, 0, TVN_ITEMEXPANDINGA },
297 { WM_NOTIFY, sent|id, 0, 0, TVN_ITEMEXPANDEDA },
298 { WM_NOTIFY, sent|id, 0, 0, TVN_ITEMEXPANDINGA },
299 { WM_NOTIFY, sent|id, 0, 0, TVN_ITEMEXPANDEDA },
300 { 0 }
301 };
302
303 static const struct message parent_singleexpand_seq6[] = {
304 /* hotel does not expand and india does not collapse because they have no children */
305 { WM_NOTIFY, sent|id, 0, 0, TVN_SELCHANGINGA },
306 { WM_NOTIFY, sent|id, 0, 0, TVN_SELCHANGEDA },
307 { WM_NOTIFY, sent|id, 0, 0, TVN_SINGLEEXPAND },
308 { 0 }
309 };
310
311 static const struct message parent_singleexpand_seq7[] = {
312 /* india does not expand and hotel does not collapse because they have no children */
313 { WM_NOTIFY, sent|id, 0, 0, TVN_SELCHANGINGA },
314 { WM_NOTIFY, sent|id, 0, 0, TVN_SELCHANGEDA },
315 { WM_NOTIFY, sent|id, 0, 0, TVN_SINGLEEXPAND },
316 { 0 }
317 };
318
319 static const struct message parent_get_dispinfo_seq[] = {
320 { WM_NOTIFY, sent|id, 0, 0, TVN_GETDISPINFOA },
321 { 0 }
322 };
323
324 static const struct message empty_seq[] = {
325 { 0 }
326 };
327
328 static const struct message parent_cd_seq[] = {
329 { WM_NOTIFY, sent|id|custdraw, 0, 0, NM_CUSTOMDRAW, CDDS_PREPAINT },
330 { WM_NOTIFY, sent|id|custdraw, 0, 0, NM_CUSTOMDRAW, CDDS_ITEMPREPAINT },
331 { WM_NOTIFY, sent|id|custdraw, 0, 0, NM_CUSTOMDRAW, CDDS_ITEMPOSTPAINT },
332 { WM_NOTIFY, sent|id|custdraw, 0, 0, NM_CUSTOMDRAW, CDDS_ITEMPREPAINT },
333 { WM_NOTIFY, sent|id|custdraw, 0, 0, NM_CUSTOMDRAW, CDDS_ITEMPOSTPAINT },
334 { WM_NOTIFY, sent|id|custdraw, 0, 0, NM_CUSTOMDRAW, CDDS_POSTPAINT },
335 { 0 }
336 };
337
338 static const struct message parent_vk_return_seq[] = {
339 { WM_NOTIFY, sent|id, 0, 0, TVN_KEYDOWN },
340 { WM_NOTIFY, sent|id, 0, 0, NM_RETURN },
341 { WM_CHANGEUISTATE, sent|optional },
342 { 0 }
343 };
344
345 static HWND hMainWnd;
346
347 static HTREEITEM hRoot, hChild;
348
349 static int pos = 0;
350 static char sequence[256];
351
352 static void Clear(void)
353 {
354 pos = 0;
355 sequence[0] = '\0';
356 }
357
358 static void AddItem(char ch)
359 {
360 sequence[pos++] = ch;
361 sequence[pos] = '\0';
362 }
363
364 static void IdentifyItem(HTREEITEM hItem)
365 {
366 if (hItem == hRoot) {
367 AddItem('R');
368 return;
369 }
370 if (hItem == hChild) {
371 AddItem('C');
372 return;
373 }
374 if (hItem == NULL) {
375 AddItem('n');
376 return;
377 }
378 AddItem('?');
379 }
380
381 /* This function hooks in and records all messages to the treeview control */
382 static LRESULT WINAPI TreeviewWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
383 {
384 static LONG defwndproc_counter = 0;
385 LRESULT ret;
386 struct message msg;
387 WNDPROC lpOldProc = (WNDPROC)GetWindowLongPtrA(hwnd, GWLP_USERDATA);
388
389 msg.message = message;
390 msg.flags = sent|wparam|lparam;
391 if (defwndproc_counter) msg.flags |= defwinproc;
392 msg.wParam = wParam;
393 msg.lParam = lParam;
394 msg.id = 0;
395 add_message(sequences, TREEVIEW_SEQ_INDEX, &msg);
396
397 defwndproc_counter++;
398 ret = CallWindowProcA(lpOldProc, hwnd, message, wParam, lParam);
399 defwndproc_counter--;
400
401 return ret;
402 }
403
404 static HWND create_treeview_control(DWORD style)
405 {
406 WNDPROC pOldWndProc;
407 HWND hTree;
408
409 hTree = CreateWindowExA(WS_EX_CLIENTEDGE, WC_TREEVIEWA, NULL, WS_CHILD|WS_VISIBLE|
410 TVS_LINESATROOT|TVS_HASLINES|TVS_HASBUTTONS|TVS_EDITLABELS|style,
411 0, 0, 120, 100, hMainWnd, (HMENU)100, GetModuleHandleA(0), 0);
412
413 SetFocus(hTree);
414
415 /* Record the old WNDPROC so we can call it after recording the messages */
416 pOldWndProc = (WNDPROC)SetWindowLongPtrA(hTree, GWLP_WNDPROC, (LONG_PTR)TreeviewWndProc);
417 SetWindowLongPtrA(hTree, GWLP_USERDATA, (LONG_PTR)pOldWndProc);
418
419 return hTree;
420 }
421
422 static void fill_tree(HWND hTree)
423 {
424 TVINSERTSTRUCTA ins;
425 static CHAR root[] = "Root",
426 child[] = "Child";
427
428 ins.hParent = TVI_ROOT;
429 ins.hInsertAfter = TVI_ROOT;
430 U(ins).item.mask = TVIF_TEXT;
431 U(ins).item.pszText = root;
432 hRoot = TreeView_InsertItemA(hTree, &ins);
433
434 ins.hParent = hRoot;
435 ins.hInsertAfter = TVI_FIRST;
436 U(ins).item.mask = TVIF_TEXT;
437 U(ins).item.pszText = child;
438 hChild = TreeView_InsertItemA(hTree, &ins);
439 }
440
441 static void test_fillroot(void)
442 {
443 TVITEMA tvi;
444 HWND hTree;
445
446 hTree = create_treeview_control(0);
447
448 flush_sequences(sequences, NUM_MSG_SEQUENCES);
449
450 fill_tree(hTree);
451
452 Clear();
453 AddItem('A');
454 ok(hRoot != NULL, "failed to set root\n");
455 AddItem('B');
456 ok(hChild != NULL, "failed to set child\n");
457 AddItem('.');
458 ok_sequence(sequences, TREEVIEW_SEQ_INDEX, FillRootSeq, "FillRoot", FALSE);
459 ok(!strcmp(sequence, "AB."), "Item creation\n");
460
461 /* UMLPad 1.15 depends on this being not -1 (I_IMAGECALLBACK) */
462 tvi.hItem = hRoot;
463 tvi.mask = TVIF_IMAGE | TVIF_SELECTEDIMAGE;
464 SendMessageA(hTree, TVM_GETITEMA, 0, (LPARAM)&tvi);
465 ok(tvi.iImage == 0, "tvi.iImage=%d\n", tvi.iImage);
466 ok(tvi.iSelectedImage == 0, "tvi.iSelectedImage=%d\n", tvi.iSelectedImage);
467
468 DestroyWindow(hTree);
469 }
470
471 static void test_callback(void)
472 {
473 HTREEITEM hRoot;
474 HTREEITEM hItem1, hItem2;
475 TVINSERTSTRUCTA ins;
476 TVITEMA tvi;
477 CHAR test_string[] = "Test_string";
478 static const CHAR test2A[] = "TEST2";
479 CHAR buf[128];
480 HWND hTree;
481 DWORD ret;
482
483 hTree = create_treeview_control(0);
484
485 ret = SendMessageA(hTree, TVM_DELETEITEM, 0, (LPARAM)TVI_ROOT);
486 expect(TRUE, ret);
487 ins.hParent = TVI_ROOT;
488 ins.hInsertAfter = TVI_ROOT;
489 U(ins).item.mask = TVIF_TEXT;
490 U(ins).item.pszText = LPSTR_TEXTCALLBACKA;
491 hRoot = TreeView_InsertItemA(hTree, &ins);
492 ok(hRoot != NULL, "failed to set root\n");
493
494 tvi.hItem = hRoot;
495 tvi.mask = TVIF_TEXT;
496 tvi.pszText = buf;
497 tvi.cchTextMax = sizeof(buf)/sizeof(buf[0]);
498 ret = TreeView_GetItemA(hTree, &tvi);
499 expect(TRUE, ret);
500 ok(strcmp(tvi.pszText, TEST_CALLBACK_TEXT) == 0, "Callback item text mismatch %s vs %s\n",
501 tvi.pszText, TEST_CALLBACK_TEXT);
502
503 ins.hParent = hRoot;
504 ins.hInsertAfter = TVI_FIRST;
505 U(ins).item.mask = TVIF_TEXT;
506 U(ins).item.pszText = test_string;
507 hItem1 = TreeView_InsertItemA(hTree, &ins);
508 ok(hItem1 != NULL, "failed to set Item1\n");
509
510 tvi.hItem = hItem1;
511 ret = TreeView_GetItemA(hTree, &tvi);
512 expect(TRUE, ret);
513 ok(strcmp(tvi.pszText, test_string) == 0, "Item text mismatch %s vs %s\n",
514 tvi.pszText, test_string);
515
516 /* undocumented: pszText of NULL also means LPSTR_CALLBACK: */
517 tvi.pszText = NULL;
518 ret = TreeView_SetItemA(hTree, &tvi);
519 expect(TRUE, ret);
520 tvi.pszText = buf;
521 ret = TreeView_GetItemA(hTree, &tvi);
522 expect(TRUE, ret);
523 ok(strcmp(tvi.pszText, TEST_CALLBACK_TEXT) == 0, "Item text mismatch %s vs %s\n",
524 tvi.pszText, TEST_CALLBACK_TEXT);
525
526 U(ins).item.pszText = NULL;
527 hItem2 = TreeView_InsertItemA(hTree, &ins);
528 ok(hItem2 != NULL, "failed to set Item2\n");
529 tvi.hItem = hItem2;
530 memset(buf, 0, sizeof(buf));
531 ret = TreeView_GetItemA(hTree, &tvi);
532 expect(TRUE, ret);
533 ok(strcmp(tvi.pszText, TEST_CALLBACK_TEXT) == 0, "Item text mismatch %s vs %s\n",
534 tvi.pszText, TEST_CALLBACK_TEXT);
535
536 /* notification handler changed A->W */
537 g_disp_A_to_W = TRUE;
538 tvi.hItem = hItem2;
539 memset(buf, 0, sizeof(buf));
540 ret = TreeView_GetItemA(hTree, &tvi);
541 expect(TRUE, ret);
542 ok(strcmp(tvi.pszText, test2A) == 0, "got %s, expected %s\n",
543 tvi.pszText, test2A);
544 g_disp_A_to_W = FALSE;
545
546 /* handler changes state image index */
547 SetWindowLongA(hTree, GWL_STYLE, GetWindowLongA(hTree, GWL_STYLE) | TVS_CHECKBOXES);
548
549 /* clear selection, handler will set selected state */
550 ret = SendMessageA(hTree, TVM_SELECTITEM, TVGN_CARET, 0);
551 expect(TRUE, ret);
552
553 flush_sequences(sequences, NUM_MSG_SEQUENCES);
554
555 tvi.hItem = hRoot;
556 tvi.mask = TVIF_STATE;
557 tvi.state = TVIS_SELECTED;
558 ret = TreeView_GetItemA(hTree, &tvi);
559 expect(TRUE, ret);
560 ok(tvi.state == INDEXTOSTATEIMAGEMASK(1), "got 0x%x\n", tvi.state);
561
562 ok_sequence(sequences, PARENT_SEQ_INDEX, empty_seq,
563 "no TVN_GETDISPINFO for a state seq", FALSE);
564
565 tvi.hItem = hRoot;
566 tvi.mask = TVIF_IMAGE | TVIF_STATE;
567 tvi.state = TVIS_FOCUSED;
568 tvi.stateMask = TVIS_FOCUSED;
569 tvi.iImage = I_IMAGECALLBACK;
570 ret = TreeView_SetItemA(hTree, &tvi);
571 expect(TRUE, ret);
572
573 /* ask for item image index through callback - state is also set with state image index */
574 flush_sequences(sequences, NUM_MSG_SEQUENCES);
575
576 tvi.hItem = hRoot;
577 tvi.mask = TVIF_IMAGE;
578 tvi.state = 0;
579 ret = TreeView_GetItemA(hTree, &tvi);
580 expect(TRUE, ret);
581 ok(tvi.state == (INDEXTOSTATEIMAGEMASK(1) | TVIS_FOCUSED), "got 0x%x\n", tvi.state);
582
583 ok_sequence(sequences, PARENT_SEQ_INDEX, parent_get_dispinfo_seq,
584 "callback for state/overlay image index, noop seq", FALSE);
585
586 /* ask for image again and overwrite state to some value in handler */
587 flush_sequences(sequences, NUM_MSG_SEQUENCES);
588
589 g_disp_set_stateimage = TRUE;
590 tvi.hItem = hRoot;
591 tvi.mask = TVIF_IMAGE;
592 tvi.state = INDEXTOSTATEIMAGEMASK(1);
593 tvi.stateMask = 0;
594 ret = TreeView_GetItemA(hTree, &tvi);
595 expect(TRUE, ret);
596 /* handler sets TVIS_SELECTED as well */
597 ok(tvi.state == (TVIS_FOCUSED | TVIS_SELECTED | INDEXTOSTATEIMAGEMASK(2) | INDEXTOOVERLAYMASK(3)), "got 0x%x\n", tvi.state);
598 g_disp_set_stateimage = FALSE;
599
600 ok_sequence(sequences, PARENT_SEQ_INDEX, parent_get_dispinfo_seq,
601 "callback for state/overlay image index seq", FALSE);
602
603 DestroyWindow(hTree);
604 }
605
606 static void test_select(void)
607 {
608 BOOL r;
609 HWND hTree;
610
611 hTree = create_treeview_control(0);
612 fill_tree(hTree);
613
614 /* root-none select tests */
615 flush_sequences(sequences, NUM_MSG_SEQUENCES);
616 r = SendMessageA(hTree, TVM_SELECTITEM, TVGN_CARET, 0);
617 expect(TRUE, r);
618 Clear();
619 AddItem('1');
620 r = SendMessageA(hTree, TVM_SELECTITEM, TVGN_CARET, (LPARAM)hRoot);
621 expect(TRUE, r);
622 AddItem('2');
623 r = SendMessageA(hTree, TVM_SELECTITEM, TVGN_CARET, (LPARAM)hRoot);
624 expect(TRUE, r);
625 AddItem('3');
626 r = SendMessageA(hTree, TVM_SELECTITEM, TVGN_CARET, 0);
627 expect(TRUE, r);
628 AddItem('4');
629 r = SendMessageA(hTree, TVM_SELECTITEM, TVGN_CARET, 0);
630 expect(TRUE, r);
631 AddItem('5');
632 r = SendMessageA(hTree, TVM_SELECTITEM, TVGN_CARET, (LPARAM)hRoot);
633 expect(TRUE, r);
634 AddItem('.');
635 ok(!strcmp(sequence, "1(nR)nR23(Rn)Rn45(nR)nR."), "root-none select test\n");
636 ok_sequence(sequences, TREEVIEW_SEQ_INDEX, rootnone_select_seq,
637 "root-none select seq", FALSE);
638
639 /* root-child select tests */
640 flush_sequences(sequences, NUM_MSG_SEQUENCES);
641 r = SendMessageA(hTree, TVM_SELECTITEM, TVGN_CARET, 0);
642 expect(TRUE, r);
643
644 Clear();
645 AddItem('1');
646 r = SendMessageA(hTree, TVM_SELECTITEM, TVGN_CARET, (LPARAM)hRoot);
647 expect(TRUE, r);
648 AddItem('2');
649 r = SendMessageA(hTree, TVM_SELECTITEM, TVGN_CARET, (LPARAM)hRoot);
650 expect(TRUE, r);
651 AddItem('3');
652 r = SendMessageA(hTree, TVM_SELECTITEM, TVGN_CARET, (LPARAM)hChild);
653 expect(TRUE, r);
654 AddItem('4');
655 r = SendMessageA(hTree, TVM_SELECTITEM, TVGN_CARET, (LPARAM)hChild);
656 expect(TRUE, r);
657 AddItem('5');
658 r = SendMessageA(hTree, TVM_SELECTITEM, TVGN_CARET, (LPARAM)hRoot);
659 expect(TRUE, r);
660 AddItem('.');
661 ok(!strcmp(sequence, "1(nR)nR23(RC)RC45(CR)CR."), "root-child select test\n");
662 ok_sequence(sequences, TREEVIEW_SEQ_INDEX, rootchild_select_seq,
663 "root-child select seq", FALSE);
664
665 DestroyWindow(hTree);
666 }
667
668 static void test_getitemtext(void)
669 {
670 TVINSERTSTRUCTA ins;
671 HTREEITEM hChild;
672 TVITEMA tvi;
673 HWND hTree;
674
675 CHAR szBuffer[80] = "Blah";
676 int nBufferSize = sizeof(szBuffer)/sizeof(CHAR);
677
678 hTree = create_treeview_control(0);
679 fill_tree(hTree);
680
681 flush_sequences(sequences, NUM_MSG_SEQUENCES);
682
683 /* add an item without TVIF_TEXT mask and pszText == NULL */
684 ins.hParent = hRoot;
685 ins.hInsertAfter = TVI_ROOT;
686 U(ins).item.mask = 0;
687 U(ins).item.pszText = NULL;
688 U(ins).item.cchTextMax = 0;
689 hChild = TreeView_InsertItemA(hTree, &ins);
690 ok(hChild != NULL, "failed to set hChild\n");
691
692 /* retrieve it with TVIF_TEXT mask */
693 tvi.hItem = hChild;
694 tvi.mask = TVIF_TEXT;
695 tvi.cchTextMax = nBufferSize;
696 tvi.pszText = szBuffer;
697
698 SendMessageA( hTree, TVM_GETITEMA, 0, (LPARAM)&tvi );
699 ok(!strcmp(szBuffer, ""), "szBuffer=\"%s\", expected \"\"\n", szBuffer);
700 ok(SendMessageA(hTree, TVM_DELETEITEM, 0, (LPARAM)hChild), "DeleteItem failed\n");
701 ok_sequence(sequences, TREEVIEW_SEQ_INDEX, getitemtext_seq, "get item text seq", FALSE);
702
703 DestroyWindow(hTree);
704 }
705
706 static void test_focus(void)
707 {
708 TVINSERTSTRUCTA ins;
709 static CHAR child1[] = "Edit",
710 child2[] = "A really long string";
711 HTREEITEM hChild1, hChild2;
712 HWND hTree;
713 HWND hEdit;
714
715 hTree = create_treeview_control(0);
716 fill_tree(hTree);
717
718 flush_sequences(sequences, NUM_MSG_SEQUENCES);
719
720 /* This test verifies that when a label is being edited, scrolling
721 * the treeview does not cause the label to lose focus. To test
722 * this, first some additional entries are added to generate
723 * scrollbars.
724 */
725 ins.hParent = hRoot;
726 ins.hInsertAfter = hChild;
727 U(ins).item.mask = TVIF_TEXT;
728 U(ins).item.pszText = child1;
729 hChild1 = TreeView_InsertItemA(hTree, &ins);
730 ok(hChild1 != NULL, "failed to set hChild1\n");
731 ins.hInsertAfter = hChild1;
732 U(ins).item.mask = TVIF_TEXT;
733 U(ins).item.pszText = child2;
734 hChild2 = TreeView_InsertItemA(hTree, &ins);
735 ok(hChild2 != NULL, "failed to set hChild2\n");
736
737 ShowWindow(hMainWnd,SW_SHOW);
738 SendMessageA(hTree, TVM_SELECTITEM, TVGN_CARET, (LPARAM)hChild);
739 hEdit = (HWND)SendMessageA(hTree, TVM_EDITLABELA, 0, (LPARAM)hChild);
740 ScrollWindowEx(hTree, -10, 0, NULL, NULL, NULL, NULL, SW_SCROLLCHILDREN);
741 ok(GetFocus() == hEdit, "Edit control should have focus\n");
742 ok_sequence(sequences, TREEVIEW_SEQ_INDEX, focus_seq, "focus test", TRUE);
743
744 DestroyWindow(hTree);
745 }
746
747 static void test_get_set_bkcolor(void)
748 {
749 COLORREF crColor;
750 HWND hTree;
751
752 hTree = create_treeview_control(0);
753 fill_tree(hTree);
754
755 flush_sequences(sequences, NUM_MSG_SEQUENCES);
756
757 /* If the value is -1, the control is using the system color for the background color. */
758 crColor = SendMessageA(hTree, TVM_GETBKCOLOR, 0, 0);
759 ok(crColor == ~0u, "Default background color reported as 0x%.8x\n", crColor);
760
761 /* Test for black background */
762 SendMessageA(hTree, TVM_SETBKCOLOR, 0, RGB(0,0,0));
763 crColor = SendMessageA(hTree, TVM_GETBKCOLOR, 0, 0);
764 ok(crColor == RGB(0,0,0), "Black background color reported as 0x%.8x\n", crColor);
765
766 /* Test for white background */
767 SendMessageA(hTree, TVM_SETBKCOLOR, 0, RGB(255,255,255));
768 crColor = SendMessageA(hTree, TVM_GETBKCOLOR, 0, 0);
769 ok(crColor == RGB(255,255,255), "White background color reported as 0x%.8x\n", crColor);
770
771 /* Reset the default background */
772 SendMessageA(hTree, TVM_SETBKCOLOR, 0, -1);
773
774 ok_sequence(sequences, TREEVIEW_SEQ_INDEX, test_get_set_bkcolor_seq,
775 "test get set bkcolor", FALSE);
776
777 DestroyWindow(hTree);
778 }
779
780 static void test_get_set_imagelist(void)
781 {
782 HIMAGELIST himl;
783 HWND hTree;
784
785 hTree = create_treeview_control(0);
786 fill_tree(hTree);
787
788 flush_sequences(sequences, NUM_MSG_SEQUENCES);
789
790 /* Test a NULL HIMAGELIST */
791 SendMessageA(hTree, TVM_SETIMAGELIST, TVSIL_NORMAL, 0);
792 himl = (HIMAGELIST)SendMessageA(hTree, TVM_GETIMAGELIST, TVSIL_NORMAL, 0);
793 ok(himl == NULL, "NULL image list, reported as %p, expected 0.\n", himl);
794
795 /* TODO: Test an actual image list */
796
797 ok_sequence(sequences, TREEVIEW_SEQ_INDEX, test_get_set_imagelist_seq,
798 "test get imagelist", FALSE);
799
800 DestroyWindow(hTree);
801 }
802
803 static void test_get_set_indent(void)
804 {
805 int ulIndent;
806 int ulMinIndent;
807 int ulMoreThanTwiceMin;
808 HWND hTree;
809
810 hTree = create_treeview_control(0);
811 fill_tree(hTree);
812
813 flush_sequences(sequences, NUM_MSG_SEQUENCES);
814
815 /* Finding the minimum indent */
816 SendMessageA(hTree, TVM_SETINDENT, 0, 0);
817 ulMinIndent = SendMessageA(hTree, TVM_GETINDENT, 0, 0);
818
819 /* Checking an indent that is more than twice the default indent */
820 ulMoreThanTwiceMin = 2*ulMinIndent+1;
821 SendMessageA(hTree, TVM_SETINDENT, ulMoreThanTwiceMin, 0);
822 ulIndent = SendMessageA(hTree, TVM_GETINDENT, 0, 0);
823 ok(ulIndent == ulMoreThanTwiceMin, "Indent reported as %d, expected %d\n", ulIndent, ulMoreThanTwiceMin);
824
825 ok_sequence(sequences, TREEVIEW_SEQ_INDEX, test_get_set_indent_seq,
826 "test get set indent", FALSE);
827
828 DestroyWindow(hTree);
829 }
830
831 static void test_get_set_insertmark(void)
832 {
833 COLORREF crColor = RGB(0,0,0);
834 HWND hTree;
835
836 hTree = create_treeview_control(0);
837 fill_tree(hTree);
838
839 flush_sequences(sequences, NUM_MSG_SEQUENCES);
840
841 SendMessageA(hTree, TVM_SETINSERTMARKCOLOR, 0, crColor);
842 crColor = SendMessageA(hTree, TVM_GETINSERTMARKCOLOR, 0, 0);
843 ok(crColor == RGB(0,0,0), "Insert mark color reported as 0x%.8x, expected 0x00000000\n", crColor);
844
845 ok_sequence(sequences, TREEVIEW_SEQ_INDEX, test_get_set_insertmarkcolor_seq,
846 "test get set insertmark color", FALSE);
847
848 DestroyWindow(hTree);
849 }
850
851 static void test_get_set_item(void)
852 {
853 TVITEMA tviRoot = {0};
854 int nBufferSize = 80;
855 char szBuffer[80] = {0};
856 HWND hTree, hTree2;
857 DWORD ret;
858
859 hTree = create_treeview_control(0);
860 fill_tree(hTree);
861
862 tviRoot.hItem = hRoot;
863 tviRoot.mask = TVIF_STATE;
864 tviRoot.state = TVIS_FOCUSED;
865 tviRoot.stateMask = TVIS_FOCUSED;
866 ret = SendMessageA(hTree, TVM_SETITEMA, 0, (LPARAM)&tviRoot);
867 expect(TRUE, ret);
868
869 flush_sequences(sequences, NUM_MSG_SEQUENCES);
870
871 /* Test the root item, state is set even when not requested */
872 tviRoot.hItem = hRoot;
873 tviRoot.mask = TVIF_TEXT;
874 tviRoot.state = 0;
875 tviRoot.stateMask = 0;
876 tviRoot.cchTextMax = nBufferSize;
877 tviRoot.pszText = szBuffer;
878 ret = SendMessageA(hTree, TVM_GETITEMA, 0, (LPARAM)&tviRoot);
879 expect(TRUE, ret);
880 ok(!strcmp("Root", szBuffer), "GetItem: szBuffer=\"%s\", expected \"Root\"\n", szBuffer);
881 ok(tviRoot.state == TVIS_FOCUSED, "got 0x%0x\n", tviRoot.state);
882
883 /* Change the root text */
884 lstrcpynA(szBuffer, "Testing123", nBufferSize);
885 ret = SendMessageA(hTree, TVM_SETITEMA, 0, (LPARAM)&tviRoot);
886 expect(TRUE, ret);
887 memset(szBuffer, 0, nBufferSize);
888 ret = SendMessageA(hTree, TVM_GETITEMA, 0, (LPARAM)&tviRoot);
889 expect(TRUE, ret);
890 ok(!strcmp("Testing123", szBuffer), "GetItem: szBuffer=\"%s\", expected \"Testing123\"\n", szBuffer);
891
892 /* Reset the root text */
893 memset(szBuffer, 0, nBufferSize);
894 lstrcpynA(szBuffer, "Root", nBufferSize);
895 ret = SendMessageA(hTree, TVM_SETITEMA, 0, (LPARAM)&tviRoot);
896 expect(TRUE, ret);
897
898 ok_sequence(sequences, TREEVIEW_SEQ_INDEX, test_get_set_item_seq,
899 "test get set item", FALSE);
900
901 /* get item from a different tree */
902 hTree2 = create_treeview_control(0);
903
904 tviRoot.hItem = hRoot;
905 tviRoot.mask = TVIF_STATE;
906 tviRoot.state = 0;
907 ret = SendMessageA(hTree2, TVM_GETITEMA, 0, (LPARAM)&tviRoot);
908 expect(TRUE, ret);
909 ok(tviRoot.state == TVIS_FOCUSED, "got state 0x%0x\n", tviRoot.state);
910
911 /* invalid item pointer, nt4 crashes here but later versions just return 0 */
912 tviRoot.hItem = (HTREEITEM)0xdeadbeef;
913 tviRoot.mask = TVIF_STATE;
914 tviRoot.state = 0;
915 ret = SendMessageA(hTree2, TVM_GETITEMA, 0, (LPARAM)&tviRoot);
916 expect(FALSE, ret);
917
918 DestroyWindow(hTree);
919 DestroyWindow(hTree2);
920 }
921
922 static void test_get_set_itemheight(void)
923 {
924 int ulOldHeight = 0;
925 int ulNewHeight = 0;
926 HWND hTree;
927
928 hTree = create_treeview_control(0);
929 fill_tree(hTree);
930
931 flush_sequences(sequences, NUM_MSG_SEQUENCES);
932
933 /* Assuming default height to begin with */
934 ulOldHeight = SendMessageA(hTree, TVM_GETITEMHEIGHT, 0, 0);
935
936 /* Explicitly setting and getting the default height */
937 SendMessageA(hTree, TVM_SETITEMHEIGHT, -1, 0);
938 ulNewHeight = SendMessageA(hTree, TVM_GETITEMHEIGHT, 0, 0);
939 ok(ulNewHeight == ulOldHeight, "Default height not set properly, reported %d, expected %d\n", ulNewHeight, ulOldHeight);
940
941 /* Explicitly setting and getting the height of twice the normal */
942 SendMessageA(hTree, TVM_SETITEMHEIGHT, 2*ulOldHeight, 0);
943 ulNewHeight = SendMessageA(hTree, TVM_GETITEMHEIGHT, 0, 0);
944 ok(ulNewHeight == 2*ulOldHeight, "New height not set properly, reported %d, expected %d\n", ulNewHeight, 2*ulOldHeight);
945
946 /* Assuming tree doesn't have TVS_NONEVENHEIGHT set, so a set of 9 will round down to 8 */
947 SendMessageA(hTree, TVM_SETITEMHEIGHT, 9, 0);
948 ulNewHeight = SendMessageA(hTree, TVM_GETITEMHEIGHT, 0, 0);
949 ok(ulNewHeight == 8, "Uneven height not set properly, reported %d, expected %d\n", ulNewHeight, 8);
950
951 ok_sequence(sequences, TREEVIEW_SEQ_INDEX, test_get_set_itemheight_seq,
952 "test get set item height", FALSE);
953
954 /* without TVS_NONEVENHEIGHT */
955 SetWindowLongA(hTree, GWL_STYLE, GetWindowLongA(hTree, GWL_STYLE) & ~TVS_NONEVENHEIGHT);
956 /* odd value */
957 ulOldHeight = SendMessageA(hTree, TVM_SETITEMHEIGHT, 3, 0);
958 ok(ulOldHeight == 8, "got %d, expected %d\n", ulOldHeight, 8);
959 ulNewHeight = SendMessageA(hTree, TVM_GETITEMHEIGHT, 0, 0);
960 ok(ulNewHeight == 2, "got %d, expected %d\n", ulNewHeight, 2);
961
962 ulOldHeight = SendMessageA(hTree, TVM_SETITEMHEIGHT, 4, 0);
963 ok(ulOldHeight == 2, "got %d, expected %d\n", ulOldHeight, 2);
964 ulNewHeight = SendMessageA(hTree, TVM_GETITEMHEIGHT, 0, 0);
965 ok(ulNewHeight == 4, "got %d, expected %d\n", ulNewHeight, 4);
966
967 /* with TVS_NONEVENHEIGHT */
968 SetWindowLongA(hTree, GWL_STYLE, GetWindowLongA(hTree, GWL_STYLE) | TVS_NONEVENHEIGHT);
969 /* odd value */
970 ulOldHeight = SendMessageA(hTree, TVM_SETITEMHEIGHT, 3, 0);
971 ok(ulOldHeight == 4, "got %d, expected %d\n", ulOldHeight, 4);
972 ulNewHeight = SendMessageA(hTree, TVM_GETITEMHEIGHT, 0, 0);
973 ok(ulNewHeight == 3, "got %d, expected %d\n", ulNewHeight, 3);
974 /* even value */
975 ulOldHeight = SendMessageA(hTree, TVM_SETITEMHEIGHT, 10, 0);
976 ok(ulOldHeight == 3, "got %d, expected %d\n", ulOldHeight, 3);
977 ulNewHeight = SendMessageA(hTree, TVM_GETITEMHEIGHT, 0, 0);
978 ok(ulNewHeight == 10, "got %d, expected %d\n", ulNewHeight, 10);
979
980 DestroyWindow(hTree);
981 }
982
983 static void test_get_set_scrolltime(void)
984 {
985 int ulExpectedTime = 20;
986 int ulTime = 0;
987 HWND hTree;
988
989 hTree = create_treeview_control(0);
990 fill_tree(hTree);
991
992 flush_sequences(sequences, NUM_MSG_SEQUENCES);
993
994 SendMessageA(hTree, TVM_SETSCROLLTIME, ulExpectedTime, 0);
995 ulTime = SendMessageA(hTree, TVM_GETSCROLLTIME, 0, 0);
996 ok(ulTime == ulExpectedTime, "Scroll time reported as %d, expected %d\n", ulTime, ulExpectedTime);
997
998 ok_sequence(sequences, TREEVIEW_SEQ_INDEX, test_get_set_scrolltime_seq,
999 "test get set scroll time", FALSE);
1000
1001 DestroyWindow(hTree);
1002 }
1003
1004 static void test_get_set_textcolor(void)
1005 {
1006 /* If the value is -1, the control is using the system color for the text color. */
1007 COLORREF crColor;
1008 HWND hTree;
1009
1010 hTree = create_treeview_control(0);
1011 fill_tree(hTree);
1012
1013 flush_sequences(sequences, NUM_MSG_SEQUENCES);
1014
1015 crColor = SendMessageA(hTree, TVM_GETTEXTCOLOR, 0, 0);
1016 ok(crColor == ~0u, "Default text color reported as 0x%.8x\n", crColor);
1017
1018 /* Test for black text */
1019 SendMessageA(hTree, TVM_SETTEXTCOLOR, 0, RGB(0,0,0));
1020 crColor = SendMessageA(hTree, TVM_GETTEXTCOLOR, 0, 0);
1021 ok(crColor == RGB(0,0,0), "Black text color reported as 0x%.8x\n", crColor);
1022
1023 /* Test for white text */
1024 SendMessageA(hTree, TVM_SETTEXTCOLOR, 0, RGB(255,255,255));
1025 crColor = SendMessageA(hTree, TVM_GETTEXTCOLOR, 0, 0);
1026 ok(crColor == RGB(255,255,255), "White text color reported as 0x%.8x\n", crColor);
1027
1028 /* Reset the default text color */
1029 SendMessageA(hTree, TVM_SETTEXTCOLOR, 0, CLR_NONE);
1030
1031 ok_sequence(sequences, TREEVIEW_SEQ_INDEX, test_get_set_textcolor_seq,
1032 "test get set text color", FALSE);
1033
1034 DestroyWindow(hTree);
1035 }
1036
1037 static void test_get_set_tooltips(void)
1038 {
1039 HWND hwndLastToolTip = NULL;
1040 HWND hPopupTreeView;
1041 HWND hTree;
1042
1043 hTree = create_treeview_control(0);
1044 fill_tree(hTree);
1045
1046 flush_sequences(sequences, NUM_MSG_SEQUENCES);
1047
1048 /* show even WS_POPUP treeview don't send NM_TOOLTIPSCREATED */
1049 hPopupTreeView = CreateWindowA(WC_TREEVIEWA, NULL, WS_POPUP|WS_VISIBLE, 0, 0, 100, 100,
1050 hMainWnd, NULL, NULL, NULL);
1051 DestroyWindow(hPopupTreeView);
1052
1053 /* Testing setting a NULL ToolTip */
1054 SendMessageA(hTree, TVM_SETTOOLTIPS, 0, 0);
1055 hwndLastToolTip = (HWND)SendMessageA(hTree, TVM_GETTOOLTIPS, 0, 0);
1056 ok(hwndLastToolTip == NULL, "NULL tool tip, reported as 0x%p, expected 0.\n", hwndLastToolTip);
1057
1058 ok_sequence(sequences, TREEVIEW_SEQ_INDEX, test_get_set_tooltips_seq,
1059 "test get set tooltips", TRUE);
1060
1061 /* TODO: Add a test of an actual tooltip */
1062 DestroyWindow(hTree);
1063 }
1064
1065 static void test_get_set_unicodeformat(void)
1066 {
1067 BOOL bPreviousSetting;
1068 BOOL bNewSetting;
1069 HWND hTree;
1070
1071 hTree = create_treeview_control(0);
1072 fill_tree(hTree);
1073
1074 /* Check that an invalid format returned by NF_QUERY defaults to ANSI */
1075 bPreviousSetting = SendMessageA(hTree, TVM_GETUNICODEFORMAT, 0, 0);
1076 ok(bPreviousSetting == FALSE, "Format should be ANSI.\n");
1077
1078 flush_sequences(sequences, NUM_MSG_SEQUENCES);
1079
1080 /* Set to Unicode */
1081 bPreviousSetting = SendMessageA(hTree, TVM_SETUNICODEFORMAT, 1, 0);
1082 bNewSetting = SendMessageA(hTree, TVM_GETUNICODEFORMAT, 0, 0);
1083 ok(bNewSetting == TRUE, "Unicode setting did not work.\n");
1084
1085 /* Set to ANSI */
1086 SendMessageA(hTree, TVM_SETUNICODEFORMAT, 0, 0);
1087 bNewSetting = SendMessageA(hTree, TVM_GETUNICODEFORMAT, 0, 0);
1088 ok(bNewSetting == FALSE, "ANSI setting did not work.\n");
1089
1090 /* Revert to original setting */
1091 SendMessageA(hTree, TVM_SETUNICODEFORMAT, bPreviousSetting, 0);
1092
1093 ok_sequence(sequences, TREEVIEW_SEQ_INDEX, test_get_set_unicodeformat_seq,
1094 "test get set unicode format", FALSE);
1095
1096 DestroyWindow(hTree);
1097 }
1098
1099 static LRESULT CALLBACK parent_wnd_proc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
1100 {
1101 static LONG defwndproc_counter = 0;
1102 struct message msg;
1103 LRESULT ret;
1104 RECT rect;
1105 HTREEITEM visibleItem;
1106
1107 msg.message = message;
1108 msg.flags = sent|wparam|lparam;
1109 if (defwndproc_counter) msg.flags |= defwinproc;
1110 msg.wParam = wParam;
1111 msg.lParam = lParam;
1112 if (message == WM_NOTIFY && lParam)
1113 msg.id = ((NMHDR*)lParam)->code;
1114 else
1115 msg.id = 0;
1116
1117 /* log system messages, except for painting */
1118 if (message < WM_USER &&
1119 message != WM_PAINT &&
1120 message != WM_ERASEBKGND &&
1121 message != WM_NCPAINT &&
1122 message != WM_NCHITTEST &&
1123 message != WM_GETTEXT &&
1124 message != WM_GETICON &&
1125 message != WM_DEVICECHANGE)
1126 {
1127 add_message(sequences, PARENT_SEQ_INDEX, &msg);
1128 }
1129
1130 switch(message) {
1131 case WM_NOTIFYFORMAT:
1132 {
1133 /* Make NF_QUERY return an invalid format to show that it defaults to ANSI */
1134 if (lParam == NF_QUERY) return 0;
1135 break;
1136 }
1137
1138 case WM_NOTIFY:
1139 {
1140 NMHDR *pHdr = (NMHDR *)lParam;
1141
1142 ok(pHdr->code != NM_TOOLTIPSCREATED, "Treeview should not send NM_TOOLTIPSCREATED\n");
1143 if (pHdr->idFrom == 100)
1144 {
1145 NMTREEVIEWA *pTreeView = (LPNMTREEVIEWA) lParam;
1146 switch(pHdr->code)
1147 {
1148 case TVN_SELCHANGINGA:
1149 AddItem('(');
1150 IdentifyItem(pTreeView->itemOld.hItem);
1151 IdentifyItem(pTreeView->itemNew.hItem);
1152 break;
1153 case TVN_SELCHANGEDA:
1154 AddItem(')');
1155 IdentifyItem(pTreeView->itemOld.hItem);
1156 IdentifyItem(pTreeView->itemNew.hItem);
1157 break;
1158 case TVN_GETDISPINFOA: {
1159 NMTVDISPINFOA *disp = (NMTVDISPINFOA *)lParam;
1160 if (disp->item.mask & TVIF_TEXT) {
1161 lstrcpynA(disp->item.pszText, TEST_CALLBACK_TEXT, disp->item.cchTextMax);
1162 }
1163
1164 if (g_disp_A_to_W && (disp->item.mask & TVIF_TEXT)) {
1165 static const WCHAR testW[] = {'T','E','S','T','2',0};
1166
1167 disp->hdr.code = TVN_GETDISPINFOW;
1168 memcpy(disp->item.pszText, testW, sizeof(testW));
1169 }
1170
1171 if (g_disp_set_stateimage)
1172 {
1173 ok(disp->item.mask == TVIF_IMAGE, "got %x\n", disp->item.mask);
1174 /* both masks set here are necessary to change state bits */
1175 disp->item.mask |= TVIF_STATE;
1176 disp->item.state = TVIS_SELECTED | INDEXTOSTATEIMAGEMASK(2) | INDEXTOOVERLAYMASK(3);
1177 disp->item.stateMask = TVIS_SELECTED | TVIS_OVERLAYMASK | TVIS_STATEIMAGEMASK;
1178 }
1179
1180 break;
1181 }
1182 case TVN_BEGINLABELEDITA:
1183 {
1184 if (g_beginedit_alter_text)
1185 {
1186 static const char* textA = "<edittextaltered>";
1187 HWND edit;
1188
1189 edit = (HWND)SendMessageA(pHdr->hwndFrom, TVM_GETEDITCONTROL, 0, 0);
1190 ok(IsWindow(edit), "failed to get edit handle\n");
1191 SetWindowTextA(edit, textA);
1192 }
1193
1194 break;
1195 }
1196
1197 case TVN_ENDLABELEDITA: return TRUE;
1198 case TVN_ITEMEXPANDINGA:
1199 {
1200 UINT newmask = pTreeView->itemNew.mask & ~TVIF_CHILDREN;
1201 ok(newmask ==
1202 (TVIF_HANDLE | TVIF_SELECTEDIMAGE | TVIF_IMAGE | TVIF_PARAM | TVIF_STATE),
1203 "got wrong mask %x\n", pTreeView->itemNew.mask);
1204 ok(pTreeView->itemOld.mask == 0,
1205 "got wrong mask %x\n", pTreeView->itemOld.mask);
1206
1207 if (g_get_from_expand)
1208 {
1209 g_item_expanding.mask = TVIF_STATE;
1210 g_item_expanding.hItem = hRoot;
1211 ret = SendMessageA(pHdr->hwndFrom, TVM_GETITEMA, 0, (LPARAM)&g_item_expanding);
1212 ok(ret == TRUE, "got %lu\n", ret);
1213 }
1214 break;
1215 }
1216 case TVN_ITEMEXPANDEDA:
1217 ok(pTreeView->itemNew.mask & TVIF_STATE, "got wrong mask %x\n", pTreeView->itemNew.mask);
1218 ok(pTreeView->itemNew.state & (TVIS_EXPANDED|TVIS_EXPANDEDONCE),
1219 "got wrong mask %x\n", pTreeView->itemNew.mask);
1220 ok(pTreeView->itemOld.mask == 0,
1221 "got wrong mask %x\n", pTreeView->itemOld.mask);
1222
1223 if (g_get_from_expand)
1224 {
1225 g_item_expanded.mask = TVIF_STATE;
1226 g_item_expanded.hItem = hRoot;
1227 ret = SendMessageA(pHdr->hwndFrom, TVM_GETITEMA, 0, (LPARAM)&g_item_expanded);
1228 ok(ret == TRUE, "got %lu\n", ret);
1229 }
1230 if (g_get_rect_in_expand)
1231 {
1232 visibleItem = (HTREEITEM)SendMessageA(pHdr->hwndFrom, TVM_GETNEXTITEM,
1233 TVGN_FIRSTVISIBLE, 0);
1234 ok(pTreeView->itemNew.hItem == visibleItem, "expanded item == first visible item\n");
1235 *(HTREEITEM*)&rect = visibleItem;
1236 ok(SendMessageA(pHdr->hwndFrom, TVM_GETITEMRECT, TRUE, (LPARAM)&rect),
1237 "Failed to get rect for first visible item.\n");
1238 visibleItem = (HTREEITEM)SendMessageA(pHdr->hwndFrom, TVM_GETNEXTITEM,
1239 TVGN_NEXTVISIBLE, (LPARAM)visibleItem);
1240 *(HTREEITEM*)&rect = visibleItem;
1241 ok(visibleItem != NULL, "There must be a visible item after the first visisble item.\n");
1242 ok(SendMessageA(pHdr->hwndFrom, TVM_GETITEMRECT, TRUE, (LPARAM)&rect),
1243 "Failed to get rect for second visible item.\n");
1244 }
1245 break;
1246 case TVN_DELETEITEMA:
1247 {
1248 struct message item;
1249
1250 ok(pTreeView->itemNew.mask == 0, "got wrong mask 0x%x\n", pTreeView->itemNew.mask);
1251
1252 ok(pTreeView->itemOld.mask == (TVIF_HANDLE | TVIF_PARAM), "got wrong mask 0x%x\n", pTreeView->itemOld.mask);
1253 ok(pTreeView->itemOld.hItem != NULL, "got %p\n", pTreeView->itemOld.hItem);
1254
1255 memset(&item, 0, sizeof(item));
1256 item.lParam = (LPARAM)pTreeView->itemOld.hItem;
1257 add_message(item_sequence, 0, &item);
1258
1259 break;
1260 }
1261 case NM_CUSTOMDRAW:
1262 {
1263 NMTVCUSTOMDRAW *nmcd = (NMTVCUSTOMDRAW*)lParam;
1264 COLORREF c0ffee = RGB(0xc0,0xff,0xee), cafe = RGB(0xca,0xfe,0x00);
1265 COLORREF text = GetTextColor(nmcd->nmcd.hdc), bkgnd = GetBkColor(nmcd->nmcd.hdc);
1266
1267 msg.flags |= custdraw;
1268 msg.stage = nmcd->nmcd.dwDrawStage;
1269 add_message(sequences, PARENT_CD_SEQ_INDEX, &msg);
1270
1271 switch (msg.stage)
1272 {
1273 case CDDS_PREPAINT:
1274 return CDRF_NOTIFYITEMDRAW|CDRF_NOTIFYITEMERASE|CDRF_NOTIFYPOSTPAINT;
1275 case CDDS_ITEMPREPAINT:
1276 ok(text == nmcd->clrText || (g_v6 && nmcd->clrText == 0xffffffff),
1277 "got %08x vs %08x\n", text, nmcd->clrText);
1278 ok(bkgnd == nmcd->clrTextBk || (g_v6 && nmcd->clrTextBk == 0xffffffff),
1279 "got %08x vs %08x\n", bkgnd, nmcd->clrTextBk);
1280 nmcd->clrText = cafe;
1281 nmcd->clrTextBk = c0ffee;
1282 SetTextColor(nmcd->nmcd.hdc, c0ffee);
1283 SetBkColor(nmcd->nmcd.hdc, cafe);
1284 if (g_customdraw_font)
1285 SelectObject(nmcd->nmcd.hdc, g_customdraw_font);
1286 return CDRF_NOTIFYPOSTPAINT|CDRF_NEWFONT;
1287 case CDDS_ITEMPOSTPAINT:
1288 /* at the point of post paint notification colors are already restored */
1289 ok(nmcd->clrText == cafe, "got 0%x\n", nmcd->clrText);
1290 ok(nmcd->clrTextBk == c0ffee, "got 0%x\n", nmcd->clrTextBk);
1291 ok(text != cafe, "got 0%x\n", text);
1292 ok(bkgnd != c0ffee, "got 0%x\n", bkgnd);
1293 if (g_customdraw_font)
1294 ok(GetCurrentObject(nmcd->nmcd.hdc, OBJ_FONT) != g_customdraw_font, "got %p\n",
1295 GetCurrentObject(nmcd->nmcd.hdc, OBJ_FONT));
1296 break;
1297 default:
1298 ;
1299 }
1300 break;
1301 }
1302 }
1303 }
1304 break;
1305 }
1306
1307 case WM_DESTROY:
1308 PostQuitMessage(0);
1309 break;
1310 }
1311
1312 defwndproc_counter++;
1313 ret = DefWindowProcA(hWnd, message, wParam, lParam);
1314 defwndproc_counter--;
1315
1316 return ret;
1317 }
1318
1319 static void test_expandinvisible(void)
1320 {
1321 static CHAR nodeText[][5] = {"0", "1", "2", "3", "4"};
1322 TVINSERTSTRUCTA ins;
1323 HTREEITEM node[5];
1324 RECT dummyRect;
1325 BOOL nodeVisible;
1326 LRESULT ret;
1327 HWND hTree;
1328
1329 hTree = create_treeview_control(0);
1330
1331 /* The test builds the following tree and expands node 1, while node 0 is collapsed.
1332 *
1333 * 0
1334 * |- 1
1335 * | |- 2
1336 * | |- 3
1337 * |- 4
1338 *
1339 */
1340
1341 ret = SendMessageA(hTree, TVM_DELETEITEM, 0, (LPARAM)TVI_ROOT);
1342 ok(ret == TRUE, "ret\n");
1343 ins.hParent = TVI_ROOT;
1344 ins.hInsertAfter = TVI_ROOT;
1345 U(ins).item.mask = TVIF_TEXT;
1346 U(ins).item.pszText = nodeText[0];
1347 node[0] = TreeView_InsertItemA(hTree, &ins);
1348 ok(node[0] != NULL, "failed to set node[0]\n");
1349
1350 ins.hInsertAfter = TVI_LAST;
1351 U(ins).item.mask = TVIF_TEXT;
1352 ins.hParent = node[0];
1353
1354 U(ins).item.pszText = nodeText[1];
1355 node[1] = TreeView_InsertItemA(hTree, &ins);
1356 ok(node[1] != NULL, "failed to set node[1]\n");
1357 U(ins).item.pszText = nodeText[4];
1358 node[4] = TreeView_InsertItemA(hTree, &ins);
1359 ok(node[4] != NULL, "failed to set node[4]\n");
1360
1361 ins.hParent = node[1];
1362
1363 U(ins).item.pszText = nodeText[2];
1364 node[2] = TreeView_InsertItemA(hTree, &ins);
1365 ok(node[2] != NULL, "failed to set node[2]\n");
1366 U(ins).item.pszText = nodeText[3];
1367 node[3] = TreeView_InsertItemA(hTree, &ins);
1368 ok(node[3] != NULL, "failed to set node[3]\n");
1369
1370 *(HTREEITEM *)&dummyRect = node[1];
1371 nodeVisible = SendMessageA(hTree, TVM_GETITEMRECT, FALSE, (LPARAM)&dummyRect);
1372 ok(!nodeVisible, "Node 1 should not be visible.\n");
1373 *(HTREEITEM *)&dummyRect = node[2];
1374 nodeVisible = SendMessageA(hTree, TVM_GETITEMRECT, FALSE, (LPARAM)&dummyRect);
1375 ok(!nodeVisible, "Node 2 should not be visible.\n");
1376 *(HTREEITEM *)&dummyRect = node[3];
1377 nodeVisible = SendMessageA(hTree, TVM_GETITEMRECT, FALSE, (LPARAM)&dummyRect);
1378 ok(!nodeVisible, "Node 3 should not be visible.\n");
1379 *(HTREEITEM *)&dummyRect = node[4];
1380 nodeVisible = SendMessageA(hTree, TVM_GETITEMRECT, FALSE, (LPARAM)&dummyRect);
1381 ok(!nodeVisible, "Node 4 should not be visible.\n");
1382
1383 ok(SendMessageA(hTree, TVM_EXPAND, TVE_EXPAND, (LPARAM)node[1]), "Expand of node 1 failed.\n");
1384
1385 *(HTREEITEM *)&dummyRect = node[1];
1386 nodeVisible = SendMessageA(hTree, TVM_GETITEMRECT, FALSE, (LPARAM)&dummyRect);
1387 ok(!nodeVisible, "Node 1 should not be visible.\n");
1388 *(HTREEITEM *)&dummyRect = node[2];
1389 nodeVisible = SendMessageA(hTree, TVM_GETITEMRECT, FALSE, (LPARAM)&dummyRect);
1390 ok(!nodeVisible, "Node 2 should not be visible.\n");
1391 *(HTREEITEM *)&dummyRect = node[3];
1392 nodeVisible = SendMessageA(hTree, TVM_GETITEMRECT, FALSE, (LPARAM)&dummyRect);
1393 ok(!nodeVisible, "Node 3 should not be visible.\n");
1394 *(HTREEITEM *)&dummyRect = node[4];
1395 nodeVisible = SendMessageA(hTree, TVM_GETITEMRECT, FALSE, (LPARAM)&dummyRect);
1396 ok(!nodeVisible, "Node 4 should not be visible.\n");
1397
1398 DestroyWindow(hTree);
1399 }
1400
1401 static void test_itemedit(void)
1402 {
1403 DWORD r;
1404 HWND edit;
1405 TVITEMA item;
1406 CHAR buffA[20];
1407 HWND hTree;
1408
1409 hTree = create_treeview_control(0);
1410 fill_tree(hTree);
1411
1412 /* try with null item */
1413 edit = (HWND)SendMessageA(hTree, TVM_EDITLABELA, 0, 0);
1414 ok(!IsWindow(edit), "Expected valid handle\n");
1415
1416 /* trigger edit */
1417 edit = (HWND)SendMessageA(hTree, TVM_EDITLABELA, 0, (LPARAM)hRoot);
1418 ok(IsWindow(edit), "Expected valid handle\n");
1419 /* item shouldn't be selected automatically after TVM_EDITLABELA */
1420 r = SendMessageA(hTree, TVM_GETITEMSTATE, (WPARAM)hRoot, TVIS_SELECTED);
1421 expect(0, r);
1422 /* try to cancel with wrong edit handle */
1423 r = SendMessageA(hTree, WM_COMMAND, MAKEWPARAM(0, EN_KILLFOCUS), 0);
1424 expect(0, r);
1425 ok(IsWindow(edit), "Expected edit control to be valid\n");
1426 r = SendMessageA(hTree, WM_COMMAND, MAKEWPARAM(0, EN_KILLFOCUS), (LPARAM)edit);
1427 expect(0, r);
1428 ok(!IsWindow(edit), "Expected edit control to be destroyed\n");
1429 /* try to cancel without creating edit */
1430 r = SendMessageA(hTree, WM_COMMAND, MAKEWPARAM(0, EN_KILLFOCUS), 0);
1431 expect(0, r);
1432
1433 /* try to cancel with wrong (not null) handle */
1434 edit = (HWND)SendMessageA(hTree, TVM_EDITLABELA, 0, (LPARAM)hRoot);
1435 ok(IsWindow(edit), "Expected valid handle\n");
1436 r = SendMessageA(hTree, WM_COMMAND, MAKEWPARAM(0, EN_KILLFOCUS), (LPARAM)hTree);
1437 expect(0, r);
1438 ok(IsWindow(edit), "Expected edit control to be valid\n");
1439 r = SendMessageA(hTree, WM_COMMAND, MAKEWPARAM(0, EN_KILLFOCUS), (LPARAM)edit);
1440 expect(0, r);
1441
1442 /* remove selection after starting edit */
1443 r = SendMessageA(hTree, TVM_SELECTITEM, TVGN_CARET, (LPARAM)hRoot);
1444 expect(TRUE, r);
1445 edit = (HWND)SendMessageA(hTree, TVM_EDITLABELA, 0, (LPARAM)hRoot);
1446 ok(IsWindow(edit), "Expected valid handle\n");
1447 r = SendMessageA(hTree, TVM_SELECTITEM, TVGN_CARET, 0);
1448 expect(TRUE, r);
1449 /* alter text */
1450 strcpy(buffA, "x");
1451 r = SendMessageA(edit, WM_SETTEXT, 0, (LPARAM)buffA);
1452 expect(TRUE, r);
1453 r = SendMessageA(hTree, WM_COMMAND, MAKEWPARAM(0, EN_KILLFOCUS), (LPARAM)edit);
1454 expect(0, r);
1455 ok(!IsWindow(edit), "Expected edit control to be destroyed\n");
1456 /* check that text is saved */
1457 item.mask = TVIF_TEXT;
1458 item.hItem = hRoot;
1459 item.pszText = buffA;
1460 item.cchTextMax = sizeof(buffA)/sizeof(CHAR);
1461 r = SendMessageA(hTree, TVM_GETITEMA, 0, (LPARAM)&item);
1462 expect(TRUE, r);
1463 ok(!strcmp("x", buffA), "Expected item text to change\n");
1464
1465 /* try A/W messages */
1466 edit = (HWND)SendMessageA(hTree, TVM_EDITLABELA, 0, (LPARAM)hRoot);
1467 ok(IsWindow(edit), "Expected valid handle\n");
1468 ok(IsWindowUnicode(edit), "got ansi window\n");
1469 r = SendMessageA(hTree, WM_COMMAND, MAKEWPARAM(0, EN_KILLFOCUS), (LPARAM)edit);
1470 expect(0, r);
1471 ok(!IsWindow(edit), "expected invalid handle\n");
1472
1473 edit = (HWND)SendMessageA(hTree, TVM_EDITLABELW, 0, (LPARAM)hRoot);
1474 ok(IsWindow(edit), "Expected valid handle\n");
1475 ok(IsWindowUnicode(edit), "got ansi window\n");
1476 r = SendMessageA(hTree, WM_COMMAND, MAKEWPARAM(0, EN_KILLFOCUS), (LPARAM)edit);
1477 expect(0, r);
1478
1479 /* alter text during TVM_BEGINLABELEDIT, check that it's preserved */
1480 strcpy(buffA, "<root>");
1481
1482 item.mask = TVIF_TEXT;
1483 item.hItem = hRoot;
1484 item.pszText = buffA;
1485 item.cchTextMax = 0;
1486 r = SendMessageA(hTree, TVM_SETITEMA, 0, (LPARAM)&item);
1487 expect(TRUE, r);
1488
1489 g_beginedit_alter_text = TRUE;
1490 edit = (HWND)SendMessageA(hTree, TVM_EDITLABELA, 0, (LPARAM)hRoot);
1491 ok(IsWindow(edit), "Expected valid handle\n");
1492 g_beginedit_alter_text = FALSE;
1493
1494 GetWindowTextA(edit, buffA, sizeof(buffA)/sizeof(CHAR));
1495 ok(!strcmp(buffA, "<edittextaltered>"), "got string %s\n", buffA);
1496
1497 DestroyWindow(hTree);
1498 }
1499
1500 static void test_treeview_classinfo(void)
1501 {
1502 WNDCLASSA cls;
1503
1504 memset(&cls, 0, sizeof(cls));
1505 GetClassInfoA(GetModuleHandleA("comctl32.dll"), WC_TREEVIEWA, &cls);
1506 ok(cls.hbrBackground == NULL, "Expected NULL background brush, got %p\n", cls.hbrBackground);
1507 ok(cls.style == (CS_GLOBALCLASS | CS_DBLCLKS), "Expected got %x\n", cls.style);
1508 expect(0, cls.cbClsExtra);
1509 }
1510
1511 static void test_get_linecolor(void)
1512 {
1513 COLORREF clr;
1514 HWND hTree;
1515
1516 hTree = create_treeview_control(0);
1517
1518 /* newly created control has default color */
1519 clr = SendMessageA(hTree, TVM_GETLINECOLOR, 0, 0);
1520 if (clr == 0)
1521 win_skip("TVM_GETLINECOLOR is not supported on comctl32 < 5.80\n");
1522 else
1523 expect(CLR_DEFAULT, clr);
1524
1525 DestroyWindow(hTree);
1526 }
1527
1528 static void test_get_insertmarkcolor(void)
1529 {
1530 COLORREF clr;
1531 HWND hTree;
1532
1533 hTree = create_treeview_control(0);
1534
1535 /* newly created control has default color */
1536 clr = SendMessageA(hTree, TVM_GETINSERTMARKCOLOR, 0, 0);
1537 if (clr == 0)
1538 win_skip("TVM_GETINSERTMARKCOLOR is not supported on comctl32 < 5.80\n");
1539 else
1540 expect(CLR_DEFAULT, clr);
1541
1542 DestroyWindow(hTree);
1543 }
1544
1545 static void test_expandnotify(void)
1546 {
1547 HTREEITEM hitem;
1548 HWND hTree;
1549 BOOL ret;
1550 TVITEMA item;
1551
1552 hTree = create_treeview_control(0);
1553 fill_tree(hTree);
1554
1555 item.hItem = hRoot;
1556 item.mask = TVIF_STATE;
1557
1558 item.state = TVIS_EXPANDED;
1559 ret = SendMessageA(hTree, TVM_GETITEMA, 0, (LPARAM)&item);
1560 expect(TRUE, ret);
1561 ok((item.state & TVIS_EXPANDED) == 0, "expected collapsed\n");
1562
1563 /* preselect root node here */
1564 ret = SendMessageA(hTree, TVM_SELECTITEM, TVGN_CARET, (LPARAM)hRoot);
1565 expect(TRUE, ret);
1566
1567 flush_sequences(sequences, NUM_MSG_SEQUENCES);
1568 ret = SendMessageA(hTree, TVM_EXPAND, TVE_COLLAPSE, (LPARAM)hRoot);
1569 expect(FALSE, ret);
1570 ok_sequence(sequences, PARENT_SEQ_INDEX, empty_seq, "no collapse notifications", FALSE);
1571
1572 g_get_from_expand = TRUE;
1573 /* expand */
1574 flush_sequences(sequences, NUM_MSG_SEQUENCES);
1575 g_item_expanding.state = 0xdeadbeef;
1576 g_item_expanded.state = 0xdeadbeef;
1577 ret = SendMessageA(hTree, TVM_EXPAND, TVE_EXPAND, (LPARAM)hRoot);
1578 expect(TRUE, ret);
1579 ok(g_item_expanding.state == TVIS_SELECTED, "got state on TVN_ITEMEXPANDING 0x%08x\n",
1580 g_item_expanding.state);
1581 ok(g_item_expanded.state == (TVIS_SELECTED|TVIS_EXPANDED), "got state on TVN_ITEMEXPANDED 0x%08x\n",
1582 g_item_expanded.state);
1583 ok_sequence(sequences, PARENT_SEQ_INDEX, parent_expand_seq, "expand notifications", FALSE);
1584 g_get_from_expand = FALSE;
1585
1586 /* check that it's expanded */
1587 item.state = TVIS_EXPANDED;
1588 ret = SendMessageA(hTree, TVM_GETITEMA, 0, (LPARAM)&item);
1589 expect(TRUE, ret);
1590 ok((item.state & TVIS_EXPANDED) == TVIS_EXPANDED, "expected expanded\n");
1591
1592 /* collapse */
1593 flush_sequences(sequences, NUM_MSG_SEQUENCES);
1594 ret = SendMessageA(hTree, TVM_EXPAND, TVE_COLLAPSE, (LPARAM)hRoot);
1595 expect(TRUE, ret);
1596 item.state = TVIS_EXPANDED;
1597 ret = SendMessageA(hTree, TVM_GETITEMA, 0, (LPARAM)&item);
1598 expect(TRUE, ret);
1599 ok((item.state & TVIS_EXPANDED) == 0, "expected collapsed\n");
1600 /* all further collapse/expand attempts won't produce any notifications,
1601 the only way is to reset with all children removed */
1602 ok_sequence(sequences, PARENT_SEQ_INDEX, empty_seq, "collapse after expand notifications", FALSE);
1603
1604 /* try to toggle child that doesn't have children itself */
1605 flush_sequences(sequences, NUM_MSG_SEQUENCES);
1606 ret = SendMessageA(hTree, TVM_EXPAND, TVE_TOGGLE, (LPARAM)hChild);
1607 expect(FALSE, ret);
1608 ok_sequence(sequences, PARENT_SEQ_INDEX, empty_seq, "toggle node without children", FALSE);
1609
1610 DestroyWindow(hTree);
1611
1612 /* test TVM_GETITEMRECT inside TVN_ITEMEXPANDED notification */
1613 hTree = create_treeview_control(0);
1614 fill_tree(hTree);
1615 g_get_rect_in_expand = TRUE;
1616 ret = SendMessageA(hTree, TVM_SELECTITEM, TVGN_CARET, (LPARAM)hChild);
1617 expect(TRUE, ret);
1618 g_get_rect_in_expand = FALSE;
1619
1620 DestroyWindow(hTree);
1621
1622 /* TVE_TOGGLE acts as any other TVM_EXPAND */
1623 hTree = create_treeview_control(0);
1624 fill_tree(hTree);
1625
1626 flush_sequences(sequences, NUM_MSG_SEQUENCES);
1627 ret = SendMessageA(hTree, TVM_EXPAND, TVE_TOGGLE, (LPARAM)hRoot);
1628 expect(TRUE, ret);
1629 ok_sequence(sequences, PARENT_SEQ_INDEX, parent_expand_seq, "toggle node (expand)", FALSE);
1630
1631 /* toggle again - no notifications */
1632 flush_sequences(sequences, NUM_MSG_SEQUENCES);
1633 ret = SendMessageA(hTree, TVM_EXPAND, TVE_TOGGLE, (LPARAM)hRoot);
1634 expect(TRUE, ret);
1635 ok_sequence(sequences, PARENT_SEQ_INDEX, empty_seq, "toggle node (collapse)", FALSE);
1636
1637 DestroyWindow(hTree);
1638
1639 /* some keyboard events are also translated to expand */
1640 hTree = create_treeview_control(0);
1641 fill_tree(hTree);
1642
1643 /* preselect root node here */
1644 ret = SendMessageA(hTree, TVM_SELECTITEM, TVGN_CARET, (LPARAM)hRoot);
1645 expect(TRUE, ret);
1646
1647 g_get_from_expand = TRUE;
1648 flush_sequences(sequences, NUM_MSG_SEQUENCES);
1649 ret = SendMessageA(hTree, WM_KEYDOWN, VK_ADD, 0);
1650 expect(FALSE, ret);
1651 ok_sequence(sequences, PARENT_SEQ_INDEX, parent_expand_kb_seq, "expand node", FALSE);
1652 ok(g_item_expanding.state == TVIS_SELECTED, "got state on TVN_ITEMEXPANDING 0x%08x\n",
1653 g_item_expanding.state);
1654 ok(g_item_expanded.state == (TVIS_SELECTED|TVIS_EXPANDED), "got state on TVN_ITEMEXPANDED 0x%08x\n",
1655 g_item_expanded.state);
1656
1657 flush_sequences(sequences, NUM_MSG_SEQUENCES);
1658 ret = SendMessageA(hTree, WM_KEYDOWN, VK_ADD, 0);
1659 expect(FALSE, ret);
1660 ok_sequence(sequences, PARENT_SEQ_INDEX, parent_expand_kb_seq, "expand node again", FALSE);
1661 ok(g_item_expanding.state == (TVIS_SELECTED|TVIS_EXPANDED|TVIS_EXPANDEDONCE), "got state on TVN_ITEMEXPANDING 0x%08x\n",
1662 g_item_expanding.state);
1663 ok(g_item_expanded.state == (TVIS_SELECTED|TVIS_EXPANDED|TVIS_EXPANDEDONCE), "got state on TVN_ITEMEXPANDED 0x%08x\n",
1664 g_item_expanded.state);
1665
1666 flush_sequences(sequences, NUM_MSG_SEQUENCES);
1667 ret = SendMessageA(hTree, WM_KEYDOWN, VK_SUBTRACT, 0);
1668 expect(FALSE, ret);
1669 ok_sequence(sequences, PARENT_SEQ_INDEX, parent_expand_kb_seq, "collapse node", FALSE);
1670 ok(g_item_expanding.state == (TVIS_SELECTED|TVIS_EXPANDED|TVIS_EXPANDEDONCE), "got state on TVN_ITEMEXPANDING 0x%08x\n",
1671 g_item_expanding.state);
1672 ok(g_item_expanded.state == (TVIS_SELECTED|TVIS_EXPANDEDONCE), "got state on TVN_ITEMEXPANDED 0x%08x\n",
1673 g_item_expanded.state);
1674
1675 flush_sequences(sequences, NUM_MSG_SEQUENCES);
1676 ret = SendMessageA(hTree, WM_KEYDOWN, VK_SUBTRACT, 0);
1677 expect(FALSE, ret);
1678 ok_sequence(sequences, PARENT_SEQ_INDEX, parent_collapse_2nd_kb_seq, "collapse node again", FALSE);
1679 ok(g_item_expanding.state == (TVIS_SELECTED|TVIS_EXPANDEDONCE), "got state on TVN_ITEMEXPANDING 0x%08x\n",
1680 g_item_expanding.state);
1681 g_get_from_expand = FALSE;
1682
1683 flush_sequences(sequences, NUM_MSG_SEQUENCES);
1684 ret = SendMessageA(hTree, WM_KEYDOWN, VK_ADD, 0);
1685 expect(FALSE, ret);
1686 ok_sequence(sequences, PARENT_SEQ_INDEX, parent_expand_kb_seq, "expand node", FALSE);
1687
1688 /* go to child */
1689 ret = SendMessageA(hTree, WM_KEYDOWN, VK_RIGHT, 0);
1690 expect(FALSE, ret);
1691
1692 /* try to expand child that doesn't have children itself */
1693 flush_sequences(sequences, NUM_MSG_SEQUENCES);
1694 ret = SendMessageA(hTree, WM_KEYDOWN, VK_ADD, 0);
1695 expect(FALSE, ret);
1696 ok_sequence(sequences, PARENT_SEQ_INDEX, parent_expand_empty_kb_seq, "expand node with no children", FALSE);
1697
1698 /* stay on current selection and set non-zero children count */
1699 hitem = (HTREEITEM)SendMessageA(hTree, TVM_GETNEXTITEM, TVGN_CARET, 0);
1700 ok(hitem != NULL, "got %p\n", hitem);
1701
1702 item.hItem = hitem;
1703 item.mask = TVIF_CHILDREN;
1704 item.cChildren = 0x80000000;
1705
1706 ret = SendMessageA(hTree, TVM_SETITEMA, 0, (LPARAM)&item);
1707 expect(TRUE, ret);
1708
1709 flush_sequences(sequences, NUM_MSG_SEQUENCES);
1710 ret = SendMessageA(hTree, WM_KEYDOWN, VK_ADD, 0);
1711 expect(FALSE, ret);
1712 ok_sequence(sequences, PARENT_SEQ_INDEX, parent_collapse_2nd_kb_seq, "expand node with children", FALSE);
1713
1714 DestroyWindow(hTree);
1715 }
1716
1717 static void test_expandedimage(void)
1718 {
1719 TVITEMEXA item;
1720 HWND hTree;
1721 BOOL ret;
1722
1723 hTree = create_treeview_control(0);
1724 fill_tree(hTree);
1725
1726 item.mask = TVIF_EXPANDEDIMAGE;
1727 item.iExpandedImage = 1;
1728 item.hItem = hRoot;
1729 ret = SendMessageA(hTree, TVM_SETITEMA, 0, (LPARAM)&item);
1730 ok(ret, "got %d\n", ret);
1731
1732 item.mask = TVIF_EXPANDEDIMAGE;
1733 item.iExpandedImage = -1;
1734 item.hItem = hRoot;
1735 ret = SendMessageA(hTree, TVM_GETITEMA, 0, (LPARAM)&item);
1736 ok(ret, "got %d\n", ret);
1737
1738 if (item.iExpandedImage != 1)
1739 {
1740 win_skip("TVIF_EXPANDEDIMAGE not supported\n");
1741 DestroyWindow(hTree);
1742 return;
1743 }
1744
1745 /* test for default iExpandedImage value */
1746 item.mask = TVIF_EXPANDEDIMAGE;
1747 item.iExpandedImage = -1;
1748 item.hItem = hChild;
1749 ret = SendMessageA(hTree, TVM_GETITEMA, 0, (LPARAM)&item);
1750 ok(ret, "got %d\n", ret);
1751 ok(item.iExpandedImage == (WORD)I_IMAGENONE, "got %d\n", item.iExpandedImage);
1752
1753 DestroyWindow(hTree);
1754 }
1755
1756 static void test_TVS_SINGLEEXPAND(void)
1757 {
1758 HWND hTree;
1759 HTREEITEM alpha, bravo, charlie, delta, echo, foxtrot, golf, hotel, india, juliet;
1760 TVINSERTSTRUCTA ins;
1761 char foo[] = "foo";
1762 char context[32];
1763 int i;
1764 BOOL ret;
1765
1766 /* build a fairly complex tree
1767 * - TVI_ROOT
1768 * - alpha
1769 * - bravo
1770 * - charlie
1771 * - delta
1772 * - echo
1773 * - foxtrot
1774 * - golf
1775 * - hotel
1776 * - india
1777 * - juliet
1778 */
1779 struct
1780 {
1781 HTREEITEM *handle;
1782 HTREEITEM *parent;
1783 UINT final_state;
1784 }
1785 items[] =
1786 {
1787 { &alpha, NULL, TVIS_EXPANDEDONCE },
1788 { &bravo, &alpha, TVIS_EXPANDEDONCE },
1789 { &charlie, &bravo, 0 },
1790 { &delta, &alpha, TVIS_EXPANDEDONCE },
1791 { &echo, &delta, 0 },
1792 { &foxtrot, NULL, TVIS_EXPANDEDONCE|TVIS_EXPANDED },
1793 { &golf, &foxtrot, TVIS_EXPANDEDONCE|TVIS_EXPANDED },
1794 { &hotel, &golf, 0 },
1795 { &india, &golf, TVIS_SELECTED },
1796 { &juliet, &foxtrot, 0 }
1797 };
1798
1799 struct
1800 {
1801 HTREEITEM *select;
1802 const struct message *sequence;
1803 }
1804 sequence_tests[] =
1805 {
1806 { &alpha, parent_singleexpand_seq0 },
1807 { &bravo, parent_singleexpand_seq1 },
1808 { &delta, parent_singleexpand_seq2 },
1809 { &foxtrot, parent_singleexpand_seq3 },
1810 { &alpha, parent_singleexpand_seq4 },
1811 { &golf, parent_singleexpand_seq5 },
1812 { &hotel, parent_singleexpand_seq6 },
1813 { &india, parent_singleexpand_seq7 },
1814 { &india, empty_seq }
1815 };
1816
1817 hTree = create_treeview_control(0);
1818 SetWindowLongA(hTree, GWL_STYLE, GetWindowLongA(hTree, GWL_STYLE) | TVS_SINGLEEXPAND);
1819 /* to avoid painting related notifications */
1820 ShowWindow(hTree, SW_HIDE);
1821 for (i = 0; i < sizeof(items)/sizeof(items[0]); i++)
1822 {
1823 ins.hParent = items[i].parent ? *items[i].parent : TVI_ROOT;
1824 ins.hInsertAfter = TVI_FIRST;
1825 U(ins).item.mask = TVIF_TEXT;
1826 U(ins).item.pszText = foo;
1827 *items[i].handle = TreeView_InsertItemA(hTree, &ins);
1828 }
1829
1830 for (i = 0; i < sizeof(sequence_tests)/sizeof(sequence_tests[0]); i++)
1831 {
1832 flush_sequences(sequences, NUM_MSG_SEQUENCES);
1833 ret = SendMessageA(hTree, TVM_SELECTITEM, TVGN_CARET, (LPARAM)(*sequence_tests[i].select));
1834 ok(ret, "got %d\n", ret);
1835 sprintf(context, "singleexpand notifications %d", i);
1836 ok_sequence(sequences, PARENT_SEQ_INDEX, sequence_tests[i].sequence, context, FALSE);
1837 }
1838
1839 for (i = 0; i < sizeof(items)/sizeof(items[0]); i++)
1840 {
1841 ret = SendMessageA(hTree, TVM_GETITEMSTATE, (WPARAM)(*items[i].handle), 0xFFFF);
1842 ok(ret == items[i].final_state, "singleexpand items[%d]: expected state 0x%x got 0x%x\n",
1843 i, items[i].final_state, ret);
1844 }
1845
1846 /* a workaround for NT4 that sends expand notifications when nothing is about to expand */
1847 ret = SendMessageA(hTree, TVM_DELETEITEM, 0, (LPARAM)TVI_ROOT);
1848 ok(ret, "got %d\n", ret);
1849 fill_tree(hTree);
1850 ret = SendMessageA(hTree, TVM_SELECTITEM, TVGN_CARET, 0);
1851 ok(ret, "got %d\n", ret);
1852
1853 DestroyWindow(hTree);
1854 }
1855
1856 static void test_WM_PAINT(void)
1857 {
1858 HWND hTree;
1859 COLORREF clr;
1860 LONG ret;
1861 RECT rc;
1862 HDC hdc;
1863
1864 hTree = create_treeview_control(0);
1865
1866 clr = SendMessageA(hTree, TVM_SETBKCOLOR, 0, RGB(255, 0, 0));
1867 ok(clr == ~0u, "got %d, expected -1\n", clr);
1868
1869 hdc = GetDC(hMainWnd);
1870
1871 GetClientRect(hMainWnd, &rc);
1872 FillRect(hdc, &rc, GetStockObject(BLACK_BRUSH));
1873
1874 clr = GetPixel(hdc, 1, 1);
1875 ok(clr == RGB(0, 0, 0), "got 0x%x\n", clr);
1876
1877 ret = SendMessageA(hTree, WM_PAINT, (WPARAM)hdc, 0);
1878 ok(ret == 0, "got %d\n", ret);
1879
1880 clr = GetPixel(hdc, 1, 1);
1881 ok(clr == RGB(255, 0, 0) || broken(clr == RGB(0, 0, 0)) /* win98 */,
1882 "got 0x%x\n", clr);
1883
1884 ReleaseDC(hMainWnd, hdc);
1885
1886 DestroyWindow(hTree);
1887 }
1888
1889 static void test_delete_items(void)
1890 {
1891 const struct message *msg;
1892 HWND hTree;
1893 HTREEITEM hItem1, hItem2;
1894 TVINSERTSTRUCTA ins;
1895 INT ret;
1896
1897 static CHAR item1[] = "Item 1";
1898 static CHAR item2[] = "Item 2";
1899
1900 hTree = create_treeview_control(0);
1901 fill_tree(hTree);
1902
1903 /* check delete order */
1904 flush_sequences(item_sequence, 1);
1905 ret = SendMessageA(hTree, TVM_DELETEITEM, 0, 0);
1906 ok(ret == TRUE, "got %d\n", ret);
1907
1908 msg = item_sequence[0]->sequence;
1909 ok(item_sequence[0]->count == 2, "expected 2 items, got %d\n", item_sequence[0]->count);
1910
1911 if (item_sequence[0]->count == 2)
1912 {
1913 ok(msg[0].lParam == (LPARAM)hChild, "expected %p, got 0x%lx\n", hChild, msg[0].lParam);
1914 ok(msg[1].lParam == (LPARAM)hRoot, "expected %p, got 0x%lx\n", hRoot, msg[1].lParam);
1915 }
1916
1917 ret = SendMessageA(hTree, TVM_GETCOUNT, 0, 0);
1918 ok(ret == 0, "got %d\n", ret);
1919
1920 DestroyWindow(hTree);
1921
1922 /* Regression test for a crash when deleting the first visible item while bRedraw == false. */
1923 hTree = create_treeview_control(0);
1924
1925 ret = SendMessageA(hTree, WM_SETREDRAW, FALSE, 0);
1926 ok(ret == 0, "got %d\n", ret);
1927
1928 ins.hParent = TVI_ROOT;
1929 ins.hInsertAfter = TVI_ROOT;
1930 U(ins).item.mask = TVIF_TEXT;
1931 U(ins).item.pszText = item1;
1932 hItem1 = TreeView_InsertItemA(hTree, &ins);
1933 ok(hItem1 != NULL, "InsertItem failed\n");
1934
1935 ins.hParent = TVI_ROOT;
1936 ins.hInsertAfter = hItem1;
1937 U(ins).item.mask = TVIF_TEXT;
1938 U(ins).item.pszText = item2;
1939 hItem2 = TreeView_InsertItemA(hTree, &ins);
1940 ok(hItem2 != NULL, "InsertItem failed\n");
1941
1942 ret = SendMessageA(hTree, TVM_DELETEITEM, 0, (LPARAM)hItem1);
1943 ok(ret == TRUE, "got %d\n", ret);
1944
1945 ret = SendMessageA(hTree, WM_SETREDRAW, TRUE, 0);
1946 ok(ret == 0, "got %d\n", ret);
1947
1948 DestroyWindow(hTree);
1949 }
1950
1951 static void test_cchildren(void)
1952 {
1953 HWND hTree;
1954 INT ret;
1955 TVITEMA item;
1956
1957 hTree = create_treeview_control(0);
1958 fill_tree(hTree);
1959
1960 ret = SendMessageA(hTree, TVM_DELETEITEM, 0, (LPARAM)hChild);
1961 expect(TRUE, ret);
1962
1963 /* check cChildren - automatic mode */
1964 item.hItem = hRoot;
1965 item.mask = TVIF_CHILDREN;
1966
1967 ret = SendMessageA(hTree, TVM_GETITEMA, 0, (LPARAM)&item);
1968 expect(TRUE, ret);
1969 expect(0, item.cChildren);
1970
1971 DestroyWindow(hTree);
1972
1973 /* start over */
1974 hTree = create_treeview_control(0);
1975 fill_tree(hTree);
1976
1977 /* turn off automatic mode by setting cChildren explicitly */
1978 item.hItem = hRoot;
1979 item.mask = TVIF_CHILDREN;
1980
1981 ret = SendMessageA(hTree, TVM_GETITEMA, 0, (LPARAM)&item);
1982 expect(TRUE, ret);
1983 expect(1, item.cChildren);
1984
1985 ret = SendMessageA(hTree, TVM_SETITEMA, 0, (LPARAM)&item);
1986 expect(TRUE, ret);
1987
1988 ret = SendMessageA(hTree, TVM_DELETEITEM, 0, (LPARAM)hChild);
1989 expect(TRUE, ret);
1990
1991 /* check cChildren */
1992 ret = SendMessageA(hTree, TVM_GETITEMA, 0, (LPARAM)&item);
1993 expect(TRUE, ret);
1994 todo_wine
1995 expect(1, item.cChildren);
1996
1997 DestroyWindow(hTree);
1998 }
1999
2000 struct _ITEM_DATA
2001 {
2002 HTREEITEM parent; /* for root value of parent field is unidetified */
2003 HTREEITEM nextsibling;
2004 HTREEITEM firstchild;
2005 };
2006
2007 static void _check_item(HTREEITEM item, HTREEITEM parent, HTREEITEM nextsibling, HTREEITEM firstchild, int line)
2008 {
2009 struct _ITEM_DATA *data = (struct _ITEM_DATA*)item;
2010
2011 ok_(__FILE__, line)(data->parent == parent, "parent %p, got %p\n", parent, data->parent);
2012 ok_(__FILE__, line)(data->nextsibling == nextsibling, "sibling %p, got %p\n", nextsibling, data->nextsibling);
2013 ok_(__FILE__, line)(data->firstchild == firstchild, "firstchild %p, got %p\n", firstchild, data->firstchild);
2014 }
2015
2016 #define check_item(a, b, c, d) _check_item(a, b, c, d, __LINE__)
2017
2018 static void test_htreeitem_layout(void)
2019 {
2020 TVINSERTSTRUCTA ins;
2021 HTREEITEM item1, item2;
2022 HWND hTree;
2023
2024 hTree = create_treeview_control(0);
2025 fill_tree(hTree);
2026
2027 /* root has some special pointer in parent field */
2028 check_item(hRoot, ((struct _ITEM_DATA*)hRoot)->parent, 0, hChild);
2029 check_item(hChild, hRoot, 0, 0);
2030
2031 ins.hParent = hChild;
2032 ins.hInsertAfter = TVI_FIRST;
2033 U(ins).item.mask = 0;
2034 item1 = TreeView_InsertItemA(hTree, &ins);
2035
2036 check_item(item1, hChild, 0, 0);
2037
2038 ins.hParent = hRoot;
2039 ins.hInsertAfter = TVI_FIRST;
2040 U(ins).item.mask = 0;
2041 item2 = TreeView_InsertItemA(hTree, &ins);
2042
2043 check_item(item2, hRoot, hChild, 0);
2044
2045 SendMessageA(hTree, TVM_DELETEITEM, 0, (LPARAM)hChild);
2046
2047 /* without children now */
2048 check_item(hRoot, ((struct _ITEM_DATA*)hRoot)->parent, 0, item2);
2049
2050 DestroyWindow(hTree);
2051 }
2052
2053 static void test_TVS_CHECKBOXES(void)
2054 {
2055 HIMAGELIST himl, himl2;
2056 HWND hTree, hTree2;
2057 TVITEMA item;
2058 DWORD ret;
2059 MSG msg;
2060
2061 hTree = create_treeview_control(0);
2062 fill_tree(hTree);
2063
2064 himl = (HIMAGELIST)SendMessageA(hTree, TVM_GETIMAGELIST, TVSIL_STATE, 0);
2065 ok(himl == NULL, "got %p\n", himl);
2066
2067 item.hItem = hRoot;
2068 item.mask = TVIF_STATE;
2069 item.state = INDEXTOSTATEIMAGEMASK(1);
2070 item.stateMask = TVIS_STATEIMAGEMASK;
2071 ret = SendMessageA(hTree, TVM_GETITEMA, 0, (LPARAM)&item);
2072 expect(TRUE, ret);
2073 ok(item.state == 0, "got 0x%x\n", item.state);
2074
2075 /* set some index for a child */
2076 item.hItem = hChild;
2077 item.mask = TVIF_STATE;
2078 item.state = INDEXTOSTATEIMAGEMASK(4);
2079 item.stateMask = TVIS_STATEIMAGEMASK;
2080 ret = SendMessageA(hTree, TVM_SETITEMA, 0, (LPARAM)&item);
2081 expect(TRUE, ret);
2082
2083 /* enabling check boxes set all items to 1 state image index */
2084 SetWindowLongA(hTree, GWL_STYLE, GetWindowLongA(hTree, GWL_STYLE) | TVS_CHECKBOXES);
2085 himl = (HIMAGELIST)SendMessageA(hTree, TVM_GETIMAGELIST, TVSIL_STATE, 0);
2086 ok(himl != NULL, "got %p\n", himl);
2087
2088 himl2 = (HIMAGELIST)SendMessageA(hTree, TVM_GETIMAGELIST, TVSIL_STATE, 0);
2089 ok(himl2 != NULL, "got %p\n", himl2);
2090 ok(himl2 == himl, "got %p, expected %p\n", himl2, himl);
2091
2092 item.hItem = hRoot;
2093 item.mask = TVIF_STATE;
2094 item.state = 0;
2095 item.stateMask = TVIS_STATEIMAGEMASK;
2096 ret = SendMessageA(hTree, TVM_GETITEMA, 0, (LPARAM)&item);
2097 expect(TRUE, ret);
2098 ok(item.state == INDEXTOSTATEIMAGEMASK(1), "got 0x%x\n", item.state);
2099
2100 item.hItem = hChild;
2101 item.mask = TVIF_STATE;
2102 item.state = 0;
2103 item.stateMask = TVIS_STATEIMAGEMASK;
2104 ret = SendMessageA(hTree, TVM_GETITEMA, 0, (LPARAM)&item);
2105 expect(TRUE, ret);
2106 ok(item.state == INDEXTOSTATEIMAGEMASK(1), "got 0x%x\n", item.state);
2107
2108 /* create another control and check its checkbox list */
2109 hTree2 = create_treeview_control(0);
2110 fill_tree(hTree2);
2111
2112 /* set some index for a child */
2113 item.hItem = hChild;
2114 item.mask = TVIF_STATE;
2115 item.state = INDEXTOSTATEIMAGEMASK(4);
2116 item.stateMask = TVIS_STATEIMAGEMASK;
2117 ret = SendMessageA(hTree2, TVM_SETITEMA, 0, (LPARAM)&item);
2118 expect(TRUE, ret);
2119
2120 /* enabling check boxes set all items to 1 state image index */
2121 SetWindowLongA(hTree2, GWL_STYLE, GetWindowLongA(hTree, GWL_STYLE) | TVS_CHECKBOXES);
2122 himl2 = (HIMAGELIST)SendMessageA(hTree2, TVM_GETIMAGELIST, TVSIL_STATE, 0);
2123 ok(himl2 != NULL, "got %p\n", himl2);
2124 ok(himl != himl2, "got %p, expected %p\n", himl2, himl);
2125
2126 DestroyWindow(hTree2);
2127 DestroyWindow(hTree);
2128
2129 /* the same, but initially created with TVS_CHECKBOXES */
2130 hTree = create_treeview_control(TVS_CHECKBOXES);
2131 fill_tree(hTree);
2132 himl = (HIMAGELIST)SendMessageA(hTree, TVM_GETIMAGELIST, TVSIL_STATE, 0);
2133 ok(himl == NULL, "got %p\n", himl);
2134
2135 item.hItem = hRoot;
2136 item.mask = TVIF_STATE;
2137 item.state = 0;
2138 item.stateMask = TVIS_STATEIMAGEMASK;
2139 ret = SendMessageA(hTree, TVM_GETITEMA, 0, (LPARAM)&item);
2140 expect(TRUE, ret);
2141 ok(item.state == INDEXTOSTATEIMAGEMASK(1), "got 0x%x\n", item.state);
2142
2143 item.hItem = hChild;
2144 item.mask = TVIF_STATE;
2145 item.state = 0;
2146 item.stateMask = TVIS_STATEIMAGEMASK;
2147 ret = SendMessageA(hTree, TVM_GETITEMA, 0, (LPARAM)&item);
2148 expect(TRUE, ret);
2149 ok(item.state == INDEXTOSTATEIMAGEMASK(1), "got 0x%x\n", item.state);
2150
2151 item.hItem = hChild;
2152 item.mask = TVIF_STATE;
2153 item.state = INDEXTOSTATEIMAGEMASK(2);
2154 item.stateMask = TVIS_STATEIMAGEMASK;
2155 ret = SendMessageA(hTree, TVM_SETITEMA, 0, (LPARAM)&item);
2156 expect(TRUE, ret);
2157
2158 item.hItem = hChild;
2159 item.mask = TVIF_STATE;
2160 item.state = 0;
2161 ret = SendMessageA(hTree, TVM_GETITEMA, 0, (LPARAM)&item);
2162 expect(TRUE, ret);
2163 ok(item.state == INDEXTOSTATEIMAGEMASK(2), "got 0x%x\n", item.state);
2164
2165 while(GetMessageA(&msg, 0, 0, 0))
2166 {
2167 TranslateMessage(&msg);
2168 DispatchMessageA(&msg);
2169
2170 if((msg.hwnd == hTree) && (msg.message == WM_PAINT))
2171 break;
2172 }
2173
2174 item.hItem = hChild;
2175 item.mask = TVIF_STATE;
2176 item.state = 0;
2177 ret = SendMessageA(hTree, TVM_GETITEMA, 0, (LPARAM)&item);
2178 expect(TRUE, ret);
2179 ok(item.state == INDEXTOSTATEIMAGEMASK(1), "got 0x%x\n", item.state);
2180
2181 himl = (HIMAGELIST)SendMessageA(hTree, TVM_GETIMAGELIST, TVSIL_STATE, 0);
2182 ok(himl != NULL, "got %p\n", himl);
2183
2184 DestroyWindow(hTree);
2185
2186 /* check what happens if TVSIL_STATE image list is removed */
2187 hTree = create_treeview_control(0);
2188 fill_tree(hTree);
2189 himl = (HIMAGELIST)SendMessageA(hTree, TVM_GETIMAGELIST, TVSIL_STATE, 0);
2190 ok(himl == NULL, "got %p\n", himl);
2191
2192 SetWindowLongA(hTree, GWL_STYLE, GetWindowLongA(hTree, GWL_STYLE) | TVS_CHECKBOXES);
2193 himl = (HIMAGELIST)SendMessageA(hTree, TVM_GETIMAGELIST, TVSIL_STATE, 0);
2194 ok(himl != NULL, "got %p\n", himl);
2195
2196 himl2 = (HIMAGELIST)SendMessageA(hTree, TVM_SETIMAGELIST, TVSIL_STATE, 0);
2197 ok(himl2 == himl, "got %p\n", himl2);
2198
2199 himl2 = (HIMAGELIST)SendMessageA(hTree, TVM_GETIMAGELIST, TVSIL_STATE, 0);
2200 ok(himl2 == NULL, "got %p\n", himl2);
2201
2202 item.hItem = hChild;
2203 item.mask = TVIF_STATE;
2204 item.state = INDEXTOSTATEIMAGEMASK(2);
2205 item.stateMask = TVIS_STATEIMAGEMASK;
2206 ret = SendMessageA(hTree, TVM_SETITEMA, 0, (LPARAM)&item);
2207 expect(TRUE, ret);
2208
2209 item.hItem = hChild;
2210 item.mask = TVIF_STATE;
2211 item.state = 0;
2212 ret = SendMessageA(hTree, TVM_GETITEMA, 0, (LPARAM)&item);
2213 expect(TRUE, ret);
2214 ok(item.state == INDEXTOSTATEIMAGEMASK(2), "got 0x%x\n", item.state);
2215
2216 while(GetMessageA(&msg, 0, 0, 0))
2217 {
2218 TranslateMessage(&msg);
2219 DispatchMessageA(&msg);
2220
2221 if((msg.hwnd == hTree) && (msg.message == WM_PAINT))
2222 break;
2223 }
2224
2225 item.hItem = hChild;
2226 item.mask = TVIF_STATE;
2227 item.state = 0;
2228 ret = SendMessageA(hTree, TVM_GETITEMA, 0, (LPARAM)&item);
2229 expect(TRUE, ret);
2230 ok(item.state == INDEXTOSTATEIMAGEMASK(1), "got 0x%x\n", item.state);
2231
2232 himl = (HIMAGELIST)SendMessageA(hTree, TVM_GETIMAGELIST, TVSIL_STATE, 0);
2233 ok(himl != NULL, "got %p\n", himl);
2234
2235 DestroyWindow(hTree);
2236 }
2237
2238 static void test_TVM_GETNEXTITEM(void)
2239 {
2240 HTREEITEM item;
2241 HWND hTree;
2242
2243 hTree = create_treeview_control(0);
2244 fill_tree(hTree);
2245
2246 item = (HTREEITEM)SendMessageA(hTree, TVM_GETNEXTITEM, TVGN_ROOT, 0);
2247 ok(item == hRoot, "got %p, expected %p\n", item, hRoot);
2248
2249 item = (HTREEITEM)SendMessageA(hTree, TVM_GETNEXTITEM, TVGN_ROOT, (LPARAM)TVI_ROOT);
2250 ok(item == hRoot, "got %p, expected %p\n", item, hRoot);
2251
2252 item = (HTREEITEM)SendMessageA(hTree, TVM_GETNEXTITEM, TVGN_ROOT, (LPARAM)hRoot);
2253 ok(item == hRoot, "got %p, expected %p\n", item, hRoot);
2254
2255 item = (HTREEITEM)SendMessageA(hTree, TVM_GETNEXTITEM, TVGN_ROOT, (LPARAM)hChild);
2256 ok(item == hRoot, "got %p, expected %p\n", item, hRoot);
2257
2258 item = (HTREEITEM)SendMessageA(hTree, TVM_GETNEXTITEM, TVGN_CHILD, 0);
2259 ok(item == hRoot, "got %p, expected %p\n", item, hRoot);
2260
2261 item = (HTREEITEM)SendMessageA(hTree, TVM_GETNEXTITEM, TVGN_CHILD, (LPARAM)hRoot);
2262 ok(item == hChild, "got %p, expected %p\n", item, hChild);
2263
2264 item = (HTREEITEM)SendMessageA(hTree, TVM_GETNEXTITEM, TVGN_CHILD, (LPARAM)TVI_ROOT);
2265 ok(item == hRoot, "got %p, expected %p\n", item, hRoot);
2266
2267 item = (HTREEITEM)SendMessageA(hTree, TVM_GETNEXTITEM, TVGN_PARENT, 0);
2268 ok(item == NULL, "got %p\n", item);
2269
2270 item = (HTREEITEM)SendMessageA(hTree, TVM_GETNEXTITEM, TVGN_PARENT, (LPARAM)hChild);
2271 ok(item == hRoot, "got %p, expected %p\n", item, hRoot);
2272
2273 DestroyWindow(hTree);
2274 }
2275
2276 static void test_TVM_HITTEST(void)
2277 {
2278 HWND hTree;
2279 LRESULT ret;
2280 RECT rc;
2281 TVHITTESTINFO ht;
2282
2283 hTree = create_treeview_control(0);
2284 fill_tree(hTree);
2285
2286 *(HTREEITEM*)&rc = hRoot;
2287 ret = SendMessageA(hTree, TVM_GETITEMRECT, TRUE, (LPARAM)&rc);
2288 expect(TRUE, (BOOL)ret);
2289
2290 ht.pt.x = rc.left-1;
2291 ht.pt.y = rc.top;
2292
2293 ret = SendMessageA(hTree, TVM_HITTEST, 0, (LPARAM)&ht);
2294 ok((HTREEITEM)ret == hRoot, "got %p, expected %p\n", (HTREEITEM)ret, hRoot);
2295 ok(ht.hItem == hRoot, "got %p, expected %p\n", ht.hItem, hRoot);
2296 ok(ht.flags == TVHT_ONITEMBUTTON, "got %d, expected %d\n", ht.flags, TVHT_ONITEMBUTTON);
2297
2298 ret = SendMessageA(hTree, TVM_EXPAND, TVE_EXPAND, (LPARAM)hRoot);
2299 expect(TRUE, (BOOL)ret);
2300
2301 *(HTREEITEM*)&rc = hChild;
2302 ret = SendMessageA(hTree, TVM_GETITEMRECT, TRUE, (LPARAM)&rc);
2303 expect(TRUE, (BOOL)ret);
2304
2305 ht.pt.x = rc.left-1;
2306 ht.pt.y = rc.top;
2307
2308 ret = SendMessageA(hTree, TVM_HITTEST, 0, (LPARAM)&ht);
2309 ok((HTREEITEM)ret == hChild, "got %p, expected %p\n", (HTREEITEM)ret, hChild);
2310 ok(ht.hItem == hChild, "got %p, expected %p\n", ht.hItem, hChild);
2311 /* Wine returns item button here, but this item has no button */
2312 todo_wine ok(ht.flags == TVHT_ONITEMINDENT, "got %d, expected %d\n", ht.flags, TVHT_ONITEMINDENT);
2313
2314 DestroyWindow(hTree);
2315 }
2316
2317 static void test_WM_GETDLGCODE(void)
2318 {
2319 DWORD code;
2320 HWND hTree;
2321
2322 hTree = create_treeview_control(0);
2323
2324 code = SendMessageA(hTree, WM_GETDLGCODE, VK_TAB, 0);
2325 ok(code == (DLGC_WANTCHARS | DLGC_WANTARROWS), "0x%08x\n", code);
2326
2327 DestroyWindow(hTree);
2328 }
2329
2330 static void test_customdraw(void)
2331 {
2332 LOGFONTA lf;
2333 HWND hwnd;
2334
2335 hwnd = create_treeview_control(0);
2336 fill_tree(hwnd);
2337 SendMessageA(hwnd, TVM_EXPAND, TVE_EXPAND, (WPARAM)hRoot);
2338
2339 /* create additional font, custom draw handler will select it */
2340 SystemParametersInfoA(SPI_GETICONTITLELOGFONT, sizeof(lf), &lf, 0);
2341 lf.lfHeight *= 2;
2342 g_customdraw_font = CreateFontIndirectA(&lf);
2343 flush_sequences(sequences, NUM_MSG_SEQUENCES);
2344 InvalidateRect(hwnd, NULL, TRUE);
2345 UpdateWindow(hwnd);
2346 ok_sequence(sequences, PARENT_CD_SEQ_INDEX, parent_cd_seq, "custom draw notifications", FALSE);
2347 DeleteObject(g_customdraw_font);
2348 g_customdraw_font = NULL;
2349
2350 DestroyWindow(hwnd);
2351 }
2352
2353 static void test_WM_KEYDOWN(void)
2354 {
2355 static const char *rootA = "root";
2356 TVINSERTSTRUCTA ins;
2357 HTREEITEM hRoot;
2358 HWND hwnd;
2359
2360 hwnd = create_treeview_control(0);
2361
2362 ins.hParent = TVI_ROOT;
2363 ins.hInsertAfter = TVI_ROOT;
2364 U(ins).item.mask = TVIF_TEXT;
2365 U(ins).item.pszText = (char*)rootA;
2366 hRoot = TreeView_InsertItemA(hwnd, &ins);
2367 ok(hRoot != NULL, "got %p\n", hRoot);
2368
2369 flush_sequences(sequences, NUM_MSG_SEQUENCES);
2370 SendMessageA(hwnd, WM_KEYDOWN, VK_RETURN, 0);
2371 ok_sequence(sequences, PARENT_SEQ_INDEX, parent_vk_return_seq, "WM_KEYDOWN/VK_RETURN parent notification", TRUE);
2372
2373 DestroyWindow(hwnd);
2374 }
2375
2376 static void test_TVS_FULLROWSELECT(void)
2377 {
2378 DWORD style;
2379 HWND hwnd;
2380
2381 /* try to create both with TVS_HASLINES and TVS_FULLROWSELECT */
2382 hwnd = create_treeview_control(TVS_FULLROWSELECT);
2383
2384 style = GetWindowLongA(hwnd, GWL_STYLE);
2385 ok((style & (TVS_FULLROWSELECT | TVS_HASLINES)) == (TVS_FULLROWSELECT | TVS_HASLINES), "got style 0x%08x\n", style);
2386
2387 DestroyWindow(hwnd);
2388
2389 /* create just with TVS_HASLINES, try to enable TVS_FULLROWSELECT later */
2390 hwnd = create_treeview_control(0);
2391
2392 style = GetWindowLongA(hwnd, GWL_STYLE);
2393 SetWindowLongA(hwnd, GWL_STYLE, style | TVS_FULLROWSELECT);
2394 style = GetWindowLongA(hwnd, GWL_STYLE);
2395 ok(style & TVS_FULLROWSELECT, "got style 0x%08x\n", style);
2396
2397 DestroyWindow(hwnd);
2398 }
2399
2400 START_TEST(treeview)
2401 {
2402 HMODULE hComctl32;
2403 BOOL (WINAPI *pInitCommonControlsEx)(const INITCOMMONCONTROLSEX*);
2404 WNDCLASSA wc;
2405 MSG msg;
2406
2407 ULONG_PTR ctx_cookie;
2408 HANDLE hCtx;
2409
2410 hComctl32 = GetModuleHandleA("comctl32.dll");
2411 pInitCommonControlsEx = (void*)GetProcAddress(hComctl32, "InitCommonControlsEx");
2412 if (pInitCommonControlsEx)
2413 {
2414 INITCOMMONCONTROLSEX iccex;
2415 iccex.dwSize = sizeof(iccex);
2416 iccex.dwICC = ICC_TREEVIEW_CLASSES;
2417 pInitCommonControlsEx(&iccex);
2418 }
2419 else
2420 InitCommonControls();
2421
2422 init_msg_sequences(sequences, NUM_MSG_SEQUENCES);
2423 init_msg_sequences(item_sequence, 1);
2424
2425 wc.style = CS_HREDRAW | CS_VREDRAW;
2426 wc.cbClsExtra = 0;
2427 wc.cbWndExtra = 0;
2428 wc.hInstance = GetModuleHandleA(NULL);
2429 wc.hIcon = NULL;
2430 wc.hCursor = LoadCursorA(NULL, (LPCSTR)IDC_IBEAM);
2431 wc.hbrBackground = GetSysColorBrush(COLOR_WINDOW);
2432 wc.lpszMenuName = NULL;
2433 wc.lpszClassName = "MyTestWnd";
2434 wc.lpfnWndProc = parent_wnd_proc;
2435 RegisterClassA(&wc);
2436
2437 hMainWnd = CreateWindowExA(0, "MyTestWnd", "Blah", WS_OVERLAPPEDWINDOW,
2438 CW_USEDEFAULT, CW_USEDEFAULT, 130, 105, NULL, NULL, GetModuleHandleA(NULL), 0);
2439
2440 ok(hMainWnd != NULL, "Failed to create parent window. Tests aborted.\n");
2441 if (!hMainWnd) return;
2442
2443 test_fillroot();
2444 test_select();
2445 test_getitemtext();
2446 test_focus();
2447 test_get_set_bkcolor();
2448 test_get_set_imagelist();
2449 test_get_set_indent();
2450 test_get_set_insertmark();
2451 test_get_set_item();
2452 test_get_set_itemheight();
2453 test_get_set_scrolltime();
2454 test_get_set_textcolor();
2455 test_get_linecolor();
2456 test_get_insertmarkcolor();
2457 test_get_set_tooltips();
2458 test_get_set_unicodeformat();
2459 test_callback();
2460 test_expandinvisible();
2461 test_itemedit();
2462 test_treeview_classinfo();
2463 test_expandnotify();
2464 test_TVS_SINGLEEXPAND();
2465 test_WM_PAINT();
2466 test_delete_items();
2467 test_cchildren();
2468 test_htreeitem_layout();
2469 test_TVS_CHECKBOXES();
2470 test_TVM_GETNEXTITEM();
2471 test_TVM_HITTEST();
2472 test_WM_GETDLGCODE();
2473 test_customdraw();
2474 test_WM_KEYDOWN();
2475 test_TVS_FULLROWSELECT();
2476
2477 if (!load_v6_module(&ctx_cookie, &hCtx))
2478 {
2479 DestroyWindow(hMainWnd);
2480 return;
2481 }
2482
2483 /* comctl32 version 6 tests start here */
2484 g_v6 = TRUE;
2485 test_expandedimage();
2486 test_htreeitem_layout();
2487 test_WM_GETDLGCODE();
2488
2489 unload_v6_module(ctx_cookie, hCtx);
2490
2491 PostMessageA(hMainWnd, WM_CLOSE, 0, 0);
2492 while(GetMessageA(&msg, 0, 0, 0))
2493 {
2494 TranslateMessage(&msg);
2495 DispatchMessageA(&msg);
2496 }
2497 }