forward SetColPos() to the header window
[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 idx, int pos)
6783 {
6784 if ( m_colAt.empty() )
6785 {
6786 // we're going to need m_colAt now, initialize it
6787 m_colAt.reserve(m_numCols);
6788 for ( int i = 0; i < m_numCols; i++ )
6789 m_colAt.push_back(i);
6790 }
6791
6792 // create the updated copy of m_colAt
6793 const unsigned count = m_colAt.size();
6794
6795 wxArrayInt colAt;
6796 colAt.reserve(count);
6797 for ( unsigned n = 0; n < count; n++ )
6798 {
6799 // NB: order of checks is important for this to work when the new
6800 // column position is the same as the old one
6801
6802 // insert the column at its new position
6803 if ( colAt.size() == static_cast<unsigned>(pos) )
6804 colAt.push_back(idx);
6805
6806 // delete the column from its old position
6807 const int idxOld = m_colAt[n];
6808 if ( idxOld == idx )
6809 continue;
6810
6811 colAt.push_back(idxOld);
6812 }
6813
6814 m_colAt = colAt;
6815
6816 // also recalculate the column rights
6817 if ( !m_colWidths.IsEmpty() )
6818 {
6819 int colRight = 0;
6820 int colPos;
6821 for ( colPos = 0; colPos < m_numCols; colPos++ )
6822 {
6823 int colID = GetColAt( colPos );
6824
6825 colRight += m_colWidths[colID];
6826 m_colRights[colID] = colRight;
6827 }
6828 }
6829
6830 // and make the changes visible
6831 if ( m_useNativeHeader )
6832 GetColHeader()->SetColumnsOrder(m_colAt);
6833 else
6834 m_colWindow->Refresh();
6835 m_gridWin->Refresh();
6836 }
6837
6838
6839
6840 void wxGrid::EnableDragColMove( bool enable )
6841 {
6842 if ( m_canDragColMove == enable )
6843 return;
6844
6845 m_canDragColMove = enable;
6846
6847 if ( !m_canDragColMove )
6848 {
6849 m_colAt.Clear();
6850
6851 //Recalculate the column rights
6852 if ( !m_colWidths.IsEmpty() )
6853 {
6854 int colRight = 0;
6855 int colPos;
6856 for ( colPos = 0; colPos < m_numCols; colPos++ )
6857 {
6858 colRight += m_colWidths[colPos];
6859 m_colRights[colPos] = colRight;
6860 }
6861 }
6862
6863 m_colWindow->Refresh();
6864 m_gridWin->Refresh();
6865 }
6866 }
6867
6868
6869 //
6870 // ------ interaction with data model
6871 //
6872 bool wxGrid::ProcessTableMessage( wxGridTableMessage& msg )
6873 {
6874 switch ( msg.GetId() )
6875 {
6876 case wxGRIDTABLE_REQUEST_VIEW_GET_VALUES:
6877 return GetModelValues();
6878
6879 case wxGRIDTABLE_REQUEST_VIEW_SEND_VALUES:
6880 return SetModelValues();
6881
6882 case wxGRIDTABLE_NOTIFY_ROWS_INSERTED:
6883 case wxGRIDTABLE_NOTIFY_ROWS_APPENDED:
6884 case wxGRIDTABLE_NOTIFY_ROWS_DELETED:
6885 case wxGRIDTABLE_NOTIFY_COLS_INSERTED:
6886 case wxGRIDTABLE_NOTIFY_COLS_APPENDED:
6887 case wxGRIDTABLE_NOTIFY_COLS_DELETED:
6888 return Redimension( msg );
6889
6890 default:
6891 return false;
6892 }
6893 }
6894
6895 // The behaviour of this function depends on the grid table class
6896 // Clear() function. For the default wxGridStringTable class the
6897 // behaviour is to replace all cell contents with wxEmptyString but
6898 // not to change the number of rows or cols.
6899 //
6900 void wxGrid::ClearGrid()
6901 {
6902 if ( m_table )
6903 {
6904 if (IsCellEditControlEnabled())
6905 DisableCellEditControl();
6906
6907 m_table->Clear();
6908 if (!GetBatchCount())
6909 m_gridWin->Refresh();
6910 }
6911 }
6912
6913 bool
6914 wxGrid::DoModifyLines(bool (wxGridTableBase::*funcModify)(size_t, size_t),
6915 int pos, int num, bool WXUNUSED(updateLabels) )
6916 {
6917 wxCHECK_MSG( m_created, false, "must finish creating the grid first" );
6918
6919 if ( !m_table )
6920 return false;
6921
6922 if ( IsCellEditControlEnabled() )
6923 DisableCellEditControl();
6924
6925 return (m_table->*funcModify)(pos, num);
6926
6927 // the table will have sent the results of the insert row
6928 // operation to this view object as a grid table message
6929 }
6930
6931 bool
6932 wxGrid::DoAppendLines(bool (wxGridTableBase::*funcAppend)(size_t),
6933 int num, bool WXUNUSED(updateLabels))
6934 {
6935 wxCHECK_MSG( m_created, false, "must finish creating the grid first" );
6936
6937 if ( !m_table )
6938 return false;
6939
6940 return (m_table->*funcAppend)(num);
6941 }
6942
6943 //
6944 // ----- event handlers
6945 //
6946
6947 // Generate a grid event based on a mouse event and return:
6948 // -1 if the event was vetoed
6949 // +1 if the event was processed (but not vetoed)
6950 // 0 if the event wasn't handled
6951 int
6952 wxGrid::SendEvent(const wxEventType type,
6953 int row, int col,
6954 wxMouseEvent& mouseEv)
6955 {
6956 bool claimed, vetoed;
6957
6958 if ( type == wxEVT_GRID_ROW_SIZE || type == wxEVT_GRID_COL_SIZE )
6959 {
6960 int rowOrCol = (row == -1 ? col : row);
6961
6962 wxGridSizeEvent gridEvt( GetId(),
6963 type,
6964 this,
6965 rowOrCol,
6966 mouseEv.GetX() + GetRowLabelSize(),
6967 mouseEv.GetY() + GetColLabelSize(),
6968 mouseEv);
6969
6970 claimed = GetEventHandler()->ProcessEvent(gridEvt);
6971 vetoed = !gridEvt.IsAllowed();
6972 }
6973 else if ( type == wxEVT_GRID_RANGE_SELECT )
6974 {
6975 // Right now, it should _never_ end up here!
6976 wxGridRangeSelectEvent gridEvt( GetId(),
6977 type,
6978 this,
6979 m_selectedBlockTopLeft,
6980 m_selectedBlockBottomRight,
6981 true,
6982 mouseEv);
6983
6984 claimed = GetEventHandler()->ProcessEvent(gridEvt);
6985 vetoed = !gridEvt.IsAllowed();
6986 }
6987 else if ( type == wxEVT_GRID_LABEL_LEFT_CLICK ||
6988 type == wxEVT_GRID_LABEL_LEFT_DCLICK ||
6989 type == wxEVT_GRID_LABEL_RIGHT_CLICK ||
6990 type == wxEVT_GRID_LABEL_RIGHT_DCLICK )
6991 {
6992 wxPoint pos = mouseEv.GetPosition();
6993
6994 if ( mouseEv.GetEventObject() == GetGridRowLabelWindow() )
6995 pos.y += GetColLabelSize();
6996 if ( mouseEv.GetEventObject() == GetGridColLabelWindow() )
6997 pos.x += GetRowLabelSize();
6998
6999 wxGridEvent gridEvt( GetId(),
7000 type,
7001 this,
7002 row, col,
7003 pos.x,
7004 pos.y,
7005 false,
7006 mouseEv);
7007 claimed = GetEventHandler()->ProcessEvent(gridEvt);
7008 vetoed = !gridEvt.IsAllowed();
7009 }
7010 else
7011 {
7012 wxGridEvent gridEvt( GetId(),
7013 type,
7014 this,
7015 row, col,
7016 mouseEv.GetX() + GetRowLabelSize(),
7017 mouseEv.GetY() + GetColLabelSize(),
7018 false,
7019 mouseEv);
7020 claimed = GetEventHandler()->ProcessEvent(gridEvt);
7021 vetoed = !gridEvt.IsAllowed();
7022 }
7023
7024 // A Veto'd event may not be `claimed' so test this first
7025 if (vetoed)
7026 return -1;
7027
7028 return claimed ? 1 : 0;
7029 }
7030
7031 // Generate a grid event of specified type, return value same as above
7032 //
7033 int wxGrid::SendEvent(const wxEventType type, int row, int col)
7034 {
7035 bool claimed, vetoed;
7036
7037 if ( type == wxEVT_GRID_ROW_SIZE || type == wxEVT_GRID_COL_SIZE )
7038 {
7039 int rowOrCol = (row == -1 ? col : row);
7040
7041 wxGridSizeEvent gridEvt( GetId(), type, this, rowOrCol );
7042
7043 claimed = GetEventHandler()->ProcessEvent(gridEvt);
7044 vetoed = !gridEvt.IsAllowed();
7045 }
7046 else
7047 {
7048 wxGridEvent gridEvt( GetId(), type, this, row, col );
7049
7050 claimed = GetEventHandler()->ProcessEvent(gridEvt);
7051 vetoed = !gridEvt.IsAllowed();
7052 }
7053
7054 // A Veto'd event may not be `claimed' so test this first
7055 if (vetoed)
7056 return -1;
7057
7058 return claimed ? 1 : 0;
7059 }
7060
7061 void wxGrid::OnPaint( wxPaintEvent& WXUNUSED(event) )
7062 {
7063 // needed to prevent zillions of paint events on MSW
7064 wxPaintDC dc(this);
7065 }
7066
7067 void wxGrid::Refresh(bool eraseb, const wxRect* rect)
7068 {
7069 // Don't do anything if between Begin/EndBatch...
7070 // EndBatch() will do all this on the last nested one anyway.
7071 if ( m_created && !GetBatchCount() )
7072 {
7073 // Refresh to get correct scrolled position:
7074 wxScrolledWindow::Refresh(eraseb, rect);
7075
7076 if (rect)
7077 {
7078 int rect_x, rect_y, rectWidth, rectHeight;
7079 int width_label, width_cell, height_label, height_cell;
7080 int x, y;
7081
7082 // Copy rectangle can get scroll offsets..
7083 rect_x = rect->GetX();
7084 rect_y = rect->GetY();
7085 rectWidth = rect->GetWidth();
7086 rectHeight = rect->GetHeight();
7087
7088 width_label = m_rowLabelWidth - rect_x;
7089 if (width_label > rectWidth)
7090 width_label = rectWidth;
7091
7092 height_label = m_colLabelHeight - rect_y;
7093 if (height_label > rectHeight)
7094 height_label = rectHeight;
7095
7096 if (rect_x > m_rowLabelWidth)
7097 {
7098 x = rect_x - m_rowLabelWidth;
7099 width_cell = rectWidth;
7100 }
7101 else
7102 {
7103 x = 0;
7104 width_cell = rectWidth - (m_rowLabelWidth - rect_x);
7105 }
7106
7107 if (rect_y > m_colLabelHeight)
7108 {
7109 y = rect_y - m_colLabelHeight;
7110 height_cell = rectHeight;
7111 }
7112 else
7113 {
7114 y = 0;
7115 height_cell = rectHeight - (m_colLabelHeight - rect_y);
7116 }
7117
7118 // Paint corner label part intersecting rect.
7119 if ( width_label > 0 && height_label > 0 )
7120 {
7121 wxRect anotherrect(rect_x, rect_y, width_label, height_label);
7122 m_cornerLabelWin->Refresh(eraseb, &anotherrect);
7123 }
7124
7125 // Paint col labels part intersecting rect.
7126 if ( width_cell > 0 && height_label > 0 )
7127 {
7128 wxRect anotherrect(x, rect_y, width_cell, height_label);
7129 m_colWindow->Refresh(eraseb, &anotherrect);
7130 }
7131
7132 // Paint row labels part intersecting rect.
7133 if ( width_label > 0 && height_cell > 0 )
7134 {
7135 wxRect anotherrect(rect_x, y, width_label, height_cell);
7136 m_rowLabelWin->Refresh(eraseb, &anotherrect);
7137 }
7138
7139 // Paint cell area part intersecting rect.
7140 if ( width_cell > 0 && height_cell > 0 )
7141 {
7142 wxRect anotherrect(x, y, width_cell, height_cell);
7143 m_gridWin->Refresh(eraseb, &anotherrect);
7144 }
7145 }
7146 else
7147 {
7148 m_cornerLabelWin->Refresh(eraseb, NULL);
7149 m_colWindow->Refresh(eraseb, NULL);
7150 m_rowLabelWin->Refresh(eraseb, NULL);
7151 m_gridWin->Refresh(eraseb, NULL);
7152 }
7153 }
7154 }
7155
7156 void wxGrid::OnSize(wxSizeEvent& WXUNUSED(event))
7157 {
7158 if (m_targetWindow != this) // check whether initialisation has been done
7159 {
7160 // reposition our children windows
7161 CalcWindowSizes();
7162 }
7163 }
7164
7165 void wxGrid::OnKeyDown( wxKeyEvent& event )
7166 {
7167 if ( m_inOnKeyDown )
7168 {
7169 // shouldn't be here - we are going round in circles...
7170 //
7171 wxFAIL_MSG( wxT("wxGrid::OnKeyDown called while already active") );
7172 }
7173
7174 m_inOnKeyDown = true;
7175
7176 // propagate the event up and see if it gets processed
7177 wxWindow *parent = GetParent();
7178 wxKeyEvent keyEvt( event );
7179 keyEvt.SetEventObject( parent );
7180
7181 if ( !parent->GetEventHandler()->ProcessEvent( keyEvt ) )
7182 {
7183 if (GetLayoutDirection() == wxLayout_RightToLeft)
7184 {
7185 if (event.GetKeyCode() == WXK_RIGHT)
7186 event.m_keyCode = WXK_LEFT;
7187 else if (event.GetKeyCode() == WXK_LEFT)
7188 event.m_keyCode = WXK_RIGHT;
7189 }
7190
7191 // try local handlers
7192 switch ( event.GetKeyCode() )
7193 {
7194 case WXK_UP:
7195 if ( event.ControlDown() )
7196 MoveCursorUpBlock( event.ShiftDown() );
7197 else
7198 MoveCursorUp( event.ShiftDown() );
7199 break;
7200
7201 case WXK_DOWN:
7202 if ( event.ControlDown() )
7203 MoveCursorDownBlock( event.ShiftDown() );
7204 else
7205 MoveCursorDown( event.ShiftDown() );
7206 break;
7207
7208 case WXK_LEFT:
7209 if ( event.ControlDown() )
7210 MoveCursorLeftBlock( event.ShiftDown() );
7211 else
7212 MoveCursorLeft( event.ShiftDown() );
7213 break;
7214
7215 case WXK_RIGHT:
7216 if ( event.ControlDown() )
7217 MoveCursorRightBlock( event.ShiftDown() );
7218 else
7219 MoveCursorRight( event.ShiftDown() );
7220 break;
7221
7222 case WXK_RETURN:
7223 case WXK_NUMPAD_ENTER:
7224 if ( event.ControlDown() )
7225 {
7226 event.Skip(); // to let the edit control have the return
7227 }
7228 else
7229 {
7230 if ( GetGridCursorRow() < GetNumberRows()-1 )
7231 {
7232 MoveCursorDown( event.ShiftDown() );
7233 }
7234 else
7235 {
7236 // at the bottom of a column
7237 DisableCellEditControl();
7238 }
7239 }
7240 break;
7241
7242 case WXK_ESCAPE:
7243 ClearSelection();
7244 break;
7245
7246 case WXK_TAB:
7247 if (event.ShiftDown())
7248 {
7249 if ( GetGridCursorCol() > 0 )
7250 {
7251 MoveCursorLeft( false );
7252 }
7253 else
7254 {
7255 // at left of grid
7256 DisableCellEditControl();
7257 }
7258 }
7259 else
7260 {
7261 if ( GetGridCursorCol() < GetNumberCols() - 1 )
7262 {
7263 MoveCursorRight( false );
7264 }
7265 else
7266 {
7267 // at right of grid
7268 DisableCellEditControl();
7269 }
7270 }
7271 break;
7272
7273 case WXK_HOME:
7274 if ( event.ControlDown() )
7275 {
7276 GoToCell(0, 0);
7277 }
7278 else
7279 {
7280 event.Skip();
7281 }
7282 break;
7283
7284 case WXK_END:
7285 if ( event.ControlDown() )
7286 {
7287 GoToCell(m_numRows - 1, m_numCols - 1);
7288 }
7289 else
7290 {
7291 event.Skip();
7292 }
7293 break;
7294
7295 case WXK_PAGEUP:
7296 MovePageUp();
7297 break;
7298
7299 case WXK_PAGEDOWN:
7300 MovePageDown();
7301 break;
7302
7303 case WXK_SPACE:
7304 // Ctrl-Space selects the current column, Shift-Space -- the
7305 // current row and Ctrl-Shift-Space -- everything
7306 switch ( m_selection ? event.GetModifiers() : wxMOD_NONE )
7307 {
7308 case wxMOD_CONTROL:
7309 m_selection->SelectCol(m_currentCellCoords.GetCol());
7310 break;
7311
7312 case wxMOD_SHIFT:
7313 m_selection->SelectRow(m_currentCellCoords.GetRow());
7314 break;
7315
7316 case wxMOD_CONTROL | wxMOD_SHIFT:
7317 m_selection->SelectBlock(0, 0,
7318 m_numRows - 1, m_numCols - 1);
7319 break;
7320
7321 case wxMOD_NONE:
7322 if ( !IsEditable() )
7323 {
7324 MoveCursorRight(false);
7325 break;
7326 }
7327 //else: fall through
7328
7329 default:
7330 event.Skip();
7331 }
7332 break;
7333
7334 default:
7335 event.Skip();
7336 break;
7337 }
7338 }
7339
7340 m_inOnKeyDown = false;
7341 }
7342
7343 void wxGrid::OnKeyUp( wxKeyEvent& event )
7344 {
7345 // try local handlers
7346 //
7347 if ( event.GetKeyCode() == WXK_SHIFT )
7348 {
7349 if ( m_selectedBlockTopLeft != wxGridNoCellCoords &&
7350 m_selectedBlockBottomRight != wxGridNoCellCoords )
7351 {
7352 if ( m_selection )
7353 {
7354 m_selection->SelectBlock(
7355 m_selectedBlockTopLeft,
7356 m_selectedBlockBottomRight,
7357 event);
7358 }
7359 }
7360
7361 m_selectedBlockTopLeft = wxGridNoCellCoords;
7362 m_selectedBlockBottomRight = wxGridNoCellCoords;
7363 m_selectedBlockCorner = wxGridNoCellCoords;
7364 }
7365 }
7366
7367 void wxGrid::OnChar( wxKeyEvent& event )
7368 {
7369 // is it possible to edit the current cell at all?
7370 if ( !IsCellEditControlEnabled() && CanEnableCellControl() )
7371 {
7372 // yes, now check whether the cells editor accepts the key
7373 int row = m_currentCellCoords.GetRow();
7374 int col = m_currentCellCoords.GetCol();
7375 wxGridCellAttr *attr = GetCellAttr(row, col);
7376 wxGridCellEditor *editor = attr->GetEditor(this, row, col);
7377
7378 // <F2> is special and will always start editing, for
7379 // other keys - ask the editor itself
7380 if ( (event.GetKeyCode() == WXK_F2 && !event.HasModifiers())
7381 || editor->IsAcceptedKey(event) )
7382 {
7383 // ensure cell is visble
7384 MakeCellVisible(row, col);
7385 EnableCellEditControl();
7386
7387 // a problem can arise if the cell is not completely
7388 // visible (even after calling MakeCellVisible the
7389 // control is not created and calling StartingKey will
7390 // crash the app
7391 if ( event.GetKeyCode() != WXK_F2 && editor->IsCreated() && m_cellEditCtrlEnabled )
7392 editor->StartingKey(event);
7393 }
7394 else
7395 {
7396 event.Skip();
7397 }
7398
7399 editor->DecRef();
7400 attr->DecRef();
7401 }
7402 else
7403 {
7404 event.Skip();
7405 }
7406 }
7407
7408 void wxGrid::OnEraseBackground(wxEraseEvent&)
7409 {
7410 }
7411
7412 bool wxGrid::SetCurrentCell( const wxGridCellCoords& coords )
7413 {
7414 if ( SendEvent(wxEVT_GRID_SELECT_CELL, coords) == -1 )
7415 {
7416 // the event has been vetoed - do nothing
7417 return false;
7418 }
7419
7420 #if !defined(__WXMAC__)
7421 wxClientDC dc( m_gridWin );
7422 PrepareDC( dc );
7423 #endif
7424
7425 if ( m_currentCellCoords != wxGridNoCellCoords )
7426 {
7427 DisableCellEditControl();
7428
7429 if ( IsVisible( m_currentCellCoords, false ) )
7430 {
7431 wxRect r;
7432 r = BlockToDeviceRect( m_currentCellCoords, m_currentCellCoords );
7433 if ( !m_gridLinesEnabled )
7434 {
7435 r.x--;
7436 r.y--;
7437 r.width++;
7438 r.height++;
7439 }
7440
7441 wxGridCellCoordsArray cells = CalcCellsExposed( r );
7442
7443 // Otherwise refresh redraws the highlight!
7444 m_currentCellCoords = coords;
7445
7446 #if defined(__WXMAC__)
7447 m_gridWin->Refresh(true /*, & r */);
7448 #else
7449 DrawGridCellArea( dc, cells );
7450 DrawAllGridLines( dc, r );
7451 #endif
7452 }
7453 }
7454
7455 m_currentCellCoords = coords;
7456
7457 wxGridCellAttr *attr = GetCellAttr( coords );
7458 #if !defined(__WXMAC__)
7459 DrawCellHighlight( dc, attr );
7460 #endif
7461 attr->DecRef();
7462
7463 return true;
7464 }
7465
7466 void
7467 wxGrid::UpdateBlockBeingSelected(int topRow, int leftCol,
7468 int bottomRow, int rightCol)
7469 {
7470 if ( m_selection )
7471 {
7472 switch ( m_selection->GetSelectionMode() )
7473 {
7474 default:
7475 wxFAIL_MSG( "unknown selection mode" );
7476 // fall through
7477
7478 case wxGridSelectCells:
7479 // arbitrary blocks selection allowed so just use the cell
7480 // coordinates as is
7481 break;
7482
7483 case wxGridSelectRows:
7484 // only full rows selection allowd, ensure that we do select
7485 // full rows
7486 leftCol = 0;
7487 rightCol = GetNumberCols() - 1;
7488 break;
7489
7490 case wxGridSelectColumns:
7491 // same as above but for columns
7492 topRow = 0;
7493 bottomRow = GetNumberRows() - 1;
7494 break;
7495
7496 case wxGridSelectRowsOrColumns:
7497 // in this mode we can select only full rows or full columns so
7498 // it doesn't make sense to select blocks at all (and we can't
7499 // extend the block because there is no preferred direction, we
7500 // could only extend it to cover the entire grid but this is
7501 // not useful)
7502 return;
7503 }
7504 }
7505
7506 m_selectedBlockCorner = wxGridCellCoords(bottomRow, rightCol);
7507 MakeCellVisible(m_selectedBlockCorner);
7508
7509 EnsureFirstLessThanSecond(topRow, bottomRow);
7510 EnsureFirstLessThanSecond(leftCol, rightCol);
7511
7512 wxGridCellCoords updateTopLeft = wxGridCellCoords(topRow, leftCol),
7513 updateBottomRight = wxGridCellCoords(bottomRow, rightCol);
7514
7515 // First the case that we selected a completely new area
7516 if ( m_selectedBlockTopLeft == wxGridNoCellCoords ||
7517 m_selectedBlockBottomRight == wxGridNoCellCoords )
7518 {
7519 wxRect rect;
7520 rect = BlockToDeviceRect( wxGridCellCoords ( topRow, leftCol ),
7521 wxGridCellCoords ( bottomRow, rightCol ) );
7522 m_gridWin->Refresh( false, &rect );
7523 }
7524
7525 // Now handle changing an existing selection area.
7526 else if ( m_selectedBlockTopLeft != updateTopLeft ||
7527 m_selectedBlockBottomRight != updateBottomRight )
7528 {
7529 // Compute two optimal update rectangles:
7530 // Either one rectangle is a real subset of the
7531 // other, or they are (almost) disjoint!
7532 wxRect rect[4];
7533 bool need_refresh[4];
7534 need_refresh[0] =
7535 need_refresh[1] =
7536 need_refresh[2] =
7537 need_refresh[3] = false;
7538 int i;
7539
7540 // Store intermediate values
7541 wxCoord oldLeft = m_selectedBlockTopLeft.GetCol();
7542 wxCoord oldTop = m_selectedBlockTopLeft.GetRow();
7543 wxCoord oldRight = m_selectedBlockBottomRight.GetCol();
7544 wxCoord oldBottom = m_selectedBlockBottomRight.GetRow();
7545
7546 // Determine the outer/inner coordinates.
7547 EnsureFirstLessThanSecond(oldLeft, leftCol);
7548 EnsureFirstLessThanSecond(oldTop, topRow);
7549 EnsureFirstLessThanSecond(rightCol, oldRight);
7550 EnsureFirstLessThanSecond(bottomRow, oldBottom);
7551
7552 // Now, either the stuff marked old is the outer
7553 // rectangle or we don't have a situation where one
7554 // is contained in the other.
7555
7556 if ( oldLeft < leftCol )
7557 {
7558 // Refresh the newly selected or deselected
7559 // area to the left of the old or new selection.
7560 need_refresh[0] = true;
7561 rect[0] = BlockToDeviceRect(
7562 wxGridCellCoords( oldTop, oldLeft ),
7563 wxGridCellCoords( oldBottom, leftCol - 1 ) );
7564 }
7565
7566 if ( oldTop < topRow )
7567 {
7568 // Refresh the newly selected or deselected
7569 // area above the old or new selection.
7570 need_refresh[1] = true;
7571 rect[1] = BlockToDeviceRect(
7572 wxGridCellCoords( oldTop, leftCol ),
7573 wxGridCellCoords( topRow - 1, rightCol ) );
7574 }
7575
7576 if ( oldRight > rightCol )
7577 {
7578 // Refresh the newly selected or deselected
7579 // area to the right of the old or new selection.
7580 need_refresh[2] = true;
7581 rect[2] = BlockToDeviceRect(
7582 wxGridCellCoords( oldTop, rightCol + 1 ),
7583 wxGridCellCoords( oldBottom, oldRight ) );
7584 }
7585
7586 if ( oldBottom > bottomRow )
7587 {
7588 // Refresh the newly selected or deselected
7589 // area below the old or new selection.
7590 need_refresh[3] = true;
7591 rect[3] = BlockToDeviceRect(
7592 wxGridCellCoords( bottomRow + 1, leftCol ),
7593 wxGridCellCoords( oldBottom, rightCol ) );
7594 }
7595
7596 // various Refresh() calls
7597 for (i = 0; i < 4; i++ )
7598 if ( need_refresh[i] && rect[i] != wxGridNoCellRect )
7599 m_gridWin->Refresh( false, &(rect[i]) );
7600 }
7601
7602 // change selection
7603 m_selectedBlockTopLeft = updateTopLeft;
7604 m_selectedBlockBottomRight = updateBottomRight;
7605 }
7606
7607 //
7608 // ------ functions to get/send data (see also public functions)
7609 //
7610
7611 bool wxGrid::GetModelValues()
7612 {
7613 // Hide the editor, so it won't hide a changed value.
7614 HideCellEditControl();
7615
7616 if ( m_table )
7617 {
7618 // all we need to do is repaint the grid
7619 //
7620 m_gridWin->Refresh();
7621 return true;
7622 }
7623
7624 return false;
7625 }
7626
7627 bool wxGrid::SetModelValues()
7628 {
7629 int row, col;
7630
7631 // Disable the editor, so it won't hide a changed value.
7632 // Do we also want to save the current value of the editor first?
7633 // I think so ...
7634 DisableCellEditControl();
7635
7636 if ( m_table )
7637 {
7638 for ( row = 0; row < m_numRows; row++ )
7639 {
7640 for ( col = 0; col < m_numCols; col++ )
7641 {
7642 m_table->SetValue( row, col, GetCellValue(row, col) );
7643 }
7644 }
7645
7646 return true;
7647 }
7648
7649 return false;
7650 }
7651
7652 // Note - this function only draws cells that are in the list of
7653 // exposed cells (usually set from the update region by
7654 // CalcExposedCells)
7655 //
7656 void wxGrid::DrawGridCellArea( wxDC& dc, const wxGridCellCoordsArray& cells )
7657 {
7658 if ( !m_numRows || !m_numCols )
7659 return;
7660
7661 int i, numCells = cells.GetCount();
7662 int row, col, cell_rows, cell_cols;
7663 wxGridCellCoordsArray redrawCells;
7664
7665 for ( i = numCells - 1; i >= 0; i-- )
7666 {
7667 row = cells[i].GetRow();
7668 col = cells[i].GetCol();
7669 GetCellSize( row, col, &cell_rows, &cell_cols );
7670
7671 // If this cell is part of a multicell block, find owner for repaint
7672 if ( cell_rows <= 0 || cell_cols <= 0 )
7673 {
7674 wxGridCellCoords cell( row + cell_rows, col + cell_cols );
7675 bool marked = false;
7676 for ( int j = 0; j < numCells; j++ )
7677 {
7678 if ( cell == cells[j] )
7679 {
7680 marked = true;
7681 break;
7682 }
7683 }
7684
7685 if (!marked)
7686 {
7687 int count = redrawCells.GetCount();
7688 for (int j = 0; j < count; j++)
7689 {
7690 if ( cell == redrawCells[j] )
7691 {
7692 marked = true;
7693 break;
7694 }
7695 }
7696
7697 if (!marked)
7698 redrawCells.Add( cell );
7699 }
7700
7701 // don't bother drawing this cell
7702 continue;
7703 }
7704
7705 // If this cell is empty, find cell to left that might want to overflow
7706 if (m_table && m_table->IsEmptyCell(row, col))
7707 {
7708 for ( int l = 0; l < cell_rows; l++ )
7709 {
7710 // find a cell in this row to leave already marked for repaint
7711 int left = col;
7712 for (int k = 0; k < int(redrawCells.GetCount()); k++)
7713 if ((redrawCells[k].GetCol() < left) &&
7714 (redrawCells[k].GetRow() == row))
7715 {
7716 left = redrawCells[k].GetCol();
7717 }
7718
7719 if (left == col)
7720 left = 0; // oh well
7721
7722 for (int j = col - 1; j >= left; j--)
7723 {
7724 if (!m_table->IsEmptyCell(row + l, j))
7725 {
7726 if (GetCellOverflow(row + l, j))
7727 {
7728 wxGridCellCoords cell(row + l, j);
7729 bool marked = false;
7730
7731 for (int k = 0; k < numCells; k++)
7732 {
7733 if ( cell == cells[k] )
7734 {
7735 marked = true;
7736 break;
7737 }
7738 }
7739
7740 if (!marked)
7741 {
7742 int count = redrawCells.GetCount();
7743 for (int k = 0; k < count; k++)
7744 {
7745 if ( cell == redrawCells[k] )
7746 {
7747 marked = true;
7748 break;
7749 }
7750 }
7751 if (!marked)
7752 redrawCells.Add( cell );
7753 }
7754 }
7755 break;
7756 }
7757 }
7758 }
7759 }
7760
7761 DrawCell( dc, cells[i] );
7762 }
7763
7764 numCells = redrawCells.GetCount();
7765
7766 for ( i = numCells - 1; i >= 0; i-- )
7767 {
7768 DrawCell( dc, redrawCells[i] );
7769 }
7770 }
7771
7772 void wxGrid::DrawGridSpace( wxDC& dc )
7773 {
7774 int cw, ch;
7775 m_gridWin->GetClientSize( &cw, &ch );
7776
7777 int right, bottom;
7778 CalcUnscrolledPosition( cw, ch, &right, &bottom );
7779
7780 int rightCol = m_numCols > 0 ? GetColRight(GetColAt( m_numCols - 1 )) : 0;
7781 int bottomRow = m_numRows > 0 ? GetRowBottom(m_numRows - 1) : 0;
7782
7783 if ( right > rightCol || bottom > bottomRow )
7784 {
7785 int left, top;
7786 CalcUnscrolledPosition( 0, 0, &left, &top );
7787
7788 dc.SetBrush(GetDefaultCellBackgroundColour());
7789 dc.SetPen( *wxTRANSPARENT_PEN );
7790
7791 if ( right > rightCol )
7792 {
7793 dc.DrawRectangle( rightCol, top, right - rightCol, ch );
7794 }
7795
7796 if ( bottom > bottomRow )
7797 {
7798 dc.DrawRectangle( left, bottomRow, cw, bottom - bottomRow );
7799 }
7800 }
7801 }
7802
7803 void wxGrid::DrawCell( wxDC& dc, const wxGridCellCoords& coords )
7804 {
7805 int row = coords.GetRow();
7806 int col = coords.GetCol();
7807
7808 if ( GetColWidth(col) <= 0 || GetRowHeight(row) <= 0 )
7809 return;
7810
7811 // we draw the cell border ourselves
7812 wxGridCellAttr* attr = GetCellAttr(row, col);
7813
7814 bool isCurrent = coords == m_currentCellCoords;
7815
7816 wxRect rect = CellToRect( row, col );
7817
7818 // if the editor is shown, we should use it and not the renderer
7819 // Note: However, only if it is really _shown_, i.e. not hidden!
7820 if ( isCurrent && IsCellEditControlShown() )
7821 {
7822 // NB: this "#if..." is temporary and fixes a problem where the
7823 // edit control is erased by this code after being rendered.
7824 // On wxMac (QD build only), the cell editor is a wxTextCntl and is rendered
7825 // implicitly, causing this out-of order render.
7826 #if !defined(__WXMAC__)
7827 wxGridCellEditor *editor = attr->GetEditor(this, row, col);
7828 editor->PaintBackground(rect, attr);
7829 editor->DecRef();
7830 #endif
7831 }
7832 else
7833 {
7834 // but all the rest is drawn by the cell renderer and hence may be customized
7835 wxGridCellRenderer *renderer = attr->GetRenderer(this, row, col);
7836 renderer->Draw(*this, *attr, dc, rect, row, col, IsInSelection(coords));
7837 renderer->DecRef();
7838 }
7839
7840 attr->DecRef();
7841 }
7842
7843 void wxGrid::DrawCellHighlight( wxDC& dc, const wxGridCellAttr *attr )
7844 {
7845 // don't show highlight when the grid doesn't have focus
7846 if ( !HasFocus() )
7847 return;
7848
7849 int row = m_currentCellCoords.GetRow();
7850 int col = m_currentCellCoords.GetCol();
7851
7852 if ( GetColWidth(col) <= 0 || GetRowHeight(row) <= 0 )
7853 return;
7854
7855 wxRect rect = CellToRect(row, col);
7856
7857 // hmmm... what could we do here to show that the cell is disabled?
7858 // for now, I just draw a thinner border than for the other ones, but
7859 // it doesn't look really good
7860
7861 int penWidth = attr->IsReadOnly() ? m_cellHighlightROPenWidth : m_cellHighlightPenWidth;
7862
7863 if (penWidth > 0)
7864 {
7865 // The center of the drawn line is where the position/width/height of
7866 // the rectangle is actually at (on wxMSW at least), so the
7867 // size of the rectangle is reduced to compensate for the thickness of
7868 // the line. If this is too strange on non-wxMSW platforms then
7869 // please #ifdef this appropriately.
7870 rect.x += penWidth / 2;
7871 rect.y += penWidth / 2;
7872 rect.width -= penWidth - 1;
7873 rect.height -= penWidth - 1;
7874
7875 // Now draw the rectangle
7876 // use the cellHighlightColour if the cell is inside a selection, this
7877 // will ensure the cell is always visible.
7878 dc.SetPen(wxPen(IsInSelection(row,col) ? m_selectionForeground
7879 : m_cellHighlightColour,
7880 penWidth));
7881 dc.SetBrush(*wxTRANSPARENT_BRUSH);
7882 dc.DrawRectangle(rect);
7883 }
7884 }
7885
7886 wxPen wxGrid::GetDefaultGridLinePen()
7887 {
7888 return wxPen(GetGridLineColour());
7889 }
7890
7891 wxPen wxGrid::GetRowGridLinePen(int WXUNUSED(row))
7892 {
7893 return GetDefaultGridLinePen();
7894 }
7895
7896 wxPen wxGrid::GetColGridLinePen(int WXUNUSED(col))
7897 {
7898 return GetDefaultGridLinePen();
7899 }
7900
7901 void wxGrid::DrawCellBorder( wxDC& dc, const wxGridCellCoords& coords )
7902 {
7903 int row = coords.GetRow();
7904 int col = coords.GetCol();
7905 if ( GetColWidth(col) <= 0 || GetRowHeight(row) <= 0 )
7906 return;
7907
7908
7909 wxRect rect = CellToRect( row, col );
7910
7911 // right hand border
7912 dc.SetPen( GetColGridLinePen(col) );
7913 dc.DrawLine( rect.x + rect.width, rect.y,
7914 rect.x + rect.width, rect.y + rect.height + 1 );
7915
7916 // bottom border
7917 dc.SetPen( GetRowGridLinePen(row) );
7918 dc.DrawLine( rect.x, rect.y + rect.height,
7919 rect.x + rect.width, rect.y + rect.height);
7920 }
7921
7922 void wxGrid::DrawHighlight(wxDC& dc, const wxGridCellCoordsArray& cells)
7923 {
7924 // This if block was previously in wxGrid::OnPaint but that doesn't
7925 // seem to get called under wxGTK - MB
7926 //
7927 if ( m_currentCellCoords == wxGridNoCellCoords &&
7928 m_numRows && m_numCols )
7929 {
7930 m_currentCellCoords.Set(0, 0);
7931 }
7932
7933 if ( IsCellEditControlShown() )
7934 {
7935 // don't show highlight when the edit control is shown
7936 return;
7937 }
7938
7939 // if the active cell was repainted, repaint its highlight too because it
7940 // might have been damaged by the grid lines
7941 size_t count = cells.GetCount();
7942 for ( size_t n = 0; n < count; n++ )
7943 {
7944 wxGridCellCoords cell = cells[n];
7945
7946 // If we are using attributes, then we may have just exposed another
7947 // cell in a partially-visible merged cluster of cells. If the "anchor"
7948 // (upper left) cell of this merged cluster is the cell indicated by
7949 // m_currentCellCoords, then we need to refresh the cell highlight even
7950 // though the "anchor" itself is not part of our update segment.
7951 if ( CanHaveAttributes() )
7952 {
7953 int rows = 0,
7954 cols = 0;
7955 GetCellSize(cell.GetRow(), cell.GetCol(), &rows, &cols);
7956
7957 if ( rows < 0 )
7958 cell.SetRow(cell.GetRow() + rows);
7959
7960 if ( cols < 0 )
7961 cell.SetCol(cell.GetCol() + cols);
7962 }
7963
7964 if ( cell == m_currentCellCoords )
7965 {
7966 wxGridCellAttr* attr = GetCellAttr(m_currentCellCoords);
7967 DrawCellHighlight(dc, attr);
7968 attr->DecRef();
7969
7970 break;
7971 }
7972 }
7973 }
7974
7975 // This is used to redraw all grid lines e.g. when the grid line colour
7976 // has been changed
7977 //
7978 void wxGrid::DrawAllGridLines( wxDC& dc, const wxRegion & WXUNUSED(reg) )
7979 {
7980 if ( !m_gridLinesEnabled )
7981 return;
7982
7983 int top, bottom, left, right;
7984
7985 int cw, ch;
7986 m_gridWin->GetClientSize(&cw, &ch);
7987 CalcUnscrolledPosition( 0, 0, &left, &top );
7988 CalcUnscrolledPosition( cw, ch, &right, &bottom );
7989
7990 // avoid drawing grid lines past the last row and col
7991 if ( m_gridLinesClipHorz )
7992 {
7993 if ( !m_numCols )
7994 return;
7995
7996 const int lastColRight = GetColRight(GetColAt(m_numCols - 1));
7997 if ( right > lastColRight )
7998 right = lastColRight;
7999 }
8000
8001 if ( m_gridLinesClipVert )
8002 {
8003 if ( !m_numRows )
8004 return;
8005
8006 const int lastRowBottom = GetRowBottom(m_numRows - 1);
8007 if ( bottom > lastRowBottom )
8008 bottom = lastRowBottom;
8009 }
8010
8011 // no gridlines inside multicells, clip them out
8012 int leftCol = GetColPos( internalXToCol(left) );
8013 int topRow = internalYToRow(top);
8014 int rightCol = GetColPos( internalXToCol(right) );
8015 int bottomRow = internalYToRow(bottom);
8016
8017 wxRegion clippedcells(0, 0, cw, ch);
8018
8019 int cell_rows, cell_cols;
8020 wxRect rect;
8021
8022 for ( int j = topRow; j <= bottomRow; j++ )
8023 {
8024 for ( int colPos = leftCol; colPos <= rightCol; colPos++ )
8025 {
8026 int i = GetColAt( colPos );
8027
8028 GetCellSize( j, i, &cell_rows, &cell_cols );
8029 if ((cell_rows > 1) || (cell_cols > 1))
8030 {
8031 rect = CellToRect(j,i);
8032 CalcScrolledPosition( rect.x, rect.y, &rect.x, &rect.y );
8033 clippedcells.Subtract(rect);
8034 }
8035 else if ((cell_rows < 0) || (cell_cols < 0))
8036 {
8037 rect = CellToRect(j + cell_rows, i + cell_cols);
8038 CalcScrolledPosition( rect.x, rect.y, &rect.x, &rect.y );
8039 clippedcells.Subtract(rect);
8040 }
8041 }
8042 }
8043
8044 dc.SetDeviceClippingRegion( clippedcells );
8045
8046
8047 // horizontal grid lines
8048 for ( int i = internalYToRow(top); i < m_numRows; i++ )
8049 {
8050 int bot = GetRowBottom(i) - 1;
8051
8052 if ( bot > bottom )
8053 break;
8054
8055 if ( bot >= top )
8056 {
8057 dc.SetPen( GetRowGridLinePen(i) );
8058 dc.DrawLine( left, bot, right, bot );
8059 }
8060 }
8061
8062 // vertical grid lines
8063 for ( int colPos = leftCol; colPos < m_numCols; colPos++ )
8064 {
8065 int i = GetColAt( colPos );
8066
8067 int colRight = GetColRight(i);
8068 #ifdef __WXGTK__
8069 if (GetLayoutDirection() != wxLayout_RightToLeft)
8070 #endif
8071 colRight--;
8072
8073 if ( colRight > right )
8074 break;
8075
8076 if ( colRight >= left )
8077 {
8078 dc.SetPen( GetColGridLinePen(i) );
8079 dc.DrawLine( colRight, top, colRight, bottom );
8080 }
8081 }
8082
8083 dc.DestroyClippingRegion();
8084 }
8085
8086 void wxGrid::DrawRowLabels( wxDC& dc, const wxArrayInt& rows)
8087 {
8088 if ( !m_numRows )
8089 return;
8090
8091 const size_t numLabels = rows.GetCount();
8092 for ( size_t i = 0; i < numLabels; i++ )
8093 {
8094 DrawRowLabel( dc, rows[i] );
8095 }
8096 }
8097
8098 void wxGrid::DrawRowLabel( wxDC& dc, int row )
8099 {
8100 if ( GetRowHeight(row) <= 0 || m_rowLabelWidth <= 0 )
8101 return;
8102
8103 wxRect rect;
8104
8105 int rowTop = GetRowTop(row),
8106 rowBottom = GetRowBottom(row) - 1;
8107
8108 dc.SetPen( wxPen(wxSystemSettings::GetColour(wxSYS_COLOUR_3DSHADOW)));
8109 dc.DrawLine( m_rowLabelWidth - 1, rowTop, m_rowLabelWidth - 1, rowBottom );
8110 dc.DrawLine( 0, rowTop, 0, rowBottom );
8111 dc.DrawLine( 0, rowBottom, m_rowLabelWidth, rowBottom );
8112
8113 dc.SetPen( *wxWHITE_PEN );
8114 dc.DrawLine( 1, rowTop, 1, rowBottom );
8115 dc.DrawLine( 1, rowTop, m_rowLabelWidth - 1, rowTop );
8116
8117 dc.SetBackgroundMode( wxBRUSHSTYLE_TRANSPARENT );
8118 dc.SetTextForeground( GetLabelTextColour() );
8119 dc.SetFont( GetLabelFont() );
8120
8121 int hAlign, vAlign;
8122 GetRowLabelAlignment( &hAlign, &vAlign );
8123
8124 rect.SetX( 2 );
8125 rect.SetY( GetRowTop(row) + 2 );
8126 rect.SetWidth( m_rowLabelWidth - 4 );
8127 rect.SetHeight( GetRowHeight(row) - 4 );
8128 DrawTextRectangle( dc, GetRowLabelValue( row ), rect, hAlign, vAlign );
8129 }
8130
8131 void wxGrid::UseNativeColHeader(bool native)
8132 {
8133 if ( native == m_useNativeHeader )
8134 return;
8135
8136 delete m_colWindow;
8137 m_useNativeHeader = native;
8138
8139 CreateColumnWindow();
8140
8141 if ( m_useNativeHeader )
8142 GetColHeader()->SetColumnCount(m_numCols);
8143 CalcWindowSizes();
8144 }
8145
8146 void wxGrid::SetUseNativeColLabels( bool native )
8147 {
8148 wxASSERT_MSG( !m_useNativeHeader,
8149 "doesn't make sense when using native header" );
8150
8151 m_nativeColumnLabels = native;
8152 if (native)
8153 {
8154 int height = wxRendererNative::Get().GetHeaderButtonHeight( this );
8155 SetColLabelSize( height );
8156 }
8157
8158 GetColLabelWindow()->Refresh();
8159 m_cornerLabelWin->Refresh();
8160 }
8161
8162 void wxGrid::DrawColLabels( wxDC& dc,const wxArrayInt& cols )
8163 {
8164 if ( !m_numCols )
8165 return;
8166
8167 const size_t numLabels = cols.GetCount();
8168 for ( size_t i = 0; i < numLabels; i++ )
8169 {
8170 DrawColLabel( dc, cols[i] );
8171 }
8172 }
8173
8174 void wxGrid::DrawCornerLabel(wxDC& dc)
8175 {
8176 if ( m_nativeColumnLabels )
8177 {
8178 wxRect rect(wxSize(m_rowLabelWidth, m_colLabelHeight));
8179 rect.Deflate(1);
8180
8181 wxRendererNative::Get().DrawHeaderButton(m_cornerLabelWin, dc, rect, 0);
8182 }
8183 else
8184 {
8185 dc.SetPen(wxPen(wxSystemSettings::GetColour(wxSYS_COLOUR_3DSHADOW)));
8186 dc.DrawLine( m_rowLabelWidth - 1, m_colLabelHeight - 1,
8187 m_rowLabelWidth - 1, 0 );
8188 dc.DrawLine( m_rowLabelWidth - 1, m_colLabelHeight - 1,
8189 0, m_colLabelHeight - 1 );
8190 dc.DrawLine( 0, 0, m_rowLabelWidth, 0 );
8191 dc.DrawLine( 0, 0, 0, m_colLabelHeight );
8192
8193 dc.SetPen( *wxWHITE_PEN );
8194 dc.DrawLine( 1, 1, m_rowLabelWidth - 1, 1 );
8195 dc.DrawLine( 1, 1, 1, m_colLabelHeight - 1 );
8196 }
8197 }
8198
8199 void wxGrid::DrawColLabel(wxDC& dc, int col)
8200 {
8201 if ( GetColWidth(col) <= 0 || m_colLabelHeight <= 0 )
8202 return;
8203
8204 int colLeft = GetColLeft(col);
8205
8206 wxRect rect(colLeft, 0, GetColWidth(col), m_colLabelHeight);
8207
8208 if ( m_nativeColumnLabels )
8209 {
8210 wxRendererNative::Get().DrawHeaderButton(GetColLabelWindow(), dc, rect, 0);
8211 }
8212 else
8213 {
8214 int colRight = GetColRight(col) - 1;
8215
8216 dc.SetPen(wxPen(wxSystemSettings::GetColour(wxSYS_COLOUR_3DSHADOW)));
8217 dc.DrawLine( colRight, 0,
8218 colRight, m_colLabelHeight - 1 );
8219 dc.DrawLine( colLeft, 0,
8220 colRight, 0 );
8221 dc.DrawLine( colLeft, m_colLabelHeight - 1,
8222 colRight + 1, m_colLabelHeight - 1 );
8223
8224 dc.SetPen( *wxWHITE_PEN );
8225 dc.DrawLine( colLeft, 1, colLeft, m_colLabelHeight - 1 );
8226 dc.DrawLine( colLeft, 1, colRight, 1 );
8227 }
8228
8229 dc.SetBackgroundMode( wxBRUSHSTYLE_TRANSPARENT );
8230 dc.SetTextForeground( GetLabelTextColour() );
8231 dc.SetFont( GetLabelFont() );
8232
8233 int hAlign, vAlign;
8234 GetColLabelAlignment( &hAlign, &vAlign );
8235 const int orient = GetColLabelTextOrientation();
8236
8237 rect.Deflate(2);
8238 DrawTextRectangle(dc, GetColLabelValue(col), rect, hAlign, vAlign, orient);
8239 }
8240
8241 // TODO: these 2 functions should be replaced with wxDC::DrawLabel() to which
8242 // we just have to add textOrientation support
8243 void wxGrid::DrawTextRectangle( wxDC& dc,
8244 const wxString& value,
8245 const wxRect& rect,
8246 int horizAlign,
8247 int vertAlign,
8248 int textOrientation )
8249 {
8250 wxArrayString lines;
8251
8252 StringToLines( value, lines );
8253
8254 DrawTextRectangle(dc, lines, rect, horizAlign, vertAlign, textOrientation);
8255 }
8256
8257 void wxGrid::DrawTextRectangle(wxDC& dc,
8258 const wxArrayString& lines,
8259 const wxRect& rect,
8260 int horizAlign,
8261 int vertAlign,
8262 int textOrientation)
8263 {
8264 if ( lines.empty() )
8265 return;
8266
8267 wxDCClipper clip(dc, rect);
8268
8269 long textWidth,
8270 textHeight;
8271
8272 if ( textOrientation == wxHORIZONTAL )
8273 GetTextBoxSize( dc, lines, &textWidth, &textHeight );
8274 else
8275 GetTextBoxSize( dc, lines, &textHeight, &textWidth );
8276
8277 int x = 0,
8278 y = 0;
8279 switch ( vertAlign )
8280 {
8281 case wxALIGN_BOTTOM:
8282 if ( textOrientation == wxHORIZONTAL )
8283 y = rect.y + (rect.height - textHeight - 1);
8284 else
8285 x = rect.x + rect.width - textWidth;
8286 break;
8287
8288 case wxALIGN_CENTRE:
8289 if ( textOrientation == wxHORIZONTAL )
8290 y = rect.y + ((rect.height - textHeight) / 2);
8291 else
8292 x = rect.x + ((rect.width - textWidth) / 2);
8293 break;
8294
8295 case wxALIGN_TOP:
8296 default:
8297 if ( textOrientation == wxHORIZONTAL )
8298 y = rect.y + 1;
8299 else
8300 x = rect.x + 1;
8301 break;
8302 }
8303
8304 // Align each line of a multi-line label
8305 size_t nLines = lines.GetCount();
8306 for ( size_t l = 0; l < nLines; l++ )
8307 {
8308 const wxString& line = lines[l];
8309
8310 if ( line.empty() )
8311 {
8312 *(textOrientation == wxHORIZONTAL ? &y : &x) += dc.GetCharHeight();
8313 continue;
8314 }
8315
8316 wxCoord lineWidth = 0,
8317 lineHeight = 0;
8318 dc.GetTextExtent(line, &lineWidth, &lineHeight);
8319
8320 switch ( horizAlign )
8321 {
8322 case wxALIGN_RIGHT:
8323 if ( textOrientation == wxHORIZONTAL )
8324 x = rect.x + (rect.width - lineWidth - 1);
8325 else
8326 y = rect.y + lineWidth + 1;
8327 break;
8328
8329 case wxALIGN_CENTRE:
8330 if ( textOrientation == wxHORIZONTAL )
8331 x = rect.x + ((rect.width - lineWidth) / 2);
8332 else
8333 y = rect.y + rect.height - ((rect.height - lineWidth) / 2);
8334 break;
8335
8336 case wxALIGN_LEFT:
8337 default:
8338 if ( textOrientation == wxHORIZONTAL )
8339 x = rect.x + 1;
8340 else
8341 y = rect.y + rect.height - 1;
8342 break;
8343 }
8344
8345 if ( textOrientation == wxHORIZONTAL )
8346 {
8347 dc.DrawText( line, x, y );
8348 y += lineHeight;
8349 }
8350 else
8351 {
8352 dc.DrawRotatedText( line, x, y, 90.0 );
8353 x += lineHeight;
8354 }
8355 }
8356 }
8357
8358 // Split multi-line text up into an array of strings.
8359 // Any existing contents of the string array are preserved.
8360 //
8361 // TODO: refactor wxTextFile::Read() and reuse the same code from here
8362 void wxGrid::StringToLines( const wxString& value, wxArrayString& lines ) const
8363 {
8364 int startPos = 0;
8365 int pos;
8366 wxString eol = wxTextFile::GetEOL( wxTextFileType_Unix );
8367 wxString tVal = wxTextFile::Translate( value, wxTextFileType_Unix );
8368
8369 while ( startPos < (int)tVal.length() )
8370 {
8371 pos = tVal.Mid(startPos).Find( eol );
8372 if ( pos < 0 )
8373 {
8374 break;
8375 }
8376 else if ( pos == 0 )
8377 {
8378 lines.Add( wxEmptyString );
8379 }
8380 else
8381 {
8382 lines.Add( tVal.Mid(startPos, pos) );
8383 }
8384
8385 startPos += pos + 1;
8386 }
8387
8388 if ( startPos < (int)tVal.length() )
8389 {
8390 lines.Add( tVal.Mid( startPos ) );
8391 }
8392 }
8393
8394 void wxGrid::GetTextBoxSize( const wxDC& dc,
8395 const wxArrayString& lines,
8396 long *width, long *height ) const
8397 {
8398 wxCoord w = 0;
8399 wxCoord h = 0;
8400 wxCoord lineW = 0, lineH = 0;
8401
8402 size_t i;
8403 for ( i = 0; i < lines.GetCount(); i++ )
8404 {
8405 dc.GetTextExtent( lines[i], &lineW, &lineH );
8406 w = wxMax( w, lineW );
8407 h += lineH;
8408 }
8409
8410 *width = w;
8411 *height = h;
8412 }
8413
8414 //
8415 // ------ Batch processing.
8416 //
8417 void wxGrid::EndBatch()
8418 {
8419 if ( m_batchCount > 0 )
8420 {
8421 m_batchCount--;
8422 if ( !m_batchCount )
8423 {
8424 CalcDimensions();
8425 m_rowLabelWin->Refresh();
8426 m_colWindow->Refresh();
8427 m_cornerLabelWin->Refresh();
8428 m_gridWin->Refresh();
8429 }
8430 }
8431 }
8432
8433 // Use this, rather than wxWindow::Refresh(), to force an immediate
8434 // repainting of the grid. Has no effect if you are already inside a
8435 // BeginBatch / EndBatch block.
8436 //
8437 void wxGrid::ForceRefresh()
8438 {
8439 BeginBatch();
8440 EndBatch();
8441 }
8442
8443 bool wxGrid::Enable(bool enable)
8444 {
8445 if ( !wxScrolledWindow::Enable(enable) )
8446 return false;
8447
8448 // redraw in the new state
8449 m_gridWin->Refresh();
8450
8451 return true;
8452 }
8453
8454 //
8455 // ------ Edit control functions
8456 //
8457
8458 void wxGrid::EnableEditing( bool edit )
8459 {
8460 if ( edit != m_editable )
8461 {
8462 if (!edit)
8463 EnableCellEditControl(edit);
8464 m_editable = edit;
8465 }
8466 }
8467
8468 void wxGrid::EnableCellEditControl( bool enable )
8469 {
8470 if (! m_editable)
8471 return;
8472
8473 if ( enable != m_cellEditCtrlEnabled )
8474 {
8475 if ( enable )
8476 {
8477 if ( SendEvent(wxEVT_GRID_EDITOR_SHOWN) == -1 )
8478 return;
8479
8480 // this should be checked by the caller!
8481 wxASSERT_MSG( CanEnableCellControl(), _T("can't enable editing for this cell!") );
8482
8483 // do it before ShowCellEditControl()
8484 m_cellEditCtrlEnabled = enable;
8485
8486 ShowCellEditControl();
8487 }
8488 else
8489 {
8490 //FIXME:add veto support
8491 SendEvent(wxEVT_GRID_EDITOR_HIDDEN);
8492
8493 HideCellEditControl();
8494 SaveEditControlValue();
8495
8496 // do it after HideCellEditControl()
8497 m_cellEditCtrlEnabled = enable;
8498 }
8499 }
8500 }
8501
8502 bool wxGrid::IsCurrentCellReadOnly() const
8503 {
8504 // const_cast
8505 wxGridCellAttr* attr = ((wxGrid *)this)->GetCellAttr(m_currentCellCoords);
8506 bool readonly = attr->IsReadOnly();
8507 attr->DecRef();
8508
8509 return readonly;
8510 }
8511
8512 bool wxGrid::CanEnableCellControl() const
8513 {
8514 return m_editable && (m_currentCellCoords != wxGridNoCellCoords) &&
8515 !IsCurrentCellReadOnly();
8516 }
8517
8518 bool wxGrid::IsCellEditControlEnabled() const
8519 {
8520 // the cell edit control might be disable for all cells or just for the
8521 // current one if it's read only
8522 return m_cellEditCtrlEnabled ? !IsCurrentCellReadOnly() : false;
8523 }
8524
8525 bool wxGrid::IsCellEditControlShown() const
8526 {
8527 bool isShown = false;
8528
8529 if ( m_cellEditCtrlEnabled )
8530 {
8531 int row = m_currentCellCoords.GetRow();
8532 int col = m_currentCellCoords.GetCol();
8533 wxGridCellAttr* attr = GetCellAttr(row, col);
8534 wxGridCellEditor* editor = attr->GetEditor((wxGrid*) this, row, col);
8535 attr->DecRef();
8536
8537 if ( editor )
8538 {
8539 if ( editor->IsCreated() )
8540 {
8541 isShown = editor->GetControl()->IsShown();
8542 }
8543
8544 editor->DecRef();
8545 }
8546 }
8547
8548 return isShown;
8549 }
8550
8551 void wxGrid::ShowCellEditControl()
8552 {
8553 if ( IsCellEditControlEnabled() )
8554 {
8555 if ( !IsVisible( m_currentCellCoords, false ) )
8556 {
8557 m_cellEditCtrlEnabled = false;
8558 return;
8559 }
8560 else
8561 {
8562 wxRect rect = CellToRect( m_currentCellCoords );
8563 int row = m_currentCellCoords.GetRow();
8564 int col = m_currentCellCoords.GetCol();
8565
8566 // if this is part of a multicell, find owner (topleft)
8567 int cell_rows, cell_cols;
8568 GetCellSize( row, col, &cell_rows, &cell_cols );
8569 if ( cell_rows <= 0 || cell_cols <= 0 )
8570 {
8571 row += cell_rows;
8572 col += cell_cols;
8573 m_currentCellCoords.SetRow( row );
8574 m_currentCellCoords.SetCol( col );
8575 }
8576
8577 // erase the highlight and the cell contents because the editor
8578 // might not cover the entire cell
8579 wxClientDC dc( m_gridWin );
8580 PrepareDC( dc );
8581 wxGridCellAttr* attr = GetCellAttr(row, col);
8582 dc.SetBrush(wxBrush(attr->GetBackgroundColour()));
8583 dc.SetPen(*wxTRANSPARENT_PEN);
8584 dc.DrawRectangle(rect);
8585
8586 // convert to scrolled coords
8587 CalcScrolledPosition( rect.x, rect.y, &rect.x, &rect.y );
8588
8589 int nXMove = 0;
8590 if (rect.x < 0)
8591 nXMove = rect.x;
8592
8593 // cell is shifted by one pixel
8594 // However, don't allow x or y to become negative
8595 // since the SetSize() method interprets that as
8596 // "don't change."
8597 if (rect.x > 0)
8598 rect.x--;
8599 if (rect.y > 0)
8600 rect.y--;
8601
8602 wxGridCellEditor* editor = attr->GetEditor(this, row, col);
8603 if ( !editor->IsCreated() )
8604 {
8605 editor->Create(m_gridWin, wxID_ANY,
8606 new wxGridCellEditorEvtHandler(this, editor));
8607
8608 wxGridEditorCreatedEvent evt(GetId(),
8609 wxEVT_GRID_EDITOR_CREATED,
8610 this,
8611 row,
8612 col,
8613 editor->GetControl());
8614 GetEventHandler()->ProcessEvent(evt);
8615 }
8616
8617 // resize editor to overflow into righthand cells if allowed
8618 int maxWidth = rect.width;
8619 wxString value = GetCellValue(row, col);
8620 if ( (value != wxEmptyString) && (attr->GetOverflow()) )
8621 {
8622 int y;
8623 GetTextExtent(value, &maxWidth, &y, NULL, NULL, &attr->GetFont());
8624 if (maxWidth < rect.width)
8625 maxWidth = rect.width;
8626 }
8627
8628 int client_right = m_gridWin->GetClientSize().GetWidth();
8629 if (rect.x + maxWidth > client_right)
8630 maxWidth = client_right - rect.x;
8631
8632 if ((maxWidth > rect.width) && (col < m_numCols) && m_table)
8633 {
8634 GetCellSize( row, col, &cell_rows, &cell_cols );
8635 // may have changed earlier
8636 for (int i = col + cell_cols; i < m_numCols; i++)
8637 {
8638 int c_rows, c_cols;
8639 GetCellSize( row, i, &c_rows, &c_cols );
8640
8641 // looks weird going over a multicell
8642 if (m_table->IsEmptyCell( row, i ) &&
8643 (rect.width < maxWidth) && (c_rows == 1))
8644 {
8645 rect.width += GetColWidth( i );
8646 }
8647 else
8648 break;
8649 }
8650
8651 if (rect.GetRight() > client_right)
8652 rect.SetRight( client_right - 1 );
8653 }
8654
8655 editor->SetCellAttr( attr );
8656 editor->SetSize( rect );
8657 if (nXMove != 0)
8658 editor->GetControl()->Move(
8659 editor->GetControl()->GetPosition().x + nXMove,
8660 editor->GetControl()->GetPosition().y );
8661 editor->Show( true, attr );
8662
8663 // recalc dimensions in case we need to
8664 // expand the scrolled window to account for editor
8665 CalcDimensions();
8666
8667 editor->BeginEdit(row, col, this);
8668 editor->SetCellAttr(NULL);
8669
8670 editor->DecRef();
8671 attr->DecRef();
8672 }
8673 }
8674 }
8675
8676 void wxGrid::HideCellEditControl()
8677 {
8678 if ( IsCellEditControlEnabled() )
8679 {
8680 int row = m_currentCellCoords.GetRow();
8681 int col = m_currentCellCoords.GetCol();
8682
8683 wxGridCellAttr *attr = GetCellAttr(row, col);
8684 wxGridCellEditor *editor = attr->GetEditor(this, row, col);
8685 const bool editorHadFocus = editor->GetControl()->HasFocus();
8686 editor->Show( false );
8687 editor->DecRef();
8688 attr->DecRef();
8689
8690 // return the focus to the grid itself if the editor had it
8691 //
8692 // note that we must not do this unconditionally to avoid stealing
8693 // focus from the window which just received it if we are hiding the
8694 // editor precisely because we lost focus
8695 if ( editorHadFocus )
8696 m_gridWin->SetFocus();
8697
8698 // refresh whole row to the right
8699 wxRect rect( CellToRect(row, col) );
8700 CalcScrolledPosition(rect.x, rect.y, &rect.x, &rect.y );
8701 rect.width = m_gridWin->GetClientSize().GetWidth() - rect.x;
8702
8703 #ifdef __WXMAC__
8704 // ensure that the pixels under the focus ring get refreshed as well
8705 rect.Inflate(10, 10);
8706 #endif
8707
8708 m_gridWin->Refresh( false, &rect );
8709 }
8710 }
8711
8712 void wxGrid::SaveEditControlValue()
8713 {
8714 if ( IsCellEditControlEnabled() )
8715 {
8716 int row = m_currentCellCoords.GetRow();
8717 int col = m_currentCellCoords.GetCol();
8718
8719 wxString oldval = GetCellValue(row, col);
8720
8721 wxGridCellAttr* attr = GetCellAttr(row, col);
8722 wxGridCellEditor* editor = attr->GetEditor(this, row, col);
8723 bool changed = editor->EndEdit(row, col, this);
8724
8725 editor->DecRef();
8726 attr->DecRef();
8727
8728 if (changed)
8729 {
8730 if ( SendEvent(wxEVT_GRID_CELL_CHANGE) == -1 )
8731 {
8732 // Event has been vetoed, set the data back.
8733 SetCellValue(row, col, oldval);
8734 }
8735 }
8736 }
8737 }
8738
8739 //
8740 // ------ Grid location functions
8741 // Note that all of these functions work with the logical coordinates of
8742 // grid cells and labels so you will need to convert from device
8743 // coordinates for mouse events etc.
8744 //
8745
8746 wxGridCellCoords wxGrid::XYToCell(int x, int y) const
8747 {
8748 int row = YToRow(y);
8749 int col = XToCol(x);
8750
8751 return row == -1 || col == -1 ? wxGridNoCellCoords
8752 : wxGridCellCoords(row, col);
8753 }
8754
8755 // compute row or column from some (unscrolled) coordinate value, using either
8756 // m_defaultRowHeight/m_defaultColWidth or binary search on array of
8757 // m_rowBottoms/m_colRights to do it quickly (linear search shouldn't be used
8758 // for large grids)
8759 int
8760 wxGrid::PosToLine(int coord,
8761 bool clipToMinMax,
8762 const wxGridOperations& oper) const
8763 {
8764 const int numLines = oper.GetNumberOfLines(this);
8765
8766 if ( coord < 0 )
8767 return clipToMinMax && numLines > 0 ? oper.GetLineAt(this, 0) : -1;
8768
8769 const int defaultLineSize = oper.GetDefaultLineSize(this);
8770 wxCHECK_MSG( defaultLineSize, -1, "can't have 0 default line size" );
8771
8772 int maxPos = coord / defaultLineSize,
8773 minPos = 0;
8774
8775 // check for the simplest case: if we have no explicit line sizes
8776 // configured, then we already know the line this position falls in
8777 const wxArrayInt& lineEnds = oper.GetLineEnds(this);
8778 if ( lineEnds.empty() )
8779 {
8780 if ( maxPos < numLines )
8781 return maxPos;
8782
8783 return clipToMinMax ? numLines - 1 : -1;
8784 }
8785
8786
8787 // adjust maxPos before starting the binary search
8788 if ( maxPos >= numLines )
8789 {
8790 maxPos = numLines - 1;
8791 }
8792 else
8793 {
8794 if ( coord >= lineEnds[oper.GetLineAt(this, maxPos)])
8795 {
8796 minPos = maxPos;
8797 const int minDist = oper.GetMinimalAcceptableLineSize(this);
8798 if ( minDist )
8799 maxPos = coord / minDist;
8800 else
8801 maxPos = numLines - 1;
8802 }
8803
8804 if ( maxPos >= numLines )
8805 maxPos = numLines - 1;
8806 }
8807
8808 // check if the position is beyond the last column
8809 const int lineAtMaxPos = oper.GetLineAt(this, maxPos);
8810 if ( coord >= lineEnds[lineAtMaxPos] )
8811 return clipToMinMax ? lineAtMaxPos : -1;
8812
8813 // or before the first one
8814 const int lineAt0 = oper.GetLineAt(this, 0);
8815 if ( coord < lineEnds[lineAt0] )
8816 return lineAt0;
8817
8818
8819 // finally do perform the binary search
8820 while ( minPos < maxPos )
8821 {
8822 wxCHECK_MSG( lineEnds[oper.GetLineAt(this, minPos)] <= coord &&
8823 coord < lineEnds[oper.GetLineAt(this, maxPos)],
8824 -1,
8825 "wxGrid: internal error in PosToLine()" );
8826
8827 if ( coord >= lineEnds[oper.GetLineAt(this, maxPos - 1)] )
8828 return oper.GetLineAt(this, maxPos);
8829 else
8830 maxPos--;
8831
8832 const int median = minPos + (maxPos - minPos + 1) / 2;
8833 if ( coord < lineEnds[oper.GetLineAt(this, median)] )
8834 maxPos = median;
8835 else
8836 minPos = median;
8837 }
8838
8839 return oper.GetLineAt(this, maxPos);
8840 }
8841
8842 int wxGrid::YToRow(int y, bool clipToMinMax) const
8843 {
8844 return PosToLine(y, clipToMinMax, wxGridRowOperations());
8845 }
8846
8847 int wxGrid::XToCol(int x, bool clipToMinMax) const
8848 {
8849 return PosToLine(x, clipToMinMax, wxGridColumnOperations());
8850 }
8851
8852 // return the row number that that the y coord is near the edge of, or -1 if
8853 // not near an edge.
8854 //
8855 // coords can only possibly be near an edge if
8856 // (a) the row/column is large enough to still allow for an "inner" area
8857 // that is _not_ near the edge (i.e., if the height/width is smaller
8858 // than WXGRID_LABEL_EDGE_ZONE, coords are _never_ considered to be
8859 // near the edge).
8860 // and
8861 // (b) resizing rows/columns (the thing for which edge detection is
8862 // relevant at all) is enabled.
8863 //
8864 int wxGrid::PosToEdgeOfLine(int pos, const wxGridOperations& oper) const
8865 {
8866 if ( !oper.CanResizeLines(this) )
8867 return -1;
8868
8869 const int line = oper.PosToLine(this, pos, true);
8870
8871 if ( oper.GetLineSize(this, line) > WXGRID_LABEL_EDGE_ZONE )
8872 {
8873 // We know that we are in this line, test whether we are close enough
8874 // to start or end border, respectively.
8875 if ( abs(oper.GetLineEndPos(this, line) - pos) < WXGRID_LABEL_EDGE_ZONE )
8876 return line;
8877 else if ( line > 0 &&
8878 pos - oper.GetLineStartPos(this,
8879 line) < WXGRID_LABEL_EDGE_ZONE )
8880 return line - 1;
8881 }
8882
8883 return -1;
8884 }
8885
8886 int wxGrid::YToEdgeOfRow(int y) const
8887 {
8888 return PosToEdgeOfLine(y, wxGridRowOperations());
8889 }
8890
8891 int wxGrid::XToEdgeOfCol(int x) const
8892 {
8893 return PosToEdgeOfLine(x, wxGridColumnOperations());
8894 }
8895
8896 wxRect wxGrid::CellToRect( int row, int col ) const
8897 {
8898 wxRect rect( -1, -1, -1, -1 );
8899
8900 if ( row >= 0 && row < m_numRows &&
8901 col >= 0 && col < m_numCols )
8902 {
8903 int i, cell_rows, cell_cols;
8904 rect.width = rect.height = 0;
8905 GetCellSize( row, col, &cell_rows, &cell_cols );
8906 // if negative then find multicell owner
8907 if (cell_rows < 0)
8908 row += cell_rows;
8909 if (cell_cols < 0)
8910 col += cell_cols;
8911 GetCellSize( row, col, &cell_rows, &cell_cols );
8912
8913 rect.x = GetColLeft(col);
8914 rect.y = GetRowTop(row);
8915 for (i=col; i < col + cell_cols; i++)
8916 rect.width += GetColWidth(i);
8917 for (i=row; i < row + cell_rows; i++)
8918 rect.height += GetRowHeight(i);
8919 }
8920
8921 // if grid lines are enabled, then the area of the cell is a bit smaller
8922 if (m_gridLinesEnabled)
8923 {
8924 rect.width -= 1;
8925 rect.height -= 1;
8926 }
8927
8928 return rect;
8929 }
8930
8931 bool wxGrid::IsVisible( int row, int col, bool wholeCellVisible ) const
8932 {
8933 // get the cell rectangle in logical coords
8934 //
8935 wxRect r( CellToRect( row, col ) );
8936
8937 // convert to device coords
8938 //
8939 int left, top, right, bottom;
8940 CalcScrolledPosition( r.GetLeft(), r.GetTop(), &left, &top );
8941 CalcScrolledPosition( r.GetRight(), r.GetBottom(), &right, &bottom );
8942
8943 // check against the client area of the grid window
8944 int cw, ch;
8945 m_gridWin->GetClientSize( &cw, &ch );
8946
8947 if ( wholeCellVisible )
8948 {
8949 // is the cell wholly visible ?
8950 return ( left >= 0 && right <= cw &&
8951 top >= 0 && bottom <= ch );
8952 }
8953 else
8954 {
8955 // is the cell partly visible ?
8956 //
8957 return ( ((left >= 0 && left < cw) || (right > 0 && right <= cw)) &&
8958 ((top >= 0 && top < ch) || (bottom > 0 && bottom <= ch)) );
8959 }
8960 }
8961
8962 // make the specified cell location visible by doing a minimal amount
8963 // of scrolling
8964 //
8965 void wxGrid::MakeCellVisible( int row, int col )
8966 {
8967 int i;
8968 int xpos = -1, ypos = -1;
8969
8970 if ( row >= 0 && row < m_numRows &&
8971 col >= 0 && col < m_numCols )
8972 {
8973 // get the cell rectangle in logical coords
8974 wxRect r( CellToRect( row, col ) );
8975
8976 // convert to device coords
8977 int left, top, right, bottom;
8978 CalcScrolledPosition( r.GetLeft(), r.GetTop(), &left, &top );
8979 CalcScrolledPosition( r.GetRight(), r.GetBottom(), &right, &bottom );
8980
8981 int cw, ch;
8982 m_gridWin->GetClientSize( &cw, &ch );
8983
8984 if ( top < 0 )
8985 {
8986 ypos = r.GetTop();
8987 }
8988 else if ( bottom > ch )
8989 {
8990 int h = r.GetHeight();
8991 ypos = r.GetTop();
8992 for ( i = row - 1; i >= 0; i-- )
8993 {
8994 int rowHeight = GetRowHeight(i);
8995 if ( h + rowHeight > ch )
8996 break;
8997
8998 h += rowHeight;
8999 ypos -= rowHeight;
9000 }
9001
9002 // we divide it later by GRID_SCROLL_LINE, make sure that we don't
9003 // have rounding errors (this is important, because if we do,
9004 // we might not scroll at all and some cells won't be redrawn)
9005 //
9006 // Sometimes GRID_SCROLL_LINE / 2 is not enough,
9007 // so just add a full scroll unit...
9008 ypos += m_scrollLineY;
9009 }
9010
9011 // special handling for wide cells - show always left part of the cell!
9012 // Otherwise, e.g. when stepping from row to row, it would jump between
9013 // left and right part of the cell on every step!
9014 // if ( left < 0 )
9015 if ( left < 0 || (right - left) >= cw )
9016 {
9017 xpos = r.GetLeft();
9018 }
9019 else if ( right > cw )
9020 {
9021 // position the view so that the cell is on the right
9022 int x0, y0;
9023 CalcUnscrolledPosition(0, 0, &x0, &y0);
9024 xpos = x0 + (right - cw);
9025
9026 // see comment for ypos above
9027 xpos += m_scrollLineX;
9028 }
9029
9030 if ( xpos != -1 || ypos != -1 )
9031 {
9032 if ( xpos != -1 )
9033 xpos /= m_scrollLineX;
9034 if ( ypos != -1 )
9035 ypos /= m_scrollLineY;
9036 Scroll( xpos, ypos );
9037 AdjustScrollbars();
9038 }
9039 }
9040 }
9041
9042 //
9043 // ------ Grid cursor movement functions
9044 //
9045
9046 bool
9047 wxGrid::DoMoveCursor(bool expandSelection,
9048 const wxGridDirectionOperations& diroper)
9049 {
9050 if ( m_currentCellCoords == wxGridNoCellCoords )
9051 return false;
9052
9053 if ( expandSelection )
9054 {
9055 wxGridCellCoords coords = m_selectedBlockCorner;
9056 if ( coords == wxGridNoCellCoords )
9057 coords = m_currentCellCoords;
9058
9059 if ( diroper.IsAtBoundary(coords) )
9060 return false;
9061
9062 diroper.Advance(coords);
9063
9064 UpdateBlockBeingSelected(m_currentCellCoords, coords);
9065 }
9066 else // don't expand selection
9067 {
9068 ClearSelection();
9069
9070 if ( diroper.IsAtBoundary(m_currentCellCoords) )
9071 return false;
9072
9073 wxGridCellCoords coords = m_currentCellCoords;
9074 diroper.Advance(coords);
9075
9076 GoToCell(coords);
9077 }
9078
9079 return true;
9080 }
9081
9082 bool wxGrid::MoveCursorUp(bool expandSelection)
9083 {
9084 return DoMoveCursor(expandSelection,
9085 wxGridBackwardOperations(this, wxGridRowOperations()));
9086 }
9087
9088 bool wxGrid::MoveCursorDown(bool expandSelection)
9089 {
9090 return DoMoveCursor(expandSelection,
9091 wxGridForwardOperations(this, wxGridRowOperations()));
9092 }
9093
9094 bool wxGrid::MoveCursorLeft(bool expandSelection)
9095 {
9096 return DoMoveCursor(expandSelection,
9097 wxGridBackwardOperations(this, wxGridColumnOperations()));
9098 }
9099
9100 bool wxGrid::MoveCursorRight(bool expandSelection)
9101 {
9102 return DoMoveCursor(expandSelection,
9103 wxGridForwardOperations(this, wxGridColumnOperations()));
9104 }
9105
9106 bool wxGrid::DoMoveCursorByPage(const wxGridDirectionOperations& diroper)
9107 {
9108 if ( m_currentCellCoords == wxGridNoCellCoords )
9109 return false;
9110
9111 if ( diroper.IsAtBoundary(m_currentCellCoords) )
9112 return false;
9113
9114 const int oldRow = m_currentCellCoords.GetRow();
9115 int newRow = diroper.MoveByPixelDistance(oldRow, m_gridWin->GetClientSize().y);
9116 if ( newRow == oldRow )
9117 {
9118 wxGridCellCoords coords(m_currentCellCoords);
9119 diroper.Advance(coords);
9120 newRow = coords.GetRow();
9121 }
9122
9123 GoToCell(newRow, m_currentCellCoords.GetCol());
9124
9125 return true;
9126 }
9127
9128 bool wxGrid::MovePageUp()
9129 {
9130 return DoMoveCursorByPage(
9131 wxGridBackwardOperations(this, wxGridRowOperations()));
9132 }
9133
9134 bool wxGrid::MovePageDown()
9135 {
9136 return DoMoveCursorByPage(
9137 wxGridForwardOperations(this, wxGridRowOperations()));
9138 }
9139
9140 // helper of DoMoveCursorByBlock(): advance the cell coordinates using diroper
9141 // until we find a non-empty cell or reach the grid end
9142 void
9143 wxGrid::AdvanceToNextNonEmpty(wxGridCellCoords& coords,
9144 const wxGridDirectionOperations& diroper)
9145 {
9146 while ( !diroper.IsAtBoundary(coords) )
9147 {
9148 diroper.Advance(coords);
9149 if ( !m_table->IsEmpty(coords) )
9150 break;
9151 }
9152 }
9153
9154 bool
9155 wxGrid::DoMoveCursorByBlock(bool expandSelection,
9156 const wxGridDirectionOperations& diroper)
9157 {
9158 if ( !m_table || m_currentCellCoords == wxGridNoCellCoords )
9159 return false;
9160
9161 if ( diroper.IsAtBoundary(m_currentCellCoords) )
9162 return false;
9163
9164 wxGridCellCoords coords(m_currentCellCoords);
9165 if ( m_table->IsEmpty(coords) )
9166 {
9167 // we are in an empty cell: find the next block of non-empty cells
9168 AdvanceToNextNonEmpty(coords, diroper);
9169 }
9170 else // current cell is not empty
9171 {
9172 diroper.Advance(coords);
9173 if ( m_table->IsEmpty(coords) )
9174 {
9175 // we started at the end of a block, find the next one
9176 AdvanceToNextNonEmpty(coords, diroper);
9177 }
9178 else // we're in a middle of a block
9179 {
9180 // go to the end of it, i.e. find the last cell before the next
9181 // empty one
9182 while ( !diroper.IsAtBoundary(coords) )
9183 {
9184 wxGridCellCoords coordsNext(coords);
9185 diroper.Advance(coordsNext);
9186 if ( m_table->IsEmpty(coordsNext) )
9187 break;
9188
9189 coords = coordsNext;
9190 }
9191 }
9192 }
9193
9194 if ( expandSelection )
9195 {
9196 UpdateBlockBeingSelected(m_currentCellCoords, coords);
9197 }
9198 else
9199 {
9200 ClearSelection();
9201 GoToCell(coords);
9202 }
9203
9204 return true;
9205 }
9206
9207 bool wxGrid::MoveCursorUpBlock(bool expandSelection)
9208 {
9209 return DoMoveCursorByBlock(
9210 expandSelection,
9211 wxGridBackwardOperations(this, wxGridRowOperations())
9212 );
9213 }
9214
9215 bool wxGrid::MoveCursorDownBlock( bool expandSelection )
9216 {
9217 return DoMoveCursorByBlock(
9218 expandSelection,
9219 wxGridForwardOperations(this, wxGridRowOperations())
9220 );
9221 }
9222
9223 bool wxGrid::MoveCursorLeftBlock( bool expandSelection )
9224 {
9225 return DoMoveCursorByBlock(
9226 expandSelection,
9227 wxGridBackwardOperations(this, wxGridColumnOperations())
9228 );
9229 }
9230
9231 bool wxGrid::MoveCursorRightBlock( bool expandSelection )
9232 {
9233 return DoMoveCursorByBlock(
9234 expandSelection,
9235 wxGridForwardOperations(this, wxGridColumnOperations())
9236 );
9237 }
9238
9239 //
9240 // ------ Label values and formatting
9241 //
9242
9243 void wxGrid::GetRowLabelAlignment( int *horiz, int *vert ) const
9244 {
9245 if ( horiz )
9246 *horiz = m_rowLabelHorizAlign;
9247 if ( vert )
9248 *vert = m_rowLabelVertAlign;
9249 }
9250
9251 void wxGrid::GetColLabelAlignment( int *horiz, int *vert ) const
9252 {
9253 if ( horiz )
9254 *horiz = m_colLabelHorizAlign;
9255 if ( vert )
9256 *vert = m_colLabelVertAlign;
9257 }
9258
9259 int wxGrid::GetColLabelTextOrientation() const
9260 {
9261 return m_colLabelTextOrientation;
9262 }
9263
9264 wxString wxGrid::GetRowLabelValue( int row ) const
9265 {
9266 if ( m_table )
9267 {
9268 return m_table->GetRowLabelValue( row );
9269 }
9270 else
9271 {
9272 wxString s;
9273 s << row;
9274 return s;
9275 }
9276 }
9277
9278 wxString wxGrid::GetColLabelValue( int col ) const
9279 {
9280 if ( m_table )
9281 {
9282 return m_table->GetColLabelValue( col );
9283 }
9284 else
9285 {
9286 wxString s;
9287 s << col;
9288 return s;
9289 }
9290 }
9291
9292 void wxGrid::SetRowLabelSize( int width )
9293 {
9294 wxASSERT( width >= 0 || width == wxGRID_AUTOSIZE );
9295
9296 if ( width == wxGRID_AUTOSIZE )
9297 {
9298 width = CalcColOrRowLabelAreaMinSize(wxGRID_ROW);
9299 }
9300
9301 if ( width != m_rowLabelWidth )
9302 {
9303 if ( width == 0 )
9304 {
9305 m_rowLabelWin->Show( false );
9306 m_cornerLabelWin->Show( false );
9307 }
9308 else if ( m_rowLabelWidth == 0 )
9309 {
9310 m_rowLabelWin->Show( true );
9311 if ( m_colLabelHeight > 0 )
9312 m_cornerLabelWin->Show( true );
9313 }
9314
9315 m_rowLabelWidth = width;
9316 CalcWindowSizes();
9317 wxScrolledWindow::Refresh( true );
9318 }
9319 }
9320
9321 void wxGrid::SetColLabelSize( int height )
9322 {
9323 wxASSERT( height >=0 || height == wxGRID_AUTOSIZE );
9324
9325 if ( height == wxGRID_AUTOSIZE )
9326 {
9327 height = CalcColOrRowLabelAreaMinSize(wxGRID_COLUMN);
9328 }
9329
9330 if ( height != m_colLabelHeight )
9331 {
9332 if ( height == 0 )
9333 {
9334 m_colWindow->Show( false );
9335 m_cornerLabelWin->Show( false );
9336 }
9337 else if ( m_colLabelHeight == 0 )
9338 {
9339 m_colWindow->Show( true );
9340 if ( m_rowLabelWidth > 0 )
9341 m_cornerLabelWin->Show( true );
9342 }
9343
9344 m_colLabelHeight = height;
9345 CalcWindowSizes();
9346 wxScrolledWindow::Refresh( true );
9347 }
9348 }
9349
9350 void wxGrid::SetLabelBackgroundColour( const wxColour& colour )
9351 {
9352 if ( m_labelBackgroundColour != colour )
9353 {
9354 m_labelBackgroundColour = colour;
9355 m_rowLabelWin->SetBackgroundColour( colour );
9356 m_colWindow->SetBackgroundColour( colour );
9357 m_cornerLabelWin->SetBackgroundColour( colour );
9358
9359 if ( !GetBatchCount() )
9360 {
9361 m_rowLabelWin->Refresh();
9362 m_colWindow->Refresh();
9363 m_cornerLabelWin->Refresh();
9364 }
9365 }
9366 }
9367
9368 void wxGrid::SetLabelTextColour( const wxColour& colour )
9369 {
9370 if ( m_labelTextColour != colour )
9371 {
9372 m_labelTextColour = colour;
9373 if ( !GetBatchCount() )
9374 {
9375 m_rowLabelWin->Refresh();
9376 m_colWindow->Refresh();
9377 }
9378 }
9379 }
9380
9381 void wxGrid::SetLabelFont( const wxFont& font )
9382 {
9383 m_labelFont = font;
9384 if ( !GetBatchCount() )
9385 {
9386 m_rowLabelWin->Refresh();
9387 m_colWindow->Refresh();
9388 }
9389 }
9390
9391 void wxGrid::SetRowLabelAlignment( int horiz, int vert )
9392 {
9393 // allow old (incorrect) defs to be used
9394 switch ( horiz )
9395 {
9396 case wxLEFT: horiz = wxALIGN_LEFT; break;
9397 case wxRIGHT: horiz = wxALIGN_RIGHT; break;
9398 case wxCENTRE: horiz = wxALIGN_CENTRE; break;
9399 }
9400
9401 switch ( vert )
9402 {
9403 case wxTOP: vert = wxALIGN_TOP; break;
9404 case wxBOTTOM: vert = wxALIGN_BOTTOM; break;
9405 case wxCENTRE: vert = wxALIGN_CENTRE; break;
9406 }
9407
9408 if ( horiz == wxALIGN_LEFT || horiz == wxALIGN_CENTRE || horiz == wxALIGN_RIGHT )
9409 {
9410 m_rowLabelHorizAlign = horiz;
9411 }
9412
9413 if ( vert == wxALIGN_TOP || vert == wxALIGN_CENTRE || vert == wxALIGN_BOTTOM )
9414 {
9415 m_rowLabelVertAlign = vert;
9416 }
9417
9418 if ( !GetBatchCount() )
9419 {
9420 m_rowLabelWin->Refresh();
9421 }
9422 }
9423
9424 void wxGrid::SetColLabelAlignment( int horiz, int vert )
9425 {
9426 // allow old (incorrect) defs to be used
9427 switch ( horiz )
9428 {
9429 case wxLEFT: horiz = wxALIGN_LEFT; break;
9430 case wxRIGHT: horiz = wxALIGN_RIGHT; break;
9431 case wxCENTRE: horiz = wxALIGN_CENTRE; break;
9432 }
9433
9434 switch ( vert )
9435 {
9436 case wxTOP: vert = wxALIGN_TOP; break;
9437 case wxBOTTOM: vert = wxALIGN_BOTTOM; break;
9438 case wxCENTRE: vert = wxALIGN_CENTRE; break;
9439 }
9440
9441 if ( horiz == wxALIGN_LEFT || horiz == wxALIGN_CENTRE || horiz == wxALIGN_RIGHT )
9442 {
9443 m_colLabelHorizAlign = horiz;
9444 }
9445
9446 if ( vert == wxALIGN_TOP || vert == wxALIGN_CENTRE || vert == wxALIGN_BOTTOM )
9447 {
9448 m_colLabelVertAlign = vert;
9449 }
9450
9451 if ( !GetBatchCount() )
9452 {
9453 m_colWindow->Refresh();
9454 }
9455 }
9456
9457 // Note: under MSW, the default column label font must be changed because it
9458 // does not support vertical printing
9459 //
9460 // Example: wxFont font(9, wxSWISS, wxNORMAL, wxBOLD);
9461 // pGrid->SetLabelFont(font);
9462 // pGrid->SetColLabelTextOrientation(wxVERTICAL);
9463 //
9464 void wxGrid::SetColLabelTextOrientation( int textOrientation )
9465 {
9466 if ( textOrientation == wxHORIZONTAL || textOrientation == wxVERTICAL )
9467 m_colLabelTextOrientation = textOrientation;
9468
9469 if ( !GetBatchCount() )
9470 m_colWindow->Refresh();
9471 }
9472
9473 void wxGrid::SetRowLabelValue( int row, const wxString& s )
9474 {
9475 if ( m_table )
9476 {
9477 m_table->SetRowLabelValue( row, s );
9478 if ( !GetBatchCount() )
9479 {
9480 wxRect rect = CellToRect( row, 0 );
9481 if ( rect.height > 0 )
9482 {
9483 CalcScrolledPosition(0, rect.y, &rect.x, &rect.y);
9484 rect.x = 0;
9485 rect.width = m_rowLabelWidth;
9486 m_rowLabelWin->Refresh( true, &rect );
9487 }
9488 }
9489 }
9490 }
9491
9492 void wxGrid::SetColLabelValue( int col, const wxString& s )
9493 {
9494 if ( m_table )
9495 {
9496 m_table->SetColLabelValue( col, s );
9497 if ( !GetBatchCount() )
9498 {
9499 if ( m_useNativeHeader )
9500 {
9501 GetColHeader()->UpdateColumn(col);
9502 }
9503 else
9504 {
9505 wxRect rect = CellToRect( 0, col );
9506 if ( rect.width > 0 )
9507 {
9508 CalcScrolledPosition(rect.x, 0, &rect.x, &rect.y);
9509 rect.y = 0;
9510 rect.height = m_colLabelHeight;
9511 GetColLabelWindow()->Refresh( true, &rect );
9512 }
9513 }
9514 }
9515 }
9516 }
9517
9518 void wxGrid::SetGridLineColour( const wxColour& colour )
9519 {
9520 if ( m_gridLineColour != colour )
9521 {
9522 m_gridLineColour = colour;
9523
9524 if ( GridLinesEnabled() )
9525 RedrawGridLines();
9526 }
9527 }
9528
9529 void wxGrid::SetCellHighlightColour( const wxColour& colour )
9530 {
9531 if ( m_cellHighlightColour != colour )
9532 {
9533 m_cellHighlightColour = colour;
9534
9535 wxClientDC dc( m_gridWin );
9536 PrepareDC( dc );
9537 wxGridCellAttr* attr = GetCellAttr(m_currentCellCoords);
9538 DrawCellHighlight(dc, attr);
9539 attr->DecRef();
9540 }
9541 }
9542
9543 void wxGrid::SetCellHighlightPenWidth(int width)
9544 {
9545 if (m_cellHighlightPenWidth != width)
9546 {
9547 m_cellHighlightPenWidth = width;
9548
9549 // Just redrawing the cell highlight is not enough since that won't
9550 // make any visible change if the the thickness is getting smaller.
9551 int row = m_currentCellCoords.GetRow();
9552 int col = m_currentCellCoords.GetCol();
9553 if ( row == -1 || col == -1 || GetColWidth(col) <= 0 || GetRowHeight(row) <= 0 )
9554 return;
9555
9556 wxRect rect = CellToRect(row, col);
9557 m_gridWin->Refresh(true, &rect);
9558 }
9559 }
9560
9561 void wxGrid::SetCellHighlightROPenWidth(int width)
9562 {
9563 if (m_cellHighlightROPenWidth != width)
9564 {
9565 m_cellHighlightROPenWidth = width;
9566
9567 // Just redrawing the cell highlight is not enough since that won't
9568 // make any visible change if the the thickness is getting smaller.
9569 int row = m_currentCellCoords.GetRow();
9570 int col = m_currentCellCoords.GetCol();
9571 if ( row == -1 || col == -1 ||
9572 GetColWidth(col) <= 0 || GetRowHeight(row) <= 0 )
9573 return;
9574
9575 wxRect rect = CellToRect(row, col);
9576 m_gridWin->Refresh(true, &rect);
9577 }
9578 }
9579
9580 void wxGrid::RedrawGridLines()
9581 {
9582 // the lines will be redrawn when the window is thawn
9583 if ( GetBatchCount() )
9584 return;
9585
9586 if ( GridLinesEnabled() )
9587 {
9588 wxClientDC dc( m_gridWin );
9589 PrepareDC( dc );
9590 DrawAllGridLines( dc, wxRegion() );
9591 }
9592 else // remove the grid lines
9593 {
9594 m_gridWin->Refresh();
9595 }
9596 }
9597
9598 void wxGrid::EnableGridLines( bool enable )
9599 {
9600 if ( enable != m_gridLinesEnabled )
9601 {
9602 m_gridLinesEnabled = enable;
9603
9604 RedrawGridLines();
9605 }
9606 }
9607
9608 void wxGrid::DoClipGridLines(bool& var, bool clip)
9609 {
9610 if ( clip != var )
9611 {
9612 var = clip;
9613
9614 if ( GridLinesEnabled() )
9615 RedrawGridLines();
9616 }
9617 }
9618
9619 int wxGrid::GetDefaultRowSize() const
9620 {
9621 return m_defaultRowHeight;
9622 }
9623
9624 int wxGrid::GetRowSize( int row ) const
9625 {
9626 wxCHECK_MSG( row >= 0 && row < m_numRows, 0, _T("invalid row index") );
9627
9628 return GetRowHeight(row);
9629 }
9630
9631 int wxGrid::GetDefaultColSize() const
9632 {
9633 return m_defaultColWidth;
9634 }
9635
9636 int wxGrid::GetColSize( int col ) const
9637 {
9638 wxCHECK_MSG( col >= 0 && col < m_numCols, 0, _T("invalid column index") );
9639
9640 return GetColWidth(col);
9641 }
9642
9643 // ============================================================================
9644 // access to the grid attributes: each of them has a default value in the grid
9645 // itself and may be overidden on a per-cell basis
9646 // ============================================================================
9647
9648 // ----------------------------------------------------------------------------
9649 // setting default attributes
9650 // ----------------------------------------------------------------------------
9651
9652 void wxGrid::SetDefaultCellBackgroundColour( const wxColour& col )
9653 {
9654 m_defaultCellAttr->SetBackgroundColour(col);
9655 #ifdef __WXGTK__
9656 m_gridWin->SetBackgroundColour(col);
9657 #endif
9658 }
9659
9660 void wxGrid::SetDefaultCellTextColour( const wxColour& col )
9661 {
9662 m_defaultCellAttr->SetTextColour(col);
9663 }
9664
9665 void wxGrid::SetDefaultCellAlignment( int horiz, int vert )
9666 {
9667 m_defaultCellAttr->SetAlignment(horiz, vert);
9668 }
9669
9670 void wxGrid::SetDefaultCellOverflow( bool allow )
9671 {
9672 m_defaultCellAttr->SetOverflow(allow);
9673 }
9674
9675 void wxGrid::SetDefaultCellFont( const wxFont& font )
9676 {
9677 m_defaultCellAttr->SetFont(font);
9678 }
9679
9680 // For editors and renderers the type registry takes precedence over the
9681 // default attr, so we need to register the new editor/renderer for the string
9682 // data type in order to make setting a default editor/renderer appear to
9683 // work correctly.
9684
9685 void wxGrid::SetDefaultRenderer(wxGridCellRenderer *renderer)
9686 {
9687 RegisterDataType(wxGRID_VALUE_STRING,
9688 renderer,
9689 GetDefaultEditorForType(wxGRID_VALUE_STRING));
9690 }
9691
9692 void wxGrid::SetDefaultEditor(wxGridCellEditor *editor)
9693 {
9694 RegisterDataType(wxGRID_VALUE_STRING,
9695 GetDefaultRendererForType(wxGRID_VALUE_STRING),
9696 editor);
9697 }
9698
9699 // ----------------------------------------------------------------------------
9700 // access to the default attributes
9701 // ----------------------------------------------------------------------------
9702
9703 wxColour wxGrid::GetDefaultCellBackgroundColour() const
9704 {
9705 return m_defaultCellAttr->GetBackgroundColour();
9706 }
9707
9708 wxColour wxGrid::GetDefaultCellTextColour() const
9709 {
9710 return m_defaultCellAttr->GetTextColour();
9711 }
9712
9713 wxFont wxGrid::GetDefaultCellFont() const
9714 {
9715 return m_defaultCellAttr->GetFont();
9716 }
9717
9718 void wxGrid::GetDefaultCellAlignment( int *horiz, int *vert ) const
9719 {
9720 m_defaultCellAttr->GetAlignment(horiz, vert);
9721 }
9722
9723 bool wxGrid::GetDefaultCellOverflow() const
9724 {
9725 return m_defaultCellAttr->GetOverflow();
9726 }
9727
9728 wxGridCellRenderer *wxGrid::GetDefaultRenderer() const
9729 {
9730 return m_defaultCellAttr->GetRenderer(NULL, 0, 0);
9731 }
9732
9733 wxGridCellEditor *wxGrid::GetDefaultEditor() const
9734 {
9735 return m_defaultCellAttr->GetEditor(NULL, 0, 0);
9736 }
9737
9738 // ----------------------------------------------------------------------------
9739 // access to cell attributes
9740 // ----------------------------------------------------------------------------
9741
9742 wxColour wxGrid::GetCellBackgroundColour(int row, int col) const
9743 {
9744 wxGridCellAttr *attr = GetCellAttr(row, col);
9745 wxColour colour = attr->GetBackgroundColour();
9746 attr->DecRef();
9747
9748 return colour;
9749 }
9750
9751 wxColour wxGrid::GetCellTextColour( int row, int col ) const
9752 {
9753 wxGridCellAttr *attr = GetCellAttr(row, col);
9754 wxColour colour = attr->GetTextColour();
9755 attr->DecRef();
9756
9757 return colour;
9758 }
9759
9760 wxFont wxGrid::GetCellFont( int row, int col ) const
9761 {
9762 wxGridCellAttr *attr = GetCellAttr(row, col);
9763 wxFont font = attr->GetFont();
9764 attr->DecRef();
9765
9766 return font;
9767 }
9768
9769 void wxGrid::GetCellAlignment( int row, int col, int *horiz, int *vert ) const
9770 {
9771 wxGridCellAttr *attr = GetCellAttr(row, col);
9772 attr->GetAlignment(horiz, vert);
9773 attr->DecRef();
9774 }
9775
9776 bool wxGrid::GetCellOverflow( int row, int col ) const
9777 {
9778 wxGridCellAttr *attr = GetCellAttr(row, col);
9779 bool allow = attr->GetOverflow();
9780 attr->DecRef();
9781
9782 return allow;
9783 }
9784
9785 void wxGrid::GetCellSize( int row, int col, int *num_rows, int *num_cols ) const
9786 {
9787 wxGridCellAttr *attr = GetCellAttr(row, col);
9788 attr->GetSize( num_rows, num_cols );
9789 attr->DecRef();
9790 }
9791
9792 wxGridCellRenderer* wxGrid::GetCellRenderer(int row, int col) const
9793 {
9794 wxGridCellAttr* attr = GetCellAttr(row, col);
9795 wxGridCellRenderer* renderer = attr->GetRenderer(this, row, col);
9796 attr->DecRef();
9797
9798 return renderer;
9799 }
9800
9801 wxGridCellEditor* wxGrid::GetCellEditor(int row, int col) const
9802 {
9803 wxGridCellAttr* attr = GetCellAttr(row, col);
9804 wxGridCellEditor* editor = attr->GetEditor(this, row, col);
9805 attr->DecRef();
9806
9807 return editor;
9808 }
9809
9810 bool wxGrid::IsReadOnly(int row, int col) const
9811 {
9812 wxGridCellAttr* attr = GetCellAttr(row, col);
9813 bool isReadOnly = attr->IsReadOnly();
9814 attr->DecRef();
9815
9816 return isReadOnly;
9817 }
9818
9819 // ----------------------------------------------------------------------------
9820 // attribute support: cache, automatic provider creation, ...
9821 // ----------------------------------------------------------------------------
9822
9823 bool wxGrid::CanHaveAttributes() const
9824 {
9825 if ( !m_table )
9826 {
9827 return false;
9828 }
9829
9830 return m_table->CanHaveAttributes();
9831 }
9832
9833 void wxGrid::ClearAttrCache()
9834 {
9835 if ( m_attrCache.row != -1 )
9836 {
9837 wxGridCellAttr *oldAttr = m_attrCache.attr;
9838 m_attrCache.attr = NULL;
9839 m_attrCache.row = -1;
9840 // wxSafeDecRec(...) might cause event processing that accesses
9841 // the cached attribute, if one exists (e.g. by deleting the
9842 // editor stored within the attribute). Therefore it is important
9843 // to invalidate the cache before calling wxSafeDecRef!
9844 wxSafeDecRef(oldAttr);
9845 }
9846 }
9847
9848 void wxGrid::CacheAttr(int row, int col, wxGridCellAttr *attr) const
9849 {
9850 if ( attr != NULL )
9851 {
9852 wxGrid *self = (wxGrid *)this; // const_cast
9853
9854 self->ClearAttrCache();
9855 self->m_attrCache.row = row;
9856 self->m_attrCache.col = col;
9857 self->m_attrCache.attr = attr;
9858 wxSafeIncRef(attr);
9859 }
9860 }
9861
9862 bool wxGrid::LookupAttr(int row, int col, wxGridCellAttr **attr) const
9863 {
9864 if ( row == m_attrCache.row && col == m_attrCache.col )
9865 {
9866 *attr = m_attrCache.attr;
9867 wxSafeIncRef(m_attrCache.attr);
9868
9869 #ifdef DEBUG_ATTR_CACHE
9870 gs_nAttrCacheHits++;
9871 #endif
9872
9873 return true;
9874 }
9875 else
9876 {
9877 #ifdef DEBUG_ATTR_CACHE
9878 gs_nAttrCacheMisses++;
9879 #endif
9880
9881 return false;
9882 }
9883 }
9884
9885 wxGridCellAttr *wxGrid::GetCellAttr(int row, int col) const
9886 {
9887 wxGridCellAttr *attr = NULL;
9888 // Additional test to avoid looking at the cache e.g. for
9889 // wxNoCellCoords, as this will confuse memory management.
9890 if ( row >= 0 )
9891 {
9892 if ( !LookupAttr(row, col, &attr) )
9893 {
9894 attr = m_table ? m_table->GetAttr(row, col, wxGridCellAttr::Any)
9895 : NULL;
9896 CacheAttr(row, col, attr);
9897 }
9898 }
9899
9900 if (attr)
9901 {
9902 attr->SetDefAttr(m_defaultCellAttr);
9903 }
9904 else
9905 {
9906 attr = m_defaultCellAttr;
9907 attr->IncRef();
9908 }
9909
9910 return attr;
9911 }
9912
9913 wxGridCellAttr *wxGrid::GetOrCreateCellAttr(int row, int col) const
9914 {
9915 wxGridCellAttr *attr = NULL;
9916 bool canHave = ((wxGrid*)this)->CanHaveAttributes();
9917
9918 wxCHECK_MSG( canHave, attr, _T("Cell attributes not allowed"));
9919 wxCHECK_MSG( m_table, attr, _T("must have a table") );
9920
9921 attr = m_table->GetAttr(row, col, wxGridCellAttr::Cell);
9922 if ( !attr )
9923 {
9924 attr = new wxGridCellAttr(m_defaultCellAttr);
9925
9926 // artificially inc the ref count to match DecRef() in caller
9927 attr->IncRef();
9928 m_table->SetAttr(attr, row, col);
9929 }
9930
9931 return attr;
9932 }
9933
9934 // ----------------------------------------------------------------------------
9935 // setting column attributes (wrappers around SetColAttr)
9936 // ----------------------------------------------------------------------------
9937
9938 void wxGrid::SetColFormatBool(int col)
9939 {
9940 SetColFormatCustom(col, wxGRID_VALUE_BOOL);
9941 }
9942
9943 void wxGrid::SetColFormatNumber(int col)
9944 {
9945 SetColFormatCustom(col, wxGRID_VALUE_NUMBER);
9946 }
9947
9948 void wxGrid::SetColFormatFloat(int col, int width, int precision)
9949 {
9950 wxString typeName = wxGRID_VALUE_FLOAT;
9951 if ( (width != -1) || (precision != -1) )
9952 {
9953 typeName << _T(':') << width << _T(',') << precision;
9954 }
9955
9956 SetColFormatCustom(col, typeName);
9957 }
9958
9959 void wxGrid::SetColFormatCustom(int col, const wxString& typeName)
9960 {
9961 wxGridCellAttr *attr = m_table->GetAttr(-1, col, wxGridCellAttr::Col );
9962 if (!attr)
9963 attr = new wxGridCellAttr;
9964 wxGridCellRenderer *renderer = GetDefaultRendererForType(typeName);
9965 attr->SetRenderer(renderer);
9966 wxGridCellEditor *editor = GetDefaultEditorForType(typeName);
9967 attr->SetEditor(editor);
9968
9969 SetColAttr(col, attr);
9970
9971 }
9972
9973 // ----------------------------------------------------------------------------
9974 // setting cell attributes: this is forwarded to the table
9975 // ----------------------------------------------------------------------------
9976
9977 void wxGrid::SetAttr(int row, int col, wxGridCellAttr *attr)
9978 {
9979 if ( CanHaveAttributes() )
9980 {
9981 m_table->SetAttr(attr, row, col);
9982 ClearAttrCache();
9983 }
9984 else
9985 {
9986 wxSafeDecRef(attr);
9987 }
9988 }
9989
9990 void wxGrid::SetRowAttr(int row, wxGridCellAttr *attr)
9991 {
9992 if ( CanHaveAttributes() )
9993 {
9994 m_table->SetRowAttr(attr, row);
9995 ClearAttrCache();
9996 }
9997 else
9998 {
9999 wxSafeDecRef(attr);
10000 }
10001 }
10002
10003 void wxGrid::SetColAttr(int col, wxGridCellAttr *attr)
10004 {
10005 if ( CanHaveAttributes() )
10006 {
10007 m_table->SetColAttr(attr, col);
10008 ClearAttrCache();
10009 }
10010 else
10011 {
10012 wxSafeDecRef(attr);
10013 }
10014 }
10015
10016 void wxGrid::SetCellBackgroundColour( int row, int col, const wxColour& colour )
10017 {
10018 if ( CanHaveAttributes() )
10019 {
10020 wxGridCellAttr *attr = GetOrCreateCellAttr(row, col);
10021 attr->SetBackgroundColour(colour);
10022 attr->DecRef();
10023 }
10024 }
10025
10026 void wxGrid::SetCellTextColour( int row, int col, const wxColour& colour )
10027 {
10028 if ( CanHaveAttributes() )
10029 {
10030 wxGridCellAttr *attr = GetOrCreateCellAttr(row, col);
10031 attr->SetTextColour(colour);
10032 attr->DecRef();
10033 }
10034 }
10035
10036 void wxGrid::SetCellFont( int row, int col, const wxFont& font )
10037 {
10038 if ( CanHaveAttributes() )
10039 {
10040 wxGridCellAttr *attr = GetOrCreateCellAttr(row, col);
10041 attr->SetFont(font);
10042 attr->DecRef();
10043 }
10044 }
10045
10046 void wxGrid::SetCellAlignment( int row, int col, int horiz, int vert )
10047 {
10048 if ( CanHaveAttributes() )
10049 {
10050 wxGridCellAttr *attr = GetOrCreateCellAttr(row, col);
10051 attr->SetAlignment(horiz, vert);
10052 attr->DecRef();
10053 }
10054 }
10055
10056 void wxGrid::SetCellOverflow( int row, int col, bool allow )
10057 {
10058 if ( CanHaveAttributes() )
10059 {
10060 wxGridCellAttr *attr = GetOrCreateCellAttr(row, col);
10061 attr->SetOverflow(allow);
10062 attr->DecRef();
10063 }
10064 }
10065
10066 void wxGrid::SetCellSize( int row, int col, int num_rows, int num_cols )
10067 {
10068 if ( CanHaveAttributes() )
10069 {
10070 int cell_rows, cell_cols;
10071
10072 wxGridCellAttr *attr = GetOrCreateCellAttr(row, col);
10073 attr->GetSize(&cell_rows, &cell_cols);
10074 attr->SetSize(num_rows, num_cols);
10075 attr->DecRef();
10076
10077 // Cannot set the size of a cell to 0 or negative values
10078 // While it is perfectly legal to do that, this function cannot
10079 // handle all the possibilies, do it by hand by getting the CellAttr.
10080 // You can only set the size of a cell to 1,1 or greater with this fn
10081 wxASSERT_MSG( !((cell_rows < 1) || (cell_cols < 1)),
10082 wxT("wxGrid::SetCellSize setting cell size that is already part of another cell"));
10083 wxASSERT_MSG( !((num_rows < 1) || (num_cols < 1)),
10084 wxT("wxGrid::SetCellSize setting cell size to < 1"));
10085
10086 // if this was already a multicell then "turn off" the other cells first
10087 if ((cell_rows > 1) || (cell_cols > 1))
10088 {
10089 int i, j;
10090 for (j=row; j < row + cell_rows; j++)
10091 {
10092 for (i=col; i < col + cell_cols; i++)
10093 {
10094 if ((i != col) || (j != row))
10095 {
10096 wxGridCellAttr *attr_stub = GetOrCreateCellAttr(j, i);
10097 attr_stub->SetSize( 1, 1 );
10098 attr_stub->DecRef();
10099 }
10100 }
10101 }
10102 }
10103
10104 // mark the cells that will be covered by this cell to
10105 // negative or zero values to point back at this cell
10106 if (((num_rows > 1) || (num_cols > 1)) && (num_rows >= 1) && (num_cols >= 1))
10107 {
10108 int i, j;
10109 for (j=row; j < row + num_rows; j++)
10110 {
10111 for (i=col; i < col + num_cols; i++)
10112 {
10113 if ((i != col) || (j != row))
10114 {
10115 wxGridCellAttr *attr_stub = GetOrCreateCellAttr(j, i);
10116 attr_stub->SetSize( row - j, col - i );
10117 attr_stub->DecRef();
10118 }
10119 }
10120 }
10121 }
10122 }
10123 }
10124
10125 void wxGrid::SetCellRenderer(int row, int col, wxGridCellRenderer *renderer)
10126 {
10127 if ( CanHaveAttributes() )
10128 {
10129 wxGridCellAttr *attr = GetOrCreateCellAttr(row, col);
10130 attr->SetRenderer(renderer);
10131 attr->DecRef();
10132 }
10133 }
10134
10135 void wxGrid::SetCellEditor(int row, int col, wxGridCellEditor* editor)
10136 {
10137 if ( CanHaveAttributes() )
10138 {
10139 wxGridCellAttr *attr = GetOrCreateCellAttr(row, col);
10140 attr->SetEditor(editor);
10141 attr->DecRef();
10142 }
10143 }
10144
10145 void wxGrid::SetReadOnly(int row, int col, bool isReadOnly)
10146 {
10147 if ( CanHaveAttributes() )
10148 {
10149 wxGridCellAttr *attr = GetOrCreateCellAttr(row, col);
10150 attr->SetReadOnly(isReadOnly);
10151 attr->DecRef();
10152 }
10153 }
10154
10155 // ----------------------------------------------------------------------------
10156 // Data type registration
10157 // ----------------------------------------------------------------------------
10158
10159 void wxGrid::RegisterDataType(const wxString& typeName,
10160 wxGridCellRenderer* renderer,
10161 wxGridCellEditor* editor)
10162 {
10163 m_typeRegistry->RegisterDataType(typeName, renderer, editor);
10164 }
10165
10166
10167 wxGridCellEditor * wxGrid::GetDefaultEditorForCell(int row, int col) const
10168 {
10169 wxString typeName = m_table->GetTypeName(row, col);
10170 return GetDefaultEditorForType(typeName);
10171 }
10172
10173 wxGridCellRenderer * wxGrid::GetDefaultRendererForCell(int row, int col) const
10174 {
10175 wxString typeName = m_table->GetTypeName(row, col);
10176 return GetDefaultRendererForType(typeName);
10177 }
10178
10179 wxGridCellEditor * wxGrid::GetDefaultEditorForType(const wxString& typeName) const
10180 {
10181 int index = m_typeRegistry->FindOrCloneDataType(typeName);
10182 if ( index == wxNOT_FOUND )
10183 {
10184 wxFAIL_MSG(wxString::Format(wxT("Unknown data type name [%s]"), typeName.c_str()));
10185
10186 return NULL;
10187 }
10188
10189 return m_typeRegistry->GetEditor(index);
10190 }
10191
10192 wxGridCellRenderer * wxGrid::GetDefaultRendererForType(const wxString& typeName) const
10193 {
10194 int index = m_typeRegistry->FindOrCloneDataType(typeName);
10195 if ( index == wxNOT_FOUND )
10196 {
10197 wxFAIL_MSG(wxString::Format(wxT("Unknown data type name [%s]"), typeName.c_str()));
10198
10199 return NULL;
10200 }
10201
10202 return m_typeRegistry->GetRenderer(index);
10203 }
10204
10205 // ----------------------------------------------------------------------------
10206 // row/col size
10207 // ----------------------------------------------------------------------------
10208
10209 void wxGrid::EnableDragRowSize( bool enable )
10210 {
10211 m_canDragRowSize = enable;
10212 }
10213
10214 void wxGrid::EnableDragColSize( bool enable )
10215 {
10216 m_canDragColSize = enable;
10217 }
10218
10219 void wxGrid::EnableDragGridSize( bool enable )
10220 {
10221 m_canDragGridSize = enable;
10222 }
10223
10224 void wxGrid::EnableDragCell( bool enable )
10225 {
10226 m_canDragCell = enable;
10227 }
10228
10229 void wxGrid::SetDefaultRowSize( int height, bool resizeExistingRows )
10230 {
10231 m_defaultRowHeight = wxMax( height, m_minAcceptableRowHeight );
10232
10233 if ( resizeExistingRows )
10234 {
10235 // since we are resizing all rows to the default row size,
10236 // we can simply clear the row heights and row bottoms
10237 // arrays (which also allows us to take advantage of
10238 // some speed optimisations)
10239 m_rowHeights.Empty();
10240 m_rowBottoms.Empty();
10241 if ( !GetBatchCount() )
10242 CalcDimensions();
10243 }
10244 }
10245
10246 void wxGrid::SetRowSize( int row, int height )
10247 {
10248 wxCHECK_RET( row >= 0 && row < m_numRows, _T("invalid row index") );
10249
10250 // if < 0 then calculate new height from label
10251 if ( height < 0 )
10252 {
10253 long w, h;
10254 wxArrayString lines;
10255 wxClientDC dc(m_rowLabelWin);
10256 dc.SetFont(GetLabelFont());
10257 StringToLines(GetRowLabelValue( row ), lines);
10258 GetTextBoxSize( dc, lines, &w, &h );
10259 //check that it is not less than the minimal height
10260 height = wxMax(h, GetRowMinimalAcceptableHeight());
10261 }
10262
10263 // See comment in SetColSize
10264 if ( height < GetRowMinimalAcceptableHeight())
10265 return;
10266
10267 if ( m_rowHeights.IsEmpty() )
10268 {
10269 // need to really create the array
10270 InitRowHeights();
10271 }
10272
10273 int h = wxMax( 0, height );
10274 int diff = h - m_rowHeights[row];
10275
10276 m_rowHeights[row] = h;
10277 for ( int i = row; i < m_numRows; i++ )
10278 {
10279 m_rowBottoms[i] += diff;
10280 }
10281
10282 if ( !GetBatchCount() )
10283 CalcDimensions();
10284 }
10285
10286 void wxGrid::SetDefaultColSize( int width, bool resizeExistingCols )
10287 {
10288 // we dont allow zero default column width
10289 m_defaultColWidth = wxMax( wxMax( width, m_minAcceptableColWidth ), 1 );
10290
10291 if ( resizeExistingCols )
10292 {
10293 // since we are resizing all columns to the default column size,
10294 // we can simply clear the col widths and col rights
10295 // arrays (which also allows us to take advantage of
10296 // some speed optimisations)
10297 m_colWidths.Empty();
10298 m_colRights.Empty();
10299 if ( !GetBatchCount() )
10300 CalcDimensions();
10301 }
10302 }
10303
10304 void wxGrid::SetColSize( int col, int width )
10305 {
10306 wxCHECK_RET( col >= 0 && col < m_numCols, _T("invalid column index") );
10307
10308 // if < 0 then calculate new width from label
10309 if ( width < 0 )
10310 {
10311 long w, h;
10312 wxArrayString lines;
10313 wxClientDC dc(m_colWindow);
10314 dc.SetFont(GetLabelFont());
10315 StringToLines(GetColLabelValue(col), lines);
10316 if ( GetColLabelTextOrientation() == wxHORIZONTAL )
10317 GetTextBoxSize( dc, lines, &w, &h );
10318 else
10319 GetTextBoxSize( dc, lines, &h, &w );
10320 width = w + 6;
10321 //check that it is not less than the minimal width
10322 width = wxMax(width, GetColMinimalAcceptableWidth());
10323 }
10324
10325 // should we check that it's bigger than GetColMinimalWidth(col) here?
10326 // (VZ)
10327 // No, because it is reasonable to assume the library user know's
10328 // what he is doing. However we should test against the weaker
10329 // constraint of minimalAcceptableWidth, as this breaks rendering
10330 //
10331 // This test then fixes sf.net bug #645734
10332
10333 if ( width < GetColMinimalAcceptableWidth() )
10334 return;
10335
10336 if ( m_colWidths.IsEmpty() )
10337 {
10338 // need to really create the array
10339 InitColWidths();
10340 }
10341
10342 int w = wxMax( 0, width );
10343 int diff = w - m_colWidths[col];
10344 m_colWidths[col] = w;
10345
10346 for ( int colPos = GetColPos(col); colPos < m_numCols; colPos++ )
10347 {
10348 m_colRights[GetColAt(colPos)] += diff;
10349 }
10350
10351 if ( !GetBatchCount() )
10352 {
10353 CalcDimensions();
10354 Refresh();
10355 }
10356 }
10357
10358 void wxGrid::SetColMinimalWidth( int col, int width )
10359 {
10360 if (width > GetColMinimalAcceptableWidth())
10361 {
10362 wxLongToLongHashMap::key_type key = (wxLongToLongHashMap::key_type)col;
10363 m_colMinWidths[key] = width;
10364 }
10365 }
10366
10367 void wxGrid::SetRowMinimalHeight( int row, int width )
10368 {
10369 if (width > GetRowMinimalAcceptableHeight())
10370 {
10371 wxLongToLongHashMap::key_type key = (wxLongToLongHashMap::key_type)row;
10372 m_rowMinHeights[key] = width;
10373 }
10374 }
10375
10376 int wxGrid::GetColMinimalWidth(int col) const
10377 {
10378 wxLongToLongHashMap::key_type key = (wxLongToLongHashMap::key_type)col;
10379 wxLongToLongHashMap::const_iterator it = m_colMinWidths.find(key);
10380
10381 return it != m_colMinWidths.end() ? (int)it->second : m_minAcceptableColWidth;
10382 }
10383
10384 int wxGrid::GetRowMinimalHeight(int row) const
10385 {
10386 wxLongToLongHashMap::key_type key = (wxLongToLongHashMap::key_type)row;
10387 wxLongToLongHashMap::const_iterator it = m_rowMinHeights.find(key);
10388
10389 return it != m_rowMinHeights.end() ? (int)it->second : m_minAcceptableRowHeight;
10390 }
10391
10392 void wxGrid::SetColMinimalAcceptableWidth( int width )
10393 {
10394 // We do allow a width of 0 since this gives us
10395 // an easy way to temporarily hiding columns.
10396 if ( width >= 0 )
10397 m_minAcceptableColWidth = width;
10398 }
10399
10400 void wxGrid::SetRowMinimalAcceptableHeight( int height )
10401 {
10402 // We do allow a height of 0 since this gives us
10403 // an easy way to temporarily hiding rows.
10404 if ( height >= 0 )
10405 m_minAcceptableRowHeight = height;
10406 }
10407
10408 int wxGrid::GetColMinimalAcceptableWidth() const
10409 {
10410 return m_minAcceptableColWidth;
10411 }
10412
10413 int wxGrid::GetRowMinimalAcceptableHeight() const
10414 {
10415 return m_minAcceptableRowHeight;
10416 }
10417
10418 // ----------------------------------------------------------------------------
10419 // auto sizing
10420 // ----------------------------------------------------------------------------
10421
10422 void
10423 wxGrid::AutoSizeColOrRow(int colOrRow, bool setAsMin, wxGridDirection direction)
10424 {
10425 const bool column = direction == wxGRID_COLUMN;
10426
10427 wxClientDC dc(m_gridWin);
10428
10429 // cancel editing of cell
10430 HideCellEditControl();
10431 SaveEditControlValue();
10432
10433 // init both of them to avoid compiler warnings, even if we only need one
10434 int row = -1,
10435 col = -1;
10436 if ( column )
10437 col = colOrRow;
10438 else
10439 row = colOrRow;
10440
10441 wxCoord extent, extentMax = 0;
10442 int max = column ? m_numRows : m_numCols;
10443 for ( int rowOrCol = 0; rowOrCol < max; rowOrCol++ )
10444 {
10445 if ( column )
10446 row = rowOrCol;
10447 else
10448 col = rowOrCol;
10449
10450 wxGridCellAttr *attr = GetCellAttr(row, col);
10451 wxGridCellRenderer *renderer = attr->GetRenderer(this, row, col);
10452 if ( renderer )
10453 {
10454 wxSize size = renderer->GetBestSize(*this, *attr, dc, row, col);
10455 extent = column ? size.x : size.y;
10456 if ( extent > extentMax )
10457 extentMax = extent;
10458
10459 renderer->DecRef();
10460 }
10461
10462 attr->DecRef();
10463 }
10464
10465 // now also compare with the column label extent
10466 wxCoord w, h;
10467 dc.SetFont( GetLabelFont() );
10468
10469 if ( column )
10470 {
10471 dc.GetMultiLineTextExtent( GetColLabelValue(col), &w, &h );
10472 if ( GetColLabelTextOrientation() == wxVERTICAL )
10473 w = h;
10474 }
10475 else
10476 dc.GetMultiLineTextExtent( GetRowLabelValue(row), &w, &h );
10477
10478 extent = column ? w : h;
10479 if ( extent > extentMax )
10480 extentMax = extent;
10481
10482 if ( !extentMax )
10483 {
10484 // empty column - give default extent (notice that if extentMax is less
10485 // than default extent but != 0, it's OK)
10486 extentMax = column ? m_defaultColWidth : m_defaultRowHeight;
10487 }
10488 else
10489 {
10490 if ( column )
10491 // leave some space around text
10492 extentMax += 10;
10493 else
10494 extentMax += 6;
10495 }
10496
10497 if ( column )
10498 {
10499 // Ensure automatic width is not less than minimal width. See the
10500 // comment in SetColSize() for explanation of why this isn't done
10501 // in SetColSize().
10502 if ( !setAsMin )
10503 extentMax = wxMax(extentMax, GetColMinimalWidth(col));
10504
10505 SetColSize( col, extentMax );
10506 if ( !GetBatchCount() )
10507 {
10508 if ( m_useNativeHeader )
10509 {
10510 GetColHeader()->UpdateColumn(col);
10511 }
10512 else
10513 {
10514 int cw, ch, dummy;
10515 m_gridWin->GetClientSize( &cw, &ch );
10516 wxRect rect ( CellToRect( 0, col ) );
10517 rect.y = 0;
10518 CalcScrolledPosition(rect.x, 0, &rect.x, &dummy);
10519 rect.width = cw - rect.x;
10520 rect.height = m_colLabelHeight;
10521 GetColLabelWindow()->Refresh( true, &rect );
10522 }
10523 }
10524 }
10525 else
10526 {
10527 // Ensure automatic width is not less than minimal height. See the
10528 // comment in SetColSize() for explanation of why this isn't done
10529 // in SetRowSize().
10530 if ( !setAsMin )
10531 extentMax = wxMax(extentMax, GetRowMinimalHeight(row));
10532
10533 SetRowSize(row, extentMax);
10534 if ( !GetBatchCount() )
10535 {
10536 int cw, ch, dummy;
10537 m_gridWin->GetClientSize( &cw, &ch );
10538 wxRect rect( CellToRect( row, 0 ) );
10539 rect.x = 0;
10540 CalcScrolledPosition(0, rect.y, &dummy, &rect.y);
10541 rect.width = m_rowLabelWidth;
10542 rect.height = ch - rect.y;
10543 m_rowLabelWin->Refresh( true, &rect );
10544 }
10545 }
10546
10547 if ( setAsMin )
10548 {
10549 if ( column )
10550 SetColMinimalWidth(col, extentMax);
10551 else
10552 SetRowMinimalHeight(row, extentMax);
10553 }
10554 }
10555
10556 wxCoord wxGrid::CalcColOrRowLabelAreaMinSize(wxGridDirection direction)
10557 {
10558 // calculate size for the rows or columns?
10559 const bool calcRows = direction == wxGRID_ROW;
10560
10561 wxClientDC dc(calcRows ? GetGridRowLabelWindow()
10562 : GetGridColLabelWindow());
10563 dc.SetFont(GetLabelFont());
10564
10565 // which dimension should we take into account for calculations?
10566 //
10567 // for columns, the text can be only horizontal so it's easy but for rows
10568 // we also have to take into account the text orientation
10569 const bool
10570 useWidth = calcRows || (GetColLabelTextOrientation() == wxVERTICAL);
10571
10572 wxArrayString lines;
10573 wxCoord extentMax = 0;
10574
10575 const int numRowsOrCols = calcRows ? m_numRows : m_numCols;
10576 for ( int rowOrCol = 0; rowOrCol < numRowsOrCols; rowOrCol++ )
10577 {
10578 lines.Clear();
10579
10580 wxString label = calcRows ? GetRowLabelValue(rowOrCol)
10581 : GetColLabelValue(rowOrCol);
10582 StringToLines(label, lines);
10583
10584 long w, h;
10585 GetTextBoxSize(dc, lines, &w, &h);
10586
10587 const wxCoord extent = useWidth ? w : h;
10588 if ( extent > extentMax )
10589 extentMax = extent;
10590 }
10591
10592 if ( !extentMax )
10593 {
10594 // empty column - give default extent (notice that if extentMax is less
10595 // than default extent but != 0, it's OK)
10596 extentMax = calcRows ? GetDefaultRowLabelSize()
10597 : GetDefaultColLabelSize();
10598 }
10599
10600 // leave some space around text (taken from AutoSizeColOrRow)
10601 if ( calcRows )
10602 extentMax += 10;
10603 else
10604 extentMax += 6;
10605
10606 return extentMax;
10607 }
10608
10609 int wxGrid::SetOrCalcColumnSizes(bool calcOnly, bool setAsMin)
10610 {
10611 int width = m_rowLabelWidth;
10612
10613 wxGridUpdateLocker locker;
10614 if(!calcOnly)
10615 locker.Create(this);
10616
10617 for ( int col = 0; col < m_numCols; col++ )
10618 {
10619 if ( !calcOnly )
10620 AutoSizeColumn(col, setAsMin);
10621
10622 width += GetColWidth(col);
10623 }
10624
10625 return width;
10626 }
10627
10628 int wxGrid::SetOrCalcRowSizes(bool calcOnly, bool setAsMin)
10629 {
10630 int height = m_colLabelHeight;
10631
10632 wxGridUpdateLocker locker;
10633 if(!calcOnly)
10634 locker.Create(this);
10635
10636 for ( int row = 0; row < m_numRows; row++ )
10637 {
10638 if ( !calcOnly )
10639 AutoSizeRow(row, setAsMin);
10640
10641 height += GetRowHeight(row);
10642 }
10643
10644 return height;
10645 }
10646
10647 void wxGrid::AutoSize()
10648 {
10649 wxGridUpdateLocker locker(this);
10650
10651 wxSize size(SetOrCalcColumnSizes(false) - m_rowLabelWidth + m_extraWidth,
10652 SetOrCalcRowSizes(false) - m_colLabelHeight + m_extraHeight);
10653
10654 // we know that we're not going to have scrollbars so disable them now to
10655 // avoid trouble in SetClientSize() which can otherwise set the correct
10656 // client size but also leave space for (not needed any more) scrollbars
10657 SetScrollbars(0, 0, 0, 0, 0, 0, true);
10658
10659 // restore the scroll rate parameters overwritten by SetScrollbars()
10660 SetScrollRate(m_scrollLineX, m_scrollLineY);
10661
10662 SetClientSize(size.x + m_rowLabelWidth, size.y + m_colLabelHeight);
10663 }
10664
10665 void wxGrid::AutoSizeRowLabelSize( int row )
10666 {
10667 // Hide the edit control, so it
10668 // won't interfere with drag-shrinking.
10669 if ( IsCellEditControlShown() )
10670 {
10671 HideCellEditControl();
10672 SaveEditControlValue();
10673 }
10674
10675 // autosize row height depending on label text
10676 SetRowSize(row, -1);
10677 ForceRefresh();
10678 }
10679
10680 void wxGrid::AutoSizeColLabelSize( int col )
10681 {
10682 // Hide the edit control, so it
10683 // won't interfere with drag-shrinking.
10684 if ( IsCellEditControlShown() )
10685 {
10686 HideCellEditControl();
10687 SaveEditControlValue();
10688 }
10689
10690 // autosize column width depending on label text
10691 SetColSize(col, -1);
10692 ForceRefresh();
10693 }
10694
10695 wxSize wxGrid::DoGetBestSize() const
10696 {
10697 wxGrid *self = (wxGrid *)this; // const_cast
10698
10699 // we do the same as in AutoSize() here with the exception that we don't
10700 // change the column/row sizes, only calculate them
10701 wxSize size(self->SetOrCalcColumnSizes(true) - m_rowLabelWidth + m_extraWidth,
10702 self->SetOrCalcRowSizes(true) - m_colLabelHeight + m_extraHeight);
10703
10704 // NOTE: This size should be cached, but first we need to add calls to
10705 // InvalidateBestSize everywhere that could change the results of this
10706 // calculation.
10707 // CacheBestSize(size);
10708
10709 return wxSize(size.x + m_rowLabelWidth, size.y + m_colLabelHeight)
10710 + GetWindowBorderSize();
10711 }
10712
10713 void wxGrid::Fit()
10714 {
10715 AutoSize();
10716 }
10717
10718 wxPen& wxGrid::GetDividerPen() const
10719 {
10720 return wxNullPen;
10721 }
10722
10723 // ----------------------------------------------------------------------------
10724 // cell value accessor functions
10725 // ----------------------------------------------------------------------------
10726
10727 void wxGrid::SetCellValue( int row, int col, const wxString& s )
10728 {
10729 if ( m_table )
10730 {
10731 m_table->SetValue( row, col, s );
10732 if ( !GetBatchCount() )
10733 {
10734 int dummy;
10735 wxRect rect( CellToRect( row, col ) );
10736 rect.x = 0;
10737 rect.width = m_gridWin->GetClientSize().GetWidth();
10738 CalcScrolledPosition(0, rect.y, &dummy, &rect.y);
10739 m_gridWin->Refresh( false, &rect );
10740 }
10741
10742 if ( m_currentCellCoords.GetRow() == row &&
10743 m_currentCellCoords.GetCol() == col &&
10744 IsCellEditControlShown())
10745 // Note: If we are using IsCellEditControlEnabled,
10746 // this interacts badly with calling SetCellValue from
10747 // an EVT_GRID_CELL_CHANGE handler.
10748 {
10749 HideCellEditControl();
10750 ShowCellEditControl(); // will reread data from table
10751 }
10752 }
10753 }
10754
10755 // ----------------------------------------------------------------------------
10756 // block, row and column selection
10757 // ----------------------------------------------------------------------------
10758
10759 void wxGrid::SelectRow( int row, bool addToSelected )
10760 {
10761 if ( !m_selection )
10762 return;
10763
10764 if ( !addToSelected )
10765 ClearSelection();
10766
10767 m_selection->SelectRow(row);
10768 }
10769
10770 void wxGrid::SelectCol( int col, bool addToSelected )
10771 {
10772 if ( !m_selection )
10773 return;
10774
10775 if ( !addToSelected )
10776 ClearSelection();
10777
10778 m_selection->SelectCol(col);
10779 }
10780
10781 void wxGrid::SelectBlock(int topRow, int leftCol, int bottomRow, int rightCol,
10782 bool addToSelected)
10783 {
10784 if ( !m_selection )
10785 return;
10786
10787 if ( !addToSelected )
10788 ClearSelection();
10789
10790 m_selection->SelectBlock(topRow, leftCol, bottomRow, rightCol);
10791 }
10792
10793 void wxGrid::SelectAll()
10794 {
10795 if ( m_numRows > 0 && m_numCols > 0 )
10796 {
10797 if ( m_selection )
10798 m_selection->SelectBlock( 0, 0, m_numRows - 1, m_numCols - 1 );
10799 }
10800 }
10801
10802 // ----------------------------------------------------------------------------
10803 // cell, row and col deselection
10804 // ----------------------------------------------------------------------------
10805
10806 void wxGrid::DeselectLine(int line, const wxGridOperations& oper)
10807 {
10808 if ( !m_selection )
10809 return;
10810
10811 const wxGridSelectionModes mode = m_selection->GetSelectionMode();
10812 if ( mode == oper.GetSelectionMode() )
10813 {
10814 const wxGridCellCoords c(oper.MakeCoords(line, 0));
10815 if ( m_selection->IsInSelection(c) )
10816 m_selection->ToggleCellSelection(c);
10817 }
10818 else if ( mode != oper.Dual().GetSelectionMode() )
10819 {
10820 const int nOther = oper.Dual().GetNumberOfLines(this);
10821 for ( int i = 0; i < nOther; i++ )
10822 {
10823 const wxGridCellCoords c(oper.MakeCoords(line, i));
10824 if ( m_selection->IsInSelection(c) )
10825 m_selection->ToggleCellSelection(c);
10826 }
10827 }
10828 //else: can only select orthogonal lines so no lines in this direction
10829 // could have been selected anyhow
10830 }
10831
10832 void wxGrid::DeselectRow(int row)
10833 {
10834 DeselectLine(row, wxGridRowOperations());
10835 }
10836
10837 void wxGrid::DeselectCol(int col)
10838 {
10839 DeselectLine(col, wxGridColumnOperations());
10840 }
10841
10842 void wxGrid::DeselectCell( int row, int col )
10843 {
10844 if ( m_selection && m_selection->IsInSelection(row, col) )
10845 m_selection->ToggleCellSelection(row, col);
10846 }
10847
10848 bool wxGrid::IsSelection() const
10849 {
10850 return ( m_selection && (m_selection->IsSelection() ||
10851 ( m_selectedBlockTopLeft != wxGridNoCellCoords &&
10852 m_selectedBlockBottomRight != wxGridNoCellCoords) ) );
10853 }
10854
10855 bool wxGrid::IsInSelection( int row, int col ) const
10856 {
10857 return ( m_selection && (m_selection->IsInSelection( row, col ) ||
10858 ( row >= m_selectedBlockTopLeft.GetRow() &&
10859 col >= m_selectedBlockTopLeft.GetCol() &&
10860 row <= m_selectedBlockBottomRight.GetRow() &&
10861 col <= m_selectedBlockBottomRight.GetCol() )) );
10862 }
10863
10864 wxGridCellCoordsArray wxGrid::GetSelectedCells() const
10865 {
10866 if (!m_selection)
10867 {
10868 wxGridCellCoordsArray a;
10869 return a;
10870 }
10871
10872 return m_selection->m_cellSelection;
10873 }
10874
10875 wxGridCellCoordsArray wxGrid::GetSelectionBlockTopLeft() const
10876 {
10877 if (!m_selection)
10878 {
10879 wxGridCellCoordsArray a;
10880 return a;
10881 }
10882
10883 return m_selection->m_blockSelectionTopLeft;
10884 }
10885
10886 wxGridCellCoordsArray wxGrid::GetSelectionBlockBottomRight() const
10887 {
10888 if (!m_selection)
10889 {
10890 wxGridCellCoordsArray a;
10891 return a;
10892 }
10893
10894 return m_selection->m_blockSelectionBottomRight;
10895 }
10896
10897 wxArrayInt wxGrid::GetSelectedRows() const
10898 {
10899 if (!m_selection)
10900 {
10901 wxArrayInt a;
10902 return a;
10903 }
10904
10905 return m_selection->m_rowSelection;
10906 }
10907
10908 wxArrayInt wxGrid::GetSelectedCols() const
10909 {
10910 if (!m_selection)
10911 {
10912 wxArrayInt a;
10913 return a;
10914 }
10915
10916 return m_selection->m_colSelection;
10917 }
10918
10919 void wxGrid::ClearSelection()
10920 {
10921 wxRect r1 = BlockToDeviceRect(m_selectedBlockTopLeft,
10922 m_selectedBlockBottomRight);
10923 wxRect r2 = BlockToDeviceRect(m_currentCellCoords,
10924 m_selectedBlockCorner);
10925
10926 m_selectedBlockTopLeft =
10927 m_selectedBlockBottomRight =
10928 m_selectedBlockCorner = wxGridNoCellCoords;
10929
10930 Refresh( false, &r1 );
10931 Refresh( false, &r2 );
10932
10933 if ( m_selection )
10934 m_selection->ClearSelection();
10935 }
10936
10937 // This function returns the rectangle that encloses the given block
10938 // in device coords clipped to the client size of the grid window.
10939 //
10940 wxRect wxGrid::BlockToDeviceRect( const wxGridCellCoords& topLeft,
10941 const wxGridCellCoords& bottomRight ) const
10942 {
10943 wxRect resultRect;
10944 wxRect tempCellRect = CellToRect(topLeft);
10945 if ( tempCellRect != wxGridNoCellRect )
10946 {
10947 resultRect = tempCellRect;
10948 }
10949 else
10950 {
10951 resultRect = wxRect(0, 0, 0, 0);
10952 }
10953
10954 tempCellRect = CellToRect(bottomRight);
10955 if ( tempCellRect != wxGridNoCellRect )
10956 {
10957 resultRect += tempCellRect;
10958 }
10959 else
10960 {
10961 // If both inputs were "wxGridNoCellRect," then there's nothing to do.
10962 return wxGridNoCellRect;
10963 }
10964
10965 // Ensure that left/right and top/bottom pairs are in order.
10966 int left = resultRect.GetLeft();
10967 int top = resultRect.GetTop();
10968 int right = resultRect.GetRight();
10969 int bottom = resultRect.GetBottom();
10970
10971 int leftCol = topLeft.GetCol();
10972 int topRow = topLeft.GetRow();
10973 int rightCol = bottomRight.GetCol();
10974 int bottomRow = bottomRight.GetRow();
10975
10976 if (left > right)
10977 {
10978 int tmp = left;
10979 left = right;
10980 right = tmp;
10981
10982 tmp = leftCol;
10983 leftCol = rightCol;
10984 rightCol = tmp;
10985 }
10986
10987 if (top > bottom)
10988 {
10989 int tmp = top;
10990 top = bottom;
10991 bottom = tmp;
10992
10993 tmp = topRow;
10994 topRow = bottomRow;
10995 bottomRow = tmp;
10996 }
10997
10998 // The following loop is ONLY necessary to detect and handle merged cells.
10999 int cw, ch;
11000 m_gridWin->GetClientSize( &cw, &ch );
11001
11002 // Get the origin coordinates: notice that they will be negative if the
11003 // grid is scrolled downwards/to the right.
11004 int gridOriginX = 0;
11005 int gridOriginY = 0;
11006 CalcScrolledPosition(gridOriginX, gridOriginY, &gridOriginX, &gridOriginY);
11007
11008 int onScreenLeftmostCol = internalXToCol(-gridOriginX);
11009 int onScreenUppermostRow = internalYToRow(-gridOriginY);
11010
11011 int onScreenRightmostCol = internalXToCol(-gridOriginX + cw);
11012 int onScreenBottommostRow = internalYToRow(-gridOriginY + ch);
11013
11014 // Bound our loop so that we only examine the portion of the selected block
11015 // that is shown on screen. Therefore, we compare the Top-Left block values
11016 // to the Top-Left screen values, and the Bottom-Right block values to the
11017 // Bottom-Right screen values, choosing appropriately.
11018 const int visibleTopRow = wxMax(topRow, onScreenUppermostRow);
11019 const int visibleBottomRow = wxMin(bottomRow, onScreenBottommostRow);
11020 const int visibleLeftCol = wxMax(leftCol, onScreenLeftmostCol);
11021 const int visibleRightCol = wxMin(rightCol, onScreenRightmostCol);
11022
11023 for ( int j = visibleTopRow; j <= visibleBottomRow; j++ )
11024 {
11025 for ( int i = visibleLeftCol; i <= visibleRightCol; i++ )
11026 {
11027 if ( (j == visibleTopRow) || (j == visibleBottomRow) ||
11028 (i == visibleLeftCol) || (i == visibleRightCol) )
11029 {
11030 tempCellRect = CellToRect( j, i );
11031
11032 if (tempCellRect.x < left)
11033 left = tempCellRect.x;
11034 if (tempCellRect.y < top)
11035 top = tempCellRect.y;
11036 if (tempCellRect.x + tempCellRect.width > right)
11037 right = tempCellRect.x + tempCellRect.width;
11038 if (tempCellRect.y + tempCellRect.height > bottom)
11039 bottom = tempCellRect.y + tempCellRect.height;
11040 }
11041 else
11042 {
11043 i = visibleRightCol; // jump over inner cells.
11044 }
11045 }
11046 }
11047
11048 // Convert to scrolled coords
11049 CalcScrolledPosition( left, top, &left, &top );
11050 CalcScrolledPosition( right, bottom, &right, &bottom );
11051
11052 if (right < 0 || bottom < 0 || left > cw || top > ch)
11053 return wxRect(0,0,0,0);
11054
11055 resultRect.SetLeft( wxMax(0, left) );
11056 resultRect.SetTop( wxMax(0, top) );
11057 resultRect.SetRight( wxMin(cw, right) );
11058 resultRect.SetBottom( wxMin(ch, bottom) );
11059
11060 return resultRect;
11061 }
11062
11063 // ----------------------------------------------------------------------------
11064 // drop target
11065 // ----------------------------------------------------------------------------
11066
11067 #if wxUSE_DRAG_AND_DROP
11068
11069 // this allow setting drop target directly on wxGrid
11070 void wxGrid::SetDropTarget(wxDropTarget *dropTarget)
11071 {
11072 GetGridWindow()->SetDropTarget(dropTarget);
11073 }
11074
11075 #endif // wxUSE_DRAG_AND_DROP
11076
11077 // ----------------------------------------------------------------------------
11078 // grid event classes
11079 // ----------------------------------------------------------------------------
11080
11081 IMPLEMENT_DYNAMIC_CLASS( wxGridEvent, wxNotifyEvent )
11082
11083 wxGridEvent::wxGridEvent( int id, wxEventType type, wxObject* obj,
11084 int row, int col, int x, int y, bool sel,
11085 bool control, bool shift, bool alt, bool meta )
11086 : wxNotifyEvent( type, id ),
11087 wxKeyboardState(control, shift, alt, meta)
11088 {
11089 Init(row, col, x, y, sel);
11090
11091 SetEventObject(obj);
11092 }
11093
11094 IMPLEMENT_DYNAMIC_CLASS( wxGridSizeEvent, wxNotifyEvent )
11095
11096 wxGridSizeEvent::wxGridSizeEvent( int id, wxEventType type, wxObject* obj,
11097 int rowOrCol, int x, int y,
11098 bool control, bool shift, bool alt, bool meta )
11099 : wxNotifyEvent( type, id ),
11100 wxKeyboardState(control, shift, alt, meta)
11101 {
11102 Init(rowOrCol, x, y);
11103
11104 SetEventObject(obj);
11105 }
11106
11107
11108 IMPLEMENT_DYNAMIC_CLASS( wxGridRangeSelectEvent, wxNotifyEvent )
11109
11110 wxGridRangeSelectEvent::wxGridRangeSelectEvent(int id, wxEventType type, wxObject* obj,
11111 const wxGridCellCoords& topLeft,
11112 const wxGridCellCoords& bottomRight,
11113 bool sel, bool control,
11114 bool shift, bool alt, bool meta )
11115 : wxNotifyEvent( type, id ),
11116 wxKeyboardState(control, shift, alt, meta)
11117 {
11118 Init(topLeft, bottomRight, sel);
11119
11120 SetEventObject(obj);
11121 }
11122
11123
11124 IMPLEMENT_DYNAMIC_CLASS(wxGridEditorCreatedEvent, wxCommandEvent)
11125
11126 wxGridEditorCreatedEvent::wxGridEditorCreatedEvent(int id, wxEventType type,
11127 wxObject* obj, int row,
11128 int col, wxControl* ctrl)
11129 : wxCommandEvent(type, id)
11130 {
11131 SetEventObject(obj);
11132 m_row = row;
11133 m_col = col;
11134 m_ctrl = ctrl;
11135 }
11136
11137 #endif // wxUSE_GRID