]> git.saurik.com Git - wxWidgets.git/blobdiff - src/generic/grid.cpp
Add wxDataViewCtrl::ExpandAncestors() and call it from both EnsureVisible() and Select()
[wxWidgets.git] / src / generic / grid.cpp
index 739fedfcacf3d0e1a04dfb5fedc74794f34fbd2f..1da201ad364d5a93bfd7606d3c5d274f21ddb0ab 100644 (file)
@@ -146,8 +146,10 @@ DEFINE_EVENT_TYPE(wxEVT_GRID_LABEL_RIGHT_DCLICK)
 DEFINE_EVENT_TYPE(wxEVT_GRID_ROW_SIZE)
 DEFINE_EVENT_TYPE(wxEVT_GRID_COL_SIZE)
 DEFINE_EVENT_TYPE(wxEVT_GRID_COL_MOVE)
+DEFINE_EVENT_TYPE(wxEVT_GRID_COL_SORT)
 DEFINE_EVENT_TYPE(wxEVT_GRID_RANGE_SELECT)
-DEFINE_EVENT_TYPE(wxEVT_GRID_CELL_CHANGE)
+DEFINE_EVENT_TYPE(wxEVT_GRID_CELL_CHANGING)
+DEFINE_EVENT_TYPE(wxEVT_GRID_CELL_CHANGED)
 DEFINE_EVENT_TYPE(wxEVT_GRID_SELECT_CELL)
 DEFINE_EVENT_TYPE(wxEVT_GRID_EDITOR_SHOWN)
 DEFINE_EVENT_TYPE(wxEVT_GRID_EDITOR_HIDDEN)
@@ -183,22 +185,35 @@ public:
 
     virtual int GetFlags() const
     {
-        int flags = 0;
+        // we can't know in advance whether we can sort by this column or not
+        // with wxGrid API so suppose we can by default
+        int flags = wxCOL_SORTABLE;
         if ( m_grid->CanDragColSize() )
             flags |= wxCOL_RESIZABLE;
         if ( m_grid->CanDragColMove() )
             flags |= wxCOL_REORDERABLE;
+        if ( GetWidth() == 0 )
+            flags |= wxCOL_HIDDEN;
 
         return flags;
     }
 
-    // TODO: currently there is no support for sorting
-    virtual bool IsSortKey() const { return false; }
-    virtual bool IsSortOrderAscending() const { return false; }
+    virtual bool IsSortKey() const
+    {
+        return m_grid->IsSortingBy(m_col);
+    }
+
+    virtual bool IsSortOrderAscending() const
+    {
+        return m_grid->IsSortOrderAscending();
+    }
 
 private:
-    wxGrid * const m_grid;
-    const int m_col;
+    // these really should be const but are not because the column needs to be
+    // assignable to be used in a wxVector (in STL build, in non-STL build we
+    // avoid the need for this)
+    wxGrid *m_grid;
+    int m_col;
 };
 
 // header control retreiving column information from the grid
@@ -210,12 +225,13 @@ public:
                        wxID_ANY,
                        wxDefaultPosition,
                        wxDefaultSize,
-                       owner->CanDragColMove() ? wxHD_DRAGDROP : 0)
+                       wxHD_ALLOW_HIDE |
+                       (owner->CanDragColMove() ? wxHD_ALLOW_REORDER : 0))
     {
     }
 
 protected:
-    virtual wxHeaderColumn& GetColumn(unsigned int idx)
+    virtual const wxHeaderColumn& GetColumn(unsigned int idx) const
     {
         return m_columns[idx];
     }
@@ -246,13 +262,37 @@ private:
     // override to implement column auto sizing
     virtual bool UpdateColumnWidthToFit(unsigned int idx, int widthTitle)
     {
+        // TODO: currently grid doesn't support computing the column best width
+        //       from its contents so we just use the best label width as is
         GetOwner()->SetColSize(idx, widthTitle);
 
         return true;
     }
 
+    // overridden to react to the actions using the columns popup menu
+    virtual void UpdateColumnVisibility(unsigned int idx, bool show)
+    {
+        GetOwner()->SetColSize(idx, show ? wxGRID_AUTOSIZE : 0);
+
+        // as this is done by the user we should notify the main program about
+        // it
+        GetOwner()->SendEvent(wxEVT_GRID_COL_SIZE, -1, idx);
+    }
+
+    // overridden to react to the columns order changes in the customization
+    // dialog
+    virtual void UpdateColumnsOrder(const wxArrayInt& order)
+    {
+        GetOwner()->SetColumnsOrder(order);
+    }
+
 
     // event handlers forwarding wxHeaderCtrl events to wxGrid
+    void OnClick(wxHeaderCtrlEvent& event)
+    {
+        GetOwner()->DoColHeaderClick(event.GetColumn());
+    }
+
     void OnBeginResize(wxHeaderCtrlEvent& event)
     {
         GetOwner()->DoStartResizeCol(event.GetColumn());
@@ -272,9 +312,14 @@ private:
         event.Skip();
     }
 
+    void OnBeginReorder(wxHeaderCtrlEvent& event)
+    {
+        GetOwner()->DoStartMoveCol(event.GetColumn());
+    }
+
     void OnEndReorder(wxHeaderCtrlEvent& event)
     {
-        event.Skip(); // TODO: position it at event.GetNewOrder()
+        GetOwner()->DoEndMoveCol(event.GetNewOrder());
     }
 
     wxVector<wxGridHeaderColumn> m_columns;
@@ -284,10 +329,13 @@ private:
 };
 
 BEGIN_EVENT_TABLE(wxGridHeaderCtrl, wxHeaderCtrl)
+    EVT_HEADER_CLICK(wxID_ANY, wxGridHeaderCtrl::OnClick)
+
     EVT_HEADER_BEGIN_RESIZE(wxID_ANY, wxGridHeaderCtrl::OnBeginResize)
     EVT_HEADER_RESIZING(wxID_ANY, wxGridHeaderCtrl::OnResizing)
     EVT_HEADER_END_RESIZE(wxID_ANY, wxGridHeaderCtrl::OnEndResize)
 
