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