treeview.c 89.4 KB
Newer Older
1 2 3
/* Unit tests for treeview.
 *
 * Copyright 2005 Krzysztof Foltman
4
 * Copyright 2007 Christopher James Peterson
5 6 7 8 9 10 11 12 13 14 15 16 17
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * 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 25 26 27 28 29 30 31 32

#include "windef.h"
#include "winbase.h"
#include "wingdi.h"
#include "winuser.h"
#include "winnls.h"
#include "winreg.h"
#include "commctrl.h" 

#include "wine/test.h"
33
#include "v6util.h"
34 35
#include "msg.h"

36
static const char *TEST_CALLBACK_TEXT = "callback_text";
37

38 39 40 41
static TVITEMA g_item_expanding, g_item_expanded;
static BOOL g_get_from_expand;
static BOOL g_get_rect_in_expand;
static BOOL g_disp_A_to_W;
42
static BOOL g_disp_set_stateimage;
43
static BOOL g_beginedit_alter_text;
44
static HFONT g_customdraw_font;
45
static BOOL g_v6;
46

47
#define NUM_MSG_SEQUENCES   3
48
#define TREEVIEW_SEQ_INDEX  0
49
#define PARENT_SEQ_INDEX    1
50
#define PARENT_CD_SEQ_INDEX 2
51

52 53
#define expect(expected, got) ok(got == expected, "Expected %d, got %d\n", expected, got)

54
static struct msg_sequence *sequences[NUM_MSG_SEQUENCES];
55
static struct msg_sequence *item_sequence[1];
56 57

static const struct message FillRootSeq[] = {
58 59
    { TVM_INSERTITEMA, sent },
    { TVM_INSERTITEMA, sent },
60 61 62
    { 0 }
};

63 64 65 66 67 68 69
static const struct message rootnone_select_seq[] = {
    { TVM_SELECTITEM, sent|wparam, 9 },
    { TVM_SELECTITEM, sent|wparam, 9 },
    { TVM_SELECTITEM, sent|wparam, 9 },
    { TVM_SELECTITEM, sent|wparam, 9 },
    { TVM_SELECTITEM, sent|wparam, 9 },
    { TVM_SELECTITEM, sent|wparam, 9 },
70 71 72
    { 0 }
};

73 74 75 76 77 78 79
static const struct message rootchild_select_seq[] = {
    { TVM_SELECTITEM, sent|wparam, 9 },
    { TVM_SELECTITEM, sent|wparam, 9 },
    { TVM_SELECTITEM, sent|wparam, 9 },
    { TVM_SELECTITEM, sent|wparam, 9 },
    { TVM_SELECTITEM, sent|wparam, 9 },
    { TVM_SELECTITEM, sent|wparam, 9 },
80 81 82
    { 0 }
};

83
static const struct message getitemtext_seq[] = {
84 85
    { TVM_INSERTITEMA, sent },
    { TVM_GETITEMA, sent },
86 87 88 89
    { TVM_DELETEITEM, sent },
    { 0 }
};

90
static const struct message focus_seq[] = {
91 92
    { TVM_INSERTITEMA, sent },
    { TVM_INSERTITEMA, sent },
93 94
    { TVM_SELECTITEM, sent|wparam, 9 },
    /* The following end up out of order in wine */
95
    { WM_WINDOWPOSCHANGING, sent|defwinproc },
96
    { WM_NCCALCSIZE, sent|wparam|defwinproc, TRUE },
97 98
    { WM_WINDOWPOSCHANGED, sent|defwinproc },
    { WM_SIZE, sent|defwinproc },
99 100 101 102
    { WM_WINDOWPOSCHANGING, sent|defwinproc|optional },
    { WM_NCCALCSIZE, sent|wparam|defwinproc|optional, TRUE },
    { WM_WINDOWPOSCHANGED, sent|defwinproc|optional },
    { WM_SIZE, sent|defwinproc|optional },
103
    { WM_PAINT, sent|defwinproc },
104
    { WM_NCPAINT, sent|wparam|defwinproc, 1 },
105
    { WM_ERASEBKGND, sent|defwinproc },
106
    { TVM_EDITLABELA, sent },
107 108 109
    { WM_COMMAND, sent|wparam|defwinproc, MAKEWPARAM(0, EN_UPDATE) },
    { WM_COMMAND, sent|wparam|defwinproc, MAKEWPARAM(0, EN_CHANGE) },
    { WM_PARENTNOTIFY, sent|wparam|defwinproc, MAKEWPARAM(WM_CREATE, 0) },
110 111
    { WM_KILLFOCUS, sent|defwinproc },
    { WM_PAINT, sent|defwinproc },
112
    { WM_IME_SETCONTEXT, sent|defwinproc|optional },
113
    { WM_COMMAND, sent|wparam|defwinproc, MAKEWPARAM(0, EN_SETFOCUS) },
114
    { WM_ERASEBKGND, sent|defwinproc|optional },
115 116
    { WM_CTLCOLOREDIT, sent|defwinproc|optional },
    { WM_CTLCOLOREDIT, sent|defwinproc|optional },
117 118 119
    { 0 }
};

120
static const struct message test_get_set_bkcolor_seq[] = {
121 122 123 124 125 126
    { TVM_GETBKCOLOR, sent|wparam|lparam, 0, 0 },
    { TVM_SETBKCOLOR, sent|wparam|lparam, 0, 0 },
    { TVM_GETBKCOLOR, sent|wparam|lparam, 0, 0 },
    { TVM_SETBKCOLOR, sent|wparam|lparam, 0, 0x00ffffff },
    { TVM_GETBKCOLOR, sent|wparam|lparam, 0, 0 },
    { TVM_SETBKCOLOR, sent|wparam|lparam, 0, -1 },
127 128 129
    { 0 }
};

130
static const struct message test_get_set_imagelist_seq[] = {
131 132
    { TVM_SETIMAGELIST, sent|wparam|lparam, 0, 0 },
    { TVM_GETIMAGELIST, sent|wparam|lparam, 0, 0 },
133 134 135
    { 0 }
};

136
static const struct message test_get_set_indent_seq[] = {
137 138
    { TVM_SETINDENT, sent|wparam|lparam, 0, 0 },
    { TVM_GETINDENT, sent|wparam|lparam, 0, 0 },
139 140
    /* The actual amount to indent is dependent on the system for this message */
    { TVM_SETINDENT, sent },
141
    { TVM_GETINDENT, sent|wparam|lparam, 0, 0 },
142 143 144
    { 0 }
};

145
static const struct message test_get_set_insertmarkcolor_seq[] = {
146 147
    { TVM_SETINSERTMARKCOLOR, sent|wparam|lparam, 0, 0 },
    { TVM_GETINSERTMARKCOLOR, sent|wparam|lparam, 0, 0 },
148 149 150
    { 0 }
};

151
static const struct message test_get_set_item_seq[] = {
152 153 154 155
    { TVM_GETITEMA, sent },
    { TVM_SETITEMA, sent },
    { TVM_GETITEMA, sent },
    { TVM_SETITEMA, sent },
156 157 158
    { 0 }
};

159
static const struct message test_get_set_itemheight_seq[] = {
160 161 162 163 164 165 166
    { TVM_GETITEMHEIGHT, sent|wparam|lparam, 0, 0 },
    { TVM_SETITEMHEIGHT, sent|wparam|lparam, -1, 0 },
    { TVM_GETITEMHEIGHT, sent|wparam|lparam, 0, 0 },
    { TVM_SETITEMHEIGHT, sent|lparam, 0xcccccccc, 0 },
    { TVM_GETITEMHEIGHT, sent|wparam|lparam|optional, 0, 0 },
    { TVM_SETITEMHEIGHT, sent|wparam|lparam|optional, 9, 0 },
    { TVM_GETITEMHEIGHT, sent|wparam|lparam, 0, 0 },
167 168 169
    { 0 }
};

170
static const struct message test_get_set_scrolltime_seq[] = {
171 172
    { TVM_SETSCROLLTIME, sent|wparam|lparam, 20, 0 },
    { TVM_GETSCROLLTIME, sent|wparam|lparam, 0, 0 },
173 174 175
    { 0 }
};

176
static const struct message test_get_set_textcolor_seq[] = {
177 178 179
    { TVM_GETTEXTCOLOR, sent|wparam|lparam, 0, 0 },
    { TVM_SETTEXTCOLOR, sent|wparam|lparam, 0, 0 },
    { TVM_GETTEXTCOLOR, sent|wparam|lparam, 0, 0 },
180
    { TVM_SETTEXTCOLOR, sent|wparam|lparam, 0, RGB(255, 255, 255) },
181
    { TVM_GETTEXTCOLOR, sent|wparam|lparam, 0, 0 },
182
    { TVM_SETTEXTCOLOR, sent|wparam|lparam, 0, CLR_NONE },
183 184 185
    { 0 }
};

186
static const struct message test_get_set_tooltips_seq[] = {
187
    { WM_KILLFOCUS,    sent },
188 189
    { WM_IME_SETCONTEXT, sent|optional },
    { WM_IME_NOTIFY, sent|optional },
190 191
    { TVM_SETTOOLTIPS, sent|wparam|lparam, 0, 0 },
    { TVM_GETTOOLTIPS, sent|wparam|lparam, 0, 0 },
192 193 194
    { 0 }
};

195
static const struct message test_get_set_unicodeformat_seq[] = {
196 197 198 199 200
    { TVM_SETUNICODEFORMAT, sent|wparam|lparam, TRUE, 0 },
    { TVM_GETUNICODEFORMAT, sent|wparam|lparam, 0, 0 },
    { TVM_SETUNICODEFORMAT, sent|wparam|lparam, 0, 0 },
    { TVM_GETUNICODEFORMAT, sent|wparam|lparam, 0, 0 },
    { TVM_SETUNICODEFORMAT, sent|wparam|lparam, 0, 0 },
201 202
    { 0 }
};
203

204
static const struct message parent_expand_seq[] = {
205 206
    { WM_NOTIFY, sent|id, 0, 0, TVN_ITEMEXPANDINGA },
    { WM_NOTIFY, sent|id, 0, 0, TVN_ITEMEXPANDEDA },
207 208 209
    { 0 }
};

210
static const struct message parent_expand_kb_seq[] = {
211
    { WM_NOTIFY, sent|id, 0, 0, TVN_KEYDOWN },
212 213 214 215 216 217
    { WM_NOTIFY, sent|id, 0, 0, TVN_ITEMEXPANDINGA },
    { WM_NOTIFY, sent|id, 0, 0, TVN_ITEMEXPANDEDA },
    { WM_CHANGEUISTATE, sent|optional },
    { 0 }
};

218 219 220 221 222 223 224
static const struct message parent_collapse_2nd_kb_seq[] = {
    { WM_NOTIFY, sent|id|optional, 0, 0, TVN_KEYDOWN },
    { WM_NOTIFY, sent|id, 0, 0, TVN_ITEMEXPANDINGA },
    { WM_CHANGEUISTATE, sent|optional },
    { 0 }
};

225
static const struct message parent_expand_empty_kb_seq[] = {
226
    { WM_NOTIFY, sent|id, 0, 0, TVN_KEYDOWN },
227 228 229 230
    { WM_CHANGEUISTATE, sent|optional },
    { 0 }
};

231 232
static const struct message parent_singleexpand_seq0[] = {
    /* alpha expands */
233 234 235 236 237 238 239 240
    { WM_NOTIFY, sent|id, 0, 0, TVN_SELCHANGINGA },
    { WM_NOTIFY, sent|id, 0, 0, TVN_SELCHANGEDA },
    { WM_NOTIFY, sent|id, 0, 0, TVN_SINGLEEXPAND },
    { WM_NOTIFY, sent|id, 0, 0, TVN_ITEMEXPANDINGA },
    { WM_NOTIFY, sent|id, 0, 0, TVN_ITEMEXPANDEDA },
    { 0 }
};

241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318
static const struct message parent_singleexpand_seq1[] = {
    /* bravo expands */
    { WM_NOTIFY, sent|id, 0, 0, TVN_SELCHANGINGA },
    { WM_NOTIFY, sent|id, 0, 0, TVN_SELCHANGEDA },
    { WM_NOTIFY, sent|id, 0, 0, TVN_SINGLEEXPAND },
    { WM_NOTIFY, sent|id, 0, 0, TVN_ITEMEXPANDINGA },
    { WM_NOTIFY, sent|id, 0, 0, TVN_ITEMEXPANDEDA },
    { 0 }
};

static const struct message parent_singleexpand_seq2[] = {
    /* delta expands, bravo collapses */
    { WM_NOTIFY, sent|id, 0, 0, TVN_SELCHANGINGA },
    { WM_NOTIFY, sent|id, 0, 0, TVN_SELCHANGEDA },
    { WM_NOTIFY, sent|id, 0, 0, TVN_SINGLEEXPAND },
    { WM_NOTIFY, sent|id, 0, 0, TVN_ITEMEXPANDINGA },
    { WM_NOTIFY, sent|id, 0, 0, TVN_ITEMEXPANDEDA },
    { WM_NOTIFY, sent|id, 0, 0, TVN_ITEMEXPANDINGA },
    { WM_NOTIFY, sent|id, 0, 0, TVN_ITEMEXPANDEDA },
    { 0 }
};

static const struct message parent_singleexpand_seq3[] = {
    /* foxtrot expands, alpha and delta collapse */
    { WM_NOTIFY, sent|id, 0, 0, TVN_SELCHANGINGA },
    { WM_NOTIFY, sent|id, 0, 0, TVN_SELCHANGEDA },
    { WM_NOTIFY, sent|id, 0, 0, TVN_SINGLEEXPAND },
    { WM_NOTIFY, sent|id, 0, 0, TVN_ITEMEXPANDINGA },
    { WM_NOTIFY, sent|id, 0, 0, TVN_ITEMEXPANDEDA },
    { WM_NOTIFY, sent|id, 0, 0, TVN_ITEMEXPANDINGA },
    { WM_NOTIFY, sent|id, 0, 0, TVN_ITEMEXPANDEDA },
    { WM_NOTIFY, sent|id, 0, 0, TVN_ITEMEXPANDINGA },
    { WM_NOTIFY, sent|id, 0, 0, TVN_ITEMEXPANDEDA },
    { 0 }
};

static const struct message parent_singleexpand_seq4[] = {
    /* alpha expands, foxtrot collapses */
    { WM_NOTIFY, sent|id, 0, 0, TVN_SELCHANGINGA },
    { WM_NOTIFY, sent|id, 0, 0, TVN_SELCHANGEDA },
    { WM_NOTIFY, sent|id, 0, 0, TVN_SINGLEEXPAND },
    { WM_NOTIFY, sent|id, 0, 0, TVN_ITEMEXPANDINGA },
    { WM_NOTIFY, sent|id, 0, 0, TVN_ITEMEXPANDEDA },
    { WM_NOTIFY, sent|id, 0, 0, TVN_ITEMEXPANDINGA },
    { WM_NOTIFY, sent|id, 0, 0, TVN_ITEMEXPANDEDA },
    { 0 }
};

static const struct message parent_singleexpand_seq5[] = {
    /* foxtrot expands while golf is selected, then golf expands and alpha collapses */
    { WM_NOTIFY, sent|id, 0, 0, TVN_SELCHANGINGA },
    { WM_NOTIFY, sent|id, 0, 0, TVN_ITEMEXPANDINGA },
    { WM_NOTIFY, sent|id, 0, 0, TVN_ITEMEXPANDEDA },
    { WM_NOTIFY, sent|id, 0, 0, TVN_SELCHANGEDA },
    { WM_NOTIFY, sent|id, 0, 0, TVN_SINGLEEXPAND },
    { WM_NOTIFY, sent|id, 0, 0, TVN_ITEMEXPANDINGA },
    { WM_NOTIFY, sent|id, 0, 0, TVN_ITEMEXPANDEDA },
    { WM_NOTIFY, sent|id, 0, 0, TVN_ITEMEXPANDINGA },
    { WM_NOTIFY, sent|id, 0, 0, TVN_ITEMEXPANDEDA },
    { 0 }
};

static const struct message parent_singleexpand_seq6[] = {
    /* hotel does not expand and india does not collapse because they have no children */
    { WM_NOTIFY, sent|id, 0, 0, TVN_SELCHANGINGA },
    { WM_NOTIFY, sent|id, 0, 0, TVN_SELCHANGEDA },
    { WM_NOTIFY, sent|id, 0, 0, TVN_SINGLEEXPAND },
    { 0 }
};

static const struct message parent_singleexpand_seq7[] = {
    /* india does not expand and hotel does not collapse because they have no children */
    { WM_NOTIFY, sent|id, 0, 0, TVN_SELCHANGINGA },
    { WM_NOTIFY, sent|id, 0, 0, TVN_SELCHANGEDA },
    { WM_NOTIFY, sent|id, 0, 0, TVN_SINGLEEXPAND },
    { 0 }
};

319 320 321 322 323
static const struct message parent_get_dispinfo_seq[] = {
    { WM_NOTIFY, sent|id, 0, 0, TVN_GETDISPINFOA },
    { 0 }
};

324 325 326 327
static const struct message empty_seq[] = {
    { 0 }
};

