/////////////////////////////////////////////////////////////////////////////
-// Name: univ/menu.cpp
+// Name: src/univ/menu.cpp
// Purpose: wxMenuItem, wxMenu and wxMenuBar implementation
// Author: Vadim Zeitlin
// Modified by:
// headers
// ----------------------------------------------------------------------------
-#if defined(__GNUG__) && !defined(NO_GCC_PRAGMA)
- #pragma implementation "univmenuitem.h"
- #pragma implementation "univmenu.h"
-#endif
-
#include "wx/wxprec.h"
#ifdef __BORLANDC__
#pragma hdrstop
#endif
+#if wxUSE_MENUS
+
+#include "wx/menu.h"
+
#ifndef WX_PRECOMP
#include "wx/dynarray.h"
#include "wx/control.h" // for FindAccelIndex()
- #include "wx/menu.h"
#include "wx/settings.h"
#include "wx/accel.h"
#include "wx/log.h"
+ #include "wx/frame.h"
+ #include "wx/dcclient.h"
#endif // WX_PRECOMP
-#if wxUSE_MENUS
-
#include "wx/popupwin.h"
#include "wx/evtloop.h"
-#include "wx/dcclient.h"
-#include "wx/frame.h"
#include "wx/univ/renderer.h"
#include "wx/msw/private.h"
#endif // __WXMSW__
+typedef wxMenuItemList::compatibility_iterator wxMenuItemIter;
+
// ----------------------------------------------------------------------------
// wxMenuInfo contains all extra information about top level menus we need
// ----------------------------------------------------------------------------
// override the base class version to dismiss any open submenus
virtual void Dismiss();
- // notify the menu when the window disappears from screen
- virtual void OnDismiss();
-
// called when a submenu is dismissed
void OnSubmenuDismiss(bool dismissParent);
+ // the default wxMSW wxPopupTransientWindow::OnIdle disables the capture
+ // when the cursor is inside the popup, which dsables the menu tracking
+ // so override it to do nothing
+#ifdef __WXMSW__
+ void OnIdle(wxIdleEvent& WXUNUSED(event)) { }
+#endif
+
// get the currently selected item (may be NULL)
wxMenuItem *GetCurrentItem() const
{
}
// find the menu item at given position
- wxMenuItemList::compatibility_iterator GetMenuItemFromPoint(const wxPoint& pt) const;
+ wxMenuItemIter GetMenuItemFromPoint(const wxPoint& pt) const;
// refresh the given item
void RefreshItem(wxMenuItem *item);
// preselect the first item
- void SelectFirst() { SetCurrent(m_menu->GetMenuItems().GetFirst()); }
+ void SelectFirst() { SetCurrentItem(m_menu->GetMenuItems().GetFirst()); }
// process the key event, return true if done
bool ProcessKeyDown(int key);
WithMouse
};
+ // notify the menu when the window disappears from screen
+ virtual void OnDismiss();
+
// draw the menu inside this window
virtual void DoDraw(wxControlRenderer *renderer);
// reset the current item and node
void ResetCurrent();
- // set the current node and item withotu refreshing anything
- void SetCurrent(wxMenuItemList::compatibility_iterator node);
- virtual bool SetCurrent(bool doit = true){return wxPopupTransientWindow::SetCurrent(doit);};
+ // set the current node and item without refreshing anything
+ void SetCurrentItem(wxMenuItemIter node);
// change the current item refreshing the old and new items
- void ChangeCurrent(wxMenuItemList::compatibility_iterator node);
+ void ChangeCurrent(wxMenuItemIter node);
// activate item, i.e. call either ClickItem() or OpenSubmenu() depending
// on what it is, return true if something was done (i.e. it's not a
bool HasOpenSubmenu() const { return m_hasOpenSubMenu; }
// get previous node after the current one
- wxMenuItemList::compatibility_iterator GetPrevNode() const;
+ wxMenuItemIter GetPrevNode() const;
// get previous node before the given one, wrapping if it's the first one
- wxMenuItemList::compatibility_iterator GetPrevNode(wxMenuItemList::compatibility_iterator node) const;
+ wxMenuItemIter GetPrevNode(wxMenuItemIter node) const;
// get next node after the current one
- wxMenuItemList::compatibility_iterator GetNextNode() const;
+ wxMenuItemIter GetNextNode() const;
// get next node after the given one, wrapping if it's the last one
- wxMenuItemList::compatibility_iterator GetNextNode(wxMenuItemList::compatibility_iterator node) const;
+ wxMenuItemIter GetNextNode(wxMenuItemIter node) const;
private:
// the menu we show
wxMenu *m_menu;
// the menu node corresponding to the current item
- wxMenuItemList::compatibility_iterator m_nodeCurrent;
+ wxMenuItemIter m_nodeCurrent;
// do we currently have an opened submenu?
bool m_hasOpenSubMenu;
EVT_LEFT_UP(wxPopupMenuWindow::OnLeftUp)
EVT_MOTION(wxPopupMenuWindow::OnMouseMove)
EVT_LEAVE_WINDOW(wxPopupMenuWindow::OnMouseLeave)
+#ifdef __WXMSW__
+ EVT_IDLE(wxPopupMenuWindow::OnIdle)
+#endif
END_EVENT_TABLE()
BEGIN_EVENT_TABLE(wxMenuBar, wxMenuBarBase)
void wxPopupMenuWindow::ResetCurrent()
{
-#if wxUSE_STL
- SetCurrent(wxMenuItemList::compatibility_iterator());
-#else
- SetCurrent((wxwxMenuItemListNode *)NULL);
-#endif
+ SetCurrentItem(wxMenuItemIter());
}
-void wxPopupMenuWindow::SetCurrent(wxMenuItemList::compatibility_iterator node)
+void wxPopupMenuWindow::SetCurrentItem(wxMenuItemIter node)
{
m_nodeCurrent = node;
}
-void wxPopupMenuWindow::ChangeCurrent(wxMenuItemList::compatibility_iterator node)
+void wxPopupMenuWindow::ChangeCurrent(wxMenuItemIter node)
{
- if ( node != m_nodeCurrent )
+ if ( !m_nodeCurrent || (node != m_nodeCurrent) )
{
- wxMenuItemList::compatibility_iterator nodeOldCurrent = m_nodeCurrent;
+ wxMenuItemIter nodeOldCurrent = m_nodeCurrent;
m_nodeCurrent = node;
}
}
-wxMenuItemList::compatibility_iterator wxPopupMenuWindow::GetPrevNode() const
+wxMenuItemIter wxPopupMenuWindow::GetPrevNode() const
{
// return the last node if there had been no previously selected one
return m_nodeCurrent ? GetPrevNode(m_nodeCurrent)
- : m_menu->GetMenuItems().GetLast();
+ : wxMenuItemIter(m_menu->GetMenuItems().GetLast());
}
-wxMenuItemList::compatibility_iterator
-wxPopupMenuWindow::GetPrevNode(wxMenuItemList::compatibility_iterator node) const
+wxMenuItemIter
+wxPopupMenuWindow::GetPrevNode(wxMenuItemIter node) const
{
if ( node )
{
return node;
}
-wxMenuItemList::compatibility_iterator wxPopupMenuWindow::GetNextNode() const
+wxMenuItemIter wxPopupMenuWindow::GetNextNode() const
{
// return the first node if there had been no previously selected one
return m_nodeCurrent ? GetNextNode(m_nodeCurrent)
- : m_menu->GetMenuItems().GetFirst();
+ : wxMenuItemIter(m_menu->GetMenuItems().GetFirst());
}
-wxMenuItemList::compatibility_iterator
-wxPopupMenuWindow::GetNextNode(wxMenuItemList::compatibility_iterator node) const
+wxMenuItemIter
+wxPopupMenuWindow::GetNextNode(wxMenuItemIter node) const
{
if ( node )
{
wxPopupTransientWindow::Popup(focus);
+ // the base class no-longer captures the mouse automatically when Popup
+ // is called, so do it here to allow the menu tracking to work
+ if ( !HasCapture() )
+ CaptureMouse();
+
#ifdef __WXMSW__
// ensure that this window is really on top of everything: without using
// SetWindowPos() it can be covered by its parent menu which is not
}
wxPopupTransientWindow::Dismiss();
+
+ ResetCurrent();
}
void wxPopupMenuWindow::OnDismiss()
HandleDismiss(true);
}
-void wxPopupMenuWindow::OnSubmenuDismiss(bool dismissParent)
+void wxPopupMenuWindow::OnSubmenuDismiss(bool WXUNUSED(dismissParent))
{
m_hasOpenSubMenu = false;
-
- // we are closing whole menu so remove current highlight
- if ( dismissParent )
- ResetCurrent();
}
void wxPopupMenuWindow::HandleDismiss(bool dismissParent)
{
- ResetCurrent();
-
m_menu->OnDismiss(dismissParent);
}
// wxPopupMenuWindow geometry
// ----------------------------------------------------------------------------
-wxMenuItemList::compatibility_iterator
+wxMenuItemIter
wxPopupMenuWindow::GetMenuItemFromPoint(const wxPoint& pt) const
{
// we only use the y coord normally, but still check x in case the point is
if ( wxWindow::HitTest(pt) == wxHT_WINDOW_INSIDE )
{
wxCoord y = 0;
- for ( wxMenuItemList::compatibility_iterator node = m_menu->GetMenuItems().GetFirst();
+ for ( wxMenuItemIter node = m_menu->GetMenuItems().GetFirst();
node;
node = node->GetNext() )
{
}
}
-#if wxUSE_STL
- return wxMenuItemList::compatibility_iterator();
-#else
- return NULL;
-#endif
+ return wxMenuItemIter();
}
// ----------------------------------------------------------------------------
wxCoord y = 0;
const wxMenuGeometryInfo& gi = m_menu->GetGeometryInfo();
- for ( wxMenuItemList::compatibility_iterator node = m_menu->GetMenuItems().GetFirst();
+ for ( wxMenuItemIter node = m_menu->GetMenuItems().GetFirst();
node;
node = node->GetNext() )
{
void wxPopupMenuWindow::OnLeftUp(wxMouseEvent& event)
{
- wxMenuItemList::compatibility_iterator node = GetMenuItemFromPoint(event.GetPosition());
+ wxMenuItemIter node = GetMenuItemFromPoint(event.GetPosition());
if ( node )
{
ActivateItem(node->GetData(), WithMouse);
void wxPopupMenuWindow::ProcessMouseMove(const wxPoint& pt)
{
- wxMenuItemList::compatibility_iterator node = GetMenuItemFromPoint(pt);
+ wxMenuItemIter node = GetMenuItemFromPoint(pt);
// don't reset current to NULL here, we only do it when the mouse leaves
// the window (see below)
if ( node )
{
- if ( node != m_nodeCurrent )
+ if ( !m_nodeCurrent || (node != m_nodeCurrent) )
{
ChangeCurrent(node);
if ( resetCurrent )
{
-#if wxUSE_STL
- ChangeCurrent(wxMenuItemList::compatibility_iterator());
-#else
- ChangeCurrent(NULL);
-#endif
+ ChangeCurrent(wxMenuItemIter());
}
}
void wxPopupMenuWindow::OnKeyDown(wxKeyEvent& event)
{
- if ( !ProcessKeyDown(event.GetKeyCode()) )
+ wxMenuBar *menubar = m_menu->GetMenuBar();
+
+ if ( menubar )
+ {
+ menubar->ProcessEvent(event);
+ }
+ else if ( !ProcessKeyDown(event.GetKeyCode()) )
{
event.Skip();
}
{
bool up = key == WXK_UP;
- wxMenuItemList::compatibility_iterator nodeStart = up ? GetPrevNode()
- : GetNextNode(),
- node = nodeStart;
+ wxMenuItemIter nodeStart = up ? GetPrevNode() : GetNextNode(),
+ node = nodeStart;
while ( node && node->GetData()->IsSeparator() )
{
node = up ? GetPrevNode(node) : GetNextNode(node);
{
// nothing but separators and disabled items in this
// menu, break out
-#if wxUSE_STL
- node = wxMenuItemList::compatibility_iterator();
-#else
- node = NULL;
-#endif
+ node = wxMenuItemIter();
}
}
// we want to start from the item after this one because
// if we're already on the item with the given accel we want to
// go to the next one, not to stay in place
- wxMenuItemList::compatibility_iterator nodeStart = GetNextNode();
+ wxMenuItemIter nodeStart = GetNextNode();
// do we have more than one item with this accel?
bool notUnique = false;
// loop through all items searching for the item with this
// accel
- wxMenuItemList::compatibility_iterator node = nodeStart,
-#if wxUSE_STL
- nodeFound = wxMenuItemList::compatibility_iterator();
-#else
- nodeFound = NULL;
-#endif
+ wxMenuItemIter nodeFound,
+ node = nodeStart;
for ( ;; )
{
item = node->GetData();
{
// we need to update its end item
item->SetRadioGroupStart(m_startRadioGroup);
- wxMenuItemList::compatibility_iterator node = GetMenuItems().Item(m_startRadioGroup);
+ wxMenuItemIter node = GetMenuItems().Item(m_startRadioGroup);
if ( node )
{
}
// try our submenus
- for ( wxMenuItemList::compatibility_iterator node = GetMenuItems().GetFirst();
+ for ( wxMenuItemIter node = GetMenuItems().GetFirst();
node;
node = node->GetNext() )
{
}
// also uncheck all the other items in this radio group
- wxMenuItemList::compatibility_iterator node = items.Item(start);
+ wxMenuItemIter node = items.Item(start);
for ( int n = start; n <= end && node; n++ )
{
if ( n != pos )
return false;
}
- // FIXME: temporary workaround for crash, to be fixed
- // in a later version.
-#if 0
// select the new active item
DoSelectMenu(currentNew);
// open the new menu if the old one we closed had been opened
PopupCurrentMenu(false /* don't select first item - as Windows does */);
}
-#endif
return true;
}
void wxMenuBar::OnDismiss()
{
- if ( GetCapture() )
- {
+ if ( ReleaseMouseCapture() )
wxLogTrace(_T("mousecapture"), _T("Releasing mouse from wxMenuBar::OnDismiss"));
- GetCapture()->ReleaseMouse();
- }
if ( m_current != -1 )
{
GiveAwayFocus();
}
+bool wxMenuBar::ReleaseMouseCapture()
+{
+#ifdef __WXX11__
+ // With wxX11, when a menu is closed by clicking away from it, a control
+ // under the click will still get an event, even though the menu has the
+ // capture (bug?). So that control may already have taken the capture by
+ // this point, preventing us from releasing the menu's capture. So to work
+ // around this, we release both captures, then put back the control's
+ // capture.
+ wxWindow *capture = GetCapture();
+ if ( capture )
+ {
+ capture->ReleaseMouse();
+
+ if ( capture == this )
+ return true;
+
+ bool had = HasCapture();
+
+ if ( had )
+ ReleaseMouse();
+
+ capture->CaptureMouse();
+
+ return had;
+ }
+#else
+ if ( HasCapture() )
+ {
+ ReleaseMouse();
+ return true;
+ }
+#endif
+ return false;
+}
+
void wxMenuBar::GiveAwayFocus()
{
GetFrame()->SetFocus();
}
#endif // wxUSE_MENUS
-