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