328 329 330 331
static const struct message parent_cd_seq[] = {
    { WM_NOTIFY, sent|id|custdraw, 0, 0, NM_CUSTOMDRAW, CDDS_PREPAINT },
    { WM_NOTIFY, sent|id|custdraw, 0, 0, NM_CUSTOMDRAW, CDDS_ITEMPREPAINT },
    { WM_NOTIFY, sent|id|custdraw, 0, 0, NM_CUSTOMDRAW, CDDS_ITEMPOSTPAINT },
332 333
    { WM_NOTIFY, sent|id|custdraw, 0, 0, NM_CUSTOMDRAW, CDDS_ITEMPREPAINT },
    { WM_NOTIFY, sent|id|custdraw, 0, 0, NM_CUSTOMDRAW, CDDS_ITEMPOSTPAINT },
334 335 336 337
    { WM_NOTIFY, sent|id|custdraw, 0, 0, NM_CUSTOMDRAW, CDDS_POSTPAINT },
    { 0 }
};

338 339 340 341 342 343 344
static const struct message parent_vk_return_seq[] = {
    { WM_NOTIFY, sent|id, 0, 0, TVN_KEYDOWN },
    { WM_NOTIFY, sent|id, 0, 0, NM_RETURN },
    { WM_CHANGEUISTATE, sent|optional },
    { 0 }
};

345 346 347 348 349 350 351
static HWND hMainWnd;

static HTREEITEM hRoot, hChild;

static int pos = 0;
static char sequence[256];

352
static void Clear(void)
353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380
{
    pos = 0;
    sequence[0] = '\0';
}

static void AddItem(char ch)
{
    sequence[pos++] = ch;
    sequence[pos] = '\0';
}

static void IdentifyItem(HTREEITEM hItem)
{
    if (hItem == hRoot) {
        AddItem('R');
        return;
    }
    if (hItem == hChild) {
        AddItem('C');
        return;
    }
    if (hItem == NULL) {
        AddItem('n');
        return;
    }
    AddItem('?');
}

381 382 383 384 385 386
/* This function hooks in and records all messages to the treeview control */
static LRESULT WINAPI TreeviewWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    static LONG defwndproc_counter = 0;
    LRESULT ret;
    WNDPROC lpOldProc = (WNDPROC)GetWindowLongPtrA(hwnd, GWLP_USERDATA);
387
    struct message msg = { 0 };
388 389 390 391 392 393

    msg.message = message;
    msg.flags = sent|wparam|lparam;
    if (defwndproc_counter) msg.flags |= defwinproc;
    msg.wParam = wParam;
    msg.lParam = lParam;
394
    add_message(sequences, TREEVIEW_SEQ_INDEX, &msg);
395 396 397 398 399 400 401 402

    defwndproc_counter++;
    ret = CallWindowProcA(lpOldProc, hwnd, message, wParam, lParam);
    defwndproc_counter--;

    return ret;
}

403
static HWND create_treeview_control(DWORD style)
404 405 406 407 408
{
    WNDPROC pOldWndProc;
    HWND hTree;

    hTree = CreateWindowExA(WS_EX_CLIENTEDGE, WC_TREEVIEWA, NULL, WS_CHILD|WS_VISIBLE|
409
            TVS_LINESATROOT|TVS_HASLINES|TVS_HASBUTTONS|TVS_EDITLABELS|style,
410 411 412 413 414 415 416 417 418 419 420 421
            0, 0, 120, 100, hMainWnd, (HMENU)100, GetModuleHandleA(0), 0);

    SetFocus(hTree);

    /* Record the old WNDPROC so we can call it after recording the messages */
    pOldWndProc = (WNDPROC)SetWindowLongPtrA(hTree, GWLP_WNDPROC, (LONG_PTR)TreeviewWndProc);
    SetWindowLongPtrA(hTree, GWLP_USERDATA, (LONG_PTR)pOldWndProc);

    return hTree;
}

static void fill_tree(HWND hTree)
422 423
{
    TVINSERTSTRUCTA ins;
424 425
    static CHAR root[]  = "Root",
                child[] = "Child";
426 427 428 429

    ins.hParent = TVI_ROOT;
    ins.hInsertAfter = TVI_ROOT;
    U(ins).item.mask = TVIF_TEXT;
430
    U(ins).item.pszText = root;
431
    hRoot = TreeView_InsertItemA(hTree, &ins);
432

433 434 435
    ins.hParent = hRoot;
    ins.hInsertAfter = TVI_FIRST;
    U(ins).item.mask = TVIF_TEXT;
436
    U(ins).item.pszText = child;
437
    hChild = TreeView_InsertItemA(hTree, &ins);
438 439 440 441
}

static void test_fillroot(void)
{
442
    TVITEMA tvi;
443
    HWND hTree;
444

445
    hTree = create_treeview_control(0);
446

447
    flush_sequences(sequences, NUM_MSG_SEQUENCES);
448 449 450 451 452

    fill_tree(hTree);

    Clear();
    AddItem('A');
453
    ok(hRoot != NULL, "failed to set root\n");
454
    AddItem('B');
455
    ok(hChild != NULL, "failed to set child\n");
456
    AddItem('.');
457
    ok_sequence(sequences, TREEVIEW_SEQ_INDEX, FillRootSeq, "FillRoot", FALSE);
458
    ok(!strcmp(sequence, "AB."), "Item creation\n");
459 460 461 462

    /* UMLPad 1.15 depends on this being not -1 (I_IMAGECALLBACK) */
    tvi.hItem = hRoot;
    tvi.mask = TVIF_IMAGE | TVIF_SELECTEDIMAGE;
463
    SendMessageA(hTree, TVM_GETITEMA, 0, (LPARAM)&tvi);
464 465 466 467
    ok(tvi.iImage == 0, "tvi.iImage=%d\n", tvi.iImage);
    ok(tvi.iSelectedImage == 0, "tvi.iSelectedImage=%d\n", tvi.iSelectedImage);

    DestroyWindow(hTree);
468 469
}

470
static void test_callback(void)
471 472 473 474
{
    HTREEITEM hRoot;
    HTREEITEM hItem1, hItem2;
    TVINSERTSTRUCTA ins;
475
    TVITEMA tvi;
476
    CHAR test_string[] = "Test_string";
477
    static const CHAR test2A[] = "TEST2";
478
    CHAR buf[128];
479
    HWND hTree;
480
    DWORD ret;
481

482
    hTree = create_treeview_control(0);
483

484
    ret = SendMessageA(hTree, TVM_DELETEITEM, 0, (LPARAM)TVI_ROOT);
485
    expect(TRUE, ret);
486 487 488
    ins.hParent = TVI_ROOT;
    ins.hInsertAfter = TVI_ROOT;
    U(ins).item.mask = TVIF_TEXT;
489 490
    U(ins).item.pszText = LPSTR_TEXTCALLBACKA;
    hRoot = TreeView_InsertItemA(hTree, &ins);
491
    ok(hRoot != NULL, "failed to set root\n");
492 493 494 495 496

    tvi.hItem = hRoot;
    tvi.mask = TVIF_TEXT;
    tvi.pszText = buf;
    tvi.cchTextMax = sizeof(buf)/sizeof(buf[0]);
497
    ret = TreeView_GetItemA(hTree, &tvi);
498
    expect(TRUE, ret);
499 500 501 502 503 504 505
    ok(strcmp(tvi.pszText, TEST_CALLBACK_TEXT) == 0, "Callback item text mismatch %s vs %s\n",
        tvi.pszText, TEST_CALLBACK_TEXT);

    ins.hParent = hRoot;
    ins.hInsertAfter = TVI_FIRST;
    U(ins).item.mask = TVIF_TEXT;
    U(ins).item.pszText = test_string;
506
    hItem1 = TreeView_InsertItemA(hTree, &ins);
507
    ok(hItem1 != NULL, "failed to set Item1\n");
508 509

    tvi.hItem = hItem1;
510
    ret = TreeView_GetItemA(hTree, &tvi);
511
    expect(TRUE, ret);
512 513 514 515 516
    ok(strcmp(tvi.pszText, test_string) == 0, "Item text mismatch %s vs %s\n",
        tvi.pszText, test_string);

    /* undocumented: pszText of NULL also means LPSTR_CALLBACK: */
    tvi.pszText = NULL;
517
    ret = TreeView_SetItemA(hTree, &tvi);
518
    expect(TRUE, ret);
519
    tvi.pszText = buf;
520
    ret = TreeView_GetItemA(hTree, &tvi);
521
    expect(TRUE, ret);
522
    ok(strcmp(tvi.pszText, TEST_CALLBACK_TEXT) == 0, "Item text mismatch %s vs %s\n",
523 524 525
        tvi.pszText, TEST_CALLBACK_TEXT);

    U(ins).item.pszText = NULL;
526
    hItem2 = TreeView_InsertItemA(hTree, &ins);
527
    ok(hItem2 != NULL, "failed to set Item2\n");
528 529
    tvi.hItem = hItem2;
    memset(buf, 0, sizeof(buf));
530
    ret = TreeView_GetItemA(hTree, &tvi);
531
    expect(TRUE, ret);
532
    ok(strcmp(tvi.pszText, TEST_CALLBACK_TEXT) == 0, "Item text mismatch %s vs %s\n",
533
        tvi.pszText, TEST_CALLBACK_TEXT);
534

535 536 537 538
    /* notification handler changed A->W */
    g_disp_A_to_W = TRUE;
    tvi.hItem = hItem2;
    memset(buf, 0, sizeof(buf));
539
    ret = TreeView_GetItemA(hTree, &tvi);
540
    expect(TRUE, ret);
541 542 543 544
    ok(strcmp(tvi.pszText, test2A) == 0, "got %s, expected %s\n",
        tvi.pszText, test2A);
    g_disp_A_to_W = FALSE;

545 546 547 548 549 550 551 552 553 554 555 556
    /* handler changes state image index */
    SetWindowLongA(hTree, GWL_STYLE, GetWindowLongA(hTree, GWL_STYLE) | TVS_CHECKBOXES);

    /* clear selection, handler will set selected state */
    ret = SendMessageA(hTree, TVM_SELECTITEM, TVGN_CARET, 0);
    expect(TRUE, ret);

    flush_sequences(sequences, NUM_MSG_SEQUENCES);

    tvi.hItem = hRoot;
    tvi.mask = TVIF_STATE;
    tvi.state = TVIS_SELECTED;
557
    ret = TreeView_GetItemA(hTree, &tvi);
558
    expect(TRUE, ret);
559
    ok(tvi.state == INDEXTOSTATEIMAGEMASK(1), "got 0x%x\n", tvi.state);
560 561 562 563 564 565 566 567 568

    ok_sequence(sequences, PARENT_SEQ_INDEX, empty_seq,
                "no TVN_GETDISPINFO for a state seq", FALSE);

    tvi.hItem     = hRoot;
    tvi.mask      = TVIF_IMAGE | TVIF_STATE;
    tvi.state     = TVIS_FOCUSED;
    tvi.stateMask = TVIS_FOCUSED;
    tvi.iImage    = I_IMAGECALLBACK;
569
    ret = TreeView_SetItemA(hTree, &tvi);
570 571 572 573 574 575 576 577
    expect(TRUE, ret);

    /* ask for item image index through callback - state is also set with state image index */
    flush_sequences(sequences, NUM_MSG_SEQUENCES);

    tvi.hItem = hRoot;
    tvi.mask = TVIF_IMAGE;
    tvi.state = 0;
578
    ret = TreeView_GetItemA(hTree, &tvi);
579
    expect(TRUE, ret);
580
    ok(tvi.state == (INDEXTOSTATEIMAGEMASK(1) | TVIS_FOCUSED), "got 0x%x\n", tvi.state);
581 582 583 584 585 586 587 588 589 590 591

    ok_sequence(sequences, PARENT_SEQ_INDEX, parent_get_dispinfo_seq,
                "callback for state/overlay image index, noop seq", FALSE);

    /* ask for image again and overwrite state to some value in handler */
    flush_sequences(sequences, NUM_MSG_SEQUENCES);

    g_disp_set_stateimage = TRUE;
    tvi.hItem = hRoot;
    tvi.mask = TVIF_IMAGE;
    tvi.state = INDEXTOSTATEIMAGEMASK(1);
592
    tvi.stateMask = 0;
593
    ret = TreeView_GetItemA(hTree, &tvi);
594 595
    expect(TRUE, ret);
    /* handler sets TVIS_SELECTED as well */
596
    ok(tvi.state == (TVIS_FOCUSED | TVIS_SELECTED | INDEXTOSTATEIMAGEMASK(2) | INDEXTOOVERLAYMASK(3)), "got 0x%x\n", tvi.state);
597 598 599 600 601
    g_disp_set_stateimage = FALSE;

    ok_sequence(sequences, PARENT_SEQ_INDEX, parent_get_dispinfo_seq,
                "callback for state/overlay image index seq", FALSE);

602
    DestroyWindow(hTree);
603 604
}

605
static void test_select(void)
606
{
607
    BOOL r;
608
    HWND hTree;
609

610
    hTree = create_treeview_control(0);
611
    fill_tree(hTree);
612

613
    /* root-none select tests */
614
    flush_sequences(sequences, NUM_MSG_SEQUENCES);
615
    r = SendMessageA(hTree, TVM_SELECTITEM, TVGN_CARET, 0);
616
    expect(TRUE, r);
617 618
    Clear();
    AddItem('1');
619
    r = SendMessageA(hTree, TVM_SELECTITEM, TVGN_CARET, (LPARAM)hRoot);
620
    expect(TRUE, r);
621
    AddItem('2');
622
    r = SendMessageA(hTree, TVM_SELECTITEM, TVGN_CARET, (LPARAM)hRoot);
623
    expect(TRUE, r);
624
    AddItem('3');
625
    r = SendMessageA(hTree, TVM_SELECTITEM, TVGN_CARET, 0);
626
    expect(TRUE, r);
627
    AddItem('4');
628
    r = SendMessageA(hTree, TVM_SELECTITEM, TVGN_CARET, 0);
629
    expect(TRUE, r);
630
    AddItem('5');
631
    r = SendMessageA(hTree, TVM_SELECTITEM, TVGN_CARET, (LPARAM)hRoot);
632
    expect(TRUE, r);
633
    AddItem('.');
634
    ok(!strcmp(sequence, "1(nR)nR23(Rn)Rn45(nR)nR."), "root-none select test\n");
635
    ok_sequence(sequences, TREEVIEW_SEQ_INDEX, rootnone_select_seq,
636
                "root-none select seq", FALSE);
637

638
    /* root-child select tests */
639
    flush_sequences(sequences, NUM_MSG_SEQUENCES);
640
    r = SendMessageA(hTree, TVM_SELECTITEM, TVGN_CARET, 0);
641 642
    expect(TRUE, r);

643 644
    Clear();
    AddItem('1');
645
    r = SendMessageA(hTree, TVM_SELECTITEM, TVGN_CARET, (LPARAM)hRoot);
646
    expect(TRUE, r);
647
    AddItem('2');
648
    r = SendMessageA(hTree, TVM_SELECTITEM, TVGN_CARET, (LPARAM)hRoot);
649
    expect(TRUE, r);
650
    AddItem('3');
651
    r = SendMessageA(hTree, TVM_SELECTITEM, TVGN_CARET, (LPARAM)hChild);
652
    expect(TRUE, r);
653
    AddItem('4');
654
    r = SendMessageA(hTree, TVM_SELECTITEM, TVGN_CARET, (LPARAM)hChild);
655
    expect(TRUE, r);
656
    AddItem('5');
657
    r = SendMessageA(hTree, TVM_SELECTITEM, TVGN_CARET, (LPARAM)hRoot);
658
    expect(TRUE, r);
659
    AddItem('.');
660
    ok(!strcmp(sequence, "1(nR)nR23(RC)RC45(CR)CR."), "root-child select test\n");
661
    ok_sequence(sequences, TREEVIEW_SEQ_INDEX, rootchild_select_seq,
662 663 664
                "root-child select seq", FALSE);

    DestroyWindow(hTree);
665 666
}

667
static void test_getitemtext(void)
668 669 670
{
    TVINSERTSTRUCTA ins;
    HTREEITEM hChild;
671
    TVITEMA tvi;
672
    HWND hTree;
673 674

    CHAR szBuffer[80] = "Blah";
675 676
    int nBufferSize = sizeof(szBuffer)/sizeof(CHAR);

677
    hTree = create_treeview_control(0);
678
    fill_tree(hTree);
679

680
    flush_sequences(sequences, NUM_MSG_SEQUENCES);
681

682 683 684 685 686 687
    /* add an item without TVIF_TEXT mask and pszText == NULL */
    ins.hParent = hRoot;
    ins.hInsertAfter = TVI_ROOT;
    U(ins).item.mask = 0;
    U(ins).item.pszText = NULL;
    U(ins).item.cchTextMax = 0;
688
    hChild = TreeView_InsertItemA(hTree, &ins);
689
    ok(hChild != NULL, "failed to set hChild\n");
690 691 692 693 694 695 696

    /* retrieve it with TVIF_TEXT mask */
    tvi.hItem = hChild;
    tvi.mask = TVIF_TEXT;
    tvi.cchTextMax = nBufferSize;
    tvi.pszText = szBuffer;

697
    SendMessageA( hTree, TVM_GETITEMA, 0, (LPARAM)&tvi );
698 699
    ok(!strcmp(szBuffer, ""), "szBuffer=\"%s\", expected \"\"\n", szBuffer);
    ok(SendMessageA(hTree, TVM_DELETEITEM, 0, (LPARAM)hChild), "DeleteItem failed\n");
700
    ok_sequence(sequences, TREEVIEW_SEQ_INDEX, getitemtext_seq, "get item text seq", FALSE);
701 702

    DestroyWindow(hTree);
703 704
}

