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