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