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