+ wxString s;
+ s << row + 1; // RD: Starting the rows at zero confuses users, no matter
+ // how much it makes sense to us geeks.
+ return s;
+}
+
+wxString wxGridTableBase::GetColLabelValue( int col )
+{
+ // default col labels are:
+ // cols 0 to 25 : A-Z
+ // cols 26 to 675 : AA-ZZ
+ // etc.
+
+ wxString s;
+ unsigned int i, n;
+ for ( n = 1; ; n++ )
+ {
+ s += (wxChar) (_T('A') + (wxChar)( col%26 ));
+ col = col/26 - 1;
+ if ( col < 0 ) break;
+ }
+
+ // reverse the string...
+ wxString s2;
+ for ( i = 0; i < n; i++ )
+ {
+ s2 += s[n-i-1];
+ }
+
+ return s2;
+}
+
+
+wxString wxGridTableBase::GetTypeName( int WXUNUSED(row), int WXUNUSED(col) )
+{
+ return wxGRID_VALUE_STRING;
+}
+
+bool wxGridTableBase::CanGetValueAs( int WXUNUSED(row), int WXUNUSED(col),
+ const wxString& typeName )
+{
+ return typeName == wxGRID_VALUE_STRING;
+}
+
+bool wxGridTableBase::CanSetValueAs( int row, int col, const wxString& typeName )
+{
+ return CanGetValueAs(row, col, typeName);
+}
+
+long wxGridTableBase::GetValueAsLong( int WXUNUSED(row), int WXUNUSED(col) )
+{
+ return 0;
+}
+
+double wxGridTableBase::GetValueAsDouble( int WXUNUSED(row), int WXUNUSED(col) )
+{
+ return 0.0;
+}
+
+bool wxGridTableBase::GetValueAsBool( int WXUNUSED(row), int WXUNUSED(col) )
+{
+ return false;
+}
+
+void wxGridTableBase::SetValueAsLong( int WXUNUSED(row), int WXUNUSED(col),
+ long WXUNUSED(value) )
+{
+}
+
+void wxGridTableBase::SetValueAsDouble( int WXUNUSED(row), int WXUNUSED(col),
+ double WXUNUSED(value) )
+{
+}
+
+void wxGridTableBase::SetValueAsBool( int WXUNUSED(row), int WXUNUSED(col),
+ bool WXUNUSED(value) )
+{
+}
+
+
+void* wxGridTableBase::GetValueAsCustom( int WXUNUSED(row), int WXUNUSED(col),
+ const wxString& WXUNUSED(typeName) )
+{
+ return NULL;
+}
+
+void wxGridTableBase::SetValueAsCustom( int WXUNUSED(row), int WXUNUSED(col),
+ const wxString& WXUNUSED(typeName),
+ void* WXUNUSED(value) )
+{
+}
+
+//////////////////////////////////////////////////////////////////////
+//
+// Message class for the grid table to send requests and notifications
+// to the grid view
+//
+
+wxGridTableMessage::wxGridTableMessage()
+{
+ m_table = (wxGridTableBase *) NULL;
+ m_id = -1;
+ m_comInt1 = -1;
+ m_comInt2 = -1;
+}
+
+wxGridTableMessage::wxGridTableMessage( wxGridTableBase *table, int id,
+ int commandInt1, int commandInt2 )
+{
+ m_table = table;
+ m_id = id;
+ m_comInt1 = commandInt1;
+ m_comInt2 = commandInt2;
+}
+
+
+
+//////////////////////////////////////////////////////////////////////
+//
+// A basic grid table for string data. An object of this class will
+// created by wxGrid if you don't specify an alternative table class.
+//
+
+WX_DEFINE_OBJARRAY(wxGridStringArray)
+
+IMPLEMENT_DYNAMIC_CLASS( wxGridStringTable, wxGridTableBase )
+
+wxGridStringTable::wxGridStringTable()
+ : wxGridTableBase()
+{
+}
+
+wxGridStringTable::wxGridStringTable( int numRows, int numCols )
+ : wxGridTableBase()
+{
+ m_data.Alloc( numRows );
+
+ wxArrayString sa;
+ sa.Alloc( numCols );
+ sa.Add( wxEmptyString, numCols );
+
+ m_data.Add( sa, numRows );
+}
+
+wxGridStringTable::~wxGridStringTable()
+{
+}
+
+int wxGridStringTable::GetNumberRows()
+{
+ return m_data.GetCount();
+}
+
+int wxGridStringTable::GetNumberCols()
+{
+ if ( m_data.GetCount() > 0 )
+ return m_data[0].GetCount();
+ else
+ return 0;
+}
+
+wxString wxGridStringTable::GetValue( int row, int col )
+{
+ wxCHECK_MSG( (row < GetNumberRows()) && (col < GetNumberCols()),
+ wxEmptyString,
+ _T("invalid row or column index in wxGridStringTable") );
+
+ return m_data[row][col];
+}
+
+void wxGridStringTable::SetValue( int row, int col, const wxString& value )
+{
+ wxCHECK_RET( (row < GetNumberRows()) && (col < GetNumberCols()),
+ _T("invalid row or column index in wxGridStringTable") );
+
+ m_data[row][col] = value;
+}
+
+bool wxGridStringTable::IsEmptyCell( int row, int col )
+{
+ wxCHECK_MSG( (row < GetNumberRows()) && (col < GetNumberCols()),
+ true,
+ _T("invalid row or column index in wxGridStringTable") );
+
+ return (m_data[row][col] == wxEmptyString);
+}
+
+void wxGridStringTable::Clear()
+{
+ int row, col;
+ int numRows, numCols;
+
+ numRows = m_data.GetCount();
+ if ( numRows > 0 )
+ {
+ numCols = m_data[0].GetCount();
+
+ for ( row = 0; row < numRows; row++ )
+ {
+ for ( col = 0; col < numCols; col++ )
+ {
+ m_data[row][col] = wxEmptyString;
+ }
+ }
+ }
+}
+
+
+bool wxGridStringTable::InsertRows( size_t pos, size_t numRows )
+{
+ size_t curNumRows = m_data.GetCount();
+ size_t curNumCols = ( curNumRows > 0 ? m_data[0].GetCount() :
+ ( GetView() ? GetView()->GetNumberCols() : 0 ) );
+
+ if ( pos >= curNumRows )
+ {
+ return AppendRows( numRows );
+ }
+
+ wxArrayString sa;
+ sa.Alloc( curNumCols );
+ sa.Add( wxEmptyString, curNumCols );
+ m_data.Insert( sa, pos, numRows );
+ if ( GetView() )
+ {
+ wxGridTableMessage msg( this,
+ wxGRIDTABLE_NOTIFY_ROWS_INSERTED,
+ pos,
+ numRows );
+
+ GetView()->ProcessTableMessage( msg );
+ }
+
+ return true;
+}
+
+bool wxGridStringTable::AppendRows( size_t numRows )
+{
+ size_t curNumRows = m_data.GetCount();
+ size_t curNumCols = ( curNumRows > 0 ? m_data[0].GetCount() :
+ ( GetView() ? GetView()->GetNumberCols() : 0 ) );
+
+ wxArrayString sa;
+ if ( curNumCols > 0 )
+ {
+ sa.Alloc( curNumCols );
+ sa.Add( wxEmptyString, curNumCols );
+ }
+
+ m_data.Add( sa, numRows );
+
+ if ( GetView() )
+ {
+ wxGridTableMessage msg( this,
+ wxGRIDTABLE_NOTIFY_ROWS_APPENDED,
+ numRows );
+
+ GetView()->ProcessTableMessage( msg );
+ }
+
+ return true;
+}
+
+bool wxGridStringTable::DeleteRows( size_t pos, size_t numRows )
+{
+ size_t curNumRows = m_data.GetCount();
+
+ if ( pos >= curNumRows )
+ {
+ wxFAIL_MSG( wxString::Format
+ (
+ wxT("Called wxGridStringTable::DeleteRows(pos=%lu, N=%lu)\nPos value is invalid for present table with %lu rows"),
+ (unsigned long)pos,
+ (unsigned long)numRows,
+ (unsigned long)curNumRows
+ ) );
+
+ return false;
+ }
+
+ if ( numRows > curNumRows - pos )
+ {
+ numRows = curNumRows - pos;
+ }
+
+ if ( numRows >= curNumRows )
+ {
+ m_data.Clear();
+ }
+ else
+ {
+ m_data.RemoveAt( pos, numRows );
+ }
+ if ( GetView() )
+ {
+ wxGridTableMessage msg( this,
+ wxGRIDTABLE_NOTIFY_ROWS_DELETED,
+ pos,
+ numRows );
+
+ GetView()->ProcessTableMessage( msg );
+ }
+
+ return true;
+}
+
+bool wxGridStringTable::InsertCols( size_t pos, size_t numCols )
+{
+ size_t row, col;
+
+ size_t curNumRows = m_data.GetCount();
+ size_t curNumCols = ( curNumRows > 0 ? m_data[0].GetCount() :
+ ( GetView() ? GetView()->GetNumberCols() : 0 ) );
+
+ if ( pos >= curNumCols )
+ {
+ return AppendCols( numCols );
+ }
+
+ for ( row = 0; row < curNumRows; row++ )
+ {
+ for ( col = pos; col < pos + numCols; col++ )
+ {
+ m_data[row].Insert( wxEmptyString, col );
+ }
+ }
+ if ( GetView() )
+ {
+ wxGridTableMessage msg( this,
+ wxGRIDTABLE_NOTIFY_COLS_INSERTED,
+ pos,
+ numCols );
+
+ GetView()->ProcessTableMessage( msg );
+ }
+
+ return true;
+}
+
+bool wxGridStringTable::AppendCols( size_t numCols )
+{
+ size_t row;
+
+ size_t curNumRows = m_data.GetCount();
+#if 0
+ if ( !curNumRows )
+ {
+ // TODO: something better than this ?
+ //
+ wxFAIL_MSG( wxT("Unable to append cols to a grid table with no rows.\nCall AppendRows() first") );
+ return false;
+ }
+#endif
+
+ for ( row = 0; row < curNumRows; row++ )
+ {
+ m_data[row].Add( wxEmptyString, numCols );
+ }
+
+ if ( GetView() )
+ {
+ wxGridTableMessage msg( this,
+ wxGRIDTABLE_NOTIFY_COLS_APPENDED,
+ numCols );
+
+ GetView()->ProcessTableMessage( msg );
+ }
+
+ return true;
+}
+
+bool wxGridStringTable::DeleteCols( size_t pos, size_t numCols )
+{
+ size_t row;
+
+ size_t curNumRows = m_data.GetCount();
+ size_t curNumCols = ( curNumRows > 0 ? m_data[0].GetCount() :
+ ( GetView() ? GetView()->GetNumberCols() : 0 ) );
+
+ if ( pos >= curNumCols )
+ {
+ wxFAIL_MSG( wxString::Format
+ (
+ wxT("Called wxGridStringTable::DeleteCols(pos=%lu, N=%lu)\nPos value is invalid for present table with %lu cols"),
+ (unsigned long)pos,
+ (unsigned long)numCols,
+ (unsigned long)curNumCols
+ ) );
+ return false;
+ }
+
+ if ( numCols > curNumCols - pos )
+ {
+ numCols = curNumCols - pos;
+ }
+
+ for ( row = 0; row < curNumRows; row++ )
+ {
+ if ( numCols >= curNumCols )
+ {
+ m_data[row].Clear();
+ }
+ else
+ {
+ m_data[row].RemoveAt( pos, numCols );
+ }
+ }
+ if ( GetView() )
+ {
+ wxGridTableMessage msg( this,
+ wxGRIDTABLE_NOTIFY_COLS_DELETED,
+ pos,
+ numCols );
+
+ GetView()->ProcessTableMessage( msg );
+ }
+
+ return true;
+}
+
+wxString wxGridStringTable::GetRowLabelValue( int row )
+{
+ if ( row > (int)(m_rowLabels.GetCount()) - 1 )
+ {
+ // using default label
+ //
+ return wxGridTableBase::GetRowLabelValue( row );
+ }
+ else
+ {
+ return m_rowLabels[ row ];
+ }
+}
+
+wxString wxGridStringTable::GetColLabelValue( int col )
+{
+ if ( col > (int)(m_colLabels.GetCount()) - 1 )
+ {
+ // using default label
+ //
+ return wxGridTableBase::GetColLabelValue( col );
+ }
+ else
+ {
+ return m_colLabels[ col ];
+ }
+}
+
+void wxGridStringTable::SetRowLabelValue( int row, const wxString& value )
+{
+ if ( row > (int)(m_rowLabels.GetCount()) - 1 )
+ {
+ int n = m_rowLabels.GetCount();
+ int i;
+ for ( i = n; i <= row; i++ )
+ {
+ m_rowLabels.Add( wxGridTableBase::GetRowLabelValue(i) );
+ }
+ }
+
+ m_rowLabels[row] = value;
+}
+
+void wxGridStringTable::SetColLabelValue( int col, const wxString& value )
+{
+ if ( col > (int)(m_colLabels.GetCount()) - 1 )
+ {
+ int n = m_colLabels.GetCount();
+ int i;
+ for ( i = n; i <= col; i++ )
+ {
+ m_colLabels.Add( wxGridTableBase::GetColLabelValue(i) );
+ }
+ }
+
+ m_colLabels[col] = value;
+}
+
+
+
+//////////////////////////////////////////////////////////////////////
+//////////////////////////////////////////////////////////////////////
+
+IMPLEMENT_DYNAMIC_CLASS( wxGridRowLabelWindow, wxWindow )
+
+BEGIN_EVENT_TABLE( wxGridRowLabelWindow, wxWindow )
+ EVT_PAINT( wxGridRowLabelWindow::OnPaint )
+ EVT_MOUSEWHEEL( wxGridRowLabelWindow::OnMouseWheel)
+ EVT_MOUSE_EVENTS( wxGridRowLabelWindow::OnMouseEvent )
+ EVT_KEY_DOWN( wxGridRowLabelWindow::OnKeyDown )
+ EVT_KEY_UP( wxGridRowLabelWindow::OnKeyUp )
+ EVT_CHAR ( wxGridRowLabelWindow::OnChar )
+END_EVENT_TABLE()
+
+wxGridRowLabelWindow::wxGridRowLabelWindow( wxGrid *parent,
+ wxWindowID id,
+ const wxPoint &pos, const wxSize &size )
+ : wxWindow( parent, id, pos, size, wxWANTS_CHARS|wxBORDER_NONE|wxFULL_REPAINT_ON_RESIZE )
+{
+ m_owner = parent;
+}
+
+void wxGridRowLabelWindow::OnPaint( wxPaintEvent& WXUNUSED(event) )
+{
+ wxPaintDC dc(this);
+
+ // NO - don't do this because it will set both the x and y origin
+ // coords to match the parent scrolled window and we just want to
+ // set the y coord - MB
+ //
+ // m_owner->PrepareDC( dc );
+
+ int x, y;
+ m_owner->CalcUnscrolledPosition( 0, 0, &x, &y );
+ dc.SetDeviceOrigin( 0, -y );
+
+ wxArrayInt rows = m_owner->CalcRowLabelsExposed( GetUpdateRegion() );
+ m_owner->DrawRowLabels( dc , rows );
+}
+
+
+void wxGridRowLabelWindow::OnMouseEvent( wxMouseEvent& event )
+{
+ m_owner->ProcessRowLabelMouseEvent( event );
+}
+
+
+void wxGridRowLabelWindow::OnMouseWheel( wxMouseEvent& event )
+{
+ m_owner->GetEventHandler()->ProcessEvent(event);
+}
+
+
+// This seems to be required for wxMotif otherwise the mouse
+// cursor must be in the cell edit control to get key events
+//
+void wxGridRowLabelWindow::OnKeyDown( wxKeyEvent& event )
+{
+ if ( !m_owner->GetEventHandler()->ProcessEvent( event ) ) event.Skip();
+}
+
+void wxGridRowLabelWindow::OnKeyUp( wxKeyEvent& event )
+{
+ if ( !m_owner->GetEventHandler()->ProcessEvent( event ) ) event.Skip();
+}
+
+void wxGridRowLabelWindow::OnChar( wxKeyEvent& event )
+{
+ if ( !m_owner->GetEventHandler()->ProcessEvent( event ) ) event.Skip();
+}
+
+
+
+//////////////////////////////////////////////////////////////////////
+
+IMPLEMENT_DYNAMIC_CLASS( wxGridColLabelWindow, wxWindow )
+
+BEGIN_EVENT_TABLE( wxGridColLabelWindow, wxWindow )
+ EVT_PAINT( wxGridColLabelWindow::OnPaint )
+ EVT_MOUSEWHEEL( wxGridColLabelWindow::OnMouseWheel)
+ EVT_MOUSE_EVENTS( wxGridColLabelWindow::OnMouseEvent )
+ EVT_KEY_DOWN( wxGridColLabelWindow::OnKeyDown )
+ EVT_KEY_UP( wxGridColLabelWindow::OnKeyUp )
+ EVT_CHAR ( wxGridColLabelWindow::OnChar )
+END_EVENT_TABLE()
+
+wxGridColLabelWindow::wxGridColLabelWindow( wxGrid *parent,
+ wxWindowID id,
+ const wxPoint &pos, const wxSize &size )
+ : wxWindow( parent, id, pos, size, wxWANTS_CHARS|wxBORDER_NONE|wxFULL_REPAINT_ON_RESIZE )
+{
+ m_owner = parent;
+}
+
+void wxGridColLabelWindow::OnPaint( wxPaintEvent& WXUNUSED(event) )
+{
+ wxPaintDC dc(this);
+
+ // NO - don't do this because it will set both the x and y origin
+ // coords to match the parent scrolled window and we just want to
+ // set the x coord - MB
+ //
+ // m_owner->PrepareDC( dc );
+
+ int x, y;
+ m_owner->CalcUnscrolledPosition( 0, 0, &x, &y );
+ dc.SetDeviceOrigin( -x, 0 );
+
+ wxArrayInt cols = m_owner->CalcColLabelsExposed( GetUpdateRegion() );
+ m_owner->DrawColLabels( dc , cols );
+}
+
+
+void wxGridColLabelWindow::OnMouseEvent( wxMouseEvent& event )
+{
+ m_owner->ProcessColLabelMouseEvent( event );
+}
+
+void wxGridColLabelWindow::OnMouseWheel( wxMouseEvent& event )
+{
+ m_owner->GetEventHandler()->ProcessEvent(event);
+}
+
+
+// This seems to be required for wxMotif otherwise the mouse
+// cursor must be in the cell edit control to get key events
+//
+void wxGridColLabelWindow::OnKeyDown( wxKeyEvent& event )
+{
+ if ( !m_owner->GetEventHandler()->ProcessEvent( event ) ) event.Skip();
+}
+
+void wxGridColLabelWindow::OnKeyUp( wxKeyEvent& event )
+{
+ if ( !m_owner->GetEventHandler()->ProcessEvent( event ) ) event.Skip();
+}
+
+void wxGridColLabelWindow::OnChar( wxKeyEvent& event )
+{
+ if ( !m_owner->GetEventHandler()->ProcessEvent( event ) ) event.Skip();
+}
+
+
+//////////////////////////////////////////////////////////////////////
+
+IMPLEMENT_DYNAMIC_CLASS( wxGridCornerLabelWindow, wxWindow )
+
+BEGIN_EVENT_TABLE( wxGridCornerLabelWindow, wxWindow )
+ EVT_MOUSEWHEEL( wxGridCornerLabelWindow::OnMouseWheel)
+ EVT_MOUSE_EVENTS( wxGridCornerLabelWindow::OnMouseEvent )
+ EVT_PAINT( wxGridCornerLabelWindow::OnPaint)
+ EVT_KEY_DOWN( wxGridCornerLabelWindow::OnKeyDown )
+ EVT_KEY_UP( wxGridCornerLabelWindow::OnKeyUp )
+ EVT_CHAR ( wxGridCornerLabelWindow::OnChar )
+END_EVENT_TABLE()
+
+wxGridCornerLabelWindow::wxGridCornerLabelWindow( wxGrid *parent,
+ wxWindowID id,
+ const wxPoint &pos, const wxSize &size )
+ : wxWindow( parent, id, pos, size, wxWANTS_CHARS|wxBORDER_NONE|wxFULL_REPAINT_ON_RESIZE )
+{
+ m_owner = parent;
+}
+
+void wxGridCornerLabelWindow::OnPaint( wxPaintEvent& WXUNUSED(event) )
+{
+ wxPaintDC dc(this);
+
+ int client_height = 0;
+ int client_width = 0;
+ GetClientSize( &client_width, &client_height );
+
+#if __WXGTK__
+ wxRect rect;
+ rect.SetX( 1 );
+ rect.SetY( 1 );
+ rect.SetWidth( client_width - 2 );
+ rect.SetHeight( client_height - 2 );
+
+ wxRendererNative::Get().DrawHeaderButton( this, dc, rect, 0 );
+#else
+ dc.SetPen( wxPen(wxSystemSettings::GetColour(wxSYS_COLOUR_3DDKSHADOW),1, wxSOLID) );
+ dc.DrawLine( client_width-1, client_height-1, client_width-1, 0 );
+ dc.DrawLine( client_width-1, client_height-1, 0, client_height-1 );
+ dc.DrawLine( 0, 0, client_width, 0 );
+ dc.DrawLine( 0, 0, 0, client_height );
+
+ dc.SetPen( *wxWHITE_PEN );
+ dc.DrawLine( 1, 1, client_width-1, 1 );
+ dc.DrawLine( 1, 1, 1, client_height-1 );
+#endif
+}
+
+
+void wxGridCornerLabelWindow::OnMouseEvent( wxMouseEvent& event )
+{
+ m_owner->ProcessCornerLabelMouseEvent( event );
+}
+
+
+void wxGridCornerLabelWindow::OnMouseWheel( wxMouseEvent& event )
+{
+ m_owner->GetEventHandler()->ProcessEvent(event);
+}
+
+// This seems to be required for wxMotif otherwise the mouse
+// cursor must be in the cell edit control to get key events
+//
+void wxGridCornerLabelWindow::OnKeyDown( wxKeyEvent& event )
+{
+ if ( !m_owner->GetEventHandler()->ProcessEvent( event ) ) event.Skip();
+}
+
+void wxGridCornerLabelWindow::OnKeyUp( wxKeyEvent& event )
+{
+ if ( !m_owner->GetEventHandler()->ProcessEvent( event ) ) event.Skip();
+}
+
+void wxGridCornerLabelWindow::OnChar( wxKeyEvent& event )
+{
+ if ( !m_owner->GetEventHandler()->ProcessEvent( event ) ) event.Skip();
+}
+
+
+//////////////////////////////////////////////////////////////////////
+
+IMPLEMENT_DYNAMIC_CLASS( wxGridWindow, wxWindow )
+
+BEGIN_EVENT_TABLE( wxGridWindow, wxWindow )
+ EVT_PAINT( wxGridWindow::OnPaint )
+ EVT_MOUSEWHEEL( wxGridWindow::OnMouseWheel)
+ EVT_MOUSE_EVENTS( wxGridWindow::OnMouseEvent )
+ EVT_KEY_DOWN( wxGridWindow::OnKeyDown )
+ EVT_KEY_UP( wxGridWindow::OnKeyUp )
+ EVT_CHAR ( wxGridWindow::OnChar )
+ EVT_SET_FOCUS( wxGridWindow::OnFocus )
+ EVT_KILL_FOCUS( wxGridWindow::OnFocus )
+ EVT_ERASE_BACKGROUND( wxGridWindow::OnEraseBackground )
+END_EVENT_TABLE()
+
+wxGridWindow::wxGridWindow( wxGrid *parent,
+ wxGridRowLabelWindow *rowLblWin,
+ wxGridColLabelWindow *colLblWin,
+ wxWindowID id,
+ const wxPoint &pos,
+ const wxSize &size )
+ : wxWindow( parent, id, pos, size, wxWANTS_CHARS | wxBORDER_NONE | wxCLIP_CHILDREN|wxFULL_REPAINT_ON_RESIZE,
+ wxT("grid window") )
+
+{
+ m_owner = parent;
+ m_rowLabelWin = rowLblWin;
+ m_colLabelWin = colLblWin;
+}
+
+
+void wxGridWindow::OnPaint( wxPaintEvent &WXUNUSED(event) )
+{
+ wxPaintDC dc( this );
+ m_owner->PrepareDC( dc );
+ wxRegion reg = GetUpdateRegion();
+ wxGridCellCoordsArray DirtyCells = m_owner->CalcCellsExposed( reg );
+ m_owner->DrawGridCellArea( dc , DirtyCells);
+#if WXGRID_DRAW_LINES
+ m_owner->DrawAllGridLines( dc, reg );
+#endif
+ m_owner->DrawGridSpace( dc );
+ m_owner->DrawHighlight( dc , DirtyCells );
+}
+
+
+void wxGridWindow::ScrollWindow( int dx, int dy, const wxRect *rect )
+{
+ wxWindow::ScrollWindow( dx, dy, rect );
+ m_rowLabelWin->ScrollWindow( 0, dy, rect );
+ m_colLabelWin->ScrollWindow( dx, 0, rect );
+}
+
+
+void wxGridWindow::OnMouseEvent( wxMouseEvent& event )
+{
+ if (event.ButtonDown(wxMOUSE_BTN_LEFT) && FindFocus() != this)
+ SetFocus();
+
+ m_owner->ProcessGridCellMouseEvent( event );
+}
+
+void wxGridWindow::OnMouseWheel( wxMouseEvent& event )
+{
+ m_owner->GetEventHandler()->ProcessEvent(event);
+}
+
+// This seems to be required for wxMotif/wxGTK otherwise the mouse
+// cursor must be in the cell edit control to get key events
+//
+void wxGridWindow::OnKeyDown( wxKeyEvent& event )
+{
+ if ( !m_owner->GetEventHandler()->ProcessEvent( event ) ) event.Skip();
+}
+
+void wxGridWindow::OnKeyUp( wxKeyEvent& event )
+{
+ if ( !m_owner->GetEventHandler()->ProcessEvent( event ) ) event.Skip();
+}
+
+void wxGridWindow::OnChar( wxKeyEvent& event )
+{
+ if ( !m_owner->GetEventHandler()->ProcessEvent( event ) ) event.Skip();
+}
+
+void wxGridWindow::OnEraseBackground( wxEraseEvent& WXUNUSED(event) )
+{
+}
+
+void wxGridWindow::OnFocus(wxFocusEvent& event)
+{
+ if ( !m_owner->GetEventHandler()->ProcessEvent( event ) )
+ event.Skip();
+}
+
+//////////////////////////////////////////////////////////////////////
+
+// Internal Helper function for computing row or column from some
+// (unscrolled) coordinate value, using either
+// m_defaultRowHeight/m_defaultColWidth or binary search on array
+// of m_rowBottoms/m_ColRights to speed up the search!
+
+// Internal helper macros for simpler use of that function
+
+static int CoordToRowOrCol(int coord, int defaultDist, int minDist,
+ const wxArrayInt& BorderArray, int nMax,
+ bool clipToMinMax);
+
+#define internalXToCol(x) CoordToRowOrCol(x, m_defaultColWidth, \
+ m_minAcceptableColWidth, \
+ m_colRights, m_numCols, true)
+#define internalYToRow(y) CoordToRowOrCol(y, m_defaultRowHeight, \
+ m_minAcceptableRowHeight, \
+ m_rowBottoms, m_numRows, true)
+/////////////////////////////////////////////////////////////////////
+
+#if wxUSE_EXTENDED_RTTI
+WX_DEFINE_FLAGS( wxGridStyle )
+
+wxBEGIN_FLAGS( wxGridStyle )
+ // new style border flags, we put them first to
+ // use them for streaming out
+ wxFLAGS_MEMBER(wxBORDER_SIMPLE)
+ wxFLAGS_MEMBER(wxBORDER_SUNKEN)
+ wxFLAGS_MEMBER(wxBORDER_DOUBLE)
+ wxFLAGS_MEMBER(wxBORDER_RAISED)
+ wxFLAGS_MEMBER(wxBORDER_STATIC)
+ wxFLAGS_MEMBER(wxBORDER_NONE)
+
+ // old style border flags
+ wxFLAGS_MEMBER(wxSIMPLE_BORDER)
+ wxFLAGS_MEMBER(wxSUNKEN_BORDER)
+ wxFLAGS_MEMBER(wxDOUBLE_BORDER)
+ wxFLAGS_MEMBER(wxRAISED_BORDER)
+ wxFLAGS_MEMBER(wxSTATIC_BORDER)
+ wxFLAGS_MEMBER(wxBORDER)
+
+ // standard window styles
+ wxFLAGS_MEMBER(wxTAB_TRAVERSAL)
+ wxFLAGS_MEMBER(wxCLIP_CHILDREN)
+ wxFLAGS_MEMBER(wxTRANSPARENT_WINDOW)
+ wxFLAGS_MEMBER(wxWANTS_CHARS)
+ wxFLAGS_MEMBER(wxFULL_REPAINT_ON_RESIZE)
+ wxFLAGS_MEMBER(wxALWAYS_SHOW_SB )
+ wxFLAGS_MEMBER(wxVSCROLL)
+ wxFLAGS_MEMBER(wxHSCROLL)
+
+wxEND_FLAGS( wxGridStyle )
+
+IMPLEMENT_DYNAMIC_CLASS_XTI(wxGrid, wxScrolledWindow,"wx/grid.h")
+
+wxBEGIN_PROPERTIES_TABLE(wxGrid)
+ wxHIDE_PROPERTY( Children )
+ wxPROPERTY_FLAGS( WindowStyle , wxGridStyle , long , SetWindowStyleFlag , GetWindowStyleFlag , EMPTY_MACROVALUE, 0 /*flags*/ , wxT("Helpstring") , wxT("group")) // style
+wxEND_PROPERTIES_TABLE()
+
+wxBEGIN_HANDLERS_TABLE(wxGrid)
+wxEND_HANDLERS_TABLE()
+
+wxCONSTRUCTOR_5( wxGrid , wxWindow* , Parent , wxWindowID , Id , wxPoint , Position , wxSize , Size , long , WindowStyle )
+
+/*
+ TODO : Expose more information of a list's layout etc. via appropriate objects (\81Ã la NotebookPageInfo)
+*/
+#else
+IMPLEMENT_DYNAMIC_CLASS( wxGrid, wxScrolledWindow )
+#endif
+
+BEGIN_EVENT_TABLE( wxGrid, wxScrolledWindow )
+ EVT_PAINT( wxGrid::OnPaint )
+ EVT_SIZE( wxGrid::OnSize )
+ EVT_KEY_DOWN( wxGrid::OnKeyDown )
+ EVT_KEY_UP( wxGrid::OnKeyUp )
+ EVT_CHAR ( wxGrid::OnChar )
+ EVT_ERASE_BACKGROUND( wxGrid::OnEraseBackground )
+END_EVENT_TABLE()
+
+wxGrid::wxGrid()
+{
+ // in order to make sure that a size event is not
+ // trigerred in a unfinished state
+ m_cornerLabelWin = NULL ;
+ m_rowLabelWin = NULL ;
+ m_colLabelWin = NULL ;
+ m_gridWin = NULL ;
+}
+
+wxGrid::wxGrid( wxWindow *parent,
+ wxWindowID id,
+ const wxPoint& pos,
+ const wxSize& size,
+ long style,
+ const wxString& name )
+ : wxScrolledWindow( parent, id, pos, size, (style | wxWANTS_CHARS), name ),
+ m_colMinWidths(GRID_HASH_SIZE),
+ m_rowMinHeights(GRID_HASH_SIZE)
+{
+ Create();
+ SetBestFittingSize(size);
+}
+
+bool wxGrid::Create(wxWindow *parent, wxWindowID id,
+ const wxPoint& pos, const wxSize& size,
+ long style, const wxString& name)
+{
+ if (!wxScrolledWindow::Create(parent, id, pos, size,
+ style | wxWANTS_CHARS , name))
+ return false;
+
+ m_colMinWidths = wxLongToLongHashMap(GRID_HASH_SIZE) ;
+ m_rowMinHeights = wxLongToLongHashMap(GRID_HASH_SIZE) ;
+
+ Create() ;
+ SetBestFittingSize(size);
+
+ return true;
+}
+
+
+wxGrid::~wxGrid()
+{
+ // Must do this or ~wxScrollHelper will pop the wrong event handler
+ SetTargetWindow(this);
+ ClearAttrCache();
+ wxSafeDecRef(m_defaultCellAttr);
+
+#ifdef DEBUG_ATTR_CACHE
+ size_t total = gs_nAttrCacheHits + gs_nAttrCacheMisses;
+ wxPrintf(_T("wxGrid attribute cache statistics: "
+ "total: %u, hits: %u (%u%%)\n"),
+ total, gs_nAttrCacheHits,
+ total ? (gs_nAttrCacheHits*100) / total : 0);
+#endif
+
+ if (m_ownTable)
+ delete m_table;
+
+ delete m_typeRegistry;
+ delete m_selection;
+}
+
+
+//
+// ----- internal init and update functions
+//
+
+// NOTE: If using the default visual attributes works everywhere then this can
+// be removed as well as the #else cases below.
+#define _USE_VISATTR 0
+
+#if _USE_VISATTR
+#include "wx/listbox.h"
+#endif
+
+void wxGrid::Create()
+{
+ m_created = false; // set to true by CreateGrid
+
+ m_table = (wxGridTableBase *) NULL;
+ m_ownTable = false;
+
+ m_cellEditCtrlEnabled = false;
+
+ m_defaultCellAttr = new wxGridCellAttr();
+
+ // Set default cell attributes
+ m_defaultCellAttr->SetDefAttr(m_defaultCellAttr);
+ m_defaultCellAttr->SetKind(wxGridCellAttr::Default);
+ m_defaultCellAttr->SetFont(GetFont());
+ m_defaultCellAttr->SetAlignment(wxALIGN_LEFT, wxALIGN_TOP);
+ m_defaultCellAttr->SetRenderer(new wxGridCellStringRenderer);
+ m_defaultCellAttr->SetEditor(new wxGridCellTextEditor);
+
+#if _USE_VISATTR
+ wxVisualAttributes gva = wxListBox::GetClassDefaultAttributes();
+ wxVisualAttributes lva = wxPanel::GetClassDefaultAttributes();
+
+ m_defaultCellAttr->SetTextColour(gva.colFg);
+ m_defaultCellAttr->SetBackgroundColour(gva.colBg);
+
+#else
+ m_defaultCellAttr->SetTextColour(
+ wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT));
+ m_defaultCellAttr->SetBackgroundColour(
+ wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW));
+#endif
+
+ m_numRows = 0;
+ m_numCols = 0;
+ m_currentCellCoords = wxGridNoCellCoords;
+
+ m_rowLabelWidth = WXGRID_DEFAULT_ROW_LABEL_WIDTH;
+ m_colLabelHeight = WXGRID_DEFAULT_COL_LABEL_HEIGHT;
+
+ // create the type registry
+ m_typeRegistry = new wxGridTypeRegistry;
+ m_selection = NULL;
+
+ // subwindow components that make up the wxGrid
+ m_cornerLabelWin = new wxGridCornerLabelWindow( this,
+ wxID_ANY,
+ wxDefaultPosition,
+ wxDefaultSize );
+
+ m_rowLabelWin = new wxGridRowLabelWindow( this,
+ wxID_ANY,
+ wxDefaultPosition,
+ wxDefaultSize );
+
+ m_colLabelWin = new wxGridColLabelWindow( this,
+ wxID_ANY,
+ wxDefaultPosition,
+ wxDefaultSize );
+
+ m_gridWin = new wxGridWindow( this,
+ m_rowLabelWin,
+ m_colLabelWin,
+ wxID_ANY,
+ wxDefaultPosition,
+ wxDefaultSize );
+
+ SetTargetWindow( m_gridWin );
+
+#if _USE_VISATTR
+ wxColour gfg = gva.colFg;
+ wxColour gbg = gva.colBg;
+ wxColour lfg = lva.colFg;
+ wxColour lbg = lva.colBg;
+#else
+ wxColour gfg = wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOWTEXT );
+ wxColour gbg = wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW );
+ wxColour lfg = wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOWTEXT );
+ wxColour lbg = wxSystemSettings::GetColour( wxSYS_COLOUR_BTNFACE );
+#endif
+ m_cornerLabelWin->SetOwnForegroundColour(lfg);
+ m_cornerLabelWin->SetOwnBackgroundColour(lbg);
+ m_rowLabelWin->SetOwnForegroundColour(lfg);
+ m_rowLabelWin->SetOwnBackgroundColour(lbg);
+ m_colLabelWin->SetOwnForegroundColour(lfg);
+ m_colLabelWin->SetOwnBackgroundColour(lbg);
+
+ m_gridWin->SetOwnForegroundColour(gfg);
+ m_gridWin->SetOwnBackgroundColour(gbg);
+
+ Init();
+}
+
+
+bool wxGrid::CreateGrid( int numRows, int numCols,
+ wxGrid::wxGridSelectionModes selmode )
+{
+ wxCHECK_MSG( !m_created,
+ false,
+ wxT("wxGrid::CreateGrid or wxGrid::SetTable called more than once") );
+
+ m_numRows = numRows;
+ m_numCols = numCols;
+
+ m_table = new wxGridStringTable( m_numRows, m_numCols );
+ m_table->SetView( this );
+ m_ownTable = true;
+ m_selection = new wxGridSelection( this, selmode );
+
+ CalcDimensions();
+
+ m_created = true;
+
+ return m_created;
+}
+
+void wxGrid::SetSelectionMode(wxGrid::wxGridSelectionModes selmode)
+{
+ wxCHECK_RET( m_created,
+ wxT("Called wxGrid::SetSelectionMode() before calling CreateGrid()") );
+
+ m_selection->SetSelectionMode( selmode );
+}
+
+wxGrid::wxGridSelectionModes wxGrid::GetSelectionMode() const
+{
+ wxCHECK_MSG( m_created, wxGrid::wxGridSelectCells,
+ wxT("Called wxGrid::GetSelectionMode() before calling CreateGrid()") );
+
+ return m_selection->GetSelectionMode();
+}
+
+bool wxGrid::SetTable( wxGridTableBase *table, bool takeOwnership,
+ wxGrid::wxGridSelectionModes selmode )
+{
+ if ( m_created )
+ {
+ // stop all processing
+ m_created = false;
+
+ if (m_ownTable)
+ {
+ wxGridTableBase *t=m_table;
+ m_table=0;
+ delete t;
+ }
+ delete m_selection;
+
+ m_table=0;
+ m_selection=0;
+ m_numRows=0;
+ m_numCols=0;
+ }
+ if (table)
+ {
+ m_numRows = table->GetNumberRows();
+ m_numCols = table->GetNumberCols();
+
+ m_table = table;
+ m_table->SetView( this );
+ if (takeOwnership)
+ m_ownTable = true;
+ m_selection = new wxGridSelection( this, selmode );
+
+ CalcDimensions();
+
+ m_created = true;
+ }
+
+ return m_created;
+}
+
+
+void wxGrid::Init()
+{
+ m_rowLabelWidth = WXGRID_DEFAULT_ROW_LABEL_WIDTH;
+ m_colLabelHeight = WXGRID_DEFAULT_COL_LABEL_HEIGHT;
+
+ if ( m_rowLabelWin )
+ {
+ m_labelBackgroundColour = m_rowLabelWin->GetBackgroundColour();
+ }
+ else
+ {
+ m_labelBackgroundColour = wxColour( _T("WHITE") );
+ }
+
+ m_labelTextColour = wxColour( _T("BLACK") );
+
+ // init attr cache
+ m_attrCache.row = -1;
+ m_attrCache.col = -1;
+ m_attrCache.attr = NULL;
+
+ // TODO: something better than this ?
+ //
+ m_labelFont = this->GetFont();
+ m_labelFont.SetWeight( wxBOLD );
+
+ m_rowLabelHorizAlign = wxALIGN_CENTRE;
+ m_rowLabelVertAlign = wxALIGN_CENTRE;
+
+ m_colLabelHorizAlign = wxALIGN_CENTRE;
+ m_colLabelVertAlign = wxALIGN_CENTRE;
+ m_colLabelTextOrientation = wxHORIZONTAL;
+
+ m_defaultColWidth = WXGRID_DEFAULT_COL_WIDTH;
+ m_defaultRowHeight = m_gridWin->GetCharHeight();
+
+ m_minAcceptableColWidth = WXGRID_MIN_COL_WIDTH;
+ m_minAcceptableRowHeight = WXGRID_MIN_ROW_HEIGHT;
+
+#if defined(__WXMOTIF__) || defined(__WXGTK__) // see also text ctrl sizing in ShowCellEditControl()
+ m_defaultRowHeight += 8;
+#else
+ m_defaultRowHeight += 4;
+#endif
+
+ m_gridLineColour = wxColour( 192,192,192 );
+ m_gridLinesEnabled = true;
+ m_cellHighlightColour = *wxBLACK;
+ m_cellHighlightPenWidth = 2;
+ m_cellHighlightROPenWidth = 1;
+
+ m_cursorMode = WXGRID_CURSOR_SELECT_CELL;
+ m_winCapture = (wxWindow *)NULL;
+ m_canDragRowSize = true;
+ m_canDragColSize = true;
+ m_canDragGridSize = true;
+ m_canDragCell = false;
+ m_dragLastPos = -1;
+ m_dragRowOrCol = -1;
+ m_isDragging = false;
+ m_startDragPos = wxDefaultPosition;
+
+ m_waitForSlowClick = false;
+
+ m_rowResizeCursor = wxCursor( wxCURSOR_SIZENS );
+ m_colResizeCursor = wxCursor( wxCURSOR_SIZEWE );
+
+ m_currentCellCoords = wxGridNoCellCoords;
+
+ m_selectingTopLeft = wxGridNoCellCoords;
+ m_selectingBottomRight = wxGridNoCellCoords;
+ m_selectionBackground = wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHT);
+ m_selectionForeground = wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHTTEXT);
+
+ m_editable = true; // default for whole grid
+
+ m_inOnKeyDown = false;
+ m_batchCount = 0;
+
+ m_extraWidth =
+ m_extraHeight = 0;
+
+ m_scrollLineX = GRID_SCROLL_LINE_X;
+ m_scrollLineY = GRID_SCROLL_LINE_Y;
+}
+
+// ----------------------------------------------------------------------------
+// the idea is to call these functions only when necessary because they create
+// quite big arrays which eat memory mostly unnecessary - in particular, if
+// default widths/heights are used for all rows/columns, we may not use these
+// arrays at all
+//
+// with some extra code, it should be possible to only store the
+// widths/heights different from default ones but this will be done later...
+// ----------------------------------------------------------------------------
+
+void wxGrid::InitRowHeights()
+{
+ m_rowHeights.Empty();
+ m_rowBottoms.Empty();
+
+ m_rowHeights.Alloc( m_numRows );
+ m_rowBottoms.Alloc( m_numRows );
+
+ int rowBottom = 0;
+
+ m_rowHeights.Add( m_defaultRowHeight, m_numRows );
+
+ for ( int i = 0; i < m_numRows; i++ )
+ {
+ rowBottom += m_defaultRowHeight;
+ m_rowBottoms.Add( rowBottom );
+ }
+}
+
+void wxGrid::InitColWidths()
+{
+ m_colWidths.Empty();
+ m_colRights.Empty();
+
+ m_colWidths.Alloc( m_numCols );
+ m_colRights.Alloc( m_numCols );
+ int colRight = 0;
+
+ m_colWidths.Add( m_defaultColWidth, m_numCols );
+
+ for ( int i = 0; i < m_numCols; i++ )
+ {
+ colRight += m_defaultColWidth;
+ m_colRights.Add( colRight );
+ }
+}
+
+int wxGrid::GetColWidth(int col) const
+{
+ return m_colWidths.IsEmpty() ? m_defaultColWidth : m_colWidths[col];
+}
+
+int wxGrid::GetColLeft(int col) const
+{
+ return m_colRights.IsEmpty() ? col * m_defaultColWidth
+ : m_colRights[col] - m_colWidths[col];
+}
+
+int wxGrid::GetColRight(int col) const
+{
+ return m_colRights.IsEmpty() ? (col + 1) * m_defaultColWidth
+ : m_colRights[col];
+}
+
+int wxGrid::GetRowHeight(int row) const
+{
+ return m_rowHeights.IsEmpty() ? m_defaultRowHeight : m_rowHeights[row];
+}
+
+int wxGrid::GetRowTop(int row) const
+{
+ return m_rowBottoms.IsEmpty() ? row * m_defaultRowHeight
+ : m_rowBottoms[row] - m_rowHeights[row];
+}
+
+int wxGrid::GetRowBottom(int row) const
+{
+ return m_rowBottoms.IsEmpty() ? (row + 1) * m_defaultRowHeight
+ : m_rowBottoms[row];
+}
+
+void wxGrid::CalcDimensions()
+{
+ int cw, ch;
+ GetClientSize( &cw, &ch );
+
+ if ( m_rowLabelWin->IsShown() )
+ cw -= m_rowLabelWidth;
+ if ( m_colLabelWin->IsShown() )
+ ch -= m_colLabelHeight;
+
+ // grid total size
+ int w = m_numCols > 0 ? GetColRight(m_numCols - 1) + m_extraWidth + 1 : 0;
+ int h = m_numRows > 0 ? GetRowBottom(m_numRows - 1) + m_extraHeight + 1 : 0;
+
+ // take into account editor if shown
+ if( IsCellEditControlShown() )
+ {
+ int w2, h2;
+ int r = m_currentCellCoords.GetRow();
+ int c = m_currentCellCoords.GetCol();
+ int x = GetColLeft(c);
+ int y = GetRowTop(r);
+
+ // how big is the editor
+ wxGridCellAttr* attr = GetCellAttr(r, c);
+ wxGridCellEditor* editor = attr->GetEditor(this, r, c);
+ editor->GetControl()->GetSize(&w2, &h2);
+ w2 += x;
+ h2 += y;
+ if( w2 > w ) w = w2;
+ if( h2 > h ) h = h2;
+ editor->DecRef();
+ attr->DecRef();
+ }
+
+ // preserve (more or less) the previous position
+ int x, y;
+ GetViewStart( &x, &y );
+
+ // ensure the position is valid for the new scroll ranges
+ if ( x >= w )
+ x = wxMax( w - 1, 0 );
+ if ( y >= h )
+ y = wxMax( h - 1, 0 );
+
+ // do set scrollbar parameters
+ SetScrollbars( m_scrollLineX, m_scrollLineY,
+ GetScrollX(w), GetScrollY(h), x, y,
+ GetBatchCount() != 0);
+
+ // if our OnSize() hadn't been called (it would if we have scrollbars), we
+ // still must reposition the children
+ CalcWindowSizes();
+}
+
+
+void wxGrid::CalcWindowSizes()
+{
+ // escape if the window is has not been fully created yet
+
+ if ( m_cornerLabelWin == NULL )
+ return ;
+
+ int cw, ch;
+ GetClientSize( &cw, &ch );
+
+ if ( m_cornerLabelWin && m_cornerLabelWin->IsShown() )
+ m_cornerLabelWin->SetSize( 0, 0, m_rowLabelWidth, m_colLabelHeight );
+
+ if ( m_colLabelWin && m_colLabelWin->IsShown() )
+ m_colLabelWin->SetSize( m_rowLabelWidth, 0, cw-m_rowLabelWidth, m_colLabelHeight);
+
+ if ( m_rowLabelWin && m_rowLabelWin->IsShown() )
+ m_rowLabelWin->SetSize( 0, m_colLabelHeight, m_rowLabelWidth, ch-m_colLabelHeight);
+
+ if ( m_gridWin && m_gridWin->IsShown() )
+ m_gridWin->SetSize( m_rowLabelWidth, m_colLabelHeight, cw-m_rowLabelWidth, ch-m_colLabelHeight);
+}
+
+
+// this is called when the grid table sends a message to say that it
+// has been redimensioned
+//
+bool wxGrid::Redimension( wxGridTableMessage& msg )
+{
+ int i;
+ bool result = false;
+
+ // Clear the attribute cache as the attribute might refer to a different
+ // cell than stored in the cache after adding/removing rows/columns.
+ ClearAttrCache();
+ // By the same reasoning, the editor should be dismissed if columns are
+ // added or removed. And for consistency, it should IMHO always be
+ // removed, not only if the cell "underneath" it actually changes.
+ // For now, I intentionally do not save the editor's content as the
+ // 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:
+ {
+ size_t pos = msg.GetCommandInt();
+ int numRows = msg.GetCommandInt2();
+
+ m_numRows += numRows;
+
+ if ( !m_rowHeights.IsEmpty() )
+ {
+ m_rowHeights.Insert( m_defaultRowHeight, pos, numRows );
+ m_rowBottoms.Insert( 0, pos, numRows );
+
+ int bottom = 0;
+ if ( pos > 0 ) bottom = m_rowBottoms[pos-1];
+
+ for ( i = pos; i < m_numRows; i++ )
+ {
+ bottom += m_rowHeights[i];
+ m_rowBottoms[i] = bottom;
+ }
+ }
+ if ( m_currentCellCoords == wxGridNoCellCoords )
+ {
+ // if we have just inserted cols into an empty grid the current
+ // cell will be undefined...
+ //
+ SetCurrentCell( 0, 0 );
+ }
+
+ if ( m_selection )
+ m_selection->UpdateRows( pos, numRows );
+ wxGridCellAttrProvider * attrProvider = m_table->GetAttrProvider();
+ if (attrProvider)
+ attrProvider->UpdateAttrRows( pos, numRows );
+
+ if ( !GetBatchCount() )
+ {
+ CalcDimensions();
+ m_rowLabelWin->Refresh();
+ }
+ }
+ result = true;
+ break;
+
+ case wxGRIDTABLE_NOTIFY_ROWS_APPENDED:
+ {
+ int numRows = msg.GetCommandInt();
+ int oldNumRows = m_numRows;
+ m_numRows += numRows;
+
+ if ( !m_rowHeights.IsEmpty() )
+ {
+ m_rowHeights.Add( m_defaultRowHeight, numRows );
+ m_rowBottoms.Add( 0, numRows );
+
+ int bottom = 0;
+ if ( oldNumRows > 0 ) bottom = m_rowBottoms[oldNumRows-1];
+
+ for ( i = oldNumRows; i < m_numRows; i++ )
+ {
+ bottom += m_rowHeights[i];
+ m_rowBottoms[i] = bottom;
+ }
+ }
+ if ( m_currentCellCoords == wxGridNoCellCoords )
+ {
+ // if we have just inserted cols into an empty grid the current
+ // cell will be undefined...
+ //
+ SetCurrentCell( 0, 0 );
+ }
+ if ( !GetBatchCount() )
+ {
+ CalcDimensions();
+ m_rowLabelWin->Refresh();
+ }
+ }
+ result = true;
+ break;
+
+ case wxGRIDTABLE_NOTIFY_ROWS_DELETED:
+ {
+ size_t pos = msg.GetCommandInt();
+ int numRows = msg.GetCommandInt2();
+ m_numRows -= numRows;
+
+ if ( !m_rowHeights.IsEmpty() )
+ {
+ m_rowHeights.RemoveAt( pos, numRows );
+ m_rowBottoms.RemoveAt( pos, numRows );
+
+ int h = 0;
+ for ( i = 0; i < m_numRows; i++ )
+ {
+ h += m_rowHeights[i];
+ m_rowBottoms[i] = h;
+ }
+ }
+ if ( !m_numRows )
+ {
+ m_currentCellCoords = wxGridNoCellCoords;
+ }
+ else
+ {
+ if ( m_currentCellCoords.GetRow() >= m_numRows )
+ m_currentCellCoords.Set( 0, 0 );
+ }
+
+ if ( m_selection )
+ m_selection->UpdateRows( pos, -((int)numRows) );
+ wxGridCellAttrProvider * attrProvider = m_table->GetAttrProvider();
+ if (attrProvider) {
+ attrProvider->UpdateAttrRows( pos, -((int)numRows) );
+// ifdef'd out following patch from Paul Gammans
+#if 0
+ // No need to touch column attributes, unless we
+ // removed _all_ rows, in this case, we remove
+ // all column attributes.
+ // I hate to do this here, but the
+ // needed data is not available inside UpdateAttrRows.
+ if ( !GetNumberRows() )
+ attrProvider->UpdateAttrCols( 0, -GetNumberCols() );
+#endif
+ }
+ if ( !GetBatchCount() )
+ {
+ CalcDimensions();
+ m_rowLabelWin->Refresh();
+ }
+ }
+ result = true;
+ break;
+
+ case wxGRIDTABLE_NOTIFY_COLS_INSERTED:
+ {
+ size_t pos = msg.GetCommandInt();
+ int numCols = msg.GetCommandInt2();
+ m_numCols += numCols;
+
+ if ( !m_colWidths.IsEmpty() )
+ {
+ m_colWidths.Insert( m_defaultColWidth, pos, numCols );
+ m_colRights.Insert( 0, pos, numCols );
+
+ int right = 0;
+ if ( pos > 0 ) right = m_colRights[pos-1];
+
+ for ( i = pos; i < m_numCols; i++ )
+ {
+ right += m_colWidths[i];
+ m_colRights[i] = right;
+ }
+ }
+ if ( m_currentCellCoords == wxGridNoCellCoords )
+ {
+ // if we have just inserted cols into an empty grid the current
+ // cell will be undefined...
+ //
+ SetCurrentCell( 0, 0 );
+ }
+
+ if ( m_selection )
+ m_selection->UpdateCols( pos, numCols );
+ wxGridCellAttrProvider * attrProvider = m_table->GetAttrProvider();
+ if (attrProvider)
+ attrProvider->UpdateAttrCols( pos, numCols );
+ if ( !GetBatchCount() )
+ {
+ CalcDimensions();
+ m_colLabelWin->Refresh();
+ }
+
+ }
+ result = true;
+ break;
+
+ case wxGRIDTABLE_NOTIFY_COLS_APPENDED:
+ {
+ int numCols = msg.GetCommandInt();
+ int oldNumCols = m_numCols;
+ m_numCols += numCols;
+ if ( !m_colWidths.IsEmpty() )
+ {
+ m_colWidths.Add( m_defaultColWidth, numCols );
+ m_colRights.Add( 0, numCols );
+
+ int right = 0;
+ if ( oldNumCols > 0 ) right = m_colRights[oldNumCols-1];
+
+ for ( i = oldNumCols; i < m_numCols; i++ )
+ {
+ right += m_colWidths[i];
+ m_colRights[i] = right;
+ }
+ }
+ if ( m_currentCellCoords == wxGridNoCellCoords )
+ {
+ // if we have just inserted cols into an empty grid the current
+ // cell will be undefined...
+ //
+ SetCurrentCell( 0, 0 );
+ }
+ if ( !GetBatchCount() )
+ {
+ CalcDimensions();
+ m_colLabelWin->Refresh();
+ }
+ }
+ result = true;
+ break;
+
+ case wxGRIDTABLE_NOTIFY_COLS_DELETED:
+ {
+ size_t pos = msg.GetCommandInt();
+ int numCols = msg.GetCommandInt2();
+ m_numCols -= numCols;
+
+ if ( !m_colWidths.IsEmpty() )
+ {
+ m_colWidths.RemoveAt( pos, numCols );
+ m_colRights.RemoveAt( pos, numCols );
+
+ int w = 0;
+ for ( i = 0; i < m_numCols; i++ )
+ {
+ w += m_colWidths[i];
+ m_colRights[i] = w;
+ }
+ }
+ if ( !m_numCols )
+ {
+ m_currentCellCoords = wxGridNoCellCoords;
+ }
+ else
+ {
+ if ( m_currentCellCoords.GetCol() >= m_numCols )
+ m_currentCellCoords.Set( 0, 0 );
+ }
+
+ if ( m_selection )
+ m_selection->UpdateCols( pos, -((int)numCols) );
+ wxGridCellAttrProvider * attrProvider = m_table->GetAttrProvider();
+ if (attrProvider) {
+ attrProvider->UpdateAttrCols( pos, -((int)numCols) );
+// ifdef'd out following patch from Paul Gammans
+#if 0
+ // No need to touch row attributes, unless we
+ // removed _all_ columns, in this case, we remove
+ // all row attributes.
+ // I hate to do this here, but the
+ // needed data is not available inside UpdateAttrCols.
+ if ( !GetNumberCols() )
+ attrProvider->UpdateAttrRows( 0, -GetNumberRows() );
+#endif
+ }
+ if ( !GetBatchCount() )
+ {
+ CalcDimensions();
+ m_colLabelWin->Refresh();
+ }
+ }
+ result = true;
+ break;
+ }
+
+ if (result && !GetBatchCount() )
+ m_gridWin->Refresh();
+ return result;
+}
+
+
+wxArrayInt wxGrid::CalcRowLabelsExposed( const wxRegion& reg )
+{
+ wxRegionIterator iter( reg );
+ wxRect r;
+
+ wxArrayInt rowlabels;
+
+ int top, bottom;
+ while ( iter )
+ {
+ r = iter.GetRect();
+
+ // TODO: remove this when we can...
+ // There is a bug in wxMotif that gives garbage update
+ // rectangles if you jump-scroll a long way by clicking the
+ // scrollbar with middle button. This is a work-around
+ //
+#if defined(__WXMOTIF__)
+ int cw, ch;
+ m_gridWin->GetClientSize( &cw, &ch );
+ if ( r.GetTop() > ch ) r.SetTop( 0 );
+ r.SetBottom( wxMin( r.GetBottom(), ch ) );
+#endif
+
+ // logical bounds of update region
+ //
+ int dummy;
+ CalcUnscrolledPosition( 0, r.GetTop(), &dummy, &top );
+ CalcUnscrolledPosition( 0, r.GetBottom(), &dummy, &bottom );
+
+ // find the row labels within these bounds
+ //
+ int row;
+ for ( row = internalYToRow(top); row < m_numRows; row++ )
+ {
+ if ( GetRowBottom(row) < top )
+ continue;
+
+ if ( GetRowTop(row) > bottom )
+ break;
+
+ rowlabels.Add( row );
+ }
+
+ ++iter;
+ }
+
+ return rowlabels;
+}
+
+
+wxArrayInt wxGrid::CalcColLabelsExposed( const wxRegion& reg )
+{
+ wxRegionIterator iter( reg );
+ wxRect r;
+
+ wxArrayInt colLabels;
+
+ int left, right;
+ while ( iter )
+ {
+ r = iter.GetRect();
+
+ // TODO: remove this when we can...
+ // There is a bug in wxMotif that gives garbage update
+ // rectangles if you jump-scroll a long way by clicking the
+ // scrollbar with middle button. This is a work-around
+ //
+#if defined(__WXMOTIF__)
+ int cw, ch;
+ m_gridWin->GetClientSize( &cw, &ch );
+ if ( r.GetLeft() > cw ) r.SetLeft( 0 );
+ r.SetRight( wxMin( r.GetRight(), cw ) );
+#endif
+
+ // logical bounds of update region
+ //
+ int dummy;
+ CalcUnscrolledPosition( r.GetLeft(), 0, &left, &dummy );
+ CalcUnscrolledPosition( r.GetRight(), 0, &right, &dummy );
+
+ // find the cells within these bounds
+ //
+ int col;
+ for ( col = internalXToCol(left); col < m_numCols; col++ )
+ {
+ if ( GetColRight(col) < left )
+ continue;
+
+ if ( GetColLeft(col) > right )
+ break;
+
+ colLabels.Add( col );
+ }
+
+ ++iter;
+ }
+ return colLabels;
+}
+
+
+wxGridCellCoordsArray wxGrid::CalcCellsExposed( const wxRegion& reg )
+{
+ wxRegionIterator iter( reg );
+ wxRect r;
+
+ wxGridCellCoordsArray cellsExposed;
+
+ int left, top, right, bottom;
+ while ( iter )
+ {
+ r = iter.GetRect();
+
+ // TODO: remove this when we can...
+ // There is a bug in wxMotif that gives garbage update
+ // rectangles if you jump-scroll a long way by clicking the
+ // scrollbar with middle button. This is a work-around
+ //
+#if defined(__WXMOTIF__)
+ int cw, ch;
+ m_gridWin->GetClientSize( &cw, &ch );
+ if ( r.GetTop() > ch ) r.SetTop( 0 );
+ if ( r.GetLeft() > cw ) r.SetLeft( 0 );
+ r.SetRight( wxMin( r.GetRight(), cw ) );
+ r.SetBottom( wxMin( r.GetBottom(), ch ) );
+#endif
+
+ // logical bounds of update region
+ //
+ CalcUnscrolledPosition( r.GetLeft(), r.GetTop(), &left, &top );
+ CalcUnscrolledPosition( r.GetRight(), r.GetBottom(), &right, &bottom );
+
+ // find the cells within these bounds
+ //
+ int row, col;
+ for ( row = internalYToRow(top); row < m_numRows; row++ )
+ {
+ if ( GetRowBottom(row) <= top )
+ continue;
+
+ if ( GetRowTop(row) > bottom )
+ break;
+
+ for ( col = internalXToCol(left); col < m_numCols; col++ )
+ {
+ if ( GetColRight(col) <= left )
+ continue;
+
+ if ( GetColLeft(col) > right )
+ break;
+
+ cellsExposed.Add( wxGridCellCoords( row, col ) );
+ }
+ }
+
+ ++iter;
+ }
+
+ return cellsExposed;
+}
+
+
+void wxGrid::ProcessRowLabelMouseEvent( wxMouseEvent& event )
+{
+ int x, y, row;
+ wxPoint pos( event.GetPosition() );
+ CalcUnscrolledPosition( pos.x, pos.y, &x, &y );
+
+ if ( event.Dragging() )
+ {
+ if (!m_isDragging)
+ {
+ m_isDragging = true;
+ m_rowLabelWin->CaptureMouse();
+ }
+
+ if ( event.LeftIsDown() )
+ {
+ switch( m_cursorMode )
+ {
+ case WXGRID_CURSOR_RESIZE_ROW:
+ {
+ int cw, ch, left, dummy;
+ m_gridWin->GetClientSize( &cw, &ch );
+ CalcUnscrolledPosition( 0, 0, &left, &dummy );
+
+ wxClientDC dc( m_gridWin );
+ PrepareDC( dc );
+ y = wxMax( y,
+ GetRowTop(m_dragRowOrCol) +
+ GetRowMinimalHeight(m_dragRowOrCol) );
+ dc.SetLogicalFunction(wxINVERT);
+ if ( m_dragLastPos >= 0 )
+ {
+ dc.DrawLine( left, m_dragLastPos, left+cw, m_dragLastPos );
+ }
+ dc.DrawLine( left, y, left+cw, y );
+ m_dragLastPos = y;
+ }
+ break;
+
+ case WXGRID_CURSOR_SELECT_ROW:
+ {
+ if ( (row = YToRow( y )) >= 0 )
+ {
+ if ( m_selection )
+ {
+ m_selection->SelectRow( row,
+ event.ControlDown(),
+ event.ShiftDown(),
+ event.AltDown(),
+ event.MetaDown() );
+ }
+ }
+ }
+ break;
+
+ // default label to suppress warnings about "enumeration value
+ // 'xxx' not handled in switch
+ default:
+ break;
+ }
+ }
+ return;
+ }
+
+ if ( m_isDragging && (event.Entering() || event.Leaving()) )
+ return;
+
+ if (m_isDragging)
+ {
+ if (m_rowLabelWin->HasCapture()) m_rowLabelWin->ReleaseMouse();
+ m_isDragging = false;
+ }
+
+ // ------------ Entering or leaving the window
+ //
+ if ( event.Entering() || event.Leaving() )
+ {
+ ChangeCursorMode(WXGRID_CURSOR_SELECT_CELL, m_rowLabelWin);
+ }
+
+
+ // ------------ Left button pressed
+ //
+ else if ( event.LeftDown() )
+ {
+ // don't send a label click event for a hit on the
+ // edge of the row label - this is probably the user
+ // wanting to resize the row
+ //
+ if ( YToEdgeOfRow(y) < 0 )
+ {
+ row = YToRow(y);
+ if ( row >= 0 &&
+ !SendEvent( wxEVT_GRID_LABEL_LEFT_CLICK, row, -1, event ) )
+ {
+ if ( !event.ShiftDown() && !event.ControlDown() )
+ ClearSelection();
+ if ( m_selection )
+ {
+ if ( event.ShiftDown() )
+ {
+ m_selection->SelectBlock( m_currentCellCoords.GetRow(),
+ 0,
+ row,
+ GetNumberCols() - 1,
+ event.ControlDown(),
+ event.ShiftDown(),
+ event.AltDown(),
+ event.MetaDown() );
+ }
+ else
+ {
+ m_selection->SelectRow( row,
+ event.ControlDown(),
+ event.ShiftDown(),
+ event.AltDown(),
+ event.MetaDown() );
+ }
+ }
+
+ ChangeCursorMode(WXGRID_CURSOR_SELECT_ROW, m_rowLabelWin);
+ }
+ }
+ else
+ {
+ // starting to drag-resize a row
+ //
+ if ( CanDragRowSize() )
+ ChangeCursorMode(WXGRID_CURSOR_RESIZE_ROW, m_rowLabelWin);
+ }
+ }
+
+
+ // ------------ Left double click
+ //
+ else if (event.LeftDClick() )
+ {
+ int row = YToEdgeOfRow(y);
+ if ( row < 0 )
+ {
+ row = YToRow(y);
+ if ( row >=0 &&
+ !SendEvent( wxEVT_GRID_LABEL_LEFT_DCLICK, row, -1, event ) )
+ {
+ // no default action at the moment
+ }
+ }
+ else
+ {
+ // adjust row height depending on label text
+ AutoSizeRowLabelSize( row );
+
+ ChangeCursorMode(WXGRID_CURSOR_SELECT_CELL, m_colLabelWin);
+ m_dragLastPos = -1;
+ }
+ }
+
+
+ // ------------ Left button released
+ //
+ else if ( event.LeftUp() )
+ {
+ if ( m_cursorMode == WXGRID_CURSOR_RESIZE_ROW )
+ {
+ DoEndDragResizeRow();
+
+ // Note: we are ending the event *after* doing
+ // default processing in this case
+ //
+ SendEvent( wxEVT_GRID_ROW_SIZE, m_dragRowOrCol, -1, event );
+ }
+
+ ChangeCursorMode(WXGRID_CURSOR_SELECT_CELL, m_rowLabelWin);
+ m_dragLastPos = -1;
+ }
+
+
+ // ------------ Right button down
+ //
+ else if ( event.RightDown() )
+ {
+ row = YToRow(y);
+ if ( row >=0 &&
+ !SendEvent( wxEVT_GRID_LABEL_RIGHT_CLICK, row, -1, event ) )
+ {
+ // no default action at the moment
+ }
+ }
+
+
+ // ------------ Right double click
+ //
+ else if ( event.RightDClick() )
+ {
+ row = YToRow(y);
+ if ( row >= 0 &&
+ !SendEvent( wxEVT_GRID_LABEL_RIGHT_DCLICK, row, -1, event ) )
+ {
+ // no default action at the moment
+ }
+ }
+
+
+ // ------------ No buttons down and mouse moving
+ //
+ else if ( event.Moving() )
+ {
+ m_dragRowOrCol = YToEdgeOfRow( y );
+ if ( m_dragRowOrCol >= 0 )
+ {
+ if ( m_cursorMode == WXGRID_CURSOR_SELECT_CELL )
+ {
+ // don't capture the mouse yet
+ if ( CanDragRowSize() )
+ ChangeCursorMode(WXGRID_CURSOR_RESIZE_ROW, m_rowLabelWin, false);
+ }
+ }
+ else if ( m_cursorMode != WXGRID_CURSOR_SELECT_CELL )
+ {
+ ChangeCursorMode(WXGRID_CURSOR_SELECT_CELL, m_rowLabelWin, false);
+ }
+ }
+}
+
+
+void wxGrid::ProcessColLabelMouseEvent( wxMouseEvent& event )
+{
+ int x, y, col;
+ wxPoint pos( event.GetPosition() );
+ CalcUnscrolledPosition( pos.x, pos.y, &x, &y );
+
+ if ( event.Dragging() )
+ {
+ if (!m_isDragging)
+ {
+ m_isDragging = true;
+ m_colLabelWin->CaptureMouse();
+ }
+
+ if ( event.LeftIsDown() )
+ {
+ switch( m_cursorMode )
+ {
+ case WXGRID_CURSOR_RESIZE_COL:
+ {
+ int cw, ch, dummy, top;
+ m_gridWin->GetClientSize( &cw, &ch );
+ CalcUnscrolledPosition( 0, 0, &dummy, &top );
+
+ wxClientDC dc( m_gridWin );
+ PrepareDC( dc );
+
+ x = wxMax( x, GetColLeft(m_dragRowOrCol) +
+ GetColMinimalWidth(m_dragRowOrCol));
+ dc.SetLogicalFunction(wxINVERT);
+ if ( m_dragLastPos >= 0 )
+ {
+ dc.DrawLine( m_dragLastPos, top, m_dragLastPos, top+ch );
+ }
+ dc.DrawLine( x, top, x, top+ch );
+ m_dragLastPos = x;
+ }
+ break;
+
+ case WXGRID_CURSOR_SELECT_COL:
+ {
+ if ( (col = XToCol( x )) >= 0 )
+ {
+ if ( m_selection )
+ {
+ m_selection->SelectCol( col,
+ event.ControlDown(),
+ event.ShiftDown(),
+ event.AltDown(),
+ event.MetaDown() );
+ }
+ }
+ }
+ break;
+
+ // default label to suppress warnings about "enumeration value
+ // 'xxx' not handled in switch
+ default:
+ break;
+ }
+ }
+ return;
+ }
+
+ if ( m_isDragging && (event.Entering() || event.Leaving()) )
+ return;
+
+ if (m_isDragging)
+ {
+ if (m_colLabelWin->HasCapture()) m_colLabelWin->ReleaseMouse();
+ m_isDragging = false;
+ }
+
+ // ------------ Entering or leaving the window
+ //
+ if ( event.Entering() || event.Leaving() )
+ {
+ ChangeCursorMode(WXGRID_CURSOR_SELECT_CELL, m_colLabelWin);
+ }
+
+
+ // ------------ Left button pressed
+ //
+ else if ( event.LeftDown() )
+ {
+ // don't send a label click event for a hit on the
+ // edge of the col label - this is probably the user
+ // wanting to resize the col
+ //
+ if ( XToEdgeOfCol(x) < 0 )
+ {
+ col = XToCol(x);
+ if ( col >= 0 &&
+ !SendEvent( wxEVT_GRID_LABEL_LEFT_CLICK, -1, col, event ) )
+ {
+ if ( !event.ShiftDown() && !event.ControlDown() )
+ ClearSelection();
+ if ( m_selection )
+ {
+ if ( event.ShiftDown() )
+ {
+ m_selection->SelectBlock( 0,
+ m_currentCellCoords.GetCol(),
+ GetNumberRows() - 1, col,
+ event.ControlDown(),
+ event.ShiftDown(),
+ event.AltDown(),
+ event.MetaDown() );
+ }
+ else
+ {
+ m_selection->SelectCol( col,
+ event.ControlDown(),
+ event.ShiftDown(),
+ event.AltDown(),
+ event.MetaDown() );
+ }
+ }
+
+ ChangeCursorMode(WXGRID_CURSOR_SELECT_COL, m_colLabelWin);
+ }
+ }
+ else
+ {
+ // starting to drag-resize a col
+ //
+ if ( CanDragColSize() )
+ ChangeCursorMode(WXGRID_CURSOR_RESIZE_COL, m_colLabelWin);
+ }
+ }
+
+
+ // ------------ Left double click
+ //
+ if ( event.LeftDClick() )
+ {
+ int col = XToEdgeOfCol(x);
+ if ( col < 0 )
+ {
+ col = XToCol(x);
+ if ( col >= 0 &&
+ ! SendEvent( wxEVT_GRID_LABEL_LEFT_DCLICK, -1, col, event ) )
+ {
+ // no default action at the moment
+ }
+ }
+ else
+ {
+ // adjust column width depending on label text
+ AutoSizeColLabelSize( col );
+
+ ChangeCursorMode(WXGRID_CURSOR_SELECT_CELL, m_colLabelWin);
+ m_dragLastPos = -1;
+ }
+ }
+
+
+ // ------------ Left button released
+ //
+ else if ( event.LeftUp() )
+ {
+ if ( m_cursorMode == WXGRID_CURSOR_RESIZE_COL )
+ {
+ DoEndDragResizeCol();
+
+ // Note: we are ending the event *after* doing
+ // default processing in this case
+ //
+ SendEvent( wxEVT_GRID_COL_SIZE, -1, m_dragRowOrCol, event );
+ }
+
+ ChangeCursorMode(WXGRID_CURSOR_SELECT_CELL, m_colLabelWin);
+ m_dragLastPos = -1;
+ }
+
+
+ // ------------ Right button down
+ //
+ else if ( event.RightDown() )
+ {
+ col = XToCol(x);
+ if ( col >= 0 &&
+ !SendEvent( wxEVT_GRID_LABEL_RIGHT_CLICK, -1, col, event ) )
+ {
+ // no default action at the moment
+ }
+ }
+
+
+ // ------------ Right double click
+ //
+ else if ( event.RightDClick() )
+ {
+ col = XToCol(x);
+ if ( col >= 0 &&
+ !SendEvent( wxEVT_GRID_LABEL_RIGHT_DCLICK, -1, col, event ) )
+ {
+ // no default action at the moment
+ }
+ }
+
+
+ // ------------ No buttons down and mouse moving
+ //
+ else if ( event.Moving() )
+ {
+ m_dragRowOrCol = XToEdgeOfCol( x );
+ if ( m_dragRowOrCol >= 0 )
+ {
+ if ( m_cursorMode == WXGRID_CURSOR_SELECT_CELL )
+ {
+ // don't capture the cursor yet
+ if ( CanDragColSize() )
+ ChangeCursorMode(WXGRID_CURSOR_RESIZE_COL, m_colLabelWin, false);
+ }
+ }
+ else if ( m_cursorMode != WXGRID_CURSOR_SELECT_CELL )
+ {
+ ChangeCursorMode(WXGRID_CURSOR_SELECT_CELL, m_colLabelWin, false);
+ }
+ }
+}
+
+
+void wxGrid::ProcessCornerLabelMouseEvent( wxMouseEvent& event )
+{
+ if ( event.LeftDown() )
+ {
+ // indicate corner label by having both row and
+ // col args == -1
+ //
+ if ( !SendEvent( wxEVT_GRID_LABEL_LEFT_CLICK, -1, -1, event ) )
+ {
+ SelectAll();
+ }
+ }
+
+ else if ( event.LeftDClick() )
+ {
+ SendEvent( wxEVT_GRID_LABEL_LEFT_DCLICK, -1, -1, event );
+ }
+
+ else if ( event.RightDown() )
+ {
+ if ( !SendEvent( wxEVT_GRID_LABEL_RIGHT_CLICK, -1, -1, event ) )
+ {
+ // no default action at the moment
+ }
+ }
+
+ else if ( event.RightDClick() )
+ {
+ if ( !SendEvent( wxEVT_GRID_LABEL_RIGHT_DCLICK, -1, -1, event ) )
+ {
+ // no default action at the moment
+ }
+ }
+}
+
+void wxGrid::ChangeCursorMode(CursorMode mode,
+ wxWindow *win,
+ bool captureMouse)
+{
+#ifdef __WXDEBUG__
+ static const wxChar *cursorModes[] =
+ {
+ _T("SELECT_CELL"),
+ _T("RESIZE_ROW"),
+ _T("RESIZE_COL"),
+ _T("SELECT_ROW"),
+ _T("SELECT_COL")
+ };
+
+ wxLogTrace(_T("grid"),
+ _T("wxGrid cursor mode (mouse capture for %s): %s -> %s"),
+ win == m_colLabelWin ? _T("colLabelWin")
+ : win ? _T("rowLabelWin")
+ : _T("gridWin"),
+ cursorModes[m_cursorMode], cursorModes[mode]);
+#endif // __WXDEBUG__
+
+ if ( mode == m_cursorMode &&
+ win == m_winCapture &&
+ captureMouse == (m_winCapture != NULL))
+ return;
+
+ if ( !win )
+ {
+ // by default use the grid itself
+ win = m_gridWin;
+ }
+
+ if ( m_winCapture )
+ {
+ if (m_winCapture->HasCapture()) m_winCapture->ReleaseMouse();
+ m_winCapture = (wxWindow *)NULL;
+ }
+
+ m_cursorMode = mode;
+
+ switch ( m_cursorMode )
+ {
+ case WXGRID_CURSOR_RESIZE_ROW:
+ win->SetCursor( m_rowResizeCursor );
+ break;
+
+ case WXGRID_CURSOR_RESIZE_COL:
+ win->SetCursor( m_colResizeCursor );
+ break;
+
+ default:
+ win->SetCursor( *wxSTANDARD_CURSOR );
+ }
+
+ // we need to capture mouse when resizing
+ bool resize = m_cursorMode == WXGRID_CURSOR_RESIZE_ROW ||
+ m_cursorMode == WXGRID_CURSOR_RESIZE_COL;
+
+ if ( captureMouse && resize )
+ {
+ win->CaptureMouse();
+ m_winCapture = win;
+ }
+}
+
+void wxGrid::ProcessGridCellMouseEvent( wxMouseEvent& event )
+{
+ int x, y;
+ wxPoint pos( event.GetPosition() );
+ CalcUnscrolledPosition( pos.x, pos.y, &x, &y );
+
+ wxGridCellCoords coords;
+ XYToCell( x, y, coords );
+
+ int cell_rows, cell_cols;
+ bool isFirstDrag = !m_isDragging;
+ GetCellSize( coords.GetRow(), coords.GetCol(), &cell_rows, &cell_cols );
+ if ((cell_rows < 0) || (cell_cols < 0))
+ {
+ coords.SetRow(coords.GetRow() + cell_rows);
+ coords.SetCol(coords.GetCol() + cell_cols);
+ }
+
+ if ( event.Dragging() )
+ {
+ //wxLogDebug("pos(%d, %d) coords(%d, %d)", pos.x, pos.y, coords.GetRow(), coords.GetCol());
+
+ // Don't start doing anything until the mouse has been drug at
+ // least 3 pixels in any direction...
+ if (! m_isDragging)
+ {
+ if (m_startDragPos == wxDefaultPosition)
+ {
+ m_startDragPos = pos;
+ return;
+ }
+ if (abs(m_startDragPos.x - pos.x) < 4 && abs(m_startDragPos.y - pos.y) < 4)
+ return;
+ }
+
+ m_isDragging = true;
+ if ( m_cursorMode == WXGRID_CURSOR_SELECT_CELL )
+ {
+ // Hide the edit control, so it
+ // won't interfer with drag-shrinking.
+ if ( IsCellEditControlShown() )
+ {
+ HideCellEditControl();
+ SaveEditControlValue();
+ }
+
+ // Have we captured the mouse yet?
+ if (! m_winCapture)
+ {
+ m_winCapture = m_gridWin;
+ m_winCapture->CaptureMouse();
+ }
+
+ if ( coords != wxGridNoCellCoords )
+ {
+ if ( event.ControlDown() )
+ {
+ if ( m_selectingKeyboard == wxGridNoCellCoords)
+ m_selectingKeyboard = coords;
+ HighlightBlock ( m_selectingKeyboard, coords );
+ }
+ else if ( CanDragCell() )
+ {
+ if ( isFirstDrag )
+ {
+ if ( m_selectingKeyboard == wxGridNoCellCoords)
+ m_selectingKeyboard = coords;
+
+ SendEvent( wxEVT_GRID_CELL_BEGIN_DRAG,
+ coords.GetRow(),
+ coords.GetCol(),
+ event );
+ }
+ }
+ else
+ {
+ if ( !IsSelection() )
+ {
+ HighlightBlock( coords, coords );
+ }
+ else
+ {
+ HighlightBlock( m_currentCellCoords, coords );
+ }
+ }
+
+ if (! IsVisible(coords))
+ {
+ MakeCellVisible(coords);
+ // TODO: need to introduce a delay or something here. The
+ // scrolling is way to fast, at least on MSW - also on GTK.
+ }
+ }
+ }
+ else if ( m_cursorMode == WXGRID_CURSOR_RESIZE_ROW )
+ {
+ int cw, ch, left, dummy;
+ m_gridWin->GetClientSize( &cw, &ch );
+ CalcUnscrolledPosition( 0, 0, &left, &dummy );
+
+ wxClientDC dc( m_gridWin );
+ PrepareDC( dc );
+ y = wxMax( y, GetRowTop(m_dragRowOrCol) +
+ GetRowMinimalHeight(m_dragRowOrCol) );
+ dc.SetLogicalFunction(wxINVERT);
+ if ( m_dragLastPos >= 0 )
+ {
+ dc.DrawLine( left, m_dragLastPos, left+cw, m_dragLastPos );
+ }
+ dc.DrawLine( left, y, left+cw, y );
+ m_dragLastPos = y;
+ }
+ else if ( m_cursorMode == WXGRID_CURSOR_RESIZE_COL )
+ {
+ int cw, ch, dummy, top;
+ m_gridWin->GetClientSize( &cw, &ch );
+ CalcUnscrolledPosition( 0, 0, &dummy, &top );
+
+ wxClientDC dc( m_gridWin );
+ PrepareDC( dc );
+ x = wxMax( x, GetColLeft(m_dragRowOrCol) +
+ GetColMinimalWidth(m_dragRowOrCol) );
+ dc.SetLogicalFunction(wxINVERT);
+ if ( m_dragLastPos >= 0 )
+ {
+ dc.DrawLine( m_dragLastPos, top, m_dragLastPos, top+ch );
+ }
+ dc.DrawLine( x, top, x, top+ch );
+ m_dragLastPos = x;
+ }
+
+ return;
+ }
+
+ m_isDragging = false;
+ m_startDragPos = wxDefaultPosition;
+
+ // VZ: if we do this, the mode is reset to WXGRID_CURSOR_SELECT_CELL
+ // immediately after it becomes WXGRID_CURSOR_RESIZE_ROW/COL under
+ // wxGTK
+#if 0
+ if ( event.Entering() || event.Leaving() )
+ {
+ ChangeCursorMode(WXGRID_CURSOR_SELECT_CELL);
+ m_gridWin->SetCursor( *wxSTANDARD_CURSOR );
+ }
+ else
+#endif // 0
+
+ // ------------ Left button pressed
+ //
+ if ( event.LeftDown() && coords != wxGridNoCellCoords )
+ {
+ if ( !SendEvent( wxEVT_GRID_CELL_LEFT_CLICK,
+ coords.GetRow(),
+ coords.GetCol(),
+ event ) )
+ {
+ if ( !event.ControlDown() )
+ ClearSelection();
+ if ( event.ShiftDown() )
+ {
+ if ( m_selection )
+ {
+ m_selection->SelectBlock( m_currentCellCoords.GetRow(),
+ m_currentCellCoords.GetCol(),
+ coords.GetRow(),
+ coords.GetCol(),
+ event.ControlDown(),
+ event.ShiftDown(),
+ event.AltDown(),
+ event.MetaDown() );
+ }
+ }
+ else if ( XToEdgeOfCol(x) < 0 &&
+ YToEdgeOfRow(y) < 0 )
+ {
+ DisableCellEditControl();
+ MakeCellVisible( coords );
+
+ if ( event.ControlDown() )
+ {
+ if ( m_selection )
+ {
+ m_selection->ToggleCellSelection( coords.GetRow(),
+ coords.GetCol(),
+ event.ControlDown(),
+ event.ShiftDown(),
+ event.AltDown(),
+ event.MetaDown() );
+ }
+ m_selectingTopLeft = wxGridNoCellCoords;
+ m_selectingBottomRight = wxGridNoCellCoords;
+ m_selectingKeyboard = coords;
+ }
+ else
+ {
+ m_waitForSlowClick = m_currentCellCoords == coords && coords != wxGridNoCellCoords;
+ SetCurrentCell( coords );
+ if ( m_selection )
+ {
+ if ( m_selection->GetSelectionMode() !=
+ wxGrid::wxGridSelectCells )
+ {
+ HighlightBlock( coords, coords );
+ }
+ }
+ }
+ }
+ }
+ }
+
+
+ // ------------ Left double click
+ //
+ else if ( event.LeftDClick() && coords != wxGridNoCellCoords )
+ {
+ DisableCellEditControl();
+
+ if ( XToEdgeOfCol(x) < 0 && YToEdgeOfRow(y) < 0 )
+ {
+ if ( !SendEvent( wxEVT_GRID_CELL_LEFT_DCLICK,
+ coords.GetRow(),
+ coords.GetCol(),
+ event ) )
+ {
+ // we want double click to select a cell and start editing
+ // (i.e. to behave in same way as sequence of two slow clicks):
+ m_waitForSlowClick = true;
+ }
+ }
+
+ }
+
+
+ // ------------ Left button released
+ //
+ else if ( event.LeftUp() )
+ {
+ if ( m_cursorMode == WXGRID_CURSOR_SELECT_CELL )
+ {
+ if (m_winCapture)
+ {
+ if (m_winCapture->HasCapture()) m_winCapture->ReleaseMouse();
+ m_winCapture = NULL;
+ }
+
+ if ( coords == m_currentCellCoords && m_waitForSlowClick && CanEnableCellControl())
+ {
+ ClearSelection();
+ EnableCellEditControl();
+
+ wxGridCellAttr* attr = GetCellAttr(coords);
+ wxGridCellEditor *editor = attr->GetEditor(this, coords.GetRow(), coords.GetCol());
+ editor->StartingClick();
+ editor->DecRef();
+ attr->DecRef();
+
+ m_waitForSlowClick = false;
+ }
+ else if ( m_selectingTopLeft != wxGridNoCellCoords &&
+ m_selectingBottomRight != wxGridNoCellCoords )
+ {
+ if ( m_selection )
+ {
+ m_selection->SelectBlock( m_selectingTopLeft.GetRow(),
+ m_selectingTopLeft.GetCol(),
+ m_selectingBottomRight.GetRow(),
+ m_selectingBottomRight.GetCol(),
+ event.ControlDown(),
+ event.ShiftDown(),
+ event.AltDown(),
+ event.MetaDown() );
+ }
+
+ m_selectingTopLeft = wxGridNoCellCoords;
+ m_selectingBottomRight = wxGridNoCellCoords;
+
+ // Show the edit control, if it has been hidden for
+ // drag-shrinking.
+ ShowCellEditControl();
+ }
+ }
+ else if ( m_cursorMode == WXGRID_CURSOR_RESIZE_ROW )
+ {
+ ChangeCursorMode(WXGRID_CURSOR_SELECT_CELL);
+ DoEndDragResizeRow();
+
+ // Note: we are ending the event *after* doing
+ // default processing in this case
+ //
+ SendEvent( wxEVT_GRID_ROW_SIZE, m_dragRowOrCol, -1, event );
+ }
+ else if ( m_cursorMode == WXGRID_CURSOR_RESIZE_COL )
+ {
+ ChangeCursorMode(WXGRID_CURSOR_SELECT_CELL);
+ DoEndDragResizeCol();
+
+ // Note: we are ending the event *after* doing
+ // default processing in this case
+ //
+ SendEvent( wxEVT_GRID_COL_SIZE, -1, m_dragRowOrCol, event );
+ }
+
+ m_dragLastPos = -1;
+ }
+
+
+ // ------------ Right button down
+ //
+ else if ( event.RightDown() && coords != wxGridNoCellCoords )
+ {
+ DisableCellEditControl();
+ if ( !SendEvent( wxEVT_GRID_CELL_RIGHT_CLICK,
+ coords.GetRow(),
+ coords.GetCol(),
+ event ) )
+ {
+ // no default action at the moment
+ }
+ }
+
+
+ // ------------ Right double click
+ //
+ else if ( event.RightDClick() && coords != wxGridNoCellCoords )
+ {
+ DisableCellEditControl();
+ if ( !SendEvent( wxEVT_GRID_CELL_RIGHT_DCLICK,
+ coords.GetRow(),
+ coords.GetCol(),
+ event ) )
+ {
+ // no default action at the moment
+ }
+ }
+
+ // ------------ Moving and no button action
+ //
+ else if ( event.Moving() && !event.IsButton() )
+ {
+ if( coords.GetRow() < 0 || coords.GetCol() < 0 )
+ {
+ // out of grid cell area
+ ChangeCursorMode(WXGRID_CURSOR_SELECT_CELL);
+ return;
+ }
+
+ int dragRow = YToEdgeOfRow( y );
+ int dragCol = XToEdgeOfCol( x );
+
+ // Dragging on the corner of a cell to resize in both
+ // directions is not implemented yet...
+ //
+ if ( dragRow >= 0 && dragCol >= 0 )
+ {
+ ChangeCursorMode(WXGRID_CURSOR_SELECT_CELL);
+ return;
+ }
+
+ if ( dragRow >= 0 )
+ {
+ m_dragRowOrCol = dragRow;
+
+ if ( m_cursorMode == WXGRID_CURSOR_SELECT_CELL )
+ {
+ if ( CanDragRowSize() && CanDragGridSize() )
+ ChangeCursorMode(WXGRID_CURSOR_RESIZE_ROW);
+ }
+
+ if ( dragCol >= 0 )
+ {
+ m_dragRowOrCol = dragCol;
+ }
+
+ return;
+ }
+
+ if ( dragCol >= 0 )
+ {
+ m_dragRowOrCol = dragCol;
+
+ if ( m_cursorMode == WXGRID_CURSOR_SELECT_CELL )
+ {
+ if ( CanDragColSize() && CanDragGridSize() )
+ ChangeCursorMode(WXGRID_CURSOR_RESIZE_COL);
+ }
+
+ return;
+ }
+
+ // Neither on a row or col edge
+ //
+ if ( m_cursorMode != WXGRID_CURSOR_SELECT_CELL )
+ {
+ ChangeCursorMode(WXGRID_CURSOR_SELECT_CELL);
+ }
+ }
+}
+
+
+void wxGrid::DoEndDragResizeRow()
+{
+ if ( m_dragLastPos >= 0 )
+ {
+ // erase the last line and resize the row
+ //
+ int cw, ch, left, dummy;
+ m_gridWin->GetClientSize( &cw, &ch );
+ CalcUnscrolledPosition( 0, 0, &left, &dummy );
+
+ wxClientDC dc( m_gridWin );
+ PrepareDC( dc );
+ dc.SetLogicalFunction( wxINVERT );
+ dc.DrawLine( left, m_dragLastPos, left+cw, m_dragLastPos );
+ HideCellEditControl();
+ SaveEditControlValue();
+
+ int rowTop = GetRowTop(m_dragRowOrCol);
+ SetRowSize( m_dragRowOrCol,
+ wxMax( m_dragLastPos - rowTop, m_minAcceptableRowHeight ) );
+
+ if ( !GetBatchCount() )
+ {
+ // Only needed to get the correct rect.y:
+ wxRect rect ( CellToRect( m_dragRowOrCol, 0 ) );
+ rect.x = 0;
+ CalcScrolledPosition(0, rect.y, &dummy, &rect.y);
+ rect.width = m_rowLabelWidth;
+ rect.height = ch - rect.y;
+ m_rowLabelWin->Refresh( true, &rect );
+ rect.width = cw;
+ // if there is a multicell block, paint all of it
+ if (m_table)
+ {
+ int i, cell_rows, cell_cols, subtract_rows = 0;
+ int leftCol = XToCol(left);
+ int rightCol = internalXToCol(left+cw);
+ if (leftCol >= 0)
+ {
+ for (i=leftCol; i<rightCol; i++)
+ {
+ GetCellSize(m_dragRowOrCol, i, &cell_rows, &cell_cols);
+ if (cell_rows < subtract_rows)
+ subtract_rows = cell_rows;
+ }
+ rect.y = GetRowTop(m_dragRowOrCol + subtract_rows);
+ CalcScrolledPosition(0, rect.y, &dummy, &rect.y);
+ rect.height = ch - rect.y;
+ }
+ }
+ m_gridWin->Refresh( false, &rect );
+ }
+
+ ShowCellEditControl();
+ }
+}
+
+
+void wxGrid::DoEndDragResizeCol()
+{
+ if ( m_dragLastPos >= 0 )
+ {
+ // erase the last line and resize the col
+ //
+ int cw, ch, dummy, top;
+ m_gridWin->GetClientSize( &cw, &ch );
+ CalcUnscrolledPosition( 0, 0, &dummy, &top );
+
+ wxClientDC dc( m_gridWin );
+ PrepareDC( dc );
+ dc.SetLogicalFunction( wxINVERT );
+ dc.DrawLine( m_dragLastPos, top, m_dragLastPos, top+ch );
+ HideCellEditControl();
+ SaveEditControlValue();
+
+ int colLeft = GetColLeft(m_dragRowOrCol);
+ SetColSize( m_dragRowOrCol,
+ wxMax( m_dragLastPos - colLeft,
+ GetColMinimalWidth(m_dragRowOrCol) ) );
+
+ if ( !GetBatchCount() )
+ {
+ // Only needed to get the correct rect.x:
+ wxRect rect ( CellToRect( 0, m_dragRowOrCol ) );
+ rect.y = 0;
+ CalcScrolledPosition(rect.x, 0, &rect.x, &dummy);
+ rect.width = cw - rect.x;
+ rect.height = m_colLabelHeight;
+ m_colLabelWin->Refresh( true, &rect );
+ rect.height = ch;
+ // if there is a multicell block, paint all of it
+ if (m_table)
+ {
+ int i, cell_rows, cell_cols, subtract_cols = 0;
+ int topRow = YToRow(top);
+ int bottomRow = internalYToRow(top+cw);
+ if (topRow >= 0)
+ {
+ for (i=topRow; i<bottomRow; i++)
+ {
+ GetCellSize(i, m_dragRowOrCol, &cell_rows, &cell_cols);
+ if (cell_cols < subtract_cols)
+ subtract_cols = cell_cols;
+ }
+ rect.x = GetColLeft(m_dragRowOrCol + subtract_cols);
+ CalcScrolledPosition(rect.x, 0, &rect.x, &dummy);
+ rect.width = cw - rect.x;
+ }
+ }
+ m_gridWin->Refresh( false, &rect );
+ }
+
+ ShowCellEditControl();
+ }
+}
+
+
+
+//
+// ------ interaction with data model
+//
+bool wxGrid::ProcessTableMessage( wxGridTableMessage& msg )
+{
+ switch ( msg.GetId() )
+ {
+ case wxGRIDTABLE_REQUEST_VIEW_GET_VALUES:
+ return GetModelValues();
+
+ case wxGRIDTABLE_REQUEST_VIEW_SEND_VALUES:
+ return SetModelValues();
+
+ case wxGRIDTABLE_NOTIFY_ROWS_INSERTED:
+ case wxGRIDTABLE_NOTIFY_ROWS_APPENDED:
+ case wxGRIDTABLE_NOTIFY_ROWS_DELETED:
+ case wxGRIDTABLE_NOTIFY_COLS_INSERTED:
+ case wxGRIDTABLE_NOTIFY_COLS_APPENDED:
+ case wxGRIDTABLE_NOTIFY_COLS_DELETED:
+ return Redimension( msg );
+
+ default:
+ return false;
+ }
+}
+
+
+
+// The behaviour of this function depends on the grid table class
+// Clear() function. For the default wxGridStringTable class the
+// behavious is to replace all cell contents with wxEmptyString but
+// not to change the number of rows or cols.
+//
+void wxGrid::ClearGrid()
+{
+ if ( m_table )
+ {
+ if (IsCellEditControlEnabled())
+ DisableCellEditControl();
+
+ m_table->Clear();
+ if ( !GetBatchCount() ) m_gridWin->Refresh();
+ }
+}
+
+
+bool wxGrid::InsertRows( int pos, int numRows, bool WXUNUSED(updateLabels) )
+{
+ // TODO: something with updateLabels flag
+
+ if ( !m_created )
+ {
+ wxFAIL_MSG( wxT("Called wxGrid::InsertRows() before calling CreateGrid()") );
+ return false;
+ }
+
+ if ( m_table )
+ {
+ if (IsCellEditControlEnabled())
+ DisableCellEditControl();
+
+ bool done = m_table->InsertRows( pos, numRows );
+ return done;
+
+ // the table will have sent the results of the insert row
+ // operation to this view object as a grid table message
+ }
+ return false;
+}
+
+
+bool wxGrid::AppendRows( int numRows, bool WXUNUSED(updateLabels) )
+{
+ // TODO: something with updateLabels flag
+
+ if ( !m_created )
+ {
+ wxFAIL_MSG( wxT("Called wxGrid::AppendRows() before calling CreateGrid()") );
+ return false;
+ }
+
+ if ( m_table )
+ {
+ bool done = m_table && m_table->AppendRows( numRows );
+ return done;
+ // the table will have sent the results of the append row
+ // operation to this view object as a grid table message
+ }
+ return false;
+}
+
+
+bool wxGrid::DeleteRows( int pos, int numRows, bool WXUNUSED(updateLabels) )
+{
+ // TODO: something with updateLabels flag
+
+ if ( !m_created )
+ {
+ wxFAIL_MSG( wxT("Called wxGrid::DeleteRows() before calling CreateGrid()") );
+ return false;
+ }
+
+ if ( m_table )
+ {
+ if (IsCellEditControlEnabled())
+ DisableCellEditControl();
+
+ bool done = m_table->DeleteRows( pos, numRows );
+ return done;
+ // the table will have sent the results of the delete row
+ // operation to this view object as a grid table message
+ }
+ return false;
+}
+
+
+bool wxGrid::InsertCols( int pos, int numCols, bool WXUNUSED(updateLabels) )
+{
+ // TODO: something with updateLabels flag
+
+ if ( !m_created )
+ {
+ wxFAIL_MSG( wxT("Called wxGrid::InsertCols() before calling CreateGrid()") );
+ return false;
+ }
+
+ if ( m_table )
+ {
+ if (IsCellEditControlEnabled())
+ DisableCellEditControl();
+
+ bool done = m_table->InsertCols( pos, numCols );
+ return done;
+ // the table will have sent the results of the insert col
+ // operation to this view object as a grid table message
+ }
+ return false;
+}
+
+
+bool wxGrid::AppendCols( int numCols, bool WXUNUSED(updateLabels) )
+{
+ // TODO: something with updateLabels flag
+
+ if ( !m_created )
+ {
+ wxFAIL_MSG( wxT("Called wxGrid::AppendCols() before calling CreateGrid()") );
+ return false;
+ }
+
+ if ( m_table )
+ {
+ bool done = m_table->AppendCols( numCols );
+ return done;
+ // the table will have sent the results of the append col
+ // operation to this view object as a grid table message
+ }
+ return false;
+}
+
+
+bool wxGrid::DeleteCols( int pos, int numCols, bool WXUNUSED(updateLabels) )
+{
+ // TODO: something with updateLabels flag
+
+ if ( !m_created )
+ {
+ wxFAIL_MSG( wxT("Called wxGrid::DeleteCols() before calling CreateGrid()") );
+ return false;
+ }
+
+ if ( m_table )
+ {
+ if (IsCellEditControlEnabled())
+ DisableCellEditControl();
+
+ bool done = m_table->DeleteCols( pos, numCols );
+ return done;
+ // the table will have sent the results of the delete col
+ // operation to this view object as a grid table message
+ }
+ return false;
+}
+
+
+
+//
+// ----- event handlers
+//
+
+// Generate a grid event based on a mouse event and
+// return the result of ProcessEvent()
+//
+int wxGrid::SendEvent( const wxEventType type,
+ int row, int col,
+ wxMouseEvent& mouseEv )
+{
+ bool claimed;
+ bool vetoed;
+
+ if ( type == wxEVT_GRID_ROW_SIZE || type == wxEVT_GRID_COL_SIZE )
+ {
+ int rowOrCol = (row == -1 ? col : row);
+
+ wxGridSizeEvent gridEvt( GetId(),
+ type,
+ this,
+ rowOrCol,
+ mouseEv.GetX() + GetRowLabelSize(),
+ mouseEv.GetY() + GetColLabelSize(),
+ mouseEv.ControlDown(),
+ mouseEv.ShiftDown(),
+ mouseEv.AltDown(),
+ mouseEv.MetaDown() );
+
+ claimed = GetEventHandler()->ProcessEvent(gridEvt);
+ vetoed = !gridEvt.IsAllowed();
+ }
+ else if ( type == wxEVT_GRID_RANGE_SELECT )
+ {
+ // Right now, it should _never_ end up here!
+ wxGridRangeSelectEvent gridEvt( GetId(),
+ type,
+ this,
+ m_selectingTopLeft,
+ m_selectingBottomRight,
+ true,
+ mouseEv.ControlDown(),
+ mouseEv.ShiftDown(),
+ mouseEv.AltDown(),
+ mouseEv.MetaDown() );
+
+ claimed = GetEventHandler()->ProcessEvent(gridEvt);
+ vetoed = !gridEvt.IsAllowed();
+ }
+ else
+ {
+ wxGridEvent gridEvt( GetId(),
+ type,
+ this,
+ row, col,
+ mouseEv.GetX() + GetRowLabelSize(),
+ mouseEv.GetY() + GetColLabelSize(),
+ false,
+ mouseEv.ControlDown(),
+ mouseEv.ShiftDown(),
+ mouseEv.AltDown(),
+ mouseEv.MetaDown() );
+ claimed = GetEventHandler()->ProcessEvent(gridEvt);
+ vetoed = !gridEvt.IsAllowed();
+ }
+
+ // A Veto'd event may not be `claimed' so test this first
+ if (vetoed) return -1;
+ return claimed ? 1 : 0;
+}
+
+
+// Generate a grid event of specified type and return the result
+// of ProcessEvent().
+//
+int wxGrid::SendEvent( const wxEventType type,
+ int row, int col )
+{
+ bool claimed;
+ bool vetoed;
+
+ if ( type == wxEVT_GRID_ROW_SIZE || type == wxEVT_GRID_COL_SIZE )
+ {
+ int rowOrCol = (row == -1 ? col : row);
+
+ wxGridSizeEvent gridEvt( GetId(),
+ type,
+ this,
+ rowOrCol );
+
+ claimed = GetEventHandler()->ProcessEvent(gridEvt);
+ vetoed = !gridEvt.IsAllowed();
+ }
+ else
+ {
+ wxGridEvent gridEvt( GetId(),
+ type,
+ this,
+ row, col );
+
+ claimed = GetEventHandler()->ProcessEvent(gridEvt);
+ vetoed = !gridEvt.IsAllowed();
+ }
+
+ // A Veto'd event may not be `claimed' so test this first
+ if (vetoed) return -1;
+ return claimed ? 1 : 0;
+}
+
+
+void wxGrid::OnPaint( wxPaintEvent& WXUNUSED(event) )
+{
+ wxPaintDC dc(this); // needed to prevent zillions of paint events on MSW
+}
+
+void wxGrid::Refresh(bool eraseb, const wxRect* rect)
+{
+ // Don't do anything if between Begin/EndBatch...
+ // EndBatch() will do all this on the last nested one anyway.
+ if (! GetBatchCount())
+ {
+ // Refresh to get correct scrolled position:
+ wxScrolledWindow::Refresh(eraseb,rect);
+
+ if (rect)
+ {
+ int rect_x, rect_y, rectWidth, rectHeight;
+ int width_label, width_cell, height_label, height_cell;
+ int x, y;
+
+ //Copy rectangle can get scroll offsets..
+ rect_x = rect->GetX();
+ rect_y = rect->GetY();
+ rectWidth = rect->GetWidth();
+ rectHeight = rect->GetHeight();
+
+ width_label = m_rowLabelWidth - rect_x;
+ if (width_label > rectWidth) width_label = rectWidth;
+
+ height_label = m_colLabelHeight - rect_y;
+ if (height_label > rectHeight) height_label = rectHeight;
+
+ if (rect_x > m_rowLabelWidth)
+ {
+ x = rect_x - m_rowLabelWidth;
+ width_cell = rectWidth;
+ }
+ else
+ {
+ x = 0;
+ width_cell = rectWidth - (m_rowLabelWidth - rect_x);
+ }
+
+ if (rect_y > m_colLabelHeight)
+ {
+ y = rect_y - m_colLabelHeight;
+ height_cell = rectHeight;
+ }
+ else
+ {
+ y = 0;
+ height_cell = rectHeight - (m_colLabelHeight - rect_y);
+ }
+
+ // Paint corner label part intersecting rect.
+ if ( width_label > 0 && height_label > 0 )
+ {
+ wxRect anotherrect(rect_x, rect_y, width_label, height_label);
+ m_cornerLabelWin->Refresh(eraseb, &anotherrect);
+ }
+
+ // Paint col labels part intersecting rect.
+ if ( width_cell > 0 && height_label > 0 )
+ {
+ wxRect anotherrect(x, rect_y, width_cell, height_label);
+ m_colLabelWin->Refresh(eraseb, &anotherrect);
+ }
+
+ // Paint row labels part intersecting rect.
+ if ( width_label > 0 && height_cell > 0 )
+ {
+ wxRect anotherrect(rect_x, y, width_label, height_cell);
+ m_rowLabelWin->Refresh(eraseb, &anotherrect);
+ }
+
+ // Paint cell area part intersecting rect.
+ if ( width_cell > 0 && height_cell > 0 )
+ {
+ wxRect anotherrect(x, y, width_cell, height_cell);
+ m_gridWin->Refresh(eraseb, &anotherrect);
+ }
+ }
+ else
+ {
+ m_cornerLabelWin->Refresh(eraseb, NULL);
+ m_colLabelWin->Refresh(eraseb, NULL);
+ m_rowLabelWin->Refresh(eraseb, NULL);
+ m_gridWin->Refresh(eraseb, NULL);
+ }
+ }
+}
+
+void wxGrid::OnSize( wxSizeEvent& event )
+{
+ // position the child windows
+ CalcWindowSizes();
+
+ // don't call CalcDimensions() from here, the base class handles the size
+ // changes itself
+ event.Skip();
+}
+
+
+void wxGrid::OnKeyDown( wxKeyEvent& event )
+{
+ if ( m_inOnKeyDown )
+ {
+ // shouldn't be here - we are going round in circles...
+ //
+ wxFAIL_MSG( wxT("wxGrid::OnKeyDown called while already active") );
+ }
+
+ m_inOnKeyDown = true;
+
+ // propagate the event up and see if it gets processed
+ //
+ wxWindow *parent = GetParent();
+ wxKeyEvent keyEvt( event );
+ keyEvt.SetEventObject( parent );
+
+ if ( !parent->GetEventHandler()->ProcessEvent( keyEvt ) )
+ {
+
+ // try local handlers
+ //
+ switch ( event.GetKeyCode() )
+ {
+ case WXK_UP:
+ if ( event.ControlDown() )
+ {
+ MoveCursorUpBlock( event.ShiftDown() );
+ }
+ else
+ {
+ MoveCursorUp( event.ShiftDown() );
+ }
+ break;
+
+ case WXK_DOWN:
+ if ( event.ControlDown() )
+ {
+ MoveCursorDownBlock( event.ShiftDown() );
+ }
+ else
+ {
+ MoveCursorDown( event.ShiftDown() );
+ }
+ break;
+
+ case WXK_LEFT:
+ if ( event.ControlDown() )
+ {
+ MoveCursorLeftBlock( event.ShiftDown() );
+ }
+ else
+ {
+ MoveCursorLeft( event.ShiftDown() );
+ }
+ break;
+
+ case WXK_RIGHT:
+ if ( event.ControlDown() )
+ {
+ MoveCursorRightBlock( event.ShiftDown() );
+ }
+ else
+ {
+ MoveCursorRight( event.ShiftDown() );
+ }
+ break;
+
+ case WXK_RETURN:
+ case WXK_NUMPAD_ENTER:
+ if ( event.ControlDown() )
+ {
+ event.Skip(); // to let the edit control have the return
+ }
+ else
+ {
+ if ( GetGridCursorRow() < GetNumberRows()-1 )
+ {
+ MoveCursorDown( event.ShiftDown() );
+ }
+ else
+ {
+ // at the bottom of a column
+ DisableCellEditControl();
+ }
+ }
+ break;
+
+ case WXK_ESCAPE:
+ ClearSelection();
+ break;
+
+ case WXK_TAB:
+ if (event.ShiftDown())
+ {
+ if ( GetGridCursorCol() > 0 )
+ {
+ MoveCursorLeft( false );
+ }
+ else
+ {
+ // at left of grid
+ DisableCellEditControl();
+ }
+ }
+ else
+ {
+ if ( GetGridCursorCol() < GetNumberCols()-1 )
+ {
+ MoveCursorRight( false );
+ }
+ else
+ {
+ // at right of grid
+ DisableCellEditControl();
+ }
+ }
+ break;
+
+ case WXK_HOME:
+ if ( event.ControlDown() )
+ {
+ MakeCellVisible( 0, 0 );
+ SetCurrentCell( 0, 0 );
+ }
+ else
+ {
+ event.Skip();
+ }
+ break;
+
+ case WXK_END:
+ if ( event.ControlDown() )
+ {
+ MakeCellVisible( m_numRows-1, m_numCols-1 );
+ SetCurrentCell( m_numRows-1, m_numCols-1 );
+ }
+ else
+ {
+ event.Skip();
+ }
+ break;
+
+ case WXK_PRIOR:
+ MovePageUp();
+ break;
+
+ case WXK_NEXT:
+ MovePageDown();
+ break;
+
+ case WXK_SPACE:
+ if ( event.ControlDown() )
+ {
+ if ( m_selection )
+ {
+ m_selection->ToggleCellSelection( m_currentCellCoords.GetRow(),
+ m_currentCellCoords.GetCol(),
+ event.ControlDown(),
+ event.ShiftDown(),
+ event.AltDown(),
+ event.MetaDown() );
+ }
+ break;
+ }
+ if ( !IsEditable() )
+ {
+ MoveCursorRight( false );
+ break;
+ }
+ // Otherwise fall through to default
+
+ default:
+ event.Skip();
+ break;
+ }
+ }
+
+ m_inOnKeyDown = false;
+}
+
+void wxGrid::OnKeyUp( wxKeyEvent& event )
+{
+ // try local handlers
+ //
+ if ( event.GetKeyCode() == WXK_SHIFT )
+ {
+ if ( m_selectingTopLeft != wxGridNoCellCoords &&
+ m_selectingBottomRight != wxGridNoCellCoords )
+ {
+ if ( m_selection )
+ {
+ m_selection->SelectBlock( m_selectingTopLeft.GetRow(),
+ m_selectingTopLeft.GetCol(),
+ m_selectingBottomRight.GetRow(),
+ m_selectingBottomRight.GetCol(),
+ event.ControlDown(),
+ true,
+ event.AltDown(),
+ event.MetaDown() );
+ }
+ }
+
+ m_selectingTopLeft = wxGridNoCellCoords;
+ m_selectingBottomRight = wxGridNoCellCoords;
+ m_selectingKeyboard = wxGridNoCellCoords;
+ }
+}
+
+void wxGrid::OnChar( wxKeyEvent& event )
+{
+ // is it possible to edit the current cell at all?
+ if ( !IsCellEditControlEnabled() && CanEnableCellControl() )
+ {
+ // yes, now check whether the cells editor accepts the key
+ int row = m_currentCellCoords.GetRow();
+ int col = m_currentCellCoords.GetCol();
+ wxGridCellAttr* attr = GetCellAttr(row, col);
+ wxGridCellEditor *editor = attr->GetEditor(this, row, col);
+
+ // <F2> is special and will always start editing, for
+ // other keys - ask the editor itself
+ if ( (event.GetKeyCode() == WXK_F2 && !event.HasModifiers())
+ || editor->IsAcceptedKey(event) )
+ {
+ // ensure cell is visble
+ MakeCellVisible(row, col);
+ EnableCellEditControl();
+
+ // a problem can arise if the cell is not completely
+ // visible (even after calling MakeCellVisible the
+ // control is not created and calling StartingKey will
+ // crash the app
+ if ( event.GetKeyCode() != WXK_F2 && editor->IsCreated() && m_cellEditCtrlEnabled )
+ editor->StartingKey(event);
+ }
+ else
+ {
+ event.Skip();
+ }
+
+ editor->DecRef();
+ attr->DecRef();
+ }
+ else
+ {
+ event.Skip();
+ }
+}
+
+
+void wxGrid::OnEraseBackground(wxEraseEvent&)
+{
+}
+
+void wxGrid::SetCurrentCell( const wxGridCellCoords& coords )
+{
+ if ( SendEvent( wxEVT_GRID_SELECT_CELL, coords.GetRow(), coords.GetCol() ) )
+ {
+ // the event has been intercepted - do nothing
+ return;
+ }
+
+ wxClientDC dc(m_gridWin);
+ PrepareDC(dc);
+
+ if ( m_currentCellCoords != wxGridNoCellCoords )
+ {
+ DisableCellEditControl();
+
+ if ( IsVisible( m_currentCellCoords, false ) )
+ {
+ wxRect r;
+ r = BlockToDeviceRect(m_currentCellCoords, m_currentCellCoords);
+ if ( !m_gridLinesEnabled )
+ {
+ r.x--;
+ r.y--;
+ r.width++;
+ r.height++;
+ }
+
+ wxGridCellCoordsArray cells = CalcCellsExposed( r );
+
+ // Otherwise refresh redraws the highlight!
+ m_currentCellCoords = coords;
+
+ DrawGridCellArea(dc,cells);
+ DrawAllGridLines( dc, r );
+ }
+ }
+
+ m_currentCellCoords = coords;
+
+ wxGridCellAttr* attr = GetCellAttr(coords);
+ DrawCellHighlight(dc, attr);
+ attr->DecRef();
+}
+
+
+void wxGrid::HighlightBlock( int topRow, int leftCol, int bottomRow, int rightCol )
+{
+ int temp;
+ wxGridCellCoords updateTopLeft, updateBottomRight;
+
+ if ( m_selection )
+ {
+ if ( m_selection->GetSelectionMode() == wxGrid::wxGridSelectRows )
+ {
+ leftCol = 0;
+ rightCol = GetNumberCols() - 1;
+ }
+ else if ( m_selection->GetSelectionMode() == wxGrid::wxGridSelectColumns )
+ {
+ topRow = 0;
+ bottomRow = GetNumberRows() - 1;
+ }
+ }
+
+ if ( topRow > bottomRow )
+ {
+ temp = topRow;
+ topRow = bottomRow;
+ bottomRow = temp;
+ }
+
+ if ( leftCol > rightCol )
+ {
+ temp = leftCol;
+ leftCol = rightCol;
+ rightCol = temp;
+ }
+
+ updateTopLeft = wxGridCellCoords( topRow, leftCol );
+ updateBottomRight = wxGridCellCoords( bottomRow, rightCol );
+
+ // First the case that we selected a completely new area
+ if ( m_selectingTopLeft == wxGridNoCellCoords ||
+ m_selectingBottomRight == wxGridNoCellCoords )
+ {
+ wxRect rect;
+ rect = BlockToDeviceRect( wxGridCellCoords ( topRow, leftCol ),
+ wxGridCellCoords ( bottomRow, rightCol ) );
+ m_gridWin->Refresh( false, &rect );
+ }
+ // Now handle changing an existing selection area.
+ else if ( m_selectingTopLeft != updateTopLeft ||
+ m_selectingBottomRight != updateBottomRight )
+ {
+ // Compute two optimal update rectangles:
+ // Either one rectangle is a real subset of the
+ // other, or they are (almost) disjoint!
+ wxRect rect[4];
+ bool need_refresh[4];
+ need_refresh[0] =
+ need_refresh[1] =
+ need_refresh[2] =
+ need_refresh[3] = false;
+ int i;
+
+ // Store intermediate values
+ wxCoord oldLeft = m_selectingTopLeft.GetCol();
+ wxCoord oldTop = m_selectingTopLeft.GetRow();
+ wxCoord oldRight = m_selectingBottomRight.GetCol();
+ wxCoord oldBottom = m_selectingBottomRight.GetRow();
+
+ // Determine the outer/inner coordinates.
+ if (oldLeft > leftCol)
+ {
+ temp = oldLeft;
+ oldLeft = leftCol;
+ leftCol = temp;
+ }
+ if (oldTop > topRow )
+ {
+ temp = oldTop;
+ oldTop = topRow;
+ topRow = temp;
+ }
+ if (oldRight < rightCol )
+ {
+ temp = oldRight;
+ oldRight = rightCol;
+ rightCol = temp;
+ }
+ if (oldBottom < bottomRow)
+ {
+ temp = oldBottom;
+ oldBottom = bottomRow;
+ bottomRow = temp;
+ }
+
+ // Now, either the stuff marked old is the outer
+ // rectangle or we don't have a situation where one
+ // is contained in the other.
+
+ if ( oldLeft < leftCol )
+ {
+ // Refresh the newly selected or deselected
+ // area to the left of the old or new selection.
+ need_refresh[0] = true;
+ rect[0] = BlockToDeviceRect( wxGridCellCoords ( oldTop,
+ oldLeft ),
+ wxGridCellCoords ( oldBottom,
+ leftCol - 1 ) );
+ }
+
+ if ( oldTop < topRow )
+ {
+ // Refresh the newly selected or deselected
+ // area above the old or new selection.
+ need_refresh[1] = true;
+ rect[1] = BlockToDeviceRect( wxGridCellCoords ( oldTop,
+ leftCol ),
+ wxGridCellCoords ( topRow - 1,
+ rightCol ) );
+ }
+
+ if ( oldRight > rightCol )
+ {
+ // Refresh the newly selected or deselected
+ // area to the right of the old or new selection.
+ need_refresh[2] = true;
+ rect[2] = BlockToDeviceRect( wxGridCellCoords ( oldTop,
+ rightCol + 1 ),
+ wxGridCellCoords ( oldBottom,
+ oldRight ) );
+ }
+
+ if ( oldBottom > bottomRow )
+ {
+ // Refresh the newly selected or deselected
+ // area below the old or new selection.
+ need_refresh[3] = true;
+ rect[3] = BlockToDeviceRect( wxGridCellCoords ( bottomRow + 1,
+ leftCol ),
+ wxGridCellCoords ( oldBottom,
+ rightCol ) );
+ }