cleaned up the checkbox creation code (~70 lines of code became 3)
[wxWidgets.git] / src / os2 / menuitem.cpp
1 ///////////////////////////////////////////////////////////////////////////////
2 // Name: menuitem.cpp
3 // Purpose: wxMenuItem implementation
4 // Author: David Webster
5 // Modified by:
6 // Created: 10/10/98
7 // RCS-ID: $Id$
8 // Copyright: (c) David Webster
9 // Licence: wxWindows licence
10 ///////////////////////////////////////////////////////////////////////////////
11
12 // ============================================================================
13 // headers & declarations
14 // ============================================================================
15
16 #ifdef __GNUG__
17 #pragma implementation "menuitem.h"
18 #endif
19
20 // For compilers that support precompilation, includes "wx.h".
21 #include "wx/wxprec.h"
22
23 #ifndef WX_PRECOMP
24 #include "wx/font.h"
25 #include "wx/bitmap.h"
26 #include "wx/settings.h"
27 #include "wx/font.h"
28 #include "wx/window.h"
29 #include "wx/accel.h"
30 #include "wx/menu.h"
31 #include "wx/string.h"
32 #endif
33
34 #include "wx/menuitem.h"
35 #include "wx/log.h"
36
37 #if wxUSE_ACCEL
38 #include "wx/accel.h"
39 #endif // wxUSE_ACCEL
40
41 #include "wx/os2/private.h"
42
43 // ---------------------------------------------------------------------------
44 // macro
45 // ---------------------------------------------------------------------------
46
47 // hide the ugly cast
48 #define GetHMenuOf(menu) ((HMENU)menu->GetHMenu())
49
50 // conditional compilation
51 #if wxUSE_OWNER_DRAWN
52 #define OWNER_DRAWN_ONLY( code ) if ( IsOwnerDrawn() ) code
53 #else // !wxUSE_OWNER_DRAWN
54 #define OWNER_DRAWN_ONLY( code )
55 #endif // wxUSE_OWNER_DRAWN/!wxUSE_OWNER_DRAWN
56
57 // ----------------------------------------------------------------------------
58 // static function for translating menu labels
59 // ----------------------------------------------------------------------------
60
61 static wxString TextToLabel(const wxString& rTitle)
62 {
63 wxString Title;
64 const wxChar *pc;
65 for (pc = rTitle.c_str(); *pc != wxT('\0'); pc++ )
66 {
67 if (*pc == wxT('&') )
68 {
69 if (*(pc+1) == wxT('&'))
70 {
71 pc++;
72 Title << wxT('&');
73 }
74 else
75 Title << wxT('~');
76 }
77 else
78 {
79 if ( *pc == wxT('~') )
80 {
81 // tildes must be doubled to prevent them from being
82 // interpreted as accelerator character prefix by PM ???
83 Title << *pc;
84 }
85 Title << *pc;
86 }
87 }
88 return Title;
89 }
90
91 // ============================================================================
92 // implementation
93 // ============================================================================
94
95 // ----------------------------------------------------------------------------
96 // dynamic classes implementation
97 // ----------------------------------------------------------------------------
98
99 IMPLEMENT_DYNAMIC_CLASS(wxMenuItem, wxObject)
100
101 // ----------------------------------------------------------------------------
102 // wxMenuItem
103 // ----------------------------------------------------------------------------
104
105 // ctor & dtor
106 // -----------
107
108 wxMenuItem::wxMenuItem(
109 wxMenu* pParentMenu
110 , int nId
111 , const wxString& rsText
112 , const wxString& rsHelp
113 , wxItemKind eKind
114 , wxMenu* pSubMenu
115 )
116 : wxMenuItemBase( pParentMenu
117 ,nId
118 ,TextToLabel(rsText)
119 ,rsHelp
120 ,eKind
121 ,pSubMenu
122 )
123 #if wxUSE_OWNER_DRAWN
124 , wxOwnerDrawn( TextToLabel(rsText)
125 ,eKind == wxITEM_CHECK
126 )
127 #endif // owner drawn
128 {
129 wxASSERT_MSG(pParentMenu != NULL, wxT("a menu item should have a parent"));
130
131 Init();
132 } // end of wxMenuItem::wxMenuItem
133
134 wxMenuItem::wxMenuItem(
135 wxMenu* pParentMenu
136 , int nId
137 , const wxString& rsText
138 , const wxString& rsHelp
139 , bool bIsCheckable
140 , wxMenu* pSubMenu
141 )
142 : wxMenuItemBase( pParentMenu
143 ,nId
144 ,TextToLabel(rsText)
145 ,rsHelp
146 ,bIsCheckable ? wxITEM_CHECK : wxITEM_NORMAL
147 ,pSubMenu
148 )
149 #if wxUSE_OWNER_DRAWN
150 , wxOwnerDrawn( TextToLabel(rsText)
151 ,bIsCheckable
152 )
153 #endif // owner drawn
154 {
155 wxASSERT_MSG(pParentMenu != NULL, wxT("a menu item should have a parent"));
156
157 Init();
158 } // end of wxMenuItem::wxMenuItem
159
160 void wxMenuItem::Init()
161 {
162 m_vRadioGroup.m_nStart = -1;
163 m_bIsRadioGroupStart = FALSE;
164
165 #if wxUSE_OWNER_DRAWN
166 //
167 // Set default menu colors
168 //
169 #define SYS_COLOR(c) (wxSystemSettings::GetColour(wxSYS_COLOUR_##c))
170
171 SetTextColour(SYS_COLOR(MENUTEXT));
172 SetBackgroundColour(SYS_COLOR(MENU));
173
174 #undef SYS_COLOR
175
176 //
177 // We don't want normal items be owner-drawn
178 //
179 ResetOwnerDrawn();
180
181 //
182 // Tell the owner drawing code to to show the accel string as well
183 //
184 SetAccelString(m_text.AfterFirst(_T('\t')));
185 #endif // wxUSE_OWNER_DRAWN
186 } // end of wxMenuItem::Init
187
188 wxMenuItem::~wxMenuItem()
189 {
190 } // end of wxMenuItem::~wxMenuItem
191
192 //
193 // Misc
194 // ----
195
196 //
197 // Return the id for calling Win32 API functions
198 //
199 int wxMenuItem::GetRealId() const
200 {
201 return m_subMenu ? (int)m_subMenu->GetHMenu() : GetId();
202 } // end of wxMenuItem::GetRealId
203
204 //
205 // Get item state
206 // --------------
207 bool wxMenuItem::IsChecked() const
208 {
209 USHORT uFlag = SHORT1FROMMR(::WinSendMsg( GetHMenuOf(m_parentMenu)
210 ,MM_QUERYITEMATTR
211 ,MPFROM2SHORT(GetId(), TRUE)
212 ,MPFROMSHORT(MIA_CHECKED)
213 ));
214
215 return (uFlag & MIA_CHECKED);
216 } // end of wxMenuItem::IsChecked
217
218 wxString wxMenuItemBase::GetLabelFromText(
219 const wxString& rText
220 )
221 {
222 wxString label;
223 for ( const wxChar *pc = rText.c_str(); *pc; pc++ )
224 {
225 if ( *pc == wxT('~') || *pc == wxT('&') )
226 {
227 // '~' is the escape character for GTK+ and '&' is the one for
228 // wxWindows - skip both of them
229 continue;
230 }
231
232 label += *pc;
233 }
234 return label;
235 }
236
237 // radio group stuff
238 // -----------------
239
240 void wxMenuItem::SetAsRadioGroupStart()
241 {
242 m_bIsRadioGroupStart = TRUE;
243 } // end of wxMenuItem::SetAsRadioGroupStart
244
245 void wxMenuItem::SetRadioGroupStart(
246 int nStart
247 )
248 {
249 wxASSERT_MSG( !m_bIsRadioGroupStart,
250 _T("should only be called for the next radio items") );
251
252 m_vRadioGroup.m_nStart = nStart;
253 } // end of wxMenuItem::SetRadioGroupStart
254
255 void wxMenuItem::SetRadioGroupEnd(
256 int nEnd
257 )
258 {
259 wxASSERT_MSG( m_bIsRadioGroupStart,
260 _T("should only be called for the first radio item") );
261
262 m_vRadioGroup.m_nEnd = nEnd;
263 } // end of wxMenuItem::SetRadioGroupEnd
264
265 // change item state
266 // -----------------
267
268 void wxMenuItem::Enable(
269 bool bEnable
270 )
271 {
272 bool bOk;
273
274 if (m_isEnabled == bEnable)
275 return;
276 if (bEnable)
277 bOk = (bool)::WinSendMsg( GetHMenuOf(m_parentMenu)
278 ,MM_SETITEMATTR
279 ,MPFROM2SHORT(GetRealId(), TRUE)
280 ,MPFROM2SHORT(MIA_DISABLED, FALSE)
281 );
282 else
283 bOk = (bool)::WinSendMsg( GetHMenuOf(m_parentMenu)
284 ,MM_SETITEMATTR
285 ,MPFROM2SHORT(GetRealId(), TRUE)
286 ,MPFROM2SHORT(MIA_DISABLED, MIA_DISABLED)
287 );
288 if (!bOk)
289 {
290 wxLogLastError("EnableMenuItem");
291 }
292 wxMenuItemBase::Enable(bEnable);
293 } // end of wxMenuItem::Enable
294
295 void wxMenuItem::Check(
296 bool bCheck
297 )
298 {
299 bool bOk;
300
301 wxCHECK_RET( IsCheckable(), wxT("only checkable items may be checked") );
302 if (m_isChecked == bCheck)
303 return;
304
305 HMENU hMenu = GetHmenuOf(m_parentMenu);
306
307 if ( GetKind() == wxITEM_RADIO )
308 {
309 //
310 // It doesn't make sense to uncheck a radio item - what would this do?
311 //
312 if (!bCheck)
313 return;
314
315 //
316 // Get the index of this item in the menu
317 //
318 const wxMenuItemList& rItems = m_parentMenu->GetMenuItems();
319 int nPos = rItems.IndexOf(this);
320 int nStart;
321 int nEnd;
322
323 wxCHECK_RET( nPos != wxNOT_FOUND,
324 _T("menuitem not found in the menu items list?") );
325
326 //
327 // Get the radio group range
328 //
329
330 if (m_bIsRadioGroupStart)
331 {
332 // we already have all information we need
333 nStart = nPos;
334 nEnd = m_vRadioGroup.m_nEnd;
335 }
336 else // Next radio group item
337 {
338 //
339 // Get the radio group end from the start item
340 //
341 nStart = m_vRadioGroup.m_nStart;
342 nEnd = rItems.Item(nStart)->GetData()->m_vRadioGroup.m_nEnd;
343 }
344
345 //
346 // Also uncheck all the other items in this radio group
347 //
348 wxMenuItemList::Node* pNode = rItems.Item(nStart);
349
350 for (int n = nStart; n <= nEnd && pNode; n++)
351 {
352 if (n != nPos)
353 {
354 pNode->GetData()->m_isChecked = FALSE;
355 }
356
357 if (n == nPos)
358 {
359 bOk = (bool)::WinSendMsg( hMenu
360 ,MM_SETITEMATTR
361 ,MPFROM2SHORT(n, TRUE)
362 ,MPFROM2SHORT(MIA_CHECKED, MIA_CHECKED)
363 );
364 }
365 else
366 {
367 bOk = (bool)::WinSendMsg( hMenu
368 ,MM_SETITEMATTR
369 ,MPFROM2SHORT(n, TRUE)
370 ,MPFROM2SHORT(MIA_CHECKED, FALSE)
371 );
372 }
373 pNode = pNode->GetNext();
374 }
375 }
376 else // check item
377 {
378 if (bCheck)
379 bOk = (bool)::WinSendMsg( hMenu
380 ,MM_SETITEMATTR
381 ,MPFROM2SHORT(GetRealId(), TRUE)
382 ,MPFROM2SHORT(MIA_CHECKED, MIA_CHECKED)
383 );
384 else
385 bOk = (bool)::WinSendMsg( hMenu
386 ,MM_SETITEMATTR
387 ,MPFROM2SHORT(GetRealId(), TRUE)
388 ,MPFROM2SHORT(MIA_CHECKED, FALSE)
389 );
390 }
391 if (!bOk)
392 {
393 wxLogLastError("CheckMenuItem");
394 }
395 wxMenuItemBase::Check(bCheck);
396 } // end of wxMenuItem::Check
397
398 void wxMenuItem::SetText(
399 const wxString& rText
400 )
401 {
402 //
403 // Don't do anything if label didn't change
404 //
405
406 wxString sText = TextToLabel(rText);
407 if (m_text == sText)
408 return;
409
410 wxMenuItemBase::SetText(sText);
411 OWNER_DRAWN_ONLY(wxOwnerDrawn::SetName(sText));
412
413 HWND hMenu = GetHmenuOf(m_parentMenu);
414
415 wxCHECK_RET(hMenu, wxT("menuitem without menu"));
416
417 #if wxUSE_ACCEL
418 m_parentMenu->UpdateAccel(this);
419 #endif // wxUSE_ACCEL
420
421 USHORT uId = GetRealId();
422 MENUITEM vItem;
423 USHORT uFlagsOld;
424
425 if (!::WinSendMsg( hMenu
426 ,MM_QUERYITEM
427 ,MPFROM2SHORT(uId, TRUE)
428 ,(MPARAM)&vItem
429 ))
430 {
431 wxLogLastError("GetMenuState");
432 }
433 else
434 {
435 uFlagsOld = vItem.afStyle;
436 if (IsSubMenu())
437 {
438 uFlagsOld |= MIS_SUBMENU;
439 }
440
441 BYTE* pData;
442
443 #if wxUSE_OWNER_DRAWN
444 if (IsOwnerDrawn())
445 {
446 uFlagsOld |= MIS_OWNERDRAW;
447 pData = (BYTE*)this;
448 }
449 else
450 #endif //owner drawn
451 {
452 uFlagsOld |= MIS_TEXT;
453 pData = (BYTE*)sText.c_str();
454 }
455
456 //
457 // Set the style
458 //
459 if (!::WinSendMsg( hMenu
460 ,MM_SETITEM
461 ,MPFROM2SHORT(uId, TRUE)
462 ,(MPARAM)&vItem
463 ))
464 {
465 wxLogLastError(wxT("ModifyMenu"));
466 }
467
468 //
469 // Set the text
470 //
471 if (::WinSendMsg( hMenu
472 ,MM_SETITEMTEXT
473 ,MPFROMSHORT(uId)
474 ,(MPARAM)pData
475 ))
476 {
477 wxLogLastError(wxT("ModifyMenu"));
478 }
479 }
480 } // end of wxMenuItem::SetText
481
482 void wxMenuItem::SetCheckable(
483 bool bCheckable
484 )
485 {
486 wxMenuItemBase::SetCheckable(bCheckable);
487 OWNER_DRAWN_ONLY(wxOwnerDrawn::SetCheckable(bCheckable));
488 } // end of wxMenuItem::SetCheckable
489
490 // ----------------------------------------------------------------------------
491 // wxMenuItemBase
492 // ----------------------------------------------------------------------------
493
494 wxMenuItem* wxMenuItemBase::New(
495 wxMenu* pParentMenu
496 , int nId
497 , const wxString& rName
498 , const wxString& rHelp
499 , wxItemKind kind
500 , wxMenu* pSubMenu
501 )
502 {
503 return new wxMenuItem( pParentMenu
504 ,nId
505 ,rName
506 ,rHelp
507 ,kind
508 ,pSubMenu
509 );
510 } // end of wxMenuItemBase::New
511