+    EVT_HEADER_BEGIN_REORDER(wxID_ANY, wxGridHeaderCtrl::OnBeginReorder)
     EVT_HEADER_END_REORDER(wxID_ANY, wxGridHeaderCtrl::OnEndReorder)
 END_EVENT_TABLE()
 
@@ -613,7 +661,7 @@ public:
     }
 
 
-    // Return the row or column at the given pixel coordinate.
+    // Return the index of the row or column at the given pixel coordinate.
     virtual int
         PosToLine(const wxGrid *grid, int pos, bool clip = false) const = 0;
 
@@ -1212,9 +1260,9 @@ void wxGridCellTextEditor::BeginEdit(int row, int col, wxGrid* grid)
 {
     wxASSERT_MSG(m_control, wxT("The wxGridCellEditor must be created first!"));
 
-    m_startValue = grid->GetTable()->GetValue(row, col);
+    m_value = grid->GetTable()->GetValue(row, col);
 
-    DoBeginEdit(m_startValue);
+    DoBeginEdit(m_value);
 }
 
 void wxGridCellTextEditor::DoBeginEdit(const wxString& startValue)
@@ -1225,31 +1273,35 @@ void wxGridCellTextEditor::DoBeginEdit(const wxString& startValue)
     Text()->SetFocus();
 }
 
-bool wxGridCellTextEditor::EndEdit(int row, int col, wxGrid* grid)
+bool wxGridCellTextEditor::EndEdit(const wxString& WXUNUSED(oldval),
+                                   wxString *newval)
 {
-    wxASSERT_MSG(m_control, wxT("The wxGridCellEditor must be created first!"));
+    wxCHECK_MSG( m_control, false,
+                 "wxGridCellTextEditor must be created first!" );
 
-    bool changed = false;
-    wxString value = Text()->GetValue();
-    if (value != m_startValue)
-        changed = true;
+    const wxString value = Text()->GetValue();
+    if ( value == m_value )
+        return false;
 
-    if (changed)
-        grid->GetTable()->SetValue(row, col, value);
+    m_value = value;
 
-    m_startValue = wxEmptyString;
+    if ( newval )
+        *newval = m_value;
 
-    // No point in setting the text of the hidden control
-    //Text()->SetValue(m_startValue);
+    return true;
+}
 
-    return changed;
+void wxGridCellTextEditor::ApplyEdit(int row, int col, wxGrid* grid)
+{
+    grid->GetTable()->SetValue(row, col, m_value);
+    m_value.clear();
 }
 
 void wxGridCellTextEditor::Reset()
 {
-    wxASSERT_MSG(m_control, wxT("The wxGridCellEditor must be created first!"));
+    wxASSERT_MSG( m_control, "wxGridCellTextEditor must be created first!" );
 
-    DoReset(m_startValue);
+    DoReset(m_value);
 }
 
 void wxGridCellTextEditor::DoReset(const wxString& startValue)
@@ -1391,13 +1443,13 @@ void wxGridCellNumberEditor::BeginEdit(int row, int col, wxGrid* grid)
     wxGridTableBase *table = grid->GetTable();
     if ( table->CanGetValueAs(row, col, wxGRID_VALUE_NUMBER) )
     {
-        m_valueOld = table->GetValueAsLong(row, col);
+        m_value = table->GetValueAsLong(row, col);
     }
     else
     {
-        m_valueOld = 0;
+        m_value = 0;
         wxString sValue = table->GetValue(row, col);
-        if (! sValue.ToLong(&m_valueOld) && ! sValue.empty())
+        if (! sValue.ToLong(&m_value) && ! sValue.empty())
         {
             wxFAIL_MSG( _T("this cell doesn't have numeric value") );
             return;
@@ -1407,7 +1459,7 @@ void wxGridCellNumberEditor::BeginEdit(int row, int col, wxGrid* grid)
 #if wxUSE_SPINCTRL
     if ( HasRange() )
     {
-        Spin()->SetValue((int)m_valueOld);
+        Spin()->SetValue((int)m_value);
         Spin()->SetFocus();
     }
     else
@@ -1417,8 +1469,7 @@ void wxGridCellNumberEditor::BeginEdit(int row, int col, wxGrid* grid)
     }
 }
 
