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
This commit is contained in:
Vadim Zeitlin
2008-12-14 22:06:58 +00:00
parent f665539195
commit 040b9528d1
2 changed files with 210 additions and 31 deletions

View File

@@ -71,9 +71,26 @@ private:
// common part of all ctors // common part of all ctors
void Init(); void Init();
// wrapper around Header_InsertItem(): insert the item by using information // wrapper around Header_InsertItem(): insert the item using information
// from GetColumn(idx) and at the given display position if order != -1 // from the given column at the given index
void DoInsertItem(unsigned int idx, int order); 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 // get the event type corresponding to a click or double click event
// (depending on dblclk value) with the specified (using MSW convention) // (depending on dblclk value) with the specified (using MSW convention)
@@ -81,6 +98,27 @@ private:
wxEventType GetClickEventType(bool dblclk, int button); 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 // the image list: initially NULL, created on demand
wxImageList *m_imageList; wxImageList *m_imageList;

View File

@@ -49,6 +49,7 @@ extern int WXDLLIMPEXP_CORE wxMSWGetColumnClicked(NMHDR *nmhdr, POINT *ptClick);
void wxHeaderCtrl::Init() void wxHeaderCtrl::Init()
{ {
m_numColumns = 0;
m_imageList = NULL; m_imageList = NULL;
m_scrollOffset = 0; m_scrollOffset = 0;
} }
@@ -144,7 +145,21 @@ wxSize wxHeaderCtrl::DoGetBestSize() const
unsigned int wxHeaderCtrl::DoGetCount() 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) void wxHeaderCtrl::DoSetCount(unsigned int count)
@@ -152,7 +167,7 @@ void wxHeaderCtrl::DoSetCount(unsigned int count)
unsigned n; unsigned n;
// first delete all old columns // first delete all old columns
const unsigned countOld = DoGetCount(); const unsigned countOld = GetShownColumnsCount();
for ( n = 0; n < countOld; n++ ) for ( n = 0; n < countOld; n++ )
{ {
if ( !Header_DeleteItem(GetHwnd(), 0) ) 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 // and add the new ones
m_numColumns = count;
m_isHidden.resize(m_numColumns);
for ( n = 0; n < count; n++ ) 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 // more complicated as we'd have to reset the old values as well as setting
// the new ones -- so instead just recreate the column // the new ones -- so instead just recreate the column
// we need to preserve the old position ourselves as the column doesn't const wxHeaderColumn& col = GetColumn(idx);
// store it (TODO: should it?) if ( col.IsHidden() )
const unsigned int pos = GetColumnPos(idx); {
Header_DeleteItem(GetHwnd(), idx); // column is hidden now
DoInsertItem(idx, pos); 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));
} }
void wxHeaderCtrl::DoInsertItem(unsigned int idx, int order) DoInsertItem(col, idx);
}
}
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; wxHDITEM hdi;
@@ -260,19 +312,17 @@ void wxHeaderCtrl::DoInsertItem(unsigned int idx, int order)
hdi.fmt |= col.IsSortOrderAscending() ? HDF_SORTUP : HDF_SORTDOWN; 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.mask |= HDI_WIDTH;
hdi.cxy = col.IsHidden() ? 0 : col.GetWidth(); hdi.cxy = col.GetWidth();
} }
if ( order != -1 )
{
hdi.mask |= HDI_ORDER; hdi.mask |= HDI_ORDER;
hdi.iOrder = 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()")); wxLogLastError(_T("Header_InsertItem()"));
} }
@@ -280,19 +330,81 @@ void wxHeaderCtrl::DoInsertItem(unsigned int idx, int order)
void wxHeaderCtrl::DoSetColumnsOrder(const wxArrayInt& 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")); wxLogLastError(_T("Header_GetOrderArray"));
} }
m_colIndices = order;
} }
wxArrayInt wxHeaderCtrl::DoGetColumnsOrder() const wxArrayInt wxHeaderCtrl::DoGetColumnsOrder() const
{ {
const unsigned count = GetColumnCount(); // we don't use Header_GetOrderArray() here because it doesn't return
wxArrayInt order(count); // information about the hidden columns, instead we just save the columns
if ( !Header_GetOrderArray(GetHwnd(), count, &order[0]) ) // order array in DoSetColumnsOrder() and update it when they're reordered
return m_colIndices;
}
int wxHeaderCtrl::MSWToNativeIdx(int idx)
{ {
wxLogLastError(_T("Header_GetOrderArray")); // 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++ )
{
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; return order;
@@ -335,11 +447,22 @@ bool wxHeaderCtrl::MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result)
NMHEADER * const nmhdr = (NMHEADER *)lParam; NMHEADER * const nmhdr = (NMHEADER *)lParam;
wxEventType evtType = wxEVT_NULL; wxEventType evtType = wxEVT_NULL;
int idx = nmhdr->iItem;
int width = 0; int width = 0;
int order = -1; int order = -1;
bool veto = false; bool veto = false;
const UINT code = nmhdr->hdr.code; 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 ) switch ( code )
{ {
// click events // click events
@@ -359,7 +482,10 @@ bool wxHeaderCtrl::MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result)
POINT pt; POINT pt;
idx = wxMSWGetColumnClicked(&nmhdr->hdr, &pt); idx = wxMSWGetColumnClicked(&nmhdr->hdr, &pt);
if ( idx != wxNOT_FOUND ) if ( idx != wxNOT_FOUND )
{
idx = MSWFromNativeIdx(idx);
evtType = GetClickEventType(code == NM_RDBLCLK, 1); evtType = GetClickEventType(code == NM_RDBLCLK, 1);
}
//else: ignore clicks outside any column //else: ignore clicks outside any column
} }
break; break;
@@ -429,7 +555,7 @@ bool wxHeaderCtrl::MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result)
case HDN_BEGINDRAG: case HDN_BEGINDRAG:
// Windows sometimes sends us events with invalid column indices // Windows sometimes sends us events with invalid column indices
if ( idx == -1 ) if ( nmhdr->iItem == -1 )
break; break;
// column must have the appropriate flag to be draggable // column must have the appropriate flag to be draggable
@@ -443,10 +569,17 @@ bool wxHeaderCtrl::MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result)
break; break;
case HDN_ENDDRAG: case HDN_ENDDRAG:
evtType = wxEVT_COMMAND_HEADER_END_REORDER;
wxASSERT_MSG( nmhdr->pitem->mask & HDI_ORDER, "should have order" ); wxASSERT_MSG( nmhdr->pitem->mask & HDI_ORDER, "should have order" );
order = nmhdr->pitem->iOrder; 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; break;
case NM_RELEASEDCAPTURE: case NM_RELEASEDCAPTURE:
@@ -468,12 +601,20 @@ bool wxHeaderCtrl::MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result)
if ( GetEventHandler()->ProcessEvent(event) ) if ( GetEventHandler()->ProcessEvent(event) )
{ {
if ( event.IsAllowed() ) if ( event.IsAllowed() )
return true; return true; // skip default message handling below
// we need to veto the default handling of this message, don't // we need to veto the default handling of this message, don't
// return to execute the code in the "if veto" branch below // return to execute the code in the "if veto" branch below
veto = true; 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 ) if ( veto )