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