From c00fed0ef4d70c9b45d9b8405f636d30eadb7ea6 Mon Sep 17 00:00:00 2001
From: Vadim Zeitlin <vadim@wxwidgets.org>
Date: Thu, 9 Mar 2006 12:48:52 +0000
Subject: [PATCH] added wxListBox::HitTest() from Ryan (patch 1446207)

git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@37923 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
---
 docs/changes.txt                |  1 +
 docs/latex/wx/listbox.tex       | 19 +++++++++
 include/wx/gtk/listbox.h        |  2 +
 include/wx/listbox.h            |  7 +++
 include/wx/mac/carbon/listbox.h |  1 +
 include/wx/msw/listbox.h        |  1 +
 samples/listbox/lboxtest.cpp    | 16 +++++++
 src/gtk/listbox.cpp             | 24 +++++++++++
 src/mac/carbon/listbox.cpp      | 76 +++++++++++++++++++++++++++++++++
 src/msw/listbox.cpp             | 10 +++++
 10 files changed, 157 insertions(+)

diff --git a/docs/changes.txt b/docs/changes.txt
index d8f127eca3..053ee2f8b1 100644
--- a/docs/changes.txt
+++ b/docs/changes.txt
@@ -91,6 +91,7 @@ All (GUI):
 - Implemented wxDisplay::GetFromWindow() for platforms other than MSW.
 - UpdateUI handler can now show/hide the window too (Ronald Weiss)
 - More than one filter allowed in in wxDocTemplate filter.
+- Added wxListBox::HitTest()
 
 wxMSW:
 
diff --git a/docs/latex/wx/listbox.tex b/docs/latex/wx/listbox.tex
index 649ba91e59..75e7b48b5b 100644
--- a/docs/latex/wx/listbox.tex
+++ b/docs/latex/wx/listbox.tex
@@ -201,6 +201,25 @@ parameter for wxPython, which is a list of strings.}
 \perlnote{In wxPerl there is just an array reference in place of {\tt nItems}
 and {\tt items}.}
 
+\membersection{wxListBox::HitTest}\label{wxlistboxhittest}
+
+\constfunc{int}{HitTest}{\param{const wxPoint&}{ point}}
+
+Returns the item located at \arg{point}, or \texttt{wxNOT\_FOUND} if there
+is no item located at \arg{point}.
+
+\newsince{2.7.0}. It is currently implemented for wxMSW, wxMac and wxGTK2
+ports.
+
+\wxheading{Parameters}
+
+\docparam{point}{Point of item (in client coordinates) to obtain}
+
+\wxheading{Return value}
+
+Item located at \arg{point}, or \texttt{wxNOT\_FOUND} if unimplemented
+or the item does not exist.
+
 \membersection{wxListBox::IsSelected}\label{wxlistboxisselected}
 
 \constfunc{bool}{IsSelected}{\param{int}{ n}}
diff --git a/include/wx/gtk/listbox.h b/include/wx/gtk/listbox.h
index 0acc30272e..7804a96625 100644
--- a/include/wx/gtk/listbox.h
+++ b/include/wx/gtk/listbox.h
@@ -114,6 +114,8 @@ protected:
     virtual void* DoGetItemClientData(int n) const;
     virtual void DoSetItemClientObject(int n, wxClientData* clientData);
     virtual wxClientData* DoGetItemClientObject(int n) const;
+    virtual int DoListHitTest(const wxPoint& point) const;
+
     void DoApplyWidgetStyle(GtkRcStyle *style);
 
 private:
diff --git a/include/wx/listbox.h b/include/wx/listbox.h
index 0556f805cf..d22b0f47d3 100644
--- a/include/wx/listbox.h
+++ b/include/wx/listbox.h
@@ -107,6 +107,9 @@ public:
     // instead
     bool Selected(int n) const { return IsSelected(n); }
 
+    // returns the item number at a point or wxNOT_FOUND
+    int HitTest(const wxPoint& point) const { return DoListHitTest(point); }
+
 protected:
     // NB: due to wxGTK implementation details, DoInsert() is implemented
     //     using DoInsertItems() and not the other way round
@@ -121,6 +124,10 @@ protected:
 
     virtual void DoSetSelection(int n, bool select) = 0;
 
+    // there is already wxWindow::DoHitTest() so call this one differently
+    virtual int DoListHitTest(const wxPoint& WXUNUSED(point)) const
+        { return wxNOT_FOUND; }
+
 
     DECLARE_NO_COPY_CLASS(wxListBoxBase)
 };
