comctl32/tests: Use client coordinates for right click test.
[wine.git] / dlls / comctl32 / tests / treeview.c
blobeda57ed2c81eff7444dfcf3693ce2d51aa86e584
1 /* Unit tests for treeview.
3 * Copyright 2005 Krzysztof Foltman
4 * Copyright 2007 Christopher James Peterson
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.
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.
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
21 #include <stdarg.h>
22 #include <stdio.h>
24 #include "windef.h"
25 #include "winbase.h"
26 #include "wingdi.h"
27 #include "winuser.h"
28 #include "winnls.h"
29 #include "winreg.h"
30 #include "commctrl.h"
31 #include "uxtheme.h"
32 #include "vsstyle.h"
34 #include "wine/test.h"
35 #include "v6util.h"
36 #include "msg.h"
38 static HTHEME (WINAPI *pGetWindowTheme)(HWND);
39 static BOOL (WINAPI *pIsThemeBackgroundPartiallyTransparent)(HTHEME, int, int);
41 static BOOL (WINAPI *pInitCommonControlsEx)(const INITCOMMONCONTROLSEX*);
42 static const char *TEST_CALLBACK_TEXT = "callback_text";
44 static TVITEMA g_item_expanding, g_item_expanded;
45 static BOOL g_get_from_expand;
46 static BOOL g_get_rect_in_expand;
47 static BOOL g_disp_A_to_W;
48 static BOOL g_disp_set_stateimage;
49 static BOOL g_beginedit_alter_text;
50 static const char *g_endedit_overwrite_contents;
51 static char *g_endedit_overwrite_ptr;
52 static HFONT g_customdraw_font;
53 static BOOL g_v6;
55 #define NUM_MSG_SEQUENCES 3
56 #define TREEVIEW_SEQ_INDEX 0
57 #define PARENT_SEQ_INDEX 1
58 #define PARENT_CD_SEQ_INDEX 2
60 #define expect(expected,got) expect_(__LINE__, expected, got)
61 static inline void expect_(unsigned line, DWORD expected, DWORD got)
63 ok_(__FILE__, line)(expected == got, "Expected %ld, got %ld\n", expected, got);
66 static struct msg_sequence *sequences[NUM_MSG_SEQUENCES];
67 static struct msg_sequence *item_sequence[1];
69 static void flush_events(void)
71 MSG msg;
72 int diff = 200;
73 int min_timeout = 100;
74 DWORD time = GetTickCount() + diff;
76 while (diff > 0)
78 if (MsgWaitForMultipleObjects(0, NULL, FALSE, min_timeout, QS_ALLINPUT) == WAIT_TIMEOUT) break;
79 while (PeekMessageA(&msg, 0, 0, 0, PM_REMOVE)) DispatchMessageA(&msg);
80 diff = time - GetTickCount();
84 static const struct message FillRootSeq[] = {
85 { TVM_INSERTITEMA, sent },
86 { TVM_INSERTITEMA, sent },
87 { 0 }
90 static const struct message rootnone_select_seq[] = {
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 { TVM_SELECTITEM, sent|wparam, 9 },
96 { TVM_SELECTITEM, sent|wparam, 9 },
97 { 0 }
100 static const struct message rootchild_select_seq[] = {
101 { TVM_SELECTITEM, sent|wparam, 9 },
102 { TVM_SELECTITEM, sent|wparam, 9 },
103 { TVM_SELECTITEM, sent|wparam, 9 },
104 { TVM_SELECTITEM, sent|wparam, 9 },
105 { TVM_SELECTITEM, sent|wparam, 9 },
106 { TVM_SELECTITEM, sent|wparam, 9 },
107 { 0 }
110 static const struct message getitemtext_seq[] = {
111 { TVM_INSERTITEMA, sent },
112 { TVM_GETITEMA, sent },
113 { TVM_DELETEITEM, sent },
114 { 0 }
117 static const struct message focus_seq[] = {
118 { TVM_INSERTITEMA, sent },
119 { TVM_INSERTITEMA, sent },
120 { TVM_SELECTITEM, sent|wparam, 9 },
121 /* The following end up out of order in wine */
122 { WM_WINDOWPOSCHANGING, sent|defwinproc },
123 { WM_NCCALCSIZE, sent|wparam|defwinproc, TRUE },
124 { WM_WINDOWPOSCHANGED, sent|defwinproc },
125 { WM_SIZE, sent|defwinproc },
126 { WM_WINDOWPOSCHANGING, sent|defwinproc|optional },
127 { WM_NCCALCSIZE, sent|wparam|defwinproc|optional, TRUE },
128 { WM_WINDOWPOSCHANGED, sent|defwinproc|optional },
129 { WM_SIZE, sent|defwinproc|optional },
130 { WM_PAINT, sent|defwinproc },
131 { WM_NCPAINT, sent|wparam|defwinproc, 1 },
132 { WM_ERASEBKGND, sent|defwinproc },
133 { TVM_EDITLABELA, sent },
134 { WM_COMMAND, sent|wparam|defwinproc, MAKEWPARAM(0, EN_UPDATE) },
135 { WM_COMMAND, sent|wparam|defwinproc, MAKEWPARAM(0, EN_CHANGE) },
136 { WM_PARENTNOTIFY, sent|wparam|defwinproc, MAKEWPARAM(WM_CREATE, 0) },
137 { WM_KILLFOCUS, sent|defwinproc },
138 { WM_PAINT, sent|defwinproc },
139 { WM_IME_SETCONTEXT, sent|defwinproc|optional },
140 { WM_COMMAND, sent|wparam|defwinproc, MAKEWPARAM(0, EN_SETFOCUS) },
141 { WM_ERASEBKGND, sent|defwinproc|optional },
142 { WM_CTLCOLOREDIT, sent|defwinproc|optional },
143 { WM_CTLCOLOREDIT, sent|defwinproc|optional },
144 { 0 }
147 static const struct message test_get_set_bkcolor_seq[] = {
148 { TVM_GETBKCOLOR, sent|wparam|lparam, 0, 0 },
149 { TVM_SETBKCOLOR, sent|wparam|lparam, 0, 0 },
150 { TVM_GETBKCOLOR, sent|wparam|lparam, 0, 0 },
151 { TVM_SETBKCOLOR, sent|wparam|lparam, 0, 0x00ffffff },
152 { TVM_GETBKCOLOR, sent|wparam|lparam, 0, 0 },
153 { TVM_SETBKCOLOR, sent|wparam|lparam, 0, -1 },
154 { 0 }
157 static const struct message test_get_set_imagelist_seq[] = {
158 { TVM_SETIMAGELIST, sent|wparam|lparam, 0, 0 },
159 { TVM_GETIMAGELIST, sent|wparam|lparam, 0, 0 },
160 { 0 }
163 static const struct message test_get_set_indent_seq[] = {
164 { TVM_SETINDENT, sent|wparam|lparam, 0, 0 },
165 { TVM_GETINDENT, sent|wparam|lparam, 0, 0 },
166 /* The actual amount to indent is dependent on the system for this message */
167 { TVM_SETINDENT, sent },
168 { TVM_GETINDENT, sent|wparam|lparam, 0, 0 },
169 { 0 }
172 static const struct message test_get_set_insertmarkcolor_seq[] = {
173 { TVM_SETINSERTMARKCOLOR, sent|wparam|lparam, 0, 0 },
174 { TVM_GETINSERTMARKCOLOR, sent|wparam|lparam, 0, 0 },
175 { 0 }
178 static const struct message test_get_set_item_seq[] = {
179 { TVM_GETITEMA, sent },
180 { TVM_SETITEMA, sent },
181 { TVM_GETITEMA, sent },
182 { TVM_SETITEMA, sent },
183 { 0 }
186 static const struct message test_get_set_itemheight_seq[] = {
187 { TVM_GETITEMHEIGHT, sent|wparam|lparam, 0, 0 },
188 { TVM_SETITEMHEIGHT, sent|wparam|lparam, -1, 0 },
189 { TVM_GETITEMHEIGHT, sent|wparam|lparam, 0, 0 },
190 { TVM_SETITEMHEIGHT, sent|lparam, 0xcccccccc, 0 },
191 { TVM_GETITEMHEIGHT, sent|wparam|lparam|optional, 0, 0 },
192 { TVM_SETITEMHEIGHT, sent|wparam|lparam|optional, 9, 0 },
193 { TVM_GETITEMHEIGHT, sent|wparam|lparam, 0, 0 },
194 { 0 }
197 static const struct message test_get_set_scrolltime_seq[] = {
198 { TVM_SETSCROLLTIME, sent|wparam|lparam, 20, 0 },
199 { TVM_GETSCROLLTIME, sent|wparam|lparam, 0, 0 },
200 { 0 }
203 static const struct message test_get_set_textcolor_seq[] = {
204 { TVM_GETTEXTCOLOR, sent|wparam|lparam, 0, 0 },
205 { TVM_SETTEXTCOLOR, sent|wparam|lparam, 0, 0 },
206 { TVM_GETTEXTCOLOR, sent|wparam|lparam, 0, 0 },
207 { TVM_SETTEXTCOLOR, sent|wparam|lparam, 0, RGB(255, 255, 255) },
208 { TVM_GETTEXTCOLOR, sent|wparam|lparam, 0, 0 },
209 { TVM_SETTEXTCOLOR, sent|wparam|lparam, 0, CLR_NONE },
210 { 0 }
213 static const struct message test_get_set_tooltips_seq[] = {
214 { WM_KILLFOCUS, sent },
215 { WM_IME_SETCONTEXT, sent|optional },
216 { WM_IME_NOTIFY, sent|optional },
217 { TVM_SETTOOLTIPS, sent|wparam|lparam, 0, 0 },
218 { TVM_GETTOOLTIPS, sent|wparam|lparam, 0, 0 },
219 { 0 }
222 static const struct message test_get_set_unicodeformat_seq[] = {
223 { TVM_SETUNICODEFORMAT, sent|wparam|lparam, TRUE, 0 },
224 { TVM_GETUNICODEFORMAT, sent|wparam|lparam, 0, 0 },
225 { TVM_SETUNICODEFORMAT, sent|wparam|lparam, 0, 0 },
226 { TVM_GETUNICODEFORMAT, sent|wparam|lparam, 0, 0 },
227 { TVM_SETUNICODEFORMAT, sent|wparam|lparam, 0, 0 },
228 { 0 }
231 static const struct message test_right_click_seq[] = {
232 { WM_RBUTTONDOWN, sent|wparam, MK_RBUTTON },
233 { WM_CAPTURECHANGED, sent|defwinproc },
234 { TVM_GETNEXTITEM, sent|wparam|lparam|defwinproc, TVGN_CARET, 0 },
235 { WM_PAINT, sent|optional },
236 { WM_ERASEBKGND, sent|defwinproc|optional },
237 { WM_NCHITTEST, sent|optional },
238 { WM_SETCURSOR, sent|optional },
239 { WM_MOUSEMOVE, sent|optional },
240 { WM_PAINT, sent|optional },
241 { WM_ERASEBKGND, sent|defwinproc|optional },
242 { 0 }
245 static const struct message parent_expand_seq[] = {
246 { WM_NOTIFY, sent|id, 0, 0, TVN_ITEMEXPANDINGA },
247 { WM_NOTIFY, sent|id, 0, 0, TVN_ITEMEXPANDEDA },
248 { 0 }
251 static const struct message parent_expand_kb_seq[] = {
252 { WM_NOTIFY, sent|id, 0, 0, TVN_KEYDOWN },
253 { WM_NOTIFY, sent|id, 0, 0, TVN_ITEMEXPANDINGA },
254 { WM_NOTIFY, sent|id, 0, 0, TVN_ITEMEXPANDEDA },
255 { WM_CHANGEUISTATE, sent|optional },
256 { 0 }
259 static const struct message parent_collapse_2nd_kb_seq[] = {
260 { WM_NOTIFY, sent|id|optional, 0, 0, TVN_KEYDOWN },
261 { WM_NOTIFY, sent|id, 0, 0, TVN_ITEMEXPANDINGA },
262 { WM_CHANGEUISTATE, sent|optional },
263 { 0 }
266 static const struct message parent_expand_empty_kb_seq[] = {
267 { WM_NOTIFY, sent|id, 0, 0, TVN_KEYDOWN },
268 { WM_CHANGEUISTATE, sent|optional },
269 { 0 }
272 static const struct message parent_singleexpand_seq0[] = {
273 /* alpha expands */
274 { WM_NOTIFY, sent|id, 0, 0, TVN_SELCHANGINGA },
275 { WM_NOTIFY, sent|id, 0, 0, TVN_SELCHANGEDA },
276 { WM_NOTIFY, sent|id, 0, 0, TVN_SINGLEEXPAND },
277 { WM_NOTIFY, sent|id, 0, 0, TVN_ITEMEXPANDINGA },
278 { WM_NOTIFY, sent|id, 0, 0, TVN_ITEMEXPANDEDA },
279 { 0 }
282 static const struct message parent_singleexpand_seq1[] = {
283 /* bravo expands */
284 { WM_NOTIFY, sent|id, 0, 0, TVN_SELCHANGINGA },
285 { WM_NOTIFY, sent|id, 0, 0, TVN_SELCHANGEDA },
286 { WM_NOTIFY, sent|id, 0, 0, TVN_SINGLEEXPAND },
287 { WM_NOTIFY, sent|id, 0, 0, TVN_ITEMEXPANDINGA },
288 { WM_NOTIFY, sent|id, 0, 0, TVN_ITEMEXPANDEDA },
289 { 0 }
292 static const struct message parent_singleexpand_seq2[] = {
293 /* delta expands, bravo collapses */
294 { WM_NOTIFY, sent|id, 0, 0, TVN_SELCHANGINGA },
295 { WM_NOTIFY, sent|id, 0, 0, TVN_SELCHANGEDA },
296 { WM_NOTIFY, sent|id, 0, 0, TVN_SINGLEEXPAND },
297 { WM_NOTIFY, sent|id, 0, 0, TVN_ITEMEXPANDINGA },
298 { WM_NOTIFY, sent|id, 0, 0, TVN_ITEMEXPANDEDA },
299 { WM_NOTIFY, sent|id, 0, 0, TVN_ITEMEXPANDINGA },
300 { WM_NOTIFY, sent|id, 0, 0, TVN_ITEMEXPANDEDA },
301 { 0 }
304 static const struct message parent_singleexpand_seq3[] = {
305 /* foxtrot expands, alpha and delta collapse */
306 { WM_NOTIFY, sent|id, 0, 0, TVN_SELCHANGINGA },
307 { WM_NOTIFY, sent|id, 0, 0, TVN_SELCHANGEDA },
308 { WM_NOTIFY, sent|id, 0, 0, TVN_SINGLEEXPAND },
309 { WM_NOTIFY, sent|id, 0, 0, TVN_ITEMEXPANDINGA },
310 { WM_NOTIFY, sent|id, 0, 0, TVN_ITEMEXPANDEDA },
311 { WM_NOTIFY, sent|id, 0, 0, TVN_ITEMEXPANDINGA },
312 { WM_NOTIFY, sent|id, 0, 0, TVN_ITEMEXPANDEDA },
313 { WM_NOTIFY, sent|id, 0, 0, TVN_ITEMEXPANDINGA },
314 { WM_NOTIFY, sent|id, 0, 0, TVN_ITEMEXPANDEDA },
315 { 0 }
318 static const struct message parent_singleexpand_seq4[] = {
319 /* alpha expands, foxtrot collapses */
320 { WM_NOTIFY, sent|id, 0, 0, TVN_SELCHANGINGA },
321 { WM_NOTIFY, sent|id, 0, 0, TVN_SELCHANGEDA },
322 { WM_NOTIFY, sent|id, 0, 0, TVN_SINGLEEXPAND },
323 { WM_NOTIFY, sent|id, 0, 0, TVN_ITEMEXPANDINGA },
324 { WM_NOTIFY, sent|id, 0, 0, TVN_ITEMEXPANDEDA },
325 { WM_NOTIFY, sent|id, 0, 0, TVN_ITEMEXPANDINGA },
326 { WM_NOTIFY, sent|id, 0, 0, TVN_ITEMEXPANDEDA },
327 { 0 }
330 static const struct message parent_singleexpand_seq5[] = {
331 /* foxtrot expands while golf is selected, then golf expands and alpha collapses */
332 { WM_NOTIFY, sent|id, 0, 0, TVN_SELCHANGINGA },
333 { WM_NOTIFY, sent|id, 0, 0, TVN_ITEMEXPANDINGA },
334 { WM_NOTIFY, sent|id, 0, 0, TVN_ITEMEXPANDEDA },
335 { WM_NOTIFY, sent|id, 0, 0, TVN_SELCHANGEDA },
336 { WM_NOTIFY, sent|id, 0, 0, TVN_SINGLEEXPAND },
337 { WM_NOTIFY, sent|id, 0, 0, TVN_ITEMEXPANDINGA },
338 { WM_NOTIFY, sent|id, 0, 0, TVN_ITEMEXPANDEDA },
339 { WM_NOTIFY, sent|id, 0, 0, TVN_ITEMEXPANDINGA },
340 { WM_NOTIFY, sent|id, 0, 0, TVN_ITEMEXPANDEDA },
341 { 0 }
344 static const struct message parent_singleexpand_seq6[] = {
345 /* hotel does not expand and india does not collapse because they have no children */
346 { WM_NOTIFY, sent|id, 0, 0, TVN_SELCHANGINGA },
347 { WM_NOTIFY, sent|id, 0, 0, TVN_SELCHANGEDA },
348 { WM_NOTIFY, sent|id, 0, 0, TVN_SINGLEEXPAND },
349 { 0 }
352 static const struct message parent_singleexpand_seq7[] = {
353 /* india does not expand and hotel does not collapse because they have no children */
354 { WM_NOTIFY, sent|id, 0, 0, TVN_SELCHANGINGA },
355 { WM_NOTIFY, sent|id, 0, 0, TVN_SELCHANGEDA },
356 { WM_NOTIFY, sent|id, 0, 0, TVN_SINGLEEXPAND },
357 { 0 }
360 static const struct message parent_get_dispinfo_seq[] = {
361 { WM_NOTIFY, sent|id, 0, 0, TVN_GETDISPINFOA },
362 { 0 }
365 static const struct message empty_seq[] = {
366 { 0 }
369 static const struct message parent_cd_seq[] = {
370 { WM_NOTIFY, sent|id|custdraw, 0, 0, NM_CUSTOMDRAW, CDDS_PREPAINT },
371 { WM_NOTIFY, sent|id|custdraw, 0, 0, NM_CUSTOMDRAW, CDDS_ITEMPREPAINT },
372 { WM_NOTIFY, sent|id|custdraw, 0, 0, NM_CUSTOMDRAW, CDDS_ITEMPOSTPAINT },
373 { WM_NOTIFY, sent|id|custdraw, 0, 0, NM_CUSTOMDRAW, CDDS_ITEMPREPAINT },
374 { WM_NOTIFY, sent|id|custdraw, 0, 0, NM_CUSTOMDRAW, CDDS_ITEMPOSTPAINT },
375 { WM_NOTIFY, sent|id|custdraw, 0, 0, NM_CUSTOMDRAW, CDDS_POSTPAINT },
376 { 0 }
379 static const struct message parent_vk_return_seq[] = {
380 { WM_NOTIFY, sent|id, 0, 0, TVN_KEYDOWN },
381 { WM_NOTIFY, sent|id, 0, 0, NM_RETURN },
382 { WM_CHANGEUISTATE, sent|optional },
383 { 0 }
386 static const struct message parent_right_click_seq[] = {
387 { WM_NOTIFY, sent|id, 0, 0, NM_RCLICK },
388 { WM_CONTEXTMENU, sent },
389 { WM_NOTIFY, sent|optional },
390 { WM_SETCURSOR, sent|optional },
391 { WM_CTLCOLOREDIT, sent|optional },
392 { WM_NOTIFY, sent|optional },
393 { WM_NOTIFY, sent|optional },
394 { WM_NOTIFY, sent|optional },
395 { WM_NOTIFY, sent|optional },
396 { WM_NOTIFY, sent|optional },
397 { WM_NOTIFY, sent|optional },
398 { WM_NOTIFY, sent|optional },
399 { WM_NOTIFY, sent|optional },
400 { WM_SETCURSOR, sent|optional },
401 { 0 }
404 static HWND hMainWnd;
406 static HTREEITEM hRoot, hChild;
408 static int pos = 0;
409 static char sequence[256];
411 static void Clear(void)
413 pos = 0;
414 sequence[0] = '\0';
417 static void AddItem(char ch)
419 sequence[pos++] = ch;
420 sequence[pos] = '\0';
423 static void IdentifyItem(HTREEITEM hItem)
425 if (hItem == hRoot) {
426 AddItem('R');
427 return;
429 if (hItem == hChild) {
430 AddItem('C');
431 return;
433 if (hItem == NULL) {
434 AddItem('n');
435 return;
437 AddItem('?');
440 /* This function hooks in and records all messages to the treeview control */
441 static LRESULT WINAPI TreeviewWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
443 static LONG defwndproc_counter = 0;
444 LRESULT ret;
445 WNDPROC lpOldProc = (WNDPROC)GetWindowLongPtrA(hwnd, GWLP_USERDATA);
446 struct message msg = { 0 };
448 msg.message = message;
449 msg.flags = sent|wparam|lparam;
450 if (defwndproc_counter) msg.flags |= defwinproc;
451 msg.wParam = wParam;
452 msg.lParam = lParam;
453 add_message(sequences, TREEVIEW_SEQ_INDEX, &msg);
455 defwndproc_counter++;
456 ret = CallWindowProcA(lpOldProc, hwnd, message, wParam, lParam);
457 defwndproc_counter--;
459 return ret;
462 static HWND create_treeview_control(DWORD style)
464 WNDPROC pOldWndProc;
465 HWND hTree;
467 hTree = CreateWindowExA(WS_EX_CLIENTEDGE, WC_TREEVIEWA, NULL, WS_CHILD|WS_VISIBLE|
468 TVS_LINESATROOT|TVS_HASLINES|TVS_HASBUTTONS|TVS_EDITLABELS|style,
469 0, 0, 120, 100, hMainWnd, (HMENU)100, GetModuleHandleA(0), 0);
471 SetFocus(hTree);
473 /* Record the old WNDPROC so we can call it after recording the messages */
474 pOldWndProc = (WNDPROC)SetWindowLongPtrA(hTree, GWLP_WNDPROC, (LONG_PTR)TreeviewWndProc);
475 SetWindowLongPtrA(hTree, GWLP_USERDATA, (LONG_PTR)pOldWndProc);
477 return hTree;
480 static void fill_tree(HWND hTree)
482 TVINSERTSTRUCTA ins;
483 static CHAR root[] = "Root",
484 child[] = "Child";
486 ins.hParent = TVI_ROOT;
487 ins.hInsertAfter = TVI_ROOT;
488 U(ins).item.mask = TVIF_TEXT;
489 U(ins).item.pszText = root;
490 hRoot = TreeView_InsertItemA(hTree, &ins);
492 ins.hParent = hRoot;
493 ins.hInsertAfter = TVI_FIRST;
494 U(ins).item.mask = TVIF_TEXT;
495 U(ins).item.pszText = child;
496 hChild = TreeView_InsertItemA(hTree, &ins);
499 static void test_fillroot(void)
501 TVITEMA tvi;
502 HWND hTree;
504 hTree = create_treeview_control(0);
506 flush_sequences(sequences, NUM_MSG_SEQUENCES);
508 fill_tree(hTree);
510 Clear();
511 AddItem('A');
512 ok(hRoot != NULL, "failed to set root\n");
513 AddItem('B');
514 ok(hChild != NULL, "failed to set child\n");
515 AddItem('.');
516 ok_sequence(sequences, TREEVIEW_SEQ_INDEX, FillRootSeq, "FillRoot", FALSE);
517 ok(!strcmp(sequence, "AB."), "Item creation\n");
519 /* UMLPad 1.15 depends on this being not -1 (I_IMAGECALLBACK) */
520 tvi.hItem = hRoot;
521 tvi.mask = TVIF_IMAGE | TVIF_SELECTEDIMAGE;
522 SendMessageA(hTree, TVM_GETITEMA, 0, (LPARAM)&tvi);
523 ok(tvi.iImage == 0, "tvi.iImage=%d\n", tvi.iImage);
524 ok(tvi.iSelectedImage == 0, "tvi.iSelectedImage=%d\n", tvi.iSelectedImage);
526 DestroyWindow(hTree);
529 static void test_callback(void)
531 HTREEITEM hRoot;
532 HTREEITEM hItem1, hItem2;
533 TVINSERTSTRUCTA ins;
534 TVITEMA tvi;
535 CHAR test_string[] = "Test_string";
536 static const CHAR test2A[] = "TEST2";
537 CHAR buf[128];
538 HWND hTree;
539 DWORD ret;
541 hTree = create_treeview_control(0);
543 ret = SendMessageA(hTree, TVM_DELETEITEM, 0, (LPARAM)TVI_ROOT);
544 expect(TRUE, ret);
545 ins.hParent = TVI_ROOT;
546 ins.hInsertAfter = TVI_ROOT;
547 U(ins).item.mask = TVIF_TEXT;
548 U(ins).item.pszText = LPSTR_TEXTCALLBACKA;
549 hRoot = TreeView_InsertItemA(hTree, &ins);
550 ok(hRoot != NULL, "failed to set root\n");
552 tvi.hItem = hRoot;
553 tvi.mask = TVIF_TEXT;
554 tvi.pszText = buf;
555 tvi.cchTextMax = ARRAY_SIZE(buf);
556 ret = TreeView_GetItemA(hTree, &tvi);
557 expect(TRUE, ret);
558 ok(strcmp(tvi.pszText, TEST_CALLBACK_TEXT) == 0, "Callback item text mismatch %s vs %s\n",
559 tvi.pszText, TEST_CALLBACK_TEXT);
561 ins.hParent = hRoot;
562 ins.hInsertAfter = TVI_FIRST;
563 U(ins).item.mask = TVIF_TEXT;
564 U(ins).item.pszText = test_string;
565 hItem1 = TreeView_InsertItemA(hTree, &ins);
566 ok(hItem1 != NULL, "failed to set Item1\n");
568 tvi.hItem = hItem1;
569 ret = TreeView_GetItemA(hTree, &tvi);
570 expect(TRUE, ret);
571 ok(strcmp(tvi.pszText, test_string) == 0, "Item text mismatch %s vs %s\n",
572 tvi.pszText, test_string);
574 /* undocumented: pszText of NULL also means LPSTR_CALLBACK: */
575 tvi.pszText = NULL;
576 ret = TreeView_SetItemA(hTree, &tvi);
577 expect(TRUE, ret);
578 tvi.pszText = buf;
579 ret = TreeView_GetItemA(hTree, &tvi);
580 expect(TRUE, ret);
581 ok(strcmp(tvi.pszText, TEST_CALLBACK_TEXT) == 0, "Item text mismatch %s vs %s\n",
582 tvi.pszText, TEST_CALLBACK_TEXT);
584 U(ins).item.pszText = NULL;
585 hItem2 = TreeView_InsertItemA(hTree, &ins);
586 ok(hItem2 != NULL, "failed to set Item2\n");
587 tvi.hItem = hItem2;
588 memset(buf, 0, sizeof(buf));
589 ret = TreeView_GetItemA(hTree, &tvi);
590 expect(TRUE, ret);
591 ok(strcmp(tvi.pszText, TEST_CALLBACK_TEXT) == 0, "Item text mismatch %s vs %s\n",
592 tvi.pszText, TEST_CALLBACK_TEXT);
594 /* notification handler changed A->W */
595 g_disp_A_to_W = TRUE;
596 tvi.hItem = hItem2;
597 memset(buf, 0, sizeof(buf));
598 ret = TreeView_GetItemA(hTree, &tvi);
599 expect(TRUE, ret);
600 ok(strcmp(tvi.pszText, test2A) == 0, "got %s, expected %s\n",
601 tvi.pszText, test2A);
602 g_disp_A_to_W = FALSE;
604 /* handler changes state image index */
605 SetWindowLongA(hTree, GWL_STYLE, GetWindowLongA(hTree, GWL_STYLE) | TVS_CHECKBOXES);
607 /* clear selection, handler will set selected state */
608 ret = SendMessageA(hTree, TVM_SELECTITEM, TVGN_CARET, 0);
609 expect(TRUE, ret);
611 flush_sequences(sequences, NUM_MSG_SEQUENCES);
613 tvi.hItem = hRoot;
614 tvi.mask = TVIF_STATE;
615 tvi.state = TVIS_SELECTED;
616 ret = TreeView_GetItemA(hTree, &tvi);
617 expect(TRUE, ret);
618 ok(tvi.state == INDEXTOSTATEIMAGEMASK(1), "got 0x%x\n", tvi.state);
620 ok_sequence(sequences, PARENT_SEQ_INDEX, empty_seq,
621 "no TVN_GETDISPINFO for a state seq", FALSE);
623 tvi.hItem = hRoot;
624 tvi.mask = TVIF_IMAGE | TVIF_STATE;
625 tvi.state = TVIS_FOCUSED;
626 tvi.stateMask = TVIS_FOCUSED;
627 tvi.iImage = I_IMAGECALLBACK;
628 ret = TreeView_SetItemA(hTree, &tvi);
629 expect(TRUE, ret);
631 /* ask for item image index through callback - state is also set with state image index */
632 flush_sequences(sequences, NUM_MSG_SEQUENCES);
634 tvi.hItem = hRoot;
635 tvi.mask = TVIF_IMAGE;
636 tvi.state = 0;
637 ret = TreeView_GetItemA(hTree, &tvi);
638 expect(TRUE, ret);
639 ok(tvi.state == (INDEXTOSTATEIMAGEMASK(1) | TVIS_FOCUSED), "got 0x%x\n", tvi.state);
641 ok_sequence(sequences, PARENT_SEQ_INDEX, parent_get_dispinfo_seq,
642 "callback for state/overlay image index, noop seq", FALSE);
644 /* ask for image again and overwrite state to some value in handler */
645 flush_sequences(sequences, NUM_MSG_SEQUENCES);
647 g_disp_set_stateimage = TRUE;
648 tvi.hItem = hRoot;
649 tvi.mask = TVIF_IMAGE;
650 tvi.state = INDEXTOSTATEIMAGEMASK(1);
651 tvi.stateMask = 0;
652 ret = TreeView_GetItemA(hTree, &tvi);
653 expect(TRUE, ret);
654 /* handler sets TVIS_SELECTED as well */
655 ok(tvi.state == (TVIS_FOCUSED | TVIS_SELECTED | INDEXTOSTATEIMAGEMASK(2) | INDEXTOOVERLAYMASK(3)), "got 0x%x\n", tvi.state);
656 g_disp_set_stateimage = FALSE;
658 ok_sequence(sequences, PARENT_SEQ_INDEX, parent_get_dispinfo_seq,
659 "callback for state/overlay image index seq", FALSE);
661 DestroyWindow(hTree);
664 static void test_select(void)
666 BOOL r;
667 HWND hTree;
669 hTree = create_treeview_control(0);
670 fill_tree(hTree);
672 /* root-none select tests */
673 flush_sequences(sequences, NUM_MSG_SEQUENCES);
674 r = SendMessageA(hTree, TVM_SELECTITEM, TVGN_CARET, 0);
675 expect(TRUE, r);
676 Clear();
677 AddItem('1');
678 r = SendMessageA(hTree, TVM_SELECTITEM, TVGN_CARET, (LPARAM)hRoot);
679 expect(TRUE, r);
680 AddItem('2');
681 r = SendMessageA(hTree, TVM_SELECTITEM, TVGN_CARET, (LPARAM)hRoot);
682 expect(TRUE, r);
683 AddItem('3');
684 r = SendMessageA(hTree, TVM_SELECTITEM, TVGN_CARET, 0);
685 expect(TRUE, r);
686 AddItem('4');
687 r = SendMessageA(hTree, TVM_SELECTITEM, TVGN_CARET, 0);
688 expect(TRUE, r);
689 AddItem('5');
690 r = SendMessageA(hTree, TVM_SELECTITEM, TVGN_CARET, (LPARAM)hRoot);
691 expect(TRUE, r);
692 AddItem('.');
693 ok(!strcmp(sequence, "1(nR)nR23(Rn)Rn45(nR)nR."), "root-none select test\n");
694 ok_sequence(sequences, TREEVIEW_SEQ_INDEX, rootnone_select_seq,
695 "root-none select seq", FALSE);
697 /* root-child select tests */
698 flush_sequences(sequences, NUM_MSG_SEQUENCES);
699 r = SendMessageA(hTree, TVM_SELECTITEM, TVGN_CARET, 0);
700 expect(TRUE, r);
702 Clear();
703 AddItem('1');
704 r = SendMessageA(hTree, TVM_SELECTITEM, TVGN_CARET, (LPARAM)hRoot);
705 expect(TRUE, r);
706 AddItem('2');
707 r = SendMessageA(hTree, TVM_SELECTITEM, TVGN_CARET, (LPARAM)hRoot);
708 expect(TRUE, r);
709 AddItem('3');
710 r = SendMessageA(hTree, TVM_SELECTITEM, TVGN_CARET, (LPARAM)hChild);
711 expect(TRUE, r);
712 AddItem('4');
713 r = SendMessageA(hTree, TVM_SELECTITEM, TVGN_CARET, (LPARAM)hChild);
714 expect(TRUE, r);
715 AddItem('5');
716 r = SendMessageA(hTree, TVM_SELECTITEM, TVGN_CARET, (LPARAM)hRoot);
717 expect(TRUE, r);
718 AddItem('.');
719 ok(!strcmp(sequence, "1(nR)nR23(RC)RC45(CR)CR."), "root-child select test\n");
720 ok_sequence(sequences, TREEVIEW_SEQ_INDEX, rootchild_select_seq,
721 "root-child select seq", FALSE);
723 DestroyWindow(hTree);
726 static void test_getitemtext(void)
728 TVINSERTSTRUCTA ins;
729 HTREEITEM hChild;
730 TVITEMA tvi;
731 HWND hTree;
733 CHAR szBuffer[80] = "Blah";
734 int nBufferSize = ARRAY_SIZE(szBuffer);
736 hTree = create_treeview_control(0);
737 fill_tree(hTree);
739 flush_sequences(sequences, NUM_MSG_SEQUENCES);
741 /* add an item without TVIF_TEXT mask and pszText == NULL */
742 ins.hParent = hRoot;
743 ins.hInsertAfter = TVI_ROOT;
744 U(ins).item.mask = 0;
745 U(ins).item.pszText = NULL;
746 U(ins).item.cchTextMax = 0;
747 hChild = TreeView_InsertItemA(hTree, &ins);
748 ok(hChild != NULL, "failed to set hChild\n");
750 /* retrieve it with TVIF_TEXT mask */
751 tvi.hItem = hChild;
752 tvi.mask = TVIF_TEXT;
753 tvi.cchTextMax = nBufferSize;
754 tvi.pszText = szBuffer;
756 SendMessageA( hTree, TVM_GETITEMA, 0, (LPARAM)&tvi );
757 ok(!strcmp(szBuffer, ""), "szBuffer=\"%s\", expected \"\"\n", szBuffer);
758 ok(SendMessageA(hTree, TVM_DELETEITEM, 0, (LPARAM)hChild), "DeleteItem failed\n");
759 ok_sequence(sequences, TREEVIEW_SEQ_INDEX, getitemtext_seq, "get item text seq", FALSE);
761 DestroyWindow(hTree);
764 static void test_focus(void)
766 TVINSERTSTRUCTA ins;
767 static CHAR child1[] = "Edit",
768 child2[] = "A really long string";
769 HTREEITEM hChild1, hChild2;
770 HWND hTree;
771 HWND hEdit;
773 hTree = create_treeview_control(0);
774 fill_tree(hTree);
776 flush_sequences(sequences, NUM_MSG_SEQUENCES);
778 /* This test verifies that when a label is being edited, scrolling
779 * the treeview does not cause the label to lose focus. To test
780 * this, first some additional entries are added to generate
781 * scrollbars.
783 ins.hParent = hRoot;
784 ins.hInsertAfter = hChild;
785 U(ins).item.mask = TVIF_TEXT;
786 U(ins).item.pszText = child1;
787 hChild1 = TreeView_InsertItemA(hTree, &ins);
788 ok(hChild1 != NULL, "failed to set hChild1\n");
789 ins.hInsertAfter = hChild1;
790 U(ins).item.mask = TVIF_TEXT;
791 U(ins).item.pszText = child2;
792 hChild2 = TreeView_InsertItemA(hTree, &ins);
793 ok(hChild2 != NULL, "failed to set hChild2\n");
795 ShowWindow(hMainWnd,SW_SHOW);
796 SendMessageA(hTree, TVM_SELECTITEM, TVGN_CARET, (LPARAM)hChild);
797 hEdit = (HWND)SendMessageA(hTree, TVM_EDITLABELA, 0, (LPARAM)hChild);
798 ScrollWindowEx(hTree, -10, 0, NULL, NULL, NULL, NULL, SW_SCROLLCHILDREN);
799 ok(GetFocus() == hEdit, "Edit control should have focus\n");
800 ok_sequence(sequences, TREEVIEW_SEQ_INDEX, focus_seq, "focus test", TRUE);
802 DestroyWindow(hTree);
805 static void test_get_set_bkcolor(void)
807 COLORREF crColor;
808 HWND hTree;
810 hTree = create_treeview_control(0);
811 fill_tree(hTree);
813 flush_sequences(sequences, NUM_MSG_SEQUENCES);
815 /* If the value is -1, the control is using the system color for the background color. */
816 crColor = SendMessageA(hTree, TVM_GETBKCOLOR, 0, 0);
817 ok(crColor == ~0u, "Default background color reported as 0x%.8lx\n", crColor);
819 /* Test for black background */
820 SendMessageA(hTree, TVM_SETBKCOLOR, 0, RGB(0,0,0));
821 crColor = SendMessageA(hTree, TVM_GETBKCOLOR, 0, 0);
822 ok(crColor == RGB(0,0,0), "Black background color reported as 0x%.8lx\n", crColor);
824 /* Test for white background */
825 SendMessageA(hTree, TVM_SETBKCOLOR, 0, RGB(255,255,255));
826 crColor = SendMessageA(hTree, TVM_GETBKCOLOR, 0, 0);
827 ok(crColor == RGB(255,255,255), "White background color reported as 0x%.8lx\n", crColor);
829 /* Reset the default background */
830 SendMessageA(hTree, TVM_SETBKCOLOR, 0, -1);
832 ok_sequence(sequences, TREEVIEW_SEQ_INDEX, test_get_set_bkcolor_seq,
833 "test get set bkcolor", FALSE);
835 DestroyWindow(hTree);
838 static void test_get_set_imagelist(void)
840 HIMAGELIST himl;
841 HWND hTree;
843 hTree = create_treeview_control(0);
844 fill_tree(hTree);
846 flush_sequences(sequences, NUM_MSG_SEQUENCES);
848 /* Test a NULL HIMAGELIST */
849 SendMessageA(hTree, TVM_SETIMAGELIST, TVSIL_NORMAL, 0);
850 himl = (HIMAGELIST)SendMessageA(hTree, TVM_GETIMAGELIST, TVSIL_NORMAL, 0);
851 ok(himl == NULL, "NULL image list, reported as %p, expected 0.\n", himl);
853 /* TODO: Test an actual image list */
855 ok_sequence(sequences, TREEVIEW_SEQ_INDEX, test_get_set_imagelist_seq,
856 "test get imagelist", FALSE);
858 DestroyWindow(hTree);
861 static void test_get_set_indent(void)
863 int ulIndent;
864 int ulMinIndent;
865 int ulMoreThanTwiceMin;
866 HWND hTree;
868 hTree = create_treeview_control(0);
869 fill_tree(hTree);
871 flush_sequences(sequences, NUM_MSG_SEQUENCES);
873 /* Finding the minimum indent */
874 SendMessageA(hTree, TVM_SETINDENT, 0, 0);
875 ulMinIndent = SendMessageA(hTree, TVM_GETINDENT, 0, 0);
877 /* Checking an indent that is more than twice the default indent */
878 ulMoreThanTwiceMin = 2*ulMinIndent+1;
879 SendMessageA(hTree, TVM_SETINDENT, ulMoreThanTwiceMin, 0);
880 ulIndent = SendMessageA(hTree, TVM_GETINDENT, 0, 0);
881 ok(ulIndent == ulMoreThanTwiceMin, "Indent reported as %d, expected %d\n", ulIndent, ulMoreThanTwiceMin);
883 ok_sequence(sequences, TREEVIEW_SEQ_INDEX, test_get_set_indent_seq,
884 "test get set indent", FALSE);
886 DestroyWindow(hTree);
889 static void test_get_set_insertmark(void)
891 COLORREF crColor = RGB(0,0,0);
892 HWND hTree;
894 hTree = create_treeview_control(0);
895 fill_tree(hTree);
897 flush_sequences(sequences, NUM_MSG_SEQUENCES);
899 SendMessageA(hTree, TVM_SETINSERTMARKCOLOR, 0, crColor);
900 crColor = SendMessageA(hTree, TVM_GETINSERTMARKCOLOR, 0, 0);
901 ok(crColor == RGB(0,0,0), "Insert mark color reported as 0x%.8lx, expected 0x00000000\n", crColor);
903 ok_sequence(sequences, TREEVIEW_SEQ_INDEX, test_get_set_insertmarkcolor_seq,
904 "test get set insertmark color", FALSE);
906 DestroyWindow(hTree);
909 static void test_get_set_item(void)
911 TVITEMA tviRoot = {0};
912 int nBufferSize = 80;
913 char szBuffer[80] = {0};
914 HWND hTree, hTree2;
915 DWORD ret;
917 hTree = create_treeview_control(0);
918 fill_tree(hTree);
920 tviRoot.hItem = hRoot;
921 tviRoot.mask = TVIF_STATE;
922 tviRoot.state = TVIS_FOCUSED;
923 tviRoot.stateMask = TVIS_FOCUSED;
924 ret = SendMessageA(hTree, TVM_SETITEMA, 0, (LPARAM)&tviRoot);
925 expect(TRUE, ret);
927 flush_sequences(sequences, NUM_MSG_SEQUENCES);
929 /* Test the root item, state is set even when not requested */
930 tviRoot.hItem = hRoot;
931 tviRoot.mask = TVIF_TEXT;
932 tviRoot.state = 0;
933 tviRoot.stateMask = 0;
934 tviRoot.cchTextMax = nBufferSize;
935 tviRoot.pszText = szBuffer;
936 ret = SendMessageA(hTree, TVM_GETITEMA, 0, (LPARAM)&tviRoot);
937 expect(TRUE, ret);
938 ok(!strcmp("Root", szBuffer), "GetItem: szBuffer=\"%s\", expected \"Root\"\n", szBuffer);
939 ok(tviRoot.state == TVIS_FOCUSED, "got 0x%0x\n", tviRoot.state);
941 /* Change the root text */
942 lstrcpynA(szBuffer, "Testing123", nBufferSize);
943 ret = SendMessageA(hTree, TVM_SETITEMA, 0, (LPARAM)&tviRoot);
944 expect(TRUE, ret);
945 memset(szBuffer, 0, nBufferSize);
946 ret = SendMessageA(hTree, TVM_GETITEMA, 0, (LPARAM)&tviRoot);
947 expect(TRUE, ret);
948 ok(!strcmp("Testing123", szBuffer), "GetItem: szBuffer=\"%s\", expected \"Testing123\"\n", szBuffer);
950 /* Reset the root text */
951 memset(szBuffer, 0, nBufferSize);
952 lstrcpynA(szBuffer, "Root", nBufferSize);
953 ret = SendMessageA(hTree, TVM_SETITEMA, 0, (LPARAM)&tviRoot);
954 expect(TRUE, ret);
956 ok_sequence(sequences, TREEVIEW_SEQ_INDEX, test_get_set_item_seq,
957 "test get set item", FALSE);
959 /* get item from a different tree */
960 hTree2 = create_treeview_control(0);
962 tviRoot.hItem = hRoot;
963 tviRoot.mask = TVIF_STATE;
964 tviRoot.state = 0;
965 ret = SendMessageA(hTree2, TVM_GETITEMA, 0, (LPARAM)&tviRoot);
966 expect(TRUE, ret);
967 ok(tviRoot.state == TVIS_FOCUSED, "got state 0x%0x\n", tviRoot.state);
969 /* invalid item pointer, nt4 crashes here but later versions just return 0 */
970 tviRoot.hItem = (HTREEITEM)0xdeadbeef;
971 tviRoot.mask = TVIF_STATE;
972 tviRoot.state = 0;
973 ret = SendMessageA(hTree2, TVM_GETITEMA, 0, (LPARAM)&tviRoot);
974 expect(FALSE, ret);
976 DestroyWindow(hTree);
977 DestroyWindow(hTree2);
980 static void test_get_set_itemheight(void)
982 int ulOldHeight = 0;
983 int ulNewHeight = 0;
984 HWND hTree;
986 hTree = create_treeview_control(0);
987 fill_tree(hTree);
989 flush_sequences(sequences, NUM_MSG_SEQUENCES);
991 /* Assuming default height to begin with */
992 ulOldHeight = SendMessageA(hTree, TVM_GETITEMHEIGHT, 0, 0);
994 /* Explicitly setting and getting the default height */
995 SendMessageA(hTree, TVM_SETITEMHEIGHT, -1, 0);
996 ulNewHeight = SendMessageA(hTree, TVM_GETITEMHEIGHT, 0, 0);
997 ok(ulNewHeight == ulOldHeight, "Default height not set properly, reported %d, expected %d\n", ulNewHeight, ulOldHeight);
999 /* Explicitly setting and getting the height of twice the normal */
1000 SendMessageA(hTree, TVM_SETITEMHEIGHT, 2*ulOldHeight, 0);
1001 ulNewHeight = SendMessageA(hTree, TVM_GETITEMHEIGHT, 0, 0);
1002 ok(ulNewHeight == 2*ulOldHeight, "New height not set properly, reported %d, expected %d\n", ulNewHeight, 2*ulOldHeight);
1004 /* Assuming tree doesn't have TVS_NONEVENHEIGHT set, so a set of 9 will round down to 8 */
1005 SendMessageA(hTree, TVM_SETITEMHEIGHT, 9, 0);
1006 ulNewHeight = SendMessageA(hTree, TVM_GETITEMHEIGHT, 0, 0);
1007 ok(ulNewHeight == 8, "Uneven height not set properly, reported %d, expected %d\n", ulNewHeight, 8);
1009 ok_sequence(sequences, TREEVIEW_SEQ_INDEX, test_get_set_itemheight_seq,
1010 "test get set item height", FALSE);
1012 /* without TVS_NONEVENHEIGHT */
1013 SetWindowLongA(hTree, GWL_STYLE, GetWindowLongA(hTree, GWL_STYLE) & ~TVS_NONEVENHEIGHT);
1014 /* odd value */
1015 ulOldHeight = SendMessageA(hTree, TVM_SETITEMHEIGHT, 3, 0);
1016 ok(ulOldHeight == 8, "got %d, expected %d\n", ulOldHeight, 8);
1017 ulNewHeight = SendMessageA(hTree, TVM_GETITEMHEIGHT, 0, 0);
1018 ok(ulNewHeight == 2, "got %d, expected %d\n", ulNewHeight, 2);
1020 ulOldHeight = SendMessageA(hTree, TVM_SETITEMHEIGHT, 4, 0);
1021 ok(ulOldHeight == 2, "got %d, expected %d\n", ulOldHeight, 2);
1022 ulNewHeight = SendMessageA(hTree, TVM_GETITEMHEIGHT, 0, 0);
1023 ok(ulNewHeight == 4, "got %d, expected %d\n", ulNewHeight, 4);
1025 /* with TVS_NONEVENHEIGHT */
1026 SetWindowLongA(hTree, GWL_STYLE, GetWindowLongA(hTree, GWL_STYLE) | TVS_NONEVENHEIGHT);
1027 /* odd value */
1028 ulOldHeight = SendMessageA(hTree, TVM_SETITEMHEIGHT, 3, 0);
1029 ok(ulOldHeight == 4, "got %d, expected %d\n", ulOldHeight, 4);
1030 ulNewHeight = SendMessageA(hTree, TVM_GETITEMHEIGHT, 0, 0);
1031 ok(ulNewHeight == 3, "got %d, expected %d\n", ulNewHeight, 3);
1032 /* even value */
1033 ulOldHeight = SendMessageA(hTree, TVM_SETITEMHEIGHT, 10, 0);
1034 ok(ulOldHeight == 3, "got %d, expected %d\n", ulOldHeight, 3);
1035 ulNewHeight = SendMessageA(hTree, TVM_GETITEMHEIGHT, 0, 0);
1036 ok(ulNewHeight == 10, "got %d, expected %d\n", ulNewHeight, 10);
1038 DestroyWindow(hTree);
1041 static void test_get_set_scrolltime(void)
1043 int ulExpectedTime = 20;
1044 int ulTime = 0;
1045 HWND hTree;
1047 hTree = create_treeview_control(0);
1048 fill_tree(hTree);
1050 flush_sequences(sequences, NUM_MSG_SEQUENCES);
1052 SendMessageA(hTree, TVM_SETSCROLLTIME, ulExpectedTime, 0);
1053 ulTime = SendMessageA(hTree, TVM_GETSCROLLTIME, 0, 0);
1054 ok(ulTime == ulExpectedTime, "Scroll time reported as %d, expected %d\n", ulTime, ulExpectedTime);
1056 ok_sequence(sequences, TREEVIEW_SEQ_INDEX, test_get_set_scrolltime_seq,
1057 "test get set scroll time", FALSE);
1059 DestroyWindow(hTree);
1062 static void test_get_set_textcolor(void)
1064 /* If the value is -1, the control is using the system color for the text color. */
1065 COLORREF crColor;
1066 HWND hTree;
1068 hTree = create_treeview_control(0);
1069 fill_tree(hTree);
1071 flush_sequences(sequences, NUM_MSG_SEQUENCES);
1073 crColor = SendMessageA(hTree, TVM_GETTEXTCOLOR, 0, 0);
1074 ok(crColor == ~0u, "Default text color reported as 0x%.8lx\n", crColor);
1076 /* Test for black text */
1077 SendMessageA(hTree, TVM_SETTEXTCOLOR, 0, RGB(0,0,0));
1078 crColor = SendMessageA(hTree, TVM_GETTEXTCOLOR, 0, 0);
1079 ok(crColor == RGB(0,0,0), "Black text color reported as 0x%.8lx\n", crColor);
1081 /* Test for white text */
1082 SendMessageA(hTree, TVM_SETTEXTCOLOR, 0, RGB(255,255,255));
1083 crColor = SendMessageA(hTree, TVM_GETTEXTCOLOR, 0, 0);
1084 ok(crColor == RGB(255,255,255), "White text color reported as 0x%.8lx\n", crColor);
1086 /* Reset the default text color */
1087 SendMessageA(hTree, TVM_SETTEXTCOLOR, 0, CLR_NONE);
1089 ok_sequence(sequences, TREEVIEW_SEQ_INDEX, test_get_set_textcolor_seq,
1090 "test get set text color", FALSE);
1092 DestroyWindow(hTree);
1095 static void test_get_set_tooltips(void)
1097 HWND hTree, tooltips, hwnd;
1098 DWORD style;
1099 int i;
1101 /* TVS_NOTOOLTIPS */
1102 hTree = create_treeview_control(TVS_NOTOOLTIPS);
1104 tooltips = (HWND)SendMessageA(hTree, TVM_GETTOOLTIPS, 0, 0);
1105 ok(tooltips == NULL, "Unexpected tooltip window %p.\n", tooltips);
1107 tooltips = (HWND)SendMessageA(hTree, TVM_SETTOOLTIPS, 0, 0);
1108 ok(tooltips == NULL, "Unexpected ret value %p.\n", tooltips);
1110 /* Toggle style */
1111 style = GetWindowLongA(hTree, GWL_STYLE);
1112 SetWindowLongA(hTree, GWL_STYLE, style & ~TVS_NOTOOLTIPS);
1114 tooltips = (HWND)SendMessageA(hTree, TVM_GETTOOLTIPS, 0, 0);
1115 ok(IsWindow(tooltips), "Unexpected tooltip window %p.\n", tooltips);
1117 style = GetWindowLongA(hTree, GWL_STYLE);
1118 SetWindowLongA(hTree, GWL_STYLE, style | TVS_NOTOOLTIPS);
1120 tooltips = (HWND)SendMessageA(hTree, TVM_GETTOOLTIPS, 0, 0);
1121 ok(tooltips == NULL, "Unexpected tooltip window %p.\n", tooltips);
1123 DestroyWindow(hTree);
1125 /* Set some valid window, does not have to be tooltips class. */
1126 hTree = create_treeview_control(TVS_NOTOOLTIPS);
1128 hwnd = CreateWindowA(WC_STATICA, "Test", WS_VISIBLE|WS_CHILD, 5, 5, 100, 100, hMainWnd, NULL, NULL, 0);
1129 ok(hwnd != NULL, "Failed to create child window.\n");
1131 tooltips = (HWND)SendMessageA(hTree, TVM_SETTOOLTIPS, (WPARAM)hwnd, 0);
1132 ok(tooltips == NULL, "Unexpected ret value %p.\n", tooltips);
1134 tooltips = (HWND)SendMessageA(hTree, TVM_GETTOOLTIPS, 0, 0);
1135 ok(tooltips == hwnd, "Unexpected tooltip window %p.\n", tooltips);
1137 /* Externally set tooltips window, disable style. */
1138 style = GetWindowLongA(hTree, GWL_STYLE);
1139 SetWindowLongA(hTree, GWL_STYLE, style & ~TVS_NOTOOLTIPS);
1141 tooltips = (HWND)SendMessageA(hTree, TVM_GETTOOLTIPS, 0, 0);
1142 ok(IsWindow(tooltips) && tooltips != hwnd, "Unexpected tooltip window %p.\n", tooltips);
1143 ok(IsWindow(hwnd), "Expected valid window.\n");
1145 style = GetWindowLongA(hTree, GWL_STYLE);
1146 SetWindowLongA(hTree, GWL_STYLE, style | TVS_NOTOOLTIPS);
1147 ok(!IsWindow(tooltips), "Unexpected tooltip window %p.\n", tooltips);
1149 tooltips = (HWND)SendMessageA(hTree, TVM_GETTOOLTIPS, 0, 0);
1150 ok(tooltips == NULL, "Unexpected tooltip window %p.\n", tooltips);
1151 ok(IsWindow(hwnd), "Expected valid window.\n");
1153 DestroyWindow(hTree);
1154 ok(IsWindow(hwnd), "Expected valid window.\n");
1156 /* Set window, disable tooltips. */
1157 hTree = create_treeview_control(0);
1159 tooltips = (HWND)SendMessageA(hTree, TVM_SETTOOLTIPS, (WPARAM)hwnd, 0);
1160 ok(IsWindow(tooltips), "Unexpected ret value %p.\n", tooltips);
1162 style = GetWindowLongA(hTree, GWL_STYLE);
1163 SetWindowLongA(hTree, GWL_STYLE, style | TVS_NOTOOLTIPS);
1164 ok(!IsWindow(hwnd), "Unexpected tooltip window %p.\n", tooltips);
1165 ok(IsWindow(tooltips), "Expected valid window %p.\n", tooltips);
1167 DestroyWindow(hTree);
1168 ok(IsWindow(tooltips), "Expected valid window %p.\n", tooltips);
1169 DestroyWindow(tooltips);
1170 DestroyWindow(hwnd);
1172 for (i = 0; i < 2; i++)
1174 DWORD style = i == 0 ? 0 : TVS_NOTOOLTIPS;
1176 hwnd = CreateWindowA(WC_STATICA, "Test", WS_VISIBLE|WS_CHILD, 5, 5, 100, 100, hMainWnd, NULL, NULL, 0);
1177 ok(hwnd != NULL, "Failed to create child window.\n");
1179 hTree = create_treeview_control(style);
1181 tooltips = (HWND)SendMessageA(hTree, TVM_SETTOOLTIPS, (WPARAM)hwnd, 0);
1182 ok(style & TVS_NOTOOLTIPS ? tooltips == NULL : IsWindow(tooltips), "Unexpected ret value %p.\n", tooltips);
1183 DestroyWindow(tooltips);
1185 tooltips = (HWND)SendMessageA(hTree, TVM_GETTOOLTIPS, 0, 0);
1186 ok(tooltips == hwnd, "Unexpected tooltip window %p.\n", tooltips);
1188 /* TreeView is destroyed, check if set window is still around. */
1189 DestroyWindow(hTree);
1190 ok(!IsWindow(hwnd), "Unexpected window.\n");
1193 hTree = create_treeview_control(0);
1194 fill_tree(hTree);
1196 flush_sequences(sequences, NUM_MSG_SEQUENCES);
1198 /* show even WS_POPUP treeview don't send NM_TOOLTIPSCREATED */
1199 hwnd = CreateWindowA(WC_TREEVIEWA, NULL, WS_POPUP|WS_VISIBLE, 0, 0, 100, 100,
1200 hMainWnd, NULL, NULL, NULL);
1201 DestroyWindow(hwnd);
1203 /* Testing setting a NULL ToolTip */
1204 tooltips = (HWND)SendMessageA(hTree, TVM_SETTOOLTIPS, 0, 0);
1205 ok(IsWindow(tooltips), "Unexpected ret value %p.\n", tooltips);
1207 hwnd = (HWND)SendMessageA(hTree, TVM_GETTOOLTIPS, 0, 0);
1208 ok(hwnd == NULL, "Unexpected tooltip window %p.\n", hwnd);
1210 ok_sequence(sequences, TREEVIEW_SEQ_INDEX, test_get_set_tooltips_seq,
1211 "test get set tooltips", TRUE);
1213 DestroyWindow(hTree);
1214 ok(IsWindow(tooltips), "Expected valid window.\n");
1215 DestroyWindow(tooltips);
1218 static void test_get_set_unicodeformat(void)
1220 BOOL bPreviousSetting;
1221 BOOL bNewSetting;
1222 HWND hTree;
1224 hTree = create_treeview_control(0);
1225 fill_tree(hTree);
1227 /* Check that an invalid format returned by NF_QUERY defaults to ANSI */
1228 bPreviousSetting = SendMessageA(hTree, TVM_GETUNICODEFORMAT, 0, 0);
1229 ok(bPreviousSetting == FALSE, "Format should be ANSI.\n");
1231 flush_sequences(sequences, NUM_MSG_SEQUENCES);
1233 /* Set to Unicode */
1234 bPreviousSetting = SendMessageA(hTree, TVM_SETUNICODEFORMAT, 1, 0);
1235 bNewSetting = SendMessageA(hTree, TVM_GETUNICODEFORMAT, 0, 0);
1236 ok(bNewSetting == TRUE, "Unicode setting did not work.\n");
1238 /* Set to ANSI */
1239 SendMessageA(hTree, TVM_SETUNICODEFORMAT, 0, 0);
1240 bNewSetting = SendMessageA(hTree, TVM_GETUNICODEFORMAT, 0, 0);
1241 ok(bNewSetting == FALSE, "ANSI setting did not work.\n");
1243 /* Revert to original setting */
1244 SendMessageA(hTree, TVM_SETUNICODEFORMAT, bPreviousSetting, 0);
1246 ok_sequence(sequences, TREEVIEW_SEQ_INDEX, test_get_set_unicodeformat_seq,
1247 "test get set unicode format", FALSE);
1249 DestroyWindow(hTree);
1252 static LRESULT CALLBACK parent_wnd_proc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
1254 static LONG defwndproc_counter = 0;
1255 struct message msg = { 0 };
1256 LRESULT ret;
1257 RECT rect;
1258 HTREEITEM visibleItem;
1260 msg.message = message;
1261 msg.flags = sent|wparam|lparam;
1262 if (defwndproc_counter) msg.flags |= defwinproc;
1263 msg.wParam = wParam;
1264 msg.lParam = lParam;
1265 if (message == WM_NOTIFY && lParam)
1266 msg.id = ((NMHDR*)lParam)->code;
1268 /* log system messages, except for painting */
1269 if (message < WM_USER &&
1270 message != WM_PAINT &&
1271 message != WM_ERASEBKGND &&
1272 message != WM_NCPAINT &&
1273 message != WM_NCHITTEST &&
1274 message != WM_GETTEXT &&
1275 message != WM_GETICON &&
1276 message != WM_DEVICECHANGE)
1278 add_message(sequences, PARENT_SEQ_INDEX, &msg);
1281 switch(message) {
1282 case WM_NOTIFYFORMAT:
1284 /* Make NF_QUERY return an invalid format to show that it defaults to ANSI */
1285 if (lParam == NF_QUERY) return 0;
1286 break;
1289 case WM_NOTIFY:
1291 NMHDR *pHdr = (NMHDR *)lParam;
1293 ok(pHdr->code != NM_TOOLTIPSCREATED, "Treeview should not send NM_TOOLTIPSCREATED\n");
1294 if (pHdr->idFrom == 100)
1296 NMTREEVIEWA *pTreeView = (LPNMTREEVIEWA) lParam;
1297 switch(pHdr->code)
1299 case TVN_SELCHANGINGA:
1300 AddItem('(');
1301 IdentifyItem(pTreeView->itemOld.hItem);
1302 IdentifyItem(pTreeView->itemNew.hItem);
1303 break;
1304 case TVN_SELCHANGEDA:
1305 AddItem(')');
1306 IdentifyItem(pTreeView->itemOld.hItem);
1307 IdentifyItem(pTreeView->itemNew.hItem);
1308 break;
1309 case TVN_GETDISPINFOA: {
1310 NMTVDISPINFOA *disp = (NMTVDISPINFOA *)lParam;
1311 NMTVDISPINFOW *dispW = (NMTVDISPINFOW *)lParam;
1313 if (disp->item.mask & TVIF_TEXT) {
1314 lstrcpynA(disp->item.pszText, TEST_CALLBACK_TEXT, disp->item.cchTextMax);
1317 if (g_disp_A_to_W && (dispW->item.mask & TVIF_TEXT))
1319 dispW->hdr.code = TVN_GETDISPINFOW;
1320 lstrcpyW(dispW->item.pszText, L"TEST2");
1323 if (g_disp_set_stateimage)
1325 ok(disp->item.mask == TVIF_IMAGE, "got %x\n", disp->item.mask);
1326 /* both masks set here are necessary to change state bits */
1327 disp->item.mask |= TVIF_STATE;
1328 disp->item.state = TVIS_SELECTED | INDEXTOSTATEIMAGEMASK(2) | INDEXTOOVERLAYMASK(3);
1329 disp->item.stateMask = TVIS_SELECTED | TVIS_OVERLAYMASK | TVIS_STATEIMAGEMASK;
1332 break;
1334 case TVN_BEGINLABELEDITA:
1336 if (g_beginedit_alter_text)
1338 static const char* textA = "<edittextaltered>";
1339 HWND edit;
1341 edit = (HWND)SendMessageA(pHdr->hwndFrom, TVM_GETEDITCONTROL, 0, 0);
1342 ok(IsWindow(edit), "failed to get edit handle\n");
1343 SetWindowTextA(edit, textA);
1346 break;
1349 case TVN_ENDLABELEDITA:
1351 NMTVDISPINFOA *disp = (NMTVDISPINFOA *)lParam;
1352 if (disp->item.mask & TVIF_TEXT)
1354 ok(disp->item.cchTextMax == MAX_PATH, "cchTextMax is %d\n", disp->item.cchTextMax);
1355 if (g_endedit_overwrite_contents)
1356 strcpy(disp->item.pszText, g_endedit_overwrite_contents);
1357 if (g_endedit_overwrite_ptr)
1358 disp->item.pszText = g_endedit_overwrite_ptr;
1360 return TRUE;
1362 case TVN_ITEMEXPANDINGA:
1364 UINT newmask = pTreeView->itemNew.mask & ~TVIF_CHILDREN;
1365 ok(newmask ==
1366 (TVIF_HANDLE | TVIF_SELECTEDIMAGE | TVIF_IMAGE | TVIF_PARAM | TVIF_STATE),
1367 "got wrong mask %x\n", pTreeView->itemNew.mask);
1368 ok(pTreeView->itemOld.mask == 0,
1369 "got wrong mask %x\n", pTreeView->itemOld.mask);
1371 if (g_get_from_expand)
1373 g_item_expanding.mask = TVIF_STATE;
1374 g_item_expanding.hItem = hRoot;
1375 ret = SendMessageA(pHdr->hwndFrom, TVM_GETITEMA, 0, (LPARAM)&g_item_expanding);
1376 ok(ret == TRUE, "got %Iu\n", ret);
1378 break;
1380 case TVN_ITEMEXPANDEDA:
1381 ok(pTreeView->itemNew.mask & TVIF_STATE, "got wrong mask %x\n", pTreeView->itemNew.mask);
1382 ok(pTreeView->itemNew.state & (TVIS_EXPANDED|TVIS_EXPANDEDONCE),
1383 "got wrong mask %x\n", pTreeView->itemNew.mask);
1384 ok(pTreeView->itemOld.mask == 0,
1385 "got wrong mask %x\n", pTreeView->itemOld.mask);
1387 if (g_get_from_expand)
1389 g_item_expanded.mask = TVIF_STATE;
1390 g_item_expanded.hItem = hRoot;
1391 ret = SendMessageA(pHdr->hwndFrom, TVM_GETITEMA, 0, (LPARAM)&g_item_expanded);
1392 ok(ret == TRUE, "got %Iu\n", ret);
1394 if (g_get_rect_in_expand)
1396 visibleItem = (HTREEITEM)SendMessageA(pHdr->hwndFrom, TVM_GETNEXTITEM,
1397 TVGN_FIRSTVISIBLE, 0);
1398 ok(pTreeView->itemNew.hItem == visibleItem, "expanded item == first visible item\n");
1399 *(HTREEITEM*)&rect = visibleItem;
1400 ok(SendMessageA(pHdr->hwndFrom, TVM_GETITEMRECT, TRUE, (LPARAM)&rect),
1401 "Failed to get rect for first visible item.\n");
1402 visibleItem = (HTREEITEM)SendMessageA(pHdr->hwndFrom, TVM_GETNEXTITEM,
1403 TVGN_NEXTVISIBLE, (LPARAM)visibleItem);
1404 *(HTREEITEM*)&rect = visibleItem;
1405 ok(visibleItem != NULL, "There must be a visible item after the first one.\n");
1406 ok(SendMessageA(pHdr->hwndFrom, TVM_GETITEMRECT, TRUE, (LPARAM)&rect),
1407 "Failed to get rect for second visible item.\n");
1409 break;
1410 case TVN_DELETEITEMA:
1412 struct message item;
1414 ok(pTreeView->itemNew.mask == 0, "got wrong mask 0x%x\n", pTreeView->itemNew.mask);
1416 ok(pTreeView->itemOld.mask == (TVIF_HANDLE | TVIF_PARAM), "got wrong mask 0x%x\n", pTreeView->itemOld.mask);
1417 ok(pTreeView->itemOld.hItem != NULL, "got %p\n", pTreeView->itemOld.hItem);
1419 memset(&item, 0, sizeof(item));
1420 item.lParam = (LPARAM)pTreeView->itemOld.hItem;
1421 add_message(item_sequence, 0, &item);
1423 break;
1425 case NM_CUSTOMDRAW:
1427 NMTVCUSTOMDRAW *nmcd = (NMTVCUSTOMDRAW*)lParam;
1428 COLORREF c0ffee = RGB(0xc0,0xff,0xee), cafe = RGB(0xca,0xfe,0x00);
1429 COLORREF text = GetTextColor(nmcd->nmcd.hdc), bkgnd = GetBkColor(nmcd->nmcd.hdc);
1431 msg.flags |= custdraw;
1432 msg.stage = nmcd->nmcd.dwDrawStage;
1433 add_message(sequences, PARENT_CD_SEQ_INDEX, &msg);
1435 switch (msg.stage)
1437 case CDDS_PREPAINT:
1438 return CDRF_NOTIFYITEMDRAW|CDRF_NOTIFYITEMERASE|CDRF_NOTIFYPOSTPAINT;
1439 case CDDS_ITEMPREPAINT:
1440 ok(text == nmcd->clrText || (g_v6 && nmcd->clrText == 0xffffffff),
1441 "got %08lx vs %08lx\n", text, nmcd->clrText);
1442 ok(bkgnd == nmcd->clrTextBk || (g_v6 && nmcd->clrTextBk == 0xffffffff),
1443 "got %08lx vs %08lx\n", bkgnd, nmcd->clrTextBk);
1444 nmcd->clrText = cafe;
1445 nmcd->clrTextBk = c0ffee;
1446 SetTextColor(nmcd->nmcd.hdc, c0ffee);
1447 SetBkColor(nmcd->nmcd.hdc, cafe);
1448 if (g_customdraw_font)
1449 SelectObject(nmcd->nmcd.hdc, g_customdraw_font);
1450 return CDRF_NOTIFYPOSTPAINT|CDRF_NEWFONT;
1451 case CDDS_ITEMPOSTPAINT:
1452 /* at the point of post paint notification colors are already restored */
1453 ok(nmcd->clrText == cafe, "got 0%lx\n", nmcd->clrText);
1454 ok(nmcd->clrTextBk == c0ffee, "got 0%lx\n", nmcd->clrTextBk);
1455 ok(text != cafe, "got 0%lx\n", text);
1456 ok(bkgnd != c0ffee, "got 0%lx\n", bkgnd);
1457 if (g_customdraw_font)
1458 ok(GetCurrentObject(nmcd->nmcd.hdc, OBJ_FONT) != g_customdraw_font, "got %p\n",
1459 GetCurrentObject(nmcd->nmcd.hdc, OBJ_FONT));
1460 break;
1461 default:
1464 break;
1466 case NM_RCLICK:
1468 HTREEITEM selected = (HTREEITEM)SendMessageA(((NMHDR *)lParam)->hwndFrom,
1469 TVM_GETNEXTITEM, TVGN_CARET, 0);
1470 ok(selected == hChild, "child item should still be selected\n");
1471 break;
1475 break;
1480 defwndproc_counter++;
1481 ret = DefWindowProcA(hWnd, message, wParam, lParam);
1482 defwndproc_counter--;
1484 return ret;
1487 static void test_expandinvisible(void)
1489 static CHAR nodeText[][5] = {"0", "1", "2", "3", "4"};
1490 TVINSERTSTRUCTA ins;
1491 HTREEITEM node[5];
1492 RECT dummyRect;
1493 BOOL nodeVisible;
1494 LRESULT ret;
1495 HWND hTree;
1497 hTree = create_treeview_control(0);
1499 /* The test builds the following tree and expands node 1, while node 0 is collapsed.
1502 * |- 1
1503 * | |- 2
1504 * | |- 3
1505 * |- 4
1509 ret = SendMessageA(hTree, TVM_DELETEITEM, 0, (LPARAM)TVI_ROOT);
1510 ok(ret == TRUE, "ret\n");
1511 ins.hParent = TVI_ROOT;
1512 ins.hInsertAfter = TVI_ROOT;
1513 U(ins).item.mask = TVIF_TEXT;
1514 U(ins).item.pszText = nodeText[0];
1515 node[0] = TreeView_InsertItemA(hTree, &ins);
1516 ok(node[0] != NULL, "failed to set node[0]\n");
1518 ins.hInsertAfter = TVI_LAST;
1519 U(ins).item.mask = TVIF_TEXT;
1520 ins.hParent = node[0];
1522 U(ins).item.pszText = nodeText[1];
1523 node[1] = TreeView_InsertItemA(hTree, &ins);
1524 ok(node[1] != NULL, "failed to set node[1]\n");
1525 U(ins).item.pszText = nodeText[4];
1526 node[4] = TreeView_InsertItemA(hTree, &ins);
1527 ok(node[4] != NULL, "failed to set node[4]\n");
1529 ins.hParent = node[1];
1531 U(ins).item.pszText = nodeText[2];
1532 node[2] = TreeView_InsertItemA(hTree, &ins);
1533 ok(node[2] != NULL, "failed to set node[2]\n");
1534 U(ins).item.pszText = nodeText[3];
1535 node[3] = TreeView_InsertItemA(hTree, &ins);
1536 ok(node[3] != NULL, "failed to set node[3]\n");
1538 *(HTREEITEM *)&dummyRect = node[1];
1539 nodeVisible = SendMessageA(hTree, TVM_GETITEMRECT, FALSE, (LPARAM)&dummyRect);
1540 ok(!nodeVisible, "Node 1 should not be visible.\n");
1541 *(HTREEITEM *)&dummyRect = node[2];
1542 nodeVisible = SendMessageA(hTree, TVM_GETITEMRECT, FALSE, (LPARAM)&dummyRect);
1543 ok(!nodeVisible, "Node 2 should not be visible.\n");
1544 *(HTREEITEM *)&dummyRect = node[3];
1545 nodeVisible = SendMessageA(hTree, TVM_GETITEMRECT, FALSE, (LPARAM)&dummyRect);
1546 ok(!nodeVisible, "Node 3 should not be visible.\n");
1547 *(HTREEITEM *)&dummyRect = node[4];
1548 nodeVisible = SendMessageA(hTree, TVM_GETITEMRECT, FALSE, (LPARAM)&dummyRect);
1549 ok(!nodeVisible, "Node 4 should not be visible.\n");
1551 ok(SendMessageA(hTree, TVM_EXPAND, TVE_EXPAND, (LPARAM)node[1]), "Expand of node 1 failed.\n");
1553 *(HTREEITEM *)&dummyRect = node[1];
1554 nodeVisible = SendMessageA(hTree, TVM_GETITEMRECT, FALSE, (LPARAM)&dummyRect);
1555 ok(!nodeVisible, "Node 1 should not be visible.\n");
1556 *(HTREEITEM *)&dummyRect = node[2];
1557 nodeVisible = SendMessageA(hTree, TVM_GETITEMRECT, FALSE, (LPARAM)&dummyRect);
1558 ok(!nodeVisible, "Node 2 should not be visible.\n");
1559 *(HTREEITEM *)&dummyRect = node[3];
1560 nodeVisible = SendMessageA(hTree, TVM_GETITEMRECT, FALSE, (LPARAM)&dummyRect);
1561 ok(!nodeVisible, "Node 3 should not be visible.\n");
1562 *(HTREEITEM *)&dummyRect = node[4];
1563 nodeVisible = SendMessageA(hTree, TVM_GETITEMRECT, FALSE, (LPARAM)&dummyRect);
1564 ok(!nodeVisible, "Node 4 should not be visible.\n");
1566 DestroyWindow(hTree);
1569 static void test_expand(void)
1571 HTREEITEM first, second, last, child;
1572 TVINSERTSTRUCTA ins;
1573 BOOL visible;
1574 RECT rect;
1575 HWND tv;
1576 int i;
1578 tv = create_treeview_control(0);
1580 ins.hParent = TVI_ROOT;
1581 ins.hInsertAfter = TVI_LAST;
1582 U(ins).item.mask = 0;
1583 first = TreeView_InsertItemA(tv, &ins);
1584 ok(first != NULL, "failed to insert first node\n");
1585 second = TreeView_InsertItemA(tv, &ins);
1586 ok(second != NULL, "failed to insert second node\n");
1587 for (i=0; i<100; i++)
1589 last = TreeView_InsertItemA(tv, &ins);
1590 ok(last != NULL, "failed to insert %d node\n", i);
1593 ins.hParent = second;
1594 child = TreeView_InsertItemA(tv, &ins);
1595 ok(child != NULL, "failed to insert child node\n");
1597 ok(SendMessageA(tv, TVM_SELECTITEM, TVGN_CARET, (LPARAM)last), "last node selection failed\n");
1598 ok(SendMessageA(tv, TVM_EXPAND, TVE_EXPAND, (LPARAM)second), "expand of second node failed\n");
1599 ok(SendMessageA(tv, TVM_SELECTITEM, TVGN_CARET, (LPARAM)first), "first node selection failed\n");
1601 *(HTREEITEM *)&rect = first;
1602 visible = SendMessageA(tv, TVM_GETITEMRECT, FALSE, (LPARAM)&rect);
1603 ok(visible, "first node should be visible\n");
1604 ok(!rect.left, "rect.left = %ld\n", rect.left);
1605 ok(!rect.top, "rect.top = %ld\n", rect.top);
1606 ok(rect.right, "rect.right = 0\n");
1607 ok(rect.bottom, "rect.bottom = 0\n");
1609 DestroyWindow(tv);
1612 static void test_itemedit(void)
1614 DWORD r;
1615 HWND edit;
1616 TVITEMA item;
1617 CHAR buffA[500];
1618 HWND hTree;
1620 hTree = create_treeview_control(0);
1621 fill_tree(hTree);
1623 /* try with null item */
1624 edit = (HWND)SendMessageA(hTree, TVM_EDITLABELA, 0, 0);
1625 ok(!IsWindow(edit), "Expected valid handle\n");
1627 /* trigger edit */
1628 edit = (HWND)SendMessageA(hTree, TVM_EDITLABELA, 0, (LPARAM)hRoot);
1629 ok(IsWindow(edit), "Expected valid handle\n");
1630 /* item shouldn't be selected automatically after TVM_EDITLABELA */
1631 r = SendMessageA(hTree, TVM_GETITEMSTATE, (WPARAM)hRoot, TVIS_SELECTED);
1632 expect(0, r);
1633 /* try to cancel with wrong edit handle */
1634 r = SendMessageA(hTree, WM_COMMAND, MAKEWPARAM(0, EN_KILLFOCUS), 0);
1635 expect(0, r);
1636 ok(IsWindow(edit), "Expected edit control to be valid\n");
1637 r = SendMessageA(hTree, WM_COMMAND, MAKEWPARAM(0, EN_KILLFOCUS), (LPARAM)edit);
1638 expect(0, r);
1639 ok(!IsWindow(edit), "Expected edit control to be destroyed\n");
1640 /* try to cancel without creating edit */
1641 r = SendMessageA(hTree, WM_COMMAND, MAKEWPARAM(0, EN_KILLFOCUS), 0);
1642 expect(0, r);
1644 /* try to cancel with wrong (not null) handle */
1645 edit = (HWND)SendMessageA(hTree, TVM_EDITLABELA, 0, (LPARAM)hRoot);
1646 ok(IsWindow(edit), "Expected valid handle\n");
1647 r = SendMessageA(hTree, WM_COMMAND, MAKEWPARAM(0, EN_KILLFOCUS), (LPARAM)hTree);
1648 expect(0, r);
1649 ok(IsWindow(edit), "Expected edit control to be valid\n");
1650 r = SendMessageA(hTree, WM_COMMAND, MAKEWPARAM(0, EN_KILLFOCUS), (LPARAM)edit);
1651 expect(0, r);
1653 /* remove selection after starting edit */
1654 r = SendMessageA(hTree, TVM_SELECTITEM, TVGN_CARET, (LPARAM)hRoot);
1655 expect(TRUE, r);
1656 edit = (HWND)SendMessageA(hTree, TVM_EDITLABELA, 0, (LPARAM)hRoot);
1657 ok(IsWindow(edit), "Expected valid handle\n");
1658 r = SendMessageA(hTree, TVM_SELECTITEM, TVGN_CARET, 0);
1659 expect(TRUE, r);
1660 /* alter text */
1661 strcpy(buffA, "x");
1662 r = SendMessageA(edit, WM_SETTEXT, 0, (LPARAM)buffA);
1663 expect(TRUE, r);
1664 r = SendMessageA(hTree, WM_COMMAND, MAKEWPARAM(0, EN_KILLFOCUS), (LPARAM)edit);
1665 expect(0, r);
1666 ok(!IsWindow(edit), "Expected edit control to be destroyed\n");
1667 /* check that text is saved */
1668 item.mask = TVIF_TEXT;
1669 item.hItem = hRoot;
1670 item.pszText = buffA;
1671 item.cchTextMax = ARRAY_SIZE(buffA);
1672 r = SendMessageA(hTree, TVM_GETITEMA, 0, (LPARAM)&item);
1673 expect(TRUE, r);
1674 ok(!strcmp("x", buffA), "Expected item text to change\n");
1676 /* try A/W messages */
1677 edit = (HWND)SendMessageA(hTree, TVM_EDITLABELA, 0, (LPARAM)hRoot);
1678 ok(IsWindow(edit), "Expected valid handle\n");
1679 ok(IsWindowUnicode(edit), "got ansi window\n");
1680 r = SendMessageA(hTree, WM_COMMAND, MAKEWPARAM(0, EN_KILLFOCUS), (LPARAM)edit);
1681 expect(0, r);
1682 ok(!IsWindow(edit), "expected invalid handle\n");
1684 edit = (HWND)SendMessageA(hTree, TVM_EDITLABELW, 0, (LPARAM)hRoot);
1685 ok(IsWindow(edit), "Expected valid handle\n");
1686 ok(IsWindowUnicode(edit), "got ansi window\n");
1687 r = SendMessageA(hTree, WM_COMMAND, MAKEWPARAM(0, EN_KILLFOCUS), (LPARAM)edit);
1688 expect(0, r);
1690 /* alter text during TVM_BEGINLABELEDIT, check that it's preserved */
1691 strcpy(buffA, "<root>");
1693 item.mask = TVIF_TEXT;
1694 item.hItem = hRoot;
1695 item.pszText = buffA;
1696 item.cchTextMax = 0;
1697 r = SendMessageA(hTree, TVM_SETITEMA, 0, (LPARAM)&item);
1698 expect(TRUE, r);
1700 g_beginedit_alter_text = TRUE;
1701 edit = (HWND)SendMessageA(hTree, TVM_EDITLABELA, 0, (LPARAM)hRoot);
1702 ok(IsWindow(edit), "Expected valid handle\n");
1703 g_beginedit_alter_text = FALSE;
1705 GetWindowTextA(edit, buffA, ARRAY_SIZE(buffA));
1706 ok(!strcmp(buffA, "<edittextaltered>"), "got string %s\n", buffA);
1708 r = SendMessageA(hTree, WM_COMMAND, MAKEWPARAM(0, EN_KILLFOCUS), (LPARAM)edit);
1709 expect(0, r);
1711 /* How much text can be typed? */
1712 edit = (HWND)SendMessageA(hTree, TVM_EDITLABELA, 0, (LPARAM)hRoot);
1713 ok(IsWindow(edit), "Expected valid handle\n");
1714 r = SendMessageA(edit, EM_GETLIMITTEXT, 0, 0);
1715 expect(MAX_PATH - 1, r);
1716 /* WM_SETTEXT can set more... */
1717 memset(buffA, 'a', ARRAY_SIZE(buffA));
1718 buffA[ARRAY_SIZE(buffA)-1] = 0;
1719 r = SetWindowTextA(edit, buffA);
1720 expect(TRUE, r);
1721 r = GetWindowTextA(edit, buffA, ARRAY_SIZE(buffA));
1722 ok( r == ARRAY_SIZE(buffA) - 1, "got %ld\n", r );
1723 /* ...but it's trimmed to MAX_PATH chars when editing ends */
1724 r = SendMessageA(hTree, WM_COMMAND, MAKEWPARAM(0, EN_KILLFOCUS), (LPARAM)edit);
1725 expect(0, r);
1726 item.mask = TVIF_TEXT;
1727 item.hItem = hRoot;
1728 item.pszText = buffA;
1729 item.cchTextMax = ARRAY_SIZE(buffA);
1730 r = SendMessageA(hTree, TVM_GETITEMA, 0, (LPARAM)&item);
1731 expect(TRUE, r);
1732 expect(MAX_PATH - 1, lstrlenA(item.pszText));
1734 /* We can't get around that MAX_PATH limit by increasing EM_SETLIMITTEXT */
1735 edit = (HWND)SendMessageA(hTree, TVM_EDITLABELA, 0, (LPARAM)hRoot);
1736 ok(IsWindow(edit), "Expected valid handle\n");
1737 SendMessageA(edit, EM_SETLIMITTEXT, ARRAY_SIZE(buffA)-1, 0);
1738 memset(buffA, 'a', ARRAY_SIZE(buffA));
1739 buffA[ARRAY_SIZE(buffA)-1] = 0;
1740 r = SetWindowTextA(edit, buffA);
1741 expect(TRUE, r);
1742 r = SendMessageA(hTree, WM_COMMAND, MAKEWPARAM(0, EN_KILLFOCUS), (LPARAM)edit);
1743 expect(0, r);
1744 item.mask = TVIF_TEXT;
1745 item.hItem = hRoot;
1746 item.pszText = buffA;
1747 item.cchTextMax = ARRAY_SIZE(buffA);
1748 r = SendMessageA(hTree, TVM_GETITEMA, 0, (LPARAM)&item);
1749 expect(TRUE, r);
1750 expect(MAX_PATH - 1, lstrlenA(item.pszText));
1752 /* Overwriting of pszText contents in TVN_ENDLABELEDIT */
1753 edit = (HWND)SendMessageA(hTree, TVM_EDITLABELA, 0, (LPARAM)hRoot);
1754 ok(IsWindow(edit), "Expected valid handle\n");
1755 r = SetWindowTextA(edit, "old");
1756 expect(TRUE, r);
1757 g_endedit_overwrite_contents = "<new_contents>";
1758 r = SendMessageA(hTree, WM_COMMAND, MAKEWPARAM(0, EN_KILLFOCUS), (LPARAM)edit);
1759 expect(0, r);
1760 g_endedit_overwrite_contents = NULL;
1761 item.mask = TVIF_TEXT;
1762 item.hItem = hRoot;
1763 item.pszText = buffA;
1764 item.cchTextMax = ARRAY_SIZE(buffA);
1765 r = SendMessageA(hTree, TVM_GETITEMA, 0, (LPARAM)&item);
1766 expect(TRUE, r);
1767 expect(0, strcmp(item.pszText, "<new_contents>"));
1769 /* Overwriting of pszText pointer in TVN_ENDLABELEDIT */
1770 edit = (HWND)SendMessageA(hTree, TVM_EDITLABELA, 0, (LPARAM)hRoot);
1771 ok(IsWindow(edit), "Expected valid handle\n");
1772 r = SetWindowTextA(edit, "old");
1773 expect(TRUE, r);
1774 g_endedit_overwrite_ptr = (char*) "<new_ptr>";
1775 r = SendMessageA(hTree, WM_COMMAND, MAKEWPARAM(0, EN_KILLFOCUS), (LPARAM)edit);
1776 expect(0, r);
1777 g_endedit_overwrite_ptr = NULL;
1778 item.mask = TVIF_TEXT;
1779 item.hItem = hRoot;
1780 item.pszText = buffA;
1781 item.cchTextMax = ARRAY_SIZE(buffA);
1782 r = SendMessageA(hTree, TVM_GETITEMA, 0, (LPARAM)&item);
1783 expect(TRUE, r);
1784 expect(0, strcmp(item.pszText, "<new_ptr>"));
1786 DestroyWindow(hTree);
1789 static void test_treeview_classinfo(void)
1791 WNDCLASSA cls;
1793 memset(&cls, 0, sizeof(cls));
1794 GetClassInfoA(GetModuleHandleA("comctl32.dll"), WC_TREEVIEWA, &cls);
1795 ok(cls.hbrBackground == NULL, "Expected NULL background brush, got %p\n", cls.hbrBackground);
1796 ok(cls.style == (CS_GLOBALCLASS | CS_DBLCLKS), "Expected got %x\n", cls.style);
1797 expect(0, cls.cbClsExtra);
1800 static void test_get_linecolor(void)
1802 COLORREF clr;
1803 HWND hTree;
1805 hTree = create_treeview_control(0);
1807 /* newly created control has default color */
1808 clr = SendMessageA(hTree, TVM_GETLINECOLOR, 0, 0);
1809 if (clr == 0)
1810 win_skip("TVM_GETLINECOLOR is not supported on comctl32 < 5.80\n");
1811 else
1812 expect(CLR_DEFAULT, clr);
1814 DestroyWindow(hTree);
1817 static void test_get_insertmarkcolor(void)
1819 COLORREF clr;
1820 HWND hTree;
1822 hTree = create_treeview_control(0);
1824 /* newly created control has default color */
1825 clr = SendMessageA(hTree, TVM_GETINSERTMARKCOLOR, 0, 0);
1826 if (clr == 0)
1827 win_skip("TVM_GETINSERTMARKCOLOR is not supported on comctl32 < 5.80\n");
1828 else
1829 expect(CLR_DEFAULT, clr);
1831 DestroyWindow(hTree);
1834 static void test_expandnotify(void)
1836 HTREEITEM hitem;
1837 HWND hTree;
1838 BOOL ret;
1839 TVITEMA item;
1841 hTree = create_treeview_control(0);
1842 fill_tree(hTree);
1844 item.hItem = hRoot;
1845 item.mask = TVIF_STATE;
1847 item.state = TVIS_EXPANDED;
1848 ret = SendMessageA(hTree, TVM_GETITEMA, 0, (LPARAM)&item);
1849 expect(TRUE, ret);
1850 ok((item.state & TVIS_EXPANDED) == 0, "expected collapsed\n");
1852 /* preselect root node here */
1853 ret = SendMessageA(hTree, TVM_SELECTITEM, TVGN_CARET, (LPARAM)hRoot);
1854 expect(TRUE, ret);
1856 flush_sequences(sequences, NUM_MSG_SEQUENCES);
1857 ret = SendMessageA(hTree, TVM_EXPAND, TVE_COLLAPSE, (LPARAM)hRoot);
1858 expect(FALSE, ret);
1859 ok_sequence(sequences, PARENT_SEQ_INDEX, empty_seq, "no collapse notifications", FALSE);
1861 g_get_from_expand = TRUE;
1862 /* expand */
1863 flush_sequences(sequences, NUM_MSG_SEQUENCES);
1864 g_item_expanding.state = 0xdeadbeef;
1865 g_item_expanded.state = 0xdeadbeef;
1866 ret = SendMessageA(hTree, TVM_EXPAND, TVE_EXPAND, (LPARAM)hRoot);
1867 expect(TRUE, ret);
1868 ok(g_item_expanding.state == TVIS_SELECTED, "got state on TVN_ITEMEXPANDING 0x%08x\n",
1869 g_item_expanding.state);
1870 ok(g_item_expanded.state == (TVIS_SELECTED|TVIS_EXPANDED), "got state on TVN_ITEMEXPANDED 0x%08x\n",
1871 g_item_expanded.state);
1872 ok_sequence(sequences, PARENT_SEQ_INDEX, parent_expand_seq, "expand notifications", FALSE);
1873 g_get_from_expand = FALSE;
1875 /* check that it's expanded */
1876 item.state = TVIS_EXPANDED;
1877 ret = SendMessageA(hTree, TVM_GETITEMA, 0, (LPARAM)&item);
1878 expect(TRUE, ret);
1879 ok((item.state & TVIS_EXPANDED) == TVIS_EXPANDED, "expected expanded\n");
1881 /* collapse */
1882 flush_sequences(sequences, NUM_MSG_SEQUENCES);
1883 ret = SendMessageA(hTree, TVM_EXPAND, TVE_COLLAPSE, (LPARAM)hRoot);
1884 expect(TRUE, ret);
1885 item.state = TVIS_EXPANDED;
1886 ret = SendMessageA(hTree, TVM_GETITEMA, 0, (LPARAM)&item);
1887 expect(TRUE, ret);
1888 ok((item.state & TVIS_EXPANDED) == 0, "expected collapsed\n");
1889 /* all further collapse/expand attempts won't produce any notifications,
1890 the only way is to reset with all children removed */
1891 ok_sequence(sequences, PARENT_SEQ_INDEX, empty_seq, "collapse after expand notifications", FALSE);
1893 /* try to toggle child that doesn't have children itself */
1894 flush_sequences(sequences, NUM_MSG_SEQUENCES);
1895 ret = SendMessageA(hTree, TVM_EXPAND, TVE_TOGGLE, (LPARAM)hChild);
1896 expect(FALSE, ret);
1897 ok_sequence(sequences, PARENT_SEQ_INDEX, empty_seq, "toggle node without children", FALSE);
1899 DestroyWindow(hTree);
1901 /* test TVM_GETITEMRECT inside TVN_ITEMEXPANDED notification */
1902 hTree = create_treeview_control(0);
1903 fill_tree(hTree);
1904 g_get_rect_in_expand = TRUE;
1905 ret = SendMessageA(hTree, TVM_SELECTITEM, TVGN_CARET, (LPARAM)hChild);
1906 expect(TRUE, ret);
1907 g_get_rect_in_expand = FALSE;
1909 DestroyWindow(hTree);
1911 /* TVE_TOGGLE acts as any other TVM_EXPAND */
1912 hTree = create_treeview_control(0);
1913 fill_tree(hTree);
1915 flush_sequences(sequences, NUM_MSG_SEQUENCES);
1916 ret = SendMessageA(hTree, TVM_EXPAND, TVE_TOGGLE, (LPARAM)hRoot);
1917 expect(TRUE, ret);
1918 ok_sequence(sequences, PARENT_SEQ_INDEX, parent_expand_seq, "toggle node (expand)", FALSE);
1920 /* toggle again - no notifications */
1921 flush_sequences(sequences, NUM_MSG_SEQUENCES);
1922 ret = SendMessageA(hTree, TVM_EXPAND, TVE_TOGGLE, (LPARAM)hRoot);
1923 expect(TRUE, ret);
1924 ok_sequence(sequences, PARENT_SEQ_INDEX, empty_seq, "toggle node (collapse)", FALSE);
1926 DestroyWindow(hTree);
1928 /* some keyboard events are also translated to expand */
1929 hTree = create_treeview_control(0);
1930 fill_tree(hTree);
1932 /* preselect root node here */
1933 ret = SendMessageA(hTree, TVM_SELECTITEM, TVGN_CARET, (LPARAM)hRoot);
1934 expect(TRUE, ret);
1936 g_get_from_expand = TRUE;
1937 flush_sequences(sequences, NUM_MSG_SEQUENCES);
1938 ret = SendMessageA(hTree, WM_KEYDOWN, VK_ADD, 0);
1939 expect(FALSE, ret);
1940 ok_sequence(sequences, PARENT_SEQ_INDEX, parent_expand_kb_seq, "expand node", FALSE);
1941 ok(g_item_expanding.state == TVIS_SELECTED, "got state on TVN_ITEMEXPANDING 0x%08x\n",
1942 g_item_expanding.state);
1943 ok(g_item_expanded.state == (TVIS_SELECTED|TVIS_EXPANDED), "got state on TVN_ITEMEXPANDED 0x%08x\n",
1944 g_item_expanded.state);
1946 flush_sequences(sequences, NUM_MSG_SEQUENCES);
1947 ret = SendMessageA(hTree, WM_KEYDOWN, VK_ADD, 0);
1948 expect(FALSE, ret);
1949 ok_sequence(sequences, PARENT_SEQ_INDEX, parent_expand_kb_seq, "expand node again", FALSE);
1950 ok(g_item_expanding.state == (TVIS_SELECTED|TVIS_EXPANDED|TVIS_EXPANDEDONCE), "got state on TVN_ITEMEXPANDING 0x%08x\n",
1951 g_item_expanding.state);
1952 ok(g_item_expanded.state == (TVIS_SELECTED|TVIS_EXPANDED|TVIS_EXPANDEDONCE), "got state on TVN_ITEMEXPANDED 0x%08x\n",
1953 g_item_expanded.state);
1955 flush_sequences(sequences, NUM_MSG_SEQUENCES);
1956 ret = SendMessageA(hTree, WM_KEYDOWN, VK_SUBTRACT, 0);
1957 expect(FALSE, ret);
1958 ok_sequence(sequences, PARENT_SEQ_INDEX, parent_expand_kb_seq, "collapse node", FALSE);
1959 ok(g_item_expanding.state == (TVIS_SELECTED|TVIS_EXPANDED|TVIS_EXPANDEDONCE), "got state on TVN_ITEMEXPANDING 0x%08x\n",
1960 g_item_expanding.state);
1961 ok(g_item_expanded.state == (TVIS_SELECTED|TVIS_EXPANDEDONCE), "got state on TVN_ITEMEXPANDED 0x%08x\n",
1962 g_item_expanded.state);
1964 flush_sequences(sequences, NUM_MSG_SEQUENCES);
1965 ret = SendMessageA(hTree, WM_KEYDOWN, VK_SUBTRACT, 0);
1966 expect(FALSE, ret);
1967 ok_sequence(sequences, PARENT_SEQ_INDEX, parent_collapse_2nd_kb_seq, "collapse node again", FALSE);
1968 ok(g_item_expanding.state == (TVIS_SELECTED|TVIS_EXPANDEDONCE), "got state on TVN_ITEMEXPANDING 0x%08x\n",
1969 g_item_expanding.state);
1970 g_get_from_expand = FALSE;
1972 flush_sequences(sequences, NUM_MSG_SEQUENCES);
1973 ret = SendMessageA(hTree, WM_KEYDOWN, VK_ADD, 0);
1974 expect(FALSE, ret);
1975 ok_sequence(sequences, PARENT_SEQ_INDEX, parent_expand_kb_seq, "expand node", FALSE);
1977 /* go to child */
1978 ret = SendMessageA(hTree, WM_KEYDOWN, VK_RIGHT, 0);
1979 expect(FALSE, ret);
1981 /* try to expand child that doesn't have children itself */
1982 flush_sequences(sequences, NUM_MSG_SEQUENCES);
1983 ret = SendMessageA(hTree, WM_KEYDOWN, VK_ADD, 0);
1984 expect(FALSE, ret);
1985 ok_sequence(sequences, PARENT_SEQ_INDEX, parent_expand_empty_kb_seq, "expand node with no children", FALSE);
1987 /* stay on current selection and set non-zero children count */
1988 hitem = (HTREEITEM)SendMessageA(hTree, TVM_GETNEXTITEM, TVGN_CARET, 0);
1989 ok(hitem != NULL, "got %p\n", hitem);
1991 item.hItem = hitem;
1992 item.mask = TVIF_CHILDREN;
1993 item.cChildren = 0x80000000;
1995 ret = SendMessageA(hTree, TVM_SETITEMA, 0, (LPARAM)&item);
1996 expect(TRUE, ret);
1998 flush_sequences(sequences, NUM_MSG_SEQUENCES);
1999 ret = SendMessageA(hTree, WM_KEYDOWN, VK_ADD, 0);
2000 expect(FALSE, ret);
2001 ok_sequence(sequences, PARENT_SEQ_INDEX, parent_collapse_2nd_kb_seq, "expand node with children", FALSE);
2003 DestroyWindow(hTree);
2006 static void test_expandedimage(void)
2008 TVITEMEXA item;
2009 HWND hTree;
2010 BOOL ret;
2012 hTree = create_treeview_control(0);
2013 fill_tree(hTree);
2015 item.mask = TVIF_EXPANDEDIMAGE;
2016 item.iExpandedImage = 1;
2017 item.hItem = hRoot;
2018 ret = SendMessageA(hTree, TVM_SETITEMA, 0, (LPARAM)&item);
2019 ok(ret, "got %d\n", ret);
2021 item.mask = TVIF_EXPANDEDIMAGE;
2022 item.iExpandedImage = -1;
2023 item.hItem = hRoot;
2024 ret = SendMessageA(hTree, TVM_GETITEMA, 0, (LPARAM)&item);
2025 ok(ret, "got %d\n", ret);
2027 if (item.iExpandedImage != 1)
2029 win_skip("TVIF_EXPANDEDIMAGE not supported\n");
2030 DestroyWindow(hTree);
2031 return;
2034 /* test for default iExpandedImage value */
2035 item.mask = TVIF_EXPANDEDIMAGE;
2036 item.iExpandedImage = -1;
2037 item.hItem = hChild;
2038 ret = SendMessageA(hTree, TVM_GETITEMA, 0, (LPARAM)&item);
2039 ok(ret, "got %d\n", ret);
2040 ok(item.iExpandedImage == (WORD)I_IMAGENONE, "got %d\n", item.iExpandedImage);
2042 DestroyWindow(hTree);
2045 static void test_TVS_SINGLEEXPAND(void)
2047 HWND hTree;
2048 HTREEITEM alpha, bravo, charlie, delta, echo, foxtrot, golf, hotel, india, juliet;
2049 TVINSERTSTRUCTA ins;
2050 char foo[] = "foo";
2051 char context[32];
2052 int i;
2053 BOOL ret;
2055 /* build a fairly complex tree
2056 * - TVI_ROOT
2057 * - alpha
2058 * - bravo
2059 * - charlie
2060 * - delta
2061 * - echo
2062 * - foxtrot
2063 * - golf
2064 * - hotel
2065 * - india
2066 * - juliet
2068 struct
2070 HTREEITEM *handle;
2071 HTREEITEM *parent;
2072 UINT final_state;
2074 items[] =
2076 { &alpha, NULL, TVIS_EXPANDEDONCE },
2077 { &bravo, &alpha, TVIS_EXPANDEDONCE },
2078 { &charlie, &bravo, 0 },
2079 { &delta, &alpha, TVIS_EXPANDEDONCE },
2080 { &echo, &delta, 0 },
2081 { &foxtrot, NULL, TVIS_EXPANDEDONCE|TVIS_EXPANDED },
2082 { &golf, &foxtrot, TVIS_EXPANDEDONCE|TVIS_EXPANDED },
2083 { &hotel, &golf, 0 },
2084 { &india, &golf, TVIS_SELECTED },
2085 { &juliet, &foxtrot, 0 }
2088 struct
2090 HTREEITEM *select;
2091 const struct message *sequence;
2093 sequence_tests[] =
2095 { &alpha, parent_singleexpand_seq0 },
2096 { &bravo, parent_singleexpand_seq1 },
2097 { &delta, parent_singleexpand_seq2 },
2098 { &foxtrot, parent_singleexpand_seq3 },
2099 { &alpha, parent_singleexpand_seq4 },
2100 { &golf, parent_singleexpand_seq5 },
2101 { &hotel, parent_singleexpand_seq6 },
2102 { &india, parent_singleexpand_seq7 },
2103 { &india, empty_seq }
2106 hTree = create_treeview_control(0);
2107 SetWindowLongA(hTree, GWL_STYLE, GetWindowLongA(hTree, GWL_STYLE) | TVS_SINGLEEXPAND);
2108 /* to avoid painting related notifications */
2109 ShowWindow(hTree, SW_HIDE);
2110 for (i = 0; i < ARRAY_SIZE(items); i++)
2112 ins.hParent = items[i].parent ? *items[i].parent : TVI_ROOT;
2113 ins.hInsertAfter = TVI_FIRST;
2114 U(ins).item.mask = TVIF_TEXT;
2115 U(ins).item.pszText = foo;
2116 *items[i].handle = TreeView_InsertItemA(hTree, &ins);
2119 for (i = 0; i < ARRAY_SIZE(sequence_tests); i++)
2121 flush_sequences(sequences, NUM_MSG_SEQUENCES);
2122 ret = SendMessageA(hTree, TVM_SELECTITEM, TVGN_CARET, (LPARAM)(*sequence_tests[i].select));
2123 ok(ret, "got %d\n", ret);
2124 sprintf(context, "singleexpand notifications %d", i);
2125 ok_sequence(sequences, PARENT_SEQ_INDEX, sequence_tests[i].sequence, context, FALSE);
2128 for (i = 0; i < ARRAY_SIZE(items); i++)
2130 ret = SendMessageA(hTree, TVM_GETITEMSTATE, (WPARAM)(*items[i].handle), 0xFFFF);
2131 ok(ret == items[i].final_state, "singleexpand items[%d]: expected state 0x%x got 0x%x\n",
2132 i, items[i].final_state, ret);
2135 /* a workaround for NT4 that sends expand notifications when nothing is about to expand */
2136 ret = SendMessageA(hTree, TVM_DELETEITEM, 0, (LPARAM)TVI_ROOT);
2137 ok(ret, "got %d\n", ret);
2138 fill_tree(hTree);
2139 ret = SendMessageA(hTree, TVM_SELECTITEM, TVGN_CARET, 0);
2140 ok(ret, "got %d\n", ret);
2142 DestroyWindow(hTree);
2145 static void test_WM_PAINT(void)
2147 BOOL is_glyph_transparent;
2148 HTHEME htheme;
2149 HWND hTree;
2150 COLORREF clr;
2151 LONG ret;
2152 RECT rc;
2153 HDC hdc;
2155 hTree = create_treeview_control(0);
2157 clr = SendMessageA(hTree, TVM_SETBKCOLOR, 0, RGB(255, 0, 0));
2158 ok(clr == ~0u, "got %ld, expected -1\n", clr);
2160 hdc = GetDC(hMainWnd);
2162 GetClientRect(hMainWnd, &rc);
2163 FillRect(hdc, &rc, GetStockObject(BLACK_BRUSH));
2165 clr = GetPixel(hdc, 1, 1);
2166 ok(clr == RGB(0, 0, 0), "got 0x%lx\n", clr);
2168 ret = SendMessageA(hTree, WM_PAINT, (WPARAM)hdc, 0);
2169 ok(ret == 0, "got %ld\n", ret);
2171 clr = GetPixel(hdc, 1, 1);
2172 htheme = pGetWindowTheme(hTree);
2173 is_glyph_transparent = htheme && pIsThemeBackgroundPartiallyTransparent(htheme, TVP_GLYPH, 0);
2174 ok(clr == RGB(255, 0, 0) || broken(clr == RGB(0, 0, 0)) /* win98 */
2175 /* When theming is on and treeview glyphs are transparent, parent window needs to be repainted */
2176 || (is_glyph_transparent && clr == GetSysColor(COLOR_WINDOW)),
2177 "got 0x%lx\n", clr);
2179 ReleaseDC(hMainWnd, hdc);
2181 DestroyWindow(hTree);
2184 static void test_delete_items(void)
2186 const struct message *msg;
2187 HWND hTree;
2188 HTREEITEM hItem1, hItem2;
2189 TVINSERTSTRUCTA ins;
2190 INT ret;
2192 static CHAR item1[] = "Item 1";
2193 static CHAR item2[] = "Item 2";
2195 hTree = create_treeview_control(0);
2196 fill_tree(hTree);
2198 /* check delete order */
2199 flush_sequences(item_sequence, 1);
2200 ret = SendMessageA(hTree, TVM_DELETEITEM, 0, 0);
2201 ok(ret == TRUE, "got %d\n", ret);
2203 msg = item_sequence[0]->sequence;
2204 ok(item_sequence[0]->count == 2, "expected 2 items, got %d\n", item_sequence[0]->count);
2206 if (item_sequence[0]->count == 2)
2208 ok(msg[0].lParam == (LPARAM)hChild, "expected %p, got 0x%Ix\n", hChild, msg[0].lParam);
2209 ok(msg[1].lParam == (LPARAM)hRoot, "expected %p, got 0x%Ix\n", hRoot, msg[1].lParam);
2212 ret = SendMessageA(hTree, TVM_GETCOUNT, 0, 0);
2213 ok(ret == 0, "got %d\n", ret);
2215 DestroyWindow(hTree);
2217 /* Regression test for a crash when deleting the first visible item while bRedraw == false. */
2218 hTree = create_treeview_control(0);
2220 ret = SendMessageA(hTree, WM_SETREDRAW, FALSE, 0);
2221 ok(ret == 0, "got %d\n", ret);
2223 ins.hParent = TVI_ROOT;
2224 ins.hInsertAfter = TVI_ROOT;
2225 U(ins).item.mask = TVIF_TEXT;
2226 U(ins).item.pszText = item1;
2227 hItem1 = TreeView_InsertItemA(hTree, &ins);
2228 ok(hItem1 != NULL, "InsertItem failed\n");
2230 ins.hParent = TVI_ROOT;
2231 ins.hInsertAfter = hItem1;
2232 U(ins).item.mask = TVIF_TEXT;
2233 U(ins).item.pszText = item2;
2234 hItem2 = TreeView_InsertItemA(hTree, &ins);
2235 ok(hItem2 != NULL, "InsertItem failed\n");
2237 ret = SendMessageA(hTree, TVM_DELETEITEM, 0, (LPARAM)hItem1);
2238 ok(ret == TRUE, "got %d\n", ret);
2240 ret = SendMessageA(hTree, WM_SETREDRAW, TRUE, 0);
2241 ok(ret == 0, "got %d\n", ret);
2243 DestroyWindow(hTree);
2246 static void test_cchildren(void)
2248 HWND hTree;
2249 INT ret;
2250 TVITEMA item;
2252 hTree = create_treeview_control(0);
2253 fill_tree(hTree);
2255 ret = SendMessageA(hTree, TVM_DELETEITEM, 0, (LPARAM)hChild);
2256 expect(TRUE, ret);
2258 /* check cChildren - automatic mode */
2259 item.hItem = hRoot;
2260 item.mask = TVIF_CHILDREN;
2262 ret = SendMessageA(hTree, TVM_GETITEMA, 0, (LPARAM)&item);
2263 expect(TRUE, ret);
2264 expect(0, item.cChildren);
2266 DestroyWindow(hTree);
2268 /* start over */
2269 hTree = create_treeview_control(0);
2270 fill_tree(hTree);
2272 /* turn off automatic mode by setting cChildren explicitly */
2273 item.hItem = hRoot;
2274 item.mask = TVIF_CHILDREN;
2276 ret = SendMessageA(hTree, TVM_GETITEMA, 0, (LPARAM)&item);
2277 expect(TRUE, ret);
2278 expect(1, item.cChildren);
2280 ret = SendMessageA(hTree, TVM_SETITEMA, 0, (LPARAM)&item);
2281 expect(TRUE, ret);
2283 ret = SendMessageA(hTree, TVM_DELETEITEM, 0, (LPARAM)hChild);
2284 expect(TRUE, ret);
2286 /* check cChildren */
2287 ret = SendMessageA(hTree, TVM_GETITEMA, 0, (LPARAM)&item);
2288 expect(TRUE, ret);
2289 todo_wine
2290 expect(1, item.cChildren);
2292 DestroyWindow(hTree);
2295 struct _ITEM_DATA
2297 HTREEITEM parent; /* for root value of parent field is unidetified */
2298 HTREEITEM nextsibling;
2299 HTREEITEM firstchild;
2300 void *unk[2];
2301 DWORD unk2;
2302 WORD pad;
2303 WORD width;
2306 struct _ITEM_DATA_V6
2308 HTREEITEM parent; /* for root value of parent field is unidetified */
2309 HTREEITEM nextsibling;
2310 HTREEITEM firstchild;
2311 void *unk[3];
2312 DWORD unk2[2];
2313 WORD pad;
2314 WORD width;
2317 static void _check_item(HWND hwnd, HTREEITEM item, BOOL is_version_6, int line)
2319 struct _ITEM_DATA *data = (struct _ITEM_DATA *)item;
2320 HTREEITEM parent, nextsibling, firstchild, root;
2321 RECT rect;
2322 BOOL ret;
2324 root = (HTREEITEM)SendMessageA(hwnd, TVM_GETNEXTITEM, TVGN_ROOT, (LPARAM)item);
2325 parent = (HTREEITEM)SendMessageA(hwnd, TVM_GETNEXTITEM, TVGN_PARENT, (LPARAM)item);
2326 nextsibling = (HTREEITEM)SendMessageA(hwnd, TVM_GETNEXTITEM, TVGN_NEXT, (LPARAM)item);
2327 firstchild = (HTREEITEM)SendMessageA(hwnd, TVM_GETNEXTITEM, TVGN_CHILD, (LPARAM)item);
2329 *(HTREEITEM*)&rect = item;
2330 ret = SendMessageA(hwnd, TVM_GETITEMRECT, TRUE, (LPARAM)&rect);
2332 ok_(__FILE__, line)(item == root ? data->parent != NULL : data->parent == parent,
2333 "Unexpected parent item %p, got %p, %p\n", parent, data->parent, hwnd);
2334 ok_(__FILE__, line)(data->nextsibling == nextsibling, "Unexpected sibling %p, got %p\n",
2335 nextsibling, data->nextsibling);
2336 ok_(__FILE__, line)(data->firstchild == firstchild, "Unexpected first child %p, got %p\n",
2337 firstchild, data->firstchild);
2338 if (ret)
2340 WORD width;
2342 if (is_version_6)
2344 struct _ITEM_DATA_V6 *data_v6 = (struct _ITEM_DATA_V6 *)item;
2345 width = data_v6->width;
2347 else
2348 width = data->width;
2349 todo_wine
2350 ok_(__FILE__, line)(width == (rect.right - rect.left) || broken(is_version_6 && width == 0) /* XP */,
2351 "Width %d, rect width %ld.\n", width, rect.right - rect.left);
2355 #define CHECK_ITEM(a, b) _check_item(a, b, is_version_6, __LINE__)
2357 static void test_htreeitem_layout(BOOL is_version_6)
2359 TVINSERTSTRUCTA ins;
2360 HTREEITEM item1, item2;
2361 HWND hTree;
2363 hTree = create_treeview_control(0);
2364 fill_tree(hTree);
2366 /* root has some special pointer in parent field */
2367 CHECK_ITEM(hTree, hRoot);
2368 CHECK_ITEM(hTree, hChild);
2370 ins.hParent = hChild;
2371 ins.hInsertAfter = TVI_FIRST;
2372 U(ins).item.mask = 0;
2373 item1 = TreeView_InsertItemA(hTree, &ins);
2375 CHECK_ITEM(hTree, item1);
2377 ins.hParent = hRoot;
2378 ins.hInsertAfter = TVI_FIRST;
2379 U(ins).item.mask = 0;
2380 item2 = TreeView_InsertItemA(hTree, &ins);
2382 CHECK_ITEM(hTree, item2);
2384 SendMessageA(hTree, TVM_DELETEITEM, 0, (LPARAM)hChild);
2386 /* without children now */
2387 CHECK_ITEM(hTree, hRoot);
2389 DestroyWindow(hTree);
2392 static void test_TVS_CHECKBOXES(void)
2394 HIMAGELIST himl, himl2;
2395 HWND hTree, hTree2;
2396 TVITEMA item;
2397 DWORD ret;
2398 MSG msg;
2400 hTree = create_treeview_control(0);
2401 fill_tree(hTree);
2403 himl = (HIMAGELIST)SendMessageA(hTree, TVM_GETIMAGELIST, TVSIL_STATE, 0);
2404 ok(himl == NULL, "got %p\n", himl);
2406 item.hItem = hRoot;
2407 item.mask = TVIF_STATE;
2408 item.state = INDEXTOSTATEIMAGEMASK(1);
2409 item.stateMask = TVIS_STATEIMAGEMASK;
2410 ret = SendMessageA(hTree, TVM_GETITEMA, 0, (LPARAM)&item);
2411 expect(TRUE, ret);
2412 ok(item.state == 0, "got 0x%x\n", item.state);
2414 /* set some index for a child */
2415 item.hItem = hChild;
2416 item.mask = TVIF_STATE;
2417 item.state = INDEXTOSTATEIMAGEMASK(4);
2418 item.stateMask = TVIS_STATEIMAGEMASK;
2419 ret = SendMessageA(hTree, TVM_SETITEMA, 0, (LPARAM)&item);
2420 expect(TRUE, ret);
2422 /* enabling check boxes set all items to 1 state image index */
2423 SetWindowLongA(hTree, GWL_STYLE, GetWindowLongA(hTree, GWL_STYLE) | TVS_CHECKBOXES);
2424 himl = (HIMAGELIST)SendMessageA(hTree, TVM_GETIMAGELIST, TVSIL_STATE, 0);
2425 ok(himl != NULL, "got %p\n", himl);
2427 himl2 = (HIMAGELIST)SendMessageA(hTree, TVM_GETIMAGELIST, TVSIL_STATE, 0);
2428 ok(himl2 != NULL, "got %p\n", himl2);
2429 ok(himl2 == himl, "got %p, expected %p\n", himl2, himl);
2431 item.hItem = hRoot;
2432 item.mask = TVIF_STATE;
2433 item.state = 0;
2434 item.stateMask = TVIS_STATEIMAGEMASK;
2435 ret = SendMessageA(hTree, TVM_GETITEMA, 0, (LPARAM)&item);
2436 expect(TRUE, ret);
2437 ok(item.state == INDEXTOSTATEIMAGEMASK(1), "got 0x%x\n", item.state);
2439 item.hItem = hChild;
2440 item.mask = TVIF_STATE;
2441 item.state = 0;
2442 item.stateMask = TVIS_STATEIMAGEMASK;
2443 ret = SendMessageA(hTree, TVM_GETITEMA, 0, (LPARAM)&item);
2444 expect(TRUE, ret);
2445 ok(item.state == INDEXTOSTATEIMAGEMASK(1), "got 0x%x\n", item.state);
2447 /* create another control and check its checkbox list */
2448 hTree2 = create_treeview_control(0);
2449 fill_tree(hTree2);
2451 /* set some index for a child */
2452 item.hItem = hChild;
2453 item.mask = TVIF_STATE;
2454 item.state = INDEXTOSTATEIMAGEMASK(4);
2455 item.stateMask = TVIS_STATEIMAGEMASK;
2456 ret = SendMessageA(hTree2, TVM_SETITEMA, 0, (LPARAM)&item);
2457 expect(TRUE, ret);
2459 /* enabling check boxes set all items to 1 state image index */
2460 SetWindowLongA(hTree2, GWL_STYLE, GetWindowLongA(hTree, GWL_STYLE) | TVS_CHECKBOXES);
2461 himl2 = (HIMAGELIST)SendMessageA(hTree2, TVM_GETIMAGELIST, TVSIL_STATE, 0);
2462 ok(himl2 != NULL, "got %p\n", himl2);
2463 ok(himl != himl2, "got %p, expected %p\n", himl2, himl);
2465 DestroyWindow(hTree2);
2466 DestroyWindow(hTree);
2468 /* the same, but initially created with TVS_CHECKBOXES */
2469 hTree = create_treeview_control(TVS_CHECKBOXES);
2470 fill_tree(hTree);
2471 himl = (HIMAGELIST)SendMessageA(hTree, TVM_GETIMAGELIST, TVSIL_STATE, 0);
2472 ok(himl == NULL, "got %p\n", himl);
2474 item.hItem = hRoot;
2475 item.mask = TVIF_STATE;
2476 item.state = 0;
2477 item.stateMask = TVIS_STATEIMAGEMASK;
2478 ret = SendMessageA(hTree, TVM_GETITEMA, 0, (LPARAM)&item);
2479 expect(TRUE, ret);
2480 ok(item.state == INDEXTOSTATEIMAGEMASK(1), "got 0x%x\n", item.state);
2482 item.hItem = hChild;
2483 item.mask = TVIF_STATE;
2484 item.state = 0;
2485 item.stateMask = TVIS_STATEIMAGEMASK;
2486 ret = SendMessageA(hTree, TVM_GETITEMA, 0, (LPARAM)&item);
2487 expect(TRUE, ret);
2488 ok(item.state == INDEXTOSTATEIMAGEMASK(1), "got 0x%x\n", item.state);
2490 item.hItem = hChild;
2491 item.mask = TVIF_STATE;
2492 item.state = INDEXTOSTATEIMAGEMASK(2);
2493 item.stateMask = TVIS_STATEIMAGEMASK;
2494 ret = SendMessageA(hTree, TVM_SETITEMA, 0, (LPARAM)&item);
2495 expect(TRUE, ret);
2497 item.hItem = hChild;
2498 item.mask = TVIF_STATE;
2499 item.state = 0;
2500 ret = SendMessageA(hTree, TVM_GETITEMA, 0, (LPARAM)&item);
2501 expect(TRUE, ret);
2502 ok(item.state == INDEXTOSTATEIMAGEMASK(2), "got 0x%x\n", item.state);
2504 while(GetMessageA(&msg, 0, 0, 0))
2506 TranslateMessage(&msg);
2507 DispatchMessageA(&msg);
2509 if((msg.hwnd == hTree) && (msg.message == WM_PAINT))
2510 break;
2513 item.hItem = hChild;
2514 item.mask = TVIF_STATE;
2515 item.state = 0;
2516 ret = SendMessageA(hTree, TVM_GETITEMA, 0, (LPARAM)&item);
2517 expect(TRUE, ret);
2518 ok(item.state == INDEXTOSTATEIMAGEMASK(1), "got 0x%x\n", item.state);
2520 himl = (HIMAGELIST)SendMessageA(hTree, TVM_GETIMAGELIST, TVSIL_STATE, 0);
2521 ok(himl != NULL, "got %p\n", himl);
2523 DestroyWindow(hTree);
2525 /* check what happens if TVSIL_STATE image list is removed */
2526 hTree = create_treeview_control(0);
2527 fill_tree(hTree);
2528 himl = (HIMAGELIST)SendMessageA(hTree, TVM_GETIMAGELIST, TVSIL_STATE, 0);
2529 ok(himl == NULL, "got %p\n", himl);
2531 SetWindowLongA(hTree, GWL_STYLE, GetWindowLongA(hTree, GWL_STYLE) | TVS_CHECKBOXES);
2532 himl = (HIMAGELIST)SendMessageA(hTree, TVM_GETIMAGELIST, TVSIL_STATE, 0);
2533 ok(himl != NULL, "got %p\n", himl);
2535 himl2 = (HIMAGELIST)SendMessageA(hTree, TVM_SETIMAGELIST, TVSIL_STATE, 0);
2536 ok(himl2 == himl, "got %p\n", himl2);
2538 himl2 = (HIMAGELIST)SendMessageA(hTree, TVM_GETIMAGELIST, TVSIL_STATE, 0);
2539 ok(himl2 == NULL, "got %p\n", himl2);
2541 item.hItem = hChild;
2542 item.mask = TVIF_STATE;
2543 item.state = INDEXTOSTATEIMAGEMASK(2);
2544 item.stateMask = TVIS_STATEIMAGEMASK;
2545 ret = SendMessageA(hTree, TVM_SETITEMA, 0, (LPARAM)&item);
2546 expect(TRUE, ret);
2548 item.hItem = hChild;
2549 item.mask = TVIF_STATE;
2550 item.state = 0;
2551 ret = SendMessageA(hTree, TVM_GETITEMA, 0, (LPARAM)&item);
2552 expect(TRUE, ret);
2553 ok(item.state == INDEXTOSTATEIMAGEMASK(2), "got 0x%x\n", item.state);
2555 while(GetMessageA(&msg, 0, 0, 0))
2557 TranslateMessage(&msg);
2558 DispatchMessageA(&msg);
2560 if((msg.hwnd == hTree) && (msg.message == WM_PAINT))
2561 break;
2564 item.hItem = hChild;
2565 item.mask = TVIF_STATE;
2566 item.state = 0;
2567 ret = SendMessageA(hTree, TVM_GETITEMA, 0, (LPARAM)&item);
2568 expect(TRUE, ret);
2569 ok(item.state == INDEXTOSTATEIMAGEMASK(1), "got 0x%x\n", item.state);
2571 himl = (HIMAGELIST)SendMessageA(hTree, TVM_GETIMAGELIST, TVSIL_STATE, 0);
2572 ok(himl != NULL, "got %p\n", himl);
2574 DestroyWindow(hTree);
2577 static void test_TVM_GETNEXTITEM(void)
2579 HTREEITEM item;
2580 HWND hTree;
2582 hTree = create_treeview_control(0);
2583 fill_tree(hTree);
2585 item = (HTREEITEM)SendMessageA(hTree, TVM_GETNEXTITEM, TVGN_ROOT, 0);
2586 ok(item == hRoot, "got %p, expected %p\n", item, hRoot);
2588 item = (HTREEITEM)SendMessageA(hTree, TVM_GETNEXTITEM, TVGN_ROOT, (LPARAM)TVI_ROOT);
2589 ok(item == hRoot, "got %p, expected %p\n", item, hRoot);
2591 item = (HTREEITEM)SendMessageA(hTree, TVM_GETNEXTITEM, TVGN_ROOT, (LPARAM)hRoot);
2592 ok(item == hRoot, "got %p, expected %p\n", item, hRoot);
2594 item = (HTREEITEM)SendMessageA(hTree, TVM_GETNEXTITEM, TVGN_ROOT, (LPARAM)hChild);
2595 ok(item == hRoot, "got %p, expected %p\n", item, hRoot);
2597 item = (HTREEITEM)SendMessageA(hTree, TVM_GETNEXTITEM, TVGN_CHILD, 0);
2598 ok(item == hRoot, "got %p, expected %p\n", item, hRoot);
2600 item = (HTREEITEM)SendMessageA(hTree, TVM_GETNEXTITEM, TVGN_CHILD, (LPARAM)hRoot);
2601 ok(item == hChild, "got %p, expected %p\n", item, hChild);
2603 item = (HTREEITEM)SendMessageA(hTree, TVM_GETNEXTITEM, TVGN_CHILD, (LPARAM)TVI_ROOT);
2604 ok(item == hRoot, "got %p, expected %p\n", item, hRoot);
2606 item = (HTREEITEM)SendMessageA(hTree, TVM_GETNEXTITEM, TVGN_PARENT, 0);
2607 ok(item == NULL, "got %p\n", item);
2609 item = (HTREEITEM)SendMessageA(hTree, TVM_GETNEXTITEM, TVGN_PARENT, (LPARAM)hChild);
2610 ok(item == hRoot, "got %p, expected %p\n", item, hRoot);
2612 DestroyWindow(hTree);
2615 static void test_TVM_HITTEST(void)
2617 HWND hTree;
2618 LRESULT ret;
2619 RECT rc;
2620 TVHITTESTINFO ht;
2622 hTree = create_treeview_control(0);
2623 fill_tree(hTree);
2625 *(HTREEITEM*)&rc = hRoot;
2626 ret = SendMessageA(hTree, TVM_GETITEMRECT, TRUE, (LPARAM)&rc);
2627 expect(TRUE, (BOOL)ret);
2629 ht.pt.x = rc.left-1;
2630 ht.pt.y = rc.top;
2632 ret = SendMessageA(hTree, TVM_HITTEST, 0, (LPARAM)&ht);
2633 ok((HTREEITEM)ret == hRoot, "got %p, expected %p\n", (HTREEITEM)ret, hRoot);
2634 ok(ht.hItem == hRoot, "got %p, expected %p\n", ht.hItem, hRoot);
2635 ok(ht.flags == TVHT_ONITEMBUTTON, "got %d, expected %d\n", ht.flags, TVHT_ONITEMBUTTON);
2637 ret = SendMessageA(hTree, TVM_EXPAND, TVE_EXPAND, (LPARAM)hRoot);
2638 expect(TRUE, (BOOL)ret);
2640 *(HTREEITEM*)&rc = hChild;
2641 ret = SendMessageA(hTree, TVM_GETITEMRECT, TRUE, (LPARAM)&rc);
2642 expect(TRUE, (BOOL)ret);
2644 ht.pt.x = rc.left-1;
2645 ht.pt.y = rc.top;
2647 ret = SendMessageA(hTree, TVM_HITTEST, 0, (LPARAM)&ht);
2648 ok((HTREEITEM)ret == hChild, "got %p, expected %p\n", (HTREEITEM)ret, hChild);
2649 ok(ht.hItem == hChild, "got %p, expected %p\n", ht.hItem, hChild);
2650 /* Wine returns item button here, but this item has no button */
2651 todo_wine ok(ht.flags == TVHT_ONITEMINDENT, "got %d, expected %d\n", ht.flags, TVHT_ONITEMINDENT);
2653 DestroyWindow(hTree);
2656 static void test_WM_GETDLGCODE(void)
2658 DWORD code;
2659 HWND hTree;
2661 hTree = create_treeview_control(0);
2663 code = SendMessageA(hTree, WM_GETDLGCODE, VK_TAB, 0);
2664 ok(code == (DLGC_WANTCHARS | DLGC_WANTARROWS), "0x%08lx\n", code);
2666 DestroyWindow(hTree);
2669 static void test_customdraw(void)
2671 LOGFONTA lf;
2672 HWND hwnd;
2674 hwnd = create_treeview_control(0);
2675 fill_tree(hwnd);
2676 SendMessageA(hwnd, TVM_EXPAND, TVE_EXPAND, (WPARAM)hRoot);
2678 /* create additional font, custom draw handler will select it */
2679 SystemParametersInfoA(SPI_GETICONTITLELOGFONT, sizeof(lf), &lf, 0);
2680 lf.lfHeight *= 2;
2681 g_customdraw_font = CreateFontIndirectA(&lf);
2682 flush_sequences(sequences, NUM_MSG_SEQUENCES);
2683 InvalidateRect(hwnd, NULL, TRUE);
2684 UpdateWindow(hwnd);
2685 ok_sequence(sequences, PARENT_CD_SEQ_INDEX, parent_cd_seq, "custom draw notifications", FALSE);
2686 DeleteObject(g_customdraw_font);
2687 g_customdraw_font = NULL;
2689 DestroyWindow(hwnd);
2692 static void test_WM_KEYDOWN(void)
2694 static const char *rootA = "root";
2695 TVINSERTSTRUCTA ins;
2696 HTREEITEM hRoot;
2697 HWND hwnd;
2699 hwnd = create_treeview_control(0);
2701 ins.hParent = TVI_ROOT;
2702 ins.hInsertAfter = TVI_ROOT;
2703 U(ins).item.mask = TVIF_TEXT;
2704 U(ins).item.pszText = (char*)rootA;
2705 hRoot = TreeView_InsertItemA(hwnd, &ins);
2706 ok(hRoot != NULL, "got %p\n", hRoot);
2708 flush_sequences(sequences, NUM_MSG_SEQUENCES);
2709 SendMessageA(hwnd, WM_KEYDOWN, VK_RETURN, 0);
2710 ok_sequence(sequences, PARENT_SEQ_INDEX, parent_vk_return_seq, "WM_KEYDOWN/VK_RETURN parent notification", TRUE);
2712 DestroyWindow(hwnd);
2715 static void test_TVS_FULLROWSELECT(void)
2717 DWORD style;
2718 HWND hwnd;
2720 /* try to create both with TVS_HASLINES and TVS_FULLROWSELECT */
2721 hwnd = create_treeview_control(TVS_FULLROWSELECT);
2723 style = GetWindowLongA(hwnd, GWL_STYLE);
2724 ok((style & (TVS_FULLROWSELECT | TVS_HASLINES)) == (TVS_FULLROWSELECT | TVS_HASLINES), "got style 0x%08lx\n", style);
2726 DestroyWindow(hwnd);
2728 /* create just with TVS_HASLINES, try to enable TVS_FULLROWSELECT later */
2729 hwnd = create_treeview_control(0);
2731 style = GetWindowLongA(hwnd, GWL_STYLE);
2732 SetWindowLongA(hwnd, GWL_STYLE, style | TVS_FULLROWSELECT);
2733 style = GetWindowLongA(hwnd, GWL_STYLE);
2734 ok(style & TVS_FULLROWSELECT, "got style 0x%08lx\n", style);
2736 DestroyWindow(hwnd);
2739 static void get_item_names_string(HWND hwnd, HTREEITEM item, char *str)
2741 TVITEMA tvitem = { 0 };
2742 HTREEITEM child;
2743 char name[16];
2745 if (!item)
2747 item = (HTREEITEM)SendMessageA(hwnd, TVM_GETNEXTITEM, TVGN_ROOT, 0);
2748 str[0] = 0;
2751 child = (HTREEITEM)SendMessageA(hwnd, TVM_GETNEXTITEM, TVGN_CHILD, (LPARAM)item);
2753 tvitem.mask = TVIF_TEXT;
2754 tvitem.hItem = item;
2755 tvitem.pszText = name;
2756 tvitem.cchTextMax = sizeof(name);
2757 SendMessageA(hwnd, TVM_GETITEMA, 0, (LPARAM)&tvitem);
2758 strcat(str, tvitem.pszText);
2760 while (child != NULL)
2762 get_item_names_string(hwnd, child, str);
2763 child = (HTREEITEM)SendMessageA(hwnd, TVM_GETNEXTITEM, TVGN_NEXT, (LPARAM)child);
2767 static void fill_treeview_sort_test(HWND hwnd)
2769 static const char *itemnames[] =
2771 "root", "Wasp", "Caribou", "Vacuum",
2772 "Ocelot", "Newspaper", "Litter bin"
2775 HTREEITEM root, children[2];
2776 TVINSERTSTRUCTA ins;
2777 unsigned i = 0;
2779 SendMessageA(hwnd, TVM_DELETEITEM, 0, 0);
2781 /* root, two children, with two children each */
2782 ins.hParent = TVI_ROOT;
2783 ins.hInsertAfter = TVI_ROOT;
2784 U(ins).item.mask = TVIF_TEXT;
2785 U(ins).item.pszText = (char *)itemnames[i++];
2786 root = (HTREEITEM)SendMessageA(hwnd, TVM_INSERTITEMA, 0, (LPARAM)&ins);
2788 ins.hParent = root;
2789 ins.hInsertAfter = TVI_LAST;
2790 U(ins).item.mask = TVIF_TEXT;
2791 U(ins).item.pszText = (char *)itemnames[i++];
2792 children[0] = (HTREEITEM)SendMessageA(hwnd, TVM_INSERTITEMA, 0, (LPARAM)&ins);
2794 U(ins).item.pszText = (char *)itemnames[i++];
2795 children[1] = (HTREEITEM)SendMessageA(hwnd, TVM_INSERTITEMA, 0, (LPARAM)&ins);
2797 ins.hParent = children[0];
2798 U(ins).item.pszText = (char *)itemnames[i++];
2799 SendMessageA(hwnd, TVM_INSERTITEMA, 0, (LPARAM)&ins);
2801 U(ins).item.pszText = (char *)itemnames[i++];
2802 SendMessageA(hwnd, TVM_INSERTITEMA, 0, (LPARAM)&ins);
2804 ins.hParent = children[1];
2805 U(ins).item.pszText = (char *)itemnames[i++];
2806 SendMessageA(hwnd, TVM_INSERTITEMA, 0, (LPARAM)&ins);
2808 U(ins).item.pszText = (char *)itemnames[i++];
2809 SendMessageA(hwnd, TVM_INSERTITEMA, 0, (LPARAM)&ins);
2812 static void test_TVM_SORTCHILDREN(void)
2814 static const char *initial_order = "rootWaspVacuumOcelotCaribouNewspaperLitter bin";
2815 static const char *sorted_order = "rootCaribouNewspaperLitter binWaspVacuumOcelot";
2816 TVINSERTSTRUCTA ins;
2817 char buff[256];
2818 HTREEITEM root;
2819 HWND hwnd;
2820 BOOL ret;
2822 hwnd = create_treeview_control(0);
2824 /* call on empty tree */
2825 ret = SendMessageA(hwnd, TVM_SORTCHILDREN, 0, 0);
2826 ok(!ret, "Unexpected ret value %d\n", ret);
2828 ret = SendMessageA(hwnd, TVM_SORTCHILDREN, 0, (LPARAM)TVI_ROOT);
2829 ok(!ret, "Unexpected ret value %d\n", ret);
2831 /* add only root, sort from it */
2832 ins.hParent = TVI_ROOT;
2833 ins.hInsertAfter = TVI_ROOT;
2834 U(ins).item.mask = TVIF_TEXT;
2835 U(ins).item.pszText = (char *)"root";
2836 root = (HTREEITEM)SendMessageA(hwnd, TVM_INSERTITEMA, 0, (LPARAM)&ins);
2837 ok(root != NULL, "Expected root node\n");
2839 ret = SendMessageA(hwnd, TVM_SORTCHILDREN, 0, (LPARAM)root);
2840 ok(!ret, "Unexpected ret value %d\n", ret);
2842 ret = SendMessageA(hwnd, TVM_SORTCHILDREN, TRUE, (LPARAM)root);
2843 ok(!ret, "Unexpected ret value %d\n", ret);
2845 /* root, two children, with two children each */
2846 fill_treeview_sort_test(hwnd);
2847 get_item_names_string(hwnd, NULL, buff);
2848 ok(!strcmp(buff, initial_order), "Wrong initial order %s, expected %s\n", buff, initial_order);
2850 /* with NULL item nothing is sorted */
2851 fill_treeview_sort_test(hwnd);
2852 ret = SendMessageA(hwnd, TVM_SORTCHILDREN, 0, 0);
2853 todo_wine
2854 ok(ret, "Unexpected ret value %d\n", ret);
2855 get_item_names_string(hwnd, NULL, buff);
2856 ok(!strcmp(buff, initial_order), "Wrong sorted order %s, expected %s\n", buff, initial_order);
2858 /* TVI_ROOT as item */
2859 fill_treeview_sort_test(hwnd);
2860 ret = SendMessageA(hwnd, TVM_SORTCHILDREN, 0, (LPARAM)TVI_ROOT);
2861 todo_wine
2862 ok(ret, "Unexpected ret value %d\n", ret);
2863 get_item_names_string(hwnd, NULL, buff);
2864 ok(!strcmp(buff, initial_order), "Wrong sorted order %s, expected %s\n", buff, initial_order);
2866 /* zero WPARAM, item is specified */
2867 fill_treeview_sort_test(hwnd);
2868 root = (HTREEITEM)SendMessageA(hwnd, TVM_GETNEXTITEM, TVGN_ROOT, 0);
2869 ok(root != NULL, "Failed to get root item\n");
2870 ret = SendMessageA(hwnd, TVM_SORTCHILDREN, 0, (LPARAM)root);
2871 ok(ret, "Unexpected ret value %d\n", ret);
2872 get_item_names_string(hwnd, NULL, buff);
2873 ok(!strcmp(buff, sorted_order), "Wrong sorted order %s, expected %s\n", buff, sorted_order);
2875 /* non-zero WPARAM, NULL item */
2876 fill_treeview_sort_test(hwnd);
2877 ret = SendMessageA(hwnd, TVM_SORTCHILDREN, TRUE, 0);
2878 todo_wine
2879 ok(ret, "Unexpected ret value %d\n", ret);
2880 get_item_names_string(hwnd, NULL, buff);
2881 ok(!strcmp(buff, initial_order), "Wrong sorted order %s, expected %s\n", buff, sorted_order);
2883 /* TVI_ROOT as item */
2884 fill_treeview_sort_test(hwnd);
2885 ret = SendMessageA(hwnd, TVM_SORTCHILDREN, TRUE, (LPARAM)TVI_ROOT);
2886 todo_wine
2887 ok(ret, "Unexpected ret value %d\n", ret);
2888 get_item_names_string(hwnd, NULL, buff);
2889 ok(!strcmp(buff, initial_order), "Wrong sorted order %s, expected %s\n", buff, sorted_order);
2891 /* non-zero WPARAM, item is specified */
2892 fill_treeview_sort_test(hwnd);
2893 root = (HTREEITEM)SendMessageA(hwnd, TVM_GETNEXTITEM, TVGN_ROOT, 0);
2894 ok(root != NULL, "Failed to get root item\n");
2895 ret = SendMessageA(hwnd, TVM_SORTCHILDREN, TRUE, (LPARAM)root);
2896 ok(ret, "Unexpected ret value %d\n", ret);
2897 get_item_names_string(hwnd, NULL, buff);
2898 ok(!strcmp(buff, sorted_order), "Wrong sorted order %s, expected %s\n", buff, sorted_order);
2900 /* case insensitive comparison */
2901 SendMessageA(hwnd, TVM_DELETEITEM, 0, 0);
2903 ins.hParent = TVI_ROOT;
2904 ins.hInsertAfter = TVI_ROOT;
2905 U(ins).item.mask = TVIF_TEXT;
2906 U(ins).item.pszText = (char *)"root";
2907 root = (HTREEITEM)SendMessageA(hwnd, TVM_INSERTITEMA, 0, (LPARAM)&ins);
2908 ok(root != NULL, "Expected root node\n");
2910 ins.hParent = root;
2911 ins.hInsertAfter = TVI_LAST;
2912 U(ins).item.pszText = (char *)"I1";
2913 SendMessageA(hwnd, TVM_INSERTITEMA, 0, (LPARAM)&ins);
2915 ins.hParent = root;
2916 ins.hInsertAfter = TVI_LAST;
2917 U(ins).item.pszText = (char *)"i1";
2918 SendMessageA(hwnd, TVM_INSERTITEMA, 0, (LPARAM)&ins);
2920 ret = SendMessageA(hwnd, TVM_SORTCHILDREN, TRUE, (LPARAM)root);
2921 ok(ret, "Unexpected ret value %d\n", ret);
2922 get_item_names_string(hwnd, NULL, buff);
2923 ok(!strcmp(buff, "rootI1i1"), "Wrong sorted order %s\n", buff);
2925 DestroyWindow(hwnd);
2928 static void test_right_click(void)
2930 HWND hTree;
2931 HTREEITEM selected;
2932 RECT rc;
2933 LRESULT result;
2934 POINT pt, orig_pos;
2936 hTree = create_treeview_control(0);
2937 fill_tree(hTree);
2939 SendMessageA(hTree, TVM_ENSUREVISIBLE, 0, (LPARAM)hChild);
2940 SendMessageA(hTree, TVM_SELECTITEM, TVGN_CARET, (LPARAM)hChild);
2941 selected = (HTREEITEM)SendMessageA(hTree, TVM_GETNEXTITEM, TVGN_CARET, 0);
2942 ok(selected == hChild, "child item not selected\n");
2944 *(HTREEITEM *)&rc = hRoot;
2945 result = SendMessageA(hTree, TVM_GETITEMRECT, TRUE, (LPARAM)&rc);
2946 ok(result, "TVM_GETITEMRECT failed\n");
2948 flush_events();
2950 pt.x = (rc.left + rc.right) / 2;
2951 pt.y = (rc.top + rc.bottom) / 2;
2952 ClientToScreen(hMainWnd, &pt);
2953 GetCursorPos(&orig_pos);
2954 SetCursorPos(pt.x, pt.y);
2956 flush_events();
2957 flush_sequences(sequences, NUM_MSG_SEQUENCES);
2959 ScreenToClient(hTree, &pt);
2960 PostMessageA(hTree, WM_RBUTTONDOWN, MK_RBUTTON, MAKELPARAM(pt.x, pt.y));
2961 PostMessageA(hTree, WM_RBUTTONUP, 0, MAKELPARAM(pt.x, pt.y));
2963 flush_events();
2965 ok_sequence(sequences, TREEVIEW_SEQ_INDEX, test_right_click_seq, "right click sequence", FALSE);
2966 ok_sequence(sequences, PARENT_SEQ_INDEX, parent_right_click_seq, "parent right click sequence", FALSE);
2968 selected = (HTREEITEM)SendMessageA(hTree, TVM_GETNEXTITEM, TVGN_CARET, 0);
2969 ok(selected == hChild, "child item should still be selected\n");
2971 SetCursorPos(orig_pos.x, orig_pos.y);
2972 DestroyWindow(hTree);
2975 static void init_functions(void)
2977 HMODULE hComCtl32 = LoadLibraryA("comctl32.dll");
2978 HMODULE hUxtheme = LoadLibraryA("uxtheme.dll");
2980 #define X(module, f) p##f = (void*)GetProcAddress(module, #f);
2981 X(hComCtl32, InitCommonControlsEx);
2983 X(hUxtheme, GetWindowTheme);
2984 X(hUxtheme, IsThemeBackgroundPartiallyTransparent);
2985 #undef X
2988 START_TEST(treeview)
2990 INITCOMMONCONTROLSEX iccex;
2991 ULONG_PTR ctx_cookie;
2992 HANDLE hCtx;
2993 WNDCLASSA wc;
2995 init_functions();
2997 iccex.dwSize = sizeof(iccex);
2998 iccex.dwICC = ICC_TREEVIEW_CLASSES;
2999 pInitCommonControlsEx(&iccex);
3001 init_msg_sequences(sequences, NUM_MSG_SEQUENCES);
3002 init_msg_sequences(item_sequence, 1);
3004 wc.style = CS_HREDRAW | CS_VREDRAW;
3005 wc.cbClsExtra = 0;
3006 wc.cbWndExtra = 0;
3007 wc.hInstance = GetModuleHandleA(NULL);
3008 wc.hIcon = NULL;
3009 wc.hCursor = LoadCursorA(NULL, (LPCSTR)IDC_IBEAM);
3010 wc.hbrBackground = GetSysColorBrush(COLOR_WINDOW);
3011 wc.lpszMenuName = NULL;
3012 wc.lpszClassName = "MyTestWnd";
3013 wc.lpfnWndProc = parent_wnd_proc;
3014 RegisterClassA(&wc);
3016 hMainWnd = CreateWindowExA(0, "MyTestWnd", "Blah", WS_OVERLAPPEDWINDOW,
3017 CW_USEDEFAULT, CW_USEDEFAULT, 130, 105, NULL, NULL, GetModuleHandleA(NULL), 0);
3019 ok(hMainWnd != NULL, "Failed to create parent window. Tests aborted.\n");
3020 if (!hMainWnd) return;
3022 test_fillroot();
3023 test_select();
3024 test_getitemtext();
3025 test_focus();
3026 test_get_set_bkcolor();
3027 test_get_set_imagelist();
3028 test_get_set_indent();
3029 test_get_set_insertmark();
3030 test_get_set_item();
3031 test_get_set_itemheight();
3032 test_get_set_scrolltime();
3033 test_get_set_textcolor();
3034 test_get_linecolor();
3035 test_get_insertmarkcolor();
3036 test_get_set_tooltips();
3037 test_get_set_unicodeformat();
3038 test_callback();
3039 test_expandinvisible();
3040 test_itemedit();
3041 test_treeview_classinfo();
3042 test_expandnotify();
3043 test_TVS_SINGLEEXPAND();
3044 test_WM_PAINT();
3045 test_delete_items();
3046 test_cchildren();
3047 test_htreeitem_layout(FALSE);
3048 test_TVS_CHECKBOXES();
3049 test_TVM_GETNEXTITEM();
3050 test_TVM_HITTEST();
3051 test_WM_GETDLGCODE();
3052 test_customdraw();
3053 test_WM_KEYDOWN();
3054 test_TVS_FULLROWSELECT();
3055 test_TVM_SORTCHILDREN();
3056 test_right_click();
3058 if (!load_v6_module(&ctx_cookie, &hCtx))
3060 DestroyWindow(hMainWnd);
3061 return;
3064 /* comctl32 version 6 tests start here */
3065 g_v6 = TRUE;
3067 test_fillroot();
3068 test_getitemtext();
3069 test_get_set_insertmark();
3070 test_get_set_item();
3071 test_get_set_scrolltime();
3072 test_get_set_textcolor();
3073 test_get_linecolor();
3074 test_get_insertmarkcolor();
3075 test_expandedimage();
3076 test_get_set_tooltips();
3077 test_get_set_unicodeformat();
3078 test_expandinvisible();
3079 test_expand();
3080 test_itemedit();
3081 test_treeview_classinfo();
3082 test_delete_items();
3083 test_cchildren();
3084 test_htreeitem_layout(TRUE);
3085 test_TVM_GETNEXTITEM();
3086 test_TVM_HITTEST();
3087 test_WM_GETDLGCODE();
3088 test_customdraw();
3089 test_WM_KEYDOWN();
3090 test_TVS_FULLROWSELECT();
3091 test_TVM_SORTCHILDREN();
3093 unload_v6_module(ctx_cookie, hCtx);