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