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