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