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
35 #define expect(expected, got) ok(got == expected, "Expected %d, got %d\n", expected, got)
39 static struct msg_sequence *sequences[NUM_MSG_SEQUENCES];
41 static const struct message redraw_listview_seq[] = {
42 { WM_PAINT, sent|id, 0, 0, LISTVIEW_ID },
43 { WM_PAINT, sent|id, 0, 0, HEADER_ID },
44 { WM_NCPAINT, sent|id|defwinproc, 0, 0, HEADER_ID },
45 { WM_ERASEBKGND, sent|id|defwinproc, 0, 0, HEADER_ID },
46 { WM_NOTIFY, sent|id|defwinproc, 0, 0, LISTVIEW_ID },
47 { WM_NCPAINT, sent|id|defwinproc, 0, 0, LISTVIEW_ID },
48 { WM_ERASEBKGND, sent|id|defwinproc, 0, 0, LISTVIEW_ID },
57 static LRESULT WINAPI parent_wnd_proc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
59 static long defwndproc_counter = 0;
63 /* do not log painting messages */
64 if (message != WM_PAINT &&
65 message != WM_ERASEBKGND &&
66 message != WM_NCPAINT &&
67 message != WM_NCHITTEST &&
68 message != WM_GETTEXT &&
69 message != WM_GETICON &&
70 message != WM_DEVICECHANGE)
72 trace("parent: %p, %04x, %08x, %08lx\n", hwnd, message, wParam, lParam);
74 msg.message = message;
75 msg.flags = sent|wparam|lparam;
76 if (defwndproc_counter) msg.flags |= defwinproc;
79 add_message(sequences, PARENT_SEQ_INDEX, &msg);
83 ret = DefWindowProcA(hwnd, message, wParam, lParam);
89 static BOOL register_parent_wnd_class(void)
94 cls.lpfnWndProc = parent_wnd_proc;
97 cls.hInstance = GetModuleHandleA(NULL);
99 cls.hCursor = LoadCursorA(0, (LPSTR)IDC_ARROW);
100 cls.hbrBackground = GetStockObject(WHITE_BRUSH);
101 cls.lpszMenuName = NULL;
102 cls.lpszClassName = "Listview test parent class";
103 return RegisterClassA(&cls);
106 static HWND create_parent_window(void)
108 if (!register_parent_wnd_class())
111 return CreateWindowEx(0, "Listview test parent class",
112 "Listview test parent window",
113 WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX |
114 WS_MAXIMIZEBOX | WS_VISIBLE,
116 GetDesktopWindow(), NULL, GetModuleHandleA(NULL), NULL);
119 static LRESULT WINAPI listview_subclass_proc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
121 struct subclass_info *info = (struct subclass_info *)GetWindowLongA(hwnd, GWL_USERDATA);
122 static long defwndproc_counter = 0;
126 trace("listview: %p, %04x, %08x, %08lx\n", hwnd, message, wParam, lParam);
128 msg.message = message;
129 msg.flags = sent|wparam|lparam;
130 if (defwndproc_counter) msg.flags |= defwinproc;
133 msg.id = LISTVIEW_ID;
134 add_message(sequences, LISTVIEW_SEQ_INDEX, &msg);
136 defwndproc_counter++;
137 ret = CallWindowProcA(info->oldproc, hwnd, message, wParam, lParam);
138 defwndproc_counter--;
142 static HWND create_listview_control(void)
144 struct subclass_info *info;
148 info = HeapAlloc(GetProcessHeap(), 0, sizeof(struct subclass_info));
152 GetClientRect(hwndparent, &rect);
153 hwnd = CreateWindowExA(0, WC_LISTVIEW, "foo",
154 WS_CHILD | WS_BORDER | WS_VISIBLE | LVS_REPORT,
155 0, 0, rect.right, rect.bottom,
156 hwndparent, NULL, GetModuleHandleA(NULL), NULL);
157 ok(hwnd != NULL, "gle=%d\n", GetLastError());
161 HeapFree(GetProcessHeap(), 0, info);
165 info->oldproc = (WNDPROC)SetWindowLongA(hwnd, GWL_WNDPROC,
166 (LONG)listview_subclass_proc);
167 SetWindowLongA(hwnd, GWL_USERDATA, (LONG)info);
172 static LRESULT WINAPI header_subclass_proc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
174 struct subclass_info *info = (struct subclass_info *)GetWindowLongA(hwnd, GWL_USERDATA);
175 static long defwndproc_counter = 0;
179 trace("header: %p, %04x, %08x, %08lx\n", hwnd, message, wParam, lParam);
181 msg.message = message;
182 msg.flags = sent|wparam|lparam;
183 if (defwndproc_counter) msg.flags |= defwinproc;
187 add_message(sequences, LISTVIEW_SEQ_INDEX, &msg);
189 defwndproc_counter++;
190 ret = CallWindowProcA(info->oldproc, hwnd, message, wParam, lParam);
191 defwndproc_counter--;
195 static HWND subclass_header(HWND hwndListview)
197 struct subclass_info *info;
200 info = HeapAlloc(GetProcessHeap(), 0, sizeof(struct subclass_info));
204 hwnd = ListView_GetHeader(hwndListview);
205 info->oldproc = (WNDPROC)SetWindowLongA(hwnd, GWL_WNDPROC,
206 (LONG)header_subclass_proc);
207 SetWindowLongA(hwnd, GWL_USERDATA, (LONG)info);
212 static void test_images(void)
220 static CHAR hello[] = "hello";
222 himl = ImageList_Create(40, 40, 0, 4, 4);
223 ok(himl != NULL, "failed to create imagelist\n");
225 hbmp = CreateBitmap(40, 40, 1, 1, NULL);
226 ok(hbmp != NULL, "failed to create bitmap\n");
228 r = ImageList_Add(himl, hbmp, 0);
229 ok(r == 0, "should be zero\n");
231 hwnd = CreateWindowEx(0, "SysListView32", "foo", LVS_OWNERDRAWFIXED,
232 10, 10, 100, 200, hwndparent, NULL, NULL, NULL);
233 ok(hwnd != NULL, "failed to create listview window\n");
235 r = SendMessage(hwnd, LVM_SETEXTENDEDLISTVIEWSTYLE, 0, 0x940);
236 ok(r == 0, "should return zero\n");
238 r = SendMessage(hwnd, LVM_SETIMAGELIST, 0, (LPARAM)himl);
239 ok(r == 0, "should return zero\n");
241 r = SendMessage(hwnd, LVM_SETICONSPACING, 0, MAKELONG(100,50));
242 /* returns dimensions */
244 r = SendMessage(hwnd, LVM_GETITEMCOUNT, 0, 0);
245 ok(r == 0, "should be zero items\n");
247 item.mask = LVIF_IMAGE | LVIF_TEXT;
252 r = SendMessage(hwnd, LVM_INSERTITEM, 0, (LPARAM) &item);
253 ok(r == -1, "should fail\n");
256 item.pszText = hello;
257 r = SendMessage(hwnd, LVM_INSERTITEM, 0, (LPARAM) &item);
258 ok(r == 0, "should not fail\n");
260 memset(&r1, 0, sizeof r1);
262 r = SendMessage(hwnd, LVM_GETITEMRECT, 0, (LPARAM) &r1);
264 r = SendMessage(hwnd, LVM_DELETEALLITEMS, 0, 0);
265 ok(r == TRUE, "should not fail\n");
268 item.pszText = hello;
269 r = SendMessage(hwnd, LVM_INSERTITEM, 0, (LPARAM) &item);
270 ok(r == 0, "should not fail\n");
272 memset(&r2, 0, sizeof r2);
274 r = SendMessage(hwnd, LVM_GETITEMRECT, 0, (LPARAM) &r2);
276 ok(!memcmp(&r1, &r2, sizeof r1), "rectangle should be the same\n");
281 static void test_checkboxes(void)
286 static CHAR text[] = "Text",
290 hwnd = CreateWindowEx(0, "SysListView32", "foo", LVS_REPORT,
291 10, 10, 100, 200, hwndparent, NULL, NULL, NULL);
292 ok(hwnd != NULL, "failed to create listview window\n");
294 /* first without LVS_EX_CHECKBOXES set and an item and check that state is preserved */
295 item.mask = LVIF_TEXT | LVIF_STATE;
296 item.stateMask = 0xffff;
301 r = SendMessage(hwnd, LVM_INSERTITEMA, 0, (LPARAM) &item);
302 ok(r == 0, "ret %d\n", r);
305 item.mask = LVIF_STATE;
306 item.stateMask = 0xffff;
307 r = SendMessage(hwnd, LVM_GETITEMA, 0, (LPARAM) &item);
308 ok(item.state == 0xfccc, "state %x\n", item.state);
310 /* Don't set LVIF_STATE */
311 item.mask = LVIF_TEXT;
312 item.stateMask = 0xffff;
317 r = SendMessage(hwnd, LVM_INSERTITEMA, 0, (LPARAM) &item);
318 ok(r == 1, "ret %d\n", r);
321 item.mask = LVIF_STATE;
322 item.stateMask = 0xffff;
323 r = SendMessage(hwnd, LVM_GETITEMA, 0, (LPARAM) &item);
324 ok(item.state == 0, "state %x\n", item.state);
326 r = SendMessage(hwnd, LVM_SETEXTENDEDLISTVIEWSTYLE, LVS_EX_CHECKBOXES, LVS_EX_CHECKBOXES);
327 ok(r == 0, "should return zero\n");
329 /* Having turned on checkboxes, check that all existing items are set to 0x1000 (unchecked) */
331 item.mask = LVIF_STATE;
332 item.stateMask = 0xffff;
333 r = SendMessage(hwnd, LVM_GETITEMA, 0, (LPARAM) &item);
334 ok(item.state == 0x1ccc, "state %x\n", item.state);
336 /* Now add an item without specifying a state and check that its state goes to 0x1000 */
338 item.mask = LVIF_TEXT;
340 item.pszText = text2;
341 r = SendMessage(hwnd, LVM_INSERTITEMA, 0, (LPARAM) &item);
342 ok(r == 2, "ret %d\n", r);
345 item.mask = LVIF_STATE;
346 item.stateMask = 0xffff;
347 r = SendMessage(hwnd, LVM_GETITEMA, 0, (LPARAM) &item);
348 ok(item.state == 0x1000, "state %x\n", item.state);
350 /* Add a further item this time specifying a state and still its state goes to 0x1000 */
352 item.mask = LVIF_TEXT | LVIF_STATE;
353 item.stateMask = 0xffff;
355 item.pszText = text3;
356 r = SendMessage(hwnd, LVM_INSERTITEMA, 0, (LPARAM) &item);
357 ok(r == 3, "ret %d\n", r);
360 item.mask = LVIF_STATE;
361 item.stateMask = 0xffff;
362 r = SendMessage(hwnd, LVM_GETITEMA, 0, (LPARAM) &item);
363 ok(item.state == 0x1aaa, "state %x\n", item.state);
365 /* Set an item's state to checked */
367 item.mask = LVIF_STATE;
368 item.stateMask = 0xf000;
370 r = SendMessage(hwnd, LVM_SETITEMA, 0, (LPARAM) &item);
373 item.mask = LVIF_STATE;
374 item.stateMask = 0xffff;
375 r = SendMessage(hwnd, LVM_GETITEMA, 0, (LPARAM) &item);
376 ok(item.state == 0x2aaa, "state %x\n", item.state);
378 /* Check that only the bits we asked for are returned,
379 * and that all the others are set to zero
382 item.mask = LVIF_STATE;
383 item.stateMask = 0xf000;
385 r = SendMessage(hwnd, LVM_GETITEMA, 0, (LPARAM) &item);
386 ok(item.state == 0x2000, "state %x\n", item.state);
388 /* Set the style again and check that doesn't change an item's state */
389 r = SendMessage(hwnd, LVM_SETEXTENDEDLISTVIEWSTYLE, LVS_EX_CHECKBOXES, LVS_EX_CHECKBOXES);
390 ok(r == LVS_EX_CHECKBOXES, "ret %x\n", r);
393 item.mask = LVIF_STATE;
394 item.stateMask = 0xffff;
395 r = SendMessage(hwnd, LVM_GETITEMA, 0, (LPARAM) &item);
396 ok(item.state == 0x2aaa, "state %x\n", item.state);
398 /* Unsetting the checkbox extended style doesn't change an item's state */
399 r = SendMessage(hwnd, LVM_SETEXTENDEDLISTVIEWSTYLE, LVS_EX_CHECKBOXES, 0);
400 ok(r == LVS_EX_CHECKBOXES, "ret %x\n", r);
403 item.mask = LVIF_STATE;
404 item.stateMask = 0xffff;
405 r = SendMessage(hwnd, LVM_GETITEMA, 0, (LPARAM) &item);
406 ok(item.state == 0x2aaa, "state %x\n", item.state);
408 /* Now setting the style again will change an item's state */
409 r = SendMessage(hwnd, LVM_SETEXTENDEDLISTVIEWSTYLE, LVS_EX_CHECKBOXES, LVS_EX_CHECKBOXES);
410 ok(r == 0, "ret %x\n", r);
413 item.mask = LVIF_STATE;
414 item.stateMask = 0xffff;
415 r = SendMessage(hwnd, LVM_GETITEMA, 0, (LPARAM) &item);
416 ok(item.state == 0x1aaa, "state %x\n", item.state);
421 static void insert_column(HWND hwnd, int idx)
426 memset(&column, 0xaa, sizeof(column));
427 column.mask = LVCF_SUBITEM;
428 column.iSubItem = idx;
430 rc = ListView_InsertColumn(hwnd, idx, &column);
434 static void insert_item(HWND hwnd, int idx)
436 static CHAR text[] = "foo";
441 memset(&item, 0xaa, sizeof (item));
442 item.mask = LVIF_TEXT;
447 rc = ListView_InsertItemA(hwnd, &item);
451 static void test_items(void)
453 const LPARAM lparamTest = 0x42;
457 static CHAR text[] = "Text";
459 hwnd = CreateWindowEx(0, "SysListView32", "foo", LVS_REPORT,
460 10, 10, 100, 200, hwndparent, NULL, NULL, NULL);
461 ok(hwnd != NULL, "failed to create listview window\n");
464 * Test setting/getting item params
467 /* Set up two columns */
468 insert_column(hwnd, 0);
469 insert_column(hwnd, 1);
471 /* Insert an item with just a param */
472 memset (&item, 0xaa, sizeof (item));
473 item.mask = LVIF_PARAM;
476 item.lParam = lparamTest;
477 r = SendMessage(hwnd, LVM_INSERTITEMA, 0, (LPARAM) &item);
478 ok(r == 0, "ret %d\n", r);
480 /* Test getting of the param */
481 memset (&item, 0xaa, sizeof (item));
482 item.mask = LVIF_PARAM;
485 r = SendMessage(hwnd, LVM_GETITEMA, 0, (LPARAM) &item);
486 ok(r != 0, "ret %d\n", r);
487 ok(item.lParam == lparamTest, "got lParam %lx, expected %lx\n", item.lParam, lparamTest);
489 /* Set up a subitem */
490 memset (&item, 0xaa, sizeof (item));
491 item.mask = LVIF_TEXT;
495 r = SendMessage(hwnd, LVM_SETITEMA, 0, (LPARAM) &item);
496 ok(r != 0, "ret %d\n", r);
498 /* Query param from subitem: returns main item param */
499 memset (&item, 0xaa, sizeof (item));
500 item.mask = LVIF_PARAM;
503 r = SendMessage(hwnd, LVM_GETITEMA, 0, (LPARAM) &item);
504 ok(r != 0, "ret %d\n", r);
505 ok(item.lParam == lparamTest, "got lParam %lx, expected %lx\n", item.lParam, lparamTest);
507 /* Set up param on first subitem: no effect */
508 memset (&item, 0xaa, sizeof (item));
509 item.mask = LVIF_PARAM;
512 item.lParam = lparamTest+1;
513 r = SendMessage(hwnd, LVM_SETITEMA, 0, (LPARAM) &item);
514 ok(r == 0, "ret %d\n", r);
516 /* Query param from subitem again: should still return main item param */
517 memset (&item, 0xaa, sizeof (item));
518 item.mask = LVIF_PARAM;
521 r = SendMessage(hwnd, LVM_GETITEMA, 0, (LPARAM) &item);
522 ok(r != 0, "ret %d\n", r);
523 ok(item.lParam == lparamTest, "got lParam %lx, expected %lx\n", item.lParam, lparamTest);
525 /**** Some tests of state highlighting ****/
526 memset (&item, 0xaa, sizeof (item));
527 item.mask = LVIF_STATE;
530 item.state = LVIS_SELECTED;
531 item.stateMask = LVIS_SELECTED | LVIS_DROPHILITED;
532 r = SendMessage(hwnd, LVM_SETITEM, 0, (LPARAM) &item);
533 ok(r != 0, "ret %d\n", r);
535 item.state = LVIS_DROPHILITED;
536 r = SendMessage(hwnd, LVM_SETITEM, 0, (LPARAM) &item);
537 ok(r != 0, "ret %d\n", r);
539 memset (&item, 0xaa, sizeof (item));
540 item.mask = LVIF_STATE;
544 r = SendMessage(hwnd, LVM_GETITEM, 0, (LPARAM) &item);
545 ok(r != 0, "ret %d\n", r);
546 ok(item.state == LVIS_SELECTED, "got state %x, expected %x\n", item.state, LVIS_SELECTED);
548 r = SendMessage(hwnd, LVM_GETITEM, 0, (LPARAM) &item);
549 ok(r != 0, "ret %d\n", r);
550 todo_wine ok(item.state == LVIS_DROPHILITED, "got state %x, expected %x\n", item.state, LVIS_DROPHILITED);
555 /* test setting imagelist between WM_NCCREATE and WM_CREATE */
556 static WNDPROC listviewWndProc;
557 static HIMAGELIST test_create_imagelist;
559 static LRESULT CALLBACK create_test_wndproc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
561 if (uMsg == WM_CREATE)
563 LPCREATESTRUCT lpcs = (LPCREATESTRUCT)lParam;
564 lpcs->style |= LVS_REPORT;
565 SendMessage(hwnd, LVM_SETIMAGELIST, 0, (LPARAM)test_create_imagelist);
567 return CallWindowProc(listviewWndProc, hwnd, uMsg, wParam, lParam);
570 static void test_create(void)
575 cls.cbSize = sizeof(WNDCLASSEX);
576 ok(GetClassInfoEx(GetModuleHandle(NULL), "SysListView32", &cls), "GetClassInfoEx failed\n");
577 listviewWndProc = cls.lpfnWndProc;
578 cls.lpfnWndProc = create_test_wndproc;
579 cls.lpszClassName = "MyListView32";
580 ok(RegisterClassEx(&cls), "RegisterClassEx failed\n");
582 test_create_imagelist = ImageList_Create(16, 16, 0, 5, 10);
583 hList = CreateWindow("MyListView32", "Test", WS_VISIBLE, 0, 0, 100, 100, NULL, NULL, GetModuleHandle(NULL), 0);
584 ok((HIMAGELIST)SendMessage(hList, LVM_GETIMAGELIST, 0, 0) == test_create_imagelist, "Image list not obtained\n");
585 hHeader = (HWND)SendMessage(hList, LVM_GETHEADER, 0, 0);
586 ok(IsWindow(hHeader) && IsWindowVisible(hHeader), "Listview not in report mode\n");
587 DestroyWindow(hList);
590 static void test_redraw(void)
592 HWND hwnd, hwndheader;
594 hwnd = create_listview_control();
595 hwndheader = subclass_header(hwnd);
597 flush_sequences(sequences, NUM_MSG_SEQUENCES);
599 trace("invalidate & update\n");
600 InvalidateRect(hwnd, NULL, TRUE);
602 ok_sequence(sequences, LISTVIEW_SEQ_INDEX, redraw_listview_seq, "redraw listview", FALSE);
604 flush_sequences(sequences, NUM_MSG_SEQUENCES);
609 static LRESULT WINAPI cd_wndproc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp)
611 COLORREF clr, c0ffee = RGB(0xc0, 0xff, 0xee);
613 if(msg == WM_NOTIFY) {
614 NMHDR *nmhdr = (PVOID)lp;
615 if(nmhdr->code == NM_CUSTOMDRAW) {
616 NMLVCUSTOMDRAW *nmlvcd = (PVOID)nmhdr;
617 trace("NMCUSTOMDRAW (0x%.8x)\n", nmlvcd->nmcd.dwDrawStage);
618 switch(nmlvcd->nmcd.dwDrawStage) {
620 SetBkColor(nmlvcd->nmcd.hdc, c0ffee);
621 return CDRF_NOTIFYITEMDRAW;
622 case CDDS_ITEMPREPAINT:
623 nmlvcd->clrTextBk = CLR_DEFAULT;
624 return CDRF_NOTIFYSUBITEMDRAW;
625 case CDDS_ITEMPREPAINT | CDDS_SUBITEM:
626 clr = GetBkColor(nmlvcd->nmcd.hdc);
627 todo_wine ok(clr == c0ffee, "clr=%.8x\n", clr);
628 return CDRF_NOTIFYPOSTPAINT;
629 case CDDS_ITEMPOSTPAINT | CDDS_SUBITEM:
630 clr = GetBkColor(nmlvcd->nmcd.hdc);
631 todo_wine ok(clr == c0ffee, "clr=%.8x\n", clr);
632 return CDRF_DODEFAULT;
634 return CDRF_DODEFAULT;
638 return DefWindowProcA(hwnd, msg, wp, lp);
641 static void test_customdraw(void)
646 hwnd = create_listview_control();
648 insert_column(hwnd, 0);
649 insert_column(hwnd, 1);
650 insert_item(hwnd, 0);
652 oldwndproc = (WNDPROC)SetWindowLongPtr(hwndparent, GWL_WNDPROC,
653 (INT_PTR)cd_wndproc);
655 InvalidateRect(hwnd, NULL, TRUE);
658 SetWindowLongPtr(hwndparent, GWL_WNDPROC, (INT_PTR)oldwndproc);
665 INITCOMMONCONTROLSEX icc;
668 icc.dwSize = sizeof icc;
669 InitCommonControlsEx(&icc);
671 init_msg_sequences(sequences, NUM_MSG_SEQUENCES);
673 hwndparent = create_parent_window();