]> git.saurik.com Git - wxWidgets.git/blame - src/msw/menuitem.cpp
fix for potention crash when conversion fails
[wxWidgets.git] / src / msw / menuitem.cpp
CommitLineData
2bda0e17 1///////////////////////////////////////////////////////////////////////////////
e4db172a 2// Name: src/msw/menuitem.cpp
2bda0e17
KB
3// Purpose: wxMenuItem implementation
4// Author: Vadim Zeitlin
c2dcfdef 5// Modified by:
2bda0e17
KB
6// Created: 11.11.97
7// RCS-ID: $Id$
8// Copyright: (c) 1998 Vadim Zeitlin <zeitlin@dptmaths.ens-cachan.fr>
65571936 9// Licence: wxWindows licence
2bda0e17
KB
10///////////////////////////////////////////////////////////////////////////////
11
c2dcfdef
VZ
12// ===========================================================================
13// declarations
14// ===========================================================================
15
16// ---------------------------------------------------------------------------
17// headers
18// ---------------------------------------------------------------------------
19
2bda0e17
KB
20// For compilers that support precompilation, includes "wx.h".
21#include "wx/wxprec.h"
22
23#ifdef __BORLANDC__
c2dcfdef 24 #pragma hdrstop
2bda0e17
KB
25#endif
26
1e6feb95
VZ
27#if wxUSE_MENUS
28
3b3dc801 29#include "wx/menuitem.h"
e4db172a 30
2bda0e17 31#ifndef WX_PRECOMP
c2dcfdef
VZ
32 #include "wx/font.h"
33 #include "wx/bitmap.h"
34 #include "wx/settings.h"
4e938f5b 35 #include "wx/window.h"
0c589ad0 36 #include "wx/accel.h"
0c589ad0 37 #include "wx/string.h"
e4db172a 38 #include "wx/log.h"
3b3dc801 39 #include "wx/menu.h"
2bda0e17
KB
40#endif
41
717a57c2
VZ
42#if wxUSE_ACCEL
43 #include "wx/accel.h"
44#endif // wxUSE_ACCEL
45
42e69d6b 46#include "wx/msw/private.h"
ce3ed50d 47
4676948b
JS
48#ifdef __WXWINCE__
49// Implemented in menu.cpp
50UINT GetMenuState(HMENU hMenu, UINT id, UINT flags) ;
51#endif
52
c2dcfdef 53// ---------------------------------------------------------------------------
974e8d94 54// macro
c2dcfdef
VZ
55// ---------------------------------------------------------------------------
56
974e8d94 57// hide the ugly cast
c2dcfdef
VZ
58#define GetHMenuOf(menu) ((HMENU)menu->GetHMenu())
59
974e8d94
VZ
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
2bda0e17
KB
67// ============================================================================
68// implementation
69// ============================================================================
70
71// ----------------------------------------------------------------------------
72// dynamic classes implementation
73// ----------------------------------------------------------------------------
74
e70b4f10
SC
75#if wxUSE_EXTENDED_RTTI
76
77bool wxMenuItemStreamingCallback( const wxObject *object, wxWriter * , wxPersister * , wxxVariantArray & )
78{
79 const wxMenuItem * mitem = dynamic_cast<const wxMenuItem*>(object) ;
3b3dc801 80 if ( mitem->GetMenu() && !mitem->GetMenu()->GetTitle().empty() )
e70b4f10
SC
81 {
82 // we don't stream out the first two items for menus with a title, they will be reconstructed
83 if ( mitem->GetMenu()->FindItemByPosition(0) == mitem || mitem->GetMenu()->FindItemByPosition(1) == mitem )
84 return false ;
85 }
86 return true ;
87}
88
3ff066a4 89wxBEGIN_ENUM( wxItemKind )
598ddd96
WS
90 wxENUM_MEMBER( wxITEM_SEPARATOR )
91 wxENUM_MEMBER( wxITEM_NORMAL )
92 wxENUM_MEMBER( wxITEM_CHECK )
93 wxENUM_MEMBER( wxITEM_RADIO )
3ff066a4 94wxEND_ENUM( wxItemKind )
e70b4f10
SC
95
96IMPLEMENT_DYNAMIC_CLASS_XTI_CALLBACK(wxMenuItem, wxObject,"wx/menuitem.h",wxMenuItemStreamingCallback)
97
3ff066a4 98wxBEGIN_PROPERTIES_TABLE(wxMenuItem)
598ddd96
WS
99 wxPROPERTY( Parent,wxMenu*, SetMenu, GetMenu, EMPTY_MACROVALUE , 0 /*flags*/ , wxT("Helpstring") , wxT("group") )
100 wxPROPERTY( Id,int, SetId, GetId, EMPTY_MACROVALUE , 0 /*flags*/ , wxT("Helpstring") , wxT("group") )
3ff066a4
SC
101 wxPROPERTY( Text, wxString , SetText, GetText, wxString(), 0 /*flags*/ , wxT("Helpstring") , wxT("group") )
102 wxPROPERTY( Help, wxString , SetHelp, GetHelp, wxString(), 0 /*flags*/ , wxT("Helpstring") , wxT("group") )
af498247 103 wxREADONLY_PROPERTY( Kind, wxItemKind , GetKind , EMPTY_MACROVALUE , 0 /*flags*/ , wxT("Helpstring") , wxT("group") )
598ddd96
WS
104 wxPROPERTY( SubMenu,wxMenu*, SetSubMenu, GetSubMenu, EMPTY_MACROVALUE , 0 /*flags*/ , wxT("Helpstring") , wxT("group") )
105 wxPROPERTY( Enabled , bool , Enable , IsEnabled , wxxVariant((bool)true) , 0 /*flags*/ , wxT("Helpstring") , wxT("group"))
106 wxPROPERTY( Checked , bool , Check , IsChecked , wxxVariant((bool)false) , 0 /*flags*/ , wxT("Helpstring") , wxT("group"))
107 wxPROPERTY( Checkable , bool , SetCheckable , IsCheckable , wxxVariant((bool)false) , 0 /*flags*/ , wxT("Helpstring") , wxT("group"))
3ff066a4
SC
108wxEND_PROPERTIES_TABLE()
109
110wxBEGIN_HANDLERS_TABLE(wxMenuItem)
111wxEND_HANDLERS_TABLE()
112
113wxDIRECT_CONSTRUCTOR_6( wxMenuItem , wxMenu* , Parent , int , Id , wxString , Text , wxString , Help , wxItemKind , Kind , wxMenu* , SubMenu )
e70b4f10 114#else
621b3e21 115IMPLEMENT_DYNAMIC_CLASS(wxMenuItem, wxObject)
e70b4f10 116#endif
2bda0e17
KB
117
118// ----------------------------------------------------------------------------
119// wxMenuItem
120// ----------------------------------------------------------------------------
121
122// ctor & dtor
123// -----------
124
974e8d94
VZ
125wxMenuItem::wxMenuItem(wxMenu *pParentMenu,
126 int id,
127 const wxString& text,
128 const wxString& strHelp,
d65c269b 129 wxItemKind kind,
90002c49 130 wxMenu *pSubMenu)
d65c269b 131 : wxMenuItemBase(pParentMenu, id, text, strHelp, kind, pSubMenu)
47d67540 132#if wxUSE_OWNER_DRAWN
51f8b246 133 , wxOwnerDrawn(text, kind == wxITEM_CHECK, true)
974e8d94 134#endif // owner drawn
2bda0e17 135{
2368dcda
VZ
136 Init();
137}
2bda0e17 138
2368dcda
VZ
139wxMenuItem::wxMenuItem(wxMenu *parentMenu,
140 int id,
141 const wxString& text,
142 const wxString& help,
143 bool isCheckable,
144 wxMenu *subMenu)
145 : wxMenuItemBase(parentMenu, id, text, help,
146 isCheckable ? wxITEM_CHECK : wxITEM_NORMAL, subMenu)
147#if wxUSE_OWNER_DRAWN
19b42379 148 , wxOwnerDrawn(text, isCheckable, true)
2368dcda
VZ
149#endif // owner drawn
150{
151 Init();
152}
153
154void wxMenuItem::Init()
155{
be15b995 156 m_radioGroup.start = -1;
598ddd96 157 m_isRadioGroupStart = false;
0472ece7 158
47d67540 159#if wxUSE_OWNER_DRAWN
c2dcfdef 160
51d2fa37
VZ
161 // when the color is not valid, wxOwnerDraw takes the default ones.
162 // If we set the colors here and they are changed by the user during
163 // the execution, then the colors are not updated until the application
164 // is restarted and our menus look bad
165 SetTextColour(wxNullColour);
166 SetBackgroundColour(wxNullColour);
6d5b2a57 167
271fa250 168 // setting default colors switched ownerdraw on: switch it off again
2bda0e17
KB
169 ResetOwnerDrawn();
170
271fa250
JS
171 // switch ownerdraw back on if using a non default margin
172 if ( GetId() != wxID_SEPARATOR )
173 SetMarginWidth(GetMarginWidth());
174
220aea7b 175 // tell the owner drawing code to show the accel string as well
2368dcda 176 SetAccelString(m_text.AfterFirst(_T('\t')));
974e8d94 177#endif // wxUSE_OWNER_DRAWN
2bda0e17
KB
178}
179
c2dcfdef 180wxMenuItem::~wxMenuItem()
2bda0e17
KB
181{
182}
183
184// misc
185// ----
186
c2dcfdef
VZ
187// return the id for calling Win32 API functions
188int wxMenuItem::GetRealId() const
189{
974e8d94 190 return m_subMenu ? (int)m_subMenu->GetHMenu() : GetId();
c2dcfdef
VZ
191}
192
3dfac970
VZ
193// get item state
194// --------------
195
a8cfd0cb 196bool wxMenuItem::IsChecked() const
3dfac970 197{
598ddd96 198 // fix that RTTI is always getting the correct state (separators cannot be checked, but the call below
e70b4f10
SC
199 // returns true
200 if ( GetId() == wxID_SEPARATOR )
201 return false ;
202
a8cfd0cb 203 int flag = ::GetMenuState(GetHMenuOf(m_parentMenu), GetId(), MF_BYCOMMAND);
3dfac970 204
4aee367e 205 return (flag & MF_CHECKED) != 0;
3dfac970
VZ
206}
207
3b59cdbf
VZ
208/* static */
209wxString wxMenuItemBase::GetLabelFromText(const wxString& text)
717a57c2 210{
4a3563c5 211 return wxStripMenuCodes(text);
717a57c2
VZ
212}
213
be15b995
VZ
214// radio group stuff
215// -----------------
216
217void wxMenuItem::SetAsRadioGroupStart()
218{
598ddd96 219 m_isRadioGroupStart = true;
be15b995
VZ
220}
221
222void wxMenuItem::SetRadioGroupStart(int start)
223{
224 wxASSERT_MSG( !m_isRadioGroupStart,
225 _T("should only be called for the next radio items") );
226
227 m_radioGroup.start = start;
228}
229
230void wxMenuItem::SetRadioGroupEnd(int end)
231{
232 wxASSERT_MSG( m_isRadioGroupStart,
233 _T("should only be called for the first radio item") );
234
235 m_radioGroup.end = end;
236}
237
2bda0e17
KB
238// change item state
239// -----------------
240
717a57c2 241void wxMenuItem::Enable(bool enable)
2bda0e17 242{
717a57c2
VZ
243 if ( m_isEnabled == enable )
244 return;
245
246 long rc = EnableMenuItem(GetHMenuOf(m_parentMenu),
247 GetRealId(),
248 MF_BYCOMMAND |
249 (enable ? MF_ENABLED : MF_GRAYED));
c2dcfdef 250
717a57c2 251 if ( rc == -1 ) {
f6bcfd97 252 wxLogLastError(wxT("EnableMenuItem"));
c2dcfdef 253 }
717a57c2
VZ
254
255 wxMenuItemBase::Enable(enable);
2bda0e17
KB
256}
257
717a57c2 258void wxMenuItem::Check(bool check)
2bda0e17 259{
d65c269b 260 wxCHECK_RET( IsCheckable(), wxT("only checkable items may be checked") );
c2dcfdef 261
717a57c2
VZ
262 if ( m_isChecked == check )
263 return;
c2dcfdef 264
0472ece7
VZ
265 int flags = check ? MF_CHECKED : MF_UNCHECKED;
266 HMENU hmenu = GetHMenuOf(m_parentMenu);
267
546bfbea 268 if ( GetKind() == wxITEM_RADIO )
0472ece7
VZ
269 {
270 // it doesn't make sense to uncheck a radio item - what would this do?
271 if ( !check )
272 return;
273
be15b995 274 // get the index of this item in the menu
0472ece7
VZ
275 const wxMenuItemList& items = m_parentMenu->GetMenuItems();
276 int pos = items.IndexOf(this);
277 wxCHECK_RET( pos != wxNOT_FOUND,
278 _T("menuitem not found in the menu items list?") );
279
be15b995
VZ
280 // get the radio group range
281 int start,
282 end;
283
284 if ( m_isRadioGroupStart )
285 {
286 // we already have all information we need
287 start = pos;
288 end = m_radioGroup.end;
289 }
290 else // next radio group item
291 {
292 // get the radio group end from the start item
293 start = m_radioGroup.start;
294 end = items.Item(start)->GetData()->m_radioGroup.end;
295 }
296
0472ece7 297#ifdef __WIN32__
be15b995
VZ
298 // calling CheckMenuRadioItem() with such parameters hangs my system
299 // (NT4 SP6) and I suspect this could happen to the others as well - so
300 // don't do it!
301 wxCHECK_RET( start != -1 && end != -1,
302 _T("invalid ::CheckMenuRadioItem() parameter(s)") );
303
0472ece7 304 if ( !::CheckMenuRadioItem(hmenu,
be15b995
VZ
305 start, // the first radio group item
306 end, // the last one
307 pos, // the one to check
0f243af3 308 MF_BYPOSITION) )
0472ece7
VZ
309 {
310 wxLogLastError(_T("CheckMenuRadioItem"));
311 }
312#endif // __WIN32__
313
314 // also uncheck all the other items in this radio group
222ed1d6 315 wxMenuItemList::compatibility_iterator node = items.Item(start);
be15b995 316 for ( int n = start; n <= end && node; n++ )
0472ece7
VZ
317 {
318 if ( n != pos )
319 {
598ddd96 320 node->GetData()->m_isChecked = false;
0472ece7
VZ
321 }
322
0472ece7
VZ
323 node = node->GetNext();
324 }
325 }
326 else // check item
327 {
328 if ( ::CheckMenuItem(hmenu,
329 GetRealId(),
9f385aa0 330 MF_BYCOMMAND | flags) == (DWORD)-1 )
0472ece7 331 {
c9cb79cb 332 wxFAIL_MSG( _T("CheckMenuItem() failed, item not in the menu?") );
0472ece7 333 }
c2dcfdef 334 }
717a57c2
VZ
335
336 wxMenuItemBase::Check(check);
c2dcfdef
VZ
337}
338
974e8d94 339void wxMenuItem::SetText(const wxString& text)
c2dcfdef
VZ
340{
341 // don't do anything if label didn't change
974e8d94 342 if ( m_text == text )
c2dcfdef
VZ
343 return;
344
974e8d94
VZ
345 wxMenuItemBase::SetText(text);
346 OWNER_DRAWN_ONLY( wxOwnerDrawn::SetName(text) );
2a2a71e3
JS
347#if wxUSE_OWNER_DRAWN
348 // tell the owner drawing code to to show the accel string as well
349 SetAccelString(text.AfterFirst(_T('\t')));
350#endif
c2dcfdef 351
974e8d94 352 HMENU hMenu = GetHMenuOf(m_parentMenu);
717a57c2
VZ
353 wxCHECK_RET( hMenu, wxT("menuitem without menu") );
354
355#if wxUSE_ACCEL
356 m_parentMenu->UpdateAccel(this);
357#endif // wxUSE_ACCEL
2bda0e17 358
c2dcfdef
VZ
359 UINT id = GetRealId();
360 UINT flagsOld = ::GetMenuState(hMenu, id, MF_BYCOMMAND);
361 if ( flagsOld == 0xFFFFFFFF )
362 {
ea16d351
RD
363 // It's not an error, it means that the menu item doesn't exist
364 //wxLogLastError(wxT("GetMenuState"));
c2dcfdef
VZ
365 }
366 else
367 {
368 if ( IsSubMenu() )
369 {
370 // high byte contains the number of items in a submenu for submenus
371 flagsOld &= 0xFF;
372 flagsOld |= MF_POPUP;
373 }
374
837e5743 375 LPCTSTR data;
974e8d94 376
c2dcfdef
VZ
377#if wxUSE_OWNER_DRAWN
378 if ( IsOwnerDrawn() )
379 {
380 flagsOld |= MF_OWNERDRAW;
837e5743 381 data = (LPCTSTR)this;
c2dcfdef
VZ
382 }
383 else
384#endif //owner drawn
385 {
386 flagsOld |= MF_STRING;
f5166ed4 387 data = (wxChar*) text.c_str();
c2dcfdef
VZ
388 }
389
4676948b
JS
390#ifdef __WXWINCE__
391 // FIXME: complete this, applying the old
09785dd3
JS
392 // flags.
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.
4676948b
JS
396 MENUITEMINFO info;
397 wxZeroMemory(info);
398 info.cbSize = sizeof(info);
399 info.fMask = MIIM_TYPE;
400 info.fType = MFT_STRING;
e4db172a 401 info.cch = text.length();
4676948b 402 info.dwTypeData = (LPTSTR) data ;
598ddd96 403 if ( !::SetMenuItemInfo(hMenu, id, FALSE, & info) )
4676948b
JS
404 {
405 wxLogLastError(wxT("SetMenuItemInfo"));
406 }
407#else
c2dcfdef
VZ
408 if ( ::ModifyMenu(hMenu, id,
409 MF_BYCOMMAND | flagsOld,
a17e237f 410 id, data) == (int)0xFFFFFFFF )
c2dcfdef 411 {
223d09f6 412 wxLogLastError(wxT("ModifyMenu"));
c2dcfdef 413 }
4676948b 414#endif
c2dcfdef
VZ
415 }
416}
2bda0e17 417
974e8d94
VZ
418void wxMenuItem::SetCheckable(bool checkable)
419{
420 wxMenuItemBase::SetCheckable(checkable);
421 OWNER_DRAWN_ONLY( wxOwnerDrawn::SetCheckable(checkable) );
422}
423
424// ----------------------------------------------------------------------------
425// wxMenuItemBase
426// ----------------------------------------------------------------------------
427
428wxMenuItem *wxMenuItemBase::New(wxMenu *parentMenu,
429 int id,
430 const wxString& name,
431 const wxString& help,
d65c269b 432 wxItemKind kind,
974e8d94
VZ
433 wxMenu *subMenu)
434{
d65c269b 435 return new wxMenuItem(parentMenu, id, name, help, kind, subMenu);
974e8d94 436}
1e6feb95
VZ
437
438#endif // wxUSE_MENUS