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