705
static void test_focus(void)
706 707 708 709 710
{
    TVINSERTSTRUCTA ins;
    static CHAR child1[]  = "Edit",
                child2[]  = "A really long string";
    HTREEITEM hChild1, hChild2;
711 712
    HWND hTree;
    HWND hEdit;
713

714
    hTree = create_treeview_control(0);
715 716
    fill_tree(hTree);

717
    flush_sequences(sequences, NUM_MSG_SEQUENCES);
718

719 720 721 722 723 724 725 726 727
    /* This test verifies that when a label is being edited, scrolling
     * the treeview does not cause the label to lose focus. To test
     * this, first some additional entries are added to generate
     * scrollbars.
     */
    ins.hParent = hRoot;
    ins.hInsertAfter = hChild;
    U(ins).item.mask = TVIF_TEXT;
    U(ins).item.pszText = child1;
728
    hChild1 = TreeView_InsertItemA(hTree, &ins);
729
    ok(hChild1 != NULL, "failed to set hChild1\n");
730 731 732
    ins.hInsertAfter = hChild1;
    U(ins).item.mask = TVIF_TEXT;
    U(ins).item.pszText = child2;
733
    hChild2 = TreeView_InsertItemA(hTree, &ins);
734
    ok(hChild2 != NULL, "failed to set hChild2\n");
735 736

    ShowWindow(hMainWnd,SW_SHOW);
737
    SendMessageA(hTree, TVM_SELECTITEM, TVGN_CARET, (LPARAM)hChild);
738
    hEdit = (HWND)SendMessageA(hTree, TVM_EDITLABELA, 0, (LPARAM)hChild);
739 740
    ScrollWindowEx(hTree, -10, 0, NULL, NULL, NULL, NULL, SW_SCROLLCHILDREN);
    ok(GetFocus() == hEdit, "Edit control should have focus\n");
741
    ok_sequence(sequences, TREEVIEW_SEQ_INDEX, focus_seq, "focus test", TRUE);
742 743

    DestroyWindow(hTree);
744 745
}

746
static void test_get_set_bkcolor(void)
747
{
748
    COLORREF crColor;
749 750
    HWND hTree;

751
    hTree = create_treeview_control(0);
752 753
    fill_tree(hTree);

754
    flush_sequences(sequences, NUM_MSG_SEQUENCES);
755

756
    /* If the value is -1, the control is using the system color for the background color. */
757
    crColor = SendMessageA(hTree, TVM_GETBKCOLOR, 0, 0);
758
    ok(crColor == ~0u, "Default background color reported as 0x%.8x\n", crColor);
759 760

    /* Test for black background */
761 762
    SendMessageA(hTree, TVM_SETBKCOLOR, 0, RGB(0,0,0));
    crColor = SendMessageA(hTree, TVM_GETBKCOLOR, 0, 0);
763 764 765
    ok(crColor == RGB(0,0,0), "Black background color reported as 0x%.8x\n", crColor);

    /* Test for white background */
766 767
    SendMessageA(hTree, TVM_SETBKCOLOR, 0, RGB(255,255,255));
    crColor = SendMessageA(hTree, TVM_GETBKCOLOR, 0, 0);
768 769 770
    ok(crColor == RGB(255,255,255), "White background color reported as 0x%.8x\n", crColor);

    /* Reset the default background */
771
    SendMessageA(hTree, TVM_SETBKCOLOR, 0, -1);
772

773
    ok_sequence(sequences, TREEVIEW_SEQ_INDEX, test_get_set_bkcolor_seq,
774 775 776
        "test get set bkcolor", FALSE);

    DestroyWindow(hTree);
777 778
}

779
static void test_get_set_imagelist(void)
780
{
781
    HIMAGELIST himl;
782 783
    HWND hTree;

784
    hTree = create_treeview_control(0);
785 786
    fill_tree(hTree);

787
    flush_sequences(sequences, NUM_MSG_SEQUENCES);
788 789

    /* Test a NULL HIMAGELIST */
790 791
    SendMessageA(hTree, TVM_SETIMAGELIST, TVSIL_NORMAL, 0);
    himl = (HIMAGELIST)SendMessageA(hTree, TVM_GETIMAGELIST, TVSIL_NORMAL, 0);
792
    ok(himl == NULL, "NULL image list, reported as %p, expected 0.\n", himl);
793 794

    /* TODO: Test an actual image list */
795

796
    ok_sequence(sequences, TREEVIEW_SEQ_INDEX, test_get_set_imagelist_seq,
797 798 799
        "test get imagelist", FALSE);

    DestroyWindow(hTree);
800 801
}

802
static void test_get_set_indent(void)
803
{
804 805 806
    int ulIndent;
    int ulMinIndent;
    int ulMoreThanTwiceMin;
807 808
    HWND hTree;

809
    hTree = create_treeview_control(0);
810 811
    fill_tree(hTree);

812
    flush_sequences(sequences, NUM_MSG_SEQUENCES);
813 814

    /* Finding the minimum indent */
815 816
    SendMessageA(hTree, TVM_SETINDENT, 0, 0);
    ulMinIndent = SendMessageA(hTree, TVM_GETINDENT, 0, 0);
817 818 819

    /* Checking an indent that is more than twice the default indent */
    ulMoreThanTwiceMin = 2*ulMinIndent+1;
820 821
    SendMessageA(hTree, TVM_SETINDENT, ulMoreThanTwiceMin, 0);
    ulIndent = SendMessageA(hTree, TVM_GETINDENT, 0, 0);
822
    ok(ulIndent == ulMoreThanTwiceMin, "Indent reported as %d, expected %d\n", ulIndent, ulMoreThanTwiceMin);
823

824
    ok_sequence(sequences, TREEVIEW_SEQ_INDEX, test_get_set_indent_seq,
825 826 827
        "test get set indent", FALSE);

    DestroyWindow(hTree);
828 829
}

830
static void test_get_set_insertmark(void)
831 832
{
    COLORREF crColor = RGB(0,0,0);
833 834
    HWND hTree;

835
    hTree = create_treeview_control(0);
836 837
    fill_tree(hTree);

838
    flush_sequences(sequences, NUM_MSG_SEQUENCES);
839

840 841
    SendMessageA(hTree, TVM_SETINSERTMARKCOLOR, 0, crColor);
    crColor = SendMessageA(hTree, TVM_GETINSERTMARKCOLOR, 0, 0);
842
    ok(crColor == RGB(0,0,0), "Insert mark color reported as 0x%.8x, expected 0x00000000\n", crColor);
843

844
    ok_sequence(sequences, TREEVIEW_SEQ_INDEX, test_get_set_insertmarkcolor_seq,
845 846 847
        "test get set insertmark color", FALSE);

    DestroyWindow(hTree);
848 849
}

850
static void test_get_set_item(void)
851
{
852
    TVITEMA tviRoot = {0};
853 854
    int nBufferSize = 80;
    char szBuffer[80] = {0};
855
    HWND hTree, hTree2;
856
    DWORD ret;
857

858
    hTree = create_treeview_control(0);
859 860
    fill_tree(hTree);

861 862 863 864
    tviRoot.hItem = hRoot;
    tviRoot.mask  = TVIF_STATE;
    tviRoot.state = TVIS_FOCUSED;
    tviRoot.stateMask = TVIS_FOCUSED;
865
    ret = SendMessageA(hTree, TVM_SETITEMA, 0, (LPARAM)&tviRoot);
866 867
    expect(TRUE, ret);

868
    flush_sequences(sequences, NUM_MSG_SEQUENCES);
869

870
    /* Test the root item, state is set even when not requested */
871 872
    tviRoot.hItem = hRoot;
    tviRoot.mask = TVIF_TEXT;
873 874
    tviRoot.state = 0;
    tviRoot.stateMask = 0;
875 876
    tviRoot.cchTextMax = nBufferSize;
    tviRoot.pszText = szBuffer;
877
    ret = SendMessageA(hTree, TVM_GETITEMA, 0, (LPARAM)&tviRoot);
878
    expect(TRUE, ret);
879
    ok(!strcmp("Root", szBuffer), "GetItem: szBuffer=\"%s\", expected \"Root\"\n", szBuffer);
880
    ok(tviRoot.state == TVIS_FOCUSED, "got 0x%0x\n", tviRoot.state);
881 882

    /* Change the root text */
883 884
    lstrcpynA(szBuffer, "Testing123", nBufferSize);
    ret = SendMessageA(hTree, TVM_SETITEMA, 0, (LPARAM)&tviRoot);
885
    expect(TRUE, ret);
886
    memset(szBuffer, 0, nBufferSize);
887
    ret = SendMessageA(hTree, TVM_GETITEMA, 0, (LPARAM)&tviRoot);
888
    expect(TRUE, ret);
889 890 891 892
    ok(!strcmp("Testing123", szBuffer), "GetItem: szBuffer=\"%s\", expected \"Testing123\"\n", szBuffer);

    /* Reset the root text */
    memset(szBuffer, 0, nBufferSize);
893 894
    lstrcpynA(szBuffer, "Root", nBufferSize);
    ret = SendMessageA(hTree, TVM_SETITEMA, 0, (LPARAM)&tviRoot);
895
    expect(TRUE, ret);
896

897
    ok_sequence(sequences, TREEVIEW_SEQ_INDEX, test_get_set_item_seq,
898 899
        "test get set item", FALSE);

900 901 902 903 904 905
    /* get item from a different tree */
    hTree2 = create_treeview_control(0);

    tviRoot.hItem = hRoot;
    tviRoot.mask = TVIF_STATE;
    tviRoot.state = 0;
906
    ret = SendMessageA(hTree2, TVM_GETITEMA, 0, (LPARAM)&tviRoot);
907 908 909
    expect(TRUE, ret);
    ok(tviRoot.state == TVIS_FOCUSED, "got state 0x%0x\n", tviRoot.state);

910 911 912 913 914 915 916
    /* invalid item pointer, nt4 crashes here but later versions just return 0 */
    tviRoot.hItem = (HTREEITEM)0xdeadbeef;
    tviRoot.mask = TVIF_STATE;
    tviRoot.state = 0;
    ret = SendMessageA(hTree2, TVM_GETITEMA, 0, (LPARAM)&tviRoot);
    expect(FALSE, ret);

917
    DestroyWindow(hTree);
918
    DestroyWindow(hTree2);
919 920
}

921
static void test_get_set_itemheight(void)
922 923 924
{
    int ulOldHeight = 0;
    int ulNewHeight = 0;
925 926
    HWND hTree;

927
    hTree = create_treeview_control(0);
928 929
    fill_tree(hTree);

930
    flush_sequences(sequences, NUM_MSG_SEQUENCES);
931 932

    /* Assuming default height to begin with */
933
    ulOldHeight = SendMessageA(hTree, TVM_GETITEMHEIGHT, 0, 0);
934 935

    /* Explicitly setting and getting the default height */
936 937
    SendMessageA(hTree, TVM_SETITEMHEIGHT, -1, 0);
    ulNewHeight = SendMessageA(hTree, TVM_GETITEMHEIGHT, 0, 0);
938 939 940
    ok(ulNewHeight == ulOldHeight, "Default height not set properly, reported %d, expected %d\n", ulNewHeight, ulOldHeight);

    /* Explicitly setting and getting the height of twice the normal */
941 942
    SendMessageA(hTree, TVM_SETITEMHEIGHT, 2*ulOldHeight, 0);
    ulNewHeight = SendMessageA(hTree, TVM_GETITEMHEIGHT, 0, 0);
943 944
    ok(ulNewHeight == 2*ulOldHeight, "New height not set properly, reported %d, expected %d\n", ulNewHeight, 2*ulOldHeight);

Felix Nawothnig's avatar
Felix Nawothnig committed
945
    /* Assuming tree doesn't have TVS_NONEVENHEIGHT set, so a set of 9 will round down to 8 */
946 947
    SendMessageA(hTree, TVM_SETITEMHEIGHT, 9, 0);
    ulNewHeight = SendMessageA(hTree, TVM_GETITEMHEIGHT, 0, 0);
Felix Nawothnig's avatar
Felix Nawothnig committed
948
    ok(ulNewHeight == 8, "Uneven height not set properly, reported %d, expected %d\n", ulNewHeight, 8);
949

950
    ok_sequence(sequences, TREEVIEW_SEQ_INDEX, test_get_set_itemheight_seq,
951 952
        "test get set item height", FALSE);

953
    /* without TVS_NONEVENHEIGHT */
954
    SetWindowLongA(hTree, GWL_STYLE, GetWindowLongA(hTree, GWL_STYLE) & ~TVS_NONEVENHEIGHT);
955
    /* odd value */
956
    ulOldHeight = SendMessageA(hTree, TVM_SETITEMHEIGHT, 3, 0);
957
    ok(ulOldHeight == 8, "got %d, expected %d\n", ulOldHeight, 8);
958
    ulNewHeight = SendMessageA(hTree, TVM_GETITEMHEIGHT, 0, 0);
959 960
    ok(ulNewHeight == 2, "got %d, expected %d\n", ulNewHeight, 2);

961
    ulOldHeight = SendMessageA(hTree, TVM_SETITEMHEIGHT, 4, 0);
962
    ok(ulOldHeight == 2, "got %d, expected %d\n", ulOldHeight, 2);
963
    ulNewHeight = SendMessageA(hTree, TVM_GETITEMHEIGHT, 0, 0);
964 965 966
    ok(ulNewHeight == 4, "got %d, expected %d\n", ulNewHeight, 4);

    /* with TVS_NONEVENHEIGHT */
967
    SetWindowLongA(hTree, GWL_STYLE, GetWindowLongA(hTree, GWL_STYLE) | TVS_NONEVENHEIGHT);
968
    /* odd value */
969
    ulOldHeight = SendMessageA(hTree, TVM_SETITEMHEIGHT, 3, 0);
970
    ok(ulOldHeight == 4, "got %d, expected %d\n", ulOldHeight, 4);
971
    ulNewHeight = SendMessageA(hTree, TVM_GETITEMHEIGHT, 0, 0);
972 973
    ok(ulNewHeight == 3, "got %d, expected %d\n", ulNewHeight, 3);
    /* even value */
974
    ulOldHeight = SendMessageA(hTree, TVM_SETITEMHEIGHT, 10, 0);
975
    ok(ulOldHeight == 3, "got %d, expected %d\n", ulOldHeight, 3);
976
    ulNewHeight = SendMessageA(hTree, TVM_GETITEMHEIGHT, 0, 0);
977 978
    ok(ulNewHeight == 10, "got %d, expected %d\n", ulNewHeight, 10);

979
    DestroyWindow(hTree);
980 981
}

982
static void test_get_set_scrolltime(void)
983 984 985
{
    int ulExpectedTime = 20;
    int ulTime = 0;
986 987
    HWND hTree;

988
    hTree = create_treeview_control(0);
989 990
    fill_tree(hTree);

991
    flush_sequences(sequences, NUM_MSG_SEQUENCES);
992

993 994
    SendMessageA(hTree, TVM_SETSCROLLTIME, ulExpectedTime, 0);
    ulTime = SendMessageA(hTree, TVM_GETSCROLLTIME, 0, 0);
995
    ok(ulTime == ulExpectedTime, "Scroll time reported as %d, expected %d\n", ulTime, ulExpectedTime);
996

997
    ok_sequence(sequences, TREEVIEW_SEQ_INDEX, test_get_set_scrolltime_seq,
998 999 1000
        "test get set scroll time", FALSE);

    DestroyWindow(hTree);
1001 1002
}

1003
static void test_get_set_textcolor(void)
1004 1005
{
    /* If the value is -1, the control is using the system color for the text color. */
1006
    COLORREF crColor;
1007 1008
    HWND hTree;

1009
    hTree = create_treeview_control(0);
1010 1011
    fill_tree(hTree);

1012
    flush_sequences(sequences, NUM_MSG_SEQUENCES);
1013

1014
    crColor = SendMessageA(hTree, TVM_GETTEXTCOLOR, 0, 0);
1015
    ok(crColor == ~0u, "Default text color reported as 0x%.8x\n", crColor);
1016 1017

    /* Test for black text */
1018 1019
    SendMessageA(hTree, TVM_SETTEXTCOLOR, 0, RGB(0,0,0));
    crColor = SendMessageA(hTree, TVM_GETTEXTCOLOR, 0, 0);
1020 1021 1022
    ok(crColor == RGB(0,0,0), "Black text color reported as 0x%.8x\n", crColor);

    /* Test for white text */
1023 1024
    SendMessageA(hTree, TVM_SETTEXTCOLOR, 0, RGB(255,255,255));
    crColor = SendMessageA(hTree, TVM_GETTEXTCOLOR, 0, 0);
1025 1026 1027
    ok(crColor == RGB(255,255,255), "White text color reported as 0x%.8x\n", crColor);

    /* Reset the default text color */
1028
    SendMessageA(hTree, TVM_SETTEXTCOLOR, 0, CLR_NONE);
1029

1030
    ok_sequence(sequences, TREEVIEW_SEQ_INDEX, test_get_set_textcolor_seq,
1031 1032 1033
        "test get set text color", FALSE);

    DestroyWindow(hTree);
1034 1035
}

