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