From f2eb4ad2267c8dfd04288c0eb7be10ce1da240e6 Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Sat, 5 Jun 2010 22:58:05 +0000 Subject: [PATCH] Use mouse position to find the item for selection events in wxMSW listbox. Using LB_GETCARETINDEX doesn't work when the mouse is used to make selection because it always returns the index of the last item, even if the mouse is clicked below it, on an area without any listbox items. So use the mouse position to find the item in this case but still use LB_GETCARETINDEX to find the item when the keyboard is used. This required adding a flag to wxListBox storing the kind of the last input message that it received as there doesn't seem to be any way to determine how the message was generated otherwise. This code will be refactored/improved further in the next two commits. git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@64498 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775 --- include/wx/msw/listbox.h | 7 +++++++ src/msw/listbox.cpp | 33 ++++++++++++++++++++++++++++++--- 2 files changed, 37 insertions(+), 3 deletions(-) diff --git a/include/wx/msw/listbox.h b/include/wx/msw/listbox.h index 38b1afcd15..9ccfa810a7 100644 --- a/include/wx/msw/listbox.h +++ b/include/wx/msw/listbox.h @@ -149,6 +149,8 @@ public: virtual void OnInternalIdle(); + virtual WXLRESULT MSWWindowProc(WXUINT nMsg, WXWPARAM wParam, WXLPARAM lParam); + protected: virtual wxSize DoGetBestClientSize() const; @@ -190,6 +192,11 @@ private: // i.e. if we need to call SetHorizontalExtent() from OnInternalIdle() bool m_updateHorizontalExtent; + // flag set to true when we get a keyboard event and reset to false when we + // get a mouse one: this is used to find the correct item for the selection + // event + bool m_selectedByKeyboard; + DECLARE_DYNAMIC_CLASS_NO_COPY(wxListBox) }; diff --git a/src/msw/listbox.cpp b/src/msw/listbox.cpp index 8a2dd34eec..b8255628c8 100644 --- a/src/msw/listbox.cpp +++ b/src/msw/listbox.cpp @@ -153,6 +153,7 @@ void wxListBox::Init() { m_noItems = 0; m_updateHorizontalExtent = false; + m_selectedByKeyboard = false; } bool wxListBox::Create(wxWindow *parent, @@ -644,16 +645,28 @@ wxSize wxListBox::DoGetBestClientSize() const bool wxListBox::MSWCommand(WXUINT param, WXWORD WXUNUSED(id)) { wxEventType evtType; - int n; + int n = wxNOT_FOUND; if ( param == LBN_SELCHANGE ) { if ( HasMultipleSelection() ) return CalcAndSendEvent(); evtType = wxEVT_COMMAND_LISTBOX_SELECTED; - n = SendMessage(GetHwnd(), LB_GETCARETINDEX, 0, 0); - // NB: conveniently enough, LB_ERR is the same as wxNOT_FOUND + if ( m_selectedByKeyboard ) + { + // We shouldn't use the mouse position to find the item as mouse + // can be anywhere, ask the listbox itself. Notice that this can't + // be used when the item is selected using the mouse however as + // LB_GETCARETINDEX will always return a valid item, even if the + // mouse is clicked below all the items, which is why we find the + // item ourselves below in this case. + n = SendMessage(GetHwnd(), LB_GETCARETINDEX, 0, 0); + } + else + { + n = HitTest(ScreenToClient(wxGetMousePosition())); + } } else if ( param == LBN_DBLCLK ) { @@ -670,6 +683,20 @@ bool wxListBox::MSWCommand(WXUINT param, WXWORD WXUNUSED(id)) return n != wxNOT_FOUND && SendEvent(evtType, n, true /* selection */); } +WXLRESULT +wxListBox::MSWWindowProc(WXUINT nMsg, WXWPARAM wParam, WXLPARAM lParam) +{ + // Remember whether there was a keyboard or mouse event before + // LBN_SELCHANGE: this allows us to correctly determine the item affected + // by it in MSWCommand() above in any case. + if ( WM_KEYFIRST <= nMsg && nMsg <= WM_KEYLAST ) + m_selectedByKeyboard = true; + else if ( WM_MOUSEFIRST <= nMsg && nMsg <= WM_MOUSELAST ) + m_selectedByKeyboard = false; + + return wxListBoxBase::MSWWindowProc(nMsg, wParam, lParam); +} + // ---------------------------------------------------------------------------- // owner-drawn list boxes support // ---------------------------------------------------------------------------- -- 2.45.2