// 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 "menuitem.h"
- #pragma implementation "menu.h"
+#if defined(__GNUG__) && !defined(NO_GCC_PRAGMA)
+ #pragma implementation "univmenuitem.h"
+ #pragma implementation "univmenu.h"
#endif
#include "wx/wxprec.h"
{
wxSize size;
wxClientDC dc(menubar);
- dc.SetFont(wxSystemSettings::GetSystemFont(wxSYS_DEFAULT_GUI_FONT));
+ dc.SetFont(wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT));
dc.GetTextExtent(m_label, &size.x, &size.y);
// adjust for the renderer we use and store the width
public:
wxPopupMenuWindow(wxWindow *parent, wxMenu *menu);
+ ~wxPopupMenuWindow();
+
// override the base class version to select the first item initially
virtual void Popup(wxWindow *focus = NULL);
}
// find the menu item at given position
- wxMenuItemList::Node *GetMenuItemFromPoint(const wxPoint& pt) const;
+ wxMenuItemList::compatibility_iterator GetMenuItemFromPoint(const wxPoint& pt) const;
// refresh the given item
void RefreshItem(wxMenuItem *item);
void ResetCurrent();
// set the current node and item withotu refreshing anything
- void SetCurrent(wxMenuItemList::Node *node);
+ void SetCurrent(wxMenuItemList::compatibility_iterator node);
+ virtual bool SetCurrent(bool doit = true){return wxPopupTransientWindow::SetCurrent(doit);};
// change the current item refreshing the old and new items
- void ChangeCurrent(wxMenuItemList::Node *node);
+ void ChangeCurrent(wxMenuItemList::compatibility_iterator 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::Node *GetPrevNode() const;
+ wxMenuItemList::compatibility_iterator GetPrevNode() const;
// get previous node before the given one, wrapping if it's the first one
- wxMenuItemList::Node *GetPrevNode(wxMenuItemList::Node *node) const;
+ wxMenuItemList::compatibility_iterator GetPrevNode(wxMenuItemList::compatibility_iterator node) const;
// get next node after the current one
- wxMenuItemList::Node *GetNextNode() const;
+ wxMenuItemList::compatibility_iterator GetNextNode() const;
// get next node after the given one, wrapping if it's the last one
- wxMenuItemList::Node *GetNextNode(wxMenuItemList::Node *node) const;
+ wxMenuItemList::compatibility_iterator GetNextNode(wxMenuItemList::compatibility_iterator node) const;
private:
// the menu we show
wxMenu *m_menu;
// the menu node corresponding to the current item
- wxMenuItemList::Node *m_nodeCurrent;
+ wxMenuItemList::compatibility_iterator m_nodeCurrent;
// do we currently have an opened submenu?
bool m_hasOpenSubMenu;
}
else
{
+ // return FALSE;
+
return wxEvtHandler::ProcessEvent(event);
}
}
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);
+#if wxUSE_STL
+ SetCurrent(wxMenuItemList::compatibility_iterator());
+#else
+ SetCurrent((wxwxMenuItemListNode *)NULL);
+#endif
}
-void wxPopupMenuWindow::SetCurrent(wxMenuItemList::Node *node)
+void wxPopupMenuWindow::SetCurrent(wxMenuItemList::compatibility_iterator node)
{
m_nodeCurrent = node;
}
-void wxPopupMenuWindow::ChangeCurrent(wxMenuItemList::Node *node)
+void wxPopupMenuWindow::ChangeCurrent(wxMenuItemList::compatibility_iterator node)
{
if ( node != m_nodeCurrent )
{
- if ( m_nodeCurrent )
+ wxMenuItemList::compatibility_iterator nodeOldCurrent = m_nodeCurrent;
+
+ m_nodeCurrent = node;
+
+ if ( nodeOldCurrent )
{
- wxMenuItem *item = m_nodeCurrent->GetData();
+ wxMenuItem *item = nodeOldCurrent->GetData();
wxCHECK_RET( item, _T("no current item?") );
// if it was the currently opened menu, close it
RefreshItem(item);
}
- m_nodeCurrent = node;
-
if ( m_nodeCurrent )
RefreshItem(m_nodeCurrent->GetData());
}
}
-wxMenuItemList::Node *wxPopupMenuWindow::GetPrevNode() const
+wxMenuItemList::compatibility_iterator wxPopupMenuWindow::GetPrevNode() const
{
- wxMenuItemList::Node *node = m_nodeCurrent;
- if ( !node )
- {
- // start from the end if no current item
- node = m_menu->GetMenuItems().GetLast();
- }
-
- return GetPrevNode(node);
+ // return the last node if there had been no previously selected one
+ return m_nodeCurrent ? GetPrevNode(m_nodeCurrent)
+ : m_menu->GetMenuItems().GetLast();
}
-wxMenuItemList::Node *
-wxPopupMenuWindow::GetPrevNode(wxMenuItemList::Node *node) const
+wxMenuItemList::compatibility_iterator
+wxPopupMenuWindow::GetPrevNode(wxMenuItemList::compatibility_iterator node) const
{
if ( node )
{
return node;
}
-wxMenuItemList::Node *wxPopupMenuWindow::GetNextNode() const
+wxMenuItemList::compatibility_iterator wxPopupMenuWindow::GetNextNode() const
{
- wxMenuItemList::Node *node = m_nodeCurrent;
- if ( !node )
- {
- // start from the beginning if no current item
- node = m_menu->GetMenuItems().GetFirst();
- }
-
- return GetNextNode(node);
+ // return the first node if there had been no previously selected one
+ return m_nodeCurrent ? GetNextNode(m_nodeCurrent)
+ : m_menu->GetMenuItems().GetFirst();
}
-wxMenuItemList::Node *
-wxPopupMenuWindow::GetNextNode(wxMenuItemList::Node *node) const
+wxMenuItemList::compatibility_iterator
+wxPopupMenuWindow::GetNextNode(wxMenuItemList::compatibility_iterator node) const
{
if ( node )
{
// wxPopupMenuWindow geometry
// ----------------------------------------------------------------------------
-wxMenuItemList::Node *
+wxMenuItemList::compatibility_iterator
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 ( wxMenuItemList::compatibility_iterator node = m_menu->GetMenuItems().GetFirst();
node;
node = node->GetNext() )
{
}
}
+#if wxUSE_STL
+ return wxMenuItemList::compatibility_iterator();
+#else
return NULL;
+#endif
}
// ----------------------------------------------------------------------------
// never partially covered as it is always on top of everything
wxDC& dc = renderer->GetDC();
- dc.SetFont(wxSystemSettings::GetSystemFont(wxSYS_DEFAULT_GUI_FONT));
+ dc.SetFont(wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT));
// FIXME: this should be done in the renderer, however when it is fixed
// wxPopupMenuWindow::RefreshItem() should be changed too!
wxCoord y = 0;
const wxMenuGeometryInfo& gi = m_menu->GetGeometryInfo();
- for ( wxMenuItemList::Node *node = m_menu->GetMenuItems().GetFirst();
+ for ( wxMenuItemList::compatibility_iterator node = m_menu->GetMenuItems().GetFirst();
node;
node = node->GetNext() )
{
wxASSERT_MSG( !item->IsSeparator() && !item->IsSubMenu(),
_T("can't click this item") );
- m_menu->ClickItem(item);
+ wxMenu* menu = m_menu;
// close all menus
DismissAndNotify();
+
+ menu->ClickItem(item);
}
void wxPopupMenuWindow::OpenSubmenu(wxMenuItem *item, InputMethod how)
void wxPopupMenuWindow::OnLeftUp(wxMouseEvent& event)
{
- wxMenuItemList::Node *node = GetMenuItemFromPoint(event.GetPosition());
+ wxMenuItemList::compatibility_iterator node = GetMenuItemFromPoint(event.GetPosition());
if ( node )
{
ActivateItem(node->GetData(), WithMouse);
void wxPopupMenuWindow::ProcessMouseMove(const wxPoint& pt)
{
- wxMenuItemList::Node *node = GetMenuItemFromPoint(pt);
+ wxMenuItemList::compatibility_iterator node = GetMenuItemFromPoint(pt);
// don't reset current to NULL here, we only do it when the mouse leaves
// the window (see below)
if ( resetCurrent )
{
+#if wxUSE_STL
+ ChangeCurrent(wxMenuItemList::compatibility_iterator());
+#else
ChangeCurrent(NULL);
+#endif
}
}
{
bool up = key == WXK_UP;
- wxMenuItemList::Node *nodeStart = up ? GetPrevNode()
+ wxMenuItemList::compatibility_iterator nodeStart = up ? GetPrevNode()
: GetNextNode(),
- *node = nodeStart;
+ 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
}
}
// 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();
+ wxMenuItemList::compatibility_iterator 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::Node *node = nodeStart,
- *nodeFound = NULL;
+ wxMenuItemList::compatibility_iterator node = nodeStart,
+#if wxUSE_STL
+ nodeFound = wxMenuItemList::compatibility_iterator();
+#else
+ nodeFound = NULL;
+#endif
for ( ;; )
{
item = node->GetData();
m_geometry = NULL;
m_popupMenu = NULL;
+
+ m_startRadioGroup = -1;
}
wxMenu::~wxMenu()
}
}
+void wxMenu::EndRadioGroup()
+{
+ // we're not inside a radio group any longer
+ m_startRadioGroup = -1;
+}
+
bool wxMenu::DoAppend(wxMenuItem *item)
{
+ #if 0
+ // not used at all
+ bool check = FALSE;
+ #endif
+
+ 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);
+
+ // ensure that we have a checked item in the radio group
+ #if 0
+ // not used at all
+ check = TRUE;
+ #endif
+ }
+ else // extend the current radio group
+ {
+ // we need to update its end item
+ item->SetRadioGroupStart(m_startRadioGroup);
+ wxMenuItemList::compatibility_iterator node = GetMenuItems().Item(m_startRadioGroup);
+
+ if ( node )
+ {
+ node->GetData()->SetRadioGroupEnd(count);
+ }
+ else
+ {
+ wxFAIL_MSG( _T("where is the radio group start item?") );
+ }
+ }
+ }
+ else // not a radio item
+ {
+ EndRadioGroup();
+ }
+
if ( !wxMenuBase::DoAppend(item) )
return FALSE;
wxMenu *menu = GetParent();
while ( menu )
{
+ // We are a submenu of a menu of a menubar
+ if (menu->GetMenuBar())
+ return menu->GetMenuBar();
+
win = menu->GetInvokingWindow();
if ( win )
break;
wxCHECK_RET( m_invokingWindow, _T("what kind of menu is this?") );
m_invokingWindow->DismissPopupMenu();
- SetInvokingWindow(NULL);
+
+ // Why reset it here? We need it for sending the event to...
+ // SetInvokingWindow(NULL);
}
}
}
}
// try our submenus
- for ( wxMenuItemList::Node *node = GetMenuItems().GetFirst();
+ for ( wxMenuItemList::compatibility_iterator node = GetMenuItems().GetFirst();
node;
node = node->GetNext() )
{
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_isCheckable = isCheckable;
- m_isEnabled = TRUE;
- m_isChecked = FALSE;
-
m_posY =
m_height = -1;
+ m_radioGroup.start = -1;
+ m_isRadioGroupStart = FALSE;
+
UpdateAccelInfo();
}
int id,
const wxString& name,
const wxString& help,
- bool isCheckable,
+ wxItemKind kind,
wxMenu *subMenu)
{
- return new wxMenuItem(parentMenu, id, name, help, isCheckable, subMenu);
+ return new wxMenuItem(parentMenu, id, name, help, kind, subMenu);
}
/* static */
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,
+ _T("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
+ wxMenuItemList::compatibility_iterator 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,
+ _T("should only be called for the next radio items") );
+
+ m_radioGroup.start = start;
+}
+
+void wxMenuItem::SetRadioGroupEnd(int end)
+{
+ wxASSERT_MSG( m_isRadioGroupStart,
+ _T("should only be called for the first radio item") );
+
+ m_radioGroup.end = end;
}
// ----------------------------------------------------------------------------
SetCursor(wxCURSOR_ARROW);
- SetFont(wxSystemSettings::GetSystemFont(wxSYS_SYSTEM_FONT));
+ SetFont(wxSystemSettings::GetFont(wxSYS_SYSTEM_FONT));
+
+ // calculate and set our height (it won't be changed any more)
+ SetSize(-1, GetBestSize().y);
}
// remember the last frame which had us to avoid unnecessarily reparenting
void wxMenuBar::RefreshAllItemsAfter(size_t pos)
{
+ if ( !IsCreated() )
+ {
+ // no need to refresh if nothing is shown yet
+ return;
+ }
+
wxRect rect = GetItemRect(pos);
rect.width = GetClientSize().x - rect.x;
RefreshRect(rect);
wxCHECK_RET( pos != (size_t)-1,
_T("invalid item in wxMenuBar::RefreshItem") );
+ if ( !IsCreated() )
+ {
+ // no need to refresh if nothing is shown yet
+ return;
+ }
+
RefreshRect(GetItemRect(pos));
}
void wxMenuBar::DoDraw(wxControlRenderer *renderer)
{
wxDC& dc = renderer->GetDC();
- dc.SetFont(wxSystemSettings::GetSystemFont(wxSYS_DEFAULT_GUI_FONT));
+ dc.SetFont(wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT));
// redraw only the items which must be redrawn
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") );
wxRect rect;
rect.x =
if ( GetMenuCount() > 0 )
{
wxClientDC dc(wxConstCast(this, wxMenuBar));
- dc.SetFont(wxSystemSettings::GetSystemFont(wxSYS_DEFAULT_GUI_FONT));
+ dc.SetFont(wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT));
dc.GetTextExtent(GetLabelTop(0), &size.x, &size.y);
// adjust for the renderer we use
void wxMenuBar::SelectMenu(size_t pos)
{
SetFocus();
+ wxLogTrace(_T("mousecapture"), _T("Capturing mouse from wxMenuBar::SelectMenu"));
CaptureMouse();
DoSelectMenu(pos);
{
wxCHECK_RET( pos < GetCount(), _T("invalid menu index in DoSelectMenu") );
- if ( m_current != -1 )
+ int posOld = m_current;
+
+ m_current = pos;
+
+ if ( posOld != -1 )
{
// close the previous menu
if ( IsShowingMenu() )
m_shouldShowMenu = old;
}
- RefreshItem((size_t)m_current);
+ RefreshItem((size_t)posOld);
}
- m_current = pos;
-
RefreshItem(pos);
}
}
else // on item
{
+ wxLogTrace(_T("mousecapture"), _T("Capturing mouse from wxMenuBar::OnLeftDown"));
CaptureMouse();
// show it as selected
// show the menu if we know that we should, even if we hadn't been showing
// it before (this may happen if the previous menu was disabled)
- if ( m_shouldShowMenu )
+ 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 */);
void wxMenuBar::OnKeyDown(wxKeyEvent& event)
{
- // the current item must have been set before
- wxCHECK_RET( m_current != -1, _T("where is current item?") );
+ // ensure that we have a current item - we might not have it if we're
+ // given the focus with Alt or F10 press (and under GTK+ the menubar
+ // somehow gets the keyboard events even when it doesn't have focus...)
+ if ( m_current == -1 )
+ {
+ if ( !HasCapture() )
+ {
+ SelectMenu(0);
+ }
+ else // we do have capture
+ {
+ // 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?") );
+
+ return;
+ }
+ }
int key = event.GetKeyCode();
}
else // right
{
- if ( ++currentNew == (int)count )
+ if ( ++currentNew == count )
currentNew = 0;
}
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++ )
{
wxCHECK_RET( m_current != -1, _T("no menu to popup") );
// forgot to call DismissMenu()?
- wxASSERT_MSG( !m_menuShown, _T("shouldn't show two menu at once!") );
+ wxASSERT_MSG( !m_menuShown, _T("shouldn't show two menus at once!") );
// in any case, we should show it - even if we won't
m_shouldShowMenu = TRUE;
// that we pass 0 as width to position the menu exactly below the
// item, not to the right of it
wxRect rectItem = GetItemRect(m_current);
+
m_menuShown->Popup(ClientToScreen(rectItem.GetPosition()),
wxSize(0, rectItem.GetHeight()),
selectFirst);
void wxMenuBar::OnDismiss()
{
- ReleaseCapture();
+ if ( GetCapture() )
+ {
+ wxLogTrace(_T("mousecapture"), _T("Releasing mouse from wxMenuBar::OnDismiss"));
+ GetCapture()->ReleaseMouse();
+ }
if ( m_current != -1 )
{
- RefreshItem((size_t)m_current);
-
+ size_t current = m_current;
m_current = -1;
+
+ RefreshItem(current);
}
GiveAwayFocus();
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
#endif // 0
menu->SetInvokingWindow(this);
+
+ // wxLogDebug( "Name of invoking window %s", menu->GetInvokingWindow()->GetName().c_str() );
+
menu->Popup(ClientToScreen(wxPoint(x, y)), wxSize(0, 0));
// this is not very useful if the menu was popped up because of the mouse