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