1 ///////////////////////////////////////////////////////////////////////////// 
   3 // Purpose:     wxMenuItem, wxMenu and wxMenuBar implementation 
   4 // Author:      Vadim Zeitlin 
   8 // Copyright:   (c) 2000 SciTech Software, Inc. (www.scitechsoft.com) 
   9 // Licence:     wxWindows licence 
  10 ///////////////////////////////////////////////////////////////////////////// 
  12 // ============================================================================ 
  14 // ============================================================================ 
  16 // ---------------------------------------------------------------------------- 
  18 // ---------------------------------------------------------------------------- 
  20 #include "wx/wxprec.h" 
  27     #include "wx/dynarray.h" 
  28     #include "wx/control.h"      // for FindAccelIndex() 
  30     #include "wx/settings.h" 
  37 #include "wx/popupwin.h" 
  38 #include "wx/evtloop.h" 
  39 #include "wx/dcclient.h" 
  42 #include "wx/univ/renderer.h" 
  45     #include "wx/msw/private.h" 
  48 // ---------------------------------------------------------------------------- 
  49 // wxMenuInfo contains all extra information about top level menus we need 
  50 // ---------------------------------------------------------------------------- 
  52 class WXDLLEXPORT wxMenuInfo
 
  56     wxMenuInfo(const wxString
& text
) 
  64     void SetLabel(const wxString
& text
) 
  66         // remember the accel char (may be -1 if none) 
  67         m_indexAccel 
= wxControl::FindAccelIndex(text
, &m_label
); 
  69         // calculate the width later, after the menu bar is created 
  73     void SetEnabled(bool enabled 
= true) { m_isEnabled 
= enabled
; } 
  77     const wxString
& GetLabel() const { return m_label
; } 
  78     bool IsEnabled() const { return m_isEnabled
; } 
  79     wxCoord 
GetWidth(wxMenuBar 
*menubar
) const 
  83             wxConstCast(this, wxMenuInfo
)->CalcWidth(menubar
); 
  89     int GetAccelIndex() const { return m_indexAccel
; } 
  92     void CalcWidth(wxMenuBar 
*menubar
) 
  95         wxClientDC 
dc(menubar
); 
  96         dc
.SetFont(wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT
)); 
  97         dc
.GetTextExtent(m_label
, &size
.x
, &size
.y
); 
  99         // adjust for the renderer we use and store the width 
 100         m_width 
= menubar
->GetRenderer()->GetMenuBarItemSize(size
).x
; 
 109 #include "wx/arrimpl.cpp" 
 111 WX_DEFINE_OBJARRAY(wxMenuInfoArray
); 
 113 // ---------------------------------------------------------------------------- 
 114 // wxPopupMenuWindow: a popup window showing a menu 
 115 // ---------------------------------------------------------------------------- 
 117 class wxPopupMenuWindow 
: public wxPopupTransientWindow
 
 120     wxPopupMenuWindow(wxWindow 
*parent
, wxMenu 
*menu
); 
 122     ~wxPopupMenuWindow(); 
 124     // override the base class version to select the first item initially 
 125     virtual void Popup(wxWindow 
*focus 
= NULL
); 
 127     // override the base class version to dismiss any open submenus 
 128     virtual void Dismiss(); 
 130     // notify the menu when the window disappears from screen 
 131     virtual void OnDismiss(); 
 133     // called when a submenu is dismissed 
 134     void OnSubmenuDismiss(bool dismissParent
); 
 136     // the default wxMSW wxPopupTransientWindow::OnIdle disables the capture 
 137     // when the cursor is inside the popup, which dsables the menu tracking 
 138     // so override it to do nothing 
 140     void OnIdle(wxIdleEvent
& WXUNUSED(event
)) { } 
 143     // get the currently selected item (may be NULL) 
 144     wxMenuItem 
*GetCurrentItem() const 
 146         return m_nodeCurrent 
? m_nodeCurrent
->GetData() : NULL
; 
 149     // find the menu item at given position 
 150     wxMenuItemList::compatibility_iterator 
GetMenuItemFromPoint(const wxPoint
& pt
) const; 
 152     // refresh the given item 
 153     void RefreshItem(wxMenuItem 
*item
); 
 155     // preselect the first item 
 156     void SelectFirst() { SetCurrent(m_menu
->GetMenuItems().GetFirst()); } 
 158     // process the key event, return true if done 
 159     bool ProcessKeyDown(int key
); 
 161     // process mouse move event 
 162     void ProcessMouseMove(const wxPoint
& pt
); 
 164     // don't dismiss the popup window if the parent menu was clicked 
 165     virtual bool ProcessLeftDown(wxMouseEvent
& event
); 
 168     // how did we perform this operation? 
 175     // draw the menu inside this window 
 176     virtual void DoDraw(wxControlRenderer 
*renderer
); 
 179     void OnLeftUp(wxMouseEvent
& event
); 
 180     void OnMouseMove(wxMouseEvent
& event
); 
 181     void OnMouseLeave(wxMouseEvent
& event
); 
 182     void OnKeyDown(wxKeyEvent
& event
); 
 184     // reset the current item and node 
 187     // set the current node and item withotu refreshing anything 
 188     void SetCurrent(wxMenuItemList::compatibility_iterator node
); 
 189     virtual bool SetCurrent(bool doit 
= true){return wxPopupTransientWindow::SetCurrent(doit
);}; 
 191     // change the current item refreshing the old and new items 
 192     void ChangeCurrent(wxMenuItemList::compatibility_iterator node
); 
 194     // activate item, i.e. call either ClickItem() or OpenSubmenu() depending 
 195     // on what it is, return true if something was done (i.e. it's not a 
 197     bool ActivateItem(wxMenuItem 
*item
, InputMethod how 
= WithKeyboard
); 
 199     // send the event about the item click 
 200     void ClickItem(wxMenuItem 
*item
); 
 202     // show the submenu for this item 
 203     void OpenSubmenu(wxMenuItem 
*item
, InputMethod how 
= WithKeyboard
); 
 205     // can this tiem be opened? 
 206     bool CanOpen(wxMenuItem 
*item
) 
 208         return item 
&& item
->IsEnabled() && item
->IsSubMenu(); 
 211     // dismiss the menu and all parent menus too 
 212     void DismissAndNotify(); 
 214     // react to dimissing this menu and also dismiss the parent if 
 216     void HandleDismiss(bool dismissParent
); 
 218     // do we have an open submenu? 
 219     bool HasOpenSubmenu() const { return m_hasOpenSubMenu
; } 
 221     // get previous node after the current one 
 222     wxMenuItemList::compatibility_iterator 
GetPrevNode() const; 
 224     // get previous node before the given one, wrapping if it's the first one 
 225     wxMenuItemList::compatibility_iterator 
GetPrevNode(wxMenuItemList::compatibility_iterator node
) const; 
 227     // get next node after the current one 
 228     wxMenuItemList::compatibility_iterator 
GetNextNode() const; 
 230     // get next node after the given one, wrapping if it's the last one 
 231     wxMenuItemList::compatibility_iterator 
GetNextNode(wxMenuItemList::compatibility_iterator node
) const; 
 237     // the menu node corresponding to the current item 
 238     wxMenuItemList::compatibility_iterator m_nodeCurrent
; 
 240     // do we currently have an opened submenu? 
 241     bool m_hasOpenSubMenu
; 
 243     DECLARE_EVENT_TABLE() 
 246 // ---------------------------------------------------------------------------- 
 247 // wxMenuKbdRedirector: an event handler which redirects kbd input to wxMenu 
 248 // ---------------------------------------------------------------------------- 
 250 class wxMenuKbdRedirector 
: public wxEvtHandler
 
 253     wxMenuKbdRedirector(wxMenu 
*menu
) { m_menu 
= menu
; } 
 255     virtual bool ProcessEvent(wxEvent
& event
) 
 257         if ( event
.GetEventType() == wxEVT_KEY_DOWN 
) 
 259             return m_menu
