1 ///////////////////////////////////////////////////////////////////////////////
2 // Name: src/os2/menuitem.cpp
3 // Purpose: wxMenuItem implementation
4 // Author: David Webster
7 // Copyright: (c) David Webster
8 // Licence: wxWindows licence
9 ///////////////////////////////////////////////////////////////////////////////
11 // ============================================================================
12 // headers & declarations
13 // ============================================================================
15 // For compilers that support precompilation, includes "wx.h".
16 #include "wx/wxprec.h"
18 #include "wx/menuitem.h"
19 #include "wx/stockitem.h"
23 #include "wx/bitmap.h"
24 #include "wx/settings.h"
25 #include "wx/window.h"
28 #include "wx/string.h"
36 #include "wx/os2/private.h"
38 // ---------------------------------------------------------------------------
40 // ---------------------------------------------------------------------------
43 #define GetHMenuOf(menu) ((HMENU)menu->GetHMenu())
45 // conditional compilation
47 #define OWNER_DRAWN_ONLY( code ) if ( IsOwnerDrawn() ) code
48 #else // !wxUSE_OWNER_DRAWN
49 #define OWNER_DRAWN_ONLY( code )
50 #endif // wxUSE_OWNER_DRAWN/!wxUSE_OWNER_DRAWN
52 // ============================================================================
54 // ============================================================================
56 // ----------------------------------------------------------------------------
57 // dynamic classes implementation
58 // ----------------------------------------------------------------------------
60 // ----------------------------------------------------------------------------
62 // ----------------------------------------------------------------------------
67 wxMenuItem::wxMenuItem(
70 , const wxString
& rsText
71 , const wxString
& rsHelp
75 : wxMenuItemBase( pParentMenu
77 ,wxPMTextToLabel(rsText
)
83 wxASSERT_MSG(pParentMenu
!= NULL
, wxT("a menu item should have a parent"));
84 memset(&m_vMenuData
, '\0', sizeof(m_vMenuData
));
85 m_vMenuData
.id
= (USHORT
)nId
;
88 } // end of wxMenuItem::wxMenuItem
90 wxMenuItem::wxMenuItem(
93 , const wxString
& rsText
94 , const wxString
& rsHelp
98 : wxMenuItemBase( pParentMenu
100 ,wxPMTextToLabel(rsText
)
102 ,bIsCheckable
? wxITEM_CHECK
: wxITEM_NORMAL
106 wxASSERT_MSG(pParentMenu
!= NULL
, wxT("a menu item should have a parent"));
107 memset(&m_vMenuData
, '\0', sizeof(m_vMenuData
));
108 m_vMenuData
.id
= (USHORT
)nId
;
111 } // end of wxMenuItem::wxMenuItem
113 void wxMenuItem::Init()
115 m_vRadioGroup
.m_nStart
= -1;
116 m_bIsRadioGroupStart
= FALSE
;
118 #if wxUSE_OWNER_DRAWN
120 // Set default menu colors
122 SetTextColour(wxNullColour
);
123 SetBackgroundColour(wxNullColour
);
126 // We don't want normal items be owner-drawn
128 SetOwnerDrawn(false);
129 #endif // wxUSE_OWNER_DRAWN
130 } // end of wxMenuItem::Init
132 wxMenuItem::~wxMenuItem()
134 } // end of wxMenuItem::~wxMenuItem
141 // Return the id for calling Win32 API functions
143 int wxMenuItem::GetRealId() const
145 return m_subMenu
? (int)m_subMenu
->GetHMenu() : GetId();
146 } // end of wxMenuItem::GetRealId
151 bool wxMenuItem::IsChecked() const
153 USHORT uFlag
= SHORT1FROMMR(::WinSendMsg( GetHMenuOf(m_parentMenu
)
155 ,MPFROM2SHORT(GetId(), TRUE
)
156 ,MPFROMSHORT(MIA_CHECKED
)
159 return (uFlag
& MIA_CHECKED
) == MIA_CHECKED
;
160 } // end of wxMenuItem::IsChecked
162 wxString
wxMenuItemBase::GetLabelText(
163 const wxString
& rsText
168 for (const wxChar
* zPc
= rsText
.c_str(); *zPc
; zPc
++)
170 if (*zPc
== wxT('~') || *zPc
== wxT('&'))
173 // '~' is the escape character for OS/2PM and '&' is the one for
174 // wxWidgets - skip both of them
181 } // end of wxMenuItemBase::GetLabelText
187 void wxMenuItem::SetAsRadioGroupStart()
189 m_bIsRadioGroupStart
= true;
190 } // end of wxMenuItem::SetAsRadioGroupStart
192 void wxMenuItem::SetRadioGroupStart(
196 wxASSERT_MSG( !m_bIsRadioGroupStart
197 ,wxT("should only be called for the next radio items")
200 m_vRadioGroup
.m_nStart
= nStart
;
201 } // wxMenuItem::SetRadioGroupStart
203 void wxMenuItem::SetRadioGroupEnd(
207 wxASSERT_MSG( m_bIsRadioGroupStart
208 ,wxT("should only be called for the first radio item")
210 m_vRadioGroup
.m_nEnd
= nEnd
;
211 } // end of wxMenuItem::SetRadioGroupEnd
216 void wxMenuItem::Enable(
222 if (m_isEnabled
== bEnable
)
225 bOk
= (bool)::WinSendMsg( GetHMenuOf(m_parentMenu
)
227 ,MPFROM2SHORT(GetRealId(), TRUE
)
228 ,MPFROM2SHORT(MIA_DISABLED
, FALSE
)
231 bOk
= (bool)::WinSendMsg( GetHMenuOf(m_parentMenu
)
233 ,MPFROM2SHORT(GetRealId(), TRUE
)
234 ,MPFROM2SHORT(MIA_DISABLED
, MIA_DISABLED
)
238 wxLogLastError(wxT("EnableMenuItem"));
240 wxMenuItemBase::Enable(bEnable
);
241 } // end of wxMenuItem::Enable
243 void wxMenuItem::Check(
249 wxCHECK_RET( IsCheckable(), wxT("only checkable items may be checked") );
250 if (m_isChecked
== bCheck
)
253 HMENU hMenu
= GetHmenuOf(m_parentMenu
);
255 if (GetKind() == wxITEM_RADIO
)
258 // It doesn't make sense to uncheck a radio item - what would this do?
264 // Get the index of this item in the menu
266 const wxMenuItemList
& rItems
= m_parentMenu
->GetMenuItems();
267 int nPos
= rItems
.IndexOf(this);
269 wxCHECK_RET( nPos
!= wxNOT_FOUND
270 ,wxT("menuitem not found in the menu items list?")
274 // Get the radio group range
279 if (m_bIsRadioGroupStart
)
282 // We already have all information we need
285 nEnd
= m_vRadioGroup
.m_nEnd
;
287 else // next radio group item
290 // Get the radio group end from the start item
292 nStart
= m_vRadioGroup
.m_nStart
;
293 nEnd
= rItems
.Item(nStart
)->GetData()->m_vRadioGroup
.m_nEnd
;
297 // Also uncheck all the other items in this radio group
299 wxMenuItemList::compatibility_iterator node
= rItems
.Item(nStart
);
301 for (int n
= nStart
; n
<= nEnd
&& node
; n
++)
307 ,MPFROM2SHORT(n
, TRUE
)
308 ,MPFROM2SHORT(MIA_CHECKED
, MIA_CHECKED
)
313 node
->GetData()->m_isChecked
= FALSE
;
316 ,MPFROM2SHORT(n
, TRUE
)
317 ,MPFROM2SHORT(MIA_CHECKED
, FALSE
)
320 node
= node
->GetNext();
326 bOk
= (bool)::WinSendMsg( hMenu
328 ,MPFROM2SHORT(GetRealId(), TRUE
)
329 ,MPFROM2SHORT(MIA_CHECKED
, MIA_CHECKED
)
332 bOk
= (bool)::WinSendMsg( hMenu
334 ,MPFROM2SHORT(GetRealId(), TRUE
)
335 ,MPFROM2SHORT(MIA_CHECKED
, FALSE
)
340 wxLogLastError(wxT("CheckMenuItem"));
342 wxMenuItemBase::Check(bCheck
);
343 } // end of wxMenuItem::Check
345 void wxMenuItem::SetItemLabel( const wxString
& rText
)
348 // Don't do anything if label didn't change
351 wxString sText
= wxPMTextToLabel(rText
);
355 // wxMenuItemBase will do stock ID checks
356 wxMenuItemBase::SetItemLabel(sText
);
358 HWND hMenu
= GetHmenuOf(m_parentMenu
);
360 wxCHECK_RET(hMenu
, wxT("menuitem without menu"));
363 m_parentMenu
->UpdateAccel(this);
364 #endif // wxUSE_ACCEL
366 USHORT uId
= (USHORT
)GetRealId();
370 if (!::WinSendMsg( hMenu
372 ,MPFROM2SHORT(uId
, TRUE
)
376 wxLogLastError(wxT("GetMenuState"));
380 uFlagsOld
= vItem
.afStyle
;
383 uFlagsOld
|= MIS_SUBMENU
;
388 #if wxUSE_OWNER_DRAWN
391 uFlagsOld
|= MIS_OWNERDRAW
;
397 uFlagsOld
|= MIS_TEXT
;
398 pData
= (char*) m_text
.wx_str();
404 if (!::WinSendMsg( hMenu
406 ,MPFROM2SHORT(uId
, TRUE
)
410 wxLogLastError(wxT("ModifyMenu"));
416 if (::WinSendMsg( hMenu
422 wxLogLastError(wxT("ModifyMenu"));
425 } // end of wxMenuItem::SetText
427 #if wxUSE_OWNER_DRAWN
429 wxString
wxMenuItem::GetName() const
431 return GetItemLabelText();
434 bool wxMenuItem::OnMeasureItem( size_t* pWidth
, size_t* pHeight
)
438 wxString sStr
= GetName();
441 // If we have a valid accel string, then pad out
442 // the menu string so that the menu and accel string are not
443 // placed on top of each other.
444 wxString accel
= GetItemLabel().AfterFirst(wxT('\t'));
447 sStr
.Pad(sStr
.length()%8
);
450 vDC
.SetFont(GetFont());
451 vDC
.GetTextExtent( sStr
458 // Measure the accelerator string, and add its width to
459 // the total item width, plus 16 (Accelerators are right justified,
460 // with the right edge of the text rectangle 16 pixels left of
461 // the right edge of the menu)
466 vDC
.GetTextExtent( m_strAccel
470 *pWidth
+= nAccelWidth
;
474 // Add space at the end of the menu for the submenu expansion arrow.
475 // This will also allow offsetting the accel string from the right edge
477 *pWidth
= (size_t)(*pWidth
+ GetDefaultMarginWidth() * 1.5);
480 // JACS: items still look too tightly packed, so adding 5 pixels.
485 // Ray Gilbert's changes - Corrects the problem of a BMP
486 // being placed next to text in a menu item, and the BMP does
487 // not match the size expected by the system. This will
488 // resize the space so the BMP will fit. Without this, BMPs
489 // must be no larger or smaller than 16x16.
491 if (m_bmpChecked
.IsOk())
494 // Is BMP height larger than text height?
496 size_t nAdjustedHeight
= m_bmpChecked
.GetHeight() +
497 wxSystemSettings::GetMetric(wxSYS_EDGE_Y
);
498 if (*pHeight
< nAdjustedHeight
)
499 *pHeight
= nAdjustedHeight
;
502 // Does BMP encroach on default check menu position?
504 size_t nAdjustedWidth
= m_bmpChecked
.GetWidth() +
505 (wxSystemSettings::GetMetric(wxSYS_EDGE_X
) * 2);
508 // Do we need to widen margin to fit BMP?
510 if ((size_t)GetMarginWidth() < nAdjustedWidth
)
511 SetMarginWidth(nAdjustedWidth
);
514 // Add the size of the bitmap to our total size...
516 *pWidth
+= GetMarginWidth();
520 // Add the size of the bitmap to our total size - even if we don't have
521 // a bitmap we leave room for one...
523 *pWidth
+= GetMarginWidth();
526 // Make sure that this item is at least as
527 // tall as the user's system settings specify
529 const size_t heightStd
= 6; // FIXME: get value from the system
530 if ( *pHeight
< heightStd
)
531 *pHeight
= heightStd
;
532 m_nHeight
= *pHeight
; // remember height for use in OnDrawItem
534 } // end of wxOwnerDrawn::OnMeasureItem
536 bool wxMenuItem::OnDrawItem( wxDC
& rDC
,
543 // Select the font and draw the text
544 // ---------------------------------
548 wxPMDCImpl
*impl
= (wxPMDCImpl
*) rDC
.GetImpl();
549 HPS hPS
= impl
->GetHPS();
554 RECTL vRect
= {rRect
.x
+ 4, rRect
.y
+ 1, rRect
.x
+ (rRect
.width
- 2), rRect
.y
+ rRect
.height
};
556 memset(&vCbnd
, 0, sizeof(CHARBUNDLE
));
559 GetColourToUse(eStatus
, vColText
, vColBack
);
562 rDC
.SetTextBackground(vColBack
);
563 rDC
.SetTextForeground(vColText
);
564 rDC
.SetBackgroundMode(wxTRANSPARENT
);
566 vCbnd
.lColor
= vColText
.GetPixel();
567 vCbnd
.lBackColor
= vColBack
.GetPixel();
570 ,CBB_BACK_COLOR
| CBB_COLOR
579 // Paint the background
581 ::WinFillRect(hPS
, &vRect
, vColBack
.GetPixel());
584 // Determine where to draw and leave space for a check-mark.
586 int nX
= rRect
.x
+ GetMarginWidth();
589 // Unfortunately, unlike Win32, PM has no owner drawn specific text
590 // drawing methods like ::DrawState that can cleanly handle accel
591 // mnemonics and deal, automatically, with various states, so we have
592 // to handle them ourselves. Notice Win32 can't handle \t in ownerdrawn
593 // strings either. We cannot handle mnemonics either. We display
594 // them, though, in the hope we can figure them out some day.
598 // Display main text and accel text separately to align better
600 wxString sTgt
= wxT("\t");
601 wxString sFullString
= GetItemLabel(); // need to save the original text
607 bool bFoundMnemonic
= false;
608 bool bFoundAccel
= false;
611 // Deal with the tab, extracting the Accel text
613 nIndex
= sFullString
.Find(sTgt
);
617 sAccel
= sFullString
.Mid(nIndex
+ 1);
618 sFullString
.Remove(nIndex
);
622 // Deal with the mnemonic character
625 nIndex
= sFullString
.Find(sTgt
);
628 wxString sTmp
= sFullString
;
630 bFoundMnemonic
= true;
632 rDC
.GetTextExtent( sTmp
636 sTmp
= sFullString
[(size_t)(nIndex
+ 1)];
637 rDC
.GetTextExtent( sTmp
638 ,(wxCoord
*)&nCharWidth
641 sFullString
.Replace(sTgt
.c_str(), wxEmptyString
, true);
645 // Draw the main item text sans the accel text
647 POINTL vPntStart
= {nX
, rRect
.y
+ 4};
648 ::GpiCharStringAt( impl
->GetHPS()
650 ,sFullString
.length()
651 ,sFullString
.char_str()
656 // Underline the mnemonic -- still won't work, but at least it "looks" right
659 POINTL vPntEnd
= {nX
+ nWidth
+ nCharWidth
- 3, rRect
.y
+ 2}; //CharWidth is bit wide
661 vPntStart
.x
= nX
+ nWidth
- 1;
662 vPntStart
.y
= rRect
.y
+ 2; // Make it look pretty!
663 vPen
= wxPen(vColText
, 1, wxSOLID
); // Assuming we are always black
665 ::GpiMove(hPS
, &vPntStart
);
666 ::GpiLine(hPS
, &vPntEnd
);
670 // Now draw the accel text
677 rDC
.GetTextExtent( sAccel
682 // Back off the starting position from the right edge
684 vPntStart
.x
= rRect
.width
- (nWidth
+ 7);
685 vPntStart
.y
= rRect
.y
+ 4;
686 ::GpiCharStringAt( impl
->GetHPS()
697 if (IsCheckable() && !m_bmpChecked
.IsOk())
699 if (eStatus
& wxODChecked
)
702 HBITMAP hBmpCheck
= ::WinGetSysBitmap(HWND_DESKTOP
, SBMP_MENUCHECK
);
704 vRect
.xLeft
= rRect
.x
;
705 vRect
.xRight
= rRect
.x
+ GetMarginWidth();
706 vRect
.yBottom
= rRect
.y
;
707 vRect
.yTop
= rRect
.y
+ m_nHeight
- 3;
709 ::WinDrawBitmap( hPS
// PS for this menuitem
710 ,hBmpCheck
// system checkmark
711 ,NULL
// draw the whole bitmap
712 ,(PPOINTL
)&vRect
// destination -- bottom left corner of the menuitem area
715 ,DBM_NORMAL
// draw normal size
722 // For uncheckable item we use only the 'checked' bitmap
724 wxBitmap
vBmp(GetBitmap(IsCheckable() ? ((eStatus
& wxODChecked
) != 0) : TRUE
));
729 wxMemoryDC
vDCMem(&rDC
);
730 wxMemoryDC
* pOldDC
= (wxMemoryDC
*)vBmp
.GetSelectedInto();
734 vBmp
.SetSelectedInto(NULL
);
736 vDCMem
.SelectObject(vBmp
);
741 int nBmpWidth
= vBmp
.GetWidth();
742 int nBmpHeight
= vBmp
.GetHeight();
745 // There should be enough space!
747 wxASSERT((nBmpWidth
<= rRect
.width
) && (nBmpHeight
<= rRect
.height
));
749 int nHeightDiff
= m_nHeight
- nBmpHeight
;
751 rDC
.Blit( rRect
.x
+ (GetMarginWidth() - nBmpWidth
) / 2
752 ,rRect
.y
+ nHeightDiff
/ 2
762 if (eStatus
& wxODSelected
)
764 POINTL vPnt1
= {rRect
.x
+ 1, rRect
.y
+ 3}; // Leave a little background border
765 POINTL vPnt2
= {rRect
.x
+ GetMarginWidth(), rRect
.y
+ m_nHeight
- 3};
769 vLine
.lColor
= vColBack
.GetPixel();
776 ::GpiMove(hPS
, &vPnt1
);
784 vBmp
.SetSelectedInto(NULL
);
788 } // end of wxOwnerDrawn::OnDrawItem
790 #endif // wxUSE_OWNER_DRAWN
792 // ----------------------------------------------------------------------------
794 // ----------------------------------------------------------------------------
796 wxMenuItem
* wxMenuItemBase::New(
799 , const wxString
& rName
800 , const wxString
& rHelp
805 return new wxMenuItem( pParentMenu
812 } // end of wxMenuItemBase::New