4 * Copyright 2006 Mike McCormack for CodeWeavers
5 * Copyright 2007 George Gov
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.
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.
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
26 #include "wine/test.h"
29 #define PARENT_SEQ_INDEX 0
30 #define LISTVIEW_SEQ_INDEX 1
31 #define NUM_MSG_SEQUENCES 2
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)
42 static struct msg_sequence
*sequences
[NUM_MSG_SEQUENCES
];
44 static const struct message create_parent_wnd_seq
[] = {
45 { WM_GETMINMAXINFO
, sent
},
46 { WM_NCCREATE
, sent
},
47 { WM_NCCALCSIZE
, sent
|wparam
, 0 },
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 },
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
},
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) },
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
},
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
},
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
},
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
},
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
},
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 },
154 static LRESULT WINAPI
parent_wnd_proc(HWND hwnd
, UINT message
, WPARAM wParam
, LPARAM lParam
)
156 static long defwndproc_counter
= 0;
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
)
170 trace("parent: %p, %04x, %08lx, %08lx\n", hwnd
, message
, wParam
, lParam
);
172 msg
.message
= message
;
173 msg
.flags
= sent
|wparam
|lparam
;
174 if (defwndproc_counter
) msg
.flags
|= defwinproc
;
177 add_message(sequences
, PARENT_SEQ_INDEX
, &msg
);
180 defwndproc_counter
++;
181 ret
= DefWindowProcA(hwnd
, message
, wParam
, lParam
);
182 defwndproc_counter
--;
187 static BOOL
register_parent_wnd_class(void)
192 cls
.lpfnWndProc
= parent_wnd_proc
;
195 cls
.hInstance
= GetModuleHandleA(NULL
);
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
);
204 static HWND
create_parent_window(void)
206 if (!register_parent_wnd_class())
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
,
214 GetDesktopWindow(), NULL
, GetModuleHandleA(NULL
), NULL
);
217 static LRESULT WINAPI
listview_subclass_proc(HWND hwnd
, UINT message
, WPARAM wParam
, LPARAM lParam
)
219 struct subclass_info
*info
= (struct subclass_info
*)GetWindowLongPtrA(hwnd
, GWLP_USERDATA
);
220 static long defwndproc_counter
= 0;
224 trace("listview: %p, %04x, %08lx, %08lx\n", hwnd
, message
, wParam
, lParam
);
226 msg
.message
= message
;
227 msg
.flags
= sent
|wparam
|lparam
;
228 if (defwndproc_counter
) msg
.flags
|= defwinproc
;
231 msg
.id
= LISTVIEW_ID
;
232 add_message(sequences
, LISTVIEW_SEQ_INDEX
, &msg
);
234 defwndproc_counter
++;
235 ret
= CallWindowProcA(info
->oldproc
, hwnd
, message
, wParam
, lParam
);
236 defwndproc_counter
--;
240 static HWND
create_listview_control(void)
242 struct subclass_info
*info
;
246 info
= HeapAlloc(GetProcessHeap(), 0, sizeof(struct subclass_info
));
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());
259 HeapFree(GetProcessHeap(), 0, info
);
263 info
->oldproc
= (WNDPROC
)SetWindowLongPtrA(hwnd
, GWLP_WNDPROC
,
264 (LONG_PTR
)listview_subclass_proc
);
265 SetWindowLongPtrA(hwnd
, GWLP_USERDATA
, (LONG_PTR
)info
);
270 static HWND
create_custom_listview_control(DWORD style
)
272 struct subclass_info
*info
;
276 info
= HeapAlloc(GetProcessHeap(), 0, sizeof(struct subclass_info
));
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());
289 HeapFree(GetProcessHeap(), 0, info
);
293 info
->oldproc
= (WNDPROC
)SetWindowLongPtrA(hwnd
, GWLP_WNDPROC
,
294 (LONG_PTR
)listview_subclass_proc
);
295 SetWindowLongPtrA(hwnd
, GWLP_USERDATA
, (LONG_PTR
)info
);
300 static LRESULT WINAPI
header_subclass_proc(HWND hwnd
, UINT message
, WPARAM wParam
, LPARAM lParam
)
302 struct subclass_info
*info
= (struct subclass_info
*)GetWindowLongPtrA(hwnd
, GWLP_USERDATA
);
303 static long defwndproc_counter
= 0;
307 trace("header: %p, %04x, %08lx, %08lx\n", hwnd
, message
, wParam
, lParam
);
309 msg
.message
= message
;
310 msg
.flags
= sent
|wparam
|lparam
;
311 if (defwndproc_counter
) msg
.flags
|= defwinproc
;
315 add_message(sequences
, LISTVIEW_SEQ_INDEX
, &msg
);
317 defwndproc_counter
++;
318 ret
= CallWindowProcA(info
->oldproc
, hwnd
, message
, wParam
, lParam
);
319 defwndproc_counter
--;
323 static HWND
subclass_header(HWND hwndListview
)
325 struct subclass_info
*info
;
328 info
= HeapAlloc(GetProcessHeap(), 0, sizeof(struct subclass_info
));
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
);
340 static void test_images(void)
348 static CHAR hello
[] = "hello";
350 himl
= ImageList_Create(40, 40, 0, 4, 4);
351 ok(himl
!= NULL
, "failed to create imagelist\n");
353 hbmp
= CreateBitmap(40, 40, 1, 1, NULL
);
354 ok(hbmp
!= NULL
, "failed to create bitmap\n");
356 r
= ImageList_Add(himl
, hbmp
, 0);
357 ok(r
== 0, "should be zero\n");
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");
363 r
= SendMessage(hwnd
, LVM_SETEXTENDEDLISTVIEWSTYLE
, 0, 0x940);
364 ok(r
== 0, "should return zero\n");
366 r
= SendMessage(hwnd
, LVM_SETIMAGELIST
, 0, (LPARAM
)himl
);
367 ok(r
== 0, "should return zero\n");
369 r
= SendMessage(hwnd
, LVM_SETICONSPACING
, 0, MAKELONG(100,50));
370 /* returns dimensions */
372 r
= SendMessage(hwnd
, LVM_GETITEMCOUNT
, 0, 0);
373 ok(r
== 0, "should be zero items\n");
375 item
.mask
= LVIF_IMAGE
| LVIF_TEXT
;
380 r
= SendMessage(hwnd
, LVM_INSERTITEM
, 0, (LPARAM
) &item
);
381 ok(r
== -1, "should fail\n");
384 item
.pszText
= hello
;
385 r
= SendMessage(hwnd
, LVM_INSERTITEM
, 0, (LPARAM
) &item
);
386 ok(r
== 0, "should not fail\n");
388 memset(&r1
, 0, sizeof r1
);
390 r
= SendMessage(hwnd
, LVM_GETITEMRECT
, 0, (LPARAM
) &r1
);
392 r
= SendMessage(hwnd
, LVM_DELETEALLITEMS
, 0, 0);
393 ok(r
== TRUE
, "should not fail\n");
396 item
.pszText
= hello
;
397 r
= SendMessage(hwnd
, LVM_INSERTITEM
, 0, (LPARAM
) &item
);
398 ok(r
== 0, "should not fail\n");
400 memset(&r2
, 0, sizeof r2
);
402 r
= SendMessage(hwnd
, LVM_GETITEMRECT
, 0, (LPARAM
) &r2
);
404 ok(!memcmp(&r1
, &r2
, sizeof r1
), "rectangle should be the same\n");
409 static void test_checkboxes(void)
414 static CHAR text
[] = "Text",
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");
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;
429 r
= SendMessage(hwnd
, LVM_INSERTITEMA
, 0, (LPARAM
) &item
);
430 ok(r
== 0, "ret %d\n", r
);
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
);
438 /* Don't set LVIF_STATE */
439 item
.mask
= LVIF_TEXT
;
440 item
.stateMask
= 0xffff;
445 r
= SendMessage(hwnd
, LVM_INSERTITEMA
, 0, (LPARAM
) &item
);
446 ok(r
== 1, "ret %d\n", r
);
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
);
454 r
= SendMessage(hwnd
, LVM_SETEXTENDEDLISTVIEWSTYLE
, LVS_EX_CHECKBOXES
, LVS_EX_CHECKBOXES
);
455 ok(r
== 0, "should return zero\n");
457 /* Having turned on checkboxes, check that all existing items are set to 0x1000 (unchecked) */
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
);
464 /* Now add an item without specifying a state and check that its state goes to 0x1000 */
466 item
.mask
= LVIF_TEXT
;
468 item
.pszText
= text2
;
469 r
= SendMessage(hwnd
, LVM_INSERTITEMA
, 0, (LPARAM
) &item
);
470 ok(r
== 2, "ret %d\n", r
);
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
);
478 /* Add a further item this time specifying a state and still its state goes to 0x1000 */
480 item
.mask
= LVIF_TEXT
| LVIF_STATE
;
481 item
.stateMask
= 0xffff;
483 item
.pszText
= text3
;
484 r
= SendMessage(hwnd
, LVM_INSERTITEMA
, 0, (LPARAM
) &item
);
485 ok(r
== 3, "ret %d\n", r
);
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
);
493 /* Set an item's state to checked */
495 item
.mask
= LVIF_STATE
;
496 item
.stateMask
= 0xf000;
498 r
= SendMessage(hwnd
, LVM_SETITEMA
, 0, (LPARAM
) &item
);
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
);
506 /* Check that only the bits we asked for are returned,
507 * and that all the others are set to zero
510 item
.mask
= LVIF_STATE
;
511 item
.stateMask
= 0xf000;
513 r
= SendMessage(hwnd
, LVM_GETITEMA
, 0, (LPARAM
) &item
);
514 ok(item
.state
== 0x2000, "state %x\n", item
.state
);
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
);
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
);
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
);
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
);
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
);
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
);
546 /* Toggle checkbox tests (bug 9934) */
547 memset (&item
, 0xcc, sizeof(item
));
548 item
.mask
= LVIF_STATE
;
551 item
.state
= LVIS_FOCUSED
;
552 item
.stateMask
= LVIS_FOCUSED
;
553 r
= SendMessage(hwnd
, LVM_SETITEM
, 0, (LPARAM
) &item
);
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
);
562 r
= SendMessage(hwnd
, WM_KEYDOWN
, VK_SPACE
, 0);
564 r
= SendMessage(hwnd
, WM_KEYUP
, VK_SPACE
, 0);
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
);
573 r
= SendMessage(hwnd
, WM_KEYDOWN
, VK_SPACE
, 0);
575 r
= SendMessage(hwnd
, WM_KEYUP
, VK_SPACE
, 0);
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
);
587 static void insert_column(HWND hwnd
, int idx
)
592 memset(&column
, 0xcc, sizeof(column
));
593 column
.mask
= LVCF_SUBITEM
;
594 column
.iSubItem
= idx
;
596 rc
= ListView_InsertColumn(hwnd
, idx
, &column
);
600 static void insert_item(HWND hwnd
, int idx
)
602 static CHAR text
[] = "foo";
607 memset(&item
, 0xcc, sizeof (item
));
608 item
.mask
= LVIF_TEXT
;
613 rc
= ListView_InsertItem(hwnd
, &item
);
617 static void test_items(void)
619 const LPARAM lparamTest
= 0x42;
623 static CHAR text
[] = "Text";
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");
630 * Test setting/getting item params
633 /* Set up two columns */
634 insert_column(hwnd
, 0);
635 insert_column(hwnd
, 1);
637 /* Insert an item with just a param */
638 memset (&item
, 0xcc, sizeof (item
));
639 item
.mask
= LVIF_PARAM
;
642 item
.lParam
= lparamTest
;
643 r
= SendMessage(hwnd
, LVM_INSERTITEMA
, 0, (LPARAM
) &item
);
644 ok(r
== 0, "ret %d\n", r
);
646 /* Test getting of the param */
647 memset (&item
, 0xcc, sizeof (item
));
648 item
.mask
= LVIF_PARAM
;
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
);
655 /* Set up a subitem */
656 memset (&item
, 0xcc, sizeof (item
));
657 item
.mask
= LVIF_TEXT
;
661 r
= SendMessage(hwnd
, LVM_SETITEMA
, 0, (LPARAM
) &item
);
662 ok(r
!= 0, "ret %d\n", r
);
664 /* Query param from subitem: returns main item param */
665 memset (&item
, 0xcc, sizeof (item
));
666 item
.mask
= LVIF_PARAM
;
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
);
673 /* Set up param on first subitem: no effect */
674 memset (&item
, 0xcc, sizeof (item
));
675 item
.mask
= LVIF_PARAM
;
678 item
.lParam
= lparamTest
+1;
679 r
= SendMessage(hwnd
, LVM_SETITEMA
, 0, (LPARAM
) &item
);
680 ok(r
== 0, "ret %d\n", r
);
682 /* Query param from subitem again: should still return main item param */
683 memset (&item
, 0xcc, sizeof (item
));
684 item
.mask
= LVIF_PARAM
;
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
);
691 /**** Some tests of state highlighting ****/
692 memset (&item
, 0xcc, sizeof (item
));
693 item
.mask
= LVIF_STATE
;
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
);
701 item
.state
= LVIS_DROPHILITED
;
702 r
= SendMessage(hwnd
, LVM_SETITEM
, 0, (LPARAM
) &item
);
703 ok(r
!= 0, "ret %d\n", r
);
705 memset (&item
, 0xcc, sizeof (item
));
706 item
.mask
= LVIF_STATE
;
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
);
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
);
721 static void test_columns(void)
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");
731 /* Add a column with no mask */
732 memset(&column
, 0xcc, sizeof(column
));
734 rc
= ListView_InsertColumn(hwnd
, 0, &column
);
735 ok(rc
==0, "Inserting column with no mask failed with %d\n", rc
);
737 /* Check its width */
738 rc
= ListView_GetColumnWidth(hwnd
, 0);
740 broken(rc
==0), /* win9x */
741 "Inserting column with no mask failed to set width to 10 with %d\n", rc
);
745 /* test setting imagelist between WM_NCCREATE and WM_CREATE */
746 static WNDPROC listviewWndProc
;
747 static HIMAGELIST test_create_imagelist
;
749 static LRESULT CALLBACK
create_test_wndproc(HWND hwnd
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
)
751 if (uMsg
== WM_CREATE
)
753 LPCREATESTRUCT lpcs
= (LPCREATESTRUCT
)lParam
;
754 lpcs
->style
|= LVS_REPORT
;
755 SendMessage(hwnd
, LVM_SETIMAGELIST
, 0, (LPARAM
)test_create_imagelist
);
757 return CallWindowProc(listviewWndProc
, hwnd
, uMsg
, wParam
, lParam
);
760 static void test_create(void)
765 cls
.cbSize
= sizeof(WNDCLASSEX
);
766 ok(GetClassInfoEx(GetModuleHandle(NULL
), "SysListView32", &cls
), "GetClassInfoEx failed\n");
767 listviewWndProc
= cls
.lpfnWndProc
;
768 cls
.lpfnWndProc
= create_test_wndproc
;
769 cls
.lpszClassName
= "MyListView32";
770 ok(RegisterClassEx(&cls
), "RegisterClassEx failed\n");
772 test_create_imagelist
= ImageList_Create(16, 16, 0, 5, 10);
773 hList
= CreateWindow("MyListView32", "Test", WS_VISIBLE
, 0, 0, 100, 100, NULL
, NULL
, GetModuleHandle(NULL
), 0);
774 ok((HIMAGELIST
)SendMessage(hList
, LVM_GETIMAGELIST
, 0, 0) == test_create_imagelist
, "Image list not obtained\n");
775 hHeader
= (HWND
)SendMessage(hList
, LVM_GETHEADER
, 0, 0);
776 ok(IsWindow(hHeader
) && IsWindowVisible(hHeader
), "Listview not in report mode\n");
777 DestroyWindow(hList
);
780 static void test_redraw(void)
782 HWND hwnd
, hwndheader
;
784 hwnd
= create_listview_control();
785 hwndheader
= subclass_header(hwnd
);
787 flush_sequences(sequences
, NUM_MSG_SEQUENCES
);
789 trace("invalidate & update\n");
790 InvalidateRect(hwnd
, NULL
, TRUE
);
792 ok_sequence(sequences
, LISTVIEW_SEQ_INDEX
, redraw_listview_seq
, "redraw listview", FALSE
);
794 flush_sequences(sequences
, NUM_MSG_SEQUENCES
);
799 static LRESULT WINAPI
cd_wndproc(HWND hwnd
, UINT msg
, WPARAM wp
, LPARAM lp
)
801 COLORREF clr
, c0ffee
= RGB(0xc0, 0xff, 0xee);
803 if(msg
== WM_NOTIFY
) {
804 NMHDR
*nmhdr
= (PVOID
)lp
;
805 if(nmhdr
->code
== NM_CUSTOMDRAW
) {
806 NMLVCUSTOMDRAW
*nmlvcd
= (PVOID
)nmhdr
;
807 trace("NMCUSTOMDRAW (0x%.8x)\n", nmlvcd
->nmcd
.dwDrawStage
);
808 switch(nmlvcd
->nmcd
.dwDrawStage
) {
810 SetBkColor(nmlvcd
->nmcd
.hdc
, c0ffee
);
811 return CDRF_NOTIFYITEMDRAW
;
812 case CDDS_ITEMPREPAINT
:
813 nmlvcd
->clrTextBk
= CLR_DEFAULT
;
814 return CDRF_NOTIFYSUBITEMDRAW
;
815 case CDDS_ITEMPREPAINT
| CDDS_SUBITEM
:
816 clr
= GetBkColor(nmlvcd
->nmcd
.hdc
);
817 todo_wine
ok(clr
== c0ffee
, "clr=%.8x\n", clr
);
818 return CDRF_NOTIFYPOSTPAINT
;
819 case CDDS_ITEMPOSTPAINT
| CDDS_SUBITEM
:
820 clr
= GetBkColor(nmlvcd
->nmcd
.hdc
);
821 todo_wine
ok(clr
== c0ffee
, "clr=%.8x\n", clr
);
822 return CDRF_DODEFAULT
;
824 return CDRF_DODEFAULT
;
828 return DefWindowProcA(hwnd
, msg
, wp
, lp
);
831 static void test_customdraw(void)
836 hwnd
= create_listview_control();
838 insert_column(hwnd
, 0);
839 insert_column(hwnd
, 1);
840 insert_item(hwnd
, 0);
842 oldwndproc
= (WNDPROC
)SetWindowLongPtr(hwndparent
, GWLP_WNDPROC
,
843 (LONG_PTR
)cd_wndproc
);
845 InvalidateRect(hwnd
, NULL
, TRUE
);
848 SetWindowLongPtr(hwndparent
, GWLP_WNDPROC
, (LONG_PTR
)oldwndproc
);
853 static void test_icon_spacing(void)
855 /* LVM_SETICONSPACING */
856 /* note: LVM_SETICONSPACING returns the previous icon spacing if successful */
862 hwnd
= create_custom_listview_control(LVS_ICON
);
863 ok(hwnd
!= NULL
, "failed to create a listview window\n");
865 r
= SendMessage(hwnd
, WM_NOTIFYFORMAT
, (WPARAM
)hwndparent
, (LPARAM
)NF_REQUERY
);
868 /* reset the icon spacing to defaults */
869 SendMessage(hwnd
, LVM_SETICONSPACING
, 0, MAKELPARAM(-1, -1));
871 /* now we can request what the defaults are */
872 r
= SendMessage(hwnd
, LVM_SETICONSPACING
, 0, MAKELPARAM(-1, -1));
876 flush_sequences(sequences
, NUM_MSG_SEQUENCES
);
878 trace("test icon spacing\n");
880 r
= SendMessage(hwnd
, LVM_SETICONSPACING
, 0, MAKELPARAM(20, 30));
881 ok(r
== MAKELONG(w
, h
) ||
882 broken(r
== MAKELONG(w
, w
)), /* win98 */
883 "Expected %d, got %d\n", MAKELONG(w
, h
), r
);
885 r
= SendMessage(hwnd
, LVM_SETICONSPACING
, 0, MAKELPARAM(25, 35));
886 expect(MAKELONG(20,30), r
);
888 r
= SendMessage(hwnd
, LVM_SETICONSPACING
, 0, MAKELPARAM(-1,-1));
889 expect(MAKELONG(25,35), r
);
891 ok_sequence(sequences
, LISTVIEW_SEQ_INDEX
, listview_icon_spacing_seq
, "test icon spacing seq", FALSE
);
893 flush_sequences(sequences
, NUM_MSG_SEQUENCES
);
897 static void test_color(void)
899 /* SETBKCOLOR/GETBKCOLOR, SETTEXTCOLOR/GETTEXTCOLOR, SETTEXTBKCOLOR/GETTEXTBKCOLOR */
906 COLORREF colors
[4] = {RGB(0,0,0), RGB(100,50,200), CLR_NONE
, RGB(255,255,255)};
908 hwnd
= create_listview_control();
909 ok(hwnd
!= NULL
, "failed to create a listview window\n");
911 flush_sequences(sequences
, NUM_MSG_SEQUENCES
);
913 trace("test color seq\n");
914 for (i
= 0; i
< 4; i
++)
918 r
= SendMessage(hwnd
, LVM_SETBKCOLOR
, 0, color
);
920 r
= SendMessage(hwnd
, LVM_GETBKCOLOR
, 0, color
);
923 r
= SendMessage(hwnd
, LVM_SETTEXTCOLOR
, 0, color
);
925 r
= SendMessage(hwnd
, LVM_GETTEXTCOLOR
, 0, color
);
928 r
= SendMessage(hwnd
, LVM_SETTEXTBKCOLOR
, 0, color
);
930 r
= SendMessage(hwnd
, LVM_GETTEXTBKCOLOR
, 0, color
);
934 ok_sequence(sequences
, LISTVIEW_SEQ_INDEX
, listview_color_seq
, "test color seq", FALSE
);
936 flush_sequences(sequences
, NUM_MSG_SEQUENCES
);
940 static void test_item_count(void)
942 /* LVM_INSERTITEM, LVM_DELETEITEM, LVM_DELETEALLITEMS, LVM_GETITEMCOUNT */
950 static CHAR item0text
[] = "item0";
951 static CHAR item1text
[] = "item1";
952 static CHAR item2text
[] = "item2";
954 hwnd
= create_listview_control();
955 ok(hwnd
!= NULL
, "failed to create a listview window\n");
957 flush_sequences(sequences
, NUM_MSG_SEQUENCES
);
959 trace("test item count\n");
961 r
= SendMessage(hwnd
, LVM_GETITEMCOUNT
, 0, 0);
965 item0
.mask
= LVIF_TEXT
;
968 item0
.pszText
= item0text
;
969 r
= SendMessage(hwnd
, LVM_INSERTITEM
, 0, (LPARAM
) &item0
);
973 item1
.mask
= LVIF_TEXT
;
976 item1
.pszText
= item1text
;
977 r
= SendMessage(hwnd
, LVM_INSERTITEM
, 0, (LPARAM
) &item1
);
980 /* [item0, item1, item2] */
981 item2
.mask
= LVIF_TEXT
;
984 item2
.pszText
= item2text
;
985 r
= SendMessage(hwnd
, LVM_INSERTITEM
, 0, (LPARAM
) &item2
);
988 r
= SendMessage(hwnd
, LVM_GETITEMCOUNT
, 0, 0);
992 r
= SendMessage(hwnd
, LVM_DELETEITEM
, 2, 0);
995 r
= SendMessage(hwnd
, LVM_GETITEMCOUNT
, 0, 0);
999 r
= SendMessage(hwnd
, LVM_DELETEALLITEMS
, 0, 0);
1002 r
= SendMessage(hwnd
, LVM_GETITEMCOUNT
, 0, 0);
1006 r
= SendMessage(hwnd
, LVM_INSERTITEM
, 0, (LPARAM
) &item1
);
1009 /* [item0, item1] */
1010 r
= SendMessage(hwnd
, LVM_INSERTITEM
, 0, (LPARAM
) &item1
);
1013 r
= SendMessage(hwnd
, LVM_GETITEMCOUNT
, 0, 0);
1016 /* [item0, item1, item2] */
1017 r
= SendMessage(hwnd
, LVM_INSERTITEM
, 0, (LPARAM
) &item2
);
1020 r
= SendMessage(hwnd
, LVM_GETITEMCOUNT
, 0, 0);
1023 ok_sequence(sequences
, LISTVIEW_SEQ_INDEX
, listview_item_count_seq
, "test item count seq", FALSE
);
1025 flush_sequences(sequences
, NUM_MSG_SEQUENCES
);
1026 DestroyWindow(hwnd
);
1029 static void test_item_position(void)
1031 /* LVM_SETITEMPOSITION/LVM_GETITEMPOSITION */
1040 static CHAR item0text
[] = "item0";
1041 static CHAR item1text
[] = "item1";
1042 static CHAR item2text
[] = "item2";
1044 hwnd
= create_custom_listview_control(LVS_ICON
);
1045 ok(hwnd
!= NULL
, "failed to create a listview window\n");
1047 flush_sequences(sequences
, NUM_MSG_SEQUENCES
);
1049 trace("test item position\n");
1052 item0
.mask
= LVIF_TEXT
;
1055 item0
.pszText
= item0text
;
1056 r
= SendMessage(hwnd
, LVM_INSERTITEM
, 0, (LPARAM
) &item0
);
1059 /* [item0, item1] */
1060 item1
.mask
= LVIF_TEXT
;
1063 item1
.pszText
= item1text
;
1064 r
= SendMessage(hwnd
, LVM_INSERTITEM
, 0, (LPARAM
) &item1
);
1067 /* [item0, item1, item2] */
1068 item2
.mask
= LVIF_TEXT
;
1071 item2
.pszText
= item2text
;
1072 r
= SendMessage(hwnd
, LVM_INSERTITEM
, 0, (LPARAM
) &item2
);
1075 r
= SendMessage(hwnd
, LVM_SETITEMPOSITION
, 1, MAKELPARAM(10,5));
1077 r
= SendMessage(hwnd
, LVM_GETITEMPOSITION
, 1, (LPARAM
) &position
);
1079 expect2(10, 5, position
.x
, position
.y
);
1081 r
= SendMessage(hwnd
, LVM_SETITEMPOSITION
, 2, MAKELPARAM(0,0));
1083 r
= SendMessage(hwnd
, LVM_GETITEMPOSITION
, 2, (LPARAM
) &position
);
1085 expect2(0, 0, position
.x
, position
.y
);
1087 r
= SendMessage(hwnd
, LVM_SETITEMPOSITION
, 0, MAKELPARAM(20,20));
1089 r
= SendMessage(hwnd
, LVM_GETITEMPOSITION
, 0, (LPARAM
) &position
);
1091 expect2(20, 20, position
.x
, position
.y
);
1093 ok_sequence(sequences
, LISTVIEW_SEQ_INDEX
, listview_itempos_seq
, "test item position seq", TRUE
);
1095 flush_sequences(sequences
, NUM_MSG_SEQUENCES
);
1096 DestroyWindow(hwnd
);
1099 static void test_getorigin(void)
1107 position
.x
= position
.y
= 0;
1109 hwnd
= create_custom_listview_control(LVS_ICON
);
1110 ok(hwnd
!= NULL
, "failed to create a listview window\n");
1111 flush_sequences(sequences
, NUM_MSG_SEQUENCES
);
1112 trace("test get origin results\n");
1113 r
= SendMessage(hwnd
, LVM_GETORIGIN
, 0, (LPARAM
)&position
);
1115 flush_sequences(sequences
, NUM_MSG_SEQUENCES
);
1116 DestroyWindow(hwnd
);
1118 hwnd
= create_custom_listview_control(LVS_SMALLICON
);
1119 ok(hwnd
!= NULL
, "failed to create a listview window\n");
1120 flush_sequences(sequences
, NUM_MSG_SEQUENCES
);
1121 trace("test get origin results\n");
1122 r
= SendMessage(hwnd
, LVM_GETORIGIN
, 0, (LPARAM
)&position
);
1124 flush_sequences(sequences
, NUM_MSG_SEQUENCES
);
1125 DestroyWindow(hwnd
);
1127 hwnd
= create_custom_listview_control(LVS_LIST
);
1128 ok(hwnd
!= NULL
, "failed to create a listview window\n");
1129 flush_sequences(sequences
, NUM_MSG_SEQUENCES
);
1130 trace("test get origin results\n");
1131 r
= SendMessage(hwnd
, LVM_GETORIGIN
, 0, (LPARAM
)&position
);
1133 flush_sequences(sequences
, NUM_MSG_SEQUENCES
);
1134 DestroyWindow(hwnd
);
1136 hwnd
= create_custom_listview_control(LVS_REPORT
);
1137 ok(hwnd
!= NULL
, "failed to create a listview window\n");
1138 flush_sequences(sequences
, NUM_MSG_SEQUENCES
);
1139 trace("test get origin results\n");
1140 r
= SendMessage(hwnd
, LVM_GETORIGIN
, 0, (LPARAM
)&position
);
1142 flush_sequences(sequences
, NUM_MSG_SEQUENCES
);
1143 DestroyWindow(hwnd
);
1147 static void test_multiselect(void)
1149 typedef struct t_select_task
1160 int i
,j
,item_count
,selected_count
;
1161 static const int items
=5;
1165 static struct t_select_task task_list
[] = {
1166 { "using VK_DOWN", 0, VK_DOWN
, -1, -1 },
1167 { "using VK_UP", -1, VK_UP
, -1, -1 },
1168 { "using VK_END", 0, VK_END
, 1, -1 },
1169 { "using VK_HOME", -1, VK_HOME
, 1, -1 }
1173 hwnd
= create_listview_control();
1175 for (i
=0;i
<items
;i
++) {
1176 insert_item(hwnd
, 0);
1179 item_count
= (int)SendMessage(hwnd
, LVM_GETITEMCOUNT
, 0, 0);
1181 expect(items
,item_count
);
1184 task
= task_list
[i
];
1186 /* deselect all items */
1187 ListView_SetItemState(hwnd
, -1, 0, LVIS_SELECTED
);
1188 SendMessage(hwnd
, LVM_SETSELECTIONMARK
, 0, -1);
1190 /* set initial position */
1191 SendMessage(hwnd
, LVM_SETSELECTIONMARK
, 0, (task
.initPos
== -1 ? item_count
-1 : task
.initPos
));
1192 ListView_SetItemState(hwnd
,(task
.initPos
== -1 ? item_count
-1 : task
.initPos
),LVIS_SELECTED
,LVIS_SELECTED
);
1194 selected_count
= (int)SendMessage(hwnd
, LVM_GETSELECTEDCOUNT
, 0, 0);
1196 ok(selected_count
== 1, "There should be only one selected item at the beginning (is %d)\n",selected_count
);
1198 /* Set SHIFT key pressed */
1199 GetKeyboardState(kstate
);
1200 kstate
[VK_SHIFT
]=0x80;
1201 SetKeyboardState(kstate
);
1203 for (j
=1;j
<=(task
.count
== -1 ? item_count
: task
.count
);j
++) {
1204 r
= SendMessage(hwnd
, WM_KEYDOWN
, task
.loopVK
, 0);
1206 r
= SendMessage(hwnd
, WM_KEYUP
, task
.loopVK
, 0);
1210 selected_count
= (int)SendMessage(hwnd
, LVM_GETSELECTEDCOUNT
, 0, 0);
1212 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
);
1214 /* Set SHIFT key released */
1215 GetKeyboardState(kstate
);
1216 kstate
[VK_SHIFT
]=0x00;
1217 SetKeyboardState(kstate
);
1219 DestroyWindow(hwnd
);
1222 START_TEST(listview
)
1225 BOOL (WINAPI
*pInitCommonControlsEx
)(const INITCOMMONCONTROLSEX
*);
1227 hComctl32
= GetModuleHandleA("comctl32.dll");
1228 pInitCommonControlsEx
= (void*)GetProcAddress(hComctl32
, "InitCommonControlsEx");
1229 if (pInitCommonControlsEx
)
1231 INITCOMMONCONTROLSEX iccex
;
1232 iccex
.dwSize
= sizeof(iccex
);
1233 iccex
.dwICC
= ICC_LISTVIEW_CLASSES
;
1234 pInitCommonControlsEx(&iccex
);
1237 InitCommonControls();
1239 init_msg_sequences(sequences
, NUM_MSG_SEQUENCES
);
1241 flush_sequences(sequences
, NUM_MSG_SEQUENCES
);
1242 hwndparent
= create_parent_window();
1243 ok_sequence(sequences
, PARENT_SEQ_INDEX
, create_parent_wnd_seq
, "create parent window", TRUE
);
1244 flush_sequences(sequences
, NUM_MSG_SEQUENCES
);
1252 test_icon_spacing();
1255 test_item_position();