->ProcessKeyDown(((wxKeyEvent 
&)event
).GetKeyCode()); 
 265             return wxEvtHandler::ProcessEvent(event
); 
 273 // ---------------------------------------------------------------------------- 
 275 // ---------------------------------------------------------------------------- 
 277 IMPLEMENT_DYNAMIC_CLASS(wxMenu
, wxEvtHandler
) 
 278 IMPLEMENT_DYNAMIC_CLASS(wxMenuBar
, wxWindow
) 
 279 IMPLEMENT_DYNAMIC_CLASS(wxMenuItem
, wxObject
) 
 281 BEGIN_EVENT_TABLE(wxPopupMenuWindow
, wxPopupTransientWindow
) 
 282     EVT_KEY_DOWN(wxPopupMenuWindow::OnKeyDown
) 
 284     EVT_LEFT_UP(wxPopupMenuWindow::OnLeftUp
) 
 285     EVT_MOTION(wxPopupMenuWindow::OnMouseMove
) 
 286     EVT_LEAVE_WINDOW(wxPopupMenuWindow::OnMouseLeave
) 
 288     EVT_IDLE(wxPopupMenuWindow::OnIdle
) 
 292 BEGIN_EVENT_TABLE(wxMenuBar
, wxMenuBarBase
) 
 293     EVT_KILL_FOCUS(wxMenuBar::OnKillFocus
) 
 295     EVT_KEY_DOWN(wxMenuBar::OnKeyDown
) 
 297     EVT_LEFT_DOWN(wxMenuBar::OnLeftDown
) 
 298     EVT_MOTION(wxMenuBar::OnMouseMove
) 
 301 // ============================================================================ 
 303 // ============================================================================ 
 305 // ---------------------------------------------------------------------------- 
 307 // ---------------------------------------------------------------------------- 
 309 wxPopupMenuWindow::wxPopupMenuWindow(wxWindow 
*parent
, wxMenu 
*menu
) 
 312     m_hasOpenSubMenu 
= false; 
 316     (void)Create(parent
, wxBORDER_RAISED
); 
 318     SetCursor(wxCURSOR_ARROW
); 
 321 wxPopupMenuWindow::~wxPopupMenuWindow() 
 323     // When m_popupMenu in wxMenu is deleted because it 
 324     // is a child of an old menu bar being deleted (note: it does 
 325     // not get destroyed by the wxMenu destructor, but 
 326     // by DestroyChildren()), m_popupMenu should be reset to NULL. 
 328     m_menu
->m_popupMenu 
= NULL
; 
 331 // ---------------------------------------------------------------------------- 
 332 // wxPopupMenuWindow current item/node handling 
 333 // ---------------------------------------------------------------------------- 
 335 void wxPopupMenuWindow::ResetCurrent() 
 338     SetCurrent(wxMenuItemList::compatibility_iterator()); 
 340     SetCurrent((wxwxMenuItemListNode 
*)NULL
); 
 344 void wxPopupMenuWindow::SetCurrent(wxMenuItemList::compatibility_iterator node
) 
 346     m_nodeCurrent 
= node
; 
 349 void wxPopupMenuWindow::ChangeCurrent(wxMenuItemList::compatibility_iterator node
) 
 351     if ( node 
!= m_nodeCurrent 
) 
 353         wxMenuItemList::compatibility_iterator nodeOldCurrent 
= m_nodeCurrent
; 
 355         m_nodeCurrent 
= node
; 
 357         if ( nodeOldCurrent 
) 
 359             wxMenuItem 
*item 
= nodeOldCurrent
->GetData(); 
 360             wxCHECK_RET( item
, _T("no current item?") ); 
 362             // if it was the currently opened menu, close it 
 363             if ( item
->IsSubMenu() && item
->GetSubMenu()->IsShown() ) 
 365                 item
->GetSubMenu()->Dismiss(); 
 366                 OnSubmenuDismiss( false ); 
 373             RefreshItem(m_nodeCurrent
->GetData()); 
 377 wxMenuItemList::compatibility_iterator 
wxPopupMenuWindow::GetPrevNode() const 
 379     // return the last node if there had been no previously selected one 
 380     return m_nodeCurrent 
? GetPrevNode(m_nodeCurrent
) 
 381                          : m_menu
->GetMenuItems().GetLast(); 
 384 wxMenuItemList::compatibility_iterator
 
 385 wxPopupMenuWindow::GetPrevNode(wxMenuItemList::compatibility_iterator node
) const 
 389         node 
= node
->GetPrevious(); 
 392             node 
= m_menu
->GetMenuItems().GetLast(); 
 395     //else: the menu is empty 
 400 wxMenuItemList::compatibility_iterator 
wxPopupMenuWindow::GetNextNode() const 
 402     // return the first node if there had been no previously selected one 
 403     return m_nodeCurrent 
? GetNextNode(m_nodeCurrent
) 
 404                          : m_menu
->GetMenuItems().GetFirst(); 
 407 wxMenuItemList::compatibility_iterator
 
 408 wxPopupMenuWindow::GetNextNode(wxMenuItemList::compatibility_iterator node
) const 
 412         node 
= node
->GetNext(); 
 415             node 
= m_menu
->GetMenuItems().GetFirst(); 
 418     //else: the menu is empty 
 423 // ---------------------------------------------------------------------------- 
 424 // wxPopupMenuWindow popup/dismiss 
 425 // ---------------------------------------------------------------------------- 
 427 void wxPopupMenuWindow::Popup(wxWindow 
*focus
) 
 429     // check that the current item had been properly reset before 
 430     wxASSERT_MSG( !m_nodeCurrent 
|| 
 431                   m_nodeCurrent 
== m_menu
->GetMenuItems().GetFirst(), 
 432                   _T("menu current item preselected incorrectly") ); 
 434     wxPopupTransientWindow::Popup(focus
); 
 436     // the base class no-longer captures the mouse automatically when Popup 
 437     // is called, so do it here to allow the menu tracking to work 
 442     // ensure that this window is really on top of everything: without using 
 443     // SetWindowPos() it can be covered by its parent menu which is not 
 444     // really what we want 
 445     wxMenu 
*menuParent 
= m_menu
->GetParent(); 
 448         wxPopupMenuWindow 
*win 
= menuParent
->m_popupMenu
; 
 450         // if we're shown, the parent menu must be also shown 
 451         wxCHECK_RET( win
, _T("parent menu is not shown?") ); 
 453         if ( !::SetWindowPos(GetHwndOf(win
), GetHwnd(), 
 455                              SWP_NOMOVE 
| SWP_NOSIZE 
| SWP_NOREDRAW
) ) 
 457             wxLogLastError(_T("SetWindowPos(HWND_TOP)")); 
 465 void wxPopupMenuWindow::Dismiss() 
 467     if ( HasOpenSubmenu() ) 
 469         wxMenuItem 
*item 
= GetCurrentItem(); 
 470         wxCHECK_RET( item 
&& item
->IsSubMenu(), _T("where is our open submenu?") ); 
 472         wxPopupMenuWindow 
*win 
= item
->GetSubMenu()->m_popupMenu
; 
 473         wxCHECK_RET( win
, _T("opened submenu is not opened?") ); 
 476         OnSubmenuDismiss( false ); 
 479     wxPopupTransientWindow::Dismiss(); 
 484 void wxPopupMenuWindow::OnDismiss() 
 486     // when we are dismissed because the user clicked elsewhere or we lost 
 487     // focus in any other way, hide the parent menu as well 
 491 void wxPopupMenuWindow::OnSubmenuDismiss(bool WXUNUSED(dismissParent
)) 
 493     m_hasOpenSubMenu 
= false; 
 496 void wxPopupMenuWindow::HandleDismiss(bool dismissParent
) 
 498     m_menu
->OnDismiss(dismissParent
); 
 501 void wxPopupMenuWindow::DismissAndNotify() 
 507 // ---------------------------------------------------------------------------- 
 508 // wxPopupMenuWindow geometry 
 509 // ---------------------------------------------------------------------------- 
 511 wxMenuItemList::compatibility_iterator
 
 512 wxPopupMenuWindow::GetMenuItemFromPoint(const wxPoint
& pt
) const 
 514     // we only use the y coord normally, but still check x in case the point is 
 515     // outside the window completely 
 516     if ( wxWindow::HitTest(pt
) == wxHT_WINDOW_INSIDE 
) 
 519         for ( wxMenuItemList::compatibility_iterator node 
= m_menu
->GetMenuItems().GetFirst(); 
 521               node 
= node
->GetNext() ) 
 523             wxMenuItem 
*item 
= node
->GetData(); 
 524             y 
+= item
->GetHeight(); 
 534     return wxMenuItemList::compatibility_iterator(); 
 540 // ---------------------------------------------------------------------------- 
 541 // wxPopupMenuWindow drawing 
 542 // ---------------------------------------------------------------------------- 
 544 void wxPopupMenuWindow::RefreshItem(wxMenuItem 
*item
) 
 546     wxCHECK_RET( item
, _T("can't refresh NULL item") ); 
 548     wxASSERT_MSG( IsShown(), _T("can't refresh menu which is not shown") ); 
 550     // FIXME: -1 here because of SetLogicalOrigin(1, 1) in DoDraw() 
 551     RefreshRect(wxRect(0, item
->GetPosition() - 1, 
 552                 m_menu
->GetGeometryInfo().GetSize().x
, item
->GetHeight())); 
 555 void wxPopupMenuWindow::DoDraw(wxControlRenderer 
*renderer
) 
 557     // no clipping so far - do we need it? I don't think so as the menu is 
 558     // never partially covered as it is always on top of everything 
 560     wxDC
