Bitmap and menu 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 //
240 // Radio group stuff
241 // -----------------
242 //
243 void wxMenuItem::SetAsRadioGroupStart()
244 {
245 m_bIsRadioGroupStart = TRUE;
246 } // end of wxMenuItem::SetAsRadioGroupStart
247
248 void wxMenuItem::SetRadioGroupStart(
249 int nStart
250 )
251 {
252 wxASSERT_MSG( !m_bIsRadioGroupStart
253 ,_T("should only be called for the next radio items")
254 );
255
256 m_vRadioGroup.m_nStart = nStart;
257 } // wxMenuItem::SetRadioGroupStart
258
259 void wxMenuItem::SetRadioGroupEnd(
260 int nEnd
261 )
262 {
263 wxASSERT_MSG( m_bIsRadioGroupStart
264 ,_T("should only be called for the first radio item")
265 );
266 m_vRadioGroup.m_nEnd = nEnd;
267 } // end of wxMenuItem::SetRadioGroupEnd
268
269 // change item state
270 // -----------------
271
272 void wxMenuItem::Enable(
273 bool bEnable
274 )
275 {
276 bool bOk;
277
278 if (m_isEnabled == bEnable)
279 return;
280 if (bEnable)
281 bOk = (bool)::WinSendMsg( GetHMenuOf(m_parentMenu)
282 ,MM_SETITEMATTR
283 ,MPFROM2SHORT(GetRealId(), TRUE)
284 ,MPFROM2SHORT(MIA_DISABLED, FALSE)
285 );
286 else
287 bOk = (bool)::WinSendMsg( GetHMenuOf(m_parentMenu)
288 ,MM_SETITEMATTR
289 ,MPFROM2SHORT(GetRealId(), TRUE)
290 ,MPFROM2SHORT(MIA_DISABLED, MIA_DISABLED)
291 );
292 if (!bOk)
293 {
294 wxLogLastError("EnableMenuItem");
295 }
296 wxMenuItemBase::Enable(bEnable);
297 } // end of wxMenuItem::Enable
298
299 void wxMenuItem::Check(
300 bool bCheck
301 )
302 {
303 bool bOk;
304
305 wxCHECK_RET( IsCheckable(), wxT("only checkable items may be checked") );
306 if (m_isChecked == bCheck)
307 return;
308
309 HMENU hMenu = GetHmenuOf(m_parentMenu);
310
311 if (GetKind() == wxITEM_RADIO)
312 {
313 //
314 // It doesn't make sense to uncheck a radio item - what would this do?
315 //
316 if (!bCheck)
317 return;
318
319 //
320 // Get the index of this item in the menu
321 //
322 const wxMenuItemList& rItems = m_parentMenu->GetMenuItems();
323 int nPos = rItems.IndexOf(this);
324
325 wxCHECK_RET( nPos != wxNOT_FOUND
326 ,_T("menuitem not found in the menu items list?")
327 );
328
329 //
330 // Get the radio group range
331 //
332 int nStart;
333 int nEnd;
334
335 if (m_bIsRadioGroupStart)
336 {
337 //
338 // We already have all information we need
339 //
340 nStart = nPos;
341 nEnd = m_vRadioGroup.m_nEnd;
342 }
343 else // next radio group item
344 {
345 //
346 // Get the radio group end from the start item
347 //
348 nStart = m_vRadioGroup.m_nStart;
349 nEnd = rItems.Item(nStart)->GetData()->m_vRadioGroup.m_nEnd;
350 }
351
352 //
353 // Also uncheck all the other items in this radio group
354 //
355 wxMenuItemList::Node* pNode = rItems.Item(nStart);
356
357 for (int n = nStart; n <= nEnd && pNode; n++)
358 {
359 if (n == nPos)
360 {
361 ::WinSendMsg( hMenu
362 ,MM_SETITEMATTR
363 ,MPFROM2SHORT(n, TRUE)
364 ,MPFROM2SHORT(MIA_CHECKED, MIA_CHECKED)
365 );
366 }
367 if (n != nPos)
368 {
369 pNode->GetData()->m_isChecked = FALSE;
370 ::WinSendMsg( hMenu
371 ,MM_SETITEMATTR
372 ,MPFROM2SHORT(n, TRUE)
373 ,MPFROM2SHORT(MIA_CHECKED, FALSE)
374 );
375 }
376 pNode = pNode->GetNext();
377 }
378 }
379 else // check item
380 {
381 if (bCheck)
382 bOk = (bool)::WinSendMsg( hMenu
383 ,MM_SETITEMATTR
384 ,MPFROM2SHORT(GetRealId(), TRUE)
385 ,MPFROM2SHORT(MIA_CHECKED, MIA_CHECKED)
386 );
387 else
388 bOk = (bool)::WinSendMsg( hMenu
389 ,MM_SETITEMATTR
390 ,MPFROM2SHORT(GetRealId(), TRUE)
391 ,MPFROM2SHORT(MIA_CHECKED, FALSE)
392 );
393 }
394 if (!bOk)
395 {
396 wxLogLastError("CheckMenuItem");
397 }
398 wxMenuItemBase::Check(bCheck);
399 } // end of wxMenuItem::Check
400
401 void wxMenuItem::SetText(
402 const wxString& rText
403 )
404 {
405 //
406 // Don't do anything if label didn't change
407 //
408
409 wxString sText = TextToLabel(rText);
410 if (m_text == sText)
411 return;
412
413 wxMenuItemBase::SetText(sText);
414 OWNER_DRAWN_ONLY(wxOwnerDrawn::SetName(sText));
415
416 HWND hMenu = GetHmenuOf(m_parentMenu);
417
418 wxCHECK_RET(hMenu, wxT("menuitem without menu"));
419
420 #if wxUSE_ACCEL
421 m_parentMenu->UpdateAccel(this);
422 #endif // wxUSE_ACCEL
423
424 USHORT uId = GetRealId();
425 MENUITEM vItem;
426 USHORT uFlagsOld;
427
428 if (!::WinSendMsg( hMenu
429 ,MM_QUERYITEM
430 ,MPFROM2SHORT(uId, TRUE)
431 ,(MPARAM)&vItem
432 ))
433 {
434 wxLogLastError("GetMenuState");
435 }
436 else
437 {
438 uFlagsOld = vItem.afStyle;
439 if (IsSubMenu())
440 {
441 uFlagsOld |= MIS_SUBMENU;
442 }
443
444 BYTE* pData;
445
446 #if wxUSE_OWNER_DRAWN
447 if (IsOwnerDrawn())
448 {
449 uFlagsOld |= MIS_OWNERDRAW;
450 pData = (BYTE*)this;
451 }
452 else
453 #endif //owner drawn
454 {
455 uFlagsOld |= MIS_TEXT;
456 pData = (BYTE*)sText.c_str();
457 }
458
459 //
460 // Set the style
461 //
462 if (!::WinSendMsg( hMenu
463 ,MM_SETITEM
464 ,MPFROM2SHORT(uId, TRUE)
465 ,(MPARAM)&vItem
466 ))
467 {
468 wxLogLastError(wxT("ModifyMenu"));
469 }
470
471 //
472 // Set the text
473 //
474 if (::WinSendMsg( hMenu
475 ,MM_SETITEMTEXT
476 ,MPFROMSHORT(uId)
477 ,(MPARAM)pData
478 ))
479 {
480 wxLogLastError(wxT("ModifyMenu"));
481 }
482 }
483 } // end of wxMenuItem::SetText
484
485 void wxMenuItem::SetCheckable(
486 bool bCheckable
487 )
488 {
489 wxMenuItemBase::SetCheckable(bCheckable);
490 OWNER_DRAWN_ONLY(wxOwnerDrawn::SetCheckable(bCheckable));
491 } // end of wxMenuItem::SetCheckable
492
493 // ----------------------------------------------------------------------------
494 // wxMenuItemBase
495 // ----------------------------------------------------------------------------
496
497 wxMenuItem* wxMenuItemBase::New(
498 wxMenu* pParentMenu
499 , int nId
500 , const wxString& rName
501 , const wxString& rHelp
502 , wxItemKind kind
503 , wxMenu* pSubMenu
504 )
505 {
506 return new wxMenuItem( pParentMenu
507 ,nId
508 ,rName
509 ,rHelp
510 ,kind
511 ,pSubMenu
512 );
513 } // end of wxMenuItemBase::New
514