]> git.saurik.com Git - wxWidgets.git/blame - src/msw/menuitem.cpp
Fixed tab removal bug, and empty text element
[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
efebabb7 140#if WXWIN_COMPATIBILITY_2_8
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}
efebabb7 155#endif
2368dcda
VZ
156
157void wxMenuItem::Init()
158{
be15b995 159 m_radioGroup.start = -1;
598ddd96 160 m_isRadioGroupStart = false;
0472ece7 161
47d67540 162#if wxUSE_OWNER_DRAWN
c2dcfdef 163
51d2fa37
VZ
164 // when the color is not valid, wxOwnerDraw takes the default ones.
165 // If we set the colors here and they are changed by the user during
166 // the execution, then the colors are not updated until the application
167 // is restarted and our menus look bad
168 SetTextColour(wxNullColour);
169 SetBackgroundColour(wxNullColour);
6d5b2a57 170
271fa250 171 // setting default colors switched ownerdraw on: switch it off again
2bda0e17
KB
172 ResetOwnerDrawn();
173
271fa250 174 // switch ownerdraw back on if using a non default margin
fa7134b0 175 if ( !IsSeparator() )
271fa250
JS
176 SetMarginWidth(GetMarginWidth());
177
220aea7b 178 // tell the owner drawing code to show the accel string as well
9a83f860 179 SetAccelString(m_text.AfterFirst(wxT('\t')));
974e8d94 180#endif // wxUSE_OWNER_DRAWN
2bda0e17
KB
181}
182
c2dcfdef 183wxMenuItem::~wxMenuItem()
2bda0e17
KB
184{
185}
186
187// misc
188// ----
189
c2dcfdef 190// return the id for calling Win32 API functions
dca0f651 191WXWPARAM wxMenuItem::GetMSWId() const
c2dcfdef 192{
660e7fda
VZ
193 // we must use ids in unsigned short range with Windows functions, if we
194 // pass ids > USHRT_MAX to them they get very confused (e.g. start
195 // generating WM_COMMAND messages with negative high word of wParam), so
196 // use the cast to ensure the id is in range
dca0f651 197 return m_subMenu ? wxPtrToUInt(m_subMenu->GetHMenu())
5c33522f 198 : static_cast<unsigned short>(GetId());
c2dcfdef
VZ
199}
200
3dfac970
VZ
201// get item state
202// --------------
203
a8cfd0cb 204bool wxMenuItem::IsChecked() const
3dfac970 205{
654c223b
VZ
206 // fix that RTTI is always getting the correct state (separators cannot be
207 // checked, but the Windows call below returns true
fa7134b0 208 if ( IsSeparator() )
654c223b 209 return false;
e70b4f10 210
654c223b
VZ
211 // the item might not be attached to a menu yet
212 //
213 // TODO: shouldn't we just always call the base class version? It seems
214 // like it ought to always be in sync
215 if ( !m_parentMenu )
216 return wxMenuItemBase::IsChecked();
217
218 HMENU hmenu = GetHMenuOf(m_parentMenu);
219 int flag = ::GetMenuState(hmenu, GetMSWId(), MF_BYCOMMAND);
3dfac970 220
4aee367e 221 return (flag & MF_CHECKED) != 0;
3dfac970
VZ
222}
223
be15b995
VZ
224// radio group stuff
225// -----------------
226
227void wxMenuItem::SetAsRadioGroupStart()
228{
598ddd96 229 m_isRadioGroupStart = true;
be15b995
VZ
230}
231
232void wxMenuItem::SetRadioGroupStart(int start)
233{
234 wxASSERT_MSG( !m_isRadioGroupStart,
9a83f860 235 wxT("should only be called for the next radio items") );
be15b995
VZ
236
237 m_radioGroup.start = start;
238}
239
240void wxMenuItem::SetRadioGroupEnd(int end)
241{
242 wxASSERT_MSG( m_isRadioGroupStart,
9a83f860 243 wxT("should only be called for the first radio item") );
be15b995
VZ
244
245 m_radioGroup.end = end;
246}
247
2bda0e17
KB
248// change item state
249// -----------------
250
717a57c2 251void wxMenuItem::Enable(bool enable)
2bda0e17 252{
717a57c2
VZ
253 if ( m_isEnabled == enable )
254 return;
255
654c223b
VZ
256 if ( m_parentMenu )
257 {
258 long rc = EnableMenuItem(GetHMenuOf(m_parentMenu),
259 GetMSWId(),
260 MF_BYCOMMAND |
261 (enable ? MF_ENABLED : MF_GRAYED));
c2dcfdef 262
654c223b
VZ
263 if ( rc == -1 )
264 {
265 wxLogLastError(wxT("EnableMenuItem"));
266 }
c2dcfdef 267 }
717a57c2
VZ
268
269 wxMenuItemBase::Enable(enable);
2bda0e17
KB
270}
271
717a57c2 272void wxMenuItem::Check(bool check)
2bda0e17 273{
d65c269b 274 wxCHECK_RET( IsCheckable(), wxT("only checkable items may be checked") );
c2dcfdef 275
717a57c2
VZ
276 if ( m_isChecked == check )
277 return;
c2dcfdef 278
654c223b 279 if ( m_parentMenu )
0472ece7 280 {
654c223b
VZ
281 int flags = check ? MF_CHECKED : MF_UNCHECKED;
282 HMENU hmenu = GetHMenuOf(m_parentMenu);
0472ece7 283
654c223b 284 if ( GetKind() == wxITEM_RADIO )
be15b995 285 {
654c223b
VZ
286 // it doesn't make sense to uncheck a radio item -- what would this
287 // do?
288 if ( !check )
289 return;
290
291 // get the index of this item in the menu
292 const wxMenuItemList& items = m_parentMenu->GetMenuItems();
293 int pos = items.IndexOf(this);
294 wxCHECK_RET( pos != wxNOT_FOUND,
9a83f860 295 wxT("menuitem not found in the menu items list?") );
654c223b
VZ
296
297 // get the radio group range
298 int start,
299 end;
300
301 if ( m_isRadioGroupStart )
302 {
303 // we already have all information we need
304 start = pos;
305 end = m_radioGroup.end;
306 }
307 else // next radio group item
308 {
309 // get the radio group end from the start item
310 start = m_radioGroup.start;
311 end = items.Item(start)->GetData()->m_radioGroup.end;
312 }
be15b995 313
0472ece7 314#ifdef __WIN32__
654c223b
VZ
315 // calling CheckMenuRadioItem() with such parameters hangs my system
316 // (NT4 SP6) and I suspect this could happen to the others as well,
317 // so don't do it!
318 wxCHECK_RET( start != -1 && end != -1,
9a83f860 319 wxT("invalid ::CheckMenuRadioItem() parameter(s)") );
654c223b
VZ
320
321 if ( !::CheckMenuRadioItem(hmenu,
322 start, // the first radio group item
323 end, // the last one
324 pos, // the one to check
325 MF_BYPOSITION) )
326 {
9a83f860 327 wxLogLastError(wxT("CheckMenuRadioItem"));
654c223b 328 }
0472ece7
VZ
329#endif // __WIN32__
330
654c223b
VZ
331 // also uncheck all the other items in this radio group
332 wxMenuItemList::compatibility_iterator node = items.Item(start);
333 for ( int n = start; n <= end && node; n++ )
0472ece7 334 {
654c223b
VZ
335 if ( n != pos )
336 {
337 node->GetData()->m_isChecked = false;
338 }
0472ece7 339
654c223b
VZ
340 node = node->GetNext();
341 }
0472ece7 342 }
654c223b 343 else // check item
0472ece7 344 {
654c223b
VZ
345 if ( ::CheckMenuItem(hmenu,
346 GetMSWId(),
347 MF_BYCOMMAND | flags) == (DWORD)-1 )
348 {
9a83f860 349 wxFAIL_MSG(wxT("CheckMenuItem() failed, item not in the menu?"));
654c223b 350 }
0472ece7 351 }
c2dcfdef 352 }
717a57c2
VZ
353
354 wxMenuItemBase::Check(check);
c2dcfdef
VZ
355}
356
52af3158 357void wxMenuItem::SetItemLabel(const wxString& txt)
c2dcfdef 358{
ee0a94cf
RR
359 wxString text = txt;
360
c2dcfdef 361 // don't do anything if label didn't change
ee0a94cf 362 if ( m_text == txt )
c2dcfdef
VZ
363 return;
364
345319d6 365 // wxMenuItemBase will do stock ID checks
52af3158 366 wxMenuItemBase::SetItemLabel(text);
345319d6
VZ
367
368 // m_text could now be different from 'text' if we are a stock menu item,
369 // so use only m_text below
370
371 OWNER_DRAWN_ONLY( wxOwnerDrawn::SetName(m_text) );
2a2a71e3
JS
372#if wxUSE_OWNER_DRAWN
373 // tell the owner drawing code to to show the accel string as well
9a83f860 374 SetAccelString(m_text.AfterFirst(wxT('\t')));
2a2a71e3 375#endif
c2dcfdef 376
5c6aad47
VZ
377 // the item can be not attached to any menu yet and SetItemLabel() is still
378 // valid to call in this case and should do nothing else
3350ab0c
VZ
379 if ( !m_parentMenu )
380 return;
381
654c223b
VZ
382#if wxUSE_ACCEL
383 m_parentMenu->UpdateAccel(this);
384#endif // wxUSE_ACCEL
385
5c6aad47
VZ
386 const UINT id = GetMSWId();
387 HMENU hMenu = GetHMenuOf(m_parentMenu);
388 if ( !hMenu || ::GetMenuState(hMenu, id, MF_BYCOMMAND) == (UINT)-1 )
389 return;
390
3d45718d
VZ
391#if wxUSE_OWNER_DRAWN
392 if ( IsOwnerDrawn() )
c2dcfdef 393 {
3d45718d
VZ
394 // we don't need to do anything for owner drawn items, they will redraw
395 // themselves using the new text the next time they're displayed
396 return;
c2dcfdef 397 }
3d45718d 398#endif // owner drawn
c2dcfdef 399
3d45718d 400 // update the text of the native menu item
3d45718d 401 WinStruct<MENUITEMINFO> info;
c2dcfdef 402
3d45718d
VZ
403 // surprisingly, calling SetMenuItemInfo() with just MIIM_STRING doesn't
404 // work as it resets the menu bitmap, so we need to first get the old item
405 // state and then modify it
406 const bool isLaterThanWin95 = wxGetWinVersion() > wxWinVersion_95;
d4290fa5
VZ
407 info.fMask = MIIM_STATE |
408 MIIM_ID |
409 MIIM_SUBMENU |
410 MIIM_CHECKMARKS |
411 MIIM_DATA;
3d45718d
VZ
412 if ( isLaterThanWin95 )
413 info.fMask |= MIIM_BITMAP | MIIM_FTYPE;
414 else
d4290fa5 415 info.fMask |= MIIM_TYPE;
3d45718d
VZ
416 if ( !::GetMenuItemInfo(hMenu, id, FALSE, &info) )
417 {
418 wxLogLastError(wxT("GetMenuItemInfo"));
419 return;
420 }
421
422 if ( isLaterThanWin95 )
423 info.fMask |= MIIM_STRING;
424 //else: MIIM_TYPE already specified
425 info.dwTypeData = (LPTSTR)m_text.wx_str();
426 info.cch = m_text.length();
427 if ( !::SetMenuItemInfo(hMenu, id, FALSE, &info) )
428 {
429 wxLogLastError(wxT("SetMenuItemInfo"));
c2dcfdef
VZ
430 }
431}
2bda0e17 432
974e8d94
VZ
433void wxMenuItem::SetCheckable(bool checkable)
434{
435 wxMenuItemBase::SetCheckable(checkable);
436 OWNER_DRAWN_ONLY( wxOwnerDrawn::SetCheckable(checkable) );
437}
438
439// ----------------------------------------------------------------------------
440// wxMenuItemBase
441// ----------------------------------------------------------------------------
442
443wxMenuItem *wxMenuItemBase::New(wxMenu *parentMenu,
444 int id,
445 const wxString& name,
446 const wxString& help,
d65c269b 447 wxItemKind kind,
974e8d94
VZ
448 wxMenu *subMenu)
449{
d65c269b 450 return new wxMenuItem(parentMenu, id, name, help, kind, subMenu);
974e8d94 451}
1e6feb95
VZ
452
453#endif // wxUSE_MENUS