& dc 
= renderer
->GetDC(); 
 561     dc
.SetFont(wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT
)); 
 563     // FIXME: this should be done in the renderer, however when it is fixed 
 564     //        wxPopupMenuWindow::RefreshItem() should be changed too! 
 565     dc
.SetLogicalOrigin(1, 1); 
 567     wxRenderer 
*rend 
= renderer
->GetRenderer(); 
 570     const wxMenuGeometryInfo
& gi 
= m_menu
->GetGeometryInfo(); 
 571     for ( wxMenuItemList::compatibility_iterator node 
= m_menu
->GetMenuItems().GetFirst(); 
 573           node 
= node
->GetNext() ) 
 575         wxMenuItem 
*item 
= node
->GetData(); 
 577         if ( item
->IsSeparator() ) 
 579             rend
->DrawMenuSeparator(dc
, y
, gi
); 
 581         else // not a separator 
 584             if ( item
->IsCheckable() ) 
 586                 flags 
|= wxCONTROL_CHECKABLE
; 
 588                 if ( item
->IsChecked() ) 
 590                     flags 
|= wxCONTROL_CHECKED
; 
 594             if ( !item
->IsEnabled() ) 
 595                 flags 
|= wxCONTROL_DISABLED
; 
 597             if ( item
->IsSubMenu() ) 
 598                 flags 
|= wxCONTROL_ISSUBMENU
; 
 600             if ( item 
== GetCurrentItem() ) 
 601                 flags 
|= wxCONTROL_SELECTED
; 
 605             if ( !item
->IsEnabled() ) 
 607                 bmp 
= item
->GetDisabledBitmap(); 
 612                 // strangely enough, for unchecked item we use the 
 613                 // "checked" bitmap because this is the default one - this 
 614                 // explains this strange boolean expression 
 615                 bmp 
= item
->GetBitmap(!item
->IsCheckable() || item
->IsChecked()); 
 624                      item
->GetAccelString(), 
 627                      item
->GetAccelIndex() 
 631         y 
+= item
->GetHeight(); 
 635 // ---------------------------------------------------------------------------- 
 636 // wxPopupMenuWindow actions 
 637 // ---------------------------------------------------------------------------- 
 639 void wxPopupMenuWindow::ClickItem(wxMenuItem 
*item
) 
 641     wxCHECK_RET( item
, _T("can't click NULL item") ); 
 643     wxASSERT_MSG( !item
->IsSeparator() && !item
->IsSubMenu(), 
 644                   _T("can't click this item") ); 
 646     wxMenu
* menu 
= m_menu
; 
 651     menu
->ClickItem(item
); 
 654 void wxPopupMenuWindow::OpenSubmenu(wxMenuItem 
*item
, InputMethod how
) 
 656     wxCHECK_RET( item
, _T("can't open NULL submenu") ); 
 658     wxMenu 
*submenu 
= item
->GetSubMenu(); 
 659     wxCHECK_RET( submenu
, _T("can only open submenus!") ); 
 661     // FIXME: should take into account the border width 
 662     submenu
->Popup(ClientToScreen(wxPoint(0, item
->GetPosition())), 
 663                    wxSize(m_menu
->GetGeometryInfo().GetSize().x
, 0), 
 664                    how 
== WithKeyboard 
/* preselect first item then */); 
 666     m_hasOpenSubMenu 
= true; 
 669 bool wxPopupMenuWindow::ActivateItem(wxMenuItem 
*item
, InputMethod how
) 
 671     // don't activate disabled items 
 672     if ( !item 
|| !item
->IsEnabled() ) 
 677     // normal menu items generate commands, submenus can be opened and 
 678     // the separators don't do anything 
 679     if ( item
->IsSubMenu() ) 
 681         OpenSubmenu(item
, how
); 
 683     else if ( !item
->IsSeparator() ) 
 687     else // separator, can't activate 
 695 // ---------------------------------------------------------------------------- 
 696 // wxPopupMenuWindow input handling 
 697 // ---------------------------------------------------------------------------- 
 699 bool wxPopupMenuWindow::ProcessLeftDown(wxMouseEvent
& event
) 
 701     // wxPopupWindowHandler dismisses the window when the mouse is clicked 
 702     // outside it which is usually just fine, but there is one case when we 
 703     // don't want to do it: if the mouse was clicked on the parent submenu item 
 704     // which opens this menu, so check for it 
 706     wxPoint pos 
= event
.GetPosition(); 
 707     if ( HitTest(pos
.x
, pos
.y
) == wxHT_WINDOW_OUTSIDE 
) 
 709         wxMenu 
*menu 
= m_menu
->GetParent(); 
 712             wxPopupMenuWindow 
*win 
= menu
->m_popupMenu
; 
 714             wxCHECK_MSG( win
, false, _T("parent menu not shown?") ); 
 716             pos 
= ClientToScreen(pos
); 
 717             if ( win
->GetMenuItemFromPoint(win
->ScreenToClient(pos
)) ) 
 722             //else: it is outside the parent menu as well, do dismiss this one 
 729 void wxPopupMenuWindow::OnLeftUp(wxMouseEvent
& event
) 
 731     wxMenuItemList::compatibility_iterator node 
= GetMenuItemFromPoint(event
.GetPosition()); 
 734         ActivateItem(node
->GetData(), WithMouse
); 
 738 void wxPopupMenuWindow::OnMouseMove(wxMouseEvent
& event
) 
 740     const wxPoint pt 
= event
.GetPosition(); 
 742     // we need to ignore extra mouse events: example when this happens is when 
 743     // the mouse is on the menu and we open a submenu from keyboard - Windows 
 744     // then sends us a dummy mouse move event, we (correctly) determine that it 
 745     // happens in the parent menu and so immediately close the just opened 
 748     static wxPoint s_ptLast
; 
 749     wxPoint ptCur 
= ClientToScreen(pt
); 
 750     if ( ptCur 
== s_ptLast 
) 
 758     ProcessMouseMove(pt
); 
 763 void wxPopupMenuWindow::ProcessMouseMove(const wxPoint
& pt
) 
 765     wxMenuItemList::compatibility_iterator node 
= GetMenuItemFromPoint(pt
); 
 767     // don't reset current to NULL here, we only do it when the mouse leaves 
 768     // the window (see below) 
 771         if ( node 
!= m_nodeCurrent 
) 
 775             wxMenuItem 
*item 
= GetCurrentItem(); 
 778                 OpenSubmenu(item
, WithMouse
); 
 781         //else: same item, nothing to do 
 783     else // not on an item 
 785         // the last open submenu forwards the mouse move messages to its 
 786         // parent, so if the mouse moves to another item of the parent menu, 
 787         // this menu is closed and this other item is selected - in the similar 
 788         // manner, the top menu forwards the mouse moves to the menubar which 
 789         // allows to select another top level menu by just moving the mouse 
 791         // we need to translate our client coords to the client coords of the 
 792         // window we forward this event to 
 793         wxPoint ptScreen 
= ClientToScreen(pt
); 
 795         // if the mouse is outside this menu, let the parent one to 
 797         wxMenu 
*menuParent 
= m_menu
->GetParent(); 
 800             wxPopupMenuWindow 
*win 
= menuParent
->m_popupMenu
; 
 802             // if we're shown, the parent menu must be also shown 
 803             wxCHECK_RET( win
, _T("parent menu is not shown?") ); 
 805             win
->ProcessMouseMove(win
->ScreenToClient(ptScreen
)); 
 807         else // no parent menu 
 809             wxMenuBar 
*menubar 
= m_menu
->GetMenuBar(); 
 812                 if ( menubar
->ProcessMouseEvent( 
 813                             menubar
->ScreenToClient(ptScreen
)) ) 
 815                     // menubar has closed this menu and opened another one, probably 
 820         //else: top level popup menu, no other processing to do 
 824 void wxPopupMenuWindow::OnMouseLeave(wxMouseEvent
& event
) 
 826     // due to the artefact of mouse events generation under MSW, we actually 
 827     // may get the mouse leave event after the menu had been already dismissed 
 828     // and calling ChangeCurrent() would then assert, so don't do it 
 831         // we shouldn't change the current them if our submenu is opened and 
 832         // mouse moved there, in this case the submenu is responsable for 
 835         if ( HasOpenSubmenu() ) 
 837             wxMenuItem 
