1 /////////////////////////////////////////////////////////////////////////////// 
   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 #if defined(__GNUG__) && !defined(NO_GCC_PRAGMA) 
  21     #pragma implementation "menuitem.h" 
  24 // For compilers that support precompilation, includes "wx.h". 
  25 #include "wx/wxprec.h" 
  35     #include "wx/bitmap.h" 
  36     #include "wx/settings.h" 
  38     #include "wx/window.h" 
  41     #include "wx/string.h" 
  44 #include "wx/menuitem.h" 
  51 #include "wx/msw/private.h" 
  54 // Implemented in menu.cpp 
  55 UINT 
GetMenuState(HMENU hMenu
, UINT id
, UINT flags
) ; 
  58 // --------------------------------------------------------------------------- 
  60 // --------------------------------------------------------------------------- 
  63 #define GetHMenuOf(menu)    ((HMENU)menu->GetHMenu()) 
  65 // conditional compilation 
  67     #define OWNER_DRAWN_ONLY( code ) if ( IsOwnerDrawn() ) code 
  68 #else // !wxUSE_OWNER_DRAWN 
  69     #define OWNER_DRAWN_ONLY( code ) 
  70 #endif // wxUSE_OWNER_DRAWN/!wxUSE_OWNER_DRAWN 
  72 // ============================================================================ 
  74 // ============================================================================ 
  76 // ---------------------------------------------------------------------------- 
  77 // dynamic classes implementation 
  78 // ---------------------------------------------------------------------------- 
  80 #if wxUSE_EXTENDED_RTTI 
  82 bool wxMenuItemStreamingCallback( const wxObject 
*object
, wxWriter 
* , wxPersister 
* , wxxVariantArray 
& ) 
  84     const wxMenuItem 
