]> git.saurik.com Git - wxWidgets.git/blame_incremental - src/msw/menuitem.cpp
Add a SetDoubleBuffered method for wxMSW (XP+)
[wxWidgets.git] / src / msw / menuitem.cpp
... / ...
CommitLineData
1///////////////////////////////////////////////////////////////////////////////
2// Name: src/msw/menuitem.cpp
3// Purpose: wxMenuItem implementation
4// Author: Vadim Zeitlin
5// Modified by:
6// Created: 11.11.97
7// RCS-ID: $Id$
8// Copyright: (c) 1998 Vadim Zeitlin <zeitlin@dptmaths.ens-cachan.fr>
9// Licence: wxWindows licence
10///////////////////////////////////////////////////////////////////////////////
11
12// ===========================================================================
13// declarations
14// ===========================================================================
15
16// ---------------------------------------------------------------------------
17// headers
18// ---------------------------------------------------------------------------
19
20// For compilers that support precompilation, includes "wx.h".
21#include "wx/wxprec.h"
22
23#ifdef __BORLANDC__
24 #pragma hdrstop
25#endif
26
27#if wxUSE_MENUS
28
29#include "wx/menuitem.h"
30#include "wx/stockitem.h"
31
32#ifndef WX_PRECOMP
33 #include "wx/font.h"
34 #include "wx/bitmap.h"
35 #include "wx/settings.h"
36 #include "wx/window.h"
37 #include "wx/accel.h"
38 #include "wx/string.h"
39 #include "wx/log.h"
40 #include "wx/menu.h"
41#endif
42
43#if wxUSE_ACCEL
44 #include "wx/accel.h"
45#endif // wxUSE_ACCEL
46
47#include "wx/msw/private.h"
48
49#ifdef __WXWINCE__
50// Implemented in menu.cpp
51UINT GetMenuState(HMENU hMenu, UINT id, UINT flags) ;
52#endif
53
54// ---------------------------------------------------------------------------
55// macro
56// ---------------------------------------------------------------------------
57
58// hide the ugly cast
59#define GetHMenuOf(menu) ((HMENU)menu->GetHMenu())
60
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
68// ============================================================================
69// implementation
70// ============================================================================
71
72// ----------------------------------------------------------------------------
73// dynamic classes implementation
74// ----------------------------------------------------------------------------
75
76#if wxUSE_EXTENDED_RTTI
77
78bool wxMenuItemStreamingCallback( const wxObject *object, wxWriter * , wxPersister * , wxxVariantArray & )
79{
80 const wxMenuItem * mitem = dynamic_cast<const wxMenuItem*>(object) ;
81 if ( mitem->GetMenu() && !mitem->GetMenu()->GetTitle().empty() )
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
90wxBEGIN_ENUM( wxItemKind )
91 wxENUM_MEMBER( wxITEM_SEPARATOR )
92 wxENUM_MEMBER( wxITEM_NORMAL )
93 wxENUM_MEMBER( wxITEM_CHECK )
94 wxENUM_MEMBER( wxITEM_RADIO )
95wxEND_ENUM( wxItemKind )
96
97IMPLEMENT_DYNAMIC_CLASS_XTI_CALLBACK(wxMenuItem, wxObject,"wx/menuitem.h",wxMenuItemStreamingCallback)
98
99wxBEGIN_PROPERTIES_TABLE(wxMenuItem)
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") )
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") )
104 wxREADONLY_PROPERTY( Kind, wxItemKind , GetKind , EMPTY_MACROVALUE , 0 /*flags*/ , wxT("Helpstring") , wxT("group") )
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"))
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 )
115#else
116IMPLEMENT_DYNAMIC_CLASS(wxMenuItem, wxObject)
117#endif
118
119// ----------------------------------------------------------------------------
120// wxMenuItem
121// ----------------------------------------------------------------------------
122
123// ctor & dtor
124// -----------
125
126wxMenuItem::wxMenuItem(wxMenu *pParentMenu,
127 int id,
128 const wxString& text,
129 const wxString& strHelp,
130 wxItemKind kind,
131 wxMenu *pSubMenu)
132 : wxMenuItemBase(pParentMenu, id, text, strHelp, kind, pSubMenu)
133#if wxUSE_OWNER_DRAWN
134 , wxOwnerDrawn(text, kind == wxITEM_CHECK, true)
135#endif // owner drawn
136{
137 Init();
138}
139
140#if WXWIN_COMPATIBILITY_2_8
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
150 , wxOwnerDrawn(text, isCheckable, true)
151#endif // owner drawn
152{
153 Init();
154}
155#endif
156
157void wxMenuItem::Init()
158{
159 m_radioGroup.start = -1;
160 m_isRadioGroupStart = false;
161
162#if wxUSE_OWNER_DRAWN
163
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);
170
171 // setting default colors switched ownerdraw on: switch it off again
172 ResetOwnerDrawn();
173
174 // switch ownerdraw back on if using a non default margin
175 if ( !IsSeparator() )
176 SetMarginWidth(GetMarginWidth());
177
178 // tell the owner drawing code to show the accel string as well
179 SetAccelString(m_text.AfterFirst(_T('\t')));
180#endif // wxUSE_OWNER_DRAWN
181}
182
183wxMenuItem::~wxMenuItem()
184{
185}
186
187// misc
188// ----
189
190// return the id for calling Win32 API functions
191WXWPARAM wxMenuItem::GetMSWId() const
192{
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
197 return m_subMenu ? wxPtrToUInt(m_subMenu->GetHMenu())
198 : wx_static_cast(unsigned short, GetId());
199}
200
201// get item state
202// --------------
203
204bool wxMenuItem::IsChecked() const
205{
206 // fix that RTTI is always getting the correct state (separators cannot be checked, but the call below
207 // returns true
208 if ( IsSeparator() )
209 return false ;
210
211 int flag = ::GetMenuState(GetHMenuOf(m_parentMenu), GetMSWId(), MF_BYCOMMAND);
212
213 return (flag & MF_CHECKED) != 0;
214}
215
216/* static */
217wxString wxMenuItemBase::GetLabelText(const wxString& text)
218{
219 return wxStripMenuCodes(text);
220}
221
222// radio group stuff
223// -----------------
224
225void wxMenuItem::SetAsRadioGroupStart()
226{
227 m_isRadioGroupStart = true;
228}
229
230void wxMenuItem::SetRadioGroupStart(int start)
231{
232 wxASSERT_MSG( !m_isRadioGroupStart,
233 _T("should only be called for the next radio items") );
234
235 m_radioGroup.start = start;
236}
237
238void wxMenuItem::SetRadioGroupEnd(int end)
239{
240 wxASSERT_MSG( m_isRadioGroupStart,
241 _T("should only be called for the first radio item") );
242
243 m_radioGroup.end = end;
244}
245
246// change item state
247// -----------------
248
249void wxMenuItem::Enable(bool enable)
250{
251 if ( m_isEnabled == enable )
252 return;
253
254 long rc = EnableMenuItem(GetHMenuOf(m_parentMenu),
255 GetMSWId(),
256 MF_BYCOMMAND |
257 (enable ? MF_ENABLED : MF_GRAYED));
258
259 if ( rc == -1 ) {
260 wxLogLastError(wxT("EnableMenuItem"));
261 }
262
263 wxMenuItemBase::Enable(enable);
264}
265
266void wxMenuItem::Check(bool check)
267{
268 wxCHECK_RET( IsCheckable(), wxT("only checkable items may be checked") );
269
270 if ( m_isChecked == check )
271 return;
272
273 int flags = check ? MF_CHECKED : MF_UNCHECKED;
274 HMENU hmenu = GetHMenuOf(m_parentMenu);
275
276 if ( GetKind() == wxITEM_RADIO )
277 {
278 // it doesn't make sense to uncheck a radio item - what would this do?
279 if ( !check )
280 return;
281
282 // get the index of this item in the menu
283 const wxMenuItemList& items = m_parentMenu->GetMenuItems();
284 int pos = items.IndexOf(this);
285 wxCHECK_RET( pos != wxNOT_FOUND,
286 _T("menuitem not found in the menu items list?") );
287
288 // get the radio group range
289 int start,
290 end;
291
292 if ( m_isRadioGroupStart )
293 {
294 // we already have all information we need
295 start = pos;
296 end = m_radioGroup.end;
297 }
298 else // next radio group item
299 {
300 // get the radio group end from the start item
301 start = m_radioGroup.start;
302 end = items.Item(start)->GetData()->m_radioGroup.end;
303 }
304
305#ifdef __WIN32__
306 // calling CheckMenuRadioItem() with such parameters hangs my system
307 // (NT4 SP6) and I suspect this could happen to the others as well - so
308 // don't do it!
309 wxCHECK_RET( start != -1 && end != -1,
310 _T("invalid ::CheckMenuRadioItem() parameter(s)") );
311
312 if ( !::CheckMenuRadioItem(hmenu,
313 start, // the first radio group item
314 end, // the last one
315 pos, // the one to check
316 MF_BYPOSITION) )
317 {
318 wxLogLastError(_T("CheckMenuRadioItem"));
319 }
320#endif // __WIN32__
321
322 // also uncheck all the other items in this radio group
323 wxMenuItemList::compatibility_iterator node = items.Item(start);
324 for ( int n = start; n <= end && node; n++ )
325 {
326 if ( n != pos )
327 {
328 node->GetData()->m_isChecked = false;
329 }
330
331 node = node->GetNext();
332 }
333 }
334 else // check item
335 {
336 if ( ::CheckMenuItem(hmenu,
337 GetMSWId(),
338 MF_BYCOMMAND | flags) == (DWORD)-1 )
339 {
340 wxFAIL_MSG( _T("CheckMenuItem() failed, item not in the menu?") );
341 }
342 }
343
344 wxMenuItemBase::Check(check);
345}
346
347void wxMenuItem::SetItemLabel(const wxString& txt)
348{
349 wxString text = txt;
350
351 // don't do anything if label didn't change
352 if ( m_text == txt )
353 return;
354
355 // wxMenuItemBase will do stock ID checks
356 wxMenuItemBase::SetItemLabel(text);
357
358 // m_text could now be different from 'text' if we are a stock menu item,
359 // so use only m_text below
360
361 OWNER_DRAWN_ONLY( wxOwnerDrawn::SetName(m_text) );
362#if wxUSE_OWNER_DRAWN
363 // tell the owner drawing code to to show the accel string as well
364 SetAccelString(m_text.AfterFirst(_T('\t')));
365#endif
366
367 HMENU hMenu = GetHMenuOf(m_parentMenu);
368 wxCHECK_RET( hMenu, wxT("menuitem without menu") );
369
370#if wxUSE_ACCEL
371 m_parentMenu->UpdateAccel(this);
372#endif // wxUSE_ACCEL
373
374 UINT id = GetMSWId();
375 UINT flagsOld = ::GetMenuState(hMenu, id, MF_BYCOMMAND);
376 if ( flagsOld == 0xFFFFFFFF )
377 {
378 // It's not an error, it means that the menu item doesn't exist
379 //wxLogLastError(wxT("GetMenuState"));
380 }
381 else
382 {
383 if ( IsSubMenu() )
384 {
385 // high byte contains the number of items in a submenu for submenus
386 flagsOld &= 0xFF;
387 flagsOld |= MF_POPUP;
388 }
389
390 LPCTSTR data;
391
392#if wxUSE_OWNER_DRAWN
393 if ( IsOwnerDrawn() )
394 {
395 flagsOld |= MF_OWNERDRAW;
396 data = (LPCTSTR)this;
397 }
398 else
399#endif //owner drawn
400 {
401 flagsOld |= MF_STRING;
402 data = (wxChar*) m_text.wx_str();
403 }
404
405#ifdef __WXWINCE__
406 // FIXME: complete this, applying the old
407 // flags.
408 // However, the WinCE doc for SetMenuItemInfo
409 // says that you can't use it to set the menu
410 // item state; only data, id and type.
411 MENUITEMINFO info;
412 wxZeroMemory(info);
413 info.cbSize = sizeof(info);
414 info.fMask = MIIM_TYPE;
415 info.fType = MFT_STRING;
416 info.cch = m_text.length();
417 info.dwTypeData = (LPTSTR) data ;
418 if ( !::SetMenuItemInfo(hMenu, id, FALSE, & info) )
419 {
420 wxLogLastError(wxT("SetMenuItemInfo"));
421 }
422#else
423 if ( ::ModifyMenu(hMenu, id,
424 MF_BYCOMMAND | flagsOld,
425 id, data) == (int)0xFFFFFFFF )
426 {
427 wxLogLastError(wxT("ModifyMenu"));
428 }
429#endif
430 }
431}
432
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,
447 wxItemKind kind,
448 wxMenu *subMenu)
449{
450 return new wxMenuItem(parentMenu, id, name, help, kind, subMenu);
451}
452
453#endif // wxUSE_MENUS