comctl32: Additional tests for ListView header creation.
[wine] / dlls / comctl32 / tests / listview.c
1 /*
2  * ListView tests
3  *
4  * Copyright 2006 Mike McCormack for CodeWeavers
5  * Copyright 2007 George Gov
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20  */
21
22 #include <stdio.h>
23 #include <windows.h>
24 #include <commctrl.h>
25
26 #include "wine/test.h"
27 #include "msg.h"
28
29 #define PARENT_SEQ_INDEX    0
30 #define LISTVIEW_SEQ_INDEX  1
31 #define NUM_MSG_SEQUENCES   2
32
33 #define LISTVIEW_ID 0
34 #define HEADER_ID   1
35
36 #define expect(expected, got) ok(got == expected, "Expected %d, got %d\n", expected, got)
37 #define expect2(expected1, expected2, got1, got2) ok(expected1 == got1 && expected2 == got2, \
38        "expected (%d,%d), got (%d,%d)\n", expected1, expected2, got1, got2)
39
40 HWND hwndparent;
41
42 static struct msg_sequence *sequences[NUM_MSG_SEQUENCES];
43
44 static const struct message create_parent_wnd_seq[] = {
45     { WM_GETMINMAXINFO,     sent },
46     { WM_NCCREATE,          sent },
47     { WM_NCCALCSIZE,        sent|wparam, 0 },
48     { WM_CREATE,            sent },
49     { WM_SHOWWINDOW,        sent|wparam, 1 },
50     { WM_WINDOWPOSCHANGING, sent|wparam, 0 },
51     { WM_QUERYNEWPALETTE,   sent|optional },
52     { WM_WINDOWPOSCHANGING, sent|wparam, 0 },
53     { WM_WINDOWPOSCHANGED,  sent|optional },
54     { WM_NCCALCSIZE,        sent|wparam|optional, 1 },
55     { WM_ACTIVATEAPP,       sent|wparam, 1 },
56     { WM_NCACTIVATE,        sent|wparam, 1 },
57     { WM_ACTIVATE,          sent|wparam, 1 },
58     { WM_IME_SETCONTEXT,    sent|wparam|defwinproc|optional, 1 },
59     { WM_IME_NOTIFY,        sent|defwinproc|optional },
60     { WM_SETFOCUS,          sent|wparam|defwinproc, 0 },
61     /* Win9x adds SWP_NOZORDER below */
62     { WM_WINDOWPOSCHANGED,  sent, /*|wparam, SWP_SHOWWINDOW|SWP_NOSIZE|SWP_NOMOVE|SWP_NOCLIENTSIZE|SWP_NOCLIENTMOVE*/ },
63     { WM_NCCALCSIZE,        sent|wparam|optional, 1 },
64     { WM_SIZE,              sent },
65     { WM_MOVE,              sent },
66     { 0 }
67 };
68
69 static const struct message redraw_listview_seq[] = {
70     { WM_PAINT,      sent|id,            0, 0, LISTVIEW_ID },
71     { WM_PAINT,      sent|id,            0, 0, HEADER_ID },
72     { WM_NCPAINT,    sent|id|defwinproc, 0, 0, HEADER_ID },
73     { WM_ERASEBKGND, sent|id|defwinproc, 0, 0, HEADER_ID },
74     { WM_NOTIFY,     sent|id|defwinproc, 0, 0, LISTVIEW_ID },
75     { WM_NCPAINT,    sent|id|defwinproc, 0, 0, LISTVIEW_ID },
76     { WM_ERASEBKGND, sent|id|defwinproc, 0, 0, LISTVIEW_ID },
77     { 0 }
78 };
79
80 static const struct message listview_icon_spacing_seq[] = {
81     { LVM_SETICONSPACING, sent|lparam, 0, MAKELPARAM(20, 30) },
82     { LVM_SETICONSPACING, sent|lparam, 0, MAKELPARAM(25, 35) },
83     { LVM_SETICONSPACING, sent|lparam, 0, MAKELPARAM(-1, -1) },
84     { 0 }
85 };
86
87 static const struct message listview_color_seq[] = {
88     { LVM_SETBKCOLOR,     sent|lparam, 0, RGB(0,0,0) },
89     { LVM_GETBKCOLOR,     sent },
90     { LVM_SETTEXTCOLOR,   sent|lparam, 0, RGB(0,0,0) },
91     { LVM_GETTEXTCOLOR,   sent },
92     { LVM_SETTEXTBKCOLOR, sent|lparam, 0, RGB(0,0,0) },
93     { LVM_GETTEXTBKCOLOR, sent },
94
95     { LVM_SETBKCOLOR,     sent|lparam, 0, RGB(100,50,200) },
96     { LVM_GETBKCOLOR,     sent },
97     { LVM_SETTEXTCOLOR,   sent|lparam, 0, RGB(100,50,200) },
98     { LVM_GETTEXTCOLOR,   sent },
99     { LVM_SETTEXTBKCOLOR, sent|lparam, 0, RGB(100,50,200) },
100     { LVM_GETTEXTBKCOLOR, sent },
101
102     { LVM_SETBKCOLOR,     sent|lparam, 0, CLR_NONE },
103     { LVM_GETBKCOLOR,     sent },
104     { LVM_SETTEXTCOLOR,   sent|lparam, 0, CLR_NONE },
105     { LVM_GETTEXTCOLOR,   sent },
106     { LVM_SETTEXTBKCOLOR, sent|lparam, 0, CLR_NONE },
107     { LVM_GETTEXTBKCOLOR, sent },
108
109     { LVM_SETBKCOLOR,     sent|lparam, 0, RGB(255,255,255) },
110     { LVM_GETBKCOLOR,     sent },
111     { LVM_SETTEXTCOLOR,   sent|lparam, 0, RGB(255,255,255) },
112     { LVM_GETTEXTCOLOR,   sent },
113     { LVM_SETTEXTBKCOLOR, sent|lparam, 0, RGB(255,255,255) },
114     { LVM_GETTEXTBKCOLOR, sent },
115     { 0 }
116 };
117
118 static const struct message listview_item_count_seq[] = {
119     { LVM_GETITEMCOUNT,   sent },
120     { LVM_INSERTITEM,     sent },
121     { LVM_INSERTITEM,     sent },
122     { LVM_INSERTITEM,     sent },
123     { LVM_GETITEMCOUNT,   sent },
124     { LVM_DELETEITEM,     sent|wparam, 2 },
125     { LVM_GETITEMCOUNT,   sent },
126     { LVM_DELETEALLITEMS, sent },
127     { LVM_GETITEMCOUNT,   sent },
128     { LVM_INSERTITEM,     sent },
129     { LVM_INSERTITEM,     sent },
130     { LVM_GETITEMCOUNT,   sent },
131     { LVM_INSERTITEM,     sent },
132     { LVM_GETITEMCOUNT,   sent },
133     { 0 }
134 };
135
136 static const struct message listview_itempos_seq[] = {
137     { LVM_INSERTITEM,      sent },
138     { LVM_INSERTITEM,      sent },
139     { LVM_INSERTITEM,      sent },
140     { LVM_SETITEMPOSITION, sent|wparam|lparam, 1, MAKELPARAM(10,5) },
141     { LVM_GETITEMPOSITION, sent|wparam,        1 },
142     { LVM_SETITEMPOSITION, sent|wparam|lparam, 2, MAKELPARAM(0,0) },
143     { LVM_GETITEMPOSITION, sent|wparam,        2 },
144     { LVM_SETITEMPOSITION, sent|wparam|lparam, 0, MAKELPARAM(20,20) },
145     { LVM_GETITEMPOSITION, sent|wparam,        0 },
146     { 0 }
147 };
148
149 struct subclass_info
150 {
151     WNDPROC oldproc;
152 };
153
154 static LRESULT WINAPI parent_wnd_proc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
155 {
156     static LONG defwndproc_counter = 0;
157     LRESULT ret;
158     struct message msg;
159
160     /* log system messages, except for painting */
161     if (message < WM_USER &&
162         message != WM_PAINT &&
163         message != WM_ERASEBKGND &&
164         message != WM_NCPAINT &&
165         message != WM_NCHITTEST &&
166         message != WM_GETTEXT &&
167         message != WM_GETICON &&
168         message != WM_DEVICECHANGE)
169     {
170         trace("parent: %p, %04x, %08lx, %08lx\n", hwnd, message, wParam, lParam);
171
172         msg.message = message;
173         msg.flags = sent|wparam|lparam;
174         if (defwndproc_counter) msg.flags |= defwinproc;
175         msg.wParam = wParam;
176         msg.lParam = lParam;
177         add_message(sequences, PARENT_SEQ_INDEX, &msg);
178     }
179
180     defwndproc_counter++;
181     ret = DefWindowProcA(hwnd, message, wParam, lParam);
182     defwndproc_counter--;
183
184     return ret;
185 }
186
187 static BOOL register_parent_wnd_class(void)
188 {
189     WNDCLASSA cls;
190
191     cls.style = 0;
192     cls.lpfnWndProc = parent_wnd_proc;
193     cls.cbClsExtra = 0;
194     cls.cbWndExtra = 0;
195     cls.hInstance = GetModuleHandleA(NULL);
196     cls.hIcon = 0;
197     cls.hCursor = LoadCursorA(0, IDC_ARROW);
198     cls.hbrBackground = GetStockObject(WHITE_BRUSH);
199     cls.lpszMenuName = NULL;
200     cls.lpszClassName = "Listview test parent class";
201     return RegisterClassA(&cls);
202 }
203
204 static HWND create_parent_window(void)
205 {
206     if (!register_parent_wnd_class())
207         return NULL;
208
209     return CreateWindowEx(0, "Listview test parent class",
210                           "Listview test parent window",
211                           WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX |
212                           WS_MAXIMIZEBOX | WS_VISIBLE,
213                           0, 0, 100, 100,
214                           GetDesktopWindow(), NULL, GetModuleHandleA(NULL), NULL);
215 }
216
217 static LRESULT WINAPI listview_subclass_proc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
218 {
219     struct subclass_info *info = (struct subclass_info *)GetWindowLongPtrA(hwnd, GWLP_USERDATA);
220     static LONG defwndproc_counter = 0;
221     LRESULT ret;
222     struct message msg;
223
224     trace("listview: %p, %04x, %08lx, %08lx\n", hwnd, message, wParam, lParam);
225
226     msg.message = message;
227     msg.flags = sent|wparam|lparam;
228     if (defwndproc_counter) msg.flags |= defwinproc;
229     msg.wParam = wParam;
230     msg.lParam = lParam;
231     msg.id = LISTVIEW_ID;
232     add_message(sequences, LISTVIEW_SEQ_INDEX, &msg);
233
234     defwndproc_counter++;
235     ret = CallWindowProcA(info->oldproc, hwnd, message, wParam, lParam);
236     defwndproc_counter--;
237     return ret;
238 }
239
240 static HWND create_listview_control(void)
241 {
242     struct subclass_info *info;
243     HWND hwnd;
244     RECT rect;
245
246     info = HeapAlloc(GetProcessHeap(), 0, sizeof(struct subclass_info));
247     if (!info)
248         return NULL;
249
250     GetClientRect(hwndparent, &rect);
251     hwnd = CreateWindowExA(0, WC_LISTVIEW, "foo",
252                            WS_CHILD | WS_BORDER | WS_VISIBLE | LVS_REPORT,
253                            0, 0, rect.right, rect.bottom,
254                            hwndparent, NULL, GetModuleHandleA(NULL), NULL);
255     ok(hwnd != NULL, "gle=%d\n", GetLastError());
256
257     if (!hwnd)
258     {
259         HeapFree(GetProcessHeap(), 0, info);
260         return NULL;
261     }
262
263     info->oldproc = (WNDPROC)SetWindowLongPtrA(hwnd, GWLP_WNDPROC,
264                                             (LONG_PTR)listview_subclass_proc);
265     SetWindowLongPtrA(hwnd, GWLP_USERDATA, (LONG_PTR)info);
266
267     return hwnd;
268 }
269
270 static HWND create_custom_listview_control(DWORD style)
271 {
272     struct subclass_info *info;
273     HWND hwnd;
274     RECT rect;
275
276     info = HeapAlloc(GetProcessHeap(), 0, sizeof(struct subclass_info));
277     if (!info)
278         return NULL;
279
280     GetClientRect(hwndparent, &rect);
281     hwnd = CreateWindowExA(0, WC_LISTVIEW, "foo",
282                            WS_CHILD | WS_BORDER | WS_VISIBLE | style,
283                            0, 0, rect.right, rect.bottom,
284                            hwndparent, NULL, GetModuleHandleA(NULL), NULL);
285     ok(hwnd != NULL, "gle=%d\n", GetLastError());
286
287     if (!hwnd)
288     {
289         HeapFree(GetProcessHeap(), 0, info);
290         return NULL;
291     }
292
293     info->oldproc = (WNDPROC)SetWindowLongPtrA(hwnd, GWLP_WNDPROC,
294                                             (LONG_PTR)listview_subclass_proc);
295     SetWindowLongPtrA(hwnd, GWLP_USERDATA, (LONG_PTR)info);
296
297     return hwnd;
298 }
299
300 static LRESULT WINAPI header_subclass_proc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
301 {
302     struct subclass_info *info = (struct subclass_info *)GetWindowLongPtrA(hwnd, GWLP_USERDATA);
303     static LONG defwndproc_counter = 0;
304     LRESULT ret;
305     struct message msg;
306
307     trace("header: %p, %04x, %08lx, %08lx\n", hwnd, message, wParam, lParam);
308
309     msg.message = message;
310     msg.flags = sent|wparam|lparam;
311     if (defwndproc_counter) msg.flags |= defwinproc;
312     msg.wParam = wParam;
313     msg.lParam = lParam;
314     msg.id = HEADER_ID;
315     add_message(sequences, LISTVIEW_SEQ_INDEX, &msg);
316
317     defwndproc_counter++;
318     ret = CallWindowProcA(info->oldproc, hwnd, message, wParam, lParam);
319     defwndproc_counter--;
320     return ret;
321 }
322
323 static HWND subclass_header(HWND hwndListview)
324 {
325     struct subclass_info *info;
326     HWND hwnd;
327
328     info = HeapAlloc(GetProcessHeap(), 0, sizeof(struct subclass_info));
329     if (!info)
330         return NULL;
331
332     hwnd = ListView_GetHeader(hwndListview);
333     info->oldproc = (WNDPROC)SetWindowLongPtrA(hwnd, GWLP_WNDPROC,
334                                             (LONG_PTR)header_subclass_proc);
335     SetWindowLongPtrA(hwnd, GWLP_USERDATA, (LONG_PTR)info);
336
337     return hwnd;
338 }
339
340 static void test_images(void)
341 {
342     HWND hwnd;
343     DWORD r;
344     LVITEM item;
345     HIMAGELIST himl;
346     HBITMAP hbmp;
347     RECT r1, r2;
348     static CHAR hello[] = "hello";
349
350     himl = ImageList_Create(40, 40, 0, 4, 4);
351     ok(himl != NULL, "failed to create imagelist\n");
352
353     hbmp = CreateBitmap(40, 40, 1, 1, NULL);
354     ok(hbmp != NULL, "failed to create bitmap\n");
355
356     r = ImageList_Add(himl, hbmp, 0);
357     ok(r == 0, "should be zero\n");
358
359     hwnd = CreateWindowEx(0, "SysListView32", "foo", LVS_OWNERDRAWFIXED, 
360                 10, 10, 100, 200, hwndparent, NULL, NULL, NULL);
361     ok(hwnd != NULL, "failed to create listview window\n");
362
363     r = SendMessage(hwnd, LVM_SETEXTENDEDLISTVIEWSTYLE, 0, 0x940);
364     ok(r == 0, "should return zero\n");
365
366     r = SendMessage(hwnd, LVM_SETIMAGELIST, 0, (LPARAM)himl);
367     ok(r == 0, "should return zero\n");
368
369     r = SendMessage(hwnd, LVM_SETICONSPACING, 0, MAKELONG(100,50));
370     /* returns dimensions */
371
372     r = SendMessage(hwnd, LVM_GETITEMCOUNT, 0, 0);
373     ok(r == 0, "should be zero items\n");
374
375     item.mask = LVIF_IMAGE | LVIF_TEXT;
376     item.iItem = 0;
377     item.iSubItem = 1;
378     item.iImage = 0;
379     item.pszText = 0;
380     r = SendMessage(hwnd, LVM_INSERTITEM, 0, (LPARAM) &item);
381     ok(r == -1, "should fail\n");
382
383     item.iSubItem = 0;
384     item.pszText = hello;
385     r = SendMessage(hwnd, LVM_INSERTITEM, 0, (LPARAM) &item);
386     ok(r == 0, "should not fail\n");
387
388     memset(&r1, 0, sizeof r1);
389     r1.left = LVIR_ICON;
390     r = SendMessage(hwnd, LVM_GETITEMRECT, 0, (LPARAM) &r1);
391
392     r = SendMessage(hwnd, LVM_DELETEALLITEMS, 0, 0);
393     ok(r == TRUE, "should not fail\n");
394
395     item.iSubItem = 0;
396     item.pszText = hello;
397     r = SendMessage(hwnd, LVM_INSERTITEM, 0, (LPARAM) &item);
398     ok(r == 0, "should not fail\n");
399
400     memset(&r2, 0, sizeof r2);
401     r2.left = LVIR_ICON;
402     r = SendMessage(hwnd, LVM_GETITEMRECT, 0, (LPARAM) &r2);
403
404     ok(!memcmp(&r1, &r2, sizeof r1), "rectangle should be the same\n");
405
406     DestroyWindow(hwnd);
407 }
408
409 static void test_checkboxes(void)
410 {
411     HWND hwnd;
412     LVITEMA item;
413     DWORD r;
414     static CHAR text[]  = "Text",
415                 text2[] = "Text2",
416                 text3[] = "Text3";
417
418     hwnd = CreateWindowEx(0, "SysListView32", "foo", LVS_REPORT, 
419                 10, 10, 100, 200, hwndparent, NULL, NULL, NULL);
420     ok(hwnd != NULL, "failed to create listview window\n");
421
422     /* first without LVS_EX_CHECKBOXES set and an item and check that state is preserved */
423     item.mask = LVIF_TEXT | LVIF_STATE;
424     item.stateMask = 0xffff;
425     item.state = 0xfccc;
426     item.iItem = 0;
427     item.iSubItem = 0;
428     item.pszText = text;
429     r = SendMessage(hwnd, LVM_INSERTITEMA, 0, (LPARAM) &item);
430     ok(r == 0, "ret %d\n", r);
431
432     item.iItem = 0;
433     item.mask = LVIF_STATE;
434     item.stateMask = 0xffff;
435     r = SendMessage(hwnd, LVM_GETITEMA, 0, (LPARAM) &item);
436     ok(item.state == 0xfccc, "state %x\n", item.state);
437
438     /* Don't set LVIF_STATE */
439     item.mask = LVIF_TEXT;
440     item.stateMask = 0xffff;
441     item.state = 0xfccc;
442     item.iItem = 1;
443     item.iSubItem = 0;
444     item.pszText = text;
445     r = SendMessage(hwnd, LVM_INSERTITEMA, 0, (LPARAM) &item);
446     ok(r == 1, "ret %d\n", r);
447
448     item.iItem = 1;
449     item.mask = LVIF_STATE;
450     item.stateMask = 0xffff;
451     r = SendMessage(hwnd, LVM_GETITEMA, 0, (LPARAM) &item);
452     ok(item.state == 0, "state %x\n", item.state);
453
454     r = SendMessage(hwnd, LVM_SETEXTENDEDLISTVIEWSTYLE, LVS_EX_CHECKBOXES, LVS_EX_CHECKBOXES);
455     ok(r == 0, "should return zero\n");
456
457     /* Having turned on checkboxes, check that all existing items are set to 0x1000 (unchecked) */
458     item.iItem = 0;
459     item.mask = LVIF_STATE;
460     item.stateMask = 0xffff;
461     r = SendMessage(hwnd, LVM_GETITEMA, 0, (LPARAM) &item);
462     ok(item.state == 0x1ccc, "state %x\n", item.state);
463
464     /* Now add an item without specifying a state and check that its state goes to 0x1000 */
465     item.iItem = 2;
466     item.mask = LVIF_TEXT;
467     item.state = 0;
468     item.pszText = text2;
469     r = SendMessage(hwnd, LVM_INSERTITEMA, 0, (LPARAM) &item);
470     ok(r == 2, "ret %d\n", r);
471
472     item.iItem = 2;
473     item.mask = LVIF_STATE;
474     item.stateMask = 0xffff;
475     r = SendMessage(hwnd, LVM_GETITEMA, 0, (LPARAM) &item);
476     ok(item.state == 0x1000, "state %x\n", item.state);
477
478     /* Add a further item this time specifying a state and still its state goes to 0x1000 */
479     item.iItem = 3;
480     item.mask = LVIF_TEXT | LVIF_STATE;
481     item.stateMask = 0xffff;
482     item.state = 0x2aaa;
483     item.pszText = text3;
484     r = SendMessage(hwnd, LVM_INSERTITEMA, 0, (LPARAM) &item);
485     ok(r == 3, "ret %d\n", r);
486
487     item.iItem = 3;
488     item.mask = LVIF_STATE;
489     item.stateMask = 0xffff;
490     r = SendMessage(hwnd, LVM_GETITEMA, 0, (LPARAM) &item);
491     ok(item.state == 0x1aaa, "state %x\n", item.state);
492
493     /* Set an item's state to checked */
494     item.iItem = 3;
495     item.mask = LVIF_STATE;
496     item.stateMask = 0xf000;
497     item.state = 0x2000;
498     r = SendMessage(hwnd, LVM_SETITEMA, 0, (LPARAM) &item);
499
500     item.iItem = 3;
501     item.mask = LVIF_STATE;
502     item.stateMask = 0xffff;
503     r = SendMessage(hwnd, LVM_GETITEMA, 0, (LPARAM) &item);
504     ok(item.state == 0x2aaa, "state %x\n", item.state);
505
506     /* Check that only the bits we asked for are returned,
507      * and that all the others are set to zero
508      */
509     item.iItem = 3;
510     item.mask = LVIF_STATE;
511     item.stateMask = 0xf000;
512     item.state = 0xffff;
513     r = SendMessage(hwnd, LVM_GETITEMA, 0, (LPARAM) &item);
514     ok(item.state == 0x2000, "state %x\n", item.state);
515
516     /* Set the style again and check that doesn't change an item's state */
517     r = SendMessage(hwnd, LVM_SETEXTENDEDLISTVIEWSTYLE, LVS_EX_CHECKBOXES, LVS_EX_CHECKBOXES);
518     ok(r == LVS_EX_CHECKBOXES, "ret %x\n", r);
519
520     item.iItem = 3;
521     item.mask = LVIF_STATE;
522     item.stateMask = 0xffff;
523     r = SendMessage(hwnd, LVM_GETITEMA, 0, (LPARAM) &item);
524     ok(item.state == 0x2aaa, "state %x\n", item.state);
525
526     /* Unsetting the checkbox extended style doesn't change an item's state */
527     r = SendMessage(hwnd, LVM_SETEXTENDEDLISTVIEWSTYLE, LVS_EX_CHECKBOXES, 0);
528     ok(r == LVS_EX_CHECKBOXES, "ret %x\n", r);
529
530     item.iItem = 3;
531     item.mask = LVIF_STATE;
532     item.stateMask = 0xffff;
533     r = SendMessage(hwnd, LVM_GETITEMA, 0, (LPARAM) &item);
534     ok(item.state == 0x2aaa, "state %x\n", item.state);
535
536     /* Now setting the style again will change an item's state */
537     r = SendMessage(hwnd, LVM_SETEXTENDEDLISTVIEWSTYLE, LVS_EX_CHECKBOXES, LVS_EX_CHECKBOXES);
538     ok(r == 0, "ret %x\n", r);
539
540     item.iItem = 3;
541     item.mask = LVIF_STATE;
542     item.stateMask = 0xffff;
543     r = SendMessage(hwnd, LVM_GETITEMA, 0, (LPARAM) &item);
544     ok(item.state == 0x1aaa, "state %x\n", item.state);
545
546     /* Toggle checkbox tests (bug 9934) */
547     memset (&item, 0xcc, sizeof(item));
548     item.mask = LVIF_STATE;
549     item.iItem = 3;
550     item.iSubItem = 0;
551     item.state = LVIS_FOCUSED;
552     item.stateMask = LVIS_FOCUSED;
553     r = SendMessage(hwnd, LVM_SETITEM, 0, (LPARAM) &item);
554     expect(1, r);
555
556     item.iItem = 3;
557     item.mask = LVIF_STATE;
558     item.stateMask = 0xffff;
559     r = SendMessage(hwnd, LVM_GETITEMA, 0, (LPARAM) &item);
560     ok(item.state == 0x1aab, "state %x\n", item.state);
561
562     r = SendMessage(hwnd, WM_KEYDOWN, VK_SPACE, 0);
563     expect(0, r);
564     r = SendMessage(hwnd, WM_KEYUP, VK_SPACE, 0);
565     expect(0, r);
566
567     item.iItem = 3;
568     item.mask = LVIF_STATE;
569     item.stateMask = 0xffff;
570     r = SendMessage(hwnd, LVM_GETITEMA, 0, (LPARAM) &item);
571     ok(item.state == 0x2aab, "state %x\n", item.state);
572
573     r = SendMessage(hwnd, WM_KEYDOWN, VK_SPACE, 0);
574     expect(0, r);
575     r = SendMessage(hwnd, WM_KEYUP, VK_SPACE, 0);
576     expect(0, r);
577
578     item.iItem = 3;
579     item.mask = LVIF_STATE;
580     item.stateMask = 0xffff;
581     r = SendMessage(hwnd, LVM_GETITEMA, 0, (LPARAM) &item);
582     ok(item.state == 0x1aab, "state %x\n", item.state);
583
584     DestroyWindow(hwnd);
585 }
586
587 static void insert_column(HWND hwnd, int idx)
588 {
589     LVCOLUMN column;
590     DWORD rc;
591
592     memset(&column, 0xcc, sizeof(column));
593     column.mask = LVCF_SUBITEM;
594     column.iSubItem = idx;
595
596     rc = ListView_InsertColumn(hwnd, idx, &column);
597     expect(idx, rc);
598 }
599
600 static void insert_item(HWND hwnd, int idx)
601 {
602     static CHAR text[] = "foo";
603
604     LVITEMA item;
605     DWORD rc;
606
607     memset(&item, 0xcc, sizeof (item));
608     item.mask = LVIF_TEXT;
609     item.iItem = idx;
610     item.iSubItem = 0;
611     item.pszText = text;
612
613     rc = ListView_InsertItem(hwnd, &item);
614     expect(idx, rc);
615 }
616
617 static void test_items(void)
618 {
619     const LPARAM lparamTest = 0x42;
620     HWND hwnd;
621     LVITEMA item;
622     DWORD r;
623     static CHAR text[] = "Text";
624
625     hwnd = CreateWindowEx(0, "SysListView32", "foo", LVS_REPORT,
626                 10, 10, 100, 200, hwndparent, NULL, NULL, NULL);
627     ok(hwnd != NULL, "failed to create listview window\n");
628
629     /*
630      * Test setting/getting item params
631      */
632
633     /* Set up two columns */
634     insert_column(hwnd, 0);
635     insert_column(hwnd, 1);
636
637     /* Insert an item with just a param */
638     memset (&item, 0xcc, sizeof (item));
639     item.mask = LVIF_PARAM;
640     item.iItem = 0;
641     item.iSubItem = 0;
642     item.lParam = lparamTest;
643     r = SendMessage(hwnd, LVM_INSERTITEMA, 0, (LPARAM) &item);
644     ok(r == 0, "ret %d\n", r);
645
646     /* Test getting of the param */
647     memset (&item, 0xcc, sizeof (item));
648     item.mask = LVIF_PARAM;
649     item.iItem = 0;
650     item.iSubItem = 0;
651     r = SendMessage(hwnd, LVM_GETITEMA, 0, (LPARAM) &item);
652     ok(r != 0, "ret %d\n", r);
653     ok(item.lParam == lparamTest, "got lParam %lx, expected %lx\n", item.lParam, lparamTest);
654
655     /* Set up a subitem */
656     memset (&item, 0xcc, sizeof (item));
657     item.mask = LVIF_TEXT;
658     item.iItem = 0;
659     item.iSubItem = 1;
660     item.pszText = text;
661     r = SendMessage(hwnd, LVM_SETITEMA, 0, (LPARAM) &item);
662     ok(r != 0, "ret %d\n", r);
663
664     /* Query param from subitem: returns main item param */
665     memset (&item, 0xcc, sizeof (item));
666     item.mask = LVIF_PARAM;
667     item.iItem = 0;
668     item.iSubItem = 1;
669     r = SendMessage(hwnd, LVM_GETITEMA, 0, (LPARAM) &item);
670     ok(r != 0, "ret %d\n", r);
671     ok(item.lParam == lparamTest, "got lParam %lx, expected %lx\n", item.lParam, lparamTest);
672
673     /* Set up param on first subitem: no effect */
674     memset (&item, 0xcc, sizeof (item));
675     item.mask = LVIF_PARAM;
676     item.iItem = 0;
677     item.iSubItem = 1;
678     item.lParam = lparamTest+1;
679     r = SendMessage(hwnd, LVM_SETITEMA, 0, (LPARAM) &item);
680     ok(r == 0, "ret %d\n", r);
681
682     /* Query param from subitem again: should still return main item param */
683     memset (&item, 0xcc, sizeof (item));
684     item.mask = LVIF_PARAM;
685     item.iItem = 0;
686     item.iSubItem = 1;
687     r = SendMessage(hwnd, LVM_GETITEMA, 0, (LPARAM) &item);
688     ok(r != 0, "ret %d\n", r);
689     ok(item.lParam == lparamTest, "got lParam %lx, expected %lx\n", item.lParam, lparamTest);
690
691     /**** Some tests of state highlighting ****/
692     memset (&item, 0xcc, sizeof (item));
693     item.mask = LVIF_STATE;
694     item.iItem = 0;
695     item.iSubItem = 0;
696     item.state = LVIS_SELECTED;
697     item.stateMask = LVIS_SELECTED | LVIS_DROPHILITED;
698     r = SendMessage(hwnd, LVM_SETITEM, 0, (LPARAM) &item);
699     ok(r != 0, "ret %d\n", r);
700     item.iSubItem = 1;
701     item.state = LVIS_DROPHILITED;
702     r = SendMessage(hwnd, LVM_SETITEM, 0, (LPARAM) &item);
703     ok(r != 0, "ret %d\n", r);
704
705     memset (&item, 0xcc, sizeof (item));
706     item.mask = LVIF_STATE;
707     item.iItem = 0;
708     item.iSubItem = 0;
709     item.stateMask = -1;
710     r = SendMessage(hwnd, LVM_GETITEM, 0, (LPARAM) &item);
711     ok(r != 0, "ret %d\n", r);
712     ok(item.state == LVIS_SELECTED, "got state %x, expected %x\n", item.state, LVIS_SELECTED);
713     item.iSubItem = 1;
714     r = SendMessage(hwnd, LVM_GETITEM, 0, (LPARAM) &item);
715     ok(r != 0, "ret %d\n", r);
716     todo_wine ok(item.state == LVIS_DROPHILITED, "got state %x, expected %x\n", item.state, LVIS_DROPHILITED);
717
718     DestroyWindow(hwnd);
719 }
720
721 static void test_columns(void)
722 {
723     HWND hwnd;
724     LVCOLUMN column;
725     DWORD rc;
726
727     hwnd = CreateWindowEx(0, "SysListView32", "foo", LVS_REPORT,
728                 10, 10, 100, 200, hwndparent, NULL, NULL, NULL);
729     ok(hwnd != NULL, "failed to create listview window\n");
730
731     /* Add a column with no mask */
732     memset(&column, 0xcc, sizeof(column));
733     column.mask = 0;
734     rc = ListView_InsertColumn(hwnd, 0, &column);
735     ok(rc==0, "Inserting column with no mask failed with %d\n", rc);
736
737     /* Check its width */
738     rc = ListView_GetColumnWidth(hwnd, 0);
739     ok(rc==10 ||
740        broken(rc==0), /* win9x */
741        "Inserting column with no mask failed to set width to 10 with %d\n", rc);
742
743     DestroyWindow(hwnd);
744 }
745 /* test setting imagelist between WM_NCCREATE and WM_CREATE */
746 static WNDPROC listviewWndProc;
747 static HIMAGELIST test_create_imagelist;
748
749 static LRESULT CALLBACK create_test_wndproc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
750 {
751     LRESULT ret;
752
753     if (uMsg == WM_CREATE)
754     {
755         LPCREATESTRUCT lpcs = (LPCREATESTRUCT)lParam;
756         lpcs->style |= LVS_REPORT;
757     }
758     ret = CallWindowProc(listviewWndProc, hwnd, uMsg, wParam, lParam);
759     if (uMsg == WM_CREATE) SendMessage(hwnd, LVM_SETIMAGELIST, 0, (LPARAM)test_create_imagelist);
760     return ret;
761 }
762
763 static void test_create(void)
764 {
765     HWND hList;
766     HWND hHeader;
767     LONG_PTR ret;
768     LONG r;
769     LVCOLUMNA col;
770     WNDCLASSEX cls;
771     cls.cbSize = sizeof(WNDCLASSEX);
772     ok(GetClassInfoEx(GetModuleHandle(NULL), "SysListView32", &cls), "GetClassInfoEx failed\n");
773     listviewWndProc = cls.lpfnWndProc;
774     cls.lpfnWndProc = create_test_wndproc;
775     cls.lpszClassName = "MyListView32";
776     ok(RegisterClassEx(&cls), "RegisterClassEx failed\n");
777
778     test_create_imagelist = ImageList_Create(16, 16, 0, 5, 10);
779     hList = CreateWindow("MyListView32", "Test", WS_VISIBLE, 0, 0, 100, 100, NULL, NULL, GetModuleHandle(NULL), 0);
780     ok((HIMAGELIST)SendMessage(hList, LVM_GETIMAGELIST, 0, 0) == test_create_imagelist, "Image list not obtained\n");
781     hHeader = (HWND)SendMessage(hList, LVM_GETHEADER, 0, 0);
782     ok(IsWindow(hHeader) && IsWindowVisible(hHeader), "Listview not in report mode\n");
783     ok(hHeader == GetDlgItem(hList, 0), "Expected header as dialog item\n");
784     DestroyWindow(hList);
785
786     /* header isn't created on LVS_ICON and LVS_LIST styles */
787     hList = CreateWindow("SysListView32", "Test", WS_VISIBLE, 0, 0, 100, 100, NULL, NULL,
788                           GetModuleHandle(NULL), 0);
789     hHeader = (HWND)SendMessage(hList, LVM_GETHEADER, 0, 0);
790     todo_wine ok(!IsWindow(hHeader), "Header shouldn't be created\n");
791     todo_wine ok(NULL == GetDlgItem(hList, 0), "NULL dialog item expected\n");
792     /* insert column */
793     memset(&col, 0, sizeof(LVCOLUMNA));
794     col.mask = LVCF_WIDTH;
795     col.cx = 100;
796     r = SendMessage(hList, LVM_INSERTCOLUMN, 0, (LPARAM)&col);
797     ok(r == 0, "Expected 0 column's inserted\n");
798     hHeader = (HWND)SendMessage(hList, LVM_GETHEADER, 0, 0);
799     ok(IsWindow(hHeader), "Header should be created\n");
800     ok(hHeader == GetDlgItem(hList, 0), "Expected header as dialog item\n");
801     DestroyWindow(hList);
802
803     hList = CreateWindow("SysListView32", "Test", WS_VISIBLE|LVS_LIST, 0, 0, 100, 100, NULL, NULL,
804                           GetModuleHandle(NULL), 0);
805     hHeader = (HWND)SendMessage(hList, LVM_GETHEADER, 0, 0);
806     todo_wine ok(!IsWindow(hHeader), "Header shouldn't be created\n");
807     todo_wine ok(NULL == GetDlgItem(hList, 0), "NULL dialog item expected\n");
808     /* insert column */
809     memset(&col, 0, sizeof(LVCOLUMNA));
810     col.mask = LVCF_WIDTH;
811     col.cx = 100;
812     r = SendMessage(hList, LVM_INSERTCOLUMN, 0, (LPARAM)&col);
813     ok(r == 0, "Expected 0 column's inserted\n");
814     hHeader = (HWND)SendMessage(hList, LVM_GETHEADER, 0, 0);
815     ok(IsWindow(hHeader), "Header should be created\n");
816     ok(hHeader == GetDlgItem(hList, 0), "Expected header as dialog item\n");
817     DestroyWindow(hList);
818
819     /* try to switch LVS_ICON -> LVS_REPORT and back LVS_ICON -> LVS_REPORT */
820     hList = CreateWindow("SysListView32", "Test", WS_VISIBLE, 0, 0, 100, 100, NULL, NULL,
821                           GetModuleHandle(NULL), 0);
822     ret = SetWindowLongPtr(hList, GWL_STYLE, GetWindowLongPtr(hList, GWL_STYLE) | LVS_REPORT);
823     ok(ret & WS_VISIBLE, "Style wrong, should have WS_VISIBLE\n");
824     hHeader = (HWND)SendMessage(hList, LVM_GETHEADER, 0, 0);
825     ok(IsWindow(hHeader), "Header should be created\n");
826     ret = SetWindowLongPtr(hList, GWL_STYLE, GetWindowLong(hList, GWL_STYLE) & ~LVS_REPORT);
827     ok((ret & WS_VISIBLE) && (ret & LVS_REPORT), "Style wrong, should have WS_VISIBLE|LVS_REPORT\n");
828     hHeader = (HWND)SendMessage(hList, LVM_GETHEADER, 0, 0);
829     ok(IsWindow(hHeader), "Header should be created\n");
830     ok(hHeader == GetDlgItem(hList, 0), "Expected header as dialog item\n");
831     DestroyWindow(hList);
832
833     /* try to switch LVS_LIST -> LVS_REPORT and back LVS_LIST -> LVS_REPORT */
834     hList = CreateWindow("SysListView32", "Test", WS_VISIBLE|LVS_LIST, 0, 0, 100, 100, NULL, NULL,
835                           GetModuleHandle(NULL), 0);
836     ret = SetWindowLongPtr(hList, GWL_STYLE,
837                           (GetWindowLongPtr(hList, GWL_STYLE) & ~LVS_LIST) | LVS_REPORT);
838     ok(((ret & WS_VISIBLE) && (ret & LVS_LIST)), "Style wrong, should have WS_VISIBLE|LVS_LIST\n");
839     hHeader = (HWND)SendMessage(hList, LVM_GETHEADER, 0, 0);
840     ok(IsWindow(hHeader), "Header shouldn't be created\n");
841     ok(hHeader == GetDlgItem(hList, 0), "NULL dialog item expected\n");
842     ret = SetWindowLongPtr(hList, GWL_STYLE,
843                           (GetWindowLongPtr(hList, GWL_STYLE) & ~LVS_REPORT) | LVS_LIST);
844     ok(((ret & WS_VISIBLE) && (ret & LVS_REPORT)), "Style wrong, should have WS_VISIBLE|LVS_REPORT\n");
845     hHeader = (HWND)SendMessage(hList, LVM_GETHEADER, 0, 0);
846     ok(IsWindow(hHeader), "Header shouldn't be created\n");
847     ok(hHeader == GetDlgItem(hList, 0), "NULL dialog item expected\n");
848     DestroyWindow(hList);
849
850     /* LVS_REPORT without WS_VISIBLE */
851     hList = CreateWindow("SysListView32", "Test", LVS_REPORT, 0, 0, 100, 100, NULL, NULL,
852                           GetModuleHandle(NULL), 0);
853     hHeader = (HWND)SendMessage(hList, LVM_GETHEADER, 0, 0);
854     todo_wine ok(!IsWindow(hHeader), "Header shouldn't be created\n");
855     todo_wine ok(NULL == GetDlgItem(hList, 0), "NULL dialog item expected\n");
856     /* insert column */
857     memset(&col, 0, sizeof(LVCOLUMNA));
858     col.mask = LVCF_WIDTH;
859     col.cx = 100;
860     r = SendMessage(hList, LVM_INSERTCOLUMN, 0, (LPARAM)&col);
861     ok(r == 0, "Expected 0 column's inserted\n");
862     hHeader = (HWND)SendMessage(hList, LVM_GETHEADER, 0, 0);
863     ok(IsWindow(hHeader), "Header should be created\n");
864     ok(hHeader == GetDlgItem(hList, 0), "Expected header as dialog item\n");
865     DestroyWindow(hList);
866
867     /* LVS_REPORT without WS_VISIBLE, try to show it */
868     hList = CreateWindow("SysListView32", "Test", LVS_REPORT, 0, 0, 100, 100, NULL, NULL,
869                           GetModuleHandle(NULL), 0);
870     hHeader = (HWND)SendMessage(hList, LVM_GETHEADER, 0, 0);
871     todo_wine ok(!IsWindow(hHeader), "Header shouldn't be created\n");
872     todo_wine ok(NULL == GetDlgItem(hList, 0), "NULL dialog item expected\n");
873     ShowWindow(hList, SW_SHOW);
874     hHeader = (HWND)SendMessage(hList, LVM_GETHEADER, 0, 0);
875     ok(IsWindow(hHeader), "Header shouldn't be created\n");
876     ok(hHeader == GetDlgItem(hList, 0), "NULL dialog item expected\n");
877     DestroyWindow(hList);
878 }
879
880 static void test_redraw(void)
881 {
882     HWND hwnd, hwndheader;
883
884     hwnd = create_listview_control();
885     hwndheader = subclass_header(hwnd);
886
887     flush_sequences(sequences, NUM_MSG_SEQUENCES);
888
889     trace("invalidate & update\n");
890     InvalidateRect(hwnd, NULL, TRUE);
891     UpdateWindow(hwnd);
892     ok_sequence(sequences, LISTVIEW_SEQ_INDEX, redraw_listview_seq, "redraw listview", FALSE);
893
894     flush_sequences(sequences, NUM_MSG_SEQUENCES);
895
896     DestroyWindow(hwnd);
897 }
898
899 static LRESULT WINAPI cd_wndproc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp)
900 {
901     COLORREF clr, c0ffee = RGB(0xc0, 0xff, 0xee);
902
903     if(msg == WM_NOTIFY) {
904         NMHDR *nmhdr = (PVOID)lp;
905         if(nmhdr->code == NM_CUSTOMDRAW) {
906             NMLVCUSTOMDRAW *nmlvcd = (PVOID)nmhdr;
907             trace("NMCUSTOMDRAW (0x%.8x)\n", nmlvcd->nmcd.dwDrawStage);
908             switch(nmlvcd->nmcd.dwDrawStage) {
909             case CDDS_PREPAINT:
910                 SetBkColor(nmlvcd->nmcd.hdc, c0ffee);
911                 return CDRF_NOTIFYITEMDRAW;
912             case CDDS_ITEMPREPAINT:
913                 nmlvcd->clrTextBk = CLR_DEFAULT;
914                 return CDRF_NOTIFYSUBITEMDRAW;
915             case CDDS_ITEMPREPAINT | CDDS_SUBITEM:
916                 clr = GetBkColor(nmlvcd->nmcd.hdc);
917                 todo_wine ok(clr == c0ffee, "clr=%.8x\n", clr);
918                 return CDRF_NOTIFYPOSTPAINT;
919             case CDDS_ITEMPOSTPAINT | CDDS_SUBITEM:
920                 clr = GetBkColor(nmlvcd->nmcd.hdc);
921                 todo_wine ok(clr == c0ffee, "clr=%.8x\n", clr);
922                 return CDRF_DODEFAULT;
923             }
924             return CDRF_DODEFAULT;
925         }
926     }
927
928     return DefWindowProcA(hwnd, msg, wp, lp);
929 }
930
931 static void test_customdraw(void)
932 {
933     HWND hwnd;
934     WNDPROC oldwndproc;
935
936     hwnd = create_listview_control();
937
938     insert_column(hwnd, 0);
939     insert_column(hwnd, 1);
940     insert_item(hwnd, 0);
941
942     oldwndproc = (WNDPROC)SetWindowLongPtr(hwndparent, GWLP_WNDPROC,
943                                            (LONG_PTR)cd_wndproc);
944
945     InvalidateRect(hwnd, NULL, TRUE);
946     UpdateWindow(hwnd);
947
948     SetWindowLongPtr(hwndparent, GWLP_WNDPROC, (LONG_PTR)oldwndproc);
949
950     DestroyWindow(hwnd);
951 }
952
953 static void test_icon_spacing(void)
954 {
955     /* LVM_SETICONSPACING */
956     /* note: LVM_SETICONSPACING returns the previous icon spacing if successful */
957
958     HWND hwnd;
959     WORD w, h;
960     DWORD r;
961
962     hwnd = create_custom_listview_control(LVS_ICON);
963     ok(hwnd != NULL, "failed to create a listview window\n");
964
965     r = SendMessage(hwnd, WM_NOTIFYFORMAT, (WPARAM)hwndparent, (LPARAM)NF_REQUERY);
966     expect(NFR_ANSI, r);
967
968     /* reset the icon spacing to defaults */
969     SendMessage(hwnd, LVM_SETICONSPACING, 0, MAKELPARAM(-1, -1));
970
971     /* now we can request what the defaults are */
972     r = SendMessage(hwnd, LVM_SETICONSPACING, 0, MAKELPARAM(-1, -1));
973     w = LOWORD(r);
974     h = HIWORD(r);
975
976     flush_sequences(sequences, NUM_MSG_SEQUENCES);
977
978     trace("test icon spacing\n");
979
980     r = SendMessage(hwnd, LVM_SETICONSPACING, 0, MAKELPARAM(20, 30));
981     ok(r == MAKELONG(w, h) ||
982        broken(r == MAKELONG(w, w)), /* win98 */
983        "Expected %d, got %d\n", MAKELONG(w, h), r);
984
985     r = SendMessage(hwnd, LVM_SETICONSPACING, 0, MAKELPARAM(25, 35));
986     expect(MAKELONG(20,30), r);
987
988     r = SendMessage(hwnd, LVM_SETICONSPACING, 0, MAKELPARAM(-1,-1));
989     expect(MAKELONG(25,35), r);
990
991     ok_sequence(sequences, LISTVIEW_SEQ_INDEX, listview_icon_spacing_seq, "test icon spacing seq", FALSE);
992
993     flush_sequences(sequences, NUM_MSG_SEQUENCES);
994     DestroyWindow(hwnd);
995 }
996
997 static void test_color(void)
998 {
999     /* SETBKCOLOR/GETBKCOLOR, SETTEXTCOLOR/GETTEXTCOLOR, SETTEXTBKCOLOR/GETTEXTBKCOLOR */
1000
1001     HWND hwnd;
1002     DWORD r;
1003     int i;
1004
1005     COLORREF color;
1006     COLORREF colors[4] = {RGB(0,0,0), RGB(100,50,200), CLR_NONE, RGB(255,255,255)};
1007
1008     hwnd = create_listview_control();
1009     ok(hwnd != NULL, "failed to create a listview window\n");
1010
1011     flush_sequences(sequences, NUM_MSG_SEQUENCES);
1012
1013     trace("test color seq\n");
1014     for (i = 0; i < 4; i++)
1015     {
1016         color = colors[i];
1017
1018         r = SendMessage(hwnd, LVM_SETBKCOLOR, 0, color);
1019         expect(TRUE, r);
1020         r = SendMessage(hwnd, LVM_GETBKCOLOR, 0, color);
1021         expect(color, r);
1022
1023         r = SendMessage(hwnd, LVM_SETTEXTCOLOR, 0, color);
1024         expect (TRUE, r);
1025         r = SendMessage(hwnd, LVM_GETTEXTCOLOR, 0, color);
1026         expect(color, r);
1027
1028         r = SendMessage(hwnd, LVM_SETTEXTBKCOLOR, 0, color);
1029         expect(TRUE, r);
1030         r = SendMessage(hwnd, LVM_GETTEXTBKCOLOR, 0, color);
1031         expect(color, r);
1032     }
1033
1034     ok_sequence(sequences, LISTVIEW_SEQ_INDEX, listview_color_seq, "test color seq", FALSE);
1035
1036     flush_sequences(sequences, NUM_MSG_SEQUENCES);
1037     DestroyWindow(hwnd);
1038 }
1039
1040 static void test_item_count(void)
1041 {
1042     /* LVM_INSERTITEM, LVM_DELETEITEM, LVM_DELETEALLITEMS, LVM_GETITEMCOUNT */
1043
1044     HWND hwnd;
1045     DWORD r;
1046
1047     LVITEM item0;
1048     LVITEM item1;
1049     LVITEM item2;
1050     static CHAR item0text[] = "item0";
1051     static CHAR item1text[] = "item1";
1052     static CHAR item2text[] = "item2";
1053
1054     hwnd = create_listview_control();
1055     ok(hwnd != NULL, "failed to create a listview window\n");
1056
1057     flush_sequences(sequences, NUM_MSG_SEQUENCES);
1058
1059     trace("test item count\n");
1060
1061     r = SendMessage(hwnd, LVM_GETITEMCOUNT, 0, 0);
1062     expect(0, r);
1063
1064     /* [item0] */
1065     item0.mask = LVIF_TEXT;
1066     item0.iItem = 0;
1067     item0.iSubItem = 0;
1068     item0.pszText = item0text;
1069     r = SendMessage(hwnd, LVM_INSERTITEM, 0, (LPARAM) &item0);
1070     expect(0, r);
1071
1072     /* [item0, item1] */
1073     item1.mask = LVIF_TEXT;
1074     item1.iItem = 1;
1075     item1.iSubItem = 0;
1076     item1.pszText = item1text;
1077     r = SendMessage(hwnd, LVM_INSERTITEM, 0, (LPARAM) &item1);
1078     expect(1, r);
1079
1080     /* [item0, item1, item2] */
1081     item2.mask = LVIF_TEXT;
1082     item2.iItem = 2;
1083     item2.iSubItem = 0;
1084     item2.pszText = item2text;
1085     r = SendMessage(hwnd, LVM_INSERTITEM, 0, (LPARAM) &item2);
1086     expect(2, r);
1087
1088     r = SendMessage(hwnd, LVM_GETITEMCOUNT, 0, 0);
1089     expect(3, r);
1090
1091     /* [item0, item1] */
1092     r = SendMessage(hwnd, LVM_DELETEITEM, 2, 0);
1093     expect(TRUE, r);
1094
1095     r = SendMessage(hwnd, LVM_GETITEMCOUNT, 0, 0);
1096     expect(2, r);
1097
1098     /* [] */
1099     r = SendMessage(hwnd, LVM_DELETEALLITEMS, 0, 0);
1100     expect(TRUE, r);
1101
1102     r = SendMessage(hwnd, LVM_GETITEMCOUNT, 0, 0);
1103     expect(0, r);
1104
1105     /* [item0] */
1106     r = SendMessage(hwnd, LVM_INSERTITEM, 0, (LPARAM) &item1);
1107     expect(0, r);
1108
1109     /* [item0, item1] */
1110     r = SendMessage(hwnd, LVM_INSERTITEM, 0, (LPARAM) &item1);
1111     expect(1, r);
1112
1113     r = SendMessage(hwnd, LVM_GETITEMCOUNT, 0, 0);
1114     expect(2, r);
1115
1116     /* [item0, item1, item2] */
1117     r = SendMessage(hwnd, LVM_INSERTITEM, 0, (LPARAM) &item2);
1118     expect(2, r);
1119
1120     r = SendMessage(hwnd, LVM_GETITEMCOUNT, 0, 0);
1121     expect(3, r);
1122
1123     ok_sequence(sequences, LISTVIEW_SEQ_INDEX, listview_item_count_seq, "test item count seq", FALSE);
1124
1125     flush_sequences(sequences, NUM_MSG_SEQUENCES);
1126     DestroyWindow(hwnd);
1127 }
1128
1129 static void test_item_position(void)
1130 {
1131     /* LVM_SETITEMPOSITION/LVM_GETITEMPOSITION */
1132
1133     HWND hwnd;
1134     DWORD r;
1135     POINT position;
1136
1137     LVITEM item0;
1138     LVITEM item1;
1139     LVITEM item2;
1140     static CHAR item0text[] = "item0";
1141     static CHAR item1text[] = "item1";
1142     static CHAR item2text[] = "item2";
1143
1144     hwnd = create_custom_listview_control(LVS_ICON);
1145     ok(hwnd != NULL, "failed to create a listview window\n");
1146
1147     flush_sequences(sequences, NUM_MSG_SEQUENCES);
1148
1149     trace("test item position\n");
1150
1151     /* [item0] */
1152     item0.mask = LVIF_TEXT;
1153     item0.iItem = 0;
1154     item0.iSubItem = 0;
1155     item0.pszText = item0text;
1156     r = SendMessage(hwnd, LVM_INSERTITEM, 0, (LPARAM) &item0);
1157     expect(0, r);
1158
1159     /* [item0, item1] */
1160     item1.mask = LVIF_TEXT;
1161     item1.iItem = 1;
1162     item1.iSubItem = 0;
1163     item1.pszText = item1text;
1164     r = SendMessage(hwnd, LVM_INSERTITEM, 0, (LPARAM) &item1);
1165     expect(1, r);
1166
1167     /* [item0, item1, item2] */
1168     item2.mask = LVIF_TEXT;
1169     item2.iItem = 2;
1170     item2.iSubItem = 0;
1171     item2.pszText = item2text;
1172     r = SendMessage(hwnd, LVM_INSERTITEM, 0, (LPARAM) &item2);
1173     expect(2, r);
1174
1175     r = SendMessage(hwnd, LVM_SETITEMPOSITION, 1, MAKELPARAM(10,5));
1176     expect(TRUE, r);
1177     r = SendMessage(hwnd, LVM_GETITEMPOSITION, 1, (LPARAM) &position);
1178     expect(TRUE, r);
1179     expect2(10, 5, position.x, position.y);
1180
1181     r = SendMessage(hwnd, LVM_SETITEMPOSITION, 2, MAKELPARAM(0,0));
1182     expect(TRUE, r);
1183     r = SendMessage(hwnd, LVM_GETITEMPOSITION, 2, (LPARAM) &position);
1184     expect(TRUE, r);
1185     expect2(0, 0, position.x, position.y);
1186
1187     r = SendMessage(hwnd, LVM_SETITEMPOSITION, 0, MAKELPARAM(20,20));
1188     expect(TRUE, r);
1189     r = SendMessage(hwnd, LVM_GETITEMPOSITION, 0, (LPARAM) &position);
1190     expect(TRUE, r);
1191     expect2(20, 20, position.x, position.y);
1192
1193     ok_sequence(sequences, LISTVIEW_SEQ_INDEX, listview_itempos_seq, "test item position seq", TRUE);
1194
1195     flush_sequences(sequences, NUM_MSG_SEQUENCES);
1196     DestroyWindow(hwnd);
1197 }
1198
1199 static void test_getorigin(void)
1200 {
1201     /* LVM_GETORIGIN */
1202
1203     HWND hwnd;
1204     DWORD r;
1205     POINT position;
1206
1207     position.x = position.y = 0;
1208
1209     hwnd = create_custom_listview_control(LVS_ICON);
1210     ok(hwnd != NULL, "failed to create a listview window\n");
1211     flush_sequences(sequences, NUM_MSG_SEQUENCES);
1212     trace("test get origin results\n");
1213     r = SendMessage(hwnd, LVM_GETORIGIN, 0, (LPARAM)&position);
1214     expect(TRUE, r);
1215     flush_sequences(sequences, NUM_MSG_SEQUENCES);
1216     DestroyWindow(hwnd);
1217
1218     hwnd = create_custom_listview_control(LVS_SMALLICON);
1219     ok(hwnd != NULL, "failed to create a listview window\n");
1220     flush_sequences(sequences, NUM_MSG_SEQUENCES);
1221     trace("test get origin results\n");
1222     r = SendMessage(hwnd, LVM_GETORIGIN, 0, (LPARAM)&position);
1223     expect(TRUE, r);
1224     flush_sequences(sequences, NUM_MSG_SEQUENCES);
1225     DestroyWindow(hwnd);
1226
1227     hwnd = create_custom_listview_control(LVS_LIST);
1228     ok(hwnd != NULL, "failed to create a listview window\n");
1229     flush_sequences(sequences, NUM_MSG_SEQUENCES);
1230     trace("test get origin results\n");
1231     r = SendMessage(hwnd, LVM_GETORIGIN, 0, (LPARAM)&position);
1232     expect(FALSE, r);
1233     flush_sequences(sequences, NUM_MSG_SEQUENCES);
1234     DestroyWindow(hwnd);
1235
1236     hwnd = create_custom_listview_control(LVS_REPORT);
1237     ok(hwnd != NULL, "failed to create a listview window\n");
1238     flush_sequences(sequences, NUM_MSG_SEQUENCES);
1239     trace("test get origin results\n");
1240     r = SendMessage(hwnd, LVM_GETORIGIN, 0, (LPARAM)&position);
1241     expect(FALSE, r);
1242     flush_sequences(sequences, NUM_MSG_SEQUENCES);
1243     DestroyWindow(hwnd);
1244
1245 }
1246
1247 static void test_multiselect(void)
1248 {
1249     typedef struct t_select_task
1250     {
1251         const char *descr;
1252         int initPos;
1253         int loopVK;
1254         int count;
1255         int result;
1256     } select_task;
1257
1258     HWND hwnd;
1259     DWORD r;
1260     int i,j,item_count,selected_count;
1261     static const int items=5;
1262     BYTE kstate[256];
1263     select_task task;
1264
1265     static struct t_select_task task_list[] = {
1266         { "using VK_DOWN", 0, VK_DOWN, -1, -1 },
1267         { "using VK_UP", -1, VK_UP, -1, -1 },
1268         { "using VK_END", 0, VK_END, 1, -1 },
1269         { "using VK_HOME", -1, VK_HOME, 1, -1 }
1270     };
1271
1272
1273     hwnd = create_listview_control();
1274
1275     for (i=0;i<items;i++) {
1276             insert_item(hwnd, 0);
1277     }
1278
1279     item_count = (int)SendMessage(hwnd, LVM_GETITEMCOUNT, 0, 0);
1280
1281     expect(items,item_count);
1282
1283     for (i=0;i<4;i++) {
1284         task = task_list[i];
1285
1286         /* deselect all items */
1287         ListView_SetItemState(hwnd, -1, 0, LVIS_SELECTED);
1288         SendMessage(hwnd, LVM_SETSELECTIONMARK, 0, -1);
1289
1290         /* set initial position */
1291         SendMessage(hwnd, LVM_SETSELECTIONMARK, 0, (task.initPos == -1 ? item_count -1 : task.initPos));
1292         ListView_SetItemState(hwnd,(task.initPos == -1 ? item_count -1 : task.initPos),LVIS_SELECTED ,LVIS_SELECTED);
1293
1294         selected_count = (int)SendMessage(hwnd, LVM_GETSELECTEDCOUNT, 0, 0);
1295
1296         ok(selected_count == 1, "There should be only one selected item at the beginning (is %d)\n",selected_count);
1297
1298         /* Set SHIFT key pressed */
1299         GetKeyboardState(kstate);
1300         kstate[VK_SHIFT]=0x80;
1301         SetKeyboardState(kstate);
1302
1303         for (j=1;j<=(task.count == -1 ? item_count : task.count);j++) {
1304             r = SendMessage(hwnd, WM_KEYDOWN, task.loopVK, 0);
1305             expect(0,r);
1306             r = SendMessage(hwnd, WM_KEYUP, task.loopVK, 0);
1307             expect(0,r);
1308         }
1309
1310         selected_count = (int)SendMessage(hwnd, LVM_GETSELECTEDCOUNT, 0, 0);
1311
1312         ok((task.result == -1 ? item_count : task.result) == selected_count, "Failed multiple selection %s. There should be %d selected items (is %d)\n", task.descr, item_count, selected_count);
1313
1314         /* Set SHIFT key released */
1315         GetKeyboardState(kstate);
1316         kstate[VK_SHIFT]=0x00;
1317         SetKeyboardState(kstate);
1318     }
1319     DestroyWindow(hwnd);
1320 }
1321
1322 static void test_subitem_rect(void)
1323 {
1324     HWND hwnd;
1325     DWORD r;
1326     LVCOLUMN col;
1327     RECT rect;
1328
1329     /* test LVM_GETSUBITEMRECT for header */
1330     hwnd = create_listview_control();
1331     ok(hwnd != NULL, "failed to create a listview window\n");
1332     /* add some columns */
1333     memset(&col, 0, sizeof(LVCOLUMN));
1334     col.mask = LVCF_WIDTH;
1335     col.cx = 100;
1336     r = -1;
1337     r = SendMessage(hwnd, LVM_INSERTCOLUMN, 0, (LPARAM)&col);
1338     expect(0, r);
1339     col.cx = 150;
1340     r = -1;
1341     r = SendMessage(hwnd, LVM_INSERTCOLUMN, 1, (LPARAM)&col);
1342     expect(1, r);
1343     col.cx = 200;
1344     r = -1;
1345     r = SendMessage(hwnd, LVM_INSERTCOLUMN, 2, (LPARAM)&col);
1346     expect(2, r);
1347     /* item = -1 means header, subitem index is 1 based */
1348     rect.left = LVIR_BOUNDS;
1349     rect.top  = 0;
1350     rect.right = rect.bottom = 0;
1351     r = SendMessage(hwnd, LVM_GETSUBITEMRECT, -1, (LPARAM)&rect);
1352     expect(0, r);
1353
1354     rect.left = LVIR_BOUNDS;
1355     rect.top  = 1;
1356     rect.right = rect.bottom = 0;
1357     r = SendMessage(hwnd, LVM_GETSUBITEMRECT, -1, (LPARAM)&rect);
1358 todo_wine{
1359     ok(r != 0, "Expected not-null LRESULT\n");
1360     expect(100, rect.left);
1361     expect(250, rect.right);
1362     expect(3, rect.top);
1363 }
1364     rect.left = LVIR_BOUNDS;
1365     rect.top  = 2;
1366     rect.right = rect.bottom = 0;
1367     r = SendMessage(hwnd, LVM_GETSUBITEMRECT, -1, (LPARAM)&rect);
1368 todo_wine{
1369     ok(r != 0, "Expected not-null LRESULT\n");
1370     expect(250, rect.left);
1371     expect(450, rect.right);
1372     expect(3, rect.top);
1373 }
1374
1375     DestroyWindow(hwnd);
1376 }
1377
1378 START_TEST(listview)
1379 {
1380     HMODULE hComctl32;
1381     BOOL (WINAPI *pInitCommonControlsEx)(const INITCOMMONCONTROLSEX*);
1382
1383     hComctl32 = GetModuleHandleA("comctl32.dll");
1384     pInitCommonControlsEx = (void*)GetProcAddress(hComctl32, "InitCommonControlsEx");
1385     if (pInitCommonControlsEx)
1386     {
1387         INITCOMMONCONTROLSEX iccex;
1388         iccex.dwSize = sizeof(iccex);
1389         iccex.dwICC  = ICC_LISTVIEW_CLASSES;
1390         pInitCommonControlsEx(&iccex);
1391     }
1392     else
1393         InitCommonControls();
1394
1395     init_msg_sequences(sequences, NUM_MSG_SEQUENCES);
1396
1397     flush_sequences(sequences, NUM_MSG_SEQUENCES);
1398     hwndparent = create_parent_window();
1399     ok_sequence(sequences, PARENT_SEQ_INDEX, create_parent_wnd_seq, "create parent window", TRUE);
1400     flush_sequences(sequences, NUM_MSG_SEQUENCES);
1401
1402     test_images();
1403     test_checkboxes();
1404     test_items();
1405     test_create();
1406     test_redraw();
1407     test_customdraw();
1408     test_icon_spacing();
1409     test_color();
1410     test_item_count();
1411     test_item_position();
1412     test_columns();
1413     test_getorigin();
1414     test_multiselect();
1415     test_subitem_rect();
1416 }