]> git.saurik.com Git - wxWidgets.git/blob - src/generic/grid.cpp
a5c4a062487f20bb194be307e2e9dd0bcf935b25
[wxWidgets.git] / src / generic / grid.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: grid.cpp
3 // Purpose: wxGrid and related classes
4 // Author: Michael Bedward (based on code by Julian Smart, Robin Dunn)
5 // Modified by:
6 // Created: 1/08/1999
7 // RCS-ID: $Id$
8 // Copyright: (c) Michael Bedward (mbedward@ozemail.com.au)
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
11
12 #ifdef __GNUG__
13 #pragma implementation "grid.h"
14 #endif
15
16 // For compilers that support precompilation, includes "wx/wx.h".
17 #include "wx/wxprec.h"
18
19 #include "wx/defs.h"
20
21 #ifdef __BORLANDC__
22 #pragma hdrstop
23 #endif
24
25 #if !defined(wxUSE_NEW_GRID) || !(wxUSE_NEW_GRID)
26 #include "gridg.cpp"
27 #else
28
29 #ifndef WX_PRECOMP
30 #include "wx/utils.h"
31 #include "wx/dcclient.h"
32 #include "wx/settings.h"
33 #include "wx/log.h"
34 #include "wx/sizer.h"
35 #endif
36
37 #include "wx/generic/grid.h"
38
39 #ifndef DRAW_LINES
40 #define DRAW_LINES 1
41 #endif
42
43 //////////////////////////////////////////////////////////////////////
44
45 wxGridCellCoords wxGridNoCellCoords( -1, -1 );
46 wxRect wxGridNoCellRect( -1, -1, -1, -1 );
47
48 // this is a magic incantation which must be done!
49 #include "wx/arrimpl.cpp"
50
51 WX_DEFINE_OBJARRAY(wxGridCellCoordsArray)
52
53 // scroll line size
54 // TODO: fixed so far - make configurable later (and also different for x/y)
55 static const size_t GRID_SCROLL_LINE = 10;
56
57 //////////////////////////////////////////////////////////////////////
58 //
59 // Abstract base class for grid data (the model)
60 //
61 IMPLEMENT_ABSTRACT_CLASS( wxGridTableBase, wxObject )
62
63
64 wxGridTableBase::wxGridTableBase()
65 : wxObject()
66 {
67 m_view = (wxGrid *) NULL;
68 }
69
70 wxGridTableBase::~wxGridTableBase()
71 {
72 }
73
74
75 bool wxGridTableBase::InsertRows( size_t pos, size_t numRows )
76 {
77 wxLogWarning( wxT("Called grid table class function InsertRows(pos=%d, N=%d)\n"
78 "but your derived table class does not override this function"),
79 pos, numRows );
80
81 return FALSE;
82 }
83
84 bool wxGridTableBase::AppendRows( size_t numRows )
85 {
86 wxLogWarning( wxT("Called grid table class function AppendRows(N=%d)\n"
87 "but your derived table class does not override this function"),
88 numRows );
89
90 return FALSE;
91 }
92
93 bool wxGridTableBase::DeleteRows( size_t pos, size_t numRows )
94 {
95 wxLogWarning( wxT("Called grid table class function DeleteRows(pos=%d, N=%d)\n"
96 "but your derived table class does not override this function"),
97 pos, numRows );
98
99 return FALSE;
100 }
101
102 bool wxGridTableBase::InsertCols( size_t pos, size_t numCols )
103 {
104 wxLogWarning( wxT("Called grid table class function InsertCols(pos=%d, N=%d)\n"
105 "but your derived table class does not override this function"),
106 pos, numCols );
107
108 return FALSE;
109 }
110
111 bool wxGridTableBase::AppendCols( size_t numCols )
112 {
113 wxLogWarning( wxT("Called grid table class function AppendCols(N=%d)\n"
114 "but your derived table class does not override this function"),
115 numCols );
116
117 return FALSE;
118 }
119
120 bool wxGridTableBase::DeleteCols( size_t pos, size_t numCols )
121 {
122 wxLogWarning( wxT("Called grid table class function DeleteCols(pos=%d, N=%d)\n"
123 "but your derived table class does not override this function"),
124 pos, numCols );
125
126 return FALSE;
127 }
128
129
130 wxString wxGridTableBase::GetRowLabelValue( int row )
131 {
132 wxString s;
133 s << row;
134 return s;
135 }
136
137 wxString wxGridTableBase::GetColLabelValue( int col )
138 {
139 // default col labels are:
140 // cols 0 to 25 : A-Z
141 // cols 26 to 675 : AA-ZZ
142 // etc.
143
144 wxString s;
145 unsigned int i, n;
146 for ( n = 1; ; n++ )
147 {
148 s += (_T('A') + (wxChar)( col%26 ));
149 col = col/26 - 1;
150 if ( col < 0 ) break;
151 }
152
153 // reverse the string...
154 wxString s2;
155 for ( i = 0; i < n; i++ )
156 {
157 s2 += s[n-i-1];
158 }
159
160 return s2;
161 }
162
163
164
165 //////////////////////////////////////////////////////////////////////
166 //
167 // Message class for the grid table to send requests and notifications
168 // to the grid view
169 //
170
171 wxGridTableMessage::wxGridTableMessage()
172 {
173 m_table = (wxGridTableBase *) NULL;
174 m_id = -1;
175 m_comInt1 = -1;
176 m_comInt2 = -1;
177 }
178
179 wxGridTableMessage::wxGridTableMessage( wxGridTableBase *table, int id,
180 int commandInt1, int commandInt2 )
181 {
182 m_table = table;
183 m_id = id;
184 m_comInt1 = commandInt1;
185 m_comInt2 = commandInt2;
186 }
187
188
189
190 //////////////////////////////////////////////////////////////////////
191 //
192 // A basic grid table for string data. An object of this class will
193 // created by wxGrid if you don't specify an alternative table class.
194 //
195
196 WX_DEFINE_OBJARRAY(wxGridStringArray)
197
198 IMPLEMENT_DYNAMIC_CLASS( wxGridStringTable, wxGridTableBase )
199
200 wxGridStringTable::wxGridStringTable()
201 : wxGridTableBase()
202 {
203 }
204
205 wxGridStringTable::wxGridStringTable( int numRows, int numCols )
206 : wxGridTableBase()
207 {
208 int row, col;
209
210 m_data.Alloc( numRows );
211
212 wxArrayString sa;
213 sa.Alloc( numCols );
214 for ( col = 0; col < numCols; col++ )
215 {
216 sa.Add( wxEmptyString );
217 }
218
219 for ( row = 0; row < numRows; row++ )
220 {
221 m_data.Add( sa );
222 }
223 }
224
225 wxGridStringTable::~wxGridStringTable()
226 {
227 }
228
229 long wxGridStringTable::GetNumberRows()
230 {
231 return m_data.GetCount();
232 }
233
234 long wxGridStringTable::GetNumberCols()
235 {
236 if ( m_data.GetCount() > 0 )
237 return m_data[0].GetCount();
238 else
239 return 0;
240 }
241
242 wxString wxGridStringTable::GetValue( int row, int col )
243 {
244 // TODO: bounds checking
245 //
246 return m_data[row][col];
247 }
248
249 void wxGridStringTable::SetValue( int row, int col, const wxString& s )
250 {
251 // TODO: bounds checking
252 //
253 m_data[row][col] = s;
254 }
255
256 bool wxGridStringTable::IsEmptyCell( int row, int col )
257 {
258 // TODO: bounds checking
259 //
260 return (m_data[row][col] == wxEmptyString);
261 }
262
263
264 void wxGridStringTable::Clear()
265 {
266 int row, col;
267 int numRows, numCols;
268
269 numRows = m_data.GetCount();
270 if ( numRows > 0 )
271 {
272 numCols = m_data[0].GetCount();
273
274 for ( row = 0; row < numRows; row++ )
275 {
276 for ( col = 0; col < numCols; col++ )
277 {
278 m_data[row][col] = wxEmptyString;
279 }
280 }
281 }
282 }
283
284
285 bool wxGridStringTable::InsertRows( size_t pos, size_t numRows )
286 {
287 size_t row, col;
288
289 size_t curNumRows = m_data.GetCount();
290 size_t curNumCols = ( curNumRows > 0 ? m_data[0].GetCount() : 0 );
291
292 if ( pos >= curNumRows )
293 {
294 return AppendRows( numRows );
295 }
296
297 wxArrayString sa;
298 sa.Alloc( curNumCols );
299 for ( col = 0; col < curNumCols; col++ )
300 {
301 sa.Add( wxEmptyString );
302 }
303
304 for ( row = pos; row < pos + numRows; row++ )
305 {
306 m_data.Insert( sa, row );
307 }
308
309 if ( GetView() )
310 {
311 wxGridTableMessage msg( this,
312 wxGRIDTABLE_NOTIFY_ROWS_INSERTED,
313 pos,
314 numRows );
315
316 GetView()->ProcessTableMessage( msg );
317 }
318
319 return TRUE;
320 }
321
322 bool wxGridStringTable::AppendRows( size_t numRows )
323 {
324 size_t row, col;
325
326 size_t curNumRows = m_data.GetCount();
327 size_t curNumCols = ( curNumRows > 0 ? m_data[0].GetCount() : 0 );
328
329 wxArrayString sa;
330 if ( curNumCols > 0 )
331 {
332 sa.Alloc( curNumCols );
333 for ( col = 0; col < curNumCols; col++ )
334 {
335 sa.Add( wxEmptyString );
336 }
337 }
338
339 for ( row = 0; row < numRows; row++ )
340 {
341 m_data.Add( sa );
342 }
343
344 if ( GetView() )
345 {
346 wxGridTableMessage msg( this,
347 wxGRIDTABLE_NOTIFY_ROWS_APPENDED,
348 numRows );
349
350 GetView()->ProcessTableMessage( msg );
351 }
352
353 return TRUE;
354 }
355
356 bool wxGridStringTable::DeleteRows( size_t pos, size_t numRows )
357 {
358 size_t n;
359
360 size_t curNumRows = m_data.GetCount();
361
362 if ( pos >= curNumRows )
363 {
364 wxLogError( wxT("Called wxGridStringTable::DeleteRows(pos=%d, N=%d)...\n"
365 "Pos value is invalid for present table with %d rows"),
366 pos, numRows, curNumRows );
367 return FALSE;
368 }
369
370 if ( numRows > curNumRows - pos )
371 {
372 numRows = curNumRows - pos;
373 }
374
375 if ( numRows >= curNumRows )
376 {
377 m_data.Empty(); // don't release memory just yet
378 }
379 else
380 {
381 for ( n = 0; n < numRows; n++ )
382 {
383 m_data.Remove( pos );
384 }
385 }
386
387 if ( GetView() )
388 {
389 wxGridTableMessage msg( this,
390 wxGRIDTABLE_NOTIFY_ROWS_DELETED,
391 pos,
392 numRows );
393
394 GetView()->ProcessTableMessage( msg );
395 }
396
397 return TRUE;
398 }
399
400 bool wxGridStringTable::InsertCols( size_t pos, size_t numCols )
401 {
402 size_t row, col;
403
404 size_t curNumRows = m_data.GetCount();
405 size_t curNumCols = ( curNumRows > 0 ? m_data[0].GetCount() : 0 );
406
407 if ( pos >= curNumCols )
408 {
409 return AppendCols( numCols );
410 }
411
412 for ( row = 0; row < curNumRows; row++ )
413 {
414 for ( col = pos; col < pos + numCols; col++ )
415 {
416 m_data[row].Insert( wxEmptyString, col );
417 }
418 }
419
420 if ( GetView() )
421 {
422 wxGridTableMessage msg( this,
423 wxGRIDTABLE_NOTIFY_COLS_INSERTED,
424 pos,
425 numCols );
426
427 GetView()->ProcessTableMessage( msg );
428 }
429
430 return TRUE;
431 }
432
433 bool wxGridStringTable::AppendCols( size_t numCols )
434 {
435 size_t row, n;
436
437 size_t curNumRows = m_data.GetCount();
438 if ( !curNumRows )
439 {
440 // TODO: something better than this ?
441 //
442 wxLogError( wxT("Unable to append cols to a grid table with no rows.\n"
443 "Call AppendRows() first") );
444 return FALSE;
445 }
446
447 for ( row = 0; row < curNumRows; row++ )
448 {
449 for ( n = 0; n < numCols; n++ )
450 {
451 m_data[row].Add( wxEmptyString );
452 }
453 }
454
455 if ( GetView() )
456 {
457 wxGridTableMessage msg( this,
458 wxGRIDTABLE_NOTIFY_COLS_APPENDED,
459 numCols );
460
461 GetView()->ProcessTableMessage( msg );
462 }
463
464 return TRUE;
465 }
466
467 bool wxGridStringTable::DeleteCols( size_t pos, size_t numCols )
468 {
469 size_t row, n;
470
471 size_t curNumRows = m_data.GetCount();
472 size_t curNumCols = ( curNumRows > 0 ? m_data[0].GetCount() : 0 );
473
474 if ( pos >= curNumCols )
475 {
476 wxLogError( wxT("Called wxGridStringTable::DeleteCols(pos=%d, N=%d)...\n"
477 "Pos value is invalid for present table with %d cols"),
478 pos, numCols, curNumCols );
479 return FALSE;
480 }
481
482 if ( numCols > curNumCols - pos )
483 {
484 numCols = curNumCols - pos;
485 }
486
487 for ( row = 0; row < curNumRows; row++ )
488 {
489 if ( numCols >= curNumCols )
490 {
491 m_data[row].Clear();
492 }
493 else
494 {
495 for ( n = 0; n < numCols; n++ )
496 {
497 m_data[row].Remove( pos );
498 }
499 }
500 }
501
502 if ( GetView() )
503 {
504 wxGridTableMessage msg( this,
505 wxGRIDTABLE_NOTIFY_COLS_DELETED,
506 pos,
507 numCols );
508
509 GetView()->ProcessTableMessage( msg );
510 }
511
512 return TRUE;
513 }
514
515 wxString wxGridStringTable::GetRowLabelValue( int row )
516 {
517 if ( row > (int)(m_rowLabels.GetCount()) - 1 )
518 {
519 // using default label
520 //
521 return wxGridTableBase::GetRowLabelValue( row );
522 }
523 else
524 {
525 return m_rowLabels[ row ];
526 }
527 }
528
529 wxString wxGridStringTable::GetColLabelValue( int col )
530 {
531 if ( col > (int)(m_colLabels.GetCount()) - 1 )
532 {
533 // using default label
534 //
535 return wxGridTableBase::GetColLabelValue( col );
536 }
537 else
538 {
539 return m_colLabels[ col ];
540 }
541 }
542
543 void wxGridStringTable::SetRowLabelValue( int row, const wxString& value )
544 {
545 if ( row > (int)(m_rowLabels.GetCount()) - 1 )
546 {
547 int n = m_rowLabels.GetCount();
548 int i;
549 for ( i = n; i <= row; i++ )
550 {
551 m_rowLabels.Add( wxGridTableBase::GetRowLabelValue(i) );
552 }
553 }
554
555 m_rowLabels[row] = value;
556 }
557
558 void wxGridStringTable::SetColLabelValue( int col, const wxString& value )
559 {
560 if ( col > (int)(m_colLabels.GetCount()) - 1 )
561 {
562 int n = m_colLabels.GetCount();
563 int i;
564 for ( i = n; i <= col; i++ )
565 {
566 m_colLabels.Add( wxGridTableBase::GetColLabelValue(i) );
567 }
568 }
569
570 m_colLabels[col] = value;
571 }
572
573
574
575
576 //////////////////////////////////////////////////////////////////////
577
578 IMPLEMENT_DYNAMIC_CLASS( wxGridTextCtrl, wxTextCtrl )
579
580 BEGIN_EVENT_TABLE( wxGridTextCtrl, wxTextCtrl )
581 EVT_KEY_DOWN( wxGridTextCtrl::OnKeyDown )
582 END_EVENT_TABLE()
583
584
585 wxGridTextCtrl::wxGridTextCtrl( wxWindow *par,
586 wxGrid *grid,
587 bool isCellControl,
588 wxWindowID id,
589 const wxString& value,
590 const wxPoint& pos,
591 const wxSize& size,
592 long style )
593 : wxTextCtrl( par, id, value, pos, size, style )
594 {
595 m_grid = grid;
596 m_isCellControl = isCellControl;
597 }
598
599
600 void wxGridTextCtrl::OnKeyDown( wxKeyEvent& event )
601 {
602 switch ( event.KeyCode() )
603 {
604 case WXK_ESCAPE:
605 m_grid->SetEditControlValue( startValue );
606 SetInsertionPointEnd();
607 break;
608
609 case WXK_UP:
610 case WXK_DOWN:
611 case WXK_LEFT:
612 case WXK_RIGHT:
613 case WXK_PRIOR:
614 case WXK_NEXT:
615 case WXK_SPACE:
616 if ( m_isCellControl )
617 {
618 // send the event to the parent grid, skipping the
619 // event if nothing happens
620 //
621 event.Skip( m_grid->ProcessEvent( event ) );
622 }
623 else
624 {
625 // default text control response within the top edit
626 // control
627 //
628 event.Skip();
629 }
630 break;
631
632 case WXK_RETURN:
633 if ( m_isCellControl )
634 {
635 if ( !m_grid->ProcessEvent( event ) )
636 {
637 #if defined(__WXMOTIF__) || defined(__WXGTK__)
638 // wxMotif needs a little extra help...
639 //
640 int pos = GetInsertionPoint();
641 wxString s( GetValue() );
642 s = s.Left(pos) + "\n" + s.Mid(pos);
643 SetValue(s);
644 SetInsertionPoint( pos );
645 #else
646 // the other ports can handle a Return key press
647 //
648 event.Skip();
649 #endif
650 }
651 }
652 break;
653
654 case WXK_HOME:
655 case WXK_END:
656 if ( m_isCellControl )
657 {
658 // send the event to the parent grid, skipping the
659 // event if nothing happens
660 //
661 event.Skip( m_grid->ProcessEvent( event ) );
662 }
663 else
664 {
665 // default text control response within the top edit
666 // control
667 //
668 event.Skip();
669 }
670 break;
671
672 default:
673 event.Skip();
674 }
675 }
676
677 void wxGridTextCtrl::SetStartValue( const wxString& s )
678 {
679 startValue = s;
680 wxTextCtrl::SetValue(s);
681 }
682
683
684
685 //////////////////////////////////////////////////////////////////////
686
687 IMPLEMENT_DYNAMIC_CLASS( wxGridRowLabelWindow, wxWindow )
688
689 BEGIN_EVENT_TABLE( wxGridRowLabelWindow, wxWindow )
690 EVT_PAINT( wxGridRowLabelWindow::OnPaint )
691 EVT_MOUSE_EVENTS( wxGridRowLabelWindow::OnMouseEvent )
692 EVT_KEY_DOWN( wxGridRowLabelWindow::OnKeyDown )
693 END_EVENT_TABLE()
694
695 wxGridRowLabelWindow::wxGridRowLabelWindow( wxGrid *parent,
696 wxWindowID id,
697 const wxPoint &pos, const wxSize &size )
698 : wxWindow( parent, id, pos, size )
699 {
700 m_owner = parent;
701 }
702
703 void wxGridRowLabelWindow::OnPaint( wxPaintEvent &event )
704 {
705 wxPaintDC dc(this);
706
707 // NO - don't do this because it will set both the x and y origin
708 // coords to match the parent scrolled window and we just want to
709 // set the y coord - MB
710 //
711 // m_owner->PrepareDC( dc );
712
713 wxCoord x, y;
714 m_owner->CalcUnscrolledPosition( 0, 0, &x, &y );
715 dc.SetDeviceOrigin( 0, -y );
716
717 m_owner->CalcRowLabelsExposed( GetUpdateRegion() );
718 m_owner->DrawRowLabels( dc );
719 }
720
721
722 void wxGridRowLabelWindow::OnMouseEvent( wxMouseEvent& event )
723 {
724 m_owner->ProcessRowLabelMouseEvent( event );
725 }
726
727
728 // This seems to be required for wxMotif otherwise the mouse
729 // cursor must be in the cell edit control to get key events
730 //
731 void wxGridRowLabelWindow::OnKeyDown( wxKeyEvent& event )
732 {
733 if ( !m_owner->ProcessEvent( event ) ) event.Skip();
734 }
735
736
737
738 //////////////////////////////////////////////////////////////////////
739
740 IMPLEMENT_DYNAMIC_CLASS( wxGridColLabelWindow, wxWindow )
741
742 BEGIN_EVENT_TABLE( wxGridColLabelWindow, wxWindow )
743 EVT_PAINT( wxGridColLabelWindow::OnPaint )
744 EVT_MOUSE_EVENTS( wxGridColLabelWindow::OnMouseEvent )
745 EVT_KEY_DOWN( wxGridColLabelWindow::OnKeyDown )
746 END_EVENT_TABLE()
747
748 wxGridColLabelWindow::wxGridColLabelWindow( wxGrid *parent,
749 wxWindowID id,
750 const wxPoint &pos, const wxSize &size )
751 : wxWindow( parent, id, pos, size )
752 {
753 m_owner = parent;
754 }
755
756 void wxGridColLabelWindow::OnPaint( wxPaintEvent &event )
757 {
758 wxPaintDC dc(this);
759
760 // NO - don't do this because it will set both the x and y origin
761 // coords to match the parent scrolled window and we just want to
762 // set the x coord - MB
763 //
764 // m_owner->PrepareDC( dc );
765
766 wxCoord x, y;
767 m_owner->CalcUnscrolledPosition( 0, 0, &x, &y );
768 dc.SetDeviceOrigin( -x, 0 );
769
770 m_owner->CalcColLabelsExposed( GetUpdateRegion() );
771 m_owner->DrawColLabels( dc );
772 }
773
774
775 void wxGridColLabelWindow::OnMouseEvent( wxMouseEvent& event )
776 {
777 m_owner->ProcessColLabelMouseEvent( event );
778 }
779
780
781 // This seems to be required for wxMotif otherwise the mouse
782 // cursor must be in the cell edit control to get key events
783 //
784 void wxGridColLabelWindow::OnKeyDown( wxKeyEvent& event )
785 {
786 if ( !m_owner->ProcessEvent( event ) ) event.Skip();
787 }
788
789
790
791 //////////////////////////////////////////////////////////////////////
792
793 IMPLEMENT_DYNAMIC_CLASS( wxGridCornerLabelWindow, wxWindow )
794
795 BEGIN_EVENT_TABLE( wxGridCornerLabelWindow, wxWindow )
796 EVT_MOUSE_EVENTS( wxGridCornerLabelWindow::OnMouseEvent )
797 EVT_PAINT( wxGridCornerLabelWindow::OnPaint)
798 EVT_KEY_DOWN( wxGridCornerLabelWindow::OnKeyDown )
799 END_EVENT_TABLE()
800
801 wxGridCornerLabelWindow::wxGridCornerLabelWindow( wxGrid *parent,
802 wxWindowID id,
803 const wxPoint &pos, const wxSize &size )
804 : wxWindow( parent, id, pos, size )
805 {
806 m_owner = parent;
807 }
808
809 void wxGridCornerLabelWindow::OnPaint( wxPaintEvent& WXUNUSED(event) )
810 {
811 wxPaintDC dc(this);
812
813 int client_height = 0;
814 int client_width = 0;
815 GetClientSize( &client_width, &client_height );
816
817 dc.SetPen( *wxBLACK_PEN );
818 dc.DrawLine( client_width-1, client_height-1, client_width-1, 0 );
819 dc.DrawLine( client_width-1, client_height-1, 0, client_height-1 );
820
821 dc.SetPen( *wxWHITE_PEN );
822 dc.DrawLine( 0, 0, client_width, 0 );
823 dc.DrawLine( 0, 0, 0, client_height );
824 }
825
826
827 void wxGridCornerLabelWindow::OnMouseEvent( wxMouseEvent& event )
828 {
829 m_owner->ProcessCornerLabelMouseEvent( event );
830 }
831
832
833 // This seems to be required for wxMotif otherwise the mouse
834 // cursor must be in the cell edit control to get key events
835 //
836 void wxGridCornerLabelWindow::OnKeyDown( wxKeyEvent& event )
837 {
838 if ( !m_owner->ProcessEvent( event ) ) event.Skip();
839 }
840
841
842
843 //////////////////////////////////////////////////////////////////////
844
845 IMPLEMENT_DYNAMIC_CLASS( wxGridWindow, wxPanel )
846
847 BEGIN_EVENT_TABLE( wxGridWindow, wxPanel )
848 EVT_PAINT( wxGridWindow::OnPaint )
849 EVT_MOUSE_EVENTS( wxGridWindow::OnMouseEvent )
850 EVT_KEY_DOWN( wxGridWindow::OnKeyDown )
851 END_EVENT_TABLE()
852
853 wxGridWindow::wxGridWindow( wxGrid *parent,
854 wxGridRowLabelWindow *rowLblWin,
855 wxGridColLabelWindow *colLblWin,
856 wxWindowID id, const wxPoint &pos, const wxSize &size )
857 : wxPanel( parent, id, pos, size, wxSUNKEN_BORDER, "grid window" )
858 {
859 m_owner = parent;
860 m_rowLabelWin = rowLblWin;
861 m_colLabelWin = colLblWin;
862
863 SetBackgroundColour( "WHITE" );
864 }
865
866
867 wxGridWindow::~wxGridWindow()
868 {
869 }
870
871
872 void wxGridWindow::OnPaint( wxPaintEvent &WXUNUSED(event) )
873 {
874 wxPaintDC dc( this );
875 m_owner->PrepareDC( dc );
876 wxRegion reg = GetUpdateRegion();
877 m_owner->CalcCellsExposed( reg );
878 m_owner->DrawGridCellArea( dc );
879 #if DRAW_LINES
880 m_owner->DrawAllGridLines( dc, reg );
881 #endif
882 }
883
884
885 void wxGridWindow::ScrollWindow( int dx, int dy, const wxRect *rect )
886 {
887 wxPanel::ScrollWindow( dx, dy, rect );
888 m_rowLabelWin->ScrollWindow( 0, dy, rect );
889 m_colLabelWin->ScrollWindow( dx, 0, rect );
890 }
891
892
893 void wxGridWindow::OnMouseEvent( wxMouseEvent& event )
894 {
895 m_owner->ProcessGridCellMouseEvent( event );
896 }
897
898
899 // This seems to be required for wxMotif otherwise the mouse
900 // cursor must be in the cell edit control to get key events
901 //
902 void wxGridWindow::OnKeyDown( wxKeyEvent& event )
903 {
904 if ( !m_owner->ProcessEvent( event ) ) event.Skip();
905 }
906
907
908
909 //////////////////////////////////////////////////////////////////////
910
911 IMPLEMENT_DYNAMIC_CLASS( wxGrid, wxScrolledWindow )
912
913 BEGIN_EVENT_TABLE( wxGrid, wxScrolledWindow )
914 EVT_PAINT( wxGrid::OnPaint )
915 EVT_SIZE( wxGrid::OnSize )
916 EVT_KEY_DOWN( wxGrid::OnKeyDown )
917 END_EVENT_TABLE()
918
919 wxGrid::wxGrid( wxWindow *parent,
920 wxWindowID id,
921 const wxPoint& pos,
922 const wxSize& size,
923 long style,
924 const wxString& name )
925 : wxScrolledWindow( parent, id, pos, size, style, name )
926 {
927 Create();
928 }
929
930
931 wxGrid::~wxGrid()
932 {
933 delete m_table;
934 }
935
936
937 //
938 // ----- internal init and update functions
939 //
940
941 void wxGrid::Create()
942 {
943 int colLblH = WXGRID_DEFAULT_COL_LABEL_HEIGHT;
944 int rowLblW = WXGRID_DEFAULT_ROW_LABEL_WIDTH;
945
946 m_rowLabelWin = new wxGridRowLabelWindow( this,
947 -1,
948 wxDefaultPosition,
949 wxSize(rowLblW,-1) );
950
951 m_colLabelWin = new wxGridColLabelWindow( this,
952 -1,
953 wxDefaultPosition,
954 wxSize(-1, colLblH ) );
955
956 m_cornerLabelWin = new wxGridCornerLabelWindow( this,
957 -1,
958 wxDefaultPosition,
959 wxSize(rowLblW, colLblH ) );
960
961 m_gridWin = new wxGridWindow( this,
962 m_rowLabelWin,
963 m_colLabelWin,
964 -1,
965 wxDefaultPosition,
966 wxDefaultSize );
967
968 SetTargetWindow( m_gridWin );
969
970 m_mainSizer = new wxBoxSizer( wxVERTICAL );
971
972 m_topSizer = new wxBoxSizer( wxHORIZONTAL );
973 m_topSizer->Add( m_cornerLabelWin, 0 );
974 m_topSizer->Add( m_colLabelWin, 1 );
975
976 m_mainSizer->Add( m_topSizer, 0, wxEXPAND );
977
978 m_middleSizer = new wxBoxSizer( wxHORIZONTAL );
979 m_middleSizer->Add( m_rowLabelWin, 0, wxEXPAND );
980 m_middleSizer->Add( m_gridWin, 1, wxEXPAND );
981
982 m_mainSizer->Add( m_middleSizer, 1, wxEXPAND );
983
984 SetAutoLayout( TRUE );
985 SetSizer( m_mainSizer );
986 }
987
988
989 bool wxGrid::CreateGrid( int numRows, int numCols )
990 {
991 if ( m_created )
992 {
993 wxLogError( wxT("wxGrid::CreateGrid(numRows, numCols) called more than once") );
994 return FALSE;
995 }
996 else
997 {
998 m_numRows = numRows;
999 m_numCols = numCols;
1000
1001 m_table = new wxGridStringTable( m_numRows, m_numCols );
1002 m_table->SetView( this );
1003 Init();
1004 m_created = TRUE;
1005 }
1006
1007 return m_created;
1008 }
1009
1010
1011 void wxGrid::Init()
1012 {
1013 int i;
1014
1015 if ( m_numRows <= 0 )
1016 m_numRows = WXGRID_DEFAULT_NUMBER_ROWS;
1017
1018 if ( m_numCols <= 0 )
1019 m_numCols = WXGRID_DEFAULT_NUMBER_COLS;
1020
1021 m_rowLabelWidth = WXGRID_DEFAULT_ROW_LABEL_WIDTH;
1022 m_colLabelHeight = WXGRID_DEFAULT_COL_LABEL_HEIGHT;
1023
1024 if ( m_rowLabelWin )
1025 {
1026 m_labelBackgroundColour = m_rowLabelWin->GetBackgroundColour();
1027 }
1028 else
1029 {
1030 m_labelBackgroundColour = wxColour( _T("WHITE") );
1031 }
1032
1033 m_labelTextColour = wxColour( _T("BLACK") );
1034
1035 // TODO: something better than this ?
1036 //
1037 m_labelFont = this->GetFont();
1038 m_labelFont.SetWeight( m_labelFont.GetWeight() + 2 );
1039
1040 m_rowLabelHorizAlign = wxLEFT;
1041 m_rowLabelVertAlign = wxCENTRE;
1042
1043 m_colLabelHorizAlign = wxCENTRE;
1044 m_colLabelVertAlign = wxTOP;
1045
1046 m_defaultColWidth = WXGRID_DEFAULT_COL_WIDTH;
1047 m_defaultRowHeight = m_gridWin->GetCharHeight();
1048
1049 #if defined(__WXMOTIF__) || defined(__WXGTK__) // see also text ctrl sizing in ShowCellEditControl()
1050 m_defaultRowHeight += 8;
1051 #else
1052 m_defaultRowHeight += 4;
1053 #endif
1054
1055 m_rowHeights.Alloc( m_numRows );
1056 m_rowBottoms.Alloc( m_numRows );
1057 int rowBottom = 0;
1058 for ( i = 0; i < m_numRows; i++ )
1059 {
1060 m_rowHeights.Add( m_defaultRowHeight );
1061 rowBottom += m_defaultRowHeight;
1062 m_rowBottoms.Add( rowBottom );
1063 }
1064
1065 m_colWidths.Alloc( m_numCols );
1066 m_colRights.Alloc( m_numCols );
1067 int colRight = 0;
1068 for ( i = 0; i < m_numCols; i++ )
1069 {
1070 m_colWidths.Add( m_defaultColWidth );
1071 colRight += m_defaultColWidth;
1072 m_colRights.Add( colRight );
1073 }
1074
1075 // TODO: improve this ?
1076 //
1077 m_defaultCellFont = this->GetFont();
1078
1079 m_gridLineColour = wxColour( 128, 128, 255 );
1080 m_gridLinesEnabled = TRUE;
1081
1082 m_cursorMode = WXGRID_CURSOR_SELECT_CELL;
1083 m_dragLastPos = -1;
1084 m_dragRowOrCol = -1;
1085 m_isDragging = FALSE;
1086
1087 m_rowResizeCursor = wxCursor( wxCURSOR_SIZENS );
1088 m_colResizeCursor = wxCursor( wxCURSOR_SIZEWE );
1089
1090 m_currentCellCoords = wxGridNoCellCoords;
1091
1092 m_selectedTopLeft = wxGridNoCellCoords;
1093 m_selectedBottomRight = wxGridNoCellCoords;
1094
1095 m_editable = TRUE; // default for whole grid
1096
1097 m_inOnKeyDown = FALSE;
1098 m_batchCount = 0;
1099
1100 // TODO: extend this to other types of controls
1101 //
1102 m_cellEditCtrl = new wxGridTextCtrl( m_gridWin,
1103 this,
1104 TRUE,
1105 wxGRID_CELLCTRL,
1106 "",
1107 wxPoint(1,1),
1108 wxSize(1,1)
1109 #if defined(__WXMSW__)
1110 , wxTE_MULTILINE | wxTE_NO_VSCROLL
1111 #endif
1112 );
1113
1114 m_cellEditCtrl->Show( FALSE );
1115 m_cellEditCtrlEnabled = TRUE;
1116 m_editCtrlType = wxGRID_TEXTCTRL;
1117 }
1118
1119
1120 void wxGrid::CalcDimensions()
1121 {
1122 int cw, ch;
1123 GetClientSize( &cw, &ch );
1124
1125 if ( m_numRows > 0 && m_numCols > 0 )
1126 {
1127 int right = m_colRights[ m_numCols-1 ] + 20;
1128 int bottom = m_rowBottoms[ m_numRows-1 ] + 20;
1129
1130 // TODO: restore the scroll position that we had before sizing
1131 //
1132 int x, y;
1133 GetViewStart( &x, &y );
1134 SetScrollbars( GRID_SCROLL_LINE, GRID_SCROLL_LINE,
1135 right/GRID_SCROLL_LINE, bottom/GRID_SCROLL_LINE,
1136 x, y );
1137 }
1138 }
1139
1140
1141 // this is called when the grid table sends a message to say that it
1142 // has been redimensioned
1143 //
1144 bool wxGrid::Redimension( wxGridTableMessage& msg )
1145 {
1146 int i;
1147
1148 switch ( msg.GetId() )
1149 {
1150 case wxGRIDTABLE_NOTIFY_ROWS_INSERTED:
1151 {
1152 size_t pos = msg.GetCommandInt();
1153 int numRows = msg.GetCommandInt2();
1154 for ( i = 0; i < numRows; i++ )
1155 {
1156 m_rowHeights.Insert( m_defaultRowHeight, pos );
1157 m_rowBottoms.Insert( 0, pos );
1158 }
1159 m_numRows += numRows;
1160
1161 int bottom = 0;
1162 if ( pos > 0 ) bottom = m_rowBottoms[pos-1];
1163
1164 for ( i = pos; i < m_numRows; i++ )
1165 {
1166 bottom += m_rowHeights[i];
1167 m_rowBottoms[i] = bottom;
1168 }
1169 CalcDimensions();
1170 }
1171 return TRUE;
1172
1173 case wxGRIDTABLE_NOTIFY_ROWS_APPENDED:
1174 {
1175 int numRows = msg.GetCommandInt();
1176 for ( i = 0; i < numRows; i++ )
1177 {
1178 m_rowHeights.Add( m_defaultRowHeight );
1179 m_rowBottoms.Add( 0 );
1180 }
1181
1182 int oldNumRows = m_numRows;
1183 m_numRows += numRows;
1184
1185 int bottom = 0;
1186 if ( oldNumRows > 0 ) bottom = m_rowBottoms[oldNumRows-1];
1187
1188 for ( i = oldNumRows; i < m_numRows; i++ )
1189 {
1190 bottom += m_rowHeights[i];
1191 m_rowBottoms[i] = bottom;
1192 }
1193 CalcDimensions();
1194 }
1195 return TRUE;
1196
1197 case wxGRIDTABLE_NOTIFY_ROWS_DELETED:
1198 {
1199 size_t pos = msg.GetCommandInt();
1200 int numRows = msg.GetCommandInt2();
1201 for ( i = 0; i < numRows; i++ )
1202 {
1203 m_rowHeights.Remove( pos );
1204 m_rowBottoms.Remove( pos );
1205 }
1206 m_numRows -= numRows;
1207
1208 if ( !m_numRows )
1209 {
1210 m_numCols = 0;
1211 m_colWidths.Clear();
1212 m_colRights.Clear();
1213 m_currentCellCoords = wxGridNoCellCoords;
1214 }
1215 else
1216 {
1217 if ( m_currentCellCoords.GetRow() >= m_numRows )
1218 m_currentCellCoords.Set( 0, 0 );
1219
1220 int h = 0;
1221 for ( i = 0; i < m_numRows; i++ )
1222 {
1223 h += m_rowHeights[i];
1224 m_rowBottoms[i] = h;
1225 }
1226 }
1227
1228 CalcDimensions();
1229 }
1230 return TRUE;
1231
1232 case wxGRIDTABLE_NOTIFY_COLS_INSERTED:
1233 {
1234 size_t pos = msg.GetCommandInt();
1235 int numCols = msg.GetCommandInt2();
1236 for ( i = 0; i < numCols; i++ )
1237 {
1238 m_colWidths.Insert( m_defaultColWidth, pos );
1239 m_colRights.Insert( 0, pos );
1240 }
1241 m_numCols += numCols;
1242
1243 int right = 0;
1244 if ( pos > 0 ) right = m_colRights[pos-1];
1245
1246 for ( i = pos; i < m_numCols; i++ )
1247 {
1248 right += m_colWidths[i];
1249 m_colRights[i] = right;
1250 }
1251 CalcDimensions();
1252 }
1253 return TRUE;
1254
1255 case wxGRIDTABLE_NOTIFY_COLS_APPENDED:
1256 {
1257 int numCols = msg.GetCommandInt();
1258 for ( i = 0; i < numCols; i++ )
1259 {
1260 m_colWidths.Add( m_defaultColWidth );
1261 m_colRights.Add( 0 );
1262 }
1263
1264 int oldNumCols = m_numCols;
1265 m_numCols += numCols;
1266
1267 int right = 0;
1268 if ( oldNumCols > 0 ) right = m_colRights[oldNumCols-1];
1269
1270 for ( i = oldNumCols; i < m_numCols; i++ )
1271 {
1272 right += m_colWidths[i];
1273 m_colRights[i] = right;
1274 }
1275 CalcDimensions();
1276 }
1277 return TRUE;
1278
1279 case wxGRIDTABLE_NOTIFY_COLS_DELETED:
1280 {
1281 size_t pos = msg.GetCommandInt();
1282 int numCols = msg.GetCommandInt2();
1283 for ( i = 0; i < numCols; i++ )
1284 {
1285 m_colWidths.Remove( pos );
1286 m_colRights.Remove( pos );
1287 }
1288 m_numCols -= numCols;
1289
1290 if ( !m_numCols )
1291 {
1292 #if 0 // leave the row alone here so that AppendCols will work subsequently
1293 m_numRows = 0;
1294 m_rowHeights.Clear();
1295 m_rowBottoms.Clear();
1296 #endif
1297 m_currentCellCoords = wxGridNoCellCoords;
1298 }
1299 else
1300 {
1301 if ( m_currentCellCoords.GetCol() >= m_numCols )
1302 m_currentCellCoords.Set( 0, 0 );
1303
1304 int w = 0;
1305 for ( i = 0; i < m_numCols; i++ )
1306 {
1307 w += m_colWidths[i];
1308 m_colRights[i] = w;
1309 }
1310 }
1311 CalcDimensions();
1312 }
1313 return TRUE;
1314 }
1315
1316 return FALSE;
1317 }
1318
1319
1320 void wxGrid::CalcRowLabelsExposed( wxRegion& reg )
1321 {
1322 wxRegionIterator iter( reg );
1323 wxRect r;
1324
1325 m_rowLabelsExposed.Empty();
1326
1327 int top, bottom;
1328 while ( iter )
1329 {
1330 r = iter.GetRect();
1331
1332 // TODO: remove this when we can...
1333 // There is a bug in wxMotif that gives garbage update
1334 // rectangles if you jump-scroll a long way by clicking the
1335 // scrollbar with middle button. This is a work-around
1336 //
1337 #if defined(__WXMOTIF__)
1338 int cw, ch;
1339 m_gridWin->GetClientSize( &cw, &ch );
1340 if ( r.GetTop() > ch ) r.SetTop( 0 );
1341 r.SetBottom( wxMin( r.GetBottom(), ch ) );
1342 #endif
1343
1344 // logical bounds of update region
1345 //
1346 int dummy;
1347 CalcUnscrolledPosition( 0, r.GetTop(), &dummy, &top );
1348 CalcUnscrolledPosition( 0, r.GetBottom(), &dummy, &bottom );
1349
1350 // find the row labels within these bounds
1351 //
1352 int row;
1353 int rowTop;
1354 for ( row = 0; row < m_numRows; row++ )
1355 {
1356 if ( m_rowBottoms[row] < top ) continue;
1357
1358 rowTop = m_rowBottoms[row] - m_rowHeights[row];
1359 if ( rowTop > bottom ) break;
1360
1361 m_rowLabelsExposed.Add( row );
1362 }
1363
1364 iter++ ;
1365 }
1366 }
1367
1368
1369 void wxGrid::CalcColLabelsExposed( wxRegion& reg )
1370 {
1371 wxRegionIterator iter( reg );
1372 wxRect r;
1373
1374 m_colLabelsExposed.Empty();
1375
1376 int left, right;
1377 while ( iter )
1378 {
1379 r = iter.GetRect();
1380
1381 // TODO: remove this when we can...
1382 // There is a bug in wxMotif that gives garbage update
1383 // rectangles if you jump-scroll a long way by clicking the
1384 // scrollbar with middle button. This is a work-around
1385 //
1386 #if defined(__WXMOTIF__)
1387 int cw, ch;
1388 m_gridWin->GetClientSize( &cw, &ch );
1389 if ( r.GetLeft() > cw ) r.SetLeft( 0 );
1390 r.SetRight( wxMin( r.GetRight(), cw ) );
1391 #endif
1392
1393 // logical bounds of update region
1394 //
1395 int dummy;
1396 CalcUnscrolledPosition( r.GetLeft(), 0, &left, &dummy );
1397 CalcUnscrolledPosition( r.GetRight(), 0, &right, &dummy );
1398
1399 // find the cells within these bounds
1400 //
1401 int col;
1402 int colLeft;
1403 for ( col = 0; col < m_numCols; col++ )
1404 {
1405 if ( m_colRights[col] < left ) continue;
1406
1407 colLeft = m_colRights[col] - m_colWidths[col];
1408 if ( colLeft > right ) break;
1409
1410 m_colLabelsExposed.Add( col );
1411 }
1412
1413 iter++ ;
1414 }
1415 }
1416
1417
1418 void wxGrid::CalcCellsExposed( wxRegion& reg )
1419 {
1420 wxRegionIterator iter( reg );
1421 wxRect r;
1422
1423 m_cellsExposed.Empty();
1424 m_rowsExposed.Empty();
1425 m_colsExposed.Empty();
1426
1427 int left, top, right, bottom;
1428 while ( iter )
1429 {
1430 r = iter.GetRect();
1431
1432 // TODO: remove this when we can...
1433 // There is a bug in wxMotif that gives garbage update
1434 // rectangles if you jump-scroll a long way by clicking the
1435 // scrollbar with middle button. This is a work-around
1436 //
1437 #if defined(__WXMOTIF__)
1438 int cw, ch;
1439 m_gridWin->GetClientSize( &cw, &ch );
1440 if ( r.GetTop() > ch ) r.SetTop( 0 );
1441 if ( r.GetLeft() > cw ) r.SetLeft( 0 );
1442 r.SetRight( wxMin( r.GetRight(), cw ) );
1443 r.SetBottom( wxMin( r.GetBottom(), ch ) );
1444 #endif
1445
1446 // logical bounds of update region
1447 //
1448 CalcUnscrolledPosition( r.GetLeft(), r.GetTop(), &left, &top );
1449 CalcUnscrolledPosition( r.GetRight(), r.GetBottom(), &right, &bottom );
1450
1451 // find the cells within these bounds
1452 //
1453 int row, col;
1454 int colLeft, rowTop;
1455 for ( row = 0; row < m_numRows; row++ )
1456 {
1457 if ( m_rowBottoms[row] < top ) continue;
1458
1459 rowTop = m_rowBottoms[row] - m_rowHeights[row];
1460 if ( rowTop > bottom ) break;
1461
1462 m_rowsExposed.Add( row );
1463
1464 for ( col = 0; col < m_numCols; col++ )
1465 {
1466 if ( m_colRights[col] < left ) continue;
1467
1468 colLeft = m_colRights[col] - m_colWidths[col];
1469 if ( colLeft > right ) break;
1470
1471 if ( m_colsExposed.Index( col ) == wxNOT_FOUND ) m_colsExposed.Add( col );
1472 m_cellsExposed.Add( wxGridCellCoords( row, col ) );
1473 }
1474 }
1475
1476 iter++ ;
1477 }
1478 }
1479
1480
1481 void wxGrid::ProcessRowLabelMouseEvent( wxMouseEvent& event )
1482 {
1483 int x, y, row;
1484 wxPoint pos( event.GetPosition() );
1485 CalcUnscrolledPosition( pos.x, pos.y, &x, &y );
1486
1487 if ( event.Dragging() )
1488 {
1489 m_isDragging = TRUE;
1490
1491 if ( event.LeftIsDown() )
1492 {
1493 switch( m_cursorMode )
1494 {
1495 case WXGRID_CURSOR_RESIZE_ROW:
1496 {
1497 int cw, ch, left, dummy;
1498 m_gridWin->GetClientSize( &cw, &ch );
1499 CalcUnscrolledPosition( 0, 0, &left, &dummy );
1500
1501 wxClientDC dc( m_gridWin );
1502 PrepareDC( dc );
1503 dc.SetLogicalFunction(wxINVERT);
1504 if ( m_dragLastPos >= 0 )
1505 {
1506 dc.DrawLine( left, m_dragLastPos, left+cw, m_dragLastPos );
1507 }
1508 dc.DrawLine( left, y, left+cw, y );
1509 m_dragLastPos = y;
1510 }
1511 break;
1512
1513 case WXGRID_CURSOR_SELECT_ROW:
1514 {
1515 if ( (row = YToRow( y )) >= 0 &&
1516 !IsInSelection( row, 0 ) )
1517 {
1518 SelectRow( row, TRUE );
1519 }
1520 }
1521 break;
1522 }
1523 }
1524 return;
1525 }
1526
1527 m_isDragging = FALSE;
1528
1529
1530 // ------------ Left button pressed
1531 //
1532 if ( event.LeftDown() )
1533 {
1534 // don't send a label click event for a hit on the
1535 // edge of the row label - this is probably the user
1536 // wanting to resize the row
1537 //
1538 if ( YToEdgeOfRow(y) < 0 )
1539 {
1540 row = YToRow(y);
1541 if ( row >= 0 &&
1542 !SendEvent( EVT_GRID_LABEL_LEFT_CLICK, row, -1, event ) )
1543 {
1544 SelectRow( row, event.ShiftDown() );
1545 m_cursorMode = WXGRID_CURSOR_SELECT_ROW;
1546 }
1547 }
1548 else
1549 {
1550 // starting to drag-resize a row
1551 //
1552 m_rowLabelWin->CaptureMouse();
1553 }
1554 }
1555
1556
1557 // ------------ Left double click
1558 //
1559 else if (event.LeftDClick() )
1560 {
1561 if ( YToEdgeOfRow(y) < 0 )
1562 {
1563 row = YToRow(y);
1564 SendEvent( EVT_GRID_LABEL_LEFT_DCLICK, row, -1, event );
1565 }
1566 }
1567
1568
1569 // ------------ Left button released
1570 //
1571 else if ( event.LeftUp() )
1572 {
1573 if ( m_cursorMode == WXGRID_CURSOR_RESIZE_ROW )
1574 {
1575 m_rowLabelWin->ReleaseMouse();
1576
1577 if ( m_dragLastPos >= 0 )
1578 {
1579 // erase the last line and resize the row
1580 //
1581 int cw, ch, left, dummy;
1582 m_gridWin->GetClientSize( &cw, &ch );
1583 CalcUnscrolledPosition( 0, 0, &left, &dummy );
1584
1585 wxClientDC dc( m_gridWin );
1586 PrepareDC( dc );
1587 dc.SetLogicalFunction( wxINVERT );
1588 dc.DrawLine( left, m_dragLastPos, left+cw, m_dragLastPos );
1589 HideCellEditControl();
1590
1591 int rowTop = m_rowBottoms[m_dragRowOrCol] - m_rowHeights[m_dragRowOrCol];
1592 SetRowSize( m_dragRowOrCol, wxMax( y - rowTop, WXGRID_MIN_ROW_HEIGHT ) );
1593 if ( !GetBatchCount() )
1594 {
1595 // TODO: optimize this
1596 m_rowLabelWin->Refresh();
1597 m_gridWin->Refresh();
1598 }
1599
1600 ShowCellEditControl();
1601
1602 // Note: we are ending the event *after* doing
1603 // default processing in this case
1604 //
1605 SendEvent( EVT_GRID_ROW_SIZE, m_dragRowOrCol, -1, event );
1606 }
1607 }
1608
1609 m_dragLastPos = -1;
1610 }
1611
1612
1613 // ------------ Right button down
1614 //
1615 else if ( event.RightDown() )
1616 {
1617 row = YToRow(y);
1618 if ( !SendEvent( EVT_GRID_LABEL_RIGHT_CLICK, row, -1, event ) )
1619 {
1620 // no default action at the moment
1621 }
1622 }
1623
1624
1625 // ------------ Right double click
1626 //
1627 else if ( event.RightDClick() )
1628 {
1629 row = YToRow(y);
1630 if ( !SendEvent( EVT_GRID_LABEL_RIGHT_DCLICK, row, -1, event ) )
1631 {
1632 // no default action at the moment
1633 }
1634 }
1635
1636
1637 // ------------ No buttons down and mouse moving
1638 //
1639 else if ( event.Moving() )
1640 {
1641 m_dragRowOrCol = YToEdgeOfRow( y );
1642 if ( m_dragRowOrCol >= 0 )
1643 {
1644 if ( m_cursorMode == WXGRID_CURSOR_SELECT_CELL )
1645 {
1646 m_cursorMode = WXGRID_CURSOR_RESIZE_ROW;
1647 m_rowLabelWin->SetCursor( m_rowResizeCursor );
1648 }
1649 }
1650 else
1651 {
1652 if ( m_cursorMode != WXGRID_CURSOR_SELECT_CELL )
1653 {
1654 m_cursorMode = WXGRID_CURSOR_SELECT_CELL;
1655 m_rowLabelWin->SetCursor( *wxSTANDARD_CURSOR );
1656 }
1657 }
1658 }
1659 }
1660
1661
1662 void wxGrid::ProcessColLabelMouseEvent( wxMouseEvent& event )
1663 {
1664 int x, y, col;
1665 wxPoint pos( event.GetPosition() );
1666 CalcUnscrolledPosition( pos.x, pos.y, &x, &y );
1667
1668 if ( event.Dragging() )
1669 {
1670 m_isDragging = TRUE;
1671
1672 if ( event.LeftIsDown() )
1673 {
1674 switch( m_cursorMode )
1675 {
1676 case WXGRID_CURSOR_RESIZE_COL:
1677 {
1678 int cw, ch, dummy, top;
1679 m_gridWin->GetClientSize( &cw, &ch );
1680 CalcUnscrolledPosition( 0, 0, &dummy, &top );
1681
1682 wxClientDC dc( m_gridWin );
1683 PrepareDC( dc );
1684 dc.SetLogicalFunction(wxINVERT);
1685 if ( m_dragLastPos >= 0 )
1686 {
1687 dc.DrawLine( m_dragLastPos, top, m_dragLastPos, top+ch );
1688 }
1689 dc.DrawLine( x, top, x, top+ch );
1690 m_dragLastPos = x;
1691 }
1692 break;
1693
1694 case WXGRID_CURSOR_SELECT_COL:
1695 {
1696 if ( (col = XToCol( x )) >= 0 &&
1697 !IsInSelection( 0, col ) )
1698 {
1699 SelectCol( col, TRUE );
1700 }
1701 }
1702 break;
1703 }
1704 }
1705 return;
1706 }
1707
1708 m_isDragging = FALSE;
1709
1710
1711 // ------------ Left button pressed
1712 //
1713 if ( event.LeftDown() )
1714 {
1715 // don't send a label click event for a hit on the
1716 // edge of the col label - this is probably the user
1717 // wanting to resize the col
1718 //
1719 if ( XToEdgeOfCol(x) < 0 )
1720 {
1721 col = XToCol(x);
1722 if ( col >= 0 &&
1723 !SendEvent( EVT_GRID_LABEL_LEFT_CLICK, -1, col, event ) )
1724 {
1725 SelectCol( col, event.ShiftDown() );
1726 m_cursorMode = WXGRID_CURSOR_SELECT_COL;
1727 }
1728 }
1729 else
1730 {
1731 // starting to drag-resize a col
1732 //
1733 m_colLabelWin->CaptureMouse();
1734 }
1735 }
1736
1737
1738 // ------------ Left double click
1739 //
1740 if ( event.LeftDClick() )
1741 {
1742 if ( XToEdgeOfCol(x) < 0 )
1743 {
1744 col = XToCol(x);
1745 SendEvent( EVT_GRID_LABEL_LEFT_DCLICK, -1, col, event );
1746 }
1747 }
1748
1749
1750 // ------------ Left button released
1751 //
1752 else if ( event.LeftUp() )
1753 {
1754 if ( m_cursorMode == WXGRID_CURSOR_RESIZE_COL )
1755 {
1756 m_colLabelWin->ReleaseMouse();
1757
1758 if ( m_dragLastPos >= 0 )
1759 {
1760 // erase the last line and resize the col
1761 //
1762 int cw, ch, dummy, top;
1763 m_gridWin->GetClientSize( &cw, &ch );
1764 CalcUnscrolledPosition( 0, 0, &dummy, &top );
1765
1766 wxClientDC dc( m_gridWin );
1767 PrepareDC( dc );
1768 dc.SetLogicalFunction( wxINVERT );
1769 dc.DrawLine( m_dragLastPos, top, m_dragLastPos, top+ch );
1770 HideCellEditControl();
1771
1772 int colLeft = m_colRights[m_dragRowOrCol] - m_colWidths[m_dragRowOrCol];
1773 SetColSize( m_dragRowOrCol, wxMax( x - colLeft, WXGRID_MIN_COL_WIDTH ) );
1774
1775 if ( !GetBatchCount() )
1776 {
1777 // TODO: optimize this
1778 m_colLabelWin->Refresh();
1779 m_gridWin->Refresh();
1780 }
1781
1782 ShowCellEditControl();
1783
1784 // Note: we are ending the event *after* doing
1785 // default processing in this case
1786 //
1787 SendEvent( EVT_GRID_COL_SIZE, -1, m_dragRowOrCol, event );
1788 }
1789 }
1790
1791 m_dragLastPos = -1;
1792 }
1793
1794
1795 // ------------ Right button down
1796 //
1797 else if ( event.RightDown() )
1798 {
1799 col = XToCol(x);
1800 if ( !SendEvent( EVT_GRID_LABEL_RIGHT_CLICK, -1, col, event ) )
1801 {
1802 // no default action at the moment
1803 }
1804 }
1805
1806
1807 // ------------ Right double click
1808 //
1809 else if ( event.RightDClick() )
1810 {
1811 col = XToCol(x);
1812 if ( !SendEvent( EVT_GRID_LABEL_RIGHT_DCLICK, -1, col, event ) )
1813 {
1814 // no default action at the moment
1815 }
1816 }
1817
1818
1819 // ------------ No buttons down and mouse moving
1820 //
1821 else if ( event.Moving() )
1822 {
1823 m_dragRowOrCol = XToEdgeOfCol( x );
1824 if ( m_dragRowOrCol >= 0 )
1825 {
1826 if ( m_cursorMode == WXGRID_CURSOR_SELECT_CELL )
1827 {
1828 m_cursorMode = WXGRID_CURSOR_RESIZE_COL;
1829 m_colLabelWin->SetCursor( m_colResizeCursor );
1830 }
1831 }
1832 else
1833 {
1834 if ( m_cursorMode != WXGRID_CURSOR_SELECT_CELL )
1835 {
1836 m_cursorMode = WXGRID_CURSOR_SELECT_CELL;
1837 m_colLabelWin->SetCursor( *wxSTANDARD_CURSOR );
1838 }
1839 }
1840 }
1841 }
1842
1843
1844 void wxGrid::ProcessCornerLabelMouseEvent( wxMouseEvent& event )
1845 {
1846 if ( event.LeftDown() )
1847 {
1848 // indicate corner label by having both row and
1849 // col args == -1
1850 //
1851 if ( !SendEvent( EVT_GRID_LABEL_LEFT_CLICK, -1, -1, event ) )
1852 {
1853 SelectAll();
1854 }
1855 }
1856
1857 else if ( event.LeftDClick() )
1858 {
1859 SendEvent( EVT_GRID_LABEL_LEFT_DCLICK, -1, -1, event );
1860 }
1861
1862 else if ( event.RightDown() )
1863 {
1864 if ( !SendEvent( EVT_GRID_LABEL_RIGHT_CLICK, -1, -1, event ) )
1865 {
1866 // no default action at the moment
1867 }
1868 }
1869
1870 else if ( event.RightDClick() )
1871 {
1872 if ( !SendEvent( EVT_GRID_LABEL_RIGHT_DCLICK, -1, -1, event ) )
1873 {
1874 // no default action at the moment
1875 }
1876 }
1877 }
1878
1879
1880 void wxGrid::ProcessGridCellMouseEvent( wxMouseEvent& event )
1881 {
1882 int x, y;
1883 wxPoint pos( event.GetPosition() );
1884 CalcUnscrolledPosition( pos.x, pos.y, &x, &y );
1885
1886 wxGridCellCoords coords;
1887 XYToCell( x, y, coords );
1888
1889 if ( event.Dragging() )
1890 {
1891 m_isDragging = TRUE;
1892 if ( m_cursorMode == WXGRID_CURSOR_SELECT_CELL )
1893 {
1894 // Hide the edit control, so it
1895 // won't interfer with drag-shrinking.
1896 if ( IsCellEditControlEnabled() )
1897 HideCellEditControl();
1898 if ( coords != wxGridNoCellCoords )
1899 {
1900 if ( !IsSelection() )
1901 {
1902 SelectBlock( coords, coords );
1903 }
1904 else
1905 {
1906 SelectBlock( m_currentCellCoords, coords );
1907 }
1908 }
1909 }
1910
1911 return;
1912 }
1913
1914 m_isDragging = FALSE;
1915
1916 if ( coords != wxGridNoCellCoords )
1917 {
1918 if ( event.LeftDown() )
1919 {
1920 if ( event.ShiftDown() )
1921 {
1922 SelectBlock( m_currentCellCoords, coords );
1923 }
1924 else
1925 {
1926 if ( !SendEvent( EVT_GRID_CELL_LEFT_CLICK,
1927 coords.GetRow(),
1928 coords.GetCol(),
1929 event ) )
1930 {
1931 MakeCellVisible( coords );
1932 SetCurrentCell( coords );
1933 }
1934 }
1935 }
1936
1937
1938 // ------------ Left double click
1939 //
1940 else if ( event.LeftDClick() )
1941 {
1942 SendEvent( EVT_GRID_CELL_LEFT_DCLICK,
1943 coords.GetRow(),
1944 coords.GetCol(),
1945 event );
1946 }
1947
1948
1949 // ------------ Left button released
1950 //
1951 else if ( event.LeftUp() )
1952 {
1953 if ( m_cursorMode == WXGRID_CURSOR_SELECT_CELL )
1954 {
1955 if ( IsSelection() )
1956 {
1957 SendEvent( EVT_GRID_RANGE_SELECT, -1, -1, event );
1958 }
1959 }
1960
1961 // Show the edit control, if it has
1962 // been hidden for drag-shrinking.
1963 if ( IsCellEditControlEnabled() )
1964 ShowCellEditControl();
1965
1966 m_dragLastPos = -1;
1967 }
1968
1969
1970 // ------------ Right button down
1971 //
1972 else if ( event.RightDown() )
1973 {
1974 if ( !SendEvent( EVT_GRID_CELL_RIGHT_CLICK,
1975 coords.GetRow(),
1976 coords.GetCol(),
1977 event ) )
1978 {
1979 // no default action at the moment
1980 }
1981 }
1982
1983
1984 // ------------ Right double click
1985 //
1986 else if ( event.RightDClick() )
1987 {
1988 if ( !SendEvent( EVT_GRID_CELL_RIGHT_DCLICK,
1989 coords.GetRow(),
1990 coords.GetCol(),
1991 event ) )
1992 {
1993 // no default action at the moment
1994 }
1995 }
1996 }
1997 }
1998
1999
2000 //
2001 // ------ interaction with data model
2002 //
2003 bool wxGrid::ProcessTableMessage( wxGridTableMessage& msg )
2004 {
2005 switch ( msg.GetId() )
2006 {
2007 case wxGRIDTABLE_REQUEST_VIEW_GET_VALUES:
2008 return GetModelValues();
2009
2010 case wxGRIDTABLE_REQUEST_VIEW_SEND_VALUES:
2011 return SetModelValues();
2012
2013 case wxGRIDTABLE_NOTIFY_ROWS_INSERTED:
2014 case wxGRIDTABLE_NOTIFY_ROWS_APPENDED:
2015 case wxGRIDTABLE_NOTIFY_ROWS_DELETED:
2016 case wxGRIDTABLE_NOTIFY_COLS_INSERTED:
2017 case wxGRIDTABLE_NOTIFY_COLS_APPENDED:
2018 case wxGRIDTABLE_NOTIFY_COLS_DELETED:
2019 return Redimension( msg );
2020
2021 default:
2022 return FALSE;
2023 }
2024 }
2025
2026
2027
2028 // The behaviour of this function depends on the grid table class
2029 // Clear() function. For the default wxGridStringTable class the
2030 // behavious is to replace all cell contents with wxEmptyString but
2031 // not to change the number of rows or cols.
2032 //
2033 void wxGrid::ClearGrid()
2034 {
2035 if ( m_table )
2036 {
2037 m_table->Clear();
2038 SetEditControlValue();
2039 if ( !GetBatchCount() ) m_gridWin->Refresh();
2040 }
2041 }
2042
2043
2044 bool wxGrid::InsertRows( int pos, int numRows, bool WXUNUSED(updateLabels) )
2045 {
2046 // TODO: something with updateLabels flag
2047
2048 if ( !m_created )
2049 {
2050 wxLogError( wxT("Called wxGrid::InsertRows() before calling CreateGrid()") );
2051 return FALSE;
2052 }
2053
2054 if ( m_table )
2055 {
2056 bool ok = m_table->InsertRows( pos, numRows );
2057
2058 // the table will have sent the results of the insert row
2059 // operation to this view object as a grid table message
2060 //
2061 if ( ok )
2062 {
2063 if ( m_numCols == 0 )
2064 {
2065 m_table->AppendCols( WXGRID_DEFAULT_NUMBER_COLS );
2066 //
2067 // TODO: perhaps instead of appending the default number of cols
2068 // we should remember what the last non-zero number of cols was ?
2069 //
2070 }
2071
2072 if ( m_currentCellCoords == wxGridNoCellCoords )
2073 {
2074 // if we have just inserted cols into an empty grid the current
2075 // cell will be undefined...
2076 //
2077 SetCurrentCell( 0, 0 );
2078 }
2079
2080 ClearSelection();
2081 if ( !GetBatchCount() ) Refresh();
2082 }
2083
2084 SetEditControlValue();
2085 return ok;
2086 }
2087 else
2088 {
2089 return FALSE;
2090 }
2091 }
2092
2093
2094 bool wxGrid::AppendRows( int numRows, bool WXUNUSED(updateLabels) )
2095 {
2096 // TODO: something with updateLabels flag
2097
2098 if ( !m_created )
2099 {
2100 wxLogError( wxT("Called wxGrid::AppendRows() before calling CreateGrid()") );
2101 return FALSE;
2102 }
2103
2104 if ( m_table && m_table->AppendRows( numRows ) )
2105 {
2106 if ( m_currentCellCoords == wxGridNoCellCoords )
2107 {
2108 // if we have just inserted cols into an empty grid the current
2109 // cell will be undefined...
2110 //
2111 SetCurrentCell( 0, 0 );
2112 }
2113
2114 // the table will have sent the results of the append row
2115 // operation to this view object as a grid table message
2116 //
2117 ClearSelection();
2118 if ( !GetBatchCount() ) Refresh();
2119 return TRUE;
2120 }
2121 else
2122 {
2123 return FALSE;
2124 }
2125 }
2126
2127
2128 bool wxGrid::DeleteRows( int pos, int numRows, bool WXUNUSED(updateLabels) )
2129 {
2130 // TODO: something with updateLabels flag
2131
2132 if ( !m_created )
2133 {
2134 wxLogError( wxT("Called wxGrid::DeleteRows() before calling CreateGrid()") );
2135 return FALSE;
2136 }
2137
2138 if ( m_table && m_table->DeleteRows( pos, numRows ) )
2139 {
2140 // the table will have sent the results of the delete row
2141 // operation to this view object as a grid table message
2142 //
2143 if ( m_numRows > 0 )
2144 SetEditControlValue();
2145 else
2146 HideCellEditControl();
2147
2148 ClearSelection();
2149 if ( !GetBatchCount() ) Refresh();
2150 return TRUE;
2151 }
2152 else
2153 {
2154 return FALSE;
2155 }
2156 }
2157
2158
2159 bool wxGrid::InsertCols( int pos, int numCols, bool WXUNUSED(updateLabels) )
2160 {
2161 // TODO: something with updateLabels flag
2162
2163 if ( !m_created )
2164 {
2165 wxLogError( wxT("Called wxGrid::InsertCols() before calling CreateGrid()") );
2166 return FALSE;
2167 }
2168
2169 if ( m_table )
2170 {
2171 HideCellEditControl();
2172 bool ok = m_table->InsertCols( pos, numCols );
2173
2174 // the table will have sent the results of the insert col
2175 // operation to this view object as a grid table message
2176 //
2177 if ( ok )
2178 {
2179 if ( m_currentCellCoords == wxGridNoCellCoords )
2180 {
2181 // if we have just inserted cols into an empty grid the current
2182 // cell will be undefined...
2183 //
2184 SetCurrentCell( 0, 0 );
2185 }
2186
2187 ClearSelection();
2188 if ( !GetBatchCount() ) Refresh();
2189 }
2190
2191 SetEditControlValue();
2192 return ok;
2193 }
2194 else
2195 {
2196 return FALSE;
2197 }
2198 }
2199
2200
2201 bool wxGrid::AppendCols( int numCols, bool WXUNUSED(updateLabels) )
2202 {
2203 // TODO: something with updateLabels flag
2204
2205 if ( !m_created )
2206 {
2207 wxLogError( wxT("Called wxGrid::AppendCols() before calling CreateGrid()") );
2208 return FALSE;
2209 }
2210
2211 if ( m_table && m_table->AppendCols( numCols ) )
2212 {
2213 // the table will have sent the results of the append col
2214 // operation to this view object as a grid table message
2215 //
2216 if ( m_currentCellCoords == wxGridNoCellCoords )
2217 {
2218 // if we have just inserted cols into an empty grid the current
2219 // cell will be undefined...
2220 //
2221 SetCurrentCell( 0, 0 );
2222 }
2223
2224 ClearSelection();
2225 if ( !GetBatchCount() ) Refresh();
2226 return TRUE;
2227 }
2228 else
2229 {
2230 return FALSE;
2231 }
2232 }
2233
2234
2235 bool wxGrid::DeleteCols( int pos, int numCols, bool WXUNUSED(updateLabels) )
2236 {
2237 // TODO: something with updateLabels flag
2238
2239 if ( !m_created )
2240 {
2241 wxLogError( wxT("Called wxGrid::DeleteCols() before calling CreateGrid()") );
2242 return FALSE;
2243 }
2244
2245 if ( m_table && m_table->DeleteCols( pos, numCols ) )
2246 {
2247 // the table will have sent the results of the delete col
2248 // operation to this view object as a grid table message
2249 //
2250 if ( m_numCols > 0 )
2251 SetEditControlValue();
2252 else
2253 HideCellEditControl();
2254
2255 ClearSelection();
2256 if ( !GetBatchCount() ) Refresh();
2257 return TRUE;
2258 }
2259 else
2260 {
2261 return FALSE;
2262 }
2263 }
2264
2265
2266
2267 //
2268 // ----- event handlers
2269 //
2270
2271 // Generate a grid event based on a mouse event and
2272 // return the result of ProcessEvent()
2273 //
2274 bool wxGrid::SendEvent( const wxEventType type,
2275 int row, int col,
2276 wxMouseEvent& mouseEv )
2277 {
2278 if ( type == EVT_GRID_ROW_SIZE ||
2279 type == EVT_GRID_COL_SIZE )
2280 {
2281 int rowOrCol = (row == -1 ? col : row);
2282
2283 wxGridSizeEvent gridEvt( GetId(),
2284 type,
2285 this,
2286 rowOrCol,
2287 mouseEv.GetX(), mouseEv.GetY(),
2288 mouseEv.ControlDown(),
2289 mouseEv.ShiftDown(),
2290 mouseEv.AltDown(),
2291 mouseEv.MetaDown() );
2292
2293 return GetEventHandler()->ProcessEvent(gridEvt);
2294 }
2295 else if ( type == EVT_GRID_RANGE_SELECT )
2296 {
2297 wxGridRangeSelectEvent gridEvt( GetId(),
2298 type,
2299 this,
2300 m_selectedTopLeft,
2301 m_selectedBottomRight,
2302 mouseEv.ControlDown(),
2303 mouseEv.ShiftDown(),
2304 mouseEv.AltDown(),
2305 mouseEv.MetaDown() );
2306
2307 return GetEventHandler()->ProcessEvent(gridEvt);
2308 }
2309 else
2310 {
2311 wxGridEvent gridEvt( GetId(),
2312 type,
2313 this,
2314 row, col,
2315 mouseEv.GetX(), mouseEv.GetY(),
2316 mouseEv.ControlDown(),
2317 mouseEv.ShiftDown(),
2318 mouseEv.AltDown(),
2319 mouseEv.MetaDown() );
2320
2321 return GetEventHandler()->ProcessEvent(gridEvt);
2322 }
2323 }
2324
2325
2326 // Generate a grid event of specified type and return the result
2327 // of ProcessEvent().
2328 //
2329 bool wxGrid::SendEvent( const wxEventType type,
2330 int row, int col )
2331 {
2332 if ( type == EVT_GRID_ROW_SIZE ||
2333 type == EVT_GRID_COL_SIZE )
2334 {
2335 int rowOrCol = (row == -1 ? col : row);
2336
2337 wxGridSizeEvent gridEvt( GetId(),
2338 type,
2339 this,
2340 rowOrCol );
2341
2342 return GetEventHandler()->ProcessEvent(gridEvt);
2343 }
2344 else
2345 {
2346 wxGridEvent gridEvt( GetId(),
2347 type,
2348 this,
2349 row, col );
2350
2351 return GetEventHandler()->ProcessEvent(gridEvt);
2352 }
2353 }
2354
2355
2356 void wxGrid::OnPaint( wxPaintEvent& WXUNUSED(event) )
2357 {
2358 wxPaintDC dc( this );
2359
2360 if ( m_currentCellCoords == wxGridNoCellCoords &&
2361 m_numRows && m_numCols )
2362 {
2363 m_currentCellCoords.Set(0, 0);
2364 SetEditControlValue();
2365 ShowCellEditControl();
2366 }
2367 }
2368
2369
2370 // This is just here to make sure that CalcDimensions gets called when
2371 // the grid view is resized... then the size event is skipped to allow
2372 // the box sizers to handle everything
2373 //
2374 void wxGrid::OnSize( wxSizeEvent& event )
2375 {
2376 CalcDimensions();
2377 event.Skip();
2378 }
2379
2380
2381 void wxGrid::OnKeyDown( wxKeyEvent& event )
2382 {
2383 if ( m_inOnKeyDown )
2384 {
2385 // shouldn't be here - we are going round in circles...
2386 //
2387 wxLogFatalError( wxT("wxGrid::OnKeyDown called while alread active") );
2388 }
2389
2390 m_inOnKeyDown = TRUE;
2391
2392 // propagate the event up and see if it gets processed
2393 //
2394 wxWindow *parent = GetParent();
2395 wxKeyEvent keyEvt( event );
2396 keyEvt.SetEventObject( parent );
2397
2398 if ( !parent->GetEventHandler()->ProcessEvent( keyEvt ) )
2399 {
2400 // try local handlers
2401 //
2402 switch ( event.KeyCode() )
2403 {
2404 case WXK_UP:
2405 if ( event.ControlDown() )
2406 {
2407 MoveCursorUpBlock();
2408 }
2409 else
2410 {
2411 MoveCursorUp();
2412 }
2413 break;
2414
2415 case WXK_DOWN:
2416 if ( event.ControlDown() )
2417 {
2418 MoveCursorDownBlock();
2419 }
2420 else
2421 {
2422 MoveCursorDown();
2423 }
2424 break;
2425
2426 case WXK_LEFT:
2427 if ( event.ControlDown() )
2428 {
2429 MoveCursorLeftBlock();
2430 }
2431 else
2432 {
2433 MoveCursorLeft();
2434 }
2435 break;
2436
2437 case WXK_RIGHT:
2438 if ( event.ControlDown() )
2439 {
2440 MoveCursorRightBlock();
2441 }
2442 else
2443 {
2444 MoveCursorRight();
2445 }
2446 break;
2447
2448 case WXK_SPACE:
2449 if ( !IsEditable() )
2450 {
2451 MoveCursorRight();
2452 }
2453 else
2454 {
2455 event.Skip();
2456 }
2457 break;
2458
2459 case WXK_RETURN:
2460 if ( event.ControlDown() )
2461 {
2462 event.Skip(); // to let the edit control have the return
2463 }
2464 else
2465 {
2466 MoveCursorDown();
2467 }
2468 break;
2469
2470 case WXK_HOME:
2471 if ( event.ControlDown() )
2472 {
2473 MakeCellVisible( 0, 0 );
2474 SetCurrentCell( 0, 0 );
2475 }
2476 else
2477 {
2478 event.Skip();
2479 }
2480 break;
2481
2482 case WXK_END:
2483 if ( event.ControlDown() )
2484 {
2485 MakeCellVisible( m_numRows-1, m_numCols-1 );
2486 SetCurrentCell( m_numRows-1, m_numCols-1 );
2487 }
2488 else
2489 {
2490 event.Skip();
2491 }
2492 break;
2493
2494 case WXK_PRIOR:
2495 MovePageUp();
2496 break;
2497
2498 case WXK_NEXT:
2499 MovePageDown();
2500 break;
2501
2502 default:
2503 // now try the cell edit control
2504 //
2505 if ( IsCellEditControlEnabled() )
2506 {
2507 event.SetEventObject( m_cellEditCtrl );
2508 m_cellEditCtrl->GetEventHandler()->ProcessEvent( event );
2509 }
2510 break;
2511 }
2512 }
2513
2514 m_inOnKeyDown = FALSE;
2515 }
2516
2517
2518 void wxGrid::SetCurrentCell( const wxGridCellCoords& coords )
2519 {
2520 if ( SendEvent( EVT_GRID_SELECT_CELL, coords.GetRow(), coords.GetCol() ) )
2521 {
2522 // the event has been intercepted - do nothing
2523 return;
2524 }
2525
2526 wxClientDC dc( m_gridWin );
2527 PrepareDC( dc );
2528
2529 if ( m_currentCellCoords != wxGridNoCellCoords )
2530 {
2531 HideCellEditControl();
2532 SaveEditControlValue();
2533 }
2534
2535 m_currentCellCoords = coords;
2536
2537 SetEditControlValue();
2538 ShowCellEditControl();
2539
2540 if ( IsSelection() )
2541 {
2542 wxRect r( SelectionToDeviceRect() );
2543 ClearSelection();
2544 if ( !GetBatchCount() ) m_gridWin->Refresh( TRUE, &r );
2545 }
2546 }
2547
2548
2549 //
2550 // ------ functions to get/send data (see also public functions)
2551 //
2552
2553 bool wxGrid::GetModelValues()
2554 {
2555 if ( m_table )
2556 {
2557 // all we need to do is repaint the grid
2558 //
2559 m_gridWin->Refresh();
2560 return TRUE;
2561 }
2562
2563 return FALSE;
2564 }
2565
2566
2567 bool wxGrid::SetModelValues()
2568 {
2569 int row, col;
2570
2571 if ( m_table )
2572 {
2573 for ( row = 0; row < m_numRows; row++ )
2574 {
2575 for ( col = 0; col < m_numCols; col++ )
2576 {
2577 m_table->SetValue( row, col, GetCellValue(row, col) );
2578 }
2579 }
2580
2581 return TRUE;
2582 }
2583
2584 return FALSE;
2585 }
2586
2587
2588
2589 // Note - this function only draws cells that are in the list of
2590 // exposed cells (usually set from the update region by
2591 // CalcExposedCells)
2592 //
2593 void wxGrid::DrawGridCellArea( wxDC& dc )
2594 {
2595 if ( !m_numRows || !m_numCols ) return;
2596
2597 size_t i;
2598 size_t numCells = m_cellsExposed.GetCount();
2599
2600 for ( i = 0; i < numCells; i++ )
2601 {
2602 DrawCell( dc, m_cellsExposed[i] );
2603 }
2604 }
2605
2606
2607 void wxGrid::DrawCell( wxDC& dc, const wxGridCellCoords& coords )
2608 {
2609 if ( m_colWidths[coords.GetCol()] <=0 ||
2610 m_rowHeights[coords.GetRow()] <= 0 ) return;
2611
2612 #if !DRAW_LINES
2613 if ( m_gridLinesEnabled )
2614 DrawCellBorder( dc, coords );
2615 #endif
2616
2617 DrawCellBackground( dc, coords );
2618
2619 // TODO: separate functions here for different kinds of cells ?
2620 // e.g. text, image
2621 //
2622 DrawCellValue( dc, coords );
2623 }
2624
2625
2626 void wxGrid::DrawCellBorder( wxDC& dc, const wxGridCellCoords& coords )
2627 {
2628 if ( m_colWidths[coords.GetCol()] <=0 ||
2629 m_rowHeights[coords.GetRow()] <= 0 ) return;
2630
2631 dc.SetPen( wxPen(GetGridLineColour(), 1, wxSOLID) );
2632 int row = coords.GetRow();
2633 int col = coords.GetCol();
2634
2635 // right hand border
2636 //
2637 dc.DrawLine( m_colRights[col], m_rowBottoms[row] - m_rowHeights[row],
2638 m_colRights[col], m_rowBottoms[row] );
2639
2640 // bottom border
2641 //
2642 dc.DrawLine( m_colRights[col] - m_colWidths[col], m_rowBottoms[row],
2643 m_colRights[col], m_rowBottoms[row] );
2644 }
2645
2646
2647 void wxGrid::DrawCellBackground( wxDC& dc, const wxGridCellCoords& coords )
2648 {
2649 if ( m_colWidths[coords.GetCol()] <=0 ||
2650 m_rowHeights[coords.GetRow()] <= 0 ) return;
2651
2652 int row = coords.GetRow();
2653 int col = coords.GetCol();
2654
2655 dc.SetBackgroundMode( wxSOLID );
2656
2657 if ( IsInSelection( coords ) )
2658 {
2659 // TODO: improve this
2660 //
2661 dc.SetBrush( *wxBLACK_BRUSH );
2662 }
2663 else
2664 {
2665 dc.SetBrush( wxBrush(GetCellBackgroundColour(row, col), wxSOLID) );
2666 }
2667
2668 dc.SetPen( *wxTRANSPARENT_PEN );
2669
2670 dc.DrawRectangle( m_colRights[col] - m_colWidths[col] + 1,
2671 m_rowBottoms[row] - m_rowHeights[row] + 1,
2672 m_colWidths[col]-1,
2673 m_rowHeights[row]-1 );
2674 }
2675
2676
2677 void wxGrid::DrawCellValue( wxDC& dc, const wxGridCellCoords& coords )
2678 {
2679 if ( m_colWidths[coords.GetCol()] <=0 ||
2680 m_rowHeights[coords.GetRow()] <= 0 ) return;
2681
2682 int row = coords.GetRow();
2683 int col = coords.GetCol();
2684
2685 dc.SetBackgroundMode( wxTRANSPARENT );
2686
2687 if ( IsInSelection( row, col ) )
2688 {
2689 // TODO: improve this
2690 //
2691 dc.SetTextBackground( wxColour(0, 0, 0) );
2692 dc.SetTextForeground( wxColour(255, 255, 255) );
2693 }
2694 else
2695 {
2696 dc.SetTextBackground( GetCellBackgroundColour(row, col) );
2697 dc.SetTextForeground( GetCellTextColour(row, col) );
2698 }
2699 dc.SetFont( GetCellFont(row, col) );
2700
2701 int hAlign, vAlign;
2702 GetCellAlignment( row, col, &hAlign, &vAlign );
2703
2704 wxRect rect;
2705 rect.SetX( m_colRights[col] - m_colWidths[col] + 2 );
2706 rect.SetY( m_rowBottoms[row] - m_rowHeights[row] + 2 );
2707 rect.SetWidth( m_colWidths[col] - 4 );
2708 rect.SetHeight( m_rowHeights[row] - 4 );
2709
2710 DrawTextRectangle( dc, GetCellValue( row, col ), rect, hAlign, vAlign );
2711 }
2712
2713
2714
2715 // TODO: remove this ???
2716 // This is used to redraw all grid lines e.g. when the grid line colour
2717 // has been changed
2718 //
2719 void wxGrid::DrawAllGridLines( wxDC& dc, const wxRegion & reg )
2720 {
2721 if ( !m_gridLinesEnabled ||
2722 !m_numRows ||
2723 !m_numCols ) return;
2724
2725 int top, bottom, left, right;
2726
2727 if (reg.IsEmpty()){
2728 int cw, ch;
2729 m_gridWin->GetClientSize(&cw, &ch);
2730
2731 // virtual coords of visible area
2732 //
2733 CalcUnscrolledPosition( 0, 0, &left, &top );
2734 CalcUnscrolledPosition( cw, ch, &right, &bottom );
2735 }
2736 else{
2737 wxCoord x, y, w, h;
2738 reg.GetBox(x, y, w, h);
2739 CalcUnscrolledPosition( x, y, &left, &top );
2740 CalcUnscrolledPosition( x + w, y + h, &right, &bottom );
2741 }
2742
2743 dc.SetPen( wxPen(GetGridLineColour(), 1, wxSOLID) );
2744
2745 // horizontal grid lines
2746 //
2747 int i;
2748 for ( i = 0; i <= m_numRows; i++ )
2749 {
2750 if ( m_rowBottoms[i] > bottom )
2751 {
2752 break;
2753 }
2754 else if ( m_rowBottoms[i] >= top )
2755 {
2756 dc.DrawLine( left, m_rowBottoms[i], right, m_rowBottoms[i] );
2757 }
2758 }
2759
2760
2761 // vertical grid lines
2762 //
2763 for ( i = 0; i <= m_numCols; i++ )
2764 {
2765 if ( m_colRights[i] > right )
2766 {
2767 break;
2768 }
2769 else if ( m_colRights[i] >= left )
2770 {
2771 dc.DrawLine( m_colRights[i], top, m_colRights[i], bottom );
2772 }
2773 }
2774 }
2775
2776
2777 void wxGrid::DrawRowLabels( wxDC& dc )
2778 {
2779 if ( !m_numRows || !m_numCols ) return;
2780
2781 size_t i;
2782 size_t numLabels = m_rowLabelsExposed.GetCount();
2783
2784 for ( i = 0; i < numLabels; i++ )
2785 {
2786 DrawRowLabel( dc, m_rowLabelsExposed[i] );
2787 }
2788 }
2789
2790
2791 void wxGrid::DrawRowLabel( wxDC& dc, int row )
2792 {
2793 if ( m_rowHeights[row] <= 0 ) return;
2794
2795 // draw the label's horizontal border (the vertical border is
2796 // provided by the cell area window margin)
2797 //
2798 dc.SetPen( *wxBLACK_PEN );
2799
2800 dc.DrawLine( 0, m_rowBottoms[row]+1,
2801 m_rowLabelWidth, m_rowBottoms[row]+1 );
2802
2803 dc.SetPen( *wxWHITE_PEN );
2804
2805 dc.DrawLine( 0, m_rowBottoms[row]+2,
2806 m_rowLabelWidth, m_rowBottoms[row]+2 );
2807
2808 dc.SetBackgroundMode( wxTRANSPARENT );
2809 dc.SetTextForeground( GetLabelTextColour() );
2810 dc.SetFont( GetLabelFont() );
2811
2812 int hAlign, vAlign;
2813 GetRowLabelAlignment( &hAlign, &vAlign );
2814
2815 wxRect rect;
2816 rect.SetX( 2 );
2817 rect.SetY( m_rowBottoms[row] - m_rowHeights[row] + 2 );
2818 rect.SetWidth( m_rowLabelWidth - 4 );
2819 rect.SetHeight( m_rowHeights[row] - 4 );
2820 DrawTextRectangle( dc, GetRowLabelValue( row ), rect, hAlign, vAlign );
2821 }
2822
2823
2824 void wxGrid::DrawColLabels( wxDC& dc )
2825 {
2826 if ( !m_numRows || !m_numCols ) return;
2827
2828 size_t i;
2829 size_t numLabels = m_colLabelsExposed.GetCount();
2830
2831 for ( i = 0; i < numLabels; i++ )
2832 {
2833 DrawColLabel( dc, m_colLabelsExposed[i] );
2834 }
2835 }
2836
2837
2838 void wxGrid::DrawColLabel( wxDC& dc, int col )
2839 {
2840 if ( m_colWidths[col] <= 0 ) return;
2841
2842 // draw the label's vertical border (the horizontal border is
2843 // provided by the cell area window margin)
2844 //
2845 dc.SetPen( *wxBLACK_PEN );
2846
2847 dc.DrawLine( m_colRights[col]+1, 0,
2848 m_colRights[col]+1, m_colLabelHeight );
2849
2850 dc.SetPen( *wxWHITE_PEN );
2851
2852 dc.DrawLine( m_colRights[col]+2, 0,
2853 m_colRights[col]+2, m_colLabelHeight );
2854
2855 dc.SetBackgroundMode( wxTRANSPARENT );
2856 dc.SetTextForeground( GetLabelTextColour() );
2857 dc.SetFont( GetLabelFont() );
2858
2859 int hAlign, vAlign;
2860 GetColLabelAlignment( &hAlign, &vAlign );
2861
2862 wxRect rect;
2863 rect.SetX( m_colRights[col] - m_colWidths[col] + 2 );
2864 rect.SetY( 2 );
2865 rect.SetWidth( m_colWidths[col] - 4 );
2866 rect.SetHeight( m_colLabelHeight - 4 );
2867 DrawTextRectangle( dc, GetColLabelValue( col ), rect, hAlign, vAlign );
2868 }
2869
2870
2871 void wxGrid::DrawTextRectangle( wxDC& dc,
2872 const wxString& value,
2873 const wxRect& rect,
2874 int horizAlign,
2875 int vertAlign )
2876 {
2877 long textWidth, textHeight;
2878 long lineWidth, lineHeight;
2879 wxArrayString lines;
2880
2881 dc.SetClippingRegion( rect );
2882 StringToLines( value, lines );
2883 if ( lines.GetCount() )
2884 {
2885 GetTextBoxSize( dc, lines, &textWidth, &textHeight );
2886 dc.GetTextExtent( lines[0], &lineWidth, &lineHeight );
2887
2888 float x, y;
2889 switch ( horizAlign )
2890 {
2891 case wxRIGHT:
2892 x = rect.x + (rect.width - textWidth - 1);
2893 break;
2894
2895 case wxCENTRE:
2896 x = rect.x + ((rect.width - textWidth)/2);
2897 break;
2898
2899 case wxLEFT:
2900 default:
2901 x = rect.x + 1;
2902 break;
2903 }
2904
2905 switch ( vertAlign )
2906 {
2907 case wxBOTTOM:
2908 y = rect.y + (rect.height - textHeight - 1);
2909 break;
2910
2911 case wxCENTRE:
2912 y = rect.y + ((rect.height - textHeight)/2);
2913 break;
2914
2915 case wxTOP:
2916 default:
2917 y = rect.y + 1;
2918 break;
2919 }
2920
2921 for ( size_t i = 0; i < lines.GetCount(); i++ )
2922 {
2923 dc.DrawText( lines[i], (long)x, (long)y );
2924 y += lineHeight;
2925 }
2926 }
2927
2928 dc.DestroyClippingRegion();
2929 }
2930
2931
2932 // Split multi line text up into an array of strings. Any existing
2933 // contents of the string array are preserved.
2934 //
2935 void wxGrid::StringToLines( const wxString& value, wxArrayString& lines )
2936 {
2937 // TODO: this won't work for WXMAC ? (lines end with '\r')
2938 // => use wxTextFile functions then (VZ)
2939 int startPos = 0;
2940 int pos;
2941 while ( startPos < (int)value.Length() )
2942 {
2943 pos = value.Mid(startPos).Find( '\n' );
2944 if ( pos < 0 )
2945 {
2946 break;
2947 }
2948 else if ( pos == 0 )
2949 {
2950 lines.Add( wxEmptyString );
2951 }
2952 else
2953 {
2954 if ( value[startPos+pos-1] == '\r' )
2955 {
2956 lines.Add( value.Mid(startPos, pos-1) );
2957 }
2958 else
2959 {
2960 lines.Add( value.Mid(startPos, pos) );
2961 }
2962 }
2963 startPos += pos+1;
2964 }
2965 if ( startPos < (int)value.Length() )
2966 {
2967 lines.Add( value.Mid( startPos ) );
2968 }
2969 }
2970
2971
2972 void wxGrid::GetTextBoxSize( wxDC& dc,
2973 wxArrayString& lines,
2974 long *width, long *height )
2975 {
2976 long w = 0;
2977 long h = 0;
2978 long lineW, lineH;
2979
2980 size_t i;
2981 for ( i = 0; i < lines.GetCount(); i++ )
2982 {
2983 dc.GetTextExtent( lines[i], &lineW, &lineH );
2984 w = wxMax( w, lineW );
2985 h += lineH;
2986 }
2987
2988 *width = w;
2989 *height = h;
2990 }
2991
2992
2993 //
2994 // ------ Edit control functions
2995 //
2996
2997
2998 void wxGrid::EnableEditing( bool edit )
2999 {
3000 // TODO: improve this ?
3001 //
3002 if ( edit != m_editable )
3003 {
3004 m_editable = edit;
3005
3006 // TODO: extend this for other edit control types
3007 //
3008 if ( m_editCtrlType == wxGRID_TEXTCTRL )
3009 {
3010 ((wxTextCtrl *)m_cellEditCtrl)->SetEditable( m_editable );
3011 }
3012 }
3013 }
3014
3015
3016 #if 0 // disabled for the moment - the cell control is always active
3017 void wxGrid::EnableCellEditControl( bool enable )
3018 {
3019 if ( m_cellEditCtrl &&
3020 enable != m_cellEditCtrlEnabled )
3021 {
3022 m_cellEditCtrlEnabled = enable;
3023
3024 if ( m_cellEditCtrlEnabled )
3025 {
3026 SetEditControlValue();
3027 ShowCellEditControl();
3028 }
3029 else
3030 {
3031 HideCellEditControl();
3032 SaveEditControlValue();
3033 }
3034 }
3035 }
3036 #endif
3037
3038
3039 void wxGrid::ShowCellEditControl()
3040 {
3041 wxRect rect;
3042
3043 if ( IsCellEditControlEnabled() )
3044 {
3045 if ( !IsVisible( m_currentCellCoords ) )
3046 {
3047 return;
3048 }
3049 else
3050 {
3051 rect = CellToRect( m_currentCellCoords );
3052
3053 // convert to scrolled coords
3054 //
3055 int left, top, right, bottom;
3056 CalcScrolledPosition( rect.GetLeft(), rect.GetTop(), &left, &top );
3057 CalcScrolledPosition( rect.GetRight(), rect.GetBottom(), &right, &bottom );
3058
3059 int cw, ch;
3060 m_gridWin->GetClientSize( &cw, &ch );
3061
3062 // Make the edit control large enough to allow for internal margins
3063 // TODO: remove this if the text ctrl sizing is improved esp. for unix
3064 //
3065 int extra;
3066 #if defined(__WXMOTIF__)
3067 if ( m_currentCellCoords.GetRow() == 0 ||
3068 m_currentCellCoords.GetCol() == 0 )
3069 {
3070 extra = 2;
3071 }
3072 else
3073 {
3074 extra = 4;
3075 }
3076 #else
3077 if ( m_currentCellCoords.GetRow() == 0 ||
3078 m_currentCellCoords.GetCol() == 0 )
3079 {
3080 extra = 1;
3081 }
3082 else
3083 {
3084 extra = 2;
3085 }
3086 #endif
3087
3088 #if defined(__WXGTK__)
3089 int top_diff = 0;
3090 int left_diff = 0;
3091 if (left != 0) left_diff++;
3092 if (top != 0) top_diff++;
3093 rect.SetLeft( left + left_diff );
3094 rect.SetTop( top + top_diff );
3095 rect.SetRight( rect.GetRight() - left_diff );
3096 rect.SetBottom( rect.GetBottom() - top_diff );
3097 #else
3098 rect.SetLeft( wxMax(0, left - extra) );
3099 rect.SetTop( wxMax(0, top - extra) );
3100 rect.SetRight( rect.GetRight() + 2*extra );
3101 rect.SetBottom( rect.GetBottom() + 2*extra );
3102 #endif
3103
3104 m_cellEditCtrl->SetSize( rect );
3105 m_cellEditCtrl->Show( TRUE );
3106
3107 switch ( m_editCtrlType )
3108 {
3109 case wxGRID_TEXTCTRL:
3110 ((wxTextCtrl *) m_cellEditCtrl)->SetInsertionPointEnd();
3111 break;
3112
3113 case wxGRID_CHECKBOX:
3114 // TODO: anything ???
3115 //
3116 break;
3117
3118 case wxGRID_CHOICE:
3119 // TODO: anything ???
3120 //
3121 break;
3122
3123 case wxGRID_COMBOBOX:
3124 // TODO: anything ???
3125 //
3126 break;
3127 }
3128
3129 m_cellEditCtrl->SetFocus();
3130 }
3131 }
3132 }
3133
3134
3135 void wxGrid::HideCellEditControl()
3136 {
3137 if ( IsCellEditControlEnabled() )
3138 {
3139 m_cellEditCtrl->Show( FALSE );
3140 }
3141 }
3142
3143
3144 void wxGrid::SetEditControlValue( const wxString& value )
3145 {
3146 if ( m_table )
3147 {
3148 wxString s;
3149 if ( !value )
3150 s = GetCellValue(m_currentCellCoords);
3151 else
3152 s = value;
3153
3154 if ( IsCellEditControlEnabled() )
3155 {
3156 switch ( m_editCtrlType )
3157 {
3158 case wxGRID_TEXTCTRL:
3159 ((wxGridTextCtrl *)m_cellEditCtrl)->SetStartValue(s);
3160 break;
3161
3162 case wxGRID_CHECKBOX:
3163 // TODO: implement this
3164 //
3165 break;
3166
3167 case wxGRID_CHOICE:
3168 // TODO: implement this
3169 //
3170 break;
3171
3172 case wxGRID_COMBOBOX:
3173 // TODO: implement this
3174 //
3175 break;
3176 }
3177 }
3178 }
3179 }
3180
3181
3182 void wxGrid::SaveEditControlValue()
3183 {
3184 if ( m_table )
3185 {
3186 wxWindow *ctrl = (wxWindow *)NULL;
3187
3188 if ( IsCellEditControlEnabled() )
3189 {
3190 ctrl = m_cellEditCtrl;
3191 }
3192 else
3193 {
3194 return;
3195 }
3196
3197 bool valueChanged = FALSE;
3198
3199 switch ( m_editCtrlType )
3200 {
3201 case wxGRID_TEXTCTRL:
3202 valueChanged = (((wxGridTextCtrl *)ctrl)->GetValue() !=
3203 ((wxGridTextCtrl *)ctrl)->GetStartValue());
3204 SetCellValue( m_currentCellCoords,
3205 ((wxTextCtrl *) ctrl)->GetValue() );
3206 break;
3207
3208 case wxGRID_CHECKBOX:
3209 // TODO: implement this
3210 //
3211 break;
3212
3213 case wxGRID_CHOICE:
3214 // TODO: implement this
3215 //
3216 break;
3217
3218 case wxGRID_COMBOBOX:
3219 // TODO: implement this
3220 //
3221 break;
3222 }
3223
3224 if ( valueChanged )
3225 {
3226 SendEvent( EVT_GRID_CELL_CHANGE,
3227 m_currentCellCoords.GetRow(),
3228 m_currentCellCoords.GetCol() );
3229 }
3230 }
3231 }
3232
3233
3234 //
3235 // ------ Grid location functions
3236 // Note that all of these functions work with the logical coordinates of
3237 // grid cells and labels so you will need to convert from device
3238 // coordinates for mouse events etc.
3239 //
3240
3241 void wxGrid::XYToCell( int x, int y, wxGridCellCoords& coords )
3242 {
3243 int row = YToRow(y);
3244 int col = XToCol(x);
3245
3246 if ( row == -1 || col == -1 )
3247 {
3248 coords = wxGridNoCellCoords;
3249 }
3250 else
3251 {
3252 coords.Set( row, col );
3253 }
3254 }
3255
3256
3257 int wxGrid::YToRow( int y )
3258 {
3259 int i;
3260
3261 for ( i = 0; i < m_numRows; i++ )
3262 {
3263 if ( y < m_rowBottoms[i] ) return i;
3264 }
3265
3266 return -1;
3267 }
3268
3269
3270 int wxGrid::XToCol( int x )
3271 {
3272 int i;
3273
3274 for ( i = 0; i < m_numCols; i++ )
3275 {
3276 if ( x < m_colRights[i] ) return i;
3277 }
3278
3279 return -1;
3280 }
3281
3282
3283 // return the row number that that the y coord is near the edge of, or
3284 // -1 if not near an edge
3285 //
3286 int wxGrid::YToEdgeOfRow( int y )
3287 {
3288 int i, d;
3289
3290 for ( i = 0; i < m_numRows; i++ )
3291 {
3292 if ( m_rowHeights[i] > WXGRID_LABEL_EDGE_ZONE )
3293 {
3294 d = abs( y - m_rowBottoms[i] );
3295 {
3296 if ( d < WXGRID_LABEL_EDGE_ZONE ) return i;
3297 }
3298 }
3299 }
3300
3301 return -1;
3302 }
3303
3304
3305 // return the col number that that the x coord is near the edge of, or
3306 // -1 if not near an edge
3307 //
3308 int wxGrid::XToEdgeOfCol( int x )
3309 {
3310 int i, d;
3311
3312 for ( i = 0; i < m_numCols; i++ )
3313 {
3314 if ( m_colWidths[i] > WXGRID_LABEL_EDGE_ZONE )
3315 {
3316 d = abs( x - m_colRights[i] );
3317 {
3318 if ( d < WXGRID_LABEL_EDGE_ZONE ) return i;
3319 }
3320 }
3321 }
3322
3323 return -1;
3324 }
3325
3326
3327 wxRect wxGrid::CellToRect( int row, int col )
3328 {
3329 wxRect rect( -1, -1, -1, -1 );
3330
3331 if ( row >= 0 && row < m_numRows &&
3332 col >= 0 && col < m_numCols )
3333 {
3334 rect.x = m_colRights[col] - m_colWidths[col];
3335 rect.y = m_rowBottoms[row] - m_rowHeights[row];
3336 rect.width = m_colWidths[col];
3337 rect.height = m_rowHeights[ row ];
3338 }
3339
3340 return rect;
3341 }
3342
3343
3344 bool wxGrid::IsVisible( int row, int col, bool wholeCellVisible )
3345 {
3346 // get the cell rectangle in logical coords
3347 //
3348 wxRect r( CellToRect( row, col ) );
3349
3350 // convert to device coords
3351 //
3352 int left, top, right, bottom;
3353 CalcScrolledPosition( r.GetLeft(), r.GetTop(), &left, &top );
3354 CalcScrolledPosition( r.GetRight(), r.GetBottom(), &right, &bottom );
3355
3356 // check against the client area of the grid window
3357 //
3358 int cw, ch;
3359 m_gridWin->GetClientSize( &cw, &ch );
3360
3361 if ( wholeCellVisible )
3362 {
3363 // is the cell wholly visible ?
3364 //
3365 return ( left >= 0 && right <= cw &&
3366 top >= 0 && bottom <= ch );
3367 }
3368 else
3369 {
3370 // is the cell partly visible ?
3371 //
3372 return ( ((left >=0 && left < cw) || (right > 0 && right <= cw)) &&
3373 ((top >=0 && top < ch) || (bottom > 0 && bottom <= ch)) );
3374 }
3375 }
3376
3377
3378 // make the specified cell location visible by doing a minimal amount
3379 // of scrolling
3380 //
3381 void wxGrid::MakeCellVisible( int row, int col )
3382 {
3383 int i;
3384 int xpos = -1, ypos = -1;
3385
3386 if ( row >= 0 && row < m_numRows &&
3387 col >= 0 && col < m_numCols )
3388 {
3389 // get the cell rectangle in logical coords
3390 //
3391 wxRect r( CellToRect( row, col ) );
3392
3393 // convert to device coords
3394 //
3395 int left, top, right, bottom;
3396 CalcScrolledPosition( r.GetLeft(), r.GetTop(), &left, &top );
3397 CalcScrolledPosition( r.GetRight(), r.GetBottom(), &right, &bottom );
3398
3399 int cw, ch;
3400 m_gridWin->GetClientSize( &cw, &ch );
3401
3402 if ( top < 0 )
3403 {
3404 ypos = r.GetTop();
3405 }
3406 else if ( bottom > ch )
3407 {
3408 int h = r.GetHeight();
3409 ypos = r.GetTop();
3410 for ( i = row-1; i >= 0; i-- )
3411 {
3412 if ( h + m_rowHeights[i] > ch ) break;
3413
3414 h += m_rowHeights[i];
3415 ypos -= m_rowHeights[i];
3416 }
3417
3418 // we divide it later by GRID_SCROLL_LINE, make sure that we don't
3419 // have rounding errors (this is important, because if we do, we
3420 // might not scroll at all and some cells won't be redrawn)
3421 ypos += GRID_SCROLL_LINE / 2;
3422 }
3423
3424 if ( left < 0 )
3425 {
3426 xpos = r.GetLeft();
3427 }
3428 else if ( right > cw )
3429 {
3430 int w = r.GetWidth();
3431 xpos = r.GetLeft();
3432 for ( i = col-1; i >= 0; i-- )
3433 {
3434 if ( w + m_colWidths[i] > cw ) break;
3435
3436 w += m_colWidths[i];
3437 xpos -= m_colWidths[i];
3438 }
3439
3440 // see comment for ypos above
3441 xpos += GRID_SCROLL_LINE / 2;
3442 }
3443
3444 if ( xpos != -1 || ypos != -1 )
3445 {
3446 if ( xpos != -1 ) xpos /= GRID_SCROLL_LINE;
3447 if ( ypos != -1 ) ypos /= GRID_SCROLL_LINE;
3448 Scroll( xpos, ypos );
3449 AdjustScrollbars();
3450 }
3451 }
3452 }
3453
3454
3455 //
3456 // ------ Grid cursor movement functions
3457 //
3458
3459 bool wxGrid::MoveCursorUp()
3460 {
3461 if ( m_currentCellCoords != wxGridNoCellCoords &&
3462 m_currentCellCoords.GetRow() > 0 )
3463 {
3464 MakeCellVisible( m_currentCellCoords.GetRow() - 1,
3465 m_currentCellCoords.GetCol() );
3466
3467 SetCurrentCell( m_currentCellCoords.GetRow() - 1,
3468 m_currentCellCoords.GetCol() );
3469
3470 return TRUE;
3471 }
3472
3473 return FALSE;
3474 }
3475
3476
3477 bool wxGrid::MoveCursorDown()
3478 {
3479 // TODO: allow for scrolling
3480 //
3481 if ( m_currentCellCoords != wxGridNoCellCoords &&
3482 m_currentCellCoords.GetRow() < m_numRows-1 )
3483 {
3484 MakeCellVisible( m_currentCellCoords.GetRow() + 1,
3485 m_currentCellCoords.GetCol() );
3486
3487 SetCurrentCell( m_currentCellCoords.GetRow() + 1,
3488 m_currentCellCoords.GetCol() );
3489
3490 return TRUE;
3491 }
3492
3493 return FALSE;
3494 }
3495
3496
3497 bool wxGrid::MoveCursorLeft()
3498 {
3499 if ( m_currentCellCoords != wxGridNoCellCoords &&
3500 m_currentCellCoords.GetCol() > 0 )
3501 {
3502 MakeCellVisible( m_currentCellCoords.GetRow(),
3503 m_currentCellCoords.GetCol() - 1 );
3504
3505 SetCurrentCell( m_currentCellCoords.GetRow(),
3506 m_currentCellCoords.GetCol() - 1 );
3507
3508 return TRUE;
3509 }
3510
3511 return FALSE;
3512 }
3513
3514
3515 bool wxGrid::MoveCursorRight()
3516 {
3517 if ( m_currentCellCoords != wxGridNoCellCoords &&
3518 m_currentCellCoords.GetCol() < m_numCols - 1 )
3519 {
3520 MakeCellVisible( m_currentCellCoords.GetRow(),
3521 m_currentCellCoords.GetCol() + 1 );
3522
3523 SetCurrentCell( m_currentCellCoords.GetRow(),
3524 m_currentCellCoords.GetCol() + 1 );
3525
3526 return TRUE;
3527 }
3528
3529 return FALSE;
3530 }
3531
3532
3533 bool wxGrid::MovePageUp()
3534 {
3535 if ( m_currentCellCoords == wxGridNoCellCoords ) return FALSE;
3536
3537 int row = m_currentCellCoords.GetRow();
3538 if ( row > 0 )
3539 {
3540 int cw, ch;
3541 m_gridWin->GetClientSize( &cw, &ch );
3542
3543 int y = m_rowBottoms[ row ] - m_rowHeights[ row ];
3544 int newRow = YToRow( y - ch + 1 );
3545 if ( newRow == -1 )
3546 {
3547 newRow = 0;
3548 }
3549 else if ( newRow == row )
3550 {
3551 newRow = row - 1;
3552 }
3553
3554 MakeCellVisible( newRow, m_currentCellCoords.GetCol() );
3555 SetCurrentCell( newRow, m_currentCellCoords.GetCol() );
3556
3557 return TRUE;
3558 }
3559
3560 return FALSE;
3561 }
3562
3563 bool wxGrid::MovePageDown()
3564 {
3565 if ( m_currentCellCoords == wxGridNoCellCoords ) return FALSE;
3566
3567 int row = m_currentCellCoords.GetRow();
3568 if ( row < m_numRows )
3569 {
3570 int cw, ch;
3571 m_gridWin->GetClientSize( &cw, &ch );
3572
3573 int y = m_rowBottoms[ row ] - m_rowHeights[ row ];
3574 int newRow = YToRow( y + ch );
3575 if ( newRow == -1 )
3576 {
3577 newRow = m_numRows - 1;
3578 }
3579 else if ( newRow == row )
3580 {
3581 newRow = row + 1;
3582 }
3583
3584 MakeCellVisible( newRow, m_currentCellCoords.GetCol() );
3585 SetCurrentCell( newRow, m_currentCellCoords.GetCol() );
3586
3587 return TRUE;
3588 }
3589
3590 return FALSE;
3591 }
3592
3593 bool wxGrid::MoveCursorUpBlock()
3594 {
3595 if ( m_table &&
3596 m_currentCellCoords != wxGridNoCellCoords &&
3597 m_currentCellCoords.GetRow() > 0 )
3598 {
3599 int row = m_currentCellCoords.GetRow();
3600 int col = m_currentCellCoords.GetCol();
3601
3602 if ( m_table->IsEmptyCell(row, col) )
3603 {
3604 // starting in an empty cell: find the next block of
3605 // non-empty cells
3606 //
3607 while ( row > 0 )
3608 {
3609 row-- ;
3610 if ( !(m_table->IsEmptyCell(row, col)) ) break;
3611 }
3612 }
3613 else if ( m_table->IsEmptyCell(row-1, col) )
3614 {
3615 // starting at the top of a block: find the next block
3616 //
3617 row--;
3618 while ( row > 0 )
3619 {
3620 row-- ;
3621 if ( !(m_table->IsEmptyCell(row, col)) ) break;
3622 }
3623 }
3624 else
3625 {
3626 // starting within a block: find the top of the block
3627 //
3628 while ( row > 0 )
3629 {
3630 row-- ;
3631 if ( m_table->IsEmptyCell(row, col) )
3632 {
3633 row++ ;
3634 break;
3635 }
3636 }
3637 }
3638
3639 MakeCellVisible( row, col );
3640 SetCurrentCell( row, col );
3641
3642 return TRUE;
3643 }
3644
3645 return FALSE;
3646 }
3647
3648 bool wxGrid::MoveCursorDownBlock()
3649 {
3650 if ( m_table &&
3651 m_currentCellCoords != wxGridNoCellCoords &&
3652 m_currentCellCoords.GetRow() < m_numRows-1 )
3653 {
3654 int row = m_currentCellCoords.GetRow();
3655 int col = m_currentCellCoords.GetCol();
3656
3657 if ( m_table->IsEmptyCell(row, col) )
3658 {
3659 // starting in an empty cell: find the next block of
3660 // non-empty cells
3661 //
3662 while ( row < m_numRows-1 )
3663 {
3664 row++ ;
3665 if ( !(m_table->IsEmptyCell(row, col)) ) break;
3666 }
3667 }
3668 else if ( m_table->IsEmptyCell(row+1, col) )
3669 {
3670 // starting at the bottom of a block: find the next block
3671 //
3672 row++;
3673 while ( row < m_numRows-1 )
3674 {
3675 row++ ;
3676 if ( !(m_table->IsEmptyCell(row, col)) ) break;
3677 }
3678 }
3679 else
3680 {
3681 // starting within a block: find the bottom of the block
3682 //
3683 while ( row < m_numRows-1 )
3684 {
3685 row++ ;
3686 if ( m_table->IsEmptyCell(row, col) )
3687 {
3688 row-- ;
3689 break;
3690 }
3691 }
3692 }
3693
3694 MakeCellVisible( row, col );
3695 SetCurrentCell( row, col );
3696
3697 return TRUE;
3698 }
3699
3700 return FALSE;
3701 }
3702
3703 bool wxGrid::MoveCursorLeftBlock()
3704 {
3705 if ( m_table &&
3706 m_currentCellCoords != wxGridNoCellCoords &&
3707 m_currentCellCoords.GetCol() > 0 )
3708 {
3709 int row = m_currentCellCoords.GetRow();
3710 int col = m_currentCellCoords.GetCol();
3711
3712 if ( m_table->IsEmptyCell(row, col) )
3713 {
3714 // starting in an empty cell: find the next block of
3715 // non-empty cells
3716 //
3717 while ( col > 0 )
3718 {
3719 col-- ;
3720 if ( !(m_table->IsEmptyCell(row, col)) ) break;
3721 }
3722 }
3723 else if ( m_table->IsEmptyCell(row, col-1) )
3724 {
3725 // starting at the left of a block: find the next block
3726 //
3727 col--;
3728 while ( col > 0 )
3729 {
3730 col-- ;
3731 if ( !(m_table->IsEmptyCell(row, col)) ) break;
3732 }
3733 }
3734 else
3735 {
3736 // starting within a block: find the left of the block
3737 //
3738 while ( col > 0 )
3739 {
3740 col-- ;
3741 if ( m_table->IsEmptyCell(row, col) )
3742 {
3743 col++ ;
3744 break;
3745 }
3746 }
3747 }
3748
3749 MakeCellVisible( row, col );
3750 SetCurrentCell( row, col );
3751
3752 return TRUE;
3753 }
3754
3755 return FALSE;
3756 }
3757
3758 bool wxGrid::MoveCursorRightBlock()
3759 {
3760 if ( m_table &&
3761 m_currentCellCoords != wxGridNoCellCoords &&
3762 m_currentCellCoords.GetCol() < m_numCols-1 )
3763 {
3764 int row = m_currentCellCoords.GetRow();
3765 int col = m_currentCellCoords.GetCol();
3766
3767 if ( m_table->IsEmptyCell(row, col) )
3768 {
3769 // starting in an empty cell: find the next block of
3770 // non-empty cells
3771 //
3772 while ( col < m_numCols-1 )
3773 {
3774 col++ ;
3775 if ( !(m_table->IsEmptyCell(row, col)) ) break;
3776 }
3777 }
3778 else if ( m_table->IsEmptyCell(row, col+1) )
3779 {
3780 // starting at the right of a block: find the next block
3781 //
3782 col++;
3783 while ( col < m_numCols-1 )
3784 {
3785 col++ ;
3786 if ( !(m_table->IsEmptyCell(row, col)) ) break;
3787 }
3788 }
3789 else
3790 {
3791 // starting within a block: find the right of the block
3792 //
3793 while ( col < m_numCols-1 )
3794 {
3795 col++ ;
3796 if ( m_table->IsEmptyCell(row, col) )
3797 {
3798 col-- ;
3799 break;
3800 }
3801 }
3802 }
3803
3804 MakeCellVisible( row, col );
3805 SetCurrentCell( row, col );
3806
3807 return TRUE;
3808 }
3809
3810 return FALSE;
3811 }
3812
3813
3814
3815 //
3816 // ------ Label values and formatting
3817 //
3818
3819 void wxGrid::GetRowLabelAlignment( int *horiz, int *vert )
3820 {
3821 *horiz = m_rowLabelHorizAlign;
3822 *vert = m_rowLabelVertAlign;
3823 }
3824
3825 void wxGrid::GetColLabelAlignment( int *horiz, int *vert )
3826 {
3827 *horiz = m_colLabelHorizAlign;
3828 *vert = m_colLabelVertAlign;
3829 }
3830
3831 wxString wxGrid::GetRowLabelValue( int row )
3832 {
3833 if ( m_table )
3834 {
3835 return m_table->GetRowLabelValue( row );
3836 }
3837 else
3838 {
3839 wxString s;
3840 s << row;
3841 return s;
3842 }
3843 }
3844
3845 wxString wxGrid::GetColLabelValue( int col )
3846 {
3847 if ( m_table )
3848 {
3849 return m_table->GetColLabelValue( col );
3850 }
3851 else
3852 {
3853 wxString s;
3854 s << col;
3855 return s;
3856 }
3857 }
3858
3859 void wxGrid::SetRowLabelSize( int width )
3860 {
3861 // TODO: how to do this with the box sizers ?
3862 }
3863
3864 void wxGrid::SetColLabelSize( int height )
3865 {
3866 // TODO: how to do this with the box sizers ?
3867 }
3868
3869 void wxGrid::SetLabelBackgroundColour( const wxColour& colour )
3870 {
3871 if ( m_labelBackgroundColour != colour )
3872 {
3873 m_labelBackgroundColour = colour;
3874 m_rowLabelWin->SetBackgroundColour( colour );
3875 m_colLabelWin->SetBackgroundColour( colour );
3876 m_cornerLabelWin->SetBackgroundColour( colour );
3877
3878 if ( !GetBatchCount() )
3879 {
3880 m_rowLabelWin->Refresh();
3881 m_colLabelWin->Refresh();
3882 m_cornerLabelWin->Refresh();
3883 }
3884 }
3885 }
3886
3887 void wxGrid::SetLabelTextColour( const wxColour& colour )
3888 {
3889 if ( m_labelTextColour != colour )
3890 {
3891 m_labelTextColour = colour;
3892 if ( !GetBatchCount() )
3893 {
3894 m_rowLabelWin->Refresh();
3895 m_colLabelWin->Refresh();
3896 }
3897 }
3898 }
3899
3900 void wxGrid::SetLabelFont( const wxFont& font )
3901 {
3902 m_labelFont = font;
3903 if ( !GetBatchCount() )
3904 {
3905 m_rowLabelWin->Refresh();
3906 m_colLabelWin->Refresh();
3907 }
3908 }
3909
3910 void wxGrid::SetRowLabelAlignment( int horiz, int vert )
3911 {
3912 if ( horiz == wxLEFT || horiz == wxCENTRE || horiz == wxRIGHT )
3913 {
3914 m_rowLabelHorizAlign = horiz;
3915 }
3916
3917 if ( vert == wxTOP || vert == wxCENTRE || vert == wxBOTTOM )
3918 {
3919 m_rowLabelVertAlign = vert;
3920 }
3921
3922 if ( !GetBatchCount() )
3923 {
3924 m_rowLabelWin->Refresh();
3925 m_colLabelWin->Refresh();
3926 }
3927 }
3928
3929 void wxGrid::SetColLabelAlignment( int horiz, int vert )
3930 {
3931 if ( horiz == wxLEFT || horiz == wxCENTRE || horiz == wxRIGHT )
3932 {
3933 m_colLabelHorizAlign = horiz;
3934 }
3935
3936 if ( vert == wxTOP || vert == wxCENTRE || vert == wxBOTTOM )
3937 {
3938 m_colLabelVertAlign = vert;
3939 }
3940
3941 if ( !GetBatchCount() )
3942 {
3943 m_rowLabelWin->Refresh();
3944 m_colLabelWin->Refresh();
3945 }
3946 }
3947
3948 void wxGrid::SetRowLabelValue( int row, const wxString& s )
3949 {
3950 if ( m_table )
3951 {
3952 m_table->SetRowLabelValue( row, s );
3953 if ( !GetBatchCount() )
3954 {
3955 // TODO: Optimize this
3956 //
3957 m_rowLabelWin->Refresh();
3958 }
3959 }
3960 }
3961
3962 void wxGrid::SetColLabelValue( int col, const wxString& s )
3963 {
3964 if ( m_table )
3965 {
3966 m_table->SetColLabelValue( col, s );
3967 if ( !GetBatchCount() )
3968 {
3969 // TODO: optimize this
3970 //
3971 m_colLabelWin->Refresh();
3972 }
3973 }
3974 }
3975
3976 void wxGrid::SetGridLineColour( const wxColour& colour )
3977 {
3978 if ( m_gridLineColour != colour )
3979 {
3980 m_gridLineColour = colour;
3981
3982 wxClientDC dc( m_gridWin );
3983 PrepareDC( dc );
3984 DrawAllGridLines( dc );
3985 }
3986 }
3987
3988 void wxGrid::EnableGridLines( bool enable )
3989 {
3990 if ( enable != m_gridLinesEnabled )
3991 {
3992 m_gridLinesEnabled = enable;
3993
3994 if ( !GetBatchCount() )
3995 {
3996 if ( enable )
3997 {
3998 wxClientDC dc( m_gridWin );
3999 PrepareDC( dc );
4000 DrawAllGridLines( dc );
4001 }
4002 else
4003 {
4004 m_gridWin->Refresh();
4005 }
4006 }
4007 }
4008 }
4009
4010
4011 int wxGrid::GetDefaultRowSize()
4012 {
4013 return m_defaultRowHeight;
4014 }
4015
4016 int wxGrid::GetRowSize( int row )
4017 {
4018 if ( row >= 0 && row < m_numRows )
4019 return m_rowHeights[row];
4020 else
4021 return 0; // TODO: log an error here
4022 }
4023
4024 int wxGrid::GetDefaultColSize()
4025 {
4026 return m_defaultColWidth;
4027 }
4028
4029 int wxGrid::GetColSize( int col )
4030 {
4031 if ( col >= 0 && col < m_numCols )
4032 return m_colWidths[col];
4033 else
4034 return 0; // TODO: log an error here
4035 }
4036
4037 wxColour wxGrid::GetDefaultCellBackgroundColour()
4038 {
4039 // TODO: replace this temp test code
4040 //
4041 return wxColour( 255, 255, 255 );
4042 }
4043
4044 wxColour wxGrid::GetCellBackgroundColour( int WXUNUSED(row), int WXUNUSED(col) )
4045 {
4046 // TODO: replace this temp test code
4047 //
4048 return wxColour( 255, 255, 255 );
4049 }
4050
4051 wxColour wxGrid::GetDefaultCellTextColour()
4052 {
4053 // TODO: replace this temp test code
4054 //
4055 return wxColour( 0, 0, 0 );
4056 }
4057
4058 wxColour wxGrid::GetCellTextColour( int WXUNUSED(row), int WXUNUSED(col) )
4059 {
4060 // TODO: replace this temp test code
4061 //
4062 return wxColour( 0, 0, 0 );
4063 }
4064
4065
4066 wxFont wxGrid::GetDefaultCellFont()
4067 {
4068 return m_defaultCellFont;
4069 }
4070
4071 wxFont wxGrid::GetCellFont( int WXUNUSED(row), int WXUNUSED(col) )
4072 {
4073 // TODO: replace this temp test code
4074 //
4075 return m_defaultCellFont;
4076 }
4077
4078 void wxGrid::GetDefaultCellAlignment( int *horiz, int *vert )
4079 {
4080 // TODO: replace this temp test code
4081 //
4082 *horiz = wxLEFT;
4083 *vert = wxTOP;
4084 }
4085
4086 void wxGrid::GetCellAlignment( int WXUNUSED(row), int WXUNUSED(col), int *horiz, int *vert )
4087 {
4088 // TODO: replace this temp test code
4089 //
4090 *horiz = wxLEFT;
4091 *vert = wxTOP;
4092 }
4093
4094 void wxGrid::SetDefaultRowSize( int height, bool resizeExistingRows )
4095 {
4096 m_defaultRowHeight = wxMax( height, WXGRID_MIN_ROW_HEIGHT );
4097
4098 if ( resizeExistingRows )
4099 {
4100 int row;
4101 int bottom = 0;
4102 for ( row = 0; row < m_numRows; row++ )
4103 {
4104 m_rowHeights[row] = m_defaultRowHeight;
4105 bottom += m_defaultRowHeight;
4106 m_rowBottoms[row] = bottom;
4107 }
4108 CalcDimensions();
4109 }
4110 }
4111
4112 void wxGrid::SetRowSize( int row, int height )
4113 {
4114 int i;
4115
4116 if ( row >= 0 && row < m_numRows )
4117 {
4118 int h = wxMax( 0, height );
4119 int diff = h - m_rowHeights[row];
4120
4121 m_rowHeights[row] = h;
4122 for ( i = row; i < m_numRows; i++ )
4123 {
4124 m_rowBottoms[i] += diff;
4125 }
4126 CalcDimensions();
4127
4128 // Note: we are ending the event *after* doing
4129 // default processing in this case
4130 //
4131 SendEvent( EVT_GRID_ROW_SIZE,
4132 row, -1 );
4133 }
4134 else
4135 {
4136 // TODO: log an error here
4137 }
4138 }
4139
4140 void wxGrid::SetDefaultColSize( int width, bool resizeExistingCols )
4141 {
4142 m_defaultColWidth = wxMax( width, WXGRID_MIN_COL_WIDTH );
4143
4144 if ( resizeExistingCols )
4145 {
4146 int col;
4147 int right = 0;
4148 for ( col = 0; col < m_numCols; col++ )
4149 {
4150 m_colWidths[col] = m_defaultColWidth;
4151 right += m_defaultColWidth;
4152 m_colRights[col] = right;
4153 }
4154 CalcDimensions();
4155 }
4156 }
4157
4158 void wxGrid::SetColSize( int col, int width )
4159 {
4160 int i;
4161
4162 if ( col >= 0 && col < m_numCols )
4163 {
4164 int w = wxMax( 0, width );
4165 int diff = w - m_colWidths[col];
4166 m_colWidths[col] = w;
4167
4168 for ( i = col; i < m_numCols; i++ )
4169 {
4170 m_colRights[i] += diff;
4171 }
4172 CalcDimensions();
4173
4174 // Note: we are ending the event *after* doing
4175 // default processing in this case
4176 //
4177 SendEvent( EVT_GRID_COL_SIZE,
4178 -1, col );
4179 }
4180 else
4181 {
4182 // TODO: log an error here
4183 }
4184 }
4185
4186 void wxGrid::SetDefaultCellBackgroundColour( const wxColour& )
4187 {
4188 // TODO: everything !!!
4189 //
4190 }
4191
4192 void wxGrid::SetCellBackgroundColour( int WXUNUSED(row), int WXUNUSED(col), const wxColour& )
4193 {
4194 // TODO: everything !!!
4195 //
4196 }
4197
4198 void wxGrid::SetDefaultCellTextColour( const wxColour& )
4199 {
4200 // TODO: everything !!!
4201 //
4202 }
4203
4204 void wxGrid::SetCellTextColour( int WXUNUSED(row), int WXUNUSED(col), const wxColour& )
4205 {
4206 // TODO: everything !!!
4207 //
4208 }
4209
4210 void wxGrid::SetDefaultCellFont( const wxFont& )
4211 {
4212 // TODO: everything !!!
4213 //
4214 }
4215
4216 void wxGrid::SetCellFont( int WXUNUSED(row), int WXUNUSED(col), const wxFont& )
4217 {
4218 // TODO: everything !!!
4219 //
4220 }
4221
4222 void wxGrid::SetDefaultCellAlignment( int WXUNUSED(horiz), int WXUNUSED(vert) )
4223 {
4224 // TODO: everything !!!
4225 //
4226 }
4227
4228 void wxGrid::SetCellAlignment( int WXUNUSED(row), int WXUNUSED(col), int WXUNUSED(horiz), int WXUNUSED(vert) )
4229 {
4230 // TODO: everything !!!
4231 //
4232 }
4233
4234
4235
4236 //
4237 // ------ cell value accessor functions
4238 //
4239
4240 void wxGrid::SetCellValue( int row, int col, const wxString& s )
4241 {
4242 if ( m_table )
4243 {
4244 m_table->SetValue( row, col, s.c_str() );
4245 if ( !GetBatchCount() )
4246 {
4247 wxClientDC dc( m_gridWin );
4248 PrepareDC( dc );
4249 DrawCell( dc, wxGridCellCoords(row, col) );
4250 }
4251
4252 #if 0 // TODO: edit in place
4253
4254 if ( m_currentCellCoords.GetRow() == row &&
4255 m_currentCellCoords.GetCol() == col )
4256 {
4257 SetEditControlValue( s );
4258 }
4259 #endif
4260
4261 }
4262 }
4263
4264
4265 //
4266 // ------ Block, row and col selection
4267 //
4268
4269 void wxGrid::SelectRow( int row, bool addToSelected )
4270 {
4271 wxRect r;
4272
4273 if ( IsSelection() && addToSelected )
4274 {
4275 if ( m_selectedTopLeft.GetRow() > row )
4276 m_selectedTopLeft.SetRow( row );
4277
4278 m_selectedTopLeft.SetCol( 0 );
4279
4280 if ( m_selectedBottomRight.GetRow() < row )
4281 m_selectedBottomRight.SetRow( row );
4282
4283 m_selectedBottomRight.SetCol( m_numCols - 1 );
4284
4285 // TODO: optimize this so that we only refresh the newly
4286 // selected cells
4287 //
4288 r = SelectionToDeviceRect();
4289 if ( r != wxGridNoCellRect ) m_gridWin->Refresh( TRUE, &r );
4290 }
4291 else
4292 {
4293 r = SelectionToDeviceRect();
4294 ClearSelection();
4295 if ( r != wxGridNoCellRect ) m_gridWin->Refresh( TRUE, &r );
4296
4297 m_selectedTopLeft.Set( row, 0 );
4298 m_selectedBottomRight.Set( row, m_numCols-1 );
4299 r = SelectionToDeviceRect();
4300 m_gridWin->Refresh( TRUE, &r );
4301 }
4302
4303 wxGridRangeSelectEvent gridEvt( GetId(),
4304 EVT_GRID_RANGE_SELECT,
4305 this,
4306 m_selectedTopLeft,
4307 m_selectedBottomRight );
4308
4309 GetEventHandler()->ProcessEvent(gridEvt);
4310 }
4311
4312
4313 void wxGrid::SelectCol( int col, bool addToSelected )
4314 {
4315 wxRect r;
4316
4317 if ( IsSelection() && addToSelected )
4318 {
4319 if ( m_selectedTopLeft.GetCol() > col )
4320 m_selectedTopLeft.SetCol( col );
4321
4322 m_selectedTopLeft.SetRow( 0 );
4323
4324 if ( m_selectedBottomRight.GetCol() < col )
4325 m_selectedBottomRight.SetCol( col );
4326
4327 m_selectedBottomRight.SetRow( m_numRows - 1 );
4328
4329 // TODO: optimize this so that we only refresh the newly
4330 // selected cells
4331 //
4332 r = SelectionToDeviceRect();
4333 if ( r != wxGridNoCellRect ) m_gridWin->Refresh( TRUE, &r );
4334 }
4335 else
4336 {
4337 r = SelectionToDeviceRect();
4338 ClearSelection();
4339 if ( r != wxGridNoCellRect ) m_gridWin->Refresh( TRUE, &r );
4340
4341 m_selectedTopLeft.Set( 0, col );
4342 m_selectedBottomRight.Set( m_numRows-1, col );
4343 r = SelectionToDeviceRect();
4344 m_gridWin->Refresh( TRUE, &r );
4345 }
4346
4347 wxGridRangeSelectEvent gridEvt( GetId(),
4348 EVT_GRID_RANGE_SELECT,
4349 this,
4350 m_selectedTopLeft,
4351 m_selectedBottomRight );
4352
4353 GetEventHandler()->ProcessEvent(gridEvt);
4354 }
4355
4356
4357 void wxGrid::SelectBlock( int topRow, int leftCol, int bottomRow, int rightCol )
4358 {
4359 int temp;
4360 bool changed = FALSE;
4361 wxGridCellCoords updateTopLeft, updateBottomRight;
4362
4363 if ( topRow > bottomRow )
4364 {
4365 temp = topRow;
4366 topRow = bottomRow;
4367 bottomRow = temp;
4368 }
4369
4370 if ( leftCol > rightCol )
4371 {
4372 temp = leftCol;
4373 leftCol = rightCol;
4374 rightCol = temp;
4375 }
4376
4377 updateTopLeft = m_selectedTopLeft;
4378 if (m_selectedTopLeft != wxGridCellCoords( topRow, leftCol ) )
4379 {
4380 m_selectedTopLeft = wxGridCellCoords( topRow, leftCol );
4381 if (updateTopLeft == wxGridNoCellCoords)
4382 {
4383 updateTopLeft = m_selectedTopLeft;
4384 }
4385 else
4386 {
4387 if(updateTopLeft.GetRow() > topRow)
4388 updateTopLeft.SetRow(topRow);
4389 if (updateTopLeft.GetCol() > leftCol)
4390 updateTopLeft.SetCol(leftCol);
4391 }
4392 changed = TRUE;
4393 }
4394
4395 updateBottomRight = m_selectedBottomRight;
4396 if (m_selectedBottomRight != wxGridCellCoords( bottomRow, rightCol ) )
4397 {
4398 m_selectedBottomRight = wxGridCellCoords( bottomRow, rightCol );
4399 if (updateBottomRight == wxGridNoCellCoords)
4400 {
4401 updateBottomRight = m_selectedBottomRight;
4402 }
4403 else
4404 {
4405 if (updateBottomRight.GetRow() < bottomRow)
4406 updateBottomRight.SetRow(bottomRow);
4407 if (updateBottomRight.GetCol() < rightCol)
4408 updateBottomRight.SetCol(rightCol);
4409 }
4410 changed = TRUE;
4411 }
4412
4413 if (changed)
4414 {
4415 wxRect r( BlockToDeviceRect( updateTopLeft, updateBottomRight ) );
4416 m_gridWin->Refresh( TRUE, &r );
4417 }
4418
4419 // only generate an event if the block is not being selected by
4420 // dragging the mouse (in which case the event will be generated in
4421 // the mouse event handler)
4422 if ( !m_isDragging )
4423 {
4424 wxGridRangeSelectEvent gridEvt( GetId(),
4425 EVT_GRID_RANGE_SELECT,
4426 this,
4427 m_selectedTopLeft,
4428 m_selectedBottomRight );
4429
4430 GetEventHandler()->ProcessEvent(gridEvt);
4431 }
4432 }
4433
4434 void wxGrid::SelectAll()
4435 {
4436 m_selectedTopLeft.Set( 0, 0 );
4437 m_selectedBottomRight.Set( m_numRows-1, m_numCols-1 );
4438
4439 m_gridWin->Refresh();
4440 }
4441
4442
4443 void wxGrid::ClearSelection()
4444 {
4445 m_selectedTopLeft = wxGridNoCellCoords;
4446 m_selectedBottomRight = wxGridNoCellCoords;
4447 }
4448
4449
4450 // This function returns the rectangle that encloses the given block
4451 // in device coords clipped to the client size of the grid window.
4452 //
4453 wxRect wxGrid::BlockToDeviceRect( const wxGridCellCoords &topLeft,
4454 const wxGridCellCoords &bottomRight )
4455 {
4456 wxRect rect( wxGridNoCellRect );
4457 wxRect cellRect;
4458
4459 cellRect = CellToRect( topLeft );
4460 if ( cellRect != wxGridNoCellRect )
4461 {
4462 rect = cellRect;
4463 }
4464 else
4465 {
4466 rect = wxRect( 0, 0, 0, 0 );
4467 }
4468
4469 cellRect = CellToRect( bottomRight );
4470 if ( cellRect != wxGridNoCellRect )
4471 {
4472 rect += cellRect;
4473 }
4474 else
4475 {
4476 return wxGridNoCellRect;
4477 }
4478
4479 // convert to scrolled coords
4480 //
4481 int left, top, right, bottom;
4482 CalcScrolledPosition( rect.GetLeft(), rect.GetTop(), &left, &top );
4483 CalcScrolledPosition( rect.GetRight(), rect.GetBottom(), &right, &bottom );
4484
4485 int cw, ch;
4486 m_gridWin->GetClientSize( &cw, &ch );
4487
4488 rect.SetLeft( wxMax(0, left) );
4489 rect.SetTop( wxMax(0, top) );
4490 rect.SetRight( wxMin(cw, right) );
4491 rect.SetBottom( wxMin(ch, bottom) );
4492
4493 return rect;
4494 }
4495
4496
4497
4498 //
4499 // ------ Grid event classes
4500 //
4501
4502 IMPLEMENT_DYNAMIC_CLASS( wxGridEvent, wxEvent )
4503
4504 wxGridEvent::wxGridEvent( int id, wxEventType type, wxObject* obj,
4505 int row, int col, int x, int y,
4506 bool control, bool shift, bool alt, bool meta )
4507 : wxNotifyEvent( type, id )
4508 {
4509 m_row = row;
4510 m_col = col;
4511 m_x = x;
4512 m_y = y;
4513 m_control = control;
4514 m_shift = shift;
4515 m_alt = alt;
4516 m_meta = meta;
4517
4518 SetEventObject(obj);
4519 }
4520
4521
4522 IMPLEMENT_DYNAMIC_CLASS( wxGridSizeEvent, wxEvent )
4523
4524 wxGridSizeEvent::wxGridSizeEvent( int id, wxEventType type, wxObject* obj,
4525 int rowOrCol, int x, int y,
4526 bool control, bool shift, bool alt, bool meta )
4527 : wxNotifyEvent( type, id )
4528 {
4529 m_rowOrCol = rowOrCol;
4530 m_x = x;
4531 m_y = y;
4532 m_control = control;
4533 m_shift = shift;
4534 m_alt = alt;
4535 m_meta = meta;
4536
4537 SetEventObject(obj);
4538 }
4539
4540
4541 IMPLEMENT_DYNAMIC_CLASS( wxGridRangeSelectEvent, wxEvent )
4542
4543 wxGridRangeSelectEvent::wxGridRangeSelectEvent(int id, wxEventType type, wxObject* obj,
4544 const wxGridCellCoords& topLeft,
4545 const wxGridCellCoords& bottomRight,
4546 bool control, bool shift, bool alt, bool meta )
4547 : wxNotifyEvent( type, id )
4548 {
4549 m_topLeft = topLeft;
4550 m_bottomRight = bottomRight;
4551 m_control = control;
4552 m_shift = shift;
4553 m_alt = alt;
4554 m_meta = meta;
4555
4556 SetEventObject(obj);
4557 }
4558
4559
4560 #endif // ifndef wxUSE_NEW_GRID
4561