*item 
= GetCurrentItem(); 
 838             wxCHECK_RET( CanOpen(item
), _T("where is our open submenu?") ); 
 840             wxPopupMenuWindow 
*win 
= item
->GetSubMenu()->m_popupMenu
; 
 841             wxCHECK_RET( win
, _T("submenu is opened but not shown?") ); 
 843             // only handle this event if the mouse is not inside the submenu 
 844             wxPoint pt 
= ClientToScreen(event
.GetPosition()); 
 846                 win
->HitTest(win
->ScreenToClient(pt
)) == wxHT_WINDOW_OUTSIDE
; 
 850             // this menu is the last opened 
 857             ChangeCurrent(wxMenuItemList::compatibility_iterator()); 
 867 void wxPopupMenuWindow::OnKeyDown(wxKeyEvent
& event
) 
 869     wxMenuBar 
*menubar 
= m_menu
->GetMenuBar(); 
 873         menubar
->ProcessEvent(event
); 
 875     else if ( !ProcessKeyDown(event
.GetKeyCode()) ) 
 881 bool wxPopupMenuWindow::ProcessKeyDown(int key
) 
 883     wxMenuItem 
*item 
= GetCurrentItem(); 
 885     // first let the opened submenu to have it (no test for IsEnabled() here, 
 886     // the keys navigate even in a disabled submenu if we had somehow managed 
 887     // to open it inspit of this) 
 888     if ( HasOpenSubmenu() ) 
 890         wxCHECK_MSG( CanOpen(item
), false, 
 891                      _T("has open submenu but another item selected?") ); 
 893         if ( item
->GetSubMenu()->ProcessKeyDown(key
) ) 
 897     bool processed 
= true; 
 899     // handle the up/down arrows, home, end, esc and return here, pass the 
 900     // left/right arrows to the menu bar except when the right arrow can be 
 901     // used to open a submenu 
 905             // if we're not a top level menu, close us, else leave this to the 
 907             if ( !m_menu
->GetParent() ) 
 916             // close just this menu 
 918             HandleDismiss(false); 
 922             processed 
= ActivateItem(item
); 
 926             ChangeCurrent(m_menu
->GetMenuItems().GetFirst()); 
 930             ChangeCurrent(m_menu
->GetMenuItems().GetLast()); 
 936                 bool up 
= key 
== WXK_UP
; 
 938                 wxMenuItemList::compatibility_iterator nodeStart 
= up 
? GetPrevNode() 
 941                 while ( node 
&& node
->GetData()->IsSeparator() ) 
 943                     node 
= up 
? GetPrevNode(node
) : GetNextNode(node
); 
 945                     if ( node 
== nodeStart 
) 
 947                         // nothing but separators and disabled items in this 
 950                         node 
= wxMenuItemList::compatibility_iterator(); 
 969             // don't try to reopen an already opened menu 
 970             if ( !HasOpenSubmenu() && CanOpen(item
) ) 
 981             // look for the menu item starting with this letter 
 982             if ( wxIsalnum((wxChar
)key
) ) 
 984                 // we want to start from the item after this one because 
 985                 // if we're already on the item with the given accel we want to 
 986                 // go to the next one, not to stay in place 
 987                 wxMenuItemList::compatibility_iterator nodeStart 
= GetNextNode(); 
 989                 // do we have more than one item with this accel? 
 990                 bool notUnique 
= false; 
 992                 // translate everything to lower case before comparing 
 993                 wxChar chAccel 
= (wxChar
)wxTolower(key
); 
 995                 // loop through all items searching for the item with this 
 997                 wxMenuItemList::compatibility_iterator node 
= nodeStart
, 
 999                                                        nodeFound 
