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