1 /////////////////////////////////////////////////////////////////////////////// 
   2 // Name:        src/msw/menuitem.cpp 
   3 // Purpose:     wxMenuItem implementation 
   4 // Author:      Vadim Zeitlin 
   8 // Copyright:   (c) 1998 Vadim Zeitlin <zeitlin@dptmaths.ens-cachan.fr> 
   9 // Licence:     wxWindows licence 
  10 /////////////////////////////////////////////////////////////////////////////// 
  12 // =========================================================================== 
  14 // =========================================================================== 
  16 // --------------------------------------------------------------------------- 
  18 // --------------------------------------------------------------------------- 
  20 // For compilers that support precompilation, includes "wx.h". 
  21 #include "wx/wxprec.h" 
  29 #include "wx/menuitem.h" 
  30 #include "wx/stockitem.h" 
  34     #include "wx/bitmap.h" 
  35     #include "wx/settings.h" 
  36     #include "wx/window.h" 
  38     #include "wx/string.h" 
  47 #include "wx/msw/private.h" 
  48 #include "wx/msw/dc.h" 
  51 // Implemented in menu.cpp 
  52 UINT 
GetMenuState(HMENU hMenu
, UINT id
, UINT flags
) ; 
  56     #include "wx/msw/uxtheme.h" 
  59 // --------------------------------------------------------------------------- 
  61 // --------------------------------------------------------------------------- 
  64 #define GetHMenuOf(menu)    ((HMENU)menu->GetHMenu()) 
  66 // ============================================================================ 
  68 // ============================================================================ 
  72 #include "wx/fontutil.h" 
  73 #include "wx/msw/private/metrics.h" 
  75 #ifndef SPI_GETKEYBOARDCUES 
  76 #define SPI_GETKEYBOARDCUES 0x100A 
  79 #ifndef DSS_HIDEPREFIX 
  80 #define DSS_HIDEPREFIX  0x0200 
  87     MENU_MENUITEM_TMSCHEMA 
= 1, 
  88     MENU_SEPARATOR_TMSCHEMA 
= 6, 
  89     MENU_POPUPBACKGROUND 
= 9, 
  90     MENU_POPUPBORDERS 
= 10, 
  92     MENU_POPUPCHECKBACKGROUND 
= 12, 
  93     MENU_POPUPGUTTER 
= 13, 
  95     MENU_POPUPSEPARATOR 
= 15, 
  96     MENU_POPUPSUBMENU 
= 16, 
 108 enum POPUPCHECKBACKGROUNDSTATES
 
 115 enum POPUPCHECKSTATES
 
 117     MC_CHECKMARKNORMAL 
= 1, 
 118     MC_CHECKMARKDISABLED 
= 2, 
 120     MC_BULLETDISABLED 
= 4, 
 123 const int TMT_MENUFONT       
= 803; 
 124 const int TMT_BORDERSIZE     
= 2403; 
 125 const int TMT_CONTENTMARGINS 
= 3602; 
 126 const int TMT_SIZINGMARGINS  
= 3601; 
 128 #endif // wxUSE_UXTHEME 
 130 #endif // wxUSE_OWNER_DRAWN 
 132 // ---------------------------------------------------------------------------- 
 133 // dynamic classes implementation 
 134 // ---------------------------------------------------------------------------- 
 136 #if wxUSE_EXTENDED_RTTI 
 138 bool wxMenuItemStreamingCallback( const wxObject 
*object
, wxWriter 
* , wxPersister 
* , wxxVariantArray 
& ) 
 140     const wxMenuItem 
