menu drawing fixes

Rein Klazes wijn at wanadoo.nl
Sat Feb 4 05:31:42 CST 2006


Hi,

Changelog:
dlls/user		: menu.c
dlls/user/tests	: menu.c

Updating drawing of menu items with bitmaps and/or strings:
- implemented MNS_NOCHECK menu style;
- support HBMMENU_CALLBACK bitmaps in menu bars;
- replaced some #defined constants by values calculated from windows
metrics;
- added a lot of tests on menu item sizes and the position of the bitmap
and making them pass on Wine.

Rein.
-------------- next part --------------
--- wine/dlls/user/menu.c	2006-01-25 14:28:21.000000000 +0100
+++ mywine/dlls/user/menu.c	2006-02-02 16:44:35.000000000 +0100
@@ -26,12 +26,16 @@
  * have been selected, i.e. their popup menu is currently displayed.
  * This is probably not the meaning this style has in MS-Windows.
  *
+ * Note 2: where there is a difference, these menu API's are according
+ * the behavior of Windows 2k and Windows XP. Known differences with
+ * Windows 9x/ME are documented in the comments, in case an application
+ * is found to depend on the old behavior.
+ * 
  * TODO:
  *    implements styles :
  *        - MNS_AUTODISMISS
  *        - MNS_DRAGDROP
  *        - MNS_MODELESS
- *        - MNS_NOCHECK
  *        - MNS_NOTIFYBYPOS
  */
 
@@ -79,6 +83,8 @@ typedef struct {
     /* ----------- Wine stuff ----------- */
     RECT      rect;		/* Item area (relative to menu window) */
     UINT      xTab;		/* X position of text after Tab */
+    SIZE   bmpsize;             /* size needed for the HBMMENU_CALLBACK
+                                 * bitmap */ 
 } MENUITEM;
 
 /* Popup menu structure */
@@ -100,7 +106,7 @@ typedef struct {
     DWORD	dwContextHelpID;
     DWORD	dwMenuData;	/* application defined value */
     HMENU       hSysMenuOwner;  /* Handle to the dummy sys menu holder */
-    SIZE        maxBmpSize;     /* Maximum size of the bitmap items in MIIM_BITMAP state */
+    SIZE        maxBmpSize;     /* Maximum size of the bitmap items */
 } POPUPMENU, *LPPOPUPMENU;
 
 /* internal flags for menu tracking */
@@ -129,15 +135,6 @@ typedef struct
 #define TPM_BUTTONDOWN		0x40000000		/* menu was clicked before tracking */
 #define TPM_POPUPMENU           0x20000000              /* menu is a popup menu */
 
-  /* Space between 2 menu bar items */
-#define MENU_BAR_ITEMS_SPACE 12
-
-  /* Minimum width of a tab character */
-#define MENU_TAB_SPACE 8
-
-  /* Height of a separator item */
-#define SEPARATOR_HEIGHT 5
-
   /* Space between 2 columns */
 #define MENU_COL_SPACE 4
 
@@ -147,6 +144,7 @@ typedef struct
 #define MENU_ITEM_TYPE(flags) \
   ((flags) & (MF_STRING | MF_BITMAP | MF_OWNERDRAW | MF_SEPARATOR))
 
+/* macro to test that flags do not indicate bitmap, ownerdraw or separator */
 #define IS_STRING_ITEM(flags) (MENU_ITEM_TYPE ((flags)) == MF_STRING)
 #define IS_MAGIC_BITMAP(id)     ((id) && ((INT_PTR)(id) < 12) && ((INT_PTR)(id) >= -1))
 