diff --git a/include/wx/mac/carbon/listbox.h b/include/wx/mac/carbon/listbox.h
index 0d55320f54..7aa58cc559 100644
--- a/include/wx/mac/carbon/listbox.h
+++ b/include/wx/mac/carbon/listbox.h
@@ -113,6 +113,7 @@ protected:
     virtual void DoSetItemClientObject(int n, wxClientData* clientData);
     virtual wxClientData* DoGetItemClientObject(int n) const;
     virtual void DoSetSize(int x, int y, int width, int height, int sizeFlags = wxSIZE_AUTO);
+    virtual int DoListHitTest(const wxPoint& point) const;
 
     void            MacDelete( int n ) ;
     void            MacInsert( int n , const wxString& item) ;
diff --git a/include/wx/msw/listbox.h b/include/wx/msw/listbox.h
index 46acf9a572..1ed147948b 100644
--- a/include/wx/msw/listbox.h
+++ b/include/wx/msw/listbox.h
@@ -142,6 +142,7 @@ protected:
     virtual void* DoGetItemClientData(int n) const;
     virtual void DoSetItemClientObject(int n, wxClientData* clientData);
     virtual wxClientData* DoGetItemClientObject(int n) const;
+    virtual int DoListHitTest(const wxPoint& point) const;
 
     // free memory (common part of Clear() and dtor)
     void Free();
diff --git a/samples/listbox/lboxtest.cpp b/samples/listbox/lboxtest.cpp
index 8f92c1920a..d2bbec8e79 100644
--- a/samples/listbox/lboxtest.cpp
+++ b/samples/listbox/lboxtest.cpp
@@ -124,6 +124,7 @@ protected:
 
     void OnListbox(wxCommandEvent& event);
     void OnListboxDClick(wxCommandEvent& event);
+    void OnListboxRDown(wxMouseEvent& event);
 
     void OnCheckOrRadioBox(wxCommandEvent& event);
 
@@ -487,6 +488,9 @@ LboxTestFrame::LboxTestFrame(const wxString& title)
     m_logTarget = new LboxLogger(m_lboxLog, wxLog::GetActiveTarget());
     wxLog::SetActiveTarget(m_logTarget);
 #endif // wxUSE_LOG
+
+    m_lbox->Connect(wxEVT_RIGHT_DOWN, 
+        wxMouseEventHandler(LboxTestFrame::OnListboxRDown), NULL, this);
 }
 
 LboxTestFrame::~LboxTestFrame()
@@ -699,6 +703,18 @@ void LboxTestFrame::OnListboxDClick(wxCommandEvent& event)
     wxLogMessage(_T("Listbox item %d double clicked"), sel);
 }
 
+void LboxTestFrame::OnListboxRDown(wxMouseEvent& event)
+{
+    int item = m_lbox->HitTest(event.GetPosition());
+
+    if ( item != wxNOT_FOUND )
+        wxLogMessage(_T("Listbox item %d right clicked"), item);
+    else
+        wxLogMessage(_T("Listbox right clicked but no item clicked upon"));
+
+    event.Skip();
+}
+
 void LboxTestFrame::OnCheckOrRadioBox(wxCommandEvent& WXUNUSED(event))
 {
     m_dirty = true;
diff --git a/src/gtk/listbox.cpp b/src/gtk/listbox.cpp
index f6dd76bf7d..42a5fe109a 100644
--- a/src/gtk/listbox.cpp
+++ b/src/gtk/listbox.cpp
@@ -1015,6 +1015,30 @@ void wxListBox::DoSetFirstItem( int n )
     gtk_adjustment_set_value( adjustment, y );
 }
 
+// ----------------------------------------------------------------------------
+// hittest
+// ----------------------------------------------------------------------------
+
+int wxListBox::DoListHitTest(const wxPoint& point)
+{
+    //Need to translate from master window since it is in client coords
+    gint binx, biny;
+    gdk_window_get_geometry(gtk_tree_view_get_bin_window(m_treeview), 
+                            &binx, &biny, NULL, NULL, NULL);
+
+    GtkTreePath* path;
+    if(!gtk_tree_view_get_path_at_pos(m_treeview, 
+                                  point.x - binx, point.y - biny,
+                                  &path, NULL, NULL, NULL)) //last two == x,y rel to cell
+    {
+        return wxNOT_FOUND;
+    }
+
+    int index = gtk_tree_path_get_indices(path)[0];
+    gtk_tree_path_free(path);
+
+    return index;
+}
 
 // ----------------------------------------------------------------------------
 // helpers
