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