no message
[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 #if wxUSE_OWNER_DRAWN
427 SetAccelString(rText.AfterFirst(_T('\t')));
428 #endif // wxUSE_OWNER_DRAWN
429
430 HWND hMenu = GetHmenuOf(m_parentMenu);
431
432 wxCHECK_RET(hMenu, wxT("menuitem without menu"));
433
434 #if wxUSE_ACCEL
435 m_parentMenu->UpdateAccel(this);
436 #endif // wxUSE_ACCEL
437
438 USHORT uId = GetRealId();
439 MENUITEM vItem;
440 USHORT uFlagsOld;
441
442 if (!::WinSendMsg( hMenu
443 ,MM_QUERYITEM
444 ,MPFROM2SHORT(uId, TRUE)
445 ,(MPARAM)&vItem
446 ))
447 {
448 wxLogLastError("GetMenuState");
449 }
450 else
451 {
452 uFlagsOld = vItem.afStyle;
453 if (IsSubMenu())
454 {
455 uFlagsOld |= MIS_SUBMENU;
456 }
457
458 BYTE* pData;
459
460 #if wxUSE_OWNER_DRAWN
461 if (IsOwnerDrawn())
462 {
463 uFlagsOld |= MIS_OWNERDRAW;
464 pData = (BYTE*)this;
465 }
466 else
467 #endif //owner drawn
468 {
469 uFlagsOld |= MIS_TEXT;
470 pData = (BYTE*)sText.c_str();
471 }
472
473 //
474 // Set the style
475 //
476 if (!::WinSendMsg( hMenu
477 ,MM_SETITEM
478 ,MPFROM2SHORT(uId, TRUE)
479 ,(MPARAM)&vItem
480 ))
481 {
482 wxLogLastError(wxT("ModifyMenu"));
483 }
484
485 //
486 // Set the text
487 //
488 if (::WinSendMsg( hMenu
489 ,MM_SETITEMTEXT
490 ,MPFROMSHORT(uId)
491 ,(MPARAM)pData
492 ))
493 {
494 wxLogLastError(wxT("ModifyMenu"));
495 }
496 }
497 } // end of wxMenuItem::SetText
498
499 void wxMenuItem::SetCheckable(
500 bool bCheckable
501 )
502 {
503 wxMenuItemBase::SetCheckable(bCheckable);
504 OWNER_DRAWN_ONLY(wxOwnerDrawn::SetCheckable(bCheckable));
505 } // end of wxMenuItem::SetCheckable
506
507 // ----------------------------------------------------------------------------
508 // wxMenuItemBase
509 // ----------------------------------------------------------------------------
510
511 wxMenuItem* wxMenuItemBase::New(
512 wxMenu* pParentMenu
513 , int nId
514 , const wxString& rName
515 , const wxString& rHelp
516 , wxItemKind kind
517 , wxMenu* pSubMenu
518 )
519 {
520 return new wxMenuItem( pParentMenu
521 ,nId
522 ,rName
523 ,rHelp
524 ,kind
525 ,pSubMenu
526 );
527 } // end of wxMenuItemBase::New
528