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 IMPLEMENT_DYNAMIC_CLASS(wxMenuItem
, wxObject
)
63 // ----------------------------------------------------------------------------
65 // ----------------------------------------------------------------------------
70 wxMenuItem::wxMenuItem(
73 , const wxString
& rsText
74 , const wxString
& rsHelp
78 : wxMenuItemBase( pParentMenu
80 ,wxPMTextToLabel(rsText
)
86 wxASSERT_MSG(pParentMenu
!= NULL
, wxT("a menu item should have a parent"));
87 memset(&m_vMenuData
, '\0', sizeof(m_vMenuData
));
88 m_vMenuData
.id
= (USHORT
)nId
;
91 } // end of wxMenuItem::wxMenuItem
93 wxMenuItem::wxMenuItem(
96 , const wxString
& rsText
97 , const wxString
& rsHelp
101 : wxMenuItemBase( pParentMenu
103 ,wxPMTextToLabel(rsText
)
105 ,bIsCheckable
? wxITEM_CHECK
: wxITEM_NORMAL
109 wxASSERT_MSG(pParentMenu
!= NULL
, wxT("a menu item should have a parent"));
110 memset(&m_vMenuData
, '\0', sizeof(m_vMenuData
));
111 m_vMenuData
.id
= (USHORT
)nId
;
114 } // end of wxMenuItem::wxMenuItem
116 void wxMenuItem::Init()
118 m_vRadioGroup
.m_nStart
= -1;
119 m_bIsRadioGroupStart
= FALSE
;
121 #if wxUSE_OWNER_DRAWN
123 // Set default menu colors
125 SetTextColour(wxNullColour
);
126 SetBackgroundColour(wxNullColour
);
129 // We don't want normal items be owner-drawn
131 SetOwnerDrawn(false);
132 #endif // wxUSE_OWNER_DRAWN
133 } // end of wxMenuItem::Init
135 wxMenuItem::~wxMenuItem()
137 } // end of wxMenuItem::~wxMenuItem
144 // Return the id for calling Win32 API functions
146 int wxMenuItem::GetRealId() const
148 return m_subMenu
? (int)m_subMenu
->GetHMenu() : GetId();
149 } // end of wxMenuItem::GetRealId
154 bool wxMenuItem::IsChecked() const
156 USHORT uFlag
= SHORT1FROMMR(::WinSendMsg( GetHMenuOf(m_parentMenu
)
158 ,MPFROM2SHORT(GetId(), TRUE
)
159 ,MPFROMSHORT(MIA_CHECKED
)
162 return (uFlag
& MIA_CHECKED
) == MIA_CHECKED
;
163 } // end of wxMenuItem::IsChecked
165 wxString
wxMenuItemBase::GetLabelText(
166 const wxString
& rsText
171 for (const wxChar
* zPc
= rsText
.c_str(); *zPc
; zPc
++)
173 if (*zPc
== wxT('~') || *zPc
== wxT('&'))
176 // '~' is the escape character for OS/2PM and '&' is the one for
177 // wxWidgets - skip both of them
184 } // end of wxMenuItemBase::GetLabelText
190 void wxMenuItem::SetAsRadioGroupStart()
192 m_bIsRadioGroupStart
= true;
193 } // end of wxMenuItem::SetAsRadioGroupStart
195 void wxMenuItem::SetRadioGroupStart(
199 wxASSERT_MSG( !m_bIsRadioGroupStart
200 ,wxT("should only be called for the next radio items")
203 m_vRadioGroup
.m_nStart
= nStart
;
204 } // wxMenuItem::SetRadioGroupStart
206 void wxMenuItem::SetRadioGroupEnd(
210 wxASSERT_MSG( m_bIsRadioGroupStart
211 ,wxT("should only be called for the first radio item")
213 m_vRadioGroup
.m_nEnd
= nEnd
;
214 } // end of wxMenuItem::SetRadioGroupEnd
219 void wxMenuItem::Enable(
225 if (m_isEnabled
== bEnable
)
228 bOk
= (bool)::WinSendMsg( GetHMenuOf(m_parentMenu
)
230 ,MPFROM2SHORT(GetRealId(), TRUE
)
231 ,MPFROM2SHORT(MIA_DISABLED
, FALSE
)
234 bOk
= (bool)::WinSendMsg( GetHMenuOf(m_parentMenu
)
236 ,MPFROM2SHORT(GetRealId(), TRUE
)
237 ,MPFROM2SHORT(MIA_DISABLED
, MIA_DISABLED
)
241 wxLogLastError(wxT("EnableMenuItem"));
243 wxMenuItemBase::Enable(bEnable
);
244 } // end of wxMenuItem::Enable
246 void wxMenuItem::Check(
252 wxCHECK_RET( IsCheckable(), wxT("only checkable items may be checked") );
253 if (m_isChecked
== bCheck
)
256 HMENU hMenu
= GetHmenuOf(m_parentMenu
);
258 if (GetKind() == wxITEM_RADIO
)
261 // It doesn't make sense to uncheck a radio item - what would this do?
267 // Get the index of this item in the menu
269 const wxMenuItemList
& rItems
= m_parentMenu
->GetMenuItems();
270 int nPos
= rItems
.IndexOf(this);
272 wxCHECK_RET( nPos
!= wxNOT_FOUND
273 ,wxT("menuitem not found in the menu items list?")
277 // Get the radio group range
282 if (m_bIsRadioGroupStart
)
285 // We already have all information we need
288 nEnd
= m_vRadioGroup
.m_nEnd
;
290 else // next radio group item
293 // Get the radio group end from the start item
295 nStart
= m_vRadioGroup
.m_nStart
;
296 nEnd
= rItems
.Item(nStart
)->GetData()->m_vRadioGroup
.m_nEnd
;
300 // Also uncheck all the other items in this radio group
302 wxMenuItemList::compatibility_iterator node
= rItems
.Item(nStart
);
304 for (int n
= nStart
; n
<= nEnd
&& node
; n
++)
310 ,MPFROM2SHORT(n
, TRUE
)
311 ,MPFROM2SHORT(MIA_CHECKED
, MIA_CHECKED
)
316 node
->GetData()->m_isChecked
= FALSE
;
319 ,MPFROM2SHORT(n
, TRUE
)
320 ,MPFROM2SHORT(MIA_CHECKED
, FALSE
)
323 node
= node
->GetNext();
329 bOk
= (bool)::WinSendMsg( hMenu
331 ,MPFROM2SHORT(GetRealId(), TRUE
)
332 ,MPFROM2SHORT(MIA_CHECKED
, MIA_CHECKED
)
335 bOk
= (bool)::WinSendMsg( hMenu
337 ,MPFROM2SHORT(GetRealId(), TRUE
)
338 ,MPFROM2SHORT(MIA_CHECKED
, FALSE
)
343 wxLogLastError(wxT("CheckMenuItem"));
345 wxMenuItemBase::Check(bCheck
);
346 } // end of wxMenuItem::Check
348 void wxMenuItem::SetItemLabel( const wxString
& rText
)
351 // Don't do anything if label didn't change
354 wxString sText
= wxPMTextToLabel(rText
);
358 // wxMenuItemBase will do stock ID checks
359 wxMenuItemBase::SetItemLabel(sText
);
361 HWND hMenu
= GetHmenuOf(m_parentMenu
);
363 wxCHECK_RET(hMenu
, wxT("menuitem without menu"));
366 m_parentMenu
->UpdateAccel(this);
367 #endif // wxUSE_ACCEL
369 USHORT uId
= (USHORT
)GetRealId();
373 if (!::WinSendMsg( hMenu
375 ,MPFROM2SHORT(uId
, TRUE
)
379 wxLogLastError(wxT("GetMenuState"));
383 uFlagsOld
= vItem
.afStyle
;
386 uFlagsOld
|= MIS_SUBMENU
;
391 #if wxUSE_OWNER_DRAWN
394 uFlagsOld
|= MIS_OWNERDRAW
;
400 uFlagsOld
|= MIS_TEXT
;
401 pData
= (char*) m_text
.wx_str();
407 if (!::WinSendMsg( hMenu
409 ,MPFROM2SHORT(uId
, TRUE
)
413 wxLogLastError(wxT("ModifyMenu"));
419 if (::WinSendMsg( hMenu
425 wxLogLastError(wxT("ModifyMenu"));
428 } // end of wxMenuItem::SetText
430 #if wxUSE_OWNER_DRAWN
432 wxString
wxMenuItem::GetName() const
434 return GetItemLabelText();
437 bool wxMenuItem::OnMeasureItem( size_t* pWidth
, size_t* pHeight
)
441 wxString sStr
= GetName();
444 // If we have a valid accel string, then pad out
445 // the menu string so that the menu and accel string are not
446 // placed on top of each other.
447 wxString accel
= GetItemLabel().AfterFirst(wxT('\t'));
450 sStr
.Pad(sStr
.length()%8
);
453 vDC
.SetFont(GetFont());
454 vDC
.GetTextExtent( sStr
461 // Measure the accelerator string, and add its width to
462 // the total item width, plus 16 (Accelerators are right justified,
463 // with the right edge of the text rectangle 16 pixels left of
464 // the right edge of the menu)
469 vDC
.GetTextExtent( m_strAccel
473 *pWidth
+= nAccelWidth
;
477 // Add space at the end of the menu for the submenu expansion arrow.
478 // This will also allow offsetting the accel string from the right edge
480 *pWidth
= (size_t)(*pWidth
+ GetDefaultMarginWidth() * 1.5);
483 // JACS: items still look too tightly packed, so adding 5 pixels.
488 // Ray Gilbert's changes - Corrects the problem of a BMP
489 // being placed next to text in a menu item, and the BMP does
490 // not match the size expected by the system. This will
491 // resize the space so the BMP will fit. Without this, BMPs
492 // must be no larger or smaller than 16x16.
494 if (m_bmpChecked
.Ok())
497 // Is BMP height larger then text height?
499 size_t nAdjustedHeight
= m_bmpChecked
.GetHeight() +
500 wxSystemSettings::GetMetric(wxSYS_EDGE_Y
);
501 if (*pHeight
< nAdjustedHeight
)
502 *pHeight
= nAdjustedHeight
;
505 // Does BMP encroach on default check menu position?
507 size_t nAdjustedWidth
= m_bmpChecked
.GetWidth() +
508 (wxSystemSettings::GetMetric(wxSYS_EDGE_X
) * 2);
511 // Do we need to widen margin to fit BMP?
513 if ((size_t)GetMarginWidth() < nAdjustedWidth
)
514 SetMarginWidth(nAdjustedWidth
);
517 // Add the size of the bitmap to our total size...
519 *pWidth
+= GetMarginWidth();
523 // Add the size of the bitmap to our total size - even if we don't have
524 // a bitmap we leave room for one...
526 *pWidth
+= GetMarginWidth();
529 // Make sure that this item is at least as
530 // tall as the user's system settings specify
532 const size_t heightStd
= 6; // FIXME: get value from the system
533 if ( *pHeight
< heightStd
)
534 *pHeight
= heightStd
;
535 m_nHeight
= *pHeight
; // remember height for use in OnDrawItem
537 } // end of wxOwnerDrawn::OnMeasureItem
539 bool wxMenuItem::OnDrawItem( wxDC
& rDC
,
546 // Select the font and draw the text
547 // ---------------------------------
551 wxPMDCImpl
*impl
= (wxPMDCImpl
*) rDC
.GetImpl();
552 HPS hPS
= impl
->GetHPS();
557 RECTL vRect
= {rRect
.x
+ 4, rRect
.y
+ 1, rRect
.x
+ (rRect
.width
- 2), rRect
.y
+ rRect
.height
};
559 memset(&vCbnd
, 0, sizeof(CHARBUNDLE
));
562 GetColourToUse(eStatus
, vColText
, vColBack
);
565 rDC
.SetTextBackground(vColBack
);
566 rDC
.SetTextForeground(vColText
);
567 rDC
.SetBackgroundMode(wxTRANSPARENT
);
569 vCbnd
.lColor
= vColText
.GetPixel();
570 vCbnd
.lBackColor
= vColBack
.GetPixel();
573 ,CBB_BACK_COLOR
| CBB_COLOR
582 // Paint the background
584 ::WinFillRect(hPS
, &vRect
, vColBack
.GetPixel());
587 // Determine where to draw and leave space for a check-mark.
589 int nX
= rRect
.x
+ GetMarginWidth();
592 // Unfortunately, unlike Win32, PM has no owner drawn specific text
593 // drawing methods like ::DrawState that can cleanly handle accel
594 // mnemonics and deal, automatically, with various states, so we have
595 // to handle them ourselves. Notice Win32 can't handle \t in ownerdrawn
596 // strings either. We cannot handle mnemonics either. We display
597 // them, though, in the hope we can figure them out some day.
601 // Display main text and accel text separately to align better
603 wxString sTgt
= wxT("\t");
604 wxString sFullString
= GetItemLabel(); // need to save the original text
610 bool bFoundMnemonic
= false;
611 bool bFoundAccel
= false;
614 // Deal with the tab, extracting the Accel text
616 nIndex
= sFullString
.Find(sTgt
);
620 sAccel
= sFullString
.Mid(nIndex
+ 1);
621 sFullString
.Remove(nIndex
);
625 // Deal with the mnemonic character
628 nIndex
= sFullString
.Find(sTgt
);
631 wxString sTmp
= sFullString
;
633 bFoundMnemonic
= true;
635 rDC
.GetTextExtent( sTmp
639 sTmp
= sFullString
[(size_t)(nIndex
+ 1)];
640 rDC
.GetTextExtent( sTmp
641 ,(wxCoord
*)&nCharWidth
644 sFullString
.Replace(sTgt
.c_str(), wxEmptyString
, true);
648 // Draw the main item text sans the accel text
650 POINTL vPntStart
= {nX
, rRect
.y
+ 4};
651 ::GpiCharStringAt( impl
->GetHPS()
653 ,sFullString
.length()
654 ,sFullString
.char_str()
659 // Underline the mnemonic -- still won't work, but at least it "looks" right
662 POINTL vPntEnd
= {nX
+ nWidth
+ nCharWidth
- 3, rRect
.y
+ 2}; //CharWidth is bit wide
664 vPntStart
.x
= nX
+ nWidth
- 1;
665 vPntStart
.y
= rRect
.y
+ 2; // Make it look pretty!
666 vPen
= wxPen(vColText
, 1, wxSOLID
); // Assuming we are always black
668 ::GpiMove(hPS
, &vPntStart
);
669 ::GpiLine(hPS
, &vPntEnd
);
673 // Now draw the accel text
680 rDC
.GetTextExtent( sAccel
685 // Back off the starting position from the right edge
687 vPntStart
.x
= rRect
.width
- (nWidth
+ 7);
688 vPntStart
.y
= rRect
.y
+ 4;
689 ::GpiCharStringAt( impl
->GetHPS()
700 if (IsCheckable() && !m_bmpChecked
.Ok())
702 if (eStatus
& wxODChecked
)
705 HBITMAP hBmpCheck
= ::WinGetSysBitmap(HWND_DESKTOP
, SBMP_MENUCHECK
);
707 vRect
.xLeft
= rRect
.x
;
708 vRect
.xRight
= rRect
.x
+ GetMarginWidth();
709 vRect
.yBottom
= rRect
.y
;
710 vRect
.yTop
= rRect
.y
+ m_nHeight
- 3;
712 ::WinDrawBitmap( hPS
// PS for this menuitem
713 ,hBmpCheck
// system checkmark
714 ,NULL
// draw the whole bitmap
715 ,(PPOINTL
)&vRect
// destination -- bottom left corner of the menuitem area
718 ,DBM_NORMAL
// draw normal size
725 // For uncheckable item we use only the 'checked' bitmap
727 wxBitmap
vBmp(GetBitmap(IsCheckable() ? ((eStatus
& wxODChecked
) != 0) : TRUE
));
732 wxMemoryDC
vDCMem(&rDC
);
733 wxMemoryDC
* pOldDC
= (wxMemoryDC
*)vBmp
.GetSelectedInto();
737 vBmp
.SetSelectedInto(NULL
);
739 vDCMem
.SelectObject(vBmp
);
744 int nBmpWidth
= vBmp
.GetWidth();
745 int nBmpHeight
= vBmp
.GetHeight();
748 // There should be enough space!
750 wxASSERT((nBmpWidth
<= rRect
.width
) && (nBmpHeight
<= rRect
.height
));
752 int nHeightDiff
= m_nHeight
- nBmpHeight
;
754 rDC
.Blit( rRect
.x
+ (GetMarginWidth() - nBmpWidth
) / 2
755 ,rRect
.y
+ nHeightDiff
/ 2
765 if (eStatus
& wxODSelected
)
767 POINTL vPnt1
= {rRect
.x
+ 1, rRect
.y
+ 3}; // Leave a little background border
768 POINTL vPnt2
= {rRect
.x
+ GetMarginWidth(), rRect
.y
+ m_nHeight
- 3};
772 vLine
.lColor
= vColBack
.GetPixel();
779 ::GpiMove(hPS
, &vPnt1
);
787 vBmp
.SetSelectedInto(NULL
);
791 } // end of wxOwnerDrawn::OnDrawItem
793 #endif // wxUSE_OWNER_DRAWN
795 // ----------------------------------------------------------------------------
797 // ----------------------------------------------------------------------------
799 wxMenuItem
* wxMenuItemBase::New(
802 , const wxString
& rName
803 , const wxString
& rHelp
808 return new wxMenuItem( pParentMenu
815 } // end of wxMenuItemBase::New