Handle WM_*MENU* events in wxWindow.

Contrary to MSDN implications, at least some of these messages are not
actually sent to the TLW for popup menus, but to the owning window or
even its parent window (!).

Move the handling of these events from wxTLW to wxWindow.  Move menu
depth tracking to wxFrame, because it only makes sense for frame's
menus and move DoGiveHelp() from wxTLW to wxFrame.

git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@76721 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
This commit is contained in:
Václav Slavík 2014-06-18 12:51:39 +00:00
parent 8847a9f499
commit 3821abef51
8 changed files with 166 additions and 185 deletions

View File

@ -173,7 +173,7 @@ public:
// show help text for the currently selected menu or toolbar item
// (typically in the status bar) or hide it and restore the status bar text
// originally shown before the menu was opened if show == false
virtual void DoGiveHelp(const wxString& text, bool show) wxOVERRIDE;
virtual void DoGiveHelp(const wxString& text, bool show);
#endif
virtual bool IsClientAreaChild(const wxWindow *child) const wxOVERRIDE

View File

@ -104,6 +104,9 @@ public:
// normal frames but is overridden by wxMDIParentFrame
virtual WXHMENU MSWGetActiveMenu() const { return m_hMenu; }
virtual bool HandleMenuSelect(WXWORD nItem, WXWORD nFlags, WXHMENU hMenu);
virtual bool DoSendMenuOpenCloseEvent(wxEventType evtType, wxMenu* menu, bool popup);
// Look up the menu in the menu bar.
virtual wxMenu* MSWFindMenuFromHMENU(WXHMENU hMenu);
#endif // wxUSE_MENUS
@ -150,6 +153,10 @@ protected:
#if wxUSE_MENUS
// frame menu, NULL if none
WXHMENU m_hMenu;
// The number of currently opened menus: 0 initially, 1 when a top level
// menu is opened, 2 when its submenu is opened and so on.
int m_menuDepth;
#endif // wxUSE_MENUS
private:

View File

@ -121,22 +121,6 @@ public:
// returns true if the platform should explicitly apply a theme border
virtual bool CanApplyThemeBorder() const { return false; }
#if wxUSE_MENUS && !defined(__WXUNIVERSAL__)
bool HandleMenuSelect(WXWORD nItem, WXWORD nFlags, WXHMENU hMenu);
// handle WM_EXITMENULOOP message for Win95 only
bool HandleExitMenuLoop(WXWORD isPopup);
// handle WM_(UN)INITMENUPOPUP message to generate wxEVT_MENU_OPEN/CLOSE
bool HandleMenuPopup(wxEventType evtType, WXHMENU hMenu);
// Command part of HandleMenuPopup() and HandleExitMenuLoop().
bool DoSendMenuOpenCloseEvent(wxEventType evtType, wxMenu* menu, bool popup);
// Find the menu corresponding to the given handle.
virtual wxMenu* MSWFindMenuFromHMENU(WXHMENU hMenu);
#endif // wxUSE_MENUS && !__WXUNIVERSAL__
protected:
// common part of all ctors
void Init();
@ -251,10 +235,6 @@ private:
// MSWGetSystemMenu(). Owned by this window.
wxMenu *m_menuSystem;
// The number of currently opened menus: 0 initially, 1 when a top level
// menu is opened, 2 when its submenu is opened and so on.
int m_menuDepth;
DECLARE_EVENT_TABLE()
wxDECLARE_NO_COPY_CLASS(wxTopLevelWindowMSW);
};

View File

@ -535,6 +535,19 @@ public:
// behaviour
virtual void OnInternalIdle();
#if wxUSE_MENUS && !defined(__WXUNIVERSAL__)
virtual bool HandleMenuSelect(WXWORD nItem, WXWORD nFlags, WXHMENU hMenu);
// handle WM_(UN)INITMENUPOPUP message to generate wxEVT_MENU_OPEN/CLOSE
bool HandleMenuPopup(wxEventType evtType, WXHMENU hMenu);
// Command part of HandleMenuPopup() and HandleExitMenuLoop().
virtual bool DoSendMenuOpenCloseEvent(wxEventType evtType, wxMenu* menu, bool popup);
// Find the menu corresponding to the given handle.
virtual wxMenu* MSWFindMenuFromHMENU(WXHMENU hMenu);
#endif // wxUSE_MENUS && !__WXUNIVERSAL__
protected:
// this allows you to implement standard control borders without
// repeating the code in different classes that are not derived from

View File

