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