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;
#include "wx/control.h"
+#include "wx/dynarray.h"
#include "wx/vector.h"
#include "wx/headercol.h"
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
// -------------------------------
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; }
: wxNotifyEvent(commandType, winid),
m_col(-1),
m_width(0),
+ m_order(static_cast<unsigned int>(-1)),
m_cancelled(false)
{
}
: wxNotifyEvent(event),
m_col(event.m_col),
m_width(event.m_width),
+ m_order(event.m_order),
m_cancelled(event.m_cancelled)
{
}
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; }
// 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;
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) \
#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_
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;
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)
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}
*/
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
*/
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.
DoScrollHorz(dx);
}
+// ----------------------------------------------------------------------------
+// wxHeaderCtrlBase event handling
+// ----------------------------------------------------------------------------
+
void wxHeaderCtrlBase::OnSeparatorDClick(wxHeaderCtrlEvent& event)
{
const unsigned col = event.GetColumn();
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
// ============================================================================
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();
}
}
+ void OnEndReorder(wxHeaderCtrlEvent& event)
+ {
+ wxDataViewCtrl * const owner = GetOwner();
+ owner->ColumnMoved(owner->GetColumn(event.GetColumn()),
+ event.GetNewOrder());
+ }
+
DECLARE_EVENT_TABLE()
DECLARE_NO_COPY_CLASS(wxDataViewHeaderWindow)
};
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()
//-----------------------------------------------------------------------------
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!
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!
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
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);
}
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();
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!
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!
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();
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!
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();
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!
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();
}
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;
}
}
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) );
}
}
// and add the new ones
for ( n = 0; n < count; n++ )
{
- DoInsertItem(n);
+ DoInsertItem(n, -1 /* default order, i.e. append */);
}
}
// 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);
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
// ----------------------------------------------------------------------------
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 )
{
// even generate any events for them
if ( !GetColumn(idx).IsResizeable() )
{
- *result = TRUE;
-
- return true;
+ veto = true;
+ break;
}
evtType = wxEVT_COMMAND_HEADER_BEGIN_RESIZE;
{
// prevent the column from being shrunk beneath its min width
if ( nmhdr->pitem->cxy < GetColumn(idx).GetMinWidth() )
- {
- *result = TRUE;
+ veto = true;
+ }
+ break;
+
+
+ // column reordering events
+ // ------------------------
+
+ case HDN_BEGINDRAG:
+ // Windows sometimes sends us events with invalid column indices
+ if ( idx == -1 )
+ break;
- return true;
- }
+ // 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:
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);
}