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