1 ///////////////////////////////////////////////////////////////////////////// 
   2 // Name:        src/msw/menu.cpp 
   3 // Purpose:     wxMenu, wxMenuBar, wxMenuItem 
   4 // Author:      Julian Smart 
   5 // Modified by: Vadim Zeitlin 
   8 // Copyright:   (c) Julian Smart 
   9 // Licence:     wxWindows licence 
  10 ///////////////////////////////////////////////////////////////////////////// 
  12 // =========================================================================== 
  14 // =========================================================================== 
  16 // --------------------------------------------------------------------------- 
  18 // --------------------------------------------------------------------------- 
  20 // For compilers that support precompilation, includes "wx.h". 
  21 #include "wx/wxprec.h" 
  40     #include "wx/ownerdrw.h" 
  43 #include "wx/scopedarray.h" 
  45 #include "wx/msw/private.h" 
  46 #include "wx/msw/wrapcctl.h" // include <commctrl.h> "properly" 
  54 #if (_WIN32_WCE < 400) && !defined(__HANDHELDPC__) 
  58 #include "wx/msw/wince/missing.h" 
  62 // other standard headers 
  66     #include "wx/dynlib.h" 
  69 #ifndef MNS_CHECKORBMP 
  70     #define MNS_CHECKORBMP 0x04000000 
  73     #define MIM_STYLE 0x00000010 
  76 // ---------------------------------------------------------------------------- 
  78 // ---------------------------------------------------------------------------- 
  80 // ---------------------------------------------------------------------------- 
  82 // ---------------------------------------------------------------------------- 
  84 // the (popup) menu title has this special id 
  85 static const UINT idMenuTitle 
= (UINT
)-3; 
  87 // ---------------------------------------------------------------------------- 
  89 // ---------------------------------------------------------------------------- 
  94 // make the given menu item default 
  95 void SetDefaultMenuItem(HMENU 
WXUNUSED_IN_WINCE(hmenu
), 
  96                         UINT 
WXUNUSED_IN_WINCE(id
)) 
 101     mii
.cbSize 
= sizeof(MENUITEMINFO
); 
 102     mii
.fMask 
= MIIM_STATE
; 
 103     mii
.fState 
= MFS_DEFAULT
; 
 105     if ( !::SetMenuItemInfo(hmenu
, id
, FALSE
, &mii
) ) 
 107         wxLogLastError(wxT("SetMenuItemInfo")); 
 109 #endif // !__WXWINCE__ 
 112 // make the given menu item owner-drawn 
 113 void SetOwnerDrawnMenuItem(HMENU 
WXUNUSED_IN_WINCE(hmenu
), 
 114                            UINT 
WXUNUSED_IN_WINCE(id
), 
 115                            ULONG_PTR 
WXUNUSED_IN_WINCE(data
)) 
 120     mii
.cbSize 
= sizeof(MENUITEMINFO
); 
 121     mii
.fMask 
= MIIM_FTYPE 
| MIIM_DATA
; 
 122     mii
.fType 
= MFT_OWNERDRAW
; 
 123     mii
.dwItemData 
= data
; 
 125     if ( !::SetMenuItemInfo(hmenu
, id
, FALSE
, &mii
) ) 
 127         wxLogLastError(wxT("SetMenuItemInfo")); 
 129 #endif // !__WXWINCE__ 
 133 UINT 
GetMenuState(HMENU hMenu
, UINT id
, UINT flags
) 
 137     info
.cbSize 
= sizeof(info
); 
 138     info
.fMask 
= MIIM_STATE
; 
 139     // MF_BYCOMMAND is zero so test MF_BYPOSITION 
 140     if ( !::GetMenuItemInfo(hMenu
, id
, flags 
& MF_BYPOSITION 
? TRUE 
: FALSE 
, & info
) ) 
 142         wxLogLastError(wxT("GetMenuItemInfo")); 
 146 #endif // __WXWINCE__ 
 148 inline bool IsLessThanStdSize(const wxBitmap
& bmp
) 
 150     // FIXME: these +4 are chosen so that 16*16 bitmaps pass this test with 
 151     //        default SM_CXMENUCHECK value but I have no idea what do we really 
 153     return bmp
.GetWidth() < ::GetSystemMetrics(SM_CXMENUCHECK
) + 4 && 
 154             bmp
.GetHeight() < ::GetSystemMetrics(SM_CYMENUCHECK
) + 4; 
 157 } // anonymous namespace 
 159 // ============================================================================ 
 161 // ============================================================================ 
 163 #include "wx/listimpl.cpp" 
 165 WX_DEFINE_LIST( wxMenuInfoList 
) 
 167 #if wxUSE_EXTENDED_RTTI 
 169 WX_DEFINE_FLAGS( wxMenuStyle 
) 
 171 wxBEGIN_FLAGS( wxMenuStyle 
) 
 172     wxFLAGS_MEMBER(wxMENU_TEAROFF
) 
 173 wxEND_FLAGS( wxMenuStyle 
) 
 175 IMPLEMENT_DYNAMIC_CLASS_XTI(wxMenu
, wxEvtHandler
,"wx/menu.h") 
 177 wxCOLLECTION_TYPE_INFO( wxMenuItem 
* , wxMenuItemList 
) ; 
 179 template<> void wxCollectionToVariantArray( wxMenuItemList 
const &theList
, wxxVariantArray 
&value
) 
 181     wxListCollectionToVariantArray
