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)
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
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];
}
// 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());
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;
};
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()
}
- // 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;
{
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)
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)
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;
#if wxUSE_SPINCTRL
if ( HasRange() )
{
- Spin()->SetValue((int)m_valueOld);
+ Spin()->SetValue((int)m_value);
Spin()->SetFocus();
}
else
}
}
-bool wxGridCellNumberEditor::EndEdit(int row, int col,
- wxGrid* grid)
+bool wxGridCellNumberEditor::EndEdit(const wxString& oldval, wxString *newval)
{
long value = 0;
wxString text;
if ( HasRange() )
{
value = Spin()->GetValue();
- if ( value == m_valueOld )
+ if ( value == m_value )
return false;
text.Printf(wxT("%ld"), value);
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)
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()
#if wxUSE_SPINCTRL
if ( HasRange() )
{
- Spin()->SetValue((int)m_valueOld);
+ Spin()->SetValue((int)m_value);
}
else
#endif
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;
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() )
}
else // new value is empty string
{
- if ( textOld.empty() )
+ if ( oldval.empty() )
return false; // nothing changed
value = 0.;
// 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()
fmt = _T("%f");
}
- return wxString::Format(fmt, m_valueOld);
+ return wxString::Format(fmt, m_value);
}
bool wxGridCellFloatEditor::IsAcceptedKey(wxKeyEvent& event)
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
}
}
- 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()
wxASSERT_MSG(m_control,
wxT("The wxGridCellEditor must be created first!"));
- CBox()->SetValue(m_startValue);
+ CBox()->SetValue(m_value);
}
void wxGridCellBoolEditor::StartingClick()
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();
}
}
-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);
m_numCols = table->GetNumberCols();
if ( m_useNativeHeader )
- GetColHeader()->SetColumnCount(m_numCols);
+ GetGridColHeader()->SetColumnCount(m_numCols);
m_table = table;
m_table->SetView( this );
m_isDragging = false;
m_startDragPos = wxDefaultPosition;
+ m_sortCol = wxNOT_FOUND;
+ m_sortIsAscending = true;
+
m_useNativeHeader =
m_nativeColumnLabels = false;
// 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:
m_numCols += numCols;
if ( m_useNativeHeader )
- GetColHeader()->SetColumnCount(m_numCols);
+ GetGridColHeader()->SetColumnCount(m_numCols);
if ( !m_colAt.IsEmpty() )
{
int oldNumCols = m_numCols;
m_numCols += numCols;
if ( m_useNativeHeader )
- GetColHeader()->SetColumnCount(m_numCols);
+ GetGridColHeader()->SetColumnCount(m_numCols);
if ( !m_colAt.IsEmpty() )
{
int numCols = msg.GetCommandInt2();
m_numCols -= numCols;
if ( m_useNativeHeader )
- GetColHeader()->SetColumnCount(m_numCols);
+ GetGridColHeader()->SetColumnCount(m_numCols);
if ( !m_colAt.IsEmpty() )
{
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;
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;
}
}
+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;
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)
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() )
case WXGRID_CURSOR_SELECT_COL:
{
- if ( (col = XToCol( x )) >= 0 )
+ if ( col != -1 )
{
if ( m_selection )
m_selection->SelectCol(col, 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 )
{
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;
//
if ( XToEdgeOfCol(x) < 0 )
{
- col = XToCol(x);
if ( col >= 0 &&
!SendEvent( wxEVT_GRID_LABEL_LEFT_CLICK, -1, col, 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 ) )
{
else
{
// adjust column width depending on label text
- AutoSizeColLabelSize( col );
+ AutoSizeColLabelSize( colEdge );
ChangeCursorMode(WXGRID_CURSOR_SELECT_CELL, GetColLabelWindow());
m_dragLastPos = -1;
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;
}
//
else if ( event.RightDown() )
{
- col = XToCol(x);
if ( col >= 0 &&
!SendEvent( wxEVT_GRID_LABEL_RIGHT_CLICK, -1, col, event ) )
{
//
else if ( event.RightDClick() )
{
- col = XToCol(x);
if ( col >= 0 &&
!SendEvent( wxEVT_GRID_LABEL_RIGHT_DCLICK, -1, col, 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
}
// 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;
else
{
wxGridEvent gridEvt( GetId(), type, this, row, col );
+ gridEvt.SetString(s);
claimed = GetEventHandler()->ProcessEvent(gridEvt);
vetoed = !gridEvt.IsAllowed();
CreateColumnWindow();
if ( m_useNativeHeader )
- GetColHeader()->SetColumnCount(m_numCols);
+ GetGridColHeader()->SetColumnCount(m_numCols);
CalcWindowSizes();
}
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
{
}
else
{
- //FIXME:add veto support
SendEvent(wxEVT_GRID_EDITOR_HIDDEN);
HideCellEditControl();
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();
}
}
// 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" );
// 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
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--;
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
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.
//
{
if ( m_useNativeHeader )
{
- GetColHeader()->UpdateColumn(col);
+ GetGridColHeader()->UpdateColumn(col);
}
else
{
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() )
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++ )
{
{
if ( m_useNativeHeader )
{
- GetColHeader()->UpdateColumn(col);
+ GetGridColHeader()->UpdateColumn(col);
}
else
{