[COMCTL32_WINETEST] Sync with Wine Staging 2.16. CORE-13762
[reactos.git] / rostests / winetests / comctl32 / pager.c
1 /*
2 * Unit tests for the pager control
3 *
4 * Copyright 2012 Alexandre Julliard
5 *
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.
10 *
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.
15 *
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
19 */
20
21 #include <wine/test.h>
22
23 //#include <windows.h>
24 #include <wingdi.h>
25 #include <winuser.h>
26 #include <commctrl.h>
27
28 #include "msg.h"
29
30 #define NUM_MSG_SEQUENCES 1
31 #define PAGER_SEQ_INDEX 0
32
33 static HWND parent_wnd, child1_wnd, child2_wnd;
34
35 #define CHILD1_ID 1
36 #define CHILD2_ID 2
37
38 static BOOL (WINAPI *pSetWindowSubclass)(HWND, SUBCLASSPROC, UINT_PTR, DWORD_PTR);
39
40 static struct msg_sequence *sequences[NUM_MSG_SEQUENCES];
41
42 static const struct message set_child_seq[] = {
43 { PGM_SETCHILD, sent },
44 { WM_WINDOWPOSCHANGING, sent },
45 { WM_NCCALCSIZE, sent|wparam, TRUE },
46 { WM_NOTIFY, sent|id|parent, 0, 0, PGN_CALCSIZE },
47 { WM_WINDOWPOSCHANGED, sent },
48 { WM_WINDOWPOSCHANGING, sent|id, 0, 0, CHILD1_ID },
49 { WM_NCCALCSIZE, sent|wparam|id|optional, TRUE, 0, CHILD1_ID },
50 { WM_CHILDACTIVATE, sent|id, 0, 0, CHILD1_ID },
51 { WM_WINDOWPOSCHANGED, sent|id, 0, 0, CHILD1_ID },
52 { WM_SIZE, sent|id|defwinproc|optional, 0, 0, CHILD1_ID },
53 { 0 }
54 };
55
56 /* This differs from the above message list only in the child window that is
57 * expected to receive the child messages. No message is sent to the old child.
58 * Also child 2 is hidden while child 1 is visible. The pager does not make the
59 * hidden child visible. */
60 static const struct message switch_child_seq[] = {
61 { PGM_SETCHILD, sent },
62 { WM_WINDOWPOSCHANGING, sent },
63 { WM_NCCALCSIZE, sent|wparam, TRUE },
64 { WM_NOTIFY, sent|id|parent, 0, 0, PGN_CALCSIZE },
65 { WM_WINDOWPOSCHANGED, sent },
66 { WM_WINDOWPOSCHANGING, sent|id, 0, 0, CHILD2_ID },
67 { WM_NCCALCSIZE, sent|wparam|id, TRUE, 0, CHILD2_ID },
68 { WM_CHILDACTIVATE, sent|id, 0, 0, CHILD2_ID },
69 { WM_WINDOWPOSCHANGED, sent|id, 0, 0, CHILD2_ID },
70 { WM_SIZE, sent|id|defwinproc, 0, 0, CHILD2_ID },
71 { 0 }
72 };
73
74 static const struct message set_pos_seq[] = {
75 { PGM_SETPOS, sent },
76 { WM_WINDOWPOSCHANGING, sent },
77 { WM_NCCALCSIZE, sent|wparam, TRUE },
78 { WM_NOTIFY, sent|id|parent, 0, 0, PGN_CALCSIZE },
79 { WM_WINDOWPOSCHANGED, sent },
80 { WM_MOVE, sent|optional },
81 /* The WM_SIZE handler sends WM_WINDOWPOSCHANGING, WM_CHILDACTIVATE
82 * and WM_WINDOWPOSCHANGED (which sends WM_MOVE) to the child.
83 * Another WM_WINDOWPOSCHANGING is sent afterwards.
84 *
85 * The 2nd WM_WINDOWPOSCHANGING is unconditional, but the comparison
86 * function is too simple to roll back an accepted message, so we have
87 * to mark the 2nd message optional. */
88 { WM_SIZE, sent|optional },
89 { WM_WINDOWPOSCHANGING, sent|id, 0, 0, CHILD1_ID }, /* Actually optional. */
90 { WM_CHILDACTIVATE, sent|id, 0, 0, CHILD1_ID }, /* Actually optional. */
91 { WM_WINDOWPOSCHANGED, sent|id|optional, TRUE, 0, CHILD1_ID},
92 { WM_MOVE, sent|id|optional|defwinproc, 0, 0, CHILD1_ID },
93 { WM_WINDOWPOSCHANGING, sent|id|optional, 0, 0, CHILD1_ID }, /* Actually not optional. */
94 { WM_CHILDACTIVATE, sent|id|optional, 0, 0, CHILD1_ID }, /* Actually not optional. */
95 { 0 }
96 };
97
98 static const struct message set_pos_empty_seq[] = {
99 { PGM_SETPOS, sent },
100 { 0 }
101 };
102
103 static LRESULT WINAPI parent_wnd_proc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
104 {
105 static LONG defwndproc_counter = 0;
106 LRESULT ret;
107 struct message msg;
108
109 /* log system messages, except for painting */
110 if (message < WM_USER &&
111 message != WM_PAINT &&
112 message != WM_ERASEBKGND &&
113 message != WM_NCPAINT &&
114 message != WM_NCHITTEST &&
115 message != WM_GETTEXT &&
116 message != WM_GETICON &&
117 message != WM_DEVICECHANGE)
118 {
119 msg.message = message;
120 msg.flags = sent|wparam|lparam|parent;
121 if (defwndproc_counter) msg.flags |= defwinproc;
122 msg.wParam = wParam;
123 msg.lParam = lParam;
124 if (message == WM_NOTIFY && lParam) msg.id = ((NMHDR*)lParam)->code;
125 add_message(sequences, PAGER_SEQ_INDEX, &msg);
126 }
127
128 if (message == WM_NOTIFY)
129 {
130 NMHDR *nmhdr = (NMHDR *)lParam;
131
132 switch (nmhdr->code)
133 {
134 case PGN_CALCSIZE:
135 {
136 NMPGCALCSIZE *nmpgcs = (NMPGCALCSIZE *)lParam;
137 DWORD style = GetWindowLongA(nmpgcs->hdr.hwndFrom, GWL_STYLE);
138
139 if (style & PGS_HORZ)
140 ok(nmpgcs->dwFlag == PGF_CALCWIDTH, "Unexpected flags %#x.\n", nmpgcs->dwFlag);
141 else
142 ok(nmpgcs->dwFlag == PGF_CALCHEIGHT, "Unexpected flags %#x.\n", nmpgcs->dwFlag);
143 break;
144 }
145 default:
146 ;
147 }
148 }
149
150 defwndproc_counter++;
151 ret = DefWindowProcA(hwnd, message, wParam, lParam);
152 defwndproc_counter--;
153
154 return ret;
155 }
156
157 static BOOL register_parent_wnd_class(void)
158 {
159 WNDCLASSA cls;
160
161 cls.style = 0;
162 cls.lpfnWndProc = parent_wnd_proc;
163 cls.cbClsExtra = 0;
164 cls.cbWndExtra = 0;
165 cls.hInstance = GetModuleHandleA(NULL);
166 cls.hIcon = 0;
167 cls.hCursor = LoadCursorA(0, (LPCSTR)IDC_ARROW);
168 cls.hbrBackground = GetStockObject(WHITE_BRUSH);
169 cls.lpszMenuName = NULL;
170 cls.lpszClassName = "Pager test parent class";
171 return RegisterClassA(&cls);
172 }
173
174 static HWND create_parent_window(void)
175 {
176 if (!register_parent_wnd_class())
177 return NULL;
178
179 return CreateWindowA("Pager test parent class", "Pager test parent window",
180 WS_OVERLAPPED | WS_VISIBLE,
181 0, 0, 200, 200, 0, NULL, GetModuleHandleA(NULL), NULL );
182 }
183
184 static LRESULT WINAPI pager_subclass_proc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
185 {
186 WNDPROC oldproc = (WNDPROC)GetWindowLongPtrA(hwnd, GWLP_USERDATA);
187 struct message msg = { 0 };
188
189 msg.message = message;
190 msg.flags = sent|wparam|lparam;
191 msg.wParam = wParam;
192 msg.lParam = lParam;
193 add_message(sequences, PAGER_SEQ_INDEX, &msg);
194 return CallWindowProcA(oldproc, hwnd, message, wParam, lParam);
195 }
196
197 static HWND create_pager_control( DWORD style )
198 {
199 WNDPROC oldproc;
200 HWND hwnd;
201 RECT rect;
202
203 GetClientRect( parent_wnd, &rect );
204 hwnd = CreateWindowA( WC_PAGESCROLLERA, "pager", WS_CHILD | WS_BORDER | WS_VISIBLE | style,
205 0, 0, 100, 100, parent_wnd, 0, GetModuleHandleA(0), 0 );
206 oldproc = (WNDPROC)SetWindowLongPtrA(hwnd, GWLP_WNDPROC, (LONG_PTR)pager_subclass_proc);
207 SetWindowLongPtrA(hwnd, GWLP_USERDATA, (LONG_PTR)oldproc);
208 return hwnd;
209 }
210
211 static LRESULT WINAPI child_proc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
212 {
213 static LONG defwndproc_counter;
214 struct message msg = { 0 };
215 LRESULT ret;
216
217 msg.message = message;
218 msg.flags = sent | wparam | lparam;
219 if (defwndproc_counter)
220 msg.flags |= defwinproc;
221 msg.wParam = wParam;
222 msg.lParam = lParam;
223
224 if (hwnd == child1_wnd)
225 msg.id = CHILD1_ID;
226 else if (hwnd == child2_wnd)
227 msg.id = CHILD2_ID;
228 else
229 msg.id = 0;
230
231 add_message(sequences, PAGER_SEQ_INDEX, &msg);
232
233 defwndproc_counter++;
234 ret = DefWindowProcA(hwnd, message, wParam, lParam);
235 defwndproc_counter--;
236
237 return ret;
238 }
239
240 static BOOL register_child_wnd_class(void)
241 {
242 WNDCLASSA cls;
243
244 cls.style = 0;
245 cls.lpfnWndProc = child_proc;
246 cls.cbClsExtra = 0;
247 cls.cbWndExtra = 0;
248 cls.hInstance = GetModuleHandleA(NULL);
249 cls.hIcon = 0;
250 cls.hCursor = LoadCursorA(0, (LPCSTR)IDC_ARROW);
251 cls.hbrBackground = GetStockObject(WHITE_BRUSH);
252 cls.lpszMenuName = NULL;
253 cls.lpszClassName = "Pager test child class";
254 return RegisterClassA(&cls);
255 }
256
257 static void test_pager(void)
258 {
259 HWND pager;
260 RECT rect, rect2;
261
262 pager = create_pager_control( PGS_HORZ );
263 if (!pager)
264 {
265 win_skip( "Pager control not supported\n" );
266 return;
267 }
268
269 register_child_wnd_class();
270
271 child1_wnd = CreateWindowA( "Pager test child class", "button", WS_CHILD | WS_BORDER | WS_VISIBLE, 0, 0, 300, 300,
272 pager, 0, GetModuleHandleA(0), 0 );
273 child2_wnd = CreateWindowA("Pager test child class", "button", WS_CHILD | WS_BORDER, 0, 0, 300, 300,
274 pager, 0, GetModuleHandleA(0), 0);
275
276 flush_sequences( sequences, NUM_MSG_SEQUENCES );
277 SendMessageA( pager, PGM_SETCHILD, 0, (LPARAM)child1_wnd );
278 ok_sequence(sequences, PAGER_SEQ_INDEX, set_child_seq, "set child", FALSE);
279 GetWindowRect( pager, &rect );
280 ok( rect.right - rect.left == 100 && rect.bottom - rect.top == 100,
281 "pager resized %dx%d\n", rect.right - rect.left, rect.bottom - rect.top );
282
283 flush_sequences(sequences, NUM_MSG_SEQUENCES);
284 SendMessageA(pager, PGM_SETCHILD, 0, (LPARAM)child2_wnd);
285 ok_sequence(sequences, PAGER_SEQ_INDEX, switch_child_seq, "switch to invisible child", FALSE);
286 GetWindowRect(pager, &rect);
287 ok(rect.right - rect.left == 100 && rect.bottom - rect.top == 100,
288 "pager resized %dx%d\n", rect.right - rect.left, rect.bottom - rect.top);
289 ok(!IsWindowVisible(child2_wnd), "Child window 2 is visible\n");
290
291 flush_sequences(sequences, NUM_MSG_SEQUENCES);
292 SendMessageA(pager, PGM_SETCHILD, 0, (LPARAM)child1_wnd);
293 ok_sequence(sequences, PAGER_SEQ_INDEX, set_child_seq, "switch to visible child", FALSE);
294 GetWindowRect(pager, &rect);
295 ok(rect.right - rect.left == 100 && rect.bottom - rect.top == 100,
296 "pager resized %dx%d\n", rect.right - rect.left, rect.bottom - rect.top);
297
298 flush_sequences( sequences, NUM_MSG_SEQUENCES );
299 SendMessageA( pager, PGM_SETPOS, 0, 10 );
300 ok_sequence(sequences, PAGER_SEQ_INDEX, set_pos_seq, "set pos", TRUE);
301 GetWindowRect( pager, &rect );
302 ok( rect.right - rect.left == 100 && rect.bottom - rect.top == 100,
303 "pager resized %dx%d\n", rect.right - rect.left, rect.bottom - rect.top );
304
305 flush_sequences( sequences, NUM_MSG_SEQUENCES );
306 SendMessageA( pager, PGM_SETPOS, 0, 10 );
307 ok_sequence(sequences, PAGER_SEQ_INDEX, set_pos_empty_seq, "set pos empty", TRUE);
308
309 flush_sequences( sequences, NUM_MSG_SEQUENCES );
310 SendMessageA( pager, PGM_SETPOS, 0, 9 );
311 ok_sequence(sequences, PAGER_SEQ_INDEX, set_pos_seq, "set pos", TRUE);
312
313 DestroyWindow( pager );
314
315 /* Test if resizing works */
316 pager = create_pager_control( CCS_NORESIZE );
317 ok(pager != NULL, "failed to create pager control\n");
318
319 GetWindowRect( pager, &rect );
320 MoveWindow( pager, 0, 0, 200, 100, TRUE );
321 GetWindowRect( pager, &rect2 );
322 ok(rect2.right - rect2.left > rect.right - rect.left, "expected pager window to resize, %s\n",
323 wine_dbgstr_rect( &rect2 ));
324
325 DestroyWindow( pager );
326
327 pager = create_pager_control( CCS_NORESIZE | PGS_HORZ );
328 ok(pager != NULL, "failed to create pager control\n");
329
330 GetWindowRect( pager, &rect );
331 MoveWindow( pager, 0, 0, 100, 200, TRUE );
332 GetWindowRect( pager, &rect2 );
333 ok(rect2.bottom - rect2.top > rect.bottom - rect.top, "expected pager window to resize, %s\n",
334 wine_dbgstr_rect( &rect2 ));
335
336 DestroyWindow( pager );
337 }
338
339 START_TEST(pager)
340 {
341 HMODULE mod = GetModuleHandleA("comctl32.dll");
342
343 pSetWindowSubclass = (void*)GetProcAddress(mod, (LPSTR)410);
344
345 InitCommonControls();
346 init_msg_sequences(sequences, NUM_MSG_SEQUENCES);
347
348 parent_wnd = create_parent_window();
349 ok(parent_wnd != NULL, "Failed to create parent window!\n");
350
351 test_pager();
352 }