1. Initialized m_displayed to TRUE to solve a cell highlight problem
[wxWidgets.git] / src / generic / grid.cpp
1 ///////////////////////////////////////////////////////////////////////////
2 // Name: grid.cpp
3 // Purpose: wxGrid and related classes
4 // Author: Michael Bedward (based on code by Julian Smart, Robin Dunn)
5 // Modified by:
6 // Created: 1/08/1999
7 // RCS-ID: $Id$
8 // Copyright: (c) Michael Bedward (mbedward@ozemail.com.au)
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
11
12 // ============================================================================
13 // declarations
14 // ============================================================================
15
16 // ----------------------------------------------------------------------------
17 // headers
18 // ----------------------------------------------------------------------------
19
20 #ifdef __GNUG__
21 #pragma implementation "grid.h"
22 #endif
23
24 // For compilers that support precompilation, includes "wx/wx.h".
25 #include "wx/wxprec.h"
26
27 #include "wx/defs.h"
28
29 #ifdef __BORLANDC__
30 #pragma hdrstop
31 #endif
32
33 #if !defined(wxUSE_NEW_GRID) || !(wxUSE_NEW_GRID)
34 #include "gridg.cpp"
35 #else
36
37 #ifndef WX_PRECOMP
38 #include "wx/utils.h"
39 #include "wx/dcclient.h"
40 #include "wx/settings.h"
41 #include "wx/log.h"
42 #endif
43
44 // this include needs to be outside precomp for BCC
45 #include "wx/textfile.h"
46
47 #include "wx/grid.h"
48
49
50 // ----------------------------------------------------------------------------
51 // array classes
52 // ----------------------------------------------------------------------------
53
54 WX_DEFINE_ARRAY(wxGridCellAttr *, wxArrayAttrs);
55
56 struct wxGridCellWithAttr
57 {
58 wxGridCellWithAttr(int row, int col, wxGridCellAttr *attr_)
59 : coords(row, col), attr(attr_)
60 {
61 }
62
63 ~wxGridCellWithAttr()
64 {
65 attr->DecRef();
66 }
67
68 wxGridCellCoords coords;
69 wxGridCellAttr *attr;
70 };
71
72 WX_DECLARE_OBJARRAY(wxGridCellWithAttr, wxGridCellWithAttrArray);
73
74 #include "wx/arrimpl.cpp"
75
76 WX_DEFINE_OBJARRAY(wxGridCellCoordsArray)
77 WX_DEFINE_OBJARRAY(wxGridCellWithAttrArray)
78
79 // ----------------------------------------------------------------------------
80 // private classes
81 // ----------------------------------------------------------------------------
82
83 class WXDLLEXPORT wxGridRowLabelWindow : public wxWindow
84 {
85 public:
86 wxGridRowLabelWindow() { m_owner = (wxGrid *)NULL; }
87 wxGridRowLabelWindow( wxGrid *parent, wxWindowID id,
88 const wxPoint &pos, const wxSize &size );
89
90 private:
91 wxGrid *m_owner;
92
93 void OnPaint( wxPaintEvent& event );
94 void OnMouseEvent( wxMouseEvent& event );
95 void OnKeyDown( wxKeyEvent& event );
96
97 DECLARE_DYNAMIC_CLASS(wxGridRowLabelWindow)
98 DECLARE_EVENT_TABLE()
99 };
100
101
102 class WXDLLEXPORT wxGridColLabelWindow : public wxWindow
103 {
104 public:
105 wxGridColLabelWindow() { m_owner = (wxGrid *)NULL; }
106 wxGridColLabelWindow( wxGrid *parent, wxWindowID id,
107 const wxPoint &pos, const wxSize &size );
108
109 private:
110 wxGrid *m_owner;
111
112 void OnPaint( wxPaintEvent &event );
113 void OnMouseEvent( wxMouseEvent& event );
114 void OnKeyDown( wxKeyEvent& event );
115
116 DECLARE_DYNAMIC_CLASS(wxGridColLabelWindow)
117 DECLARE_EVENT_TABLE()
118 };
119
120
121 class WXDLLEXPORT wxGridCornerLabelWindow : public wxWindow
122 {
123 public:
124 wxGridCornerLabelWindow() { m_owner = (wxGrid *)NULL; }
125 wxGridCornerLabelWindow( wxGrid *parent, wxWindowID id,
126 const wxPoint &pos, const wxSize &size );
127
128 private:
129 wxGrid *m_owner;
130
131 void OnMouseEvent( wxMouseEvent& event );
132 void OnKeyDown( wxKeyEvent& event );
133 void OnPaint( wxPaintEvent& event );
134
135 DECLARE_DYNAMIC_CLASS(wxGridCornerLabelWindow)
136 DECLARE_EVENT_TABLE()
137 };
138
139 class WXDLLEXPORT wxGridWindow : public wxPanel
140 {
141 public:
142 wxGridWindow()
143 {
144 m_owner = (wxGrid *)NULL;
145 m_rowLabelWin = (wxGridRowLabelWindow *)NULL;
146 m_colLabelWin = (wxGridColLabelWindow *)NULL;
147 }
148
149 wxGridWindow( wxGrid *parent,
150 wxGridRowLabelWindow *rowLblWin,
151 wxGridColLabelWindow *colLblWin,
152 wxWindowID id, const wxPoint &pos, const wxSize &size );
153 ~wxGridWindow();
154
155 void ScrollWindow( int dx, int dy, const wxRect *rect );
156
157 private:
158 wxGrid *m_owner;
159 wxGridRowLabelWindow *m_rowLabelWin;
160 wxGridColLabelWindow *m_colLabelWin;
161
162 void OnPaint( wxPaintEvent &event );
163 void OnMouseEvent( wxMouseEvent& event );
164 void OnKeyDown( wxKeyEvent& );
165 void OnEraseBackground( wxEraseEvent& );
166
167
168 DECLARE_DYNAMIC_CLASS(wxGridWindow)
169 DECLARE_EVENT_TABLE()
170 };
171
172
173
174 class wxGridCellEditorEvtHandler : public wxEvtHandler
175 {
176 public:
177 wxGridCellEditorEvtHandler()
178 : m_grid(0), m_editor(0)
179 { }
180 wxGridCellEditorEvtHandler(wxGrid* grid, wxGridCellEditor* editor)
181 : m_grid(grid), m_editor(editor)
182 { }
183
184 void OnKeyDown(wxKeyEvent& event);
185
186 private:
187 wxGrid* m_grid;
188 wxGridCellEditor* m_editor;
189 DECLARE_DYNAMIC_CLASS(wxGridCellEditorEvtHandler)
190 DECLARE_EVENT_TABLE()
191 };
192
193
194 IMPLEMENT_DYNAMIC_CLASS( wxGridCellEditorEvtHandler, wxEvtHandler )
195 BEGIN_EVENT_TABLE( wxGridCellEditorEvtHandler, wxEvtHandler )
196 EVT_KEY_DOWN( wxGridCellEditorEvtHandler::OnKeyDown )
197 END_EVENT_TABLE()
198
199
200
201 // ----------------------------------------------------------------------------
202 // the internal data representation used by wxGridCellAttrProvider
203 // ----------------------------------------------------------------------------
204
205 // this class stores attributes set for cells
206 class WXDLLEXPORT wxGridCellAttrData
207 {
208 public:
209 void SetAttr(wxGridCellAttr *attr, int row, int col);
210 wxGridCellAttr *GetAttr(int row, int col) const;
211
212 private:
213 // searches for the attr for given cell, returns wxNOT_FOUND if not found
214 int FindIndex(int row, int col) const;
215
216 wxGridCellWithAttrArray m_attrs;
217 };
218
219 // this class stores attributes set for rows or columns
220 class WXDLLEXPORT wxGridRowOrColAttrData
221 {
222 public:
223 ~wxGridRowOrColAttrData();
224
225 void SetAttr(wxGridCellAttr *attr, int rowOrCol);
226 wxGridCellAttr *GetAttr(int rowOrCol) const;
227
228 private:
229 wxArrayInt m_rowsOrCols;
230 wxArrayAttrs m_attrs;
231 };
232
233 // NB: this is just a wrapper around 3 objects: one which stores cell
234 // attributes, and 2 others for row/col ones
235 class WXDLLEXPORT wxGridCellAttrProviderData
236 {
237 public:
238 wxGridCellAttrData m_cellAttrs;
239 wxGridRowOrColAttrData m_rowAttrs,
240 m_colAttrs;
241 };
242
243 // ----------------------------------------------------------------------------
244 // conditional compilation
245 // ----------------------------------------------------------------------------
246
247 #ifndef WXGRID_DRAW_LINES
248 #define WXGRID_DRAW_LINES 1
249 #endif
250
251 // ----------------------------------------------------------------------------
252 // globals
253 // ----------------------------------------------------------------------------
254
255 //#define DEBUG_ATTR_CACHE
256 #ifdef DEBUG_ATTR_CACHE
257 static size_t gs_nAttrCacheHits = 0;
258 static size_t gs_nAttrCacheMisses = 0;
259 #endif // DEBUG_ATTR_CACHE
260
261 wxGridCellCoords wxGridNoCellCoords( -1, -1 );
262 wxRect wxGridNoCellRect( -1, -1, -1, -1 );
263
264 // scroll line size
265 // TODO: fixed so far - make configurable later (and also different for x/y)
266 static const size_t GRID_SCROLL_LINE = 10;
267
268 // ============================================================================
269 // implementation
270 // ============================================================================
271
272
273 // ----------------------------------------------------------------------------
274 // wxGridCellEditor
275 // ----------------------------------------------------------------------------
276
277 wxGridCellEditor::wxGridCellEditor()
278 {
279 m_control = NULL;
280 }
281
282
283 wxGridCellEditor::~wxGridCellEditor()
284 {
285 Destroy();
286 }
287
288
289 void wxGridCellEditor::Destroy()
290 {
291 if (m_control) {
292 m_control->Destroy();
293 m_control = NULL;
294 }
295 }
296
297 void wxGridCellEditor::Show(bool show)
298 {
299 wxASSERT_MSG(m_control,
300 wxT("The wxGridCellEditor must be Created first!"));
301 m_control->Show(show);
302 }
303
304 void wxGridCellEditor::SetSize(const wxRect& rect)
305 {
306 wxASSERT_MSG(m_control,
307 wxT("The wxGridCellEditor must be Created first!"));
308 m_control->SetSize(rect);
309 }
310
311 void wxGridCellEditor::HandleReturn(wxKeyEvent& event)
312 {
313 event.Skip();
314 }
315
316
317 void wxGridCellEditor::StartingKey(wxKeyEvent& event)
318 {
319 }
320
321
322
323 wxGridCellTextEditor::wxGridCellTextEditor()
324 {
325 }
326
327 void wxGridCellTextEditor::Create(wxWindow* parent,
328 wxWindowID id,
329 wxEvtHandler* evtHandler)
330 {
331 m_control = new wxTextCtrl(parent, -1, "",
332 wxDefaultPosition, wxDefaultSize
333 #if defined(__WXMSW__)
334 , wxTE_MULTILINE | wxTE_NO_VSCROLL // necessary ???
335 #endif
336 );
337
338 if (evtHandler)
339 m_control->PushEventHandler(evtHandler);
340 }
341
342
343 void wxGridCellTextEditor::BeginEdit(int row, int col, wxGrid* grid,
344 wxGridCellAttr* attr)
345 {
346 wxASSERT_MSG(m_control,
347 wxT("The wxGridCellEditor must be Created first!"));
348
349 m_startValue = grid->GetTable()->GetValue(row, col);
350 ((wxTextCtrl*)m_control)->SetValue(m_startValue);
351 ((wxTextCtrl*)m_control)->SetInsertionPointEnd();
352 ((wxTextCtrl*)m_control)->SetFocus();
353
354 // ??? Should we use attr and try to set colours and font?
355 }
356
357
358
359 bool wxGridCellTextEditor::EndEdit(int row, int col, bool saveValue,
360 wxGrid* grid, wxGridCellAttr* attr)
361 {
362 wxASSERT_MSG(m_control,
363 wxT("The wxGridCellEditor must be Created first!"));
364
365 bool changed = FALSE;
366 wxString value = ((wxTextCtrl*)m_control)->GetValue();
367 if (value != m_startValue)
368 changed = TRUE;
369
370 if (changed)
371 grid->GetTable()->SetValue(row, col, value);
372
373 m_startValue = "";
374 ((wxTextCtrl*)m_control)->SetValue(m_startValue);
375
376 return changed;
377 }
378
379
380 void wxGridCellTextEditor::Reset()
381 {
382 wxASSERT_MSG(m_control,
383 wxT("The wxGridCellEditor must be Created first!"));
384
385 ((wxTextCtrl*)m_control)->SetValue(m_startValue);
386 ((wxTextCtrl*)m_control)->SetInsertionPointEnd();
387 }
388
389
390 void wxGridCellTextEditor::StartingKey(wxKeyEvent& event)
391 {
392 wxASSERT_MSG(m_control,
393 wxT("The wxGridCellEditor must be Created first!"));
394
395 int code = event.KeyCode();
396 if (code >= 32 && code < 255) {
397 wxString st((char)code);
398 if (! event.ShiftDown())
399 st.LowerCase();
400 ((wxTextCtrl*)m_control)->AppendText(st);
401 }
402 }
403
404
405 void wxGridCellTextEditor::HandleReturn(wxKeyEvent& event)
406 {
407 #if defined(__WXMOTIF__) || defined(__WXGTK__)
408 // wxMotif needs a little extra help...
409 int pos = ((wxTextCtrl*)m_control)->GetInsertionPoint();
410 wxString s( ((wxTextCtrl*)m_control)->GetValue() );
411 s = s.Left(pos) + "\n" + s.Mid(pos);
412 ((wxTextCtrl*)m_control)->SetValue(s);
413 ((wxTextCtrl*)m_control)->SetInsertionPoint( pos );
414 #else
415 // the other ports can handle a Return key press
416 //
417 event.Skip();
418 #endif
419 }
420
421
422 void wxGridCellEditorEvtHandler::OnKeyDown(wxKeyEvent& event)
423 {
424 switch ( event.KeyCode() )
425 {
426 case WXK_ESCAPE:
427 m_editor->Reset();
428 m_grid->EnableCellEditControl(FALSE);
429 break;
430
431 // case WXK_UP:
432 // case WXK_DOWN:
433 // case WXK_LEFT:
434 // case WXK_RIGHT:
435 // case WXK_PRIOR:
436 // case WXK_NEXT:
437 // case WXK_SPACE:
438 // case WXK_HOME:
439 // case WXK_END:
440 // // send the event to the parent grid, skipping the
441 // // event if nothing happens
442 // //
443 // event.Skip( m_grid->ProcessEvent( event ) );
444 // break;
445
446 case WXK_TAB:
447 case WXK_RETURN:
448 if (!m_grid->ProcessEvent(event))
449 m_editor->HandleReturn(event);
450 break;
451
452
453 default:
454 event.Skip();
455 }
456 }
457
458 // ----------------------------------------------------------------------------
459 // wxGridCellRenderer
460 // ----------------------------------------------------------------------------
461
462 void wxGridCellRenderer::Draw(wxGrid& grid,
463 wxGridCellAttr& attr,
464 wxDC& dc,
465 const wxRect& rect,
466 int row, int col,
467 bool isSelected)
468 {
469 dc.SetBackgroundMode( wxSOLID );
470
471 if ( isSelected )
472 {
473 dc.SetBrush( wxBrush(grid.GetSelectionBackground(), wxSOLID) );
474 }
475 else
476 {
477 dc.SetBrush( wxBrush(attr.GetBackgroundColour(), wxSOLID) );
478 }
479
480 dc.SetPen( *wxTRANSPARENT_PEN );
481 dc.DrawRectangle(rect);
482 }
483
484 void wxGridCellStringRenderer::Draw(wxGrid& grid,
485 wxGridCellAttr& attr,
486 wxDC& dc,
487 const wxRect& rectCell,
488 int row, int col,
489 bool isSelected)
490 {
491 wxGridCellRenderer::Draw(grid, attr, dc, rectCell, row, col, isSelected);
492
493 // now we only have to draw the text
494 dc.SetBackgroundMode( wxTRANSPARENT );
495
496 if ( isSelected )
497 {
498 dc.SetTextBackground( grid.GetSelectionBackground() );
499 dc.SetTextForeground( grid.GetSelectionForeground() );
500 }
501 else
502 {
503 dc.SetTextBackground( attr.GetBackgroundColour() );
504 dc.SetTextForeground( attr.GetTextColour() );
505 }
506 dc.SetFont( attr.GetFont() );
507
508 int hAlign, vAlign;
509 attr.GetAlignment(&hAlign, &vAlign);
510
511 wxRect rect = rectCell;
512 rect.x++;
513 rect.y++;
514 rect.width -= 2;
515 rect.height -= 2;
516
517 grid.DrawTextRectangle(dc, grid.GetCellValue(row, col),
518 rect, hAlign, vAlign);
519 }
520
521 // ----------------------------------------------------------------------------
522 // wxGridCellAttr
523 // ----------------------------------------------------------------------------
524
525 const wxColour& wxGridCellAttr::GetTextColour() const
526 {
527 if (HasTextColour())
528 return m_colText;
529 else if (m_defGridAttr != this)
530 return m_defGridAttr->GetTextColour();
531 else {
532 wxFAIL_MSG(wxT("Missing default cell attribute"));
533 return wxNullColour;
534 }
535 }
536
537
538 const wxColour& wxGridCellAttr::GetBackgroundColour() const
539 {
540 if (HasBackgroundColour())
541 return m_colBack;
542 else if (m_defGridAttr != this)
543 return m_defGridAttr->GetBackgroundColour();
544 else {
545 wxFAIL_MSG(wxT("Missing default cell attribute"));
546 return wxNullColour;
547 }
548 }
549
550
551 const wxFont& wxGridCellAttr::GetFont() const
552 {
553 if (HasFont())
554 return m_font;
555 else if (m_defGridAttr != this)
556 return m_defGridAttr->GetFont();
557 else {
558 wxFAIL_MSG(wxT("Missing default cell attribute"));
559 return wxNullFont;
560 }
561 }
562
563
564 void wxGridCellAttr::GetAlignment(int *hAlign, int *vAlign) const
565 {
566 if (HasAlignment()) {
567 if ( hAlign ) *hAlign = m_hAlign;
568 if ( vAlign ) *vAlign = m_vAlign;
569 }
570 else if (m_defGridAttr != this)
571 m_defGridAttr->GetAlignment(hAlign, vAlign);
572 else {
573 wxFAIL_MSG(wxT("Missing default cell attribute"));
574 }
575 }
576
577
578 wxGridCellRenderer* wxGridCellAttr::GetRenderer() const
579 {
580 if (HasRenderer())
581 return m_renderer;
582 else if (m_defGridAttr != this)
583 return m_defGridAttr->GetRenderer();
584 else {
585 wxFAIL_MSG(wxT("Missing default cell attribute"));
586 return NULL;
587 }
588 }
589
590 wxGridCellEditor* wxGridCellAttr::GetEditor() const
591 {
592 if (HasEditor())
593 return m_editor;
594 else if (m_defGridAttr != this)
595 return m_defGridAttr->GetEditor();
596 else {
597 wxFAIL_MSG(wxT("Missing default cell attribute"));
598 return NULL;
599 }
600 }
601
602 // ----------------------------------------------------------------------------
603 // wxGridCellAttrData
604 // ----------------------------------------------------------------------------
605
606 void wxGridCellAttrData::SetAttr(wxGridCellAttr *attr, int row, int col)
607 {
608 int n = FindIndex(row, col);
609 if ( n == wxNOT_FOUND )
610 {
611 // add the attribute
612 m_attrs.Add(new wxGridCellWithAttr(row, col, attr));
613 }
614 else
615 {
616 if ( attr )
617 {
618 // change the attribute
619 m_attrs[(size_t)n].attr = attr;
620 }
621 else
622 {
623 // remove this attribute
624 m_attrs.RemoveAt((size_t)n);
625 }
626 }
627 }
628
629 wxGridCellAttr *wxGridCellAttrData::GetAttr(int row, int col) const
630 {
631 wxGridCellAttr *attr = (wxGridCellAttr *)NULL;
632
633 int n = FindIndex(row, col);
634 if ( n != wxNOT_FOUND )
635 {
636 attr = m_attrs[(size_t)n].attr;
637 attr->IncRef();
638 }
639
640 return attr;
641 }
642
643 int wxGridCellAttrData::FindIndex(int row, int col) const
644 {
645 size_t count = m_attrs.GetCount();
646 for ( size_t n = 0; n < count; n++ )
647 {
648 const wxGridCellCoords& coords = m_attrs[n].coords;
649 if ( (coords.GetRow() == row) && (coords.GetCol() == col) )
650 {
651 return n;
652 }
653 }
654
655 return wxNOT_FOUND;
656 }
657
658 // ----------------------------------------------------------------------------
659 // wxGridRowOrColAttrData
660 // ----------------------------------------------------------------------------
661
662 wxGridRowOrColAttrData::~wxGridRowOrColAttrData()
663 {
664 size_t count = m_attrs.Count();
665 for ( size_t n = 0; n < count; n++ )
666 {
667 m_attrs[n]->DecRef();
668 }
669 }
670
671 wxGridCellAttr *wxGridRowOrColAttrData::GetAttr(int rowOrCol) const
672 {
673 wxGridCellAttr *attr = (wxGridCellAttr *)NULL;
674
675 int n = m_rowsOrCols.Index(rowOrCol);
676 if ( n != wxNOT_FOUND )
677 {
678 attr = m_attrs[(size_t)n];
679 attr->IncRef();
680 }
681
682 return attr;
683 }
684
685 void wxGridRowOrColAttrData::SetAttr(wxGridCellAttr *attr, int rowOrCol)
686 {
687 int n = m_rowsOrCols.Index(rowOrCol);
688 if ( n == wxNOT_FOUND )
689 {
690 // add the attribute
691 m_rowsOrCols.Add(rowOrCol);
692 m_attrs.Add(attr);
693 }
694 else
695 {
696 if ( attr )
697 {
698 // change the attribute
699 m_attrs[(size_t)n] = attr;
700 }
701 else
702 {
703 // remove this attribute
704 m_attrs[(size_t)n]->DecRef();
705 m_rowsOrCols.RemoveAt((size_t)n);
706 m_attrs.RemoveAt((size_t)n);
707 }
708 }
709 }
710
711 // ----------------------------------------------------------------------------
712 // wxGridCellAttrProvider
713 // ----------------------------------------------------------------------------
714
715 wxGridCellAttrProvider::wxGridCellAttrProvider()
716 {
717 m_data = (wxGridCellAttrProviderData *)NULL;
718 }
719
720 wxGridCellAttrProvider::~wxGridCellAttrProvider()
721 {
722 delete m_data;
723 }
724
725 void wxGridCellAttrProvider::InitData()
726 {
727 m_data = new wxGridCellAttrProviderData;
728 }
729
730 wxGridCellAttr *wxGridCellAttrProvider::GetAttr(int row, int col) const
731 {
732 wxGridCellAttr *attr = (wxGridCellAttr *)NULL;
733 if ( m_data )
734 {
735 // first look for the attribute of this specific cell
736 attr = m_data->m_cellAttrs.GetAttr(row, col);
737
738 if ( !attr )
739 {
740 // then look for the col attr (col attributes are more common than
741 // the row ones, hence they have priority)
742 attr = m_data->m_colAttrs.GetAttr(col);
743 }
744
745 if ( !attr )
746 {
747 // finally try the row attributes
748 attr = m_data->m_rowAttrs.GetAttr(row);
749 }
750 }
751
752 return attr;
753 }
754
755 void wxGridCellAttrProvider::SetAttr(wxGridCellAttr *attr,
756 int row, int col)
757 {
758 if ( !m_data )
759 InitData();
760
761 m_data->m_cellAttrs.SetAttr(attr, row, col);
762 }
763
764 void wxGridCellAttrProvider::SetRowAttr(wxGridCellAttr *attr, int row)
765 {
766 if ( !m_data )
767 InitData();
768
769 m_data->m_rowAttrs.SetAttr(attr, row);
770 }
771
772 void wxGridCellAttrProvider::SetColAttr(wxGridCellAttr *attr, int col)
773 {
774 if ( !m_data )
775 InitData();
776
777 m_data->m_colAttrs.SetAttr(attr, col);
778 }
779
780 // ----------------------------------------------------------------------------
781 // wxGridTableBase
782 // ----------------------------------------------------------------------------
783
784 //////////////////////////////////////////////////////////////////////
785 //
786 // Abstract base class for grid data (the model)
787 //
788 IMPLEMENT_ABSTRACT_CLASS( wxGridTableBase, wxObject )
789
790
791 wxGridTableBase::wxGridTableBase()
792 {
793 m_view = (wxGrid *) NULL;
794 m_attrProvider = (wxGridCellAttrProvider *) NULL;
795 }
796
797 wxGridTableBase::~wxGridTableBase()
798 {
799 delete m_attrProvider;
800 }
801
802 void wxGridTableBase::SetAttrProvider(wxGridCellAttrProvider *attrProvider)
803 {
804 delete m_attrProvider;
805 m_attrProvider = attrProvider;
806 }
807
808 wxGridCellAttr *wxGridTableBase::GetAttr(int row, int col)
809 {
810 if ( m_attrProvider )
811 return m_attrProvider->GetAttr(row, col);
812 else
813 return (wxGridCellAttr *)NULL;
814 }
815
816 void wxGridTableBase::SetAttr(wxGridCellAttr* attr, int row, int col)
817 {
818 if ( m_attrProvider )
819 {
820 m_attrProvider->SetAttr(attr, row, col);
821 }
822 else
823 {
824 // as we take ownership of the pointer and don't store it, we must
825 // free it now
826 attr->SafeDecRef();
827 }
828 }
829
830 void wxGridTableBase::SetRowAttr(wxGridCellAttr *attr, int row)
831 {
832 if ( m_attrProvider )
833 {
834 m_attrProvider->SetRowAttr(attr, row);
835 }
836 else
837 {
838 // as we take ownership of the pointer and don't store it, we must
839 // free it now
840 attr->SafeDecRef();
841 }
842 }
843
844 void wxGridTableBase::SetColAttr(wxGridCellAttr *attr, int col)
845 {
846 if ( m_attrProvider )
847 {
848 m_attrProvider->SetColAttr(attr, col);
849 }
850 else
851 {
852 // as we take ownership of the pointer and don't store it, we must
853 // free it now
854 attr->SafeDecRef();
855 }
856 }
857
858 bool wxGridTableBase::InsertRows( size_t pos, size_t numRows )
859 {
860 wxFAIL_MSG( wxT("Called grid table class function InsertRows\n"
861 "but your derived table class does not override this function") );
862
863 return FALSE;
864 }
865
866 bool wxGridTableBase::AppendRows( size_t numRows )
867 {
868 wxFAIL_MSG( wxT("Called grid table class function AppendRows\n"
869 "but your derived table class does not override this function"));
870
871 return FALSE;
872 }
873
874 bool wxGridTableBase::DeleteRows( size_t pos, size_t numRows )
875 {
876 wxFAIL_MSG( wxT("Called grid table class function DeleteRows\n"
877 "but your derived table class does not override this function"));
878
879 return FALSE;
880 }
881
882 bool wxGridTableBase::InsertCols( size_t pos, size_t numCols )
883 {
884 wxFAIL_MSG( wxT("Called grid table class function InsertCols\n"
885 "but your derived table class does not override this function"));
886
887 return FALSE;
888 }
889
890 bool wxGridTableBase::AppendCols( size_t numCols )
891 {
892 wxFAIL_MSG(wxT("Called grid table class function AppendCols\n"
893 "but your derived table class does not override this function"));
894
895 return FALSE;
896 }
897
898 bool wxGridTableBase::DeleteCols( size_t pos, size_t numCols )
899 {
900 wxFAIL_MSG( wxT("Called grid table class function DeleteCols\n"
901 "but your derived table class does not override this function"));
902
903 return FALSE;
904 }
905
906
907 wxString wxGridTableBase::GetRowLabelValue( int row )
908 {
909 wxString s;
910 s << row;
911 return s;
912 }
913
914 wxString wxGridTableBase::GetColLabelValue( int col )
915 {
916 // default col labels are:
917 // cols 0 to 25 : A-Z
918 // cols 26 to 675 : AA-ZZ
919 // etc.
920
921 wxString s;
922 unsigned int i, n;
923 for ( n = 1; ; n++ )
924 {
925 s += (_T('A') + (wxChar)( col%26 ));
926 col = col/26 - 1;
927 if ( col < 0 ) break;
928 }
929
930 // reverse the string...
931 wxString s2;
932 for ( i = 0; i < n; i++ )
933 {
934 s2 += s[n-i-1];
935 }
936
937 return s2;
938 }
939
940
941
942 //////////////////////////////////////////////////////////////////////
943 //
944 // Message class for the grid table to send requests and notifications
945 // to the grid view
946 //
947
948 wxGridTableMessage::wxGridTableMessage()
949 {
950 m_table = (wxGridTableBase *) NULL;
951 m_id = -1;
952 m_comInt1 = -1;
953 m_comInt2 = -1;
954 }
955
956 wxGridTableMessage::wxGridTableMessage( wxGridTableBase *table, int id,
957 int commandInt1, int commandInt2 )
958 {
959 m_table = table;
960 m_id = id;
961 m_comInt1 = commandInt1;
962 m_comInt2 = commandInt2;
963 }
964
965
966
967 //////////////////////////////////////////////////////////////////////
968 //
969 // A basic grid table for string data. An object of this class will
970 // created by wxGrid if you don't specify an alternative table class.
971 //
972
973 WX_DEFINE_OBJARRAY(wxGridStringArray)
974
975 IMPLEMENT_DYNAMIC_CLASS( wxGridStringTable, wxGridTableBase )
976
977 wxGridStringTable::wxGridStringTable()
978 : wxGridTableBase()
979 {
980 }
981
982 wxGridStringTable::wxGridStringTable( int numRows, int numCols )
983 : wxGridTableBase()
984 {
985 int row, col;
986
987 m_data.Alloc( numRows );
988
989 wxArrayString sa;
990 sa.Alloc( numCols );
991 for ( col = 0; col < numCols; col++ )
992 {
993 sa.Add( wxEmptyString );
994 }
995
996 for ( row = 0; row < numRows; row++ )
997 {
998 m_data.Add( sa );
999 }
1000 }
1001
1002 wxGridStringTable::~wxGridStringTable()
1003 {
1004 }
1005
1006 long wxGridStringTable::GetNumberRows()
1007 {
1008 return m_data.GetCount();
1009 }
1010
1011 long wxGridStringTable::GetNumberCols()
1012 {
1013 if ( m_data.GetCount() > 0 )
1014 return m_data[0].GetCount();
1015 else
1016 return 0;
1017 }
1018
1019 wxString wxGridStringTable::GetValue( int row, int col )
1020 {
1021 // TODO: bounds checking
1022 //
1023 return m_data[row][col];
1024 }
1025
1026 void wxGridStringTable::SetValue( int row, int col, const wxString& s )
1027 {
1028 // TODO: bounds checking
1029 //
1030 m_data[row][col] = s;
1031 }
1032
1033 bool wxGridStringTable::IsEmptyCell( int row, int col )
1034 {
1035 // TODO: bounds checking
1036 //
1037 return (m_data[row][col] == wxEmptyString);
1038 }
1039
1040
1041 void wxGridStringTable::Clear()
1042 {
1043 int row, col;
1044 int numRows, numCols;
1045
1046 numRows = m_data.GetCount();
1047 if ( numRows > 0 )
1048 {
1049 numCols = m_data[0].GetCount();
1050
1051 for ( row = 0; row < numRows; row++ )
1052 {
1053 for ( col = 0; col < numCols; col++ )
1054 {
1055 m_data[row][col] = wxEmptyString;
1056 }
1057 }
1058 }
1059 }
1060
1061
1062 bool wxGridStringTable::InsertRows( size_t pos, size_t numRows )
1063 {
1064 size_t row, col;
1065
1066 size_t curNumRows = m_data.GetCount();
1067 size_t curNumCols = ( curNumRows > 0 ? m_data[0].GetCount() : 0 );
1068
1069 if ( pos >= curNumRows )
1070 {
1071 return AppendRows( numRows );
1072 }
1073
1074 wxArrayString sa;
1075 sa.Alloc( curNumCols );
1076 for ( col = 0; col < curNumCols; col++ )
1077 {
1078 sa.Add( wxEmptyString );
1079 }
1080
1081 for ( row = pos; row < pos + numRows; row++ )
1082 {
1083 m_data.Insert( sa, row );
1084 }
1085
1086 if ( GetView() )
1087 {
1088 wxGridTableMessage msg( this,
1089 wxGRIDTABLE_NOTIFY_ROWS_INSERTED,
1090 pos,
1091 numRows );
1092
1093 GetView()->ProcessTableMessage( msg );
1094 }
1095
1096 return TRUE;
1097 }
1098
1099 bool wxGridStringTable::AppendRows( size_t numRows )
1100 {
1101 size_t row, col;
1102
1103 size_t curNumRows = m_data.GetCount();
1104 size_t curNumCols = ( curNumRows > 0 ? m_data[0].GetCount() : 0 );
1105
1106 wxArrayString sa;
1107 if ( curNumCols > 0 )
1108 {
1109 sa.Alloc( curNumCols );
1110 for ( col = 0; col < curNumCols; col++ )
1111 {
1112 sa.Add( wxEmptyString );
1113 }
1114 }
1115
1116 for ( row = 0; row < numRows; row++ )
1117 {
1118 m_data.Add( sa );
1119 }
1120
1121 if ( GetView() )
1122 {
1123 wxGridTableMessage msg( this,
1124 wxGRIDTABLE_NOTIFY_ROWS_APPENDED,
1125 numRows );
1126
1127 GetView()->ProcessTableMessage( msg );
1128 }
1129
1130 return TRUE;
1131 }
1132
1133 bool wxGridStringTable::DeleteRows( size_t pos, size_t numRows )
1134 {
1135 size_t n;
1136
1137 size_t curNumRows = m_data.GetCount();
1138
1139 if ( pos >= curNumRows )
1140 {
1141 wxString errmsg;
1142 errmsg.Printf("Called wxGridStringTable::DeleteRows(pos=%d, N=%d)\n"
1143 "Pos value is invalid for present table with %d rows",
1144 pos, numRows, curNumRows );
1145 wxFAIL_MSG( wxT(errmsg) );
1146 return FALSE;
1147 }
1148
1149 if ( numRows > curNumRows - pos )
1150 {
1151 numRows = curNumRows - pos;
1152 }
1153
1154 if ( numRows >= curNumRows )
1155 {
1156 m_data.Empty(); // don't release memory just yet
1157 }
1158 else
1159 {
1160 for ( n = 0; n < numRows; n++ )
1161 {
1162 m_data.Remove( pos );
1163 }
1164 }
1165
1166 if ( GetView() )
1167 {
1168 wxGridTableMessage msg( this,
1169 wxGRIDTABLE_NOTIFY_ROWS_DELETED,
1170 pos,
1171 numRows );
1172
1173 GetView()->ProcessTableMessage( msg );
1174 }
1175
1176 return TRUE;
1177 }
1178
1179 bool wxGridStringTable::InsertCols( size_t pos, size_t numCols )
1180 {
1181 size_t row, col;
1182
1183 size_t curNumRows = m_data.GetCount();
1184 size_t curNumCols = ( curNumRows > 0 ? m_data[0].GetCount() : 0 );
1185
1186 if ( pos >= curNumCols )
1187 {
1188 return AppendCols( numCols );
1189 }
1190
1191 for ( row = 0; row < curNumRows; row++ )
1192 {
1193 for ( col = pos; col < pos + numCols; col++ )
1194 {
1195 m_data[row].Insert( wxEmptyString, col );
1196 }
1197 }
1198
1199 if ( GetView() )
1200 {
1201 wxGridTableMessage msg( this,
1202 wxGRIDTABLE_NOTIFY_COLS_INSERTED,
1203 pos,
1204 numCols );
1205
1206 GetView()->ProcessTableMessage( msg );
1207 }
1208
1209 return TRUE;
1210 }
1211
1212 bool wxGridStringTable::AppendCols( size_t numCols )
1213 {
1214 size_t row, n;
1215
1216 size_t curNumRows = m_data.GetCount();
1217 if ( !curNumRows )
1218 {
1219 // TODO: something better than this ?
1220 //
1221 wxFAIL_MSG( wxT("Unable to append cols to a grid table with no rows.\n"
1222 "Call AppendRows() first") );
1223 return FALSE;
1224 }
1225
1226 for ( row = 0; row < curNumRows; row++ )
1227 {
1228 for ( n = 0; n < numCols; n++ )
1229 {
1230 m_data[row].Add( wxEmptyString );
1231 }
1232 }
1233
1234 if ( GetView() )
1235 {
1236 wxGridTableMessage msg( this,
1237 wxGRIDTABLE_NOTIFY_COLS_APPENDED,
1238 numCols );
1239
1240 GetView()->ProcessTableMessage( msg );
1241 }
1242
1243 return TRUE;
1244 }
1245
1246 bool wxGridStringTable::DeleteCols( size_t pos, size_t numCols )
1247 {
1248 size_t row, n;
1249
1250 size_t curNumRows = m_data.GetCount();
1251 size_t curNumCols = ( curNumRows > 0 ? m_data[0].GetCount() : 0 );
1252
1253 if ( pos >= curNumCols )
1254 {
1255 wxString errmsg;
1256 errmsg.Printf( "Called wxGridStringTable::DeleteCols(pos=%d, N=%d)...\n"
1257 "Pos value is invalid for present table with %d cols",
1258 pos, numCols, curNumCols );
1259 wxFAIL_MSG( wxT( errmsg ) );
1260 return FALSE;
1261 }
1262
1263 if ( numCols > curNumCols - pos )
1264 {
1265 numCols = curNumCols - pos;
1266 }
1267
1268 for ( row = 0; row < curNumRows; row++ )
1269 {
1270 if ( numCols >= curNumCols )
1271 {
1272 m_data[row].Clear();
1273 }
1274 else
1275 {
1276 for ( n = 0; n < numCols; n++ )
1277 {
1278 m_data[row].Remove( pos );
1279 }
1280 }
1281 }
1282
1283 if ( GetView() )
1284 {
1285 wxGridTableMessage msg( this,
1286 wxGRIDTABLE_NOTIFY_COLS_DELETED,
1287 pos,
1288 numCols );
1289
1290 GetView()->ProcessTableMessage( msg );
1291 }
1292
1293 return TRUE;
1294 }
1295
1296 wxString wxGridStringTable::GetRowLabelValue( int row )
1297 {
1298 if ( row > (int)(m_rowLabels.GetCount()) - 1 )
1299 {
1300 // using default label
1301 //
1302 return wxGridTableBase::GetRowLabelValue( row );
1303 }
1304 else
1305 {
1306 return m_rowLabels[ row ];
1307 }
1308 }
1309
1310 wxString wxGridStringTable::GetColLabelValue( int col )
1311 {
1312 if ( col > (int)(m_colLabels.GetCount()) - 1 )
1313 {
1314 // using default label
1315 //
1316 return wxGridTableBase::GetColLabelValue( col );
1317 }
1318 else
1319 {
1320 return m_colLabels[ col ];
1321 }
1322 }
1323
1324 void wxGridStringTable::SetRowLabelValue( int row, const wxString& value )
1325 {
1326 if ( row > (int)(m_rowLabels.GetCount()) - 1 )
1327 {
1328 int n = m_rowLabels.GetCount();
1329 int i;
1330 for ( i = n; i <= row; i++ )
1331 {
1332 m_rowLabels.Add( wxGridTableBase::GetRowLabelValue(i) );
1333 }
1334 }
1335
1336 m_rowLabels[row] = value;
1337 }
1338
1339 void wxGridStringTable::SetColLabelValue( int col, const wxString& value )
1340 {
1341 if ( col > (int)(m_colLabels.GetCount()) - 1 )
1342 {
1343 int n = m_colLabels.GetCount();
1344 int i;
1345 for ( i = n; i <= col; i++ )
1346 {
1347 m_colLabels.Add( wxGridTableBase::GetColLabelValue(i) );
1348 }
1349 }
1350
1351 m_colLabels[col] = value;
1352 }
1353
1354
1355
1356 //////////////////////////////////////////////////////////////////////
1357
1358 IMPLEMENT_DYNAMIC_CLASS( wxGridTextCtrl, wxTextCtrl )
1359
1360 BEGIN_EVENT_TABLE( wxGridTextCtrl, wxTextCtrl )
1361 EVT_KEY_DOWN( wxGridTextCtrl::OnKeyDown )
1362 END_EVENT_TABLE()
1363
1364
1365 wxGridTextCtrl::wxGridTextCtrl( wxWindow *par,
1366 wxGrid *grid,
1367 bool isCellControl,
1368 wxWindowID id,
1369 const wxString& value,
1370 const wxPoint& pos,
1371 const wxSize& size,
1372 long style )
1373 : wxTextCtrl( par, id, value, pos, size, style )
1374 {
1375 m_grid = grid;
1376 m_isCellControl = isCellControl;
1377 }
1378
1379
1380 void wxGridTextCtrl::OnKeyDown( wxKeyEvent& event )
1381 {
1382 switch ( event.KeyCode() )
1383 {
1384 #if 0
1385 case WXK_ESCAPE:
1386 m_grid->SetEditControlValue( startValue );
1387 SetInsertionPointEnd();
1388 break;
1389 #else
1390 case WXK_ESCAPE:
1391 m_grid->EnableCellEditControl( FALSE );
1392 #endif
1393 case WXK_UP:
1394 case WXK_DOWN:
1395 case WXK_LEFT:
1396 case WXK_RIGHT:
1397 case WXK_PRIOR:
1398 case WXK_NEXT:
1399 case WXK_SPACE:
1400 if ( m_isCellControl )
1401 {
1402 // send the event to the parent grid, skipping the
1403 // event if nothing happens
1404 //
1405 event.Skip( m_grid->ProcessEvent( event ) );
1406 }
1407 else
1408 {
1409 // default text control response within the top edit
1410 // control
1411 //
1412 event.Skip();
1413 }
1414 break;
1415
1416 case WXK_RETURN:
1417 if ( m_isCellControl )
1418 {
1419 if ( !m_grid->ProcessEvent( event ) )
1420 {
1421 #if defined(__WXMOTIF__) || defined(__WXGTK__)
1422 // wxMotif needs a little extra help...
1423 //
1424 int pos = GetInsertionPoint();
1425 wxString s( GetValue() );
1426 s = s.Left(pos) + "\n" + s.Mid(pos);
1427 SetValue(s);
1428 SetInsertionPoint( pos );
1429 #else
1430 // the other ports can handle a Return key press
1431 //
1432 event.Skip();
1433 #endif
1434 }
1435 }
1436 break;
1437 case WXK_HOME:
1438 case WXK_END:
1439 if ( m_isCellControl )
1440 {
1441 // send the event to the parent grid, skipping the
1442 // event if nothing happens
1443 //
1444 event.Skip( m_grid->ProcessEvent( event ) );
1445 }
1446 else
1447 {
1448 // default text control response within the top edit
1449 // control
1450 //
1451 event.Skip();
1452 }
1453 break;
1454
1455 default:
1456 event.Skip();
1457 }
1458 }
1459
1460 void wxGridTextCtrl::SetStartValue( const wxString& s )
1461 {
1462 startValue = s;
1463 wxTextCtrl::SetValue(s);
1464 }
1465
1466
1467
1468 //////////////////////////////////////////////////////////////////////
1469
1470 IMPLEMENT_DYNAMIC_CLASS( wxGridRowLabelWindow, wxWindow )
1471
1472 BEGIN_EVENT_TABLE( wxGridRowLabelWindow, wxWindow )
1473 EVT_PAINT( wxGridRowLabelWindow::OnPaint )
1474 EVT_MOUSE_EVENTS( wxGridRowLabelWindow::OnMouseEvent )
1475 EVT_KEY_DOWN( wxGridRowLabelWindow::OnKeyDown )
1476 END_EVENT_TABLE()
1477
1478 wxGridRowLabelWindow::wxGridRowLabelWindow( wxGrid *parent,
1479 wxWindowID id,
1480 const wxPoint &pos, const wxSize &size )
1481 : wxWindow( parent, id, pos, size )
1482 {
1483 m_owner = parent;
1484 }
1485
1486 void wxGridRowLabelWindow::OnPaint( wxPaintEvent &event )
1487 {
1488 wxPaintDC dc(this);
1489
1490 // NO - don't do this because it will set both the x and y origin
1491 // coords to match the parent scrolled window and we just want to
1492 // set the y coord - MB
1493 //
1494 // m_owner->PrepareDC( dc );
1495
1496 int x, y;
1497 m_owner->CalcUnscrolledPosition( 0, 0, &x, &y );
1498 dc.SetDeviceOrigin( 0, -y );
1499
1500 m_owner->CalcRowLabelsExposed( GetUpdateRegion() );
1501 m_owner->DrawRowLabels( dc );
1502 }
1503
1504
1505 void wxGridRowLabelWindow::OnMouseEvent( wxMouseEvent& event )
1506 {
1507 m_owner->ProcessRowLabelMouseEvent( event );
1508 }
1509
1510
1511 // This seems to be required for wxMotif otherwise the mouse
1512 // cursor must be in the cell edit control to get key events
1513 //
1514 void wxGridRowLabelWindow::OnKeyDown( wxKeyEvent& event )
1515 {
1516 if ( !m_owner->ProcessEvent( event ) ) event.Skip();
1517 }
1518
1519
1520
1521 //////////////////////////////////////////////////////////////////////
1522
1523 IMPLEMENT_DYNAMIC_CLASS( wxGridColLabelWindow, wxWindow )
1524
1525 BEGIN_EVENT_TABLE( wxGridColLabelWindow, wxWindow )
1526 EVT_PAINT( wxGridColLabelWindow::OnPaint )
1527 EVT_MOUSE_EVENTS( wxGridColLabelWindow::OnMouseEvent )
1528 EVT_KEY_DOWN( wxGridColLabelWindow::OnKeyDown )
1529 END_EVENT_TABLE()
1530
1531 wxGridColLabelWindow::wxGridColLabelWindow( wxGrid *parent,
1532 wxWindowID id,
1533 const wxPoint &pos, const wxSize &size )
1534 : wxWindow( parent, id, pos, size )
1535 {
1536 m_owner = parent;
1537 }
1538
1539 void wxGridColLabelWindow::OnPaint( wxPaintEvent &event )
1540 {
1541 wxPaintDC dc(this);
1542
1543 // NO - don't do this because it will set both the x and y origin
1544 // coords to match the parent scrolled window and we just want to
1545 // set the x coord - MB
1546 //
1547 // m_owner->PrepareDC( dc );
1548
1549 int x, y;
1550 m_owner->CalcUnscrolledPosition( 0, 0, &x, &y );
1551 dc.SetDeviceOrigin( -x, 0 );
1552
1553 m_owner->CalcColLabelsExposed( GetUpdateRegion() );
1554 m_owner->DrawColLabels( dc );
1555 }
1556
1557
1558 void wxGridColLabelWindow::OnMouseEvent( wxMouseEvent& event )
1559 {
1560 m_owner->ProcessColLabelMouseEvent( event );
1561 }
1562
1563
1564 // This seems to be required for wxMotif otherwise the mouse
1565 // cursor must be in the cell edit control to get key events
1566 //
1567 void wxGridColLabelWindow::OnKeyDown( wxKeyEvent& event )
1568 {
1569 if ( !m_owner->ProcessEvent( event ) ) event.Skip();
1570 }
1571
1572
1573
1574 //////////////////////////////////////////////////////////////////////
1575
1576 IMPLEMENT_DYNAMIC_CLASS( wxGridCornerLabelWindow, wxWindow )
1577
1578 BEGIN_EVENT_TABLE( wxGridCornerLabelWindow, wxWindow )
1579 EVT_MOUSE_EVENTS( wxGridCornerLabelWindow::OnMouseEvent )
1580 EVT_PAINT( wxGridCornerLabelWindow::OnPaint)
1581 EVT_KEY_DOWN( wxGridCornerLabelWindow::OnKeyDown )
1582 END_EVENT_TABLE()
1583
1584 wxGridCornerLabelWindow::wxGridCornerLabelWindow( wxGrid *parent,
1585 wxWindowID id,
1586 const wxPoint &pos, const wxSize &size )
1587 : wxWindow( parent, id, pos, size )
1588 {
1589 m_owner = parent;
1590 }
1591
1592 void wxGridCornerLabelWindow::OnPaint( wxPaintEvent& WXUNUSED(event) )
1593 {
1594 wxPaintDC dc(this);
1595
1596 int client_height = 0;
1597 int client_width = 0;
1598 GetClientSize( &client_width, &client_height );
1599
1600 dc.SetPen( *wxBLACK_PEN );
1601 dc.DrawLine( client_width-1, client_height-1, client_width-1, 0 );
1602 dc.DrawLine( client_width-1, client_height-1, 0, client_height-1 );
1603
1604 dc.SetPen( *wxWHITE_PEN );
1605 dc.DrawLine( 0, 0, client_width, 0 );
1606 dc.DrawLine( 0, 0, 0, client_height );
1607 }
1608
1609
1610 void wxGridCornerLabelWindow::OnMouseEvent( wxMouseEvent& event )
1611 {
1612 m_owner->ProcessCornerLabelMouseEvent( event );
1613 }
1614
1615
1616 // This seems to be required for wxMotif otherwise the mouse
1617 // cursor must be in the cell edit control to get key events
1618 //
1619 void wxGridCornerLabelWindow::OnKeyDown( wxKeyEvent& event )
1620 {
1621 if ( !m_owner->ProcessEvent( event ) ) event.Skip();
1622 }
1623
1624
1625
1626 //////////////////////////////////////////////////////////////////////
1627
1628 IMPLEMENT_DYNAMIC_CLASS( wxGridWindow, wxPanel )
1629
1630 BEGIN_EVENT_TABLE( wxGridWindow, wxPanel )
1631 EVT_PAINT( wxGridWindow::OnPaint )
1632 EVT_MOUSE_EVENTS( wxGridWindow::OnMouseEvent )
1633 EVT_KEY_DOWN( wxGridWindow::OnKeyDown )
1634 EVT_ERASE_BACKGROUND( wxGridWindow::OnEraseBackground )
1635 END_EVENT_TABLE()
1636
1637 wxGridWindow::wxGridWindow( wxGrid *parent,
1638 wxGridRowLabelWindow *rowLblWin,
1639 wxGridColLabelWindow *colLblWin,
1640 wxWindowID id, const wxPoint &pos, const wxSize &size )
1641 : wxPanel( parent, id, pos, size, 0, "grid window" )
1642 {
1643 m_owner = parent;
1644 m_rowLabelWin = rowLblWin;
1645 m_colLabelWin = colLblWin;
1646 SetBackgroundColour( "WHITE" );
1647 }
1648
1649
1650 wxGridWindow::~wxGridWindow()
1651 {
1652 }
1653
1654
1655 void wxGridWindow::OnPaint( wxPaintEvent &WXUNUSED(event) )
1656 {
1657 wxPaintDC dc( this );
1658 m_owner->PrepareDC( dc );
1659 wxRegion reg = GetUpdateRegion();
1660 m_owner->CalcCellsExposed( reg );
1661 m_owner->DrawGridCellArea( dc );
1662 #if WXGRID_DRAW_LINES
1663 m_owner->DrawAllGridLines( dc, reg );
1664 #endif
1665 }
1666
1667
1668 void wxGridWindow::ScrollWindow( int dx, int dy, const wxRect *rect )
1669 {
1670 wxPanel::ScrollWindow( dx, dy, rect );
1671 m_rowLabelWin->ScrollWindow( 0, dy, rect );
1672 m_colLabelWin->ScrollWindow( dx, 0, rect );
1673 }
1674
1675
1676 void wxGridWindow::OnMouseEvent( wxMouseEvent& event )
1677 {
1678 m_owner->ProcessGridCellMouseEvent( event );
1679 }
1680
1681
1682 // This seems to be required for wxMotif otherwise the mouse
1683 // cursor must be in the cell edit control to get key events
1684 //
1685 void wxGridWindow::OnKeyDown( wxKeyEvent& event )
1686 {
1687 if ( !m_owner->ProcessEvent( event ) ) event.Skip();
1688 }
1689
1690 void wxGridWindow::OnEraseBackground(wxEraseEvent&)
1691 { }
1692
1693
1694
1695
1696 //////////////////////////////////////////////////////////////////////
1697
1698
1699 IMPLEMENT_DYNAMIC_CLASS( wxGrid, wxScrolledWindow )
1700
1701 BEGIN_EVENT_TABLE( wxGrid, wxScrolledWindow )
1702 EVT_PAINT( wxGrid::OnPaint )
1703 EVT_SIZE( wxGrid::OnSize )
1704 EVT_KEY_DOWN( wxGrid::OnKeyDown )
1705 EVT_ERASE_BACKGROUND( wxGrid::OnEraseBackground )
1706 END_EVENT_TABLE()
1707
1708 wxGrid::wxGrid( wxWindow *parent,
1709 wxWindowID id,
1710 const wxPoint& pos,
1711 const wxSize& size,
1712 long style,
1713 const wxString& name )
1714 : wxScrolledWindow( parent, id, pos, size, style, name )
1715 {
1716 Create();
1717 }
1718
1719
1720 wxGrid::~wxGrid()
1721 {
1722 ClearAttrCache();
1723 m_defaultCellAttr->SafeDecRef();
1724
1725 #ifdef DEBUG_ATTR_CACHE
1726 size_t total = gs_nAttrCacheHits + gs_nAttrCacheMisses;
1727 wxPrintf(_T("wxGrid attribute cache statistics: "
1728 "total: %u, hits: %u (%u%%)\n"),
1729 total, gs_nAttrCacheHits,
1730 total ? (gs_nAttrCacheHits*100) / total : 0);
1731 #endif
1732
1733 if (m_ownTable)
1734 delete m_table;
1735 }
1736
1737
1738 //
1739 // ----- internal init and update functions
1740 //
1741
1742 void wxGrid::Create()
1743 {
1744 m_created = FALSE; // set to TRUE by CreateGrid
1745 m_displayed = TRUE; // FALSE; // set to TRUE by OnPaint
1746
1747 m_table = (wxGridTableBase *) NULL;
1748 m_ownTable = FALSE;
1749
1750 m_cellEditCtrlEnabled = FALSE;
1751
1752 m_defaultCellAttr = new wxGridCellAttr;
1753 m_defaultCellAttr->SetDefAttr(m_defaultCellAttr);
1754 // RD: Should we fill the default attrs now or is waiting until Init() okay?
1755
1756
1757 m_numRows = 0;
1758 m_numCols = 0;
1759 m_currentCellCoords = wxGridNoCellCoords;
1760
1761 m_rowLabelWidth = WXGRID_DEFAULT_ROW_LABEL_WIDTH;
1762 m_colLabelHeight = WXGRID_DEFAULT_COL_LABEL_HEIGHT;
1763
1764 m_cornerLabelWin = new wxGridCornerLabelWindow( this,
1765 -1,
1766 wxDefaultPosition,
1767 wxDefaultSize );
1768
1769 m_rowLabelWin = new wxGridRowLabelWindow( this,
1770 -1,
1771 wxDefaultPosition,
1772 wxDefaultSize );
1773
1774 m_colLabelWin = new wxGridColLabelWindow( this,
1775 -1,
1776 wxDefaultPosition,
1777 wxDefaultSize );
1778
1779 m_gridWin = new wxGridWindow( this,
1780 m_rowLabelWin,
1781 m_colLabelWin,
1782 -1,
1783 wxDefaultPosition,
1784 wxDefaultSize );
1785
1786 SetTargetWindow( m_gridWin );
1787 }
1788
1789
1790 bool wxGrid::CreateGrid( int numRows, int numCols )
1791 {
1792 if ( m_created )
1793 {
1794 wxFAIL_MSG( wxT("wxGrid::CreateGrid or wxGrid::SetTable called more than once") );
1795 return FALSE;
1796 }
1797 else
1798 {
1799 m_numRows = numRows;
1800 m_numCols = numCols;
1801
1802 m_table = new wxGridStringTable( m_numRows, m_numCols );
1803 m_table->SetView( this );
1804 m_ownTable = TRUE;
1805 Init();
1806 m_created = TRUE;
1807 }
1808
1809 return m_created;
1810 }
1811
1812 bool wxGrid::SetTable( wxGridTableBase *table, bool takeOwnership )
1813 {
1814 if ( m_created )
1815 {
1816 wxFAIL_MSG( wxT("wxGrid::CreateGrid or wxGrid::SetTable called more than once") );
1817 return FALSE;
1818 }
1819 else
1820 {
1821 m_numRows = table->GetNumberRows();
1822 m_numCols = table->GetNumberCols();
1823
1824 m_table = table;
1825 m_table->SetView( this );
1826 if (takeOwnership)
1827 m_ownTable = TRUE;
1828 Init();
1829 m_created = TRUE;
1830 }
1831
1832 return m_created;
1833 }
1834
1835
1836 void wxGrid::Init()
1837 {
1838 int i;
1839
1840 if ( m_numRows <= 0 )
1841 m_numRows = WXGRID_DEFAULT_NUMBER_ROWS;
1842
1843 if ( m_numCols <= 0 )
1844 m_numCols = WXGRID_DEFAULT_NUMBER_COLS;
1845
1846 m_rowLabelWidth = WXGRID_DEFAULT_ROW_LABEL_WIDTH;
1847 m_colLabelHeight = WXGRID_DEFAULT_COL_LABEL_HEIGHT;
1848
1849 if ( m_rowLabelWin )
1850 {
1851 m_labelBackgroundColour = m_rowLabelWin->GetBackgroundColour();
1852 }
1853 else
1854 {
1855 m_labelBackgroundColour = wxColour( _T("WHITE") );
1856 }
1857
1858 m_labelTextColour = wxColour( _T("BLACK") );
1859
1860 // init attr cache
1861 m_attrCache.row = -1;
1862
1863 // TODO: something better than this ?
1864 //
1865 m_labelFont = this->GetFont();
1866 m_labelFont.SetWeight( m_labelFont.GetWeight() + 2 );
1867
1868 m_rowLabelHorizAlign = wxLEFT;
1869 m_rowLabelVertAlign = wxCENTRE;
1870
1871 m_colLabelHorizAlign = wxCENTRE;
1872 m_colLabelVertAlign = wxTOP;
1873
1874 m_defaultColWidth = WXGRID_DEFAULT_COL_WIDTH;
1875 m_defaultRowHeight = m_gridWin->GetCharHeight();
1876
1877 #if defined(__WXMOTIF__) || defined(__WXGTK__) // see also text ctrl sizing in ShowCellEditControl()
1878 m_defaultRowHeight += 8;
1879 #else
1880 m_defaultRowHeight += 4;
1881 #endif
1882
1883 m_rowHeights.Alloc( m_numRows );
1884 m_rowBottoms.Alloc( m_numRows );
1885 int rowBottom = 0;
1886 for ( i = 0; i < m_numRows; i++ )
1887 {
1888 m_rowHeights.Add( m_defaultRowHeight );
1889 rowBottom += m_defaultRowHeight;
1890 m_rowBottoms.Add( rowBottom );
1891 }
1892
1893 m_colWidths.Alloc( m_numCols );
1894 m_colRights.Alloc( m_numCols );
1895 int colRight = 0;
1896 for ( i = 0; i < m_numCols; i++ )
1897 {
1898 m_colWidths.Add( m_defaultColWidth );
1899 colRight += m_defaultColWidth;
1900 m_colRights.Add( colRight );
1901 }
1902
1903 // Set default cell attributes
1904 m_defaultCellAttr->SetFont(GetFont());
1905 m_defaultCellAttr->SetAlignment(wxLEFT, wxTOP);
1906 m_defaultCellAttr->SetRenderer(new wxGridCellStringRenderer);
1907 m_defaultCellAttr->SetEditor(new wxGridCellTextEditor);
1908 m_defaultCellAttr->SetTextColour(
1909 wxSystemSettings::GetSystemColour(wxSYS_COLOUR_WINDOWTEXT));
1910 m_defaultCellAttr->SetBackgroundColour(
1911 wxSystemSettings::GetSystemColour(wxSYS_COLOUR_WINDOW));
1912
1913
1914 m_gridLineColour = wxColour( 128, 128, 255 );
1915 m_gridLinesEnabled = TRUE;
1916
1917 m_cursorMode = WXGRID_CURSOR_SELECT_CELL;
1918 m_winCapture = (wxWindow *)NULL;
1919 m_dragLastPos = -1;
1920 m_dragRowOrCol = -1;
1921 m_isDragging = FALSE;
1922 m_startDragPos = wxDefaultPosition;
1923
1924 m_waitForSlowClick = FALSE;
1925
1926 m_rowResizeCursor = wxCursor( wxCURSOR_SIZENS );
1927 m_colResizeCursor = wxCursor( wxCURSOR_SIZEWE );
1928
1929 m_currentCellCoords = wxGridNoCellCoords;
1930
1931 m_selectedTopLeft = wxGridNoCellCoords;
1932 m_selectedBottomRight = wxGridNoCellCoords;
1933 m_selectionBackground = wxSystemSettings::GetSystemColour(wxSYS_COLOUR_HIGHLIGHT);
1934 m_selectionForeground = wxSystemSettings::GetSystemColour(wxSYS_COLOUR_HIGHLIGHTTEXT);
1935
1936 m_editable = TRUE; // default for whole grid
1937
1938 m_inOnKeyDown = FALSE;
1939 m_batchCount = 0;
1940
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 m_waitForSlowClick = FALSE;
2897 }
2898 else {
2899 SetCurrentCell( coords );
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_TAB:
3586 if (event.ShiftDown())
3587 MoveCursorLeft();
3588 else
3589 MoveCursorRight();
3590 break;
3591
3592 case WXK_HOME:
3593 if ( event.ControlDown() )
3594 {
3595 MakeCellVisible( 0, 0 );
3596 SetCurrentCell( 0, 0 );
3597 }
3598 else
3599 {
3600 event.Skip();
3601 }
3602 break;
3603
3604 case WXK_END:
3605 if ( event.ControlDown() )
3606 {
3607 MakeCellVisible( m_numRows-1, m_numCols-1 );
3608 SetCurrentCell( m_numRows-1, m_numCols-1 );
3609 }
3610 else
3611 {
3612 event.Skip();
3613 }
3614 break;
3615
3616 case WXK_PRIOR:
3617 MovePageUp();
3618 break;
3619
3620 case WXK_NEXT:
3621 MovePageDown();
3622 break;
3623
3624 // We don't want these keys to trigger the edit control, any others?
3625 case WXK_SHIFT:
3626 case WXK_ALT:
3627 case WXK_CONTROL:
3628 case WXK_CAPITAL:
3629 event.Skip();
3630 break;
3631
3632 case WXK_SPACE:
3633 if ( !IsEditable() )
3634 {
3635 MoveCursorRight();
3636 break;
3637 }
3638 // Otherwise fall through to default
3639
3640 default:
3641 // now try the cell edit control
3642 //
3643 if ( !IsCellEditControlEnabled() )
3644 EnableCellEditControl( TRUE );
3645 if (IsCellEditControlEnabled()) {
3646 wxGridCellAttr* attr = GetCellAttr(m_currentCellCoords);
3647 attr->GetEditor()->StartingKey(event);
3648 attr->DecRef();
3649 }
3650 break;
3651 }
3652 }
3653
3654 m_inOnKeyDown = FALSE;
3655 }
3656
3657
3658 void wxGrid::OnEraseBackground(wxEraseEvent&)
3659 { }
3660
3661
3662
3663
3664 void wxGrid::SetCurrentCell( const wxGridCellCoords& coords )
3665 {
3666 if ( SendEvent( EVT_GRID_SELECT_CELL, coords.GetRow(), coords.GetCol() ) )
3667 {
3668 // the event has been intercepted - do nothing
3669 return;
3670 }
3671
3672 if ( m_displayed &&
3673 m_currentCellCoords != wxGridNoCellCoords )
3674 {
3675 HideCellEditControl();
3676 SaveEditControlValue();
3677 EnableCellEditControl(FALSE);
3678
3679 // Clear the old current cell highlight
3680 wxRect r = BlockToDeviceRect(m_currentCellCoords, m_currentCellCoords);
3681 m_currentCellCoords = coords; // Otherwise refresh redraws the hilit!
3682 m_gridWin->Refresh( FALSE, &r );
3683 }
3684
3685 m_currentCellCoords = coords;
3686
3687 SetEditControlValue();
3688
3689 if ( m_displayed )
3690 {
3691 #if 0
3692 ShowCellEditControl();
3693 #else
3694 wxClientDC dc(m_gridWin);
3695 PrepareDC(dc);
3696 DrawCellHighlight(dc);
3697 #endif
3698
3699 if ( IsSelection() )
3700 {
3701 wxRect r( SelectionToDeviceRect() );
3702 ClearSelection();
3703 if ( !GetBatchCount() ) m_gridWin->Refresh( FALSE, &r );
3704 }
3705 }
3706 }
3707
3708
3709 //
3710 // ------ functions to get/send data (see also public functions)
3711 //
3712
3713 bool wxGrid::GetModelValues()
3714 {
3715 if ( m_table )
3716 {
3717 // all we need to do is repaint the grid
3718 //
3719 m_gridWin->Refresh();
3720 return TRUE;
3721 }
3722
3723 return FALSE;
3724 }
3725
3726
3727 bool wxGrid::SetModelValues()
3728 {
3729 int row, col;
3730
3731 if ( m_table )
3732 {
3733 for ( row = 0; row < m_numRows; row++ )
3734 {
3735 for ( col = 0; col < m_numCols; col++ )
3736 {
3737 m_table->SetValue( row, col, GetCellValue(row, col) );
3738 }
3739 }
3740
3741 return TRUE;
3742 }
3743
3744 return FALSE;
3745 }
3746
3747
3748
3749 // Note - this function only draws cells that are in the list of
3750 // exposed cells (usually set from the update region by
3751 // CalcExposedCells)
3752 //
3753 void wxGrid::DrawGridCellArea( wxDC& dc )
3754 {
3755 if ( !m_numRows || !m_numCols ) return;
3756
3757 size_t i;
3758 size_t numCells = m_cellsExposed.GetCount();
3759
3760 for ( i = 0; i < numCells; i++ )
3761 {
3762 DrawCell( dc, m_cellsExposed[i] );
3763 }
3764 }
3765
3766
3767 void wxGrid::DrawCell( wxDC& dc, const wxGridCellCoords& coords )
3768 {
3769 int row = coords.GetRow();
3770 int col = coords.GetCol();
3771
3772 if ( m_colWidths[col] <= 0 || m_rowHeights[row] <= 0 )
3773 return;
3774
3775 // we draw the cell border ourselves
3776 #if !WXGRID_DRAW_LINES
3777 if ( m_gridLinesEnabled )
3778 DrawCellBorder( dc, coords );
3779 #endif
3780
3781 // but all the rest is drawn by the cell renderer and hence may be
3782 // customized
3783 wxRect rect;
3784 rect.x = m_colRights[col] - m_colWidths[col];
3785 rect.y = m_rowBottoms[row] - m_rowHeights[row];
3786 rect.width = m_colWidths[col];
3787 rect.height = m_rowHeights[row];
3788
3789 wxGridCellAttr* attr = GetCellAttr(row, col);
3790 attr->GetRenderer()->Draw(*this, *attr, dc, rect, row, col, IsInSelection(coords));
3791 attr->DecRef();
3792
3793 if (m_currentCellCoords == coords)
3794 DrawCellHighlight(dc);
3795 }
3796
3797
3798 void wxGrid::DrawCellHighlight( wxDC& dc )
3799 {
3800 int row = m_currentCellCoords.GetRow();
3801 int col = m_currentCellCoords.GetCol();
3802
3803 if ( m_colWidths[col] <= 0 || m_rowHeights[row] <= 0 )
3804 return;
3805
3806 wxRect rect;
3807 rect.x = m_colRights[col] - m_colWidths[col];
3808 rect.y = m_rowBottoms[row] - m_rowHeights[row];
3809 rect.width = m_colWidths[col] - 1;
3810 rect.height = m_rowHeights[row] - 1;
3811
3812 dc.SetPen(wxPen(m_gridLineColour, 3, wxSOLID));
3813 dc.SetBrush(*wxTRANSPARENT_BRUSH);
3814 //dc.SetLogicalFunction(wxINVERT);
3815
3816 dc.DrawRectangle(rect);
3817 }
3818
3819 void wxGrid::DrawCellBorder( wxDC& dc, const wxGridCellCoords& coords )
3820 {
3821 if ( m_colWidths[coords.GetCol()] <=0 ||
3822 m_rowHeights[coords.GetRow()] <= 0 ) return;
3823
3824 dc.SetPen( wxPen(GetGridLineColour(), 1, wxSOLID) );
3825 int row = coords.GetRow();
3826 int col = coords.GetCol();
3827
3828 // right hand border
3829 //
3830 dc.DrawLine( m_colRights[col], m_rowBottoms[row] - m_rowHeights[row],
3831 m_colRights[col], m_rowBottoms[row] );
3832
3833 // bottom border
3834 //
3835 dc.DrawLine( m_colRights[col] - m_colWidths[col], m_rowBottoms[row],
3836 m_colRights[col], m_rowBottoms[row] );
3837 }
3838
3839
3840 // TODO: remove this ???
3841 // This is used to redraw all grid lines e.g. when the grid line colour
3842 // has been changed
3843 //
3844 void wxGrid::DrawAllGridLines( wxDC& dc, const wxRegion & reg )
3845 {
3846 if ( !m_gridLinesEnabled ||
3847 !m_numRows ||
3848 !m_numCols ) return;
3849
3850 int top, bottom, left, right;
3851
3852 if (reg.IsEmpty()){
3853 int cw, ch;
3854 m_gridWin->GetClientSize(&cw, &ch);
3855
3856 // virtual coords of visible area
3857 //
3858 CalcUnscrolledPosition( 0, 0, &left, &top );
3859 CalcUnscrolledPosition( cw, ch, &right, &bottom );
3860 }
3861 else{
3862 wxCoord x, y, w, h;
3863 reg.GetBox(x, y, w, h);
3864 CalcUnscrolledPosition( x, y, &left, &top );
3865 CalcUnscrolledPosition( x + w, y + h, &right, &bottom );
3866 }
3867
3868 // avoid drawing grid lines past the last row and col
3869 //
3870 right = wxMin( right, m_colRights[m_numCols-1] );
3871 bottom = wxMin( bottom, m_rowBottoms[m_numRows-1] );
3872
3873 dc.SetPen( wxPen(GetGridLineColour(), 1, wxSOLID) );
3874
3875 // horizontal grid lines
3876 //
3877 int i;
3878 for ( i = 0; i < m_numRows; i++ )
3879 {
3880 if ( m_rowBottoms[i]-1 > bottom )
3881 {
3882 break;
3883 }
3884 else if ( m_rowBottoms[i]-1 >= top )
3885 {
3886 dc.DrawLine( left, m_rowBottoms[i]-1, right, m_rowBottoms[i]-1 );
3887 }
3888 }
3889
3890
3891 // vertical grid lines
3892 //
3893 for ( i = 0; i < m_numCols; i++ )
3894 {
3895 if ( m_colRights[i]-1 > right )
3896 {
3897 break;
3898 }
3899 else if ( m_colRights[i]-1 >= left )
3900 {
3901 dc.DrawLine( m_colRights[i]-1, top, m_colRights[i]-1, bottom );
3902 }
3903 }
3904 }
3905
3906
3907 void wxGrid::DrawRowLabels( wxDC& dc )
3908 {
3909 if ( !m_numRows || !m_numCols ) return;
3910
3911 size_t i;
3912 size_t numLabels = m_rowLabelsExposed.GetCount();
3913
3914 for ( i = 0; i < numLabels; i++ )
3915 {
3916 DrawRowLabel( dc, m_rowLabelsExposed[i] );
3917 }
3918 }
3919
3920
3921 void wxGrid::DrawRowLabel( wxDC& dc, int row )
3922 {
3923 if ( m_rowHeights[row] <= 0 ) return;
3924
3925 int rowTop = m_rowBottoms[row] - m_rowHeights[row];
3926
3927 dc.SetPen( *wxBLACK_PEN );
3928 dc.DrawLine( m_rowLabelWidth-1, rowTop,
3929 m_rowLabelWidth-1, m_rowBottoms[row]-1 );
3930
3931 dc.DrawLine( 0, m_rowBottoms[row]-1,
3932 m_rowLabelWidth-1, m_rowBottoms[row]-1 );
3933
3934 dc.SetPen( *wxWHITE_PEN );
3935 dc.DrawLine( 0, rowTop, 0, m_rowBottoms[row]-1 );
3936 dc.DrawLine( 0, rowTop, m_rowLabelWidth-1, rowTop );
3937
3938 dc.SetBackgroundMode( wxTRANSPARENT );
3939 dc.SetTextForeground( GetLabelTextColour() );
3940 dc.SetFont( GetLabelFont() );
3941
3942 int hAlign, vAlign;
3943 GetRowLabelAlignment( &hAlign, &vAlign );
3944
3945 wxRect rect;
3946 rect.SetX( 2 );
3947 rect.SetY( m_rowBottoms[row] - m_rowHeights[row] + 2 );
3948 rect.SetWidth( m_rowLabelWidth - 4 );
3949 rect.SetHeight( m_rowHeights[row] - 4 );
3950 DrawTextRectangle( dc, GetRowLabelValue( row ), rect, hAlign, vAlign );
3951 }
3952
3953
3954 void wxGrid::DrawColLabels( wxDC& dc )
3955 {
3956 if ( !m_numRows || !m_numCols ) return;
3957
3958 size_t i;
3959 size_t numLabels = m_colLabelsExposed.GetCount();
3960
3961 for ( i = 0; i < numLabels; i++ )
3962 {
3963 DrawColLabel( dc, m_colLabelsExposed[i] );
3964 }
3965 }
3966
3967
3968 void wxGrid::DrawColLabel( wxDC& dc, int col )
3969 {
3970 if ( m_colWidths[col] <= 0 ) return;
3971
3972 int colLeft = m_colRights[col] - m_colWidths[col];
3973
3974 dc.SetPen( *wxBLACK_PEN );
3975 dc.DrawLine( m_colRights[col]-1, 0,
3976 m_colRights[col]-1, m_colLabelHeight-1 );
3977
3978 dc.DrawLine( colLeft, m_colLabelHeight-1,
3979 m_colRights[col]-1, m_colLabelHeight-1 );
3980
3981 dc.SetPen( *wxWHITE_PEN );
3982 dc.DrawLine( colLeft, 0, colLeft, m_colLabelHeight-1 );
3983 dc.DrawLine( colLeft, 0, m_colRights[col]-1, 0 );
3984
3985 dc.SetBackgroundMode( wxTRANSPARENT );
3986 dc.SetTextForeground( GetLabelTextColour() );
3987 dc.SetFont( GetLabelFont() );
3988
3989 dc.SetBackgroundMode( wxTRANSPARENT );
3990 dc.SetTextForeground( GetLabelTextColour() );
3991 dc.SetFont( GetLabelFont() );
3992
3993 int hAlign, vAlign;
3994 GetColLabelAlignment( &hAlign, &vAlign );
3995
3996 wxRect rect;
3997 rect.SetX( m_colRights[col] - m_colWidths[col] + 2 );
3998 rect.SetY( 2 );
3999 rect.SetWidth( m_colWidths[col] - 4 );
4000 rect.SetHeight( m_colLabelHeight - 4 );
4001 DrawTextRectangle( dc, GetColLabelValue( col ), rect, hAlign, vAlign );
4002 }
4003
4004
4005 void wxGrid::DrawTextRectangle( wxDC& dc,
4006 const wxString& value,
4007 const wxRect& rect,
4008 int horizAlign,
4009 int vertAlign )
4010 {
4011 long textWidth, textHeight;
4012 long lineWidth, lineHeight;
4013 wxArrayString lines;
4014
4015 dc.SetClippingRegion( rect );
4016 StringToLines( value, lines );
4017 if ( lines.GetCount() )
4018 {
4019 GetTextBoxSize( dc, lines, &textWidth, &textHeight );
4020 dc.GetTextExtent( lines[0], &lineWidth, &lineHeight );
4021
4022 float x, y;
4023 switch ( horizAlign )
4024 {
4025 case wxRIGHT:
4026 x = rect.x + (rect.width - textWidth - 1);
4027 break;
4028
4029 case wxCENTRE:
4030 x = rect.x + ((rect.width - textWidth)/2);
4031 break;
4032
4033 case wxLEFT:
4034 default:
4035 x = rect.x + 1;
4036 break;
4037 }
4038
4039 switch ( vertAlign )
4040 {
4041 case wxBOTTOM:
4042 y = rect.y + (rect.height - textHeight - 1);
4043 break;
4044
4045 case wxCENTRE:
4046 y = rect.y + ((rect.height - textHeight)/2);
4047 break;
4048
4049 case wxTOP:
4050 default:
4051 y = rect.y + 1;
4052 break;
4053 }
4054
4055 for ( size_t i = 0; i < lines.GetCount(); i++ )
4056 {
4057 dc.DrawText( lines[i], (long)x, (long)y );
4058 y += lineHeight;
4059 }
4060 }
4061
4062 dc.DestroyClippingRegion();
4063 }
4064
4065
4066 // Split multi line text up into an array of strings. Any existing
4067 // contents of the string array are preserved.
4068 //
4069 void wxGrid::StringToLines( const wxString& value, wxArrayString& lines )
4070 {
4071 int startPos = 0;
4072 int pos;
4073 wxString eol = wxTextFile::GetEOL( wxTextFileType_Unix );
4074 wxString tVal = wxTextFile::Translate( value, wxTextFileType_Unix );
4075
4076 while ( startPos < (int)tVal.Length() )
4077 {
4078 pos = tVal.Mid(startPos).Find( eol );
4079 if ( pos < 0 )
4080 {
4081 break;
4082 }
4083 else if ( pos == 0 )
4084 {
4085 lines.Add( wxEmptyString );
4086 }
4087 else
4088 {
4089 lines.Add( value.Mid(startPos, pos) );
4090 }
4091 startPos += pos+1;
4092 }
4093 if ( startPos < (int)value.Length() )
4094 {
4095 lines.Add( value.Mid( startPos ) );
4096 }
4097 }
4098
4099
4100 void wxGrid::GetTextBoxSize( wxDC& dc,
4101 wxArrayString& lines,
4102 long *width, long *height )
4103 {
4104 long w = 0;
4105 long h = 0;
4106 long lineW, lineH;
4107
4108 size_t i;
4109 for ( i = 0; i < lines.GetCount(); i++ )
4110 {
4111 dc.GetTextExtent( lines[i], &lineW, &lineH );
4112 w = wxMax( w, lineW );
4113 h += lineH;
4114 }
4115
4116 *width = w;
4117 *height = h;
4118 }
4119
4120
4121 //
4122 // ------ Edit control functions
4123 //
4124
4125
4126 void wxGrid::EnableEditing( bool edit )
4127 {
4128 // TODO: improve this ?
4129 //
4130 if ( edit != m_editable )
4131 {
4132 m_editable = edit;
4133
4134 EnableCellEditControl(m_editable);
4135 }
4136 }
4137
4138
4139 void wxGrid::EnableCellEditControl( bool enable )
4140 {
4141 if (! m_editable)
4142 return;
4143
4144 if ( m_currentCellCoords == wxGridNoCellCoords )
4145 SetCurrentCell( 0, 0 );
4146
4147 if ( enable != m_cellEditCtrlEnabled )
4148 {
4149 if ( enable )
4150 {
4151 m_cellEditCtrlEnabled = enable;
4152 SetEditControlValue();
4153 ShowCellEditControl();
4154 }
4155 else
4156 {
4157 HideCellEditControl();
4158 SaveEditControlValue();
4159 m_cellEditCtrlEnabled = enable;
4160 }
4161 }
4162 }
4163
4164
4165 void wxGrid::ShowCellEditControl()
4166 {
4167 if ( IsCellEditControlEnabled() )
4168 {
4169 if ( !IsVisible( m_currentCellCoords ) )
4170 {
4171 return;
4172 }
4173 else
4174 {
4175 wxRect rect = CellToRect( m_currentCellCoords );
4176 int row = m_currentCellCoords.GetRow();
4177 int col = m_currentCellCoords.GetCol();
4178
4179 // convert to scrolled coords
4180 //
4181 int left, top, right, bottom;
4182 CalcScrolledPosition( rect.GetLeft(), rect.GetTop(), &left, &top );
4183 CalcScrolledPosition( rect.GetRight(), rect.GetBottom(), &right, &bottom );
4184 left--; top--; right--; bottom--; // cell is shifted by one pixel
4185 int cw, ch;
4186 m_gridWin->GetClientSize( &cw, &ch );
4187
4188 // Make the edit control large enough to allow for internal margins
4189 // TODO: remove this if the text ctrl sizing is improved esp. for unix
4190 //
4191 int extra;
4192 #if defined(__WXMOTIF__)
4193 if ( row == 0 || col == 0 )
4194 {
4195 extra = 2;
4196 }
4197 else
4198 {
4199 extra = 4;
4200 }
4201 #else
4202 if ( row == 0 || col == 0 )
4203 {
4204 extra = 1;
4205 }
4206 else
4207 {
4208 extra = 2;
4209 }
4210 #endif
4211
4212 #if defined(__WXGTK__)
4213 int top_diff = 0;
4214 int left_diff = 0;
4215 if (left != 0) left_diff++;
4216 if (top != 0) top_diff++;
4217 rect.SetLeft( left + left_diff );
4218 rect.SetTop( top + top_diff );
4219 rect.SetRight( rect.GetRight() - left_diff );
4220 rect.SetBottom( rect.GetBottom() - top_diff );
4221 #else
4222 rect.SetLeft( wxMax(0, left - extra) );
4223 rect.SetTop( wxMax(0, top - extra) );
4224 rect.SetRight( rect.GetRight() + 2*extra );
4225 rect.SetBottom( rect.GetBottom() + 2*extra );
4226 #endif
4227
4228 wxGridCellAttr* attr = GetCellAttr(row, col);
4229 wxGridCellEditor* editor = attr->GetEditor();
4230 if (! editor->IsCreated()) {
4231 editor->Create(m_gridWin, -1,
4232 new wxGridCellEditorEvtHandler(this, editor));
4233 }
4234
4235 editor->SetSize( rect );
4236 editor->Show( TRUE );
4237 editor->BeginEdit(row, col, this, attr);
4238 attr->DecRef();
4239 }
4240 }
4241 }
4242
4243
4244 void wxGrid::HideCellEditControl()
4245 {
4246 if ( IsCellEditControlEnabled() )
4247 {
4248 int row = m_currentCellCoords.GetRow();
4249 int col = m_currentCellCoords.GetCol();
4250
4251 wxGridCellAttr* attr = GetCellAttr(row, col);
4252 attr->GetEditor()->Show( FALSE );
4253 attr->DecRef();
4254 m_gridWin->SetFocus();
4255 }
4256 }
4257
4258
4259 void wxGrid::SetEditControlValue( const wxString& value )
4260 {
4261 }
4262
4263
4264 void wxGrid::SaveEditControlValue()
4265 {
4266 if (IsCellEditControlEnabled()) {
4267 int row = m_currentCellCoords.GetRow();
4268 int col = m_currentCellCoords.GetCol();
4269
4270 wxGridCellAttr* attr = GetCellAttr(row, col);
4271 bool changed = attr->GetEditor()->EndEdit(row, col, TRUE, this, attr);
4272
4273 attr->DecRef();
4274
4275 if (changed) {
4276 SendEvent( EVT_GRID_CELL_CHANGE,
4277 m_currentCellCoords.GetRow(),
4278 m_currentCellCoords.GetCol() );
4279 }
4280 }
4281 }
4282
4283
4284 //
4285 // ------ Grid location functions
4286 // Note that all of these functions work with the logical coordinates of
4287 // grid cells and labels so you will need to convert from device
4288 // coordinates for mouse events etc.
4289 //
4290
4291 void wxGrid::XYToCell( int x, int y, wxGridCellCoords& coords )
4292 {
4293 int row = YToRow(y);
4294 int col = XToCol(x);
4295
4296 if ( row == -1 || col == -1 )
4297 {
4298 coords = wxGridNoCellCoords;
4299 }
4300 else
4301 {
4302 coords.Set( row, col );
4303 }
4304 }
4305
4306
4307 int wxGrid::YToRow( int y )
4308 {
4309 int i;
4310
4311 for ( i = 0; i < m_numRows; i++ )
4312 {
4313 if ( y < m_rowBottoms[i] ) return i;
4314 }
4315
4316 return m_numRows; //-1;
4317 }
4318
4319
4320 int wxGrid::XToCol( int x )
4321 {
4322 int i;
4323
4324 for ( i = 0; i < m_numCols; i++ )
4325 {
4326 if ( x < m_colRights[i] ) return i;
4327 }
4328
4329 return m_numCols; //-1;
4330 }
4331
4332
4333 // return the row number that that the y coord is near the edge of, or
4334 // -1 if not near an edge
4335 //
4336 int wxGrid::YToEdgeOfRow( int y )
4337 {
4338 int i, d;
4339
4340 for ( i = 0; i < m_numRows; i++ )
4341 {
4342 if ( m_rowHeights[i] > WXGRID_LABEL_EDGE_ZONE )
4343 {
4344 d = abs( y - m_rowBottoms[i] );
4345 {
4346 if ( d < WXGRID_LABEL_EDGE_ZONE ) return i;
4347 }
4348 }
4349 }
4350
4351 return -1;
4352 }
4353
4354
4355 // return the col number that that the x coord is near the edge of, or
4356 // -1 if not near an edge
4357 //
4358 int wxGrid::XToEdgeOfCol( int x )
4359 {
4360 int i, d;
4361
4362 for ( i = 0; i < m_numCols; i++ )
4363 {
4364 if ( m_colWidths[i] > WXGRID_LABEL_EDGE_ZONE )
4365 {
4366 d = abs( x - m_colRights[i] );
4367 {
4368 if ( d < WXGRID_LABEL_EDGE_ZONE ) return i;
4369 }
4370 }
4371 }
4372
4373 return -1;
4374 }
4375
4376
4377 wxRect wxGrid::CellToRect( int row, int col )
4378 {
4379 wxRect rect( -1, -1, -1, -1 );
4380
4381 if ( row >= 0 && row < m_numRows &&
4382 col >= 0 && col < m_numCols )
4383 {
4384 rect.x = m_colRights[col] - m_colWidths[col];
4385 rect.y = m_rowBottoms[row] - m_rowHeights[row];
4386 rect.width = m_colWidths[col];
4387 rect.height = m_rowHeights[ row ];
4388 }
4389
4390 return rect;
4391 }
4392
4393
4394 bool wxGrid::IsVisible( int row, int col, bool wholeCellVisible )
4395 {
4396 // get the cell rectangle in logical coords
4397 //
4398 wxRect r( CellToRect( row, col ) );
4399
4400 // convert to device coords
4401 //
4402 int left, top, right, bottom;
4403 CalcScrolledPosition( r.GetLeft(), r.GetTop(), &left, &top );
4404 CalcScrolledPosition( r.GetRight(), r.GetBottom(), &right, &bottom );
4405
4406 // check against the client area of the grid window
4407 //
4408 int cw, ch;
4409 m_gridWin->GetClientSize( &cw, &ch );
4410
4411 if ( wholeCellVisible )
4412 {
4413 // is the cell wholly visible ?
4414 //
4415 return ( left >= 0 && right <= cw &&
4416 top >= 0 && bottom <= ch );
4417 }
4418 else
4419 {
4420 // is the cell partly visible ?
4421 //
4422 return ( ((left >=0 && left < cw) || (right > 0 && right <= cw)) &&
4423 ((top >=0 && top < ch) || (bottom > 0 && bottom <= ch)) );
4424 }
4425 }
4426
4427
4428 // make the specified cell location visible by doing a minimal amount
4429 // of scrolling
4430 //
4431 void wxGrid::MakeCellVisible( int row, int col )
4432 {
4433 int i;
4434 int xpos = -1, ypos = -1;
4435
4436 if ( row >= 0 && row < m_numRows &&
4437 col >= 0 && col < m_numCols )
4438 {
4439 // get the cell rectangle in logical coords
4440 //
4441 wxRect r( CellToRect( row, col ) );
4442
4443 // convert to device coords
4444 //
4445 int left, top, right, bottom;
4446 CalcScrolledPosition( r.GetLeft(), r.GetTop(), &left, &top );
4447 CalcScrolledPosition( r.GetRight(), r.GetBottom(), &right, &bottom );
4448
4449 int cw, ch;
4450 m_gridWin->GetClientSize( &cw, &ch );
4451
4452 if ( top < 0 )
4453 {
4454 ypos = r.GetTop();
4455 }
4456 else if ( bottom > ch )
4457 {
4458 int h = r.GetHeight();
4459 ypos = r.GetTop();
4460 for ( i = row-1; i >= 0; i-- )
4461 {
4462 if ( h + m_rowHeights[i] > ch ) break;
4463
4464 h += m_rowHeights[i];
4465 ypos -= m_rowHeights[i];
4466 }
4467
4468 // we divide it later by GRID_SCROLL_LINE, make sure that we don't
4469 // have rounding errors (this is important, because if we do, we
4470 // might not scroll at all and some cells won't be redrawn)
4471 ypos += GRID_SCROLL_LINE / 2;
4472 }
4473
4474 if ( left < 0 )
4475 {
4476 xpos = r.GetLeft();
4477 }
4478 else if ( right > cw )
4479 {
4480 int w = r.GetWidth();
4481 xpos = r.GetLeft();
4482 for ( i = col-1; i >= 0; i-- )
4483 {
4484 if ( w + m_colWidths[i] > cw ) break;
4485
4486 w += m_colWidths[i];
4487 xpos -= m_colWidths[i];
4488 }
4489
4490 // see comment for ypos above
4491 xpos += GRID_SCROLL_LINE / 2;
4492 }
4493
4494 if ( xpos != -1 || ypos != -1 )
4495 {
4496 if ( xpos != -1 ) xpos /= GRID_SCROLL_LINE;
4497 if ( ypos != -1 ) ypos /= GRID_SCROLL_LINE;
4498 Scroll( xpos, ypos );
4499 AdjustScrollbars();
4500 }
4501 }
4502 }
4503
4504
4505 //
4506 // ------ Grid cursor movement functions
4507 //
4508
4509 bool wxGrid::MoveCursorUp()
4510 {
4511 if ( m_currentCellCoords != wxGridNoCellCoords &&
4512 m_currentCellCoords.GetRow() > 0 )
4513 {
4514 MakeCellVisible( m_currentCellCoords.GetRow() - 1,
4515 m_currentCellCoords.GetCol() );
4516
4517 SetCurrentCell( m_currentCellCoords.GetRow() - 1,
4518 m_currentCellCoords.GetCol() );
4519
4520 return TRUE;
4521 }
4522
4523 return FALSE;
4524 }
4525
4526
4527 bool wxGrid::MoveCursorDown()
4528 {
4529 // TODO: allow for scrolling
4530 //
4531 if ( m_currentCellCoords != wxGridNoCellCoords &&
4532 m_currentCellCoords.GetRow() < m_numRows-1 )
4533 {
4534 MakeCellVisible( m_currentCellCoords.GetRow() + 1,
4535 m_currentCellCoords.GetCol() );
4536
4537 SetCurrentCell( m_currentCellCoords.GetRow() + 1,
4538 m_currentCellCoords.GetCol() );
4539
4540 return TRUE;
4541 }
4542
4543 return FALSE;
4544 }
4545
4546
4547 bool wxGrid::MoveCursorLeft()
4548 {
4549 if ( m_currentCellCoords != wxGridNoCellCoords &&
4550 m_currentCellCoords.GetCol() > 0 )
4551 {
4552 MakeCellVisible( m_currentCellCoords.GetRow(),
4553 m_currentCellCoords.GetCol() - 1 );
4554
4555 SetCurrentCell( m_currentCellCoords.GetRow(),
4556 m_currentCellCoords.GetCol() - 1 );
4557
4558 return TRUE;
4559 }
4560
4561 return FALSE;
4562 }
4563
4564
4565 bool wxGrid::MoveCursorRight()
4566 {
4567 if ( m_currentCellCoords != wxGridNoCellCoords &&
4568 m_currentCellCoords.GetCol() < m_numCols - 1 )
4569 {
4570 MakeCellVisible( m_currentCellCoords.GetRow(),
4571 m_currentCellCoords.GetCol() + 1 );
4572
4573 SetCurrentCell( m_currentCellCoords.GetRow(),
4574 m_currentCellCoords.GetCol() + 1 );
4575
4576 return TRUE;
4577 }
4578
4579 return FALSE;
4580 }
4581
4582
4583 bool wxGrid::MovePageUp()
4584 {
4585 if ( m_currentCellCoords == wxGridNoCellCoords ) return FALSE;
4586
4587 int row = m_currentCellCoords.GetRow();
4588 if ( row > 0 )
4589 {
4590 int cw, ch;
4591 m_gridWin->GetClientSize( &cw, &ch );
4592
4593 int y = m_rowBottoms[ row ] - m_rowHeights[ row ];
4594 int newRow = YToRow( y - ch + 1 );
4595 if ( newRow == -1 )
4596 {
4597 newRow = 0;
4598 }
4599 else if ( newRow == row )
4600 {
4601 newRow = row - 1;
4602 }
4603
4604 MakeCellVisible( newRow, m_currentCellCoords.GetCol() );
4605 SetCurrentCell( newRow, m_currentCellCoords.GetCol() );
4606
4607 return TRUE;
4608 }
4609
4610 return FALSE;
4611 }
4612
4613 bool wxGrid::MovePageDown()
4614 {
4615 if ( m_currentCellCoords == wxGridNoCellCoords ) return FALSE;
4616
4617 int row = m_currentCellCoords.GetRow();
4618 if ( row < m_numRows )
4619 {
4620 int cw, ch;
4621 m_gridWin->GetClientSize( &cw, &ch );
4622
4623 int y = m_rowBottoms[ row ] - m_rowHeights[ row ];
4624 int newRow = YToRow( y + ch );
4625 if ( newRow == -1 )
4626 {
4627 newRow = m_numRows - 1;
4628 }
4629 else if ( newRow == row )
4630 {
4631 newRow = row + 1;
4632 }
4633
4634 MakeCellVisible( newRow, m_currentCellCoords.GetCol() );
4635 SetCurrentCell( newRow, m_currentCellCoords.GetCol() );
4636
4637 return TRUE;
4638 }
4639
4640 return FALSE;
4641 }
4642
4643 bool wxGrid::MoveCursorUpBlock()
4644 {
4645 if ( m_table &&
4646 m_currentCellCoords != wxGridNoCellCoords &&
4647 m_currentCellCoords.GetRow() > 0 )
4648 {
4649 int row = m_currentCellCoords.GetRow();
4650 int col = m_currentCellCoords.GetCol();
4651
4652 if ( m_table->IsEmptyCell(row, col) )
4653 {
4654 // starting in an empty cell: find the next block of
4655 // non-empty cells
4656 //
4657 while ( row > 0 )
4658 {
4659 row-- ;
4660 if ( !(m_table->IsEmptyCell(row, col)) ) break;
4661 }
4662 }
4663 else if ( m_table->IsEmptyCell(row-1, col) )
4664 {
4665 // starting at the top of a block: find the next block
4666 //
4667 row--;
4668 while ( row > 0 )
4669 {
4670 row-- ;
4671 if ( !(m_table->IsEmptyCell(row, col)) ) break;
4672 }
4673 }
4674 else
4675 {
4676 // starting within a block: find the top of the block
4677 //
4678 while ( row > 0 )
4679 {
4680 row-- ;
4681 if ( m_table->IsEmptyCell(row, col) )
4682 {
4683 row++ ;
4684 break;
4685 }
4686 }
4687 }
4688
4689 MakeCellVisible( row, col );
4690 SetCurrentCell( row, col );
4691
4692 return TRUE;
4693 }
4694
4695 return FALSE;
4696 }
4697
4698 bool wxGrid::MoveCursorDownBlock()
4699 {
4700 if ( m_table &&
4701 m_currentCellCoords != wxGridNoCellCoords &&
4702 m_currentCellCoords.GetRow() < m_numRows-1 )
4703 {
4704 int row = m_currentCellCoords.GetRow();
4705 int col = m_currentCellCoords.GetCol();
4706
4707 if ( m_table->IsEmptyCell(row, col) )
4708 {
4709 // starting in an empty cell: find the next block of
4710 // non-empty cells
4711 //
4712 while ( row < m_numRows-1 )
4713 {
4714 row++ ;
4715 if ( !(m_table->IsEmptyCell(row, col)) ) break;
4716 }
4717 }
4718 else if ( m_table->IsEmptyCell(row+1, col) )
4719 {
4720 // starting at the bottom of a block: find the next block
4721 //
4722 row++;
4723 while ( row < m_numRows-1 )
4724 {
4725 row++ ;
4726 if ( !(m_table->IsEmptyCell(row, col)) ) break;
4727 }
4728 }
4729 else
4730 {
4731 // starting within a block: find the bottom of the block
4732 //
4733 while ( row < m_numRows-1 )
4734 {
4735 row++ ;
4736 if ( m_table->IsEmptyCell(row, col) )
4737 {
4738 row-- ;
4739 break;
4740 }
4741 }
4742 }
4743
4744 MakeCellVisible( row, col );
4745 SetCurrentCell( row, col );
4746
4747 return TRUE;
4748 }
4749
4750 return FALSE;
4751 }
4752
4753 bool wxGrid::MoveCursorLeftBlock()
4754 {
4755 if ( m_table &&
4756 m_currentCellCoords != wxGridNoCellCoords &&
4757 m_currentCellCoords.GetCol() > 0 )
4758 {
4759 int row = m_currentCellCoords.GetRow();
4760 int col = m_currentCellCoords.GetCol();
4761
4762 if ( m_table->IsEmptyCell(row, col) )
4763 {
4764 // starting in an empty cell: find the next block of
4765 // non-empty cells
4766 //
4767 while ( col > 0 )
4768 {
4769 col-- ;
4770 if ( !(m_table->IsEmptyCell(row, col)) ) break;
4771 }
4772 }
4773 else if ( m_table->IsEmptyCell(row, col-1) )
4774 {
4775 // starting at the left of a block: find the next block
4776 //
4777 col--;
4778 while ( col > 0 )
4779 {
4780 col-- ;
4781 if ( !(m_table->IsEmptyCell(row, col)) ) break;
4782 }
4783 }
4784 else
4785 {
4786 // starting within a block: find the left of the block
4787 //
4788 while ( col > 0 )
4789 {
4790 col-- ;
4791 if ( m_table->IsEmptyCell(row, col) )
4792 {
4793 col++ ;
4794 break;
4795 }
4796 }
4797 }
4798
4799 MakeCellVisible( row, col );
4800 SetCurrentCell( row, col );
4801
4802 return TRUE;
4803 }
4804
4805 return FALSE;
4806 }
4807
4808 bool wxGrid::MoveCursorRightBlock()
4809 {
4810 if ( m_table &&
4811 m_currentCellCoords != wxGridNoCellCoords &&
4812 m_currentCellCoords.GetCol() < m_numCols-1 )
4813 {
4814 int row = m_currentCellCoords.GetRow();
4815 int col = m_currentCellCoords.GetCol();
4816
4817 if ( m_table->IsEmptyCell(row, col) )
4818 {
4819 // starting in an empty cell: find the next block of
4820 // non-empty cells
4821 //
4822 while ( col < m_numCols-1 )
4823 {
4824 col++ ;
4825 if ( !(m_table->IsEmptyCell(row, col)) ) break;
4826 }
4827 }
4828 else if ( m_table->IsEmptyCell(row, col+1) )
4829 {
4830 // starting at the right of a block: find the next block
4831 //
4832 col++;
4833 while ( col < m_numCols-1 )
4834 {
4835 col++ ;
4836 if ( !(m_table->IsEmptyCell(row, col)) ) break;
4837 }
4838 }
4839 else
4840 {
4841 // starting within a block: find the right of the block
4842 //
4843 while ( col < m_numCols-1 )
4844 {
4845 col++ ;
4846 if ( m_table->IsEmptyCell(row, col) )
4847 {
4848 col-- ;
4849 break;
4850 }
4851 }
4852 }
4853
4854 MakeCellVisible( row, col );
4855 SetCurrentCell( row, col );
4856
4857 return TRUE;
4858 }
4859
4860 return FALSE;
4861 }
4862
4863
4864
4865 //
4866 // ------ Label values and formatting
4867 //
4868
4869 void wxGrid::GetRowLabelAlignment( int *horiz, int *vert )
4870 {
4871 *horiz = m_rowLabelHorizAlign;
4872 *vert = m_rowLabelVertAlign;
4873 }
4874
4875 void wxGrid::GetColLabelAlignment( int *horiz, int *vert )
4876 {
4877 *horiz = m_colLabelHorizAlign;
4878 *vert = m_colLabelVertAlign;
4879 }
4880
4881 wxString wxGrid::GetRowLabelValue( int row )
4882 {
4883 if ( m_table )
4884 {
4885 return m_table->GetRowLabelValue( row );
4886 }
4887 else
4888 {
4889 wxString s;
4890 s << row;
4891 return s;
4892 }
4893 }
4894
4895 wxString wxGrid::GetColLabelValue( int col )
4896 {
4897 if ( m_table )
4898 {
4899 return m_table->GetColLabelValue( col );
4900 }
4901 else
4902 {
4903 wxString s;
4904 s << col;
4905 return s;
4906 }
4907 }
4908
4909
4910 void wxGrid::SetRowLabelSize( int width )
4911 {
4912 width = wxMax( width, 0 );
4913 if ( width != m_rowLabelWidth )
4914 {
4915 if ( width == 0 )
4916 {
4917 m_rowLabelWin->Show( FALSE );
4918 m_cornerLabelWin->Show( FALSE );
4919 }
4920 else if ( m_rowLabelWidth == 0 )
4921 {
4922 m_rowLabelWin->Show( TRUE );
4923 if ( m_colLabelHeight > 0 ) m_cornerLabelWin->Show( TRUE );
4924 }
4925
4926 m_rowLabelWidth = width;
4927 CalcWindowSizes();
4928 Refresh( TRUE );
4929 }
4930 }
4931
4932
4933 void wxGrid::SetColLabelSize( int height )
4934 {
4935 height = wxMax( height, 0 );
4936 if ( height != m_colLabelHeight )
4937 {
4938 if ( height == 0 )
4939 {
4940 m_colLabelWin->Show( FALSE );
4941 m_cornerLabelWin->Show( FALSE );
4942 }
4943 else if ( m_colLabelHeight == 0 )
4944 {
4945 m_colLabelWin->Show( TRUE );
4946 if ( m_rowLabelWidth > 0 ) m_cornerLabelWin->Show( TRUE );
4947 }
4948
4949 m_colLabelHeight = height;
4950 CalcWindowSizes();
4951 Refresh( TRUE );
4952 }
4953 }
4954
4955
4956 void wxGrid::SetLabelBackgroundColour( const wxColour& colour )
4957 {
4958 if ( m_labelBackgroundColour != colour )
4959 {
4960 m_labelBackgroundColour = colour;
4961 m_rowLabelWin->SetBackgroundColour( colour );
4962 m_colLabelWin->SetBackgroundColour( colour );
4963 m_cornerLabelWin->SetBackgroundColour( colour );
4964
4965 if ( !GetBatchCount() )
4966 {
4967 m_rowLabelWin->Refresh();
4968 m_colLabelWin->Refresh();
4969 m_cornerLabelWin->Refresh();
4970 }
4971 }
4972 }
4973
4974 void wxGrid::SetLabelTextColour( const wxColour& colour )
4975 {
4976 if ( m_labelTextColour != colour )
4977 {
4978 m_labelTextColour = colour;
4979 if ( !GetBatchCount() )
4980 {
4981 m_rowLabelWin->Refresh();
4982 m_colLabelWin->Refresh();
4983 }
4984 }
4985 }
4986
4987 void wxGrid::SetLabelFont( const wxFont& font )
4988 {
4989 m_labelFont = font;
4990 if ( !GetBatchCount() )
4991 {
4992 m_rowLabelWin->Refresh();
4993 m_colLabelWin->Refresh();
4994 }
4995 }
4996
4997 void wxGrid::SetRowLabelAlignment( int horiz, int vert )
4998 {
4999 if ( horiz == wxLEFT || horiz == wxCENTRE || horiz == wxRIGHT )
5000 {
5001 m_rowLabelHorizAlign = horiz;
5002 }
5003
5004 if ( vert == wxTOP || vert == wxCENTRE || vert == wxBOTTOM )
5005 {
5006 m_rowLabelVertAlign = vert;
5007 }
5008
5009 if ( !GetBatchCount() )
5010 {
5011 m_rowLabelWin->Refresh();
5012 }
5013 }
5014
5015 void wxGrid::SetColLabelAlignment( int horiz, int vert )
5016 {
5017 if ( horiz == wxLEFT || horiz == wxCENTRE || horiz == wxRIGHT )
5018 {
5019 m_colLabelHorizAlign = horiz;
5020 }
5021
5022 if ( vert == wxTOP || vert == wxCENTRE || vert == wxBOTTOM )
5023 {
5024 m_colLabelVertAlign = vert;
5025 }
5026
5027 if ( !GetBatchCount() )
5028 {
5029 m_colLabelWin->Refresh();
5030 }
5031 }
5032
5033 void wxGrid::SetRowLabelValue( int row, const wxString& s )
5034 {
5035 if ( m_table )
5036 {
5037 m_table->SetRowLabelValue( row, s );
5038 if ( !GetBatchCount() )
5039 {
5040 wxRect rect = CellToRect( row, 0);
5041 if ( rect.height > 0 )
5042 {
5043 CalcScrolledPosition(0, rect.y, &rect.x, &rect.y);
5044 rect.x = m_left;
5045 rect.width = m_rowLabelWidth;
5046 m_rowLabelWin->Refresh( TRUE, &rect );
5047 }
5048 }
5049 }
5050 }
5051
5052 void wxGrid::SetColLabelValue( int col, const wxString& s )
5053 {
5054 if ( m_table )
5055 {
5056 m_table->SetColLabelValue( col, s );
5057 if ( !GetBatchCount() )
5058 {
5059 wxRect rect = CellToRect( 0, col );
5060 if ( rect.width > 0 )
5061 {
5062 CalcScrolledPosition(rect.x, 0, &rect.x, &rect.y);
5063 rect.y = m_top;
5064 rect.height = m_colLabelHeight;
5065 m_colLabelWin->Refresh( TRUE, &rect );
5066 }
5067 }
5068 }
5069 }
5070
5071 void wxGrid::SetGridLineColour( const wxColour& colour )
5072 {
5073 if ( m_gridLineColour != colour )
5074 {
5075 m_gridLineColour = colour;
5076
5077 wxClientDC dc( m_gridWin );
5078 PrepareDC( dc );
5079 DrawAllGridLines( dc, wxRegion() );
5080 }
5081 }
5082
5083 void wxGrid::EnableGridLines( bool enable )
5084 {
5085 if ( enable != m_gridLinesEnabled )
5086 {
5087 m_gridLinesEnabled = enable;
5088
5089 if ( !GetBatchCount() )
5090 {
5091 if ( enable )
5092 {
5093 wxClientDC dc( m_gridWin );
5094 PrepareDC( dc );
5095 DrawAllGridLines( dc, wxRegion() );
5096 }
5097 else
5098 {
5099 m_gridWin->Refresh();
5100 }
5101 }
5102 }
5103 }
5104
5105
5106 int wxGrid::GetDefaultRowSize()
5107 {
5108 return m_defaultRowHeight;
5109 }
5110
5111 int wxGrid::GetRowSize( int row )
5112 {
5113 wxCHECK_MSG( row >= 0 && row < m_numRows, 0, _T("invalid row index") );
5114
5115 return m_rowHeights[row];
5116 }
5117
5118 int wxGrid::GetDefaultColSize()
5119 {
5120 return m_defaultColWidth;
5121 }
5122
5123 int wxGrid::GetColSize( int col )
5124 {
5125 wxCHECK_MSG( col >= 0 && col < m_numCols, 0, _T("invalid column index") );
5126
5127 return m_colWidths[col];
5128 }
5129
5130 // ============================================================================
5131 // access to the grid attributes: each of them has a default value in the grid
5132 // itself and may be overidden on a per-cell basis
5133 // ============================================================================
5134
5135 // ----------------------------------------------------------------------------
5136 // setting default attributes
5137 // ----------------------------------------------------------------------------
5138
5139 void wxGrid::SetDefaultCellBackgroundColour( const wxColour& col )
5140 {
5141 m_defaultCellAttr->SetBackgroundColour(col);
5142 }
5143
5144 void wxGrid::SetDefaultCellTextColour( const wxColour& col )
5145 {
5146 m_defaultCellAttr->SetTextColour(col);
5147 }
5148
5149 void wxGrid::SetDefaultCellAlignment( int horiz, int vert )
5150 {
5151 m_defaultCellAttr->SetAlignment(horiz, vert);
5152 }
5153
5154 void wxGrid::SetDefaultCellFont( const wxFont& font )
5155 {
5156 m_defaultCellAttr->SetFont(font);
5157 }
5158
5159 void wxGrid::SetDefaultRenderer(wxGridCellRenderer *renderer)
5160 {
5161 m_defaultCellAttr->SetRenderer(renderer);
5162 }
5163
5164 // ----------------------------------------------------------------------------
5165 // access to the default attrbiutes
5166 // ----------------------------------------------------------------------------
5167
5168 wxColour wxGrid::GetDefaultCellBackgroundColour()
5169 {
5170 return m_defaultCellAttr->GetBackgroundColour();
5171 }
5172
5173 wxColour wxGrid::GetDefaultCellTextColour()
5174 {
5175 return m_defaultCellAttr->GetTextColour();
5176 }
5177
5178 wxFont wxGrid::GetDefaultCellFont()
5179 {
5180 return m_defaultCellAttr->GetFont();
5181 }
5182
5183 void wxGrid::GetDefaultCellAlignment( int *horiz, int *vert )
5184 {
5185 m_defaultCellAttr->GetAlignment(horiz, vert);
5186 }
5187
5188 wxGridCellRenderer *wxGrid::GetDefaultRenderer() const
5189 {
5190 return m_defaultCellAttr->GetRenderer();
5191 }
5192
5193 // ----------------------------------------------------------------------------
5194 // access to cell attributes
5195 // ----------------------------------------------------------------------------
5196
5197 wxColour wxGrid::GetCellBackgroundColour(int row, int col)
5198 {
5199 wxGridCellAttr *attr = GetCellAttr(row, col);
5200 wxColour colour = attr->GetBackgroundColour();
5201 attr->SafeDecRef();
5202 return colour;
5203 }
5204
5205 wxColour wxGrid::GetCellTextColour( int row, int col )
5206 {
5207 wxGridCellAttr *attr = GetCellAttr(row, col);
5208 wxColour colour = attr->GetTextColour();
5209 attr->SafeDecRef();
5210 return colour;
5211 }
5212
5213 wxFont wxGrid::GetCellFont( int row, int col )
5214 {
5215 wxGridCellAttr *attr = GetCellAttr(row, col);
5216 wxFont font = attr->GetFont();
5217 attr->SafeDecRef();
5218 return font;
5219 }
5220
5221 void wxGrid::GetCellAlignment( int row, int col, int *horiz, int *vert )
5222 {
5223 wxGridCellAttr *attr = GetCellAttr(row, col);
5224 attr->GetAlignment(horiz, vert);
5225 attr->SafeDecRef();
5226 }
5227
5228 wxGridCellRenderer* wxGrid::GetCellRenderer(int row, int col)
5229 {
5230 wxGridCellAttr* attr = GetCellAttr(row, col);
5231 wxGridCellRenderer* renderer = attr->GetRenderer();
5232 attr->DecRef();
5233 return renderer;
5234 }
5235
5236 // ----------------------------------------------------------------------------
5237 // attribute support: cache, automatic provider creation, ...
5238 // ----------------------------------------------------------------------------
5239
5240 bool wxGrid::CanHaveAttributes()
5241 {
5242 if ( !m_table )
5243 {
5244 return FALSE;
5245 }
5246
5247 // RD: Maybe m_table->CanHaveAttributes() would be better in case the
5248 // table is providing the attributes itself??? In which case
5249 // I don't think the grid should create a Provider object for the
5250 // table but the table should be smart enough to do that on its own.
5251 if ( !m_table->GetAttrProvider() )
5252 {
5253 // use the default attr provider by default
5254 // (another choice would be to just return FALSE thus forcing the user
5255 // to it himself)
5256 m_table->SetAttrProvider(new wxGridCellAttrProvider);
5257 }
5258
5259 return TRUE;
5260 }
5261
5262 void wxGrid::ClearAttrCache()
5263 {
5264 if ( m_attrCache.row != -1 )
5265 {
5266 m_attrCache.attr->SafeDecRef();
5267 m_attrCache.row = -1;
5268 }
5269 }
5270
5271 void wxGrid::CacheAttr(int row, int col, wxGridCellAttr *attr) const
5272 {
5273 wxGrid *self = (wxGrid *)this; // const_cast
5274
5275 self->ClearAttrCache();
5276 self->m_attrCache.row = row;
5277 self->m_attrCache.col = col;
5278 self->m_attrCache.attr = attr;
5279 attr->SafeIncRef();
5280 }
5281
5282 bool wxGrid::LookupAttr(int row, int col, wxGridCellAttr **attr) const
5283 {
5284 if ( row == m_attrCache.row && col == m_attrCache.col )
5285 {
5286 *attr = m_attrCache.attr;
5287 (*attr)->SafeIncRef();
5288
5289 #ifdef DEBUG_ATTR_CACHE
5290 gs_nAttrCacheHits++;
5291 #endif
5292
5293 return TRUE;
5294 }
5295 else
5296 {
5297 #ifdef DEBUG_ATTR_CACHE
5298 gs_nAttrCacheMisses++;
5299 #endif
5300 return FALSE;
5301 }
5302 }
5303
5304 wxGridCellAttr *wxGrid::GetCellAttr(int row, int col) const
5305 {
5306 wxGridCellAttr *attr;
5307 if ( !LookupAttr(row, col, &attr) )
5308 {
5309 attr = m_table ? m_table->GetAttr(row, col) : (wxGridCellAttr *)NULL;
5310 CacheAttr(row, col, attr);
5311 }
5312 if (attr) {
5313 attr->SetDefAttr(m_defaultCellAttr);
5314 } else {
5315 attr = m_defaultCellAttr;
5316 attr->IncRef();
5317 }
5318
5319 return attr;
5320 }
5321
5322 wxGridCellAttr *wxGrid::GetOrCreateCellAttr(int row, int col) const
5323 {
5324 wxGridCellAttr *attr;
5325 if ( !LookupAttr(row, col, &attr) || !attr )
5326 {
5327 wxASSERT_MSG( m_table,
5328 _T("we may only be called if CanHaveAttributes() "
5329 "returned TRUE and then m_table should be !NULL") );
5330
5331 attr = m_table->GetAttr(row, col);
5332 if ( !attr )
5333 {
5334 attr = new wxGridCellAttr;
5335
5336 // artificially inc the ref count to match DecRef() in caller
5337 attr->IncRef();
5338
5339 m_table->SetAttr(attr, row, col);
5340 }
5341
5342 CacheAttr(row, col, attr);
5343 }
5344 attr->SetDefAttr(m_defaultCellAttr);
5345 return attr;
5346 }
5347
5348 // ----------------------------------------------------------------------------
5349 // setting cell attributes: this is forwarded to the table
5350 // ----------------------------------------------------------------------------
5351
5352 void wxGrid::SetRowAttr(int row, wxGridCellAttr *attr)
5353 {
5354 if ( CanHaveAttributes() )
5355 {
5356 m_table->SetRowAttr(attr, row);
5357 }
5358 else
5359 {
5360 attr->SafeDecRef();
5361 }
5362 }
5363
5364 void wxGrid::SetColAttr(int col, wxGridCellAttr *attr)
5365 {
5366 if ( CanHaveAttributes() )
5367 {
5368 m_table->SetColAttr(attr, col);
5369 }
5370 else
5371 {
5372 attr->SafeDecRef();
5373 }
5374 }
5375
5376 void wxGrid::SetCellBackgroundColour( int row, int col, const wxColour& colour )
5377 {
5378 if ( CanHaveAttributes() )
5379 {
5380 wxGridCellAttr *attr = GetOrCreateCellAttr(row, col);
5381 attr->SetBackgroundColour(colour);
5382 attr->DecRef();
5383 }
5384 }
5385
5386 void wxGrid::SetCellTextColour( int row, int col, const wxColour& colour )
5387 {
5388 if ( CanHaveAttributes() )
5389 {
5390 wxGridCellAttr *attr = GetOrCreateCellAttr(row, col);
5391 attr->SetTextColour(colour);
5392 attr->DecRef();
5393 }
5394 }
5395
5396 void wxGrid::SetCellFont( int row, int col, const wxFont& font )
5397 {
5398 if ( CanHaveAttributes() )
5399 {
5400 wxGridCellAttr *attr = GetOrCreateCellAttr(row, col);
5401 attr->SetFont(font);
5402 attr->DecRef();
5403 }
5404 }
5405
5406 void wxGrid::SetCellAlignment( int row, int col, int horiz, int vert )
5407 {
5408 if ( CanHaveAttributes() )
5409 {
5410 wxGridCellAttr *attr = GetOrCreateCellAttr(row, col);
5411 attr->SetAlignment(horiz, vert);
5412 attr->DecRef();
5413 }
5414 }
5415
5416 void wxGrid::SetCellRenderer(int row, int col, wxGridCellRenderer *renderer)
5417 {
5418 if ( CanHaveAttributes() )
5419 {
5420 wxGridCellAttr *attr = GetOrCreateCellAttr(row, col);
5421 attr->SetRenderer(renderer);
5422 attr->DecRef();
5423 }
5424 }
5425
5426 // ----------------------------------------------------------------------------
5427 // row/col size
5428 // ----------------------------------------------------------------------------
5429
5430 void wxGrid::SetDefaultRowSize( int height, bool resizeExistingRows )
5431 {
5432 m_defaultRowHeight = wxMax( height, WXGRID_MIN_ROW_HEIGHT );
5433
5434 if ( resizeExistingRows )
5435 {
5436 int row;
5437 int bottom = 0;
5438 for ( row = 0; row < m_numRows; row++ )
5439 {
5440 m_rowHeights[row] = m_defaultRowHeight;
5441 bottom += m_defaultRowHeight;
5442 m_rowBottoms[row] = bottom;
5443 }
5444 CalcDimensions();
5445 }
5446 }
5447
5448 void wxGrid::SetRowSize( int row, int height )
5449 {
5450 wxCHECK_RET( row >= 0 && row < m_numRows, _T("invalid row index") );
5451
5452 int i;
5453
5454 int h = wxMax( 0, height );
5455 int diff = h - m_rowHeights[row];
5456
5457 m_rowHeights[row] = h;
5458 for ( i = row; i < m_numRows; i++ )
5459 {
5460 m_rowBottoms[i] += diff;
5461 }
5462 CalcDimensions();
5463 }
5464
5465 void wxGrid::SetDefaultColSize( int width, bool resizeExistingCols )
5466 {
5467 m_defaultColWidth = wxMax( width, WXGRID_MIN_COL_WIDTH );
5468
5469 if ( resizeExistingCols )
5470 {
5471 int col;
5472 int right = 0;
5473 for ( col = 0; col < m_numCols; col++ )
5474 {
5475 m_colWidths[col] = m_defaultColWidth;
5476 right += m_defaultColWidth;
5477 m_colRights[col] = right;
5478 }
5479 CalcDimensions();
5480 }
5481 }
5482
5483 void wxGrid::SetColSize( int col, int width )
5484 {
5485 wxCHECK_RET( col >= 0 && col < m_numCols, _T("invalid column index") );
5486
5487 int i;
5488
5489 int w = wxMax( 0, width );
5490 int diff = w - m_colWidths[col];
5491 m_colWidths[col] = w;
5492
5493 for ( i = col; i < m_numCols; i++ )
5494 {
5495 m_colRights[i] += diff;
5496 }
5497 CalcDimensions();
5498 }
5499
5500
5501 //
5502 // ------ cell value accessor functions
5503 //
5504
5505 void wxGrid::SetCellValue( int row, int col, const wxString& s )
5506 {
5507 if ( m_table )
5508 {
5509 m_table->SetValue( row, col, s.c_str() );
5510 if ( !GetBatchCount() )
5511 {
5512 wxClientDC dc( m_gridWin );
5513 PrepareDC( dc );
5514 DrawCell( dc, wxGridCellCoords(row, col) );
5515 }
5516
5517 #if 0 // TODO: edit in place
5518
5519 if ( m_currentCellCoords.GetRow() == row &&
5520 m_currentCellCoords.GetCol() == col )
5521 {
5522 SetEditControlValue( s );
5523 }
5524 #endif
5525
5526 }
5527 }
5528
5529
5530 //
5531 // ------ Block, row and col selection
5532 //
5533
5534 void wxGrid::SelectRow( int row, bool addToSelected )
5535 {
5536 wxRect r;
5537
5538 if ( IsSelection() && addToSelected )
5539 {
5540 wxRect rect[4];
5541 bool need_refresh[4] = { FALSE, FALSE, FALSE, FALSE };
5542 int i;
5543
5544 wxCoord oldLeft = m_selectedTopLeft.GetCol();
5545 wxCoord oldTop = m_selectedTopLeft.GetRow();
5546 wxCoord oldRight = m_selectedBottomRight.GetCol();
5547 wxCoord oldBottom = m_selectedBottomRight.GetRow();
5548
5549 if ( oldTop > row )
5550 {
5551 need_refresh[0] = TRUE;
5552 rect[0] = BlockToDeviceRect( wxGridCellCoords ( row, 0 ),
5553 wxGridCellCoords ( oldTop - 1,
5554 m_numCols - 1 ) );
5555 m_selectedTopLeft.SetRow( row );
5556 }
5557
5558 if ( oldLeft > 0 )
5559 {
5560 need_refresh[1] = TRUE;
5561 rect[1] = BlockToDeviceRect( wxGridCellCoords ( oldTop, 0 ),
5562 wxGridCellCoords ( oldBottom,
5563 oldLeft - 1 ) );
5564
5565 m_selectedTopLeft.SetCol( 0 );
5566 }
5567
5568 if ( oldBottom < row )
5569 {
5570 need_refresh[2] = TRUE;
5571 rect[2] = BlockToDeviceRect( wxGridCellCoords ( oldBottom + 1, 0 ),
5572 wxGridCellCoords ( row,
5573 m_numCols - 1 ) );
5574 m_selectedBottomRight.SetRow( row );
5575 }
5576
5577 if ( oldRight < m_numCols - 1 )
5578 {
5579 need_refresh[3] = TRUE;
5580 rect[3] = BlockToDeviceRect( wxGridCellCoords ( oldTop ,
5581 oldRight + 1 ),
5582 wxGridCellCoords ( oldBottom,
5583 m_numCols - 1 ) );
5584 m_selectedBottomRight.SetCol( m_numCols - 1 );
5585 }
5586
5587 for (i = 0; i < 4; i++ )
5588 if ( need_refresh[i] && rect[i] != wxGridNoCellRect )
5589 m_gridWin->Refresh( FALSE, &(rect[i]) );
5590 }
5591 else
5592 {
5593 r = SelectionToDeviceRect();
5594 ClearSelection();
5595 if ( r != wxGridNoCellRect ) m_gridWin->Refresh( FALSE, &r );
5596
5597 m_selectedTopLeft.Set( row, 0 );
5598 m_selectedBottomRight.Set( row, m_numCols-1 );
5599 r = SelectionToDeviceRect();
5600 m_gridWin->Refresh( FALSE, &r );
5601 }
5602
5603 wxGridRangeSelectEvent gridEvt( GetId(),
5604 EVT_GRID_RANGE_SELECT,
5605 this,
5606 m_selectedTopLeft,
5607 m_selectedBottomRight );
5608
5609 GetEventHandler()->ProcessEvent(gridEvt);
5610 }
5611
5612
5613 void wxGrid::SelectCol( int col, bool addToSelected )
5614 {
5615 if ( IsSelection() && addToSelected )
5616 {
5617 wxRect rect[4];
5618 bool need_refresh[4] = { FALSE, FALSE, FALSE, FALSE };
5619 int i;
5620
5621 wxCoord oldLeft = m_selectedTopLeft.GetCol();
5622 wxCoord oldTop = m_selectedTopLeft.GetRow();
5623 wxCoord oldRight = m_selectedBottomRight.GetCol();
5624 wxCoord oldBottom = m_selectedBottomRight.GetRow();
5625
5626 if ( oldLeft > col )
5627 {
5628 need_refresh[0] = TRUE;
5629 rect[0] = BlockToDeviceRect( wxGridCellCoords ( 0, col ),
5630 wxGridCellCoords ( m_numRows - 1,
5631 oldLeft - 1 ) );
5632 m_selectedTopLeft.SetCol( col );
5633 }
5634
5635 if ( oldTop > 0 )
5636 {
5637 need_refresh[1] = TRUE;
5638 rect[1] = BlockToDeviceRect( wxGridCellCoords ( 0, oldLeft ),
5639 wxGridCellCoords ( oldTop - 1,
5640 oldRight ) );
5641 m_selectedTopLeft.SetRow( 0 );
5642 }
5643
5644 if ( oldRight < col )
5645 {
5646 need_refresh[2] = TRUE;
5647 rect[2] = BlockToDeviceRect( wxGridCellCoords ( 0, oldRight + 1 ),
5648 wxGridCellCoords ( m_numRows - 1,
5649 col ) );
5650 m_selectedBottomRight.SetCol( col );
5651 }
5652
5653 if ( oldBottom < m_numRows - 1 )
5654 {
5655 need_refresh[3] = TRUE;
5656 rect[3] = BlockToDeviceRect( wxGridCellCoords ( oldBottom + 1,
5657 oldLeft ),
5658 wxGridCellCoords ( m_numRows - 1,
5659 oldRight ) );
5660 m_selectedBottomRight.SetRow( m_numRows - 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 wxRect r;
5670
5671 r = SelectionToDeviceRect();
5672 ClearSelection();
5673 if ( r != wxGridNoCellRect ) m_gridWin->Refresh( FALSE, &r );
5674
5675 m_selectedTopLeft.Set( 0, col );
5676 m_selectedBottomRight.Set( m_numRows-1, col );
5677 r = SelectionToDeviceRect();
5678 m_gridWin->Refresh( FALSE, &r );
5679 }
5680
5681 wxGridRangeSelectEvent gridEvt( GetId(),
5682 EVT_GRID_RANGE_SELECT,
5683 this,
5684 m_selectedTopLeft,
5685 m_selectedBottomRight );
5686
5687 GetEventHandler()->ProcessEvent(gridEvt);
5688 }
5689
5690
5691 void wxGrid::SelectBlock( int topRow, int leftCol, int bottomRow, int rightCol )
5692 {
5693 int temp;
5694 wxGridCellCoords updateTopLeft, updateBottomRight;
5695
5696 if ( topRow > bottomRow )
5697 {
5698 temp = topRow;
5699 topRow = bottomRow;
5700 bottomRow = temp;
5701 }
5702
5703 if ( leftCol > rightCol )
5704 {
5705 temp = leftCol;
5706 leftCol = rightCol;
5707 rightCol = temp;
5708 }
5709
5710 updateTopLeft = wxGridCellCoords( topRow, leftCol );
5711 updateBottomRight = wxGridCellCoords( bottomRow, rightCol );
5712
5713 if ( m_selectedTopLeft != updateTopLeft ||
5714 m_selectedBottomRight != updateBottomRight )
5715 {
5716 // Compute two optimal update rectangles:
5717 // Either one rectangle is a real subset of the
5718 // other, or they are (almost) disjoint!
5719 wxRect rect[4];
5720 bool need_refresh[4] = { FALSE, FALSE, FALSE, FALSE };
5721 int i;
5722
5723 // Store intermediate values
5724 wxCoord oldLeft = m_selectedTopLeft.GetCol();
5725 wxCoord oldTop = m_selectedTopLeft.GetRow();
5726 wxCoord oldRight = m_selectedBottomRight.GetCol();
5727 wxCoord oldBottom = m_selectedBottomRight.GetRow();
5728
5729 // Determine the outer/inner coordinates.
5730 if (oldLeft > leftCol)
5731 {
5732 temp = oldLeft;
5733 oldLeft = leftCol;
5734 leftCol = temp;
5735 }
5736 if (oldTop > topRow )
5737 {
5738 temp = oldTop;
5739 oldTop = topRow;
5740 topRow = temp;
5741 }
5742 if (oldRight < rightCol )
5743 {
5744 temp = oldRight;
5745 oldRight = rightCol;
5746 rightCol = temp;
5747 }
5748 if (oldBottom < bottomRow)
5749 {
5750 temp = oldBottom;
5751 oldBottom = bottomRow;
5752 bottomRow = temp;
5753 }
5754
5755 // Now, either the stuff marked old is the outer
5756 // rectangle or we don't have a situation where one
5757 // is contained in the other.
5758
5759 if ( oldLeft < leftCol )
5760 {
5761 need_refresh[0] = TRUE;
5762 rect[0] = BlockToDeviceRect( wxGridCellCoords ( oldTop,
5763 oldLeft ),
5764 wxGridCellCoords ( oldBottom,
5765 leftCol - 1 ) );
5766 }
5767
5768 if ( oldTop < topRow )
5769 {
5770 need_refresh[1] = TRUE;
5771 rect[1] = BlockToDeviceRect( wxGridCellCoords ( oldTop,
5772 leftCol ),
5773 wxGridCellCoords ( topRow - 1,
5774 rightCol ) );
5775 }
5776
5777 if ( oldRight > rightCol )
5778 {
5779 need_refresh[2] = TRUE;
5780 rect[2] = BlockToDeviceRect( wxGridCellCoords ( oldTop,
5781 rightCol + 1 ),
5782 wxGridCellCoords ( oldBottom,
5783 oldRight ) );
5784 }
5785
5786 if ( oldBottom > bottomRow )
5787 {
5788 need_refresh[3] = TRUE;
5789 rect[3] = BlockToDeviceRect( wxGridCellCoords ( bottomRow + 1,
5790 leftCol ),
5791 wxGridCellCoords ( oldBottom,
5792 rightCol ) );
5793 }
5794
5795
5796 // Change Selection
5797 m_selectedTopLeft = updateTopLeft;
5798 m_selectedBottomRight = updateBottomRight;
5799
5800 // various Refresh() calls
5801 for (i = 0; i < 4; i++ )
5802 if ( need_refresh[i] && rect[i] != wxGridNoCellRect )
5803 m_gridWin->Refresh( FALSE, &(rect[i]) );
5804 }
5805
5806 // only generate an event if the block is not being selected by
5807 // dragging the mouse (in which case the event will be generated in
5808 // the mouse event handler)
5809 if ( !m_isDragging )
5810 {
5811 wxGridRangeSelectEvent gridEvt( GetId(),
5812 EVT_GRID_RANGE_SELECT,
5813 this,
5814 m_selectedTopLeft,
5815 m_selectedBottomRight );
5816
5817 GetEventHandler()->ProcessEvent(gridEvt);
5818 }
5819 }
5820
5821 void wxGrid::SelectAll()
5822 {
5823 m_selectedTopLeft.Set( 0, 0 );
5824 m_selectedBottomRight.Set( m_numRows-1, m_numCols-1 );
5825
5826 m_gridWin->Refresh();
5827 }
5828
5829
5830 void wxGrid::ClearSelection()
5831 {
5832 m_selectedTopLeft = wxGridNoCellCoords;
5833 m_selectedBottomRight = wxGridNoCellCoords;
5834 }
5835
5836
5837 // This function returns the rectangle that encloses the given block
5838 // in device coords clipped to the client size of the grid window.
5839 //
5840 wxRect wxGrid::BlockToDeviceRect( const wxGridCellCoords &topLeft,
5841 const wxGridCellCoords &bottomRight )
5842 {
5843 wxRect rect( wxGridNoCellRect );
5844 wxRect cellRect;
5845
5846 cellRect = CellToRect( topLeft );
5847 if ( cellRect != wxGridNoCellRect )
5848 {
5849 rect = cellRect;
5850 }
5851 else
5852 {
5853 rect = wxRect( 0, 0, 0, 0 );
5854 }
5855
5856 cellRect = CellToRect( bottomRight );
5857 if ( cellRect != wxGridNoCellRect )
5858 {
5859 rect += cellRect;
5860 }
5861 else
5862 {
5863 return wxGridNoCellRect;
5864 }
5865
5866 // convert to scrolled coords
5867 //
5868 int left, top, right, bottom;
5869 CalcScrolledPosition( rect.GetLeft(), rect.GetTop(), &left, &top );
5870 CalcScrolledPosition( rect.GetRight(), rect.GetBottom(), &right, &bottom );
5871
5872 int cw, ch;
5873 m_gridWin->GetClientSize( &cw, &ch );
5874
5875 rect.SetLeft( wxMax(0, left) );
5876 rect.SetTop( wxMax(0, top) );
5877 rect.SetRight( wxMin(cw, right) );
5878 rect.SetBottom( wxMin(ch, bottom) );
5879
5880 return rect;
5881 }
5882
5883
5884
5885 //
5886 // ------ Grid event classes
5887 //
5888
5889 IMPLEMENT_DYNAMIC_CLASS( wxGridEvent, wxEvent )
5890
5891 wxGridEvent::wxGridEvent( int id, wxEventType type, wxObject* obj,
5892 int row, int col, int x, int y,
5893 bool control, bool shift, bool alt, bool meta )
5894 : wxNotifyEvent( type, id )
5895 {
5896 m_row = row;
5897 m_col = col;
5898 m_x = x;
5899 m_y = y;
5900 m_control = control;
5901 m_shift = shift;
5902 m_alt = alt;
5903 m_meta = meta;
5904
5905 SetEventObject(obj);
5906 }
5907
5908
5909 IMPLEMENT_DYNAMIC_CLASS( wxGridSizeEvent, wxEvent )
5910
5911 wxGridSizeEvent::wxGridSizeEvent( int id, wxEventType type, wxObject* obj,
5912 int rowOrCol, int x, int y,
5913 bool control, bool shift, bool alt, bool meta )
5914 : wxNotifyEvent( type, id )
5915 {
5916 m_rowOrCol = rowOrCol;
5917 m_x = x;
5918 m_y = y;
5919 m_control = control;
5920 m_shift = shift;
5921 m_alt = alt;
5922 m_meta = meta;
5923
5924 SetEventObject(obj);
5925 }
5926
5927
5928 IMPLEMENT_DYNAMIC_CLASS( wxGridRangeSelectEvent, wxEvent )
5929
5930 wxGridRangeSelectEvent::wxGridRangeSelectEvent(int id, wxEventType type, wxObject* obj,
5931 const wxGridCellCoords& topLeft,
5932 const wxGridCellCoords& bottomRight,
5933 bool control, bool shift, bool alt, bool meta )
5934 : wxNotifyEvent( type, id )
5935 {
5936 m_topLeft = topLeft;
5937 m_bottomRight = bottomRight;
5938 m_control = control;
5939 m_shift = shift;
5940 m_alt = alt;
5941 m_meta = meta;
5942
5943 SetEventObject(obj);
5944 }
5945
5946
5947 #endif // ifndef wxUSE_NEW_GRID
5948