@@ -688,19 +686,36 @@ static UINT MENU_FindItemByKey( HWND hwn
  *
  * Get the size of a bitmap item.
  */
-static void MENU_GetBitmapItemSize( HBITMAP bmp, DWORD data, SIZE *size )
+static void MENU_GetBitmapItemSize( MENUITEM *lpitem, SIZE *size,
+                                    HWND hwndOwner)
 {
     BITMAP bm;
+    HBITMAP bmp = lpitem->hbmpItem;
 
     size->cx = size->cy = 0;
 
     /* check if there is a magic menu item associated with this item */
-    switch( (INT_PTR)bmp )
+    switch( (INT_PTR) bmp )
     {
+    case (INT_PTR)HBMMENU_CALLBACK:
+        {
+            MEASUREITEMSTRUCT measItem;
+            measItem.CtlType = ODT_MENU;
+            measItem.CtlID = 0;
+            measItem.itemID = lpitem->wID;
+            measItem.itemWidth = lpitem->rect.right - lpitem->rect.left;
+            measItem.itemHeight = lpitem->rect.bottom - lpitem->rect.top;
+            measItem.itemData = lpitem->dwItemData;
+            SendMessageW( hwndOwner, WM_MEASUREITEM, lpitem->wID, (LPARAM)&measItem);
+            size->cx = measItem.itemWidth;
+            size->cy = measItem.itemHeight;
+            return;
+        }
+        break;
     case (INT_PTR)HBMMENU_SYSTEM:
-        if (data)
+        if (lpitem->dwItemData)
         {
-            bmp = (HBITMAP)data;
+            bmp = (HBITMAP)lpitem->dwItemData;
             break;
         }
         /* fall through */
@@ -712,7 +727,6 @@ static void MENU_GetBitmapItemSize( HBIT
         size->cx = GetSystemMetrics( SM_CYMENU ) - 4;
         size->cy = size->cx;
         return;
-    case (INT_PTR)HBMMENU_CALLBACK:
     case (INT_PTR)HBMMENU_POPUP_CLOSE:
     case (INT_PTR)HBMMENU_POPUP_RESTORE:
     case (INT_PTR)HBMMENU_POPUP_MAXIMIZE:
@@ -732,7 +746,8 @@ static void MENU_GetBitmapItemSize( HBIT
  *
  * Draw a bitmap item.
  */
-static void MENU_DrawBitmapItem( HDC hdc, MENUITEM *lpitem, const RECT *rect, BOOL menuBar)
+static void MENU_DrawBitmapItem( HDC hdc, MENUITEM *lpitem, const RECT *rect,
+                    HMENU hmenu, HWND hwndOwner, UINT odaction, BOOL menuBar)
 {
     BITMAP bm;
     DWORD rop;
@@ -787,6 +802,25 @@ static void MENU_DrawBitmapItem( HDC hdc
             flags = DFCS_CAPTIONCLOSE | DFCS_INACTIVE;
             break;
         case (INT_PTR)HBMMENU_CALLBACK:
+            {
+                DRAWITEMSTRUCT drawItem;
+                drawItem.CtlType = ODT_MENU;
+                drawItem.CtlID = 0;
+                drawItem.itemID = lpitem->wID;
+                drawItem.itemAction = odaction;
+                drawItem.itemState = (lpitem->fState & MF_CHECKED)?ODS_CHECKED:0;
+                drawItem.itemState |= (lpitem->fState & MF_DEFAULT)?ODS_DEFAULT:0;
+                drawItem.itemState |= (lpitem->fState & MF_DISABLED)?ODS_DISABLED:0;
+                drawItem.itemState |= (lpitem->fState & MF_GRAYED)?ODS_GRAYED|ODS_DISABLED:0;
+                drawItem.itemState |= (lpitem->fState & MF_HILITE)?ODS_SELECTED:0;
+                drawItem.hwndItem = (HWND)hmenu;
+                drawItem.hDC = hdc;
+                drawItem.itemData = lpitem->dwItemData;
+                drawItem.rcItem = *rect;
+                SendMessageW( hwndOwner, WM_DRAWITEM, 0, (LPARAM)&drawItem);
+                return;
+            }
+            break;
         case (INT_PTR)HBMMENU_POPUP_CLOSE:
         case (INT_PTR)HBMMENU_POPUP_RESTORE:
         case (INT_PTR)HBMMENU_POPUP_MAXIMIZE:
@@ -831,6 +865,7 @@ static void MENU_CalcItemSize( HDC hdc, 
     UINT check_bitmap_width = GetSystemMetrics( SM_CXMENUCHECK );
     UINT arrow_bitmap_width;
     BITMAP bm;
+    INT itemheight;
 
     TRACE("dc=%p owner=%p (%d,%d)\n", hdc, hwndOwner, orgX, orgY);
     debug_print_menuitem("MENU_CalcItemSize: menuitem:", lpitem,
@@ -839,18 +874,19 @@ static void MENU_CalcItemSize( HDC hdc, 
     GetObjectW( get_arrow_bitmap(), sizeof(bm), &bm );
     arrow_bitmap_width = bm.bmWidth;
 
+    /* not done in Menu_Init: GetDialogBaseUnits() breaks there */
+    if( !menucharsize.cx ) {
+        menucharsize.cx = GdiGetCharDimensions( hdc, NULL, &menucharsize.cy );
+        /* Win95/98/ME will use menucharsize.cy here. Testing is possible
+         * but it is unlikely an application will depend on that */
+        ODitemheight = HIWORD( GetDialogBaseUnits());
+    }
+
     SetRect( &lpitem->rect, orgX, orgY, orgX, orgY );
 
     if (lpitem->fType & MF_OWNERDRAW)
     {
         MEASUREITEMSTRUCT mis;
-        /* not done in Menu_Init: GetDialogBaseUnits() breaks there */
-        if( !menucharsize.cx ) {
-            menucharsize.cx = GdiGetCharDimensions( hdc, NULL, &menucharsize.cy );
-            /* Win95/98/ME will use menucharsize.cy here. Testing is possible
-             * but it is unlikely an application will depend on that */
-            ODitemheight = HIWORD( GetDialogBaseUnits());
-        }
         mis.CtlType    = ODT_MENU;
         mis.CtlID      = 0;
         mis.itemID     = lpitem->wID;
@@ -877,87 +913,92 @@ static void MENU_CalcItemSize( HDC hdc, 
 
     if (lpitem->fType & MF_SEPARATOR)
     {
-	lpitem->rect.bottom += SEPARATOR_HEIGHT;
-	return;
+        lpitem->rect.bottom += GetSystemMetrics( SM_CYMENUSIZE)/2;
+        if( !menuBar)
+            lpitem->rect.right += arrow_bitmap_width + menucharsize.cx;
+        return;
     }
 
-    if (!menuBar)
-    {
-	if (lpitem->hbmpItem) 
-	{
-	    if (lpitem->hbmpItem == HBMMENU_CALLBACK)
-	    {
-		MEASUREITEMSTRUCT measItem;
-		measItem.CtlType = ODT_MENU;
-		measItem.CtlID = 0;
-		measItem.itemID = lpitem->wID;
-		measItem.itemWidth = lpitem->rect.right - lpitem->rect.left;
-		measItem.itemHeight = lpitem->rect.bottom - lpitem->rect.top;
-		measItem.itemData = lpitem->dwItemData;
-		
-		SendMessageW( hwndOwner, WM_MEASUREITEM, lpitem->wID, (LPARAM)&measItem);
-	    
-		/* Keep the size of the bitmap in callback mode to be able to draw it correctly */
-		lppop->maxBmpSize.cx = max(lppop->maxBmpSize.cx, measItem.itemWidth - (lpitem->rect.right - lpitem->rect.left));
-		lppop->maxBmpSize.cy = max(lppop->maxBmpSize.cy, measItem.itemHeight - (lpitem->rect.bottom - lpitem->rect.top));
-		lpitem->rect.right = lpitem->rect.left + measItem.itemWidth;
-	    } else {
-		SIZE size;
-		MENU_GetBitmapItemSize(lpitem->hbmpItem, lpitem->dwItemData, &size);
-		lppop->maxBmpSize.cx = max(lppop->maxBmpSize.cx, size.cx);
-		lppop->maxBmpSize.cy = max(lppop->maxBmpSize.cy, size.cy);
-		lpitem->rect.right  += size.cx;
-		lpitem->rect.bottom += size.cy;
-	    }
-	    if (lppop->dwStyle & MNS_CHECKORBMP) 
-		lpitem->rect.right += check_bitmap_width;
-	    else
-		lpitem->rect.right += 2 * check_bitmap_width;
-	} else
-	    lpitem->rect.right += 2 * check_bitmap_width;
-	if (lpitem->fType & MF_POPUP)
-	    lpitem->rect.right += arrow_bitmap_width;
-    } else if (lpitem->hbmpItem)
-    {
+    itemheight = 0;
+
+    if (!menuBar) {
+        if (lpitem->hbmpItem) {
+            SIZE size;
+
+            MENU_GetBitmapItemSize(lpitem, &size, hwndOwner);
+            /* Keep the size of the bitmap in callback mode to be able
+             * to draw it correctly */
+            lpitem->bmpsize = size;
+            lppop->maxBmpSize.cx = max( lppop->maxBmpSize.cx, size.cx);
+            lppop->maxBmpSize.cy = max( lppop->maxBmpSize.cy, size.cy);
+            lpitem->rect.right += size.cx + 2;
+            itemheight = size.cy + 2;
+        }
+        lpitem->rect.right += 4 + arrow_bitmap_width + menucharsize.cx;
+        if( !(lppop->dwStyle & MNS_NOCHECK))
+            lpitem->rect.right += check_bitmap_width; 
+    } else if (lpitem->hbmpItem) { /* menuBar */
         SIZE size;
 
-        MENU_GetBitmapItemSize( (HBITMAP) lpitem->hbmpItem, lpitem->dwItemData, &size );
+        MENU_GetBitmapItemSize( lpitem, &size, hwndOwner );
+        lpitem->bmpsize = size;
         lpitem->rect.right  += size.cx;
-        lpitem->rect.bottom += size.cy;
-        /* Leave space for the sunken border */
-        lpitem->rect.right  += 2;
-        lpitem->rect.bottom += 2;
+        if( lpitem->text) lpitem->rect.right  += 2;
+        itemheight = size.cy;
     }
 
     /* it must be a text item - unless it's the system menu */
-    if (!(lpitem->fType & MF_SYSMENU) && lpitem->text)
-    {   SIZE size;
-
-	GetTextExtentPoint32W(hdc, lpitem->text,  strlenW(lpitem->text), &size);
-
-	lpitem->rect.right  += size.cx;
-	lpitem->rect.bottom += max(max(size.cy, GetSystemMetrics(SM_CYMENU)-1), lppop->maxBmpSize.cy);
-	lpitem->xTab = 0;
-
-	if (menuBar)
-	{
-	     lpitem->rect.right += MENU_BAR_ITEMS_SPACE;
-	}
-	else if ((p = strchrW( lpitem->text, '\t' )) != NULL)
-	{
-	    /* Item contains a tab (only meaningful in popup menus) */
-	    GetTextExtentPoint32W(hdc, lpitem->text, (int)(p - lpitem->text) , &size);
-	    lpitem->xTab = check_bitmap_width + MENU_TAB_SPACE + size.cx;
-	    lpitem->rect.right += MENU_TAB_SPACE;
-	}
-	else
-	{
-	    if (strchrW( lpitem->text, '\b' ))
-	        lpitem->rect.right += MENU_TAB_SPACE;
-	    lpitem->xTab = lpitem->rect.right - check_bitmap_width
-	                   - arrow_bitmap_width;
-	}
+    if (!(lpitem->fType & MF_SYSMENU) && lpitem->text) {
+        RECT rc = lpitem->rect;
+        LONG txtheight, txtwidth;
+        lpitem->xTab = 0;
+        if (menuBar) {
+            txtheight = DrawTextW( hdc, lpitem->text, -1, &rc,
+                    DT_SINGLELINE|DT_CALCRECT); 
+            lpitem->rect.right  += rc.right - rc.left;
+            itemheight = max( max( itemheight, txtheight),
+                    GetSystemMetrics( SM_CYMENU) - 1);
+            lpitem->rect.right +=  2 * menucharsize.cx;
+        } else {
+            if ((p = strchrW( lpitem->text, '\t' )) != NULL) {
+                RECT tmprc = rc;
+                LONG tmpheight;
+                int n = (int)( p - lpitem->text);
+                /* Item contains a tab (only meaningful in popup menus) */
+                /* get text size before the tab */
+                txtheight = DrawTextW( hdc, lpitem->text, n, &rc,
+                        DT_SINGLELINE|DT_CALCRECT);
+                txtwidth = rc.right - rc.left;
+                p += 1; /* advance past the Tab */
+                tmpheight = DrawTextW( hdc, p, -1, &tmprc,
+                        DT_SINGLELINE|DT_CALCRECT);
+                lpitem->xTab = menucharsize.cx +
+                    4 + check_bitmap_width + lpitem->bmpsize.cx + txtwidth;
+                txtheight = max( txtheight, tmpheight);
+                txtwidth += menucharsize.cx + /* space for the tab */
+                    tmprc.right - tmprc.left; /* space for the short cut */
+            } else {
+                txtheight = DrawTextW( hdc, lpitem->text, -1, &rc,
+                        DT_SINGLELINE|DT_CALCRECT);
+                txtwidth = rc.right - rc.left;
+                if (strchrW( lpitem->text, '\b' ))
+                    lpitem->rect.right += menucharsize.cx;
+                lpitem->xTab = 4 + check_bitmap_width + lpitem->bmpsize.cx +
+                    txtwidth;
+            }
+            if( (lppop->dwStyle & MNS_NOCHECK))
+                lpitem->xTab -= check_bitmap_width;
+            lpitem->rect.right  += 2 + txtwidth;
+            itemheight = max( itemheight,
+                    max( txtheight + 2, menucharsize.cy + 4));
+        }
+    } else if( menuBar) {
+        if( itemheight) 
+            itemheight = max( itemheight, GetSystemMetrics(SM_CYMENU)-1);
+        else
+            itemheight = GetSystemMetrics( SM_CYMENUSIZE)/2;
     }
+    lpitem->rect.bottom += itemheight;
     TRACE("%s\n", wine_dbgstr_rect( &lpitem->rect));
 }
 
@@ -1110,6 +1151,25 @@ static void MENU_MenuBarCalcSize( HDC hd
 }
 
 /***********************************************************************
+ *           draw_popup_arrow
+ *
+ * Draws the popup-menu arrow.
+ */
+static void draw_popup_arrow( HDC hdc, RECT rect, UINT arrow_bitmap_width,
+        UINT arrow_bitmap_height)
+{
+    HDC hdcMem = CreateCompatibleDC( hdc );
+    HBITMAP hOrigBitmap;
+
+    hOrigBitmap = SelectObject( hdcMem, get_arrow_bitmap() );
+    BitBlt( hdc, rect.right - arrow_bitmap_width - 1,
+            (rect.top + rect.bottom - arrow_bitmap_height) / 2,
+            arrow_bitmap_width, arrow_bitmap_height,
+            hdcMem, 0, 0, SRCCOPY );
+    SelectObject( hdcMem, hOrigBitmap );
+    DeleteDC( hdcMem );
+}
+/***********************************************************************
  *           MENU_DrawMenuItem
  *
  * Draw a single menu item.
@@ -1120,9 +1180,19 @@ static void MENU_DrawMenuItem( HWND hwnd
     RECT rect;
     BOOL flat_menu = FALSE;
     int bkgnd;
+    POPUPMENU *menu = MENU_GetMenu(hmenu);
+    UINT arrow_bitmap_width = 0, arrow_bitmap_height = 0;
+    RECT bmprc;
 
     debug_print_menuitem("MENU_DrawMenuItem: ", lpitem, "");
 
+    if (!menuBar) {
+        BITMAP bmp;
+        GetObjectW( get_arrow_bitmap(), sizeof(bmp), &bmp );
+        arrow_bitmap_width = bmp.bmWidth;
+        arrow_bitmap_height = bmp.bmHeight;
+    }
+
     if (lpitem->fType & MF_SYSMENU)
     {
 	if( !IsIconic(hwnd) )
@@ -1187,7 +1257,11 @@ static void MENU_DrawMenuItem( HWND hwnd
 	      dis.itemID, dis.itemState, dis.itemAction, dis.hwndItem,
 	      dis.hDC, wine_dbgstr_rect( &dis.rcItem));
         SendMessageW( hwndOwner, WM_DRAWITEM, 0, (LPARAM)&dis );
-        /* Fall through to draw popup-menu arrow */
+        /* Draw the popup-menu arrow */
+        if (lpitem->fType & MF_POPUP)
+            draw_popup_arrow( hdc, rect, arrow_bitmap_width,
+                    arrow_bitmap_height);
+        return;
     }
 
     TRACE("rect=%s\n", wine_dbgstr_rect( &lpitem->rect));
@@ -1196,70 +1270,66 @@ static void MENU_DrawMenuItem( HWND hwnd
 
     rect = lpitem->rect;
 
-    if (!(lpitem->fType & MF_OWNERDRAW))
+    if (lpitem->fState & MF_HILITE)
     {
-	if (lpitem->fState & MF_HILITE)
-	{
-	    if (flat_menu)
-	    {
-		InflateRect (&rect, -1, -1);
-		FillRect(hdc, &rect, GetSysColorBrush(COLOR_MENUHILIGHT));
-		InflateRect (&rect, 1, 1);
-		FrameRect(hdc, &rect, GetSysColorBrush(COLOR_HIGHLIGHT));
-	    }
-	    else
-	    {
-		if(menuBar)
-		    DrawEdge(hdc, &rect, BDR_SUNKENOUTER, BF_RECT);
-		else
-		    FillRect(hdc, &rect, GetSysColorBrush(COLOR_HIGHLIGHT));
-	    }
-	}
+        if (flat_menu)
+        {
+            InflateRect (&rect, -1, -1);
+            FillRect(hdc, &rect, GetSysColorBrush(COLOR_MENUHILIGHT));
+            InflateRect (&rect, 1, 1);
+            FrameRect(hdc, &rect, GetSysColorBrush(COLOR_HIGHLIGHT));
+        }
         else
-	    FillRect( hdc, &rect, GetSysColorBrush(bkgnd) );
+        {
+            if(menuBar)
+                DrawEdge(hdc, &rect, BDR_SUNKENOUTER, BF_RECT);
+            else
+                FillRect(hdc, &rect, GetSysColorBrush(COLOR_HIGHLIGHT));
+        }
     }
+    else
+        FillRect( hdc, &rect, GetSysColorBrush(bkgnd) );
 
     SetBkMode( hdc, TRANSPARENT );
 
-    if (!(lpitem->fType & MF_OWNERDRAW))
+    /* vertical separator */
+    if (!menuBar && (lpitem->fType & MF_MENUBARBREAK))
     {
-	HPEN oldPen;
-    
-        /* vertical separator */
-        if (!menuBar && (lpitem->fType & MF_MENUBARBREAK))
+        HPEN oldPen;
+        RECT rc = rect;
+
+        rc.top = 3;
+        rc.bottom = height - 3;
+        if (flat_menu)
         {
-	    RECT rc = rect;
-	    rc.top = 3;
-	    rc.bottom = height - 3;
-	    if (flat_menu)
-	    {
-		oldPen = SelectObject( hdc, SYSCOLOR_GetPen(COLOR_BTNSHADOW) );
-		MoveToEx( hdc, rc.left, rc.top, NULL );
-		LineTo( hdc, rc.left, rc.bottom );
-		SelectObject( hdc, oldPen );
-	    }
-	    else
-		DrawEdge (hdc, &rc, EDGE_ETCHED, BF_LEFT);
+            oldPen = SelectObject( hdc, SYSCOLOR_GetPen(COLOR_BTNSHADOW) );
+            MoveToEx( hdc, rc.left, rc.top, NULL );
+            LineTo( hdc, rc.left, rc.bottom );
+            SelectObject( hdc, oldPen );
         }
+        else
+            DrawEdge (hdc, &rc, EDGE_ETCHED, BF_LEFT);
+    }
 
-        /* horizontal separator */
-        if (lpitem->fType & MF_SEPARATOR)
+    /* horizontal separator */
+    if (lpitem->fType & MF_SEPARATOR)
+    {
+        HPEN oldPen;
+        RECT rc = rect;
+
+        rc.left++;
+        rc.right--;
+        rc.top = ( rc.top + rc.bottom) / 2;
+        if (flat_menu)
         {
-	    RECT rc = rect;
-	    rc.left++;
-	    rc.right--;
-	    rc.top += SEPARATOR_HEIGHT / 2;
-	    if (flat_menu)
-	    {
-		oldPen = SelectObject( hdc, SYSCOLOR_GetPen(COLOR_BTNSHADOW) );
-		MoveToEx( hdc, rc.left, rc.top, NULL );
-		LineTo( hdc, rc.right, rc.top );
-		SelectObject( hdc, oldPen );
-	    }
-	    else
-		DrawEdge (hdc, &rc, EDGE_ETCHED, BF_TOP);
-	    return;
+            oldPen = SelectObject( hdc, SYSCOLOR_GetPen(COLOR_BTNSHADOW) );
+            MoveToEx( hdc, rc.left, rc.top, NULL );
+            LineTo( hdc, rc.right, rc.top );
+            SelectObject( hdc, oldPen );
         }
+        else
+            DrawEdge (hdc, &rc, EDGE_ETCHED, BF_TOP);
+        return;
     }
 
 	/* helper lines for debugging */
@@ -1269,119 +1339,97 @@ static void MENU_DrawMenuItem( HWND hwnd
 	LineTo( hdc, rect.right, (rect.top + rect.bottom)/2 );
 */
 
+    if (lpitem->hbmpItem) {
+        /* calculate the bitmap rectangle in coordinates relative
+         * to the item rectangle */
+        if( menuBar) {
+            if( lpitem->hbmpItem == HBMMENU_CALLBACK)
+                bmprc.left = 3;
+            else 
+                bmprc.left = lpitem->text ? menucharsize.cx : 0;          
+        } else {
+            bmprc.left = 4;
+            if( !(menu->dwStyle & ( MNS_CHECKORBMP | MNS_NOCHECK)))
+                bmprc.left += GetSystemMetrics( SM_CXMENUCHECK); 
+        }
+        bmprc.right =  bmprc.left + lpitem->bmpsize.cx;
+        if( menuBar && !(lpitem->hbmpItem == HBMMENU_CALLBACK))
+            bmprc.top = 0;
+        else
+            bmprc.top = (lpitem->rect.bottom - lpitem->rect.top -
+                    lpitem->bmpsize.cy) / 2; 
+        bmprc.bottom =  bmprc.top + lpitem->bmpsize.cy;
+    }
+
     if (!menuBar)
     {
         HBITMAP bm;
         INT y = rect.top + rect.bottom;
+        RECT rc = rect;
+        int checked = FALSE;
         UINT check_bitmap_width = GetSystemMetrics( SM_CXMENUCHECK );
         UINT check_bitmap_height = GetSystemMetrics( SM_CYMENUCHECK );
-        UINT arrow_bitmap_width, arrow_bitmap_height;
-        BITMAP bmp;
-
-        GetObjectW( get_arrow_bitmap(), sizeof(bmp), &bmp );
-        arrow_bitmap_width = bmp.bmWidth;
-        arrow_bitmap_height = bmp.bmHeight;
-
-        if (!(lpitem->fType & MF_OWNERDRAW))
-        {
-            RECT rc;
-            rc = rect;
-            if (lpitem->hbmpItem)
-            {
-                POPUPMENU *menu = MENU_GetMenu(hmenu);
-                if (menu->dwStyle & MNS_CHECKORBMP)
-                    rc.left += menu->maxBmpSize.cx - check_bitmap_width;
-                else
-                    rc.left += menu->maxBmpSize.cx;
-            }
-            /* Draw the check mark
-             *
-             * FIXME:
-             * Custom checkmark bitmaps are monochrome but not always 1bpp.
-             */
-            bm = (lpitem->fState & MF_CHECKED) ? lpitem->hCheckBit : lpitem->hUnCheckBit;
-            if (bm)  /* we have a custom bitmap */
-            {
+        /* Draw the check mark
+         *
+         * FIXME:
+         * Custom checkmark bitmaps are monochrome but not always 1bpp.
+         */
+        if( !(menu->dwStyle & MNS_NOCHECK)) {
+            bm = (lpitem->fState & MF_CHECKED) ? lpitem->hCheckBit :
+                lpitem->hUnCheckBit;
+            if (bm)  {/* we have a custom bitmap */
                 HDC hdcMem = CreateCompatibleDC( hdc );
+
                 SelectObject( hdcMem, bm );
                 BitBlt( hdc, rc.left, (y - check_bitmap_height) / 2,
                         check_bitmap_width, check_bitmap_height,
                         hdcMem, 0, 0, SRCCOPY );
                 DeleteDC( hdcMem );
-            }
-            else if (lpitem->fState & MF_CHECKED)  /* standard bitmaps */
-            {
+                checked = TRUE;
+            } else if (lpitem->fState & MF_CHECKED) {/* standard bitmaps */
                 RECT r;
-                HBITMAP bm = CreateBitmap( check_bitmap_width, check_bitmap_height, 1, 1, NULL );
+                HBITMAP bm = CreateBitmap( check_bitmap_width,
+                        check_bitmap_height, 1, 1, NULL );
                 HDC hdcMem = CreateCompatibleDC( hdc );
+
                 SelectObject( hdcMem, bm );
-                SetRect( &r, 0, 0, check_bitmap_width, check_bitmap_height );
+                SetRect( &r, 0, 0, check_bitmap_width, check_bitmap_height);
                 DrawFrameControl( hdcMem, &r, DFC_MENU,
-                                  (lpitem->fType & MFT_RADIOCHECK) ?
-                                  DFCS_MENUBULLET : DFCS_MENUCHECK );
+                        (lpitem->fType & MFT_RADIOCHECK) ?
+                        DFCS_MENUBULLET : DFCS_MENUCHECK );
                 BitBlt( hdc, rc.left, (y - r.bottom) / 2, r.right, r.bottom,
                         hdcMem, 0, 0, SRCCOPY );
                 DeleteDC( hdcMem );
                 DeleteObject( bm );
-            }
-            if (lpitem->hbmpItem)
-            {
-                HBITMAP hbm = lpitem->hbmpItem;
-
-                if (hbm == HBMMENU_CALLBACK)
-                {
-                    DRAWITEMSTRUCT drawItem;
-                    POINT origorg;
-                    drawItem.CtlType = ODT_MENU;
-                    drawItem.CtlID = 0;
-                    drawItem.itemID = lpitem->wID;
-                    drawItem.itemAction = odaction;
-                    drawItem.itemState = (lpitem->fState & MF_CHECKED)?ODS_CHECKED:0;
-                    drawItem.itemState |= (lpitem->fState & MF_DEFAULT)?ODS_DEFAULT:0;
-                    drawItem.itemState |= (lpitem->fState & MF_DISABLED)?ODS_DISABLED:0;
-                    drawItem.itemState |= (lpitem->fState & MF_GRAYED)?ODS_GRAYED|ODS_DISABLED:0;
-                    drawItem.itemState |= (lpitem->fState & MF_HILITE)?ODS_SELECTED:0;
-                    drawItem.hwndItem = (HWND)hmenu;
-                    drawItem.hDC = hdc;
-                    drawItem.rcItem = lpitem->rect;
-                    drawItem.itemData = lpitem->dwItemData;
-                    /* some applications make this assumption on the DC's origin */
-                    SetViewportOrgEx( hdc, lpitem->rect.left, lpitem->rect.top, &origorg);
-                    OffsetRect( &drawItem.rcItem, - lpitem->rect.left, - lpitem->rect.top);
-                    SendMessageW( hwndOwner, WM_DRAWITEM, 0, (LPARAM)&drawItem);
-                    SetViewportOrgEx( hdc, origorg.x, origorg.y, NULL);
-
-                } else {
-                    MENU_DrawBitmapItem(hdc, lpitem, &rect, FALSE);
-                }
+                checked = TRUE;
             }
         }
-
-	  /* Draw the popup-menu arrow */
-	if (lpitem->fType & MF_POPUP)
-	{
-	    HDC hdcMem = CreateCompatibleDC( hdc );
-	    HBITMAP hOrigBitmap;
-
-	    hOrigBitmap = SelectObject( hdcMem, get_arrow_bitmap() );
-	    BitBlt( hdc, rect.right - arrow_bitmap_width - 1,
-		      (y - arrow_bitmap_height) / 2,
-		      arrow_bitmap_width, arrow_bitmap_height,
-		      hdcMem, 0, 0, SRCCOPY );
-            SelectObject( hdcMem, hOrigBitmap );
-	    DeleteDC( hdcMem );
-	}
-
-	rect.left += check_bitmap_width;
+        if( lpitem->hbmpItem &&
+                !( checked && (menu->dwStyle & MNS_CHECKORBMP))) {
+            POINT origorg;
+            /* some applications make this assumption on the DC's origin */
+            SetViewportOrgEx( hdc, lpitem->rect.left, lpitem->rect.top, &origorg);
+            MENU_DrawBitmapItem(hdc, lpitem, &bmprc, hmenu, hwndOwner,
+                    odaction, FALSE);
+            SetViewportOrgEx( hdc, origorg.x, origorg.y, NULL);
+        }
+	/* Draw the popup-menu arrow */
+        if (lpitem->fType & MF_POPUP)
+            draw_popup_arrow( hdc, rect, arrow_bitmap_width,
+                    arrow_bitmap_height);
+	rect.left += 4;
+        if( !(menu->dwStyle & MNS_NOCHECK)) rect.left += check_bitmap_width;
 	rect.right -= arrow_bitmap_width;
     }
-    else if( lpitem->hbmpItem && !(lpitem->fType & MF_OWNERDRAW))
+    else if( lpitem->hbmpItem)
     {   /* Draw the bitmap */
-	MENU_DrawBitmapItem( hdc, lpitem, &rect, menuBar);
+        POINT origorg;
+        
+        SetViewportOrgEx( hdc, lpitem->rect.left, lpitem->rect.top, &origorg);
+        MENU_DrawBitmapItem( hdc, lpitem, &bmprc, hmenu, hwndOwner,
+                odaction, menuBar);
+        SetViewportOrgEx( hdc, origorg.x, origorg.y, NULL);
     }
-    /* Done for owner-drawn */
-    if (lpitem->fType & MF_OWNERDRAW)
-        return;
     /* process text if present */
     if (lpitem->text)
     {
@@ -1392,15 +1440,20 @@ static void MENU_DrawMenuItem( HWND hwnd
 			DT_CENTER | DT_VCENTER | DT_SINGLELINE :
 			DT_LEFT | DT_VCENTER | DT_SINGLELINE;
 
+        if( !(menu->dwStyle & MNS_CHECKORBMP))
+            rect.left += menu->maxBmpSize.cx;
+
 	if ( lpitem->fState & MFS_DEFAULT )
 	{
 	     hfontOld = SelectObject( hdc, get_menu_font(TRUE) );
 	}
 
-	if (menuBar)
-	{
-	    rect.left += MENU_BAR_ITEMS_SPACE / 2;
-	    rect.right -= MENU_BAR_ITEMS_SPACE / 2;
+	if (menuBar) {
+            if( lpitem->hbmpItem)
+                rect.left += lpitem->bmpsize.cx;
+            if( !(lpitem->hbmpItem == HBMMENU_CALLBACK))
+                rect.left += menucharsize.cx;
+            rect.right -= menucharsize.cx;
 	}
 
 	for (i = 0; lpitem->text[i]; i++)
@@ -1451,6 +1504,7 @@ static void MENU_DrawMenuItem( HWND hwnd
 	if (hfontOld)
 	    SelectObject (hdc, hfontOld);
     }
+    return;
 }
 
 
@@ -4079,16 +4133,19 @@ static BOOL GetMenuItemInfo_common ( HME
     
     if( lpmii->fMask & MIIM_TYPE) {
         if( lpmii->fMask & ( MIIM_STRING | MIIM_FTYPE | MIIM_BITMAP)) {
+            WARN("invalid combination of fMask bits used\n");
+            /* this does not happen on Win9x/ME */
             SetLastError( ERROR_INVALID_PARAMETER);
             return FALSE;
         }
 	lpmii->fType = menu->fType & ~MF_POPUP;
         if( menu->hbmpItem) lpmii->fType |= MFT_BITMAP;
-	lpmii->hbmpItem = menu->hbmpItem;
+	lpmii->hbmpItem = menu->hbmpItem; /* not on Win9x/ME */
         if( lpmii->fType & MFT_BITMAP) {
 	    lpmii->dwTypeData = (LPWSTR) menu->hbmpItem;
 	    lpmii->cch = 0;
         } else if( lpmii->fType & (MFT_OWNERDRAW | MFT_SEPARATOR)) {
+            /* this does not happen on Win9x/ME */
 	    lpmii->dwTypeData = 0;
 	    lpmii->cch = 0;
         }
@@ -4127,8 +4184,11 @@ static BOOL GetMenuItemInfo_common ( HME
                     lpmii->cch--;
                 else
                     lpmii->cch = len;
-            else /* return length of string */
+            else {
+                /* return length of string */
+                /* not on Win9x/ME if fType & MFT_BITMAP */
                 lpmii->cch = len;
+            }
         }
     }
 
@@ -4146,8 +4206,11 @@ static BOOL GetMenuItemInfo_common ( HME
 
     if (lpmii->fMask & MIIM_SUBMENU)
 	lpmii->hSubMenu = menu->hSubMenu;
-    else
-        lpmii->hSubMenu = 0; /* hSubMenu is always cleared */
+    else {
+        /* hSubMenu is always cleared 
+         * (not on Win9x/ME ) */
+        lpmii->hSubMenu = 0;
+    }
 
     if (lpmii->fMask & MIIM_CHECKMARKS) {
 	lpmii->hbmpChecked = menu->hCheckBit;
@@ -4233,10 +4296,12 @@ static BOOL SetMenuItemInfo_common(MENUI
 {
     if (!menu) return FALSE;
 
-    debug_print_menuitem("MENU_SetItemInfo_common from: ", menu, "");
+    debug_print_menuitem("SetmenuItemInfo_common from: ", menu, "");
 
     if (lpmii->fMask & MIIM_TYPE ) {
         if( lpmii->fMask & ( MIIM_STRING | MIIM_FTYPE | MIIM_BITMAP)) {
+            WARN("invalid combination of fMask bits used\n");
+            /* this does not happen on Win9x/ME */
             SetLastError( ERROR_INVALID_PARAMETER);
             return FALSE;
         }
@@ -4603,7 +4668,6 @@ BOOL WINAPI SetMenuInfo (HMENU hMenu, LP
 	    if (menu->dwStyle & MNS_AUTODISMISS) FIXME("MNS_AUTODISMISS unimplemented\n");
 	    if (menu->dwStyle & MNS_DRAGDROP) FIXME("MNS_DRAGDROP unimplemented\n");
 	    if (menu->dwStyle & MNS_MODELESS) FIXME("MNS_MODELESS unimplemented\n");
-	    if (menu->dwStyle & MNS_NOCHECK) FIXME("MNS_NOCHECK unimplemented\n");
 	    if (menu->dwStyle & MNS_NOTIFYBYPOS) FIXME("MNS_NOTIFYBYPOS unimplemented\n");
 	}
 
--- wine/dlls/user/tests/menu.c	2006-02-03 11:24:11.000000000 +0100
+++ mywine/dlls/user/tests/menu.c	2006-02-03 20:32:29.000000000 +0100
@@ -23,6 +23,7 @@
 
 #include <stdlib.h>
 #include <stdarg.h>
+#include <stdio.h>
 #include <assert.h>
 
 #include "windef.h"
@@ -34,6 +35,9 @@
 
 static ATOM atomMenuCheckClass;
 
+static BOOL (WINAPI *pSetMenuInfo)(HMENU,LPCMENUINFO);
+static BOOL (WINAPI *pGetMenuInfo)(HMENU,LPCMENUINFO);
+
 static LRESULT WINAPI menu_check_wnd_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
 {
     switch (msg)
@@ -49,11 +53,30 @@ static LRESULT WINAPI menu_check_wnd_pro
 }
 
 /* globals to communicate between test and wndproc */
+
+#define MOD_SIZE 10
+#define MOD_NRMENUS 8
+
+ /* menu texts with their sizes */
+static struct {
+    char *text;
+    SIZE size; /* size of text upto any \t */
+    SIZE sc_size; /* size of the short-cut */
+} MOD_txtsizes[] = {
+        { "Pinot &Noir" },
+        { "&Merlot\t" },
+        { "Shira&z\tAlt+S" },
+        { "" },
+        { NULL }
+};
+
 unsigned int MOD_maxid;
-RECT MOD_rc[4];
+RECT MOD_rc[MOD_NRMENUS];
 int MOD_avec, MOD_hic;
 int MOD_odheight;
-#define MOD_SIZE 10
+SIZE MODsizes[MOD_NRMENUS]= { {MOD_SIZE, MOD_SIZE},{MOD_SIZE, MOD_SIZE},
+    {MOD_SIZE, MOD_SIZE},{MOD_SIZE, MOD_SIZE}};
+int MOD_GotDrawItemMsg = FALSE;
 /* wndproc used by test_menu_ownerdraw() */
 static LRESULT WINAPI menu_ownerdraw_wnd_proc(HWND hwnd, UINT msg,
         WPARAM wparam, LPARAM lparam)
@@ -64,11 +87,11 @@ static LRESULT WINAPI menu_ownerdraw_wnd
             {
                 MEASUREITEMSTRUCT* pmis = (MEASUREITEMSTRUCT*)lparam;
                 if( winetest_debug)
-                    trace("WM_MEASUREITEM received %d,%d\n",
-                            pmis->itemWidth, pmis->itemHeight);
+                    trace("WM_MEASUREITEM received data %lx size %dx%d\n",
+                            pmis->itemData, pmis->itemWidth, pmis->itemHeight);
                 MOD_odheight = pmis->itemHeight;
-                pmis->itemWidth = MOD_SIZE;
-                pmis->itemHeight = MOD_SIZE;
+                pmis->itemWidth = MODsizes[pmis->itemData].cx;
+                pmis->itemHeight = MODsizes[pmis->itemData].cy;
                 return TRUE;
             }
         case WM_DRAWITEM:
@@ -78,18 +101,41 @@ static LRESULT WINAPI menu_ownerdraw_wnd
                 HPEN oldpen;
                 char chrs[]="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
                 SIZE sz;
+                int i;
                 pdis = (DRAWITEMSTRUCT *) lparam;
                 if( winetest_debug) {
-                    trace("WM_DRAWITEM received itemdata %ld item %d rc %ld,%ld-%ld,%ld\n",
-                            pdis->itemData,
+                    RECT rc;
+                    GetMenuItemRect( hwnd, (HMENU)pdis->hwndItem, pdis->itemData ,&rc);
+                    trace("WM_DRAWITEM received hwnd %p hmenu %p itemdata %ld item %d rc %ld,%ld-%ld,%ld itemrc:  %ld,%ld-%ld,%ld\n",
+                            hwnd, (HMENU)pdis->hwndItem, pdis->itemData,
                             pdis->itemID, pdis->rcItem.left, pdis->rcItem.top,
-                            pdis->rcItem.right,pdis->rcItem.bottom );
+                            pdis->rcItem.right,pdis->rcItem.bottom,
+                            rc.left,rc.top,rc.right,rc.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);
                 }
+                /* calculate widths of some menu texts */
+                if( ! MOD_txtsizes[0].size.cx)
+                    for(i = 0; MOD_txtsizes[i].text; i++) {
+                        char buf[100], *p;
+                        RECT rc={0,0,0,0};
+                        strcpy( buf, MOD_txtsizes[i].text);
+                        if( ( p = strchr( buf, '\t'))) {
+                            *p = '\0';
+                            DrawText( pdis->hDC, p + 1, -1, &rc,
+                                    DT_SINGLELINE|DT_CALCRECT);
+                            MOD_txtsizes[i].sc_size.cx= rc.right - rc.left;
+                            MOD_txtsizes[i].sc_size.cy= rc.bottom - rc.top;
+                        }
+                        DrawText( pdis->hDC, buf, -1, &rc,
+                                DT_SINGLELINE|DT_CALCRECT);
+                        MOD_txtsizes[i].size.cx= rc.right - rc.left;
+                        MOD_txtsizes[i].size.cy= rc.bottom - rc.top;
+                    }
+
                 if( pdis->itemData > MOD_maxid) return TRUE;
                 /* store the rectangl */
                 MOD_rc[pdis->itemData] = pdis->rcItem;
@@ -98,7 +144,12 @@ static LRESULT WINAPI menu_ownerdraw_wnd
                 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);
+                MOD_GotDrawItemMsg = TRUE;
+                return TRUE;
+            }
+        case WM_ENTERIDLE:
+            {
+                PostMessage(hwnd, WM_CANCELMODE, 0, 0);
                 return TRUE;
             }
 
@@ -236,7 +287,7 @@ static void test_menu_ownerdraw(void)
         ret = AppendMenu( hmenu, MF_OWNERDRAW , i, 0);
         ok( ret, "AppendMenu failed for %d\n", i);
     }
-    SetMenu( hwnd, hmenu);
+    ret = SetMenu( hwnd, hmenu);
     UpdateWindow( hwnd); /* hack for wine to draw the window + menu */
     ok(ret, "SetMenu failed with error %ld\n", GetLastError());
     /* test width */
@@ -247,6 +298,207 @@ static void test_menu_ownerdraw(void)
     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 */
+    ret = DestroyMenu(hmenu);
+    ok(ret, "DestroyMenu failed with error %ld\n", GetLastError());
+    DestroyWindow(hwnd);
+}
+
+/* helper for test_menu_bmp_and_string() */
+static void test_mbs_help( int ispop, int hassub, int mnuopt,
+        HWND hwnd, int arrowwidth, int count, HBITMAP hbmp,
+        SIZE bmpsize, char *text, SIZE size, SIZE sc_size)
+{
+    BOOL ret;
+    HMENU hmenu, submenu;
+    MENUITEMINFO mii={ sizeof( MENUITEMINFO )};
+    MENUINFO mi;
+    RECT rc;
+    int hastab,  expect;
+    int failed = 0;
+
+    MOD_GotDrawItemMsg = FALSE;
+    mii.fMask = MIIM_FTYPE | MIIM_DATA | MIIM_STATE;
+    mii.fType = 0;
+    mii.fState = MF_CHECKED;
+    mii.dwItemData =0;
+    MODsizes[0] = bmpsize;
+    hastab = 0;
+    if( text ) {
+        char *p;
+        mii.fMask |= MIIM_STRING;
+        mii.dwTypeData = text;
+        if( ( p = strchr( text, '\t'))) {
+            hastab = *(p + 1) ? 2 : 1;
+        }
+    }
+    /* tabs don't make sense in menubars */
+    if(hastab && !ispop) return;
+    if( hbmp) {
+        mii.fMask |= MIIM_BITMAP;
+        mii.hbmpItem = hbmp;
+    }
+    submenu = CreateMenu();
+    ok( submenu != 0, "CreateMenu failed with error %ld\n", GetLastError());
+    if( ispop)
+        hmenu = CreatePopupMenu();
+    else
+        hmenu = CreateMenu();
+    ok( hmenu != 0, "Create{Popup}Menu failed with error %ld\n", GetLastError());
+    if( hassub) {
+        mii.fMask |= MIIM_SUBMENU;
+        mii.hSubMenu = submenu;
+    }
+    if( mnuopt) {
+        mi.cbSize = sizeof(mi);
+        mi.fMask = MIM_STYLE;
+        pGetMenuInfo( hmenu, &mi);
+        mi.dwStyle |= mnuopt == 1 ? MNS_NOCHECK : MNS_CHECKORBMP;
+        ret = pSetMenuInfo( hmenu, &mi);
+        ok( ret, "SetMenuInfo failed with error %ld\n", GetLastError());
+    }
+    ret = InsertMenuItem( hmenu, 0, FALSE, &mii);
+    ok( ret, "InsertMenuItem failed with error %ld\n", GetLastError());
+    failed = !ret;
+    if( winetest_debug) {
+        HDC hdc=GetDC(hwnd);
+        RECT rc = {100, 50, 400, 70};
+        char buf[100];
+
+        sprintf( buf,"%d text \"%s\" mnuopt %d", count, text ? text: "(nil)", mnuopt);
+        FillRect( hdc, &rc, (HBRUSH) COLOR_WINDOW);
+        TextOut( hdc, 100, 50, buf, strlen( buf));
+        ReleaseDC( hwnd, hdc);
+    }
+    if(ispop)
+        ret = TrackPopupMenu( hmenu, 0x100, 100,100, 0, hwnd, NULL);
+    else {
+        ret = SetMenu( hwnd, hmenu);
+        ok(ret, "SetMenu failed with error %ld\n", GetLastError());
+        DrawMenuBar( hwnd);
+    }
+    ret = GetMenuItemRect( hwnd, hmenu, 0, &rc);
+    /* check menu width */
+    if( ispop)
+        expect = ( text || hbmp ?
+                4 + (mnuopt != 1 ? GetSystemMetrics(SM_CXMENUCHECK) : 0)
+                : 0) +
+            arrowwidth  + MOD_avec + (hbmp ? bmpsize.cx + 2 : 0) +
+            (text && hastab ? /* TAB space */
+             MOD_avec + ( hastab==2 ? sc_size.cx : 0) : 0) +
+            (text ?  2 + (text[0] ? size.cx :0): 0) ;
+    else
+        expect = !(text || hbmp) ? 0 :
+            ( hbmp ? (text ? 2:0) + bmpsize.cx  : 0 ) +
+            (text ? 2 * MOD_avec + (text[0] ? size.cx :0): 0) ;
+    ok( rc.right - rc.left == expect,
+            "menu width wrong, got %ld expected %d\n", rc.right - rc.left, expect);
+    failed = failed || !(rc.right - rc.left == expect);
+    /* check menu height */
+    if( ispop)
+        expect = max( ( !(text || hbmp) ? GetSystemMetrics( SM_CYMENUSIZE)/2 : 0),
+                max( (text ? max( 2 + size.cy, MOD_hic + 4) : 0),
+                    (hbmp ? bmpsize.cy + 2 : 0)));
+    else
+        expect = ( !(text || hbmp) ? GetSystemMetrics( SM_CYMENUSIZE)/2 :
+                max( GetSystemMetrics( SM_CYMENU) - 1, (hbmp ? bmpsize.cy : 0)));
+    ok( rc.bottom - rc.top == expect,
+            "menu height wrong, got %ld expected %d (%d)\n",
+            rc.bottom - rc.top, expect, GetSystemMetrics( SM_CYMENU));
+    failed = failed || !(rc.bottom - rc.top == expect);
+    if( hbmp == HBMMENU_CALLBACK && MOD_GotDrawItemMsg) {
+        /* check the position of the bitmap */
+        /* horizontal */
+        expect = ispop ? (4 + ( mnuopt  ? 0 : GetSystemMetrics(SM_CXMENUCHECK)))
+            : 3;
+        ok( expect == MOD_rc[0].left,
+                "bitmap left is %ld expected %d\n", MOD_rc[0].left, expect);
+        failed = failed || !(expect == MOD_rc[0].left);
+        /* vertical */
+        expect = (rc.bottom - rc.top - MOD_rc[0].bottom + MOD_rc[0].top) / 2;
+        ok( expect == MOD_rc[0].top,
+                "bitmap top is %ld expected %d\n", MOD_rc[0].top, expect);
+        failed = failed || !(expect == MOD_rc[0].top);
+    }
+    /* if there was a failure, report details */
+    if( failed) {
+        trace("*** count %d text \"%s\" bitmap %p bmsize %ld,%ld textsize %ld+%ld,%ld mnuopt %d hastab %d\n",
+                count, text ? text: "(nil)", hbmp, bmpsize.cx, bmpsize.cy,
+                size.cx, size.cy, sc_size.cx, mnuopt, hastab);
+        trace("    check %d,%d arrow %d avechar %d\n",
+                GetSystemMetrics(SM_CXMENUCHECK ),
+                GetSystemMetrics(SM_CYMENUCHECK ),arrowwidth, MOD_avec);
+        if( hbmp == HBMMENU_CALLBACK)
+            trace( "    rc %ld,%ld-%ld,%ld bmp.rc %ld,%ld-%ld,%ld\n",
+                rc.left, rc.top, rc.top, rc.bottom, MOD_rc[0].left,
+                MOD_rc[0].top,MOD_rc[0].right, MOD_rc[0].bottom);
+    }
+    /* clean up */
+    ret = DestroyMenu(submenu);
+    ok(ret, "DestroyMenu failed with error %ld\n", GetLastError());
+    ret = DestroyMenu(hmenu);
+    ok(ret, "DestroyMenu failed with error %ld\n", GetLastError());
+}
+
+
+static void test_menu_bmp_and_string(void)
+{
+    BYTE bmfill[300];
+    HBITMAP hbm_arrow;
+    BITMAP bm;
+    INT arrowwidth;
+    HWND hwnd;
+    int count, szidx, txtidx, bmpidx, hassub, mnuopt, ispop;
+
+    if( !pGetMenuInfo) return;
+
+    memset( bmfill, 0x55, sizeof( bmfill));
+    hwnd = CreateWindowEx(0, MAKEINTATOM(atomMenuCheckClass), NULL,
+            WS_VISIBLE, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
+            NULL, NULL, NULL, NULL);
+    hbm_arrow=LoadBitmap( 0, (CHAR*)OBM_MNARROW);
+    GetObject( hbm_arrow, sizeof(bm), &bm);
+    arrowwidth = bm.bmWidth;
+
+    ok(hwnd != NULL, "CreateWindowEx failed with error %ld\n", GetLastError());
+    if( !hwnd) return;
+    SetWindowLongPtr( hwnd, GWLP_WNDPROC, (LONG)menu_ownerdraw_wnd_proc);
+
+    if( winetest_debug)
+        trace("    check %d,%d arrow %d avechar %d\n",
+                GetSystemMetrics(SM_CXMENUCHECK ),
+                GetSystemMetrics(SM_CYMENUCHECK ),arrowwidth, MOD_avec);
+    count = 0;
+    MOD_maxid = 0;
+    for( ispop=1; ispop >= 0; ispop--){
+        static SIZE bmsizes[]= {
+            {10,10},{38,38},{1,30},{55,5}};
+        for( szidx=0; szidx < sizeof( bmsizes) / sizeof( SIZE); szidx++) {
+            HBITMAP hbm = CreateBitmap( bmsizes[szidx].cx, bmsizes[szidx].cy,1,1,bmfill);
+            HBITMAP bitmaps[] = { HBMMENU_CALLBACK, hbm, NULL  };
+            ok( (int)hbm, "CreateBitmap failed err %ld\n", GetLastError());
+            for( txtidx = 0; txtidx < sizeof(MOD_txtsizes)/sizeof(MOD_txtsizes[0]); txtidx++) {
+                for( hassub = 0; hassub < 2 ; hassub++) { /* add submenu item */
+                    for( mnuopt = 0; mnuopt < 3 ; mnuopt++){ /* test MNS_NOCHECK/MNS_CHECKORBMP */
+                        for( bmpidx = 0; bmpidx <sizeof(bitmaps)/sizeof(HBITMAP); bmpidx++) {
+                            /* no need to test NULL bitmaps of several sizes */
+                            if( !bitmaps[bmpidx] && szidx > 0) continue;
+                            if( !ispop && hassub) continue;
+                            test_mbs_help( ispop, hassub, mnuopt,
+                                    hwnd, arrowwidth, ++count,
+                                    bitmaps[bmpidx],
+                                    bmsizes[szidx],
+                                    MOD_txtsizes[txtidx].text,
+                                    MOD_txtsizes[txtidx].size,
+                                    MOD_txtsizes[txtidx].sc_size);
+                        }
+                    }
+                }
+            }
+            DeleteObject( hbm);
+        }
+    }
     /* clean up */
     DestroyWindow(hwnd);
 }
@@ -256,7 +508,7 @@ static void test_menu_add_string( void )
     HMENU hmenu;
     MENUITEMINFO info;
     BOOL rc;
-    
+
     char string[0x80];
     char string2[0x80];
 
@@ -880,9 +1132,22 @@ static void test_menu_iteminfo(  )
         {, S, MIIM_TYPE, MFT_STRING, -9, -9, 0, -9, -9, -9, string, 4, 0, },
         txt,  OK, OK )
     TMII_DONE
+    /* MFT_SEPARATOR bit is kept when the text is added */
+    TMII_INSMI( {, S, MIIM_STRING|MIIM_FTYPE, MFT_STRING, -1, -1, -1, -1, -1, -1, NULL, 0, -1, }, OK)
+    TMII_SMII( {, S, MIIM_STRING, -1, -1, -1, -1, -1, -1, -1, txt, -1, -1, }, OK)
+    TMII_GMII ( {, S, MIIM_STRING|MIIM_FTYPE, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
+        {, S, MIIM_STRING|MIIM_FTYPE, MFT_SEPARATOR, -9, -9, 0, -9, -9, -9, string, 4, -9, },
+        txt, OK, OK )
+    TMII_DONE
+    /* MFT_SEPARATOR bit is kept when bitmap is added */
+    TMII_INSMI( {, S, MIIM_STRING|MIIM_FTYPE, MFT_STRING, -1, -1, -1, -1, -1, -1, NULL, 0, -1, }, OK)
+    TMII_SMII( {, S, MIIM_BITMAP, -1, -1, -1, -1, -1, -1, -1, -1, -1, hbm, }, OK)
+    TMII_GMII ( {, S, MIIM_BITMAP|MIIM_FTYPE, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
+        {, S, MIIM_BITMAP|MIIM_FTYPE, MFT_SEPARATOR, -9, -9, 0, -9, -9, -9, string, 80, hbm, },
+        init, OK, ER )
+    TMII_DONE
 
-    ansi = !ansi;
-  } while( !ansi);
+  } while( !(ansi = !ansi) );
   DeleteObject( hbm);
 }
 
@@ -1149,6 +1414,11 @@ void test_menu_search_bycommand( void )
 
 START_TEST(menu)
 {
+    pSetMenuInfo =
+        (void *)GetProcAddress( GetModuleHandleA("user32.dll"), "SetMenuInfo" );
+    pGetMenuInfo =
+        (void *)GetProcAddress( GetModuleHandleA("user32.dll"), "GetMenuInfo" );
+
     register_menu_check_class();
 
     test_menu_locked_by_window();
@@ -1156,4 +1426,5 @@ START_TEST(menu)
     test_menu_add_string();
     test_menu_iteminfo();
     test_menu_search_bycommand();
+    test_menu_bmp_and_string();
 }


More information about the wine-patches mailing list