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