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