4 * Copyright 2006 Mike McCormack for CodeWeavers
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
25 #include "wine/test.h"
28 #define PARENT_SEQ_INDEX 0
29 #define LISTVIEW_SEQ_INDEX 1
30 #define NUM_MSG_SEQUENCES 2
37 static struct msg_sequence *sequences[NUM_MSG_SEQUENCES];
39 static const struct message redraw_listview_seq[] = {
40 { WM_PAINT, sent|id, 0, 0, LISTVIEW_ID },
41 { WM_PAINT, sent|id, 0, 0, HEADER_ID },
42 { WM_NCPAINT, sent|id|defwinproc, 0, 0, HEADER_ID },
43 { WM_ERASEBKGND, sent|id|defwinproc, 0, 0, HEADER_ID },
44 { WM_NOTIFY, sent|id|defwinproc, 0, 0, LISTVIEW_ID },
45 { WM_NCPAINT, sent|id|defwinproc, 0, 0, LISTVIEW_ID },
46 { WM_ERASEBKGND, sent|id|defwinproc, 0, 0, LISTVIEW_ID },
55 static LRESULT WINAPI parent_wnd_proc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
57 static long defwndproc_counter = 0;
61 /* do not log painting messages */
62 if (message != WM_PAINT &&
63 message != WM_ERASEBKGND &&
64 message != WM_NCPAINT &&
65 message != WM_NCHITTEST &&
66 message != WM_GETTEXT &&
67 message != WM_GETICON &&
68 message != WM_DEVICECHANGE)
70 trace("parent: %p, %04x, %08x, %08lx\n", hwnd, message, wParam, lParam);
72 msg.message = message;
73 msg.flags = sent|wparam|lparam;
74 if (defwndproc_counter) msg.flags |= defwinproc;
77 add_message(sequences, PARENT_SEQ_INDEX, &msg);
81 ret = DefWindowProcA(hwnd, message, wParam, lParam);
87 static BOOL register_parent_wnd_class(void)
92 cls.lpfnWndProc = parent_wnd_proc;
95 cls.hInstance = GetModuleHandleA(NULL);
97 cls.hCursor = LoadCursorA(0, (LPSTR)IDC_ARROW);
98 cls.hbrBackground = GetStockObject(WHITE_BRUSH);
99 cls.lpszMenuName = NULL;
100 cls.lpszClassName = "Listview test parent class";
101 return RegisterClassA(&cls);
104 static HWND create_parent_window(void)
106 if (!register_parent_wnd_class())
109 return CreateWindowEx(0, "Listview test parent class",
110 "Listview test parent window",
111 WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX |
112 WS_MAXIMIZEBOX | WS_VISIBLE,
114 GetDesktopWindow(), NULL, GetModuleHandleA(NULL), NULL);
117 static LRESULT WINAPI listview_subclass_proc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
119 struct subclass_info *info = (struct subclass_info *)GetWindowLongA(hwnd, GWL_USERDATA);
120 static long defwndproc_counter = 0;
124 trace("listview: %p, %04x, %08x, %08lx\n", hwnd, message, wParam, lParam);
126 msg.message = message;
127 msg.flags = sent|wparam|lparam;
128 if (defwndproc_counter) msg.flags |= defwinproc;
131 msg.id = LISTVIEW_ID;
132 add_message(sequences, LISTVIEW_SEQ_INDEX, &msg);
134 defwndproc_counter++;
135 ret = CallWindowProcA(info->oldproc, hwnd, message, wParam, lParam);
136 defwndproc_counter--;
140 static HWND create_listview_control()
142 struct subclass_info *info;
146 info = HeapAlloc(GetProcessHeap(), 0, sizeof(struct subclass_info));
150 GetClientRect(hwndparent, &rect);
151 hwnd = CreateWindowExA(0, WC_LISTVIEW, "foo",
152 WS_CHILD | WS_BORDER | WS_VISIBLE | LVS_REPORT,
153 0, 0, rect.right, rect.bottom,
154 hwndparent, NULL, GetModuleHandleA(NULL), NULL);
155 ok(hwnd != NULL, "gle=%d\n", GetLastError());
159 HeapFree(GetProcessHeap(), 0, info);
163 info->oldproc = (WNDPROC)SetWindowLongA(hwnd, GWL_WNDPROC,
164 (LONG)listview_subclass_proc);
165 SetWindowLongA(hwnd, GWL_USERDATA, (LONG)info);
170 static LRESULT WINAPI header_subclass_proc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
172 struct subclass_info *info = (struct subclass_info *)GetWindowLongA(hwnd, GWL_USERDATA);
173 static long defwndproc_counter = 0;
177 trace("header: %p, %04x, %08x, %08lx\n", hwnd, message, wParam, lParam);
179 msg.message = message;
180 msg.flags = sent|wparam|lparam;
181 if (defwndproc_counter) msg.flags |= defwinproc;
185 add_message(sequences, LISTVIEW_SEQ_INDEX, &msg);
187 defwndproc_counter++;
188 ret = CallWindowProcA(info->oldproc, hwnd, message, wParam, lParam);
189 defwndproc_counter--;
193 static HWND subclass_header(HWND hwndListview)
195 struct subclass_info *info;
198 info = HeapAlloc(GetProcessHeap(), 0, sizeof(struct subclass_info));
202 hwnd = ListView_GetHeader(hwndListview);
203 info->oldproc = (WNDPROC)SetWindowLongA(hwnd, GWL_WNDPROC,
204 (LONG)header_subclass_proc);
205 SetWindowLongA(hwnd, GWL_USERDATA, (LONG)info);
210 static void test_images(void)
218 static CHAR hello[] = "hello";
220 himl = ImageList_Create(40, 40, 0, 4, 4);
221 ok(himl != NULL, "failed to create imagelist\n");
223 hbmp = CreateBitmap(40, 40, 1, 1, NULL);
224 ok(hbmp != NULL, "failed to create bitmap\n");
226 r = ImageList_Add(himl, hbmp, 0);
227 ok(r == 0, "should be zero\n");
229 hwnd = CreateWindowEx(0, "SysListView32", "foo", LVS_OWNERDRAWFIXED,
230 10, 10, 100, 200, hwndparent, NULL, NULL, NULL);
231 ok(hwnd != NULL, "failed to create listview window\n");
233 r = SendMessage(hwnd, LVM_SETEXTENDEDLISTVIEWSTYLE, 0, 0x940);
234 ok(r == 0, "should return zero\n");
236 r = SendMessage(hwnd, LVM_SETIMAGELIST, 0, (LPARAM)himl);
237 ok(r == 0, "should return zero\n");
239 r = SendMessage(hwnd, LVM_SETICONSPACING, 0, MAKELONG(100,50));
240 /* returns dimensions */
242 r = SendMessage(hwnd, LVM_GETITEMCOUNT, 0, 0);
243 ok(r == 0, "should be zero items\n");
245 item.mask = LVIF_IMAGE | LVIF_TEXT;
250 r = SendMessage(hwnd, LVM_INSERTITEM, 0, (LPARAM) &item);
251 ok(r == -1, "should fail\n");
254 item.pszText = hello;
255 r = SendMessage(hwnd, LVM_INSERTITEM, 0, (LPARAM) &item);
256 ok(r == 0, "should not fail\n");
258 memset(&r1, 0, sizeof r1);
260 r = SendMessage(hwnd, LVM_GETITEMRECT, 0, (LPARAM) &r1);
262 r = SendMessage(hwnd, LVM_DELETEALLITEMS, 0, 0);
263 ok(r == TRUE, "should not fail\n");
266 item.pszText = hello;
267 r = SendMessage(hwnd, LVM_INSERTITEM, 0, (LPARAM) &item);
268 ok(r == 0, "should not fail\n");
270 memset(&r2, 0, sizeof r2);
272 r = SendMessage(hwnd, LVM_GETITEMRECT, 0, (LPARAM) &r2);
274 ok(!memcmp(&r1, &r2, sizeof r1), "rectangle should be the same\n");
279 static void test_checkboxes(void)
284 static CHAR text[] = "Text",
288 hwnd = CreateWindowEx(0, "SysListView32", "foo", LVS_REPORT,
289 10, 10, 100, 200, hwndparent, NULL, NULL, NULL);
290 ok(hwnd != NULL, "failed to create listview window\n");
292 /* first without LVS_EX_CHECKBOXES set and an item and check that state is preserved */
293 item.mask = LVIF_TEXT | LVIF_STATE;
294 item.stateMask = 0xffff;
299 r = SendMessage(hwnd, LVM_INSERTITEMA, 0, (LPARAM) &item);
300 ok(r == 0, "ret %d\n", r);
303 item.mask = LVIF_STATE;
304 item.stateMask = 0xffff;
305 r = SendMessage(hwnd, LVM_GETITEMA, 0, (LPARAM) &item);
306 ok(item.state == 0xfccc, "state %x\n", item.state);
308 /* Don't set LVIF_STATE */
309 item.mask = LVIF_TEXT;
310 item.stateMask = 0xffff;
315 r = SendMessage(hwnd, LVM_INSERTITEMA, 0, (LPARAM) &item);
316 ok(r == 1, "ret %d\n", r);
319 item.mask = LVIF_STATE;
320 item.stateMask = 0xffff;
321 r = SendMessage(hwnd, LVM_GETITEMA, 0, (LPARAM) &item);
322 ok(item.state == 0, "state %x\n", item.state);
324 r = SendMessage(hwnd, LVM_SETEXTENDEDLISTVIEWSTYLE, LVS_EX_CHECKBOXES, LVS_EX_CHECKBOXES);
325 ok(r == 0, "should return zero\n");
327 /* Having turned on checkboxes, check that all existing items are set to 0x1000 (unchecked) */
329 item.mask = LVIF_STATE;
330 item.stateMask = 0xffff;
331 r = SendMessage(hwnd, LVM_GETITEMA, 0, (LPARAM) &item);
332 ok(item.state == 0x1ccc, "state %x\n", item.state);
334 /* Now add an item without specifying a state and check that its state goes to 0x1000 */
336 item.mask = LVIF_TEXT;
338 item.pszText = text2;
339 r = SendMessage(hwnd, LVM_INSERTITEMA, 0, (LPARAM) &item);
340 ok(r == 2, "ret %d\n", r);
343 item.mask = LVIF_STATE;
344 item.stateMask = 0xffff;
345 r = SendMessage(hwnd, LVM_GETITEMA, 0, (LPARAM) &item);
346 ok(item.state == 0x1000, "state %x\n", item.state);
348 /* Add a further item this time specifying a state and still its state goes to 0x1000 */
350 item.mask = LVIF_TEXT | LVIF_STATE;
351 item.stateMask = 0xffff;
353 item.pszText = text3;
354 r = SendMessage(hwnd, LVM_INSERTITEMA, 0, (LPARAM) &item);
355 ok(r == 3, "ret %d\n", r);
358 item.mask = LVIF_STATE;
359 item.stateMask = 0xffff;
360 r = SendMessage(hwnd, LVM_GETITEMA, 0, (LPARAM) &item);
361 ok(item.state == 0x1aaa, "state %x\n", item.state);
363 /* Set an item's state to checked */
365 item.mask = LVIF_STATE;
366 item.stateMask = 0xf000;
368 r = SendMessage(hwnd, LVM_SETITEMA, 0, (LPARAM) &item);
371 item.mask = LVIF_STATE;
372 item.stateMask = 0xffff;
373 r = SendMessage(hwnd, LVM_GETITEMA, 0, (LPARAM) &item);
374 ok(item.state == 0x2aaa, "state %x\n", item.state);
376 /* Check that only the bits we asked for are returned,
377 * and that all the others are set to zero
380 item.mask = LVIF_STATE;
381 item.stateMask = 0xf000;
383 r = SendMessage(hwnd, LVM_GETITEMA, 0, (LPARAM) &item);
384 ok(item.state == 0x2000, "state %x\n", item.state);
386 /* Set the style again and check that doesn't change an item's state */
387 r = SendMessage(hwnd, LVM_SETEXTENDEDLISTVIEWSTYLE, LVS_EX_CHECKBOXES, LVS_EX_CHECKBOXES);
388 ok(r == LVS_EX_CHECKBOXES, "ret %x\n", r);
391 item.mask = LVIF_STATE;
392 item.stateMask = 0xffff;
393 r = SendMessage(hwnd, LVM_GETITEMA, 0, (LPARAM) &item);
394 ok(item.state == 0x2aaa, "state %x\n", item.state);
396 /* Unsetting the checkbox extended style doesn't change an item's state */
397 r = SendMessage(hwnd, LVM_SETEXTENDEDLISTVIEWSTYLE, LVS_EX_CHECKBOXES, 0);
398 ok(r == LVS_EX_CHECKBOXES, "ret %x\n", r);
401 item.mask = LVIF_STATE;
402 item.stateMask = 0xffff;
403 r = SendMessage(hwnd, LVM_GETITEMA, 0, (LPARAM) &item);
404 ok(item.state == 0x2aaa, "state %x\n", item.state);
406 /* Now setting the style again will change an item's state */
407 r = SendMessage(hwnd, LVM_SETEXTENDEDLISTVIEWSTYLE, LVS_EX_CHECKBOXES, LVS_EX_CHECKBOXES);
408 ok(r == 0, "ret %x\n", r);
411 item.mask = LVIF_STATE;
412 item.stateMask = 0xffff;
413 r = SendMessage(hwnd, LVM_GETITEMA, 0, (LPARAM) &item);
414 ok(item.state == 0x1aaa, "state %x\n", item.state);
419 static void test_items(void)
421 const LPARAM lparamTest = 0x42;
426 static CHAR text[] = "Text";
428 hwnd = CreateWindowEx(0, "SysListView32", "foo", LVS_REPORT,
429 10, 10, 100, 200, hwndparent, NULL, NULL, NULL);
430 ok(hwnd != NULL, "failed to create listview window\n");
433 * Test setting/getting item params
436 /* Set up two columns */
437 column.mask = LVCF_SUBITEM;
439 r = SendMessage(hwnd, LVM_INSERTCOLUMNA, 0, (LPARAM)&column);
440 ok(r == 0, "ret %d\n", r);
442 r = SendMessage(hwnd, LVM_INSERTCOLUMNA, 1, (LPARAM)&column);
443 ok(r == 1, "ret %d\n", r);
445 /* Insert an item with just a param */
446 memset (&item, 0xaa, sizeof (item));
447 item.mask = LVIF_PARAM;
450 item.lParam = lparamTest;
451 r = SendMessage(hwnd, LVM_INSERTITEMA, 0, (LPARAM) &item);
452 ok(r == 0, "ret %d\n", r);
454 /* Test getting of the param */
455 memset (&item, 0xaa, sizeof (item));
456 item.mask = LVIF_PARAM;
459 r = SendMessage(hwnd, LVM_GETITEMA, 0, (LPARAM) &item);
460 ok(r != 0, "ret %d\n", r);
461 ok(item.lParam == lparamTest, "got lParam %lx, expected %lx\n", item.lParam, lparamTest);
463 /* Set up a subitem */
464 memset (&item, 0xaa, sizeof (item));
465 item.mask = LVIF_TEXT;
469 r = SendMessage(hwnd, LVM_SETITEMA, 0, (LPARAM) &item);
470 ok(r != 0, "ret %d\n", r);
472 /* Query param from subitem: returns main item param */
473 memset (&item, 0xaa, sizeof (item));
474 item.mask = LVIF_PARAM;
477 r = SendMessage(hwnd, LVM_GETITEMA, 0, (LPARAM) &item);
478 ok(r != 0, "ret %d\n", r);
479 ok(item.lParam == lparamTest, "got lParam %lx, expected %lx\n", item.lParam, lparamTest);
481 /* Set up param on first subitem: no effect */
482 memset (&item, 0xaa, sizeof (item));
483 item.mask = LVIF_PARAM;
486 item.lParam = lparamTest+1;
487 r = SendMessage(hwnd, LVM_SETITEMA, 0, (LPARAM) &item);
488 ok(r == 0, "ret %d\n", r);
490 /* Query param from subitem again: should still return main item param */
491 memset (&item, 0xaa, sizeof (item));
492 item.mask = LVIF_PARAM;
495 r = SendMessage(hwnd, LVM_GETITEMA, 0, (LPARAM) &item);
496 ok(r != 0, "ret %d\n", r);
497 ok(item.lParam == lparamTest, "got lParam %lx, expected %lx\n", item.lParam, lparamTest);
499 /**** Some tests of state highlighting ****/
500 memset (&item, 0xaa, sizeof (item));
501 item.mask = LVIF_STATE;
504 item.state = LVIS_SELECTED;
505 item.stateMask = LVIS_SELECTED | LVIS_DROPHILITED;
506 r = SendMessage(hwnd, LVM_SETITEM, 0, (LPARAM) &item);
507 ok(r != 0, "ret %d\n", r);
509 item.state = LVIS_DROPHILITED;
510 r = SendMessage(hwnd, LVM_SETITEM, 0, (LPARAM) &item);
511 ok(r != 0, "ret %d\n", r);
513 memset (&item, 0xaa, sizeof (item));
514 item.mask = LVIF_STATE;
518 r = SendMessage(hwnd, LVM_GETITEM, 0, (LPARAM) &item);
519 ok(r != 0, "ret %d\n", r);
520 ok(item.state == LVIS_SELECTED, "got state %x, expected %x\n", item.state, LVIS_SELECTED);
522 r = SendMessage(hwnd, LVM_GETITEM, 0, (LPARAM) &item);
523 ok(r != 0, "ret %d\n", r);
524 todo_wine ok(item.state == LVIS_DROPHILITED, "got state %x, expected %x\n", item.state, LVIS_DROPHILITED);
529 /* test setting imagelist between WM_NCCREATE and WM_CREATE */
530 static WNDPROC listviewWndProc;
531 static HIMAGELIST test_create_imagelist;
533 static LRESULT CALLBACK create_test_wndproc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
535 if (uMsg == WM_CREATE)
537 LPCREATESTRUCT lpcs = (LPCREATESTRUCT)lParam;
538 lpcs->style |= LVS_REPORT;
539 SendMessage(hwnd, LVM_SETIMAGELIST, 0, (LPARAM)test_create_imagelist);
541 return CallWindowProc(listviewWndProc, hwnd, uMsg, wParam, lParam);
544 static void test_create()
549 cls.cbSize = sizeof(WNDCLASSEX);
550 ok(GetClassInfoEx(GetModuleHandle(NULL), "SysListView32", &cls), "GetClassInfoEx failed\n");
551 listviewWndProc = cls.lpfnWndProc;
552 cls.lpfnWndProc = create_test_wndproc;
553 cls.lpszClassName = "MyListView32";
554 ok(RegisterClassEx(&cls), "RegisterClassEx failed\n");
556 test_create_imagelist = ImageList_Create(16, 16, 0, 5, 10);
557 hList = CreateWindow("MyListView32", "Test", WS_VISIBLE, 0, 0, 100, 100, NULL, NULL, GetModuleHandle(NULL), 0);
558 ok((HIMAGELIST)SendMessage(hList, LVM_GETIMAGELIST, 0, 0) == test_create_imagelist, "Image list not obtained\n");
559 hHeader = (HWND)SendMessage(hList, LVM_GETHEADER, 0, 0);
560 ok(IsWindow(hHeader) && IsWindowVisible(hHeader), "Listview not in report mode\n");
561 DestroyWindow(hList);
564 static void test_redraw(void)
566 HWND hwnd, hwndheader;
568 hwnd = create_listview_control();
569 hwndheader = subclass_header(hwnd);
571 flush_sequences(sequences, NUM_MSG_SEQUENCES);
573 trace("invalidate & update\n");
574 InvalidateRect(hwnd, NULL, TRUE);
576 ok_sequence(sequences, LISTVIEW_SEQ_INDEX, redraw_listview_seq, "redraw listview", TRUE);
578 flush_sequences(sequences, NUM_MSG_SEQUENCES);
585 INITCOMMONCONTROLSEX icc;
588 icc.dwSize = sizeof icc;
589 InitCommonControlsEx(&icc);
591 init_msg_sequences(sequences, NUM_MSG_SEQUENCES);
593 hwndparent = create_parent_window();