From fa3d4aaf0fdffa51372d15f17862cb7081687107 Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Mon, 8 Dec 2008 00:57:53 +0000 Subject: [PATCH] implement click events in wxHeaderCtrl git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@57178 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775 --- include/wx/generic/headerctrlg.h | 8 +++ include/wx/headerctrl.h | 59 +++++++++++++++++- include/wx/msw/headerctrl.h | 5 ++ interface/wx/headerctrl.h | 36 ++++++++++- src/common/headerctrlcmn.cpp | 13 ++++ src/generic/datavgen.cpp | 46 ++++++++++++-- src/generic/headerctrlg.cpp | 100 ++++++++++++++++++++++++++++++- src/msw/headerctrl.cpp | 73 ++++++++++++++++++++++ 8 files changed, 328 insertions(+), 12 deletions(-) diff --git a/include/wx/generic/headerctrlg.h b/include/wx/generic/headerctrlg.h index 6b7f3e410d..2bbe5efc8f 100644 --- a/include/wx/generic/headerctrlg.h +++ b/include/wx/generic/headerctrlg.h @@ -74,6 +74,14 @@ private: // refresh all the controls starting from (and including) the given one void RefreshColsAfter(unsigned int idx); + // return the column at the given position or -1 if it is beyond the + // rightmost column and put true into onSeparator output parameter if the + // position is near the divider at the right end of this column (notice + // that this means that we return column 0 even if the position is over + // column 1 but close enough to the divider separating it from column 0) + int FindColumnAtPos(int x, bool& onSeparator) const; + + // number of columns in the control currently unsigned int m_numColumns; diff --git a/include/wx/headerctrl.h b/include/wx/headerctrl.h index 50c897137a..6264cfec0f 100644 --- a/include/wx/headerctrl.h +++ b/include/wx/headerctrl.h @@ -120,7 +120,7 @@ private: // control, see wxHeaderCtrlSimple for a standalone version // ---------------------------------------------------------------------------- -#if defined(__WXMSW__) && !defined(__WXUNIVERSAL__) +#if 0// defined(__WXMSW__) && !defined(__WXUNIVERSAL__) #include "wx/msw/headerctrl.h" #else #define wxHAS_GENERIC_HEADERCTRL @@ -236,4 +236,61 @@ private: DECLARE_NO_COPY_CLASS(wxHeaderCtrlSimple) }; +// ---------------------------------------------------------------------------- +// wxHeaderCtrl events +// ---------------------------------------------------------------------------- + +class WXDLLIMPEXP_CORE wxHeaderCtrlEvent : public wxNotifyEvent +{ +public: + wxHeaderCtrlEvent(wxEventType commandType = wxEVT_NULL, int winid = 0) + : wxNotifyEvent(commandType, winid) + { + } + + wxHeaderCtrlEvent(const wxHeaderCtrlEvent& event) + : wxNotifyEvent(event), + m_col(event.m_col) + { + } + + int GetColumn() const { return m_col; } + void SetColumn(int col) { m_col = col; } + + virtual wxEvent *Clone() const { return new wxHeaderCtrlEvent(*this); } + +protected: + // the column affected by the event + int m_col; + +private: + DECLARE_DYNAMIC_CLASS_NO_ASSIGN(wxHeaderCtrlEvent) +}; + + +extern WXDLLIMPEXP_ADV const wxEventType wxEVT_COMMAND_HEADER_CLICK; +extern WXDLLIMPEXP_ADV const wxEventType wxEVT_COMMAND_HEADER_RIGHT_CLICK; +extern WXDLLIMPEXP_ADV const wxEventType wxEVT_COMMAND_HEADER_MIDDLE_CLICK; + +extern WXDLLIMPEXP_ADV const wxEventType wxEVT_COMMAND_HEADER_DCLICK; +extern WXDLLIMPEXP_ADV const wxEventType wxEVT_COMMAND_HEADER_RIGHT_DCLICK; +extern WXDLLIMPEXP_ADV const wxEventType wxEVT_COMMAND_HEADER_MIDDLE_DCLICK; + +typedef void (wxEvtHandler::*wxHeaderCtrlEventFunction)(wxHeaderCtrlEvent&); + +#define wxHeaderCtrlEventHandler(func) \ + (wxObjectEventFunction)(wxEventFunction)wxStaticCastEvent( \ + wxHeaderCtrlEventFunction, &func) + +#define wx__DECLARE_HEADER_EVT(evt, id, fn) \ + wx__DECLARE_EVT1(wxEVT_COMMAND_HEADER_ ## evt, id, wxHeaderCtrlEventHandler(fn)) + +#define EVT_HEADER_CLICK(id, fn) wx__DECLARE_HEADER_EVT(CLICK, id, fn) +#define EVT_HEADER_RIGHT_CLICK(id, fn) wx__DECLARE_HEADER_EVT(RIGHT_CLICK, id, fn) +#define EVT_HEADER_MIDDLE_CLICK(id, fn) wx__DECLARE_HEADER_EVT(MIDDLE_CLICK, id, fn) + +#define EVT_HEADER_DCLICK(id, fn) wx__DECLARE_HEADER_EVT(DCLICK, id, fn) +#define EVT_HEADER_RIGHT_DCLICK(id, fn) wx__DECLARE_HEADER_EVT(RIGHT_DCLICK, id, fn) +#define EVT_HEADER_MIDDLE_DCLICK(id, fn) wx__DECLARE_HEADER_EVT(MIDDLE_DCLICK, id, fn) + #endif // _WX_HEADERCTRL_H_ diff --git a/include/wx/msw/headerctrl.h b/include/wx/msw/headerctrl.h index 74919ef111..9eb0b24b94 100644 --- a/include/wx/msw/headerctrl.h +++ b/include/wx/msw/headerctrl.h @@ -60,6 +60,7 @@ private: // override MSW-specific methods needed for new control virtual WXDWORD MSWGetStyle(long style, WXDWORD *exstyle) const; + virtual bool MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result); // common part of all ctors void Init(); @@ -70,6 +71,10 @@ private: enum Operation { Set, Insert }; void DoSetOrInsertItem(Operation oper, unsigned int idx); + // send a click or double click event (depending on dblclk value) for the + // click with the given button on the given item + bool SendClickEvent(bool dblclk, unsigned int idx, int button); + // the image list: initially NULL, created on demand wxImageList *m_imageList; diff --git a/interface/wx/headerctrl.h b/interface/wx/headerctrl.h index 76b76e746f..9dd66fec19 100644 --- a/interface/wx/headerctrl.h +++ b/interface/wx/headerctrl.h @@ -49,9 +49,20 @@ wxHD_DRAGDROP. @endStyleTable - @beginEventTable{wxHeaderEvent} - @event{EVT_HEADER_CLICK(id, func)} - A column heading was clicked. + @beginEventTable{wxHeaderCtrlEvent} + @event{EVT_HEADER_CLICK(id, func)} + A column heading was clicked. + @event{EVT_HEADER_RIGHT_CLICK(id, func)} + A column heading was right clicked. + @event{EVT_HEADER_MIDDLE_CLICK(id, func)} + A column heading was clicked with the middle mouse button. + + @event{EVT_HEADER_DCLICK(id, func)} + A column heading was double clicked. + @event{EVT_HEADER_RIGHT_DCLICK(id, func)} + A column heading was right double clicked. + @event{EVT_HEADER_MIDDLE_DCLICK(id, func)} + A column heading was double clicked with the middle mouse button. @endEventTable @library{wxcore} @@ -285,3 +296,22 @@ public: */ void RemoveSortIndicator(unsigned int idx); }; + +/** + @class wxHeaderCtrlEvent + + Event class representing the events generated by wxHeaderCtrl. + + @library{wxcore} + @category{ctrl} + + @see wxHeaderCtrl +*/ +class wxHeaderCtrlEvent : public wxNotifyEvent +{ +public: + /** + Return the index of the column affected by this event. + */ + int GetColumn() const; +}; diff --git a/src/common/headerctrlcmn.cpp b/src/common/headerctrlcmn.cpp index b91f3fc7d8..718a9d6fff 100644 --- a/src/common/headerctrlcmn.cpp +++ b/src/common/headerctrlcmn.cpp @@ -132,3 +132,16 @@ void wxHeaderCtrlSimple::RemoveSortIndicator() } } +// ============================================================================ +// wxHeaderCtrlEvent implementation +// ============================================================================ + +IMPLEMENT_DYNAMIC_CLASS(wxHeaderCtrlEvent, wxNotifyEvent) + +const wxEventType wxEVT_COMMAND_HEADER_CLICK = wxNewEventType(); +const wxEventType wxEVT_COMMAND_HEADER_RIGHT_CLICK = wxNewEventType(); +const wxEventType wxEVT_COMMAND_HEADER_MIDDLE_CLICK = wxNewEventType(); + +const wxEventType wxEVT_COMMAND_HEADER_DCLICK = wxNewEventType(); +const wxEventType wxEVT_COMMAND_HEADER_RIGHT_DCLICK = wxNewEventType(); +const wxEventType wxEVT_COMMAND_HEADER_MIDDLE_DCLICK = wxNewEventType(); diff --git a/src/generic/datavgen.cpp b/src/generic/datavgen.cpp index 339d490174..ca91268171 100644 --- a/src/generic/datavgen.cpp +++ b/src/generic/datavgen.cpp @@ -67,16 +67,16 @@ static const int EXPANDER_OFFSET = 4; static const int EXPANDER_OFFSET = 1; #endif -//----------------------------------------------------------------------------- -// wxDataViewHeaderWindow -//----------------------------------------------------------------------------- - //Below is the compare stuff //For the generic implements, both the leaf nodes and the nodes are sorted for fast search when needed static wxDataViewModel * g_model; static int g_column = -2; static bool g_asending = true; +//----------------------------------------------------------------------------- +// wxDataViewHeaderWindow +//----------------------------------------------------------------------------- + class wxDataViewHeaderWindow : public wxHeaderCtrl { public: @@ -88,16 +88,50 @@ public: wxDataViewCtrl *GetOwner() const { return static_cast(GetParent()); } -protected: +private: virtual wxHeaderColumnBase& GetColumn(unsigned int idx) { return *(GetOwner()->GetColumn(idx)); } -private: + bool SendEvent(wxEventType type, unsigned int n) + { + wxDataViewCtrl * const owner = GetOwner(); + wxDataViewEvent event(type, owner->GetId()); + + event.SetEventObject(owner); + event.SetColumn(n); + event.SetDataViewColumn(owner->GetColumn(n)); + event.SetModel(owner->GetModel()); + + // for events created by wxDataViewHeaderWindow the + // row / value fields are not valid + return owner->GetEventHandler()->ProcessEvent(event); + } + + void OnClick(wxHeaderCtrlEvent& event) + { + if ( !SendEvent(wxEVT_COMMAND_DATAVIEW_COLUMN_HEADER_CLICK, + event.GetColumn()) ) + event.Skip(); + } + + void OnRClick(wxHeaderCtrlEvent& event) + { + if ( !SendEvent(wxEVT_COMMAND_DATAVIEW_COLUMN_HEADER_RIGHT_CLICK, + event.GetColumn()) ) + event.Skip(); + } + + DECLARE_EVENT_TABLE() DECLARE_NO_COPY_CLASS(wxDataViewHeaderWindow) }; +BEGIN_EVENT_TABLE(wxDataViewHeaderWindow, wxHeaderCtrl) + EVT_HEADER_CLICK(wxID_ANY, wxDataViewHeaderWindow::OnClick) + EVT_HEADER_RIGHT_CLICK(wxID_ANY, wxDataViewHeaderWindow::OnRClick) +END_EVENT_TABLE() + //----------------------------------------------------------------------------- // wxDataViewRenameTimer //----------------------------------------------------------------------------- diff --git a/src/generic/headerctrlg.cpp b/src/generic/headerctrlg.cpp index f55b7e1f6f..823ea60aac 100644 --- a/src/generic/headerctrlg.cpp +++ b/src/generic/headerctrlg.cpp @@ -145,6 +145,41 @@ int wxHeaderCtrl::GetColStart(unsigned int idx) const return pos; } +int wxHeaderCtrl::FindColumnAtPos(int x, bool& onSeparator) const +{ + wxHeaderCtrl * const self = const_cast(this); + + int pos = 0; + const unsigned count = GetColumnCount(); + for ( unsigned n = 0; n < count; n++ ) + { + const wxHeaderColumnBase& col = self->GetColumn(n); + if ( col.IsHidden() ) + continue; + + pos += col.GetWidth(); + + // if the column is resizeable, check if we're approximatively over the + // line separating it from the next column + // + // TODO: don't hardcode sensitivity + if ( col.IsResizeable() && abs(x - pos) < 8 ) + { + onSeparator = true; + return n; + } + + // inside this column? + if ( x < pos ) + { + onSeparator = false; + return n; + } + } + + return COL_NONE; +} + // ---------------------------------------------------------------------------- // wxHeaderCtrl repainting // ---------------------------------------------------------------------------- @@ -242,9 +277,70 @@ void wxHeaderCtrl::OnPaint(wxPaintEvent& WXUNUSED(event)) } } -void wxHeaderCtrl::OnMouse(wxMouseEvent& event) +void wxHeaderCtrl::OnMouse(wxMouseEvent& mevent) { - event.Skip(); + mevent.Skip(); + + // find if the event is over a column at all + bool onSeparator; + const unsigned col = FindColumnAtPos(mevent.GetX(), onSeparator); + if ( col == COL_NONE ) + return; + + // update mouse cursor as it moves around + if ( mevent.Moving() ) + { + SetCursor(onSeparator ? wxCursor(wxCURSOR_SIZEWE) : wxNullCursor); + return; + } + + if ( mevent.LeftDown() ) + { + // TODO + if ( onSeparator ) + // resize column + ; + else + // drag column + ; + + return; + } + + // determine the type of header event corresponding to this mouse event + wxEventType evtType; + const bool click = mevent.ButtonUp(); + if ( click || mevent.ButtonDClick() ) + { + switch ( mevent.GetButton() ) + { + case wxMOUSE_BTN_LEFT: + evtType = click ? wxEVT_COMMAND_HEADER_CLICK + : wxEVT_COMMAND_HEADER_DCLICK; + break; + + case wxMOUSE_BTN_RIGHT: + evtType = click ? wxEVT_COMMAND_HEADER_RIGHT_CLICK + : wxEVT_COMMAND_HEADER_RIGHT_DCLICK; + break; + + case wxMOUSE_BTN_MIDDLE: + evtType = click ? wxEVT_COMMAND_HEADER_MIDDLE_CLICK + : wxEVT_COMMAND_HEADER_MIDDLE_DCLICK; + break; + + default: + // ignore clicks from other mouse buttons + return; + } + + wxHeaderCtrlEvent event(evtType, GetId()); + event.SetEventObject(this); + event.SetColumn(col); + + if ( GetEventHandler()->ProcessEvent(event) ) + mevent.Skip(false); + } } #endif // wxHAS_GENERIC_HEADERCTRL diff --git a/src/msw/headerctrl.cpp b/src/msw/headerctrl.cpp index f12c8c1f71..601cb236f8 100644 --- a/src/msw/headerctrl.cpp +++ b/src/msw/headerctrl.cpp @@ -33,6 +33,9 @@ #include "wx/msw/wrapcctl.h" #include "wx/msw/private.h" +// from src/msw/listctrl.cpp +extern int WXDLLIMPEXP_CORE wxMSWGetColumnClicked(NMHDR *nmhdr, POINT *ptClick); + // ============================================================================ // wxHeaderCtrl implementation // ============================================================================ @@ -250,3 +253,73 @@ void wxHeaderCtrl::DoSetOrInsertItem(Operation oper, unsigned int idx) } } +// ---------------------------------------------------------------------------- +// wxHeaderCtrl events +// ---------------------------------------------------------------------------- + +bool wxHeaderCtrl::SendClickEvent(bool dblclk, unsigned int idx, int button) +{ + wxEventType evtType; + switch ( button ) + { + case 0: + evtType = dblclk ? wxEVT_COMMAND_HEADER_DCLICK + : wxEVT_COMMAND_HEADER_CLICK; + break; + + case 1: + evtType = dblclk ? wxEVT_COMMAND_HEADER_RIGHT_DCLICK + : wxEVT_COMMAND_HEADER_RIGHT_CLICK; + break; + + case 2: + evtType = dblclk ? wxEVT_COMMAND_HEADER_MIDDLE_DCLICK + : wxEVT_COMMAND_HEADER_MIDDLE_CLICK; + break; + + default: + wxFAIL_MSG( wxS("unexpected event type") ); + return false; + } + + wxHeaderCtrlEvent event(evtType, GetId()); + event.SetEventObject(this); + event.SetColumn(idx); + + return GetEventHandler()->ProcessEvent(event); +} + +bool wxHeaderCtrl::MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result) +{ + NMHEADER * const nmhdr = (NMHEADER *)lParam; + + switch ( nmhdr->hdr.code ) + { + case HDN_ITEMCLICK: + case HDN_ITEMDBLCLICK: + if ( SendClickEvent(nmhdr->hdr.code == HDN_ITEMDBLCLICK, + nmhdr->iItem, + nmhdr->iButton) ) + return true; + break; + + // although we should get the notifications about the right clicks + // via HDN_ITEM[DBL]CLICK too according to MSDN this simply doesn't + // happen in practice on any Windows system up to 2003 + case NM_RCLICK: + case NM_RDBLCLK: + { + POINT pt; + const int col = wxMSWGetColumnClicked(&nmhdr->hdr, &pt); + if ( col != wxNOT_FOUND ) + { + if ( SendClickEvent(nmhdr->hdr.code == NM_RDBLCLK, col, 1) ) + return true; + } + //else: ignore clicks outside any column + } + break; + } + + return wxHeaderCtrlBase::MSWOnNotify(idCtrl, lParam, result); +} -- 2.45.2