1036
static void test_get_set_tooltips(void)
1037 1038
{
    HWND hwndLastToolTip = NULL;
1039
    HWND hPopupTreeView;
1040 1041
    HWND hTree;

1042
    hTree = create_treeview_control(0);
1043 1044
    fill_tree(hTree);

1045
    flush_sequences(sequences, NUM_MSG_SEQUENCES);
1046 1047

    /* show even WS_POPUP treeview don't send NM_TOOLTIPSCREATED */
1048 1049
    hPopupTreeView = CreateWindowA(WC_TREEVIEWA, NULL, WS_POPUP|WS_VISIBLE, 0, 0, 100, 100,
            hMainWnd, NULL, NULL, NULL);
1050
    DestroyWindow(hPopupTreeView);
1051 1052

    /* Testing setting a NULL ToolTip */
1053 1054
    SendMessageA(hTree, TVM_SETTOOLTIPS, 0, 0);
    hwndLastToolTip = (HWND)SendMessageA(hTree, TVM_GETTOOLTIPS, 0, 0);
1055 1056
    ok(hwndLastToolTip == NULL, "NULL tool tip, reported as 0x%p, expected 0.\n", hwndLastToolTip);

1057
    ok_sequence(sequences, TREEVIEW_SEQ_INDEX, test_get_set_tooltips_seq,
1058 1059
        "test get set tooltips", TRUE);

1060
    /* TODO: Add a test of an actual tooltip */
1061
    DestroyWindow(hTree);
1062 1063
}

1064
static void test_get_set_unicodeformat(void)
1065
{
1066 1067
    BOOL bPreviousSetting;
    BOOL bNewSetting;
1068 1069
    HWND hTree;

1070
    hTree = create_treeview_control(0);
1071 1072
    fill_tree(hTree);

1073
    /* Check that an invalid format returned by NF_QUERY defaults to ANSI */
1074
    bPreviousSetting = SendMessageA(hTree, TVM_GETUNICODEFORMAT, 0, 0);
1075
    ok(bPreviousSetting == FALSE, "Format should be ANSI.\n");
1076

1077
    flush_sequences(sequences, NUM_MSG_SEQUENCES);
1078 1079

    /* Set to Unicode */
1080 1081
    bPreviousSetting = SendMessageA(hTree, TVM_SETUNICODEFORMAT, 1, 0);
    bNewSetting = SendMessageA(hTree, TVM_GETUNICODEFORMAT, 0, 0);
1082
    ok(bNewSetting == TRUE, "Unicode setting did not work.\n");
1083 1084

    /* Set to ANSI */
1085 1086
    SendMessageA(hTree, TVM_SETUNICODEFORMAT, 0, 0);
    bNewSetting = SendMessageA(hTree, TVM_GETUNICODEFORMAT, 0, 0);
1087
    ok(bNewSetting == FALSE, "ANSI setting did not work.\n");
1088 1089

    /* Revert to original setting */
1090
    SendMessageA(hTree, TVM_SETUNICODEFORMAT, bPreviousSetting, 0);
1091

1092
    ok_sequence(sequences, TREEVIEW_SEQ_INDEX, test_get_set_unicodeformat_seq,
1093
        "test get set unicode format", FALSE);
1094

1095
    DestroyWindow(hTree);
1096 1097
}

1098
static LRESULT CALLBACK parent_wnd_proc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
1099
{
1100
    static LONG defwndproc_counter = 0;
1101
    struct message msg = { 0 };
1102
    LRESULT ret;
1103 1104
    RECT rect;
    HTREEITEM visibleItem;
1105 1106 1107 1108 1109 1110

    msg.message = message;
    msg.flags = sent|wparam|lparam;
    if (defwndproc_counter) msg.flags |= defwinproc;
    msg.wParam = wParam;
    msg.lParam = lParam;
1111 1112
    if (message == WM_NOTIFY && lParam)
        msg.id = ((NMHDR*)lParam)->code;
1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127

    /* log system messages, except for painting */
    if (message < WM_USER &&
        message != WM_PAINT &&
        message != WM_ERASEBKGND &&
        message != WM_NCPAINT &&
        message != WM_NCHITTEST &&
        message != WM_GETTEXT &&
        message != WM_GETICON &&
        message != WM_DEVICECHANGE)
    {
        add_message(sequences, PARENT_SEQ_INDEX, &msg);
    }

    switch(message) {
1128 1129 1130 1131 1132 1133 1134
    case WM_NOTIFYFORMAT:
    {
        /* Make NF_QUERY return an invalid format to show that it defaults to ANSI */
        if (lParam == NF_QUERY) return 0;
        break;
    }

1135 1136 1137
    case WM_NOTIFY:
    {
        NMHDR *pHdr = (NMHDR *)lParam;
1138

1139 1140 1141
        ok(pHdr->code != NM_TOOLTIPSCREATED, "Treeview should not send NM_TOOLTIPSCREATED\n");
        if (pHdr->idFrom == 100)
        {
1142
            NMTREEVIEWA *pTreeView = (LPNMTREEVIEWA) lParam;
1143 1144
            switch(pHdr->code)
            {
1145 1146 1147 1148
            case TVN_SELCHANGINGA:
                AddItem('(');
                IdentifyItem(pTreeView->itemOld.hItem);
                IdentifyItem(pTreeView->itemNew.hItem);
1149
                break;
1150 1151 1152 1153
            case TVN_SELCHANGEDA:
                AddItem(')');
                IdentifyItem(pTreeView->itemOld.hItem);
                IdentifyItem(pTreeView->itemNew.hItem);
1154
                break;
1155 1156 1157
            case TVN_GETDISPINFOA: {
                NMTVDISPINFOA *disp = (NMTVDISPINFOA *)lParam;
                if (disp->item.mask & TVIF_TEXT) {
1158
                    lstrcpynA(disp->item.pszText, TEST_CALLBACK_TEXT, disp->item.cchTextMax);
1159
                }
1160 1161 1162 1163 1164 1165 1166 1167

                if (g_disp_A_to_W && (disp->item.mask & TVIF_TEXT)) {
                    static const WCHAR testW[] = {'T','E','S','T','2',0};

                    disp->hdr.code = TVN_GETDISPINFOW;
                    memcpy(disp->item.pszText, testW, sizeof(testW));
                }

1168 1169 1170 1171 1172 1173 1174 1175 1176
                if (g_disp_set_stateimage)
                {
                    ok(disp->item.mask == TVIF_IMAGE, "got %x\n", disp->item.mask);
                    /* both masks set here are necessary to change state bits */
                    disp->item.mask |= TVIF_STATE;
                    disp->item.state = TVIS_SELECTED | INDEXTOSTATEIMAGEMASK(2) | INDEXTOOVERLAYMASK(3);
                    disp->item.stateMask = TVIS_SELECTED | TVIS_OVERLAYMASK | TVIS_STATEIMAGEMASK;
                }

1177
                break;
1178
              }
1179
            case TVN_BEGINLABELEDITA:
1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193
              {
                if (g_beginedit_alter_text)
                {
                    static const char* textA = "<edittextaltered>";
                    HWND edit;

                    edit = (HWND)SendMessageA(pHdr->hwndFrom, TVM_GETEDITCONTROL, 0, 0);
                    ok(IsWindow(edit), "failed to get edit handle\n");
                    SetWindowTextA(edit, textA);
                }

                break;
              }

1194
            case TVN_ENDLABELEDITA: return TRUE;
1195
            case TVN_ITEMEXPANDINGA:
1196 1197 1198
              {
                UINT newmask = pTreeView->itemNew.mask & ~TVIF_CHILDREN;
                ok(newmask ==
1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211
                   (TVIF_HANDLE | TVIF_SELECTEDIMAGE | TVIF_IMAGE | TVIF_PARAM | TVIF_STATE),
                   "got wrong mask %x\n", pTreeView->itemNew.mask);
                ok(pTreeView->itemOld.mask == 0,
                   "got wrong mask %x\n", pTreeView->itemOld.mask);

                if (g_get_from_expand)
                {
                  g_item_expanding.mask = TVIF_STATE;
                  g_item_expanding.hItem = hRoot;
                  ret = SendMessageA(pHdr->hwndFrom, TVM_GETITEMA, 0, (LPARAM)&g_item_expanding);
                  ok(ret == TRUE, "got %lu\n", ret);
                }
                break;
1212
              }
1213
            case TVN_ITEMEXPANDEDA:
1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226
                ok(pTreeView->itemNew.mask & TVIF_STATE, "got wrong mask %x\n", pTreeView->itemNew.mask);
                ok(pTreeView->itemNew.state & (TVIS_EXPANDED|TVIS_EXPANDEDONCE),
                   "got wrong mask %x\n", pTreeView->itemNew.mask);
                ok(pTreeView->itemOld.mask == 0,
                   "got wrong mask %x\n", pTreeView->itemOld.mask);

                if (g_get_from_expand)
                {
                  g_item_expanded.mask = TVIF_STATE;
                  g_item_expanded.hItem = hRoot;
                  ret = SendMessageA(pHdr->hwndFrom, TVM_GETITEMA, 0, (LPARAM)&g_item_expanded);
                  ok(ret == TRUE, "got %lu\n", ret);
                }
1227 1228
                if (g_get_rect_in_expand)
                {
1229 1230
                  visibleItem = (HTREEITEM)SendMessageA(pHdr->hwndFrom, TVM_GETNEXTITEM,
                          TVGN_FIRSTVISIBLE, 0);
1231 1232
                  ok(pTreeView->itemNew.hItem == visibleItem, "expanded item == first visible item\n");
                  *(HTREEITEM*)&rect = visibleItem;
1233 1234
                  ok(SendMessageA(pHdr->hwndFrom, TVM_GETITEMRECT, TRUE, (LPARAM)&rect),
                          "Failed to get rect for first visible item.\n");
1235 1236
                  visibleItem = (HTREEITEM)SendMessageA(pHdr->hwndFrom, TVM_GETNEXTITEM,
                          TVGN_NEXTVISIBLE, (LPARAM)visibleItem);
1237 1238
                  *(HTREEITEM*)&rect = visibleItem;
                  ok(visibleItem != NULL, "There must be a visible item after the first visisble item.\n");
1239 1240
                  ok(SendMessageA(pHdr->hwndFrom, TVM_GETITEMRECT, TRUE, (LPARAM)&rect),
                          "Failed to get rect for second visible item.\n");
1241
                }
1242
                break;
1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257
            case TVN_DELETEITEMA:
            {
                struct message item;

                ok(pTreeView->itemNew.mask == 0, "got wrong mask 0x%x\n", pTreeView->itemNew.mask);

                ok(pTreeView->itemOld.mask == (TVIF_HANDLE | TVIF_PARAM), "got wrong mask 0x%x\n", pTreeView->itemOld.mask);
                ok(pTreeView->itemOld.hItem != NULL, "got %p\n", pTreeView->itemOld.hItem);

                memset(&item, 0, sizeof(item));
                item.lParam = (LPARAM)pTreeView->itemOld.hItem;
                add_message(item_sequence, 0, &item);

                break;
            }
1258 1259 1260 1261
            case NM_CUSTOMDRAW:
            {
                NMTVCUSTOMDRAW *nmcd = (NMTVCUSTOMDRAW*)lParam;
                COLORREF c0ffee = RGB(0xc0,0xff,0xee), cafe = RGB(0xca,0xfe,0x00);
1262
                COLORREF text = GetTextColor(nmcd->nmcd.hdc), bkgnd = GetBkColor(nmcd->nmcd.hdc);
1263 1264 1265 1266 1267 1268 1269 1270 1271 1272

                msg.flags |= custdraw;
                msg.stage = nmcd->nmcd.dwDrawStage;
                add_message(sequences, PARENT_CD_SEQ_INDEX, &msg);

                switch (msg.stage)
                {
                case CDDS_PREPAINT:
                    return CDRF_NOTIFYITEMDRAW|CDRF_NOTIFYITEMERASE|CDRF_NOTIFYPOSTPAINT;
                case CDDS_ITEMPREPAINT:
1273 1274 1275 1276
                    ok(text == nmcd->clrText || (g_v6 && nmcd->clrText == 0xffffffff),
                       "got %08x vs %08x\n", text, nmcd->clrText);
                    ok(bkgnd == nmcd->clrTextBk || (g_v6 && nmcd->clrTextBk == 0xffffffff),
                       "got %08x vs %08x\n", bkgnd, nmcd->clrTextBk);
1277
                    nmcd->clrText = cafe;
1278 1279 1280
                    nmcd->clrTextBk = c0ffee;
                    SetTextColor(nmcd->nmcd.hdc, c0ffee);
                    SetBkColor(nmcd->nmcd.hdc, cafe);
1281 1282 1283 1284 1285
                    if (g_customdraw_font)
                        SelectObject(nmcd->nmcd.hdc, g_customdraw_font);
                    return CDRF_NOTIFYPOSTPAINT|CDRF_NEWFONT;
                case CDDS_ITEMPOSTPAINT:
                    /* at the point of post paint notification colors are already restored */
1286 1287 1288 1289
                    ok(nmcd->clrText == cafe, "got 0%x\n", nmcd->clrText);
                    ok(nmcd->clrTextBk == c0ffee, "got 0%x\n", nmcd->clrTextBk);
                    ok(text != cafe, "got 0%x\n", text);
                    ok(bkgnd != c0ffee, "got 0%x\n", bkgnd);
1290 1291 1292 1293 1294 1295 1296 1297 1298
                    if (g_customdraw_font)
                        ok(GetCurrentObject(nmcd->nmcd.hdc, OBJ_FONT) != g_customdraw_font, "got %p\n",
                           GetCurrentObject(nmcd->nmcd.hdc, OBJ_FONT));
                    break;
                default:
                    ;
                }
                break;
            }
1299 1300
            }
        }
1301
        break;
1302
    }
1303

1304 1305 1306 1307
    case WM_DESTROY:
        PostQuitMessage(0);
        break;
    }
1308 1309 1310 1311 1312 1313

    defwndproc_counter++;
    ret = DefWindowProcA(hWnd, message, wParam, lParam);
    defwndproc_counter--;

    return ret;
1314 1315
}

1316
static void test_expandinvisible(void)
1317 1318 1319 1320 1321 1322
{
    static CHAR nodeText[][5] = {"0", "1", "2", "3", "4"};
    TVINSERTSTRUCTA ins;
    HTREEITEM node[5];
    RECT dummyRect;
    BOOL nodeVisible;
1323
    LRESULT ret;
1324
    HWND hTree;
1325

1326
    hTree = create_treeview_control(0);
1327

1328
    /* The test builds the following tree and expands node 1, while node 0 is collapsed.
1329 1330 1331 1332 1333 1334 1335 1336 1337
     *
     * 0
     * |- 1
     * |  |- 2
     * |  |- 3
     * |- 4
     *
     */

1338
    ret = SendMessageA(hTree, TVM_DELETEITEM, 0, (LPARAM)TVI_ROOT);
1339
    ok(ret == TRUE, "ret\n");
1340 1341 1342 1343
    ins.hParent = TVI_ROOT;
    ins.hInsertAfter = TVI_ROOT;
    U(ins).item.mask = TVIF_TEXT;
    U(ins).item.pszText = nodeText[0];
1344
    node[0] = TreeView_InsertItemA(hTree, &ins);
1345
    ok(node[0] != NULL, "failed to set node[0]\n");
1346 1347 1348 1349 1350 1351

    ins.hInsertAfter = TVI_LAST;
    U(ins).item.mask = TVIF_TEXT;
    ins.hParent = node[0];

    U(ins).item.pszText = nodeText[1];
1352
    node[1] = TreeView_InsertItemA(hTree, &ins);
1353
    ok(node[1] != NULL, "failed to set node[1]\n");
1354
    U(ins).item.pszText = nodeText[4];
1355
    node[4] = TreeView_InsertItemA(hTree, &ins);
1356
    ok(node[4] != NULL, "failed to set node[4]\n");
1357 1358 1359 1360

    ins.hParent = node[1];

    U(ins).item.pszText = nodeText[2];
1361
    node[2] = TreeView_InsertItemA(hTree, &ins);
1362
    ok(node[2] != NULL, "failed to set node[2]\n");
1363
    U(ins).item.pszText = nodeText[3];
1364
    node[3] = TreeView_InsertItemA(hTree, &ins);
1365
    ok(node[3] != NULL, "failed to set node[3]\n");
1366

1367 1368
    *(HTREEITEM *)&dummyRect = node[1];
    nodeVisible = SendMessageA(hTree, TVM_GETITEMRECT, FALSE, (LPARAM)&dummyRect);
1369
    ok(!nodeVisible, "Node 1 should not be visible.\n");
1370 1371
    *(HTREEITEM *)&dummyRect = node[2];
    nodeVisible = SendMessageA(hTree, TVM_GETITEMRECT, FALSE, (LPARAM)&dummyRect);
1372
    ok(!nodeVisible, "Node 2 should not be visible.\n");
1373 1374
    *(HTREEITEM *)&dummyRect = node[3];
    nodeVisible = SendMessageA(hTree, TVM_GETITEMRECT, FALSE, (LPARAM)&dummyRect);
1375
    ok(!nodeVisible, "Node 3 should not be visible.\n");
1376 1377
    *(HTREEITEM *)&dummyRect = node[4];
    nodeVisible = SendMessageA(hTree, TVM_GETITEMRECT, FALSE, (LPARAM)&dummyRect);
1378 1379
    ok(!nodeVisible, "Node 4 should not be visible.\n");

1380
    ok(SendMessageA(hTree, TVM_EXPAND, TVE_EXPAND, (LPARAM)node[1]), "Expand of node 1 failed.\n");
1381

1382 1383
    *(HTREEITEM *)&dummyRect = node[1];
    nodeVisible = SendMessageA(hTree, TVM_GETITEMRECT, FALSE, (LPARAM)&dummyRect);
1384
    ok(!nodeVisible, "Node 1 should not be visible.\n");
1385 1386
    *(HTREEITEM *)&dummyRect = node[2];
    nodeVisible = SendMessageA(hTree, TVM_GETITEMRECT, FALSE, (LPARAM)&dummyRect);
1387
    ok(!nodeVisible, "Node 2 should not be visible.\n");
1388 1389
    *(HTREEITEM *)&dummyRect = node[3];
    nodeVisible = SendMessageA(hTree, TVM_GETITEMRECT, FALSE, (LPARAM)&dummyRect);
1390
    ok(!nodeVisible, "Node 3 should not be visible.\n");
1391 1392
    *(HTREEITEM *)&dummyRect = node[4];
    nodeVisible = SendMessageA(hTree, TVM_GETITEMRECT, FALSE, (LPARAM)&dummyRect);
1393 1394
    ok(!nodeVisible, "Node 4 should not be visible.\n");

1395 1396
    DestroyWindow(hTree);
}
1397

