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