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