1398 1399 1400 1401
static void test_itemedit(void)
{
    DWORD r;
    HWND edit;
1402
    TVITEMA item;
1403
    CHAR buffA[20];
1404
    HWND hTree;
1405

1406
    hTree = create_treeview_control(0);
1407 1408
    fill_tree(hTree);

1409
    /* try with null item */
1410
    edit = (HWND)SendMessageA(hTree, TVM_EDITLABELA, 0, 0);
1411 1412
    ok(!IsWindow(edit), "Expected valid handle\n");

1413
    /* trigger edit */
1414
    edit = (HWND)SendMessageA(hTree, TVM_EDITLABELA, 0, (LPARAM)hRoot);
1415
    ok(IsWindow(edit), "Expected valid handle\n");
1416 1417
    /* item shouldn't be selected automatically after TVM_EDITLABELA */
    r = SendMessageA(hTree, TVM_GETITEMSTATE, (WPARAM)hRoot, TVIS_SELECTED);
1418
    expect(0, r);
1419
    /* try to cancel with wrong edit handle */
1420
    r = SendMessageA(hTree, WM_COMMAND, MAKEWPARAM(0, EN_KILLFOCUS), 0);
1421 1422
    expect(0, r);
    ok(IsWindow(edit), "Expected edit control to be valid\n");
1423
    r = SendMessageA(hTree, WM_COMMAND, MAKEWPARAM(0, EN_KILLFOCUS), (LPARAM)edit);
1424 1425
    expect(0, r);
    ok(!IsWindow(edit), "Expected edit control to be destroyed\n");
1426
    /* try to cancel without creating edit */
1427
    r = SendMessageA(hTree, WM_COMMAND, MAKEWPARAM(0, EN_KILLFOCUS), 0);
1428 1429 1430
    expect(0, r);

    /* try to cancel with wrong (not null) handle */
1431
    edit = (HWND)SendMessageA(hTree, TVM_EDITLABELA, 0, (LPARAM)hRoot);
1432
    ok(IsWindow(edit), "Expected valid handle\n");
1433
    r = SendMessageA(hTree, WM_COMMAND, MAKEWPARAM(0, EN_KILLFOCUS), (LPARAM)hTree);
1434 1435
    expect(0, r);
    ok(IsWindow(edit), "Expected edit control to be valid\n");
1436
    r = SendMessageA(hTree, WM_COMMAND, MAKEWPARAM(0, EN_KILLFOCUS), (LPARAM)edit);
1437
    expect(0, r);
1438

1439
    /* remove selection after starting edit */
1440
    r = SendMessageA(hTree, TVM_SELECTITEM, TVGN_CARET, (LPARAM)hRoot);
1441
    expect(TRUE, r);
1442
    edit = (HWND)SendMessageA(hTree, TVM_EDITLABELA, 0, (LPARAM)hRoot);
1443
    ok(IsWindow(edit), "Expected valid handle\n");
1444
    r = SendMessageA(hTree, TVM_SELECTITEM, TVGN_CARET, 0);
1445 1446
    expect(TRUE, r);
    /* alter text */
1447
    strcpy(buffA, "x");
1448
    r = SendMessageA(edit, WM_SETTEXT, 0, (LPARAM)buffA);
1449
    expect(TRUE, r);
1450
    r = SendMessageA(hTree, WM_COMMAND, MAKEWPARAM(0, EN_KILLFOCUS), (LPARAM)edit);
1451
    expect(0, r);
1452
    ok(!IsWindow(edit), "Expected edit control to be destroyed\n");
1453 1454 1455
    /* check that text is saved */
    item.mask = TVIF_TEXT;
    item.hItem = hRoot;
1456 1457
    item.pszText = buffA;
    item.cchTextMax = sizeof(buffA)/sizeof(CHAR);
1458
    r = SendMessageA(hTree, TVM_GETITEMA, 0, (LPARAM)&item);
1459
    expect(TRUE, r);
1460 1461 1462 1463 1464 1465
    ok(!strcmp("x", buffA), "Expected item text to change\n");

    /* try A/W messages */
    edit = (HWND)SendMessageA(hTree, TVM_EDITLABELA, 0, (LPARAM)hRoot);
    ok(IsWindow(edit), "Expected valid handle\n");
    ok(IsWindowUnicode(edit), "got ansi window\n");
1466
    r = SendMessageA(hTree, WM_COMMAND, MAKEWPARAM(0, EN_KILLFOCUS), (LPARAM)edit);
1467 1468 1469 1470 1471 1472
    expect(0, r);
    ok(!IsWindow(edit), "expected invalid handle\n");

    edit = (HWND)SendMessageA(hTree, TVM_EDITLABELW, 0, (LPARAM)hRoot);
    ok(IsWindow(edit), "Expected valid handle\n");
    ok(IsWindowUnicode(edit), "got ansi window\n");
1473
    r = SendMessageA(hTree, WM_COMMAND, MAKEWPARAM(0, EN_KILLFOCUS), (LPARAM)edit);
1474 1475 1476 1477 1478 1479 1480 1481 1482
    expect(0, r);

    /* alter text during TVM_BEGINLABELEDIT, check that it's preserved */
    strcpy(buffA, "<root>");

    item.mask = TVIF_TEXT;
    item.hItem = hRoot;
    item.pszText = buffA;
    item.cchTextMax = 0;
1483
    r = SendMessageA(hTree, TVM_SETITEMA, 0, (LPARAM)&item);
1484 1485 1486 1487 1488 1489 1490 1491 1492
    expect(TRUE, r);

    g_beginedit_alter_text = TRUE;
    edit = (HWND)SendMessageA(hTree, TVM_EDITLABELA, 0, (LPARAM)hRoot);
    ok(IsWindow(edit), "Expected valid handle\n");
    g_beginedit_alter_text = FALSE;

    GetWindowTextA(edit, buffA, sizeof(buffA)/sizeof(CHAR));
    ok(!strcmp(buffA, "<edittextaltered>"), "got string %s\n", buffA);
1493 1494 1495 1496

    DestroyWindow(hTree);
}

1497 1498 1499 1500 1501
static void test_treeview_classinfo(void)
{
    WNDCLASSA cls;

    memset(&cls, 0, sizeof(cls));
1502
    GetClassInfoA(GetModuleHandleA("comctl32.dll"), WC_TREEVIEWA, &cls);
1503 1504 1505 1506 1507
    ok(cls.hbrBackground == NULL, "Expected NULL background brush, got %p\n", cls.hbrBackground);
    ok(cls.style == (CS_GLOBALCLASS | CS_DBLCLKS), "Expected got %x\n", cls.style);
    expect(0, cls.cbClsExtra);
}

1508 1509 1510 1511 1512
static void test_get_linecolor(void)
{
    COLORREF clr;
    HWND hTree;

1513
    hTree = create_treeview_control(0);
1514 1515

    /* newly created control has default color */
1516
    clr = SendMessageA(hTree, TVM_GETLINECOLOR, 0, 0);
1517 1518 1519 1520
    if (clr == 0)
        win_skip("TVM_GETLINECOLOR is not supported on comctl32 < 5.80\n");
    else
        expect(CLR_DEFAULT, clr);
1521 1522 1523 1524 1525 1526 1527 1528 1529

    DestroyWindow(hTree);
}

static void test_get_insertmarkcolor(void)
{
    COLORREF clr;
    HWND hTree;

1530
    hTree = create_treeview_control(0);
1531 1532

    /* newly created control has default color */
1533
    clr = SendMessageA(hTree, TVM_GETINSERTMARKCOLOR, 0, 0);
1534 1535 1536 1537
    if (clr == 0)
        win_skip("TVM_GETINSERTMARKCOLOR is not supported on comctl32 < 5.80\n");
    else
        expect(CLR_DEFAULT, clr);
1538 1539 1540 1541

    DestroyWindow(hTree);
}

1542 1543
static void test_expandnotify(void)
{
1544
    HTREEITEM hitem;
1545 1546 1547 1548
    HWND hTree;
    BOOL ret;
    TVITEMA item;

1549
    hTree = create_treeview_control(0);
1550 1551 1552 1553 1554 1555 1556
    fill_tree(hTree);

    item.hItem = hRoot;
    item.mask = TVIF_STATE;

    item.state = TVIS_EXPANDED;
    ret = SendMessageA(hTree, TVM_GETITEMA, 0, (LPARAM)&item);
1557
    expect(TRUE, ret);
1558 1559 1560 1561
    ok((item.state & TVIS_EXPANDED) == 0, "expected collapsed\n");

    /* preselect root node here */
    ret = SendMessageA(hTree, TVM_SELECTITEM, TVGN_CARET, (LPARAM)hRoot);
1562
    expect(TRUE, ret);
1563

1564 1565
    flush_sequences(sequences, NUM_MSG_SEQUENCES);
    ret = SendMessageA(hTree, TVM_EXPAND, TVE_COLLAPSE, (LPARAM)hRoot);
1566
    expect(FALSE, ret);
1567 1568
    ok_sequence(sequences, PARENT_SEQ_INDEX, empty_seq, "no collapse notifications", FALSE);

1569 1570 1571 1572 1573 1574
    g_get_from_expand = TRUE;
    /* expand */
    flush_sequences(sequences, NUM_MSG_SEQUENCES);
    g_item_expanding.state = 0xdeadbeef;
    g_item_expanded.state = 0xdeadbeef;
    ret = SendMessageA(hTree, TVM_EXPAND, TVE_EXPAND, (LPARAM)hRoot);
1575
    expect(TRUE, ret);
1576 1577 1578 1579 1580 1581 1582 1583 1584 1585
    ok(g_item_expanding.state == TVIS_SELECTED, "got state on TVN_ITEMEXPANDING 0x%08x\n",
       g_item_expanding.state);
    ok(g_item_expanded.state == (TVIS_SELECTED|TVIS_EXPANDED), "got state on TVN_ITEMEXPANDED 0x%08x\n",
       g_item_expanded.state);
    ok_sequence(sequences, PARENT_SEQ_INDEX, parent_expand_seq, "expand notifications", FALSE);
    g_get_from_expand = FALSE;

    /* check that it's expanded */
    item.state = TVIS_EXPANDED;
    ret = SendMessageA(hTree, TVM_GETITEMA, 0, (LPARAM)&item);
1586
    expect(TRUE, ret);
1587 1588 1589 1590 1591
    ok((item.state & TVIS_EXPANDED) == TVIS_EXPANDED, "expected expanded\n");

    /* collapse */
    flush_sequences(sequences, NUM_MSG_SEQUENCES);
    ret = SendMessageA(hTree, TVM_EXPAND, TVE_COLLAPSE, (LPARAM)hRoot);
1592
    expect(TRUE, ret);
1593 1594
    item.state = TVIS_EXPANDED;
    ret = SendMessageA(hTree, TVM_GETITEMA, 0, (LPARAM)&item);
1595
    expect(TRUE, ret);
1596
    ok((item.state & TVIS_EXPANDED) == 0, "expected collapsed\n");
1597
    /* all further collapse/expand attempts won't produce any notifications,
1598 1599 1600
       the only way is to reset with all children removed */
    ok_sequence(sequences, PARENT_SEQ_INDEX, empty_seq, "collapse after expand notifications", FALSE);

1601 1602 1603 1604
    /* try to toggle child that doesn't have children itself */
    flush_sequences(sequences, NUM_MSG_SEQUENCES);
    ret = SendMessageA(hTree, TVM_EXPAND, TVE_TOGGLE, (LPARAM)hChild);
    expect(FALSE, ret);
1605
    ok_sequence(sequences, PARENT_SEQ_INDEX, empty_seq, "toggle node without children", FALSE);
1606

1607 1608
    DestroyWindow(hTree);

1609
    /* test TVM_GETITEMRECT inside TVN_ITEMEXPANDED notification */
1610
    hTree = create_treeview_control(0);
1611 1612
    fill_tree(hTree);
    g_get_rect_in_expand = TRUE;
1613
    ret = SendMessageA(hTree, TVM_SELECTITEM, TVGN_CARET, (LPARAM)hChild);
1614
    expect(TRUE, ret);
1615
    g_get_rect_in_expand = FALSE;
1616 1617 1618 1619 1620 1621 1622 1623 1624 1625 1626 1627 1628 1629 1630 1631 1632

    DestroyWindow(hTree);

    /* TVE_TOGGLE acts as any other TVM_EXPAND */
    hTree = create_treeview_control(0);
    fill_tree(hTree);

    flush_sequences(sequences, NUM_MSG_SEQUENCES);
    ret = SendMessageA(hTree, TVM_EXPAND, TVE_TOGGLE, (LPARAM)hRoot);
    expect(TRUE, ret);
    ok_sequence(sequences, PARENT_SEQ_INDEX, parent_expand_seq, "toggle node (expand)", FALSE);

    /* toggle again - no notifications */
    flush_sequences(sequences, NUM_MSG_SEQUENCES);
    ret = SendMessageA(hTree, TVM_EXPAND, TVE_TOGGLE, (LPARAM)hRoot);
    expect(TRUE, ret);
    ok_sequence(sequences, PARENT_SEQ_INDEX, empty_seq, "toggle node (collapse)", FALSE);
1633

1634
    DestroyWindow(hTree);
1635 1636 1637 1638 1639 1640 1641 1642 1643

    /* some keyboard events are also translated to expand */
    hTree = create_treeview_control(0);
    fill_tree(hTree);

    /* preselect root node here */
    ret = SendMessageA(hTree, TVM_SELECTITEM, TVGN_CARET, (LPARAM)hRoot);
    expect(TRUE, ret);

1644 1645 1646 1647 1648 1649 1650 1651 1652 1653 1654 1655 1656 1657 1658 1659 1660 1661 1662 1663 1664 1665 1666 1667 1668 1669 1670 1671 1672 1673 1674 1675 1676 1677 1678 1679
    g_get_from_expand = TRUE;
    flush_sequences(sequences, NUM_MSG_SEQUENCES);
    ret = SendMessageA(hTree, WM_KEYDOWN, VK_ADD, 0);
    expect(FALSE, ret);
    ok_sequence(sequences, PARENT_SEQ_INDEX, parent_expand_kb_seq, "expand node", FALSE);
    ok(g_item_expanding.state == TVIS_SELECTED, "got state on TVN_ITEMEXPANDING 0x%08x\n",
       g_item_expanding.state);
    ok(g_item_expanded.state == (TVIS_SELECTED|TVIS_EXPANDED), "got state on TVN_ITEMEXPANDED 0x%08x\n",
       g_item_expanded.state);

    flush_sequences(sequences, NUM_MSG_SEQUENCES);
    ret = SendMessageA(hTree, WM_KEYDOWN, VK_ADD, 0);
    expect(FALSE, ret);
    ok_sequence(sequences, PARENT_SEQ_INDEX, parent_expand_kb_seq, "expand node again", FALSE);
    ok(g_item_expanding.state == (TVIS_SELECTED|TVIS_EXPANDED|TVIS_EXPANDEDONCE), "got state on TVN_ITEMEXPANDING 0x%08x\n",
       g_item_expanding.state);
    ok(g_item_expanded.state == (TVIS_SELECTED|TVIS_EXPANDED|TVIS_EXPANDEDONCE), "got state on TVN_ITEMEXPANDED 0x%08x\n",
       g_item_expanded.state);

    flush_sequences(sequences, NUM_MSG_SEQUENCES);
    ret = SendMessageA(hTree, WM_KEYDOWN, VK_SUBTRACT, 0);
    expect(FALSE, ret);
    ok_sequence(sequences, PARENT_SEQ_INDEX, parent_expand_kb_seq, "collapse node", FALSE);
    ok(g_item_expanding.state == (TVIS_SELECTED|TVIS_EXPANDED|TVIS_EXPANDEDONCE), "got state on TVN_ITEMEXPANDING 0x%08x\n",
       g_item_expanding.state);
    ok(g_item_expanded.state == (TVIS_SELECTED|TVIS_EXPANDEDONCE), "got state on TVN_ITEMEXPANDED 0x%08x\n",
       g_item_expanded.state);

    flush_sequences(sequences, NUM_MSG_SEQUENCES);
    ret = SendMessageA(hTree, WM_KEYDOWN, VK_SUBTRACT, 0);
    expect(FALSE, ret);
    ok_sequence(sequences, PARENT_SEQ_INDEX, parent_collapse_2nd_kb_seq, "collapse node again", FALSE);
    ok(g_item_expanding.state == (TVIS_SELECTED|TVIS_EXPANDEDONCE), "got state on TVN_ITEMEXPANDING 0x%08x\n",
       g_item_expanding.state);
    g_get_from_expand = FALSE;

1680 1681 1682 1683 1684 1685 1686 1687 1688 1689 1690 1691 1692
    flush_sequences(sequences, NUM_MSG_SEQUENCES);
    ret = SendMessageA(hTree, WM_KEYDOWN, VK_ADD, 0);
    expect(FALSE, ret);
    ok_sequence(sequences, PARENT_SEQ_INDEX, parent_expand_kb_seq, "expand node", FALSE);

    /* go to child */
    ret = SendMessageA(hTree, WM_KEYDOWN, VK_RIGHT, 0);
    expect(FALSE, ret);

    /* try to expand child that doesn't have children itself */
    flush_sequences(sequences, NUM_MSG_SEQUENCES);
    ret = SendMessageA(hTree, WM_KEYDOWN, VK_ADD, 0);
    expect(FALSE, ret);
1693
    ok_sequence(sequences, PARENT_SEQ_INDEX, parent_expand_empty_kb_seq, "expand node with no children", FALSE);
1694

1695 1696 1697 1698 1699 1700 1701 1702 1703 1704 1705 1706 1707 1708 1709 1710
    /* stay on current selection and set non-zero children count */
    hitem = (HTREEITEM)SendMessageA(hTree, TVM_GETNEXTITEM, TVGN_CARET, 0);
    ok(hitem != NULL, "got %p\n", hitem);

    item.hItem = hitem;
    item.mask = TVIF_CHILDREN;
    item.cChildren = 0x80000000;

    ret = SendMessageA(hTree, TVM_SETITEMA, 0, (LPARAM)&item);
    expect(TRUE, ret);

    flush_sequences(sequences, NUM_MSG_SEQUENCES);
    ret = SendMessageA(hTree, WM_KEYDOWN, VK_ADD, 0);
    expect(FALSE, ret);
    ok_sequence(sequences, PARENT_SEQ_INDEX, parent_collapse_2nd_kb_seq, "expand node with children", FALSE);

1711
    DestroyWindow(hTree);
1712 1713
}

