Merge in from trunk r67662 to r64801
[wxWidgets.git] / src / osx / menuitem_osx.cpp
1 ///////////////////////////////////////////////////////////////////////////////
2 // Name: src/osx/menuitem_osx.cpp
3 // Purpose: wxMenuItem implementation
4 // Author: Stefan Csomor
5 // Modified by:
6 // Created: 1998-01-01
7 // RCS-ID: $Id$
8 // Copyright: (c) Stefan Csomor
9 // Licence: wxWindows licence
10 ///////////////////////////////////////////////////////////////////////////////
11
12 #include "wx/wxprec.h"
13
14 #if wxUSE_MENUS
15
16 #include "wx/menuitem.h"
17 #include "wx/stockitem.h"
18
19 #ifndef WX_PRECOMP
20 #include "wx/app.h"
21 #include "wx/menu.h"
22 #endif // WX_PRECOMP
23
24 #include "wx/osx/private.h"
25
26 IMPLEMENT_ABSTRACT_CLASS( wxMenuItemImpl , wxObject )
27
28 wxMenuItemImpl::~wxMenuItemImpl()
29 {
30 }
31
32 wxMenuItem::wxMenuItem(wxMenu *pParentMenu,
33 int id,
34 const wxString& t,
35 const wxString& strHelp,
36 wxItemKind kind,
37 wxMenu *pSubMenu)
38 :wxMenuItemBase(pParentMenu, id, t, strHelp, kind, pSubMenu)
39 {
40 wxASSERT_MSG( id != 0 || pSubMenu != NULL , wxT("A MenuItem ID of Zero does not work under Mac") ) ;
41
42 // In other languages there is no difference in naming the Exit/Quit menu item between MacOS and Windows guidelines
43 // therefore these item must not be translated
44 if (pParentMenu != NULL && !pParentMenu->GetNoEventsMode())
45 if ( wxStripMenuCodes(m_text).Upper() == wxT("EXIT") )
46 m_text = wxT("Quit\tCtrl+Q") ;
47
48 m_radioGroup.start = -1;
49 m_isRadioGroupStart = false;
50
51 wxString text = wxStripMenuCodes(m_text, (pParentMenu != NULL && pParentMenu->GetNoEventsMode()) ? wxStrip_Accel : wxStrip_All);
52 if (text.IsEmpty() && !IsSeparator())
53 {
54 wxASSERT_MSG(wxIsStockID(GetId()), wxT("A non-stock menu item with an empty label?"));
55 text = wxGetStockLabel(GetId(), wxSTOCK_WITH_ACCELERATOR|wxSTOCK_WITH_MNEMONIC);
56 }
57
58 wxAcceleratorEntry *entry = wxAcceleratorEntry::Create( m_text ) ;
59 // use accessors for ID and Kind because they might have been changed in the base constructor
60 m_peer = wxMenuItemImpl::Create( this, pParentMenu, GetId(), text, entry, strHelp, GetKind(), pSubMenu );
61 delete entry;
62 }
63
64 wxMenuItem::~wxMenuItem()
65 {
66 delete m_peer;
67 }
68
69 // change item state
70 // -----------------
71
72 void wxMenuItem::SetBitmap(const wxBitmap& bitmap)
73 {
74 m_bitmap = bitmap;
75 UpdateItemBitmap();
76 }
77
78 void wxMenuItem::Enable(bool bDoEnable)
79 {
80 if (( m_isEnabled != bDoEnable
81 // avoid changing menuitem state when menu is disabled
82 // eg. BeginAppModalStateForWindow() will disable menus and ignore this change
83 // which in turn causes m_isEnabled to become out of sync with real menuitem state
84 #if wxOSX_USE_CARBON
85 && !(m_parentMenu && !IsMenuItemEnabled(MAC_WXHMENU(m_parentMenu->GetHMenu()), 0))
86 #endif
87 )
88 // always update builtin menuitems
89 || ( GetId() == wxApp::s_macPreferencesMenuItemId
90 || GetId() == wxApp::s_macExitMenuItemId
91 || GetId() == wxApp::s_macAboutMenuItemId
92 ))
93 {
94 wxMenuItemBase::Enable( bDoEnable ) ;
95 UpdateItemStatus() ;
96 }
97 }
98
99 void wxMenuItem::UncheckRadio()
100 {
101 if ( m_isChecked )
102 {
103 wxMenuItemBase::Check( false ) ;
104 UpdateItemStatus() ;
105 }
106 }
107
108 void wxMenuItem::Check(bool bDoCheck)
109 {
110 wxCHECK_RET( IsCheckable() && !IsSeparator(), wxT("only checkable items may be checked") );
111
112 if ( m_isChecked != bDoCheck )
113 {
114 if ( GetKind() == wxITEM_RADIO )
115 {
116 if ( bDoCheck )
117 {
118 wxMenuItemBase::Check( bDoCheck ) ;
119 UpdateItemStatus() ;
120
121 // get the index of this item in the menu
122 const wxMenuItemList& items = m_parentMenu->GetMenuItems();
123 int pos = items.IndexOf(this);
124 wxCHECK_RET( pos != wxNOT_FOUND,
125 wxT("menuitem not found in the menu items list?") );
126
127 // get the radio group range
128 int start, end;
129
130 if ( m_isRadioGroupStart )
131 {
132 // we already have all information we need
133 start = pos;
134 end = m_radioGroup.end;
135 }
136 else // next radio group item
137 {
138 // get the radio group end from the start item
139 start = m_radioGroup.start;
140 end = items.Item(start)->GetData()->m_radioGroup.end;
141 }
142
143 // also uncheck all the other items in this radio group
144 wxMenuItemList::compatibility_iterator node = items.Item(start);
145 for ( int n = start; n <= end && node; n++ )
146 {
147 if ( n != pos )
148 ((wxMenuItem*)node->GetData())->UncheckRadio();
149
150 node = node->GetNext();
151 }
152 }
153 }
154 else
155 {
156 wxMenuItemBase::Check( bDoCheck ) ;
157 UpdateItemStatus() ;
158 }
159 }
160 }
161
162 void wxMenuItem::SetItemLabel(const wxString& text)
163 {
164 // don't do anything if label didn't change
165 if ( m_text == text )
166 return;
167
168 wxMenuItemBase::SetItemLabel(text);
169
170 UpdateItemText() ;
171 }
172
173
174 void wxMenuItem::UpdateItemBitmap()
175 {
176 if ( !m_parentMenu )
177 return;
178
179 if ( m_bitmap.IsOk() )
180 {
181 GetPeer()->SetBitmap( m_bitmap );
182 }
183 }
184
185 void wxMenuItem::UpdateItemStatus()
186 {
187 if ( !m_parentMenu )
188 return ;
189
190 if ( IsSeparator() )
191 return ;
192
193 if ( IsCheckable() && IsChecked() )
194 GetPeer()->Check( true );
195 else
196 GetPeer()->Check( false );
197
198 GetPeer()->Enable( IsEnabled() );
199 }
200
201 void wxMenuItem::UpdateItemText()
202 {
203 if ( !m_parentMenu )
204 return ;
205
206 wxString text = wxStripMenuCodes(m_text, m_parentMenu != NULL && m_parentMenu->GetNoEventsMode() ? wxStrip_Accel : wxStrip_All);
207 if (text.IsEmpty() && !IsSeparator())
208 {
209 wxASSERT_MSG(wxIsStockID(GetId()), wxT("A non-stock menu item with an empty label?"));
210 text = wxGetStockLabel(GetId(), wxSTOCK_WITH_ACCELERATOR|wxSTOCK_WITH_MNEMONIC);
211 }
212
213 wxAcceleratorEntry *entry = wxAcceleratorEntry::Create( m_text ) ;
214 GetPeer()->SetLabel( text, entry );
215 delete entry ;
216 }
217
218 // radio group stuff
219 // -----------------
220
221 void wxMenuItem::SetAsRadioGroupStart()
222 {
223 m_isRadioGroupStart = true;
224 }
225
226 void wxMenuItem::SetRadioGroupStart(int start)
227 {
228 wxASSERT_MSG( !m_isRadioGroupStart,
229 wxT("should only be called for the next radio items") );
230
231 m_radioGroup.start = start;
232 }
233
234 void wxMenuItem::SetRadioGroupEnd(int end)
235 {
236 wxASSERT_MSG( m_isRadioGroupStart,
237 wxT("should only be called for the first radio item") );
238
239 m_radioGroup.end = end;
240 }
241
242 // ----------------------------------------------------------------------------
243 // wxMenuItemBase
244 // ----------------------------------------------------------------------------
245
246 wxMenuItem *wxMenuItemBase::New(wxMenu *parentMenu,
247 int id,
248 const wxString& name,
249 const wxString& help,
250 wxItemKind kind,
251 wxMenu *subMenu)
252 {
253 return new wxMenuItem(parentMenu, id, name, help, kind, subMenu);
254 }
255
256 #endif