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