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