<wxMenuItemList::compatibility_iterator
>( theList 
, value 
) ; 
 184 wxBEGIN_PROPERTIES_TABLE(wxMenu
) 
 185     wxEVENT_PROPERTY( Select 
, wxEVT_COMMAND_MENU_SELECTED 
, wxCommandEvent
) 
 186     wxPROPERTY( Title
, wxString 
, SetTitle
, GetTitle
, wxString(), 0 /*flags*/ , wxT("Helpstring") , wxT("group") ) 
 187     wxREADONLY_PROPERTY_FLAGS( MenuStyle 
, wxMenuStyle 
, long , GetStyle 
, EMPTY_MACROVALUE 
, 0 /*flags*/ , wxT("Helpstring") , wxT("group")) // style 
 188     wxPROPERTY_COLLECTION( MenuItems 
, wxMenuItemList 
, wxMenuItem
* , Append 
, GetMenuItems 
, 0 /*flags*/ , wxT("Helpstring") , wxT("group")) 
 189 wxEND_PROPERTIES_TABLE() 
 191 wxBEGIN_HANDLERS_TABLE(wxMenu
) 
 192 wxEND_HANDLERS_TABLE() 
 194 wxDIRECT_CONSTRUCTOR_2( wxMenu 
, wxString 
, Title 
, long , MenuStyle  
) 
 196 WX_DEFINE_FLAGS( wxMenuBarStyle 
) 
 198 wxBEGIN_FLAGS( wxMenuBarStyle 
) 
 199     wxFLAGS_MEMBER(wxMB_DOCKABLE
) 
 200 wxEND_FLAGS( wxMenuBarStyle 
) 
 202 // the negative id would lead the window (its superclass !) to vetoe streaming out otherwise 
 203 bool wxMenuBarStreamingCallback( const wxObject 
*WXUNUSED(object
), wxWriter 
* , wxPersister 
* , wxxVariantArray 
& ) 
 208 IMPLEMENT_DYNAMIC_CLASS_XTI_CALLBACK(wxMenuBar
, wxWindow 
,"wx/menu.h",wxMenuBarStreamingCallback
) 
 210 IMPLEMENT_DYNAMIC_CLASS_XTI(wxMenuInfo
, wxObject 
, "wx/menu.h" ) 
 212 wxBEGIN_PROPERTIES_TABLE(wxMenuInfo
) 
 213     wxREADONLY_PROPERTY( Menu 
, wxMenu
* , GetMenu 
, EMPTY_MACROVALUE 
, 0 /*flags*/ , wxT("Helpstring") , wxT("group")) 
 214     wxREADONLY_PROPERTY( Title 
, wxString 
, GetTitle 
, wxString() , 0 /*flags*/ , wxT("Helpstring") , wxT("group")) 
 215 wxEND_PROPERTIES_TABLE() 
 217 wxBEGIN_HANDLERS_TABLE(wxMenuInfo
) 
 218 wxEND_HANDLERS_TABLE() 
 220 wxCONSTRUCTOR_2( wxMenuInfo 
, wxMenu
* , Menu 
, wxString 
, Title 
) 
 222 wxCOLLECTION_TYPE_INFO( wxMenuInfo 
* , wxMenuInfoList 
) ; 
 224 template<> void wxCollectionToVariantArray( wxMenuInfoList 
const &theList
, wxxVariantArray 
&value
) 
 226     wxListCollectionToVariantArray
<wxMenuInfoList::compatibility_iterator
>( theList 
, value 
) ; 
 229 wxBEGIN_PROPERTIES_TABLE(wxMenuBar
) 
 230     wxPROPERTY_COLLECTION( MenuInfos 
, wxMenuInfoList 
, wxMenuInfo
* , Append 
, GetMenuInfos 
, 0 /*flags*/ , wxT("Helpstring") , wxT("group")) 
 231 wxEND_PROPERTIES_TABLE() 
 233 wxBEGIN_HANDLERS_TABLE(wxMenuBar
) 
 234 wxEND_HANDLERS_TABLE() 
 236 wxCONSTRUCTOR_DUMMY( wxMenuBar 
) 
 239 IMPLEMENT_DYNAMIC_CLASS(wxMenu
, wxEvtHandler
) 
 240 IMPLEMENT_DYNAMIC_CLASS(wxMenuBar
, wxWindow
) 
 241 IMPLEMENT_DYNAMIC_CLASS(wxMenuInfo
, wxObject
) 
 244 const wxMenuInfoList
& wxMenuBar::GetMenuInfos() const 
 246     wxMenuInfoList
* list 
= const_cast< wxMenuInfoList
* >( &m_menuInfos 
) ; 
 247     WX_CLEAR_LIST( wxMenuInfoList 
, *list 
) ; 
 248     for( size_t i 
= 0 ; i 
< GetMenuCount() ; ++i 
) 
 250         wxMenuInfo
* info 
= new wxMenuInfo() ; 
 251         info
->Create( const_cast<wxMenuBar
*>(this)->GetMenu(i
) , GetMenuLabel(i
) ) ; 
 252         list
->Append( info 
) ; 
 257 // --------------------------------------------------------------------------- 
 258 // wxMenu construction, adding and removing menu items 
 259 // --------------------------------------------------------------------------- 
 261 // Construct a menu with optional title (then use append) 
 265     m_startRadioGroup 
= -1; 
 267 #if wxUSE_OWNER_DRAWN 
 268     m_ownerDrawn 
= false; 
 269     m_maxBitmapWidth 
= 0; 
 270 #endif // wxUSE_OWNER_DRAWN 
 273     m_hMenu 
= (WXHMENU
)CreatePopupMenu(); 
 276         wxLogLastError(wxT("CreatePopupMenu")); 
 279     // if we have a title, insert it in the beginning of the menu 
 280     if ( !m_title
.empty() ) 
 282         Append(idMenuTitle
, m_title
); 
 287 // The wxWindow destructor will take care of deleting the submenus. 
 290     // we should free Windows resources only if Windows doesn't do it for us 
 291     // which happens if we're attached to a menubar or a submenu of another 
 293     if ( !IsAttached() && !GetParent() ) 
 295         if ( !::DestroyMenu(GetHmenu()) ) 
 297             wxLogLastError(wxT("DestroyMenu")); 
 303     WX_CLEAR_ARRAY(m_accels
); 
 304 #endif // wxUSE_ACCEL 
 309     // this will take effect during the next call to Append() 
 313 void wxMenu::Attach(wxMenuBarBase 
*menubar
) 
 315     wxMenuBase::Attach(menubar
); 
 322 int wxMenu::FindAccel(int id
) const 
 324     size_t n
, count 
= m_accels
.GetCount(); 
 325     for ( n 
= 0; n 
< count
; n
++ ) 
 327         if ( m_accels
[n
]->m_command 
== id 
) 
 334 void wxMenu::UpdateAccel(wxMenuItem 
*item
) 
 336     if ( item
->IsSubMenu() ) 
 338         wxMenu 
*submenu 
= item
->GetSubMenu(); 
 339         wxMenuItemList::compatibility_iterator node 
= submenu
->GetMenuItems().GetFirst(); 
 342             UpdateAccel(node
->GetData()); 
 344             node 
