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