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" 
  50 // Implemented in menu.cpp 
  51 UINT 
GetMenuState(HMENU hMenu
, UINT id
, UINT flags
) ; 
  54 // --------------------------------------------------------------------------- 
  56 // --------------------------------------------------------------------------- 
  59 #define GetHMenuOf(menu)    ((HMENU)menu->GetHMenu()) 
  61 // conditional compilation 
  63     #define OWNER_DRAWN_ONLY( code ) if ( IsOwnerDrawn() ) code 
  64 #else // !wxUSE_OWNER_DRAWN 
  65     #define OWNER_DRAWN_ONLY( code ) 
  66 #endif // wxUSE_OWNER_DRAWN/!wxUSE_OWNER_DRAWN 
  68 // ============================================================================ 
  70 // ============================================================================ 
  72 // ---------------------------------------------------------------------------- 
  73 // dynamic classes implementation 
  74 // ---------------------------------------------------------------------------- 
  76 #if wxUSE_EXTENDED_RTTI 
  78 bool wxMenuItemStreamingCallback( const wxObject 
*object
, wxWriter 
* , wxPersister 
* , wxxVariantArray 
& ) 
  80     const wxMenuItem 
* mitem 
= dynamic_cast<const wxMenuItem
*>(object
) ; 
  81     if ( mitem
->GetMenu() && !mitem
->GetMenu()->GetTitle().empty() ) 
  83         // we don't stream out the first two items for menus with a title, they will be reconstructed 
  84         if ( mitem
->GetMenu()->FindItemByPosition(0) == mitem 
|| mitem
->GetMenu()->FindItemByPosition(1) == mitem 
) 
  90 wxBEGIN_ENUM( wxItemKind 
) 
  91     wxENUM_MEMBER( wxITEM_SEPARATOR 
) 
  92     wxENUM_MEMBER( wxITEM_NORMAL 
) 
  93     wxENUM_MEMBER( wxITEM_CHECK 
) 
  94     wxENUM_MEMBER( wxITEM_RADIO 
) 
  95 wxEND_ENUM( wxItemKind 
) 
  97 IMPLEMENT_DYNAMIC_CLASS_XTI_CALLBACK(wxMenuItem
, wxObject
,"wx/menuitem.h",wxMenuItemStreamingCallback
) 
  99 wxBEGIN_PROPERTIES_TABLE(wxMenuItem
) 
 100     wxPROPERTY( Parent
,wxMenu
*, SetMenu
, GetMenu
, EMPTY_MACROVALUE 
, 0 /*flags*/ , wxT("Helpstring") , wxT("group") ) 
 101     wxPROPERTY( Id
,int, SetId
, GetId
, EMPTY_MACROVALUE 
, 0 /*flags*/ , wxT("Helpstring") , wxT("group") ) 
 102     wxPROPERTY( Text
, wxString 
, SetText
, GetText
, wxString(), 0 /*flags*/ , wxT("Helpstring") , wxT("group") ) 
 103     wxPROPERTY( Help
, wxString 
, SetHelp
, GetHelp
, wxString(), 0 /*flags*/ , wxT("Helpstring") , wxT("group") ) 
 104     wxREADONLY_PROPERTY( Kind
, wxItemKind 
, GetKind 
, EMPTY_MACROVALUE 
, 0 /*flags*/ , wxT("Helpstring") , wxT("group") ) 
 105     wxPROPERTY( SubMenu
,wxMenu
*, SetSubMenu
, GetSubMenu
, EMPTY_MACROVALUE 
, 0 /*flags*/ , wxT("Helpstring") , wxT("group") ) 
 106     wxPROPERTY( Enabled 
, bool , Enable 
, IsEnabled 
, wxxVariant((bool)true) , 0 /*flags*/ , wxT("Helpstring") , wxT("group")) 
 107     wxPROPERTY( Checked 
, bool , Check 
, IsChecked 
, wxxVariant((bool)false) , 0 /*flags*/ , wxT("Helpstring") , wxT("group")) 
 108     wxPROPERTY( Checkable 
, bool , SetCheckable 
, IsCheckable 
, wxxVariant((bool)false) , 0 /*flags*/ , wxT("Helpstring") , wxT("group")) 
 109 wxEND_PROPERTIES_TABLE() 
 111 wxBEGIN_HANDLERS_TABLE(wxMenuItem
) 
 112 wxEND_HANDLERS_TABLE() 
 114 wxDIRECT_CONSTRUCTOR_6( wxMenuItem 
, wxMenu
* , Parent 
, int , Id 
, wxString 
, Text 
, wxString 
, Help 
, wxItemKind 
, Kind 
, wxMenu
* , SubMenu  
) 
 116 IMPLEMENT_DYNAMIC_CLASS(wxMenuItem
, wxObject
) 
 119 // ---------------------------------------------------------------------------- 
 121 // ---------------------------------------------------------------------------- 
 126 wxMenuItem::wxMenuItem(wxMenu 
*pParentMenu
, 
 128                        const wxString
& text
, 
 129                        const wxString
& strHelp
, 
 132           : wxMenuItemBase(pParentMenu
, id
, text
, strHelp
, kind
, pSubMenu
) 
 133 #if wxUSE_OWNER_DRAWN 
 134             , wxOwnerDrawn(text
, kind 
== wxITEM_CHECK
, true) 
 135 #endif // owner drawn 
 140 #if WXWIN_COMPATIBILITY_2_8 
 141 wxMenuItem::wxMenuItem(wxMenu 
*parentMenu
, 
 143                        const wxString
& text
, 
 144                        const wxString
& help
, 
 147           : wxMenuItemBase(parentMenu
, id
, text
, help
, 
 148                            isCheckable 
? wxITEM_CHECK 
: wxITEM_NORMAL
, subMenu
) 
 149 #if wxUSE_OWNER_DRAWN 
 150            , wxOwnerDrawn(text
, isCheckable
, true) 
 151 #endif // owner drawn 
 157 void wxMenuItem::Init() 
 159     m_radioGroup
.start 
= -1; 
 160     m_isRadioGroupStart 
= false; 
 162 #if  wxUSE_OWNER_DRAWN 
 164     // when the color is not valid, wxOwnerDraw takes the default ones. 
 165     // If we set the colors here and they are changed by the user during 
 166     // the execution, then the colors are not updated until the application 
 167     // is restarted and our menus look bad 
 168     SetTextColour(wxNullColour
); 
 169     SetBackgroundColour(wxNullColour
); 
 171     // setting default colors switched ownerdraw on: switch it off again 
 174     //  switch ownerdraw back on if using a non default margin 
 175     if ( !IsSeparator() ) 
 176         SetMarginWidth(GetMarginWidth()); 
 178     // tell the owner drawing code to show the accel string as well 
 179     SetAccelString(m_text
.AfterFirst(_T('\t'))); 
 180 #endif // wxUSE_OWNER_DRAWN 
 183 wxMenuItem::~wxMenuItem() 
 190 // return the id for calling Win32 API functions 
 191 WXWPARAM 
wxMenuItem::GetMSWId() const 
 193     // we must use ids in unsigned short range with Windows functions, if we 
 194     // pass ids > USHRT_MAX to them they get very confused (e.g. start 
 195     // generating WM_COMMAND messages with negative high word of wParam), so 
 196     // use the cast to ensure the id is in range 
 197     return m_subMenu 
? wxPtrToUInt(m_subMenu
->GetHMenu()) 
 198                      : wx_static_cast(unsigned short, GetId()); 
 204 bool wxMenuItem::IsChecked() const 
 206     // fix that RTTI is always getting the correct state (separators cannot be checked, but the call below 
 211     int flag 
= ::GetMenuState(GetHMenuOf(m_parentMenu
), GetMSWId(), MF_BYCOMMAND
); 
 213     return (flag 
& MF_CHECKED
) != 0; 
 219 void wxMenuItem::SetAsRadioGroupStart() 
 221     m_isRadioGroupStart 
= true; 
 224 void wxMenuItem::SetRadioGroupStart(int start
) 
 226     wxASSERT_MSG( !m_isRadioGroupStart
, 
 227                   _T("should only be called for the next radio items") ); 
 229     m_radioGroup
.start 
= start
; 
 232 void wxMenuItem::SetRadioGroupEnd(int end
) 
 234     wxASSERT_MSG( m_isRadioGroupStart
, 
 235                   _T("should only be called for the first radio item") ); 
 237     m_radioGroup
.end 
= end
; 
 243 void wxMenuItem::Enable(bool enable
) 
 245     if ( m_isEnabled 
== enable 
) 
 248     long rc 
= EnableMenuItem(GetHMenuOf(m_parentMenu
), 
 251                              (enable 
? MF_ENABLED 
: MF_GRAYED
)); 
 254         wxLogLastError(wxT("EnableMenuItem")); 
 257     wxMenuItemBase::Enable(enable
); 
 260 void wxMenuItem::Check(bool check
) 
 262     wxCHECK_RET( IsCheckable(), wxT("only checkable items may be checked") ); 
 264     if ( m_isChecked 
== check 
) 
 267     int flags 
= check 
? MF_CHECKED 
: MF_UNCHECKED
; 
 268     HMENU hmenu 
= GetHMenuOf(m_parentMenu
); 
 270     if ( GetKind() == wxITEM_RADIO 
) 
 272         // it doesn't make sense to uncheck a radio item - what would this do? 
 276         // get the index of this item in the menu 
 277         const wxMenuItemList
& items 
= m_parentMenu
->GetMenuItems(); 
 278         int pos 
= items
.IndexOf(this); 
 279         wxCHECK_RET( pos 
!= wxNOT_FOUND
, 
 280                      _T("menuitem not found in the menu items list?") ); 
 282         // get the radio group range 
 286         if ( m_isRadioGroupStart 
) 
 288             // we already have all information we need 
 290             end 
= m_radioGroup
.end
; 
 292         else // next radio group item 
 294             // get the radio group end from the start item 
 295             start 
= m_radioGroup
.start
; 
 296             end 
= items
.Item(start
)->GetData()->m_radioGroup
.end
; 
 300         // calling CheckMenuRadioItem() with such parameters hangs my system 
 301         // (NT4 SP6) and I suspect this could happen to the others as well - so 
 303         wxCHECK_RET( start 
!= -1 && end 
!= -1, 
 304                      _T("invalid ::CheckMenuRadioItem() parameter(s)") ); 
 306         if ( !::CheckMenuRadioItem(hmenu
, 
 307                                    start
,   // the first radio group item 
 309                                    pos
,     // the one to check 
 312             wxLogLastError(_T("CheckMenuRadioItem")); 
 316         // also uncheck all the other items in this radio group 
 317         wxMenuItemList::compatibility_iterator node 
= items
.Item(start
); 
 318         for ( int n 
= start
; n 
<= end 
&& node
; n
++ ) 
 322                 node
->GetData()->m_isChecked 
= false; 
 325             node 
= node
->GetNext(); 
 330         if ( ::CheckMenuItem(hmenu
, 
 332                              MF_BYCOMMAND 
| flags
) == (DWORD
)-1 ) 
 334             wxFAIL_MSG( _T("CheckMenuItem() failed, item not in the menu?") ); 
 338     wxMenuItemBase::Check(check
); 
 341 void wxMenuItem::SetItemLabel(const wxString
& txt
) 
 345     // don't do anything if label didn't change 
 349     // wxMenuItemBase will do stock ID checks 
 350     wxMenuItemBase::SetItemLabel(text
); 
 352     // m_text could now be different from 'text' if we are a stock menu item, 
 353     // so use only m_text below 
 355     OWNER_DRAWN_ONLY( wxOwnerDrawn::SetName(m_text
) ); 
 356 #if wxUSE_OWNER_DRAWN 
 357     // tell the owner drawing code to to show the accel string as well 
 358     SetAccelString(m_text
.AfterFirst(_T('\t'))); 
 361     HMENU hMenu 
= GetHMenuOf(m_parentMenu
); 
 362     wxCHECK_RET( hMenu
, wxT("menuitem without menu") ); 
 365     m_parentMenu
->UpdateAccel(this); 
 366 #endif // wxUSE_ACCEL 
 368     UINT id 
= GetMSWId(); 
 369     UINT flagsOld 
= ::GetMenuState(hMenu
, id
, MF_BYCOMMAND
); 
 370     if ( flagsOld 
== 0xFFFFFFFF ) 
 372         // It's not an error, it means that the menu item doesn't exist 
 373         //wxLogLastError(wxT("GetMenuState")); 
 379             // high byte contains the number of items in a submenu for submenus 
 381             flagsOld 
|= MF_POPUP
; 
 386 #if wxUSE_OWNER_DRAWN 
 387         if ( IsOwnerDrawn() ) 
 389             flagsOld 
|= MF_OWNERDRAW
; 
 390             data 
= (LPCTSTR
)this; 
 395             flagsOld 
|= MF_STRING
; 
 396             data 
= (wxChar
*) m_text
.wx_str(); 
 400         // FIXME: complete this, applying the old 
 402         // However, the WinCE doc for SetMenuItemInfo 
 403         // says that you can't use it to set the menu 
 404         // item state; only data, id and type. 
 407         info
.cbSize 
= sizeof(info
); 
 408         info
.fMask 
= MIIM_TYPE
; 
 409         info
.fType 
= MFT_STRING
; 
 410         info
.cch 
= m_text
.length(); 
 411         info
.dwTypeData 
= (LPTSTR
) data 
; 
 412         if ( !::SetMenuItemInfo(hMenu
, id
, FALSE
, & info
) ) 
 414             wxLogLastError(wxT("SetMenuItemInfo")); 
 417         if ( ::ModifyMenu(hMenu
, id
, 
 418                           MF_BYCOMMAND 
| flagsOld
, 
 419                           id
, data
) == (int)0xFFFFFFFF ) 
 421             wxLogLastError(wxT("ModifyMenu")); 
 427 void wxMenuItem::SetCheckable(bool checkable
) 
 429     wxMenuItemBase::SetCheckable(checkable
); 
 430     OWNER_DRAWN_ONLY( wxOwnerDrawn::SetCheckable(checkable
) ); 
 433 // ---------------------------------------------------------------------------- 
 435 // ---------------------------------------------------------------------------- 
 437 wxMenuItem 
*wxMenuItemBase::New(wxMenu 
*parentMenu
, 
 439                                 const wxString
& name
, 
 440                                 const wxString
& help
, 
 444     return new wxMenuItem(parentMenu
, id
, name
, help
, kind
, subMenu
); 
 447 #endif // wxUSE_MENUS