Applied patch [ 642172 ] Fix menu accelerators in ownerdrw
[wxWidgets.git] / src / msw / menuitem.cpp
1 ///////////////////////////////////////////////////////////////////////////////
2 // Name: menuitem.cpp
3 // Purpose: wxMenuItem implementation
4 // Author: Vadim Zeitlin
5 // Modified by:
6 // Created: 11.11.97
7 // RCS-ID: $Id$
8 // Copyright: (c) 1998 Vadim Zeitlin <zeitlin@dptmaths.ens-cachan.fr>
9 // Licence: wxWindows license
10 ///////////////////////////////////////////////////////////////////////////////
11
12 // ===========================================================================
13 // declarations
14 // ===========================================================================
15
16 // ---------------------------------------------------------------------------
17 // headers
18 // ---------------------------------------------------------------------------
19
20 #ifdef __GNUG__
21 #pragma implementation "menuitem.h"
22 #endif
23
24 // For compilers that support precompilation, includes "wx.h".
25 #include "wx/wxprec.h"
26
27 #ifdef __BORLANDC__
28 #pragma hdrstop
29 #endif
30
31 #if wxUSE_MENUS
32
33 #ifndef WX_PRECOMP
34 #include "wx/font.h"
35 #include "wx/bitmap.h"
36 #include "wx/settings.h"
37 #include "wx/font.h"
38 #include "wx/window.h"
39 #include "wx/accel.h"
40 #include "wx/menu.h"
41 #include "wx/string.h"
42 #endif
43
44 #include "wx/menuitem.h"
45 #include "wx/log.h"
46
47 #if wxUSE_ACCEL
48 #include "wx/accel.h"
49 #endif // wxUSE_ACCEL
50
51 #include "wx/msw/private.h"
52
53 // ---------------------------------------------------------------------------
54 // macro
55 // ---------------------------------------------------------------------------
56
57 // hide the ugly cast
58 #define GetHMenuOf(menu) ((HMENU)menu->GetHMenu())
59
60 // conditional compilation
61 #if wxUSE_OWNER_DRAWN
62 #define OWNER_DRAWN_ONLY( code ) if ( IsOwnerDrawn() ) code
63 #else // !wxUSE_OWNER_DRAWN
64 #define OWNER_DRAWN_ONLY( code )
65 #endif // wxUSE_OWNER_DRAWN/!wxUSE_OWNER_DRAWN
66
67 // ============================================================================
68 // implementation
69 // ============================================================================
70
71 // ----------------------------------------------------------------------------
72 // dynamic classes implementation
73 // ----------------------------------------------------------------------------
74
75 IMPLEMENT_DYNAMIC_CLASS(wxMenuItem, wxObject)
76
77 // ----------------------------------------------------------------------------
78 // wxMenuItem
79 // ----------------------------------------------------------------------------
80
81 // ctor & dtor
82 // -----------
83
84 wxMenuItem::wxMenuItem(wxMenu *pParentMenu,
85 int id,
86 const wxString& text,
87 const wxString& strHelp,
88 wxItemKind kind,
89 wxMenu *pSubMenu)
90 : wxMenuItemBase(pParentMenu, id, text, strHelp, kind, pSubMenu)
91 #if wxUSE_OWNER_DRAWN
92 , wxOwnerDrawn(text, kind == wxITEM_CHECK)
93 #endif // owner drawn
94 {
95 Init();
96 }
97
98 wxMenuItem::wxMenuItem(wxMenu *parentMenu,
99 int id,
100 const wxString& text,
101 const wxString& help,
102 bool isCheckable,
103 wxMenu *subMenu)
104 : wxMenuItemBase(parentMenu, id, text, help,
105 isCheckable ? wxITEM_CHECK : wxITEM_NORMAL, subMenu)
106 #if wxUSE_OWNER_DRAWN
107 , wxOwnerDrawn(text, isCheckable)
108 #endif // owner drawn
109 {
110 Init();
111 }
112
113 void wxMenuItem::Init()
114 {
115 m_radioGroup.start = -1;
116 m_isRadioGroupStart = FALSE;
117
118 #if wxUSE_OWNER_DRAWN
119 // set default menu colors
120 #define SYS_COLOR(c) (wxSystemSettings::GetColour(wxSYS_COLOUR_##c))
121
122 SetTextColour(SYS_COLOR(MENUTEXT));
123 SetBackgroundColour(SYS_COLOR(MENU));
124
125 #undef SYS_COLOR
126
127 // we don't want normal items be owner-drawn
128 ResetOwnerDrawn();
129
130 // tell the owner drawing code to to show the accel string as well
131 SetAccelString(m_text.AfterFirst(_T('\t')));
132 #endif // wxUSE_OWNER_DRAWN
133 }
134
135 wxMenuItem::~wxMenuItem()
136 {
137 }
138
139 // misc
140 // ----
141
142 // return the id for calling Win32 API functions
143 int wxMenuItem::GetRealId() const
144 {
145 return m_subMenu ? (int)m_subMenu->GetHMenu() : GetId();
146 }
147
148 // get item state
149 // --------------
150
151 bool wxMenuItem::IsChecked() const
152 {
153 int flag = ::GetMenuState(GetHMenuOf(m_parentMenu), GetId(), MF_BYCOMMAND);
154
155 return (flag & MF_CHECKED) != 0;
156 }
157
158 /* static */
159 wxString wxMenuItemBase::GetLabelFromText(const wxString& text)
160 {
161 return wxStripMenuCodes(text);
162 }
163
164 // radio group stuff
165 // -----------------
166
167 void wxMenuItem::SetAsRadioGroupStart()
168 {
169 m_isRadioGroupStart = TRUE;
170 }
171
172 void wxMenuItem::SetRadioGroupStart(int start)
173 {
174 wxASSERT_MSG( !m_isRadioGroupStart,
175 _T("should only be called for the next radio items") );
176
177 m_radioGroup.start = start;
178 }
179
180 void wxMenuItem::SetRadioGroupEnd(int end)
181 {
182 wxASSERT_MSG( m_isRadioGroupStart,
183 _T("should only be called for the first radio item") );
184
185 m_radioGroup.end = end;
186 }
187
188 // change item state
189 // -----------------
190
191 void wxMenuItem::Enable(bool enable)
192 {
193 if ( m_isEnabled == enable )
194 return;
195
196 long rc = EnableMenuItem(GetHMenuOf(m_parentMenu),
197 GetRealId(),
198 MF_BYCOMMAND |
199 (enable ? MF_ENABLED : MF_GRAYED));
200
201 if ( rc == -1 ) {
202 wxLogLastError(wxT("EnableMenuItem"));
203 }
204
205 wxMenuItemBase::Enable(enable);
206 }
207
208 void wxMenuItem::Check(bool check)
209 {
210 wxCHECK_RET( IsCheckable(), wxT("only checkable items may be checked") );
211
212 if ( m_isChecked == check )
213 return;
214
215 int flags = check ? MF_CHECKED : MF_UNCHECKED;
216 HMENU hmenu = GetHMenuOf(m_parentMenu);
217
218 if ( GetKind() == wxITEM_RADIO )
219 {
220 // it doesn't make sense to uncheck a radio item - what would this do?
221 if ( !check )
222 return;
223
224 // get the index of this item in the menu
225 const wxMenuItemList& items = m_parentMenu->GetMenuItems();
226 int pos = items.IndexOf(this);
227 wxCHECK_RET( pos != wxNOT_FOUND,
228 _T("menuitem not found in the menu items list?") );
229
230 // get the radio group range
231 int start,
232 end;
233
234 if ( m_isRadioGroupStart )
235 {
236 // we already have all information we need
237 start = pos;
238 end = m_radioGroup.end;
239 }
240 else // next radio group item
241 {
242 // get the radio group end from the start item
243 start = m_radioGroup.start;
244 end = items.Item(start)->GetData()->m_radioGroup.end;
245 }
246
247 #ifdef __WIN32__
248 // calling CheckMenuRadioItem() with such parameters hangs my system
249 // (NT4 SP6) and I suspect this could happen to the others as well - so
250 // don't do it!
251 wxCHECK_RET( start != -1 && end != -1,
252 _T("invalid ::CheckMenuRadioItem() parameter(s)") );
253
254 if ( !::CheckMenuRadioItem(hmenu,
255 start, // the first radio group item
256 end, // the last one
257 pos, // the one to check
258 MF_BYPOSITION) )
259 {
260 wxLogLastError(_T("CheckMenuRadioItem"));
261 }
262 #endif // __WIN32__
263
264 // also uncheck all the other items in this radio group
265 wxMenuItemList::Node *node = items.Item(start);
266 for ( int n = start; n <= end && node; n++ )
267 {
268 if ( n != pos )
269 {
270 node->GetData()->m_isChecked = FALSE;
271 }
272
273 // we also have to do it in the menu for Win16 (under Win32
274 // CheckMenuRadioItem() does it for us)
275 #ifndef __WIN32__
276 ::CheckMenuItem(hmenu, n, n == pos ? MF_CHECKED : MF_UNCHECKED);
277 #endif // Win16
278
279 node = node->GetNext();
280 }
281 }
282 else // check item
283 {
284 if ( ::CheckMenuItem(hmenu,
285 GetRealId(),
286 MF_BYCOMMAND | flags) == (DWORD)-1 )
287 {
288 wxLogLastError(wxT("CheckMenuItem"));
289 }
290 }
291
292 wxMenuItemBase::Check(check);
293 }
294
295 void wxMenuItem::SetText(const wxString& text)
296 {
297 // don't do anything if label didn't change
298 if ( m_text == text )
299 return;
300
301 wxMenuItemBase::SetText(text);
302 OWNER_DRAWN_ONLY( wxOwnerDrawn::SetName(text) );
303 #if wxUSE_OWNER_DRAWN
304 // tell the owner drawing code to to show the accel string as well
305 SetAccelString(text.AfterFirst(_T('\t')));
306 #endif
307
308 HMENU hMenu = GetHMenuOf(m_parentMenu);
309 wxCHECK_RET( hMenu, wxT("menuitem without menu") );
310
311 #if wxUSE_ACCEL
312 m_parentMenu->UpdateAccel(this);
313 #endif // wxUSE_ACCEL
314
315 UINT id = GetRealId();
316 UINT flagsOld = ::GetMenuState(hMenu, id, MF_BYCOMMAND);
317 if ( flagsOld == 0xFFFFFFFF )
318 {
319 wxLogLastError(wxT("GetMenuState"));
320 }
321 else
322 {
323 if ( IsSubMenu() )
324 {
325 // high byte contains the number of items in a submenu for submenus
326 flagsOld &= 0xFF;
327 flagsOld |= MF_POPUP;
328 }
329
330 LPCTSTR data;
331
332 #if wxUSE_OWNER_DRAWN
333 if ( IsOwnerDrawn() )
334 {
335 flagsOld |= MF_OWNERDRAW;
336 data = (LPCTSTR)this;
337 }
338 else
339 #endif //owner drawn
340 {
341 flagsOld |= MF_STRING;
342 data = (wxChar*) text.c_str();
343 }
344
345 if ( ::ModifyMenu(hMenu, id,
346 MF_BYCOMMAND | flagsOld,
347 id, data) == (int)0xFFFFFFFF )
348 {
349 wxLogLastError(wxT("ModifyMenu"));
350 }
351 }
352 }
353
354 void wxMenuItem::SetCheckable(bool checkable)
355 {
356 wxMenuItemBase::SetCheckable(checkable);
357 OWNER_DRAWN_ONLY( wxOwnerDrawn::SetCheckable(checkable) );
358 }
359
360 // ----------------------------------------------------------------------------
361 // wxMenuItemBase
362 // ----------------------------------------------------------------------------
363
364 wxMenuItem *wxMenuItemBase::New(wxMenu *parentMenu,
365 int id,
366 const wxString& name,
367 const wxString& help,
368 wxItemKind kind,
369 wxMenu *subMenu)
370 {
371 return new wxMenuItem(parentMenu, id, name, help, kind, subMenu);
372 }
373
374 #endif // wxUSE_MENUS