= node
->GetNext(); 
 347     else if ( !item
->IsSeparator() ) 
 349         // recurse upwards: we should only modify m_accels of the top level 
 350         // menus, not of the submenus as wxMenuBar doesn't look at them 
 351         // (alternative and arguable cleaner solution would be to recurse 
 352         // downwards in GetAccelCount() and CopyAccels()) 
 355             GetParent()->UpdateAccel(item
); 
 359         // find the (new) accel for this item 
 360         wxAcceleratorEntry 
*accel 
= wxAcceleratorEntry::Create(item
->GetItemLabel()); 
 362             accel
->m_command 
= item
->GetId(); 
 365         int n 
= FindAccel(item
->GetId()); 
 366         if ( n 
== wxNOT_FOUND 
) 
 368             // no old, add new if any 
 372                 return;     // skipping RebuildAccelTable() below 
 376             // replace old with new or just remove the old one if no new 
 381                 m_accels
.RemoveAt(n
); 
 386             GetMenuBar()->RebuildAccelTable(); 
 389     //else: it is a separator, they can't have accels, nothing to do 
 392 #endif // wxUSE_ACCEL 
 397 // helper of DoInsertOrAppend(): returns the HBITMAP to use in MENUITEMINFO 
 398 HBITMAP 
GetHBitmapForMenu(wxMenuItem 
*pItem
, bool checked 
= true) 
 400     // Under versions of Windows older than Vista we can't pass HBITMAP 
 401     // directly as hbmpItem for 2 reasons: 
 402     //  1. We can't draw it with transparency then (this is not 
 403     //     very important now but would be with themed menu bg) 
 404     //  2. Worse, Windows inverts the bitmap for the selected 
 405     //     item and this looks downright ugly 
 407     // So we prefer to instead draw it ourselves in MSWOnDrawItem().by using 
 408     // HBMMENU_CALLBACK when inserting it 
 410     // However under Vista using HBMMENU_CALLBACK causes the entire menu to be 
 411     // drawn using the classic theme instead of the current one and it does 
 412     // handle transparency just fine so do use the real bitmap there 
 414     if ( wxGetWinVersion() >= wxWinVersion_Vista 
) 
 416         wxBitmap bmp 
= pItem
->GetBitmap(checked
); 
 419             // we must use PARGB DIB for the menu bitmaps so ensure that we do 
 420             wxImage 
img(bmp
.ConvertToImage()); 
 421             if ( !img
.HasAlpha() ) 
 424                 pItem
->SetBitmap(img
, checked
); 
 427             return GetHbitmapOf(pItem
->GetBitmap(checked
)); 
 429         //else: bitmap is not set 
 433 #endif // wxUSE_IMAGE 
 435     return HBMMENU_CALLBACK
; 
 438 } // anonymous namespace 
 440 // append a new item or submenu to the menu 
 441 bool wxMenu::DoInsertOrAppend(wxMenuItem 
*pItem
, size_t pos
) 
 445 #endif // wxUSE_ACCEL 
 447     // we should support disabling the item even prior to adding it to the menu 
 448     UINT flags 
= pItem
->IsEnabled() ? MF_ENABLED 
: MF_GRAYED
; 
 450     // if "Break" has just been called, insert a menu break before this item 
 451     // (and don't forget to reset the flag) 
 453         flags 
