]> git.saurik.com Git - wxWidgets.git/blob - src/msw/menuitem.cpp
wxCocoa: Better icon support
[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 licence
10 ///////////////////////////////////////////////////////////////////////////////
11
12 // ===========================================================================
13 // declarations
14 // ===========================================================================
15
16 // ---------------------------------------------------------------------------
17 // headers
18 // ---------------------------------------------------------------------------
19
20 #if defined(__GNUG__) && !defined(NO_GCC_PRAGMA)
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 #ifdef __WXWINCE__
54 // Implemented in menu.cpp
55 UINT GetMenuState(HMENU hMenu, UINT id, UINT flags) ;
56 #endif
57
58 // ---------------------------------------------------------------------------
59 // macro
60 // ---------------------------------------------------------------------------
61
62 // hide the ugly cast
63 #define GetHMenuOf(menu) ((HMENU)menu->GetHMenu())
64
65 // conditional compilation
66 #if wxUSE_OWNER_DRAWN
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
71
72 // ============================================================================
73 // implementation
74 // ============================================================================
75
76 // ----------------------------------------------------------------------------
77 // dynamic classes implementation
78 // ----------------------------------------------------------------------------
79
80 #if wxUSE_EXTENDED_RTTI
81
82 bool wxMenuItemStreamingCallback( const wxObject *object, wxWriter * , wxPersister * , wxxVariantArray & )
83 {
84 const wxMenuItem * mitem = dynamic_cast<const wxMenuItem*>(object) ;
85 if ( mitem->GetMenu() && !mitem->GetMenu()->GetTitle().IsEmpty() )
86 {
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 )
89 return false ;
90 }
91 return true ;
92 }
93
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 )
100
101 IMPLEMENT_DYNAMIC_CLASS_XTI_CALLBACK(wxMenuItem, wxObject,"wx/menuitem.h",wxMenuItemStreamingCallback)
102
103 wxBEGIN_PROPERTIES_TABLE(wxMenuItem)
104 wxPROPERTY( Parent,wxMenu*, SetMenu, GetMenu, , 0 /*flags*/ , wxT("Helpstring") , wxT("group") )
105 wxPROPERTY( Id,int, SetId, GetId, , 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 , , 0 /*flags*/ , wxT("Helpstring") , wxT("group") )
109 wxPROPERTY( SubMenu,wxMenu*, SetSubMenu, GetSubMenu, , 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()
114
115 wxBEGIN_HANDLERS_TABLE(wxMenuItem)
116 wxEND_HANDLERS_TABLE()
117
118 wxDIRECT_CONSTRUCTOR_6( wxMenuItem , wxMenu* , Parent , int , Id , wxString , Text , wxString , Help , wxItemKind , Kind , wxMenu* , SubMenu )
119 #else
120 IMPLEMENT_DYNAMIC_CLASS(wxMenuItem, wxObject)
121 #endif
122
123 // ----------------------------------------------------------------------------
124 // wxMenuItem
125 // ----------------------------------------------------------------------------
126
127 // ctor & dtor
128 // -----------
129
130 wxMenuItem::wxMenuItem(wxMenu *pParentMenu,
131 int id,
132 const wxString& text,
133 const wxString& strHelp,
134 wxItemKind kind,
135 wxMenu *pSubMenu)
136 : wxMenuItemBase(pParentMenu, id, text, strHelp, kind, pSubMenu)
137 #if wxUSE_OWNER_DRAWN
138 , wxOwnerDrawn(text, kind == wxITEM_CHECK, true)
139 #endif // owner drawn
140 {
141 Init();
142 }
143
144 wxMenuItem::wxMenuItem(wxMenu *parentMenu,
145 int id,
146 const wxString& text,
147 const wxString& help,
148 bool isCheckable,
149 wxMenu *subMenu)
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
155 {
156 Init();
157 }
158
159 void wxMenuItem::Init()
160 {
161 m_radioGroup.start = -1;
162 m_isRadioGroupStart = FALSE;
163
164 #if wxUSE_OWNER_DRAWN
165 // set default menu colors
166 #define SYS_COLOR(c) (wxSystemSettings::GetColour(wxSYS_COLOUR_##c))
167
168 SetTextColour(SYS_COLOR(MENUTEXT));
169 SetBackgroundColour(SYS_COLOR(MENU));
170
171 #undef SYS_COLOR
172
173 // we don't want normal items be owner-drawn
174 ResetOwnerDrawn();
175
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
179 }
180
181 wxMenuItem::~wxMenuItem()
182 {
183 }
184
185 // misc
186 // ----
187
188 // return the id for calling Win32 API functions
189 int wxMenuItem::GetRealId() const
190 {
191 return m_subMenu ? (int)m_subMenu->GetHMenu() : GetId();
192 }
193
194 // get item state
195 // --------------
196
197 bool wxMenuItem::IsChecked() const
198 {
199 // fix that RTTI is always getting the correct state (separators cannot be checked, but the call below
200 // returns true
201 if ( GetId() == wxID_SEPARATOR )
202 return false ;
203
204 int flag = ::GetMenuState(GetHMenuOf(m_parentMenu), GetId(), MF_BYCOMMAND);
205
206 return (flag & MF_CHECKED) != 0;
207 }
208
209 /* static */
210 wxString wxMenuItemBase::GetLabelFromText(const wxString& text)
211 {
212 return wxStripMenuCodes(text);
213 }
214
215 // radio group stuff
216 // -----------------
217
218 void wxMenuItem::SetAsRadioGroupStart()
219 {
220 m_isRadioGroupStart = TRUE;
221 }
222
223 void wxMenuItem::SetRadioGroupStart(int start)
224 {
225 wxASSERT_MSG( !m_isRadioGroupStart,
226 _T("should only be called for the next radio items") );
227
228 m_radioGroup.start = start;
229 }
230
231 void wxMenuItem::SetRadioGroupEnd(int end)
232 {
233 wxASSERT_MSG( m_isRadioGroupStart,
234 _T("should only be called for the first radio item") );
235
236 m_radioGroup.end = end;
237 }
238
239 // change item state
240 // -----------------
241
242 void wxMenuItem::Enable(bool enable)
243 {
244 if ( m_isEnabled == enable )
245 return;
246
247 long rc = EnableMenuItem(GetHMenuOf(m_parentMenu),
248 GetRealId(),
249 MF_BYCOMMAND |
250 (enable ? MF_ENABLED : MF_GRAYED));
251
252 if ( rc == -1 ) {
253 wxLogLastError(wxT("EnableMenuItem"));
254 }
255
256 wxMenuItemBase::Enable(enable);
257 }
258
259 void wxMenuItem::Check(bool check)
260 {
261 wxCHECK_RET( IsCheckable(), wxT("only checkable items may be checked") );
262
263 if ( m_isChecked == check )
264 return;
265
266 int flags = check ? MF_CHECKED : MF_UNCHECKED;
267 HMENU hmenu = GetHMenuOf(m_parentMenu);
268
269 if ( GetKind() == wxITEM_RADIO )
270 {
271 // it doesn't make sense to uncheck a radio item - what would this do?
272 if ( !check )
273 return;
274
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?") );
280
281 // get the radio group range
282 int start,
283 end;
284
285 if ( m_isRadioGroupStart )
286 {
287 // we already have all information we need
288 start = pos;
289 end = m_radioGroup.end;
290 }
291 else // next radio group item
292 {
293 // get the radio group end from the start item
294 start = m_radioGroup.start;
295 end = items.Item(start)->GetData()->m_radioGroup.end;
296 }
297
298 #ifdef __WIN32__
299 // calling CheckMenuRadioItem() with such parameters hangs my system
300 // (NT4 SP6) and I suspect this could happen to the others as well - so
301 // don't do it!
302 wxCHECK_RET( start != -1 && end != -1,
303 _T("invalid ::CheckMenuRadioItem() parameter(s)") );
304
305 if ( !::CheckMenuRadioItem(hmenu,
306 start, // the first radio group item
307 end, // the last one
308 pos, // the one to check
309 MF_BYPOSITION) )
310 {
311 wxLogLastError(_T("CheckMenuRadioItem"));
312 }
313 #endif // __WIN32__
314
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++ )
318 {
319 if ( n != pos )
320 {
321 node->GetData()->m_isChecked = FALSE;
322 }
323
324 // we also have to do it in the menu for Win16 (under Win32
325 // CheckMenuRadioItem() does it for us)
326 #ifndef __WIN32__
327 ::CheckMenuItem(hmenu, n, n == pos ? MF_CHECKED : MF_UNCHECKED);
328 #endif // Win16
329
330 node = node->GetNext();
331 }
332 }
333 else // check item
334 {
335 if ( ::CheckMenuItem(hmenu,
336 GetRealId(),
337 MF_BYCOMMAND | flags) == (DWORD)-1 )
338 {
339 wxLogLastError(wxT("CheckMenuItem"));
340 }
341 }
342
343 wxMenuItemBase::Check(check);
344 }
345
346 void wxMenuItem::SetText(const wxString& text)
347 {
348 // don't do anything if label didn't change
349 if ( m_text == text )
350 return;
351
352 wxMenuItemBase::SetText(text);
353 OWNER_DRAWN_ONLY( wxOwnerDrawn::SetName(text) );
354 #if wxUSE_OWNER_DRAWN
355 // tell the owner drawing code to to show the accel string as well
356 SetAccelString(text.AfterFirst(_T('\t')));
357 #endif
358
359 HMENU hMenu = GetHMenuOf(m_parentMenu);
360 wxCHECK_RET( hMenu, wxT("menuitem without menu") );
361
362 #if wxUSE_ACCEL
363 m_parentMenu->UpdateAccel(this);
364 #endif // wxUSE_ACCEL
365
366 UINT id = GetRealId();
367 UINT flagsOld = ::GetMenuState(hMenu, id, MF_BYCOMMAND);
368 if ( flagsOld == 0xFFFFFFFF )
369 {
370 wxLogLastError(wxT("GetMenuState"));
371 }
372 else
373 {
374 if ( IsSubMenu() )
375 {
376 // high byte contains the number of items in a submenu for submenus
377 flagsOld &= 0xFF;
378 flagsOld |= MF_POPUP;
379 }
380
381 LPCTSTR data;
382
383 #if wxUSE_OWNER_DRAWN
384 if ( IsOwnerDrawn() )
385 {
386 flagsOld |= MF_OWNERDRAW;
387 data = (LPCTSTR)this;
388 }
389 else
390 #endif //owner drawn
391 {
392 flagsOld |= MF_STRING;
393 data = (wxChar*) text.c_str();
394 }
395
396 #ifdef __WXWINCE__
397 // FIXME: complete this, applying the old
398 // flags.
399 // However, the WinCE doc for SetMenuItemInfo
400 // says that you can't use it to set the menu
401 // item state; only data, id and type.
402 MENUITEMINFO info;
403 wxZeroMemory(info);
404 info.cbSize = sizeof(info);
405 info.fMask = MIIM_TYPE;
406 info.fType = MFT_STRING;
407 info.cch = text.Length();
408 info.dwTypeData = (LPTSTR) data ;
409 if ( !SetMenuItemInfo(hMenu, id, FALSE, & info) )
410 {
411 wxLogLastError(wxT("SetMenuItemInfo"));
412 }
413 #else
414 if ( ::ModifyMenu(hMenu, id,
415 MF_BYCOMMAND | flagsOld,
416 id, data) == (int)0xFFFFFFFF )
417 {
418 wxLogLastError(wxT("ModifyMenu"));
419 }
420 #endif
421 }
422 }
423
424 void wxMenuItem::SetCheckable(bool checkable)
425 {
426 wxMenuItemBase::SetCheckable(checkable);
427 OWNER_DRAWN_ONLY( wxOwnerDrawn::SetCheckable(checkable) );
428 }
429
430 // ----------------------------------------------------------------------------
431 // wxMenuItemBase
432 // ----------------------------------------------------------------------------
433
434 wxMenuItem *wxMenuItemBase::New(wxMenu *parentMenu,
435 int id,
436 const wxString& name,
437 const wxString& help,
438 wxItemKind kind,
439 wxMenu *subMenu)
440 {
441 return new wxMenuItem(parentMenu, id, name, help, kind, subMenu);
442 }
443
444 #endif // wxUSE_MENUS