From 040b9528d149c2bb2497d47d9175635c03baffe2 Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Sun, 14 Dec 2008 22:06:58 +0000 Subject: [PATCH] don't use the hack with adding hidden columns to the native control because it doesn't play well with resizing; maintain the mapping between wx and MSW column indices internally even if this is much more work git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@57341 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775 --- include/wx/msw/headerctrl.h | 44 +++++++- src/msw/headerctrl.cpp | 197 +++++++++++++++++++++++++++++++----- 2 files changed, 210 insertions(+), 31 deletions(-) diff --git a/include/wx/msw/headerctrl.h b/include/wx/msw/headerctrl.h index e707cef5f3..38bf952f54 100644 --- a/include/wx/msw/headerctrl.h +++ b/include/wx/msw/headerctrl.h @@ -71,9 +71,26 @@ private: // common part of all ctors void Init(); - // wrapper around Header_InsertItem(): insert the item by using information - // from GetColumn(idx) and at the given display position if order != -1 - void DoInsertItem(unsigned int idx, int order); + // wrapper around Header_InsertItem(): insert the item using information + // from the given column at the given index + void DoInsertItem(const wxHeaderColumn& col, unsigned int idx); + + // get the number of currently visible items: this is also the total number + // of items contained in the native control + int GetShownColumnsCount() const; + + // due to the discrepancy for the hidden columns which we know about but + // the native control does not, there can be a difference between the + // column indices we use and the ones used by the native control; these + // functions translate between them + // + // notice that MSWToNativeIdx() shouldn't be called for hidden columns and + // MSWFromNativeIdx() always returns an index of a visible column + int MSWToNativeIdx(int idx); + int MSWFromNativeIdx(int item); + + // this is the same as above but for order, not index + int MSWFromNativeOrder(int order); // get the event type corresponding to a click or double click event // (depending on dblclk value) with the specified (using MSW convention) @@ -81,6 +98,27 @@ private: wxEventType GetClickEventType(bool dblclk, int button); + // the number of columns in the control, including the hidden ones (not + // taken into account by the native control, see comment in DoGetCount()) + unsigned int m_numColumns; + + // this is a lookup table allowing us to check whether the column with the + // given index is currently shown in the native control, in which case the + // value of this array element with this index is 0, or hidden + // + // notice that this may be different from GetColumn(idx).IsHidden() and in + // fact we need this array precisely because it will be different from it + // in DoUpdate() when the column hidden flag gets toggled and we need it to + // handle this transition correctly + wxArrayInt m_isHidden; + + // the order of our columns: this array contains the index of the column + // shown at the position n as the n-th element + // + // this is necessary only to handle the hidden columns: the native control + // doesn't know about them and so we can't use Header_GetOrderArray() + wxArrayInt m_colIndices; + // the image list: initially NULL, created on demand wxImageList *m_imageList; diff --git a/src/msw/headerctrl.cpp b/src/msw/headerctrl.cpp index fd323a4a1c..ef512e0e74 100644 --- a/src/msw/headerctrl.cpp +++ b/src/msw/headerctrl.cpp @@ -49,6 +49,7 @@ extern int WXDLLIMPEXP_CORE wxMSWGetColumnClicked(NMHDR *nmhdr, POINT *ptClick); void wxHeaderCtrl::Init() { + m_numColumns = 0; m_imageList = NULL; m_scrollOffset = 0; } @@ -144,7 +145,21 @@ wxSize wxHeaderCtrl::DoGetBestSize() const unsigned int wxHeaderCtrl::DoGetCount() const { - return Header_GetItemCount(GetHwnd()); + // we can't use Header_GetItemCount() here because it doesn't take the + // hidden columns into account and we can't find the hidden columns after + // the last shown one in MSWFromNativeIdx() without knowing where to stop + // so we have to store the columns count internally + return m_numColumns; +} + +int wxHeaderCtrl::GetShownColumnsCount() const +{ + const int numItems = Header_GetItemCount(GetHwnd()); + + wxASSERT_MSG( numItems >= 0 && (unsigned)numItems <= m_numColumns, + "unexpected number of items in the native control" ); + + return numItems; } void wxHeaderCtrl::DoSetCount(unsigned int count) @@ -152,7 +167,7 @@ void wxHeaderCtrl::DoSetCount(unsigned int count) unsigned n; // first delete all old columns - const unsigned countOld = DoGetCount(); + const unsigned countOld = GetShownColumnsCount(); for ( n = 0; n < countOld; n++ ) { if ( !Header_DeleteItem(GetHwnd(), 0) ) @@ -161,10 +176,25 @@ void wxHeaderCtrl::DoSetCount(unsigned int count) } } + // update the column indices order array before changing m_numColumns + DoResizeColumnIndices(m_colIndices, count); + // and add the new ones + m_numColumns = count; + m_isHidden.resize(m_numColumns); for ( n = 0; n < count; n++ ) { - DoInsertItem(n, -1 /* default order, i.e. append */); + const wxHeaderColumn& col = GetColumn(n); + if ( col.IsShown() ) + { + m_isHidden[n] = false; + + DoInsertItem(col, n); + } + else // hidden initially + { + m_isHidden[n] = true; + } } } @@ -176,16 +206,38 @@ void wxHeaderCtrl::DoUpdate(unsigned int idx) // more complicated as we'd have to reset the old values as well as setting // the new ones -- so instead just recreate the column - // we need to preserve the old position ourselves as the column doesn't - // store it (TODO: should it?) - const unsigned int pos = GetColumnPos(idx); - Header_DeleteItem(GetHwnd(), idx); - DoInsertItem(idx, pos); + const wxHeaderColumn& col = GetColumn(idx); + if ( col.IsHidden() ) + { + // column is hidden now + if ( !m_isHidden[idx] ) + { + // but it wasn't hidden before, so remove it + Header_DeleteItem(GetHwnd(), MSWToNativeIdx(idx)); + + m_isHidden[idx] = true; + } + //else: nothing to do, updating hidden column doesn't have any effect + } + else // column is shown now + { + if ( m_isHidden[idx] ) + { + m_isHidden[idx] = false; + } + else // and it was shown before as well + { + // we need to remove the old column + Header_DeleteItem(GetHwnd(), MSWToNativeIdx(idx)); + } + + DoInsertItem(col, idx); + } } -void wxHeaderCtrl::DoInsertItem(unsigned int idx, int order) +void wxHeaderCtrl::DoInsertItem(const wxHeaderColumn& col, unsigned int idx) { - const wxHeaderColumn& col = GetColumn(idx); + wxASSERT_MSG( !col.IsHidden(), "should only be called for shown columns" ); wxHDITEM hdi; @@ -260,19 +312,17 @@ void wxHeaderCtrl::DoInsertItem(unsigned int idx, int order) hdi.fmt |= col.IsSortOrderAscending() ? HDF_SORTUP : HDF_SORTDOWN; } - if ( col.GetWidth() != wxCOL_WIDTH_DEFAULT || col.IsHidden() ) + if ( col.GetWidth() != wxCOL_WIDTH_DEFAULT ) { hdi.mask |= HDI_WIDTH; - hdi.cxy = col.IsHidden() ? 0 : col.GetWidth(); + hdi.cxy = col.GetWidth(); } - if ( order != -1 ) - { - hdi.mask |= HDI_ORDER; - hdi.iOrder = order; - } + hdi.mask |= HDI_ORDER; + hdi.iOrder = m_colIndices.Index(idx); - if ( ::SendMessage(GetHwnd(), HDM_INSERTITEM, idx, (LPARAM)&hdi) == -1 ) + if ( ::SendMessage(GetHwnd(), HDM_INSERTITEM, + MSWToNativeIdx(idx), (LPARAM)&hdi) == -1 ) { wxLogLastError(_T("Header_InsertItem()")); } @@ -280,19 +330,81 @@ void wxHeaderCtrl::DoInsertItem(unsigned int idx, int order) void wxHeaderCtrl::DoSetColumnsOrder(const wxArrayInt& order) { - if ( !Header_SetOrderArray(GetHwnd(), order.size(), &order[0]) ) + wxArrayInt orderShown; + orderShown.reserve(m_numColumns); + + for ( unsigned n = 0; n < m_numColumns; n++ ) + { + const int idx = order[n]; + if ( GetColumn(idx).IsShown() ) + orderShown.push_back(MSWToNativeIdx(idx)); + } + + if ( !Header_SetOrderArray(GetHwnd(), orderShown.size(), &orderShown[0]) ) { wxLogLastError(_T("Header_GetOrderArray")); } + + m_colIndices = order; } wxArrayInt wxHeaderCtrl::DoGetColumnsOrder() const { - const unsigned count = GetColumnCount(); - wxArrayInt order(count); - if ( !Header_GetOrderArray(GetHwnd(), count, &order[0]) ) + // we don't use Header_GetOrderArray() here because it doesn't return + // information about the hidden columns, instead we just save the columns + // order array in DoSetColumnsOrder() and update it when they're reordered + return m_colIndices; +} + +int wxHeaderCtrl::MSWToNativeIdx(int idx) +{ + // don't check for GetColumn(idx).IsShown() as it could have just became + // false and we may be called from DoUpdate() to delete the old column + wxASSERT_MSG( !m_isHidden[idx], + "column must be visible to have an " + "index in the native control" ); + + for ( int i = 0; i < idx; i++ ) { - wxLogLastError(_T("Header_GetOrderArray")); + if ( GetColumn(i).IsHidden() ) + idx--; // one less column the native control knows about + } + + return idx; +} + +int wxHeaderCtrl::MSWFromNativeIdx(int item) +{ + wxASSERT_MSG( item >= 0 && item < GetShownColumnsCount(), + "column index out of range" ); + + // reverse the above function + for ( int i = 0; i <= item; i++ ) + { + if ( GetColumn(i).IsHidden() ) + item++; + } + + return item; +} + +int wxHeaderCtrl::MSWFromNativeOrder(int order) +{ + wxASSERT_MSG( order >= 0 && order < GetShownColumnsCount(), + "column position out of range" ); + + // notice that the loop condition is inclusive because if the column + // exactly at position order is hidden we should go to the next one + for ( int pos = 0; pos <= order; pos++ ) + { + if ( GetColumn(m_colIndices[pos]).IsHidden() ) + { + // order can't become greater than m_numColumns here because it is + // less than the number of shown columns initially and it is going + // to be incremented at most once for every hidden column and + // m_numColumns is the total columns number + order++; + } } return order; @@ -335,11 +447,22 @@ bool wxHeaderCtrl::MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result) NMHEADER * const nmhdr = (NMHEADER *)lParam; wxEventType evtType = wxEVT_NULL; - int idx = nmhdr->iItem; int width = 0; int order = -1; bool veto = false; const UINT code = nmhdr->hdr.code; + + // we don't have the index for all events, e.g. not for NM_RELEASEDCAPTURE + // so only access for header control events (and yes, the direction of + // comparisons with FIRST/LAST is correct even if it seems inverted) + int idx = code <= HDN_FIRST && code > HDN_LAST ? nmhdr->iItem : -1; + if ( idx != -1 ) + { + // we also get bogus HDN_BEGINDRAG with -1 index so don't call + // MSWFromNativeIdx() unconditionally for nmhdr->iItem + idx = MSWFromNativeIdx(idx); + } + switch ( code ) { // click events @@ -359,7 +482,10 @@ bool wxHeaderCtrl::MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result) POINT pt; idx = wxMSWGetColumnClicked(&nmhdr->hdr, &pt); if ( idx != wxNOT_FOUND ) + { + idx = MSWFromNativeIdx(idx); evtType = GetClickEventType(code == NM_RDBLCLK, 1); + } //else: ignore clicks outside any column } break; @@ -429,7 +555,7 @@ bool wxHeaderCtrl::MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result) case HDN_BEGINDRAG: // Windows sometimes sends us events with invalid column indices - if ( idx == -1 ) + if ( nmhdr->iItem == -1 ) break; // column must have the appropriate flag to be draggable @@ -443,10 +569,17 @@ bool wxHeaderCtrl::MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result) break; case HDN_ENDDRAG: - evtType = wxEVT_COMMAND_HEADER_END_REORDER; - wxASSERT_MSG( nmhdr->pitem->mask & HDI_ORDER, "should have order" ); order = nmhdr->pitem->iOrder; + + // we also get messages with invalid order when column reordering + // is cancelled (e.g. by pressing Esc) + if ( order == -1 ) + break; + + order = MSWFromNativeOrder(order); + + evtType = wxEVT_COMMAND_HEADER_END_REORDER; break; case NM_RELEASEDCAPTURE: @@ -468,12 +601,20 @@ bool wxHeaderCtrl::MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result) if ( GetEventHandler()->ProcessEvent(event) ) { if ( event.IsAllowed() ) - return true; + return true; // skip default message handling below // we need to veto the default handling of this message, don't // return to execute the code in the "if veto" branch below veto = true; } + else // not processed + { + // special post-processing for HDN_ENDDRAG: we need to update the + // internal column indices array if this is allowed to go ahead as + // the native control is going to reorder its columns now + if ( evtType == wxEVT_COMMAND_HEADER_END_REORDER ) + MoveColumnInOrderArray(m_colIndices, idx, order); + } } if ( veto ) -- 2.45.2