1714 1715
static void test_expandedimage(void)
{
1716
    TVITEMEXA item;
1717 1718 1719
    HWND hTree;
    BOOL ret;

1720
    hTree = create_treeview_control(0);
1721 1722 1723 1724 1725
    fill_tree(hTree);

    item.mask = TVIF_EXPANDEDIMAGE;
    item.iExpandedImage = 1;
    item.hItem = hRoot;
1726
    ret = SendMessageA(hTree, TVM_SETITEMA, 0, (LPARAM)&item);
1727 1728 1729 1730 1731
    ok(ret, "got %d\n", ret);

    item.mask = TVIF_EXPANDEDIMAGE;
    item.iExpandedImage = -1;
    item.hItem = hRoot;
1732
    ret = SendMessageA(hTree, TVM_GETITEMA, 0, (LPARAM)&item);
1733 1734 1735 1736 1737 1738 1739 1740 1741 1742 1743 1744 1745
    ok(ret, "got %d\n", ret);

    if (item.iExpandedImage != 1)
    {
        win_skip("TVIF_EXPANDEDIMAGE not supported\n");
        DestroyWindow(hTree);
        return;
    }

    /* test for default iExpandedImage value */
    item.mask = TVIF_EXPANDEDIMAGE;
    item.iExpandedImage = -1;
    item.hItem = hChild;
1746
    ret = SendMessageA(hTree, TVM_GETITEMA, 0, (LPARAM)&item);
1747 1748 1749 1750 1751 1752
    ok(ret, "got %d\n", ret);
    ok(item.iExpandedImage == (WORD)I_IMAGENONE, "got %d\n", item.iExpandedImage);

    DestroyWindow(hTree);
}

1753 1754 1755
static void test_TVS_SINGLEEXPAND(void)
{
    HWND hTree;
1756 1757 1758 1759 1760
    HTREEITEM alpha, bravo, charlie, delta, echo, foxtrot, golf, hotel, india, juliet;
    TVINSERTSTRUCTA ins;
    char foo[] = "foo";
    char context[32];
    int i;
1761 1762
    BOOL ret;

1763 1764 1765 1766 1767 1768 1769 1770 1771 1772 1773 1774 1775 1776 1777 1778 1779 1780 1781 1782 1783 1784 1785 1786 1787 1788 1789 1790 1791 1792 1793 1794 1795 1796 1797 1798 1799 1800 1801 1802
    /* build a fairly complex tree
     * - TVI_ROOT
     *   - alpha
     *     - bravo
     *       - charlie
     *     - delta
     *       - echo
     *   - foxtrot
     *     - golf
     *       - hotel
     *       - india
     *     - juliet
     */
    struct
    {
        HTREEITEM *handle;
        HTREEITEM *parent;
        UINT final_state;
    }
    items[] =
    {
        { &alpha,    NULL,      TVIS_EXPANDEDONCE               },
        { &bravo,    &alpha,    TVIS_EXPANDEDONCE               },
        { &charlie,  &bravo,    0                               },
        { &delta,    &alpha,    TVIS_EXPANDEDONCE               },
        { &echo,     &delta,    0                               },
        { &foxtrot,  NULL,      TVIS_EXPANDEDONCE|TVIS_EXPANDED },
        { &golf,     &foxtrot,  TVIS_EXPANDEDONCE|TVIS_EXPANDED },
        { &hotel,    &golf,     0                               },
        { &india,    &golf,     TVIS_SELECTED                   },
        { &juliet,   &foxtrot,  0                               }
    };

    struct
    {
        HTREEITEM *select;
        const struct message *sequence;
    }
    sequence_tests[] =
    {
1803 1804 1805 1806 1807 1808 1809 1810 1811
        { &alpha,    parent_singleexpand_seq0 },
        { &bravo,    parent_singleexpand_seq1 },
        { &delta,    parent_singleexpand_seq2 },
        { &foxtrot,  parent_singleexpand_seq3 },
        { &alpha,    parent_singleexpand_seq4 },
        { &golf,     parent_singleexpand_seq5 },
        { &hotel,    parent_singleexpand_seq6 },
        { &india,    parent_singleexpand_seq7 },
        { &india,    empty_seq }
1812 1813
    };

1814
    hTree = create_treeview_control(0);
1815
    SetWindowLongA(hTree, GWL_STYLE, GetWindowLongA(hTree, GWL_STYLE) | TVS_SINGLEEXPAND);
1816
    /* to avoid painting related notifications */
1817
    ShowWindow(hTree, SW_HIDE);
1818 1819 1820 1821 1822 1823 1824 1825
    for (i = 0; i < sizeof(items)/sizeof(items[0]); i++)
    {
        ins.hParent = items[i].parent ? *items[i].parent : TVI_ROOT;
        ins.hInsertAfter = TVI_FIRST;
        U(ins).item.mask = TVIF_TEXT;
        U(ins).item.pszText = foo;
        *items[i].handle = TreeView_InsertItemA(hTree, &ins);
    }
1826

1827 1828 1829 1830 1831 1832
    for (i = 0; i < sizeof(sequence_tests)/sizeof(sequence_tests[0]); i++)
    {
        flush_sequences(sequences, NUM_MSG_SEQUENCES);
        ret = SendMessageA(hTree, TVM_SELECTITEM, TVGN_CARET, (LPARAM)(*sequence_tests[i].select));
        ok(ret, "got %d\n", ret);
        sprintf(context, "singleexpand notifications %d", i);
1833
        ok_sequence(sequences, PARENT_SEQ_INDEX, sequence_tests[i].sequence, context, FALSE);
1834 1835 1836 1837 1838
    }

    for (i = 0; i < sizeof(items)/sizeof(items[0]); i++)
    {
        ret = SendMessageA(hTree, TVM_GETITEMSTATE, (WPARAM)(*items[i].handle), 0xFFFF);
1839 1840
        ok(ret == items[i].final_state, "singleexpand items[%d]: expected state 0x%x got 0x%x\n",
           i, items[i].final_state, ret);
1841
    }
1842

1843
    /* a workaround for NT4 that sends expand notifications when nothing is about to expand */
1844
    ret = SendMessageA(hTree, TVM_DELETEITEM, 0, (LPARAM)TVI_ROOT);
1845 1846 1847 1848 1849
    ok(ret, "got %d\n", ret);
    fill_tree(hTree);
    ret = SendMessageA(hTree, TVM_SELECTITEM, TVGN_CARET, 0);
    ok(ret, "got %d\n", ret);

1850 1851 1852
    DestroyWindow(hTree);
}

1853 1854 1855 1856 1857 1858 1859 1860
static void test_WM_PAINT(void)
{
    HWND hTree;
    COLORREF clr;
    LONG ret;
    RECT rc;
    HDC hdc;

1861
    hTree = create_treeview_control(0);
1862 1863

    clr = SendMessageA(hTree, TVM_SETBKCOLOR, 0, RGB(255, 0, 0));
1864
    ok(clr == ~0u, "got %d, expected -1\n", clr);
1865 1866 1867 1868 1869 1870 1871 1872 1873 1874 1875 1876 1877 1878 1879 1880 1881 1882 1883 1884 1885

    hdc = GetDC(hMainWnd);

    GetClientRect(hMainWnd, &rc);
    FillRect(hdc, &rc, GetStockObject(BLACK_BRUSH));

    clr = GetPixel(hdc, 1, 1);
    ok(clr == RGB(0, 0, 0), "got 0x%x\n", clr);

    ret = SendMessageA(hTree, WM_PAINT, (WPARAM)hdc, 0);
    ok(ret == 0, "got %d\n", ret);

    clr = GetPixel(hdc, 1, 1);
    ok(clr == RGB(255, 0, 0) || broken(clr == RGB(0, 0, 0)) /* win98 */,
        "got 0x%x\n", clr);

    ReleaseDC(hMainWnd, hdc);

    DestroyWindow(hTree);
}

1886 1887 1888 1889
static void test_delete_items(void)
{
    const struct message *msg;
    HWND hTree;
1890 1891
    HTREEITEM hItem1, hItem2;
    TVINSERTSTRUCTA ins;
1892 1893
    INT ret;

1894 1895 1896
    static CHAR item1[] = "Item 1";
    static CHAR item2[] = "Item 2";

1897
    hTree = create_treeview_control(0);
1898 1899 1900 1901
    fill_tree(hTree);

    /* check delete order */
    flush_sequences(item_sequence, 1);
1902
    ret = SendMessageA(hTree, TVM_DELETEITEM, 0, 0);
1903 1904 1905 1906 1907 1908 1909
    ok(ret == TRUE, "got %d\n", ret);

    msg = item_sequence[0]->sequence;
    ok(item_sequence[0]->count == 2, "expected 2 items, got %d\n", item_sequence[0]->count);

    if (item_sequence[0]->count == 2)
    {
1910 1911
      ok(msg[0].lParam == (LPARAM)hChild, "expected %p, got 0x%lx\n", hChild, msg[0].lParam);
      ok(msg[1].lParam == (LPARAM)hRoot, "expected %p, got 0x%lx\n", hRoot, msg[1].lParam);
1912 1913 1914 1915 1916 1917
    }

    ret = SendMessageA(hTree, TVM_GETCOUNT, 0, 0);
    ok(ret == 0, "got %d\n", ret);

    DestroyWindow(hTree);
1918 1919 1920 1921

    /* Regression test for a crash when deleting the first visible item while bRedraw == false. */
    hTree = create_treeview_control(0);

1922 1923 1924
    ret = SendMessageA(hTree, WM_SETREDRAW, FALSE, 0);
    ok(ret == 0, "got %d\n", ret);

1925 1926 1927 1928 1929 1930 1931 1932 1933 1934 1935 1936 1937 1938 1939 1940 1941 1942 1943 1944 1945
    ins.hParent = TVI_ROOT;
    ins.hInsertAfter = TVI_ROOT;
    U(ins).item.mask = TVIF_TEXT;
    U(ins).item.pszText = item1;
    hItem1 = TreeView_InsertItemA(hTree, &ins);
    ok(hItem1 != NULL, "InsertItem failed\n");

    ins.hParent = TVI_ROOT;
    ins.hInsertAfter = hItem1;
    U(ins).item.mask = TVIF_TEXT;
    U(ins).item.pszText = item2;
    hItem2 = TreeView_InsertItemA(hTree, &ins);
    ok(hItem2 != NULL, "InsertItem failed\n");

    ret = SendMessageA(hTree, TVM_DELETEITEM, 0, (LPARAM)hItem1);
    ok(ret == TRUE, "got %d\n", ret);

    ret = SendMessageA(hTree, WM_SETREDRAW, TRUE, 0);
    ok(ret == 0, "got %d\n", ret);

    DestroyWindow(hTree);
1946 1947
}

1948 1949 1950 1951 1952 1953 1954 1955 1956
static void test_cchildren(void)
{
    HWND hTree;
    INT ret;
    TVITEMA item;

    hTree = create_treeview_control(0);
    fill_tree(hTree);

1957
    ret = SendMessageA(hTree, TVM_DELETEITEM, 0, (LPARAM)hChild);
1958 1959 1960 1961 1962 1963 1964 1965 1966 1967 1968 1969 1970 1971 1972 1973 1974 1975 1976 1977 1978 1979 1980 1981 1982 1983 1984
    expect(TRUE, ret);

    /* check cChildren - automatic mode */
    item.hItem = hRoot;
    item.mask = TVIF_CHILDREN;

    ret = SendMessageA(hTree, TVM_GETITEMA, 0, (LPARAM)&item);
    expect(TRUE, ret);
    expect(0, item.cChildren);

    DestroyWindow(hTree);

    /* start over */
    hTree = create_treeview_control(0);
    fill_tree(hTree);

    /* turn off automatic mode by setting cChildren explicitly */
    item.hItem = hRoot;
    item.mask = TVIF_CHILDREN;

    ret = SendMessageA(hTree, TVM_GETITEMA, 0, (LPARAM)&item);
    expect(TRUE, ret);
    expect(1, item.cChildren);

    ret = SendMessageA(hTree, TVM_SETITEMA, 0, (LPARAM)&item);
    expect(TRUE, ret);

1985
    ret = SendMessageA(hTree, TVM_DELETEITEM, 0, (LPARAM)hChild);
1986 1987 1988 1989 1990 1991 1992 1993 1994 1995 1996
    expect(TRUE, ret);

    /* check cChildren */
    ret = SendMessageA(hTree, TVM_GETITEMA, 0, (LPARAM)&item);
    expect(TRUE, ret);
todo_wine
    expect(1, item.cChildren);

    DestroyWindow(hTree);
}

1997 1998 1999 2000 2001 2002 2003 2004 2005 2006 2007 2008 2009 2010 2011 2012 2013 2014 2015 2016 2017 2018 2019 2020
struct _ITEM_DATA
{
    HTREEITEM  parent; /* for root value of parent field is unidetified */
    HTREEITEM  nextsibling;
    HTREEITEM  firstchild;
};

static void _check_item(HTREEITEM item, HTREEITEM parent, HTREEITEM nextsibling, HTREEITEM firstchild, int line)
{
    struct _ITEM_DATA *data = (struct _ITEM_DATA*)item;

    ok_(__FILE__, line)(data->parent == parent, "parent %p, got %p\n", parent, data->parent);
    ok_(__FILE__, line)(data->nextsibling == nextsibling, "sibling %p, got %p\n", nextsibling, data->nextsibling);
    ok_(__FILE__, line)(data->firstchild == firstchild, "firstchild %p, got %p\n", firstchild, data->firstchild);
}

#define check_item(a, b, c, d) _check_item(a, b, c, d, __LINE__)