* mitem 
= dynamic_cast<const wxMenuItem
*>(object
) ; 
 141     if ( mitem
->GetMenu() && !mitem
->GetMenu()->GetTitle().empty() ) 
 143         // we don't stream out the first two items for menus with a title, they will be reconstructed 
 144         if ( mitem
->GetMenu()->FindItemByPosition(0) == mitem 
|| mitem
->GetMenu()->FindItemByPosition(1) == mitem 
) 
 150 wxBEGIN_ENUM( wxItemKind 
) 
 151     wxENUM_MEMBER( wxITEM_SEPARATOR 
) 
 152     wxENUM_MEMBER( wxITEM_NORMAL 
) 
 153     wxENUM_MEMBER( wxITEM_CHECK 
) 
 154     wxENUM_MEMBER( wxITEM_RADIO 
) 
 155 wxEND_ENUM( wxItemKind 
) 
 157 IMPLEMENT_DYNAMIC_CLASS_XTI_CALLBACK(wxMenuItem
, wxObject
,"wx/menuitem.h",wxMenuItemStreamingCallback
) 
 159 wxBEGIN_PROPERTIES_TABLE(wxMenuItem
) 
 160     wxPROPERTY( Parent
,wxMenu
*, SetMenu
, GetMenu
, EMPTY_MACROVALUE 
, 0 /*flags*/ , wxT("Helpstring") , wxT("group") ) 
 161     wxPROPERTY( Id
,int, SetId
, GetId
, EMPTY_MACROVALUE 
, 0 /*flags*/ , wxT("Helpstring") , wxT("group") ) 
 162     wxPROPERTY( Text
, wxString 
, SetText
, GetText
, wxString(), 0 /*flags*/ , wxT("Helpstring") , wxT("group") ) 
 163     wxPROPERTY( Help
, wxString 
, SetHelp
, GetHelp
, wxString(), 0 /*flags*/ , wxT("Helpstring") , wxT("group") ) 
 164     wxREADONLY_PROPERTY( Kind
, wxItemKind 
, GetKind 
, EMPTY_MACROVALUE 
, 0 /*flags*/ , wxT("Helpstring") , wxT("group") ) 
 165     wxPROPERTY( SubMenu
,wxMenu
*, SetSubMenu
, GetSubMenu
, EMPTY_MACROVALUE 
, 0 /*flags*/ , wxT("Helpstring") , wxT("group") ) 
 166     wxPROPERTY( Enabled 
, bool , Enable 
, IsEnabled 
, wxxVariant((bool)true) , 0 /*flags*/ , wxT("Helpstring") , wxT("group")) 
 167     wxPROPERTY( Checked 
, bool , Check 
, IsChecked 
, wxxVariant((bool)false) , 0 /*flags*/ , wxT("Helpstring") , wxT("group")) 
 168     wxPROPERTY( Checkable 
, bool , SetCheckable 
, IsCheckable 
, wxxVariant((bool)false) , 0 /*flags*/ , wxT("Helpstring") , wxT("group")) 
 169 wxEND_PROPERTIES_TABLE() 
 171 wxBEGIN_HANDLERS_TABLE(wxMenuItem
) 
 172 wxEND_HANDLERS_TABLE() 
 174 wxDIRECT_CONSTRUCTOR_6( wxMenuItem 
, wxMenu
* , Parent 
, int , Id 
, wxString 
, Text 
, wxString 
, Help 
, wxItemKind 
, Kind 
, wxMenu
* , SubMenu  
) 
 176 IMPLEMENT_DYNAMIC_CLASS(wxMenuItem
, wxObject
) 
 179 // ---------------------------------------------------------------------------- 
 181 // ---------------------------------------------------------------------------- 
 183 #if wxUSE_OWNER_DRAWN 
 188 // helper class to keep information about metrics and other stuff 
 189 // needed for measuring and drawing menu item 
 209     Margins ItemMargin
;         // popup item margins 
 211     Margins CheckMargin
;        // popup check margins 
 212     Margins CheckBgMargin
;      // popup check background margins 
 214     Margins ArrowMargin
;        // popup submenu arrow margins 
 216     Margins SeparatorMargin
;    // popup separator margins 
 218     SIZE CheckSize
;             // popup check size metric 
 219     SIZE ArrowSize
;             // popup submenu arrow size metric 
 220     SIZE SeparatorSize
;         // popup separator size metric 
 222     int TextBorder
;             // popup border space between 
 223                                 // item text and gutter 
 225     int AccelBorder
;            // popup border space between 
 226                                 // item text and accelerator 
 228     int ArrowBorder
;            // popup border space between 
 229                                 // item accelerator and submenu arrow 
 231     int Offset
;                 // system added space at the end of the menu, 
 232                                 // add this offset for remove the extra space 
 234     wxFont Font
;                // default menu font 
 236     bool AlwaysShowCues
;        // must keyboard cues always be shown? 
 238     bool Theme
;                 // is data initialized for FullTheme? 
 240     static const MenuDrawData
* Get() 
 243         bool theme 
= MenuLayout() == FullTheme
; 
 244         if ( ms_instance
->Theme 
!= theme 
) 
 246     #endif // wxUSE_UXTHEME 
 257     // get the theme engine or NULL if themes 
 258     // are not available or not supported on menu 
 259     static wxUxThemeEngine 
*GetUxThemeEngine() 
 262         if ( MenuLayout() == FullTheme 
) 
 263             return wxUxThemeEngine::GetIfActive(); 
 264     #endif // wxUSE_UXTHEME 
 271         FullTheme
,      // full menu themes (Vista or new) 
 272         PseudoTheme
,    // pseudo menu themes (on XP) 
 276     static MenuLayoutType 
MenuLayout() 
 278         MenuLayoutType menu 
= Classic
; 
 280         if ( wxUxThemeEngine::GetIfActive() != NULL 
) 
 282             static wxWinVersion ver 
= wxGetWinVersion(); 
 283             if ( ver 
>= wxWinVersion_Vista 
) 
 285             else if ( ver 
== wxWinVersion_XP 
) 
 288     #endif // wxUSE_UXTHEME 
 295     static MenuDrawData
* ms_instance
; 
 298 MenuDrawData
* MenuDrawData::ms_instance 
= NULL
; 
 300 MenuDrawData s_menuData
; 
 302 void MenuDrawData::Init() 
 305     wxUxThemeEngine
* theme 
= GetUxThemeEngine(); 
 308         wxWindow
* window 
= static_cast<wxApp
*>(wxApp::GetInstance())->GetTopWindow(); 
 309         wxUxThemeHandle 
hTheme(window
, L
"MENU"); 
 311         theme
