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