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