| /* |
| * Unit tests for menus |
| * |
| * Copyright 2005 Robert Shearman |
| * |
| * This library is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU Lesser General Public |
| * License as published by the Free Software Foundation; either |
| * version 2.1 of the License, or (at your option) any later version. |
| * |
| * This library is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| * Lesser General Public License for more details. |
| * |
| * You should have received a copy of the GNU Lesser General Public |
| * License along with this library; if not, write to the Free Software |
| * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
| */ |
| |
| #define NONAMELESSUNION |
| #define NONAMELESSSTRUCT |
| |
| #include <stdlib.h> |
| #include <stdarg.h> |
| #include <assert.h> |
| |
| #include "windef.h" |
| #include "winbase.h" |
| #include "wingdi.h" |
| #include "winuser.h" |
| |
| #include "wine/test.h" |
| |
| static ATOM atomMenuCheckClass; |
| |
| static LRESULT WINAPI menu_check_wnd_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) |
| { |
| switch (msg) |
| { |
| case WM_ENTERMENULOOP: |
| /* mark window as having entered menu loop */ |
| SetWindowLongPtr(hwnd, GWLP_USERDATA, TRUE); |
| /* exit menu modal loop |
| * ( A SendMessage does not work on NT3.51 here ) */ |
| return PostMessage(hwnd, WM_CANCELMODE, 0, 0); |
| } |
| return DefWindowProc(hwnd, msg, wparam, lparam); |
| } |
| |
| /* globals to communicate between test and wndproc */ |
| unsigned int MOD_maxid; |
| RECT MOD_rc[4]; |
| int MOD_avec, MOD_hic; |
| int MOD_odheight; |
| #define MOD_SIZE 10 |
| /* wndproc used by test_menu_ownerdraw() */ |
| static LRESULT WINAPI menu_ownerdraw_wnd_proc(HWND hwnd, UINT msg, |
| WPARAM wparam, LPARAM lparam) |
| { |
| switch (msg) |
| { |
| case WM_MEASUREITEM: |
| { |
| MEASUREITEMSTRUCT* pmis = (MEASUREITEMSTRUCT*)lparam; |
| if( winetest_debug) |
| trace("WM_MEASUREITEM received %d,%d\n", |
| pmis->itemWidth, pmis->itemHeight); |
| MOD_odheight = pmis->itemHeight; |
| pmis->itemWidth = MOD_SIZE; |
| pmis->itemHeight = MOD_SIZE; |
| return TRUE; |
| } |
| case WM_DRAWITEM: |
| { |
| DRAWITEMSTRUCT * pdis; |
| TEXTMETRIC tm; |
| HPEN oldpen; |
| char chrs[]="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; |
| SIZE sz; |
| pdis = (DRAWITEMSTRUCT *) lparam; |
| if( winetest_debug) { |
| trace("WM_DRAWITEM received itemdata %ld item %d rc %ld,%ld-%ld,%ld \n", |
| pdis->itemData, |
| pdis->itemID, pdis->rcItem.left, pdis->rcItem.top, |
| pdis->rcItem.right,pdis->rcItem.bottom ); |
| oldpen=SelectObject( pdis->hDC, GetStockObject( |
| pdis->itemState & ODS_SELECTED ? WHITE_PEN :BLACK_PEN)); |
| Rectangle( pdis->hDC, pdis->rcItem.left,pdis->rcItem.top, |
| pdis->rcItem.right,pdis->rcItem.bottom ); |
| SelectObject( pdis->hDC, oldpen); |
| } |
| if( pdis->itemData > MOD_maxid) return TRUE; |
| /* store the rectangl */ |
| MOD_rc[pdis->itemData] = pdis->rcItem; |
| /* calculate average character width */ |
| GetTextExtentPoint( pdis->hDC, chrs, 52, &sz ); |
| MOD_avec = (sz.cx + 26)/52; |
| GetTextMetrics( pdis->hDC, &tm); |
| MOD_hic = tm.tmHeight; |
| if( pdis->itemData == MOD_maxid) PostMessage(hwnd, WM_CANCELMODE, 0, 0); |
| return TRUE; |
| } |
| |
| } |
| return DefWindowProc(hwnd, msg, wparam, lparam); |
| } |
| |
| static void register_menu_check_class(void) |
| { |
| WNDCLASS wc = |
| { |
| 0, |
| menu_check_wnd_proc, |
| 0, |
| 0, |
| GetModuleHandle(NULL), |
| NULL, |
| LoadCursor(NULL, IDC_ARROW), |
| (HBRUSH)(COLOR_BTNFACE+1), |
| NULL, |
| TEXT("WineMenuCheck"), |
| }; |
| |
| atomMenuCheckClass = RegisterClass(&wc); |
| } |
| |
| /* demonstrates that windows lock the menu object so that it is still valid |
| * even after a client calls DestroyMenu on it */ |
| static void test_menu_locked_by_window(void) |
| { |
| BOOL ret; |
| HMENU hmenu; |
| HWND hwnd = CreateWindowEx(0, MAKEINTATOM(atomMenuCheckClass), NULL, |
| WS_VISIBLE, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, |
| NULL, NULL, NULL, NULL); |
| ok(hwnd != NULL, "CreateWindowEx failed with error %ld\n", GetLastError()); |
| hmenu = CreateMenu(); |
| ok(hmenu != NULL, "CreateMenu failed with error %ld\n", GetLastError()); |
| ret = InsertMenu(hmenu, 0, MF_STRING, 0, TEXT("&Test")); |
| ok(ret, "InsertMenu failed with error %ld\n", GetLastError()); |
| ret = SetMenu(hwnd, hmenu); |
| ok(ret, "SetMenu failed with error %ld\n", GetLastError()); |
| ret = DestroyMenu(hmenu); |
| ok(ret, "DestroyMenu failed with error %ld\n", GetLastError()); |
| |
| ret = DrawMenuBar(hwnd); |
| todo_wine { |
| ok(ret, "DrawMenuBar failed with error %ld\n", GetLastError()); |
| } |
| ret = IsMenu(GetMenu(hwnd)); |
| ok(!ret, "Menu handle should have been destroyed\n"); |
| |
| SendMessage(hwnd, WM_SYSCOMMAND, SC_KEYMENU, 0); |
| /* did we process the WM_INITMENU message? */ |
| ret = GetWindowLongPtr(hwnd, GWLP_USERDATA); |
| todo_wine { |
| ok(ret, "WM_INITMENU should have been sent\n"); |
| } |
| |
| DestroyWindow(hwnd); |
| } |
| |
| static void test_menu_ownerdraw(void) |
| { |
| int i,j,k; |
| BOOL ret; |
| HMENU hmenu; |
| LONG leftcol; |
| HWND hwnd = CreateWindowEx(0, MAKEINTATOM(atomMenuCheckClass), NULL, |
| WS_VISIBLE, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, |
| NULL, NULL, NULL, NULL); |
| ok(hwnd != NULL, "CreateWindowEx failed with error %ld\n", GetLastError()); |
| if( !hwnd) return; |
| SetWindowLong( hwnd, GWL_WNDPROC, (LONG)menu_ownerdraw_wnd_proc); |
| hmenu = CreatePopupMenu(); |
| ok(hmenu != NULL, "CreateMenu failed with error %ld\n", GetLastError()); |
| if( !hmenu) { DestroyWindow(hwnd);return;} |
| k=0; |
| for( j=0;j<2;j++) /* create columns */ |
| for(i=0;i<2;i++) { /* create rows */ |
| ret = AppendMenu( hmenu, MF_OWNERDRAW | |
| (i==0 ? MF_MENUBREAK : 0), k, (LPCTSTR) k); |
| k++; |
| ok( ret, "AppendMenu failed for %d\n", k-1); |
| } |
| MOD_maxid = k-1; |
| assert( k <= sizeof(MOD_rc)/sizeof(RECT)); |
| /* display the menu */ |
| ret = TrackPopupMenu( hmenu, 0x100, 100,100, 0, hwnd, NULL); |
| |
| /* columns have a 4 pixel gap between them */ |
| ok( MOD_rc[0].right + 4 == MOD_rc[2].left, |
| "item rectangles are not separated by 4 pixels space\n"); |
| /* height should be what the MEASUREITEM message has returned */ |
| ok( MOD_rc[0].bottom - MOD_rc[0].top == MOD_SIZE, |
| "menu item has wrong height: %ld should be %d\n", |
| MOD_rc[0].bottom - MOD_rc[0].top, MOD_SIZE); |
| /* no gaps between the rows */ |
| ok( MOD_rc[0].bottom - MOD_rc[1].top == 0, |
| "There should not be a space between the rows, gap is %ld\n", |
| MOD_rc[0].bottom - MOD_rc[1].top); |
| /* test the correct value of the item height that was sent |
| * by the WM_MEASUREITEM message */ |
| ok( MOD_odheight == HIWORD( GetDialogBaseUnits()) || /* WinNT,2k,XP */ |
| MOD_odheight == MOD_hic, /* Win95,98,ME */ |
| "Wrong height field in MEASUREITEMSTRUCT, expected %d or %d actual %d\n", |
| HIWORD( GetDialogBaseUnits()), MOD_hic, MOD_odheight); |
| /* test what MF_MENUBREAK did at the first position. Also show |
| * that an MF_SEPARATOR is ignored in the height calculation. */ |
| leftcol= MOD_rc[0].left; |
| ModifyMenu( hmenu, 0, MF_BYCOMMAND| MF_OWNERDRAW| MF_SEPARATOR, 0, 0); |
| /* display the menu */ |
| ret = TrackPopupMenu( hmenu, 0x100, 100,100, 0, hwnd, NULL); |
| /* left should be 4 pixels less now */ |
| ok( leftcol == MOD_rc[0].left + 4, |
| "columns should be 4 pixels to the left (actual %ld).\n", |
| leftcol - MOD_rc[0].left); |
| /* test width */ |
| ok( MOD_rc[0].right - MOD_rc[0].left == 2 * MOD_avec + MOD_SIZE, |
| "width of owner drawn menu item is wrong. Got %ld expected %d\n", |
| MOD_rc[0].right - MOD_rc[0].left , 2*MOD_avec + MOD_SIZE); |
| /* and height */ |
| ok( MOD_rc[0].bottom - MOD_rc[0].top == MOD_SIZE, |
| "Height is incorrect. Got %ld expected %d\n", |
| MOD_rc[0].bottom - MOD_rc[0].top, MOD_SIZE); |
| |
| /* test width/height of a OD menu bar as well */ |
| ret = DestroyMenu(hmenu); |
| ok(ret, "DestroyMenu failed with error %ld\n", GetLastError()); |
| hmenu = CreateMenu(); |
| ok(hmenu != NULL, "CreateMenu failed with error %ld\n", GetLastError()); |
| if( !hmenu) { DestroyWindow(hwnd);return;} |
| MOD_maxid=1; |
| for(i=0;i<2;i++) { |
| ret = AppendMenu( hmenu, MF_OWNERDRAW , i, 0); |
| ok( ret, "AppendMenu failed for %d\n", i); |
| } |
| SetMenu( hwnd, hmenu); |
| UpdateWindow( hwnd); /* hack for wine to draw the window + menu */ |
| ok(ret, "SetMenu failed with error %ld\n", GetLastError()); |
| /* test width */ |
| ok( MOD_rc[0].right - MOD_rc[0].left == 2 * MOD_avec + MOD_SIZE, |
| "width of owner drawn menu item is wrong. Got %ld expected %d\n", |
| MOD_rc[0].right - MOD_rc[0].left , 2*MOD_avec + MOD_SIZE); |
| /* test hight */ |
| ok( MOD_rc[0].bottom - MOD_rc[0].top == GetSystemMetrics( SM_CYMENU) - 1, |
| "Height of owner drawn menu item is wrong. Got %ld expected %d\n", |
| MOD_rc[0].bottom - MOD_rc[0].top, GetSystemMetrics( SM_CYMENU) - 1); |
| /* clean up */ |
| DestroyWindow(hwnd); |
| } |
| |
| START_TEST(menu) |
| { |
| register_menu_check_class(); |
| |
| test_menu_locked_by_window(); |
| test_menu_ownerdraw(); |
| } |