From 6b12639c491c70016c4c37e550f6ecaf608adb87 Mon Sep 17 00:00:00 2001 From: Dimitri Schoolwerth Date: Mon, 23 Jun 2014 20:05:30 +0000 Subject: [PATCH] Fix failing Win32 calls with checkable menu item. Since r76202 InsertMenuItem is used when adding checkable menu items even without a bitmap. The call fails because hbmpChecked and hbmpUnchecked are set to HBMMENU_CALLBACK on pre-Vista, making the menu owner drawn unnecessarily. Fix by adding GetHBitmapForMenuCheckable which is used when assigning values to hbmpChecked and hbmpUnchecked. GetHBitmapForMenu remains unchanged (for possible porting reasons) and is used for hBmpItem only. git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@76757 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775 --- include/wx/msw/menuitem.h | 5 ++- samples/menu/menu.cpp | 29 ++++++++++++ src/msw/menu.cpp | 4 +- src/msw/menuitem.cpp | 93 +++++++++++++++++++++++---------------- 4 files changed, 91 insertions(+), 40 deletions(-) diff --git a/include/wx/msw/menuitem.h b/include/wx/msw/menuitem.h index 741addbcc7..4362d621e6 100644 --- a/include/wx/msw/menuitem.h +++ b/include/wx/msw/menuitem.h @@ -117,9 +117,12 @@ private: bool MSWMustUseOwnerDrawn(); #endif // wxUSE_OWNER_DRAWN - // helper function to get a handle of bitmap associated with item + // helper function to get a handle for normal bitmap associated with item WXHBITMAP GetHBitmapForMenu(bool checked = true); + // helper function to get a handle for checkable bitmap associated with item + WXHBITMAP GetHBitmapForMenuCheckable(bool checked); + // helper function to set/change the bitmap void DoSetBitmap(const wxBitmap& bmp, bool bChecked); diff --git a/samples/menu/menu.cpp b/samples/menu/menu.cpp index f02168fbf8..3afe190faf 100644 --- a/samples/menu/menu.cpp +++ b/samples/menu/menu.cpp @@ -25,7 +25,9 @@ #ifndef WX_PRECOMP #include "wx/app.h" + #include "wx/bitmap.h" #include "wx/frame.h" + #include "wx/image.h" #include "wx/menu.h" #include "wx/msgdlg.h" #include "wx/log.h" @@ -607,6 +609,33 @@ MyFrame::MyFrame() testMenu->Append(Menu_Test_Normal, wxT("&Normal item")); testMenu->AppendSeparator(); testMenu->AppendCheckItem(Menu_Test_Check, wxT("&Check item")); + +#ifdef __WXMSW__ +#if wxUSE_IMAGE + wxBitmap bmpUnchecked(8, 8); + + wxImage imageChecked(8, 8); + imageChecked.Clear(0xff); + wxBitmap bmpChecked(imageChecked); + + wxMenuItem *checkedBitmapItem = new wxMenuItem(testMenu, wxID_ANY, + "Check item with bitmaps", "", wxITEM_CHECK); + checkedBitmapItem->SetBitmaps(bmpChecked, bmpUnchecked); + testMenu->Append(checkedBitmapItem); + + checkedBitmapItem = new wxMenuItem(testMenu, wxID_ANY, + "Check item with bitmaps set afterwards", "", wxITEM_CHECK); + testMenu->Append(checkedBitmapItem); + checkedBitmapItem->SetBitmaps(bmpChecked, bmpUnchecked); + + checkedBitmapItem = new wxMenuItem(testMenu, wxID_ANY, + "Check item with bitmaps set afterwards (initially checked)", "", wxITEM_CHECK); + testMenu->Append(checkedBitmapItem); + checkedBitmapItem->Check(); + checkedBitmapItem->SetBitmaps(bmpChecked, bmpUnchecked); +#endif +#endif + testMenu->AppendSeparator(); testMenu->AppendRadioItem(Menu_Test_Radio1, wxT("Radio item &1")); testMenu->AppendRadioItem(Menu_Test_Radio2, wxT("Radio item &2")); diff --git a/src/msw/menu.cpp b/src/msw/menu.cpp index acc9e48867..ee4e49936d 100644 --- a/src/msw/menu.cpp +++ b/src/msw/menu.cpp @@ -573,8 +573,8 @@ bool wxMenu::DoInsertOrAppend(wxMenuItem *pItem, size_t pos) if ( pItem->IsCheckable() ) { mii.fMask |= MIIM_CHECKMARKS; - mii.hbmpChecked = pItem->GetHBitmapForMenu(true); - mii.hbmpUnchecked = pItem->GetHBitmapForMenu(false); + mii.hbmpChecked = pItem->GetHBitmapForMenuCheckable(true); + mii.hbmpUnchecked = pItem->GetHBitmapForMenuCheckable(false); } else if ( pItem->GetBitmap().IsOk() ) { diff --git a/src/msw/menuitem.cpp b/src/msw/menuitem.cpp index a6593f35ed..62d7922cf3 100644 --- a/src/msw/menuitem.cpp +++ b/src/msw/menuitem.cpp @@ -807,8 +807,8 @@ void wxMenuItem::DoSetBitmap(const wxBitmap& bmp, bool bChecked) if ( IsCheckable() ) { mii.fMask = MIIM_CHECKMARKS; - mii.hbmpChecked = GetHBitmapForMenu(true); - mii.hbmpUnchecked = GetHBitmapForMenu(false); + mii.hbmpChecked = GetHBitmapForMenuCheckable(true); + mii.hbmpUnchecked = GetHBitmapForMenuCheckable(false); } else { @@ -1184,6 +1184,52 @@ bool wxMenuItem::OnDrawItem(wxDC& dc, const wxRect& rc, namespace { +// Returns the HBITMAP to use in MENUITEMINFO, either for hBmpItem (then +// forCheckableItem is false) or for hbmpChecked/hBmpUnchecked +// usage (forCheckableItem is true). The difference is needed because +// HBMMENU_CALLBACK can only be used for hBmpItem items. +HBITMAP DoGetHBitmapForMenu(wxMenuItem *menuItem, + bool checked, bool forCheckableItem) +{ + // Under versions of Windows older than Vista we can't pass HBITMAP + // directly as hbmpItem for 2 reasons: + // 1. We can't draw it with transparency then (this is not + // very important now but would be with themed menu bg) + // 2. Worse, Windows inverts the bitmap for the selected + // item and this looks downright ugly + // + // So we prefer to instead draw it ourselves in MSWOnDrawItem() by using + // HBMMENU_CALLBACK for normal menu items when inserting it. And use + // NULL for checkable menu items as hbmpChecked/hBmpUnchecked does not + // support HBMMENU_CALLBACK. + // + // However under Vista using HBMMENU_CALLBACK causes the entire menu to be + // drawn using the classic theme instead of the current one and it does + // handle transparency just fine so do use the real bitmap there +#if wxUSE_IMAGE + if ( wxGetWinVersion() >= wxWinVersion_Vista ) + { + wxBitmap bmp = menuItem->GetBitmap(checked); + if ( bmp.IsOk() ) + { + // we must use PARGB DIB for the menu bitmaps so ensure that we do + wxImage img(bmp.ConvertToImage()); + if ( !img.HasAlpha() ) + { + img.InitAlpha(); + menuItem->SetBitmap(img, checked); + } + + return GetHbitmapOf(menuItem->GetBitmap(checked)); + } + //else: bitmap is not set + return NULL; + } +#endif // wxUSE_IMAGE + + return forCheckableItem ? NULL : HBMMENU_CALLBACK; +} + // helper function for draw coloured check mark void DrawColorCheckMark(HDC hdc, int x, int y, int cx, int cy, HDC hdcCheckMask, int idxColor) { @@ -1376,44 +1422,17 @@ bool wxMenuItem::MSWMustUseOwnerDrawn() #endif // wxUSE_OWNER_DRAWN -// returns the HBITMAP to use in MENUITEMINFO +// returns the HBITMAP to use in MENUITEMINFO for hBmpItem member HBITMAP wxMenuItem::GetHBitmapForMenu(bool checked) { - // Under versions of Windows older than Vista we can't pass HBITMAP - // directly as hbmpItem for 2 reasons: - // 1. We can't draw it with transparency then (this is not - // very important now but would be with themed menu bg) - // 2. Worse, Windows inverts the bitmap for the selected - // item and this looks downright ugly - // - // So we prefer to instead draw it ourselves in MSWOnDrawItem().by using - // HBMMENU_CALLBACK when inserting it - // - // However under Vista using HBMMENU_CALLBACK causes the entire menu to be - // drawn using the classic theme instead of the current one and it does - // handle transparency just fine so do use the real bitmap there -#if wxUSE_IMAGE - if ( wxGetWinVersion() >= wxWinVersion_Vista ) - { - wxBitmap bmp = GetBitmap(checked); - if ( bmp.IsOk() ) - { - // we must use PARGB DIB for the menu bitmaps so ensure that we do - wxImage img(bmp.ConvertToImage()); - if ( !img.HasAlpha() ) - { - img.InitAlpha(); - SetBitmap(img, checked); - } + return ::DoGetHBitmapForMenu(this, checked, false /*for checkable item?*/); +} - return GetHbitmapOf(GetBitmap(checked)); - } - //else: bitmap is not set - return NULL; - } -#endif // wxUSE_IMAGE - - return HBMMENU_CALLBACK; +// returns the HBITMAP to use in MENUITEMINFO for hbmpChecked and +// hbmpUnchecked member +HBITMAP wxMenuItem::GetHBitmapForMenuCheckable(bool checked) +{ + return ::DoGetHBitmapForMenu(this, checked, true /*for checkable item?*/); } int wxMenuItem::MSGetMenuItemPos() const