/////////////////////////////////////////////////////////////////////////////
-// Name: univ/menu.cpp
+// Name: src/univ/menu.cpp
// Purpose: wxMenuItem, wxMenu and wxMenuBar implementation
// Author: Vadim Zeitlin
// Modified by:
// Created: 25.08.00
// RCS-ID: $Id$
// Copyright: (c) 2000 SciTech Software, Inc. (www.scitechsoft.com)
-// Licence: wxWindows license
+// Licence: wxWindows licence
/////////////////////////////////////////////////////////////////////////////
// ============================================================================
// headers
// ----------------------------------------------------------------------------
-#ifdef __GNUG__
- #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"
+#include "wx/stockitem.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
// ----------------------------------------------------------------------------
void SetLabel(const wxString& text)
{
+ m_originalLabel = text;
+
// remember the accel char (may be -1 if none)
m_indexAccel = wxControl::FindAccelIndex(text, &m_label);
m_width = 0;
}
- void SetEnabled(bool enabled = TRUE) { m_isEnabled = enabled; }
+ void SetEnabled(bool enabled = true) { m_isEnabled = enabled; }
// accessors
const wxString& GetLabel() const { return m_label; }
+ const wxString& GetOriginalLabel() const { return m_originalLabel; }
bool IsEnabled() const { return m_isEnabled; }
wxCoord GetWidth(wxMenuBar *menubar) const
{
}
wxString m_label;
+ wxString m_originalLabel;
wxCoord m_width;
int m_indexAccel;
bool m_isEnabled;
public:
wxPopupMenuWindow(wxWindow *parent, wxMenu *menu);
+ virtual ~wxPopupMenuWindow();
+
// override the base class version to select the first item initially
virtual void Popup(wxWindow *focus = NULL);
// 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() { m_hasOpenSubMenu = FALSE; }
+ 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::Node *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
+ // process the key event, return true if done
bool ProcessKeyDown(int key);
// process mouse move event
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::Node *node);
+ // 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::Node *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
+ // on what it is, return true if something was done (i.e. it's not a
// separator...)
bool ActivateItem(wxMenuItem *item, InputMethod how = WithKeyboard);
bool HasOpenSubmenu() const { return m_hasOpenSubMenu; }
// get previous node after the current one
- wxMenuItemList::Node *GetPrevNode() const;
+ wxMenuItemIter GetPrevNode() const;
// get previous node before the given one, wrapping if it's the first one
- wxMenuItemList::Node *GetPrevNode(wxMenuItemList::Node *node) const;
+ wxMenuItemIter GetPrevNode(wxMenuItemIter node) const;
// get next node after the current one
- wxMenuItemList::Node *GetNextNode() const;
+ wxMenuItemIter GetNextNode() const;
// get next node after the given one, wrapping if it's the last one
- wxMenuItemList::Node *GetNextNode(wxMenuItemList::Node *node) const;
+ wxMenuItemIter GetNextNode(wxMenuItemIter node) const;
private:
// the menu we show
wxMenu *m_menu;
// the menu node corresponding to the current item
- wxMenuItemList::Node *m_nodeCurrent;
+ wxMenuItemIter m_nodeCurrent;
// do we currently have an opened submenu?
bool m_hasOpenSubMenu;
}
else
{
+ // return false;
+
return wxEvtHandler::ProcessEvent(event);
}
}
// wxWin macros
// ----------------------------------------------------------------------------
-IMPLEMENT_DYNAMIC_CLASS(wxMenu, wxEvtHandler)
-IMPLEMENT_DYNAMIC_CLASS(wxMenuBar, wxWindow)
-IMPLEMENT_DYNAMIC_CLASS(wxMenuItem, wxObject)
-
BEGIN_EVENT_TABLE(wxPopupMenuWindow, wxPopupTransientWindow)
EVT_KEY_DOWN(wxPopupMenuWindow::OnKeyDown)
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)
wxPopupMenuWindow::wxPopupMenuWindow(wxWindow *parent, wxMenu *menu)
{
m_menu = menu;
- m_hasOpenSubMenu = FALSE;
+ m_hasOpenSubMenu = false;
ResetCurrent();
SetCursor(wxCURSOR_ARROW);
}
+wxPopupMenuWindow::~wxPopupMenuWindow()
+{
+ // When m_popupMenu in wxMenu is deleted because it
+ // is a child of an old menu bar being deleted (note: it does
+ // not get destroyed by the wxMenu destructor, but
+ // by DestroyChildren()), m_popupMenu should be reset to NULL.
+
+ m_menu->m_popupMenu = NULL;
+}
+
// ----------------------------------------------------------------------------
// wxPopupMenuWindow current item/node handling
// ----------------------------------------------------------------------------
void wxPopupMenuWindow::ResetCurrent()
{
- SetCurrent(NULL);
+ SetCurrentItem(wxMenuItemIter());
}
-void wxPopupMenuWindow::SetCurrent(wxMenuItemList::Node *node)
+void wxPopupMenuWindow::SetCurrentItem(wxMenuItemIter node)
{
m_nodeCurrent = node;
}
-void wxPopupMenuWindow::ChangeCurrent(wxMenuItemList::Node *node)
+void wxPopupMenuWindow::ChangeCurrent(wxMenuItemIter node)
{
- if ( node != m_nodeCurrent )
+ if ( !m_nodeCurrent || !node || (node != m_nodeCurrent) )
{
- wxMenuItemList::Node *nodeOldCurrent = m_nodeCurrent;
+ wxMenuItemIter nodeOldCurrent = m_nodeCurrent;
m_nodeCurrent = node;
if ( nodeOldCurrent )
{
wxMenuItem *item = nodeOldCurrent->GetData();
- wxCHECK_RET( item, _T("no current item?") );
+ wxCHECK_RET( item, wxT("no current item?") );
// if it was the currently opened menu, close it
if ( item->IsSubMenu() && item->GetSubMenu()->IsShown() )
{
item->GetSubMenu()->Dismiss();
- OnSubmenuDismiss();
+ OnSubmenuDismiss( false );
}
RefreshItem(item);
}
}
-wxMenuItemList::Node *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::Node *
-wxPopupMenuWindow::GetPrevNode(wxMenuItemList::Node *node) const
+wxMenuItemIter
+wxPopupMenuWindow::GetPrevNode(wxMenuItemIter node) const
{
if ( node )
{
return node;
}
-wxMenuItemList::Node *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::Node *
-wxPopupMenuWindow::GetNextNode(wxMenuItemList::Node *node) const
+wxMenuItemIter
+wxPopupMenuWindow::GetNextNode(wxMenuItemIter node) const
{
if ( node )
{
// check that the current item had been properly reset before
wxASSERT_MSG( !m_nodeCurrent ||
m_nodeCurrent == m_menu->GetMenuItems().GetFirst(),
- _T("menu current item preselected incorrectly") );
+ wxT("menu current item preselected incorrectly") );
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
wxPopupMenuWindow *win = menuParent->m_popupMenu;
// if we're shown, the parent menu must be also shown
- wxCHECK_RET( win, _T("parent menu is not shown?") );
+ wxCHECK_RET( win, wxT("parent menu is not shown?") );
if ( !::SetWindowPos(GetHwndOf(win), GetHwnd(),
0, 0, 0, 0,
SWP_NOMOVE | SWP_NOSIZE | SWP_NOREDRAW) )
{
- wxLogLastError(_T("SetWindowPos(HWND_TOP)"));
+ wxLogLastError(wxT("SetWindowPos(HWND_TOP)"));
}
Refresh();
if ( HasOpenSubmenu() )
{
wxMenuItem *item = GetCurrentItem();
- wxCHECK_RET( item && item->IsSubMenu(), _T("where is our open submenu?") );
+ wxCHECK_RET( item && item->IsSubMenu(), wxT("where is our open submenu?") );
wxPopupMenuWindow *win = item->GetSubMenu()->m_popupMenu;
- wxCHECK_RET( win, _T("opened submenu is not opened?") );
+ wxCHECK_RET( win, wxT("opened submenu is not opened?") );
win->Dismiss();
- OnSubmenuDismiss();
+ OnSubmenuDismiss( false );
}
wxPopupTransientWindow::Dismiss();
+
+ ResetCurrent();
}
void wxPopupMenuWindow::OnDismiss()
{
// when we are dismissed because the user clicked elsewhere or we lost
// focus in any other way, hide the parent menu as well
- HandleDismiss(TRUE);
+ HandleDismiss(true);
}
-void wxPopupMenuWindow::HandleDismiss(bool dismissParent)
+void wxPopupMenuWindow::OnSubmenuDismiss(bool WXUNUSED(dismissParent))
{
- ResetCurrent();
+ m_hasOpenSubMenu = false;
+}
+void wxPopupMenuWindow::HandleDismiss(bool dismissParent)
+{
m_menu->OnDismiss(dismissParent);
}
void wxPopupMenuWindow::DismissAndNotify()
{
Dismiss();
- HandleDismiss(TRUE);
+ HandleDismiss(true);
}
// ----------------------------------------------------------------------------
// wxPopupMenuWindow geometry
// ----------------------------------------------------------------------------
-wxMenuItemList::Node *
+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::Node *node = m_menu->GetMenuItems().GetFirst();
+ for ( wxMenuItemIter node = m_menu->GetMenuItems().GetFirst();
node;
node = node->GetNext() )
{
}
}
- return NULL;
+ return wxMenuItemIter();
}
// ----------------------------------------------------------------------------
void wxPopupMenuWindow::RefreshItem(wxMenuItem *item)
{
- wxCHECK_RET( item, _T("can't refresh NULL item") );
+ wxCHECK_RET( item, wxT("can't refresh NULL item") );
- wxASSERT_MSG( IsShown(), _T("can't refresh menu which is not shown") );
+ wxASSERT_MSG( IsShown(), wxT("can't refresh menu which is not shown") );
// FIXME: -1 here because of SetLogicalOrigin(1, 1) in DoDraw()
RefreshRect(wxRect(0, item->GetPosition() - 1,
wxCoord y = 0;
const wxMenuGeometryInfo& gi = m_menu->GetGeometryInfo();
- for ( wxMenuItemList::Node *node = m_menu->GetMenuItems().GetFirst();
+ for ( wxMenuItemIter node = m_menu->GetMenuItems().GetFirst();
node;
node = node->GetNext() )
{
if ( item == GetCurrentItem() )
flags |= wxCONTROL_SELECTED;
+ wxBitmap bmp;
+
+ if ( !item->IsEnabled() )
+ {
+ bmp = item->GetDisabledBitmap();
+ }
+
+ if ( !bmp.IsOk() )
+ {
+ // strangely enough, for unchecked item we use the
+ // "checked" bitmap because this is the default one - this
+ // explains this strange boolean expression
+ bmp = item->GetBitmap(!item->IsCheckable() || item->IsChecked());
+ }
+
rend->DrawMenuItem
(
dc,
y,
gi,
- item->GetLabel(),
+ item->GetItemLabelText(),
item->GetAccelString(),
- // strangely enough, for unchecked item we use the
- // "checked" bitmap because this is the default one - this
- // explains this strange boolean expression
- item->GetBitmap(!item->IsCheckable() || item->IsChecked()),
+ bmp,
flags,
item->GetAccelIndex()
);
void wxPopupMenuWindow::ClickItem(wxMenuItem *item)
{
- wxCHECK_RET( item, _T("can't click NULL item") );
+ wxCHECK_RET( item, wxT("can't click NULL item") );
wxASSERT_MSG( !item->IsSeparator() && !item->IsSubMenu(),
- _T("can't click this item") );
+ wxT("can't click this item") );
wxMenu* menu = m_menu;
// close all menus
DismissAndNotify();
-
+
menu->ClickItem(item);
}
void wxPopupMenuWindow::OpenSubmenu(wxMenuItem *item, InputMethod how)
{
- wxCHECK_RET( item, _T("can't open NULL submenu") );
+ wxCHECK_RET( item, wxT("can't open NULL submenu") );
wxMenu *submenu = item->GetSubMenu();
- wxCHECK_RET( submenu, _T("can only open submenus!") );
+ wxCHECK_RET( submenu, wxT("can only open submenus!") );
// FIXME: should take into account the border width
submenu->Popup(ClientToScreen(wxPoint(0, item->GetPosition())),
wxSize(m_menu->GetGeometryInfo().GetSize().x, 0),
how == WithKeyboard /* preselect first item then */);
- m_hasOpenSubMenu = TRUE;
+ m_hasOpenSubMenu = true;
}
bool wxPopupMenuWindow::ActivateItem(wxMenuItem *item, InputMethod how)
// don't activate disabled items
if ( !item || !item->IsEnabled() )
{
- return FALSE;
+ return false;
}
// normal menu items generate commands, submenus can be opened and
}
else // separator, can't activate
{
- return FALSE;
+ return false;
}
- return TRUE;
+ return true;
}
// ----------------------------------------------------------------------------
{
wxPopupMenuWindow *win = menu->m_popupMenu;
- wxCHECK_MSG( win, FALSE, _T("parent menu not shown?") );
+ wxCHECK_MSG( win, false, wxT("parent menu not shown?") );
pos = ClientToScreen(pos);
if ( win->GetMenuItemFromPoint(win->ScreenToClient(pos)) )
{
// eat the event
- return TRUE;
+ return true;
}
//else: it is outside the parent menu as well, do dismiss this one
}
}
- return FALSE;
+ return false;
}
void wxPopupMenuWindow::OnLeftUp(wxMouseEvent& event)
{
- wxMenuItemList::Node *node = GetMenuItemFromPoint(event.GetPosition());
+ wxMenuItemIter node = GetMenuItemFromPoint(event.GetPosition());
if ( node )
{
ActivateItem(node->GetData(), WithMouse);
void wxPopupMenuWindow::ProcessMouseMove(const wxPoint& pt)
{
- wxMenuItemList::Node *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);
wxPopupMenuWindow *win = menuParent->m_popupMenu;
// if we're shown, the parent menu must be also shown
- wxCHECK_RET( win, _T("parent menu is not shown?") );
+ wxCHECK_RET( win, wxT("parent menu is not shown?") );
win->ProcessMouseMove(win->ScreenToClient(ptScreen));
}
if ( HasOpenSubmenu() )
{
wxMenuItem *item = GetCurrentItem();
- wxCHECK_RET( CanOpen(item), _T("where is our open submenu?") );
+ wxCHECK_RET( CanOpen(item), wxT("where is our open submenu?") );
wxPopupMenuWindow *win = item->GetSubMenu()->m_popupMenu;
- wxCHECK_RET( win, _T("submenu is opened but not shown?") );
+ wxCHECK_RET( win, wxT("submenu is opened but not shown?") );
// only handle this event if the mouse is not inside the submenu
wxPoint pt = ClientToScreen(event.GetPosition());
else
{
// this menu is the last opened
- resetCurrent = TRUE;
+ resetCurrent = true;
}
if ( resetCurrent )
{
- ChangeCurrent(NULL);
+ 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();
}
// to open it inspit of this)
if ( HasOpenSubmenu() )
{
- wxCHECK_MSG( CanOpen(item), FALSE,
- _T("has open submenu but another item selected?") );
+ wxCHECK_MSG( CanOpen(item), false,
+ wxT("has open submenu but another item selected?") );
if ( item->GetSubMenu()->ProcessKeyDown(key) )
- return TRUE;
+ return true;
}
- bool processed = TRUE;
+ bool processed = true;
// handle the up/down arrows, home, end, esc and return here, pass the
// left/right arrows to the menu bar except when the right arrow can be
// menubar
if ( !m_menu->GetParent() )
{
- processed = FALSE;
+ processed = false;
break;
}
case WXK_ESCAPE:
// close just this menu
Dismiss();
- HandleDismiss(FALSE);
+ HandleDismiss(false);
break;
case WXK_RETURN:
{
bool up = key == WXK_UP;
- wxMenuItemList::Node *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
- node = NULL;
+ node = wxMenuItemIter();
}
}
}
else
{
- processed = FALSE;
+ processed = false;
}
}
break;
}
else
{
- processed = FALSE;
+ processed = false;
}
break;
default:
// look for the menu item starting with this letter
- if ( wxIsalnum(key) )
+ if ( wxIsalnum((wxChar)key) )
{
// 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::Node *nodeStart = GetNextNode();
+ wxMenuItemIter nodeStart = GetNextNode();
// do we have more than one item with this accel?
- bool notUnique = FALSE;
+ bool notUnique = false;
// translate everything to lower case before comparing
- wxChar chAccel = wxTolower(key);
+ wxChar chAccel = (wxChar)wxTolower(key);
// loop through all items searching for the item with this
// accel
- wxMenuItemList::Node *node = nodeStart,
- *nodeFound = NULL;
+ wxMenuItemIter nodeFound,
+ node = nodeStart;
for ( ;; )
{
item = node->GetData();
int idxAccel = item->GetAccelIndex();
if ( idxAccel != -1 &&
- wxTolower(item->GetLabel()[(size_t)idxAccel])
+ (wxChar)wxTolower(item->GetItemLabelText()[(size_t)idxAccel])
== chAccel )
{
// ok, found an item with this accel
}
else // we already had found such item
{
- notUnique = TRUE;
+ notUnique = true;
// no need to continue further, we won't find
// anything we don't already know
//else: just select it but don't activate as the user might
// have wanted to activate another item
- // skip "processed = FALSE" below
+ // skip "processed = false" below
break;
}
}
- processed = FALSE;
+ processed = false;
}
return processed;
m_geometry = NULL;
m_popupMenu = NULL;
+
+ m_startRadioGroup = -1;
}
wxMenu::~wxMenu()
}
else
{
- wxFAIL_MSG( _T("can't get geometry without window") );
+ wxFAIL_MSG( wxT("can't get geometry without window") );
}
}
void wxMenu::InvalidateGeometryInfo()
{
- if ( m_geometry )
- {
- delete m_geometry;
- m_geometry = NULL;
- }
+ wxDELETE(m_geometry);
}
// ----------------------------------------------------------------------------
#if wxUSE_ACCEL
AddAccelFor(item);
#endif // wxUSE_ACCEL
+}
- // the submenus of a popup menu should have the same invoking window as it
- // has
- if ( m_invokingWindow && item->IsSubMenu() )
- {
- item->GetSubMenu()->SetInvokingWindow(m_invokingWindow);
- }
+void wxMenu::EndRadioGroup()
+{
+ // we're not inside a radio group any longer
+ m_startRadioGroup = -1;
}
-bool wxMenu::DoAppend(wxMenuItem *item)
+wxMenuItem* wxMenu::DoAppend(wxMenuItem *item)
{
+ if ( item->GetKind() == wxITEM_RADIO )
+ {
+ int count = GetMenuItemCount();
+
+ if ( m_startRadioGroup == -1 )
+ {
+ // start a new radio group
+ m_startRadioGroup = count;
+
+ // for now it has just one element
+ item->SetAsRadioGroupStart();
+ item->SetRadioGroupEnd(m_startRadioGroup);
+ }
+ else // extend the current radio group
+ {
+ // we need to update its end item
+ item->SetRadioGroupStart(m_startRadioGroup);
+ wxMenuItemIter node = GetMenuItems().Item(m_startRadioGroup);
+
+ if ( node )
+ {
+ node->GetData()->SetRadioGroupEnd(count);
+ }
+ else
+ {
+ wxFAIL_MSG( wxT("where is the radio group start item?") );
+ }
+ }
+ }
+ else // not a radio item
+ {
+ EndRadioGroup();
+ }
+
if ( !wxMenuBase::DoAppend(item) )
- return FALSE;
+ return NULL;
OnItemAdded(item);
- return TRUE;
+ return item;
}
-bool wxMenu::DoInsert(size_t pos, wxMenuItem *item)
+wxMenuItem* wxMenu::DoInsert(size_t pos, wxMenuItem *item)
{
if ( !wxMenuBase::DoInsert(pos, item) )
- return FALSE;
+ return NULL;
OnItemAdded(item);
- return TRUE;
+ return item;
}
wxMenuItem *wxMenu::DoRemove(wxMenuItem *item)
{
wxMenuBase::Attach(menubar);
- wxCHECK_RET( m_menuBar, _T("menubar can't be NULL after attaching") );
+ wxCHECK_RET( m_menuBar, wxT("menubar can't be NULL after attaching") );
// unfortunately, we can't use m_menuBar->GetEventHandler() here because,
// if the menubar is currently showing a menu, its event handler is a
void wxMenu::Detach()
{
+ // After the menu is detached from the menu bar, it shouldn't send its
+ // events to it.
+ SetNextHandler(NULL);
+
wxMenuBase::Detach();
}
wxWindow *wxMenu::GetRootWindow() const
{
- if ( m_menuBar )
- {
- // simple case - a normal menu attached to the menubar
- return m_menuBar;
- }
-
- // we're a popup menu but the trouble is that only the top level popup menu
- // has a pointer to the invoking window, so we must walk up the menu chain
- // if needed
- wxWindow *win = GetInvokingWindow();
- if ( win )
- {
- // we already have it
- return win;
- }
-
- wxMenu *menu = GetParent();
- while ( menu )
- {
- win = menu->GetInvokingWindow();
- if ( win )
- break;
-
- menu = menu->GetParent();
- }
-
- // we're probably going to crash in the caller anyhow, but try to detect
- // this error as soon as possible
- wxASSERT_MSG( win, _T("menu without any associated window?") );
-
- // also remember it in this menu so that we don't have to search for it the
- // next time
- wxConstCast(this, wxMenu)->m_invokingWindow = win;
-
- return win;
+ return GetMenuBar() ? GetMenuBar() : GetInvokingWindow();
}
wxRenderer *wxMenu::GetRenderer() const
{
// we're going to crash without renderer!
- wxCHECK_MSG( m_popupMenu, NULL, _T("neither popup nor menubar menu?") );
+ wxCHECK_MSG( m_popupMenu, NULL, wxT("neither popup nor menubar menu?") );
return m_popupMenu->GetRenderer();
}
if ( IsShown() )
{
// this would be a bug in IsShown()
- wxCHECK_RET( m_popupMenu, _T("must have popup window if shown!") );
+ wxCHECK_RET( m_popupMenu, wxT("must have popup window if shown!") );
// recalc geometry to update the item height and such
(void)GetGeometryInfo();
wxPopupMenuWindow *win = m_menuParent->m_popupMenu;
if ( win )
{
- win->OnSubmenuDismiss();
+ win->OnSubmenuDismiss( true );
}
else
{
- wxFAIL_MSG( _T("parent menu not shown?") );
+ wxFAIL_MSG( wxT("parent menu not shown?") );
}
// and if we dismiss everything, propagate to parent
{
// dismissParent is recursive
m_menuParent->Dismiss();
- m_menuParent->OnDismiss(TRUE);
+ m_menuParent->OnDismiss(true);
}
}
else // no parent menu
}
else // popup menu
{
- wxCHECK_RET( m_invokingWindow, _T("what kind of menu is this?") );
+ wxWindow * const win = GetInvokingWindow();
+ wxCHECK_RET( win, wxT("what kind of menu is this?") );
- m_invokingWindow->DismissPopupMenu();
- SetInvokingWindow(NULL);
+ win->DismissPopupMenu();
}
}
}
{
m_popupMenu->SelectFirst();
}
-
+
// the geometry might have changed since the last time we were shown, so
// always resize
m_popupMenu->SetClientSize(GetGeometryInfo().GetSize());
// always keep the focus at the originating window
wxWindow *focus = GetRootWindow();
- wxASSERT_MSG( focus, _T("no window to keep focus on?") );
+ wxASSERT_MSG( focus, wxT("no window to keep focus on?") );
// and show it
m_popupMenu->Popup(focus);
void wxMenu::Dismiss()
{
- wxCHECK_RET( IsShown(), _T("can't dismiss hidden menu") );
+ wxCHECK_RET( IsShown(), wxT("can't dismiss hidden menu") );
m_popupMenu->Dismiss();
}
bool wxMenu::ProcessKeyDown(int key)
{
- wxCHECK_MSG( m_popupMenu, FALSE,
- _T("can't process key events if not shown") );
+ wxCHECK_MSG( m_popupMenu, false,
+ wxT("can't process key events if not shown") );
return m_popupMenu->ProcessKeyDown(key);
}
}
// try our submenus
- for ( wxMenuItemList::Node *node = GetMenuItems().GetFirst();
+ for ( wxMenuItemIter node = GetMenuItems().GetFirst();
node;
node = node->GetNext() )
{
// try its elements
if ( item->GetSubMenu()->ProcessAccelEvent(event) )
{
- return TRUE;
+ return true;
}
}
}
- return FALSE;
+ return false;
}
void wxMenu::AddAccelFor(wxMenuItem *item)
int id,
const wxString& text,
const wxString& help,
- bool isCheckable,
+ wxItemKind kind,
wxMenu *subMenu)
+ : wxMenuItemBase(parentMenu, id, text, help, kind, subMenu)
{
- m_id = id;
- m_parentMenu = parentMenu;
- m_subMenu = subMenu;
-
- m_text = text;
- m_help = help;
+ m_posY =
+ m_height = wxDefaultCoord;
- m_isCheckable = isCheckable;
- m_isEnabled = TRUE;
- m_isChecked = FALSE;
+ m_radioGroup.start = -1;
+ m_isRadioGroupStart = false;
- m_posY =
- m_height = -1;
+ m_bmpDisabled = wxNullBitmap;
UpdateAccelInfo();
}
int id,
const wxString& name,
const wxString& help,
- bool isCheckable,
+ wxItemKind kind,
wxMenu *subMenu)
{
- return new wxMenuItem(parentMenu, id, name, help, isCheckable, subMenu);
-}
-
-/* static */
-wxString wxMenuItemBase::GetLabelFromText(const wxString& text)
-{
- return wxStripMenuCodes(text);
+ return new wxMenuItem(parentMenu, id, name, help, kind, subMenu);
}
// ----------------------------------------------------------------------------
m_indexAccel = wxControl::FindAccelIndex(m_text);
// will be empty if the text contains no TABs - ok
- m_strAccel = m_text.AfterFirst(_T('\t'));
+ m_strAccel = m_text.AfterFirst(wxT('\t'));
}
-void wxMenuItem::SetText(const wxString& text)
+void wxMenuItem::SetItemLabel(const wxString& text)
{
if ( text != m_text )
{
// first call the base class version to change m_text
- wxMenuItemBase::SetText(text);
+ // (and also check if we don't have a stock menu item)
+ wxMenuItemBase::SetItemLabel(text);
UpdateAccelInfo();
void wxMenuItem::SetCheckable(bool checkable)
{
- if ( checkable != m_isCheckable )
+ if ( checkable != IsCheckable() )
{
wxMenuItemBase::SetCheckable(checkable);
void wxMenuItem::Check(bool check)
{
- if ( check != m_isChecked )
+ wxCHECK_RET( IsCheckable(), wxT("only checkable items may be checked") );
+
+ if ( m_isChecked == check )
+ return;
+
+ if ( GetKind() == wxITEM_RADIO )
{
- wxMenuItemBase::Check(check);
+ // it doesn't make sense to uncheck a radio item - what would this do?
+ if ( !check )
+ return;
- NotifyMenu();
+ // get the index of this item in the menu
+ const wxMenuItemList& items = m_parentMenu->GetMenuItems();
+ int pos = items.IndexOf(this);
+ wxCHECK_RET( pos != wxNOT_FOUND,
+ wxT("menuitem not found in the menu items list?") );
+
+ // get the radio group range
+ int start,
+ end;
+
+ if ( m_isRadioGroupStart )
+ {
+ // we already have all information we need
+ start = pos;
+ end = m_radioGroup.end;
+ }
+ else // next radio group item
+ {
+ // get the radio group end from the start item
+ start = m_radioGroup.start;
+ end = items.Item(start)->GetData()->m_radioGroup.end;
+ }
+
+ // also uncheck all the other items in this radio group
+ wxMenuItemIter node = items.Item(start);
+ for ( int n = start; n <= end && node; n++ )
+ {
+ if ( n != pos )
+ {
+ node->GetData()->m_isChecked = false;
+ }
+ node = node->GetNext();
+ }
}
+
+ wxMenuItemBase::Check(check);
+
+ NotifyMenu();
+}
+
+// radio group stuff
+// -----------------
+
+void wxMenuItem::SetAsRadioGroupStart()
+{
+ m_isRadioGroupStart = true;
+}
+
+void wxMenuItem::SetRadioGroupStart(int start)
+{
+ wxASSERT_MSG( !m_isRadioGroupStart,
+ wxT("should only be called for the next radio items") );
+
+ m_radioGroup.start = start;
+}
+
+void wxMenuItem::SetRadioGroupEnd(int end)
+{
+ wxASSERT_MSG( m_isRadioGroupStart,
+ wxT("should only be called for the first radio item") );
+
+ m_radioGroup.end = end;
}
// ----------------------------------------------------------------------------
m_menuShown = NULL;
- m_shouldShowMenu = FALSE;
+ m_shouldShowMenu = false;
+}
+
+wxMenuBar::wxMenuBar(size_t n, wxMenu *menus[], const wxString titles[], long WXUNUSED(style))
+{
+ Init();
+
+ for (size_t i = 0; i < n; ++i )
+ Append(menus[i], titles[i]);
}
void wxMenuBar::Attach(wxFrame *frame)
{
// maybe you really wanted to call Detach()?
- wxCHECK_RET( frame, _T("wxMenuBar::Attach(NULL) called") );
+ wxCHECK_RET( frame, wxT("wxMenuBar::Attach(NULL) called") );
wxMenuBarBase::Attach(frame);
else // not created yet, do it now
{
// we have no way to return the error from here anyhow :-(
- (void)Create(frame, -1);
+ (void)Create(frame, wxID_ANY);
SetCursor(wxCURSOR_ARROW);
SetFont(wxSystemSettings::GetFont(wxSYS_SYSTEM_FONT));
// calculate and set our height (it won't be changed any more)
- SetSize(-1, GetBestSize().y);
+ SetSize(wxDefaultCoord, GetBestSize().y);
}
// remember the last frame which had us to avoid unnecessarily reparenting
bool wxMenuBar::Insert(size_t pos, wxMenu *menu, const wxString& title)
{
if ( !wxMenuBarBase::Insert(pos, menu, title) )
- return FALSE;
+ return false;
wxMenuInfo *info = new wxMenuInfo(title);
m_menuInfos.Insert(info, pos);
RefreshAllItemsAfter(pos);
- return TRUE;
+ return true;
}
wxMenu *wxMenuBar::Replace(size_t pos, wxMenu *menu, const wxString& title)
void wxMenuBar::EnableTop(size_t pos, bool enable)
{
- wxCHECK_RET( pos < GetCount(), _T("invalid index in EnableTop") );
+ wxCHECK_RET( pos < GetCount(), wxT("invalid index in EnableTop") );
if ( enable != m_menuInfos[pos].IsEnabled() )
{
bool wxMenuBar::IsEnabledTop(size_t pos) const
{
- wxCHECK_MSG( pos < GetCount(), FALSE, _T("invalid index in IsEnabledTop") );
+ wxCHECK_MSG( pos < GetCount(), false, wxT("invalid index in IsEnabledTop") );
return m_menuInfos[pos].IsEnabled();
}
-void wxMenuBar::SetLabelTop(size_t pos, const wxString& label)
+void wxMenuBar::SetMenuLabel(size_t pos, const wxString& label)
{
- wxCHECK_RET( pos < GetCount(), _T("invalid index in EnableTop") );
+ wxCHECK_RET( pos < GetCount(), wxT("invalid index in SetMenuLabel") );
- if ( label != m_menuInfos[pos].GetLabel() )
+ if ( label != m_menuInfos[pos].GetOriginalLabel() )
{
m_menuInfos[pos].SetLabel(label);
//else: nothing to do
}
-wxString wxMenuBar::GetLabelTop(size_t pos) const
+wxString wxMenuBar::GetMenuLabel(size_t pos) const
{
- wxCHECK_MSG( pos < GetCount(), _T(""), _T("invalid index in GetLabelTop") );
+ wxCHECK_MSG( pos < GetCount(), wxEmptyString, wxT("invalid index in GetMenuLabel") );
- return m_menuInfos[pos].GetLabel();
+ return m_menuInfos[pos].GetOriginalLabel();
}
// ----------------------------------------------------------------------------
void wxMenuBar::RefreshItem(size_t pos)
{
wxCHECK_RET( pos != (size_t)-1,
- _T("invalid item in wxMenuBar::RefreshItem") );
+ wxT("invalid item in wxMenuBar::RefreshItem") );
if ( !IsCreated() )
{
wxRect wxMenuBar::GetItemRect(size_t pos) const
{
- wxASSERT_MSG( pos < GetCount(), _T("invalid menu bar item index") );
- wxASSERT_MSG( IsCreated(), _T("can't call this method yet") );
+ wxASSERT_MSG( pos < GetCount(), wxT("invalid menu bar item index") );
+ wxASSERT_MSG( IsCreated(), wxT("can't call this method yet") );
wxRect rect;
rect.x =
{
wxClientDC dc(wxConstCast(this, wxMenuBar));
dc.SetFont(wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT));
- dc.GetTextExtent(GetLabelTop(0), &size.x, &size.y);
+ dc.GetTextExtent(GetMenuLabel(0), &size.x, &size.y);
// adjust for the renderer we use
size = GetRenderer()->GetMenuBarItemSize(size);
void wxMenuBar::SelectMenu(size_t pos)
{
SetFocus();
- wxLogTrace(_T("mousecapture"), _T("Capturing mouse from wxMenuBar::SelectMenu"));
+ wxLogTrace(wxT("mousecapture"), wxT("Capturing mouse from wxMenuBar::SelectMenu"));
CaptureMouse();
DoSelectMenu(pos);
void wxMenuBar::DoSelectMenu(size_t pos)
{
- wxCHECK_RET( pos < GetCount(), _T("invalid menu index in DoSelectMenu") );
+ wxCHECK_RET( pos < GetCount(), wxT("invalid menu index in DoSelectMenu") );
int posOld = m_current;
if ( IsShowingMenu() )
{
// restore m_shouldShowMenu flag after DismissMenu() which resets
- // it to FALSE
+ // it to false
bool old = m_shouldShowMenu;
DismissMenu();
void wxMenuBar::PopupMenu(size_t pos)
{
- wxCHECK_RET( pos < GetCount(), _T("invalid menu index in PopupCurrentMenu") );
+ wxCHECK_RET( pos < GetCount(), wxT("invalid menu index in PopupCurrentMenu") );
SetFocus();
DoSelectMenu(pos);
}
else // on item
{
- wxLogTrace(_T("mousecapture"), _T("Capturing mouse from wxMenuBar::OnLeftDown"));
+ wxLogTrace(wxT("mousecapture"), wxT("Capturing mouse from wxMenuBar::OnLeftDown"));
CaptureMouse();
// show it as selected
RefreshItem((size_t)m_current);
// show the menu
- PopupCurrentMenu(FALSE /* don't select first item - as Windows does */);
+ PopupCurrentMenu(false /* don't select first item - as Windows does */);
}
}
}
static wxPoint s_ptLast;
if ( pt == s_ptLast )
{
- return FALSE;
+ return false;
}
s_ptLast = pt;
int currentNew = GetMenuFromPoint(pt);
if ( (currentNew == -1) || (currentNew == m_current) )
{
- return FALSE;
+ return false;
}
// select the new active item
if ( m_shouldShowMenu && !m_menuShown)
{
// open the new menu if the old one we closed had been opened
- PopupCurrentMenu(FALSE /* don't select first item - as Windows does */);
+ PopupCurrentMenu(false /* don't select first item - as Windows does */);
}
- return TRUE;
+ return true;
}
void wxMenuBar::OnKeyDown(wxKeyEvent& event)
{
// we always maintain a valid current item while we're in modal
// state (i.e. have the capture)
- wxFAIL_MSG( _T("how did we manage to lose current item?") );
+ wxFAIL_MSG( wxT("how did we manage to lose current item?") );
return;
}
// the menu when up/down one is
switch ( key )
{
- case WXK_MENU:
+ case WXK_ALT:
// Alt must be processed at wxWindow level too
event.Skip();
// fall through
int wxMenuBar::FindNextItemForAccel(int idxStart, int key, bool *unique) const
{
- if ( !wxIsalnum(key) )
+ if ( !wxIsalnum((wxChar)key) )
{
// we only support letters/digits as accels
return -1;
// do we have more than one item with this accel?
if ( unique )
- *unique = TRUE;
+ *unique = true;
// translate everything to lower case before comparing
- wxChar chAccel = wxTolower(key);
+ wxChar chAccel = (wxChar)wxTolower(key);
// the index of the item with this accel
int idxFound = -1;
int idxAccel = info.GetAccelIndex();
if ( idxAccel != -1 &&
- wxTolower(info.GetLabel()[(size_t)idxAccel])
- == chAccel )
+ (wxChar)wxTolower(info.GetLabel()[(size_t)idxAccel]) == chAccel )
{
// ok, found an item with this accel
if ( idxFound == -1 )
else // we already had found such item
{
if ( unique )
- *unique = FALSE;
+ *unique = false;
// no need to continue further, we won't find
// anything we don't already know
bool wxMenuBar::ProcessAccelEvent(const wxKeyEvent& event)
{
size_t n = 0;
- for ( wxMenuList::Node *node = m_menus.GetFirst();
+ for ( wxMenuList::compatibility_iterator node = m_menus.GetFirst();
node;
node = node->GetNext(), n++ )
{
if ( node->GetData()->ProcessAccelEvent(event) )
{
// menu processed it
- return TRUE;
+ return true;
}
}
}
// not found
- return FALSE;
+ return false;
}
#endif // wxUSE_ACCEL
void wxMenuBar::PopupCurrentMenu(bool selectFirst)
{
- wxCHECK_RET( m_current != -1, _T("no menu to popup") );
+ wxCHECK_RET( m_current != -1, wxT("no menu to popup") );
// forgot to call DismissMenu()?
- wxASSERT_MSG( !m_menuShown, _T("shouldn't show two menu at once!") );
+ wxASSERT_MSG( !m_menuShown, wxT("shouldn't show two menus at once!") );
// in any case, we should show it - even if we won't
- m_shouldShowMenu = TRUE;
+ m_shouldShowMenu = true;
if ( IsEnabledTop(m_current) )
{
void wxMenuBar::DismissMenu()
{
- wxCHECK_RET( m_menuShown, _T("can't dismiss menu if none is shown") );
+ wxCHECK_RET( m_menuShown, wxT("can't dismiss menu if none is shown") );
m_menuShown->Dismiss();
OnDismissMenu();
void wxMenuBar::OnDismissMenu(bool dismissMenuBar)
{
- m_shouldShowMenu = FALSE;
+ m_shouldShowMenu = false;
m_menuShown = NULL;
if ( dismissMenuBar )
{
void wxMenuBar::OnDismiss()
{
- if ( GetCapture() )
+ if ( ReleaseMouseCapture() )
{
- wxLogTrace(_T("mousecapture"), _T("Releasing mouse from wxMenuBar::OnDismiss"));
- GetCapture()->ReleaseMouse();
+ wxLogTrace(wxT("mousecapture"), wxT("Releasing mouse from wxMenuBar::OnDismiss"));
}
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();
bool wxWindow::DoPopupMenu(wxMenu *menu, int x, int y)
{
- wxCHECK_MSG( !ms_evtLoopPopup, FALSE,
- _T("can't show more than one popup menu at a time") );
+ wxCHECK_MSG( !ms_evtLoopPopup, false,
+ wxT("can't show more than one popup menu at a time") );
#ifdef __WXMSW__
// we need to change the cursor before showing the menu as, apparently, no
wxLog::FlushActive();
// some controls update themselves from OnIdle() call - let them do it
- wxIdleEvent event;
- wxTheApp->ProcessEvent(event);
+ wxTheApp->ProcessIdle();
// if the window hadn't been refreshed yet, the menu can adversely affect
// its next OnPaint() handler execution - i.e. scrolled window refresh
Update();
#endif // 0
- menu->SetInvokingWindow(this);
- menu->Popup(ClientToScreen(wxPoint(x, y)), wxSize(0, 0));
+ menu->Popup(ClientToScreen(wxPoint(x, y)), wxSize(0,0));
// this is not very useful if the menu was popped up because of the mouse
// click but I think it is nice to do when it appears because of a key
ms_evtLoopPopup = new wxEventLoop;
ms_evtLoopPopup->Run();
- delete ms_evtLoopPopup;
- ms_evtLoopPopup = NULL;
+ wxDELETE(ms_evtLoopPopup);
// remove the handler
- PopEventHandler(TRUE /* delete it */);
-
- menu->SetInvokingWindow(NULL);
+ PopEventHandler(true /* delete it */);
#ifdef __WXMSW__
SetCursor(cursorOld);
#endif // __WXMSW__
- return TRUE;
+ return true;
}
void wxWindow::DismissPopupMenu()
{
- wxCHECK_RET( ms_evtLoopPopup, _T("no popup menu shown") );
+ wxCHECK_RET( ms_evtLoopPopup, wxT("no popup menu shown") );
ms_evtLoopPopup->Exit();
}
#endif // wxUSE_MENUS
-