= wxMenuItemList::compatibility_iterator(); 
1005                     item 
= node
->GetData(); 
1007                     int idxAccel 
= item
->GetAccelIndex(); 
1008                     if ( idxAccel 
!= -1 && 
1009                          wxTolower(item
->GetLabel()[(size_t)idxAccel
]) 
1012                         // ok, found an item with this accel 
1015                             // store it but continue searching as we need to 
1016                             // know if it's the only item with this accel or if 
1020                         else // we already had found such item 
1024                             // no need to continue further, we won't find 
1025                             // anything we don't already know 
1030                     // we want to iterate over all items wrapping around if 
1032                     node 
= GetNextNode(node
); 
1033                     if ( node 
== nodeStart 
) 
1035                         // we've seen all nodes 
1042                     item 
= nodeFound
->GetData(); 
1044                     // go to this item anyhow 
1045                     ChangeCurrent(nodeFound
); 
1047                     if ( !notUnique 
&& item
->IsEnabled() ) 
1049                         // unique item with this accel - activate it 
1050                         processed 
= ActivateItem(item
); 
1052                     //else: just select it but don't activate as the user might 
1053                     //      have wanted to activate another item 
1055                     // skip "processed = false" below 
1066 // ---------------------------------------------------------------------------- 
1068 // ---------------------------------------------------------------------------- 
1076     m_startRadioGroup 
= -1; 
1085 // ---------------------------------------------------------------------------- 
1086 // wxMenu and wxMenuGeometryInfo 
1087 // ---------------------------------------------------------------------------- 
1089 wxMenuGeometryInfo::~wxMenuGeometryInfo() 
1093 const wxMenuGeometryInfo
& wxMenu::GetGeometryInfo() const 
1099             wxConstCast(this, wxMenu
)->m_geometry 
= 
1100                 m_popupMenu
->GetRenderer()->GetMenuGeometry(m_popupMenu
, *this); 
1104             wxFAIL_MSG( _T("can't get geometry without window") ); 
1111 void wxMenu::InvalidateGeometryInfo() 
1120 // ---------------------------------------------------------------------------- 
1121 // wxMenu adding/removing items 
1122 // ---------------------------------------------------------------------------- 
1124 void wxMenu::OnItemAdded(wxMenuItem 
*item
) 
1126     InvalidateGeometryInfo(); 
1130 #endif // wxUSE_ACCEL 
1132     // the submenus of a popup menu should have the same invoking window as it 
1134     if ( m_invokingWindow 
&& item
->IsSubMenu() ) 
1136         item
->GetSubMenu()->SetInvokingWindow(m_invokingWindow
); 
1140 void wxMenu::EndRadioGroup() 
1142     // we're not inside a radio group any longer 
1143     m_startRadioGroup 
= -1; 
1146 wxMenuItem
* wxMenu::DoAppend(wxMenuItem 
*item
) 
1148     if ( item
->GetKind() == wxITEM_RADIO 
) 
1150         int count 
= GetMenuItemCount(); 
1152         if ( m_startRadioGroup 
== -1 ) 
1154             // start a new radio group 
1155             m_startRadioGroup 
= count
; 
1157             // for now it has just one element 
1158             item
->SetAsRadioGroupStart(); 
1159             item
->SetRadioGroupEnd(m_startRadioGroup
); 
1161         else // extend the current radio group 
1163             // we need to update its end item 
1164             item
->SetRadioGroupStart(m_startRadioGroup
); 
1165             wxMenuItemList::compatibility_iterator node 
= GetMenuItems().Item(m_startRadioGroup
); 
1169                 node
->GetData()->SetRadioGroupEnd(count
); 
1173                 wxFAIL_MSG( _T("where is the radio group start item?") ); 
1177     else // not a radio item 
1182     if ( !wxMenuBase::DoAppend(item
) ) 
1190 wxMenuItem
* wxMenu::DoInsert(size_t pos
, wxMenuItem 
*item
) 
1192     if ( !wxMenuBase::DoInsert(pos
, item
) ) 
1200 wxMenuItem 
*wxMenu::DoRemove(wxMenuItem 
*item
) 
1202     wxMenuItem 
*itemOld 
= wxMenuBase::DoRemove(item
); 
1206         InvalidateGeometryInfo(); 
1209         RemoveAccelFor(item
); 
1210 #endif // wxUSE_ACCEL 
1216 // ---------------------------------------------------------------------------- 
1217 // wxMenu attaching/detaching 
1218 // ---------------------------------------------------------------------------- 
1220 void wxMenu::Attach(wxMenuBarBase 
*menubar
) 
1222     wxMenuBase::Attach(menubar
); 
1224     wxCHECK_RET( m_menuBar
, _T("menubar can't be NULL after attaching") ); 
1226     // unfortunately, we can't use m_menuBar->GetEventHandler() here because, 
1227     // if the menubar is currently showing a menu, its event handler is a 
1228     // temporary one installed by wxPopupWindow and so will disappear soon any 
1229     // any attempts to use it from the newly attached menu would result in a 
1232     // so we use the menubar itself, even if it's a pity as it means we can't 
1233     // redirect all menu events by changing the menubar handler (FIXME) 
1234     SetNextHandler(m_menuBar
); 
1237 void wxMenu::Detach() 
1239     wxMenuBase::Detach(); 
1242 // ---------------------------------------------------------------------------- 
1243 // wxMenu misc functions 
1244 // ---------------------------------------------------------------------------- 
1246 wxWindow 
*wxMenu::GetRootWindow() const 
1250         // simple case - a normal menu attached to the menubar 
1251         return GetMenuBar(); 
1254     // we're a popup menu but the trouble is that only the top level popup menu 
1255     // has a pointer to the invoking window, so we must walk up the menu chain 
1257     wxWindow 
*win 
= GetInvokingWindow(); 
1260         // we already have it 
1264     wxMenu 
*menu 
= GetParent(); 
1267         // We are a submenu of a menu of a menubar 
1268         if (menu
->GetMenuBar()) 
1269            return menu
->GetMenuBar(); 
1271         win 
= menu
->GetInvokingWindow(); 
1275         menu 
= menu
->GetParent(); 
1278     // we're probably going to crash in the caller anyhow, but try to detect 
1279     // this error as soon as possible 
1280     wxASSERT_MSG( win
, _T("menu without any associated window?") ); 
1282     // also remember it in this menu so that we don't have to search for it the 
1284     wxConstCast(this, wxMenu
)->m_invokingWindow 
= win
; 
1289 wxRenderer 
*wxMenu::GetRenderer() const 
1291     // we're going to crash without renderer! 
1292     wxCHECK_MSG( m_popupMenu
, NULL
, _T("neither popup nor menubar menu?") ); 
1294     return m_popupMenu
->GetRenderer(); 
1297 void wxMenu::RefreshItem(wxMenuItem 
*item
) 
1299     // the item geometry changed, so our might have changed as well 
1300     InvalidateGeometryInfo(); 
1304         // this would be a bug in IsShown() 
1305         wxCHECK_RET( m_popupMenu
, _T("must have popup window if shown!") ); 
1307         // recalc geometry to update the item height and such 
1308         (void)GetGeometryInfo(); 
1310         m_popupMenu
->RefreshItem(item
); 
1314 // ---------------------------------------------------------------------------- 
1315 // wxMenu showing and hiding 
1316 // ---------------------------------------------------------------------------- 
1318 bool wxMenu::IsShown() const 
1320     return m_popupMenu 
&& m_popupMenu
->IsShown(); 
1323 void wxMenu::OnDismiss(bool dismissParent
) 
1327         // always notify the parent about submenu disappearance 
1328         wxPopupMenuWindow 
*win 
= m_menuParent
->m_popupMenu
; 
1331             win
->OnSubmenuDismiss( true ); 
1335             wxFAIL_MSG( _T("parent menu not shown?") ); 
1338         // and if we dismiss everything, propagate to parent 
1339         if ( dismissParent 
) 
1341             // dismissParent is recursive 
1342             m_menuParent
->Dismiss(); 
1343             m_menuParent
->OnDismiss(true); 
1346     else // no parent menu 
1348         // notify the menu bar if we're a top level menu 
1351             m_menuBar
->OnDismissMenu(dismissParent
); 
1355             wxCHECK_RET( m_invokingWindow
, _T("what kind of menu is this?") ); 
1357             m_invokingWindow
->DismissPopupMenu(); 
1359             // Why reset it here? We need it for sending the event to... 
1360             // SetInvokingWindow(NULL); 
1365 void wxMenu::Popup(const wxPoint
& pos
, const wxSize
& size
, bool selectFirst
) 
1367     // create the popup window if not done yet 
1370         m_popupMenu 
= new wxPopupMenuWindow(GetRootWindow(), this); 
1373     // select the first item unless disabled 
1376         m_popupMenu
->SelectFirst(); 
1379     // the geometry might have changed since the last time we were shown, so 
1381     m_popupMenu
->SetClientSize(GetGeometryInfo().GetSize()); 
1383     // position it as specified 
1384     m_popupMenu
->Position(pos
, size
); 
1386     // the menu can't have the focus itself (it is a Windows limitation), so 
1387     // always keep the focus at the originating window 
1388     wxWindow 
*focus 
= GetRootWindow(); 
1390     wxASSERT_MSG( focus
, _T("no window to keep focus on?") ); 
1393     m_popupMenu
->Popup(focus
); 
1396 void wxMenu::Dismiss() 
1398     wxCHECK_RET( IsShown(), _T("can't dismiss hidden menu") ); 
1400     m_popupMenu
->Dismiss(); 
1403 // ---------------------------------------------------------------------------- 
1404 // wxMenu event processing 
1405 // ---------------------------------------------------------------------------- 
1407 bool wxMenu::ProcessKeyDown(int key
) 
1409     wxCHECK_MSG( m_popupMenu
, false, 
1410                  _T("can't process key events if not shown") ); 
1412     return m_popupMenu
->ProcessKeyDown(key
); 
1415 bool wxMenu::ClickItem(wxMenuItem 
*item
) 
1418     if ( item
->IsCheckable() ) 
1420         // update the item state 
1421         isChecked 
= !item
->IsChecked(); 
1423         item
->Check(isChecked 
!= 0); 
1431     return SendEvent(item
->GetId(), isChecked
); 
1434 // ---------------------------------------------------------------------------- 
1435 // wxMenu accel support 
1436 // ---------------------------------------------------------------------------- 
1440 bool wxMenu::ProcessAccelEvent(const wxKeyEvent
& event
) 
1442     // do we have an item for this accel? 
1443     wxMenuItem 
*item 
= m_accelTable
.GetMenuItem(event
); 
1444     if ( item 
&& item
->IsEnabled() ) 
1446         return ClickItem(item
); 
1450     for ( wxMenuItemList::compatibility_iterator node 
= GetMenuItems().GetFirst(); 
1452           node 
= node
->GetNext() ) 
1454         const wxMenuItem 
*item 
= node
->GetData(); 
1455         if ( item
->IsSubMenu() && item
->IsEnabled() ) 
1458             if ( item
->GetSubMenu()->ProcessAccelEvent(event
) ) 
1468 void wxMenu::AddAccelFor(wxMenuItem 
*item
) 
1470     wxAcceleratorEntry 
*accel 
= item
->GetAccel(); 
1473         accel
->SetMenuItem(item
); 
1475         m_accelTable
.Add(*accel
); 
1481 void wxMenu::RemoveAccelFor(wxMenuItem 
*item
) 
1483     wxAcceleratorEntry 
*accel 
= item
->GetAccel(); 
1486         m_accelTable
.Remove(*accel
); 
1492 #endif // wxUSE_ACCEL 
1494 // ---------------------------------------------------------------------------- 
1495 // wxMenuItem construction 
1496 // ---------------------------------------------------------------------------- 
1498 wxMenuItem::wxMenuItem(wxMenu 
*parentMenu
, 
1500                        const wxString
& text
, 
1501                        const wxString
& help
, 
1504           : wxMenuItemBase(parentMenu
, id
, text
, help
, kind
, subMenu
) 
1507     m_height 
= wxDefaultCoord
; 
1509     m_radioGroup
.start 
= -1; 
1510     m_isRadioGroupStart 
= false; 
1512     m_bmpDisabled 
= wxNullBitmap
; 
1517 wxMenuItem::~wxMenuItem() 
1521 // ---------------------------------------------------------------------------- 
1522 // wxMenuItemBase methods implemented here 
1523 // ---------------------------------------------------------------------------- 
1526 wxMenuItem 
*wxMenuItemBase::New(wxMenu 
*parentMenu
, 
1528                                 const wxString
& name
, 
1529                                 const wxString
& help
, 
1533     return new wxMenuItem(parentMenu
, id
, name
, help
, kind
, subMenu
); 
1537 wxString 
wxMenuItemBase::GetLabelFromText(const wxString
& text
) 
1539     return wxStripMenuCodes(text
); 
1542 // ---------------------------------------------------------------------------- 
1543 // wxMenuItem operations 
1544 // ---------------------------------------------------------------------------- 
1546 void wxMenuItem::NotifyMenu() 
1548     m_parentMenu
->RefreshItem(this); 
1551 void wxMenuItem::UpdateAccelInfo() 
1553     m_indexAccel 
= wxControl::FindAccelIndex(m_text
); 
1555     // will be empty if the text contains no TABs - ok 
1556     m_strAccel 
= m_text
.AfterFirst(_T('\t')); 
1559 void wxMenuItem::SetText(const wxString
& text
) 
1561     if ( text 
!= m_text 
) 
1563         // first call the base class version to change m_text 
1564         wxMenuItemBase::SetText(text
); 
1572 void wxMenuItem::SetCheckable(bool checkable
) 
1574     if ( checkable 
!= IsCheckable() ) 
1576         wxMenuItemBase::SetCheckable(checkable
); 
1582 void wxMenuItem::SetBitmaps(const wxBitmap
& bmpChecked
, 
1583                             const wxBitmap
& bmpUnchecked
) 
1585     m_bmpChecked 
= bmpChecked
; 
1586     m_bmpUnchecked 
= bmpUnchecked
; 
1591 void wxMenuItem::Enable(bool enable
) 
1593     if ( enable 
!= m_isEnabled 
) 
1595         wxMenuItemBase::Enable(enable
); 
1601 void wxMenuItem::Check(bool check
) 
1603     wxCHECK_RET( IsCheckable(), wxT("only checkable items may be checked") ); 
1605     if ( m_isChecked 
== check 
) 
1608     if ( GetKind() == wxITEM_RADIO 
) 
1610         // it doesn't make sense to uncheck a radio item - what would this do? 
1614         // get the index of this item in the menu 
1615         const wxMenuItemList
& items 
= m_parentMenu
->GetMenuItems(); 
1616         int pos 
= items
.IndexOf(this); 
1617         wxCHECK_RET( pos 
!= wxNOT_FOUND
, 
1618                      _T("menuitem not found in the menu items list?") ); 
1620         // get the radio group range 
1624         if ( m_isRadioGroupStart 
) 
1626             // we already have all information we need 
1628             end 
= m_radioGroup
.end
; 
1630         else // next radio group item 
1632             // get the radio group end from the start item 
1633             start 
= m_radioGroup
.start
; 
1634             end 
= items
.Item(start
)->GetData()->m_radioGroup
.end
; 
1637         // also uncheck all the other items in this radio group 
1638         wxMenuItemList::compatibility_iterator node 
= items
.Item(start
); 
1639         for ( int n 
= start
; n 
<= end 
&& node
; n
++ ) 
1643                 node
->GetData()->m_isChecked 
= false; 
1645             node 
= node
->GetNext(); 
1649     wxMenuItemBase::Check(check
); 
1654 // radio group stuff 
1655 // ----------------- 
1657 void wxMenuItem::SetAsRadioGroupStart() 
1659     m_isRadioGroupStart 
= true; 
1662 void wxMenuItem::SetRadioGroupStart(int start
) 
1664     wxASSERT_MSG( !m_isRadioGroupStart
, 
1665                   _T("should only be called for the next radio items") ); 
1667     m_radioGroup
.start 
= start
; 
1670 void wxMenuItem::SetRadioGroupEnd(int end
) 
1672     wxASSERT_MSG( m_isRadioGroupStart
, 
1673                   _T("should only be called for the first radio item") ); 
1675     m_radioGroup
.end 
= end
; 
1678 // ---------------------------------------------------------------------------- 
1679 // wxMenuBar creation 
1680 // ---------------------------------------------------------------------------- 
1682 void wxMenuBar::Init() 
1690     m_shouldShowMenu 
= false; 
1693 wxMenuBar::wxMenuBar(size_t n
, wxMenu 
*menus
[], const wxString titles
[], long WXUNUSED(style
)) 
1697     for (size_t i 
= 0; i 
< n
; ++i 
) 
1698         Append(menus
[i
], titles
[i
]); 
1701 void wxMenuBar::Attach(wxFrame 
*frame
) 
1703     // maybe you really wanted to call Detach()? 
1704     wxCHECK_RET( frame
, _T("wxMenuBar::Attach(NULL) called") ); 
1706     wxMenuBarBase::Attach(frame
); 
1710         // reparent if necessary 
1711         if ( m_frameLast 
!= frame 
) 
1716         // show it back - was hidden by Detach() 
1719     else // not created yet, do it now 
1721         // we have no way to return the error from here anyhow :-( 
1722         (void)Create(frame
, wxID_ANY
); 
1724         SetCursor(wxCURSOR_ARROW
); 
1726         SetFont(wxSystemSettings::GetFont(wxSYS_SYSTEM_FONT
)); 
1728         // calculate and set our height (it won't be changed any more) 
1729         SetSize(wxDefaultCoord
, GetBestSize().y
); 
1732     // remember the last frame which had us to avoid unnecessarily reparenting 
1734     m_frameLast 
= frame
; 
1737 void wxMenuBar::Detach() 
1739     // don't delete the window because we may be reattached later, just hide it 
1745     wxMenuBarBase::Detach(); 
1748 wxMenuBar::~wxMenuBar() 
1752 // ---------------------------------------------------------------------------- 
1753 // wxMenuBar adding/removing items 
1754 // ---------------------------------------------------------------------------- 
1756 bool wxMenuBar::Append(wxMenu 
*menu
, const wxString
& title
) 
1758     return Insert(GetCount(), menu
, title
); 
1761 bool wxMenuBar::Insert(size_t pos
, wxMenu 
*menu
, const wxString
& title
) 
1763     if ( !wxMenuBarBase::Insert(pos
, menu
, title
) ) 
1766     wxMenuInfo 
*info 
= new wxMenuInfo(title
); 
1767     m_menuInfos
.Insert(info
, pos
); 
1769     RefreshAllItemsAfter(pos
); 
1774 wxMenu 
*wxMenuBar::Replace(size_t pos
, wxMenu 
*menu
, const wxString
& title
) 
1776     wxMenu 
*menuOld 
= wxMenuBarBase::Replace(pos
, menu
, title
); 
1780         wxMenuInfo
& info 
= m_menuInfos
[pos
]; 
1782         info
.SetLabel(title
); 
1784         // even if the old menu was disabled, the new one is not any more 
1787         // even if we change only this one, the new label has different width, 
1788         // so we need to refresh everything beyond this item as well 
1789         RefreshAllItemsAfter(pos
); 
1795 wxMenu 
*wxMenuBar::Remove(size_t pos
) 
1797     wxMenu 
*menuOld 
= wxMenuBarBase::Remove(pos
); 
1801         m_menuInfos
.RemoveAt(pos
); 
1803         // this doesn't happen too often, so don't try to be too smart - just 
1804         // refresh everything 
1811 // ---------------------------------------------------------------------------- 
1812 // wxMenuBar top level menus access 
1813 // ---------------------------------------------------------------------------- 
1815 wxCoord 
wxMenuBar::GetItemWidth(size_t pos
) const 
1817     return m_menuInfos
[pos
].GetWidth(wxConstCast(this, wxMenuBar
)); 
1820 void wxMenuBar::EnableTop(size_t pos
, bool enable
) 
1822     wxCHECK_RET( pos 
< GetCount(), _T("invalid index in EnableTop") ); 
1824     if ( enable 
!= m_menuInfos
[pos
].IsEnabled() ) 
1826         m_menuInfos
[pos
].SetEnabled(enable
); 
1830     //else: nothing to do 
1833 bool wxMenuBar::IsEnabledTop(size_t pos
) const 
1835     wxCHECK_MSG( pos 
< GetCount(), false, _T("invalid index in IsEnabledTop") ); 
1837     return m_menuInfos
[pos
].IsEnabled(); 
1840 void wxMenuBar::SetLabelTop(size_t pos
, const wxString
& label
) 
1842     wxCHECK_RET( pos 
< GetCount(), _T("invalid index in EnableTop") ); 
1844     if ( label 
!= m_menuInfos
[pos
].GetLabel() ) 
1846         m_menuInfos
[pos
].SetLabel(label
); 
1850     //else: nothing to do 
1853 wxString 
wxMenuBar::GetLabelTop(size_t pos
) const 
1855     wxCHECK_MSG( pos 
< GetCount(), wxEmptyString
, _T("invalid index in GetLabelTop") ); 
1857     return m_menuInfos
[pos
].GetLabel(); 
1860 // ---------------------------------------------------------------------------- 
1861 // wxMenuBar drawing 
1862 // ---------------------------------------------------------------------------- 
1864 void wxMenuBar::RefreshAllItemsAfter(size_t pos
) 
1868         // no need to refresh if nothing is shown yet 
1872     wxRect rect 
= GetItemRect(pos
); 
1873     rect
.width 
= GetClientSize().x 
- rect
.x
; 
1877 void wxMenuBar::RefreshItem(size_t pos
) 
1879     wxCHECK_RET( pos 
!= (size_t)-1, 
1880                  _T("invalid item in wxMenuBar::RefreshItem") ); 
1884         // no need to refresh if nothing is shown yet 
1888     RefreshRect(GetItemRect(pos
)); 
1891 void wxMenuBar::DoDraw(wxControlRenderer 
*renderer
) 
1893     wxDC
& dc 
= renderer
->GetDC(); 
1894     dc
.SetFont(wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT
)); 
1896     // redraw only the items which must be redrawn 
1898     // we don't have to use GetUpdateClientRect() here because our client rect 
1899     // is the same as total one 
1900     wxRect rectUpdate 
= GetUpdateRegion().GetBox(); 
1902     int flagsMenubar 
= GetStateFlags(); 
1906     rect
.height 
= GetClientSize().y
; 
1909     size_t count 
= GetCount(); 
1910     for ( size_t n 
= 0; n 
< count
; n
++ ) 
1912         if ( x 
> rectUpdate
.GetRight() ) 
1914             // all remaining items are to the right of rectUpdate 
1919         rect
.width 
= GetItemWidth(n
); 
1921         if ( x 
< rectUpdate
.x 
) 
1923             // this item is still to the left of rectUpdate 
1927         int flags 
= flagsMenubar
; 
1928         if ( m_current 
!= -1 && n 
== (size_t)m_current 
) 
1930             flags 
|= wxCONTROL_SELECTED
; 
1933         if ( !IsEnabledTop(n
) ) 
1935             flags 
|= wxCONTROL_DISABLED
; 
1938         GetRenderer()->DrawMenuBarItem
 
1942                             m_menuInfos
[n
].GetLabel(), 
1944                             m_menuInfos
[n
].GetAccelIndex() 
1949 // ---------------------------------------------------------------------------- 
1950 // wxMenuBar geometry 
1951 // ---------------------------------------------------------------------------- 
1953 wxRect 
wxMenuBar::GetItemRect(size_t pos
) const 
1955     wxASSERT_MSG( pos 
< GetCount(), _T("invalid menu bar item index") ); 
1956     wxASSERT_MSG( IsCreated(), _T("can't call this method yet") ); 
1961     rect
.height 
= GetClientSize().y
; 
1963     for ( size_t n 
= 0; n 
< pos
; n
++ ) 
1965         rect
.x 
+= GetItemWidth(n
); 
1968     rect
.width 
= GetItemWidth(pos
); 
1973 wxSize 
wxMenuBar::DoGetBestClientSize() const 
1976     if ( GetMenuCount() > 0 ) 
1978         wxClientDC 
dc(wxConstCast(this, wxMenuBar
)); 
1979         dc
.SetFont(wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT
)); 
1980         dc
.GetTextExtent(GetLabelTop(0), &size
.x
, &size
.y
); 
1982         // adjust for the renderer we use 
1983         size 
= GetRenderer()->GetMenuBarItemSize(size
); 
1985     else // empty menubar 
1991     // the width is arbitrary, of course, for horizontal menubar 
1997 int wxMenuBar::GetMenuFromPoint(const wxPoint
& pos
) const 
1999     if ( pos
.x 
< 0 || pos
.y 
< 0 || pos
.y 
> GetClientSize().y 
) 
2004     size_t count 
= GetCount(); 
2005     for ( size_t item 
= 0; item 
< count
; item
++ ) 
2007         x 
+= GetItemWidth(item
); 
2015     // to the right of the last menu item 
2019 // ---------------------------------------------------------------------------- 
2020 // wxMenuBar menu operations 
2021 // ---------------------------------------------------------------------------- 
2023 void wxMenuBar::SelectMenu(size_t pos
) 
2026     wxLogTrace(_T("mousecapture"), _T("Capturing mouse from wxMenuBar::SelectMenu")); 
2032 void wxMenuBar::DoSelectMenu(size_t pos
) 
2034     wxCHECK_RET( pos 
< GetCount(), _T("invalid menu index in DoSelectMenu") ); 
2036     int posOld 
= m_current
; 
2042         // close the previous menu 
2043         if ( IsShowingMenu() ) 
2045             // restore m_shouldShowMenu flag after DismissMenu() which resets 
2047             bool old 
= m_shouldShowMenu
; 
2051             m_shouldShowMenu 
= old
; 
2054         RefreshItem((size_t)posOld
); 
2060 void wxMenuBar::PopupMenu(size_t pos
) 
2062     wxCHECK_RET( pos 
< GetCount(), _T("invalid menu index in PopupCurrentMenu") ); 
2069 // ---------------------------------------------------------------------------- 
2070 // wxMenuBar input handing 
2071 // ---------------------------------------------------------------------------- 
2074    Note that wxMenuBar doesn't use wxInputHandler but handles keyboard and 
2075    mouse in the same way under all platforms. This is because it doesn't derive 
2076    from wxControl (which works with input handlers) but directly from wxWindow. 
2078    Also, menu bar input handling is rather simple, so maybe it's not really 
2079    worth making it themeable - at least I've decided against doing it now as it 
2080    would merging the changes back into trunk more difficult. But it still could 
2081    be done later if really needed. 
2084 void wxMenuBar::OnKillFocus(wxFocusEvent
& event
) 
2086     if ( m_current 
!= -1 ) 
2088         RefreshItem((size_t)m_current
); 
2096 void wxMenuBar::OnLeftDown(wxMouseEvent
& event
) 
2104     else // we didn't have mouse capture, capture it now 
2106         m_current 
= GetMenuFromPoint(event
.GetPosition()); 
2107         if ( m_current 
== -1 ) 
2109             // unfortunately, we can't prevent wxMSW from giving us the focus, 
2110             // so we can only give it back 
2115             wxLogTrace(_T("mousecapture"), _T("Capturing mouse from wxMenuBar::OnLeftDown")); 
2118             // show it as selected 
2119             RefreshItem((size_t)m_current
); 
2122             PopupCurrentMenu(false /* don't select first item - as Windows does */); 
2127 void wxMenuBar::OnMouseMove(wxMouseEvent
& event
) 
2131         (void)ProcessMouseEvent(event
.GetPosition()); 
2139 bool wxMenuBar::ProcessMouseEvent(const wxPoint
& pt
) 
2141     // a hack to ignore the extra mouse events MSW sends us: this is similar to 
2142     // wxUSE_MOUSEEVENT_HACK in wxWin itself but it isn't enough for us here as 
2143     // we get the messages from different windows (old and new popup menus for 
2146     static wxPoint s_ptLast
; 
2147     if ( pt 
== s_ptLast 
) 
2155     int currentNew 
= GetMenuFromPoint(pt
); 
2156     if ( (currentNew 
== -1) || (currentNew 
== m_current
) ) 
2161     // select the new active item 
2162     DoSelectMenu(currentNew
); 
2164     // show the menu if we know that we should, even if we hadn't been showing 
2165     // it before (this may happen if the previous menu was disabled) 
2166     if ( m_shouldShowMenu 
&& !m_menuShown
) 
2168         // open the new menu if the old one we closed had been opened 
2169         PopupCurrentMenu(false /* don't select first item - as Windows does */); 
2175 void wxMenuBar::OnKeyDown(wxKeyEvent
& event
) 
2177     // ensure that we have a current item - we might not have it if we're 
2178     // given the focus with Alt or F10 press (and under GTK+ the menubar 
2179     // somehow gets the keyboard events even when it doesn't have focus...) 
2180     if ( m_current 
== -1 ) 
2182         if ( !HasCapture() ) 
2186         else // we do have capture 
2188             // we always maintain a valid current item while we're in modal 
2189             // state (i.e. have the capture) 
2190             wxFAIL_MSG( _T("how did we manage to lose current item?") ); 
2196     int key 
= event
.GetKeyCode(); 
2198     // first let the menu have it 
2199     if ( IsShowingMenu() && m_menuShown
->ProcessKeyDown(key
) ) 
2204     // cycle through the menu items when left/right arrows are pressed and open 
2205     // the menu when up/down one is 
2209             // Alt must be processed at wxWindow level too 
2214             // remove the selection and give the focus away 
2215             if ( m_current 
!= -1 ) 
2217                 if ( IsShowingMenu() ) 
2229                 size_t count 
= GetCount(); 
2232                     // the item won't change anyhow 
2235                 //else: otherwise, it will 
2237                 // remember if we were showing a menu - if we did, we should 
2238                 // show the new menu after changing the item 
2239                 bool wasMenuOpened 
= IsShowingMenu(); 
2240                 if ( wasMenuOpened 
) 
2245                 // cast is safe as we tested for -1 above 
2246                 size_t currentNew 
= (size_t)m_current
; 
2248                 if ( key 
== WXK_LEFT 
) 
2250                     if ( currentNew
-- == 0 ) 
2251                         currentNew 
= count 
- 1; 
2255                     if ( ++currentNew 
== count 
) 
2259                 DoSelectMenu(currentNew
); 
2261                 if ( wasMenuOpened 
) 
2276             // letters open the corresponding menu 
2279                 int idxFound 
= FindNextItemForAccel(m_current
, key
, &unique
); 
2281                 if ( idxFound 
!= -1 ) 
2283                     if ( IsShowingMenu() ) 
2288                     DoSelectMenu((size_t)idxFound
); 
2290                     // if the item is not unique, just select it but don't 
2291                     // activate as the user might have wanted to activate 
2294                     // also, don't try to open a disabled menu 
2295                     if ( unique 
&& IsEnabledTop((size_t)idxFound
) ) 
2301                     // skip the "event.Skip()" below 
2310 // ---------------------------------------------------------------------------- 
2311 // wxMenuBar accel handling 
2312 // ---------------------------------------------------------------------------- 
2314 int wxMenuBar::FindNextItemForAccel(int idxStart
, int key
, bool *unique
) const 
2316     if ( !wxIsalnum((wxChar
)key
) ) 
2318         // we only support letters/digits as accels 
2322     // do we have more than one item with this accel? 
2326     // translate everything to lower case before comparing 
2327     wxChar chAccel 
= (wxChar
)wxTolower(key
); 
2329     // the index of the item with this accel 
2332     // loop through all items searching for the item with this 
2333     // accel starting at the item after the current one 
2334     int count 
= GetCount(); 
2335     int n 
= idxStart 
== -1 ? 0 : idxStart 
+ 1; 
2346         const wxMenuInfo
& info 
= m_menuInfos
[n
]; 
2348         int idxAccel 
= info
.GetAccelIndex(); 
2349         if ( idxAccel 
!= -1 && 
2350              wxTolower(info
.GetLabel()[(size_t)idxAccel
]) 
2353             // ok, found an item with this accel 
2354             if ( idxFound 
== -1 ) 
2356                 // store it but continue searching as we need to 
2357                 // know if it's the only item with this accel or if 
2361             else // we already had found such item 
2366                 // no need to continue further, we won't find 
2367                 // anything we don't already know 
2372         // we want to iterate over all items wrapping around if 
2380         if ( n 
== idxStart 
) 
2382             // we've seen all items 
2392 bool wxMenuBar::ProcessAccelEvent(const wxKeyEvent
& event
) 
2395     for ( wxMenuList::compatibility_iterator node 
= m_menus
.GetFirst(); 
2397           node 
= node
->GetNext(), n
++ ) 
2399         // accels of the items in the disabled menus shouldn't work 
2400         if ( m_menuInfos
[n
].IsEnabled() ) 
2402             if ( node
->GetData()->ProcessAccelEvent(event
) ) 
2404                 // menu processed it 
2414 #endif // wxUSE_ACCEL 
2416 // ---------------------------------------------------------------------------- 
2417 // wxMenuBar menus showing 
2418 // ---------------------------------------------------------------------------- 
2420 void wxMenuBar::PopupCurrentMenu(bool selectFirst
) 
2422     wxCHECK_RET( m_current 
!= -1, _T("no menu to popup") ); 
2424     // forgot to call DismissMenu()? 
2425     wxASSERT_MSG( !m_menuShown
, _T("shouldn't show two menus at once!") ); 
2427     // in any case, we should show it - even if we won't 
2428     m_shouldShowMenu 
= true; 
2430     if ( IsEnabledTop(m_current
) ) 
2432         // remember the menu we show 
2433         m_menuShown 
= GetMenu(m_current
); 
2435         // we don't show the menu at all if it has no items 
2436         if ( !m_menuShown
->IsEmpty() ) 
2438             // position it correctly: note that we must use screen coords and 
2439             // that we pass 0 as width to position the menu exactly below the 
2440             // item, not to the right of it 
2441             wxRect rectItem 
= GetItemRect(m_current
); 
2443             m_menuShown
->SetInvokingWindow(m_frameLast
); 
2445             m_menuShown
->Popup(ClientToScreen(rectItem
.GetPosition()), 
2446                                wxSize(0, rectItem
.GetHeight()), 
2451             // reset it back as no menu is shown 
2455     //else: don't show disabled menu 
2458 void wxMenuBar::DismissMenu() 
2460     wxCHECK_RET( m_menuShown
, _T("can't dismiss menu if none is shown") ); 
2462     m_menuShown
->Dismiss(); 
2466 void wxMenuBar::OnDismissMenu(bool dismissMenuBar
) 
2468     m_shouldShowMenu 
= false; 
2470     if ( dismissMenuBar 
) 
2476 void wxMenuBar::OnDismiss() 
2478     if ( ReleaseMouseCapture() ) 
2479         wxLogTrace(_T("mousecapture"), _T("Releasing mouse from wxMenuBar::OnDismiss")); 
2481     if ( m_current 
!= -1 ) 
2483         size_t current 
= m_current
; 
2486         RefreshItem(current
); 
2492 bool wxMenuBar::ReleaseMouseCapture() 
2495     // With wxX11, when a menu is closed by clicking away from it, a control 
2496     // under the click will still get an event, even though the menu has the 
2497     // capture (bug?). So that control may already have taken the capture by 
2498     // this point, preventing us from releasing the menu's capture. So to work 
2499     // around this, we release both captures, then put back the control's 
2501     wxWindow 
*capture 
= GetCapture(); 
2504         capture
->ReleaseMouse(); 
2506         if ( capture 
== this ) 
2509         bool had 
= HasCapture(); 
2514         capture
->CaptureMouse(); 
2528 void wxMenuBar::GiveAwayFocus() 
2530     GetFrame()->SetFocus(); 
2533 // ---------------------------------------------------------------------------- 
2534 // popup menu support 
2535 // ---------------------------------------------------------------------------- 
2537 wxEventLoop 
*wxWindow::ms_evtLoopPopup 
= NULL
; 
2539 bool wxWindow::DoPopupMenu(wxMenu 
*menu
, int x
, int y
) 
2541     wxCHECK_MSG( !ms_evtLoopPopup
, false, 
2542                  _T("can't show more than one popup menu at a time") ); 
2545     // we need to change the cursor before showing the menu as, apparently, no 
2546     // cursor changes took place while the mouse is captured 
2547     wxCursor cursorOld 
= GetCursor(); 
2548     SetCursor(wxCURSOR_ARROW
); 
2552     // flash any delayed log messages before showing the menu, otherwise it 
2553     // could be dismissed (because it would lose focus) immediately after being 
2555     wxLog::FlushActive(); 
2557     // some controls update themselves from OnIdle() call - let them do it 
2558     wxTheApp
->ProcessIdle(); 
2560     // if the window hadn't been refreshed yet, the menu can adversely affect 
2561     // its next OnPaint() handler execution - i.e. scrolled window refresh 
2562     // logic breaks then as it scrolls part of the menu which hadn't been there 
2563     // when the update event was generated into view 
2567     menu
->SetInvokingWindow(this); 
2569     // wxLogDebug( "Name of invoking window %s", menu->GetInvokingWindow()->GetName().c_str() ); 
2571     menu
->Popup(ClientToScreen(wxPoint(x
, y
)), wxSize(0,0)); 
2573     // this is not very useful if the menu was popped up because of the mouse 
2574     // click but I think it is nice to do when it appears because of a key 
2575     // press (i.e. Windows menu key) 
2577     // Windows itself doesn't do it, but IMHO this is nice 
2580     // we have to redirect all keyboard input to the menu temporarily 
2581     PushEventHandler(new wxMenuKbdRedirector(menu
)); 
2583     // enter the local modal loop 
2584     ms_evtLoopPopup 
= new wxEventLoop
; 
2585     ms_evtLoopPopup
->Run(); 
2587     delete ms_evtLoopPopup
; 
2588     ms_evtLoopPopup 
= NULL
; 
2590     // remove the handler 
2591     PopEventHandler(true /* delete it */); 
2593     menu
->SetInvokingWindow(NULL
); 
2596     SetCursor(cursorOld
); 
2602 void wxWindow::DismissPopupMenu() 
2604     wxCHECK_RET( ms_evtLoopPopup
, _T("no popup menu shown") ); 
2606     ms_evtLoopPopup
->Exit(); 
2609 #endif // wxUSE_MENUS