* mitem 
= dynamic_cast<const wxMenuItem
*>(object
) ; 
  85     if ( mitem
->GetMenu() && !mitem
->GetMenu()->GetTitle().IsEmpty() ) 
  87         // we don't stream out the first two items for menus with a title, they will be reconstructed 
  88         if ( mitem
->GetMenu()->FindItemByPosition(0) == mitem 
|| mitem
->GetMenu()->FindItemByPosition(1) == mitem 
) 
  94 wxBEGIN_ENUM( wxItemKind 
) 
  95     wxENUM_MEMBER( wxITEM_SEPARATOR 
)  
  96     wxENUM_MEMBER( wxITEM_NORMAL 
)  
  97     wxENUM_MEMBER( wxITEM_CHECK 
)  
  98     wxENUM_MEMBER( wxITEM_RADIO 
)  
  99 wxEND_ENUM( wxItemKind 
) 
 101 IMPLEMENT_DYNAMIC_CLASS_XTI_CALLBACK(wxMenuItem
, wxObject
,"wx/menuitem.h",wxMenuItemStreamingCallback
) 
 103 wxBEGIN_PROPERTIES_TABLE(wxMenuItem
) 
 104         wxPROPERTY( Parent
,wxMenu
*, SetMenu
, GetMenu
, , 0 /*flags*/ , wxT("Helpstring") , wxT("group") ) 
 105         wxPROPERTY( Id
,int, SetId
, GetId
, , 0 /*flags*/ , wxT("Helpstring") , wxT("group") ) 
 106     wxPROPERTY( Text
, wxString 
, SetText
, GetText
, wxString(), 0 /*flags*/ , wxT("Helpstring") , wxT("group") ) 
 107     wxPROPERTY( Help
, wxString 
, SetHelp
, GetHelp
, wxString(), 0 /*flags*/ , wxT("Helpstring") , wxT("group") ) 
 108     wxREADONLY_PROPERTY( Kind
, wxItemKind 
, GetKind 
, , 0 /*flags*/ , wxT("Helpstring") , wxT("group") ) 
 109         wxPROPERTY( SubMenu
,wxMenu
*, SetSubMenu
, GetSubMenu
, , 0 /*flags*/ , wxT("Helpstring") , wxT("group") ) 
 110         wxPROPERTY( Enabled 
, bool , Enable 
, IsEnabled 
, wxxVariant((bool)true) , 0 /*flags*/ , wxT("Helpstring") , wxT("group")) 
 111         wxPROPERTY( Checked 
, bool , Check 
, IsChecked 
, wxxVariant((bool)false) , 0 /*flags*/ , wxT("Helpstring") , wxT("group")) 
 112         wxPROPERTY( Checkable 
, bool , SetCheckable 
, IsCheckable 
, wxxVariant((bool)false) , 0 /*flags*/ , wxT("Helpstring") , wxT("group")) 
 113 wxEND_PROPERTIES_TABLE() 
 115 wxBEGIN_HANDLERS_TABLE(wxMenuItem
) 
 116 wxEND_HANDLERS_TABLE() 
 118 wxDIRECT_CONSTRUCTOR_6( wxMenuItem 
, wxMenu
* , Parent 
, int , Id 
, wxString 
, Text 
, wxString 
, Help 
, wxItemKind 
, Kind 
, wxMenu
* , SubMenu  
) 
 120 IMPLEMENT_DYNAMIC_CLASS(wxMenuItem
, wxObject
) 
 123 // ---------------------------------------------------------------------------- 
 125 // ---------------------------------------------------------------------------- 
 130 wxMenuItem::wxMenuItem(wxMenu 
*pParentMenu
, 
 132                        const wxString
& text
, 
 133                        const wxString
& strHelp
, 
 136           : wxMenuItemBase(pParentMenu
, id
, text
, strHelp
, kind
, pSubMenu
) 
 137 #if wxUSE_OWNER_DRAWN 
 138             , wxOwnerDrawn(text
, kind 
== wxITEM_CHECK
, true) 
 139 #endif // owner drawn 
 144 wxMenuItem::wxMenuItem(wxMenu 
*parentMenu
, 
 146                        const wxString
& text
, 
 147                        const wxString
& help
, 
 150           : wxMenuItemBase(parentMenu
, id
, text
, help
, 
 151                            isCheckable 
? wxITEM_CHECK 
: wxITEM_NORMAL
, subMenu
) 
 152 #if wxUSE_OWNER_DRAWN 
 153            , wxOwnerDrawn(text
, isCheckable
, true) 
 154 #endif // owner drawn 
 159 void wxMenuItem::Init() 
 161     m_radioGroup
.start 
= -1; 
 162     m_isRadioGroupStart 
= FALSE
; 
 164 #if  wxUSE_OWNER_DRAWN 
 165     // set default menu colors 
 166     #define SYS_COLOR(c) (wxSystemSettings::GetColour(wxSYS_COLOUR_##c)) 
 168     SetTextColour(SYS_COLOR(MENUTEXT
)); 
 169     SetBackgroundColour(SYS_COLOR(MENU
)); 
 173     // we don't want normal items be owner-drawn 
 176     // tell the owner drawing code to to show the accel string as well 
 177     SetAccelString(m_text
.AfterFirst(_T('\t'))); 
 178 #endif // wxUSE_OWNER_DRAWN 
 181 wxMenuItem::~wxMenuItem() 
 188 // return the id for calling Win32 API functions 
 189 int wxMenuItem::GetRealId() const 
 191     return m_subMenu 
? (int)m_subMenu
->GetHMenu() : GetId(); 
 197 bool wxMenuItem::IsChecked() const 
 199     // fix that RTTI is always getting the correct state (separators cannot be checked, but the call below  
 201     if ( GetId() == wxID_SEPARATOR 
) 
 204     int flag 
= ::GetMenuState(GetHMenuOf(m_parentMenu
), GetId(), MF_BYCOMMAND
); 
 206     return (flag 
& MF_CHECKED
) != 0; 
 210 wxString 
wxMenuItemBase::GetLabelFromText(const wxString
& text
) 
 212     return wxStripMenuCodes(text
); 
 218 void wxMenuItem::SetAsRadioGroupStart() 
 220     m_isRadioGroupStart 
= TRUE
; 
 223 void wxMenuItem::SetRadioGroupStart(int start
) 
 225     wxASSERT_MSG( !m_isRadioGroupStart
, 
 226                   _T("should only be called for the next radio items") ); 
 228     m_radioGroup
.start 
= start
; 
 231 void wxMenuItem::SetRadioGroupEnd(int end
) 
 233     wxASSERT_MSG( m_isRadioGroupStart
, 
 234                   _T("should only be called for the first radio item") ); 
 236     m_radioGroup
.end 
= end
; 
 242 void wxMenuItem::Enable(bool enable
) 
 244     if ( m_isEnabled 
== enable 
) 
 247     long rc 
= EnableMenuItem(GetHMenuOf(m_parentMenu
), 
 250                              (enable 
? MF_ENABLED 
: MF_GRAYED
)); 
 253         wxLogLastError(wxT("EnableMenuItem")); 
 256     wxMenuItemBase::Enable(enable
); 
 259 void wxMenuItem::Check(bool check
) 
 261     wxCHECK_RET( IsCheckable(), wxT("only checkable items may be checked") ); 
 263     if ( m_isChecked 
== check 
) 
 266     int flags 
= check 
? MF_CHECKED 
: MF_UNCHECKED
; 
 267     HMENU hmenu 
= GetHMenuOf(m_parentMenu
); 
 269     if ( GetKind() == wxITEM_RADIO 
) 
 271         // it doesn't make sense to uncheck a radio item - what would this do? 
 275         // get the index of this item in the menu 
 276         const wxMenuItemList
& items 
= m_parentMenu
->GetMenuItems(); 
 277         int pos 
= items
.IndexOf(this); 
 278         wxCHECK_RET( pos 
!= wxNOT_FOUND
, 
 279                      _T("menuitem not found in the menu items list?") ); 
 281         // get the radio group range 
 285         if ( m_isRadioGroupStart 
) 
 287             // we already have all information we need 
 289             end 
= m_radioGroup
.end
; 
 291         else // next radio group item 
 293             // get the radio group end from the start item 
 294             start 
= m_radioGroup
.start
; 
 295             end 
= items
.Item(start
)->GetData()->m_radioGroup
.end
; 
 299         // calling CheckMenuRadioItem() with such parameters hangs my system 
 300         // (NT4 SP6) and I suspect this could happen to the others as well - so 
 302         wxCHECK_RET( start 
!= -1 && end 
!= -1, 
 303                      _T("invalid ::CheckMenuRadioItem() parameter(s)") ); 
 305         if ( !::CheckMenuRadioItem(hmenu
, 
 306                                    start
,   // the first radio group item 
 308                                    pos
,     // the one to check 
 311             wxLogLastError(_T("CheckMenuRadioItem")); 
 315         // also uncheck all the other items in this radio group 
 316         wxMenuItemList::compatibility_iterator node 
= items
.Item(start
); 
 317         for ( int n 
= start
; n 
<= end 
&& node
; n
++ ) 
 321                 node
->GetData()->m_isChecked 
= FALSE
; 
 324             node 
= node
->GetNext(); 
 329         if ( ::CheckMenuItem(hmenu
, 
 331                              MF_BYCOMMAND 
| flags
) == (DWORD
)-1 ) 
 333             wxLogLastError(wxT("CheckMenuItem")); 
 337     wxMenuItemBase::Check(check
); 
 340 void wxMenuItem::SetText(const wxString
& text
) 
 342     // don't do anything if label didn't change 
 343     if ( m_text 
== text 
) 
 346     wxMenuItemBase::SetText(text
); 
 347     OWNER_DRAWN_ONLY( wxOwnerDrawn::SetName(text
) ); 
 348 #if wxUSE_OWNER_DRAWN 
 349     // tell the owner drawing code to to show the accel string as well 
 350     SetAccelString(text
.AfterFirst(_T('\t'))); 
 353     HMENU hMenu 
= GetHMenuOf(m_parentMenu
); 
 354     wxCHECK_RET( hMenu
, wxT("menuitem without menu") ); 
 357     m_parentMenu
->UpdateAccel(this); 
 358 #endif // wxUSE_ACCEL 
 360     UINT id 
= GetRealId(); 
 361     UINT flagsOld 
= ::GetMenuState(hMenu
, id
, MF_BYCOMMAND
); 
 362     if ( flagsOld 
== 0xFFFFFFFF ) 
 364         wxLogLastError(wxT("GetMenuState")); 
 370             // high byte contains the number of items in a submenu for submenus 
 372             flagsOld 
|= MF_POPUP
; 
 377 #if wxUSE_OWNER_DRAWN 
 378         if ( IsOwnerDrawn() ) 
 380             flagsOld 
|= MF_OWNERDRAW
; 
 381             data 
= (LPCTSTR
)this; 
 386             flagsOld 
|= MF_STRING
; 
 387             data 
= (wxChar
*) text
.c_str(); 
 391         // FIXME: complete this, applying the old 
 393         // However, the WinCE doc for SetMenuItemInfo 
 394         // says that you can't use it to set the menu 
 395         // item state; only data, id and type. 
 398         info
.cbSize 
= sizeof(info
); 
 399         info
.fMask 
= MIIM_TYPE
; 
 400         info
.fType 
= MFT_STRING
; 
 401         info
.cch 
= text
.Length(); 
 402         info
.dwTypeData 
= (LPTSTR
) data 
; 
 403         if ( !SetMenuItemInfo(hMenu
, id
, FALSE
, & info
) ) 
 405             wxLogLastError(wxT("SetMenuItemInfo")); 
 408         if ( ::ModifyMenu(hMenu
, id
, 
 409                           MF_BYCOMMAND 
| flagsOld
, 
 410                           id
, data
) == (int)0xFFFFFFFF ) 
 412             wxLogLastError(wxT("ModifyMenu")); 
 418 void wxMenuItem::SetCheckable(bool checkable
) 
 420     wxMenuItemBase::SetCheckable(checkable
); 
 421     OWNER_DRAWN_ONLY( wxOwnerDrawn::SetCheckable(checkable
) ); 
 424 // ---------------------------------------------------------------------------- 
 426 // ---------------------------------------------------------------------------- 
 428 wxMenuItem 
*wxMenuItemBase::New(wxMenu 
*parentMenu
, 
 430                                 const wxString
& name
, 
 431                                 const wxString
& help
, 
 435     return new wxMenuItem(parentMenu
, id
, name
, help
, kind
, subMenu
); 
 438 #endif // wxUSE_MENUS