]> git.saurik.com Git - wxWidgets.git/blob - src/generic/grid.cpp
068d735b617fa0188c905625fc73e07bdbb5cccb
[wxWidgets.git] / src / generic / grid.cpp
1 ///////////////////////////////////////////////////////////////////////////
2 // Name: generic/grid.cpp
3 // Purpose: wxGrid and related classes
4 // Author: Michael Bedward (based on code by Julian Smart, Robin Dunn)
5 // Modified by: Robin Dunn, Vadim Zeitlin
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 precompilatixon, 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 wxUSE_GRID
34
35 #ifndef WX_PRECOMP
36 #include "wx/utils.h"
37 #include "wx/dcclient.h"
38 #include "wx/settings.h"
39 #include "wx/log.h"
40 #include "wx/textctrl.h"
41 #include "wx/checkbox.h"
42 #include "wx/combobox.h"
43 #include "wx/valtext.h"
44 #endif
45
46 #include "wx/textfile.h"
47 #include "wx/spinctrl.h"
48 #include "wx/tokenzr.h"
49
50 #include "wx/grid.h"
51 #include "wx/generic/gridsel.h"
52
53 #if defined(__WXMOTIF__)
54 #define WXUNUSED_MOTIF(identifier) WXUNUSED(identifier)
55 #else
56 #define WXUNUSED_MOTIF(identifier) identifier
57 #endif
58
59 #if defined(__WXGTK__)
60 #define WXUNUSED_GTK(identifier) WXUNUSED(identifier)
61 #else
62 #define WXUNUSED_GTK(identifier) identifier
63 #endif
64
65 // Required for wxIs... functions
66 #include <ctype.h>
67
68 // ----------------------------------------------------------------------------
69 // array classes
70 // ----------------------------------------------------------------------------
71
72 WX_DEFINE_EXPORTED_ARRAY(wxGridCellAttr *, wxArrayAttrs);
73
74 struct wxGridCellWithAttr
75 {
76 wxGridCellWithAttr(int row, int col, wxGridCellAttr *attr_)
77 : coords(row, col), attr(attr_)
78 {
79 }
80
81 ~wxGridCellWithAttr()
82 {
83 attr->DecRef();
84 }
85
86 wxGridCellCoords coords;
87 wxGridCellAttr *attr;
88
89 // Cannot do this:
90 // DECLARE_NO_COPY_CLASS(wxGridCellWithAttr)
91 // without rewriting the macros, which require a public copy constructor.
92 };
93
94 WX_DECLARE_EXPORTED_OBJARRAY(wxGridCellWithAttr, wxGridCellWithAttrArray);
95
96 #include "wx/arrimpl.cpp"
97
98 WX_DEFINE_OBJARRAY(wxGridCellCoordsArray)
99 WX_DEFINE_OBJARRAY(wxGridCellWithAttrArray)
100
101 // ----------------------------------------------------------------------------
102 // events
103 // ----------------------------------------------------------------------------
104
105 DEFINE_EVENT_TYPE(wxEVT_GRID_CELL_LEFT_CLICK)
106 DEFINE_EVENT_TYPE(wxEVT_GRID_CELL_RIGHT_CLICK)
107 DEFINE_EVENT_TYPE(wxEVT_GRID_CELL_LEFT_DCLICK)
108 DEFINE_EVENT_TYPE(wxEVT_GRID_CELL_RIGHT_DCLICK)
109 DEFINE_EVENT_TYPE(wxEVT_GRID_LABEL_LEFT_CLICK)
110 DEFINE_EVENT_TYPE(wxEVT_GRID_LABEL_RIGHT_CLICK)
111 DEFINE_EVENT_TYPE(wxEVT_GRID_LABEL_LEFT_DCLICK)
112 DEFINE_EVENT_TYPE(wxEVT_GRID_LABEL_RIGHT_DCLICK)
113 DEFINE_EVENT_TYPE(wxEVT_GRID_ROW_SIZE)
114 DEFINE_EVENT_TYPE(wxEVT_GRID_COL_SIZE)
115 DEFINE_EVENT_TYPE(wxEVT_GRID_RANGE_SELECT)
116 DEFINE_EVENT_TYPE(wxEVT_GRID_CELL_CHANGE)
117 DEFINE_EVENT_TYPE(wxEVT_GRID_SELECT_CELL)
118 DEFINE_EVENT_TYPE(wxEVT_GRID_EDITOR_SHOWN)
119 DEFINE_EVENT_TYPE(wxEVT_GRID_EDITOR_HIDDEN)
120 DEFINE_EVENT_TYPE(wxEVT_GRID_EDITOR_CREATED)
121
122 // ----------------------------------------------------------------------------
123 // private classes
124 // ----------------------------------------------------------------------------
125
126 class WXDLLEXPORT wxGridRowLabelWindow : public wxWindow
127 {
128 public:
129 wxGridRowLabelWindow() { m_owner = (wxGrid *)NULL; }
130 wxGridRowLabelWindow( wxGrid *parent, wxWindowID id,
131 const wxPoint &pos, const wxSize &size );
132
133 private:
134 wxGrid *m_owner;
135
136 void OnPaint( wxPaintEvent& event );
137 void OnMouseEvent( wxMouseEvent& event );
138 void OnMouseWheel( wxMouseEvent& event );
139 void OnKeyDown( wxKeyEvent& event );
140 void OnKeyUp( wxKeyEvent& );
141
142 DECLARE_DYNAMIC_CLASS(wxGridRowLabelWindow)
143 DECLARE_EVENT_TABLE()
144 DECLARE_NO_COPY_CLASS(wxGridRowLabelWindow)
145 };
146
147
148 class WXDLLEXPORT wxGridColLabelWindow : public wxWindow
149 {
150 public:
151 wxGridColLabelWindow() { m_owner = (wxGrid *)NULL; }
152 wxGridColLabelWindow( wxGrid *parent, wxWindowID id,
153 const wxPoint &pos, const wxSize &size );
154
155 private:
156 wxGrid *m_owner;
157
158 void OnPaint( wxPaintEvent &event );
159 void OnMouseEvent( wxMouseEvent& event );
160 void OnMouseWheel( wxMouseEvent& event );
161 void OnKeyDown( wxKeyEvent& event );
162 void OnKeyUp( wxKeyEvent& );
163
164 DECLARE_DYNAMIC_CLASS(wxGridColLabelWindow)
165 DECLARE_EVENT_TABLE()
166 DECLARE_NO_COPY_CLASS(wxGridColLabelWindow)
167 };
168
169
170 class WXDLLEXPORT wxGridCornerLabelWindow : public wxWindow
171 {
172 public:
173 wxGridCornerLabelWindow() { m_owner = (wxGrid *)NULL; }
174 wxGridCornerLabelWindow( wxGrid *parent, wxWindowID id,
175 const wxPoint &pos, const wxSize &size );
176
177 private:
178 wxGrid *m_owner;
179
180 void OnMouseEvent( wxMouseEvent& event );
181 void OnMouseWheel( wxMouseEvent& event );
182 void OnKeyDown( wxKeyEvent& event );
183 void OnKeyUp( wxKeyEvent& );
184 void OnPaint( wxPaintEvent& event );
185
186 DECLARE_DYNAMIC_CLASS(wxGridCornerLabelWindow)
187 DECLARE_EVENT_TABLE()
188 DECLARE_NO_COPY_CLASS(wxGridCornerLabelWindow)
189 };
190
191 class WXDLLEXPORT wxGridWindow : public wxWindow
192 {
193 public:
194 wxGridWindow()
195 {
196 m_owner = (wxGrid *)NULL;
197 m_rowLabelWin = (wxGridRowLabelWindow *)NULL;
198 m_colLabelWin = (wxGridColLabelWindow *)NULL;
199 }
200
201 wxGridWindow( wxGrid *parent,
202 wxGridRowLabelWindow *rowLblWin,
203 wxGridColLabelWindow *colLblWin,
204 wxWindowID id, const wxPoint &pos, const wxSize &size );
205 ~wxGridWindow();
206
207 void ScrollWindow( int dx, int dy, const wxRect *rect );
208
209 wxGrid* GetOwner() { return m_owner; }
210
211 private:
212 wxGrid *m_owner;
213 wxGridRowLabelWindow *m_rowLabelWin;
214 wxGridColLabelWindow *m_colLabelWin;
215
216 void OnPaint( wxPaintEvent &event );
217 void OnMouseWheel( wxMouseEvent& event );
218 void OnMouseEvent( wxMouseEvent& event );
219 void OnKeyDown( wxKeyEvent& );
220 void OnKeyUp( wxKeyEvent& );
221 void OnEraseBackground( wxEraseEvent& );
222
223
224 DECLARE_DYNAMIC_CLASS(wxGridWindow)
225 DECLARE_EVENT_TABLE()
226 DECLARE_NO_COPY_CLASS(wxGridWindow)
227 };
228
229
230
231 class wxGridCellEditorEvtHandler : public wxEvtHandler
232 {
233 public:
234 wxGridCellEditorEvtHandler()
235 : m_grid(0), m_editor(0)
236 { }
237 wxGridCellEditorEvtHandler(wxGrid* grid, wxGridCellEditor* editor)
238 : m_grid(grid), m_editor(editor)
239 { }
240
241 void OnKeyDown(wxKeyEvent& event);
242 void OnChar(wxKeyEvent& event);
243
244 private:
245 wxGrid* m_grid;
246 wxGridCellEditor* m_editor;
247 DECLARE_DYNAMIC_CLASS(wxGridCellEditorEvtHandler)
248 DECLARE_EVENT_TABLE()
249 DECLARE_NO_COPY_CLASS(wxGridCellEditorEvtHandler)
250 };
251
252
253 IMPLEMENT_DYNAMIC_CLASS( wxGridCellEditorEvtHandler, wxEvtHandler )
254 BEGIN_EVENT_TABLE( wxGridCellEditorEvtHandler, wxEvtHandler )
255 EVT_KEY_DOWN( wxGridCellEditorEvtHandler::OnKeyDown )
256 EVT_CHAR( wxGridCellEditorEvtHandler::OnChar )
257 END_EVENT_TABLE()
258
259
260
261 // ----------------------------------------------------------------------------
262 // the internal data representation used by wxGridCellAttrProvider
263 // ----------------------------------------------------------------------------
264
265 // this class stores attributes set for cells
266 class WXDLLEXPORT wxGridCellAttrData
267 {
268 public:
269 void SetAttr(wxGridCellAttr *attr, int row, int col);
270 wxGridCellAttr *GetAttr(int row, int col) const;
271 void UpdateAttrRows( size_t pos, int numRows );
272 void UpdateAttrCols( size_t pos, int numCols );
273
274 private:
275 // searches for the attr for given cell, returns wxNOT_FOUND if not found
276 int FindIndex(int row, int col) const;
277
278 wxGridCellWithAttrArray m_attrs;
279 };
280
281 // this class stores attributes set for rows or columns
282 class WXDLLEXPORT wxGridRowOrColAttrData
283 {
284 public:
285 // empty ctor to suppress warnings
286 wxGridRowOrColAttrData() { }
287 ~wxGridRowOrColAttrData();
288
289 void SetAttr(wxGridCellAttr *attr, int rowOrCol);
290 wxGridCellAttr *GetAttr(int rowOrCol) const;
291 void UpdateAttrRowsOrCols( size_t pos, int numRowsOrCols );
292
293 private:
294 wxArrayInt m_rowsOrCols;
295 wxArrayAttrs m_attrs;
296 };
297
298 // NB: this is just a wrapper around 3 objects: one which stores cell
299 // attributes, and 2 others for row/col ones
300 class WXDLLEXPORT wxGridCellAttrProviderData
301 {
302 public:
303 wxGridCellAttrData m_cellAttrs;
304 wxGridRowOrColAttrData m_rowAttrs,
305 m_colAttrs;
306 };
307
308
309 // ----------------------------------------------------------------------------
310 // data structures used for the data type registry
311 // ----------------------------------------------------------------------------
312
313 struct wxGridDataTypeInfo
314 {
315 wxGridDataTypeInfo(const wxString& typeName,
316 wxGridCellRenderer* renderer,
317 wxGridCellEditor* editor)
318 : m_typeName(typeName), m_renderer(renderer), m_editor(editor)
319 { }
320
321 ~wxGridDataTypeInfo()
322 {
323 wxSafeDecRef(m_renderer);
324 wxSafeDecRef(m_editor);
325 }
326
327 wxString m_typeName;
328 wxGridCellRenderer* m_renderer;
329 wxGridCellEditor* m_editor;
330
331 DECLARE_NO_COPY_CLASS(wxGridDataTypeInfo)
332 };
333
334
335 WX_DEFINE_EXPORTED_ARRAY(wxGridDataTypeInfo*, wxGridDataTypeInfoArray);
336
337
338 class WXDLLEXPORT wxGridTypeRegistry
339 {
340 public:
341 wxGridTypeRegistry() {}
342 ~wxGridTypeRegistry();
343
344 void RegisterDataType(const wxString& typeName,
345 wxGridCellRenderer* renderer,
346 wxGridCellEditor* editor);
347
348 // find one of already registered data types
349 int FindRegisteredDataType(const wxString& typeName);
350
351 // try to FindRegisteredDataType(), if this fails and typeName is one of
352 // standard typenames, register it and return its index
353 int FindDataType(const wxString& typeName);
354
355 // try to FindDataType(), if it fails see if it is not one of already
356 // registered data types with some params in which case clone the
357 // registered data type and set params for it
358 int FindOrCloneDataType(const wxString& typeName);
359
360 wxGridCellRenderer* GetRenderer(int index);
361 wxGridCellEditor* GetEditor(int index);
362
363 private:
364 wxGridDataTypeInfoArray m_typeinfo;
365 };
366
367 // ----------------------------------------------------------------------------
368 // conditional compilation
369 // ----------------------------------------------------------------------------
370
371 #ifndef WXGRID_DRAW_LINES
372 #define WXGRID_DRAW_LINES 1
373 #endif
374
375 // ----------------------------------------------------------------------------
376 // globals
377 // ----------------------------------------------------------------------------
378
379 //#define DEBUG_ATTR_CACHE
380 #ifdef DEBUG_ATTR_CACHE
381 static size_t gs_nAttrCacheHits = 0;
382 static size_t gs_nAttrCacheMisses = 0;
383 #endif // DEBUG_ATTR_CACHE
384
385 // ----------------------------------------------------------------------------
386 // constants
387 // ----------------------------------------------------------------------------
388
389 wxGridCellCoords wxGridNoCellCoords( -1, -1 );
390 wxRect wxGridNoCellRect( -1, -1, -1, -1 );
391
392 // scroll line size
393 // TODO: this doesn't work at all, grid cells have different sizes and approx
394 // calculations don't work as because of the size mismatch scrollbars
395 // sometimes fail to be shown when they should be or vice versa
396 //
397 // The scroll bars may be a little flakey once in a while, but that is
398 // surely much less horrible than having scroll lines of only 1!!!
399 // -- Robin
400 //
401 // Well, it's still seriously broken so it might be better but needs
402 // fixing anyhow
403 // -- Vadim
404 static const size_t GRID_SCROLL_LINE_X = 15; // 1;
405 static const size_t GRID_SCROLL_LINE_Y = GRID_SCROLL_LINE_X;
406
407 // the size of hash tables used a bit everywhere (the max number of elements
408 // in these hash tables is the number of rows/columns)
409 static const int GRID_HASH_SIZE = 100;
410
411 // ----------------------------------------------------------------------------
412 // private functions
413 // ----------------------------------------------------------------------------
414
415 static inline int GetScrollX(int x)
416 {
417 return (x + GRID_SCROLL_LINE_X - 1) / GRID_SCROLL_LINE_X;
418 }
419
420 static inline int GetScrollY(int y)
421 {
422 return (y + GRID_SCROLL_LINE_Y - 1) / GRID_SCROLL_LINE_Y;
423 }
424
425 // ============================================================================
426 // implementation
427 // ============================================================================
428
429 // ----------------------------------------------------------------------------
430 // wxGridCellEditor
431 // ----------------------------------------------------------------------------
432
433 wxGridCellEditor::wxGridCellEditor()
434 {
435 m_control = NULL;
436 m_attr = NULL;
437 }
438
439
440 wxGridCellEditor::~wxGridCellEditor()
441 {
442 Destroy();
443 }
444
445 void wxGridCellEditor::Create(wxWindow* WXUNUSED(parent),
446 wxWindowID WXUNUSED(id),
447 wxEvtHandler* evtHandler)
448 {
449 if ( evtHandler )
450 m_control->PushEventHandler(evtHandler);
451 }
452
453 void wxGridCellEditor::PaintBackground(const wxRect& rectCell,
454 wxGridCellAttr *attr)
455 {
456 // erase the background because we might not fill the cell
457 wxClientDC dc(m_control->GetParent());
458 wxGridWindow* gridWindow = wxDynamicCast(m_control->GetParent(), wxGridWindow);
459 if (gridWindow)
460 gridWindow->GetOwner()->PrepareDC(dc);
461
462 dc.SetPen(*wxTRANSPARENT_PEN);
463 dc.SetBrush(wxBrush(attr->GetBackgroundColour(), wxSOLID));
464 dc.DrawRectangle(rectCell);
465
466 // redraw the control we just painted over
467 m_control->Refresh();
468 }
469
470 void wxGridCellEditor::Destroy()
471 {
472 if (m_control)
473 {
474 m_control->PopEventHandler(TRUE /* delete it*/);
475
476 m_control->Destroy();
477 m_control = NULL;
478 }
479 }
480
481 void wxGridCellEditor::Show(bool show, wxGridCellAttr *attr)
482 {
483 wxASSERT_MSG(m_control,
484 wxT("The wxGridCellEditor must be Created first!"));
485 m_control->Show(show);
486
487 if ( show )
488 {
489 // set the colours/fonts if we have any
490 if ( attr )
491 {
492 m_colFgOld = m_control->GetForegroundColour();
493 m_control->SetForegroundColour(attr->GetTextColour());
494
495 m_colBgOld = m_control->GetBackgroundColour();
496 m_control->SetBackgroundColour(attr->GetBackgroundColour());
497
498 m_fontOld = m_control->GetFont();
499 m_control->SetFont(attr->GetFont());
500
501 // can't do anything more in the base class version, the other
502 // attributes may only be used by the derived classes
503 }
504 }
505 else
506 {
507 // restore the standard colours fonts
508 if ( m_colFgOld.Ok() )
509 {
510 m_control->SetForegroundColour(m_colFgOld);
511 m_colFgOld = wxNullColour;
512 }
513
514 if ( m_colBgOld.Ok() )
515 {
516 m_control->SetBackgroundColour(m_colBgOld);
517 m_colBgOld = wxNullColour;
518 }
519
520 if ( m_fontOld.Ok() )
521 {
522 m_control->SetFont(m_fontOld);
523 m_fontOld = wxNullFont;
524 }
525 }
526 }
527
528 void wxGridCellEditor::SetSize(const wxRect& rect)
529 {
530 wxASSERT_MSG(m_control,
531 wxT("The wxGridCellEditor must be Created first!"));
532 m_control->SetSize(rect, wxSIZE_ALLOW_MINUS_ONE);
533 }
534
535 void wxGridCellEditor::HandleReturn(wxKeyEvent& event)
536 {
537 event.Skip();
538 }
539
540 bool wxGridCellEditor::IsAcceptedKey(wxKeyEvent& event)
541 {
542 // accept the simple key presses, not anything with Ctrl/Alt/Meta
543 return !(event.ControlDown() || event.AltDown());
544 }
545
546 void wxGridCellEditor::StartingKey(wxKeyEvent& event)
547 {
548 event.Skip();
549 }
550
551 void wxGridCellEditor::StartingClick()
552 {
553 }
554
555 #if wxUSE_TEXTCTRL
556
557 // ----------------------------------------------------------------------------
558 // wxGridCellTextEditor
559 // ----------------------------------------------------------------------------
560
561 wxGridCellTextEditor::wxGridCellTextEditor()
562 {
563 m_maxChars = 0;
564 }
565
566 void wxGridCellTextEditor::Create(wxWindow* parent,
567 wxWindowID id,
568 wxEvtHandler* evtHandler)
569 {
570 m_control = new wxTextCtrl(parent, id, wxEmptyString,
571 wxDefaultPosition, wxDefaultSize
572 #if defined(__WXMSW__)
573 , wxTE_PROCESS_TAB | wxTE_MULTILINE |
574 wxTE_NO_VSCROLL | wxTE_AUTO_SCROLL
575 #endif
576 );
577
578 // set max length allowed in the textctrl, if the parameter was set
579 if (m_maxChars != 0)
580 {
581 ((wxTextCtrl*)m_control)->SetMaxLength(m_maxChars);
582 }
583
584 wxGridCellEditor::Create(parent, id, evtHandler);
585 }
586
587 void wxGridCellTextEditor::PaintBackground(const wxRect& WXUNUSED(rectCell),
588 wxGridCellAttr * WXUNUSED(attr))
589 {
590 // as we fill the entire client area, don't do anything here to minimize
591 // flicker
592 }
593
594 void wxGridCellTextEditor::SetSize(const wxRect& rectOrig)
595 {
596 wxRect rect(rectOrig);
597
598 // Make the edit control large enough to allow for internal
599 // margins
600 //
601 // TODO: remove this if the text ctrl sizing is improved esp. for
602 // unix
603 //
604 #if defined(__WXGTK__)
605 if (rect.x != 0)
606 {
607 rect.x += 1;
608 rect.y += 1;
609 rect.width -= 1;
610 rect.height -= 1;
611 }
612 #else // !GTK
613 int extra_x = ( rect.x > 2 )? 2 : 1;
614
615 // MB: treat MSW separately here otherwise the caret doesn't show
616 // when the editor is in the first row.
617 #if defined(__WXMSW__)
618 int extra_y = 2;
619 #else
620 int extra_y = ( rect.y > 2 )? 2 : 1;
621 #endif // MSW
622
623 #if defined(__WXMOTIF__)
624 extra_x *= 2;
625 extra_y *= 2;
626 #endif
627 rect.SetLeft( wxMax(0, rect.x - extra_x) );
628 rect.SetTop( wxMax(0, rect.y - extra_y) );
629 rect.SetRight( rect.GetRight() + 2*extra_x );
630 rect.SetBottom( rect.GetBottom() + 2*extra_y );
631 #endif // GTK/!GTK
632
633 wxGridCellEditor::SetSize(rect);
634 }
635
636 void wxGridCellTextEditor::BeginEdit(int row, int col, wxGrid* grid)
637 {
638 wxASSERT_MSG(m_control,
639 wxT("The wxGridCellEditor must be Created first!"));
640
641 m_startValue = grid->GetTable()->GetValue(row, col);
642
643 DoBeginEdit(m_startValue);
644 }
645
646 void wxGridCellTextEditor::DoBeginEdit(const wxString& startValue)
647 {
648 Text()->SetValue(startValue);
649 Text()->SetInsertionPointEnd();
650 Text()->SetSelection(-1,-1);
651 Text()->SetFocus();
652 }
653
654 bool wxGridCellTextEditor::EndEdit(int row, int col,
655 wxGrid* grid)
656 {
657 wxASSERT_MSG(m_control,
658 wxT("The wxGridCellEditor must be Created first!"));
659
660 bool changed = FALSE;
661 wxString value = Text()->GetValue();
662 if (value != m_startValue)
663 changed = TRUE;
664
665 if (changed)
666 grid->GetTable()->SetValue(row, col, value);
667
668 m_startValue = wxEmptyString;
669 Text()->SetValue(m_startValue);
670
671 return changed;
672 }
673
674
675 void wxGridCellTextEditor::Reset()
676 {
677 wxASSERT_MSG(m_control,
678 wxT("The wxGridCellEditor must be Created first!"));
679
680 DoReset(m_startValue);
681 }
682
683 void wxGridCellTextEditor::DoReset(const wxString& startValue)
684 {
685 Text()->SetValue(startValue);
686 Text()->SetInsertionPointEnd();
687 }
688
689 bool wxGridCellTextEditor::IsAcceptedKey(wxKeyEvent& event)
690 {
691 if ( wxGridCellEditor::IsAcceptedKey(event) )
692 {
693 int keycode = event.GetKeyCode();
694 switch ( keycode )
695 {
696 case WXK_NUMPAD0:
697 case WXK_NUMPAD1:
698 case WXK_NUMPAD2:
699 case WXK_NUMPAD3:
700 case WXK_NUMPAD4:
701 case WXK_NUMPAD5:
702 case WXK_NUMPAD6:
703 case WXK_NUMPAD7:
704 case WXK_NUMPAD8:
705 case WXK_NUMPAD9:
706 case WXK_MULTIPLY:
707 case WXK_NUMPAD_MULTIPLY:
708 case WXK_ADD:
709 case WXK_NUMPAD_ADD:
710 case WXK_SUBTRACT:
711 case WXK_NUMPAD_SUBTRACT:
712 case WXK_DECIMAL:
713 case WXK_NUMPAD_DECIMAL:
714 case WXK_DIVIDE:
715 case WXK_NUMPAD_DIVIDE:
716 return TRUE;
717
718 default:
719 // accept 8 bit chars too if isprint() agrees
720 if ( (keycode < 255) && (isprint(keycode)) )
721 return TRUE;
722 }
723 }
724
725 return FALSE;
726 }
727
728 void wxGridCellTextEditor::StartingKey(wxKeyEvent& event)
729 {
730 if ( !Text()->EmulateKeyPress(event) )
731 {
732 event.Skip();
733 }
734 }
735
736 void wxGridCellTextEditor::HandleReturn( wxKeyEvent&
737 WXUNUSED_GTK(WXUNUSED_MOTIF(event)) )
738 {
739 #if defined(__WXMOTIF__) || defined(__WXGTK__)
740 // wxMotif needs a little extra help...
741 size_t pos = (size_t)( Text()->GetInsertionPoint() );
742 wxString s( Text()->GetValue() );
743 s = s.Left(pos) + wxT("\n") + s.Mid(pos);
744 Text()->SetValue(s);
745 Text()->SetInsertionPoint( pos );
746 #else
747 // the other ports can handle a Return key press
748 //
749 event.Skip();
750 #endif
751 }
752
753 void wxGridCellTextEditor::SetParameters(const wxString& params)
754 {
755 if ( !params )
756 {
757 // reset to default
758 m_maxChars = 0;
759 }
760 else
761 {
762 long tmp;
763 if ( !params.ToLong(&tmp) )
764 {
765 wxLogDebug(_T("Invalid wxGridCellTextEditor parameter string '%s' ignored"), params.c_str());
766 }
767 else
768 {
769 m_maxChars = (size_t)tmp;
770 }
771 }
772 }
773
774 // return the value in the text control
775 wxString wxGridCellTextEditor::GetValue() const
776 {
777 return Text()->GetValue();
778 }
779
780 // ----------------------------------------------------------------------------
781 // wxGridCellNumberEditor
782 // ----------------------------------------------------------------------------
783
784 wxGridCellNumberEditor::wxGridCellNumberEditor(int min, int max)
785 {
786 m_min = min;
787 m_max = max;
788 }
789
790 void wxGridCellNumberEditor::Create(wxWindow* parent,
791 wxWindowID id,
792 wxEvtHandler* evtHandler)
793 {
794 if ( HasRange() )
795 {
796 // create a spin ctrl
797 m_control = new wxSpinCtrl(parent, -1, wxEmptyString,
798 wxDefaultPosition, wxDefaultSize,
799 wxSP_ARROW_KEYS,
800 m_min, m_max);
801
802 wxGridCellEditor::Create(parent, id, evtHandler);
803 }
804 else
805 {
806 // just a text control
807 wxGridCellTextEditor::Create(parent, id, evtHandler);
808
809 #if wxUSE_VALIDATORS
810 Text()->SetValidator(wxTextValidator(wxFILTER_NUMERIC));
811 #endif // wxUSE_VALIDATORS
812 }
813 }
814
815 void wxGridCellNumberEditor::BeginEdit(int row, int col, wxGrid* grid)
816 {
817 // first get the value
818 wxGridTableBase *table = grid->GetTable();
819 if ( table->CanGetValueAs(row, col, wxGRID_VALUE_NUMBER) )
820 {
821 m_valueOld = table->GetValueAsLong(row, col);
822 }
823 else
824 {
825 m_valueOld = 0;
826 wxString sValue = table->GetValue(row, col);
827 if (! sValue.ToLong(&m_valueOld) && ! sValue.IsEmpty())
828 {
829 wxFAIL_MSG( _T("this cell doesn't have numeric value") );
830 return;
831 }
832 }
833
834 if ( HasRange() )
835 {
836 Spin()->SetValue((int)m_valueOld);
837 Spin()->SetFocus();
838 }
839 else
840 {
841 DoBeginEdit(GetString());
842 }
843 }
844
845 bool wxGridCellNumberEditor::EndEdit(int row, int col,
846 wxGrid* grid)
847 {
848 bool changed;
849 long value = 0;
850 wxString text;
851
852 if ( HasRange() )
853 {
854 value = Spin()->GetValue();
855 changed = value != m_valueOld;
856 if (changed)
857 text = wxString::Format(wxT("%ld"), value);
858 }
859 else
860 {
861 text = Text()->GetValue();
862 changed = (text.IsEmpty() || text.ToLong(&value)) && (value != m_valueOld);
863 }
864
865 if ( changed )
866 {
867 if (grid->GetTable()->CanSetValueAs(row, col, wxGRID_VALUE_NUMBER))
868 grid->GetTable()->SetValueAsLong(row, col, value);
869 else
870 grid->GetTable()->SetValue(row, col, text);
871 }
872
873 return changed;
874 }
875
876 void wxGridCellNumberEditor::Reset()
877 {
878 if ( HasRange() )
879 {
880 Spin()->SetValue((int)m_valueOld);
881 }
882 else
883 {
884 DoReset(GetString());
885 }
886 }
887
888 bool wxGridCellNumberEditor::IsAcceptedKey(wxKeyEvent& event)
889 {
890 if ( wxGridCellEditor::IsAcceptedKey(event) )
891 {
892 int keycode = event.GetKeyCode();
893 switch ( keycode )
894 {
895 case WXK_NUMPAD0:
896 case WXK_NUMPAD1:
897 case WXK_NUMPAD2:
898 case WXK_NUMPAD3:
899 case WXK_NUMPAD4:
900 case WXK_NUMPAD5:
901 case WXK_NUMPAD6:
902 case WXK_NUMPAD7:
903 case WXK_NUMPAD8:
904 case WXK_NUMPAD9:
905 case WXK_ADD:
906 case WXK_NUMPAD_ADD:
907 case WXK_SUBTRACT:
908 case WXK_NUMPAD_SUBTRACT:
909 case WXK_UP:
910 case WXK_DOWN:
911 return TRUE;
912
913 default:
914 if ( (keycode < 128) && isdigit(keycode) )
915 return TRUE;
916 }
917 }
918
919 return FALSE;
920 }
921
922 void wxGridCellNumberEditor::StartingKey(wxKeyEvent& event)
923 {
924 if ( !HasRange() )
925 {
926 int keycode = event.GetKeyCode();
927 if ( isdigit(keycode) || keycode == '+' || keycode == '-'
928 || keycode == WXK_NUMPAD0
929 || keycode == WXK_NUMPAD1
930 || keycode == WXK_NUMPAD2
931 || keycode == WXK_NUMPAD3
932 || keycode == WXK_NUMPAD4
933 || keycode == WXK_NUMPAD5
934 || keycode == WXK_NUMPAD6
935 || keycode == WXK_NUMPAD7
936 || keycode == WXK_NUMPAD8
937 || keycode == WXK_NUMPAD9
938 || keycode == WXK_ADD
939 || keycode == WXK_NUMPAD_ADD
940 || keycode == WXK_SUBTRACT
941 || keycode == WXK_NUMPAD_SUBTRACT)
942 {
943 wxGridCellTextEditor::StartingKey(event);
944
945 // skip Skip() below
946 return;
947 }
948 }
949
950 event.Skip();
951 }
952
953 void wxGridCellNumberEditor::SetParameters(const wxString& params)
954 {
955 if ( !params )
956 {
957 // reset to default
958 m_min =
959 m_max = -1;
960 }
961 else
962 {
963 long tmp;
964 if ( params.BeforeFirst(_T(',')).ToLong(&tmp) )
965 {
966 m_min = (int)tmp;
967
968 if ( params.AfterFirst(_T(',')).ToLong(&tmp) )
969 {
970 m_max = (int)tmp;
971
972 // skip the error message below
973 return;
974 }
975 }
976
977 wxLogDebug(_T("Invalid wxGridCellNumberEditor parameter string '%s' ignored"), params.c_str());
978 }
979 }
980
981 // return the value in the spin control if it is there (the text control otherwise)
982 wxString wxGridCellNumberEditor::GetValue() const
983 {
984 wxString s;
985
986 if( HasRange() )
987 {
988 long value = Spin()->GetValue();
989 s.Printf(wxT("%ld"), value);
990 }
991 else
992 {
993 s = Text()->GetValue();
994 }
995 return s;
996 }
997
998 // ----------------------------------------------------------------------------
999 // wxGridCellFloatEditor
1000 // ----------------------------------------------------------------------------
1001
1002 wxGridCellFloatEditor::wxGridCellFloatEditor(int width, int precision)
1003 {
1004 m_width = width;
1005 m_precision = precision;
1006 }
1007
1008 void wxGridCellFloatEditor::Create(wxWindow* parent,
1009 wxWindowID id,
1010 wxEvtHandler* evtHandler)
1011 {
1012 wxGridCellTextEditor::Create(parent, id, evtHandler);
1013
1014 #if wxUSE_VALIDATORS
1015 Text()->SetValidator(wxTextValidator(wxFILTER_NUMERIC));
1016 #endif // wxUSE_VALIDATORS
1017 }
1018
1019 void wxGridCellFloatEditor::BeginEdit(int row, int col, wxGrid* grid)
1020 {
1021 // first get the value
1022 wxGridTableBase *table = grid->GetTable();
1023 if ( table->CanGetValueAs(row, col, wxGRID_VALUE_FLOAT) )
1024 {
1025 m_valueOld = table->GetValueAsDouble(row, col);
1026 }
1027 else
1028 {
1029 m_valueOld = 0.0;
1030 wxString sValue = table->GetValue(row, col);
1031 if (! sValue.ToDouble(&m_valueOld) && ! sValue.IsEmpty())
1032 {
1033 wxFAIL_MSG( _T("this cell doesn't have float value") );
1034 return;
1035 }
1036 }
1037
1038 DoBeginEdit(GetString());
1039 }
1040
1041 bool wxGridCellFloatEditor::EndEdit(int row, int col,
1042 wxGrid* grid)
1043 {
1044 double value = 0.0;
1045 wxString text(Text()->GetValue());
1046
1047 if ( (text.IsEmpty() || text.ToDouble(&value)) && (value != m_valueOld) )
1048 {
1049 if (grid->GetTable()->CanSetValueAs(row, col, wxGRID_VALUE_FLOAT))
1050 grid->GetTable()->SetValueAsDouble(row, col, value);
1051 else
1052 grid->GetTable()->SetValue(row, col, text);
1053
1054 return TRUE;
1055 }
1056 return FALSE;
1057 }
1058
1059 void wxGridCellFloatEditor::Reset()
1060 {
1061 DoReset(GetString());
1062 }
1063
1064 void wxGridCellFloatEditor::StartingKey(wxKeyEvent& event)
1065 {
1066 int keycode = event.GetKeyCode();
1067 if ( isdigit(keycode) || keycode == '+' || keycode == '-' || keycode == '.'
1068 || keycode == WXK_NUMPAD0
1069 || keycode == WXK_NUMPAD1
1070 || keycode == WXK_NUMPAD2
1071 || keycode == WXK_NUMPAD3
1072 || keycode == WXK_NUMPAD4
1073 || keycode == WXK_NUMPAD5
1074 || keycode == WXK_NUMPAD6
1075 || keycode == WXK_NUMPAD7
1076 || keycode == WXK_NUMPAD8
1077 || keycode == WXK_NUMPAD9
1078 || keycode == WXK_ADD
1079 || keycode == WXK_NUMPAD_ADD
1080 || keycode == WXK_SUBTRACT
1081 || keycode == WXK_NUMPAD_SUBTRACT)
1082 {
1083 wxGridCellTextEditor::StartingKey(event);
1084
1085 // skip Skip() below
1086 return;
1087 }
1088
1089 event.Skip();
1090 }
1091
1092 void wxGridCellFloatEditor::SetParameters(const wxString& params)
1093 {
1094 if ( !params )
1095 {
1096 // reset to default
1097 m_width =
1098 m_precision = -1;
1099 }
1100 else
1101 {
1102 long tmp;
1103 if ( params.BeforeFirst(_T(',')).ToLong(&tmp) )
1104 {
1105 m_width = (int)tmp;
1106
1107 if ( params.AfterFirst(_T(',')).ToLong(&tmp) )
1108 {
1109 m_precision = (int)tmp;
1110
1111 // skip the error message below
1112 return;
1113 }
1114 }
1115
1116 wxLogDebug(_T("Invalid wxGridCellFloatEditor parameter string '%s' ignored"), params.c_str());
1117 }
1118 }
1119
1120 wxString wxGridCellFloatEditor::GetString() const
1121 {
1122 wxString fmt;
1123 if ( m_width == -1 )
1124 {
1125 // default width/precision
1126 fmt = _T("%f");
1127 }
1128 else if ( m_precision == -1 )
1129 {
1130 // default precision
1131 fmt.Printf(_T("%%%d.f"), m_width);
1132 }
1133 else
1134 {
1135 fmt.Printf(_T("%%%d.%df"), m_width, m_precision);
1136 }
1137
1138 return wxString::Format(fmt, m_valueOld);
1139 }
1140
1141 bool wxGridCellFloatEditor::IsAcceptedKey(wxKeyEvent& event)
1142 {
1143 if ( wxGridCellEditor::IsAcceptedKey(event) )
1144 {
1145 int keycode = event.GetKeyCode();
1146 switch ( keycode )
1147 {
1148 case WXK_NUMPAD0:
1149 case WXK_NUMPAD1:
1150 case WXK_NUMPAD2:
1151 case WXK_NUMPAD3:
1152 case WXK_NUMPAD4:
1153 case WXK_NUMPAD5:
1154 case WXK_NUMPAD6:
1155 case WXK_NUMPAD7:
1156 case WXK_NUMPAD8:
1157 case WXK_NUMPAD9:
1158 case WXK_ADD:
1159 case WXK_NUMPAD_ADD:
1160 case WXK_SUBTRACT:
1161 case WXK_NUMPAD_SUBTRACT:
1162 case WXK_DECIMAL:
1163 case WXK_NUMPAD_DECIMAL:
1164 return TRUE;
1165
1166 default:
1167 // additionally accept 'e' as in '1e+6'
1168 if ( (keycode < 128) &&
1169 (isdigit(keycode) || tolower(keycode) == 'e') )
1170 return TRUE;
1171 }
1172 }
1173
1174 return FALSE;
1175 }
1176
1177 #endif // wxUSE_TEXTCTRL
1178
1179 #if wxUSE_CHECKBOX
1180
1181 // ----------------------------------------------------------------------------
1182 // wxGridCellBoolEditor
1183 // ----------------------------------------------------------------------------
1184
1185 void wxGridCellBoolEditor::Create(wxWindow* parent,
1186 wxWindowID id,
1187 wxEvtHandler* evtHandler)
1188 {
1189 m_control = new wxCheckBox(parent, id, wxEmptyString,
1190 wxDefaultPosition, wxDefaultSize,
1191 wxNO_BORDER);
1192
1193 wxGridCellEditor::Create(parent, id, evtHandler);
1194 }
1195
1196 void wxGridCellBoolEditor::SetSize(const wxRect& r)
1197 {
1198 bool resize = FALSE;
1199 wxSize size = m_control->GetSize();
1200 wxCoord minSize = wxMin(r.width, r.height);
1201
1202 // check if the checkbox is not too big/small for this cell
1203 wxSize sizeBest = m_control->GetBestSize();
1204 if ( !(size == sizeBest) )
1205 {
1206 // reset to default size if it had been made smaller
1207 size = sizeBest;
1208
1209 resize = TRUE;
1210 }
1211
1212 if ( size.x >= minSize || size.y >= minSize )
1213 {
1214 // leave 1 pixel margin
1215 size.x = size.y = minSize - 2;
1216
1217 resize = TRUE;
1218 }
1219
1220 if ( resize )
1221 {
1222 m_control->SetSize(size);
1223 }
1224
1225 // position it in the centre of the rectangle (TODO: support alignment?)
1226
1227 #if defined(__WXGTK__) || defined (__WXMOTIF__)
1228 // the checkbox without label still has some space to the right in wxGTK,
1229 // so shift it to the right
1230 size.x -= 8;
1231 #elif defined(__WXMSW__)
1232 // here too, but in other way
1233 size.x += 1;
1234 size.y -= 2;
1235 #endif
1236
1237 int hAlign = wxALIGN_CENTRE;
1238 int vAlign = wxALIGN_CENTRE;
1239 if (GetCellAttr())
1240 GetCellAttr()->GetAlignment(& hAlign, & vAlign);
1241
1242 int x = 0, y = 0;
1243 if (hAlign == wxALIGN_LEFT)
1244 {
1245 x = r.x + 2;
1246 #ifdef __WXMSW__
1247 x += 2;
1248 #endif
1249 y = r.y + r.height/2 - size.y/2;
1250 }
1251 else if (hAlign == wxALIGN_RIGHT)
1252 {
1253 x = r.x + r.width - size.x - 2;
1254 y = r.y + r.height/2 - size.y/2;
1255 }
1256 else if (hAlign == wxALIGN_CENTRE)
1257 {
1258 x = r.x + r.width/2 - size.x/2;
1259 y = r.y + r.height/2 - size.y/2;
1260 }
1261
1262 m_control->Move(x, y);
1263 }
1264
1265 void wxGridCellBoolEditor::Show(bool show, wxGridCellAttr *attr)
1266 {
1267 m_control->Show(show);
1268
1269 if ( show )
1270 {
1271 wxColour colBg = attr ? attr->GetBackgroundColour() : *wxLIGHT_GREY;
1272 CBox()->SetBackgroundColour(colBg);
1273 }
1274 }
1275
1276 void wxGridCellBoolEditor::BeginEdit(int row, int col, wxGrid* grid)
1277 {
1278 wxASSERT_MSG(m_control,
1279 wxT("The wxGridCellEditor must be Created first!"));
1280
1281 if (grid->GetTable()->CanGetValueAs(row, col, wxGRID_VALUE_BOOL))
1282 m_startValue = grid->GetTable()->GetValueAsBool(row, col);
1283 else
1284 {
1285 wxString cellval( grid->GetTable()->GetValue(row, col) );
1286 m_startValue = !( !cellval || (cellval == wxT("0")) );
1287 }
1288 CBox()->SetValue(m_startValue);
1289 CBox()->SetFocus();
1290 }
1291
1292 bool wxGridCellBoolEditor::EndEdit(int row, int col,
1293 wxGrid* grid)
1294 {
1295 wxASSERT_MSG(m_control,
1296 wxT("The wxGridCellEditor must be Created first!"));
1297
1298 bool changed = FALSE;
1299 bool value = CBox()->GetValue();
1300 if ( value != m_startValue )
1301 changed = TRUE;
1302
1303 if ( changed )
1304 {
1305 if (grid->GetTable()->CanGetValueAs(row, col, wxGRID_VALUE_BOOL))
1306 grid->GetTable()->SetValueAsBool(row, col, value);
1307 else
1308 grid->GetTable()->SetValue(row, col, value ? _T("1") : wxEmptyString);
1309 }
1310
1311 return changed;
1312 }
1313
1314 void wxGridCellBoolEditor::Reset()
1315 {
1316 wxASSERT_MSG(m_control,
1317 wxT("The wxGridCellEditor must be Created first!"));
1318
1319 CBox()->SetValue(m_startValue);
1320 }
1321
1322 void wxGridCellBoolEditor::StartingClick()
1323 {
1324 CBox()->SetValue(!CBox()->GetValue());
1325 }
1326
1327 bool wxGridCellBoolEditor::IsAcceptedKey(wxKeyEvent& event)
1328 {
1329 if ( wxGridCellEditor::IsAcceptedKey(event) )
1330 {
1331 int keycode = event.GetKeyCode();
1332 switch ( keycode )
1333 {
1334 case WXK_MULTIPLY:
1335 case WXK_NUMPAD_MULTIPLY:
1336 case WXK_ADD:
1337 case WXK_NUMPAD_ADD:
1338 case WXK_SUBTRACT:
1339 case WXK_NUMPAD_SUBTRACT:
1340 case WXK_SPACE:
1341 case '+':
1342 case '-':
1343 return TRUE;
1344 }
1345 }
1346
1347 return FALSE;
1348 }
1349
1350 // return the value as "1" for true and the empty string for false
1351 wxString wxGridCellBoolEditor::GetValue() const
1352 {
1353 bool bSet = CBox()->GetValue();
1354 return bSet ? _T("1") : wxEmptyString;
1355 }
1356
1357 #endif // wxUSE_CHECKBOX
1358
1359 #if wxUSE_COMBOBOX
1360
1361 // ----------------------------------------------------------------------------
1362 // wxGridCellChoiceEditor
1363 // ----------------------------------------------------------------------------
1364
1365 wxGridCellChoiceEditor::wxGridCellChoiceEditor(size_t count,
1366 const wxString choices[],
1367 bool allowOthers)
1368 : m_allowOthers(allowOthers)
1369 {
1370 if ( count )
1371 {
1372 m_choices.Alloc(count);
1373 for ( size_t n = 0; n < count; n++ )
1374 {
1375 m_choices.Add(choices[n]);
1376 }
1377 }
1378 }
1379
1380 wxGridCellEditor *wxGridCellChoiceEditor::Clone() const
1381 {
1382 wxGridCellChoiceEditor *editor = new wxGridCellChoiceEditor;
1383 editor->m_allowOthers = m_allowOthers;
1384 editor->m_choices = m_choices;
1385
1386 return editor;
1387 }
1388
1389 void wxGridCellChoiceEditor::Create(wxWindow* parent,
1390 wxWindowID id,
1391 wxEvtHandler* evtHandler)
1392 {
1393 size_t count = m_choices.GetCount();
1394 wxString *choices = new wxString[count];
1395 for ( size_t n = 0; n < count; n++ )
1396 {
1397 choices[n] = m_choices[n];
1398 }
1399
1400 m_control = new wxComboBox(parent, id, wxEmptyString,
1401 wxDefaultPosition, wxDefaultSize,
1402 count, choices,
1403 m_allowOthers ? 0 : wxCB_READONLY);
1404
1405 delete [] choices;
1406
1407 wxGridCellEditor::Create(parent, id, evtHandler);
1408 }
1409
1410 void wxGridCellChoiceEditor::PaintBackground(const wxRect& rectCell,
1411 wxGridCellAttr * attr)
1412 {
1413 // as we fill the entire client area, don't do anything here to minimize
1414 // flicker
1415
1416 // TODO: It doesn't actually fill the client area since the height of a
1417 // combo always defaults to the standard... Until someone has time to
1418 // figure out the right rectangle to paint, just do it the normal way...
1419 wxGridCellEditor::PaintBackground(rectCell, attr);
1420 }
1421
1422 void wxGridCellChoiceEditor::BeginEdit(int row, int col, wxGrid* grid)
1423 {
1424 wxASSERT_MSG(m_control,
1425 wxT("The wxGridCellEditor must be Created first!"));
1426
1427 m_startValue = grid->GetTable()->GetValue(row, col);
1428
1429 if (m_allowOthers)
1430 Combo()->SetValue(m_startValue);
1431 else
1432 {
1433 // find the right position, or default to the first if not found
1434 int pos = Combo()->FindString(m_startValue);
1435 if (pos == -1)
1436 pos = 0;
1437 Combo()->SetSelection(pos);
1438 }
1439 Combo()->SetInsertionPointEnd();
1440 Combo()->SetFocus();
1441 }
1442
1443 bool wxGridCellChoiceEditor::EndEdit(int row, int col,
1444 wxGrid* grid)
1445 {
1446 wxString value = Combo()->GetValue();
1447 bool changed = value != m_startValue;
1448
1449 if ( changed )
1450 grid->GetTable()->SetValue(row, col, value);
1451
1452 m_startValue = wxEmptyString;
1453 if (m_allowOthers)
1454 Combo()->SetValue(m_startValue);
1455 else
1456 Combo()->SetSelection(0);
1457
1458 return changed;
1459 }
1460
1461 void wxGridCellChoiceEditor::Reset()
1462 {
1463 Combo()->SetValue(m_startValue);
1464 Combo()->SetInsertionPointEnd();
1465 }
1466
1467 void wxGridCellChoiceEditor::SetParameters(const wxString& params)
1468 {
1469 if ( !params )
1470 {
1471 // what can we do?
1472 return;
1473 }
1474
1475 m_choices.Empty();
1476
1477 wxStringTokenizer tk(params, _T(','));
1478 while ( tk.HasMoreTokens() )
1479 {
1480 m_choices.Add(tk.GetNextToken());
1481 }
1482 }
1483
1484 // return the value in the text control
1485 wxString wxGridCellChoiceEditor::GetValue() const
1486 {
1487 return Combo()->GetValue();
1488 }
1489
1490 #endif // wxUSE_COMBOBOX
1491
1492 // ----------------------------------------------------------------------------
1493 // wxGridCellEditorEvtHandler
1494 // ----------------------------------------------------------------------------
1495
1496 void wxGridCellEditorEvtHandler::OnKeyDown(wxKeyEvent& event)
1497 {
1498 switch ( event.GetKeyCode() )
1499 {
1500 case WXK_ESCAPE:
1501 m_editor->Reset();
1502 m_grid->DisableCellEditControl();
1503 break;
1504
1505 case WXK_TAB:
1506 m_grid->GetEventHandler()->ProcessEvent( event );
1507 break;
1508
1509 case WXK_RETURN:
1510 case WXK_NUMPAD_ENTER:
1511 if (!m_grid->GetEventHandler()->ProcessEvent(event))
1512 m_editor->HandleReturn(event);
1513 break;
1514
1515
1516 default:
1517 event.Skip();
1518 }
1519 }
1520
1521 void wxGridCellEditorEvtHandler::OnChar(wxKeyEvent& event)
1522 {
1523 switch ( event.GetKeyCode() )
1524 {
1525 case WXK_ESCAPE:
1526 case WXK_TAB:
1527 case WXK_RETURN:
1528 case WXK_NUMPAD_ENTER:
1529 break;
1530
1531 default:
1532 event.Skip();
1533 }
1534 }
1535
1536 // ----------------------------------------------------------------------------
1537 // wxGridCellWorker is an (almost) empty common base class for
1538 // wxGridCellRenderer and wxGridCellEditor managing ref counting
1539 // ----------------------------------------------------------------------------
1540
1541 void wxGridCellWorker::SetParameters(const wxString& WXUNUSED(params))
1542 {
1543 // nothing to do
1544 }
1545
1546 wxGridCellWorker::~wxGridCellWorker()
1547 {
1548 }
1549
1550 // ============================================================================
1551 // renderer classes
1552 // ============================================================================
1553
1554 // ----------------------------------------------------------------------------
1555 // wxGridCellRenderer
1556 // ----------------------------------------------------------------------------
1557
1558 void wxGridCellRenderer::Draw(wxGrid& grid,
1559 wxGridCellAttr& attr,
1560 wxDC& dc,
1561 const wxRect& rect,
1562 int WXUNUSED(row), int WXUNUSED(col),
1563 bool isSelected)
1564 {
1565 dc.SetBackgroundMode( wxSOLID );
1566
1567 // grey out fields if the grid is disabled
1568 if( grid.IsEnabled() )
1569 {
1570 if ( isSelected )
1571 {
1572 dc.SetBrush( wxBrush(grid.GetSelectionBackground(), wxSOLID) );
1573 }
1574 else
1575 {
1576 dc.SetBrush( wxBrush(attr.GetBackgroundColour(), wxSOLID) );
1577 }
1578 }
1579 else
1580 {
1581 dc.SetBrush(wxBrush(wxSystemSettings::GetSystemColour(wxSYS_COLOUR_BTNFACE), wxSOLID));
1582 }
1583
1584 dc.SetPen( *wxTRANSPARENT_PEN );
1585 dc.DrawRectangle(rect);
1586 }
1587
1588 // ----------------------------------------------------------------------------
1589 // wxGridCellStringRenderer
1590 // ----------------------------------------------------------------------------
1591
1592 void wxGridCellStringRenderer::SetTextColoursAndFont(wxGrid& grid,
1593 wxGridCellAttr& attr,
1594 wxDC& dc,
1595 bool isSelected)
1596 {
1597 dc.SetBackgroundMode( wxTRANSPARENT );
1598
1599 // TODO some special colours for attr.IsReadOnly() case?
1600
1601 // different coloured text when the grid is disabled
1602 if( grid.IsEnabled() )
1603 {
1604 if ( isSelected )
1605 {
1606 dc.SetTextBackground( grid.GetSelectionBackground() );
1607 dc.SetTextForeground( grid.GetSelectionForeground() );
1608 }
1609 else
1610 {
1611 dc.SetTextBackground( attr.GetBackgroundColour() );
1612 dc.SetTextForeground( attr.GetTextColour() );
1613 }
1614 }
1615 else
1616 {
1617 dc.SetTextBackground(wxSystemSettings::GetSystemColour(wxSYS_COLOUR_BTNFACE));
1618 dc.SetTextForeground(wxSystemSettings::GetSystemColour(wxSYS_COLOUR_GRAYTEXT));
1619 }
1620
1621 dc.SetFont( attr.GetFont() );
1622 }
1623
1624 wxSize wxGridCellStringRenderer::DoGetBestSize(wxGridCellAttr& attr,
1625 wxDC& dc,
1626 const wxString& text)
1627 {
1628 wxCoord x = 0, y = 0, max_x = 0;
1629 dc.SetFont(attr.GetFont());
1630 wxStringTokenizer tk(text, _T('\n'));
1631 while ( tk.HasMoreTokens() )
1632 {
1633 dc.GetTextExtent(tk.GetNextToken(), &x, &y);
1634 max_x = wxMax(max_x, x);
1635 }
1636
1637 y *= 1 + text.Freq(wxT('\n')); // multiply by the number of lines.
1638
1639 return wxSize(max_x, y);
1640 }
1641
1642 wxSize wxGridCellStringRenderer::GetBestSize(wxGrid& grid,
1643 wxGridCellAttr& attr,
1644 wxDC& dc,
1645 int row, int col)
1646 {
1647 return DoGetBestSize(attr, dc, grid.GetCellValue(row, col));
1648 }
1649
1650 void wxGridCellStringRenderer::Draw(wxGrid& grid,
1651 wxGridCellAttr& attr,
1652 wxDC& dc,
1653 const wxRect& rectCell,
1654 int row, int col,
1655 bool isSelected)
1656 {
1657 wxRect rect = rectCell;
1658 rect.Inflate(-1);
1659
1660 // erase only this cells background, overflow cells should have been erased
1661 wxGridCellRenderer::Draw(grid, attr, dc, rectCell, row, col, isSelected);
1662
1663 int hAlign, vAlign;
1664 attr.GetAlignment(&hAlign, &vAlign);
1665
1666 int overflowCols = 0;
1667
1668 if (attr.GetOverflow())
1669 {
1670 int cols = grid.GetNumberCols();
1671 int best_width = GetBestSize(grid,attr,dc,row,col).GetWidth();
1672 int cell_rows, cell_cols;
1673 attr.GetSize( &cell_rows, &cell_cols ); // shouldn't get here if <=0
1674 if ((best_width > rectCell.width) && (col < cols) && grid.GetTable())
1675 {
1676 int i, c_cols, c_rows;
1677 for (i = col+cell_cols; i < cols; i++)
1678 {
1679 bool is_empty = TRUE;
1680 for (int j=row; j<row+cell_rows; j++)
1681 {
1682 // check w/ anchor cell for multicell block
1683 grid.GetCellSize(j, i, &c_rows, &c_cols);
1684 if (c_rows > 0) c_rows = 0;
1685 if (!grid.GetTable()->IsEmptyCell(j+c_rows, i))
1686 {
1687 is_empty = FALSE;
1688 break;
1689 }
1690 }
1691 if (is_empty)
1692 rect.width += grid.GetColSize(i);
1693 else
1694 {
1695 i--;
1696 break;
1697 }
1698 if (rect.width >= best_width) break;
1699 }
1700 overflowCols = i - col - cell_cols + 1;
1701 if (overflowCols >= cols) overflowCols = cols - 1;
1702 }
1703
1704 if (overflowCols > 0) // redraw overflow cells w/ proper hilight
1705 {
1706 hAlign = wxALIGN_LEFT; // if oveflowed then it's left aligned
1707 wxRect clip = rect;
1708 clip.x += rectCell.width;
1709 // draw each overflow cell individually
1710 int col_end = col+cell_cols+overflowCols;
1711 if (col_end >= grid.GetNumberCols())
1712 col_end = grid.GetNumberCols() - 1;
1713 for (int i = col+cell_cols; i <= col_end; i++)
1714 {
1715 clip.width = grid.GetColSize(i) - 1;
1716 dc.DestroyClippingRegion();
1717 dc.SetClippingRegion(clip);
1718
1719 SetTextColoursAndFont(grid, attr, dc,
1720 grid.IsInSelection(row,i));
1721
1722 grid.DrawTextRectangle(dc, grid.GetCellValue(row, col),
1723 rect, hAlign, vAlign);
1724 clip.x += grid.GetColSize(i) - 1;
1725 }
1726
1727 rect = rectCell;
1728 rect.Inflate(-1);
1729 rect.width++;
1730 dc.DestroyClippingRegion();
1731 }
1732 }
1733
1734 // now we only have to draw the text
1735 SetTextColoursAndFont(grid, attr, dc, isSelected);
1736
1737 grid.DrawTextRectangle(dc, grid.GetCellValue(row, col),
1738 rect, hAlign, vAlign);
1739 }
1740
1741 // ----------------------------------------------------------------------------
1742 // wxGridCellNumberRenderer
1743 // ----------------------------------------------------------------------------
1744
1745 wxString wxGridCellNumberRenderer::GetString(wxGrid& grid, int row, int col)
1746 {
1747 wxGridTableBase *table = grid.GetTable();
1748 wxString text;
1749 if ( table->CanGetValueAs(row, col, wxGRID_VALUE_NUMBER) )
1750 {
1751 text.Printf(_T("%ld"), table->GetValueAsLong(row, col));
1752 }
1753 else
1754 {
1755 text = table->GetValue(row, col);
1756 }
1757
1758 return text;
1759 }
1760
1761 void wxGridCellNumberRenderer::Draw(wxGrid& grid,
1762 wxGridCellAttr& attr,
1763 wxDC& dc,
1764 const wxRect& rectCell,
1765 int row, int col,
1766 bool isSelected)
1767 {
1768 wxGridCellRenderer::Draw(grid, attr, dc, rectCell, row, col, isSelected);
1769
1770 SetTextColoursAndFont(grid, attr, dc, isSelected);
1771
1772 // draw the text right aligned by default
1773 int hAlign, vAlign;
1774 attr.GetAlignment(&hAlign, &vAlign);
1775 hAlign = wxALIGN_RIGHT;
1776
1777 wxRect rect = rectCell;
1778 rect.Inflate(-1);
1779
1780 grid.DrawTextRectangle(dc, GetString(grid, row, col), rect, hAlign, vAlign);
1781 }
1782
1783 wxSize wxGridCellNumberRenderer::GetBestSize(wxGrid& grid,
1784 wxGridCellAttr& attr,
1785 wxDC& dc,
1786 int row, int col)
1787 {
1788 return DoGetBestSize(attr, dc, GetString(grid, row, col));
1789 }
1790
1791 // ----------------------------------------------------------------------------
1792 // wxGridCellFloatRenderer
1793 // ----------------------------------------------------------------------------
1794
1795 wxGridCellFloatRenderer::wxGridCellFloatRenderer(int width, int precision)
1796 {
1797 SetWidth(width);
1798 SetPrecision(precision);
1799 }
1800
1801 wxGridCellRenderer *wxGridCellFloatRenderer::Clone() const
1802 {
1803 wxGridCellFloatRenderer *renderer = new wxGridCellFloatRenderer;
1804 renderer->m_width = m_width;
1805 renderer->m_precision = m_precision;
1806 renderer->m_format = m_format;
1807
1808 return renderer;
1809 }
1810
1811 wxString wxGridCellFloatRenderer::GetString(wxGrid& grid, int row, int col)
1812 {
1813 wxGridTableBase *table = grid.GetTable();
1814
1815 bool hasDouble;
1816 double val;
1817 wxString text;
1818 if ( table->CanGetValueAs(row, col, wxGRID_VALUE_FLOAT) )
1819 {
1820 val = table->GetValueAsDouble(row, col);
1821 hasDouble = TRUE;
1822 }
1823 else
1824 {
1825 text = table->GetValue(row, col);
1826 hasDouble = text.ToDouble(&val);
1827 }
1828
1829 if ( hasDouble )
1830 {
1831 if ( !m_format )
1832 {
1833 if ( m_width == -1 )
1834 {
1835 if ( m_precision == -1 )
1836 {
1837 // default width/precision
1838 m_format = _T("%f");
1839 }
1840 else
1841 {
1842 m_format.Printf(_T("%%.%df"), m_precision);
1843 }
1844 }
1845 else if ( m_precision == -1 )
1846 {
1847 // default precision
1848 m_format.Printf(_T("%%%d.f"), m_width);
1849 }
1850 else
1851 {
1852 m_format.Printf(_T("%%%d.%df"), m_width, m_precision);
1853 }
1854 }
1855
1856 text.Printf(m_format, val);
1857
1858 }
1859 //else: text already contains the string
1860
1861 return text;
1862 }
1863
1864 void wxGridCellFloatRenderer::Draw(wxGrid& grid,
1865 wxGridCellAttr& attr,
1866 wxDC& dc,
1867 const wxRect& rectCell,
1868 int row, int col,
1869 bool isSelected)
1870 {
1871 wxGridCellRenderer::Draw(grid, attr, dc, rectCell, row, col, isSelected);
1872
1873 SetTextColoursAndFont(grid, attr, dc, isSelected);
1874
1875 // draw the text right aligned by default
1876 int hAlign, vAlign;
1877 attr.GetAlignment(&hAlign, &vAlign);
1878 hAlign = wxALIGN_RIGHT;
1879
1880 wxRect rect = rectCell;
1881 rect.Inflate(-1);
1882
1883 grid.DrawTextRectangle(dc, GetString(grid, row, col), rect, hAlign, vAlign);
1884 }
1885
1886 wxSize wxGridCellFloatRenderer::GetBestSize(wxGrid& grid,
1887 wxGridCellAttr& attr,
1888 wxDC& dc,
1889 int row, int col)
1890 {
1891 return DoGetBestSize(attr, dc, GetString(grid, row, col));
1892 }
1893
1894 void wxGridCellFloatRenderer::SetParameters(const wxString& params)
1895 {
1896 if ( !params )
1897 {
1898 // reset to defaults
1899 SetWidth(-1);
1900 SetPrecision(-1);
1901 }
1902 else
1903 {
1904 wxString tmp = params.BeforeFirst(_T(','));
1905 if ( !!tmp )
1906 {
1907 long width;
1908 if ( tmp.ToLong(&width) )
1909 {
1910 SetWidth((int)width);
1911 }
1912 else
1913 {
1914 wxLogDebug(_T("Invalid wxGridCellFloatRenderer width parameter string '%s ignored"), params.c_str());
1915 }
1916
1917 }
1918 tmp = params.AfterFirst(_T(','));
1919 if ( !!tmp )
1920 {
1921 long precision;
1922 if ( tmp.ToLong(&precision) )
1923 {
1924 SetPrecision((int)precision);
1925 }
1926 else
1927 {
1928 wxLogDebug(_T("Invalid wxGridCellFloatRenderer precision parameter string '%s ignored"), params.c_str());
1929 }
1930
1931 }
1932 }
1933 }
1934
1935
1936 // ----------------------------------------------------------------------------
1937 // wxGridCellBoolRenderer
1938 // ----------------------------------------------------------------------------
1939
1940 wxSize wxGridCellBoolRenderer::ms_sizeCheckMark;
1941
1942 // FIXME these checkbox size calculations are really ugly...
1943
1944 // between checkmark and box
1945 static const wxCoord wxGRID_CHECKMARK_MARGIN = 2;
1946
1947 wxSize wxGridCellBoolRenderer::GetBestSize(wxGrid& grid,
1948 wxGridCellAttr& WXUNUSED(attr),
1949 wxDC& WXUNUSED(dc),
1950 int WXUNUSED(row),
1951 int WXUNUSED(col))
1952 {
1953 // compute it only once (no locks for MT safeness in GUI thread...)
1954 if ( !ms_sizeCheckMark.x )
1955 {
1956 // get checkbox size
1957 wxCoord checkSize = 0;
1958 wxCheckBox *checkbox = new wxCheckBox(&grid, -1, wxEmptyString);
1959 wxSize size = checkbox->GetBestSize();
1960 checkSize = size.y + 2*wxGRID_CHECKMARK_MARGIN;
1961
1962 // FIXME wxGTK::wxCheckBox::GetBestSize() gives "wrong" result
1963 #if defined(__WXGTK__) || defined(__WXMOTIF__)
1964 checkSize -= size.y / 2;
1965 #endif
1966
1967 delete checkbox;
1968
1969 ms_sizeCheckMark.x = ms_sizeCheckMark.y = checkSize;
1970 }
1971
1972 return ms_sizeCheckMark;
1973 }
1974
1975 void wxGridCellBoolRenderer::Draw(wxGrid& grid,
1976 wxGridCellAttr& attr,
1977 wxDC& dc,
1978 const wxRect& rect,
1979 int row, int col,
1980 bool isSelected)
1981 {
1982 wxGridCellRenderer::Draw(grid, attr, dc, rect, row, col, isSelected);
1983
1984 // draw a check mark in the centre (ignoring alignment - TODO)
1985 wxSize size = GetBestSize(grid, attr, dc, row, col);
1986
1987 // don't draw outside the cell
1988 wxCoord minSize = wxMin(rect.width, rect.height);
1989 if ( size.x >= minSize || size.y >= minSize )
1990 {
1991 // and even leave (at least) 1 pixel margin
1992 size.x = size.y = minSize - 2;
1993 }
1994
1995 // draw a border around checkmark
1996 int vAlign, hAlign;
1997 attr.GetAlignment(& hAlign, &vAlign);
1998
1999 wxRect rectBorder;
2000 if (hAlign == wxALIGN_CENTRE)
2001 {
2002 rectBorder.x = rect.x + rect.width/2 - size.x/2;
2003 rectBorder.y = rect.y + rect.height/2 - size.y/2;
2004 rectBorder.width = size.x;
2005 rectBorder.height = size.y;
2006 }
2007 else if (hAlign == wxALIGN_LEFT)
2008 {
2009 rectBorder.x = rect.x + 2;
2010 rectBorder.y = rect.y + rect.height/2 - size.y/2;
2011 rectBorder.width = size.x;
2012 rectBorder.height = size.y;
2013 }
2014 else if (hAlign == wxALIGN_RIGHT)
2015 {
2016 rectBorder.x = rect.x + rect.width - size.x - 2;
2017 rectBorder.y = rect.y + rect.height/2 - size.y/2;
2018 rectBorder.width = size.x;
2019 rectBorder.height = size.y;
2020 }
2021
2022 bool value;
2023 if ( grid.GetTable()->CanGetValueAs(row, col, wxGRID_VALUE_BOOL) )
2024 value = grid.GetTable()->GetValueAsBool(row, col);
2025 else
2026 {
2027 wxString cellval( grid.GetTable()->GetValue(row, col) );
2028 value = !( !cellval || (cellval == wxT("0")) );
2029 }
2030
2031 if ( value )
2032 {
2033 wxRect rectMark = rectBorder;
2034 #ifdef __WXMSW__
2035 // MSW DrawCheckMark() is weird (and should probably be changed...)
2036 rectMark.Inflate(-wxGRID_CHECKMARK_MARGIN/2);
2037 rectMark.x++;
2038 rectMark.y++;
2039 #else // !MSW
2040 rectMark.Inflate(-wxGRID_CHECKMARK_MARGIN);
2041 #endif // MSW/!MSW
2042
2043 dc.SetTextForeground(attr.GetTextColour());
2044 dc.DrawCheckMark(rectMark);
2045 }
2046
2047 dc.SetBrush(*wxTRANSPARENT_BRUSH);
2048 dc.SetPen(wxPen(attr.GetTextColour(), 1, wxSOLID));
2049 dc.DrawRectangle(rectBorder);
2050 }
2051
2052 // ----------------------------------------------------------------------------
2053 // wxGridCellAttr
2054 // ----------------------------------------------------------------------------
2055
2056 void wxGridCellAttr::Init(wxGridCellAttr *attrDefault)
2057 {
2058 m_nRef = 1;
2059
2060 m_isReadOnly = Unset;
2061
2062 m_renderer = NULL;
2063 m_editor = NULL;
2064
2065 m_attrkind = wxGridCellAttr::Cell;
2066
2067 m_sizeRows = m_sizeCols = 1;
2068 m_overflow = UnsetOverflow;
2069
2070 SetDefAttr(attrDefault);
2071 }
2072
2073 wxGridCellAttr *wxGridCellAttr::Clone() const
2074 {
2075 wxGridCellAttr *attr = new wxGridCellAttr(m_defGridAttr);
2076
2077 if ( HasTextColour() )
2078 attr->SetTextColour(GetTextColour());
2079 if ( HasBackgroundColour() )
2080 attr->SetBackgroundColour(GetBackgroundColour());
2081 if ( HasFont() )
2082 attr->SetFont(GetFont());
2083 if ( HasAlignment() )
2084 attr->SetAlignment(m_hAlign, m_vAlign);
2085
2086 attr->SetSize( m_sizeRows, m_sizeCols );
2087
2088 if ( m_renderer )
2089 {
2090 attr->SetRenderer(m_renderer);
2091 m_renderer->IncRef();
2092 }
2093 if ( m_editor )
2094 {
2095 attr->SetEditor(m_editor);
2096 m_editor->IncRef();
2097 }
2098
2099 if ( IsReadOnly() )
2100 attr->SetReadOnly();
2101
2102 attr->SetKind( m_attrkind );
2103
2104 return attr;
2105 }
2106
2107 void wxGridCellAttr::MergeWith(wxGridCellAttr *mergefrom)
2108 {
2109 if ( !HasTextColour() && mergefrom->HasTextColour() )
2110 SetTextColour(mergefrom->GetTextColour());
2111 if ( !HasBackgroundColour() && mergefrom->HasBackgroundColour() )
2112 SetBackgroundColour(mergefrom->GetBackgroundColour());
2113 if ( !HasFont() && mergefrom->HasFont() )
2114 SetFont(mergefrom->GetFont());
2115 if ( !HasAlignment() && mergefrom->HasAlignment() ){
2116 int hAlign, vAlign;
2117 mergefrom->GetAlignment( &hAlign, &vAlign);
2118 SetAlignment(hAlign, vAlign);
2119 }
2120
2121 mergefrom->GetSize( &m_sizeRows, &m_sizeCols );
2122
2123 // Directly access member functions as GetRender/Editor don't just return
2124 // m_renderer/m_editor
2125 //
2126 // Maybe add support for merge of Render and Editor?
2127 if (!HasRenderer() && mergefrom->HasRenderer() )
2128 {
2129 m_renderer = mergefrom->m_renderer;
2130 m_renderer->IncRef();
2131 }
2132 if ( !HasEditor() && mergefrom->HasEditor() )
2133 {
2134 m_editor = mergefrom->m_editor;
2135 m_editor->IncRef();
2136 }
2137 if ( !HasReadWriteMode() && mergefrom->HasReadWriteMode() )
2138 SetReadOnly(mergefrom->IsReadOnly());
2139
2140 if (!HasOverflowMode() && mergefrom->HasOverflowMode() )
2141 SetOverflow(mergefrom->GetOverflow());
2142
2143 SetDefAttr(mergefrom->m_defGridAttr);
2144 }
2145
2146 void wxGridCellAttr::SetSize(int num_rows, int num_cols)
2147 {
2148 // The size of a cell is normally 1,1
2149
2150 // If this cell is larger (2,2) then this is the top left cell
2151 // the other cells that will be covered (lower right cells) must be
2152 // set to negative or zero values such that
2153 // row + num_rows of the covered cell points to the larger cell (this cell)
2154 // same goes for the col + num_cols.
2155
2156 // Size of 0,0 is NOT valid, neither is <=0 and any positive value
2157
2158 wxASSERT_MSG( (!((num_rows>0)&&(num_cols<=0)) ||
2159 !((num_rows<=0)&&(num_cols>0)) ||
2160 !((num_rows==0)&&(num_cols==0))),
2161 wxT("wxGridCellAttr::SetSize only takes two postive values or negative/zero values"));
2162
2163 m_sizeRows = num_rows;
2164 m_sizeCols = num_cols;
2165 }
2166
2167 const wxColour& wxGridCellAttr::GetTextColour() const
2168 {
2169 if (HasTextColour())
2170 {
2171 return m_colText;
2172 }
2173 else if (m_defGridAttr && m_defGridAttr != this)
2174 {
2175 return m_defGridAttr->GetTextColour();
2176 }
2177 else
2178 {
2179 wxFAIL_MSG(wxT("Missing default cell attribute"));
2180 return wxNullColour;
2181 }
2182 }
2183
2184
2185 const wxColour& wxGridCellAttr::GetBackgroundColour() const
2186 {
2187 if (HasBackgroundColour())
2188 return m_colBack;
2189 else if (m_defGridAttr && m_defGridAttr != this)
2190 return m_defGridAttr->GetBackgroundColour();
2191 else
2192 {
2193 wxFAIL_MSG(wxT("Missing default cell attribute"));
2194 return wxNullColour;
2195 }
2196 }
2197
2198
2199 const wxFont& wxGridCellAttr::GetFont() const
2200 {
2201 if (HasFont())
2202 return m_font;
2203 else if (m_defGridAttr && m_defGridAttr != this)
2204 return m_defGridAttr->GetFont();
2205 else
2206 {
2207 wxFAIL_MSG(wxT("Missing default cell attribute"));
2208 return wxNullFont;
2209 }
2210 }
2211
2212
2213 void wxGridCellAttr::GetAlignment(int *hAlign, int *vAlign) const
2214 {
2215 if (HasAlignment())
2216 {
2217 if ( hAlign ) *hAlign = m_hAlign;
2218 if ( vAlign ) *vAlign = m_vAlign;
2219 }
2220 else if (m_defGridAttr && m_defGridAttr != this)
2221 m_defGridAttr->GetAlignment(hAlign, vAlign);
2222 else
2223 {
2224 wxFAIL_MSG(wxT("Missing default cell attribute"));
2225 }
2226 }
2227
2228 void wxGridCellAttr::GetSize( int *num_rows, int *num_cols ) const
2229 {
2230 if ( num_rows ) *num_rows = m_sizeRows;
2231 if ( num_cols ) *num_cols = m_sizeCols;
2232 }
2233
2234 // GetRenderer and GetEditor use a slightly different decision path about
2235 // which attribute to use. If a non-default attr object has one then it is
2236 // used, otherwise the default editor or renderer is fetched from the grid and
2237 // used. It should be the default for the data type of the cell. If it is
2238 // NULL (because the table has a type that the grid does not have in its
2239 // registry,) then the grid's default editor or renderer is used.
2240
2241 wxGridCellRenderer* wxGridCellAttr::GetRenderer(wxGrid* grid, int row, int col) const
2242 {
2243 wxGridCellRenderer *renderer;
2244
2245 if ( m_renderer && this != m_defGridAttr )
2246 {
2247 // use the cells renderer if it has one
2248 renderer = m_renderer;
2249 renderer->IncRef();
2250 }
2251 else // no non default cell renderer
2252 {
2253 // get default renderer for the data type
2254 if ( grid )
2255 {
2256 // GetDefaultRendererForCell() will do IncRef() for us
2257 renderer = grid->GetDefaultRendererForCell(row, col);
2258 }
2259 else
2260 {
2261 renderer = NULL;
2262 }
2263
2264 if ( !renderer )
2265 {
2266 if (m_defGridAttr && this != m_defGridAttr )
2267 {
2268 // if we still don't have one then use the grid default
2269 // (no need for IncRef() here neither)
2270 renderer = m_defGridAttr->GetRenderer(NULL, 0, 0);
2271 }
2272 else // default grid attr
2273 {
2274 // use m_renderer which we had decided not to use initially
2275 renderer = m_renderer;
2276 if ( renderer )
2277 renderer->IncRef();
2278 }
2279 }
2280 }
2281
2282 // we're supposed to always find something
2283 wxASSERT_MSG(renderer, wxT("Missing default cell renderer"));
2284
2285 return renderer;
2286 }
2287
2288 // same as above, except for s/renderer/editor/g
2289 wxGridCellEditor* wxGridCellAttr::GetEditor(wxGrid* grid, int row, int col) const
2290 {
2291 wxGridCellEditor *editor;
2292
2293 if ( m_editor && this != m_defGridAttr )
2294 {
2295 // use the cells editor if it has one
2296 editor = m_editor;
2297 editor->IncRef();
2298 }
2299 else // no non default cell editor
2300 {
2301 // get default editor for the data type
2302 if ( grid )
2303 {
2304 // GetDefaultEditorForCell() will do IncRef() for us
2305 editor = grid->GetDefaultEditorForCell(row, col);
2306 }
2307 else
2308 {
2309 editor = NULL;
2310 }
2311
2312 if ( !editor )
2313 {
2314 if ( m_defGridAttr && this != m_defGridAttr )
2315 {
2316 // if we still don't have one then use the grid default
2317 // (no need for IncRef() here neither)
2318 editor = m_defGridAttr->GetEditor(NULL, 0, 0);
2319 }
2320 else // default grid attr
2321 {
2322 // use m_editor which we had decided not to use initially
2323 editor = m_editor;
2324 if ( editor )
2325 editor->IncRef();
2326 }
2327 }
2328 }
2329
2330 // we're supposed to always find something
2331 wxASSERT_MSG(editor, wxT("Missing default cell editor"));
2332
2333 return editor;
2334 }
2335
2336 // ----------------------------------------------------------------------------
2337 // wxGridCellAttrData
2338 // ----------------------------------------------------------------------------
2339
2340 void wxGridCellAttrData::SetAttr(wxGridCellAttr *attr, int row, int col)
2341 {
2342 int n = FindIndex(row, col);
2343 if ( n == wxNOT_FOUND )
2344 {
2345 // add the attribute
2346 m_attrs.Add(new wxGridCellWithAttr(row, col, attr));
2347 }
2348 else
2349 {
2350 // free the old attribute
2351 m_attrs[(size_t)n].attr->DecRef();
2352
2353 if ( attr )
2354 {
2355 // change the attribute
2356 m_attrs[(size_t)n].attr = attr;
2357 }
2358 else
2359 {
2360 // remove this attribute
2361 m_attrs.RemoveAt((size_t)n);
2362 }
2363 }
2364 }
2365
2366 wxGridCellAttr *wxGridCellAttrData::GetAttr(int row, int col) const
2367 {
2368 wxGridCellAttr *attr = (wxGridCellAttr *)NULL;
2369
2370 int n = FindIndex(row, col);
2371 if ( n != wxNOT_FOUND )
2372 {
2373 attr = m_attrs[(size_t)n].attr;
2374 attr->IncRef();
2375 }
2376
2377 return attr;
2378 }
2379
2380 void wxGridCellAttrData::UpdateAttrRows( size_t pos, int numRows )
2381 {
2382 size_t count = m_attrs.GetCount();
2383 for ( size_t n = 0; n < count; n++ )
2384 {
2385 wxGridCellCoords& coords = m_attrs[n].coords;
2386 wxCoord row = coords.GetRow();
2387 if ((size_t)row >= pos)
2388 {
2389 if (numRows > 0)
2390 {
2391 // If rows inserted, include row counter where necessary
2392 coords.SetRow(row + numRows);
2393 }
2394 else if (numRows < 0)
2395 {
2396 // If rows deleted ...
2397 if ((size_t)row >= pos - numRows)
2398 {
2399 // ...either decrement row counter (if row still exists)...
2400 coords.SetRow(row + numRows);
2401 }
2402 else
2403 {
2404 // ...or remove the attribute
2405 m_attrs.RemoveAt((size_t)n);
2406 n--; count--;
2407 }
2408 }
2409 }
2410 }
2411 }
2412
2413 void wxGridCellAttrData::UpdateAttrCols( size_t pos, int numCols )
2414 {
2415 size_t count = m_attrs.GetCount();
2416 for ( size_t n = 0; n < count; n++ )
2417 {
2418 wxGridCellCoords& coords = m_attrs[n].coords;
2419 wxCoord col = coords.GetCol();
2420 if ( (size_t)col >= pos )
2421 {
2422 if ( numCols > 0 )
2423 {
2424 // If rows inserted, include row counter where necessary
2425 coords.SetCol(col + numCols);
2426 }
2427 else if (numCols < 0)
2428 {
2429 // If rows deleted ...
2430 if ((size_t)col >= pos - numCols)
2431 {
2432 // ...either decrement row counter (if row still exists)...
2433 coords.SetCol(col + numCols);
2434 }
2435 else
2436 {
2437 // ...or remove the attribute
2438 m_attrs.RemoveAt((size_t)n);
2439 n--; count--;
2440 }
2441 }
2442 }
2443 }
2444 }
2445
2446 int wxGridCellAttrData::FindIndex(int row, int col) const
2447 {
2448 size_t count = m_attrs.GetCount();
2449 for ( size_t n = 0; n < count; n++ )
2450 {
2451 const wxGridCellCoords& coords = m_attrs[n].coords;
2452 if ( (coords.GetRow() == row) && (coords.GetCol() == col) )
2453 {
2454 return n;
2455 }
2456 }
2457
2458 return wxNOT_FOUND;
2459 }
2460
2461 // ----------------------------------------------------------------------------
2462 // wxGridRowOrColAttrData
2463 // ----------------------------------------------------------------------------
2464
2465 wxGridRowOrColAttrData::~wxGridRowOrColAttrData()
2466 {
2467 size_t count = m_attrs.Count();
2468 for ( size_t n = 0; n < count; n++ )
2469 {
2470 m_attrs[n]->DecRef();
2471 }
2472 }
2473
2474 wxGridCellAttr *wxGridRowOrColAttrData::GetAttr(int rowOrCol) const
2475 {
2476 wxGridCellAttr *attr = (wxGridCellAttr *)NULL;
2477
2478 int n = m_rowsOrCols.Index(rowOrCol);
2479 if ( n != wxNOT_FOUND )
2480 {
2481 attr = m_attrs[(size_t)n];
2482 attr->IncRef();
2483 }
2484
2485 return attr;
2486 }
2487
2488 void wxGridRowOrColAttrData::SetAttr(wxGridCellAttr *attr, int rowOrCol)
2489 {
2490 int i = m_rowsOrCols.Index(rowOrCol);
2491 if ( i == wxNOT_FOUND )
2492 {
2493 // add the attribute
2494 m_rowsOrCols.Add(rowOrCol);
2495 m_attrs.Add(attr);
2496 }
2497 else
2498 {
2499 size_t n = (size_t)i;
2500 if ( attr )
2501 {
2502 // change the attribute
2503 m_attrs[n]->DecRef();
2504 m_attrs[n] = attr;
2505 }
2506 else
2507 {
2508 // remove this attribute
2509 m_attrs[n]->DecRef();
2510 m_rowsOrCols.RemoveAt(n);
2511 m_attrs.RemoveAt(n);
2512 }
2513 }
2514 }
2515
2516 void wxGridRowOrColAttrData::UpdateAttrRowsOrCols( size_t pos, int numRowsOrCols )
2517 {
2518 size_t count = m_attrs.GetCount();
2519 for ( size_t n = 0; n < count; n++ )
2520 {
2521 int & rowOrCol = m_rowsOrCols[n];
2522 if ( (size_t)rowOrCol >= pos )
2523 {
2524 if ( numRowsOrCols > 0 )
2525 {
2526 // If rows inserted, include row counter where necessary
2527 rowOrCol += numRowsOrCols;
2528 }
2529 else if ( numRowsOrCols < 0)
2530 {
2531 // If rows deleted, either decrement row counter (if row still exists)
2532 if ((size_t)rowOrCol >= pos - numRowsOrCols)
2533 rowOrCol += numRowsOrCols;
2534 else
2535 {
2536 m_rowsOrCols.RemoveAt((size_t)n);
2537 m_attrs.RemoveAt((size_t)n);
2538 n--; count--;
2539 }
2540 }
2541 }
2542 }
2543 }
2544
2545 // ----------------------------------------------------------------------------
2546 // wxGridCellAttrProvider
2547 // ----------------------------------------------------------------------------
2548
2549 wxGridCellAttrProvider::wxGridCellAttrProvider()
2550 {
2551 m_data = (wxGridCellAttrProviderData *)NULL;
2552 }
2553
2554 wxGridCellAttrProvider::~wxGridCellAttrProvider()
2555 {
2556 delete m_data;
2557 }
2558
2559 void wxGridCellAttrProvider::InitData()
2560 {
2561 m_data = new wxGridCellAttrProviderData;
2562 }
2563
2564 wxGridCellAttr *wxGridCellAttrProvider::GetAttr(int row, int col,
2565 wxGridCellAttr::wxAttrKind kind ) const
2566 {
2567 wxGridCellAttr *attr = (wxGridCellAttr *)NULL;
2568 if ( m_data )
2569 {
2570 switch(kind)
2571 {
2572 case (wxGridCellAttr::Any):
2573 //Get cached merge attributes.
2574 // Currenlty not used as no cache implemented as not mutiable
2575 // attr = m_data->m_mergeAttr.GetAttr(row, col);
2576 if(!attr)
2577 {
2578 //Basicaly implement old version.
2579 //Also check merge cache, so we don't have to re-merge every time..
2580 wxGridCellAttr *attrcell = (wxGridCellAttr *)NULL,
2581 *attrrow = (wxGridCellAttr *)NULL,
2582 *attrcol = (wxGridCellAttr *)NULL;
2583
2584 attrcell = m_data->m_cellAttrs.GetAttr(row, col);
2585 attrcol = m_data->m_colAttrs.GetAttr(col);
2586 attrrow = m_data->m_rowAttrs.GetAttr(row);
2587
2588 if((attrcell != attrrow) && (attrrow !=attrcol) && (attrcell != attrcol)){
2589 // Two or move are non NULL
2590 attr = new wxGridCellAttr;
2591 attr->SetKind(wxGridCellAttr::Merged);
2592
2593 //Order important..
2594 if(attrcell){
2595 attr->MergeWith(attrcell);
2596 attrcell->DecRef();
2597 }
2598 if(attrcol){
2599 attr->MergeWith(attrcol);
2600 attrcol->DecRef();
2601 }
2602 if(attrrow){
2603 attr->MergeWith(attrrow);
2604 attrrow->DecRef();
2605 }
2606 //store merge attr if cache implemented
2607 //attr->IncRef();
2608 //m_data->m_mergeAttr.SetAttr(attr, row, col);
2609 }
2610 else
2611 {
2612 // one or none is non null return it or null.
2613 if(attrrow) attr = attrrow;
2614 if(attrcol) attr = attrcol;
2615 if(attrcell) attr = attrcell;
2616 }
2617 }
2618 break;
2619 case (wxGridCellAttr::Cell):
2620 attr = m_data->m_cellAttrs.GetAttr(row, col);
2621 break;
2622 case (wxGridCellAttr::Col):
2623 attr = m_data->m_colAttrs.GetAttr(col);
2624 break;
2625 case (wxGridCellAttr::Row):
2626 attr = m_data->m_rowAttrs.GetAttr(row);
2627 break;
2628 default:
2629 // unused as yet...
2630 // (wxGridCellAttr::Default):
2631 // (wxGridCellAttr::Merged):
2632 break;
2633 }
2634 }
2635 return attr;
2636 }
2637
2638 void wxGridCellAttrProvider::SetAttr(wxGridCellAttr *attr,
2639 int row, int col)
2640 {
2641 if ( !m_data )
2642 InitData();
2643
2644 m_data->m_cellAttrs.SetAttr(attr, row, col);
2645 }
2646
2647 void wxGridCellAttrProvider::SetRowAttr(wxGridCellAttr *attr, int row)
2648 {
2649 if ( !m_data )
2650 InitData();
2651
2652 m_data->m_rowAttrs.SetAttr(attr, row);
2653 }
2654
2655 void wxGridCellAttrProvider::SetColAttr(wxGridCellAttr *attr, int col)
2656 {
2657 if ( !m_data )
2658 InitData();
2659
2660 m_data->m_colAttrs.SetAttr(attr, col);
2661 }
2662
2663 void wxGridCellAttrProvider::UpdateAttrRows( size_t pos, int numRows )
2664 {
2665 if ( m_data )
2666 {
2667 m_data->m_cellAttrs.UpdateAttrRows( pos, numRows );
2668
2669 m_data->m_rowAttrs.UpdateAttrRowsOrCols( pos, numRows );
2670 }
2671 }
2672
2673 void wxGridCellAttrProvider::UpdateAttrCols( size_t pos, int numCols )
2674 {
2675 if ( m_data )
2676 {
2677 m_data->m_cellAttrs.UpdateAttrCols( pos, numCols );
2678
2679 m_data->m_colAttrs.UpdateAttrRowsOrCols( pos, numCols );
2680 }
2681 }
2682
2683 // ----------------------------------------------------------------------------
2684 // wxGridTypeRegistry
2685 // ----------------------------------------------------------------------------
2686
2687 wxGridTypeRegistry::~wxGridTypeRegistry()
2688 {
2689 size_t count = m_typeinfo.Count();
2690 for ( size_t i = 0; i < count; i++ )
2691 delete m_typeinfo[i];
2692 }
2693
2694
2695 void wxGridTypeRegistry::RegisterDataType(const wxString& typeName,
2696 wxGridCellRenderer* renderer,
2697 wxGridCellEditor* editor)
2698 {
2699 wxGridDataTypeInfo* info = new wxGridDataTypeInfo(typeName, renderer, editor);
2700
2701 // is it already registered?
2702 int loc = FindRegisteredDataType(typeName);
2703 if ( loc != wxNOT_FOUND )
2704 {
2705 delete m_typeinfo[loc];
2706 m_typeinfo[loc] = info;
2707 }
2708 else
2709 {
2710 m_typeinfo.Add(info);
2711 }
2712 }
2713
2714 int wxGridTypeRegistry::FindRegisteredDataType(const wxString& typeName)
2715 {
2716 size_t count = m_typeinfo.GetCount();
2717 for ( size_t i = 0; i < count; i++ )
2718 {
2719 if ( typeName == m_typeinfo[i]->m_typeName )
2720 {
2721 return i;
2722 }
2723 }
2724
2725 return wxNOT_FOUND;
2726 }
2727
2728 int wxGridTypeRegistry::FindDataType(const wxString& typeName)
2729 {
2730 int index = FindRegisteredDataType(typeName);
2731 if ( index == wxNOT_FOUND )
2732 {
2733 // check whether this is one of the standard ones, in which case
2734 // register it "on the fly"
2735 #if wxUSE_TEXTCTRL
2736 if ( typeName == wxGRID_VALUE_STRING )
2737 {
2738 RegisterDataType(wxGRID_VALUE_STRING,
2739 new wxGridCellStringRenderer,
2740 new wxGridCellTextEditor);
2741 } else
2742 #endif // wxUSE_TEXTCTRL
2743 #if wxUSE_CHECKBOX
2744 if ( typeName == wxGRID_VALUE_BOOL )
2745 {
2746 RegisterDataType(wxGRID_VALUE_BOOL,
2747 new wxGridCellBoolRenderer,
2748 new wxGridCellBoolEditor);
2749 } else
2750 #endif // wxUSE_CHECKBOX
2751 #if wxUSE_TEXTCTRL
2752 if ( typeName == wxGRID_VALUE_NUMBER )
2753 {
2754 RegisterDataType(wxGRID_VALUE_NUMBER,
2755 new wxGridCellNumberRenderer,
2756 new wxGridCellNumberEditor);
2757 }
2758 else if ( typeName == wxGRID_VALUE_FLOAT )
2759 {
2760 RegisterDataType(wxGRID_VALUE_FLOAT,
2761 new wxGridCellFloatRenderer,
2762 new wxGridCellFloatEditor);
2763 } else
2764 #endif // wxUSE_TEXTCTRL
2765 #if wxUSE_COMBOBOX
2766 if ( typeName == wxGRID_VALUE_CHOICE )
2767 {
2768 RegisterDataType(wxGRID_VALUE_CHOICE,
2769 new wxGridCellStringRenderer,
2770 new wxGridCellChoiceEditor);
2771 } else
2772 #endif // wxUSE_COMBOBOX
2773 {
2774 return wxNOT_FOUND;
2775 }
2776
2777 // we get here only if just added the entry for this type, so return
2778 // the last index
2779 index = m_typeinfo.GetCount() - 1;
2780 }
2781
2782 return index;
2783 }
2784
2785 int wxGridTypeRegistry::FindOrCloneDataType(const wxString& typeName)
2786 {
2787 int index = FindDataType(typeName);
2788 if ( index == wxNOT_FOUND )
2789 {
2790 // the first part of the typename is the "real" type, anything after ':'
2791 // are the parameters for the renderer
2792 index = FindDataType(typeName.BeforeFirst(_T(':')));
2793 if ( index == wxNOT_FOUND )
2794 {
2795 return wxNOT_FOUND;
2796 }
2797
2798 wxGridCellRenderer *renderer = GetRenderer(index);
2799 wxGridCellRenderer *rendererOld = renderer;
2800 renderer = renderer->Clone();
2801 rendererOld->DecRef();
2802
2803 wxGridCellEditor *editor = GetEditor(index);
2804 wxGridCellEditor *editorOld = editor;
2805 editor = editor->Clone();
2806 editorOld->DecRef();
2807
2808 // do it even if there are no parameters to reset them to defaults
2809 wxString params = typeName.AfterFirst(_T(':'));
2810 renderer->SetParameters(params);
2811 editor->SetParameters(params);
2812
2813 // register the new typename
2814 RegisterDataType(typeName, renderer, editor);
2815
2816 // we just registered it, it's the last one
2817 index = m_typeinfo.GetCount() - 1;
2818 }
2819
2820 return index;
2821 }
2822
2823 wxGridCellRenderer* wxGridTypeRegistry::GetRenderer(int index)
2824 {
2825 wxGridCellRenderer* renderer = m_typeinfo[index]->m_renderer;
2826 if (renderer)
2827 renderer->IncRef();
2828 return renderer;
2829 }
2830
2831 wxGridCellEditor* wxGridTypeRegistry::GetEditor(int index)
2832 {
2833 wxGridCellEditor* editor = m_typeinfo[index]->m_editor;
2834 if (editor)
2835 editor->IncRef();
2836 return editor;
2837 }
2838
2839 // ----------------------------------------------------------------------------
2840 // wxGridTableBase
2841 // ----------------------------------------------------------------------------
2842
2843 IMPLEMENT_ABSTRACT_CLASS( wxGridTableBase, wxObject )
2844
2845
2846 wxGridTableBase::wxGridTableBase()
2847 {
2848 m_view = (wxGrid *) NULL;
2849 m_attrProvider = (wxGridCellAttrProvider *) NULL;
2850 }
2851
2852 wxGridTableBase::~wxGridTableBase()
2853 {
2854 delete m_attrProvider;
2855 }
2856
2857 void wxGridTableBase::SetAttrProvider(wxGridCellAttrProvider *attrProvider)
2858 {
2859 delete m_attrProvider;
2860 m_attrProvider = attrProvider;
2861 }
2862
2863 bool wxGridTableBase::CanHaveAttributes()
2864 {
2865 if ( ! GetAttrProvider() )
2866 {
2867 // use the default attr provider by default
2868 SetAttrProvider(new wxGridCellAttrProvider);
2869 }
2870 return TRUE;
2871 }
2872
2873 wxGridCellAttr *wxGridTableBase::GetAttr(int row, int col, wxGridCellAttr::wxAttrKind kind)
2874 {
2875 if ( m_attrProvider )
2876 return m_attrProvider->GetAttr(row, col, kind);
2877 else
2878 return (wxGridCellAttr *)NULL;
2879 }
2880
2881 void wxGridTableBase::SetAttr(wxGridCellAttr* attr, int row, int col)
2882 {
2883 if ( m_attrProvider )
2884 {
2885 attr->SetKind(wxGridCellAttr::Cell);
2886 m_attrProvider->SetAttr(attr, row, col);
2887 }
2888 else
2889 {
2890 // as we take ownership of the pointer and don't store it, we must
2891 // free it now
2892 wxSafeDecRef(attr);
2893 }
2894 }
2895
2896 void wxGridTableBase::SetRowAttr(wxGridCellAttr *attr, int row)
2897 {
2898 if ( m_attrProvider )
2899 {
2900 attr->SetKind(wxGridCellAttr::Row);
2901 m_attrProvider->SetRowAttr(attr, row);
2902 }
2903 else
2904 {
2905 // as we take ownership of the pointer and don't store it, we must
2906 // free it now
2907 wxSafeDecRef(attr);
2908 }
2909 }
2910
2911 void wxGridTableBase::SetColAttr(wxGridCellAttr *attr, int col)
2912 {
2913 if ( m_attrProvider )
2914 {
2915 attr->SetKind(wxGridCellAttr::Col);
2916 m_attrProvider->SetColAttr(attr, col);
2917 }
2918 else
2919 {
2920 // as we take ownership of the pointer and don't store it, we must
2921 // free it now
2922 wxSafeDecRef(attr);
2923 }
2924 }
2925
2926 bool wxGridTableBase::InsertRows( size_t WXUNUSED(pos),
2927 size_t WXUNUSED(numRows) )
2928 {
2929 wxFAIL_MSG( wxT("Called grid table class function InsertRows\nbut your derived table class does not override this function") );
2930
2931 return FALSE;
2932 }
2933
2934 bool wxGridTableBase::AppendRows( size_t WXUNUSED(numRows) )
2935 {
2936 wxFAIL_MSG( wxT("Called grid table class function AppendRows\nbut your derived table class does not override this function"));
2937
2938 return FALSE;
2939 }
2940
2941 bool wxGridTableBase::DeleteRows( size_t WXUNUSED(pos),
2942 size_t WXUNUSED(numRows) )
2943 {
2944 wxFAIL_MSG( wxT("Called grid table class function DeleteRows\nbut your derived table class does not override this function"));
2945
2946 return FALSE;
2947 }
2948
2949 bool wxGridTableBase::InsertCols( size_t WXUNUSED(pos),
2950 size_t WXUNUSED(numCols) )
2951 {
2952 wxFAIL_MSG( wxT("Called grid table class function InsertCols\nbut your derived table class does not override this function"));
2953
2954 return FALSE;
2955 }
2956
2957 bool wxGridTableBase::AppendCols( size_t WXUNUSED(numCols) )
2958 {
2959 wxFAIL_MSG(wxT("Called grid table class function AppendCols\nbut your derived table class does not override this function"));
2960
2961 return FALSE;
2962 }
2963
2964 bool wxGridTableBase::DeleteCols( size_t WXUNUSED(pos),
2965 size_t WXUNUSED(numCols) )
2966 {
2967 wxFAIL_MSG( wxT("Called grid table class function DeleteCols\nbut your derived table class does not override this function"));
2968
2969 return FALSE;
2970 }
2971
2972
2973 wxString wxGridTableBase::GetRowLabelValue( int row )
2974 {
2975 wxString s;
2976 s << row + 1; // RD: Starting the rows at zero confuses users, no matter
2977 // how much it makes sense to us geeks.
2978 return s;
2979 }
2980
2981 wxString wxGridTableBase::GetColLabelValue( int col )
2982 {
2983 // default col labels are:
2984 // cols 0 to 25 : A-Z
2985 // cols 26 to 675 : AA-ZZ
2986 // etc.
2987
2988 wxString s;
2989 unsigned int i, n;
2990 for ( n = 1; ; n++ )
2991 {
2992 s += (_T('A') + (wxChar)( col%26 ));
2993 col = col/26 - 1;
2994 if ( col < 0 ) break;
2995 }
2996
2997 // reverse the string...
2998 wxString s2;
2999 for ( i = 0; i < n; i++ )
3000 {
3001 s2 += s[n-i-1];
3002 }
3003
3004 return s2;
3005 }
3006
3007
3008 wxString wxGridTableBase::GetTypeName( int WXUNUSED(row), int WXUNUSED(col) )
3009 {
3010 return wxGRID_VALUE_STRING;
3011 }
3012
3013 bool wxGridTableBase::CanGetValueAs( int WXUNUSED(row), int WXUNUSED(col),
3014 const wxString& typeName )
3015 {
3016 return typeName == wxGRID_VALUE_STRING;
3017 }
3018
3019 bool wxGridTableBase::CanSetValueAs( int row, int col, const wxString& typeName )
3020 {
3021 return CanGetValueAs(row, col, typeName);
3022 }
3023
3024 long wxGridTableBase::GetValueAsLong( int WXUNUSED(row), int WXUNUSED(col) )
3025 {
3026 return 0;
3027 }
3028
3029 double wxGridTableBase::GetValueAsDouble( int WXUNUSED(row), int WXUNUSED(col) )
3030 {
3031 return 0.0;
3032 }
3033
3034 bool wxGridTableBase::GetValueAsBool( int WXUNUSED(row), int WXUNUSED(col) )
3035 {
3036 return FALSE;
3037 }
3038
3039 void wxGridTableBase::SetValueAsLong( int WXUNUSED(row), int WXUNUSED(col),
3040 long WXUNUSED(value) )
3041 {
3042 }
3043
3044 void wxGridTableBase::SetValueAsDouble( int WXUNUSED(row), int WXUNUSED(col),
3045 double WXUNUSED(value) )
3046 {
3047 }
3048
3049 void wxGridTableBase::SetValueAsBool( int WXUNUSED(row), int WXUNUSED(col),
3050 bool WXUNUSED(value) )
3051 {
3052 }
3053
3054
3055 void* wxGridTableBase::GetValueAsCustom( int WXUNUSED(row), int WXUNUSED(col),
3056 const wxString& WXUNUSED(typeName) )
3057 {
3058 return NULL;
3059 }
3060
3061 void wxGridTableBase::SetValueAsCustom( int WXUNUSED(row), int WXUNUSED(col),
3062 const wxString& WXUNUSED(typeName),
3063 void* WXUNUSED(value) )
3064 {
3065 }
3066
3067 //////////////////////////////////////////////////////////////////////
3068 //
3069 // Message class for the grid table to send requests and notifications
3070 // to the grid view
3071 //
3072
3073 wxGridTableMessage::wxGridTableMessage()
3074 {
3075 m_table = (wxGridTableBase *) NULL;
3076 m_id = -1;
3077 m_comInt1 = -1;
3078 m_comInt2 = -1;
3079 }
3080
3081 wxGridTableMessage::wxGridTableMessage( wxGridTableBase *table, int id,
3082 int commandInt1, int commandInt2 )
3083 {
3084 m_table = table;
3085 m_id = id;
3086 m_comInt1 = commandInt1;
3087 m_comInt2 = commandInt2;
3088 }
3089
3090
3091
3092 //////////////////////////////////////////////////////////////////////
3093 //
3094 // A basic grid table for string data. An object of this class will
3095 // created by wxGrid if you don't specify an alternative table class.
3096 //
3097
3098 WX_DEFINE_OBJARRAY(wxGridStringArray)
3099
3100 IMPLEMENT_DYNAMIC_CLASS( wxGridStringTable, wxGridTableBase )
3101
3102 wxGridStringTable::wxGridStringTable()
3103 : wxGridTableBase()
3104 {
3105 }
3106
3107 wxGridStringTable::wxGridStringTable( int numRows, int numCols )
3108 : wxGridTableBase()
3109 {
3110 m_data.Alloc( numRows );
3111
3112 wxArrayString sa;
3113 sa.Alloc( numCols );
3114 sa.Add( wxEmptyString, numCols );
3115
3116 m_data.Add( sa, numRows );
3117 }
3118
3119 wxGridStringTable::~wxGridStringTable()
3120 {
3121 }
3122
3123 int wxGridStringTable::GetNumberRows()
3124 {
3125 return m_data.GetCount();
3126 }
3127
3128 int wxGridStringTable::GetNumberCols()
3129 {
3130 if ( m_data.GetCount() > 0 )
3131 return m_data[0].GetCount();
3132 else
3133 return 0;
3134 }
3135
3136 wxString wxGridStringTable::GetValue( int row, int col )
3137 {
3138 wxASSERT_MSG( (row < GetNumberRows()) && (col < GetNumberCols()),
3139 _T("invalid row or column index in wxGridStringTable") );
3140
3141 return m_data[row][col];
3142 }
3143
3144 void wxGridStringTable::SetValue( int row, int col, const wxString& value )
3145 {
3146 wxASSERT_MSG( (row < GetNumberRows()) && (col < GetNumberCols()),
3147 _T("invalid row or column index in wxGridStringTable") );
3148
3149 m_data[row][col] = value;
3150 }
3151
3152 bool wxGridStringTable::IsEmptyCell( int row, int col )
3153 {
3154 wxASSERT_MSG( (row < GetNumberRows()) && (col < GetNumberCols()),
3155 _T("invalid row or column index in wxGridStringTable") );
3156
3157 return (m_data[row][col] == wxEmptyString);
3158 }
3159
3160 void wxGridStringTable::Clear()
3161 {
3162 int row, col;
3163 int numRows, numCols;
3164
3165 numRows = m_data.GetCount();
3166 if ( numRows > 0 )
3167 {
3168 numCols = m_data[0].GetCount();
3169
3170 for ( row = 0; row < numRows; row++ )
3171 {
3172 for ( col = 0; col < numCols; col++ )
3173 {
3174 m_data[row][col] = wxEmptyString;
3175 }
3176 }
3177 }
3178 }
3179
3180
3181 bool wxGridStringTable::InsertRows( size_t pos, size_t numRows )
3182 {
3183 size_t curNumRows = m_data.GetCount();
3184 size_t curNumCols = ( curNumRows > 0 ? m_data[0].GetCount() :
3185 ( GetView() ? GetView()->GetNumberCols() : 0 ) );
3186
3187 if ( pos >= curNumRows )
3188 {
3189 return AppendRows( numRows );
3190 }
3191
3192 wxArrayString sa;
3193 sa.Alloc( curNumCols );
3194 sa.Add( wxEmptyString, curNumCols );
3195 m_data.Insert( sa, pos, numRows );
3196 if ( GetView() )
3197 {
3198 wxGridTableMessage msg( this,
3199 wxGRIDTABLE_NOTIFY_ROWS_INSERTED,
3200 pos,
3201 numRows );
3202
3203 GetView()->ProcessTableMessage( msg );
3204 }
3205
3206 return TRUE;
3207 }
3208
3209 bool wxGridStringTable::AppendRows( size_t numRows )
3210 {
3211 size_t curNumRows = m_data.GetCount();
3212 size_t curNumCols = ( curNumRows > 0 ? m_data[0].GetCount() :
3213 ( GetView() ? GetView()->GetNumberCols() : 0 ) );
3214
3215 wxArrayString sa;
3216 if ( curNumCols > 0 )
3217 {
3218 sa.Alloc( curNumCols );
3219 sa.Add( wxEmptyString, curNumCols );
3220 }
3221
3222 m_data.Add( sa, numRows );
3223
3224 if ( GetView() )
3225 {
3226 wxGridTableMessage msg( this,
3227 wxGRIDTABLE_NOTIFY_ROWS_APPENDED,
3228 numRows );
3229
3230 GetView()->ProcessTableMessage( msg );
3231 }
3232
3233 return TRUE;
3234 }
3235
3236 bool wxGridStringTable::DeleteRows( size_t pos, size_t numRows )
3237 {
3238 size_t curNumRows = m_data.GetCount();
3239
3240 if ( pos >= curNumRows )
3241 {
3242 wxFAIL_MSG( wxString::Format
3243 (
3244 wxT("Called wxGridStringTable::DeleteRows(pos=%lu, N=%lu)\nPos value is invalid for present table with %lu rows"),
3245 (unsigned long)pos,
3246 (unsigned long)numRows,
3247 (unsigned long)curNumRows
3248 ) );
3249
3250 return FALSE;
3251 }
3252
3253 if ( numRows > curNumRows - pos )
3254 {
3255 numRows = curNumRows - pos;
3256 }
3257
3258 if ( numRows >= curNumRows )
3259 {
3260 m_data.Clear();
3261 }
3262 else
3263 {
3264 m_data.RemoveAt( pos, numRows );
3265 }
3266 if ( GetView() )
3267 {
3268 wxGridTableMessage msg( this,
3269 wxGRIDTABLE_NOTIFY_ROWS_DELETED,
3270 pos,
3271 numRows );
3272
3273 GetView()->ProcessTableMessage( msg );
3274 }
3275
3276 return TRUE;
3277 }
3278
3279 bool wxGridStringTable::InsertCols( size_t pos, size_t numCols )
3280 {
3281 size_t row, col;
3282
3283 size_t curNumRows = m_data.GetCount();
3284 size_t curNumCols = ( curNumRows > 0 ? m_data[0].GetCount() :
3285 ( GetView() ? GetView()->GetNumberCols() : 0 ) );
3286
3287 if ( pos >= curNumCols )
3288 {
3289 return AppendCols( numCols );
3290 }
3291
3292 for ( row = 0; row < curNumRows; row++ )
3293 {
3294 for ( col = pos; col < pos + numCols; col++ )
3295 {
3296 m_data[row].Insert( wxEmptyString, col );
3297 }
3298 }
3299 if ( GetView() )
3300 {
3301 wxGridTableMessage msg( this,
3302 wxGRIDTABLE_NOTIFY_COLS_INSERTED,
3303 pos,
3304 numCols );
3305
3306 GetView()->ProcessTableMessage( msg );
3307 }
3308
3309 return TRUE;
3310 }
3311
3312 bool wxGridStringTable::AppendCols( size_t numCols )
3313 {
3314 size_t row;
3315
3316 size_t curNumRows = m_data.GetCount();
3317 #if 0
3318 if ( !curNumRows )
3319 {
3320 // TODO: something better than this ?
3321 //
3322 wxFAIL_MSG( wxT("Unable to append cols to a grid table with no rows.\nCall AppendRows() first") );
3323 return FALSE;
3324 }
3325 #endif
3326
3327 for ( row = 0; row < curNumRows; row++ )
3328 {
3329 m_data[row].Add( wxEmptyString, numCols );
3330 }
3331
3332 if ( GetView() )
3333 {
3334 wxGridTableMessage msg( this,
3335 wxGRIDTABLE_NOTIFY_COLS_APPENDED,
3336 numCols );
3337
3338 GetView()->ProcessTableMessage( msg );
3339 }
3340
3341 return TRUE;
3342 }
3343
3344 bool wxGridStringTable::DeleteCols( size_t pos, size_t numCols )
3345 {
3346 size_t row;
3347
3348 size_t curNumRows = m_data.GetCount();
3349 size_t curNumCols = ( curNumRows > 0 ? m_data[0].GetCount() :
3350 ( GetView() ? GetView()->GetNumberCols() : 0 ) );
3351
3352 if ( pos >= curNumCols )
3353 {
3354 wxFAIL_MSG( wxString::Format
3355 (
3356 wxT("Called wxGridStringTable::DeleteCols(pos=%lu, N=%lu)\nPos value is invalid for present table with %lu cols"),
3357 (unsigned long)pos,
3358 (unsigned long)numCols,
3359 (unsigned long)curNumCols
3360 ) );
3361 return FALSE;
3362 }
3363
3364 if ( numCols > curNumCols - pos )
3365 {
3366 numCols = curNumCols - pos;
3367 }
3368
3369 for ( row = 0; row < curNumRows; row++ )
3370 {
3371 if ( numCols >= curNumCols )
3372 {
3373 m_data[row].Clear();
3374 }
3375 else
3376 {
3377 m_data[row].RemoveAt( pos, numCols );
3378 }
3379 }
3380 if ( GetView() )
3381 {
3382 wxGridTableMessage msg( this,
3383 wxGRIDTABLE_NOTIFY_COLS_DELETED,
3384 pos,
3385 numCols );
3386
3387 GetView()->ProcessTableMessage( msg );
3388 }
3389
3390 return TRUE;
3391 }
3392
3393 wxString wxGridStringTable::GetRowLabelValue( int row )
3394 {
3395 if ( row > (int)(m_rowLabels.GetCount()) - 1 )
3396 {
3397 // using default label
3398 //
3399 return wxGridTableBase::GetRowLabelValue( row );
3400 }
3401 else
3402 {
3403 return m_rowLabels[ row ];
3404 }
3405 }
3406
3407 wxString wxGridStringTable::GetColLabelValue( int col )
3408 {
3409 if ( col > (int)(m_colLabels.GetCount()) - 1 )
3410 {
3411 // using default label
3412 //
3413 return wxGridTableBase::GetColLabelValue( col );
3414 }
3415 else
3416 {
3417 return m_colLabels[ col ];
3418 }
3419 }
3420
3421 void wxGridStringTable::SetRowLabelValue( int row, const wxString& value )
3422 {
3423 if ( row > (int)(m_rowLabels.GetCount()) - 1 )
3424 {
3425 int n = m_rowLabels.GetCount();
3426 int i;
3427 for ( i = n; i <= row; i++ )
3428 {
3429 m_rowLabels.Add( wxGridTableBase::GetRowLabelValue(i) );
3430 }
3431 }
3432
3433 m_rowLabels[row] = value;
3434 }
3435
3436 void wxGridStringTable::SetColLabelValue( int col, const wxString& value )
3437 {
3438 if ( col > (int)(m_colLabels.GetCount()) - 1 )
3439 {
3440 int n = m_colLabels.GetCount();
3441 int i;
3442 for ( i = n; i <= col; i++ )
3443 {
3444 m_colLabels.Add( wxGridTableBase::GetColLabelValue(i) );
3445 }
3446 }
3447
3448 m_colLabels[col] = value;
3449 }
3450
3451
3452
3453 //////////////////////////////////////////////////////////////////////
3454 //////////////////////////////////////////////////////////////////////
3455
3456 IMPLEMENT_DYNAMIC_CLASS( wxGridRowLabelWindow, wxWindow )
3457
3458 BEGIN_EVENT_TABLE( wxGridRowLabelWindow, wxWindow )
3459 EVT_PAINT( wxGridRowLabelWindow::OnPaint )
3460 EVT_MOUSEWHEEL( wxGridRowLabelWindow::OnMouseWheel)
3461 EVT_MOUSE_EVENTS( wxGridRowLabelWindow::OnMouseEvent )
3462 EVT_KEY_DOWN( wxGridRowLabelWindow::OnKeyDown )
3463 EVT_KEY_UP( wxGridRowLabelWindow::OnKeyUp )
3464 END_EVENT_TABLE()
3465
3466 wxGridRowLabelWindow::wxGridRowLabelWindow( wxGrid *parent,
3467 wxWindowID id,
3468 const wxPoint &pos, const wxSize &size )
3469 : wxWindow( parent, id, pos, size, wxWANTS_CHARS|wxBORDER_NONE )
3470 {
3471 m_owner = parent;
3472 }
3473
3474 void wxGridRowLabelWindow::OnPaint( wxPaintEvent& WXUNUSED(event) )
3475 {
3476 wxPaintDC dc(this);
3477
3478 // NO - don't do this because it will set both the x and y origin
3479 // coords to match the parent scrolled window and we just want to
3480 // set the y coord - MB
3481 //
3482 // m_owner->PrepareDC( dc );
3483
3484 int x, y;
3485 m_owner->CalcUnscrolledPosition( 0, 0, &x, &y );
3486 dc.SetDeviceOrigin( 0, -y );
3487
3488 wxArrayInt rows = m_owner->CalcRowLabelsExposed( GetUpdateRegion() );
3489 m_owner->DrawRowLabels( dc , rows );
3490 }
3491
3492
3493 void wxGridRowLabelWindow::OnMouseEvent( wxMouseEvent& event )
3494 {
3495 m_owner->ProcessRowLabelMouseEvent( event );
3496 }
3497
3498
3499 void wxGridRowLabelWindow::OnMouseWheel( wxMouseEvent& event )
3500 {
3501 m_owner->GetEventHandler()->ProcessEvent(event);
3502 }
3503
3504
3505 // This seems to be required for wxMotif otherwise the mouse
3506 // cursor must be in the cell edit control to get key events
3507 //
3508 void wxGridRowLabelWindow::OnKeyDown( wxKeyEvent& event )
3509 {
3510 if ( !m_owner->GetEventHandler()->ProcessEvent( event ) ) event.Skip();
3511 }
3512
3513 void wxGridRowLabelWindow::OnKeyUp( wxKeyEvent& event )
3514 {
3515 if ( !m_owner->GetEventHandler()->ProcessEvent( event ) ) event.Skip();
3516 }
3517
3518
3519
3520 //////////////////////////////////////////////////////////////////////
3521
3522 IMPLEMENT_DYNAMIC_CLASS( wxGridColLabelWindow, wxWindow )
3523
3524 BEGIN_EVENT_TABLE( wxGridColLabelWindow, wxWindow )
3525 EVT_PAINT( wxGridColLabelWindow::OnPaint )
3526 EVT_MOUSEWHEEL( wxGridColLabelWindow::OnMouseWheel)
3527 EVT_MOUSE_EVENTS( wxGridColLabelWindow::OnMouseEvent )
3528 EVT_KEY_DOWN( wxGridColLabelWindow::OnKeyDown )
3529 EVT_KEY_UP( wxGridColLabelWindow::OnKeyUp )
3530 END_EVENT_TABLE()
3531
3532 wxGridColLabelWindow::wxGridColLabelWindow( wxGrid *parent,
3533 wxWindowID id,
3534 const wxPoint &pos, const wxSize &size )
3535 : wxWindow( parent, id, pos, size, wxWANTS_CHARS|wxBORDER_NONE )
3536 {
3537 m_owner = parent;
3538 }
3539
3540 void wxGridColLabelWindow::OnPaint( wxPaintEvent& WXUNUSED(event) )
3541 {
3542 wxPaintDC dc(this);
3543
3544 // NO - don't do this because it will set both the x and y origin
3545 // coords to match the parent scrolled window and we just want to
3546 // set the x coord - MB
3547 //
3548 // m_owner->PrepareDC( dc );
3549
3550 int x, y;
3551 m_owner->CalcUnscrolledPosition( 0, 0, &x, &y );
3552 dc.SetDeviceOrigin( -x, 0 );
3553
3554 wxArrayInt cols = m_owner->CalcColLabelsExposed( GetUpdateRegion() );
3555 m_owner->DrawColLabels( dc , cols );
3556 }
3557
3558
3559 void wxGridColLabelWindow::OnMouseEvent( wxMouseEvent& event )
3560 {
3561 m_owner->ProcessColLabelMouseEvent( event );
3562 }
3563
3564 void wxGridColLabelWindow::OnMouseWheel( wxMouseEvent& event )
3565 {
3566 m_owner->GetEventHandler()->ProcessEvent(event);
3567 }
3568
3569
3570 // This seems to be required for wxMotif otherwise the mouse
3571 // cursor must be in the cell edit control to get key events
3572 //
3573 void wxGridColLabelWindow::OnKeyDown( wxKeyEvent& event )
3574 {
3575 if ( !m_owner->GetEventHandler()->ProcessEvent( event ) ) event.Skip();
3576 }
3577
3578 void wxGridColLabelWindow::OnKeyUp( wxKeyEvent& event )
3579 {
3580 if ( !m_owner->GetEventHandler()->ProcessEvent( event ) ) event.Skip();
3581 }
3582
3583
3584
3585 //////////////////////////////////////////////////////////////////////
3586
3587 IMPLEMENT_DYNAMIC_CLASS( wxGridCornerLabelWindow, wxWindow )
3588
3589 BEGIN_EVENT_TABLE( wxGridCornerLabelWindow, wxWindow )
3590 EVT_MOUSEWHEEL( wxGridCornerLabelWindow::OnMouseWheel)
3591 EVT_MOUSE_EVENTS( wxGridCornerLabelWindow::OnMouseEvent )
3592 EVT_PAINT( wxGridCornerLabelWindow::OnPaint)
3593 EVT_KEY_DOWN( wxGridCornerLabelWindow::OnKeyDown )
3594 EVT_KEY_UP( wxGridCornerLabelWindow::OnKeyUp )
3595 END_EVENT_TABLE()
3596
3597 wxGridCornerLabelWindow::wxGridCornerLabelWindow( wxGrid *parent,
3598 wxWindowID id,
3599 const wxPoint &pos, const wxSize &size )
3600 : wxWindow( parent, id, pos, size, wxWANTS_CHARS|wxBORDER_NONE )
3601 {
3602 m_owner = parent;
3603 }
3604
3605 void wxGridCornerLabelWindow::OnPaint( wxPaintEvent& WXUNUSED(event) )
3606 {
3607 wxPaintDC dc(this);
3608
3609 int client_height = 0;
3610 int client_width = 0;
3611 GetClientSize( &client_width, &client_height );
3612
3613 dc.SetPen( wxPen(wxSystemSettings::GetColour(wxSYS_COLOUR_3DDKSHADOW),1, wxSOLID) );
3614 dc.DrawLine( client_width-1, client_height-1, client_width-1, 0 );
3615 dc.DrawLine( client_width-1, client_height-1, 0, client_height-1 );
3616 dc.DrawLine( 0, 0, client_width, 0 );
3617 dc.DrawLine( 0, 0, 0, client_height );
3618
3619 dc.SetPen( *wxWHITE_PEN );
3620 dc.DrawLine( 1, 1, client_width-1, 1 );
3621 dc.DrawLine( 1, 1, 1, client_height-1 );
3622 }
3623
3624
3625 void wxGridCornerLabelWindow::OnMouseEvent( wxMouseEvent& event )
3626 {
3627 m_owner->ProcessCornerLabelMouseEvent( event );
3628 }
3629
3630
3631 void wxGridCornerLabelWindow::OnMouseWheel( wxMouseEvent& event )
3632 {
3633 m_owner->GetEventHandler()->ProcessEvent(event);
3634 }
3635
3636 // This seems to be required for wxMotif otherwise the mouse
3637 // cursor must be in the cell edit control to get key events
3638 //
3639 void wxGridCornerLabelWindow::OnKeyDown( wxKeyEvent& event )
3640 {
3641 if ( !m_owner->GetEventHandler()->ProcessEvent( event ) ) event.Skip();
3642 }
3643
3644 void wxGridCornerLabelWindow::OnKeyUp( wxKeyEvent& event )
3645 {
3646 if ( !m_owner->GetEventHandler()->ProcessEvent( event ) ) event.Skip();
3647 }
3648
3649
3650
3651 //////////////////////////////////////////////////////////////////////
3652
3653 IMPLEMENT_DYNAMIC_CLASS( wxGridWindow, wxWindow )
3654
3655 BEGIN_EVENT_TABLE( wxGridWindow, wxWindow )
3656 EVT_PAINT( wxGridWindow::OnPaint )
3657 EVT_MOUSEWHEEL( wxGridWindow::OnMouseWheel)
3658 EVT_MOUSE_EVENTS( wxGridWindow::OnMouseEvent )
3659 EVT_KEY_DOWN( wxGridWindow::OnKeyDown )
3660 EVT_KEY_UP( wxGridWindow::OnKeyUp )
3661 EVT_ERASE_BACKGROUND( wxGridWindow::OnEraseBackground )
3662 END_EVENT_TABLE()
3663
3664 wxGridWindow::wxGridWindow( wxGrid *parent,
3665 wxGridRowLabelWindow *rowLblWin,
3666 wxGridColLabelWindow *colLblWin,
3667 wxWindowID id,
3668 const wxPoint &pos,
3669 const wxSize &size )
3670 : wxWindow( parent, id, pos, size, wxWANTS_CHARS | wxBORDER_NONE | wxCLIP_CHILDREN,
3671 wxT("grid window") )
3672
3673 {
3674 m_owner = parent;
3675 m_rowLabelWin = rowLblWin;
3676 m_colLabelWin = colLblWin;
3677 SetBackgroundColour(_T("WHITE"));
3678 }
3679
3680
3681 wxGridWindow::~wxGridWindow()
3682 {
3683 }
3684
3685
3686 void wxGridWindow::OnPaint( wxPaintEvent &WXUNUSED(event) )
3687 {
3688 wxPaintDC dc( this );
3689 m_owner->PrepareDC( dc );
3690 wxRegion reg = GetUpdateRegion();
3691 wxGridCellCoordsArray DirtyCells = m_owner->CalcCellsExposed( reg );
3692 m_owner->DrawGridCellArea( dc , DirtyCells);
3693 #if WXGRID_DRAW_LINES
3694 m_owner->DrawAllGridLines( dc, reg );
3695 #endif
3696 m_owner->DrawGridSpace( dc );
3697 m_owner->DrawHighlight( dc , DirtyCells );
3698 }
3699
3700
3701 void wxGridWindow::ScrollWindow( int dx, int dy, const wxRect *rect )
3702 {
3703 wxWindow::ScrollWindow( dx, dy, rect );
3704 m_rowLabelWin->ScrollWindow( 0, dy, rect );
3705 m_colLabelWin->ScrollWindow( dx, 0, rect );
3706 }
3707
3708
3709 void wxGridWindow::OnMouseEvent( wxMouseEvent& event )
3710 {
3711 m_owner->ProcessGridCellMouseEvent( event );
3712 }
3713
3714 void wxGridWindow::OnMouseWheel( wxMouseEvent& event )
3715 {
3716 m_owner->GetEventHandler()->ProcessEvent(event);
3717 }
3718
3719 // This seems to be required for wxMotif/wxGTK otherwise the mouse
3720 // cursor must be in the cell edit control to get key events
3721 //
3722 void wxGridWindow::OnKeyDown( wxKeyEvent& event )
3723 {
3724 if ( !m_owner->GetEventHandler()->ProcessEvent( event ) ) event.Skip();
3725 }
3726
3727 void wxGridWindow::OnKeyUp( wxKeyEvent& event )
3728 {
3729 if ( !m_owner->GetEventHandler()->ProcessEvent( event ) ) event.Skip();
3730 }
3731
3732 void wxGridWindow::OnEraseBackground( wxEraseEvent& WXUNUSED(event) )
3733 {
3734 }
3735
3736
3737 //////////////////////////////////////////////////////////////////////
3738
3739 // Internal Helper function for computing row or column from some
3740 // (unscrolled) coordinate value, using either
3741 // m_defaultRowHeight/m_defaultColWidth or binary search on array
3742 // of m_rowBottoms/m_ColRights to speed up the search!
3743
3744 // Internal helper macros for simpler use of that function
3745
3746 static int CoordToRowOrCol(int coord, int defaultDist, int minDist,
3747 const wxArrayInt& BorderArray, int nMax,
3748 bool clipToMinMax);
3749
3750 #define internalXToCol(x) CoordToRowOrCol(x, m_defaultColWidth, \
3751 m_minAcceptableColWidth, \
3752 m_colRights, m_numCols, TRUE)
3753 #define internalYToRow(y) CoordToRowOrCol(y, m_defaultRowHeight, \
3754 m_minAcceptableRowHeight, \
3755 m_rowBottoms, m_numRows, TRUE)
3756 /////////////////////////////////////////////////////////////////////
3757
3758 IMPLEMENT_DYNAMIC_CLASS( wxGrid, wxScrolledWindow )
3759
3760 BEGIN_EVENT_TABLE( wxGrid, wxScrolledWindow )
3761 EVT_PAINT( wxGrid::OnPaint )
3762 EVT_SIZE( wxGrid::OnSize )
3763 EVT_KEY_DOWN( wxGrid::OnKeyDown )
3764 EVT_KEY_UP( wxGrid::OnKeyUp )
3765 EVT_ERASE_BACKGROUND( wxGrid::OnEraseBackground )
3766 END_EVENT_TABLE()
3767
3768 wxGrid::wxGrid( wxWindow *parent,
3769 wxWindowID id,
3770 const wxPoint& pos,
3771 const wxSize& size,
3772 long style,
3773 const wxString& name )
3774 : wxScrolledWindow( parent, id, pos, size, (style | wxWANTS_CHARS), name ),
3775 m_colMinWidths(GRID_HASH_SIZE),
3776 m_rowMinHeights(GRID_HASH_SIZE)
3777 {
3778 Create();
3779 }
3780
3781
3782 wxGrid::~wxGrid()
3783 {
3784 // Must do this or ~wxScrollHelper will pop the wrong event handler
3785 SetTargetWindow(this);
3786 ClearAttrCache();
3787 wxSafeDecRef(m_defaultCellAttr);
3788
3789 #ifdef DEBUG_ATTR_CACHE
3790 size_t total = gs_nAttrCacheHits + gs_nAttrCacheMisses;
3791 wxPrintf(_T("wxGrid attribute cache statistics: "
3792 "total: %u, hits: %u (%u%%)\n"),
3793 total, gs_nAttrCacheHits,
3794 total ? (gs_nAttrCacheHits*100) / total : 0);
3795 #endif
3796
3797 if (m_ownTable)
3798 delete m_table;
3799
3800 delete m_typeRegistry;
3801 delete m_selection;
3802 }
3803
3804
3805 //
3806 // ----- internal init and update functions
3807 //
3808
3809 void wxGrid::Create()
3810 {
3811 m_created = FALSE; // set to TRUE by CreateGrid
3812
3813 m_table = (wxGridTableBase *) NULL;
3814 m_ownTable = FALSE;
3815
3816 m_cellEditCtrlEnabled = FALSE;
3817
3818 m_defaultCellAttr = new wxGridCellAttr();
3819
3820 // Set default cell attributes
3821 m_defaultCellAttr->SetDefAttr(m_defaultCellAttr);
3822 m_defaultCellAttr->SetKind(wxGridCellAttr::Default);
3823 m_defaultCellAttr->SetFont(GetFont());
3824 m_defaultCellAttr->SetAlignment(wxALIGN_LEFT, wxALIGN_TOP);
3825 m_defaultCellAttr->SetTextColour(
3826 wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT));
3827 m_defaultCellAttr->SetBackgroundColour(
3828 wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW));
3829 m_defaultCellAttr->SetRenderer(new wxGridCellStringRenderer);
3830 m_defaultCellAttr->SetEditor(new wxGridCellTextEditor);
3831
3832
3833 m_numRows = 0;
3834 m_numCols = 0;
3835 m_currentCellCoords = wxGridNoCellCoords;
3836
3837 m_rowLabelWidth = WXGRID_DEFAULT_ROW_LABEL_WIDTH;
3838 m_colLabelHeight = WXGRID_DEFAULT_COL_LABEL_HEIGHT;
3839
3840 // create the type registry
3841 m_typeRegistry = new wxGridTypeRegistry;
3842 m_selection = NULL;
3843
3844 // subwindow components that make up the wxGrid
3845 m_cornerLabelWin = new wxGridCornerLabelWindow( this,
3846 -1,
3847 wxDefaultPosition,
3848 wxDefaultSize );
3849
3850 m_rowLabelWin = new wxGridRowLabelWindow( this,
3851 -1,
3852 wxDefaultPosition,
3853 wxDefaultSize );
3854
3855 m_colLabelWin = new wxGridColLabelWindow( this,
3856 -1,
3857 wxDefaultPosition,
3858 wxDefaultSize );
3859
3860 m_gridWin = new wxGridWindow( this,
3861 m_rowLabelWin,
3862 m_colLabelWin,
3863 -1,
3864 wxDefaultPosition,
3865 wxDefaultSize );
3866
3867 SetTargetWindow( m_gridWin );
3868
3869 Init();
3870 }
3871
3872
3873 bool wxGrid::CreateGrid( int numRows, int numCols,
3874 wxGrid::wxGridSelectionModes selmode )
3875 {
3876 wxCHECK_MSG( !m_created,
3877 FALSE,
3878 wxT("wxGrid::CreateGrid or wxGrid::SetTable called more than once") );
3879
3880 m_numRows = numRows;
3881 m_numCols = numCols;
3882
3883 m_table = new wxGridStringTable( m_numRows, m_numCols );
3884 m_table->SetView( this );
3885 m_ownTable = TRUE;
3886 m_selection = new wxGridSelection( this, selmode );
3887
3888 CalcDimensions();
3889
3890 m_created = TRUE;
3891
3892 return m_created;
3893 }
3894
3895 void wxGrid::SetSelectionMode(wxGrid::wxGridSelectionModes selmode)
3896 {
3897 wxCHECK_RET( m_created,
3898 wxT("Called wxGrid::SetSelectionMode() before calling CreateGrid()") );
3899
3900 m_selection->SetSelectionMode( selmode );
3901 }
3902
3903 wxGrid::wxGridSelectionModes wxGrid::GetSelectionMode() const
3904 {
3905 wxCHECK_MSG( m_created, wxGrid::wxGridSelectCells,
3906 wxT("Called wxGrid::GetSelectionMode() before calling CreateGrid()") );
3907
3908 return m_selection->GetSelectionMode();
3909 }
3910
3911 bool wxGrid::SetTable( wxGridTableBase *table, bool takeOwnership,
3912 wxGrid::wxGridSelectionModes selmode )
3913 {
3914 if ( m_created )
3915 {
3916 // stop all processing
3917 m_created = FALSE;
3918
3919 if (m_ownTable)
3920 {
3921 wxGridTableBase *t=m_table;
3922 m_table=0;
3923 delete t;
3924 }
3925 delete m_selection;
3926
3927 m_table=0;
3928 m_selection=0;
3929 m_numRows=0;
3930 m_numCols=0;
3931 }
3932 if (table)
3933 {
3934 m_numRows = table->GetNumberRows();
3935 m_numCols = table->GetNumberCols();
3936
3937 m_table = table;
3938 m_table->SetView( this );
3939 if (takeOwnership)
3940 m_ownTable = TRUE;
3941 m_selection = new wxGridSelection( this, selmode );
3942
3943 CalcDimensions();
3944
3945 m_created = TRUE;
3946 }
3947
3948 return m_created;
3949 }
3950
3951
3952 void wxGrid::Init()
3953 {
3954 m_rowLabelWidth = WXGRID_DEFAULT_ROW_LABEL_WIDTH;
3955 m_colLabelHeight = WXGRID_DEFAULT_COL_LABEL_HEIGHT;
3956
3957 if ( m_rowLabelWin )
3958 {
3959 m_labelBackgroundColour = m_rowLabelWin->GetBackgroundColour();
3960 }
3961 else
3962 {
3963 m_labelBackgroundColour = wxColour( _T("WHITE") );
3964 }
3965
3966 m_labelTextColour = wxColour( _T("BLACK") );
3967
3968 // init attr cache
3969 m_attrCache.row = -1;
3970 m_attrCache.col = -1;
3971 m_attrCache.attr = NULL;
3972
3973 // TODO: something better than this ?
3974 //
3975 m_labelFont = this->GetFont();
3976 m_labelFont.SetWeight( wxBOLD );
3977
3978 m_rowLabelHorizAlign = wxALIGN_CENTRE;
3979 m_rowLabelVertAlign = wxALIGN_CENTRE;
3980
3981 m_colLabelHorizAlign = wxALIGN_CENTRE;
3982 m_colLabelVertAlign = wxALIGN_CENTRE;
3983 m_colLabelTextOrientation = wxHORIZONTAL;
3984
3985 m_defaultColWidth = WXGRID_DEFAULT_COL_WIDTH;
3986 m_defaultRowHeight = m_gridWin->GetCharHeight();
3987
3988 m_minAcceptableColWidth = WXGRID_MIN_COL_WIDTH;
3989 m_minAcceptableRowHeight = WXGRID_MIN_ROW_HEIGHT;
3990
3991 #if defined(__WXMOTIF__) || defined(__WXGTK__) // see also text ctrl sizing in ShowCellEditControl()
3992 m_defaultRowHeight += 8;
3993 #else
3994 m_defaultRowHeight += 4;
3995 #endif
3996
3997 m_gridLineColour = wxColour( 192,192,192 );
3998 m_gridLinesEnabled = TRUE;
3999 m_cellHighlightColour = *wxBLACK;
4000 m_cellHighlightPenWidth = 2;
4001 m_cellHighlightROPenWidth = 1;
4002
4003 m_cursorMode = WXGRID_CURSOR_SELECT_CELL;
4004 m_winCapture = (wxWindow *)NULL;
4005 m_canDragRowSize = TRUE;
4006 m_canDragColSize = TRUE;
4007 m_canDragGridSize = TRUE;
4008 m_dragLastPos = -1;
4009 m_dragRowOrCol = -1;
4010 m_isDragging = FALSE;
4011 m_startDragPos = wxDefaultPosition;
4012
4013 m_waitForSlowClick = FALSE;
4014
4015 m_rowResizeCursor = wxCursor( wxCURSOR_SIZENS );
4016 m_colResizeCursor = wxCursor( wxCURSOR_SIZEWE );
4017
4018 m_currentCellCoords = wxGridNoCellCoords;
4019
4020 m_selectingTopLeft = wxGridNoCellCoords;
4021 m_selectingBottomRight = wxGridNoCellCoords;
4022 m_selectionBackground = wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHT);
4023 m_selectionForeground = wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHTTEXT);
4024
4025 m_editable = TRUE; // default for whole grid
4026
4027 m_inOnKeyDown = FALSE;
4028 m_batchCount = 0;
4029
4030 m_extraWidth =
4031 m_extraHeight = 0;
4032 }
4033
4034 // ----------------------------------------------------------------------------
4035 // the idea is to call these functions only when necessary because they create
4036 // quite big arrays which eat memory mostly unnecessary - in particular, if
4037 // default widths/heights are used for all rows/columns, we may not use these
4038 // arrays at all
4039 //
4040 // with some extra code, it should be possible to only store the
4041 // widths/heights different from default ones but this will be done later...
4042 // ----------------------------------------------------------------------------
4043
4044 void wxGrid::InitRowHeights()
4045 {
4046 m_rowHeights.Empty();
4047 m_rowBottoms.Empty();
4048
4049 m_rowHeights.Alloc( m_numRows );
4050 m_rowBottoms.Alloc( m_numRows );
4051
4052 int rowBottom = 0;
4053
4054 m_rowHeights.Add( m_defaultRowHeight, m_numRows );
4055
4056 for ( int i = 0; i < m_numRows; i++ )
4057 {
4058 rowBottom += m_defaultRowHeight;
4059 m_rowBottoms.Add( rowBottom );
4060 }
4061 }
4062
4063 void wxGrid::InitColWidths()
4064 {
4065 m_colWidths.Empty();
4066 m_colRights.Empty();
4067
4068 m_colWidths.Alloc( m_numCols );
4069 m_colRights.Alloc( m_numCols );
4070 int colRight = 0;
4071
4072 m_colWidths.Add( m_defaultColWidth, m_numCols );
4073
4074 for ( int i = 0; i < m_numCols; i++ )
4075 {
4076 colRight += m_defaultColWidth;
4077 m_colRights.Add( colRight );
4078 }
4079 }
4080
4081 int wxGrid::GetColWidth(int col) const
4082 {
4083 return m_colWidths.IsEmpty() ? m_defaultColWidth : m_colWidths[col];
4084 }
4085
4086 int wxGrid::GetColLeft(int col) const
4087 {
4088 return m_colRights.IsEmpty() ? col * m_defaultColWidth
4089 : m_colRights[col] - m_colWidths[col];
4090 }
4091
4092 int wxGrid::GetColRight(int col) const
4093 {
4094 return m_colRights.IsEmpty() ? (col + 1) * m_defaultColWidth
4095 : m_colRights[col];
4096 }
4097
4098 int wxGrid::GetRowHeight(int row) const
4099 {
4100 return m_rowHeights.IsEmpty() ? m_defaultRowHeight : m_rowHeights[row];
4101 }
4102
4103 int wxGrid::GetRowTop(int row) const
4104 {
4105 return m_rowBottoms.IsEmpty() ? row * m_defaultRowHeight
4106 : m_rowBottoms[row] - m_rowHeights[row];
4107 }
4108
4109 int wxGrid::GetRowBottom(int row) const
4110 {
4111 return m_rowBottoms.IsEmpty() ? (row + 1) * m_defaultRowHeight
4112 : m_rowBottoms[row];
4113 }
4114
4115 void wxGrid::CalcDimensions()
4116 {
4117 int cw, ch;
4118 GetClientSize( &cw, &ch );
4119
4120 if ( m_rowLabelWin->IsShown() )
4121 cw -= m_rowLabelWidth;
4122 if ( m_colLabelWin->IsShown() )
4123 ch -= m_colLabelHeight;
4124
4125 // grid total size
4126 int w = m_numCols > 0 ? GetColRight(m_numCols - 1) + m_extraWidth + 1 : 0;
4127 int h = m_numRows > 0 ? GetRowBottom(m_numRows - 1) + m_extraHeight + 1 : 0;
4128
4129 // take into account editor if shown
4130 if( IsCellEditControlShown() )
4131 {
4132 int w2, h2;
4133 int r = m_currentCellCoords.GetRow();
4134 int c = m_currentCellCoords.GetCol();
4135 int x = GetColLeft(c);
4136 int y = GetRowTop(r);
4137
4138 // how big is the editor
4139 wxGridCellAttr* attr = GetCellAttr(r, c);
4140 wxGridCellEditor* editor = attr->GetEditor(this, r, c);
4141 editor->GetControl()->GetSize(&w2, &h2);
4142 w2 += x;
4143 h2 += y;
4144 if( w2 > w ) w = w2;
4145 if( h2 > h ) h = h2;
4146 editor->DecRef();
4147 attr->DecRef();
4148 }
4149
4150 // preserve (more or less) the previous position
4151 int x, y;
4152 GetViewStart( &x, &y );
4153
4154 // maybe we don't need scrollbars at all?
4155 //
4156 // also adjust the position to be valid for the new scroll rangs
4157 if ( w <= cw )
4158 {
4159 w = x = 0;
4160 }
4161 else
4162 {
4163 if ( x >= w )
4164 x = w - 1;
4165 }
4166
4167 if ( h <= ch )
4168 {
4169 h = y = 0;
4170 }
4171 else
4172 {
4173 if ( y >= h )
4174 y = h - 1;
4175 }
4176
4177 // do set scrollbar parameters
4178 SetScrollbars( GRID_SCROLL_LINE_X, GRID_SCROLL_LINE_Y,
4179 GetScrollX(w), GetScrollY(h), x, y,
4180 GetBatchCount() != 0);
4181
4182 // if our OnSize() hadn't been called (it would if we have scrollbars), we
4183 // still must reposition the children
4184 CalcWindowSizes();
4185 }
4186
4187
4188 void wxGrid::CalcWindowSizes()
4189 {
4190 int cw, ch;
4191 GetClientSize( &cw, &ch );
4192
4193 if ( m_cornerLabelWin->IsShown() )
4194 m_cornerLabelWin->SetSize( 0, 0, m_rowLabelWidth, m_colLabelHeight );
4195
4196 if ( m_colLabelWin->IsShown() )
4197 m_colLabelWin->SetSize( m_rowLabelWidth, 0, cw-m_rowLabelWidth, m_colLabelHeight);
4198
4199 if ( m_rowLabelWin->IsShown() )
4200 m_rowLabelWin->SetSize( 0, m_colLabelHeight, m_rowLabelWidth, ch-m_colLabelHeight);
4201
4202 if ( m_gridWin->IsShown() )
4203 m_gridWin->SetSize( m_rowLabelWidth, m_colLabelHeight, cw-m_rowLabelWidth, ch-m_colLabelHeight);
4204 }
4205
4206
4207 // this is called when the grid table sends a message to say that it
4208 // has been redimensioned
4209 //
4210 bool wxGrid::Redimension( wxGridTableMessage& msg )
4211 {
4212 int i;
4213 bool result = FALSE;
4214
4215 // Clear the attribute cache as the attribute might refer to a different
4216 // cell than stored in the cache after adding/removing rows/columns.
4217 ClearAttrCache();
4218 // By the same reasoning, the editor should be dismissed if columns are
4219 // added or removed. And for consistency, it should IMHO always be
4220 // removed, not only if the cell "underneath" it actually changes.
4221 // For now, I intentionally do not save the editor's content as the
4222 // cell it might want to save that stuff to might no longer exist.
4223 HideCellEditControl();
4224 #if 0
4225 // if we were using the default widths/heights so far, we must change them
4226 // now
4227 if ( m_colWidths.IsEmpty() )
4228 {
4229 InitColWidths();
4230 }
4231
4232 if ( m_rowHeights.IsEmpty() )
4233 {
4234 InitRowHeights();
4235 }
4236 #endif
4237
4238 switch ( msg.GetId() )
4239 {
4240 case wxGRIDTABLE_NOTIFY_ROWS_INSERTED:
4241 {
4242 size_t pos = msg.GetCommandInt();
4243 int numRows = msg.GetCommandInt2();
4244
4245 m_numRows += numRows;
4246
4247 if ( !m_rowHeights.IsEmpty() )
4248 {
4249 m_rowHeights.Insert( m_defaultRowHeight, pos, numRows );
4250 m_rowBottoms.Insert( 0, pos, numRows );
4251
4252 int bottom = 0;
4253 if ( pos > 0 ) bottom = m_rowBottoms[pos-1];
4254
4255 for ( i = pos; i < m_numRows; i++ )
4256 {
4257 bottom += m_rowHeights[i];
4258 m_rowBottoms[i] = bottom;
4259 }
4260 }
4261 if ( m_currentCellCoords == wxGridNoCellCoords )
4262 {
4263 // if we have just inserted cols into an empty grid the current
4264 // cell will be undefined...
4265 //
4266 SetCurrentCell( 0, 0 );
4267 }
4268
4269 if ( m_selection )
4270 m_selection->UpdateRows( pos, numRows );
4271 wxGridCellAttrProvider * attrProvider = m_table->GetAttrProvider();
4272 if (attrProvider)
4273 attrProvider->UpdateAttrRows( pos, numRows );
4274
4275 if ( !GetBatchCount() )
4276 {
4277 CalcDimensions();
4278 m_rowLabelWin->Refresh();
4279 }
4280 }
4281 result = TRUE;
4282 break;
4283
4284 case wxGRIDTABLE_NOTIFY_ROWS_APPENDED:
4285 {
4286 int numRows = msg.GetCommandInt();
4287 int oldNumRows = m_numRows;
4288 m_numRows += numRows;
4289
4290 if ( !m_rowHeights.IsEmpty() )
4291 {
4292 m_rowHeights.Add( m_defaultRowHeight, numRows );
4293 m_rowBottoms.Add( 0, numRows );
4294
4295 int bottom = 0;
4296 if ( oldNumRows > 0 ) bottom = m_rowBottoms[oldNumRows-1];
4297
4298 for ( i = oldNumRows; i < m_numRows; i++ )
4299 {
4300 bottom += m_rowHeights[i];
4301 m_rowBottoms[i] = bottom;
4302 }
4303 }
4304 if ( m_currentCellCoords == wxGridNoCellCoords )
4305 {
4306 // if we have just inserted cols into an empty grid the current
4307 // cell will be undefined...
4308 //
4309 SetCurrentCell( 0, 0 );
4310 }
4311 if ( !GetBatchCount() )
4312 {
4313 CalcDimensions();
4314 m_rowLabelWin->Refresh();
4315 }
4316 }
4317 result = TRUE;
4318 break;
4319
4320 case wxGRIDTABLE_NOTIFY_ROWS_DELETED:
4321 {
4322 size_t pos = msg.GetCommandInt();
4323 int numRows = msg.GetCommandInt2();
4324 m_numRows -= numRows;
4325
4326 if ( !m_rowHeights.IsEmpty() )
4327 {
4328 m_rowHeights.RemoveAt( pos, numRows );
4329 m_rowBottoms.RemoveAt( pos, numRows );
4330
4331 int h = 0;
4332 for ( i = 0; i < m_numRows; i++ )
4333 {
4334 h += m_rowHeights[i];
4335 m_rowBottoms[i] = h;
4336 }
4337 }
4338 if ( !m_numRows )
4339 {
4340 m_currentCellCoords = wxGridNoCellCoords;
4341 }
4342 else
4343 {
4344 if ( m_currentCellCoords.GetRow() >= m_numRows )
4345 m_currentCellCoords.Set( 0, 0 );
4346 }
4347
4348 if ( m_selection )
4349 m_selection->UpdateRows( pos, -((int)numRows) );
4350 wxGridCellAttrProvider * attrProvider = m_table->GetAttrProvider();
4351 if (attrProvider) {
4352 attrProvider->UpdateAttrRows( pos, -((int)numRows) );
4353 // ifdef'd out following patch from Paul Gammans
4354 #if 0
4355 // No need to touch column attributes, unless we
4356 // removed _all_ rows, in this case, we remove
4357 // all column attributes.
4358 // I hate to do this here, but the
4359 // needed data is not available inside UpdateAttrRows.
4360 if ( !GetNumberRows() )
4361 attrProvider->UpdateAttrCols( 0, -GetNumberCols() );
4362 #endif
4363 }
4364 if ( !GetBatchCount() )
4365 {
4366 CalcDimensions();
4367 m_rowLabelWin->Refresh();
4368 }
4369 }
4370 result = TRUE;
4371 break;
4372
4373 case wxGRIDTABLE_NOTIFY_COLS_INSERTED:
4374 {
4375 size_t pos = msg.GetCommandInt();
4376 int numCols = msg.GetCommandInt2();
4377 m_numCols += numCols;
4378
4379 if ( !m_colWidths.IsEmpty() )
4380 {
4381 m_colWidths.Insert( m_defaultColWidth, pos, numCols );
4382 m_colRights.Insert( 0, pos, numCols );
4383
4384 int right = 0;
4385 if ( pos > 0 ) right = m_colRights[pos-1];
4386
4387 for ( i = pos; i < m_numCols; i++ )
4388 {
4389 right += m_colWidths[i];
4390 m_colRights[i] = right;
4391 }
4392 }
4393 if ( m_currentCellCoords == wxGridNoCellCoords )
4394 {
4395 // if we have just inserted cols into an empty grid the current
4396 // cell will be undefined...
4397 //
4398 SetCurrentCell( 0, 0 );
4399 }
4400
4401 if ( m_selection )
4402 m_selection->UpdateCols( pos, numCols );
4403 wxGridCellAttrProvider * attrProvider = m_table->GetAttrProvider();
4404 if (attrProvider)
4405 attrProvider->UpdateAttrCols( pos, numCols );
4406 if ( !GetBatchCount() )
4407 {
4408 CalcDimensions();
4409 m_colLabelWin->Refresh();
4410 }
4411
4412 }
4413 result = TRUE;
4414 break;
4415
4416 case wxGRIDTABLE_NOTIFY_COLS_APPENDED:
4417 {
4418 int numCols = msg.GetCommandInt();
4419 int oldNumCols = m_numCols;
4420 m_numCols += numCols;
4421 if ( !m_colWidths.IsEmpty() )
4422 {
4423 m_colWidths.Add( m_defaultColWidth, numCols );
4424 m_colRights.Add( 0, numCols );
4425
4426 int right = 0;
4427 if ( oldNumCols > 0 ) right = m_colRights[oldNumCols-1];
4428
4429 for ( i = oldNumCols; i < m_numCols; i++ )
4430 {
4431 right += m_colWidths[i];
4432 m_colRights[i] = right;
4433 }
4434 }
4435 if ( m_currentCellCoords == wxGridNoCellCoords )
4436 {
4437 // if we have just inserted cols into an empty grid the current
4438 // cell will be undefined...
4439 //
4440 SetCurrentCell( 0, 0 );
4441 }
4442 if ( !GetBatchCount() )
4443 {
4444 CalcDimensions();
4445 m_colLabelWin->Refresh();
4446 }
4447 }
4448 result = TRUE;
4449 break;
4450
4451 case wxGRIDTABLE_NOTIFY_COLS_DELETED:
4452 {
4453 size_t pos = msg.GetCommandInt();
4454 int numCols = msg.GetCommandInt2();
4455 m_numCols -= numCols;
4456
4457 if ( !m_colWidths.IsEmpty() )
4458 {
4459 m_colWidths.RemoveAt( pos, numCols );
4460 m_colRights.RemoveAt( pos, numCols );
4461
4462 int w = 0;
4463 for ( i = 0; i < m_numCols; i++ )
4464 {
4465 w += m_colWidths[i];
4466 m_colRights[i] = w;
4467 }
4468 }
4469 if ( !m_numCols )
4470 {
4471 m_currentCellCoords = wxGridNoCellCoords;
4472 }
4473 else
4474 {
4475 if ( m_currentCellCoords.GetCol() >= m_numCols )
4476 m_currentCellCoords.Set( 0, 0 );
4477 }
4478
4479 if ( m_selection )
4480 m_selection->UpdateCols( pos, -((int)numCols) );
4481 wxGridCellAttrProvider * attrProvider = m_table->GetAttrProvider();
4482 if (attrProvider) {
4483 attrProvider->UpdateAttrCols( pos, -((int)numCols) );
4484 // ifdef'd out following patch from Paul Gammans
4485 #if 0
4486 // No need to touch row attributes, unless we
4487 // removed _all_ columns, in this case, we remove
4488 // all row attributes.
4489 // I hate to do this here, but the
4490 // needed data is not available inside UpdateAttrCols.
4491 if ( !GetNumberCols() )
4492 attrProvider->UpdateAttrRows( 0, -GetNumberRows() );
4493 #endif
4494 }
4495 if ( !GetBatchCount() )
4496 {
4497 CalcDimensions();
4498 m_colLabelWin->Refresh();
4499 }
4500 }
4501 result = TRUE;
4502 break;
4503 }
4504
4505 if (result && !GetBatchCount() )
4506 m_gridWin->Refresh();
4507 return result;
4508 }
4509
4510
4511 wxArrayInt wxGrid::CalcRowLabelsExposed( const wxRegion& reg )
4512 {
4513 wxRegionIterator iter( reg );
4514 wxRect r;
4515
4516 wxArrayInt rowlabels;
4517
4518 int top, bottom;
4519 while ( iter )
4520 {
4521 r = iter.GetRect();
4522
4523 // TODO: remove this when we can...
4524 // There is a bug in wxMotif that gives garbage update
4525 // rectangles if you jump-scroll a long way by clicking the
4526 // scrollbar with middle button. This is a work-around
4527 //
4528 #if defined(__WXMOTIF__)
4529 int cw, ch;
4530 m_gridWin->GetClientSize( &cw, &ch );
4531 if ( r.GetTop() > ch ) r.SetTop( 0 );
4532 r.SetBottom( wxMin( r.GetBottom(), ch ) );
4533 #endif
4534
4535 // logical bounds of update region
4536 //
4537 int dummy;
4538 CalcUnscrolledPosition( 0, r.GetTop(), &dummy, &top );
4539 CalcUnscrolledPosition( 0, r.GetBottom(), &dummy, &bottom );
4540
4541 // find the row labels within these bounds
4542 //
4543 int row;
4544 for ( row = internalYToRow(top); row < m_numRows; row++ )
4545 {
4546 if ( GetRowBottom(row) < top )
4547 continue;
4548
4549 if ( GetRowTop(row) > bottom )
4550 break;
4551
4552 rowlabels.Add( row );
4553 }
4554
4555 iter++ ;
4556 }
4557
4558 return rowlabels;
4559 }
4560
4561
4562 wxArrayInt wxGrid::CalcColLabelsExposed( const wxRegion& reg )
4563 {
4564 wxRegionIterator iter( reg );
4565 wxRect r;
4566
4567 wxArrayInt colLabels;
4568
4569 int left, right;
4570 while ( iter )
4571 {
4572 r = iter.GetRect();
4573
4574 // TODO: remove this when we can...
4575 // There is a bug in wxMotif that gives garbage update
4576 // rectangles if you jump-scroll a long way by clicking the
4577 // scrollbar with middle button. This is a work-around
4578 //
4579 #if defined(__WXMOTIF__)
4580 int cw, ch;
4581 m_gridWin->GetClientSize( &cw, &ch );
4582 if ( r.GetLeft() > cw ) r.SetLeft( 0 );
4583 r.SetRight( wxMin( r.GetRight(), cw ) );
4584 #endif
4585
4586 // logical bounds of update region
4587 //
4588 int dummy;
4589 CalcUnscrolledPosition( r.GetLeft(), 0, &left, &dummy );
4590 CalcUnscrolledPosition( r.GetRight(), 0, &right, &dummy );
4591
4592 // find the cells within these bounds
4593 //
4594 int col;
4595 for ( col = internalXToCol(left); col < m_numCols; col++ )
4596 {
4597 if ( GetColRight(col) < left )
4598 continue;
4599
4600 if ( GetColLeft(col) > right )
4601 break;
4602
4603 colLabels.Add( col );
4604 }
4605
4606 iter++ ;
4607 }
4608 return colLabels;
4609 }
4610
4611
4612 wxGridCellCoordsArray wxGrid::CalcCellsExposed( const wxRegion& reg )
4613 {
4614 wxRegionIterator iter( reg );
4615 wxRect r;
4616
4617 wxGridCellCoordsArray cellsExposed;
4618
4619 int left, top, right, bottom;
4620 while ( iter )
4621 {
4622 r = iter.GetRect();
4623
4624 // TODO: remove this when we can...
4625 // There is a bug in wxMotif that gives garbage update
4626 // rectangles if you jump-scroll a long way by clicking the
4627 // scrollbar with middle button. This is a work-around
4628 //
4629 #if defined(__WXMOTIF__)
4630 int cw, ch;
4631 m_gridWin->GetClientSize( &cw, &ch );
4632 if ( r.GetTop() > ch ) r.SetTop( 0 );
4633 if ( r.GetLeft() > cw ) r.SetLeft( 0 );
4634 r.SetRight( wxMin( r.GetRight(), cw ) );
4635 r.SetBottom( wxMin( r.GetBottom(), ch ) );
4636 #endif
4637
4638 // logical bounds of update region
4639 //
4640 CalcUnscrolledPosition( r.GetLeft(), r.GetTop(), &left, &top );
4641 CalcUnscrolledPosition( r.GetRight(), r.GetBottom(), &right, &bottom );
4642
4643 // find the cells within these bounds
4644 //
4645 int row, col;
4646 for ( row = internalYToRow(top); row < m_numRows; row++ )
4647 {
4648 if ( GetRowBottom(row) <= top )
4649 continue;
4650
4651 if ( GetRowTop(row) > bottom )
4652 break;
4653
4654 for ( col = internalXToCol(left); col < m_numCols; col++ )
4655 {
4656 if ( GetColRight(col) <= left )
4657 continue;
4658
4659 if ( GetColLeft(col) > right )
4660 break;
4661
4662 cellsExposed.Add( wxGridCellCoords( row, col ) );
4663 }
4664 }
4665
4666 iter++;
4667 }
4668
4669 return cellsExposed;
4670 }
4671
4672
4673 void wxGrid::ProcessRowLabelMouseEvent( wxMouseEvent& event )
4674 {
4675 int x, y, row;
4676 wxPoint pos( event.GetPosition() );
4677 CalcUnscrolledPosition( pos.x, pos.y, &x, &y );
4678
4679 if ( event.Dragging() )
4680 {
4681 if (!m_isDragging)
4682 {
4683 m_isDragging = TRUE;
4684 m_rowLabelWin->CaptureMouse();
4685 }
4686
4687 if ( event.LeftIsDown() )
4688 {
4689 switch( m_cursorMode )
4690 {
4691 case WXGRID_CURSOR_RESIZE_ROW:
4692 {
4693 int cw, ch, left, dummy;
4694 m_gridWin->GetClientSize( &cw, &ch );
4695 CalcUnscrolledPosition( 0, 0, &left, &dummy );
4696
4697 wxClientDC dc( m_gridWin );
4698 PrepareDC( dc );
4699 y = wxMax( y,
4700 GetRowTop(m_dragRowOrCol) +
4701 GetRowMinimalHeight(m_dragRowOrCol) );
4702 dc.SetLogicalFunction(wxINVERT);
4703 if ( m_dragLastPos >= 0 )
4704 {
4705 dc.DrawLine( left, m_dragLastPos, left+cw, m_dragLastPos );
4706 }
4707 dc.DrawLine( left, y, left+cw, y );
4708 m_dragLastPos = y;
4709 }
4710 break;
4711
4712 case WXGRID_CURSOR_SELECT_ROW:
4713 if ( (row = YToRow( y )) >= 0 )
4714 {
4715 if ( m_selection )
4716 {
4717 m_selection->SelectRow( row,
4718 event.ControlDown(),
4719 event.ShiftDown(),
4720 event.AltDown(),
4721 event.MetaDown() );
4722 }
4723 }
4724
4725 // default label to suppress warnings about "enumeration value
4726 // 'xxx' not handled in switch
4727 default:
4728 break;
4729 }
4730 }
4731 return;
4732 }
4733
4734 if ( m_isDragging && (event.Entering() || event.Leaving()) )
4735 return;
4736
4737 if (m_isDragging)
4738 {
4739 if (m_rowLabelWin->HasCapture()) m_rowLabelWin->ReleaseMouse();
4740 m_isDragging = FALSE;
4741 }
4742
4743 // ------------ Entering or leaving the window
4744 //
4745 if ( event.Entering() || event.Leaving() )
4746 {
4747 ChangeCursorMode(WXGRID_CURSOR_SELECT_CELL, m_rowLabelWin);
4748 }
4749
4750
4751 // ------------ Left button pressed
4752 //
4753 else if ( event.LeftDown() )
4754 {
4755 // don't send a label click event for a hit on the
4756 // edge of the row label - this is probably the user
4757 // wanting to resize the row
4758 //
4759 if ( YToEdgeOfRow(y) < 0 )
4760 {
4761 row = YToRow(y);
4762 if ( row >= 0 &&
4763 !SendEvent( wxEVT_GRID_LABEL_LEFT_CLICK, row, -1, event ) )
4764 {
4765 if ( !event.ShiftDown() && !event.ControlDown() )
4766 ClearSelection();
4767 if ( m_selection )
4768 {
4769 if ( event.ShiftDown() )
4770 {
4771 m_selection->SelectBlock( m_currentCellCoords.GetRow(),
4772 0,
4773 row,
4774 GetNumberCols() - 1,
4775 event.ControlDown(),
4776 event.ShiftDown(),
4777 event.AltDown(),
4778 event.MetaDown() );
4779 }
4780 else
4781 {
4782 m_selection->SelectRow( row,
4783 event.ControlDown(),
4784 event.ShiftDown(),
4785 event.AltDown(),
4786 event.MetaDown() );
4787 }
4788 }
4789
4790 ChangeCursorMode(WXGRID_CURSOR_SELECT_ROW, m_rowLabelWin);
4791 }
4792 }
4793 else
4794 {
4795 // starting to drag-resize a row
4796 //
4797 if ( CanDragRowSize() )
4798 ChangeCursorMode(WXGRID_CURSOR_RESIZE_ROW, m_rowLabelWin);
4799 }
4800 }
4801
4802
4803 // ------------ Left double click
4804 //
4805 else if (event.LeftDClick() )
4806 {
4807 int row = YToEdgeOfRow(y);
4808 if ( row < 0 )
4809 {
4810 row = YToRow(y);
4811 if ( row >=0 &&
4812 !SendEvent( wxEVT_GRID_LABEL_LEFT_DCLICK, row, -1, event ) )
4813 {
4814 // no default action at the moment
4815 }
4816 }
4817 else
4818 {
4819 // adjust row height depending on label text
4820 AutoSizeRowLabelSize( row );
4821
4822 ChangeCursorMode(WXGRID_CURSOR_SELECT_CELL, m_colLabelWin);
4823 m_dragLastPos = -1;
4824 }
4825 }
4826
4827
4828 // ------------ Left button released
4829 //
4830 else if ( event.LeftUp() )
4831 {
4832 if ( m_cursorMode == WXGRID_CURSOR_RESIZE_ROW )
4833 {
4834 DoEndDragResizeRow();
4835
4836 // Note: we are ending the event *after* doing
4837 // default processing in this case
4838 //
4839 SendEvent( wxEVT_GRID_ROW_SIZE, m_dragRowOrCol, -1, event );
4840 }
4841
4842 ChangeCursorMode(WXGRID_CURSOR_SELECT_CELL, m_rowLabelWin);
4843 m_dragLastPos = -1;
4844 }
4845
4846
4847 // ------------ Right button down
4848 //
4849 else if ( event.RightDown() )
4850 {
4851 row = YToRow(y);
4852 if ( row >=0 &&
4853 !SendEvent( wxEVT_GRID_LABEL_RIGHT_CLICK, row, -1, event ) )
4854 {
4855 // no default action at the moment
4856 }
4857 }
4858
4859
4860 // ------------ Right double click
4861 //
4862 else if ( event.RightDClick() )
4863 {
4864 row = YToRow(y);
4865 if ( row >= 0 &&
4866 !SendEvent( wxEVT_GRID_LABEL_RIGHT_DCLICK, row, -1, event ) )
4867 {
4868 // no default action at the moment
4869 }
4870 }
4871
4872
4873 // ------------ No buttons down and mouse moving
4874 //
4875 else if ( event.Moving() )
4876 {
4877 m_dragRowOrCol = YToEdgeOfRow( y );
4878 if ( m_dragRowOrCol >= 0 )
4879 {
4880 if ( m_cursorMode == WXGRID_CURSOR_SELECT_CELL )
4881 {
4882 // don't capture the mouse yet
4883 if ( CanDragRowSize() )
4884 ChangeCursorMode(WXGRID_CURSOR_RESIZE_ROW, m_rowLabelWin, FALSE);
4885 }
4886 }
4887 else if ( m_cursorMode != WXGRID_CURSOR_SELECT_CELL )
4888 {
4889 ChangeCursorMode(WXGRID_CURSOR_SELECT_CELL, m_rowLabelWin, FALSE);
4890 }
4891 }
4892 }
4893
4894
4895 void wxGrid::ProcessColLabelMouseEvent( wxMouseEvent& event )
4896 {
4897 int x, y, col;
4898 wxPoint pos( event.GetPosition() );
4899 CalcUnscrolledPosition( pos.x, pos.y, &x, &y );
4900
4901 if ( event.Dragging() )
4902 {
4903 if (!m_isDragging)
4904 {
4905 m_isDragging = TRUE;
4906 m_colLabelWin->CaptureMouse();
4907 }
4908
4909 if ( event.LeftIsDown() )
4910 {
4911 switch( m_cursorMode )
4912 {
4913 case WXGRID_CURSOR_RESIZE_COL:
4914 {
4915 int cw, ch, dummy, top;
4916 m_gridWin->GetClientSize( &cw, &ch );
4917 CalcUnscrolledPosition( 0, 0, &dummy, &top );
4918
4919 wxClientDC dc( m_gridWin );
4920 PrepareDC( dc );
4921
4922 x = wxMax( x, GetColLeft(m_dragRowOrCol) +
4923 GetColMinimalWidth(m_dragRowOrCol));
4924 dc.SetLogicalFunction(wxINVERT);
4925 if ( m_dragLastPos >= 0 )
4926 {
4927 dc.DrawLine( m_dragLastPos, top, m_dragLastPos, top+ch );
4928 }
4929 dc.DrawLine( x, top, x, top+ch );
4930 m_dragLastPos = x;
4931 }
4932 break;
4933
4934 case WXGRID_CURSOR_SELECT_COL:
4935 if ( (col = XToCol( x )) >= 0 )
4936 {
4937 if ( m_selection )
4938 {
4939 m_selection->SelectCol( col,
4940 event.ControlDown(),
4941 event.ShiftDown(),
4942 event.AltDown(),
4943 event.MetaDown() );
4944 }
4945 }
4946
4947 // default label to suppress warnings about "enumeration value
4948 // 'xxx' not handled in switch
4949 default:
4950 break;
4951 }
4952 }
4953 return;
4954 }
4955
4956 if ( m_isDragging && (event.Entering() || event.Leaving()) )
4957 return;
4958
4959 if (m_isDragging)
4960 {
4961 if (m_colLabelWin->HasCapture()) m_colLabelWin->ReleaseMouse();
4962 m_isDragging = FALSE;
4963 }
4964
4965 // ------------ Entering or leaving the window
4966 //
4967 if ( event.Entering() || event.Leaving() )
4968 {
4969 ChangeCursorMode(WXGRID_CURSOR_SELECT_CELL, m_colLabelWin);
4970 }
4971
4972
4973 // ------------ Left button pressed
4974 //
4975 else if ( event.LeftDown() )
4976 {
4977 // don't send a label click event for a hit on the
4978 // edge of the col label - this is probably the user
4979 // wanting to resize the col
4980 //
4981 if ( XToEdgeOfCol(x) < 0 )
4982 {
4983 col = XToCol(x);
4984 if ( col >= 0 &&
4985 !SendEvent( wxEVT_GRID_LABEL_LEFT_CLICK, -1, col, event ) )
4986 {
4987 if ( !event.ShiftDown() && !event.ControlDown() )
4988 ClearSelection();
4989 if ( m_selection )
4990 {
4991 if ( event.ShiftDown() )
4992 {
4993 m_selection->SelectBlock( 0,
4994 m_currentCellCoords.GetCol(),
4995 GetNumberRows() - 1, col,
4996 event.ControlDown(),
4997 event.ShiftDown(),
4998 event.AltDown(),
4999 event.MetaDown() );
5000 }
5001 else
5002 {
5003 m_selection->SelectCol( col,
5004 event.ControlDown(),
5005 event.ShiftDown(),
5006 event.AltDown(),
5007 event.MetaDown() );
5008 }
5009 }
5010
5011 ChangeCursorMode(WXGRID_CURSOR_SELECT_COL, m_colLabelWin);
5012 }
5013 }
5014 else
5015 {
5016 // starting to drag-resize a col
5017 //
5018 if ( CanDragColSize() )
5019 ChangeCursorMode(WXGRID_CURSOR_RESIZE_COL, m_colLabelWin);
5020 }
5021 }
5022
5023
5024 // ------------ Left double click
5025 //
5026 if ( event.LeftDClick() )
5027 {
5028 int col = XToEdgeOfCol(x);
5029 if ( col < 0 )
5030 {
5031 col = XToCol(x);
5032 if ( col >= 0 &&
5033 ! SendEvent( wxEVT_GRID_LABEL_LEFT_DCLICK, -1, col, event ) )
5034 {
5035 // no default action at the moment
5036 }
5037 }
5038 else
5039 {
5040 // adjust column width depending on label text
5041 AutoSizeColLabelSize( col );
5042
5043 ChangeCursorMode(WXGRID_CURSOR_SELECT_CELL, m_colLabelWin);
5044 m_dragLastPos = -1;
5045 }
5046 }
5047
5048
5049 // ------------ Left button released
5050 //
5051 else if ( event.LeftUp() )
5052 {
5053 if ( m_cursorMode == WXGRID_CURSOR_RESIZE_COL )
5054 {
5055 DoEndDragResizeCol();
5056
5057 // Note: we are ending the event *after* doing
5058 // default processing in this case
5059 //
5060 SendEvent( wxEVT_GRID_COL_SIZE, -1, m_dragRowOrCol, event );
5061 }
5062
5063 ChangeCursorMode(WXGRID_CURSOR_SELECT_CELL, m_colLabelWin);
5064 m_dragLastPos = -1;
5065 }
5066
5067
5068 // ------------ Right button down
5069 //
5070 else if ( event.RightDown() )
5071 {
5072 col = XToCol(x);
5073 if ( col >= 0 &&
5074 !SendEvent( wxEVT_GRID_LABEL_RIGHT_CLICK, -1, col, event ) )
5075 {
5076 // no default action at the moment
5077 }
5078 }
5079
5080
5081 // ------------ Right double click
5082 //
5083 else if ( event.RightDClick() )
5084 {
5085 col = XToCol(x);
5086 if ( col >= 0 &&
5087 !SendEvent( wxEVT_GRID_LABEL_RIGHT_DCLICK, -1, col, event ) )
5088 {
5089 // no default action at the moment
5090 }
5091 }
5092
5093
5094 // ------------ No buttons down and mouse moving
5095 //
5096 else if ( event.Moving() )
5097 {
5098 m_dragRowOrCol = XToEdgeOfCol( x );
5099 if ( m_dragRowOrCol >= 0 )
5100 {
5101 if ( m_cursorMode == WXGRID_CURSOR_SELECT_CELL )
5102 {
5103 // don't capture the cursor yet
5104 if ( CanDragColSize() )
5105 ChangeCursorMode(WXGRID_CURSOR_RESIZE_COL, m_colLabelWin, FALSE);
5106 }
5107 }
5108 else if ( m_cursorMode != WXGRID_CURSOR_SELECT_CELL )
5109 {
5110 ChangeCursorMode(WXGRID_CURSOR_SELECT_CELL, m_colLabelWin, FALSE);
5111 }
5112 }
5113 }
5114
5115
5116 void wxGrid::ProcessCornerLabelMouseEvent( wxMouseEvent& event )
5117 {
5118 if ( event.LeftDown() )
5119 {
5120 // indicate corner label by having both row and
5121 // col args == -1
5122 //
5123 if ( !SendEvent( wxEVT_GRID_LABEL_LEFT_CLICK, -1, -1, event ) )
5124 {
5125 SelectAll();
5126 }
5127 }
5128
5129 else if ( event.LeftDClick() )
5130 {
5131 SendEvent( wxEVT_GRID_LABEL_LEFT_DCLICK, -1, -1, event );
5132 }
5133
5134 else if ( event.RightDown() )
5135 {
5136 if ( !SendEvent( wxEVT_GRID_LABEL_RIGHT_CLICK, -1, -1, event ) )
5137 {
5138 // no default action at the moment
5139 }
5140 }
5141
5142 else if ( event.RightDClick() )
5143 {
5144 if ( !SendEvent( wxEVT_GRID_LABEL_RIGHT_DCLICK, -1, -1, event ) )
5145 {
5146 // no default action at the moment
5147 }
5148 }
5149 }
5150
5151 void wxGrid::ChangeCursorMode(CursorMode mode,
5152 wxWindow *win,
5153 bool captureMouse)
5154 {
5155 #ifdef __WXDEBUG__
5156 static const wxChar *cursorModes[] =
5157 {
5158 _T("SELECT_CELL"),
5159 _T("RESIZE_ROW"),
5160 _T("RESIZE_COL"),
5161 _T("SELECT_ROW"),
5162 _T("SELECT_COL")
5163 };
5164
5165 wxLogTrace(_T("grid"),
5166 _T("wxGrid cursor mode (mouse capture for %s): %s -> %s"),
5167 win == m_colLabelWin ? _T("colLabelWin")
5168 : win ? _T("rowLabelWin")
5169 : _T("gridWin"),
5170 cursorModes[m_cursorMode], cursorModes[mode]);
5171 #endif // __WXDEBUG__
5172
5173 if ( mode == m_cursorMode &&
5174 win == m_winCapture &&
5175 captureMouse == (m_winCapture != NULL))
5176 return;
5177
5178 if ( !win )
5179 {
5180 // by default use the grid itself
5181 win = m_gridWin;
5182 }
5183
5184 if ( m_winCapture )
5185 {
5186 if (m_winCapture->HasCapture()) m_winCapture->ReleaseMouse();
5187 m_winCapture = (wxWindow *)NULL;
5188 }
5189
5190 m_cursorMode = mode;
5191
5192 switch ( m_cursorMode )
5193 {
5194 case WXGRID_CURSOR_RESIZE_ROW:
5195 win->SetCursor( m_rowResizeCursor );
5196 break;
5197
5198 case WXGRID_CURSOR_RESIZE_COL:
5199 win->SetCursor( m_colResizeCursor );
5200 break;
5201
5202 default:
5203 win->SetCursor( *wxSTANDARD_CURSOR );
5204 }
5205
5206 // we need to capture mouse when resizing
5207 bool resize = m_cursorMode == WXGRID_CURSOR_RESIZE_ROW ||
5208 m_cursorMode == WXGRID_CURSOR_RESIZE_COL;
5209
5210 if ( captureMouse && resize )
5211 {
5212 win->CaptureMouse();
5213 m_winCapture = win;
5214 }
5215 }
5216
5217 void wxGrid::ProcessGridCellMouseEvent( wxMouseEvent& event )
5218 {
5219 int x, y;
5220 wxPoint pos( event.GetPosition() );
5221 CalcUnscrolledPosition( pos.x, pos.y, &x, &y );
5222
5223 wxGridCellCoords coords;
5224 XYToCell( x, y, coords );
5225
5226 int cell_rows, cell_cols;
5227 GetCellSize( coords.GetRow(), coords.GetCol(), &cell_rows, &cell_cols );
5228 if ((cell_rows < 0) || (cell_cols < 0))
5229 {
5230 coords.SetRow(coords.GetRow() + cell_rows);
5231 coords.SetCol(coords.GetCol() + cell_cols);
5232 }
5233
5234 if ( event.Dragging() )
5235 {
5236 //wxLogDebug("pos(%d, %d) coords(%d, %d)", pos.x, pos.y, coords.GetRow(), coords.GetCol());
5237
5238 // Don't start doing anything until the mouse has been drug at
5239 // least 3 pixels in any direction...
5240 if (! m_isDragging)
5241 {
5242 if (m_startDragPos == wxDefaultPosition)
5243 {
5244 m_startDragPos = pos;
5245 return;
5246 }
5247 if (abs(m_startDragPos.x - pos.x) < 4 && abs(m_startDragPos.y - pos.y) < 4)
5248 return;
5249 }
5250
5251 m_isDragging = TRUE;
5252 if ( m_cursorMode == WXGRID_CURSOR_SELECT_CELL )
5253 {
5254 // Hide the edit control, so it
5255 // won't interfer with drag-shrinking.
5256 if ( IsCellEditControlShown() )
5257 {
5258 HideCellEditControl();
5259 SaveEditControlValue();
5260 }
5261
5262 // Have we captured the mouse yet?
5263 if (! m_winCapture)
5264 {
5265 m_winCapture = m_gridWin;
5266 m_winCapture->CaptureMouse();
5267 }
5268
5269 if ( coords != wxGridNoCellCoords )
5270 {
5271 if ( event.ControlDown() )
5272 {
5273 if ( m_selectingKeyboard == wxGridNoCellCoords)
5274 m_selectingKeyboard = coords;
5275 HighlightBlock ( m_selectingKeyboard, coords );
5276 }
5277 else
5278 {
5279 if ( !IsSelection() )
5280 {
5281 HighlightBlock( coords, coords );
5282 }
5283 else
5284 {
5285 HighlightBlock( m_currentCellCoords, coords );
5286 }
5287 }
5288
5289 if (! IsVisible(coords))
5290 {
5291 MakeCellVisible(coords);
5292 // TODO: need to introduce a delay or something here. The
5293 // scrolling is way to fast, at least on MSW - also on GTK.
5294 }
5295 }
5296 }
5297 else if ( m_cursorMode == WXGRID_CURSOR_RESIZE_ROW )
5298 {
5299 int cw, ch, left, dummy;
5300 m_gridWin->GetClientSize( &cw, &ch );
5301 CalcUnscrolledPosition( 0, 0, &left, &dummy );
5302
5303 wxClientDC dc( m_gridWin );
5304 PrepareDC( dc );
5305 y = wxMax( y, GetRowTop(m_dragRowOrCol) +
5306 GetRowMinimalHeight(m_dragRowOrCol) );
5307 dc.SetLogicalFunction(wxINVERT);
5308 if ( m_dragLastPos >= 0 )
5309 {
5310 dc.DrawLine( left, m_dragLastPos, left+cw, m_dragLastPos );
5311 }
5312 dc.DrawLine( left, y, left+cw, y );
5313 m_dragLastPos = y;
5314 }
5315 else if ( m_cursorMode == WXGRID_CURSOR_RESIZE_COL )
5316 {
5317 int cw, ch, dummy, top;
5318 m_gridWin->GetClientSize( &cw, &ch );
5319 CalcUnscrolledPosition( 0, 0, &dummy, &top );
5320
5321 wxClientDC dc( m_gridWin );
5322 PrepareDC( dc );
5323 x = wxMax( x, GetColLeft(m_dragRowOrCol) +
5324 GetColMinimalWidth(m_dragRowOrCol) );
5325 dc.SetLogicalFunction(wxINVERT);
5326 if ( m_dragLastPos >= 0 )
5327 {
5328 dc.DrawLine( m_dragLastPos, top, m_dragLastPos, top+ch );
5329 }
5330 dc.DrawLine( x, top, x, top+ch );
5331 m_dragLastPos = x;
5332 }
5333
5334 return;
5335 }
5336
5337 m_isDragging = FALSE;
5338 m_startDragPos = wxDefaultPosition;
5339
5340 // VZ: if we do this, the mode is reset to WXGRID_CURSOR_SELECT_CELL
5341 // immediately after it becomes WXGRID_CURSOR_RESIZE_ROW/COL under
5342 // wxGTK
5343 #if 0
5344 if ( event.Entering() || event.Leaving() )
5345 {
5346 ChangeCursorMode(WXGRID_CURSOR_SELECT_CELL);
5347 m_gridWin->SetCursor( *wxSTANDARD_CURSOR );
5348 }
5349 else
5350 #endif // 0
5351
5352 // ------------ Left button pressed
5353 //
5354 if ( event.LeftDown() && coords != wxGridNoCellCoords )
5355 {
5356 if ( !SendEvent( wxEVT_GRID_CELL_LEFT_CLICK,
5357 coords.GetRow(),
5358 coords.GetCol(),
5359 event ) )
5360 {
5361 if ( !event.ControlDown() )
5362 ClearSelection();
5363 if ( event.ShiftDown() )
5364 {
5365 if ( m_selection )
5366 {
5367 m_selection->SelectBlock( m_currentCellCoords.GetRow(),
5368 m_currentCellCoords.GetCol(),
5369 coords.GetRow(),
5370 coords.GetCol(),
5371 event.ControlDown(),
5372 event.ShiftDown(),
5373 event.AltDown(),
5374 event.MetaDown() );
5375 }
5376 }
5377 else if ( XToEdgeOfCol(x) < 0 &&
5378 YToEdgeOfRow(y) < 0 )
5379 {
5380 DisableCellEditControl();
5381 MakeCellVisible( coords );
5382
5383 if ( event.ControlDown() )
5384 {
5385 if ( m_selection )
5386 {
5387 m_selection->ToggleCellSelection( coords.GetRow(),
5388 coords.GetCol(),
5389 event.ControlDown(),
5390 event.ShiftDown(),
5391 event.AltDown(),
5392 event.MetaDown() );
5393 }
5394 m_selectingTopLeft = wxGridNoCellCoords;
5395 m_selectingBottomRight = wxGridNoCellCoords;
5396 m_selectingKeyboard = coords;
5397 }
5398 else
5399 {
5400 m_waitForSlowClick = m_currentCellCoords == coords && coords != wxGridNoCellCoords;
5401 SetCurrentCell( coords );
5402 if ( m_selection )
5403 {
5404 if ( m_selection->GetSelectionMode() !=
5405 wxGrid::wxGridSelectCells )
5406 {
5407 HighlightBlock( coords, coords );
5408 }
5409 }
5410 }
5411 }
5412 }
5413 }
5414
5415
5416 // ------------ Left double click
5417 //
5418 else if ( event.LeftDClick() && coords != wxGridNoCellCoords )
5419 {
5420 DisableCellEditControl();
5421
5422 if ( XToEdgeOfCol(x) < 0 && YToEdgeOfRow(y) < 0 )
5423 {
5424 SendEvent( wxEVT_GRID_CELL_LEFT_DCLICK,
5425 coords.GetRow(),
5426 coords.GetCol(),
5427 event );
5428 }
5429 }
5430
5431
5432 // ------------ Left button released
5433 //
5434 else if ( event.LeftUp() )
5435 {
5436 if ( m_cursorMode == WXGRID_CURSOR_SELECT_CELL )
5437 {
5438 if (m_winCapture)
5439 {
5440 if (m_winCapture->HasCapture()) m_winCapture->ReleaseMouse();
5441 m_winCapture = NULL;
5442 }
5443
5444 if ( coords == m_currentCellCoords && m_waitForSlowClick && CanEnableCellControl())
5445 {
5446 ClearSelection();
5447 EnableCellEditControl();
5448
5449 wxGridCellAttr* attr = GetCellAttr(coords);
5450 wxGridCellEditor *editor = attr->GetEditor(this, coords.GetRow(), coords.GetCol());
5451 editor->StartingClick();
5452 editor->DecRef();
5453 attr->DecRef();
5454
5455 m_waitForSlowClick = FALSE;
5456 }
5457 else if ( m_selectingTopLeft != wxGridNoCellCoords &&
5458 m_selectingBottomRight != wxGridNoCellCoords )
5459 {
5460 if ( m_selection )
5461 {
5462 m_selection->SelectBlock( m_selectingTopLeft.GetRow(),
5463 m_selectingTopLeft.GetCol(),
5464 m_selectingBottomRight.GetRow(),
5465 m_selectingBottomRight.GetCol(),
5466 event.ControlDown(),
5467 event.ShiftDown(),
5468 event.AltDown(),
5469 event.MetaDown() );
5470 }
5471
5472 m_selectingTopLeft = wxGridNoCellCoords;
5473 m_selectingBottomRight = wxGridNoCellCoords;
5474
5475 // Show the edit control, if it has been hidden for
5476 // drag-shrinking.
5477 ShowCellEditControl();
5478 }
5479 }
5480 else if ( m_cursorMode == WXGRID_CURSOR_RESIZE_ROW )
5481 {
5482 ChangeCursorMode(WXGRID_CURSOR_SELECT_CELL);
5483 DoEndDragResizeRow();
5484
5485 // Note: we are ending the event *after* doing
5486 // default processing in this case
5487 //
5488 SendEvent( wxEVT_GRID_ROW_SIZE, m_dragRowOrCol, -1, event );
5489 }
5490 else if ( m_cursorMode == WXGRID_CURSOR_RESIZE_COL )
5491 {
5492 ChangeCursorMode(WXGRID_CURSOR_SELECT_CELL);
5493 DoEndDragResizeCol();
5494
5495 // Note: we are ending the event *after* doing
5496 // default processing in this case
5497 //
5498 SendEvent( wxEVT_GRID_COL_SIZE, -1, m_dragRowOrCol, event );
5499 }
5500
5501 m_dragLastPos = -1;
5502 }
5503
5504
5505 // ------------ Right button down
5506 //
5507 else if ( event.RightDown() && coords != wxGridNoCellCoords )
5508 {
5509 DisableCellEditControl();
5510 if ( !SendEvent( wxEVT_GRID_CELL_RIGHT_CLICK,
5511 coords.GetRow(),
5512 coords.GetCol(),
5513 event ) )
5514 {
5515 // no default action at the moment
5516 }
5517 }
5518
5519
5520 // ------------ Right double click
5521 //
5522 else if ( event.RightDClick() && coords != wxGridNoCellCoords )
5523 {
5524 DisableCellEditControl();
5525 if ( !SendEvent( wxEVT_GRID_CELL_RIGHT_DCLICK,
5526 coords.GetRow(),
5527 coords.GetCol(),
5528 event ) )
5529 {
5530 // no default action at the moment
5531 }
5532 }
5533
5534 // ------------ Moving and no button action
5535 //
5536 else if ( event.Moving() && !event.IsButton() )
5537 {
5538 if( coords.GetRow() < 0 || coords.GetCol() < 0 )
5539 {
5540 // out of grid cell area
5541 ChangeCursorMode(WXGRID_CURSOR_SELECT_CELL);
5542 return;
5543 }
5544
5545 int dragRow = YToEdgeOfRow( y );
5546 int dragCol = XToEdgeOfCol( x );
5547
5548 // Dragging on the corner of a cell to resize in both
5549 // directions is not implemented yet...
5550 //
5551 if ( dragRow >= 0 && dragCol >= 0 )
5552 {
5553 ChangeCursorMode(WXGRID_CURSOR_SELECT_CELL);
5554 return;
5555 }
5556
5557 if ( dragRow >= 0 )
5558 {
5559 m_dragRowOrCol = dragRow;
5560
5561 if ( m_cursorMode == WXGRID_CURSOR_SELECT_CELL )
5562 {
5563 if ( CanDragRowSize() && CanDragGridSize() )
5564 ChangeCursorMode(WXGRID_CURSOR_RESIZE_ROW);
5565 }
5566
5567 if ( dragCol >= 0 )
5568 {
5569 m_dragRowOrCol = dragCol;
5570 }
5571
5572 return;
5573 }
5574
5575 if ( dragCol >= 0 )
5576 {
5577 m_dragRowOrCol = dragCol;
5578
5579 if ( m_cursorMode == WXGRID_CURSOR_SELECT_CELL )
5580 {
5581 if ( CanDragColSize() && CanDragGridSize() )
5582 ChangeCursorMode(WXGRID_CURSOR_RESIZE_COL);
5583 }
5584
5585 return;
5586 }
5587
5588 // Neither on a row or col edge
5589 //
5590 if ( m_cursorMode != WXGRID_CURSOR_SELECT_CELL )
5591 {
5592 ChangeCursorMode(WXGRID_CURSOR_SELECT_CELL);
5593 }
5594 }
5595 }
5596
5597
5598 void wxGrid::DoEndDragResizeRow()
5599 {
5600 if ( m_dragLastPos >= 0 )
5601 {
5602 // erase the last line and resize the row
5603 //
5604 int cw, ch, left, dummy;
5605 m_gridWin->GetClientSize( &cw, &ch );
5606 CalcUnscrolledPosition( 0, 0, &left, &dummy );
5607
5608 wxClientDC dc( m_gridWin );
5609 PrepareDC( dc );
5610 dc.SetLogicalFunction( wxINVERT );
5611 dc.DrawLine( left, m_dragLastPos, left+cw, m_dragLastPos );
5612 HideCellEditControl();
5613 SaveEditControlValue();
5614
5615 int rowTop = GetRowTop(m_dragRowOrCol);
5616 SetRowSize( m_dragRowOrCol,
5617 wxMax( m_dragLastPos - rowTop, m_minAcceptableRowHeight ) );
5618
5619 if ( !GetBatchCount() )
5620 {
5621 // Only needed to get the correct rect.y:
5622 wxRect rect ( CellToRect( m_dragRowOrCol, 0 ) );
5623 rect.x = 0;
5624 CalcScrolledPosition(0, rect.y, &dummy, &rect.y);
5625 rect.width = m_rowLabelWidth;
5626 rect.height = ch - rect.y;
5627 m_rowLabelWin->Refresh( TRUE, &rect );
5628 rect.width = cw;
5629 // if there is a multicell block, paint all of it
5630 if (m_table)
5631 {
5632 int i, cell_rows, cell_cols, subtract_rows = 0;
5633 int leftCol = XToCol(left);
5634 int rightCol = internalXToCol(left+cw);
5635 if (leftCol >= 0)
5636 {
5637 for (i=leftCol; i<rightCol; i++)
5638 {
5639 GetCellSize(m_dragRowOrCol, i, &cell_rows, &cell_cols);
5640 if (cell_rows < subtract_rows)
5641 subtract_rows = cell_rows;
5642 }
5643 rect.y = GetRowTop(m_dragRowOrCol + subtract_rows);
5644 CalcScrolledPosition(0, rect.y, &dummy, &rect.y);
5645 rect.height = ch - rect.y;
5646 }
5647 }
5648 m_gridWin->Refresh( FALSE, &rect );
5649 }
5650
5651 ShowCellEditControl();
5652 }
5653 }
5654
5655
5656 void wxGrid::DoEndDragResizeCol()
5657 {
5658 if ( m_dragLastPos >= 0 )
5659 {
5660 // erase the last line and resize the col
5661 //
5662 int cw, ch, dummy, top;
5663 m_gridWin->GetClientSize( &cw, &ch );
5664 CalcUnscrolledPosition( 0, 0, &dummy, &top );
5665
5666 wxClientDC dc( m_gridWin );
5667 PrepareDC( dc );
5668 dc.SetLogicalFunction( wxINVERT );
5669 dc.DrawLine( m_dragLastPos, top, m_dragLastPos, top+ch );
5670 HideCellEditControl();
5671 SaveEditControlValue();
5672
5673 int colLeft = GetColLeft(m_dragRowOrCol);
5674 SetColSize( m_dragRowOrCol,
5675 wxMax( m_dragLastPos - colLeft,
5676 GetColMinimalWidth(m_dragRowOrCol) ) );
5677
5678 if ( !GetBatchCount() )
5679 {
5680 // Only needed to get the correct rect.x:
5681 wxRect rect ( CellToRect( 0, m_dragRowOrCol ) );
5682 rect.y = 0;
5683 CalcScrolledPosition(rect.x, 0, &rect.x, &dummy);
5684 rect.width = cw - rect.x;
5685 rect.height = m_colLabelHeight;
5686 m_colLabelWin->Refresh( TRUE, &rect );
5687 rect.height = ch;
5688 // if there is a multicell block, paint all of it
5689 if (m_table)
5690 {
5691 int i, cell_rows, cell_cols, subtract_cols = 0;
5692 int topRow = YToRow(top);
5693 int bottomRow = internalYToRow(top+cw);
5694 if (topRow >= 0)
5695 {
5696 for (i=topRow; i<bottomRow; i++)
5697 {
5698 GetCellSize(i, m_dragRowOrCol, &cell_rows, &cell_cols);
5699 if (cell_cols < subtract_cols)
5700 subtract_cols = cell_cols;
5701 }
5702 rect.x = GetColLeft(m_dragRowOrCol + subtract_cols);
5703 CalcScrolledPosition(rect.x, 0, &rect.x, &dummy);
5704 rect.width = cw - rect.x;
5705 }
5706 }
5707 m_gridWin->Refresh( FALSE, &rect );
5708 }
5709
5710 ShowCellEditControl();
5711 }
5712 }
5713
5714
5715
5716 //
5717 // ------ interaction with data model
5718 //
5719 bool wxGrid::ProcessTableMessage( wxGridTableMessage& msg )
5720 {
5721 switch ( msg.GetId() )
5722 {
5723 case wxGRIDTABLE_REQUEST_VIEW_GET_VALUES:
5724 return GetModelValues();
5725
5726 case wxGRIDTABLE_REQUEST_VIEW_SEND_VALUES:
5727 return SetModelValues();
5728
5729 case wxGRIDTABLE_NOTIFY_ROWS_INSERTED:
5730 case wxGRIDTABLE_NOTIFY_ROWS_APPENDED:
5731 case wxGRIDTABLE_NOTIFY_ROWS_DELETED:
5732 case wxGRIDTABLE_NOTIFY_COLS_INSERTED:
5733 case wxGRIDTABLE_NOTIFY_COLS_APPENDED:
5734 case wxGRIDTABLE_NOTIFY_COLS_DELETED:
5735 return Redimension( msg );
5736
5737 default:
5738 return FALSE;
5739 }
5740 }
5741
5742
5743
5744 // The behaviour of this function depends on the grid table class
5745 // Clear() function. For the default wxGridStringTable class the
5746 // behavious is to replace all cell contents with wxEmptyString but
5747 // not to change the number of rows or cols.
5748 //
5749 void wxGrid::ClearGrid()
5750 {
5751 if ( m_table )
5752 {
5753 if (IsCellEditControlEnabled())
5754 DisableCellEditControl();
5755
5756 m_table->Clear();
5757 if ( !GetBatchCount() ) m_gridWin->Refresh();
5758 }
5759 }
5760
5761
5762 bool wxGrid::InsertRows( int pos, int numRows, bool WXUNUSED(updateLabels) )
5763 {
5764 // TODO: something with updateLabels flag
5765
5766 if ( !m_created )
5767 {
5768 wxFAIL_MSG( wxT("Called wxGrid::InsertRows() before calling CreateGrid()") );
5769 return FALSE;
5770 }
5771
5772 if ( m_table )
5773 {
5774 if (IsCellEditControlEnabled())
5775 DisableCellEditControl();
5776
5777 bool done = m_table->InsertRows( pos, numRows );
5778 return done;
5779
5780 // the table will have sent the results of the insert row
5781 // operation to this view object as a grid table message
5782 }
5783 return FALSE;
5784 }
5785
5786
5787 bool wxGrid::AppendRows( int numRows, bool WXUNUSED(updateLabels) )
5788 {
5789 // TODO: something with updateLabels flag
5790
5791 if ( !m_created )
5792 {
5793 wxFAIL_MSG( wxT("Called wxGrid::AppendRows() before calling CreateGrid()") );
5794 return FALSE;
5795 }
5796
5797 if ( m_table )
5798 {
5799 bool done = m_table && m_table->AppendRows( numRows );
5800 return done;
5801 // the table will have sent the results of the append row
5802 // operation to this view object as a grid table message
5803 }
5804 return FALSE;
5805 }
5806
5807
5808 bool wxGrid::DeleteRows( int pos, int numRows, bool WXUNUSED(updateLabels) )
5809 {
5810 // TODO: something with updateLabels flag
5811
5812 if ( !m_created )
5813 {
5814 wxFAIL_MSG( wxT("Called wxGrid::DeleteRows() before calling CreateGrid()") );
5815 return FALSE;
5816 }
5817
5818 if ( m_table )
5819 {
5820 if (IsCellEditControlEnabled())
5821 DisableCellEditControl();
5822
5823 bool done = m_table->DeleteRows( pos, numRows );
5824 return done;
5825 // the table will have sent the results of the delete row
5826 // operation to this view object as a grid table message
5827 }
5828 return FALSE;
5829 }
5830
5831
5832 bool wxGrid::InsertCols( int pos, int numCols, bool WXUNUSED(updateLabels) )
5833 {
5834 // TODO: something with updateLabels flag
5835
5836 if ( !m_created )
5837 {
5838 wxFAIL_MSG( wxT("Called wxGrid::InsertCols() before calling CreateGrid()") );
5839 return FALSE;
5840 }
5841
5842 if ( m_table )
5843 {
5844 if (IsCellEditControlEnabled())
5845 DisableCellEditControl();
5846
5847 bool done = m_table->InsertCols( pos, numCols );
5848 return done;
5849 // the table will have sent the results of the insert col
5850 // operation to this view object as a grid table message
5851 }
5852 return FALSE;
5853 }
5854
5855
5856 bool wxGrid::AppendCols( int numCols, bool WXUNUSED(updateLabels) )
5857 {
5858 // TODO: something with updateLabels flag
5859
5860 if ( !m_created )
5861 {
5862 wxFAIL_MSG( wxT("Called wxGrid::AppendCols() before calling CreateGrid()") );
5863 return FALSE;
5864 }
5865
5866 if ( m_table )
5867 {
5868 bool done = m_table->AppendCols( numCols );
5869 return done;
5870 // the table will have sent the results of the append col
5871 // operation to this view object as a grid table message
5872 }
5873 return FALSE;
5874 }
5875
5876
5877 bool wxGrid::DeleteCols( int pos, int numCols, bool WXUNUSED(updateLabels) )
5878 {
5879 // TODO: something with updateLabels flag
5880
5881 if ( !m_created )
5882 {
5883 wxFAIL_MSG( wxT("Called wxGrid::DeleteCols() before calling CreateGrid()") );
5884 return FALSE;
5885 }
5886
5887 if ( m_table )
5888 {
5889 if (IsCellEditControlEnabled())
5890 DisableCellEditControl();
5891
5892 bool done = m_table->DeleteCols( pos, numCols );
5893 return done;
5894 // the table will have sent the results of the delete col
5895 // operation to this view object as a grid table message
5896 }
5897 return FALSE;
5898 }
5899
5900
5901
5902 //
5903 // ----- event handlers
5904 //
5905
5906 // Generate a grid event based on a mouse event and
5907 // return the result of ProcessEvent()
5908 //
5909 int wxGrid::SendEvent( const wxEventType type,
5910 int row, int col,
5911 wxMouseEvent& mouseEv )
5912 {
5913 bool claimed;
5914 bool vetoed= FALSE;
5915
5916 if ( type == wxEVT_GRID_ROW_SIZE || type == wxEVT_GRID_COL_SIZE )
5917 {
5918 int rowOrCol = (row == -1 ? col : row);
5919
5920 wxGridSizeEvent gridEvt( GetId(),
5921 type,
5922 this,
5923 rowOrCol,
5924 mouseEv.GetX() + GetRowLabelSize(),
5925 mouseEv.GetY() + GetColLabelSize(),
5926 mouseEv.ControlDown(),
5927 mouseEv.ShiftDown(),
5928 mouseEv.AltDown(),
5929 mouseEv.MetaDown() );
5930
5931 claimed = GetEventHandler()->ProcessEvent(gridEvt);
5932 vetoed = !gridEvt.IsAllowed();
5933 }
5934 else if ( type == wxEVT_GRID_RANGE_SELECT )
5935 {
5936 // Right now, it should _never_ end up here!
5937 wxGridRangeSelectEvent gridEvt( GetId(),
5938 type,
5939 this,
5940 m_selectingTopLeft,
5941 m_selectingBottomRight,
5942 TRUE,
5943 mouseEv.ControlDown(),
5944 mouseEv.ShiftDown(),
5945 mouseEv.AltDown(),
5946 mouseEv.MetaDown() );
5947
5948 claimed = GetEventHandler()->ProcessEvent(gridEvt);
5949 vetoed = !gridEvt.IsAllowed();
5950 }
5951 else
5952 {
5953 wxGridEvent gridEvt( GetId(),
5954 type,
5955 this,
5956 row, col,
5957 mouseEv.GetX() + GetRowLabelSize(),
5958 mouseEv.GetY() + GetColLabelSize(),
5959 FALSE,
5960 mouseEv.ControlDown(),
5961 mouseEv.ShiftDown(),
5962 mouseEv.AltDown(),
5963 mouseEv.MetaDown() );
5964 claimed = GetEventHandler()->ProcessEvent(gridEvt);
5965 vetoed = !gridEvt.IsAllowed();
5966 }
5967
5968 // A Veto'd event may not be `claimed' so test this first
5969 if (vetoed) return -1;
5970 return claimed ? 1 : 0;
5971 }
5972
5973
5974 // Generate a grid event of specified type and return the result
5975 // of ProcessEvent().
5976 //
5977 int wxGrid::SendEvent( const wxEventType type,
5978 int row, int col )
5979 {
5980 bool claimed;
5981 bool vetoed= FALSE;
5982
5983 if ( type == wxEVT_GRID_ROW_SIZE || type == wxEVT_GRID_COL_SIZE )
5984 {
5985 int rowOrCol = (row == -1 ? col : row);
5986
5987 wxGridSizeEvent gridEvt( GetId(),
5988 type,
5989 this,
5990 rowOrCol );
5991
5992 claimed = GetEventHandler()->ProcessEvent(gridEvt);
5993 vetoed = !gridEvt.IsAllowed();
5994 }
5995 else
5996 {
5997 wxGridEvent gridEvt( GetId(),
5998 type,
5999 this,
6000 row, col );
6001
6002 claimed = GetEventHandler()->ProcessEvent(gridEvt);
6003 vetoed = !gridEvt.IsAllowed();
6004 }
6005
6006 // A Veto'd event may not be `claimed' so test this first
6007 if (vetoed) return -1;
6008 return claimed ? 1 : 0;
6009 }
6010
6011
6012 void wxGrid::OnPaint( wxPaintEvent& WXUNUSED(event) )
6013 {
6014 wxPaintDC dc(this); // needed to prevent zillions of paint events on MSW
6015 }
6016
6017 void wxGrid::Refresh(bool eraseb, const wxRect* rect)
6018 {
6019 // Don't do anything if between Begin/EndBatch...
6020 // EndBatch() will do all this on the last nested one anyway.
6021 if (! GetBatchCount())
6022 {
6023 // Refresh to get correct scrolled position:
6024 wxScrolledWindow::Refresh(eraseb,rect);
6025
6026 if (rect)
6027 {
6028 int rect_x, rect_y, rectWidth, rectHeight;
6029 int width_label, width_cell, height_label, height_cell;
6030 int x, y;
6031
6032 //Copy rectangle can get scroll offsets..
6033 rect_x = rect->GetX();
6034 rect_y = rect->GetY();
6035 rectWidth = rect->GetWidth();
6036 rectHeight = rect->GetHeight();
6037
6038 width_label = m_rowLabelWidth - rect_x;
6039 if (width_label > rectWidth) width_label = rectWidth;
6040
6041 height_label = m_colLabelHeight - rect_y;
6042 if (height_label > rectHeight) height_label = rectHeight;
6043
6044 if (rect_x > m_rowLabelWidth)
6045 {
6046 x = rect_x - m_rowLabelWidth;
6047 width_cell = rectWidth;
6048 }
6049 else
6050 {
6051 x = 0;
6052 width_cell = rectWidth - (m_rowLabelWidth - rect_x);
6053 }
6054
6055 if (rect_y > m_colLabelHeight)
6056 {
6057 y = rect_y - m_colLabelHeight;
6058 height_cell = rectHeight;
6059 }
6060 else
6061 {
6062 y = 0;
6063 height_cell = rectHeight - (m_colLabelHeight - rect_y);
6064 }
6065
6066 // Paint corner label part intersecting rect.
6067 if ( width_label > 0 && height_label > 0 )
6068 {
6069 wxRect anotherrect(rect_x, rect_y, width_label, height_label);
6070 m_cornerLabelWin->Refresh(eraseb, &anotherrect);
6071 }
6072
6073 // Paint col labels part intersecting rect.
6074 if ( width_cell > 0 && height_label > 0 )
6075 {
6076 wxRect anotherrect(x, rect_y, width_cell, height_label);
6077 m_colLabelWin->Refresh(eraseb, &anotherrect);
6078 }
6079
6080 // Paint row labels part intersecting rect.
6081 if ( width_label > 0 && height_cell > 0 )
6082 {
6083 wxRect anotherrect(rect_x, y, width_label, height_cell);
6084 m_rowLabelWin->Refresh(eraseb, &anotherrect);
6085 }
6086
6087 // Paint cell area part intersecting rect.
6088 if ( width_cell > 0 && height_cell > 0 )
6089 {
6090 wxRect anotherrect(x, y, width_cell, height_cell);
6091 m_gridWin->Refresh(eraseb, &anotherrect);
6092 }
6093 }
6094 else
6095 {
6096 m_cornerLabelWin->Refresh(eraseb, NULL);
6097 m_colLabelWin->Refresh(eraseb, NULL);
6098 m_rowLabelWin->Refresh(eraseb, NULL);
6099 m_gridWin->Refresh(eraseb, NULL);
6100 }
6101 }
6102 }
6103
6104 void wxGrid::OnSize( wxSizeEvent& event )
6105 {
6106 // position the child windows
6107 CalcWindowSizes();
6108
6109 // don't call CalcDimensions() from here, the base class handles the size
6110 // changes itself
6111 event.Skip();
6112 }
6113
6114
6115 void wxGrid::OnKeyDown( wxKeyEvent& event )
6116 {
6117 if ( m_inOnKeyDown )
6118 {
6119 // shouldn't be here - we are going round in circles...
6120 //
6121 wxFAIL_MSG( wxT("wxGrid::OnKeyDown called while already active") );
6122 }
6123
6124 m_inOnKeyDown = TRUE;
6125
6126 // propagate the event up and see if it gets processed
6127 //
6128 wxWindow *parent = GetParent();
6129 wxKeyEvent keyEvt( event );
6130 keyEvt.SetEventObject( parent );
6131
6132 if ( !parent->GetEventHandler()->ProcessEvent( keyEvt ) )
6133 {
6134
6135 // try local handlers
6136 //
6137 switch ( event.GetKeyCode() )
6138 {
6139 case WXK_UP:
6140 if ( event.ControlDown() )
6141 {
6142 MoveCursorUpBlock( event.ShiftDown() );
6143 }
6144 else
6145 {
6146 MoveCursorUp( event.ShiftDown() );
6147 }
6148 break;
6149
6150 case WXK_DOWN:
6151 if ( event.ControlDown() )
6152 {
6153 MoveCursorDownBlock( event.ShiftDown() );
6154 }
6155 else
6156 {
6157 MoveCursorDown( event.ShiftDown() );
6158 }
6159 break;
6160
6161 case WXK_LEFT:
6162 if ( event.ControlDown() )
6163 {
6164 MoveCursorLeftBlock( event.ShiftDown() );
6165 }
6166 else
6167 {
6168 MoveCursorLeft( event.ShiftDown() );
6169 }
6170 break;
6171
6172 case WXK_RIGHT:
6173 if ( event.ControlDown() )
6174 {
6175 MoveCursorRightBlock( event.ShiftDown() );
6176 }
6177 else
6178 {
6179 MoveCursorRight( event.ShiftDown() );
6180 }
6181 break;
6182
6183 case WXK_RETURN:
6184 case WXK_NUMPAD_ENTER:
6185 if ( event.ControlDown() )
6186 {
6187 event.Skip(); // to let the edit control have the return
6188 }
6189 else
6190 {
6191 if ( GetGridCursorRow() < GetNumberRows()-1 )
6192 {
6193 MoveCursorDown( event.ShiftDown() );
6194 }
6195 else
6196 {
6197 // at the bottom of a column
6198 HideCellEditControl();
6199 SaveEditControlValue();
6200 }
6201 }
6202 break;
6203
6204 case WXK_ESCAPE:
6205 ClearSelection();
6206 break;
6207
6208 case WXK_TAB:
6209 if (event.ShiftDown())
6210 {
6211 if ( GetGridCursorCol() > 0 )
6212 {
6213 MoveCursorLeft( FALSE );
6214 }
6215 else
6216 {
6217 // at left of grid
6218 HideCellEditControl();
6219 SaveEditControlValue();
6220 }
6221 }
6222 else
6223 {
6224 if ( GetGridCursorCol() < GetNumberCols()-1 )
6225 {
6226 MoveCursorRight( FALSE );
6227 }
6228 else
6229 {
6230 // at right of grid
6231 HideCellEditControl();
6232 SaveEditControlValue();
6233 }
6234 }
6235 break;
6236
6237 case WXK_HOME:
6238 if ( event.ControlDown() )
6239 {
6240 MakeCellVisible( 0, 0 );
6241 SetCurrentCell( 0, 0 );
6242 }
6243 else
6244 {
6245 event.Skip();
6246 }
6247 break;
6248
6249 case WXK_END:
6250 if ( event.ControlDown() )
6251 {
6252 MakeCellVisible( m_numRows-1, m_numCols-1 );
6253 SetCurrentCell( m_numRows-1, m_numCols-1 );
6254 }
6255 else
6256 {
6257 event.Skip();
6258 }
6259 break;
6260
6261 case WXK_PRIOR:
6262 MovePageUp();
6263 break;
6264
6265 case WXK_NEXT:
6266 MovePageDown();
6267 break;
6268
6269 case WXK_SPACE:
6270 if ( event.ControlDown() )
6271 {
6272 if ( m_selection )
6273 {
6274 m_selection->ToggleCellSelection( m_currentCellCoords.GetRow(),
6275 m_currentCellCoords.GetCol(),
6276 event.ControlDown(),
6277 event.ShiftDown(),
6278 event.AltDown(),
6279 event.MetaDown() );
6280 }
6281 break;
6282 }
6283 if ( !IsEditable() )
6284 {
6285 MoveCursorRight( FALSE );
6286 break;
6287 }
6288 // Otherwise fall through to default
6289
6290 default:
6291 // is it possible to edit the current cell at all?
6292 if ( !IsCellEditControlEnabled() && CanEnableCellControl() )
6293 {
6294 // yes, now check whether the cells editor accepts the key
6295 int row = m_currentCellCoords.GetRow();
6296 int col = m_currentCellCoords.GetCol();
6297 wxGridCellAttr* attr = GetCellAttr(row, col);
6298 wxGridCellEditor *editor = attr->GetEditor(this, row, col);
6299
6300 // <F2> is special and will always start editing, for
6301 // other keys - ask the editor itself
6302 if ( (event.GetKeyCode() == WXK_F2 && !event.HasModifiers())
6303 || editor->IsAcceptedKey(event) )
6304 {
6305 // ensure cell is visble
6306 MakeCellVisible(row, col);
6307 EnableCellEditControl();
6308
6309 // a problem can arise if the cell is not completely
6310 // visible (even after calling MakeCellVisible the
6311 // control is not created and calling StartingKey will
6312 // crash the app
6313 if( editor->IsCreated() && m_cellEditCtrlEnabled ) editor->StartingKey(event);
6314 }
6315 else
6316 {
6317 event.Skip();
6318 }
6319
6320 editor->DecRef();
6321 attr->DecRef();
6322 }
6323 else
6324 {
6325 // let others process char events with modifiers or all
6326 // char events for readonly cells
6327 event.Skip();
6328 }
6329 break;
6330 }
6331 }
6332
6333 m_inOnKeyDown = FALSE;
6334 }
6335
6336 void wxGrid::OnKeyUp( wxKeyEvent& event )
6337 {
6338 // try local handlers
6339 //
6340 if ( event.GetKeyCode() == WXK_SHIFT )
6341 {
6342 if ( m_selectingTopLeft != wxGridNoCellCoords &&
6343 m_selectingBottomRight != wxGridNoCellCoords )
6344 {
6345 if ( m_selection )
6346 {
6347 m_selection->SelectBlock( m_selectingTopLeft.GetRow(),
6348 m_selectingTopLeft.GetCol(),
6349 m_selectingBottomRight.GetRow(),
6350 m_selectingBottomRight.GetCol(),
6351 event.ControlDown(),
6352 TRUE,
6353 event.AltDown(),
6354 event.MetaDown() );
6355 }
6356 }
6357
6358 m_selectingTopLeft = wxGridNoCellCoords;
6359 m_selectingBottomRight = wxGridNoCellCoords;
6360 m_selectingKeyboard = wxGridNoCellCoords;
6361 }
6362 }
6363
6364 void wxGrid::OnEraseBackground(wxEraseEvent&)
6365 {
6366 }
6367
6368 void wxGrid::SetCurrentCell( const wxGridCellCoords& coords )
6369 {
6370 if ( SendEvent( wxEVT_GRID_SELECT_CELL, coords.GetRow(), coords.GetCol() ) )
6371 {
6372 // the event has been intercepted - do nothing
6373 return;
6374 }
6375
6376 wxClientDC dc(m_gridWin);
6377 PrepareDC(dc);
6378
6379 if ( m_currentCellCoords != wxGridNoCellCoords )
6380 {
6381 HideCellEditControl();
6382 DisableCellEditControl();
6383
6384 if ( IsVisible( m_currentCellCoords, FALSE ) )
6385 {
6386 wxRect r;
6387 r = BlockToDeviceRect(m_currentCellCoords, m_currentCellCoords);
6388 if ( !m_gridLinesEnabled )
6389 {
6390 r.x--;
6391 r.y--;
6392 r.width++;
6393 r.height++;
6394 }
6395
6396 wxGridCellCoordsArray cells = CalcCellsExposed( r );
6397
6398 // Otherwise refresh redraws the highlight!
6399 m_currentCellCoords = coords;
6400
6401 DrawGridCellArea(dc,cells);
6402 DrawAllGridLines( dc, r );
6403 }
6404 }
6405
6406 m_currentCellCoords = coords;
6407
6408 wxGridCellAttr* attr = GetCellAttr(coords);
6409 DrawCellHighlight(dc, attr);
6410 attr->DecRef();
6411 }
6412
6413
6414 void wxGrid::HighlightBlock( int topRow, int leftCol, int bottomRow, int rightCol )
6415 {
6416 int temp;
6417 wxGridCellCoords updateTopLeft, updateBottomRight;
6418
6419 if ( m_selection )
6420 {
6421 if ( m_selection->GetSelectionMode() == wxGrid::wxGridSelectRows )
6422 {
6423 leftCol = 0;
6424 rightCol = GetNumberCols() - 1;
6425 }
6426 else if ( m_selection->GetSelectionMode() == wxGrid::wxGridSelectColumns )
6427 {
6428 topRow = 0;
6429 bottomRow = GetNumberRows() - 1;
6430 }
6431 }
6432
6433 if ( topRow > bottomRow )
6434 {
6435 temp = topRow;
6436 topRow = bottomRow;
6437 bottomRow = temp;
6438 }
6439
6440 if ( leftCol > rightCol )
6441 {
6442 temp = leftCol;
6443 leftCol = rightCol;
6444 rightCol = temp;
6445 }
6446
6447 updateTopLeft = wxGridCellCoords( topRow, leftCol );
6448 updateBottomRight = wxGridCellCoords( bottomRow, rightCol );
6449
6450 // First the case that we selected a completely new area
6451 if ( m_selectingTopLeft == wxGridNoCellCoords ||
6452 m_selectingBottomRight == wxGridNoCellCoords )
6453 {
6454 wxRect rect;
6455 rect = BlockToDeviceRect( wxGridCellCoords ( topRow, leftCol ),
6456 wxGridCellCoords ( bottomRow, rightCol ) );
6457 m_gridWin->Refresh( FALSE, &rect );
6458 }
6459 // Now handle changing an existing selection area.
6460 else if ( m_selectingTopLeft != updateTopLeft ||
6461 m_selectingBottomRight != updateBottomRight )
6462 {
6463 // Compute two optimal update rectangles:
6464 // Either one rectangle is a real subset of the
6465 // other, or they are (almost) disjoint!
6466 wxRect rect[4];
6467 bool need_refresh[4];
6468 need_refresh[0] =
6469 need_refresh[1] =
6470 need_refresh[2] =
6471 need_refresh[3] = FALSE;
6472 int i;
6473
6474 // Store intermediate values
6475 wxCoord oldLeft = m_selectingTopLeft.GetCol();
6476 wxCoord oldTop = m_selectingTopLeft.GetRow();
6477 wxCoord oldRight = m_selectingBottomRight.GetCol();
6478 wxCoord oldBottom = m_selectingBottomRight.GetRow();
6479
6480 // Determine the outer/inner coordinates.
6481 if (oldLeft > leftCol)
6482 {
6483 temp = oldLeft;
6484 oldLeft = leftCol;
6485 leftCol = temp;
6486 }
6487 if (oldTop > topRow )
6488 {
6489 temp = oldTop;
6490 oldTop = topRow;
6491 topRow = temp;
6492 }
6493 if (oldRight < rightCol )
6494 {
6495 temp = oldRight;
6496 oldRight = rightCol;
6497 rightCol = temp;
6498 }
6499 if (oldBottom < bottomRow)
6500 {
6501 temp = oldBottom;
6502 oldBottom = bottomRow;
6503 bottomRow = temp;
6504 }
6505
6506 // Now, either the stuff marked old is the outer
6507 // rectangle or we don't have a situation where one
6508 // is contained in the other.
6509
6510 if ( oldLeft < leftCol )
6511 {
6512 // Refresh the newly selected or deselected
6513 // area to the left of the old or new selection.
6514 need_refresh[0] = TRUE;
6515 rect[0] = BlockToDeviceRect( wxGridCellCoords ( oldTop,
6516 oldLeft ),
6517 wxGridCellCoords ( oldBottom,
6518 leftCol - 1 ) );
6519 }
6520
6521 if ( oldTop < topRow )
6522 {
6523 // Refresh the newly selected or deselected
6524 // area above the old or new selection.
6525 need_refresh[1] = TRUE;
6526 rect[1] = BlockToDeviceRect( wxGridCellCoords ( oldTop,
6527 leftCol ),
6528 wxGridCellCoords ( topRow - 1,
6529 rightCol ) );
6530 }
6531
6532 if ( oldRight > rightCol )
6533 {
6534 // Refresh the newly selected or deselected
6535 // area to the right of the old or new selection.
6536 need_refresh[2] = TRUE;
6537 rect[2] = BlockToDeviceRect( wxGridCellCoords ( oldTop,
6538 rightCol + 1 ),
6539 wxGridCellCoords ( oldBottom,
6540 oldRight ) );
6541 }
6542
6543 if ( oldBottom > bottomRow )
6544 {
6545 // Refresh the newly selected or deselected
6546 // area below the old or new selection.
6547 need_refresh[3] = TRUE;
6548 rect[3] = BlockToDeviceRect( wxGridCellCoords ( bottomRow + 1,
6549 leftCol ),
6550 wxGridCellCoords ( oldBottom,
6551 rightCol ) );
6552 }
6553
6554 // various Refresh() calls
6555 for (i = 0; i < 4; i++ )
6556 if ( need_refresh[i] && rect[i] != wxGridNoCellRect )
6557 m_gridWin->Refresh( FALSE, &(rect[i]) );
6558 }
6559 // Change Selection
6560 m_selectingTopLeft = updateTopLeft;
6561 m_selectingBottomRight = updateBottomRight;
6562 }
6563
6564 //
6565 // ------ functions to get/send data (see also public functions)
6566 //
6567
6568 bool wxGrid::GetModelValues()
6569 {
6570 // Hide the editor, so it won't hide a changed value.
6571 HideCellEditControl();
6572
6573 if ( m_table )
6574 {
6575 // all we need to do is repaint the grid
6576 //
6577 m_gridWin->Refresh();
6578 return TRUE;
6579 }
6580
6581 return FALSE;
6582 }
6583
6584
6585 bool wxGrid::SetModelValues()
6586 {
6587 int row, col;
6588
6589 // Disable the editor, so it won't hide a changed value.
6590 // Do we also want to save the current value of the editor first?
6591 // I think so ...
6592 DisableCellEditControl();
6593
6594 if ( m_table )
6595 {
6596 for ( row = 0; row < m_numRows; row++ )
6597 {
6598 for ( col = 0; col < m_numCols; col++ )
6599 {
6600 m_table->SetValue( row, col, GetCellValue(row, col) );
6601 }
6602 }
6603
6604 return TRUE;
6605 }
6606
6607 return FALSE;
6608 }
6609
6610
6611
6612 // Note - this function only draws cells that are in the list of
6613 // exposed cells (usually set from the update region by
6614 // CalcExposedCells)
6615 //
6616 void wxGrid::DrawGridCellArea( wxDC& dc, const wxGridCellCoordsArray& cells )
6617 {
6618 if ( !m_numRows || !m_numCols ) return;
6619
6620 int i, numCells = cells.GetCount();
6621 int row, col, cell_rows, cell_cols;
6622 wxGridCellCoordsArray redrawCells;
6623
6624 for ( i = numCells-1; i >= 0; i-- )
6625 {
6626 row = cells[i].GetRow();
6627 col = cells[i].GetCol();
6628 GetCellSize( row, col, &cell_rows, &cell_cols );
6629
6630 // If this cell is part of a multicell block, find owner for repaint
6631 if ( cell_rows <= 0 || cell_cols <= 0 )
6632 {
6633 wxGridCellCoords cell(row+cell_rows, col+cell_cols);
6634 bool marked = FALSE;
6635 for ( int j = 0; j < numCells; j++ )
6636 {
6637 if ( cell == cells[j] )
6638 {
6639 marked = TRUE;
6640 break;
6641 }
6642 }
6643 if (!marked)
6644 {
6645 int count = redrawCells.GetCount();
6646 for (int j = 0; j < count; j++)
6647 {
6648 if ( cell == redrawCells[j] )
6649 {
6650 marked = TRUE;
6651 break;
6652 }
6653 }
6654 if (!marked) redrawCells.Add( cell );
6655 }
6656 continue; // don't bother drawing this cell
6657 }
6658
6659 // If this cell is empty, find cell to left that might want to overflow
6660 if (m_table && m_table->IsEmptyCell(row, col))
6661 {
6662 for ( int l = 0; l < cell_rows; l++ )
6663 {
6664 // find a cell in this row to left alreay marked for repaint
6665 int left = col;
6666 for (int k = 0; k < int(redrawCells.GetCount()); k++)
6667 if ((redrawCells[k].GetCol() < left) &&
6668 (redrawCells[k].GetRow() == row))
6669 left=redrawCells[k].GetCol();
6670
6671 if (left == col) left = 0; // oh well
6672
6673 for (int j = col-1; j >= left; j--)
6674 {
6675 if (!m_table->IsEmptyCell(row+l, j))
6676 {
6677 if (GetCellOverflow(row+l, j))
6678 {
6679 wxGridCellCoords cell(row+l, j);
6680 bool marked = FALSE;
6681
6682 for (int k = 0; k < numCells; k++)
6683 {
6684 if ( cell == cells[k] )
6685 {
6686 marked = TRUE;
6687 break;
6688 }
6689 }
6690 if (!marked)
6691 {
6692 int count = redrawCells.GetCount();
6693 for (int k = 0; k < count; k++)
6694 {
6695 if ( cell == redrawCells[k] )
6696 {
6697 marked = TRUE;
6698 break;
6699 }
6700 }
6701 if (!marked) redrawCells.Add( cell );
6702 }
6703 }
6704 break;
6705 }
6706 }
6707 }
6708 }
6709 DrawCell( dc, cells[i] );
6710 }
6711
6712 numCells = redrawCells.GetCount();
6713
6714 for ( i = numCells - 1; i >= 0; i-- )
6715 {
6716 DrawCell( dc, redrawCells[i] );
6717 }
6718 }
6719
6720
6721 void wxGrid::DrawGridSpace( wxDC& dc )
6722 {
6723 int cw, ch;
6724 m_gridWin->GetClientSize( &cw, &ch );
6725
6726 int right, bottom;
6727 CalcUnscrolledPosition( cw, ch, &right, &bottom );
6728
6729 int rightCol = m_numCols > 0 ? GetColRight(m_numCols - 1) : 0;
6730 int bottomRow = m_numRows > 0 ? GetRowBottom(m_numRows - 1) : 0 ;
6731
6732 if ( right > rightCol || bottom > bottomRow )
6733 {
6734 int left, top;
6735 CalcUnscrolledPosition( 0, 0, &left, &top );
6736
6737 dc.SetBrush( wxBrush(GetDefaultCellBackgroundColour(), wxSOLID) );
6738 dc.SetPen( *wxTRANSPARENT_PEN );
6739
6740 if ( right > rightCol )
6741 {
6742 dc.DrawRectangle( rightCol, top, right - rightCol, ch);
6743 }
6744
6745 if ( bottom > bottomRow )
6746 {
6747 dc.DrawRectangle( left, bottomRow, cw, bottom - bottomRow);
6748 }
6749 }
6750 }
6751
6752
6753 void wxGrid::DrawCell( wxDC& dc, const wxGridCellCoords& coords )
6754 {
6755 int row = coords.GetRow();
6756 int col = coords.GetCol();
6757
6758 if ( GetColWidth(col) <= 0 || GetRowHeight(row) <= 0 )
6759 return;
6760
6761 // we draw the cell border ourselves
6762 #if !WXGRID_DRAW_LINES
6763 if ( m_gridLinesEnabled )
6764 DrawCellBorder( dc, coords );
6765 #endif
6766
6767 wxGridCellAttr* attr = GetCellAttr(row, col);
6768
6769 bool isCurrent = coords == m_currentCellCoords;
6770
6771 wxRect rect = CellToRect( row, col );
6772
6773 // if the editor is shown, we should use it and not the renderer
6774 // Note: However, only if it is really _shown_, i.e. not hidden!
6775 if ( isCurrent && IsCellEditControlShown() )
6776 {
6777 wxGridCellEditor *editor = attr->GetEditor(this, row, col);
6778 editor->PaintBackground(rect, attr);
6779 editor->DecRef();
6780 }
6781 else
6782 {
6783 // but all the rest is drawn by the cell renderer and hence may be
6784 // customized
6785 wxGridCellRenderer *renderer = attr->GetRenderer(this, row, col);
6786 renderer->Draw(*this, *attr, dc, rect, row, col, IsInSelection(coords));
6787 renderer->DecRef();
6788 }
6789
6790 attr->DecRef();
6791 }
6792
6793 void wxGrid::DrawCellHighlight( wxDC& dc, const wxGridCellAttr *attr )
6794 {
6795 int row = m_currentCellCoords.GetRow();
6796 int col = m_currentCellCoords.GetCol();
6797
6798 if ( GetColWidth(col) <= 0 || GetRowHeight(row) <= 0 )
6799 return;
6800
6801 wxRect rect = CellToRect(row, col);
6802
6803 // hmmm... what could we do here to show that the cell is disabled?
6804 // for now, I just draw a thinner border than for the other ones, but
6805 // it doesn't look really good
6806
6807 int penWidth = attr->IsReadOnly() ? m_cellHighlightROPenWidth : m_cellHighlightPenWidth;
6808
6809 if (penWidth > 0)
6810 {
6811 // The center of th drawn line is where the position/width/height of
6812 // the rectangle is actually at, (on wxMSW atr least,) so we will
6813 // reduce the size of the rectangle to compensate for the thickness of
6814 // the line. If this is too strange on non wxMSW platforms then
6815 // please #ifdef this appropriately.
6816 rect.x += penWidth/2;
6817 rect.y += penWidth/2;
6818 rect.width -= penWidth-1;
6819 rect.height -= penWidth-1;
6820
6821
6822 // Now draw the rectangle
6823 // use the cellHighlightColour if the cell is inside a selection, this
6824 // will ensure the cell is always visible.
6825 dc.SetPen(wxPen(IsInSelection(row,col)?m_selectionForeground:m_cellHighlightColour, penWidth, wxSOLID));
6826 dc.SetBrush(*wxTRANSPARENT_BRUSH);
6827 dc.DrawRectangle(rect);
6828 }
6829
6830 #if 0
6831 // VZ: my experiments with 3d borders...
6832
6833 // how to properly set colours for arbitrary bg?
6834 wxCoord x1 = rect.x,
6835 y1 = rect.y,
6836 x2 = rect.x + rect.width -1,
6837 y2 = rect.y + rect.height -1;
6838
6839 dc.SetPen(*wxWHITE_PEN);
6840 dc.DrawLine(x1, y1, x2, y1);
6841 dc.DrawLine(x1, y1, x1, y2);
6842
6843 dc.DrawLine(x1 + 1, y2 - 1, x2 - 1, y2 - 1);
6844 dc.DrawLine(x2 - 1, y1 + 1, x2 - 1, y2 );
6845
6846 dc.SetPen(*wxBLACK_PEN);
6847 dc.DrawLine(x1, y2, x2, y2);
6848 dc.DrawLine(x2, y1, x2, y2+1);
6849 #endif // 0
6850 }
6851
6852
6853 void wxGrid::DrawCellBorder( wxDC& dc, const wxGridCellCoords& coords )
6854 {
6855 int row = coords.GetRow();
6856 int col = coords.GetCol();
6857 if ( GetColWidth(col) <= 0 || GetRowHeight(row) <= 0 )
6858 return;
6859
6860 dc.SetPen( wxPen(GetGridLineColour(), 1, wxSOLID) );
6861
6862 wxRect rect = CellToRect( row, col );
6863
6864 // right hand border
6865 //
6866 dc.DrawLine( rect.x + rect.width, rect.y,
6867 rect.x + rect.width, rect.y + rect.height + 1 );
6868
6869 // bottom border
6870 //
6871 dc.DrawLine( rect.x, rect.y + rect.height,
6872 rect.x + rect.width, rect.y + rect.height);
6873 }
6874
6875 void wxGrid::DrawHighlight(wxDC& dc,const wxGridCellCoordsArray& cells)
6876 {
6877 // This if block was previously in wxGrid::OnPaint but that doesn't
6878 // seem to get called under wxGTK - MB
6879 //
6880 if ( m_currentCellCoords == wxGridNoCellCoords &&
6881 m_numRows && m_numCols )
6882 {
6883 m_currentCellCoords.Set(0, 0);
6884 }
6885
6886 if ( IsCellEditControlShown() )
6887 {
6888 // don't show highlight when the edit control is shown
6889 return;
6890 }
6891
6892 // if the active cell was repainted, repaint its highlight too because it
6893 // might have been damaged by the grid lines
6894 size_t count = cells.GetCount();
6895 for ( size_t n = 0; n < count; n++ )
6896 {
6897 if ( cells[n] == m_currentCellCoords )
6898 {
6899 wxGridCellAttr* attr = GetCellAttr(m_currentCellCoords);
6900 DrawCellHighlight(dc, attr);
6901 attr->DecRef();
6902
6903 break;
6904 }
6905 }
6906 }
6907
6908 // TODO: remove this ???
6909 // This is used to redraw all grid lines e.g. when the grid line colour
6910 // has been changed
6911 //
6912 void wxGrid::DrawAllGridLines( wxDC& dc, const wxRegion & WXUNUSED(reg) )
6913 {
6914 #if !WXGRID_DRAW_LINES
6915 return;
6916 #endif
6917
6918 if ( !m_gridLinesEnabled ||
6919 !m_numRows ||
6920 !m_numCols ) return;
6921
6922 int top, bottom, left, right;
6923
6924 #if 0 //#ifndef __WXGTK__
6925 if (reg.IsEmpty())
6926 {
6927 int cw, ch;
6928 m_gridWin->GetClientSize(&cw, &ch);
6929
6930 // virtual coords of visible area
6931 //
6932 CalcUnscrolledPosition( 0, 0, &left, &top );
6933 CalcUnscrolledPosition( cw, ch, &right, &bottom );
6934 }
6935 else
6936 {
6937 wxCoord x, y, w, h;
6938 reg.GetBox(x, y, w, h);
6939 CalcUnscrolledPosition( x, y, &left, &top );
6940 CalcUnscrolledPosition( x + w, y + h, &right, &bottom );
6941 }
6942 #else
6943 int cw, ch;
6944 m_gridWin->GetClientSize(&cw, &ch);
6945 CalcUnscrolledPosition( 0, 0, &left, &top );
6946 CalcUnscrolledPosition( cw, ch, &right, &bottom );
6947 #endif
6948
6949 // avoid drawing grid lines past the last row and col
6950 //
6951 right = wxMin( right, GetColRight(m_numCols - 1) );
6952 bottom = wxMin( bottom, GetRowBottom(m_numRows - 1) );
6953
6954 // no gridlines inside multicells, clip them out
6955 int leftCol = internalXToCol(left);
6956 int topRow = internalYToRow(top);
6957 int rightCol = internalXToCol(right);
6958 int bottomRow = internalYToRow(bottom);
6959 wxRegion clippedcells(0, 0, cw, ch);
6960
6961
6962 int i, j, cell_rows, cell_cols;
6963 wxRect rect;
6964
6965 for (j=topRow; j<bottomRow; j++)
6966 {
6967 for (i=leftCol; i<rightCol; i++)
6968 {
6969 GetCellSize( j, i, &cell_rows, &cell_cols );
6970 if ((cell_rows > 1) || (cell_cols > 1))
6971 {
6972 rect = CellToRect(j,i);
6973 CalcScrolledPosition( rect.x, rect.y, &rect.x, &rect.y );
6974 clippedcells.Subtract(rect);
6975 }
6976 else if ((cell_rows < 0) || (cell_cols < 0))
6977 {
6978 rect = CellToRect(j+cell_rows, i+cell_cols);
6979 CalcScrolledPosition( rect.x, rect.y, &rect.x, &rect.y );
6980 clippedcells.Subtract(rect);
6981 }
6982 }
6983 }
6984 dc.SetClippingRegion( clippedcells );
6985
6986 dc.SetPen( wxPen(GetGridLineColour(), 1, wxSOLID) );
6987
6988 // horizontal grid lines
6989 //
6990 // already declared above - int i;
6991 for ( i = internalYToRow(top); i < m_numRows; i++ )
6992 {
6993 int bot = GetRowBottom(i) - 1;
6994
6995 if ( bot > bottom )
6996 {
6997 break;
6998 }
6999
7000 if ( bot >= top )
7001 {
7002 dc.DrawLine( left, bot, right, bot );
7003 }
7004 }
7005
7006
7007 // vertical grid lines
7008 //
7009 for ( i = internalXToCol(left); i < m_numCols; i++ )
7010 {
7011 int colRight = GetColRight(i) - 1;
7012 if ( colRight > right )
7013 {
7014 break;
7015 }
7016
7017 if ( colRight >= left )
7018 {
7019 dc.DrawLine( colRight, top, colRight, bottom );
7020 }
7021 }
7022 dc.DestroyClippingRegion();
7023 }
7024
7025
7026 void wxGrid::DrawRowLabels( wxDC& dc ,const wxArrayInt& rows)
7027 {
7028 if ( !m_numRows ) return;
7029
7030 size_t i;
7031 size_t numLabels = rows.GetCount();
7032
7033 for ( i = 0; i < numLabels; i++ )
7034 {
7035 DrawRowLabel( dc, rows[i] );
7036 }
7037 }
7038
7039
7040 void wxGrid::DrawRowLabel( wxDC& dc, int row )
7041 {
7042 if ( GetRowHeight(row) <= 0 )
7043 return;
7044
7045 int rowTop = GetRowTop(row),
7046 rowBottom = GetRowBottom(row) - 1;
7047
7048 dc.SetPen( wxPen(wxSystemSettings::GetColour(wxSYS_COLOUR_3DDKSHADOW),1, wxSOLID) );
7049 dc.DrawLine( m_rowLabelWidth-1, rowTop,
7050 m_rowLabelWidth-1, rowBottom );
7051
7052 dc.DrawLine( 0, rowTop, 0, rowBottom );
7053
7054 dc.DrawLine( 0, rowBottom, m_rowLabelWidth, rowBottom );
7055
7056 dc.SetPen( *wxWHITE_PEN );
7057 dc.DrawLine( 1, rowTop, 1, rowBottom );
7058 dc.DrawLine( 1, rowTop, m_rowLabelWidth-1, rowTop );
7059
7060 dc.SetBackgroundMode( wxTRANSPARENT );
7061 dc.SetTextForeground( GetLabelTextColour() );
7062 dc.SetFont( GetLabelFont() );
7063
7064 int hAlign, vAlign;
7065 GetRowLabelAlignment( &hAlign, &vAlign );
7066
7067 wxRect rect;
7068 rect.SetX( 2 );
7069 rect.SetY( GetRowTop(row) + 2 );
7070 rect.SetWidth( m_rowLabelWidth - 4 );
7071 rect.SetHeight( GetRowHeight(row) - 4 );
7072 DrawTextRectangle( dc, GetRowLabelValue( row ), rect, hAlign, vAlign );
7073 }
7074
7075
7076 void wxGrid::DrawColLabels( wxDC& dc,const wxArrayInt& cols )
7077 {
7078 if ( !m_numCols ) return;
7079
7080 size_t i;
7081 size_t numLabels = cols.GetCount();
7082
7083 for ( i = 0; i < numLabels; i++ )
7084 {
7085 DrawColLabel( dc, cols[i] );
7086 }
7087 }
7088
7089
7090 void wxGrid::DrawColLabel( wxDC& dc, int col )
7091 {
7092 if ( GetColWidth(col) <= 0 )
7093 return;
7094
7095 int colLeft = GetColLeft(col),
7096 colRight = GetColRight(col) - 1;
7097
7098 dc.SetPen( wxPen(wxSystemSettings::GetColour(wxSYS_COLOUR_3DDKSHADOW),1, wxSOLID) );
7099 dc.DrawLine( colRight, 0,
7100 colRight, m_colLabelHeight-1 );
7101
7102 dc.DrawLine( colLeft, 0, colRight, 0 );
7103
7104 dc.DrawLine( colLeft, m_colLabelHeight-1,
7105 colRight+1, m_colLabelHeight-1 );
7106
7107 dc.SetPen( *wxWHITE_PEN );
7108 dc.DrawLine( colLeft, 1, colLeft, m_colLabelHeight-1 );
7109 dc.DrawLine( colLeft, 1, colRight, 1 );
7110
7111 dc.SetBackgroundMode( wxTRANSPARENT );
7112 dc.SetTextForeground( GetLabelTextColour() );
7113 dc.SetFont( GetLabelFont() );
7114
7115 int hAlign, vAlign, orient;
7116 GetColLabelAlignment( &hAlign, &vAlign );
7117 orient = GetColLabelTextOrientation();
7118
7119 wxRect rect;
7120 rect.SetX( colLeft + 2 );
7121 rect.SetY( 2 );
7122 rect.SetWidth( GetColWidth(col) - 4 );
7123 rect.SetHeight( m_colLabelHeight - 4 );
7124 DrawTextRectangle( dc, GetColLabelValue( col ), rect, hAlign, vAlign, orient );
7125 }
7126
7127 void wxGrid::DrawTextRectangle( wxDC& dc,
7128 const wxString& value,
7129 const wxRect& rect,
7130 int horizAlign,
7131 int vertAlign,
7132 int textOrientation )
7133 {
7134 wxArrayString lines;
7135
7136 StringToLines( value, lines );
7137
7138
7139 //Forward to new API.
7140 DrawTextRectangle( dc,
7141 lines,
7142 rect,
7143 horizAlign,
7144 vertAlign,
7145 textOrientation );
7146
7147 }
7148
7149 void wxGrid::DrawTextRectangle( wxDC& dc,
7150 const wxArrayString& lines,
7151 const wxRect& rect,
7152 int horizAlign,
7153 int vertAlign,
7154 int textOrientation )
7155 {
7156 long textWidth, textHeight;
7157 long lineWidth, lineHeight;
7158 int nLines;
7159
7160 dc.SetClippingRegion( rect );
7161
7162 nLines = lines.GetCount();
7163 if( nLines > 0 )
7164 {
7165 int l;
7166 float x = 0.0, y = 0.0;
7167
7168 if( textOrientation == wxHORIZONTAL )
7169 GetTextBoxSize(dc, lines, &textWidth, &textHeight);
7170 else
7171 GetTextBoxSize( dc, lines, &textHeight, &textWidth );
7172
7173 switch( vertAlign )
7174 {
7175 case wxALIGN_BOTTOM:
7176 if( textOrientation == wxHORIZONTAL )
7177 y = rect.y + (rect.height - textHeight - 1);
7178 else
7179 x = rect.x + rect.width - textWidth;
7180 break;
7181
7182 case wxALIGN_CENTRE:
7183 if( textOrientation == wxHORIZONTAL )
7184 y = rect.y + ((rect.height - textHeight)/2);
7185 else
7186 x = rect.x + ((rect.width - textWidth)/2);
7187 break;
7188
7189 case wxALIGN_TOP:
7190 default:
7191 if( textOrientation == wxHORIZONTAL )
7192 y = rect.y + 1;
7193 else
7194 x = rect.x + 1;
7195 break;
7196 }
7197
7198 // Align each line of a multi-line label
7199 for( l = 0; l < nLines; l++ )
7200 {
7201 dc.GetTextExtent(lines[l], &lineWidth, &lineHeight);
7202
7203 switch( horizAlign )
7204 {
7205 case wxALIGN_RIGHT:
7206 if( textOrientation == wxHORIZONTAL )
7207 x = rect.x + (rect.width - lineWidth - 1);
7208 else
7209 y = rect.y + lineWidth + 1;
7210 break;
7211
7212 case wxALIGN_CENTRE:
7213 if( textOrientation == wxHORIZONTAL )
7214 x = rect.x + ((rect.width - lineWidth)/2);
7215 else
7216 y = rect.y + rect.height - ((rect.height - lineWidth)/2);
7217 break;
7218
7219 case wxALIGN_LEFT:
7220 default:
7221 if( textOrientation == wxHORIZONTAL )
7222 x = rect.x + 1;
7223 else
7224 y = rect.y + rect.height - 1;
7225 break;
7226 }
7227
7228 if( textOrientation == wxHORIZONTAL )
7229 {
7230 dc.DrawText( lines[l], (int)x, (int)y );
7231 y += lineHeight;
7232 }
7233 else
7234 {
7235 dc.DrawRotatedText( lines[l], (int)x, (int)y, 90.0 );
7236 x += lineHeight;
7237 }
7238 }
7239 }
7240 dc.DestroyClippingRegion();
7241 }
7242
7243
7244 // Split multi line text up into an array of strings. Any existing
7245 // contents of the string array are preserved.
7246 //
7247 void wxGrid::StringToLines( const wxString& value, wxArrayString& lines )
7248 {
7249 int startPos = 0;
7250 int pos;
7251 wxString eol = wxTextFile::GetEOL( wxTextFileType_Unix );
7252 wxString tVal = wxTextFile::Translate( value, wxTextFileType_Unix );
7253
7254 while ( startPos < (int)tVal.Length() )
7255 {
7256 pos = tVal.Mid(startPos).Find( eol );
7257 if ( pos < 0 )
7258 {
7259 break;
7260 }
7261 else if ( pos == 0 )
7262 {
7263 lines.Add( wxEmptyString );
7264 }
7265 else
7266 {
7267 lines.Add( value.Mid(startPos, pos) );
7268 }
7269 startPos += pos+1;
7270 }
7271 if ( startPos < (int)value.Length() )
7272 {
7273 lines.Add( value.Mid( startPos ) );
7274 }
7275 }
7276
7277
7278 void wxGrid::GetTextBoxSize( wxDC& dc,
7279 const wxArrayString& lines,
7280 long *width, long *height )
7281 {
7282 long w = 0;
7283 long h = 0;
7284 long lineW, lineH;
7285
7286 size_t i;
7287 for ( i = 0; i < lines.GetCount(); i++ )
7288 {
7289 dc.GetTextExtent( lines[i], &lineW, &lineH );
7290 w = wxMax( w, lineW );
7291 h += lineH;
7292 }
7293
7294 *width = w;
7295 *height = h;
7296 }
7297
7298 //
7299 // ------ Batch processing.
7300 //
7301 void wxGrid::EndBatch()
7302 {
7303 if ( m_batchCount > 0 )
7304 {
7305 m_batchCount--;
7306 if ( !m_batchCount )
7307 {
7308 CalcDimensions();
7309 m_rowLabelWin->Refresh();
7310 m_colLabelWin->Refresh();
7311 m_cornerLabelWin->Refresh();
7312 m_gridWin->Refresh();
7313 }
7314 }
7315 }
7316
7317 // Use this, rather than wxWindow::Refresh(), to force an immediate
7318 // repainting of the grid. Has no effect if you are already inside a
7319 // BeginBatch / EndBatch block.
7320 //
7321 void wxGrid::ForceRefresh()
7322 {
7323 BeginBatch();
7324 EndBatch();
7325 }
7326
7327
7328 //
7329 // ------ Edit control functions
7330 //
7331
7332
7333 void wxGrid::EnableEditing( bool edit )
7334 {
7335 // TODO: improve this ?
7336 //
7337 if ( edit != m_editable )
7338 {
7339 if(!edit) EnableCellEditControl(edit);
7340 m_editable = edit;
7341 }
7342 }
7343
7344
7345 void wxGrid::EnableCellEditControl( bool enable )
7346 {
7347 if (! m_editable)
7348 return;
7349
7350 if ( m_currentCellCoords == wxGridNoCellCoords )
7351 SetCurrentCell( 0, 0 );
7352
7353 if ( enable != m_cellEditCtrlEnabled )
7354 {
7355 if ( enable )
7356 {
7357 if (SendEvent( wxEVT_GRID_EDITOR_SHOWN) <0)
7358 return;
7359
7360 // this should be checked by the caller!
7361 wxASSERT_MSG( CanEnableCellControl(),
7362 _T("can't enable editing for this cell!") );
7363
7364 // do it before ShowCellEditControl()
7365 m_cellEditCtrlEnabled = enable;
7366
7367 ShowCellEditControl();
7368 }
7369 else
7370 {
7371 //FIXME:add veto support
7372 SendEvent( wxEVT_GRID_EDITOR_HIDDEN);
7373
7374 HideCellEditControl();
7375 SaveEditControlValue();
7376
7377 // do it after HideCellEditControl()
7378 m_cellEditCtrlEnabled = enable;
7379 }
7380 }
7381 }
7382
7383 bool wxGrid::IsCurrentCellReadOnly() const
7384 {
7385 // const_cast
7386 wxGridCellAttr* attr = ((wxGrid *)this)->GetCellAttr(m_currentCellCoords);
7387 bool readonly = attr->IsReadOnly();
7388 attr->DecRef();
7389
7390 return readonly;
7391 }
7392
7393 bool wxGrid::CanEnableCellControl() const
7394 {
7395 return m_editable && !IsCurrentCellReadOnly();
7396 }
7397
7398 bool wxGrid::IsCellEditControlEnabled() const
7399 {
7400 // the cell edit control might be disable for all cells or just for the
7401 // current one if it's read only
7402 return m_cellEditCtrlEnabled ? !IsCurrentCellReadOnly() : FALSE;
7403 }
7404
7405 bool wxGrid::IsCellEditControlShown() const
7406 {
7407 bool isShown = FALSE;
7408
7409 if ( m_cellEditCtrlEnabled )
7410 {
7411 int row = m_currentCellCoords.GetRow();
7412 int col = m_currentCellCoords.GetCol();
7413 wxGridCellAttr* attr = GetCellAttr(row, col);
7414 wxGridCellEditor* editor = attr->GetEditor((wxGrid*) this, row, col);
7415 attr->DecRef();
7416
7417 if ( editor )
7418 {
7419 if ( editor->IsCreated() )
7420 {
7421 isShown = editor->GetControl()->IsShown();
7422 }
7423
7424 editor->DecRef();
7425 }
7426 }
7427
7428 return isShown;
7429 }
7430
7431 void wxGrid::ShowCellEditControl()
7432 {
7433 if ( IsCellEditControlEnabled() )
7434 {
7435 if ( !IsVisible( m_currentCellCoords ) )
7436 {
7437 m_cellEditCtrlEnabled = FALSE;
7438 return;
7439 }
7440 else
7441 {
7442 wxRect rect = CellToRect( m_currentCellCoords );
7443 int row = m_currentCellCoords.GetRow();
7444 int col = m_currentCellCoords.GetCol();
7445
7446 // if this is part of a multicell, find owner (topleft)
7447 int cell_rows, cell_cols;
7448 GetCellSize( row, col, &cell_rows, &cell_cols );
7449 if ( cell_rows <= 0 || cell_cols <= 0 )
7450 {
7451 row += cell_rows;
7452 col += cell_cols;
7453 m_currentCellCoords.SetRow( row );
7454 m_currentCellCoords.SetCol( col );
7455 }
7456
7457 // convert to scrolled coords
7458 //
7459 CalcScrolledPosition( rect.x, rect.y, &rect.x, &rect.y );
7460
7461 // done in PaintBackground()
7462 #if 0
7463 // erase the highlight and the cell contents because the editor
7464 // might not cover the entire cell
7465 wxClientDC dc( m_gridWin );
7466 PrepareDC( dc );
7467 dc.SetBrush(*wxLIGHT_GREY_BRUSH); //wxBrush(attr->GetBackgroundColour(), wxSOLID));
7468 dc.SetPen(*wxTRANSPARENT_PEN);
7469 dc.DrawRectangle(rect);
7470 #endif // 0
7471
7472 // cell is shifted by one pixel
7473 // However, don't allow x or y to become negative
7474 // since the SetSize() method interprets that as
7475 // "don't change."
7476 if (rect.x > 0)
7477 rect.x--;
7478 if (rect.y > 0)
7479 rect.y--;
7480
7481 wxGridCellAttr* attr = GetCellAttr(row, col);
7482 wxGridCellEditor* editor = attr->GetEditor(this, row, col);
7483 if ( !editor->IsCreated() )
7484 {
7485 editor->Create(m_gridWin, -1,
7486 new wxGridCellEditorEvtHandler(this, editor));
7487
7488 wxGridEditorCreatedEvent evt(GetId(),
7489 wxEVT_GRID_EDITOR_CREATED,
7490 this,
7491 row,
7492 col,
7493 editor->GetControl());
7494 GetEventHandler()->ProcessEvent(evt);
7495 }
7496
7497
7498 // resize editor to overflow into righthand cells if allowed
7499 int maxWidth = rect.width;
7500 wxString value = GetCellValue(row, col);
7501 if ( (value != wxEmptyString) && (attr->GetOverflow()) )
7502 {
7503 int y;
7504 GetTextExtent(value, &maxWidth, &y,
7505 NULL, NULL, &attr->GetFont());
7506 if (maxWidth < rect.width) maxWidth = rect.width;
7507 }
7508 int client_right = m_gridWin->GetClientSize().GetWidth();
7509 if (rect.x+maxWidth > client_right)
7510 maxWidth = client_right - rect.x;
7511
7512 if ((maxWidth > rect.width) && (col < m_numCols) && m_table)
7513 {
7514 GetCellSize( row, col, &cell_rows, &cell_cols );
7515 // may have changed earlier
7516 for (int i = col+cell_cols; i < m_numCols; i++)
7517 {
7518 int c_rows, c_cols;
7519 GetCellSize( row, i, &c_rows, &c_cols );
7520 // looks weird going over a multicell
7521 if (m_table->IsEmptyCell(row,i) &&
7522 (rect.width < maxWidth) && (c_rows == 1))
7523 rect.width += GetColWidth(i);
7524 else
7525 break;
7526 }
7527 if (rect.GetRight() > client_right)
7528 rect.SetRight(client_right-1);
7529 }
7530
7531 editor->SetCellAttr(attr);
7532 editor->SetSize( rect );
7533 editor->Show( TRUE, attr );
7534
7535 // recalc dimensions in case we need to
7536 // expand the scrolled window to account for editor
7537 CalcDimensions();
7538
7539 editor->BeginEdit(row, col, this);
7540 editor->SetCellAttr(NULL);
7541
7542 editor->DecRef();
7543 attr->DecRef();
7544 }
7545 }
7546 }
7547
7548
7549 void wxGrid::HideCellEditControl()
7550 {
7551 if ( IsCellEditControlEnabled() )
7552 {
7553 int row = m_currentCellCoords.GetRow();
7554 int col = m_currentCellCoords.GetCol();
7555
7556 wxGridCellAttr* attr = GetCellAttr(row, col);
7557 wxGridCellEditor *editor = attr->GetEditor(this, row, col);
7558 editor->Show( FALSE );
7559 editor->DecRef();
7560 attr->DecRef();
7561 m_gridWin->SetFocus();
7562 // refresh whole row to the right
7563 wxRect rect( CellToRect(row, col) );
7564 CalcScrolledPosition(rect.x, rect.y, &rect.x, &rect.y );
7565 rect.width = m_gridWin->GetClientSize().GetWidth() - rect.x;
7566 m_gridWin->Refresh( FALSE, &rect );
7567 }
7568 }
7569
7570
7571 void wxGrid::SaveEditControlValue()
7572 {
7573 if ( IsCellEditControlEnabled() )
7574 {
7575 int row = m_currentCellCoords.GetRow();
7576 int col = m_currentCellCoords.GetCol();
7577
7578 wxString oldval = GetCellValue(row,col);
7579
7580 wxGridCellAttr* attr = GetCellAttr(row, col);
7581 wxGridCellEditor* editor = attr->GetEditor(this, row, col);
7582 bool changed = editor->EndEdit(row, col, this);
7583
7584 editor->DecRef();
7585 attr->DecRef();
7586
7587 if (changed)
7588 {
7589 if ( SendEvent( wxEVT_GRID_CELL_CHANGE,
7590 m_currentCellCoords.GetRow(),
7591 m_currentCellCoords.GetCol() ) < 0 ) {
7592
7593 // Event has been vetoed, set the data back.
7594 SetCellValue(row,col,oldval);
7595 }
7596 }
7597 }
7598 }
7599
7600
7601 //
7602 // ------ Grid location functions
7603 // Note that all of these functions work with the logical coordinates of
7604 // grid cells and labels so you will need to convert from device
7605 // coordinates for mouse events etc.
7606 //
7607
7608 void wxGrid::XYToCell( int x, int y, wxGridCellCoords& coords )
7609 {
7610 int row = YToRow(y);
7611 int col = XToCol(x);
7612
7613 if ( row == -1 || col == -1 )
7614 {
7615 coords = wxGridNoCellCoords;
7616 }
7617 else
7618 {
7619 coords.Set( row, col );
7620 }
7621 }
7622
7623
7624 // Internal Helper function for computing row or column from some
7625 // (unscrolled) coordinate value, using either
7626 // m_defaultRowHeight/m_defaultColWidth or binary search on array
7627 // of m_rowBottoms/m_ColRights to speed up the search!
7628
7629 static int CoordToRowOrCol(int coord, int defaultDist, int minDist,
7630 const wxArrayInt& BorderArray, int nMax,
7631 bool clipToMinMax)
7632 {
7633
7634 if (coord < 0)
7635 return clipToMinMax && (nMax > 0) ? 0 : -1;
7636
7637
7638 if (!defaultDist)
7639 defaultDist = 1;
7640
7641 size_t i_max = coord / defaultDist,
7642 i_min = 0;
7643
7644 if (BorderArray.IsEmpty())
7645 {
7646 if((int) i_max < nMax)
7647 return i_max;
7648 return clipToMinMax ? nMax - 1 : -1;
7649 }
7650
7651 if ( i_max >= BorderArray.GetCount())
7652 i_max = BorderArray.GetCount() - 1;
7653 else
7654 {
7655 if ( coord >= BorderArray[i_max])
7656 {
7657 i_min = i_max;
7658 i_max = coord / minDist;
7659 }
7660 if ( i_max >= BorderArray.GetCount())
7661 i_max = BorderArray.GetCount() - 1;
7662 }
7663 if ( coord >= BorderArray[i_max])
7664 return clipToMinMax ? (int)i_max : -1;
7665 if ( coord < BorderArray[0] )
7666 return 0;
7667
7668 while ( i_max - i_min > 0 )
7669 {
7670 wxCHECK_MSG(BorderArray[i_min] <= coord && coord < BorderArray[i_max],
7671 0, _T("wxGrid: internal error in CoordToRowOrCol"));
7672 if (coord >= BorderArray[ i_max - 1])
7673 return i_max;
7674 else
7675 i_max--;
7676 int median = i_min + (i_max - i_min + 1) / 2;
7677 if (coord < BorderArray[median])
7678 i_max = median;
7679 else
7680 i_min = median;
7681 }
7682 return i_max;
7683 }
7684
7685 int wxGrid::YToRow( int y )
7686 {
7687 return CoordToRowOrCol(y, m_defaultRowHeight,
7688 m_minAcceptableRowHeight, m_rowBottoms, m_numRows, FALSE);
7689 }
7690
7691
7692 int wxGrid::XToCol( int x )
7693 {
7694 return CoordToRowOrCol(x, m_defaultColWidth,
7695 m_minAcceptableColWidth, m_colRights, m_numCols, FALSE);
7696 }
7697
7698
7699 // return the row number that that the y coord is near the edge of, or
7700 // -1 if not near an edge
7701 //
7702 int wxGrid::YToEdgeOfRow( int y )
7703 {
7704 int i;
7705 i = internalYToRow(y);
7706
7707 if ( GetRowHeight(i) > WXGRID_LABEL_EDGE_ZONE )
7708 {
7709 // We know that we are in row i, test whether we are
7710 // close enough to lower or upper border, respectively.
7711 if ( abs(GetRowBottom(i) - y) < WXGRID_LABEL_EDGE_ZONE )
7712 return i;
7713 else if( i > 0 && y - GetRowTop(i) < WXGRID_LABEL_EDGE_ZONE )
7714 return i - 1;
7715 }
7716
7717 return -1;
7718 }
7719
7720
7721 // return the col number that that the x coord is near the edge of, or
7722 // -1 if not near an edge
7723 //
7724 int wxGrid::XToEdgeOfCol( int x )
7725 {
7726 int i;
7727 i = internalXToCol(x);
7728
7729 if ( GetColWidth(i) > WXGRID_LABEL_EDGE_ZONE )
7730 {
7731 // We know that we are in column i, test whether we are
7732 // close enough to right or left border, respectively.
7733 if ( abs(GetColRight(i) - x) < WXGRID_LABEL_EDGE_ZONE )
7734 return i;
7735 else if( i > 0 && x - GetColLeft(i) < WXGRID_LABEL_EDGE_ZONE )
7736 return i - 1;
7737 }
7738
7739 return -1;
7740 }
7741
7742
7743 wxRect wxGrid::CellToRect( int row, int col )
7744 {
7745 wxRect rect( -1, -1, -1, -1 );
7746
7747 if ( row >= 0 && row < m_numRows &&
7748 col >= 0 && col < m_numCols )
7749 {
7750 int i, cell_rows, cell_cols;
7751 rect.width = rect.height = 0;
7752 GetCellSize( row, col, &cell_rows, &cell_cols );
7753 // if negative then find multicell owner
7754 if (cell_rows < 0) row += cell_rows;
7755 if (cell_cols < 0) col += cell_cols;
7756 GetCellSize( row, col, &cell_rows, &cell_cols );
7757
7758 rect.x = GetColLeft(col);
7759 rect.y = GetRowTop(row);
7760 for (i=col; i<col+cell_cols; i++)
7761 rect.width += GetColWidth(i);
7762 for (i=row; i<row+cell_rows; i++)
7763 rect.height += GetRowHeight(i);
7764 }
7765
7766 // if grid lines are enabled, then the area of the cell is a bit smaller
7767 if (m_gridLinesEnabled) {
7768 rect.width -= 1;
7769 rect.height -= 1;
7770 }
7771 return rect;
7772 }
7773
7774
7775 bool wxGrid::IsVisible( int row, int col, bool wholeCellVisible )
7776 {
7777 // get the cell rectangle in logical coords
7778 //
7779 wxRect r( CellToRect( row, col ) );
7780
7781 // convert to device coords
7782 //
7783 int left, top, right, bottom;
7784 CalcScrolledPosition( r.GetLeft(), r.GetTop(), &left, &top );
7785 CalcScrolledPosition( r.GetRight(), r.GetBottom(), &right, &bottom );
7786
7787 // check against the client area of the grid window
7788 //
7789 int cw, ch;
7790 m_gridWin->GetClientSize( &cw, &ch );
7791
7792 if ( wholeCellVisible )
7793 {
7794 // is the cell wholly visible ?
7795 //
7796 return ( left >= 0 && right <= cw &&
7797 top >= 0 && bottom <= ch );
7798 }
7799 else
7800 {
7801 // is the cell partly visible ?
7802 //
7803 return ( ((left >=0 && left < cw) || (right > 0 && right <= cw)) &&
7804 ((top >=0 && top < ch) || (bottom > 0 && bottom <= ch)) );
7805 }
7806 }
7807
7808
7809 // make the specified cell location visible by doing a minimal amount
7810 // of scrolling
7811 //
7812 void wxGrid::MakeCellVisible( int row, int col )
7813 {
7814
7815 int i;
7816 int xpos = -1, ypos = -1;
7817
7818 if ( row >= 0 && row < m_numRows &&
7819 col >= 0 && col < m_numCols )
7820 {
7821 // get the cell rectangle in logical coords
7822 //
7823 wxRect r( CellToRect( row, col ) );
7824
7825 // convert to device coords
7826 //
7827 int left, top, right, bottom;
7828 CalcScrolledPosition( r.GetLeft(), r.GetTop(), &left, &top );
7829 CalcScrolledPosition( r.GetRight(), r.GetBottom(), &right, &bottom );
7830
7831 int cw, ch;
7832 m_gridWin->GetClientSize( &cw, &ch );
7833
7834 if ( top < 0 )
7835 {
7836 ypos = r.GetTop();
7837 }
7838 else if ( bottom > ch )
7839 {
7840 int h = r.GetHeight();
7841 ypos = r.GetTop();
7842 for ( i = row-1; i >= 0; i-- )
7843 {
7844 int rowHeight = GetRowHeight(i);
7845 if ( h + rowHeight > ch )
7846 break;
7847
7848 h += rowHeight;
7849 ypos -= rowHeight;
7850 }
7851
7852 // we divide it later by GRID_SCROLL_LINE, make sure that we don't
7853 // have rounding errors (this is important, because if we do, we
7854 // might not scroll at all and some cells won't be redrawn)
7855 //
7856 // Sometimes GRID_SCROLL_LINE/2 is not enough, so just add a full
7857 // scroll unit...
7858 ypos += GRID_SCROLL_LINE_Y;
7859 }
7860
7861 if ( left < 0 )
7862 {
7863 xpos = r.GetLeft();
7864 }
7865 else if ( right > cw )
7866 {
7867 // position the view so that the cell is on the right
7868 int x0, y0;
7869 CalcUnscrolledPosition(0, 0, &x0, &y0);
7870 xpos = x0 + (right - cw);
7871
7872 // see comment for ypos above
7873 xpos += GRID_SCROLL_LINE_X;
7874 }
7875
7876 if ( xpos != -1 || ypos != -1 )
7877 {
7878 if ( xpos != -1 )
7879 xpos /= GRID_SCROLL_LINE_X;
7880 if ( ypos != -1 )
7881 ypos /= GRID_SCROLL_LINE_Y;
7882 Scroll( xpos, ypos );
7883 AdjustScrollbars();
7884 }
7885 }
7886 }
7887
7888
7889 //
7890 // ------ Grid cursor movement functions
7891 //
7892
7893 bool wxGrid::MoveCursorUp( bool expandSelection )
7894 {
7895 if ( m_currentCellCoords != wxGridNoCellCoords &&
7896 m_currentCellCoords.GetRow() >= 0 )
7897 {
7898 if ( expandSelection)
7899 {
7900 if ( m_selectingKeyboard == wxGridNoCellCoords )
7901 m_selectingKeyboard = m_currentCellCoords;
7902 if ( m_selectingKeyboard.GetRow() > 0 )
7903 {
7904 m_selectingKeyboard.SetRow( m_selectingKeyboard.GetRow() - 1 );
7905 MakeCellVisible( m_selectingKeyboard.GetRow(),
7906 m_selectingKeyboard.GetCol() );
7907 HighlightBlock( m_currentCellCoords, m_selectingKeyboard );
7908 }
7909 }
7910 else if ( m_currentCellCoords.GetRow() > 0 )
7911 {
7912 ClearSelection();
7913 MakeCellVisible( m_currentCellCoords.GetRow() - 1,
7914 m_currentCellCoords.GetCol() );
7915 SetCurrentCell( m_currentCellCoords.GetRow() - 1,
7916 m_currentCellCoords.GetCol() );
7917 }
7918 else
7919 return FALSE;
7920 return TRUE;
7921 }
7922
7923 return FALSE;
7924 }
7925
7926
7927 bool wxGrid::MoveCursorDown( bool expandSelection )
7928 {
7929 if ( m_currentCellCoords != wxGridNoCellCoords &&
7930 m_currentCellCoords.GetRow() < m_numRows )
7931 {
7932 if ( expandSelection )
7933 {
7934 if ( m_selectingKeyboard == wxGridNoCellCoords )
7935 m_selectingKeyboard = m_currentCellCoords;
7936 if ( m_selectingKeyboard.GetRow() < m_numRows-1 )
7937 {
7938 m_selectingKeyboard.SetRow( m_selectingKeyboard.GetRow() + 1 );
7939 MakeCellVisible( m_selectingKeyboard.GetRow(),
7940 m_selectingKeyboard.GetCol() );
7941 HighlightBlock( m_currentCellCoords, m_selectingKeyboard );
7942 }
7943 }
7944 else if ( m_currentCellCoords.GetRow() < m_numRows - 1 )
7945 {
7946 ClearSelection();
7947 MakeCellVisible( m_currentCellCoords.GetRow() + 1,
7948 m_currentCellCoords.GetCol() );
7949 SetCurrentCell( m_currentCellCoords.GetRow() + 1,
7950 m_currentCellCoords.GetCol() );
7951 }
7952 else
7953 return FALSE;
7954 return TRUE;
7955 }
7956
7957 return FALSE;
7958 }
7959
7960
7961 bool wxGrid::MoveCursorLeft( bool expandSelection )
7962 {
7963 if ( m_currentCellCoords != wxGridNoCellCoords &&
7964 m_currentCellCoords.GetCol() >= 0 )
7965 {
7966 if ( expandSelection )
7967 {
7968 if ( m_selectingKeyboard == wxGridNoCellCoords )
7969 m_selectingKeyboard = m_currentCellCoords;
7970 if ( m_selectingKeyboard.GetCol() > 0 )
7971 {
7972 m_selectingKeyboard.SetCol( m_selectingKeyboard.GetCol() - 1 );
7973 MakeCellVisible( m_selectingKeyboard.GetRow(),
7974 m_selectingKeyboard.GetCol() );
7975 HighlightBlock( m_currentCellCoords, m_selectingKeyboard );
7976 }
7977 }
7978 else if ( m_currentCellCoords.GetCol() > 0 )
7979 {
7980 ClearSelection();
7981 MakeCellVisible( m_currentCellCoords.GetRow(),
7982 m_currentCellCoords.GetCol() - 1 );
7983 SetCurrentCell( m_currentCellCoords.GetRow(),
7984 m_currentCellCoords.GetCol() - 1 );
7985 }
7986 else
7987 return FALSE;
7988 return TRUE;
7989 }
7990
7991 return FALSE;
7992 }
7993
7994
7995 bool wxGrid::MoveCursorRight( bool expandSelection )
7996 {
7997 if ( m_currentCellCoords != wxGridNoCellCoords &&
7998 m_currentCellCoords.GetCol() < m_numCols )
7999 {
8000 if ( expandSelection )
8001 {
8002 if ( m_selectingKeyboard == wxGridNoCellCoords )
8003 m_selectingKeyboard = m_currentCellCoords;
8004 if ( m_selectingKeyboard.GetCol() < m_numCols - 1 )
8005 {
8006 m_selectingKeyboard.SetCol( m_selectingKeyboard.GetCol() + 1 );
8007 MakeCellVisible( m_selectingKeyboard.GetRow(),
8008 m_selectingKeyboard.GetCol() );
8009 HighlightBlock( m_currentCellCoords, m_selectingKeyboard );
8010 }
8011 }
8012 else if ( m_currentCellCoords.GetCol() < m_numCols - 1 )
8013 {
8014 ClearSelection();
8015 MakeCellVisible( m_currentCellCoords.GetRow(),
8016 m_currentCellCoords.GetCol() + 1 );
8017 SetCurrentCell( m_currentCellCoords.GetRow(),
8018 m_currentCellCoords.GetCol() + 1 );
8019 }
8020 else
8021 return FALSE;
8022 return TRUE;
8023 }
8024
8025 return FALSE;
8026 }
8027
8028
8029 bool wxGrid::MovePageUp()
8030 {
8031 if ( m_currentCellCoords == wxGridNoCellCoords ) return FALSE;
8032
8033 int row = m_currentCellCoords.GetRow();
8034 if ( row > 0 )
8035 {
8036 int cw, ch;
8037 m_gridWin->GetClientSize( &cw, &ch );
8038
8039 int y = GetRowTop(row);
8040 int newRow = internalYToRow( y - ch + 1 );
8041
8042 if ( newRow == row )
8043 {
8044 //row > 0 , so newrow can never be less than 0 here.
8045 newRow = row - 1;
8046 }
8047
8048 MakeCellVisible( newRow, m_currentCellCoords.GetCol() );
8049 SetCurrentCell( newRow, m_currentCellCoords.GetCol() );
8050
8051 return TRUE;
8052 }
8053
8054 return FALSE;
8055 }
8056
8057 bool wxGrid::MovePageDown()
8058 {
8059 if ( m_currentCellCoords == wxGridNoCellCoords ) return FALSE;
8060
8061 int row = m_currentCellCoords.GetRow();
8062 if ( (row+1) < m_numRows )
8063 {
8064 int cw, ch;
8065 m_gridWin->GetClientSize( &cw, &ch );
8066
8067 int y = GetRowTop(row);
8068 int newRow = internalYToRow( y + ch );
8069 if ( newRow == row )
8070 {
8071 // row < m_numRows , so newrow can't overflow here.
8072 newRow = row + 1;
8073 }
8074
8075 MakeCellVisible( newRow, m_currentCellCoords.GetCol() );
8076 SetCurrentCell( newRow, m_currentCellCoords.GetCol() );
8077
8078 return TRUE;
8079 }
8080
8081 return FALSE;
8082 }
8083
8084 bool wxGrid::MoveCursorUpBlock( bool expandSelection )
8085 {
8086 if ( m_table &&
8087 m_currentCellCoords != wxGridNoCellCoords &&
8088 m_currentCellCoords.GetRow() > 0 )
8089 {
8090 int row = m_currentCellCoords.GetRow();
8091 int col = m_currentCellCoords.GetCol();
8092
8093 if ( m_table->IsEmptyCell(row, col) )
8094 {
8095 // starting in an empty cell: find the next block of
8096 // non-empty cells
8097 //
8098 while ( row > 0 )
8099 {
8100 row-- ;
8101 if ( !(m_table->IsEmptyCell(row, col)) ) break;
8102 }
8103 }
8104 else if ( m_table->IsEmptyCell(row-1, col) )
8105 {
8106 // starting at the top of a block: find the next block
8107 //
8108 row--;
8109 while ( row > 0 )
8110 {
8111 row-- ;
8112 if ( !(m_table->IsEmptyCell(row, col)) ) break;
8113 }
8114 }
8115 else
8116 {
8117 // starting within a block: find the top of the block
8118 //
8119 while ( row > 0 )
8120 {
8121 row-- ;
8122 if ( m_table->IsEmptyCell(row, col) )
8123 {
8124 row++ ;
8125 break;
8126 }
8127 }
8128 }
8129
8130 MakeCellVisible( row, col );
8131 if ( expandSelection )
8132 {
8133 m_selectingKeyboard = wxGridCellCoords( row, col );
8134 HighlightBlock( m_currentCellCoords, m_selectingKeyboard );
8135 }
8136 else
8137 {
8138 ClearSelection();
8139 SetCurrentCell( row, col );
8140 }
8141 return TRUE;
8142 }
8143
8144 return FALSE;
8145 }
8146
8147 bool wxGrid::MoveCursorDownBlock( bool expandSelection )
8148 {
8149 if ( m_table &&
8150 m_currentCellCoords != wxGridNoCellCoords &&
8151 m_currentCellCoords.GetRow() < m_numRows-1 )
8152 {
8153 int row = m_currentCellCoords.GetRow();
8154 int col = m_currentCellCoords.GetCol();
8155
8156 if ( m_table->IsEmptyCell(row, col) )
8157 {
8158 // starting in an empty cell: find the next block of
8159 // non-empty cells
8160 //
8161 while ( row < m_numRows-1 )
8162 {
8163 row++ ;
8164 if ( !(m_table->IsEmptyCell(row, col)) ) break;
8165 }
8166 }
8167 else if ( m_table->IsEmptyCell(row+1, col) )
8168 {
8169 // starting at the bottom of a block: find the next block
8170 //
8171 row++;
8172 while ( row < m_numRows-1 )
8173 {
8174 row++ ;
8175 if ( !(m_table->IsEmptyCell(row, col)) ) break;
8176 }
8177 }
8178 else
8179 {
8180 // starting within a block: find the bottom of the block
8181 //
8182 while ( row < m_numRows-1 )
8183 {
8184 row++ ;
8185 if ( m_table->IsEmptyCell(row, col) )
8186 {
8187 row-- ;
8188 break;
8189 }
8190 }
8191 }
8192
8193 MakeCellVisible( row, col );
8194 if ( expandSelection )
8195 {
8196 m_selectingKeyboard = wxGridCellCoords( row, col );
8197 HighlightBlock( m_currentCellCoords, m_selectingKeyboard );
8198 }
8199 else
8200 {
8201 ClearSelection();
8202 SetCurrentCell( row, col );
8203 }
8204
8205 return TRUE;
8206 }
8207
8208 return FALSE;
8209 }
8210
8211 bool wxGrid::MoveCursorLeftBlock( bool expandSelection )
8212 {
8213 if ( m_table &&
8214 m_currentCellCoords != wxGridNoCellCoords &&
8215 m_currentCellCoords.GetCol() > 0 )
8216 {
8217 int row = m_currentCellCoords.GetRow();
8218 int col = m_currentCellCoords.GetCol();
8219
8220 if ( m_table->IsEmptyCell(row, col) )
8221 {
8222 // starting in an empty cell: find the next block of
8223 // non-empty cells
8224 //
8225 while ( col > 0 )
8226 {
8227 col-- ;
8228 if ( !(m_table->IsEmptyCell(row, col)) ) break;
8229 }
8230 }
8231 else if ( m_table->IsEmptyCell(row, col-1) )
8232 {
8233 // starting at the left of a block: find the next block
8234 //
8235 col--;
8236 while ( col > 0 )
8237 {
8238 col-- ;
8239 if ( !(m_table->IsEmptyCell(row, col)) ) break;
8240 }
8241 }
8242 else
8243 {
8244 // starting within a block: find the left of the block
8245 //
8246 while ( col > 0 )
8247 {
8248 col-- ;
8249 if ( m_table->IsEmptyCell(row, col) )
8250 {
8251 col++ ;
8252 break;
8253 }
8254 }
8255 }
8256
8257 MakeCellVisible( row, col );
8258 if ( expandSelection )
8259 {
8260 m_selectingKeyboard = wxGridCellCoords( row, col );
8261 HighlightBlock( m_currentCellCoords, m_selectingKeyboard );
8262 }
8263 else
8264 {
8265 ClearSelection();
8266 SetCurrentCell( row, col );
8267 }
8268
8269 return TRUE;
8270 }
8271
8272 return FALSE;
8273 }
8274
8275 bool wxGrid::MoveCursorRightBlock( bool expandSelection )
8276 {
8277 if ( m_table &&
8278 m_currentCellCoords != wxGridNoCellCoords &&
8279 m_currentCellCoords.GetCol() < m_numCols-1 )
8280 {
8281 int row = m_currentCellCoords.GetRow();
8282 int col = m_currentCellCoords.GetCol();
8283
8284 if ( m_table->IsEmptyCell(row, col) )
8285 {
8286 // starting in an empty cell: find the next block of
8287 // non-empty cells
8288 //
8289 while ( col < m_numCols-1 )
8290 {
8291 col++ ;
8292 if ( !(m_table->IsEmptyCell(row, col)) ) break;
8293 }
8294 }
8295 else if ( m_table->IsEmptyCell(row, col+1) )
8296 {
8297 // starting at the right of a block: find the next block
8298 //
8299 col++;
8300 while ( col < m_numCols-1 )
8301 {
8302 col++ ;
8303 if ( !(m_table->IsEmptyCell(row, col)) ) break;
8304 }
8305 }
8306 else
8307 {
8308 // starting within a block: find the right of the block
8309 //
8310 while ( col < m_numCols-1 )
8311 {
8312 col++ ;
8313 if ( m_table->IsEmptyCell(row, col) )
8314 {
8315 col-- ;
8316 break;
8317 }
8318 }
8319 }
8320
8321 MakeCellVisible( row, col );
8322 if ( expandSelection )
8323 {
8324 m_selectingKeyboard = wxGridCellCoords( row, col );
8325 HighlightBlock( m_currentCellCoords, m_selectingKeyboard );
8326 }
8327 else
8328 {
8329 ClearSelection();
8330 SetCurrentCell( row, col );
8331 }
8332
8333 return TRUE;
8334 }
8335
8336 return FALSE;
8337 }
8338
8339
8340
8341 //
8342 // ------ Label values and formatting
8343 //
8344
8345 void wxGrid::GetRowLabelAlignment( int *horiz, int *vert )
8346 {
8347 *horiz = m_rowLabelHorizAlign;
8348 *vert = m_rowLabelVertAlign;
8349 }
8350
8351 void wxGrid::GetColLabelAlignment( int *horiz, int *vert )
8352 {
8353 *horiz = m_colLabelHorizAlign;
8354 *vert = m_colLabelVertAlign;
8355 }
8356
8357 int wxGrid::GetColLabelTextOrientation()
8358 {
8359 return m_colLabelTextOrientation;
8360 }
8361
8362 wxString wxGrid::GetRowLabelValue( int row )
8363 {
8364 if ( m_table )
8365 {
8366 return m_table->GetRowLabelValue( row );
8367 }
8368 else
8369 {
8370 wxString s;
8371 s << row;
8372 return s;
8373 }
8374 }
8375
8376 wxString wxGrid::GetColLabelValue( int col )
8377 {
8378 if ( m_table )
8379 {
8380 return m_table->GetColLabelValue( col );
8381 }
8382 else
8383 {
8384 wxString s;
8385 s << col;
8386 return s;
8387 }
8388 }
8389
8390
8391 void wxGrid::SetRowLabelSize( int width )
8392 {
8393 width = wxMax( width, 0 );
8394 if ( width != m_rowLabelWidth )
8395 {
8396 if ( width == 0 )
8397 {
8398 m_rowLabelWin->Show( FALSE );
8399 m_cornerLabelWin->Show( FALSE );
8400 }
8401 else if ( m_rowLabelWidth == 0 )
8402 {
8403 m_rowLabelWin->Show( TRUE );
8404 if ( m_colLabelHeight > 0 ) m_cornerLabelWin->Show( TRUE );
8405 }
8406
8407 m_rowLabelWidth = width;
8408 CalcWindowSizes();
8409 wxScrolledWindow::Refresh( TRUE );
8410 }
8411 }
8412
8413
8414 void wxGrid::SetColLabelSize( int height )
8415 {
8416 height = wxMax( height, 0 );
8417 if ( height != m_colLabelHeight )
8418 {
8419 if ( height == 0 )
8420 {
8421 m_colLabelWin->Show( FALSE );
8422 m_cornerLabelWin->Show( FALSE );
8423 }
8424 else if ( m_colLabelHeight == 0 )
8425 {
8426 m_colLabelWin->Show( TRUE );
8427 if ( m_rowLabelWidth > 0 ) m_cornerLabelWin->Show( TRUE );
8428 }
8429
8430 m_colLabelHeight = height;
8431 CalcWindowSizes();
8432 wxScrolledWindow::Refresh( TRUE );
8433 }
8434 }
8435
8436
8437 void wxGrid::SetLabelBackgroundColour( const wxColour& colour )
8438 {
8439 if ( m_labelBackgroundColour != colour )
8440 {
8441 m_labelBackgroundColour = colour;
8442 m_rowLabelWin->SetBackgroundColour( colour );
8443 m_colLabelWin->SetBackgroundColour( colour );
8444 m_cornerLabelWin->SetBackgroundColour( colour );
8445
8446 if ( !GetBatchCount() )
8447 {
8448 m_rowLabelWin->Refresh();
8449 m_colLabelWin->Refresh();
8450 m_cornerLabelWin->Refresh();
8451 }
8452 }
8453 }
8454
8455 void wxGrid::SetLabelTextColour( const wxColour& colour )
8456 {
8457 if ( m_labelTextColour != colour )
8458 {
8459 m_labelTextColour = colour;
8460 if ( !GetBatchCount() )
8461 {
8462 m_rowLabelWin->Refresh();
8463 m_colLabelWin->Refresh();
8464 }
8465 }
8466 }
8467
8468 void wxGrid::SetLabelFont( const wxFont& font )
8469 {
8470 m_labelFont = font;
8471 if ( !GetBatchCount() )
8472 {
8473 m_rowLabelWin->Refresh();
8474 m_colLabelWin->Refresh();
8475 }
8476 }
8477
8478 void wxGrid::SetRowLabelAlignment( int horiz, int vert )
8479 {
8480 // allow old (incorrect) defs to be used
8481 switch ( horiz )
8482 {
8483 case wxLEFT: horiz = wxALIGN_LEFT; break;
8484 case wxRIGHT: horiz = wxALIGN_RIGHT; break;
8485 case wxCENTRE: horiz = wxALIGN_CENTRE; break;
8486 }
8487
8488 switch ( vert )
8489 {
8490 case wxTOP: vert = wxALIGN_TOP; break;
8491 case wxBOTTOM: vert = wxALIGN_BOTTOM; break;
8492 case wxCENTRE: vert = wxALIGN_CENTRE; break;
8493 }
8494
8495 if ( horiz == wxALIGN_LEFT || horiz == wxALIGN_CENTRE || horiz == wxALIGN_RIGHT )
8496 {
8497 m_rowLabelHorizAlign = horiz;
8498 }
8499
8500 if ( vert == wxALIGN_TOP || vert == wxALIGN_CENTRE || vert == wxALIGN_BOTTOM )
8501 {
8502 m_rowLabelVertAlign = vert;
8503 }
8504
8505 if ( !GetBatchCount() )
8506 {
8507 m_rowLabelWin->Refresh();
8508 }
8509 }
8510
8511 void wxGrid::SetColLabelAlignment( int horiz, int vert )
8512 {
8513 // allow old (incorrect) defs to be used
8514 switch ( horiz )
8515 {
8516 case wxLEFT: horiz = wxALIGN_LEFT; break;
8517 case wxRIGHT: horiz = wxALIGN_RIGHT; break;
8518 case wxCENTRE: horiz = wxALIGN_CENTRE; break;
8519 }
8520
8521 switch ( vert )
8522 {
8523 case wxTOP: vert = wxALIGN_TOP; break;
8524 case wxBOTTOM: vert = wxALIGN_BOTTOM; break;
8525 case wxCENTRE: vert = wxALIGN_CENTRE; break;
8526 }
8527
8528 if ( horiz == wxALIGN_LEFT || horiz == wxALIGN_CENTRE || horiz == wxALIGN_RIGHT )
8529 {
8530 m_colLabelHorizAlign = horiz;
8531 }
8532
8533 if ( vert == wxALIGN_TOP || vert == wxALIGN_CENTRE || vert == wxALIGN_BOTTOM )
8534 {
8535 m_colLabelVertAlign = vert;
8536 }
8537
8538 if ( !GetBatchCount() )
8539 {
8540 m_colLabelWin->Refresh();
8541 }
8542 }
8543
8544 // Note: under MSW, the default column label font must be changed because it
8545 // does not support vertical printing
8546 //
8547 // Example: wxFont font(9, wxSWISS, wxNORMAL, wxBOLD);
8548 // pGrid->SetLabelFont(font);
8549 // pGrid->SetColLabelTextOrientation(wxVERTICAL);
8550 //
8551 void wxGrid::SetColLabelTextOrientation( int textOrientation )
8552 {
8553 if( textOrientation == wxHORIZONTAL || textOrientation == wxVERTICAL )
8554 {
8555 m_colLabelTextOrientation = textOrientation;
8556 }
8557
8558 if ( !GetBatchCount() )
8559 {
8560 m_colLabelWin->Refresh();
8561 }
8562 }
8563
8564 void wxGrid::SetRowLabelValue( int row, const wxString& s )
8565 {
8566 if ( m_table )
8567 {
8568 m_table->SetRowLabelValue( row, s );
8569 if ( !GetBatchCount() )
8570 {
8571 wxRect rect = CellToRect( row, 0);
8572 if ( rect.height > 0 )
8573 {
8574 CalcScrolledPosition(0, rect.y, &rect.x, &rect.y);
8575 rect.x = 0;
8576 rect.width = m_rowLabelWidth;
8577 m_rowLabelWin->Refresh( TRUE, &rect );
8578 }
8579 }
8580 }
8581 }
8582
8583 void wxGrid::SetColLabelValue( int col, const wxString& s )
8584 {
8585 if ( m_table )
8586 {
8587 m_table->SetColLabelValue( col, s );
8588 if ( !GetBatchCount() )
8589 {
8590 wxRect rect = CellToRect( 0, col );
8591 if ( rect.width > 0 )
8592 {
8593 CalcScrolledPosition(rect.x, 0, &rect.x, &rect.y);
8594 rect.y = 0;
8595 rect.height = m_colLabelHeight;
8596 m_colLabelWin->Refresh( TRUE, &rect );
8597 }
8598 }
8599 }
8600 }
8601
8602 void wxGrid::SetGridLineColour( const wxColour& colour )
8603 {
8604 if ( m_gridLineColour != colour )
8605 {
8606 m_gridLineColour = colour;
8607
8608 wxClientDC dc( m_gridWin );
8609 PrepareDC( dc );
8610 DrawAllGridLines( dc, wxRegion() );
8611 }
8612 }
8613
8614
8615 void wxGrid::SetCellHighlightColour( const wxColour& colour )
8616 {
8617 if ( m_cellHighlightColour != colour )
8618 {
8619 m_cellHighlightColour = colour;
8620
8621 wxClientDC dc( m_gridWin );
8622 PrepareDC( dc );
8623 wxGridCellAttr* attr = GetCellAttr(m_currentCellCoords);
8624 DrawCellHighlight(dc, attr);
8625 attr->DecRef();
8626 }
8627 }
8628
8629 void wxGrid::SetCellHighlightPenWidth(int width)
8630 {
8631 if (m_cellHighlightPenWidth != width) {
8632 m_cellHighlightPenWidth = width;
8633
8634 // Just redrawing the cell highlight is not enough since that won't
8635 // make any visible change if the the thickness is getting smaller.
8636 int row = m_currentCellCoords.GetRow();
8637 int col = m_currentCellCoords.GetCol();
8638 if ( GetColWidth(col) <= 0 || GetRowHeight(row) <= 0 )
8639 return;
8640 wxRect rect = CellToRect(row, col);
8641 m_gridWin->Refresh(TRUE, &rect);
8642 }
8643 }
8644
8645 void wxGrid::SetCellHighlightROPenWidth(int width)
8646 {
8647 if (m_cellHighlightROPenWidth != width) {
8648 m_cellHighlightROPenWidth = width;
8649
8650 // Just redrawing the cell highlight is not enough since that won't
8651 // make any visible change if the the thickness is getting smaller.
8652 int row = m_currentCellCoords.GetRow();
8653 int col = m_currentCellCoords.GetCol();
8654 if ( GetColWidth(col) <= 0 || GetRowHeight(row) <= 0 )
8655 return;
8656 wxRect rect = CellToRect(row, col);
8657 m_gridWin->Refresh(TRUE, &rect);
8658 }
8659 }
8660
8661 void wxGrid::EnableGridLines( bool enable )
8662 {
8663 if ( enable != m_gridLinesEnabled )
8664 {
8665 m_gridLinesEnabled = enable;
8666
8667 if ( !GetBatchCount() )
8668 {
8669 if ( enable )
8670 {
8671 wxClientDC dc( m_gridWin );
8672 PrepareDC( dc );
8673 DrawAllGridLines( dc, wxRegion() );
8674 }
8675 else
8676 {
8677 m_gridWin->Refresh();
8678 }
8679 }
8680 }
8681 }
8682
8683
8684 int wxGrid::GetDefaultRowSize()
8685 {
8686 return m_defaultRowHeight;
8687 }
8688
8689 int wxGrid::GetRowSize( int row )
8690 {
8691 wxCHECK_MSG( row >= 0 && row < m_numRows, 0, _T("invalid row index") );
8692
8693 return GetRowHeight(row);
8694 }
8695
8696 int wxGrid::GetDefaultColSize()
8697 {
8698 return m_defaultColWidth;
8699 }
8700
8701 int wxGrid::GetColSize( int col )
8702 {
8703 wxCHECK_MSG( col >= 0 && col < m_numCols, 0, _T("invalid column index") );
8704
8705 return GetColWidth(col);
8706 }
8707
8708 // ============================================================================
8709 // access to the grid attributes: each of them has a default value in the grid
8710 // itself and may be overidden on a per-cell basis
8711 // ============================================================================
8712
8713 // ----------------------------------------------------------------------------
8714 // setting default attributes
8715 // ----------------------------------------------------------------------------
8716
8717 void wxGrid::SetDefaultCellBackgroundColour( const wxColour& col )
8718 {
8719 m_defaultCellAttr->SetBackgroundColour(col);
8720 #ifdef __WXGTK__
8721 m_gridWin->SetBackgroundColour(col);
8722 #endif
8723 }
8724
8725 void wxGrid::SetDefaultCellTextColour( const wxColour& col )
8726 {
8727 m_defaultCellAttr->SetTextColour(col);
8728 }
8729
8730 void wxGrid::SetDefaultCellAlignment( int horiz, int vert )
8731 {
8732 m_defaultCellAttr->SetAlignment(horiz, vert);
8733 }
8734
8735 void wxGrid::SetDefaultCellOverflow( bool allow )
8736 {
8737 m_defaultCellAttr->SetOverflow(allow);
8738 }
8739
8740 void wxGrid::SetDefaultCellFont( const wxFont& font )
8741 {
8742 m_defaultCellAttr->SetFont(font);
8743 }
8744
8745 void wxGrid::SetDefaultRenderer(wxGridCellRenderer *renderer)
8746 {
8747 m_defaultCellAttr->SetRenderer(renderer);
8748 }
8749
8750 void wxGrid::SetDefaultEditor(wxGridCellEditor *editor)
8751 {
8752 m_defaultCellAttr->SetEditor(editor);
8753 }
8754
8755 // ----------------------------------------------------------------------------
8756 // access to the default attrbiutes
8757 // ----------------------------------------------------------------------------
8758
8759 wxColour wxGrid::GetDefaultCellBackgroundColour()
8760 {
8761 return m_defaultCellAttr->GetBackgroundColour();
8762 }
8763
8764 wxColour wxGrid::GetDefaultCellTextColour()
8765 {
8766 return m_defaultCellAttr->GetTextColour();
8767 }
8768
8769 wxFont wxGrid::GetDefaultCellFont()
8770 {
8771 return m_defaultCellAttr->GetFont();
8772 }
8773
8774 void wxGrid::GetDefaultCellAlignment( int *horiz, int *vert )
8775 {
8776 m_defaultCellAttr->GetAlignment(horiz, vert);
8777 }
8778
8779 bool wxGrid::GetDefaultCellOverflow()
8780 {
8781 return m_defaultCellAttr->GetOverflow();
8782 }
8783
8784 wxGridCellRenderer *wxGrid::GetDefaultRenderer() const
8785 {
8786 return m_defaultCellAttr->GetRenderer(NULL, 0, 0);
8787 }
8788
8789 wxGridCellEditor *wxGrid::GetDefaultEditor() const
8790 {
8791 return m_defaultCellAttr->GetEditor(NULL,0,0);
8792 }
8793
8794 // ----------------------------------------------------------------------------
8795 // access to cell attributes
8796 // ----------------------------------------------------------------------------
8797
8798 wxColour wxGrid::GetCellBackgroundColour(int row, int col)
8799 {
8800 wxGridCellAttr *attr = GetCellAttr(row, col);
8801 wxColour colour = attr->GetBackgroundColour();
8802 attr->DecRef();
8803 return colour;
8804 }
8805
8806 wxColour wxGrid::GetCellTextColour( int row, int col )
8807 {
8808 wxGridCellAttr *attr = GetCellAttr(row, col);
8809 wxColour colour = attr->GetTextColour();
8810 attr->DecRef();
8811 return colour;
8812 }
8813
8814 wxFont wxGrid::GetCellFont( int row, int col )
8815 {
8816 wxGridCellAttr *attr = GetCellAttr(row, col);
8817 wxFont font = attr->GetFont();
8818 attr->DecRef();
8819 return font;
8820 }
8821
8822 void wxGrid::GetCellAlignment( int row, int col, int *horiz, int *vert )
8823 {
8824 wxGridCellAttr *attr = GetCellAttr(row, col);
8825 attr->GetAlignment(horiz, vert);
8826 attr->DecRef();
8827 }
8828
8829 bool wxGrid::GetCellOverflow( int row, int col )
8830 {
8831 wxGridCellAttr *attr = GetCellAttr(row, col);
8832 bool allow = attr->GetOverflow();
8833 attr->DecRef();
8834 return allow;
8835 }
8836
8837 void wxGrid::GetCellSize( int row, int col, int *num_rows, int *num_cols )
8838 {
8839 wxGridCellAttr *attr = GetCellAttr(row, col);
8840 attr->GetSize( num_rows, num_cols );
8841 attr->DecRef();
8842 }
8843
8844 wxGridCellRenderer* wxGrid::GetCellRenderer(int row, int col)
8845 {
8846 wxGridCellAttr* attr = GetCellAttr(row, col);
8847 wxGridCellRenderer* renderer = attr->GetRenderer(this, row, col);
8848 attr->DecRef();
8849
8850 return renderer;
8851 }
8852
8853 wxGridCellEditor* wxGrid::GetCellEditor(int row, int col)
8854 {
8855 wxGridCellAttr* attr = GetCellAttr(row, col);
8856 wxGridCellEditor* editor = attr->GetEditor(this, row, col);
8857 attr->DecRef();
8858
8859 return editor;
8860 }
8861
8862 bool wxGrid::IsReadOnly(int row, int col) const
8863 {
8864 wxGridCellAttr* attr = GetCellAttr(row, col);
8865 bool isReadOnly = attr->IsReadOnly();
8866 attr->DecRef();
8867 return isReadOnly;
8868 }
8869
8870 // ----------------------------------------------------------------------------
8871 // attribute support: cache, automatic provider creation, ...
8872 // ----------------------------------------------------------------------------
8873
8874 bool wxGrid::CanHaveAttributes()
8875 {
8876 if ( !m_table )
8877 {
8878 return FALSE;
8879 }
8880
8881 return m_table->CanHaveAttributes();
8882 }
8883
8884 void wxGrid::ClearAttrCache()
8885 {
8886 if ( m_attrCache.row != -1 )
8887 {
8888 wxSafeDecRef(m_attrCache.attr);
8889 m_attrCache.attr = NULL;
8890 m_attrCache.row = -1;
8891 }
8892 }
8893
8894 void wxGrid::CacheAttr(int row, int col, wxGridCellAttr *attr) const
8895 {
8896 if ( attr != NULL )
8897 {
8898 wxGrid *self = (wxGrid *)this; // const_cast
8899
8900 self->ClearAttrCache();
8901 self->m_attrCache.row = row;
8902 self->m_attrCache.col = col;
8903 self->m_attrCache.attr = attr;
8904 wxSafeIncRef(attr);
8905 }
8906 }
8907
8908 bool wxGrid::LookupAttr(int row, int col, wxGridCellAttr **attr) const
8909 {
8910 if ( row == m_attrCache.row && col == m_attrCache.col )
8911 {
8912 *attr = m_attrCache.attr;
8913 wxSafeIncRef(m_attrCache.attr);
8914
8915 #ifdef DEBUG_ATTR_CACHE
8916 gs_nAttrCacheHits++;
8917 #endif
8918
8919 return TRUE;
8920 }
8921 else
8922 {
8923 #ifdef DEBUG_ATTR_CACHE
8924 gs_nAttrCacheMisses++;
8925 #endif
8926 return FALSE;
8927 }
8928 }
8929
8930 wxGridCellAttr *wxGrid::GetCellAttr(int row, int col) const
8931 {
8932 wxGridCellAttr *attr = NULL;
8933 // Additional test to avoid looking at the cache e.g. for
8934 // wxNoCellCoords, as this will confuse memory management.
8935 if ( row >= 0 )
8936 {
8937 if ( !LookupAttr(row, col, &attr) )
8938 {
8939 attr = m_table ? m_table->GetAttr(row, col , wxGridCellAttr::Any)
8940 : (wxGridCellAttr *)NULL;
8941 CacheAttr(row, col, attr);
8942 }
8943 }
8944 if (attr)
8945 {
8946 attr->SetDefAttr(m_defaultCellAttr);
8947 }
8948 else
8949 {
8950 attr = m_defaultCellAttr;
8951 attr->IncRef();
8952 }
8953
8954 return attr;
8955 }
8956
8957 wxGridCellAttr *wxGrid::GetOrCreateCellAttr(int row, int col) const
8958 {
8959 wxGridCellAttr *attr = (wxGridCellAttr *)NULL;
8960
8961 wxCHECK_MSG( m_table, attr,
8962 _T("we may only be called if CanHaveAttributes() returned TRUE and then m_table should be !NULL") );
8963
8964 attr = m_table->GetAttr(row, col, wxGridCellAttr::Cell);
8965 if ( !attr )
8966 {
8967 attr = new wxGridCellAttr(m_defaultCellAttr);
8968
8969 // artificially inc the ref count to match DecRef() in caller
8970 attr->IncRef();
8971 m_table->SetAttr(attr, row, col);
8972 }
8973
8974 return attr;
8975 }
8976
8977 // ----------------------------------------------------------------------------
8978 // setting column attributes (wrappers around SetColAttr)
8979 // ----------------------------------------------------------------------------
8980
8981 void wxGrid::SetColFormatBool(int col)
8982 {
8983 SetColFormatCustom(col, wxGRID_VALUE_BOOL);
8984 }
8985
8986 void wxGrid::SetColFormatNumber(int col)
8987 {
8988 SetColFormatCustom(col, wxGRID_VALUE_NUMBER);
8989 }
8990
8991 void wxGrid::SetColFormatFloat(int col, int width, int precision)
8992 {
8993 wxString typeName = wxGRID_VALUE_FLOAT;
8994 if ( (width != -1) || (precision != -1) )
8995 {
8996 typeName << _T(':') << width << _T(',') << precision;
8997 }
8998
8999 SetColFormatCustom(col, typeName);
9000 }
9001
9002 void wxGrid::SetColFormatCustom(int col, const wxString& typeName)
9003 {
9004 wxGridCellAttr *attr = (wxGridCellAttr *)NULL;
9005
9006 attr = m_table->GetAttr(-1, col, wxGridCellAttr::Col );
9007 if(!attr)
9008 attr = new wxGridCellAttr;
9009 wxGridCellRenderer *renderer = GetDefaultRendererForType(typeName);
9010 attr->SetRenderer(renderer);
9011
9012 SetColAttr(col, attr);
9013
9014 }
9015
9016 // ----------------------------------------------------------------------------
9017 // setting cell attributes: this is forwarded to the table
9018 // ----------------------------------------------------------------------------
9019
9020 void wxGrid::SetAttr(int row, int col, wxGridCellAttr *attr)
9021 {
9022 if ( CanHaveAttributes() )
9023 {
9024 m_table->SetAttr(attr, row, col);
9025 ClearAttrCache();
9026 }
9027 else
9028 {
9029 wxSafeDecRef(attr);
9030 }
9031 }
9032
9033 void wxGrid::SetRowAttr(int row, wxGridCellAttr *attr)
9034 {
9035 if ( CanHaveAttributes() )
9036 {
9037 m_table->SetRowAttr(attr, row);
9038 ClearAttrCache();
9039 }
9040 else
9041 {
9042 wxSafeDecRef(attr);
9043 }
9044 }
9045
9046 void wxGrid::SetColAttr(int col, wxGridCellAttr *attr)
9047 {
9048 if ( CanHaveAttributes() )
9049 {
9050 m_table->SetColAttr(attr, col);
9051 ClearAttrCache();
9052 }
9053 else
9054 {
9055 wxSafeDecRef(attr);
9056 }
9057 }
9058
9059 void wxGrid::SetCellBackgroundColour( int row, int col, const wxColour& colour )
9060 {
9061 if ( CanHaveAttributes() )
9062 {
9063 wxGridCellAttr *attr = GetOrCreateCellAttr(row, col);
9064 attr->SetBackgroundColour(colour);
9065 attr->DecRef();
9066 }
9067 }
9068
9069 void wxGrid::SetCellTextColour( int row, int col, const wxColour& colour )
9070 {
9071 if ( CanHaveAttributes() )
9072 {
9073 wxGridCellAttr *attr = GetOrCreateCellAttr(row, col);
9074 attr->SetTextColour(colour);
9075 attr->DecRef();
9076 }
9077 }
9078
9079 void wxGrid::SetCellFont( int row, int col, const wxFont& font )
9080 {
9081 if ( CanHaveAttributes() )
9082 {
9083 wxGridCellAttr *attr = GetOrCreateCellAttr(row, col);
9084 attr->SetFont(font);
9085 attr->DecRef();
9086 }
9087 }
9088
9089 void wxGrid::SetCellAlignment( int row, int col, int horiz, int vert )
9090 {
9091 if ( CanHaveAttributes() )
9092 {
9093 wxGridCellAttr *attr = GetOrCreateCellAttr(row, col);
9094 attr->SetAlignment(horiz, vert);
9095 attr->DecRef();
9096 }
9097 }
9098
9099 void wxGrid::SetCellOverflow( int row, int col, bool allow )
9100 {
9101 if ( CanHaveAttributes() )
9102 {
9103 wxGridCellAttr *attr = GetOrCreateCellAttr(row, col);
9104 attr->SetOverflow(allow);
9105 attr->DecRef();
9106 }
9107 }
9108
9109 void wxGrid::SetCellSize( int row, int col, int num_rows, int num_cols )
9110 {
9111 if ( CanHaveAttributes() )
9112 {
9113 int cell_rows, cell_cols;
9114
9115 wxGridCellAttr *attr = GetOrCreateCellAttr(row, col);
9116 attr->GetSize(&cell_rows, &cell_cols);
9117 attr->SetSize(num_rows, num_cols);
9118 attr->DecRef();
9119
9120 // Cannot set the size of a cell to 0 or negative values
9121 // While it is perfectly legal to do that, this function cannot
9122 // handle all the possibilies, do it by hand by getting the CellAttr.
9123 // You can only set the size of a cell to 1,1 or greater with this fn
9124 wxASSERT_MSG( !((cell_rows < 1) || (cell_cols < 1)),
9125 wxT("wxGrid::SetCellSize setting cell size that is already part of another cell"));
9126 wxASSERT_MSG( !((num_rows < 1) || (num_cols < 1)),
9127 wxT("wxGrid::SetCellSize setting cell size to < 1"));
9128
9129 // if this was already a multicell then "turn off" the other cells first
9130 if ((cell_rows > 1) || (cell_rows > 1))
9131 {
9132 int i, j;
9133 for (j=row; j<row+cell_rows; j++)
9134 {
9135 for (i=col; i<col+cell_cols; i++)
9136 {
9137 if ((i != col) || (j != row))
9138 {
9139 wxGridCellAttr *attr_stub = GetOrCreateCellAttr(j, i);
9140 attr_stub->SetSize( 1, 1 );
9141 attr_stub->DecRef();
9142 }
9143 }
9144 }
9145 }
9146
9147 // mark the cells that will be covered by this cell to
9148 // negative or zero values to point back at this cell
9149 if (((num_rows > 1) || (num_cols > 1)) && (num_rows >= 1) && (num_cols >= 1))
9150 {
9151 int i, j;
9152 for (j=row; j<row+num_rows; j++)
9153 {
9154 for (i=col; i<col+num_cols; i++)
9155 {
9156 if ((i != col) || (j != row))
9157 {
9158 wxGridCellAttr *attr_stub = GetOrCreateCellAttr(j, i);
9159 attr_stub->SetSize( row-j, col-i );
9160 attr_stub->DecRef();
9161 }
9162 }
9163 }
9164 }
9165 }
9166 }
9167
9168 void wxGrid::SetCellRenderer(int row, int col, wxGridCellRenderer *renderer)
9169 {
9170 if ( CanHaveAttributes() )
9171 {
9172 wxGridCellAttr *attr = GetOrCreateCellAttr(row, col);
9173 attr->SetRenderer(renderer);
9174 attr->DecRef();
9175 }
9176 }
9177
9178 void wxGrid::SetCellEditor(int row, int col, wxGridCellEditor* editor)
9179 {
9180 if ( CanHaveAttributes() )
9181 {
9182 wxGridCellAttr *attr = GetOrCreateCellAttr(row, col);
9183 attr->SetEditor(editor);
9184 attr->DecRef();
9185 }
9186 }
9187
9188 void wxGrid::SetReadOnly(int row, int col, bool isReadOnly)
9189 {
9190 if ( CanHaveAttributes() )
9191 {
9192 wxGridCellAttr *attr = GetOrCreateCellAttr(row, col);
9193 attr->SetReadOnly(isReadOnly);
9194 attr->DecRef();
9195 }
9196 }
9197
9198 // ----------------------------------------------------------------------------
9199 // Data type registration
9200 // ----------------------------------------------------------------------------
9201
9202 void wxGrid::RegisterDataType(const wxString& typeName,
9203 wxGridCellRenderer* renderer,
9204 wxGridCellEditor* editor)
9205 {
9206 m_typeRegistry->RegisterDataType(typeName, renderer, editor);
9207 }
9208
9209
9210 wxGridCellEditor* wxGrid::GetDefaultEditorForCell(int row, int col) const
9211 {
9212 wxString typeName = m_table->GetTypeName(row, col);
9213 return GetDefaultEditorForType(typeName);
9214 }
9215
9216 wxGridCellRenderer* wxGrid::GetDefaultRendererForCell(int row, int col) const
9217 {
9218 wxString typeName = m_table->GetTypeName(row, col);
9219 return GetDefaultRendererForType(typeName);
9220 }
9221
9222 wxGridCellEditor*
9223 wxGrid::GetDefaultEditorForType(const wxString& typeName) const
9224 {
9225 int index = m_typeRegistry->FindOrCloneDataType(typeName);
9226 if ( index == wxNOT_FOUND )
9227 {
9228 wxFAIL_MSG(wxT("Unknown data type name"));
9229
9230 return NULL;
9231 }
9232
9233 return m_typeRegistry->GetEditor(index);
9234 }
9235
9236 wxGridCellRenderer*
9237 wxGrid::GetDefaultRendererForType(const wxString& typeName) const
9238 {
9239 int index = m_typeRegistry->FindOrCloneDataType(typeName);
9240 if ( index == wxNOT_FOUND )
9241 {
9242 wxFAIL_MSG(wxT("Unknown data type name"));
9243
9244 return NULL;
9245 }
9246
9247 return m_typeRegistry->GetRenderer(index);
9248 }
9249
9250
9251 // ----------------------------------------------------------------------------
9252 // row/col size
9253 // ----------------------------------------------------------------------------
9254
9255 void wxGrid::EnableDragRowSize( bool enable )
9256 {
9257 m_canDragRowSize = enable;
9258 }
9259
9260
9261 void wxGrid::EnableDragColSize( bool enable )
9262 {
9263 m_canDragColSize = enable;
9264 }
9265
9266 void wxGrid::EnableDragGridSize( bool enable )
9267 {
9268 m_canDragGridSize = enable;
9269 }
9270
9271
9272 void wxGrid::SetDefaultRowSize( int height, bool resizeExistingRows )
9273 {
9274 m_defaultRowHeight = wxMax( height, m_minAcceptableRowHeight );
9275
9276 if ( resizeExistingRows )
9277 {
9278 // since we are resizing all rows to the default row size,
9279 // we can simply clear the row heights and row bottoms
9280 // arrays (which also allows us to take advantage of
9281 // some speed optimisations)
9282 m_rowHeights.Empty();
9283 m_rowBottoms.Empty();
9284 if ( !GetBatchCount() )
9285 CalcDimensions();
9286 }
9287 }
9288
9289 void wxGrid::SetRowSize( int row, int height )
9290 {
9291 wxCHECK_RET( row >= 0 && row < m_numRows, _T("invalid row index") );
9292
9293 // See comment in SetColSize
9294 if ( height < GetRowMinimalAcceptableHeight()) { return; }
9295
9296 if ( m_rowHeights.IsEmpty() )
9297 {
9298 // need to really create the array
9299 InitRowHeights();
9300 }
9301
9302 int h = wxMax( 0, height );
9303 int diff = h - m_rowHeights[row];
9304
9305 m_rowHeights[row] = h;
9306 int i;
9307 for ( i = row; i < m_numRows; i++ )
9308 {
9309 m_rowBottoms[i] += diff;
9310 }
9311 if ( !GetBatchCount() )
9312 CalcDimensions();
9313 }
9314
9315 void wxGrid::SetDefaultColSize( int width, bool resizeExistingCols )
9316 {
9317 m_defaultColWidth = wxMax( width, m_minAcceptableColWidth );
9318
9319 if ( resizeExistingCols )
9320 {
9321 // since we are resizing all columns to the default column size,
9322 // we can simply clear the col widths and col rights
9323 // arrays (which also allows us to take advantage of
9324 // some speed optimisations)
9325 m_colWidths.Empty();
9326 m_colRights.Empty();
9327 if ( !GetBatchCount() )
9328 CalcDimensions();
9329 }
9330 }
9331
9332 void wxGrid::SetColSize( int col, int width )
9333 {
9334 wxCHECK_RET( col >= 0 && col < m_numCols, _T("invalid column index") );
9335
9336 // should we check that it's bigger than GetColMinimalWidth(col) here?
9337 // (VZ)
9338 // No, because it is reasonable to assume the library user know's
9339 // what he is doing. However whe should test against the weaker
9340 // constariant of minimalAcceptableWidth, as this breaks rendering
9341 //
9342 // This test then fixes sf.net bug #645734
9343
9344 if ( width < GetColMinimalAcceptableWidth()) { return; }
9345
9346 if ( m_colWidths.IsEmpty() )
9347 {
9348 // need to really create the array
9349 InitColWidths();
9350 }
9351
9352 // if < 0 calc new width from label
9353 if( width < 0 )
9354 {
9355 long w, h;
9356 wxArrayString lines;
9357 wxClientDC dc(m_colLabelWin);
9358 dc.SetFont(GetLabelFont());
9359 StringToLines(GetColLabelValue(col), lines);
9360 GetTextBoxSize(dc, lines, &w, &h);
9361 width = w + 6;
9362 }
9363 int w = wxMax( 0, width );
9364 int diff = w - m_colWidths[col];
9365 m_colWidths[col] = w;
9366
9367 int i;
9368 for ( i = col; i < m_numCols; i++ )
9369 {
9370 m_colRights[i] += diff;
9371 }
9372 if ( !GetBatchCount() )
9373 CalcDimensions();
9374 }
9375
9376
9377 void wxGrid::SetColMinimalWidth( int col, int width )
9378 {
9379 if (width > GetColMinimalAcceptableWidth()) {
9380 m_colMinWidths[col] = width;
9381 }
9382 }
9383
9384 void wxGrid::SetRowMinimalHeight( int row, int width )
9385 {
9386 if (width > GetRowMinimalAcceptableHeight()) {
9387 m_rowMinHeights[row] = width;
9388 }
9389 }
9390
9391 int wxGrid::GetColMinimalWidth(int col) const
9392 {
9393 wxLongToLongHashMap::const_iterator it = m_colMinWidths.find(col);
9394 return it != m_colMinWidths.end() ? (int)it->second : m_minAcceptableColWidth;
9395 }
9396
9397 int wxGrid::GetRowMinimalHeight(int row) const
9398 {
9399 wxLongToLongHashMap::const_iterator it = m_rowMinHeights.find(row);
9400 return it != m_rowMinHeights.end() ? (int)it->second : m_minAcceptableRowHeight;
9401 }
9402
9403 void wxGrid::SetColMinimalAcceptableWidth( int width )
9404 {
9405 if ( width<1 )
9406 return;
9407 m_minAcceptableColWidth = width;
9408 }
9409
9410 void wxGrid::SetRowMinimalAcceptableHeight( int height )
9411 {
9412 if ( height<1 )
9413 return;
9414 m_minAcceptableRowHeight = height;
9415 };
9416
9417 int wxGrid::GetColMinimalAcceptableWidth() const
9418 {
9419 return m_minAcceptableColWidth;
9420 }
9421
9422 int wxGrid::GetRowMinimalAcceptableHeight() const
9423 {
9424 return m_minAcceptableRowHeight;
9425 }
9426
9427 // ----------------------------------------------------------------------------
9428 // auto sizing
9429 // ----------------------------------------------------------------------------
9430
9431 void wxGrid::AutoSizeColOrRow( int colOrRow, bool setAsMin, bool column )
9432 {
9433 wxClientDC dc(m_gridWin);
9434
9435 // init both of them to avoid compiler warnings, even if weo nly need one
9436 int row = -1,
9437 col = -1;
9438 if ( column )
9439 col = colOrRow;
9440 else
9441 row = colOrRow;
9442
9443 wxCoord extent, extentMax = 0;
9444 int max = column ? m_numRows : m_numCols;
9445 for ( int rowOrCol = 0; rowOrCol < max; rowOrCol++ )
9446 {
9447 if ( column )
9448 row = rowOrCol;
9449 else
9450 col = rowOrCol;
9451
9452 wxGridCellAttr* attr = GetCellAttr(row, col);
9453 wxGridCellRenderer* renderer = attr->GetRenderer(this, row, col);
9454 if ( renderer )
9455 {
9456 wxSize size = renderer->GetBestSize(*this, *attr, dc, row, col);
9457 extent = column ? size.x : size.y;
9458 if ( extent > extentMax )
9459 {
9460 extentMax = extent;
9461 }
9462
9463 renderer->DecRef();
9464 }
9465
9466 attr->DecRef();
9467 }
9468
9469 // now also compare with the column label extent
9470 wxCoord w, h;
9471 dc.SetFont( GetLabelFont() );
9472
9473 if ( column )
9474 {
9475 dc.GetTextExtent( GetColLabelValue(col), &w, &h );
9476 if( GetColLabelTextOrientation() == wxVERTICAL )
9477 w = h;
9478 }
9479 else
9480 dc.GetTextExtent( GetRowLabelValue(row), &w, &h );
9481
9482 extent = column ? w : h;
9483 if ( extent > extentMax )
9484 {
9485 extentMax = extent;
9486 }
9487
9488 if ( !extentMax )
9489 {
9490 // empty column - give default extent (notice that if extentMax is less
9491 // than default extent but != 0, it's ok)
9492 extentMax = column ? m_defaultColWidth : m_defaultRowHeight;
9493 }
9494 else
9495 {
9496 if ( column )
9497 {
9498 // leave some space around text
9499 extentMax += 10;
9500 }
9501 else
9502 {
9503 extentMax += 6;
9504 }
9505 }
9506
9507 if ( column )
9508 {
9509 SetColSize(col, extentMax);
9510 if ( !GetBatchCount() )
9511 {
9512 int cw, ch, dummy;
9513 m_gridWin->GetClientSize( &cw, &ch );
9514 wxRect rect ( CellToRect( 0, col ) );
9515 rect.y = 0;
9516 CalcScrolledPosition(rect.x, 0, &rect.x, &dummy);
9517 rect.width = cw - rect.x;
9518 rect.height = m_colLabelHeight;
9519 m_colLabelWin->Refresh( TRUE, &rect );
9520 }
9521 }
9522 else
9523 {
9524 SetRowSize(row, extentMax);
9525 if ( !GetBatchCount() )
9526 {
9527 int cw, ch, dummy;
9528 m_gridWin->GetClientSize( &cw, &ch );
9529 wxRect rect ( CellToRect( row, 0 ) );
9530 rect.x = 0;
9531 CalcScrolledPosition(0, rect.y, &dummy, &rect.y);
9532 rect.width = m_rowLabelWidth;
9533 rect.height = ch - rect.y;
9534 m_rowLabelWin->Refresh( TRUE, &rect );
9535 }
9536 }
9537 if ( setAsMin )
9538 {
9539 if ( column )
9540 SetColMinimalWidth(col, extentMax);
9541 else
9542 SetRowMinimalHeight(row, extentMax);
9543 }
9544 }
9545
9546 int wxGrid::SetOrCalcColumnSizes(bool calcOnly, bool setAsMin)
9547 {
9548 int width = m_rowLabelWidth;
9549
9550 if ( !calcOnly )
9551 BeginBatch();
9552
9553 for ( int col = 0; col < m_numCols; col++ )
9554 {
9555 if ( !calcOnly )
9556 {
9557 AutoSizeColumn(col, setAsMin);
9558 }
9559
9560 width += GetColWidth(col);
9561 }
9562
9563 if ( !calcOnly )
9564 EndBatch();
9565
9566 return width;
9567 }
9568
9569 int wxGrid::SetOrCalcRowSizes(bool calcOnly, bool setAsMin)
9570 {
9571 int height = m_colLabelHeight;
9572
9573 if ( !calcOnly )
9574 BeginBatch();
9575
9576 for ( int row = 0; row < m_numRows; row++ )
9577 {
9578 if ( !calcOnly )
9579 {
9580 AutoSizeRow(row, setAsMin);
9581 }
9582
9583 height += GetRowHeight(row);
9584 }
9585
9586 if ( !calcOnly )
9587 EndBatch();
9588
9589 return height;
9590 }
9591
9592 void wxGrid::AutoSize()
9593 {
9594 BeginBatch();
9595
9596 wxSize size(SetOrCalcColumnSizes(FALSE), SetOrCalcRowSizes(FALSE));
9597
9598 // round up the size to a multiple of scroll step - this ensures that we
9599 // won't get the scrollbars if we're sized exactly to this width
9600 // CalcDimension adds m_extraWidth + 1 etc. to calculate the necessary
9601 // scrollbar steps
9602 wxSize sizeFit(GetScrollX(size.x + m_extraWidth + 1) * GRID_SCROLL_LINE_X,
9603 GetScrollY(size.y + m_extraHeight + 1) * GRID_SCROLL_LINE_Y);
9604
9605 // distribute the extra space between the columns/rows to avoid having
9606 // extra white space
9607
9608 // Remove the extra m_extraWidth + 1 added above
9609 wxCoord diff = sizeFit.x - size.x + (m_extraWidth + 1);
9610 if ( diff && m_numCols )
9611 {
9612 // try to resize the columns uniformly
9613 wxCoord diffPerCol = diff / m_numCols;
9614 if ( diffPerCol )
9615 {
9616 for ( int col = 0; col < m_numCols; col++ )
9617 {
9618 SetColSize(col, GetColWidth(col) + diffPerCol);
9619 }
9620 }
9621
9622 // add remaining amount to the last columns
9623 diff -= diffPerCol * m_numCols;
9624 if ( diff )
9625 {
9626 for ( int col = m_numCols - 1; col >= m_numCols - diff; col-- )
9627 {
9628 SetColSize(col, GetColWidth(col) + 1);
9629 }
9630 }
9631 }
9632
9633 // same for rows
9634 diff = sizeFit.y - size.y - (m_extraHeight + 1);
9635 if ( diff && m_numRows )
9636 {
9637 // try to resize the columns uniformly
9638 wxCoord diffPerRow = diff / m_numRows;
9639 if ( diffPerRow )
9640 {
9641 for ( int row = 0; row < m_numRows; row++ )
9642 {
9643 SetRowSize(row, GetRowHeight(row) + diffPerRow);
9644 }
9645 }
9646
9647 // add remaining amount to the last rows
9648 diff -= diffPerRow * m_numRows;
9649 if ( diff )
9650 {
9651 for ( int row = m_numRows - 1; row >= m_numRows - diff; row-- )
9652 {
9653 SetRowSize(row, GetRowHeight(row) + 1);
9654 }
9655 }
9656 }
9657
9658 EndBatch();
9659
9660 SetClientSize(sizeFit);
9661 }
9662
9663 void wxGrid::AutoSizeRowLabelSize( int row )
9664 {
9665 wxArrayString lines;
9666 long w, h;
9667
9668 // Hide the edit control, so it
9669 // won't interfer with drag-shrinking.
9670 if( IsCellEditControlShown() )
9671 {
9672 HideCellEditControl();
9673 SaveEditControlValue();
9674 }
9675
9676 // autosize row height depending on label text
9677 StringToLines( GetRowLabelValue( row ), lines );
9678 wxClientDC dc( m_rowLabelWin );
9679 GetTextBoxSize( dc, lines, &w, &h);
9680 if( h < m_defaultRowHeight )
9681 h = m_defaultRowHeight;
9682 SetRowSize(row, h);
9683 ForceRefresh();
9684 }
9685
9686 void wxGrid::AutoSizeColLabelSize( int col )
9687 {
9688 wxArrayString lines;
9689 long w, h;
9690
9691 // Hide the edit control, so it
9692 // won't interfer with drag-shrinking.
9693 if( IsCellEditControlShown() )
9694 {
9695 HideCellEditControl();
9696 SaveEditControlValue();
9697 }
9698
9699 // autosize column width depending on label text
9700 StringToLines( GetColLabelValue( col ), lines );
9701 wxClientDC dc( m_colLabelWin );
9702 if( GetColLabelTextOrientation() == wxHORIZONTAL )
9703 GetTextBoxSize( dc, lines, &w, &h);
9704 else
9705 GetTextBoxSize( dc, lines, &h, &w);
9706 if( w < m_defaultColWidth )
9707 w = m_defaultColWidth;
9708 SetColSize(col, w);
9709 ForceRefresh();
9710 }
9711
9712 wxSize wxGrid::DoGetBestSize() const
9713 {
9714 // don't set sizes, only calculate them
9715 wxGrid *self = (wxGrid *)this; // const_cast
9716
9717 int width, height;
9718 width = self->SetOrCalcColumnSizes(TRUE);
9719 height = self->SetOrCalcRowSizes(TRUE);
9720
9721 int maxwidth, maxheight;
9722 wxDisplaySize( & maxwidth, & maxheight );
9723
9724 if ( width > maxwidth ) width = maxwidth;
9725 if ( height > maxheight ) height = maxheight;
9726
9727 return wxSize( width, height );
9728 }
9729
9730 void wxGrid::Fit()
9731 {
9732 AutoSize();
9733 }
9734
9735
9736 wxPen& wxGrid::GetDividerPen() const
9737 {
9738 return wxNullPen;
9739 }
9740
9741 // ----------------------------------------------------------------------------
9742 // cell value accessor functions
9743 // ----------------------------------------------------------------------------
9744
9745 void wxGrid::SetCellValue( int row, int col, const wxString& s )
9746 {
9747 if ( m_table )
9748 {
9749 m_table->SetValue( row, col, s );
9750 if ( !GetBatchCount() )
9751 {
9752 int dummy;
9753 wxRect rect( CellToRect( row, col ) );
9754 rect.x = 0;
9755 rect.width = m_gridWin->GetClientSize().GetWidth();
9756 CalcScrolledPosition(0, rect.y, &dummy, &rect.y);
9757 m_gridWin->Refresh( FALSE, &rect );
9758 }
9759
9760 if ( m_currentCellCoords.GetRow() == row &&
9761 m_currentCellCoords.GetCol() == col &&
9762 IsCellEditControlShown())
9763 // Note: If we are using IsCellEditControlEnabled,
9764 // this interacts badly with calling SetCellValue from
9765 // an EVT_GRID_CELL_CHANGE handler.
9766 {
9767 HideCellEditControl();
9768 ShowCellEditControl(); // will reread data from table
9769 }
9770 }
9771 }
9772
9773
9774 //
9775 // ------ Block, row and col selection
9776 //
9777
9778 void wxGrid::SelectRow( int row, bool addToSelected )
9779 {
9780 if ( IsSelection() && !addToSelected )
9781 ClearSelection();
9782
9783 if ( m_selection )
9784 m_selection->SelectRow( row, FALSE, addToSelected );
9785 }
9786
9787
9788 void wxGrid::SelectCol( int col, bool addToSelected )
9789 {
9790 if ( IsSelection() && !addToSelected )
9791 ClearSelection();
9792
9793 if ( m_selection )
9794 m_selection->SelectCol( col, FALSE, addToSelected );
9795 }
9796
9797
9798 void wxGrid::SelectBlock( int topRow, int leftCol, int bottomRow, int rightCol,
9799 bool addToSelected )
9800 {
9801 if ( IsSelection() && !addToSelected )
9802 ClearSelection();
9803
9804 if ( m_selection )
9805 m_selection->SelectBlock( topRow, leftCol, bottomRow, rightCol,
9806 FALSE, addToSelected );
9807 }
9808
9809
9810 void wxGrid::SelectAll()
9811 {
9812 if ( m_numRows > 0 && m_numCols > 0 )
9813 {
9814 if ( m_selection )
9815 m_selection->SelectBlock( 0, 0, m_numRows-1, m_numCols-1 );
9816 }
9817 }
9818
9819 //
9820 // ------ Cell, row and col deselection
9821 //
9822
9823 void wxGrid::DeselectRow( int row )
9824 {
9825 if ( !m_selection )
9826 return;
9827
9828 if ( m_selection->GetSelectionMode() == wxGrid::wxGridSelectRows )
9829 {
9830 if ( m_selection->IsInSelection(row, 0 ) )
9831 m_selection->ToggleCellSelection( row, 0);
9832 }
9833 else
9834 {
9835 int nCols = GetNumberCols();
9836 for ( int i = 0; i < nCols ; i++ )
9837 {
9838 if ( m_selection->IsInSelection(row, i ) )
9839 m_selection->ToggleCellSelection( row, i);
9840 }
9841 }
9842 }
9843
9844 void wxGrid::DeselectCol( int col )
9845 {
9846 if ( !m_selection )
9847 return;
9848
9849 if ( m_selection->GetSelectionMode() == wxGrid::wxGridSelectColumns )
9850 {
9851 if ( m_selection->IsInSelection(0, col ) )
9852 m_selection->ToggleCellSelection( 0, col);
9853 }
9854 else
9855 {
9856 int nRows = GetNumberRows();
9857 for ( int i = 0; i < nRows ; i++ )
9858 {
9859 if ( m_selection->IsInSelection(i, col ) )
9860 m_selection->ToggleCellSelection(i, col);
9861 }
9862 }
9863 }
9864
9865 void wxGrid::DeselectCell( int row, int col )
9866 {
9867 if ( m_selection && m_selection->IsInSelection(row, col) )
9868 m_selection->ToggleCellSelection(row, col);
9869 }
9870
9871 bool wxGrid::IsSelection()
9872 {
9873 return ( m_selection && (m_selection->IsSelection() ||
9874 ( m_selectingTopLeft != wxGridNoCellCoords &&
9875 m_selectingBottomRight != wxGridNoCellCoords) ) );
9876 }
9877
9878 bool wxGrid::IsInSelection( int row, int col ) const
9879 {
9880 return ( m_selection && (m_selection->IsInSelection( row, col ) ||
9881 ( row >= m_selectingTopLeft.GetRow() &&
9882 col >= m_selectingTopLeft.GetCol() &&
9883 row <= m_selectingBottomRight.GetRow() &&
9884 col <= m_selectingBottomRight.GetCol() )) );
9885 }
9886
9887 wxGridCellCoordsArray wxGrid::GetSelectedCells() const
9888 {
9889 if (!m_selection) { wxGridCellCoordsArray a; return a; }
9890 return m_selection->m_cellSelection;
9891 }
9892 wxGridCellCoordsArray wxGrid::GetSelectionBlockTopLeft() const
9893 {
9894 if (!m_selection) { wxGridCellCoordsArray a; return a; }
9895 return m_selection->m_blockSelectionTopLeft;
9896 }
9897 wxGridCellCoordsArray wxGrid::GetSelectionBlockBottomRight() const
9898 {
9899 if (!m_selection) { wxGridCellCoordsArray a; return a; }
9900 return m_selection->m_blockSelectionBottomRight;
9901 }
9902 wxArrayInt wxGrid::GetSelectedRows() const
9903 {
9904 if (!m_selection) { wxArrayInt a; return a; }
9905 return m_selection->m_rowSelection;
9906 }
9907 wxArrayInt wxGrid::GetSelectedCols() const
9908 {
9909 if (!m_selection) { wxArrayInt a; return a; }
9910 return m_selection->m_colSelection;
9911 }
9912
9913
9914 void wxGrid::ClearSelection()
9915 {
9916 m_selectingTopLeft = wxGridNoCellCoords;
9917 m_selectingBottomRight = wxGridNoCellCoords;
9918 if ( m_selection )
9919 m_selection->ClearSelection();
9920 }
9921
9922
9923 // This function returns the rectangle that encloses the given block
9924 // in device coords clipped to the client size of the grid window.
9925 //
9926 wxRect wxGrid::BlockToDeviceRect( const wxGridCellCoords &topLeft,
9927 const wxGridCellCoords &bottomRight )
9928 {
9929 wxRect rect( wxGridNoCellRect );
9930 wxRect cellRect;
9931
9932 cellRect = CellToRect( topLeft );
9933 if ( cellRect != wxGridNoCellRect )
9934 {
9935 rect = cellRect;
9936 }
9937 else
9938 {
9939 rect = wxRect( 0, 0, 0, 0 );
9940 }
9941
9942 cellRect = CellToRect( bottomRight );
9943 if ( cellRect != wxGridNoCellRect )
9944 {
9945 rect += cellRect;
9946 }
9947 else
9948 {
9949 return wxGridNoCellRect;
9950 }
9951
9952 int i, j;
9953 int left = rect.GetLeft();
9954 int top = rect.GetTop();
9955 int right = rect.GetRight();
9956 int bottom = rect.GetBottom();
9957
9958 int leftCol = topLeft.GetCol();
9959 int topRow = topLeft.GetRow();
9960 int rightCol = bottomRight.GetCol();
9961 int bottomRow = bottomRight.GetRow();
9962
9963 if (left > right)
9964 {
9965 i = left;
9966 left = right;
9967 right = i;
9968 i = leftCol;
9969 leftCol=rightCol;
9970 rightCol = i;
9971 }
9972
9973 if (top > bottom)
9974 {
9975 i = top;
9976 top = bottom;
9977 bottom = i;
9978 i = topRow;
9979 topRow = bottomRow;
9980 bottomRow = i;
9981 }
9982
9983
9984 for ( j = topRow; j <= bottomRow; j++ )
9985 {
9986 for ( i = leftCol; i <= rightCol; i++ )
9987 {
9988 if ((j==topRow) || (j==bottomRow) || (i==leftCol) || (i==rightCol))
9989 {
9990 cellRect = CellToRect( j, i );
9991
9992 if (cellRect.x < left)
9993 left = cellRect.x;
9994 if (cellRect.y < top)
9995 top = cellRect.y;
9996 if (cellRect.x + cellRect.width > right)
9997 right = cellRect.x + cellRect.width;
9998 if (cellRect.y + cellRect.height > bottom)
9999 bottom = cellRect.y + cellRect.height;
10000 }
10001 else i = rightCol; // jump over inner cells.
10002 }
10003 }
10004
10005 // convert to scrolled coords
10006 //
10007 CalcScrolledPosition( left, top, &left, &top );
10008 CalcScrolledPosition( right, bottom, &right, &bottom );
10009
10010 int cw, ch;
10011 m_gridWin->GetClientSize( &cw, &ch );
10012
10013 if (right < 0 || bottom < 0 || left > cw || top > ch)
10014 return wxRect( 0, 0, 0, 0);
10015
10016 rect.SetLeft( wxMax(0, left) );
10017 rect.SetTop( wxMax(0, top) );
10018 rect.SetRight( wxMin(cw, right) );
10019 rect.SetBottom( wxMin(ch, bottom) );
10020
10021 return rect;
10022 }
10023
10024 //
10025 // ------ Grid event classes
10026 //
10027
10028 IMPLEMENT_DYNAMIC_CLASS( wxGridEvent, wxNotifyEvent )
10029
10030 wxGridEvent::wxGridEvent( int id, wxEventType type, wxObject* obj,
10031 int row, int col, int x, int y, bool sel,
10032 bool control, bool shift, bool alt, bool meta )
10033 : wxNotifyEvent( type, id )
10034 {
10035 m_row = row;
10036 m_col = col;
10037 m_x = x;
10038 m_y = y;
10039 m_selecting = sel;
10040 m_control = control;
10041 m_shift = shift;
10042 m_alt = alt;
10043 m_meta = meta;
10044
10045 SetEventObject(obj);
10046 }
10047
10048
10049 IMPLEMENT_DYNAMIC_CLASS( wxGridSizeEvent, wxNotifyEvent )
10050
10051 wxGridSizeEvent::wxGridSizeEvent( int id, wxEventType type, wxObject* obj,
10052 int rowOrCol, int x, int y,
10053 bool control, bool shift, bool alt, bool meta )
10054 : wxNotifyEvent( type, id )
10055 {
10056 m_rowOrCol = rowOrCol;
10057 m_x = x;
10058 m_y = y;
10059 m_control = control;
10060 m_shift = shift;
10061 m_alt = alt;
10062 m_meta = meta;
10063
10064 SetEventObject(obj);
10065 }
10066
10067
10068 IMPLEMENT_DYNAMIC_CLASS( wxGridRangeSelectEvent, wxNotifyEvent )
10069
10070 wxGridRangeSelectEvent::wxGridRangeSelectEvent(int id, wxEventType type, wxObject* obj,
10071 const wxGridCellCoords& topLeft,
10072 const wxGridCellCoords& bottomRight,
10073 bool sel, bool control,
10074 bool shift, bool alt, bool meta )
10075 : wxNotifyEvent( type, id )
10076 {
10077 m_topLeft = topLeft;
10078 m_bottomRight = bottomRight;
10079 m_selecting = sel;
10080 m_control = control;
10081 m_shift = shift;
10082 m_alt = alt;
10083 m_meta = meta;
10084
10085 SetEventObject(obj);
10086 }
10087
10088
10089 IMPLEMENT_DYNAMIC_CLASS(wxGridEditorCreatedEvent, wxCommandEvent)
10090
10091 wxGridEditorCreatedEvent::wxGridEditorCreatedEvent(int id, wxEventType type,
10092 wxObject* obj, int row,
10093 int col, wxControl* ctrl)
10094 : wxCommandEvent(type, id)
10095 {
10096 SetEventObject(obj);
10097 m_row = row;
10098 m_col = col;
10099 m_ctrl = ctrl;
10100 }
10101
10102 #endif // wxUSE_GRID
10103