1 ///////////////////////////////////////////////////////////////////////////////
2 // Name: src/os2/menuitem.cpp
3 // Purpose: wxMenuItem implementation
4 // Author: David Webster
8 // Copyright: (c) David Webster
9 // Licence: wxWindows licence
10 ///////////////////////////////////////////////////////////////////////////////
12 // ============================================================================
13 // headers & declarations
14 // ============================================================================
16 // For compilers that support precompilation, includes "wx.h".
17 #include "wx/wxprec.h"
19 #include "wx/menuitem.h"
20 #include "wx/stockitem.h"
24 #include "wx/bitmap.h"
25 #include "wx/settings.h"
26 #include "wx/window.h"
29 #include "wx/string.h"
37 #include "wx/os2/private.h"
39 // ---------------------------------------------------------------------------
41 // ---------------------------------------------------------------------------
44 #define GetHMenuOf(menu) ((HMENU)menu->GetHMenu())
46 // conditional compilation
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
53 // ============================================================================
55 // ============================================================================
57 // ----------------------------------------------------------------------------
58 // dynamic classes implementation
59 // ----------------------------------------------------------------------------
61 // ----------------------------------------------------------------------------
63 // ----------------------------------------------------------------------------
68 wxMenuItem::wxMenuItem(
71 , const wxString
& rsText
72 , const wxString
& rsHelp
76 : wxMenuItemBase( pParentMenu
78 ,wxPMTextToLabel(rsText
)
84 wxASSERT_MSG(pParentMenu
!= NULL
, wxT("a menu item should have a parent"));
85 memset(&m_vMenuData
, '\0', sizeof(m_vMenuData
));
86 m_vMenuData
.id
= (USHORT
)nId
;
89 } // end of wxMenuItem::wxMenuItem
91 wxMenuItem::wxMenuItem(
94 , const wxString
& rsText
95 , const wxString
& rsHelp
99 : wxMenuItemBase( pParentMenu
101 ,wxPMTextToLabel(rsText
)
103 ,bIsCheckable
? wxITEM_CHECK
: wxITEM_NORMAL
107 wxASSERT_MSG(pParentMenu
!= NULL
, wxT("a menu item should have a parent"));
108 memset(&m_vMenuData
, '\0', sizeof(m_vMenuData
));
109 m_vMenuData
.id
= (USHORT
)nId
;
112 } // end of wxMenuItem::wxMenuItem
114 void wxMenuItem::Init()
116 m_vRadioGroup
.m_nStart
= -1;
117 m_bIsRadioGroupStart
= FALSE
;
119 #if wxUSE_OWNER_DRAWN
121 // Set default menu colors
123 SetTextColour(wxNullColour
);
124 SetBackgroundColour(wxNullColour
);
127 // We don't want normal items be owner-drawn
129 SetOwnerDrawn(false);
130 #endif // wxUSE_OWNER_DRAWN
131 } // end of wxMenuItem::Init
133 wxMenuItem::~wxMenuItem()
135 } // end of wxMenuItem::~wxMenuItem
142 // Return the id for calling Win32 API functions
144 int wxMenuItem::GetRealId() const
146 return m_subMenu
? (int)m_subMenu
->GetHMenu() : GetId();
147 } // end of wxMenuItem::GetRealId
152 bool wxMenuItem::IsChecked() const
154 USHORT uFlag
= SHORT1FROMMR(::WinSendMsg( GetHMenuOf(m_parentMenu
)
156 ,MPFROM2SHORT(GetId(), TRUE
)
157 ,MPFROMSHORT(MIA_CHECKED
)
160 return (uFlag
& MIA_CHECKED
) == MIA_CHECKED
;
161 } // end of wxMenuItem::IsChecked
163 wxString
wxMenuItemBase::GetLabelText(
164 const wxString
& rsText
169 for (const wxChar
* zPc
= rsText
.c_str(); *zPc
; zPc
++)
171 if (*zPc
== wxT('~') || *zPc
== wxT('&'))
174 // '~' is the escape character for OS/2PM and '&' is the one for
175 // wxWidgets - skip both of them
182 } // end of wxMenuItemBase::GetLabelText
188 void wxMenuItem::SetAsRadioGroupStart()
190 m_bIsRadioGroupStart
= true;
191 } // end of wxMenuItem::SetAsRadioGroupStart
193 void wxMenuItem::SetRadioGroupStart(
197 wxASSERT_MSG( !m_bIsRadioGroupStart
198 ,wxT("should only be called for the next radio items")
201 m_vRadioGroup
.m_nStart
= nStart
;
202 } // wxMenuItem::SetRadioGroupStart
204 void wxMenuItem::SetRadioGroupEnd(
208 wxASSERT_MSG( m_bIsRadioGroupStart
209 ,wxT("should only be called for the first radio item")
211 m_vRadioGroup
.m_nEnd
= nEnd
;
212 } // end of wxMenuItem::SetRadioGroupEnd
217 void wxMenuItem::Enable(
223 if (m_isEnabled
== bEnable
)
226 bOk
= (bool)::WinSendMsg( GetHMenuOf(m_parentMenu
)
228 ,MPFROM2SHORT(GetRealId(), TRUE
)
229 ,MPFROM2SHORT(MIA_DISABLED
, FALSE
)
232 bOk
= (bool)::WinSendMsg( GetHMenuOf(m_parentMenu
)
234 ,MPFROM2SHORT(GetRealId(), TRUE
)
235 ,MPFROM2SHORT(MIA_DISABLED
, MIA_DISABLED
)
239 wxLogLastError(wxT("EnableMenuItem"));
241 wxMenuItemBase::Enable(bEnable
);
242 } // end of wxMenuItem::Enable
244 void wxMenuItem::Check(
250 wxCHECK_RET( IsCheckable(), wxT("only checkable items may be checked") );
251 if (m_isChecked
== bCheck
)
254 HMENU hMenu
= GetHmenuOf(m_parentMenu
);
256 if (GetKind() == wxITEM_RADIO
)
259 // It doesn't make sense to uncheck a radio item - what would this do?
265 // Get the index of this item in the menu
267 const wxMenuItemList
& rItems
= m_parentMenu
->GetMenuItems();
268 int nPos
= rItems
.IndexOf(this);
270 wxCHECK_RET( nPos
!= wxNOT_FOUND
271 ,wxT("menuitem not found in the menu items list?")
275 // Get the radio group range
280 if (m_bIsRadioGroupStart
)
283 // We already have all information we need
286 nEnd
= m_vRadioGroup
.m_nEnd
;
288 else // next radio group item
291 // Get the radio group end from the start item
293 nStart
= m_vRadioGroup
.m_nStart
;
294 nEnd
= rItems
.Item(nStart
)->GetData()->m_vRadioGroup
.m_nEnd
;
298 // Also uncheck all the other items in this radio group
300 wxMenuItemList::compatibility_iterator node
= rItems
.Item(nStart
);
302 for (int n
= nStart
; n
<= nEnd
&& node
; n
++)
308 ,MPFROM2SHORT(n
, TRUE
)
309 ,MPFROM2SHORT(MIA_CHECKED
, MIA_CHECKED
)
314 node
->GetData()->m_isChecked
= FALSE
;
317 ,MPFROM2SHORT(n
, TRUE
)
318 ,MPFROM2SHORT(MIA_CHECKED
, FALSE
)
321 node
= node
->GetNext();
327 bOk
= (bool)::WinSendMsg( hMenu
329 ,MPFROM2SHORT(GetRealId(), TRUE
)
330 ,MPFROM2SHORT(MIA_CHECKED
, MIA_CHECKED
)
333 bOk
= (bool)::WinSendMsg( hMenu
335 ,MPFROM2SHORT(GetRealId(), TRUE
)
336 ,MPFROM2SHORT(MIA_CHECKED
, FALSE
)
341 wxLogLastError(wxT("CheckMenuItem"));
343 wxMenuItemBase::Check(bCheck
);
344 } // end of wxMenuItem::Check
346 void wxMenuItem::SetItemLabel( const wxString
& rText
)
349 // Don't do anything if label didn't change
352 wxString sText
= wxPMTextToLabel(rText
);
356 // wxMenuItemBase will do stock ID checks
357 wxMenuItemBase::SetItemLabel(sText
);
359 HWND hMenu
= GetHmenuOf(m_parentMenu
);
361 wxCHECK_RET(hMenu
, wxT("menuitem without menu"));
364 m_parentMenu
->UpdateAccel(this);
365 #endif // wxUSE_ACCEL
367 USHORT uId
= (USHORT
)GetRealId();
371 if (!::WinSendMsg( hMenu
373 ,MPFROM2SHORT(uId
, TRUE
)
377 wxLogLastError(wxT("GetMenuState"));
381 uFlagsOld
= vItem
.afStyle
;
384 uFlagsOld
|= MIS_SUBMENU
;
389 #if wxUSE_OWNER_DRAWN
392 uFlagsOld
|= MIS_OWNERDRAW
;
398 uFlagsOld
|= MIS_TEXT
;
399 pData
= (char*) m_text
.wx_str();
405 if (!::WinSendMsg( hMenu
407 ,MPFROM2SHORT(uId
, TRUE
)
411 wxLogLastError(wxT("ModifyMenu"));
417 if (::WinSendMsg( hMenu
423 wxLogLastError(wxT("ModifyMenu"));
426 } // end of wxMenuItem::SetText
428 #if wxUSE_OWNER_DRAWN
430 wxString
wxMenuItem::GetName() const
432 return GetItemLabelText();
435 bool wxMenuItem::OnMeasureItem( size_t* pWidth
, size_t* pHeight
)
439 wxString sStr
= GetName();
442 // If we have a valid accel string, then pad out
443 // the menu string so that the menu and accel string are not
444 // placed on top of each other.
445 wxString accel
= GetItemLabel().AfterFirst(wxT('\t'));
448 sStr
.Pad(sStr
.length()%8
);
451 vDC
.SetFont(GetFont());
452 vDC
.GetTextExtent( sStr
459 // Measure the accelerator string, and add its width to
460 // the total item width, plus 16 (Accelerators are right justified,
461 // with the right edge of the text rectangle 16 pixels left of
462 // the right edge of the menu)
467 vDC
.GetTextExtent( m_strAccel
471 *pWidth
+= nAccelWidth
;
475 // Add space at the end of the menu for the submenu expansion arrow.
476 // This will also allow offsetting the accel string from the right edge
478 *pWidth
= (size_t)(*pWidth
+ GetDefaultMarginWidth() * 1.5);
481 // JACS: items still look too tightly packed, so adding 5 pixels.
486 // Ray Gilbert's changes - Corrects the problem of a BMP
487 // being placed next to text in a menu item, and the BMP does
488 // not match the size expected by the system. This will
489 // resize the space so the BMP will fit. Without this, BMPs
490 // must be no larger or smaller than 16x16.
492 if (m_bmpChecked
.Ok())
495 // Is BMP height larger then text height?
497 size_t nAdjustedHeight
= m_bmpChecked
.GetHeight() +
498 wxSystemSettings::GetMetric(wxSYS_EDGE_Y
);
499 if (*pHeight
< nAdjustedHeight
)
500 *pHeight
= nAdjustedHeight
;
503 // Does BMP encroach on default check menu position?
505 size_t nAdjustedWidth
= m_bmpChecked
.GetWidth() +
506 (wxSystemSettings::GetMetric(wxSYS_EDGE_X
) * 2);
509 // Do we need to widen margin to fit BMP?
511 if ((size_t)GetMarginWidth() < nAdjustedWidth
)
512 SetMarginWidth(nAdjustedWidth
);
515 // Add the size of the bitmap to our total size...
517 *pWidth
+= GetMarginWidth();
521 // Add the size of the bitmap to our total size - even if we don't have
522 // a bitmap we leave room for one...
524 *pWidth
+= GetMarginWidth();
527 // Make sure that this item is at least as
528 // tall as the user's system settings specify
530 const size_t heightStd
= 6; // FIXME: get value from the system
531 if ( *pHeight
< heightStd
)
532 *pHeight
= heightStd
;
533 m_nHeight
= *pHeight
; // remember height for use in OnDrawItem
535 } // end of wxOwnerDrawn::OnMeasureItem
537 bool wxMenuItem::OnDrawItem( wxDC
& rDC
,
544 // Select the font and draw the text
545 // ---------------------------------
549 wxPMDCImpl
*impl
= (wxPMDCImpl
*) rDC
.GetImpl();
550 HPS hPS
= impl
->GetHPS();
555 RECTL vRect
= {rRect
.x
+ 4, rRect
.y
+ 1, rRect
.x
+ (rRect
.width
- 2), rRect
.y
+ rRect
.height
};
557 memset(&vCbnd
, 0, sizeof(CHARBUNDLE
));
560 GetColourToUse(eStatus
, vColText
, vColBack
);
563 rDC
.SetTextBackground(vColBack
);
564 rDC
.SetTextForeground(vColText
);
565 rDC
.SetBackgroundMode(wxTRANSPARENT
);
567 vCbnd
.lColor
= vColText
.GetPixel();
568 vCbnd
.lBackColor
= vColBack
.GetPixel();
571 ,CBB_BACK_COLOR
| CBB_COLOR
580 // Paint the background
582 ::WinFillRect(hPS
, &vRect
, vColBack
.GetPixel());
585 // Determine where to draw and leave space for a check-mark.
587 int nX
= rRect
.x
+ GetMarginWidth();
590 // Unfortunately, unlike Win32, PM has no owner drawn specific text
591 // drawing methods like ::DrawState that can cleanly handle accel
592 // mnemonics and deal, automatically, with various states, so we have
593 // to handle them ourselves. Notice Win32 can't handle \t in ownerdrawn
594 // strings either. We cannot handle mnemonics either. We display
595 // them, though, in the hope we can figure them out some day.
599 // Display main text and accel text separately to align better
601 wxString sTgt
= wxT("\t");
602 wxString sFullString
= GetItemLabel(); // need to save the original text
608 bool bFoundMnemonic
= false;
609 bool bFoundAccel
= false;
612 // Deal with the tab, extracting the Accel text
614 nIndex
= sFullString
.Find(sTgt
);
618 sAccel
= sFullString
.Mid(nIndex
+ 1);
619 sFullString
.Remove(nIndex
);
623 // Deal with the mnemonic character
626 nIndex
= sFullString
.Find(sTgt
);
629 wxString sTmp
= sFullString
;
631 bFoundMnemonic
= true;
633 rDC
.GetTextExtent( sTmp
637 sTmp
= sFullString
[(size_t)(nIndex
+ 1)];
638 rDC
.GetTextExtent( sTmp
639 ,(wxCoord
*)&nCharWidth
642 sFullString
.Replace(sTgt
.c_str(), wxEmptyString
, true);
646 // Draw the main item text sans the accel text
648 POINTL vPntStart
= {nX
, rRect
.y
+ 4};
649 ::GpiCharStringAt( impl
->GetHPS()
651 ,sFullString
.length()
652 ,sFullString
.char_str()
657 // Underline the mnemonic -- still won't work, but at least it "looks" right
660 POINTL vPntEnd
= {nX
+ nWidth
+ nCharWidth
- 3, rRect
.y
+ 2}; //CharWidth is bit wide
662 vPntStart
.x
= nX
+ nWidth
- 1;
663 vPntStart
.y
= rRect
.y
+ 2; // Make it look pretty!
664 vPen
= wxPen(vColText
, 1, wxSOLID
); // Assuming we are always black
666 ::GpiMove(hPS
, &vPntStart
);
667 ::GpiLine(hPS
, &vPntEnd
);
671 // Now draw the accel text
678 rDC
.GetTextExtent( sAccel
683 // Back off the starting position from the right edge
685 vPntStart
.x
= rRect
.width
- (nWidth
+ 7);
686 vPntStart
.y
= rRect
.y
+ 4;
687 ::GpiCharStringAt( impl
->GetHPS()
698 if (IsCheckable() && !m_bmpChecked
.Ok())
700 if (eStatus
& wxODChecked
)
703 HBITMAP hBmpCheck
= ::WinGetSysBitmap(HWND_DESKTOP
, SBMP_MENUCHECK
);
705 vRect
.xLeft
= rRect
.x
;
706 vRect
.xRight
= rRect
.x
+ GetMarginWidth();
707 vRect
.yBottom
= rRect
.y
;
708 vRect
.yTop
= rRect
.y
+ m_nHeight
- 3;
710 ::WinDrawBitmap( hPS
// PS for this menuitem
711 ,hBmpCheck
// system checkmark
712 ,NULL
// draw the whole bitmap
713 ,(PPOINTL
)&vRect
// destination -- bottom left corner of the menuitem area
716 ,DBM_NORMAL
// draw normal size
723 // For uncheckable item we use only the 'checked' bitmap
725 wxBitmap
vBmp(GetBitmap(IsCheckable() ? ((eStatus
& wxODChecked
) != 0) : TRUE
));
730 wxMemoryDC
vDCMem(&rDC
);
731 wxMemoryDC
* pOldDC
= (wxMemoryDC
*)vBmp
.GetSelectedInto();
735 vBmp
.SetSelectedInto(NULL
);
737 vDCMem
.SelectObject(vBmp
);
742 int nBmpWidth
= vBmp
.GetWidth();
743 int nBmpHeight
= vBmp
.GetHeight();
746 // There should be enough space!
748 wxASSERT((nBmpWidth
<= rRect
.width
) && (nBmpHeight
<= rRect
.height
));
750 int nHeightDiff
= m_nHeight
- nBmpHeight
;
752 rDC
.Blit( rRect
.x
+ (GetMarginWidth() - nBmpWidth
) / 2
753 ,rRect
.y
+ nHeightDiff
/ 2
763 if (eStatus
& wxODSelected
)
765 POINTL vPnt1
= {rRect
.x
+ 1, rRect
.y
+ 3}; // Leave a little background border
766 POINTL vPnt2
= {rRect
.x
+ GetMarginWidth(), rRect
.y
+ m_nHeight
- 3};
770 vLine
.lColor
= vColBack
.GetPixel();
777 ::GpiMove(hPS
, &vPnt1
);
785 vBmp
.SetSelectedInto(NULL
);
789 } // end of wxOwnerDrawn::OnDrawItem
791 #endif // wxUSE_OWNER_DRAWN
793 // ----------------------------------------------------------------------------
795 // ----------------------------------------------------------------------------
797 wxMenuItem
* wxMenuItemBase::New(
800 , const wxString
& rName
801 , const wxString
& rHelp
806 return new wxMenuItem( pParentMenu
813 } // end of wxMenuItemBase::New