->GetThemeMargins(hTheme
, NULL
, MENU_POPUPITEM
, 0, 
 312                                TMT_CONTENTMARGINS
, NULL
, 
 313                                reinterpret_cast<MARGINS
*>(&ItemMargin
)); 
 315         theme
->GetThemeMargins(hTheme
, NULL
, MENU_POPUPCHECK
, 0, 
 316                                TMT_CONTENTMARGINS
, NULL
, 
 317                                reinterpret_cast<MARGINS
*>(&CheckMargin
)); 
 318         theme
->GetThemeMargins(hTheme
, NULL
, MENU_POPUPCHECKBACKGROUND
, 0, 
 319                                TMT_CONTENTMARGINS
, NULL
, 
 320                                reinterpret_cast<MARGINS
*>(&CheckBgMargin
)); 
 322         theme
->GetThemeMargins(hTheme
, NULL
, MENU_POPUPSUBMENU
, 0, 
 323                                TMT_CONTENTMARGINS
, NULL
, 
 324                                reinterpret_cast<MARGINS
*>(&ArrowMargin
)); 
 326         theme
->GetThemeMargins(hTheme
, NULL
, MENU_POPUPSEPARATOR
, 0, 
 327                                TMT_SIZINGMARGINS
, NULL
, 
 328                                reinterpret_cast<MARGINS
*>(&SeparatorMargin
)); 
 330         theme
->GetThemePartSize(hTheme
, NULL
, MENU_POPUPCHECK
, 0, 
 331                                 NULL
, TS_TRUE
, &CheckSize
); 
 333         theme
->GetThemePartSize(hTheme
, NULL
, MENU_POPUPSUBMENU
, 0, 
 334                                 NULL
, TS_TRUE
, &ArrowSize
); 
 336         theme
->GetThemePartSize(hTheme
, NULL
, MENU_POPUPSEPARATOR
, 0, 
 337                                 NULL
, TS_TRUE
, &SeparatorSize
); 
 339         theme
->GetThemeInt(hTheme
, MENU_POPUPBACKGROUND
, 0, TMT_BORDERSIZE
, &TextBorder
); 
 346         wxNativeFontInfo fontInfo
; 
 347         theme
->GetThemeSysFont(hTheme
, TMT_MENUFONT
, &fontInfo
.lf
); 
 348         Font 
= wxFont(fontInfo
); 
 352         // native menu doesn't uses the vertical margins 
 353         ItemMargin
.top 
= ItemMargin
.bottom 
= 0; 
 355         // native menu uses small top margin for separator 
 356         if ( SeparatorMargin
.top 
>= 2 ) 
 357             SeparatorMargin
.top 
-= 2; 
 360 #endif // wxUSE_UXTHEME 
 362         const NONCLIENTMETRICS
& metrics 
= wxMSWImpl::GetNonClientMetrics(); 
 364         ItemMargin 
= Margins(); 
 367         CheckMargin
.right  
= ::GetSystemMetrics(SM_CXEDGE
); 
 369         CheckMargin
.bottom 
= ::GetSystemMetrics(SM_CYEDGE
); 
 371         CheckBgMargin 
= Margins(); 
 373         CheckSize
.cx 
= ::GetSystemMetrics(SM_CXMENUCHECK
); 
 374         CheckSize
.cy 
= ::GetSystemMetrics(SM_CYMENUCHECK
); 
 376         ArrowMargin 
= Margins(); 
 378         ArrowSize 
= CheckSize
; 
 380         // separator height with margins 
 381         int sepFullSize 
= metrics
.iMenuHeight 
/ 2; 
 383         SeparatorMargin
.left 
= 
 384         SeparatorMargin
.right 
= 1; 
 385         SeparatorMargin
.top 
= 
 386         SeparatorMargin
.bottom 
= sepFullSize 
/ 2 - 1; 
 388         SeparatorSize
.cx 
= 1; 
 389         SeparatorSize
.cy 
= sepFullSize 
- SeparatorMargin
.top 
- SeparatorMargin
.bottom
; 
 397         Font 
= wxFont(wxNativeFontInfo(metrics
.lfMenuFont
)); 
 403     if ( ::SystemParametersInfo(SPI_GETKEYBOARDCUES
, 0, &value
, 0) == 0 ) 
 405         // if it's not supported, we must be on an old Windows version 
 406         // which always shows them 
 410     AlwaysShowCues 
