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