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