From 3bfaa5a7e4929d7259d1eb7efb6528c7a90c7831 Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Mon, 8 Dec 2008 12:15:17 +0000 Subject: [PATCH] add wxEVT_COMMAND_HEADER_SEPARATOR_DCLICK and semi-automatic header resizing support git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@57186 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775 --- include/wx/generic/headerctrlg.h | 5 ++- include/wx/headerctrl.h | 19 ++++++++++ include/wx/msw/headerctrl.h | 6 +++- interface/wx/headerctrl.h | 61 ++++++++++++++++++++++++++++++++ src/common/headerctrlcmn.cpp | 19 ++++++++++ src/generic/datavgen.cpp | 15 +++++++- src/generic/headerctrlg.cpp | 38 +++++++++++++------- src/msw/headerctrl.cpp | 36 +++++++++++++------ 8 files changed, 172 insertions(+), 27 deletions(-) diff --git a/include/wx/generic/headerctrlg.h b/include/wx/generic/headerctrlg.h index 2bbe5efc8f..7c86e97730 100644 --- a/include/wx/generic/headerctrlg.h +++ b/include/wx/generic/headerctrlg.h @@ -68,9 +68,12 @@ private: // return the horizontal start position of the given column int GetColStart(unsigned int idx) const; - // refresh the given column [only] + // refresh the given column [only]; idx must be valid void RefreshCol(unsigned int idx); + // refresh the given column if idx is valid + void RefreshColIfNotNone(unsigned int idx); + // refresh all the controls starting from (and including) the given one void RefreshColsAfter(unsigned int idx); diff --git a/include/wx/headerctrl.h b/include/wx/headerctrl.h index 170cf641db..f7f8dda827 100644 --- a/include/wx/headerctrl.h +++ b/include/wx/headerctrl.h @@ -20,6 +20,7 @@ // notice that the classes in this header are defined in the core library even // although currently they're only used by wxGrid which is in wxAdv because we // plan to use it in wxListCtrl which is in core too in the future +class WXDLLIMPEXP_FWD_CORE wxHeaderCtrlEvent; // ---------------------------------------------------------------------------- // constants @@ -101,6 +102,15 @@ protected: // information for the given column virtual wxHeaderColumnBase& GetColumn(unsigned int idx) = 0; + // this method is called from the default EVT_HEADER_SEPARATOR_DCLICK + // handler to update the fitting column width of the given column, it + // should return true if the width was really updated + virtual bool UpdateColumnWidthToFit(unsigned int WXUNUSED(idx), + int WXUNUSED(widthTitle)) + { + return false; + } + private: // methods implementing our public API and defined in platform-specific // implementations @@ -112,6 +122,11 @@ private: // this window doesn't look nice with the border so don't use it by default virtual wxBorder GetDefaultBorder() const { return wxBORDER_NONE; } + + // event handlers + void OnSeparatorDClick(wxHeaderCtrlEvent& event); + + DECLARE_EVENT_TABLE() }; // ---------------------------------------------------------------------------- @@ -276,6 +291,8 @@ extern WXDLLIMPEXP_CORE const wxEventType wxEVT_COMMAND_HEADER_DCLICK; extern WXDLLIMPEXP_CORE const wxEventType wxEVT_COMMAND_HEADER_RIGHT_DCLICK; extern WXDLLIMPEXP_CORE const wxEventType wxEVT_COMMAND_HEADER_MIDDLE_DCLICK; +extern WXDLLIMPEXP_CORE const wxEventType wxEVT_COMMAND_HEADER_SEPARATOR_DCLICK; + typedef void (wxEvtHandler::*wxHeaderCtrlEventFunction)(wxHeaderCtrlEvent&); #define wxHeaderCtrlEventHandler(func) \ @@ -293,4 +310,6 @@ typedef void (wxEvtHandler::*wxHeaderCtrlEventFunction)(wxHeaderCtrlEvent&); #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) +#define EVT_HEADER_SEPARATOR_DCLICK(id, fn) wx__DECLARE_HEADER_EVT(SEPARATOR_DCLICK, id, fn) + #endif // _WX_HEADERCTRL_H_ diff --git a/include/wx/msw/headerctrl.h b/include/wx/msw/headerctrl.h index 9eb0b24b94..8a48ef0d56 100644 --- a/include/wx/msw/headerctrl.h +++ b/include/wx/msw/headerctrl.h @@ -71,9 +71,13 @@ private: enum Operation { Set, Insert }; void DoSetOrInsertItem(Operation oper, unsigned int idx); + // send an event of the given type for the given column, return true if it + // was processed + bool SendEvent(wxEventType evtType, 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); + bool SendClickEvent(bool dblclk, int button, unsigned int idx); // the image list: initially NULL, created on demand diff --git a/interface/wx/headerctrl.h b/interface/wx/headerctrl.h index 9dd66fec19..c7e5114da6 100644 --- a/interface/wx/headerctrl.h +++ b/interface/wx/headerctrl.h @@ -63,6 +63,12 @@ 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. + + @event{EVT_HEADER_SEPARATOR_DCLICK(id, func)} + Separator to the right of the specified column was double clicked + (this action is commonly used to resize the column to fit its + contents width and the control provides UpdateColumnWidthToFit() method + to make implementing this easier). @endEventTable @library{wxcore} @@ -170,6 +176,61 @@ protected: SetColumnCount(). */ virtual wxHeaderColumnBase& GetColumn(unsigned int idx) = 0; + + /** + Method which may be implemented by the derived classes to allow double + clicking the column separator to resize the column to fit its contents. + + When a separator is double clicked, the default handler of + EVT_HEADER_SEPARATOR_DCLICK event calls this function and refreshes the + column if it returns @true so to implement the resizing of the column + to fit its width on header double click you need to implement this + method using logic similar to this example: + @code + class MyHeaderCtrl : public wxHeaderColumnBase + { + public: + ... + + void SetWidth(int width) { m_width = width; } + virtual int GetWidth() const { return m_width; } + + private: + int m_width; + }; + + class MyHeaderCtrl : public wxHeaderCtrl + { + public: + protected: + virtual wxHeaderColumnBase& GetColumn(unsigned int idx) + { + return m_cols[idx]; + } + + virtual bool UpdateColumnWidthToFit(unsigned int idx, int widthTitle) + { + int widthContents = ... compute minimal width for column idx ... + m_cols[idx].SetWidth(wxMax(widthContents, widthTitle)); + return true; + } + + wxVector m_cols; + }; + @endcode + + Base class version simply returns @false. + + @param width + Contains minimal width needed to display the column header itself + and will usually be used as a starting point for the fitting width + calculation. + @return + @true to indicate that the column was resized, i.e. GetColumn() now + returns the new width value, and so must be refreshed or @false + meaning that the control didn't reach to the separator double click. + */ + virtual bool UpdateColumnWidthToFit(unsigned int idx, int widthTitle); }; diff --git a/src/common/headerctrlcmn.cpp b/src/common/headerctrlcmn.cpp index 718a9d6fff..fa9766ab81 100644 --- a/src/common/headerctrlcmn.cpp +++ b/src/common/headerctrlcmn.cpp @@ -45,6 +45,10 @@ const unsigned int wxNO_COLUMN = static_cast(-1); extern WXDLLIMPEXP_DATA_CORE(const char) wxHeaderCtrlNameStr[] = "wxHeaderCtrl"; +BEGIN_EVENT_TABLE(wxHeaderCtrlBase, wxControl) + EVT_HEADER_SEPARATOR_DCLICK(wxID_ANY, wxHeaderCtrlBase::OnSeparatorDClick) +END_EVENT_TABLE() + void wxHeaderCtrlBase::ScrollWindow(int dx, int WXUNUSED_UNLESS_DEBUG(dy), const wxRect * WXUNUSED_UNLESS_DEBUG(rect)) @@ -60,6 +64,19 @@ void wxHeaderCtrlBase::ScrollWindow(int dx, DoScrollHorz(dx); } +void wxHeaderCtrlBase::OnSeparatorDClick(wxHeaderCtrlEvent& event) +{ + const unsigned col = event.GetColumn(); + + int w = wxWindowBase::GetTextExtent(GetColumn(col).GetTitle()).x; + w += 2*GetCharWidth(); // add some arbitrary margins around text + + if ( !UpdateColumnWidthToFit(col, w) ) + event.Skip(); + else + UpdateColumn(col); +} + // ============================================================================ // wxHeaderCtrlSimple implementation // ============================================================================ @@ -145,3 +162,5 @@ 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(); + +const wxEventType wxEVT_COMMAND_HEADER_SEPARATOR_DCLICK = wxNewEventType(); diff --git a/src/generic/datavgen.cpp b/src/generic/datavgen.cpp index ca91268171..0fbcc1510c 100644 --- a/src/generic/datavgen.cpp +++ b/src/generic/datavgen.cpp @@ -88,12 +88,25 @@ public: wxDataViewCtrl *GetOwner() const { return static_cast(GetParent()); } -private: +protected: + // implement/override wxHeaderCtrl functions by forwarding them to the main + // control virtual wxHeaderColumnBase& GetColumn(unsigned int idx) { return *(GetOwner()->GetColumn(idx)); } + virtual bool UpdateColumnWidthToFit(unsigned int idx, int widthTitle) + { + wxDataViewCtrl * const owner = GetOwner(); + + int widthContents = owner->GetBestColumnWidth(idx); + owner->GetColumn(idx)->SetWidth(wxMax(widthTitle, widthContents)); + + return true; + } + +private: bool SendEvent(wxEventType type, unsigned int n) { wxDataViewCtrl * const owner = GetOwner(); diff --git a/src/generic/headerctrlg.cpp b/src/generic/headerctrlg.cpp index 54d7cace41..8d8adc9fc4 100644 --- a/src/generic/headerctrlg.cpp +++ b/src/generic/headerctrlg.cpp @@ -213,7 +213,7 @@ void wxHeaderCtrl::RefreshColsAfter(unsigned int idx) // wxHeaderCtrl event handlers // ---------------------------------------------------------------------------- -BEGIN_EVENT_TABLE(wxHeaderCtrl, wxControl) +BEGIN_EVENT_TABLE(wxHeaderCtrl, wxHeaderCtrlBase) EVT_PAINT(wxHeaderCtrl::OnPaint) EVT_MOUSE_EVENTS(wxHeaderCtrl::OnMouse) @@ -330,15 +330,24 @@ void wxHeaderCtrl::OnMouse(wxMouseEvent& mevent) } // determine the type of header event corresponding to this mouse event - wxEventType evtType; - const bool click = mevent.ButtonUp(); - if ( click || mevent.ButtonDClick() ) + wxEventType evtType = wxEVT_NULL; + const bool click = mevent.ButtonUp(), + dblclk = mevent.ButtonDClick(); + if ( click || dblclk ) { switch ( mevent.GetButton() ) { case wxMOUSE_BTN_LEFT: - evtType = click ? wxEVT_COMMAND_HEADER_CLICK - : wxEVT_COMMAND_HEADER_DCLICK; + // treat left double clicks on separator specially + if ( onSeparator && dblclk ) + { + evtType = wxEVT_COMMAND_HEADER_SEPARATOR_DCLICK; + } + else // not double click on separator + { + evtType = click ? wxEVT_COMMAND_HEADER_CLICK + : wxEVT_COMMAND_HEADER_DCLICK; + } break; case wxMOUSE_BTN_RIGHT: @@ -353,16 +362,19 @@ void wxHeaderCtrl::OnMouse(wxMouseEvent& mevent) default: // ignore clicks from other mouse buttons - return; + ; } + } + + if ( evtType == wxEVT_NULL ) + return; - wxHeaderCtrlEvent event(evtType, GetId()); - event.SetEventObject(this); - event.SetColumn(col); + wxHeaderCtrlEvent event(evtType, GetId()); + event.SetEventObject(this); + event.SetColumn(col); - if ( GetEventHandler()->ProcessEvent(event) ) - mevent.Skip(false); - } + if ( GetEventHandler()->ProcessEvent(event) ) + mevent.Skip(false); } #endif // wxHAS_GENERIC_HEADERCTRL diff --git a/src/msw/headerctrl.cpp b/src/msw/headerctrl.cpp index 601cb236f8..8df0e8286c 100644 --- a/src/msw/headerctrl.cpp +++ b/src/msw/headerctrl.cpp @@ -28,6 +28,9 @@ #endif // WX_PRECOMP #include "wx/headerctrl.h" + +#ifndef wxHAS_GENERIC_HEADERCTRL + #include "wx/imaglist.h" #include "wx/msw/wrapcctl.h" @@ -257,7 +260,16 @@ void wxHeaderCtrl::DoSetOrInsertItem(Operation oper, unsigned int idx) // wxHeaderCtrl events // ---------------------------------------------------------------------------- -bool wxHeaderCtrl::SendClickEvent(bool dblclk, unsigned int idx, int button) +bool wxHeaderCtrl::SendEvent(wxEventType evtType, unsigned int idx) +{ + wxHeaderCtrlEvent event(evtType, GetId()); + event.SetEventObject(this); + event.SetColumn(idx); + + return GetEventHandler()->ProcessEvent(event); +} + +bool wxHeaderCtrl::SendClickEvent(bool dblclk, int button, unsigned int idx) { wxEventType evtType; switch ( button ) @@ -282,24 +294,19 @@ bool wxHeaderCtrl::SendClickEvent(bool dblclk, unsigned int idx, int button) return false; } - wxHeaderCtrlEvent event(evtType, GetId()); - event.SetEventObject(this); - event.SetColumn(idx); - - return GetEventHandler()->ProcessEvent(event); + return SendEvent(evtType, idx); } bool wxHeaderCtrl::MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result) { NMHEADER * const nmhdr = (NMHEADER *)lParam; - switch ( nmhdr->hdr.code ) + const int idx = nmhdr->iItem; + switch ( const UINT code = nmhdr->hdr.code ) { case HDN_ITEMCLICK: case HDN_ITEMDBLCLICK: - if ( SendClickEvent(nmhdr->hdr.code == HDN_ITEMDBLCLICK, - nmhdr->iItem, - nmhdr->iButton) ) + if ( SendClickEvent(code == HDN_ITEMDBLCLICK, nmhdr->iButton, idx) ) return true; break; @@ -313,13 +320,20 @@ bool wxHeaderCtrl::MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result) const int col = wxMSWGetColumnClicked(&nmhdr->hdr, &pt); if ( col != wxNOT_FOUND ) { - if ( SendClickEvent(nmhdr->hdr.code == NM_RDBLCLK, col, 1) ) + if ( SendClickEvent(code == NM_RDBLCLK, 1, col) ) return true; } //else: ignore clicks outside any column } break; + + case HDN_DIVIDERDBLCLICK: + if ( SendEvent(wxEVT_COMMAND_HEADER_SEPARATOR_DCLICK, idx) ) + return true; + break; } return wxHeaderCtrlBase::MSWOnNotify(idCtrl, lParam, result); } + +#endif // wxHAS_GENERIC_HEADERCTRL -- 2.45.2