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
, EMPTY_MACROVALUE
, 0 /*flags*/ , wxT("Helpstring") , wxT("group") )
105 wxPROPERTY( Id
,int, SetId
, GetId
, EMPTY_MACROVALUE
, 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
, EMPTY_MACROVALUE
, 0 /*flags*/ , wxT("Helpstring") , wxT("group") )
109 wxPROPERTY( SubMenu
,wxMenu
*, SetSubMenu
, GetSubMenu
, EMPTY_MACROVALUE
, 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