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
.IsOk()) 
 495         // Is BMP height larger than 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
.IsOk()) 
 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