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