]> git.saurik.com Git - wxWidgets.git/blob - src/msw/menuitem.cpp
Added wxPaintDCEx class, to handle the case where an HDC
[wxWidgets.git] / src / msw / menuitem.cpp
1 ///////////////////////////////////////////////////////////////////////////////
2 // Name: 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 #ifdef __GNUG__
21 #pragma implementation "menuitem.h"
22 #endif
23
24 // For compilers that support precompilation, includes "wx.h".
25 #include "wx/wxprec.h"
26
27 #ifdef __BORLANDC__
28 #pragma hdrstop
29 #endif
30
31 #if wxUSE_MENUS
32
33 #ifndef WX_PRECOMP
34 #include "wx/font.h"
35 #include "wx/bitmap.h"
36 #include "wx/settings.h"
37 #include "wx/font.h"
38 #include "wx/window.h"
39 #include "wx/accel.h"
40 #include "wx/menu.h"
41 #include "wx/string.h"
42 #endif
43
44 #include "wx/menuitem.h"
45 #include "wx/log.h"
46
47 #if wxUSE_ACCEL
48 #include "wx/accel.h"
49 #endif // wxUSE_ACCEL
50
51 #include "wx/msw/private.h"
52
53 #ifdef __WXWINCE__
54 // Implemented in menu.cpp
55 UINT GetMenuState(HMENU hMenu, UINT id, UINT flags) ;
56 #endif
57
58 // ---------------------------------------------------------------------------
59 // macro
60 // ---------------------------------------------------------------------------
61
62 // hide the ugly cast
63 #define GetHMenuOf(menu) ((HMENU)menu->GetHMenu())
64
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
72 // ============================================================================
73 // implementation
74 // ============================================================================
75
76 // ----------------------------------------------------------------------------
77 // dynamic classes implementation
78 // ----------------------------------------------------------------------------
79
80 IMPLEMENT_DYNAMIC_CLASS(wxMenuItem, wxObject)
81
82 // ----------------------------------------------------------------------------
83 // wxMenuItem
84 // ----------------------------------------------------------------------------
85
86 // ctor & dtor
87 // -----------
88
89 wxMenuItem::wxMenuItem(wxMenu *pParentMenu,
90 int id,
91 const wxString& text,
92 const wxString& strHelp,
93 wxItemKind kind,
94 wxMenu *pSubMenu)
95 : wxMenuItemBase(pParentMenu, id, text, strHelp, kind, pSubMenu)
96 #if wxUSE_OWNER_DRAWN
97 , wxOwnerDrawn(text, kind == wxITEM_CHECK)
98 #endif // owner drawn
99 {
100 Init();
101 }
102
103 wxMenuItem::wxMenuItem(wxMenu *parentMenu,
104 int id,
105 const wxString& text,
106 const wxString& help,
107 bool isCheckable,
108 wxMenu *subMenu)
109 : wxMenuItemBase(parentMenu, id, text, help,
110 isCheckable ? wxITEM_CHECK : wxITEM_NORMAL, subMenu)
111 #if wxUSE_OWNER_DRAWN
112 , wxOwnerDrawn(text, isCheckable, true)
113 #endif // owner drawn
114 {
115 Init();
116 }
117
118 void wxMenuItem::Init()
119 {
120 m_radioGroup.start = -1;
121 m_isRadioGroupStart = FALSE;
122
123 #if wxUSE_OWNER_DRAWN
124 // set default menu colors
125 #define SYS_COLOR(c) (wxSystemSettings::GetColour(wxSYS_COLOUR_##c))
126
127 SetTextColour(SYS_COLOR(MENUTEXT));
128 SetBackgroundColour(SYS_COLOR(MENU));
129
130 #undef SYS_COLOR
131
132 // we don't want normal items be owner-drawn
133 ResetOwnerDrawn();
134
135 // tell the owner drawing code to to show the accel string as well
136 SetAccelString(m_text.AfterFirst(_T('\t')));
137 #endif // wxUSE_OWNER_DRAWN
138 }
139
140 wxMenuItem::~wxMenuItem()
141 {
142 }
143
144 // misc
145 // ----
146
147 // return the id for calling Win32 API functions
148 int wxMenuItem::GetRealId() const
149 {
150 return m_subMenu ? (int)m_subMenu->GetHMenu() : GetId();
151 }
152
153 // get item state
154 // --------------
155
156 bool wxMenuItem::IsChecked() const
157 {
158 int flag = ::GetMenuState(GetHMenuOf(m_parentMenu), GetId(), MF_BYCOMMAND);
159
160 return (flag & MF_CHECKED) != 0;
161 }
162
163 /* static */
164 wxString wxMenuItemBase::GetLabelFromText(const wxString& text)
165 {
166 return wxStripMenuCodes(text);
167 }
168
169 // radio group stuff
170 // -----------------
171
172 void wxMenuItem::SetAsRadioGroupStart()
173 {
174 m_isRadioGroupStart = TRUE;
175 }
176
177 void wxMenuItem::SetRadioGroupStart(int start)
178 {
179 wxASSERT_MSG( !m_isRadioGroupStart,
180 _T("should only be called for the next radio items") );
181
182 m_radioGroup.start = start;
183 }
184
185 void wxMenuItem::SetRadioGroupEnd(int end)
186 {
187 wxASSERT_MSG( m_isRadioGroupStart,
188 _T("should only be called for the first radio item") );
189
190 m_radioGroup.end = end;
191 }
192
193 // change item state
194 // -----------------
195
196 void wxMenuItem::Enable(bool enable)
197 {
198 if ( m_isEnabled == enable )
199 return;
200
201 long rc = EnableMenuItem(GetHMenuOf(m_parentMenu),
202 GetRealId(),
203 MF_BYCOMMAND |
204 (enable ? MF_ENABLED : MF_GRAYED));
205
206 if ( rc == -1 ) {
207 wxLogLastError(wxT("EnableMenuItem"));
208 }
209
210 wxMenuItemBase::Enable(enable);
211 }
212
213 void wxMenuItem::Check(bool check)
214 {
215 wxCHECK_RET( IsCheckable(), wxT("only checkable items may be checked") );
216
217 if ( m_isChecked == check )
218 return;
219
220 int flags = check ? MF_CHECKED : MF_UNCHECKED;
221 HMENU hmenu = GetHMenuOf(m_parentMenu);
222
223 if ( GetKind() == wxITEM_RADIO )
224 {
225 // it doesn't make sense to uncheck a radio item - what would this do?
226 if ( !check )
227 return;
228
229 // get the index of this item in the menu
230 const wxMenuItemList& items = m_parentMenu->GetMenuItems();
231 int pos = items.IndexOf(this);
232 wxCHECK_RET( pos != wxNOT_FOUND,
233 _T("menuitem not found in the menu items list?") );
234
235 // get the radio group range
236 int start,
237 end;
238
239 if ( m_isRadioGroupStart )
240 {
241 // we already have all information we need
242 start = pos;
243 end = m_radioGroup.end;
244 }
245 else // next radio group item
246 {
247 // get the radio group end from the start item
248 start = m_radioGroup.start;
249 end = items.Item(start)->GetData()->m_radioGroup.end;
250 }
251
252 #ifdef __WIN32__
253 // calling CheckMenuRadioItem() with such parameters hangs my system
254 // (NT4 SP6) and I suspect this could happen to the others as well - so
255 // don't do it!
256 wxCHECK_RET( start != -1 && end != -1,
257 _T("invalid ::CheckMenuRadioItem() parameter(s)") );
258
259 if ( !::CheckMenuRadioItem(hmenu,
260 start, // the first radio group item
261 end, // the last one
262 pos, // the one to check
263 MF_BYPOSITION) )
264 {
265 wxLogLastError(_T("CheckMenuRadioItem"));
266 }
267 #endif // __WIN32__
268
269 // also uncheck all the other items in this radio group
270 wxMenuItemList::compatibility_iterator node = items.Item(start);
271 for ( int n = start; n <= end && node; n++ )
272 {
273 if ( n != pos )
274 {
275 node->GetData()->m_isChecked = FALSE;
276 }
277
278 // we also have to do it in the menu for Win16 (under Win32
279 // CheckMenuRadioItem() does it for us)
280 #ifndef __WIN32__
281 ::CheckMenuItem(hmenu, n, n == pos ? MF_CHECKED : MF_UNCHECKED);
282 #endif // Win16
283
284 node = node->GetNext();
285 }
286 }
287 else // check item
288 {
289 if ( ::CheckMenuItem(hmenu,
290 GetRealId(),
291 MF_BYCOMMAND | flags) == (DWORD)-1 )
292 {
293 wxLogLastError(wxT("CheckMenuItem"));
294 }
295 }
296
297 wxMenuItemBase::Check(check);
298 }
299
300 void wxMenuItem::SetText(const wxString& text)
301 {
302 // don't do anything if label didn't change
303 if ( m_text == text )
304 return;
305
306 wxMenuItemBase::SetText(text);
307 OWNER_DRAWN_ONLY( wxOwnerDrawn::SetName(text) );
308 #if wxUSE_OWNER_DRAWN
309 // tell the owner drawing code to to show the accel string as well
310 SetAccelString(text.AfterFirst(_T('\t')));
311 #endif
312
313 HMENU hMenu = GetHMenuOf(m_parentMenu);
314 wxCHECK_RET( hMenu, wxT("menuitem without menu") );
315
316 #if wxUSE_ACCEL
317 m_parentMenu->UpdateAccel(this);
318 #endif // wxUSE_ACCEL
319
320 UINT id = GetRealId();
321 UINT flagsOld = ::GetMenuState(hMenu, id, MF_BYCOMMAND);
322 if ( flagsOld == 0xFFFFFFFF )
323 {
324 wxLogLastError(wxT("GetMenuState"));
325 }
326 else
327 {
328 if ( IsSubMenu() )
329 {
330 // high byte contains the number of items in a submenu for submenus
331 flagsOld &= 0xFF;
332 flagsOld |= MF_POPUP;
333 }
334
335 LPCTSTR data;
336
337 #if wxUSE_OWNER_DRAWN
338 if ( IsOwnerDrawn() )
339 {
340 flagsOld |= MF_OWNERDRAW;
341 data = (LPCTSTR)this;
342 }
343 else
344 #endif //owner drawn
345 {
346 flagsOld |= MF_STRING;
347 data = (wxChar*) text.c_str();
348 }
349
350 #ifdef __WXWINCE__
351 // FIXME: complete this, applying the old
352 // flags.
353 // However, the WinCE doc for SetMenuItemInfo
354 // says that you can't use it to set the menu
355 // item state; only data, id and type.
356 MENUITEMINFO info;
357 wxZeroMemory(info);
358 info.cbSize = sizeof(info);
359 info.fMask = MIIM_TYPE;
360 info.fType = MFT_STRING;
361 info.cch = text.Length();
362 info.dwTypeData = (LPTSTR) data ;
363 if ( !SetMenuItemInfo(hMenu, id, FALSE, & info) )
364 {
365 wxLogLastError(wxT("SetMenuItemInfo"));
366 }
367 #else
368 if ( ::ModifyMenu(hMenu, id,
369 MF_BYCOMMAND | flagsOld,
370 id, data) == (int)0xFFFFFFFF )
371 {
372 wxLogLastError(wxT("ModifyMenu"));
373 }
374 #endif
375 }
376 }
377
378 void wxMenuItem::SetCheckable(bool checkable)
379 {
380 wxMenuItemBase::SetCheckable(checkable);
381 OWNER_DRAWN_ONLY( wxOwnerDrawn::SetCheckable(checkable) );
382 }
383
384 // ----------------------------------------------------------------------------
385 // wxMenuItemBase
386 // ----------------------------------------------------------------------------
387
388 wxMenuItem *wxMenuItemBase::New(wxMenu *parentMenu,
389 int id,
390 const wxString& name,
391 const wxString& help,
392 wxItemKind kind,
393 wxMenu *subMenu)
394 {
395 return new wxMenuItem(parentMenu, id, name, help, kind, subMenu);
396 }
397
398 #endif // wxUSE_MENUS