implement column reordering support in wxMSW wxHeaderCtrl; use it in wxDataViewCtrl (and make difference between column indices and positions more clear in it)

git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@57232 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
This commit is contained in:
Vadim Zeitlin
2008-12-10 15:56:11 +00:00
parent fbc25b5107
commit 702f5349c6
7 changed files with 323 additions and 57 deletions

View File

@@ -485,6 +485,12 @@ public: // utility functions not part of the API
wxWindow *GetMainWindow() { return (wxWindow*) m_clientArea; }
// return the index of the given column in m_cols
int GetColumnIndex(const wxDataViewColumn *column) const;
// return the column displayed at the given position in the control
wxDataViewColumn *GetColumnAt(unsigned int pos) const;
private:
wxDataViewColumnList m_cols;
wxDataViewModelNotifier *m_notifier;

View File

@@ -13,6 +13,7 @@
#include "wx/control.h"
#include "wx/dynarray.h"
#include "wx/vector.h"
#include "wx/headercol.h"
@@ -85,6 +86,18 @@ public:
DoUpdate(idx);
}
// set the columns order: the array defines the column index which appears
// the given position, it must have GetColumnCount() elements and contain
// all indices exactly once
void SetColumnsOrder(const wxArrayInt& order);
wxArrayInt GetColumnsOrder() const;
// get the index of the column at the given display position
unsigned int GetColumnAt(unsigned int pos) const;
// get the position at which this column is currently displayed
unsigned int GetColumnPos(unsigned int idx) const;
// implementation only from now on
// -------------------------------
@@ -120,6 +133,9 @@ private:
virtual void DoScrollHorz(int dx) = 0;
virtual void DoSetColumnsOrder(const wxArrayInt& order) = 0;
virtual wxArrayInt DoGetColumnsOrder() const = 0;
// this window doesn't look nice with the border so don't use it by default
virtual wxBorder GetDefaultBorder() const { return wxBORDER_NONE; }
@@ -272,6 +288,7 @@ public:
: wxNotifyEvent(commandType, winid),
m_col(-1),
m_width(0),
m_order(static_cast<unsigned int>(-1)),
m_cancelled(false)
{
}
@@ -280,6 +297,7 @@ public:
: wxNotifyEvent(event),
m_col(event.m_col),
m_width(event.m_width),
m_order(event.m_order),
m_cancelled(event.m_cancelled)
{
}
@@ -292,6 +310,10 @@ public:
int GetWidth() const { return m_width; }
void SetWidth(int width) { m_width = width; }
// the new position of the column: for end reorder events only
unsigned int GetNewOrder() const { return m_order; }
void SetNewOrder(unsigned int order) { m_order = order; }
// was the drag operation cancelled or did it complete successfully?
bool IsCancelled() const { return m_cancelled; }
void SetCancelled() { m_cancelled = true; }
@@ -305,6 +327,9 @@ protected:
// the current width for the dragging events
int m_width;
// the new column position for end reorder event
unsigned int m_order;
// was the drag operation cancelled?
bool m_cancelled;
@@ -327,6 +352,9 @@ extern WXDLLIMPEXP_CORE const wxEventType wxEVT_COMMAND_HEADER_BEGIN_RESIZE;
extern WXDLLIMPEXP_CORE const wxEventType wxEVT_COMMAND_HEADER_RESIZING;
extern WXDLLIMPEXP_CORE const wxEventType wxEVT_COMMAND_HEADER_END_RESIZE;
extern WXDLLIMPEXP_CORE const wxEventType wxEVT_COMMAND_HEADER_BEGIN_REORDER;
extern WXDLLIMPEXP_CORE const wxEventType wxEVT_COMMAND_HEADER_END_REORDER;
typedef void (wxEvtHandler::*wxHeaderCtrlEventFunction)(wxHeaderCtrlEvent&);
#define wxHeaderCtrlEventHandler(func) \
@@ -350,4 +378,7 @@ typedef void (wxEvtHandler::*wxHeaderCtrlEventFunction)(wxHeaderCtrlEvent&);
#define EVT_HEADER_RESIZING(id, fn) wx__DECLARE_HEADER_EVT(RESIZING, id, fn)
#define EVT_HEADER_END_RESIZE(id, fn) wx__DECLARE_HEADER_EVT(END_RESIZE, id, fn)
#define EVT_HEADER_BEGIN_REORDER(id, fn) wx__DECLARE_HEADER_EVT(BEGIN_REORDER, id, fn)
#define EVT_HEADER_END_REORDER(id, fn) wx__DECLARE_HEADER_EVT(END_REORDER, id, fn)
#endif // _WX_HEADERCTRL_H_

