Added a few more Unicode digits from Unicode version 4.1.
[wine] / dlls / user / tests / menu.c
1 /*
2  * Unit tests for menus
3  *
4  * Copyright 2005 Robert Shearman
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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  */
20
21 #define NONAMELESSUNION
22 #define NONAMELESSSTRUCT
23
24 #include <stdlib.h>
25 #include <stdarg.h>
26 #include <assert.h>
27
28 #include "windef.h"
29 #include "winbase.h"
30 #include "wingdi.h"
31 #include "winuser.h"
32
33 #include "wine/test.h"
34
35 static ATOM atomMenuCheckClass;
36
37 static LRESULT WINAPI menu_check_wnd_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
38 {
39     switch (msg)
40     {
41     case WM_ENTERMENULOOP:
42         /* mark window as having entered menu loop */
43         SetWindowLongPtr(hwnd, GWLP_USERDATA, TRUE);
44         /* exit menu modal loop
45          * ( A SendMessage does not work on NT3.51 here ) */
46         return PostMessage(hwnd, WM_CANCELMODE, 0, 0);
47     }
48     return DefWindowProc(hwnd, msg, wparam, lparam);
49 }
50
51 /* globals to communicate between test and wndproc */
52 unsigned int MOD_maxid;
53 RECT MOD_rc[4];
54 int MOD_avec, MOD_hic;
55 int MOD_odheight;
56 #define MOD_SIZE 10
57 /* wndproc used by test_menu_ownerdraw() */
58 static LRESULT WINAPI menu_ownerdraw_wnd_proc(HWND hwnd, UINT msg,
59         WPARAM wparam, LPARAM lparam)
60 {
61     switch (msg)
62     {
63         case WM_MEASUREITEM:
64             {
65                 MEASUREITEMSTRUCT* pmis = (MEASUREITEMSTRUCT*)lparam;
66                 if( winetest_debug)
67                     trace("WM_MEASUREITEM received %d,%d\n",
68                             pmis->itemWidth, pmis->itemHeight);
69                 MOD_odheight = pmis->itemHeight;
70                 pmis->itemWidth = MOD_SIZE;
71                 pmis->itemHeight = MOD_SIZE;
72                 return TRUE;
73             }
74         case WM_DRAWITEM:
75             {
76                 DRAWITEMSTRUCT * pdis;
77                 TEXTMETRIC tm;
78                 HPEN oldpen;
79                 char chrs[]="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
80                 SIZE sz;
81                 pdis = (DRAWITEMSTRUCT *) lparam;
82                 if( winetest_debug) {
83                     trace("WM_DRAWITEM received itemdata %ld item %d rc %ld,%ld-%ld,%ld\n",
84                             pdis->itemData,
85                             pdis->itemID, pdis->rcItem.left, pdis->rcItem.top,
86                             pdis->rcItem.right,pdis->rcItem.bottom );
87                     oldpen=SelectObject( pdis->hDC, GetStockObject(
88                                 pdis->itemState & ODS_SELECTED ? WHITE_PEN :BLACK_PEN));
89                     Rectangle( pdis->hDC, pdis->rcItem.left,pdis->rcItem.top,
90                             pdis->rcItem.right,pdis->rcItem.bottom );
91                     SelectObject( pdis->hDC, oldpen);
92                 }
93                 if( pdis->itemData > MOD_maxid) return TRUE;
94                 /* store the rectangl */
95                 MOD_rc[pdis->itemData] = pdis->rcItem;
96                 /* calculate average character width */
97                 GetTextExtentPoint( pdis->hDC, chrs, 52, &sz );
98                 MOD_avec = (sz.cx + 26)/52;
99                 GetTextMetrics( pdis->hDC, &tm);
100                 MOD_hic = tm.tmHeight;
101                 if( pdis->itemData == MOD_maxid) PostMessage(hwnd, WM_CANCELMODE, 0, 0);
102                 return TRUE;
103             }
104
105     }
106     return DefWindowProc(hwnd, msg, wparam, lparam);
107 }
108
109 static void register_menu_check_class(void)
110 {
111     WNDCLASS wc =
112     {
113         0,
114         menu_check_wnd_proc,
115         0,
116         0,
117         GetModuleHandle(NULL),
118         NULL,
119         LoadCursor(NULL, IDC_ARROW),
120         (HBRUSH)(COLOR_BTNFACE+1),
121         NULL,
122         TEXT("WineMenuCheck"),
123     };
124     
125     atomMenuCheckClass = RegisterClass(&wc);
126 }
127
128 /* demonstrates that windows lock the menu object so that it is still valid
129  * even after a client calls DestroyMenu on it */
130 static void test_menu_locked_by_window(void)
131 {
132     BOOL ret;
133     HMENU hmenu;
134     HWND hwnd = CreateWindowEx(0, MAKEINTATOM(atomMenuCheckClass), NULL,
135         WS_VISIBLE, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
136         NULL, NULL, NULL, NULL);
137     ok(hwnd != NULL, "CreateWindowEx failed with error %ld\n", GetLastError());
138     hmenu = CreateMenu();
139     ok(hmenu != NULL, "CreateMenu failed with error %ld\n", GetLastError());
140     ret = InsertMenu(hmenu, 0, MF_STRING, 0, TEXT("&Test"));
141     ok(ret, "InsertMenu failed with error %ld\n", GetLastError());
142     ret = SetMenu(hwnd, hmenu);
143     ok(ret, "SetMenu failed with error %ld\n", GetLastError());
144     ret = DestroyMenu(hmenu);
145     ok(ret, "DestroyMenu failed with error %ld\n", GetLastError());
146
147     ret = DrawMenuBar(hwnd);
148     todo_wine {
149     ok(ret, "DrawMenuBar failed with error %ld\n", GetLastError());
150     }
151     ret = IsMenu(GetMenu(hwnd));
152     ok(!ret, "Menu handle should have been destroyed\n");
153
154     SendMessage(hwnd, WM_SYSCOMMAND, SC_KEYMENU, 0);
155     /* did we process the WM_INITMENU message? */
156     ret = GetWindowLongPtr(hwnd, GWLP_USERDATA);
157     todo_wine {
158     ok(ret, "WM_INITMENU should have been sent\n");
159     }
160
161     DestroyWindow(hwnd);
162 }
163
164 static void test_menu_ownerdraw(void)
165 {
166     int i,j,k;
167     BOOL ret;
168     HMENU hmenu;
169     LONG leftcol;
170     HWND hwnd = CreateWindowEx(0, MAKEINTATOM(atomMenuCheckClass), NULL,
171             WS_VISIBLE, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
172             NULL, NULL, NULL, NULL);
173     ok(hwnd != NULL, "CreateWindowEx failed with error %ld\n", GetLastError());
174     if( !hwnd) return;
175     SetWindowLongPtr( hwnd, GWLP_WNDPROC, (LONG)menu_ownerdraw_wnd_proc);
176     hmenu = CreatePopupMenu();
177     ok(hmenu != NULL, "CreateMenu failed with error %ld\n", GetLastError());
178     if( !hmenu) { DestroyWindow(hwnd);return;}
179     k=0;
180     for( j=0;j<2;j++) /* create columns */
181         for(i=0;i<2;i++) { /* create rows */
182             ret = AppendMenu( hmenu, MF_OWNERDRAW | 
183                     (i==0 ? MF_MENUBREAK : 0), k, (LPCTSTR) k);
184             k++;
185             ok( ret, "AppendMenu failed for %d\n", k-1);
186         }
187     MOD_maxid = k-1;
188     assert( k <= sizeof(MOD_rc)/sizeof(RECT));
189     /* display the menu */
190     ret = TrackPopupMenu( hmenu, 0x100, 100,100, 0, hwnd, NULL);
191
192     /* columns have a 4 pixel gap between them */
193     ok( MOD_rc[0].right + 4 ==  MOD_rc[2].left,
194             "item rectangles are not separated by 4 pixels space\n");
195     /* height should be what the MEASUREITEM message has returned */
196     ok( MOD_rc[0].bottom - MOD_rc[0].top == MOD_SIZE,
197             "menu item has wrong height: %ld should be %d\n",
198             MOD_rc[0].bottom - MOD_rc[0].top, MOD_SIZE);
199     /* no gaps between the rows */
200     ok( MOD_rc[0].bottom - MOD_rc[1].top == 0,
201             "There should not be a space between the rows, gap is %ld\n",
202             MOD_rc[0].bottom - MOD_rc[1].top);
203     /* test the correct value of the item height that was sent
204      * by the WM_MEASUREITEM message */
205     ok( MOD_odheight == HIWORD( GetDialogBaseUnits()) || /* WinNT,2k,XP */
206             MOD_odheight == MOD_hic,                     /* Win95,98,ME */
207             "Wrong height field in MEASUREITEMSTRUCT, expected %d or %d actual %d\n",
208             HIWORD( GetDialogBaseUnits()), MOD_hic, MOD_odheight);
209     /* test what MF_MENUBREAK did at the first position. Also show
210      * that an MF_SEPARATOR is ignored in the height calculation. */
211     leftcol= MOD_rc[0].left;
212     ModifyMenu( hmenu, 0, MF_BYCOMMAND| MF_OWNERDRAW| MF_SEPARATOR, 0, 0); 
213     /* display the menu */
214     ret = TrackPopupMenu( hmenu, 0x100, 100,100, 0, hwnd, NULL);
215     /* left should be 4 pixels less now */
216     ok( leftcol == MOD_rc[0].left + 4, 
217             "columns should be 4 pixels to the left (actual %ld).\n",
218             leftcol - MOD_rc[0].left);
219     /* test width */
220     ok( MOD_rc[0].right - MOD_rc[0].left == 2 * MOD_avec + MOD_SIZE,
221             "width of owner drawn menu item is wrong. Got %ld expected %d\n",
222             MOD_rc[0].right - MOD_rc[0].left , 2*MOD_avec + MOD_SIZE);
223     /* and height */
224     ok( MOD_rc[0].bottom - MOD_rc[0].top == MOD_SIZE,
225             "Height is incorrect. Got %ld expected %d\n",
226             MOD_rc[0].bottom - MOD_rc[0].top, MOD_SIZE);
227
228     /* test width/height of a OD menu bar as well */
229     ret = DestroyMenu(hmenu);
230     ok(ret, "DestroyMenu failed with error %ld\n", GetLastError());
231     hmenu = CreateMenu();
232     ok(hmenu != NULL, "CreateMenu failed with error %ld\n", GetLastError());
233     if( !hmenu) { DestroyWindow(hwnd);return;}
234     MOD_maxid=1;
235     for(i=0;i<2;i++) { 
236         ret = AppendMenu( hmenu, MF_OWNERDRAW , i, 0);
237         ok( ret, "AppendMenu failed for %d\n", i);
238     }
239     SetMenu( hwnd, hmenu);
240     UpdateWindow( hwnd); /* hack for wine to draw the window + menu */
241     ok(ret, "SetMenu failed with error %ld\n", GetLastError());
242     /* test width */
243     ok( MOD_rc[0].right - MOD_rc[0].left == 2 * MOD_avec + MOD_SIZE,
244             "width of owner drawn menu item is wrong. Got %ld expected %d\n",
245             MOD_rc[0].right - MOD_rc[0].left , 2*MOD_avec + MOD_SIZE);
246     /* test hight */
247     ok( MOD_rc[0].bottom - MOD_rc[0].top == GetSystemMetrics( SM_CYMENU) - 1,
248             "Height of owner drawn menu item is wrong. Got %ld expected %d\n",
249             MOD_rc[0].bottom - MOD_rc[0].top, GetSystemMetrics( SM_CYMENU) - 1);
250     /* clean up */
251     DestroyWindow(hwnd);
252 }
253
254 static void test_menu_add_string( void )
255 {
256     MENUITEMINFO info;
257     char string[0x80];
258     HMENU hmenu;
259
260     hmenu = CreateMenu();
261
262     memset( &info, 0, sizeof info );
263     info.cbSize = sizeof info;
264     info.fMask = MIIM_FTYPE | MIIM_STRING | MIIM_STATE | MIIM_ID;
265     info.dwTypeData = "blah";
266     info.cch = 6;
267     info.dwItemData = 0;
268     info.wID = 1;
269     info.fState = 0;
270     InsertMenuItem(hmenu, 0, TRUE, &info );
271
272     memset( &info, 0, sizeof info );
273     info.cbSize = sizeof info;
274     info.fMask = MIIM_FTYPE | MIIM_STRING | MIIM_STATE | MIIM_DATA | MIIM_ID;
275     info.dwTypeData = string;
276     info.cch = sizeof string;
277     string[0] = 0;
278     GetMenuItemInfo( hmenu, 0, TRUE, &info );
279
280     todo_wine {
281     ok( !strcmp( string, "blah" ), "menu item name differed\n");
282     }
283
284     DestroyMenu( hmenu );
285 }
286
287
288 START_TEST(menu)
289 {
290     register_menu_check_class();
291
292     test_menu_locked_by_window();
293     test_menu_ownerdraw();
294     test_menu_add_string();
295 }