|= MF_MENUBREAK
; 
 457     if ( pItem
->IsSeparator() ) { 
 458         flags 
|= MF_SEPARATOR
; 
 461     // id is the numeric id for normal menu items and HMENU for submenus as 
 462     // required by ::AppendMenu() API 
 464     wxMenu 
*submenu 
= pItem
->GetSubMenu(); 
 465     if ( submenu 
!= NULL 
) { 
 466         wxASSERT_MSG( submenu
->GetHMenu(), wxT("invalid submenu") ); 
 468         submenu
->SetParent(this); 
 470         id 
= (UINT_PTR
)submenu
->GetHMenu(); 
 475         id 
= pItem
->GetMSWId(); 
 479     // prepare to insert the item in the menu 
 480     wxString itemText 
= pItem
->GetItemLabel(); 
 481     LPCTSTR pData 
= NULL
; 
 482     if ( pos 
== (size_t)-1 ) 
 484         // append at the end (note that the item is already appended to 
 485         // internal data structures) 
 486         pos 
= GetMenuItemCount() - 1; 
 489     // adjust position to account for the title, if any 
 490     if ( !m_title
.empty() ) 
 491         pos 
+= 2; // for the title itself and its separator 
 495 #if wxUSE_OWNER_DRAWN 
 496     // Under older systems mixing owner-drawn and non-owner-drawn items results 
 497     // in inconsistent margins, so we force this one to be owner-drawn if any 
 498     // other items already are. 
 499     if ( m_ownerDrawn 
&& !pItem
->IsSeparator() ) 
 500         pItem
->SetOwnerDrawn(true); 
 501 #endif // wxUSE_OWNER_DRAWN 
 503     // check if we have something more than a simple text item 
 504 #if wxUSE_OWNER_DRAWN 
 505     if ( pItem
->IsOwnerDrawn() ) 
 511             // MIIM_BITMAP only works under WinME/2000+ so we always use owner 
 512             // drawn item under the previous versions and we also have to use 
 513             // them in any case if the item has custom colours or font 
 514             static const wxWinVersion winver 
= wxGetWinVersion(); 
 515             bool mustUseOwnerDrawn 
= winver 
< wxWinVersion_98 
|| 
 516                                      pItem
->GetTextColour().Ok() || 
 517                                      pItem
->GetBackgroundColour().Ok() || 
 518                                      pItem
->GetFont().Ok(); 
 520             if ( !mustUseOwnerDrawn 
) 
 522                 const wxBitmap
& bmpUnchecked 
= pItem
->GetBitmap(false), 
 523                                 bmpChecked   
= pItem
->GetBitmap(true); 
 525                 if ( (bmpUnchecked
.Ok() && !IsLessThanStdSize(bmpUnchecked
)) || 
 526                      (bmpChecked
.Ok()   && !IsLessThanStdSize(bmpChecked
)) ) 
 528                     mustUseOwnerDrawn 
= true; 
 532             // use InsertMenuItem() if possible as it's guaranteed to look 
 533             // correct while our owner-drawn code is not 
 534             if ( !mustUseOwnerDrawn 
) 
 536                 WinStruct
<MENUITEMINFO
> mii
; 
 537                 mii
.fMask 
= MIIM_STRING 
| MIIM_DATA
; 
 539                 // don't set hbmpItem for the checkable items as it would 
 540                 // be used for both checked and unchecked state 
 541                 if ( pItem
->IsCheckable() ) 
 543                     mii
.fMask 
|= MIIM_CHECKMARKS
; 
 544                     mii
.hbmpChecked 
= GetHBitmapForMenu(pItem
, true); 
 545                     mii
.hbmpUnchecked 
= GetHBitmapForMenu(pItem
, false); 
 547                 else if ( pItem
->GetBitmap().IsOk() ) 
 549                     mii
.fMask 
|= MIIM_BITMAP
; 
 550                     mii
.hbmpItem 
= GetHBitmapForMenu(pItem
); 
 553                 mii
.cch 
= itemText
.length(); 
 554                 mii
.dwTypeData 
= const_cast<wxChar 
*>(itemText
.wx_str()); 
 556                 if ( flags 
& MF_POPUP 
) 
 558                     mii
.fMask 
|= MIIM_SUBMENU
; 
 559                     mii
.hSubMenu 
= GetHmenuOf(pItem
->GetSubMenu()); 
 563                     mii
.fMask 
|= MIIM_ID
; 
 567                 mii
.dwItemData 
= reinterpret_cast<ULONG_PTR
>(pItem
); 
 569                 ok 
= ::InsertMenuItem(GetHmenu(), pos
, TRUE 
/* by pos */, &mii
); 
 572                     wxLogLastError(wxT("InsertMenuItem()")); 
 574                 else // InsertMenuItem() ok 
 576                     // we need to remove the extra indent which is reserved for 
 577                     // the checkboxes by default as it looks ugly unless check 
 578                     // boxes are used together with bitmaps and this is not the 
 580                     WinStruct
<MENUINFO
> mi
; 
 582                     // don't call SetMenuInfo() directly, this would prevent 
 583                     // the app from starting up under Windows 95/NT 4 
 584                     typedef BOOL (WINAPI 
*SetMenuInfo_t
)(HMENU
, MENUINFO 
*); 
 586                     wxDynamicLibrary 
dllUser(wxT("user32")); 
 587                     wxDYNLIB_FUNCTION(SetMenuInfo_t
, SetMenuInfo
, dllUser
); 
 588                     if ( pfnSetMenuInfo 
) 
 590                         mi
.fMask 
= MIM_STYLE
; 
 591                         mi
.dwStyle 
= MNS_CHECKORBMP
; 
 592                         if ( !(*pfnSetMenuInfo
)(GetHmenu(), &mi
) ) 
 594                             wxLogLastError(wxT("SetMenuInfo(MNS_NOCHECK)")); 
 598                     // tell the item that it's not really owner-drawn but only 
 599                     // needs to draw its bitmap, the rest is done by Windows 
 600                     pItem
->SetOwnerDrawn(false); 
 608             // item draws itself, pass pointer to it in data parameter 
 609             flags 
|= MF_OWNERDRAW
; 
 610             pData 
= (LPCTSTR
)pItem
; 
 612             bool updateAllMargins 
= false; 
 614             // get size of bitmap always return valid value (0 for invalid bitmap), 
 615             // so we don't needed check if bitmap is valid ;) 
 616             int uncheckedW 
= pItem
->GetBitmap(false).GetWidth(); 
 617             int checkedW   
= pItem
->GetBitmap(true).GetWidth(); 
 619             if ( m_maxBitmapWidth 
< uncheckedW 
) 
 621                 m_maxBitmapWidth 
= uncheckedW
; 
 622                 updateAllMargins 
= true; 
 625             if ( m_maxBitmapWidth 
< checkedW 
) 
 627                 m_maxBitmapWidth 
= checkedW
; 
 628                 updateAllMargins 
= true; 
 631             // make other item ownerdrawn and update margin width for equals alignment 
 632             if ( !m_ownerDrawn 
|| updateAllMargins 
) 
 634                 wxMenuItemList::compatibility_iterator node 
= GetMenuItems().GetFirst(); 
 637                     wxMenuItem
* item 
= node
->GetData(); 
 639                     if ( !item
->IsSeparator() ) 
 641                         if ( !item
->IsOwnerDrawn() ) 
 643                             item
->SetOwnerDrawn(true); 
 644                             SetOwnerDrawnMenuItem(GetHmenu(), item
->GetMSWId(), 
 645                                                   reinterpret_cast<ULONG_PTR
>(item
)); 
 647                         item
->SetMarginWidth(m_maxBitmapWidth
); 
 650                     node 
= node
->GetNext(); 
 653                 // set menu as ownerdrawn 
 656             // only update our margin for equals alignment to other item 
 657             else if ( !updateAllMargins 
) 
 659                 pItem
->SetMarginWidth(m_maxBitmapWidth
); 
 664 #endif // wxUSE_OWNER_DRAWN 
 666         // item is just a normal string (passed in data parameter) 
 670         itemText 
= wxMenuItem::GetLabelText(itemText
); 
 673         pData 
= (wxChar
*)itemText
.wx_str(); 
 676     // item might have already been inserted by InsertMenuItem() above 
 679         if ( !::InsertMenu(GetHmenu(), pos
, flags 
| MF_BYPOSITION
, id
, pData
) ) 
 681             wxLogLastError(wxT("InsertMenu[Item]()")); 
 688     // if we just appended the title, highlight it 
 689     if ( id 
== idMenuTitle 
) 
 691         // visually select the menu title 
 692         SetDefaultMenuItem(GetHmenu(), id
); 
 695     // if we're already attached to the menubar, we must update it 
 696     if ( IsAttached() && GetMenuBar()->IsAttached() ) 
 698         GetMenuBar()->Refresh(); 
 704 void wxMenu::EndRadioGroup() 
 706     // we're not inside a radio group any longer 
 707     m_startRadioGroup 
= -1; 
 710 wxMenuItem
* wxMenu::DoAppend(wxMenuItem 
*item
) 
 712     wxCHECK_MSG( item
, NULL
, wxT("NULL item in wxMenu::DoAppend") ); 
 716     if ( item
->GetKind() == wxITEM_RADIO 
) 
 718         int count 
= GetMenuItemCount(); 
 720         if ( m_startRadioGroup 
== -1 ) 
 722             // start a new radio group 
 723             m_startRadioGroup 
= count
; 
 725             // for now it has just one element 
 726             item
->SetAsRadioGroupStart(); 
 727             item
->SetRadioGroupEnd(m_startRadioGroup
); 
 729             // ensure that we have a checked item in the radio group 
 732         else // extend the current radio group 
 734             // we need to update its end item 
 735             item
->SetRadioGroupStart(m_startRadioGroup
); 
 736             wxMenuItemList::compatibility_iterator node 
= GetMenuItems().Item(m_startRadioGroup
); 
 740                 node
->GetData()->SetRadioGroupEnd(count
); 
 744                 wxFAIL_MSG( wxT("where is the radio group start item?") ); 
 748     else // not a radio item 
 753     if ( !wxMenuBase::DoAppend(item
) || !DoInsertOrAppend(item
) ) 
 760         // check the item initially 
 767 wxMenuItem
* wxMenu::DoInsert(size_t pos
, wxMenuItem 
*item
) 
 769     if (wxMenuBase::DoInsert(pos
, item
) && DoInsertOrAppend(item
, pos
)) 
 775 wxMenuItem 
*wxMenu::DoRemove(wxMenuItem 
*item
) 
 777     // we need to find the item's position in the child list 
 779     wxMenuItemList::compatibility_iterator node 
= GetMenuItems().GetFirst(); 
 780     for ( pos 
= 0; node
; pos
++ ) 
 782         if ( node
->GetData() == item 
) 
 785         node 
= node
->GetNext(); 
 788     // DoRemove() (unlike Remove) can only be called for an existing item! 
 789     wxCHECK_MSG( node
, NULL
, wxT("bug in wxMenu::Remove logic") ); 
 792     // remove the corresponding accel from the accel table 
 793     int n 
= FindAccel(item
->GetId()); 
 794     if ( n 
!= wxNOT_FOUND 
) 
 798         m_accels
.RemoveAt(n
); 
 800     //else: this item doesn't have an accel, nothing to do 
 801 #endif // wxUSE_ACCEL 
 803     // remove the item from the menu 
 804     if ( !::RemoveMenu(GetHmenu(), (UINT
)pos
, MF_BYPOSITION
) ) 
 806         wxLogLastError(wxT("RemoveMenu")); 
 809     if ( IsAttached() && GetMenuBar()->IsAttached() ) 
 811         // otherwise, the change won't be visible 
 812         GetMenuBar()->Refresh(); 
 815     // and from internal data structures 
 816     return wxMenuBase::DoRemove(item
); 
 819 // --------------------------------------------------------------------------- 
 820 // accelerator helpers 
 821 // --------------------------------------------------------------------------- 
 825 // create the wxAcceleratorEntries for our accels and put them into the provided 
 826 // array - return the number of accels we have 
 827 size_t wxMenu::CopyAccels(wxAcceleratorEntry 
*accels
) const 
 829     size_t count 
= GetAccelCount(); 
 830     for ( size_t n 
= 0; n 
< count
; n
++ ) 
 832         *accels
++ = *m_accels
[n
]; 
 838 wxAcceleratorTable 
*wxMenu::CreateAccelTable() const 
 840     const size_t count 
= m_accels
.size(); 
 841     wxScopedArray
<wxAcceleratorEntry
> accels(new wxAcceleratorEntry
[count
]); 
 842     CopyAccels(accels
.get()); 
 844     return new wxAcceleratorTable(count
, accels
.get()); 
 847 #endif // wxUSE_ACCEL 
 849 // --------------------------------------------------------------------------- 
 851 // --------------------------------------------------------------------------- 
 853 void wxMenu::SetTitle(const wxString
& label
) 
 855     bool hasNoTitle 
= m_title
.empty(); 
 858     HMENU hMenu 
= GetHmenu(); 
 862         if ( !label
.empty() ) 
 864             if ( !::InsertMenu(hMenu
, 0u, MF_BYPOSITION 
| MF_STRING
, 
 865                                idMenuTitle
, m_title
.wx_str()) || 
 866                  !::InsertMenu(hMenu
, 1u, MF_BYPOSITION
, (unsigned)-1, NULL
) ) 
 868                 wxLogLastError(wxT("InsertMenu")); 
 876             // remove the title and the separator after it 
 877             if ( !RemoveMenu(hMenu
, 0, MF_BYPOSITION
) || 
 878                  !RemoveMenu(hMenu
, 0, MF_BYPOSITION
) ) 
 880                 wxLogLastError(wxT("RemoveMenu")); 
 889             info
.cbSize 
= sizeof(info
); 
 890             info
.fMask 
= MIIM_TYPE
; 
 891             info
.fType 
= MFT_STRING
; 
 892             info
.cch 
= m_title
.length(); 
 893             info
.dwTypeData 
= const_cast<wxChar 
*>(m_title
.wx_str()); 
 894             if ( !SetMenuItemInfo(hMenu
, 0, TRUE
, & info
) ) 
 896                 wxLogLastError(wxT("SetMenuItemInfo")); 
 899             if ( !ModifyMenu(hMenu
, 0u, 
 900                              MF_BYPOSITION 
| MF_STRING
, 
 901                              idMenuTitle
, m_title
.wx_str()) ) 
 903                 wxLogLastError(wxT("ModifyMenu")); 
 910     // put the title string in bold face 
 911     if ( !m_title
.empty() ) 
 913         SetDefaultMenuItem(GetHmenu(), idMenuTitle
); 
 918 // --------------------------------------------------------------------------- 
 920 // --------------------------------------------------------------------------- 
 922 bool wxMenu::MSWCommand(WXUINT 
WXUNUSED(param
), WXWORD id_
) 
 924     const int id 
= (signed short)id_
; 
 926     // ignore commands from the menu title 
 927     if ( id 
!= (int)idMenuTitle 
) 
 929         // update the check item when it's clicked 
 930         wxMenuItem 
* const item 
= FindItem(id
); 
 931         if ( item 
&& item
->IsCheckable() ) 
 934         // get the status of the menu item: note that it has been just changed 
 935         // by Toggle() above so here we already get the new state of the item 
 936         UINT menuState 
= ::GetMenuState(GetHmenu(), id
, MF_BYCOMMAND
); 
 937         SendEvent(id
, menuState 
& MF_CHECKED
); 
 943 // --------------------------------------------------------------------------- 
 945 // --------------------------------------------------------------------------- 
 947 wxWindow 
*wxMenu::GetWindow() const 
 949     if ( m_invokingWindow 
!= NULL 
) 
 950         return m_invokingWindow
; 
 951     else if ( GetMenuBar() != NULL
) 
 952         return GetMenuBar()->GetFrame(); 
 957 // --------------------------------------------------------------------------- 
 959 // --------------------------------------------------------------------------- 
 961 void wxMenuBar::Init() 
 963     m_eventHandler 
= this; 
 965 #if wxUSE_TOOLBAR && defined(__WXWINCE__) 
 968     // Not using a combined wxToolBar/wxMenuBar? then use 
 969     // a commandbar in WinCE .NET just to implement the 
 971 #if defined(WINCE_WITH_COMMANDBAR) 
 973     m_adornmentsAdded 
= false; 
 977 wxMenuBar::wxMenuBar() 
 982 wxMenuBar::wxMenuBar( long WXUNUSED(style
) ) 
 987 wxMenuBar::wxMenuBar(size_t count
, wxMenu 
*menus
[], const wxString titles
[], long WXUNUSED(style
)) 
 991     m_titles
.Alloc(count
); 
 993     for ( size_t i 
= 0; i 
< count
; i
++ ) 
 995         m_menus
.Append(menus
[i
]); 
 996         m_titles
.Add(titles
[i
]); 
 998         menus
[i
]->Attach(this); 
1002 wxMenuBar::~wxMenuBar() 
1004     // In Windows CE (not .NET), the menubar is always associated 
1005     // with a toolbar, which destroys the menu implicitly. 
1006 #if defined(WINCE_WITHOUT_COMMANDBAR) && defined(__POCKETPC__) 
1009         wxToolMenuBar
* toolMenuBar 
= wxDynamicCast(GetToolBar(), wxToolMenuBar
); 
1011             toolMenuBar
->SetMenuBar(NULL
); 
1014     // we should free Windows resources only if Windows doesn't do it for us 
1015     // which happens if we're attached to a frame 
1016     if (m_hMenu 
&& !IsAttached()) 
1018 #if defined(WINCE_WITH_COMMANDBAR) 
1019         ::DestroyWindow((HWND
) m_commandBar
); 
1020         m_commandBar 
= (WXHWND
) NULL
; 
1022         ::DestroyMenu((HMENU
)m_hMenu
); 
1024         m_hMenu 
= (WXHMENU
)NULL
; 
1029 // --------------------------------------------------------------------------- 
1030 // wxMenuBar helpers 
1031 // --------------------------------------------------------------------------- 
1033 void wxMenuBar::Refresh() 
1038     wxCHECK_RET( IsAttached(), wxT("can't refresh unattached menubar") ); 
1040 #if defined(WINCE_WITHOUT_COMMANDBAR) 
1043         CommandBar_DrawMenuBar((HWND
) GetToolBar()->GetHWND(), 0); 
1045 #elif defined(WINCE_WITH_COMMANDBAR) 
1047         DrawMenuBar((HWND
) m_commandBar
); 
1049     DrawMenuBar(GetHwndOf(GetFrame())); 
1053 WXHMENU 
wxMenuBar::Create() 
1055     // Note: this doesn't work at all on Smartphone, 
1056     // since you have to use resources. 
1057     // We'll have to find another way to add a menu 
1058     // by changing/adding menu items to an existing menu. 
1059 #if defined(WINCE_WITHOUT_COMMANDBAR) 
1063     wxToolMenuBar 
* const bar 
= static_cast<wxToolMenuBar 
*>(GetToolBar()); 
1067     HWND hCommandBar 
= GetHwndOf(bar
); 
1069     // notify comctl32.dll about the version of the headers we use before using 
1070     // any other TB_XXX messages 
1071     SendMessage(hCommandBar
, TB_BUTTONSTRUCTSIZE
, sizeof(TBBUTTON
), 0); 
1074     wxZeroMemory(tbButton
); 
1075     tbButton
.iBitmap 
= I_IMAGENONE
; 
1076     tbButton
.fsState 
= TBSTATE_ENABLED
; 
1077     tbButton
.fsStyle 
= TBSTYLE_DROPDOWN 
| 
1078                        TBSTYLE_NO_DROPDOWN_ARROW 
| 
1081     for ( unsigned i 
= 0; i 
< GetMenuCount(); i
++ ) 
1083         HMENU hPopupMenu 
= (HMENU
) GetMenu(i
)->GetHMenu(); 
1084         tbButton
.dwData 
= (DWORD
)hPopupMenu
; 
1085         wxString label 
= wxStripMenuCodes(GetMenuLabel(i
)); 
1086         tbButton
.iString 
= (int) label
.wx_str(); 
1088         tbButton
.idCommand 
= NewControlId(); 
1089         if ( !::SendMessage(hCommandBar
, TB_INSERTBUTTON
, i
, (LPARAM
)&tbButton
) ) 
1091             wxLogLastError(wxT("TB_INSERTBUTTON")); 
1095     m_hMenu 
= bar
->GetHMenu(); 
1097 #else // !__WXWINCE__ 
1101     m_hMenu 
= (WXHMENU
)::CreateMenu(); 
1105         wxLogLastError(wxT("CreateMenu")); 
1109         size_t count 
= GetMenuCount(), i
; 
1110         wxMenuList::iterator it
; 
1111         for ( i 
= 0, it 
= m_menus
.begin(); i 
< count
; i
++, it
++ ) 
1113             if ( !::AppendMenu((HMENU
)m_hMenu
, MF_POPUP 
| MF_STRING
, 
1114                                (UINT_PTR
)(*it
)->GetHMenu(), 
1115                                m_titles
[i
].wx_str()) ) 
1117                 wxLogLastError(wxT("AppendMenu")); 
1123 #endif // __WXWINCE__/!__WXWINCE__ 
1126 int wxMenuBar::MSWPositionForWxMenu(wxMenu 
*menu
, int wxpos
) 
1129     wxASSERT(menu
->GetHMenu()); 
1132 #if defined(__WXWINCE__) 
1133     int totalMSWItems 
= GetMenuCount(); 
1135     int totalMSWItems 
= GetMenuItemCount((HMENU
)m_hMenu
); 
1138     int i
; // For old C++ compatibility 
1139     for(i
=wxpos
; i
<totalMSWItems
; i
++) 
1141         if(GetSubMenu((HMENU
)m_hMenu
,i
)==(HMENU
)menu
->GetHMenu()) 
1144     for(i
=0; i
<wxpos
; i
++) 
1146         if(GetSubMenu((HMENU
)m_hMenu
,i
)==(HMENU
)menu
->GetHMenu()) 
1153 // --------------------------------------------------------------------------- 
1154 // wxMenuBar functions to work with the top level submenus 
1155 // --------------------------------------------------------------------------- 
1157 // NB: we don't support owner drawn top level items for now, if we do these 
1158 //     functions would have to be changed to use wxMenuItem as well 
1160 void wxMenuBar::EnableTop(size_t pos
, bool enable
) 
1162     wxCHECK_RET( IsAttached(), wxT("doesn't work with unattached menubars") ); 
1163     wxCHECK_RET( pos 
< GetMenuCount(), wxT("invalid menu index") ); 
1165     int flag 
= enable 
? MF_ENABLED 
: MF_GRAYED
; 
1167     EnableMenuItem((HMENU
)m_hMenu
, MSWPositionForWxMenu(GetMenu(pos
),pos
), MF_BYPOSITION 
| flag
); 
1172 void wxMenuBar::SetMenuLabel(size_t pos
, const wxString
& label
) 
1174     wxCHECK_RET( pos 
< GetMenuCount(), wxT("invalid menu index") ); 
1176     m_titles
[pos
] = label
; 
1178     if ( !IsAttached() ) 
1182     //else: have to modify the existing menu 
1184     int mswpos 
= MSWPositionForWxMenu(GetMenu(pos
),pos
); 
1187     UINT flagsOld 
= ::GetMenuState((HMENU
)m_hMenu
, mswpos
, MF_BYPOSITION
); 
1188     if ( flagsOld 
== 0xFFFFFFFF ) 
1190         wxLogLastError(wxT("GetMenuState")); 
1195     if ( flagsOld 
& MF_POPUP 
) 
1197         // HIBYTE contains the number of items in the submenu in this case 
1199         id 
= (UINT_PTR
)::GetSubMenu((HMENU
)m_hMenu
, mswpos
); 
1209     info
.cbSize 
= sizeof(info
); 
1210     info
.fMask 
= MIIM_TYPE
; 
1211     info
.fType 
= MFT_STRING
; 
1212     info
.cch 
= label
.length(); 
1213     info
.dwTypeData 
= const_cast<wxChar 
*>(label
.wx_str()); 
1214     if ( !SetMenuItemInfo(GetHmenu(), id
, TRUE
, &info
) ) 
1216         wxLogLastError(wxT("SetMenuItemInfo")); 
1220     if ( ::ModifyMenu(GetHmenu(), mswpos
, MF_BYPOSITION 
| MF_STRING 
| flagsOld
, 
1221                       id
, label
.wx_str()) == (int)0xFFFFFFFF ) 
1223         wxLogLastError(wxT("ModifyMenu")); 
1230 wxString 
wxMenuBar::GetMenuLabel(size_t pos
) const 
1232     wxCHECK_MSG( pos 
< GetMenuCount(), wxEmptyString
, 
1233                  wxT("invalid menu index in wxMenuBar::GetMenuLabel") ); 
1235     return m_titles
[pos
]; 
1238 // --------------------------------------------------------------------------- 
1239 // wxMenuBar construction 
1240 // --------------------------------------------------------------------------- 
1242 wxMenu 
*wxMenuBar::Replace(size_t pos
, wxMenu 
*menu
, const wxString
& title
) 
1244     wxMenu 
*menuOld 
= wxMenuBarBase::Replace(pos
, menu
, title
); 
1248     m_titles
[pos
] = title
; 
1250 #if defined(WINCE_WITHOUT_COMMANDBAR) 
1256         int mswpos 
= MSWPositionForWxMenu(menuOld
,pos
); 
1258         // can't use ModifyMenu() because it deletes the submenu it replaces 
1259         if ( !::RemoveMenu(GetHmenu(), (UINT
)mswpos
, MF_BYPOSITION
) ) 
1261             wxLogLastError(wxT("RemoveMenu")); 
1264         if ( !::InsertMenu(GetHmenu(), (UINT
)mswpos
, 
1265                            MF_BYPOSITION 
| MF_POPUP 
| MF_STRING
, 
1266                            (UINT_PTR
)GetHmenuOf(menu
), title
.wx_str()) ) 
1268             wxLogLastError(wxT("InsertMenu")); 
1272         if ( menuOld
->HasAccels() || menu
->HasAccels() ) 
1274             // need to rebuild accell table 
1275             RebuildAccelTable(); 
1277 #endif // wxUSE_ACCEL 
1286 bool wxMenuBar::Insert(size_t pos
, wxMenu 
*menu
, const wxString
& title
) 
1288     // Find out which MSW item before which we'll be inserting before 
1289     // wxMenuBarBase::Insert is called and GetMenu(pos) is the new menu. 
1290     // If IsAttached() is false this won't be used anyway 
1292 #if defined(WINCE_WITHOUT_COMMANDBAR) 
1298     int mswpos 
= (!isAttached 
|| (pos 
== m_menus
.GetCount())) 
1299         ?   -1 // append the menu 
1300         :   MSWPositionForWxMenu(GetMenu(pos
),pos
); 
1302     if ( !wxMenuBarBase::Insert(pos
, menu
, title
) ) 
1305     m_titles
.Insert(title
, pos
); 
1309 #if defined(WINCE_WITHOUT_COMMANDBAR) 
1313         memset(&tbButton
, 0, sizeof(TBBUTTON
)); 
1314         tbButton
.iBitmap 
= I_IMAGENONE
; 
1315         tbButton
.fsState 
= TBSTATE_ENABLED
; 
1316         tbButton
.fsStyle 
= TBSTYLE_DROPDOWN 
| TBSTYLE_NO_DROPDOWN_ARROW 
| TBSTYLE_AUTOSIZE
; 
1318         HMENU hPopupMenu 
= (HMENU
) menu
->GetHMenu() ; 
1319         tbButton
.dwData 
= (DWORD
)hPopupMenu
; 
1320         wxString label 
= wxStripMenuCodes(title
); 
1321         tbButton
.iString 
= (int) label
.wx_str(); 
1323         tbButton
.idCommand 
= NewControlId(); 
1324         if (!::SendMessage((HWND
) GetToolBar()->GetHWND(), TB_INSERTBUTTON
, pos
, (LPARAM
)&tbButton
)) 
1326             wxLogLastError(wxT("TB_INSERTBUTTON")); 
1329         wxUnusedVar(mswpos
); 
1331         if ( !::InsertMenu(GetHmenu(), mswpos
, 
1332                            MF_BYPOSITION 
| MF_POPUP 
| MF_STRING
, 
1333                            (UINT_PTR
)GetHmenuOf(menu
), title
.wx_str()) ) 
1335             wxLogLastError(wxT("InsertMenu")); 
1339         if ( menu
->HasAccels() ) 
1341             // need to rebuild accell table 
1342             RebuildAccelTable(); 
1344 #endif // wxUSE_ACCEL 
1353 bool wxMenuBar::Append(wxMenu 
*menu
, const wxString
& title
) 
1355     WXHMENU submenu 
= menu 
? menu
->GetHMenu() : 0; 
1356     wxCHECK_MSG( submenu
, false, wxT("can't append invalid menu to menubar") ); 
1358     if ( !wxMenuBarBase::Append(menu
, title
) ) 
1361     m_titles
.Add(title
); 
1363 #if defined(WINCE_WITHOUT_COMMANDBAR) 
1369 #if defined(WINCE_WITHOUT_COMMANDBAR) 
1373         memset(&tbButton
, 0, sizeof(TBBUTTON
)); 
1374         tbButton
.iBitmap 
= I_IMAGENONE
; 
1375         tbButton
.fsState 
= TBSTATE_ENABLED
; 
1376         tbButton
.fsStyle 
= TBSTYLE_DROPDOWN 
| TBSTYLE_NO_DROPDOWN_ARROW 
| TBSTYLE_AUTOSIZE
; 
1378         size_t pos 
= GetMenuCount(); 
1379         HMENU hPopupMenu 
= (HMENU
) menu
->GetHMenu() ; 
1380         tbButton
.dwData 
= (DWORD
)hPopupMenu
; 
1381         wxString label 
= wxStripMenuCodes(title
); 
1382         tbButton
.iString 
= (int) label
.wx_str(); 
1384         tbButton
.idCommand 
= NewControlId(); 
1385         if (!::SendMessage((HWND
) GetToolBar()->GetHWND(), TB_INSERTBUTTON
, pos
, (LPARAM
)&tbButton
)) 
1387             wxLogLastError(wxT("TB_INSERTBUTTON")); 
1391         if ( !::AppendMenu(GetHmenu(), MF_POPUP 
| MF_STRING
, 
1392                            (UINT_PTR
)submenu
, title
.wx_str()) ) 
1394             wxLogLastError(wxT("AppendMenu")); 
1399         if ( menu
->HasAccels() ) 
1401             // need to rebuild accelerator table 
1402             RebuildAccelTable(); 
1404 #endif // wxUSE_ACCEL 
1413 wxMenu 
*wxMenuBar::Remove(size_t pos
) 
1415     wxMenu 
*menu 
= wxMenuBarBase::Remove(pos
); 
1419 #if defined(WINCE_WITHOUT_COMMANDBAR) 
1425 #if defined(WINCE_WITHOUT_COMMANDBAR) 
1428             if (!::SendMessage((HWND
) GetToolBar()->GetHWND(), TB_DELETEBUTTON
, (UINT
) pos
, (LPARAM
) 0)) 
1430                 wxLogLastError(wxT("TB_DELETEBUTTON")); 
1434         if ( !::RemoveMenu(GetHmenu(), (UINT
)MSWPositionForWxMenu(menu
,pos
), MF_BYPOSITION
) ) 
1436             wxLogLastError(wxT("RemoveMenu")); 
1441         if ( menu
->HasAccels() ) 
1443             // need to rebuild accell table 
1444             RebuildAccelTable(); 
1446 #endif // wxUSE_ACCEL 
1452     m_titles
.RemoveAt(pos
); 
1459 void wxMenuBar::RebuildAccelTable() 
1461     // merge the accelerators of all menus into one accel table 
1462     size_t nAccelCount 
= 0; 
1463     size_t i
, count 
= GetMenuCount(); 
1464     wxMenuList::iterator it
; 
1465     for ( i 
= 0, it 
= m_menus
.begin(); i 
< count
; i
++, it
++ ) 
1467         nAccelCount 
+= (*it
)->GetAccelCount(); 
1472         wxAcceleratorEntry 
*accelEntries 
= new wxAcceleratorEntry
[nAccelCount
]; 
1475         for ( i 
= 0, it 
= m_menus
.begin(); i 
< count
; i
++, it
++ ) 
1477             nAccelCount 
+= (*it
)->CopyAccels(&accelEntries
[nAccelCount
]); 
1480         SetAcceleratorTable(wxAcceleratorTable(nAccelCount
, accelEntries
)); 
1482         delete [] accelEntries
; 
1486 #endif // wxUSE_ACCEL 
1488 void wxMenuBar::Attach(wxFrame 
*frame
) 
1490     wxMenuBarBase::Attach(frame
); 
1492 #if defined(WINCE_WITH_COMMANDBAR) 
1496         m_commandBar 
= (WXHWND
) CommandBar_Create(wxGetInstance(), (HWND
) frame
->GetHWND(), NewControlId()); 
1501             if (!CommandBar_InsertMenubarEx((HWND
) m_commandBar
, NULL
, (LPTSTR
) m_hMenu
, 0)) 
1503                 wxLogLastError(wxT("CommandBar_InsertMenubarEx")); 
1510     RebuildAccelTable(); 
1511 #endif // wxUSE_ACCEL 
1514 #if defined(WINCE_WITH_COMMANDBAR) 
1515 bool wxMenuBar::AddAdornments(long style
) 
1517     if (m_adornmentsAdded 
|| !m_commandBar
) 
1520     if (style 
& wxCLOSE_BOX
) 
1522         if (!CommandBar_AddAdornments((HWND
) m_commandBar
, 0, 0)) 
1524             wxLogLastError(wxT("CommandBar_AddAdornments")); 
1535 void wxMenuBar::Detach() 
1537     wxMenuBarBase::Detach(); 
1540 #endif // wxUSE_MENUS