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