diff --git a/docs/changes.txt b/docs/changes.txt index dca685aacb..a9f6d35cbd 100644 --- a/docs/changes.txt +++ b/docs/changes.txt @@ -93,6 +93,7 @@ All (GUI): - Add wxUIActionSimulator::Select(). - Add wxOwnerDrawnComboBox::Is{List,Text}Empty() methods. - Fix creating/removing mode buttons in wxPG manager (Artur Wieczorek). +- Harmonize wxMenuEvent handling between all major ports. wxGTK: diff --git a/include/wx/menu.h b/include/wx/menu.h index 9e980665e6..6ca2205ddf 100644 --- a/include/wx/menu.h +++ b/include/wx/menu.h @@ -309,6 +309,13 @@ public: // the checked parameter may have boolean value or -1 for uncheckable items bool SendEvent(int itemid, int checked = -1); + // called to dispatch a wxMenuEvent to the right recipients, menu pointer + // can be NULL if we failed to find the associated menu (this happens at + // least in wxMSW for the events from the system menus) + static + bool ProcessMenuEvent(wxMenu* menu, wxMenuEvent& event, wxWindow* win); + + // compatibility: these functions are deprecated, use the new ones instead // ----------------------------------------------------------------------- @@ -390,8 +397,8 @@ protected: private: - // Helper of SendEvent(): sends the event to its intended recipients, - // returns true if it was processed. + // Common part of SendEvent() and ProcessMenuEvent(): sends the event to + // its intended recipients, returns true if it was processed. static bool DoProcessEvent(wxMenuBase* menu, wxEvent& event, wxWindow* win); wxDECLARE_NO_COPY_CLASS(wxMenuBase); diff --git a/interface/wx/event.h b/interface/wx/event.h index 6d249a01fc..b1702d3a9b 100644 --- a/interface/wx/event.h +++ b/interface/wx/event.h @@ -4107,8 +4107,24 @@ public: these do not include menu command events, which are handled using wxCommandEvent objects. - The default handler for @c wxEVT_MENU_HIGHLIGHT displays help - text in the first field of the status bar. + Events of this class are generated by both menus that are part of a + wxMenuBar, attached to wxFrame, and popup menus shown by + wxWindow::PopupMenu(). They are sent to the following objects until one of + them handles the event: + + -# The menu object itself, as returned by GetMenu(), if any. + -# The wxMenuBar to which this menu is attached, if any. + -# The window associated with the menu, e.g. the one calling + PopupMenu() for the popup menus. + -# The top level parent of that window if it's different from the + window itself. + + This is similar to command events generated by the menu items, but, unlike + them, wxMenuEvent are only sent to the window itself and its top level + parent but not any intermediate windows in the hierarchy. + + The default handler for @c wxEVT_MENU_HIGHLIGHT in wxFrame displays help + text in the status bar, see wxFrame::SetStatusBarPane(). @beginEventTable{wxMenuEvent} @event{EVT_MENU_OPEN(func)} diff --git a/src/common/menucmn.cpp b/src/common/menucmn.cpp index ca8f1f57a2..031ba7e5e3 100644 --- a/src/common/menucmn.cpp +++ b/src/common/menucmn.cpp @@ -651,28 +651,39 @@ bool wxMenuBase::DoProcessEvent(wxMenuBase* menu, wxEvent& event, wxWindow* win) { event.SetEventObject(menu); - wxMenuBar* const mb = menu->GetMenuBar(); - - // Try the menu's event handler first - wxEvtHandler *handler = menu->GetEventHandler(); - if ( handler ) + if ( menu ) { - // Indicate to the event processing code that we're going to pass this - // event to another handler if it's not processed here to prevent it - // from passing the event to wxTheApp: this will be done below if we do - // have the associated window. - if ( win || mb ) - event.SetWillBeProcessedAgain(); + wxMenuBar* const mb = menu->GetMenuBar(); - if ( handler->SafelyProcessEvent(event) ) - return true; + // Try the menu's event handler first + wxEvtHandler *handler = menu->GetEventHandler(); + if ( handler ) + { + // Indicate to the event processing code that we're going to pass + // this event to another handler if it's not processed here to + // prevent it from passing the event to wxTheApp: this will be done + // below if we do have the associated window. + if ( win || mb ) + event.SetWillBeProcessedAgain(); + + if ( handler->SafelyProcessEvent(event) ) + return true; + } + + // If this menu is part of the menu bar, try the event there. this + if ( mb ) + { + if ( mb->HandleWindowEvent(event) ) + return true; + + // If this already propagated it upwards to the window containing + // the menu bar, we don't have to handle it in this window again + // below. + if ( event.ShouldPropagate() ) + return false; + } } - // If this menu is part of the menu bar, process the event there: this will - // also propagate it upwards to the window containing the menu bar. - if ( mb ) - return mb->HandleWindowEvent(event); - // Try the window the menu was popped up from. if ( win ) return win->HandleWindowEvent(event); @@ -681,6 +692,24 @@ bool wxMenuBase::DoProcessEvent(wxMenuBase* menu, wxEvent& event, wxWindow* win) return false; } +/* static */ +bool +wxMenuBase::ProcessMenuEvent(wxMenu* menu, wxMenuEvent& event, wxWindow* win) +{ + // Try to process the event in the usual places first. + if ( DoProcessEvent(menu, event, win) ) + return true; + + // But the menu events should also reach the TLW parent if they were not + // processed before so, as it's not a command event and hence doesn't + // bubble up by default, send it there explicitly if not done yet. + wxWindow* const tlw = wxGetTopLevelParent(win); + if ( tlw != win && tlw->HandleWindowEvent(event) ) + return true; + + return false; +} + // ---------------------------------------------------------------------------- // wxMenu attaching/detaching to/from menu bar // ---------------------------------------------------------------------------- diff --git a/src/gtk/menu.cpp b/src/gtk/menu.cpp index 3c5426dc80..1244df1723 100644 --- a/src/gtk/menu.cpp +++ b/src/gtk/menu.cpp @@ -74,16 +74,7 @@ static void DoCommonMenuCallbackCode(wxMenu *menu, wxMenuEvent& event) if ( !IsMenuEventAllowed(menu) ) return; - event.SetEventObject( menu ); - - wxEvtHandler* handler = menu->GetEventHandler(); - if (handler && handler->SafelyProcessEvent(event)) - return; - - wxWindow *win = menu->GetWindow(); - wxCHECK_RET( win, "event for a menu without associated window?" ); - - win->HandleWindowEvent( event ); + wxMenu::ProcessMenuEvent(menu, event, menu->GetWindow()); } // Return the top level menu containing this menu (possibly this menu itself). diff --git a/src/msw/window.cpp b/src/msw/window.cpp index 6f2d4b3f15..62e5879935 100644 --- a/src/msw/window.cpp +++ b/src/msw/window.cpp @@ -2269,9 +2269,7 @@ wxWindowMSW::HandleMenuSelect(WXWORD nItem, WXWORD flags, WXHMENU hMenu) item = wxID_NONE; wxMenuEvent event(wxEVT_MENU_HIGHLIGHT, item); - event.SetEventObject(this); - - if ( HandleWindowEvent(event) ) + if ( wxMenu::ProcessMenuEvent(MSWFindMenuFromHMENU(hMenu), event, this) ) return true; // by default, i.e. if the event wasn't handled above, clear the status bar @@ -2291,9 +2289,8 @@ bool wxWindowMSW::DoSendMenuOpenCloseEvent(wxEventType evtType, wxMenu* menu) { wxMenuEvent event(evtType, menu && !menu->IsAttached() ? wxID_ANY : 0, menu); - event.SetEventObject(menu); - return HandleWindowEvent(event); + return wxMenu::ProcessMenuEvent(menu, event, this); } bool wxWindowMSW::HandleMenuPopup(wxEventType evtType, WXHMENU hMenu) diff --git a/src/osx/menu_osx.cpp b/src/osx/menu_osx.cpp index 88676f78a2..16debbdf7a 100644 --- a/src/osx/menu_osx.cpp +++ b/src/osx/menu_osx.cpp @@ -555,22 +555,7 @@ void wxMenu::HandleMenuClosed() bool wxMenu::DoHandleMenuEvent(wxEvent& wxevent) { - wxevent.SetEventObject(this); - wxEvtHandler* handler = GetEventHandler(); - if (handler && handler->ProcessEvent(wxevent)) - { - return true; - } - else - { - wxWindow *win = GetWindow(); - if (win) - { - if ( win->HandleWindowEvent(wxevent) ) - return true; - } - } - return false; + return ProcessMenuEvent(this, wxevent, GetWindow()); } // Menu Bar