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