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 : static_cast<unsigned short>(GetId());
204 bool wxMenuItem::IsChecked() const
206 // fix that RTTI is always getting the correct state (separators cannot be
207 // checked, but the Windows call below returns true
211 // the item might not be attached to a menu yet
213 // TODO: shouldn't we just always call the base class version? It seems
214 // like it ought to always be in sync
216 return wxMenuItemBase::IsChecked();
218 HMENU hmenu
= GetHMenuOf(m_parentMenu
);
219 int flag
= ::GetMenuState(hmenu
, GetMSWId(), MF_BYCOMMAND
);
221 return (flag
& MF_CHECKED
) != 0;
227 void wxMenuItem::SetAsRadioGroupStart()
229 m_isRadioGroupStart
= true;
232 void wxMenuItem::SetRadioGroupStart(int start
)
234 wxASSERT_MSG( !m_isRadioGroupStart
,
235 _T("should only be called for the next radio items") );
237 m_radioGroup
.start
= start
;
240 void wxMenuItem::SetRadioGroupEnd(int end
)
242 wxASSERT_MSG( m_isRadioGroupStart
,
243 _T("should only be called for the first radio item") );
245 m_radioGroup
.end
= end
;
251 void wxMenuItem::Enable(bool enable
)
253 if ( m_isEnabled
== enable
)
258 long rc
= EnableMenuItem(GetHMenuOf(m_parentMenu
),
261 (enable
? MF_ENABLED
: MF_GRAYED
));
265 wxLogLastError(wxT("EnableMenuItem"));
269 wxMenuItemBase::Enable(enable
);
272 void wxMenuItem::Check(bool check
)
274 wxCHECK_RET( IsCheckable(), wxT("only checkable items may be checked") );
276 if ( m_isChecked
== check
)
281 int flags
= check
? MF_CHECKED
: MF_UNCHECKED
;
282 HMENU hmenu
= GetHMenuOf(m_parentMenu
);
284 if ( GetKind() == wxITEM_RADIO
)
286 // it doesn't make sense to uncheck a radio item -- what would this
291 // get the index of this item in the menu
292 const wxMenuItemList
& items
= m_parentMenu
->GetMenuItems();
293 int pos
= items
.IndexOf(this);
294 wxCHECK_RET( pos
!= wxNOT_FOUND
,
295 _T("menuitem not found in the menu items list?") );
297 // get the radio group range
301 if ( m_isRadioGroupStart
)
303 // we already have all information we need
305 end
= m_radioGroup
.end
;
307 else // next radio group item
309 // get the radio group end from the start item
310 start
= m_radioGroup
.start
;
311 end
= items
.Item(start
)->GetData()->m_radioGroup
.end
;
315 // calling CheckMenuRadioItem() with such parameters hangs my system
316 // (NT4 SP6) and I suspect this could happen to the others as well,
318 wxCHECK_RET( start
!= -1 && end
!= -1,
319 _T("invalid ::CheckMenuRadioItem() parameter(s)") );
321 if ( !::CheckMenuRadioItem(hmenu
,
322 start
, // the first radio group item
324 pos
, // the one to check
327 wxLogLastError(_T("CheckMenuRadioItem"));
331 // also uncheck all the other items in this radio group
332 wxMenuItemList::compatibility_iterator node
= items
.Item(start
);
333 for ( int n
= start
; n
<= end
&& node
; n
++ )
337 node
->GetData()->m_isChecked
= false;
340 node
= node
->GetNext();
345 if ( ::CheckMenuItem(hmenu
,
347 MF_BYCOMMAND
| flags
) == (DWORD
)-1 )
349 wxFAIL_MSG(_T("CheckMenuItem() failed, item not in the menu?"));
354 wxMenuItemBase::Check(check
);
357 void wxMenuItem::SetItemLabel(const wxString
& txt
)
361 // don't do anything if label didn't change
365 // wxMenuItemBase will do stock ID checks
366 wxMenuItemBase::SetItemLabel(text
);
368 // m_text could now be different from 'text' if we are a stock menu item,
369 // so use only m_text below
371 OWNER_DRAWN_ONLY( wxOwnerDrawn::SetName(m_text
) );
372 #if wxUSE_OWNER_DRAWN
373 // tell the owner drawing code to to show the accel string as well
374 SetAccelString(m_text
.AfterFirst(_T('\t')));
377 // the item can be not attached to any menu yet and SetItemLabel() is still
378 // valid to call in this case and should do nothing else
383 m_parentMenu
->UpdateAccel(this);
384 #endif // wxUSE_ACCEL
386 const UINT id
= GetMSWId();
387 HMENU hMenu
= GetHMenuOf(m_parentMenu
);
388 if ( !hMenu
|| ::GetMenuState(hMenu
, id
, MF_BYCOMMAND
) == (UINT
)-1 )
391 #if wxUSE_OWNER_DRAWN
392 if ( IsOwnerDrawn() )
394 // we don't need to do anything for owner drawn items, they will redraw
395 // themselves using the new text the next time they're displayed
398 #endif // owner drawn
400 // update the text of the native menu item
401 WinStruct
<MENUITEMINFO
> info
;
403 // surprisingly, calling SetMenuItemInfo() with just MIIM_STRING doesn't
404 // work as it resets the menu bitmap, so we need to first get the old item
405 // state and then modify it
406 const bool isLaterThanWin95
= wxGetWinVersion() > wxWinVersion_95
;
407 info
.fMask
= MIIM_STATE
|
412 if ( isLaterThanWin95
)
413 info
.fMask
|= MIIM_BITMAP
| MIIM_FTYPE
;
415 info
.fMask
|= MIIM_TYPE
;
416 if ( !::GetMenuItemInfo(hMenu
, id
, FALSE
, &info
) )
418 wxLogLastError(wxT("GetMenuItemInfo"));
422 if ( isLaterThanWin95
)
423 info
.fMask
|= MIIM_STRING
;
424 //else: MIIM_TYPE already specified
425 info
.dwTypeData
= (LPTSTR
)m_text
.wx_str();
426 info
.cch
= m_text
.length();
427 if ( !::SetMenuItemInfo(hMenu
, id
, FALSE
, &info
) )
429 wxLogLastError(wxT("SetMenuItemInfo"));
433 void wxMenuItem::SetCheckable(bool checkable
)
435 wxMenuItemBase::SetCheckable(checkable
);
436 OWNER_DRAWN_ONLY( wxOwnerDrawn::SetCheckable(checkable
) );
439 // ----------------------------------------------------------------------------
441 // ----------------------------------------------------------------------------
443 wxMenuItem
*wxMenuItemBase::New(wxMenu
*parentMenu
,
445 const wxString
& name
,
446 const wxString
& help
,
450 return new wxMenuItem(parentMenu
, id
, name
, help
, kind
, subMenu
);
453 #endif // wxUSE_MENUS