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