+ 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 += (_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()
+{
+ int row, col;
+
+ m_data.Alloc( numRows );
+
+ wxArrayString sa;
+ sa.Alloc( numCols );
+ for ( col = 0; col < numCols; col++ )
+ {
+ sa.Add( wxEmptyString );
+ }
+
+ for ( row = 0; row < numRows; row++ )
+ {
+ m_data.Add( sa );
+ }
+}
+
+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 )
+{
+ wxASSERT_MSG( (row < GetNumberRows()) && (col < GetNumberCols()),
+ _T("invalid row or column index in wxGridStringTable") );
+
+ return m_data[row][col];
+}
+
+void wxGridStringTable::SetValue( int row, int col, const wxString& value )
+{
+ wxASSERT_MSG( (row < GetNumberRows()) && (col < GetNumberCols()),
+ _T("invalid row or column index in wxGridStringTable") );
+
+ m_data[row][col] = value;
+}
+
+bool wxGridStringTable::IsEmptyCell( int row, int col )
+{
+ wxASSERT_MSG( (row < GetNumberRows()) && (col < GetNumberCols()),
+ _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 row, col;
+
+ 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 );
+ for ( col = 0; col < curNumCols; col++ )
+ {
+ sa.Add( wxEmptyString );
+ }
+
+ for ( row = pos; row < pos + numRows; row++ )
+ {
+ m_data.Insert( sa, row );
+ }
+ if ( GetView() )
+ {
+ wxGridTableMessage msg( this,
+ wxGRIDTABLE_NOTIFY_ROWS_INSERTED,
+ pos,
+ numRows );
+
+ GetView()->ProcessTableMessage( msg );
+ }
+
+ return TRUE;
+}
+
+bool wxGridStringTable::AppendRows( size_t numRows )
+{
+ size_t row, col;
+
+ 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 );
+ for ( col = 0; col < curNumCols; col++ )
+ {
+ sa.Add( wxEmptyString );
+ }
+ }
+
+ for ( row = 0; row < numRows; row++ )
+ {
+ m_data.Add( sa );
+ }
+
+ 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 n;
+
+ size_t curNumRows = m_data.GetCount();
+
+ if ( pos >= curNumRows )
+ {
+ wxString errmsg;
+ errmsg.Printf(wxT("Called wxGridStringTable::DeleteRows(pos=%d, N=%d)\nPos value is invalid for present table with %d rows"),
+ pos, numRows, curNumRows );
+ wxFAIL_MSG( errmsg );
+ return FALSE;
+ }
+
+ if ( numRows > curNumRows - pos )
+ {
+ numRows = curNumRows - pos;
+ }
+
+ if ( numRows >= curNumRows )
+ {
+ m_data.Empty(); // don't release memory just yet
+ }
+ else
+ {
+ for ( n = 0; n < numRows; n++ )
+ {
+ m_data.RemoveAt( pos );
+ }
+ }
+ 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, n;
+
+ 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++ )
+ {
+ for ( n = 0; n < numCols; n++ )
+ {
+ m_data[row].Add( wxEmptyString );
+ }
+ }
+
+ 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, n;
+
+ size_t curNumRows = m_data.GetCount();
+ size_t curNumCols = ( curNumRows > 0 ? m_data[0].GetCount() :
+ ( GetView() ? GetView()->GetNumberCols() : 0 ) );
+
+ if ( pos >= curNumCols )
+ {
+ wxString errmsg;
+ errmsg.Printf( wxT("Called wxGridStringTable::DeleteCols(pos=%d, N=%d)...\nPos value is invalid for present table with %d cols"),
+ pos, numCols, curNumCols );
+ wxFAIL_MSG( errmsg );
+ return FALSE;
+ }
+
+ if ( numCols > curNumCols - pos )
+ {
+ numCols = curNumCols - pos;
+ }
+
+ for ( row = 0; row < curNumRows; row++ )
+ {
+ if ( numCols >= curNumCols )
+ {
+ m_data[row].Clear();
+ }
+ else
+ {
+ for ( n = 0; n < numCols; n++ )
+ {
+ m_data[row].RemoveAt( pos );
+ }
+ }
+ }
+ 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 )
+END_EVENT_TABLE()
+
+wxGridRowLabelWindow::wxGridRowLabelWindow( wxGrid *parent,
+ wxWindowID id,
+ const wxPoint &pos, const wxSize &size )
+ : wxWindow( parent, id, pos, size, wxWANTS_CHARS )
+{
+ 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();
+}
+
+
+
+//////////////////////////////////////////////////////////////////////
+
+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 )
+END_EVENT_TABLE()
+
+wxGridColLabelWindow::wxGridColLabelWindow( wxGrid *parent,
+ wxWindowID id,
+ const wxPoint &pos, const wxSize &size )
+ : wxWindow( parent, id, pos, size, wxWANTS_CHARS )
+{
+ 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();
+}
+
+
+
+//////////////////////////////////////////////////////////////////////
+
+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 )
+END_EVENT_TABLE()
+
+wxGridCornerLabelWindow::wxGridCornerLabelWindow( wxGrid *parent,
+ wxWindowID id,
+ const wxPoint &pos, const wxSize &size )
+ : wxWindow( parent, id, pos, size, wxWANTS_CHARS )
+{
+ 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 );
+
+ dc.SetPen( *wxBLACK_PEN );
+ 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.SetPen( *wxWHITE_PEN );
+ dc.DrawLine( 0, 0, client_width, 0 );
+ dc.DrawLine( 0, 0, 0, client_height );
+}
+
+
+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();
+}
+
+
+
+//////////////////////////////////////////////////////////////////////
+
+IMPLEMENT_DYNAMIC_CLASS( wxGridWindow, wxPanel )
+
+BEGIN_EVENT_TABLE( wxGridWindow, wxPanel )
+ EVT_PAINT( wxGridWindow::OnPaint )
+ EVT_MOUSEWHEEL( wxGridWindow::OnMouseWheel)
+ EVT_MOUSE_EVENTS( wxGridWindow::OnMouseEvent )
+ EVT_KEY_DOWN( wxGridWindow::OnKeyDown )
+ EVT_KEY_UP( wxGridWindow::OnKeyUp )
+ EVT_ERASE_BACKGROUND( wxGridWindow::OnEraseBackground )
+END_EVENT_TABLE()
+
+wxGridWindow::wxGridWindow( wxGrid *parent,
+ wxGridRowLabelWindow *rowLblWin,
+ wxGridColLabelWindow *colLblWin,
+ wxWindowID id, const wxPoint &pos, const wxSize &size )
+ : wxPanel( parent, id, pos, size, wxWANTS_CHARS, "grid window" )
+{
+ m_owner = parent;
+ m_rowLabelWin = rowLblWin;
+ m_colLabelWin = colLblWin;
+ SetBackgroundColour(_T("WHITE"));
+}
+
+
+wxGridWindow::~wxGridWindow()
+{
+}
+
+
+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 )
+{
+ wxPanel::ScrollWindow( dx, dy, rect );
+ m_rowLabelWin->ScrollWindow( 0, dy, rect );
+ m_colLabelWin->ScrollWindow( dx, 0, rect );
+}
+
+
+void wxGridWindow::OnMouseEvent( wxMouseEvent& event )
+{
+ 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::OnEraseBackground( wxEraseEvent& WXUNUSED(event) )
+{
+}
+
+
+//////////////////////////////////////////////////////////////////////
+
+
+IMPLEMENT_DYNAMIC_CLASS( wxGrid, wxScrolledWindow )
+
+BEGIN_EVENT_TABLE( wxGrid, wxScrolledWindow )
+ EVT_PAINT( wxGrid::OnPaint )
+ EVT_SIZE( wxGrid::OnSize )
+ EVT_KEY_DOWN( wxGrid::OnKeyDown )
+ EVT_KEY_UP( wxGrid::OnKeyUp )
+ EVT_ERASE_BACKGROUND( wxGrid::OnEraseBackground )
+END_EVENT_TABLE()
+
+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();
+}
+
+
+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
+//
+
+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;
+ m_defaultCellAttr->SetDefAttr(m_defaultCellAttr);
+
+ // Set default cell attributes
+ m_defaultCellAttr->SetKind(wxGridCellAttr::Default);
+ m_defaultCellAttr->SetFont(GetFont());
+ m_defaultCellAttr->SetAlignment(wxALIGN_LEFT, wxALIGN_TOP);
+ m_defaultCellAttr->SetTextColour(
+ wxSystemSettings::GetSystemColour(wxSYS_COLOUR_WINDOWTEXT));
+ m_defaultCellAttr->SetBackgroundColour(
+ wxSystemSettings::GetSystemColour(wxSYS_COLOUR_WINDOW));
+ m_defaultCellAttr->SetRenderer(new wxGridCellStringRenderer);
+ m_defaultCellAttr->SetEditor(new wxGridCellTextEditor);
+
+
+ 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 = 0;
+ // subwindow components that make up the wxGrid
+ m_cornerLabelWin = new wxGridCornerLabelWindow( this,
+ -1,
+ wxDefaultPosition,
+ wxDefaultSize );
+
+ m_rowLabelWin = new wxGridRowLabelWindow( this,
+ -1,
+ wxDefaultPosition,
+ wxDefaultSize );
+
+ m_colLabelWin = new wxGridColLabelWindow( this,
+ -1,
+ wxDefaultPosition,
+ wxDefaultSize );
+
+ m_gridWin = new wxGridWindow( this,
+ m_rowLabelWin,
+ m_colLabelWin,
+ -1,
+ wxDefaultPosition,
+ wxDefaultSize );
+
+ SetTargetWindow( m_gridWin );
+}
+
+
+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 );
+ Init();
+ m_created = TRUE;
+
+ return m_created;
+}
+
+void wxGrid::SetSelectionMode(wxGrid::wxGridSelectionModes selmode)
+{
+ if ( !m_created )
+ {
+ wxFAIL_MSG( wxT("Called wxGrid::SetSelectionMode() before calling CreateGrid()") );
+ }
+ else
+ m_selection->SetSelectionMode( selmode );
+}
+
+bool wxGrid::SetTable( wxGridTableBase *table, bool takeOwnership,
+ wxGrid::wxGridSelectionModes selmode )
+{
+ if ( m_created )
+ {
+ // RD: Actually, this should probably be allowed. I think it would be
+ // nice to be able to switch multiple Tables in and out of a single
+ // View at runtime. Is there anything in the implmentation that would
+ // prevent this?
+
+ // At least, you now have to cope with m_selection
+ wxFAIL_MSG( wxT("wxGrid::CreateGrid or wxGrid::SetTable called more than once") );
+ return FALSE;
+ }
+ else
+ {
+ 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 );
+ Init();
+ 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;
+
+ // TODO: something better than this ?
+ //
+ m_labelFont = this->GetFont();
+ m_labelFont.SetWeight( m_labelFont.GetWeight() + 2 );
+
+ m_rowLabelHorizAlign = wxALIGN_LEFT;
+ m_rowLabelVertAlign = wxALIGN_CENTRE;
+
+ m_colLabelHorizAlign = wxALIGN_CENTRE;
+ m_colLabelVertAlign = wxALIGN_TOP;
+
+ m_defaultColWidth = WXGRID_DEFAULT_COL_WIDTH;
+ m_defaultRowHeight = m_gridWin->GetCharHeight();
+
+#if defined(__WXMOTIF__) || defined(__WXGTK__) // see also text ctrl sizing in ShowCellEditControl()
+ m_defaultRowHeight += 8;
+#else
+ m_defaultRowHeight += 4;
+#endif
+
+ m_gridLineColour = wxColour( 128, 128, 255 );
+ m_gridLinesEnabled = TRUE;
+ m_cellHighlightColour = m_gridLineColour;
+ 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_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::GetSystemColour(wxSYS_COLOUR_HIGHLIGHT);
+ m_selectionForeground = wxSystemSettings::GetSystemColour(wxSYS_COLOUR_HIGHLIGHTTEXT);
+
+ m_editable = TRUE; // default for whole grid
+
+ m_inOnKeyDown = FALSE;
+ m_batchCount = 0;
+
+ m_extraWidth =
+ m_extraHeight = 50;
+
+ CalcDimensions();
+}
+
+// ----------------------------------------------------------------------------
+// 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;
+ for ( int i = 0; i < m_numRows; i++ )
+ {
+ m_rowHeights.Add( m_defaultRowHeight );
+ 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;
+ for ( int i = 0; i < m_numCols; i++ )
+ {
+ m_colWidths.Add( m_defaultColWidth );
+ 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_colLabelWin->IsShown() )
+ cw -= m_rowLabelWidth;
+ if ( m_rowLabelWin->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;
+
+ // preserve (more or less) the previous position
+ int x, y;
+ GetViewStart( &x, &y );
+ // maybe we don't need scrollbars at all? and if we do, transform w and h
+ // from pixels into logical units
+ if ( w <= cw )
+ {
+ w = 0; x= 0;
+ }
+ else
+ {
+ w = (w + GRID_SCROLL_LINE - 1)/GRID_SCROLL_LINE;
+ if ( x >= w )
+ x = w - 1;
+ }
+ if ( h <= ch )
+ {
+ h = 0; y = 0;
+ }
+ else
+ {
+ h = (h + GRID_SCROLL_LINE - 1)/GRID_SCROLL_LINE;
+ if ( y >= h )
+ y = h - 1;
+ }
+
+ // do set scrollbar parameters
+ SetScrollbars( GRID_SCROLL_LINE, GRID_SCROLL_LINE,
+ w, h, x, y, (GetBatchCount() != 0));
+}
+
+
+void wxGrid::CalcWindowSizes()
+{
+ int cw, ch;
+ GetClientSize( &cw, &ch );
+
+ if ( m_cornerLabelWin->IsShown() )
+ m_cornerLabelWin->SetSize( 0, 0, m_rowLabelWidth, m_colLabelHeight );
+
+ if ( m_colLabelWin->IsShown() )
+ m_colLabelWin->SetSize( m_rowLabelWidth, 0, cw-m_rowLabelWidth, m_colLabelHeight);
+
+ if ( m_rowLabelWin->IsShown() )
+ m_rowLabelWin->SetSize( 0, m_colLabelHeight, m_rowLabelWidth, ch-m_colLabelHeight);
+
+ if ( 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;
+
+#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() )
+ {
+ for ( i = 0; i < numRows; i++ )
+ {
+ m_rowHeights.Insert( m_defaultRowHeight, pos );
+ m_rowBottoms.Insert( 0, pos );
+ }
+
+ 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 );
+ }
+ 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() )
+ {
+ for ( i = 0; i < numRows; i++ )
+ {
+ m_rowHeights.Add( m_defaultRowHeight );
+ m_rowBottoms.Add( 0 );
+ }
+
+ 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() )
+ {
+ for ( i = 0; i < numRows; i++ )
+ {
+ m_rowHeights.RemoveAt( pos );
+ m_rowBottoms.RemoveAt( pos );
+ }
+
+ 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 );
+ }
+ 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() )
+ {
+ for ( i = 0; i < numCols; i++ )
+ {
+ m_colWidths.Insert( m_defaultColWidth, pos );
+ m_colRights.Insert( 0, pos );
+ }
+
+ 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 );
+ }
+ 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() )
+ {
+ for ( i = 0; i < numCols; i++ )
+ {
+ m_colWidths.Add( m_defaultColWidth );
+ m_colRights.Add( 0 );
+ }
+
+ 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() )
+ {
+ for ( i = 0; i < numCols; i++ )
+ {
+ m_colWidths.RemoveAt( pos );
+ m_colRights.RemoveAt( pos );
+ }
+
+ 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 );
+ }
+ 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 = 0; 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 = 0; 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 = 0; row < m_numRows; row++ )
+ {
+ if ( GetRowBottom(row) <= top )
+ continue;
+
+ if ( GetRowTop(row) > bottom )
+ break;
+
+
+ for ( col = 0; 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() )
+ {
+ m_isDragging = TRUE;
+
+ 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 )
+ {
+ m_selection->SelectRow( row,
+ event.ControlDown(),
+ event.ShiftDown(),
+ event.AltDown(),
+ event.MetaDown() );
+ }
+
+ // default label to suppress warnings about "enumeration value
+ // 'xxx' not handled in switch
+ default:
+ break;
+ }
+ }
+ return;
+ }
+
+ 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 ( 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() )
+ {
+ if ( YToEdgeOfRow(y) < 0 )
+ {
+ row = YToRow(y);
+ SendEvent( wxEVT_GRID_LABEL_LEFT_DCLICK, row, -1, event );
+ }
+ }
+
+
+ // ------------ 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 ( !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 ( !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() )
+ {
+ m_isDragging = TRUE;
+
+ 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 )
+ {
+ m_selection->SelectCol( col,
+ event.ControlDown(),
+ event.ShiftDown(),
+ event.AltDown(),
+ event.MetaDown() );
+ }
+
+ // default label to suppress warnings about "enumeration value
+ // 'xxx' not handled in switch
+ default:
+ break;
+ }
+ }
+ return;
+ }
+
+ 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 ( 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() )
+ {
+ if ( XToEdgeOfCol(x) < 0 )
+ {
+ col = XToCol(x);
+ SendEvent( wxEVT_GRID_LABEL_LEFT_DCLICK, -1, col, event );
+ }
+ }
+
+
+ // ------------ 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 ( !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 ( !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 )
+ {
+ 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 );