View File

@@ -55,6 +55,9 @@ private:
virtual void DoScrollHorz(int dx);
virtual void DoSetColumnsOrder(const wxArrayInt& order);
virtual wxArrayInt DoGetColumnsOrder() const;
// override wxWindow methods which must be implemented by a new control
virtual wxSize DoGetBestSize() const;
@@ -66,8 +69,8 @@ private:
void Init();
// wrapper around Header_InsertItem(): insert the item by using information
// from GetColumn(idx)
void DoInsertItem(unsigned int idx);
// from GetColumn(idx) and at the given display position if order != -1
void DoInsertItem(unsigned int idx, int order);
// get the event type corresponding to a click or double click event
// (depending on dblclk value) with the specified (using MSW convention)

View File

@@ -86,6 +86,20 @@
or the resizing was cancelled. If wxHeaderCtrlEvent::IsCancelled()
returns @true, nothing should be done, otherwise the column should
normally be resized to the value of wxHeaderCtrlEvent::GetWidth().
@event{EVT_HEADER_BEGIN_REORDER(id, func)}
The user started to drag the column with the specified index (this
can only happen for the controls with wxHD_DRAGDROP style). This
event can be vetoed to prevent the column from being reordered,
otherwise the end reorder message will be generated later.
@event{EVT_HEADER_END_REORDER(id, func)}
Either the user dropped the column in its new location or the
drag operation was cancelled. If wxHeaderCtrlEvent::IsCancelled()
returns @true, nothing should be done, otherwise the event can be
vetoed to prevent the column from being placed at the new position
or handled to update the display of the data in the associated
control to match the new column location (available from
wxHeaderCtrlEvent::GetNewOrder()).
@endEventTable
@library{wxcore}
@@ -195,6 +209,62 @@ public:
*/
void UpdateColumn(unsigned int idx);
/**
Change the columns display order.
The display order defines the order in which the columns appear on the
screen and does @em not affect the interpretation of indices by all the
other class methods.
The @a order array specifies the column indices corresponding to the
display positions.
@param order
A permutation of all column indices, i.e. an array of size
GetColumnsOrder() containing all column indices exactly once. The
n-th element of this array defines the index of the column shown at
the n-th position from left (for the default left-to-right writing
direction).
@see wxListCtrl::SetColumnsOrder()
*/
void SetColumnsOrder(const wxArrayInt& order);
/**
Return the array describing the columns display order.
For the controls without wxHD_DRAGDROP style the returned array will be
the same as was passed to SetColumnsOrder() previously or define the
default order (with n-th element being n) if it hadn't been called. But
for the controls with wxHD_DRAGDROP style, the columns can be also
reordered by user.
*/
wxArrayInt GetColumnsOrder() const;
/**
Return the index of the column displayed at the given position.
@param pos
The display position, e.g. 0 for the left-most column, 1 for the
next one and so on until GetColumnCount() - 1.
@see GetColumnPos()
*/
unsigned int GetColumnAt(unsigned int pos) const;
/**
Get the position at which this column is currently displayed.
Notice that a valid position is returned even for the hidden columns
currently.
@param idx
The column index, must be less than GetColumnCount().
@see GetColumnAt()
*/
unsigned int GetColumnPos(unsigned int idx) const;
protected:
/**
Method to be implemented by the derived classes to return the
@@ -430,6 +500,16 @@ public:
*/
int GetWidth() const;
/**
Return the new order of the column.
This method can only be called for end reorder event for which it
indicates the tentative new position for the column GetColumn()
selected by the user. If the event is not vetoed, this will become the
new column position in wxHeaderCtrl::GetColumnsOrder().
*/
unsigned int GetNewOrder() const;
/**
Return @true if the drag operation was cancelled.

View File

@@ -64,6 +64,10 @@ void wxHeaderCtrlBase::ScrollWindow(int dx,
DoScrollHorz(dx);
}
// ----------------------------------------------------------------------------
// wxHeaderCtrlBase event handling
// ----------------------------------------------------------------------------
void wxHeaderCtrlBase::OnSeparatorDClick(wxHeaderCtrlEvent& event)
{
const unsigned col = event.GetColumn();
@@ -77,6 +81,65 @@ void wxHeaderCtrlBase::OnSeparatorDClick(wxHeaderCtrlEvent& event)
UpdateColumn(col);
}
// ----------------------------------------------------------------------------
// wxHeaderCtrlBase column reordering
// ----------------------------------------------------------------------------
void wxHeaderCtrlBase::SetColumnsOrder(const wxArrayInt& order)
{
const unsigned count = GetColumnCount();
wxCHECK_RET( order.size() == count, "wrong number of columns" );
// check the array validity
wxArrayInt seen(count, 0);
for ( unsigned n = 0; n < count; n++ )
{
const unsigned idx = order[n];
wxCHECK_RET( idx < count, "invalid column index" );
wxCHECK_RET( !seen[idx], "duplicate column index" );
seen[idx] = 1;
}
DoSetColumnsOrder(order);
// TODO-RTL: do we need to reverse the array?
}
wxArrayInt wxHeaderCtrlBase::GetColumnsOrder() const
{
const wxArrayInt order = DoGetColumnsOrder();
wxASSERT_MSG( order.size() == GetColumnCount(), "invalid order array" );
return order;
}
unsigned int wxHeaderCtrlBase::GetColumnAt(unsigned int pos) const
{
wxCHECK_MSG( pos < GetColumnCount(), wxNO_COLUMN, "invalid position" );
return GetColumnsOrder()[pos];
}
unsigned int wxHeaderCtrlBase::GetColumnPos(unsigned int idx) const
{
const unsigned count = GetColumnCount();
wxCHECK_MSG( idx < count, wxNO_COLUMN, "invalid index" );
const wxArrayInt order = GetColumnsOrder();
for ( unsigned n = 0; n < count; n++ )
{
if ( (unsigned)order[n] == idx )
return n;
}
wxFAIL_MSG( "column unexpectedly not displayed at all" );
return wxNO_COLUMN;
}
// ============================================================================
// wxHeaderCtrlSimple implementation
// ============================================================================
@@ -181,3 +244,6 @@ const wxEventType wxEVT_COMMAND_HEADER_SEPARATOR_DCLICK = wxNewEventType();
const wxEventType wxEVT_COMMAND_HEADER_BEGIN_RESIZE = wxNewEventType();
const wxEventType wxEVT_COMMAND_HEADER_RESIZING = wxNewEventType();
const wxEventType wxEVT_COMMAND_HEADER_END_RESIZE = wxNewEventType();
const wxEventType wxEVT_COMMAND_HEADER_BEGIN_REORDER = wxNewEventType();
const wxEventType wxEVT_COMMAND_HEADER_END_REORDER = wxNewEventType();

View File

@@ -184,6 +184,13 @@ private:
}
}
void OnEndReorder(wxHeaderCtrlEvent& event)
{
wxDataViewCtrl * const owner = GetOwner();
owner->ColumnMoved(owner->GetColumn(event.GetColumn()),
event.GetNewOrder());
}
DECLARE_EVENT_TABLE()
DECLARE_NO_COPY_CLASS(wxDataViewHeaderWindow)
};
@@ -193,6 +200,8 @@ BEGIN_EVENT_TABLE(wxDataViewHeaderWindow, wxHeaderCtrl)
EVT_HEADER_RIGHT_CLICK(wxID_ANY, wxDataViewHeaderWindow::OnRClick)
EVT_HEADER_END_RESIZE(wxID_ANY, wxDataViewHeaderWindow::OnEndResize)
EVT_HEADER_END_REORDER(wxID_ANY, wxDataViewHeaderWindow::OnEndReorder)
END_EVENT_TABLE()
//-----------------------------------------------------------------------------
@@ -1227,7 +1236,7 @@ void wxDataViewMainWindow::OnPaint( wxPaintEvent &WXUNUSED(event) )
unsigned int x_start;
for (x_start = 0; col_start < cols; col_start++)
{
wxDataViewColumn *col = GetOwner()->GetColumn(col_start);
wxDataViewColumn *col = GetOwner()->GetColumnAt(col_start);
if (col->IsHidden())
continue; // skip it!
@@ -1242,7 +1251,7 @@ void wxDataViewMainWindow::OnPaint( wxPaintEvent &WXUNUSED(event) )
unsigned int x_last = x_start;
for (; col_last < cols; col_last++)
{
wxDataViewColumn *col = GetOwner()->GetColumn(col_last);
wxDataViewColumn *col = GetOwner()->GetColumnAt(col_last);
if (col->IsHidden())
continue; // skip it!
@@ -1274,7 +1283,7 @@ void wxDataViewMainWindow::OnPaint( wxPaintEvent &WXUNUSED(event) )
int x = x_start;
for (unsigned int i = col_start; i < col_last; i++)
{
wxDataViewColumn *col = GetOwner()->GetColumn(i);
wxDataViewColumn *col = GetOwner()->GetColumnAt(i);
if (col->IsHidden())
continue; // skip it
@@ -1315,8 +1324,8 @@ void wxDataViewMainWindow::OnPaint( wxPaintEvent &WXUNUSED(event) )
wxDataViewColumn *expander = GetOwner()->GetExpanderColumn();
if (!expander)
{
// TODO: last column for RTL support
expander = GetOwner()->GetColumn( 0 );
// TODO-RTL: last column for RTL support
expander = GetOwner()->GetColumnAt( 0 );
GetOwner()->SetExpanderColumn(expander);
}
@@ -1326,7 +1335,7 @@ void wxDataViewMainWindow::OnPaint( wxPaintEvent &WXUNUSED(event) )
for (unsigned int i = col_start; i < col_last; i++)
{
wxDataViewColumn *col = GetOwner()->GetColumn( i );
wxDataViewColumn *col = GetOwner()->GetColumnAt( i );
wxDataViewRenderer *cell = col->GetRenderer();
cell_rect.width = col->GetWidth();
@@ -1476,7 +1485,7 @@ void wxDataViewMainWindow::OnRenameTimer()
unsigned int i;
for (i = 0; i < cols; i++)
{
wxDataViewColumn *c = GetOwner()->GetColumn( i );
wxDataViewColumn *c = GetOwner()->GetColumnAt( i );
if (c->IsHidden())
continue; // skip it!
@@ -1787,7 +1796,7 @@ void wxDataViewMainWindow::ScrollTo( int rows, int column )
m_owner->CalcUnscrolledPosition( rect.x, rect.y, &xx, &yy );
for (x_start = 0; colnum < column; colnum++)
{
wxDataViewColumn *col = GetOwner()->GetColumn(colnum);
wxDataViewColumn *col = GetOwner()->GetColumnAt(colnum);
if (col->IsHidden())
continue; // skip it!
@@ -1822,7 +1831,7 @@ int wxDataViewMainWindow::GetEndOfLastCol() const
for (i = 0; i < GetOwner()->GetColumnCount(); i++)
{
const wxDataViewColumn *c =
const_cast<wxDataViewCtrl*>(GetOwner())->GetColumn( i );
const_cast<wxDataViewCtrl*>(GetOwner())->GetColumnAt( i );
if (!c->IsHidden())
width += c->GetWidth();
@@ -2549,7 +2558,7 @@ void wxDataViewMainWindow::HitTest( const wxPoint & point, wxDataViewItem & item
m_owner->CalcUnscrolledPosition( point.x, point.y, &x, &y );
for (unsigned x_start = 0; colnum < cols; colnum++)
{
col = GetOwner()->GetColumn(colnum);
col = GetOwner()->GetColumnAt(colnum);
if (col->IsHidden())
continue; // skip it!
@@ -2573,9 +2582,9 @@ wxRect wxDataViewMainWindow::GetItemRect( const wxDataViewItem & item, const wxD
wxDataViewColumn *col = NULL;
for( int i = 0, cols = GetOwner()->GetColumnCount(); i < cols; i ++ )
{
col = GetOwner()->GetColumn( i );
col = GetOwner()->GetColumnAt( i );
x += col->GetWidth();
if( GetOwner()->GetColumn(i+1) == column )
if( GetOwner()->GetColumnAt(i+1) == column )
break;
}
int w = col->GetWidth();
@@ -2864,7 +2873,7 @@ void wxDataViewMainWindow::OnMouse( wxMouseEvent &event )
unsigned int i;
for (i = 0; i < cols; i++)
{
wxDataViewColumn *c = GetOwner()->GetColumn( i );
wxDataViewColumn *c = GetOwner()->GetColumnAt( i );
if (c->IsHidden())
continue; // skip it!
@@ -3399,16 +3408,34 @@ wxDataViewColumn* wxDataViewCtrl::GetColumn( unsigned int idx ) const
return m_cols[idx];
}
void wxDataViewCtrl::ColumnMoved( wxDataViewColumn* col, unsigned int new_pos )
wxDataViewColumn *wxDataViewCtrl::GetColumnAt(unsigned int pos) const
{
if (new_pos > m_cols.GetCount()) return;
// columns can't be reordered if there is no header window which allows
// to do this
const unsigned idx = m_headerArea ? m_headerArea->GetColumnsOrder()[pos]
: pos;
// Exchange position
m_cols.DeleteContents(false);
m_cols.DeleteObject( col );
m_cols.Insert( new_pos, col );
m_cols.DeleteContents(true);
return GetColumn(idx);
}
int wxDataViewCtrl::GetColumnIndex(const wxDataViewColumn *column) const
{
const unsigned count = m_cols.size();
for ( unsigned n = 0; n < count; n++ )
{
if ( m_cols[n] == column )
return n;
}
return wxNOT_FOUND;
}
void wxDataViewCtrl::ColumnMoved(wxDataViewColumn * WXUNUSED(col),
unsigned int WXUNUSED(new_pos))
{
// do _not_ reorder m_cols elements here, they should always be in the
// order in which columns were added, we only display the columns in
// different order
m_clientArea->UpdateDisplay();
}
@@ -3433,17 +3460,18 @@ bool wxDataViewCtrl::ClearColumns()
int wxDataViewCtrl::GetColumnPosition( const wxDataViewColumn *column ) const
{
int ret = 0, dead = 0;
int len = GetColumnCount();
for (int i=0; i<len; i++)
int ret = 0,
dummy = 0;
unsigned int len = GetColumnCount();
for ( unsigned int i = 0; i < len; i++ )
{
wxDataViewColumn * col = GetColumn(i);
wxDataViewColumn * col = GetColumnAt(i);
if (col->IsHidden())
continue;
ret += col->GetWidth();
if (column==col)
{
CalcScrolledPosition( ret, dead, &ret, &dead );
CalcScrolledPosition( ret, dummy, &ret, &dummy );
break;
}
}
@@ -3619,17 +3647,7 @@ void wxDataViewCtrl::EnsureVisible( const wxDataViewItem & item, const wxDataVie
if( column == NULL )
EnsureVisible(row, -1);
else
{
int col = 0;
int len = GetColumnCount();
for( int i = 0; i < len; i ++ )
if( GetColumn(i) == column )
{
col = i;
break;
}
EnsureVisible( row, col );
}
EnsureVisible( row, GetColumnIndex(column) );
}
}

View File

@@ -151,7 +151,7 @@ void wxHeaderCtrl::DoSetCount(unsigned int count)
// and add the new ones
for ( n = 0; n < count; n++ )
{
DoInsertItem(n);
DoInsertItem(n, -1 /* default order, i.e. append */);
}
}
@@ -162,11 +162,15 @@ void wxHeaderCtrl::DoUpdate(unsigned int idx)
// arrange not to block setting the width from there and the logic would be
// 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);
DoInsertItem(idx, pos);
}
void wxHeaderCtrl::DoInsertItem(unsigned int idx)
void wxHeaderCtrl::DoInsertItem(unsigned int idx, int order)
{
const wxHeaderColumnBase& col = GetColumn(idx);
@@ -249,12 +253,38 @@ void wxHeaderCtrl::DoInsertItem(unsigned int idx)
hdi.cxy = col.IsHidden() ? 0 : col.GetWidth();
}
if ( order != -1 )
{
hdi.mask |= HDI_ORDER;
hdi.iOrder = order;
}
if ( ::SendMessage(GetHwnd(), HDM_INSERTITEM, idx, (LPARAM)&hdi) == -1 )
{
wxLogLastError(_T("Header_InsertItem()"));
}
}
void wxHeaderCtrl::DoSetColumnsOrder(const wxArrayInt& order)
{
if ( !Header_SetOrderArray(GetHwnd(), order.size(), &order[0]) )
{
wxLogLastError(_T("Header_GetOrderArray"));
}
}
wxArrayInt wxHeaderCtrl::DoGetColumnsOrder() const
{
const unsigned count = GetColumnCount();
wxArrayInt order(count);
if ( !Header_GetOrderArray(GetHwnd(), count, &order[0]) )
{
wxLogLastError(_T("Header_GetOrderArray"));
}
return order;
}
// ----------------------------------------------------------------------------
// wxHeaderCtrl events
// ----------------------------------------------------------------------------
@@ -294,7 +324,9 @@ bool wxHeaderCtrl::MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result)
wxEventType evtType = wxEVT_NULL;
int idx = nmhdr->iItem;
int width = 0;
int order = -1;
bool cancelled = false;
bool veto = false;
const UINT code = nmhdr->hdr.code;
switch ( code )
{
@@ -336,9 +368,8 @@ bool wxHeaderCtrl::MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result)
// even generate any events for them
if ( !GetColumn(idx).IsResizeable() )
{
*result = TRUE;
return true;
veto = true;
break;
}
evtType = wxEVT_COMMAND_HEADER_BEGIN_RESIZE;
@@ -370,14 +401,36 @@ bool wxHeaderCtrl::MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result)
{
// prevent the column from being shrunk beneath its min width
if ( nmhdr->pitem->cxy < GetColumn(idx).GetMinWidth() )
{
*result = TRUE;
return true;
}
veto = true;
}
break;
// column reordering events
// ------------------------
case HDN_BEGINDRAG:
// Windows sometimes sends us events with invalid column indices
if ( idx == -1 )
break;
// column must have the appropriate flag to be draggable
if ( !GetColumn(idx).IsReorderable() )
{
veto = true;
break;
}
evtType = wxEVT_COMMAND_HEADER_BEGIN_REORDER;
break;
case HDN_ENDDRAG:
evtType = wxEVT_COMMAND_HEADER_END_REORDER;
wxASSERT_MSG( nmhdr->pitem->mask & HDI_ORDER, "should have order" );
order = nmhdr->pitem->iOrder;
break;
case NM_RELEASEDCAPTURE:
cancelled = true;
break;
@@ -391,23 +444,32 @@ bool wxHeaderCtrl::MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result)
event.SetEventObject(this);
event.SetColumn(idx);
event.SetWidth(width);
if ( order != -1 )
event.SetNewOrder(order);
if ( cancelled )
event.SetCancelled();
if ( GetEventHandler()->ProcessEvent(event) )
{
if ( !event.IsAllowed() )
{
// all of HDN_BEGIN{DRAG,TRACK}, HDN_TRACK and HDN_ITEMCHANGING
// interpret TRUE return value as meaning to stop the control
// default handling of the message
*result = TRUE;
}
if ( event.IsAllowed() )
return true;
return true;
// 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;
}
}
if ( veto )
{
// all of HDN_BEGIN{DRAG,TRACK}, HDN_TRACK and HDN_ITEMCHANGING
// interpret TRUE return value as meaning to stop the control
// default handling of the message
*result = TRUE;
return true;
}
return wxHeaderCtrlBase::MSWOnNotify(idCtrl, lParam, result);
}