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