= value 
== 1; 
 414 } // anonymous namespace 
 416 #endif // wxUSE_OWNER_DRAWN 
 422 wxMenuItem::wxMenuItem(wxMenu 
*pParentMenu
, 
 424                        const wxString
& text
, 
 425                        const wxString
& strHelp
, 
 428           : wxMenuItemBase(pParentMenu
, id
, text
, strHelp
, kind
, pSubMenu
) 
 433 #if WXWIN_COMPATIBILITY_2_8 
 434 wxMenuItem::wxMenuItem(wxMenu 
*parentMenu
, 
 436                        const wxString
& text
, 
 437                        const wxString
& help
, 
 440           : wxMenuItemBase(parentMenu
, id
, text
, help
, 
 441                            isCheckable 
? wxITEM_CHECK 
: wxITEM_NORMAL
, subMenu
) 
 447 void wxMenuItem::Init() 
 449     m_radioGroup
.start 
= -1; 
 450     m_isRadioGroupStart 
= false; 
 452 #if  wxUSE_OWNER_DRAWN 
 454     // when the color is not valid, wxOwnerDraw takes the default ones. 
 455     // If we set the colors here and they are changed by the user during 
 456     // the execution, then the colors are not updated until the application 
 457     // is restarted and our menus look bad 
 458     SetTextColour(wxNullColour
); 
 459     SetBackgroundColour(wxNullColour
); 
 461     // setting default colors switched ownerdraw on: switch it off again 
 462     SetOwnerDrawn(false); 
 464     //  switch ownerdraw back on if using a non default margin 
 465     if ( !IsSeparator() ) 
 466         SetMarginWidth(GetMarginWidth()); 
 468 #endif // wxUSE_OWNER_DRAWN 
 471 wxMenuItem::~wxMenuItem() 
 478 // return the id for calling Win32 API functions 
 479 WXWPARAM 
wxMenuItem::GetMSWId() const 
 481     // we must use ids in unsigned short range with Windows functions, if we 
 482     // pass ids > USHRT_MAX to them they get very confused (e.g. start 
 483     // generating WM_COMMAND messages with negative high word of wParam), so 
 484     // use the cast to ensure the id is in range 
 485     return m_subMenu 
? wxPtrToUInt(m_subMenu
->GetHMenu()) 
 486                      : static_cast<unsigned short>(GetId()); 
 492 bool wxMenuItem::IsChecked() const 
 494     // fix that RTTI is always getting the correct state (separators cannot be 
 495     // checked, but the Windows call below returns true 
 499     // the item might not be attached to a menu yet 
 501     // TODO: shouldn't we just always call the base class version? It seems 
 502     //       like it ought to always be in sync 
 504         return wxMenuItemBase::IsChecked(); 
 506     HMENU hmenu 
= GetHMenuOf(m_parentMenu
); 
 507     int flag 
= ::GetMenuState(hmenu
, GetMSWId(), MF_BYCOMMAND
); 
 509     return (flag 
& MF_CHECKED
) != 0; 
 515 void wxMenuItem::SetAsRadioGroupStart() 
 517     m_isRadioGroupStart 
= true; 
 520 void wxMenuItem::SetRadioGroupStart(int start
) 
 522     wxASSERT_MSG( !m_isRadioGroupStart
, 
 523                   wxT("should only be called for the next radio items") ); 
 525     m_radioGroup
.start 
= start
; 
 528 void wxMenuItem::SetRadioGroupEnd(int end
) 
 530     wxASSERT_MSG( m_isRadioGroupStart
, 
 531                   wxT("should only be called for the first radio item") ); 
 533     m_radioGroup
.end 
= end
; 
 539 void wxMenuItem::Enable(bool enable
) 
 541     if ( m_isEnabled 
== enable 
) 
 546         long rc 
= EnableMenuItem(GetHMenuOf(m_parentMenu
), 
 549                                  (enable 
? MF_ENABLED 
: MF_GRAYED
)); 
 553             wxLogLastError(wxT("EnableMenuItem")); 
 557     wxMenuItemBase::Enable(enable
); 
 560 void wxMenuItem::Check(bool check
) 
 562     wxCHECK_RET( IsCheckable(), wxT("only checkable items may be checked") ); 
 564     if ( m_isChecked 
== check 
) 
 569         int flags 
= check 
? MF_CHECKED 
: MF_UNCHECKED
; 
 570         HMENU hmenu 
= GetHMenuOf(m_parentMenu
); 
 572         if ( GetKind() == wxITEM_RADIO 
) 
 574             // it doesn't make sense to uncheck a radio item -- what would this 
 579             // get the index of this item in the menu 
 580             const wxMenuItemList
& items 
= m_parentMenu
->GetMenuItems(); 
 581             int pos 
= items
.IndexOf(this); 
 582             wxCHECK_RET( pos 
!= wxNOT_FOUND
, 
 583                          wxT("menuitem not found in the menu items list?") ); 
 585             // get the radio group range 
 589             if ( m_isRadioGroupStart 
) 
 591                 // we already have all information we need 
 593                 end 
= m_radioGroup
.end
; 
 595             else // next radio group item 
 597                 // get the radio group end from the start item 
 598                 start 
= m_radioGroup
.start
; 
 599                 end 
= items
.Item(start
)->GetData()->m_radioGroup
.end
; 
 603             // calling CheckMenuRadioItem() with such parameters hangs my system 
 604             // (NT4 SP6) and I suspect this could happen to the others as well, 
 606             wxCHECK_RET( start 
!= -1 && end 
!= -1, 
 607                          wxT("invalid ::CheckMenuRadioItem() parameter(s)") ); 
 609             if ( !::CheckMenuRadioItem(hmenu
, 
 610                                        start
,   // the first radio group item 
 612                                        pos
,     // the one to check 
 615                 wxLogLastError(wxT("CheckMenuRadioItem")); 
 619             // also uncheck all the other items in this radio group 
 620             wxMenuItemList::compatibility_iterator node 
= items
.Item(start
); 
 621             for ( int n 
= start
; n 
<= end 
&& node
; n
++ ) 
 625                     node
->GetData()->m_isChecked 
= false; 
 628                 node 
= node
->GetNext(); 
 633             if ( ::CheckMenuItem(hmenu
, 
 635                                  MF_BYCOMMAND 
| flags
) == (DWORD
)-1 ) 
 637                 wxFAIL_MSG(wxT("CheckMenuItem() failed, item not in the menu?")); 
 642     wxMenuItemBase::Check(check
); 
 645 void wxMenuItem::SetItemLabel(const wxString
& txt
) 
 649     // don't do anything if label didn't change 
 653     // wxMenuItemBase will do stock ID checks 
 654     wxMenuItemBase::SetItemLabel(text
); 
 656     // the item can be not attached to any menu yet and SetItemLabel() is still 
 657     // valid to call in this case and should do nothing else 
 662     m_parentMenu
->UpdateAccel(this); 
 663 #endif // wxUSE_ACCEL 
 665     const UINT id 
= GetMSWId(); 
 666     HMENU hMenu 
= GetHMenuOf(m_parentMenu
); 
 667     if ( !hMenu 
|| ::GetMenuState(hMenu
, id
, MF_BYCOMMAND
) == (UINT
)-1 ) 
 670 #if wxUSE_OWNER_DRAWN 
 671     if ( IsOwnerDrawn() ) 
 673         // we don't need to do anything for owner drawn items, they will redraw 
 674         // themselves using the new text the next time they're displayed 
 677 #endif // owner drawn 
 679     // update the text of the native menu item 
 680     WinStruct
<MENUITEMINFO
> info
; 
 682     // surprisingly, calling SetMenuItemInfo() with just MIIM_STRING doesn't 
 683     // work as it resets the menu bitmap, so we need to first get the old item 
 684     // state and then modify it 
 685     const bool isLaterThanWin95 
= wxGetWinVersion() > wxWinVersion_95
; 
 686     info
.fMask 
= MIIM_STATE 
| 
 691     if ( isLaterThanWin95 
) 
 692         info
.fMask 
|= MIIM_BITMAP 
| MIIM_FTYPE
; 
 694         info
.fMask 
|= MIIM_TYPE
; 
 695     if ( !::GetMenuItemInfo(hMenu
, id
, FALSE
, &info
) ) 
 697         wxLogLastError(wxT("GetMenuItemInfo")); 
 701     if ( isLaterThanWin95 
) 
 702         info
.fMask 
|= MIIM_STRING
; 
 703     //else: MIIM_TYPE already specified 
 704     info
.dwTypeData 
= (LPTSTR
)m_text
.wx_str(); 
 705     info
.cch 
= m_text
.length(); 
 706     if ( !::SetMenuItemInfo(hMenu
, id
, FALSE
, &info
) ) 
 708         wxLogLastError(wxT("SetMenuItemInfo")); 
 712 #if wxUSE_OWNER_DRAWN 
 714 int wxMenuItem::MeasureAccelWidth() const 
 716     wxString accel 
= GetItemLabel().AfterFirst(wxT('\t')); 
 724     dc
.GetTextExtent(accel
, &w
, NULL
); 
 729 wxString 
wxMenuItem::GetName() const 
 731     return GetItemLabelText(); 
 734 bool wxMenuItem::OnMeasureItem(size_t *width
, size_t *height
) 
 736     const MenuDrawData
* data 
= MenuDrawData::Get(); 
 738     if ( IsOwnerDrawn() ) 
 740         *width  
= data
->ItemMargin
.left 
+ data
->ItemMargin
.right
; 
 741         *height 
= data
->ItemMargin
.top  
+ data
->ItemMargin
.bottom
; 
 745             *width  
+= data
->SeparatorSize
.cx
 
 746                      + data
->SeparatorMargin
.left 
+ data
->SeparatorMargin
.right
; 
 747             *height 
+= data
->SeparatorSize
.cy
 
 748                      + data
->SeparatorMargin
.top 
+ data
->SeparatorMargin
.bottom
; 
 752         wxString str 
= GetName(); 
 760         dc
.GetTextExtent(str
, &w
, &h
); 
 762         *width 
= data
->TextBorder 
+ w 
+ data
->AccelBorder
; 
 765         w 
= m_parentMenu
->GetMaxAccelWidth(); 
 767             *width 
+= w 
+ data
->ArrowBorder
; 
 769         *width 
+= data
->Offset
; 
 770         *width 
+= data
->ArrowMargin
.left 
+ data
->ArrowSize
.cx 
+ data
->ArrowMargin
.right
; 
 772     else // don't draw the text, just the bitmap (if any) 
 780     if ( IsOwnerDrawn() ) 
 782         // width of menu icon in ownerdrawn menu 
 783         // if any bitmap is not set, the width of space reserved for icon 
 784         // image is equal to the width of std check mark, 
 785         // if bitmap is set, then the width is set to the width of the widest 
 786         // bitmap in menu (GetMarginWidth()) unless std check mark is wider, 
 787         // then it's is set to std mark's width 
 788         int imgWidth 
= wxMax(GetMarginWidth(), data
->CheckSize
.cx
) 
 789                      + data
->CheckMargin
.left 
+ data
->CheckMargin
.right
; 
 791         *width 
+= imgWidth 
+ data
->CheckBgMargin
.left 
+ data
->CheckBgMargin
.right
; 
 794     if ( m_bmpChecked
.IsOk() || m_bmpChecked
.IsOk() ) 
 796         // get size of bitmap always return valid value (0 for invalid bitmap), 
 797         // so we don't needed check if bitmap is valid ;) 
 798         size_t heightBmp 
= wxMax(m_bmpChecked
.GetHeight(), m_bmpUnchecked
.GetHeight()); 
 799         size_t widthtBmp 
= wxMax(m_bmpChecked
.GetWidth(),  m_bmpUnchecked
.GetWidth()); 
 801         if ( IsOwnerDrawn() ) 
 803             heightBmp 
+= data
->CheckMargin
.top 
+ data
->CheckMargin
.bottom
; 
 807             // we must allocate enough space for the bitmap 
 811         // Is BMP height larger than text height? 
 812         if ( *height 
< heightBmp 
) 
 816     // make sure that this item is at least as tall as the system menu height 
 817     const size_t menuHeight 
= data
->CheckMargin
.top 
+ data
->CheckMargin
.bottom
 
 818                             + data
->CheckSize
.cy
; 
 819     if (*height 
< menuHeight
) 
 820         *height 
= menuHeight
; 
 825 bool wxMenuItem::OnDrawItem(wxDC
& dc
, const wxRect
& rc
, 
 826                             wxODAction 
WXUNUSED(act
), wxODStatus stat
) 
 828     const MenuDrawData
* data 
= MenuDrawData::Get(); 
 830     wxMSWDCImpl 
*impl 
= (wxMSWDCImpl
*) dc
.GetImpl(); 
 831     HDC hdc 
= GetHdcOf(*impl
); 
 834     wxCopyRectToRECT(rc
, rect
); 
 836     int imgWidth 
= wxMax(GetMarginWidth(), data
->CheckSize
.cx
) 
 837                  + data
->CheckMargin
.left 
+ data
->CheckMargin
.right
; 
 839     if ( IsOwnerDrawn() ) 
 841         // font and colors to use 
 845         wxColour colText1
, colBack1
; 
 846         GetColourToUse(stat
, colText1
, colBack1
); 
 848         DWORD colText 
= wxColourToPalRGB(colText1
); 
 849         DWORD colBack 
= wxColourToPalRGB(colBack1
); 
 851         // calculate metrics of item parts 
 857         SetRect(&rcSelection
, 
 858                 rect
.left   
+ data
->ItemMargin
.left
, 
 859                 rect
.top    
+ data
->ItemMargin
.top
, 
 860                 rect
.right  
- data
->ItemMargin
.right
, 
 861                 rect
.bottom 
- data
->ItemMargin
.bottom
); 
 863         SetRect(&rcSeparator
, 
 864                 rcSelection
.left   
+ data
->SeparatorMargin
.left
, 
 865                 rcSelection
.top    
+ data
->SeparatorMargin
.top
, 
 866                 rcSelection
.right  
- data
->SeparatorMargin
.right
, 
 867                 rcSelection
.bottom 
- data
->SeparatorMargin
.bottom
); 
 869         CopyRect(&rcGutter
, &rcSelection
); 
 870         rcGutter
.right 
= data
->ItemMargin
.left
 
 871                        + data
->CheckBgMargin
.left
 
 873                        + data
->CheckBgMargin
.right
; 
 875         CopyRect(&rcText
, &rcSelection
); 
 876         rcText
.left 
= rcGutter
.right 
+ data
->TextBorder
; 
 879         wxUxThemeEngine
* theme 
= MenuDrawData::GetUxThemeEngine(); 
 882             POPUPITEMSTATES state
; 
 883             if ( stat 
& wxODDisabled 
) 
 885                 state 
= (stat 
& wxODSelected
) ? MPI_DISABLEDHOT
 
 888             else if ( stat 
& wxODSelected 
) 
 897             wxUxThemeHandle 
hTheme(GetMenu()->GetWindow(), L
"MENU"); 
 899             if ( theme
->IsThemeBackgroundPartiallyTransparent(hTheme
, 
 900                     MENU_POPUPITEM
, state
) ) 
 902                 theme
->DrawThemeBackground(hTheme
, hdc
, 
 903                                            MENU_POPUPBACKGROUND
, 
 907             theme
->DrawThemeBackground(hTheme
, hdc
, MENU_POPUPGUTTER
, 
 912                 rcSeparator
.left 
= rcGutter
.right
; 
 913                 theme
->DrawThemeBackground(hTheme
, hdc
, MENU_POPUPSEPARATOR
, 
 914                                            0, &rcSeparator
, NULL
); 
 918             theme
->DrawThemeBackground(hTheme
, hdc
, MENU_POPUPITEM
, 
 919                                        state
, &rcSelection
, NULL
); 
 923 #endif // wxUSE_UXTHEME 
 927                 DrawEdge(hdc
, &rcSeparator
, EDGE_ETCHED
, BF_TOP
); 
 931             AutoHBRUSH 
hbr(colBack
); 
 932             SelectInHDC 
selBrush(hdc
, hbr
); 
 933             ::FillRect(hdc
, &rcSelection
, hbr
); 
 938         // using native API because it recognizes '&' 
 940         COLORREF colOldText 
= ::SetTextColor(hdc
, colText
); 
 941         COLORREF colOldBack 
= ::SetBkColor(hdc
, colBack
); 
 943         int prevMode 
= SetBkMode(hdc
, TRANSPARENT
); 
 945         SelectInHDC 
selFont(hdc
, GetHfontOf(font
)); 
 948         // item text name without menemonic for calculating size 
 949         wxString text 
= GetName(); 
 952         ::GetTextExtentPoint32(hdc
, text
.c_str(), text
.length(), &textSize
); 
 954         // item text name with menemonic 
 955         text 
= GetItemLabel().BeforeFirst('\t'); 
 957         int flags 
= DST_PREFIXTEXT
; 
 958         // themes menu is using specified color for disabled labels 
 959         if ( data
->MenuLayout() == MenuDrawData::Classic 
&& 
 960              (stat 
& wxODDisabled
) && !(stat 
& wxODSelected
) ) 
 961             flags 
|= DSS_DISABLED
; 
 963         if ( (stat 
& wxODHidePrefix
) && !data
->AlwaysShowCues 
) 
 964             flags 
|= DSS_HIDEPREFIX
; 
 967         int y 
= rcText
.top 
+ (rcText
.bottom 
- rcText
.top 
- textSize
.cy
) / 2; 
 969         ::DrawState(hdc
, NULL
, NULL
, (LPARAM
)text
.wx_str(), 
 970                     text
.length(), x
, y
, 0, 0, flags
); 
 972         // ::SetTextAlign(hdc, TA_RIGHT) doesn't work with DSS_DISABLED or DSS_MONO 
 973         // as the last parameter in DrawState() (at least with Windows98). So we have 
 974         // to take care of right alignment ourselves. 
 975         wxString accel 
= GetItemLabel().AfterFirst(wxT('\t')); 
 976         if ( !accel
.empty() ) 
 979             ::GetTextExtentPoint32(hdc
, accel
.c_str(), accel
.length(), &accelSize
); 
 981             int flags 
= DST_TEXT
; 
 982             // themes menu is using specified color for disabled labels 
 983             if ( data
->MenuLayout() == MenuDrawData::Classic 
&& 
 984                  (stat 
& wxODDisabled
) && !(stat 
& wxODSelected
) ) 
 985                 flags 
|= DSS_DISABLED
; 
 987             int x 
= rcText
.right 
- data
->ArrowMargin
.left
 
 989                                  - data
->ArrowMargin
.right
 
 992             // right align accel on FullTheme menu, left otherwise 
 993             if ( data
->MenuLayout() == MenuDrawData::FullTheme
) 
 996                 x 
-= m_parentMenu
->GetMaxAccelWidth(); 
 998             int y 
= rcText
.top 
+ (rcText
.bottom 
- rcText
.top 
- accelSize
.cy
) / 2; 
1000             ::DrawState(hdc
, NULL
, NULL
, (LPARAM
)accel
.wx_str(), 
1001                         accel
.length(), x
, y
, 0, 0, flags
); 
1004         ::SetBkMode(hdc
, prevMode
); 
1005         ::SetBkColor(hdc
, colOldBack
); 
1006         ::SetTextColor(hdc
, colOldText
); 
1014             rect
.left 
+ data
->ItemMargin
.left 
+ data
->CheckBgMargin
.left
, 
1015             rect
.top  
+ data
->ItemMargin
.top  
+ data
->CheckBgMargin
.top
, 
1016             rect
.left 
+ data
->ItemMargin
.left 
+ data
->CheckBgMargin
.left
 
1018             rect
.bottom 
- data
->ItemMargin
.bottom 
- data
->CheckBgMargin
.bottom
); 
1020     if ( IsCheckable() && !m_bmpChecked
.Ok() ) 
1022         if ( stat 
& wxODChecked 
) 
1025             wxUxThemeEngine
* theme 
= MenuDrawData::GetUxThemeEngine(); 
1028                 wxUxThemeHandle 
hTheme(GetMenu()->GetWindow(), L
"MENU"); 
1030                 POPUPCHECKBACKGROUNDSTATES stateCheckBg 
= (stat 
& wxODDisabled
) 
1034                 theme
->DrawThemeBackground(hTheme
, hdc
, MENU_POPUPCHECKBACKGROUND
, 
1035                                            stateCheckBg
, &rcImg
, NULL
); 
1037                 // check mark will be drawn centered on the background 
1039                 POPUPCHECKSTATES stateCheck 
= (stat 
& wxODDisabled
) 
1040                                                 ? MC_CHECKMARKDISABLED
 
1041                                                 : MC_CHECKMARKNORMAL
; 
1043                 theme
->DrawThemeBackground(hTheme
, hdc
, MENU_POPUPCHECK
, 
1044                                            stateCheck
, &rcImg
, NULL
); 
1047         #endif // wxUSE_UXTHEME 
1050                 int cy 
= rcImg
.bottom 
- rcImg
.top
; 
1052                 // what goes on: DrawFrameControl creates a b/w mask, 
1053                 // then we copy it to screen to have right colors 
1055                 // first create a monochrome bitmap in a memory DC 
1056                 HDC hdcMem 
= ::CreateCompatibleDC(hdc
); 
1057                 HBITMAP hbmpCheck 
= ::CreateBitmap(cx
, cy
, 1, 1, 0); 
1058                 ::SelectObject(hdcMem
, hbmpCheck
); 
1060                 // then draw a check mark into it 
1061                 RECT rect 
= { 0, 0, cx
, cy 
}; 
1062                 if ( rc
.GetHeight() > 0 ) 
1064                     ::DrawFrameControl(hdcMem
, &rect
, DFC_MENU
, DFCS_MENUCHECK
); 
1067                 // finally copy it to screen DC and clean up 
1068                 ::BitBlt(hdc
, rcImg
.left
, rcImg
.top
, cx
, cy
, hdcMem
, 0, 0, SRCCOPY
); 
1078         if ( stat 
& wxODDisabled 
) 
1080             bmp 
= GetDisabledBitmap(); 
1085             // for not checkable bitmaps we should always use unchecked one 
1086             // because their checked bitmap is not set 
1087             bmp 
= GetBitmap(!IsCheckable() || (stat 
& wxODChecked
)); 
1090             if ( bmp
.Ok() && stat 
& wxODDisabled 
) 
1092                 // we need to grey out the bitmap as we don't have any specific 
1094                 wxImage imgGrey 
= bmp
.ConvertToImage().ConvertToGreyscale(); 
1096                     bmp 
= wxBitmap(imgGrey
); 
1098 #endif // wxUSE_IMAGE 
1103             wxMemoryDC 
dcMem(&dc
); 
1104             dcMem
.SelectObjectAsSource(bmp
); 
1107             int nBmpWidth  
= bmp
.GetWidth(), 
1108                 nBmpHeight 
= bmp
.GetHeight(); 
1110             // there should be enough space! 
1111             wxASSERT( nBmpWidth 
<= imgWidth 
&& nBmpHeight 
<= (rcImg
.bottom 
- rcImg
.top
) ); 
1113             int x 
= rcImg
.left 
+ (imgWidth 
- nBmpWidth
) / 2; 
1114             int y 
= rcImg
.top  
+ (rcImg
.bottom 
- rcImg
.top 
- nBmpHeight
) / 2; 
1115             dc
.Blit(x
, y
, nBmpWidth
, nBmpHeight
, &dcMem
, 0, 0, wxCOPY
, true); 
1123 void wxMenuItem::GetFontToUse(wxFont
& font
) const 
1127         font 
= MenuDrawData::Get()->Font
; 
1130 void wxMenuItem::GetColourToUse(wxODStatus stat
, wxColour
& colText
, wxColour
& colBack
) const 
1133     wxUxThemeEngine
* theme 
= MenuDrawData::GetUxThemeEngine(); 
1136         wxUxThemeHandle 
hTheme(GetMenu()->GetWindow(), L
"MENU"); 
1138         if ( stat 
& wxODDisabled
) 
1140             wxRGBToColour(colText
, theme
->GetThemeSysColor(hTheme
, COLOR_GRAYTEXT
)); 
1144             colText 
= GetTextColour(); 
1145             if ( !colText
.IsOk() ) 
1146                 wxRGBToColour(colText
, theme
->GetThemeSysColor(hTheme
, COLOR_MENUTEXT
)); 
1149         if ( stat 
& wxODSelected 
) 
1151             wxRGBToColour(colBack
, theme
->GetThemeSysColor(hTheme
, COLOR_HIGHLIGHT
)); 
1155             colBack 
= GetBackgroundColour(); 
1156             if ( !colBack
.IsOk() ) 
1157                 wxRGBToColour(colBack
, theme
->GetThemeSysColor(hTheme
, COLOR_MENU
)); 
1161 #endif // wxUSE_UXTHEME 
1163         wxOwnerDrawn::GetColourToUse(stat
, colText
, colBack
); 
1166 #endif // wxUSE_OWNER_DRAWN 
1168 // ---------------------------------------------------------------------------- 
1170 // ---------------------------------------------------------------------------- 
1172 wxMenuItem 
*wxMenuItemBase::New(wxMenu 
*parentMenu
, 
1174                                 const wxString
& name
, 
1175                                 const wxString
& help
, 
1179     return new wxMenuItem(parentMenu
, id
, name
, help
, kind
, subMenu
); 
1182 #endif // wxUSE_MENUS