static void test_htreeitem_layout(void)
{
    TVINSERTSTRUCTA ins;
    HTREEITEM item1, item2;
    HWND hTree;

2021
    hTree = create_treeview_control(0);
2022 2023 2024 2025 2026 2027 2028 2029
    fill_tree(hTree);

    /* root has some special pointer in parent field */
    check_item(hRoot, ((struct _ITEM_DATA*)hRoot)->parent, 0, hChild);
    check_item(hChild, hRoot, 0, 0);

    ins.hParent = hChild;
    ins.hInsertAfter = TVI_FIRST;
2030
    U(ins).item.mask = 0;
2031
    item1 = TreeView_InsertItemA(hTree, &ins);
2032 2033 2034 2035 2036

    check_item(item1, hChild, 0, 0);

    ins.hParent = hRoot;
    ins.hInsertAfter = TVI_FIRST;
2037
    U(ins).item.mask = 0;
2038
    item2 = TreeView_InsertItemA(hTree, &ins);
2039 2040 2041

    check_item(item2, hRoot, hChild, 0);

2042
    SendMessageA(hTree, TVM_DELETEITEM, 0, (LPARAM)hChild);
2043 2044 2045 2046 2047 2048 2049

    /* without children now */
    check_item(hRoot, ((struct _ITEM_DATA*)hRoot)->parent, 0, item2);

    DestroyWindow(hTree);
}

2050 2051
static void test_TVS_CHECKBOXES(void)
{
2052 2053
    HIMAGELIST himl, himl2;
    HWND hTree, hTree2;
2054 2055
    TVITEMA item;
    DWORD ret;
2056
    MSG msg;
2057 2058 2059 2060

    hTree = create_treeview_control(0);
    fill_tree(hTree);

2061 2062 2063
    himl = (HIMAGELIST)SendMessageA(hTree, TVM_GETIMAGELIST, TVSIL_STATE, 0);
    ok(himl == NULL, "got %p\n", himl);

2064 2065 2066 2067 2068 2069 2070 2071 2072 2073 2074 2075 2076 2077 2078 2079 2080 2081
    item.hItem = hRoot;
    item.mask = TVIF_STATE;
    item.state = INDEXTOSTATEIMAGEMASK(1);
    item.stateMask = TVIS_STATEIMAGEMASK;
    ret = SendMessageA(hTree, TVM_GETITEMA, 0, (LPARAM)&item);
    expect(TRUE, ret);
    ok(item.state == 0, "got 0x%x\n", item.state);

    /* set some index for a child */
    item.hItem = hChild;
    item.mask = TVIF_STATE;
    item.state = INDEXTOSTATEIMAGEMASK(4);
    item.stateMask = TVIS_STATEIMAGEMASK;
    ret = SendMessageA(hTree, TVM_SETITEMA, 0, (LPARAM)&item);
    expect(TRUE, ret);

    /* enabling check boxes set all items to 1 state image index */
    SetWindowLongA(hTree, GWL_STYLE, GetWindowLongA(hTree, GWL_STYLE) | TVS_CHECKBOXES);
2082 2083
    himl = (HIMAGELIST)SendMessageA(hTree, TVM_GETIMAGELIST, TVSIL_STATE, 0);
    ok(himl != NULL, "got %p\n", himl);
2084

2085 2086 2087 2088
    himl2 = (HIMAGELIST)SendMessageA(hTree, TVM_GETIMAGELIST, TVSIL_STATE, 0);
    ok(himl2 != NULL, "got %p\n", himl2);
    ok(himl2 == himl, "got %p, expected %p\n", himl2, himl);

2089 2090 2091 2092 2093 2094 2095 2096 2097 2098 2099 2100 2101 2102 2103 2104
    item.hItem = hRoot;
    item.mask = TVIF_STATE;
    item.state = 0;
    item.stateMask = TVIS_STATEIMAGEMASK;
    ret = SendMessageA(hTree, TVM_GETITEMA, 0, (LPARAM)&item);
    expect(TRUE, ret);
    ok(item.state == INDEXTOSTATEIMAGEMASK(1), "got 0x%x\n", item.state);

    item.hItem = hChild;
    item.mask = TVIF_STATE;
    item.state = 0;
    item.stateMask = TVIS_STATEIMAGEMASK;
    ret = SendMessageA(hTree, TVM_GETITEMA, 0, (LPARAM)&item);
    expect(TRUE, ret);
    ok(item.state == INDEXTOSTATEIMAGEMASK(1), "got 0x%x\n", item.state);

2105 2106 2107 2108 2109 2110 2111 2112 2113 2114 2115 2116 2117 2118 2119 2120 2121 2122 2123
    /* create another control and check its checkbox list */
    hTree2 = create_treeview_control(0);
    fill_tree(hTree2);

    /* set some index for a child */
    item.hItem = hChild;
    item.mask = TVIF_STATE;
    item.state = INDEXTOSTATEIMAGEMASK(4);
    item.stateMask = TVIS_STATEIMAGEMASK;
    ret = SendMessageA(hTree2, TVM_SETITEMA, 0, (LPARAM)&item);
    expect(TRUE, ret);

    /* enabling check boxes set all items to 1 state image index */
    SetWindowLongA(hTree2, GWL_STYLE, GetWindowLongA(hTree, GWL_STYLE) | TVS_CHECKBOXES);
    himl2 = (HIMAGELIST)SendMessageA(hTree2, TVM_GETIMAGELIST, TVSIL_STATE, 0);
    ok(himl2 != NULL, "got %p\n", himl2);
    ok(himl != himl2, "got %p, expected %p\n", himl2, himl);

    DestroyWindow(hTree2);
2124 2125 2126 2127 2128
    DestroyWindow(hTree);

    /* the same, but initially created with TVS_CHECKBOXES */
    hTree = create_treeview_control(TVS_CHECKBOXES);
    fill_tree(hTree);
2129
    himl = (HIMAGELIST)SendMessageA(hTree, TVM_GETIMAGELIST, TVSIL_STATE, 0);
2130
    ok(himl == NULL, "got %p\n", himl);
2131 2132 2133 2134 2135 2136 2137 2138 2139 2140 2141 2142 2143 2144 2145 2146 2147

    item.hItem = hRoot;
    item.mask = TVIF_STATE;
    item.state = 0;
    item.stateMask = TVIS_STATEIMAGEMASK;
    ret = SendMessageA(hTree, TVM_GETITEMA, 0, (LPARAM)&item);
    expect(TRUE, ret);
    ok(item.state == INDEXTOSTATEIMAGEMASK(1), "got 0x%x\n", item.state);

    item.hItem = hChild;
    item.mask = TVIF_STATE;
    item.state = 0;
    item.stateMask = TVIS_STATEIMAGEMASK;
    ret = SendMessageA(hTree, TVM_GETITEMA, 0, (LPARAM)&item);
    expect(TRUE, ret);
    ok(item.state == INDEXTOSTATEIMAGEMASK(1), "got 0x%x\n", item.state);

2148 2149 2150 2151 2152 2153 2154 2155 2156 2157 2158 2159 2160 2161 2162 2163 2164
    item.hItem = hChild;
    item.mask = TVIF_STATE;
    item.state = INDEXTOSTATEIMAGEMASK(2);
    item.stateMask = TVIS_STATEIMAGEMASK;
    ret = SendMessageA(hTree, TVM_SETITEMA, 0, (LPARAM)&item);
    expect(TRUE, ret);

    item.hItem = hChild;
    item.mask = TVIF_STATE;
    item.state = 0;
    ret = SendMessageA(hTree, TVM_GETITEMA, 0, (LPARAM)&item);
    expect(TRUE, ret);
    ok(item.state == INDEXTOSTATEIMAGEMASK(2), "got 0x%x\n", item.state);

    while(GetMessageA(&msg, 0, 0, 0))
    {
        TranslateMessage(&msg);
2165 2166 2167 2168 2169 2170 2171 2172 2173 2174 2175 2176 2177 2178 2179 2180 2181 2182 2183 2184 2185 2186 2187 2188 2189 2190 2191 2192 2193 2194 2195 2196 2197 2198 2199 2200 2201 2202 2203 2204 2205 2206 2207 2208 2209 2210 2211 2212 2213 2214 2215
        DispatchMessageA(&msg);

        if((msg.hwnd == hTree) && (msg.message == WM_PAINT))
            break;
    }

    item.hItem = hChild;
    item.mask = TVIF_STATE;
    item.state = 0;
    ret = SendMessageA(hTree, TVM_GETITEMA, 0, (LPARAM)&item);
    expect(TRUE, ret);
    ok(item.state == INDEXTOSTATEIMAGEMASK(1), "got 0x%x\n", item.state);

    himl = (HIMAGELIST)SendMessageA(hTree, TVM_GETIMAGELIST, TVSIL_STATE, 0);
    ok(himl != NULL, "got %p\n", himl);

    DestroyWindow(hTree);

    /* check what happens if TVSIL_STATE image list is removed */
    hTree = create_treeview_control(0);
    fill_tree(hTree);
    himl = (HIMAGELIST)SendMessageA(hTree, TVM_GETIMAGELIST, TVSIL_STATE, 0);
    ok(himl == NULL, "got %p\n", himl);

    SetWindowLongA(hTree, GWL_STYLE, GetWindowLongA(hTree, GWL_STYLE) | TVS_CHECKBOXES);
    himl = (HIMAGELIST)SendMessageA(hTree, TVM_GETIMAGELIST, TVSIL_STATE, 0);
    ok(himl != NULL, "got %p\n", himl);

    himl2 = (HIMAGELIST)SendMessageA(hTree, TVM_SETIMAGELIST, TVSIL_STATE, 0);
    ok(himl2 == himl, "got %p\n", himl2);

    himl2 = (HIMAGELIST)SendMessageA(hTree, TVM_GETIMAGELIST, TVSIL_STATE, 0);
    ok(himl2 == NULL, "got %p\n", himl2);

    item.hItem = hChild;
    item.mask = TVIF_STATE;
    item.state = INDEXTOSTATEIMAGEMASK(2);
    item.stateMask = TVIS_STATEIMAGEMASK;
    ret = SendMessageA(hTree, TVM_SETITEMA, 0, (LPARAM)&item);
    expect(TRUE, ret);

    item.hItem = hChild;
    item.mask = TVIF_STATE;
    item.state = 0;
    ret = SendMessageA(hTree, TVM_GETITEMA, 0, (LPARAM)&item);
    expect(TRUE, ret);
    ok(item.state == INDEXTOSTATEIMAGEMASK(2), "got 0x%x\n", item.state);

    while(GetMessageA(&msg, 0, 0, 0))
    {
        TranslateMessage(&msg);
2216 2217 2218 2219 2220 2221 2222 2223 2224 2225 2226 2227 2228 2229 2230 2231
        DispatchMessageA(&msg);

        if((msg.hwnd == hTree) && (msg.message == WM_PAINT))
            break;
    }

    item.hItem = hChild;
    item.mask = TVIF_STATE;
    item.state = 0;
    ret = SendMessageA(hTree, TVM_GETITEMA, 0, (LPARAM)&item);
    expect(TRUE, ret);
    ok(item.state == INDEXTOSTATEIMAGEMASK(1), "got 0x%x\n", item.state);

    himl = (HIMAGELIST)SendMessageA(hTree, TVM_GETIMAGELIST, TVSIL_STATE, 0);
    ok(himl != NULL, "got %p\n", himl);

2232 2233 2234
    DestroyWindow(hTree);
}

2235 2236 2237 2238 2239 2240 2241 2242 2243 2244 2245 2246 2247 2248 2249 2250 2251 2252 2253 2254 2255 2256 2257 2258 2259 2260 2261 2262 2263 2264 2265 2266 2267 2268 2269 2270 2271 2272
static void test_TVM_GETNEXTITEM(void)
{
    HTREEITEM item;
    HWND hTree;

    hTree = create_treeview_control(0);
    fill_tree(hTree);

    item = (HTREEITEM)SendMessageA(hTree, TVM_GETNEXTITEM, TVGN_ROOT, 0);
    ok(item == hRoot, "got %p, expected %p\n", item, hRoot);

    item = (HTREEITEM)SendMessageA(hTree, TVM_GETNEXTITEM, TVGN_ROOT, (LPARAM)TVI_ROOT);
    ok(item == hRoot, "got %p, expected %p\n", item, hRoot);

    item = (HTREEITEM)SendMessageA(hTree, TVM_GETNEXTITEM, TVGN_ROOT, (LPARAM)hRoot);
    ok(item == hRoot, "got %p, expected %p\n", item, hRoot);

    item = (HTREEITEM)SendMessageA(hTree, TVM_GETNEXTITEM, TVGN_ROOT, (LPARAM)hChild);
    ok(item == hRoot, "got %p, expected %p\n", item, hRoot);

    item = (HTREEITEM)SendMessageA(hTree, TVM_GETNEXTITEM, TVGN_CHILD, 0);
    ok(item == hRoot, "got %p, expected %p\n", item, hRoot);

    item = (HTREEITEM)SendMessageA(hTree, TVM_GETNEXTITEM, TVGN_CHILD, (LPARAM)hRoot);
    ok(item == hChild, "got %p, expected %p\n", item, hChild);

    item = (HTREEITEM)SendMessageA(hTree, TVM_GETNEXTITEM, TVGN_CHILD, (LPARAM)TVI_ROOT);
    ok(item == hRoot, "got %p, expected %p\n", item, hRoot);

    item = (HTREEITEM)SendMessageA(hTree, TVM_GETNEXTITEM, TVGN_PARENT, 0);
    ok(item == NULL, "got %p\n", item);

    item = (HTREEITEM)SendMessageA(hTree, TVM_GETNEXTITEM, TVGN_PARENT, (LPARAM)hChild);
    ok(item == hRoot, "got %p, expected %p\n", item, hRoot);

    DestroyWindow(hTree);
}

2273 2274 2275 2276 2277 2278 2279 2280 2281 2282 2283
static void test_TVM_HITTEST(void)
{
    HWND hTree;
    LRESULT ret;
    RECT rc;
    TVHITTESTINFO ht;

    hTree = create_treeview_control(0);
    fill_tree(hTree);

    *(HTREEITEM*)&rc = hRoot;
2284
    ret = SendMessageA(hTree, TVM_GETITEMRECT, TRUE, (LPARAM)&rc);
2285 2286 2287 2288 2289
    expect(TRUE, (BOOL)ret);

    ht.pt.x = rc.left-1;
    ht.pt.y = rc.top;

2290
    ret = SendMessageA(hTree, TVM_HITTEST, 0, (LPARAM)&ht);
2291 2292 2293 2294 2295 2296 2297 2298
    ok((HTREEITEM)ret == hRoot, "got %p, expected %p\n", (HTREEITEM)ret, hRoot);
    ok(ht.hItem == hRoot, "got %p, expected %p\n", ht.hItem, hRoot);
    ok(ht.flags == TVHT_ONITEMBUTTON, "got %d, expected %d\n", ht.flags, TVHT_ONITEMBUTTON);

    ret = SendMessageA(hTree, TVM_EXPAND, TVE_EXPAND, (LPARAM)hRoot);
    expect(TRUE, (BOOL)ret);

    *(HTREEITEM*)&rc = hChild;
2299
    ret = SendMessageA(hTree, TVM_GETITEMRECT, TRUE, (LPARAM)&rc);
2300 2301 2302 2303 2304
    expect(TRUE, (BOOL)ret);

    ht.pt.x = rc.left-1;
    ht.pt.y = rc.top;

2305
    ret = SendMessageA(hTree, TVM_HITTEST, 0, (LPARAM)&ht);
2306 2307 2308 2309 2310 2311 2312 2313
    ok((HTREEITEM)ret == hChild, "got %p, expected %p\n", (HTREEITEM)ret, hChild);
    ok(ht.hItem == hChild, "got %p, expected %p\n", ht.hItem, hChild);
    /* Wine returns item button here, but this item has no button */
    todo_wine ok(ht.flags == TVHT_ONITEMINDENT, "got %d, expected %d\n", ht.flags, TVHT_ONITEMINDENT);

    DestroyWindow(hTree);
}

2314 2315 2316 2317 2318 2319 2320 2321 2322 2323 2324 2325 2326
static void test_WM_GETDLGCODE(void)
{
    DWORD code;
    HWND hTree;

    hTree = create_treeview_control(0);

    code = SendMessageA(hTree, WM_GETDLGCODE, VK_TAB, 0);
    ok(code == (DLGC_WANTCHARS | DLGC_WANTARROWS), "0x%08x\n", code);

    DestroyWindow(hTree);
}

2327 2328 2329 2330 2331 2332
static void test_customdraw(void)
{
    LOGFONTA lf;
    HWND hwnd;

    hwnd = create_treeview_control(0);
2333 2334
    fill_tree(hwnd);
    SendMessageA(hwnd, TVM_EXPAND, TVE_EXPAND, (WPARAM)hRoot);
2335 2336 2337 2338 2339 2340 2341 2342 2343 2344 2345 2346 2347 2348 2349

    /* create additional font, custom draw handler will select it */
    SystemParametersInfoA(SPI_GETICONTITLELOGFONT, sizeof(lf), &lf, 0);
    lf.lfHeight *= 2;
    g_customdraw_font = CreateFontIndirectA(&lf);
    flush_sequences(sequences, NUM_MSG_SEQUENCES);
    InvalidateRect(hwnd, NULL, TRUE);
    UpdateWindow(hwnd);
    ok_sequence(sequences, PARENT_CD_SEQ_INDEX, parent_cd_seq, "custom draw notifications", FALSE);
    DeleteObject(g_customdraw_font);
    g_customdraw_font = NULL;

    DestroyWindow(hwnd);
}

