diff --git a/docs/changes.txt b/docs/changes.txt index c785d23c77..d40be39dc9 100644 --- a/docs/changes.txt +++ b/docs/changes.txt @@ -112,6 +112,7 @@ All (GUI): - Add support for style="page-break-inside:avoid" to
in wxHTML. - Support strike-through in wxDataViewItem attributes (approach, Igor Korot). - Allow distinguishing between user- and script-opened windows in wxWebView. +- Allow binding to events generated by their items in submenus too. wxGTK: diff --git a/interface/wx/menu.h b/interface/wx/menu.h index 3157e3a75b..ace479f0dc 100644 --- a/interface/wx/menu.h +++ b/interface/wx/menu.h @@ -482,18 +482,20 @@ public: @section menu_eventhandling Event handling - If the menu is part of a menubar, then wxMenuBar event processing is used. + Event handlers for the commands generated by the menu items can be + connected directly to the menu object itself using wxEvtHandler::Bind(). If + this menu is a submenu of another one, the events from its items can also + be processed in the parent menu and so on, recursively. - With a popup menu (see wxWindow::PopupMenu), there is a variety of ways to - handle a menu selection event (@c wxEVT_MENU): - - Provide @c EVT_MENU handlers in the window which pops up the menu, or in an - ancestor of that window (the simplest method); - - Derive a new class from wxMenu and define event table entries using the @c EVT_MENU macro; - - Set a new event handler for wxMenu, through wxEvtHandler::SetNextHandler, - specifying an object whose class has @c EVT_MENU entries; + If the menu is part of a menu bar, then events can also be handled in + wxMenuBar object. - Note that instead of static @c EVT_MENU macros you can also use dynamic - connection; see @ref overview_events_bind. + Finally, menu events can also be handled in the associated window, which is + either the wxFrame associated with the menu bar this menu belongs to or the + window for which wxWindow::PopupMenu() was called for the popup menus. + + See @ref overview_events_bind for how to bind event handlers to the various + objects. @library{wxcore} @category{menus} diff --git a/src/common/menucmn.cpp b/src/common/menucmn.cpp index 7c12f30765..1c6fd085df 100644 --- a/src/common/menucmn.cpp +++ b/src/common/menucmn.cpp @@ -648,11 +648,12 @@ bool wxMenuBase::DoProcessEvent(wxMenuBase* menu, wxEvent& event, wxWindow* win) { event.SetEventObject(menu); - if ( menu ) - { - wxMenuBar* const mb = menu->GetMenuBar(); + wxMenuBar* const mb = menu ? menu->GetMenuBar() : NULL; - // Try the menu's event handler first + // Process event in the menu itself and all its parent menus, if it's a + // submenu, first. + for ( ; menu; menu = menu->GetParent() ) + { wxEvtHandler *handler = menu->GetEventHandler(); if ( handler ) { @@ -666,19 +667,19 @@ bool wxMenuBase::DoProcessEvent(wxMenuBase* menu, wxEvent& event, wxWindow* win) 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 menu is part of the menu bar, try the event there. + 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 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; } // Try the window the menu was popped up from. diff --git a/src/msw/menu.cpp b/src/msw/menu.cpp index 4ebd6a5d2d..4705b802c9 100644 --- a/src/msw/menu.cpp +++ b/src/msw/menu.cpp @@ -795,7 +795,7 @@ bool wxMenu::MSWCommand(WXUINT WXUNUSED(param), WXWORD id_) } } - SendEvent(id, checked); + item->GetMenu()->SendEvent(id, checked); } return true; diff --git a/tests/events/propagation.cpp b/tests/events/propagation.cpp index 8c58d662fd..c0d302b7ee 100644 --- a/tests/events/propagation.cpp +++ b/tests/events/propagation.cpp @@ -445,11 +445,14 @@ wxMenu* CreateTestMenu(wxFrame* frame) // reliable than using wxUIActionSimulator and currently works in all ports as // they all call wxMenuBase::SendEvent() from their respective menu event // handlers. -#define ASSERT_MENU_EVENT_RESULT(menu, result) \ - g_str.clear(); \ - menu->SendEvent(wxID_APPLY); \ +#define ASSERT_MENU_EVENT_RESULT_FOR(cmd, menu, result) \ + g_str.clear(); \ + menu->SendEvent(cmd); \ CHECK( g_str == result ) +#define ASSERT_MENU_EVENT_RESULT(menu, result) \ + ASSERT_MENU_EVENT_RESULT_FOR(wxID_APPLY, menu, result) + void EventPropagationTestCase::MenuEvent() { wxFrame* const frame = static_cast(wxTheApp->GetTopWindow()); @@ -472,6 +475,17 @@ void EventPropagationTestCase::MenuEvent() ASSERT_MENU_EVENT_RESULT( menu, "aomA" ); + // Check that a handler can also be attached to a submenu. + wxMenu* const submenu = new wxMenu; + submenu->Append(wxID_ABOUT); + menu->Append(wxID_ANY, "Submenu", submenu); + + TestMenuEvtHandler hs('s'); // 's' for "submenu" + submenu->SetNextHandler(&hs); + wxON_BLOCK_EXIT_OBJ1( *submenu, + wxEvtHandler::SetNextHandler, (wxEvtHandler*)NULL ); + ASSERT_MENU_EVENT_RESULT_FOR( wxID_ABOUT, submenu, "aosomA" ); + // Test that the event handler associated with the menu bar gets the event. TestMenuEvtHandler hb('b'); // 'b' for "menu Bar" mb->PushEventHandler(&hb);