-bool wxGridCellNumberEditor::EndEdit(int row, int col,
-                                     wxGrid* grid)
+bool wxGridCellNumberEditor::EndEdit(const wxString& oldval, wxString *newval)
 {
     long value = 0;
     wxString text;
@@ -1427,7 +1478,7 @@ bool wxGridCellNumberEditor::EndEdit(int row, int col,
     if ( HasRange() )
     {
         value = Spin()->GetValue();
-        if ( value == m_valueOld )
+        if ( value == m_value )
             return false;
 
         text.Printf(wxT("%ld"), value);
@@ -1435,11 +1486,10 @@ bool wxGridCellNumberEditor::EndEdit(int row, int col,
     else // using unconstrained input
 #endif // wxUSE_SPINCTRL
     {
-        const wxString textOld(grid->GetCellValue(row, col));
         text = Text()->GetValue();
         if ( text.empty() )
         {
-            if ( textOld.empty() )
+            if ( oldval.empty() )
                 return false;
         }
         else // non-empty text now (maybe 0)
@@ -1447,20 +1497,28 @@ bool wxGridCellNumberEditor::EndEdit(int row, int col,
             if ( !text.ToLong(&value) )
                 return false;
 
-            // if value == m_valueOld == 0 but old text was "" and new one is
+            // if value == m_value == 0 but old text was "" and new one is
             // "0" something still did change
-            if ( value == m_valueOld && (value || !textOld.empty()) )
+            if ( value == m_value && (value || !oldval.empty()) )
                 return false;
         }
     }
 
+    m_value = value;
+
+    if ( newval )
+        *newval = text;
+
+    return true;
+}
+
+void wxGridCellNumberEditor::ApplyEdit(int row, int col, wxGrid* grid)
+{
     wxGridTableBase * const table = grid->GetTable();
     if ( table->CanSetValueAs(row, col, wxGRID_VALUE_NUMBER) )
-        table->SetValueAsLong(row, col, value);
+        table->SetValueAsLong(row, col, m_value);
     else
-        table->SetValue(row, col, text);
-
-    return true;
+        table->SetValue(row, col, wxString::Format("%ld", m_value));
 }
 
 void wxGridCellNumberEditor::Reset()
@@ -1468,7 +1526,7 @@ void wxGridCellNumberEditor::Reset()
 #if wxUSE_SPINCTRL
     if ( HasRange() )
     {
-        Spin()->SetValue((int)m_valueOld);
+        Spin()->SetValue((int)m_value);
     }
     else
 #endif
@@ -1596,16 +1654,16 @@ void wxGridCellFloatEditor::BeginEdit(int row, int col, wxGrid* grid)
     wxGridTableBase * const table = grid->GetTable();
     if ( table->CanGetValueAs(row, col, wxGRID_VALUE_FLOAT) )
     {
-        m_valueOld = table->GetValueAsDouble(row, col);
+        m_value = table->GetValueAsDouble(row, col);
     }
     else
     {
-        m_valueOld = 0.0;
+        m_value = 0.0;
 
         const wxString value = table->GetValue(row, col);
         if ( !value.empty() )
         {
-            if ( !value.ToDouble(&m_valueOld) )
+            if ( !value.ToDouble(&m_value) )
             {
                 wxFAIL_MSG( _T("this cell doesn't have float value") );
                 return;
@@ -1616,10 +1674,9 @@ void wxGridCellFloatEditor::BeginEdit(int row, int col, wxGrid* grid)
     DoBeginEdit(GetString());
 }
 
-bool wxGridCellFloatEditor::EndEdit(int row, int col, wxGrid* grid)
+bool wxGridCellFloatEditor::EndEdit(const wxString& oldval, wxString *newval)
 {
-    const wxString text(Text()->GetValue()),
-                   textOld(grid->GetCellValue(row, col));
+    const wxString text(Text()->GetValue());
 
     double value;
     if ( !text.empty() )
@@ -1629,7 +1686,7 @@ bool wxGridCellFloatEditor::EndEdit(int row, int col, wxGrid* grid)
     }
     else // new value is empty string
     {
-        if ( textOld.empty() )
+        if ( oldval.empty() )
             return false;           // nothing changed
 
         value = 0.;
@@ -1637,17 +1694,25 @@ bool wxGridCellFloatEditor::EndEdit(int row, int col, wxGrid* grid)
 
     // the test for empty strings ensures that we don't skip the value setting
     // when "" is replaced by "0" or vice versa as "" numeric value is also 0.
-    if ( wxIsSameDouble(value, m_valueOld) && !text.empty() && !textOld.empty() )
+    if ( wxIsSameDouble(value, m_value) && !text.empty() && !oldval.empty() )
         return false;           // nothing changed
 
+    m_value = value;
+
+    if ( newval )
+        *newval = text;
+
+    return true;
+}
+
+void wxGridCellFloatEditor::ApplyEdit(int row, int col, wxGrid* grid)
+{
     wxGridTableBase * const table = grid->GetTable();
 
     if ( table->CanSetValueAs(row, col, wxGRID_VALUE_FLOAT) )
-        table->SetValueAsDouble(row, col, value);
+        table->SetValueAsDouble(row, col, m_value);
     else
-        table->SetValue(row, col, text);
-
-    return true;
+        table->SetValue(row, col, Text()->GetValue());
 }
 
 void wxGridCellFloatEditor::Reset()
@@ -1733,7 +1798,7 @@ wxString wxGridCellFloatEditor::GetString() const
         fmt = _T("%f");
     }
 
-    return wxString::Format(fmt, m_valueOld);
+    return wxString::Format(fmt, m_value);
 }
 
 bool wxGridCellFloatEditor::IsAcceptedKey(wxKeyEvent& event)
@@ -1881,16 +1946,16 @@ void wxGridCellBoolEditor::BeginEdit(int row, int col, wxGrid* grid)
 
     if (grid->GetTable()->CanGetValueAs(row, col, wxGRID_VALUE_BOOL))
     {
-        m_startValue = grid->GetTable()->GetValueAsBool(row, col);
+        m_value = grid->GetTable()->GetValueAsBool(row, col);
     }
     else
     {
         wxString cellval( grid->GetTable()->GetValue(row, col) );
 
         if ( cellval == ms_stringValues[false] )
-            m_startValue = false;
+            m_value = false;
         else if ( cellval == ms_stringValues[true] )
-            m_startValue = true;
+            m_value = true;
         else
         {
             // do not try to be smart here and convert it to true or false
@@ -1901,31 +1966,32 @@ void wxGridCellBoolEditor::BeginEdit(int row, int col, wxGrid* grid)
         }
     }
 
-    CBox()->SetValue(m_startValue);
+    CBox()->SetValue(m_value);
     CBox()->SetFocus();
 }
 
-bool wxGridCellBoolEditor::EndEdit(int row, int col,
-                                   wxGrid* grid)
+bool wxGridCellBoolEditor::EndEdit(const wxString& WXUNUSED(oldval),
+                                   wxString *newval)
 {
-    wxASSERT_MSG(m_control,
-                 wxT("The wxGridCellEditor must be created first!"));
-
-    bool changed = false;
     bool value = CBox()->GetValue();
-    if ( value != m_startValue )
-        changed = true;
+    if ( value == m_value )
+        return false;
 
-    if ( changed )
-    {
-        wxGridTableBase * const table = grid->GetTable();
-        if ( table->CanGetValueAs(row, col, wxGRID_VALUE_BOOL) )
-            table->SetValueAsBool(row, col, value);
-        else
-            table->SetValue(row, col, GetValue());
-    }
+    m_value = value;
+
+    if ( newval )
+        *newval = GetValue();
+
+    return true;
+}
 
-    return changed;
+void wxGridCellBoolEditor::ApplyEdit(int row, int col, wxGrid* grid)
+{
+    wxGridTableBase * const table = grid->GetTable();
+    if ( table->CanSetValueAs(row, col, wxGRID_VALUE_BOOL) )
+        table->SetValueAsBool(row, col, m_value);
+    else
+        table->SetValue(row, col, GetValue());
 }
 
 void wxGridCellBoolEditor::Reset()
@@ -1933,7 +1999,7 @@ void wxGridCellBoolEditor::Reset()
     wxASSERT_MSG(m_control,
                  wxT("The wxGridCellEditor must be created first!"));
 
-    CBox()->SetValue(m_startValue);
+    CBox()->SetValue(m_value);
 }
 
 void wxGridCellBoolEditor::StartingClick()
@@ -2076,9 +2142,9 @@ void wxGridCellChoiceEditor::BeginEdit(int row, int col, wxGrid* grid)
     if (evtHandler)
         evtHandler->SetInSetFocus(true);
 
-    m_startValue = grid->GetTable()->GetValue(row, col);
+    m_value = grid->GetTable()->GetValue(row, col);
 
-    Reset(); // this updates combo box to correspond to m_startValue
+    Reset(); // this updates combo box to correspond to m_value
 
     Combo()->SetFocus();
 
@@ -2092,29 +2158,37 @@ void wxGridCellChoiceEditor::BeginEdit(int row, int col, wxGrid* grid)
     }
 }
 
