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