diff --git a/src/mac/carbon/listbox.cpp b/src/mac/carbon/listbox.cpp
index 8d55c0f0ba..1dc7a3c2e4 100644
--- a/src/mac/carbon/listbox.cpp
+++ b/src/mac/carbon/listbox.cpp
@@ -782,6 +782,82 @@ void wxListBox::MacScrollTo( int n )
     verify_noerr( m_peer->RevealItem( id , kTextColumnId , kDataBrowserRevealWithoutSelecting ) ) ;
 }
 
+int wxListBox::DoListHitTest(const wxPoint& point) const
+{
+    //Yuck - there is no easy way to get a databrowseritem from a point
+    //so we need to iterate through our items to see which one this falls under
+    int count = GetCount();
+    DataBrowserTableViewColumnID colId = 0;
+
+    //Get column property id (req. for call to itempartbounds)
+    GetDataBrowserTableViewColumnProperty(m_peer->GetControlRef(), 0, &colId);
+    
+    for(int i = 1; i <= count; ++i)
+    {
+        Rect bounds;
+        GetDataBrowserItemPartBounds(m_peer->GetControlRef(), i, colId, 
+                                     kDataBrowserPropertyEnclosingPart,
+                                     &bounds);
+        
+        //translate to client coords
+        MacRootWindowToWindow(&bounds.left, &bounds.top);
+        MacRootWindowToWindow(&bounds.right, &bounds.bottom);
+        
+#if 0 //debugging :)
+        wxPrintf(wxT("L:%i R:%i T:%i B:%i HT:%i,%i\n"),
+                    bounds.left, bounds.right,
+                    bounds.top, bounds.bottom, 
+                    point.x, point.y);
+        fflush(stdout);
+#endif
+        //if point is within the bounds, return this item
+        if( (point.x >= bounds.left && point.x <= bounds.right) &&
+            (point.y >= bounds.top && point.y <= bounds.bottom) )
+        {
+            return i - 1; //found
+        }
+    }
+    
+    return wxNOT_FOUND;
+}
+
+int wxListBox::MacHitTest(const wxPoint& point)
+{
+    //Yuck - there is no easy way to get a databrowseritem from a point
+    //so we need to iterate through our items to see which one this falls under
+
+    // TODO: binary search would be faster
+    int count = GetCount();
+    DataBrowserTableViewColumnID colId = 0;
+
+    //Get column property id (req. for call to itempartbounds)
+    GetDataBrowserTableViewColumnProperty(m_peer->GetControlRef(), 0, &colId);
+    
+    for(int i = 1; i <= count; ++i)
+    {
+        Rect bounds;
+        GetDataBrowserItemPartBounds(m_peer->GetControlRef(), i, colId, 
+                                     kDataBrowserPropertyEnclosingPart,
+                                     &bounds);
+        
+        //translate to client coords
+        //
+        // TODO: it would probably be more efficient to translate point to
+        //       screen coordinates once outside of the loop
+        MacRootWindowToWindow(&bounds.left, &bounds.top);
+        MacRootWindowToWindow(&bounds.right, &bounds.bottom);
+        
+        //if point is within the bounds, return this item
+        if( (point.x >= bounds.left && point.x <= bounds.right) &&
+            (point.y >= bounds.top && point.y <= bounds.bottom) )
+        {
+            return i - 1; //found
+        }
+    }
+    
+    return wxNOT_FOUND;
+}
+
 #if !TARGET_API_MAC_OSX
 
 void wxListBox::OnChar(wxKeyEvent& event)
diff --git a/src/msw/listbox.cpp b/src/msw/listbox.cpp
index 1f8a77955d..7abbf18e2b 100644
--- a/src/msw/listbox.cpp
+++ b/src/msw/listbox.cpp
@@ -552,6 +552,16 @@ wxListBox::DoInsertItems(const wxArrayString& items, int pos)
     InvalidateBestSize();
 }
 
+int wxListBox::DoListHitTest(const wxPoint& point) const
+{
+    LRESULT lRes =  ::SendMessage(GetHwnd(), LB_ITEMFROMPOINT, 
+                                  0L, MAKELONG(point.x, point.y));
+
+    // non zero high-order word means that this item is outside of the client
+    // area, IOW the point is outside of the listbox
+    return HIWORD(lRes) ? wxNOT_FOUND : lRes;
+}
+
 void wxListBox::SetString(int N, const wxString& s)
 {
     wxCHECK_RET( N >= 0 && N < m_noItems,
-- 
2.50.0

