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