]> git.saurik.com Git - wxWidgets.git/blob - src/generic/grid.cpp
a999ea3e08a6717f7f331a6dacd0caf4e4ddf0b5
[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 WXGRID_DRAW_LINES
40 #define WXGRID_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 int 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 int 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, 0, "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 WXGRID_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 ] + 50;
1128 int bottom = m_rowBottoms[ m_numRows-1 ] + 50;
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 // Only needed to get the correct rect.y:
1596 wxRect rect ( CellToRect( m_dragRowOrCol, 0 ) );
1597 rect.x = 0;
1598 CalcScrolledPosition(0, rect.y, &dummy, &rect.y);
1599 rect.width = m_rowLabelWidth;
1600 rect.height = ch - rect.y;
1601 m_rowLabelWin->Refresh( TRUE, &rect );
1602 rect.width = cw;
1603 m_gridWin->Refresh( FALSE, &rect );
1604 }
1605
1606 ShowCellEditControl();
1607
1608 // Note: we are ending the event *after* doing
1609 // default processing in this case
1610 //
1611 SendEvent( EVT_GRID_ROW_SIZE, m_dragRowOrCol, -1, event );
1612 }
1613 }
1614
1615 m_cursorMode = WXGRID_CURSOR_SELECT_CELL;
1616 m_dragLastPos = -1;
1617 }
1618
1619
1620 // ------------ Right button down
1621 //
1622 else if ( event.RightDown() )
1623 {
1624 row = YToRow(y);
1625 if ( !SendEvent( EVT_GRID_LABEL_RIGHT_CLICK, row, -1, event ) )
1626 {
1627 // no default action at the moment
1628 }
1629 }
1630
1631
1632 // ------------ Right double click
1633 //
1634 else if ( event.RightDClick() )
1635 {
1636 row = YToRow(y);
1637 if ( !SendEvent( EVT_GRID_LABEL_RIGHT_DCLICK, row, -1, event ) )
1638 {
1639 // no default action at the moment
1640 }
1641 }
1642
1643
1644 // ------------ No buttons down and mouse moving
1645 //
1646 else if ( event.Moving() )
1647 {
1648 m_dragRowOrCol = YToEdgeOfRow( y );
1649 if ( m_dragRowOrCol >= 0 )
1650 {
1651 if ( m_cursorMode == WXGRID_CURSOR_SELECT_CELL )
1652 {
1653 m_cursorMode = WXGRID_CURSOR_RESIZE_ROW;
1654 m_rowLabelWin->SetCursor( m_rowResizeCursor );
1655 }
1656 }
1657 else
1658 {
1659 m_cursorMode = WXGRID_CURSOR_SELECT_CELL;
1660 if ( m_rowLabelWin->GetCursor() == m_rowResizeCursor )
1661 m_rowLabelWin->SetCursor( *wxSTANDARD_CURSOR );
1662 }
1663 }
1664 }
1665
1666
1667 void wxGrid::ProcessColLabelMouseEvent( wxMouseEvent& event )
1668 {
1669 int x, y, col;
1670 wxPoint pos( event.GetPosition() );
1671 CalcUnscrolledPosition( pos.x, pos.y, &x, &y );
1672
1673 if ( event.Dragging() )
1674 {
1675 m_isDragging = TRUE;
1676
1677 if ( event.LeftIsDown() )
1678 {
1679 switch( m_cursorMode )
1680 {
1681 case WXGRID_CURSOR_RESIZE_COL:
1682 {
1683 int cw, ch, dummy, top;
1684 m_gridWin->GetClientSize( &cw, &ch );
1685 CalcUnscrolledPosition( 0, 0, &dummy, &top );
1686
1687 wxClientDC dc( m_gridWin );
1688 PrepareDC( dc );
1689 dc.SetLogicalFunction(wxINVERT);
1690 if ( m_dragLastPos >= 0 )
1691 {
1692 dc.DrawLine( m_dragLastPos, top, m_dragLastPos, top+ch );
1693 }
1694 dc.DrawLine( x, top, x, top+ch );
1695 m_dragLastPos = x;
1696 }
1697 break;
1698
1699 case WXGRID_CURSOR_SELECT_COL:
1700 {
1701 if ( (col = XToCol( x )) >= 0 &&
1702 !IsInSelection( 0, col ) )
1703 {
1704 SelectCol( col, TRUE );
1705 }
1706 }
1707 break;
1708 }
1709 }
1710 return;
1711 }
1712
1713 m_isDragging = FALSE;
1714
1715
1716 // ------------ Left button pressed
1717 //
1718 if ( event.LeftDown() )
1719 {
1720 // don't send a label click event for a hit on the
1721 // edge of the col label - this is probably the user
1722 // wanting to resize the col
1723 //
1724 if ( XToEdgeOfCol(x) < 0 )
1725 {
1726 col = XToCol(x);
1727 if ( col >= 0 &&
1728 !SendEvent( EVT_GRID_LABEL_LEFT_CLICK, -1, col, event ) )
1729 {
1730 SelectCol( col, event.ShiftDown() );
1731 m_cursorMode = WXGRID_CURSOR_SELECT_COL;
1732 }
1733 }
1734 else
1735 {
1736 // starting to drag-resize a col
1737 //
1738 m_colLabelWin->CaptureMouse();
1739 }
1740 }
1741
1742
1743 // ------------ Left double click
1744 //
1745 if ( event.LeftDClick() )
1746 {
1747 if ( XToEdgeOfCol(x) < 0 )
1748 {
1749 col = XToCol(x);
1750 SendEvent( EVT_GRID_LABEL_LEFT_DCLICK, -1, col, event );
1751 }
1752 }
1753
1754
1755 // ------------ Left button released
1756 //
1757 else if ( event.LeftUp() )
1758 {
1759 if ( m_cursorMode == WXGRID_CURSOR_RESIZE_COL )
1760 {
1761 m_colLabelWin->ReleaseMouse();
1762
1763 if ( m_dragLastPos >= 0 )
1764 {
1765 // erase the last line and resize the col
1766 //
1767 int cw, ch, dummy, top;
1768 m_gridWin->GetClientSize( &cw, &ch );
1769 CalcUnscrolledPosition( 0, 0, &dummy, &top );
1770
1771 wxClientDC dc( m_gridWin );
1772 PrepareDC( dc );
1773 dc.SetLogicalFunction( wxINVERT );
1774 dc.DrawLine( m_dragLastPos, top, m_dragLastPos, top+ch );
1775 HideCellEditControl();
1776
1777 int colLeft = m_colRights[m_dragRowOrCol] - m_colWidths[m_dragRowOrCol];
1778 SetColSize( m_dragRowOrCol, wxMax( x - colLeft, WXGRID_MIN_COL_WIDTH ) );
1779
1780 if ( !GetBatchCount() )
1781 {
1782 // Only needed to get the correct rect.x:
1783 wxRect rect ( CellToRect( 0, m_dragRowOrCol ) );
1784 rect.y = 0;
1785 CalcScrolledPosition(rect.x, 0, &rect.x, &dummy);
1786 rect.width = cw - rect.x;
1787 rect.height = m_colLabelHeight;
1788 m_colLabelWin->Refresh( TRUE, &rect );
1789 rect.height = ch;
1790 m_gridWin->Refresh( FALSE, &rect );
1791 }
1792
1793 ShowCellEditControl();
1794
1795 // Note: we are ending the event *after* doing
1796 // default processing in this case
1797 //
1798 SendEvent( EVT_GRID_COL_SIZE, -1, m_dragRowOrCol, event );
1799 }
1800 }
1801
1802 m_cursorMode = WXGRID_CURSOR_SELECT_CELL;
1803 m_dragLastPos = -1;
1804 }
1805
1806
1807 // ------------ Right button down
1808 //
1809 else if ( event.RightDown() )
1810 {
1811 col = XToCol(x);
1812 if ( !SendEvent( EVT_GRID_LABEL_RIGHT_CLICK, -1, col, event ) )
1813 {
1814 // no default action at the moment
1815 }
1816 }
1817
1818
1819 // ------------ Right double click
1820 //
1821 else if ( event.RightDClick() )
1822 {
1823 col = XToCol(x);
1824 if ( !SendEvent( EVT_GRID_LABEL_RIGHT_DCLICK, -1, col, event ) )
1825 {
1826 // no default action at the moment
1827 }
1828 }
1829
1830
1831 // ------------ No buttons down and mouse moving
1832 //
1833 else if ( event.Moving() )
1834 {
1835 m_dragRowOrCol = XToEdgeOfCol( x );
1836 if ( m_dragRowOrCol >= 0 )
1837 {
1838 if ( m_cursorMode == WXGRID_CURSOR_SELECT_CELL )
1839 {
1840 m_cursorMode = WXGRID_CURSOR_RESIZE_COL;
1841 m_colLabelWin->SetCursor( m_colResizeCursor );
1842 }
1843 }
1844 else
1845 {
1846 m_cursorMode = WXGRID_CURSOR_SELECT_CELL;
1847 if ( m_colLabelWin->GetCursor() == m_colResizeCursor )
1848 m_colLabelWin->SetCursor( *wxSTANDARD_CURSOR );
1849 }
1850 }
1851 }
1852
1853
1854 void wxGrid::ProcessCornerLabelMouseEvent( wxMouseEvent& event )
1855 {
1856 if ( event.LeftDown() )
1857 {
1858 // indicate corner label by having both row and
1859 // col args == -1
1860 //
1861 if ( !SendEvent( EVT_GRID_LABEL_LEFT_CLICK, -1, -1, event ) )
1862 {
1863 SelectAll();
1864 }
1865 }
1866
1867 else if ( event.LeftDClick() )
1868 {
1869 SendEvent( EVT_GRID_LABEL_LEFT_DCLICK, -1, -1, event );
1870 }
1871
1872 else if ( event.RightDown() )
1873 {
1874 if ( !SendEvent( EVT_GRID_LABEL_RIGHT_CLICK, -1, -1, event ) )
1875 {
1876 // no default action at the moment
1877 }
1878 }
1879
1880 else if ( event.RightDClick() )
1881 {
1882 if ( !SendEvent( EVT_GRID_LABEL_RIGHT_DCLICK, -1, -1, event ) )
1883 {
1884 // no default action at the moment
1885 }
1886 }
1887 }
1888
1889
1890 void wxGrid::ProcessGridCellMouseEvent( wxMouseEvent& event )
1891 {
1892 int x, y;
1893 wxPoint pos( event.GetPosition() );
1894 CalcUnscrolledPosition( pos.x, pos.y, &x, &y );
1895
1896 wxGridCellCoords coords;
1897 XYToCell( x, y, coords );
1898
1899 if ( event.Dragging() )
1900 {
1901 m_isDragging = TRUE;
1902 if ( m_cursorMode == WXGRID_CURSOR_SELECT_CELL )
1903 {
1904 // Hide the edit control, so it
1905 // won't interfer with drag-shrinking.
1906 if ( IsCellEditControlEnabled() )
1907 HideCellEditControl();
1908 if ( coords != wxGridNoCellCoords )
1909 {
1910 if ( !IsSelection() )
1911 {
1912 SelectBlock( coords, coords );
1913 }
1914 else
1915 {
1916 SelectBlock( m_currentCellCoords, coords );
1917 }
1918 }
1919 }
1920
1921 return;
1922 }
1923
1924 m_isDragging = FALSE;
1925
1926 if ( coords != wxGridNoCellCoords )
1927 {
1928 if ( event.LeftDown() )
1929 {
1930 if ( event.ShiftDown() )
1931 {
1932 SelectBlock( m_currentCellCoords, coords );
1933 }
1934 else
1935 {
1936 if ( !SendEvent( EVT_GRID_CELL_LEFT_CLICK,
1937 coords.GetRow(),
1938 coords.GetCol(),
1939 event ) )
1940 {
1941 MakeCellVisible( coords );
1942 SetCurrentCell( coords );
1943 }
1944 }
1945 }
1946
1947
1948 // ------------ Left double click
1949 //
1950 else if ( event.LeftDClick() )
1951 {
1952 SendEvent( EVT_GRID_CELL_LEFT_DCLICK,
1953 coords.GetRow(),
1954 coords.GetCol(),
1955 event );
1956 }
1957
1958
1959 // ------------ Left button released
1960 //
1961 else if ( event.LeftUp() )
1962 {
1963 if ( m_cursorMode == WXGRID_CURSOR_SELECT_CELL )
1964 {
1965 if ( IsSelection() )
1966 {
1967 SendEvent( EVT_GRID_RANGE_SELECT, -1, -1, event );
1968 }
1969 }
1970
1971 // Show the edit control, if it has
1972 // been hidden for drag-shrinking.
1973 if ( IsCellEditControlEnabled() )
1974 ShowCellEditControl();
1975
1976 m_dragLastPos = -1;
1977 }
1978
1979
1980 // ------------ Right button down
1981 //
1982 else if ( event.RightDown() )
1983 {
1984 if ( !SendEvent( EVT_GRID_CELL_RIGHT_CLICK,
1985 coords.GetRow(),
1986 coords.GetCol(),
1987 event ) )
1988 {
1989 // no default action at the moment
1990 }
1991 }
1992
1993
1994 // ------------ Right double click
1995 //
1996 else if ( event.RightDClick() )
1997 {
1998 if ( !SendEvent( EVT_GRID_CELL_RIGHT_DCLICK,
1999 coords.GetRow(),
2000 coords.GetCol(),
2001 event ) )
2002 {
2003 // no default action at the moment
2004 }
2005 }
2006
2007 // ------------ Moving and no button action
2008 //
2009 else if ( event.Moving() && !event.IsButton() )
2010 {
2011 m_cursorMode = WXGRID_CURSOR_SELECT_CELL;
2012 }
2013 }
2014 }
2015
2016
2017 //
2018 // ------ interaction with data model
2019 //
2020 bool wxGrid::ProcessTableMessage( wxGridTableMessage& msg )
2021 {
2022 switch ( msg.GetId() )
2023 {
2024 case wxGRIDTABLE_REQUEST_VIEW_GET_VALUES:
2025 return GetModelValues();
2026
2027 case wxGRIDTABLE_REQUEST_VIEW_SEND_VALUES:
2028 return SetModelValues();
2029
2030 case wxGRIDTABLE_NOTIFY_ROWS_INSERTED:
2031 case wxGRIDTABLE_NOTIFY_ROWS_APPENDED:
2032 case wxGRIDTABLE_NOTIFY_ROWS_DELETED:
2033 case wxGRIDTABLE_NOTIFY_COLS_INSERTED:
2034 case wxGRIDTABLE_NOTIFY_COLS_APPENDED:
2035 case wxGRIDTABLE_NOTIFY_COLS_DELETED:
2036 return Redimension( msg );
2037
2038 default:
2039 return FALSE;
2040 }
2041 }
2042
2043
2044
2045 // The behaviour of this function depends on the grid table class
2046 // Clear() function. For the default wxGridStringTable class the
2047 // behavious is to replace all cell contents with wxEmptyString but
2048 // not to change the number of rows or cols.
2049 //
2050 void wxGrid::ClearGrid()
2051 {
2052 if ( m_table )
2053 {
2054 m_table->Clear();
2055 SetEditControlValue();
2056 if ( !GetBatchCount() ) m_gridWin->Refresh();
2057 }
2058 }
2059
2060
2061 bool wxGrid::InsertRows( int pos, int numRows, bool WXUNUSED(updateLabels) )
2062 {
2063 // TODO: something with updateLabels flag
2064
2065 if ( !m_created )
2066 {
2067 wxLogError( wxT("Called wxGrid::InsertRows() before calling CreateGrid()") );
2068 return FALSE;
2069 }
2070
2071 if ( m_table )
2072 {
2073 bool ok = m_table->InsertRows( pos, numRows );
2074
2075 // the table will have sent the results of the insert row
2076 // operation to this view object as a grid table message
2077 //
2078 if ( ok )
2079 {
2080 if ( m_numCols == 0 )
2081 {
2082 m_table->AppendCols( WXGRID_DEFAULT_NUMBER_COLS );
2083 //
2084 // TODO: perhaps instead of appending the default number of cols
2085 // we should remember what the last non-zero number of cols was ?
2086 //
2087 }
2088
2089 if ( m_currentCellCoords == wxGridNoCellCoords )
2090 {
2091 // if we have just inserted cols into an empty grid the current
2092 // cell will be undefined...
2093 //
2094 SetCurrentCell( 0, 0 );
2095 }
2096
2097 ClearSelection();
2098 if ( !GetBatchCount() ) Refresh();
2099 }
2100
2101 SetEditControlValue();
2102 return ok;
2103 }
2104 else
2105 {
2106 return FALSE;
2107 }
2108 }
2109
2110
2111 bool wxGrid::AppendRows( int numRows, bool WXUNUSED(updateLabels) )
2112 {
2113 // TODO: something with updateLabels flag
2114
2115 if ( !m_created )
2116 {
2117 wxLogError( wxT("Called wxGrid::AppendRows() before calling CreateGrid()") );
2118 return FALSE;
2119 }
2120
2121 if ( m_table && m_table->AppendRows( numRows ) )
2122 {
2123 if ( m_currentCellCoords == wxGridNoCellCoords )
2124 {
2125 // if we have just inserted cols into an empty grid the current
2126 // cell will be undefined...
2127 //
2128 SetCurrentCell( 0, 0 );
2129 }
2130
2131 // the table will have sent the results of the append row
2132 // operation to this view object as a grid table message
2133 //
2134 ClearSelection();
2135 if ( !GetBatchCount() ) Refresh();
2136 return TRUE;
2137 }
2138 else
2139 {
2140 return FALSE;
2141 }
2142 }
2143
2144
2145 bool wxGrid::DeleteRows( int pos, int numRows, bool WXUNUSED(updateLabels) )
2146 {
2147 // TODO: something with updateLabels flag
2148
2149 if ( !m_created )
2150 {
2151 wxLogError( wxT("Called wxGrid::DeleteRows() before calling CreateGrid()") );
2152 return FALSE;
2153 }
2154
2155 if ( m_table && m_table->DeleteRows( pos, numRows ) )
2156 {
2157 // the table will have sent the results of the delete row
2158 // operation to this view object as a grid table message
2159 //
2160 if ( m_numRows > 0 )
2161 SetEditControlValue();
2162 else
2163 HideCellEditControl();
2164
2165 ClearSelection();
2166 if ( !GetBatchCount() ) Refresh();
2167 return TRUE;
2168 }
2169 else
2170 {
2171 return FALSE;
2172 }
2173 }
2174
2175
2176 bool wxGrid::InsertCols( int pos, int numCols, bool WXUNUSED(updateLabels) )
2177 {
2178 // TODO: something with updateLabels flag
2179
2180 if ( !m_created )
2181 {
2182 wxLogError( wxT("Called wxGrid::InsertCols() before calling CreateGrid()") );
2183 return FALSE;
2184 }
2185
2186 if ( m_table )
2187 {
2188 HideCellEditControl();
2189 bool ok = m_table->InsertCols( pos, numCols );
2190
2191 // the table will have sent the results of the insert col
2192 // operation to this view object as a grid table message
2193 //
2194 if ( ok )
2195 {
2196 if ( m_currentCellCoords == wxGridNoCellCoords )
2197 {
2198 // if we have just inserted cols into an empty grid the current
2199 // cell will be undefined...
2200 //
2201 SetCurrentCell( 0, 0 );
2202 }
2203
2204 ClearSelection();
2205 if ( !GetBatchCount() ) Refresh();
2206 }
2207
2208 SetEditControlValue();
2209 return ok;
2210 }
2211 else
2212 {
2213 return FALSE;
2214 }
2215 }
2216
2217
2218 bool wxGrid::AppendCols( int numCols, bool WXUNUSED(updateLabels) )
2219 {
2220 // TODO: something with updateLabels flag
2221
2222 if ( !m_created )
2223 {
2224 wxLogError( wxT("Called wxGrid::AppendCols() before calling CreateGrid()") );
2225 return FALSE;
2226 }
2227
2228 if ( m_table && m_table->AppendCols( numCols ) )
2229 {
2230 // the table will have sent the results of the append col
2231 // operation to this view object as a grid table message
2232 //
2233 if ( m_currentCellCoords == wxGridNoCellCoords )
2234 {
2235 // if we have just inserted cols into an empty grid the current
2236 // cell will be undefined...
2237 //
2238 SetCurrentCell( 0, 0 );
2239 }
2240
2241 ClearSelection();
2242 if ( !GetBatchCount() ) Refresh();
2243 return TRUE;
2244 }
2245 else
2246 {
2247 return FALSE;
2248 }
2249 }
2250
2251
2252 bool wxGrid::DeleteCols( int pos, int numCols, bool WXUNUSED(updateLabels) )
2253 {
2254 // TODO: something with updateLabels flag
2255
2256 if ( !m_created )
2257 {
2258 wxLogError( wxT("Called wxGrid::DeleteCols() before calling CreateGrid()") );
2259 return FALSE;
2260 }
2261
2262 if ( m_table && m_table->DeleteCols( pos, numCols ) )
2263 {
2264 // the table will have sent the results of the delete col
2265 // operation to this view object as a grid table message
2266 //
2267 if ( m_numCols > 0 )
2268 SetEditControlValue();
2269 else
2270 HideCellEditControl();
2271
2272 ClearSelection();
2273 if ( !GetBatchCount() ) Refresh();
2274 return TRUE;
2275 }
2276 else
2277 {
2278 return FALSE;
2279 }
2280 }
2281
2282
2283
2284 //
2285 // ----- event handlers
2286 //
2287
2288 // Generate a grid event based on a mouse event and
2289 // return the result of ProcessEvent()
2290 //
2291 bool wxGrid::SendEvent( const wxEventType type,
2292 int row, int col,
2293 wxMouseEvent& mouseEv )
2294 {
2295 if ( type == EVT_GRID_ROW_SIZE ||
2296 type == EVT_GRID_COL_SIZE )
2297 {
2298 int rowOrCol = (row == -1 ? col : row);
2299
2300 wxGridSizeEvent gridEvt( GetId(),
2301 type,
2302 this,
2303 rowOrCol,
2304 mouseEv.GetX(), mouseEv.GetY(),
2305 mouseEv.ControlDown(),
2306 mouseEv.ShiftDown(),
2307 mouseEv.AltDown(),
2308 mouseEv.MetaDown() );
2309
2310 return GetEventHandler()->ProcessEvent(gridEvt);
2311 }
2312 else if ( type == EVT_GRID_RANGE_SELECT )
2313 {
2314 wxGridRangeSelectEvent gridEvt( GetId(),
2315 type,
2316 this,
2317 m_selectedTopLeft,
2318 m_selectedBottomRight,
2319 mouseEv.ControlDown(),
2320 mouseEv.ShiftDown(),
2321 mouseEv.AltDown(),
2322 mouseEv.MetaDown() );
2323
2324 return GetEventHandler()->ProcessEvent(gridEvt);
2325 }
2326 else
2327 {
2328 wxGridEvent gridEvt( GetId(),
2329 type,
2330 this,
2331 row, col,
2332 mouseEv.GetX(), mouseEv.GetY(),
2333 mouseEv.ControlDown(),
2334 mouseEv.ShiftDown(),
2335 mouseEv.AltDown(),
2336 mouseEv.MetaDown() );
2337
2338 return GetEventHandler()->ProcessEvent(gridEvt);
2339 }
2340 }
2341
2342
2343 // Generate a grid event of specified type and return the result
2344 // of ProcessEvent().
2345 //
2346 bool wxGrid::SendEvent( const wxEventType type,
2347 int row, int col )
2348 {
2349 if ( type == EVT_GRID_ROW_SIZE ||
2350 type == EVT_GRID_COL_SIZE )
2351 {
2352 int rowOrCol = (row == -1 ? col : row);
2353
2354 wxGridSizeEvent gridEvt( GetId(),
2355 type,
2356 this,
2357 rowOrCol );
2358
2359 return GetEventHandler()->ProcessEvent(gridEvt);
2360 }
2361 else
2362 {
2363 wxGridEvent gridEvt( GetId(),
2364 type,
2365 this,
2366 row, col );
2367
2368 return GetEventHandler()->ProcessEvent(gridEvt);
2369 }
2370 }
2371
2372
2373 void wxGrid::OnPaint( wxPaintEvent& WXUNUSED(event) )
2374 {
2375 wxPaintDC dc( this );
2376
2377 if ( m_currentCellCoords == wxGridNoCellCoords &&
2378 m_numRows && m_numCols )
2379 {
2380 m_currentCellCoords.Set(0, 0);
2381 SetEditControlValue();
2382 ShowCellEditControl();
2383 }
2384 }
2385
2386
2387 // This is just here to make sure that CalcDimensions gets called when
2388 // the grid view is resized... then the size event is skipped to allow
2389 // the box sizers to handle everything
2390 //
2391 void wxGrid::OnSize( wxSizeEvent& event )
2392 {
2393 CalcDimensions();
2394 event.Skip();
2395 }
2396
2397
2398 void wxGrid::OnKeyDown( wxKeyEvent& event )
2399 {
2400 if ( m_inOnKeyDown )
2401 {
2402 // shouldn't be here - we are going round in circles...
2403 //
2404 wxLogFatalError( wxT("wxGrid::OnKeyDown called while alread active") );
2405 }
2406
2407 m_inOnKeyDown = TRUE;
2408
2409 // propagate the event up and see if it gets processed
2410 //
2411 wxWindow *parent = GetParent();
2412 wxKeyEvent keyEvt( event );
2413 keyEvt.SetEventObject( parent );
2414
2415 if ( !parent->GetEventHandler()->ProcessEvent( keyEvt ) )
2416 {
2417 // try local handlers
2418 //
2419 switch ( event.KeyCode() )
2420 {
2421 case WXK_UP:
2422 if ( event.ControlDown() )
2423 {
2424 MoveCursorUpBlock();
2425 }
2426 else
2427 {
2428 MoveCursorUp();
2429 }
2430 break;
2431
2432 case WXK_DOWN:
2433 if ( event.ControlDown() )
2434 {
2435 MoveCursorDownBlock();
2436 }
2437 else
2438 {
2439 MoveCursorDown();
2440 }
2441 break;
2442
2443 case WXK_LEFT:
2444 if ( event.ControlDown() )
2445 {
2446 MoveCursorLeftBlock();
2447 }
2448 else
2449 {
2450 MoveCursorLeft();
2451 }
2452 break;
2453
2454 case WXK_RIGHT:
2455 if ( event.ControlDown() )
2456 {
2457 MoveCursorRightBlock();
2458 }
2459 else
2460 {
2461 MoveCursorRight();
2462 }
2463 break;
2464
2465 case WXK_SPACE:
2466 if ( !IsEditable() )
2467 {
2468 MoveCursorRight();
2469 }
2470 else
2471 {
2472 event.Skip();
2473 }
2474 break;
2475
2476 case WXK_RETURN:
2477 if ( event.ControlDown() )
2478 {
2479 event.Skip(); // to let the edit control have the return
2480 }
2481 else
2482 {
2483 MoveCursorDown();
2484 }
2485 break;
2486
2487 case WXK_HOME:
2488 if ( event.ControlDown() )
2489 {
2490 MakeCellVisible( 0, 0 );
2491 SetCurrentCell( 0, 0 );
2492 }
2493 else
2494 {
2495 event.Skip();
2496 }
2497 break;
2498
2499 case WXK_END:
2500 if ( event.ControlDown() )
2501 {
2502 MakeCellVisible( m_numRows-1, m_numCols-1 );
2503 SetCurrentCell( m_numRows-1, m_numCols-1 );
2504 }
2505 else
2506 {
2507 event.Skip();
2508 }
2509 break;
2510
2511 case WXK_PRIOR:
2512 MovePageUp();
2513 break;
2514
2515 case WXK_NEXT:
2516 MovePageDown();
2517 break;
2518
2519 default:
2520 // now try the cell edit control
2521 //
2522 if ( IsCellEditControlEnabled() )
2523 {
2524 event.SetEventObject( m_cellEditCtrl );
2525 m_cellEditCtrl->GetEventHandler()->ProcessEvent( event );
2526 }
2527 break;
2528 }
2529 }
2530
2531 m_inOnKeyDown = FALSE;
2532 }
2533
2534
2535 void wxGrid::SetCurrentCell( const wxGridCellCoords& coords )
2536 {
2537 if ( SendEvent( EVT_GRID_SELECT_CELL, coords.GetRow(), coords.GetCol() ) )
2538 {
2539 // the event has been intercepted - do nothing
2540 return;
2541 }
2542
2543 wxClientDC dc( m_gridWin );
2544 PrepareDC( dc );
2545
2546 if ( m_currentCellCoords != wxGridNoCellCoords )
2547 {
2548 HideCellEditControl();
2549 SaveEditControlValue();
2550 }
2551
2552 m_currentCellCoords = coords;
2553
2554 SetEditControlValue();
2555 ShowCellEditControl();
2556
2557 if ( IsSelection() )
2558 {
2559 wxRect r( SelectionToDeviceRect() );
2560 ClearSelection();
2561 if ( !GetBatchCount() ) m_gridWin->Refresh( FALSE, &r );
2562 }
2563 }
2564
2565
2566 //
2567 // ------ functions to get/send data (see also public functions)
2568 //
2569
2570 bool wxGrid::GetModelValues()
2571 {
2572 if ( m_table )
2573 {
2574 // all we need to do is repaint the grid
2575 //
2576 m_gridWin->Refresh();
2577 return TRUE;
2578 }
2579
2580 return FALSE;
2581 }
2582
2583
2584 bool wxGrid::SetModelValues()
2585 {
2586 int row, col;
2587
2588 if ( m_table )
2589 {
2590 for ( row = 0; row < m_numRows; row++ )
2591 {
2592 for ( col = 0; col < m_numCols; col++ )
2593 {
2594 m_table->SetValue( row, col, GetCellValue(row, col) );
2595 }
2596 }
2597
2598 return TRUE;
2599 }
2600
2601 return FALSE;
2602 }
2603
2604
2605
2606 // Note - this function only draws cells that are in the list of
2607 // exposed cells (usually set from the update region by
2608 // CalcExposedCells)
2609 //
2610 void wxGrid::DrawGridCellArea( wxDC& dc )
2611 {
2612 if ( !m_numRows || !m_numCols ) return;
2613
2614 size_t i;
2615 size_t numCells = m_cellsExposed.GetCount();
2616
2617 for ( i = 0; i < numCells; i++ )
2618 {
2619 DrawCell( dc, m_cellsExposed[i] );
2620 }
2621 }
2622
2623
2624 void wxGrid::DrawCell( wxDC& dc, const wxGridCellCoords& coords )
2625 {
2626 if ( m_colWidths[coords.GetCol()] <=0 ||
2627 m_rowHeights[coords.GetRow()] <= 0 ) return;
2628
2629 #if !WXGRID_DRAW_LINES
2630 if ( m_gridLinesEnabled )
2631 DrawCellBorder( dc, coords );
2632 #endif
2633
2634 DrawCellBackground( dc, coords );
2635
2636 // TODO: separate functions here for different kinds of cells ?
2637 // e.g. text, image
2638 //
2639 DrawCellValue( dc, coords );
2640 }
2641
2642
2643 void wxGrid::DrawCellBorder( wxDC& dc, const wxGridCellCoords& coords )
2644 {
2645 if ( m_colWidths[coords.GetCol()] <=0 ||
2646 m_rowHeights[coords.GetRow()] <= 0 ) return;
2647
2648 dc.SetPen( wxPen(GetGridLineColour(), 1, wxSOLID) );
2649 int row = coords.GetRow();
2650 int col = coords.GetCol();
2651
2652 // right hand border
2653 //
2654 dc.DrawLine( m_colRights[col], m_rowBottoms[row] - m_rowHeights[row],
2655 m_colRights[col], m_rowBottoms[row] );
2656
2657 // bottom border
2658 //
2659 dc.DrawLine( m_colRights[col] - m_colWidths[col], m_rowBottoms[row],
2660 m_colRights[col], m_rowBottoms[row] );
2661 }
2662
2663
2664 void wxGrid::DrawCellBackground( wxDC& dc, const wxGridCellCoords& coords )
2665 {
2666 if ( m_colWidths[coords.GetCol()] <=0 ||
2667 m_rowHeights[coords.GetRow()] <= 0 ) return;
2668
2669 int row = coords.GetRow();
2670 int col = coords.GetCol();
2671
2672 dc.SetBackgroundMode( wxSOLID );
2673
2674 if ( IsInSelection( coords ) )
2675 {
2676 // TODO: improve this
2677 //
2678 dc.SetBrush( *wxBLACK_BRUSH );
2679 }
2680 else
2681 {
2682 dc.SetBrush( wxBrush(GetCellBackgroundColour(row, col), wxSOLID) );
2683 }
2684
2685 dc.SetPen( *wxTRANSPARENT_PEN );
2686
2687 dc.DrawRectangle( m_colRights[col] - m_colWidths[col] + 1,
2688 m_rowBottoms[row] - m_rowHeights[row] + 1,
2689 m_colWidths[col]-1,
2690 m_rowHeights[row]-1 );
2691 }
2692
2693
2694 void wxGrid::DrawCellValue( wxDC& dc, const wxGridCellCoords& coords )
2695 {
2696 if ( m_colWidths[coords.GetCol()] <=0 ||
2697 m_rowHeights[coords.GetRow()] <= 0 ) return;
2698
2699 int row = coords.GetRow();
2700 int col = coords.GetCol();
2701
2702 dc.SetBackgroundMode( wxTRANSPARENT );
2703
2704 if ( IsInSelection( row, col ) )
2705 {
2706 // TODO: improve this
2707 //
2708 dc.SetTextBackground( wxColour(0, 0, 0) );
2709 dc.SetTextForeground( wxColour(255, 255, 255) );
2710 }
2711 else
2712 {
2713 dc.SetTextBackground( GetCellBackgroundColour(row, col) );
2714 dc.SetTextForeground( GetCellTextColour(row, col) );
2715 }
2716 dc.SetFont( GetCellFont(row, col) );
2717
2718 int hAlign, vAlign;
2719 GetCellAlignment( row, col, &hAlign, &vAlign );
2720
2721 wxRect rect;
2722 rect.SetX( m_colRights[col] - m_colWidths[col] + 2 );
2723 rect.SetY( m_rowBottoms[row] - m_rowHeights[row] + 2 );
2724 rect.SetWidth( m_colWidths[col] - 4 );
2725 rect.SetHeight( m_rowHeights[row] - 4 );
2726
2727 DrawTextRectangle( dc, GetCellValue( row, col ), rect, hAlign, vAlign );
2728 }
2729
2730
2731
2732 // TODO: remove this ???
2733 // This is used to redraw all grid lines e.g. when the grid line colour
2734 // has been changed
2735 //
2736 void wxGrid::DrawAllGridLines( wxDC& dc, const wxRegion & reg )
2737 {
2738 if ( !m_gridLinesEnabled ||
2739 !m_numRows ||
2740 !m_numCols ) return;
2741
2742 int top, bottom, left, right;
2743
2744 if (reg.IsEmpty()){
2745 int cw, ch;
2746 m_gridWin->GetClientSize(&cw, &ch);
2747
2748 // virtual coords of visible area
2749 //
2750 CalcUnscrolledPosition( 0, 0, &left, &top );
2751 CalcUnscrolledPosition( cw, ch, &right, &bottom );
2752 }
2753 else{
2754 wxCoord x, y, w, h;
2755 reg.GetBox(x, y, w, h);
2756 CalcUnscrolledPosition( x, y, &left, &top );
2757 CalcUnscrolledPosition( x + w, y + h, &right, &bottom );
2758 }
2759
2760 // avoid drawing grid lines past the last row and col
2761 //
2762 right = wxMin( right, m_colRights[m_numCols-1] );
2763 bottom = wxMin( bottom, m_rowBottoms[m_numRows-1] );
2764
2765 dc.SetPen( wxPen(GetGridLineColour(), 1, wxSOLID) );
2766
2767 // horizontal grid lines
2768 //
2769 int i;
2770 for ( i = 0; i < m_numRows; i++ )
2771 {
2772 if ( m_rowBottoms[i] > bottom )
2773 {
2774 break;
2775 }
2776 else if ( m_rowBottoms[i] >= top )
2777 {
2778 dc.DrawLine( left, m_rowBottoms[i], right, m_rowBottoms[i] );
2779 }
2780 }
2781
2782
2783 // vertical grid lines
2784 //
2785 for ( i = 0; i < m_numCols; i++ )
2786 {
2787 if ( m_colRights[i] > right )
2788 {
2789 break;
2790 }
2791 else if ( m_colRights[i] >= left )
2792 {
2793 dc.DrawLine( m_colRights[i], top, m_colRights[i], bottom );
2794 }
2795 }
2796 }
2797
2798
2799 void wxGrid::DrawRowLabels( wxDC& dc )
2800 {
2801 if ( !m_numRows || !m_numCols ) return;
2802
2803 size_t i;
2804 size_t numLabels = m_rowLabelsExposed.GetCount();
2805
2806 for ( i = 0; i < numLabels; i++ )
2807 {
2808 DrawRowLabel( dc, m_rowLabelsExposed[i] );
2809 }
2810 }
2811
2812
2813 void wxGrid::DrawRowLabel( wxDC& dc, int row )
2814 {
2815 if ( m_rowHeights[row] <= 0 ) return;
2816
2817 int rowTop = m_rowBottoms[row] - m_rowHeights[row];
2818
2819 dc.SetPen( *wxBLACK_PEN );
2820 dc.DrawLine( m_rowLabelWidth-1, rowTop,
2821 m_rowLabelWidth-1, m_rowBottoms[row]-1 );
2822
2823 dc.DrawLine( 0, m_rowBottoms[row]-1,
2824 m_rowLabelWidth-1, m_rowBottoms[row]-1 );
2825
2826 dc.SetPen( *wxWHITE_PEN );
2827 dc.DrawLine( 0, rowTop, 0, m_rowBottoms[row]-1 );
2828 dc.DrawLine( 0, rowTop, m_rowLabelWidth-1, rowTop );
2829
2830 dc.SetBackgroundMode( wxTRANSPARENT );
2831 dc.SetTextForeground( GetLabelTextColour() );
2832 dc.SetFont( GetLabelFont() );
2833
2834 int hAlign, vAlign;
2835 GetRowLabelAlignment( &hAlign, &vAlign );
2836
2837 wxRect rect;
2838 rect.SetX( 2 );
2839 rect.SetY( m_rowBottoms[row] - m_rowHeights[row] + 2 );
2840 rect.SetWidth( m_rowLabelWidth - 4 );
2841 rect.SetHeight( m_rowHeights[row] - 4 );
2842 DrawTextRectangle( dc, GetRowLabelValue( row ), rect, hAlign, vAlign );
2843 }
2844
2845
2846 void wxGrid::DrawColLabels( wxDC& dc )
2847 {
2848 if ( !m_numRows || !m_numCols ) return;
2849
2850 size_t i;
2851 size_t numLabels = m_colLabelsExposed.GetCount();
2852
2853 for ( i = 0; i < numLabels; i++ )
2854 {
2855 DrawColLabel( dc, m_colLabelsExposed[i] );
2856 }
2857 }
2858
2859
2860 void wxGrid::DrawColLabel( wxDC& dc, int col )
2861 {
2862 if ( m_colWidths[col] <= 0 ) return;
2863
2864 int colLeft = m_colRights[col] - m_colWidths[col];
2865
2866 dc.SetPen( *wxBLACK_PEN );
2867 dc.DrawLine( m_colRights[col]-1, 0,
2868 m_colRights[col]-1, m_colLabelHeight-1 );
2869
2870 dc.DrawLine( colLeft, m_colLabelHeight-1,
2871 m_colRights[col]-1, m_colLabelHeight-1 );
2872
2873 dc.SetPen( *wxWHITE_PEN );
2874 dc.DrawLine( colLeft, 0, colLeft, m_colLabelHeight-1 );
2875 dc.DrawLine( colLeft, 0, m_colRights[col]-1, 0 );
2876
2877 dc.SetBackgroundMode( wxTRANSPARENT );
2878 dc.SetTextForeground( GetLabelTextColour() );
2879 dc.SetFont( GetLabelFont() );
2880
2881 dc.SetBackgroundMode( wxTRANSPARENT );
2882 dc.SetTextForeground( GetLabelTextColour() );
2883 dc.SetFont( GetLabelFont() );
2884
2885 int hAlign, vAlign;
2886 GetColLabelAlignment( &hAlign, &vAlign );
2887
2888 wxRect rect;
2889 rect.SetX( m_colRights[col] - m_colWidths[col] + 2 );
2890 rect.SetY( 2 );
2891 rect.SetWidth( m_colWidths[col] - 4 );
2892 rect.SetHeight( m_colLabelHeight - 4 );
2893 DrawTextRectangle( dc, GetColLabelValue( col ), rect, hAlign, vAlign );
2894 }
2895
2896
2897 void wxGrid::DrawTextRectangle( wxDC& dc,
2898 const wxString& value,
2899 const wxRect& rect,
2900 int horizAlign,
2901 int vertAlign )
2902 {
2903 long textWidth, textHeight;
2904 long lineWidth, lineHeight;
2905 wxArrayString lines;
2906
2907 dc.SetClippingRegion( rect );
2908 StringToLines( value, lines );
2909 if ( lines.GetCount() )
2910 {
2911 GetTextBoxSize( dc, lines, &textWidth, &textHeight );
2912 dc.GetTextExtent( lines[0], &lineWidth, &lineHeight );
2913
2914 float x, y;
2915 switch ( horizAlign )
2916 {
2917 case wxRIGHT:
2918 x = rect.x + (rect.width - textWidth - 1);
2919 break;
2920
2921 case wxCENTRE:
2922 x = rect.x + ((rect.width - textWidth)/2);
2923 break;
2924
2925 case wxLEFT:
2926 default:
2927 x = rect.x + 1;
2928 break;
2929 }
2930
2931 switch ( vertAlign )
2932 {
2933 case wxBOTTOM:
2934 y = rect.y + (rect.height - textHeight - 1);
2935 break;
2936
2937 case wxCENTRE:
2938 y = rect.y + ((rect.height - textHeight)/2);
2939 break;
2940
2941 case wxTOP:
2942 default:
2943 y = rect.y + 1;
2944 break;
2945 }
2946
2947 for ( size_t i = 0; i < lines.GetCount(); i++ )
2948 {
2949 dc.DrawText( lines[i], (long)x, (long)y );
2950 y += lineHeight;
2951 }
2952 }
2953
2954 dc.DestroyClippingRegion();
2955 }
2956
2957
2958 // Split multi line text up into an array of strings. Any existing
2959 // contents of the string array are preserved.
2960 //
2961 void wxGrid::StringToLines( const wxString& value, wxArrayString& lines )
2962 {
2963 // TODO: this won't work for WXMAC ? (lines end with '\r')
2964 // => use wxTextFile functions then (VZ)
2965 int startPos = 0;
2966 int pos;
2967 while ( startPos < (int)value.Length() )
2968 {
2969 pos = value.Mid(startPos).Find( '\n' );
2970 if ( pos < 0 )
2971 {
2972 break;
2973 }
2974 else if ( pos == 0 )
2975 {
2976 lines.Add( wxEmptyString );
2977 }
2978 else
2979 {
2980 if ( value[startPos+pos-1] == '\r' )
2981 {
2982 lines.Add( value.Mid(startPos, pos-1) );
2983 }
2984 else
2985 {
2986 lines.Add( value.Mid(startPos, pos) );
2987 }
2988 }
2989 startPos += pos+1;
2990 }
2991 if ( startPos < (int)value.Length() )
2992 {
2993 lines.Add( value.Mid( startPos ) );
2994 }
2995 }
2996
2997
2998 void wxGrid::GetTextBoxSize( wxDC& dc,
2999 wxArrayString& lines,
3000 long *width, long *height )
3001 {
3002 long w = 0;
3003 long h = 0;
3004 long lineW, lineH;
3005
3006 size_t i;
3007 for ( i = 0; i < lines.GetCount(); i++ )
3008 {
3009 dc.GetTextExtent( lines[i], &lineW, &lineH );
3010 w = wxMax( w, lineW );
3011 h += lineH;
3012 }
3013
3014 *width = w;
3015 *height = h;
3016 }
3017
3018
3019 //
3020 // ------ Edit control functions
3021 //
3022
3023
3024 void wxGrid::EnableEditing( bool edit )
3025 {
3026 // TODO: improve this ?
3027 //
3028 if ( edit != m_editable )
3029 {
3030 m_editable = edit;
3031
3032 // TODO: extend this for other edit control types
3033 //
3034 if ( m_editCtrlType == wxGRID_TEXTCTRL )
3035 {
3036 ((wxTextCtrl *)m_cellEditCtrl)->SetEditable( m_editable );
3037 }
3038 }
3039 }
3040
3041
3042 #if 0 // disabled for the moment - the cell control is always active
3043 void wxGrid::EnableCellEditControl( bool enable )
3044 {
3045 if ( m_cellEditCtrl &&
3046 enable != m_cellEditCtrlEnabled )
3047 {
3048 m_cellEditCtrlEnabled = enable;
3049
3050 if ( m_cellEditCtrlEnabled )
3051 {
3052 SetEditControlValue();
3053 ShowCellEditControl();
3054 }
3055 else
3056 {
3057 HideCellEditControl();
3058 SaveEditControlValue();
3059 }
3060 }
3061 }
3062 #endif
3063
3064
3065 void wxGrid::ShowCellEditControl()
3066 {
3067 wxRect rect;
3068
3069 if ( IsCellEditControlEnabled() )
3070 {
3071 if ( !IsVisible( m_currentCellCoords ) )
3072 {
3073 return;
3074 }
3075 else
3076 {
3077 rect = CellToRect( m_currentCellCoords );
3078
3079 // convert to scrolled coords
3080 //
3081 int left, top, right, bottom;
3082 CalcScrolledPosition( rect.GetLeft(), rect.GetTop(), &left, &top );
3083 CalcScrolledPosition( rect.GetRight(), rect.GetBottom(), &right, &bottom );
3084
3085 int cw, ch;
3086 m_gridWin->GetClientSize( &cw, &ch );
3087
3088 // Make the edit control large enough to allow for internal margins
3089 // TODO: remove this if the text ctrl sizing is improved esp. for unix
3090 //
3091 int extra;
3092 #if defined(__WXMOTIF__)
3093 if ( m_currentCellCoords.GetRow() == 0 ||
3094 m_currentCellCoords.GetCol() == 0 )
3095 {
3096 extra = 2;
3097 }
3098 else
3099 {
3100 extra = 4;
3101 }
3102 #else
3103 if ( m_currentCellCoords.GetRow() == 0 ||
3104 m_currentCellCoords.GetCol() == 0 )
3105 {
3106 extra = 1;
3107 }
3108 else
3109 {
3110 extra = 2;
3111 }
3112 #endif
3113
3114 #if defined(__WXGTK__)
3115 int top_diff = 0;
3116 int left_diff = 0;
3117 if (left != 0) left_diff++;
3118 if (top != 0) top_diff++;
3119 rect.SetLeft( left + left_diff );
3120 rect.SetTop( top + top_diff );
3121 rect.SetRight( rect.GetRight() - left_diff );
3122 rect.SetBottom( rect.GetBottom() - top_diff );
3123 #else
3124 rect.SetLeft( wxMax(0, left - extra) );
3125 rect.SetTop( wxMax(0, top - extra) );
3126 rect.SetRight( rect.GetRight() + 2*extra );
3127 rect.SetBottom( rect.GetBottom() + 2*extra );
3128 #endif
3129
3130 m_cellEditCtrl->SetSize( rect );
3131 m_cellEditCtrl->Show( TRUE );
3132
3133 switch ( m_editCtrlType )
3134 {
3135 case wxGRID_TEXTCTRL:
3136 ((wxTextCtrl *) m_cellEditCtrl)->SetInsertionPointEnd();
3137 break;
3138
3139 case wxGRID_CHECKBOX:
3140 // TODO: anything ???
3141 //
3142 break;
3143
3144 case wxGRID_CHOICE:
3145 // TODO: anything ???
3146 //
3147 break;
3148
3149 case wxGRID_COMBOBOX:
3150 // TODO: anything ???
3151 //
3152 break;
3153 }
3154
3155 m_cellEditCtrl->SetFocus();
3156 }
3157 }
3158 }
3159
3160
3161 void wxGrid::HideCellEditControl()
3162 {
3163 if ( IsCellEditControlEnabled() )
3164 {
3165 m_cellEditCtrl->Show( FALSE );
3166 }
3167 }
3168
3169
3170 void wxGrid::SetEditControlValue( const wxString& value )
3171 {
3172 if ( m_table )
3173 {
3174 wxString s;
3175 if ( !value )
3176 s = GetCellValue(m_currentCellCoords);
3177 else
3178 s = value;
3179
3180 if ( IsCellEditControlEnabled() )
3181 {
3182 switch ( m_editCtrlType )
3183 {
3184 case wxGRID_TEXTCTRL:
3185 ((wxGridTextCtrl *)m_cellEditCtrl)->SetStartValue(s);
3186 break;
3187
3188 case wxGRID_CHECKBOX:
3189 // TODO: implement this
3190 //
3191 break;
3192
3193 case wxGRID_CHOICE:
3194 // TODO: implement this
3195 //
3196 break;
3197
3198 case wxGRID_COMBOBOX:
3199 // TODO: implement this
3200 //
3201 break;
3202 }
3203 }
3204 }
3205 }
3206
3207
3208 void wxGrid::SaveEditControlValue()
3209 {
3210 if ( m_table )
3211 {
3212 wxWindow *ctrl = (wxWindow *)NULL;
3213
3214 if ( IsCellEditControlEnabled() )
3215 {
3216 ctrl = m_cellEditCtrl;
3217 }
3218 else
3219 {
3220 return;
3221 }
3222
3223 bool valueChanged = FALSE;
3224
3225 switch ( m_editCtrlType )
3226 {
3227 case wxGRID_TEXTCTRL:
3228 valueChanged = (((wxGridTextCtrl *)ctrl)->GetValue() !=
3229 ((wxGridTextCtrl *)ctrl)->GetStartValue());
3230 SetCellValue( m_currentCellCoords,
3231 ((wxTextCtrl *) ctrl)->GetValue() );
3232 break;
3233
3234 case wxGRID_CHECKBOX:
3235 // TODO: implement this
3236 //
3237 break;
3238
3239 case wxGRID_CHOICE:
3240 // TODO: implement this
3241 //
3242 break;
3243
3244 case wxGRID_COMBOBOX:
3245 // TODO: implement this
3246 //
3247 break;
3248 }
3249
3250 if ( valueChanged )
3251 {
3252 SendEvent( EVT_GRID_CELL_CHANGE,
3253 m_currentCellCoords.GetRow(),
3254 m_currentCellCoords.GetCol() );
3255 }
3256 }
3257 }
3258
3259
3260 //
3261 // ------ Grid location functions
3262 // Note that all of these functions work with the logical coordinates of
3263 // grid cells and labels so you will need to convert from device
3264 // coordinates for mouse events etc.
3265 //
3266
3267 void wxGrid::XYToCell( int x, int y, wxGridCellCoords& coords )
3268 {
3269 int row = YToRow(y);
3270 int col = XToCol(x);
3271
3272 if ( row == -1 || col == -1 )
3273 {
3274 coords = wxGridNoCellCoords;
3275 }
3276 else
3277 {
3278 coords.Set( row, col );
3279 }
3280 }
3281
3282
3283 int wxGrid::YToRow( int y )
3284 {
3285 int i;
3286
3287 for ( i = 0; i < m_numRows; i++ )
3288 {
3289 if ( y < m_rowBottoms[i] ) return i;
3290 }
3291
3292 return -1;
3293 }
3294
3295
3296 int wxGrid::XToCol( int x )
3297 {
3298 int i;
3299
3300 for ( i = 0; i < m_numCols; i++ )
3301 {
3302 if ( x < m_colRights[i] ) return i;
3303 }
3304
3305 return -1;
3306 }
3307
3308
3309 // return the row number that that the y coord is near the edge of, or
3310 // -1 if not near an edge
3311 //
3312 int wxGrid::YToEdgeOfRow( int y )
3313 {
3314 int i, d;
3315
3316 for ( i = 0; i < m_numRows; i++ )
3317 {
3318 if ( m_rowHeights[i] > WXGRID_LABEL_EDGE_ZONE )
3319 {
3320 d = abs( y - m_rowBottoms[i] );
3321 {
3322 if ( d < WXGRID_LABEL_EDGE_ZONE ) return i;
3323 }
3324 }
3325 }
3326
3327 return -1;
3328 }
3329
3330
3331 // return the col number that that the x coord is near the edge of, or
3332 // -1 if not near an edge
3333 //
3334 int wxGrid::XToEdgeOfCol( int x )
3335 {
3336 int i, d;
3337
3338 for ( i = 0; i < m_numCols; i++ )
3339 {
3340 if ( m_colWidths[i] > WXGRID_LABEL_EDGE_ZONE )
3341 {
3342 d = abs( x - m_colRights[i] );
3343 {
3344 if ( d < WXGRID_LABEL_EDGE_ZONE ) return i;
3345 }
3346 }
3347 }
3348
3349 return -1;
3350 }
3351
3352
3353 wxRect wxGrid::CellToRect( int row, int col )
3354 {
3355 wxRect rect( -1, -1, -1, -1 );
3356
3357 if ( row >= 0 && row < m_numRows &&
3358 col >= 0 && col < m_numCols )
3359 {
3360 rect.x = m_colRights[col] - m_colWidths[col];
3361 rect.y = m_rowBottoms[row] - m_rowHeights[row];
3362 rect.width = m_colWidths[col];
3363 rect.height = m_rowHeights[ row ];
3364 }
3365
3366 return rect;
3367 }
3368
3369
3370 bool wxGrid::IsVisible( int row, int col, bool wholeCellVisible )
3371 {
3372 // get the cell rectangle in logical coords
3373 //
3374 wxRect r( CellToRect( row, col ) );
3375
3376 // convert to device coords
3377 //
3378 int left, top, right, bottom;
3379 CalcScrolledPosition( r.GetLeft(), r.GetTop(), &left, &top );
3380 CalcScrolledPosition( r.GetRight(), r.GetBottom(), &right, &bottom );
3381
3382 // check against the client area of the grid window
3383 //
3384 int cw, ch;
3385 m_gridWin->GetClientSize( &cw, &ch );
3386
3387 if ( wholeCellVisible )
3388 {
3389 // is the cell wholly visible ?
3390 //
3391 return ( left >= 0 && right <= cw &&
3392 top >= 0 && bottom <= ch );
3393 }
3394 else
3395 {
3396 // is the cell partly visible ?
3397 //
3398 return ( ((left >=0 && left < cw) || (right > 0 && right <= cw)) &&
3399 ((top >=0 && top < ch) || (bottom > 0 && bottom <= ch)) );
3400 }
3401 }
3402
3403
3404 // make the specified cell location visible by doing a minimal amount
3405 // of scrolling
3406 //
3407 void wxGrid::MakeCellVisible( int row, int col )
3408 {
3409 int i;
3410 int xpos = -1, ypos = -1;
3411
3412 if ( row >= 0 && row < m_numRows &&
3413 col >= 0 && col < m_numCols )
3414 {
3415 // get the cell rectangle in logical coords
3416 //
3417 wxRect r( CellToRect( row, col ) );
3418
3419 // convert to device coords
3420 //
3421 int left, top, right, bottom;
3422 CalcScrolledPosition( r.GetLeft(), r.GetTop(), &left, &top );
3423 CalcScrolledPosition( r.GetRight(), r.GetBottom(), &right, &bottom );
3424
3425 int cw, ch;
3426 m_gridWin->GetClientSize( &cw, &ch );
3427
3428 if ( top < 0 )
3429 {
3430 ypos = r.GetTop();
3431 }
3432 else if ( bottom > ch )
3433 {
3434 int h = r.GetHeight();
3435 ypos = r.GetTop();
3436 for ( i = row-1; i >= 0; i-- )
3437 {
3438 if ( h + m_rowHeights[i] > ch ) break;
3439
3440 h += m_rowHeights[i];
3441 ypos -= m_rowHeights[i];
3442 }
3443
3444 // we divide it later by GRID_SCROLL_LINE, make sure that we don't
3445 // have rounding errors (this is important, because if we do, we
3446 // might not scroll at all and some cells won't be redrawn)
3447 ypos += GRID_SCROLL_LINE / 2;
3448 }
3449
3450 if ( left < 0 )
3451 {
3452 xpos = r.GetLeft();
3453 }
3454 else if ( right > cw )
3455 {
3456 int w = r.GetWidth();
3457 xpos = r.GetLeft();
3458 for ( i = col-1; i >= 0; i-- )
3459 {
3460 if ( w + m_colWidths[i] > cw ) break;
3461
3462 w += m_colWidths[i];
3463 xpos -= m_colWidths[i];
3464 }
3465
3466 // see comment for ypos above
3467 xpos += GRID_SCROLL_LINE / 2;
3468 }
3469
3470 if ( xpos != -1 || ypos != -1 )
3471 {
3472 if ( xpos != -1 ) xpos /= GRID_SCROLL_LINE;
3473 if ( ypos != -1 ) ypos /= GRID_SCROLL_LINE;
3474 Scroll( xpos, ypos );
3475 AdjustScrollbars();
3476 }
3477 }
3478 }
3479
3480
3481 //
3482 // ------ Grid cursor movement functions
3483 //
3484
3485 bool wxGrid::MoveCursorUp()
3486 {
3487 if ( m_currentCellCoords != wxGridNoCellCoords &&
3488 m_currentCellCoords.GetRow() > 0 )
3489 {
3490 MakeCellVisible( m_currentCellCoords.GetRow() - 1,
3491 m_currentCellCoords.GetCol() );
3492
3493 SetCurrentCell( m_currentCellCoords.GetRow() - 1,
3494 m_currentCellCoords.GetCol() );
3495
3496 return TRUE;
3497 }
3498
3499 return FALSE;
3500 }
3501
3502
3503 bool wxGrid::MoveCursorDown()
3504 {
3505 // TODO: allow for scrolling
3506 //
3507 if ( m_currentCellCoords != wxGridNoCellCoords &&
3508 m_currentCellCoords.GetRow() < m_numRows-1 )
3509 {
3510 MakeCellVisible( m_currentCellCoords.GetRow() + 1,
3511 m_currentCellCoords.GetCol() );
3512
3513 SetCurrentCell( m_currentCellCoords.GetRow() + 1,
3514 m_currentCellCoords.GetCol() );
3515
3516 return TRUE;
3517 }
3518
3519 return FALSE;
3520 }
3521
3522
3523 bool wxGrid::MoveCursorLeft()
3524 {
3525 if ( m_currentCellCoords != wxGridNoCellCoords &&
3526 m_currentCellCoords.GetCol() > 0 )
3527 {
3528 MakeCellVisible( m_currentCellCoords.GetRow(),
3529 m_currentCellCoords.GetCol() - 1 );
3530
3531 SetCurrentCell( m_currentCellCoords.GetRow(),
3532 m_currentCellCoords.GetCol() - 1 );
3533
3534 return TRUE;
3535 }
3536
3537 return FALSE;
3538 }
3539
3540
3541 bool wxGrid::MoveCursorRight()
3542 {
3543 if ( m_currentCellCoords != wxGridNoCellCoords &&
3544 m_currentCellCoords.GetCol() < m_numCols - 1 )
3545 {
3546 MakeCellVisible( m_currentCellCoords.GetRow(),
3547 m_currentCellCoords.GetCol() + 1 );
3548
3549 SetCurrentCell( m_currentCellCoords.GetRow(),
3550 m_currentCellCoords.GetCol() + 1 );
3551
3552 return TRUE;
3553 }
3554
3555 return FALSE;
3556 }
3557
3558
3559 bool wxGrid::MovePageUp()
3560 {
3561 if ( m_currentCellCoords == wxGridNoCellCoords ) return FALSE;
3562
3563 int row = m_currentCellCoords.GetRow();
3564 if ( row > 0 )
3565 {
3566 int cw, ch;
3567 m_gridWin->GetClientSize( &cw, &ch );
3568
3569 int y = m_rowBottoms[ row ] - m_rowHeights[ row ];
3570 int newRow = YToRow( y - ch + 1 );
3571 if ( newRow == -1 )
3572 {
3573 newRow = 0;
3574 }
3575 else if ( newRow == row )
3576 {
3577 newRow = row - 1;
3578 }
3579
3580 MakeCellVisible( newRow, m_currentCellCoords.GetCol() );
3581 SetCurrentCell( newRow, m_currentCellCoords.GetCol() );
3582
3583 return TRUE;
3584 }
3585
3586 return FALSE;
3587 }
3588
3589 bool wxGrid::MovePageDown()
3590 {
3591 if ( m_currentCellCoords == wxGridNoCellCoords ) return FALSE;
3592
3593 int row = m_currentCellCoords.GetRow();
3594 if ( row < m_numRows )
3595 {
3596 int cw, ch;
3597 m_gridWin->GetClientSize( &cw, &ch );
3598
3599 int y = m_rowBottoms[ row ] - m_rowHeights[ row ];
3600 int newRow = YToRow( y + ch );
3601 if ( newRow == -1 )
3602 {
3603 newRow = m_numRows - 1;
3604 }
3605 else if ( newRow == row )
3606 {
3607 newRow = row + 1;
3608 }
3609
3610 MakeCellVisible( newRow, m_currentCellCoords.GetCol() );
3611 SetCurrentCell( newRow, m_currentCellCoords.GetCol() );
3612
3613 return TRUE;
3614 }
3615
3616 return FALSE;
3617 }
3618
3619 bool wxGrid::MoveCursorUpBlock()
3620 {
3621 if ( m_table &&
3622 m_currentCellCoords != wxGridNoCellCoords &&
3623 m_currentCellCoords.GetRow() > 0 )
3624 {
3625 int row = m_currentCellCoords.GetRow();
3626 int col = m_currentCellCoords.GetCol();
3627
3628 if ( m_table->IsEmptyCell(row, col) )
3629 {
3630 // starting in an empty cell: find the next block of
3631 // non-empty cells
3632 //
3633 while ( row > 0 )
3634 {
3635 row-- ;
3636 if ( !(m_table->IsEmptyCell(row, col)) ) break;
3637 }
3638 }
3639 else if ( m_table->IsEmptyCell(row-1, col) )
3640 {
3641 // starting at the top of a block: find the next block
3642 //
3643 row--;
3644 while ( row > 0 )
3645 {
3646 row-- ;
3647 if ( !(m_table->IsEmptyCell(row, col)) ) break;
3648 }
3649 }
3650 else
3651 {
3652 // starting within a block: find the top of the block
3653 //
3654 while ( row > 0 )
3655 {
3656 row-- ;
3657 if ( m_table->IsEmptyCell(row, col) )
3658 {
3659 row++ ;
3660 break;
3661 }
3662 }
3663 }
3664
3665 MakeCellVisible( row, col );
3666 SetCurrentCell( row, col );
3667
3668 return TRUE;
3669 }
3670
3671 return FALSE;
3672 }
3673
3674 bool wxGrid::MoveCursorDownBlock()
3675 {
3676 if ( m_table &&
3677 m_currentCellCoords != wxGridNoCellCoords &&
3678 m_currentCellCoords.GetRow() < m_numRows-1 )
3679 {
3680 int row = m_currentCellCoords.GetRow();
3681 int col = m_currentCellCoords.GetCol();
3682
3683 if ( m_table->IsEmptyCell(row, col) )
3684 {
3685 // starting in an empty cell: find the next block of
3686 // non-empty cells
3687 //
3688 while ( row < m_numRows-1 )
3689 {
3690 row++ ;
3691 if ( !(m_table->IsEmptyCell(row, col)) ) break;
3692 }
3693 }
3694 else if ( m_table->IsEmptyCell(row+1, col) )
3695 {
3696 // starting at the bottom of a block: find the next block
3697 //
3698 row++;
3699 while ( row < m_numRows-1 )
3700 {
3701 row++ ;
3702 if ( !(m_table->IsEmptyCell(row, col)) ) break;
3703 }
3704 }
3705 else
3706 {
3707 // starting within a block: find the bottom of the block
3708 //
3709 while ( row < m_numRows-1 )
3710 {
3711 row++ ;
3712 if ( m_table->IsEmptyCell(row, col) )
3713 {
3714 row-- ;
3715 break;
3716 }
3717 }
3718 }
3719
3720 MakeCellVisible( row, col );
3721 SetCurrentCell( row, col );
3722
3723 return TRUE;
3724 }
3725
3726 return FALSE;
3727 }
3728
3729 bool wxGrid::MoveCursorLeftBlock()
3730 {
3731 if ( m_table &&
3732 m_currentCellCoords != wxGridNoCellCoords &&
3733 m_currentCellCoords.GetCol() > 0 )
3734 {
3735 int row = m_currentCellCoords.GetRow();
3736 int col = m_currentCellCoords.GetCol();
3737
3738 if ( m_table->IsEmptyCell(row, col) )
3739 {
3740 // starting in an empty cell: find the next block of
3741 // non-empty cells
3742 //
3743 while ( col > 0 )
3744 {
3745 col-- ;
3746 if ( !(m_table->IsEmptyCell(row, col)) ) break;
3747 }
3748 }
3749 else if ( m_table->IsEmptyCell(row, col-1) )
3750 {
3751 // starting at the left of a block: find the next block
3752 //
3753 col--;
3754 while ( col > 0 )
3755 {
3756 col-- ;
3757 if ( !(m_table->IsEmptyCell(row, col)) ) break;
3758 }
3759 }
3760 else
3761 {
3762 // starting within a block: find the left of the block
3763 //
3764 while ( col > 0 )
3765 {
3766 col-- ;
3767 if ( m_table->IsEmptyCell(row, col) )
3768 {
3769 col++ ;
3770 break;
3771 }
3772 }
3773 }
3774
3775 MakeCellVisible( row, col );
3776 SetCurrentCell( row, col );
3777
3778 return TRUE;
3779 }
3780
3781 return FALSE;
3782 }
3783
3784 bool wxGrid::MoveCursorRightBlock()
3785 {
3786 if ( m_table &&
3787 m_currentCellCoords != wxGridNoCellCoords &&
3788 m_currentCellCoords.GetCol() < m_numCols-1 )
3789 {
3790 int row = m_currentCellCoords.GetRow();
3791 int col = m_currentCellCoords.GetCol();
3792
3793 if ( m_table->IsEmptyCell(row, col) )
3794 {
3795 // starting in an empty cell: find the next block of
3796 // non-empty cells
3797 //
3798 while ( col < m_numCols-1 )
3799 {
3800 col++ ;
3801 if ( !(m_table->IsEmptyCell(row, col)) ) break;
3802 }
3803 }
3804 else if ( m_table->IsEmptyCell(row, col+1) )
3805 {
3806 // starting at the right of a block: find the next block
3807 //
3808 col++;
3809 while ( col < m_numCols-1 )
3810 {
3811 col++ ;
3812 if ( !(m_table->IsEmptyCell(row, col)) ) break;
3813 }
3814 }
3815 else
3816 {
3817 // starting within a block: find the right of the block
3818 //
3819 while ( col < m_numCols-1 )
3820 {
3821 col++ ;
3822 if ( m_table->IsEmptyCell(row, col) )
3823 {
3824 col-- ;
3825 break;
3826 }
3827 }
3828 }
3829
3830 MakeCellVisible( row, col );
3831 SetCurrentCell( row, col );
3832
3833 return TRUE;
3834 }
3835
3836 return FALSE;
3837 }
3838
3839
3840
3841 //
3842 // ------ Label values and formatting
3843 //
3844
3845 void wxGrid::GetRowLabelAlignment( int *horiz, int *vert )
3846 {
3847 *horiz = m_rowLabelHorizAlign;
3848 *vert = m_rowLabelVertAlign;
3849 }
3850
3851 void wxGrid::GetColLabelAlignment( int *horiz, int *vert )
3852 {
3853 *horiz = m_colLabelHorizAlign;
3854 *vert = m_colLabelVertAlign;
3855 }
3856
3857 wxString wxGrid::GetRowLabelValue( int row )
3858 {
3859 if ( m_table )
3860 {
3861 return m_table->GetRowLabelValue( row );
3862 }
3863 else
3864 {
3865 wxString s;
3866 s << row;
3867 return s;
3868 }
3869 }
3870
3871 wxString wxGrid::GetColLabelValue( int col )
3872 {
3873 if ( m_table )
3874 {
3875 return m_table->GetColLabelValue( col );
3876 }
3877 else
3878 {
3879 wxString s;
3880 s << col;
3881 return s;
3882 }
3883 }
3884
3885
3886 void wxGrid::SetRowLabelSize( int width )
3887 {
3888 wxSize sz;
3889
3890 width = wxMax( width, 0 );
3891 if ( width != m_rowLabelWidth )
3892 {
3893 // Hiding the row labels (and possible the corner label)
3894 //
3895 if ( width == 0 )
3896 {
3897 m_rowLabelWin->Show( FALSE );
3898
3899 // If the col labels are on display we need to hide the
3900 // corner label and remove it from the top sizer
3901 //
3902 if ( m_colLabelHeight > 0 )
3903 {
3904 m_cornerLabelWin->Show( FALSE );
3905 m_topSizer->Remove( m_cornerLabelWin );
3906 }
3907
3908 m_middleSizer->Remove( m_rowLabelWin );
3909 }
3910 else
3911 {
3912 // Displaying the row labels (and possibly the corner
3913 // label) after being hidden
3914 //
3915 if ( m_rowLabelWidth == 0 )
3916 {
3917 m_rowLabelWin->Show( TRUE );
3918
3919 if ( m_colLabelHeight > 0 )
3920 {
3921 m_cornerLabelWin->Show( TRUE );
3922 m_topSizer->Prepend( m_cornerLabelWin, 0 );
3923 }
3924
3925 m_middleSizer->Prepend( m_rowLabelWin, 0, wxEXPAND );
3926 }
3927
3928
3929 // set the width of the corner label if it is on display
3930 //
3931 if ( m_colLabelHeight > 0 )
3932 {
3933 wxList& childList = m_topSizer->GetChildren();
3934 wxNode *node = childList.First();
3935 while (node)
3936 {
3937 wxSizerItem *item = (wxSizerItem*)node->Data();
3938 if ( item->GetWindow() == m_cornerLabelWin )
3939 {
3940 item->SetInitSize( width, m_colLabelHeight );
3941 break;
3942 }
3943 node = node->Next();
3944 }
3945 }
3946
3947 // set the width of the row labels
3948 //
3949 wxList& childList = m_middleSizer->GetChildren();
3950 wxNode *node = childList.First();
3951 while (node)
3952 {
3953 wxSizerItem *item = (wxSizerItem*)node->Data();
3954 if ( item->GetWindow() == m_rowLabelWin )
3955 {
3956 sz = item->GetWindow()->GetSize();
3957 item->SetInitSize( width, sz.GetHeight() );
3958 break;
3959 }
3960 node = node->Next();
3961 }
3962 }
3963
3964 m_rowLabelWidth = width;
3965 m_mainSizer->Layout();
3966 }
3967 }
3968
3969
3970 void wxGrid::SetColLabelSize( int height )
3971 {
3972 wxSize sz;
3973
3974 if ( height < 0 ) height = 0;
3975 if ( height != m_colLabelHeight )
3976 {
3977 // hiding the column labels
3978 //
3979 if ( height == 0 )
3980 {
3981 m_cornerLabelWin->Show( FALSE );
3982 m_colLabelWin->Show( FALSE );
3983
3984 // Note: this call will actually delete the sizer
3985 //
3986 m_mainSizer->Remove( m_topSizer );
3987 m_topSizer = (wxBoxSizer *)NULL;
3988 }
3989 else
3990 {
3991 // column labels to be displayed after being hidden
3992 //
3993 if ( m_colLabelHeight == 0 )
3994 {
3995 // recreate the top sizer
3996 //
3997 m_topSizer = new wxBoxSizer( wxHORIZONTAL );
3998
3999 if ( m_rowLabelWidth > 0 )
4000 m_topSizer->Add( m_cornerLabelWin, 0 );
4001
4002 m_topSizer->Add( m_colLabelWin, 1 );
4003 m_mainSizer->Prepend( m_topSizer, 0, wxEXPAND );
4004
4005 // only show the corner label if the row labels are
4006 // also displayed
4007 //
4008 if ( m_rowLabelWidth > 0 )
4009 m_cornerLabelWin->Show( TRUE );
4010
4011 m_colLabelWin->Show( TRUE );
4012 }
4013
4014 wxList& childList = m_topSizer->GetChildren();
4015 wxNode *node = childList.First();
4016 while (node)
4017 {
4018 wxSizerItem *item = (wxSizerItem*)node->Data();
4019
4020 if ( (item->GetWindow() == m_cornerLabelWin && m_rowLabelWidth > 0) ||
4021 item->GetWindow() == m_colLabelWin )
4022 {
4023 sz = item->GetWindow()->GetSize();
4024 item->SetInitSize( sz.GetWidth(), height );
4025 }
4026 node = node->Next();
4027 }
4028 }
4029
4030 m_colLabelHeight = height;
4031 m_mainSizer->Layout();
4032 }
4033 }
4034
4035
4036 void wxGrid::SetLabelBackgroundColour( const wxColour& colour )
4037 {
4038 if ( m_labelBackgroundColour != colour )
4039 {
4040 m_labelBackgroundColour = colour;
4041 m_rowLabelWin->SetBackgroundColour( colour );
4042 m_colLabelWin->SetBackgroundColour( colour );
4043 m_cornerLabelWin->SetBackgroundColour( colour );
4044
4045 if ( !GetBatchCount() )
4046 {
4047 m_rowLabelWin->Refresh();
4048 m_colLabelWin->Refresh();
4049 m_cornerLabelWin->Refresh();
4050 }
4051 }
4052 }
4053
4054 void wxGrid::SetLabelTextColour( const wxColour& colour )
4055 {
4056 if ( m_labelTextColour != colour )
4057 {
4058 m_labelTextColour = colour;
4059 if ( !GetBatchCount() )
4060 {
4061 m_rowLabelWin->Refresh();
4062 m_colLabelWin->Refresh();
4063 }
4064 }
4065 }
4066
4067 void wxGrid::SetLabelFont( const wxFont& font )
4068 {
4069 m_labelFont = font;
4070 if ( !GetBatchCount() )
4071 {
4072 m_rowLabelWin->Refresh();
4073 m_colLabelWin->Refresh();
4074 }
4075 }
4076
4077 void wxGrid::SetRowLabelAlignment( int horiz, int vert )
4078 {
4079 if ( horiz == wxLEFT || horiz == wxCENTRE || horiz == wxRIGHT )
4080 {
4081 m_rowLabelHorizAlign = horiz;
4082 }
4083
4084 if ( vert == wxTOP || vert == wxCENTRE || vert == wxBOTTOM )
4085 {
4086 m_rowLabelVertAlign = vert;
4087 }
4088
4089 if ( !GetBatchCount() )
4090 {
4091 m_rowLabelWin->Refresh();
4092 }
4093 }
4094
4095 void wxGrid::SetColLabelAlignment( int horiz, int vert )
4096 {
4097 if ( horiz == wxLEFT || horiz == wxCENTRE || horiz == wxRIGHT )
4098 {
4099 m_colLabelHorizAlign = horiz;
4100 }
4101
4102 if ( vert == wxTOP || vert == wxCENTRE || vert == wxBOTTOM )
4103 {
4104 m_colLabelVertAlign = vert;
4105 }
4106
4107 if ( !GetBatchCount() )
4108 {
4109 m_colLabelWin->Refresh();
4110 }
4111 }
4112
4113 void wxGrid::SetRowLabelValue( int row, const wxString& s )
4114 {
4115 if ( m_table )
4116 {
4117 m_table->SetRowLabelValue( row, s );
4118 if ( !GetBatchCount() )
4119 {
4120 wxRect rect = CellToRect( row, 0);
4121 if ( rect.height > 0 )
4122 {
4123 CalcScrolledPosition(0, rect.y, &rect.x, &rect.y);
4124 rect.x = m_left;
4125 rect.width = m_rowLabelWidth;
4126 m_rowLabelWin->Refresh( TRUE, &rect );
4127 }
4128 }
4129 }
4130 }
4131
4132 void wxGrid::SetColLabelValue( int col, const wxString& s )
4133 {
4134 if ( m_table )
4135 {
4136 m_table->SetColLabelValue( col, s );
4137 if ( !GetBatchCount() )
4138 {
4139 wxRect rect = CellToRect( 0, col );
4140 if ( rect.width > 0 )
4141 {
4142 CalcScrolledPosition(rect.x, 0, &rect.x, &rect.y);
4143 rect.y = m_top;
4144 rect.height = m_colLabelHeight;
4145 m_colLabelWin->Refresh( TRUE, &rect );
4146 }
4147 }
4148 }
4149 }
4150
4151 void wxGrid::SetGridLineColour( const wxColour& colour )
4152 {
4153 if ( m_gridLineColour != colour )
4154 {
4155 m_gridLineColour = colour;
4156
4157 wxClientDC dc( m_gridWin );
4158 PrepareDC( dc );
4159 DrawAllGridLines( dc, wxRegion() );
4160 }
4161 }
4162
4163 void wxGrid::EnableGridLines( bool enable )
4164 {
4165 if ( enable != m_gridLinesEnabled )
4166 {
4167 m_gridLinesEnabled = enable;
4168
4169 if ( !GetBatchCount() )
4170 {
4171 if ( enable )
4172 {
4173 wxClientDC dc( m_gridWin );
4174 PrepareDC( dc );
4175 DrawAllGridLines( dc, wxRegion() );
4176 }
4177 else
4178 {
4179 m_gridWin->Refresh();
4180 }
4181 }
4182 }
4183 }
4184
4185
4186 int wxGrid::GetDefaultRowSize()
4187 {
4188 return m_defaultRowHeight;
4189 }
4190
4191 int wxGrid::GetRowSize( int row )
4192 {
4193 if ( row >= 0 && row < m_numRows )
4194 return m_rowHeights[row];
4195 else
4196 return 0; // TODO: log an error here
4197 }
4198
4199 int wxGrid::GetDefaultColSize()
4200 {
4201 return m_defaultColWidth;
4202 }
4203
4204 int wxGrid::GetColSize( int col )
4205 {
4206 if ( col >= 0 && col < m_numCols )
4207 return m_colWidths[col];
4208 else
4209 return 0; // TODO: log an error here
4210 }
4211
4212 wxColour wxGrid::GetDefaultCellBackgroundColour()
4213 {
4214 // TODO: replace this temp test code
4215 //
4216 return wxColour( 255, 255, 255 );
4217 }
4218
4219 wxColour wxGrid::GetCellBackgroundColour( int WXUNUSED(row), int WXUNUSED(col) )
4220 {
4221 // TODO: replace this temp test code
4222 //
4223 return wxColour( 255, 255, 255 );
4224 }
4225
4226 wxColour wxGrid::GetDefaultCellTextColour()
4227 {
4228 // TODO: replace this temp test code
4229 //
4230 return wxColour( 0, 0, 0 );
4231 }
4232
4233 wxColour wxGrid::GetCellTextColour( int WXUNUSED(row), int WXUNUSED(col) )
4234 {
4235 // TODO: replace this temp test code
4236 //
4237 return wxColour( 0, 0, 0 );
4238 }
4239
4240
4241 wxFont wxGrid::GetDefaultCellFont()
4242 {
4243 return m_defaultCellFont;
4244 }
4245
4246 wxFont wxGrid::GetCellFont( int WXUNUSED(row), int WXUNUSED(col) )
4247 {
4248 // TODO: replace this temp test code
4249 //
4250 return m_defaultCellFont;
4251 }
4252
4253 void wxGrid::GetDefaultCellAlignment( int *horiz, int *vert )
4254 {
4255 // TODO: replace this temp test code
4256 //
4257 *horiz = wxLEFT;
4258 *vert = wxTOP;
4259 }
4260
4261 void wxGrid::GetCellAlignment( int WXUNUSED(row), int WXUNUSED(col), int *horiz, int *vert )
4262 {
4263 // TODO: replace this temp test code
4264 //
4265 *horiz = wxLEFT;
4266 *vert = wxTOP;
4267 }
4268
4269 void wxGrid::SetDefaultRowSize( int height, bool resizeExistingRows )
4270 {
4271 m_defaultRowHeight = wxMax( height, WXGRID_MIN_ROW_HEIGHT );
4272
4273 if ( resizeExistingRows )
4274 {
4275 int row;
4276 int bottom = 0;
4277 for ( row = 0; row < m_numRows; row++ )
4278 {
4279 m_rowHeights[row] = m_defaultRowHeight;
4280 bottom += m_defaultRowHeight;
4281 m_rowBottoms[row] = bottom;
4282 }
4283 CalcDimensions();
4284 }
4285 }
4286
4287 void wxGrid::SetRowSize( int row, int height )
4288 {
4289 int i;
4290
4291 if ( row >= 0 && row < m_numRows )
4292 {
4293 int h = wxMax( 0, height );
4294 int diff = h - m_rowHeights[row];
4295
4296 m_rowHeights[row] = h;
4297 for ( i = row; i < m_numRows; i++ )
4298 {
4299 m_rowBottoms[i] += diff;
4300 }
4301 CalcDimensions();
4302
4303 // Note: we are ending the event *after* doing
4304 // default processing in this case
4305 //
4306 SendEvent( EVT_GRID_ROW_SIZE,
4307 row, -1 );
4308 }
4309 else
4310 {
4311 // TODO: log an error here
4312 }
4313 }
4314
4315 void wxGrid::SetDefaultColSize( int width, bool resizeExistingCols )
4316 {
4317 m_defaultColWidth = wxMax( width, WXGRID_MIN_COL_WIDTH );
4318
4319 if ( resizeExistingCols )
4320 {
4321 int col;
4322 int right = 0;
4323 for ( col = 0; col < m_numCols; col++ )
4324 {
4325 m_colWidths[col] = m_defaultColWidth;
4326 right += m_defaultColWidth;
4327 m_colRights[col] = right;
4328 }
4329 CalcDimensions();
4330 }
4331 }
4332
4333 void wxGrid::SetColSize( int col, int width )
4334 {
4335 int i;
4336
4337 if ( col >= 0 && col < m_numCols )
4338 {
4339 int w = wxMax( 0, width );
4340 int diff = w - m_colWidths[col];
4341 m_colWidths[col] = w;
4342
4343 for ( i = col; i < m_numCols; i++ )
4344 {
4345 m_colRights[i] += diff;
4346 }
4347 CalcDimensions();
4348
4349 // Note: we are ending the event *after* doing
4350 // default processing in this case
4351 //
4352 SendEvent( EVT_GRID_COL_SIZE,
4353 -1, col );
4354 }
4355 else
4356 {
4357 // TODO: log an error here
4358 }
4359 }
4360
4361 void wxGrid::SetDefaultCellBackgroundColour( const wxColour& )
4362 {
4363 // TODO: everything !!!
4364 //
4365 }
4366
4367 void wxGrid::SetCellBackgroundColour( int WXUNUSED(row), int WXUNUSED(col), const wxColour& )
4368 {
4369 // TODO: everything !!!
4370 //
4371 }
4372
4373 void wxGrid::SetDefaultCellTextColour( const wxColour& )
4374 {
4375 // TODO: everything !!!
4376 //
4377 }
4378
4379 void wxGrid::SetCellTextColour( int WXUNUSED(row), int WXUNUSED(col), const wxColour& )
4380 {
4381 // TODO: everything !!!
4382 //
4383 }
4384
4385 void wxGrid::SetDefaultCellFont( const wxFont& )
4386 {
4387 // TODO: everything !!!
4388 //
4389 }
4390
4391 void wxGrid::SetCellFont( int WXUNUSED(row), int WXUNUSED(col), const wxFont& )
4392 {
4393 // TODO: everything !!!
4394 //
4395 }
4396
4397 void wxGrid::SetDefaultCellAlignment( int WXUNUSED(horiz), int WXUNUSED(vert) )
4398 {
4399 // TODO: everything !!!
4400 //
4401 }
4402
4403 void wxGrid::SetCellAlignment( int WXUNUSED(row), int WXUNUSED(col), int WXUNUSED(horiz), int WXUNUSED(vert) )
4404 {
4405 // TODO: everything !!!
4406 //
4407 }
4408
4409
4410
4411 //
4412 // ------ cell value accessor functions
4413 //
4414
4415 void wxGrid::SetCellValue( int row, int col, const wxString& s )
4416 {
4417 if ( m_table )
4418 {
4419 m_table->SetValue( row, col, s.c_str() );
4420 if ( !GetBatchCount() )
4421 {
4422 wxClientDC dc( m_gridWin );
4423 PrepareDC( dc );
4424 DrawCell( dc, wxGridCellCoords(row, col) );
4425 }
4426
4427 #if 0 // TODO: edit in place
4428
4429 if ( m_currentCellCoords.GetRow() == row &&
4430 m_currentCellCoords.GetCol() == col )
4431 {
4432 SetEditControlValue( s );
4433 }
4434 #endif
4435
4436 }
4437 }
4438
4439
4440 //
4441 // ------ Block, row and col selection
4442 //
4443
4444 void wxGrid::SelectRow( int row, bool addToSelected )
4445 {
4446 wxRect r;
4447
4448 if ( IsSelection() && addToSelected )
4449 {
4450 wxRect rect[4];
4451 bool need_refresh[4] = { FALSE, FALSE, FALSE, FALSE };
4452 int i;
4453
4454 wxCoord oldLeft = m_selectedTopLeft.GetCol();
4455 wxCoord oldTop = m_selectedTopLeft.GetRow();
4456 wxCoord oldRight = m_selectedBottomRight.GetCol();
4457 wxCoord oldBottom = m_selectedBottomRight.GetRow();
4458
4459 if ( oldTop > row )
4460 {
4461 need_refresh[0] = TRUE;
4462 rect[0] = BlockToDeviceRect( wxGridCellCoords ( row, 0 ),
4463 wxGridCellCoords ( oldTop - 1,
4464 m_numCols - 1 ) );
4465 m_selectedTopLeft.SetRow( row );
4466 }
4467
4468 if ( oldLeft > 0 )
4469 {
4470 need_refresh[1] = TRUE;
4471 rect[1] = BlockToDeviceRect( wxGridCellCoords ( oldTop, 0 ),
4472 wxGridCellCoords ( oldBottom,
4473 oldLeft - 1 ) );
4474
4475 m_selectedTopLeft.SetCol( 0 );
4476 }
4477
4478 if ( oldBottom < row )
4479 {
4480 need_refresh[2] = TRUE;
4481 rect[2] = BlockToDeviceRect( wxGridCellCoords ( oldBottom + 1, 0 ),
4482 wxGridCellCoords ( row,
4483 m_numCols - 1 ) );
4484 m_selectedBottomRight.SetRow( row );
4485 }
4486
4487 if ( oldRight < m_numCols - 1 )
4488 {
4489 need_refresh[3] = TRUE;
4490 rect[3] = BlockToDeviceRect( wxGridCellCoords ( oldTop ,
4491 oldRight + 1 ),
4492 wxGridCellCoords ( oldBottom,
4493 m_numCols - 1 ) );
4494 m_selectedBottomRight.SetCol( m_numCols - 1 );
4495 }
4496
4497 for (i = 0; i < 4; i++ )
4498 if ( need_refresh[i] && rect[i] != wxGridNoCellRect )
4499 m_gridWin->Refresh( FALSE, &(rect[i]) );
4500 }
4501 else
4502 {
4503 r = SelectionToDeviceRect();
4504 ClearSelection();
4505 if ( r != wxGridNoCellRect ) m_gridWin->Refresh( FALSE, &r );
4506
4507 m_selectedTopLeft.Set( row, 0 );
4508 m_selectedBottomRight.Set( row, m_numCols-1 );
4509 r = SelectionToDeviceRect();
4510 m_gridWin->Refresh( FALSE, &r );
4511 }
4512
4513 wxGridRangeSelectEvent gridEvt( GetId(),
4514 EVT_GRID_RANGE_SELECT,
4515 this,
4516 m_selectedTopLeft,
4517 m_selectedBottomRight );
4518
4519 GetEventHandler()->ProcessEvent(gridEvt);
4520 }
4521
4522
4523 void wxGrid::SelectCol( int col, bool addToSelected )
4524 {
4525 if ( IsSelection() && addToSelected )
4526 {
4527 wxRect rect[4];
4528 bool need_refresh[4] = { FALSE, FALSE, FALSE, FALSE };
4529 int i;
4530
4531 wxCoord oldLeft = m_selectedTopLeft.GetCol();
4532 wxCoord oldTop = m_selectedTopLeft.GetRow();
4533 wxCoord oldRight = m_selectedBottomRight.GetCol();
4534 wxCoord oldBottom = m_selectedBottomRight.GetRow();
4535
4536 if ( oldLeft > col )
4537 {
4538 need_refresh[0] = TRUE;
4539 rect[0] = BlockToDeviceRect( wxGridCellCoords ( 0, col ),
4540 wxGridCellCoords ( m_numRows - 1,
4541 oldLeft - 1 ) );
4542 m_selectedTopLeft.SetCol( col );
4543 }
4544
4545 if ( oldTop > 0 )
4546 {
4547 need_refresh[1] = TRUE;
4548 rect[1] = BlockToDeviceRect( wxGridCellCoords ( 0, oldLeft ),
4549 wxGridCellCoords ( oldTop - 1,
4550 oldRight ) );
4551 m_selectedTopLeft.SetRow( 0 );
4552 }
4553
4554 if ( oldRight < col )
4555 {
4556 need_refresh[2] = TRUE;
4557 rect[2] = BlockToDeviceRect( wxGridCellCoords ( 0, oldRight + 1 ),
4558 wxGridCellCoords ( m_numRows - 1,
4559 col ) );
4560 m_selectedBottomRight.SetCol( col );
4561 }
4562
4563 if ( oldBottom < m_numRows - 1 )
4564 {
4565 need_refresh[3] = TRUE;
4566 rect[3] = BlockToDeviceRect( wxGridCellCoords ( oldBottom + 1,
4567 oldLeft ),
4568 wxGridCellCoords ( m_numRows - 1,
4569 oldRight ) );
4570 m_selectedBottomRight.SetRow( m_numRows - 1 );
4571 }
4572
4573 for (i = 0; i < 4; i++ )
4574 if ( need_refresh[i] && rect[i] != wxGridNoCellRect )
4575 m_gridWin->Refresh( FALSE, &(rect[i]) );
4576 }
4577 else
4578 {
4579 wxRect r;
4580
4581 r = SelectionToDeviceRect();
4582 ClearSelection();
4583 if ( r != wxGridNoCellRect ) m_gridWin->Refresh( FALSE, &r );
4584
4585 m_selectedTopLeft.Set( 0, col );
4586 m_selectedBottomRight.Set( m_numRows-1, col );
4587 r = SelectionToDeviceRect();
4588 m_gridWin->Refresh( FALSE, &r );
4589 }
4590
4591 wxGridRangeSelectEvent gridEvt( GetId(),
4592 EVT_GRID_RANGE_SELECT,
4593 this,
4594 m_selectedTopLeft,
4595 m_selectedBottomRight );
4596
4597 GetEventHandler()->ProcessEvent(gridEvt);
4598 }
4599
4600
4601 void wxGrid::SelectBlock( int topRow, int leftCol, int bottomRow, int rightCol )
4602 {
4603 int temp;
4604 wxGridCellCoords updateTopLeft, updateBottomRight;
4605
4606 if ( topRow > bottomRow )
4607 {
4608 temp = topRow;
4609 topRow = bottomRow;
4610 bottomRow = temp;
4611 }
4612
4613 if ( leftCol > rightCol )
4614 {
4615 temp = leftCol;
4616 leftCol = rightCol;
4617 rightCol = temp;
4618 }
4619
4620 updateTopLeft = wxGridCellCoords( topRow, leftCol );
4621 updateBottomRight = wxGridCellCoords( bottomRow, rightCol );
4622
4623 if ( m_selectedTopLeft != updateTopLeft ||
4624 m_selectedBottomRight != updateBottomRight )
4625 {
4626 // Compute two optimal update rectangles:
4627 // Either one rectangle is a real subset of the
4628 // other, or they are (almost) disjoint!
4629 wxRect rect[4];
4630 bool need_refresh[4] = { FALSE, FALSE, FALSE, FALSE };
4631 int i;
4632
4633 // Store intermediate values
4634 wxCoord oldLeft = m_selectedTopLeft.GetCol();
4635 wxCoord oldTop = m_selectedTopLeft.GetRow();
4636 wxCoord oldRight = m_selectedBottomRight.GetCol();
4637 wxCoord oldBottom = m_selectedBottomRight.GetRow();
4638
4639 // Determine the outer/inner coordinates.
4640 if (oldLeft > leftCol)
4641 {
4642 temp = oldLeft;
4643 oldLeft = leftCol;
4644 leftCol = temp;
4645 }
4646 if (oldTop > topRow )
4647 {
4648 temp = oldTop;
4649 oldTop = topRow;
4650 topRow = temp;
4651 }
4652 if (oldRight < rightCol )
4653 {
4654 temp = oldRight;
4655 oldRight = rightCol;
4656 rightCol = temp;
4657 }
4658 if (oldBottom < bottomRow)
4659 {
4660 temp = oldBottom;
4661 oldBottom = bottomRow;
4662 bottomRow = temp;
4663 }
4664
4665 // Now, either the stuff marked old is the outer
4666 // rectangle or we don't have a situation where one
4667 // is contained in the other.
4668
4669 if ( oldLeft < leftCol )
4670 {
4671 need_refresh[0] = TRUE;
4672 rect[0] = BlockToDeviceRect( wxGridCellCoords ( oldTop,
4673 oldLeft ),
4674 wxGridCellCoords ( oldBottom,
4675 leftCol - 1 ) );
4676 }
4677
4678 if ( oldTop < topRow )
4679 {
4680 need_refresh[1] = TRUE;
4681 rect[1] = BlockToDeviceRect( wxGridCellCoords ( oldTop,
4682 leftCol ),
4683 wxGridCellCoords ( topRow - 1,
4684 rightCol ) );
4685 }
4686
4687 if ( oldRight > rightCol )
4688 {
4689 need_refresh[2] = TRUE;
4690 rect[2] = BlockToDeviceRect( wxGridCellCoords ( oldTop,
4691 rightCol + 1 ),
4692 wxGridCellCoords ( oldBottom,
4693 oldRight ) );
4694 }
4695
4696 if ( oldBottom > bottomRow )
4697 {
4698 need_refresh[3] = TRUE;
4699 rect[3] = BlockToDeviceRect( wxGridCellCoords ( bottomRow + 1,
4700 leftCol ),
4701 wxGridCellCoords ( oldBottom,
4702 rightCol ) );
4703 }
4704
4705
4706 // Change Selection
4707 m_selectedTopLeft = updateTopLeft;
4708 m_selectedBottomRight = updateBottomRight;
4709
4710 // various Refresh() calls
4711 for (i = 0; i < 4; i++ )
4712 if ( need_refresh[i] && rect[i] != wxGridNoCellRect )
4713 m_gridWin->Refresh( FALSE, &(rect[i]) );
4714 }
4715
4716 // only generate an event if the block is not being selected by
4717 // dragging the mouse (in which case the event will be generated in
4718 // the mouse event handler)
4719 if ( !m_isDragging )
4720 {
4721 wxGridRangeSelectEvent gridEvt( GetId(),
4722 EVT_GRID_RANGE_SELECT,
4723 this,
4724 m_selectedTopLeft,
4725 m_selectedBottomRight );
4726
4727 GetEventHandler()->ProcessEvent(gridEvt);
4728 }
4729 }
4730
4731 void wxGrid::SelectAll()
4732 {
4733 m_selectedTopLeft.Set( 0, 0 );
4734 m_selectedBottomRight.Set( m_numRows-1, m_numCols-1 );
4735
4736 m_gridWin->Refresh();
4737 }
4738
4739
4740 void wxGrid::ClearSelection()
4741 {
4742 m_selectedTopLeft = wxGridNoCellCoords;
4743 m_selectedBottomRight = wxGridNoCellCoords;
4744 }
4745
4746
4747 // This function returns the rectangle that encloses the given block
4748 // in device coords clipped to the client size of the grid window.
4749 //
4750 wxRect wxGrid::BlockToDeviceRect( const wxGridCellCoords &topLeft,
4751 const wxGridCellCoords &bottomRight )
4752 {
4753 wxRect rect( wxGridNoCellRect );
4754 wxRect cellRect;
4755
4756 cellRect = CellToRect( topLeft );
4757 if ( cellRect != wxGridNoCellRect )
4758 {
4759 rect = cellRect;
4760 }
4761 else
4762 {
4763 rect = wxRect( 0, 0, 0, 0 );
4764 }
4765
4766 cellRect = CellToRect( bottomRight );
4767 if ( cellRect != wxGridNoCellRect )
4768 {
4769 rect += cellRect;
4770 }
4771 else
4772 {
4773 return wxGridNoCellRect;
4774 }
4775
4776 // convert to scrolled coords
4777 //
4778 int left, top, right, bottom;
4779 CalcScrolledPosition( rect.GetLeft(), rect.GetTop(), &left, &top );
4780 CalcScrolledPosition( rect.GetRight(), rect.GetBottom(), &right, &bottom );
4781
4782 int cw, ch;
4783 m_gridWin->GetClientSize( &cw, &ch );
4784
4785 rect.SetLeft( wxMax(0, left) );
4786 rect.SetTop( wxMax(0, top) );
4787 rect.SetRight( wxMin(cw, right) );
4788 rect.SetBottom( wxMin(ch, bottom) );
4789
4790 return rect;
4791 }
4792
4793
4794
4795 //
4796 // ------ Grid event classes
4797 //
4798
4799 IMPLEMENT_DYNAMIC_CLASS( wxGridEvent, wxEvent )
4800
4801 wxGridEvent::wxGridEvent( int id, wxEventType type, wxObject* obj,
4802 int row, int col, int x, int y,
4803 bool control, bool shift, bool alt, bool meta )
4804 : wxNotifyEvent( type, id )
4805 {
4806 m_row = row;
4807 m_col = col;
4808 m_x = x;
4809 m_y = y;
4810 m_control = control;
4811 m_shift = shift;
4812 m_alt = alt;
4813 m_meta = meta;
4814
4815 SetEventObject(obj);
4816 }
4817
4818
4819 IMPLEMENT_DYNAMIC_CLASS( wxGridSizeEvent, wxEvent )
4820
4821 wxGridSizeEvent::wxGridSizeEvent( int id, wxEventType type, wxObject* obj,
4822 int rowOrCol, int x, int y,
4823 bool control, bool shift, bool alt, bool meta )
4824 : wxNotifyEvent( type, id )
4825 {
4826 m_rowOrCol = rowOrCol;
4827 m_x = x;
4828 m_y = y;
4829 m_control = control;
4830 m_shift = shift;
4831 m_alt = alt;
4832 m_meta = meta;
4833
4834 SetEventObject(obj);
4835 }
4836
4837
4838 IMPLEMENT_DYNAMIC_CLASS( wxGridRangeSelectEvent, wxEvent )
4839
4840 wxGridRangeSelectEvent::wxGridRangeSelectEvent(int id, wxEventType type, wxObject* obj,
4841 const wxGridCellCoords& topLeft,
4842 const wxGridCellCoords& bottomRight,
4843 bool control, bool shift, bool alt, bool meta )
4844 : wxNotifyEvent( type, id )
4845 {
4846 m_topLeft = topLeft;
4847 m_bottomRight = bottomRight;
4848 m_control = control;
4849 m_shift = shift;
4850 m_alt = alt;
4851 m_meta = meta;
4852
4853 SetEventObject(obj);
4854 }
4855
4856
4857 #endif // ifndef wxUSE_NEW_GRID
4858