2350 2351 2352 2353 2354 2355 2356 2357 2358 2359 2360 2361 2362 2363 2364 2365 2366 2367 2368 2369 2370 2371 2372
static void test_WM_KEYDOWN(void)
{
    static const char *rootA = "root";
    TVINSERTSTRUCTA ins;
    HTREEITEM hRoot;
    HWND hwnd;

    hwnd = create_treeview_control(0);

    ins.hParent = TVI_ROOT;
    ins.hInsertAfter = TVI_ROOT;
    U(ins).item.mask = TVIF_TEXT;
    U(ins).item.pszText = (char*)rootA;
    hRoot = TreeView_InsertItemA(hwnd, &ins);
    ok(hRoot != NULL, "got %p\n", hRoot);

    flush_sequences(sequences, NUM_MSG_SEQUENCES);
    SendMessageA(hwnd, WM_KEYDOWN, VK_RETURN, 0);
    ok_sequence(sequences, PARENT_SEQ_INDEX, parent_vk_return_seq, "WM_KEYDOWN/VK_RETURN parent notification", TRUE);

    DestroyWindow(hwnd);
}

2373 2374 2375 2376 2377 2378 2379 2380 2381 2382 2383 2384 2385 2386 2387 2388 2389 2390 2391 2392 2393 2394 2395 2396
static void test_TVS_FULLROWSELECT(void)
{
    DWORD style;
    HWND hwnd;

    /* try to create both with TVS_HASLINES and TVS_FULLROWSELECT */
    hwnd = create_treeview_control(TVS_FULLROWSELECT);

    style = GetWindowLongA(hwnd, GWL_STYLE);
    ok((style & (TVS_FULLROWSELECT | TVS_HASLINES)) == (TVS_FULLROWSELECT | TVS_HASLINES), "got style 0x%08x\n", style);

    DestroyWindow(hwnd);

    /* create just with TVS_HASLINES, try to enable TVS_FULLROWSELECT later */
    hwnd = create_treeview_control(0);

    style = GetWindowLongA(hwnd, GWL_STYLE);
    SetWindowLongA(hwnd, GWL_STYLE, style | TVS_FULLROWSELECT);
    style = GetWindowLongA(hwnd, GWL_STYLE);
    ok(style & TVS_FULLROWSELECT, "got style 0x%08x\n", style);

    DestroyWindow(hwnd);
}

2397 2398 2399 2400 2401 2402 2403 2404 2405 2406 2407 2408 2409 2410 2411 2412 2413 2414 2415 2416 2417 2418 2419 2420 2421 2422 2423 2424 2425 2426 2427 2428 2429 2430 2431 2432 2433 2434 2435 2436 2437 2438 2439 2440 2441 2442 2443 2444 2445 2446 2447 2448 2449 2450 2451 2452 2453 2454 2455 2456 2457 2458 2459 2460 2461 2462 2463 2464 2465 2466 2467 2468 2469 2470 2471 2472 2473 2474 2475 2476 2477 2478 2479 2480 2481 2482 2483 2484 2485 2486 2487 2488 2489 2490 2491 2492 2493 2494 2495 2496 2497 2498 2499 2500 2501 2502 2503 2504 2505 2506 2507 2508 2509 2510 2511 2512 2513 2514 2515 2516 2517 2518 2519 2520 2521 2522 2523 2524 2525 2526 2527 2528 2529 2530 2531 2532 2533 2534 2535 2536 2537 2538 2539 2540 2541 2542 2543 2544 2545 2546 2547 2548 2549 2550 2551 2552 2553 2554 2555 2556 2557 2558 2559 2560 2561 2562 2563 2564 2565 2566 2567 2568 2569 2570 2571 2572 2573 2574 2575 2576 2577 2578 2579 2580 2581 2582 2583 2584 2585
static void get_item_names_string(HWND hwnd, HTREEITEM item, char *str)
{
    TVITEMA tvitem = { 0 };
    HTREEITEM child;
    char name[16];

    if (!item)
    {
        item = (HTREEITEM)SendMessageA(hwnd, TVM_GETNEXTITEM, TVGN_ROOT, 0);
        str[0] = 0;
    }

    child = (HTREEITEM)SendMessageA(hwnd, TVM_GETNEXTITEM, TVGN_CHILD, (LPARAM)item);

    tvitem.mask = TVIF_TEXT;
    tvitem.hItem = item;
    tvitem.pszText = name;
    tvitem.cchTextMax = sizeof(name);
    SendMessageA(hwnd, TVM_GETITEMA, 0, (LPARAM)&tvitem);
    strcat(str, tvitem.pszText);

    while (child != NULL)
    {
        get_item_names_string(hwnd, child, str);
        child = (HTREEITEM)SendMessageA(hwnd, TVM_GETNEXTITEM, TVGN_NEXT, (LPARAM)child);
    }
}

static void fill_treeview_sort_test(HWND hwnd)
{
    static const char *itemnames[] =
    {
        "root", "Wasp", "Caribou", "Vacuum",
        "Ocelot", "Newspaper", "Litter bin"
    };

    HTREEITEM root, children[2];
    TVINSERTSTRUCTA ins;
    unsigned i = 0;

    SendMessageA(hwnd, TVM_DELETEITEM, 0, 0);

    /* root, two children, with two children each */
    ins.hParent = TVI_ROOT;
    ins.hInsertAfter = TVI_ROOT;
    U(ins).item.mask = TVIF_TEXT;
    U(ins).item.pszText = (char *)itemnames[i++];
    root = (HTREEITEM)SendMessageA(hwnd, TVM_INSERTITEMA, 0, (LPARAM)&ins);

    ins.hParent = root;
    ins.hInsertAfter = TVI_LAST;
    U(ins).item.mask = TVIF_TEXT;
    U(ins).item.pszText = (char *)itemnames[i++];
    children[0] = (HTREEITEM)SendMessageA(hwnd, TVM_INSERTITEMA, 0, (LPARAM)&ins);

    U(ins).item.pszText = (char *)itemnames[i++];
    children[1] = (HTREEITEM)SendMessageA(hwnd, TVM_INSERTITEMA, 0, (LPARAM)&ins);

    ins.hParent = children[0];
    U(ins).item.pszText = (char *)itemnames[i++];
    SendMessageA(hwnd, TVM_INSERTITEMA, 0, (LPARAM)&ins);

    U(ins).item.pszText = (char *)itemnames[i++];
    SendMessageA(hwnd, TVM_INSERTITEMA, 0, (LPARAM)&ins);

    ins.hParent = children[1];
    U(ins).item.pszText = (char *)itemnames[i++];
    SendMessageA(hwnd, TVM_INSERTITEMA, 0, (LPARAM)&ins);

    U(ins).item.pszText = (char *)itemnames[i++];
    SendMessageA(hwnd, TVM_INSERTITEMA, 0, (LPARAM)&ins);
}

static void test_TVM_SORTCHILDREN(void)
{
    static const char *initial_order = "rootWaspVacuumOcelotCaribouNewspaperLitter bin";
    static const char *sorted_order = "rootCaribouNewspaperLitter binWaspVacuumOcelot";
    TVINSERTSTRUCTA ins;
    char buff[256];
    HTREEITEM root;
    HWND hwnd;
    BOOL ret;

    hwnd = create_treeview_control(0);

    /* call on empty tree */
    ret = SendMessageA(hwnd, TVM_SORTCHILDREN, 0, 0);
    ok(!ret, "Unexpected ret value %d\n", ret);

    ret = SendMessageA(hwnd, TVM_SORTCHILDREN, 0, (LPARAM)TVI_ROOT);
    ok(!ret, "Unexpected ret value %d\n", ret);

    /* add only root, sort from it */
    ins.hParent = TVI_ROOT;
    ins.hInsertAfter = TVI_ROOT;
    U(ins).item.mask = TVIF_TEXT;
    U(ins).item.pszText = (char *)"root";
    root = (HTREEITEM)SendMessageA(hwnd, TVM_INSERTITEMA, 0, (LPARAM)&ins);
    ok(root != NULL, "Expected root node\n");

    ret = SendMessageA(hwnd, TVM_SORTCHILDREN, 0, (LPARAM)root);
    ok(!ret, "Unexpected ret value %d\n", ret);

    ret = SendMessageA(hwnd, TVM_SORTCHILDREN, TRUE, (LPARAM)root);
    ok(!ret, "Unexpected ret value %d\n", ret);

    /* root, two children, with two children each */
    fill_treeview_sort_test(hwnd);
    get_item_names_string(hwnd, NULL, buff);
    ok(!strcmp(buff, initial_order), "Wrong initial order %s, expected %s\n", buff, initial_order);

    /* with NULL item nothing is sorted */
    fill_treeview_sort_test(hwnd);
    ret = SendMessageA(hwnd, TVM_SORTCHILDREN, 0, 0);
todo_wine
    ok(ret, "Unexpected ret value %d\n", ret);
    get_item_names_string(hwnd, NULL, buff);
    ok(!strcmp(buff, initial_order), "Wrong sorted order %s, expected %s\n", buff, initial_order);

    /* TVI_ROOT as item */
    fill_treeview_sort_test(hwnd);
    ret = SendMessageA(hwnd, TVM_SORTCHILDREN, 0, (LPARAM)TVI_ROOT);
todo_wine
    ok(ret, "Unexpected ret value %d\n", ret);
    get_item_names_string(hwnd, NULL, buff);
    ok(!strcmp(buff, initial_order), "Wrong sorted order %s, expected %s\n", buff, initial_order);

    /* zero WPARAM, item is specified */
    fill_treeview_sort_test(hwnd);
    root = (HTREEITEM)SendMessageA(hwnd, TVM_GETNEXTITEM, TVGN_ROOT, 0);
    ok(root != NULL, "Failed to get root item\n");
    ret = SendMessageA(hwnd, TVM_SORTCHILDREN, 0, (LPARAM)root);
    ok(ret, "Unexpected ret value %d\n", ret);
    get_item_names_string(hwnd, NULL, buff);
    ok(!strcmp(buff, sorted_order), "Wrong sorted order %s, expected %s\n", buff, sorted_order);

    /* non-zero WPARAM, NULL item */
    fill_treeview_sort_test(hwnd);
    ret = SendMessageA(hwnd, TVM_SORTCHILDREN, TRUE, 0);
todo_wine
    ok(ret, "Unexpected ret value %d\n", ret);
    get_item_names_string(hwnd, NULL, buff);
    ok(!strcmp(buff, initial_order), "Wrong sorted order %s, expected %s\n", buff, sorted_order);

    /* TVI_ROOT as item */
    fill_treeview_sort_test(hwnd);
    ret = SendMessageA(hwnd, TVM_SORTCHILDREN, TRUE, (LPARAM)TVI_ROOT);
todo_wine
    ok(ret, "Unexpected ret value %d\n", ret);
    get_item_names_string(hwnd, NULL, buff);
    ok(!strcmp(buff, initial_order), "Wrong sorted order %s, expected %s\n", buff, sorted_order);

    /* non-zero WPARAM, item is specified */
    fill_treeview_sort_test(hwnd);
    root = (HTREEITEM)SendMessageA(hwnd, TVM_GETNEXTITEM, TVGN_ROOT, 0);
    ok(root != NULL, "Failed to get root item\n");
    ret = SendMessageA(hwnd, TVM_SORTCHILDREN, TRUE, (LPARAM)root);
    ok(ret, "Unexpected ret value %d\n", ret);
    get_item_names_string(hwnd, NULL, buff);
    ok(!strcmp(buff, sorted_order), "Wrong sorted order %s, expected %s\n", buff, sorted_order);

    /* case insensitive comparison */
    SendMessageA(hwnd, TVM_DELETEITEM, 0, 0);

    ins.hParent = TVI_ROOT;
    ins.hInsertAfter = TVI_ROOT;
    U(ins).item.mask = TVIF_TEXT;
    U(ins).item.pszText = (char *)"root";
    root = (HTREEITEM)SendMessageA(hwnd, TVM_INSERTITEMA, 0, (LPARAM)&ins);
    ok(root != NULL, "Expected root node\n");

    ins.hParent = root;
    ins.hInsertAfter = TVI_LAST;
    U(ins).item.pszText = (char *)"I1";
    SendMessageA(hwnd, TVM_INSERTITEMA, 0, (LPARAM)&ins);

    ins.hParent = root;
    ins.hInsertAfter = TVI_LAST;
    U(ins).item.pszText = (char *)"i1";
    SendMessageA(hwnd, TVM_INSERTITEMA, 0, (LPARAM)&ins);

    ret = SendMessageA(hwnd, TVM_SORTCHILDREN, TRUE, (LPARAM)root);
    ok(ret, "Unexpected ret value %d\n", ret);
    get_item_names_string(hwnd, NULL, buff);
    ok(!strcmp(buff, "rootI1i1"), "Wrong sorted order %s\n", buff);

    DestroyWindow(hwnd);
}

2586 2587
START_TEST(treeview)
{
2588 2589
    HMODULE hComctl32;
    BOOL (WINAPI *pInitCommonControlsEx)(const INITCOMMONCONTROLSEX*);
2590 2591
    WNDCLASSA wc;
    MSG msg;
2592 2593 2594

    ULONG_PTR ctx_cookie;
    HANDLE hCtx;
2595
  
2596 2597 2598 2599 2600 2601 2602 2603 2604 2605 2606 2607
    hComctl32 = GetModuleHandleA("comctl32.dll");
    pInitCommonControlsEx = (void*)GetProcAddress(hComctl32, "InitCommonControlsEx");
    if (pInitCommonControlsEx)
    {
        INITCOMMONCONTROLSEX iccex;
        iccex.dwSize = sizeof(iccex);
        iccex.dwICC  = ICC_TREEVIEW_CLASSES;
        pInitCommonControlsEx(&iccex);
    }
    else
        InitCommonControls();

2608
    init_msg_sequences(sequences, NUM_MSG_SEQUENCES);
2609
    init_msg_sequences(item_sequence, 1);
2610 2611 2612 2613 2614 2615
  
    wc.style = CS_HREDRAW | CS_VREDRAW;
    wc.cbClsExtra = 0;
    wc.cbWndExtra = 0;
    wc.hInstance = GetModuleHandleA(NULL);
    wc.hIcon = NULL;
2616
    wc.hCursor = LoadCursorA(NULL, (LPCSTR)IDC_IBEAM);
2617 2618 2619
    wc.hbrBackground = GetSysColorBrush(COLOR_WINDOW);
    wc.lpszMenuName = NULL;
    wc.lpszClassName = "MyTestWnd";
2620
    wc.lpfnWndProc = parent_wnd_proc;
2621
    RegisterClassA(&wc);
2622 2623 2624

    hMainWnd = CreateWindowExA(0, "MyTestWnd", "Blah", WS_OVERLAPPEDWINDOW,
      CW_USEDEFAULT, CW_USEDEFAULT, 130, 105, NULL, NULL, GetModuleHandleA(NULL), 0);
2625

2626 2627
    ok(hMainWnd != NULL, "Failed to create parent window. Tests aborted.\n");
    if (!hMainWnd) return;
2628

2629 2630 2631 2632
    test_fillroot();
    test_select();
    test_getitemtext();
    test_focus();
2633 2634 2635 2636 2637 2638 2639 2640
    test_get_set_bkcolor();
    test_get_set_imagelist();
    test_get_set_indent();
    test_get_set_insertmark();
    test_get_set_item();
    test_get_set_itemheight();
    test_get_set_scrolltime();
    test_get_set_textcolor();
2641 2642
    test_get_linecolor();
    test_get_insertmarkcolor();
2643 2644
    test_get_set_tooltips();
    test_get_set_unicodeformat();
2645 2646
    test_callback();
    test_expandinvisible();
2647
    test_itemedit();
2648
    test_treeview_classinfo();
2649
    test_expandnotify();
2650
    test_TVS_SINGLEEXPAND();
2651
    test_WM_PAINT();
2652
    test_delete_items();
2653
    test_cchildren();
2654
    test_htreeitem_layout();
2655
    test_TVS_CHECKBOXES();
2656
    test_TVM_GETNEXTITEM();
2657
    test_TVM_HITTEST();
2658
    test_WM_GETDLGCODE();
2659
    test_customdraw();
2660
    test_WM_KEYDOWN();
2661
    test_TVS_FULLROWSELECT();
2662
    test_TVM_SORTCHILDREN();
2663

2664 2665 2666 2667 2668 2669 2670
    if (!load_v6_module(&ctx_cookie, &hCtx))
    {
        DestroyWindow(hMainWnd);
        return;
    }

    /* comctl32 version 6 tests start here */
2671
    g_v6 = TRUE;
2672
    test_expandedimage();
2673
    test_htreeitem_layout();
2674
    test_WM_GETDLGCODE();
2675 2676 2677

    unload_v6_module(ctx_cookie, hCtx);

2678
    PostMessageA(hMainWnd, WM_CLOSE, 0, 0);
2679 2680
    while(GetMessageA(&msg, 0, 0, 0))
    {
2681 2682 2683 2684
        TranslateMessage(&msg);
        DispatchMessageA(&msg);
    }
}