Added functions to enable/disable drag-resizing of rows and cols.
[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_canDragRowSize = TRUE;
2402 m_canDragColSize = TRUE;
2403 m_dragLastPos = -1;
2404 m_dragRowOrCol = -1;
2405 m_isDragging = FALSE;
2406 m_startDragPos = wxDefaultPosition;
2407
2408 m_waitForSlowClick = FALSE;
2409
2410 m_rowResizeCursor = wxCursor( wxCURSOR_SIZENS );
2411 m_colResizeCursor = wxCursor( wxCURSOR_SIZEWE );
2412
2413 m_currentCellCoords = wxGridNoCellCoords;
2414
2415 m_selectedTopLeft = wxGridNoCellCoords;
2416 m_selectedBottomRight = wxGridNoCellCoords;
2417 m_selectionBackground = wxSystemSettings::GetSystemColour(wxSYS_COLOUR_HIGHLIGHT);
2418 m_selectionForeground = wxSystemSettings::GetSystemColour(wxSYS_COLOUR_HIGHLIGHTTEXT);
2419
2420 m_editable = TRUE; // default for whole grid
2421
2422 m_inOnKeyDown = FALSE;
2423 m_batchCount = 0;
2424 }
2425
2426 // ----------------------------------------------------------------------------
2427 // the idea is to call these functions only when necessary because they create
2428 // quite big arrays which eat memory mostly unnecessary - in particular, if
2429 // default widths/heights are used for all rows/columns, we may not use these
2430 // arrays at all
2431 //
2432 // with some extra code, it should be possible to only store the
2433 // widths/heights different from default ones but this will be done later...
2434 // ----------------------------------------------------------------------------
2435
2436 void wxGrid::InitRowHeights()
2437 {
2438 m_rowHeights.Empty();
2439 m_rowBottoms.Empty();
2440
2441 m_rowHeights.Alloc( m_numRows );
2442 m_rowBottoms.Alloc( m_numRows );
2443
2444 int rowBottom = 0;
2445 for ( int i = 0; i < m_numRows; i++ )
2446 {
2447 m_rowHeights.Add( m_defaultRowHeight );
2448 rowBottom += m_defaultRowHeight;
2449 m_rowBottoms.Add( rowBottom );
2450 }
2451 }
2452
2453 void wxGrid::InitColWidths()
2454 {
2455 m_colWidths.Empty();
2456 m_colRights.Empty();
2457
2458 m_colWidths.Alloc( m_numCols );
2459 m_colRights.Alloc( m_numCols );
2460 int colRight = 0;
2461 for ( int i = 0; i < m_numCols; i++ )
2462 {
2463 m_colWidths.Add( m_defaultColWidth );
2464 colRight += m_defaultColWidth;
2465 m_colRights.Add( colRight );
2466 }
2467 }
2468
2469 int wxGrid::GetColWidth(int col) const
2470 {
2471 return m_colWidths.IsEmpty() ? m_defaultColWidth : m_colWidths[col];
2472 }
2473
2474 int wxGrid::GetColLeft(int col) const
2475 {
2476 return m_colRights.IsEmpty() ? col * m_defaultColWidth
2477 : m_colRights[col] - m_colWidths[col];
2478 }
2479
2480 int wxGrid::GetColRight(int col) const
2481 {
2482 return m_colRights.IsEmpty() ? (col + 1) * m_defaultColWidth
2483 : m_colRights[col];
2484 }
2485
2486 int wxGrid::GetRowHeight(int row) const
2487 {
2488 return m_rowHeights.IsEmpty() ? m_defaultRowHeight : m_rowHeights[row];
2489 }
2490
2491 int wxGrid::GetRowTop(int row) const
2492 {
2493 return m_rowBottoms.IsEmpty() ? row * m_defaultRowHeight
2494 : m_rowBottoms[row] - m_rowHeights[row];
2495 }
2496
2497 int wxGrid::GetRowBottom(int row) const
2498 {
2499 return m_rowBottoms.IsEmpty() ? (row + 1) * m_defaultRowHeight
2500 : m_rowBottoms[row];
2501 }
2502
2503 void wxGrid::CalcDimensions()
2504 {
2505 int cw, ch;
2506 GetClientSize( &cw, &ch );
2507
2508 if ( m_numRows > 0 && m_numCols > 0 )
2509 {
2510 int right = GetColRight( m_numCols-1 ) + 50;
2511 int bottom = GetRowBottom( m_numRows-1 ) + 50;
2512
2513 // TODO: restore the scroll position that we had before sizing
2514 //
2515 int x, y;
2516 GetViewStart( &x, &y );
2517 SetScrollbars( GRID_SCROLL_LINE, GRID_SCROLL_LINE,
2518 right/GRID_SCROLL_LINE, bottom/GRID_SCROLL_LINE,
2519 x, y );
2520 }
2521 }
2522
2523
2524 void wxGrid::CalcWindowSizes()
2525 {
2526 int cw, ch;
2527 GetClientSize( &cw, &ch );
2528
2529 if ( m_cornerLabelWin->IsShown() )
2530 m_cornerLabelWin->SetSize( 0, 0, m_rowLabelWidth, m_colLabelHeight );
2531
2532 if ( m_colLabelWin->IsShown() )
2533 m_colLabelWin->SetSize( m_rowLabelWidth, 0, cw-m_rowLabelWidth, m_colLabelHeight);
2534
2535 if ( m_rowLabelWin->IsShown() )
2536 m_rowLabelWin->SetSize( 0, m_colLabelHeight, m_rowLabelWidth, ch-m_colLabelHeight);
2537
2538 if ( m_gridWin->IsShown() )
2539 m_gridWin->SetSize( m_rowLabelWidth, m_colLabelHeight, cw-m_rowLabelWidth, ch-m_colLabelHeight);
2540 }
2541
2542
2543 // this is called when the grid table sends a message to say that it
2544 // has been redimensioned
2545 //
2546 bool wxGrid::Redimension( wxGridTableMessage& msg )
2547 {
2548 int i;
2549
2550 // if we were using the default widths/heights so far, we must change them
2551 // now
2552 if ( m_colWidths.IsEmpty() )
2553 {
2554 InitColWidths();
2555 }
2556
2557 if ( m_rowHeights.IsEmpty() )
2558 {
2559 InitRowHeights();
2560 }
2561
2562 switch ( msg.GetId() )
2563 {
2564 case wxGRIDTABLE_NOTIFY_ROWS_INSERTED:
2565 {
2566 size_t pos = msg.GetCommandInt();
2567 int numRows = msg.GetCommandInt2();
2568 for ( i = 0; i < numRows; i++ )
2569 {
2570 m_rowHeights.Insert( m_defaultRowHeight, pos );
2571 m_rowBottoms.Insert( 0, pos );
2572 }
2573 m_numRows += numRows;
2574
2575 int bottom = 0;
2576 if ( pos > 0 ) bottom = m_rowBottoms[pos-1];
2577
2578 for ( i = pos; i < m_numRows; i++ )
2579 {
2580 bottom += m_rowHeights[i];
2581 m_rowBottoms[i] = bottom;
2582 }
2583 CalcDimensions();
2584 }
2585 return TRUE;
2586
2587 case wxGRIDTABLE_NOTIFY_ROWS_APPENDED:
2588 {
2589 int numRows = msg.GetCommandInt();
2590 for ( i = 0; i < numRows; i++ )
2591 {
2592 m_rowHeights.Add( m_defaultRowHeight );
2593 m_rowBottoms.Add( 0 );
2594 }
2595
2596 int oldNumRows = m_numRows;
2597 m_numRows += numRows;
2598
2599 int bottom = 0;
2600 if ( oldNumRows > 0 ) bottom = m_rowBottoms[oldNumRows-1];
2601
2602 for ( i = oldNumRows; i < m_numRows; i++ )
2603 {
2604 bottom += m_rowHeights[i];
2605 m_rowBottoms[i] = bottom;
2606 }
2607 CalcDimensions();
2608 }
2609 return TRUE;
2610
2611 case wxGRIDTABLE_NOTIFY_ROWS_DELETED:
2612 {
2613 size_t pos = msg.GetCommandInt();
2614 int numRows = msg.GetCommandInt2();
2615 for ( i = 0; i < numRows; i++ )
2616 {
2617 m_rowHeights.Remove( pos );
2618 m_rowBottoms.Remove( pos );
2619 }
2620 m_numRows -= numRows;
2621
2622 if ( !m_numRows )
2623 {
2624 m_numCols = 0;
2625 m_colWidths.Clear();
2626 m_colRights.Clear();
2627 m_currentCellCoords = wxGridNoCellCoords;
2628 }
2629 else
2630 {
2631 if ( m_currentCellCoords.GetRow() >= m_numRows )
2632 m_currentCellCoords.Set( 0, 0 );
2633
2634 int h = 0;
2635 for ( i = 0; i < m_numRows; i++ )
2636 {
2637 h += m_rowHeights[i];
2638 m_rowBottoms[i] = h;
2639 }
2640 }
2641
2642 CalcDimensions();
2643 }
2644 return TRUE;
2645
2646 case wxGRIDTABLE_NOTIFY_COLS_INSERTED:
2647 {
2648 size_t pos = msg.GetCommandInt();
2649 int numCols = msg.GetCommandInt2();
2650 for ( i = 0; i < numCols; i++ )
2651 {
2652 m_colWidths.Insert( m_defaultColWidth, pos );
2653 m_colRights.Insert( 0, pos );
2654 }
2655 m_numCols += numCols;
2656
2657 int right = 0;
2658 if ( pos > 0 ) right = m_colRights[pos-1];
2659
2660 for ( i = pos; i < m_numCols; i++ )
2661 {
2662 right += m_colWidths[i];
2663 m_colRights[i] = right;
2664 }
2665 CalcDimensions();
2666 }
2667 return TRUE;
2668
2669 case wxGRIDTABLE_NOTIFY_COLS_APPENDED:
2670 {
2671 int numCols = msg.GetCommandInt();
2672 for ( i = 0; i < numCols; i++ )
2673 {
2674 m_colWidths.Add( m_defaultColWidth );
2675 m_colRights.Add( 0 );
2676 }
2677
2678 int oldNumCols = m_numCols;
2679 m_numCols += numCols;
2680
2681 int right = 0;
2682 if ( oldNumCols > 0 ) right = m_colRights[oldNumCols-1];
2683
2684 for ( i = oldNumCols; i < m_numCols; i++ )
2685 {
2686 right += m_colWidths[i];
2687 m_colRights[i] = right;
2688 }
2689 CalcDimensions();
2690 }
2691 return TRUE;
2692
2693 case wxGRIDTABLE_NOTIFY_COLS_DELETED:
2694 {
2695 size_t pos = msg.GetCommandInt();
2696 int numCols = msg.GetCommandInt2();
2697 for ( i = 0; i < numCols; i++ )
2698 {
2699 m_colWidths.Remove( pos );
2700 m_colRights.Remove( pos );
2701 }
2702 m_numCols -= numCols;
2703
2704 if ( !m_numCols )
2705 {
2706 #if 0 // leave the row alone here so that AppendCols will work subsequently
2707 m_numRows = 0;
2708 m_rowHeights.Clear();
2709 m_rowBottoms.Clear();
2710 #endif
2711 m_currentCellCoords = wxGridNoCellCoords;
2712 }
2713 else
2714 {
2715 if ( m_currentCellCoords.GetCol() >= m_numCols )
2716 m_currentCellCoords.Set( 0, 0 );
2717
2718 int w = 0;
2719 for ( i = 0; i < m_numCols; i++ )
2720 {
2721 w += m_colWidths[i];
2722 m_colRights[i] = w;
2723 }
2724 }
2725 CalcDimensions();
2726 }
2727 return TRUE;
2728 }
2729
2730 return FALSE;
2731 }
2732
2733
2734 void wxGrid::CalcRowLabelsExposed( wxRegion& reg )
2735 {
2736 wxRegionIterator iter( reg );
2737 wxRect r;
2738
2739 m_rowLabelsExposed.Empty();
2740
2741 int top, bottom;
2742 while ( iter )
2743 {
2744 r = iter.GetRect();
2745
2746 // TODO: remove this when we can...
2747 // There is a bug in wxMotif that gives garbage update
2748 // rectangles if you jump-scroll a long way by clicking the
2749 // scrollbar with middle button. This is a work-around
2750 //
2751 #if defined(__WXMOTIF__)
2752 int cw, ch;
2753 m_gridWin->GetClientSize( &cw, &ch );
2754 if ( r.GetTop() > ch ) r.SetTop( 0 );
2755 r.SetBottom( wxMin( r.GetBottom(), ch ) );
2756 #endif
2757
2758 // logical bounds of update region
2759 //
2760 int dummy;
2761 CalcUnscrolledPosition( 0, r.GetTop(), &dummy, &top );
2762 CalcUnscrolledPosition( 0, r.GetBottom(), &dummy, &bottom );
2763
2764 // find the row labels within these bounds
2765 //
2766 int row;
2767 for ( row = 0; row < m_numRows; row++ )
2768 {
2769 if ( GetRowBottom(row) < top )
2770 continue;
2771
2772 if ( GetRowTop(row) > bottom )
2773 break;
2774
2775 m_rowLabelsExposed.Add( row );
2776 }
2777
2778 iter++ ;
2779 }
2780 }
2781
2782
2783 void wxGrid::CalcColLabelsExposed( wxRegion& reg )
2784 {
2785 wxRegionIterator iter( reg );
2786 wxRect r;
2787
2788 m_colLabelsExposed.Empty();
2789
2790 int left, right;
2791 while ( iter )
2792 {
2793 r = iter.GetRect();
2794
2795 // TODO: remove this when we can...
2796 // There is a bug in wxMotif that gives garbage update
2797 // rectangles if you jump-scroll a long way by clicking the
2798 // scrollbar with middle button. This is a work-around
2799 //
2800 #if defined(__WXMOTIF__)
2801 int cw, ch;
2802 m_gridWin->GetClientSize( &cw, &ch );
2803 if ( r.GetLeft() > cw ) r.SetLeft( 0 );
2804 r.SetRight( wxMin( r.GetRight(), cw ) );
2805 #endif
2806
2807 // logical bounds of update region
2808 //
2809 int dummy;
2810 CalcUnscrolledPosition( r.GetLeft(), 0, &left, &dummy );
2811 CalcUnscrolledPosition( r.GetRight(), 0, &right, &dummy );
2812
2813 // find the cells within these bounds
2814 //
2815 int col;
2816 for ( col = 0; col < m_numCols; col++ )
2817 {
2818 if ( GetColRight(col) < left )
2819 continue;
2820
2821 if ( GetColLeft(col) > right )
2822 break;
2823
2824 m_colLabelsExposed.Add( col );
2825 }
2826
2827 iter++ ;
2828 }
2829 }
2830
2831
2832 void wxGrid::CalcCellsExposed( wxRegion& reg )
2833 {
2834 wxRegionIterator iter( reg );
2835 wxRect r;
2836
2837 m_cellsExposed.Empty();
2838 m_rowsExposed.Empty();
2839 m_colsExposed.Empty();
2840
2841 int left, top, right, bottom;
2842 while ( iter )
2843 {
2844 r = iter.GetRect();
2845
2846 // TODO: remove this when we can...
2847 // There is a bug in wxMotif that gives garbage update
2848 // rectangles if you jump-scroll a long way by clicking the
2849 // scrollbar with middle button. This is a work-around
2850 //
2851 #if defined(__WXMOTIF__)
2852 int cw, ch;
2853 m_gridWin->GetClientSize( &cw, &ch );
2854 if ( r.GetTop() > ch ) r.SetTop( 0 );
2855 if ( r.GetLeft() > cw ) r.SetLeft( 0 );
2856 r.SetRight( wxMin( r.GetRight(), cw ) );
2857 r.SetBottom( wxMin( r.GetBottom(), ch ) );
2858 #endif
2859
2860 // logical bounds of update region
2861 //
2862 CalcUnscrolledPosition( r.GetLeft(), r.GetTop(), &left, &top );
2863 CalcUnscrolledPosition( r.GetRight(), r.GetBottom(), &right, &bottom );
2864
2865 // find the cells within these bounds
2866 //
2867 int row, col;
2868 for ( row = 0; row < m_numRows; row++ )
2869 {
2870 if ( GetRowBottom(row) <= top )
2871 continue;
2872
2873 if ( GetRowTop(row) > bottom )
2874 break;
2875
2876 m_rowsExposed.Add( row );
2877
2878 for ( col = 0; col < m_numCols; col++ )
2879 {
2880 if ( GetColRight(col) <= left )
2881 continue;
2882
2883 if ( GetColLeft(col) > right )
2884 break;
2885
2886 if ( m_colsExposed.Index( col ) == wxNOT_FOUND )
2887 m_colsExposed.Add( col );
2888 m_cellsExposed.Add( wxGridCellCoords( row, col ) );
2889 }
2890 }
2891
2892 iter++;
2893 }
2894 }
2895
2896
2897 void wxGrid::ProcessRowLabelMouseEvent( wxMouseEvent& event )
2898 {
2899 int x, y, row;
2900 wxPoint pos( event.GetPosition() );
2901 CalcUnscrolledPosition( pos.x, pos.y, &x, &y );
2902
2903 if ( event.Dragging() )
2904 {
2905 m_isDragging = TRUE;
2906
2907 if ( event.LeftIsDown() )
2908 {
2909 switch( m_cursorMode )
2910 {
2911 case WXGRID_CURSOR_RESIZE_ROW:
2912 {
2913 int cw, ch, left, dummy;
2914 m_gridWin->GetClientSize( &cw, &ch );
2915 CalcUnscrolledPosition( 0, 0, &left, &dummy );
2916
2917 wxClientDC dc( m_gridWin );
2918 PrepareDC( dc );
2919 y = wxMax( y, GetRowTop(m_dragRowOrCol) + WXGRID_MIN_ROW_HEIGHT );
2920 dc.SetLogicalFunction(wxINVERT);
2921 if ( m_dragLastPos >= 0 )
2922 {
2923 dc.DrawLine( left, m_dragLastPos, left+cw, m_dragLastPos );
2924 }
2925 dc.DrawLine( left, y, left+cw, y );
2926 m_dragLastPos = y;
2927 }
2928 break;
2929
2930 case WXGRID_CURSOR_SELECT_ROW:
2931 if ( (row = YToRow( y )) >= 0 &&
2932 !IsInSelection( row, 0 ) )
2933 {
2934 SelectRow( row, TRUE );
2935 }
2936
2937 // default label to suppress warnings about "enumeration value
2938 // 'xxx' not handled in switch
2939 default:
2940 break;
2941 }
2942 }
2943 return;
2944 }
2945
2946 m_isDragging = FALSE;
2947
2948
2949 // ------------ Entering or leaving the window
2950 //
2951 if ( event.Entering() || event.Leaving() )
2952 {
2953 ChangeCursorMode(WXGRID_CURSOR_SELECT_CELL, m_rowLabelWin);
2954 }
2955
2956
2957 // ------------ Left button pressed
2958 //
2959 else if ( event.LeftDown() )
2960 {
2961 // don't send a label click event for a hit on the
2962 // edge of the row label - this is probably the user
2963 // wanting to resize the row
2964 //
2965 if ( YToEdgeOfRow(y) < 0 )
2966 {
2967 row = YToRow(y);
2968 if ( row >= 0 &&
2969 !SendEvent( wxEVT_GRID_LABEL_LEFT_CLICK, row, -1, event ) )
2970 {
2971 SelectRow( row, event.ShiftDown() );
2972 ChangeCursorMode(WXGRID_CURSOR_SELECT_ROW, m_rowLabelWin);
2973 }
2974 }
2975 else
2976 {
2977 // starting to drag-resize a row
2978 //
2979 if ( CanDragRowSize() )
2980 ChangeCursorMode(WXGRID_CURSOR_RESIZE_ROW, m_rowLabelWin);
2981 }
2982 }
2983
2984
2985 // ------------ Left double click
2986 //
2987 else if (event.LeftDClick() )
2988 {
2989 if ( YToEdgeOfRow(y) < 0 )
2990 {
2991 row = YToRow(y);
2992 SendEvent( wxEVT_GRID_LABEL_LEFT_DCLICK, row, -1, event );
2993 }
2994 }
2995
2996
2997 // ------------ Left button released
2998 //
2999 else if ( event.LeftUp() )
3000 {
3001 if ( m_cursorMode == WXGRID_CURSOR_RESIZE_ROW )
3002 {
3003 DoEndDragResizeRow();
3004
3005 // Note: we are ending the event *after* doing
3006 // default processing in this case
3007 //
3008 SendEvent( wxEVT_GRID_ROW_SIZE, m_dragRowOrCol, -1, event );
3009 }
3010
3011 ChangeCursorMode(WXGRID_CURSOR_SELECT_CELL, m_rowLabelWin);
3012 m_dragLastPos = -1;
3013 }
3014
3015
3016 // ------------ Right button down
3017 //
3018 else if ( event.RightDown() )
3019 {
3020 row = YToRow(y);
3021 if ( !SendEvent( wxEVT_GRID_LABEL_RIGHT_CLICK, row, -1, event ) )
3022 {
3023 // no default action at the moment
3024 }
3025 }
3026
3027
3028 // ------------ Right double click
3029 //
3030 else if ( event.RightDClick() )
3031 {
3032 row = YToRow(y);
3033 if ( !SendEvent( wxEVT_GRID_LABEL_RIGHT_DCLICK, row, -1, event ) )
3034 {
3035 // no default action at the moment
3036 }
3037 }
3038
3039
3040 // ------------ No buttons down and mouse moving
3041 //
3042 else if ( event.Moving() )
3043 {
3044 m_dragRowOrCol = YToEdgeOfRow( y );
3045 if ( m_dragRowOrCol >= 0 )
3046 {
3047 if ( m_cursorMode == WXGRID_CURSOR_SELECT_CELL )
3048 {
3049 // don't capture the mouse yet
3050 if ( CanDragRowSize() )
3051 ChangeCursorMode(WXGRID_CURSOR_RESIZE_ROW, m_rowLabelWin, FALSE);
3052 }
3053 }
3054 else if ( m_cursorMode != WXGRID_CURSOR_SELECT_CELL )
3055 {
3056 ChangeCursorMode(WXGRID_CURSOR_SELECT_CELL, m_rowLabelWin, FALSE);
3057 }
3058 }
3059 }
3060
3061
3062 void wxGrid::ProcessColLabelMouseEvent( wxMouseEvent& event )
3063 {
3064 int x, y, col;
3065 wxPoint pos( event.GetPosition() );
3066 CalcUnscrolledPosition( pos.x, pos.y, &x, &y );
3067
3068 if ( event.Dragging() )
3069 {
3070 m_isDragging = TRUE;
3071
3072 if ( event.LeftIsDown() )
3073 {
3074 switch( m_cursorMode )
3075 {
3076 case WXGRID_CURSOR_RESIZE_COL:
3077 {
3078 int cw, ch, dummy, top;
3079 m_gridWin->GetClientSize( &cw, &ch );
3080 CalcUnscrolledPosition( 0, 0, &dummy, &top );
3081
3082 wxClientDC dc( m_gridWin );
3083 PrepareDC( dc );
3084
3085 x = wxMax( x, GetColLeft(m_dragRowOrCol) +
3086 GetColMinimalWidth(m_dragRowOrCol));
3087 dc.SetLogicalFunction(wxINVERT);
3088 if ( m_dragLastPos >= 0 )
3089 {
3090 dc.DrawLine( m_dragLastPos, top, m_dragLastPos, top+ch );
3091 }
3092 dc.DrawLine( x, top, x, top+ch );
3093 m_dragLastPos = x;
3094 }
3095 break;
3096
3097 case WXGRID_CURSOR_SELECT_COL:
3098 if ( (col = XToCol( x )) >= 0 &&
3099 !IsInSelection( 0, col ) )
3100 {
3101 SelectCol( col, TRUE );
3102 }
3103
3104 // default label to suppress warnings about "enumeration value
3105 // 'xxx' not handled in switch
3106 default:
3107 break;
3108 }
3109 }
3110 return;
3111 }
3112
3113 m_isDragging = FALSE;
3114
3115
3116 // ------------ Entering or leaving the window
3117 //
3118 if ( event.Entering() || event.Leaving() )
3119 {
3120 ChangeCursorMode(WXGRID_CURSOR_SELECT_CELL, m_colLabelWin);
3121 }
3122
3123
3124 // ------------ Left button pressed
3125 //
3126 else if ( event.LeftDown() )
3127 {
3128 // don't send a label click event for a hit on the
3129 // edge of the col label - this is probably the user
3130 // wanting to resize the col
3131 //
3132 if ( XToEdgeOfCol(x) < 0 )
3133 {
3134 col = XToCol(x);
3135 if ( col >= 0 &&
3136 !SendEvent( wxEVT_GRID_LABEL_LEFT_CLICK, -1, col, event ) )
3137 {
3138 SelectCol( col, event.ShiftDown() );
3139 ChangeCursorMode(WXGRID_CURSOR_SELECT_COL, m_colLabelWin);
3140 }
3141 }
3142 else
3143 {
3144 // starting to drag-resize a col
3145 //
3146 if ( CanDragColSize() )
3147 ChangeCursorMode(WXGRID_CURSOR_RESIZE_COL, m_colLabelWin);
3148 }
3149 }
3150
3151
3152 // ------------ Left double click
3153 //
3154 if ( event.LeftDClick() )
3155 {
3156 if ( XToEdgeOfCol(x) < 0 )
3157 {
3158 col = XToCol(x);
3159 SendEvent( wxEVT_GRID_LABEL_LEFT_DCLICK, -1, col, event );
3160 }
3161 }
3162
3163
3164 // ------------ Left button released
3165 //
3166 else if ( event.LeftUp() )
3167 {
3168 if ( m_cursorMode == WXGRID_CURSOR_RESIZE_COL )
3169 {
3170 DoEndDragResizeCol();
3171
3172 // Note: we are ending the event *after* doing
3173 // default processing in this case
3174 //
3175 SendEvent( wxEVT_GRID_COL_SIZE, -1, m_dragRowOrCol, event );
3176 }
3177
3178 ChangeCursorMode(WXGRID_CURSOR_SELECT_CELL, m_colLabelWin);
3179 m_dragLastPos = -1;
3180 }
3181
3182
3183 // ------------ Right button down
3184 //
3185 else if ( event.RightDown() )
3186 {
3187 col = XToCol(x);
3188 if ( !SendEvent( wxEVT_GRID_LABEL_RIGHT_CLICK, -1, col, event ) )
3189 {
3190 // no default action at the moment
3191 }
3192 }
3193
3194
3195 // ------------ Right double click
3196 //
3197 else if ( event.RightDClick() )
3198 {
3199 col = XToCol(x);
3200 if ( !SendEvent( wxEVT_GRID_LABEL_RIGHT_DCLICK, -1, col, event ) )
3201 {
3202 // no default action at the moment
3203 }
3204 }
3205
3206
3207 // ------------ No buttons down and mouse moving
3208 //
3209 else if ( event.Moving() )
3210 {
3211 m_dragRowOrCol = XToEdgeOfCol( x );
3212 if ( m_dragRowOrCol >= 0 )
3213 {
3214 if ( m_cursorMode == WXGRID_CURSOR_SELECT_CELL )
3215 {
3216 // don't capture the cursor yet
3217 if ( CanDragColSize() )
3218 ChangeCursorMode(WXGRID_CURSOR_RESIZE_COL, m_colLabelWin, FALSE);
3219 }
3220 }
3221 else if ( m_cursorMode != WXGRID_CURSOR_SELECT_CELL )
3222 {
3223 ChangeCursorMode(WXGRID_CURSOR_SELECT_CELL, m_colLabelWin, FALSE);
3224 }
3225 }
3226 }
3227
3228
3229 void wxGrid::ProcessCornerLabelMouseEvent( wxMouseEvent& event )
3230 {
3231 if ( event.LeftDown() )
3232 {
3233 // indicate corner label by having both row and
3234 // col args == -1
3235 //
3236 if ( !SendEvent( wxEVT_GRID_LABEL_LEFT_CLICK, -1, -1, event ) )
3237 {
3238 SelectAll();
3239 }
3240 }
3241
3242 else if ( event.LeftDClick() )
3243 {
3244 SendEvent( wxEVT_GRID_LABEL_LEFT_DCLICK, -1, -1, event );
3245 }
3246
3247 else if ( event.RightDown() )
3248 {
3249 if ( !SendEvent( wxEVT_GRID_LABEL_RIGHT_CLICK, -1, -1, event ) )
3250 {
3251 // no default action at the moment
3252 }
3253 }
3254
3255 else if ( event.RightDClick() )
3256 {
3257 if ( !SendEvent( wxEVT_GRID_LABEL_RIGHT_DCLICK, -1, -1, event ) )
3258 {
3259 // no default action at the moment
3260 }
3261 }
3262 }
3263
3264 void wxGrid::ChangeCursorMode(CursorMode mode,
3265 wxWindow *win,
3266 bool captureMouse)
3267 {
3268 #ifdef __WXDEBUG__
3269 static const wxChar *cursorModes[] =
3270 {
3271 _T("SELECT_CELL"),
3272 _T("RESIZE_ROW"),
3273 _T("RESIZE_COL"),
3274 _T("SELECT_ROW"),
3275 _T("SELECT_COL")
3276 };
3277
3278 wxLogTrace(_T("grid"),
3279 _T("wxGrid cursor mode (mouse capture for %s): %s -> %s"),
3280 win == m_colLabelWin ? _T("colLabelWin")
3281 : win ? _T("rowLabelWin")
3282 : _T("gridWin"),
3283 cursorModes[m_cursorMode], cursorModes[mode]);
3284 #endif // __WXDEBUG__
3285
3286 if ( mode == m_cursorMode )
3287 return;
3288
3289 if ( !win )
3290 {
3291 // by default use the grid itself
3292 win = m_gridWin;
3293 }
3294
3295 if ( m_winCapture )
3296 {
3297 m_winCapture->ReleaseMouse();
3298 m_winCapture = (wxWindow *)NULL;
3299 }
3300
3301 m_cursorMode = mode;
3302
3303 switch ( m_cursorMode )
3304 {
3305 case WXGRID_CURSOR_RESIZE_ROW:
3306 win->SetCursor( m_rowResizeCursor );
3307 break;
3308
3309 case WXGRID_CURSOR_RESIZE_COL:
3310 win->SetCursor( m_colResizeCursor );
3311 break;
3312
3313 default:
3314 win->SetCursor( *wxSTANDARD_CURSOR );
3315 }
3316
3317 // we need to capture mouse when resizing
3318 bool resize = m_cursorMode == WXGRID_CURSOR_RESIZE_ROW ||
3319 m_cursorMode == WXGRID_CURSOR_RESIZE_COL;
3320
3321 if ( captureMouse && resize )
3322 {
3323 win->CaptureMouse();
3324 m_winCapture = win;
3325 }
3326 }
3327
3328 void wxGrid::ProcessGridCellMouseEvent( wxMouseEvent& event )
3329 {
3330 int x, y;
3331 wxPoint pos( event.GetPosition() );
3332 CalcUnscrolledPosition( pos.x, pos.y, &x, &y );
3333
3334 wxGridCellCoords coords;
3335 XYToCell( x, y, coords );
3336
3337 if ( event.Dragging() )
3338 {
3339 //wxLogDebug("pos(%d, %d) coords(%d, %d)", pos.x, pos.y, coords.GetRow(), coords.GetCol());
3340
3341 // Don't start doing anything until the mouse has been drug at
3342 // least 3 pixels in any direction...
3343 if (! m_isDragging)
3344 {
3345 if (m_startDragPos == wxDefaultPosition)
3346 {
3347 m_startDragPos = pos;
3348 return;
3349 }
3350 if (abs(m_startDragPos.x - pos.x) < 4 && abs(m_startDragPos.y - pos.y) < 4)
3351 return;
3352 }
3353
3354 m_isDragging = TRUE;
3355 if ( m_cursorMode == WXGRID_CURSOR_SELECT_CELL )
3356 {
3357 // Hide the edit control, so it
3358 // won't interfer with drag-shrinking.
3359 if ( IsCellEditControlEnabled() )
3360 HideCellEditControl();
3361
3362 // Have we captured the mouse yet?
3363 if (! m_winCapture)
3364 {
3365 m_winCapture = m_gridWin;
3366 m_winCapture->CaptureMouse();
3367 }
3368
3369 if ( coords != wxGridNoCellCoords )
3370 {
3371 if ( !IsSelection() )
3372 {
3373 SelectBlock( coords, coords );
3374 }
3375 else
3376 {
3377 SelectBlock( m_currentCellCoords, coords );
3378 }
3379
3380 if (! IsVisible(coords))
3381 {
3382 MakeCellVisible(coords);
3383 // TODO: need to introduce a delay or something here. The
3384 // scrolling is way to fast, at least on MSW.
3385 }
3386 }
3387 }
3388 else if ( m_cursorMode == WXGRID_CURSOR_RESIZE_ROW )
3389 {
3390 int cw, ch, left, dummy;
3391 m_gridWin->GetClientSize( &cw, &ch );
3392 CalcUnscrolledPosition( 0, 0, &left, &dummy );
3393
3394 wxClientDC dc( m_gridWin );
3395 PrepareDC( dc );
3396 y = wxMax( y, GetRowTop(m_dragRowOrCol) + WXGRID_MIN_ROW_HEIGHT );
3397 dc.SetLogicalFunction(wxINVERT);
3398 if ( m_dragLastPos >= 0 )
3399 {
3400 dc.DrawLine( left, m_dragLastPos, left+cw, m_dragLastPos );
3401 }
3402 dc.DrawLine( left, y, left+cw, y );
3403 m_dragLastPos = y;
3404 }
3405 else if ( m_cursorMode == WXGRID_CURSOR_RESIZE_COL )
3406 {
3407 int cw, ch, dummy, top;
3408 m_gridWin->GetClientSize( &cw, &ch );
3409 CalcUnscrolledPosition( 0, 0, &dummy, &top );
3410
3411 wxClientDC dc( m_gridWin );
3412 PrepareDC( dc );
3413 x = wxMax( x, GetColLeft(m_dragRowOrCol) +
3414 GetColMinimalWidth(m_dragRowOrCol) );
3415 dc.SetLogicalFunction(wxINVERT);
3416 if ( m_dragLastPos >= 0 )
3417 {
3418 dc.DrawLine( m_dragLastPos, top, m_dragLastPos, top+ch );
3419 }
3420 dc.DrawLine( x, top, x, top+ch );
3421 m_dragLastPos = x;
3422 }
3423
3424 return;
3425 }
3426
3427 m_isDragging = FALSE;
3428 m_startDragPos = wxDefaultPosition;
3429
3430
3431 if ( coords != wxGridNoCellCoords )
3432 {
3433 // VZ: if we do this, the mode is reset to WXGRID_CURSOR_SELECT_CELL
3434 // immediately after it becomes WXGRID_CURSOR_RESIZE_ROW/COL under
3435 // wxGTK
3436 #if 0
3437 if ( event.Entering() || event.Leaving() )
3438 {
3439 ChangeCursorMode(WXGRID_CURSOR_SELECT_CELL);
3440 m_gridWin->SetCursor( *wxSTANDARD_CURSOR );
3441 }
3442 else
3443 #endif // 0
3444
3445 // ------------ Left button pressed
3446 //
3447 if ( event.LeftDown() )
3448 {
3449 DisableCellEditControl();
3450 if ( event.ShiftDown() )
3451 {
3452 SelectBlock( m_currentCellCoords, coords );
3453 }
3454 else if ( XToEdgeOfCol(x) < 0 &&
3455 YToEdgeOfRow(y) < 0 )
3456 {
3457 if ( !SendEvent( wxEVT_GRID_CELL_LEFT_CLICK,
3458 coords.GetRow(),
3459 coords.GetCol(),
3460 event ) )
3461 {
3462 MakeCellVisible( coords );
3463
3464 // if this is the second click on this cell then start
3465 // the edit control
3466 if ( m_waitForSlowClick &&
3467 (coords == m_currentCellCoords) &&
3468 CanEnableCellControl())
3469 {
3470 EnableCellEditControl();
3471
3472 wxGridCellAttr* attr = GetCellAttr(m_currentCellCoords);
3473 attr->GetEditor(GetDefaultEditorForCell(coords.GetRow(), coords.GetCol()))->StartingClick();
3474 attr->DecRef();
3475
3476 m_waitForSlowClick = FALSE;
3477 }
3478 else
3479 {
3480 SetCurrentCell( coords );
3481 m_waitForSlowClick = TRUE;
3482 }
3483 }
3484 }
3485 }
3486
3487
3488 // ------------ Left double click
3489 //
3490 else if ( event.LeftDClick() )
3491 {
3492 DisableCellEditControl();
3493 if ( XToEdgeOfCol(x) < 0 && YToEdgeOfRow(y) < 0 )
3494 {
3495 SendEvent( wxEVT_GRID_CELL_LEFT_DCLICK,
3496 coords.GetRow(),
3497 coords.GetCol(),
3498 event );
3499 }
3500 }
3501
3502
3503 // ------------ Left button released
3504 //
3505 else if ( event.LeftUp() )
3506 {
3507 if ( m_cursorMode == WXGRID_CURSOR_SELECT_CELL )
3508 {
3509 if ( IsSelection() )
3510 {
3511 if (m_winCapture)
3512 {
3513 m_winCapture->ReleaseMouse();
3514 m_winCapture = NULL;
3515 }
3516 SendEvent( wxEVT_GRID_RANGE_SELECT, -1, -1, event );
3517 }
3518
3519 // Show the edit control, if it has been hidden for
3520 // drag-shrinking.
3521 ShowCellEditControl();
3522 }
3523 else if ( m_cursorMode == WXGRID_CURSOR_RESIZE_ROW )
3524 {
3525 ChangeCursorMode(WXGRID_CURSOR_SELECT_CELL);
3526 DoEndDragResizeRow();
3527
3528 // Note: we are ending the event *after* doing
3529 // default processing in this case
3530 //
3531 SendEvent( wxEVT_GRID_ROW_SIZE, m_dragRowOrCol, -1, event );
3532 }
3533 else if ( m_cursorMode == WXGRID_CURSOR_RESIZE_COL )
3534 {
3535 ChangeCursorMode(WXGRID_CURSOR_SELECT_CELL);
3536 DoEndDragResizeCol();
3537
3538 // Note: we are ending the event *after* doing
3539 // default processing in this case
3540 //
3541 SendEvent( wxEVT_GRID_COL_SIZE, -1, m_dragRowOrCol, event );
3542 }
3543
3544 m_dragLastPos = -1;
3545 }
3546
3547
3548 // ------------ Right button down
3549 //
3550 else if ( event.RightDown() )
3551 {
3552 DisableCellEditControl();
3553 if ( !SendEvent( wxEVT_GRID_CELL_RIGHT_CLICK,
3554 coords.GetRow(),
3555 coords.GetCol(),
3556 event ) )
3557 {
3558 // no default action at the moment
3559 }
3560 }
3561
3562
3563 // ------------ Right double click
3564 //
3565 else if ( event.RightDClick() )
3566 {
3567 DisableCellEditControl();
3568 if ( !SendEvent( wxEVT_GRID_CELL_RIGHT_DCLICK,
3569 coords.GetRow(),
3570 coords.GetCol(),
3571 event ) )
3572 {
3573 // no default action at the moment
3574 }
3575 }
3576
3577 // ------------ Moving and no button action
3578 //
3579 else if ( event.Moving() && !event.IsButton() )
3580 {
3581 int dragRow = YToEdgeOfRow( y );
3582 int dragCol = XToEdgeOfCol( x );
3583
3584 // Dragging on the corner of a cell to resize in both
3585 // directions is not implemented yet...
3586 //
3587 if ( dragRow >= 0 && dragCol >= 0 )
3588 {
3589 ChangeCursorMode(WXGRID_CURSOR_SELECT_CELL);
3590 return;
3591 }
3592
3593 if ( dragRow >= 0 )
3594 {
3595 m_dragRowOrCol = dragRow;
3596
3597 if ( m_cursorMode == WXGRID_CURSOR_SELECT_CELL )
3598 {
3599 if ( CanDragRowSize() )
3600 ChangeCursorMode(WXGRID_CURSOR_RESIZE_ROW);
3601 }
3602
3603 return;
3604 }
3605
3606 if ( dragCol >= 0 )
3607 {
3608 m_dragRowOrCol = dragCol;
3609
3610 if ( m_cursorMode == WXGRID_CURSOR_SELECT_CELL )
3611 {
3612 if ( CanDragColSize() )
3613 ChangeCursorMode(WXGRID_CURSOR_RESIZE_COL);
3614 }
3615
3616 return;
3617 }
3618
3619 // Neither on a row or col edge
3620 //
3621 if ( m_cursorMode != WXGRID_CURSOR_SELECT_CELL )
3622 {
3623 ChangeCursorMode(WXGRID_CURSOR_SELECT_CELL);
3624 }
3625 }
3626 }
3627 }
3628
3629
3630 void wxGrid::DoEndDragResizeRow()
3631 {
3632 if ( m_dragLastPos >= 0 )
3633 {
3634 // erase the last line and resize the row
3635 //
3636 int cw, ch, left, dummy;
3637 m_gridWin->GetClientSize( &cw, &ch );
3638 CalcUnscrolledPosition( 0, 0, &left, &dummy );
3639
3640 wxClientDC dc( m_gridWin );
3641 PrepareDC( dc );
3642 dc.SetLogicalFunction( wxINVERT );
3643 dc.DrawLine( left, m_dragLastPos, left+cw, m_dragLastPos );
3644 HideCellEditControl();
3645
3646 int rowTop = GetRowTop(m_dragRowOrCol);
3647 SetRowSize( m_dragRowOrCol,
3648 wxMax( m_dragLastPos - rowTop, WXGRID_MIN_ROW_HEIGHT ) );
3649
3650 if ( !GetBatchCount() )
3651 {
3652 // Only needed to get the correct rect.y:
3653 wxRect rect ( CellToRect( m_dragRowOrCol, 0 ) );
3654 rect.x = 0;
3655 CalcScrolledPosition(0, rect.y, &dummy, &rect.y);
3656 rect.width = m_rowLabelWidth;
3657 rect.height = ch - rect.y;
3658 m_rowLabelWin->Refresh( TRUE, &rect );
3659 rect.width = cw;
3660 m_gridWin->Refresh( FALSE, &rect );
3661 }
3662
3663 ShowCellEditControl();
3664 }
3665 }
3666
3667
3668 void wxGrid::DoEndDragResizeCol()
3669 {
3670 if ( m_dragLastPos >= 0 )
3671 {
3672 // erase the last line and resize the col
3673 //
3674 int cw, ch, dummy, top;
3675 m_gridWin->GetClientSize( &cw, &ch );
3676 CalcUnscrolledPosition( 0, 0, &dummy, &top );
3677
3678 wxClientDC dc( m_gridWin );
3679 PrepareDC( dc );
3680 dc.SetLogicalFunction( wxINVERT );
3681 dc.DrawLine( m_dragLastPos, top, m_dragLastPos, top+ch );
3682 HideCellEditControl();
3683
3684 int colLeft = GetColLeft(m_dragRowOrCol);
3685 SetColSize( m_dragRowOrCol,
3686 wxMax( m_dragLastPos - colLeft,
3687 GetColMinimalWidth(m_dragRowOrCol) ) );
3688
3689 if ( !GetBatchCount() )
3690 {
3691 // Only needed to get the correct rect.x:
3692 wxRect rect ( CellToRect( 0, m_dragRowOrCol ) );
3693 rect.y = 0;
3694 CalcScrolledPosition(rect.x, 0, &rect.x, &dummy);
3695 rect.width = cw - rect.x;
3696 rect.height = m_colLabelHeight;
3697 m_colLabelWin->Refresh( TRUE, &rect );
3698 rect.height = ch;
3699 m_gridWin->Refresh( FALSE, &rect );
3700 }
3701
3702 ShowCellEditControl();
3703 }
3704 }
3705
3706
3707
3708 //
3709 // ------ interaction with data model
3710 //
3711 bool wxGrid::ProcessTableMessage( wxGridTableMessage& msg )
3712 {
3713 switch ( msg.GetId() )
3714 {
3715 case wxGRIDTABLE_REQUEST_VIEW_GET_VALUES:
3716 return GetModelValues();
3717
3718 case wxGRIDTABLE_REQUEST_VIEW_SEND_VALUES:
3719 return SetModelValues();
3720
3721 case wxGRIDTABLE_NOTIFY_ROWS_INSERTED:
3722 case wxGRIDTABLE_NOTIFY_ROWS_APPENDED:
3723 case wxGRIDTABLE_NOTIFY_ROWS_DELETED:
3724 case wxGRIDTABLE_NOTIFY_COLS_INSERTED:
3725 case wxGRIDTABLE_NOTIFY_COLS_APPENDED:
3726 case wxGRIDTABLE_NOTIFY_COLS_DELETED:
3727 return Redimension( msg );
3728
3729 default:
3730 return FALSE;
3731 }
3732 }
3733
3734
3735
3736 // The behaviour of this function depends on the grid table class
3737 // Clear() function. For the default wxGridStringTable class the
3738 // behavious is to replace all cell contents with wxEmptyString but
3739 // not to change the number of rows or cols.
3740 //
3741 void wxGrid::ClearGrid()
3742 {
3743 if ( m_table )
3744 {
3745 m_table->Clear();
3746 SetEditControlValue();
3747 if ( !GetBatchCount() ) m_gridWin->Refresh();
3748 }
3749 }
3750
3751
3752 bool wxGrid::InsertRows( int pos, int numRows, bool WXUNUSED(updateLabels) )
3753 {
3754 // TODO: something with updateLabels flag
3755
3756 if ( !m_created )
3757 {
3758 wxFAIL_MSG( wxT("Called wxGrid::InsertRows() before calling CreateGrid()") );
3759 return FALSE;
3760 }
3761
3762 if ( m_table )
3763 {
3764 if (IsCellEditControlEnabled())
3765 DisableCellEditControl();
3766
3767 bool ok = m_table->InsertRows( pos, numRows );
3768
3769 // the table will have sent the results of the insert row
3770 // operation to this view object as a grid table message
3771 //
3772 if ( ok )
3773 {
3774 if ( m_numCols == 0 )
3775 {
3776 m_table->AppendCols( WXGRID_DEFAULT_NUMBER_COLS );
3777 //
3778 // TODO: perhaps instead of appending the default number of cols
3779 // we should remember what the last non-zero number of cols was ?
3780 //
3781 }
3782
3783 if ( m_currentCellCoords == wxGridNoCellCoords )
3784 {
3785 // if we have just inserted cols into an empty grid the current
3786 // cell will be undefined...
3787 //
3788 SetCurrentCell( 0, 0 );
3789 }
3790
3791 ClearSelection();
3792 if ( !GetBatchCount() ) Refresh();
3793 }
3794
3795 SetEditControlValue();
3796 return ok;
3797 }
3798 else
3799 {
3800 return FALSE;
3801 }
3802 }
3803
3804
3805 bool wxGrid::AppendRows( int numRows, bool WXUNUSED(updateLabels) )
3806 {
3807 // TODO: something with updateLabels flag
3808
3809 if ( !m_created )
3810 {
3811 wxFAIL_MSG( wxT("Called wxGrid::AppendRows() before calling CreateGrid()") );
3812 return FALSE;
3813 }
3814
3815 if ( m_table && m_table->AppendRows( numRows ) )
3816 {
3817 if ( m_currentCellCoords == wxGridNoCellCoords )
3818 {
3819 // if we have just inserted cols into an empty grid the current
3820 // cell will be undefined...
3821 //
3822 SetCurrentCell( 0, 0 );
3823 }
3824
3825 // the table will have sent the results of the append row
3826 // operation to this view object as a grid table message
3827 //
3828 ClearSelection();
3829 if ( !GetBatchCount() ) Refresh();
3830 return TRUE;
3831 }
3832 else
3833 {
3834 return FALSE;
3835 }
3836 }
3837
3838
3839 bool wxGrid::DeleteRows( int pos, int numRows, bool WXUNUSED(updateLabels) )
3840 {
3841 // TODO: something with updateLabels flag
3842
3843 if ( !m_created )
3844 {
3845 wxFAIL_MSG( wxT("Called wxGrid::DeleteRows() before calling CreateGrid()") );
3846 return FALSE;
3847 }
3848
3849 if ( m_table )
3850 {
3851 if (IsCellEditControlEnabled())
3852 DisableCellEditControl();
3853
3854 if (m_table->DeleteRows( pos, numRows ))
3855 {
3856
3857 // the table will have sent the results of the delete row
3858 // operation to this view object as a grid table message
3859 //
3860 ClearSelection();
3861 if ( !GetBatchCount() ) Refresh();
3862 return TRUE;
3863 }
3864 }
3865 return FALSE;
3866 }
3867
3868
3869 bool wxGrid::InsertCols( int pos, int numCols, bool WXUNUSED(updateLabels) )
3870 {
3871 // TODO: something with updateLabels flag
3872
3873 if ( !m_created )
3874 {
3875 wxFAIL_MSG( wxT("Called wxGrid::InsertCols() before calling CreateGrid()") );
3876 return FALSE;
3877 }
3878
3879 if ( m_table )
3880 {
3881 if (IsCellEditControlEnabled())
3882 DisableCellEditControl();
3883
3884 bool ok = m_table->InsertCols( pos, numCols );
3885
3886 // the table will have sent the results of the insert col
3887 // operation to this view object as a grid table message
3888 //
3889 if ( ok )
3890 {
3891 if ( m_currentCellCoords == wxGridNoCellCoords )
3892 {
3893 // if we have just inserted cols into an empty grid the current
3894 // cell will be undefined...
3895 //
3896 SetCurrentCell( 0, 0 );
3897 }
3898
3899 ClearSelection();
3900 if ( !GetBatchCount() ) Refresh();
3901 }
3902
3903 SetEditControlValue();
3904 return ok;
3905 }
3906 else
3907 {
3908 return FALSE;
3909 }
3910 }
3911
3912
3913 bool wxGrid::AppendCols( int numCols, bool WXUNUSED(updateLabels) )
3914 {
3915 // TODO: something with updateLabels flag
3916
3917 if ( !m_created )
3918 {
3919 wxFAIL_MSG( wxT("Called wxGrid::AppendCols() before calling CreateGrid()") );
3920 return FALSE;
3921 }
3922
3923 if ( m_table && m_table->AppendCols( numCols ) )
3924 {
3925 // the table will have sent the results of the append col
3926 // operation to this view object as a grid table message
3927 //
3928 if ( m_currentCellCoords == wxGridNoCellCoords )
3929 {
3930 // if we have just inserted cols into an empty grid the current
3931 // cell will be undefined...
3932 //
3933 SetCurrentCell( 0, 0 );
3934 }
3935
3936 ClearSelection();
3937 if ( !GetBatchCount() ) Refresh();
3938 return TRUE;
3939 }
3940 else
3941 {
3942 return FALSE;
3943 }
3944 }
3945
3946
3947 bool wxGrid::DeleteCols( int pos, int numCols, bool WXUNUSED(updateLabels) )
3948 {
3949 // TODO: something with updateLabels flag
3950
3951 if ( !m_created )
3952 {
3953 wxFAIL_MSG( wxT("Called wxGrid::DeleteCols() before calling CreateGrid()") );
3954 return FALSE;
3955 }
3956
3957 if ( m_table )
3958 {
3959 if (IsCellEditControlEnabled())
3960 DisableCellEditControl();
3961
3962 if ( m_table->DeleteCols( pos, numCols ) )
3963 {
3964 // the table will have sent the results of the delete col
3965 // operation to this view object as a grid table message
3966 //
3967 ClearSelection();
3968 if ( !GetBatchCount() ) Refresh();
3969 return TRUE;
3970 }
3971 }
3972 return FALSE;
3973 }
3974
3975
3976
3977 //
3978 // ----- event handlers
3979 //
3980
3981 // Generate a grid event based on a mouse event and
3982 // return the result of ProcessEvent()
3983 //
3984 bool wxGrid::SendEvent( const wxEventType type,
3985 int row, int col,
3986 wxMouseEvent& mouseEv )
3987 {
3988 if ( type == wxEVT_GRID_ROW_SIZE || type == wxEVT_GRID_COL_SIZE )
3989 {
3990 int rowOrCol = (row == -1 ? col : row);
3991
3992 wxGridSizeEvent gridEvt( GetId(),
3993 type,
3994 this,
3995 rowOrCol,
3996 mouseEv.GetX(), mouseEv.GetY(),
3997 mouseEv.ControlDown(),
3998 mouseEv.ShiftDown(),
3999 mouseEv.AltDown(),
4000 mouseEv.MetaDown() );
4001
4002 return GetEventHandler()->ProcessEvent(gridEvt);
4003 }
4004 else if ( type == wxEVT_GRID_RANGE_SELECT )
4005 {
4006 wxGridRangeSelectEvent gridEvt( GetId(),
4007 type,
4008 this,
4009 m_selectedTopLeft,
4010 m_selectedBottomRight,
4011 mouseEv.ControlDown(),
4012 mouseEv.ShiftDown(),
4013 mouseEv.AltDown(),
4014 mouseEv.MetaDown() );
4015
4016 return GetEventHandler()->ProcessEvent(gridEvt);
4017 }
4018 else
4019 {
4020 wxGridEvent gridEvt( GetId(),
4021 type,
4022 this,
4023 row, col,
4024 mouseEv.GetX(), mouseEv.GetY(),
4025 mouseEv.ControlDown(),
4026 mouseEv.ShiftDown(),
4027 mouseEv.AltDown(),
4028 mouseEv.MetaDown() );
4029
4030 return GetEventHandler()->ProcessEvent(gridEvt);
4031 }
4032 }
4033
4034
4035 // Generate a grid event of specified type and return the result
4036 // of ProcessEvent().
4037 //
4038 bool wxGrid::SendEvent( const wxEventType type,
4039 int row, int col )
4040 {
4041 if ( type == wxEVT_GRID_ROW_SIZE || type == wxEVT_GRID_COL_SIZE )
4042 {
4043 int rowOrCol = (row == -1 ? col : row);
4044
4045 wxGridSizeEvent gridEvt( GetId(),
4046 type,
4047 this,
4048 rowOrCol );
4049
4050 return GetEventHandler()->ProcessEvent(gridEvt);
4051 }
4052 else
4053 {
4054 wxGridEvent gridEvt( GetId(),
4055 type,
4056 this,
4057 row, col );
4058
4059 return GetEventHandler()->ProcessEvent(gridEvt);
4060 }
4061 }
4062
4063
4064 void wxGrid::OnPaint( wxPaintEvent& WXUNUSED(event) )
4065 {
4066 wxPaintDC dc( this );
4067
4068 if ( m_currentCellCoords == wxGridNoCellCoords &&
4069 m_numRows && m_numCols )
4070 {
4071 m_currentCellCoords.Set(0, 0);
4072 SetEditControlValue();
4073 ShowCellEditControl();
4074 }
4075
4076 m_displayed = TRUE;
4077 }
4078
4079
4080 // This is just here to make sure that CalcDimensions gets called when
4081 // the grid view is resized... then the size event is skipped to allow
4082 // the box sizers to handle everything
4083 //
4084 void wxGrid::OnSize( wxSizeEvent& event )
4085 {
4086 CalcWindowSizes();
4087 CalcDimensions();
4088 }
4089
4090
4091 void wxGrid::OnKeyDown( wxKeyEvent& event )
4092 {
4093 if ( m_inOnKeyDown )
4094 {
4095 // shouldn't be here - we are going round in circles...
4096 //
4097 wxFAIL_MSG( wxT("wxGrid::OnKeyDown called while already active") );
4098 }
4099
4100 m_inOnKeyDown = TRUE;
4101
4102 // propagate the event up and see if it gets processed
4103 //
4104 wxWindow *parent = GetParent();
4105 wxKeyEvent keyEvt( event );
4106 keyEvt.SetEventObject( parent );
4107
4108 if ( !parent->GetEventHandler()->ProcessEvent( keyEvt ) )
4109 {
4110
4111 // TODO: Should also support Shift-cursor keys for
4112 // extending the selection. Maybe add a flag to
4113 // MoveCursorXXX() and MoveCursorXXXBlock() and
4114 // just send event.ShiftDown().
4115
4116 // try local handlers
4117 //
4118 switch ( event.KeyCode() )
4119 {
4120 case WXK_UP:
4121 if ( event.ControlDown() )
4122 {
4123 MoveCursorUpBlock();
4124 }
4125 else
4126 {
4127 MoveCursorUp();
4128 }
4129 break;
4130
4131 case WXK_DOWN:
4132 if ( event.ControlDown() )
4133 {
4134 MoveCursorDownBlock();
4135 }
4136 else
4137 {
4138 MoveCursorDown();
4139 }
4140 break;
4141
4142 case WXK_LEFT:
4143 if ( event.ControlDown() )
4144 {
4145 MoveCursorLeftBlock();
4146 }
4147 else
4148 {
4149 MoveCursorLeft();
4150 }
4151 break;
4152
4153 case WXK_RIGHT:
4154 if ( event.ControlDown() )
4155 {
4156 MoveCursorRightBlock();
4157 }
4158 else
4159 {
4160 MoveCursorRight();
4161 }
4162 break;
4163
4164 case WXK_RETURN:
4165 if ( event.ControlDown() )
4166 {
4167 event.Skip(); // to let the edit control have the return
4168 }
4169 else
4170 {
4171 MoveCursorDown();
4172 }
4173 break;
4174
4175 case WXK_TAB:
4176 if (event.ShiftDown())
4177 MoveCursorLeft();
4178 else
4179 MoveCursorRight();
4180 break;
4181
4182 case WXK_HOME:
4183 if ( event.ControlDown() )
4184 {
4185 MakeCellVisible( 0, 0 );
4186 SetCurrentCell( 0, 0 );
4187 }
4188 else
4189 {
4190 event.Skip();
4191 }
4192 break;
4193
4194 case WXK_END:
4195 if ( event.ControlDown() )
4196 {
4197 MakeCellVisible( m_numRows-1, m_numCols-1 );
4198 SetCurrentCell( m_numRows-1, m_numCols-1 );
4199 }
4200 else
4201 {
4202 event.Skip();
4203 }
4204 break;
4205
4206 case WXK_PRIOR:
4207 MovePageUp();
4208 break;
4209
4210 case WXK_NEXT:
4211 MovePageDown();
4212 break;
4213
4214 // We don't want these keys to trigger the edit control, any others?
4215 case WXK_SHIFT:
4216 case WXK_ALT:
4217 case WXK_CONTROL:
4218 case WXK_CAPITAL:
4219 event.Skip();
4220 break;
4221
4222 case WXK_SPACE:
4223 if ( !IsEditable() )
4224 {
4225 MoveCursorRight();
4226 break;
4227 }
4228 // Otherwise fall through to default
4229
4230 default:
4231 // now try the cell edit control
4232 //
4233 if ( !IsCellEditControlEnabled() && CanEnableCellControl() )
4234 {
4235 EnableCellEditControl();
4236 int row = m_currentCellCoords.GetRow();
4237 int col = m_currentCellCoords.GetCol();
4238 wxGridCellAttr* attr = GetCellAttr(row, col);
4239 attr->GetEditor(GetDefaultEditorForCell(row, col))->StartingKey(event);
4240 attr->DecRef();
4241 }
4242 else
4243 {
4244 // let others process char events for readonly cells
4245 event.Skip();
4246 }
4247 break;
4248 }
4249 }
4250
4251 m_inOnKeyDown = FALSE;
4252 }
4253
4254
4255 void wxGrid::OnEraseBackground(wxEraseEvent&)
4256 {
4257 }
4258
4259 void wxGrid::SetCurrentCell( const wxGridCellCoords& coords )
4260 {
4261 if ( SendEvent( wxEVT_GRID_SELECT_CELL, coords.GetRow(), coords.GetCol() ) )
4262 {
4263 // the event has been intercepted - do nothing
4264 return;
4265 }
4266
4267 if ( m_displayed &&
4268 m_currentCellCoords != wxGridNoCellCoords )
4269 {
4270 HideCellEditControl();
4271 SaveEditControlValue();
4272 DisableCellEditControl();
4273
4274 // Clear the old current cell highlight
4275 wxRect r = BlockToDeviceRect(m_currentCellCoords, m_currentCellCoords);
4276
4277 // Otherwise refresh redraws the highlight!
4278 m_currentCellCoords = coords;
4279
4280 m_gridWin->Refresh( FALSE, &r );
4281 }
4282
4283 m_currentCellCoords = coords;
4284
4285 SetEditControlValue();
4286
4287 if ( m_displayed )
4288 {
4289 wxClientDC dc(m_gridWin);
4290 PrepareDC(dc);
4291
4292 wxGridCellAttr* attr = GetCellAttr(coords);
4293 DrawCellHighlight(dc, attr);
4294 attr->DecRef();
4295
4296 if ( IsSelection() )
4297 {
4298 wxRect r( SelectionToDeviceRect() );
4299 ClearSelection();
4300 if ( !GetBatchCount() ) m_gridWin->Refresh( FALSE, &r );
4301 }
4302 }
4303 }
4304
4305
4306 //
4307 // ------ functions to get/send data (see also public functions)
4308 //
4309
4310 bool wxGrid::GetModelValues()
4311 {
4312 if ( m_table )
4313 {
4314 // all we need to do is repaint the grid
4315 //
4316 m_gridWin->Refresh();
4317 return TRUE;
4318 }
4319
4320 return FALSE;
4321 }
4322
4323
4324 bool wxGrid::SetModelValues()
4325 {
4326 int row, col;
4327
4328 if ( m_table )
4329 {
4330 for ( row = 0; row < m_numRows; row++ )
4331 {
4332 for ( col = 0; col < m_numCols; col++ )
4333 {
4334 m_table->SetValue( row, col, GetCellValue(row, col) );
4335 }
4336 }
4337
4338 return TRUE;
4339 }
4340
4341 return FALSE;
4342 }
4343
4344
4345
4346 // Note - this function only draws cells that are in the list of
4347 // exposed cells (usually set from the update region by
4348 // CalcExposedCells)
4349 //
4350 void wxGrid::DrawGridCellArea( wxDC& dc )
4351 {
4352 if ( !m_numRows || !m_numCols ) return;
4353
4354 size_t i;
4355 size_t numCells = m_cellsExposed.GetCount();
4356
4357 for ( i = 0; i < numCells; i++ )
4358 {
4359 DrawCell( dc, m_cellsExposed[i] );
4360 }
4361 }
4362
4363
4364 void wxGrid::DrawCell( wxDC& dc, const wxGridCellCoords& coords )
4365 {
4366 int row = coords.GetRow();
4367 int col = coords.GetCol();
4368
4369 if ( GetColWidth(col) <= 0 || GetRowHeight(row) <= 0 )
4370 return;
4371
4372 // we draw the cell border ourselves
4373 #if !WXGRID_DRAW_LINES
4374 if ( m_gridLinesEnabled )
4375 DrawCellBorder( dc, coords );
4376 #endif
4377
4378 wxGridCellAttr* attr = GetCellAttr(row, col);
4379
4380 bool isCurrent = coords == m_currentCellCoords;
4381
4382 wxRect rect;
4383 rect.x = GetColLeft(col);
4384 rect.y = GetRowTop(row);
4385 rect.width = GetColWidth(col) - 1;
4386 rect.height = GetRowHeight(row) - 1;
4387
4388 // if the editor is shown, we should use it and not the renderer
4389 if ( isCurrent && IsCellEditControlEnabled() )
4390 {
4391 attr->GetEditor(GetDefaultEditorForCell(row, col))->
4392 PaintBackground(rect, attr);
4393 }
4394 else
4395 {
4396 // but all the rest is drawn by the cell renderer and hence may be
4397 // customized
4398 attr->GetRenderer(GetDefaultRendererForCell(row,col))->
4399 Draw(*this, *attr, dc, rect, row, col, IsInSelection(coords));
4400
4401 }
4402
4403 attr->DecRef();
4404 }
4405
4406 void wxGrid::DrawCellHighlight( wxDC& dc, const wxGridCellAttr *attr )
4407 {
4408 int row = m_currentCellCoords.GetRow();
4409 int col = m_currentCellCoords.GetCol();
4410
4411 if ( GetColWidth(col) <= 0 || GetRowHeight(row) <= 0 )
4412 return;
4413
4414 wxRect rect;
4415 rect.x = GetColLeft(col);
4416 rect.y = GetRowTop(row);
4417 rect.width = GetColWidth(col) - 1;
4418 rect.height = GetRowHeight(row) - 1;
4419
4420 // hmmm... what could we do here to show that the cell is disabled?
4421 // for now, I just draw a thinner border than for the other ones, but
4422 // it doesn't look really good
4423 dc.SetPen(wxPen(m_gridLineColour, attr->IsReadOnly() ? 1 : 3, wxSOLID));
4424 dc.SetBrush(*wxTRANSPARENT_BRUSH);
4425
4426 dc.DrawRectangle(rect);
4427
4428 #if 0
4429 // VZ: my experiments with 3d borders...
4430
4431 // how to properly set colours for arbitrary bg?
4432 wxCoord x1 = rect.x,
4433 y1 = rect.y,
4434 x2 = rect.x + rect.width -1,
4435 y2 = rect.y + rect.height -1;
4436
4437 dc.SetPen(*wxWHITE_PEN);
4438 dc.DrawLine(x1, y1, x2, y1);
4439 dc.DrawLine(x1, y1, x1, y2);
4440
4441 dc.DrawLine(x1 + 1, y2 - 1, x2 - 1, y2 - 1);
4442 dc.DrawLine(x2 - 1, y1 + 1, x2 - 1, y2 );
4443
4444 dc.SetPen(*wxBLACK_PEN);
4445 dc.DrawLine(x1, y2, x2, y2);
4446 dc.DrawLine(x2, y1, x2, y2+1);
4447 #endif // 0
4448 }
4449
4450
4451 void wxGrid::DrawCellBorder( wxDC& dc, const wxGridCellCoords& coords )
4452 {
4453 int row = coords.GetRow();
4454 int col = coords.GetCol();
4455 if ( GetColWidth(col) <= 0 || GetRowHeight(row) <= 0 )
4456 return;
4457
4458 dc.SetPen( wxPen(GetGridLineColour(), 1, wxSOLID) );
4459
4460 // right hand border
4461 //
4462 dc.DrawLine( GetColRight(col), GetRowTop(row),
4463 GetColRight(col), GetRowBottom(row) );
4464
4465 // bottom border
4466 //
4467 dc.DrawLine( GetColLeft(col), GetRowBottom(row),
4468 GetColRight(col), GetRowBottom(row) );
4469 }
4470
4471 void wxGrid::DrawHighlight(wxDC& dc)
4472 {
4473 // if the active cell was repainted, repaint its highlight too because it
4474 // might have been damaged by the grid lines
4475 size_t count = m_cellsExposed.GetCount();
4476 for ( size_t n = 0; n < count; n++ )
4477 {
4478 if ( m_cellsExposed[n] == m_currentCellCoords )
4479 {
4480 wxGridCellAttr* attr = GetCellAttr(m_currentCellCoords);
4481 DrawCellHighlight(dc, attr);
4482 attr->DecRef();
4483
4484 break;
4485 }
4486 }
4487 }
4488
4489 // TODO: remove this ???
4490 // This is used to redraw all grid lines e.g. when the grid line colour
4491 // has been changed
4492 //
4493 void wxGrid::DrawAllGridLines( wxDC& dc, const wxRegion & reg )
4494 {
4495 if ( !m_gridLinesEnabled ||
4496 !m_numRows ||
4497 !m_numCols ) return;
4498
4499 int top, bottom, left, right;
4500
4501 if (reg.IsEmpty())
4502 {
4503 int cw, ch;
4504 m_gridWin->GetClientSize(&cw, &ch);
4505
4506 // virtual coords of visible area
4507 //
4508 CalcUnscrolledPosition( 0, 0, &left, &top );
4509 CalcUnscrolledPosition( cw, ch, &right, &bottom );
4510 }
4511 else
4512 {
4513 wxCoord x, y, w, h;
4514 reg.GetBox(x, y, w, h);
4515 CalcUnscrolledPosition( x, y, &left, &top );
4516 CalcUnscrolledPosition( x + w, y + h, &right, &bottom );
4517 }
4518
4519 // avoid drawing grid lines past the last row and col
4520 //
4521 right = wxMin( right, GetColRight(m_numCols - 1) );
4522 bottom = wxMin( bottom, GetRowBottom(m_numRows - 1) );
4523
4524 dc.SetPen( wxPen(GetGridLineColour(), 1, wxSOLID) );
4525
4526 // horizontal grid lines
4527 //
4528 int i;
4529 for ( i = 0; i < m_numRows; i++ )
4530 {
4531 int bot = GetRowBottom(i) - 1;
4532
4533 if ( bot > bottom )
4534 {
4535 break;
4536 }
4537
4538 if ( bot >= top )
4539 {
4540 dc.DrawLine( left, bot, right, bot );
4541 }
4542 }
4543
4544
4545 // vertical grid lines
4546 //
4547 for ( i = 0; i < m_numCols; i++ )
4548 {
4549 int colRight = GetColRight(i) - 1;
4550 if ( colRight > right )
4551 {
4552 break;
4553 }
4554
4555 if ( colRight >= left )
4556 {
4557 dc.DrawLine( colRight, top, colRight, bottom );
4558 }
4559 }
4560 }
4561
4562
4563 void wxGrid::DrawRowLabels( wxDC& dc )
4564 {
4565 if ( !m_numRows || !m_numCols ) return;
4566
4567 size_t i;
4568 size_t numLabels = m_rowLabelsExposed.GetCount();
4569
4570 for ( i = 0; i < numLabels; i++ )
4571 {
4572 DrawRowLabel( dc, m_rowLabelsExposed[i] );
4573 }
4574 }
4575
4576
4577 void wxGrid::DrawRowLabel( wxDC& dc, int row )
4578 {
4579 if ( GetRowHeight(row) <= 0 )
4580 return;
4581
4582 int rowTop = GetRowTop(row),
4583 rowBottom = GetRowBottom(row) - 1;
4584
4585 dc.SetPen( *wxBLACK_PEN );
4586 dc.DrawLine( m_rowLabelWidth-1, rowTop,
4587 m_rowLabelWidth-1, rowBottom );
4588
4589 dc.DrawLine( 0, rowBottom, m_rowLabelWidth-1, rowBottom );
4590
4591 dc.SetPen( *wxWHITE_PEN );
4592 dc.DrawLine( 0, rowTop, 0, rowBottom );
4593 dc.DrawLine( 0, rowTop, m_rowLabelWidth-1, rowTop );
4594
4595 dc.SetBackgroundMode( wxTRANSPARENT );
4596 dc.SetTextForeground( GetLabelTextColour() );
4597 dc.SetFont( GetLabelFont() );
4598
4599 int hAlign, vAlign;
4600 GetRowLabelAlignment( &hAlign, &vAlign );
4601
4602 wxRect rect;
4603 rect.SetX( 2 );
4604 rect.SetY( GetRowTop(row) + 2 );
4605 rect.SetWidth( m_rowLabelWidth - 4 );
4606 rect.SetHeight( GetRowHeight(row) - 4 );
4607 DrawTextRectangle( dc, GetRowLabelValue( row ), rect, hAlign, vAlign );
4608 }
4609
4610
4611 void wxGrid::DrawColLabels( wxDC& dc )
4612 {
4613 if ( !m_numRows || !m_numCols ) return;
4614
4615 size_t i;
4616 size_t numLabels = m_colLabelsExposed.GetCount();
4617
4618 for ( i = 0; i < numLabels; i++ )
4619 {
4620 DrawColLabel( dc, m_colLabelsExposed[i] );
4621 }
4622 }
4623
4624
4625 void wxGrid::DrawColLabel( wxDC& dc, int col )
4626 {
4627 if ( GetColWidth(col) <= 0 )
4628 return;
4629
4630 int colLeft = GetColLeft(col),
4631 colRight = GetColRight(col) - 1;
4632
4633 dc.SetPen( *wxBLACK_PEN );
4634 dc.DrawLine( colRight, 0,
4635 colRight, m_colLabelHeight-1 );
4636
4637 dc.DrawLine( colLeft, m_colLabelHeight-1,
4638 colRight, m_colLabelHeight-1 );
4639
4640 dc.SetPen( *wxWHITE_PEN );
4641 dc.DrawLine( colLeft, 0, colLeft, m_colLabelHeight-1 );
4642 dc.DrawLine( colLeft, 0, colRight, 0 );
4643
4644 dc.SetBackgroundMode( wxTRANSPARENT );
4645 dc.SetTextForeground( GetLabelTextColour() );
4646 dc.SetFont( GetLabelFont() );
4647
4648 dc.SetBackgroundMode( wxTRANSPARENT );
4649 dc.SetTextForeground( GetLabelTextColour() );
4650 dc.SetFont( GetLabelFont() );
4651
4652 int hAlign, vAlign;
4653 GetColLabelAlignment( &hAlign, &vAlign );
4654
4655 wxRect rect;
4656 rect.SetX( colLeft + 2 );
4657 rect.SetY( 2 );
4658 rect.SetWidth( GetColWidth(col) - 4 );
4659 rect.SetHeight( m_colLabelHeight - 4 );
4660 DrawTextRectangle( dc, GetColLabelValue( col ), rect, hAlign, vAlign );
4661 }
4662
4663
4664 void wxGrid::DrawTextRectangle( wxDC& dc,
4665 const wxString& value,
4666 const wxRect& rect,
4667 int horizAlign,
4668 int vertAlign )
4669 {
4670 long textWidth, textHeight;
4671 long lineWidth, lineHeight;
4672 wxArrayString lines;
4673
4674 dc.SetClippingRegion( rect );
4675 StringToLines( value, lines );
4676 if ( lines.GetCount() )
4677 {
4678 GetTextBoxSize( dc, lines, &textWidth, &textHeight );
4679 dc.GetTextExtent( lines[0], &lineWidth, &lineHeight );
4680
4681 float x, y;
4682 switch ( horizAlign )
4683 {
4684 case wxRIGHT:
4685 x = rect.x + (rect.width - textWidth - 1);
4686 break;
4687
4688 case wxCENTRE:
4689 x = rect.x + ((rect.width - textWidth)/2);
4690 break;
4691
4692 case wxLEFT:
4693 default:
4694 x = rect.x + 1;
4695 break;
4696 }
4697
4698 switch ( vertAlign )
4699 {
4700 case wxBOTTOM:
4701 y = rect.y + (rect.height - textHeight - 1);
4702 break;
4703
4704 case wxCENTRE:
4705 y = rect.y + ((rect.height - textHeight)/2);
4706 break;
4707
4708 case wxTOP:
4709 default:
4710 y = rect.y + 1;
4711 break;
4712 }
4713
4714 for ( size_t i = 0; i < lines.GetCount(); i++ )
4715 {
4716 dc.DrawText( lines[i], (long)x, (long)y );
4717 y += lineHeight;
4718 }
4719 }
4720
4721 dc.DestroyClippingRegion();
4722 }
4723
4724
4725 // Split multi line text up into an array of strings. Any existing
4726 // contents of the string array are preserved.
4727 //
4728 void wxGrid::StringToLines( const wxString& value, wxArrayString& lines )
4729 {
4730 int startPos = 0;
4731 int pos;
4732 wxString eol = wxTextFile::GetEOL( wxTextFileType_Unix );
4733 wxString tVal = wxTextFile::Translate( value, wxTextFileType_Unix );
4734
4735 while ( startPos < (int)tVal.Length() )
4736 {
4737 pos = tVal.Mid(startPos).Find( eol );
4738 if ( pos < 0 )
4739 {
4740 break;
4741 }
4742 else if ( pos == 0 )
4743 {
4744 lines.Add( wxEmptyString );
4745 }
4746 else
4747 {
4748 lines.Add( value.Mid(startPos, pos) );
4749 }
4750 startPos += pos+1;
4751 }
4752 if ( startPos < (int)value.Length() )
4753 {
4754 lines.Add( value.Mid( startPos ) );
4755 }
4756 }
4757
4758
4759 void wxGrid::GetTextBoxSize( wxDC& dc,
4760 wxArrayString& lines,
4761 long *width, long *height )
4762 {
4763 long w = 0;
4764 long h = 0;
4765 long lineW, lineH;
4766
4767 size_t i;
4768 for ( i = 0; i < lines.GetCount(); i++ )
4769 {
4770 dc.GetTextExtent( lines[i], &lineW, &lineH );
4771 w = wxMax( w, lineW );
4772 h += lineH;
4773 }
4774
4775 *width = w;
4776 *height = h;
4777 }
4778
4779
4780 //
4781 // ------ Edit control functions
4782 //
4783
4784
4785 void wxGrid::EnableEditing( bool edit )
4786 {
4787 // TODO: improve this ?
4788 //
4789 if ( edit != m_editable )
4790 {
4791 m_editable = edit;
4792
4793 // FIXME IMHO this won't disable the edit control if edit == FALSE
4794 // because of the check in the beginning of
4795 // EnableCellEditControl() just below (VZ)
4796 EnableCellEditControl(m_editable);
4797 }
4798 }
4799
4800
4801 void wxGrid::EnableCellEditControl( bool enable )
4802 {
4803 if (! m_editable)
4804 return;
4805
4806 if ( m_currentCellCoords == wxGridNoCellCoords )
4807 SetCurrentCell( 0, 0 );
4808
4809 if ( enable != m_cellEditCtrlEnabled )
4810 {
4811 // TODO allow the app to Veto() this event?
4812 SendEvent(enable ? wxEVT_GRID_EDITOR_SHOWN : wxEVT_GRID_EDITOR_HIDDEN);
4813
4814 if ( enable )
4815 {
4816 // this should be checked by the caller!
4817 wxASSERT_MSG( CanEnableCellControl(),
4818 _T("can't enable editing for this cell!") );
4819
4820 // do it before ShowCellEditControl()
4821 m_cellEditCtrlEnabled = enable;
4822
4823 SetEditControlValue();
4824 ShowCellEditControl();
4825 }
4826 else
4827 {
4828 HideCellEditControl();
4829 SaveEditControlValue();
4830
4831 // do it after HideCellEditControl()
4832 m_cellEditCtrlEnabled = enable;
4833 }
4834 }
4835 }
4836
4837 bool wxGrid::IsCurrentCellReadOnly() const
4838 {
4839 // const_cast
4840 wxGridCellAttr* attr = ((wxGrid *)this)->GetCellAttr(m_currentCellCoords);
4841 bool readonly = attr->IsReadOnly();
4842 attr->DecRef();
4843
4844 return readonly;
4845 }
4846
4847 bool wxGrid::CanEnableCellControl() const
4848 {
4849 return m_editable && !IsCurrentCellReadOnly();
4850 }
4851
4852 bool wxGrid::IsCellEditControlEnabled() const
4853 {
4854 // the cell edit control might be disable for all cells or just for the
4855 // current one if it's read only
4856 return m_cellEditCtrlEnabled ? !IsCurrentCellReadOnly() : FALSE;
4857 }
4858
4859 wxWindow *wxGrid::GetGridWindow() const
4860 {
4861 return m_gridWin;
4862 }
4863
4864 void wxGrid::ShowCellEditControl()
4865 {
4866 if ( IsCellEditControlEnabled() )
4867 {
4868 if ( !IsVisible( m_currentCellCoords ) )
4869 {
4870 return;
4871 }
4872 else
4873 {
4874 wxRect rect = CellToRect( m_currentCellCoords );
4875 int row = m_currentCellCoords.GetRow();
4876 int col = m_currentCellCoords.GetCol();
4877
4878 // convert to scrolled coords
4879 //
4880 int left, top, right, bottom;
4881 CalcScrolledPosition( rect.GetLeft(), rect.GetTop(), &left, &top );
4882 CalcScrolledPosition( rect.GetRight(), rect.GetBottom(), &right, &bottom );
4883
4884 // cell is shifted by one pixel
4885 left--;
4886 top--;
4887 right--;
4888 bottom--;
4889
4890 // Make the edit control large enough to allow for internal
4891 // margins
4892 //
4893 // TODO: remove this if the text ctrl sizing is improved esp. for
4894 // unix
4895 //
4896 int extra;
4897 #if defined(__WXMOTIF__)
4898 if ( row == 0 || col == 0 )
4899 {
4900 extra = 2;
4901 }
4902 else
4903 {
4904 extra = 4;
4905 }
4906 #else
4907 if ( row == 0 || col == 0 )
4908 {
4909 extra = 1;
4910 }
4911 else
4912 {
4913 extra = 2;
4914 }
4915 #endif
4916
4917 #if defined(__WXGTK__)
4918 int top_diff = 0;
4919 int left_diff = 0;
4920 if (left != 0) left_diff++;
4921 if (top != 0) top_diff++;
4922 rect.SetLeft( left + left_diff );
4923 rect.SetTop( top + top_diff );
4924 rect.SetRight( rect.GetRight() - left_diff );
4925 rect.SetBottom( rect.GetBottom() - top_diff );
4926 #else
4927 rect.SetLeft( wxMax(0, left - extra) );
4928 rect.SetTop( wxMax(0, top - extra) );
4929 rect.SetRight( rect.GetRight() + 2*extra );
4930 rect.SetBottom( rect.GetBottom() + 2*extra );
4931 #endif
4932
4933 wxGridCellAttr* attr = GetCellAttr(row, col);
4934 wxGridCellEditor* editor = attr->GetEditor(GetDefaultEditorForCell(row, col));
4935 if ( !editor->IsCreated() )
4936 {
4937 editor->Create(m_gridWin, -1,
4938 new wxGridCellEditorEvtHandler(this, editor));
4939 }
4940
4941 editor->SetSize( rect );
4942 editor->Show( TRUE, attr );
4943 editor->BeginEdit(row, col, this);
4944 attr->DecRef();
4945 }
4946 }
4947 }
4948
4949
4950 void wxGrid::HideCellEditControl()
4951 {
4952 if ( IsCellEditControlEnabled() )
4953 {
4954 int row = m_currentCellCoords.GetRow();
4955 int col = m_currentCellCoords.GetCol();
4956
4957 wxGridCellAttr* attr = GetCellAttr(row, col);
4958 attr->GetEditor(GetDefaultEditorForCell(row, col))->Show( FALSE );
4959 attr->DecRef();
4960 m_gridWin->SetFocus();
4961 }
4962 }
4963
4964
4965 void wxGrid::SetEditControlValue( const wxString& value )
4966 {
4967 // RD: The new Editors get the value from the table themselves now. This
4968 // method can probably be removed...
4969 }
4970
4971
4972 void wxGrid::SaveEditControlValue()
4973 {
4974 if ( IsCellEditControlEnabled() )
4975 {
4976 int row = m_currentCellCoords.GetRow();
4977 int col = m_currentCellCoords.GetCol();
4978
4979 wxGridCellAttr* attr = GetCellAttr(row, col);
4980 wxGridCellEditor* editor = attr->GetEditor(GetDefaultEditorForCell(row, col));
4981 bool changed = editor->EndEdit(row, col, TRUE, this);
4982
4983 attr->DecRef();
4984
4985 if (changed)
4986 {
4987 SendEvent( wxEVT_GRID_CELL_CHANGE,
4988 m_currentCellCoords.GetRow(),
4989 m_currentCellCoords.GetCol() );
4990 }
4991 }
4992 }
4993
4994
4995 //
4996 // ------ Grid location functions
4997 // Note that all of these functions work with the logical coordinates of
4998 // grid cells and labels so you will need to convert from device
4999 // coordinates for mouse events etc.
5000 //
5001
5002 void wxGrid::XYToCell( int x, int y, wxGridCellCoords& coords )
5003 {
5004 int row = YToRow(y);
5005 int col = XToCol(x);
5006
5007 if ( row == -1 || col == -1 )
5008 {
5009 coords = wxGridNoCellCoords;
5010 }
5011 else
5012 {
5013 coords.Set( row, col );
5014 }
5015 }
5016
5017
5018 int wxGrid::YToRow( int y )
5019 {
5020 int i;
5021
5022 for ( i = 0; i < m_numRows; i++ )
5023 {
5024 if ( y < GetRowBottom(i) )
5025 return i;
5026 }
5027
5028 return m_numRows; //-1;
5029 }
5030
5031
5032 int wxGrid::XToCol( int x )
5033 {
5034 int i;
5035
5036 for ( i = 0; i < m_numCols; i++ )
5037 {
5038 if ( x < GetColRight(i) )
5039 return i;
5040 }
5041
5042 return m_numCols; //-1;
5043 }
5044
5045
5046 // return the row number that that the y coord is near the edge of, or
5047 // -1 if not near an edge
5048 //
5049 int wxGrid::YToEdgeOfRow( int y )
5050 {
5051 int i, d;
5052
5053 for ( i = 0; i < m_numRows; i++ )
5054 {
5055 if ( GetRowHeight(i) > WXGRID_LABEL_EDGE_ZONE )
5056 {
5057 d = abs( y - GetRowBottom(i) );
5058 if ( d < WXGRID_LABEL_EDGE_ZONE )
5059 return i;
5060 }
5061 }
5062
5063 return -1;
5064 }
5065
5066
5067 // return the col number that that the x coord is near the edge of, or
5068 // -1 if not near an edge
5069 //
5070 int wxGrid::XToEdgeOfCol( int x )
5071 {
5072 int i, d;
5073
5074 for ( i = 0; i < m_numCols; i++ )
5075 {
5076 if ( GetColWidth(i) > WXGRID_LABEL_EDGE_ZONE )
5077 {
5078 d = abs( x - GetColRight(i) );
5079 if ( d < WXGRID_LABEL_EDGE_ZONE )
5080 return i;
5081 }
5082 }
5083
5084 return -1;
5085 }
5086
5087
5088 wxRect wxGrid::CellToRect( int row, int col )
5089 {
5090 wxRect rect( -1, -1, -1, -1 );
5091
5092 if ( row >= 0 && row < m_numRows &&
5093 col >= 0 && col < m_numCols )
5094 {
5095 rect.x = GetColLeft(col);
5096 rect.y = GetRowTop(row);
5097 rect.width = GetColWidth(col);
5098 rect.height = GetRowHeight(row);
5099 }
5100
5101 return rect;
5102 }
5103
5104
5105 bool wxGrid::IsVisible( int row, int col, bool wholeCellVisible )
5106 {
5107 // get the cell rectangle in logical coords
5108 //
5109 wxRect r( CellToRect( row, col ) );
5110
5111 // convert to device coords
5112 //
5113 int left, top, right, bottom;
5114 CalcScrolledPosition( r.GetLeft(), r.GetTop(), &left, &top );
5115 CalcScrolledPosition( r.GetRight(), r.GetBottom(), &right, &bottom );
5116
5117 // check against the client area of the grid window
5118 //
5119 int cw, ch;
5120 m_gridWin->GetClientSize( &cw, &ch );
5121
5122 if ( wholeCellVisible )
5123 {
5124 // is the cell wholly visible ?
5125 //
5126 return ( left >= 0 && right <= cw &&
5127 top >= 0 && bottom <= ch );
5128 }
5129 else
5130 {
5131 // is the cell partly visible ?
5132 //
5133 return ( ((left >=0 && left < cw) || (right > 0 && right <= cw)) &&
5134 ((top >=0 && top < ch) || (bottom > 0 && bottom <= ch)) );
5135 }
5136 }
5137
5138
5139 // make the specified cell location visible by doing a minimal amount
5140 // of scrolling
5141 //
5142 void wxGrid::MakeCellVisible( int row, int col )
5143 {
5144 int i;
5145 int xpos = -1, ypos = -1;
5146
5147 if ( row >= 0 && row < m_numRows &&
5148 col >= 0 && col < m_numCols )
5149 {
5150 // get the cell rectangle in logical coords
5151 //
5152 wxRect r( CellToRect( row, col ) );
5153
5154 // convert to device coords
5155 //
5156 int left, top, right, bottom;
5157 CalcScrolledPosition( r.GetLeft(), r.GetTop(), &left, &top );
5158 CalcScrolledPosition( r.GetRight(), r.GetBottom(), &right, &bottom );
5159
5160 int cw, ch;
5161 m_gridWin->GetClientSize( &cw, &ch );
5162
5163 if ( top < 0 )
5164 {
5165 ypos = r.GetTop();
5166 }
5167 else if ( bottom > ch )
5168 {
5169 int h = r.GetHeight();
5170 ypos = r.GetTop();
5171 for ( i = row-1; i >= 0; i-- )
5172 {
5173 int rowHeight = GetRowHeight(i);
5174 if ( h + rowHeight > ch )
5175 break;
5176
5177 h += rowHeight;
5178 ypos -= rowHeight;
5179 }
5180
5181 // we divide it later by GRID_SCROLL_LINE, make sure that we don't
5182 // have rounding errors (this is important, because if we do, we
5183 // might not scroll at all and some cells won't be redrawn)
5184 ypos += GRID_SCROLL_LINE / 2;
5185 }
5186
5187 if ( left < 0 )
5188 {
5189 xpos = r.GetLeft();
5190 }
5191 else if ( right > cw )
5192 {
5193 int w = r.GetWidth();
5194 xpos = r.GetLeft();
5195 for ( i = col-1; i >= 0; i-- )
5196 {
5197 int colWidth = GetColWidth(i);
5198 if ( w + colWidth > cw )
5199 break;
5200
5201 w += colWidth;
5202 xpos -= colWidth;
5203 }
5204
5205 // see comment for ypos above
5206 xpos += GRID_SCROLL_LINE / 2;
5207 }
5208
5209 if ( xpos != -1 || ypos != -1 )
5210 {
5211 if ( xpos != -1 ) xpos /= GRID_SCROLL_LINE;
5212 if ( ypos != -1 ) ypos /= GRID_SCROLL_LINE;
5213 Scroll( xpos, ypos );
5214 AdjustScrollbars();
5215 }
5216 }
5217 }
5218
5219
5220 //
5221 // ------ Grid cursor movement functions
5222 //
5223
5224 bool wxGrid::MoveCursorUp()
5225 {
5226 if ( m_currentCellCoords != wxGridNoCellCoords &&
5227 m_currentCellCoords.GetRow() > 0 )
5228 {
5229 MakeCellVisible( m_currentCellCoords.GetRow() - 1,
5230 m_currentCellCoords.GetCol() );
5231
5232 SetCurrentCell( m_currentCellCoords.GetRow() - 1,
5233 m_currentCellCoords.GetCol() );
5234
5235 return TRUE;
5236 }
5237
5238 return FALSE;
5239 }
5240
5241
5242 bool wxGrid::MoveCursorDown()
5243 {
5244 // TODO: allow for scrolling
5245 //
5246 if ( m_currentCellCoords != wxGridNoCellCoords &&
5247 m_currentCellCoords.GetRow() < m_numRows-1 )
5248 {
5249 MakeCellVisible( m_currentCellCoords.GetRow() + 1,
5250 m_currentCellCoords.GetCol() );
5251
5252 SetCurrentCell( m_currentCellCoords.GetRow() + 1,
5253 m_currentCellCoords.GetCol() );
5254
5255 return TRUE;
5256 }
5257
5258 return FALSE;
5259 }
5260
5261
5262 bool wxGrid::MoveCursorLeft()
5263 {
5264 if ( m_currentCellCoords != wxGridNoCellCoords &&
5265 m_currentCellCoords.GetCol() > 0 )
5266 {
5267 MakeCellVisible( m_currentCellCoords.GetRow(),
5268 m_currentCellCoords.GetCol() - 1 );
5269
5270 SetCurrentCell( m_currentCellCoords.GetRow(),
5271 m_currentCellCoords.GetCol() - 1 );
5272
5273 return TRUE;
5274 }
5275
5276 return FALSE;
5277 }
5278
5279
5280 bool wxGrid::MoveCursorRight()
5281 {
5282 if ( m_currentCellCoords != wxGridNoCellCoords &&
5283 m_currentCellCoords.GetCol() < m_numCols - 1 )
5284 {
5285 MakeCellVisible( m_currentCellCoords.GetRow(),
5286 m_currentCellCoords.GetCol() + 1 );
5287
5288 SetCurrentCell( m_currentCellCoords.GetRow(),
5289 m_currentCellCoords.GetCol() + 1 );
5290
5291 return TRUE;
5292 }
5293
5294 return FALSE;
5295 }
5296
5297
5298 bool wxGrid::MovePageUp()
5299 {
5300 if ( m_currentCellCoords == wxGridNoCellCoords ) return FALSE;
5301
5302 int row = m_currentCellCoords.GetRow();
5303 if ( row > 0 )
5304 {
5305 int cw, ch;
5306 m_gridWin->GetClientSize( &cw, &ch );
5307
5308 int y = GetRowTop(row);
5309 int newRow = YToRow( y - ch + 1 );
5310 if ( newRow == -1 )
5311 {
5312 newRow = 0;
5313 }
5314 else if ( newRow == row )
5315 {
5316 newRow = row - 1;
5317 }
5318
5319 MakeCellVisible( newRow, m_currentCellCoords.GetCol() );
5320 SetCurrentCell( newRow, m_currentCellCoords.GetCol() );
5321
5322 return TRUE;
5323 }
5324
5325 return FALSE;
5326 }
5327
5328 bool wxGrid::MovePageDown()
5329 {
5330 if ( m_currentCellCoords == wxGridNoCellCoords ) return FALSE;
5331
5332 int row = m_currentCellCoords.GetRow();
5333 if ( row < m_numRows )
5334 {
5335 int cw, ch;
5336 m_gridWin->GetClientSize( &cw, &ch );
5337
5338 int y = GetRowTop(row);
5339 int newRow = YToRow( y + ch );
5340 if ( newRow == -1 )
5341 {
5342 newRow = m_numRows - 1;
5343 }
5344 else if ( newRow == row )
5345 {
5346 newRow = row + 1;
5347 }
5348
5349 MakeCellVisible( newRow, m_currentCellCoords.GetCol() );
5350 SetCurrentCell( newRow, m_currentCellCoords.GetCol() );
5351
5352 return TRUE;
5353 }
5354
5355 return FALSE;
5356 }
5357
5358 bool wxGrid::MoveCursorUpBlock()
5359 {
5360 if ( m_table &&
5361 m_currentCellCoords != wxGridNoCellCoords &&
5362 m_currentCellCoords.GetRow() > 0 )
5363 {
5364 int row = m_currentCellCoords.GetRow();
5365 int col = m_currentCellCoords.GetCol();
5366
5367 if ( m_table->IsEmptyCell(row, col) )
5368 {
5369 // starting in an empty cell: find the next block of
5370 // non-empty cells
5371 //
5372 while ( row > 0 )
5373 {
5374 row-- ;
5375 if ( !(m_table->IsEmptyCell(row, col)) ) break;
5376 }
5377 }
5378 else if ( m_table->IsEmptyCell(row-1, col) )
5379 {
5380 // starting at the top of a block: find the next block
5381 //
5382 row--;
5383 while ( row > 0 )
5384 {
5385 row-- ;
5386 if ( !(m_table->IsEmptyCell(row, col)) ) break;
5387 }
5388 }
5389 else
5390 {
5391 // starting within a block: find the top of the block
5392 //
5393 while ( row > 0 )
5394 {
5395 row-- ;
5396 if ( m_table->IsEmptyCell(row, col) )
5397 {
5398 row++ ;
5399 break;
5400 }
5401 }
5402 }
5403
5404 MakeCellVisible( row, col );
5405 SetCurrentCell( row, col );
5406
5407 return TRUE;
5408 }
5409
5410 return FALSE;
5411 }
5412
5413 bool wxGrid::MoveCursorDownBlock()
5414 {
5415 if ( m_table &&
5416 m_currentCellCoords != wxGridNoCellCoords &&
5417 m_currentCellCoords.GetRow() < m_numRows-1 )
5418 {
5419 int row = m_currentCellCoords.GetRow();
5420 int col = m_currentCellCoords.GetCol();
5421
5422 if ( m_table->IsEmptyCell(row, col) )
5423 {
5424 // starting in an empty cell: find the next block of
5425 // non-empty cells
5426 //
5427 while ( row < m_numRows-1 )
5428 {
5429 row++ ;
5430 if ( !(m_table->IsEmptyCell(row, col)) ) break;
5431 }
5432 }
5433 else if ( m_table->IsEmptyCell(row+1, col) )
5434 {
5435 // starting at the bottom of a block: find the next block
5436 //
5437 row++;
5438 while ( row < m_numRows-1 )
5439 {
5440 row++ ;
5441 if ( !(m_table->IsEmptyCell(row, col)) ) break;
5442 }
5443 }
5444 else
5445 {
5446 // starting within a block: find the bottom of the block
5447 //
5448 while ( row < m_numRows-1 )
5449 {
5450 row++ ;
5451 if ( m_table->IsEmptyCell(row, col) )
5452 {
5453 row-- ;
5454 break;
5455 }
5456 }
5457 }
5458
5459 MakeCellVisible( row, col );
5460 SetCurrentCell( row, col );
5461
5462 return TRUE;
5463 }
5464
5465 return FALSE;
5466 }
5467
5468 bool wxGrid::MoveCursorLeftBlock()
5469 {
5470 if ( m_table &&
5471 m_currentCellCoords != wxGridNoCellCoords &&
5472 m_currentCellCoords.GetCol() > 0 )
5473 {
5474 int row = m_currentCellCoords.GetRow();
5475 int col = m_currentCellCoords.GetCol();
5476
5477 if ( m_table->IsEmptyCell(row, col) )
5478 {
5479 // starting in an empty cell: find the next block of
5480 // non-empty cells
5481 //
5482 while ( col > 0 )
5483 {
5484 col-- ;
5485 if ( !(m_table->IsEmptyCell(row, col)) ) break;
5486 }
5487 }
5488 else if ( m_table->IsEmptyCell(row, col-1) )
5489 {
5490 // starting at the left of a block: find the next block
5491 //
5492 col--;
5493 while ( col > 0 )
5494 {
5495 col-- ;
5496 if ( !(m_table->IsEmptyCell(row, col)) ) break;
5497 }
5498 }
5499 else
5500 {
5501 // starting within a block: find the left of the block
5502 //
5503 while ( col > 0 )
5504 {
5505 col-- ;
5506 if ( m_table->IsEmptyCell(row, col) )
5507 {
5508 col++ ;
5509 break;
5510 }
5511 }
5512 }
5513
5514 MakeCellVisible( row, col );
5515 SetCurrentCell( row, col );
5516
5517 return TRUE;
5518 }
5519
5520 return FALSE;
5521 }
5522
5523 bool wxGrid::MoveCursorRightBlock()
5524 {
5525 if ( m_table &&
5526 m_currentCellCoords != wxGridNoCellCoords &&
5527 m_currentCellCoords.GetCol() < m_numCols-1 )
5528 {
5529 int row = m_currentCellCoords.GetRow();
5530 int col = m_currentCellCoords.GetCol();
5531
5532 if ( m_table->IsEmptyCell(row, col) )
5533 {
5534 // starting in an empty cell: find the next block of
5535 // non-empty cells
5536 //
5537 while ( col < m_numCols-1 )
5538 {
5539 col++ ;
5540 if ( !(m_table->IsEmptyCell(row, col)) ) break;
5541 }
5542 }
5543 else if ( m_table->IsEmptyCell(row, col+1) )
5544 {
5545 // starting at the right of a block: find the next block
5546 //
5547 col++;
5548 while ( col < m_numCols-1 )
5549 {
5550 col++ ;
5551 if ( !(m_table->IsEmptyCell(row, col)) ) break;
5552 }
5553 }
5554 else
5555 {
5556 // starting within a block: find the right of the block
5557 //
5558 while ( col < m_numCols-1 )
5559 {
5560 col++ ;
5561 if ( m_table->IsEmptyCell(row, col) )
5562 {
5563 col-- ;
5564 break;
5565 }
5566 }
5567 }
5568
5569 MakeCellVisible( row, col );
5570 SetCurrentCell( row, col );
5571
5572 return TRUE;
5573 }
5574
5575 return FALSE;
5576 }
5577
5578
5579
5580 //
5581 // ------ Label values and formatting
5582 //
5583
5584 void wxGrid::GetRowLabelAlignment( int *horiz, int *vert )
5585 {
5586 *horiz = m_rowLabelHorizAlign;
5587 *vert = m_rowLabelVertAlign;
5588 }
5589
5590 void wxGrid::GetColLabelAlignment( int *horiz, int *vert )
5591 {
5592 *horiz = m_colLabelHorizAlign;
5593 *vert = m_colLabelVertAlign;
5594 }
5595
5596 wxString wxGrid::GetRowLabelValue( int row )
5597 {
5598 if ( m_table )
5599 {
5600 return m_table->GetRowLabelValue( row );
5601 }
5602 else
5603 {
5604 wxString s;
5605 s << row;
5606 return s;
5607 }
5608 }
5609
5610 wxString wxGrid::GetColLabelValue( int col )
5611 {
5612 if ( m_table )
5613 {
5614 return m_table->GetColLabelValue( col );
5615 }
5616 else
5617 {
5618 wxString s;
5619 s << col;
5620 return s;
5621 }
5622 }
5623
5624
5625 void wxGrid::SetRowLabelSize( int width )
5626 {
5627 width = wxMax( width, 0 );
5628 if ( width != m_rowLabelWidth )
5629 {
5630 if ( width == 0 )
5631 {
5632 m_rowLabelWin->Show( FALSE );
5633 m_cornerLabelWin->Show( FALSE );
5634 }
5635 else if ( m_rowLabelWidth == 0 )
5636 {
5637 m_rowLabelWin->Show( TRUE );
5638 if ( m_colLabelHeight > 0 ) m_cornerLabelWin->Show( TRUE );
5639 }
5640
5641 m_rowLabelWidth = width;
5642 CalcWindowSizes();
5643 Refresh( TRUE );
5644 }
5645 }
5646
5647
5648 void wxGrid::SetColLabelSize( int height )
5649 {
5650 height = wxMax( height, 0 );
5651 if ( height != m_colLabelHeight )
5652 {
5653 if ( height == 0 )
5654 {
5655 m_colLabelWin->Show( FALSE );
5656 m_cornerLabelWin->Show( FALSE );
5657 }
5658 else if ( m_colLabelHeight == 0 )
5659 {
5660 m_colLabelWin->Show( TRUE );
5661 if ( m_rowLabelWidth > 0 ) m_cornerLabelWin->Show( TRUE );
5662 }
5663
5664 m_colLabelHeight = height;
5665 CalcWindowSizes();
5666 Refresh( TRUE );
5667 }
5668 }
5669
5670
5671 void wxGrid::SetLabelBackgroundColour( const wxColour& colour )
5672 {
5673 if ( m_labelBackgroundColour != colour )
5674 {
5675 m_labelBackgroundColour = colour;
5676 m_rowLabelWin->SetBackgroundColour( colour );
5677 m_colLabelWin->SetBackgroundColour( colour );
5678 m_cornerLabelWin->SetBackgroundColour( colour );
5679
5680 if ( !GetBatchCount() )
5681 {
5682 m_rowLabelWin->Refresh();
5683 m_colLabelWin->Refresh();
5684 m_cornerLabelWin->Refresh();
5685 }
5686 }
5687 }
5688
5689 void wxGrid::SetLabelTextColour( const wxColour& colour )
5690 {
5691 if ( m_labelTextColour != colour )
5692 {
5693 m_labelTextColour = colour;
5694 if ( !GetBatchCount() )
5695 {
5696 m_rowLabelWin->Refresh();
5697 m_colLabelWin->Refresh();
5698 }
5699 }
5700 }
5701
5702 void wxGrid::SetLabelFont( const wxFont& font )
5703 {
5704 m_labelFont = font;
5705 if ( !GetBatchCount() )
5706 {
5707 m_rowLabelWin->Refresh();
5708 m_colLabelWin->Refresh();
5709 }
5710 }
5711
5712 void wxGrid::SetRowLabelAlignment( int horiz, int vert )
5713 {
5714 if ( horiz == wxLEFT || horiz == wxCENTRE || horiz == wxRIGHT )
5715 {
5716 m_rowLabelHorizAlign = horiz;
5717 }
5718
5719 if ( vert == wxTOP || vert == wxCENTRE || vert == wxBOTTOM )
5720 {
5721 m_rowLabelVertAlign = vert;
5722 }
5723
5724 if ( !GetBatchCount() )
5725 {
5726 m_rowLabelWin->Refresh();
5727 }
5728 }
5729
5730 void wxGrid::SetColLabelAlignment( int horiz, int vert )
5731 {
5732 if ( horiz == wxLEFT || horiz == wxCENTRE || horiz == wxRIGHT )
5733 {
5734 m_colLabelHorizAlign = horiz;
5735 }
5736
5737 if ( vert == wxTOP || vert == wxCENTRE || vert == wxBOTTOM )
5738 {
5739 m_colLabelVertAlign = vert;
5740 }
5741
5742 if ( !GetBatchCount() )
5743 {
5744 m_colLabelWin->Refresh();
5745 }
5746 }
5747
5748 void wxGrid::SetRowLabelValue( int row, const wxString& s )
5749 {
5750 if ( m_table )
5751 {
5752 m_table->SetRowLabelValue( row, s );
5753 if ( !GetBatchCount() )
5754 {
5755 wxRect rect = CellToRect( row, 0);
5756 if ( rect.height > 0 )
5757 {
5758 CalcScrolledPosition(0, rect.y, &rect.x, &rect.y);
5759 rect.x = m_left;
5760 rect.width = m_rowLabelWidth;
5761 m_rowLabelWin->Refresh( TRUE, &rect );
5762 }
5763 }
5764 }
5765 }
5766
5767 void wxGrid::SetColLabelValue( int col, const wxString& s )
5768 {
5769 if ( m_table )
5770 {
5771 m_table->SetColLabelValue( col, s );
5772 if ( !GetBatchCount() )
5773 {
5774 wxRect rect = CellToRect( 0, col );
5775 if ( rect.width > 0 )
5776 {
5777 CalcScrolledPosition(rect.x, 0, &rect.x, &rect.y);
5778 rect.y = m_top;
5779 rect.height = m_colLabelHeight;
5780 m_colLabelWin->Refresh( TRUE, &rect );
5781 }
5782 }
5783 }
5784 }
5785
5786 void wxGrid::SetGridLineColour( const wxColour& colour )
5787 {
5788 if ( m_gridLineColour != colour )
5789 {
5790 m_gridLineColour = colour;
5791
5792 wxClientDC dc( m_gridWin );
5793 PrepareDC( dc );
5794 DrawAllGridLines( dc, wxRegion() );
5795 }
5796 }
5797
5798 void wxGrid::EnableGridLines( bool enable )
5799 {
5800 if ( enable != m_gridLinesEnabled )
5801 {
5802 m_gridLinesEnabled = enable;
5803
5804 if ( !GetBatchCount() )
5805 {
5806 if ( enable )
5807 {
5808 wxClientDC dc( m_gridWin );
5809 PrepareDC( dc );
5810 DrawAllGridLines( dc, wxRegion() );
5811 }
5812 else
5813 {
5814 m_gridWin->Refresh();
5815 }
5816 }
5817 }
5818 }
5819
5820
5821 int wxGrid::GetDefaultRowSize()
5822 {
5823 return m_defaultRowHeight;
5824 }
5825
5826 int wxGrid::GetRowSize( int row )
5827 {
5828 wxCHECK_MSG( row >= 0 && row < m_numRows, 0, _T("invalid row index") );
5829
5830 return GetRowHeight(row);
5831 }
5832
5833 int wxGrid::GetDefaultColSize()
5834 {
5835 return m_defaultColWidth;
5836 }
5837
5838 int wxGrid::GetColSize( int col )
5839 {
5840 wxCHECK_MSG( col >= 0 && col < m_numCols, 0, _T("invalid column index") );
5841
5842 return GetColWidth(col);
5843 }
5844
5845 // ============================================================================
5846 // access to the grid attributes: each of them has a default value in the grid
5847 // itself and may be overidden on a per-cell basis
5848 // ============================================================================
5849
5850 // ----------------------------------------------------------------------------
5851 // setting default attributes
5852 // ----------------------------------------------------------------------------
5853
5854 void wxGrid::SetDefaultCellBackgroundColour( const wxColour& col )
5855 {
5856 m_defaultCellAttr->SetBackgroundColour(col);
5857 #ifdef __WXGTK__
5858 m_gridWin->SetBackgroundColour(col);
5859 #endif
5860 }
5861
5862 void wxGrid::SetDefaultCellTextColour( const wxColour& col )
5863 {
5864 m_defaultCellAttr->SetTextColour(col);
5865 }
5866
5867 void wxGrid::SetDefaultCellAlignment( int horiz, int vert )
5868 {
5869 m_defaultCellAttr->SetAlignment(horiz, vert);
5870 }
5871
5872 void wxGrid::SetDefaultCellFont( const wxFont& font )
5873 {
5874 m_defaultCellAttr->SetFont(font);
5875 }
5876
5877 void wxGrid::SetDefaultRenderer(wxGridCellRenderer *renderer)
5878 {
5879 m_defaultCellAttr->SetRenderer(renderer);
5880 }
5881
5882 void wxGrid::SetDefaultEditor(wxGridCellEditor *editor)
5883 {
5884 m_defaultCellAttr->SetEditor(editor);
5885 }
5886
5887 // ----------------------------------------------------------------------------
5888 // access to the default attrbiutes
5889 // ----------------------------------------------------------------------------
5890
5891 wxColour wxGrid::GetDefaultCellBackgroundColour()
5892 {
5893 return m_defaultCellAttr->GetBackgroundColour();
5894 }
5895
5896 wxColour wxGrid::GetDefaultCellTextColour()
5897 {
5898 return m_defaultCellAttr->GetTextColour();
5899 }
5900
5901 wxFont wxGrid::GetDefaultCellFont()
5902 {
5903 return m_defaultCellAttr->GetFont();
5904 }
5905
5906 void wxGrid::GetDefaultCellAlignment( int *horiz, int *vert )
5907 {
5908 m_defaultCellAttr->GetAlignment(horiz, vert);
5909 }
5910
5911 wxGridCellRenderer *wxGrid::GetDefaultRenderer() const
5912 {
5913 return m_defaultCellAttr->GetRenderer(NULL);
5914 }
5915
5916 wxGridCellEditor *wxGrid::GetDefaultEditor() const
5917 {
5918 return m_defaultCellAttr->GetEditor(NULL);
5919 }
5920
5921 // ----------------------------------------------------------------------------
5922 // access to cell attributes
5923 // ----------------------------------------------------------------------------
5924
5925 wxColour wxGrid::GetCellBackgroundColour(int row, int col)
5926 {
5927 wxGridCellAttr *attr = GetCellAttr(row, col);
5928 wxColour colour = attr->GetBackgroundColour();
5929 attr->SafeDecRef();
5930 return colour;
5931 }
5932
5933 wxColour wxGrid::GetCellTextColour( int row, int col )
5934 {
5935 wxGridCellAttr *attr = GetCellAttr(row, col);
5936 wxColour colour = attr->GetTextColour();
5937 attr->SafeDecRef();
5938 return colour;
5939 }
5940
5941 wxFont wxGrid::GetCellFont( int row, int col )
5942 {
5943 wxGridCellAttr *attr = GetCellAttr(row, col);
5944 wxFont font = attr->GetFont();
5945 attr->SafeDecRef();
5946 return font;
5947 }
5948
5949 void wxGrid::GetCellAlignment( int row, int col, int *horiz, int *vert )
5950 {
5951 wxGridCellAttr *attr = GetCellAttr(row, col);
5952 attr->GetAlignment(horiz, vert);
5953 attr->SafeDecRef();
5954 }
5955
5956 wxGridCellRenderer* wxGrid::GetCellRenderer(int row, int col)
5957 {
5958 wxGridCellAttr* attr = GetCellAttr(row, col);
5959 wxGridCellRenderer* renderer = attr->GetRenderer(GetDefaultRendererForCell(row,col));
5960 attr->DecRef();
5961 return renderer;
5962 }
5963
5964 wxGridCellEditor* wxGrid::GetCellEditor(int row, int col)
5965 {
5966 wxGridCellAttr* attr = GetCellAttr(row, col);
5967 wxGridCellEditor* editor = attr->GetEditor(GetDefaultEditorForCell(row, col));
5968 attr->DecRef();
5969 return editor;
5970 }
5971
5972 bool wxGrid::IsReadOnly(int row, int col) const
5973 {
5974 wxGridCellAttr* attr = GetCellAttr(row, col);
5975 bool isReadOnly = attr->IsReadOnly();
5976 attr->DecRef();
5977 return isReadOnly;
5978 }
5979
5980 // ----------------------------------------------------------------------------
5981 // attribute support: cache, automatic provider creation, ...
5982 // ----------------------------------------------------------------------------
5983
5984 bool wxGrid::CanHaveAttributes()
5985 {
5986 if ( !m_table )
5987 {
5988 return FALSE;
5989 }
5990
5991 return m_table->CanHaveAttributes();
5992 }
5993
5994 void wxGrid::ClearAttrCache()
5995 {
5996 if ( m_attrCache.row != -1 )
5997 {
5998 m_attrCache.attr->SafeDecRef();
5999 m_attrCache.row = -1;
6000 }
6001 }
6002
6003 void wxGrid::CacheAttr(int row, int col, wxGridCellAttr *attr) const
6004 {
6005 wxGrid *self = (wxGrid *)this; // const_cast
6006
6007 self->ClearAttrCache();
6008 self->m_attrCache.row = row;
6009 self->m_attrCache.col = col;
6010 self->m_attrCache.attr = attr;
6011 attr->SafeIncRef();
6012 }
6013
6014 bool wxGrid::LookupAttr(int row, int col, wxGridCellAttr **attr) const
6015 {
6016 if ( row == m_attrCache.row && col == m_attrCache.col )
6017 {
6018 *attr = m_attrCache.attr;
6019 (*attr)->SafeIncRef();
6020
6021 #ifdef DEBUG_ATTR_CACHE
6022 gs_nAttrCacheHits++;
6023 #endif
6024
6025 return TRUE;
6026 }
6027 else
6028 {
6029 #ifdef DEBUG_ATTR_CACHE
6030 gs_nAttrCacheMisses++;
6031 #endif
6032 return FALSE;
6033 }
6034 }
6035
6036 wxGridCellAttr *wxGrid::GetCellAttr(int row, int col) const
6037 {
6038 wxGridCellAttr *attr;
6039 if ( !LookupAttr(row, col, &attr) )
6040 {
6041 attr = m_table ? m_table->GetAttr(row, col) : (wxGridCellAttr *)NULL;
6042 CacheAttr(row, col, attr);
6043 }
6044 if (attr)
6045 {
6046 attr->SetDefAttr(m_defaultCellAttr);
6047 }
6048 else
6049 {
6050 attr = m_defaultCellAttr;
6051 attr->IncRef();
6052 }
6053
6054 return attr;
6055 }
6056
6057 wxGridCellAttr *wxGrid::GetOrCreateCellAttr(int row, int col) const
6058 {
6059 wxGridCellAttr *attr;
6060 if ( !LookupAttr(row, col, &attr) || !attr )
6061 {
6062 wxASSERT_MSG( m_table,
6063 _T("we may only be called if CanHaveAttributes() "
6064 "returned TRUE and then m_table should be !NULL") );
6065
6066 attr = m_table->GetAttr(row, col);
6067 if ( !attr )
6068 {
6069 attr = new wxGridCellAttr;
6070
6071 // artificially inc the ref count to match DecRef() in caller
6072 attr->IncRef();
6073
6074 m_table->SetAttr(attr, row, col);
6075 }
6076
6077 CacheAttr(row, col, attr);
6078 }
6079 attr->SetDefAttr(m_defaultCellAttr);
6080 return attr;
6081 }
6082
6083 // ----------------------------------------------------------------------------
6084 // setting cell attributes: this is forwarded to the table
6085 // ----------------------------------------------------------------------------
6086
6087 void wxGrid::SetRowAttr(int row, wxGridCellAttr *attr)
6088 {
6089 if ( CanHaveAttributes() )
6090 {
6091 m_table->SetRowAttr(attr, row);
6092 }
6093 else
6094 {
6095 attr->SafeDecRef();
6096 }
6097 }
6098
6099 void wxGrid::SetColAttr(int col, wxGridCellAttr *attr)
6100 {
6101 if ( CanHaveAttributes() )
6102 {
6103 m_table->SetColAttr(attr, col);
6104 }
6105 else
6106 {
6107 attr->SafeDecRef();
6108 }
6109 }
6110
6111 void wxGrid::SetCellBackgroundColour( int row, int col, const wxColour& colour )
6112 {
6113 if ( CanHaveAttributes() )
6114 {
6115 wxGridCellAttr *attr = GetOrCreateCellAttr(row, col);
6116 attr->SetBackgroundColour(colour);
6117 attr->DecRef();
6118 }
6119 }
6120
6121 void wxGrid::SetCellTextColour( int row, int col, const wxColour& colour )
6122 {
6123 if ( CanHaveAttributes() )
6124 {
6125 wxGridCellAttr *attr = GetOrCreateCellAttr(row, col);
6126 attr->SetTextColour(colour);
6127 attr->DecRef();
6128 }
6129 }
6130
6131 void wxGrid::SetCellFont( int row, int col, const wxFont& font )
6132 {
6133 if ( CanHaveAttributes() )
6134 {
6135 wxGridCellAttr *attr = GetOrCreateCellAttr(row, col);
6136 attr->SetFont(font);
6137 attr->DecRef();
6138 }
6139 }
6140
6141 void wxGrid::SetCellAlignment( int row, int col, int horiz, int vert )
6142 {
6143 if ( CanHaveAttributes() )
6144 {
6145 wxGridCellAttr *attr = GetOrCreateCellAttr(row, col);
6146 attr->SetAlignment(horiz, vert);
6147 attr->DecRef();
6148 }
6149 }
6150
6151 void wxGrid::SetCellRenderer(int row, int col, wxGridCellRenderer *renderer)
6152 {
6153 if ( CanHaveAttributes() )
6154 {
6155 wxGridCellAttr *attr = GetOrCreateCellAttr(row, col);
6156 attr->SetRenderer(renderer);
6157 attr->DecRef();
6158 }
6159 }
6160
6161 void wxGrid::SetCellEditor(int row, int col, wxGridCellEditor* editor)
6162 {
6163 if ( CanHaveAttributes() )
6164 {
6165 wxGridCellAttr *attr = GetOrCreateCellAttr(row, col);
6166 attr->SetEditor(editor);
6167 attr->DecRef();
6168 }
6169 }
6170
6171 void wxGrid::SetReadOnly(int row, int col, bool isReadOnly)
6172 {
6173 if ( CanHaveAttributes() )
6174 {
6175 wxGridCellAttr *attr = GetOrCreateCellAttr(row, col);
6176 attr->SetReadOnly(isReadOnly);
6177 attr->DecRef();
6178 }
6179 }
6180
6181 // ----------------------------------------------------------------------------
6182 // Data type registration
6183 // ----------------------------------------------------------------------------
6184
6185 void wxGrid::RegisterDataType(const wxString& typeName,
6186 wxGridCellRenderer* renderer,
6187 wxGridCellEditor* editor)
6188 {
6189 m_typeRegistry->RegisterDataType(typeName, renderer, editor);
6190 }
6191
6192
6193 wxGridCellEditor* wxGrid::GetDefaultEditorForCell(int row, int col)
6194 {
6195 wxString typeName = m_table->GetTypeName(row, col);
6196 return GetDefaultEditorForType(typeName);
6197 }
6198
6199 wxGridCellRenderer* wxGrid::GetDefaultRendererForCell(int row, int col)
6200 {
6201 wxString typeName = m_table->GetTypeName(row, col);
6202 return GetDefaultRendererForType(typeName);
6203 }
6204
6205 wxGridCellEditor* wxGrid::GetDefaultEditorForType(const wxString& typeName)
6206 {
6207 int index = m_typeRegistry->FindDataType(typeName);
6208 if (index == -1) {
6209 // Should we force the failure here or let it fallback to string handling???
6210 // wxFAIL_MSG(wxT("Unknown data type name"));
6211 return NULL;
6212 }
6213 return m_typeRegistry->GetEditor(index);
6214 }
6215
6216 wxGridCellRenderer* wxGrid::GetDefaultRendererForType(const wxString& typeName)
6217 {
6218 int index = m_typeRegistry->FindDataType(typeName);
6219 if (index == -1) {
6220 // Should we force the failure here or let it fallback to string handling???
6221 // wxFAIL_MSG(wxT("Unknown data type name"));
6222 return NULL;
6223 }
6224 return m_typeRegistry->GetRenderer(index);
6225 }
6226
6227
6228 // ----------------------------------------------------------------------------
6229 // row/col size
6230 // ----------------------------------------------------------------------------
6231
6232 void wxGrid::EnableDragRowSize( bool enable )
6233 {
6234 m_canDragRowSize = enable;
6235 }
6236
6237
6238 void wxGrid::EnableDragColSize( bool enable )
6239 {
6240 m_canDragColSize = enable;
6241 }
6242
6243
6244 void wxGrid::SetDefaultRowSize( int height, bool resizeExistingRows )
6245 {
6246 m_defaultRowHeight = wxMax( height, WXGRID_MIN_ROW_HEIGHT );
6247
6248 if ( resizeExistingRows )
6249 {
6250 InitRowHeights();
6251
6252 CalcDimensions();
6253 }
6254 }
6255
6256 void wxGrid::SetRowSize( int row, int height )
6257 {
6258 wxCHECK_RET( row >= 0 && row < m_numRows, _T("invalid row index") );
6259
6260 if ( m_rowHeights.IsEmpty() )
6261 {
6262 // need to really create the array
6263 InitRowHeights();
6264 }
6265
6266 int h = wxMax( 0, height );
6267 int diff = h - m_rowHeights[row];
6268
6269 m_rowHeights[row] = h;
6270 int i;
6271 for ( i = row; i < m_numRows; i++ )
6272 {
6273 m_rowBottoms[i] += diff;
6274 }
6275 CalcDimensions();
6276 }
6277
6278 void wxGrid::SetDefaultColSize( int width, bool resizeExistingCols )
6279 {
6280 m_defaultColWidth = wxMax( width, WXGRID_MIN_COL_WIDTH );
6281
6282 if ( resizeExistingCols )
6283 {
6284 InitColWidths();
6285
6286 CalcDimensions();
6287 }
6288 }
6289
6290 void wxGrid::SetColSize( int col, int width )
6291 {
6292 wxCHECK_RET( col >= 0 && col < m_numCols, _T("invalid column index") );
6293
6294 // should we check that it's bigger than GetColMinimalWidth(col) here?
6295
6296 if ( m_colWidths.IsEmpty() )
6297 {
6298 // need to really create the array
6299 InitColWidths();
6300 }
6301
6302 int w = wxMax( 0, width );
6303 int diff = w - m_colWidths[col];
6304 m_colWidths[col] = w;
6305
6306 int i;
6307 for ( i = col; i < m_numCols; i++ )
6308 {
6309 m_colRights[i] += diff;
6310 }
6311 CalcDimensions();
6312 }
6313
6314
6315 void wxGrid::SetColMinimalWidth( int col, int width )
6316 {
6317 m_colMinWidths.Put(col, (wxObject *)width);
6318 }
6319
6320 int wxGrid::GetColMinimalWidth(int col) const
6321 {
6322 wxObject *obj = m_colMinWidths.Get(m_dragRowOrCol);
6323 return obj ? (int)obj : WXGRID_MIN_COL_WIDTH;
6324 }
6325
6326 //
6327 // ------ cell value accessor functions
6328 //
6329
6330 void wxGrid::SetCellValue( int row, int col, const wxString& s )
6331 {
6332 if ( m_table )
6333 {
6334 m_table->SetValue( row, col, s.c_str() );
6335 if ( !GetBatchCount() )
6336 {
6337 wxClientDC dc( m_gridWin );
6338 PrepareDC( dc );
6339 DrawCell( dc, wxGridCellCoords(row, col) );
6340 }
6341
6342 #if 0 // TODO: edit in place
6343
6344 if ( m_currentCellCoords.GetRow() == row &&
6345 m_currentCellCoords.GetCol() == col )
6346 {
6347 SetEditControlValue( s );
6348 }
6349 #endif
6350
6351 }
6352 }
6353
6354
6355 //
6356 // ------ Block, row and col selection
6357 //
6358
6359 void wxGrid::SelectRow( int row, bool addToSelected )
6360 {
6361 wxRect r;
6362
6363 if ( IsSelection() && addToSelected )
6364 {
6365 wxRect rect[4];
6366 bool need_refresh[4];
6367 need_refresh[0] =
6368 need_refresh[1] =
6369 need_refresh[2] =
6370 need_refresh[3] = FALSE;
6371
6372 int i;
6373
6374 wxCoord oldLeft = m_selectedTopLeft.GetCol();
6375 wxCoord oldTop = m_selectedTopLeft.GetRow();
6376 wxCoord oldRight = m_selectedBottomRight.GetCol();
6377 wxCoord oldBottom = m_selectedBottomRight.GetRow();
6378
6379 if ( oldTop > row )
6380 {
6381 need_refresh[0] = TRUE;
6382 rect[0] = BlockToDeviceRect( wxGridCellCoords ( row, 0 ),
6383 wxGridCellCoords ( oldTop - 1,
6384 m_numCols - 1 ) );
6385 m_selectedTopLeft.SetRow( row );
6386 }
6387
6388 if ( oldLeft > 0 )
6389 {
6390 need_refresh[1] = TRUE;
6391 rect[1] = BlockToDeviceRect( wxGridCellCoords ( oldTop, 0 ),
6392 wxGridCellCoords ( oldBottom,
6393 oldLeft - 1 ) );
6394
6395 m_selectedTopLeft.SetCol( 0 );
6396 }
6397
6398 if ( oldBottom < row )
6399 {
6400 need_refresh[2] = TRUE;
6401 rect[2] = BlockToDeviceRect( wxGridCellCoords ( oldBottom + 1, 0 ),
6402 wxGridCellCoords ( row,
6403 m_numCols - 1 ) );
6404 m_selectedBottomRight.SetRow( row );
6405 }
6406
6407 if ( oldRight < m_numCols - 1 )
6408 {
6409 need_refresh[3] = TRUE;
6410 rect[3] = BlockToDeviceRect( wxGridCellCoords ( oldTop ,
6411 oldRight + 1 ),
6412 wxGridCellCoords ( oldBottom,
6413 m_numCols - 1 ) );
6414 m_selectedBottomRight.SetCol( m_numCols - 1 );
6415 }
6416
6417 for (i = 0; i < 4; i++ )
6418 if ( need_refresh[i] && rect[i] != wxGridNoCellRect )
6419 m_gridWin->Refresh( FALSE, &(rect[i]) );
6420 }
6421 else
6422 {
6423 r = SelectionToDeviceRect();
6424 ClearSelection();
6425 if ( r != wxGridNoCellRect ) m_gridWin->Refresh( FALSE, &r );
6426
6427 m_selectedTopLeft.Set( row, 0 );
6428 m_selectedBottomRight.Set( row, m_numCols-1 );
6429 r = SelectionToDeviceRect();
6430 m_gridWin->Refresh( FALSE, &r );
6431 }
6432
6433 wxGridRangeSelectEvent gridEvt( GetId(),
6434 wxEVT_GRID_RANGE_SELECT,
6435 this,
6436 m_selectedTopLeft,
6437 m_selectedBottomRight );
6438
6439 GetEventHandler()->ProcessEvent(gridEvt);
6440 }
6441
6442
6443 void wxGrid::SelectCol( int col, bool addToSelected )
6444 {
6445 if ( IsSelection() && addToSelected )
6446 {
6447 wxRect rect[4];
6448 bool need_refresh[4];
6449 need_refresh[0] =
6450 need_refresh[1] =
6451 need_refresh[2] =
6452 need_refresh[3] = FALSE;
6453 int i;
6454
6455 wxCoord oldLeft = m_selectedTopLeft.GetCol();
6456 wxCoord oldTop = m_selectedTopLeft.GetRow();
6457 wxCoord oldRight = m_selectedBottomRight.GetCol();
6458 wxCoord oldBottom = m_selectedBottomRight.GetRow();
6459
6460 if ( oldLeft > col )
6461 {
6462 need_refresh[0] = TRUE;
6463 rect[0] = BlockToDeviceRect( wxGridCellCoords ( 0, col ),
6464 wxGridCellCoords ( m_numRows - 1,
6465 oldLeft - 1 ) );
6466 m_selectedTopLeft.SetCol( col );
6467 }
6468
6469 if ( oldTop > 0 )
6470 {
6471 need_refresh[1] = TRUE;
6472 rect[1] = BlockToDeviceRect( wxGridCellCoords ( 0, oldLeft ),
6473 wxGridCellCoords ( oldTop - 1,
6474 oldRight ) );
6475 m_selectedTopLeft.SetRow( 0 );
6476 }
6477
6478 if ( oldRight < col )
6479 {
6480 need_refresh[2] = TRUE;
6481 rect[2] = BlockToDeviceRect( wxGridCellCoords ( 0, oldRight + 1 ),
6482 wxGridCellCoords ( m_numRows - 1,
6483 col ) );
6484 m_selectedBottomRight.SetCol( col );
6485 }
6486
6487 if ( oldBottom < m_numRows - 1 )
6488 {
6489 need_refresh[3] = TRUE;
6490 rect[3] = BlockToDeviceRect( wxGridCellCoords ( oldBottom + 1,
6491 oldLeft ),
6492 wxGridCellCoords ( m_numRows - 1,
6493 oldRight ) );
6494 m_selectedBottomRight.SetRow( m_numRows - 1 );
6495 }
6496
6497 for (i = 0; i < 4; i++ )
6498 if ( need_refresh[i] && rect[i] != wxGridNoCellRect )
6499 m_gridWin->Refresh( FALSE, &(rect[i]) );
6500 }
6501 else
6502 {
6503 wxRect r;
6504
6505 r = SelectionToDeviceRect();
6506 ClearSelection();
6507 if ( r != wxGridNoCellRect ) m_gridWin->Refresh( FALSE, &r );
6508
6509 m_selectedTopLeft.Set( 0, col );
6510 m_selectedBottomRight.Set( m_numRows-1, col );
6511 r = SelectionToDeviceRect();
6512 m_gridWin->Refresh( FALSE, &r );
6513 }
6514
6515 wxGridRangeSelectEvent gridEvt( GetId(),
6516 wxEVT_GRID_RANGE_SELECT,
6517 this,
6518 m_selectedTopLeft,
6519 m_selectedBottomRight );
6520
6521 GetEventHandler()->ProcessEvent(gridEvt);
6522 }
6523
6524
6525 void wxGrid::SelectBlock( int topRow, int leftCol, int bottomRow, int rightCol )
6526 {
6527 int temp;
6528 wxGridCellCoords updateTopLeft, updateBottomRight;
6529
6530 if ( topRow > bottomRow )
6531 {
6532 temp = topRow;
6533 topRow = bottomRow;
6534 bottomRow = temp;
6535 }
6536
6537 if ( leftCol > rightCol )
6538 {
6539 temp = leftCol;
6540 leftCol = rightCol;
6541 rightCol = temp;
6542 }
6543
6544 updateTopLeft = wxGridCellCoords( topRow, leftCol );
6545 updateBottomRight = wxGridCellCoords( bottomRow, rightCol );
6546
6547 if ( m_selectedTopLeft != updateTopLeft ||
6548 m_selectedBottomRight != updateBottomRight )
6549 {
6550 // Compute two optimal update rectangles:
6551 // Either one rectangle is a real subset of the
6552 // other, or they are (almost) disjoint!
6553 wxRect rect[4];
6554 bool need_refresh[4];
6555 need_refresh[0] =
6556 need_refresh[1] =
6557 need_refresh[2] =
6558 need_refresh[3] = FALSE;
6559 int i;
6560
6561 // Store intermediate values
6562 wxCoord oldLeft = m_selectedTopLeft.GetCol();
6563 wxCoord oldTop = m_selectedTopLeft.GetRow();
6564 wxCoord oldRight = m_selectedBottomRight.GetCol();
6565 wxCoord oldBottom = m_selectedBottomRight.GetRow();
6566
6567 // Determine the outer/inner coordinates.
6568 if (oldLeft > leftCol)
6569 {
6570 temp = oldLeft;
6571 oldLeft = leftCol;
6572 leftCol = temp;
6573 }
6574 if (oldTop > topRow )
6575 {
6576 temp = oldTop;
6577 oldTop = topRow;
6578 topRow = temp;
6579 }
6580 if (oldRight < rightCol )
6581 {
6582 temp = oldRight;
6583 oldRight = rightCol;
6584 rightCol = temp;
6585 }
6586 if (oldBottom < bottomRow)
6587 {
6588 temp = oldBottom;
6589 oldBottom = bottomRow;
6590 bottomRow = temp;
6591 }
6592
6593 // Now, either the stuff marked old is the outer
6594 // rectangle or we don't have a situation where one
6595 // is contained in the other.
6596
6597 if ( oldLeft < leftCol )
6598 {
6599 need_refresh[0] = TRUE;
6600 rect[0] = BlockToDeviceRect( wxGridCellCoords ( oldTop,
6601 oldLeft ),
6602 wxGridCellCoords ( oldBottom,
6603 leftCol - 1 ) );
6604 }
6605
6606 if ( oldTop < topRow )
6607 {
6608 need_refresh[1] = TRUE;
6609 rect[1] = BlockToDeviceRect( wxGridCellCoords ( oldTop,
6610 leftCol ),
6611 wxGridCellCoords ( topRow - 1,
6612 rightCol ) );
6613 }
6614
6615 if ( oldRight > rightCol )
6616 {
6617 need_refresh[2] = TRUE;
6618 rect[2] = BlockToDeviceRect( wxGridCellCoords ( oldTop,
6619 rightCol + 1 ),
6620 wxGridCellCoords ( oldBottom,
6621 oldRight ) );
6622 }
6623
6624 if ( oldBottom > bottomRow )
6625 {
6626 need_refresh[3] = TRUE;
6627 rect[3] = BlockToDeviceRect( wxGridCellCoords ( bottomRow + 1,
6628 leftCol ),
6629 wxGridCellCoords ( oldBottom,
6630 rightCol ) );
6631 }
6632
6633
6634 // Change Selection
6635 m_selectedTopLeft = updateTopLeft;
6636 m_selectedBottomRight = updateBottomRight;
6637
6638 // various Refresh() calls
6639 for (i = 0; i < 4; i++ )
6640 if ( need_refresh[i] && rect[i] != wxGridNoCellRect )
6641 m_gridWin->Refresh( FALSE, &(rect[i]) );
6642 }
6643
6644 // only generate an event if the block is not being selected by
6645 // dragging the mouse (in which case the event will be generated in
6646 // the mouse event handler)
6647 if ( !m_isDragging )
6648 {
6649 wxGridRangeSelectEvent gridEvt( GetId(),
6650 wxEVT_GRID_RANGE_SELECT,
6651 this,
6652 m_selectedTopLeft,
6653 m_selectedBottomRight );
6654
6655 GetEventHandler()->ProcessEvent(gridEvt);
6656 }
6657 }
6658
6659 void wxGrid::SelectAll()
6660 {
6661 m_selectedTopLeft.Set( 0, 0 );
6662 m_selectedBottomRight.Set( m_numRows-1, m_numCols-1 );
6663
6664 m_gridWin->Refresh();
6665 }
6666
6667
6668 void wxGrid::ClearSelection()
6669 {
6670 m_selectedTopLeft = wxGridNoCellCoords;
6671 m_selectedBottomRight = wxGridNoCellCoords;
6672 }
6673
6674
6675 // This function returns the rectangle that encloses the given block
6676 // in device coords clipped to the client size of the grid window.
6677 //
6678 wxRect wxGrid::BlockToDeviceRect( const wxGridCellCoords &topLeft,
6679 const wxGridCellCoords &bottomRight )
6680 {
6681 wxRect rect( wxGridNoCellRect );
6682 wxRect cellRect;
6683
6684 cellRect = CellToRect( topLeft );
6685 if ( cellRect != wxGridNoCellRect )
6686 {
6687 rect = cellRect;
6688 }
6689 else
6690 {
6691 rect = wxRect( 0, 0, 0, 0 );
6692 }
6693
6694 cellRect = CellToRect( bottomRight );
6695 if ( cellRect != wxGridNoCellRect )
6696 {
6697 rect += cellRect;
6698 }
6699 else
6700 {
6701 return wxGridNoCellRect;
6702 }
6703
6704 // convert to scrolled coords
6705 //
6706 int left, top, right, bottom;
6707 CalcScrolledPosition( rect.GetLeft(), rect.GetTop(), &left, &top );
6708 CalcScrolledPosition( rect.GetRight(), rect.GetBottom(), &right, &bottom );
6709
6710 int cw, ch;
6711 m_gridWin->GetClientSize( &cw, &ch );
6712
6713 rect.SetLeft( wxMax(0, left) );
6714 rect.SetTop( wxMax(0, top) );
6715 rect.SetRight( wxMin(cw, right) );
6716 rect.SetBottom( wxMin(ch, bottom) );
6717
6718 return rect;
6719 }
6720
6721
6722
6723 //
6724 // ------ Grid event classes
6725 //
6726
6727 IMPLEMENT_DYNAMIC_CLASS( wxGridEvent, wxEvent )
6728
6729 wxGridEvent::wxGridEvent( int id, wxEventType type, wxObject* obj,
6730 int row, int col, int x, int y,
6731 bool control, bool shift, bool alt, bool meta )
6732 : wxNotifyEvent( type, id )
6733 {
6734 m_row = row;
6735 m_col = col;
6736 m_x = x;
6737 m_y = y;
6738 m_control = control;
6739 m_shift = shift;
6740 m_alt = alt;
6741 m_meta = meta;
6742
6743 SetEventObject(obj);
6744 }
6745
6746
6747 IMPLEMENT_DYNAMIC_CLASS( wxGridSizeEvent, wxEvent )
6748
6749 wxGridSizeEvent::wxGridSizeEvent( int id, wxEventType type, wxObject* obj,
6750 int rowOrCol, int x, int y,
6751 bool control, bool shift, bool alt, bool meta )
6752 : wxNotifyEvent( type, id )
6753 {
6754 m_rowOrCol = rowOrCol;
6755 m_x = x;
6756 m_y = y;
6757 m_control = control;
6758 m_shift = shift;
6759 m_alt = alt;
6760 m_meta = meta;
6761
6762 SetEventObject(obj);
6763 }
6764
6765
6766 IMPLEMENT_DYNAMIC_CLASS( wxGridRangeSelectEvent, wxEvent )
6767
6768 wxGridRangeSelectEvent::wxGridRangeSelectEvent(int id, wxEventType type, wxObject* obj,
6769 const wxGridCellCoords& topLeft,
6770 const wxGridCellCoords& bottomRight,
6771 bool control, bool shift, bool alt, bool meta )
6772 : wxNotifyEvent( type, id )
6773 {
6774 m_topLeft = topLeft;
6775 m_bottomRight = bottomRight;
6776 m_control = control;
6777 m_shift = shift;
6778 m_alt = alt;
6779 m_meta = meta;
6780
6781 SetEventObject(obj);
6782 }
6783
6784
6785 #endif // ifndef wxUSE_NEW_GRID
6786