]> git.saurik.com Git - wxWidgets.git/commitdiff
implement column reordering support in wxMSW wxHeaderCtrl; use it in wxDataViewCtrl...
authorVadim Zeitlin <vadim@wxwidgets.org>
Wed, 10 Dec 2008 15:56:11 +0000 (15:56 +0000)
committerVadim Zeitlin <vadim@wxwidgets.org>
Wed, 10 Dec 2008 15:56:11 +0000 (15:56 +0000)
git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@57232 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775

include/wx/generic/dataview.h
include/wx/headerctrl.h
include/wx/msw/headerctrl.h
interface/wx/headerctrl.h
src/common/headerctrlcmn.cpp
src/generic/datavgen.cpp
src/msw/headerctrl.cpp

index e26bb1043488109e5e8d484e258daedc146e340c..db9202eea9e084c4d9c1cfcb4830049843f607d6 100644 (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;
index bfb1ece3ad48679746d140b82718fd5d6763a143..322ac6134d129d275f08a8479db1bea9c32f026c 100644 (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_
index 5b268e9ed2a3b4f7697997294a386d0c18072001..9a67856a8597afe06adc6a8c712ddc54e4941e2e 100644 (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)
index f8f23a8aed2fd75926fe305c1b3cabdd7f911eda..a05fff563d50d454785f383231a3fc00063c3859 100644 (file)
             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.
 
index 883e8c7c9c159abd15344123af7912ad3d94ff05..02a18b889e2141d779ab3b7ce4ac86e2a7a4705f 100644 (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();
index 356337231302342a8d67e8525ccf639831835ddc..b5f7c321e794351f527d20f94e56949ce6750a93 100644 (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) );
     }
 
 }
index d6ad5ccddaad6e63e8d7e36efe1fbf91b81c7be7..2e2f75482b788bbcb07cd800fefdfa695ac09f77 100644 (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,12 +401,34 @@ 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;
+                    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:
@@ -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);
 }