ole32: Fix memory leaks in the storage test.
[wine] / dlls / user32 / tests / combo.c
1 /* Unit test suite for combo boxes.
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 #include <assert.h>
21 #include <stdarg.h>
22 #include <stdio.h>
23
24 #define STRICT
25 #define WIN32_LEAN_AND_MEAN
26 #include <windows.h>
27
28 #include "wine/test.h"
29
30 #define COMBO_ID 1995
31
32 static HWND hMainWnd;
33
34 #define expect_eq(expr, value, type, fmt); { type val = expr; ok(val == (value), #expr " expected " #fmt " got " #fmt "\n", (value), val); }
35 #define expect_rect(r, _left, _top, _right, _bottom) ok(r.left == _left && r.top == _top && \
36     r.bottom == _bottom && r.right == _right, "Invalid rect (%d,%d) (%d,%d) vs (%d,%d) (%d,%d)\n", \
37     r.left, r.top, r.right, r.bottom, _left, _top, _right, _bottom);
38
39 static HWND build_combo(DWORD style)
40 {
41     return CreateWindow("ComboBox", "Combo", WS_VISIBLE|WS_CHILD|style, 5, 5, 100, 100, hMainWnd, (HMENU)COMBO_ID, NULL, 0);
42 }
43
44 static int font_height(HFONT hFont)
45 {
46     TEXTMETRIC tm;
47     HFONT hFontOld;
48     HDC hDC;
49
50     hDC = CreateCompatibleDC(NULL);
51     hFontOld = SelectObject(hDC, hFont);
52     GetTextMetrics(hDC, &tm);
53     SelectObject(hDC, hFontOld);
54     DeleteDC(hDC);
55
56     return tm.tmHeight;
57 }
58
59 static INT CALLBACK is_font_installed_proc(const LOGFONT *elf, const TEXTMETRIC *tm, DWORD type, LPARAM lParam)
60 {
61     return 0;
62 }
63
64 static int is_font_installed(const char *name)
65 {
66     HDC hdc = GetDC(NULL);
67     BOOL ret = !EnumFontFamilies(hdc, name, is_font_installed_proc, 0);
68     ReleaseDC(NULL, hdc);
69     return ret;
70 }
71
72 static void test_setitemheight(DWORD style)
73 {
74     HWND hCombo = build_combo(style);
75     RECT r;
76     int i;
77
78     trace("Style %x\n", style);
79     GetClientRect(hCombo, &r);
80     expect_rect(r, 0, 0, 100, 24);
81     SendMessageA(hCombo, CB_GETDROPPEDCONTROLRECT, 0, (LPARAM)&r);
82     MapWindowPoints(HWND_DESKTOP, hMainWnd, (LPPOINT)&r, 2);
83     todo_wine expect_rect(r, 5, 5, 105, 105);
84
85     for (i = 1; i < 30; i++)
86     {
87         SendMessage(hCombo, CB_SETITEMHEIGHT, -1, i);
88         GetClientRect(hCombo, &r);
89         expect_eq(r.bottom - r.top, i + 6, int, "%d");
90     }
91
92     DestroyWindow(hCombo);
93 }
94
95 static void test_setfont(DWORD style)
96 {
97     HWND hCombo;
98     HFONT hFont1, hFont2;
99     RECT r;
100     int i;
101
102     if (!is_font_installed("Marlett"))
103     {
104         skip("Marlett font not available\n");
105         return;
106     }
107
108     trace("Style %x\n", style);
109
110     hCombo = build_combo(style);
111     hFont1 = CreateFont(10, 0, 0, 0, FW_DONTCARE, FALSE, FALSE, FALSE, SYMBOL_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH|FF_DONTCARE, "Marlett");
112     hFont2 = CreateFont(8, 0, 0, 0, FW_DONTCARE, FALSE, FALSE, FALSE, SYMBOL_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH|FF_DONTCARE, "Marlett");
113
114     GetClientRect(hCombo, &r);
115     expect_rect(r, 0, 0, 100, 24);
116     SendMessageA(hCombo, CB_GETDROPPEDCONTROLRECT, 0, (LPARAM)&r);
117     MapWindowPoints(HWND_DESKTOP, hMainWnd, (LPPOINT)&r, 2);
118     todo_wine expect_rect(r, 5, 5, 105, 105);
119
120     if (font_height(hFont1) == 10 && font_height(hFont2) == 8)
121     {
122         SendMessage(hCombo, WM_SETFONT, (WPARAM)hFont1, FALSE);
123         GetClientRect(hCombo, &r);
124         expect_rect(r, 0, 0, 100, 18);
125         SendMessageA(hCombo, CB_GETDROPPEDCONTROLRECT, 0, (LPARAM)&r);
126         MapWindowPoints(HWND_DESKTOP, hMainWnd, (LPPOINT)&r, 2);
127         todo_wine expect_rect(r, 5, 5, 105, 99);
128
129         SendMessage(hCombo, WM_SETFONT, (WPARAM)hFont2, FALSE);
130         GetClientRect(hCombo, &r);
131         expect_rect(r, 0, 0, 100, 16);
132         SendMessageA(hCombo, CB_GETDROPPEDCONTROLRECT, 0, (LPARAM)&r);
133         MapWindowPoints(HWND_DESKTOP, hMainWnd, (LPPOINT)&r, 2);
134         todo_wine expect_rect(r, 5, 5, 105, 97);
135
136         SendMessage(hCombo, WM_SETFONT, (WPARAM)hFont1, FALSE);
137         GetClientRect(hCombo, &r);
138         expect_rect(r, 0, 0, 100, 18);
139         SendMessageA(hCombo, CB_GETDROPPEDCONTROLRECT, 0, (LPARAM)&r);
140         MapWindowPoints(HWND_DESKTOP, hMainWnd, (LPPOINT)&r, 2);
141         todo_wine expect_rect(r, 5, 5, 105, 99);
142     }
143     else
144     {
145         ok(0, "Expected Marlett font heights 10/8, got %d/%d\n",
146            font_height(hFont1), font_height(hFont2));
147     }
148
149     for (i = 1; i < 30; i++)
150     {
151         HFONT hFont = CreateFont(i, 0, 0, 0, FW_DONTCARE, FALSE, FALSE, FALSE, SYMBOL_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH|FF_DONTCARE, "Marlett");
152         int height = font_height(hFont);
153
154         SendMessage(hCombo, WM_SETFONT, (WPARAM)hFont, FALSE);
155         GetClientRect(hCombo, &r);
156         expect_eq(r.bottom - r.top, height + 8, int, "%d");
157         SendMessage(hCombo, WM_SETFONT, 0, FALSE);
158         DeleteObject(hFont);
159     }
160
161     DestroyWindow(hCombo);
162     DeleteObject(hFont1);
163     DeleteObject(hFont2);
164 }
165
166 static LRESULT (CALLBACK *old_parent_proc)(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam);
167 static LPCSTR expected_edit_text;
168 static LPCSTR expected_list_text;
169 static BOOL selchange_fired;
170
171 static LRESULT CALLBACK parent_wnd_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
172 {
173     switch (msg)
174     {
175     case WM_COMMAND:
176         switch (wparam)
177         {
178             case MAKEWPARAM(COMBO_ID, CBN_SELCHANGE):
179             {
180                 HWND hCombo = (HWND)lparam;
181                 int idx;
182                 char list[20], edit[20];
183
184                 memset(list, 0, sizeof(list));
185                 memset(edit, 0, sizeof(edit));
186
187                 idx = SendMessage(hCombo, CB_GETCURSEL, 0, 0);
188                 SendMessage(hCombo, CB_GETLBTEXT, idx, (LPARAM)list);
189                 SendMessage(hCombo, WM_GETTEXT, sizeof(edit), (LPARAM)edit);
190
191                 ok(!strcmp(edit, expected_edit_text), "edit: got %s, expected %s\n",
192                    edit, expected_edit_text);
193                 ok(!strcmp(list, expected_list_text), "list: got %s, expected %s\n",
194                    list, expected_list_text);
195
196                 selchange_fired = TRUE;
197             }
198             break;
199         }
200         break;
201     }
202
203     return CallWindowProc(old_parent_proc, hwnd, msg, wparam, lparam);
204 }
205
206 static void test_selection(DWORD style, const char * const text[],
207                            const int *edit, const int *list)
208 {
209     INT idx;
210     HWND hCombo;
211
212     hCombo = build_combo(style);
213
214     SendMessage(hCombo, CB_ADDSTRING, 0, (LPARAM)text[0]);
215     SendMessage(hCombo, CB_ADDSTRING, 0, (LPARAM)text[1]);
216     SendMessage(hCombo, CB_SETCURSEL, -1, 0);
217
218     old_parent_proc = (void *)SetWindowLongPtr(hMainWnd, GWLP_WNDPROC, (ULONG_PTR)parent_wnd_proc);
219
220     idx = SendMessage(hCombo, CB_GETCURSEL, 0, 0);
221     ok(idx == -1, "expected selection -1, got %d\n", idx);
222
223     /* keyboard navigation */
224
225     expected_list_text = text[list[0]];
226     expected_edit_text = text[edit[0]];
227     selchange_fired = FALSE;
228     SendMessage(hCombo, WM_KEYDOWN, VK_DOWN, 0);
229     ok(selchange_fired, "CBN_SELCHANGE not sent!\n");
230
231     expected_list_text = text[list[1]];
232     expected_edit_text = text[edit[1]];
233     selchange_fired = FALSE;
234     SendMessage(hCombo, WM_KEYDOWN, VK_DOWN, 0);
235     ok(selchange_fired, "CBN_SELCHANGE not sent!\n");
236
237     expected_list_text = text[list[2]];
238     expected_edit_text = text[edit[2]];
239     selchange_fired = FALSE;
240     SendMessage(hCombo, WM_KEYDOWN, VK_UP, 0);
241     ok(selchange_fired, "CBN_SELCHANGE not sent!\n");
242
243     /* programmatic navigation */
244
245     expected_list_text = text[list[3]];
246     expected_edit_text = text[edit[3]];
247     selchange_fired = FALSE;
248     SendMessage(hCombo, CB_SETCURSEL, list[3], 0);
249     ok(!selchange_fired, "CBN_SELCHANGE sent!\n");
250
251     expected_list_text = text[list[4]];
252     expected_edit_text = text[edit[4]];
253     selchange_fired = FALSE;
254     SendMessage(hCombo, CB_SETCURSEL, list[4], 0);
255     ok(!selchange_fired, "CBN_SELCHANGE sent!\n");
256
257     SetWindowLongPtr(hMainWnd, GWLP_WNDPROC, (ULONG_PTR)old_parent_proc);
258     DestroyWindow(hCombo);
259 }
260
261 static void test_CBN_SELCHANGE(void)
262 {
263     static const char * const text[] = { "alpha", "beta", "" };
264     static const int sel_1[] = { 2, 0, 1, 0, 1 };
265     static const int sel_2[] = { 0, 1, 0, 0, 1 };
266
267     test_selection(CBS_SIMPLE, text, sel_1, sel_2);
268     test_selection(CBS_DROPDOWN, text, sel_1, sel_2);
269     test_selection(CBS_DROPDOWNLIST, text, sel_2, sel_2);
270 }
271
272 static void test_WM_LBUTTONDOWN(void)
273 {
274     HWND hCombo, hEdit, hList;
275     COMBOBOXINFO cbInfo;
276     UINT x, y, item_height;
277     LRESULT result;
278     int i, idx;
279     RECT rect;
280     CHAR buffer[3];
281     static const UINT choices[] = {8,9,10,11,12,14,16,18,20,22,24,26,28,36,48,72};
282     static const CHAR stringFormat[] = "%2d";
283     BOOL ret;
284     BOOL (WINAPI *pGetComboBoxInfo)(HWND, PCOMBOBOXINFO);
285
286     pGetComboBoxInfo = (void*)GetProcAddress(GetModuleHandleA("user32.dll"), "GetComboBoxInfo");
287     if (!pGetComboBoxInfo){
288         win_skip("GetComboBoxInfo is not available\n");
289         return;
290     }
291
292     hCombo = CreateWindow("ComboBox", "Combo", WS_VISIBLE|WS_CHILD|CBS_DROPDOWN,
293             0, 0, 200, 150, hMainWnd, (HMENU)COMBO_ID, NULL, 0);
294
295     for (i = 0; i < sizeof(choices)/sizeof(UINT); i++){
296         sprintf(buffer, stringFormat, choices[i]);
297         result = SendMessageA(hCombo, CB_ADDSTRING, 0, (LPARAM)buffer);
298         ok(result == i,
299            "Failed to add item %d\n", i);
300     }
301
302     cbInfo.cbSize = sizeof(COMBOBOXINFO);
303     SetLastError(0xdeadbeef);
304     ret = pGetComboBoxInfo(hCombo, &cbInfo);
305     ok(ret, "Failed to get combobox info structure. LastError=%d\n",
306        GetLastError());
307     hEdit = cbInfo.hwndItem;
308     hList = cbInfo.hwndList;
309
310     trace("hMainWnd=%p, hCombo=%p, hList=%p, hEdit=%p\n", hMainWnd, hCombo, hList, hEdit);
311     ok(GetFocus() == hMainWnd, "Focus not on Main Window, instead on %p\n", GetFocus());
312
313     /* Click on the button to drop down the list */
314     x = cbInfo.rcButton.left + (cbInfo.rcButton.right-cbInfo.rcButton.left)/2;
315     y = cbInfo.rcButton.top + (cbInfo.rcButton.bottom-cbInfo.rcButton.top)/2;
316     result = SendMessage(hCombo, WM_LBUTTONDOWN, 0, MAKELPARAM(x, y));
317     ok(result, "WM_LBUTTONDOWN was not processed. LastError=%d\n",
318        GetLastError());
319     ok(SendMessage(hCombo, CB_GETDROPPEDSTATE, 0, 0),
320        "The dropdown list should have appeared after clicking the button.\n");
321
322     ok(GetFocus() == hEdit,
323        "Focus not on ComboBox's Edit Control, instead on %p\n", GetFocus());
324     result = SendMessage(hCombo, WM_LBUTTONUP, 0, MAKELPARAM(x, y));
325     ok(result, "WM_LBUTTONUP was not processed. LastError=%d\n",
326        GetLastError());
327     ok(GetFocus() == hEdit,
328        "Focus not on ComboBox's Edit Control, instead on %p\n", GetFocus());
329
330     /* Click on the 5th item in the list */
331     item_height = SendMessage(hCombo, CB_GETITEMHEIGHT, 0, 0);
332     ok(GetClientRect(hList, &rect), "Failed to get list's client rect.\n");
333     x = rect.left + (rect.right-rect.left)/2;
334     y = item_height/2 + item_height*4;
335     result = SendMessage(hList, WM_LBUTTONDOWN, 0, MAKELPARAM(x, y));
336     ok(!result, "WM_LBUTTONDOWN was not processed. LastError=%d\n",
337        GetLastError());
338     ok(GetFocus() == hEdit,
339        "Focus not on ComboBox's Edit Control, instead on %p\n", GetFocus());
340
341     result = SendMessage(hList, WM_MOUSEMOVE, 0, MAKELPARAM(x, y));
342     ok(!result, "WM_MOUSEMOVE was not processed. LastError=%d\n",
343        GetLastError());
344     ok(GetFocus() == hEdit,
345        "Focus not on ComboBox's Edit Control, instead on %p\n", GetFocus());
346     ok(SendMessage(hCombo, CB_GETDROPPEDSTATE, 0, 0),
347        "The dropdown list should still be visible.\n");
348
349     result = SendMessage(hList, WM_LBUTTONUP, 0, MAKELPARAM(x, y));
350     ok(!result, "WM_LBUTTONUP was not processed. LastError=%d\n",
351        GetLastError());
352     ok(GetFocus() == hEdit,
353        "Focus not on ComboBox's Edit Control, instead on %p\n", GetFocus());
354     ok(!SendMessage(hCombo, CB_GETDROPPEDSTATE, 0, 0),
355        "The dropdown list should have been rolled up.\n");
356     idx = SendMessage(hCombo, CB_GETCURSEL, 0, 0);
357     ok(idx, "Current Selection: expected %d, got %d\n", 4, idx);
358
359     DestroyWindow(hCombo);
360 }
361
362 static void test_changesize( DWORD style)
363 {
364     HWND hCombo = build_combo(style);
365     RECT rc;
366     INT ddheight, clheight, ddwidth, clwidth;
367     /* get initial measurements */
368     GetClientRect( hCombo, &rc);
369     clheight = rc.bottom - rc.top;
370     clwidth = rc.right - rc.left;
371     SendMessageA(hCombo, CB_GETDROPPEDCONTROLRECT, 0, (LPARAM)&rc);
372     ddheight = rc.bottom - rc.top;
373     ddwidth = rc.right - rc.left;
374     /* use MoveWindow to move & resize the combo */
375     /* first make it slightly smaller */
376     MoveWindow( hCombo, 10, 10, clwidth - 2, clheight - 2, TRUE);
377     GetClientRect( hCombo, &rc);
378     ok( rc.right - rc.left == clwidth - 2, "clientrect witdh is %d vs %d\n",
379             rc.right - rc.left, clwidth - 2);
380     ok( rc.bottom - rc.top == clheight, "clientrect height is %d vs %d\n",
381                 rc.bottom - rc.top, clheight);
382     SendMessageA(hCombo, CB_GETDROPPEDCONTROLRECT, 0, (LPARAM)&rc);
383     ok( rc.right - rc.left == clwidth - 2, "drop-down rect witdh is %d vs %d\n",
384             rc.right - rc.left, clwidth - 2);
385     ok( rc.bottom - rc.top == ddheight, "drop-down rect height is %d vs %d\n",
386             rc.bottom - rc.top, ddheight);
387     /* new cx, cy is slightly bigger than the initial values */
388     MoveWindow( hCombo, 10, 10, clwidth + 2, clheight + 2, TRUE);
389     GetClientRect( hCombo, &rc);
390     ok( rc.right - rc.left == clwidth + 2, "clientrect witdh is %d vs %d\n",
391             rc.right - rc.left, clwidth + 2);
392     ok( rc.bottom - rc.top == clheight, "clientrect height is %d vs %d\n",
393             rc.bottom - rc.top, clheight);
394     SendMessageA(hCombo, CB_GETDROPPEDCONTROLRECT, 0, (LPARAM)&rc);
395     ok( rc.right - rc.left == clwidth + 2, "drop-down rect witdh is %d vs %d\n",
396             rc.right - rc.left, clwidth + 2);
397     todo_wine {
398         ok( rc.bottom - rc.top == clheight + 2, "drop-down rect height is %d vs %d\n",
399                 rc.bottom - rc.top, clheight + 2);
400     }
401     DestroyWindow(hCombo);
402 }
403
404 START_TEST(combo)
405 {
406     hMainWnd = CreateWindow("static", "Test", WS_OVERLAPPEDWINDOW, 10, 10, 300, 300, NULL, NULL, NULL, 0);
407     ShowWindow(hMainWnd, SW_SHOW);
408
409     test_setfont(CBS_DROPDOWN);
410     test_setfont(CBS_DROPDOWNLIST);
411     test_setitemheight(CBS_DROPDOWN);
412     test_setitemheight(CBS_DROPDOWNLIST);
413     test_CBN_SELCHANGE();
414     test_WM_LBUTTONDOWN();
415     test_changesize(CBS_DROPDOWN);
416     test_changesize(CBS_DROPDOWNLIST);
417
418     DestroyWindow(hMainWnd);
419 }