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