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