@ -292,13 +292,6 @@ public:
virtual void SetRepresentedFilename(const wxString& WXUNUSED(filename)) { }
#if wxUSE_MENUS || wxUSE_TOOLBAR
// show help text for the currently selected menu or toolbar item
// (typically in the status bar) or hide it and restore the status bar text
// originally shown before the menu was opened if show == false
virtual void DoGiveHelp(const wxString& WXUNUSED(text), bool WXUNUSED(show)) {}
#endif
protected:
// the frame client to screen translation should take account of the
// toolbar which may shift the origin of the client area

View File

@ -96,6 +96,7 @@ void wxFrame::Init()
{
#if wxUSE_MENUS
m_hMenu = NULL;
m_menuDepth = 0;
#endif // wxUSE_MENUS
#if wxUSE_TOOLTIPS
@ -429,12 +430,52 @@ void wxFrame::InternalSetMenuBar()
#endif // wxUSE_MENUS_NATIVE
#if wxUSE_MENUS
#if wxUSE_MENUS && !defined(__WXUNIVERSAL__)
bool wxFrame::HandleMenuSelect(WXWORD nItem, WXWORD flags, WXHMENU hMenu)
{
// Unfortunately we need to ignore a message which is sent after
// closing the currently active submenu of the menu bar by pressing Escape:
// in this case we get WM_UNINITMENUPOPUP, from which we generate
// wxEVT_MENU_CLOSE, and _then_ we get WM_MENUSELECT for the top level menu
// from which we overwrite the help string just restored by OnMenuClose()
// handler in wxFrameBase. To prevent this from happening we discard these
// messages but only in the case it's really the top level menu as we still
// need to clear the help string when a submenu is selected in a menu.
if ( flags == (MF_POPUP | MF_HILITE) && !m_menuDepth )
return false;
return wxWindow::HandleMenuSelect(nItem, flags, hMenu);
}
bool wxFrame::DoSendMenuOpenCloseEvent(wxEventType evtType, wxMenu* menu, bool popup)
{
// Update the menu depth when dealing with the top level menus.
if ( !popup )
{
if ( evtType == wxEVT_MENU_OPEN )
{
m_menuDepth++;
}
else if ( evtType == wxEVT_MENU_CLOSE )
{
wxASSERT_MSG( m_menuDepth > 0, wxS("No open menus?") );
m_menuDepth--;
}
else
{
wxFAIL_MSG( wxS("Unexpected menu event type") );
}
}
return wxWindow::DoSendMenuOpenCloseEvent(evtType, menu, popup);
}
wxMenu* wxFrame::MSWFindMenuFromHMENU(WXHMENU hMenu)
{
return GetMenuBar() ? GetMenuBar()->MSWGetMenu(hMenu) : NULL;
}
#endif // wxUSE_MENUS
#endif // wxUSE_MENUS && !defined(__WXUNIVERSAL__)
// Responds to colour changes, and passes event on to children.
void wxFrame::OnSysColourChanged(wxSysColourChangedEvent& event)

View File

@ -63,15 +63,6 @@
#define ICON_SMALL 0
#endif
// ----------------------------------------------------------------------------
// globals
// ----------------------------------------------------------------------------
#if wxUSE_MENUS || wxUSE_MENUS_NATIVE
extern wxMenu *wxCurrentPopupMenu;
#endif // wxUSE_MENUS || wxUSE_MENUS_NATIVE
// ----------------------------------------------------------------------------
// stubs for missing functions under MicroWindows
// ----------------------------------------------------------------------------
@ -153,7 +144,6 @@ void wxTopLevelWindowMSW::Init()
#endif
m_menuSystem = NULL;
m_menuDepth = 0;
}
WXDWORD wxTopLevelWindowMSW::MSWGetStyle(long style, WXDWORD *exflags) const
@ -424,38 +414,6 @@ WXLRESULT wxTopLevelWindowMSW::MSWWindowProc(WXUINT message, WXWPARAM wParam, WX
#endif // #ifndef __WXUNIVERSAL__
}
break;
#if !defined(__WXMICROWIN__) && !defined(__WXWINCE__)
#if wxUSE_MENUS && !defined(__WXUNIVERSAL__)
case WM_INITMENUPOPUP:
processed = HandleMenuPopup(wxEVT_MENU_OPEN, (WXHMENU)wParam);
break;
case WM_MENUSELECT:
{
WXWORD item, flags;
WXHMENU hmenu;
UnpackMenuSelect(wParam, lParam, &item, &flags, &hmenu);
processed = HandleMenuSelect(item, flags, hmenu);
}
break;
case WM_EXITMENULOOP:
// Under Windows 98 and 2000 and later we're going to get
// WM_UNINITMENUPOPUP which will be used to generate this event
// with more information (notably the menu that was closed) so we
// only need this one under old Windows systems where the newer
// event is never sent.
if ( wxGetWinVersion() < wxWinVersion_98 )
processed = HandleExitMenuLoop(wParam);
break;
case WM_UNINITMENUPOPUP:
processed = HandleMenuPopup(wxEVT_MENU_CLOSE, (WXHMENU)wParam);
break;
#endif // wxUSE_MENUS && !__WXUNIVERSAL__
#endif // !__WXMICROWIN__
}
if ( !processed )
@ -1476,117 +1434,6 @@ void wxTopLevelWindowMSW::OnActivate(wxActivateEvent& event)
}
}
#if wxUSE_MENUS && !defined(__WXUNIVERSAL__)
bool
wxTopLevelWindowMSW::HandleMenuSelect(WXWORD nItem, WXWORD flags, WXHMENU hMenu)
{
// Ignore the special messages generated when the menu is closed (this is
// the only case when the flags are set to -1), in particular don't clear
// the help string in the status bar when this happens as it had just been
// restored by the base class code.
if ( !hMenu && flags == 0xffff )
return false;
// Unfortunately we also need to ignore another message which is sent after
// closing the currently active submenu of the menu bar by pressing Escape:
// in this case we get WM_UNINITMENUPOPUP, from which we generate
// wxEVT_MENU_CLOSE, and _then_ we get WM_MENUSELECT for the top level menu
// from which we overwrite the help string just restored by OnMenuClose()
// handler in wxFrameBase. To prevent this from happening we discard these
// messages but only in the case it's really the top level menu as we still
// need to clear the help string when a submenu is selected in a menu.
if ( flags == (MF_POPUP | MF_HILITE) && !m_menuDepth )
return false;
// sign extend to int from unsigned short we get from Windows
int item = (signed short)nItem;
// WM_MENUSELECT is generated for both normal items and menus, including
// the top level menus of the menu bar, which can't be represented using
// any valid identifier in wxMenuEvent so use an otherwise unused value for
// them
if ( flags & (MF_POPUP | MF_SEPARATOR) )
item = wxID_NONE;
wxMenuEvent event(wxEVT_MENU_HIGHLIGHT, item);
event.SetEventObject(this);
if ( HandleWindowEvent(event) )
return true;
// by default, i.e. if the event wasn't handled above, clear the status bar
// text when an item which can't have any associated help string in wx API
// is selected
if ( item == wxID_NONE )
DoGiveHelp(wxEmptyString, true);
return false;
}
bool
wxTopLevelWindowMSW::DoSendMenuOpenCloseEvent(wxEventType evtType, wxMenu* menu, bool popup)
{
// Update the menu depth when dealing with the top level menus.
if ( !popup )
{
if ( evtType == wxEVT_MENU_OPEN )
{
m_menuDepth++;
}
else if ( evtType == wxEVT_MENU_CLOSE )
{
wxASSERT_MSG( m_menuDepth > 0, wxS("No open menus?") );
m_menuDepth--;
}
else
{
wxFAIL_MSG( wxS("Unexpected menu event type") );
}
}
wxMenuEvent event(evtType, popup ? wxID_ANY : 0, menu);
event.SetEventObject(menu);
return HandleWindowEvent(event);
}
bool wxTopLevelWindowMSW::HandleExitMenuLoop(WXWORD isPopup)
{
return DoSendMenuOpenCloseEvent(wxEVT_MENU_CLOSE,
isPopup ? wxCurrentPopupMenu : NULL,
isPopup != 0);
}
bool wxTopLevelWindowMSW::HandleMenuPopup(wxEventType evtType, WXHMENU hMenu)
{
bool isPopup = false;
wxMenu* menu = NULL;
if ( wxCurrentPopupMenu && wxCurrentPopupMenu->GetHMenu() == hMenu )
{
menu = wxCurrentPopupMenu;
isPopup = true;
}
else
{
menu = MSWFindMenuFromHMENU(hMenu);
}
return DoSendMenuOpenCloseEvent(evtType, menu, isPopup);
}
wxMenu* wxTopLevelWindowMSW::MSWFindMenuFromHMENU(WXHMENU WXUNUSED(hMenu))
{
// We don't have any menus at this level.
return NULL;
}
#endif // wxUSE_MENUS && !__WXUNIVERSAL__
// the DialogProc for all wxWidgets dialogs
LONG APIENTRY _EXPORT
wxDlgProc(HWND hDlg,

View File

@ -2273,6 +2273,86 @@ bool wxWindowMSW::DoPopupMenu(wxMenu *menu, int x, int y)
#endif // wxUSE_MENUS_NATIVE
// ---------------------------------------------------------------------------
// menu events
// ---------------------------------------------------------------------------
#if wxUSE_MENUS && !defined(__WXUNIVERSAL__)
bool
wxWindowMSW::HandleMenuSelect(WXWORD nItem, WXWORD flags, WXHMENU hMenu)
{
// Ignore the special messages generated when the menu is closed (this is
// the only case when the flags are set to -1), in particular don't clear
// the help string in the status bar when this happens as it had just been
// restored by the base class code.
if ( !hMenu && flags == 0xffff )
return false;
// sign extend to int from unsigned short we get from Windows
int item = (signed short)nItem;
// WM_MENUSELECT is generated for both normal items and menus, including
// the top level menus of the menu bar, which can't be represented using
// any valid identifier in wxMenuEvent so use an otherwise unused value for
// them
if ( flags & (MF_POPUP | MF_SEPARATOR) )
item = wxID_NONE;
wxMenuEvent event(wxEVT_MENU_HIGHLIGHT, item);
event.SetEventObject(this);
if ( HandleWindowEvent(event) )
return true;
// by default, i.e. if the event wasn't handled above, clear the status bar
// text when an item which can't have any associated help string in wx API
// is selected
if ( item == wxID_NONE )
{
wxFrame *frame = wxDynamicCast(wxGetTopLevelParent(this), wxFrame);
if ( frame )
frame->DoGiveHelp(wxEmptyString, true);
}
return false;
}
bool
wxWindowMSW::DoSendMenuOpenCloseEvent(wxEventType evtType, wxMenu* menu, bool popup)
{
wxMenuEvent event(evtType, popup ? wxID_ANY : 0, menu);
event.SetEventObject(menu);
return HandleWindowEvent(event);
}
bool wxWindowMSW::HandleMenuPopup(wxEventType evtType, WXHMENU hMenu)
{
bool isPopup = false;
wxMenu* menu = NULL;
if ( wxCurrentPopupMenu && wxCurrentPopupMenu->GetHMenu() == hMenu )
{
menu = wxCurrentPopupMenu;
isPopup = true;
}
else
{
menu = MSWFindMenuFromHMENU(hMenu);
}
return DoSendMenuOpenCloseEvent(evtType, menu, isPopup);
}
wxMenu* wxWindowMSW::MSWFindMenuFromHMENU(WXHMENU WXUNUSED(hMenu))
{
// We don't have any menus at this level.
return NULL;
}
#endif // wxUSE_MENUS && !defined(__WXUNIVERSAL__)
// ===========================================================================
// pre/post message processing
// ===========================================================================
@ -3470,7 +3550,7 @@ wxWindowMSW::MSWHandleMessage(WXLRESULT *result,
break;
#endif
#if wxUSE_MENUS
#if wxUSE_MENUS && !defined(__WXUNIVERSAL__)
case WM_MENUCHAR:
// we're only interested in our own menus, not MF_SYSMENU
if ( HIWORD(wParam) == MF_POPUP )
@ -3484,7 +3564,27 @@ wxWindowMSW::MSWHandleMessage(WXLRESULT *result,
}
}
break;
#endif // wxUSE_MENUS
#if !defined(__WXMICROWIN__) && !defined(__WXWINCE__)
case WM_INITMENUPOPUP:
processed = HandleMenuPopup(wxEVT_MENU_OPEN, (WXHMENU)wParam);
break;
case WM_MENUSELECT:
{
WXWORD item, flags;
WXHMENU hmenu;
UnpackMenuSelect(wParam, lParam, &item, &flags, &hmenu);
processed = HandleMenuSelect(item, flags, hmenu);
}
break;
case WM_UNINITMENUPOPUP:
processed = HandleMenuPopup(wxEVT_MENU_CLOSE, (WXHMENU)wParam);
break;
#endif // !__WXMICROWIN__
#endif // wxUSE_MENUS && !defined(__WXUNIVERSAL__)
#ifndef __WXWINCE__
case WM_POWERBROADCAST: