]> git.saurik.com Git - wxWidgets.git/blame_incremental - src/os2/menuitem.cpp
Fix for crash when opening empty node
[wxWidgets.git] / src / os2 / menuitem.cpp
... / ...
CommitLineData
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
61IMPLEMENT_DYNAMIC_CLASS(wxMenuItem, wxObject)
62
63// ----------------------------------------------------------------------------
64// wxMenuItem
65// ----------------------------------------------------------------------------
66
67// ctor & dtor
68// -----------
69
70wxMenuItem::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
98wxMenuItem::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
126void 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
153wxMenuItem::~wxMenuItem()
154{
155} // end of wxMenuItem::~wxMenuItem
156
157//
158// Misc
159// ----
160
161//
162// Return the id for calling Win32 API functions
163//
164int 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// --------------
172bool 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
183wxString wxMenuItemBase::GetLabelFromText(
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::GetLabelFromText
203
204//
205// Radio group stuff
206// -----------------
207//
208void wxMenuItem::SetAsRadioGroupStart()
209{
210 m_bIsRadioGroupStart = true;
211} // end of wxMenuItem::SetAsRadioGroupStart
212
213void 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
224void 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
237void 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
264void 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
366void wxMenuItem::SetText( 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::SetText(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
459void 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
471wxMenuItem* 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