-bool wxGridCellChoiceEditor::EndEdit(int row, int col,
-                                     wxGrid* grid)
+bool wxGridCellChoiceEditor::EndEdit(const wxString& WXUNUSED(oldval),
+                                     wxString *newval)
 {
-    wxString value = Combo()->GetValue();
-    if ( value == m_startValue )
+    const wxString value = Combo()->GetValue();
+    if ( value == m_value )
         return false;
 
-    grid->GetTable()->SetValue(row, col, value);
+    m_value = value;
+
+    if ( newval )
+        *newval = value;
 
     return true;
 }
 
+void wxGridCellChoiceEditor::ApplyEdit(int row, int col, wxGrid* grid)
+{
+    grid->GetTable()->SetValue(row, col, m_value);
+}
+
 void wxGridCellChoiceEditor::Reset()
 {
     if (m_allowOthers)
     {
-        Combo()->SetValue(m_startValue);
+        Combo()->SetValue(m_value);
         Combo()->SetInsertionPointEnd();
     }
     else // the combobox is read-only
     {
         // find the right position, or default to the first if not found
-        int pos = Combo()->FindString(m_startValue);
+        int pos = Combo()->FindString(m_value);
         if (pos == wxNOT_FOUND)
             pos = 0;
         Combo()->SetSelection(pos);
@@ -4777,7 +4851,7 @@ wxGrid::SetTable(wxGridTableBase *table,
         m_numCols = table->GetNumberCols();
 
         if ( m_useNativeHeader )
-            GetColHeader()->SetColumnCount(m_numCols);
+            GetGridColHeader()->SetColumnCount(m_numCols);
 
         m_table = table;
         m_table->SetView( this );
@@ -4875,6 +4949,9 @@ void wxGrid::Init()
     m_isDragging = false;
     m_startDragPos = wxDefaultPosition;
 
+    m_sortCol = wxNOT_FOUND;
+    m_sortIsAscending = true;
+
     m_useNativeHeader =
     m_nativeColumnLabels = false;
 
@@ -5096,20 +5173,6 @@ bool wxGrid::Redimension( wxGridTableMessage& msg )
     // cell it might want to save that stuff to might no longer exist.
     HideCellEditControl();
 
-#if 0
-    // if we were using the default widths/heights so far, we must change them
-    // now
-    if ( m_colWidths.IsEmpty() )
-    {
-        InitColWidths();
-    }
-
-    if ( m_rowHeights.IsEmpty() )
-    {
-        InitRowHeights();
-    }
-#endif
-
     switch ( msg.GetId() )
     {
         case wxGRIDTABLE_NOTIFY_ROWS_INSERTED:
@@ -5261,7 +5324,7 @@ bool wxGrid::Redimension( wxGridTableMessage& msg )
             m_numCols += numCols;
 
             if ( m_useNativeHeader )
-                GetColHeader()->SetColumnCount(m_numCols);
+                GetGridColHeader()->SetColumnCount(m_numCols);
 
             if ( !m_colAt.IsEmpty() )
             {
@@ -5329,7 +5392,7 @@ bool wxGrid::Redimension( wxGridTableMessage& msg )
             int oldNumCols = m_numCols;
             m_numCols += numCols;
             if ( m_useNativeHeader )
-                GetColHeader()->SetColumnCount(m_numCols);
+                GetGridColHeader()->SetColumnCount(m_numCols);
 
             if ( !m_colAt.IsEmpty() )
             {
@@ -5384,7 +5447,7 @@ bool wxGrid::Redimension( wxGridTableMessage& msg )
             int numCols = msg.GetCommandInt2();
             m_numCols -= numCols;
             if ( m_useNativeHeader )
-                GetColHeader()->SetColumnCount(m_numCols);
+                GetGridColHeader()->SetColumnCount(m_numCols);
 
             if ( !m_colAt.IsEmpty() )
             {
@@ -5599,9 +5662,8 @@ wxGridCellCoordsArray wxGrid::CalcCellsExposed( const wxRegion& reg ) const
         CalcUnscrolledPosition( r.GetRight(), r.GetBottom(), &right, &bottom );
 
         // find the cells within these bounds
-        //
-        int row, col;
-        for ( row = internalYToRow(top); row < m_numRows; row++ )
+        wxArrayInt cols;
+        for ( int row = internalYToRow(top); row < m_numRows; row++ )
         {
             if ( GetRowBottom(row) <= top )
                 continue;
@@ -5609,19 +5671,23 @@ wxGridCellCoordsArray wxGrid::CalcCellsExposed( const wxRegion& reg ) const
             if ( GetRowTop(row) > bottom )
                 break;
 
-            int colPos;
-            for ( colPos = GetColPos( internalXToCol(left) ); colPos < m_numCols; colPos++ )
+            // add all dirty cells in this row: notice that the columns which
+            // are dirty don't depend on the row so we compute them only once
+            // for the first dirty row and then reuse for all the next ones
+            if ( cols.empty() )
             {
-                col = GetColAt( colPos );
-
-                if ( GetColRight(col) <= left )
-                    continue;
+                // do determine the dirty columns
+                for ( int pos = XToPos(left); pos <= XToPos(right); pos++ )
+                    cols.push_back(GetColAt(pos));
 
-                if ( GetColLeft(col) > right )
+                // if there are no dirty columns at all, nothing to do
+                if ( cols.empty() )
                     break;
-
-                cellsExposed.Add( wxGridCellCoords( row, col ) );
             }
+
+            const size_t count = cols.size();
+            for ( size_t n = 0; n < count; n++ )
+                cellsExposed.Add(wxGridCellCoords(row, cols[n]));
         }
 
         ++iter;
@@ -5837,6 +5903,60 @@ void wxGrid::ProcessRowLabelMouseEvent( wxMouseEvent& event )
     }
 }
 
+void wxGrid::UpdateColumnSortingIndicator(int col)
+{
+    wxCHECK_RET( col != wxNOT_FOUND, "invalid column index" );
+
+    if ( m_useNativeHeader )
+        GetGridColHeader()->UpdateColumn(col);
+    else if ( m_nativeColumnLabels )
+        m_colWindow->Refresh();
+    //else: sorting indicator display not yet implemented in grid version
+}
+
+void wxGrid::SetSortingColumn(int col, bool ascending)
+{
+    if ( col == m_sortCol )
+    {
+        // we are already using this column for sorting (or not sorting at all)
+        // but we might still change the sorting order, check for it
+        if ( m_sortCol != wxNOT_FOUND && ascending != m_sortIsAscending )
+        {
+            m_sortIsAscending = ascending;
+
+            UpdateColumnSortingIndicator(m_sortCol);
+        }
+    }
+    else // we're changing the column used for sorting
+    {
+        const int sortColOld = m_sortCol;
+
+        // change it before updating the column as we want GetSortingColumn()
+        // to return the correct new value
+        m_sortCol = col;
+
+        if ( sortColOld != wxNOT_FOUND )
+            UpdateColumnSortingIndicator(sortColOld);
+
+        if ( m_sortCol != wxNOT_FOUND )
+        {
+            m_sortIsAscending = ascending;
+            UpdateColumnSortingIndicator(m_sortCol);
+        }
+    }
+}
+
+void wxGrid::DoColHeaderClick(int col)
+{
+    // we consider that the grid was resorted if this event is processed and
+    // not vetoed
+    if ( SendEvent(wxEVT_GRID_COL_SORT, -1, col) == 1 )
+    {
+        SetSortingColumn(col, IsSortingBy(col) ? !m_sortIsAscending : true);
+        Refresh();
+    }
+}
+
 void wxGrid::DoStartResizeCol(int col)
 {
     m_dragRowOrCol = col;
@@ -5870,10 +5990,11 @@ void wxGrid::DoUpdateResizeColWidth(int w)
 
 void wxGrid::ProcessColLabelMouseEvent( wxMouseEvent& event )
 {
-    int x, y, col;
+    int x, y;
     wxPoint pos( event.GetPosition() );
     CalcUnscrolledPosition( pos.x, pos.y, &x, &y );
 
+    int col = XToCol(x);
     if ( event.Dragging() )
     {
         if (!m_isDragging)
@@ -5881,8 +6002,8 @@ void wxGrid::ProcessColLabelMouseEvent( wxMouseEvent& event )
             m_isDragging = true;
             GetColLabelWindow()->CaptureMouse();
 
-            if ( m_cursorMode == WXGRID_CURSOR_MOVE_COL )
-                m_dragRowOrCol = XToCol( x );
+            if ( m_cursorMode == WXGRID_CURSOR_MOVE_COL && col != -1 )
+                DoStartMoveCol(col);
         }
 
         if ( event.LeftIsDown() )
@@ -5895,7 +6016,7 @@ void wxGrid::ProcessColLabelMouseEvent( wxMouseEvent& event )
 
                 case WXGRID_CURSOR_SELECT_COL:
                 {
-                    if ( (col = XToCol( x )) >= 0 )
+                    if ( col != -1 )
                     {
                         if ( m_selection )
                             m_selection->SelectCol(col, event);
@@ -5905,25 +6026,15 @@ void wxGrid::ProcessColLabelMouseEvent( wxMouseEvent& event )
 
                 case WXGRID_CURSOR_MOVE_COL:
                 {
-                    if ( x < 0 )
-                        m_moveToCol = GetColAt( 0 );
-                    else
-                        m_moveToCol = XToCol( x );
+                    int posNew = XToPos(x);
+                    int colNew = GetColAt(posNew);
 
+                    // determine the position of the drop marker
                     int markerX;
-
-                    if ( m_moveToCol < 0 )
-                        markerX = GetColRight( GetColAt( m_numCols - 1 ) );
-                    else if ( x >= (GetColLeft( m_moveToCol ) + (GetColWidth(m_moveToCol) / 2)) )
-                    {
-                        m_moveToCol = GetColAt( GetColPos( m_moveToCol ) + 1 );
-                        if ( m_moveToCol < 0 )
-                            markerX = GetColRight( GetColAt( m_numCols - 1 ) );
-                        else
-                            markerX = GetColLeft( m_moveToCol );
-                    }
+                    if ( x >= GetColLeft(colNew) + (GetColWidth(colNew) / 2) )
+                        markerX = GetColRight(colNew);
                     else
-                        markerX = GetColLeft( m_moveToCol );
+                        markerX = GetColLeft(colNew);
 
                     if ( markerX != m_dragLastPos )
                     {
@@ -5949,9 +6060,7 @@ void wxGrid::ProcessColLabelMouseEvent( wxMouseEvent& event )
 
                         const wxColour *color;
                         //Moving to the same place? Don't draw a marker
-                        if ( (m_moveToCol == m_dragRowOrCol)
-                          || (GetColPos( m_moveToCol ) == GetColPos( m_dragRowOrCol ) + 1)
-                          || (m_moveToCol < 0 && m_dragRowOrCol == GetColAt( m_numCols - 1 )))
+                        if ( colNew == m_dragRowOrCol )
                             color = wxLIGHT_GREY;
                         else
                             color = wxBLUE;
@@ -6005,7 +6114,6 @@ void wxGrid::ProcessColLabelMouseEvent( wxMouseEvent& event )
         //
         if ( XToEdgeOfCol(x) < 0 )
         {
-            col = XToCol(x);
             if ( col >= 0 &&
                  !SendEvent( wxEVT_GRID_LABEL_LEFT_CLICK, -1, col, event ) )
             {
@@ -6059,10 +6167,9 @@ void wxGrid::ProcessColLabelMouseEvent( wxMouseEvent& event )
     //
     if ( event.LeftDClick() )
     {
-        col = XToEdgeOfCol(x);
-        if ( col < 0 )
+        const int colEdge = XToEdgeOfCol(x);
+        if ( colEdge == -1 )
         {
-            col = XToCol(x);
             if ( col >= 0 &&
                  ! SendEvent( wxEVT_GRID_LABEL_LEFT_DCLICK, -1, col, event ) )
             {
@@ -6072,7 +6179,7 @@ void wxGrid::ProcessColLabelMouseEvent( wxMouseEvent& event )
         else
         {
             // adjust column width depending on label text
-            AutoSizeColLabelSize( col );
+            AutoSizeColLabelSize( colEdge );
 
             ChangeCursorMode(WXGRID_CURSOR_SELECT_CELL, GetColLabelWindow());
             m_dragLastPos = -1;
@@ -6090,16 +6197,25 @@ void wxGrid::ProcessColLabelMouseEvent( wxMouseEvent& event )
                 break;
 
             case WXGRID_CURSOR_MOVE_COL:
-                DoEndDragMoveCol();
-
-                SendEvent( wxEVT_GRID_COL_MOVE, -1, m_dragRowOrCol, event );
+                if ( m_dragLastPos == -1 || col == m_dragRowOrCol )
+                {
+                    // the column didn't actually move anywhere
+                    if ( col != -1 )
+                        DoColHeaderClick(col);
+                    m_colWindow->Refresh();   // "unpress" the column
+                }
+                else
+                {
+                    DoEndMoveCol(XToPos(x));
+                }
                 break;
 
             case WXGRID_CURSOR_SELECT_COL:
             case WXGRID_CURSOR_SELECT_CELL:
             case WXGRID_CURSOR_RESIZE_ROW:
             case WXGRID_CURSOR_SELECT_ROW:
-                // nothing to do (?)
+                if ( col != -1 )
+                    DoColHeaderClick(col);
                 break;
         }
 
@@ -6111,7 +6227,6 @@ void wxGrid::ProcessColLabelMouseEvent( wxMouseEvent& event )
     //
     else if ( event.RightDown() )
     {
-        col = XToCol(x);
         if ( col >= 0 &&
              !SendEvent( wxEVT_GRID_LABEL_RIGHT_CLICK, -1, col, event ) )
         {
@@ -6123,7 +6238,6 @@ void wxGrid::ProcessColLabelMouseEvent( wxMouseEvent& event )
     //
     else if ( event.RightDClick() )
     {
-        col = XToCol(x);
         if ( col >= 0 &&
              !SendEvent( wxEVT_GRID_LABEL_RIGHT_DCLICK, -1, col, event ) )
         {
@@ -6757,109 +6871,100 @@ void wxGrid::DoEndDragResizeCol(wxMouseEvent *event)
         SendEvent( wxEVT_GRID_COL_SIZE, -1, m_dragRowOrCol );
 }
 
-void wxGrid::DoEndDragMoveCol()
+void wxGrid::DoStartMoveCol(int col)
 {
-    //The user clicked on the column but didn't actually drag
-    if ( m_dragLastPos < 0 )
-    {
-        m_colWindow->Refresh();   //Do this to "unpress" the column
-        return;
-    }
+    m_dragRowOrCol = col;
+}
 
-    int newPos;
-    if ( m_moveToCol == -1 )
-        newPos = m_numCols - 1;
-    else
-    {
-        newPos = GetColPos( m_moveToCol );
-        if ( newPos > GetColPos( m_dragRowOrCol ) )
-            newPos--;
-    }
+void wxGrid::DoEndMoveCol(int pos)
+{
+    wxASSERT_MSG( m_dragRowOrCol != -1, "no matching DoStartMoveCol?" );
 
-    SetColPos( m_dragRowOrCol, newPos );
+    if ( SendEvent(wxEVT_GRID_COL_MOVE, -1, m_dragRowOrCol) != -1 )
+        SetColPos(m_dragRowOrCol, pos);
+    //else: vetoed by user
+
+    m_dragRowOrCol = -1;
 }
 
-void wxGrid::SetColPos( int colID, int newPos )
+void wxGrid::RefreshAfterColPosChange()
 {
-    if ( m_colAt.IsEmpty() )
+    // recalculate the column rights as the column positions have changed,
+    // unless we calculate them dynamically because all columns widths are the
+    // same and it's easy to do
+    if ( !m_colWidths.empty() )
     {
-        m_colAt.Alloc( m_numCols );
-
-        int i;
-        for ( i = 0; i < m_numCols; i++ )
+        int colRight = 0;
+        for ( int colPos = 0; colPos < m_numCols; colPos++ )
         {
-            m_colAt.Add( i );
+            int colID = GetColAt( colPos );
+
+            colRight += m_colWidths[colID];
+            m_colRights[colID] = colRight;
         }
     }
 
-    int oldPos = GetColPos( colID );
-
-    //Reshuffle the m_colAt array
-    if ( newPos > oldPos )
+    // and make the changes visible
+    if ( m_useNativeHeader )
     {
-        int i;
-        for ( i = oldPos; i < newPos; i++ )
-        {
-            m_colAt[i] = m_colAt[i+1];
-        }
+        if ( m_colAt.empty() )
+            GetGridColHeader()->ResetColumnsOrder();
+        else
+            GetGridColHeader()->SetColumnsOrder(m_colAt);
     }
     else
     {
-        int i;
-        for ( i = oldPos; i > newPos; i-- )
-        {
-            m_colAt[i] = m_colAt[i-1];
-        }
+        m_colWindow->Refresh();
     }
+    m_gridWin->Refresh();
+}
 
-    m_colAt[newPos] = colID;
+void wxGrid::SetColumnsOrder(const wxArrayInt& order)
+{
+    m_colAt = order;
 
-    //Recalculate the column rights
-    if ( !m_colWidths.IsEmpty() )
-    {
-        int colRight = 0;
-        int colPos;
-        for ( colPos = 0; colPos < m_numCols; colPos++ )
-        {
-            int colID = GetColAt( colPos );
+    RefreshAfterColPosChange();
+}
 
-            colRight += m_colWidths[colID];
-            m_colRights[colID] = colRight;
-        }
+void wxGrid::SetColPos(int idx, int pos)
+{
+    // we're going to need m_colAt now, initialize it if needed
+    if ( m_colAt.empty() )
+    {
+        m_colAt.reserve(m_numCols);
+        for ( int i = 0; i < m_numCols; i++ )
+            m_colAt.push_back(i);
     }
 
-    m_colWindow->Refresh();
-    m_gridWin->Refresh();
+    wxHeaderCtrl::MoveColumnInOrderArray(m_colAt, idx, pos);
+
+    RefreshAfterColPosChange();
 }
 
+void wxGrid::ResetColPos()
+{
+    m_colAt.clear();
 
+    RefreshAfterColPosChange();
+}
 
 void wxGrid::EnableDragColMove( bool enable )
 {
     if ( m_canDragColMove == enable )
         return;
 
-    m_canDragColMove = enable;
-
-    if ( !m_canDragColMove )
+    if ( m_useNativeHeader )
     {
-        m_colAt.Clear();
+        // update all columns to make them [not] reorderable
+        GetGridColHeader()->SetColumnCount(m_numCols);
+    }
 
-        //Recalculate the column rights
-        if ( !m_colWidths.IsEmpty() )
-        {
-            int colRight = 0;
-            int colPos;
-            for ( colPos = 0; colPos < m_numCols; colPos++ )
-            {
-                colRight += m_colWidths[colPos];
-                m_colRights[colPos] = colRight;
-            }
-        }
+    m_canDragColMove = enable;
 
-        m_colWindow->Refresh();
-        m_gridWin->Refresh();
-    }
+    // we use to call ResetColPos() from here if !enable but this doesn't seem
+    // right as it would mean there would be no way to "freeze" the current
+    // columns order by disabling moving them after putting them in the desired
+    // order, whereas now you can always call ResetColPos() manually if needed
 }
 
 
@@ -7027,7 +7132,8 @@ wxGrid::SendEvent(const wxEventType type,
 
 // Generate a grid event of specified type, return value same as above
 //
-int wxGrid::SendEvent(const wxEventType type, int row, int col)
+int
+wxGrid::SendEvent(const wxEventType type, int row, int col, const wxString& s)
 {
    bool claimed, vetoed;
 
@@ -7043,6 +7149,7 @@ int wxGrid::SendEvent(const wxEventType type, int row, int col)
     else
     {
         wxGridEvent gridEvt( GetId(), type, this, row, col );
+        gridEvt.SetString(s);
 
         claimed = GetEventHandler()->ProcessEvent(gridEvt);
         vetoed  = !gridEvt.IsAllowed();
@@ -8136,7 +8243,7 @@ void wxGrid::UseNativeColHeader(bool native)
     CreateColumnWindow();
 
     if ( m_useNativeHeader )
-        GetColHeader()->SetColumnCount(m_numCols);
+        GetGridColHeader()->SetColumnCount(m_numCols);
     CalcWindowSizes();
 }
 
@@ -8204,7 +8311,18 @@ void wxGrid::DrawColLabel(wxDC& dc, int col)
 
     if ( m_nativeColumnLabels )
     {
-        wxRendererNative::Get().DrawHeaderButton(GetColLabelWindow(), dc, rect, 0);
+        wxRendererNative::Get().DrawHeaderButton
+                                (
+                                    GetColLabelWindow(),
+                                    dc,
+                                    rect,
+                                    0,
+                                    IsSortingBy(col)
+                                        ? IsSortOrderAscending()
+                                            ? wxHDR_SORT_ICON_UP
+                                            : wxHDR_SORT_ICON_DOWN
+                                        : wxHDR_SORT_ICON_NONE
+                                );
     }
     else
     {
@@ -8484,7 +8602,6 @@ void wxGrid::EnableCellEditControl( bool enable )
         }
         else
         {
-            //FIXME:add veto support
             SendEvent(wxEVT_GRID_EDITOR_HIDDEN);
 
             HideCellEditControl();
@@ -8717,19 +8834,26 @@ void wxGrid::SaveEditControlValue()
 
         wxGridCellAttr* attr = GetCellAttr(row, col);
         wxGridCellEditor* editor = attr->GetEditor(this, row, col);
-        bool changed = editor->EndEdit(row, col, this);
 
-        editor->DecRef();
-        attr->DecRef();
+        wxString newval;
+        bool changed = editor->EndEdit(oldval, &newval);
 
-        if (changed)
+        if ( changed && SendEvent(wxEVT_GRID_CELL_CHANGING, newval) != -1 )
         {
-            if ( SendEvent(wxEVT_GRID_CELL_CHANGE) == -1 )
+            editor->ApplyEdit(row, col, this);
+
+            // for compatibility reasons dating back to wx 2.8 when this event
+            // was called wxEVT_GRID_CELL_CHANGE and wxEVT_GRID_CELL_CHANGING
+            // didn't exist we allow vetoing this one too
+            if ( SendEvent(wxEVT_GRID_CELL_CHANGED, oldval) == -1 )
             {
                 // Event has been vetoed, set the data back.
                 SetCellValue(row, col, oldval);
             }
         }
+
+        editor->DecRef();
+        attr->DecRef();
     }
 }
 
@@ -8753,15 +8877,14 @@ wxGridCellCoords wxGrid::XYToCell(int x, int y) const
 // m_defaultRowHeight/m_defaultColWidth or binary search on array of
 // m_rowBottoms/m_colRights to do it quickly (linear search shouldn't be used
 // for large grids)
-int
-wxGrid::PosToLine(int coord,
-                  bool clipToMinMax,
-                  const wxGridOperations& oper) const
+int wxGrid::PosToLinePos(int coord,
+                         bool clipToMinMax,
+                         const wxGridOperations& oper) const
 {
     const int numLines = oper.GetNumberOfLines(this);
 
     if ( coord < 0 )
-        return clipToMinMax && numLines > 0 ? oper.GetLineAt(this, 0) : -1;
+        return clipToMinMax && numLines > 0 ? 0 : wxNOT_FOUND;
 
     const int defaultLineSize = oper.GetDefaultLineSize(this);
     wxCHECK_MSG( defaultLineSize, -1, "can't have 0 default line size" );
@@ -8805,12 +8928,12 @@ wxGrid::PosToLine(int coord,
     // check if the position is beyond the last column
     const int lineAtMaxPos = oper.GetLineAt(this, maxPos);
     if ( coord >= lineEnds[lineAtMaxPos] )
-        return clipToMinMax ? lineAtMaxPos : -1;
+        return clipToMinMax ? maxPos : -1;
 
     // or before the first one
     const int lineAt0 = oper.GetLineAt(this, 0);
     if ( coord < lineEnds[lineAt0] )
-        return lineAt0;
+        return 0;
 
 
     // finally do perform the binary search
@@ -8819,10 +8942,10 @@ wxGrid::PosToLine(int coord,
         wxCHECK_MSG( lineEnds[oper.GetLineAt(this, minPos)] <= coord &&
                         coord < lineEnds[oper.GetLineAt(this, maxPos)],
                      -1,
-                     "wxGrid: internal error in PosToLine()" );
+                     "wxGrid: internal error in PosToLinePos()" );
 
         if ( coord >= lineEnds[oper.GetLineAt(this, maxPos - 1)] )
-            return oper.GetLineAt(this, maxPos);
+            return maxPos;
         else
             maxPos--;
 
@@ -8833,7 +8956,17 @@ wxGrid::PosToLine(int coord,
             minPos = median;
     }
 
-    return oper.GetLineAt(this, maxPos);
+    return maxPos;
+}
+
+int
+wxGrid::PosToLine(int coord,
+                  bool clipToMinMax,
+                  const wxGridOperations& oper) const
+{
+    int pos = PosToLinePos(coord, clipToMinMax, oper);
+
+    return pos == wxNOT_FOUND ? wxNOT_FOUND : oper.GetLineAt(this, pos);
 }
 
 int wxGrid::YToRow(int y, bool clipToMinMax) const
@@ -8846,6 +8979,11 @@ int wxGrid::XToCol(int x, bool clipToMinMax) const
     return PosToLine(x, clipToMinMax, wxGridColumnOperations());
 }
 
+int wxGrid::XToPos(int x) const
+{
+    return PosToLinePos(x, true /* clip */, wxGridColumnOperations());
+}
+
 // return the row number that that the y coord is near the edge of, or -1 if
 // not near an edge.
 //
@@ -9495,7 +9633,7 @@ void wxGrid::SetColLabelValue( int col, const wxString& s )
         {
             if ( m_useNativeHeader )
             {
-                GetColHeader()->UpdateColumn(col);
+                GetGridColHeader()->UpdateColumn(col);
             }
             else
             {
@@ -10319,15 +10457,12 @@ void wxGrid::SetColSize( int col, int width )
         width = wxMax(width, GetColMinimalAcceptableWidth());
     }
 
-    // should we check that it's bigger than GetColMinimalWidth(col) here?
-    //                                                                 (VZ)
-    // No, because it is reasonable to assume the library user know's
-    // what he is doing. However we should test against the weaker
-    // constraint of minimalAcceptableWidth, as this breaks rendering
-    //
-    // This test then fixes sf.net bug #645734
-
-    if ( width < GetColMinimalAcceptableWidth() )
+    // we intentionally don't test whether the width is less than
+    // GetColMinimalWidth() here but we do compare it with
+    // GetColMinimalAcceptableWidth() as otherwise things currently break (see
+    // #651) -- and we also always allow the width of 0 as it has the special
+    // sense of hiding the column
+    if ( width > 0 && width < GetColMinimalAcceptableWidth() )
         return;
 
     if ( m_colWidths.IsEmpty() )
@@ -10336,9 +10471,11 @@ void wxGrid::SetColSize( int col, int width )
         InitColWidths();
     }
 
-    int w = wxMax( 0, width );
-    int diff = w - m_colWidths[col];
-    m_colWidths[col] = w;
+    const int diff = width - m_colWidths[col];
+    m_colWidths[col] = width;
+    if ( m_useNativeHeader )
+        GetGridColHeader()->UpdateColumn(col);
+    //else: will be refreshed when the header is redrawn
 
     for ( int colPos = GetColPos(col); colPos < m_numCols; colPos++ )
     {
@@ -10504,7 +10641,7 @@ wxGrid::AutoSizeColOrRow(int colOrRow, bool setAsMin, wxGridDirection direction)
         {
             if ( m_useNativeHeader )
             {
-                GetColHeader()->UpdateColumn(col);
+                GetGridColHeader()->UpdateColumn(col);
             }
             else
             {