comctl32: rebar: Image height should be taken into account when computing the band...
[wine] / dlls / comctl32 / tests / rebar.c
1 /* Unit tests for rebar.
2  *
3  * Copyright 2007 Mikolaj Zalewski
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2.1 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with this library; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
18  */
19
20 /* make sure the structures work with a comctl32 v5.x */
21 #define _WIN32_WINNT 0x500
22 #define _WIN32_IE 0x500
23
24 #include <assert.h>
25 #include <stdarg.h>
26
27 #include <windows.h>
28 #include <commctrl.h>
29 #include <uxtheme.h>
30
31 #include "wine/test.h"
32
33 RECT height_change_notify_rect;
34 static HWND hMainWnd;
35 static HWND hRebar;
36
37
38 #define check_rect(name, val, exp) ok(val.top == exp.top && val.bottom == exp.bottom && \
39     val.left == exp.left && val.right == exp.right, "invalid rect (" name ") (%d,%d) (%d,%d) - expected (%d,%d) (%d,%d)\n", \
40     val.left, val.top, val.right, val.bottom, exp.left, exp.top, exp.right, exp.bottom);
41
42 #define check_rect_no_top(name, val, exp) { \
43         ok((val.bottom - val.top == exp.bottom - exp.top) && \
44             val.left == exp.left && val.right == exp.right, "invalid rect (" name ") (%d,%d) (%d,%d) - expected (%d,%d) (%d,%d), ignoring top\n", \
45             val.left, val.top, val.right, val.bottom, exp.left, exp.top, exp.right, exp.bottom); \
46     }
47
48 #define compare(val, exp, format) ok((val) == (exp), #val " value " format " expected " format "\n", (val), (exp));
49
50 #define expect_eq(expr, value, type, format) { type ret = expr; ok((value) == ret, #expr " expected " format "  got " format "\n", (value), (ret)); }
51
52 static INT CALLBACK is_font_installed_proc(const LOGFONT *elf, const TEXTMETRIC *ntm, DWORD type, LPARAM lParam)
53 {
54     return 0;
55 }
56
57 static BOOL is_font_installed(const char *name)
58 {
59     HDC hdc = GetDC(0);
60     BOOL ret = FALSE;
61
62     if(!EnumFontFamiliesA(hdc, name, is_font_installed_proc, 0))
63         ret = TRUE;
64
65     ReleaseDC(0, hdc);
66     return ret;
67 }
68
69 static void rebuild_rebar(HWND *hRebar)
70 {
71     if (*hRebar)
72         DestroyWindow(*hRebar);
73
74     *hRebar = CreateWindow(REBARCLASSNAME, NULL, WS_CHILD | WS_VISIBLE, 0, 0, 0, 0,
75         hMainWnd, (HMENU)17, GetModuleHandle(NULL), NULL);
76     SendMessageA(*hRebar, WM_SETFONT, (WPARAM)GetStockObject(SYSTEM_FONT), 0);
77 }
78
79 static HWND build_toolbar(int nr, HWND hParent)
80 {
81     TBBUTTON btns[8];
82     HWND hToolbar = CreateWindowEx(0, TOOLBARCLASSNAME, NULL, WS_CHILD | WS_VISIBLE | CCS_NORESIZE, 0, 0, 0, 0,
83         hParent, (HMENU)5, GetModuleHandle(NULL), NULL);
84     int iBitmapId = 0;
85     int i;
86
87     ok(hToolbar != NULL, "Toolbar creation problem\n");
88     ok(SendMessage(hToolbar, TB_BUTTONSTRUCTSIZE, (WPARAM)sizeof(TBBUTTON), 0) == 0, "TB_BUTTONSTRUCTSIZE failed\n");
89     ok(SendMessage(hToolbar, TB_AUTOSIZE, 0, 0) == 0, "TB_AUTOSIZE failed\n");
90     ok(SendMessage(hToolbar, WM_SETFONT, (WPARAM)GetStockObject(SYSTEM_FONT), 0)==1, "WM_SETFONT\n");
91
92     for (i=0; i<5+nr; i++)
93     {
94         btns[i].iBitmap = i;
95         btns[i].idCommand = i;
96         btns[i].fsStyle = BTNS_BUTTON;
97         btns[i].fsState = TBSTATE_ENABLED;
98         btns[i].iString = 0;
99     }
100
101     switch (nr)
102     {
103         case 0: iBitmapId = IDB_HIST_SMALL_COLOR; break;
104         case 1: iBitmapId = IDB_VIEW_SMALL_COLOR; break;
105         case 2: iBitmapId = IDB_STD_SMALL_COLOR; break;
106     }
107     ok(SendMessage(hToolbar, TB_LOADIMAGES, iBitmapId, (LPARAM)HINST_COMMCTRL) == 0, "TB_LOADIMAGE failed\n");
108     ok(SendMessage(hToolbar, TB_ADDBUTTONS, 5+nr, (LPARAM)btns), "TB_ADDBUTTONS failed\n");
109     return hToolbar;
110 }
111
112 static LRESULT CALLBACK MyWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
113 {
114     switch (msg)
115     {
116         case WM_NOTIFY:
117             {
118                 NMHDR *lpnm = (NMHDR *)lParam;
119                 if (lpnm->code == RBN_HEIGHTCHANGE)
120                     GetClientRect(hRebar, &height_change_notify_rect);
121             }
122             break;
123     }
124     return DefWindowProcA(hWnd, msg, wParam, lParam);
125 }
126
127 #if 0  /* use this to generate more tests*/
128
129 static void dump_sizes(HWND hRebar)
130 {
131     SIZE sz;
132     RECT r;
133     int count;
134     int i, h;
135
136     GetClientRect(hRebar, &r);
137     count = SendMessageA(hRebar, RB_GETROWCOUNT, 0, 0);
138     printf("  { {%d, %d, %d, %d}, %d, %d, {", r.left, r.top, r.right, r.bottom,
139         SendMessageA(hRebar, RB_GETBARHEIGHT, 0, 0), count);
140     if (count == 0)
141         printf("0, ");
142     for (i = 0; i < count; i++)  /* rows */
143         printf("%d, ", SendMessageA(hRebar, RB_GETROWHEIGHT, i, 0));
144     printf("}, ");
145
146     count = SendMessageA(hRebar, RB_GETBANDCOUNT, 0, 0);
147     printf("%d, {", count);
148     if (count == 0)
149         printf("{{0, 0, 0, 0}, 0, 0},");
150     for (i=0; i<count; i++)
151     {
152         REBARBANDINFO rbi;
153         rbi.cbSize = sizeof(REBARBANDINFO);
154         rbi.fMask = RBBIM_SIZE | RBBIM_CHILDSIZE | RBBIM_STYLE;
155         ok(SendMessageA(hRebar, RB_GETBANDINFOA, i, (LPARAM)&rbi), "RB_GETBANDINFO failed\n");
156         ok(SendMessageA(hRebar, RB_GETRECT, i, (LPARAM)&r), "RB_GETRECT failed\n");
157         printf("%s{ {%3d, %3d, %3d, %3d}, 0x%02x, %d}, ", (i%2==0 ? "\n    " : ""), r.left, r.top, r.right, r.bottom,
158             rbi.fStyle, rbi.cx);
159     }
160     printf("\n  }, }, \n");
161 }
162
163 #define check_sizes() dump_sizes(hRebar);
164 #define check_sizes_todo(todomask) dump_sizes(hRebar);
165
166 #else
167
168 typedef struct {
169     RECT rc;
170     DWORD fStyle;
171     INT cx;
172 } rbband_result_t;
173
174 typedef struct {
175     RECT rcClient;
176     int cyBarHeight;
177     int nRows;
178     int cyRowHeights[50];
179     int nBands;
180     rbband_result_t bands[50];
181 } rbsize_result_t;
182
183 rbsize_result_t rbsize_results[] = {
184   { {0, 0, 672, 0}, 0, 0, {0, }, 0, {{{0, 0, 0, 0}, 0, 0},
185   }, },
186   { {0, 0, 672, 4}, 4, 1, {4, }, 1, {
187     { {  0,   0, 672,   4}, 0x00, 200},
188   }, },
189   { {0, 0, 672, 4}, 4, 1, {4, }, 2, {
190     { {  0,   0, 200,   4}, 0x00, 200}, { {200,   0, 672,   4}, 0x04, 200},
191   }, },
192   { {0, 0, 672, 30}, 30, 1, {30, }, 3, {
193     { {  0,   0, 200,  30}, 0x00, 200}, { {200,   0, 400,  30}, 0x04, 200},
194     { {400,   0, 672,  30}, 0x00, 200},
195   }, },
196   { {0, 0, 672, 34}, 34, 1, {34, }, 4, {
197     { {  0,   0, 200,  34}, 0x00, 200}, { {200,   0, 400,  34}, 0x04, 200},
198     { {400,   0, 604,  34}, 0x00, 200}, { {604,   0, 672,  34}, 0x04, 68},
199   }, },
200   { {0, 0, 672, 34}, 34, 1, {34, }, 4, {
201     { {  0,   0, 200,  34}, 0x00, 200}, { {200,   0, 400,  34}, 0x04, 200},
202     { {400,   0, 604,  34}, 0x00, 200}, { {604,   0, 672,  34}, 0x04, 68},
203   }, },
204   { {0, 0, 672, 34}, 34, 1, {34, }, 4, {
205     { {  0,   0, 200,  34}, 0x00, 200}, { {202,   0, 402,  34}, 0x04, 200},
206     { {404,   0, 604,  34}, 0x00, 200}, { {606,   0, 672,  34}, 0x04, 66},
207   }, },
208   { {0, 0, 672, 70}, 70, 2, {34, 34, }, 5, {
209     { {  0,   0, 142,  34}, 0x00, 200}, { {144,   0, 557,  34}, 0x00, 200},
210     { {559,   0, 672,  34}, 0x04, 200}, { {  0,  36, 200,  70}, 0x00, 200},
211     { {202,  36, 672,  70}, 0x04, 66},
212   }, },
213   { {0, 0, 672, 34}, 34, 1, {34, }, 5, {
214     { {  0,   0, 167,  34}, 0x00, 200}, { {169,   0, 582,  34}, 0x00, 200},
215     { {559,   0, 759,  34}, 0x08, 200}, { {584,   0, 627,  34}, 0x00, 200},
216     { {629,   0, 672,  34}, 0x04, 66},
217   }, },
218   { {0, 0, 672, 34}, 34, 1, {34, }, 4, {
219     { {  0,   0, 167,  34}, 0x00, 200}, { {169,   0, 582,  34}, 0x00, 200},
220     { {584,   0, 627,  34}, 0x00, 200}, { {629,   0, 672,  34}, 0x04, 66},
221   }, },
222   { {0, 0, 672, 34}, 34, 1, {34, }, 3, {
223     { {  0,   0, 413,  34}, 0x00, 200}, { {415,   0, 615,  34}, 0x00, 200},
224     { {617,   0, 672,  34}, 0x04, 66},
225   }, },
226   { {0, 0, 672, 34}, 34, 1, {34, }, 2, {
227     { {  0,   0, 604,  34}, 0x00, 200}, { {606,   0, 672,  34}, 0x04, 66},
228   }, },
229   { {0, 0, 672, 40}, 40, 2, {20, 20, }, 5, {
230     { {  0,   0, 114,  20}, 0x00, 40}, { {114,   0, 184,  20}, 0x00, 70},
231     { {184,   0, 424,  20}, 0x00, 240}, { {424,   0, 672,  20}, 0x00, 60},
232     { {  0,  20, 672,  40}, 0x00, 200},
233   }, },
234   { {0, 0, 672, 40}, 40, 2, {20, 20, }, 5, {
235     { {  0,   0, 114,  20}, 0x00, 40}, { {114,   0, 227,  20}, 0x00, 113},
236     { {227,   0, 424,  20}, 0x00, 197}, { {424,   0, 672,  20}, 0x00, 60},
237     { {  0,  20, 672,  40}, 0x00, 200},
238   }, },
239   { {0, 0, 672, 40}, 40, 2, {20, 20, }, 5, {
240     { {  0,   0, 114,  20}, 0x00, 40}, { {114,   0, 328,  20}, 0x00, 214},
241     { {328,   0, 511,  20}, 0x00, 183}, { {511,   0, 672,  20}, 0x00, 161},
242     { {  0,  20, 672,  40}, 0x00, 200},
243   }, },
244   { {0, 0, 672, 40}, 40, 2, {20, 20, }, 5, {
245     { {  0,   0, 114,  20}, 0x00, 40}, { {114,   0, 167,  20}, 0x00, 53},
246     { {167,   0, 511,  20}, 0x00, 344}, { {511,   0, 672,  20}, 0x00, 161},
247     { {  0,  20, 672,  40}, 0x00, 200},
248   }, },
249   { {0, 0, 672, 40}, 40, 2, {20, 20, }, 5, {
250     { {  0,   0, 114,  20}, 0x00, 40}, { {114,   0, 328,  20}, 0x00, 214},
251     { {328,   0, 511,  20}, 0x00, 183}, { {511,   0, 672,  20}, 0x00, 161},
252     { {  0,  20, 672,  40}, 0x00, 200},
253   }, },
254   { {0, 0, 672, 40}, 40, 2, {20, 20, }, 5, {
255     { {  0,   0, 114,  20}, 0x00, 40}, { {114,   0, 328,  20}, 0x00, 214},
256     { {328,   0, 511,  20}, 0x00, 183}, { {511,   0, 672,  20}, 0x00, 161},
257     { {  0,  20, 672,  40}, 0x00, 200},
258   }, },
259   { {0, 0, 672, 56}, 56, 2, {28, 28, }, 5, {
260     { {  0,   0, 114,  28}, 0x00, 40}, { {114,   0, 328,  28}, 0x00, 214},
261     { {328,   0, 511,  28}, 0x00, 183}, { {511,   0, 672,  28}, 0x00, 161},
262     { {  0,  28, 672,  56}, 0x00, 200},
263   }, },
264   { {0, 0, 672, 0}, 0, 0, {0, }, 0, {{{0, 0, 0, 0}, 0, 0},
265   }, },
266   { {0, 0, 672, 65}, 65, 1, {65, }, 3, {
267     { {  0,   0,  90,  65}, 0x40, 90}, { { 90,   0, 180,  65}, 0x40, 90},
268     { {180,   0, 672,  65}, 0x40, 90},
269   }, },
270   { {0, 0, 0, 226}, 0, 0, {0, }, 0, {{{0, 0, 0, 0}, 0, 0},
271   }, },
272   { {0, 0, 65, 226}, 65, 1, {65, }, 1, {
273     { {  0,   0, 226,  65}, 0x40, 90},
274   }, },
275   { {0, 0, 65, 226}, 65, 1, {65, }, 2, {
276     { {  0,   0,  90,  65}, 0x40, 90}, { { 90,   0, 226,  65}, 0x40, 90},
277   }, },
278   { {0, 0, 65, 226}, 65, 1, {65, }, 3, {
279     { {  0,   0,  90,  65}, 0x40, 90}, { { 90,   0, 163,  65}, 0x40, 90},
280     { {163,   0, 226,  65}, 0x40, 90},
281   }, },
282 };
283
284 static int rbsize_numtests = 0;
285
286 #define check_sizes_todo(todomask) { \
287         RECT rc; \
288         REBARBANDINFO rbi; \
289         int count, i/*, mask=(todomask)*/; \
290         rbsize_result_t *res = &rbsize_results[rbsize_numtests]; \
291         assert(rbsize_numtests < sizeof(rbsize_results)/sizeof(rbsize_results[0])); \
292         GetClientRect(hRebar, &rc); \
293         check_rect("client", rc, res->rcClient); \
294         count = SendMessage(hRebar, RB_GETROWCOUNT, 0, 0); \
295         compare(count, res->nRows, "%d"); \
296         for (i=0; i<min(count, res->nRows); i++) { \
297             int height = SendMessageA(hRebar, RB_GETROWHEIGHT, 0, 0);\
298             ok(height == res->cyRowHeights[i], "Height mismatch for row %d - %d vs %d\n", i, res->cyRowHeights[i], height); \
299         } \
300         count = SendMessage(hRebar, RB_GETBANDCOUNT, 0, 0); \
301         compare(count, res->nBands, "%d"); \
302         for (i=0; i<min(count, res->nBands); i++) { \
303             ok(SendMessageA(hRebar, RB_GETRECT, i, (LPARAM)&rc) == 1, "RB_ITEMRECT\n"); \
304             if (!(res->bands[i].fStyle & RBBS_HIDDEN)) \
305                 check_rect("band", rc, res->bands[i].rc); \
306             rbi.cbSize = sizeof(REBARBANDINFO); \
307             rbi.fMask = RBBIM_STYLE | RBBIM_SIZE; \
308             ok(SendMessageA(hRebar, RB_GETBANDINFO,  i, (LPARAM)&rbi) == 1, "RB_GETBANDINFO\n"); \
309             compare(rbi.fStyle, res->bands[i].fStyle, "%x"); \
310             compare(rbi.cx, res->bands[i].cx, "%d"); \
311         } \
312         rbsize_numtests++; \
313     }
314
315 #define check_sizes() check_sizes_todo(0)
316
317 #endif
318
319 static void add_band_w(HWND hRebar, LPCSTR lpszText, int cxMinChild, int cx, int cxIdeal)
320 {
321     CHAR buffer[MAX_PATH];
322     REBARBANDINFO rbi;
323
324     if (lpszText != NULL)
325         strcpy(buffer, lpszText);
326     rbi.cbSize = sizeof(rbi);
327     rbi.fMask = RBBIM_SIZE | RBBIM_CHILDSIZE | RBBIM_CHILD | RBBIM_IDEALSIZE | RBBIM_TEXT;
328     rbi.cx = cx;
329     rbi.cxMinChild = cxMinChild;
330     rbi.cxIdeal = cxIdeal;
331     rbi.cyMinChild = 20;
332     rbi.hwndChild = build_toolbar(1, hRebar);
333     rbi.lpText = (lpszText ? buffer : NULL);
334     SendMessage(hRebar, RB_INSERTBAND, -1, (LPARAM)&rbi);
335 }
336
337 static void layout_test(void)
338 {
339     HWND hRebar = NULL;
340     REBARBANDINFO rbi;
341     HIMAGELIST himl;
342     REBARINFO ri;
343
344     rebuild_rebar(&hRebar);
345     check_sizes();
346     rbi.cbSize = sizeof(rbi);
347     rbi.fMask = RBBIM_SIZE | RBBIM_CHILDSIZE | RBBIM_CHILD;
348     rbi.cx = 200;
349     rbi.cxMinChild = 100;
350     rbi.cyMinChild = 30;
351     rbi.hwndChild = NULL;
352     SendMessageA(hRebar, RB_INSERTBAND, -1, (LPARAM)&rbi);
353     check_sizes();
354
355     rbi.fMask |= RBBIM_STYLE;
356     rbi.fStyle = RBBS_CHILDEDGE;
357     SendMessageA(hRebar, RB_INSERTBAND, -1, (LPARAM)&rbi);
358     check_sizes();
359
360     rbi.fStyle = 0;
361     rbi.cx = 200;
362     rbi.cxMinChild = 30;
363     rbi.cyMinChild = 30;
364     rbi.hwndChild = build_toolbar(0, hRebar);
365     SendMessageA(hRebar, RB_INSERTBAND, -1, (LPARAM)&rbi);
366     check_sizes();
367
368     rbi.fStyle = RBBS_CHILDEDGE;
369     rbi.cx = 68;
370     rbi.hwndChild = build_toolbar(0, hRebar);
371     SendMessageA(hRebar, RB_INSERTBAND, -1, (LPARAM)&rbi);
372     check_sizes();
373
374     SetWindowLong(hRebar, GWL_STYLE, GetWindowLong(hRebar, GWL_STYLE) | RBS_BANDBORDERS);
375     check_sizes();      /* a style change won't start a relayout */
376     rbi.fMask = RBBIM_SIZE;
377     rbi.cx = 66;
378     SendMessageA(hRebar, RB_SETBANDINFO, 3, (LPARAM)&rbi);
379     check_sizes();      /* here it will be relayouted */
380
381     /* this will force a new row */
382     rbi.fMask = RBBIM_SIZE | RBBIM_CHILDSIZE | RBBIM_CHILD;
383     rbi.cx = 200;
384     rbi.cxMinChild = 400;
385     rbi.cyMinChild = 30;
386     rbi.hwndChild = build_toolbar(0, hRebar);
387     SendMessageA(hRebar, RB_INSERTBAND, 1, (LPARAM)&rbi);
388     check_sizes();
389
390     rbi.fMask = RBBIM_STYLE;
391     rbi.fStyle = RBBS_HIDDEN;
392     SendMessageA(hRebar, RB_SETBANDINFO, 2, (LPARAM)&rbi);
393     check_sizes();
394
395     SendMessageA(hRebar, RB_DELETEBAND, 2, 0);
396     check_sizes();
397     SendMessageA(hRebar, RB_DELETEBAND, 0, 0);
398     check_sizes();
399     SendMessageA(hRebar, RB_DELETEBAND, 1, 0);
400     check_sizes();
401
402     rebuild_rebar(&hRebar);
403     add_band_w(hRebar, "ABC",     70,  40, 100);
404     add_band_w(hRebar, NULL,      40,  70, 100);
405     add_band_w(hRebar, NULL,     170, 240, 100);
406     add_band_w(hRebar, "MMMMMMM", 60,  60, 100);
407     add_band_w(hRebar, NULL,     200, 200, 100);
408     check_sizes();
409     SendMessageA(hRebar, RB_MAXIMIZEBAND, 1, TRUE);
410     check_sizes();
411     SendMessageA(hRebar, RB_MAXIMIZEBAND, 1, TRUE);
412     check_sizes();
413     SendMessageA(hRebar, RB_MAXIMIZEBAND, 2, FALSE);
414     check_sizes();
415     SendMessageA(hRebar, RB_MINIMIZEBAND, 2, 0);
416     check_sizes();
417     SendMessageA(hRebar, RB_MINIMIZEBAND, 0, 0);
418     check_sizes();
419
420     /* an image will increase the band height */
421     himl = ImageList_LoadImage(LoadLibrary("comctl32"), MAKEINTRESOURCE(121), 24, 2, CLR_NONE, IMAGE_BITMAP, LR_DEFAULTCOLOR);
422     ri.cbSize = sizeof(ri);
423     ri.fMask = RBIM_IMAGELIST;
424     ri.himl = himl;
425     ok(SendMessage(hRebar, RB_SETBARINFO, 0, (LPARAM)&ri), "RB_SETBARINFO failed\n");
426     rbi.fMask = RBBIM_IMAGE;
427     rbi.iImage = 1;
428     SendMessage(hRebar, RB_SETBANDINFO, 1, (LPARAM)&rbi);
429     check_sizes();
430
431     /* VARHEIGHT resizing test on a horizontal rebar */
432     rebuild_rebar(&hRebar);
433     SetWindowLong(hRebar, GWL_STYLE, GetWindowLong(hRebar, GWL_STYLE) | RBS_AUTOSIZE);
434     check_sizes();
435     rbi.fMask = RBBIM_CHILD | RBBIM_CHILDSIZE | RBBIM_SIZE | RBBIM_STYLE;
436     rbi.fStyle = RBBS_VARIABLEHEIGHT;
437     rbi.cxMinChild = 50;
438     rbi.cyMinChild = 10;
439     rbi.cyIntegral = 11;
440     rbi.cyChild = 70;
441     rbi.cyMaxChild = 200;
442     rbi.cx = 90;
443     rbi.hwndChild = build_toolbar(0, hRebar);
444     SendMessageA(hRebar, RB_INSERTBAND, -1, (LPARAM)&rbi);
445
446     rbi.cyChild = 50;
447     rbi.hwndChild = build_toolbar(0, hRebar);
448     SendMessageA(hRebar, RB_INSERTBAND, -1, (LPARAM)&rbi);
449
450     rbi.cyMinChild = 40;
451     rbi.cyChild = 50;
452     rbi.cyIntegral = 5;
453     rbi.hwndChild = build_toolbar(0, hRebar);
454     SendMessageA(hRebar, RB_INSERTBAND, -1, (LPARAM)&rbi);
455     check_sizes();
456
457     /* VARHEIGHT resizing on a vertical rebar */
458     rebuild_rebar(&hRebar);
459     SetWindowLong(hRebar, GWL_STYLE, GetWindowLong(hRebar, GWL_STYLE) | CCS_VERT | RBS_AUTOSIZE);
460     check_sizes();
461     rbi.fMask = RBBIM_CHILD | RBBIM_CHILDSIZE | RBBIM_SIZE | RBBIM_STYLE;
462     rbi.fStyle = RBBS_VARIABLEHEIGHT;
463     rbi.cxMinChild = 50;
464     rbi.cyMinChild = 10;
465     rbi.cyIntegral = 11;
466     rbi.cyChild = 70;
467     rbi.cyMaxChild = 90;
468     rbi.cx = 90;
469     rbi.hwndChild = build_toolbar(0, hRebar);
470     SendMessageA(hRebar, RB_INSERTBAND, -1, (LPARAM)&rbi);
471     check_sizes();
472
473     rbi.cyChild = 50;
474     rbi.hwndChild = build_toolbar(0, hRebar);
475     SendMessageA(hRebar, RB_INSERTBAND, -1, (LPARAM)&rbi);
476     check_sizes();
477
478     rbi.cyMinChild = 40;
479     rbi.cyChild = 50;
480     rbi.cyIntegral = 5;
481     rbi.hwndChild = build_toolbar(0, hRebar);
482     SendMessageA(hRebar, RB_INSERTBAND, -1, (LPARAM)&rbi);
483     check_sizes();
484
485     DestroyWindow(hRebar);
486 }
487
488 #if 0       /* use this to generate more tests */
489
490 static void dump_client(HWND hRebar)
491 {
492     RECT r;
493     BOOL notify;
494     GetWindowRect(hRebar, &r);
495     MapWindowPoints(HWND_DESKTOP, hMainWnd, (LPPOINT)&r, 2);
496     if (height_change_notify_rect.top != -1)
497     {
498         RECT rcClient;
499         GetClientRect(hRebar, &rcClient);
500         assert(EqualRect(&rcClient, &height_change_notify_rect));
501         notify = TRUE;
502     }
503     else
504         notify = FALSE;
505     printf("    {{%d, %d, %d, %d}, %d, %s},\n", r.left, r.top, r.right, r.bottom, SendMessage(hRebar, RB_GETROWCOUNT, 0, 0),
506         notify ? "TRUE" : "FALSE");
507     SetRect(&height_change_notify_rect, -1, -1, -1, -1);
508 }
509
510 #define comment(fmt, arg1) printf("/* " fmt " */\n", arg1);
511 #define check_client() dump_client(hRebar)
512
513 #else
514
515 typedef struct {
516     RECT rc;
517     INT iNumRows;
518     BOOL heightNotify;
519 } rbresize_test_result_t;
520
521 rbresize_test_result_t resize_results[] = {
522 /* style 00000001 */
523     {{0, 2, 672, 2}, 0, FALSE},
524     {{0, 2, 672, 22}, 1, TRUE},
525     {{0, 2, 672, 22}, 1, FALSE},
526     {{0, 2, 672, 22}, 1, FALSE},
527     {{0, 2, 672, 22}, 1, FALSE},
528     {{0, 2, 672, 22}, 0, FALSE},
529 /* style 00000041 */
530     {{0, 0, 672, 0}, 0, FALSE},
531     {{0, 0, 672, 20}, 1, TRUE},
532     {{0, 0, 672, 20}, 1, FALSE},
533     {{0, 0, 672, 20}, 1, FALSE},
534     {{0, 0, 672, 20}, 1, FALSE},
535     {{0, 0, 672, 20}, 0, FALSE},
536 /* style 00000003 */
537     {{0, 226, 672, 226}, 0, FALSE},
538     {{0, 206, 672, 226}, 1, TRUE},
539     {{0, 206, 672, 226}, 1, FALSE},
540     {{0, 206, 672, 226}, 1, FALSE},
541     {{0, 206, 672, 226}, 1, FALSE},
542     {{0, 206, 672, 226}, 0, FALSE},
543 /* style 00000043 */
544     {{0, 226, 672, 226}, 0, FALSE},
545     {{0, 206, 672, 226}, 1, TRUE},
546     {{0, 206, 672, 226}, 1, FALSE},
547     {{0, 206, 672, 226}, 1, FALSE},
548     {{0, 206, 672, 226}, 1, FALSE},
549     {{0, 206, 672, 226}, 0, FALSE},
550 /* style 00000080 */
551     {{2, 0, 2, 226}, 0, FALSE},
552     {{2, 0, 22, 226}, 1, TRUE},
553     {{2, 0, 22, 226}, 1, FALSE},
554     {{2, 0, 22, 226}, 1, FALSE},
555     {{2, 0, 22, 226}, 1, FALSE},
556     {{2, 0, 22, 226}, 0, FALSE},
557 /* style 00000083 */
558     {{672, 0, 672, 226}, 0, FALSE},
559     {{652, 0, 672, 226}, 1, TRUE},
560     {{652, 0, 672, 226}, 1, FALSE},
561     {{652, 0, 672, 226}, 1, FALSE},
562     {{652, 0, 672, 226}, 1, FALSE},
563     {{652, 0, 672, 226}, 0, FALSE},
564 /* style 00000008 */
565     {{10, 11, 510, 11}, 0, FALSE},
566     {{10, 15, 510, 35}, 1, TRUE},
567     {{10, 17, 510, 37}, 1, FALSE},
568     {{10, 14, 110, 54}, 2, TRUE},
569     {{0, 4, 0, 44}, 2, FALSE},
570     {{0, 6, 0, 46}, 2, FALSE},
571     {{0, 8, 0, 48}, 2, FALSE},
572     {{0, 12, 0, 32}, 1, TRUE},
573     {{0, 4, 100, 24}, 0, FALSE},
574 /* style 00000048 */
575     {{10, 5, 510, 5}, 0, FALSE},
576     {{10, 5, 510, 25}, 1, TRUE},
577     {{10, 5, 510, 25}, 1, FALSE},
578     {{10, 10, 110, 50}, 2, TRUE},
579     {{0, 0, 0, 40}, 2, FALSE},
580     {{0, 0, 0, 40}, 2, FALSE},
581     {{0, 0, 0, 40}, 2, FALSE},
582     {{0, 0, 0, 20}, 1, TRUE},
583     {{0, 0, 100, 20}, 0, FALSE},
584 /* style 00000004 */
585     {{10, 5, 510, 20}, 0, FALSE},
586     {{10, 5, 510, 20}, 1, TRUE},
587     {{10, 10, 110, 110}, 2, TRUE},
588     {{0, 0, 0, 0}, 2, FALSE},
589     {{0, 0, 0, 0}, 2, FALSE},
590     {{0, 0, 0, 0}, 2, FALSE},
591     {{0, 0, 0, 0}, 1, TRUE},
592     {{0, 0, 100, 100}, 0, FALSE},
593 /* style 00000002 */
594     {{0, 5, 672, 5}, 0, FALSE},
595     {{0, 5, 672, 25}, 1, TRUE},
596     {{0, 10, 672, 30}, 1, FALSE},
597     {{0, 0, 672, 20}, 1, FALSE},
598     {{0, 0, 672, 20}, 1, FALSE},
599     {{0, 0, 672, 20}, 0, FALSE},
600 /* style 00000082 */
601     {{10, 0, 10, 226}, 0, FALSE},
602     {{10, 0, 30, 226}, 1, TRUE},
603     {{10, 0, 30, 226}, 1, FALSE},
604     {{0, 0, 20, 226}, 1, FALSE},
605     {{0, 0, 20, 226}, 1, FALSE},
606     {{0, 0, 20, 226}, 0, FALSE},
607 /* style 00800001 */
608     {{-2, 0, 674, 4}, 0, FALSE},
609     {{-2, 0, 674, 24}, 1, TRUE},
610     {{-2, 0, 674, 24}, 1, FALSE},
611     {{-2, 0, 674, 24}, 1, FALSE},
612     {{-2, 0, 674, 24}, 1, FALSE},
613     {{-2, 0, 674, 24}, 0, FALSE},
614 /* style 00800048 */
615     {{10, 5, 510, 9}, 0, FALSE},
616     {{10, 5, 510, 29}, 1, TRUE},
617     {{10, 5, 510, 29}, 1, FALSE},
618     {{10, 10, 110, 54}, 2, TRUE},
619     {{0, 0, 0, 44}, 2, FALSE},
620     {{0, 0, 0, 44}, 2, FALSE},
621     {{0, 0, 0, 44}, 2, FALSE},
622     {{0, 0, 0, 24}, 1, TRUE},
623     {{0, 0, 100, 24}, 0, FALSE},
624 /* style 00800004 */
625     {{10, 5, 510, 20}, 0, FALSE},
626     {{10, 5, 510, 20}, 1, TRUE},
627     {{10, 10, 110, 110}, 2, TRUE},
628     {{0, 0, 0, 0}, 2, FALSE},
629     {{0, 0, 0, 0}, 2, FALSE},
630     {{0, 0, 0, 0}, 2, FALSE},
631     {{0, 0, 0, 0}, 1, TRUE},
632     {{0, 0, 100, 100}, 0, FALSE},
633 /* style 00800002 */
634     {{-2, 5, 674, 9}, 0, FALSE},
635     {{-2, 5, 674, 29}, 1, TRUE},
636     {{-2, 10, 674, 34}, 1, FALSE},
637     {{-2, 0, 674, 24}, 1, FALSE},
638     {{-2, 0, 674, 24}, 1, FALSE},
639     {{-2, 0, 674, 24}, 0, FALSE},
640 };
641
642 static int resize_numtests = 0;
643
644 #define comment(fmt, arg1)
645 #define check_client() { \
646         RECT r; \
647         rbresize_test_result_t *res = &resize_results[resize_numtests++]; \
648         assert(resize_numtests <= sizeof(resize_results)/sizeof(resize_results[0])); \
649         GetWindowRect(hRebar, &r); \
650         MapWindowPoints(HWND_DESKTOP, hMainWnd, (LPPOINT)&r, 2); \
651         if ((dwStyles[i] & (CCS_NOPARENTALIGN|CCS_NODIVIDER)) == CCS_NOPARENTALIGN) {\
652             check_rect_no_top("client", r, res->rc); /* the top coordinate changes after every layout and is very implementation-dependent */ \
653         } else { \
654             check_rect("client", r, res->rc); \
655         } \
656         expect_eq((int)SendMessage(hRebar, RB_GETROWCOUNT, 0, 0), res->iNumRows, int, "%d"); \
657         if (res->heightNotify) { \
658             RECT rcClient; \
659             GetClientRect(hRebar, &rcClient); \
660             check_rect("notify", height_change_notify_rect, rcClient); \
661         } else ok(height_change_notify_rect.top == -1, "Unexpected RBN_HEIGHTCHANGE received\n"); \
662         SetRect(&height_change_notify_rect, -1, -1, -1, -1); \
663     }
664
665 #endif
666
667 static void resize_test(void)
668 {
669     DWORD dwStyles[] = {CCS_TOP, CCS_TOP | CCS_NODIVIDER, CCS_BOTTOM, CCS_BOTTOM | CCS_NODIVIDER, CCS_VERT, CCS_RIGHT,
670         CCS_NOPARENTALIGN, CCS_NOPARENTALIGN | CCS_NODIVIDER, CCS_NORESIZE, CCS_NOMOVEY, CCS_NOMOVEY | CCS_VERT,
671         CCS_TOP | WS_BORDER, CCS_NOPARENTALIGN | CCS_NODIVIDER | WS_BORDER, CCS_NORESIZE | WS_BORDER,
672         CCS_NOMOVEY | WS_BORDER};
673
674     const int styles_count = sizeof(dwStyles) / sizeof(dwStyles[0]);
675     int i;
676
677     for (i = 0; i < styles_count; i++)
678     {
679         comment("style %08x", dwStyles[i]);
680         SetRect(&height_change_notify_rect, -1, -1, -1, -1);
681         hRebar = CreateWindow(REBARCLASSNAME, "A", dwStyles[i] | WS_CHILD | WS_VISIBLE, 10, 5, 500, 15, hMainWnd, NULL, GetModuleHandle(NULL), 0);
682         check_client();
683         add_band_w(hRebar, NULL, 70, 100, 0);
684         if (dwStyles[i] & CCS_NOPARENTALIGN)  /* the window drifts downward for CCS_NOPARENTALIGN without CCS_NODIVIDER */
685             check_client();
686         add_band_w(hRebar, NULL, 70, 100, 0);
687         check_client();
688         MoveWindow(hRebar, 10, 10, 100, 100, TRUE);
689         check_client();
690         MoveWindow(hRebar, 0, 0, 0, 0, TRUE);
691         check_client();
692         /* try to fool the rebar by sending invalid width/height - won't work */
693         if (dwStyles[i] & (CCS_NORESIZE | CCS_NOPARENTALIGN))
694         {
695             WINDOWPOS pos;
696             pos.hwnd = hRebar;
697             pos.hwndInsertAfter = NULL;
698             pos.cx = 500;
699             pos.cy = 500;
700             pos.x = 10;
701             pos.y = 10;
702             pos.flags = 0;
703             SendMessage(hRebar, WM_WINDOWPOSCHANGING, 0, (LPARAM)&pos);
704             SendMessage(hRebar, WM_WINDOWPOSCHANGED, 0, (LPARAM)&pos);
705             check_client();
706             SendMessage(hRebar, WM_SIZE, SIZE_RESTORED, MAKELONG(500, 500));
707             check_client();
708         }
709         SendMessage(hRebar, RB_DELETEBAND, 0, 0);
710         check_client();
711         SendMessage(hRebar, RB_DELETEBAND, 0, 0);
712         MoveWindow(hRebar, 0, 0, 100, 100, TRUE);
713         check_client();
714         DestroyWindow(hRebar);
715     }
716 }
717
718 static void expect_band_content(UINT uBand, INT fStyle, COLORREF clrFore,
719     COLORREF clrBack, LPCSTR lpText, int iImage, HWND hwndChild,
720     INT cxMinChild, INT cyMinChild, INT cx, HBITMAP hbmBack, INT wID,
721     INT cyChild, INT cyMaxChild, INT cyIntegral, INT cxIdeal, LPARAM lParam,
722     INT cxHeader)
723 {
724     CHAR buf[MAX_PATH] = "abc";
725     REBARBANDINFO rb;
726
727     memset(&rb, 0xdd, sizeof(rb));
728     rb.cbSize = sizeof(rb);
729     rb.fMask = RBBIM_BACKGROUND | RBBIM_CHILD | RBBIM_CHILDSIZE | RBBIM_COLORS
730         | RBBIM_HEADERSIZE | RBBIM_ID | RBBIM_IDEALSIZE | RBBIM_IMAGE | RBBIM_LPARAM
731         | RBBIM_SIZE | RBBIM_STYLE | RBBIM_TEXT;
732     rb.lpText = buf;
733     rb.cch = MAX_PATH;
734     ok(SendMessageA(hRebar, RB_GETBANDINFOA, uBand, (LPARAM)&rb), "RB_GETBANDINFO failed\n");
735     expect_eq(rb.fStyle, fStyle, int, "%x");
736     todo_wine expect_eq(rb.clrFore, clrFore, COLORREF, "%x");
737     todo_wine expect_eq(rb.clrBack, clrBack, unsigned, "%x");
738     expect_eq(strcmp(rb.lpText, lpText), 0, int, "%d");
739     expect_eq(rb.iImage, iImage, int, "%x");
740     expect_eq(rb.hwndChild, hwndChild, HWND, "%p");
741     expect_eq(rb.cxMinChild, cxMinChild, int, "%d");
742     expect_eq(rb.cyMinChild, cyMinChild, int, "%d");
743     expect_eq(rb.cx, cx, int, "%d");
744     expect_eq(rb.hbmBack, hbmBack, HBITMAP, "%p");
745     expect_eq(rb.wID, wID, int, "%d");
746     /* the values of cyChild, cyMaxChild and cyIntegral can't be read unless the band is RBBS_VARIABLEHEIGHT */
747     expect_eq(rb.cyChild, cyChild, int, "%x");
748     expect_eq(rb.cyMaxChild, cyMaxChild, int, "%x");
749     expect_eq(rb.cyIntegral, cyIntegral, int, "%x");
750     expect_eq(rb.cxIdeal, cxIdeal, int, "%d");
751     expect_eq(rb.lParam, lParam, LPARAM, "%ld");
752     expect_eq(rb.cxHeader, cxHeader, int, "%d");
753 }
754
755 static void bandinfo_test(void)
756 {
757     REBARBANDINFOA rb;
758     CHAR szABC[] = "ABC";
759     CHAR szABCD[] = "ABCD";
760
761     rebuild_rebar(&hRebar);
762     rb.cbSize = sizeof(REBARBANDINFO);
763     rb.fMask = 0;
764     ok(SendMessageA(hRebar, RB_INSERTBANDA, 0, (LPARAM)&rb), "RB_INSERTBAND failed\n");
765     expect_band_content(0, 0, 0, GetSysColor(COLOR_3DFACE), "", -1, NULL, 0, 0, 0, NULL, 0, 0xdddddddd, 0xdddddddd, 0xdddddddd, 0, 0, 0);
766
767     rb.fMask = RBBIM_CHILDSIZE;
768     rb.cxMinChild = 15;
769     rb.cyMinChild = 20;
770     rb.cyChild = 30;
771     rb.cyMaxChild = 20;
772     rb.cyIntegral = 10;
773     ok(SendMessageA(hRebar, RB_SETBANDINFOA, 0, (LPARAM)&rb), "RB_INSERTBAND failed\n");
774     expect_band_content(0, 0, 0, GetSysColor(COLOR_3DFACE), "", -1, NULL, 15, 20, 0, NULL, 0, 0xdddddddd, 0xdddddddd, 0xdddddddd, 0, 0, 0);
775
776     rb.fMask = RBBIM_TEXT;
777     rb.lpText = szABC;
778     ok(SendMessageA(hRebar, RB_SETBANDINFOA, 0, (LPARAM)&rb), "RB_INSERTBAND failed\n");
779     expect_band_content(0, 0, 0, GetSysColor(COLOR_3DFACE), "ABC", -1, NULL, 15, 20, 0, NULL, 0, 0xdddddddd, 0xdddddddd, 0xdddddddd, 0, 0, 35);
780
781     rb.cbSize = sizeof(REBARBANDINFO);
782     rb.fMask = 0;
783     ok(SendMessageA(hRebar, RB_INSERTBANDA, 1, (LPARAM)&rb), "RB_INSERTBAND failed\n");
784     expect_band_content(1, 0, 0, GetSysColor(COLOR_3DFACE), "", -1, NULL, 0, 0, 0, NULL, 0, 0xdddddddd, 0xdddddddd, 0xdddddddd, 0, 0, 9);
785     expect_band_content(0, 0, 0, GetSysColor(COLOR_3DFACE), "ABC", -1, NULL, 15, 20, 0, NULL, 0, 0xdddddddd, 0xdddddddd, 0xdddddddd, 0, 0, 40);
786
787     rb.fMask = RBBIM_HEADERSIZE;
788     rb.cxHeader = 50;
789     ok(SendMessageA(hRebar, RB_SETBANDINFOA, 0, (LPARAM)&rb), "RB_INSERTBAND failed\n");
790     expect_band_content(0, 0x40000000, 0, GetSysColor(COLOR_3DFACE), "ABC", -1, NULL, 15, 20, 0, NULL, 0, 0xdddddddd, 0xdddddddd, 0xdddddddd, 0, 0, 50);
791
792     rb.cxHeader = 5;
793     ok(SendMessageA(hRebar, RB_SETBANDINFOA, 0, (LPARAM)&rb), "RB_INSERTBAND failed\n");
794     expect_band_content(0, 0x40000000, 0, GetSysColor(COLOR_3DFACE), "ABC", -1, NULL, 15, 20, 0, NULL, 0, 0xdddddddd, 0xdddddddd, 0xdddddddd, 0, 0, 5);
795
796     rb.fMask = RBBIM_TEXT;
797     rb.lpText = szABCD;
798     ok(SendMessageA(hRebar, RB_SETBANDINFOA, 0, (LPARAM)&rb), "RB_INSERTBAND failed\n");
799     expect_band_content(0, 0x40000000, 0, GetSysColor(COLOR_3DFACE), "ABCD", -1, NULL, 15, 20, 0, NULL, 0, 0xdddddddd, 0xdddddddd, 0xdddddddd, 0, 0, 5);
800     rb.fMask = RBBIM_STYLE | RBBIM_TEXT;
801     rb.fStyle = RBBS_VARIABLEHEIGHT;
802     rb.lpText = szABC;
803     ok(SendMessageA(hRebar, RB_SETBANDINFOA, 0, (LPARAM)&rb), "RB_INSERTBAND failed\n");
804     expect_band_content(0, RBBS_VARIABLEHEIGHT, 0, GetSysColor(COLOR_3DFACE), "ABC", -1, NULL, 15, 20, 0, NULL, 0, 20, 0x7fffffff, 0, 0, 0, 40);
805
806     DestroyWindow(hRebar);
807 }
808
809 START_TEST(rebar)
810 {
811     HMODULE hComctl32;
812     BOOL (WINAPI *pInitCommonControlsEx)(const INITCOMMONCONTROLSEX*);
813     INITCOMMONCONTROLSEX iccex;
814     WNDCLASSA wc;
815     MSG msg;
816     RECT rc;
817
818     hComctl32 = LoadLibraryA("comctl32.dll");
819     pInitCommonControlsEx = (void*)GetProcAddress(hComctl32, "InitCommonControlsEx");
820     if (!pInitCommonControlsEx)
821     {
822         skip("InitCommonControlsEx() is missing. Skipping the tests\n");
823         return;
824     }
825     iccex.dwSize = sizeof(iccex);
826     iccex.dwICC = ICC_COOL_CLASSES;
827     pInitCommonControlsEx(&iccex);
828
829     wc.style = CS_HREDRAW | CS_VREDRAW;
830     wc.cbClsExtra = 0;
831     wc.cbWndExtra = 0;
832     wc.hInstance = GetModuleHandleA(NULL);
833     wc.hIcon = NULL;
834     wc.hCursor = LoadCursorA(NULL, IDC_IBEAM);
835     wc.hbrBackground = GetSysColorBrush(COLOR_WINDOW);
836     wc.lpszMenuName = NULL;
837     wc.lpszClassName = "MyTestWnd";
838     wc.lpfnWndProc = MyWndProc;
839     RegisterClassA(&wc);
840     hMainWnd = CreateWindowExA(0, "MyTestWnd", "Blah", WS_OVERLAPPEDWINDOW,
841       CW_USEDEFAULT, CW_USEDEFAULT, 672+2*GetSystemMetrics(SM_CXSIZEFRAME),
842       226+GetSystemMetrics(SM_CYCAPTION)+2*GetSystemMetrics(SM_CYSIZEFRAME),
843       NULL, NULL, GetModuleHandleA(NULL), 0);
844     GetClientRect(hMainWnd, &rc);
845     ShowWindow(hMainWnd, SW_SHOW);
846
847     bandinfo_test();
848
849     if(is_font_installed("System") && is_font_installed("Tahoma"))
850     {
851         layout_test();
852         resize_test();
853     } else
854         skip("Missing System or Tahoma font\n");
855
856     PostQuitMessage(0);
857     while(GetMessageA(&msg,0,0,0)) {
858         TranslateMessage(&msg);
859         DispatchMessageA(&msg);
860     }
861     DestroyWindow(hMainWnd);
862 }