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