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