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