X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/8907154c1a8a6882c6797d1f16393ddfb23e7f3a..815399d5bb73387364936a85a61e33cdbfb81193:/src/msw/ownerdrw.cpp diff --git a/src/msw/ownerdrw.cpp b/src/msw/ownerdrw.cpp index 1931e8c11c..cf3cc95bda 100644 --- a/src/msw/ownerdrw.cpp +++ b/src/msw/ownerdrw.cpp @@ -1,5 +1,5 @@ /////////////////////////////////////////////////////////////////////////////// -// Name: msw/ownerdrw.cpp +// Name: src/msw/ownerdrw.cpp // Purpose: implementation of wxOwnerDrawn class // Author: Vadim Zeitlin // Modified by: @@ -11,73 +11,131 @@ // For compilers that support precompilation, includes "wx.h". #include "wx/wxprec.h" -#include "wx/msw/private.h" #ifdef __BORLANDC__ -#pragma hdrstop + #pragma hdrstop #endif +#if wxUSE_OWNER_DRAWN + #ifndef WX_PRECOMP - #include "wx/window.h" - #include "wx/msw/private.h" - #include "wx/font.h" - #include "wx/bitmap.h" - #include "wx/dcmemory.h" - #include "wx/menu.h" - #include "wx/utils.h" + #include "wx/window.h" + #include "wx/font.h" + #include "wx/bitmap.h" + #include "wx/image.h" + #include "wx/dcmemory.h" + #include "wx/menu.h" + #include "wx/utils.h" + #include "wx/settings.h" + #include "wx/menuitem.h" + #include "wx/module.h" #endif -#include "wx/settings.h" #include "wx/ownerdrw.h" -#include "wx/menuitem.h" #include "wx/fontutil.h" -#include "wx/module.h" -#if wxUSE_OWNER_DRAWN +#include "wx/msw/private.h" #ifndef SPI_GETKEYBOARDCUES #define SPI_GETKEYBOARDCUES 0x100A #endif +#ifndef DSS_HIDEPREFIX +#define DSS_HIDEPREFIX 0x0200 +#endif + class wxMSWSystemMenuFontModule : public wxModule { public: - virtual bool OnInit() { - ms_systemMenuFont = new wxFont; + return true; + } -#if defined(__WXMSW__) && defined(__WIN32__) && defined(SM_CXMENUCHECK) - NONCLIENTMETRICS nm; - nm.cbSize = sizeof(NONCLIENTMETRICS); - SystemParametersInfo(SPI_GETNONCLIENTMETRICS,0,&nm,0); + virtual void OnExit() + { + if ( ms_systemMenuFont ) + { + delete ms_systemMenuFont; + ms_systemMenuFont = NULL; + } + } - ms_systemMenuButtonWidth = nm.iMenuHeight; - ms_systemMenuHeight = nm.iMenuHeight; + static const wxFont& GetSystemMenuFont() + { + if ( !ms_systemMenuFont ) + DoInitFont(); - // create menu font - wxNativeFontInfo info; - memcpy(&info.lf, &nm.lfMenuFont, sizeof(LOGFONT)); - ms_systemMenuFont->Create(info); + return *ms_systemMenuFont; + } - if (SystemParametersInfo(SPI_GETKEYBOARDCUES, 0, &ms_showCues, 0) == 0) - ms_showCues = true; -#endif + static int GetSystemMenuHeight() + { + if ( !ms_systemMenuHeight ) + DoInitMetrics(); - return true; + return ms_systemMenuHeight; } - virtual void OnExit() + static bool AlwaysShowCues() { - delete ms_systemMenuFont; - ms_systemMenuFont = NULL; + if ( !ms_systemMenuHeight ) + DoInitMetrics(); + + return ms_alwaysShowCues; } - static wxFont* ms_systemMenuFont; - static int ms_systemMenuButtonWidth; // windows clean install default - static int ms_systemMenuHeight; // windows clean install default - static bool ms_showCues; private: + static NONCLIENTMETRICS GetNCM() + { + WinStruct nm; + if ( !::SystemParametersInfo(SPI_GETNONCLIENTMETRICS, 0, &nm, 0) ) + { +#if WINVER >= 0x0600 + // a new field has been added to NONCLIENTMETRICS under Vista, so + // the call to SystemParametersInfo() fails if we use the struct + // size incorporating this new value on an older system -- retry + // without it + nm.cbSize -= sizeof(int); + if ( !::SystemParametersInfo(SPI_GETNONCLIENTMETRICS, 0, &nm, 0) ) +#endif // WINVER >= 0x0600 + { + // maybe we should initialize the struct with some defaults? + wxLogLastError(_T("SystemParametersInfo(SPI_GETNONCLIENTMETRICS)")); + } + } + + return nm; + } + + static void DoInitMetrics() + { + // iMenuHeight is the menu bar height and the menu items are less tall, + // although I don't know by how much -- below is the value for my system + ms_systemMenuHeight = GetNCM().iMenuHeight - 4; + + wxASSERT_MSG( ms_systemMenuHeight > 0, + "menu height should be positive" ); + + if ( ::SystemParametersInfo(SPI_GETKEYBOARDCUES, 0, + &ms_alwaysShowCues, 0) == 0 ) + { + // if it's not supported, we must be on an old Windows version + // which always shows them + ms_alwaysShowCues = true; + } + } + + static void DoInitFont() + { + ms_systemMenuFont = new wxFont(wxNativeFontInfo(GetNCM().lfMenuFont)); + } + + static wxFont* ms_systemMenuFont; + static int ms_systemMenuHeight; + static bool ms_alwaysShowCues; + + DECLARE_DYNAMIC_CLASS(wxMSWSystemMenuFontModule) }; @@ -86,19 +144,12 @@ private: // SystemParametersInfo() call. wxFont* wxMSWSystemMenuFontModule::ms_systemMenuFont = NULL; -int wxMSWSystemMenuFontModule::ms_systemMenuButtonWidth = 18; // windows clean install default -int wxMSWSystemMenuFontModule::ms_systemMenuHeight = 18; // windows clean install default -bool wxMSWSystemMenuFontModule::ms_showCues = true; +int wxMSWSystemMenuFontModule::ms_systemMenuHeight = 0; +bool wxMSWSystemMenuFontModule::ms_alwaysShowCues = false; IMPLEMENT_DYNAMIC_CLASS(wxMSWSystemMenuFontModule, wxModule) -// temporary hack to implement wxOwnerDrawn::IsMenuItem() without breaking -// backwards compatibility -#if wxCHECK_VERSION(2, 7, 0) - #pragma warning "TODO: remove gs_menuItems hack" -#endif - // VC++ 6 gives a warning here: // // return type for 'OwnerDrawnSet_wxImplementation_HashTable::iterator:: @@ -106,12 +157,12 @@ IMPLEMENT_DYNAMIC_CLASS(wxMSWSystemMenuFontModule, wxModule) // a UDT. Will produce errors if applied using infix notation. // // shut it down -#ifdef __VISUALC__ - #if __VISUALC__ <= 1300 +#if defined __VISUALC__ && __VISUALC__ <= 1300 + #if __VISUALC__ >= 1200 #pragma warning(push) - #pragma warning(disable: 4284) #define POP_WARNINGS #endif + #pragma warning(disable: 4284) #endif #include "wx/hashset.h" @@ -121,8 +172,6 @@ WX_DECLARE_HASH_SET(wxOwnerDrawn*, wxPointerHash, wxPointerEqual, OwnerDrawnSet) #pragma warning(pop) #endif -static OwnerDrawnSet gs_menuItems; - // ============================================================================ // implementation of wxOwnerDrawn class // ============================================================================ @@ -134,7 +183,7 @@ wxOwnerDrawn::wxOwnerDrawn(const wxString& str, bool bMenuItem) : m_strName(str) { - if (ms_nDefaultMarginWidth == 0) + if ( ms_nDefaultMarginWidth == 0 ) { ms_nDefaultMarginWidth = ::GetSystemMetrics(SM_CXMENUCHECK) + wxSystemSettings::GetMetric(wxSYS_EDGE_X); @@ -143,36 +192,22 @@ wxOwnerDrawn::wxOwnerDrawn(const wxString& str, m_bCheckable = bCheckable; m_bOwnerDrawn = false; + m_isMenuItem = bMenuItem; m_nHeight = 0; m_nMarginWidth = ms_nLastMarginWidth; - m_nMinHeight = wxMSWSystemMenuFontModule::ms_systemMenuHeight; - - m_bmpDisabled = wxNullBitmap; - - // TODO: we can't add new m_isMenuItem field in 2.6, so we use this hack - // with the map, but do add m_isMenuItem in 2.7 - if ( bMenuItem ) - { - gs_menuItems.insert(this); - } } wxOwnerDrawn::~wxOwnerDrawn() { - // TODO: remove this in 2.7 - gs_menuItems.erase(this); } bool wxOwnerDrawn::IsMenuItem() const { - // TODO: in 2.7, replace this with simple "return m_isMenuItem" - - // some versions of mingw have problems without const_cast when wxUSE_STL=1 - return gs_menuItems.count(wx_const_cast(wxOwnerDrawn *, this)) == 1; + return m_isMenuItem; } -// these items will be set during the first invocation of the c'tor, +// these items will be set during the first invocation of the ctor, // because the values will be determined by checking the system settings, // which is a chunk of code size_t wxOwnerDrawn::ms_nDefaultMarginWidth = 0; @@ -188,7 +223,7 @@ wxFont wxOwnerDrawn::GetFontToUse() const if ( !font.Ok() ) { if ( IsMenuItem() ) - font = *wxMSWSystemMenuFontModule::ms_systemMenuFont; + font = wxMSWSystemMenuFontModule::GetSystemMenuFont(); if ( !font.Ok() ) font = *wxNORMAL_FONT; @@ -213,13 +248,16 @@ bool wxOwnerDrawn::OnMeasureItem(size_t *pwidth, size_t *pheight) // placed on top of each other. if ( !m_strAccel.empty() ) { - str.Pad(str.Length()%8); + str.Pad(str.length()%8); str += m_strAccel; } dc.SetFont(GetFontToUse()); - dc.GetTextExtent(str, (long *)pwidth, (long *)pheight); + wxCoord w, h; + dc.GetTextExtent(str, &w, &h); + *pwidth = w; + *pheight = h; // add space at the end of the menu for the submenu expansion arrow // this will also allow offsetting the accel string from the right edge @@ -234,10 +272,9 @@ bool wxOwnerDrawn::OnMeasureItem(size_t *pwidth, size_t *pheight) // increase size to accommodate bigger bitmaps if necessary if (m_bmpChecked.Ok()) { - // Is BMP height larger then text height? - size_t adjustedHeight = m_bmpChecked.GetHeight() + - 2*wxSystemSettings::GetMetric(wxSYS_EDGE_Y); - if (*pheight < adjustedHeight) + // Is BMP height larger than text height? + size_t adjustedHeight = m_bmpChecked.GetHeight(); + if ( *pheight < adjustedHeight ) *pheight = adjustedHeight; const size_t widthBmp = m_bmpChecked.GetWidth(); @@ -257,8 +294,9 @@ bool wxOwnerDrawn::OnMeasureItem(size_t *pwidth, size_t *pheight) *pwidth += 4; // make sure that this item is at least as tall as the system menu height - if ( *pheight < m_nMinHeight ) - *pheight = m_nMinHeight; + const size_t heightStd = wxMSWSystemMenuFontModule::GetSystemMenuHeight(); + if ( *pheight < heightStd ) + *pheight = heightStd; // remember height for use in OnDrawItem m_nHeight = *pheight; @@ -330,7 +368,8 @@ bool wxOwnerDrawn::OnDrawItem(wxDC& dc, } - HDC hdc = GetHdcOf(dc); + wxMSWDCImpl *impl = (wxMSWDCImpl*) dc.GetImpl(); + HDC hdc = GetHdcOf(*impl); COLORREF colOldText = ::SetTextColor(hdc, colText), colOldBack = ::SetBkColor(hdc, colBack); @@ -374,14 +413,29 @@ bool wxOwnerDrawn::OnDrawItem(wxDC& dc, xText += 3; // separate text from the highlight rectangle SIZE sizeRect; - ::GetTextExtentPoint32(hdc, strMenuText.c_str(), strMenuText.Length(), &sizeRect); - ::DrawState(hdc, NULL, NULL, - (LPARAM)strMenuText.c_str(), strMenuText.length(), - xText, rc.y + (int) ((rc.GetHeight()-sizeRect.cy)/2.0), // centre text vertically - rc.GetWidth()-margin, sizeRect.cy, - DST_PREFIXTEXT | - (((st & wxODDisabled) && !(st & wxODSelected)) ? DSS_DISABLED : 0) | - (((st & wxODHidePrefix) && !wxMSWSystemMenuFontModule::ms_showCues) ? 512 : 0)); // 512 == DSS_HIDEPREFIX + ::GetTextExtentPoint32(hdc, strMenuText.c_str(), strMenuText.length(), &sizeRect); + + int flags = DST_PREFIXTEXT; + if ( (st & wxODDisabled) && !(st & wxODSelected) ) + flags |= DSS_DISABLED; + + if ( (st & wxODHidePrefix) && + !wxMSWSystemMenuFontModule::AlwaysShowCues() ) + flags |= DSS_HIDEPREFIX; + + ::DrawState + ( + hdc, + NULL, + NULL, + (LPARAM)strMenuText.wx_str(), + strMenuText.length(), + xText, + rc.y + (rc.GetHeight() - sizeRect.cy + 1)/2, // centre vertically + rc.GetWidth() - margin, + sizeRect.cy, + flags + ); // ::SetTextAlign(hdc, TA_RIGHT) doesn't work with DSS_DISABLED or DSS_MONO // as the last parameter in DrawState() (at least with Windows98). So we have @@ -393,7 +447,8 @@ bool wxOwnerDrawn::OnDrawItem(wxDC& dc, // right align accel string with right edge of menu ( offset by the // margin width ) ::DrawState(hdc, NULL, NULL, - (LPARAM)m_strAccel.c_str(), m_strAccel.length(), + (LPARAM)m_strAccel.wx_str(), + m_strAccel.length(), rc.GetWidth()-16-accel_width, rc.y+(int) ((rc.GetHeight()-sizeRect.cy)/2.0), 0, 0, DST_TEXT | @@ -443,15 +498,26 @@ bool wxOwnerDrawn::OnDrawItem(wxDC& dc, if ( !bmp.Ok() ) { - // for not checkable bitmaps we should always use unchecked one because - // their checked bitmap is not set + // for not checkable bitmaps we should always use unchecked one + // because their checked bitmap is not set bmp = GetBitmap(!IsCheckable() || (st & wxODChecked)); + +#if wxUSE_IMAGE + if ( bmp.Ok() && st & wxODDisabled ) + { + // we need to grey out the bitmap as we don't have any specific + // disabled bitmap + wxImage imgGrey = bmp.ConvertToImage().ConvertToGreyscale(); + if ( imgGrey.Ok() ) + bmp = wxBitmap(imgGrey); + } +#endif // wxUSE_IMAGE } if ( bmp.Ok() ) { wxMemoryDC dcMem(&dc); - dcMem.SelectObject(bmp); + dcMem.SelectObjectAsSource(bmp); // center bitmap int nBmpWidth = bmp.GetWidth(), @@ -486,4 +552,3 @@ bool wxOwnerDrawn::OnDrawItem(wxDC& dc, #endif // wxUSE_OWNER_DRAWN -