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